From 8e6a4096371e4dc96b40b622e5e0a438b5fe2714 Mon Sep 17 00:00:00 2001 From: Sebastian Jeong Date: Sun, 16 Aug 2026 08:49:50 +0900 Subject: [PATCH] feat: implement M3 aggregate types and iteration --- fec/build-dos.bat | 14 +- fec/src/ast.c | 4 +- fec/src/ast.h | 5 +- fec/src/check.c | 366 ++++++++++++++++++++++++++++- fec/src/check.h | 4 +- fec/src/driver.c | 8 +- fec/src/emit_c.c | 450 +++++++++++++++++++++++++++++++++-- fec/src/emit_c.h | 5 +- fec/src/parser.c | 116 +++++++-- fec/src/parser.h | 1 + fec/src/types.c | 495 ++++++++++++++++++++++++++++++++++++--- fec/src/types.h | 56 ++++- fec/test-dos.bat | 96 ++++++++ fec/tests/m3/array.fe | 9 + fec/tests/m3/arrayctx.fe | 7 + fec/tests/m3/badarr.fe | 5 + fec/tests/m3/badchar.fe | 6 + fec/tests/m3/badcycle.fe | 6 + fec/tests/m3/badfield.fe | 8 + fec/tests/m3/badfld.fe | 6 + fec/tests/m3/badindex.fe | 7 + fec/tests/m3/badmat.fe | 6 + fec/tests/m3/badstr.fe | 6 + fec/tests/m3/bounds.fe | 6 + fec/tests/m3/char.fe | 9 + fec/tests/m3/enum.fe | 16 ++ fec/tests/m3/for.fe | 18 ++ fec/tests/m3/nested.fe | 10 + fec/tests/m3/str.fe | 7 + fec/tests/m3/struct.fe | 14 ++ fec/tests/run-tests.sh | 21 ++ fec/vm-m1.bat | 36 +++ 32 files changed, 1729 insertions(+), 94 deletions(-) create mode 100644 fec/tests/m3/array.fe create mode 100644 fec/tests/m3/arrayctx.fe create mode 100644 fec/tests/m3/badarr.fe create mode 100644 fec/tests/m3/badchar.fe create mode 100644 fec/tests/m3/badcycle.fe create mode 100644 fec/tests/m3/badfield.fe create mode 100644 fec/tests/m3/badfld.fe create mode 100644 fec/tests/m3/badindex.fe create mode 100644 fec/tests/m3/badmat.fe create mode 100644 fec/tests/m3/badstr.fe create mode 100644 fec/tests/m3/bounds.fe create mode 100644 fec/tests/m3/char.fe create mode 100644 fec/tests/m3/enum.fe create mode 100644 fec/tests/m3/for.fe create mode 100644 fec/tests/m3/nested.fe create mode 100644 fec/tests/m3/str.fe create mode 100644 fec/tests/m3/struct.fe diff --git a/fec/build-dos.bat b/fec/build-dos.bat index 42a1f5e..38300c2 100644 --- a/fec/build-dos.bat +++ b/fec/build-dos.bat @@ -6,16 +6,10 @@ 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 arena.obj del arena.obj -if exist diag.obj del diag.obj -if exist lexer.obj del lexer.obj -if exist ast.obj del ast.obj -if exist parser.obj del parser.obj -if exist types.obj del types.obj -if exist check.obj del check.obj -if exist emitc.obj del emitc.obj -if exist emit_c.obj del emit_c.obj -if exist driver.obj del driver.obj +rem WCL writes test objects into the current directory. Remove all prior build +rem artifacts before the wildcard link so 32-bit test objects cannot enter the +rem 16-bit compiler executable. +if exist *.obj del *.obj if "%WATCOM%"=="" set WATCOM=C:\DEVEL\WATCOMC if not exist %WATCOM%\BINW\WCL.EXE goto build_fail diff --git a/fec/src/ast.c b/fec/src/ast.c index 798c4ed..63fdff7 100644 --- a/fec/src/ast.c +++ b/fec/src/ast.c @@ -8,7 +8,7 @@ FeNode *fe_node(FeAst *a, FeNodeKind k, FeLoc loc, const char *text, unsigned lo FeNode *n=(FeNode *)fe_arena_alloc(&a->arena,sizeof(FeNode)); if (!n) return 0; n->kind=k; n->loc=loc; n->text=text?fe_arena_strdup(&a->arena,text,len):0; - n->a=n->b=n->c=n->children=n->next=0; n->cname=0; n->sem_type=0; return n; + n->a=n->b=n->c=n->children=n->next=0; n->cname=0; n->aux_text=0; n->aux_cname=0; n->sem_type=0; n->flags=0; return n; } void fe_node_add(FeNode *parent, FeNode *child) { @@ -33,7 +33,7 @@ void fe_ast_dump(const FeNode *n, int indent, FILE *out) } const char *fe_node_name(FeNodeKind k) { - static const char *names[] = {"unit","import","fn","struct","enum","error","const","global","field","param","variant","block","let","var","expr-stmt","assign","if","while","for","match","arm","return","break","continue","defer","unsafe","asm","type","expr","binary","unary","call","index","member","literal","ident","struct-init","error"}; + static const char *names[] = {"unit","import","fn","struct","enum","error","const","global","field","param","variant","block","let","var","expr-stmt","assign","if","while","for","match","arm","return","break","continue","defer","unsafe","asm","type","expr","binary","unary","call","index","member","literal","ident","struct-init","array-init","error"}; if ((unsigned)k >= sizeof(names)/sizeof(names[0])) return "node"; return names[k]; } diff --git a/fec/src/ast.h b/fec/src/ast.h index 535d4ce..33140e4 100644 --- a/fec/src/ast.h +++ b/fec/src/ast.h @@ -11,7 +11,7 @@ typedef enum FeNodeKind { FE_N_EXPR_STMT, FE_N_ASSIGN, FE_N_IF, FE_N_WHILE, FE_N_FOR, FE_N_MATCH, FE_N_ARM, FE_N_RETURN, FE_N_BREAK, FE_N_CONTINUE, FE_N_DEFER, FE_N_UNSAFE, FE_N_ASM, FE_N_TYPE, FE_N_EXPR, FE_N_BINARY, FE_N_UNARY, FE_N_CALL, FE_N_INDEX, FE_N_MEMBER, - FE_N_LITERAL, FE_N_IDENT, FE_N_STRUCT_INIT, FE_N_ERROR_NODE + FE_N_LITERAL, FE_N_IDENT, FE_N_STRUCT_INIT, FE_N_ARRAY_INIT, FE_N_ERROR_NODE } FeNodeKind; typedef struct FeNode FeNode; @@ -27,7 +27,10 @@ struct FeNode { FeNode *next; /* Semantic information filled by checking; kept out of AST dumps. */ char *cname; + char *aux_text; + char *aux_cname; FeType *sem_type; + unsigned flags; }; typedef struct FeAst { diff --git a/fec/src/check.c b/fec/src/check.c index 536c149..7714945 100644 --- a/fec/src/check.c +++ b/fec/src/check.c @@ -45,6 +45,25 @@ static int known(FeType *t) static int compatible(FeType *want, FeType *got, FeNode *value) { + FeNode *item; + unsigned long count; + if (want && got && value && value->kind==FE_N_ARRAY_INIT && + want->kind==FE_TYPE_ARRAY && got->kind==FE_TYPE_ARRAY) { + if (want->length != got->length) return 0; + count=0; + for (item=value->children; item; item=item->next) { + if (!compatible(want->elem,item->sem_type,item)) return 0; + if (item->kind==FE_N_LITERAL && item->text && + fe_type_is_integer(want->elem) && + fe_type_is_integer(item->sem_type) && + item->text[0]!='\'' && item->text[0]!='"') + item->sem_type=want->elem; + ++count; + } + if (count!=want->length) return 0; + value->sem_type=want; + return 1; + } if (fe_type_equal(want, got)) return 1; if (!known(want) || !known(got)) return 1; return fe_type_is_integer(want) && fe_type_is_integer(got) && value && @@ -52,6 +71,13 @@ static int compatible(FeType *want, FeType *got, FeNode *value) value->text[0] != '\'' && value->text[0] != '"'; } +static int explicit_castable(FeType *a, FeType *b) +{ + if (!a || !b) return 0; + return (fe_type_is_integer(a) || a->kind == FE_TYPE_CHAR) && + (fe_type_is_integer(b) || b->kind == FE_TYPE_CHAR); +} + static FeType *node_type(FeCheck *c, FeNode *n) { FeType *t; @@ -174,22 +200,114 @@ static FeSym *add_symbol(FeCheckerState *s, FeScope *scope, } void fe_check_init(FeCheck *c, FeAst *ast, FeDiags *diags, - unsigned pointer_bits) + unsigned pointer_bits, int no_checks) { c->ast = ast; c->diags = diags; c->pointer_bits = pointer_bits; c->local_serial = 0; + c->no_checks = no_checks; fe_types_init(&c->types, &ast->arena, pointer_bits); + c->types.unit_name = ast->root && ast->root->text ? ast->root->text : "unit"; } static FeType *check_expr(FeCheckerState *s, FeNode *n); +static void check_match(FeCheckerState *s, FeNode *n); +static void check_stmt(FeCheckerState *s, FeNode *n); + +static int lvalue_writable(FeCheckerState *s, FeNode *n) +{ + FeSym *sym; + FeType *t; + if (!n) return 0; + if (n->kind == FE_N_IDENT) { + sym=find_symbol(s->scope,n->text ? n->text : ""); + return sym ? sym->mutable : 0; + } + if (n->kind == FE_N_MEMBER) { + t=n->a ? n->a->sem_type : 0; + if (t && t->kind==FE_TYPE_REF && n->b && n->b->text && + strcmp(n->b->text,"^")==0) return t->ref_mut; + return lvalue_writable(s,n->a); + } + if (n->kind == FE_N_INDEX) return lvalue_writable(s,n->a); + return 0; +} + +static int has_field(FeNode *list, const char *name) +{ + FeNode *f; + for (f=list; f; f=f->next) + if (f->text && name && strcmp(f->text,name)==0) return 1; + return 0; +} + +static FeType *check_struct_init(FeCheckerState *s, FeNode *n) +{ + FeType *t; + FeFieldType *field; + FeNode *f; + FeType *v; + FeType *et; + FeVariantType *variant; + unsigned i; + if (n->a && n->a->kind == FE_N_MEMBER) { + et=check_expr(s,n->a->a); + variant=et && et->kind==FE_TYPE_ENUM ? + fe_type_variant(et,n->a->b ? n->a->b->text : "") : 0; + if (!variant) { err(s->c,n->loc,"invalid enum variant"); return unknown(s->c); } + if (variant->field_count != 0) { + for (f=n->children; f; f=f->next) { + if (f->kind != FE_N_FIELD) continue; + field=0; + if (variant->fields) { + unsigned i; + for(i=0;ifield_count;i++) if(strcmp(variant->fields[i].name,f->text)==0) field=&variant->fields[i]; + } + if (!field) { err(s->c,f->loc,"invalid enum payload field"); continue; } + v=check_expr(s,f->a); + if (!compatible(field->type,v,f->a) && v->kind!=FE_TYPE_UNKNOWN) err(s->c,f->loc,"enum payload type mismatch"); + } + } else if (n->children) err(s->c,n->loc,"empty enum variant cannot have payload"); + n->sem_type=et; return et; + } + t=fe_type_intern(&s->c->types,n->text ? n->text : ""); + if (!t || t->kind!=FE_TYPE_STRUCT) { err(s->c,n->loc,"unknown struct type"); return unknown(s->c); } + for(f=n->children;f;f=f->next) if(f->kind==FE_N_FIELD) { + if(has_field(f->next,f->text)) { err(s->c,f->loc,"duplicate struct field"); } + field=fe_type_field(t,f->text); + if(!field) { err(s->c,f->loc,"invalid struct field"); continue; } + v=check_expr(s,f->a); + if(!compatible(field->type,v,f->a) && v->kind!=FE_TYPE_UNKNOWN) err(s->c,f->loc,"struct field type mismatch"); + } + for(i=0;ifield_count;i++) if(!has_field(n->children,t->fields[i].name)) err(s->c,n->loc,"missing struct field"); + n->sem_type=t; return t; +} + +static FeType *check_array_init(FeCheckerState *s, FeNode *n) +{ + FeNode *x; FeType *elem=0; FeType *v; unsigned long count=0; + for(x=n->children;x;x=x->next) { v=check_expr(s,x); if(!elem) elem=v; else if(!compatible(elem,v,x)&&v->kind!=FE_TYPE_UNKNOWN) err(s->c,x->loc,"array element type mismatch"); ++count; } + if(!elem) elem=unknown(s->c); + n->sem_type=fe_type_array(&s->c->types,count,elem); return n->sem_type; +} + +static FeType *check_index(FeCheckerState *s, FeNode *n) +{ + FeType *base=check_expr(s,n->a); FeType *idx; FeType *elem; + if(!fe_type_is_indexable(base)) { err(s->c,n->loc,"indexing requires an array or slice"); return unknown(s->c); } + if(n->b) { idx=check_expr(s,n->b); if(known(idx)&&!fe_type_is_integer(idx)) err(s->c,n->loc,"index must be an integer"); } + if(n->c || !n->b) { if(n->c) { idx=check_expr(s,n->c); if(known(idx)&&!fe_type_is_integer(idx)) err(s->c,n->loc,"slice bound must be an integer"); } elem=base->elem; n->sem_type=fe_type_slice(&s->c->types,elem); if(base->kind==FE_TYPE_STR) n->flags|=2U; return n->sem_type; } + n->sem_type=base->elem; return n->sem_type; +} static FeType *check_identifier(FeCheckerState *s, FeNode *n, int read) { FeSym *sym; sym = find_symbol(s->scope, n->text ? n->text : ""); if (!sym) { + FeType *named=fe_type_intern(&s->c->types,n->text ? n->text : ""); + if(named->kind==FE_TYPE_STRUCT || named->kind==FE_TYPE_ENUM) { n->sem_type=named; return named; } err(s->c, n->loc, "unknown name"); return unknown(s->c); } @@ -209,6 +327,9 @@ static FeType *check_expr(FeCheckerState *s, FeNode *n) FeNode *x; FeNode *param; FeNode *arg; + FeType *et; + FeFieldType *field; + FeVariantType *variant; const char *op; if (!n) return unknown(c); if (n->kind == FE_N_IDENT) @@ -218,14 +339,18 @@ static FeType *check_expr(FeCheckerState *s, FeNode *n) if (strcmp(n->text, "true") == 0 || strcmp(n->text, "false") == 0) a = fe_type_intern(&c->types, "bool"); else if (n->text[0] == '\'') - a = fe_type_intern(&c->types, "u8"); + a = fe_type_intern(&c->types, "char"); else if (n->text[0] == '"') - a = unknown(c); + a = fe_type_intern(&c->types, "str"); else a = fe_type_intern(&c->types, "i32"); n->sem_type = a; return a; } + if (n->kind == FE_N_STRUCT_INIT) return check_struct_init(s,n); + if (n->kind == FE_N_ARRAY_INIT) return check_array_init(s,n); + if (n->kind == FE_N_INDEX) return check_index(s,n); + if (n->kind == FE_N_MATCH) { check_match(s,n); n->sem_type=unknown(c); return n->sem_type; } if (n->kind == FE_N_UNARY) { a = check_expr(s, n->a); op = n->text ? n->text : ""; @@ -245,9 +370,8 @@ static FeType *check_expr(FeCheckerState *s, FeNode *n) b = node_type(c, n->b); if (b->kind == FE_TYPE_VOID) err(c, n->loc, "cast target cannot be void"); - else if ((known(a) && !fe_type_is_integer(a)) || - (known(b) && !fe_type_is_integer(b))) - err(c, n->loc, "'as' requires integer types"); + else if (known(a) && known(b) && !explicit_castable(a,b)) + err(c, n->loc, "'as' requires integer or char types"); n->sem_type = b; return b; } @@ -279,6 +403,24 @@ static FeType *check_expr(FeCheckerState *s, FeNode *n) return a; } if (n->kind == FE_N_CALL) { + if (!n->a && n->text && (strcmp(n->text,"@size_of")==0 || strcmp(n->text,"@align_of")==0)) { + FeNode *type_arg=n->children; + FeType *target=type_arg && type_arg->kind==FE_N_IDENT ? fe_type_intern(&c->types,type_arg->text) : unknown(c); + if(!target || !known(target)) err(c,n->loc,"size/align requires a known type"); + n->sem_type=fe_type_intern(&c->types,"usize"); return n->sem_type; + } + if (n->a && n->a->kind == FE_N_MEMBER) { + et=check_expr(s,n->a->a); + variant=et && et->kind==FE_TYPE_ENUM ? + fe_type_variant(et,n->a->b ? n->a->b->text : "") : 0; + arg=n->children; + if (!variant) { err(c,n->loc,"invalid enum variant constructor"); return unknown(c); } + if (variant->field_count==1 && arg) { + FeType *av=check_expr(s,arg); + if(!compatible(variant->fields[0].type,av,arg)&&av->kind!=FE_TYPE_UNKNOWN) err(c,arg->loc,"enum payload type mismatch"); + } else if (variant->field_count != 0 || arg) err(c,n->loc,"wrong enum payload arity"); + n->sem_type=et; return et; + } if (n->a && n->a->kind == FE_N_IDENT) { sym = find_symbol(s->scope, n->a->text ? n->a->text : ""); if (!sym) { @@ -310,7 +452,25 @@ static FeType *check_expr(FeCheckerState *s, FeNode *n) return unknown(c); } if (n->kind == FE_N_MEMBER) { - check_expr(s, n->a); + a=check_expr(s,n->a); + if (a->kind == FE_TYPE_REF && n->b && n->b->text && + strcmp(n->b->text,"^")==0) { + n->sem_type=a->elem; + return a->elem; + } + if(a->kind==FE_TYPE_STRUCT) { + field=fe_type_field(a,n->b ? n->b->text : ""); + if(!field) { err(c,n->loc,"unknown struct field"); return unknown(c); } + n->sem_type=field->type; return field->type; + } + if(a->kind==FE_TYPE_ENUM) { + if(!fe_type_variant(a,n->b ? n->b->text : "")) err(c,n->loc,"unknown enum variant"); + n->sem_type=a; return a; + } + if((a->kind==FE_TYPE_SLICE || a->kind==FE_TYPE_STR) && n->b && + strcmp(n->b->text,"n")==0) { + n->sem_type=fe_type_intern(&c->types,"usize"); return n->sem_type; + } return unknown(c); } return unknown(c); @@ -319,6 +479,8 @@ static FeType *check_expr(FeCheckerState *s, FeNode *n) static FeType *check_lvalue(FeCheckerState *s, FeNode *n, int read) { FeSym *sym; + FeType *base; + FeFieldType *field; if (n && n->kind == FE_N_IDENT) { sym = find_symbol(s->scope, n->text ? n->text : ""); if (!sym) { @@ -337,6 +499,32 @@ static FeType *check_lvalue(FeCheckerState *s, FeNode *n, int read) err(s->c, n->loc, "use of uninitialized variable"); return sym->type; } + if (n && n->kind == FE_N_MEMBER) { + base=check_expr(s,n->a); + if (base && base->kind == FE_TYPE_REF && n->b && n->b->text && + strcmp(n->b->text,"^")==0) { + if (!base->ref_mut) + err(s->c,n->loc,"cannot write through shared reference"); + n->sem_type=base->elem; + return base->elem; + } + if (!lvalue_writable(s,n->a)) + err(s->c,n->loc,"cannot assign through immutable value"); + field=base && base->kind==FE_TYPE_STRUCT ? fe_type_field(base,n->b ? n->b->text : "") : 0; + if(!field) { err(s->c,n->loc,"assignment requires a valid struct field"); return unknown(s->c); } + n->sem_type=field->type; return field->type; + } + if (n && n->kind == FE_N_INDEX) { + if (n->a && n->a->sem_type && n->a->sem_type->kind == FE_TYPE_STR) { + err(s->c, n->loc, "str is immutable"); + } + if (n->a && n->a->kind == FE_N_INDEX && (n->a->flags & 2U)) + err(s->c, n->loc, "str slice is immutable"); + if (!lvalue_writable(s,n->a)) + err(s->c,n->loc,"cannot assign through immutable value"); + base=check_index(s,n); + return base; + } if (n) err(s->c, n->loc, "assignment requires a variable"); return unknown(s->c); } @@ -346,6 +534,153 @@ static int compound_operator(const char *op) return op && strcmp(op, "=") != 0; } +static void check_match(FeCheckerState *s, FeNode *n) +{ + FeType *value; + FeNode *arm; + FeVariantType *variant; + int seen[256]; + int wildcard=0; + unsigned i; + for(i=0;i<256U;i++) seen[i]=0; + value=check_expr(s,n->a); + if(!value || value->kind!=FE_TYPE_ENUM) { err(s->c,n->loc,"match requires an enum value"); return; } + for(arm=n->children;arm;arm=arm->next) { + FeScope *old=s->scope; + if(arm->text && strcmp(arm->text,"_")==0) wildcard=1; + else { + variant=fe_type_variant(value,arm->text); + if(!variant) { err(s->c,arm->loc,"unknown match variant"); continue; } + if(variant->tag<256U) { + if(seen[variant->tag]) err(s->c,arm->loc,"duplicate match variant"); + seen[variant->tag]=1; + } + s->scope=scope_new(s,old); + if(variant->field_count==1 && arm->children) { + add_symbol(s,s->scope,arm->children->text,variant->fields[0].type,0,0,1, + local_cname(s->c,arm->children->text),arm->children); + } else if(variant->field_count>0) { + FeNode *b=arm->children; + for(i=0;ifield_count && b;i++,b=b->next) { + FeFieldType *f=&variant->fields[i]; + add_symbol(s,s->scope,b->text,f->type,0,0,1, + local_cname(s->c,b->text),b); + } + } + } + if(arm->a && arm->a->kind==FE_N_BLOCK) check_stmt(s,arm->a); + else if(arm->a) check_expr(s,arm->a); + s->scope=old; + } + if(!wildcard) for(i=0;ivariant_count && i<256U;i++) if(!seen[i]) err(s->c,n->loc,"non-exhaustive match"); +} + +static void check_for(FeCheckerState *s, FeNode *n) +{ + FeType *start; + FeType *finish; + FeType *elem; + FeType *ref_type; + FeSym *iter_sym; + char *index_cname; + char *item_cname; + int iter_mut; + FeScope *old=s->scope; + if(!n->c) { + start=check_expr(s,n->a); + if (!fe_type_is_indexable(start)) { + err(s->c,n->loc,"for iterable must be an array, slice, or str"); + return; + } + elem=start->elem; + iter_sym=0; + if (n->a && n->a->kind==FE_N_IDENT) + iter_sym=find_symbol(s->scope,n->a->text ? n->a->text : ""); + else if (n->a && n->a->kind==FE_N_INDEX && n->a->a && + n->a->a->kind==FE_N_IDENT) + iter_sym=find_symbol(s->scope,n->a->a->text ? n->a->a->text : ""); + iter_mut=iter_sym && iter_sym->mutable; + if (start->kind==FE_TYPE_STR) iter_mut=0; + ref_type=fe_type_ref(&s->c->types,elem,iter_mut); + if (iter_mut) n->flags |= 4U; + s->scope=scope_new(s,old); + if (n->aux_text) { + index_cname=local_cname(s->c,n->text ? n->text : "index"); + item_cname=local_cname(s->c,n->aux_text); + add_symbol(s,s->scope,n->text,fe_type_intern(&s->c->types,"usize"),0,0,1, + index_cname,n); + add_symbol(s,s->scope,n->aux_text,ref_type,0,iter_mut,1, + item_cname,0); + n->cname=index_cname; + n->aux_cname=item_cname; + } else { + item_cname=local_cname(s->c,n->text ? n->text : "item"); + add_symbol(s,s->scope,n->text,ref_type,0,iter_mut,1, + item_cname,n); + n->cname=item_cname; + } + check_stmt(s,n->b); + s->scope=old; + return; + } + start=check_expr(s,n->a); + finish=check_expr(s,n->c); + if(known(start)&&!fe_type_is_integer(start)) err(s->c,n->loc,"range start must be integer"); + if(known(finish)&&!fe_type_is_integer(finish)) err(s->c,n->loc,"range end must be integer"); + s->scope=scope_new(s,old); + index_cname=local_cname(s->c,n->text ? n->text : "index"); + add_symbol(s,s->scope,n->text,fe_type_intern(&s->c->types,"usize"),0,0,1, + index_cname,n); + n->cname=index_cname; + check_stmt(s,n->b); + s->scope=old; +} + +static void check_type_cycle(FeCheck *c, FeType *t) +{ + unsigned i; + FeType *next; + if (!t || t->kind == FE_TYPE_SLICE || t->kind == FE_TYPE_STR || + t->kind == FE_TYPE_REF || + t->kind == FE_TYPE_INT || t->kind == FE_TYPE_BOOL || + t->kind == FE_TYPE_CHAR || t->kind == FE_TYPE_VOID || + t->kind == FE_TYPE_UNKNOWN || t->kind == FE_TYPE_ERROR) return; + if (t->cycle_state == 1) { + if (c->ast->root) err(c, c->ast->root->loc, "by-value recursive type"); + return; + } + if (t->cycle_state == 2) return; + t->cycle_state = 1; + if (t->kind == FE_TYPE_ARRAY) { + check_type_cycle(c,t->elem); + } else if (t->kind == FE_TYPE_STRUCT) { + for (i=0;ifield_count;i++) { + if (!t->fields[i].type && t->fields[i].ast_node) + t->fields[i].type=fe_type_from_ast(&c->types,t->fields[i].ast_node->a); + check_type_cycle(c,t->fields[i].type); + } + } else if (t->kind == FE_TYPE_ENUM) { + for (i=0;ivariant_count;i++) { + unsigned j; + for (j=0;jvariants[i].field_count;j++) { + if (!t->variants[i].fields[j].type && t->variants[i].fields[j].ast_node) + t->variants[i].fields[j].type=fe_type_from_ast(&c->types, + t->variants[i].fields[j].ast_node->a); + next=t->variants[i].fields[j].type; + check_type_cycle(c,next); + } + } + } + t->cycle_state=2; +} + +static void check_type_cycles(FeCheck *c) +{ + FeType *t; + for (t=c->types.types;t;t=t->next) t->cycle_state=0; + for (t=c->types.types;t;t=t->next) check_type_cycle(c,t); +} + static void check_stmt(FeCheckerState *s, FeNode *n) { FeCheck *c = s->c; @@ -418,12 +753,19 @@ static void check_stmt(FeCheckerState *s, FeNode *n) err(c, n->loc, "while condition must be bool"); check_stmt(s, n->b); break; + case FE_N_FOR: + check_for(s,n); + break; + case FE_N_MATCH: + check_match(s,n); + break; case FE_N_RETURN: b = n->a ? check_expr(s, n->a) : fe_type_intern(&c->types, "void"); if (known(b) && b->kind == FE_TYPE_VOID && s->ret->kind != FE_TYPE_VOID) err(c, n->loc, "void expression returned from value function"); else if (known(s->ret) && known(b) && !fe_type_equal(s->ret, b) && - b->kind != FE_TYPE_UNKNOWN) + b->kind != FE_TYPE_UNKNOWN && + !compatible(s->ret,b,n->a)) err(c, n->loc, "return type mismatch"); break; case FE_N_UNSAFE: @@ -468,6 +810,13 @@ int fe_check_program(FeCheck *c) s.scope = scope_new(&s, 0); s.globals = s.scope; s.ret = fe_type_intern(&c->types, "void"); + for (n = c->ast->root ? c->ast->root->children : 0; n; n = n->next) + if (n->kind == FE_N_STRUCT) + fe_type_declare_struct(&c->types, n, (n->flags & 1U) != 0); + for (n = c->ast->root ? c->ast->root->children : 0; n; n = n->next) + if (n->kind == FE_N_ENUM) fe_type_declare_enum(&c->types, n); + check_type_cycles(c); + fe_type_layout_all(&c->types); for (n = c->ast->root ? c->ast->root->children : 0; n; n = n->next) { if (n->kind == FE_N_GLOBAL || n->kind == FE_N_CONST) { t = n->a ? node_type(c, n->a) : unknown(c); @@ -501,6 +850,7 @@ int fe_check_program(FeCheck *c) } for (n = c->ast->root ? c->ast->root->children : 0; n; n = n->next) if (n->kind == FE_N_FN) check_fn(c, n, s.globals); + fe_type_layout_all(&c->types); return c->diags->errors == 0; } diff --git a/fec/src/check.h b/fec/src/check.h index b30cb3c..b26388a 100644 --- a/fec/src/check.h +++ b/fec/src/check.h @@ -10,9 +10,11 @@ typedef struct FeCheck { FeDiags *diags; unsigned pointer_bits; unsigned local_serial; + int no_checks; } FeCheck; -void fe_check_init(FeCheck *c, FeAst *ast, FeDiags *diags, unsigned pointer_bits); +void fe_check_init(FeCheck *c, FeAst *ast, FeDiags *diags, + unsigned pointer_bits, int no_checks); int fe_check_program(FeCheck *c); FeType *fe_check_expr_type(FeCheck *c, FeNode *n); diff --git a/fec/src/driver.c b/fec/src/driver.c index ad6d117..bfaa518 100644 --- a/fec/src/driver.c +++ b/fec/src/driver.c @@ -17,15 +17,15 @@ static void usage(void) { puts("usage: fec [--dump-ast|--emit-c] file.fe [--target=bits16|bits32] [-o output.c]"); } int main(int argc, char **argv) { - int i,dump=0,emit=0; const char *file=0,*outname=0; unsigned long n; char *src; FeDiags d; FeAst ast; FeParser p; FeCheck check; FeEmitter emitter; FILE *out; unsigned pointer_bits=32; + int i,dump=0,emit=0,no_checks=0; const char *file=0,*outname=0; unsigned long n; char *src; FeDiags d; FeAst ast; FeParser p; FeCheck check; FeEmitter emitter; FILE *out; unsigned pointer_bits=32; (void)emit; if(argc<2){usage();return 2;} - for(i=1;i=argc){fprintf(stderr,"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(strncmp(argv[i],"--target=",9)==0 || strncmp(argv[i],"--model=",8)==0 || strcmp(argv[i],"--no-checks")==0 || strcmp(argv[i],"--strip-error-names")==0) { } else if(argv[i][0]!='-') file=argv[i]; else if(strcmp(argv[i],"--help")==0){usage();return 0;} else {fprintf(stderr,"fec: unknown option %s\n",argv[i]);return 2;} } + for(i=1;i=argc){fprintf(stderr,"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; else if(strncmp(argv[i],"--target=",9)==0 || strncmp(argv[i],"--model=",8)==0 || strcmp(argv[i],"--strip-error-names")==0) { } else if(argv[i][0]!='-') file=argv[i]; else if(strcmp(argv[i],"--help")==0){usage();return 0;} else {fprintf(stderr,"fec: unknown option %s\n",argv[i]);return 2;} } if(!file){fprintf(stderr,"fec: no input file\n");return 2;} src=read_file(file,&n);if(!src)return 2;d.errors=0;d.warnings=0;fe_ast_init(&ast);fe_parser_init(&p,&ast,src,n,file,&d);ast.root=fe_parse_unit(&p); if(dump) { fe_ast_dump(ast.root,0,stdout); fe_ast_destroy(&ast); free(src); return d.errors?1:0; } - fe_check_init(&check,&ast,&d,pointer_bits); if(!fe_check_program(&check)){fe_ast_destroy(&ast);free(src);return 1;} + fe_check_init(&check,&ast,&d,pointer_bits,no_checks); if(!fe_check_program(&check)){fe_ast_destroy(&ast);free(src);return 1;} out=outname?fopen(outname,"w"):stdout; if(!out){fprintf(stderr,"fec: cannot create %s\n",outname);fe_ast_destroy(&ast);free(src);return 2;} - fe_emit_c_init(&emitter,out,&check,pointer_bits);fe_emit_c_program(&emitter);if(outname)fclose(out); + fe_emit_c_init(&emitter,out,&check,pointer_bits,no_checks);fe_emit_c_program(&emitter);if(outname)fclose(out); 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 index e696aea..04dd5f5 100644 --- a/fec/src/emit_c.c +++ b/fec/src/emit_c.c @@ -22,9 +22,305 @@ 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); +} + +static void emit_one_type(FeEmitter *e, FeType *t) +{ + unsigned i,j; + 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)) 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); 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 *p, unsigned long n) { %s s; s.p=p; s.n=n; return s; }\n",t->cname,t->maker,fe_type_c_name(t->elem,e->pointer_bits),t->cname); + } 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 void emit_type_defs(FeEmitter *e) +{ + FeType *t; + fputs("typedef struct { const unsigned char *p; unsigned long n; } fe_str;\n",e->out); + fputs("static fe_str fe_make_str(const unsigned char *p, unsigned long n) { fe_str s; s.p=p; s.n=n; return s; }\n",e->out); + /* Every fixed array has a slice conversion helper, even if this unit + only indexes the array. Intern those result types before emission so + their typedefs are present before helper definitions. */ + 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) emit_one_type(e,t); +} + +static void emit_type_helpers(FeEmitter *e) +{ + FeType *t; + unsigned i,j; + for(t=e->check->types.types;t;t=t->next) { + if(t->kind==FE_TYPE_STRUCT && t->maker) { + fprintf(e->out,"static %s %s(",t->cname,t->maker); + for(i=0;ifield_count;i++) { if(i) fputs(", ",e->out); fputs(fe_type_c_name(t->fields[i].type,e->pointer_bits),e->out); fprintf(e->out," p%u",i); } + fputs(") { ",e->out); fprintf(e->out,"%s v;",t->cname); + 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(",t->cname,t->maker); + for(i=0;ilength;i++) { if(i) fputs(", ",e->out); fputs(fe_type_c_name(t->elem,e->pointer_bits),e->out); fprintf(e->out," p%u",i); } + fputs(") { ",e->out); fprintf(e->out,"%s v;",t->cname); + 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) { + for(i=0;ivariant_count;i++) { + FeVariantType *v=&t->variants[i]; + fprintf(e->out,"static %s %s(",t->cname,v->maker); + for(j=0;jfield_count;j++) { if(j) fputs(", ",e->out); fputs(fe_type_c_name(v->fields[j].type,e->pointer_bits),e->out); fprintf(e->out," p%u",j); } + fputs(") { ",e->out); fprintf(e->out,"%s x; x.tag=%u;",t->cname,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); + } + } + } + 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) { ", + fe_type_c_name(t->elem,e->pointer_bits),t->indexer,t->cname); + if(!e->no_checks) fprintf(e->out,"if (i >= %lu) fe_trap_bounds(); ",t->length); + fprintf(e->out,"return x.a[i]; }\n"); + fprintf(e->out,"static %s %s(%s x, unsigned long a, unsigned long b) { ", + fe_type_c_name(fe_type_slice(&e->check->types,t->elem),e->pointer_bits),t->slicer,t->cname); + if(!e->no_checks) fputs("if (a > b || b > ",e->out), fprintf(e->out,"%lu",t->length), fputs(") fe_trap_bounds(); ",e->out); + fprintf(e->out,"return %s(x.a+a,b-a); }\n",fe_type_slice(&e->check->types,t->elem)->maker); + fprintf(e->out,"static %s %s(%s x) { return %s(x,0,%lu); }\n",fe_type_c_name(fe_type_slice(&e->check->types,t->elem),e->pointer_bits),t->full_slicer,t->cname,t->slicer,t->length); + fprintf(e->out,"static %s %s(%s x, unsigned long a) { return %s(x,a,%lu); }\n",fe_type_c_name(fe_type_slice(&e->check->types,t->elem),e->pointer_bits),t->tail_slicer,t->cname,t->slicer,t->length); + } else if (t->kind==FE_TYPE_SLICE && t->indexer) { + fprintf(e->out,"static %s %s(%s x, unsigned long i) { ", + fe_type_c_name(t->elem,e->pointer_bits),t->indexer,t->cname); + if(!e->no_checks) fputs("if (i >= x.n) fe_trap_bounds(); ",e->out); + fputs("return x.p[i]; }\n",e->out); + fprintf(e->out,"static %s %s(%s x, unsigned long a, unsigned long b) { ", + fe_type_c_name(t,e->pointer_bits),t->slicer,t->cname); + 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",t->cname,t->full_slicer,t->cname,t->slicer); + fprintf(e->out,"static %s %s(%s x, unsigned long a) { return %s(x,a,x.n); }\n",t->cname,t->tail_slicer,t->cname,t->slicer); + } + } + fputs("static unsigned char fe_idx_str(fe_str x, unsigned long i) { ",e->out); + if(!e->no_checks) fputs("if (i >= x.n) fe_trap_bounds(); ",e->out); + fputs("return x.p[i]; }\n",e->out); + fputs("static fe_str fe_slice_str(fe_str x, unsigned long a, unsigned long b) { ",e->out); + if(!e->no_checks) fputs("if (a > b || b > x.n) fe_trap_bounds(); ",e->out); + fputs("return fe_make_str(x.p+a,b-a); }\n",e->out); + fputs("static fe_str fe_full_slice_str(fe_str x) { return fe_slice_str(x,0,x.n); }\n",e->out); + fputs("static fe_str fe_tail_slice_str(fe_str x, unsigned long a) { return fe_slice_str(x,a,x.n); }\n",e->out); +} + static void emit_expr(FeEmitter *e, FeNode *n); static void emit_stmt(FeEmitter *e, FeNode *n); +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_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; } + if (n->kind==FE_N_MEMBER) { + 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 { emit_lvalue(e,n->a); fputc('.',e->out); fputs(n->b ? 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; + } + emit_expr(e,n); +} + +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=bt && bt->slicer ? bt->slicer : "fe_slice_str"; + if (!n->b && !n->c && bt && bt->full_slicer) { + fputs(bt->full_slicer,e->out); 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); 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); 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(FeEmitter *e, FeNode *n) { FeNode *x; @@ -40,8 +336,43 @@ static void emit_expr(FeEmitter *e, FeNode *n) 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("fe_make_str((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, "not") == 0) fputs("(!", e->out); @@ -71,21 +402,35 @@ static void emit_expr(FeEmitter *e, FeNode *n) fputc(')', e->out); } else emit_expr(e, n->a); break; - case FE_N_CALL: - if (n->a) emit_expr(e, n->a); + case FE_N_CALL: { + FeVariantType *v; + int special=0; + 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); - fputc('(', e->out); - for (x = n->children; x; x = x->next) { - if (x != n->children) fputs(", ", e->out); - emit_expr(e, x); + if(!special) { + fputc('(', e->out); + for (x = n->children; x; x = x->next) { + if (x != n->children) fputs(", ", e->out); + emit_expr(e, x); + } + fputc(')', e->out); } - fputc(')', e->out); break; - case FE_N_MEMBER: - emit_expr(e, n->a); - fputc('.', e->out); - if (n->b) fputs(n->b->text ? n->b->text : "member", e->out); + } + case FE_N_MEMBER: { + FeVariantType *v; + 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_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 { 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; @@ -125,6 +470,38 @@ static void emit_block(FeEmitter *e, FeNode *n) fputc('}', e->out); } +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 void emit_stmt(FeEmitter *e, FeNode *n) { if (!n) return; @@ -145,7 +522,7 @@ static void emit_stmt(FeEmitter *e, FeNode *n) break; case FE_N_ASSIGN: pad(e); - emit_expr(e, n->a); + emit_lvalue(e, n->a); fputc(' ', e->out); fputs(n->text ? n->text : "=", e->out); fputs(" ", e->out); @@ -159,6 +536,10 @@ static void emit_stmt(FeEmitter *e, FeNode *n) break; case FE_N_RETURN: pad(e); + if (n->a && n->a->kind == FE_N_MATCH) { + emit_match(e,n->a,1); + break; + } fputs("return", e->out); if (n->a) { fputc(' ', e->out); @@ -189,6 +570,38 @@ static void emit_stmt(FeEmitter *e, FeNode *n) else emit_block(e, 0); fputc('\n', e->out); break; + case FE_N_FOR: + pad(e); fputs("{\n",e->out); ++e->indent; + 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; pad(e); fputs("}\n",e->out); break; + case FE_N_MATCH: + emit_match(e,n,0); break; default: break; } @@ -237,19 +650,28 @@ static void emit_main_wrapper(FeEmitter *e, FeNode *fn) } void fe_emit_c_init(FeEmitter *e, FILE *out, FeCheck *check, - unsigned pointer_bits) + 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; } void fe_emit_c_program(FeEmitter *e) { FeNode *n; FeNode *main_fn = 0; - fputs("/* generated by fec M2 */\n#include \n\n", e->out); + fputs("/* generated by fec M3 */\n#include \n#include \ntypedef 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(); }\n\n", e->out); + emit_type_defs(e); + 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) { diff --git a/fec/src/emit_c.h b/fec/src/emit_c.h index 0d2aa29..b9f12ab 100644 --- a/fec/src/emit_c.h +++ b/fec/src/emit_c.h @@ -8,9 +8,12 @@ typedef struct FeEmitter { FeCheck *check; unsigned pointer_bits; int indent; + int no_checks; + unsigned temp_serial; } FeEmitter; -void fe_emit_c_init(FeEmitter *e, FILE *out, FeCheck *check, unsigned pointer_bits); +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/parser.c b/fec/src/parser.c index 434befa..f5abc90 100644 --- a/fec/src/parser.c +++ b/fec/src/parser.c @@ -11,13 +11,14 @@ static int want(FeParser *p, FeTokKind k, const char *what) { if(eat(p,k)) return 1; error(p,what); return 0; } static int is_name(FeParser *p) { return is(p,FE_TOK_IDENT)||is(p,FE_TOK_SELF)||is(p,FE_TOK_SELFTYPE); } static FeNode *expr(FeParser *p, int minprec); +static FeNode *delimited_expr(FeParser *p); static FeNode *type(FeParser *p); static FeNode *statement(FeParser *p); static FeNode *block(FeParser *p); void fe_parser_init(FeParser *p, FeAst *ast, const char *src, unsigned long length, const char *file, FeDiags *d) { - p->ast=ast; p->diags=d; fe_lexer_init(&p->lexer,src,length,file,d); + p->ast=ast; p->diags=d; p->forbid_struct_literal=0; fe_lexer_init(&p->lexer,src,length,file,d); p->previous=p->current=fe_lexer_next(&p->lexer); } @@ -81,10 +82,19 @@ static int precedence(FeTokKind k) static FeNode *primary(FeParser *p) { FeToken t=p->current; FeNode *n; + if (is(p,FE_TOK_LBRACKET)) { + FeNode *a=toknode(p,FE_N_ARRAY_INIT,t); next(p); + while(!is(p,FE_TOK_RBRACKET)&&!is(p,FE_TOK_EOF)) { + fe_node_add(a,delimited_expr(p)); + if(!eat(p,FE_TOK_COMMA)) break; + } + want(p,FE_TOK_RBRACKET,"expected ']' after array literal"); + return a; + } if(is(p,FE_TOK_INT)||is(p,FE_TOK_CHAR)||is(p,FE_TOK_STRING)||is(p,FE_TOK_TRUE)||is(p,FE_TOK_FALSE)||is(p,FE_TOK_NULL)||is(p,FE_TOK_UNDEFINED)) {next(p);return toknode(p,FE_N_LITERAL,t);} if(is_name(p) || is(p,FE_TOK_ERROR_KW)) { next(p); n=toknode(p,FE_N_IDENT,t); - if(is(p,FE_TOK_LBRACE)) { + if(is(p,FE_TOK_LBRACE) && !p->forbid_struct_literal) { FeNode *s=toknode(p,FE_N_STRUCT_INIT,t); next(p); while(!is(p,FE_TOK_RBRACE)&&!is(p,FE_TOK_EOF)) { FeNode *f; if(!is_name(p)){error(p,"expected field name");recover(p);break;} f=toknode(p,FE_N_FIELD,p->current);next(p);want(p,FE_TOK_COLON,"expected ':' after field");f->a=expr(p,0);fe_node_add(s,f);if(!eat(p,FE_TOK_COMMA))break; @@ -92,11 +102,11 @@ static FeNode *primary(FeParser *p) } return n; } - if(eat(p,FE_TOK_LPAREN)) { n=expr(p,0); want(p,FE_TOK_RPAREN,"expected ')'"); return n; } + if(eat(p,FE_TOK_LPAREN)) { int old=p->forbid_struct_literal; p->forbid_struct_literal=0; n=expr(p,0); p->forbid_struct_literal=old; want(p,FE_TOK_RPAREN,"expected ')'"); return n; } if(eat(p,FE_TOK_AT)) { FeToken name=p->current; if(!is_name(p)){error(p,"expected builtin name after '@'");return fe_node(p->ast,FE_N_ERROR_NODE,t.loc,"builtin",7);} next(p); n=toknode(p,FE_N_CALL,name); n->text=fe_arena_strdup(&p->ast->arena,name.begin-1,name.length+1); - if(eat(p,FE_TOK_LPAREN)){while(!is(p,FE_TOK_RPAREN)&&!is(p,FE_TOK_EOF)){fe_node_add(n,expr(p,0));if(!eat(p,FE_TOK_COMMA))break;}want(p,FE_TOK_RPAREN,"expected ')' after builtin");} + if(eat(p,FE_TOK_LPAREN)){while(!is(p,FE_TOK_RPAREN)&&!is(p,FE_TOK_EOF)){fe_node_add(n,delimited_expr(p));if(!eat(p,FE_TOK_COMMA))break;}want(p,FE_TOK_RPAREN,"expected ')' after builtin");} return n; } error(p,"expected expression"); next(p); return fe_node(p->ast,FE_N_ERROR_NODE,t.loc,"expression",10); @@ -106,9 +116,29 @@ static FeNode *postfix(FeParser *p) FeNode *n=primary(p); for(;;) { FeToken t=p->current; FeNode *m; - if(eat(p,FE_TOK_LPAREN)) { m=toknode(p,FE_N_CALL,t); m->a=n; while(!is(p,FE_TOK_RPAREN)&&!is(p,FE_TOK_EOF)){fe_node_add(m,expr(p,0));if(!eat(p,FE_TOK_COMMA))break;} want(p,FE_TOK_RPAREN,"expected ')' after call"); n=m; } - else if(eat(p,FE_TOK_LBRACKET)) { m=toknode(p,FE_N_INDEX,t);m->a=n;m->b=expr(p,0);if(eat(p,FE_TOK_DOTDOT)){m->c=expr(p,0);}want(p,FE_TOK_RBRACKET,"expected ']' after index");n=m; } - else if(eat(p,FE_TOK_DOT)) { m=toknode(p,FE_N_MEMBER,t);m->a=n;if(is_name(p)){m->b=toknode(p,FE_N_IDENT,p->current);next(p);}else if(eat(p,FE_TOK_QUESTION)){m->text=fe_arena_strdup(&p->ast->arena,".?",2);}else error(p,"expected member name");n=m; } + if(eat(p,FE_TOK_LPAREN)) { m=toknode(p,FE_N_CALL,t); m->a=n; while(!is(p,FE_TOK_RPAREN)&&!is(p,FE_TOK_EOF)){fe_node_add(m,delimited_expr(p));if(!eat(p,FE_TOK_COMMA))break;} want(p,FE_TOK_RPAREN,"expected ')' after call"); n=m; } + else if(eat(p,FE_TOK_LBRACKET)) { + m=toknode(p,FE_N_INDEX,t);m->a=n; + if(is(p,FE_TOK_DOTDOT)) m->b=0; else m->b=delimited_expr(p); + if(eat(p,FE_TOK_DOTDOT)) { if(!is(p,FE_TOK_RBRACKET)) m->c=delimited_expr(p); } + want(p,FE_TOK_RBRACKET,"expected ']' after index");n=m; + } + else if(eat(p,FE_TOK_DOT)) { + m=toknode(p,FE_N_MEMBER,t);m->a=n; + if(is_name(p)){m->b=toknode(p,FE_N_IDENT,p->current);next(p);} + else if(eat(p,FE_TOK_QUESTION)){m->text=fe_arena_strdup(&p->ast->arena,".?",2);} + else if(eat(p,FE_TOK_XOR)){m->text=fe_arena_strdup(&p->ast->arena,".^",2);m->b=fe_node(p->ast,FE_N_IDENT,p->previous.loc,"^",1);} + else error(p,"expected member name"); + n=m; + if(is(p,FE_TOK_LBRACE) && !p->forbid_struct_literal) { + FeNode *s=toknode(p,FE_N_STRUCT_INIT,t); s->a=n; next(p); + while(!is(p,FE_TOK_RBRACE)&&!is(p,FE_TOK_EOF)) { FeNode *f; + if(!is_name(p)){error(p,"expected variant field");recover(p);break;} + f=toknode(p,FE_N_FIELD,p->current);next(p);want(p,FE_TOK_COLON,"expected ':' after variant field");f->a=expr(p,0);fe_node_add(s,f);if(!eat(p,FE_TOK_COMMA))break; + } + want(p,FE_TOK_RBRACE,"expected '}' in variant constructor");n=s; + } + } else if(eat(p,FE_TOK_AS)) { m=toknode(p,FE_N_TYPE,t);m->a=n;m->b=type(p);n=m; } else break; } @@ -123,6 +153,29 @@ static FeNode *expr(FeParser *p, int minprec) return left; } +/* A control-flow header is followed by a body '{'. Do not let that body + brace be consumed as the postfix struct-literal brace; callers can use + parentheses when a struct literal is intended in the header. */ +static FeNode *header_expr(FeParser *p) +{ + FeNode *n; + int old=p->forbid_struct_literal; + p->forbid_struct_literal=1; + n=expr(p,0); + p->forbid_struct_literal=old; + return n; +} + +static FeNode *delimited_expr(FeParser *p) +{ + FeNode *n; + int old=p->forbid_struct_literal; + p->forbid_struct_literal=0; + n=expr(p,0); + p->forbid_struct_literal=old; + return n; +} + static FeNode *params(FeParser *p) { FeNode *list=fe_node(p->ast,FE_N_BLOCK,p->current.loc,"params",6); @@ -147,7 +200,7 @@ static FeNode *field(FeParser *p) } static FeNode *decl(FeParser *p) { - int pub=0, external=0, interrupt=0, interrupt_safe=0, shared=0, atomic=0; FeToken t=p->current; FeNode *n; + int pub=0, external=0, interrupt=0, interrupt_safe=0, shared=0, atomic=0; FeToken t=p->current; FeNode *n; FeTokKind before; (void)shared; (void)atomic; if(eat(p,FE_TOK_PUB)) pub=1; if(eat(p,FE_TOK_EXTERN)) { external=1; if(is(p,FE_TOK_STRING)) next(p); } @@ -156,12 +209,14 @@ static FeNode *decl(FeParser *p) if(!is(p,FE_TOK_PACKED)) t=p->current; if(is(p,FE_TOK_FN)) return fn_decl(p,pub,external,interrupt,interrupt_safe); if(eat(p,FE_TOK_PACKED)) t=p->previous; - if(eat(p,FE_TOK_STRUCT)) { n=toknode(p,FE_N_STRUCT,t);if(!is_name(p)){error(p,"expected struct name");return n;}next(p);n->text=fe_arena_strdup(&p->ast->arena,p->previous.begin,p->previous.length);if(eat(p,FE_TOK_LPAREN)){while(!is(p,FE_TOK_RPAREN)&&!is(p,FE_TOK_EOF)){fe_node_add(n,type(p));if(!eat(p,FE_TOK_COMMA))break;}want(p,FE_TOK_RPAREN,"expected ')' after generic parameters");}want(p,FE_TOK_LBRACE,"expected '{' in struct");while(!is(p,FE_TOK_RBRACE)&&!is(p,FE_TOK_EOF)){if(is(p,FE_TOK_PUB))next(p);if(is(p,FE_TOK_FN))fe_node_add(n,fn_decl(p,0,0,0,0));else fe_node_add(n,field(p));}want(p,FE_TOK_RBRACE,"expected '}' after struct");return n; } + if(eat(p,FE_TOK_STRUCT)) { n=toknode(p,FE_N_STRUCT,t);if(t.kind==FE_TOK_PACKED)n->flags|=1U;if(!is_name(p)){error(p,"expected struct name");return n;}next(p);n->text=fe_arena_strdup(&p->ast->arena,p->previous.begin,p->previous.length);if(eat(p,FE_TOK_LPAREN)){while(!is(p,FE_TOK_RPAREN)&&!is(p,FE_TOK_EOF)){fe_node_add(n,type(p));if(!eat(p,FE_TOK_COMMA))break;}want(p,FE_TOK_RPAREN,"expected ')' after generic parameters");}want(p,FE_TOK_LBRACE,"expected '{' in struct");while(!is(p,FE_TOK_RBRACE)&&!is(p,FE_TOK_EOF)){if(is(p,FE_TOK_PUB))next(p);if(is(p,FE_TOK_FN))fe_node_add(n,fn_decl(p,0,0,0,0));else fe_node_add(n,field(p));}want(p,FE_TOK_RBRACE,"expected '}' after struct");return n; } if(eat(p,FE_TOK_ENUM)) { n=toknode(p,FE_N_ENUM,t);if(is_name(p)){next(p);n->text=fe_arena_strdup(&p->ast->arena,p->previous.begin,p->previous.length);}else error(p,"expected enum name");want(p,FE_TOK_LBRACE,"expected '{' in enum");while(!is(p,FE_TOK_RBRACE)&&!is(p,FE_TOK_EOF)){FeNode *v=toknode(p,FE_N_VARIANT,p->current);if(is_name(p))next(p);else{error(p,"expected variant name");recover(p);break;}if(eat(p,FE_TOK_LPAREN)){v->a=type(p);want(p,FE_TOK_RPAREN,"expected ')' in variant");}else if(eat(p,FE_TOK_LBRACE)){while(!is(p,FE_TOK_RBRACE)&&!is(p,FE_TOK_EOF))fe_node_add(v,field(p));want(p,FE_TOK_RBRACE,"expected '}' in variant");}fe_node_add(n,v);if(!eat(p,FE_TOK_COMMA))break;}want(p,FE_TOK_RBRACE,"expected '}' after enum");return n; } if(eat(p,FE_TOK_ERROR_KW)) { n=toknode(p,FE_N_ERROR_DECL,t);if(is_name(p)){next(p);n->text=fe_arena_strdup(&p->ast->arena,p->previous.begin,p->previous.length);}else error(p,"expected error name");want(p,FE_TOK_LBRACE,"expected '{' in error declaration");while(!is(p,FE_TOK_RBRACE)&&!is(p,FE_TOK_EOF)){FeNode *v=toknode(p,FE_N_VARIANT,p->current);if(is_name(p))next(p);else{error(p,"expected error member");recover(p);break;}want(p,FE_TOK_EQ,"expected '=' in error member");v->a=expr(p,0);want(p,FE_TOK_COMMA,"expected ',' in error declaration");fe_node_add(n,v);}want(p,FE_TOK_RBRACE,"expected '}' after error");return n; } if(eat(p,FE_TOK_SHARED)) { shared=1; if(eat(p,FE_TOK_ATOMIC)) atomic=1; if(!is(p,FE_TOK_VAR)) error(p,"expected 'var' after shared"); } if(is(p,FE_TOK_CONST)||is(p,FE_TOK_STATIC)||is(p,FE_TOK_VAR)) { FeTokKind kk=p->current.kind;next(p);n=toknode(p,kk==FE_TOK_CONST?FE_N_CONST:FE_N_GLOBAL,t);if(is_name(p)){next(p);n->text=fe_arena_strdup(&p->ast->arena,p->previous.begin,p->previous.length);}else error(p,"expected declaration name");if(eat(p,FE_TOK_COLON))n->a=type(p);want(p,FE_TOK_EQ,"expected '=' in declaration");n->b=expr(p,0);want(p,FE_TOK_SEMI,"expected ';' after declaration");return n; } - error(p,"expected declaration"); recover(p); return 0; + error(p,"expected declaration"); before=p->current.kind; recover(p); + if (p->current.kind==before && p->current.kind!=FE_TOK_EOF) next(p); + return 0; } static FeNode *block(FeParser *p) @@ -175,11 +230,42 @@ static FeNode *statement(FeParser *p) if(eat(p,FE_TOK_LET)) { n=toknode(p,FE_N_LET,t);if(is_name(p)){next(p);n->text=fe_arena_strdup(&p->ast->arena,p->previous.begin,p->previous.length);}else error(p,"expected variable name");if(eat(p,FE_TOK_COLON))n->a=type(p);want(p,FE_TOK_EQ,"expected '=' in let");n->b=expr(p,0);want(p,FE_TOK_SEMI,"expected ';'");return n; } if(eat(p,FE_TOK_VAR)) { n=toknode(p,FE_N_VAR,t);if(is_name(p)){next(p);n->text=fe_arena_strdup(&p->ast->arena,p->previous.begin,p->previous.length);}else error(p,"expected variable name");if(eat(p,FE_TOK_COLON))n->a=type(p);if(eat(p,FE_TOK_EQ))n->b=expr(p,0);want(p,FE_TOK_SEMI,"expected ';'");return n; } if(eat(p,FE_TOK_CONST)) { n=toknode(p,FE_N_CONST,t);if(is_name(p))next(p);else error(p,"expected constant name");if(eat(p,FE_TOK_COLON))n->a=type(p);want(p,FE_TOK_EQ,"expected '=' in const");n->b=expr(p,0);want(p,FE_TOK_SEMI,"expected ';'");return n; } - if(eat(p,FE_TOK_IF)) { n=toknode(p,FE_N_IF,t);if(eat(p,FE_TOK_LET)){n->text=fe_arena_strdup(&p->ast->arena,"if let",6);if(is_name(p))next(p);if(eat(p,FE_TOK_LPAREN)){if(is_name(p))next(p);want(p,FE_TOK_RPAREN,"expected ')' in if let pattern");}want(p,FE_TOK_EQ,"expected '=' in if let");}n->a=expr(p,0);n->b=block(p);if(eat(p,FE_TOK_ELSE))n->c=is(p,FE_TOK_IF)?statement(p):block(p);return n; } - if(eat(p,FE_TOK_COMPTIME)) { n=toknode(p,FE_N_IF,t);want(p,FE_TOK_IF,"expected 'if' after comptime");n->text=fe_arena_strdup(&p->ast->arena,"comptime if",11);n->a=expr(p,0);n->b=block(p);if(eat(p,FE_TOK_ELSE))n->c=is(p,FE_TOK_IF)?statement(p):block(p);return n; } - if(eat(p,FE_TOK_WHILE)) {n=toknode(p,FE_N_WHILE,t);n->a=expr(p,0);n->b=block(p);return n;} - if(eat(p,FE_TOK_FOR)) {n=toknode(p,FE_N_FOR,t);if(is_name(p))next(p);else error(p,"expected loop variable");if(eat(p,FE_TOK_COMMA)){if(is_name(p))next(p);else error(p,"expected second loop variable");}want(p,FE_TOK_IN,"expected 'in' in for");n->a=expr(p,0);if(eat(p,FE_TOK_DOTDOT))n->c=expr(p,0);n->b=block(p);return n;} - if(eat(p,FE_TOK_MATCH)) { n=toknode(p,FE_N_MATCH,t);n->a=expr(p,0);want(p,FE_TOK_LBRACE,"expected '{' after match expression");while(!is(p,FE_TOK_RBRACE)&&!is(p,FE_TOK_EOF)){FeNode *arm=toknode(p,FE_N_ARM,p->current);if(is_name(p)||is(p,FE_TOK_INT)||is(p,FE_TOK_CHAR)||is(p,FE_TOK_NULL)||is(p,FE_TOK_TRUE)||is(p,FE_TOK_FALSE)||is(p,FE_TOK_IDENT)){arm->text=fe_arena_strdup(&p->ast->arena,p->current.begin,p->current.length);next(p);}else{error(p,"expected match pattern");recover(p);continue;}while(is(p,FE_TOK_LPAREN)||is(p,FE_TOK_LBRACE)){FeTokKind close=is(p,FE_TOK_LPAREN)?FE_TOK_RPAREN:FE_TOK_RBRACE;next(p);while(!is(p,close)&&!is(p,FE_TOK_EOF))next(p);want(p,close,"expected end of match pattern");}want(p,FE_TOK_FATARROW,"expected '=>' in match arm");if(is(p,FE_TOK_LBRACE))arm->a=block(p);else{arm->a=expr(p,0);want(p,FE_TOK_SEMI,"expected ';' in match arm");}fe_node_add(n,arm);}want(p,FE_TOK_RBRACE,"expected '}' after match");return n;} + if(eat(p,FE_TOK_IF)) { n=toknode(p,FE_N_IF,t);if(eat(p,FE_TOK_LET)){n->text=fe_arena_strdup(&p->ast->arena,"if let",6);if(is_name(p))next(p);if(eat(p,FE_TOK_LPAREN)){if(is_name(p))next(p);want(p,FE_TOK_RPAREN,"expected ')' in if let pattern");}want(p,FE_TOK_EQ,"expected '=' in if let");}n->a=header_expr(p);n->b=block(p);if(eat(p,FE_TOK_ELSE))n->c=is(p,FE_TOK_IF)?statement(p):block(p);return n; } + if(eat(p,FE_TOK_COMPTIME)) { n=toknode(p,FE_N_IF,t);want(p,FE_TOK_IF,"expected 'if' after comptime");n->text=fe_arena_strdup(&p->ast->arena,"comptime if",11);n->a=header_expr(p);n->b=block(p);if(eat(p,FE_TOK_ELSE))n->c=is(p,FE_TOK_IF)?statement(p):block(p);return n; } + if(eat(p,FE_TOK_WHILE)) {n=toknode(p,FE_N_WHILE,t);n->a=header_expr(p);n->b=block(p);return n;} + if(eat(p,FE_TOK_FOR)) {n=toknode(p,FE_N_FOR,t);if(is_name(p)){n->text=fe_arena_strdup(&p->ast->arena,p->current.begin,p->current.length);next(p);}else error(p,"expected loop variable");if(eat(p,FE_TOK_COMMA)){if(is_name(p)){n->aux_text=fe_arena_strdup(&p->ast->arena,p->current.begin,p->current.length);next(p);}else error(p,"expected second loop variable");}want(p,FE_TOK_IN,"expected 'in' in for");n->a=header_expr(p);if(eat(p,FE_TOK_DOTDOT))n->c=header_expr(p);n->b=block(p);return n;} + if(eat(p,FE_TOK_MATCH)) { + int old=p->forbid_struct_literal; + n=toknode(p,FE_N_MATCH,t); p->forbid_struct_literal=1; n->a=header_expr(p); p->forbid_struct_literal=old; + want(p,FE_TOK_LBRACE,"expected '{' after match expression"); + while(!is(p,FE_TOK_RBRACE)&&!is(p,FE_TOK_EOF)) { + FeNode *arm=toknode(p,FE_N_ARM,p->current); + FeToken pt=p->current; + if(is_name(p)||is(p,FE_TOK_INT)||is(p,FE_TOK_CHAR)||is(p,FE_TOK_NULL)||is(p,FE_TOK_TRUE)||is(p,FE_TOK_FALSE)) { + arm->text=fe_arena_strdup(&p->ast->arena,pt.begin,pt.length); next(p); + } else { error(p,"expected match pattern"); recover(p); continue; } + if(eat(p,FE_TOK_LPAREN)) { + while(!is(p,FE_TOK_RPAREN)&&!is(p,FE_TOK_EOF)) { + if(is_name(p)) { fe_node_add(arm,toknode(p,FE_N_IDENT,p->current)); next(p); } + else { error(p,"expected pattern binding"); recover(p); break; } + if(!eat(p,FE_TOK_COMMA)) break; + } + want(p,FE_TOK_RPAREN,"expected ')' after match pattern"); + } else if(eat(p,FE_TOK_LBRACE)) { + while(!is(p,FE_TOK_RBRACE)&&!is(p,FE_TOK_EOF)) { + if(is_name(p)) { fe_node_add(arm,toknode(p,FE_N_IDENT,p->current)); next(p); } + else { error(p,"expected field binding"); recover(p); break; } + if(!eat(p,FE_TOK_COMMA)) break; + } + want(p,FE_TOK_RBRACE,"expected '}' after match pattern"); + } + want(p,FE_TOK_FATARROW,"expected '=>' in match arm"); + if(is(p,FE_TOK_LBRACE)) arm->a=block(p); + else { arm->a=expr(p,0); want(p,FE_TOK_SEMI,"expected ';' in match arm"); } + fe_node_add(n,arm); + } + want(p,FE_TOK_RBRACE,"expected '}' after match"); return n; + } if(eat(p,FE_TOK_RETURN)) {n=toknode(p,FE_N_RETURN,t);if(!is(p,FE_TOK_SEMI))n->a=expr(p,0);want(p,FE_TOK_SEMI,"expected ';' after return");return n;} if(eat(p,FE_TOK_BREAK)){n=toknode(p,FE_N_BREAK,t);want(p,FE_TOK_SEMI,"expected ';'");return n;} if(eat(p,FE_TOK_CONTINUE)){n=toknode(p,FE_N_CONTINUE,t);want(p,FE_TOK_SEMI,"expected ';'");return n;} diff --git a/fec/src/parser.h b/fec/src/parser.h index a0d5ef7..8b6f5fe 100644 --- a/fec/src/parser.h +++ b/fec/src/parser.h @@ -9,6 +9,7 @@ typedef struct FeParser { FeToken previous; FeAst *ast; FeDiags *diags; + int forbid_struct_literal; } FeParser; void fe_parser_init(FeParser *p, FeAst *ast, const char *src, unsigned long length, const char *file, FeDiags *d); diff --git a/fec/src/types.c b/fec/src/types.c index 37a17d0..8640ac1 100644 --- a/fec/src/types.c +++ b/fec/src/types.c @@ -1,50 +1,473 @@ #include "types.h" #include +#include +#include + +static FeType *new_type(FeTypeCtx *ctx, const char *name, FeTypeKind kind) +{ + FeType *t; + unsigned i; + t = (FeType *)fe_arena_alloc(ctx->arena, sizeof(FeType)); + if (!t) return 0; + for (i = 0; i + 1U < sizeof(t->name) && name && name[i]; ++i) + t->name[i] = name[i]; + t->name[i] = '\0'; + t->kind = kind; + t->cname = 0; + t->maker = 0; + t->indexer = 0; + t->slicer = 0; + t->full_slicer = 0; + t->tail_slicer = 0; + t->bits = 0; + t->is_unsigned = 0; + t->packed = 0; + t->length = 0; + t->size = 0; + t->align = 1; + t->elem = 0; + t->ref_mut = 0; + t->fields = 0; + t->field_count = 0; + t->variants = 0; + t->variant_count = 0; + t->next = ctx->types; + t->emit_state = 0; + t->cycle_state = 0; + ctx->types = t; + return t; +} void fe_types_init(FeTypeCtx *ctx, FeArena *arena, unsigned pointer_bits) -{ ctx->arena=arena; ctx->types=0; ctx->pointer_bits=pointer_bits; } +{ + ctx->arena = arena; + ctx->types = 0; + ctx->pointer_bits = pointer_bits; + ctx->unit_name = "unit"; + ctx->generated_serial = 0; +} FeType *fe_type_intern(FeTypeCtx *ctx, const char *name) { - FeType *t; unsigned i; unsigned bits=0; int uns=0; FeTypeKind kind=FE_TYPE_UNKNOWN; - if (!name) name=""; - for (t=ctx->types;t;t=t->next) if(strcmp(t->name,name)==0) return t; - if(strcmp(name,"void")==0) kind=FE_TYPE_VOID; - else if(strcmp(name,"bool")==0) kind=FE_TYPE_BOOL; - else if(strcmp(name,"i8")==0||strcmp(name,"u8")==0){kind=FE_TYPE_INT;bits=8;uns=name[0]=='u';} - else if(strcmp(name,"i16")==0||strcmp(name,"u16")==0){kind=FE_TYPE_INT;bits=16;uns=name[0]=='u';} - else if(strcmp(name,"i32")==0||strcmp(name,"u32")==0){kind=FE_TYPE_INT;bits=32;uns=name[0]=='u';} - else if(strcmp(name,"usize")==0||strcmp(name,"isize")==0){kind=FE_TYPE_INT;bits=ctx->pointer_bits;uns=name[0]=='u';} - t=(FeType *)fe_arena_alloc(ctx->arena,sizeof(FeType)); if(!t)return 0; - for(i=0;iname)-1 && name[i];i++) t->name[i]=name[i]; - t->name[i]='\0'; - t->kind=kind;t->bits=bits;t->is_unsigned=uns;t->next=ctx->types;ctx->types=t;return t; + FeType *t; + unsigned bits = 0; + int uns = 0; + FeTypeKind kind = FE_TYPE_UNKNOWN; + if (!name) name = ""; + for (t = ctx->types; t; t = t->next) + if (strcmp(t->name, name) == 0) return t; + if (strcmp(name, "void") == 0) kind = FE_TYPE_VOID; + else if (strcmp(name, "bool") == 0) kind = FE_TYPE_BOOL; + else if (strcmp(name, "char") == 0) kind = FE_TYPE_CHAR; + else if (strcmp(name, "str") == 0) kind = FE_TYPE_STR; + else if (strcmp(name, "i8") == 0 || strcmp(name, "u8") == 0) { + kind = FE_TYPE_INT; bits = 8; uns = name[0] == 'u'; + } else if (strcmp(name, "i16") == 0 || strcmp(name, "u16") == 0) { + kind = FE_TYPE_INT; bits = 16; uns = name[0] == 'u'; + } else if (strcmp(name, "i32") == 0 || strcmp(name, "u32") == 0) { + kind = FE_TYPE_INT; bits = 32; uns = name[0] == 'u'; + } else if (strcmp(name, "usize") == 0 || strcmp(name, "isize") == 0) { + kind = FE_TYPE_INT; bits = ctx->pointer_bits; uns = name[0] == 'u'; + } + t = new_type(ctx, name, kind); + if (!t) return 0; + t->bits = bits; + t->is_unsigned = uns; + if (kind == FE_TYPE_STR) { + t->cname = fe_arena_strdup(ctx->arena, "fe_str", 6); + t->elem = fe_type_intern(ctx, "u8"); + t->indexer = "fe_idx_str"; + t->slicer = "fe_slice_str"; + t->full_slicer = "fe_full_slice_str"; + t->tail_slicer = "fe_tail_slice_str"; + } + return t; +} + +static char *generated_name(FeTypeCtx *ctx, const char *prefix, + const char *name) +{ + char number[24]; + unsigned long n; + char *p; + sprintf(number, "%u", ctx->generated_serial++); + n = (unsigned long)strlen(prefix) + (unsigned long)strlen(name) + + (unsigned long)strlen(number) + 2UL; + p = (char *)fe_arena_alloc(ctx->arena, n); + if (!p) return 0; + strcpy(p, prefix); + strcat(p, name); + strcat(p, "_"); + strcat(p, number); + return p; +} + +FeType *fe_type_array(FeTypeCtx *ctx, unsigned long length, FeType *elem) +{ + char key[96]; + FeType *t; + sprintf(key, "[%lu]%s", length, elem ? elem->name : "?"); + t = fe_type_intern(ctx, key); + if (t->kind == FE_TYPE_UNKNOWN) { + t->kind = FE_TYPE_ARRAY; + t->length = length; + t->elem = elem; + t->cname = generated_name(ctx, "struct fe_arr_", "type"); + t->maker = generated_name(ctx, "fe_make_arr_", "type"); + t->indexer = generated_name(ctx, "fe_idx_arr_", "type"); + t->slicer = generated_name(ctx, "fe_slice_arr_", "type"); + t->full_slicer = generated_name(ctx, "fe_full_arr_", "type"); + t->tail_slicer = generated_name(ctx, "fe_tail_arr_", "type"); + } + return t; +} + +FeType *fe_type_slice(FeTypeCtx *ctx, FeType *elem) +{ + char key[96]; + FeType *t; + sprintf(key, "[]%s", elem ? elem->name : "?"); + t = fe_type_intern(ctx, key); + if (t->kind == FE_TYPE_UNKNOWN) { + t->kind = FE_TYPE_SLICE; + t->elem = elem; + t->cname = generated_name(ctx, "fe_slice_", "type"); + t->maker = generated_name(ctx, "fe_make_slice_", "type"); + t->indexer = generated_name(ctx, "fe_idx_slice_", "type"); + t->slicer = generated_name(ctx, "fe_slice_slice_", "type"); + t->full_slicer = generated_name(ctx, "fe_full_slice_", "type"); + t->tail_slicer = generated_name(ctx, "fe_tail_slice_", "type"); + } + return t; +} + +FeType *fe_type_ref(FeTypeCtx *ctx, FeType *elem, int mutable) +{ + char key[128]; + FeType *t; + sprintf(key,"%s%s",mutable ? "&mut " : "&",elem ? elem->name : "?"); + t=fe_type_intern(ctx,key); + if(t->kind==FE_TYPE_UNKNOWN) { + t->kind=FE_TYPE_REF; + t->elem=elem; + t->ref_mut=mutable; + } + return t; +} + +FeType *fe_type_declare_struct(FeTypeCtx *ctx, const FeNode *node, int packed) +{ + FeType *t; + FeNode *f; + unsigned count = 0; + unsigned i = 0; + char *cname; + if (!node || !node->text) return 0; + t = fe_type_intern(ctx, node->text); + if (t->kind != FE_TYPE_UNKNOWN && t->kind != FE_TYPE_STRUCT) return t; + if (t->kind == FE_TYPE_STRUCT) return t; + t->kind = FE_TYPE_STRUCT; + t->packed = packed; + cname = (char *)fe_arena_alloc(ctx->arena, + (unsigned long)strlen("struct fe_") + strlen(ctx->unit_name) + + strlen(node->text) + 2UL); + if (!cname) return t; + strcpy(cname, "struct fe_"); + strcat(cname, ctx->unit_name); + strcat(cname, "_"); + strcat(cname, node->text); + t->cname = cname; + t->maker = generated_name(ctx, "fe_make_", node->text); + for (f = node->children; f; f = f->next) + if (f->kind == FE_N_FIELD) ++count; + t->field_count = count; + if (count) { + t->fields = (FeFieldType *)fe_arena_alloc(ctx->arena, + count * sizeof(FeFieldType)); + if (!t->fields) return t; + for (f = node->children; f; f = f->next) if (f->kind == FE_N_FIELD) { + t->fields[i].name = f->text; + t->fields[i].type = 0; + t->fields[i].offset = 0; + t->fields[i].ast_node = f; + ++i; + } + } + return t; +} + +FeType *fe_type_declare_enum(FeTypeCtx *ctx, const FeNode *node) +{ + FeType *t; + FeNode *v; + unsigned count = 0; + unsigned i = 0; + char *cname; + if (!node || !node->text) return 0; + t = fe_type_intern(ctx, node->text); + if (t->kind != FE_TYPE_UNKNOWN && t->kind != FE_TYPE_ENUM) return t; + if (t->kind == FE_TYPE_ENUM) return t; + t->kind = FE_TYPE_ENUM; + cname = (char *)fe_arena_alloc(ctx->arena, + (unsigned long)strlen("struct fe_") + strlen(ctx->unit_name) + + strlen(node->text) + 2UL); + if (!cname) return t; + strcpy(cname, "struct fe_"); + strcat(cname, ctx->unit_name); + strcat(cname, "_"); + strcat(cname, node->text); + t->cname = cname; + for (v = node->children; v; v = v->next) ++count; + t->variant_count = count; + if (count) { + t->variants = (FeVariantType *)fe_arena_alloc(ctx->arena, + count * sizeof(FeVariantType)); + if (!t->variants) return t; + for (v = node->children; v; v = v->next) { + t->variants[i].name = v->text; + t->variants[i].fields = 0; + t->variants[i].field_count = 0; + t->variants[i].tag = i; + t->variants[i].ast_node = v; + t->variants[i].maker = generated_name(ctx, "fe_make_variant_", v->text ? v->text : "variant"); + if (v->a && v->a->kind == FE_N_TYPE) { + t->variants[i].field_count = 1; + t->variants[i].fields = (FeFieldType *)fe_arena_alloc(ctx->arena, sizeof(FeFieldType)); + if (t->variants[i].fields) { + t->variants[i].fields[0].name = "value"; + t->variants[i].fields[0].type = fe_type_from_ast(ctx, v->a); + t->variants[i].fields[0].offset = 0; + t->variants[i].fields[0].ast_node = v; + } + } + if (!v->a) { + FeNode *f; + unsigned fc = 0; + unsigned j = 0; + for (f = v->children; f; f = f->next) + if (f->kind == FE_N_FIELD) ++fc; + t->variants[i].field_count = fc; + if (fc) { + t->variants[i].fields = (FeFieldType *)fe_arena_alloc( + ctx->arena, fc * sizeof(FeFieldType)); + if (t->variants[i].fields) for (f = v->children; f; f = f->next) + if (f->kind == FE_N_FIELD) { + t->variants[i].fields[j].name = f->text; + t->variants[i].fields[j].type = 0; + t->variants[i].fields[j].offset = 0; + t->variants[i].fields[j].ast_node = f; + ++j; + } + } + } + ++i; + } + } + return t; +} + +static unsigned long round_up(unsigned long x, unsigned a) +{ + unsigned long rem; + if (a <= 1U) return x; + rem = x % (unsigned long)a; + return rem ? x + (unsigned long)a - rem : x; +} + +unsigned long fe_type_size(const FeType *t) +{ + return t ? t->size : 0; +} + +unsigned fe_type_align(const FeType *t) +{ + return t && t->align ? t->align : 1U; +} + +static void layout_type(FeTypeCtx *ctx, FeType *t) +{ + unsigned i; + unsigned align; + unsigned long off; + unsigned long max_size; + unsigned max_align; + if (!t || t->size) return; + if (t->cycle_state == 1) { + /* The checker reports this as an invalid by-value cycle. Give the + layout walk a sentinel size so error recovery cannot recurse. */ + t->size = 1; + t->align = 1; + return; + } + t->cycle_state = 1; + if (t->kind == FE_TYPE_VOID || t->kind == FE_TYPE_UNKNOWN || + t->kind == FE_TYPE_ERROR) { t->size = 0; t->align = 1; t->cycle_state = 2; return; } + if (t->kind == FE_TYPE_BOOL || t->kind == FE_TYPE_CHAR) { + t->size = 1; t->align = 1; t->cycle_state = 2; return; + } + if (t->kind == FE_TYPE_INT) { + t->size = (t->bits + 7U) / 8U; + t->align = ctx->pointer_bits == 16 ? 1U : t->size; + if (t->size > 4UL) t->size = ctx->pointer_bits == 16 ? 2UL : 4UL; + t->cycle_state = 2; return; + } + if (t->kind == FE_TYPE_REF) { + t->size = ctx->pointer_bits == 16 ? 2UL : 4UL; + t->align = ctx->pointer_bits == 16 ? 1U : 4U; + t->cycle_state = 2; return; + } + if (t->kind == FE_TYPE_SLICE || t->kind == FE_TYPE_STR) { + t->size = ctx->pointer_bits == 16 ? 4UL : 8UL; + t->align = ctx->pointer_bits == 16 ? 1U : 4U; + t->cycle_state = 2; return; + } + if (t->kind == FE_TYPE_ARRAY) { + layout_type(ctx, t->elem); + t->align = t->packed || ctx->pointer_bits == 16 ? 1U : fe_type_align(t->elem); + t->size = t->length * fe_type_size(t->elem); + t->cycle_state = 2; return; + } + if (t->kind == FE_TYPE_STRUCT) { + off = 0; max_align = 1; + for (i = 0; i < t->field_count; ++i) { + if (!t->fields[i].type && t->fields[i].ast_node) + t->fields[i].type = fe_type_from_ast(ctx, t->fields[i].ast_node->a); + layout_type(ctx, t->fields[i].type); + align = t->packed || ctx->pointer_bits == 16 ? 1U : fe_type_align(t->fields[i].type); + if (align > max_align) max_align = align; + off = round_up(off, align); + t->fields[i].offset = off; + off += fe_type_size(t->fields[i].type); + } + t->align = max_align; + t->size = round_up(off, max_align); + t->cycle_state = 2; + return; + } + if (t->kind == FE_TYPE_ENUM) { + max_size = 0; max_align = 1; + for (i = 0; i < t->variant_count; ++i) { + unsigned j; + off = 0; + for (j = 0; j < t->variants[i].field_count; ++j) { + if (!t->variants[i].fields[j].type && t->variants[i].fields[j].ast_node) + t->variants[i].fields[j].type = fe_type_from_ast( + ctx, t->variants[i].fields[j].ast_node->a); + layout_type(ctx, t->variants[i].fields[j].type); + if (fe_type_align(t->variants[i].fields[j].type) > max_align) + max_align = fe_type_align(t->variants[i].fields[j].type); + off += fe_type_size(t->variants[i].fields[j].type); + } + if (off > max_size) max_size = off; + } + t->bits = t->variant_count > 256U ? 16U : 8U; + off = ctx->pointer_bits == 16 ? t->bits / 8U : round_up(t->bits / 8U, max_align); + t->size = round_up(off + max_size, ctx->pointer_bits == 16 ? 1U : max_align); + t->align = ctx->pointer_bits == 16 ? 1U : max_align; + t->cycle_state = 2; + } +} + +void fe_type_layout_all(FeTypeCtx *ctx) +{ + FeType *t; + for (t = ctx->types; t; t = t->next) layout_type(ctx, t); +} + +FeFieldType *fe_type_field(FeType *t, const char *name) +{ + unsigned i; + if (!t || t->kind != FE_TYPE_STRUCT || !name) return 0; + for (i = 0; i < t->field_count; ++i) + if (strcmp(t->fields[i].name, name) == 0) return &t->fields[i]; + return 0; +} + +FeVariantType *fe_type_variant(FeType *t, const char *name) +{ + unsigned i; + if (!t || t->kind != FE_TYPE_ENUM || !name) return 0; + for (i = 0; i < t->variant_count; ++i) + if (strcmp(t->variants[i].name, name) == 0) return &t->variants[i]; + return 0; } FeType *fe_type_from_ast(FeTypeCtx *ctx, const FeNode *node) { - if(!node)return fe_type_intern(ctx,""); - if(node->kind!=FE_N_TYPE)return fe_type_intern(ctx,""); - if(node->text && (strcmp(node->text,"?")==0||strcmp(node->text,"!")==0||strcmp(node->text,"^")==0||strcmp(node->text,"&")==0||strcmp(node->text,"*")==0||strcmp(node->text,"far")==0))return fe_type_intern(ctx,""); - if(node->text && strcmp(node->text,"as")==0)return fe_type_from_ast(ctx,node->b); - if(node->text && strcmp(node->text,"fn")==0)return fe_type_intern(ctx,""); - return fe_type_intern(ctx,node->text); + unsigned long length = 0; + if (!node) return fe_type_intern(ctx, ""); + if (node->kind != FE_N_TYPE) return fe_type_intern(ctx, ""); + if (node->text && strcmp(node->text, "as") == 0) + return fe_type_from_ast(ctx, node->b); + if (node->text && strcmp(node->text, "str") == 0) + return fe_type_intern(ctx, "str"); + if (node->text && (strcmp(node->text, "&") == 0 || + strcmp(node->text, "&mut") == 0)) + return fe_type_ref(ctx, fe_type_from_ast(ctx,node->a), + strcmp(node->text,"&mut") == 0); + if (node->text && strcmp(node->text, "[") == 0) { + if (node->a) { + if (node->a->kind == FE_N_LITERAL && node->a->text) + length = strtoul(node->a->text, 0, 0); + return fe_type_array(ctx, length, fe_type_from_ast(ctx, node->b)); + } + return fe_type_slice(ctx, fe_type_from_ast(ctx, node->b)); + } + if (node->text && (strcmp(node->text, "?") == 0 || + strcmp(node->text, "!") == 0 || + strcmp(node->text, "^") == 0 || + strcmp(node->text, "&") == 0 || + strcmp(node->text, "&mut") == 0 || + strcmp(node->text, "*") == 0 || + strcmp(node->text, "far") == 0)) + return fe_type_intern(ctx, ""); + if (node->text && strcmp(node->text, "fn") == 0) + return fe_type_intern(ctx, ""); + return fe_type_intern(ctx, node->text); } -int fe_type_equal(const FeType *a,const FeType *b){return a==b || (a&&b&&strcmp(a->name,b->name)==0);} -int fe_type_is_integer(const FeType *t){return t&&t->kind==FE_TYPE_INT;} -const char *fe_type_c_name(const FeType *t,unsigned pointer_bits) + +int fe_type_equal(const FeType *a, const FeType *b) { - if(!t)return "int"; - if(t->kind==FE_TYPE_VOID)return "void"; - if(t->kind==FE_TYPE_BOOL)return "unsigned char"; - if(t->kind!=FE_TYPE_INT)return "int"; - if(strcmp(t->name,"usize")==0)return pointer_bits==16?"unsigned short":"unsigned long"; - if(strcmp(t->name,"isize")==0)return pointer_bits==16?"short":"long"; - if(strcmp(t->name,"i8")==0)return "signed char"; - if(strcmp(t->name,"u8")==0)return "unsigned char"; - if(strcmp(t->name,"i16")==0)return "short"; - if(strcmp(t->name,"u16")==0)return "unsigned short"; - if(strcmp(t->name,"i32")==0)return "long"; - if(strcmp(t->name,"u32")==0)return "unsigned long"; - return "int"; + return a == b || (a && b && strcmp(a->name, b->name) == 0); +} + +int fe_type_is_integer(const FeType *t) +{ + return t && t->kind == FE_TYPE_INT; +} + +int fe_type_is_indexable(const FeType *t) +{ + return t && (t->kind == FE_TYPE_ARRAY || t->kind == FE_TYPE_SLICE || + t->kind == FE_TYPE_STR); +} + +const char *fe_type_c_name(const FeType *t, unsigned pointer_bits) +{ + if (!t) return "long"; + if (t->cname) return t->cname; + if (t->kind == FE_TYPE_VOID) return "void"; + if (t->kind == FE_TYPE_BOOL || t->kind == FE_TYPE_CHAR) return "unsigned char"; + if (t->kind == FE_TYPE_REF) { + static char ref_name[128]; + if (t->ref_mut) { + strcpy(ref_name,fe_type_c_name(t->elem,pointer_bits)); + strcat(ref_name," *"); + } else { + strcpy(ref_name,"const "); + strcat(ref_name,fe_type_c_name(t->elem,pointer_bits)); + strcat(ref_name," *"); + } + return ref_name; + } + if (t->kind != FE_TYPE_INT) return "long"; + if (strcmp(t->name, "usize") == 0) return pointer_bits == 16 ? "unsigned short" : "unsigned long"; + if (strcmp(t->name, "isize") == 0) return pointer_bits == 16 ? "short" : "long"; + if (strcmp(t->name, "i8") == 0) return "signed char"; + if (strcmp(t->name, "u8") == 0) return "unsigned char"; + if (strcmp(t->name, "i16") == 0) return "short"; + if (strcmp(t->name, "u16") == 0) return "unsigned short"; + if (strcmp(t->name, "i32") == 0) return "long"; + if (strcmp(t->name, "u32") == 0) return "unsigned long"; + return "long"; } diff --git a/fec/src/types.h b/fec/src/types.h index 290777f..5165c78 100644 --- a/fec/src/types.h +++ b/fec/src/types.h @@ -4,28 +4,80 @@ #include "ast.h" typedef enum FeTypeKind { - FE_TYPE_ERROR, FE_TYPE_VOID, FE_TYPE_BOOL, FE_TYPE_INT, FE_TYPE_UNKNOWN + FE_TYPE_ERROR, FE_TYPE_VOID, FE_TYPE_BOOL, FE_TYPE_CHAR, FE_TYPE_INT, + FE_TYPE_STRUCT, FE_TYPE_ENUM, FE_TYPE_ARRAY, FE_TYPE_SLICE, FE_TYPE_STR, + FE_TYPE_REF, FE_TYPE_UNKNOWN } FeTypeKind; +typedef struct FeFieldType FeFieldType; +typedef struct FeVariantType FeVariantType; + +struct FeFieldType { + char *name; + FeType *type; + unsigned long offset; + const FeNode *ast_node; +}; + +struct FeVariantType { + char *name; + FeFieldType *fields; + unsigned field_count; + unsigned tag; + const FeNode *ast_node; + char *maker; +}; + struct FeType { FeTypeKind kind; - char name[16]; + char name[64]; + char *cname; + char *maker; + char *indexer; + char *slicer; + char *full_slicer; + char *tail_slicer; unsigned bits; int is_unsigned; + int packed; + unsigned long length; + unsigned long size; + unsigned align; + FeType *elem; + int ref_mut; + FeFieldType *fields; + unsigned field_count; + FeVariantType *variants; + unsigned variant_count; FeType *next; + int emit_state; + int cycle_state; }; typedef struct FeTypeCtx { FeArena *arena; FeType *types; unsigned pointer_bits; + const char *unit_name; + unsigned generated_serial; } FeTypeCtx; void fe_types_init(FeTypeCtx *ctx, FeArena *arena, unsigned pointer_bits); FeType *fe_type_intern(FeTypeCtx *ctx, const char *name); FeType *fe_type_from_ast(FeTypeCtx *ctx, const FeNode *node); +FeType *fe_type_array(FeTypeCtx *ctx, unsigned long length, FeType *elem); +FeType *fe_type_slice(FeTypeCtx *ctx, FeType *elem); +FeType *fe_type_ref(FeTypeCtx *ctx, FeType *elem, int mutable); +FeType *fe_type_declare_struct(FeTypeCtx *ctx, const FeNode *node, int packed); +FeType *fe_type_declare_enum(FeTypeCtx *ctx, const FeNode *node); +void fe_type_layout_all(FeTypeCtx *ctx); +FeFieldType *fe_type_field(FeType *t, const char *name); +FeVariantType *fe_type_variant(FeType *t, const char *name); int fe_type_equal(const FeType *a, const FeType *b); int fe_type_is_integer(const FeType *t); +int fe_type_is_indexable(const FeType *t); const char *fe_type_c_name(const FeType *t, unsigned pointer_bits); +unsigned long fe_type_size(const FeType *t); +unsigned fe_type_align(const FeType *t); #endif diff --git a/fec/test-dos.bat b/fec/test-dos.bat index 5427a5e..22ce1f9 100644 --- a/fec/test-dos.bat +++ b/fec/test-dos.bat @@ -93,6 +93,102 @@ if not errorlevel 1 goto test_fail fec.exe --target=bits32 --emit-c TESTS\M2\BAD-VO.FE -o TESTS\M2\BAD-VO.C > nul if not errorlevel 1 goto test_fail +if exist TESTS\M3\STRUCT.C del TESTS\M3\STRUCT.C +if exist TESTS\M3\STRUCT.EXE del TESTS\M3\STRUCT.EXE +if exist TESTS\M3\ENUM.C del TESTS\M3\ENUM.C +if exist TESTS\M3\ENUM.EXE del TESTS\M3\ENUM.EXE +if exist TESTS\M3\ARRAY.C del TESTS\M3\ARRAY.C +if exist TESTS\M3\ARRAY.EXE del TESTS\M3\ARRAY.EXE +if exist TESTS\M3\STR.C del TESTS\M3\STR.C +if exist TESTS\M3\STR.EXE del TESTS\M3\STR.EXE +if exist TESTS\M3\FOR.C del TESTS\M3\FOR.C +if exist TESTS\M3\FOR.EXE del TESTS\M3\FOR.EXE +if exist TESTS\M3\NESTED.C del TESTS\M3\NESTED.C +if exist TESTS\M3\NESTED.EXE del TESTS\M3\NESTED.EXE +if exist TESTS\M3\CHAR.C del TESTS\M3\CHAR.C +if exist TESTS\M3\CHAR.EXE del TESTS\M3\CHAR.EXE +if exist TESTS\M3\ARRAYCTX.C del TESTS\M3\ARRAYCTX.C +if exist TESTS\M3\ARRAYCTX.EXE del TESTS\M3\ARRAYCTX.EXE +if exist TESTS\M3\BOUNDS.C del TESTS\M3\BOUNDS.C +if exist TESTS\M3\BOUNDS.EXE del TESTS\M3\BOUNDS.EXE +if exist TESTS\M3\BOUNDS-N.C del TESTS\M3\BOUNDS-N.C +if exist TESTS\M3\BOUNDS-N.EXE del TESTS\M3\BOUNDS-N.EXE + +fec.exe --target=bits32 --emit-c TESTS\M3\STRUCT.FE -o TESTS\M3\STRUCT.C > nul +if errorlevel 1 goto test_fail +wcl386 -q -za -bt=dos -fe=TESTS\M3\STRUCT.EXE TESTS\M3\STRUCT.C +if errorlevel 1 goto test_fail +TESTS\M3\STRUCT.EXE +if errorlevel 1 goto test_fail +fec.exe --target=bits32 --emit-c TESTS\M3\ENUM.FE -o TESTS\M3\ENUM.C > nul +if errorlevel 1 goto test_fail +wcl386 -q -za -bt=dos -fe=TESTS\M3\ENUM.EXE TESTS\M3\ENUM.C +if errorlevel 1 goto test_fail +TESTS\M3\ENUM.EXE +if errorlevel 1 goto test_fail +fec.exe --target=bits32 --emit-c TESTS\M3\ARRAY.FE -o TESTS\M3\ARRAY.C > nul +if errorlevel 1 goto test_fail +wcl386 -q -za -bt=dos -fe=TESTS\M3\ARRAY.EXE TESTS\M3\ARRAY.C +if errorlevel 1 goto test_fail +TESTS\M3\ARRAY.EXE +if errorlevel 1 goto test_fail +fec.exe --target=bits32 --emit-c TESTS\M3\STR.FE -o TESTS\M3\STR.C > nul +if errorlevel 1 goto test_fail +wcl386 -q -za -bt=dos -fe=TESTS\M3\STR.EXE TESTS\M3\STR.C +if errorlevel 1 goto test_fail +TESTS\M3\STR.EXE +if errorlevel 1 goto test_fail +fec.exe --target=bits32 --emit-c TESTS\M3\FOR.FE -o TESTS\M3\FOR.C > nul +if errorlevel 1 goto test_fail +wcl386 -q -za -bt=dos -fe=TESTS\M3\FOR.EXE TESTS\M3\FOR.C +if errorlevel 1 goto test_fail +TESTS\M3\FOR.EXE +if errorlevel 1 goto test_fail +fec.exe --target=bits32 --emit-c TESTS\M3\NESTED.FE -o TESTS\M3\NESTED.C > nul +if errorlevel 1 goto test_fail +wcl386 -q -za -bt=dos -fe=TESTS\M3\NESTED.EXE TESTS\M3\NESTED.C +if errorlevel 1 goto test_fail +TESTS\M3\NESTED.EXE +if errorlevel 1 goto test_fail +fec.exe --target=bits32 --emit-c TESTS\M3\CHAR.FE -o TESTS\M3\CHAR.C > nul +if errorlevel 1 goto test_fail +wcl386 -q -za -bt=dos -fe=TESTS\M3\CHAR.EXE TESTS\M3\CHAR.C +if errorlevel 1 goto test_fail +TESTS\M3\CHAR.EXE +if errorlevel 1 goto test_fail +fec.exe --target=bits32 --emit-c TESTS\M3\ARRAYCTX.FE -o TESTS\M3\ARRAYCTX.C > nul +if errorlevel 1 goto test_fail +wcl386 -q -za -bt=dos -fe=TESTS\M3\ARRAYCTX.EXE TESTS\M3\ARRAYCTX.C +if errorlevel 1 goto test_fail +TESTS\M3\ARRAYCTX.EXE +if errorlevel 1 goto test_fail +fec.exe --target=bits32 --emit-c TESTS\M3\BOUNDS.FE -o TESTS\M3\BOUNDS.C > nul +if errorlevel 1 goto test_fail +wcl386 -q -za -bt=dos -fe=TESTS\M3\BOUNDS.EXE TESTS\M3\BOUNDS.C +if errorlevel 1 goto test_fail +TESTS\M3\BOUNDS.EXE +if not errorlevel 1 goto test_fail +fec.exe --target=bits32 --no-checks --emit-c TESTS\M3\BOUNDS.FE -o TESTS\M3\BOUNDS-N.C > nul +if errorlevel 1 goto test_fail +wcl386 -q -za -bt=dos -fe=TESTS\M3\BOUNDS-N.EXE TESTS\M3\BOUNDS-N.C +if errorlevel 1 goto test_fail +fec.exe --target=bits32 --emit-c TESTS\M3\BADFLD.FE -o TESTS\M3\BADFLD.C > nul +if not errorlevel 1 goto test_fail +fec.exe --target=bits32 --emit-c TESTS\M3\BADMAT.FE -o TESTS\M3\BADMAT.C > nul +if not errorlevel 1 goto test_fail +fec.exe --target=bits32 --emit-c TESTS\M3\BADARR.FE -o TESTS\M3\BADARR.C > nul +if not errorlevel 1 goto test_fail +fec.exe --target=bits32 --emit-c TESTS\M3\BADCYCLE.FE -o TESTS\M3\BADCYCLE.C > nul +if not errorlevel 1 goto test_fail +fec.exe --target=bits32 --emit-c TESTS\M3\BADSTR.FE -o TESTS\M3\BADSTR.C > nul +if not errorlevel 1 goto test_fail +fec.exe --target=bits32 --emit-c TESTS\M3\BADCHAR.FE -o TESTS\M3\BADCHAR.C > nul +if not errorlevel 1 goto test_fail +fec.exe --target=bits32 --emit-c TESTS\M3\BADFIELD.FE -o TESTS\M3\BADFIELD.C > nul +if not errorlevel 1 goto test_fail +fec.exe --target=bits32 --emit-c TESTS\M3\BADINDEX.FE -o TESTS\M3\BADINDEX.C > nul +if not errorlevel 1 goto test_fail + echo OK>TEST.OK cd C:\FEC goto test_done diff --git a/fec/tests/m3/array.fe b/fec/tests/m3/array.fe new file mode 100644 index 0000000..a8ff358 --- /dev/null +++ b/fec/tests/m3/array.fe @@ -0,0 +1,9 @@ +unit m3_array; + +fn main() -> i32 { + let a: [3]i32 = [1, 2, 3]; + let s: []i32 = a[..]; + let t: []i32 = s[1..3]; + if a[0] + s[1] + t[0] == 5 and s.n == 3 { return 0; } + return 1; +} diff --git a/fec/tests/m3/arrayctx.fe b/fec/tests/m3/arrayctx.fe new file mode 100644 index 0000000..9e6d047 --- /dev/null +++ b/fec/tests/m3/arrayctx.fe @@ -0,0 +1,7 @@ +unit m3_arrayctx; + +fn main() -> i32 { + let bytes: [3]u8 = [1, 2, 3]; + if bytes[0] == 1 and bytes[2] == 3 { return 0; } + return 1; +} diff --git a/fec/tests/m3/badarr.fe b/fec/tests/m3/badarr.fe new file mode 100644 index 0000000..f419f7a --- /dev/null +++ b/fec/tests/m3/badarr.fe @@ -0,0 +1,5 @@ +unit fail_m3_array; +fn main() -> i32 { + let a: [2]i32 = [1, true, 3]; + return a[0]; +} diff --git a/fec/tests/m3/badchar.fe b/fec/tests/m3/badchar.fe new file mode 100644 index 0000000..aed42d4 --- /dev/null +++ b/fec/tests/m3/badchar.fe @@ -0,0 +1,6 @@ +unit fail_m3_char; + +fn main() -> i32 { + let u: u8 = 'A'; + return u; +} diff --git a/fec/tests/m3/badcycle.fe b/fec/tests/m3/badcycle.fe new file mode 100644 index 0000000..e96c17e --- /dev/null +++ b/fec/tests/m3/badcycle.fe @@ -0,0 +1,6 @@ +unit fail_m3_cycle; + +struct A { b: B, } +struct B { a: A, } + +fn main() -> i32 { return 0; } diff --git a/fec/tests/m3/badfield.fe b/fec/tests/m3/badfield.fe new file mode 100644 index 0000000..350edbb --- /dev/null +++ b/fec/tests/m3/badfield.fe @@ -0,0 +1,8 @@ +unit fail_m3_let_field; + +struct Point { x: i32, y: i32, } +fn main() -> i32 { + let p: Point = Point{ x: 1, y: 2 }; + p.x = 3; + return p.x; +} diff --git a/fec/tests/m3/badfld.fe b/fec/tests/m3/badfld.fe new file mode 100644 index 0000000..dd9e0c4 --- /dev/null +++ b/fec/tests/m3/badfld.fe @@ -0,0 +1,6 @@ +unit fail_m3_fields; +struct Point { x: i32, y: i32, } +fn main() -> i32 { + let p: Point = Point{ x: 1 }; + return p.z; +} diff --git a/fec/tests/m3/badindex.fe b/fec/tests/m3/badindex.fe new file mode 100644 index 0000000..88e8bca --- /dev/null +++ b/fec/tests/m3/badindex.fe @@ -0,0 +1,7 @@ +unit fail_m3_let_index; + +fn main() -> i32 { + let a: [2]i32 = [1, 2]; + a[0] = 3; + return a[0]; +} diff --git a/fec/tests/m3/badmat.fe b/fec/tests/m3/badmat.fe new file mode 100644 index 0000000..f1c1d54 --- /dev/null +++ b/fec/tests/m3/badmat.fe @@ -0,0 +1,6 @@ +unit fail_m3_match; +enum Shape { Empty, Circle(i32), } +fn main() -> i32 { + match Shape.Empty { Empty => 0; } + return 0; +} diff --git a/fec/tests/m3/badstr.fe b/fec/tests/m3/badstr.fe new file mode 100644 index 0000000..2896f0c --- /dev/null +++ b/fec/tests/m3/badstr.fe @@ -0,0 +1,6 @@ +unit fail_m3_str; +fn main() -> i32 { + var text: str = "abc"; + text[0] = 'z'; + return 0; +} diff --git a/fec/tests/m3/bounds.fe b/fec/tests/m3/bounds.fe new file mode 100644 index 0000000..ac581ee --- /dev/null +++ b/fec/tests/m3/bounds.fe @@ -0,0 +1,6 @@ +unit m3_bounds; + +fn main() -> i32 { + let a: [2]i32 = [1, 2]; + return a[2]; +} diff --git a/fec/tests/m3/char.fe b/fec/tests/m3/char.fe new file mode 100644 index 0000000..253ff85 --- /dev/null +++ b/fec/tests/m3/char.fe @@ -0,0 +1,9 @@ +unit m3_char; + +fn main() -> i32 { + let c: char = '\u0041'; + let u: u8 = c as u8; + let d: char = u as char; + if c == d and u == ('A' as u8) { return 0; } + return 1; +} diff --git a/fec/tests/m3/enum.fe b/fec/tests/m3/enum.fe new file mode 100644 index 0000000..2e2e934 --- /dev/null +++ b/fec/tests/m3/enum.fe @@ -0,0 +1,16 @@ +unit m3_enum; + +enum Shape { Empty, Circle(i32), Rect { w: i32, h: i32, }, } + +fn score(s: Shape) -> i32 { + match s { + Empty => { return 0; } + Circle(value) => { return value; } + Rect { w, h } => { return w * h; } + } +} + +fn main() -> i32 { + if score(Shape.Circle(5)) == 5 and score(Shape.Rect{ w: 2, h: 3 }) == 6 { return 0; } + return 1; +} diff --git a/fec/tests/m3/for.fe b/fec/tests/m3/for.fe new file mode 100644 index 0000000..e591000 --- /dev/null +++ b/fec/tests/m3/for.fe @@ -0,0 +1,18 @@ +unit m3_for; + +fn main() -> i32 { + var total: i32 = 0; + let a: [3]i32 = [1, 2, 3]; + let s: []i32 = a[..]; + var m: [1]i32 = [1]; + let ready: bool = true; + if ready { total += 0; } + while false { total += 100; } + for x in a { total += x.^; } + for i, x in a { if i == 1 and x.^ == 2 { total += 1; } } + for x in "ab" { if x.^ == ('a' as u8) { total += 1; } } + for x in s { if x.^ == 3 { total += 1; } } + for x in m { x.^ = 2; } + if total == 9 and m[0] == 2 { return 0; } + return 1; +} diff --git a/fec/tests/m3/nested.fe b/fec/tests/m3/nested.fe new file mode 100644 index 0000000..3f71cc9 --- /dev/null +++ b/fec/tests/m3/nested.fe @@ -0,0 +1,10 @@ +unit m3_nested; + +struct Outer { inner: Inner, } +struct Inner { value: i32, } + +fn main() -> i32 { + let x: Outer = Outer{ inner: Inner{ value: 7 } }; + if x.inner.value == 7 { return 0; } + return 1; +} diff --git a/fec/tests/m3/str.fe b/fec/tests/m3/str.fe new file mode 100644 index 0000000..f0077fd --- /dev/null +++ b/fec/tests/m3/str.fe @@ -0,0 +1,7 @@ +unit m3_str; + +fn main() -> i32 { + let text: str = "abc"; + if text[1] == ('b' as u8) and text.n == 3 { return 0; } + return 1; +} diff --git a/fec/tests/m3/struct.fe b/fec/tests/m3/struct.fe new file mode 100644 index 0000000..2d7e2b1 --- /dev/null +++ b/fec/tests/m3/struct.fe @@ -0,0 +1,14 @@ +unit m3_struct; + +struct Point { x: i32, y: i32, } +packed struct PackedPoint { x: u8, y: i32, } +struct Natural { a: u8, b: i32, } + +fn main() -> i32 { + let p: Point = Point{ x: 3, y: 4 }; + let q: PackedPoint = PackedPoint{ x: 1, y: 6 }; + let n: Natural = Natural{ a: 1, b: 2 }; + if p.x + p.y == 7 and q.y == 6 and n.b == 2 and + (Point{ x: 1, y: 2 }.x == 1) and @size_of(Natural) == 8 { return 0; } + return 1; +} diff --git a/fec/tests/run-tests.sh b/fec/tests/run-tests.sh index d7b4d75..9b907cc 100644 --- a/fec/tests/run-tests.sh +++ b/fec/tests/run-tests.sh @@ -40,3 +40,24 @@ for f in bad-condition bad-cast bad-assign bad-unknown bad-arity bad-types bad-r fi done echo "M2 tests: integer control-flow smoke passed" + +m3tmp=$(mktemp -d) +trap 'rm -rf "$m2tmp" "$m3tmp"' EXIT HUP INT TERM +for f in struct enum array arrayctx str for nested char; do + "$root"/fec --target=bits32 --emit-c "$root"/tests/m3/$f.fe -o "$m3tmp/$f.c" + ${CC:-cc} -std=c89 -pedantic "$m3tmp/$f.c" -o "$m3tmp/$f" + "$m3tmp/$f" +done +"$root"/fec --target=bits32 --emit-c "$root"/tests/m3/bounds.fe -o "$m3tmp/bounds.c" +${CC:-cc} -std=c89 -pedantic "$m3tmp/bounds.c" -o "$m3tmp/bounds" +if "$m3tmp/bounds"; then + echo "FAIL (bounds trap did not fire): tests/m3/bounds.fe" + exit 1 +fi +for f in badfld badmat badarr badcycle badstr badchar badfield badindex; do + if "$root"/fec --target=bits32 --emit-c "$root"/tests/m3/$f.fe -o "$m3tmp/$f.c" >/dev/null 2>/dev/null; then + echo "FAIL (accepted M3 semantic error): $f.fe" + exit 1 + fi +done +echo "M3 tests: structs, enums, arrays, slices, str, match, and bounds passed" diff --git a/fec/vm-m1.bat b/fec/vm-m1.bat index af2b722..63f9cb7 100644 --- a/fec/vm-m1.bat +++ b/fec/vm-m1.bat @@ -7,6 +7,7 @@ if not exist C:\FEC\TESTS md C:\FEC\TESTS if not exist C:\FEC\TESTS\PASS md C:\FEC\TESTS\PASS if not exist C:\FEC\TESTS\FAIL md C:\FEC\TESTS\FAIL if not exist C:\FEC\TESTS\M2 md C:\FEC\TESTS\M2 +if not exist C:\FEC\TESTS\M3 md C:\FEC\TESTS\M3 if exist C:\FEC\VM.FAIL del C:\FEC\VM.FAIL if exist C:\FEC\STAGE.FAIL del C:\FEC\STAGE.FAIL @@ -106,6 +107,41 @@ if errorlevel 1 goto stage_fail copy D:\FEC\TESTS\M2\CAST-W~1.FE C:\FEC\TESTS\M2\CAST-W.FE > nul if errorlevel 1 goto stage_fail +copy D:\FEC\TESTS\M3\STRUCT.FE C:\FEC\TESTS\M3\STRUCT.FE > nul +if errorlevel 1 goto stage_fail +copy D:\FEC\TESTS\M3\ENUM.FE C:\FEC\TESTS\M3\ENUM.FE > nul +if errorlevel 1 goto stage_fail +copy D:\FEC\TESTS\M3\ARRAY.FE C:\FEC\TESTS\M3\ARRAY.FE > nul +if errorlevel 1 goto stage_fail +copy D:\FEC\TESTS\M3\STR.FE C:\FEC\TESTS\M3\STR.FE > nul +if errorlevel 1 goto stage_fail +copy D:\FEC\TESTS\M3\FOR.FE C:\FEC\TESTS\M3\FOR.FE > nul +if errorlevel 1 goto stage_fail +copy D:\FEC\TESTS\M3\NESTED.FE C:\FEC\TESTS\M3\NESTED.FE > nul +if errorlevel 1 goto stage_fail +copy D:\FEC\TESTS\M3\CHAR.FE C:\FEC\TESTS\M3\CHAR.FE > nul +if errorlevel 1 goto stage_fail +copy D:\FEC\TESTS\M3\ARRAYCTX.FE C:\FEC\TESTS\M3\ARRAYCTX.FE > nul +if errorlevel 1 goto stage_fail +copy D:\FEC\TESTS\M3\BOUNDS.FE C:\FEC\TESTS\M3\BOUNDS.FE > nul +if errorlevel 1 goto stage_fail +copy D:\FEC\TESTS\M3\BADFLD.FE C:\FEC\TESTS\M3\BADFLD.FE > nul +if errorlevel 1 goto stage_fail +copy D:\FEC\TESTS\M3\BADMAT.FE C:\FEC\TESTS\M3\BADMAT.FE > nul +if errorlevel 1 goto stage_fail +copy D:\FEC\TESTS\M3\BADARR.FE C:\FEC\TESTS\M3\BADARR.FE > nul +if errorlevel 1 goto stage_fail +copy D:\FEC\TESTS\M3\BADCYCLE.FE C:\FEC\TESTS\M3\BADCYCLE.FE > nul +if errorlevel 1 goto stage_fail +copy D:\FEC\TESTS\M3\BADSTR.FE C:\FEC\TESTS\M3\BADSTR.FE > nul +if errorlevel 1 goto stage_fail +copy D:\FEC\TESTS\M3\BADCHAR.FE C:\FEC\TESTS\M3\BADCHAR.FE > nul +if errorlevel 1 goto stage_fail +copy D:\FEC\TESTS\M3\BADFIELD.FE C:\FEC\TESTS\M3\BADFIELD.FE > nul +if errorlevel 1 goto stage_fail +copy D:\FEC\TESTS\M3\BADINDEX.FE C:\FEC\TESTS\M3\BADINDEX.FE > nul +if errorlevel 1 goto stage_fail + call C:\FEC\TEST-DOS.BAT if exist C:\FEC\TEST.OK goto vm_success echo FAIL>C:\FEC\VM.FAIL