diff --git a/.gitignore b/.gitignore index 6979fd4..2ad4b9f 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ __pycache__/ node_modules/ .npm/ .cache/ + +# host build output of the front end +.build/ diff --git a/fec/build-dos.bat b/fec/build-dos.bat deleted file mode 100644 index e8c1178..0000000 --- a/fec/build-dos.bat +++ /dev/null @@ -1,49 +0,0 @@ -@echo off -rem Open Watcom C89 build. The runner's generated RUN.BAT calls this from C:\FEC. -C: -cd \FEC -if exist BUILD.OK del BUILD.OK -if exist BUILD.FAIL del BUILD.FAIL -if exist fec.exe del fec.exe -if exist __wcl__.lnk del __wcl__.lnk -if exist *.obj del *.obj - -if "%WATCOM%"=="" set WATCOM=C:\DEVEL\WATCOMC -if not exist %WATCOM%\BINW\WCL.EXE goto build_fail -set PATH=%WATCOM%\BINW;%WATCOM%\BINP;%PATH% -wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=arena.obj src\arena.c -if errorlevel 1 goto build_fail -wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=diag.obj src\diag.c -if errorlevel 1 goto build_fail -wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=lexer.obj src\lexer.c -if errorlevel 1 goto build_fail -wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=ast.obj src\ast.c -if errorlevel 1 goto build_fail -wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=parser.obj src\parser.c -if errorlevel 1 goto build_fail -wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=types.obj src\types.c -if errorlevel 1 goto build_fail -wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=m7.obj src\m7.c -if errorlevel 1 goto build_fail -wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=own.obj src\own.c -if errorlevel 1 goto build_fail -wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=check.obj src\check.c -if errorlevel 1 goto build_fail -wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=lower.obj src\lower.c -if errorlevel 1 goto build_fail -rem Use an unambiguous short object name for the emitter source. -wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=emitc.obj src\emit_c.c -if errorlevel 1 goto build_fail -wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=driver.obj src\driver.c -if errorlevel 1 goto build_fail -wcl -q -za -wx -bt=dos -ml -k32768 -fe=fec.exe *.obj -if errorlevel 1 goto build_fail -if not exist fec.exe goto build_fail -echo OK>BUILD.OK -cd C:\FEC -goto build_done - -:build_fail -echo FAIL>BUILD.FAIL - -:build_done diff --git a/fec/src/driver.c b/fec/src/driver.c index 90903d5..7a5c55f 100644 --- a/fec/src/driver.c +++ b/fec/src/driver.c @@ -1,6 +1,5 @@ #include "parser.h" #include "check.h" -#include "emit_c.h" #include #include #include @@ -37,25 +36,20 @@ static void dump_tokens(const char *src, unsigned long n, const char *file, int main(int argc, char **argv) { - int i,dump=0,dump_tok=0,check_only=0,emit=0,no_checks=0; - const char *file=0,*outname=0; + int i,dump=0,dump_tok=0,check_only=0,no_checks=0; + const char *file=0; unsigned long n; char *src; FeDiags d; FeAst ast; FeParser p; FeCheck check; - FeEmitter emitter; - FILE *out; unsigned pointer_bits=32; if(argc<2){usage();return 2;} for(i=1;i=argc){fprintf(fe_diag_stream(),"fec: -o needs a path\n");return 2;}outname=argv[++i];} - else if(strncmp(argv[i],"-o",2)==0 && argv[i][2]) outname=argv[i]+2; else if(strncmp(argv[i],"--target=bits16",15)==0) pointer_bits=16; else if(strncmp(argv[i],"--target=bits32",15)==0) pointer_bits=32; else if(strcmp(argv[i],"--no-checks")==0) no_checks=1; @@ -64,7 +58,7 @@ int main(int argc, char **argv) else if(strcmp(argv[i],"--help")==0){usage();return 0;} else {fprintf(fe_diag_stream(),"fec: unknown option %s\n",argv[i]);return 2;} } - if((dump?1:0)+(dump_tok?1:0)+(check_only?1:0)+(emit?1:0)>1){ + if((dump?1:0)+(dump_tok?1:0)+(check_only?1:0)>1){ fprintf(fe_diag_stream(),"fec: choose only one output mode\n"); return 2; } @@ -92,16 +86,9 @@ int main(int argc, char **argv) free(src); return 1; } - if(check_only){ - fe_ast_destroy(&ast); - free(src); - return 0; - } - out=outname?fopen(outname,"w"):stdout; - if(!out){fprintf(fe_diag_stream(),"fec: cannot create %s\n",outname);fe_ast_destroy(&ast);free(src);return 2;} - fe_emit_c_init(&emitter,out,&check,pointer_bits,no_checks); - fe_emit_c_program(&emitter); - if(outname)fclose(out); + /* Semantic analysis is the last pass there is. A code generator attaches + here; until then --check and the default path are the same thing. */ + (void)check_only; fe_ast_destroy(&ast); free(src); return d.errors?1:0; diff --git a/fec/src/emit_c.c b/fec/src/emit_c.c deleted file mode 100644 index 1fc185b..0000000 --- a/fec/src/emit_c.c +++ /dev/null @@ -1,2428 +0,0 @@ -#include "emit_c.h" -#include -#include - -static int type_needs_drop(FeType *t); -static void emit_lvalue(FeEmitter *e, FeNode *n); - -static void emit_expr_core(FeEmitter *e, FeNode *n); -static void emit_stmt_core(FeEmitter *e, FeNode *n); -static void emit_block(FeEmitter *e, FeNode *n); - -static void pad(FeEmitter *e) -{ - int i; - for (i = 0; i < e->indent; ++i) fputs(" ", e->out); -} - -static const char *ctype(FeEmitter *e, FeNode *n) -{ - FeType *t; - if (n && n->sem_type) t = n->sem_type; - else if (n) t = fe_type_from_ast(&e->check->types, n); - else t = fe_type_intern(&e->check->types, "i32"); - return fe_type_c_name(t, e->pointer_bits); -} - -static const char *cname(FeNode *n, const char *fallback) -{ - return n && n->cname ? n->cname : fallback; -} - -static void emit_one_type(FeEmitter *e, FeType *t); - - -static void emit_type_deps(FeEmitter *e, FeType *t) -{ - unsigned i,j; - if (!t) return; - if (t->kind == FE_TYPE_ARRAY) emit_one_type(e,t->elem); - if (t->kind == FE_TYPE_STRUCT) - for (i=0;ifield_count;i++) emit_one_type(e,t->fields[i].type); - if (t->kind == FE_TYPE_ENUM) - for (i=0;ivariant_count;i++) - for (j=0;jvariants[i].field_count;j++) - emit_one_type(e,t->variants[i].fields[j].type); - if (t->kind == FE_TYPE_ERROR_UNION && t->error_value) - emit_one_type(e,t->error_value); -} - -static void emit_one_type(FeEmitter *e, FeType *t) -{ - unsigned i,j; - if (t && strcmp(t->name,"io.Writer")==0) return; - if (!t || t->emit_state || - (t->kind != FE_TYPE_STRUCT && t->kind != FE_TYPE_ENUM && - t->kind != FE_TYPE_ARRAY && t->kind != FE_TYPE_SLICE && - !(t->kind == FE_TYPE_OWNED && t->elem && - t->elem->kind == FE_TYPE_SLICE) && - t->kind != FE_TYPE_ERROR_UNION) || - (t->kind == FE_TYPE_ERROR_UNION && - (!t->error_value || t->error_value->kind == FE_TYPE_VOID))) return; - t->emit_state=1; - emit_type_deps(e,t); - if(t->kind==FE_TYPE_STRUCT) { - fputs(t->cname,e->out); fputs(" {\n",e->out); - for(i=0;ifield_count;i++) { fputs(" ",e->out); fputs(fe_type_c_name(t->fields[i].type,e->pointer_bits),e->out); fputc(' ',e->out); fputs(t->fields[i].name,e->out); fputs(";\n",e->out); } - fputs("};\n",e->out); - } else if(t->kind==FE_TYPE_ARRAY) { - fputs(t->cname,e->out); fputs(" { ",e->out); fputs(fe_type_c_name(t->elem,e->pointer_bits),e->out); fputs(" a[",e->out); fprintf(e->out,"%lu",t->length); fputs("]; };\n",e->out); - } else if(t->kind==FE_TYPE_SLICE && t->cname) { - fputs("typedef struct { ",e->out); - if(!t->ref_mut) fputs("const ",e->out); - fputs(fe_type_c_name(t->elem,e->pointer_bits),e->out); fputs(" *p; unsigned long n; } ",e->out); fputs(t->cname,e->out); fputs(";\n",e->out); - fprintf(e->out,"static %s %s(%s%s *p, unsigned long n) { %s s; s.p=p; s.n=n; return s; }\n",t->cname,t->maker,t->ref_mut ? "" : "const ",fe_type_c_name(t->elem,e->pointer_bits),t->cname); - } else if(t->kind==FE_TYPE_OWNED && t->elem && - t->elem->kind==FE_TYPE_SLICE) { - FeType *item=t->elem->elem; - fputs("typedef struct { ",e->out); - fputs(fe_type_c_name(item,e->pointer_bits),e->out); - fputs(" *p; unsigned long n; } ",e->out); fputs(t->cname,e->out); - fputs(";\n",e->out); - fprintf(e->out,"static %s %s(%s *p, unsigned long n) { %s s; s.p=p; s.n=n; return s; }\n", - t->cname,t->maker,fe_type_c_name(item,e->pointer_bits),t->cname); - } else if(t->kind==FE_TYPE_ERROR_UNION) { - fputs(t->cname,e->out); fputs(" { unsigned short e; ",e->out); - fputs(fe_type_c_name(t->error_value,e->pointer_bits),e->out); - fputs(" v; } ;\n",e->out); - } else if(t->kind==FE_TYPE_ENUM) { - for(i=0;ivariant_count;i++) if(t->variants[i].field_count>1) { - fprintf(e->out,"struct fe_payload_%s_%s {",t->name,t->variants[i].name); - for(j=0;jvariants[i].field_count;j++) { fputs(" ",e->out); fputs(fe_type_c_name(t->variants[i].fields[j].type,e->pointer_bits),e->out); fputc(' ',e->out); fputs(t->variants[i].fields[j].name,e->out); fputc(';',e->out); } - fputs(" };\n",e->out); - } - fputs(t->cname,e->out); fputs(" { ",e->out); fputs(t->bits>8 ? "unsigned short" : "unsigned char",e->out); fputs(" tag; union { ",e->out); - for(i=0;ivariant_count;i++) { if(t->variants[i].field_count==0) fputs("unsigned char",e->out); else if(t->variants[i].field_count==1) fputs(fe_type_c_name(t->variants[i].fields[0].type,e->pointer_bits),e->out); else fprintf(e->out,"struct fe_payload_%s_%s",t->name,t->variants[i].name); fputc(' ',e->out); fputs(t->variants[i].name,e->out); fputc(';',e->out); } - fputs(" } payload; };\n",e->out); - } - t->emit_state=2; -} - - -static FeNode *find_drop_method(FeEmitter *e, const char *name) -{ - FeNode *n; - FeNode *m; - for (n=e->check->ast->root ? e->check->ast->root->children : 0; n; n=n->next) - if (n->kind==FE_N_STRUCT && n->text && name && strcmp(n->text,name)==0) - for (m=n->children; m; m=m->next) - if (m->kind==FE_N_FN && m->text && strcmp(m->text,"drop")==0) - return m; - return 0; -} - - - -/* SPEC 12.3 lists `trim` among the built-in alias methods on `str`, called as - `line.trim()`. The checker accepts it; this emits the lowering. Only the - helper for slice types actually reached by a trim call is emitted, so a - program that never trims does not carry it. */ -static int node_uses_trim(FeNode *n) -{ - FeNode *x; - if (!n) return 0; - if (n->kind==FE_N_CALL && n->a && n->a->kind==FE_N_MEMBER && - n->a->b && n->a->b->text && strcmp(n->a->b->text,"trim")==0) return 1; - if (node_uses_trim(n->a) || node_uses_trim(n->b) || node_uses_trim(n->c)) - return 1; - for (x=n->children; x; x=x->next) if (node_uses_trim(x)) return 1; - return 0; -} - - -static void emit_m4_runtime(FeEmitter *e) -{ - fputs("typedef struct { unsigned char *p; unsigned long n; } fe_m4_slice;\n",e->out); - fputs("typedef struct { unsigned char tag; unsigned short handle; } fe_writer;\n",e->out); - fputs("unsigned short fe_m4_error;\n",e->out); - fputs("fe_writer fe_m4_writer(unsigned char tag, unsigned short handle) { fe_writer w; w.tag=tag; w.handle=handle; return w; }\n",e->out); - fputs("fe_writer fe_m4_stdout_writer(void) { return fe_m4_writer(0,1); }\n",e->out); - fputs("fe_writer fe_m4_stderr_writer(void) { return fe_m4_writer(1,2); }\n",e->out); - fputs("fe_writer fe_m4_null_writer(void) { return fe_m4_writer(3,0); }\n",e->out); - fputs("/* bounded sprint stack; overflow traps instead of corrupting an outer call */\n#define FE_M4_SPRINT_DEPTH 8\n",e->out); - fputs("typedef struct { fe_m4_slice b; unsigned long start_n; } fe_m4_sprint_frame;\n",e->out); - fputs("static fe_m4_sprint_frame fe_m4_sprint_stack[FE_M4_SPRINT_DEPTH];\n",e->out); - fputs("static unsigned fe_m4_sprint_depth;\n",e->out); - fputs("void fe_m4_sprint_begin(fe_m4_slice *b) { if (fe_m4_sprint_depth>=FE_M4_SPRINT_DEPTH) abort(); fe_m4_sprint_stack[fe_m4_sprint_depth].b=*b; fe_m4_sprint_stack[fe_m4_sprint_depth].start_n=b->n; ++fe_m4_sprint_depth; }\n",e->out); - fputs("fe_writer fe_m4_sprint_writer(void) { return fe_m4_writer(4,(unsigned short)(fe_m4_sprint_depth-1)); }\n",e->out); - fputs("unsigned long fe_m4_sprint_finish(void) { unsigned long result; if (!fe_m4_sprint_depth) abort(); --fe_m4_sprint_depth; result=fe_m4_sprint_stack[fe_m4_sprint_depth].start_n-fe_m4_sprint_stack[fe_m4_sprint_depth].b.n; return result; }\n",e->out); - fputs("unsigned short fe_m4_write_bytes(fe_writer w, const unsigned char *p, unsigned long n) { if(w.tag==0) return fwrite(p,1,(size_t)n,stdout)==(size_t)n?0:1; if(w.tag==1) return fwrite(p,1,(size_t)n,stderr)==(size_t)n?0:1; if(w.tag==3) return 0; if(w.tag==4 && w.handlen?n:b->n; if(k) memcpy(b->p,p,(size_t)k); b->p+=k; b->n-=k; return 0; } return 1; }\n",e->out); - fputs("unsigned short fe_m4_write_cstr(fe_writer w, const char *p) { return fe_m4_write_bytes(w,(const unsigned char*)p,(unsigned long)strlen(p)); }\n",e->out); - fputs("#define fe_m4_write_slice(w,s) fe_m4_write_bytes((w),(s).p,(s).n)\n",e->out); - fputs("unsigned short fe_m4_write_int(fe_writer w, long v) { char b[40]; sprintf(b,\"%ld\",v); return fe_m4_write_cstr(w,b); }\n",e->out); - fputs("unsigned short fe_m4_write_hex(fe_writer w, unsigned long v) { char b[40]; sprintf(b,\"%lx\",v); return fe_m4_write_cstr(w,b); }\n",e->out); - fputs("unsigned short fe_m4_write_char(fe_writer w, unsigned char v) { return fe_m4_write_bytes(w,&v,1); }\n",e->out); - fputs("unsigned short fe_m4_write_bool(fe_writer w, unsigned char v) { return fe_m4_write_cstr(w,v ? \"true\" : \"false\"); }\n",e->out); - fputs("unsigned short fe_m4_write_error(fe_writer w, unsigned long v) { char b[40]; sprintf(b,\"error#%lu\",v); return fe_m4_write_cstr(w,b); }\n",e->out); -} - -static int node_uses_m4(FeNode *n) -{ - FeNode *x; - if (!n) return 0; - if (n->kind==FE_N_CALL && n->text && - (strcmp(n->text,"@print")==0 || strcmp(n->text,"@fprint")==0 || - strcmp(n->text,"@sprint")==0)) return 1; - if (n->kind==FE_N_CALL && n->a && n->a->kind==FE_N_MEMBER && - n->a->a && n->a->a->text && strcmp(n->a->a->text,"io")==0) return 1; - if (node_uses_m4(n->a) || node_uses_m4(n->b) || node_uses_m4(n->c)) return 1; - for (x=n->children; x; x=x->next) if (node_uses_m4(x)) return 1; - return 0; -} - -static void emit_expr(FeEmitter *e, FeNode *n); -static void emit_stmt(FeEmitter *e, FeNode *n); -static void emit_owned_live(FeEmitter *e, FeNode *n, int value); -static void emit_cleanup_all(FeEmitter *e); -static void emit_value_drop(FeEmitter *e, FeNode *n); -static void emit_cleanup_block(FeEmitter *e, FeNode *n); -static void emit_cleanup_to(FeEmitter *e, unsigned floor); -static void emit_param_cleanup(FeEmitter *e); -static void emit_error_return(FeEmitter *e, const char *error_expr); -static void emit_fn(FeEmitter *e, FeNode *fn, int prototype); -static void emit_main_wrapper(FeEmitter *e, FeNode *fn); -static void emit_type_defs(FeEmitter *e); - -static int stmt_definitely_returns(FeNode *n); - -static int match_is_exhaustive(FeNode *n) -{ - FeType *t; - FeNode *arm; - unsigned i; - int found; - if (!n || !n->a) return 0; - t=n->a->sem_type; - if (!t || t->kind!=FE_TYPE_ENUM) return 0; - for (arm=n->children; arm; arm=arm->next) - if (arm->text && strcmp(arm->text,"_")==0) return 1; - for (i=0; ivariant_count; ++i) { - found=0; - for (arm=n->children; arm; arm=arm->next) - if (arm->text && strcmp(arm->text,t->variants[i].name)==0) { - found=1; - break; - } - if (!found) return 0; - } - return 1; -} - -static int match_definitely_returns(FeNode *n) -{ - FeNode *arm; - if (!match_is_exhaustive(n)) return 0; - for (arm=n->children; arm; arm=arm->next) - if (!stmt_definitely_returns(arm->a)) return 0; - return 1; -} - -static int stmt_definitely_returns(FeNode *n) -{ - FeNode *last; - if (!n) return 0; - if (n->kind==FE_N_RETURN) return 1; - if (n->kind==FE_N_MATCH) return match_definitely_returns(n); - if (n->kind==FE_N_BLOCK) { - last=n->children; - if (!last) return 0; - while (last->next) last=last->next; - return stmt_definitely_returns(last); - } - if (n->kind==FE_N_IF) - return n->b && n->c && stmt_definitely_returns(n->b) && - stmt_definitely_returns(n->c); - return 0; -} - -static int hex_value(int c) -{ - if (c>='0' && c<='9') return c-'0'; - if (c>='a' && c<='f') return c-'a'+10; - if (c>='A' && c<='F') return c-'A'+10; - return -1; -} - -static void emit_byte(FILE *out, unsigned value) -{ - fprintf(out,"\\%03o",value & 255U); -} - -static void emit_codepoint(FILE *out, unsigned long cp) -{ - if (cp<=0x7fUL) emit_byte(out,(unsigned)cp); - else if (cp<=0x7ffUL) { - emit_byte(out,(unsigned)(0xc0UL | (cp>>6))); - emit_byte(out,(unsigned)(0x80UL | (cp&0x3fUL))); - } else if (cp<=0xffffUL) { - emit_byte(out,(unsigned)(0xe0UL | (cp>>12))); - emit_byte(out,(unsigned)(0x80UL | ((cp>>6)&0x3fUL))); - emit_byte(out,(unsigned)(0x80UL | (cp&0x3fUL))); - } else { - emit_byte(out,(unsigned)(0xf0UL | (cp>>18))); - emit_byte(out,(unsigned)(0x80UL | ((cp>>12)&0x3fUL))); - emit_byte(out,(unsigned)(0x80UL | ((cp>>6)&0x3fUL))); - emit_byte(out,(unsigned)(0x80UL | (cp&0x3fUL))); - } -} - -static void emit_c_literal(FILE *out, const char *text, int string) -{ - unsigned long i; - unsigned long cp; - int h0,h1,h2,h3; - int c; - char quote=string ? '"' : '\''; - if (!text) { fputs(string ? "\"\"" : "'\\000'",out); return; } - fputc(quote,out); - for(i=1;text[i] && text[i]!=quote;i++) { - c=(unsigned char)text[i]; - if(c!='\\') { - if(c==quote || c=='\\') fputc('\\',out); - fputc(c,out); - continue; - } - ++i; c=(unsigned char)text[i]; - if(c=='u' && text[i+1] && text[i+2] && text[i+3] && text[i+4]) { - h0=hex_value(text[i+1]); h1=hex_value(text[i+2]); - h2=hex_value(text[i+3]); h3=hex_value(text[i+4]); - if(h0>=0 && h1>=0 && h2>=0 && h3>=0) { - cp=(unsigned long)((h0<<12)|(h1<<8)|(h2<<4)|h3); - emit_codepoint(out,cp); i+=4; continue; - } - } - if(c=='x' && text[i+1] && text[i+2]) { - h0=hex_value(text[i+1]); h1=hex_value(text[i+2]); - if(h0>=0 && h1>=0) { emit_byte(out,(unsigned)((h0<<4)|h1)); i+=2; continue; } - } - if(c=='n') emit_byte(out,10U); - else if(c=='r') emit_byte(out,13U); - else if(c=='t') emit_byte(out,9U); - else if(c=='0') emit_byte(out,0U); - else emit_byte(out,(unsigned char)c); - } - fputc(quote,out); -} - -static void emit_m4_piece(FILE *out, const char *fmt, unsigned long begin, - unsigned long end) -{ - unsigned long i; - unsigned long cp; - int h0,h1,h2,h3; - int c; - fputc('"',out); - for(i=begin;i=0 && - (h1=hex_value((unsigned char)fmt[i+2]))>=0) { - emit_byte(out,(unsigned)((h0<<4)|h1)); i+=2; - } else if(c=='u' && i+4=0 && - (h1=hex_value((unsigned char)fmt[i+2]))>=0 && - (h2=hex_value((unsigned char)fmt[i+3]))>=0 && - (h3=hex_value((unsigned char)fmt[i+4]))>=0) { - cp=(unsigned long)((h0<<12)|(h1<<8)|(h2<<4)|h3); - emit_codepoint(out,cp); i+=4; - } - else if(c=='\\' || c=='"') { fputc('\\',out); fputc(c,out); } - else emit_byte(out,(unsigned)c); - continue; - } - if(c=='"' || c=='\\') fputc('\\',out); - fputc(c,out); - } - fputc('"',out); -} - -static void emit_m4_writer(FeEmitter *e, FeNode *arg, int buffer) -{ - (void)buffer; - if (arg && arg->kind==FE_N_UNARY && arg->text && - (strcmp(arg->text,"&")==0 || strcmp(arg->text,"&mut")==0)) { - emit_expr(e,arg->a); - } else if (arg && arg->kind==FE_N_CALL && arg->a && - arg->a->kind==FE_N_MEMBER) { - emit_expr(e,arg); - } else if (arg && arg->sem_type && arg->sem_type->kind==FE_TYPE_REF) { - fputs("(*",e->out); emit_expr(e,arg); fputc(')',e->out); - } else { - emit_expr(e,arg); - } -} - -static void emit_m4_writer_value(FeEmitter *e, FeNode *writer, int buffer) -{ - if (!writer) fputs("fe_m4_stdout_writer()",e->out); - else if (buffer) fputs("fe_m4_sprint_writer()",e->out); - else emit_m4_writer(e,writer,buffer); -} - -static void emit_m4_arg(FeEmitter *e, FeNode *arg, int verb, - FeNode *writer, int buffer, int error_value) -{ - FeType *t=arg ? arg->sem_type : 0; - if (verb=='x') { - fputs("fe_m4_write_hex(",e->out); emit_m4_writer_value(e,writer,buffer); fputs(", (unsigned long)",e->out); emit_expr(e,arg); fputc(')',e->out); return; - } - if (verb=='c') { - fputs("fe_m4_write_char(",e->out); emit_m4_writer_value(e,writer,buffer); fputs(", (unsigned char)",e->out); emit_expr(e,arg); fputc(')',e->out); return; - } - if (verb=='b') { - fputs("fe_m4_write_bool(",e->out); emit_m4_writer_value(e,writer,buffer); fputs(", ",e->out); emit_expr(e,arg); fputc(')',e->out); return; - } - if (verb=='s' || (verb==' ' && t && t->kind==FE_TYPE_SLICE)) { - fputs("fe_m4_write_slice(",e->out); - emit_m4_writer_value(e,writer,buffer); fputs(", ",e->out); emit_expr(e,arg); fputc(')',e->out); return; - } - if (error_value) { - fputs("fe_m4_write_error(",e->out); emit_m4_writer_value(e,writer,buffer); fputs(", (unsigned long)",e->out); emit_expr(e,arg); fputs(".tag)",e->out); return; - } - if (verb==' ' && t && t->kind==FE_TYPE_BOOL) { - fputs("fe_m4_write_bool(",e->out); emit_m4_writer_value(e,writer,buffer); fputs(", ",e->out); emit_expr(e,arg); fputc(')',e->out); return; - } - if (verb==' ' && t && t->kind==FE_TYPE_CHAR) { - fputs("fe_m4_write_char(",e->out); emit_m4_writer_value(e,writer,buffer); fputs(", (unsigned char)",e->out); emit_expr(e,arg); fputc(')',e->out); return; - } - fputs("fe_m4_write_int(",e->out); emit_m4_writer_value(e,writer,buffer); fputs(", (long)",e->out); emit_expr(e,arg); fputc(')',e->out); -} - -static void emit_m4_builtin(FeEmitter *e, FeNode *n) -{ - const char *fmt=n->aux_text; - FeNode *fmt_node=n->children; - FeNode *arg; - FeNode *writer_arg=0; - FeNode *buffer_arg=0; - unsigned long i,j,last=1; - unsigned count=0; - int verb; - int error_value; - int first=1; - int is_print=strcmp(n->text,"@print")==0; - int is_sprint=strcmp(n->text,"@sprint")==0; - if (!fmt) { fputs("0",e->out); return; } - if (is_print) writer_arg=0; - else if (is_sprint) { buffer_arg=fmt_node; fmt_node=fmt_node ? fmt_node->next : 0; } - else { writer_arg=fmt_node; fmt_node=fmt_node ? fmt_node->next : 0; } - arg=fmt_node ? fmt_node->next : 0; - error_value=0; - fputc('(',e->out); - if (!is_print && !is_sprint) { - fputs("fe_m4_error=0",e->out); - first=0; - } - if (is_sprint) { - fputs("fe_m4_sprint_begin((fe_m4_slice*)&",e->out); emit_expr(e,buffer_arg); fputs(")",e->out); - first=0; - } - i=1; - while(fmt[i] && fmt[i]!='"') { - if(fmt[i]=='\\') { ++i; if(fmt[i]) ++i; continue; } - if(fmt[i]=='{' && fmt[i+1]=='{') { i+=2; continue; } - if(fmt[i]=='}' && fmt[i+1]=='}') { i+=2; continue; } - if(fmt[i]=='{') { - j=i+1; while(fmt[j] && fmt[j]!='}') ++j; - if(!fmt[j]) break; - if(i>last) { - if(!first) fputs(", ",e->out); - if (!is_print && !is_sprint) - fputs("fe_m4_error ? fe_m4_error : (fe_m4_error = ",e->out); - if(is_print) { fputs("fe_m4_write_cstr(fe_m4_stdout_writer(), ",e->out); emit_m4_piece(e->out,fmt,last,i); fputc(')',e->out); } - else { fputs("fe_m4_write_cstr(",e->out); if(is_sprint) emit_m4_writer_value(e,buffer_arg,1); else emit_m4_writer(e,writer_arg,0); fputs(", ",e->out); emit_m4_piece(e->out,fmt,last,i); fputc(')',e->out); } - if (!is_print && !is_sprint) fputc(')',e->out); - first=0; - } - verb=(j==i+1) ? ' ' : (j==i+2 ? (unsigned char)fmt[i+1] : '?'); - if(arg) { - error_value=arg->sem_type && arg->sem_type->kind==FE_TYPE_ENUM && - arg->sem_type->is_error; - if(!first) fputs(", ",e->out); - if (!is_print && !is_sprint) - fputs("fe_m4_error ? fe_m4_error : (fe_m4_error = ",e->out); - if(is_print) emit_m4_arg(e,arg,verb,0,0,error_value); - else { - /* The writer expression is repeated intentionally; it is - a value wrapper and does not re-evaluate source args. */ - emit_m4_arg(e,arg,verb, is_sprint ? buffer_arg : writer_arg, - is_sprint,error_value); - } - if (!is_print && !is_sprint) fputc(')',e->out); - first=0; arg=arg->next; ++count; - } - last=j+1; i=j+1; continue; - } - ++i; - } - if(fmt[i]=='"' && i>last) { - if(!first) fputs(", ",e->out); - if (!is_print && !is_sprint) - fputs("fe_m4_error ? fe_m4_error : (fe_m4_error = ",e->out); - if(is_print) { fputs("fe_m4_write_cstr(fe_m4_stdout_writer(), ",e->out); emit_m4_piece(e->out,fmt,last,i); fputc(')',e->out); } - else { fputs("fe_m4_write_cstr(",e->out); if(is_sprint) emit_m4_writer_value(e,buffer_arg,1); else emit_m4_writer(e,writer_arg,0); fputs(", ",e->out); emit_m4_piece(e->out,fmt,last,i); fputc(')',e->out); } - if (!is_print && !is_sprint) fputc(')',e->out); - first=0; - } - if(first) fputs("0",e->out); - if(is_print) fputs(", (void)0",e->out); - else if(is_sprint) { fputs(", fe_m4_sprint_finish()",e->out); } - fputc(')',e->out); - (void)count; -} - - -static void emit_destroy_expr(FeEmitter *e, FeNode *n) -{ - fputs("(free(",e->out); emit_expr(e,n); - if(n && n->sem_type && n->sem_type->kind==FE_TYPE_OWNED && - n->sem_type->elem && n->sem_type->elem->kind==FE_TYPE_SLICE) - fputs(".p",e->out); - fputs(")",e->out); - if (n && n->kind==FE_N_IDENT) { - fputs(", ",e->out); emit_lvalue(e,n); - if(n->sem_type && n->sem_type->elem && - n->sem_type->elem->kind==FE_TYPE_SLICE) fputs(".p=0",e->out); - else fputs("=0",e->out); - fputs(", fe_live_",e->out); fputs(cname(n,"owned"),e->out); - fputs("=0",e->out); - } else { - fputs(", ",e->out); emit_lvalue(e,n); fputs("=0",e->out); - } - fputs(", 0)",e->out); -} - -static FeNode *init_field(FeNode *n, const char *name) -{ - FeNode *f; - for(f=n ? n->children : 0;f;f=f->next) - if(f->kind==FE_N_FIELD && f->text && name && strcmp(f->text,name)==0) return f; - return 0; -} - -static void emit_slice_call(FeEmitter *e, FeNode *n) -{ - FeType *bt=n->a ? n->a->sem_type : 0; - const char *maker=n->sem_type && n->sem_type->maker ? n->sem_type->maker : - (bt && bt->slicer ? bt->slicer : "fe_missing_slice"); - if (bt && (bt->kind==FE_TYPE_ARRAY || bt->kind==FE_TYPE_SLICE)) { - fputs(n->sem_type && n->sem_type->slicer ? - n->sem_type->slicer : "fe_missing_slicer",e->out); - fputc('(',e->out); fputs(maker,e->out); fputc('(',e->out); - if (bt->kind==FE_TYPE_ARRAY) { - fputs("(&",e->out); emit_lvalue(e,n->a); fputs(")->a",e->out); - } else { - emit_expr(e,n->a); fputs(".p",e->out); - } - fputs(", ",e->out); - if(bt->kind==FE_TYPE_ARRAY) fprintf(e->out,"%lu",bt->length); - else { emit_expr(e,n->a); fputs(".n",e->out); } - fputs("), ",e->out); - if(n->b) emit_expr(e,n->b); else fputs("0",e->out); - fputs(", ",e->out); - if(n->c) emit_expr(e,n->c); - else if(bt->kind==FE_TYPE_ARRAY) fprintf(e->out,"%lu",bt->length); - else { emit_expr(e,n->a); fputs(".n",e->out); } - fputc(')',e->out); - return; - } - if (!n->b && !n->c && bt && bt->full_slicer) { - fputs(bt->full_slicer,e->out); - if (bt->kind==FE_TYPE_ARRAY) { fputs("(&",e->out); emit_lvalue(e,n->a); } - else { fputc('(',e->out); emit_expr(e,n->a); } - fputc(')',e->out); return; - } - if (!n->c && bt && bt->tail_slicer) { - fputs(bt->tail_slicer,e->out); - if (bt->kind==FE_TYPE_ARRAY) { fputs("(&",e->out); emit_lvalue(e,n->a); } - else { fputc('(',e->out); emit_expr(e,n->a); } - fputs(", ",e->out); - if (n->b) emit_expr(e,n->b); else fputs("0",e->out); - fputc(')',e->out); return; - } - fputs(maker,e->out); - if (bt && bt->kind==FE_TYPE_ARRAY) { fputs("(&",e->out); emit_lvalue(e,n->a); } - else { fputc('(',e->out); emit_expr(e,n->a); } - fputs(", ",e->out); - if(n->b) emit_expr(e,n->b); else fputs("0",e->out); - fputs(", ",e->out); - if(n->c) emit_expr(e,n->c); - else if(bt && bt->kind==FE_TYPE_ARRAY) fprintf(e->out,"%lu",bt->length); - else fputs("((unsigned long)",e->out), emit_expr(e,n->a), fputs(".n)",e->out); - fputc(')',e->out); -} - -static void emit_slice(FeEmitter *e, FeNode *n) -{ - emit_slice_call(e,n); -} - -static void emit_expr_core(FeEmitter *e, FeNode *n) -{ - FeNode *x; - const char *op; - if (!n) { - fputs("0", e->out); - return; - } - switch (n->kind) { - case FE_N_IDENT: - if((n->flags & 0x100U) && n->sem_type && - type_needs_drop(n->sem_type)) { - fputs("(fe_live_",e->out); fputs(cname(n,"owned"),e->out); - fputs("=0, ",e->out); fputs(cname(n,"fe_missing"),e->out); - fputc(')',e->out); - } else fputs(cname(n, "fe_missing"), e->out); - break; - case FE_N_LITERAL: - if (n->text && strcmp(n->text, "true") == 0) fputs("1", e->out); - else if (n->text && strcmp(n->text, "false") == 0) fputs("0", e->out); - else if (n->text && n->text[0]=='"') { - fputs(n->sem_type && n->sem_type->maker ? - n->sem_type->maker : "fe_missing_str",e->out); - fputs("((const unsigned char*)",e->out); - emit_c_literal(e->out,n->text,1); fputs(", sizeof(",e->out); - emit_c_literal(e->out,n->text,1); fputs(")-1)",e->out); - } - else if (n->text && n->text[0]=='\'') emit_c_literal(e->out,n->text,0); - else fputs(n->text ? n->text : "0", e->out); - break; - case FE_N_STRUCT_INIT: { - FeVariantType *v; - FeNode *f; - unsigned i; - if(n->sem_type && n->a && n->a->kind==FE_N_MEMBER) { - v=fe_type_variant(n->sem_type,n->a->b ? n->a->b->text : ""); - if(v) { fputs(v->maker,e->out); fputc('(',e->out); for(i=0;ifield_count;i++){f=init_field(n,v->fields[i].name);if(i)fputs(", ",e->out);if(f)emit_expr(e,f->a);else fputs("0",e->out);} fputc(')',e->out); } - else fputs("0",e->out); - } else if(n->sem_type && n->sem_type->maker) { - fputs(n->sem_type->maker,e->out); fputc('(',e->out); - if(n->sem_type->kind==FE_TYPE_STRUCT) { for(i=0;isem_type->field_count;i++){f=init_field(n,n->sem_type->fields[i].name);if(i)fputs(", ",e->out);if(f)emit_expr(e,f->a);else fputs("0",e->out);} } - fputc(')',e->out); - } else fputs("0",e->out); - break; - } - case FE_N_ARRAY_INIT: { - int first=1; - if(n->sem_type && n->sem_type->maker) { fputs(n->sem_type->maker,e->out); fputc('(',e->out); for(x=n->children;x;x=x->next){if(!first)fputs(", ",e->out);emit_expr(e,x);first=0;} fputc(')',e->out); } else fputs("0",e->out); - break; - } - case FE_N_INDEX: { - FeType *bt; - bt=n->a ? n->a->sem_type : 0; - if(n->c || !n->b) { - emit_slice(e,n); - } else { - if (bt && bt->indexer) { - fputs(bt->indexer,e->out); fputc('(',e->out); - emit_expr(e,n->a); fputs(", ",e->out); emit_expr(e,n->b); fputc(')',e->out); - } else fputs("0",e->out); - } - break; - } - case FE_N_UNARY: - op = n->text ? n->text : ""; - if (strcmp(op, "try") == 0) { - if (n->a && n->a->sem_type && - n->a->sem_type->kind==FE_TYPE_ERROR_UNION && - n->a->sem_type->error_value && - n->a->sem_type->error_value->kind!=FE_TYPE_VOID) { - fputc('(',e->out); emit_expr(e,n->a); fputs(").v",e->out); - } else emit_expr(e,n->a); - break; - } - if (strcmp(op, "not") == 0) fputs("(!", e->out); - else { - fputc('(', e->out); - fputs(strcmp(op,"&mut")==0 ? "&" : op, e->out); - } - /* Borrowing needs a place, not a value. emit_expr lowers an index to - the bounds-checking accessor, and the address of that call is not an - lvalue -- `&s[0]` became `&fe_idx_slice_type_2(s, 0)`, which C - rejects. emit_lvalue spells the same element as `s.p[0]`. */ - if (strcmp(op,"&")==0 || strcmp(op,"&mut")==0) emit_lvalue(e, n->a); - else emit_expr(e, n->a); - fputc(')', e->out); - break; - case FE_N_BINARY: - op = n->text ? n->text : "+"; - fputc('(', e->out); - emit_expr(e, n->a); - if (strcmp(op, "and") == 0) fputs(" && ", e->out); - else if (strcmp(op, "or") == 0) fputs(" || ", e->out); - else fputs(op, e->out); - emit_expr(e, n->b); - fputc(')', e->out); - break; - case FE_N_TYPE: - if (n->text && strcmp(n->text, "as") == 0) { - fputs("((", e->out); - fputs(ctype(e, n->b), e->out); - fputc(')', e->out); - emit_expr(e, n->a); - fputc(')', e->out); - } else emit_expr(e, n->a); - break; - case FE_N_CALL: { - FeVariantType *v; - FeNode *call_param=0; - int special=0; - if(n->text && (strcmp(n->text,"@print")==0 || strcmp(n->text,"@fprint")==0 || strcmp(n->text,"@sprint")==0)) { emit_m4_builtin(e,n); special=1; } - else if(n->a && n->a->kind==FE_N_MEMBER && n->a->a && - n->a->a->kind==FE_N_IDENT && n->a->a->text && - strcmp(n->a->a->text,"mem")==0 && n->a->b && - n->a->b->text && strcmp(n->a->b->text,"destroy")==0 && - n->children) { - emit_destroy_expr(e,n->children); - special=1; - } - else if(n->a && n->a->kind==FE_N_MEMBER && n->a->a && - n->a->a->kind==FE_N_IDENT && n->a->a->text && - strcmp(n->a->a->text,"mem")==0 && n->a->b && - n->a->b->text && strcmp(n->a->b->text,"create")==0 && - n->children) { - FeType *created=n->children->sem_type; - FeType *owned=fe_type_owned(&e->check->types,created); - FeType *result=fe_type_error_union(&e->check->types,owned); - if (result->alloc_cname) fputs(result->alloc_cname,e->out); - else fputs("fe_bad_alloc",e->out); - fputc('(',e->out); emit_expr(e,n->children); fputc(')',e->out); - special=1; - } - else if(n->a && n->a->kind==FE_N_MEMBER && n->a->a && - n->a->a->kind==FE_N_IDENT && n->a->a->text && - strcmp(n->a->a->text,"mem")==0 && n->a->b && - n->a->b->text && strcmp(n->a->b->text,"alloc_slice")==0 && - n->children && n->children->next) { - FeType *result=n->sem_type; - if(result && result->alloc_cname) fputs(result->alloc_cname,e->out); - else fputs("fe_bad_slice_alloc",e->out); - fputc('(',e->out); emit_expr(e,n->children->next); fputc(')',e->out); - special=1; - } - else if(n->a && n->a->kind==FE_N_MEMBER && n->a->b && n->a->b->text && - strcmp(n->a->b->text,"trim")==0 && !n->children && - n->a->a && n->a->a->sem_type && - n->a->a->sem_type->kind==FE_TYPE_SLICE && - n->a->a->sem_type->cname) { - fputs("fe_trim_",e->out); fputs(n->a->a->sem_type->cname,e->out); - fputc('(',e->out); emit_expr(e,n->a->a); fputc(')',e->out); - special=1; - } - else if(n->a && n->a->kind==FE_N_MEMBER && n->a->a && - n->a->a->kind==FE_N_IDENT && n->a->a->text && - strcmp(n->a->a->text,"mem")==0 && n->a->b && - n->a->b->text && strcmp(n->a->b->text,"replace")==0 && - n->children && n->children->next && n->sem_type) { - fputs(n->sem_type->replace_cname ? n->sem_type->replace_cname : - "fe_bad_replace",e->out); - fputc('(',e->out); emit_expr(e,n->children); fputs(", ",e->out); - emit_expr(e,n->children->next); fputc(')',e->out); - special=1; - } - else if(n->a && n->a->kind==FE_N_MEMBER && n->a->a && - n->a->a->kind==FE_N_IDENT && n->a->a->text && - strcmp(n->a->a->text,"io")==0 && n->a->b && n->a->b->text && - strcmp(n->a->b->text,"null_writer")==0) { fputs("fe_m4_null_writer()",e->out); special=1; } - else if(n->a && n->a->kind==FE_N_MEMBER && n->sem_decl && - n->sem_decl->kind==FE_N_FN) { - FeNode *mp=n->sem_decl->a ? n->sem_decl->a->children : 0; - FeNode *ma; - fputs(cname(n->sem_decl,"fe_method"),e->out); fputc('(',e->out); - if(mp && mp->sem_type && mp->sem_type->kind==FE_TYPE_REF) { - fputc('&',e->out); emit_lvalue(e,n->a->a); - } else emit_expr(e,n->a->a); - for(ma=n->children; ma; ma=ma->next) { - fputs(", ",e->out); emit_expr(e,ma); - } - fputc(')',e->out); - special=1; - } - else if(!n->a && n->text && strcmp(n->text,"@size_of")==0 && n->children && n->children->kind==FE_N_IDENT) { fprintf(e->out,"%lu",fe_type_size(fe_type_intern(&e->check->types,n->children->text))); special=1; } - else if(!n->a && n->text && strcmp(n->text,"@align_of")==0 && n->children && n->children->kind==FE_N_IDENT) { fprintf(e->out,"%u",fe_type_align(fe_type_intern(&e->check->types,n->children->text))); special=1; } - else if (n->a && n->a->kind==FE_N_MEMBER && n->a->a && n->a->a->sem_type && n->a->a->sem_type->kind==FE_TYPE_ENUM) { - v=fe_type_variant(n->a->a->sem_type,n->a->b ? n->a->b->text : ""); - if(v) fputs(v->maker,e->out); else fputs("fe_bad_variant",e->out); - } else if (n->a) emit_expr(e, n->a); - else fputs(n->text ? n->text : "fe_builtin", e->out); - if(!special) { - if(n->sem_decl && n->sem_decl->kind==FE_N_FN && n->sem_decl->a) - call_param=n->sem_decl->a->children; - fputc('(', e->out); - for (x = n->children; x; x = x->next) { - FeType *want=call_param && call_param->a ? - fe_type_from_ast(&e->check->types,call_param->a) : 0; - if (x != n->children) fputs(", ", e->out); - if(want && want->kind==FE_TYPE_SLICE && !want->ref_mut && - x->sem_type && x->sem_type->kind==FE_TYPE_SLICE && - x->sem_type->ref_mut) { - fputs(want->maker,e->out); fputc('(',e->out); - emit_expr(e,x); fputs(".p, ",e->out); - emit_expr(e,x); fputs(".n)",e->out); - } else if ((x->flags & 0x100U) && x->kind==FE_N_IDENT && - x->sem_type && x->sem_type->kind==FE_TYPE_OWNED) { - fputs("(fe_live_",e->out); fputs(cname(x,"owned"),e->out); - fputs("=0, ",e->out); emit_expr(e,x); fputc(')',e->out); - } else emit_expr(e, x); - if(call_param) call_param=call_param->next; - } - fputc(')', e->out); - } - break; - } - case FE_N_MEMBER: { - FeVariantType *v; - if(n->a && n->a->kind==FE_N_IDENT && n->a->text && - strcmp(n->a->text,"io")==0 && n->b && n->b->text && - (strcmp(n->b->text,"stdout")==0 || strcmp(n->b->text,"stderr")==0)) - fputs(strcmp(n->b->text,"stderr")==0 ? - "fe_m4_stderr_writer()" : "fe_m4_stdout_writer()",e->out); - else if(n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_REF && - n->b && n->b->text && strcmp(n->b->text,"^")==0) { - fputs("(*",e->out); emit_expr(e,n->a); fputs(")",e->out); - } else if(n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_OWNED && - n->b && n->b->text && strcmp(n->b->text,"^")==0) { - fputs("(*",e->out); emit_expr(e,n->a); fputs(")",e->out); - } else if(n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_ENUM) { v=fe_type_variant(n->a->sem_type,n->b ? n->b->text : ""); if(v) fputs(v->maker,e->out); else fputs("0",e->out); if(v)fputs("()",e->out); } - else if(n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_REF) { - emit_expr(e,n->a); fputs("->",e->out); - if(n->b) fputs(n->b->text ? n->b->text : "member",e->out); - } else { emit_expr(e, n->a); fputc('.', e->out); if (n->b) fputs(n->b->text ? n->b->text : "member", e->out); } - break; - } - default: - fputs("0", e->out); - break; - } -} - - - - - - - - - - -static void emit_match(FeEmitter *e, FeNode *n, int value_context) -{ - FeNode *arm; - FeType *t=n->a ? n->a->sem_type : 0; - FeVariantType *v; - FeNode *b; - unsigned i; - char temp[32]; - sprintf(temp,"fe_match_%u",e->temp_serial++); - pad(e); fputs("{\n",e->out); ++e->indent; - pad(e); fputs(fe_type_c_name(t,e->pointer_bits),e->out); fputc(' ',e->out); - fputs(temp,e->out); fputs(" = ",e->out); emit_expr(e,n->a); fputs(";\n",e->out); - pad(e); fputs("switch (",e->out); fputs(temp,e->out); fputs(".tag) {\n",e->out); ++e->indent; - for(arm=n->children;arm;arm=arm->next) { - if(arm->text && strcmp(arm->text,"_")==0) { pad(e); fputs("default: ",e->out); } - else { v=t && t->kind==FE_TYPE_ENUM ? fe_type_variant(t,arm->text) : 0; if(!v) continue; fprintf(e->out,"case %u: ",v->tag); } - fputs("{\n",e->out); ++e->indent; - v=t && t->kind==FE_TYPE_ENUM ? fe_type_variant(t,arm->text) : 0; - if(v) for(i=0,b=arm->children;ifield_count && b;i++,b=b->next) { - pad(e); fputs(fe_type_c_name(v->fields[i].type,e->pointer_bits),e->out); fputc(' ',e->out); fputs(cname(b,"fe_match"),e->out); fputs(" = ",e->out); fputs(temp,e->out); fputs(".payload.",e->out); fputs(v->name,e->out); if(v->field_count>1){fputc('.',e->out);fputs(v->fields[i].name,e->out);} fputs(";\n",e->out); - } - if(arm->a && arm->a->kind==FE_N_BLOCK) emit_stmt(e,arm->a); else { pad(e); emit_expr(e,arm->a); fputs(";\n",e->out); } - pad(e); fputs("break;\n",e->out); --e->indent; pad(e); fputs("}\n",e->out); - } - --e->indent; pad(e); fputs("}\n",e->out); - --e->indent; pad(e); fputs("}\n",e->out); - if (value_context || match_definitely_returns(n)) { - pad(e); fputs("fe_trap_bounds();\n",e->out); - pad(e); fputs("return 0;\n",e->out); - } -} - -static int try_has_value(FeNode *n) -{ - return n && n->kind==FE_N_UNARY && n->text && strcmp(n->text,"try")==0 && - n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_ERROR_UNION && - n->a->sem_type->error_value && n->a->sem_type->error_value->kind!=FE_TYPE_VOID; -} - -static void emit_try_statement(FeEmitter *e, FeNode *try_node, FeNode *target) -{ - char temp[40]; - char error[48]; - FeType *result=try_node->a->sem_type; - sprintf(temp,"fe_try_%u",e->temp_serial++); - sprintf(error,"%s.e",temp); - pad(e); fputs("{ ",e->out); fputs(fe_type_c_name(result,e->pointer_bits),e->out); - fputc(' ',e->out); fputs(temp,e->out); fputs(" = ",e->out); - emit_expr(e,try_node->a); fputs("; if (",e->out); fputs(temp,e->out); - fputs(".e) {\n",e->out); ++e->indent; - emit_cleanup_all(e); - pad(e); emit_error_return(e,error); - --e->indent; pad(e); fputs("} ",e->out); - if (target) { - if (target->kind==FE_N_LET || target->kind==FE_N_VAR) - fputs(cname(target,"fe_local"),e->out); - else - emit_lvalue(e,target); - fputs(" = ",e->out); fputs(temp,e->out); fputs(".v;\n",e->out); - } - fputs("}\n",e->out); -} - -static void emit_stmt_core(FeEmitter *e, FeNode *n) -{ - if (!n) return; - switch (n->kind) { - case FE_N_BLOCK: - emit_block(e, n); - fputc('\n', e->out); - break; - case FE_N_LET: - case FE_N_VAR: - if (n->b) { - if (try_has_value(n->b)) emit_try_statement(e,n->b,n); - else { - pad(e); - fputs(cname(n, "fe_local"), e->out); - fputs(" = ", e->out); - emit_expr(e, n->b); - fputs(";\n", e->out); - } - emit_owned_live(e,n,1); - } - break; - case FE_N_ASSIGN: - if (n->a && n->a->kind==FE_N_IDENT && n->a->sem_type && - n->a->sem_type->kind==FE_TYPE_OWNED) { - pad(e); fputs("if (fe_live_",e->out); fputs(cname(n->a,"owned"),e->out); - fputs(") { ",e->out); - if(n->a->sem_type->elem && - n->a->sem_type->elem->kind==FE_TYPE_SLICE) { - fputs("free(",e->out); fputs(cname(n->a,"owned"),e->out); - fputs(".p); ",e->out); - } else if (n->a->sem_type->elem && type_needs_drop(n->a->sem_type->elem) && - n->a->sem_type->elem->drop_cname) - fprintf(e->out,"%s(%s); ",n->a->sem_type->elem->drop_cname,cname(n->a,"owned")); - if(!(n->a->sem_type->elem && - n->a->sem_type->elem->kind==FE_TYPE_SLICE)) { - fputs("free(",e->out); fputs(cname(n->a,"owned"),e->out); fputs("); ",e->out); - } - fputs("fe_live_",e->out); fputs(cname(n->a,"owned"),e->out); - fputs("=0; }\n",e->out); - } else if(n->a && n->a->kind==FE_N_IDENT && n->a->sem_type && - type_needs_drop(n->a->sem_type) && - n->a->sem_type->drop_cname) { - pad(e); fputs("if (fe_live_",e->out); fputs(cname(n->a,"local"),e->out); - fprintf(e->out,") { %s(&%s); fe_live_", - n->a->sem_type->drop_cname,cname(n->a,"local")); - fputs(cname(n->a,"local"),e->out); fputs("=0; }\n",e->out); - } - pad(e); - emit_lvalue(e, n->a); - fputc(' ', e->out); - fputs(n->text ? n->text : "=", e->out); - fputs(" ", e->out); - if (n->b && (n->b->flags & 0x100U) && n->b->kind==FE_N_IDENT && - n->b->sem_type && n->b->sem_type->kind==FE_TYPE_OWNED) { - fputs("(fe_live_",e->out); fputs(cname(n->b,"owned"),e->out); - fputs("=0, ",e->out); emit_expr(e,n->b); fputc(')',e->out); - } else emit_expr(e, n->b); - fputs(";\n", e->out); - if (n->a && n->a->kind==FE_N_IDENT) emit_owned_live(e,n->a,1); - break; - case FE_N_EXPR_STMT: - if (try_has_value(n->a)) { - emit_try_statement(e,n->a,0); - } else { - pad(e); - if (n->a && n->a->kind==FE_N_UNARY && n->a->text && - strcmp(n->a->text,"try")==0 && n->a->a) { - fputs("if ((fe_error_temp = ",e->out); - emit_expr(e,n->a->a); - fputs(") != 0) {\n",e->out); - ++e->indent; - emit_cleanup_all(e); - pad(e); fputs("return fe_error_temp;\n",e->out); - --e->indent; - pad(e); fputs("}\n",e->out); - } else { - emit_expr(e, n->a); - fputs(";\n", e->out); - } - } - break; - case FE_N_BREAK: - case FE_N_CONTINUE: - if (e->loop_depth) { - emit_cleanup_to(e,e->loop_floor[e->loop_depth-1]); - pad(e); fputs(n->kind==FE_N_BREAK ? "break;\n" : "continue;\n",e->out); - } - break; - case FE_N_RETURN: - if (n->a && n->a->kind == FE_N_MATCH) { - emit_match(e,n->a,1); - break; - } - /* Evaluate before cleanup: `return p.^` must not dereference p after - its owned cleanup has run. The block-local temporary is declared - before all statements to retain C89 declaration ordering. */ - if (n->a && e->current_ret && e->current_ret->kind!=FE_TYPE_VOID) { - pad(e); fputs("fe_return_value = ",e->out); - if ((n->a->flags & 0x100U) && n->a->kind==FE_N_IDENT && - n->a->sem_type && n->a->sem_type->kind==FE_TYPE_OWNED) { - fputs("(fe_live_",e->out); fputs(cname(n->a,"owned"),e->out); - fputs("=0, ",e->out); emit_expr(e,n->a); fputc(')',e->out); - } else emit_expr(e,n->a); - fputs(";\n",e->out); - } - emit_cleanup_all(e); - pad(e); fputs("return",e->out); - if (n->a) fputs(" fe_return_value",e->out); - fputs(";\n",e->out); - break; - case FE_N_IF: - pad(e); - fputs("if (", e->out); - emit_expr(e, n->a); - fputs(") ", e->out); - if (n->b && n->b->kind == FE_N_BLOCK) emit_block(e, n->b); - else emit_block(e, 0); - if (n->c) { - fputs(" else ", e->out); - if (n->c->kind == FE_N_IF) emit_stmt(e, n->c); - else emit_block(e, n->c); - } - fputc('\n', e->out); - break; - case FE_N_WHILE: - pad(e); - fputs("while (", e->out); - emit_expr(e, n->a); - fputs(") ", e->out); - if (e->loop_depth<16U) e->loop_floor[e->loop_depth++]=e->block_depth; - if (n->b && n->b->kind == FE_N_BLOCK) emit_block(e, n->b); - else emit_block(e, 0); - if (e->loop_depth) --e->loop_depth; - fputc('\n', e->out); - break; - case FE_N_FOR: - pad(e); fputs("{\n",e->out); ++e->indent; - if (e->loop_depth<16U) e->loop_floor[e->loop_depth++]=e->block_depth; - if (n->c) { - pad(e); fputs("unsigned long ",e->out); fputs(cname(n,"fe_index"),e->out); fputs(";\n",e->out); - pad(e); fputs(cname(n,"fe_index"),e->out); fputs(" = ",e->out); emit_expr(e,n->a); fputs(";\n",e->out); - pad(e); fputs("for (; ",e->out); fputs(cname(n,"fe_index"),e->out); fputs(" < ",e->out); emit_expr(e,n->c); fputs("; ++",e->out); fputs(cname(n,"fe_index"),e->out); fputs(") ",e->out); emit_block(e,n->b); fputc('\n',e->out); - } else { - FeType *bt=n->a ? n->a->sem_type : 0; - FeType *et=bt ? bt->elem : 0; - char temp[32]; - int mutable_iter=(n->flags & 4U) != 0; - sprintf(temp,"fe_iter_%u",e->temp_serial++); - pad(e); fputs(fe_type_c_name(bt,e->pointer_bits),e->out); if (mutable_iter) fputs(" *",e->out); fputc(' ',e->out); fputs(temp,e->out); fputs(" = ",e->out); if (mutable_iter) fputc('&',e->out); emit_expr(e,n->a); fputs(";\n",e->out); - if (n->aux_text) { - pad(e); fputs("unsigned long ",e->out); fputs(cname(n,"fe_index"),e->out); fputs(";\n",e->out); - } else { - pad(e); fputs(fe_type_c_name(n->sem_type ? n->sem_type : fe_type_ref(&e->check->types,et,0),e->pointer_bits),e->out); fputc(' ',e->out); fputs(cname(n,"fe_item"),e->out); fputs(";\n",e->out); - } - pad(e); fputs("{ unsigned long fe_i; for (fe_i = 0; fe_i < ",e->out); - if(bt && bt->kind==FE_TYPE_ARRAY) fprintf(e->out,"%lu",bt->length); else { if(mutable_iter) fputs("(*",e->out); fputs(temp,e->out); if(mutable_iter) fputs(").n",e->out); else fputs(".n",e->out); } - fputs("; ++fe_i) { ",e->out); - if(n->aux_text) { - fputs(fe_type_c_name(fe_type_ref(&e->check->types,et,(n->flags & 4U) != 0),e->pointer_bits),e->out); fputc(' ',e->out); fputs(n->aux_cname ? n->aux_cname : "fe_item",e->out); fputs("; ",e->out); - fputs(cname(n,"fe_index"),e->out); fputs(" = fe_i; ",e->out); - fputs(n->aux_cname ? n->aux_cname : "fe_item",e->out); fputs(" = ",e->out); - } else { fputs(cname(n,"fe_item"),e->out); fputs(" = ",e->out); } - fputc('&',e->out); if(mutable_iter) fputs("(*",e->out); fputs(temp,e->out); if(mutable_iter) fputs(")",e->out); if(bt && bt->kind==FE_TYPE_ARRAY) fputs(".a[fe_i]",e->out); else fputs(".p[fe_i]",e->out); fputs("; ",e->out); - emit_block(e,n->b); fputs(" } }\n",e->out); - } - --e->indent; - if (e->loop_depth) --e->loop_depth; - pad(e); fputs("}\n",e->out); break; - case FE_N_MATCH: - emit_match(e,n,0); break; - default: - break; - } -} - - -static void emit_main_wrapper_core(FeEmitter *e, FeNode *fn) -{ - fputs("int main(void) {\n ", e->out); - if (fn->sem_type && fn->sem_type->kind == FE_TYPE_VOID) { - fputs(cname(fn, "fe_main"), e->out); - fputs("();\n return 0;\n", e->out); - } else { - fputs("return ", e->out); - fputs(cname(fn, "fe_main"), e->out); - fputs("();\n", e->out); - } - fputs("}\n", e->out); -} - -void fe_emit_c_init(FeEmitter *e, FILE *out, FeCheck *check, - unsigned pointer_bits, int no_checks) -{ - e->out = out; - e->check = check; - e->pointer_bits = pointer_bits; - e->indent = 0; - e->no_checks = no_checks; - e->temp_serial = 0; - e->fallthrough_block = 0; - e->block_depth = 0; - e->loop_depth = 0; - e->current_ret = 0; - e->current_fn = 0; -} - - -#include "m7.h" -#include "lower.h" - -static void emit_expr(FeEmitter *e, FeNode *n); -static void emit_stmt(FeEmitter *e, FeNode *n); -static void emit_block(FeEmitter *e, FeNode *n); - -static int type_needs_drop(FeType *t) -{ - return fe_lower_type_needs_drop(t); -} - -static const char *m7_c_type(FeEmitter *e, FeType *t) -{ - if (!t) return "long"; - if ((t->kind==FE_TYPE_ENUM && t->is_error) || - strcmp(t->name,"core.Error")==0) - return "unsigned short"; - return fe_type_c_name(t,e->pointer_bits); -} - -static char *m7_temp_name(FeEmitter *e) -{ - char number[24]; - char *p; - unsigned long len; - sprintf(number,"%u",e->temp_serial++); - len=(unsigned long)strlen("fe_m7_tmp_")+ - (unsigned long)strlen(number)+1UL; - p=(char *)fe_arena_alloc(&e->check->ast->arena,len); - if (!p) return 0; - strcpy(p,"fe_m7_tmp_"); - strcat(p,number); - return p; -} - -static int m7_needs_temp(FeNode *n) -{ - if (!n) return 0; - if (fe_m7_is_try(n)) return 1; - if (n->kind==FE_N_BINARY && fe_m7_lazy_kind(n)!=FE_M7_LAZY_NONE) - return 1; - if (n->kind==FE_N_IF && n->text && strcmp(n->text,"if let")==0) - return 1; - if (n->kind==FE_N_MATCH && n->a && n->a->sem_type && - n->a->sem_type->kind==FE_TYPE_OPTIONAL) - return 1; - return 0; -} - -static FeType *m7_temp_type(FeNode *n) -{ - if (!n) return 0; - if (fe_m7_is_try(n)) return n->a ? n->a->sem_type : 0; - if (n->kind==FE_N_BINARY) return n->a ? n->a->sem_type : 0; - if ((n->kind==FE_N_IF || n->kind==FE_N_MATCH) && n->a) - return n->a->sem_type; - return 0; -} - -static void m7_prepare_temps(FeEmitter *e, FeNode *n) -{ - FeNode *x; - if (!n) return; - if (m7_needs_temp(n) && !n->aux_cname) - n->aux_cname=m7_temp_name(e); - m7_prepare_temps(e,n->a); - m7_prepare_temps(e,n->b); - m7_prepare_temps(e,n->c); - for (x=n->children;x;x=x->next) m7_prepare_temps(e,x); -} - -static void m7_emit_temp_decls(FeEmitter *e, FeNode *n) -{ - FeNode *x; - FeType *t; - if (!n) return; - if (m7_needs_temp(n) && n->aux_cname) { - t=m7_temp_type(n); - if (t) { - pad(e); fputs(m7_c_type(e,t),e->out); fputc(' ',e->out); - fputs(n->aux_cname,e->out); fputs(";\n",e->out); - } - } - m7_emit_temp_decls(e,n->a); - m7_emit_temp_decls(e,n->b); - m7_emit_temp_decls(e,n->c); - for (x=n->children;x;x=x->next) m7_emit_temp_decls(e,x); -} - -static void m7_emit_type(FeEmitter *e, FeType *t) -{ - unsigned i; - unsigned j; - if (!t || t->emit_state) return; - if (t->kind==FE_TYPE_OPTIONAL) { - t->emit_state=1; - m7_emit_type(e,t->elem); - if (!fe_m7_optional_uses_niche(t->elem) && t->cname) { - fputs(t->cname,e->out); fputs(" { unsigned char has; ",e->out); - fputs(m7_c_type(e,t->elem),e->out); - fputs(" v; };\n",e->out); - } - t->emit_state=2; - return; - } - if (t->kind==FE_TYPE_ARRAY) m7_emit_type(e,t->elem); - if (t->kind==FE_TYPE_SLICE) m7_emit_type(e,t->elem); - if (t->kind==FE_TYPE_OWNED) m7_emit_type(e,t->elem); - if (t->kind==FE_TYPE_STRUCT) - for (i=0;ifield_count;++i) m7_emit_type(e,t->fields[i].type); - if (t->kind==FE_TYPE_ENUM) - for (i=0;ivariant_count;++i) - for (j=0;jvariants[i].field_count;++j) - m7_emit_type(e,t->variants[i].fields[j].type); - if (t->kind==FE_TYPE_ERROR_UNION) { - m7_emit_type(e,t->elem); - m7_emit_type(e,t->error_value); - } - emit_one_type(e,t); -} - -static void emit_type_defs(FeEmitter *e) -{ - FeType *t; - for (t=e->check->types.types;t;t=t->next) - if (t->kind==FE_TYPE_ARRAY) - fe_type_slice(&e->check->types,t->elem); - for (t=e->check->types.types;t;t=t->next) m7_emit_type(e,t); -} - -static void m7_emit_drop_access(FeEmitter *e, FeType *t, - const char *access) -{ - if (!t || !access || !type_needs_drop(t)) return; - if (t->kind==FE_TYPE_OWNED) { - if (t->elem && t->elem->kind==FE_TYPE_SLICE) { - fprintf(e->out,"if ((%s).p) { free((%s).p); (%s).p=0; } ", - access,access,access); - } else { - fprintf(e->out,"if (%s) { ",access); - if (t->elem && type_needs_drop(t->elem) && t->elem->drop_cname) - fprintf(e->out,"%s(%s); ",t->elem->drop_cname,access); - fprintf(e->out,"free(%s); %s=0; } ",access,access); - } - return; - } - if (t->drop_cname) - fprintf(e->out,"%s(&(%s)); ",t->drop_cname,access); -} - -static void m7_emit_drop_helpers(FeEmitter *e) -{ - FeType *t; - FeNode *method; - unsigned i; - char access[256]; - for (t=e->check->types.types;t;t=t->next) - if (type_needs_drop(t) && t->drop_cname && - (t->kind==FE_TYPE_STRUCT || t->kind==FE_TYPE_ARRAY || - t->kind==FE_TYPE_OPTIONAL || t->kind==FE_TYPE_ERROR_UNION)) - fprintf(e->out,"static void %s(%s *self);\n", - t->drop_cname,m7_c_type(e,t)); - for (t=e->check->types.types;t;t=t->next) { - if (!type_needs_drop(t) || !t->drop_cname) continue; - if (t->kind==FE_TYPE_STRUCT) { - fprintf(e->out,"static void %s(%s *self) { ", - t->drop_cname,m7_c_type(e,t)); - method=find_drop_method(e,t->name); - if (method) fprintf(e->out,"%s(self); ",cname(method,"fe_drop_method")); - for (i=t->field_count;i>0;--i) { - sprintf(access,"self->%s",t->fields[i-1U].name); - m7_emit_drop_access(e,t->fields[i-1U].type,access); - } - fputs("}\n",e->out); - } else if (t->kind==FE_TYPE_ARRAY) { - fprintf(e->out,"static void %s(%s *self) { unsigned long i; for (i=0; i<%lu; ++i) { ", - t->drop_cname,m7_c_type(e,t),t->length); - strcpy(access,"self->a[i]"); - m7_emit_drop_access(e,t->elem,access); - fputs("} }\n",e->out); - } else if (t->kind==FE_TYPE_OPTIONAL) { - fprintf(e->out,"static void %s(%s *self) { ", - t->drop_cname,m7_c_type(e,t)); - if (fe_m7_optional_uses_niche(t->elem)) { - fputs("if (*self) { ",e->out); - m7_emit_drop_access(e,t->elem,"*self"); - fputs("} ",e->out); - } else { - fputs("if (self->has) { ",e->out); - m7_emit_drop_access(e,t->elem,"self->v"); - fputs("self->has=0; } ",e->out); - } - fputs("}\n",e->out); - } else if (t->kind==FE_TYPE_ERROR_UNION && t->error_value && - t->error_value->kind!=FE_TYPE_VOID) { - fprintf(e->out,"static void %s(%s *self) { if (!self->e) { ", - t->drop_cname,m7_c_type(e,t)); - m7_emit_drop_access(e,t->error_value,"self->v"); - fputs("self->e=1; } }\n",e->out); - } - } - /* Error enums are scalar codes, so their enum payload helper functions - from M3 are deliberately not emitted in the M7 path. */ -} - -static void m7_emit_type_helpers(FeEmitter *e) -{ - FeType *t; - FeType *st; - unsigned i; - unsigned j; - const char *ct; - FeVariantType *v; - for (t=e->check->types.types;t;t=t->next) { - if (t->kind==FE_TYPE_OPTIONAL) { - if (!fe_m7_optional_uses_niche(t->elem)) { - fprintf(e->out,"static %s %s(%s v) { %s r; r.has=1; r.v=v; return r; }\n", - m7_c_type(e,t),t->maker,m7_c_type(e,t->elem),m7_c_type(e,t)); - fprintf(e->out,"static %s %s(void) { %s r; memset(&r,0,sizeof(r)); return r; }\n", - m7_c_type(e,t),t->none_cname,m7_c_type(e,t)); - fprintf(e->out,"static %s %s(%s x) { ", - m7_c_type(e,t->elem),t->unwrap_cname,m7_c_type(e,t)); - if (!e->no_checks) fputs("if (!x.has) fe_trap_bounds(); ",e->out); - fputs("return x.v; }\n",e->out); - } else { - fprintf(e->out,"static %s %s(%s x) { ", - m7_c_type(e,t->elem),t->unwrap_cname,m7_c_type(e,t)); - if (!e->no_checks) fputs("if (!x) fe_trap_bounds(); ",e->out); - fputs("return x; }\n",e->out); - } - } - if (t->kind==FE_TYPE_ERROR_UNION && t->error_value && - t->error_value->kind!=FE_TYPE_VOID) { - fprintf(e->out,"static %s %s(unsigned short e, %s v) { %s r; r.e=e; r.v=v; return r; }\n", - m7_c_type(e,t),t->maker,m7_c_type(e,t->error_value),m7_c_type(e,t)); - if (t->none_cname) - fprintf(e->out,"static %s %s(unsigned short e) { %s r; memset(&r,0,sizeof(r)); r.e=e; return r; }\n", - m7_c_type(e,t),t->none_cname,m7_c_type(e,t)); - if (t->error_value->kind==FE_TYPE_OWNED && - t->error_value->elem && t->error_value->elem->kind==FE_TYPE_SLICE) { - FeType *item=t->error_value->elem->elem; - fprintf(e->out,"static %s %s(unsigned long n) { %s r; r.v.p=(%s*)malloc(sizeof(%s)*n); r.v.n=n; r.e=(r.v.p || !n) ? 0 : 1; return r; }\n", - m7_c_type(e,t),t->alloc_cname,m7_c_type(e,t), - m7_c_type(e,item),m7_c_type(e,item)); - } else if (t->error_value->kind==FE_TYPE_OWNED) { - fprintf(e->out,"static %s %s(%s v) { %s r; r.v=(%s)malloc(sizeof(%s)); if(r.v) *r.v=v; r.e=r.v ? 0 : 1; return r; }\n", - m7_c_type(e,t),t->alloc_cname, - m7_c_type(e,t->error_value->elem),m7_c_type(e,t), - m7_c_type(e,t->error_value), - m7_c_type(e,t->error_value->elem)); - } - } - } - for (t=e->check->types.types;t;t=t->next) { - if (t->replace_cname) { - ct=m7_c_type(e,t); - fprintf(e->out,"static %s %s(%s *dst, %s value) { %s old=*dst; *dst=value; return old; }\n", - ct,t->replace_cname,ct,ct,ct); - } - if (t->kind==FE_TYPE_STRUCT && t->maker) { - fprintf(e->out,"static %s %s(",m7_c_type(e,t),t->maker); - for (i=0;ifield_count;i++) { - if (i) fputs(", ",e->out); - fputs(m7_c_type(e,t->fields[i].type),e->out); - fprintf(e->out," p%u",i); - } - fprintf(e->out,") { %s v;",m7_c_type(e,t)); - for (i=0;ifield_count;i++) - fprintf(e->out," v.%s=p%u;",t->fields[i].name,i); - fputs(" return v; }\n",e->out); - } else if (t->kind==FE_TYPE_ARRAY && t->maker) { - fprintf(e->out,"static %s %s(",m7_c_type(e,t),t->maker); - for (i=0;ilength;i++) { - if (i) fputs(", ",e->out); - fputs(m7_c_type(e,t->elem),e->out); - fprintf(e->out," p%u",i); - } - fprintf(e->out,") { %s v;",m7_c_type(e,t)); - for (i=0;ilength;i++) fprintf(e->out," v.a[%u]=p%u;",i,i); - fputs(" return v; }\n",e->out); - } else if (t->kind==FE_TYPE_ENUM && !t->is_error) { - for (i=0;ivariant_count;i++) { - v=&t->variants[i]; - fprintf(e->out,"static %s %s(",m7_c_type(e,t),v->maker); - for (j=0;jfield_count;j++) { - if (j) fputs(", ",e->out); - fputs(m7_c_type(e,v->fields[j].type),e->out); - fprintf(e->out," p%u",j); - } - fprintf(e->out,") { %s x; x.tag=%u;",m7_c_type(e,t),v->tag); - for (j=0;jfield_count;j++) { - if (v->field_count==1) - fprintf(e->out," x.payload.%s=p%u;",v->name,j); - else - fprintf(e->out," x.payload.%s.%s=p%u;",v->name, - v->fields[j].name,j); - } - fputs(" return x; }\n",e->out); - } - } - } - m7_emit_drop_helpers(e); - /* Reuse the mature M3 index/slice helper generator. It does not depend - on M7 drop policy and all wrapper dependencies are already emitted. */ - for (t=e->check->types.types;t;t=t->next) { - if (t->kind==FE_TYPE_ARRAY && t->indexer) { - fprintf(e->out,"static %s %s(%s x, unsigned long i) { ", - m7_c_type(e,t->elem),t->indexer,m7_c_type(e,t)); - if (!e->no_checks) - fprintf(e->out,"if (i >= %lu) fe_trap_bounds(); ",t->length); - fputs("return x.a[i]; }\n",e->out); - if (t->slicer) { - st=fe_type_slice(&e->check->types,t->elem); - fprintf(e->out,"static %s %s(%s *x, unsigned long a, unsigned long b) { ", - m7_c_type(e,st),t->slicer,m7_c_type(e,t)); - if (!e->no_checks) - fprintf(e->out,"if (a > b || b > %lu) fe_trap_bounds(); ",t->length); - fprintf(e->out,"return %s(x->a+a,b-a); }\n",st->maker); - fprintf(e->out,"static %s %s(%s *x) { return %s(x,0,%lu); }\n", - m7_c_type(e,st),t->full_slicer,m7_c_type(e,t),t->slicer,t->length); - fprintf(e->out,"static %s %s(%s *x, unsigned long a) { return %s(x,a,%lu); }\n", - m7_c_type(e,st),t->tail_slicer,m7_c_type(e,t),t->slicer,t->length); - } - } else if (t->kind==FE_TYPE_SLICE && t->indexer) { - fprintf(e->out,"static %s %s(%s x, unsigned long i) { ", - m7_c_type(e,t->elem),t->indexer,m7_c_type(e,t)); - if (!e->no_checks) fputs("if (i >= x.n) fe_trap_bounds(); ",e->out); - fputs("return x.p[i]; }\n",e->out); - if (t->slicer) { - fprintf(e->out,"static %s %s(%s x, unsigned long a, unsigned long b) { ", - m7_c_type(e,t),t->slicer,m7_c_type(e,t)); - if (!e->no_checks) - fputs("if (a > b || b > x.n) fe_trap_bounds(); ",e->out); - fprintf(e->out,"return %s(x.p+a,b-a); }\n",t->maker); - fprintf(e->out,"static %s %s(%s x) { return %s(x,0,x.n); }\n", - m7_c_type(e,t),t->full_slicer,m7_c_type(e,t),t->slicer); - fprintf(e->out,"static %s %s(%s x, unsigned long a) { return %s(x,a,x.n); }\n", - m7_c_type(e,t),t->tail_slicer,m7_c_type(e,t),t->slicer); - if (node_uses_trim(e->check->ast->root) && !t->ref_mut) { - fprintf(e->out, - "static %s fe_trim_%s(%s s) { unsigned long a=0; unsigned long b=s.n;" - " while (aa && (s.p[b-1]==' '||s.p[b-1]=='\\t'||s.p[b-1]=='\\r'||s.p[b-1]=='\\n')) --b;" - " return %s(s.p+a,b-a); }\n", - t->cname,t->cname,t->cname,t->maker); - } - } - } - } -} - -static void m7_emit_present(FeEmitter *e, FeType *opt, const char *name) -{ - if (fe_m7_optional_uses_niche(opt->elem)) { - fputs("(",e->out); fputs(name,e->out); fputs(" != 0)",e->out); - } else { - fputs(name,e->out); fputs(".has",e->out); - } -} - -static void m7_emit_payload_var(FeEmitter *e, FeType *opt, const char *name) -{ - (void)e; - fputs(name,e->out); - if (!fe_m7_optional_uses_niche(opt->elem)) fputs(".v",e->out); -} - -static void m7_emit_error_member(FeEmitter *e, FeNode *n) -{ - FeVariantType *v; - FeType *t; - t=n && n->a ? n->a->sem_type : 0; - v=t && t->kind==FE_TYPE_ENUM ? - fe_type_variant(t,n->b ? n->b->text : "") : 0; - if (v) fprintf(e->out,"%u",v->tag); - else fputs("0",e->out); -} - -static void m7_emit_raw_expr(FeEmitter *e, FeNode *n); - -static void m7_emit_contextual(FeEmitter *e, FeNode *n) -{ - FeType *ctx; - FeType *actual; - FeType *error_type; - ctx=n ? n->sem_context : 0; - actual=n ? n->sem_type : 0; - if (!ctx) { m7_emit_raw_expr(e,n); return; } - if (ctx->kind==FE_TYPE_OPTIONAL) { - if (fe_m7_is_null(n)) { - if (fe_m7_optional_uses_niche(ctx->elem)) fputs("0",e->out); - else { fputs(ctx->none_cname,e->out); fputs("()",e->out); } - return; - } - if (fe_m7_optional_uses_niche(ctx->elem)) { - m7_emit_raw_expr(e,n); - } else { - fputs(ctx->maker,e->out); fputc('(',e->out); - m7_emit_raw_expr(e,n); fputc(')',e->out); - } - return; - } - if (ctx->kind==FE_TYPE_ERROR_UNION) { - error_type=ctx->elem; - if (!error_type) error_type=fe_type_intern(&e->check->types,"core.Error"); - if (actual && fe_type_equal(actual,ctx->error_value)) { - if (ctx->error_value->kind==FE_TYPE_VOID) fputs("0",e->out); - else { - fputs(ctx->maker,e->out); fputs("(0, ",e->out); - m7_emit_raw_expr(e,n); fputc(')',e->out); - } - return; - } - if (actual && fe_type_equal(actual,error_type)) { - if (ctx->error_value->kind==FE_TYPE_VOID) - m7_emit_raw_expr(e,n); - else { - fputs(ctx->none_cname,e->out); fputc('(',e->out); - m7_emit_raw_expr(e,n); fputc(')',e->out); - } - return; - } - } - m7_emit_raw_expr(e,n); -} - -static void emit_expr(FeEmitter *e, FeNode *n) -{ - if (!n) { fputs("0",e->out); return; } - if (n->sem_context) m7_emit_contextual(e,n); - else m7_emit_raw_expr(e,n); -} - -static void emit_lvalue(FeEmitter *e, FeNode *n) -{ - FeType *bt; - if (!n) { fputs("fe_bad_lvalue",e->out); return; } - if (n->kind==FE_N_IDENT) { - fputs(cname(n,"fe_local"),e->out); - return; - } - /* A declaration names its own storage. The initializer for `let`/`var` is - emitted as a separate assignment statement, so the declaration node is - handed here as the target; without this it falls through to the raw - expression path, which emits a declaration as "0" and produces `0 = ...`. */ - if (n->kind==FE_N_LET || n->kind==FE_N_VAR || n->kind==FE_N_CONST) { - fputs(cname(n,"fe_local"),e->out); - return; - } - if (n->kind==FE_N_MEMBER) { - bt=n->a ? n->a->sem_type : 0; - if (n->text && strcmp(n->text,".?")==0) { - emit_expr(e,n); - return; - } - if ((bt && (bt->kind==FE_TYPE_REF || bt->kind==FE_TYPE_OWNED)) && - n->b && n->b->text && strcmp(n->b->text,"^")==0) { - fputs("(*",e->out); emit_expr(e,n->a); fputc(')',e->out); - } else if (bt && (bt->kind==FE_TYPE_REF || bt->kind==FE_TYPE_OWNED)) { - emit_expr(e,n->a); fputs("->",e->out); - fputs(n->b && n->b->text ? n->b->text : "member",e->out); - } else { - emit_lvalue(e,n->a); fputc('.',e->out); - fputs(n->b && n->b->text ? n->b->text : "member",e->out); - } - return; - } - if (n->kind==FE_N_INDEX) { - bt=n->a ? n->a->sem_type : 0; - emit_lvalue(e,n->a); - fputs(bt && bt->kind==FE_TYPE_ARRAY ? ".a[" : ".p[",e->out); - emit_expr(e,n->b); fputc(']',e->out); - return; - } - m7_emit_raw_expr(e,n); -} - -static void m7_emit_call(FeEmitter *e, FeNode *n) -{ - FeNode *x; - FeNode *call_param; - FeVariantType *v; - int special; - call_param=0; - special=0; - if (n->a && n->a->kind==FE_N_MEMBER && n->a->a && - n->a->a->kind==FE_N_IDENT && n->a->a->text && - strcmp(n->a->a->text,"mem")==0 && n->a->b && n->a->b->text && - strcmp(n->a->b->text,"destroy")==0 && n->children) { - FeNode *arg=n->children; - fputs("(free(",e->out); emit_expr(e,arg); - if (arg->sem_type && arg->sem_type->kind==FE_TYPE_OWNED && - arg->sem_type->elem && arg->sem_type->elem->kind==FE_TYPE_SLICE) - fputs(".p",e->out); - fputc(')',e->out); - if (arg->kind==FE_N_IDENT) { - fputs(", ",e->out); emit_lvalue(e,arg); - if (arg->sem_type && arg->sem_type->elem && - arg->sem_type->elem->kind==FE_TYPE_SLICE) fputs(".p=0",e->out); - else fputs("=0",e->out); - fputs(", fe_live_",e->out); fputs(cname(arg,"owned"),e->out); - fputs("=0",e->out); - } - fputs(", 0)",e->out); - return; - } - if (n->a && n->a->kind==FE_N_MEMBER && n->a->a && - n->a->a->kind==FE_N_IDENT && n->a->a->text && - strcmp(n->a->a->text,"mem")==0 && n->a->b && n->a->b->text && - strcmp(n->a->b->text,"replace")==0 && n->children && - n->children->next && n->sem_type) { - fputs(n->sem_type->replace_cname ? n->sem_type->replace_cname : - "fe_bad_replace",e->out); - fputc('(',e->out); emit_expr(e,n->children); fputs(", ",e->out); - emit_expr(e,n->children->next); fputc(')',e->out); - return; - } - if (n->a && n->a->kind==FE_N_MEMBER && n->a->a && - n->a->a->kind==FE_N_IDENT && n->a->a->text && - strcmp(n->a->a->text,"mem")==0 && n->a->b && n->a->b->text && - strcmp(n->a->b->text,"create")==0 && n->children) { - FeType *created=n->children->sem_type; - FeType *owned=fe_type_owned(&e->check->types,created); - FeType *result=fe_type_error_union(&e->check->types,owned); - fputs(result->alloc_cname ? result->alloc_cname : "fe_bad_alloc",e->out); - fputc('(',e->out); emit_expr(e,n->children); fputc(')',e->out); - return; - } - if (n->a && n->a->kind==FE_N_MEMBER && n->a->a && - n->a->a->kind==FE_N_IDENT && n->a->a->text && - strcmp(n->a->a->text,"mem")==0 && n->a->b && n->a->b->text && - strcmp(n->a->b->text,"alloc_slice")==0 && n->children && - n->children->next) { - FeType *result=n->sem_type; - fputs(result && result->alloc_cname ? result->alloc_cname : - "fe_bad_slice_alloc",e->out); - fputc('(',e->out); emit_expr(e,n->children->next); fputc(')',e->out); - return; - } - if (n->text && (strcmp(n->text,"@print")==0 || - strcmp(n->text,"@fprint")==0 || strcmp(n->text,"@sprint")==0)) { - emit_m4_builtin(e,n); - return; - } - /* Every other builtin -- @size_of, @align_of and friends -- is lowered by - the core emitter. Without this the generic path below emits the call - verbatim, which is not C. */ - if (!n->a && n->text && n->text[0]=='@') { - emit_expr_core(e,n); - return; - } - /* Same for the built-in alias methods on str: the core emitter knows how to - lower `line.trim()`, the generic member path would emit `.trim()`. */ - if (n->a && n->a->kind==FE_N_MEMBER && n->a->b && n->a->b->text && - strcmp(n->a->b->text,"trim")==0 && !n->children) { - emit_expr_core(e,n); - return; - } - if (n->a && n->a->kind==FE_N_MEMBER && n->a->a && - n->a->a->kind==FE_N_IDENT && n->a->a->text && - strcmp(n->a->a->text,"io")==0 && n->a->b && n->a->b->text && - strcmp(n->a->b->text,"null_writer")==0) { - fputs("fe_m4_null_writer()",e->out); - return; - } - if (n->a && n->a->kind==FE_N_MEMBER && n->a->a && - n->a->a->sem_type && n->a->a->sem_type->kind==FE_TYPE_ENUM && - !n->a->a->sem_type->is_error) { - v=fe_type_variant(n->a->a->sem_type,n->a->b ? n->a->b->text : ""); - fputs(v ? v->maker : "fe_bad_variant",e->out); - } else if (n->a && n->a->kind==FE_N_MEMBER && n->sem_decl && - n->sem_decl->kind==FE_N_FN) { - FeNode *mp=n->sem_decl->a ? n->sem_decl->a->children : 0; - FeNode *ma; - fputs(cname(n->sem_decl,"fe_method"),e->out); fputc('(',e->out); - if (mp && mp->sem_type && mp->sem_type->kind==FE_TYPE_REF) { - fputc('&',e->out); emit_lvalue(e,n->a->a); - } else emit_expr(e,n->a->a); - for (ma=n->children;ma;ma=ma->next) { - fputs(", ",e->out); emit_expr(e,ma); - } - fputc(')',e->out); - return; - } else if (n->a) emit_expr(e,n->a); - else fputs(n->text ? n->text : "fe_builtin",e->out); - if (!special) { - if (n->sem_decl && n->sem_decl->kind==FE_N_FN && n->sem_decl->a) - call_param=n->sem_decl->a->children; - fputc('(',e->out); - for (x=n->children;x;x=x->next) { - FeType *want=call_param && call_param->a ? - fe_type_from_ast(&e->check->types,call_param->a) : 0; - if (x!=n->children) fputs(", ",e->out); - if (want && want->kind==FE_TYPE_SLICE && !want->ref_mut && - x->sem_type && x->sem_type->kind==FE_TYPE_SLICE && - x->sem_type->ref_mut) { - fputs(want->maker,e->out); fputc('(',e->out); - emit_expr(e,x); fputs(".p, ",e->out); - emit_expr(e,x); fputs(".n)",e->out); - } else emit_expr(e,x); - if (call_param) call_param=call_param->next; - } - fputc(')',e->out); - } -} - -/* `dst = ;` as a statement, avoiding a comma expression on the right. - - A consumed identifier lowers to `(fe_live_x=0, x)`. When dst is a struct, - Watcom crashes on a struct assignment whose right side is a comma expression - -- hard enough to take DOSBox-X down with it -- so clear the move flag as its - own statement and assign the plain name. */ -static void m7_emit_assign_stmt(FeEmitter *e, const char *dst, FeNode *src) -{ - if (src && src->kind==FE_N_IDENT && (src->flags & FE_OWN_NODE_CONSUMED) && - src->sem_type && type_needs_drop(src->sem_type)) { - pad(e); fputs("fe_live_",e->out); fputs(cname(src,"owned"),e->out); - fputs("=0;\n",e->out); - pad(e); fputs(dst,e->out); fputs(" = ",e->out); - fputs(cname(src,"fe_missing"),e->out); fputs(";\n",e->out); - return; - } - pad(e); fputs(dst,e->out); fputs(" = ",e->out); - emit_expr(e,src); fputs(";\n",e->out); -} - -static void m7_emit_raw_expr(FeEmitter *e, FeNode *n) -{ - FeType *bt; - FeVariantType *v; - const char *op; - FeM7LazyKind lazy; - if (!n) { fputs("0",e->out); return; } - /* No feature scan: the switch below handles the node kinds this emitter - changes and its default hands everything else to emit_expr_core, so the - same path serves a unit whether or not it mentions optionals. */ - switch (n->kind) { - case FE_N_IDENT: - if ((n->flags & FE_OWN_NODE_CONSUMED) && n->sem_type && - type_needs_drop(n->sem_type)) { - fputs("(fe_live_",e->out); fputs(cname(n,"owned"),e->out); - fputs("=0, ",e->out); fputs(cname(n,"fe_missing"),e->out); - fputc(')',e->out); - } else fputs(cname(n,"fe_missing"),e->out); - break; - case FE_N_LITERAL: - if (fe_m7_is_null(n)) fputs("0",e->out); - else emit_expr_core(e,n); - break; - case FE_N_UNARY: - op=n->text ? n->text : ""; - if (strcmp(op,"try")==0) { - if (n->a && n->a->sem_type && - n->a->sem_type->kind==FE_TYPE_ERROR_UNION && - n->a->sem_type->error_value && - n->a->sem_type->error_value->kind!=FE_TYPE_VOID) { - fputs("(",e->out); fputs(n->aux_cname,e->out); - fputs(" = ",e->out); emit_expr(e,n->a); fputs(", ",e->out); - fputs(n->aux_cname,e->out); fputs(".v)",e->out); - } else emit_expr(e,n->a); - } else if (strcmp(op,"&")==0 || strcmp(op,"&mut")==0) { - fputs("(&",e->out); emit_lvalue(e,n->a); fputc(')',e->out); - } else if (strcmp(op,"not")==0) { - fputs("(!",e->out); emit_expr(e,n->a); fputc(')',e->out); - } else { - fputc('(',e->out); fputs(op,e->out); emit_expr(e,n->a); - fputc(')',e->out); - } - break; - case FE_N_BINARY: - lazy=fe_m7_lazy_kind(n); - if (lazy==FE_M7_LAZY_ORELSE) { - FeType *opt=n->a ? n->a->sem_type : 0; - fputs("((",e->out); fputs(n->aux_cname,e->out); fputs(" = ",e->out); - emit_expr(e,n->a); fputs("), ",e->out); - m7_emit_present(e,opt,n->aux_cname); fputs(" ? ",e->out); - if (fe_m7_optional_uses_niche(opt->elem)) fputs(n->aux_cname,e->out); - else { fputs(n->aux_cname,e->out); fputs(".v",e->out); } - fputs(" : ",e->out); emit_expr(e,n->b); fputc(')',e->out); - } else if (lazy==FE_M7_LAZY_CATCH && !n->c) { - FeType *res=n->a ? n->a->sem_type : 0; - fputs("((",e->out); fputs(n->aux_cname,e->out); fputs(" = ",e->out); - emit_expr(e,n->a); fputs("), ",e->out); - if (res && res->error_value && res->error_value->kind==FE_TYPE_VOID) { - fputs(n->aux_cname,e->out); fputs(" ? ",e->out); - emit_expr(e,n->b); fputs(" : 0)",e->out); - } else { - fputs(n->aux_cname,e->out); fputs(".e ? ",e->out); - emit_expr(e,n->b); fputs(" : ",e->out); - fputs(n->aux_cname,e->out); fputs(".v)",e->out); - } - } else if (lazy==FE_M7_LAZY_CATCH && n->c) { - fputs("0",e->out); - } else if ((n->text && (strcmp(n->text,"==")==0 || - strcmp(n->text,"!=")==0)) && - (fe_m7_is_null(n->a) || fe_m7_is_null(n->b))) { - FeNode *value=fe_m7_is_null(n->a) ? n->b : n->a; - FeType *opt=value ? value->sem_type : 0; - if (opt && opt->kind==FE_TYPE_OPTIONAL && - !fe_m7_optional_uses_niche(opt->elem)) { - fputs("(!",e->out); emit_expr(e,value); fputs(".has)",e->out); - if (strcmp(n->text,"!=")==0) { - fputs(" == 0",e->out); - } - } else { - fputc('(',e->out); emit_expr(e,value); - fputs(strcmp(n->text,"==")==0 ? " == 0)" : " != 0)",e->out); - } - } else { - op=n->text ? n->text : "+"; - fputc('(',e->out); emit_expr(e,n->a); - if (strcmp(op,"and")==0) fputs(" && ",e->out); - else if (strcmp(op,"or")==0) fputs(" || ",e->out); - else fputs(op,e->out); - emit_expr(e,n->b); fputc(')',e->out); - } - break; - case FE_N_MEMBER: - bt=n->a ? n->a->sem_type : 0; - if (n->text && strcmp(n->text,".?")==0 && bt && - bt->kind==FE_TYPE_OPTIONAL) { - fputs(bt->unwrap_cname,e->out); fputc('(',e->out); - emit_expr(e,n->a); fputc(')',e->out); - } else if (bt && bt->kind==FE_TYPE_ENUM && bt->is_error) { - m7_emit_error_member(e,n); - } else if ((bt && (bt->kind==FE_TYPE_REF || bt->kind==FE_TYPE_OWNED)) && - n->b && n->b->text && strcmp(n->b->text,"^")==0) { - fputs("(*",e->out); emit_expr(e,n->a); fputc(')',e->out); - } else if (bt && (bt->kind==FE_TYPE_REF || bt->kind==FE_TYPE_OWNED)) { - emit_expr(e,n->a); fputs("->",e->out); - fputs(n->b && n->b->text ? n->b->text : "member",e->out); - } else if (bt && bt->kind==FE_TYPE_ENUM && !bt->is_error) { - v=fe_type_variant(bt,n->b ? n->b->text : ""); - if (v) { fputs(v->maker,e->out); fputs("()",e->out); } - else fputs("0",e->out); - } else { - emit_expr(e,n->a); fputc('.',e->out); - if (n->b) fputs(n->b->text ? n->b->text : "member",e->out); - } - break; - case FE_N_CALL: - m7_emit_call(e,n); - break; - case FE_N_TYPE: - if (n->text && strcmp(n->text,"as")==0) { - fputs("((",e->out); fputs(m7_c_type(e,n->sem_type),e->out); - fputc(')',e->out); emit_expr(e,n->a); fputc(')',e->out); - } else emit_expr(e,n->a); - break; - case FE_N_INDEX: - bt=n->a ? n->a->sem_type : 0; - if (n->c || !n->b) { - emit_expr_core(e,n); - } else if (bt && bt->indexer) { - fputs(bt->indexer,e->out); fputc('(',e->out); - emit_expr(e,n->a); fputs(", ",e->out); emit_expr(e,n->b); - fputc(')',e->out); - } else fputs("0",e->out); - break; - case FE_N_STRUCT_INIT: - case FE_N_ARRAY_INIT: - emit_expr_core(e,n); - break; - default: - emit_expr_core(e,n); - break; - } -} - -/* Emit the initializer for a `const` declaration. - - A string literal normally lowers to a maker call, but C89 requires the - initializer of an aggregate -- at file scope and for automatics alike -- to be - a constant expression, and the build runs with -za. Emit the slice braced - instead. Returns non-zero when it handled the initializer. */ -static int m7_emit_const_init(FeEmitter *e, FeNode *n) -{ - if (n->kind!=FE_N_CONST || !n->b || n->b->kind!=FE_N_LITERAL || - !n->b->text || n->b->text[0]!='"') return 0; - fputs("{ (const unsigned char*)",e->out); - emit_c_literal(e->out,n->b->text,1); - fputs(", sizeof(",e->out); - emit_c_literal(e->out,n->b->text,1); - fputs(")-1 }",e->out); - return 1; -} - -static void emit_decl(FeEmitter *e, FeNode *n) -{ - pad(e); fputs(m7_c_type(e,n->sem_type),e->out); fputc(' ',e->out); - fputs(cname(n,"fe_local"),e->out); - if (n->kind==FE_N_CONST && n->b) { - fputs(" = ",e->out); - if (!m7_emit_const_init(e,n)) emit_expr(e,n->b); - } - fputs(";\n",e->out); - if ((n->kind==FE_N_LET || n->kind==FE_N_VAR) && n->sem_type && - type_needs_drop(n->sem_type)) { - pad(e); fputs("unsigned char fe_live_",e->out); - fputs(cname(n,"owned"),e->out); fputs("=0;\n",e->out); - } -} - -static void emit_owned_live(FeEmitter *e, FeNode *n, int value) -{ - if (n && n->sem_type && type_needs_drop(n->sem_type)) { - pad(e); fputs("fe_live_",e->out); fputs(cname(n,"owned"),e->out); - fprintf(e->out,"=%d;\n",value); - } -} - -static void emit_value_drop(FeEmitter *e, FeNode *n) -{ - FeType *t; - t=n ? n->sem_type : 0; - if (!n || !t || !type_needs_drop(t) || - (n->flags & FE_OWN_NODE_CONSUMED) || - (n->flags & FE_OWN_NODE_DEFER_CAPTURE)) return; - pad(e); fputs("if (fe_live_",e->out); fputs(cname(n,"owned"),e->out); - fputs(") { ",e->out); - if (t->kind==FE_TYPE_OWNED) { - if (t->elem && t->elem->kind==FE_TYPE_SLICE) { - fputs("free(",e->out); fputs(cname(n,"owned"),e->out); - fputs(".p); ",e->out); - } else { - if (t->elem && type_needs_drop(t->elem) && t->elem->drop_cname) - fprintf(e->out,"%s(%s); ",t->elem->drop_cname,cname(n,"owned")); - fputs("free(",e->out); fputs(cname(n,"owned"),e->out); - fputs("); ",e->out); - } - } else if (t->drop_cname) { - fprintf(e->out,"%s(&%s); ",t->drop_cname,cname(n,"local")); - } - fputs("fe_live_",e->out); fputs(cname(n,"owned"),e->out); - fputs("=0; }\n",e->out); -} - -static void emit_cleanup_block(FeEmitter *e, FeNode *n) -{ - FeNode *x; - unsigned count; - unsigned index; - unsigned seen; - unsigned depth; - count=0; - seen=0xffffffffU; - for (depth=0;depthblock_depth;++depth) - if (e->block_stack[depth]==n) { - seen=e->block_seen[depth]; - break; - } - for (x=n ? n->children : 0,index=0;x;x=x->next,++index) - if (indexkind==FE_N_DEFER || x->kind==FE_N_LET || - x->kind==FE_N_VAR)) ++count; - while (count) { - index=0; - for (x=n->children;x;x=x->next) - if ((x->kind==FE_N_DEFER || x->kind==FE_N_LET || - x->kind==FE_N_VAR) && index++==count-1U) { - if (x->kind==FE_N_DEFER) emit_stmt(e,x->a); - else emit_value_drop(e,x); - break; - } - --count; - } -} - -static void emit_cleanup_to(FeEmitter *e, unsigned floor) -{ - unsigned i; - for (i=e->block_depth;i>floor;--i) - emit_cleanup_block(e,e->block_stack[i-1U]); -} - -static void emit_param_cleanup(FeEmitter *e) -{ - FeNode *p; - if (!e->current_fn || !e->current_fn->a) return; - for (p=e->current_fn->a->children;p;p=p->next) emit_value_drop(e,p); -} - -static void emit_cleanup_all(FeEmitter *e) -{ - emit_cleanup_to(e,0); - emit_param_cleanup(e); -} - -static void emit_error_return(FeEmitter *e, const char *error_expr) -{ - FeType *ret=e->current_ret; - if (ret && ret->kind==FE_TYPE_ERROR_UNION && ret->error_value && - ret->error_value->kind!=FE_TYPE_VOID) { - fputs("return ",e->out); fputs(ret->none_cname,e->out); - fputc('(',e->out); fputs(error_expr,e->out); fputs(");\n",e->out); - } else { - fputs("return ",e->out); fputs(error_expr,e->out); fputs(";\n",e->out); - } -} - -static void m7_emit_try_error_check(FeEmitter *e, FeNode *n) -{ - FeType *result=n->a ? n->a->sem_type : 0; - pad(e); fputs(n->aux_cname,e->out); fputs(" = ",e->out); - emit_expr(e,n->a); fputs(";\n",e->out); - pad(e); fputs("if (",e->out); fputs(n->aux_cname,e->out); - if (result && result->error_value && result->error_value->kind!=FE_TYPE_VOID) - fputs(".e",e->out); - fputs(") {\n",e->out); ++e->indent; - emit_cleanup_all(e); - pad(e); - if (result && result->error_value && result->error_value->kind!=FE_TYPE_VOID) { - char error[192]; - sprintf(error,"%s.e",n->aux_cname); - emit_error_return(e,error); - } else emit_error_return(e,n->aux_cname); - --e->indent; pad(e); fputs("}\n",e->out); -} - -static void m7_emit_catch_block(FeEmitter *e, FeNode *n, - FeNode *target) -{ - FeType *result=n->a ? n->a->sem_type : 0; - FeNode *binding=n->b; - pad(e); fputs(n->aux_cname,e->out); fputs(" = ",e->out); - emit_expr(e,n->a); fputs(";\n",e->out); - pad(e); fputs("if (",e->out); fputs(n->aux_cname,e->out); - if (result && result->error_value && result->error_value->kind!=FE_TYPE_VOID) - fputs(".e",e->out); - fputs(") {\n",e->out); ++e->indent; - if (binding && binding->cname) { - pad(e); fputs("unsigned short ",e->out); fputs(binding->cname,e->out); - fputs(" = ",e->out); fputs(n->aux_cname,e->out); - if (result && result->error_value && result->error_value->kind!=FE_TYPE_VOID) - fputs(".e",e->out); - fputs(";\n",e->out); - } - emit_stmt(e,n->c); - --e->indent; pad(e); fputs("}",e->out); - if (target && result && result->error_value && - result->error_value->kind!=FE_TYPE_VOID) { - fputs(" else {\n",e->out); ++e->indent; - pad(e); emit_lvalue(e,target); fputs(" = ",e->out); - fputs(n->aux_cname,e->out); fputs(".v;\n",e->out); - emit_owned_live(e,target,1); - --e->indent; pad(e); fputs("}",e->out); - } - fputc('\n',e->out); -} - -static void m7_emit_optional_match(FeEmitter *e, FeNode *n) -{ - FeType *opt=n->a ? n->a->sem_type : 0; - FeNode *arm; - FeNode *binding; - int first; - pad(e); fputs(n->aux_cname,e->out); fputs(" = ",e->out); - emit_expr(e,n->a); fputs(";\n",e->out); - first=1; - for (arm=n->children;arm;arm=arm->next) { - if (arm->text && strcmp(arm->text,"Some")==0) { - pad(e); if (!first) fputs("else ",e->out); - fputs("if (",e->out); m7_emit_present(e,opt,n->aux_cname); - fputs(") {\n",e->out); ++e->indent; - binding=arm->children; - if (binding && binding->cname) { - pad(e); fputs(m7_c_type(e,binding->sem_type),e->out); - fputc(' ',e->out); fputs(binding->cname,e->out); fputs(" = ",e->out); - m7_emit_payload_var(e,opt,n->aux_cname); fputs(";\n",e->out); - } - if (arm->a) emit_stmt(e,arm->a); - --e->indent; pad(e); fputs("}\n",e->out); - first=0; - } else if (arm->text && strcmp(arm->text,"None")==0) { - pad(e); if (!first) fputs("else ",e->out); - fputs("if (!",e->out); m7_emit_present(e,opt,n->aux_cname); - fputs(") {\n",e->out); ++e->indent; - if (arm->a) emit_stmt(e,arm->a); - --e->indent; pad(e); fputs("}\n",e->out); - first=0; - } else if (arm->text && strcmp(arm->text,"_")==0) { - pad(e); if (!first) fputs("else ",e->out); - fputs("{\n",e->out); ++e->indent; - if (arm->a) emit_stmt(e,arm->a); - --e->indent; pad(e); fputs("}\n",e->out); - first=0; - } - } -} - -static void m7_emit_if_let(FeEmitter *e, FeNode *n) -{ - FeType *opt=n->a ? n->a->sem_type : 0; - FeNode *binding=n->children; - int some=n->aux_text && strcmp(n->aux_text,"Some")==0; - pad(e); fputs(n->aux_cname,e->out); fputs(" = ",e->out); - emit_expr(e,n->a); fputs(";\n",e->out); - pad(e); fputs("if (",e->out); - if (!some) fputc('!',e->out); - m7_emit_present(e,opt,n->aux_cname); fputs(") {\n",e->out); - ++e->indent; - if (some && binding && binding->cname) { - pad(e); fputs(m7_c_type(e,binding->sem_type),e->out); fputc(' ',e->out); - fputs(binding->cname,e->out); fputs(" = ",e->out); - m7_emit_payload_var(e,opt,n->aux_cname); fputs(";\n",e->out); - } - if (n->b) emit_stmt(e,n->b); - --e->indent; pad(e); fputs("}",e->out); - if (n->c) { - fputs(" else ",e->out); - emit_stmt(e,n->c); - } - fputc('\n',e->out); -} - -static void emit_block(FeEmitter *e, FeNode *n) -{ - FeNode *x; - unsigned seen; - if (!n) { - pad(e); fputs("{}",e->out); return; - } - pad(e); fputs("{\n",e->out); ++e->indent; - if (e->block_depth<32U) { - e->block_stack[e->block_depth]=n; - e->block_seen[e->block_depth]=0; - ++e->block_depth; - } - for (x=n->children;x;x=x->next) - if (x->kind==FE_N_LET || x->kind==FE_N_VAR || x->kind==FE_N_CONST) - emit_decl(e,x); - if (e->current_fn && e->current_fn->c==n) { - if (e->current_fn->a) { - FeNode *p; - for (p=e->current_fn->a->children;p;p=p->next) - if (p->sem_type && type_needs_drop(p->sem_type)) { - pad(e); fputs("unsigned char fe_live_",e->out); - fputs(cname(p,"owned"),e->out); fputs("=1;\n",e->out); - } - } - m7_emit_temp_decls(e,n); - } - if (e->current_ret && e->current_ret->kind!=FE_TYPE_VOID) { - pad(e); fputs(m7_c_type(e,e->current_ret),e->out); - fputs(" fe_return_value;\n",e->out); - } - seen=0; - for (x=n->children;x;x=x->next) { - ++seen; - if (e->block_depth) e->block_seen[e->block_depth-1U]=seen; - emit_stmt(e,x); - } - --e->indent; - emit_cleanup_block(e,n); - if (e->current_fn && e->current_fn->c==n) emit_param_cleanup(e); - if (e->block_depth) --e->block_depth; - if (e->fallthrough_block==n) { - pad(e); fputs("return 0;\n",e->out); - e->fallthrough_block=0; - } - pad(e); fputc('}',e->out); -} - -static void emit_stmt(FeEmitter *e, FeNode *n) -{ - FeType *result; - if (!n) return; - switch (n->kind) { - case FE_N_BLOCK: - emit_block(e,n); fputc('\n',e->out); break; - case FE_N_LET: - case FE_N_VAR: - if (n->b) { - if (fe_m7_is_try(n->b)) { - m7_emit_try_error_check(e,n->b); - pad(e); emit_lvalue(e,n); fputs(" = ",e->out); - fputs(n->b->aux_cname,e->out); - result=n->b->a ? n->b->a->sem_type : 0; - if (result && result->error_value && - result->error_value->kind!=FE_TYPE_VOID) fputs(".v",e->out); - fputs(";\n",e->out); emit_owned_live(e,n,1); - } else if (n->b->kind==FE_N_BINARY && n->b->c && - fe_m7_lazy_kind(n->b)==FE_M7_LAZY_CATCH) { - m7_emit_catch_block(e,n->b,n); - } else { - pad(e); emit_lvalue(e,n); fputs(" = ",e->out); - emit_expr(e,n->b); fputs(";\n",e->out); - emit_owned_live(e,n,1); - } - } - break; - case FE_N_ASSIGN: - if (n->a && n->a->kind==FE_N_IDENT) emit_value_drop(e,n->a); - pad(e); emit_lvalue(e,n->a); fputc(' ',e->out); - fputs(n->text ? n->text : "=",e->out); fputc(' ',e->out); - emit_expr(e,n->b); fputs(";\n",e->out); - if (n->a && n->a->kind==FE_N_IDENT) emit_owned_live(e,n->a,1); - break; - case FE_N_EXPR_STMT: - if (fe_m7_is_try(n->a)) { - m7_emit_try_error_check(e,n->a); - } else if (n->a && n->a->kind==FE_N_BINARY && n->a->c && - fe_m7_lazy_kind(n->a)==FE_M7_LAZY_CATCH) { - m7_emit_catch_block(e,n->a,0); - } else { - pad(e); emit_expr(e,n->a); fputs(";\n",e->out); - } - break; - case FE_N_DEFER: - break; - case FE_N_RETURN: - if (n->a && fe_m7_is_try(n->a)) { - FeNode *tr=n->a; - FeType *res=tr->a ? tr->a->sem_type : 0; - m7_emit_try_error_check(e,tr); - if (e->current_ret && e->current_ret->kind!=FE_TYPE_VOID) { - pad(e); fputs("fe_return_value = ",e->out); - if (e->current_ret->kind==FE_TYPE_ERROR_UNION && - e->current_ret->error_value && - e->current_ret->error_value->kind!=FE_TYPE_VOID) { - fputs(e->current_ret->maker,e->out); fputs("(0, ",e->out); - fputs(tr->aux_cname,e->out); - if (res && res->error_value && res->error_value->kind!=FE_TYPE_VOID) - fputs(".v",e->out); - fputc(')',e->out); - } else { - fputs(tr->aux_cname,e->out); - if (res && res->error_value && res->error_value->kind!=FE_TYPE_VOID) - fputs(".v",e->out); - } - fputs(";\n",e->out); - } - emit_cleanup_all(e); - pad(e); fputs("return fe_return_value;\n",e->out); - } else if (n->a && n->a->kind==FE_N_BINARY && n->a->c && - fe_m7_lazy_kind(n->a)==FE_M7_LAZY_CATCH) { - /* A value catch-block is lowered as a temporary local success - assignment; the handler is required by the checker to exit. */ - FeNode *cx=n->a; - FeType *res=cx->a ? cx->a->sem_type : 0; - pad(e); fputs(cx->aux_cname,e->out); fputs(" = ",e->out); - emit_expr(e,cx->a); fputs(";\n",e->out); - pad(e); fputs("if (",e->out); fputs(cx->aux_cname,e->out); - if (res && res->error_value && res->error_value->kind!=FE_TYPE_VOID) - fputs(".e",e->out); - fputs(") {\n",e->out); ++e->indent; - if (cx->b && cx->b->cname) { - pad(e); fputs("unsigned short ",e->out); fputs(cx->b->cname,e->out); - fputs(" = ",e->out); fputs(cx->aux_cname,e->out); - if (res && res->error_value && res->error_value->kind!=FE_TYPE_VOID) - fputs(".e",e->out); - fputs(";\n",e->out); - } - emit_stmt(e,cx->c); - --e->indent; pad(e); fputs("}\n",e->out); - pad(e); fputs("fe_return_value = ",e->out); - fputs(cx->aux_cname,e->out); - if (res && res->error_value && res->error_value->kind!=FE_TYPE_VOID) - fputs(".v",e->out); - fputs(";\n",e->out); - emit_cleanup_all(e); - pad(e); fputs("return fe_return_value;\n",e->out); - } else if (n->a && n->a->kind==FE_N_BINARY && !n->a->c && - fe_m7_lazy_kind(n->a)==FE_M7_LAZY_CATCH && - e->current_ret && e->current_ret->kind!=FE_TYPE_VOID) { - /* Short catch in return position. As an expression this lowers to - `((tmp = X), tmp.e ? fallback : tmp.v)`, and when X carries a move - it becomes a struct assignment whose right side is itself a comma - expression -- which crashes wcc386 hard enough to take DOSBox-X - down with it. The same lowering as statements is also plainer. */ - FeNode *cx=n->a; - FeType *res=cx->a ? cx->a->sem_type : 0; - int has_value=res && res->error_value && - res->error_value->kind!=FE_TYPE_VOID; - m7_emit_assign_stmt(e,cx->aux_cname,cx->a); - pad(e); fputs("if (",e->out); fputs(cx->aux_cname,e->out); - if (has_value) fputs(".e",e->out); - fputs(") {\n",e->out); ++e->indent; - pad(e); fputs("fe_return_value = ",e->out); - emit_expr(e,cx->b); fputs(";\n",e->out); - --e->indent; pad(e); fputs("} else {\n",e->out); ++e->indent; - pad(e); fputs("fe_return_value = ",e->out); - fputs(cx->aux_cname,e->out); - if (has_value) fputs(".v",e->out); - fputs(";\n",e->out); - --e->indent; pad(e); fputs("}\n",e->out); - emit_cleanup_all(e); - pad(e); fputs("return fe_return_value;\n",e->out); - } else { - if (n->a && e->current_ret && e->current_ret->kind!=FE_TYPE_VOID) { - pad(e); fputs("fe_return_value = ",e->out); - emit_expr(e,n->a); fputs(";\n",e->out); - } - emit_cleanup_all(e); - pad(e); fputs("return",e->out); - if (n->a) fputs(" fe_return_value",e->out); - fputs(";\n",e->out); - } - break; - case FE_N_IF: - if (n->text && strcmp(n->text,"if let")==0) { - m7_emit_if_let(e,n); - } else { - pad(e); fputs("if (",e->out); emit_expr(e,n->a); fputs(") ",e->out); - emit_block(e,n->b); - if (n->c) { - fputs(" else ",e->out); - if (n->c->kind==FE_N_IF) emit_stmt(e,n->c); - else emit_block(e,n->c); - } - fputc('\n',e->out); - } - break; - case FE_N_MATCH: - if (n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_OPTIONAL) - m7_emit_optional_match(e,n); - else emit_match(e,n,0); - break; - case FE_N_BREAK: - case FE_N_CONTINUE: - if (e->loop_depth) { - emit_cleanup_to(e,e->loop_floor[e->loop_depth-1U]); - pad(e); fputs(n->kind==FE_N_BREAK ? "break;\n" : "continue;\n",e->out); - } - break; - case FE_N_WHILE: - pad(e); fputs("while (",e->out); emit_expr(e,n->a); fputs(") ",e->out); - if (e->loop_depth<16U) e->loop_floor[e->loop_depth++]=e->block_depth; - emit_block(e,n->b); - if (e->loop_depth) --e->loop_depth; - fputc('\n',e->out); - break; - case FE_N_FOR: - emit_stmt_core(e,n); - break; - default: - emit_stmt_core(e,n); - break; - } -} - -static void emit_fn(FeEmitter *e, FeNode *fn, int prototype) -{ - FeNode *p; - FeType *old_ret; - FeNode *old_fn; - fputs(m7_c_type(e,fn->sem_type ? fn->sem_type : - (fn->b ? fe_type_from_ast(&e->check->types,fn->b) : - fe_type_intern(&e->check->types,"void"))),e->out); - fputc(' ',e->out); fputs(cname(fn,"fe_fn"),e->out); fputc('(',e->out); - p=fn->a ? fn->a->children : 0; - if (!p) fputs("void",e->out); - while (p) { - if (p!=fn->a->children) fputs(", ",e->out); - fputs(m7_c_type(e,p->sem_type ? p->sem_type : - fe_type_from_ast(&e->check->types,p->a)),e->out); - fputc(' ',e->out); fputs(cname(p,"fe_arg"),e->out); - p=p->next; - } - fputc(')',e->out); - if (prototype) { fputs(";\n",e->out); return; } - old_ret=e->current_ret; - old_fn=e->current_fn; - e->current_ret=fn->sem_type; - e->current_fn=fn; - m7_prepare_temps(e,fn->c); - fputc(' ',e->out); - if (fn->sem_type && fn->sem_type->kind==FE_TYPE_ERROR_UNION && - fn->sem_type->error_value && fn->sem_type->error_value->kind==FE_TYPE_VOID) - e->fallthrough_block=fn->c; - emit_block(e,fn->c); - e->current_ret=old_ret; - e->current_fn=old_fn; - fputc('\n',e->out); -} - -static void emit_main_wrapper(FeEmitter *e, FeNode *fn) -{ - FeType *ret=fn->sem_type; - if (ret && ret->kind==FE_TYPE_ERROR_UNION && ret->error_value && - ret->error_value->kind!=FE_TYPE_VOID) { - fputs("int main(void) { ",e->out); fputs(m7_c_type(e,ret),e->out); - fputs(" r = ",e->out); fputs(cname(fn,"fe_main"),e->out); - fputs("(); return r.e ? 1 : 0; }\n",e->out); - } else emit_main_wrapper_core(e,fn); -} - -void fe_emit_c_program(FeEmitter *e) -{ - FeNode *n; - FeNode *main_fn; - FeType *type; - int need_m4; - main_fn=0; - need_m4=node_uses_m4(e->check->ast->root); - for (type=e->check->types.types;type;type=type->next) - if (strcmp(type->name,"io.Writer")==0) need_m4=1; - /* See emit_c.c: stdio only comes in with the M4 writer runtime. */ - fputs("/* generated by fec M7 */\n#include \n#include \n#include \n",e->out); - if (need_m4) fputs("#include \n",e->out); - fputs("typedef char fe_assert_u8[(sizeof(unsigned char)==1) ? 1 : -1];\ntypedef char fe_assert_u16[(sizeof(unsigned short)==2) ? 1 : -1];\ntypedef char fe_assert_u32[(sizeof(unsigned long)==4) ? 1 : -1];\n",e->out); - if (e->pointer_bits==16) - fputs("typedef char fe_assert_usize[(sizeof(unsigned short)==2) ? 1 : -1];\n",e->out); - else - fputs("typedef char fe_assert_usize[(sizeof(unsigned long)==4) ? 1 : -1];\n",e->out); - fputs("static void fe_trap_bounds(void) { abort(); }\nstatic unsigned short fe_error_temp;\n\n",e->out); - emit_type_defs(e); - if (need_m4) emit_m4_runtime(e); - m7_emit_type_helpers(e); - for (n=e->check->ast->root ? e->check->ast->root->children : 0;n;n=n->next) { - if (n->kind==FE_N_GLOBAL || n->kind==FE_N_CONST) { - fputs(m7_c_type(e,n->sem_type),e->out); fputc(' ',e->out); - fputs(cname(n,"fe_global"),e->out); - if (n->b) { - fputs(" = ",e->out); - if (!m7_emit_const_init(e,n)) emit_expr(e,n->b); - } - fputs(";\n",e->out); - } - } - for (n=e->check->ast->root ? e->check->ast->root->children : 0;n;n=n->next) - if (n->kind==FE_N_FN) { - emit_fn(e,n,1); - if (n->text && strcmp(n->text,"main")==0) main_fn=n; - } - for (n=e->check->ast->root ? e->check->ast->root->children : 0;n;n=n->next) - if (n->kind==FE_N_STRUCT) { - FeNode *m; - for (m=n->children;m;m=m->next) - if (m->kind==FE_N_FN) emit_fn(e,m,1); - } - fputc('\n',e->out); - for (n=e->check->ast->root ? e->check->ast->root->children : 0;n;n=n->next) - if (n->kind==FE_N_FN) emit_fn(e,n,0); - for (n=e->check->ast->root ? e->check->ast->root->children : 0;n;n=n->next) - if (n->kind==FE_N_STRUCT) { - FeNode *m; - for (m=n->children;m;m=m->next) - if (m->kind==FE_N_FN) emit_fn(e,m,0); - } - if (main_fn) { fputc('\n',e->out); emit_main_wrapper(e,main_fn); } -} diff --git a/fec/src/emit_c.h b/fec/src/emit_c.h deleted file mode 100644 index 8be10e6..0000000 --- a/fec/src/emit_c.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef FE_EMIT_C_H -#define FE_EMIT_C_H - -#include "check.h" -#include "own.h" - -typedef struct FeEmitter { - FILE *out; - FeCheck *check; - unsigned pointer_bits; - int indent; - int no_checks; - unsigned temp_serial; - FeNode *fallthrough_block; - FeNode *block_stack[32]; - unsigned block_seen[32]; - unsigned block_depth; - unsigned loop_floor[16]; - unsigned loop_depth; - FeType *current_ret; - FeNode *current_fn; -} FeEmitter; - -void fe_emit_c_init(FeEmitter *e, FILE *out, FeCheck *check, - unsigned pointer_bits, int no_checks); -void fe_emit_c_program(FeEmitter *e); - -#endif diff --git a/fec/src/lower.c b/fec/src/lower.c deleted file mode 100644 index abd6942..0000000 --- a/fec/src/lower.c +++ /dev/null @@ -1,227 +0,0 @@ -#include "lower.h" -#include - -static int lower_grow_scopes(FeLowerPlan *plan) -{ - FeLowerScope *items; - unsigned capacity; - if (plan->scope_count < plan->scope_capacity) return 1; - capacity = plan->scope_capacity ? plan->scope_capacity * 2U : 8U; - items = (FeLowerScope *)fe_arena_alloc(plan->arena, - capacity * sizeof(FeLowerScope)); - if (!items) return 0; - if (plan->scopes) - memcpy(items, plan->scopes, - plan->scope_count * sizeof(FeLowerScope)); - plan->scopes = items; - plan->scope_capacity = capacity; - return 1; -} - -static int lower_grow_cleanups(FeLowerPlan *plan) -{ - FeLowerCleanup *items; - unsigned capacity; - if (plan->cleanup_count < plan->cleanup_capacity) return 1; - capacity = plan->cleanup_capacity ? plan->cleanup_capacity * 2U : 16U; - items = (FeLowerCleanup *)fe_arena_alloc(plan->arena, - capacity * sizeof(FeLowerCleanup)); - if (!items) return 0; - if (plan->cleanups) - memcpy(items, plan->cleanups, - plan->cleanup_count * sizeof(FeLowerCleanup)); - plan->cleanups = items; - plan->cleanup_capacity = capacity; - return 1; -} - -static unsigned lower_add_scope(FeLowerPlan *plan, FeNode *block, - unsigned parent) -{ - FeLowerScope *scope; - unsigned index; - if (!lower_grow_scopes(plan)) return FE_LOWER_NO_SCOPE; - index = plan->scope_count++; - scope = &plan->scopes[index]; - scope->block = block; - scope->parent = parent; - scope->ordinal = plan->next_ordinal++; - return index; -} - -static int lower_add_cleanup(FeLowerPlan *plan, unsigned scope, - FeLowerCleanupKind kind, FeNode *node, - FeNode *decl, FeType *type) -{ - FeLowerCleanup *cleanup; - if (!lower_grow_cleanups(plan)) return 0; - cleanup = &plan->cleanups[plan->cleanup_count++]; - cleanup->kind = kind; - cleanup->scope = scope; - cleanup->ordinal = plan->next_ordinal++; - cleanup->node = node; - cleanup->decl = decl; - cleanup->type = type; - return 1; -} - -int fe_lower_type_needs_drop(const FeType *type) -{ - unsigned i; - unsigned j; - if (!type) return 0; - if (type->kind == FE_TYPE_OWNED) return 1; - if (type->kind == FE_TYPE_OPTIONAL) - return fe_lower_type_needs_drop(type->elem); - if (type->kind == FE_TYPE_ERROR_UNION) - return fe_lower_type_needs_drop(type->error_value); - if (type->kind == FE_TYPE_ARRAY) - return fe_lower_type_needs_drop(type->elem); - if (type->kind == FE_TYPE_STRUCT) { - if (type->has_drop) return 1; - for (i = 0; i < type->field_count; ++i) - if (fe_lower_type_needs_drop(type->fields[i].type)) return 1; - return 0; - } - if (type->kind == FE_TYPE_ENUM) { - for (i = 0; i < type->variant_count; ++i) - for (j = 0; j < type->variants[i].field_count; ++j) - if (fe_lower_type_needs_drop(type->variants[i].fields[j].type)) - return 1; - } - return 0; -} - -static int lower_build_node(FeLowerPlan *plan, FeNode *node, - unsigned scope); - -static int lower_build_list(FeLowerPlan *plan, FeNode *node, - unsigned scope) -{ - while (node) { - if (!lower_build_node(plan, node, scope)) return 0; - node = node->next; - } - return 1; -} - -static int lower_build_block(FeLowerPlan *plan, FeNode *block, - unsigned parent) -{ - unsigned scope; - if (!block || block->kind != FE_N_BLOCK) return 1; - scope = lower_add_scope(plan, block, parent); - if (scope == FE_LOWER_NO_SCOPE) return 0; - return lower_build_list(plan, block->children, scope); -} - -static int lower_build_node(FeLowerPlan *plan, FeNode *node, - unsigned scope) -{ - FeNode *child; - FeType *type; - if (!node) return 1; - if (node->kind == FE_N_BLOCK) - return lower_build_block(plan, node, scope); - if (node->kind == FE_N_DEFER) { - if (!lower_add_cleanup(plan, scope, FE_LOWER_CLEANUP_DEFER, - node->a, node, 0)) - return 0; - return lower_build_node(plan, node->a, scope); - } - if (node->kind == FE_N_LET || node->kind == FE_N_VAR || - node->kind == FE_N_CONST) { - type = node->sem_type; - if (fe_lower_type_needs_drop(type)) - if (!lower_add_cleanup(plan, scope, FE_LOWER_CLEANUP_DROP, - node, node, type)) - return 0; - } - if (!lower_build_node(plan, node->a, scope)) return 0; - if (!lower_build_node(plan, node->b, scope)) return 0; - if (!lower_build_node(plan, node->c, scope)) return 0; - for (child = node->children; child; child = child->next) - if (!lower_build_node(plan, child, scope)) return 0; - return 1; -} - -void fe_lower_plan_init(FeLowerPlan *plan, FeArena *arena) -{ - if (!plan) return; - plan->arena = arena; - plan->fn = 0; - plan->scopes = 0; - plan->scope_count = 0; - plan->scope_capacity = 0; - plan->cleanups = 0; - plan->cleanup_count = 0; - plan->cleanup_capacity = 0; - plan->next_ordinal = 0; -} - -int fe_lower_plan_build(FeLowerPlan *plan, FeNode *fn) -{ - if (!plan || !plan->arena || !fn || fn->kind != FE_N_FN) return 0; - plan->fn = fn; - plan->scopes = 0; - plan->scope_count = 0; - plan->scope_capacity = 0; - plan->cleanups = 0; - plan->cleanup_count = 0; - plan->cleanup_capacity = 0; - plan->next_ordinal = 0; - if (!fn->c) return 1; - return lower_build_block(plan, fn->c, FE_LOWER_NO_SCOPE); -} - -unsigned fe_lower_scope_for_block(const FeLowerPlan *plan, - const FeNode *block) -{ - unsigned i; - if (!plan || !block) return FE_LOWER_NO_SCOPE; - for (i = 0; i < plan->scope_count; ++i) - if (plan->scopes[i].block == block) return i; - return FE_LOWER_NO_SCOPE; -} - -unsigned fe_lower_collect_cleanups(const FeLowerPlan *plan, - const FeNode *from_block, - const FeNode *stop_block, - const FeLowerCleanup **out, - unsigned out_capacity) -{ - unsigned scope; - unsigned stop; - unsigned i; - unsigned count; - if (!plan || !from_block) return 0; - scope = fe_lower_scope_for_block(plan, from_block); - stop = stop_block ? fe_lower_scope_for_block(plan, stop_block) : - FE_LOWER_NO_SCOPE; - count = 0; - while (scope != FE_LOWER_NO_SCOPE && scope != stop) { - for (i = plan->cleanup_count; i > 0; --i) { - if (plan->cleanups[i - 1U].scope != scope) continue; - if (out && count < out_capacity) - out[count] = &plan->cleanups[i - 1U]; - ++count; - } - scope = plan->scopes[scope].parent; - } - return count; -} - -int fe_lower_exit_runs_cleanup(FeLowerExitKind kind) -{ - return kind == FE_LOWER_EXIT_FALLTHROUGH || - kind == FE_LOWER_EXIT_RETURN || - kind == FE_LOWER_EXIT_ERROR_RETURN || - kind == FE_LOWER_EXIT_BREAK || - kind == FE_LOWER_EXIT_CONTINUE; -} - -int fe_lower_exit_leaves_function(FeLowerExitKind kind) -{ - return kind == FE_LOWER_EXIT_RETURN || - kind == FE_LOWER_EXIT_ERROR_RETURN; -} diff --git a/fec/src/lower.h b/fec/src/lower.h deleted file mode 100644 index bb2e544..0000000 --- a/fec/src/lower.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef FE_LOWER_H -#define FE_LOWER_H - -#include "types.h" - -#define FE_LOWER_NO_SCOPE ((unsigned)~0U) - -typedef enum FeLowerExitKind { - FE_LOWER_EXIT_FALLTHROUGH = 0, - FE_LOWER_EXIT_RETURN, - FE_LOWER_EXIT_ERROR_RETURN, - FE_LOWER_EXIT_BREAK, - FE_LOWER_EXIT_CONTINUE -} FeLowerExitKind; - -typedef enum FeLowerCleanupKind { - FE_LOWER_CLEANUP_DROP = 0, - FE_LOWER_CLEANUP_DEFER -} FeLowerCleanupKind; - -typedef struct FeLowerScope { - FeNode *block; - unsigned parent; - unsigned ordinal; -} FeLowerScope; - -typedef struct FeLowerCleanup { - FeLowerCleanupKind kind; - unsigned scope; - unsigned ordinal; - FeNode *node; - FeNode *decl; - FeType *type; -} FeLowerCleanup; - -typedef struct FeLowerPlan { - FeArena *arena; - FeNode *fn; - FeLowerScope *scopes; - unsigned scope_count; - unsigned scope_capacity; - FeLowerCleanup *cleanups; - unsigned cleanup_count; - unsigned cleanup_capacity; - unsigned next_ordinal; -} FeLowerPlan; - -void fe_lower_plan_init(FeLowerPlan *plan, FeArena *arena); -int fe_lower_plan_build(FeLowerPlan *plan, FeNode *fn); -unsigned fe_lower_scope_for_block(const FeLowerPlan *plan, - const FeNode *block); -unsigned fe_lower_collect_cleanups(const FeLowerPlan *plan, - const FeNode *from_block, - const FeNode *stop_block, - const FeLowerCleanup **out, - unsigned out_capacity); -int fe_lower_type_needs_drop(const FeType *type); -int fe_lower_exit_runs_cleanup(FeLowerExitKind kind); -int fe_lower_exit_leaves_function(FeLowerExitKind kind); - -#endif diff --git a/fec/tests/m4/bad-ari.fe b/fec/tests/format/bad-ari.fe similarity index 100% rename from fec/tests/m4/bad-ari.fe rename to fec/tests/format/bad-ari.fe diff --git a/fec/tests/m4/bad-bufw.fe b/fec/tests/format/bad-bufw.fe similarity index 100% rename from fec/tests/m4/bad-bufw.fe rename to fec/tests/format/bad-bufw.fe diff --git a/fec/tests/m4/bad-cls.fe b/fec/tests/format/bad-cls.fe similarity index 100% rename from fec/tests/m4/bad-cls.fe rename to fec/tests/format/bad-cls.fe diff --git a/fec/tests/m4/bad-many.fe b/fec/tests/format/bad-many.fe similarity index 100% rename from fec/tests/m4/bad-many.fe rename to fec/tests/format/bad-many.fe diff --git a/fec/tests/m4/bad-open.fe b/fec/tests/format/bad-open.fe similarity index 100% rename from fec/tests/m4/bad-open.fe rename to fec/tests/format/bad-open.fe diff --git a/fec/tests/m4/bad-run.fe b/fec/tests/format/bad-run.fe similarity index 100% rename from fec/tests/m4/bad-run.fe rename to fec/tests/format/bad-run.fe diff --git a/fec/tests/m4/bad-try.fe b/fec/tests/format/bad-try.fe similarity index 100% rename from fec/tests/m4/bad-try.fe rename to fec/tests/format/bad-try.fe diff --git a/fec/tests/m4/bad-type.fe b/fec/tests/format/bad-type.fe similarity index 100% rename from fec/tests/m4/bad-type.fe rename to fec/tests/format/bad-type.fe diff --git a/fec/tests/m4/bad-verb.fe b/fec/tests/format/bad-verb.fe similarity index 100% rename from fec/tests/m4/bad-verb.fe rename to fec/tests/format/bad-verb.fe diff --git a/fec/tests/m4/bad-writ.fe b/fec/tests/format/bad-writ.fe similarity index 100% rename from fec/tests/m4/bad-writ.fe rename to fec/tests/format/bad-writ.fe diff --git a/fec/tests/m4/format.fe b/fec/tests/format/ok-format.fe similarity index 100% rename from fec/tests/m4/format.fe rename to fec/tests/format/ok-format.fe diff --git a/fec/tests/m4/prop.fe b/fec/tests/format/ok-prop.fe similarity index 100% rename from fec/tests/m4/prop.fe rename to fec/tests/format/ok-prop.fe diff --git a/fec/tests/m4/try-fpr.fe b/fec/tests/format/ok-try-fpr.fe similarity index 100% rename from fec/tests/m4/try-fpr.fe rename to fec/tests/format/ok-try-fpr.fe diff --git a/fec/tests/m9/README.md b/fec/tests/generic/README.md similarity index 100% rename from fec/tests/m9/README.md rename to fec/tests/generic/README.md diff --git a/fec/tests/m9/badarg.fe b/fec/tests/generic/badarg.fe similarity index 100% rename from fec/tests/m9/badarg.fe rename to fec/tests/generic/badarg.fe diff --git a/fec/tests/m9/badarity.fe b/fec/tests/generic/badarity.fe similarity index 100% rename from fec/tests/m9/badarity.fe rename to fec/tests/generic/badarity.fe diff --git a/fec/tests/m9/badbody.fe b/fec/tests/generic/badbody.fe similarity index 100% rename from fec/tests/m9/badbody.fe rename to fec/tests/generic/badbody.fe diff --git a/fec/tests/m9/baddepth.fe b/fec/tests/generic/baddepth.fe similarity index 100% rename from fec/tests/m9/baddepth.fe rename to fec/tests/generic/baddepth.fe diff --git a/fec/tests/m9/baddist.fe b/fec/tests/generic/baddist.fe similarity index 100% rename from fec/tests/m9/baddist.fe rename to fec/tests/generic/baddist.fe diff --git a/fec/tests/m9/badfew.fe b/fec/tests/generic/badfew.fe similarity index 100% rename from fec/tests/m9/badfew.fe rename to fec/tests/generic/badfew.fe diff --git a/fec/tests/m9/badinfer.fe b/fec/tests/generic/badinfer.fe similarity index 100% rename from fec/tests/m9/badinfer.fe rename to fec/tests/generic/badinfer.fe diff --git a/fec/tests/m9/badop.fe b/fec/tests/generic/badop.fe similarity index 100% rename from fec/tests/m9/badop.fe rename to fec/tests/generic/badop.fe diff --git a/fec/tests/m9/badtype.fe b/fec/tests/generic/badtype.fe similarity index 100% rename from fec/tests/m9/badtype.fe rename to fec/tests/generic/badtype.fe diff --git a/fec/tests/m9/badvalue.fe b/fec/tests/generic/badvalue.fe similarity index 100% rename from fec/tests/m9/badvalue.fe rename to fec/tests/generic/badvalue.fe diff --git a/fec/tests/m9/defscope/lib.fe b/fec/tests/generic/defscope/lib.fe similarity index 100% rename from fec/tests/m9/defscope/lib.fe rename to fec/tests/generic/defscope/lib.fe diff --git a/fec/tests/m9/defscope/main.fe b/fec/tests/generic/defscope/main.fe similarity index 100% rename from fec/tests/m9/defscope/main.fe rename to fec/tests/generic/defscope/main.fe diff --git a/fec/tests/m9/okalias.fe b/fec/tests/generic/okalias.fe similarity index 100% rename from fec/tests/m9/okalias.fe rename to fec/tests/generic/okalias.fe diff --git a/fec/tests/m9/okbox.fe b/fec/tests/generic/okbox.fe similarity index 100% rename from fec/tests/m9/okbox.fe rename to fec/tests/generic/okbox.fe diff --git a/fec/tests/m9/okdedup.fe b/fec/tests/generic/okdedup.fe similarity index 100% rename from fec/tests/m9/okdedup.fe rename to fec/tests/generic/okdedup.fe diff --git a/fec/tests/m9/okid.fe b/fec/tests/generic/okid.fe similarity index 100% rename from fec/tests/m9/okid.fe rename to fec/tests/generic/okid.fe diff --git a/fec/tests/m9/okisint.fe b/fec/tests/generic/okisint.fe similarity index 100% rename from fec/tests/m9/okisint.fe rename to fec/tests/generic/okisint.fe diff --git a/fec/tests/m9/okmulti.fe b/fec/tests/generic/okmulti.fe similarity index 100% rename from fec/tests/m9/okmulti.fe rename to fec/tests/generic/okmulti.fe diff --git a/fec/tests/m9/oknested.fe b/fec/tests/generic/oknested.fe similarity index 100% rename from fec/tests/m9/oknested.fe rename to fec/tests/generic/oknested.fe diff --git a/fec/tests/m9/okpair.fe b/fec/tests/generic/okpair.fe similarity index 100% rename from fec/tests/m9/okpair.fe rename to fec/tests/generic/okpair.fe diff --git a/fec/tests/m9/oksamrec.fe b/fec/tests/generic/oksamrec.fe similarity index 100% rename from fec/tests/m9/oksamrec.fe rename to fec/tests/generic/oksamrec.fe diff --git a/fec/tests/m9/okscope/lib.fe b/fec/tests/generic/okscope/lib.fe similarity index 100% rename from fec/tests/m9/okscope/lib.fe rename to fec/tests/generic/okscope/lib.fe diff --git a/fec/tests/m9/okscope/main.fe b/fec/tests/generic/okscope/main.fe similarity index 100% rename from fec/tests/m9/okscope/main.fe rename to fec/tests/generic/okscope/main.fe diff --git a/fec/tests/m9/okskip.fe b/fec/tests/generic/okskip.fe similarity index 100% rename from fec/tests/m9/okskip.fe rename to fec/tests/generic/okskip.fe diff --git a/fec/tests/m9/oktypeeq.fe b/fec/tests/generic/oktypeeq.fe similarity index 100% rename from fec/tests/m9/oktypeeq.fe rename to fec/tests/generic/oktypeeq.fe diff --git a/fec/tests/m7/README.md b/fec/tests/optional/README.md similarity index 100% rename from fec/tests/m7/README.md rename to fec/tests/optional/README.md diff --git a/fec/tests/m7/badcatch.fe b/fec/tests/optional/badcatch.fe similarity index 100% rename from fec/tests/m7/badcatch.fe rename to fec/tests/optional/badcatch.fe diff --git a/fec/tests/m7/baddef.fe b/fec/tests/optional/baddef.fe similarity index 100% rename from fec/tests/m7/baddef.fe rename to fec/tests/optional/baddef.fe diff --git a/fec/tests/m7/baddir.fe b/fec/tests/optional/baddir.fe similarity index 100% rename from fec/tests/m7/baddir.fe rename to fec/tests/optional/baddir.fe diff --git a/fec/tests/m7/badercod.fe b/fec/tests/optional/badercod.fe similarity index 100% rename from fec/tests/m7/badercod.fe rename to fec/tests/optional/badercod.fe diff --git a/fec/tests/m7/badernam.fe b/fec/tests/optional/badernam.fe similarity index 100% rename from fec/tests/m7/badernam.fe rename to fec/tests/optional/badernam.fe diff --git a/fec/tests/m7/badetype.fe b/fec/tests/optional/badetype.fe similarity index 100% rename from fec/tests/m7/badetype.fe rename to fec/tests/optional/badetype.fe diff --git a/fec/tests/m7/badnull.fe b/fec/tests/optional/badnull.fe similarity index 100% rename from fec/tests/m7/badnull.fe rename to fec/tests/optional/badnull.fe diff --git a/fec/tests/m7/badoref.fe b/fec/tests/optional/badoref.fe similarity index 100% rename from fec/tests/m7/badoref.fe rename to fec/tests/optional/badoref.fe diff --git a/fec/tests/m7/badorel.fe b/fec/tests/optional/badorel.fe similarity index 100% rename from fec/tests/m7/badorel.fe rename to fec/tests/optional/badorel.fe diff --git a/fec/tests/m7/badproj.fe b/fec/tests/optional/badproj.fe similarity index 100% rename from fec/tests/m7/badproj.fe rename to fec/tests/optional/badproj.fe diff --git a/fec/tests/m7/badqmark.fe b/fec/tests/optional/badqmark.fe similarity index 100% rename from fec/tests/m7/badqmark.fe rename to fec/tests/optional/badqmark.fe diff --git a/fec/tests/m7/badret.fe b/fec/tests/optional/badret.fe similarity index 100% rename from fec/tests/m7/badret.fe rename to fec/tests/optional/badret.fe diff --git a/fec/tests/m7/badsome.fe b/fec/tests/optional/badsome.fe similarity index 100% rename from fec/tests/m7/badsome.fe rename to fec/tests/optional/badsome.fe diff --git a/fec/tests/m7/badtry.fe b/fec/tests/optional/badtry.fe similarity index 100% rename from fec/tests/m7/badtry.fe rename to fec/tests/optional/badtry.fe diff --git a/fec/tests/m7/badzero.fe b/fec/tests/optional/badzero.fe similarity index 100% rename from fec/tests/m7/badzero.fe rename to fec/tests/optional/badzero.fe diff --git a/fec/tests/m7/okcatch.fe b/fec/tests/optional/okcatch.fe similarity index 100% rename from fec/tests/m7/okcatch.fe rename to fec/tests/optional/okcatch.fe diff --git a/fec/tests/m7/okcatmov.fe b/fec/tests/optional/okcatmov.fe similarity index 100% rename from fec/tests/m7/okcatmov.fe rename to fec/tests/optional/okcatmov.fe diff --git a/fec/tests/m7/okcvoid.fe b/fec/tests/optional/okcvoid.fe similarity index 100% rename from fec/tests/m7/okcvoid.fe rename to fec/tests/optional/okcvoid.fe diff --git a/fec/tests/m7/okdeflt.fe b/fec/tests/optional/okdeflt.fe similarity index 100% rename from fec/tests/m7/okdeflt.fe rename to fec/tests/optional/okdeflt.fe diff --git a/fec/tests/m7/okiflet.fe b/fec/tests/optional/okiflet.fe similarity index 100% rename from fec/tests/m7/okiflet.fe rename to fec/tests/optional/okiflet.fe diff --git a/fec/tests/m7/okmatch.fe b/fec/tests/optional/okmatch.fe similarity index 100% rename from fec/tests/m7/okmatch.fe rename to fec/tests/optional/okmatch.fe diff --git a/fec/tests/m7/oknull.fe b/fec/tests/optional/oknull.fe similarity index 100% rename from fec/tests/m7/oknull.fe rename to fec/tests/optional/oknull.fe diff --git a/fec/tests/m7/okorelse.fe b/fec/tests/optional/okorelse.fe similarity index 100% rename from fec/tests/m7/okorelse.fe rename to fec/tests/optional/okorelse.fe diff --git a/fec/tests/m7/okpatvw.fe b/fec/tests/optional/okpatvw.fe similarity index 100% rename from fec/tests/m7/okpatvw.fe rename to fec/tests/optional/okpatvw.fe diff --git a/fec/tests/m7/okproj.fe b/fec/tests/optional/okproj.fe similarity index 100% rename from fec/tests/m7/okproj.fe rename to fec/tests/optional/okproj.fe diff --git a/fec/tests/m7/okrepl.fe b/fec/tests/optional/okrepl.fe similarity index 100% rename from fec/tests/m7/okrepl.fe rename to fec/tests/optional/okrepl.fe diff --git a/fec/tests/m7/oktrdef.fe b/fec/tests/optional/oktrdef.fe similarity index 100% rename from fec/tests/m7/oktrdef.fe rename to fec/tests/optional/oktrdef.fe diff --git a/fec/tests/m7/oktry.fe b/fec/tests/optional/oktry.fe similarity index 100% rename from fec/tests/m7/oktry.fe rename to fec/tests/optional/oktry.fe diff --git a/fec/tests/m6/README.md b/fec/tests/own/README.md similarity index 100% rename from fec/tests/m6/README.md rename to fec/tests/own/README.md diff --git a/fec/tests/m6/badarg.fe b/fec/tests/own/badarg.fe similarity index 100% rename from fec/tests/m6/badarg.fe rename to fec/tests/own/badarg.fe diff --git a/fec/tests/m6/badbinit.fe b/fec/tests/own/badbinit.fe similarity index 100% rename from fec/tests/m6/badbinit.fe rename to fec/tests/own/badbinit.fe diff --git a/fec/tests/m6/badbrmov.fe b/fec/tests/own/badbrmov.fe similarity index 100% rename from fec/tests/m6/badbrmov.fe rename to fec/tests/own/badbrmov.fe diff --git a/fec/tests/m6/baddefer.fe b/fec/tests/own/baddefer.fe similarity index 100% rename from fec/tests/m6/baddefer.fe rename to fec/tests/own/baddefer.fe diff --git a/fec/tests/m6/badfld.fe b/fec/tests/own/badfld.fe similarity index 100% rename from fec/tests/m6/badfld.fe rename to fec/tests/own/badfld.fe diff --git a/fec/tests/m6/badglob.fe b/fec/tests/own/badglob.fe similarity index 100% rename from fec/tests/m6/badglob.fe rename to fec/tests/own/badglob.fe diff --git a/fec/tests/m6/badgmut.fe b/fec/tests/own/badgmut.fe similarity index 100% rename from fec/tests/m6/badgmut.fe rename to fec/tests/own/badgmut.fe diff --git a/fec/tests/m6/badinv.fe b/fec/tests/own/badinv.fe similarity index 100% rename from fec/tests/m6/badinv.fe rename to fec/tests/own/badinv.fe diff --git a/fec/tests/m6/badlocsl.fe b/fec/tests/own/badlocsl.fe similarity index 100% rename from fec/tests/m6/badlocsl.fe rename to fec/tests/own/badlocsl.fe diff --git a/fec/tests/m6/badloop.fe b/fec/tests/own/badloop.fe similarity index 100% rename from fec/tests/m6/badloop.fe rename to fec/tests/own/badloop.fe diff --git a/fec/tests/m6/badmove.fe b/fec/tests/own/badmove.fe similarity index 100% rename from fec/tests/m6/badmove.fe rename to fec/tests/own/badmove.fe diff --git a/fec/tests/m6/badmut.fe b/fec/tests/own/badmut.fe similarity index 100% rename from fec/tests/m6/badmut.fe rename to fec/tests/own/badmut.fe diff --git a/fec/tests/m6/badmut2.fe b/fec/tests/own/badmut2.fe similarity index 100% rename from fec/tests/m6/badmut2.fe rename to fec/tests/own/badmut2.fe diff --git a/fec/tests/m6/badptr.fe b/fec/tests/own/badptr.fe similarity index 100% rename from fec/tests/m6/badptr.fe rename to fec/tests/own/badptr.fe diff --git a/fec/tests/m6/badret.fe b/fec/tests/own/badret.fe similarity index 100% rename from fec/tests/m6/badret.fe rename to fec/tests/own/badret.fe diff --git a/fec/tests/m6/badrfld.fe b/fec/tests/own/badrfld.fe similarity index 100% rename from fec/tests/m6/badrfld.fe rename to fec/tests/own/badrfld.fe diff --git a/fec/tests/m6/badridx.fe b/fec/tests/own/badridx.fe similarity index 100% rename from fec/tests/m6/badridx.fe rename to fec/tests/own/badridx.fe diff --git a/fec/tests/m6/badscop.fe b/fec/tests/own/badscop.fe similarity index 100% rename from fec/tests/m6/badscop.fe rename to fec/tests/own/badscop.fe diff --git a/fec/tests/m6/badself.fe b/fec/tests/own/badself.fe similarity index 100% rename from fec/tests/m6/badself.fe rename to fec/tests/own/badself.fe diff --git a/fec/tests/m6/badshwr.fe b/fec/tests/own/badshwr.fe similarity index 100% rename from fec/tests/m6/badshwr.fe rename to fec/tests/own/badshwr.fe diff --git a/fec/tests/m6/badslfld.fe b/fec/tests/own/badslfld.fe similarity index 100% rename from fec/tests/m6/badslfld.fe rename to fec/tests/own/badslfld.fe diff --git a/fec/tests/m6/badtwo.fe b/fec/tests/own/badtwo.fe similarity index 100% rename from fec/tests/m6/badtwo.fe rename to fec/tests/own/badtwo.fe diff --git a/fec/tests/m6/badup.fe b/fec/tests/own/badup.fe similarity index 100% rename from fec/tests/m6/badup.fe rename to fec/tests/own/badup.fe diff --git a/fec/tests/m6/badweak.fe b/fec/tests/own/badweak.fe similarity index 100% rename from fec/tests/m6/badweak.fe rename to fec/tests/own/badweak.fe diff --git a/fec/tests/m5/defer.fe b/fec/tests/own/ok-defer.fe similarity index 100% rename from fec/tests/m5/defer.fe rename to fec/tests/own/ok-defer.fe diff --git a/fec/tests/m5/owned.fe b/fec/tests/own/ok-owned.fe similarity index 100% rename from fec/tests/m5/owned.fe rename to fec/tests/own/ok-owned.fe diff --git a/fec/tests/m6/okbranch.fe b/fec/tests/own/okbranch.fe similarity index 100% rename from fec/tests/m6/okbranch.fe rename to fec/tests/own/okbranch.fe diff --git a/fec/tests/m6/okdefer.fe b/fec/tests/own/okdefer.fe similarity index 100% rename from fec/tests/m6/okdefer.fe rename to fec/tests/own/okdefer.fe diff --git a/fec/tests/m6/okglobcp.fe b/fec/tests/own/okglobcp.fe similarity index 100% rename from fec/tests/m6/okglobcp.fe rename to fec/tests/own/okglobcp.fe diff --git a/fec/tests/m6/oklast.fe b/fec/tests/own/oklast.fe similarity index 100% rename from fec/tests/m6/oklast.fe rename to fec/tests/own/oklast.fe diff --git a/fec/tests/m6/okr8free.fe b/fec/tests/own/okr8free.fe similarity index 100% rename from fec/tests/m6/okr8free.fe rename to fec/tests/own/okr8free.fe diff --git a/fec/tests/m6/okr8join.fe b/fec/tests/own/okr8join.fe similarity index 100% rename from fec/tests/m6/okr8join.fe rename to fec/tests/own/okr8join.fe diff --git a/fec/tests/m6/okr8meth.fe b/fec/tests/own/okr8meth.fe similarity index 100% rename from fec/tests/m6/okr8meth.fe rename to fec/tests/own/okr8meth.fe diff --git a/fec/tests/m6/okr8stat.fe b/fec/tests/own/okr8stat.fe similarity index 100% rename from fec/tests/m6/okr8stat.fe rename to fec/tests/own/okr8stat.fe diff --git a/fec/tests/m6/okrebor.fe b/fec/tests/own/okrebor.fe similarity index 100% rename from fec/tests/m6/okrebor.fe rename to fec/tests/own/okrebor.fe diff --git a/fec/tests/m6/okrtlast.fe b/fec/tests/own/okrtlast.fe similarity index 100% rename from fec/tests/m6/okrtlast.fe rename to fec/tests/own/okrtlast.fe diff --git a/fec/tests/m6/okshare.fe b/fec/tests/own/okshare.fe similarity index 100% rename from fec/tests/m6/okshare.fe rename to fec/tests/own/okshare.fe diff --git a/fec/tests/m6/okslreb.fe b/fec/tests/own/okslreb.fe similarity index 100% rename from fec/tests/m6/okslreb.fe rename to fec/tests/own/okslreb.fe diff --git a/fec/tests/m6/okstatic.fe b/fec/tests/own/okstatic.fe similarity index 100% rename from fec/tests/m6/okstatic.fe rename to fec/tests/own/okstatic.fe diff --git a/fec/tests/m6/oktemp.fe b/fec/tests/own/oktemp.fe similarity index 100% rename from fec/tests/m6/oktemp.fe rename to fec/tests/own/oktemp.fe diff --git a/fec/tests/m6/oktrim.fe b/fec/tests/own/oktrim.fe similarity index 100% rename from fec/tests/m6/oktrim.fe rename to fec/tests/own/oktrim.fe diff --git a/fec/tests/m6/okwcall.fe b/fec/tests/own/okwcall.fe similarity index 100% rename from fec/tests/m6/okwcall.fe rename to fec/tests/own/okwcall.fe diff --git a/fec/tests/m5/bad-clos.fe b/fec/tests/own/own-bad-clos.fe similarity index 100% rename from fec/tests/m5/bad-clos.fe rename to fec/tests/own/own-bad-clos.fe diff --git a/fec/tests/m5/bad-cond.fe b/fec/tests/own/own-bad-cond.fe similarity index 100% rename from fec/tests/m5/bad-cond.fe rename to fec/tests/own/own-bad-cond.fe diff --git a/fec/tests/m5/bad-dbl.fe b/fec/tests/own/own-bad-dbl.fe similarity index 100% rename from fec/tests/m5/bad-dbl.fe rename to fec/tests/own/own-bad-dbl.fe diff --git a/fec/tests/m5/bad-dest.fe b/fec/tests/own/own-bad-dest.fe similarity index 100% rename from fec/tests/m5/bad-dest.fe rename to fec/tests/own/own-bad-dest.fe diff --git a/fec/tests/m5/bad-drop.fe b/fec/tests/own/own-bad-drop.fe similarity index 100% rename from fec/tests/m5/bad-drop.fe rename to fec/tests/own/own-bad-drop.fe diff --git a/fec/tests/m5/bad-loop.fe b/fec/tests/own/own-bad-loop.fe similarity index 100% rename from fec/tests/m5/bad-loop.fe rename to fec/tests/own/own-bad-loop.fe diff --git a/fec/tests/m5/bad-move.fe b/fec/tests/own/own-bad-move.fe similarity index 100% rename from fec/tests/m5/bad-move.fe rename to fec/tests/own/own-bad-move.fe diff --git a/fec/tests/m5/bad-proj.fe b/fec/tests/own/own-bad-proj.fe similarity index 100% rename from fec/tests/m5/bad-proj.fe rename to fec/tests/own/own-bad-proj.fe diff --git a/fec/tests/pass/basic.fe b/fec/tests/parse/basic.fe similarity index 100% rename from fec/tests/pass/basic.fe rename to fec/tests/parse/basic.fe diff --git a/fec/tests/pass/keybuilt.fe b/fec/tests/parse/keybuilt.fe similarity index 100% rename from fec/tests/pass/keybuilt.fe rename to fec/tests/parse/keybuilt.fe diff --git a/fec/tests/pass/literals.fe b/fec/tests/parse/literals.fe similarity index 100% rename from fec/tests/pass/literals.fe rename to fec/tests/parse/literals.fe diff --git a/fec/tests/fail/logical.fe b/fec/tests/parse/logical.fe similarity index 100% rename from fec/tests/fail/logical.fe rename to fec/tests/parse/logical.fe diff --git a/fec/tests/fail/misssemi.fe b/fec/tests/parse/misssemi.fe similarity index 100% rename from fec/tests/fail/misssemi.fe rename to fec/tests/parse/misssemi.fe diff --git a/fec/tests/fail/unclcomm.fe b/fec/tests/parse/unclcomm.fe similarity index 100% rename from fec/tests/fail/unclcomm.fe rename to fec/tests/parse/unclcomm.fe diff --git a/fec/tests/pass/v012form.fe b/fec/tests/parse/v012form.fe similarity index 100% rename from fec/tests/pass/v012form.fe rename to fec/tests/parse/v012form.fe diff --git a/fec/tests/m3/nochk.fe b/fec/tests/pending-backend/bounds-nocheck.fe similarity index 100% rename from fec/tests/m3/nochk.fe rename to fec/tests/pending-backend/bounds-nocheck.fe diff --git a/fec/tests/m3/bounds.fe b/fec/tests/pending-backend/bounds-trap.fe similarity index 100% rename from fec/tests/m3/bounds.fe rename to fec/tests/pending-backend/bounds-trap.fe diff --git a/fec/tests/m4/proptest.c b/fec/tests/pending-backend/format-prop.c similarity index 100% rename from fec/tests/m4/proptest.c rename to fec/tests/pending-backend/format-prop.c diff --git a/fec/tests/m5/runtime.c b/fec/tests/pending-backend/ownership-drop.c similarity index 100% rename from fec/tests/m5/runtime.c rename to fec/tests/pending-backend/ownership-drop.c diff --git a/fec/tests/m5/runtime.fe b/fec/tests/pending-backend/ownership-drop.fe similarity index 100% rename from fec/tests/m5/runtime.fe rename to fec/tests/pending-backend/ownership-drop.fe diff --git a/fec/tests/m3/slcbound.fe b/fec/tests/pending-backend/slice-bounds-trap.fe similarity index 100% rename from fec/tests/m3/slcbound.fe rename to fec/tests/pending-backend/slice-bounds-trap.fe diff --git a/fec/tests/m2/bad-ari.fe b/fec/tests/types/bad-ari.fe similarity index 100% rename from fec/tests/m2/bad-ari.fe rename to fec/tests/types/bad-ari.fe diff --git a/fec/tests/m2/bad-asgn.fe b/fec/tests/types/bad-asgn.fe similarity index 100% rename from fec/tests/m2/bad-asgn.fe rename to fec/tests/types/bad-asgn.fe diff --git a/fec/tests/m2/bad-cast.fe b/fec/tests/types/bad-cast.fe similarity index 100% rename from fec/tests/m2/bad-cast.fe rename to fec/tests/types/bad-cast.fe diff --git a/fec/tests/m2/bad-cond.fe b/fec/tests/types/bad-cond.fe similarity index 100% rename from fec/tests/m2/bad-cond.fe rename to fec/tests/types/bad-cond.fe diff --git a/fec/tests/m3/bad-mlet.fe b/fec/tests/types/bad-mlet.fe similarity index 100% rename from fec/tests/m3/bad-mlet.fe rename to fec/tests/types/bad-mlet.fe diff --git a/fec/tests/m2/bad-ret.fe b/fec/tests/types/bad-ret.fe similarity index 100% rename from fec/tests/m2/bad-ret.fe rename to fec/tests/types/bad-ret.fe diff --git a/fec/tests/m3/bad-shwr.fe b/fec/tests/types/bad-shwr.fe similarity index 100% rename from fec/tests/m3/bad-shwr.fe rename to fec/tests/types/bad-shwr.fe diff --git a/fec/tests/m2/bad-type.fe b/fec/tests/types/bad-type.fe similarity index 100% rename from fec/tests/m2/bad-type.fe rename to fec/tests/types/bad-type.fe diff --git a/fec/tests/m2/bad-unit.fe b/fec/tests/types/bad-unit.fe similarity index 100% rename from fec/tests/m2/bad-unit.fe rename to fec/tests/types/bad-unit.fe diff --git a/fec/tests/m2/bad-unk.fe b/fec/tests/types/bad-unk.fe similarity index 100% rename from fec/tests/m2/bad-unk.fe rename to fec/tests/types/bad-unk.fe diff --git a/fec/tests/m2/bad-void.fe b/fec/tests/types/bad-void.fe similarity index 100% rename from fec/tests/m2/bad-void.fe rename to fec/tests/types/bad-void.fe diff --git a/fec/tests/m3/badarr.fe b/fec/tests/types/badarr.fe similarity index 100% rename from fec/tests/m3/badarr.fe rename to fec/tests/types/badarr.fe diff --git a/fec/tests/m3/badchar.fe b/fec/tests/types/badchar.fe similarity index 100% rename from fec/tests/m3/badchar.fe rename to fec/tests/types/badchar.fe diff --git a/fec/tests/m3/badcycle.fe b/fec/tests/types/badcycle.fe similarity index 100% rename from fec/tests/m3/badcycle.fe rename to fec/tests/types/badcycle.fe diff --git a/fec/tests/m3/badfield.fe b/fec/tests/types/badfield.fe similarity index 100% rename from fec/tests/m3/badfield.fe rename to fec/tests/types/badfield.fe diff --git a/fec/tests/m3/badfld.fe b/fec/tests/types/badfld.fe similarity index 100% rename from fec/tests/m3/badfld.fe rename to fec/tests/types/badfld.fe diff --git a/fec/tests/m3/badindex.fe b/fec/tests/types/badindex.fe similarity index 100% rename from fec/tests/m3/badindex.fe rename to fec/tests/types/badindex.fe diff --git a/fec/tests/m3/badmat.fe b/fec/tests/types/badmat.fe similarity index 100% rename from fec/tests/m3/badmat.fe rename to fec/tests/types/badmat.fe diff --git a/fec/tests/m3/badstr.fe b/fec/tests/types/badstr.fe similarity index 100% rename from fec/tests/m3/badstr.fe rename to fec/tests/types/badstr.fe diff --git a/fec/tests/m3/array.fe b/fec/tests/types/ok-array.fe similarity index 100% rename from fec/tests/m3/array.fe rename to fec/tests/types/ok-array.fe diff --git a/fec/tests/m3/arrayctx.fe b/fec/tests/types/ok-arrayctx.fe similarity index 100% rename from fec/tests/m3/arrayctx.fe rename to fec/tests/types/ok-arrayctx.fe diff --git a/fec/tests/m2/castwhil.fe b/fec/tests/types/ok-castwhil.fe similarity index 100% rename from fec/tests/m2/castwhil.fe rename to fec/tests/types/ok-castwhil.fe diff --git a/fec/tests/m3/char.fe b/fec/tests/types/ok-char.fe similarity index 100% rename from fec/tests/m3/char.fe rename to fec/tests/types/ok-char.fe diff --git a/fec/tests/m3/enum.fe b/fec/tests/types/ok-enum.fe similarity index 100% rename from fec/tests/m3/enum.fe rename to fec/tests/types/ok-enum.fe diff --git a/fec/tests/m3/for.fe b/fec/tests/types/ok-for.fe similarity index 100% rename from fec/tests/m3/for.fe rename to fec/tests/types/ok-for.fe diff --git a/fec/tests/m2/hello.fe b/fec/tests/types/ok-hello.fe similarity index 100% rename from fec/tests/m2/hello.fe rename to fec/tests/types/ok-hello.fe diff --git a/fec/tests/m3/mutable.fe b/fec/tests/types/ok-mutable.fe similarity index 100% rename from fec/tests/m3/mutable.fe rename to fec/tests/types/ok-mutable.fe diff --git a/fec/tests/m3/nested.fe b/fec/tests/types/ok-nested.fe similarity index 100% rename from fec/tests/m3/nested.fe rename to fec/tests/types/ok-nested.fe diff --git a/fec/tests/m2/scopes.fe b/fec/tests/types/ok-scopes.fe similarity index 100% rename from fec/tests/m2/scopes.fe rename to fec/tests/types/ok-scopes.fe diff --git a/fec/tests/m3/str.fe b/fec/tests/types/ok-str.fe similarity index 100% rename from fec/tests/m3/str.fe rename to fec/tests/types/ok-str.fe diff --git a/fec/tests/m3/struct.fe b/fec/tests/types/ok-struct.fe similarity index 100% rename from fec/tests/m3/struct.fe rename to fec/tests/types/ok-struct.fe diff --git a/fec/tests/m8/README.md b/fec/tests/units/README.md similarity index 100% rename from fec/tests/m8/README.md rename to fec/tests/units/README.md diff --git a/fec/tests/m8/alias/acme/math.fe b/fec/tests/units/alias/acme/math.fe similarity index 100% rename from fec/tests/m8/alias/acme/math.fe rename to fec/tests/units/alias/acme/math.fe diff --git a/fec/tests/m8/alias/main.fe b/fec/tests/units/alias/main.fe similarity index 100% rename from fec/tests/m8/alias/main.fe rename to fec/tests/units/alias/main.fe diff --git a/fec/tests/m8/badlong/main.fe b/fec/tests/units/badlong/main.fe similarity index 100% rename from fec/tests/m8/badlong/main.fe rename to fec/tests/units/badlong/main.fe diff --git a/fec/tests/m8/badupper/main.fe b/fec/tests/units/badupper/main.fe similarity index 100% rename from fec/tests/m8/badupper/main.fe rename to fec/tests/units/badupper/main.fe diff --git a/fec/tests/m8/basic/main.fe b/fec/tests/units/basic/main.fe similarity index 100% rename from fec/tests/m8/basic/main.fe rename to fec/tests/units/basic/main.fe diff --git a/fec/tests/m8/basic/util.fe b/fec/tests/units/basic/util.fe similarity index 100% rename from fec/tests/m8/basic/util.fe rename to fec/tests/units/basic/util.fe diff --git a/fec/tests/m8/bindconf/alpha/net.fe b/fec/tests/units/bindconf/alpha/net.fe similarity index 100% rename from fec/tests/m8/bindconf/alpha/net.fe rename to fec/tests/units/bindconf/alpha/net.fe diff --git a/fec/tests/m8/bindconf/beta/net.fe b/fec/tests/units/bindconf/beta/net.fe similarity index 100% rename from fec/tests/m8/bindconf/beta/net.fe rename to fec/tests/units/bindconf/beta/net.fe diff --git a/fec/tests/m8/bindconf/main.fe b/fec/tests/units/bindconf/main.fe similarity index 100% rename from fec/tests/m8/bindconf/main.fe rename to fec/tests/units/bindconf/main.fe diff --git a/fec/tests/m8/cycle/a.fe b/fec/tests/units/cycle/a.fe similarity index 100% rename from fec/tests/m8/cycle/a.fe rename to fec/tests/units/cycle/a.fe diff --git a/fec/tests/m8/cycle/b.fe b/fec/tests/units/cycle/b.fe similarity index 100% rename from fec/tests/m8/cycle/b.fe rename to fec/tests/units/cycle/b.fe diff --git a/fec/tests/m8/dotpriv/game/bar.fe b/fec/tests/units/dotpriv/game/bar.fe similarity index 100% rename from fec/tests/m8/dotpriv/game/bar.fe rename to fec/tests/units/dotpriv/game/bar.fe diff --git a/fec/tests/m8/dotpriv/game/foo.fe b/fec/tests/units/dotpriv/game/foo.fe similarity index 100% rename from fec/tests/m8/dotpriv/game/foo.fe rename to fec/tests/units/dotpriv/game/foo.fe diff --git a/fec/tests/m8/dotpriv/main.fe b/fec/tests/units/dotpriv/main.fe similarity index 100% rename from fec/tests/m8/dotpriv/main.fe rename to fec/tests/units/dotpriv/main.fe diff --git a/fec/tests/m8/dotted/acme/math.fe b/fec/tests/units/dotted/acme/math.fe similarity index 100% rename from fec/tests/m8/dotted/acme/math.fe rename to fec/tests/units/dotted/acme/math.fe diff --git a/fec/tests/m8/dotted/main.fe b/fec/tests/units/dotted/main.fe similarity index 100% rename from fec/tests/m8/dotted/main.fe rename to fec/tests/units/dotted/main.fe diff --git a/fec/tests/m8/errdet/alpha.fe b/fec/tests/units/errdet/alpha.fe similarity index 100% rename from fec/tests/m8/errdet/alpha.fe rename to fec/tests/units/errdet/alpha.fe diff --git a/fec/tests/m8/errdet/beta.fe b/fec/tests/units/errdet/beta.fe similarity index 100% rename from fec/tests/m8/errdet/beta.fe rename to fec/tests/units/errdet/beta.fe diff --git a/fec/tests/m8/errdet/main.fe b/fec/tests/units/errdet/main.fe similarity index 100% rename from fec/tests/m8/errdet/main.fe rename to fec/tests/units/errdet/main.fe diff --git a/fec/tests/m8/errnom/lib.fe b/fec/tests/units/errnom/lib.fe similarity index 100% rename from fec/tests/m8/errnom/lib.fe rename to fec/tests/units/errnom/lib.fe diff --git a/fec/tests/m8/errnom/main.fe b/fec/tests/units/errnom/main.fe similarity index 100% rename from fec/tests/m8/errnom/main.fe rename to fec/tests/units/errnom/main.fe diff --git a/fec/tests/m8/errsame/alpha.fe b/fec/tests/units/errsame/alpha.fe similarity index 100% rename from fec/tests/m8/errsame/alpha.fe rename to fec/tests/units/errsame/alpha.fe diff --git a/fec/tests/m8/errsame/beta.fe b/fec/tests/units/errsame/beta.fe similarity index 100% rename from fec/tests/m8/errsame/beta.fe rename to fec/tests/units/errsame/beta.fe diff --git a/fec/tests/m8/errsame/main.fe b/fec/tests/units/errsame/main.fe similarity index 100% rename from fec/tests/m8/errsame/main.fe rename to fec/tests/units/errsame/main.fe diff --git a/fec/tests/m8/missing/main.fe b/fec/tests/units/missing/main.fe similarity index 100% rename from fec/tests/m8/missing/main.fe rename to fec/tests/units/missing/main.fe diff --git a/fec/tests/m8/privfld/data.fe b/fec/tests/units/privfld/data.fe similarity index 100% rename from fec/tests/m8/privfld/data.fe rename to fec/tests/units/privfld/data.fe diff --git a/fec/tests/m8/privfld/main.fe b/fec/tests/units/privfld/main.fe similarity index 100% rename from fec/tests/m8/privfld/main.fe rename to fec/tests/units/privfld/main.fe diff --git a/fec/tests/m8/privfn/main.fe b/fec/tests/units/privfn/main.fe similarity index 100% rename from fec/tests/m8/privfn/main.fe rename to fec/tests/units/privfn/main.fe diff --git a/fec/tests/m8/privfn/util.fe b/fec/tests/units/privfn/util.fe similarity index 100% rename from fec/tests/m8/privfn/util.fe rename to fec/tests/units/privfn/util.fe diff --git a/fec/tests/m8/pubfld/data.fe b/fec/tests/units/pubfld/data.fe similarity index 100% rename from fec/tests/m8/pubfld/data.fe rename to fec/tests/units/pubfld/data.fe diff --git a/fec/tests/m8/pubfld/main.fe b/fec/tests/units/pubfld/main.fe similarity index 100% rename from fec/tests/m8/pubfld/main.fe rename to fec/tests/units/pubfld/main.fe diff --git a/fec/tests/m8/pubpriv/lib.fe b/fec/tests/units/pubpriv/lib.fe similarity index 100% rename from fec/tests/m8/pubpriv/lib.fe rename to fec/tests/units/pubpriv/lib.fe diff --git a/fec/tests/m8/pubpriv/main.fe b/fec/tests/units/pubpriv/main.fe similarity index 100% rename from fec/tests/m8/pubpriv/main.fe rename to fec/tests/units/pubpriv/main.fe diff --git a/fec/tests/m8/unitbad/main.fe b/fec/tests/units/unitbad/main.fe similarity index 100% rename from fec/tests/m8/unitbad/main.fe rename to fec/tests/units/unitbad/main.fe diff --git a/src/ferrolang_vm/__init__.py b/src/ferrolang_vm/__init__.py deleted file mode 100644 index a63f890..0000000 --- a/src/ferrolang_vm/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Reproducible DOS development tools for the Ferro compiler.""" diff --git a/src/ferrolang_vm/dos_cli.py b/src/ferrolang_vm/dos_cli.py deleted file mode 100644 index 02a0567..0000000 --- a/src/ferrolang_vm/dos_cli.py +++ /dev/null @@ -1,80 +0,0 @@ -"""General-purpose disposable DOSBox-X/Open Watcom environment CLI.""" -from __future__ import annotations - -import argparse -import subprocess -import sys -from pathlib import Path - -from .dosboxx import DosboxError, run_suite, setup -from .paths import ROOT -from .suite import Case - - -def _run(command: str | None, *, keep: bool, show_dos: bool) -> int: - cases = [] if command is None else [Case("command", 0, command, True)] - run = run_suite(cases, keep=keep, show_dos=show_dos, trace_dos=show_dos) - try: - if run.result() != "PASS": - print(run.log(), file=sys.stderr) - return 1 - if cases and run.result(cases[0]) != "PASS": - print(run.log(cases[0]), file=sys.stderr) - return 1 - if cases: - output = run.log(cases[0]) - if output: - print(output, end="" if output.endswith("\n") else "\n") - if keep: - print(f"DOS workspace: {run.root}") - return 0 - finally: - run.cleanup() - - -def main() -> int: - parser = argparse.ArgumentParser( - prog="ferro-dos", - description="Disposable directory-backed DOSBox-X/Open Watcom environment.", - ) - commands = parser.add_subparsers(dest="action", required=True) - prepare = commands.add_parser("setup", help="install the pinned DOSBox-X and Open Watcom tools") - prepare.add_argument("--accept-watcom-license", action="store_true") - for name, help_text in ( - ("build", "build the current FEC source inside DOS"), - ("exec", "build FEC and execute one DOS command"), - ("batch", "build FEC and call a repository DOS batch"), - ("shell", "build FEC and open an interactive DOS shell"), - ): - command = commands.add_parser(name, help=help_text) - command.add_argument("--keep", action="store_true", help="preserve the temporary DOS workspace") - command.add_argument("--show-dos", action="store_true", help="show and pause the DOS window") - if name == "exec": - command.add_argument("dos_command") - elif name == "batch": - command.add_argument("path", type=Path) - args = parser.parse_args() - try: - if args.action == "setup": - dosbox, watcom = setup(accept_watcom_license=args.accept_watcom_license) - print(f"DOSBox-X: {dosbox}") - print(f"Open Watcom: {watcom}") - return 0 - if args.action == "build": - return _run(None, keep=args.keep, show_dos=args.show_dos) - if args.action == "exec": - return _run(args.dos_command, keep=args.keep, show_dos=args.show_dos) - if args.action == "shell": - return _run("COMMAND.COM", keep=args.keep, show_dos=True) - path = (ROOT / args.path).resolve() - if (path != ROOT and ROOT not in path.parents) or not path.is_file(): - raise DosboxError("batch path must be an existing file inside the repository") - relative = path.relative_to(ROOT).as_posix().replace("/", "\\").upper() - return _run(f"CALL R:\\{relative}", keep=args.keep, show_dos=args.show_dos) - except (DosboxError, subprocess.SubprocessError) as exc: - print(f"ferro-dos: {exc}", file=sys.stderr) - return 2 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/src/ferrolang_vm/dosboxx.py b/src/ferrolang_vm/dosboxx.py deleted file mode 100644 index 0dcc2b3..0000000 --- a/src/ferrolang_vm/dosboxx.py +++ /dev/null @@ -1,332 +0,0 @@ -"""Reproducible DOSBox-X/Open Watcom development test backend.""" -from __future__ import annotations - -import hashlib -import json -import os -import shutil -import subprocess -import tempfile -import urllib.request -import zipfile -from dataclasses import dataclass -from pathlib import Path - -from .paths import ROOT -from .suite import Case - - -CACHE = ROOT / ".dosboxx" -LOCK_PATH = ROOT / "tools" / "toolchains" / "dosboxx.lock.json" -RUNS = CACHE / "runs" - - -class DosboxError(RuntimeError): - pass - - -def _lock() -> dict[str, object]: - return json.loads(LOCK_PATH.read_text(encoding="utf-8")) - - -def _sha256(path: Path) -> str: - digest = hashlib.sha256() - with path.open("rb") as source: - while chunk := source.read(1024 * 1024): - digest.update(chunk) - return digest.hexdigest() - - -def _download(name: str, spec: dict[str, object]) -> Path: - downloads = CACHE / "downloads" - downloads.mkdir(parents=True, exist_ok=True) - target = downloads / Path(str(spec["url"])).name - expected = str(spec["sha256"]).lower() - if target.is_file() and _sha256(target) == expected: - return target - target.unlink(missing_ok=True) - partial = target.with_suffix(target.suffix + ".part") - partial.unlink(missing_ok=True) - print(f"ferro-test: downloading {name} {spec['version']}...") - try: - with urllib.request.urlopen(str(spec["url"])) as response, partial.open("wb") as output: - shutil.copyfileobj(response, output, length=1024 * 1024) - except Exception: - partial.unlink(missing_ok=True) - raise - actual = _sha256(partial) - if actual != expected: - partial.unlink(missing_ok=True) - raise DosboxError(f"{name} SHA-256 mismatch: expected {expected}, got {actual}") - partial.replace(target) - return target - - -def _safe_extract(archive: Path, destination: Path) -> None: - destination.parent.mkdir(parents=True, exist_ok=True) - temporary = Path(tempfile.mkdtemp(prefix=f".{destination.name}-", dir=destination.parent)) - try: - with zipfile.ZipFile(archive) as bundle: - root = temporary.resolve() - for member in bundle.infolist(): - target = (temporary / member.filename).resolve() - if target != root and root not in target.parents: - raise DosboxError(f"unsafe archive member: {member.filename}") - bundle.extractall(temporary) - if destination.exists(): - shutil.rmtree(destination) - temporary.replace(destination) - except Exception: - shutil.rmtree(temporary, ignore_errors=True) - raise - - -def _required_paths(lock: dict[str, object]) -> tuple[Path, Path, list[Path]]: - dosbox_spec = lock["dosboxx"] - watcom_spec = lock["open_watcom"] - assert isinstance(dosbox_spec, dict) and isinstance(watcom_spec, dict) - dosbox = CACHE / "dosbox-x" / str(dosbox_spec["executable"]) - watcom = CACHE / "watcom" - required = [watcom / str(item) for item in watcom_spec["required"]] - return dosbox, watcom, required - - -def setup(*, accept_watcom_license: bool = False) -> tuple[Path, Path]: - if os.name != "nt": - raise DosboxError("the DOSBox-X development backend currently supports Windows only") - lock = _lock() - dosbox, watcom, watcom_required = _required_paths(lock) - tools_ready = dosbox.is_file() and all(path.is_file() for path in watcom_required) - if not tools_ready and not accept_watcom_license: - raise DosboxError( - "Open Watcom is distributed under the Sybase Open Watcom Public License. " - "Review tools/toolchains/dosboxx.lock.json and rerun " - "`uv run ferro-dos setup --accept-watcom-license`." - ) - if not tools_ready: - dosbox_spec = lock["dosboxx"] - watcom_spec = lock["open_watcom"] - assert isinstance(dosbox_spec, dict) and isinstance(watcom_spec, dict) - _safe_extract(_download("DOSBox-X", dosbox_spec), CACHE / "dosbox-x") - _safe_extract(_download("Open Watcom", watcom_spec), watcom) - dosbox, watcom, watcom_required = _required_paths(lock) - missing = [str(path.relative_to(CACHE)) for path in [dosbox, *watcom_required] - if not path.is_file()] - if missing: - raise DosboxError("toolchain archive is missing: " + ", ".join(missing)) - (CACHE / "SETUP.OK").write_text(_sha256(LOCK_PATH) + "\n", encoding="ascii") - return dosbox, watcom - - -def resolve_tools() -> tuple[Path, Path]: - lock = _lock() - dosbox, watcom, required = _required_paths(lock) - if not dosbox.is_file() or not all(path.is_file() for path in required): - raise DosboxError("toolchain is not installed; run `uv run ferro-dos setup`") - return dosbox, watcom - - -# ``if errorlevel N`` in DOS tests ``>= N``, so an exact code needs a descending -# ladder. Small values get their own rung because they carry the meaning -- 1 is -# an ordinary compiler error, 3 is Watcom's abort() -- while anything above 8 is -# bucketed to a lower bound, which is enough to tell a crash from a diagnostic. -_RC_LADDER = (255, 128, 64, 32, 16, 8, 7, 6, 5, 4, 3, 2, 1) - - -def _rc_batch() -> str: - """Batch helper that records the previous command's exit code. - - Called as ``call RC.BAT `` right after a case command, since anything - else -- including writing a file -- would clobber ERRORLEVEL first. Note the - space before each ``>``: ``echo 0>FILE`` would parse as a redirect of handle - 0 rather than an echo of "0", so the value is written with a trailing space - and stripped on the host. - """ - lines = ["@echo off"] - lines.extend(f"if errorlevel {value} goto R{value}" for value in _RC_LADDER) - lines.extend(["echo 0 >RESULTS\\%1.RC", "goto END"]) - for value in _RC_LADDER: - lines.extend([f":R{value}", f"echo {value} >RESULTS\\%1.RC", "goto END"]) - lines.extend([":END", ""]) - return "\r\n".join(lines) - - -def _batch(cases: list[Case], *, show_dos: bool, trace_dos: bool, - prebuilt: bool = False) -> str: - # The compiler build is the step that fails first and blocks everything after - # it, so its output is captured exactly like a case command's. - build = "call BUILD.BAT" if trace_dos else "call BUILD.BAT > RESULTS\\BUILD.LOG" - if prebuilt: - # FEC.EXE was restored from cache; BUILD.BAT would delete and rebuild it. - # It also puts the Watcom binaries on PATH, which the case commands need - # after it, so that line has to be reproduced rather than skipped. - build = ("set PATH=%WATCOM%\\BINW;%WATCOM%\\BINP;%PATH%\r\n" - "echo OK>BUILD.OK") - lines = [ - "@echo off", "if not exist RESULTS md RESULTS", "if not exist OUT md OUT", - "set WATCOM=W:", "set INCLUDE=W:\\H", - "set LIB=W:\\LIB286\\DOS;W:\\LIB286;W:\\LIB386\\DOS;W:\\LIB386", - # COMMAND.COM can only redirect handle 1, so fec diagnostics written to - # stderr never reach RESULTS\.LOG. Ask it for stdout instead. - "set FE_DIAG_STDOUT=1", - build, "if not exist BUILD.OK goto BUILDFAIL", - "echo PASS>RESULTS\\BUILD.RES", - ] - for index, case in enumerate(cases): - key = f"C{index:03d}" - command = case.command - # Watcom writes diagnostics into the current directory. Isolate each - # case so a later pytest item never sees stale diagnostics. - lines.extend([ - "if exist *.ERR del *.ERR > NUL", - f"if exist RESULTS\\{key}.ERR del RESULTS\\{key}.ERR > NUL", - ]) - if not trace_dos: - command += f" > RESULTS\\{key}.LOG" - lines.extend([ - command, - f"call RC.BAT {key}", - f"if exist *.ERR type *.ERR > RESULTS\\{key}.ERR", - f"if not exist RESULTS\\{key}.ERR type NUL > RESULTS\\{key}.ERR", - ]) - lines.extend([ - "goto FINISH", ":BUILDFAIL", "echo FAIL>RESULTS\\BUILD.RES", ":FINISH", - "echo DONE>RUN.OK", - *(["pause"] if show_dos else []), "exit", "", - ]) - return "\r\n".join(lines) - - -@dataclass -class SuiteRun: - root: Path - cases: list[Case] - keep: bool = False - - @property - def fec(self) -> Path: - return self.root / "FEC" - - def _key(self, case: Case) -> str: - return f"C{self.cases.index(case):03d}" - - def rc(self, case: Case) -> int | None: - """Exit code the DOS command reported, or None if it was never recorded. - - Values above 8 are a lower bound; see ``_RC_LADDER``. - """ - path = self.fec / "RESULTS" / f"{self._key(case)}.RC" - if not path.is_file(): - return None - text = path.read_text(encoding="ascii", errors="replace").strip() - return int(text) if text.isdigit() else None - - def result(self, case: Case | None = None) -> str: - if case is None: - path = self.fec / "RESULTS" / "BUILD.RES" - return path.read_text(encoding="ascii").strip() if path.is_file() else "MISSING" - code = self.rc(case) - if code is None: - return "MISSING" - return "PASS" if (code == 0) == case.expect_success else "FAIL" - - def log(self, case: Case | None = None) -> str: - name = "BUILD" if case is None else self._key(case) - path = self.fec / "RESULTS" / f"{name}.LOG" - content = path.read_text(encoding="utf-8", errors="replace") if path.is_file() else "" - if content.strip(): - return content - errors = sorted(self.fec.glob("*.ERR")) - joined = "\n".join(p.read_text(encoding="utf-8", errors="replace") for p in errors) - if joined.strip(): - return joined - # Deliberately not falling back to CONSOLE.LOG: that is the emulator's own - # log (display enumeration, INT15 chatter) and burying one useful line in - # it reads as output when there was none. Use --dos-log to see it. - return "(no DOS output captured; the command wrote nothing before exiting)" - - def err(self, case: Case) -> str: - path = self.fec / "RESULTS" / f"{self._key(case)}.ERR" - return path.read_text(encoding="utf-8", errors="replace") if path.is_file() else "" - - def cleanup(self) -> None: - if not self.keep: - shutil.rmtree(self.root, ignore_errors=True) - - -def _compiler_key() -> str: - """Hash of everything the compiler build reads. - - Sources and the build batch only; the toolchain itself is pinned by - dosboxx.lock.json, so it cannot drift underneath a cache hit. - """ - digest = hashlib.sha256() - paths = sorted((ROOT / "fec" / "src").rglob("*")) - paths.append(ROOT / "fec" / "build-dos.bat") - for path in paths: - if not path.is_file(): - continue - digest.update(path.name.encode("utf-8")) - digest.update(path.read_bytes()) - return digest.hexdigest()[:16] - - -def run_suite(cases: list[Case], *, keep: bool = False, show_dos: bool = False, - trace_dos: bool = False) -> SuiteRun: - dosbox, watcom = resolve_tools() - RUNS.mkdir(parents=True, exist_ok=True) - run_root = Path(tempfile.mkdtemp(prefix="suite-", dir=RUNS)) - result = SuiteRun(run_root, cases, keep) - fec = result.fec - cached = CACHE / "compilers" / f"{_compiler_key()}.exe" - try: - shutil.copytree(ROOT / "fec" / "src", fec / "SRC") - shutil.copytree(ROOT / "fec" / "std", fec / "STD") - shutil.copytree(ROOT / "fec" / "tests", fec / "TESTS") - shutil.copy2(ROOT / "fec" / "build-dos.bat", fec / "BUILD.BAT") - console = run_root / "CONSOLE.LOG" - config = run_root / "DOSBOX.CON" - config.write_text( - # core=auto leaves real mode on the interpreter, which is where the - # 16-bit compiler build spends its time. Nothing here is timing - # sensitive -- a compiler and a batch file -- so ask for the - # recompiler explicitly. The M3 bounds cases confirm abort() still - # reports its exit status under it. - f"[log]\nlogfile={console}\n" - f"[dosbox]\nlog console=quiet\n" - f"[cpu]\ncore=dynamic\ncycles=max\n", - encoding="ascii", - ) - if cached.is_file(): - shutil.copy2(cached, fec / "FEC.EXE") - (fec / "RUN.BAT").write_text( - _batch(cases, show_dos=show_dos, trace_dos=trace_dos, - prebuilt=cached.is_file()), - encoding="ascii", newline="", - ) - (fec / "RC.BAT").write_text(_rc_batch(), encoding="ascii", newline="") - command = [str(dosbox)] - if not show_dos: - command.append("-silent") - command.extend([ - "-fastlaunch", "-conf", str(config), - "-c", f'mount C "{run_root}"', "-c", f'mount R "{ROOT}" -ro', - "-c", f'mount W "{watcom}" -ro', - "-c", "C:", "-c", "cd \\FEC", "-c", "RUN.BAT", - ]) - # Every case pays a DOS process spawn, and the compile-only checks spawn - # wcc386 once each, so the whole-suite run is minutes rather than the - # under-a-minute a single milestone takes. - completed = subprocess.run(command, check=False, timeout=1800) - if completed.returncode != 0: - raise DosboxError(f"DOSBox-X exited with status {completed.returncode}") - if not (fec / "RUN.OK").is_file(): - raise DosboxError("DOSBox-X did not complete the test batch") - built = fec / "FEC.EXE" - if not cached.is_file() and built.is_file() and result.result() == "PASS": - cached.parent.mkdir(parents=True, exist_ok=True) - shutil.copy2(built, cached) - return result - except Exception: - result.keep = True - raise diff --git a/src/ferrolang_vm/paths.py b/src/ferrolang_vm/paths.py deleted file mode 100644 index 87ffbbd..0000000 --- a/src/ferrolang_vm/paths.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Repository and local cache paths shared by Ferro developer tools.""" -from pathlib import Path - - -ROOT = Path(__file__).resolve().parents[2] diff --git a/src/ferrolang_vm/registry.py b/src/ferrolang_vm/registry.py deleted file mode 100644 index 5a47497..0000000 --- a/src/ferrolang_vm/registry.py +++ /dev/null @@ -1,260 +0,0 @@ -"""Milestone case registry: DOS commands and their expected exit status. - -Only commands live here; the ``.fe`` fixtures stay under ``fec/tests`` and are -copied into the disposable DOS filesystem by the runner. Case order is load -bearing -- ``emit`` must precede ``build`` must precede ``run`` for the same -fixture, because each step consumes the previous step's output. -""" -from __future__ import annotations - -from .suite import Case - -PASS = "TESTS\\PASS" -FAIL = "TESTS\\FAIL" -STD = "STD" -M2 = "TESTS\\M2" -M3 = "TESTS\\M3" -M4 = "TESTS\\M4" -M5 = "TESTS\\M5" -M6 = "TESTS\\M6" -M7 = "TESTS\\M7" -OUT = "OUT" - -# Emitted-C basenames that were hand-shortened for DOS 8.3. Keyed by milestone -# because the same fixture name maps to different outputs across milestones -# (``bad-type`` is BAD-TY in M2 but BAD-TYP in M4). The shortenings are not -# consistent -- M2 cut to six characters, M4 to seven, and several were never -# required at all since BAD-COND is already a legal 8.3 name. Preserved verbatim; -# changing one renames a file inside the DOS run, so re-verify if you touch it. -_OUT83 = { - (2, "bad-cond"): "BAD-CO", - (2, "bad-cast"): "BAD-CA", - (2, "bad-asgn"): "BAD-AS", - (2, "bad-unk"): "BAD-UN", - (2, "bad-ari"): "BAD-AR", - (2, "bad-type"): "BAD-TY", - (2, "bad-ret"): "BAD-RE", - (2, "bad-unit"): "BAD-UI", - (2, "bad-void"): "BAD-VO", - (4, "bad-type"): "BAD-TYP", - (4, "bad-writ"): "BAD-WRI", - (5, "bad-dest"): "BAD-DES", -} - - -def _case(milestone: int, name: str, command: str, ok: bool = True) -> Case: - return Case(f"m{milestone}-{name}", milestone, command, ok) - - -def _fe(directory: str, name: str) -> str: - return f"{directory}\\{name.upper()}.FE" - - -def _emit(source: str, output: str, *, target: str = "bits32", - flags: tuple[str, ...] = (), output_first: bool = False) -> str: - """``fec`` invocation that translates ``source`` to C at ``output``. - - ``output_first`` reproduces the M6 cases, which pass ``-o`` before the input - file while every other milestone passes it after. - """ - parts = ["FEC.EXE", f"--target={target}", *flags, "--emit-c"] - parts += ["-o", output, source] if output_first else [source, "-o", output] - return " ".join(parts) - - -def _wcl(exe: str, *sources: str, bits: int = 32, strict: bool = False, - defines: tuple[str, ...] = ()) -> str: - """Open Watcom invocation. ``strict`` is the M4 ``-wx -wcd=202`` pairing: - warnings are errors except W202, which the generated C trips on unused - helpers (see AGENTS.md).""" - parts = ["WCL386" if bits == 32 else "WCL", "-q", "-za"] - if strict: - parts += ["-wx", "-wcd=202"] - parts += ["-bt=dos", *defines, f"-fe={exe}", *sources] - return " ".join(parts) - - -def _wcc(source: str, obj: str) -> str: - """Compile the generated C without linking. - - A fixture with no ``main`` cannot be run, so this is the floor for it: the - backend's output has to survive the compiler the project actually ships - with. It catches a malformed emission -- an unnamed assignment target, a - helper that is called but never defined, an initializer C89 rejects -- which - otherwise sits unnoticed in a case that only ever emitted text. - - This asserts nothing about the C itself; it is a conformance check on the - backend's output, so a future non-C backend swaps the command rather than - the intent. Never grep the generated C to prove a language feature -- write a - fixture whose exit code differs instead, as TESTS\\M3\\NOCHK.FE does. - """ - # Through the wcl386 driver with -c rather than calling wcc386 directly: - # wcc386 writes its diagnostics to stderr, which COMMAND.COM cannot - # redirect, so a failure would report a count and no messages. The driver - # leaves an .ERR file, which the runner already collects. - return f"WCL386 -q -za -wx -wcd=202 -bt=dos -c -fo={obj} {source}" - - -def _accepts(milestone: int, directory: str, names: tuple[str, ...], *, - output: str = OUT, output_first: bool = True) -> list[Case]: - """Fixtures that must compile: emit the C, then build it.""" - cases: list[Case] = [] - for name in names: - cfile = f"{output}\\{name.upper()}.C" - cases.append(_case(milestone, name, - _emit(_fe(directory, name), cfile, - output_first=output_first))) - cases.append(_case(milestone, f"{name}-cc", - _wcc(cfile, f"{OUT}\\{name.upper()}.OBJ"))) - return cases - - -def _dump_ast(milestone: int, directory: str, names: tuple[str, ...], *, - suffix: str, ok: bool = True, prefix: str = "") -> list[Case]: - return [ - _case(milestone, f"{prefix}{name}-{suffix}", - f"FEC.EXE --dump-ast {_fe(directory, name)}", ok) - for name in names - ] - - -def _rejects(milestone: int, directory: str, names: tuple[str, ...], *, - suffix: str = "") -> list[Case]: - """Fixtures that must fail to compile. The emitted-C path is still spelled - out because ``fec`` needs an ``-o`` even when it is expected to bail.""" - return [ - _case(milestone, f"{name}-{suffix}" if suffix else name, - _emit(_fe(directory, name), - f"{directory}\\{_OUT83.get((milestone, name), name.upper())}.C"), - False) - for name in names - ] - - -def _triple(milestone: int, name: str, directory: str, *, stem: str | None = None, - target: str = "bits32", bits: int = 32, strict: bool = False, - build_source: str | None = None, emit_suffix: str | None = "emit", - run_suffix: str = "run", run_ok: bool = True) -> list[Case]: - """emit -> build -> run for one fixture. - - ``stem`` renames the C/EXE pair when the fixture name does not fit 8.3 or - collides (M2 castwhil emits CAST16). ``build_source`` compiles a different - file than the one emitted (M4 prop emits PROP.C but builds PROPTEST.C, which - ``#include``s it). - """ - stem = stem or name.upper() - cfile = f"{directory}\\{stem}.C" - exe = f"{directory}\\{stem}.EXE" - emit_id = f"{name}-{emit_suffix}" if emit_suffix else name - return [ - _case(milestone, emit_id, _emit(_fe(directory, name), cfile, target=target)), - _case(milestone, f"{name}-build", - _wcl(exe, build_source or cfile, bits=bits, strict=strict)), - _case(milestone, f"{name}-{run_suffix}", exe, run_ok), - ] - - -CASES: list[Case] = [ - # -- M1: parse only ------------------------------------------------------- - *_dump_ast(1, PASS, ("basic", "literals", "keybuilt", "v012form"), suffix="parse"), - *_dump_ast(1, STD, ("core", "fmt", "io", "list", "map", "mem", "str", "sys"), - suffix="parse", prefix="std-"), - *_dump_ast(1, FAIL, ("misssemi", "unclcomm", "logical"), suffix="reject", ok=False), - - # -- M2: first generated C ------------------------------------------------ - *_triple(2, "hello", M2), - *_triple(2, "scopes", M2), - *_triple(2, "castwhil", M2, stem="CAST16", target="bits16", bits=16), - *_rejects(2, M2, ("bad-cond", "bad-cast", "bad-asgn", "bad-unk", "bad-ari", - "bad-type", "bad-ret", "bad-unit", "bad-void"), suffix="reject"), - - # -- M3: aggregates, strings, bounds checks ------------------------------- - *_triple(3, "struct", M3), - *_triple(3, "enum", M3), - *_triple(3, "array", M3), - *_triple(3, "mutable", M3), - *_rejects(3, M3, ("bad-mlet", "bad-shwr"), suffix="reject"), - *_triple(3, "str", M3), - *_triple(3, "for", M3), - *_triple(3, "nested", M3), - *_triple(3, "char", M3), - *_triple(3, "arrayctx", M3), - # These two must trap at runtime: the bounds check is the feature under test. - *_triple(3, "bounds", M3, run_suffix="trap", run_ok=False), - *_triple(3, "slcbound", M3, run_suffix="trap", run_ok=False), - # --no-checks is proved by a differential on one source. NOCHK.FE reads one - # element past a [2]i32 and returns x - x, which is 0 whatever garbage the - # unchecked read produced: compiled with checks it must trap, compiled with - # --no-checks it must run to completion. BOUNDS.FE cannot serve as the - # unchecked half because it returns the out-of-bounds value directly, so its - # exit code would be whatever happens to sit past the array on the stack. - *_triple(3, "nochk", M3, run_suffix="trap", run_ok=False), - _case(3, "nochk-off-emit", - _emit(_fe(M3, "nochk"), f"{M3}\\NOCHK-N.C", flags=("--no-checks",))), - _case(3, "nochk-off-build", - _wcl(f"{M3}\\NOCHK-N.EXE", f"{M3}\\NOCHK-N.C")), - _case(3, "nochk-off-run", f"{M3}\\NOCHK-N.EXE"), - *_rejects(3, M3, ("badfld", "badmat", "badarr", "badcycle", "badstr", "badchar", - "badfield", "badindex"), suffix="reject"), - - # -- M4: formatting and error propagation --------------------------------- - *_triple(4, "format", M4, strict=True, emit_suffix=None), - *_triple(4, "try-fpr", M4, strict=True, emit_suffix=None), - *_triple(4, "prop", M4, strict=True, emit_suffix=None, - build_source=f"{M4}\\PROPTEST.C"), - *_rejects(4, M4, ("bad-ari", "bad-verb", "bad-run", "bad-type", "bad-try", - "bad-writ", "bad-bufw", "bad-many", "bad-open", "bad-cls")), - - # -- M5: defer and ownership ---------------------------------------------- - *_accepts(5, M5, ("defer", "owned"), output=M5, output_first=False), - *_rejects(5, M5, ("bad-move", "bad-dest", "bad-drop", "bad-dbl", "bad-cond", - "bad-proj", "bad-clos", "bad-loop")), - # The runtime case links the generated C against a hand-written allocator - # shim, so malloc/free are redirected at compile time. - _case(5, "runtime", _emit(_fe(M5, "runtime"), f"{M5}\\RUNT-G.C")), - _case(5, "runtime-build", - _wcl(f"{M5}\\RUNTIME.EXE", f"{M5}\\RUNT-G.C", f"{M5}\\RUNTIME.C", - defines=("-dmalloc=m5_malloc", "-dfree=m5_free"))), - _case(5, "runtime-run", f"{M5}\\RUNTIME.EXE"), - - # -- M6: borrow checking (R1--R8) ----------------------------------------- - *[_case(6, name, f"FEC.EXE --check {_fe(M6, name)}", False) for name in ( - "badarg", "badbinit", "badbrmov", "baddefer", "badfld", "badglob", "badgmut", - "badinv", "badlocsl", "badloop", "badmove", "badmut", "badmut2", "badptr", - "badret", "badrfld", "badridx", "badscop", "badself", "badshwr", "badslfld", - "badtwo", "badup", "badweak")], - *_accepts(6, M6, ( - "okbranch", "okdefer", "okglobcp", "oklast", "okr8free", "okr8join", - "okr8meth", "okr8stat", "okrebor", "okrtlast", "okshare", "okslreb", - "okstatic", "oktemp", "oktrim", "okwcall")), - - # -- M7: optionals and error unions --------------------------------------- - *[_case(7, name, f"FEC.EXE --check {_fe(M7, name)}", False) for name in ( - "badcatch", "baddef", "baddir", "badercod", "badernam", "badetype", - "badnull", "badoref", "badorel", "badproj", "badqmark", "badret", - "badsome", "badtry", "badzero")], - *_accepts(7, M7, ( - "okcatch", "okcatmov", "okcvoid", "okdeflt", "okiflet", "okmatch", - "oknull", "okorelse", "okpatvw", "okproj", "okrepl", "oktrdef", - "oktry")), -] - -MAX_MILESTONE: int = max(case.milestone for case in CASES) -MILESTONES: tuple[str, ...] = tuple(f"m{number}" - for number in range(1, MAX_MILESTONE + 1)) - - -def milestone_number(name: str) -> int: - """Parse an ``mN`` selector against the milestones the registry knows about.""" - if not name.startswith("m") or not name[1:].isdigit(): - raise ValueError(f"invalid milestone: {name}") - value = int(name[1:]) - if value not in range(1, MAX_MILESTONE + 1): - raise ValueError(f"unsupported milestone: {name}") - return value - - -def all_cases(*, through: int = MAX_MILESTONE, only: int | None = None) -> list[Case]: - if only is not None: - return [case for case in CASES if case.milestone == only] - return [case for case in CASES if case.milestone <= through] diff --git a/src/ferrolang_vm/suite.py b/src/ferrolang_vm/suite.py deleted file mode 100644 index b412298..0000000 --- a/src/ferrolang_vm/suite.py +++ /dev/null @@ -1,16 +0,0 @@ -"""The one type shared by the case registry and the DOSBox-X runner. - -Kept separate from ``registry`` so that ``dosboxx`` can depend on the type -without importing the case data. -""" -from __future__ import annotations - -from dataclasses import dataclass - - -@dataclass(frozen=True) -class Case: - id: str - milestone: int - command: str - expect_success: bool diff --git a/src/ferrolang_vm/test_cli.py b/src/ferrolang_vm/test_cli.py deleted file mode 100644 index d82b2ee..0000000 --- a/src/ferrolang_vm/test_cli.py +++ /dev/null @@ -1,90 +0,0 @@ -"""Developer test entry point backed by a disposable DOSBox-X run.""" -from __future__ import annotations - -import argparse -import os -import sys -from pathlib import Path - -from .dosboxx import DosboxError, setup -from .registry import MAX_MILESTONE, MILESTONES - - -def main() -> int: - parser = argparse.ArgumentParser( - prog="ferro-test", - description="Fast Ferro development tests in DOSBox-X/Open Watcom.", - ) - commands = parser.add_subparsers(dest="command", required=True) - prepare = commands.add_parser("setup", help="download and verify pinned development tools") - prepare.add_argument("--accept-watcom-license", action="store_true", - help="confirm acceptance of the Sybase Open Watcom Public License") - run = commands.add_parser("run", help="build once and run milestone pytest cases") - selection = run.add_mutually_exclusive_group() - selection.add_argument("--through", choices=MILESTONES, default=f"m{MAX_MILESTONE}", - help="run cumulatively through this milestone " - f"(default: m{MAX_MILESTONE})") - selection.add_argument("--only", choices=MILESTONES, - help="run only this milestone's cases") - run.add_argument("-v", "--verbose", action="store_true", help="show every pytest case") - run.add_argument("--keep-failed", action="store_true", - help="keep the disposable DOS filesystem after failures") - run.add_argument("--show-dos", action="store_true", - help="show DOSBox-X and wait for a key before closing") - run.add_argument("--dos-log", action="store_true", - help="print the captured DOS console after the run") - run.add_argument("--trace-dos", action="store_true", - help="do not redirect case command output") - run.add_argument("-k", dest="select", metavar="EXPR", - help="run only cases whose id matches this pytest -k expression") - args, extra = parser.parse_known_args() - try: - if args.command == "setup": - dosbox, watcom = setup(accept_watcom_license=args.accept_watcom_license) - print(f"DOSBox-X: {dosbox}") - print(f"Open Watcom: {watcom}") - return 0 - if args.only: - os.environ["FERRO_TEST_ONLY"] = args.only - else: - os.environ["FERRO_TEST_THROUGH"] = args.through - for enabled, name in ( - (args.keep_failed, "FERRO_TEST_KEEP_FAILED"), - (args.show_dos, "FERRO_TEST_SHOW_DOS"), - (args.trace_dos, "FERRO_TEST_TRACE_DOS"), - (args.dos_log, "FERRO_TEST_DOS_LOG"), - ): - if enabled: - os.environ[name] = "1" - import pytest - from .paths import ROOT - # The package can be imported from a different checkout than the one the - # shell is sitting in -- an editable install plus a git worktree is enough - # to silently build and test the wrong tree. Say which tree this is. - print(f"ferro-test: building {ROOT}", file=sys.stderr) - tests = ROOT / "tools" / "tests" - # The host gates run first and take under a second. A missing declaration - # or an 8.3-illegal name would otherwise be found only after a DOSBox-X - # boot and a full compiler build, and the DOS-side message for either is - # unhelpful. They do not replace the DOS run; they precede it. - gates = [os.fspath(tests / name) for name in - ("test_host_syntax.py", "test_dos_names.py")] - if pytest.main([*gates, "-q", "--no-header"]) != 0: - print("ferro-test: host gates failed; not starting DOSBox-X", - file=sys.stderr) - return 1 - test_file = os.fspath(tests / "test_milestones_dosboxx.py") - pytest_args = [test_file, "--tb=short", "-v" if args.verbose else "-q"] - if args.select: - pytest_args.extend(["-k", args.select]) - if args.dos_log: - pytest_args.append("-s") - pytest_args.extend(extra) - return int(pytest.main(pytest_args)) - except (DosboxError, ValueError) as exc: - print(f"ferro-test: {exc}", file=sys.stderr) - return 2 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/src/node-types.json b/src/node-types.json deleted file mode 100644 index ae9616e..0000000 --- a/src/node-types.json +++ /dev/null @@ -1,121 +0,0 @@ -[ - { - "type": "block_comment", - "named": true, - "fields": {}, - "children": { - "multiple": true, - "required": false, - "types": [ - { - "type": "block_comment", - "named": true - } - ] - } - }, - { - "type": "source_file", - "named": true, - "root": true, - "fields": {}, - "children": { - "multiple": true, - "required": false, - "types": [ - { - "type": "block_comment", - "named": true - }, - { - "type": "builtin", - "named": true - }, - { - "type": "builtin_type", - "named": true - }, - { - "type": "char_literal", - "named": true - }, - { - "type": "identifier", - "named": true - }, - { - "type": "integer_literal", - "named": true - }, - { - "type": "keyword", - "named": true - }, - { - "type": "line_comment", - "named": true - }, - { - "type": "operator", - "named": true - }, - { - "type": "punctuation", - "named": true - }, - { - "type": "string_literal", - "named": true - } - ] - } - }, - { - "type": "*/", - "named": false - }, - { - "type": "/*", - "named": false - }, - { - "type": "builtin", - "named": true - }, - { - "type": "builtin_type", - "named": true - }, - { - "type": "char_literal", - "named": true - }, - { - "type": "identifier", - "named": true - }, - { - "type": "integer_literal", - "named": true - }, - { - "type": "keyword", - "named": true - }, - { - "type": "line_comment", - "named": true - }, - { - "type": "operator", - "named": true - }, - { - "type": "punctuation", - "named": true - }, - { - "type": "string_literal", - "named": true - } -] \ No newline at end of file diff --git a/src/parser.c b/src/parser.c deleted file mode 100644 index 9173d00..0000000 --- a/src/parser.c +++ /dev/null @@ -1,1744 +0,0 @@ -/* Automatically @generated by tree-sitter */ - -#include "tree_sitter/parser.h" - -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic ignored "-Wmissing-field-initializers" -#endif - -#define LANGUAGE_VERSION 14 -#define STATE_COUNT 14 -#define LARGE_STATE_COUNT 6 -#define SYMBOL_COUNT 20 -#define ALIAS_COUNT 0 -#define TOKEN_COUNT 16 -#define EXTERNAL_TOKEN_COUNT 0 -#define FIELD_COUNT 0 -#define MAX_ALIAS_SEQUENCE_LENGTH 3 -#define MAX_RESERVED_WORD_SET_SIZE 0 -#define PRODUCTION_ID_COUNT 1 -#define SUPERTYPE_COUNT 0 - -enum ts_symbol_identifiers { - sym_line_comment = 1, - anon_sym_SLASH_STAR = 2, - aux_sym_block_comment_token1 = 3, - aux_sym_block_comment_token2 = 4, - aux_sym_block_comment_token3 = 5, - anon_sym_STAR_SLASH = 6, - sym_string_literal = 7, - sym_char_literal = 8, - sym_integer_literal = 9, - sym_builtin = 10, - sym_builtin_type = 11, - sym_keyword = 12, - sym_identifier = 13, - sym_operator = 14, - sym_punctuation = 15, - sym_source_file = 16, - sym_block_comment = 17, - aux_sym_source_file_repeat1 = 18, - aux_sym_block_comment_repeat1 = 19, -}; - -static const char * const ts_symbol_names[] = { - [ts_builtin_sym_end] = "end", - [sym_line_comment] = "line_comment", - [anon_sym_SLASH_STAR] = "/*", - [aux_sym_block_comment_token1] = "block_comment_token1", - [aux_sym_block_comment_token2] = "block_comment_token2", - [aux_sym_block_comment_token3] = "block_comment_token3", - [anon_sym_STAR_SLASH] = "*/", - [sym_string_literal] = "string_literal", - [sym_char_literal] = "char_literal", - [sym_integer_literal] = "integer_literal", - [sym_builtin] = "builtin", - [sym_builtin_type] = "builtin_type", - [sym_keyword] = "keyword", - [sym_identifier] = "identifier", - [sym_operator] = "operator", - [sym_punctuation] = "punctuation", - [sym_source_file] = "source_file", - [sym_block_comment] = "block_comment", - [aux_sym_source_file_repeat1] = "source_file_repeat1", - [aux_sym_block_comment_repeat1] = "block_comment_repeat1", -}; - -static const TSSymbol ts_symbol_map[] = { - [ts_builtin_sym_end] = ts_builtin_sym_end, - [sym_line_comment] = sym_line_comment, - [anon_sym_SLASH_STAR] = anon_sym_SLASH_STAR, - [aux_sym_block_comment_token1] = aux_sym_block_comment_token1, - [aux_sym_block_comment_token2] = aux_sym_block_comment_token2, - [aux_sym_block_comment_token3] = aux_sym_block_comment_token3, - [anon_sym_STAR_SLASH] = anon_sym_STAR_SLASH, - [sym_string_literal] = sym_string_literal, - [sym_char_literal] = sym_char_literal, - [sym_integer_literal] = sym_integer_literal, - [sym_builtin] = sym_builtin, - [sym_builtin_type] = sym_builtin_type, - [sym_keyword] = sym_keyword, - [sym_identifier] = sym_identifier, - [sym_operator] = sym_operator, - [sym_punctuation] = sym_punctuation, - [sym_source_file] = sym_source_file, - [sym_block_comment] = sym_block_comment, - [aux_sym_source_file_repeat1] = aux_sym_source_file_repeat1, - [aux_sym_block_comment_repeat1] = aux_sym_block_comment_repeat1, -}; - -static const TSSymbolMetadata ts_symbol_metadata[] = { - [ts_builtin_sym_end] = { - .visible = false, - .named = true, - }, - [sym_line_comment] = { - .visible = true, - .named = true, - }, - [anon_sym_SLASH_STAR] = { - .visible = true, - .named = false, - }, - [aux_sym_block_comment_token1] = { - .visible = false, - .named = false, - }, - [aux_sym_block_comment_token2] = { - .visible = false, - .named = false, - }, - [aux_sym_block_comment_token3] = { - .visible = false, - .named = false, - }, - [anon_sym_STAR_SLASH] = { - .visible = true, - .named = false, - }, - [sym_string_literal] = { - .visible = true, - .named = true, - }, - [sym_char_literal] = { - .visible = true, - .named = true, - }, - [sym_integer_literal] = { - .visible = true, - .named = true, - }, - [sym_builtin] = { - .visible = true, - .named = true, - }, - [sym_builtin_type] = { - .visible = true, - .named = true, - }, - [sym_keyword] = { - .visible = true, - .named = true, - }, - [sym_identifier] = { - .visible = true, - .named = true, - }, - [sym_operator] = { - .visible = true, - .named = true, - }, - [sym_punctuation] = { - .visible = true, - .named = true, - }, - [sym_source_file] = { - .visible = true, - .named = true, - }, - [sym_block_comment] = { - .visible = true, - .named = true, - }, - [aux_sym_source_file_repeat1] = { - .visible = false, - .named = false, - }, - [aux_sym_block_comment_repeat1] = { - .visible = false, - .named = false, - }, -}; - -static const TSSymbol ts_alias_sequences[PRODUCTION_ID_COUNT][MAX_ALIAS_SEQUENCE_LENGTH] = { - [0] = {0}, -}; - -static const uint16_t ts_non_terminal_alias_map[] = { - 0, -}; - -static const TSStateId ts_primary_state_ids[STATE_COUNT] = { - [0] = 0, - [1] = 1, - [2] = 2, - [3] = 3, - [4] = 4, - [5] = 5, - [6] = 6, - [7] = 7, - [8] = 8, - [9] = 6, - [10] = 7, - [11] = 4, - [12] = 5, - [13] = 13, -}; - -static bool ts_lex(TSLexer *lexer, TSStateId state) { - START_LEXER(); - eof = lexer->eof(lexer); - switch (state) { - case 0: - if (eof) ADVANCE(21); - ADVANCE_MAP( - '!', 155, - '"', 1, - '%', 155, - '&', 155, - '\'', 6, - '*', 157, - '+', 157, - '-', 159, - '.', 161, - '/', 153, - '0', 31, - '<', 154, - '=', 158, - '>', 156, - '@', 20, - 'S', 69, - '^', 155, - 'a', 110, - 'b', 115, - 'c', 48, - 'd', 71, - 'e', 98, - 'f', 50, - 'i', 44, - 'l', 76, - 'm', 49, - 'n', 118, - 'o', 126, - 'p', 51, - 'r', 78, - 's', 68, - 't', 127, - 'u', 45, - 'v', 53, - 'w', 86, - '|', 155, - '?', 152, - '~', 152, - '(', 160, - ')', 160, - ',', 160, - ':', 160, - ';', 160, - '[', 160, - ']', 160, - '{', 160, - '}', 160, - ); - if (('\t' <= lookahead && lookahead <= '\r') || - lookahead == ' ' || - lookahead == 0x200b || - lookahead == 0x2060 || - lookahead == 0xfeff) SKIP(0); - if (('1' <= lookahead && lookahead <= '9')) ADVANCE(34); - if (('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('g' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 1: - if (lookahead == '"') ADVANCE(29); - if (lookahead == '\\') ADVANCE(7); - if (lookahead != 0 && - lookahead != '\n' && - lookahead != '\r' && - lookahead != 0x17f && - lookahead != 0x212a) ADVANCE(1); - END_STATE(); - case 2: - if (lookahead == '\'') ADVANCE(30); - END_STATE(); - case 3: - if (lookahead == '*') ADVANCE(23); - if (lookahead != 0 && - lookahead != 0x17f && - lookahead != 0x212a) ADVANCE(27); - END_STATE(); - case 4: - if (lookahead == '*') ADVANCE(5); - if (lookahead == '/') ADVANCE(3); - if (('\t' <= lookahead && lookahead <= '\r') || - lookahead == ' ' || - lookahead == 0x200b || - lookahead == 0x2060 || - lookahead == 0xfeff) ADVANCE(24); - if (lookahead != 0 && - lookahead != 0x17f && - lookahead != 0x212a) ADVANCE(25); - END_STATE(); - case 5: - if (lookahead == '/') ADVANCE(28); - if (lookahead != 0 && - lookahead != 0x17f && - lookahead != 0x212a) ADVANCE(26); - END_STATE(); - case 6: - if (lookahead == '\\') ADVANCE(8); - if (lookahead != 0 && - lookahead != '\n' && - lookahead != '\r' && - lookahead != '\'' && - lookahead != 0x17f && - lookahead != 0x212a) ADVANCE(2); - END_STATE(); - case 7: - ADVANCE_MAP( - 'u', 17, - 'x', 13, - '"', 1, - '\'', 1, - '0', 1, - '\\', 1, - 'n', 1, - 'r', 1, - 't', 1, - ); - END_STATE(); - case 8: - ADVANCE_MAP( - 'u', 18, - 'x', 14, - '"', 2, - '\'', 2, - '0', 2, - '\\', 2, - 'n', 2, - 'r', 2, - 't', 2, - ); - END_STATE(); - case 9: - if (lookahead == '0' || - lookahead == '1' || - lookahead == '_') ADVANCE(32); - END_STATE(); - case 10: - if (('0' <= lookahead && lookahead <= '7') || - lookahead == '_') ADVANCE(33); - END_STATE(); - case 11: - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(1); - END_STATE(); - case 12: - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(2); - END_STATE(); - case 13: - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(11); - END_STATE(); - case 14: - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(12); - END_STATE(); - case 15: - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(13); - END_STATE(); - case 16: - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(14); - END_STATE(); - case 17: - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(15); - END_STATE(); - case 18: - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(16); - END_STATE(); - case 19: - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'F') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(35); - END_STATE(); - case 20: - if (('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(36); - END_STATE(); - case 21: - ACCEPT_TOKEN(ts_builtin_sym_end); - END_STATE(); - case 22: - ACCEPT_TOKEN(sym_line_comment); - if (lookahead != 0 && - lookahead != '\n' && - lookahead != 0x17f && - lookahead != 0x212a) ADVANCE(22); - END_STATE(); - case 23: - ACCEPT_TOKEN(anon_sym_SLASH_STAR); - END_STATE(); - case 24: - ACCEPT_TOKEN(aux_sym_block_comment_token1); - if (('\t' <= lookahead && lookahead <= '\r') || - lookahead == ' ' || - lookahead == 0x200b || - lookahead == 0x2060 || - lookahead == 0xfeff) ADVANCE(24); - if (lookahead != 0 && - lookahead != '*' && - lookahead != '/' && - lookahead != 0x17f && - lookahead != 0x212a) ADVANCE(25); - END_STATE(); - case 25: - ACCEPT_TOKEN(aux_sym_block_comment_token1); - if (lookahead != 0 && - lookahead != '*' && - lookahead != '/' && - lookahead != 0x17f && - lookahead != 0x212a) ADVANCE(25); - END_STATE(); - case 26: - ACCEPT_TOKEN(aux_sym_block_comment_token2); - END_STATE(); - case 27: - ACCEPT_TOKEN(aux_sym_block_comment_token3); - END_STATE(); - case 28: - ACCEPT_TOKEN(anon_sym_STAR_SLASH); - END_STATE(); - case 29: - ACCEPT_TOKEN(sym_string_literal); - END_STATE(); - case 30: - ACCEPT_TOKEN(sym_char_literal); - END_STATE(); - case 31: - ACCEPT_TOKEN(sym_integer_literal); - if (lookahead == 'B' || - lookahead == 'b') ADVANCE(9); - if (lookahead == 'O' || - lookahead == 'o') ADVANCE(10); - if (lookahead == 'X' || - lookahead == 'x') ADVANCE(19); - if (('0' <= lookahead && lookahead <= '9') || - lookahead == '_') ADVANCE(34); - END_STATE(); - case 32: - ACCEPT_TOKEN(sym_integer_literal); - if (lookahead == '0' || - lookahead == '1' || - lookahead == '_') ADVANCE(32); - END_STATE(); - case 33: - ACCEPT_TOKEN(sym_integer_literal); - if (('0' <= lookahead && lookahead <= '7') || - lookahead == '_') ADVANCE(33); - END_STATE(); - case 34: - ACCEPT_TOKEN(sym_integer_literal); - if (('0' <= lookahead && lookahead <= '9') || - lookahead == '_') ADVANCE(34); - END_STATE(); - case 35: - ACCEPT_TOKEN(sym_integer_literal); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'F') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(35); - END_STATE(); - case 36: - ACCEPT_TOKEN(sym_builtin); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(36); - END_STATE(); - case 37: - ACCEPT_TOKEN(sym_builtin_type); - END_STATE(); - case 38: - ACCEPT_TOKEN(sym_builtin_type); - if (lookahead == 'u') ADVANCE(63); - END_STATE(); - case 39: - ACCEPT_TOKEN(sym_keyword); - END_STATE(); - case 40: - ACCEPT_TOKEN(sym_keyword); - if (lookahead == '_') ADVANCE(135); - END_STATE(); - case 41: - ACCEPT_TOKEN(sym_keyword); - if (lookahead == 'e') ADVANCE(100); - END_STATE(); - case 42: - ACCEPT_TOKEN(sym_keyword); - if (lookahead == 'm') ADVANCE(39); - END_STATE(); - case 43: - ACCEPT_TOKEN(sym_keyword); - if (lookahead == 't') ADVANCE(80); - END_STATE(); - case 44: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == '1') ADVANCE(47); - if (lookahead == '3') ADVANCE(46); - if (lookahead == '8') ADVANCE(37); - if (lookahead == 'f') ADVANCE(39); - if (lookahead == 'm') ADVANCE(120); - if (lookahead == 'n') ADVANCE(43); - if (lookahead == 's') ADVANCE(87); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 45: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == '1') ADVANCE(47); - if (lookahead == '3') ADVANCE(46); - if (lookahead == '8') ADVANCE(37); - if (lookahead == 'n') ADVANCE(67); - if (lookahead == 's') ADVANCE(87); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 46: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == '2') ADVANCE(37); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 47: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == '6') ADVANCE(37); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 48: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'a') ADVANCE(140); - if (lookahead == 'h') ADVANCE(54); - if (lookahead == 'o') ADVANCE(107); - if (lookahead == 'r') ADVANCE(93); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('b' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 49: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'a') ADVANCE(140); - if (lookahead == 'u') ADVANCE(137); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('b' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 50: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'a') ADVANCE(99); - if (lookahead == 'n') ADVANCE(39); - if (lookahead == 'o') ADVANCE(124); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('b' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 51: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'a') ADVANCE(61); - if (lookahead == 'u') ADVANCE(59); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('b' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 52: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'a') ADVANCE(96); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('b' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 53: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'a') ADVANCE(124); - if (lookahead == 'o') ADVANCE(89); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('b' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 54: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'a') ADVANCE(125); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('b' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 55: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'a') ADVANCE(101); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('b' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 56: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'a') ADVANCE(133); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('b' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 57: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'a') ADVANCE(83); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('b' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 58: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'a') ADVANCE(141); - if (lookahead == 'r') ADVANCE(38); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('b' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 59: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'b') ADVANCE(39); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 60: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'c') ADVANCE(39); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 61: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'c') ADVANCE(97); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 62: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'c') ADVANCE(85); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 63: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'c') ADVANCE(137); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 64: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'c') ADVANCE(55); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 65: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'd') ADVANCE(39); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 66: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'd') ADVANCE(37); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 67: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'd') ADVANCE(77); - if (lookahead == 'i') ADVANCE(137); - if (lookahead == 's') ADVANCE(57); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 68: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'e') ADVANCE(103); - if (lookahead == 'h') ADVANCE(56); - if (lookahead == 't') ADVANCE(58); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 69: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'e') ADVANCE(103); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 70: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'e') ADVANCE(65); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 71: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'e') ADVANCE(82); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 72: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'e') ADVANCE(39); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 73: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'e') ADVANCE(37); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 74: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'e') ADVANCE(52); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 75: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'e') ADVANCE(124); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 76: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'e') ADVANCE(137); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 77: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'e') ADVANCE(84); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 78: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'e') ADVANCE(139); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 79: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'e') ADVANCE(128); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 80: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'e') ADVANCE(132); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 81: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'f') ADVANCE(39); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 82: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'f') ADVANCE(75); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 83: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'f') ADVANCE(72); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 84: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'f') ADVANCE(91); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 85: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'h') ADVANCE(39); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 86: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'h') ADVANCE(95); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 87: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'i') ADVANCE(150); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 88: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'i') ADVANCE(112); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 89: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'i') ADVANCE(66); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 90: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'i') ADVANCE(109); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 91: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'i') ADVANCE(113); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 92: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'i') ADVANCE(60); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 93: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'i') ADVANCE(143); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 94: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'i') ADVANCE(64); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 95: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'i') ADVANCE(105); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 96: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'k') ADVANCE(39); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 97: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'k') ADVANCE(70); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 98: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'l') ADVANCE(136); - if (lookahead == 'n') ADVANCE(145); - if (lookahead == 'r') ADVANCE(129); - if (lookahead == 'x') ADVANCE(142); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 99: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'l') ADVANCE(136); - if (lookahead == 'r') ADVANCE(39); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 100: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'l') ADVANCE(136); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 101: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'l') ADVANCE(39); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 102: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'l') ADVANCE(37); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 103: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'l') ADVANCE(81); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 104: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'l') ADVANCE(101); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 105: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'l') ADVANCE(72); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 106: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'm') ADVANCE(39); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 107: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'm') ADVANCE(123); - if (lookahead == 'n') ADVANCE(134); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 108: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'm') ADVANCE(92); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 109: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'm') ADVANCE(72); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 110: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'n') ADVANCE(65); - if (lookahead == 's') ADVANCE(42); - if (lookahead == 't') ADVANCE(114); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 111: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'n') ADVANCE(39); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 112: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'n') ADVANCE(147); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 113: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'n') ADVANCE(70); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 114: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'o') ADVANCE(108); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 115: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'o') ADVANCE(117); - if (lookahead == 'r') ADVANCE(74); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 116: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'o') ADVANCE(124); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 117: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'o') ADVANCE(102); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 118: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'o') ADVANCE(137); - if (lookahead == 'u') ADVANCE(104); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 119: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'o') ADVANCE(130); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 120: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'p') ADVANCE(119); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 121: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'p') ADVANCE(72); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 122: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'p') ADVANCE(138); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 123: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'p') ADVANCE(144); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 124: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'r') ADVANCE(39); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 125: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'r') ADVANCE(37); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 126: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'r') ADVANCE(41); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 127: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'r') ADVANCE(146); - if (lookahead == 'y') ADVANCE(121); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 128: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'r') ADVANCE(111); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 129: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'r') ADVANCE(116); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 130: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'r') ADVANCE(137); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 131: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'r') ADVANCE(149); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 132: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'r') ADVANCE(131); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 133: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'r') ADVANCE(70); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 134: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 's') ADVANCE(137); - if (lookahead == 't') ADVANCE(88); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 135: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 's') ADVANCE(57); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 136: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 's') ADVANCE(72); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 137: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 't') ADVANCE(39); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 138: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 't') ADVANCE(40); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 139: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 't') ADVANCE(148); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 140: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 't') ADVANCE(62); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 141: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 't') ADVANCE(92); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 142: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 't') ADVANCE(79); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 143: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 't') ADVANCE(94); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 144: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 't') ADVANCE(90); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 145: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'u') ADVANCE(106); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 146: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'u') ADVANCE(72); - if (lookahead == 'y') ADVANCE(39); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 147: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'u') ADVANCE(72); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 148: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'u') ADVANCE(128); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 149: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'u') ADVANCE(122); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 150: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'z') ADVANCE(73); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'y')) ADVANCE(151); - END_STATE(); - case 151: - ACCEPT_TOKEN(sym_identifier); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 152: - ACCEPT_TOKEN(sym_operator); - END_STATE(); - case 153: - ACCEPT_TOKEN(sym_operator); - if (lookahead == '*') ADVANCE(23); - if (lookahead == '/') ADVANCE(22); - if (lookahead == '=') ADVANCE(152); - END_STATE(); - case 154: - ACCEPT_TOKEN(sym_operator); - if (lookahead == '<') ADVANCE(155); - if (lookahead == '=') ADVANCE(152); - END_STATE(); - case 155: - ACCEPT_TOKEN(sym_operator); - if (lookahead == '=') ADVANCE(152); - END_STATE(); - case 156: - ACCEPT_TOKEN(sym_operator); - if (lookahead == '=') ADVANCE(152); - if (lookahead == '>') ADVANCE(155); - END_STATE(); - case 157: - ACCEPT_TOKEN(sym_operator); - if (lookahead == '%' || - lookahead == '=') ADVANCE(152); - END_STATE(); - case 158: - ACCEPT_TOKEN(sym_operator); - if (lookahead == '=' || - lookahead == '>') ADVANCE(152); - END_STATE(); - case 159: - ACCEPT_TOKEN(sym_operator); - if (lookahead == '%' || - lookahead == '=' || - lookahead == '>') ADVANCE(152); - END_STATE(); - case 160: - ACCEPT_TOKEN(sym_punctuation); - END_STATE(); - case 161: - ACCEPT_TOKEN(sym_punctuation); - if (lookahead == '.') ADVANCE(152); - END_STATE(); - default: - return false; - } -} - -static const TSLexMode ts_lex_modes[STATE_COUNT] = { - [0] = {.lex_state = 0}, - [1] = {.lex_state = 0}, - [2] = {.lex_state = 0}, - [3] = {.lex_state = 0}, - [4] = {.lex_state = 0}, - [5] = {.lex_state = 0}, - [6] = {.lex_state = 4}, - [7] = {.lex_state = 4}, - [8] = {.lex_state = 4}, - [9] = {.lex_state = 4}, - [10] = {.lex_state = 4}, - [11] = {.lex_state = 4}, - [12] = {.lex_state = 4}, - [13] = {.lex_state = 0}, -}; - -static const uint16_t ts_parse_table[LARGE_STATE_COUNT][SYMBOL_COUNT] = { - [STATE(0)] = { - [ts_builtin_sym_end] = ACTIONS(1), - [sym_line_comment] = ACTIONS(1), - [anon_sym_SLASH_STAR] = ACTIONS(1), - [sym_string_literal] = ACTIONS(1), - [sym_char_literal] = ACTIONS(1), - [sym_integer_literal] = ACTIONS(1), - [sym_builtin] = ACTIONS(1), - [sym_builtin_type] = ACTIONS(1), - [sym_keyword] = ACTIONS(1), - [sym_identifier] = ACTIONS(1), - [sym_operator] = ACTIONS(1), - [sym_punctuation] = ACTIONS(1), - }, - [STATE(1)] = { - [sym_source_file] = STATE(13), - [sym_block_comment] = STATE(2), - [aux_sym_source_file_repeat1] = STATE(2), - [ts_builtin_sym_end] = ACTIONS(3), - [sym_line_comment] = ACTIONS(5), - [anon_sym_SLASH_STAR] = ACTIONS(7), - [sym_string_literal] = ACTIONS(5), - [sym_char_literal] = ACTIONS(5), - [sym_integer_literal] = ACTIONS(5), - [sym_builtin] = ACTIONS(5), - [sym_builtin_type] = ACTIONS(9), - [sym_keyword] = ACTIONS(5), - [sym_identifier] = ACTIONS(9), - [sym_operator] = ACTIONS(9), - [sym_punctuation] = ACTIONS(9), - }, - [STATE(2)] = { - [sym_block_comment] = STATE(3), - [aux_sym_source_file_repeat1] = STATE(3), - [ts_builtin_sym_end] = ACTIONS(11), - [sym_line_comment] = ACTIONS(13), - [anon_sym_SLASH_STAR] = ACTIONS(7), - [sym_string_literal] = ACTIONS(13), - [sym_char_literal] = ACTIONS(13), - [sym_integer_literal] = ACTIONS(13), - [sym_builtin] = ACTIONS(13), - [sym_builtin_type] = ACTIONS(15), - [sym_keyword] = ACTIONS(13), - [sym_identifier] = ACTIONS(15), - [sym_operator] = ACTIONS(15), - [sym_punctuation] = ACTIONS(15), - }, - [STATE(3)] = { - [sym_block_comment] = STATE(3), - [aux_sym_source_file_repeat1] = STATE(3), - [ts_builtin_sym_end] = ACTIONS(17), - [sym_line_comment] = ACTIONS(19), - [anon_sym_SLASH_STAR] = ACTIONS(22), - [sym_string_literal] = ACTIONS(19), - [sym_char_literal] = ACTIONS(19), - [sym_integer_literal] = ACTIONS(19), - [sym_builtin] = ACTIONS(19), - [sym_builtin_type] = ACTIONS(25), - [sym_keyword] = ACTIONS(19), - [sym_identifier] = ACTIONS(25), - [sym_operator] = ACTIONS(25), - [sym_punctuation] = ACTIONS(25), - }, - [STATE(4)] = { - [ts_builtin_sym_end] = ACTIONS(28), - [sym_line_comment] = ACTIONS(28), - [anon_sym_SLASH_STAR] = ACTIONS(28), - [sym_string_literal] = ACTIONS(28), - [sym_char_literal] = ACTIONS(28), - [sym_integer_literal] = ACTIONS(28), - [sym_builtin] = ACTIONS(28), - [sym_builtin_type] = ACTIONS(30), - [sym_keyword] = ACTIONS(28), - [sym_identifier] = ACTIONS(30), - [sym_operator] = ACTIONS(30), - [sym_punctuation] = ACTIONS(30), - }, - [STATE(5)] = { - [ts_builtin_sym_end] = ACTIONS(32), - [sym_line_comment] = ACTIONS(32), - [anon_sym_SLASH_STAR] = ACTIONS(32), - [sym_string_literal] = ACTIONS(32), - [sym_char_literal] = ACTIONS(32), - [sym_integer_literal] = ACTIONS(32), - [sym_builtin] = ACTIONS(32), - [sym_builtin_type] = ACTIONS(34), - [sym_keyword] = ACTIONS(32), - [sym_identifier] = ACTIONS(34), - [sym_operator] = ACTIONS(34), - [sym_punctuation] = ACTIONS(34), - }, -}; - -static const uint16_t ts_small_parse_table[] = { - [0] = 5, - ACTIONS(36), 1, - anon_sym_SLASH_STAR, - ACTIONS(38), 1, - aux_sym_block_comment_token1, - ACTIONS(42), 1, - anon_sym_STAR_SLASH, - ACTIONS(40), 2, - aux_sym_block_comment_token2, - aux_sym_block_comment_token3, - STATE(7), 2, - sym_block_comment, - aux_sym_block_comment_repeat1, - [18] = 5, - ACTIONS(36), 1, - anon_sym_SLASH_STAR, - ACTIONS(44), 1, - aux_sym_block_comment_token1, - ACTIONS(48), 1, - anon_sym_STAR_SLASH, - ACTIONS(46), 2, - aux_sym_block_comment_token2, - aux_sym_block_comment_token3, - STATE(8), 2, - sym_block_comment, - aux_sym_block_comment_repeat1, - [36] = 5, - ACTIONS(50), 1, - anon_sym_SLASH_STAR, - ACTIONS(53), 1, - aux_sym_block_comment_token1, - ACTIONS(59), 1, - anon_sym_STAR_SLASH, - ACTIONS(56), 2, - aux_sym_block_comment_token2, - aux_sym_block_comment_token3, - STATE(8), 2, - sym_block_comment, - aux_sym_block_comment_repeat1, - [54] = 5, - ACTIONS(36), 1, - anon_sym_SLASH_STAR, - ACTIONS(61), 1, - aux_sym_block_comment_token1, - ACTIONS(65), 1, - anon_sym_STAR_SLASH, - ACTIONS(63), 2, - aux_sym_block_comment_token2, - aux_sym_block_comment_token3, - STATE(10), 2, - sym_block_comment, - aux_sym_block_comment_repeat1, - [72] = 5, - ACTIONS(36), 1, - anon_sym_SLASH_STAR, - ACTIONS(44), 1, - aux_sym_block_comment_token1, - ACTIONS(67), 1, - anon_sym_STAR_SLASH, - ACTIONS(46), 2, - aux_sym_block_comment_token2, - aux_sym_block_comment_token3, - STATE(8), 2, - sym_block_comment, - aux_sym_block_comment_repeat1, - [90] = 2, - ACTIONS(28), 1, - aux_sym_block_comment_token1, - ACTIONS(30), 4, - anon_sym_SLASH_STAR, - aux_sym_block_comment_token2, - aux_sym_block_comment_token3, - anon_sym_STAR_SLASH, - [100] = 2, - ACTIONS(32), 1, - aux_sym_block_comment_token1, - ACTIONS(34), 4, - anon_sym_SLASH_STAR, - aux_sym_block_comment_token2, - aux_sym_block_comment_token3, - anon_sym_STAR_SLASH, - [110] = 1, - ACTIONS(69), 1, - ts_builtin_sym_end, -}; - -static const uint32_t ts_small_parse_table_map[] = { - [SMALL_STATE(6)] = 0, - [SMALL_STATE(7)] = 18, - [SMALL_STATE(8)] = 36, - [SMALL_STATE(9)] = 54, - [SMALL_STATE(10)] = 72, - [SMALL_STATE(11)] = 90, - [SMALL_STATE(12)] = 100, - [SMALL_STATE(13)] = 110, -}; - -static const TSParseActionEntry ts_parse_actions[] = { - [0] = {.entry = {.count = 0, .reusable = false}}, - [1] = {.entry = {.count = 1, .reusable = false}}, RECOVER(), - [3] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_source_file, 0, 0, 0), - [5] = {.entry = {.count = 1, .reusable = true}}, SHIFT(2), - [7] = {.entry = {.count = 1, .reusable = true}}, SHIFT(6), - [9] = {.entry = {.count = 1, .reusable = false}}, SHIFT(2), - [11] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_source_file, 1, 0, 0), - [13] = {.entry = {.count = 1, .reusable = true}}, SHIFT(3), - [15] = {.entry = {.count = 1, .reusable = false}}, SHIFT(3), - [17] = {.entry = {.count = 1, .reusable = true}}, REDUCE(aux_sym_source_file_repeat1, 2, 0, 0), - [19] = {.entry = {.count = 2, .reusable = true}}, REDUCE(aux_sym_source_file_repeat1, 2, 0, 0), SHIFT_REPEAT(3), - [22] = {.entry = {.count = 2, .reusable = true}}, REDUCE(aux_sym_source_file_repeat1, 2, 0, 0), SHIFT_REPEAT(6), - [25] = {.entry = {.count = 2, .reusable = false}}, REDUCE(aux_sym_source_file_repeat1, 2, 0, 0), SHIFT_REPEAT(3), - [28] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_block_comment, 2, 0, 0), - [30] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_block_comment, 2, 0, 0), - [32] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_block_comment, 3, 0, 0), - [34] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_block_comment, 3, 0, 0), - [36] = {.entry = {.count = 1, .reusable = false}}, SHIFT(9), - [38] = {.entry = {.count = 1, .reusable = true}}, SHIFT(7), - [40] = {.entry = {.count = 1, .reusable = false}}, SHIFT(7), - [42] = {.entry = {.count = 1, .reusable = false}}, SHIFT(4), - [44] = {.entry = {.count = 1, .reusable = true}}, SHIFT(8), - [46] = {.entry = {.count = 1, .reusable = false}}, SHIFT(8), - [48] = {.entry = {.count = 1, .reusable = false}}, SHIFT(5), - [50] = {.entry = {.count = 2, .reusable = false}}, REDUCE(aux_sym_block_comment_repeat1, 2, 0, 0), SHIFT_REPEAT(9), - [53] = {.entry = {.count = 2, .reusable = true}}, REDUCE(aux_sym_block_comment_repeat1, 2, 0, 0), SHIFT_REPEAT(8), - [56] = {.entry = {.count = 2, .reusable = false}}, REDUCE(aux_sym_block_comment_repeat1, 2, 0, 0), SHIFT_REPEAT(8), - [59] = {.entry = {.count = 1, .reusable = false}}, REDUCE(aux_sym_block_comment_repeat1, 2, 0, 0), - [61] = {.entry = {.count = 1, .reusable = true}}, SHIFT(10), - [63] = {.entry = {.count = 1, .reusable = false}}, SHIFT(10), - [65] = {.entry = {.count = 1, .reusable = false}}, SHIFT(11), - [67] = {.entry = {.count = 1, .reusable = false}}, SHIFT(12), - [69] = {.entry = {.count = 1, .reusable = true}}, ACCEPT_INPUT(), -}; - -#ifdef __cplusplus -extern "C" { -#endif -#ifdef TREE_SITTER_HIDE_SYMBOLS -#define TS_PUBLIC -#elif defined(_WIN32) -#define TS_PUBLIC __declspec(dllexport) -#else -#define TS_PUBLIC __attribute__((visibility("default"))) -#endif - -TS_PUBLIC const TSLanguage *tree_sitter_ferro(void) { - static const TSLanguage language = { - .abi_version = LANGUAGE_VERSION, - .symbol_count = SYMBOL_COUNT, - .alias_count = ALIAS_COUNT, - .token_count = TOKEN_COUNT, - .external_token_count = EXTERNAL_TOKEN_COUNT, - .state_count = STATE_COUNT, - .large_state_count = LARGE_STATE_COUNT, - .production_id_count = PRODUCTION_ID_COUNT, - .field_count = FIELD_COUNT, - .max_alias_sequence_length = MAX_ALIAS_SEQUENCE_LENGTH, - .parse_table = &ts_parse_table[0][0], - .small_parse_table = ts_small_parse_table, - .small_parse_table_map = ts_small_parse_table_map, - .parse_actions = ts_parse_actions, - .symbol_names = ts_symbol_names, - .symbol_metadata = ts_symbol_metadata, - .public_symbol_map = ts_symbol_map, - .alias_map = ts_non_terminal_alias_map, - .alias_sequences = &ts_alias_sequences[0][0], - .lex_modes = (const void*)ts_lex_modes, - .lex_fn = ts_lex, - .primary_state_ids = ts_primary_state_ids, - }; - return &language; -} -#ifdef __cplusplus -} -#endif diff --git a/src/tree_sitter/parser.h b/src/tree_sitter/parser.h deleted file mode 100644 index 5f37997..0000000 --- a/src/tree_sitter/parser.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef TREE_SITTER_PARSER_H_ -#define TREE_SITTER_PARSER_H_ -#ifdef __cplusplus -extern "C" { -#endif -#include -#include -#include -#define ts_builtin_sym_error ((TSSymbol)-1) -#define ts_builtin_sym_end 0 -#define TREE_SITTER_SERIALIZATION_BUFFER_SIZE 1024 -#ifndef TREE_SITTER_API_H_ -typedef uint16_t TSStateId; -typedef uint16_t TSSymbol; -typedef uint16_t TSFieldId; -typedef struct TSLanguage TSLanguage; -typedef struct TSLanguageMetadata { uint8_t major_version; uint8_t minor_version; uint8_t patch_version; } TSLanguageMetadata; -#endif -typedef struct { TSFieldId field_id; uint8_t child_index; bool inherited; } TSFieldMapEntry; -typedef struct { uint16_t index; uint16_t length; } TSMapSlice; -typedef struct { bool visible; bool named; bool supertype; } TSSymbolMetadata; -typedef struct TSLexer TSLexer; -struct TSLexer { int32_t lookahead; TSSymbol result_symbol; void (*advance)(TSLexer *, bool); void (*mark_end)(TSLexer *); uint32_t (*get_column)(TSLexer *); bool (*is_at_included_range_start)(const TSLexer *); bool (*eof)(const TSLexer *); void (*log)(const TSLexer *, const char *, ...); }; -typedef enum { TSParseActionTypeShift, TSParseActionTypeReduce, TSParseActionTypeAccept, TSParseActionTypeRecover } TSParseActionType; -typedef union { struct { uint8_t type; TSStateId state; bool extra; bool repetition; } shift; struct { uint8_t type; uint8_t child_count; TSSymbol symbol; int16_t dynamic_precedence; uint16_t production_id; } reduce; uint8_t type; } TSParseAction; -typedef struct { uint16_t lex_state; uint16_t external_lex_state; } TSLexMode; -typedef struct { uint16_t lex_state; uint16_t external_lex_state; uint16_t reserved_word_set_id; } TSLexerMode; -typedef union { TSParseAction action; struct { uint8_t count; bool reusable; } entry; } TSParseActionEntry; -typedef struct { int32_t start; int32_t end; } TSCharacterRange; -struct TSLanguage { uint32_t abi_version; uint32_t symbol_count; uint32_t alias_count; uint32_t token_count; uint32_t external_token_count; uint32_t state_count; uint32_t large_state_count; uint32_t production_id_count; uint32_t field_count; uint16_t max_alias_sequence_length; const uint16_t *parse_table; const uint16_t *small_parse_table; const uint32_t *small_parse_table_map; const TSParseActionEntry *parse_actions; const char * const *symbol_names; const char * const *field_names; const TSMapSlice *field_map_slices; const TSFieldMapEntry *field_map_entries; const TSSymbolMetadata *symbol_metadata; const TSSymbol *public_symbol_map; const uint16_t *alias_map; const TSSymbol *alias_sequences; const TSLexerMode *lex_modes; bool (*lex_fn)(TSLexer *, TSStateId); bool (*keyword_lex_fn)(TSLexer *, TSStateId); TSSymbol keyword_capture_token; struct { const bool *states; const TSSymbol *symbol_map; void *(*create)(void); void (*destroy)(void *); bool (*scan)(void *, TSLexer *, const bool *); unsigned (*serialize)(void *, char *); void (*deserialize)(void *, const char *, unsigned); } external_scanner; const TSStateId *primary_state_ids; const char *name; const TSSymbol *reserved_words; uint16_t max_reserved_word_set_size; uint32_t supertype_count; const TSSymbol *supertype_symbols; const TSMapSlice *supertype_map_slices; const TSSymbol *supertype_map_entries; TSLanguageMetadata metadata; }; -static inline bool set_contains(const TSCharacterRange *ranges, uint32_t len, int32_t lookahead) { uint32_t index=0,size=len; while(size>1){uint32_t half=size/2,mid=index+half; const TSCharacterRange *r=&ranges[mid]; if(lookahead>=r->start&&lookahead<=r->end)return true; else if(lookahead>r->end)index=mid; size-=half;} const TSCharacterRange *r=&ranges[index]; return lookahead>=r->start&&lookahead<=r->end; } -#ifdef _MSC_VER -#define UNUSED __pragma(warning(suppress : 4101)) -#else -#define UNUSED __attribute__((unused)) -#endif -#define START_LEXER() bool result=false; bool skip=false; UNUSED bool eof=false; int32_t lookahead; goto start; next_state: lexer->advance(lexer,skip); start: skip=false; lookahead=lexer->lookahead; -#define ADVANCE(state_value) { state=state_value; goto next_state; } -#define ADVANCE_MAP(...) { static const uint16_t map[]={__VA_ARGS__}; for(uint32_t i=0;iresult_symbol=symbol_value; lexer->mark_end(lexer); -#define END_STATE() return result; -#define SMALL_STATE(id) ((id)-LARGE_STATE_COUNT) -#define STATE(id) id -#define ACTIONS(id) id -#define SHIFT(state_value) {{.shift={.type=TSParseActionTypeShift,.state=(state_value)}}} -#define SHIFT_REPEAT(state_value) {{.shift={.type=TSParseActionTypeShift,.state=(state_value),.repetition=true}}} -#define SHIFT_EXTRA() {{.shift={.type=TSParseActionTypeShift,.extra=true}}} -#define REDUCE(symbol_name,children,precedence,prod_id) {{.reduce={.type=TSParseActionTypeReduce,.symbol=symbol_name,.child_count=children,.dynamic_precedence=precedence,.production_id=prod_id},}} -#define RECOVER() {{.type=TSParseActionTypeRecover}} -#define ACCEPT_INPUT() {{.type=TSParseActionTypeAccept}} -#ifdef __cplusplus -} -#endif -#endif diff --git a/tests/run.py b/tests/run.py new file mode 100644 index 0000000..20700dd --- /dev/null +++ b/tests/run.py @@ -0,0 +1,140 @@ +"""Run every fixture through the front end and check what it reports. + +The compiler is a front end now -- lexer, parser, types, ownership -- so a +fixture is checked by running `fec` on it and looking at two things: whether it +was accepted, and, when it was rejected, whether the diagnostic is the one the +fixture asked for. + +A fixture states its expectation in its first line: + + // ERROR:8:self rejected at line 8, with "self" in the message + // ERROR:expected ';' rejected, message only -- the parse fixtures, where + the line is not the interesting part + +A fixture with no marker whose name starts with `bad` must be rejected but does +not pin the message yet. Anything else must be accepted. + +Fixtures under `parse/` are checked with --dump-ast rather than --check: they +exercise the grammar, and several are deliberately not well-typed. + +This runs on the host in about a second. There is no VM: nothing here executes +generated code, because there is no code generator. +""" +from __future__ import annotations + +import argparse +import os +import re +import subprocess +import sys +from dataclasses import dataclass +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +FIXTURES = ROOT / "fec" / "tests" +WATCOM = ROOT / ".dosboxx" / "watcom" +SOURCES = ("arena", "diag", "lexer", "ast", "parser", "types", "m7", "own", + "check", "driver") +# Fixtures live here until there is a code generator to run them against. +QUARANTINE = "pending-backend" + +MARKER = re.compile(r"^//\s*ERROR:(?:(\d+):)?(.*)$") + + +@dataclass +class Expectation: + rejected: bool + line: int | None = None + text: str | None = None + + +def expectation(path: Path) -> Expectation: + first = path.read_text(encoding="utf-8", errors="replace").split("\n", 1)[0] + m = MARKER.match(first.strip()) + if m: + line = int(m.group(1)) if m.group(1) else None + return Expectation(True, line, m.group(2).strip()) + name = path.stem + return Expectation(name.startswith("bad") or "-bad-" in name or name.startswith("own-bad")) + + +def build(out: Path) -> Path: + """Build the front end with the pinned toolchain, hosted.""" + wcl = WATCOM / "binnt" / "wcl386.exe" + if not wcl.is_file(): + sys.exit(f"pinned Open Watcom not found at {WATCOM}") + out.mkdir(parents=True, exist_ok=True) + env = dict(os.environ) + env.update(WATCOM=str(WATCOM), INCLUDE=f"{WATCOM / 'h'};{WATCOM / 'h' / 'nt'}", + PATH=f"{WATCOM / 'binnt'}{os.pathsep}{env.get('PATH', '')}") + src = ROOT / "fec" / "src" + cmd = [str(wcl), "-q", "-za", "-wx", "-bt=nt", "-fe=fec.exe", f"-i={src}"] + cmd += [str(src / f"{n}.c") for n in SOURCES] + done = subprocess.run(cmd, cwd=out, capture_output=True, text=True, env=env) + if done.returncode != 0 or (done.stdout + done.stderr).strip(): + sys.exit("front end does not build clean:\n" + done.stdout + done.stderr) + return out / "fec.exe" + + +def run_case(fec: Path, path: Path) -> tuple[bool, str]: + want = expectation(path) + # The grammar fixtures are not all well-typed; stop after parsing. + mode = "--dump-ast" if path.parent.name == "parse" else "--check" + done = subprocess.run([str(fec), mode, str(path)], + capture_output=True, text=True, timeout=30) + output = (done.stdout + done.stderr).strip() + rejected = done.returncode != 0 + + if want.rejected != rejected: + verb = "accepted" if rejected else "rejected" + return False, f"expected to be {'rejected' if want.rejected else verb}" + if not want.rejected: + return True, "" + if want.line is None: + if want.text and want.text.lower() not in output.lower(): + got = output.splitlines()[0] if output else "(silent)" + return False, f"marker wants {want.text!r}\n {got}" + return True, "" + # The marker pins where and roughly what, so a rule can be moved or reworded + # only deliberately. + first = output.split("\n", 1)[0] if output else "" + at = re.search(r":(\d+):\d+: error:", first) + if not at: + return False, f"no diagnostic to match marker\n got: {first or '(silent)'}" + if int(at.group(1)) != want.line: + return False, f"marker says line {want.line}, diagnostic is line {at.group(1)}\n {first}" + if want.text and want.text.lower() not in output.lower(): + return False, f"marker wants {want.text!r}\n {first}" + return True, "" + + +def main() -> int: + ap = argparse.ArgumentParser(description="run the front-end fixtures") + ap.add_argument("-k", dest="select", help="only fixtures whose path contains this") + ap.add_argument("-v", dest="verbose", action="store_true") + args = ap.parse_args() + + fec = build(ROOT / ".build") + cases = sorted(p for p in FIXTURES.rglob("*.fe") if QUARANTINE not in p.parts) + if args.select: + cases = [p for p in cases if args.select in p.as_posix()] + + failed = [] + for path in cases: + ok, why = run_case(fec, path) + rel = path.relative_to(FIXTURES).as_posix() + if ok: + if args.verbose: + print(f" ok {rel}") + else: + failed.append((rel, why)) + for rel, why in failed: + print(f"FAIL {rel}: {why}") + marked = sum(1 for p in cases if expectation(p).line is not None) + print(f"\n{len(cases) - len(failed)}/{len(cases)} passed " + f"({marked} pin a line and message)") + return 1 if failed else 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/README.md b/tools/README.md deleted file mode 100644 index 97b141e..0000000 --- a/tools/README.md +++ /dev/null @@ -1,55 +0,0 @@ -# Development tools - -## Host support - -The automated DOS development environment currently supports Windows 10/11. -The host only needs `uv`. The setup command downloads the pinned DOSBox-X and -Open Watcom DOS archives, verifies their SHA-256 hashes, and installs them in the -ignored `.dosboxx/` cache. - -```powershell -uv run ferro-dos setup --accept-watcom-license -``` - -Review the Open Watcom license referenced by -`tools/toolchains/dosboxx.lock.json` before accepting it. Neither downloaded -archives nor installed tools are committed. - -## General DOS environment - -`ferro-dos` provides the development entry points: - -```powershell -uv run ferro-dos build -uv run ferro-dos exec "FEC.EXE --check TESTS\M6\OKLAST.FE" -uv run ferro-dos batch fec\test-dos.bat -uv run ferro-dos shell -uv run ferro-dos --help -``` - -Every invocation creates an isolated host directory under `.dosboxx/runs/` and -mounts it as writable `C:`. The repository is mounted read-only as `R:` and the -pinned Open Watcom installation as read-only `W:`. Current compiler sources, -the standard library, and fixtures are copied to `C:\FEC`; all compilation and -execution happen there inside DOSBox-X. Successful runs are removed by default. -Use `--keep` to retain a workspace and `--show-dos` to display the DOS window. - -This directory-backed layout deliberately has no QEMU, disk-image, TCP-agent, -or OCR dependency. A future disk-image backend can be added without changing -the command interface. - -## Pytest regression suite - -`ferro-test` uses the same isolated DOSBox-X/Open Watcom environment, builds -`FEC.EXE` once, and executes all selected cases sequentially in that one DOS -instance. Pytest still reports each registered case separately. - -```powershell -uv run ferro-test run --through m6 -v -uv run ferro-test run --only m6 --dos-log -uv run ferro-test --help -``` - -`--keep-failed` preserves a failed workspace, `--dos-log` prints captured DOS -output, `--trace-dos` disables per-command redirection, and `--show-dos` displays -the GUI. Working rules and DOS/Open Watcom build traps are in `AGENTS.md`. diff --git a/tools/tests/test_dos_names.py b/tools/tests/test_dos_names.py deleted file mode 100644 index 2daf1d5..0000000 --- a/tools/tests/test_dos_names.py +++ /dev/null @@ -1,72 +0,0 @@ -"""Host-side checks for constraints the DOS toolchain enforces far too late. - -Everything here runs without DOSBox-X. The point is to fail in a tenth of a -second with the offending name, instead of after a DOSBox-X boot and ten -successful object builds -- and with a message that says what is actually wrong. -A 9-character source name reaches the DOS build as ``Unable to open "src\\x.c"``, -which reads as a missing file rather than a name that cannot be represented. -""" -from __future__ import annotations - -from pathlib import Path - -import pytest - -from ferrolang_vm.paths import ROOT -from ferrolang_vm.registry import CASES - -# The runner copies these onto a FAT filesystem, where a name is at most eight -# characters plus a three-character extension. -COPIED_TREES = ("fec/src", "fec/std", "fec/tests") - - -def _offenders(root: Path) -> list[str]: - bad = [] - for path in sorted(root.rglob("*")): - name = path.name - if name.startswith("."): - continue - stem, _, suffix = name.rpartition(".") if "." in name else (name, "", "") - if len(stem) > 8 or len(suffix) > 3: - bad.append(f"{path.relative_to(ROOT).as_posix()} (stem {len(stem)}, ext {len(suffix)})") - return bad - - -@pytest.mark.parametrize("tree", COPIED_TREES) -def test_copied_files_fit_dos_8_3(tree: str) -> None: - root = ROOT / tree - if not root.is_dir(): - pytest.skip(f"{tree} is absent") - bad = _offenders(root) - assert not bad, ( - f"{len(bad)} name(s) under {tree} cannot be represented on the DOS side.\n" - "The DOS build will report them as missing files, not as long names:\n " - + "\n ".join(bad) - ) - - -def test_registry_paths_exist_on_the_host() -> None: - """Every ``.FE`` a case names must exist, matched case-insensitively. - - DOS is case-insensitive, so a registry typo survives until the command runs - inside the VM and fails with a message about the wrong thing. - """ - available = { - path.relative_to(ROOT / "fec").as_posix().upper() - for path in (ROOT / "fec").rglob("*.fe") - } - available |= { - path.relative_to(ROOT / "fec").as_posix().upper() - for path in (ROOT / "fec").rglob("*.FE") - } - missing = [] - for case in CASES: - for token in case.command.split(): - if not token.upper().endswith(".FE"): - continue - wanted = token.replace("\\", "/").upper() - if wanted.startswith("STD/"): - wanted = f"STD/{wanted[4:]}" - if not any(entry.endswith(wanted) for entry in available): - missing.append(f"{case.id}: {token}") - assert not missing, "registry names fixtures that do not exist:\n " + "\n ".join(missing) diff --git a/tools/tests/test_host_syntax.py b/tools/tests/test_host_syntax.py deleted file mode 100644 index 69a3e2b..0000000 --- a/tools/tests/test_host_syntax.py +++ /dev/null @@ -1,113 +0,0 @@ -"""Compile the compiler's own sources on the host, as a syntax gate. - -This is not verification. AGENTS.md is explicit that a host compiler's result -does not count, and it still does not: the DOS build and the milestone suite -decide whether anything works. What this buys is the turnaround. A missing -declaration or a signature that disagrees with its definition used to surface -only after a DOSBox-X boot and a full compiler build; here it surfaces in about -a second, with the line number. - -It runs the pinned toolchain's Windows-hosted 16-bit driver with the exact -command ``fec/build-dos.bat`` uses, so the diagnostics match what the DOS build -sees. A 32-bit compile is not equivalent: it misses warnings that only the -16-bit large model reports, which is how a dead function survived the M7 -unification with a clean 32-bit check. - -What it still cannot see is the DOS environment itself -- memory limits, the -command line length, the filesystem. The DOS build and the milestone suite -remain the gate. - -It uses the pinned toolchain only. There is no environment override and no -skip: a system-wide Watcom is a different version reporting different things, -and a gate that quietly skips is not a gate. -""" -from __future__ import annotations - -import os -import re -import subprocess -from pathlib import Path - -import pytest - -from ferrolang_vm.paths import ROOT - -SRC = ROOT / "fec" / "src" -# Mirrors the compile order in fec/build-dos.bat. -SOURCES = ("arena", "diag", "lexer", "ast", "parser", "types", "m7", "own", - "check", "lower", "emit_c", "driver") - - -@pytest.fixture(scope="session") -def watcom() -> Path: - """The pinned toolchain, and nothing else. - - Deliberately no environment override and no skip. A system-wide Open Watcom - is a different version with different diagnostics, and a gate that skips is - a gate that is not running -- which is the failure mode this file exists to - close. dosboxx.py fails the same way when the toolchain is missing. - """ - base = ROOT / ".dosboxx" / "watcom" - if not (base / "binnt" / "wcl.exe").is_file(): - raise AssertionError( - f"the pinned Open Watcom is not at {base}; " - "run `uv run ferro-dos setup --accept-watcom-license`") - return base - - -@pytest.fixture(scope="session") -def objdir(tmp_path_factory: pytest.TempPathFactory) -> Path: - return tmp_path_factory.mktemp("wcc") - - -@pytest.mark.parametrize("name", SOURCES) -def test_source_compiles_clean(name: str, watcom: Path, objdir: Path) -> None: - source = SRC / f"{name}.c" - if not source.is_file(): - pytest.fail(f"{source} is missing but build-dos.bat compiles it") - env = dict(os.environ) - env["WATCOM"] = os.fspath(watcom) - env["INCLUDE"] = os.fspath(watcom / "h") - env["PATH"] = os.pathsep.join( - [os.fspath(watcom / "binnt"), env.get("PATH", "")]) - # The same command build-dos.bat runs, minus the object name. - completed = subprocess.run( - [os.fspath(watcom / "binnt" / "wcl.exe"), "-q", "-za", "-wx", - "-bt=dos", "-ml", "-k32768", "-c", f"-i={SRC}", os.fspath(source)], - cwd=objdir, capture_output=True, text=True, env=env, timeout=120, - ) - output = (completed.stdout + completed.stderr).strip() - # -wx keeps warnings meaningful, so treat any diagnostic as a failure. The - # DOS build prints them to a screen nobody reads, which is how they - # accumulate unnoticed. - assert completed.returncode == 0 and not output, ( - f"{name}.c does not compile clean\n{output}" - ) - - -def test_build_scripts_agree_on_sources() -> None: - """The two build files and this test must name the same translation units. - - They drifted apart while the M7 wrapper existed, which is how a source could - stop being compiled without anyone noticing. - """ - batch = (ROOT / "fec" / "build-dos.bat").read_text(encoding="utf-8", errors="replace") - makefile = (ROOT / "fec" / "Makefile").read_text(encoding="utf-8", errors="replace") - in_batch = set(re.findall(r"src\\(\w+)\.c", batch)) - srcline = next(line for line in makefile.splitlines() if line.startswith("SRC =")) - in_make = set(re.findall(r"src/(\w+)\.c", srcline)) - assert in_batch == set(SOURCES), f"build-dos.bat compiles {sorted(in_batch)}" - assert in_make == set(SOURCES), f"Makefile compiles {sorted(in_make)}" - - -def test_no_source_is_orphaned() -> None: - """Every .c under fec/src must be compiled by something. - - check_m7.c and emitcm7.c hid check.c and emit_c.c from the build by - including them textually; nothing flagged that they had stopped being - translation units of their own. - """ - on_disk = {p.stem for p in SRC.glob("*.c")} - assert on_disk == set(SOURCES), ( - f"fec/src has {sorted(on_disk - set(SOURCES))} that no build step compiles" - ) diff --git a/tools/tests/test_milestones_dosboxx.py b/tools/tests/test_milestones_dosboxx.py deleted file mode 100644 index 8b8ae3f..0000000 --- a/tools/tests/test_milestones_dosboxx.py +++ /dev/null @@ -1,55 +0,0 @@ -from __future__ import annotations - -import os -import warnings - -import pytest - -from ferrolang_vm.dosboxx import SuiteRun, run_suite -from ferrolang_vm.registry import MAX_MILESTONE, all_cases, milestone_number -from ferrolang_vm.suite import Case - -ONLY = os.environ.get("FERRO_TEST_ONLY") -CASES = all_cases( - through=milestone_number(os.environ.get("FERRO_TEST_THROUGH", f"m{MAX_MILESTONE}")), - only=milestone_number(ONLY) if ONLY else None, -) - - -@pytest.fixture(scope="session") -def suite_run() -> SuiteRun: - run = run_suite( - CASES, - keep=os.environ.get("FERRO_TEST_KEEP_FAILED") == "1", - show_dos=os.environ.get("FERRO_TEST_SHOW_DOS") == "1", - trace_dos=os.environ.get("FERRO_TEST_TRACE_DOS") == "1", - ) - yield run - if os.environ.get("FERRO_TEST_DOS_LOG") == "1": - console = run.root / "CONSOLE.LOG" - if console.is_file(): - print(console.read_text(encoding="utf-8", errors="replace")) - run.cleanup() - - -def test_compiler_build(suite_run: SuiteRun) -> None: - assert suite_run.result() == "PASS", suite_run.log() - - -@pytest.mark.parametrize("case", CASES, ids=lambda case: case.id) -def test_milestone_case(case: Case, suite_run: SuiteRun) -> None: - if suite_run.result() != "PASS": - pytest.skip("compiler build failed") - result = suite_run.result(case) - err = suite_run.err(case) - if result == "PASS" and err: - warning_lines = [line for line in err.splitlines() if "warning" in line.lower()] - if warning_lines: - warnings.warn("\n".join(warning_lines), stacklevel=1) - code = suite_run.rc(case) - assert result == "PASS", ( - f"DOS command: {case.command}\n" - f"Expected success: {case.expect_success}\n" - f"Exit code: {'not recorded' if code is None else code}\n" - f"{suite_run.log(case)}\n{err}" - ) diff --git a/tools/toolchains/dosboxx.lock.json b/tools/toolchains/dosboxx.lock.json deleted file mode 100644 index 08b1123..0000000 --- a/tools/toolchains/dosboxx.lock.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "schema": 1, - "dosboxx": { - "version": "2026.08.02", - "url": "https://github.com/joncampbell123/dosbox-x/releases/download/dosbox-x-v2026.08.02/dosbox-x-vsbuild-win64-2026.08.02-portable.zip", - "sha256": "ca28208f5fee25a74caf3a02cc0189c7f7943a42ce37c02848f5fc03450a96cf", - "executable": "bin/x64/Release/dosbox-x.exe" - }, - "open_watcom": { - "version": "2026-08-01-Build", - "url": "https://github.com/open-watcom/open-watcom-v2/releases/download/2026-08-01-Build/open-watcom-2_0-c-dos.exe", - "sha256": "80db4ab340f382e59bf3d396280576ec837964c2ef00e8ddd3b2b3724ab63edf", - "required": [ - "binw/wcl.exe", - "binw/wcl386.exe", - "binp/wlink.exe", - "h/stdio.h", - "lib286/dos/clibl.lib", - "lib386/dos/clib3r.lib", - "license.txt" - ] - } -}