summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Tenenbaum <pommicket@gmail.com>2020-05-11 22:52:30 -0400
committerLeo Tenenbaum <pommicket@gmail.com>2020-05-11 22:52:30 -0400
commitd5e0c67e29e7da0e393a9e0d50cfa36f9829165d (patch)
treec931a2731b6bda43a819efc972c8ace68d7379a8
parente64ad4e05257063863040c087181959577535834 (diff)
fixed some problems you can get when you put a function in a struct
-rw-r--r--cgen.c21
-rw-r--r--decls_cgen.c41
-rw-r--r--main.c5
-rw-r--r--parse.c41
-rw-r--r--test.toc21
-rw-r--r--types.c60
-rw-r--r--types.h35
7 files changed, 147 insertions, 77 deletions
diff --git a/cgen.c b/cgen.c
index 87fc015..a88f516 100644
--- a/cgen.c
+++ b/cgen.c
@@ -51,10 +51,9 @@ static void cgen_defs_decl(CGenerator *g, Declaration *d);
g->block = prev_block; \
}
-/* calls f on every sub-expression of e, block_f on every sub-block, and decl_f on every sub-declaration. */
-#define cgen_recurse_subexprs(g, e, f, block_f, decl_f) \
+/* calls f on every sub-expression of e, block_f on every sub-block, type_f on every type, and decl_f on every sub-declaration. */
+#define cgen_recurse_subexprs(g, e, f, block_f, decl_f, type_f) \
switch (e->kind) { \
- case EXPR_TYPE: \
case EXPR_VAL: \
case EXPR_C: \
case EXPR_BUILTIN: \
@@ -64,6 +63,9 @@ static void cgen_defs_decl(CGenerator *g, Declaration *d);
case EXPR_LITERAL_STR: \
case EXPR_LITERAL_CHAR: \
case EXPR_LITERAL_FLOAT: \
+ break; \
+ case EXPR_TYPE: \
+ type_f(g, e->typeval); \
break; \
case EXPR_UNARY_OP: \
f(g, e->unary.of); \
@@ -75,6 +77,7 @@ static void cgen_defs_decl(CGenerator *g, Declaration *d);
break; \
case EXPR_CAST: \
f(g, e->cast.expr); \
+ type_f(g, &e->cast.type); \
break; \
case EXPR_CALL: \
f(g, e->call.fn); \
@@ -2164,6 +2167,16 @@ static void cgen_stmt(CGenerator *g, Statement *s) {
}
}
+static void cgen_defs_type(CGenerator *g, Type *t) {
+ if (t->kind == TYPE_STRUCT) {
+ StructDef *sdef = t->struc;
+ if (!(sdef->flags & STRUCT_DEF_CGEN_FN_DEFS)) {
+ cgen_defs_block(g, &sdef->body);
+ sdef->flags |= STRUCT_DEF_CGEN_FN_DEFS;
+ }
+ }
+}
+
static void cgen_defs_fn(CGenerator *g, FnExpr *f) {
if (fn_has_instances(f)) {
HashTable *instances = f->instances;
@@ -2184,7 +2197,7 @@ static void cgen_defs_expr(CGenerator *g, Expression *e) {
if (e->kind == EXPR_FN) {
cgen_defs_fn(g, e->fn);
}
- cgen_recurse_subexprs(g, e, cgen_defs_expr, cgen_defs_block, cgen_defs_decl);
+ cgen_recurse_subexprs(g, e, cgen_defs_expr, cgen_defs_block, cgen_defs_decl, cgen_defs_type);
}
static void cgen_defs_decl(CGenerator *g, Declaration *d) {
diff --git a/decls_cgen.c b/decls_cgen.c
index 97c63ec..5d527cd 100644
--- a/decls_cgen.c
+++ b/decls_cgen.c
@@ -10,6 +10,19 @@ static void cgen_decls_stmt(CGenerator *g, Statement *s);
static void cgen_decls_block(CGenerator *g, Block *b);
static void cgen_decls_decl(CGenerator *g, Declaration *d);
+static void cgen_sdecls_block(CGenerator *g, Block *b) {
+ Block *prev_block = g->block;
+ g->block = b;
+ b->c.break_lbl = 0;
+ b->c.cont_lbl = 0;
+
+ arr_foreach(b->stmts, Statement, s)
+ cgen_sdecls_stmt(g, s);
+ if (b->ret_expr)
+ cgen_sdecls_expr(g, b->ret_expr);
+ g->block = prev_block;
+}
+
/* i is the name for this type, NULL if not available */
static void cgen_sdecls_type(CGenerator *g, Type *type) {
if (type->kind == TYPE_STRUCT) {
@@ -24,25 +37,13 @@ static void cgen_sdecls_type(CGenerator *g, Type *type) {
cgen_struct_name(g, sdef);
cgen_write(g, ";");
cgen_nl(g);
+ cgen_sdecls_block(g, &sdef->body);
sdef->flags |= STRUCT_DEF_CGEN_DECLARED;
}
}
cgen_recurse_subtypes(cgen_sdecls_type, g, type);
}
-static void cgen_sdecls_block(CGenerator *g, Block *b) {
- Block *prev_block = g->block;
- g->block = b;
- b->c.break_lbl = 0;
- b->c.cont_lbl = 0;
-
- arr_foreach(b->stmts, Statement, s)
- cgen_sdecls_stmt(g, s);
- if (b->ret_expr)
- cgen_sdecls_expr(g, b->ret_expr);
- g->block = prev_block;
-}
-
static char *cgen_nms_prefix_part(CGenerator *g, Namespace *n) {
char *s;
if (n->associated_ident) {
@@ -65,9 +66,6 @@ static char *cgen_nms_prefix_part(CGenerator *g, Namespace *n) {
static void cgen_sdecls_expr(CGenerator *g, Expression *e) {
switch (e->kind) {
- case EXPR_CAST:
- cgen_sdecls_type(g, &e->cast.type);
- break;
case EXPR_FN:
/* needs to go before decls_cgen.c... */
e->fn->c.id = ++g->ident_counter;
@@ -92,7 +90,7 @@ static void cgen_sdecls_expr(CGenerator *g, Expression *e) {
default: break;
}
if (e->kind != EXPR_IDENT) {
- cgen_recurse_subexprs(g, e, cgen_sdecls_expr, cgen_sdecls_block, cgen_sdecls_decl);
+ cgen_recurse_subexprs(g, e, cgen_sdecls_expr, cgen_sdecls_block, cgen_sdecls_decl, cgen_sdecls_type);
}
if (e->kind == EXPR_NMS) {
arr_remove_last(g->nms_prefixes);
@@ -190,8 +188,10 @@ static void cgen_decls_type(CGenerator *g, Type *type) {
--g->indent_lvl;
cgen_write(g, "};");
cgen_nl(g);
+ cgen_decls_block(g, &sdef->body);
sdef->flags |= STRUCT_DEF_CGEN_DEFINED;
}
+
}
cgen_recurse_subtypes(cgen_decls_type, g, type);
}
@@ -294,7 +294,7 @@ static void cgen_fn_decl(CGenerator *g, FnExpr *f, Type *t) {
static void cgen_decls_expr(CGenerator *g, Expression *e) {
assert(e->flags & EXPR_FOUND_TYPE);
- cgen_recurse_subexprs(g, e, cgen_decls_expr, cgen_decls_block, cgen_decls_decl);
+ cgen_recurse_subexprs(g, e, cgen_decls_expr, cgen_decls_block, cgen_decls_decl, cgen_decls_type);
switch (e->kind) {
case EXPR_FN: {
FnExpr *f = e->fn;
@@ -307,9 +307,6 @@ static void cgen_decls_expr(CGenerator *g, Expression *e) {
Type *type = e->typeval;
cgen_decls_type(g, type);
} break;
- case EXPR_CAST:
- cgen_decls_type(g, &e->cast.type);
- break;
case EXPR_BINARY_OP: {
Type *lhs_type = &e->binary.lhs->type;
if (lhs_type->kind == TYPE_PTR)
@@ -333,7 +330,7 @@ static void cgen_decls_decl(CGenerator *g, Declaration *d) {
cgen_decls_type(g, &d->type);
if (cgen_fn_is_direct(g, d)) {
cgen_fn_decl(g, d->expr.fn, &d->expr.type);
- cgen_recurse_subexprs(g, (&d->expr), cgen_decls_expr, cgen_decls_block, cgen_decls_decl);
+ cgen_recurse_subexprs(g, (&d->expr), cgen_decls_expr, cgen_decls_block, cgen_decls_decl, cgen_decls_type);
} else {
if (d->flags & DECL_HAS_EXPR) {
cgen_decls_expr(g, &d->expr);
diff --git a/main.c b/main.c
index 49b1cc2..c45fb0b 100644
--- a/main.c
+++ b/main.c
@@ -8,9 +8,11 @@
/*
@TODO:
-initialization functions (maybe #init(-50), where -50 is the priority and <0 is reserved for standard library)
+struct_add_block needs serious changes, now that #ifs generate *inline* blocks
+get rid of UNARY_LEN?
detect circular declarations (A ::= B; B ::= A)
either detect circular #includes or set a #include depth limit (maybe sometimes you want finite circular includes with #if)
+initialization functions (maybe #init(-50), where -50 is the priority and <0 is reserved for standard library)
if we do #include "foo.toc", bar; and foo.toc fails, bar should be declared as TYPE_UNKNOWN (right now it's undeclared)
improve type_to_str:
Foo ::= struct(t::Type) {}
@@ -26,6 +28,7 @@ switch to / add as an alternative: libffi
don't bother generating ret_ if nothing's deferred
X ::= newtype(int); or something
any odd number of "s for a string
+give each warning a number; #no_warn(warning), #no_warn_throughout_this_file(warning)
use point #except x;
optional -Wshadow
format errors so that vim/emacs can jump to them
diff --git a/parse.c b/parse.c
index 3e0a5dd..4ba2af2 100644
--- a/parse.c
+++ b/parse.c
@@ -652,9 +652,9 @@ static Status parse_type(Parser *p, Type *type, Location *where) {
p->block = prev_block;
if (!parse_block(p, &struc->body, PARSE_BLOCK_DONT_CREATE_IDENTS))
return false;
+ struc->body.kind = BLOCK_STRUCT;
parser_put_end(p, &struc->where);
break;
-
struct_fail:
p->block = prev_block;
return false;
@@ -2420,18 +2420,14 @@ static Status parse_stmt(Parser *p, Statement *s, bool *was_a_statement) {
}
break;
case KW_DEFER: {
- if (p->block == NULL) {
- tokr_err(t, "You can't defer something at global scope.");
- return false;
- }
++t->token;
s->kind = STMT_DEFER;
Token *deferred_start = t->token;
s->defer = parser_malloc(p, sizeof *s->defer);
if (!parse_stmt(p, s->defer, was_a_statement))
- return false;
+ return false;
if (!*was_a_statement) {
- err_print(token_location(p->file, deferred_start), "Empty defer");
+ err_print(token_location(p->file, deferred_start), "Invalid defer (are you missing a statement?).");
return false;
}
break;
@@ -2505,6 +2501,36 @@ static Status parse_stmt(Parser *p, Statement *s, bool *was_a_statement) {
}
break;
}
+ case DIRECT_INIT: {
+ *was_a_statement = false;
+ Initialization *init = parser_arr_add_ptr(p, p->parsed_file->inits);
+ ++t->token;
+ if (!token_is_kw(t->token, KW_LPAREN)) {
+ tokr_err(t, "Expected ( after #init.");
+ tokr_skip_semicolon(t);
+ return false;
+ }
+ ++t->token;
+ if (!parse_expr(p, &init->priority_expr, expr_find_end(p, 0))) {
+ tokr_skip_semicolon(t);
+ return false;
+ }
+ 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))
+ return false;
+ if (!was_stmt) {
+ err_print(token_location(p->file, stmt_start), "Invalid #init (are you missing a statement?).");
+ tokr_skip_semicolon(t);
+ return false;
+ }
+ } break;
default:
goto stmt_expr;
}
@@ -2544,6 +2570,7 @@ static void parser_create(Parser *p, Identifiers *globals, Tokenizer *t, Allocat
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/test.toc b/test.toc
index ec5fe91..7697c24 100644
--- a/test.toc
+++ b/test.toc
@@ -1,15 +1,16 @@
+#include "tests/std/io.toc";
-#include "std/io.toc", io;
-#include "std/base.toc", base;
-
-sleep ::= #foreign("sleep", base.libc) fn(#C unsigned) #C unsigned;
+Foo ::= struct {
+ x : int;
+ N ::= 5;
+ S ::= "hello!";
+ f ::= fn() []char {
+ S
+ }
+}
main ::= fn() {
- io.io_init();
- io.file_writes(&io.std_out, "hello!\n");
- io.file_writes(&io.std_out, "goodbye!\n");
- io.file_writes(&io.std_err, "ERROR: ");
- sleep(1);
- io.file_writes(&io.std_err, "something went wrong!\n");
+ puts(Foo.f());
+
}
main();
diff --git a/types.c b/types.c
index 42267bd..51128f6 100644
--- a/types.c
+++ b/types.c
@@ -787,8 +787,8 @@ top:;
if (!(d->flags & DECL_IS_CONST)) {
/* check for trying to capture a variable into a function */
bool captured = false;
- if (ident_scope(i) != NULL && ident_scope(i)->kind != BLOCK_NMS) {
- Block *decl_scope = ident_scope(i);
+ Block *decl_scope = ident_scope(i);
+ if (decl_scope && decl_scope->kind != BLOCK_NMS) {
if (decl_scope->kind != BLOCK_NMS) {
/* go back through scopes */
arr_foreach_reversed(tr->blocks, BlockPtr, block) {
@@ -2080,6 +2080,21 @@ static Status types_expr(Typer *tr, Expression *e) {
free(s);
return false;
}
+ if (b && b->kind == BLOCK_STRUCT) {
+ /* this is really necessary if you're trying to access a struct constant from inside a function in the same struct */
+ e->kind = EXPR_VAL;
+ Declaration *decl = final_ident->decl;
+ if (!(decl->flags & DECL_IS_CONST)) {
+ /* not sure if this can even happen right now, but might as well have this check here */
+ err_print(e->where, "Trying to access non-constant struct member from inside of it. This is not allowed.");
+ return false;
+ }
+ assert(decl->flags & DECL_FOUND_VAL);
+ int idx = decl_ident_index(decl, final_ident);
+ e->val = *decl_val_at_index(decl, idx);
+ e->type = *decl_type_at_index(decl, idx);
+ break;
+ }
e->ident = final_ident;
if (!type_of_ident(tr, e->where, e->ident, t)) {
return false;
@@ -3396,14 +3411,8 @@ static Status types_decl(Typer *tr, Declaration *d) {
Type *dtype = &d->type;
if (d->flags & DECL_FOUND_TYPE) return true;
bool success = true;
+ Expression *e = (d->flags & DECL_HAS_EXPR) ? &d->expr : NULL;
- if ((d->flags & DECL_HAS_EXPR)
- && d->expr.kind == EXPR_TYPE
- && d->expr.typeval->kind == TYPE_STRUCT
- && tr->fn == NULL) {
- d->expr.typeval->struc->name = d->idents[0];
- }
-
if (d->flags & DECL_INFER) {
dtype->kind = TYPE_UNKNOWN;
dtype->flags = 0;
@@ -3420,36 +3429,45 @@ static Status types_decl(Typer *tr, Declaration *d) {
size_t n_idents; n_idents = arr_len(d->idents);
- if (d->flags & DECL_HAS_EXPR) {
- if (!types_expr(tr, &d->expr)) {
+ if (e) {
+ if (e->kind == EXPR_FN && tr->block && tr->block->kind == BLOCK_STRUCT) {
+ warn_print(d->where, "This function is in the body of a struct. Are you trying to declare a method, because they don't exist in this language.\n"
+ "Try moving the function outside of the struct, otherwise you might run into problems.");
+ }
+ if (e->kind == EXPR_TYPE
+ && e->typeval->kind == TYPE_STRUCT
+ && tr->fn == NULL) {
+ e->typeval->struc->name = d->idents[0];
+ }
+ if (!types_expr(tr, e)) {
success = false;
goto ret;
}
assert(d->expr.type.flags & TYPE_IS_RESOLVED);
if (d->flags & DECL_ANNOTATES_TYPE) {
- if (!type_eq_implicit(&d->expr.type, dtype)) {
+ if (!type_eq_implicit(&e->type, dtype)) {
char *decl_type = type_to_str(dtype),
- *expr_type = type_to_str(&d->expr.type);
- err_print(d->expr.where, "Declaration type %s does not match expression type %s.", decl_type, expr_type);
+ *expr_type = type_to_str(&e->type);
+ err_print(e->where, "Declaration type %s does not match expression type %s.", decl_type, expr_type);
free(decl_type); free(expr_type);
success = false;
goto ret;
}
} else {
- if (type_is_void(&d->expr.type)) {
+ if (type_is_void(&e->type)) {
/* e.g. x := (fn(){})(); */
- err_print(d->expr.where, "Use of void value.");
+ err_print(e->where, "Use of void value.");
success = false;
goto ret;
}
- *dtype = d->expr.type;
+ *dtype = e->type;
dtype->flags &= (TypeFlags)~(TypeFlags)TYPE_IS_FLEXIBLE; /* x := 5; => x is not flexible */
}
bool need_value = (d->flags & DECL_IS_CONST) || !tr->block || tr->block->kind == BLOCK_NMS;
if (need_value) {
if (!(d->flags & DECL_FOUND_VAL)) {
Value val;
- if (!eval_expr(tr->evalr, &d->expr, &val)) {
+ if (!eval_expr(tr->evalr, e, &val)) {
success = false;
goto ret;
}
@@ -3567,7 +3585,7 @@ static Status types_decl(Typer *tr, Declaration *d) {
return false;
}
- if (n_idents == 1 && (d->flags & DECL_HAS_EXPR) && d->expr.kind == EXPR_NMS) {
+ if (n_idents == 1 && e && e->kind == EXPR_NMS) {
bool is_at_top_level = true;
arr_foreach(tr->blocks, BlockPtr, b) {
if (*b && (*b)->kind != BLOCK_NMS) {
@@ -3916,6 +3934,10 @@ static Status types_stmt(Typer *tr, Statement *s) {
}
} break;
case STMT_DEFER:
+ if (tr->block == NULL) {
+ err_print(s->where, "You can't defer something at global scope.");
+ return false;
+ }
if (!types_stmt(tr, s->defer))
return false;
if (s->defer->kind == STMT_DEFER) {
diff --git a/types.h b/types.h
index 079242e..ce2fb09 100644
--- a/types.h
+++ b/types.h
@@ -249,12 +249,13 @@ typedef enum {
DIRECT_WARN,
DIRECT_INFO,
DIRECT_NO_WARN,
+ DIRECT_INIT,
DIRECT_COUNT
} Directive;
static const char *directives[DIRECT_COUNT] = {
"C", "sizeof", "alignof", "export", "foreign", "builtin", "include", "force", "if", "error", "warn",
- "info", "no_warn"
+ "info", "no_warn", "init"
};
typedef enum {
@@ -502,14 +503,16 @@ typedef enum {
BLOCK_FN,
BLOCK_NMS,
BLOCK_FOR,
- BLOCK_WHILE
+ BLOCK_WHILE,
+ BLOCK_STRUCT
} BlockKind;
typedef U8 BlockFlags;
typedef struct Block {
/* NOTE: make sure you check copy.c when you add something to this */
BlockFlags flags;
- BlockKind kind;
+ BlockKind kind; /* set during the parsing phase, but don't access while this specific block is being
+ parsed, because sometimes it's set after parse_block */
struct {
IdentID break_lbl, cont_lbl; /* initially 0, set to non-zero values if needed (++g->lbl_counter); set by sdecls_cgen. */
} c;
@@ -527,9 +530,10 @@ enum {
STRUCT_DEF_FOUND_OFFSETS = 0x01,
STRUCT_DEF_CGEN_DECLARED = 0x02,
STRUCT_DEF_CGEN_DEFINED = 0x04,
- STRUCT_DEF_RESOLVED = 0x08,
- STRUCT_DEF_RESOLVING = 0x10,
- STRUCT_DEF_RESOLVING_FAILED = 0x20
+ STRUCT_DEF_CGEN_FN_DEFS = 0x08, /* have the functions contained in this struct been defined? */
+ STRUCT_DEF_RESOLVED = 0x10,
+ STRUCT_DEF_RESOLVING = 0x20,
+ STRUCT_DEF_RESOLVING_FAILED = 0x40
};
typedef U8 StructFlags;
typedef struct StructDef {
@@ -1004,16 +1008,19 @@ typedef struct Statement {
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)
+ 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 s;
- I64 priority;
+ Statement stmt;
+ union {
+ Expression priority_expr; /* before resolving */
+ I64 priority; /* after resolving */
+ };
} Initialization;
typedef struct ParsedFile {