diff options
| author | Kimplul <kimi.h.kuparinen@gmail.com> | 2024-02-18 23:05:50 +0200 |
|---|---|---|
| committer | Kimplul <kimi.h.kuparinen@gmail.com> | 2024-02-18 23:05:50 +0200 |
| commit | b8eb76ef8efb2b000cb53d4d92d0c0df509cbdb8 (patch) | |
| tree | e928c8b6a4117cc5d683f27350866b54d26aba12 | |
| parent | c62ce7a940a4d90e2b7bd8699bdfdb79f0331507 (diff) | |
| download | ek-b8eb76ef8efb2b000cb53d4d92d0c0df509cbdb8.tar.gz ek-b8eb76ef8efb2b000cb53d4d92d0c0df509cbdb8.zip | |
implement some basic generic features
| -rw-r--r-- | examples/std.ek | 32 | ||||
| -rw-r--r-- | include/ek/ast.h | 9 | ||||
| -rw-r--r-- | src/actualize.c | 586 | ||||
| -rw-r--r-- | src/ast.c | 90 | ||||
| -rw-r--r-- | src/compiler.c | 3 | ||||
| -rw-r--r-- | src/parser.y | 39 | ||||
| -rw-r--r-- | src/scope.c | 3 |
7 files changed, 526 insertions, 236 deletions
diff --git a/examples/std.ek b/examples/std.ek index 4404730..f9eb58c 100644 --- a/examples/std.ek +++ b/examples/std.ek @@ -1,32 +1,31 @@ /* comparison traits */ -pub typedef cmp { +pub define cmp[] { eq(*cmp a, *cmp b => bool); - cmp(*cmp a, *cmp b => bool); + lt(*cmp a, *cmp b => bool); + bt(*cmp a, *cmp b => bool); ne(*cmp a, *cmp b => bool) {return !a.eq(b)} - lt(*cmp a, *cmp b => bool) {return a.cmp(b) < 0} - bt(*cmp a, *cmp b => bool) {return a.cmp(b) > 0} le(*cmp a, *cmp b => bool) {return !a.bt(b)} ge(*cmp a, *cmp b => bool) {return !a.lt(b)} - } /* hash traits */ -pub typedef hash { +pub define hash[] { hash(*hash h => i27); } /* builtin type 'implementations' */ /* as a special case, builtin types are allowed to be typedef'd to implement * interfaces */ +pub typedef i27 {} pub typedef i9 { - fmt; + fmt![]; fmt(*i9 i, string args => result![string]) { return ok!("123".str()); } - cmp; + cmp![]; eq(*i9 i, *i9 o => bool) { return *i == *o; } @@ -35,23 +34,26 @@ pub typedef i9 { return *i - *o; } - hash; + hash![]; hash(*i9 i => i27) { return i; } } +pub typedef bool { +} + /* special case of special case, 'str' means *i9 but pointers aren't allowed in * the parser stage. Is this an ugly solution? Feels kind of ugly. */ pub typedef str { - fmt; + fmt![]; fmt(*i9 s, string args => result![string]) { /* here we should probably copy s in case it is statically * defined */ return {.len = 2, .buf = "cp"} as string; } - cmp; + cmp![]; eq(*i9 s, *i9 o => bool) { if s == o {return true;} /* iterate over stuff I guess */ @@ -62,7 +64,7 @@ pub typedef str { /* iterate over stuff */ } - hash; + hash![]; hash(*i9 s => i27) { /* iterate over all characters and hash them I guess */ } @@ -109,7 +111,7 @@ pub define err(e) { } /* fmt import */ -pub typedef fmt { +pub define fmt[] { fmt(*fmt p, string args => result![string]); str(*fmt p => string) { /* is this a loop? allowed? */ @@ -178,7 +180,7 @@ pub define fprint(f, fmt, ...args) { /* vec import */ pub typedef vec[any T] { // we want our vector to be formattable, used by print etc. - fmt; + fmt![]; fmt(*vec v, string args => result![string]) { return ok!("test".str()); } @@ -238,6 +240,6 @@ pub typedef vec[any T] { } main() { - vec![i8] what; + vec![i9] what; what.length(); } diff --git a/include/ek/ast.h b/include/ek/ast.h index 211a6a0..ab727eb 100644 --- a/include/ek/ast.h +++ b/include/ek/ast.h @@ -542,7 +542,7 @@ struct ast_return { }; enum ast_primitive { - AST_VOID, AST_I9, AST_I27 + AST_VOID, AST_BOOL, AST_I9, AST_I27, AST_STR }; /** @@ -579,6 +579,8 @@ struct ast_type { struct { enum ast_primitive type; + struct ast_node *def; // for possible user defined + // member functions } _primitive; /** Array type. */ @@ -1013,7 +1015,7 @@ struct ast_node *gen_switch(struct ast_node *cond, struct ast_node *cases, struc */ struct ast_node *gen_case(struct ast_node *expr, struct ast_node *body, struct src_loc loc); -struct ast_node *gen_primitive(enum ast_primitive type, struct src_loc loc); +struct ast_node *gen_primitive(enum ast_primitive type, struct ast_node *def, struct src_loc loc); /** * Generate Ek type (besides primitive). * @@ -1315,4 +1317,7 @@ int same_id(struct ast_node *id1, struct ast_node *id2); int equiv_nodes(struct ast_node *n1, struct ast_node *n2); int equiv_node_chains(struct ast_node *c1, struct ast_node *c2); +#define foreach_node(iter, nodes)\ + for (struct ast_node *iter = nodes; iter; iter = iter->next) + #endif /* AST_H */ diff --git a/src/actualize.c b/src/actualize.c index b64fa02..20ac5ef 100644 --- a/src/actualize.c +++ b/src/actualize.c @@ -18,6 +18,8 @@ #define UNUSED(x) do { (void)(x); } while (0) +static int replace_type_id(struct ast_node *nodes, struct ast_node *id, struct ast_node *replacement); + struct act_stack { struct ast_node *node; struct act_stack *next; @@ -77,15 +79,41 @@ static int is_void(struct ast_node *type) static struct ast_node *void_type() { - struct ast_node *v = gen_primitive(AST_VOID, NULL_LOC()); + struct ast_node *v = gen_primitive(AST_VOID, NULL, NULL_LOC()); ast_set_flags(v, AST_FLAG_INIT | AST_FLAG_ACTUAL); v->type = v; return v; } -static struct ast_node *i27_type() +/* very inefficient, should probably cache somewhere */ +static struct ast_node *i27_type(struct scope *scope) +{ + struct ast_node *i27 = gen_id("i27", NULL_LOC()); + struct ast_node *def = file_scope_find_type(scope, i27); + + struct ast_node *a = gen_primitive(AST_I27, def, def->loc); + ast_set_flags(a, AST_FLAG_INIT | AST_FLAG_ACTUAL); + a->type = a; + return a; +} + +static struct ast_node *str_type(struct scope *scope) { - struct ast_node *a = gen_primitive(AST_I27, NULL_LOC()); + struct ast_node *str = gen_id("str", NULL_LOC()); + struct ast_node *def = file_scope_find_type(scope, str); + + struct ast_node *a = gen_primitive(AST_STR, def, def->loc); + ast_set_flags(a, AST_FLAG_INIT | AST_FLAG_ACTUAL); + a->type = a; + return a; +} + +static struct ast_node *bool_type(struct scope *scope) +{ + struct ast_node *b = gen_id("bool", NULL_LOC()); + struct ast_node *def = file_scope_find_type(scope, b); + + struct ast_node *a = gen_primitive(AST_BOOL, def, def->loc); ast_set_flags(a, AST_FLAG_INIT | AST_FLAG_ACTUAL); a->type = a; return a; @@ -263,10 +291,8 @@ static int eval_const_if(struct scope *scope, struct ast_node *node) return 0; } -static int analyze_file_visibility(struct scope *scope, struct ast_node *node) +static int analyze_visibility(struct scope *scope, struct ast_node *node) { - assert(scope_flags(scope, SCOPE_FILE)); - if (!node) return 0; @@ -338,14 +364,6 @@ static int analyze_file_visibility(struct scope *scope, struct ast_node *node) break; } - case AST_BLOCK: { - /* a block might be inserted by something, in which case jump - * down into it */ - assert(ast_flags(node, AST_FLAG_UNHYGIENIC)); - ret = analyze(scope, AST_BLOCK(node).body); - break; - } - default: ret = -1; semantic_error(scope->fctx, node, "unknown top element\n"); @@ -355,16 +373,188 @@ static int analyze_file_visibility(struct scope *scope, struct ast_node *node) return ret; } +static int analyze_var(struct scope *scope, struct ast_node *node) +{ + struct act_state state = {0}; + return actualize(&state, scope, node); +} + +static int analyze_proc(struct scope *scope, struct ast_node *node) +{ + struct scope *proc_scope = create_scope(); + scope_add_scope(scope, proc_scope); + node->scope = proc_scope; + + struct ast_node *sign = AST_PROC(node).sign; + + struct act_state state = {0}; + int ret = actualize(&state, proc_scope, sign); + node->type = sign; + return ret; +} + +static struct ast_node *do_type_expand(struct scope *scope, struct ast_node *n) +{ + assert(n->node_type == AST_TYPE_EXPAND); + struct ast_node *trait = file_scope_find_type(scope, AST_TYPE_EXPAND(n).id); + if (!trait) { + semantic_error(scope->fctx, n, "no such type"); + return NULL; + } + + if (trait->node_type != AST_TRAIT) { + semantic_error(scope->fctx, n, "not a trait"); + return NULL; + } + + semantic_info(scope->fctx, n, "FIXME: skipping type param check for now"); + struct ast_node *body = AST_TRAIT(trait).body; + body = clone_ast_node(body); + + struct ast_node *pa = AST_TYPE_EXPAND(n).args; + foreach_node(pt, AST_TRAIT(trait).params) { + replace_type_id(body, AST_VAR(pt).id, pa); + pa = pa->next; + } + return body; +} + +static int analyze_struct(struct scope *scope, struct ast_node *node) +{ + assert(node->node_type == AST_STRUCT); + struct ast_node *generics = AST_STRUCT(node).generics; + struct scope *struct_scope = create_scope(); + if (!struct_scope) + return -1; + + scope_add_scope(node->scope, struct_scope); + node->scope = struct_scope; + if (generics) + ast_set_flags(node, AST_FLAG_GENERIC); + + foreach_node(n, AST_STRUCT(node).body) { + /* function prototypes are checked later */ + if (n->node_type == AST_PROC && !AST_PROC(n).body) + continue; + + if (n->node_type == AST_TYPE_EXPAND) { + struct ast_node *body = do_type_expand(scope, n); + if (!body) + return -1; + + replace_type_id(body, AST_TYPE_EXPAND(n).id, AST_STRUCT(node).id); + *n = *body; + continue; + } + + if (analyze_visibility(struct_scope, n)) + return -1; + } + + foreach_node(n, AST_STRUCT(node).body) { + /* also checks prototypes */ + if (n->node_type == AST_PROC) + continue; + + if (analyze_proc(struct_scope, n)) + return -1; + } + + /* check that all prototypes are implemented */ + foreach_node(n, AST_STRUCT(node).body) { + if (n->node_type != AST_PROC) + continue; + + if (!AST_PROC(n).body) + continue; + + struct ast_node *proc = scope_find_proc(struct_scope, AST_PROC(n).id); + if (!proc) { + semantic_error(scope->fctx, n, "missing implementation"); + return -1; + } + + if (!equiv_nodes(AST_PROC(n).sign, AST_PROC(proc).sign)) { + semantic_error(scope->fctx, n, "mismatched signatures"); + semantic_info(scope->fctx, proc, "note: here"); + return -1; + } + } + + + return 0; +} + +static int analyze_trait(struct scope *scope, struct ast_node *node) +{ + assert(node->node_type == AST_TRAIT); + struct ast_node *generics = AST_TRAIT(node).params; + struct scope *trait_scope = create_scope(); + if (!trait_scope) + return -1; + + scope_add_scope(node->scope, trait_scope); + node->scope = trait_scope; + if (generics) + ast_set_flags(node, AST_FLAG_GENERIC); + + foreach_node(n, AST_TRAIT(node).body) { + if (n->node_type == AST_TYPE_EXPAND) { + struct ast_node *body = do_type_expand(scope, n); + if (!body) + return -1; + + replace_type_id(body, AST_TYPE_EXPAND(n).id, AST_STRUCT(node).id); + *n = *body; + continue; + } + + if (analyze_visibility(trait_scope, n)) + return -1; + } + + foreach_node(n, AST_TRAIT(node).body) { + struct act_state state = {0}; + if (analyze_proc(trait_scope, n)) + return -1; + } + + return 0; +} + +static int analyze_signs(struct scope *scope, struct ast_node *node) +{ + switch (node->node_type) { + case AST_VAR: return analyze_var(scope, node); break; + case AST_PROC: return analyze_proc(scope, node); break; + case AST_STRUCT: return analyze_struct(scope, node); break; + case AST_TRAIT: return analyze_trait(scope, node); break; + default: + } + + return 0; +} + static int analyze(struct scope *scope, struct ast_node *tree) { struct ast_node *node = tree, *next; - while (node) { - next = node->next; - node->next = NULL; - if (analyze_file_visibility(scope, node)) + foreach_node(node, tree) { + if (analyze_visibility(scope, node)) return -1; + } + + foreach_node(node, tree) { + if (analyze_signs(scope, node)) + return -1; + } - node = next; + foreach_node(node, tree){ + struct act_state state = {0}; + if (actualize(&state, scope, node)) + return -1; + + printf("actualized:\n"); + dump_ast(0, node); } return 0; @@ -583,19 +773,18 @@ static int actualize_call(struct act_state *state, if (ret) return ret; - /** @todo check if call args and expr types match */ - struct ast_node *callable = file_scope_find_proc(scope, call); - if (!callable) { - char *str = call_str(call); - semantic_error(scope->fctx, call, "no such callable: %s", str); - free(str); + semantic_info(scope->fctx, call, "FIXME: skipping type checks for now"); + struct ast_node *expr = AST_CALL(call).expr; + if (AST_TYPE(expr->type).kind != AST_TYPE_SIGN) { + char *tstr = type_str(expr->type); + semantic_info(scope->fctx, call, "not a callable type: %s", tstr); + free(tstr); return -1; } - assert(callable->node_type == AST_PROC); - /** @todo we should probably start with just iterating over all procs and check - * if they're valid rather than generating them on 'demand' */ - return actualize_proc_call(state, scope, call, callable); + struct ast_node *sign = expr->type; + call->type = AST_SIGN_TYPE(sign).ret; + return 0; } static void warn_unused_labels(struct act_state *state, struct scope *scope) @@ -633,65 +822,49 @@ static int undefined_gotos(struct act_state *state, struct scope *scope) return ret; } -/* TODO: add in some kind of subsystem for keeping track of which procedure - * we're in, what the last defined var is, possibly more? */ static int actualize_proc(struct act_state *state, struct scope *scope, struct ast_node *proc) { /* actualize_proc is called on trait procs as well, but I believe * that's fine? */ assert(proc && proc->node_type == AST_PROC); - int ret = 0; - - struct ast_node *actual = proc; - struct ast_node *sign = AST_PROC(actual).sign; - struct scope *param_scope = create_scope(); - scope_add_scope(scope, param_scope); + struct ast_node *sign = AST_PROC(proc).sign; - /* procedure type is the signature */ - sign->scope = param_scope; - - /* actualize types in signature */ - if (actualize(state, param_scope, sign)) + /* signature should already be typed etc. */ + struct act_state new_state = {0}; + if (actualize(&new_state, proc->scope, sign)) return -1; - actual->type = sign; + proc->type = sign; - /* we don't have to maintain the same flags as the parent state, I don't - * think */ - struct act_state new_state = {0}; - new_state.cur_proc = actual; /* actualize body */ - ret |= actualize(&new_state, sign->scope, AST_PROC(actual).body); + new_state.cur_proc = proc; + if (actualize(&new_state, proc->scope, AST_PROC(proc).body)) + return -1; + if (!act_flags(&new_state, ACT_HAS_RETURN)) { if (!is_void(AST_SIGN_TYPE(sign).ret)) { - semantic_error(scope->fctx, actual, + semantic_error(scope->fctx, proc, "no return with non-void return type"); - ret = -1; + return -1; } + /* add 'implicit' return */ - struct ast_node *body = AST_PROC(actual).body; + struct ast_node *body = AST_PROC(proc).body; struct ast_node *r = gen_return(NULL, NULL_LOC()); r->scope = body->scope; ast_append(AST_BLOCK(body).body, r); } - else if (ast_block_last(actual->_proc.body)->node_type != AST_RETURN) { + else if (ast_block_last(AST_PROC(proc).body)->node_type != AST_RETURN) { /* TODO: something more sophisticated than this */ - semantic_warn(scope->fctx, actual, + semantic_warn(scope->fctx, proc, "unable to determine explicit return for all branches"); + return -1; } warn_unused_labels(&new_state, scope); if (undefined_gotos(&new_state, scope)) - ret = -1; - - /* TODO: should ret be checked between each stage? */ - if (ret) - return ret; - - printf("Actualized func:\n"); - dump_ast(0, actual); - scope_add_actual(scope, actual); + return -1; /* we have successfully actualized the procedure */ return 0; @@ -856,7 +1029,7 @@ static int actualize_var(struct act_state *state, /* an unnamed var is a var in a signature that should not produce a * warning on not being used (if I ever get around to adding those kinds * of warnings) */ - if (var->_var.id && !ast_flags(var, AST_FLAG_MEMBER)) + if (AST_VAR(var).id && !ast_flags(var, AST_FLAG_MEMBER)) return scope_add_var(scope, var); /* TODO: we should make sure the type is fully qualified in bodies */ @@ -874,6 +1047,44 @@ static int actualize_var(struct act_state *state, return r; \ } while (0); +static enum ast_primitive id_to_primitive(struct ast_node *id) +{ + const char *name = AST_ID(id).id; + if (strcmp(name, "i27") == 0) + return AST_I27; + + if (strcmp(name, "i9") == 0) + return AST_I9; + + if (strcmp(name, "bool") == 0) + return AST_BOOL; + + /** @todo some better error reporting should probably be used */ + internal_error( "illegal primitive: %s\n", name); + return AST_VOID; +} + +static int struct_is_primitive(struct ast_node *s) +{ + struct ast_node *id = AST_STRUCT(s).id; + const char *name = AST_ID(id).id; + if (strcmp(name, "i27") == 0) + return 1; + + if (strcmp(name, "i9") == 0) + return 1; + + if (strcmp(name, "bool") == 0) + return 1; + + /* special case of a special case? + if (strcmp(name, "str")) + return 1; + */ + + return 0; +} + static int actualize_type(struct act_state *state, struct scope *scope, struct ast_node *type) { @@ -898,8 +1109,8 @@ static int actualize_type(struct act_state *state, gen_id(strdup("void"), NULL_LOC()); } - struct ast_node *exists = file_scope_find_type(scope, AST_ID_TYPE( - type).id); + struct ast_node *exists = file_scope_find_type(scope, + AST_ID_TYPE(type).id); if (!exists) { semantic_error(scope->fctx, type, "no such type"); EXIT_ACT(-1); @@ -911,8 +1122,10 @@ static int actualize_type(struct act_state *state, break; } + /* if (actualize(state, exists->scope, exists)) EXIT_ACT(-1); + */ assert(AST_TYPE(type).next == NULL); if (exists->node_type == AST_ALIAS) { @@ -921,13 +1134,23 @@ static int actualize_type(struct act_state *state, } else if (exists->node_type == AST_TRAIT) { /* this is kind of weird, have to think about it */ - AST_TYPE(type) = AST_TYPE(exists); + *type = *gen_type(AST_TYPE_TRAIT, exists, + NULL, exists->loc); } else if (exists->node_type == AST_STRUCT) { - AST_TYPE(type) = AST_TYPE(exists); + if (struct_is_primitive(exists)) { + *type = *gen_primitive( + id_to_primitive(AST_STRUCT(exists).id), + exists, + exists->loc); + } else { + *type = *gen_type(AST_TYPE_STRUCT, exists, + NULL, exists->loc); + } } else if (exists->node_type == AST_ENUM) { - AST_TYPE(type) = AST_TYPE(exists); + *type = *gen_type(AST_TYPE_ENUM, exists, + NULL, exists->loc); } break; @@ -954,8 +1177,10 @@ static int actualize_type(struct act_state *state, struct ast_node *params = AST_SIGN_TYPE(type).params; struct ast_node *ret = AST_SIGN_TYPE(type).ret; - if (actualize(state, scope, params)) - EXIT_ACT(-1); + foreach_node(p, params){ + if (actualize(state, scope, p)) + EXIT_ACT(-1); + } if (!ret) ret = AST_SIGN_TYPE(type).ret = void_type(); @@ -966,13 +1191,21 @@ static int actualize_type(struct act_state *state, break; } + case AST_TYPE_TRAIT: { + assert(ast_flags(type, AST_FLAG_ACTUAL)); + semantic_info(scope->fctx, type, "FIXME skipping trait type checks"); + break; + } + case AST_TYPE_STRUCT: { assert(ast_flags(type, AST_FLAG_ACTUAL)); + semantic_info(scope->fctx, type, "FIXME skipping struct type checks"); break; } case AST_TYPE_PRIMITIVE: { assert(ast_flags(type, AST_FLAG_ACTUAL)); + semantic_info(scope->fctx, type, "FIXME skipping primitive type checks"); break; } @@ -1299,9 +1532,14 @@ static int actualize_const(struct act_state *state, struct scope *scope, { UNUSED(state); assert(cons->node_type == AST_CONST); - if (cons->_const.kind == AST_CONST_INTEGER) { + if (AST_CONST(cons).kind == AST_CONST_INTEGER) { /* error checking would be doog */ - cons->type = i27_type(); + cons->type = i27_type(scope); + return 0; + } + + if (AST_CONST(cons).kind == AST_CONST_STRING) { + cons->type = str_type(scope); return 0; } @@ -1328,35 +1566,6 @@ static int actualize_alias(struct act_state *state, struct scope *scope, return 0; } -static int actualize_trait(struct act_state *state, struct scope *scope, - struct ast_node *trait) -{ - assert(trait->node_type == AST_TRAIT); - ast_set_flags(trait, AST_FLAG_ACTUAL); - ENTER_ACT(); - state->cur_trait = trait; - - /* will still have to figure out how I want to inform the actualizer - * that a some_type in a some_type just means whichever type we're - * testing for */ - if (actualize(state, trait->scope, trait->_trait.body)) { - semantic_error(scope->fctx, trait, - "failed actualizing trait"); - EXIT_ACT(-1); - } - - /* TODO: check that all procs in the trait have something to do with - * the type, either as an argument or as a return type or something */ - - /* TODO: implement supertraits, i.e. adding some previous trait to - * this trait */ - - /* TODO: make sure that the trait itself doesn't have duplicates */ - - trait->type = trait; - EXIT_ACT(0); -} - static int actualize_defer(struct act_state *state, struct scope *scope, struct ast_node *node) { @@ -1396,15 +1605,17 @@ static int actualize_return(struct act_state *state, struct scope *scope, char *rt = type_str(ret); char *et = type_str(node); semantic_error(scope->fctx, node, - "return type mismatch: %s vs %s", - rt, et); + "return type mismatch: %s", et); + + semantic_info(scope->fctx, ret, + "vs %s", rt); free(rt); free(et); return -1; } if (state->defer_stack) { - node->_return.defers = clone_defers(state, NULL); + AST_RETURN(node).defers = clone_defers(state, NULL); if (!node->_return.defers) { internal_error("failed cloning return defers"); return -1; @@ -1520,7 +1731,7 @@ static int actualize_unop(struct act_state *state, struct scope *scope, struct ast_node *node) { assert(node->node_type == AST_UNOP); - struct ast_node *expr = node->_unop.expr; + struct ast_node *expr = AST_UNOP(node).expr; if (actualize(state, scope, expr)) return -1; @@ -1549,6 +1760,21 @@ static int actualize_unop(struct act_state *state, break; } + case AST_LNOT: { + if (AST_TYPE(expr->type).kind != AST_TYPE_PRIMITIVE) { + semantic_error(scope->fctx, node, "'!' only implemented for primitive types"); + return -1; + } + + if (AST_PRIMITIVE_TYPE(expr->type).type == AST_VOID) { + semantic_error(scope->fctx, node, "'!' not implemented for void"); + return -1; + } + + node->type = bool_type(scope); + break; + } + default: semantic_error(scope->fctx, node, "unimplemented unary operation"); return -1; @@ -1569,33 +1795,75 @@ static int actualize_as(struct act_state *state, return 0; } -static int actualize_struct(struct act_state *state, - struct scope *scope, struct ast_node *node) +static int _replace_type_id(struct ast_node *node, void *data) { - assert(node->node_type == AST_STRUCT); - struct ast_node *generics = node->_struct.generics; - struct scope *struct_scope = create_scope(); - if (!struct_scope) - return -1; + if (!node) + return 0; - scope_add_scope(node->scope, struct_scope); - if (generics) - ast_set_flags(node, AST_FLAG_GENERIC); + struct ast_node **pair = data; + struct ast_node *id = pair[0]; + struct ast_node *replacement = pair[1]; - /* TODO: some IDs should be handles as just placeholders, I think? */ - if (actualize(state, struct_scope, generics)) - return -1; + switch (node->node_type) { + case AST_VAR: replace_id(AST_VAR(node).type, id, replacement); break; + /* I think this might also change some extra stuff we might not want, + * hmm */ + case AST_TYPE: replace_id(node, id, replacement); break; + default: + } - struct ast_node *body = node->_struct.body; - if (actualize(state, struct_scope, body)) - return -1; + return ast_call_on(_replace_type_id, node, data); +} - /* cloning slightly odd, but I guess it's fine? */ - struct ast_node *clone_id = clone_ast_node(node->_struct.id); - node->type = - gen_type(AST_TYPE_STRUCT, clone_id, NULL, NULL_LOC()); +static int replace_type_id(struct ast_node *nodes, struct ast_node *id, struct ast_node *replacement) +{ + struct ast_node *pair[2] = {id, replacement}; + return ast_call_on(_replace_type_id, nodes, pair); +} + + +/* lots of overlap with actualize_struct, kind of ugly... */ +static int actualize_trait(struct act_state *state, struct scope *scope, + struct ast_node *node) +{ + assert(node->node_type == AST_TRAIT); + foreach_node(n, AST_TRAIT(node).body) { + /* there's really only prodcedure body actualization left I + * guess, as type stuff was taken care of in the analysis phase + * */ + if (n->node_type != AST_PROC) + continue; + + /* don't actualize prototypes, duh */ + if (!AST_PROC(n).body) + continue; + + if (actualize(state, node->scope, n)) + return -1; + } + + return 0; +} + +static int actualize_struct(struct act_state *state, + struct scope *scope, struct ast_node *node) +{ + assert(node->node_type == AST_TRAIT); + foreach_node(n, AST_STRUCT(node).body) { + /* there's really only prodcedure body actualization left I + * guess, as type stuff was taken care of in the analysis phase + * */ + if (n->node_type != AST_PROC) + continue; + + /* don't actualize prototypes, duh */ + if (!AST_PROC(n).body) + continue; + + if (actualize(state, node->scope, n)) + return -1; + } - ast_set_flags(node, AST_FLAG_ACTUAL); return 0; } @@ -1604,9 +1872,6 @@ static int actualize_struct(struct act_state *state, * structures or traits. */ static int has_members(struct ast_node *type) { - if (AST_TYPE(type).kind == AST_TYPE_POINTER) - type = AST_TYPE(type).next; - /* most likely */ if (AST_TYPE(type).kind == AST_TYPE_STRUCT) return 1; @@ -1632,21 +1897,47 @@ static int actualize_dot(struct act_state *state, * the corresponding constant? **/ assert(node->node_type == AST_DOT); - struct ast_node *expr = node->_dot.expr; + struct ast_node *expr = AST_DOT(node).expr; if (actualize(state, scope, expr)) return -1; struct ast_node *id = AST_DOT(node).id; - if (!has_members(expr->type)) { - char *tstr = type_str(expr->type); - semantic_error(scope->fctx, node, "%s does not have member %s", - tstr, id->_id.id); + struct ast_node *type = expr->type; + if (AST_TYPE(type).kind == AST_TYPE_POINTER) + type = AST_PTR_TYPE(type).base; + + struct ast_node *def = NULL; + switch (AST_TYPE(type).kind) { + case AST_TYPE_PRIMITIVE: def = AST_PRIMITIVE_TYPE(type).def; break; + case AST_TYPE_TRAIT: def = AST_TRAIT_TYPE(type).def; break; + case AST_TYPE_STRUCT: def = AST_STRUCT_TYPE(type).def; break; + default: { + char *tstr = type_str(type); + semantic_error(scope->fctx, node, + "illegal type in dot expression: %s", + tstr); free(tstr); - return -1; + return -1; + } } - /* TODO: actually look through stuff */ - return 0; + struct ast_node *exists = scope_find_var(def->scope, id); + if (exists) { + assert(exists->type); + node->type = exists->type; + return 0; + } + + exists = scope_find_proc(def->scope, id); + if (exists) { + assert(exists->type); + node->type = exists->type; + return 0; + } + + semantic_error(scope->fctx, node, + "does not have member"); + return -1; } static int actualize_init(struct act_state *state, @@ -1740,7 +2031,7 @@ static int actualize_enum(struct act_state *state, struct scope *scope, /* TODO: here we could save space by choosing the smallest type that * fits */ if (!type) { - type = i27_type(); + type = i27_type(scope); node->_enum.type = type; } else if (actualize(state, enum_scope, type)) return -1; @@ -1791,19 +2082,19 @@ static int actualize(struct act_state *state, struct scope *scope, if (!node->scope) node->scope = scope; - if (ast_flags(node, - AST_FLAG_INIT) && !ast_flags(node, AST_FLAG_ACTUAL)) { - semantic_error(scope->fctx, node, "detected dependency loop"); - return -1; + /* this node was already actualized, presumed to be fine */ + if (ast_flags(node, AST_FLAG_ACTUAL)) { + return 0; } /* actualization started */ ast_set_flags(node, AST_FLAG_INIT); + /* actualization done for this node (optimistic) */ + ast_set_flags(node, AST_FLAG_ACTUAL); + switch (node->node_type) { case AST_PROC: - /* procedure definitions only allowed at file scope */ - assert(scope_flags(scope, SCOPE_FILE)); ret |= actualize_proc(state, scope, node); break; @@ -1841,13 +2132,6 @@ static int actualize(struct act_state *state, struct scope *scope, "unimplemented actualization"); break; } - - /* actualization done for this node */ - ast_set_flags(node, AST_FLAG_ACTUAL); - - /* should it automatically handle next nodes or should it be saved to - * actualize_block or something? */ - ret |= actualize(state, scope, node->next); return ret; } @@ -334,12 +334,13 @@ struct ast_node *gen_case(struct ast_node *cond, struct ast_node *body, struct s return n; } -struct ast_node *gen_primitive(enum ast_primitive type, struct src_loc loc) +struct ast_node *gen_primitive(enum ast_primitive type, struct ast_node *def, struct src_loc loc) { ALLOC_NODE(n, "primitive"); n->node_type = AST_TYPE; AST_TYPE(n).kind = AST_TYPE_PRIMITIVE; AST_PRIMITIVE_TYPE(n).type = type; + AST_PRIMITIVE_TYPE(n).def = def; n->loc = loc; return n; } @@ -666,6 +667,7 @@ const char *primitive_str(enum ast_primitive type) case AST_VOID: return "void"; case AST_I9: return "i9"; case AST_I27: return "i27"; + case AST_BOOL: return "bool"; default: return "unimp"; } @@ -896,11 +898,20 @@ static void __dump_ast(int depth, struct ast_node *node) primitive_str(AST_PRIMITIVE_TYPE(node).type)); break; - case AST_TYPE_TRAIT: + case AST_TYPE_TRAIT: { printf(" TRAIT\n"); - /* might be a bit overkill? */ - dump_ast(depth + 1, AST_TRAIT_TYPE(node).def); + struct ast_node *def = AST_TRAIT_TYPE(node).def; + /* this should be enough, avoid print loops */ + dump_ast(depth + 1, AST_TRAIT(def).id); break; + } + + case AST_TYPE_CONSTRUCT: { + printf(" CONSTRUCT\n"); + dump_ast(depth + 1, AST_CONSTRUCT_TYPE(node).id); + dump_ast(depth + 1, AST_CONSTRUCT_TYPE(node).args); + break; + } case AST_TYPE_ID: printf(" ID\n"); @@ -918,17 +929,21 @@ static void __dump_ast(int depth, struct ast_node *node) dump_ast(depth + 1, AST_PTR_TYPE(node).base); break; - case AST_TYPE_STRUCT: + case AST_TYPE_STRUCT: { printf(" STRUCT\n"); + struct ast_node *def = AST_STRUCT_TYPE(node).def; /* oh yeah, struc is at least right now just an ID that * we can use to fetch the actual struct with. */ - dump_ast(depth + 1, AST_STRUCT_TYPE(node).def); + dump_ast(depth + 1, AST_STRUCT(def).id); break; + } - case AST_TYPE_ENUM: + case AST_TYPE_ENUM: { printf(" ENUM\n"); - dump_ast(depth + 1, AST_ENUM_TYPE(node).def); + struct ast_node *def = AST_ENUM_TYPE(node).def; + dump_ast(depth + 1, AST_ENUM(def).id); break; + } case AST_TYPE_SIGN: printf(" SIGN\n"); @@ -1243,8 +1258,8 @@ struct ast_node *clone_ast_node(struct ast_node *node) * correctly... */ switch (node->_type.kind) { case AST_TYPE_PRIMITIVE: - new = gen_primitive(AST_PRIMITIVE_TYPE( - node).type, node->loc); + new = gen_primitive(AST_PRIMITIVE_TYPE(node).type, + AST_PRIMITIVE_TYPE(node).def, node->loc); break; case AST_TYPE_TRAIT: @@ -1254,6 +1269,13 @@ struct ast_node *clone_ast_node(struct ast_node *node) node->loc); break; + case AST_TYPE_CONSTRUCT: + new = gen_type(AST_TYPE_CONSTRUCT, + clone_ast_node(AST_CONSTRUCT_TYPE(node).id), + clone_ast_node(AST_CONSTRUCT_TYPE(node).args), + node->loc); + break; + case AST_TYPE_ID: new = gen_type(AST_TYPE_ID, clone_ast_node(AST_ID_TYPE(node).id), @@ -1269,20 +1291,21 @@ struct ast_node *clone_ast_node(struct ast_node *node) break; case AST_TYPE_POINTER: - new = gen_type(AST_TYPE_POINTER, AST_PTR_TYPE(node).base, - NULL, - node->loc); + new = gen_type(AST_TYPE_POINTER, + AST_PTR_TYPE(node).base, + NULL, + node->loc); break; case AST_TYPE_STRUCT: new = gen_type(AST_TYPE_STRUCT, - clone_ast_node(AST_STRUCT_TYPE(node).def), + AST_STRUCT_TYPE(node).def, NULL, node->loc); break; case AST_TYPE_ENUM: new = gen_type(AST_TYPE_ENUM, - clone_ast_node(AST_ENUM_TYPE(node).def), + AST_ENUM_TYPE(node).def, NULL, node->loc); break; @@ -1567,14 +1590,6 @@ static int call_on_case(int (*call)(struct ast_node *, return ret; } -static int call_on_type_trait(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - int ret = 0; - ret |= call(AST_TRAIT_TYPE(node).def, data); - return ret; -} - static int call_on_type_id(int (*call)(struct ast_node *, void *), struct ast_node *node, void *data) { @@ -1587,14 +1602,6 @@ static int call_on_type_arr(int (*call)(struct ast_node *, return call(AST_ARR_TYPE(node).size, data); } -static int call_on_type_struct(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - int ret = 0; - ret |= call(AST_STRUCT_TYPE(node).def, data); - return ret; -} - static int call_on_type_sign(int (*call)(struct ast_node *, void *), struct ast_node *node, void *data) { @@ -1604,26 +1611,27 @@ static int call_on_type_sign(int (*call)(struct ast_node *, return ret; } -static int call_on_type_enum(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) +static int call_on_type_construct(int (*call)(struct ast_node *, void *), + struct ast_node *node, void *data) { int ret = 0; - ret |= call(AST_ENUM_TYPE(node).def, data); + ret |= call(AST_CONSTRUCT_TYPE(node).id, data); + ret |= call(AST_CONSTRUCT_TYPE(node).args, data); return ret; } -static int call_on_type(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) +static int call_on_type(int (*call)(struct ast_node *, void *), + struct ast_node *node, void *data) { int ret = 0; - switch (node->_type.kind) { - case AST_TYPE_ENUM: ret = call_on_type_enum(call, node, data); break; - case AST_TYPE_TRAIT: ret = call_on_type_trait(call, node, data); break; + switch (AST_TYPE(node).kind) { + case AST_TYPE_ENUM: break; + case AST_TYPE_TRAIT: break; case AST_TYPE_ID: ret = call_on_type_id(call, node, data); break; case AST_TYPE_ARR: ret = call_on_type_arr(call, node, data); break; - case AST_TYPE_STRUCT: ret = call_on_type_struct(call, node, data); - break; + case AST_TYPE_STRUCT: break; case AST_TYPE_SIGN: ret = call_on_type_sign(call, node, data); break; + case AST_TYPE_CONSTRUCT: ret = call_on_type_construct(call, node, data); break; case AST_TYPE_POINTER: break; case AST_TYPE_PRIMITIVE: break; } diff --git a/src/compiler.c b/src/compiler.c index 5845283..76b420a 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -168,16 +168,19 @@ int compile(const char *input, const char *output) { struct scope *root = NULL; if (process_file(&root, 0, input)) { destroy_scope(root); + destroy_ast_nodes(); error("compilation of %s stopped due to errors", input); return ret; } + /* if ((ret = actualize_main(root))) { destroy_scope(root); destroy_ast_nodes(); error("compilation of %s stopped due to errors", input); return ret; } + */ if ((ret = lower_ops(root, output))) { destroy_scope(root); diff --git a/src/parser.y b/src/parser.y index 18756b3..ddddcb8 100644 --- a/src/parser.y +++ b/src/parser.y @@ -133,7 +133,7 @@ %nterm <node> construct construct_args construct_arg %nterm <node> statelet apply types -%nterm <node> tagged_struct anon_struct tagged_union anon_union +%nterm <node> tagged_struct /* constant operations */ %nterm <node> const_expr const_unop const_binop @@ -147,7 +147,7 @@ /* optional stuff */ %nterm <node> opt_args opt_exprs proc_decl member opt_members -%nterm <node> opt_statements opt_types +%nterm <node> opt_statements opt_types opt_type_params %{ @@ -438,7 +438,6 @@ statement | do_while | body | tagged_struct - | tagged_union | for | defer | if @@ -596,8 +595,6 @@ type | "mut" type { $$ = $2; ast_set_flags($$, AST_FLAG_MUTABLE); } - | anon_struct - | anon_union | apply "[" opt_types "]" { $$ = gen_type(AST_TYPE_CONSTRUCT, $1, $3, src_loc(@$)); } @@ -644,7 +641,6 @@ member : var_decl ";" | type_expand ";" | proc_decl ";" - | id ";" | proc ; @@ -656,30 +652,17 @@ opt_members : members | {$$ = NULL;} -tagged_union - : "union" id "{" opt_members "}" { - /* essentially struct {union{members}} */ - $$ = gen_struct($2, NULL, $4, src_loc(@$)); - } - -anon_union - : "union" "{" opt_members "}" { - $$ = gen_struct(NULL, NULL, $3, src_loc(@$)); - } - macro_expand : apply "(" opt_args ")" { $$ = gen_macro_expand($1, $3, src_loc(@$)); } tagged_struct - : "typedef" id "{" opt_members "}" { - $$ = gen_struct($2, NULL, $4, src_loc(@$)); + : "typedef" id "[" opt_type_params "]" "{" opt_members "}" { + $$ = gen_struct($2, $4, $7, src_loc(@$)); } - -anon_struct - : "typedef" "{" opt_members "}" { - $$ = gen_struct(NULL, NULL, $3, src_loc(@$)); + | "typedef" id "{" opt_members "}" { + $$ = gen_struct($2, NULL, $4, src_loc(@$)); } alias @@ -688,7 +671,7 @@ alias } type_param - : type id { + : id id { $$ = gen_var($2, $1, NULL, src_loc(@$)); } @@ -696,8 +679,12 @@ type_params : type_param "," type_params { $$ = $1; $1->next = $3; } | type_param +opt_type_params + : type_params + | { $$ = NULL; } + trait - : "typedef" id "[" type_params "]" "{" opt_members "}" { + : "define" id "[" opt_type_params "]" "{" opt_members "}" { $$ = gen_trait($2, $4, $7, src_loc(@$)); } @@ -743,7 +730,6 @@ top : enum | proc | tagged_struct - | tagged_union | macro { $$ = $1; } | top_if { $$ = $1; ast_set_flags($$, AST_FLAG_CONST); } | import { $$ = $1; } @@ -751,7 +737,6 @@ top | trait { $$ = $1; } | "pub" enum { $$ = $2; ast_set_flags($2, AST_FLAG_PUBLIC); } | "pub" tagged_struct { $$ = $2; ast_set_flags($2, AST_FLAG_PUBLIC); } - | "pub" tagged_union { $$ = $2; ast_set_flags($2, AST_FLAG_PUBLIC); } | "pub" proc { $$ = $2; ast_set_flags($2, AST_FLAG_PUBLIC); } | "pub" macro { $$ = $2; ast_set_flags($2, AST_FLAG_PUBLIC); } | "pub" import { $$ = $2; ast_set_flags($2, AST_FLAG_PUBLIC); } diff --git a/src/scope.c b/src/scope.c index b5729cd..48d3bc5 100644 --- a/src/scope.c +++ b/src/scope.c @@ -54,6 +54,8 @@ void destroy_scope(struct scope *scope) } destroy_visible(scope, scope->vars); + destroy_visible(scope, scope->procs); + destroy_visible(scope, scope->macros); destroy_visible(scope, scope->types); struct scope *prev = scope->children, *cur; @@ -204,6 +206,7 @@ int scope_add_proc(struct scope *scope, struct ast_node *proc) semantic_info(scope->fctx, exists, "previously here"); return -1; } + /* always add to scope, do resolve checking later */ create_proc(scope, AST_PROC(proc).id, proc); if (scope->parent && |
