diff options
-rw-r--r-- | main.c | 3 | ||||
-rw-r--r-- | package.c | 14 | ||||
-rw-r--r-- | parse.c | 97 | ||||
-rw-r--r-- | test.toc | 3 | ||||
-rw-r--r-- | tokenizer.c | 7 | ||||
-rw-r--r-- | types.c | 21 | ||||
-rw-r--r-- | types.h | 5 |
7 files changed, 116 insertions, 34 deletions
@@ -124,9 +124,8 @@ int main(int argc, char **argv) { #ifdef TOC_DEBUG FILE *out_pkg = fopen("out.top", "wb"); - exptr_create(&exptr, out_pkg); + exptr_create(&exptr, out_pkg, contents); exptr.export_locations = false; - exptr_start(&exptr, contents); #endif if (!block_enter(NULL, f.stmts, SCOPE_CHECK_REDECL)) /* enter global scope */ return false; @@ -10,15 +10,18 @@ static bool export_decl(Exporter *ex, Declaration *d); static bool export_block(Exporter *ex, Block *b); static bool export_expr(Exporter *ex, Expression *e); -static void exptr_create(Exporter *ex, FILE *out) { +static void exptr_create(Exporter *ex, FILE *out, char *code) { ex->out = out; ex->ident_id = 0; ex->export_locations = true; ex->exported_fns = NULL; ex->exported_structs = NULL; ex->exported_idents = NULL; + ex->started = false; + ex->code = code; } + static inline void export_u8(Exporter *ex, U8 u8) { write_u8(ex->out, u8); } @@ -58,7 +61,7 @@ static inline void export_char(Exporter *ex, char c) { static inline void export_vlq(Exporter *ex, U64 x) { write_vlq(ex->out, x); } -/* since char may be signed or unsigned, use this only if necessary */ + static inline void export_str(Exporter *ex, const char *str, size_t len) { #ifdef TOC_DEBUG for (size_t i = 0; i < len; ++i) @@ -95,12 +98,16 @@ static inline void export_len(Exporter *ex, size_t len) { } /* writes the header */ -static void exptr_start(Exporter *ex, char *code) { +static void exptr_start(Exporter *ex, const char *pkg_name, size_t pkg_name_len) { const U8 toc[3] = {116, 111, 99}; /* "toc" in ASCII */ + const char *code = ex->code; + ex->started = true; export_u8(ex, toc[0]); export_u8(ex, toc[1]); export_u8(ex, toc[2]); export_u32(ex, TOP_FMT_VERSION); + export_len(ex, pkg_name_len); + export_str(ex, pkg_name, pkg_name_len); bool has_code = code != NULL; export_bool(ex, has_code); if (has_code) { @@ -440,6 +447,7 @@ enum { }; static bool export_decl(Exporter *ex, Declaration *d) { + assert(ex->started); if (d->type.kind == TYPE_UNKNOWN) { err_print(d->where, "Can't export declaration of unknown type."); return false; @@ -4,7 +4,7 @@ You should have received a copy of the GNU General Public License along with toc. If not, see <https://www.gnu.org/licenses/>. */ static bool parse_expr(Parser *p, Expression *e, Token *end); -static bool parse_stmt(Parser *p, Statement *s); +static bool parse_stmt(Parser *p, Statement *s, bool *was_a_statement); enum { PARSE_DECL_ALLOW_CONST_WITH_NO_EXPR = 0x01, PARSE_DECL_ALLOW_SEMI_CONST = 0x02, @@ -787,11 +787,11 @@ static bool parse_block(Parser *p, Block *b) { Block *prev_block = p->block; b->flags = 0; b->ret_expr = NULL; - p->block = b; if (!token_is_kw(t->token, KW_LBRACE)) { tokr_err(t, "Expected '{' to open block."); return false; } + p->block = b; b->start = t->token->where; ++t->token; /* move past { */ b->stmts = NULL; @@ -801,22 +801,28 @@ static bool parse_block(Parser *p, Block *b) { /* non-empty block */ while (1) { Statement *stmt = parser_arr_add(p, &b->stmts); - bool success = parse_stmt(p, stmt); + bool was_a_statement; + bool success = parse_stmt(p, stmt, &was_a_statement); if (!success) { ret = false; } + if (!was_a_statement) { + arr_remove_lasta(&b->stmts, p->allocr); + } if (token_is_kw(t->token, KW_RBRACE)) { break; } if (t->token->kind == TOKEN_EOF) { tokr_err(t, "Expected '}' to close function body."); - return false; + ret = false; + goto end; } } } b->end = t->token->where; ++t->token; /* move past } */ + end: p->block = prev_block; return ret; } @@ -1892,7 +1898,8 @@ static bool is_decl(Tokenizer *t) { } } -static bool parse_stmt(Parser *p, Statement *s) { +/* sets *was_a_statement to false if s was not filled, but the token was advanced */ +static bool parse_stmt(Parser *p, Statement *s, bool *was_a_statement) { Tokenizer *t = p->tokr; if (t->token->kind == TOKEN_EOF) { tokr_err(t, "Expected statement."); @@ -1900,30 +1907,61 @@ static bool parse_stmt(Parser *p, Statement *s) { } s->where = t->token->where; s->flags = 0; - if (token_is_kw(t->token, KW_RETURN)) { - s->kind = STMT_RET; - ++t->token; - s->ret.flags = 0; - if (token_is_kw(t->token, KW_SEMICOLON)) { - /* return with no expr */ + *was_a_statement = true; + if (t->token->kind == TOKEN_KW) { + switch (t->token->kw) { + case KW_RETURN: { + s->kind = STMT_RET; ++t->token; - return true; + s->ret.flags = 0; + if (token_is_kw(t->token, KW_SEMICOLON)) { + /* return with no expr */ + ++t->token; + return true; + } + s->ret.flags |= RET_HAS_EXPR; + Token *end = expr_find_end(p, 0); + if (!end) { + while (t->token->kind != TOKEN_EOF) ++t->token; /* move to end of file */ + return false; + } + if (!token_is_kw(end, KW_SEMICOLON)) { + err_print(end->where, "Expected ';' at end of return statement."); + t->token = end->kind == TOKEN_EOF ? end : end + 1; + return false; + } + bool success = parse_expr(p, &s->ret.expr, end); + t->token = end + 1; + return success; } - s->ret.flags |= RET_HAS_EXPR; - Token *end = expr_find_end(p, 0); - if (!end) { - while (t->token->kind != TOKEN_EOF) ++t->token; /* move to end of file */ - return false; + case KW_PKG: { + /* declaration of package name */ + Expression *pkg_name = parser_new_expr(p); + ++t->token; + Token *end = expr_find_end(p, 0); + if (!end || !token_is_kw(end, KW_SEMICOLON)) { + tokr_err(t, "No semicolon at end of package name."); + tokr_skip_to_eof(t); + return false; + } + if (p->block) { + tokr_err(t, "You must set the package name at global scope."); + t->token = end + 1; + return false; + } + if (p->file->pkg_name) { + tokr_err(t, "You've already set the package name."); + t->token = end + 1; + return false; + } + p->file->pkg_name = pkg_name; + bool success = parse_expr(p, pkg_name, end); + t->token = end + 1; + *was_a_statement = false; + return success; } - if (!token_is_kw(end, KW_SEMICOLON)) { - err_print(end->where, "Expected ';' at end of return statement."); - t->token = end->kind == TOKEN_EOF ? end : end + 1; - return false; + default: break; } - bool success = parse_expr(p, &s->ret.expr, end); - t->token = end + 1; - return success; - } if (is_decl(t)) { s->kind = STMT_DECL; @@ -1936,7 +1974,7 @@ static bool parse_stmt(Parser *p, Statement *s) { Token *end = expr_find_end(p, 0); if (!end) { tokr_err(t, "No semicolon found at end of statement."); - while (t->token->kind != TOKEN_EOF) ++t->token; /* move to end of file */ + tokr_skip_to_eof(t); return false; } @@ -1963,11 +2001,16 @@ static void parser_create(Parser *p, Tokenizer *t, Allocator *allocr) { static bool parse_file(Parser *p, ParsedFile *f) { Tokenizer *t = p->tokr; f->stmts = NULL; + f->pkg_name = NULL; + p->file = f; bool ret = true; while (t->token->kind != TOKEN_EOF) { + bool was_a_statement; Statement *stmt = parser_arr_add(p, &f->stmts); - if (!parse_stmt(p, stmt)) + if (!parse_stmt(p, stmt, &was_a_statement)) ret = false; + if (!was_a_statement) + arr_remove_lasta(&f->stmts, p->allocr); if (token_is_kw(t->token, KW_RBRACE)) { tokr_err(t, "} without a matching {."); return false; @@ -1,3 +1,5 @@ +pkg "foo"; + Foo ::= struct { x: int; }; @@ -5,7 +7,6 @@ Foo ::= struct { foo ::= fn() int { 3 }; - #export main ::= fn() { g ::= fn() int { 3 }; a := new (int, 3); diff --git a/tokenizer.c b/tokenizer.c index d2bf87f..4c1d31a 100644 --- a/tokenizer.c +++ b/tokenizer.c @@ -13,7 +13,8 @@ static const char *keywords[KW_COUNT] = "new", "del", "struct", "int", "i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64", "float", "f32", "f64", "Type", - "char", "bool", "true", "false"}; + "char", "bool", "true", "false", + "pkg"}; static inline const char *kw_to_str(Keyword k) { return keywords[k]; } @@ -542,6 +543,10 @@ static void tokr_skip_semicolon(Tokenizer *t) { } } +static inline void tokr_skip_to_eof(Tokenizer *t) { + while (t->token->kind != TOKEN_EOF) ++t->token; /* move to end of file */ +} + /* only frees tokens, not string literals (because those are on the allocator). */ static void tokr_free(Tokenizer *t) { arr_clear(&t->tokens); @@ -2127,6 +2127,27 @@ static void typer_create(Typer *tr, Evaluator *ev, Allocator *allocr) { static bool types_file(Typer *tr, ParsedFile *f) { bool ret = true; + if (f->pkg_name) { + Value pkg_name; + if (!types_expr(tr, f->pkg_name)) + return false; + if (f->pkg_name->type.kind != TYPE_SLICE || !type_is_builtin(f->pkg_name->type.slice, BUILTIN_CHAR)) { + char *typestr = type_to_str(&f->pkg_name->type); + err_print(f->pkg_name->where, "Package names must be of type []char, but this is of type %s.", typestr); + free(typestr); + return false; + } + if (!eval_expr(tr->evalr, f->pkg_name, &pkg_name)) + return false; + Slice pkg_name_slice = pkg_name.slice; + char *pkg_name_str = pkg_name_slice.data; + I64 pkg_name_len = pkg_name_slice.n; + if (pkg_name_len < 0) { + err_print(f->pkg_name->where, "Package name has a negative length (" I64_FMT ")!", pkg_name_len); + return false; + } + exptr_start(tr->exptr, pkg_name_str, (size_t)pkg_name_len); + } arr_foreach(f->stmts, Statement, s) { if (!types_stmt(tr, s)) { ret = false; @@ -276,6 +276,7 @@ typedef enum { KW_BOOL, KW_TRUE, KW_FALSE, + KW_PKG, KW_COUNT } Keyword; @@ -720,12 +721,14 @@ typedef struct Statement { typedef struct ParsedFile { Statement *stmts; + Expression *pkg_name; } ParsedFile; typedef struct Parser { Tokenizer *tokr; Allocator *allocr; Block *block; /* which block are we in? NULL = file scope */ + ParsedFile *file; } Parser; typedef enum { @@ -758,10 +761,12 @@ typedef struct Typer { typedef struct Exporter { FILE *out; /* .top (toc package) to output to */ bool export_locations; + bool started; U64 ident_id; FnExpr **exported_fns; StructDef **exported_structs; Identifier *exported_idents; /* (only those whose names are exported) */ + const char *code; } Exporter; typedef struct CGenerator { |