summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--main.c3
-rw-r--r--package.c14
-rw-r--r--parse.c97
-rw-r--r--test.toc3
-rw-r--r--tokenizer.c7
-rw-r--r--types.c21
-rw-r--r--types.h5
7 files changed, 116 insertions, 34 deletions
diff --git a/main.c b/main.c
index bcdec84..e7b8567 100644
--- a/main.c
+++ b/main.c
@@ -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;
diff --git a/package.c b/package.c
index a050d90..7d42928 100644
--- a/package.c
+++ b/package.c
@@ -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;
diff --git a/parse.c b/parse.c
index b754f3f..ac87acc 100644
--- a/parse.c
+++ b/parse.c
@@ -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;
diff --git a/test.toc b/test.toc
index 1e3969c..5a69154 100644
--- a/test.toc
+++ b/test.toc
@@ -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);
diff --git a/types.c b/types.c
index e2a1bf1..fab4c73 100644
--- a/types.c
+++ b/types.c
@@ -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;
diff --git a/types.h b/types.h
index 82b1c08..a612368 100644
--- a/types.h
+++ b/types.h
@@ -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 {