diff options
-rw-r--r-- | cgen.c | 21 | ||||
-rw-r--r-- | decls_cgen.c | 41 | ||||
-rw-r--r-- | main.c | 5 | ||||
-rw-r--r-- | parse.c | 41 | ||||
-rw-r--r-- | test.toc | 21 | ||||
-rw-r--r-- | types.c | 60 | ||||
-rw-r--r-- | types.h | 35 |
7 files changed, 147 insertions, 77 deletions
@@ -51,10 +51,9 @@ static void cgen_defs_decl(CGenerator *g, Declaration *d); g->block = prev_block; \ } -/* calls f on every sub-expression of e, block_f on every sub-block, and decl_f on every sub-declaration. */ -#define cgen_recurse_subexprs(g, e, f, block_f, decl_f) \ +/* calls f on every sub-expression of e, block_f on every sub-block, type_f on every type, and decl_f on every sub-declaration. */ +#define cgen_recurse_subexprs(g, e, f, block_f, decl_f, type_f) \ switch (e->kind) { \ - case EXPR_TYPE: \ case EXPR_VAL: \ case EXPR_C: \ case EXPR_BUILTIN: \ @@ -64,6 +63,9 @@ static void cgen_defs_decl(CGenerator *g, Declaration *d); case EXPR_LITERAL_STR: \ case EXPR_LITERAL_CHAR: \ case EXPR_LITERAL_FLOAT: \ + break; \ + case EXPR_TYPE: \ + type_f(g, e->typeval); \ break; \ case EXPR_UNARY_OP: \ f(g, e->unary.of); \ @@ -75,6 +77,7 @@ static void cgen_defs_decl(CGenerator *g, Declaration *d); break; \ case EXPR_CAST: \ f(g, e->cast.expr); \ + type_f(g, &e->cast.type); \ break; \ case EXPR_CALL: \ f(g, e->call.fn); \ @@ -2164,6 +2167,16 @@ static void cgen_stmt(CGenerator *g, Statement *s) { } } +static void cgen_defs_type(CGenerator *g, Type *t) { + if (t->kind == TYPE_STRUCT) { + StructDef *sdef = t->struc; + if (!(sdef->flags & STRUCT_DEF_CGEN_FN_DEFS)) { + cgen_defs_block(g, &sdef->body); + sdef->flags |= STRUCT_DEF_CGEN_FN_DEFS; + } + } +} + static void cgen_defs_fn(CGenerator *g, FnExpr *f) { if (fn_has_instances(f)) { HashTable *instances = f->instances; @@ -2184,7 +2197,7 @@ static void cgen_defs_expr(CGenerator *g, Expression *e) { if (e->kind == EXPR_FN) { cgen_defs_fn(g, e->fn); } - cgen_recurse_subexprs(g, e, cgen_defs_expr, cgen_defs_block, cgen_defs_decl); + cgen_recurse_subexprs(g, e, cgen_defs_expr, cgen_defs_block, cgen_defs_decl, cgen_defs_type); } static void cgen_defs_decl(CGenerator *g, Declaration *d) { diff --git a/decls_cgen.c b/decls_cgen.c index 97c63ec..5d527cd 100644 --- a/decls_cgen.c +++ b/decls_cgen.c @@ -10,6 +10,19 @@ static void cgen_decls_stmt(CGenerator *g, Statement *s); static void cgen_decls_block(CGenerator *g, Block *b); static void cgen_decls_decl(CGenerator *g, Declaration *d); +static void cgen_sdecls_block(CGenerator *g, Block *b) { + Block *prev_block = g->block; + g->block = b; + b->c.break_lbl = 0; + b->c.cont_lbl = 0; + + arr_foreach(b->stmts, Statement, s) + cgen_sdecls_stmt(g, s); + if (b->ret_expr) + cgen_sdecls_expr(g, b->ret_expr); + g->block = prev_block; +} + /* i is the name for this type, NULL if not available */ static void cgen_sdecls_type(CGenerator *g, Type *type) { if (type->kind == TYPE_STRUCT) { @@ -24,25 +37,13 @@ static void cgen_sdecls_type(CGenerator *g, Type *type) { cgen_struct_name(g, sdef); cgen_write(g, ";"); cgen_nl(g); + cgen_sdecls_block(g, &sdef->body); sdef->flags |= STRUCT_DEF_CGEN_DECLARED; } } cgen_recurse_subtypes(cgen_sdecls_type, g, type); } -static void cgen_sdecls_block(CGenerator *g, Block *b) { - Block *prev_block = g->block; - g->block = b; - b->c.break_lbl = 0; - b->c.cont_lbl = 0; - - arr_foreach(b->stmts, Statement, s) - cgen_sdecls_stmt(g, s); - if (b->ret_expr) - cgen_sdecls_expr(g, b->ret_expr); - g->block = prev_block; -} - static char *cgen_nms_prefix_part(CGenerator *g, Namespace *n) { char *s; if (n->associated_ident) { @@ -65,9 +66,6 @@ static char *cgen_nms_prefix_part(CGenerator *g, Namespace *n) { static void cgen_sdecls_expr(CGenerator *g, Expression *e) { switch (e->kind) { - case EXPR_CAST: - cgen_sdecls_type(g, &e->cast.type); - break; case EXPR_FN: /* needs to go before decls_cgen.c... */ e->fn->c.id = ++g->ident_counter; @@ -92,7 +90,7 @@ static void cgen_sdecls_expr(CGenerator *g, Expression *e) { default: break; } if (e->kind != EXPR_IDENT) { - cgen_recurse_subexprs(g, e, cgen_sdecls_expr, cgen_sdecls_block, cgen_sdecls_decl); + cgen_recurse_subexprs(g, e, cgen_sdecls_expr, cgen_sdecls_block, cgen_sdecls_decl, cgen_sdecls_type); } if (e->kind == EXPR_NMS) { arr_remove_last(g->nms_prefixes); @@ -190,8 +188,10 @@ static void cgen_decls_type(CGenerator *g, Type *type) { --g->indent_lvl; cgen_write(g, "};"); cgen_nl(g); + cgen_decls_block(g, &sdef->body); sdef->flags |= STRUCT_DEF_CGEN_DEFINED; } + } cgen_recurse_subtypes(cgen_decls_type, g, type); } @@ -294,7 +294,7 @@ static void cgen_fn_decl(CGenerator *g, FnExpr *f, Type *t) { static void cgen_decls_expr(CGenerator *g, Expression *e) { assert(e->flags & EXPR_FOUND_TYPE); - cgen_recurse_subexprs(g, e, cgen_decls_expr, cgen_decls_block, cgen_decls_decl); + cgen_recurse_subexprs(g, e, cgen_decls_expr, cgen_decls_block, cgen_decls_decl, cgen_decls_type); switch (e->kind) { case EXPR_FN: { FnExpr *f = e->fn; @@ -307,9 +307,6 @@ static void cgen_decls_expr(CGenerator *g, Expression *e) { Type *type = e->typeval; cgen_decls_type(g, type); } break; - case EXPR_CAST: - cgen_decls_type(g, &e->cast.type); - break; case EXPR_BINARY_OP: { Type *lhs_type = &e->binary.lhs->type; if (lhs_type->kind == TYPE_PTR) @@ -333,7 +330,7 @@ static void cgen_decls_decl(CGenerator *g, Declaration *d) { cgen_decls_type(g, &d->type); if (cgen_fn_is_direct(g, d)) { cgen_fn_decl(g, d->expr.fn, &d->expr.type); - cgen_recurse_subexprs(g, (&d->expr), cgen_decls_expr, cgen_decls_block, cgen_decls_decl); + cgen_recurse_subexprs(g, (&d->expr), cgen_decls_expr, cgen_decls_block, cgen_decls_decl, cgen_decls_type); } else { if (d->flags & DECL_HAS_EXPR) { cgen_decls_expr(g, &d->expr); @@ -8,9 +8,11 @@ /* @TODO: -initialization functions (maybe #init(-50), where -50 is the priority and <0 is reserved for standard library) +struct_add_block needs serious changes, now that #ifs generate *inline* blocks +get rid of UNARY_LEN? detect circular declarations (A ::= B; B ::= A) either detect circular #includes or set a #include depth limit (maybe sometimes you want finite circular includes with #if) +initialization functions (maybe #init(-50), where -50 is the priority and <0 is reserved for standard library) if we do #include "foo.toc", bar; and foo.toc fails, bar should be declared as TYPE_UNKNOWN (right now it's undeclared) improve type_to_str: Foo ::= struct(t::Type) {} @@ -26,6 +28,7 @@ switch to / add as an alternative: libffi don't bother generating ret_ if nothing's deferred X ::= newtype(int); or something any odd number of "s for a string +give each warning a number; #no_warn(warning), #no_warn_throughout_this_file(warning) use point #except x; optional -Wshadow format errors so that vim/emacs can jump to them @@ -652,9 +652,9 @@ static Status parse_type(Parser *p, Type *type, Location *where) { p->block = prev_block; if (!parse_block(p, &struc->body, PARSE_BLOCK_DONT_CREATE_IDENTS)) return false; + struc->body.kind = BLOCK_STRUCT; parser_put_end(p, &struc->where); break; - struct_fail: p->block = prev_block; return false; @@ -2420,18 +2420,14 @@ static Status parse_stmt(Parser *p, Statement *s, bool *was_a_statement) { } break; case KW_DEFER: { - if (p->block == NULL) { - tokr_err(t, "You can't defer something at global scope."); - return false; - } ++t->token; s->kind = STMT_DEFER; Token *deferred_start = t->token; s->defer = parser_malloc(p, sizeof *s->defer); if (!parse_stmt(p, s->defer, was_a_statement)) - return false; + return false; if (!*was_a_statement) { - err_print(token_location(p->file, deferred_start), "Empty defer"); + err_print(token_location(p->file, deferred_start), "Invalid defer (are you missing a statement?)."); return false; } break; @@ -2505,6 +2501,36 @@ static Status parse_stmt(Parser *p, Statement *s, bool *was_a_statement) { } break; } + case DIRECT_INIT: { + *was_a_statement = false; + Initialization *init = parser_arr_add_ptr(p, p->parsed_file->inits); + ++t->token; + if (!token_is_kw(t->token, KW_LPAREN)) { + tokr_err(t, "Expected ( after #init."); + tokr_skip_semicolon(t); + return false; + } + ++t->token; + if (!parse_expr(p, &init->priority_expr, expr_find_end(p, 0))) { + tokr_skip_semicolon(t); + return false; + } + if (!token_is_kw(t->token, KW_RPAREN)) { + tokr_err(t, "Expected ) after #init priority."); + tokr_skip_semicolon(t); + return false; + } + ++t->token; + Token *stmt_start = t->token; + bool was_stmt; + if (!parse_stmt(p, &init->stmt, &was_stmt)) + return false; + if (!was_stmt) { + err_print(token_location(p->file, stmt_start), "Invalid #init (are you missing a statement?)."); + tokr_skip_semicolon(t); + return false; + } + } break; default: goto stmt_expr; } @@ -2544,6 +2570,7 @@ static void parser_create(Parser *p, Identifiers *globals, Tokenizer *t, Allocat static Status parse_file(Parser *p, ParsedFile *f) { Tokenizer *t = p->tokr; f->stmts = NULL; + f->inits = NULL; p->file = t->file; p->parsed_file = f; bool ret = true; @@ -1,15 +1,16 @@ +#include "tests/std/io.toc"; -#include "std/io.toc", io; -#include "std/base.toc", base; - -sleep ::= #foreign("sleep", base.libc) fn(#C unsigned) #C unsigned; +Foo ::= struct { + x : int; + N ::= 5; + S ::= "hello!"; + f ::= fn() []char { + S + } +} main ::= fn() { - io.io_init(); - io.file_writes(&io.std_out, "hello!\n"); - io.file_writes(&io.std_out, "goodbye!\n"); - io.file_writes(&io.std_err, "ERROR: "); - sleep(1); - io.file_writes(&io.std_err, "something went wrong!\n"); + puts(Foo.f()); + } main(); @@ -787,8 +787,8 @@ top:; if (!(d->flags & DECL_IS_CONST)) { /* check for trying to capture a variable into a function */ bool captured = false; - if (ident_scope(i) != NULL && ident_scope(i)->kind != BLOCK_NMS) { - Block *decl_scope = ident_scope(i); + Block *decl_scope = ident_scope(i); + if (decl_scope && decl_scope->kind != BLOCK_NMS) { if (decl_scope->kind != BLOCK_NMS) { /* go back through scopes */ arr_foreach_reversed(tr->blocks, BlockPtr, block) { @@ -2080,6 +2080,21 @@ static Status types_expr(Typer *tr, Expression *e) { free(s); return false; } + if (b && b->kind == BLOCK_STRUCT) { + /* this is really necessary if you're trying to access a struct constant from inside a function in the same struct */ + e->kind = EXPR_VAL; + Declaration *decl = final_ident->decl; + if (!(decl->flags & DECL_IS_CONST)) { + /* not sure if this can even happen right now, but might as well have this check here */ + err_print(e->where, "Trying to access non-constant struct member from inside of it. This is not allowed."); + return false; + } + assert(decl->flags & DECL_FOUND_VAL); + int idx = decl_ident_index(decl, final_ident); + e->val = *decl_val_at_index(decl, idx); + e->type = *decl_type_at_index(decl, idx); + break; + } e->ident = final_ident; if (!type_of_ident(tr, e->where, e->ident, t)) { return false; @@ -3396,14 +3411,8 @@ static Status types_decl(Typer *tr, Declaration *d) { Type *dtype = &d->type; if (d->flags & DECL_FOUND_TYPE) return true; bool success = true; + Expression *e = (d->flags & DECL_HAS_EXPR) ? &d->expr : NULL; - if ((d->flags & DECL_HAS_EXPR) - && d->expr.kind == EXPR_TYPE - && d->expr.typeval->kind == TYPE_STRUCT - && tr->fn == NULL) { - d->expr.typeval->struc->name = d->idents[0]; - } - if (d->flags & DECL_INFER) { dtype->kind = TYPE_UNKNOWN; dtype->flags = 0; @@ -3420,36 +3429,45 @@ static Status types_decl(Typer *tr, Declaration *d) { size_t n_idents; n_idents = arr_len(d->idents); - if (d->flags & DECL_HAS_EXPR) { - if (!types_expr(tr, &d->expr)) { + if (e) { + if (e->kind == EXPR_FN && tr->block && tr->block->kind == BLOCK_STRUCT) { + warn_print(d->where, "This function is in the body of a struct. Are you trying to declare a method, because they don't exist in this language.\n" + "Try moving the function outside of the struct, otherwise you might run into problems."); + } + if (e->kind == EXPR_TYPE + && e->typeval->kind == TYPE_STRUCT + && tr->fn == NULL) { + e->typeval->struc->name = d->idents[0]; + } + if (!types_expr(tr, e)) { success = false; goto ret; } assert(d->expr.type.flags & TYPE_IS_RESOLVED); if (d->flags & DECL_ANNOTATES_TYPE) { - if (!type_eq_implicit(&d->expr.type, dtype)) { + if (!type_eq_implicit(&e->type, dtype)) { char *decl_type = type_to_str(dtype), - *expr_type = type_to_str(&d->expr.type); - err_print(d->expr.where, "Declaration type %s does not match expression type %s.", decl_type, expr_type); + *expr_type = type_to_str(&e->type); + err_print(e->where, "Declaration type %s does not match expression type %s.", decl_type, expr_type); free(decl_type); free(expr_type); success = false; goto ret; } } else { - if (type_is_void(&d->expr.type)) { + if (type_is_void(&e->type)) { /* e.g. x := (fn(){})(); */ - err_print(d->expr.where, "Use of void value."); + err_print(e->where, "Use of void value."); success = false; goto ret; } - *dtype = d->expr.type; + *dtype = e->type; dtype->flags &= (TypeFlags)~(TypeFlags)TYPE_IS_FLEXIBLE; /* x := 5; => x is not flexible */ } bool need_value = (d->flags & DECL_IS_CONST) || !tr->block || tr->block->kind == BLOCK_NMS; if (need_value) { if (!(d->flags & DECL_FOUND_VAL)) { Value val; - if (!eval_expr(tr->evalr, &d->expr, &val)) { + if (!eval_expr(tr->evalr, e, &val)) { success = false; goto ret; } @@ -3567,7 +3585,7 @@ static Status types_decl(Typer *tr, Declaration *d) { return false; } - if (n_idents == 1 && (d->flags & DECL_HAS_EXPR) && d->expr.kind == EXPR_NMS) { + if (n_idents == 1 && e && e->kind == EXPR_NMS) { bool is_at_top_level = true; arr_foreach(tr->blocks, BlockPtr, b) { if (*b && (*b)->kind != BLOCK_NMS) { @@ -3916,6 +3934,10 @@ static Status types_stmt(Typer *tr, Statement *s) { } } break; case STMT_DEFER: + if (tr->block == NULL) { + err_print(s->where, "You can't defer something at global scope."); + return false; + } if (!types_stmt(tr, s->defer)) return false; if (s->defer->kind == STMT_DEFER) { @@ -249,12 +249,13 @@ typedef enum { DIRECT_WARN, DIRECT_INFO, DIRECT_NO_WARN, + DIRECT_INIT, DIRECT_COUNT } Directive; static const char *directives[DIRECT_COUNT] = { "C", "sizeof", "alignof", "export", "foreign", "builtin", "include", "force", "if", "error", "warn", - "info", "no_warn" + "info", "no_warn", "init" }; typedef enum { @@ -502,14 +503,16 @@ typedef enum { BLOCK_FN, BLOCK_NMS, BLOCK_FOR, - BLOCK_WHILE + BLOCK_WHILE, + BLOCK_STRUCT } BlockKind; typedef U8 BlockFlags; typedef struct Block { /* NOTE: make sure you check copy.c when you add something to this */ BlockFlags flags; - BlockKind kind; + BlockKind kind; /* set during the parsing phase, but don't access while this specific block is being + parsed, because sometimes it's set after parse_block */ struct { IdentID break_lbl, cont_lbl; /* initially 0, set to non-zero values if needed (++g->lbl_counter); set by sdecls_cgen. */ } c; @@ -527,9 +530,10 @@ enum { STRUCT_DEF_FOUND_OFFSETS = 0x01, STRUCT_DEF_CGEN_DECLARED = 0x02, STRUCT_DEF_CGEN_DEFINED = 0x04, - STRUCT_DEF_RESOLVED = 0x08, - STRUCT_DEF_RESOLVING = 0x10, - STRUCT_DEF_RESOLVING_FAILED = 0x20 + STRUCT_DEF_CGEN_FN_DEFS = 0x08, /* have the functions contained in this struct been defined? */ + STRUCT_DEF_RESOLVED = 0x10, + STRUCT_DEF_RESOLVING = 0x20, + STRUCT_DEF_RESOLVING_FAILED = 0x40 }; typedef U8 StructFlags; typedef struct StructDef { @@ -1004,16 +1008,19 @@ typedef struct Statement { typedef Statement *StatementPtr; /* - Statements to be run before any code in main is called. - This is mainly for the standard library, so you don't have to do something weird - like io.init(); - Each initialization has a "priority", with lower priorities being executed first. - Priorities <0 are reserved for the standard library (you can use them if you want, - but standard library functions might not work) + Statements to be run before any code in main is called. + This is mainly for the standard library, so you don't have to do something weird + like io.init(); + Each initialization has a "priority", with lower priorities being executed first. + Priorities <0 are reserved for the standard library (you can use them if you want, + but standard library functions might not work) */ typedef struct { - Statement s; - I64 priority; + Statement stmt; + union { + Expression priority_expr; /* before resolving */ + I64 priority; /* after resolving */ + }; } Initialization; typedef struct ParsedFile { |