feat: add M2 type checking and C emission
This commit is contained in:
+1
-1
@@ -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
@@ -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
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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
@@ -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);
|
||||||
|
}
|
||||||
@@ -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
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
@@ -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; }
|
||||||
|
|||||||
@@ -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";
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
unit bad_arity;
|
||||||
|
|
||||||
|
fn add(a: i32, b: i32) -> i32 {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> i32 {
|
||||||
|
return add(1);
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
unit bad_assign;
|
||||||
|
|
||||||
|
fn main() -> i32 {
|
||||||
|
let value: i32 = 1;
|
||||||
|
value = 2;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
unit bad_cast;
|
||||||
|
|
||||||
|
fn main() -> i32 {
|
||||||
|
let x: i32 = true as i32;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
unit bad_condition;
|
||||||
|
|
||||||
|
fn main() -> i32 {
|
||||||
|
if 1 { return 0; }
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
unit bad_return;
|
||||||
|
|
||||||
|
fn main() -> i32 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
unit bad_types;
|
||||||
|
|
||||||
|
fn add(a: i32, b: i32) -> i32 {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> i32 {
|
||||||
|
return add(true, 1);
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
unit bad_uninit;
|
||||||
|
|
||||||
|
fn main() -> i32 {
|
||||||
|
var value: i32;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
unit bad_unknown;
|
||||||
|
|
||||||
|
fn main() -> i32 {
|
||||||
|
return missing_name;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
unit bad_void;
|
||||||
|
|
||||||
|
fn noop() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> i32 {
|
||||||
|
let value: i32 = noop();
|
||||||
|
return value;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user