feat: implement M4 formatting builtins

This commit is contained in:
2026-08-16 13:24:25 +09:00
parent 8e6a409637
commit 53bca214b8
22 changed files with 779 additions and 21 deletions
+187 -1
View File
@@ -12,6 +12,7 @@ struct FeSym {
FeNode *fn;
int mutable;
int initialized;
FeNode *decl;
};
struct FeScope {
@@ -192,6 +193,7 @@ static FeSym *add_symbol(FeCheckerState *s, FeScope *scope,
sym->fn = fn;
sym->mutable = mutable;
sym->initialized = initialized;
sym->decl = decl;
if (decl) {
decl->cname = cname;
decl->sem_type = type;
@@ -215,6 +217,128 @@ 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 FeNode *find_const_node(FeCheck *c, const char *name)
{
FeNode *n;
for (n=c->ast->root ? c->ast->root->children : 0; n; n=n->next)
if (n->kind==FE_N_CONST && n->text && name && strcmp(n->text,name)==0)
return n;
return 0;
}
static const char *builtin_format(FeCheckerState *s, FeNode *fmt)
{
FeNode *decl;
FeSym *sym;
if (fmt && fmt->kind==FE_N_LITERAL && fmt->text && fmt->text[0]=='"')
return fmt->text;
if (fmt && fmt->kind==FE_N_IDENT) {
sym=find_symbol(s->scope,fmt->text);
decl=sym && sym->decl && sym->decl->kind==FE_N_CONST ?
sym->decl : find_const_node(s->c,fmt->text);
if (decl && decl->b && decl->b->kind==FE_N_LITERAL &&
decl->b->text && decl->b->text[0]=='"') {
if (!decl->a || fe_type_from_ast(&s->c->types,decl->a)->kind==FE_TYPE_STR)
return decl->b->text;
}
}
return 0;
}
static int format_is_slice_u8(FeType *t)
{
return t && t->kind==FE_TYPE_SLICE && t->elem &&
t->elem->kind==FE_TYPE_INT && strcmp(t->elem->name,"u8")==0;
}
static int format_is_writer_type(FeType *t)
{
return t && t->kind==FE_TYPE_STRUCT &&
(strcmp(t->name,"Writer")==0 || strcmp(t->name,"io.Writer")==0);
}
static int format_arg_ok(FeType *t, int verb)
{
if (!t) return 0;
if (verb=='x') return fe_type_is_integer(t);
if (verb=='c') return t->kind==FE_TYPE_CHAR;
if (verb=='s') return t->kind==FE_TYPE_STR || format_is_slice_u8(t);
if (verb=='b') return t->kind==FE_TYPE_BOOL;
if (t->kind==FE_TYPE_INT || t->kind==FE_TYPE_BOOL ||
t->kind==FE_TYPE_CHAR || t->kind==FE_TYPE_STR) return 1;
return format_is_slice_u8(t) ||
(t->kind==FE_TYPE_ENUM && t->is_error);
}
static void check_format_call(FeCheckerState *s, FeNode *n)
{
const char *fmt;
FeNode *fmt_node;
FeNode *arg;
FeNode *x;
FeType *t;
unsigned long i,j;
unsigned count=0;
unsigned argc=0;
unsigned offset=0;
int verb;
int bad=0;
if (strcmp(n->text,"@fprint")==0) offset=1;
fmt_node=n->children;
if (offset) {
if (!fmt_node) { err(s->c,n->loc,"@fprint requires a writer"); return; }
t=check_expr(s,fmt_node);
if (!(t && t->kind==FE_TYPE_REF && t->ref_mut &&
format_is_writer_type(t->elem)))
err(s->c,fmt_node->loc,"@fprint requires &mut io.Writer");
fmt_node=fmt_node->next;
}
if (strcmp(n->text,"@sprint")==0) {
if (!fmt_node) { err(s->c,n->loc,"@sprint requires a buffer"); return; }
t=check_expr(s,fmt_node);
if (!format_is_slice_u8(t)) err(s->c,fmt_node->loc,"@sprint requires []u8 buffer");
fmt_node=fmt_node->next;
}
fmt=builtin_format(s,fmt_node);
if (!fmt) { err(s->c,n->loc,"format must be a comptime string"); return; }
n->aux_text=(char *)fmt;
arg=fmt_node ? fmt_node->next : 0;
for (x=arg;x;x=x->next) { check_expr(s,x); ++argc; }
i=1;
while (fmt[i] && fmt[i]!='"') {
if (fmt[i]=='\\') { if (fmt[i+1]) ++i; ++i; continue; }
if (fmt[i]=='{' && fmt[i+1]=='{') { i+=2; continue; }
if (fmt[i]=='}' && fmt[i+1]=='}') { i+=2; continue; }
if (fmt[i]=='{') {
j=i+1;
while (fmt[j] && fmt[j]!='}') ++j;
if (!fmt[j]) { err(s->c,n->loc,"unterminated format placeholder"); bad=1; break; }
if (j==i+1) verb=' '; else if (j==i+2) verb=(unsigned char)fmt[i+1]; else verb='?';
if (verb!=' ' && verb!='x' && verb!='c' && verb!='s' && verb!='b') {
err(s->c,n->loc,"unsupported format verb"); bad=1;
}
if (!arg) { err(s->c,n->loc,"format argument count mismatch"); bad=1; }
else {
t=arg->sem_type;
if (verb==' ' && t && t->kind==FE_TYPE_ENUM && t->is_error) verb='s';
if (!format_arg_ok(t,verb)) { err(s->c,arg->loc,"no fmt writer for argument type"); bad=1; }
arg=arg->next;
}
++count; i=j+1; continue;
}
if (fmt[i]=='}') { err(s->c,n->loc,"unmatched '}' in format"); bad=1; }
++i;
}
if (count!=argc) { err(s->c,n->loc,"format argument count mismatch"); bad=1; }
(void)bad;
}
static int is_format_builtin(const char *name)
{
return name && (strcmp(name,"@print")==0 || strcmp(name,"@fprint")==0 ||
strcmp(name,"@sprint")==0);
}
static int lvalue_writable(FeCheckerState *s, FeNode *n)
{
FeSym *sym;
@@ -292,12 +416,21 @@ static FeType *check_array_init(FeCheckerState *s, FeNode *n)
n->sem_type=fe_type_array(&s->c->types,count,elem); return n->sem_type;
}
static int array_slice_lvalue(FeNode *n)
{
return n && (n->kind==FE_N_IDENT || n->kind==FE_N_MEMBER ||
n->kind==FE_N_INDEX);
}
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; }
if(n->c || !n->b) {
if (base->kind==FE_TYPE_ARRAY && !array_slice_lvalue(n->a))
err(s->c,n->loc,"array slicing requires a stable lvalue");
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;
}
@@ -361,6 +494,15 @@ static FeType *check_expr(FeCheckerState *s, FeNode *n)
} else if (strcmp(op, "-") == 0) {
if (known(a) && !fe_type_is_integer(a))
err(c, n->loc, "unary '-' requires integer");
} else if (strcmp(op, "try") == 0) {
if (a && a->kind==FE_TYPE_ERROR_UNION)
a=a->error_value;
else {
err(c,n->loc,"try requires an error result");
a=unknown(c);
}
} else if (strcmp(op,"&")==0 || strcmp(op,"&mut")==0) {
a=fe_type_ref(&c->types,a,strcmp(op,"&mut")==0);
}
n->sem_type = a;
return a;
@@ -403,6 +545,34 @@ static FeType *check_expr(FeCheckerState *s, FeNode *n)
return a;
}
if (n->kind == FE_N_CALL) {
if (n->a && n->a->kind==FE_N_MEMBER && n->a->a &&
n->a->a->kind==FE_N_IDENT && n->a->a->text &&
strcmp(n->a->a->text,"io")==0 && n->a->b && n->a->b->text &&
(strcmp(n->a->b->text,"buf_writer")==0 ||
strcmp(n->a->b->text,"null_writer")==0)) {
FeNode *arg=n->children;
if (strcmp(n->a->b->text,"buf_writer")==0) {
if (!arg) err(c,n->loc,"io.buf_writer requires a buffer");
else {
a=check_expr(s,arg);
if (!(a && a->kind==FE_TYPE_REF && a->ref_mut &&
format_is_slice_u8(a->elem)))
err(c,arg->loc,"io.buf_writer requires &mut []u8 buffer");
}
} else if (arg) err(c,n->loc,"io.null_writer takes no arguments");
n->sem_type=fe_type_intern(&c->types,"io.Writer");
return n->sem_type;
}
if (n->text && is_format_builtin(n->text)) {
check_format_call(s,n);
if (strcmp(n->text,"@print")==0)
n->sem_type=fe_type_intern(&c->types,"void");
else if (strcmp(n->text,"@sprint")==0)
n->sem_type=fe_type_intern(&c->types,"usize");
else
n->sem_type=fe_type_error_union(&c->types,fe_type_intern(&c->types,"void"));
return n->sem_type;
}
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);
@@ -452,6 +622,12 @@ static FeType *check_expr(FeCheckerState *s, FeNode *n)
return unknown(c);
}
if (n->kind == FE_N_MEMBER) {
if (n->a && n->a->kind==FE_N_IDENT && n->a->text &&
strcmp(n->a->text,"io")==0 && n->b && n->b->text &&
strcmp(n->b->text,"stdout")==0) {
n->sem_type=fe_type_intern(&c->types,"io.Writer");
return n->sem_type;
}
a=check_expr(s,n->a);
if (a->kind == FE_TYPE_REF && n->b && n->b->text &&
strcmp(n->b->text,"^")==0) {
@@ -645,6 +821,10 @@ static void check_type_cycle(FeCheck *c, FeType *t)
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->kind == FE_TYPE_ERROR_UNION) {
check_type_cycle(c,t->error_value);
return;
}
if (t->cycle_state == 1) {
if (c->ast->root) err(c, c->ast->root->loc, "by-value recursive type");
return;
@@ -739,6 +919,10 @@ static void check_stmt(FeCheckerState *s, FeNode *n)
break;
case FE_N_EXPR_STMT:
check_expr(s, n->a);
if (n->a && n->a->kind==FE_N_UNARY && n->a->text &&
strcmp(n->a->text,"try")==0 &&
(!s->ret || s->ret->kind!=FE_TYPE_ERROR_UNION))
err(c,n->loc,"try requires an enclosing error result");
break;
case FE_N_IF:
a = check_expr(s, n->a);
@@ -815,6 +999,8 @@ int fe_check_program(FeCheck *c)
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);
for (n = c->ast->root ? c->ast->root->children : 0; n; n = n->next)
if (n->kind == FE_N_ERROR_DECL) fe_type_declare_error(&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) {
+302 -14
View File
@@ -40,6 +40,7 @@ static void emit_type_deps(FeEmitter *e, FeType *t)
static void emit_one_type(FeEmitter *e, FeType *t)
{
unsigned i,j;
if (t && strcmp(t->name,"io.Writer")==0) return;
if (!t || t->emit_state ||
(t->kind != FE_TYPE_STRUCT && t->kind != FE_TYPE_ENUM &&
t->kind != FE_TYPE_ARRAY && t->kind != FE_TYPE_SLICE)) return;
@@ -114,12 +115,12 @@ static void emit_type_helpers(FeEmitter *e)
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) { ",
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);
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);
@@ -143,6 +144,49 @@ static void emit_type_helpers(FeEmitter *e)
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_m4_runtime(FeEmitter *e)
{
fputs("typedef struct { unsigned char *p; unsigned long n; } fe_m4_slice;\n",e->out);
fputs("typedef struct { void *ctx; unsigned short (*write_fn)(void *, const unsigned char *, unsigned long); } fe_writer;\n",e->out);
fputs("unsigned short fe_m4_error;\n",e->out);
fputs("unsigned short fe_m4_stdout_write(void *ctx, const unsigned char *p, unsigned long n) { (void)ctx; return fwrite(p,1,(size_t)n,stdout)==(size_t)n ? 0 : 1; }\n",e->out);
fputs("unsigned short fe_m4_null_write(void *ctx, const unsigned char *p, unsigned long n) { (void)ctx; (void)p; (void)n; return 0; }\n",e->out);
fputs("unsigned short fe_m4_buf_write(void *ctx, const unsigned char *p, unsigned long n) { fe_m4_slice *b=(fe_m4_slice*)ctx; unsigned long k=n<b->n?n:b->n; if(k) memcpy(b->p,p,(size_t)k); b->p+=k; b->n-=k; return 0; }\n",e->out);
fputs("fe_writer fe_m4_stdout_writer(void) { fe_writer w; w.ctx=0; w.write_fn=fe_m4_stdout_write; return w; }\n",e->out);
fputs("fe_writer fe_m4_null_writer(void) { fe_writer w; w.ctx=0; w.write_fn=fe_m4_null_write; return w; }\n",e->out);
fputs("fe_writer fe_m4_buf_writer(fe_m4_slice *b) { fe_writer w; w.ctx=b; w.write_fn=fe_m4_buf_write; return w; }\n",e->out);
fputs("/* bounded sprint stack; overflow traps instead of corrupting an outer call */\n#define FE_M4_SPRINT_DEPTH 8\n",e->out);
fputs("typedef struct { fe_m4_slice b; unsigned long start_n; } fe_m4_sprint_frame;\n",e->out);
fputs("static fe_m4_sprint_frame fe_m4_sprint_stack[FE_M4_SPRINT_DEPTH];\n",e->out);
fputs("static unsigned fe_m4_sprint_depth;\n",e->out);
fputs("void fe_m4_sprint_begin(fe_m4_slice *b) { if (fe_m4_sprint_depth>=FE_M4_SPRINT_DEPTH) abort(); fe_m4_sprint_stack[fe_m4_sprint_depth].b=*b; fe_m4_sprint_stack[fe_m4_sprint_depth].start_n=b->n; ++fe_m4_sprint_depth; }\n",e->out);
fputs("fe_writer fe_m4_sprint_writer(void) { return fe_m4_buf_writer(&fe_m4_sprint_stack[fe_m4_sprint_depth-1].b); }\n",e->out);
fputs("unsigned long fe_m4_sprint_finish(void) { unsigned long result; if (!fe_m4_sprint_depth) abort(); --fe_m4_sprint_depth; result=fe_m4_sprint_stack[fe_m4_sprint_depth].start_n-fe_m4_sprint_stack[fe_m4_sprint_depth].b.n; return result; }\n",e->out);
fputs("unsigned short fe_m4_write_bytes(fe_writer w, const unsigned char *p, unsigned long n) { return w.write_fn ? w.write_fn(w.ctx,p,n) : 1; }\n",e->out);
fputs("unsigned short fe_m4_write_cstr(fe_writer w, const char *p) { return fe_m4_write_bytes(w,(const unsigned char*)p,(unsigned long)strlen(p)); }\n",e->out);
fputs("unsigned short fe_m4_write_str(fe_writer w, fe_str s) { return fe_m4_write_bytes(w,s.p,s.n); }\n",e->out);
fputs("#define fe_m4_write_slice(w,s) fe_m4_write_bytes((w),(s).p,(s).n)\n",e->out);
fputs("unsigned short fe_m4_write_int(fe_writer w, long v) { char b[40]; sprintf(b,\"%ld\",v); return fe_m4_write_cstr(w,b); }\n",e->out);
fputs("unsigned short fe_m4_write_hex(fe_writer w, unsigned long v) { char b[40]; sprintf(b,\"%lx\",v); return fe_m4_write_cstr(w,b); }\n",e->out);
fputs("unsigned short fe_m4_write_char(fe_writer w, unsigned char v) { return fe_m4_write_bytes(w,&v,1); }\n",e->out);
fputs("unsigned short fe_m4_write_bool(fe_writer w, unsigned char v) { return fe_m4_write_cstr(w,v ? \"true\" : \"false\"); }\n",e->out);
fputs("unsigned short fe_m4_write_error(fe_writer w, unsigned long v) { char b[40]; sprintf(b,\"error#%lu\",v); return fe_m4_write_cstr(w,b); }\n",e->out);
}
static int node_uses_m4(FeNode *n)
{
FeNode *x;
if (!n) return 0;
if (n->kind==FE_N_CALL && n->text &&
(strcmp(n->text,"@print")==0 || strcmp(n->text,"@fprint")==0 ||
strcmp(n->text,"@sprint")==0)) return 1;
if (n->kind==FE_N_CALL && n->a && n->a->kind==FE_N_MEMBER &&
n->a->a && n->a->a->text && strcmp(n->a->a->text,"io")==0) return 1;
if (node_uses_m4(n->a) || node_uses_m4(n->b) || node_uses_m4(n->c)) return 1;
for (x=n->children; x; x=x->next) if (node_uses_m4(x)) return 1;
return 0;
}
static void emit_expr(FeEmitter *e, FeNode *n);
static void emit_stmt(FeEmitter *e, FeNode *n);
@@ -267,6 +311,187 @@ static void emit_c_literal(FILE *out, const char *text, int string)
fputc(quote,out);
}
static void emit_m4_piece(FILE *out, const char *fmt, unsigned long begin,
unsigned long end)
{
unsigned long i;
unsigned long cp;
int h0,h1,h2,h3;
int c;
fputc('"',out);
for(i=begin;i<end;i++) {
c=(unsigned char)fmt[i];
if(c=='{' && i+1<end && fmt[i+1]=='{') { fputc('{',out); ++i; continue; }
if(c=='}' && i+1<end && fmt[i+1]=='}') { fputc('}',out); ++i; continue; }
if(c=='\\' && i+1<end) {
++i; c=(unsigned char)fmt[i];
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 if(c=='x' && i+2<end &&
(h0=hex_value((unsigned char)fmt[i+1]))>=0 &&
(h1=hex_value((unsigned char)fmt[i+2]))>=0) {
emit_byte(out,(unsigned)((h0<<4)|h1)); i+=2;
} else if(c=='u' && i+4<end &&
(h0=hex_value((unsigned char)fmt[i+1]))>=0 &&
(h1=hex_value((unsigned char)fmt[i+2]))>=0 &&
(h2=hex_value((unsigned char)fmt[i+3]))>=0 &&
(h3=hex_value((unsigned char)fmt[i+4]))>=0) {
cp=(unsigned long)((h0<<12)|(h1<<8)|(h2<<4)|h3);
emit_codepoint(out,cp); i+=4;
}
else if(c=='\\' || c=='"') { fputc('\\',out); fputc(c,out); }
else emit_byte(out,(unsigned)c);
continue;
}
if(c=='"' || c=='\\') fputc('\\',out);
fputc(c,out);
}
fputc('"',out);
}
static void emit_m4_writer(FeEmitter *e, FeNode *arg, int buffer)
{
if (buffer) {
fputs("fe_m4_buf_writer((fe_m4_slice*)&",e->out);
if (arg && arg->kind==FE_N_UNARY && arg->text &&
(strcmp(arg->text,"&")==0 || strcmp(arg->text,"&mut")==0)) emit_expr(e,arg->a);
else emit_expr(e,arg);
fputs(")",e->out);
} else if (arg && arg->kind==FE_N_UNARY && arg->text &&
(strcmp(arg->text,"&")==0 || strcmp(arg->text,"&mut")==0)) {
emit_expr(e,arg->a);
} else if (arg && arg->kind==FE_N_CALL && arg->a &&
arg->a->kind==FE_N_MEMBER) {
emit_expr(e,arg);
} else if (arg && arg->sem_type && arg->sem_type->kind==FE_TYPE_REF) {
fputs("(*",e->out); emit_expr(e,arg); fputc(')',e->out);
} else {
emit_expr(e,arg);
}
}
static void emit_m4_writer_value(FeEmitter *e, FeNode *writer, int buffer)
{
if (!writer) fputs("fe_m4_stdout_writer()",e->out);
else if (buffer) fputs("fe_m4_sprint_writer()",e->out);
else emit_m4_writer(e,writer,buffer);
}
static void emit_m4_arg(FeEmitter *e, FeNode *arg, int verb,
FeNode *writer, int buffer, int error_value)
{
FeType *t=arg ? arg->sem_type : 0;
if (verb=='x') {
fputs("fe_m4_write_hex(",e->out); emit_m4_writer_value(e,writer,buffer); fputs(", (unsigned long)",e->out); emit_expr(e,arg); fputc(')',e->out); return;
}
if (verb=='c') {
fputs("fe_m4_write_char(",e->out); emit_m4_writer_value(e,writer,buffer); fputs(", (unsigned char)",e->out); emit_expr(e,arg); fputc(')',e->out); return;
}
if (verb=='b') {
fputs("fe_m4_write_bool(",e->out); emit_m4_writer_value(e,writer,buffer); fputs(", ",e->out); emit_expr(e,arg); fputc(')',e->out); return;
}
if (verb=='s' || (verb==' ' && t && (t->kind==FE_TYPE_STR || t->kind==FE_TYPE_SLICE))) {
if (t && t->kind==FE_TYPE_STR) fputs("fe_m4_write_str(",e->out);
else fputs("fe_m4_write_slice(",e->out);
emit_m4_writer_value(e,writer,buffer); fputs(", ",e->out); emit_expr(e,arg); fputc(')',e->out); return;
}
if (error_value) {
fputs("fe_m4_write_error(",e->out); emit_m4_writer_value(e,writer,buffer); fputs(", (unsigned long)",e->out); emit_expr(e,arg); fputs(".tag)",e->out); return;
}
if (verb==' ' && t && t->kind==FE_TYPE_BOOL) {
fputs("fe_m4_write_bool(",e->out); emit_m4_writer_value(e,writer,buffer); fputs(", ",e->out); emit_expr(e,arg); fputc(')',e->out); return;
}
if (verb==' ' && t && t->kind==FE_TYPE_CHAR) {
fputs("fe_m4_write_char(",e->out); emit_m4_writer_value(e,writer,buffer); fputs(", (unsigned char)",e->out); emit_expr(e,arg); fputc(')',e->out); return;
}
fputs("fe_m4_write_int(",e->out); emit_m4_writer_value(e,writer,buffer); fputs(", (long)",e->out); emit_expr(e,arg); fputc(')',e->out);
}
static void emit_m4_builtin(FeEmitter *e, FeNode *n)
{
const char *fmt=n->aux_text;
FeNode *fmt_node=n->children;
FeNode *arg;
FeNode *writer_arg=0;
FeNode *buffer_arg=0;
unsigned long i,j,last=1;
unsigned count=0;
int verb;
int error_value;
int first=1;
int is_print=strcmp(n->text,"@print")==0;
int is_sprint=strcmp(n->text,"@sprint")==0;
if (!fmt) { fputs("0",e->out); return; }
if (is_print) writer_arg=0;
else if (is_sprint) { buffer_arg=fmt_node; fmt_node=fmt_node ? fmt_node->next : 0; }
else { writer_arg=fmt_node; fmt_node=fmt_node ? fmt_node->next : 0; }
arg=fmt_node ? fmt_node->next : 0;
error_value=0;
fputc('(',e->out);
if (!is_print && !is_sprint) {
fputs("fe_m4_error=0",e->out);
first=0;
}
if (is_sprint) {
fputs("fe_m4_sprint_begin((fe_m4_slice*)&",e->out); emit_expr(e,buffer_arg); fputs(")",e->out);
first=0;
}
i=1;
while(fmt[i] && fmt[i]!='"') {
if(fmt[i]=='\\') { ++i; if(fmt[i]) ++i; continue; }
if(fmt[i]=='{' && fmt[i+1]=='{') { i+=2; continue; }
if(fmt[i]=='}' && fmt[i+1]=='}') { i+=2; continue; }
if(fmt[i]=='{') {
j=i+1; while(fmt[j] && fmt[j]!='}') ++j;
if(!fmt[j]) break;
if(i>last) {
if(!first) fputs(", ",e->out);
if (!is_print && !is_sprint)
fputs("fe_m4_error ? fe_m4_error : (fe_m4_error = ",e->out);
if(is_print) { fputs("fe_m4_write_cstr(fe_m4_stdout_writer(), ",e->out); emit_m4_piece(e->out,fmt,last,i); fputc(')',e->out); }
else { fputs("fe_m4_write_cstr(",e->out); if(is_sprint) emit_m4_writer_value(e,buffer_arg,1); else emit_m4_writer(e,writer_arg,0); fputs(", ",e->out); emit_m4_piece(e->out,fmt,last,i); fputc(')',e->out); }
if (!is_print && !is_sprint) fputc(')',e->out);
first=0;
}
verb=(j==i+1) ? ' ' : (j==i+2 ? (unsigned char)fmt[i+1] : '?');
if(arg) {
error_value=arg->sem_type && arg->sem_type->kind==FE_TYPE_ENUM &&
arg->sem_type->is_error;
if(!first) fputs(", ",e->out);
if (!is_print && !is_sprint)
fputs("fe_m4_error ? fe_m4_error : (fe_m4_error = ",e->out);
if(is_print) emit_m4_arg(e,arg,verb,0,0,error_value);
else {
/* The writer expression is repeated intentionally; it is
a value wrapper and does not re-evaluate source args. */
emit_m4_arg(e,arg,verb, is_sprint ? buffer_arg : writer_arg,
is_sprint,error_value);
}
if (!is_print && !is_sprint) fputc(')',e->out);
first=0; arg=arg->next; ++count;
}
last=j+1; i=j+1; continue;
}
++i;
}
if(fmt[i]=='"' && i>last) {
if(!first) fputs(", ",e->out);
if (!is_print && !is_sprint)
fputs("fe_m4_error ? fe_m4_error : (fe_m4_error = ",e->out);
if(is_print) { fputs("fe_m4_write_cstr(fe_m4_stdout_writer(), ",e->out); emit_m4_piece(e->out,fmt,last,i); fputc(')',e->out); }
else { fputs("fe_m4_write_cstr(",e->out); if(is_sprint) emit_m4_writer_value(e,buffer_arg,1); else emit_m4_writer(e,writer_arg,0); fputs(", ",e->out); emit_m4_piece(e->out,fmt,last,i); fputc(')',e->out); }
if (!is_print && !is_sprint) fputc(')',e->out);
first=0;
}
if(first) fputs("0",e->out);
if(is_print) fputs(", (void)0",e->out);
else if(is_sprint) { fputs(", fe_m4_sprint_finish()",e->out); }
fputc(')',e->out);
(void)count;
}
static void emit_lvalue(FeEmitter *e, FeNode *n)
{
FeType *bt;
@@ -300,14 +525,23 @@ 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;
fputs(bt->full_slicer,e->out);
if (bt->kind==FE_TYPE_ARRAY) { fputs("(&",e->out); emit_lvalue(e,n->a); }
else { fputc('(',e->out); emit_expr(e,n->a); }
fputc(')',e->out); return;
}
if (!n->c && bt && bt->tail_slicer) {
fputs(bt->tail_slicer,e->out); fputc('(',e->out); emit_expr(e,n->a); fputs(", ",e->out);
fputs(bt->tail_slicer,e->out);
if (bt->kind==FE_TYPE_ARRAY) { fputs("(&",e->out); emit_lvalue(e,n->a); }
else { fputc('(',e->out); emit_expr(e,n->a); }
fputs(", ",e->out);
if (n->b) emit_expr(e,n->b); else fputs("0",e->out);
fputc(')',e->out); return;
}
fputs(maker,e->out); fputc('(',e->out); emit_expr(e,n->a); fputs(", ",e->out);
fputs(maker,e->out);
if (bt && bt->kind==FE_TYPE_ARRAY) { fputs("(&",e->out); emit_lvalue(e,n->a); }
else { fputc('(',e->out); emit_expr(e,n->a); }
fputs(", ",e->out);
if(n->b) emit_expr(e,n->b); else fputs("0",e->out);
fputs(", ",e->out);
if(n->c) emit_expr(e,n->c);
@@ -375,6 +609,7 @@ static void emit_expr(FeEmitter *e, FeNode *n)
}
case FE_N_UNARY:
op = n->text ? n->text : "";
if (strcmp(op, "try") == 0) { emit_expr(e,n->a); break; }
if (strcmp(op, "not") == 0) fputs("(!", e->out);
else {
fputc('(', e->out);
@@ -405,7 +640,16 @@ static void emit_expr(FeEmitter *e, FeNode *n)
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; }
if(n->text && (strcmp(n->text,"@print")==0 || strcmp(n->text,"@fprint")==0 || strcmp(n->text,"@sprint")==0)) { emit_m4_builtin(e,n); special=1; }
else if(n->a && n->a->kind==FE_N_MEMBER && n->a->a &&
n->a->a->kind==FE_N_IDENT && n->a->a->text &&
strcmp(n->a->a->text,"io")==0 && n->a->b && n->a->b->text &&
strcmp(n->a->b->text,"buf_writer")==0 && n->children) { emit_m4_writer(e,n->children,1); special=1; }
else if(n->a && n->a->kind==FE_N_MEMBER && n->a->a &&
n->a->a->kind==FE_N_IDENT && n->a->a->text &&
strcmp(n->a->a->text,"io")==0 && n->a->b && n->a->b->text &&
strcmp(n->a->b->text,"null_writer")==0) { fputs("fe_m4_null_writer()",e->out); special=1; }
else if(!n->a && n->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 : "");
@@ -424,7 +668,10 @@ static void emit_expr(FeEmitter *e, FeNode *n)
}
case FE_N_MEMBER: {
FeVariantType *v;
if(n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_REF &&
if(n->a && n->a->kind==FE_N_IDENT && n->a->text &&
strcmp(n->a->text,"io")==0 && n->b && n->b->text &&
strcmp(n->b->text,"stdout")==0) fputs("fe_m4_stdout_writer()",e->out);
else if(n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_REF &&
n->b && n->b->text && strcmp(n->b->text,"^")==0) {
fputs("(*",e->out); emit_expr(e,n->a); fputs(")",e->out);
} else if(n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_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); }
@@ -443,6 +690,16 @@ static void emit_decl(FeEmitter *e, FeNode *n)
fputs(ctype(e, n), e->out);
fputc(' ', e->out);
fputs(cname(n, "fe_local"), e->out);
if (n->kind==FE_N_CONST && n->b) {
fputs(" = ",e->out);
if (n->b->kind==FE_N_LITERAL && n->b->text && n->b->text[0]=='"') {
fputs("{ (const unsigned char*)",e->out);
emit_c_literal(e->out,n->b->text,1);
fputs(", sizeof(",e->out);
emit_c_literal(e->out,n->b->text,1);
fputs(")-1 }",e->out);
} else emit_expr(e,n->b);
}
fputs(";\n", e->out);
}
@@ -463,9 +720,15 @@ static void emit_block(FeEmitter *e, FeNode *n)
++e->indent;
/* C89 requires declarations before statements in each actual block. */
for (x = n->children; x; x = x->next)
if (x->kind == FE_N_LET || x->kind == FE_N_VAR) emit_decl(e, x);
if (x->kind == FE_N_LET || x->kind == FE_N_VAR ||
x->kind == FE_N_CONST) emit_decl(e, x);
for (x = n->children; x; x = x->next) emit_stmt(e, x);
--e->indent;
if (e->fallthrough_block==n) {
pad(e);
fputs("return 0;\n",e->out);
e->fallthrough_block=0;
}
pad(e);
fputc('}', e->out);
}
@@ -531,8 +794,15 @@ static void emit_stmt(FeEmitter *e, FeNode *n)
break;
case FE_N_EXPR_STMT:
pad(e);
emit_expr(e, n->a);
fputs(";\n", e->out);
if (n->a && n->a->kind==FE_N_UNARY && n->a->text &&
strcmp(n->a->text,"try")==0 && n->a->a) {
fputs("if ((fe_m4_error = ",e->out);
emit_expr(e,n->a->a);
fputs(") != 0) return fe_m4_error;\n",e->out);
} else {
emit_expr(e, n->a);
fputs(";\n", e->out);
}
break;
case FE_N_RETURN:
pad(e);
@@ -630,6 +900,10 @@ static void emit_fn(FeEmitter *e, FeNode *fn, int prototype)
if (prototype) fputs(";\n", e->out);
else {
fputs(" ", e->out);
if (fn->sem_type && fn->sem_type->kind==FE_TYPE_ERROR_UNION &&
fn->sem_type->error_value &&
fn->sem_type->error_value->kind==FE_TYPE_VOID)
e->fallthrough_block=fn->c;
emit_block(e, fn->c);
fputc('\n', e->out);
}
@@ -658,19 +932,26 @@ void fe_emit_c_init(FeEmitter *e, FILE *out, FeCheck *check,
e->indent = 0;
e->no_checks = no_checks;
e->temp_serial = 0;
e->fallthrough_block = 0;
}
void fe_emit_c_program(FeEmitter *e)
{
FeNode *n;
FeNode *main_fn = 0;
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);
FeType *type;
int need_m4;
need_m4=node_uses_m4(e->check->ast->root);
for (type=e->check->types.types; type; type=type->next)
if (strcmp(type->name,"io.Writer")==0) need_m4=1;
fputs("/* generated by fec M4 */\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.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);
if (need_m4) emit_m4_runtime(e);
emit_type_helpers(e);
for (n = e->check->ast->root ? e->check->ast->root->children : 0;
n; n = n->next) {
@@ -680,7 +961,14 @@ void fe_emit_c_program(FeEmitter *e)
fputs(cname(n, "fe_global"), e->out);
if (n->b) {
fputs(" = ", e->out);
emit_expr(e, n->b);
if (n->kind==FE_N_CONST && n->b->kind==FE_N_LITERAL &&
n->b->text && n->b->text[0]=='"') {
fputs("{ (const unsigned char*)",e->out);
emit_c_literal(e->out,n->b->text,1);
fputs(", sizeof(",e->out);
emit_c_literal(e->out,n->b->text,1);
fputs(")-1 }",e->out);
} else emit_expr(e, n->b);
}
fputs(";\n", e->out);
}
+1
View File
@@ -10,6 +10,7 @@ typedef struct FeEmitter {
int indent;
int no_checks;
unsigned temp_serial;
FeNode *fallthrough_block;
} FeEmitter;
void fe_emit_c_init(FeEmitter *e, FILE *out, FeCheck *check,
+10 -3
View File
@@ -58,7 +58,14 @@ static FeNode *type(FeParser *p)
}
if (is_name(p) || is(p,FE_TOK_TYPE)) {
next(p); n=toknode(p,FE_N_TYPE,t);
if (eat(p,FE_TOK_DOT)) { if(is_name(p)){FeNode *m=toknode(p,FE_N_IDENT,p->previous); n->a=m; next(p);} else error(p,"expected type name after '.'"); }
if (eat(p,FE_TOK_DOT)) {
if(is_name(p)) {
FeToken mt=p->current;
FeNode *m=toknode(p,FE_N_IDENT,mt);
n->a=m;
next(p);
} else error(p,"expected type name after '.'");
}
if (eat(p,FE_TOK_BANG)) { FeNode *e=toknode(p,FE_N_TYPE,p->previous); e->a=n; e->b=type(p); return e; }
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 ')' in generic type"); }
return n;
@@ -147,7 +154,7 @@ static FeNode *postfix(FeParser *p)
static FeNode *expr(FeParser *p, int minprec)
{
FeToken t=p->current; FeNode *left,*n; int prec;
if(is(p,FE_TOK_MINUS)||is(p,FE_TOK_NOT)||is(p,FE_TOK_XOR)||is(p,FE_TOK_AND)||is(p,FE_TOK_STAR)||is(p,FE_TOK_TRY)) { next(p); n=toknode(p,FE_N_UNARY,t); n->a=expr(p,11); left=n; }
if(is(p,FE_TOK_MINUS)||is(p,FE_TOK_NOT)||is(p,FE_TOK_XOR)||is(p,FE_TOK_AND)||is(p,FE_TOK_STAR)||is(p,FE_TOK_TRY)) { next(p); n=toknode(p,FE_N_UNARY,t); if(t.kind==FE_TOK_AND && eat(p,FE_TOK_MUT)) n->text=fe_arena_strdup(&p->ast->arena,"&mut",4); n->a=expr(p,11); left=n; }
else left=postfix(p);
for(;;) { t=p->current;prec=precedence(t.kind);if(prec<=minprec)break;next(p);n=toknode(p,FE_N_BINARY,t);n->a=left;if(t.kind==FE_TOK_CATCH && eat(p,FE_TOK_OR)){if(is_name(p))n->b=toknode(p,FE_N_IDENT,p->current),next(p);else error(p,"expected catch binding");want(p,FE_TOK_OR,"expected '|' after catch binding");n->c=block(p);}else n->b=expr(p,prec);left=n; }
return left;
@@ -229,7 +236,7 @@ static FeNode *statement(FeParser *p)
if(is(p,FE_TOK_LBRACE)) return block(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_CONST)) { n=toknode(p,FE_N_CONST,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 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=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;}
+51 -2
View File
@@ -22,10 +22,12 @@ static FeType *new_type(FeTypeCtx *ctx, const char *name, FeTypeKind kind)
t->bits = 0;
t->is_unsigned = 0;
t->packed = 0;
t->is_error = 0;
t->length = 0;
t->size = 0;
t->align = 1;
t->elem = 0;
t->error_value = 0;
t->ref_mut = 0;
t->fields = 0;
t->field_count = 0;
@@ -60,6 +62,9 @@ FeType *fe_type_intern(FeTypeCtx *ctx, const char *name)
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, "io.Writer") == 0) {
kind = FE_TYPE_STRUCT;
}
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) {
@@ -73,6 +78,12 @@ FeType *fe_type_intern(FeTypeCtx *ctx, const char *name)
if (!t) return 0;
t->bits = bits;
t->is_unsigned = uns;
if (strcmp(name,"io.Writer")==0) {
t->cname=fe_arena_strdup(ctx->arena,"fe_writer",10);
t->size=4;
t->align=1;
return t;
}
if (kind == FE_TYPE_STR) {
t->cname = fe_arena_strdup(ctx->arena, "fe_str", 6);
t->elem = fe_type_intern(ctx, "u8");
@@ -155,6 +166,19 @@ FeType *fe_type_ref(FeTypeCtx *ctx, FeType *elem, int mutable)
return t;
}
FeType *fe_type_error_union(FeTypeCtx *ctx, FeType *value)
{
char key[128];
FeType *t;
sprintf(key,"!%s",value ? value->name : "?");
t=fe_type_intern(ctx,key);
if(t->kind==FE_TYPE_UNKNOWN) {
t->kind=FE_TYPE_ERROR_UNION;
t->error_value=value;
}
return t;
}
FeType *fe_type_declare_struct(FeTypeCtx *ctx, const FeNode *node, int packed)
{
FeType *t;
@@ -227,7 +251,10 @@ FeType *fe_type_declare_enum(FeTypeCtx *ctx, const FeNode *node)
t->variants[i].name = v->text;
t->variants[i].fields = 0;
t->variants[i].field_count = 0;
t->variants[i].tag = i;
if (node->kind==FE_N_ERROR_DECL && v->a &&
v->a->kind==FE_N_LITERAL && v->a->text)
t->variants[i].tag=(unsigned)strtoul(v->a->text,0,0);
else 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) {
@@ -266,6 +293,13 @@ FeType *fe_type_declare_enum(FeTypeCtx *ctx, const FeNode *node)
return t;
}
FeType *fe_type_declare_error(FeTypeCtx *ctx, const FeNode *node)
{
FeType *t=fe_type_declare_enum(ctx,node);
if (t) t->is_error=1;
return t;
}
static unsigned long round_up(unsigned long x, unsigned a)
{
unsigned long rem;
@@ -302,6 +336,10 @@ static void layout_type(FeTypeCtx *ctx, FeType *t)
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_ERROR_UNION) {
t->size = 2; t->align = ctx->pointer_bits == 16 ? 1U : 2U;
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;
}
@@ -395,12 +433,18 @@ FeVariantType *fe_type_variant(FeType *t, const char *name)
FeType *fe_type_from_ast(FeTypeCtx *ctx, const FeNode *node)
{
unsigned long length = 0;
char qualified[128];
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->a && node->a->kind==FE_N_IDENT && node->text &&
strcmp(node->text,"io")==0 && node->a->text) {
sprintf(qualified,"%s.%s",node->text,node->a->text);
return fe_type_intern(ctx,qualified);
}
if (node->text && (strcmp(node->text, "&") == 0 ||
strcmp(node->text, "&mut") == 0))
return fe_type_ref(ctx, fe_type_from_ast(ctx,node->a),
@@ -413,8 +457,12 @@ FeType *fe_type_from_ast(FeTypeCtx *ctx, const FeNode *node)
}
return fe_type_slice(ctx, fe_type_from_ast(ctx, node->b));
}
if (node->text && strcmp(node->text, "!") == 0)
/* Prefix !T stores T in a; the E!T spelling stores its success
type in b and the error type in a. */
return fe_type_error_union(ctx, fe_type_from_ast(
ctx, node->b ? node->b : node->a));
if (node->text && (strcmp(node->text, "?") == 0 ||
strcmp(node->text, "!") == 0 ||
strcmp(node->text, "^") == 0 ||
strcmp(node->text, "&") == 0 ||
strcmp(node->text, "&mut") == 0 ||
@@ -447,6 +495,7 @@ 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_ERROR_UNION) return "unsigned short";
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];
+6 -1
View File
@@ -4,7 +4,7 @@
#include "ast.h"
typedef enum FeTypeKind {
FE_TYPE_ERROR, FE_TYPE_VOID, FE_TYPE_BOOL, FE_TYPE_CHAR, FE_TYPE_INT,
FE_TYPE_ERROR, FE_TYPE_ERROR_UNION, 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;
@@ -40,10 +40,13 @@ struct FeType {
unsigned bits;
int is_unsigned;
int packed;
int is_error;
unsigned long length;
unsigned long size;
unsigned align;
FeType *elem;
/* Success value for an error union; !void is represented directly. */
FeType *error_value;
int ref_mut;
FeFieldType *fields;
unsigned field_count;
@@ -68,8 +71,10 @@ 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_error_union(FeTypeCtx *ctx, FeType *value);
FeType *fe_type_declare_struct(FeTypeCtx *ctx, const FeNode *node, int packed);
FeType *fe_type_declare_enum(FeTypeCtx *ctx, const FeNode *node);
FeType *fe_type_declare_error(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);
+39
View File
@@ -189,6 +189,45 @@ 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
if exist TESTS\M4\FORMAT.C del TESTS\M4\FORMAT.C
if exist TESTS\M4\FORMAT.EXE del TESTS\M4\FORMAT.EXE
fec.exe --target=bits32 --emit-c TESTS\M4\FORMAT.FE -o TESTS\M4\FORMAT.C > nul
if errorlevel 1 goto test_fail
wcl386 -q -za -wx -wcd=202 -bt=dos -fe=TESTS\M4\FORMAT.EXE TESTS\M4\FORMAT.C
if errorlevel 1 goto test_fail
TESTS\M4\FORMAT.EXE > nul
if errorlevel 1 goto test_fail
fec.exe --target=bits32 --emit-c TESTS\M4\TRY-FPR.FE -o TESTS\M4\TRY-FPR.C > nul
if errorlevel 1 goto test_fail
wcl386 -q -za -wx -wcd=202 -bt=dos -fe=TESTS\M4\TRY-FPR.EXE TESTS\M4\TRY-FPR.C
if errorlevel 1 goto test_fail
TESTS\M4\TRY-FPR.EXE > nul
if errorlevel 1 goto test_fail
fec.exe --target=bits32 --emit-c TESTS\M4\PROP.FE -o TESTS\M4\PROP.C > nul
if errorlevel 1 goto test_fail
wcl386 -q -za -wx -wcd=202 -bt=dos -fe=TESTS\M4\PROP.EXE TESTS\M4\PROPTEST.C
if errorlevel 1 goto test_fail
TESTS\M4\PROP.EXE > nul
if errorlevel 1 goto test_fail
fec.exe --target=bits32 --emit-c TESTS\M4\BAD-ARI.FE -o TESTS\M4\BAD-ARI.C > nul
if not errorlevel 1 goto test_fail
fec.exe --target=bits32 --emit-c TESTS\M4\BAD-VERB.FE -o TESTS\M4\BAD-VERB.C > nul
if not errorlevel 1 goto test_fail
fec.exe --target=bits32 --emit-c TESTS\M4\BAD-RUN.FE -o TESTS\M4\BAD-RUN.C > nul
if not errorlevel 1 goto test_fail
fec.exe --target=bits32 --emit-c TESTS\M4\BAD-TYP.FE -o TESTS\M4\BAD-TYP.C > nul
if not errorlevel 1 goto test_fail
fec.exe --target=bits32 --emit-c TESTS\M4\BAD-TRY.FE -o TESTS\M4\BAD-TRY.C > nul
if not errorlevel 1 goto test_fail
fec.exe --target=bits32 --emit-c TESTS\M4\BAD-WRI.FE -o TESTS\M4\BAD-WRI.C > nul
if not errorlevel 1 goto test_fail
fec.exe --target=bits32 --emit-c TESTS\M4\BAD-MANY.FE -o TESTS\M4\BAD-MANY.C > nul
if not errorlevel 1 goto test_fail
fec.exe --target=bits32 --emit-c TESTS\M4\BAD-OPEN.FE -o TESTS\M4\BAD-OPEN.C > nul
if not errorlevel 1 goto test_fail
fec.exe --target=bits32 --emit-c TESTS\M4\BAD-CLS.FE -o TESTS\M4\BAD-CLS.C > nul
if not errorlevel 1 goto test_fail
echo OK>TEST.OK
cd C:\FEC
goto test_done
+6
View File
@@ -0,0 +1,6 @@
unit m4_bad_arity;
fn main() -> i32 {
@print("{} {}", 1);
return 0;
}
+6
View File
@@ -0,0 +1,6 @@
unit m4_bad_cls;
fn main() -> i32 {
@print("}", 1);
return 0;
}
+6
View File
@@ -0,0 +1,6 @@
unit m4_bad_many;
fn main() -> i32 {
@print("{}", 1, 2);
return 0;
}
+6
View File
@@ -0,0 +1,6 @@
unit m4_bad_open;
fn main() -> i32 {
@print("{", 1);
return 0;
}
+7
View File
@@ -0,0 +1,7 @@
unit m4_bad_runtime;
fn main() -> i32 {
var fmt: str = "{}";
@print(fmt, 1);
return 0;
}
+6
View File
@@ -0,0 +1,6 @@
unit m4_bad_try;
fn main() -> i32 {
try @print("nope");
return 0;
}
+9
View File
@@ -0,0 +1,9 @@
unit m4_bad_type;
struct Point { x: i32, }
fn main() -> i32 {
let p: Point = Point{ x: 1 };
@print("{}", p);
return 0;
}
+6
View File
@@ -0,0 +1,6 @@
unit m4_bad_verb;
fn main() -> i32 {
@print("{q}", 1);
return 0;
}
+7
View File
@@ -0,0 +1,7 @@
unit m4_bad_writer;
fn main() -> i32 {
var x: i32 = 0;
@fprint(&mut x, "bad");
return 0;
}
+30
View File
@@ -0,0 +1,30 @@
unit m4_format;
const FMT: str = "n={} hex={x} c={c} s={s} b={b} {{ok}}\n";
fn main() -> i32 {
var raw: [8]u8 = [0, 0, 0, 0, 0, 0, 0, 0];
var raw2: [8]u8 = [0, 0, 0, 0, 0, 0, 0, 0];
var raw3: [8]u8 = [0, 0, 0, 0, 0, 0, 0, 0];
var raw4: [8]u8 = [0, 0, 0, 0, 0, 0, 0, 0];
var raw5: [8]u8 = [0, 0, 0, 0, 0, 0, 0, 0];
var buf: []u8 = raw[..];
var buf2: []u8 = raw2[..];
var buf3: []u8 = raw3[..];
var buf4: []u8 = raw4[..];
var buf5: []u8 = raw5[..];
let w: io.Writer = io.buf_writer(&mut buf);
const LOCAL_FMT: str = "value={}\n";
@print(FMT, 7, 15, 'A', "yes", true);
@fprint(&mut w, LOCAL_FMT, 12);
let n: usize = @sprint(buf2, "A\x42\u0043defghi");
let n2: usize = @sprint(buf3, "xy");
let inner_n: usize = @sprint(buf4, "xy");
let outer_n: usize = @sprint(buf5, "n={}", @sprint(buf4, "xy"));
if n == 8 and buf2[0] == ('A' as u8) and
buf2[1] == ('B' as u8) and buf2[2] == ('C' as u8) and
n2 == 2 and buf3[0] == ('x' as u8) and
inner_n == 2 and outer_n == 3 and
buf5[0] == ('n' as u8) and buf5[2] == ('2' as u8) { return 0; }
return 1;
}
+5
View File
@@ -0,0 +1,5 @@
unit m4_prop;
pub fn propagate(w: &mut io.Writer) -> !void {
try @fprint(w, "a{}b", 1);
}
+23
View File
@@ -0,0 +1,23 @@
#include "prop.c"
static unsigned short calls;
static unsigned short fail_write(void *ctx, const unsigned char *p,
unsigned long n)
{
(void)ctx;
(void)p;
(void)n;
++calls;
return calls == 1 ? 7 : 0;
}
int main(void)
{
fe_writer w;
unsigned short result;
w.ctx=0;
w.write_fn=fail_write;
result=fe_m4_prop_propagate(&w);
return (result==7 && calls==1) ? 0 : 1;
}
+8
View File
@@ -0,0 +1,8 @@
unit m4_try_fprint;
fn main() -> !void {
var raw: [4]u8 = [0, 0, 0, 0];
var buf: []u8 = raw[..];
let w: io.Writer = io.buf_writer(&mut buf);
try @fprint(&mut w, "ok");
}
+30
View File
@@ -61,3 +61,33 @@ for f in badfld badmat badarr badcycle badstr badchar badfield badindex; do
fi
done
echo "M3 tests: structs, enums, arrays, slices, str, match, and bounds passed"
m4tmp=$(mktemp -d)
trap 'rm -rf "$m2tmp" "$m3tmp" "$m4tmp"' EXIT HUP INT TERM
for f in format; do
"$root"/fec --target=bits32 --emit-c "$root"/tests/m4/$f.fe -o "$m4tmp/$f.c"
${CC:-cc} -std=c89 -pedantic "$m4tmp/$f.c" -o "$m4tmp/$f"
"$m4tmp/$f" >"$m4tmp/$f.out"
done
for f in try-fprint; do
"$root"/fec --target=bits32 --emit-c "$root"/tests/m4/$f.fe -o "$m4tmp/$f.c"
${CC:-cc} -std=c89 -pedantic "$m4tmp/$f.c" -o "$m4tmp/$f"
"$m4tmp/$f"
done
"$root"/fec --target=bits32 --emit-c "$root"/tests/m4/prop.fe -o "$m4tmp/prop.c"
cp "$root"/tests/m4/proptest.c "$m4tmp/proptest.c"
${CC:-cc} -std=c89 -pedantic "$m4tmp/proptest.c" -o "$m4tmp/prop"
"$m4tmp/prop"
for f in bad-arity bad-verb bad-runtime bad-type bad-try bad-writer; do
if "$root"/fec --target=bits32 --emit-c "$root"/tests/m4/$f.fe -o "$m4tmp/$f.c" >/dev/null 2>/dev/null; then
echo "FAIL (accepted M4 semantic error): $f.fe"
exit 1
fi
done
for f in bad-many bad-open bad-cls; do
if "$root"/fec --target=bits32 --emit-c "$root"/tests/m4/$f.fe -o "$m4tmp/$f.c" >/dev/null 2>/dev/null; then
echo "FAIL (accepted M4 format-brace error): $f.fe"
exit 1
fi
done
echo "M4 tests: formatting builtins passed"
+28
View File
@@ -8,6 +8,7 @@ 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 not exist C:\FEC\TESTS\M4 md C:\FEC\TESTS\M4
if exist C:\FEC\VM.FAIL del C:\FEC\VM.FAIL
if exist C:\FEC\STAGE.FAIL del C:\FEC\STAGE.FAIL
@@ -142,6 +143,33 @@ 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
copy D:\FEC\TESTS\M4\FORMAT.FE C:\FEC\TESTS\M4\FORMAT.FE > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\TESTS\M4\BAD-ARI~1.FE C:\FEC\TESTS\M4\BAD-ARI.FE > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\TESTS\M4\BAD-VERB.FE C:\FEC\TESTS\M4\BAD-VERB.FE > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\TESTS\M4\BAD-RUN~1.FE C:\FEC\TESTS\M4\BAD-RUN.FE > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\TESTS\M4\BAD-TYP~1.FE C:\FEC\TESTS\M4\BAD-TYP.FE > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\TESTS\M4\BAD-TRY.FE C:\FEC\TESTS\M4\BAD-TRY.FE > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\TESTS\M4\TRY-FPR~1.FE C:\FEC\TESTS\M4\TRY-FPR.FE > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\TESTS\M4\BAD-WRI~1.FE C:\FEC\TESTS\M4\BAD-WRI.FE > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\TESTS\M4\PROP.FE C:\FEC\TESTS\M4\PROP.FE > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\TESTS\M4\PROPTEST.C C:\FEC\TESTS\M4\PROPTEST.C > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\TESTS\M4\BAD-MANY.FE C:\FEC\TESTS\M4\BAD-MANY.FE > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\TESTS\M4\BAD-OPEN.FE C:\FEC\TESTS\M4\BAD-OPEN.FE > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\TESTS\M4\BAD-CLS.FE C:\FEC\TESTS\M4\BAD-CLS.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