feat: add M2 type checking and C emission

This commit is contained in:
2026-08-16 07:40:50 +09:00
parent 005056a5ea
commit da78a615fb
27 changed files with 1180 additions and 9 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
CC ?= cc CC ?= cc
CFLAGS ?= -O2 -Wall -Wextra -std=c89 CFLAGS ?= -O2 -Wall -Wextra -std=c89
CPPFLAGS ?= -Isrc CPPFLAGS ?= -Isrc
SRC = src/arena.c src/diag.c src/lexer.c src/ast.c src/parser.c src/driver.c SRC = src/arena.c src/diag.c src/lexer.c src/ast.c src/parser.c src/types.c src/check.c src/emit_c.c src/driver.c
OBJ = $(SRC:.c=.o) OBJ = $(SRC:.c=.o)
.PHONY: all clean test dos-build .PHONY: all clean test dos-build
+26 -1
View File
@@ -11,12 +11,37 @@ if exist diag.obj del diag.obj
if exist lexer.obj del lexer.obj if exist lexer.obj del lexer.obj
if exist ast.obj del ast.obj if exist ast.obj del ast.obj
if exist parser.obj del parser.obj if exist parser.obj del parser.obj
if exist types.obj del types.obj
if exist check.obj del check.obj
if exist emitc.obj del emitc.obj
if exist emit_c.obj del emit_c.obj
if exist driver.obj del driver.obj if exist driver.obj del driver.obj
if "%WATCOM%"=="" set WATCOM=C:\DEVEL\WATCOMC if "%WATCOM%"=="" set WATCOM=C:\DEVEL\WATCOMC
if not exist %WATCOM%\BINW\WCL.EXE goto build_fail if not exist %WATCOM%\BINW\WCL.EXE goto build_fail
set PATH=%WATCOM%\BINW;%WATCOM%\BINP;%PATH% set PATH=%WATCOM%\BINW;%WATCOM%\BINP;%PATH%
wcl -q -za -wx -bt=dos -k32768 -fe=fec.exe src\arena.c src\diag.c src\lexer.c src\ast.c src\parser.c src\driver.c wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=arena.obj src\arena.c
if errorlevel 1 goto build_fail
wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=diag.obj src\diag.c
if errorlevel 1 goto build_fail
wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=lexer.obj src\lexer.c
if errorlevel 1 goto build_fail
wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=ast.obj src\ast.c
if errorlevel 1 goto build_fail
wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=parser.obj src\parser.c
if errorlevel 1 goto build_fail
wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=types.obj src\types.c
if errorlevel 1 goto build_fail
wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=check.obj src\check.c
if errorlevel 1 goto build_fail
rem Use an unambiguous short object name for the emit_c source.
wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=emitc.obj src\emit_c.c
if errorlevel 1 goto build_fail
wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=driver.obj src\driver.c
if errorlevel 1 goto build_fail
rem DOS command lines are limited to roughly 126 characters. All stale objects
rem were removed above, so the wildcard contains exactly this build's objects.
wcl -q -za -wx -bt=dos -ml -k32768 -fe=fec.exe *.obj
if errorlevel 1 goto build_fail if errorlevel 1 goto build_fail
if not exist fec.exe goto build_fail if not exist fec.exe goto build_fail
echo OK>BUILD.OK echo OK>BUILD.OK
+1 -1
View File
@@ -8,7 +8,7 @@ FeNode *fe_node(FeAst *a, FeNodeKind k, FeLoc loc, const char *text, unsigned lo
FeNode *n=(FeNode *)fe_arena_alloc(&a->arena,sizeof(FeNode)); FeNode *n=(FeNode *)fe_arena_alloc(&a->arena,sizeof(FeNode));
if (!n) return 0; if (!n) return 0;
n->kind=k; n->loc=loc; n->text=text?fe_arena_strdup(&a->arena,text,len):0; n->kind=k; n->loc=loc; n->text=text?fe_arena_strdup(&a->arena,text,len):0;
n->a=n->b=n->c=n->children=n->next=0; return n; n->a=n->b=n->c=n->children=n->next=0; n->cname=0; n->sem_type=0; return n;
} }
void fe_node_add(FeNode *parent, FeNode *child) void fe_node_add(FeNode *parent, FeNode *child)
{ {
+4
View File
@@ -15,6 +15,7 @@ typedef enum FeNodeKind {
} FeNodeKind; } FeNodeKind;
typedef struct FeNode FeNode; typedef struct FeNode FeNode;
typedef struct FeType FeType;
struct FeNode { struct FeNode {
FeNodeKind kind; FeNodeKind kind;
FeLoc loc; FeLoc loc;
@@ -24,6 +25,9 @@ struct FeNode {
FeNode *c; FeNode *c;
FeNode *children; FeNode *children;
FeNode *next; FeNode *next;
/* Semantic information filled by checking; kept out of AST dumps. */
char *cname;
FeType *sem_type;
}; };
typedef struct FeAst { typedef struct FeAst {
+515
View File
@@ -0,0 +1,515 @@
#include "check.h"
#include <string.h>
#include <stdio.h>
typedef struct FeSym FeSym;
typedef struct FeScope FeScope;
struct FeSym {
const char *name;
char *cname;
FeType *type;
FeNode *fn;
int mutable;
int initialized;
};
struct FeScope {
FeScope *parent;
FeSym *items;
unsigned count;
unsigned capacity;
};
typedef struct FeCheckerState {
FeCheck *c;
FeScope *scope;
FeScope *globals;
FeType *ret;
} FeCheckerState;
static FeType *unknown(FeCheck *c)
{
return fe_type_intern(&c->types, "<unknown>");
}
static void err(FeCheck *c, FeLoc loc, const char *msg)
{
fe_diag_error(c->diags, loc, msg);
}
static int known(FeType *t)
{
return t && t->kind != FE_TYPE_UNKNOWN && t->kind != FE_TYPE_ERROR;
}
static int compatible(FeType *want, FeType *got, FeNode *value)
{
if (fe_type_equal(want, got)) return 1;
if (!known(want) || !known(got)) return 1;
return fe_type_is_integer(want) && fe_type_is_integer(got) && value &&
value->kind == FE_N_LITERAL && value->text &&
value->text[0] != '\'' && value->text[0] != '"';
}
static FeType *node_type(FeCheck *c, FeNode *n)
{
FeType *t;
if (!n) return unknown(c);
t = fe_type_from_ast(&c->types, n);
n->sem_type = t;
return t;
}
static char *unit_cname(FeCheck *c, const char *name)
{
char *u;
char *p;
unsigned long n;
u = c->ast->root && c->ast->root->text ? c->ast->root->text : "unit";
n = (unsigned long)strlen("fe_") + (unsigned long)strlen(u) +
(unsigned long)strlen(name ? name : "name") + 2UL;
p = (char *)fe_arena_alloc(&c->ast->arena, n);
if (!p) return 0;
strcpy(p, "fe_");
strcat(p, u);
strcat(p, "_");
strcat(p, name ? name : "name");
return p;
}
static char *local_cname(FeCheck *c, const char *name)
{
char number[24];
char *p;
unsigned long n;
sprintf(number, "%u", c->local_serial++);
n = (unsigned long)strlen("fe_l_") + (unsigned long)strlen(name) +
(unsigned long)strlen(number) + 2UL;
p = (char *)fe_arena_alloc(&c->ast->arena, n);
if (!p) return 0;
strcpy(p, "fe_l_");
strcat(p, name ? name : "local");
strcat(p, "_");
strcat(p, number);
return p;
}
static FeScope *scope_new(FeCheckerState *s, FeScope *parent)
{
FeScope *scope;
scope = (FeScope *)fe_arena_alloc(&s->c->ast->arena, sizeof(FeScope));
if (!scope) {
err(s->c, s->c->ast->root->loc, "out of memory creating scope");
return parent;
}
scope->parent = parent;
scope->items = 0;
scope->count = 0;
scope->capacity = 0;
return scope;
}
static FeSym *find_current(FeScope *scope, const char *name)
{
unsigned i;
if (!scope) return 0;
for (i = scope->count; i > 0; --i)
if (strcmp(scope->items[i - 1].name, name) == 0)
return &scope->items[i - 1];
return 0;
}
static FeSym *find_symbol(FeScope *scope, const char *name)
{
FeSym *sym;
while (scope) {
sym = find_current(scope, name);
if (sym) return sym;
scope = scope->parent;
}
return 0;
}
static FeSym *add_symbol(FeCheckerState *s, FeScope *scope,
const char *name, FeType *type, FeNode *fn,
int mutable, int initialized, char *cname,
FeNode *decl)
{
FeSym *items;
unsigned capacity;
FeSym *sym;
if (!name) name = "<unnamed>";
if (find_current(scope, name)) {
err(s->c, decl ? decl->loc : s->c->ast->root->loc,
"duplicate declaration in scope");
return 0;
}
if (scope->count == scope->capacity) {
capacity = scope->capacity ? scope->capacity * 2U : 8U;
items = (FeSym *)fe_arena_alloc(&s->c->ast->arena,
capacity * sizeof(FeSym));
if (!items) {
err(s->c, decl ? decl->loc : s->c->ast->root->loc,
"out of memory growing symbol scope");
return 0;
}
if (scope->items)
memcpy(items, scope->items, scope->count * sizeof(FeSym));
scope->items = items;
scope->capacity = capacity;
}
sym = &scope->items[scope->count++];
sym->name = name;
sym->cname = cname;
sym->type = type;
sym->fn = fn;
sym->mutable = mutable;
sym->initialized = initialized;
if (decl) {
decl->cname = cname;
decl->sem_type = type;
}
return sym;
}
void fe_check_init(FeCheck *c, FeAst *ast, FeDiags *diags,
unsigned pointer_bits)
{
c->ast = ast;
c->diags = diags;
c->pointer_bits = pointer_bits;
c->local_serial = 0;
fe_types_init(&c->types, &ast->arena, pointer_bits);
}
static FeType *check_expr(FeCheckerState *s, FeNode *n);
static FeType *check_identifier(FeCheckerState *s, FeNode *n, int read)
{
FeSym *sym;
sym = find_symbol(s->scope, n->text ? n->text : "");
if (!sym) {
err(s->c, n->loc, "unknown name");
return unknown(s->c);
}
n->cname = sym->cname;
n->sem_type = sym->type;
if (read && !sym->initialized && !sym->fn)
err(s->c, n->loc, "use of uninitialized variable");
return sym->type;
}
static FeType *check_expr(FeCheckerState *s, FeNode *n)
{
FeCheck *c = s->c;
FeType *a;
FeType *b;
FeSym *sym;
FeNode *x;
FeNode *param;
FeNode *arg;
const char *op;
if (!n) return unknown(c);
if (n->kind == FE_N_IDENT)
return check_identifier(s, n, 1);
if (n->kind == FE_N_LITERAL) {
if (!n->text) return unknown(c);
if (strcmp(n->text, "true") == 0 || strcmp(n->text, "false") == 0)
a = fe_type_intern(&c->types, "bool");
else if (n->text[0] == '\'')
a = fe_type_intern(&c->types, "u8");
else if (n->text[0] == '"')
a = unknown(c);
else
a = fe_type_intern(&c->types, "i32");
n->sem_type = a;
return a;
}
if (n->kind == FE_N_UNARY) {
a = check_expr(s, n->a);
op = n->text ? n->text : "";
if (strcmp(op, "not") == 0) {
if (known(a) && a->kind != FE_TYPE_BOOL)
err(c, n->loc, "'not' requires bool");
a = fe_type_intern(&c->types, "bool");
} else if (strcmp(op, "-") == 0) {
if (known(a) && !fe_type_is_integer(a))
err(c, n->loc, "unary '-' requires integer");
}
n->sem_type = a;
return a;
}
if (n->kind == FE_N_TYPE && n->text && strcmp(n->text, "as") == 0) {
a = check_expr(s, n->a);
b = node_type(c, n->b);
if (b->kind == FE_TYPE_VOID)
err(c, n->loc, "cast target cannot be void");
else if ((known(a) && !fe_type_is_integer(a)) ||
(known(b) && !fe_type_is_integer(b)))
err(c, n->loc, "'as' requires integer types");
n->sem_type = b;
return b;
}
if (n->kind == FE_N_BINARY) {
a = check_expr(s, n->a);
b = check_expr(s, n->b);
op = n->text ? n->text : "";
if (strcmp(op, "and") == 0 || strcmp(op, "or") == 0) {
if ((known(a) && a->kind != FE_TYPE_BOOL) ||
(known(b) && b->kind != FE_TYPE_BOOL))
err(c, n->loc, "logical operator requires bool operands");
a = fe_type_intern(&c->types, "bool");
} else if (strcmp(op, "==") == 0 || strcmp(op, "!=") == 0 ||
strcmp(op, "<") == 0 || strcmp(op, "<=") == 0 ||
strcmp(op, ">") == 0 || strcmp(op, ">=") == 0) {
if (known(a) && known(b) && !fe_type_equal(a, b) &&
!compatible(a, b, n->b) && !compatible(b, a, n->a))
err(c, n->loc, "comparison operands have different types");
a = fe_type_intern(&c->types, "bool");
} else {
if ((known(a) && !fe_type_is_integer(a)) ||
(known(b) && !fe_type_is_integer(b)) ||
(known(a) && known(b) && !fe_type_equal(a, b) &&
!compatible(a, b, n->b) && !compatible(b, a, n->a)))
err(c, n->loc,
"arithmetic operands must have the same integer type");
}
n->sem_type = a;
return a;
}
if (n->kind == FE_N_CALL) {
if (n->a && n->a->kind == FE_N_IDENT) {
sym = find_symbol(s->scope, n->a->text ? n->a->text : "");
if (!sym) {
err(c, n->loc, "unknown function");
return unknown(c);
}
n->a->cname = sym->cname;
if (!sym->fn) {
err(c, n->loc, "name is not a function");
return unknown(c);
}
param = sym->fn->a ? sym->fn->a->children : 0;
arg = n->children;
while (param && arg) {
a = check_expr(s, arg);
b = node_type(c, param->a);
if (!compatible(b, a, arg) && a->kind != FE_TYPE_UNKNOWN)
err(c, arg->loc, "argument type mismatch");
param = param->next;
arg = arg->next;
}
if (param || arg) err(c, n->loc, "wrong number of arguments");
a = sym->fn->b ? node_type(c, sym->fn->b) :
fe_type_intern(&c->types, "void");
n->sem_type = a;
return a;
}
for (x = n->children; x; x = x->next) check_expr(s, x);
return unknown(c);
}
if (n->kind == FE_N_MEMBER) {
check_expr(s, n->a);
return unknown(c);
}
return unknown(c);
}
static FeType *check_lvalue(FeCheckerState *s, FeNode *n, int read)
{
FeSym *sym;
if (n && n->kind == FE_N_IDENT) {
sym = find_symbol(s->scope, n->text ? n->text : "");
if (!sym) {
err(s->c, n->loc, "unknown name");
return unknown(s->c);
}
if (sym->fn) {
err(s->c, n->loc, "function is not assignable");
return unknown(s->c);
}
if (!sym->mutable)
err(s->c, n->loc, "cannot assign to immutable let");
n->cname = sym->cname;
n->sem_type = sym->type;
if (read && !sym->initialized)
err(s->c, n->loc, "use of uninitialized variable");
return sym->type;
}
if (n) err(s->c, n->loc, "assignment requires a variable");
return unknown(s->c);
}
static int compound_operator(const char *op)
{
return op && strcmp(op, "=") != 0;
}
static void check_stmt(FeCheckerState *s, FeNode *n)
{
FeCheck *c = s->c;
FeScope *old;
FeType *a;
FeType *b;
FeSym *sym;
FeNode *x;
int initialized;
if (!n) return;
switch (n->kind) {
case FE_N_BLOCK:
old = s->scope;
s->scope = scope_new(s, old);
for (x = n->children; x; x = x->next) check_stmt(s, x);
s->scope = old;
break;
case FE_N_LET:
case FE_N_CONST:
a = n->a ? node_type(c, n->a) : unknown(c);
b = check_expr(s, n->b);
if (!n->a) a = b;
if (a->kind == FE_TYPE_VOID)
err(c, n->loc, "variable cannot have void type");
if (n->a && !compatible(a, b, n->b) && b->kind != FE_TYPE_UNKNOWN)
err(c, n->loc, "initializer type mismatch");
if (b->kind == FE_TYPE_VOID)
err(c, n->loc, "void expression cannot initialize a variable");
add_symbol(s, s->scope, n->text, a, 0, 0, 1,
local_cname(c, n->text ? n->text : "local"), n);
break;
case FE_N_VAR:
a = n->a ? node_type(c, n->a) : unknown(c);
if (!n->b && !n->a)
err(c, n->loc, "uninitialized var requires an explicit type");
b = n->b ? check_expr(s, n->b) : unknown(c);
if (!n->a && n->b) a = b;
if (a->kind == FE_TYPE_VOID)
err(c, n->loc, "variable cannot have void type");
if (n->b && !compatible(a, b, n->b) && b->kind != FE_TYPE_UNKNOWN)
err(c, n->loc, "initializer type mismatch");
if (b->kind == FE_TYPE_VOID)
err(c, n->loc, "void expression cannot initialize a variable");
initialized = n->b != 0;
add_symbol(s, s->scope, n->text, a, 0, 1, initialized,
local_cname(c, n->text ? n->text : "local"), n);
break;
case FE_N_ASSIGN:
b = check_expr(s, n->b);
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");
sym = n->a && n->a->kind == FE_N_IDENT ?
find_symbol(s->scope, n->a->text) : 0;
if (sym && sym->mutable) sym->initialized = 1;
break;
case FE_N_EXPR_STMT:
check_expr(s, n->a);
break;
case FE_N_IF:
a = check_expr(s, n->a);
if (known(a) && a->kind != FE_TYPE_BOOL)
err(c, n->loc, "if condition must be bool");
check_stmt(s, n->b);
check_stmt(s, n->c);
break;
case FE_N_WHILE:
a = check_expr(s, n->a);
if (known(a) && a->kind != FE_TYPE_BOOL)
err(c, n->loc, "while condition must be bool");
check_stmt(s, n->b);
break;
case FE_N_RETURN:
b = n->a ? check_expr(s, n->a) : fe_type_intern(&c->types, "void");
if (known(b) && b->kind == FE_TYPE_VOID && s->ret->kind != FE_TYPE_VOID)
err(c, n->loc, "void expression returned from value function");
else if (known(s->ret) && known(b) && !fe_type_equal(s->ret, b) &&
b->kind != FE_TYPE_UNKNOWN)
err(c, n->loc, "return type mismatch");
break;
case FE_N_UNSAFE:
check_stmt(s, n->a);
break;
default:
break;
}
}
static void check_fn(FeCheck *c, FeNode *fn, FeScope *globals)
{
FeCheckerState s;
FeScope *old;
FeNode *x;
FeType *t;
s.c = c;
s.globals = globals;
s.scope = scope_new(&s, globals);
s.ret = fn->b ? node_type(c, fn->b) : fe_type_intern(&c->types, "void");
fn->sem_type = s.ret;
for (x = fn->a ? fn->a->children : 0; x; x = x->next) {
t = node_type(c, x->a);
if (t->kind == FE_TYPE_VOID)
err(c, x->loc, "parameter cannot have void type");
add_symbol(&s, s.scope, x->text, t, 0, 1, 1,
local_cname(c, x->text ? x->text : "arg"), x);
}
old = s.scope;
if (fn->c) check_stmt(&s, fn->c);
s.scope = old;
}
int fe_check_program(FeCheck *c)
{
FeCheckerState s;
FeNode *n;
FeSym *sym;
FeType *t;
FeType *iv;
s.c = c;
s.scope = scope_new(&s, 0);
s.globals = s.scope;
s.ret = fe_type_intern(&c->types, "void");
for (n = c->ast->root ? c->ast->root->children : 0; n; n = n->next) {
if (n->kind == FE_N_GLOBAL || n->kind == FE_N_CONST) {
t = n->a ? node_type(c, n->a) : unknown(c);
add_symbol(&s, s.globals, n->text, t, 0,
n->kind == FE_N_GLOBAL, n->b != 0,
unit_cname(c, n->text ? n->text : "global"), n);
}
}
for (n = c->ast->root ? c->ast->root->children : 0; n; n = n->next) {
if (n->kind == FE_N_FN) {
t = fe_type_intern(&c->types, "<fn>");
add_symbol(&s, s.globals, n->text, t, n, 0, 1,
unit_cname(c, n->text ? n->text : "fn"), n);
}
}
for (n = c->ast->root ? c->ast->root->children : 0; n; n = n->next) {
if (n->kind == FE_N_GLOBAL || n->kind == FE_N_CONST) {
sym = find_current(s.globals, n->text ? n->text : "");
if (n->b) {
iv = check_expr(&s, n->b);
if (sym && sym->type->kind == FE_TYPE_UNKNOWN) {
sym->type = iv;
n->sem_type = iv;
} else if (sym && !compatible(sym->type, iv, n->b) &&
iv->kind != FE_TYPE_UNKNOWN)
err(c, n->loc, "global initializer type mismatch");
if (iv->kind == FE_TYPE_VOID)
err(c, n->loc, "void expression cannot initialize a global");
}
}
}
for (n = c->ast->root ? c->ast->root->children : 0; n; n = n->next)
if (n->kind == FE_N_FN) check_fn(c, n, s.globals);
return c->diags->errors == 0;
}
FeType *fe_check_expr_type(FeCheck *c, FeNode *n)
{
FeCheckerState s;
s.c = c;
s.scope = scope_new(&s, 0);
s.globals = s.scope;
s.ret = fe_type_intern(&c->types, "void");
return check_expr(&s, n);
}
+19
View File
@@ -0,0 +1,19 @@
#ifndef FE_CHECK_H
#define FE_CHECK_H
#include "types.h"
#include "diag.h"
typedef struct FeCheck {
FeAst *ast;
FeTypeCtx types;
FeDiags *diags;
unsigned pointer_bits;
unsigned local_serial;
} FeCheck;
void fe_check_init(FeCheck *c, FeAst *ast, FeDiags *diags, unsigned pointer_bits);
int fe_check_program(FeCheck *c);
FeType *fe_check_expr_type(FeCheck *c, FeNode *n);
#endif
+10 -4
View File
@@ -1,4 +1,6 @@
#include "parser.h" #include "parser.h"
#include "check.h"
#include "emit_c.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -12,14 +14,18 @@ static char *read_file(const char *name, unsigned long *size)
if(n && fread(p,1,(size_t)n,f)!=(size_t)n){free(p);fclose(f);return 0;} fclose(f);p[n]='\0';*size=(unsigned long)n;return p; if(n && fread(p,1,(size_t)n,f)!=(size_t)n){free(p);fclose(f);return 0;} fclose(f);p[n]='\0';*size=(unsigned long)n;return p;
} }
static void usage(void) static void usage(void)
{ puts("usage: fec [--dump-ast] file.fe [--target=bits16|bits32] [--model=small|large]"); } { puts("usage: fec [--dump-ast|--emit-c] file.fe [--target=bits16|bits32] [-o output.c]"); }
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int i,dump=0; const char *file=0; unsigned long n; char *src; FeDiags d; FeAst ast; FeParser p; int i,dump=0,emit=0; const char *file=0,*outname=0; unsigned long n; char *src; FeDiags d; FeAst ast; FeParser p; FeCheck check; FeEmitter emitter; FILE *out; unsigned pointer_bits=32;
(void)emit;
if(argc<2){usage();return 2;} if(argc<2){usage();return 2;}
for(i=1;i<argc;i++) { if(strcmp(argv[i],"--dump-ast")==0) dump=1; else if(argv[i][0]!='-') file=argv[i]; else if(strncmp(argv[i],"--target=",9)==0 || strncmp(argv[i],"--model=",8)==0 || strcmp(argv[i],"--no-checks")==0 || strcmp(argv[i],"--emit-c")==0 || strcmp(argv[i],"--strip-error-names")==0) { } else if(strcmp(argv[i],"--help")==0){usage();return 0;} else {fprintf(stderr,"fec: unknown option %s\n",argv[i]);return 2;} } for(i=1;i<argc;i++) { if(strcmp(argv[i],"--dump-ast")==0) dump=1; else if(strcmp(argv[i],"--emit-c")==0) emit=1; else if(strcmp(argv[i],"-o")==0){if(i+1>=argc){fprintf(stderr,"fec: -o needs a path\n");return 2;}outname=argv[++i];} else if(strncmp(argv[i],"-o",2)==0 && argv[i][2]) outname=argv[i]+2; else if(strncmp(argv[i],"--target=bits16",15)==0) pointer_bits=16; else if(strncmp(argv[i],"--target=bits32",15)==0) pointer_bits=32; else if(strncmp(argv[i],"--target=",9)==0 || strncmp(argv[i],"--model=",8)==0 || strcmp(argv[i],"--no-checks")==0 || strcmp(argv[i],"--strip-error-names")==0) { } else if(argv[i][0]!='-') file=argv[i]; else if(strcmp(argv[i],"--help")==0){usage();return 0;} else {fprintf(stderr,"fec: unknown option %s\n",argv[i]);return 2;} }
if(!file){fprintf(stderr,"fec: no input file\n");return 2;} if(!file){fprintf(stderr,"fec: no input file\n");return 2;}
src=read_file(file,&n);if(!src)return 2;d.errors=0;d.warnings=0;fe_ast_init(&ast);fe_parser_init(&p,&ast,src,n,file,&d);ast.root=fe_parse_unit(&p); src=read_file(file,&n);if(!src)return 2;d.errors=0;d.warnings=0;fe_ast_init(&ast);fe_parser_init(&p,&ast,src,n,file,&d);ast.root=fe_parse_unit(&p);
if(dump) fe_ast_dump(ast.root,0,stdout); if(dump) { fe_ast_dump(ast.root,0,stdout); fe_ast_destroy(&ast); free(src); return d.errors?1:0; }
fe_check_init(&check,&ast,&d,pointer_bits); if(!fe_check_program(&check)){fe_ast_destroy(&ast);free(src);return 1;}
out=outname?fopen(outname,"w"):stdout; if(!out){fprintf(stderr,"fec: cannot create %s\n",outname);fe_ast_destroy(&ast);free(src);return 2;}
fe_emit_c_init(&emitter,out,&check,pointer_bits);fe_emit_c_program(&emitter);if(outname)fclose(out);
fe_ast_destroy(&ast); free(src); return d.errors?1:0; fe_ast_destroy(&ast); free(src); return d.errors?1:0;
} }
+280
View File
@@ -0,0 +1,280 @@
#include "emit_c.h"
#include <stdio.h>
#include <string.h>
static void pad(FeEmitter *e)
{
int i;
for (i = 0; i < e->indent; ++i) fputs(" ", e->out);
}
static const char *ctype(FeEmitter *e, FeNode *n)
{
FeType *t;
if (n && n->sem_type) t = n->sem_type;
else if (n) t = fe_type_from_ast(&e->check->types, n);
else t = fe_type_intern(&e->check->types, "i32");
return fe_type_c_name(t, e->pointer_bits);
}
static const char *cname(FeNode *n, const char *fallback)
{
return n && n->cname ? n->cname : fallback;
}
static void emit_expr(FeEmitter *e, FeNode *n);
static void emit_stmt(FeEmitter *e, FeNode *n);
static void emit_expr(FeEmitter *e, FeNode *n)
{
FeNode *x;
const char *op;
if (!n) {
fputs("0", e->out);
return;
}
switch (n->kind) {
case FE_N_IDENT:
fputs(cname(n, "fe_missing"), e->out);
break;
case FE_N_LITERAL:
if (n->text && strcmp(n->text, "true") == 0) fputs("1", e->out);
else if (n->text && strcmp(n->text, "false") == 0) fputs("0", e->out);
else fputs(n->text ? n->text : "0", e->out);
break;
case FE_N_UNARY:
op = n->text ? n->text : "";
if (strcmp(op, "not") == 0) fputs("(!", e->out);
else {
fputc('(', e->out);
fputs(op, e->out);
}
emit_expr(e, n->a);
fputc(')', e->out);
break;
case FE_N_BINARY:
op = n->text ? n->text : "+";
fputc('(', e->out);
emit_expr(e, n->a);
if (strcmp(op, "and") == 0) fputs(" && ", e->out);
else if (strcmp(op, "or") == 0) fputs(" || ", e->out);
else fputs(op, e->out);
emit_expr(e, n->b);
fputc(')', e->out);
break;
case FE_N_TYPE:
if (n->text && strcmp(n->text, "as") == 0) {
fputs("((", e->out);
fputs(ctype(e, n->b), e->out);
fputc(')', e->out);
emit_expr(e, n->a);
fputc(')', e->out);
} else emit_expr(e, n->a);
break;
case FE_N_CALL:
if (n->a) emit_expr(e, n->a);
else fputs(n->text ? n->text : "fe_builtin", e->out);
fputc('(', e->out);
for (x = n->children; x; x = x->next) {
if (x != n->children) fputs(", ", e->out);
emit_expr(e, x);
}
fputc(')', e->out);
break;
case FE_N_MEMBER:
emit_expr(e, n->a);
fputc('.', e->out);
if (n->b) fputs(n->b->text ? n->b->text : "member", e->out);
break;
default:
fputs("0", e->out);
break;
}
}
static void emit_decl(FeEmitter *e, FeNode *n)
{
pad(e);
fputs(ctype(e, n), e->out);
fputc(' ', e->out);
fputs(cname(n, "fe_local"), e->out);
fputs(";\n", e->out);
}
static void emit_block(FeEmitter *e, FeNode *n)
{
FeNode *x;
if (!n) {
pad(e);
fputs("{\n", e->out);
++e->indent;
--e->indent;
pad(e);
fputc('}', e->out);
return;
}
pad(e);
fputs("{\n", e->out);
++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);
for (x = n->children; x; x = x->next) emit_stmt(e, x);
--e->indent;
pad(e);
fputc('}', e->out);
}
static void emit_stmt(FeEmitter *e, FeNode *n)
{
if (!n) return;
switch (n->kind) {
case FE_N_BLOCK:
emit_block(e, n);
fputc('\n', e->out);
break;
case FE_N_LET:
case FE_N_VAR:
if (n->b) {
pad(e);
fputs(cname(n, "fe_local"), e->out);
fputs(" = ", e->out);
emit_expr(e, n->b);
fputs(";\n", e->out);
}
break;
case FE_N_ASSIGN:
pad(e);
emit_expr(e, n->a);
fputc(' ', e->out);
fputs(n->text ? n->text : "=", e->out);
fputs(" ", e->out);
emit_expr(e, n->b);
fputs(";\n", e->out);
break;
case FE_N_EXPR_STMT:
pad(e);
emit_expr(e, n->a);
fputs(";\n", e->out);
break;
case FE_N_RETURN:
pad(e);
fputs("return", e->out);
if (n->a) {
fputc(' ', e->out);
emit_expr(e, n->a);
}
fputs(";\n", e->out);
break;
case FE_N_IF:
pad(e);
fputs("if (", e->out);
emit_expr(e, n->a);
fputs(") ", e->out);
if (n->b && n->b->kind == FE_N_BLOCK) emit_block(e, n->b);
else emit_block(e, 0);
if (n->c) {
fputs(" else ", e->out);
if (n->c->kind == FE_N_IF) emit_stmt(e, n->c);
else emit_block(e, n->c);
}
fputc('\n', e->out);
break;
case FE_N_WHILE:
pad(e);
fputs("while (", e->out);
emit_expr(e, n->a);
fputs(") ", e->out);
if (n->b && n->b->kind == FE_N_BLOCK) emit_block(e, n->b);
else emit_block(e, 0);
fputc('\n', e->out);
break;
default:
break;
}
}
static void emit_fn(FeEmitter *e, FeNode *fn, int prototype)
{
FeNode *p;
const char *ret;
ret = fn->sem_type ? fe_type_c_name(fn->sem_type, e->pointer_bits) :
(fn->b ? ctype(e, fn->b) : "void");
fputs(ret, e->out);
fputc(' ', e->out);
fputs(cname(fn, "fe_fn"), e->out);
fputc('(', e->out);
p = fn->a ? fn->a->children : 0;
if (!p) fputs("void", e->out);
while (p) {
if (p != fn->a->children) fputs(", ", e->out);
fputs(ctype(e, p->a), e->out);
fputc(' ', e->out);
fputs(cname(p, "fe_arg"), e->out);
p = p->next;
}
fputc(')', e->out);
if (prototype) fputs(";\n", e->out);
else {
fputs(" ", e->out);
emit_block(e, fn->c);
fputc('\n', e->out);
}
}
static void emit_main_wrapper(FeEmitter *e, FeNode *fn)
{
fputs("int main(void) {\n ", e->out);
if (fn->sem_type && fn->sem_type->kind == FE_TYPE_VOID) {
fputs(cname(fn, "fe_main"), e->out);
fputs("();\n return 0;\n", e->out);
} else {
fputs("return ", e->out);
fputs(cname(fn, "fe_main"), e->out);
fputs("();\n", e->out);
}
fputs("}\n", e->out);
}
void fe_emit_c_init(FeEmitter *e, FILE *out, FeCheck *check,
unsigned pointer_bits)
{
e->out = out;
e->check = check;
e->pointer_bits = pointer_bits;
e->indent = 0;
}
void fe_emit_c_program(FeEmitter *e)
{
FeNode *n;
FeNode *main_fn = 0;
fputs("/* generated by fec M2 */\n#include <stddef.h>\n\n", e->out);
for (n = e->check->ast->root ? e->check->ast->root->children : 0;
n; n = n->next) {
if (n->kind == FE_N_GLOBAL || n->kind == FE_N_CONST) {
fputs(ctype(e, n), e->out);
fputc(' ', e->out);
fputs(cname(n, "fe_global"), e->out);
if (n->b) {
fputs(" = ", e->out);
emit_expr(e, n->b);
}
fputs(";\n", e->out);
}
}
for (n = e->check->ast->root ? e->check->ast->root->children : 0;
n; n = n->next)
if (n->kind == FE_N_FN) {
emit_fn(e, n, 1);
if (n->text && strcmp(n->text, "main") == 0) main_fn = n;
}
fputc('\n', e->out);
for (n = e->check->ast->root ? e->check->ast->root->children : 0;
n; n = n->next)
if (n->kind == FE_N_FN) emit_fn(e, n, 0);
if (main_fn) {
fputc('\n', e->out);
emit_main_wrapper(e, main_fn);
}
}
+16
View File
@@ -0,0 +1,16 @@
#ifndef FE_EMIT_C_H
#define FE_EMIT_C_H
#include "check.h"
typedef struct FeEmitter {
FILE *out;
FeCheck *check;
unsigned pointer_bits;
int indent;
} FeEmitter;
void fe_emit_c_init(FeEmitter *e, FILE *out, FeCheck *check, unsigned pointer_bits);
void fe_emit_c_program(FeEmitter *e);
#endif
+2 -2
View File
@@ -172,8 +172,8 @@ static FeNode *statement(FeParser *p)
{ {
FeToken t=p->current; FeNode *n,*e; FeToken t=p->current; FeNode *n,*e;
if(is(p,FE_TOK_LBRACE)) return block(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);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_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);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_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))next(p);else error(p,"expected constant name");if(eat(p,FE_TOK_COLON))n->a=type(p);want(p,FE_TOK_EQ,"expected '=' in const");n->b=expr(p,0);want(p,FE_TOK_SEMI,"expected ';'");return n; }
if(eat(p,FE_TOK_IF)) { n=toknode(p,FE_N_IF,t);if(eat(p,FE_TOK_LET)){n->text=fe_arena_strdup(&p->ast->arena,"if let",6);if(is_name(p))next(p);if(eat(p,FE_TOK_LPAREN)){if(is_name(p))next(p);want(p,FE_TOK_RPAREN,"expected ')' in if let pattern");}want(p,FE_TOK_EQ,"expected '=' in if let");}n->a=expr(p,0);n->b=block(p);if(eat(p,FE_TOK_ELSE))n->c=is(p,FE_TOK_IF)?statement(p):block(p);return n; } if(eat(p,FE_TOK_IF)) { n=toknode(p,FE_N_IF,t);if(eat(p,FE_TOK_LET)){n->text=fe_arena_strdup(&p->ast->arena,"if let",6);if(is_name(p))next(p);if(eat(p,FE_TOK_LPAREN)){if(is_name(p))next(p);want(p,FE_TOK_RPAREN,"expected ')' in if let pattern");}want(p,FE_TOK_EQ,"expected '=' in if let");}n->a=expr(p,0);n->b=block(p);if(eat(p,FE_TOK_ELSE))n->c=is(p,FE_TOK_IF)?statement(p):block(p);return n; }
if(eat(p,FE_TOK_COMPTIME)) { n=toknode(p,FE_N_IF,t);want(p,FE_TOK_IF,"expected 'if' after comptime");n->text=fe_arena_strdup(&p->ast->arena,"comptime if",11);n->a=expr(p,0);n->b=block(p);if(eat(p,FE_TOK_ELSE))n->c=is(p,FE_TOK_IF)?statement(p):block(p);return n; } if(eat(p,FE_TOK_COMPTIME)) { n=toknode(p,FE_N_IF,t);want(p,FE_TOK_IF,"expected 'if' after comptime");n->text=fe_arena_strdup(&p->ast->arena,"comptime if",11);n->a=expr(p,0);n->b=block(p);if(eat(p,FE_TOK_ELSE))n->c=is(p,FE_TOK_IF)?statement(p):block(p);return n; }
+50
View File
@@ -0,0 +1,50 @@
#include "types.h"
#include <string.h>
void fe_types_init(FeTypeCtx *ctx, FeArena *arena, unsigned pointer_bits)
{ ctx->arena=arena; ctx->types=0; ctx->pointer_bits=pointer_bits; }
FeType *fe_type_intern(FeTypeCtx *ctx, const char *name)
{
FeType *t; unsigned i; unsigned bits=0; int uns=0; FeTypeKind kind=FE_TYPE_UNKNOWN;
if (!name) name="<unknown>";
for (t=ctx->types;t;t=t->next) if(strcmp(t->name,name)==0) return t;
if(strcmp(name,"void")==0) kind=FE_TYPE_VOID;
else if(strcmp(name,"bool")==0) kind=FE_TYPE_BOOL;
else if(strcmp(name,"i8")==0||strcmp(name,"u8")==0){kind=FE_TYPE_INT;bits=8;uns=name[0]=='u';}
else if(strcmp(name,"i16")==0||strcmp(name,"u16")==0){kind=FE_TYPE_INT;bits=16;uns=name[0]=='u';}
else if(strcmp(name,"i32")==0||strcmp(name,"u32")==0){kind=FE_TYPE_INT;bits=32;uns=name[0]=='u';}
else if(strcmp(name,"usize")==0||strcmp(name,"isize")==0){kind=FE_TYPE_INT;bits=ctx->pointer_bits;uns=name[0]=='u';}
t=(FeType *)fe_arena_alloc(ctx->arena,sizeof(FeType)); if(!t)return 0;
for(i=0;i<sizeof(t->name)-1 && name[i];i++) t->name[i]=name[i];
t->name[i]='\0';
t->kind=kind;t->bits=bits;t->is_unsigned=uns;t->next=ctx->types;ctx->types=t;return t;
}
FeType *fe_type_from_ast(FeTypeCtx *ctx, const FeNode *node)
{
if(!node)return fe_type_intern(ctx,"<unknown>");
if(node->kind!=FE_N_TYPE)return fe_type_intern(ctx,"<unknown>");
if(node->text && (strcmp(node->text,"?")==0||strcmp(node->text,"!")==0||strcmp(node->text,"^")==0||strcmp(node->text,"&")==0||strcmp(node->text,"*")==0||strcmp(node->text,"far")==0))return fe_type_intern(ctx,"<unknown>");
if(node->text && strcmp(node->text,"as")==0)return fe_type_from_ast(ctx,node->b);
if(node->text && strcmp(node->text,"fn")==0)return fe_type_intern(ctx,"<unknown>");
return fe_type_intern(ctx,node->text);
}
int fe_type_equal(const FeType *a,const FeType *b){return a==b || (a&&b&&strcmp(a->name,b->name)==0);}
int fe_type_is_integer(const FeType *t){return t&&t->kind==FE_TYPE_INT;}
const char *fe_type_c_name(const FeType *t,unsigned pointer_bits)
{
if(!t)return "int";
if(t->kind==FE_TYPE_VOID)return "void";
if(t->kind==FE_TYPE_BOOL)return "unsigned char";
if(t->kind!=FE_TYPE_INT)return "int";
if(strcmp(t->name,"usize")==0)return pointer_bits==16?"unsigned short":"unsigned long";
if(strcmp(t->name,"isize")==0)return pointer_bits==16?"short":"long";
if(strcmp(t->name,"i8")==0)return "signed char";
if(strcmp(t->name,"u8")==0)return "unsigned char";
if(strcmp(t->name,"i16")==0)return "short";
if(strcmp(t->name,"u16")==0)return "unsigned short";
if(strcmp(t->name,"i32")==0)return "long";
if(strcmp(t->name,"u32")==0)return "unsigned long";
return "int";
}
+31
View File
@@ -0,0 +1,31 @@
#ifndef FE_TYPES_H
#define FE_TYPES_H
#include "ast.h"
typedef enum FeTypeKind {
FE_TYPE_ERROR, FE_TYPE_VOID, FE_TYPE_BOOL, FE_TYPE_INT, FE_TYPE_UNKNOWN
} FeTypeKind;
struct FeType {
FeTypeKind kind;
char name[16];
unsigned bits;
int is_unsigned;
FeType *next;
};
typedef struct FeTypeCtx {
FeArena *arena;
FeType *types;
unsigned pointer_bits;
} FeTypeCtx;
void fe_types_init(FeTypeCtx *ctx, FeArena *arena, unsigned pointer_bits);
FeType *fe_type_intern(FeTypeCtx *ctx, const char *name);
FeType *fe_type_from_ast(FeTypeCtx *ctx, const FeNode *node);
int fe_type_equal(const FeType *a, const FeType *b);
int fe_type_is_integer(const FeType *t);
const char *fe_type_c_name(const FeType *t, unsigned pointer_bits);
#endif
+53
View File
@@ -6,6 +6,10 @@ if exist TEST.OK del TEST.OK
if exist TEST.FAIL del TEST.FAIL if exist TEST.FAIL del TEST.FAIL
call C:\FEC\BUILD.BAT call C:\FEC\BUILD.BAT
if not exist BUILD.OK goto test_fail if not exist BUILD.OK goto test_fail
if "%WATCOM%"=="" set WATCOM=C:\DEVEL\WATCOMC
if not exist %WATCOM%\BINW\WCL.EXE goto test_fail
if not exist %WATCOM%\BINW\WCL386.EXE goto test_fail
set PATH=%WATCOM%\BINW;%WATCOM%\BINP;%PATH%
fec.exe --dump-ast TESTS\PASS\BASIC.FE > nul fec.exe --dump-ast TESTS\PASS\BASIC.FE > nul
if errorlevel 1 goto test_fail if errorlevel 1 goto test_fail
@@ -40,6 +44,55 @@ if not errorlevel 1 goto test_fail
fec.exe --dump-ast TESTS\FAIL\LOGICA.FE > nul fec.exe --dump-ast TESTS\FAIL\LOGICA.FE > nul
if not errorlevel 1 goto test_fail if not errorlevel 1 goto test_fail
if exist TESTS\M2\HELLO.C del TESTS\M2\HELLO.C
if exist TESTS\M2\HELLO.EXE del TESTS\M2\HELLO.EXE
if exist TESTS\M2\SCOPES.C del TESTS\M2\SCOPES.C
if exist TESTS\M2\SCOPES.EXE del TESTS\M2\SCOPES.EXE
if exist TESTS\M2\CAST16.C del TESTS\M2\CAST16.C
if exist TESTS\M2\CAST16.EXE del TESTS\M2\CAST16.EXE
rem M2 bits32 path: Open Watcom 32-bit compiler and DOS extender executable.
fec.exe --target=bits32 --emit-c TESTS\M2\HELLO.FE -o TESTS\M2\HELLO.C > nul
if errorlevel 1 goto test_fail
wcl386 -q -za -bt=dos -fe=TESTS\M2\HELLO.EXE TESTS\M2\HELLO.C
if errorlevel 1 goto test_fail
TESTS\M2\HELLO.EXE
if errorlevel 1 goto test_fail
fec.exe --target=bits32 --emit-c TESTS\M2\SCOPES.FE -o TESTS\M2\SCOPES.C > nul
if errorlevel 1 goto test_fail
wcl386 -q -za -bt=dos -fe=TESTS\M2\SCOPES.EXE TESTS\M2\SCOPES.C
if errorlevel 1 goto test_fail
TESTS\M2\SCOPES.EXE
if errorlevel 1 goto test_fail
rem M2 bits16 regression path remains on compiler A (wcl).
fec.exe --target=bits16 --emit-c TESTS\M2\CAST-W.FE -o TESTS\M2\CAST16.C > nul
if errorlevel 1 goto test_fail
wcl -q -za -bt=dos -fe=TESTS\M2\CAST16.EXE TESTS\M2\CAST16.C
if errorlevel 1 goto test_fail
TESTS\M2\CAST16.EXE
if errorlevel 1 goto test_fail
fec.exe --target=bits32 --emit-c TESTS\M2\BAD-CO.FE -o TESTS\M2\BAD-CO.C > nul
if not errorlevel 1 goto test_fail
fec.exe --target=bits32 --emit-c TESTS\M2\BAD-CA.FE -o TESTS\M2\BAD-CA.C > nul
if not errorlevel 1 goto test_fail
fec.exe --target=bits32 --emit-c TESTS\M2\BAD-AS.FE -o TESTS\M2\BAD-AS.C > nul
if not errorlevel 1 goto test_fail
fec.exe --target=bits32 --emit-c TESTS\M2\BAD-UN.FE -o TESTS\M2\BAD-UN.C > nul
if not errorlevel 1 goto test_fail
fec.exe --target=bits32 --emit-c TESTS\M2\BAD-AR.FE -o TESTS\M2\BAD-AR.C > nul
if not errorlevel 1 goto test_fail
fec.exe --target=bits32 --emit-c TESTS\M2\BAD-TY.FE -o TESTS\M2\BAD-TY.C > nul
if not errorlevel 1 goto test_fail
fec.exe --target=bits32 --emit-c TESTS\M2\BAD-RE.FE -o TESTS\M2\BAD-RE.C > nul
if not errorlevel 1 goto test_fail
fec.exe --target=bits32 --emit-c TESTS\M2\BAD-UI.FE -o TESTS\M2\BAD-UI.C > nul
if not errorlevel 1 goto test_fail
fec.exe --target=bits32 --emit-c TESTS\M2\BAD-VO.FE -o TESTS\M2\BAD-VO.C > nul
if not errorlevel 1 goto test_fail
echo OK>TEST.OK echo OK>TEST.OK
cd C:\FEC cd C:\FEC
goto test_done goto test_done
+9
View File
@@ -0,0 +1,9 @@
unit bad_arity;
fn add(a: i32, b: i32) -> i32 {
return a + b;
}
fn main() -> i32 {
return add(1);
}
+7
View File
@@ -0,0 +1,7 @@
unit bad_assign;
fn main() -> i32 {
let value: i32 = 1;
value = 2;
return value;
}
+6
View File
@@ -0,0 +1,6 @@
unit bad_cast;
fn main() -> i32 {
let x: i32 = true as i32;
return x;
}
+6
View File
@@ -0,0 +1,6 @@
unit bad_condition;
fn main() -> i32 {
if 1 { return 0; }
return 1;
}
+5
View File
@@ -0,0 +1,5 @@
unit bad_return;
fn main() -> i32 {
return true;
}
+9
View File
@@ -0,0 +1,9 @@
unit bad_types;
fn add(a: i32, b: i32) -> i32 {
return a + b;
}
fn main() -> i32 {
return add(true, 1);
}
+6
View File
@@ -0,0 +1,6 @@
unit bad_uninit;
fn main() -> i32 {
var value: i32;
return value;
}
+5
View File
@@ -0,0 +1,5 @@
unit bad_unknown;
fn main() -> i32 {
return missing_name;
}
+10
View File
@@ -0,0 +1,10 @@
unit bad_void;
fn noop() {
return;
}
fn main() -> i32 {
let value: i32 = noop();
return value;
}
+11
View File
@@ -0,0 +1,11 @@
unit cast_while;
pub fn main() -> i32 {
var x: i16 = 0;
while x < 3 {
x += 1;
}
let y: i32 = x as i32;
if y == 3 { return 0; }
return 1;
}
+23
View File
@@ -0,0 +1,23 @@
unit hello;
fn add(a: i32, b: i32) -> i32 {
return a + b;
}
fn is_answer(value: i32) -> bool {
return value == 42;
}
fn touch() {
return;
}
pub fn main() -> i32 {
touch();
let value: i32 = add(20, 22);
if is_answer(value) {
return 0;
} else {
return 1;
}
}
+15
View File
@@ -0,0 +1,15 @@
unit scopes;
fn register(switch: i32) -> i32 {
let auto: i32 = switch;
if true {
let auto: i32 = auto + 1;
if auto == 2 { return 0; }
}
if auto == 1 { return 0; }
return 1;
}
pub fn main() -> i32 {
return register(1);
}
+22
View File
@@ -18,3 +18,25 @@ for f in "$root"/tests/fail/*.fe; do
ok=$((ok+1)) ok=$((ok+1))
done done
echo "M1 tests: $ok cases passed" echo "M1 tests: $ok cases passed"
m2tmp=$(mktemp -d)
trap 'rm -rf "$m2tmp"' EXIT HUP INT TERM
"$root"/fec --emit-c "$root"/tests/m2/hello.fe -o "$m2tmp/hello.c"
${CC:-cc} -std=c89 -pedantic "$m2tmp/hello.c" -o "$m2tmp/hello"
"$m2tmp/hello"
"$root"/fec --target=bits16 --emit-c "$root"/tests/m2/hello.fe -o "$m2tmp/hello16.c"
${CC:-cc} -std=c89 -pedantic "$m2tmp/hello16.c" -o "$m2tmp/hello16"
"$m2tmp/hello16"
"$root"/fec --emit-c "$root"/tests/m2/cast-while.fe -o "$m2tmp/cast.c"
${CC:-cc} -std=c89 -pedantic "$m2tmp/cast.c" -o "$m2tmp/cast"
"$m2tmp/cast"
"$root"/fec --emit-c "$root"/tests/m2/scopes.fe -o "$m2tmp/scopes.c"
${CC:-cc} -std=c89 -pedantic "$m2tmp/scopes.c" -o "$m2tmp/scopes"
"$m2tmp/scopes"
for f in bad-condition bad-cast bad-assign bad-unknown bad-arity bad-types bad-return bad-uninit bad-void; do
if "$root"/fec --emit-c "$root"/tests/m2/$f.fe -o "$m2tmp/$f.c" >/dev/null 2>/dev/null; then
echo "FAIL (accepted M2 semantic error): $f.fe"
exit 1
fi
done
echo "M2 tests: integer control-flow smoke passed"
+38
View File
@@ -6,7 +6,9 @@ if not exist C:\FEC\STD md C:\FEC\STD
if not exist C:\FEC\TESTS md C:\FEC\TESTS if not exist C:\FEC\TESTS md C:\FEC\TESTS
if not exist C:\FEC\TESTS\PASS md C:\FEC\TESTS\PASS if not exist C:\FEC\TESTS\PASS md C:\FEC\TESTS\PASS
if not exist C:\FEC\TESTS\FAIL md C:\FEC\TESTS\FAIL 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 exist C:\FEC\VM.FAIL del C:\FEC\VM.FAIL if exist C:\FEC\VM.FAIL del C:\FEC\VM.FAIL
if exist C:\FEC\STAGE.FAIL del C:\FEC\STAGE.FAIL
copy D:\FEC\BUILD-~1.BAT C:\FEC\BUILD.BAT > nul copy D:\FEC\BUILD-~1.BAT C:\FEC\BUILD.BAT > nul
if errorlevel 1 goto stage_fail if errorlevel 1 goto stage_fail
@@ -35,6 +37,18 @@ copy D:\FEC\SRC\PARSER.H C:\FEC\SRC\PARSER.H > nul
if errorlevel 1 goto stage_fail if errorlevel 1 goto stage_fail
copy D:\FEC\SRC\DRIVER.C C:\FEC\SRC\DRIVER.C > nul copy D:\FEC\SRC\DRIVER.C C:\FEC\SRC\DRIVER.C > nul
if errorlevel 1 goto stage_fail if errorlevel 1 goto stage_fail
copy D:\FEC\SRC\TYPES.C C:\FEC\SRC\TYPES.C > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\SRC\TYPES.H C:\FEC\SRC\TYPES.H > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\SRC\CHECK.C C:\FEC\SRC\CHECK.C > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\SRC\CHECK.H C:\FEC\SRC\CHECK.H > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\SRC\EMIT_C.C C:\FEC\SRC\EMIT_C.C > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\SRC\EMIT_C.H C:\FEC\SRC\EMIT_C.H > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\STD\CORE.FE C:\FEC\STD\CORE.FE > nul copy D:\FEC\STD\CORE.FE C:\FEC\STD\CORE.FE > nul
if errorlevel 1 goto stage_fail if errorlevel 1 goto stage_fail
@@ -67,6 +81,30 @@ copy D:\FEC\TESTS\FAIL\UNCLOS~1.FE C:\FEC\TESTS\FAIL\UNCLOS.FE > nul
if errorlevel 1 goto stage_fail if errorlevel 1 goto stage_fail
copy D:\FEC\TESTS\FAIL\LOGICA~1.FE C:\FEC\TESTS\FAIL\LOGICA.FE > nul copy D:\FEC\TESTS\FAIL\LOGICA~1.FE C:\FEC\TESTS\FAIL\LOGICA.FE > nul
if errorlevel 1 goto stage_fail if errorlevel 1 goto stage_fail
copy D:\FEC\TESTS\M2\HELLO.FE C:\FEC\TESTS\M2\HELLO.FE > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\TESTS\M2\SCOPES.FE C:\FEC\TESTS\M2\SCOPES.FE > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\TESTS\M2\BAD-CO~1.FE C:\FEC\TESTS\M2\BAD-CO.FE > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\TESTS\M2\BAD-CAST.FE C:\FEC\TESTS\M2\BAD-CA.FE > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\TESTS\M2\BAD-ASSI~1.FE C:\FEC\TESTS\M2\BAD-AS.FE > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\TESTS\M2\BAD-UN~1.FE C:\FEC\TESTS\M2\BAD-UN.FE > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\TESTS\M2\BAD-UN~2.FE C:\FEC\TESTS\M2\BAD-UI.FE > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\TESTS\M2\BAD-AR~1.FE C:\FEC\TESTS\M2\BAD-AR.FE > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\TESTS\M2\BAD-TY~1.FE C:\FEC\TESTS\M2\BAD-TY.FE > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\TESTS\M2\BAD-RE~1.FE C:\FEC\TESTS\M2\BAD-RE.FE > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\TESTS\M2\BAD-VOID.FE C:\FEC\TESTS\M2\BAD-VO.FE > nul
if errorlevel 1 goto stage_fail
copy D:\FEC\TESTS\M2\CAST-W~1.FE C:\FEC\TESTS\M2\CAST-W.FE > nul
if errorlevel 1 goto stage_fail
call C:\FEC\TEST-DOS.BAT call C:\FEC\TEST-DOS.BAT
if exist C:\FEC\TEST.OK goto vm_success if exist C:\FEC\TEST.OK goto vm_success