diff options
-rw-r--r-- | main.c | 1 | ||||
-rw-r--r-- | parse.c | 12 | ||||
-rw-r--r-- | test.toc | 13 | ||||
-rw-r--r-- | types.c | 22 |
4 files changed, 38 insertions, 10 deletions
@@ -1,6 +1,5 @@ /* TODO: -allow void block expression statements not to have semicolons don't allow nested functions to capture outer variables (constants are allowed though) if, else re-do cgen @@ -49,10 +49,17 @@ typedef struct Type { }; } Type; +typedef enum { + BLOCK_FN, + BLOCK_EXPR +} BlockKind; + typedef struct Block { + BlockKind kind; Location start; Location end; Array stmts; + struct Block *parent; struct Expression *ret_expr; /* the return expression of this block, e.g. {foo(); 3} => 3 NULL for no expression. */ } Block; @@ -555,6 +562,7 @@ static bool parse_stmt(Parser *p, Statement *s); static bool parse_block(Parser *p, Block *b) { Tokenizer *t = p->tokr; Block *prev_block = p->block; + b->parent = prev_block; p->block = b; if (!token_is_kw(t->token, KW_LBRACE)) { tokr_err(t, "Expected '{' to open block."); @@ -566,7 +574,7 @@ static bool parse_block(Parser *p, Block *b) { bool ret = true; b->ret_expr = NULL; /* default to no return unless overwritten later */ if (!token_is_kw(t->token, KW_RBRACE)) { - /* non-empty function body */ + /* non-empty block */ while (1) { Statement *stmt = arr_add(&b->stmts); bool success = parse_stmt(p, stmt); @@ -629,6 +637,7 @@ static bool parse_fn_expr(Parser *p, FnExpr *f) { return false; } } + f->body.kind = BLOCK_FN; return parse_block(p, &f->body); } @@ -918,6 +927,7 @@ static bool parse_expr(Parser *p, Expression *e, Token *end) { if (token_is_kw(t->token, KW_LBRACE)) { /* it's a block */ e->kind = EXPR_BLOCK; + e->block.kind = BLOCK_EXPR; if (!parse_block(p, &e->block)) return false; if (t->token != end) { tokr_err(t, "Expression continues after end of block."); /* TODO: improve this err message */ @@ -1,7 +1,12 @@ +x := 3; + main @= fn() { - f := main; - f(); - { main(); } - f(); + y := 5; + z @= 10; + foo @= fn() { + bar := y; + asdf := z; + gfdij := x; + }; }; @@ -1,5 +1,6 @@ typedef struct { Array in_decls; /* array of declarations we are currently inside */ + Block *block; } Typer; static bool type_of_expr(Typer *tr, Expression *e); @@ -128,10 +129,7 @@ static bool expr_must_lval(Expression *e) { switch (e->kind) { case EXPR_IDENT: { IdentDecl *id_decl = ident_decl(e->ident); - if (!id_decl) { - err_print(e->where, "Undeclared identifier."); - return false; - } + assert(id_decl); Declaration *d = id_decl->decl; if (d->flags & DECL_FLAG_CONST) { char *istr = ident_to_str(e->ident); @@ -163,6 +161,18 @@ static bool type_of_ident(Typer *tr, Location where, Identifier i, Type *t, bool return false; } Declaration *d = decl->decl; + bool captured = false; + if (decl->scope != NULL) + for (Block *block = tr->block; block != decl->scope; block = block->parent) { + if (block->kind == BLOCK_FN) { + captured = true; + break; + } + } + if (captured && !(d->flags & DECL_FLAG_CONST)) { + err_print(where, "Variables cannot be captured into inner functions (but constants can)."); + return false; + } /* are we inside this declaration? */ typedef Declaration *DeclarationPtr; arr_foreach(&tr->in_decls, DeclarationPtr, in_decl) { @@ -504,6 +514,8 @@ static bool type_of_expr(Typer *tr, Expression *e) { static bool types_block(Typer *tr, Block *b) { bool ret = true; + Block *prev_block = tr->block; + tr->block = b; if (!block_enter(b, &b->stmts)) return false; arr_foreach(&b->stmts, Statement, s) { if (!types_stmt(tr, s)) ret = false; @@ -512,6 +524,7 @@ static bool types_block(Typer *tr, Block *b) { if (!types_expr(tr, b->ret_expr)) ret = false; block_exit(b, &b->stmts); + tr->block = prev_block; return ret; } @@ -617,6 +630,7 @@ static bool types_stmt(Typer *tr, Statement *s) { } static void typer_create(Typer *tr) { + tr->block = NULL; arr_create(&tr->in_decls, sizeof(Declaration *)); } |