// This file is part of SmallBASIC
//
// pseudo-compiler: Converts the source to byte-code.
//
// This program is distributed under the terms of the GPL v2.0 or later
// Download the GNU Public License (GPL) from www.gnu.org
//
// Copyright(C) 2000 Nicholas Christopoulos
#define SCAN_MODULE
#include "common/sys.h"
#include "common/device.h"
#include "common/kw.h"
#include "common/bc.h"
#include "common/scan.h"
#include "common/smbas.h"
#include "common/units.h"
#include "common/extlib.h"
#include "common/messages.h"
char *comp_array_uds_field(char *p, bc_t * bc);
void comp_text_line(char *text);
addr_t comp_search_bc(addr_t ip, code_t code);
extern void expr_parser(bc_t * bc);
extern void sc_raise2(const char *fmt, int line, const char *buff); // sberr
#define SKIP_SPACES(p) \
while (*p == ' ' || *p == '\t') { \
p++; \
}
#define CHKOPT(x) \
(strncmp(p, (x), strlen((x))) == 0)
#define GROWSIZE 128
void err_wrongproc(const char *name) {
sc_raise(MSG_WRONG_PROCNAME, name);
}
void err_comp_missing_rp() {
sc_raise(MSG_EXP_MIS_RP);
}
void err_comp_missing_lp() {
sc_raise(MSG_EXP_MIS_LP);
}
void err_comp_label_not_def(const char *name) {
sc_raise(MSG_LABEL_NOT_DEFINED, name);
}
#include "languages/keywords.en.c"
/*
* reset the external proc/func lists
*/
void comp_reset_externals(void) {
// reset functions
if (comp_extfunctable) {
tmp_free(comp_extfunctable);
}
comp_extfunctable = NULL;
comp_extfunccount = comp_extfuncsize = 0;
// reset procedures
if (comp_extproctable) {
tmp_free(comp_extproctable);
}
comp_extproctable = NULL;
comp_extproccount = comp_extprocsize = 0;
}
/*
* add an external procedure to the list
*/
int comp_add_external_proc(const char *proc_name, int lib_id) {
// TODO: scan for conflicts
if (comp_extproctable == NULL) {
comp_extprocsize = 16;
comp_extproctable = (ext_proc_node_t *)tmp_alloc(sizeof(ext_proc_node_t) * comp_extprocsize);
} else if (comp_extprocsize <= (comp_extproccount + 1)) {
comp_extprocsize += 16;
comp_extproctable = (ext_proc_node_t *)tmp_realloc(comp_extproctable,
sizeof(ext_proc_node_t) * comp_extprocsize);
}
comp_extproctable[comp_extproccount].lib_id = lib_id;
comp_extproctable[comp_extproccount].symbol_index = comp_impcount;
strcpy(comp_extproctable[comp_extproccount].name, proc_name);
strupper(comp_extproctable[comp_extproccount].name);
// update imports table
bc_symbol_rec_t sym;
strcpy(sym.symbol, proc_name); // symbol name
sym.type = stt_procedure; // symbol type
sym.lib_id = lib_id; // library id
sym.sym_id = comp_impcount; // symbol index
// store it
dbt_write(comp_imptable, comp_impcount, &sym, sizeof(bc_symbol_rec_t));
comp_impcount++;
comp_extproccount++;
return comp_extproccount - 1;
}
/*
* Add an external function to the list
*/
int comp_add_external_func(const char *func_name, int lib_id) {
// TODO: scan for conflicts
if (comp_extfunctable == NULL) {
comp_extfuncsize = 16;
comp_extfunctable = (ext_func_node_t *)tmp_alloc(sizeof(ext_func_node_t) * comp_extfuncsize);
} else if (comp_extfuncsize <= (comp_extfunccount + 1)) {
comp_extfuncsize += 16;
comp_extfunctable = (ext_func_node_t *)tmp_realloc(comp_extfunctable,
sizeof(ext_func_node_t) * comp_extfuncsize);
}
comp_extfunctable[comp_extfunccount].lib_id = lib_id;
comp_extfunctable[comp_extfunccount].symbol_index = comp_impcount;
strcpy(comp_extfunctable[comp_extfunccount].name, func_name);
strupper(comp_extfunctable[comp_extfunccount].name);
// update imports table
bc_symbol_rec_t sym;
strcpy(sym.symbol, func_name); // symbol name
sym.type = stt_function; // symbol type
sym.lib_id = lib_id; // library id
sym.sym_id = comp_impcount; // symbol index
// store it
dbt_write(comp_imptable, comp_impcount, &sym, sizeof(bc_symbol_rec_t));
comp_impcount++;
comp_extfunccount++;
return comp_extfunccount - 1;
}
/*
* returns the external procedure id
*/
int comp_is_external_proc(const char *name) {
int i;
for (i = 0; i < comp_extproccount; i++) {
if (strcmp(comp_extproctable[i].name, name) == 0) {
return i;
}
}
return -1;
}
/*
* returns the external function id
*/
int comp_is_external_func(const char *name) {
int i;
for (i = 0; i < comp_extfunccount; i++) {
if (strcmp(comp_extfunctable[i].name, name) == 0) {
return i;
}
}
return -1;
}
/*
* Notes:
* block_level = the depth of nested block
* block_id = unique number of each block (based on stack use)
*
* Example:
* ? xxx ' level 0, id 0
* for i=1 to 20 ' level 1, id 1
* ? yyy ' level 1, id 1
* if a=1 ' level 2, id 2 (our IF uses stack)
* ... ' level 2, id 2
* else ' level 2, id 2 // not 3
* ... ' level 2, id 2
* fi ' level 2, id 2
* if a=2 ' level 2, id 3
* ... ' level 2, id 3
* fi ' level 2, id 3
* ? zzz ' level 1, id 1
* next ' level 1, id 1
* ? ooo ' level 0, id 0
*/
/*
* error messages
*/
void sc_raise(const char *fmt, ...) {
char *buff;
va_list ap;
va_start(ap, fmt);
comp_error = 1;
buff = tmp_alloc(SB_SOURCELINE_SIZE + 1);
#if defined(_DOS)
vsprintf(buff, fmt, ap);
#else
vsnprintf(buff, SB_SOURCELINE_SIZE, fmt, ap);
#endif
va_end(ap);
sc_raise2(comp_bc_sec, comp_line, buff); // sberr.h
tmp_free(buff);
}
/*
* prepare name (keywords, variables, labels, proc/func names)
*/
char *comp_prepare_name(char *dest, const char *source, int size) {
char *p = (char *)source;
SKIP_SPACES(p);
strncpy(dest, p, size);
dest[size] = '\0';
p = dest;
while (*p && (is_alpha(*p) || is_digit(*p) || *p == '$' || *p == '/' || *p == '_' || *p == '.')) {
p++;
}
*p = '\0';
str_alltrim(dest);
return dest;
}
/*
* returns the ID of the label. If there is no one, then it creates one
*/
bid_t comp_label_getID(const char *label_name) {
bid_t idx = -1, i;
char name[SB_KEYWORD_SIZE + 1];
comp_label_t label;
comp_prepare_name(name, label_name, SB_KEYWORD_SIZE);
for (i = 0; i < comp_labcount; i++) {
dbt_read(comp_labtable, i, &label, sizeof(comp_label_t));
if (strcmp(label.name, name) == 0) {
idx = i;
break;
}
}
if (idx == -1) {
#if !defined(OS_LIMITED)
if (opt_verbose) {
log_printf(MSG_NEW_LABEL, comp_line, name, comp_labcount);
}
#endif
strcpy(label.name, name);
label.ip = INVALID_ADDR;
label.dp = INVALID_ADDR;
label.level = comp_block_level;
label.block_id = comp_block_id;
dbt_write(comp_labtable, comp_labcount, &label, sizeof(comp_label_t));
idx = comp_labcount;
comp_labcount++;
}
return idx;
}
/*
* set LABEL's position (IP)
*/
void comp_label_setip(bid_t idx) {
comp_label_t label;
dbt_read(comp_labtable, idx, &label, sizeof(comp_label_t));
label.ip = comp_prog.count;
label.dp = comp_data.count;
label.level = comp_block_level;
label.block_id = comp_block_id;
dbt_write(comp_labtable, idx, &label, sizeof(comp_label_t));
}
/*
* returns the full-path UDP/UDF name
*/
void comp_prepare_udp_name(char *dest, const char *basename) {
char tmp[SB_SOURCELINE_SIZE + 1];
comp_prepare_name(tmp, baseof(basename, '/'), SB_KEYWORD_SIZE);
if (comp_proc_level) {
sprintf(dest, "%s/%s", comp_bc_proc, tmp);
} else {
strcpy(dest, tmp);
}
}
/*
* returns the ID of the UDP/UDF
*/
bid_t comp_udp_id(const char *proc_name, int scan_tree) {
bid_t i;
char *name = comp_bc_temp, *p;
char base[SB_KEYWORD_SIZE + 1];
char *root;
int len;
if (scan_tree) {
comp_prepare_name(base, baseof(proc_name, '/'), SB_KEYWORD_SIZE);
root = tmp_strdup(comp_bc_proc);
do {
// (nested procs) move root down
if ((len = strlen(root)) != 0) {
sprintf(name, "%s/%s", root, base);
p = strrchr(root, '/');
if (p) {
*p = '\0';
} else {
strcpy(root, "");
}
} else {
strcpy(name, base);
}
// search on local
for (i = 0; i < comp_udpcount; i++) {
if (strcmp(comp_udptable[i].name, name) == 0) {
tmp_free(root);
return i;
}
}
} while (len);
// not found
tmp_free(root);
} else {
comp_prepare_udp_name(name, proc_name);
// search on local
for (i = 0; i < comp_udpcount; i++) {
if (strcmp(comp_udptable[i].name, name) == 0) {
return i;
}
}
}
return -1;
}
/*
* creates a new UDP/UDF node
* and returns the new ID
*/
bid_t comp_add_udp(const char *proc_name) {
char *name = comp_bc_temp;
bid_t idx = -1, i;
comp_prepare_udp_name(name, proc_name);
/*
* #if !defined(OS_LIMITED) // check variables for conflict for ( i =
* 0; i < comp_varcount; i ++ ) { if ( strcmp(comp_vartable[i].name,
* name) == 0 ) { sc_raise("User-defined function/procedure name,
* '%s', conflicts with variable", name); break; } } #endif
*/
// search
for (i = 0; i < comp_udpcount; i++) {
if (strcmp(comp_udptable[i].name, name) == 0) {
idx = i;
break;
}
}
if (idx == -1) {
if (comp_udpcount >= comp_udpsize) {
comp_udpsize += GROWSIZE;
comp_udptable = tmp_realloc(comp_udptable, comp_udpsize * sizeof(comp_udp_t));
}
if (!(is_alpha(name[0]) || name[0] == '_')) {
err_wrongproc(name);
} else {
#if !defined(OS_LIMITED)
if (opt_verbose) {
log_printf(MSG_NEW_UDP, comp_line, name, comp_udpcount);
}
#endif
comp_udptable[comp_udpcount].name = tmp_alloc(strlen(name) + 1);
comp_udptable[comp_udpcount].ip = INVALID_ADDR; // bc_prog.count;
comp_udptable[comp_udpcount].level = comp_block_level;
comp_udptable[comp_udpcount].block_id = comp_block_id;
comp_udptable[comp_udpcount].pline = comp_line;
strcpy(comp_udptable[comp_udpcount].name, name);
idx = comp_udpcount;
comp_udpcount++;
}
}
return idx;
}
/*
* sets the IP of the user-defined-procedure (or function)
*/
bid_t comp_udp_setip(const char *proc_name, addr_t ip) {
bid_t idx;
char *name = comp_bc_temp;
comp_prepare_udp_name(name, proc_name);
idx = comp_udp_id(name, 0);
if (idx != -1) {
comp_udptable[idx].ip = comp_prog.count;
comp_udptable[idx].level = comp_block_level;
comp_udptable[idx].block_id = comp_block_id;
}
return idx;
}
/*
* Returns the IP of an UDP/UDF
*/
addr_t comp_udp_getip(const char *proc_name) {
bid_t idx;
char *name = comp_bc_temp;
comp_prepare_udp_name(name, proc_name);
idx = comp_udp_id(name, 1);
if (idx != -1) {
return comp_udptable[idx].ip;
}
return INVALID_ADDR;
}
/*
* parameters string-section
*/
char *get_param_sect(char *text, const char *delim, char *dest) {
char *p = (char *)text;
char *d = dest;
int quotes = 0, level = 0, skip_ch = 0;
if (p == NULL) {
*dest = '\0';
return 0;
}
while (is_space(*p)) {
p++;
}
while (*p) {
if (quotes) {
if (*p == '\\' && *(p + 1) == '\"') {
// add the escaped quote and continue
*d++ = *p++;
} else if (*p == '\"') {
quotes = 0;
}
} else {
switch (*p) {
case '\"':
quotes = 1;
break;
case '(':
level++;
break;
case ')':
level--;
break;
case '\n':
case '\r':
skip_ch = 1;
break;
};
}
// delim check
if (delim != NULL && level <= 0 && quotes == 0) {
if (strchr(delim, *p) != NULL) {
break;
}
}
// copy
if (!skip_ch) {
*d = *p;
d++;
} else {
skip_ch = 0;
}
p++;
}
if (quotes) {
*d++ = '\"';
}
*d = '\0';
if (level > 0) {
err_comp_missing_rp();
}
if (level < 0) {
err_comp_missing_lp();
}
str_alltrim(dest);
return p;
}
/*
*/
int comp_geterror() {
return comp_error;
}
/*
* checking for missing labels
*/
int comp_check_labels() {
bid_t i;
comp_label_t label;
for (i = 0; i < comp_labcount; i++) {
dbt_read(comp_labtable, i, &label, sizeof(comp_label_t));
if (label.ip == INVALID_ADDR) {
err_comp_label_not_def(label.name);
return 0;
}
}
return 1;
}
/*
* returns true if 'name' is a unit or c-module
*/
int comp_check_lib(const char *name) {
char tmp[SB_KEYWORD_SIZE + 1];char *p;
int i;
strcpy(tmp, name);
p = strchr(tmp, '.');
if (p) {
*p = '\0';
for (i = 0; i < comp_libcount; i++) {
bc_lib_rec_t lib;
dbt_read(comp_libtable, i, &lib, sizeof(bc_lib_rec_t));
// remove any file path component from the name
char *dir_sep = strrchr(lib.lib, OS_DIRSEP);
char *lib_name = dir_sep ? dir_sep + 1 : lib.lib;
if (strcasecmp(lib_name, tmp) == 0) {
return 1;
}
}
}
return 0;
}
/*
*/
int comp_create_var(const char *name) {
int idx = -1;
if (!(is_alpha(name[0]) || name[0] == '_'))
sc_raise(MSG_WRONG_VARNAME, name);
else {
// realloc table if it is needed
if (comp_varcount >= comp_varsize) {
comp_varsize += GROWSIZE;
comp_vartable = tmp_realloc(comp_vartable, comp_varsize * sizeof(comp_var_t));
}
#if !defined(OS_LIMITED)
if (opt_verbose) {
log_printf(MSG_NEW_VAR, comp_line, name, comp_varcount);
}
#endif
comp_vartable[comp_varcount].name = tmp_alloc(strlen(name) + 1);
strcpy(comp_vartable[comp_varcount].name, name);
comp_vartable[comp_varcount].dolar_sup = 0;
comp_vartable[comp_varcount].lib_id = -1;
idx = comp_varcount;
comp_varcount++;
}
return idx;
}
/*
*/
int comp_add_external_var(const char *name, int lib_id) {
int idx;
idx = comp_create_var(name);
comp_vartable[idx].lib_id = lib_id;
if (lib_id & UID_UNIT_BIT) {
// update imports table
bc_symbol_rec_t sym;
strcpy(sym.symbol, name); // symbol name
sym.type = stt_variable; // symbol type
sym.lib_id = lib_id; // library id
sym.sym_id = comp_impcount; // symbol index
sym.var_id = idx; // variable index
// store it
dbt_write(comp_imptable, comp_impcount, &sym, sizeof(bc_symbol_rec_t));
comp_impcount++;
}
return idx;
}
/*
* returns the id of the variable 'name'
*
* if there is no such variable then creates a new one
*
* if a new variable must created then if the var_name includes the path then
* the new variable created at local space otherwise at globale space
*/
bid_t comp_var_getID(const char *var_name) {
bid_t idx = -1, i;
char tmp[SB_KEYWORD_SIZE + 1];
char *name = comp_bc_temp;
comp_prepare_name(tmp, baseof(var_name, '/'), SB_KEYWORD_SIZE);
char *dot = strchr(tmp, '.');
if (dot != 0 && *(dot + 1) == 0) {
// name ends with dot
sc_raise(MSG_MEMBER_DOES_NOT_EXISTS, tmp);
return 0;
}
//
// check for external
// external variables are recognized by the 'class' name
// example: my_unit.my_var
//
// If the name is not found in comp_libtable then it
// is treated as a structure reference
if (dot != 0 && comp_check_lib(tmp)) {
for (i = 0; i < comp_varcount; i++) {
if (strcmp(comp_vartable[i].name, tmp) == 0) {
return i;
}
}
sc_raise(MSG_MEMBER_DOES_NOT_EXISTS, tmp);
return 0;
}
//
// search in global name-space
//
// Note: local space is dynamic,
// however a global var-ID per var-name is required
//
strcpy(name, tmp);
for (i = 0; i < comp_varcount; i++) {
if (strcmp(comp_vartable[i].name, name) == 0) {
idx = i;
break;
}
if (comp_vartable[i].dolar_sup) {
// system variables must be visible with or without '$' suffix
char *dollar_name = tmp_alloc(strlen(comp_vartable[i].name) + 2);
strcpy(dollar_name, comp_vartable[i].name);
strcat(dollar_name, "$");
if (strcmp(dollar_name, name) == 0) {
idx = i;
tmp_free(dollar_name);
break;
}
tmp_free(dollar_name);
}
}
if (idx == -1) {
// variable not found; create a new one
idx = comp_create_var(tmp);
}
return idx;
}
/*
* returns true if 'name' is a user defined structure
*/
int comp_check_uds(const char *name) {
int i;
comp_struct_t uds;
for (i = 0; i < comp_udscount; i++) {
dbt_read(comp_udstable, i, &uds, sizeof(comp_struct_t));
if (uds.field_id == -1 && strcasecmp(uds.name, name) == 0) {
return 1;
}
}
return 0;
}
/*
* returns the shared field_id for the given field name
*/
int comp_get_uds_field_id(const char *field_name, int len) {
int i;
comp_struct_t uds;
for (i = 0; i < comp_udscount; i++) {
dbt_read(comp_udstable, i, &uds, sizeof(comp_struct_t));
if (uds.field_id != -1 && strlen(uds.name) == len && strncasecmp(uds.name, field_name, len) == 0) {
return uds.field_id;
}
}
strncpy(uds.name, field_name, len);
uds.name[len] = 0;
uds.field_id = ++comp_next_field_id;
dbt_write(comp_udstable, comp_udscount, &uds, sizeof(comp_struct_t));
comp_udscount++;
return uds.field_id;
}
/*
* add the named variable to the current position in the byte code stream
*
* if the name 'foo' has already been used in a struct context, eg 'foo.x'
* then the foo variable is added as kwTYPE_UDS.
*
*/
void comp_add_variable(bc_t * bc, const char *var_name) {
char *dot = strchr(var_name, '.');
if (dot != 0 && !comp_check_lib(var_name)) {
// uds-element (or sub-element eg foo.x.y.z)
// record the uds-parent
int len = dot - var_name;
comp_struct_t uds;
strncpy(uds.name, var_name, len);
uds.name[len] = 0;
uds.field_id = -1;
dbt_write(comp_udstable, comp_udscount, &uds, sizeof(comp_struct_t));
comp_udscount++;
bid_t var_id = comp_var_getID(uds.name);
bc_add_code(bc, kwTYPE_UDS);
bc_add_addr(bc, var_id);
while (dot && dot[0]) {
char *dot_end = strchr(dot + 1, '.');
if (dot_end) {
// next sub-element
len = (dot_end - dot) - 1;
var_id = comp_get_uds_field_id(dot + 1, len);
dot = dot_end;
} else {
// final element
len = strlen(dot + 1);
var_id = comp_get_uds_field_id(dot + 1, len);
dot = 0;
}
bc_add_code(bc, kwTYPE_UDS_EL);
bc_add_addr(bc, var_id);
}
} else if (comp_check_uds(var_name)) {
// uds-container
// all of var_name same as dot-less portion of existing variable
bc_add_code(bc, kwTYPE_UDS);
bc_add_addr(bc, comp_var_getID(var_name));
} else {
// regular variable
bc_add_code(bc, kwTYPE_VAR);
bc_add_addr(bc, comp_var_getID(var_name));
}
}
/*
* adds a mark in stack at the current code position
*/
void comp_push(addr_t ip) {
comp_pass_node_t node;
strcpy(node.sec, comp_bc_sec);
node.pos = ip;
node.level = comp_block_level;
node.block_id = comp_block_id;
node.line = comp_line;
dbt_write(comp_stack, comp_sp, &node, sizeof(comp_pass_node_t));
comp_sp++;
}
/*
* returns the keyword code
*/
int comp_is_keyword(const char *name) {
int i, idx;
byte dolar_sup = 0;
// Code to enable the $ but not for keywords (INKEY$=INKEY,
// PRINT$=PRINT !!!)
// I don't want to increase the size of keywords table.
idx = strlen(name) - 1;
if (name[idx] == '$') {
*((char *)(name + idx)) = '\0';
dolar_sup++;
}
for (i = 0; keyword_table[i].name[0] != '\0'; i++) {
if (strcmp(keyword_table[i].name, name) == 0) {
return keyword_table[i].code;
}
}
if (dolar_sup) {
*((char *)(name + idx)) = '$';
}
return -1;
}
/*
* returns the keyword code (buildin functions)
*/
fcode_t comp_is_func(const char *name) {
fcode_t i;
int idx;
byte dolar_sup = 0;
// Code to enable the $ but not for keywords (INKEY$=INKEY,
// PRINT$=PRINT !!!)
// I don't want to increase the size of keywords table.
idx = strlen(name) - 1;
if (name[idx] == '$') {
*((char *)(name + idx)) = '\0';
dolar_sup++;
}
for (i = 0; func_table[i].name[0] != '\0'; i++) {
if (strcmp(func_table[i].name, name) == 0) {
return func_table[i].fcode;
}
}
if (dolar_sup) {
*((char *)(name + idx)) = '$';
}
return -1;
}
/*
* returns the keyword code (buildin procedures)
*/
pcode_t comp_is_proc(const char *name) {
pcode_t i;
for (i = 0; proc_table[i].name[0] != '\0'; i++) {
if (strcmp(proc_table[i].name, name) == 0) {
return proc_table[i].pcode;
}
}
return -1;
}
/*
* returns the keyword code (special separators)
*/
int comp_is_special_operator(const char *name) {
int i;
for (i = 0; spopr_table[i].name[0] != '\0'; i++) {
if (strcmp(spopr_table[i].name, name) == 0) {
return spopr_table[i].code;
}
}
return -1;
}
/*
* returns the keyword code (operators)
*/
long comp_is_operator(const char *name) {
int i;
for (i = 0; opr_table[i].name[0] != '\0'; i++) {
if (strcmp(opr_table[i].name, name) == 0) {
return ((opr_table[i].code << 8) | opr_table[i].opr);
}
}
return -1;
}
/*
*/
char *comp_next_char(char *source) {
char *p = source;
while (*p) {
if (*p != ' ') {
return p;
}
p++;
}
return p;
}
/*
*/
char *comp_prev_char(const char *root, const char *ptr) {
char *p = (char *)ptr;
if (p > root) {
p--;
} else {
return (char *)root;
}
while (p > root) {
if (*p != ' ') {
return p;
}
p--;
}
return p;
}
/**
* get next word
* if buffer's len is zero, then the next element is not a word
*
* @param text the source
* @param dest the buffer to store the result
* @return pointer of text to the next element
*/
const char *comp_next_word(const char *text, char *dest) {
const char *p = text;
char *d = dest;
if (p == NULL) {
*dest = '\0';
return 0;
}
while (is_space(*p)) {
p++;
}
if (*p == '?') {
strcpy(dest, LCN_PRINT);
p++;
while (is_space(*p)) {
p++;
}
return p;
}
if (*p == '\'' || *p == '#') {
strcpy(dest, LCN_REM);
p++;
while (is_space(*p)) {
p++;
}
return p;
}
if (is_alnum(*p) || *p == '_') { // don't forget the numeric-labels
while (is_alnum(*p) || (*p == '_') || (*p == '.')) {
*d = *p;
d++;
p++;
}
}
// Code to kill the $
// if ( *p == '$' )
// p ++;
// Code to enable the $
if (*p == '$') {
*d++ = *p++;
}
*d = '\0';
while (is_space(*p)) {
p++;
}
return p;
}
/*
* skips past any leading empty "()" parentheses characters
*/
char *trim_empty_parentheses(char *text) {
char *result = text;
char *next = comp_next_char(text);
if (*next == '(') {
next = comp_next_char(next + 1);
if (*next == ')') {
result = comp_next_char(next + 1);
}
}
return result;
}
/*
* return whether the given name is a func or sub
*/
int comp_is_function(char *name) {
int result = 0;
if (comp_is_proc(name) != -1 ||
comp_is_func(name) != -1 ||
comp_is_external_proc(name) != -1 ||
comp_is_external_func(name) != -1 ||
comp_udp_id(comp_bc_name, 1) != -1) {
result = 1;
}
return result;
}
/*
* scan expression
*/
void comp_expression(char *expr, byte no_parser) {
char *ptr = (char *)expr;
long idx;
int level = 0, check_udf = 0;
int kw_exec_more = 0;
int tp;
addr_t stip, cip;
var_int_t lv = 0;
var_num_t dv = 0;
bc_t bc;
int addr_opr = 0;
comp_use_global_vartable = 0; // check local-variables first
str_alltrim(expr);
if (*ptr == '\0') {
return;
}
bc_create(&bc);
while (*ptr) {
if (is_digit(*ptr) || *ptr == '.' || (*ptr == '&' && strchr("XHOB", *(ptr + 1)))) {
// A CONSTANT NUMBER
ptr = get_numexpr(ptr, comp_bc_name, &tp, &lv, &dv);
switch (tp) {
case 1:
bc_add_cint(&bc, lv);
continue;
case 2:
bc_add_creal(&bc, dv);
continue;
default:
sc_raise(MSG_EXP_GENERR);
}
} else if (*ptr == '\'' /* || *ptr == '#' */) { // remarks
break;
} else if (is_alpha(*ptr) || *ptr == '?' || *ptr == '_') {
// A NAME
ptr = (char *)comp_next_word(ptr, comp_bc_name);
idx = comp_is_func(comp_bc_name);
// special case for INPUT
if (idx == kwINPUTF) {
if (*comp_next_char(ptr) != '(') {
idx = -1; // INPUT is SPECIAL SEPARATOR (OPEN...FOR
// INPUT...)
}
}
if (idx != -1) {
// IS A FUNCTION
if (!kw_noarg_func(idx)) {
if (*comp_next_char(ptr) != '(') {
sc_raise(MSG_BF_ARGERR, comp_bc_name);
}
}
if (idx == kwCALLCF) {
bc_add_code(&bc, kwTYPE_CALL_UDF);
bc_add_addr(&bc, idx); // place holder
bc_add_addr(&bc, 0); // return-variable ID
bc_add_code(&bc, kwTYPE_LEVEL_BEGIN);
bc_add_code(&bc, kwTYPE_CALL_PTR);
// next is address
// skip next ( since we already added
// kwTYPE_LEVEL_BEGIN
// to allow kwTYPE_CALL_PTR to be the next code
char *par = comp_next_char(ptr);
if (*par == '(') {
ptr = par + 1;
level++;
}
} else {
bc_add_fcode(&bc, idx);
}
check_udf++;
} else {
// CHECK SPECIAL SEPARATORS
idx = comp_is_special_operator(comp_bc_name);
if (idx != -1) {
if (idx == kwUSE) {
bc_add_code(&bc, idx);
bc_add_addr(&bc, 0);
bc_add_addr(&bc, 0);
comp_use_global_vartable = 1;
// all the next variables are global (needed for
// X)
check_udf++;
} else if (idx == kwDO) {
SKIP_SPACES(ptr);
if (strlen(ptr)) {
if (strlen(comp_do_close_cmd)) {
kw_exec_more = 1;
strcpy(comp_bc_tmp2, ptr);
strcat(comp_bc_tmp2, ":");
strcat(comp_bc_tmp2, comp_do_close_cmd);
strcpy(comp_do_close_cmd, "");
} else {
sc_raise(MSG_KEYWORD_DO_ERR);
}
}
break;
} else {
bc_add_code(&bc, idx);
}
} else {
// NOT A COMMAND, CHECK OPERATORS
idx = comp_is_operator(comp_bc_name);
if (idx != -1) {
bc_add_code(&bc, idx >> 8);
bc_add_code(&bc, idx & 0xFF);
} else {
// EXTERNAL FUNCTION
idx = comp_is_external_func(comp_bc_name);
if (idx != -1) {
bc_add_extfcode(&bc, comp_extfunctable[idx].lib_id, comp_extfunctable[idx].symbol_index);
} else {
idx = comp_is_keyword(comp_bc_name);
if (idx == -1) {
idx = comp_is_proc(comp_bc_name);
}
if (idx != -1) {
sc_raise(MSG_STATEMENT_ON_RIGHT, comp_bc_name);
} else {
// UDF OR VARIABLE
int udf = comp_udp_id(comp_bc_name, 1);
if (udf != -1) {
// UDF
if (addr_opr != 0) {
// pointer to UDF
bc_add_code(&bc, kwTYPE_PTR);
} else {
bc_add_code(&bc, kwTYPE_CALL_UDF);
}
check_udf++;
bc_add_addr(&bc, udf);
bc_add_addr(&bc, 0); // var place holder
ptr = trim_empty_parentheses(ptr);
} else {
// VARIABLE
if (addr_opr != 0) {
sc_raise("PTR to invalid SUB/FUNC");
kw_exec_more = level = 0;
break;
}
SKIP_SPACES(ptr);
if (*ptr == '(') {
if (*(ptr + 1) == ')') {
// null array
ptr += 2;
}
}
comp_add_variable(&bc, comp_bc_name);
}
} // kw
} // extf
} // opr
} // sp. sep
} // check sep
addr_opr = 0;
// end isalpha block
} else if (*ptr == ',' || *ptr == ';' || *ptr == '#') {
// parameter separator
bc_add_code(&bc, kwTYPE_SEP);
bc_add_code(&bc, *ptr);
ptr++;
} else if (*ptr == '\"') {
// string
ptr = bc_store_string(&bc, ptr);
} else if (*ptr == '[') { // code-defined array
ptr++;
level++;
bc_add_fcode(&bc, kwCODEARRAY);
bc_add_code(&bc, kwTYPE_LEVEL_BEGIN);
} else if (*ptr == '(') {
// parenthesis
level++;
bc_add_code(&bc, kwTYPE_LEVEL_BEGIN);
ptr++;
} else if (*ptr == ')' || *ptr == ']') {
// parenthesis
bc_add_code(&bc, kwTYPE_LEVEL_END);
level--;
ptr++;
if (*ptr == '.') {
ptr = comp_array_uds_field(ptr + 1, &bc);
}
} else if (is_space(*ptr)) {
// null characters
ptr++;
} else {
// operators
if (*ptr == '+' || *ptr == '-') {
bc_add_code(&bc, kwTYPE_ADDOPR);
bc_add_code(&bc, *ptr);
} else if (*ptr == '*' || *ptr == '/' || *ptr == '\\' || *ptr == '%') {
bc_add_code(&bc, kwTYPE_MULOPR);
bc_add_code(&bc, *ptr);
} else if (*ptr == '^') {
bc_add_code(&bc, kwTYPE_POWOPR);
bc_add_code(&bc, *ptr);
} else if (strncmp(ptr, "<=", 2) == 0 || strncmp(ptr, "=<", 2) == 0) {
bc_add_code(&bc, kwTYPE_CMPOPR);
bc_add_code(&bc, OPLOG_LE);
ptr++;
} else if (strncmp(ptr, ">=", 2) == 0 || strncmp(ptr, "=>", 2) == 0) {
bc_add_code(&bc, kwTYPE_CMPOPR);
bc_add_code(&bc, OPLOG_GE);
ptr++;
} else if (strncmp(ptr, "<>", 2) == 0 || strncmp(ptr, "!=", 2) == 0) {
bc_add_code(&bc, kwTYPE_CMPOPR);
bc_add_code(&bc, OPLOG_NE);
ptr++;
} else if (strncmp(ptr, "<<", 2) == 0) {
ptr += 2;
SKIP_SPACES(ptr);
if (strlen(ptr)) {
kw_exec_more = 1;
strcpy(comp_bc_tmp2, comp_bc_name);
strcat(comp_bc_tmp2, " << ");
strcat(comp_bc_tmp2, ptr);
} else {
sc_raise(MSG_OPR_APPEND_ERR);
}
break;
} else if (strncmp(ptr, "==", 2) == 0) {
// support == syntax to prevent java or c programmers
// getting used to single = thus causing embarrasing
// coding errors in their normal work :)
bc_add_code(&bc, kwTYPE_CMPOPR);
bc_add_code(&bc, *ptr);
ptr++;
} else if (*ptr == '=' || *ptr == '>' || *ptr == '<') {
bc_add_code(&bc, kwTYPE_CMPOPR);
bc_add_code(&bc, *ptr);
} else if (strncmp(ptr, "&&", 2) == 0 || strncmp(ptr, "||", 2) == 0) {
bc_add_code(&bc, kwTYPE_LOGOPR);
bc_add_code(&bc, *ptr);
ptr++;
} else if (*ptr == '&') {
bc_add_code(&bc, kwTYPE_LOGOPR);
bc_add_code(&bc, OPLOG_BAND);
} else if (*ptr == '|') {
bc_add_code(&bc, kwTYPE_LOGOPR);
bc_add_code(&bc, OPLOG_BOR);
} else if (*ptr == '~') {
bc_add_code(&bc, kwTYPE_UNROPR);
bc_add_code(&bc, OPLOG_INV);
} else if (*ptr == '!') {
bc_add_code(&bc, kwTYPE_UNROPR);
bc_add_code(&bc, *ptr);
} else if (*ptr == '@') {
addr_opr = 1;
} else {
sc_raise(MSG_WRONG_OPR, *ptr);
}
ptr++;
}
};
if (level) {
sc_raise(MSG_EXP_MIS_RP);
}
if (!comp_error) {
if (no_parser == 0) {
// optimization
bc_add_code(&bc, kwTYPE_EOC);
// printf("=== before:\n"); hex_dump(bc.ptr, bc.count);
expr_parser(&bc);
// printf("=== after:\n"); hex_dump(bc.ptr, bc.count);
}
if (bc.count) {
stip = comp_prog.count;
bc_append(&comp_prog, &bc); // merge code segments
// update pass2 stack-nodes
if (check_udf) {
cip = stip;
while ((cip = comp_search_bc(cip, kwUSE)) != INVALID_ADDR) {
comp_push(cip);
cip += (1 + ADDRSZ + ADDRSZ);
}
cip = stip;
while ((cip = comp_search_bc(cip, kwTYPE_CALL_UDF)) != INVALID_ADDR) {
comp_push(cip);
cip += (1 + ADDRSZ + ADDRSZ);
}
cip = stip;
while ((cip = comp_search_bc(cip, kwTYPE_PTR)) != INVALID_ADDR) {
comp_push(cip);
cip += (1 + ADDRSZ + ADDRSZ);
}
}
}
bc_eoc(&comp_prog);
}
// clean-up
comp_use_global_vartable = 0; // check local-variables first
bc_destroy(&bc);
// do additional steps
if (kw_exec_more) {
comp_text_line(comp_bc_tmp2);
}
}
/*
* Converts DATA commands to bytecode
*/
void comp_data_seg(char *source) {
char *ptr = source;
char *commap;
var_int_t lv = 0;
var_num_t dv = 0;
double sign = 1;
char *tmp = comp_bc_temp;
int quotes;
int tp;
while (*ptr) {
SKIP_SPACES(ptr);
if (*ptr == '\0') {
break;
} else if (*ptr == ',') {
bc_add_code(&comp_data, kwTYPE_EOC);
ptr++;
} else {
// find the end of the element
commap = ptr;
quotes = 0;
while (*commap) {
if (*commap == '\"') {
quotes = !quotes;
} else if ((*commap == ',') && (quotes == 0)) {
break;
}
commap++;
}
if (*commap == '\0') {
commap = NULL;
}
if (commap != NULL) {
*commap = '\0';
}
if ((*ptr == '-' || *ptr == '+') && strchr("0123456789.", *(ptr + 1))) {
if (*ptr == '-') {
sign = -1;
}
ptr++;
} else {
sign = 1;
}
if (is_digit(*ptr) || *ptr == '.' || (*ptr == '&' && strchr("XHOB", *(ptr + 1)))) {
// number - constant
ptr = get_numexpr(ptr, tmp, &tp, &lv, &dv);
switch (tp) {
case 1:
bc_add_cint(&comp_data, lv * sign);
break;
case 2:
bc_add_creal(&comp_data, dv * sign);
break;
default:
sc_raise(MSG_EXP_GENERR);
}
} else {
// add it as string
if (*ptr != '\"') {
strcpy(tmp, "\"");
strcat(tmp, ptr);
strcat(tmp, "\"");
bc_store_string(&comp_data, tmp);
if (commap) {
ptr = commap;
} else {
ptr = ptr + strlen(ptr);
}
} else {
ptr = bc_store_string(&comp_data, ptr);
}
}
if (commap != NULL) {
*commap = ',';
}
}
}
bc_add_code(&comp_data, kwTYPE_EOC);
// no bc_eoc
}
/*
* Scans the 'source' for "names" separated by 'delims' and returns
* the elements (pointer in source) into args array.
*
* Returns the number of items
*/
int comp_getlist(char *source, char_p_t * args, char *delims, int maxarg) {
char *p, *ps;
int count = 0;
ps = p = source;
while (*p) {
if (strchr(delims, *p)) {
*p = '\0';
SKIP_SPACES(ps);
args[count] = ps;
count++;
if (count == maxarg) {
if (*ps) {
sc_raise(MSG_PARNUM_LIMIT, maxarg);
}
return count;
}
ps = p + 1;
}
p++;
}
if (*ps) {
SKIP_SPACES(ps);
if (*ps) {
*p = '\0';
args[count] = ps;
count++;
}
}
return count;
}
/*
* returns a list of names
*
* the list is included between sep[0] and sep[1] characters
* each element is separated by 'delims' characters
*
* the 'source' is the raw string (null chars will be placed at the end of each name)
* the 'args' is the names (pointers on the 'source')
* maxarg is the maximum number of names (actually the size of args)
* the count is the number of names which are found by this routine.
*
* returns the next position in 'source' (after the sep[1])
*/
char *comp_getlist_insep(char *source, char_p_t * args, char *sep, char *delims, int maxarg, int *count) {
char *p;
char *ps;
int level = 1;
*count = 0;
p = strchr(source, sep[0]);
if (p) {
ps = p + 1;
p++;
while (*p) {
if (*p == sep[1]) {
level--;
if (level == 0) {
break;
}
} else if (*p == sep[0]) {
level++;
}
p++;
}
if (*p == sep[1]) {
*p = '\0';
if (strlen(ps)) {
SKIP_SPACES(ps);
if (strlen(ps)) {
*count = comp_getlist(ps, args, delims, maxarg);
} else {
sc_raise(MSG_NIL_PAR_ERR);
}
}
} else {
sc_raise(MSG_MISSING_CHAR, sep[1]);
}
} else {
p = source;
}
return p;
}
/*
* Single-line IFs
*
* converts the string from single-line IF to normal IF syntax
* returns true if there is a single-line IF.
*
* IF expr THEN ... ---> IF expr THEN (:) .... (:FI)
* IF expr THEN ... ELSE ... ---> IF expr THEN (:) .... (:ELSE:) ... (:FI)
*/
int comp_single_line_if(char *text) {
char *p = (char *)text; // *text points to 'expr'
char *pthen, *pelse;
char buf[SB_SOURCELINE_SIZE + 1];
if( comp_error) {
return 0;
}
pthen = p;
do {
pthen = strstr(pthen + 1, LCN_THEN_WS);
if (pthen) {
// store the expression
SKIP_SPACES(p);
strcpy(buf, p);
p = strstr(buf, LCN_THEN_WS);
*p = '\0';
// check for ':'
p = pthen + 6;
SKIP_SPACES(p);
if (*p != ':' && *p != '\0') {
// store the IF
comp_block_level++;
comp_block_id++;
comp_push(comp_prog.count);
bc_add_ctrl(&comp_prog, kwIF, 0, 0);
comp_expression(buf, 0);
if (comp_error) {
return 0;
}
// store EOC
bc_add_code(&comp_prog, kwTYPE_EOC);
// bc_eoc();
// auto-goto
p = pthen + 6;
SKIP_SPACES(p);
if (is_digit(*p)) {
// add goto
strcpy(buf, LCN_GOTO_WRS);
strcat(buf, p);
} else {
strcpy(buf, p);
}
// ELSE command
// If there are more inline-ifs (nested) the ELSE belongs
// to the first IF (that's an error)
pelse = strstr(buf + 1, LCN_ELSE);
if (pelse) {
do {
if ((*(pelse - 1) == ' ' || *(pelse - 1) == '\t')
&& (*(pelse + 4) == ' ' || *(pelse + 4) == '\t')) {
*pelse = '\0';
// scan the commands before ELSE
comp_text_line(buf);
// add EOC
bc_eoc(&comp_prog);
// auto-goto
strcpy(buf, LCN_ELSE);
strcat(buf, ":");
p = pelse + 4;
SKIP_SPACES(p);
if (is_digit(*p)) {
// add goto
strcat(buf, LCN_GOTO_WRS);
strcat(buf, p);
} else
strcat(buf, p);
//
break;
} else {
pelse = strstr(pelse + 1, LCN_ELSE);
}
} while (pelse != NULL);
}
// scan the rest commands
comp_text_line(buf);
// add EOC
bc_eoc(&comp_prog);
// add ENDIF
comp_push(comp_prog.count);
bc_add_ctrl(&comp_prog, kwENDIF, 0, 0);
comp_block_level--;
comp_block_id--;
return 1;
} else { // *p == ':'
return 0;
}
} else {
break;
}
} while (pthen != NULL);
return 0; // false
}
/**
* Referencing a UDS field via array, eg foo(10).x
*/
char *comp_array_uds_field(char *p, bc_t * bc) {
char *p_begin = p;
while (1) {
if (*p == 0 || !isalnum(*p)) {
int len = (p - p_begin);
if (len) {
bc_add_code(bc, kwTYPE_UDS_EL);
bc_add_addr(bc, comp_get_uds_field_id(p_begin, len));
}
if (*p == '.') {
p_begin = p + 1;
} else {
return p;
}
}
p++;
}
return p;
}
/*
* array's args
*/
void comp_array_params(char *src) {
char *p = src;
char *ss = NULL, *se = NULL;
int level = 0;
while (*p) {
switch (*p) {
case '(':
if (level == 0) {
ss = p;
}
level++;
break;
case ')':
level--;
if (level == 0) {
se = p;
// store this index
if (!ss) {
sc_raise(MSG_ARRAY_SE);
} else {
*ss = ' ';
*se = '\0';
bc_add_code(&comp_prog, kwTYPE_LEVEL_BEGIN);
comp_expression(ss, 0);
bc_store1(&comp_prog, comp_prog.count - 1, kwTYPE_LEVEL_END);
*ss = '(';
*se = ')';
ss = se = NULL;
}
if (*(p + 1) == '.') {
p = comp_array_uds_field(p + 2, &comp_prog);
}
} // lev = 0
break;
};
p++;
}
//
if (level > 0) {
sc_raise(MSG_ARRAY_MIS_RP);
} else if (level < 0) {
sc_raise(MSG_ARRAY_MIS_LP);
}
}
/*
* run-time options
*/
void comp_cmd_option(char *src) {
char *p = src;
if (CHKOPT(LCN_UICS_WRS)) {
bc_add_code(&comp_prog, kwOPTION);
bc_add_code(&comp_prog, OPTION_UICS);
p += 5;
while (is_space(*p)) {
p++;
}
if (CHKOPT(LCN_CHARS)) {
bc_add_addr(&comp_prog, OPTION_UICS_CHARS);
} else if (CHKOPT(LCN_PIXELS)) {
bc_add_addr(&comp_prog, OPTION_UICS_PIXELS);
} else {
sc_raise(MSG_OPT_UICS_ERR);
}
} else if (CHKOPT(LCN_BASE_WRS)) {
bc_add_code(&comp_prog, kwOPTION);
bc_add_code(&comp_prog, OPTION_BASE);
bc_add_addr(&comp_prog, xstrtol(src + 5));
} else if (CHKOPT(LCN_PCRE_CASELESS)) {
bc_add_code(&comp_prog, kwOPTION);
bc_add_code(&comp_prog, OPTION_MATCH);
bc_add_addr(&comp_prog, 2);
} else if (CHKOPT(LCN_PCRE)) {
bc_add_code(&comp_prog, kwOPTION);
bc_add_code(&comp_prog, OPTION_MATCH);
bc_add_addr(&comp_prog, 1);
} else if (CHKOPT(LCN_SIMPLE)) {
bc_add_code(&comp_prog, kwOPTION);
bc_add_code(&comp_prog, OPTION_MATCH);
bc_add_addr(&comp_prog, 0);
} else if (CHKOPT(LCN_PREDEF_WRS) || CHKOPT(LCN_IMPORT_WRS)) {
; // ignore it
} else {
sc_raise(MSG_OPTION_ERR, src);
}
}
int comp_error_if_keyword(const char *name) {
// check if keyword
if (!comp_error) {
if ((comp_is_func(name) >= 0) || (comp_is_proc(name) >= 0) || (comp_is_special_operator(name) >= 0)
|| (comp_is_keyword(name) >= 0) || (comp_is_operator(name) >= 0)) {
sc_raise(MSG_IT_IS_KEYWORD, name);
}
}
return comp_error;
}
/**
* stores export symbols (in pass2 will be checked again)
*/
void bc_store_exports(const char *slist) {
#if defined(OS_LIMITED)
char_p_t pars[32];
#else
char_p_t pars[256];
#endif
int count = 0, i;
char *newlist;
unit_sym_t sym;
newlist = (char *)tmp_alloc(strlen(slist) + 3);
strcpy(newlist, "(");
strcat(newlist, slist);
strcat(newlist, ")");
#if defined(OS_LIMITED)
comp_getlist_insep(newlist, pars, "()", ",", 32, &count);
#else
comp_getlist_insep(newlist, pars, "()", ",", 256, &count);
#endif
for (i = 0; i < count; i++) {
strcpy(sym.symbol, pars[i]);
dbt_write(comp_exptable, comp_expcount, &sym, sizeof(unit_sym_t));
comp_expcount++;
}
tmp_free(newlist);
}
/*
* PASS1: scan source line
*/
void comp_text_line(char *text) {
char *p;
char *lb_end;
char *last_cmd;
#if defined(OS_ADDR16)
int idx;
#else
long idx;
#endif
int sharp, ladd, linc, ldec, decl = 0, vattr;
int leqop;
char pname[SB_KEYWORD_SIZE + 1];
char vname[SB_KEYWORD_SIZE + 1];
if (comp_error) {
return;
}
str_alltrim(text);
p = text;
// EOL
if (*p == ':') {
p++;
comp_text_line(p);
return;
}
// remark
if (*p == '\'' || *p == '#') {
return;
}
// empty line
if (*p == '\0') {
return;
}
lb_end = p = (char *)comp_next_word(text, comp_bc_name);
last_cmd = p;
p = get_param_sect(p, ":", comp_bc_parm);
// check old style labels
if (is_all_digits(comp_bc_name)) {
str_alltrim(comp_bc_name);
idx = comp_label_getID(comp_bc_name);
comp_label_setip(idx);
if (comp_error) {
return;
}
// continue
last_cmd = p = (char *)comp_next_word(lb_end, comp_bc_name);
if (strlen(comp_bc_name) == 0) {
if (!p) {
return;
}
if (*p == '\0') {
return;
}
}
p = get_param_sect(p, ":", comp_bc_parm);
}
// what's this ?
idx = comp_is_keyword(comp_bc_name);
if (idx == kwREM) {
return; // remarks... return
}
if (idx == -1) {
idx = comp_is_proc(comp_bc_name);
if (idx != -1) {
if (idx == kwCALLCP) {
bc_add_code(&comp_prog, kwTYPE_CALL_UDP);
bc_add_addr(&comp_prog, idx); // place holder
bc_add_addr(&comp_prog, 0); // return-variable ID
bc_add_code(&comp_prog, kwTYPE_LEVEL_BEGIN);
// allow cmd_udp to find the initial var-ptr arg
bc_add_code(&comp_prog, kwTYPE_CALL_PTR);
char *next = trim_empty_parentheses(comp_bc_parm);
comp_expression(next, 0);
bc_add_code(&comp_prog, kwTYPE_LEVEL_END);
} else {
// simple buildin procedure
// there is no need to check it more...
// save it and return (go to next)
bc_add_pcode(&comp_prog, idx);
char *next = trim_empty_parentheses(comp_bc_parm);
comp_expression(next, 0);
}
if (*p == ':') { // command separator
bc_eoc(&comp_prog);
p++;
comp_text_line(p);
}
return;
}
}
if (idx == kwLET) { // old-style keyword LET
char *p;
idx = -1;
p = (char *)comp_next_word(comp_bc_parm, comp_bc_name);
strcpy(comp_bc_parm, p);
} else if (idx == kwDECLARE) { // declaration
char *p;
decl = 1;
p = (char *)comp_next_word(comp_bc_parm, comp_bc_name);
idx = comp_is_keyword(comp_bc_name);
if (idx == -1) {
idx = comp_is_proc(comp_bc_name);
}
strcpy(comp_bc_parm, p);
if (idx != kwPROC && idx != kwFUNC) {
sc_raise(MSG_USE_DECL);
return;
}
}
if (idx == kwREM) {
return;
}
sharp = (comp_bc_parm[0] == '#'); // if # -> file commands
ladd = (strncmp(comp_bc_parm, "<<", 2) == 0); // if << -> array,
// append
linc = (strncmp(comp_bc_parm, "++", 2) == 0);
ldec = (strncmp(comp_bc_parm, "--", 2) == 0);
if (comp_bc_parm[1] == '=' && strchr("-+/\\*^%&|", comp_bc_parm[0])) {
leqop = comp_bc_parm[0];
} else {
leqop = 0;
}
if ((comp_bc_parm[0] == '=' || ladd || linc || ldec || leqop) && (idx != -1)) {
sc_raise(MSG_IT_IS_KEYWORD, comp_bc_name);
return;
}
if ((idx == kwCONST) ||
((comp_bc_parm[0] == '=' ||
(comp_bc_parm[0] == '(' && !comp_is_function(comp_bc_name)) ||
ladd || linc || ldec || leqop) && (idx == -1))) {
//
// LET/CONST commands
//
char *parms = comp_bc_parm;
if (idx == kwCONST) {
// const a=10: b=10
p = (char *)comp_next_word(comp_bc_parm, comp_bc_name);
p = get_param_sect(p, ":", comp_bc_parm);
parms = comp_bc_parm;
bc_add_code(&comp_prog, kwCONST);
} else if (ladd) {
bc_add_code(&comp_prog, kwAPPEND);
parms += 2;
} else if (linc) {
bc_add_code(&comp_prog, kwLET);
strcpy(comp_bc_parm, "=");
strcat(comp_bc_parm, comp_bc_name);
strcat(comp_bc_parm, "+1");
} else if (ldec) {
bc_add_code(&comp_prog, kwLET);
strcpy(comp_bc_parm, "=");
strcat(comp_bc_parm, comp_bc_name);
strcat(comp_bc_parm, "-1");
} else if (leqop) {
char *buf;
int l;
// a += 10: b -= 10 etc
bc_add_code(&comp_prog, kwLET);
l = strlen(comp_bc_parm) + strlen(comp_bc_name) + 1;
buf = tmp_alloc(l);
memset(buf, 0, l);
strcpy(buf, "=");
strcat(buf, comp_bc_name);
buf[strlen(buf)] = leqop;
strcat(buf, comp_bc_parm + 2);
strcpy(comp_bc_parm, buf);
tmp_free(buf);
} else {
bc_add_code(&comp_prog, kwLET);
}
comp_error_if_keyword(comp_bc_name);
comp_add_variable(&comp_prog, comp_bc_name);
if (!comp_error) {
if (parms[0] == '(') {
char *p = strchr(parms, '=');
if (!p) {
sc_raise(MSG_LET_MISSING_EQ);
} else {
if (*comp_next_char(parms + 1) == ')') {
// its the variable's name only
comp_expression(p, 0);
} else {
// ARRAY (LEFT)
*p = '\0';
comp_array_params(parms);
*p = '=';
if (!comp_error) {
bc_add_code(&comp_prog, kwTYPE_CMPOPR);
bc_add_code(&comp_prog, '=');
comp_expression(p + 1, 0);
}
}
}
} else {
bc_add_code(&comp_prog, kwTYPE_CMPOPR);
bc_add_code(&comp_prog, '=');
comp_expression(parms + 1, 0);
}
}
} else {
// add generic command
#if defined(OS_LIMITED)
char_p_t pars[32];
#else
char_p_t pars[256];
#endif
char *lpar_ptr, *eq_ptr, *p, *p_do;
int keep_ip, udp, count, i;
switch (idx) {
case kwLABEL:
str_alltrim(comp_bc_parm);
idx = comp_label_getID(comp_bc_parm);
comp_label_setip(idx);
break;
case kwEXIT:
bc_add_code(&comp_prog, idx);
str_alltrim(comp_bc_parm);
if (strlen(comp_bc_parm) && comp_bc_parm[0] != '\'') {
idx = comp_is_special_operator(comp_bc_parm);
if (idx == kwFORSEP || idx == kwLOOPSEP || idx == kwPROCSEP || idx == kwFUNCSEP) {
bc_add_code(&comp_prog, idx);
} else {
sc_raise(MSG_EXIT_ERR);
}
} else {
bc_add_code(&comp_prog, 0);
}
break;
case kwDECLARE:
break;
case kwPROC:
case kwFUNC:
//
// USER-DEFINED PROCEDURES/FUNCTIONS
//
// single-line function (DEF FN)
if ((eq_ptr = strchr(comp_bc_parm, '='))) {
*eq_ptr = '\0';
}
// parameters start
if ((lpar_ptr = strchr(comp_bc_parm, '('))) {
*lpar_ptr = '\0';
}
comp_prepare_name(pname, baseof(comp_bc_parm, '/'), SB_KEYWORD_SIZE);
comp_error_if_keyword(baseof(comp_bc_parm, '/'));
if (decl) {
// its only a declaration (DECLARE)
if (comp_udp_getip(pname) == INVALID_ADDR) {
comp_add_udp(pname);
}
} else {
// func/sub
if (comp_udp_getip(pname) != INVALID_ADDR) {
sc_raise(MSG_UDP_ALREADY_EXISTS, pname);
} else {
// setup routine's address (and get an id)
int pidx;
if ((pidx = comp_udp_setip(pname, comp_prog.count)) == -1) {
pidx = comp_add_udp(pname);
comp_udp_setip(pname, comp_prog.count);
}
// put JMP to the next command after the END
// (now we just keep the rq space, pass2 will
// update that)
bc_add_code(&comp_prog, kwGOTO);
bc_add_addr(&comp_prog, 0);
bc_add_code(&comp_prog, 0);
comp_block_level++;
comp_block_id++;
// keep it in stack for 'pass2'
comp_push(comp_prog.count);
// store (FUNC/PROC) code
bc_add_code(&comp_prog, idx);
// func/proc name (also, update comp_bc_proc)
if (comp_proc_level) {
strcat(comp_bc_proc, "/");
strcat(comp_bc_proc, baseof(pname, '/'));
} else {
strcpy(comp_bc_proc, pname);
}
if (!comp_error) {
comp_proc_level++;
// if its a function,
// setup the code for the return-value
// (vid={F}/{F})
if (idx == kwFUNC) {
strcpy(comp_bc_tmp2, baseof(pname, '/'));
comp_udptable[pidx].vid = comp_var_getID(comp_bc_tmp2);
} else {
// procedure, no return value here
comp_udptable[pidx].vid = INVALID_ADDR;
}
// parameters
if (lpar_ptr) {
int i;
*lpar_ptr = '(';
#if defined(OS_LIMITED)
comp_getlist_insep(comp_bc_parm, pars, "()", ",", 32, &count);
#else
comp_getlist_insep(comp_bc_parm, pars, "()", ",", 256, &count);
#endif
bc_add_code(&comp_prog, kwTYPE_PARAM);
bc_add_code(&comp_prog, count);
for (i = 0; i < count; i++) {
if ((strncmp(pars[i], LCN_BYREF_WRS, 6) == 0) || (pars[i][0] == '@')) {
if (pars[i][0] == '@') {
comp_prepare_name(vname, pars[i] + 1, SB_KEYWORD_SIZE);
} else {
comp_prepare_name(vname, pars[i] + 6, SB_KEYWORD_SIZE);
}
vattr = 0x80;
} else {
comp_prepare_name(vname, pars[i], SB_KEYWORD_SIZE);
vattr = 0;
}
if (strchr(pars[i], '(')) {
vattr |= 1;
}
bc_add_code(&comp_prog, vattr);
bc_add_addr(&comp_prog, comp_var_getID(vname));
}
} else {
// no parameters
bc_add_code(&comp_prog, kwTYPE_PARAM);
// params
bc_add_code(&comp_prog, 0);
// pcount = 0
}
bc_eoc(&comp_prog); // EOC
// -----------------------------------------------
// scan for single-line function (DEF FN
// format)
if (eq_ptr && idx == kwFUNC) {
eq_ptr++; // *eq_ptr was '\0'
SKIP_SPACES(eq_ptr);
if (strlen(eq_ptr)) {
char *macro = tmp_alloc(SB_SOURCELINE_SIZE + 1);
sprintf(macro, "%s=%s:%s", pname, eq_ptr, LCN_END);
// run comp_text_line again
comp_text_line(macro);
tmp_free(macro);
} else {
sc_raise(MSG_MISSING_UDP_BODY);
}
}
}
}
}
break;
case kwLOCAL:
// local variables
#if defined(OS_LIMITED)
count = comp_getlist(comp_bc_parm, pars, ",", 32);
#else
count = comp_getlist(comp_bc_parm, pars, ",", 256);
#endif
bc_add_code(&comp_prog, kwTYPE_CRVAR);
bc_add_code(&comp_prog, count);
for (i = 0; i < count; i++) {
comp_prepare_name(vname, pars[i], SB_KEYWORD_SIZE);
bc_add_addr(&comp_prog, comp_var_getID(vname));
}
// handle same line variable assignment, eg local blah = foo
for (i = 0; i < count; i++) {
comp_prepare_name(vname, pars[i], SB_KEYWORD_SIZE);
if (strlen(vname) != strlen(pars[i])) {
// kwTYPE_LINE is required for executor
bc_add_code(&comp_prog, kwTYPE_LINE);
bc_add_addr(&comp_prog, comp_line);
comp_text_line(pars[i]);
}
}
break;
case kwREM:
return;
case kwEXPORT: // export
if (comp_unit_flag) {
bc_store_exports(comp_bc_parm);
} else {
sc_raise(MSG_UNIT_NAME_MISSING);
}
break;
case kwOPTION:
comp_cmd_option(comp_bc_parm);
break;
case kwGOTO:
str_alltrim(comp_bc_parm);
comp_push(comp_prog.count);
bc_add_code(&comp_prog, idx);
bc_add_addr(&comp_prog, comp_label_getID(comp_bc_parm));
bc_add_code(&comp_prog, comp_block_level);
break;
case kwGOSUB:
str_alltrim(comp_bc_parm);
bc_add_code(&comp_prog, idx);
bc_add_addr(&comp_prog, comp_label_getID(comp_bc_parm));
break;
case kwIF:
strcpy(comp_do_close_cmd, LCN_ENDIF);
// from here, we can scan for inline IF
if (comp_single_line_if(last_cmd)) {
// inline-IFs
return;
} else {
comp_block_level++;
comp_block_id++;
comp_push(comp_prog.count);
bc_add_ctrl(&comp_prog, idx, 0, 0);
comp_expression(comp_bc_parm, 0);
bc_add_code(&comp_prog, kwTYPE_EOC);
// bc_eoc();
}
break;
case kwON:
//
// ON x GOTO|GOSUB ...
//
idx = kwONJMP; // WARNING!
comp_push(comp_prog.count);
bc_add_ctrl(&comp_prog, idx, 0, 0);
if ((p = strstr(comp_bc_parm, LCN_GOTO_WS)) != NULL) {
bc_add_code(&comp_prog, kwGOTO);
// the command
*p = '\0';
p += 6;
keep_ip = comp_prog.count;
bc_add_code(&comp_prog, 0);
// the counter
// count = bc_scan_label_list(p);
#if defined(OS_LIMITED)
count = comp_getlist(p, pars, ",", 32);
#else
count = comp_getlist(p, pars, ",", 256);
#endif
for (i = 0; i < count; i++) {
bc_add_addr(&comp_prog, comp_label_getID(pars[i])); // IDs
}
if (count == 0) {
sc_raise(MSG_ON_GOTO_ERR);
} else {
comp_prog.ptr[keep_ip] = count;
}
comp_expression(comp_bc_parm, 0); // the expression
bc_eoc(&comp_prog);
} else if ((p = strstr(comp_bc_parm, LCN_GOSUB_WS)) != NULL) {
bc_add_code(&comp_prog, kwGOSUB);
// the command
*p = '\0';
p += 7;
keep_ip = comp_prog.count;
bc_add_code(&comp_prog, 0);
// the counter
// count = bc_scan_label_list(p);
#if defined(OS_LIMITED)
count = comp_getlist(p, pars, ",", 32);
#else
count = comp_getlist(p, pars, ",", 256);
#endif
for (i = 0; i < count; i++) {
bc_add_addr(&comp_prog, comp_label_getID(pars[i]));
}
if (count == 0) {
sc_raise(MSG_ON_GOSUB_ERR);
} else {
comp_prog.ptr[keep_ip] = count;
}
comp_expression(comp_bc_parm, 0); // the expression
bc_eoc(&comp_prog);
} else {
sc_raise(MSG_ON_NOTHING);
}
break;
case kwFOR:
//
// FOR
//
p = strchr(comp_bc_parm, '=');
p_do = strstr(comp_bc_parm, LCN_DO_WS);
// fix DO bug
if (p_do) {
if (p > p_do) {
p = NULL;
}
}
strcpy(comp_do_close_cmd, LCN_NEXT);
comp_block_level++;
comp_block_id++;
comp_push(comp_prog.count);
bc_add_ctrl(&comp_prog, kwFOR, 0, 0);
if (!p) {
// FOR [EACH] X IN Y
if ((p = strstr(comp_bc_parm, LCN_IN_WS)) == NULL) {
sc_raise(MSG_FOR_NOTHING);
} else {
*p = '\0';
char *n = p;
strcpy(comp_bc_name, comp_bc_parm);
str_alltrim(comp_bc_name);
if (!is_alpha(*comp_bc_name)) {
sc_raise(MSG_FOR_COUNT_ERR, comp_bc_name);
} else {
char *p_lev = comp_bc_name;
while (is_alnum(*p_lev) || *p_lev == ' ') {
p_lev++;
}
if (*p_lev == '(') {
sc_raise(MSG_FOR_ARR_COUNT, comp_bc_name);
} else {
if (!comp_error_if_keyword(comp_bc_name)) {
comp_add_variable(&comp_prog, comp_bc_name);
*n = ' ';
bc_add_code(&comp_prog, kwIN);
comp_expression(n + 4, 0);
}
}
}
}
} else {
// FOR X=Y TO Z [STEP L]
*p = '\0';
char *n = p;
strcpy(comp_bc_name, comp_bc_parm);
str_alltrim(comp_bc_name);
if (!is_alpha(*comp_bc_name)) {
sc_raise(MSG_FOR_COUNT_ERR, comp_bc_name);
} else {
char *p_lev = comp_bc_name;
while (is_alnum(*p_lev) || *p_lev == ' ') {
p_lev++;
}
if (*p_lev == '(') {
sc_raise(MSG_FOR_ARR_COUNT, comp_bc_name);
} else {
if (!comp_error_if_keyword(comp_bc_name)) {
comp_add_variable(&comp_prog, comp_bc_name);
*n = '=';
comp_expression(n + 1, 0);
}
}
}
}
break;
case kwWHILE:
strcpy(comp_do_close_cmd, LCN_WEND);
comp_block_level++;
comp_block_id++;
comp_push(comp_prog.count);
bc_add_ctrl(&comp_prog, idx, 0, 0);
comp_expression(comp_bc_parm, 0);
break;
case kwREPEAT:
// WHILE & REPEAT DOES NOT USE STACK
comp_block_level++;
comp_block_id++;
comp_push(comp_prog.count);
bc_add_ctrl(&comp_prog, idx, 0, 0);
comp_expression(comp_bc_parm, 0);
break;
case kwSELECT:
comp_block_level++;
comp_block_id++;
comp_push(comp_prog.count);
bc_add_code(&comp_prog, idx);
// if comp_bc_parm starts with "CASE ", then skip first 5 chars
int index = strncasecmp("CASE ", comp_bc_parm, 5) == 0 ? 5 : 0;
comp_expression(comp_bc_parm + index, 0);
break;
case kwCASE:
// link to matched block or next CASE/END-SELECT
if (!comp_bc_parm || !comp_bc_parm[0] || strncasecmp(LCN_ELSE, comp_bc_parm, 4) == 0) {
comp_push(comp_prog.count);
bc_add_ctrl(&comp_prog, kwCASE_ELSE, 0, 0);
} else {
comp_push(comp_prog.count);
bc_add_ctrl(&comp_prog, idx, 0, 0);
comp_expression(comp_bc_parm, 0);
}
break;
case kwELSE:
case kwELIF: {
int index = 0;
// handle "ELSE IF"
if (idx == kwELSE && strncasecmp(LCN_IF, comp_bc_parm, 2) == 0) {
idx = kwELIF;
index = 2;
}
// handle error for ELSE xxxx
if (idx == kwELSE && comp_bc_parm[0]) {
sc_raise(ERR_SYNTAX);
break;
}
comp_push(comp_prog.count);
bc_add_ctrl(&comp_prog, idx, 0, 0);
comp_expression(comp_bc_parm + index, 0);
}
break;
case kwENDIF:
case kwNEXT:
comp_push(comp_prog.count);
bc_add_ctrl(&comp_prog, idx, 0, 0);
comp_block_level--;
comp_block_id--;
break;
case kwWEND:
case kwUNTIL:
comp_push(comp_prog.count);
bc_add_ctrl(&comp_prog, idx, 0, 0);
comp_block_level--;
comp_block_id--;
comp_expression(comp_bc_parm, 0);
break;
case kwSTEP:
case kwTO:
case kwIN:
case kwTHEN:
case kwCOS:
case kwSIN:
case kwLEN:
case kwLOOP: // functions...
sc_raise(MSG_SPECIAL_KW_ERR, comp_bc_name);
break;
case kwRESTORE:
comp_push(comp_prog.count);
bc_add_code(&comp_prog, idx);
bc_add_addr(&comp_prog, comp_label_getID(comp_bc_parm));
break;
case kwEND:
if (strncmp(comp_bc_parm, LCN_IF, 2) == 0 || strncmp(comp_bc_parm, LCN_SELECT, 6) == 0) {
idx = strncmp(comp_bc_parm, LCN_IF, 2) == 0 ? kwENDIF : kwENDSELECT;
comp_push(comp_prog.count);
bc_add_ctrl(&comp_prog, idx, 0, 0);
comp_block_level--;
comp_block_id--;
} else if (comp_proc_level) {
char *dol;
// UDP/F RETURN
dol = strrchr(comp_bc_proc, '/');
if (dol) {
*dol = '\0';
} else {
*comp_bc_proc = '\0';
}
comp_push(comp_prog.count);
bc_add_code(&comp_prog, kwTYPE_RET);
comp_proc_level--;
comp_block_level--;
comp_block_id++; // advance to next block
} else {
// END OF PROG
bc_add_code(&comp_prog, idx);
}
break;
case kwDATA:
comp_data_seg(comp_bc_parm);
break;
case kwREAD:
bc_add_code(&comp_prog, sharp ? kwFILEREAD : idx);
comp_expression(comp_bc_parm, 0);
break;
case kwINPUT:
bc_add_code(&comp_prog, sharp ? kwFILEINPUT : idx);
comp_expression(comp_bc_parm, 0);
break;
case kwPRINT:
bc_add_code(&comp_prog, sharp ? kwFILEPRINT : idx);
comp_expression(comp_bc_parm, 0);
break;
case kwLINE:
if (strncmp(comp_bc_parm, LCN_INPUT_WRS, 6) == 0) {
bc_add_code(&comp_prog, kwLINEINPUT);
comp_expression(comp_bc_parm + 6, 0);
} else {
bc_add_code(&comp_prog, idx);
comp_expression(comp_bc_parm, 0);
}
break;
case -1:
// EXTERNAL OR USER-DEFINED PROCEDURE
udp = comp_is_external_proc(comp_bc_name);
if (udp > -1) {
bc_add_extpcode(&comp_prog, comp_extproctable[udp].lib_id,
comp_extproctable[udp].symbol_index);
bc_add_code(&comp_prog, kwTYPE_LEVEL_BEGIN);
char *next = trim_empty_parentheses(comp_bc_parm);
comp_expression(next, 0);
bc_add_code(&comp_prog, kwTYPE_LEVEL_END);
} else {
udp = comp_udp_id(comp_bc_name, 1);
if (udp == -1) {
udp = comp_add_udp(comp_bc_name);
}
comp_push(comp_prog.count);
bc_add_ctrl(&comp_prog, kwTYPE_CALL_UDP, udp, 0);
bc_add_code(&comp_prog, kwTYPE_LEVEL_BEGIN);
char *next = trim_empty_parentheses(comp_bc_parm);
comp_expression(next, 0);
bc_add_code(&comp_prog, kwTYPE_LEVEL_END);
}
break;
default:
// something else
bc_add_code(&comp_prog, idx);
comp_expression(comp_bc_parm, 0);
}
}
if (*p == ':') {
// command separator
bc_eoc(&comp_prog);
p++;
comp_text_line(p);
}
}
/*
* skip command bytes
*/
addr_t comp_next_bc_cmd(addr_t ip) {
code_t code;
#if defined(ADDR16)
word len;
#else
dword len;
#endif
code = comp_prog.ptr[ip];
ip++;
switch (code) {
case kwTYPE_INT: // integer
ip += OS_INTSZ;
break;
case kwTYPE_NUM: // number
ip += OS_REALSZ;
break;
case kwTYPE_STR: // string: [2/4B-len][data]
memcpy(&len, comp_prog.ptr + ip, OS_STRLEN);
len += OS_STRLEN;
ip += len;
break;
case kwTYPE_CALLF:
case kwTYPE_CALLP: // [fcode_t]
ip += CODESZ;
break;
case kwTYPE_CALLEXTF:
case kwTYPE_CALLEXTP: // [lib][index]
ip += (ADDRSZ * 2);
break;
case kwEXIT:
case kwTYPE_SEP:
case kwTYPE_LOGOPR:
case kwTYPE_CMPOPR:
case kwTYPE_ADDOPR:
case kwTYPE_MULOPR:
case kwTYPE_POWOPR:
case kwTYPE_UNROPR: // [1B data]
ip++;
break;
case kwRESTORE:
case kwGOSUB:
case kwTYPE_LINE:
case kwTYPE_VAR: // [addr|id]
case kwTYPE_UDS:
case kwTYPE_UDS_EL:
ip += ADDRSZ;
break;
case kwTYPE_PTR:
case kwTYPE_CALL_UDP:
case kwTYPE_CALL_UDF: // [true-ip][false-ip]
ip += BC_CTRLSZ;
break;
case kwGOTO: // [addr][pop-count]
ip += (ADDRSZ + 1);
break;
case kwTYPE_CRVAR: // [1B count][addr1][addr2]...
len = comp_prog.ptr[ip];
ip += ((len * ADDRSZ) + 1);
break;
case kwTYPE_PARAM: // [1B count] {[1B-pattr][addr1]} ...
len = comp_prog.ptr[ip];
ip += ((len * (ADDRSZ + 1)) + 1);
break;
case kwONJMP: // [true-ip][false-ip] [GOTO|GOSUB]
// [count] [addr1]...
ip += (BC_CTRLSZ + 1);
ip += (comp_prog.ptr[ip] * ADDRSZ);
break;
case kwOPTION: // [1B-optcode][addr-data]
ip += (ADDRSZ + 1);
break;
case kwIF:
case kwFOR:
case kwWHILE:
case kwREPEAT:
case kwELSE:
case kwELIF:
case kwENDIF:
case kwNEXT:
case kwWEND:
case kwUNTIL:
case kwUSE:
case kwCASE:
case kwCASE_ELSE:
case kwENDSELECT:
ip += BC_CTRLSZ;
break;
case kwTYPE_EVAL_SC:
ip += 2; // kwTYPE_LOGOPR+op
ip += ADDRSZ; // the shortcut address
break;
};
return ip;
}
/*
* search for command (in byte-code)
*/
addr_t comp_search_bc(addr_t ip, code_t code) {
addr_t i = ip;
do {
if (code == comp_prog.ptr[i]) {
return i;
}
i = comp_next_bc_cmd(i);
} while (i < comp_prog.count);
return INVALID_ADDR;
}
/*
* search for End-Of-Command mark
*/
addr_t comp_search_bc_eoc(addr_t ip) {
addr_t i = ip;
code_t code;
do {
code = comp_prog.ptr[i];
if (code == kwTYPE_EOC || code == kwTYPE_LINE) {
return i;
}
i = comp_next_bc_cmd(i);
} while (i < comp_prog.count);
return comp_prog.count;
}
/*
* search stack
*/
addr_t comp_search_bc_stack(addr_t start, code_t code, byte level, bid_t block_id) {
addr_t i;
comp_pass_node_t node;
for (i = start; i < comp_sp; i++) {
dbt_read(comp_stack, i, &node, sizeof(comp_pass_node_t));
if (comp_prog.ptr[node.pos] == code) {
if (node.level == level && (block_id == -1 || block_id == node.block_id)) {
return node.pos;
}
}
}
return INVALID_ADDR;
}
/*
* search stack backward
*/
addr_t comp_search_bc_stack_backward(addr_t start, code_t code, byte level, bid_t block_id) {
addr_t i = start;
comp_pass_node_t node;
for (; i < comp_sp; i--) {
// WARNING: ITS UNSIGNED, SO WE'LL SEARCH
// IN RANGE [0..STK_COUNT]
dbt_read(comp_stack, i, &node, sizeof(comp_pass_node_t));
if (comp_prog.ptr[node.pos] == code) {
if (node.level == level && (block_id == -1 || block_id == node.block_id)) {
return node.pos;
}
}
}
return INVALID_ADDR;
}
/*
* inspect the byte-code at the given location
*/
addr_t comp_next_bc_peek(addr_t start) {
comp_pass_node_t node;
dbt_read(comp_stack, start, &node, sizeof(comp_pass_node_t));
return comp_prog.ptr[node.pos];
}
/*
* Advanced error messages:
* Analyze LOOP-END errors
*/
void print_pass2_stack(addr_t pos, code_t lcode, int level) {
#if !defined(OS_LIMITED)
addr_t ip;
#endif
addr_t i;
int j, cs_idx;
char cmd[16], cmd2[16];
comp_pass_node_t node;
code_t ccode[256], code;
int csum[256];
int cs_count;
code_t start_code[] = { kwWHILE, kwREPEAT, kwIF, kwFOR, kwFUNC, 0 };
code_t end_code[] = { kwWEND, kwUNTIL, kwENDIF, kwNEXT, kwTYPE_RET, 0 };
#if !defined(OS_LIMITED)
int details = 1;
#endif
char buff[256];
code = lcode;
kw_getcmdname(code, cmd);
// search for closest keyword (forward)
#if !defined(OS_LIMITED)
buff[0] = 0;
#if !defined(NO_SCAN_ERROR_PROMPT)
log_printf(MSG_DETAILED_REPORT_Q);
dev_gets(buff, sizeof(buff));
details = (buff[0] == 'y' || buff[0] == 'Y');
#endif
if (details) {
ip = comp_search_bc_stack(pos + 1, code, level - 1, -1);
if (ip == INVALID_ADDR) {
ip = comp_search_bc_stack(pos + 1, code, level + 1, -1);
if (ip == INVALID_ADDR) {
int cnt = 0;
for (i = pos + 1; i < comp_sp; i++) {
dbt_read(comp_stack, i, &node, sizeof(comp_pass_node_t));
if (comp_prog.ptr[node.pos] == code) {
log_printf("\n%s found on level %d (@%d) instead of %d (@%d+)\n", cmd, node.level, node.pos,
level, pos);
cnt++;
if (cnt > 3) {
break;
}
}
}
} else {
log_printf("\n%s found on level %d (@%d) instead of %d (@%d+)\n", cmd, level + 1, node.pos, level,
pos);
}
} else {
log_printf("\n%s found on level %d (@%d) instead of %d (@%d+)\n", cmd, level - 1, node.pos, level, pos);
}
}
#endif
// print stack
cs_count = 0;
#if !defined(OS_LIMITED)
if (details) {
log_printf("\n");
log_printf("--- Pass 2 - stack ------------------------------------------------------\n");
log_printf("%s%4s %16s %16s %6s %6s %5s %5s %5s\n", " ", " i", "Command", "Section", "Addr", "Line",
"Level", "BlkID", "Count");
log_printf("-------------------------------------------------------------------------\n");
}
#endif
for (i = 0; i < comp_sp; i++) {
dbt_read(comp_stack, i, &node, sizeof(comp_pass_node_t));
code = comp_prog.ptr[node.pos];
if (node.pos != INVALID_ADDR) {
kw_getcmdname(code, cmd);
} else {
strcpy(cmd, "---");
}
// sum
cs_idx = -1;
for (j = 0; j < cs_count; j++) {
if (ccode[j] == code) {
cs_idx = j;
csum[cs_idx]++;
break;
}
}
if (cs_idx == -1) {
cs_idx = cs_count;
cs_count++;
ccode[cs_idx] = code;
csum[cs_idx] = 1;
}
#if !defined(OS_LIMITED)
if (details) {
// info
log_printf("%s%4d: %16s %16s %6d %6d %5d %5d %5d\n", ((i == pos) ? ">>" : " "), i, cmd, node.sec, node.pos,
node.line, node.level, node.block_id, csum[cs_idx]);
}
#endif
}
// sum
#if !defined(OS_LIMITED)
if (details) {
log_printf("\n");
log_printf("--- Sum -----------------------------------------------------------------\n");
for (i = 0; i < cs_count; i++) {
code = ccode[i];
if (!kw_getcmdname(code, cmd))
sprintf(cmd, "(%d)", code);
log_printf("%16s - %5d\n", cmd, csum[i]);
}
}
#endif
// decide
log_printf("\n");
for (i = 0; start_code[i] != 0; i++) {
int sa, sb;
code_t ca, cb;
ca = start_code[i];
cb = end_code[i];
sa = 0;
for (j = 0; j < cs_count; j++) {
if (ccode[j] == ca)
sa = csum[j];
if (ca == kwFUNC) {
if (ccode[j] == kwPROC)
sa += csum[j];
}
}
sb = 0;
for (j = 0; j < cs_count; j++) {
if (ccode[j] == cb) {
sb = csum[j];
break;
}
}
if (sa - sb != 0) {
kw_getcmdname(ca, cmd);
kw_getcmdname(cb, cmd2);
if (sa > sb) {
log_printf("Hint: Missing %d %s or there is/are %d more %s\n", sa - sb, cmd2, sa - sb, cmd);
} else {
log_printf("Hint: There is/are %d more %s or missing %d %s\n", sb - sa, cmd2, sb - sa, cmd);
}
}
}
log_printf("\n\n");
}
/*
* PASS 2 (write jumps for IF,FOR,WHILE,REPEAT,etc)
*/
void comp_pass2_scan() {
addr_t i = 0, j, true_ip, false_ip, label_id, w;
addr_t a_ip, b_ip, c_ip, count;
code_t code;
byte level;
comp_pass_node_t node;
comp_label_t label;
if (!opt_quiet && !opt_interactive) {
#if defined(_UnixOS)
if (isatty(STDOUT_FILENO))
#endif
log_printf(MSG_PASS2_COUNT, i, comp_sp);
}
// for each node in stack
for (i = 0; i < comp_sp; i++) {
if (!opt_quiet && !opt_interactive) {
#if defined(_UnixOS)
if (isatty(STDOUT_FILENO))
#endif
if ((i % SB_KEYWORD_SIZE) == 0) {
log_printf(MSG_PASS2_COUNT, i, comp_sp);
}
}
dbt_read(comp_stack, i, &node, sizeof(comp_pass_node_t));
comp_line = node.line;
strcpy(comp_bc_sec, node.sec);
code = comp_prog.ptr[node.pos];
if (code == kwTYPE_EOC || code == kwTYPE_LINE) {
continue;
}
// debug (node.pos = the address of the error)
//
// if (node.pos == 360 || node.pos == 361)
// trace("=== stack code %d\n", code);
if (code != kwGOTO &&
code != kwRESTORE &&
code != kwSELECT &&
code != kwONJMP &&
code != kwTYPE_PTR &&
code != kwTYPE_CALL_UDP &&
code != kwTYPE_CALL_UDF &&
code != kwPROC &&
code != kwFUNC &&
code != kwTYPE_RET) {
// default - calculate true-ip
true_ip = comp_search_bc_eoc(node.pos + (BC_CTRLSZ + 1));
memcpy(comp_prog.ptr + node.pos + 1, &true_ip, ADDRSZ);
}
switch (code) {
case kwPROC:
case kwFUNC:
// update start's GOTO
true_ip = comp_search_bc_stack(i + 1, kwTYPE_RET, node.level, -1) + 1;
if (true_ip == INVALID_ADDR) {
sc_raise(MSG_UDP_MISSING_END);
print_pass2_stack(i, kwTYPE_RET, node.level);
return;
}
memcpy(comp_prog.ptr + node.pos - (ADDRSZ + 1), &true_ip, ADDRSZ);
break;
case kwRESTORE:
// replace the label ID with the real IP
memcpy(&label_id, comp_prog.ptr + node.pos + 1, ADDRSZ);
dbt_read(comp_labtable, label_id, &label, sizeof(comp_label_t));
count = comp_first_data_ip + label.dp;
memcpy(comp_prog.ptr + node.pos + 1, &count, ADDRSZ);
// change LABEL-ID with DataPointer
break;
case kwTYPE_PTR:
case kwTYPE_CALL_UDP:
case kwTYPE_CALL_UDF:
// update real IP
memcpy(&label_id, comp_prog.ptr + node.pos + 1, ADDRSZ);
true_ip = comp_udptable[label_id].ip + (ADDRSZ + 3);
memcpy(comp_prog.ptr + node.pos + 1, &true_ip, ADDRSZ);
// update return-var ID
true_ip = comp_udptable[label_id].vid;
memcpy(comp_prog.ptr + node.pos + (ADDRSZ + 1), &true_ip, ADDRSZ);
break;
case kwONJMP:
// kwONJMP:1 trueip:2 falseip:2 command:1 count:1 label1:2
// label2:2 ...
count = comp_prog.ptr[node.pos + (ADDRSZ + ADDRSZ + 2)];
true_ip = comp_search_bc_eoc(node.pos + BC_CTRLSZ + (count * ADDRSZ) + 3);
memcpy(comp_prog.ptr + node.pos + 1, &true_ip, ADDRSZ);
// change label IDs with the real IPs
for (j = 0; j < count; j++) {
memcpy(&label_id, comp_prog.ptr + node.pos + (j * ADDRSZ) + (ADDRSZ + ADDRSZ + 3), ADDRSZ);
dbt_read(comp_labtable, label_id, &label, sizeof(comp_label_t));
w = label.ip;
memcpy(comp_prog.ptr + node.pos + (j * ADDRSZ) + (ADDRSZ + ADDRSZ + 3), &w, ADDRSZ);
}
break;
case kwGOTO: // LONG JUMPS
memcpy(&label_id, comp_prog.ptr + node.pos + 1, ADDRSZ);
dbt_read(comp_labtable, label_id, &label, sizeof(comp_label_t));
w = label.ip;
memcpy(comp_prog.ptr + node.pos + 1, &w, ADDRSZ);
// change LABEL-ID with IP
level = comp_prog.ptr[node.pos + (ADDRSZ + 1)];
comp_prog.ptr[node.pos + (ADDRSZ + 1)] = 0; // number of POPs
if (level >= label.level) {
// number of POPs
comp_prog.ptr[node.pos + (ADDRSZ + 1)] = level - label.level;
} else {
// number of POPs
comp_prog.ptr[node.pos + (ADDRSZ + 1)] = 0;
}
break;
case kwFOR:
a_ip = comp_search_bc(node.pos + (ADDRSZ + ADDRSZ + 1), kwTO);
b_ip = comp_search_bc(node.pos + (ADDRSZ + ADDRSZ + 1), kwIN);
if (a_ip < b_ip) {
b_ip = INVALID_ADDR;
} else if (a_ip > b_ip) {
a_ip = b_ip;
}
false_ip = comp_search_bc_stack(i + 1, kwNEXT, node.level, -1);
if (false_ip == INVALID_ADDR) {
sc_raise(MSG_MISSING_NEXT);
print_pass2_stack(i, kwNEXT, node.level);
return;
}
if (a_ip > false_ip || a_ip == INVALID_ADDR) {
if (b_ip != INVALID_ADDR) {
sc_raise(MSG_MISSING_IN);
} else {
sc_raise(MSG_MISSING_TO);
}
return;
}
memcpy(comp_prog.ptr + node.pos + (ADDRSZ + 1), &false_ip, ADDRSZ);
break;
case kwWHILE:
false_ip = comp_search_bc_stack(i + 1, kwWEND, node.level, -1);
if (false_ip == INVALID_ADDR) {
sc_raise(MSG_MISSING_WEND);
print_pass2_stack(i, kwWEND, node.level);
return;
}
memcpy(comp_prog.ptr + node.pos + (ADDRSZ + 1), &false_ip, ADDRSZ);
break;
case kwREPEAT:
false_ip = comp_search_bc_stack(i + 1, kwUNTIL, node.level, -1);
if (false_ip == INVALID_ADDR) {
sc_raise(MSG_MISSING_UNTIL);
print_pass2_stack(i, kwUNTIL, node.level);
return;
}
memcpy(comp_prog.ptr + node.pos + (ADDRSZ + 1), &false_ip, ADDRSZ);
break;
case kwUSE:
true_ip = node.pos + (ADDRSZ + ADDRSZ + 1);
false_ip = comp_search_bc_eoc(true_ip);
memcpy(comp_prog.ptr + node.pos + 1, &true_ip, ADDRSZ);
memcpy(comp_prog.ptr + node.pos + (ADDRSZ + 1), &false_ip, ADDRSZ);
break;
case kwIF:
case kwELIF:
a_ip = comp_search_bc_stack(i + 1, kwENDIF, node.level, -1);
b_ip = comp_search_bc_stack(i + 1, kwELSE, node.level, -1);
c_ip = comp_search_bc_stack(i + 1, kwELIF, node.level, -1);
false_ip = a_ip;
if (b_ip != INVALID_ADDR && b_ip < false_ip) {
false_ip = b_ip;
}
if (c_ip != INVALID_ADDR && c_ip < false_ip) {
false_ip = c_ip;
}
if (false_ip == INVALID_ADDR) {
sc_raise(MSG_MISSING_ENDIF_OR_ELSE);
print_pass2_stack(i, kwENDIF, node.level);
return;
}
memcpy(comp_prog.ptr + node.pos + (ADDRSZ + 1), &false_ip, ADDRSZ);
break;
case kwELSE:
false_ip = comp_search_bc_stack(i + 1, kwENDIF, node.level, -1);
if (false_ip == INVALID_ADDR) {
sc_raise(MSG_MISSING_ENDIF);
print_pass2_stack(i, kwENDIF, node.level);
return;
}
memcpy(comp_prog.ptr + node.pos + (ADDRSZ + 1), &false_ip, ADDRSZ);
break;
case kwTYPE_RET:
break;
case kwWEND:
false_ip = comp_search_bc_stack_backward(i - 1, kwWHILE, node.level, -1);
if (false_ip == INVALID_ADDR) {
sc_raise(MSG_MISSING_WHILE);
print_pass2_stack(i, kwWHILE, node.level);
return;
}
memcpy(comp_prog.ptr + node.pos + (ADDRSZ + 1), &false_ip, ADDRSZ);
break;
case kwUNTIL:
false_ip = comp_search_bc_stack_backward(i - 1, kwREPEAT, node.level, -1);
if (false_ip == INVALID_ADDR) {
sc_raise(MSG_MISSING_REPEAT);
print_pass2_stack(i, kwREPEAT, node.level);
return;
}
memcpy(comp_prog.ptr + node.pos + (ADDRSZ + 1), &false_ip, ADDRSZ);
break;
case kwNEXT:
false_ip = comp_search_bc_stack_backward(i - 1, kwFOR, node.level, -1);
if (false_ip == INVALID_ADDR) {
sc_raise(MSG_MISSING_FOR);
print_pass2_stack(i, kwFOR, node.level);
return;
}
memcpy(comp_prog.ptr + node.pos + (ADDRSZ + 1), &false_ip, ADDRSZ);
break;
case kwENDIF:
false_ip = comp_search_bc_stack_backward(i - 1, kwIF, node.level, -1);
if (false_ip == INVALID_ADDR) {
sc_raise(MSG_MISSING_IF);
print_pass2_stack(i, kwIF, node.level);
return;
}
memcpy(comp_prog.ptr + node.pos + (ADDRSZ + 1), &false_ip, ADDRSZ);
break;
case kwSELECT:
// next instruction should be CASE
false_ip = comp_next_bc_peek(i + 1);
if (false_ip != kwCASE && false_ip != kwCASE_ELSE) {
sc_raise(MSG_MISSING_CASE);
print_pass2_stack(i, kwCASE, node.level);
return;
}
break;
case kwCASE:
// false path is either next case statement or "end select"
false_ip = comp_search_bc_stack(i + 1, kwCASE, node.level, node.block_id);
// avoid finding another CASE or CASE ELSE on the same level, but after END SELECT
j = comp_search_bc_stack(i + 1, kwENDSELECT, node.level, node.block_id);
if (false_ip == INVALID_ADDR || false_ip > j) {
false_ip = comp_search_bc_stack(i + 1, kwCASE_ELSE, node.level, node.block_id);
if (false_ip == INVALID_ADDR || false_ip > j) {
false_ip = j;
if (false_ip == INVALID_ADDR) {
sc_raise(MSG_MISSING_END_SELECT);
print_pass2_stack(i, kwCASE, node.level);
return;
}
}
}
// if expression returns false jump to the next case
memcpy(comp_prog.ptr + node.pos + (ADDRSZ + 1), &false_ip, ADDRSZ);
break;
case kwCASE_ELSE:
// check for END SELECT statement
false_ip = comp_search_bc_stack(i + 1, kwENDSELECT, node.level, node.block_id);
if (false_ip == INVALID_ADDR) {
sc_raise(MSG_MISSING_END_SELECT);
print_pass2_stack(i, kwCASE, node.level);
return;
}
// validate no futher CASE expr statements
j = comp_search_bc_stack(i + 1, kwCASE, node.level, node.block_id);
if (j != INVALID_ADDR && j < false_ip) {
sc_raise(MSG_CASE_CASE_ELSE);
print_pass2_stack(i, kwCASE, node.level);
return;
}
// validate no futher CASE ELSE expr statements
j = comp_search_bc_stack(i + 1, kwCASE_ELSE, node.level, node.block_id);
if (j != INVALID_ADDR && j < false_ip) {
sc_raise(MSG_CASE_CASE_ELSE);
print_pass2_stack(i, kwCASE_ELSE, node.level);
return;
}
// if the expression is false jump to the end-select
memcpy(comp_prog.ptr + node.pos + (ADDRSZ + 1), &false_ip, ADDRSZ);
break;
case kwENDSELECT:
false_ip = comp_search_bc_stack_backward(i - 1, kwSELECT, node.level, node.block_id);
if (false_ip == INVALID_ADDR) {
sc_raise(MSG_MISSING_SELECT);
print_pass2_stack(i, kwSELECT, node.level);
return;
}
break;
};
}
if (!opt_quiet && !opt_interactive) {
log_printf(MSG_PASS2_COUNT, comp_sp, comp_sp);
log_printf("\n");
}
}
/*
* initialize compiler
*/
void comp_init() {
comp_bc_sec = tmp_alloc(SB_KEYWORD_SIZE + 1);
memset(comp_bc_sec, 0, SB_KEYWORD_SIZE + 1);
comp_bc_name = tmp_alloc(SB_SOURCELINE_SIZE + 1);
comp_bc_parm = tmp_alloc(SB_SOURCELINE_SIZE + 1);
comp_bc_temp = tmp_alloc(SB_SOURCELINE_SIZE + 1);
comp_bc_tmp2 = tmp_alloc(SB_SOURCELINE_SIZE + 1);
comp_bc_proc = tmp_alloc(SB_SOURCELINE_SIZE + 1);
comp_line = 0;
comp_error = 0;
comp_labcount = 0;
comp_expcount = 0;
comp_impcount = 0;
comp_libcount = 0;
comp_varcount = 0;
comp_udscount = 0;
comp_next_field_id = 0;
comp_sp = 0;
comp_udpcount = 0;
comp_block_level = 0;
comp_block_id = 0;
comp_unit_flag = 0;
comp_first_data_ip = INVALID_ADDR;
comp_proc_level = 0;
comp_bc_proc[0] = '\0';
comp_vartable = (comp_var_t *)tmp_alloc(GROWSIZE * sizeof(comp_var_t));
comp_udptable = (comp_udp_t *)tmp_alloc(GROWSIZE * sizeof(comp_udp_t));
sprintf(comp_bc_temp, "SBI-LBL%d", ctask->tid);
comp_labtable = dbt_create(comp_bc_temp, 0);
sprintf(comp_bc_temp, "SBI-STK%d", ctask->tid);
comp_stack = dbt_create(comp_bc_temp, 0);
sprintf(comp_bc_temp, "SBI-ILB%d", ctask->tid);
comp_libtable = dbt_create(comp_bc_temp, 0);
sprintf(comp_bc_temp, "SBI-IMP%d", ctask->tid);
comp_imptable = dbt_create(comp_bc_temp, 0);
sprintf(comp_bc_temp, "SBI-EXP%d", ctask->tid);
comp_exptable = dbt_create(comp_bc_temp, 0);
sprintf(comp_bc_temp, "SBI-UDS%d", ctask->tid);
comp_udstable = dbt_create(comp_bc_temp, 0);
comp_varsize = comp_udpsize = GROWSIZE;
comp_varcount = comp_labcount = comp_sp = comp_udpcount = 0;
bc_create(&comp_prog);
bc_create(&comp_data);
#if !defined(_UnixOS)
if (!comp_vartable || comp_imptable == -1 || comp_libtable == -1 || comp_exptable == -1
|| comp_udstable == -1 || comp_labtable == -1 || !comp_udptable || comp_stack == -1)
panic("comp_init(): OUT OF MEMORY");
#else
assert(comp_vartable != 0);
assert(comp_imptable != -1);
assert(comp_libtable != -1);
assert(comp_exptable != -1);
assert(comp_labtable != -1);
assert(comp_udptable != 0);
assert(comp_stack != -1);
assert(comp_udstable != -1);
#endif
dbt_prealloc(comp_labtable, os_cclabs1, sizeof(comp_label_t));
dbt_prealloc(comp_stack, os_ccpass2, sizeof(comp_pass_node_t));
// create system variables
comp_var_getID(LCN_SV_OSVER);
comp_vartable[comp_var_getID(LCN_SV_OSNAME)].dolar_sup = 1;
comp_var_getID(LCN_SV_SBVER);
comp_var_getID(LCN_SV_PI);
comp_var_getID(LCN_SV_XMAX);
comp_var_getID(LCN_SV_YMAX);
comp_var_getID(LCN_SV_BPP);
comp_var_getID(LCN_SV_TRUE);
comp_var_getID(LCN_SV_FALSE);
comp_var_getID(LCN_SV_LINECHART);
comp_var_getID(LCN_SV_BARCHART);
comp_vartable[comp_var_getID(LCN_SV_CWD)].dolar_sup = 1;
comp_vartable[comp_var_getID(LCN_SV_HOME)].dolar_sup = 1;
comp_vartable[comp_var_getID(LCN_SV_COMMAND)].dolar_sup = 1;
comp_var_getID(LCN_SV_X); // USE keyword
comp_var_getID(LCN_SV_Y); // USE keyword
comp_var_getID(LCN_SV_Z); // USE keyword
comp_var_getID(LCN_SV_VADR);
}
/*
* clean up
*/
void comp_close() {
int i;
bc_destroy(&comp_prog);
bc_destroy(&comp_data);
for (i = 0; i < comp_varcount; i++) {
tmp_free(comp_vartable[i].name);
}
for (i = 0; i < comp_udpcount; i++) {
tmp_free(comp_udptable[i].name);
}
tmp_free(comp_vartable);
dbt_close(comp_labtable);
dbt_close(comp_exptable);
dbt_close(comp_imptable);
dbt_close(comp_libtable);
dbt_close(comp_stack);
dbt_close(comp_udstable);
tmp_free(comp_udptable);
comp_varcount = comp_labcount = comp_sp = comp_udpcount = 0;
comp_libcount = comp_impcount = comp_expcount = 0;
tmp_free(comp_bc_proc);
tmp_free(comp_bc_tmp2);
tmp_free(comp_bc_temp);
tmp_free(comp_bc_parm);
tmp_free(comp_bc_name);
tmp_free(comp_bc_sec);
comp_reset_externals();
}
/*
* returns true if the 'fileName' exists
*/
int comp_bas_exist(const char *basfile) {
int check = 0;
char *p, *fileName;
fileName = tmp_alloc(strlen(basfile) + 5);
strcpy(fileName, basfile);
p = strchr(fileName, '.');
if (!p) {
strcat(fileName, ".bas");
}
#if !defined(_UnixOS)
check = (access(fileName, 0) == 0);
#else
check = (access(fileName, R_OK) == 0);
#endif
tmp_free(fileName);
return check;
}
/*
* load a source file
*/
char *comp_load(const char *file_name) {
char *buf;
int h;
strcpy(comp_file_name, file_name);
#if defined(IMPL_DEV_READ)
buf = dev_read(file_name);
#else
h = open(comp_file_name, O_BINARY | O_RDONLY, 0644);
if (h == -1) {
buf = NULL;
#if defined(__CYGWIN__)
char temp[1024];
getcwd(temp, 1024);
panic(MSG_CANT_OPEN_FILE_AT, comp_file_name, temp);
#else
panic(MSG_CANT_OPEN_FILE, comp_file_name);
#endif
} else {
int size;
size = lseek(h, 0, SEEK_END);
lseek(h, 0, SEEK_SET);
buf = (char *)tmp_alloc(size + 1);
read(h, buf, size);
buf[size] = '\0';
close(h);
}
#endif
return buf;
}
/**
* format source-code text
*
* space-chars is the only the space
* CR/LF are fixed
* control chars are out
* remove remarks (')
*
* TODO: join-lines character (&)
*
* returns a newly created string
*/
char *comp_format_text(const char *source) {
const char *p;
char *ps;
int quotes = 0;
char *new_text;
int sl, last_ch = 0, i;
char *last_nonsp_ptr;
int adj_line_num = 0;
sl = strlen(source);
new_text = tmp_alloc(sl + 2);
memset(new_text, 0, sl + 2);
comp_line = 0;
p = source;
last_nonsp_ptr = ps = new_text;
while (*p) {
if (!quotes) {
switch (*p) {
case '\n': // new line
if (*last_nonsp_ptr == '&') { // join lines
p++;
*last_nonsp_ptr = ' ';
if (*(last_nonsp_ptr - 1) == ' ') {
ps = last_nonsp_ptr;
} else {
ps = last_nonsp_ptr + 1;
}
adj_line_num++;
} else {
for (i = 0; i <= adj_line_num; i++) {
*ps++ = '\n'; // at least one nl
}
adj_line_num = 0;
p++;
}
SKIP_SPACES(p);
comp_line++;
last_ch = '\n';
last_nonsp_ptr = ps - 1;
break;
case '\'': // remarks
// skip the rest line
while (*p) {
if (*p == '\n') {
break;
}
p++;
}
break;
case ' ': // spaces
case '\t':
if (last_ch == ' ' || last_ch == '\n') {
p++;
} else {
*ps++ = ' ';
p++;
last_ch = ' ';
}
break;
case '\"': // quotes
quotes = !quotes;
last_nonsp_ptr = ps;
*ps++ = last_ch = *p++;
break;
default:
if ((strcaselessn(p, LCN_REM_1, 5) == 0) || (strcaselessn(p, LCN_REM_2, 5) == 0)
|| (strcaselessn(p, LCN_REM_3, 4) == 0 && last_ch == '\n')
|| (strcaselessn(p, LCN_REM_4, 4) == 0 && last_ch == '\n')) {
// skip the rest line
while (*p) {
if (*p == '\n')
break;
p++;
}
break;
} else {
if ((*p > ' ') || (*p < 0)) { // simple
// code-character
last_nonsp_ptr = ps;
*ps++ = last_ch = to_upper(*p);
p++;
} else
p++;
// else ignore it
}
}
} else { // in quotes
if (*p == '\\' && *(p + 1) == '\"') {
// add the escaped quote and continue
*ps++ = *p++;
} else if (*p == '\"' || *p == '\n') {
// new line auto-ends the quoted string
quotes = !quotes;
}
*ps++ = *p++;
}
}
// close
*ps++ = '\n';
*ps = '\0';
return new_text;
}
/**
* scans prefered graphics mode paramaters
*
* syntax: XXXXxYYYY[xBB]
*/
void err_grmode() {
// log_printf() instead of sc_raise()... it is just a warning...
#if !defined(OS_LIMITED)
log_printf(MSG_GRMODE_ERR);
#endif
}
void comp_preproc_grmode(const char *source) {
char *p, *v;
int x, y, b;
char buffer[32];
// prepare the string (copy it to buffer)
// we use second buffer because we want to place some '\0' characters
// into the buffer
// in a non-SB code, there must be a dynamic allocation
strncpy(buffer, source, 32);
buffer[31] = '\0';
p = buffer;
// searching the end of the string
while (*p) { // while *p is not '\0'
if (*p == '\n' || *p == ':') { // yeap, we must close the string
// here (enter or
// command-seperator)
// it is supposed that remarks had already removed from source
*p = '\0'; // terminate the string
break;
}
p++; // next
}
// get parameters
p = buffer;
SKIP_SPACES(p);
// the width
v = p; // 'v' points to first letter of 'width',
// (1024x768)
// ........................................^ <- p, v
p = strchr(v, 'X'); // search for the end of 'width' parameter
//
//
// (1024x768). Remeber that the string is
// in upper-case
// .............................................^ <- p
if (!p) { // we don't accept one parameter, the
// width must followed by the height
// so, if 'X' delimiter is omitted, there is no height parameter
err_grmode();
return;
}
*p = '\0'; // we close the string at X position
// (example: "1024x768" it will be
// "1024\0768")
x = xstrtol(v); // now the v points to a string-of-digits,
//
//
// we can perform atoi()
// (xstrtol()=atoi())
p++;
v = p; // v points to first letter of 'height'
// (1024x768x24)
// ...........................................^ <- v
// the height
p = strchr(v, 'X'); // searching for the end of 'height'
// (1024x768x24)
// ...........................................^ <- p
if (p) { // if there is a 'X' delimiter, then the
// 'bpp' is followed, so, we need
// different path
*p = '\0'; // we close the string at second's X
// position
y = xstrtol(v); // now the v points to a string-of-digits,
//
//
// we can perform atoi()
// (xstrtol()=atoi())
p++;
v = p; // v points to first letter of 'bpp'
// (1024x768x24)
// ............................................^ <- v
// the bits-per-pixel
if (strlen(v)) // if *v != '\0', there is actually a
// string
b = xstrtol(v); // integer value of (v). v points to a
// string-of-digits...
// btw, if the user pass some wrong characters (like a-z), the
// xstrtol will return a value of zero
else
b = 0; // undefined = 0, user deserves a
// compile-time error becase v is empty,
// but we forgive him :)
// remember that, the source, except of upper-case, is also
// trimmed
} else { // there was no 'X' delimiter after the
// 'height', so, bpp is undefined
y = xstrtol(v); // now the v points to a string-of-digits,
//
//
// we can perform atoi()
// (xstrtol()=atoi())
b = 0; // bpp is undefined (value 0)
}
// setup the globals
opt_pref_width = x;
opt_pref_height = y;
opt_pref_bpp = b;
}
/**
* copy the unit name from the source string to the given buffer
*/
const char *get_unit_name(const char *p, char *buf_p) {
while (is_alnum(*p) || *p == '_' || *p == '.') {
if (*p == '.') {
*buf_p++ = OS_DIRSEP;
p++;
} else {
*buf_p++ = *p++;
}
}
*buf_p = '\0';
return p;
}
/**
* imports units
*/
void comp_preproc_import(const char *slist) {
const char *p;
char buf[OS_PATHNAME_SIZE + 1];int
uid;
bc_lib_rec_t imlib;
p = slist;
SKIP_SPACES(p);
while (is_alpha(*p)) {
// get name - "Import other.Foo => "other/Foo"
p = get_unit_name(p, buf);
// import name
strlower(buf);
if ((uid = slib_get_module_id(buf)) != -1) { // C module
// store lib-record
strcpy(imlib.lib, buf);
imlib.id = uid;
imlib.type = 0; // C module
slib_setup_comp(uid);
dbt_write(comp_libtable, comp_libcount, &imlib, sizeof(bc_lib_rec_t));
comp_libcount++;
} else { // SB unit
uid = open_unit(buf);
if (uid < 0) {
sc_raise(MSG_UNIT_NOT_FOUND, buf);
return;
}
if (import_unit(uid) < 0) {
sc_raise(MSG_IMPORT_FAILED, buf);
close_unit(uid);
return;
}
// store lib-record
strcpy(imlib.lib, buf);
imlib.id = uid;
imlib.type = 1; // unit
dbt_write(comp_libtable, comp_libcount, &imlib, sizeof(bc_lib_rec_t));
comp_libcount++;
// clean up
close_unit(uid);
}
// skip spaces and commas
while (*p == ' ' || *p == '\t' || *p == ',') {
p++;
}
}
}
/**
* makes the current line full of spaces
*/
void comp_preproc_remove_line(char *s, int cmd_sep_allowed) {
char *p = s;
if (cmd_sep_allowed) {
while (*p != '\n' && *p != ':') {
*p = ' ';
p++;
}
} else {
while (*p != '\n') {
*p = ' ';
p++;
}
}
}
/**
* prepare compiler for UNIT-source
*/
void comp_preproc_unit(char *name) {
const char *p = name;
SKIP_SPACES(p);
if (!is_alpha(*p)) {
sc_raise(MSG_INVALID_UNIT_NAME);
}
p = get_unit_name(p, comp_unit_name);
comp_unit_flag = 1;
SKIP_SPACES(p);
if (*p != '\n' && *p != ':') {
sc_raise(MSG_UNIT_ALREADY_DEFINED);
}
}
/**
* PASS 1
*/
int comp_pass1(const char *section, const char *text) {
char *ps, *p, lc = 0;
int i;
char pname[SB_KEYWORD_SIZE + 1];char
*code_line;
char *new_text;
int len_option, len_import, len_unit, len_unit_path, len_inc;
int len_sub, len_func, len_def, len_end, len_end_select;
code_line = tmp_alloc(SB_SOURCELINE_SIZE + 1);
memset(comp_bc_sec, 0, SB_KEYWORD_SIZE + 1);
if (section) {
strncpy(comp_bc_sec, section, SB_KEYWORD_SIZE);
} else {
strncpy(comp_bc_sec, SYS_MAIN_SECTION_NAME, SB_KEYWORD_SIZE);
}
new_text = comp_format_text(text);
// second (we can change it to support preprocessor)
// Check for:
// include (#inc:)
// units-dir (#unit-path:)
// IMPORT
// UDF and UDP declarations
// PREDEF OPTIONS
p = ps = new_text;
comp_proc_level = 0;
*comp_bc_proc = '\0';
len_option = strlen(LCN_OPTION);
len_import = strlen(LCN_IMPORT_WRS);
len_unit = strlen(LCN_UNIT_WRS);
len_unit_path = strlen(LCN_UNIT_PATH);
len_inc = strlen(LCN_INC);
len_sub = strlen(LCN_SUB_WRS);
len_func = strlen(LCN_FUNC_WRS);
len_def = strlen(LCN_DEF_WRS);
len_end = strlen(LCN_END_WRS);
len_end_select = strlen(LCN_END_SELECT);
while (*p) {
// OPTION environment parameters
if (strncmp(LCN_OPTION, p, len_option) == 0) {
p += len_option;
SKIP_SPACES(p);
if (strncmp(LCN_PREDEF, p, strlen(LCN_PREDEF)) == 0) {
p += strlen(LCN_PREDEF);
SKIP_SPACES(p);
if (strncmp(LCN_QUIET, p, strlen(LCN_QUIET)) == 0) {
opt_quiet = 1;
} else if (strncmp(LCN_GRMODE, p, strlen(LCN_GRMODE)) == 0) {
p += strlen(LCN_GRMODE);
comp_preproc_grmode(p);
opt_graphics = 1;
} else if (strncmp(LCN_TEXTMODE, p, strlen(LCN_TEXTMODE)) == 0) {
opt_graphics = 0;
} else if (strncmp(LCN_CSTR, p, strlen(LCN_CSTR)) == 0) {
opt_cstr = 1;
} else if (strncmp(LCN_COMMAND, p, strlen(LCN_COMMAND)) == 0) {
char *pe;
p += strlen(LCN_COMMAND);
SKIP_SPACES(p);
pe = p;
while (*pe != '\0' && *pe != '\n') {
pe++;
}
lc = *pe;
*pe = '\0';
if (strlen(p) < OPT_CMD_SZ) {
strcpy(opt_command, p);
} else {
memcpy(opt_command, p, OPT_CMD_SZ - 1);
opt_command[OPT_CMD_SZ - 1] = '\0';
}
*pe = lc;
} else {
sc_raise(MSG_OPT_PREDEF_ERR, p);
}
}
} else if (strncmp(LCN_IMPORT_WRS, p, len_import) == 0) {
// IMPORT units
comp_preproc_import(p + len_import);
comp_preproc_remove_line(p, 1);
} else if (strncmp(LCN_UNIT_WRS, p, len_unit) == 0) {
// UNIT name
if (comp_unit_flag) {
sc_raise(MSG_MANY_UNIT_DECL);
} else {
comp_preproc_unit(p + len_unit);
}
comp_preproc_remove_line(p, 1);
} else if (strncmp(LCN_UNIT_PATH, p, len_unit_path) == 0) {
// UNIT-PATH name
#if defined(_UnixOS) || defined(_DOS) || defined(_Win32)
char upath[SB_SOURCELINE_SIZE + 1], *up;
char *ps;
ps = p;
p += len_unit_path;
SKIP_SPACES(p);
if (*p == '\"') {
p++;
}
up = upath;
while (*p != '\n' && *p != '\"') {
*up++ = *p++;
}
*up = '\0';
sprintf(comp_bc_temp, "SB_UNIT_PATH=%s", upath);
putenv(strdup(comp_bc_temp));
p = ps;
comp_preproc_remove_line(p, 0);
#else // supported OSes
comp_preproc_remove_line(p, 0);
#endif
} else {
// INCLUDE FILE
// this is not a normal way but needs less memory
if (strncmp(LCN_INC, p, len_inc) == 0) {
char *crp = NULL;
p += len_inc;
if (*p == '\"') {
p++;
crp = p;
while (*crp != '\0' && *crp != '\"') {
crp++;
}
if (*crp == '\0') {
sc_raise(MSG_INC_MIS_DQ);
break;
}
(lc = *crp, *crp = '\0');
} else {
crp = strchr(p, '\n');
*crp = '\0';
lc = '\n';
}
strcpy(code_line, p);
*crp = lc;
str_alltrim(code_line);
if (!comp_bas_exist(code_line)) {
sc_raise(MSG_INC_FILE_DNE, comp_file_name, code_line);
} else {
char fileName[1024];
char sec[SB_KEYWORD_SIZE + 1];
strcpy(sec, comp_bc_sec);
strcpy(fileName, comp_file_name);
if (strchr(code_line, '.') == NULL) {
strcat(code_line, ".bas");
}
comp_load(code_line);
strcpy(comp_file_name, fileName);
strcpy(comp_bc_sec, sec);
}
}
if ((strncmp(LCN_SUB_WRS, p, len_sub) == 0) ||
(strncmp(LCN_FUNC_WRS, p, len_func) == 0)
|| (strncmp(LCN_DEF_WRS, p, len_def) == 0)) {
// SUB/FUNC/DEF - Automatic declaration - BEGIN
char *dp;
int single_line_f = 0;
if (strncmp(LCN_SUB_WRS, p, len_sub) == 0) {
p += len_sub;
} else if (strncmp(LCN_FUNC_WRS, p, len_func) == 0) {
p += len_func;
} else {
p += len_def;
}
SKIP_SPACES(p);
// copy proc/func name
dp = pname;
while (is_alnum(*p) || *p == '_') {
*dp++ = *p++;
}
*dp = '\0';
// search for '='
while (*p != '\n' && *p != '=') {
p++;
}
if (*p == '=') {
single_line_f = 1;
while (*p != '\n') {
p++;
}
}
// add declaration
if (comp_udp_getip(pname) == INVALID_ADDR) {
comp_add_udp(pname);
} else {
sc_raise(MSG_UDP_ALREADY_DECL, pname);
}
// func/proc name (also, update comp_bc_proc)
if (comp_proc_level) {
strcat(comp_bc_proc, "/");
strcat(comp_bc_proc, baseof(pname, '/'));
} else {
strcpy(comp_bc_proc, pname);
}
if (!single_line_f) {
comp_proc_level++;
} else {
// inline (DEF FN)
char *dol = strrchr(comp_bc_proc, '/');
if (dol) {
*dol = '\0';
} else {
*comp_bc_proc = '\0';
}
}
} else if (comp_proc_level) {
// SUB/FUNC/DEF - Automatic declaration - END
if (strncmp(LCN_END_WRS, p, len_end) == 0 || strncmp(LCN_END_WNL, p, len_end) == 0) {
// avoid seeing "END SELECT" which doesn't end a SUB/F
if (strncmp(p, LCN_END_SELECT, len_end_select) != 0) {
char *dol = strrchr(comp_bc_proc, '/');
if (dol) {
*dol = '\0';
} else {
*comp_bc_proc = '\0';
}
comp_proc_level--;
}
}
}
} // OPTION
// skip text line
while (*p != '\0' && *p != '\n') {
p++;
}
if (*p) {
p++;
}
}
if (comp_proc_level) {
sc_raise(MSG_UDP_MIS_END_2, comp_file_name, comp_bc_proc);
}
comp_proc_level = 0;
*comp_bc_proc = '\0';
if (!opt_quiet && !opt_interactive) {
#if defined(_UnixOS)
if (!isatty(STDOUT_FILENO)) {
fprintf(stdout, "%s: %s\n", WORD_FILE, comp_file_name);
}
else {
log_printf("%s: \033[1m%s\033[0m\n", WORD_FILE, comp_file_name);
}
#elif defined(_PalmOS) // if (code-sections)
log_printf
("%s: \033[1m%s\033[0m\n\033[80m%s: \033[1m%s\033[0m\033[80m\n",
WORD_FILE, comp_file_name, WORD_SECTION, comp_bc_sec);
#else
log_printf("%s: \033[1m%s\033[0m\n", WORD_FILE, comp_file_name);
#endif
}
// Start
if (!comp_error) {
comp_line = 0;
if (!opt_quiet && !opt_interactive) {
#if defined(_UnixOS)
if (!isatty(STDOUT_FILENO)) {
fprintf(stdout, MSG_PASS1);
}
else {
#endif
log_printf(MSG_PASS1_COUNT, comp_line + 1);
#if defined(_UnixOS)
}
#endif
}
ps = p = new_text;
while (*p) {
if (*p == '\n') {
// proceed
*p = '\0';
comp_line++;
if (!opt_quiet && !opt_interactive) {
#if defined(_UnixOS)
if (isatty(STDOUT_FILENO)) {
#endif
#if defined(_PalmOS)
if ((comp_line % 16) == 0) {
if ((comp_line % 64) == 0)
log_printf(MSG_PASS1_COUNT, comp_line);
if (dev_events(0) < 0) {
dev_print("\n\n\a*** interrupted ***\n");
comp_error = -1;
}
}
#else
if ((comp_line % 256) == 0) {
log_printf(MSG_PASS1_COUNT, comp_line);
}
#endif
#if defined(_UnixOS)
}
#endif
}
// add debug info: line-number
bc_add_code(&comp_prog, kwTYPE_LINE);
bc_add_addr(&comp_prog, comp_line);
strcpy(code_line, ps);
comp_text_line(code_line);
if (comp_error) {
break;
}
ps = p + 1;
}
if (comp_error) {
break;
}
p++;
}
}
tmp_free(code_line);
tmp_free(new_text);
// undefined keywords... by default are UDP, but if there is no
// UDP-body then ring the bell
if (!comp_error) {
for (i = 0; i < comp_udpcount; i++) {
if (comp_udptable[i].ip == INVALID_ADDR) {
comp_line = comp_udptable[i].pline;
sc_raise(MSG_UNDEFINED_UDP, comp_udptable[i].name);
}
}
}
bc_eoc(&comp_prog);
bc_resize(&comp_prog, comp_prog.count);
if (!comp_error) {
if (!opt_quiet && !opt_interactive) {
log_printf(MSG_PASS1_FIN, comp_line + 1);
log_printf("\n");
}
}
return (comp_error == 0);
}
/**
* setup export table
*/
int comp_pass2_exports() {
int i, j;
for (i = 0; i < comp_expcount; i++) {
unit_sym_t sym;
bid_t pid;
dbt_read(comp_exptable, i, &sym, sizeof(unit_sym_t));
// look on procedures/functions
if ((pid = comp_udp_id(sym.symbol, 0)) != -1) {
if (comp_udptable[pid].vid == INVALID_ADDR) {
sym.type = stt_procedure;
} else {
sym.type = stt_function;
}
sym.address = comp_udptable[pid].ip;
sym.vid = comp_udptable[pid].vid;
} else {
// look on variables
pid = -1;
for (j = 0; j < comp_varcount; j++) {
if (strcmp(comp_vartable[j].name, sym.symbol) == 0) {
pid = j;
break;
}
}
if (pid != -1) {
sym.type = stt_variable;
sym.address = 0;
sym.vid = j;
} else {
sc_raise(MSG_EXP_SYM_NOT_FOUND, sym.symbol);
return 0;
}
}
dbt_write(comp_exptable, i, &sym, sizeof(unit_sym_t));
}
return (comp_error == 0);
}
/*
* PASS 2
*/
int comp_pass2() {
if (!opt_quiet && !opt_interactive) {
#if defined(_UnixOS)
if (!isatty(STDOUT_FILENO)) {
fprintf(stdout, "Pass2...\n");
}
else {
#endif
log_printf(MSG_PASS2);
#if defined(_UnixOS)
}
#endif
}
if (comp_proc_level) {
sc_raise(MSG_MISSING_END_3);
} else {
bc_add_code(&comp_prog, kwSTOP);
comp_first_data_ip = comp_prog.count;
comp_pass2_scan();
}
if (comp_block_level && (comp_error == 0)) {
sc_raise(MSG_LOOPS_OPEN, comp_block_level);
}
if (comp_data.count) {
bc_append(&comp_prog, &comp_data);
}
if (comp_expcount) {
comp_pass2_exports();
}
return (comp_error == 0);
}
/*
* final, create bytecode
*/
mem_t comp_create_bin() {
int i;
mem_t buff_h;
byte *buff, *cp;
bc_head_t hdr;
dword size;
unit_file_t uft;
unit_sym_t sym;
if (!opt_quiet && !opt_interactive) {
if (comp_unit_flag) {
log_printf(MSG_CREATING_UNIT, comp_unit_name);
} else {
log_printf(MSG_CREATING_BC);
}
}
//
memcpy(&hdr.sign, "SBEx", 4);
hdr.ver = 2;
hdr.sbver = SB_DWORD_VER;
#if defined(CPU_BIGENDIAN)
hdr.flags = 1;
#else
hdr.flags = 0;
#endif
#if defined(OS_ADDR16)
hdr.flags |= 2;
#elif defined(OS_ADDR32)
hdr.flags |= 4;
#endif
// executable header
hdr.bc_count = comp_prog.count;
hdr.var_count = comp_varcount;
hdr.lab_count = comp_labcount;
hdr.data_ip = comp_first_data_ip;
hdr.size = sizeof(bc_head_t) + comp_prog.count + (comp_labcount * ADDRSZ)
+ sizeof(unit_sym_t) * comp_expcount + sizeof(bc_lib_rec_t) * comp_libcount
+ sizeof(bc_symbol_rec_t) * comp_impcount;
if (comp_unit_flag) {
hdr.size += sizeof(unit_file_t);
}
hdr.lib_count = comp_libcount;
hdr.sym_count = comp_impcount;
if (comp_unit_flag) {
// it is a unit... add more info
buff_h = mem_alloc(hdr.size + 4); // +4
buff = mem_lock(buff_h);
// unit header
memcpy(&uft.sign, "SBUn", 4);
uft.version = 1;
strcpy(uft.base, comp_unit_name);
uft.sym_count = comp_expcount;
cp = buff;
memcpy(cp, &uft, sizeof(unit_file_t));
cp += sizeof(unit_file_t);
// unit symbol table (export)
for (i = 0; i < uft.sym_count; i++) {
dbt_read(comp_exptable, i, &sym, sizeof(unit_sym_t));
memcpy(cp, &sym, sizeof(unit_sym_t));
cp += sizeof(unit_sym_t);
}
// normal file
memcpy(cp, &hdr, sizeof(bc_head_t));
cp += sizeof(bc_head_t);
} else {
// simple executable
buff_h = mem_alloc(hdr.size + 4); // +4
buff = mem_lock(buff_h);
cp = buff;
memcpy(cp, &hdr, sizeof(bc_head_t));
cp += sizeof(bc_head_t);
}
// append label table
for (i = 0; i < comp_labcount; i++) {
comp_label_t label;
dbt_read(comp_labtable, i, &label, sizeof(comp_label_t));
memcpy(cp, &label.ip, ADDRSZ);
cp += ADDRSZ;
}
// append library table
for (i = 0; i < comp_libcount; i++) {
bc_lib_rec_t lib;
dbt_read(comp_libtable, i, &lib, sizeof(bc_lib_rec_t));
memcpy(cp, &lib, sizeof(bc_lib_rec_t));
cp += sizeof(bc_lib_rec_t);
}
// append symbol table
for (i = 0; i < comp_impcount; i++) {
bc_symbol_rec_t sym;
dbt_read(comp_imptable, i, &sym, sizeof(bc_symbol_rec_t));
memcpy(cp, &sym, sizeof(bc_symbol_rec_t));
cp += sizeof(bc_symbol_rec_t);
}
size = cp - buff;
// the program itself
memcpy(cp, comp_prog.ptr, comp_prog.count);
mem_unlock(buff_h);
size += comp_prog.count;
// print statistics
if (!opt_quiet && !opt_interactive) {
log_printf("\n");
log_printf(RES_NUMBER_OF_VARS, comp_varcount, comp_varcount - 18);
// system variables
log_printf(RES_NUMBER_OF_LABS, comp_labcount);
log_printf(RES_NUMBER_OF_UDPS, comp_udpcount);
log_printf(RES_CODE_SIZE, comp_prog.count);
log_printf("\n");
log_printf(RES_IMPORTED_LIBS, comp_libcount);
log_printf(RES_IMPORTED_SYMS, comp_impcount);
log_printf(RES_EXPORTED_SYMS, comp_expcount);
log_printf("\n");
log_printf(RES_FINAL_SIZE, size);
log_printf("\n");
}
return buff_h;
}
/**
* save binary
*
* @param h_bc is the memory-handle of the bytecode (created by create_bin)
* @return non-zero on success
*/
int comp_save_bin(mem_t h_bc) {
int h;
char fname[OS_FILENAME_SIZE + 1];
char *buf;
char *p;
int result = 1;
if ((opt_nosave && !comp_unit_flag) || opt_syntaxcheck) {
return 1;
}
strcpy(fname, comp_file_name);
p = strrchr(fname, '.');
if (p) {
*p = '\0';
}
strcat(fname, comp_unit_flag ? ".sbu" : ".sbx");
h = open(fname, O_BINARY | O_RDWR | O_TRUNC | O_CREAT, 0660);
if (h != -1) {
buf = (char *)mem_lock(h_bc);
write(h, buf, mem_handle_size(h_bc));
close(h);
mem_unlock(h_bc);
if (!opt_quiet && !opt_interactive) {
log_printf(MSG_BC_FILE_CREATED, fname);
}
} else {
// non-fatal error
result = 0;
}
return result;
}
/**
* compiler - main
*
* @param sb_file_name the source file-name
* @return non-zero on success
*/
int comp_compile(const char *sb_file_name) {
char *source;
int tid, prev_tid;
int success = 0;
mem_t h_bc = 0;
tid = create_task(sb_file_name);
prev_tid = activate_task(tid);
comp_reset_externals();
comp_init(); // initialize compiler
source = comp_load(sb_file_name); // load file and run pre-processor
if (source) {
success = comp_pass1(NULL, source); // PASS1
tmp_free(source);
if (success) {
success = comp_pass2(); // PASS2
}
if (success) {
success = comp_check_labels();
}
if (success) {
success = ((h_bc = comp_create_bin()) != 0);
}
if (success) {
success = comp_save_bin(h_bc);
}
}
comp_close();
close_task(tid);
activate_task(prev_tid);
if (opt_nosave) {
bytecode_h = h_bc; // update task's bytecode
} else if (h_bc) {
mem_free(h_bc);
}
return success;
}
/**
* compiler - main.
*
* @param source buffer
* @return non-zero on success
*/
int comp_compile_buffer(const char *source) {
comp_init(); // initialize compiler
int success = comp_pass1(NULL, source); // PASS1
if (success) {
success = comp_pass2(); // PASS2
}
if (success) {
success = comp_check_labels();
}
if (success) {
bytecode_h = comp_create_bin(); // update task's bytecode
}
comp_close();
return (success && bytecode_h);
}