summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Tenenbaum <pommicket@gmail.com>2020-07-03 16:09:10 -0400
committerLeo Tenenbaum <pommicket@gmail.com>2020-07-03 16:09:10 -0400
commit119950ebde3c9f096d1866ba6448d6bd9b22d063 (patch)
tree35e6bb7a2507910ac784356baf25c2412b6959b6
parentb25f392b221cea498ea7695f68828909734b51cd (diff)
removed ability to evaluate a function before it is declared at compile time; that was causing too many problems
-rw-r--r--abbrevs.txt1
-rw-r--r--allocator.c7
-rw-r--r--cgen.c92
-rw-r--r--copy.c16
-rw-r--r--development.md50
-rw-r--r--docs/00.html2
-rw-r--r--eval.c9
-rw-r--r--main.c6
-rw-r--r--misc.c9
-rw-r--r--parse.c235
-rw-r--r--test.toc6
-rw-r--r--tests/init.toc15
-rw-r--r--tests/misc.toc22
-rw-r--r--tests/std/base.toc5
-rw-r--r--tests/tests.c1
-rw-r--r--tests/types.toc46
-rw-r--r--types.c242
-rw-r--r--types.h30
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;
-}
diff --git a/cgen.c b/cgen.c
index cdc16b4..c4427e1 100644
--- a/cgen.c
+++ b/cgen.c
@@ -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;
}
diff --git a/copy.c b/copy.c
index 0adce52..b6716f5 100644
--- a/copy.c
+++ b/copy.c
@@ -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>
diff --git a/eval.c b/eval.c
index 1bbea0d..e4b5313 100644
--- a/eval.c
+++ b/eval.c
@@ -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;
}
diff --git a/main.c b/main.c
index 8a33d27..fbd6029 100644
--- a/main.c
+++ b/main.c
@@ -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;
diff --git a/misc.c b/misc.c
index 352deb2..994e0ea 100644
--- a/misc.c
+++ b/misc.c
@@ -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");
+}
diff --git a/parse.c b/parse.c
index 378eef3..53df60f 100644
--- a/parse.c
+++ b/parse.c
@@ -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) {
diff --git a/test.toc b/test.toc
index 43ccf56..d79f526 100644
--- a/test.toc
+++ b/test.toc
@@ -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;
-}
diff --git a/types.c b/types.c
index 36f6b81..f103f5d 100644
--- a/types.c
+++ b/types.c
@@ -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;
}
}
diff --git a/types.h b/types.h
index d5fe6c0..11efddb 100644
--- a/types.h
+++ b/types.h
@@ -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 {