feat: implement M4 formatting builtins
This commit is contained in:
+187
-1
@@ -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) {
|
||||
|
||||
+300
-12
@@ -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);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
unit m4_bad_arity;
|
||||
|
||||
fn main() -> i32 {
|
||||
@print("{} {}", 1);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
unit m4_bad_cls;
|
||||
|
||||
fn main() -> i32 {
|
||||
@print("}", 1);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
unit m4_bad_many;
|
||||
|
||||
fn main() -> i32 {
|
||||
@print("{}", 1, 2);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
unit m4_bad_open;
|
||||
|
||||
fn main() -> i32 {
|
||||
@print("{", 1);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
unit m4_bad_runtime;
|
||||
|
||||
fn main() -> i32 {
|
||||
var fmt: str = "{}";
|
||||
@print(fmt, 1);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
unit m4_bad_try;
|
||||
|
||||
fn main() -> i32 {
|
||||
try @print("nope");
|
||||
return 0;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
unit m4_bad_verb;
|
||||
|
||||
fn main() -> i32 {
|
||||
@print("{q}", 1);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
unit m4_bad_writer;
|
||||
|
||||
fn main() -> i32 {
|
||||
var x: i32 = 0;
|
||||
@fprint(&mut x, "bad");
|
||||
return 0;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
unit m4_prop;
|
||||
|
||||
pub fn propagate(w: &mut io.Writer) -> !void {
|
||||
try @fprint(w, "a{}b", 1);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user