feat: implement M3 aggregate types and iteration

This commit is contained in:
2026-08-16 08:49:50 +09:00
parent 57b47a574a
commit 8e6a409637
32 changed files with 1729 additions and 94 deletions
+4 -10
View File
@@ -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
+2 -2
View File
@@ -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];
}
+4 -1
View File
@@ -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 {
+358 -8
View File
@@ -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;i<variant->field_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 : "<unknown>");
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;i<t->field_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;i<variant->field_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;i<value->variant_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;i<t->field_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;i<t->variant_count;i++) {
unsigned j;
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(&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;
}
+3 -1
View File
@@ -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);
+4 -4
View File
@@ -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;i++) { if(strcmp(argv[i],"--dump-ast")==0) dump=1; else if(strcmp(argv[i],"--emit-c")==0) emit=1; else if(strcmp(argv[i],"-o")==0){if(i+1>=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;i++) { if(strcmp(argv[i],"--dump-ast")==0) dump=1; else if(strcmp(argv[i],"--emit-c")==0) emit=1; else if(strcmp(argv[i],"-o")==0){if(i+1>=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;
}
+436 -14
View File
@@ -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;i<t->field_count;i++) emit_one_type(e,t->fields[i].type);
if (t->kind == FE_TYPE_ENUM)
for (i=0;i<t->variant_count;i++)
for (j=0;j<t->variants[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;i<t->field_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;i<t->variant_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;j<t->variants[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;i<t->variant_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;i<t->field_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;i<t->field_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;i<t->length;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;i<t->length;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;i<t->variant_count;i++) {
FeVariantType *v=&t->variants[i];
fprintf(e->out,"static %s %s(",t->cname,v->maker);
for(j=0;j<v->field_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;j<v->field_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; i<t->variant_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;i<v->field_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;i<n->sem_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;i<v->field_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 <stddef.h>\n\n", e->out);
fputs("/* generated by fec M3 */\n#include <stddef.h>\n#include <stdlib.h>\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) {
+4 -1
View File
@@ -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
+101 -15
View File
@@ -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;}
+1
View File
@@ -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);
+459 -36
View File
@@ -1,50 +1,473 @@
#include "types.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
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="<unknown>";
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;i<sizeof(t->name)-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 = "<unknown>";
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,"<unknown>");
if(node->kind!=FE_N_TYPE)return fe_type_intern(ctx,"<unknown>");
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,"<unknown>");
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,"<unknown>");
return fe_type_intern(ctx,node->text);
unsigned long length = 0;
if (!node) return fe_type_intern(ctx, "<unknown>");
if (node->kind != FE_N_TYPE) return fe_type_intern(ctx, "<unknown>");
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, "<unknown>");
if (node->text && strcmp(node->text, "fn") == 0)
return fe_type_intern(ctx, "<unknown>");
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";
}
+54 -2
View File
@@ -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
+96
View File
@@ -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
+9
View File
@@ -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;
}
+7
View File
@@ -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;
}
+5
View File
@@ -0,0 +1,5 @@
unit fail_m3_array;
fn main() -> i32 {
let a: [2]i32 = [1, true, 3];
return a[0];
}
+6
View File
@@ -0,0 +1,6 @@
unit fail_m3_char;
fn main() -> i32 {
let u: u8 = 'A';
return u;
}
+6
View File
@@ -0,0 +1,6 @@
unit fail_m3_cycle;
struct A { b: B, }
struct B { a: A, }
fn main() -> i32 { return 0; }
+8
View File
@@ -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;
}
+6
View File
@@ -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;
}
+7
View File
@@ -0,0 +1,7 @@
unit fail_m3_let_index;
fn main() -> i32 {
let a: [2]i32 = [1, 2];
a[0] = 3;
return a[0];
}
+6
View File
@@ -0,0 +1,6 @@
unit fail_m3_match;
enum Shape { Empty, Circle(i32), }
fn main() -> i32 {
match Shape.Empty { Empty => 0; }
return 0;
}
+6
View File
@@ -0,0 +1,6 @@
unit fail_m3_str;
fn main() -> i32 {
var text: str = "abc";
text[0] = 'z';
return 0;
}
+6
View File
@@ -0,0 +1,6 @@
unit m3_bounds;
fn main() -> i32 {
let a: [2]i32 = [1, 2];
return a[2];
}
+9
View File
@@ -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;
}
+16
View File
@@ -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;
}
+18
View File
@@ -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;
}
+10
View File
@@ -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;
}
+7
View File
@@ -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;
}
+14
View File
@@ -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;
}
+21
View File
@@ -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"
+36
View File
@@ -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