Compiler: DEF FN, DEFINT, STRING$("c"), gw_cint rounding — 37/72 tests
DEF FN: embed definition tokens and call gw_exec_stmt() at runtime. FN calls: embed call tokens, sync variables, call gw_eval_fn_call(). Unlocks def_fn.bas, def_types.bas, stats_calc (partial). DEFINT/DEFSNG/DEFDBL/DEFSTR: pre-scan pass in analysis.c sets the def_type table before variable type resolution. Integer variables now correctly use gw_cint() rounding instead of C truncation (7.9 → 8, not 7). Unlocks variables.bas, mult_table.bas. STRING$(n, "c"): handle string second argument by extracting the first character's ASCII code at compile time. Unlocks diamond.bas. Extended expressions (FE-prefix): TIMER, DATE$, TIME$, ENVIRON$, ERDEV$ handled in both emit_atom and emit_str_expr. 37/72 tests pass. 0 compile errors. 32 output mismatches.
This commit is contained in:
@@ -250,6 +250,49 @@ void analysis_run(analysis_t *a)
|
||||
for (int i = 0; i < 26; i++)
|
||||
a->def_type[i] = VT_SNG;
|
||||
|
||||
/* Pass 0: scan for DEFINT/DEFSNG/DEFDBL/DEFSTR to set type defaults */
|
||||
for (program_line_t *line = gw.prog_head; line; line = line->next) {
|
||||
uint8_t *p = line->tokens;
|
||||
while (*p) {
|
||||
if (*p == TOK_DEFINT || *p == TOK_DEFSNG ||
|
||||
*p == TOK_DEFDBL || *p == TOK_DEFSTR) {
|
||||
gw_valtype_t dt;
|
||||
switch (*p) {
|
||||
case TOK_DEFINT: dt = VT_INT; break;
|
||||
case TOK_DEFSNG: dt = VT_SNG; break;
|
||||
case TOK_DEFDBL: dt = VT_DBL; break;
|
||||
case TOK_DEFSTR: dt = VT_STR; break;
|
||||
default: dt = VT_SNG; break;
|
||||
}
|
||||
p++;
|
||||
while (*p == ' ') p++;
|
||||
/* Parse letter ranges: A-Z, X-Z, etc. */
|
||||
while (*p && *p != ':' && *p != 0) {
|
||||
if (is_letter(*p)) {
|
||||
int from = toupper(*p) - 'A';
|
||||
int to = from;
|
||||
p++;
|
||||
while (*p == ' ') p++;
|
||||
if (*p == TOK_MINUS || *p == '-') {
|
||||
p++;
|
||||
while (*p == ' ') p++;
|
||||
if (is_letter(*p)) {
|
||||
to = toupper(*p) - 'A';
|
||||
p++;
|
||||
}
|
||||
}
|
||||
for (int c = from; c <= to && c < 26; c++)
|
||||
a->def_type[c] = dt;
|
||||
}
|
||||
while (*p == ' ' || *p == ',') p++;
|
||||
if (!is_letter(*p)) break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Pass 1: collect line numbers and scan tokens */
|
||||
for (program_line_t *line = gw.prog_head; line; line = line->next) {
|
||||
if (a->line_count >= MAX_LINES) break;
|
||||
|
||||
+76
-12
@@ -289,7 +289,7 @@ static void emit_atom(void)
|
||||
return;
|
||||
}
|
||||
case FUNC_CINT: {
|
||||
EMIT("((int16_t)gw_round(");
|
||||
EMIT("((int16_t)gw_cint(");
|
||||
advance(); emit_num_expr();
|
||||
if (cur() == ')') advance();
|
||||
EMIT("))");
|
||||
@@ -385,6 +385,52 @@ static void emit_atom(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* FN call: embed tokens and use interpreter's gw_eval_fn_call */
|
||||
if (tok == TOK_FN) {
|
||||
uint8_t *start = tp;
|
||||
tp++; /* skip TOK_FN */
|
||||
skip_spaces();
|
||||
/* Skip function name + arguments */
|
||||
while (is_letter(cur()) || (cur() >= '0' && cur() <= '9')) advance();
|
||||
if (cur() == '(') {
|
||||
int depth = 1; advance();
|
||||
while (*tp && depth > 0) {
|
||||
if (cur() == '(') depth++;
|
||||
else if (cur() == ')') depth--;
|
||||
advance();
|
||||
}
|
||||
}
|
||||
int len = (int)(tp - start);
|
||||
/* Sync variables, embed tokens, call evaluator */
|
||||
EMIT("({");
|
||||
for (int vi = 0; vi < ana->var_count; vi++) {
|
||||
var_info_t *v = &ana->vars[vi];
|
||||
if (v->name[1])
|
||||
EMIT(" gw_var_find_or_create(\"%c%c\",%d)->val=(gw_value_t){.type=%d,",
|
||||
v->name[0], v->name[1], v->type, v->type);
|
||||
else
|
||||
EMIT(" gw_var_find_or_create(\"%c\",%d)->val=(gw_value_t){.type=%d,",
|
||||
v->name[0], v->type, v->type);
|
||||
switch (v->type) {
|
||||
case VT_INT: EMIT(".ival="); break;
|
||||
case VT_SNG: EMIT(".fval="); break;
|
||||
case VT_DBL: EMIT(".dval="); break;
|
||||
case VT_STR: EMIT(".sval="); break;
|
||||
}
|
||||
emit_varname(v->name, v->type);
|
||||
EMIT("};");
|
||||
}
|
||||
EMIT(" static const uint8_t _fn[]={");
|
||||
for (int i = 0; i < len; i++)
|
||||
EMIT("%s%u", i ? "," : "", start[i]);
|
||||
EMIT(",0};");
|
||||
EMIT(" gw.text_ptr=(uint8_t*)_fn+1;"); /* skip TOK_FN */
|
||||
EMIT(" while(*gw.text_ptr==' ')gw.text_ptr++;"); /* skip spaces */
|
||||
EMIT(" gw_value_t _fr=gw_eval_fn_call();");
|
||||
EMIT(" _fr.type==VT_INT?_fr.ival:_fr.type==VT_DBL?_fr.dval:(double)_fr.fval; })");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Extended expressions (0xFE prefix) — DATE$, TIME$, TIMER, etc. */
|
||||
if (tok == TOK_PREFIX_FE) {
|
||||
uint8_t xtok = tp[1];
|
||||
@@ -754,13 +800,13 @@ static void emit_str_expr(void)
|
||||
char *arg1 = emit_to_buf(emit_prec_wrapper, 0);
|
||||
if (cur() == ',') advance();
|
||||
skip_spaces();
|
||||
/* Second arg can be a number (char code) or a string */
|
||||
if (cur() == '"' || (is_letter(cur()) && ({
|
||||
uint8_t *sv = tp; char n[2]; gw_valtype_t t = parse_var(n); tp = sv; t == VT_STR; }))) {
|
||||
/* String second arg — use first char's ASCII value */
|
||||
char *arg2 = emit_to_buf(emit_prec_wrapper, 0);
|
||||
EMIT("gw_fn_strings((int)(%s), (int)(%s)).sval", arg1, arg2);
|
||||
free(arg2);
|
||||
if (cur() == '"') {
|
||||
/* STRING$(n, "c") — use ASCII value of first char */
|
||||
tp++; /* skip opening " */
|
||||
int ch = *tp ? *tp : 32;
|
||||
while (*tp && *tp != '"') tp++;
|
||||
if (*tp == '"') tp++;
|
||||
EMIT("gw_fn_strings((int)(%s), %d).sval", arg1, ch);
|
||||
} else {
|
||||
char *arg2 = emit_to_buf(emit_prec_wrapper, 0);
|
||||
EMIT("gw_fn_strings((int)(%s), (int)(%s)).sval", arg1, arg2);
|
||||
@@ -1005,9 +1051,15 @@ static void emit_assignment(void)
|
||||
} else {
|
||||
EMIT(" ");
|
||||
emit_varname(name, type);
|
||||
EMIT(" = (%s)(", c_type(type));
|
||||
emit_num_expr();
|
||||
EMIT(");\n");
|
||||
if (type == VT_INT) {
|
||||
EMIT(" = gw_cint((double)(");
|
||||
emit_num_expr();
|
||||
EMIT("));\n");
|
||||
} else {
|
||||
EMIT(" = (%s)(", c_type(type));
|
||||
emit_num_expr();
|
||||
EMIT(");\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1390,8 +1442,20 @@ static void emit_stmt(void)
|
||||
} else {
|
||||
EMIT(" gw.def_seg = 0;\n");
|
||||
}
|
||||
} else if (cur() == TOK_FN) {
|
||||
/* DEF FN — embed tokens for runtime interpreter */
|
||||
uint8_t *start = tp - 2; /* back to TOK_DEF */
|
||||
while (*tp && *tp != ':') tp++;
|
||||
int len = (int)(tp - start);
|
||||
EMIT(" { static const uint8_t _df[] = {");
|
||||
for (int i = 0; i < len; i++)
|
||||
EMIT("%s%u", i ? "," : "", start[i]);
|
||||
EMIT(",0};\n");
|
||||
EMIT(" uint8_t *_save = gw.text_ptr;\n");
|
||||
EMIT(" gw.text_ptr = (uint8_t *)_df;\n");
|
||||
EMIT(" gw_exec_stmt();\n");
|
||||
EMIT(" gw.text_ptr = _save; }\n");
|
||||
} else {
|
||||
/* DEF FN — skip for now, handled by analysis */
|
||||
while (cur() && cur() != ':' && cur() != 0) tp++;
|
||||
}
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user