summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--abbrevs.txt1
-rw-r--r--cgen.c2
-rw-r--r--development.md34
-rw-r--r--eval.c2
-rw-r--r--main.c10
-rw-r--r--parse.c38
-rw-r--r--types.c30
-rw-r--r--types.h20
8 files changed, 96 insertions, 41 deletions
diff --git a/abbrevs.txt b/abbrevs.txt
index d486c78..d865020 100644
--- a/abbrevs.txt
+++ b/abbrevs.txt
@@ -17,6 +17,7 @@ eval - evaluate
evalr - evaluator
expr - expression
fn - function
+gctx - global context
ident - identifier
inc - include
init - initialization
diff --git a/cgen.c b/cgen.c
index bf2c9c7..cdc16b4 100644
--- a/cgen.c
+++ b/cgen.c
@@ -2085,7 +2085,7 @@ 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(f->inits, Initialization, init) {
+ 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);
diff --git a/development.md b/development.md
index e3ec8c4..9f0980c 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 which is very useful.
+data\_structures.c contains a dynamic array implementation and string hash table which are very useful.
### Notes
@@ -37,9 +37,9 @@ 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
-#### 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:
```
@@ -57,3 +57,31 @@ The way we evaluate `bar()` is by typing the function `bar` when we come across
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/eval.c b/eval.c
index 6e26be9..1bbea0d 100644
--- a/eval.c
+++ b/eval.c
@@ -671,7 +671,7 @@ static Value *ident_val(Evaluator *ev, Identifier i, Location where) {
Declaration *decl = i->decl;
assert(decl);
int idx = decl_ident_index(decl, i);
- if (decl->type.kind == TYPE_UNKNOWN && ev->typer->err_ctx->have_errored)
+ if (decl->type.kind == TYPE_UNKNOWN && ev->typer->gctx->err_ctx->have_errored)
return NULL; /* silently fail (something went wrong when we typed this decl) */
if (decl->flags & DECL_IS_PARAM) {
if (decl->val_stack) {
diff --git a/main.c b/main.c
index 05a94fe..8a33d27 100644
--- a/main.c
+++ b/main.c
@@ -213,10 +213,12 @@ int main(int argc, char **argv) {
}
if (verbose) printf("Parsing...\n");
- StrHashTable included_files = {0};
- str_hash_table_create(&included_files, sizeof(IncludedFile), &main_allocr);
+ 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, &file, &included_files);
+ parser_create(&p, &globals, &t, &main_allocr, &global_ctx);
ParsedFile f;
if (!parse_file(&p, &f)) {
err_text_important(&err_ctx, "Errors occured during parsing.\n");
@@ -234,7 +236,7 @@ int main(int argc, char **argv) {
Typer tr;
Evaluator ev;
evalr_create(&ev, &tr, &main_allocr);
- typer_create(&tr, &ev, &err_ctx, &main_allocr, &globals);;
+ typer_create(&tr, &ev, &main_allocr, &globals, &global_ctx);
if (!types_file(&tr, &f)) {
err_text_important(&err_ctx, "Errors occured while determining types.\n");
diff --git a/parse.c b/parse.c
index a48fc6b..378eef3 100644
--- a/parse.c
+++ b/parse.c
@@ -3,7 +3,7 @@
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, File *main_file, StrHashTable *included_files);
+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);
@@ -344,6 +344,18 @@ static inline Identifiers *parser_get_idents(Parser *p) {
return p->block == NULL ? p->globals : &p->block->idents;
}
+static inline bool block_is_at_top_level(Block *b) {
+ for (Block *bb = b; bb; bb = bb->parent)
+ if (bb->kind != BLOCK_NMS)
+ return false;
+ return true;
+}
+
+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)
@@ -2329,6 +2341,8 @@ 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;
@@ -2572,11 +2586,12 @@ static Status parse_stmt(Parser *p, Statement *s, bool *was_a_statement) {
if (!forced) {
size_t filename_len = strlen(filename);
- if (streq(filename, p->main_file->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;
}
- inc_f = str_hash_table_get(p->included_files, filename, filename_len);
+ 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) {
@@ -2590,7 +2605,7 @@ static Status parse_stmt(Parser *p, Statement *s, bool *was_a_statement) {
}
goto nms_done;
} else {
- inc_f = str_hash_table_insert(p->included_files, filename, filename_len);
+ inc_f = str_hash_table_insert(included_files, filename, filename_len);
inc_f->flags |= INC_FILE_INCLUDING;
inc_f->main_nms = p->nms;
}
@@ -2620,7 +2635,7 @@ static Status parse_stmt(Parser *p, Statement *s, bool *was_a_statement) {
#endif
Parser parser;
- parser_create(&parser, p->globals, &tokr, p->allocr, p->main_file, p->included_files);
+ parser_create(&parser, p->globals, &tokr, p->allocr, p->gctx);
parser.block = p->block;
parser.nms = p->nms;
ParsedFile parsed_file;
@@ -2672,8 +2687,13 @@ static Status parse_stmt(Parser *p, Statement *s, bool *was_a_statement) {
}
} 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->parsed_file->inits);
+ 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.");
@@ -2746,19 +2766,17 @@ static Status parse_stmt(Parser *p, Statement *s, bool *was_a_statement) {
return true;
}
-static void parser_create(Parser *p, Identifiers *globals, Tokenizer *t, Allocator *allocr, File *main_file, StrHashTable *included_files) {
+static void parser_create(Parser *p, Identifiers *globals, Tokenizer *t, Allocator *allocr, GlobalCtx *gctx) {
p->tokr = t;
p->block = NULL;
p->globals = globals;
p->allocr = allocr;
- p->main_file = main_file;
- p->included_files = included_files;
+ p->gctx = gctx;
}
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;
diff --git a/types.c b/types.c
index 7ec7520..36f6b81 100644
--- a/types.c
+++ b/types.c
@@ -36,14 +36,7 @@ static inline void typer_block_enter(Typer *tr, Block *b) {
tr->block = b;
}
-static inline bool block_is_at_top_level(Block *b) {
- for (Block *bb = b; bb; bb = bb->parent)
- if (bb->kind != BLOCK_NMS)
- return false;
- return true;
-}
-
-static bool is_at_top_level(Typer *tr) {
+static bool typer_is_at_top_level(Typer *tr) {
return block_is_at_top_level(tr->block);
}
@@ -940,7 +933,7 @@ static Status type_resolve(Typer *tr, Type *t, Location where) {
Expression *expr = t->expr;
if (!types_expr(tr, expr))
return false;
- if (expr->type.kind == TYPE_UNKNOWN && tr->err_ctx->have_errored)
+ if (expr->type.kind == TYPE_UNKNOWN && tr->gctx->err_ctx->have_errored)
return false; /* silently fail (e.g. if a function couldn't be typed) */
if (!type_is_builtin(&expr->type, BUILTIN_TYPE)) {
/* ok maybe it's a tuple of types, which we'll convert to a TYPE_TUPLE */
@@ -1561,7 +1554,7 @@ static Status expr_must_usable(Typer *tr, Expression *e) {
if (t->kind != TYPE_STRUCT && !type_is_builtin(t, BUILTIN_NMS)) {
if (!(t->kind == TYPE_PTR && t->ptr->kind == TYPE_STRUCT)) {
if (t->kind == TYPE_UNKNOWN) {
- if (tr->err_ctx->have_errored) {
+ if (tr->gctx->err_ctx->have_errored) {
/* silently fail; this could've been because of an earlier error */
return false;
}
@@ -1875,7 +1868,7 @@ static Status types_expr(Typer *tr, Expression *e) {
Type *arg_types = NULL;
arr_set_len(arg_types, nparams);
Value *arg_vals = typer_malloc(tr, nparams * sizeof *arg_vals);
- ErrCtx *err_ctx = tr->err_ctx;
+ ErrCtx *err_ctx = tr->gctx->err_ctx;
size_t p = 0;
arr_foreach(struc.params, Declaration, param) {
Value param_val = {0};
@@ -3031,7 +3024,7 @@ static Status types_decl(Typer *tr, Declaration *d) {
"Try moving the function outside of the struct, otherwise you might run into problems.");
}
} else if (e->kind == EXPR_NMS) {
- if (is_at_top_level(tr))
+ if (typer_is_at_top_level(tr))
e->nms->associated_ident = d->idents[0];
} else if (e->kind == EXPR_TYPE
&& e->typeval->kind == TYPE_STRUCT
@@ -3195,7 +3188,7 @@ static Status types_decl(Typer *tr, Declaration *d) {
d->expr.fn->flags |= FN_EXPR_EXPORT;
}
- if (is_at_top_level(tr)) {
+ if (typer_is_at_top_level(tr)) {
DeclWithCtx dctx = {d, tr->nms, tr->block};
typer_arr_add(tr, tr->all_globals, dctx);
}
@@ -3532,7 +3525,7 @@ top:
}
/* fallthrough */
default: {
- if (fo->of->type.kind == TYPE_UNKNOWN && tr->err_ctx->have_errored) {
+ if (fo->of->type.kind == TYPE_UNKNOWN && tr->gctx->err_ctx->have_errored) {
/* silently fail */
goto for_fail;
}
@@ -3803,12 +3796,12 @@ success:
return true;
}
-static void typer_create(Typer *tr, Evaluator *ev, ErrCtx *err_ctx, Allocator *allocr, Identifiers *idents) {
+static void typer_create(Typer *tr, Evaluator *ev, Allocator *allocr, Identifiers *idents, GlobalCtx *gctx) {
memset(tr, 0, sizeof *tr);
tr->evalr = ev;
- tr->err_ctx = err_ctx;
tr->allocr = allocr;
tr->globals = idents;
+ tr->gctx = gctx;
}
static int compare_inits(const void *av, const void *bv) {
@@ -3822,8 +3815,9 @@ static Status types_file(Typer *tr, ParsedFile *f) {
bool ret = true;
tr->parsed_file = f;
tr->uses = NULL;
- qsort(f->inits, arr_len(f->inits), sizeof *f->inits, compare_inits);
- arr_foreach(f->inits, Initialization, init) {
+ 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;
diff --git a/types.h b/types.h
index e5c665b..d5fe6c0 100644
--- a/types.h
+++ b/types.h
@@ -982,23 +982,35 @@ typedef struct {
typedef struct ParsedFile {
Statement *stmts;
+} ParsedFile;
+
+typedef struct {
+ Statement *stmt;
+ Block *block;
+ Namespace *nms;
+} StatementWithCtx;
+
+typedef struct {
/*
statements run before any typing happens
after typing, these will be in sorted order (for cgen)
*/
Initialization *inits;
-} ParsedFile;
+ StrHashTable included_files; /* maps to IncludedFile. */
+ File *main_file; /* this is the file which the compiler is invoked on. needed for checking for circular includes. */
+ StatementWithCtx *static_ifs; /* all the #ifs */
+ ErrCtx *err_ctx;
+} GlobalCtx;
typedef struct Parser {
Tokenizer *tokr;
Allocator *allocr;
Identifiers *globals;
+ GlobalCtx *gctx;
File *file;
- File *main_file; /* this is the file which the compiler is invoked on. needed for checking for circular includes. */
Block *block; /* which block are we in? NULL = file scope */
Namespace *nms;
ParsedFile *parsed_file;
- StrHashTable *included_files; /* maps to IncludedFile. this is a pointer because all Parsers (i.e. the Parser for every file) have a common included_files */
} Parser;
typedef struct {
@@ -1040,7 +1052,7 @@ typedef struct Typer {
Declaration **in_decls; /* array of declarations we are currently inside */
Block *block;
FnExpr *fn; /* the function we're currently parsing. */
- ErrCtx *err_ctx;
+ GlobalCtx *gctx;
ParsedFile *parsed_file;
Namespace *nms;
FnWithCtx *all_fns; /* does not include templates */