From 1858ad4b0cdf2c3ffcb43927e2b0a3cbe5a22f43 Mon Sep 17 00:00:00 2001 From: Eremey Valetov Date: Sun, 29 Mar 2026 14:28:28 -0400 Subject: [PATCH] =?UTF-8?q?Compiler:=20DEF=20FN,=20DEFINT,=20STRING$("c"),?= =?UTF-8?q?=20gw=5Fcint=20rounding=20=E2=80=94=2037/72=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- src/analysis.c | 43 ++++++++++++++++++++++++ src/codegen.c | 88 +++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 119 insertions(+), 12 deletions(-) diff --git a/src/analysis.c b/src/analysis.c index 8544add..f740e7f 100644 --- a/src/analysis.c +++ b/src/analysis.c @@ -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; diff --git a/src/codegen.c b/src/codegen.c index b851ecf..b5a7ed3 100644 --- a/src/codegen.c +++ b/src/codegen.c @@ -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;