diff options
author | Leo Tenenbaum <pommicket@gmail.com> | 2020-07-03 16:09:10 -0400 |
---|---|---|
committer | Leo Tenenbaum <pommicket@gmail.com> | 2020-07-03 16:09:10 -0400 |
commit | 119950ebde3c9f096d1866ba6448d6bd9b22d063 (patch) | |
tree | 35e6bb7a2507910ac784356baf25c2412b6959b6 | |
parent | b25f392b221cea498ea7695f68828909734b51cd (diff) |
removed ability to evaluate a function before it is declared at compile time; that was causing too many problems
-rw-r--r-- | abbrevs.txt | 1 | ||||
-rw-r--r-- | allocator.c | 7 | ||||
-rw-r--r-- | cgen.c | 92 | ||||
-rw-r--r-- | copy.c | 16 | ||||
-rw-r--r-- | development.md | 50 | ||||
-rw-r--r-- | docs/00.html | 2 | ||||
-rw-r--r-- | eval.c | 9 | ||||
-rw-r--r-- | main.c | 6 | ||||
-rw-r--r-- | misc.c | 9 | ||||
-rw-r--r-- | parse.c | 235 | ||||
-rw-r--r-- | test.toc | 6 | ||||
-rw-r--r-- | tests/init.toc | 15 | ||||
-rw-r--r-- | tests/misc.toc | 22 | ||||
-rw-r--r-- | tests/std/base.toc | 5 | ||||
-rw-r--r-- | tests/tests.c | 1 | ||||
-rw-r--r-- | tests/types.toc | 46 | ||||
-rw-r--r-- | types.c | 242 | ||||
-rw-r--r-- | types.h | 30 |
18 files changed, 371 insertions, 423 deletions
diff --git a/abbrevs.txt b/abbrevs.txt index d865020..d486c78 100644 --- a/abbrevs.txt +++ b/abbrevs.txt @@ -17,7 +17,6 @@ eval - evaluate evalr - evaluator expr - expression fn - function -gctx - global context ident - identifier inc - include init - initialization diff --git a/allocator.c b/allocator.c index f3fbff6..98005ef 100644 --- a/allocator.c +++ b/allocator.c @@ -114,10 +114,3 @@ static void allocr_free_all(Allocator *a) { page = next; } } - -static char *allocr_str_to_cstr(Allocator *a, String s) { - char *ret = allocr_malloc(a, s.len + 1); - memcpy(ret, s.str, s.len); - ret[s.len] = 0; - return ret; -} @@ -1628,7 +1628,7 @@ static void cgen_stmt(CGenerator *g, Statement *s) { break; case STMT_EXPR: { Expression *e = s->expr; - if ((g->block != NULL || e->kind == EXPR_C) && !type_is_compileonly(&e->type)) { + if (!type_is_compileonly(&e->type)) { cgen_expr_pre(g, e); cgen_expr(g, e); if (e->kind != EXPR_C) @@ -1882,6 +1882,9 @@ static void cgen_stmt(CGenerator *g, Statement *s) { case STMT_BLOCK: cgen_block(g, s->block, 0); break; + case STMT_INCLUDE: + assert(0); + break; } } @@ -1997,38 +2000,39 @@ static void cgen_file(CGenerator *g, ParsedFile *f, Typer *tr) { g->fn = NULL; g->file = f; - cgen_write(g, "#include <stdint.h>\n" - "#include <stddef.h>\n" - "typedef int8_t i8;\n" - "typedef int16_t i16;\n" - "typedef int32_t i32;\n" - "typedef int64_t i64;\n" - "typedef uint8_t u8;\n" - "typedef uint16_t u16;\n" - "typedef uint32_t u32;\n" - "typedef uint64_t u64;\n" - "typedef float f32;\n" - "typedef double f64;\n" - "typedef u8 bool;\n" - "typedef struct { void *data; i64 len; } slice_;\n" - "#define false ((bool)0)\n" - "#define true ((bool)1)\n" - "#ifdef __linux__\n" /* see also toc.c */ - "#define platform__ " stringify(PLATFORM_LINUX) "\n" - "#elif defined _WIN32\n" - "#define platform__ " stringify(PLATFORM_WINDOWS) "\n" - "#elif defined __APPLE__\n" - "#define platform__ " stringify(PLATFORM_OSX) "\n" - "#elif defined __FreeBSD__\n" - "#define platform__ " stringify(PLATFORM_FREEBSD) "\n" - "#elif defined __OpenBSD__\n" - "#define platform__ " stringify(PLATFORM_OPENBSD) "\n" - "#elif defined __unix__\n" - "#define platform__ " stringify(PLATFORM_MISC_UNIX) "\n" - "#else\n" - "#define platform__ " stringify(PLATFORM_OTHER) "\n" - "#endif\n" - "static slice_ mkslice_(void *data, i64 len) { slice_ ret; ret.data = data; ret.len = len; return ret; }\n"); + cgen_write(g, + "#include <stdint.h>\n" + "#include <stddef.h>\n" + "typedef int8_t i8;\n" + "typedef int16_t i16;\n" + "typedef int32_t i32;\n" + "typedef int64_t i64;\n" + "typedef uint8_t u8;\n" + "typedef uint16_t u16;\n" + "typedef uint32_t u32;\n" + "typedef uint64_t u64;\n" + "typedef float f32;\n" + "typedef double f64;\n" + "typedef u8 bool;\n" + "typedef struct { void *data; i64 len; } slice_;\n" + "#define false ((bool)0)\n" + "#define true ((bool)1)\n" + "#ifdef __linux__\n" /* see also toc.c */ + "#define platform__ " stringify(PLATFORM_LINUX) "\n" + "#elif defined _WIN32\n" + "#define platform__ " stringify(PLATFORM_WINDOWS) "\n" + "#elif defined __APPLE__\n" + "#define platform__ " stringify(PLATFORM_OSX) "\n" + "#elif defined __FreeBSD__\n" + "#define platform__ " stringify(PLATFORM_FREEBSD) "\n" + "#elif defined __OpenBSD__\n" + "#define platform__ " stringify(PLATFORM_OPENBSD) "\n" + "#elif defined __unix__\n" + "#define platform__ " stringify(PLATFORM_MISC_UNIX) "\n" + "#else\n" + "#define platform__ " stringify(PLATFORM_OTHER) "\n" + "#endif\n" + "static slice_ mkslice_(void *data, i64 len) { slice_ ret; ret.data = data; ret.len = len; return ret; }\n"); cgen_write(g, "/* types */\n"); /* struct declarations */ arr_foreach(tr->all_structs, StructDefPtr, sdefp) { @@ -2083,20 +2087,6 @@ static void cgen_file(CGenerator *g, ParsedFile *f, Typer *tr) { } cgen_write(g, "/* code */\n"); - cgen_write(g, "int main(void) {\n"); - g->indent_lvl = 1; - arr_foreach(tr->gctx->inits, Initialization, init) { - Statement *s = &init->stmt; - if (s->kind == STMT_EXPR) { /* these wouldn't be generated otherwise */ - cgen_expr(g, s->expr); - cgen_writeln(g, ";"); - } else { - cgen_stmt(g, s); - } - } - cgen_nl(g); - cgen_write(g, "main_();\n\treturn 0;\n}\n\n"); - g->indent_lvl = 0; /* function definitions */ arr_foreach(tr->all_fns, FnWithCtx, fn_ctx) { g->nms = fn_ctx->nms; @@ -2106,7 +2096,13 @@ static void cgen_file(CGenerator *g, ParsedFile *f, Typer *tr) { g->nms = NULL; g->block = NULL; - arr_foreach(f->stmts, Statement, s) { + cgen_write(g, "int main(void) {\n"); + g->indent_lvl = 1; + arr_foreach(tr->gctx->inits, Initialization, init) { + Statement *s = init->stmt; cgen_stmt(g, s); } + cgen_nl(g); + cgen_write(g, "main_();\n\treturn 0;\n}\n\n"); + g->indent_lvl = 0; } @@ -348,6 +348,11 @@ static void copy_stmt(Copier *c, Statement *out, Statement *in) { if (in->ret->flags & RET_HAS_EXPR) copy_expr(c, &out->ret->expr, &in->ret->expr); break; + case STMT_INCLUDE: + out->inc = copier_malloc(c, sizeof *out->inc); + *out->inc = *in->inc; + copy_expr(c, &out->inc->filename, &in->inc->filename); + break; case STMT_EXPR: out->expr = copy_expr_(c, in->expr); break; @@ -417,14 +422,9 @@ static void copy_stmt(Copier *c, Statement *out, Statement *in) { case STMT_BLOCK: copy_block(c, out->block = copier_malloc(c, sizeof *out->block), in->block, 0); break; - case STMT_INLINE_BLOCK: { - size_t nstmts = arr_len(in->inline_block); - out->inline_block = NULL; - arr_set_lena(out->inline_block, nstmts, c->allocr); - for (size_t i = 0; i < nstmts; ++i) { - copy_stmt(c, &out->inline_block[i], &in->inline_block[i]); - } - } break; + case STMT_INLINE_BLOCK: + assert(0); /* only exists after typing */ + break; } } diff --git a/development.md b/development.md index 9f0980c..6795987 100644 --- a/development.md +++ b/development.md @@ -26,7 +26,7 @@ Sometimes a type ending in Ptr is defined, e.g. typedef Declaration \*Declaratio for the arr\_foreach macro, and not meant for normal use. The fixed-width types U8/16/32/64 and I8/16/32/64 have been defined. -data\_structures.c contains a dynamic array implementation and string hash table which are very useful. +data\_structures.c contains a dynamic array implementation which is very useful. ### Notes @@ -37,51 +37,3 @@ declaration). It is assumed that the number of identifiers in a declaration, or parameters to a function will fit in an int, since a function with (at least) 32768 parameters is ridiculous. -### Miscellaneous thoughts - -#### includes during parsing -Wouldn't it be nice if `#include` could be done during typing, so that the filename wouldn't have to be a -string literal? Well, unfortunately we end up in situations like this: -``` -asdf ::= fn() { - x ::= bar(); - a: [x]int; - // ... -} -#include "foo.toc"; // foo defined here -bar ::= fn() int { - return foo(17); -} -``` -The way we evaluate `bar()` is by typing the function `bar` when we come across it in the declaration `x ::= bar();`. -In order to evaluate `foo`, we need to know what it refers to, and we don't know at this point that it comes from the -include, because we haven't expanded it yet. We could potentially expand all includes preceding any function which -is evaluated during typing, but that seems a bit cumbersome and would probably cause other problems. - - -#### Why `#if` is complicated -Allowing arbitrary `#if` conditions leads to the following problem: -``` -bar1 ::= fn() bool { ... }; -bar2 ::= fn() bool { ... }; -foo1 ::= fn() bool { ... }; -foo2 ::= fn() bool { ... }; - -#if foo() { - bar ::= bar1; -} else { - bar ::= bar2; -} - -#if bar() { - foo ::= foo1; -} else { - foo ::= foo2; -} -``` - -In order to determine which version of `bar` to use, we need to call `foo`, and in order to determine which version of `foo` -to use, we need to call `bar`. You could just error on circular dependencies like these, but there is still the fact that you -have to figure out where the declaration for `foo` is when `foo()` is evaluated, and it could be in some other `#if`. To -avoid things getting complicated, we restrict `#if`s to make this situation impossible. While it's not ideal, it is necessary -to avoid some edge cases where we can't find out which declaration you're referring to. diff --git a/docs/00.html b/docs/00.html index b32be13..7609f7b 100644 --- a/docs/00.html +++ b/docs/00.html @@ -66,6 +66,6 @@ x ::= 5+3; y :: float = 5.123; </c></p> -<p>Here, "constant" means constant at compile time, not read-only as it does in C. One interesting thing about toc is that normal functions can run at compile time, so pretty much any expression is a valid initializer for a constant, e.g. doing <c>x ::= some_function();</c> runs <c>some_function</c> at compile time, not at run time.</p> +<p>Here, "constant" means constant at compile time, not read-only as it does in C.</p> </body> </html> @@ -1023,7 +1023,9 @@ static Status eval_ident(Evaluator *ev, Identifier ident, Value *v, Location whe v->type->kind = TYPE_STRUCT; v->type->struc = d->expr.typeval->struc; return true; + } else if (d->flags & DECL_FOUND_TYPE) { } else { + #if 0 Typer *tr = ev->typer; Block *prev_block = tr->block; /* make sure we're in the right block for typing the declaration */ @@ -1032,6 +1034,10 @@ static Status eval_ident(Evaluator *ev, Identifier ident, Value *v, Location whe tr->block = prev_block; if (!success) return false; assert(d->type.flags & TYPE_IS_RESOLVED); + #else + err_print(where, "Use of identifier at compile time before it is declared. This isn't allowed. Sorry."); + return false; + #endif } Value *ival = ident_val(ev, ident, where); if (!ival) return false; @@ -1691,6 +1697,9 @@ static Status eval_stmt(Evaluator *ev, Statement *stmt) { case STMT_BLOCK: if (!eval_block(ev, stmt->block)) return false; break; + case STMT_INCLUDE: + assert(0); + break; } return true; } @@ -41,12 +41,8 @@ once you have a bunch of test code: - try making Value.slice a pointer - should val_stack be on the allocator? what about temporary arrays? -->on the contrary, should in_decls be off the allocator? -struct param inference maybe macros are just inline functions passing untyped expressions to macros -#returns_code (struct body is a block, to be evaluated at compile time, which returns the actual statements) - - struct varargs - - also use with functions for macros? */ #if defined __unix__ || (defined __APPLE__ && defined __MACH__) @@ -213,10 +209,12 @@ int main(int argc, char **argv) { } if (verbose) printf("Parsing...\n"); + GlobalCtx global_ctx = {0}; str_hash_table_create(&global_ctx.included_files, sizeof(IncludedFile), &main_allocr); global_ctx.main_file = &file; global_ctx.err_ctx = &err_ctx; + Parser p; parser_create(&p, &globals, &t, &main_allocr, &global_ctx); ParsedFile f; @@ -66,11 +66,6 @@ static inline char *str_to_cstr(String s) { return cstr(s.str, s.len); } -static void print_str(String s) { - fwrite(s.str, 1, s.len, stdout); - printf("\n"); -} - static inline bool str_eq_cstr(String s, const char *str) { return strncmp(s.str, str, s.len) == 0; } @@ -109,4 +104,8 @@ static U32 *bsearch_u32(U32 *data, size_t count, U32 search) { return NULL; } +static void print_str(String s) { + fwrite(s.str, 1, s.len, stdout); + printf("\n"); +} @@ -3,8 +3,6 @@ This file is part of toc. toc is distributed under version 3 of the GNU General Public License, without any warranty whatsoever. You should have received a copy of the GNU General Public License along with toc. If not, see <https://www.gnu.org/licenses/>. */ -static void parser_create(Parser *p, Identifiers *globals, Tokenizer *t, Allocator *allocr, GlobalCtx *gctx); -static Status parse_file(Parser *p, ParsedFile *f); static Status parse_expr(Parser *p, Expression *e, Token *end); static Status parse_stmt(Parser *p, Statement *s, bool *was_a_statement); enum { @@ -340,6 +338,7 @@ static inline void parser_put_end(Parser *p, Location *l) { parser_set_end_to_token(p, l, p->tokr->token); } + static inline Identifiers *parser_get_idents(Parser *p) { return p->block == NULL ? p->globals : &p->block->idents; } @@ -355,7 +354,6 @@ static inline bool parser_is_at_top_level(Parser *p) { return block_is_at_top_level(p->block); } - #define parser_arr_add_ptr(p, a) arr_adda_ptr(a, p->allocr) #define parser_arr_add(p, a, x) arr_adda(a, x, p->allocr) #define parser_arr_set_len(p, a, l) arr_set_lena(a, l, p->allocr) @@ -1358,13 +1356,8 @@ static Status parse_expr(Parser *p, Expression *e, Token *end) { Namespace *n = e->nms = parser_calloc(p, 1, sizeof *n); e->kind = EXPR_NMS; ++t->token; - Namespace *prev = p->nms; - p->nms = n; - if (!parse_block(p, &n->body, 0)) { - p->nms = prev; + if (!parse_block(p, &n->body, 0)) return false; - } - p->nms = prev; n->body.kind = BLOCK_NMS; goto success; } @@ -2218,33 +2211,6 @@ static bool is_decl(Tokenizer *t) { } } -/* introduce identifiers from stmts into current scope, setting their "nms" field to nms */ -static Status include_stmts_link_to_nms(Parser *p, Namespace *nms, Statement *stmts) { - Identifiers *idents = parser_get_idents(p); - arr_foreach(stmts, Statement, s) { - if (s->kind == STMT_INLINE_BLOCK) { - if (!include_stmts_link_to_nms(p, nms, s->inline_block)) - return false; - } else if (s->kind == STMT_DECL) { - Declaration *d = s->decl; - arr_foreach(d->idents, Identifier, ident) { - /* @OPTIM: only hash once */ - Identifier preexisting = ident_translate(*ident, idents); - if (preexisting && preexisting->decl != d) { - char *istr = ident_to_str(preexisting); - err_print(d->where, "Redeclaration of identifier %s.", istr); - info_print(preexisting->decl->where, "%s was first declared here.", istr); - free(istr); - } - Identifier i = ident_translate_forced(*ident, idents); - i->nms = nms; - i->decl = d; - } - } - } - return true; -} - /* sets *was_a_statement to false if s was not filled, but the token was advanced */ static Status parse_stmt(Parser *p, Statement *s, bool *was_a_statement) { Tokenizer *t = p->tokr; @@ -2341,8 +2307,6 @@ static Status parse_stmt(Parser *p, Statement *s, bool *was_a_statement) { i->flags = 0; if (t->token->kind == TOKEN_DIRECT) { i->flags |= IF_STATIC; - StatementWithCtx sctx = {s, p->block, p->nms}; - parser_arr_add(p, p->gctx->static_ifs, sctx); } s->kind = STMT_IF; ++t->token; @@ -2504,28 +2468,28 @@ static Status parse_stmt(Parser *p, Statement *s, bool *was_a_statement) { } else if (t->token->kind == TOKEN_DIRECT) { switch (t->token->direct) { case DIRECT_INCLUDE: { + Include *i = s->inc = parser_malloc(p, sizeof *i); ++t->token; - s->kind = STMT_INLINE_BLOCK; - bool forced = false; + s->kind = STMT_INCLUDE; + i->flags = 0; if (token_is_direct(t->token, DIRECT_FORCE)) { - forced = true; + i->flags |= INC_FORCED; ++t->token; } - if (t->token->kind != TOKEN_LITERAL_STR) { - tokr_err(t, "#include file name must be string literal."); + if (!parse_expr(p, &i->filename, expr_find_end(p, EXPR_CAN_END_WITH_COMMA))) { + tokr_skip_semicolon(t); return false; } - char *filename = allocr_str_to_cstr(p->allocr, t->token->str); - ++t->token; - Identifier nms_ident = NULL; /* identifier of namespace to include to */ if (token_is_kw(t->token, KW_COMMA)) { ++t->token; if (t->token->kind != TOKEN_IDENT) { tokr_err(t, "Expected identifier after , in #include (to specify include namespace)."); return false; } - nms_ident = parser_ident_insert(p, t->token->ident); + i->nms = t->token->ident; ++t->token; + } else { + i->nms = NULL; } if (!token_is_kw(t->token, KW_SEMICOLON)) { tokr_err(t, "Expected ; after #include directive"); @@ -2533,131 +2497,6 @@ static Status parse_stmt(Parser *p, Statement *s, bool *was_a_statement) { return false; } ++t->token; - - Block *prev_block = p->block; - Namespace *prev_nms = p->nms; - IncludedFile *inc_f = NULL; - Namespace *inc_nms = NULL; /* non-NULL if this is an include to nms */ - bool success = true; - if (nms_ident) { - inc_nms = parser_calloc(p, 1, sizeof *inc_nms); - Block *body = &inc_nms->body; - body->kind = BLOCK_NMS; - body->where = s->where; - idents_create(&body->idents, p->allocr, body); - body->parent = prev_block; - /* turn #include "foo", bar into bar ::= nms { ... } */ - s->kind = STMT_DECL; - Declaration *d = s->decl = parser_calloc(p, 1, sizeof *d); - d->flags = DECL_HAS_EXPR | DECL_IS_CONST; - d->type.kind = TYPE_BUILTIN; - d->type.builtin = BUILTIN_NMS; - Identifier i = nms_ident; - if (i->decl) { - Declaration *d2 = i->decl; - /* maybe they included it twice into one namespace */ - if ((d2->flags & DECL_HAS_EXPR) && (d2->expr.kind == EXPR_NMS) && - (d2->expr.nms->inc_file == inc_f)) { - /* that's okay; get rid of this declaration */ - s->kind = STMT_INLINE_BLOCK; - s->inline_block = NULL; - break; - } else { - char *istr = ident_to_str(i); - err_print(s->where, "Redeclaration of identifier %s.", istr); - info_print(ident_decl_location(i), "Previous declaration was here."); - free(istr); - return false; /* NOT goto inc_fail; */ - } - } - parser_arr_add(p, d->idents, i); - i->decl = d; - d->expr.kind = EXPR_NMS; - d->expr.nms = inc_nms; - d->expr.type = d->type; - parser_put_end(p, &s->where); - d->where = d->expr.where = s->where; - /* we need to be in the block to parse it properly */ - p->block = &inc_nms->body; - p->nms = inc_nms; - } else { - s->kind = STMT_INLINE_BLOCK; - } - - if (!forced) { - size_t filename_len = strlen(filename); - if (streq(filename, p->gctx->main_file->filename)) { - err_print(s->where, "Circular #include detected. You can add #force to this #include to force it to be included."); - success = false; goto nms_done; - } - StrHashTable *included_files = &p->gctx->included_files; - inc_f = str_hash_table_get(included_files, filename, filename_len); - if (inc_f) { - /* has already been included */ - if (inc_f->flags & INC_FILE_INCLUDING) { - err_print(s->where, "Circular #include detected. You can add #force to this #include to force it to be included."); - success = false; goto nms_done; - } - if (s->kind == STMT_INLINE_BLOCK) s->inline_block = NULL; /* nothing needed here */ - /* just set ident declarations */ - if (!include_stmts_link_to_nms(p, inc_f->main_nms, inc_f->stmts)) { - success = false; goto nms_done; - } - goto nms_done; - } else { - inc_f = str_hash_table_insert(included_files, filename, filename_len); - inc_f->flags |= INC_FILE_INCLUDING; - inc_f->main_nms = p->nms; - } - } - { - char *contents = read_file_contents(p->allocr, filename, s->where); - if (!contents) { - success = false; goto nms_done; - } - - Tokenizer tokr; - tokr_create(&tokr, p->file->ctx, p->allocr); - File *file = parser_calloc(p, 1, sizeof *file); - file->filename = filename; - file->contents = contents; - file->ctx = p->file->ctx; - - if (!tokenize_file(&tokr, file)) { - success = false; goto nms_done; - } - - #if 0 - arr_foreach(tokr.tokens, Token, token) { - fprint_token(stdout, token); - printf(" "); - } - #endif - - Parser parser; - parser_create(&parser, p->globals, &tokr, p->allocr, p->gctx); - parser.block = p->block; - parser.nms = p->nms; - ParsedFile parsed_file; - if (!parse_file(&parser, &parsed_file)) { - success = false; goto nms_done; - } - Statement *stmts_inc = parsed_file.stmts; - if (inc_f) { - inc_f->stmts = stmts_inc; - } - if (s->kind == STMT_INLINE_BLOCK) s->inline_block = stmts_inc; - if (inc_nms) { - inc_nms->body.stmts = stmts_inc; - } - } - nms_done: - if (inc_nms) { - p->nms = prev_nms; - p->block = prev_block; - } - if (inc_f) inc_f->flags &= (IncFileFlags)~(IncFileFlags)INC_FILE_INCLUDING; - if (!success) return false; } break; case DIRECT_IF: goto if_stmt; @@ -2685,55 +2524,29 @@ static Status parse_stmt(Parser *p, Statement *s, bool *was_a_statement) { tokr_skip_semicolon(t); return false; } - } break; + break; + } case DIRECT_INIT: { if (!parser_is_at_top_level(p)) { tokr_err(t, "#init directives can't be inside a block."); tokr_skip_semicolon(t); return false; } - *was_a_statement = false; - Initialization *init = parser_arr_add_ptr(p, p->gctx->inits); ++t->token; - if (!token_is_kw(t->token, KW_LPAREN)) { - tokr_err(t, "Expected ( after #init."); + if (token_is_direct(t->token, DIRECT_INIT)) { + tokr_err(t, "You can't do #init #init."); tokr_skip_semicolon(t); return false; } - ++t->token; - bool negative = false; /* negative priority? */ - if (token_is_kw(t->token, KW_MINUS)) { - negative = true; - ++t->token; - } else if (token_is_kw(t->token, KW_PLUS)) { - /* ignore unary + in priority */ - ++t->token; - } - if (t->token->kind != TOKEN_LITERAL_INT) { - tokr_err(t, "Priority for #init must be an integer literal (like 50)."); - tokr_skip_semicolon(t); - return false; - } - U64 priority = t->token->intl; - ++t->token; - if (priority > I64_MAX) { - tokr_err(t, "Priority must be less than 9223372036854775808."); - tokr_skip_semicolon(t); - return false; - } - I64 signed_priority = (I64)priority; - if (negative) signed_priority = -signed_priority; - init->priority = signed_priority; - 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)) + if (!parse_stmt(p, s, &was_stmt)) return false; + if (s->kind == STMT_DECL || s->kind == STMT_INCLUDE || s->kind == STMT_USE || s->kind == STMT_MESSAGE) { + err_print(s->where, "Invalid use of #init."); + return false; + } + s->flags |= STMT_IS_INIT; if (!was_stmt) { err_print(token_location(p->file, stmt_start), "Invalid #init (are you missing a statement?)."); tokr_skip_semicolon(t); @@ -3052,6 +2865,12 @@ static void fprint_stmt(FILE *out, Statement *s) { fprint_expr(out, &r->expr); fprintf(out, ";\n"); } break; + case STMT_INCLUDE: { + Include *i = s->inc; + fprintf(out, "#include "); + fprint_expr(out, &i->filename); + fprintf(out, ";\n"); + } break; case STMT_MESSAGE: { Message *m = s->message; switch (m->kind) { @@ -1,5 +1,5 @@ -#include "tests/std/io.toc", io; +f(); -main ::= fn() { - io.puts("hi"); +f ::= fn( ) int { + return 3; } diff --git a/tests/init.toc b/tests/init.toc index ac85058..92fb9ed 100644 --- a/tests/init.toc +++ b/tests/init.toc @@ -1,11 +1,3 @@ -#init(-10) init(10); -#init(44) init(44); -#init(22) init(22); -#init(88) init(88); -#init(1002389) for i := 89..100 {init(i);} -#init(-20) init(0); - - #include "std/io.toc"; total : int; @@ -23,3 +15,10 @@ main ::= fn() { writes("hello from main. total is "); puti(total); } + +#init init(0); +#init init(10); +#init init(22); +#init init(44); +#init init(88); +#init for i := 89..100 {init(i);} diff --git a/tests/misc.toc b/tests/misc.toc index 04932e8..a6a7da5 100644 --- a/tests/misc.toc +++ b/tests/misc.toc @@ -1,17 +1,6 @@ #include "std/io.toc", io; main ::= fn() { - x ::= 3; - #if x > 2 { - io.puts("Hello!"); - } else { - foo("yes"); - } - - io.puti(arr_sum(mk_arr(5,89,22))); - io.puti(arr_sum(mk_arr(1,2,3))); - io.puti(arr_sum(mk_arr(z=0, 0, 0))); - arr_sum ::= fn(n::=, t::=, a:[n]t) t { total := 0 as t; for x := a { @@ -25,6 +14,17 @@ main ::= fn() { a[1] = y; a[2] = z; }; + x ::= 3; + #if x > 2 { + io.puts("Hello!"); + } else { + foo("yes"); + } + + io.puti(arr_sum(mk_arr(5,89,22))); + io.puti(arr_sum(mk_arr(1,2,3))); + io.puti(arr_sum(mk_arr(z=0, 0, 0))); + s ::= struct { diff --git a/tests/std/base.toc b/tests/std/base.toc index 54d64d6..0bd707e 100644 --- a/tests/std/base.toc +++ b/tests/std/base.toc @@ -7,6 +7,8 @@ PLATFORM_OPENBSD ::= 5; PLATFORM_MISC_UNIX ::= 6; PLATFORM ::= #builtin("platform"); +PLATFORM_IS_UNIX :: bool = PLATFORM == PLATFORM_LINUX || PLATFORM == PLATFORM_OSX || PLATFORM == PLATFORM_FREEBSD + || PLATFORM == PLATFORM_OPENBSD || PLATFORM == PLATFORM_MISC_UNIX; #if PLATFORM == PLATFORM_LINUX { libc ::= "libc.so.6"; } elif PLATFORM == PLATFORM_WINDOWS { @@ -20,7 +22,4 @@ PLATFORM ::= #builtin("platform"); libc ::= "libc.so.6"; } -PLATFORM_IS_UNIX :: bool = PLATFORM == PLATFORM_LINUX || PLATFORM == PLATFORM_OSX || PLATFORM == PLATFORM_FREEBSD - || PLATFORM == PLATFORM_OPENBSD || PLATFORM == PLATFORM_MISC_UNIX; - diff --git a/tests/tests.c b/tests/tests.c index 57f611e..3e49843 100644 --- a/tests/tests.c +++ b/tests/tests.c @@ -10,6 +10,7 @@ static const char *tests[] = { "bf", "control_flow", "types", + "init", "include", "arrs_slices", "ptr_arithmetic", diff --git a/tests/types.toc b/tests/types.toc index 25c38d5..726cc85 100644 --- a/tests/types.toc +++ b/tests/types.toc @@ -19,6 +19,29 @@ z ::= nms { } } +slice_to_ll ::= fn(t::=, slice: []t) use ll: LinkedList(t) { + head = slice[0]; + if slice.len == 1 { + tail = null; + } else { + tail = new(LinkedList(t)); + *tail = slice_to_ll(slice[1:]); + } +} + +LinkedList ::= struct (of :: Type) { + head: of; + tail: &LinkedList(of); +} + +Foo ::= struct { + k: int; + b: &Bar; +} + +Bar ::= struct { + f: Foo; +} main ::= fn() { nums := news(int, 10); @@ -45,26 +68,3 @@ main ::= fn() { io.puti(y); } -slice_to_ll ::= fn(t::=, slice: []t) use ll: LinkedList(t) { - head = slice[0]; - if slice.len == 1 { - tail = null; - } else { - tail = new(LinkedList(t)); - *tail = slice_to_ll(slice[1:]); - } -} - -LinkedList ::= struct (of :: Type) { - head: of; - tail: &LinkedList(of); -} - -Foo ::= struct { - k: int; - b: &Bar; -} - -Bar ::= struct { - f: Foo; -} @@ -585,6 +585,7 @@ enum { }; static Status type_of_fn(Typer *tr, FnExpr *f, Type *t, U16 flags) { + if (f->flags & FN_EXPR_FOREIGN) { /* we've already mostly determined the type in parse_expr */ if (!type_resolve(tr, &f->foreign.type, f->where)) @@ -2969,6 +2970,10 @@ static Status types_block(Typer *tr, Block *b) { arr_foreach(b->stmts, Statement, s) { if (!types_stmt(tr, s)) { success = false; + if (tr->had_include_err) { + /* stop immediately; prevent too many "undeclared identifier" errors */ + break; + } continue; } } @@ -3031,6 +3036,7 @@ static Status types_decl(Typer *tr, Declaration *d) { && tr->fn == NULL) { e->typeval->struc->name = d->idents[0]; } + if (!types_expr(tr, e)) { success = false; goto ret; @@ -3227,6 +3233,33 @@ static Status fix_ident_decls_inline_block(Typer *tr, Statement *stmts) { return true; } +/* introduce identifiers from stmts into current scope, setting their "nms" field to nms */ +static Status include_stmts_link_to_nms(Typer *tr, Namespace *nms, Statement *stmts) { + Identifiers *idents = typer_get_idents(tr); + arr_foreach(stmts, Statement, s) { + if (s->kind == STMT_INLINE_BLOCK) { + if (!include_stmts_link_to_nms(tr, nms, s->inline_block)) + return false; + } else if (s->kind == STMT_DECL) { + Declaration *d = s->decl; + arr_foreach(d->idents, Identifier, ident) { + /* @OPTIM: only hash once */ + Identifier preexisting = ident_translate(*ident, idents); + if (preexisting && preexisting->decl != d) { + char *istr = ident_to_str(preexisting); + err_print(d->where, "Redeclaration of identifier %s.", istr); + info_print(preexisting->decl->where, "%s was first declared here.", istr); + free(istr); + } + Identifier i = ident_translate_forced(*ident, idents); + i->nms = nms; + i->decl = d; + } + } + } + return true; +} + static Status types_stmt(Typer *tr, Statement *s) { top: if (s->flags & STMT_TYPED) return true; @@ -3249,14 +3282,8 @@ top: free(str); } } - if (tr->block == NULL) { - /* evaluate expression statements at global scope */ - if (e->kind != EXPR_C) { - if (!eval_stmt(tr->evalr, s)) - return false; - } - } } break; + case STMT_FOR: { bool in_header = true; Block *prev_block = tr->block; { /* additional block because c++ */ @@ -3711,6 +3738,139 @@ top: } } } break; + case STMT_INCLUDE: { + Include *inc = s->inc; + char *filename = eval_expr_as_cstr(tr, &inc->filename, "import filename"); + if (!filename) + return false; + Namespace *prev_nms = tr->nms; + Block *prev_block = tr->block; + IncludedFile *inc_f = NULL; + Namespace *inc_nms = NULL; /* non-NULL if this is an include to nms */ + bool success = true; + if (inc->nms) { + inc_nms = typer_calloc(tr, 1, sizeof *inc_nms); + + Block *body = &inc_nms->body; + body->kind = BLOCK_NMS; + body->where = s->where; + idents_create(&body->idents, tr->allocr, body); + body->parent = tr->block; + + inc_nms->inc_file = inc_f; + /* turn #include "foo", bar into bar ::= nms { ... } */ + s->kind = STMT_DECL; + Declaration *d = s->decl = typer_calloc(tr, 1, sizeof *d); + d->flags = DECL_FOUND_TYPE | DECL_HAS_EXPR | DECL_IS_CONST | DECL_FOUND_VAL; + construct_resolved_builtin_type(&d->type, BUILTIN_NMS); + char *ident_str = inc->nms; + Identifier i = ident_insert(typer_get_idents(tr), &ident_str); + if (i->decl) { + Declaration *d2 = i->decl; + /* maybe they included it twice into one namespace */ + if ((d2->flags & DECL_HAS_EXPR) && (d2->expr.kind == EXPR_NMS) && + (d2->expr.nms->inc_file == inc_f)) { + /* that's okay; get rid of this declaration */ + s->kind = STMT_INLINE_BLOCK; + s->inline_block = NULL; + break; + } else { + char *istr = ident_to_str(i); + err_print(s->where, "Redeclaration of identifier %s.", istr); + info_print(ident_decl_location(i), "Previous declaration was here."); + free(istr); + return false; /* NOT goto inc_fail; */ + } + } + typer_arr_add(tr, d->idents, i); + i->decl = d; + if (typer_is_at_top_level(tr)) inc_nms->associated_ident = i; + typer_gen_nms_prefix(tr, inc_nms); + + d->expr.kind = EXPR_NMS; + d->expr.nms = inc_nms; + d->expr.flags = EXPR_FOUND_TYPE; + d->expr.type = d->type; + d->val.nms = inc_nms; + d->where = d->expr.where = s->where; + + /* go inside namespace and block (it'll help to be there later on) */ + tr->nms = inc_nms; + typer_block_enter(tr, &inc_nms->body); + } else { + s->kind = STMT_INLINE_BLOCK; + } + + if (!(inc->flags & INC_FORCED)) { + size_t filename_len = strlen(filename); + if (streq(filename, tr->gctx->main_file->filename)) { + err_print(s->where, "Circular #include detected. You can add #force to this #include to force it to be included."); + success = false; goto nms_done; + } + inc_f = str_hash_table_get(&tr->included_files, filename, filename_len); + if (inc_f) { + /* has already been included */ + if (inc_f->flags & INC_FILE_INCLUDING) { + err_print(s->where, "Circular #include detected. You can add #force to this #include to force it to be included."); + success = false; goto nms_done; + } + if (s->kind == STMT_INLINE_BLOCK) s->inline_block = NULL; /* nothing needed here */ + /* just set ident declarations */ + if (!include_stmts_link_to_nms(tr, inc_f->main_nms, inc_f->stmts)) { + success = false; goto nms_done; + } + goto nms_done; + } + inc_f = str_hash_table_insert(&tr->included_files, filename, filename_len); + inc_f->flags |= INC_FILE_INCLUDING; + inc_f->main_nms = tr->nms; + } + { + char *contents = read_file_contents(tr->allocr, filename, s->where); + if (!contents) { + tr->had_include_err = true; + success = false; goto nms_done; + } + + Tokenizer tokr; + tokr_create(&tokr, tr->gctx->err_ctx, tr->allocr); + File *file = typer_calloc(tr, 1, sizeof *file); + file->filename = filename; + file->contents = contents; + file->ctx = tr->gctx->err_ctx; + + if (!tokenize_file(&tokr, file)) { + success = false; goto nms_done; + } + Parser parser; + parser_create(&parser, tr->globals, &tokr, tr->allocr, tr->gctx); + parser.block = tr->block; + ParsedFile parsed_file; + if (!parse_file(&parser, &parsed_file)) { + success = false; goto nms_done; + } + Statement *stmts_inc = parsed_file.stmts; + if (inc_f) { + inc_f->stmts = stmts_inc; + } + if (s->kind == STMT_INLINE_BLOCK) s->inline_block = stmts_inc; + arr_foreach(stmts_inc, Statement, s_incd) { + if (!types_stmt(tr, s_incd)) { + success = false; goto nms_done; + } + } + if (inc_nms) { + inc_nms->body.stmts = stmts_inc; + } + } + nms_done: + if (inc_nms) { + tr->nms = prev_nms; + tr->block = prev_block; + } + if (inc_f) inc_f->flags &= (IncFileFlags)~(IncFileFlags)INC_FILE_INCLUDING; + if (!success) return false; + } break; case STMT_MESSAGE: { Message *m = s->message; char *text = eval_expr_as_cstr(tr, &m->text, "message"); @@ -3782,51 +3942,65 @@ top: else typer_arr_add(tr, tr->uses, u); } break; - case STMT_INLINE_BLOCK: { - bool success = true; - arr_foreach(s->inline_block, Statement, sub) { - if (!types_stmt(tr, sub)) - success = false; - } - if (!success) return false; - } break; + case STMT_INLINE_BLOCK: + assert(0); /* only exists after typing */ + break; } success: s->flags |= STMT_TYPED; + if (tr->block == NULL) { + /* evaluate statements at global scope */ + switch (s->kind) { + case STMT_DECL: + case STMT_USE: + case STMT_INLINE_BLOCK: /* have already been evaluated */ + case STMT_MESSAGE: + break; + case STMT_EXPR: + case STMT_IF: + case STMT_FOR: + case STMT_WHILE: + case STMT_BLOCK: + if (s->expr->kind != EXPR_C) { + if (!eval_stmt(tr->evalr, s)) + return false; + } + break; + case STMT_RET: + case STMT_BREAK: + case STMT_CONT: + case STMT_INCLUDE: + case STMT_DEFER: + assert(0); + break; + } + } + if (s->flags & STMT_IS_INIT) { + Initialization *init = typer_arr_add_ptr(tr, tr->gctx->inits); + init->stmt = s; + } return true; } -static void typer_create(Typer *tr, Evaluator *ev, Allocator *allocr, Identifiers *idents, GlobalCtx *gctx) { +static void typer_create(Typer *tr, Evaluator *ev, Allocator *allocr, Identifiers *globals, GlobalCtx *gctx) { memset(tr, 0, sizeof *tr); tr->evalr = ev; tr->allocr = allocr; - tr->globals = idents; + tr->globals = globals; tr->gctx = gctx; -} - -static int compare_inits(const void *av, const void *bv) { - const Initialization *a = av, *b = bv; - if (a->priority < b->priority) return -1; - if (a->priority > b->priority) return +1; - return 0; + str_hash_table_create(&tr->included_files, sizeof(IncludedFile), tr->allocr); } static Status types_file(Typer *tr, ParsedFile *f) { bool ret = true; tr->parsed_file = f; tr->uses = NULL; - Initialization *inits = tr->gctx->inits; - qsort(inits, arr_len(inits), sizeof *inits, compare_inits); - arr_foreach(inits, Initialization, init) { - Statement *s = &init->stmt; - if (!types_stmt(tr, s)) - return false; - if (s->kind != STMT_EXPR) /* already evaluated in types_stmt */ - if (!eval_stmt(tr->evalr, s)) - return false; - } arr_foreach(f->stmts, Statement, s) { if (!types_stmt(tr, s)) { + if (tr->had_include_err) { + /* stop immediately; prevent too many "undeclared identifier" errors */ + return false; + } ret = false; } } @@ -912,6 +912,12 @@ enum { INC_TO_NMS = 0x02 }; +typedef struct { + U8 flags; + Expression filename; + char *nms; /* NULL if this is just a plain old #include, otherwise string which can be used with ident_get */ +} Include; + typedef enum { MESSAGE_ERROR, MESSAGE_WARN, @@ -934,6 +940,7 @@ typedef enum { STMT_RET, STMT_BREAK, STMT_CONT, + STMT_INCLUDE, /* turns into STMT_INLINE_BLOCK after typing */ STMT_MESSAGE, STMT_DEFER, STMT_USE, @@ -944,7 +951,8 @@ typedef enum { STMT_INLINE_BLOCK /* a group of statements acting as one statement, e.g. all the statements from a #include */ } StatementKind; enum { - STMT_TYPED = 0x01 + STMT_TYPED = 0x01, + STMT_IS_INIT = 0x02 /* is this an initialization statement? if so, during typing it will be added to gctx->inits (this can't be done at parsing because the statements array is changing) */ }; typedef struct Statement { Location where; @@ -954,6 +962,7 @@ typedef struct Statement { Declaration *decl; /* we want the pointer to be fixed so that we can refer to it from an identifier */ Expression *expr; Return *ret; + Include *inc; Message *message; /* #error, #warn, #info */ Block *referring_to; /* for break/continue; set during typing */ struct Statement *defer; @@ -971,13 +980,9 @@ 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) */ typedef struct { - Statement stmt; - I64 priority; + Statement *stmt; } Initialization; typedef struct ParsedFile { @@ -992,8 +997,8 @@ typedef struct { typedef struct { /* - statements run before any typing happens - after typing, these will be in sorted order (for cgen) + statements to be run before main function is called. + these are in order of appearance (which is the order in which they are called) */ Initialization *inits; StrHashTable included_files; /* maps to IncludedFile. */ @@ -1006,11 +1011,10 @@ typedef struct Parser { Tokenizer *tokr; Allocator *allocr; Identifiers *globals; - GlobalCtx *gctx; File *file; Block *block; /* which block are we in? NULL = file scope */ - Namespace *nms; ParsedFile *parsed_file; + GlobalCtx *gctx; } Parser; typedef struct { @@ -1060,6 +1064,12 @@ typedef struct Typer { DeclWithCtx *all_globals; /* includes stuff in namespaces, as long as it's not in a function */ IdentID lbl_counter; unsigned long nms_counter; /* counter for namespace IDs */ + StrHashTable included_files; /* maps to IncludedFile */ + /* + have we had an error because we couldn't find a file that was #include'd + (so that we can stop compiling immediately) + */ + bool had_include_err; } Typer; typedef struct CGenerator { |