wip: advance M5 ownership cleanup
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
---
|
||||
description: "Fast read-only search agent for locating code. Use it to find files by pattern (eg. \"src/components/**/*.tsx\"), grep for symbols or keywords (eg. \"API endpoints\"), or answer \"where is X defined / which files reference Y.\" Do NOT use it for code review, design-doc auditing, cross-file consistency checks, or open-ended analysis — it reads excerpts rather than whole files and will miss content past its read window. When calling, specify search breadth: \"quick\" for a single targeted lookup, \"medium\" for moderate exploration, or \"very thorough\" to search across multiple locations and naming conventions."
|
||||
display_name: Explore
|
||||
tools: read, bash, grep, find, ls
|
||||
model: gpt-5.6-luna
|
||||
prompt_mode: replace
|
||||
---
|
||||
|
||||
# CRITICAL: READ-ONLY MODE - NO FILE MODIFICATIONS
|
||||
You are a file search specialist. You excel at thoroughly navigating and exploring codebases.
|
||||
Your role is EXCLUSIVELY to search and analyze existing code. You do NOT have access to file editing tools.
|
||||
|
||||
You are STRICTLY PROHIBITED from:
|
||||
- Creating new files
|
||||
- Modifying existing files
|
||||
- Deleting files
|
||||
- Moving or copying files
|
||||
- Creating temporary files anywhere, including /tmp
|
||||
- Using redirect operators (>, >>, |) or heredocs to write to files
|
||||
- Running ANY commands that change system state
|
||||
|
||||
Use Bash ONLY for read-only operations: ls, git status, git log, git diff, find, cat, head, tail.
|
||||
|
||||
# Tool Usage
|
||||
- Use the find tool for file pattern matching (NOT the bash find command)
|
||||
- Use the grep tool for content search (NOT bash grep/rg command)
|
||||
- Use the read tool for reading files (NOT bash cat/head/tail)
|
||||
- Use Bash ONLY for read-only operations
|
||||
- Make independent tool calls in parallel for efficiency
|
||||
- Adapt search approach based on thoroughness level specified
|
||||
|
||||
# Output
|
||||
- Use absolute file paths in all references
|
||||
- Report findings as regular messages
|
||||
- Do not use emojis
|
||||
- Be thorough and precise
|
||||
+177
-5
@@ -12,6 +12,7 @@ struct FeSym {
|
||||
FeNode *fn;
|
||||
int mutable;
|
||||
int initialized;
|
||||
int moved;
|
||||
FeNode *decl;
|
||||
};
|
||||
|
||||
@@ -22,11 +23,15 @@ struct FeScope {
|
||||
unsigned capacity;
|
||||
};
|
||||
|
||||
static FeSym *find_symbol(FeScope *scope, const char *name);
|
||||
|
||||
typedef struct FeCheckerState {
|
||||
FeCheck *c;
|
||||
FeScope *scope;
|
||||
FeScope *globals;
|
||||
FeType *ret;
|
||||
unsigned loop_depth;
|
||||
unsigned defer_depth;
|
||||
} FeCheckerState;
|
||||
|
||||
static FeType *unknown(FeCheck *c)
|
||||
@@ -44,6 +49,41 @@ static int known(FeType *t)
|
||||
return t && t->kind != FE_TYPE_UNKNOWN && t->kind != FE_TYPE_ERROR;
|
||||
}
|
||||
|
||||
static int is_copy_type(FeType *t)
|
||||
{
|
||||
unsigned i;
|
||||
if (!t) return 1;
|
||||
if (t->kind==FE_TYPE_OWNED) return 0;
|
||||
if (t->kind==FE_TYPE_REF) return !t->ref_mut;
|
||||
if (t->kind==FE_TYPE_ARRAY) return is_copy_type(t->elem);
|
||||
if (t->kind==FE_TYPE_STRUCT) {
|
||||
if (t->has_drop) return 0;
|
||||
for (i=0;i<t->field_count;i++) if (!is_copy_type(t->fields[i].type)) return 0;
|
||||
}
|
||||
if (t->kind==FE_TYPE_ENUM)
|
||||
for (i=0;i<t->variant_count;i++) {
|
||||
unsigned j;
|
||||
for (j=0;j<t->variants[i].field_count;j++)
|
||||
if (!is_copy_type(t->variants[i].fields[j].type)) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void mark_moved(FeCheckerState *s, FeNode *n, FeType *t)
|
||||
{
|
||||
FeSym *sym;
|
||||
if (!n || !t || is_copy_type(t) || n->kind!=FE_N_IDENT) return;
|
||||
sym=find_symbol(s->scope,n->text ? n->text : "");
|
||||
if (sym) {
|
||||
if (s->defer_depth) {
|
||||
if (sym->decl) sym->decl->flags |= 0x200U;
|
||||
} else {
|
||||
sym->moved=1;
|
||||
if (sym->decl) sym->decl->flags |= 0x100U;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int compatible(FeType *want, FeType *got, FeNode *value)
|
||||
{
|
||||
FeNode *item;
|
||||
@@ -193,6 +233,7 @@ static FeSym *add_symbol(FeCheckerState *s, FeScope *scope,
|
||||
sym->fn = fn;
|
||||
sym->mutable = mutable;
|
||||
sym->initialized = initialized;
|
||||
sym->moved = 0;
|
||||
sym->decl = decl;
|
||||
if (decl) {
|
||||
decl->cname = cname;
|
||||
@@ -217,6 +258,47 @@ static FeType *check_expr(FeCheckerState *s, FeNode *n);
|
||||
static void check_match(FeCheckerState *s, FeNode *n);
|
||||
static void check_stmt(FeCheckerState *s, FeNode *n);
|
||||
|
||||
typedef struct FeFlowSlot {
|
||||
FeSym *sym;
|
||||
int moved;
|
||||
int initialized;
|
||||
} FeFlowSlot;
|
||||
|
||||
static unsigned flow_capture(FeScope *scope, FeFlowSlot *slots, unsigned cap)
|
||||
{
|
||||
unsigned count=0;
|
||||
unsigned i;
|
||||
FeScope *p;
|
||||
for (p=scope; p && count<cap; p=p->parent)
|
||||
for (i=0; i<p->count && count<cap; ++i) {
|
||||
slots[count].sym=&p->items[i];
|
||||
slots[count].moved=p->items[i].moved;
|
||||
slots[count].initialized=p->items[i].initialized;
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static void flow_restore(FeFlowSlot *slots, unsigned count)
|
||||
{
|
||||
unsigned i;
|
||||
for (i=0; i<count; ++i) {
|
||||
slots[i].sym->moved=slots[i].moved;
|
||||
slots[i].sym->initialized=slots[i].initialized;
|
||||
}
|
||||
}
|
||||
|
||||
static void flow_merge(FeFlowSlot *base, FeFlowSlot *left, FeFlowSlot *right,
|
||||
unsigned count)
|
||||
{
|
||||
unsigned i;
|
||||
for (i=0; i<count; ++i) {
|
||||
base[i].sym->moved = left[i].moved==1 && right[i].moved==1 ? 1 :
|
||||
(left[i].moved || right[i].moved ? 2 : 0);
|
||||
base[i].sym->initialized = left[i].initialized && right[i].initialized;
|
||||
}
|
||||
}
|
||||
|
||||
static FeNode *find_const_node(FeCheck *c, const char *name)
|
||||
{
|
||||
FeNode *n;
|
||||
@@ -390,6 +472,7 @@ static FeType *check_struct_init(FeCheckerState *s, FeNode *n)
|
||||
}
|
||||
if (!field) { err(s->c,f->loc,"invalid enum payload field"); continue; }
|
||||
v=check_expr(s,f->a);
|
||||
mark_moved(s,f->a,v);
|
||||
if (!compatible(field->type,v,f->a) && v->kind!=FE_TYPE_UNKNOWN) err(s->c,f->loc,"enum payload type mismatch");
|
||||
}
|
||||
} else if (n->children) err(s->c,n->loc,"empty enum variant cannot have payload");
|
||||
@@ -402,6 +485,7 @@ static FeType *check_struct_init(FeCheckerState *s, FeNode *n)
|
||||
field=fe_type_field(t,f->text);
|
||||
if(!field) { err(s->c,f->loc,"invalid struct field"); continue; }
|
||||
v=check_expr(s,f->a);
|
||||
mark_moved(s,f->a,v);
|
||||
if(!compatible(field->type,v,f->a) && v->kind!=FE_TYPE_UNKNOWN) err(s->c,f->loc,"struct field type mismatch");
|
||||
}
|
||||
for(i=0;i<t->field_count;i++) if(!has_field(n->children,t->fields[i].name)) err(s->c,n->loc,"missing struct field");
|
||||
@@ -411,7 +495,7 @@ static FeType *check_struct_init(FeCheckerState *s, FeNode *n)
|
||||
static FeType *check_array_init(FeCheckerState *s, FeNode *n)
|
||||
{
|
||||
FeNode *x; FeType *elem=0; FeType *v; unsigned long count=0;
|
||||
for(x=n->children;x;x=x->next) { v=check_expr(s,x); if(!elem) elem=v; else if(!compatible(elem,v,x)&&v->kind!=FE_TYPE_UNKNOWN) err(s->c,x->loc,"array element type mismatch"); ++count; }
|
||||
for(x=n->children;x;x=x->next) { v=check_expr(s,x); mark_moved(s,x,v); if(!elem) elem=v; else if(!compatible(elem,v,x)&&v->kind!=FE_TYPE_UNKNOWN) err(s->c,x->loc,"array element type mismatch"); ++count; }
|
||||
if(!elem) elem=unknown(s->c);
|
||||
n->sem_type=fe_type_array(&s->c->types,count,elem); return n->sem_type;
|
||||
}
|
||||
@@ -446,6 +530,8 @@ static FeType *check_identifier(FeCheckerState *s, FeNode *n, int read)
|
||||
}
|
||||
n->cname = sym->cname;
|
||||
n->sem_type = sym->type;
|
||||
if (sym->moved == 1) err(s->c,n->loc,"use of moved value");
|
||||
else if (sym->moved == 2) err(s->c,n->loc,"use of possibly moved value");
|
||||
if (read && !sym->initialized && !sym->fn)
|
||||
err(s->c, n->loc, "use of uninitialized variable");
|
||||
return sym->type;
|
||||
@@ -545,6 +631,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->b && n->a->b->text &&
|
||||
strcmp(n->a->b->text,"drop")==0) {
|
||||
err(c,n->loc,"drop may only be invoked by scope cleanup");
|
||||
return unknown(c);
|
||||
}
|
||||
if (n->a && n->a->kind==FE_N_MEMBER && n->a->a &&
|
||||
n->a->a->kind==FE_N_IDENT && n->a->a->text &&
|
||||
strcmp(n->a->a->text,"mem")==0 && n->a->b && n->a->b->text) {
|
||||
FeNode *arg=n->children;
|
||||
if (strcmp(n->a->b->text,"destroy")==0) {
|
||||
a=arg ? check_expr(s,arg) : unknown(c);
|
||||
if (!arg || arg->next || !a || a->kind!=FE_TYPE_OWNED)
|
||||
err(c,n->loc,"mem.destroy requires exactly one owned pointer");
|
||||
else
|
||||
mark_moved(s,arg,a);
|
||||
n->sem_type=fe_type_intern(&c->types,"void");
|
||||
return n->sem_type;
|
||||
}
|
||||
if (strcmp(n->a->b->text,"create")==0) {
|
||||
if (!arg || arg->next || arg->kind!=FE_N_IDENT)
|
||||
err(c,n->loc,"mem.create requires exactly one type argument");
|
||||
a=arg && arg->kind==FE_N_IDENT ?
|
||||
fe_type_owned(&c->types,fe_type_intern(&c->types,arg->text)) :
|
||||
fe_type_owned(&c->types,unknown(c));
|
||||
n->sem_type=fe_type_error_union(&c->types,a);
|
||||
return n->sem_type;
|
||||
}
|
||||
}
|
||||
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 &&
|
||||
@@ -606,6 +720,7 @@ static FeType *check_expr(FeCheckerState *s, FeNode *n)
|
||||
arg = n->children;
|
||||
while (param && arg) {
|
||||
a = check_expr(s, arg);
|
||||
mark_moved(s,arg,a);
|
||||
b = node_type(c, param->a);
|
||||
if (!compatible(b, a, arg) && a->kind != FE_TYPE_UNKNOWN)
|
||||
err(c, arg->loc, "argument type mismatch");
|
||||
@@ -634,6 +749,11 @@ static FeType *check_expr(FeCheckerState *s, FeNode *n)
|
||||
n->sem_type=a->elem;
|
||||
return a->elem;
|
||||
}
|
||||
if (a->kind == FE_TYPE_OWNED && n->b && n->b->text &&
|
||||
strcmp(n->b->text,"^")==0) {
|
||||
n->sem_type=a->elem;
|
||||
return a->elem;
|
||||
}
|
||||
if(a->kind==FE_TYPE_STRUCT) {
|
||||
field=fe_type_field(a,n->b ? n->b->text : "");
|
||||
if(!field) { err(c,n->loc,"unknown struct field"); return unknown(c); }
|
||||
@@ -684,6 +804,11 @@ static FeType *check_lvalue(FeCheckerState *s, FeNode *n, int read)
|
||||
n->sem_type=base->elem;
|
||||
return base->elem;
|
||||
}
|
||||
if (base && base->kind == FE_TYPE_OWNED && n->b && n->b->text &&
|
||||
strcmp(n->b->text,"^")==0) {
|
||||
n->sem_type=base->elem;
|
||||
return base->elem;
|
||||
}
|
||||
if (!lvalue_writable(s,n->a))
|
||||
err(s->c,n->loc,"cannot assign through immutable value");
|
||||
field=base && base->kind==FE_TYPE_STRUCT ? fe_type_field(base,n->b ? n->b->text : "") : 0;
|
||||
@@ -817,7 +942,7 @@ static void check_type_cycle(FeCheck *c, FeType *t)
|
||||
unsigned i;
|
||||
FeType *next;
|
||||
if (!t || t->kind == FE_TYPE_SLICE || t->kind == FE_TYPE_STR ||
|
||||
t->kind == FE_TYPE_REF ||
|
||||
t->kind == FE_TYPE_REF || t->kind == FE_TYPE_OWNED ||
|
||||
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;
|
||||
@@ -889,6 +1014,7 @@ static void check_stmt(FeCheckerState *s, FeNode *n)
|
||||
err(c, n->loc, "initializer type mismatch");
|
||||
if (b->kind == FE_TYPE_VOID)
|
||||
err(c, n->loc, "void expression cannot initialize a variable");
|
||||
mark_moved(s,n->b,b);
|
||||
add_symbol(s, s->scope, n->text, a, 0, 0, 1,
|
||||
local_cname(c, n->text ? n->text : "local"), n);
|
||||
break;
|
||||
@@ -904,6 +1030,7 @@ static void check_stmt(FeCheckerState *s, FeNode *n)
|
||||
err(c, n->loc, "initializer type mismatch");
|
||||
if (b->kind == FE_TYPE_VOID)
|
||||
err(c, n->loc, "void expression cannot initialize a variable");
|
||||
mark_moved(s,n->b,b);
|
||||
initialized = n->b != 0;
|
||||
add_symbol(s, s->scope, n->text, a, 0, 1, initialized,
|
||||
local_cname(c, n->text ? n->text : "local"), n);
|
||||
@@ -913,6 +1040,7 @@ static void check_stmt(FeCheckerState *s, FeNode *n)
|
||||
a = check_lvalue(s, n->a, compound_operator(n->text));
|
||||
if (!compatible(a, b, n->b) && b->kind != FE_TYPE_UNKNOWN)
|
||||
err(c, n->loc, "assignment type mismatch");
|
||||
mark_moved(s,n->b,b);
|
||||
sym = n->a && n->a->kind == FE_N_IDENT ?
|
||||
find_symbol(s->scope, n->a->text) : 0;
|
||||
if (sym && sym->mutable) sym->initialized = 1;
|
||||
@@ -924,27 +1052,67 @@ static void check_stmt(FeCheckerState *s, FeNode *n)
|
||||
(!s->ret || s->ret->kind!=FE_TYPE_ERROR_UNION))
|
||||
err(c,n->loc,"try requires an enclosing error result");
|
||||
break;
|
||||
case FE_N_IF:
|
||||
case FE_N_DEFER:
|
||||
++s->defer_depth;
|
||||
check_stmt(s,n->a);
|
||||
--s->defer_depth;
|
||||
break;
|
||||
case FE_N_IF: {
|
||||
FeFlowSlot base[128], left[128], right[128];
|
||||
unsigned flow_count;
|
||||
a = check_expr(s, n->a);
|
||||
if (known(a) && a->kind != FE_TYPE_BOOL)
|
||||
err(c, n->loc, "if condition must be bool");
|
||||
flow_count=flow_capture(s->scope,base,128);
|
||||
check_stmt(s, n->b);
|
||||
check_stmt(s, n->c);
|
||||
flow_capture(s->scope,left,flow_count);
|
||||
flow_restore(base,flow_count);
|
||||
if (n->c) check_stmt(s, n->c);
|
||||
if (n->c) flow_capture(s->scope,right,flow_count);
|
||||
else {
|
||||
unsigned i;
|
||||
for (i=0;i<flow_count;++i) right[i]=base[i];
|
||||
}
|
||||
flow_merge(base,left,right,flow_count);
|
||||
break;
|
||||
case FE_N_WHILE:
|
||||
}
|
||||
case FE_N_WHILE: {
|
||||
FeFlowSlot base[128], body[128];
|
||||
unsigned flow_count;
|
||||
a = check_expr(s, n->a);
|
||||
if (known(a) && a->kind != FE_TYPE_BOOL)
|
||||
err(c, n->loc, "while condition must be bool");
|
||||
flow_count=flow_capture(s->scope,base,128);
|
||||
if (s->loop_depth < 255U) ++s->loop_depth;
|
||||
check_stmt(s, n->b);
|
||||
if (s->loop_depth) --s->loop_depth;
|
||||
flow_capture(s->scope,body,flow_count);
|
||||
flow_restore(base,flow_count);
|
||||
{
|
||||
unsigned i;
|
||||
for (i=0;i<flow_count;++i) {
|
||||
if (body[i].moved) base[i].moved=2;
|
||||
if (!body[i].initialized) base[i].initialized=0;
|
||||
}
|
||||
}
|
||||
flow_restore(base,flow_count);
|
||||
break;
|
||||
}
|
||||
case FE_N_FOR:
|
||||
if (s->loop_depth < 255U) ++s->loop_depth;
|
||||
check_for(s,n);
|
||||
if (s->loop_depth) --s->loop_depth;
|
||||
break;
|
||||
case FE_N_MATCH:
|
||||
check_match(s,n);
|
||||
break;
|
||||
case FE_N_BREAK:
|
||||
case FE_N_CONTINUE:
|
||||
if (!s->loop_depth) err(c,n->loc,"break or continue outside loop");
|
||||
break;
|
||||
case FE_N_RETURN:
|
||||
b = n->a ? check_expr(s, n->a) : fe_type_intern(&c->types, "void");
|
||||
mark_moved(s,n->a,b);
|
||||
if (known(b) && b->kind == FE_TYPE_VOID && s->ret->kind != FE_TYPE_VOID)
|
||||
err(c, n->loc, "void expression returned from value function");
|
||||
else if (known(s->ret) && known(b) && !fe_type_equal(s->ret, b) &&
|
||||
@@ -970,6 +1138,8 @@ static void check_fn(FeCheck *c, FeNode *fn, FeScope *globals)
|
||||
s.globals = globals;
|
||||
s.scope = scope_new(&s, globals);
|
||||
s.ret = fn->b ? node_type(c, fn->b) : fe_type_intern(&c->types, "void");
|
||||
s.loop_depth=0;
|
||||
s.defer_depth=0;
|
||||
fn->sem_type = s.ret;
|
||||
for (x = fn->a ? fn->a->children : 0; x; x = x->next) {
|
||||
t = node_type(c, x->a);
|
||||
@@ -1047,5 +1217,7 @@ FeType *fe_check_expr_type(FeCheck *c, FeNode *n)
|
||||
s.scope = scope_new(&s, 0);
|
||||
s.globals = s.scope;
|
||||
s.ret = fe_type_intern(&c->types, "void");
|
||||
s.loop_depth=0;
|
||||
s.defer_depth=0;
|
||||
return check_expr(&s, n);
|
||||
}
|
||||
|
||||
+365
-13
@@ -2,6 +2,10 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static void emit_expr(FeEmitter *e, FeNode *n);
|
||||
static void emit_stmt(FeEmitter *e, FeNode *n);
|
||||
static void emit_block(FeEmitter *e, FeNode *n);
|
||||
|
||||
static void pad(FeEmitter *e)
|
||||
{
|
||||
int i;
|
||||
@@ -24,6 +28,19 @@ static const char *cname(FeNode *n, const char *fallback)
|
||||
|
||||
static void emit_one_type(FeEmitter *e, FeType *t);
|
||||
|
||||
static int type_needs_drop(FeType *t)
|
||||
{
|
||||
unsigned i;
|
||||
if (!t) return 0;
|
||||
if (t->kind==FE_TYPE_OWNED) return 1;
|
||||
if (t->kind==FE_TYPE_ARRAY) return type_needs_drop(t->elem);
|
||||
if (t->kind==FE_TYPE_STRUCT) {
|
||||
if (t->has_drop) return 1;
|
||||
for (i=0;i<t->field_count;i++) if (type_needs_drop(t->fields[i].type)) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void emit_type_deps(FeEmitter *e, FeType *t)
|
||||
{
|
||||
unsigned i,j;
|
||||
@@ -35,6 +52,8 @@ static void emit_type_deps(FeEmitter *e, FeType *t)
|
||||
for (i=0;i<t->variant_count;i++)
|
||||
for (j=0;j<t->variants[i].field_count;j++)
|
||||
emit_one_type(e,t->variants[i].fields[j].type);
|
||||
if (t->kind == FE_TYPE_ERROR_UNION && t->error_value)
|
||||
emit_one_type(e,t->error_value);
|
||||
}
|
||||
|
||||
static void emit_one_type(FeEmitter *e, FeType *t)
|
||||
@@ -43,7 +62,10 @@ static void emit_one_type(FeEmitter *e, FeType *t)
|
||||
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;
|
||||
t->kind != FE_TYPE_ARRAY && t->kind != FE_TYPE_SLICE &&
|
||||
t->kind != FE_TYPE_ERROR_UNION) ||
|
||||
(t->kind == FE_TYPE_ERROR_UNION &&
|
||||
(!t->error_value || t->error_value->kind == FE_TYPE_VOID))) return;
|
||||
t->emit_state=1;
|
||||
emit_type_deps(e,t);
|
||||
if(t->kind==FE_TYPE_STRUCT) {
|
||||
@@ -55,6 +77,10 @@ static void emit_one_type(FeEmitter *e, FeType *t)
|
||||
} else if(t->kind==FE_TYPE_SLICE && t->cname) {
|
||||
fputs("typedef struct { ",e->out); fputs(fe_type_c_name(t->elem,e->pointer_bits),e->out); fputs(" *p; unsigned long n; } ",e->out); fputs(t->cname,e->out); fputs(";\n",e->out);
|
||||
fprintf(e->out,"static %s %s(%s *p, unsigned long n) { %s s; s.p=p; s.n=n; return s; }\n",t->cname,t->maker,fe_type_c_name(t->elem,e->pointer_bits),t->cname);
|
||||
} else if(t->kind==FE_TYPE_ERROR_UNION) {
|
||||
fputs(t->cname,e->out); fputs(" { unsigned short e; ",e->out);
|
||||
fputs(fe_type_c_name(t->error_value,e->pointer_bits),e->out);
|
||||
fputs(" v; } ;\n",e->out);
|
||||
} else if(t->kind==FE_TYPE_ENUM) {
|
||||
for(i=0;i<t->variant_count;i++) if(t->variants[i].field_count>1) {
|
||||
fprintf(e->out,"struct fe_payload_%s_%s {",t->name,t->variants[i].name);
|
||||
@@ -81,10 +107,95 @@ static void emit_type_defs(FeEmitter *e)
|
||||
for(t=e->check->types.types;t;t=t->next) emit_one_type(e,t);
|
||||
}
|
||||
|
||||
static FeNode *find_drop_method(FeEmitter *e, const char *name)
|
||||
{
|
||||
FeNode *n;
|
||||
FeNode *m;
|
||||
for (n=e->check->ast->root ? e->check->ast->root->children : 0; n; n=n->next)
|
||||
if (n->kind==FE_N_STRUCT && n->text && name && strcmp(n->text,name)==0)
|
||||
for (m=n->children; m; m=m->next)
|
||||
if (m->kind==FE_N_FN && m->text && strcmp(m->text,"drop")==0)
|
||||
return m;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void emit_drop_fields(FeEmitter *e, FeType *t)
|
||||
{
|
||||
unsigned i;
|
||||
FeType *ft;
|
||||
for (i=t->field_count; i>0; --i) {
|
||||
ft=t->fields[i-1].type;
|
||||
if (!type_needs_drop(ft)) continue;
|
||||
if (ft->kind==FE_TYPE_OWNED) {
|
||||
fputs("if (self->",e->out); fputs(t->fields[i-1].name,e->out);
|
||||
fputs(") { ",e->out);
|
||||
if (ft->elem && type_needs_drop(ft->elem) && ft->elem->drop_cname) {
|
||||
fprintf(e->out,"%s(self->%s); ",ft->elem->drop_cname,t->fields[i-1].name);
|
||||
}
|
||||
fputs("free(self->",e->out); fputs(t->fields[i-1].name,e->out);
|
||||
fputs("); self->",e->out); fputs(t->fields[i-1].name,e->out);
|
||||
fputs("=0; }\n",e->out);
|
||||
} else if (ft->kind==FE_TYPE_STRUCT && ft->drop_cname) {
|
||||
fprintf(e->out,"%s(&self->%s);\n",ft->drop_cname,t->fields[i-1].name);
|
||||
} else if (ft->kind==FE_TYPE_ARRAY && ft->drop_cname) {
|
||||
fprintf(e->out,"%s(&self->%s);\n",ft->drop_cname,t->fields[i-1].name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void emit_drop_helpers(FeEmitter *e)
|
||||
{
|
||||
FeType *t;
|
||||
FeNode *method;
|
||||
FeNode *param;
|
||||
for (t=e->check->types.types; t; t=t->next)
|
||||
if ((t->kind==FE_TYPE_STRUCT || t->kind==FE_TYPE_ARRAY) &&
|
||||
type_needs_drop(t) && t->drop_cname)
|
||||
fprintf(e->out,"static void %s(%s *self);\n",t->drop_cname,t->cname);
|
||||
for (t=e->check->types.types; t; t=t->next) {
|
||||
if (t->kind!=FE_TYPE_STRUCT || !type_needs_drop(t) || !t->drop_cname) continue;
|
||||
fprintf(e->out,"static void %s(%s *self) {\n",t->drop_cname,t->cname);
|
||||
method=find_drop_method(e,t->name);
|
||||
if (method) {
|
||||
param=method->a ? method->a->children : 0;
|
||||
if (param) param->cname="self";
|
||||
if (method->c) emit_block(e,method->c);
|
||||
fputc('\n',e->out);
|
||||
}
|
||||
emit_drop_fields(e,t);
|
||||
fputs("}\n",e->out);
|
||||
}
|
||||
for (t=e->check->types.types; t; t=t->next)
|
||||
if (t->kind==FE_TYPE_ARRAY && type_needs_drop(t) && t->drop_cname) {
|
||||
fprintf(e->out,"static void %s(%s *self) { unsigned long i; for (i=0; i<%lu; ++i) { ",
|
||||
t->drop_cname,t->cname,t->length);
|
||||
if (t->elem->kind==FE_TYPE_OWNED) {
|
||||
fputs("if (self->a[i]) { ",e->out);
|
||||
if (t->elem->elem && type_needs_drop(t->elem->elem) && t->elem->elem->drop_cname)
|
||||
fprintf(e->out,"%s(self->a[i]); ",t->elem->elem->drop_cname);
|
||||
fputs("free(self->a[i]); self->a[i]=0; }",e->out);
|
||||
} else if (t->elem->drop_cname)
|
||||
fprintf(e->out,"%s(&self->a[i]);",t->elem->drop_cname);
|
||||
fputs(" } }\n",e->out);
|
||||
}
|
||||
}
|
||||
|
||||
static void emit_type_helpers(FeEmitter *e)
|
||||
{
|
||||
FeType *t;
|
||||
unsigned i,j;
|
||||
for(t=e->check->types.types;t;t=t->next) {
|
||||
if (t->kind==FE_TYPE_ERROR_UNION && t->error_value &&
|
||||
t->error_value->kind!=FE_TYPE_VOID) {
|
||||
fprintf(e->out,"static %s %s(unsigned short e, %s v) { %s r; r.e=e; r.v=v; return r; }\n",
|
||||
t->cname,t->maker,fe_type_c_name(t->error_value,e->pointer_bits),t->cname);
|
||||
if (t->error_value->kind==FE_TYPE_OWNED)
|
||||
fprintf(e->out,"static %s %s(void) { %s r; r.v=(%s)malloc(sizeof(%s)); r.e=r.v ? 0 : 1; return r; }\n",
|
||||
t->cname,t->alloc_cname,t->cname,
|
||||
fe_type_c_name(t->error_value,e->pointer_bits),
|
||||
fe_type_c_name(t->error_value->elem,e->pointer_bits));
|
||||
}
|
||||
}
|
||||
for(t=e->check->types.types;t;t=t->next) {
|
||||
if(t->kind==FE_TYPE_STRUCT && t->maker) {
|
||||
fprintf(e->out,"static %s %s(",t->cname,t->maker);
|
||||
@@ -109,6 +220,7 @@ static void emit_type_helpers(FeEmitter *e)
|
||||
}
|
||||
}
|
||||
}
|
||||
emit_drop_helpers(e);
|
||||
for(t=e->check->types.types;t;t=t->next) {
|
||||
if (t->kind==FE_TYPE_ARRAY && t->indexer) {
|
||||
fprintf(e->out,"static %s %s(%s x, unsigned long i) { ",
|
||||
@@ -501,6 +613,9 @@ static void emit_lvalue(FeEmitter *e, FeNode *n)
|
||||
if (n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_REF &&
|
||||
n->b && n->b->text && strcmp(n->b->text,"^")==0) {
|
||||
fputs("(*",e->out); emit_expr(e,n->a); fputs(")",e->out);
|
||||
} else if (n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_OWNED &&
|
||||
n->b && n->b->text && strcmp(n->b->text,"^")==0) {
|
||||
fputs("(*",e->out); emit_expr(e,n->a); fputs(")",e->out);
|
||||
} else { emit_lvalue(e,n->a); fputc('.',e->out); fputs(n->b ? n->b->text : "member",e->out); }
|
||||
return;
|
||||
}
|
||||
@@ -512,6 +627,19 @@ static void emit_lvalue(FeEmitter *e, FeNode *n)
|
||||
emit_expr(e,n);
|
||||
}
|
||||
|
||||
static void emit_destroy_expr(FeEmitter *e, FeNode *n)
|
||||
{
|
||||
fputs("(free(",e->out); emit_expr(e,n); fputs(")",e->out);
|
||||
if (n && n->kind==FE_N_IDENT) {
|
||||
fputs(", ",e->out); emit_lvalue(e,n);
|
||||
fputs("=0, fe_live_",e->out); fputs(cname(n,"owned"),e->out);
|
||||
fputs("=0",e->out);
|
||||
} else {
|
||||
fputs(", ",e->out); emit_lvalue(e,n); fputs("=0",e->out);
|
||||
}
|
||||
fputs(", 0)",e->out);
|
||||
}
|
||||
|
||||
static FeNode *init_field(FeNode *n, const char *name)
|
||||
{
|
||||
FeNode *f;
|
||||
@@ -609,7 +737,15 @@ 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, "try") == 0) {
|
||||
if (n->a && n->a->sem_type &&
|
||||
n->a->sem_type->kind==FE_TYPE_ERROR_UNION &&
|
||||
n->a->sem_type->error_value &&
|
||||
n->a->sem_type->error_value->kind!=FE_TYPE_VOID) {
|
||||
fputc('(',e->out); emit_expr(e,n->a); fputs(").v",e->out);
|
||||
} else emit_expr(e,n->a);
|
||||
break;
|
||||
}
|
||||
if (strcmp(op, "not") == 0) fputs("(!", e->out);
|
||||
else {
|
||||
fputc('(', e->out);
|
||||
@@ -641,6 +777,27 @@ static void emit_expr(FeEmitter *e, FeNode *n)
|
||||
FeVariantType *v;
|
||||
int special=0;
|
||||
if(n->text && (strcmp(n->text,"@print")==0 || strcmp(n->text,"@fprint")==0 || strcmp(n->text,"@sprint")==0)) { emit_m4_builtin(e,n); special=1; }
|
||||
else if(n->a && n->a->kind==FE_N_MEMBER && n->a->a &&
|
||||
n->a->a->kind==FE_N_IDENT && n->a->a->text &&
|
||||
strcmp(n->a->a->text,"mem")==0 && n->a->b &&
|
||||
n->a->b->text && strcmp(n->a->b->text,"destroy")==0 &&
|
||||
n->children) {
|
||||
emit_destroy_expr(e,n->children);
|
||||
special=1;
|
||||
}
|
||||
else if(n->a && n->a->kind==FE_N_MEMBER && n->a->a &&
|
||||
n->a->a->kind==FE_N_IDENT && n->a->a->text &&
|
||||
strcmp(n->a->a->text,"mem")==0 && n->a->b &&
|
||||
n->a->b->text && strcmp(n->a->b->text,"create")==0 &&
|
||||
n->children && n->children->kind==FE_N_IDENT) {
|
||||
FeType *created=fe_type_intern(&e->check->types,n->children->text);
|
||||
FeType *owned=fe_type_owned(&e->check->types,created);
|
||||
FeType *result=fe_type_error_union(&e->check->types,owned);
|
||||
if (result->alloc_cname) fputs(result->alloc_cname,e->out);
|
||||
else fputs("fe_bad_alloc",e->out);
|
||||
fputs("()",e->out);
|
||||
special=1;
|
||||
}
|
||||
else if(n->a && n->a->kind==FE_N_MEMBER && n->a->a &&
|
||||
n->a->a->kind==FE_N_IDENT && n->a->a->text &&
|
||||
strcmp(n->a->a->text,"io")==0 && n->a->b && n->a->b->text &&
|
||||
@@ -660,7 +817,11 @@ static void emit_expr(FeEmitter *e, FeNode *n)
|
||||
fputc('(', e->out);
|
||||
for (x = n->children; x; x = x->next) {
|
||||
if (x != n->children) fputs(", ", e->out);
|
||||
emit_expr(e, x);
|
||||
if ((x->flags & 0x100U) && x->kind==FE_N_IDENT &&
|
||||
x->sem_type && x->sem_type->kind==FE_TYPE_OWNED) {
|
||||
fputs("(fe_live_",e->out); fputs(cname(x,"owned"),e->out);
|
||||
fputs("=0, ",e->out); emit_expr(e,x); fputc(')',e->out);
|
||||
} else emit_expr(e, x);
|
||||
}
|
||||
fputc(')', e->out);
|
||||
}
|
||||
@@ -674,6 +835,9 @@ static void emit_expr(FeEmitter *e, FeNode *n)
|
||||
else if(n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_REF &&
|
||||
n->b && n->b->text && strcmp(n->b->text,"^")==0) {
|
||||
fputs("(*",e->out); emit_expr(e,n->a); fputs(")",e->out);
|
||||
} else if(n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_OWNED &&
|
||||
n->b && n->b->text && strcmp(n->b->text,"^")==0) {
|
||||
fputs("(*",e->out); emit_expr(e,n->a); fputs(")",e->out);
|
||||
} else if(n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_ENUM) { v=fe_type_variant(n->a->sem_type,n->b ? n->b->text : ""); if(v) fputs(v->maker,e->out); else fputs("0",e->out); if(v)fputs("()",e->out); }
|
||||
else { emit_expr(e, n->a); fputc('.', e->out); if (n->b) fputs(n->b->text ? n->b->text : "member", e->out); }
|
||||
break;
|
||||
@@ -701,6 +865,89 @@ static void emit_decl(FeEmitter *e, FeNode *n)
|
||||
} else emit_expr(e,n->b);
|
||||
}
|
||||
fputs(";\n", e->out);
|
||||
if ((n->kind==FE_N_LET || n->kind==FE_N_VAR) && n->sem_type &&
|
||||
n->sem_type->kind==FE_TYPE_OWNED) {
|
||||
pad(e); fputs("unsigned char fe_live_",e->out);
|
||||
fputs(cname(n,"owned"),e->out); fputs("=0;\n",e->out);
|
||||
}
|
||||
}
|
||||
|
||||
static void emit_owned_live(FeEmitter *e, FeNode *n, int value)
|
||||
{
|
||||
if (n && n->sem_type && n->sem_type->kind==FE_TYPE_OWNED) {
|
||||
pad(e); fputs("fe_live_",e->out); fputs(cname(n,"owned"),e->out);
|
||||
fprintf(e->out,"=%d;\n",value);
|
||||
}
|
||||
}
|
||||
|
||||
static void emit_value_drop(FeEmitter *e, FeNode *n)
|
||||
{
|
||||
FeType *t=n ? n->sem_type : 0;
|
||||
if (!n || !t || !type_needs_drop(t) || (n->flags & 0x100U) ||
|
||||
(n->flags & 0x200U)) return;
|
||||
if (t->kind==FE_TYPE_OWNED) {
|
||||
pad(e); fputs("if (fe_live_",e->out); fputs(cname(n,"owned"),e->out);
|
||||
fputs(") { ",e->out);
|
||||
if (t->elem && type_needs_drop(t->elem) && t->elem->drop_cname) {
|
||||
fprintf(e->out,"%s(%s); ",t->elem->drop_cname,cname(n,"owned"));
|
||||
}
|
||||
fputs("free(",e->out); fputs(cname(n,"owned"),e->out);
|
||||
fputs("); fe_live_",e->out); fputs(cname(n,"owned"),e->out);
|
||||
fputs("=0; }\n",e->out);
|
||||
} else if (t->drop_cname) {
|
||||
pad(e); fprintf(e->out,"%s(&%s);\n",t->drop_cname,cname(n,"local"));
|
||||
}
|
||||
}
|
||||
|
||||
static void emit_cleanup_block(FeEmitter *e, FeNode *n)
|
||||
{
|
||||
FeNode *x;
|
||||
unsigned count=0;
|
||||
unsigned index;
|
||||
unsigned seen=0xffffffffU;
|
||||
unsigned depth;
|
||||
/* A defer becomes active only after its declaration statement was
|
||||
reached. In particular, a failing initializer must not run a later
|
||||
defer merely because it shares this AST block. */
|
||||
for (depth=0; depth<e->block_depth; ++depth)
|
||||
if (e->block_stack[depth]==n) { seen=e->block_seen[depth]; break; }
|
||||
for (x=n ? n->children : 0, index=0; x; x=x->next, ++index)
|
||||
if (index<seen &&
|
||||
(x->kind==FE_N_DEFER || x->kind==FE_N_LET || x->kind==FE_N_VAR)) ++count;
|
||||
while (count) {
|
||||
index=0;
|
||||
for (x=n->children; x; x=x->next)
|
||||
if ((x->kind==FE_N_DEFER || x->kind==FE_N_LET || x->kind==FE_N_VAR) &&
|
||||
index++==count-1) {
|
||||
if (x->kind==FE_N_DEFER) emit_stmt(e,x->a); else emit_value_drop(e,x);
|
||||
break;
|
||||
}
|
||||
--count;
|
||||
}
|
||||
}
|
||||
|
||||
static void emit_cleanup_to(FeEmitter *e, unsigned floor)
|
||||
{
|
||||
unsigned i;
|
||||
for (i=e->block_depth; i>floor; --i) emit_cleanup_block(e,e->block_stack[i-1]);
|
||||
}
|
||||
|
||||
static void emit_cleanup_all(FeEmitter *e)
|
||||
{
|
||||
emit_cleanup_to(e,0);
|
||||
}
|
||||
|
||||
static void emit_error_return(FeEmitter *e, const char *error_expr)
|
||||
{
|
||||
if (e->current_ret && e->current_ret->kind==FE_TYPE_ERROR_UNION &&
|
||||
e->current_ret->error_value && e->current_ret->error_value->kind!=FE_TYPE_VOID) {
|
||||
fputs("return ",e->out); fputs(e->current_ret->maker,e->out);
|
||||
fputs("(",e->out); fputs(error_expr,e->out); fputs(", (",e->out);
|
||||
fputs(fe_type_c_name(e->current_ret->error_value,e->pointer_bits),e->out);
|
||||
fputs(")0);\n",e->out);
|
||||
} else {
|
||||
fputs("return ",e->out); fputs(error_expr,e->out); fputs(";\n",e->out);
|
||||
}
|
||||
}
|
||||
|
||||
static void emit_block(FeEmitter *e, FeNode *n)
|
||||
@@ -718,12 +965,30 @@ static void emit_block(FeEmitter *e, FeNode *n)
|
||||
pad(e);
|
||||
fputs("{\n", e->out);
|
||||
++e->indent;
|
||||
if (e->block_depth<32U) {
|
||||
e->block_stack[e->block_depth]=n;
|
||||
e->block_seen[e->block_depth]=0;
|
||||
++e->block_depth;
|
||||
}
|
||||
/* 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 ||
|
||||
x->kind == FE_N_CONST) emit_decl(e, x);
|
||||
for (x = n->children; x; x = x->next) emit_stmt(e, x);
|
||||
if (e->current_ret && e->current_ret->kind!=FE_TYPE_VOID) {
|
||||
pad(e); fputs(fe_type_c_name(e->current_ret,e->pointer_bits),e->out);
|
||||
fputs(" fe_return_value;\n",e->out);
|
||||
}
|
||||
{
|
||||
unsigned seen=0;
|
||||
for (x = n->children; x; x = x->next) {
|
||||
++seen;
|
||||
if (e->block_depth) e->block_seen[e->block_depth-1]=seen;
|
||||
emit_stmt(e, x);
|
||||
}
|
||||
}
|
||||
--e->indent;
|
||||
emit_cleanup_block(e,n);
|
||||
if (e->block_depth) --e->block_depth;
|
||||
if (e->fallthrough_block==n) {
|
||||
pad(e);
|
||||
fputs("return 0;\n",e->out);
|
||||
@@ -765,6 +1030,37 @@ static void emit_match(FeEmitter *e, FeNode *n, int value_context)
|
||||
}
|
||||
}
|
||||
|
||||
static int try_has_value(FeNode *n)
|
||||
{
|
||||
return n && n->kind==FE_N_UNARY && n->text && strcmp(n->text,"try")==0 &&
|
||||
n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_ERROR_UNION &&
|
||||
n->a->sem_type->error_value && n->a->sem_type->error_value->kind!=FE_TYPE_VOID;
|
||||
}
|
||||
|
||||
static void emit_try_statement(FeEmitter *e, FeNode *try_node, FeNode *target)
|
||||
{
|
||||
char temp[40];
|
||||
char error[48];
|
||||
FeType *result=try_node->a->sem_type;
|
||||
sprintf(temp,"fe_try_%u",e->temp_serial++);
|
||||
sprintf(error,"%s.e",temp);
|
||||
pad(e); fputs("{ ",e->out); fputs(fe_type_c_name(result,e->pointer_bits),e->out);
|
||||
fputc(' ',e->out); fputs(temp,e->out); fputs(" = ",e->out);
|
||||
emit_expr(e,try_node->a); fputs("; if (",e->out); fputs(temp,e->out);
|
||||
fputs(".e) {\n",e->out); ++e->indent;
|
||||
emit_cleanup_all(e);
|
||||
pad(e); emit_error_return(e,error);
|
||||
--e->indent; pad(e); fputs("} ",e->out);
|
||||
if (target) {
|
||||
if (target->kind==FE_N_LET || target->kind==FE_N_VAR)
|
||||
fputs(cname(target,"fe_local"),e->out);
|
||||
else
|
||||
emit_lvalue(e,target);
|
||||
fputs(" = ",e->out); fputs(temp,e->out); fputs(".v;\n",e->out);
|
||||
}
|
||||
fputs("}\n",e->out);
|
||||
}
|
||||
|
||||
static void emit_stmt(FeEmitter *e, FeNode *n)
|
||||
{
|
||||
if (!n) return;
|
||||
@@ -776,46 +1072,91 @@ static void emit_stmt(FeEmitter *e, FeNode *n)
|
||||
case FE_N_LET:
|
||||
case FE_N_VAR:
|
||||
if (n->b) {
|
||||
if (try_has_value(n->b)) emit_try_statement(e,n->b,n);
|
||||
else {
|
||||
pad(e);
|
||||
fputs(cname(n, "fe_local"), e->out);
|
||||
fputs(" = ", e->out);
|
||||
emit_expr(e, n->b);
|
||||
fputs(";\n", e->out);
|
||||
}
|
||||
emit_owned_live(e,n,1);
|
||||
}
|
||||
break;
|
||||
case FE_N_ASSIGN:
|
||||
if (n->a && n->a->kind==FE_N_IDENT && n->a->sem_type &&
|
||||
n->a->sem_type->kind==FE_TYPE_OWNED) {
|
||||
pad(e); fputs("if (fe_live_",e->out); fputs(cname(n->a,"owned"),e->out);
|
||||
fputs(") { ",e->out);
|
||||
if (n->a->sem_type->elem && type_needs_drop(n->a->sem_type->elem) &&
|
||||
n->a->sem_type->elem->drop_cname)
|
||||
fprintf(e->out,"%s(%s); ",n->a->sem_type->elem->drop_cname,cname(n->a,"owned"));
|
||||
fputs("free(",e->out); fputs(cname(n->a,"owned"),e->out);
|
||||
fputs("); fe_live_",e->out); fputs(cname(n->a,"owned"),e->out);
|
||||
fputs("=0; }\n",e->out);
|
||||
}
|
||||
pad(e);
|
||||
emit_lvalue(e, n->a);
|
||||
fputc(' ', e->out);
|
||||
fputs(n->text ? n->text : "=", e->out);
|
||||
fputs(" ", e->out);
|
||||
emit_expr(e, n->b);
|
||||
if (n->b && (n->b->flags & 0x100U) && n->b->kind==FE_N_IDENT &&
|
||||
n->b->sem_type && n->b->sem_type->kind==FE_TYPE_OWNED) {
|
||||
fputs("(fe_live_",e->out); fputs(cname(n->b,"owned"),e->out);
|
||||
fputs("=0, ",e->out); emit_expr(e,n->b); fputc(')',e->out);
|
||||
} else emit_expr(e, n->b);
|
||||
fputs(";\n", e->out);
|
||||
if (n->a && n->a->kind==FE_N_IDENT) emit_owned_live(e,n->a,1);
|
||||
break;
|
||||
case FE_N_EXPR_STMT:
|
||||
if (try_has_value(n->a)) {
|
||||
emit_try_statement(e,n->a,0);
|
||||
} else {
|
||||
pad(e);
|
||||
if (n->a && n->a->kind==FE_N_UNARY && n->a->text &&
|
||||
strcmp(n->a->text,"try")==0 && n->a->a) {
|
||||
fputs("if ((fe_m4_error = ",e->out);
|
||||
emit_expr(e,n->a->a);
|
||||
fputs(") != 0) return fe_m4_error;\n",e->out);
|
||||
fputs(") != 0) {\n",e->out);
|
||||
++e->indent;
|
||||
emit_cleanup_all(e);
|
||||
pad(e); fputs("return fe_m4_error;\n",e->out);
|
||||
--e->indent;
|
||||
pad(e); fputs("}\n",e->out);
|
||||
} else {
|
||||
emit_expr(e, n->a);
|
||||
fputs(";\n", e->out);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FE_N_BREAK:
|
||||
case FE_N_CONTINUE:
|
||||
if (e->loop_depth) {
|
||||
emit_cleanup_to(e,e->loop_floor[e->loop_depth-1]);
|
||||
pad(e); fputs(n->kind==FE_N_BREAK ? "break;\n" : "continue;\n",e->out);
|
||||
}
|
||||
break;
|
||||
case FE_N_RETURN:
|
||||
pad(e);
|
||||
if (n->a && n->a->kind == FE_N_MATCH) {
|
||||
emit_match(e,n->a,1);
|
||||
break;
|
||||
}
|
||||
fputs("return", e->out);
|
||||
if (n->a) {
|
||||
fputc(' ', e->out);
|
||||
emit_expr(e, n->a);
|
||||
/* Evaluate before cleanup: `return p.^` must not dereference p after
|
||||
its owned cleanup has run. The block-local temporary is declared
|
||||
before all statements to retain C89 declaration ordering. */
|
||||
if (n->a && e->current_ret && e->current_ret->kind!=FE_TYPE_VOID) {
|
||||
pad(e); fputs("fe_return_value = ",e->out);
|
||||
if ((n->a->flags & 0x100U) && n->a->kind==FE_N_IDENT &&
|
||||
n->a->sem_type && n->a->sem_type->kind==FE_TYPE_OWNED) {
|
||||
fputs("(fe_live_",e->out); fputs(cname(n->a,"owned"),e->out);
|
||||
fputs("=0, ",e->out); emit_expr(e,n->a); fputc(')',e->out);
|
||||
} else emit_expr(e,n->a);
|
||||
fputs(";\n",e->out);
|
||||
}
|
||||
fputs(";\n", e->out);
|
||||
emit_cleanup_all(e);
|
||||
pad(e); fputs("return",e->out);
|
||||
if (n->a) fputs(" fe_return_value",e->out);
|
||||
fputs(";\n",e->out);
|
||||
break;
|
||||
case FE_N_IF:
|
||||
pad(e);
|
||||
@@ -836,12 +1177,15 @@ static void emit_stmt(FeEmitter *e, FeNode *n)
|
||||
fputs("while (", e->out);
|
||||
emit_expr(e, n->a);
|
||||
fputs(") ", e->out);
|
||||
if (e->loop_depth<16U) e->loop_floor[e->loop_depth++]=e->block_depth;
|
||||
if (n->b && n->b->kind == FE_N_BLOCK) emit_block(e, n->b);
|
||||
else emit_block(e, 0);
|
||||
if (e->loop_depth) --e->loop_depth;
|
||||
fputc('\n', e->out);
|
||||
break;
|
||||
case FE_N_FOR:
|
||||
pad(e); fputs("{\n",e->out); ++e->indent;
|
||||
if (e->loop_depth<16U) e->loop_floor[e->loop_depth++]=e->block_depth;
|
||||
if (n->c) {
|
||||
pad(e); fputs("unsigned long ",e->out); fputs(cname(n,"fe_index"),e->out); fputs(";\n",e->out);
|
||||
pad(e); fputs(cname(n,"fe_index"),e->out); fputs(" = ",e->out); emit_expr(e,n->a); fputs(";\n",e->out);
|
||||
@@ -869,7 +1213,9 @@ static void emit_stmt(FeEmitter *e, FeNode *n)
|
||||
fputc('&',e->out); if(mutable_iter) fputs("(*",e->out); fputs(temp,e->out); if(mutable_iter) fputs(")",e->out); if(bt && bt->kind==FE_TYPE_ARRAY) fputs(".a[fe_i]",e->out); else fputs(".p[fe_i]",e->out); fputs("; ",e->out);
|
||||
emit_block(e,n->b); fputs(" } }\n",e->out);
|
||||
}
|
||||
--e->indent; pad(e); fputs("}\n",e->out); break;
|
||||
--e->indent;
|
||||
if (e->loop_depth) --e->loop_depth;
|
||||
pad(e); fputs("}\n",e->out); break;
|
||||
case FE_N_MATCH:
|
||||
emit_match(e,n,0); break;
|
||||
default:
|
||||
@@ -899,12 +1245,15 @@ static void emit_fn(FeEmitter *e, FeNode *fn, int prototype)
|
||||
fputc(')', e->out);
|
||||
if (prototype) fputs(";\n", e->out);
|
||||
else {
|
||||
FeType *old_ret=e->current_ret;
|
||||
e->current_ret=fn->sem_type;
|
||||
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);
|
||||
e->current_ret=old_ret;
|
||||
fputc('\n', e->out);
|
||||
}
|
||||
}
|
||||
@@ -933,6 +1282,9 @@ void fe_emit_c_init(FeEmitter *e, FILE *out, FeCheck *check,
|
||||
e->no_checks = no_checks;
|
||||
e->temp_serial = 0;
|
||||
e->fallthrough_block = 0;
|
||||
e->block_depth = 0;
|
||||
e->loop_depth = 0;
|
||||
e->current_ret = 0;
|
||||
}
|
||||
|
||||
void fe_emit_c_program(FeEmitter *e)
|
||||
|
||||
@@ -11,6 +11,12 @@ typedef struct FeEmitter {
|
||||
int no_checks;
|
||||
unsigned temp_serial;
|
||||
FeNode *fallthrough_block;
|
||||
FeNode *block_stack[32];
|
||||
unsigned block_seen[32];
|
||||
unsigned block_depth;
|
||||
unsigned loop_floor[16];
|
||||
unsigned loop_depth;
|
||||
FeType *current_ret;
|
||||
} FeEmitter;
|
||||
|
||||
void fe_emit_c_init(FeEmitter *e, FILE *out, FeCheck *check,
|
||||
|
||||
+53
-2
@@ -19,10 +19,13 @@ static FeType *new_type(FeTypeCtx *ctx, const char *name, FeTypeKind kind)
|
||||
t->slicer = 0;
|
||||
t->full_slicer = 0;
|
||||
t->tail_slicer = 0;
|
||||
t->drop_cname = 0;
|
||||
t->alloc_cname = 0;
|
||||
t->bits = 0;
|
||||
t->is_unsigned = 0;
|
||||
t->packed = 0;
|
||||
t->is_error = 0;
|
||||
t->has_drop = 0;
|
||||
t->length = 0;
|
||||
t->size = 0;
|
||||
t->align = 1;
|
||||
@@ -125,6 +128,7 @@ FeType *fe_type_array(FeTypeCtx *ctx, unsigned long length, FeType *elem)
|
||||
t->elem = elem;
|
||||
t->cname = generated_name(ctx, "struct fe_arr_", "type");
|
||||
t->maker = generated_name(ctx, "fe_make_arr_", "type");
|
||||
t->drop_cname = generated_name(ctx, "fe_drop_arr_", "type");
|
||||
t->indexer = generated_name(ctx, "fe_idx_arr_", "type");
|
||||
t->slicer = generated_name(ctx, "fe_slice_arr_", "type");
|
||||
t->full_slicer = generated_name(ctx, "fe_full_arr_", "type");
|
||||
@@ -166,6 +170,19 @@ FeType *fe_type_ref(FeTypeCtx *ctx, FeType *elem, int mutable)
|
||||
return t;
|
||||
}
|
||||
|
||||
FeType *fe_type_owned(FeTypeCtx *ctx, FeType *elem)
|
||||
{
|
||||
char key[128];
|
||||
FeType *t;
|
||||
sprintf(key,"^%s",elem ? elem->name : "?");
|
||||
t=fe_type_intern(ctx,key);
|
||||
if(t->kind==FE_TYPE_UNKNOWN) {
|
||||
t->kind=FE_TYPE_OWNED;
|
||||
t->elem=elem;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
FeType *fe_type_error_union(FeTypeCtx *ctx, FeType *value)
|
||||
{
|
||||
char key[128];
|
||||
@@ -175,6 +192,11 @@ FeType *fe_type_error_union(FeTypeCtx *ctx, FeType *value)
|
||||
if(t->kind==FE_TYPE_UNKNOWN) {
|
||||
t->kind=FE_TYPE_ERROR_UNION;
|
||||
t->error_value=value;
|
||||
if (value && value->kind != FE_TYPE_VOID) {
|
||||
t->cname=generated_name(ctx,"struct fe_result_","value");
|
||||
t->maker=generated_name(ctx,"fe_make_result_","value");
|
||||
t->alloc_cname=generated_name(ctx,"fe_alloc_result_","value");
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
@@ -192,6 +214,9 @@ FeType *fe_type_declare_struct(FeTypeCtx *ctx, const FeNode *node, int packed)
|
||||
if (t->kind == FE_TYPE_STRUCT) return t;
|
||||
t->kind = FE_TYPE_STRUCT;
|
||||
t->packed = packed;
|
||||
for (f = node->children; f; f = f->next)
|
||||
if (f->kind==FE_N_FN && f->text && strcmp(f->text,"drop")==0)
|
||||
t->has_drop=1;
|
||||
cname = (char *)fe_arena_alloc(ctx->arena,
|
||||
(unsigned long)strlen("struct fe_") + strlen(ctx->unit_name) +
|
||||
strlen(node->text) + 2UL);
|
||||
@@ -202,6 +227,7 @@ FeType *fe_type_declare_struct(FeTypeCtx *ctx, const FeNode *node, int packed)
|
||||
strcat(cname, node->text);
|
||||
t->cname = cname;
|
||||
t->maker = generated_name(ctx, "fe_make_", node->text);
|
||||
t->drop_cname = generated_name(ctx, "fe_drop_", node->text);
|
||||
for (f = node->children; f; f = f->next)
|
||||
if (f->kind == FE_N_FIELD) ++count;
|
||||
t->field_count = count;
|
||||
@@ -337,7 +363,15 @@ static void layout_type(FeTypeCtx *ctx, FeType *t)
|
||||
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;
|
||||
if (t->error_value && t->error_value->kind != FE_TYPE_VOID) {
|
||||
layout_type(ctx,t->error_value);
|
||||
t->align=ctx->pointer_bits==16 ? 1U : fe_type_align(t->error_value);
|
||||
t->size=round_up(2UL,t->align)+fe_type_size(t->error_value);
|
||||
t->size=round_up(t->size,t->align);
|
||||
} else {
|
||||
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) {
|
||||
@@ -354,6 +388,11 @@ static void layout_type(FeTypeCtx *ctx, FeType *t)
|
||||
t->align = ctx->pointer_bits == 16 ? 1U : 4U;
|
||||
t->cycle_state = 2; return;
|
||||
}
|
||||
if (t->kind == FE_TYPE_OWNED) {
|
||||
t->size = ctx->pointer_bits == 16 ? 2UL : 4UL;
|
||||
t->align = ctx->pointer_bits == 16 ? 1U : 4U;
|
||||
t->cycle_state = 2; return;
|
||||
}
|
||||
if (t->kind == FE_TYPE_SLICE || t->kind == FE_TYPE_STR) {
|
||||
t->size = ctx->pointer_bits == 16 ? 4UL : 8UL;
|
||||
t->align = ctx->pointer_bits == 16 ? 1U : 4U;
|
||||
@@ -449,6 +488,8 @@ FeType *fe_type_from_ast(FeTypeCtx *ctx, const FeNode *node)
|
||||
strcmp(node->text, "&mut") == 0))
|
||||
return fe_type_ref(ctx, fe_type_from_ast(ctx,node->a),
|
||||
strcmp(node->text,"&mut") == 0);
|
||||
if (node->text && strcmp(node->text,"^")==0)
|
||||
return fe_type_owned(ctx,fe_type_from_ast(ctx,node->a));
|
||||
if (node->text && strcmp(node->text, "[") == 0) {
|
||||
if (node->a) {
|
||||
if (node->a->kind == FE_N_LITERAL && node->a->text)
|
||||
@@ -495,7 +536,11 @@ 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_ERROR_UNION) {
|
||||
if (t->error_value && t->error_value->kind != FE_TYPE_VOID && t->cname)
|
||||
return t->cname;
|
||||
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];
|
||||
@@ -509,6 +554,12 @@ const char *fe_type_c_name(const FeType *t, unsigned pointer_bits)
|
||||
}
|
||||
return ref_name;
|
||||
}
|
||||
if (t->kind == FE_TYPE_OWNED) {
|
||||
static char owned_name[128];
|
||||
strcpy(owned_name,fe_type_c_name(t->elem,pointer_bits));
|
||||
strcat(owned_name," *");
|
||||
return owned_name;
|
||||
}
|
||||
if (t->kind != FE_TYPE_INT) return "long";
|
||||
if (strcmp(t->name, "usize") == 0) return pointer_bits == 16 ? "unsigned short" : "unsigned long";
|
||||
if (strcmp(t->name, "isize") == 0) return pointer_bits == 16 ? "short" : "long";
|
||||
|
||||
+5
-1
@@ -6,7 +6,7 @@
|
||||
typedef enum FeTypeKind {
|
||||
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
|
||||
FE_TYPE_REF, FE_TYPE_OWNED, FE_TYPE_UNKNOWN
|
||||
} FeTypeKind;
|
||||
|
||||
typedef struct FeFieldType FeFieldType;
|
||||
@@ -37,10 +37,13 @@ struct FeType {
|
||||
char *slicer;
|
||||
char *full_slicer;
|
||||
char *tail_slicer;
|
||||
char *drop_cname;
|
||||
char *alloc_cname;
|
||||
unsigned bits;
|
||||
int is_unsigned;
|
||||
int packed;
|
||||
int is_error;
|
||||
int has_drop;
|
||||
unsigned long length;
|
||||
unsigned long size;
|
||||
unsigned align;
|
||||
@@ -71,6 +74,7 @@ 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_owned(FeTypeCtx *ctx, FeType *elem);
|
||||
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);
|
||||
|
||||
@@ -227,6 +227,25 @@ fec.exe --target=bits32 --emit-c TESTS\M4\BAD-OPEN.FE -o TESTS\M4\BAD-OPEN.C > n
|
||||
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
|
||||
fec.exe --target=bits32 --emit-c TESTS\M5\DEFER.FE -o TESTS\M5\DEFER.C > nul
|
||||
if errorlevel 1 goto test_fail
|
||||
fec.exe --target=bits32 --emit-c TESTS\M5\OWNED.FE -o TESTS\M5\OWNED.C > nul
|
||||
if errorlevel 1 goto test_fail
|
||||
fec.exe --target=bits32 --emit-c TESTS\M5\BAD-MOVE.FE -o TESTS\M5\BAD-MOVE.C > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
fec.exe --target=bits32 --emit-c TESTS\M5\BAD-DES.FE -o TESTS\M5\BAD-DES.C > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
if exist TESTS\M5\RUNTIME-G.C del TESTS\M5\RUNTIME-G.C
|
||||
if exist TESTS\M5\RUNTIME.O del TESTS\M5\RUNTIME.O
|
||||
if exist TESTS\M5\RUNTIME.EXE del TESTS\M5\RUNTIME.EXE
|
||||
fec.exe --target=bits32 --emit-c TESTS\M5\RUNTIME.FE -o TESTS\M5\RUNTIME-G.C > nul
|
||||
if errorlevel 1 goto test_fail
|
||||
rem Compile generated source and the C89 runtime harness in one WCL386 invocation
|
||||
rem so both objects use the same DOS/4GW startup and runtime library.
|
||||
wcl386 -q -za -bt=dos -fe=TESTS\M5\RUNTIME.EXE TESTS\M5\RUNTIME-G.C TESTS\M5\RUNTIME.C
|
||||
if errorlevel 1 goto test_fail
|
||||
TESTS\M5\RUNTIME.EXE
|
||||
if errorlevel 1 goto test_fail
|
||||
|
||||
echo OK>TEST.OK
|
||||
cd C:\FEC
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
unit m5_bad_destroy;
|
||||
|
||||
fn bad(x: i32) -> void {
|
||||
mem.destroy(x);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
unit m5_bad_move;
|
||||
|
||||
fn take(p: ^i32) -> void { mem.destroy(p); }
|
||||
|
||||
fn twice(p: ^i32) -> void {
|
||||
take(p);
|
||||
take(p);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
unit m5_defer;
|
||||
|
||||
pub fn cleanup(p: ^i32) -> void {
|
||||
defer { mem.destroy(p); }
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
unit m5_owned;
|
||||
|
||||
fn main() -> void {
|
||||
var p: ^i32 = try mem.create(i32);
|
||||
p = try mem.create(i32);
|
||||
p.^ = 7;
|
||||
let value: i32 = p.^;
|
||||
defer { mem.destroy(p); }
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
extern long fe_m5_runtime_run(long mode);
|
||||
|
||||
int main(void)
|
||||
{
|
||||
if (fe_m5_runtime_run(0) != 0) return 1;
|
||||
if (fe_m5_runtime_run(1) != 9) return 2;
|
||||
if (fe_m5_runtime_run(2) != 0) return 3;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
unit m5_runtime;
|
||||
|
||||
pub fn run(mode: i32) -> i32 {
|
||||
var p: ^i32 = try mem.create(i32);
|
||||
defer { mem.destroy(p); }
|
||||
p.^ = 7;
|
||||
if mode == 1 {
|
||||
p = try mem.create(i32);
|
||||
p.^ = 9;
|
||||
return p.^;
|
||||
}
|
||||
while true {
|
||||
break;
|
||||
}
|
||||
if mode == 2 {
|
||||
return 0;
|
||||
}
|
||||
return p.^ - 7;
|
||||
}
|
||||
@@ -91,3 +91,14 @@ for f in bad-many bad-open bad-cls; do
|
||||
fi
|
||||
done
|
||||
echo "M4 tests: formatting builtins passed"
|
||||
|
||||
for f in defer owned; do
|
||||
"$root"/fec --target=bits32 --emit-c "$root"/tests/m5/$f.fe -o "$m4tmp/m5-$f.c"
|
||||
done
|
||||
for f in bad-move bad-destroy; do
|
||||
if "$root"/fec --target=bits32 --emit-c "$root"/tests/m5/$f.fe -o "$m4tmp/m5-$f.c" >/dev/null 2>/dev/null; then
|
||||
echo "FAIL (accepted M5 ownership error): $f.fe"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
echo "M5 tests: owned move/defer checks passed"
|
||||
|
||||
@@ -9,6 +9,7 @@ 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 not exist C:\FEC\TESTS\M5 md C:\FEC\TESTS\M5
|
||||
if exist C:\FEC\VM.FAIL del C:\FEC\VM.FAIL
|
||||
if exist C:\FEC\STAGE.FAIL del C:\FEC\STAGE.FAIL
|
||||
|
||||
@@ -169,6 +170,18 @@ 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
|
||||
copy D:\FEC\TESTS\M5\DEFER.FE C:\FEC\TESTS\M5\DEFER.FE > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\TESTS\M5\OWNED.FE C:\FEC\TESTS\M5\OWNED.FE > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\TESTS\M5\BAD-MOVE.FE C:\FEC\TESTS\M5\BAD-MOVE.FE > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\TESTS\M5\BAD-DES~1.FE C:\FEC\TESTS\M5\BAD-DES.FE > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\TESTS\M5\RUNTIME.FE C:\FEC\TESTS\M5\RUNTIME.FE > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\TESTS\M5\RUNTIME.C C:\FEC\TESTS\M5\RUNTIME.C > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
|
||||
call C:\FEC\TEST-DOS.BAT
|
||||
if exist C:\FEC\TEST.OK goto vm_success
|
||||
|
||||
Reference in New Issue
Block a user