From e5f9d49e39210fe634305d57f1b01e013d66aa71 Mon Sep 17 00:00:00 2001 From: Kimplul Date: Tue, 9 Apr 2024 02:50:34 +0300 Subject: simplify ast definition + Makes it a lot nicer to work with. --- src/actualize.c | 1865 +++++++++++++++++++++++--------------------------- src/ast.c | 2021 +++++++++++-------------------------------------------- src/compiler.c | 12 +- src/debug.c | 133 ++-- src/lexer.l | 36 +- src/lower.c | 365 +++++----- src/parser.y | 265 +++++--- src/scope.c | 118 ++-- 8 files changed, 1717 insertions(+), 3098 deletions(-) (limited to 'src') diff --git a/src/actualize.c b/src/actualize.c index 744dc1a..6d85640 100644 --- a/src/actualize.c +++ b/src/actualize.c @@ -18,11 +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 ast *node; struct act_stack *next; }; @@ -35,23 +32,36 @@ enum act_flags { struct act_state { enum act_flags flags; - struct ast_node *last_var; - struct ast_node *cur_trait; - struct ast_node *cur_proc; + struct ast *cur_proc; struct act_stack *defer_stack; struct act_stack *goto_stack; struct act_stack *label_stack; }; -static int is_lvalue(struct ast_node *n) +static int replace_type_id(struct ast *nodes, char *id, + struct type *replacement); + +static int actualize(struct act_state *state, struct scope *scope, + struct ast *node); + +static int actualize_list(struct act_state *state, struct scope *scope, + struct ast *l); + +static int actualize_type(struct act_state *state, struct scope *scope, + struct type *node); + +static int actualize_type_list(struct act_state *state, struct scope *scope, + struct type *node); + +static int is_lvalue(struct ast *n) { - if (n->node_type == AST_ARR_ACCESS) + if (n->k == AST_ARR) return 1; - if (n->node_type == AST_ID) + if (n->k == AST_ID) return 1; - if (n->node_type == AST_UNOP && AST_UNOP(n).op == AST_DEREF) + if (n->k == AST_DEREF) return 1; return 0; @@ -68,85 +78,65 @@ static enum act_flags act_flags(struct act_state *state, enum act_flags flags) return state->flags & flags; } -/* TODO: this could be improved if we get a global void thing */ -static int is_void(struct ast_node *type) +static bool is_void(struct type *t) { - assert(type->node_type == AST_TYPE); - if (AST_TYPE(type).kind != AST_TYPE_PRIMITIVE) - return 0; + /* missing return is void */ + if (!t) + return 1; - return AST_PRIMITIVE_TYPE(type).type == AST_VOID; + return t->k == TYPE_VOID; } -static struct ast_node *void_type() +static struct type *void_type() { - 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; + return tgen_primitive(TYPE_VOID, strdup("void"), NULL, NULL_LOC()); } -static struct ast_node *i27_type(struct scope *scope) +static struct type *i27_type(struct scope *scope) { - struct ast_node *i27 = gen_id(strdup("i27"), NULL_LOC()); - struct ast_node *def = file_scope_find_type(scope, i27); + struct ast *def = file_scope_find_type(scope, "i27"); if (!def) { error("missing definition of type 'i27'"); return NULL; } - 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; + return tgen_primitive(TYPE_I27, strdup("i27"), def, def->loc); } -static struct ast_node *i9_type(struct scope *scope) +static struct type *i9_type(struct scope *scope) { - struct ast_node *i27 = gen_id(strdup("i9"), NULL_LOC()); - struct ast_node *def = file_scope_find_type(scope, i27); + struct ast *def = file_scope_find_type(scope, "i9"); if (!def) { error("missing definition of type 'i9'"); return NULL; } - struct ast_node *a = gen_primitive(AST_I9, def, def->loc); - ast_set_flags(a, AST_FLAG_INIT | AST_FLAG_ACTUAL); - a->type = a; - return a; + return tgen_primitive(TYPE_I9, strdup("i9"), def, def->loc); } -static struct ast_node *str_type(struct scope *scope) +static struct type *str_type(struct scope *scope) { - struct ast_node *str = gen_id(strdup("str"), NULL_LOC()); - struct ast_node *def = file_scope_find_type(scope, str); + struct ast *def = file_scope_find_type(scope, "str"); if (!def) { error("missing definition of type 'str'"); return NULL; } - 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; + return tgen_primitive(TYPE_STR, strdup("str"), def, def->loc); } -static struct ast_node *bool_type(struct scope *scope) +static struct type *bool_type(struct scope *scope) { - struct ast_node *b = gen_id(strdup("bool"), NULL_LOC()); - struct ast_node *def = file_scope_find_type(scope, b); + struct ast *def = file_scope_find_type(scope, "bool"); if (!def) { error("missing definition of type 'bool'"); return NULL; } - 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; + return tgen_primitive(TYPE_BOOL, strdup("bool"), def, def->loc); } -static int push_defer(struct act_state *state, struct ast_node *expr) +static int push_defer(struct act_state *state, struct ast *expr) { struct act_stack *new = calloc(1, sizeof(struct act_stack)); if (!new) { @@ -160,7 +150,7 @@ static int push_defer(struct act_state *state, struct ast_node *expr) return 0; } -static int push_label(struct act_state *state, struct ast_node *label) +static int push_label(struct act_state *state, struct ast *label) { struct act_stack *new = calloc(1, sizeof(struct act_stack)); if (!new) { @@ -174,23 +164,20 @@ static int push_label(struct act_state *state, struct ast_node *label) return 0; } -static struct ast_node *find_label(struct act_state *state, - struct ast_node *label) +static struct ast *find_label(struct act_state *state, char *label) { - assert(label->node_type == AST_LABEL); struct act_stack *prev = state->label_stack, *cur; if (prev) do { cur = prev->next; - if (same_id(AST_LABEL(prev->node).id, - AST_LABEL(label).id)) + if (same_id(label_id(prev->node), label)) return prev->node; } while ((prev = cur)); return NULL; } -static int push_goto(struct act_state *state, struct ast_node *got) +static int push_goto(struct act_state *state, struct ast *got) { struct act_stack *new = calloc(1, sizeof(struct act_stack)); if (!new) { @@ -204,17 +191,17 @@ static int push_goto(struct act_state *state, struct ast_node *got) return 0; } -static struct ast_node *clone_defers(struct act_state *state, +static struct ast *clone_defers(struct act_state *state, struct act_stack *to) { struct act_stack *from = state->defer_stack; /* maintain reverse order */ - struct ast_node *defers = NULL, *prev = NULL; + struct ast *defers = NULL, *prev = NULL; while (from != to) { - struct ast_node *defer = clone_ast_node(from->node); + struct ast *defer = clone_ast(from->node); if (prev) - prev->next = defer; + prev->n = defer; if (!defers) defers = defer; @@ -263,130 +250,78 @@ static void destroy_act_state(struct act_state *state) clear_gotos(state, NULL); } -static int actualize(struct act_state *state, struct scope *scope, - struct ast_node *node); -static int analyze(struct scope *scope, struct ast_node *tree); +static void type_mismatch(struct scope *scope, struct ast *node, struct type *a, struct type *b) +{ + char *left_type = type_str(a); + char *right_type = type_str(b); + semantic_error(scope->fctx, node, + "type mismatch: %s vs %s", + left_type, right_type); + free(left_type); + free(right_type); +} + +static int analyze(struct scope *scope, struct ast *tree); -static int eval_const_if(struct scope *scope, struct ast_node *node) +static int eval_const_if(struct scope *scope, struct ast *node) { assert(node - && node->node_type == AST_IF + && node->k == AST_IF && ast_flags(node, AST_FLAG_CONST)); - int eval = 0; - struct ast_node *cond = node->_if.cond; - struct ast_node *next = node->next; - - switch (cond->node_type) { - /* for now just check if the variable exists, might change in - * the future */ - case AST_ID: eval = file_scope_find_var(scope, cond) != NULL; - break; - default: semantic_error(scope->fctx, node, - "const if conditional at file scope unimplemented"); - return -1; - } - - if (eval) { - /* condition evaluated true, so keep the if block */ - struct ast_node *body = node->_if.body; - ast_last_node(body)->next = node->next; - - /* an if must have a body, otherwise the parser messed up */ - assert(body); - - *node = *body; - free(body); - return 0; - } - - struct ast_node *els = node->_if.els; - if (els) { - *node = *els; - ast_last_node(els)->next = next; - free(els); - return 0; - } - - if (next) { - /* copy the next node into our place */ - *node = *next; - free(next); - return 0; - } - - free(node); - return 0; + semantic_error(scope->fctx, node, + "const if unimplemented"); + return -1; } -static int analyze_visibility(struct scope *scope, struct ast_node *node) +static int analyze_visibility(struct scope *scope, struct ast *node) { if (!node) return 0; - int ret = 0; node->scope = scope; - /* TODO: add error checking */ - switch (node->node_type) { - case AST_PROC: { - ret |= scope_add_proc(scope, node); - break; - } - - case AST_VAR: { - ret |= scope_add_var(scope, node); - break; - } + switch (node->k) { + case AST_PROC_DEF: return scope_add_proc(scope, node); + case AST_MACRO_DEF: return scope_add_macro(scope, node); + case AST_VAR_DEF: return scope_add_var(scope, node); case AST_IMPORT: { - const char *file = AST_IMPORT(node).file; - ret |= process_file(&scope, + const char *file = import_file(node); + return process_file(&scope, (int)ast_flags(node, AST_FLAG_PUBLIC), file); - break; } case AST_IF: { assert(ast_flags(node, AST_FLAG_CONST)); - ret |= eval_const_if(scope, node); - if (ret) + if (eval_const_if(scope, node)) return -1; /* since a const if likely replaced the current node with * something else, we have to analyze the replacement */ - ret = analyze(scope, node); - break; + return analyze(scope, node); } - case AST_STRUCT: { + case AST_STRUCT_DEF: { /* we shouldn't get any anonymous structs at this stage */ - struct ast_node *id = AST_STRUCT(node).id; - ret |= scope_add_type(scope, id, node); - break; + char *id = struct_id(node); + return scope_add_type(scope, id, node); } - case AST_ENUM: { - struct ast_node *id = AST_ENUM(node).id; - ret |= scope_add_type(scope, id, node); - break; + case AST_ENUM_DEF: { + char *id = enum_id(node); + return scope_add_type(scope, id, node); } - case AST_ALIAS: { - struct ast_node *id = AST_ALIAS(node).id; - ret |= scope_add_type(scope, id, node); - break; + case AST_ALIAS_DEF: { + char *id = alias_id(node); + return scope_add_type(scope, id, node); } - case AST_TRAIT: { - struct ast_node *id = AST_TRAIT(node).id; - ret |= scope_add_type(scope, id, node); - break; - } - - case AST_MACRO_CONSTRUCT: { - ret |= scope_add_macro(scope, node); - break; + case AST_TRAIT_DEF: { + char *id = trait_id(node); + return scope_add_type(scope, id, node); } case AST_EMPTY: { @@ -394,145 +329,188 @@ static int analyze_visibility(struct scope *scope, struct ast_node *node) } default: - ret = -1; semantic_error(scope->fctx, node, "unknown top element"); - break; + return -1; }; - return ret; + return 0; } -static int analyze_var(struct scope *scope, struct ast_node *node) +static int analyze_var(struct scope *scope, struct ast *node) { struct act_state state = {0}; return actualize(&state, scope, node); } -static void set_type(struct ast_node *node, struct ast_node *type) +static void set_type(struct ast *node, struct type *type) +{ + node->t = clone_type_list(type); +} + +static void replace_type(struct type *t, struct type *r) { - assert(type->node_type == AST_TYPE); - node->type = clone_ast_node(type); + /* free strings before they get overwritten */ + if (t->id) + free(t->id); + + struct src_loc loc = t->loc; + struct scope *scope = t->scope; + + *t = *r; + + /* clone so we don't accidentally free same string twice later */ + if (t->id) + t->id = strdup(t->id); + + /* these things we generally don't want to replace */ + t->loc = loc; + t->scope = scope; } -static int analyze_proc(struct scope *scope, struct ast_node *node) +static void replace_ast(struct ast *n, struct ast *t) { - /* not sure if this is the best place for this */ - AST_PROC(node).id->scope = scope; + if (n->s) + free(n->s); + + struct src_loc loc = n->loc; + struct scope *scope = n->scope; + *n = *t; + + if (n->s) + n->s = strdup(n->s); + + n->loc = loc; + t->scope = scope; +} + +static int analyze_proc(struct scope *scope, struct ast *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 ast *params = proc_params(node); struct act_state state = {0}; - int ret = actualize(&state, proc_scope, sign); - set_type(node, sign); - return ret; + if (actualize_list(&state, proc_scope, params)) + return -1; + + struct type *rtype = proc_rtype(node); + + if (actualize_type_list(&state, proc_scope, rtype)) + return -1; + + if (!rtype) + proc_rtype(node) = void_type(); + + struct type *callable = tgen_callable(NULL, proc_rtype(node), node->loc); + foreach_node(p, params) { + /* we must manually 'start' the chain **/ + if (!callable_ptypes(callable)) { + callable_ptypes(callable) = clone_type(p->t); + continue; + } + + type_append(callable_ptypes(callable), p->t); + } + + set_type(node, callable); + return 0; } -static struct ast_node *analyze_type_expand(struct scope *scope, - struct ast_node *n) +static struct ast *analyze_type_expand(struct scope *scope, + struct ast *n) { - assert(n->node_type == AST_TYPE_EXPAND); - struct ast_node *trait = file_scope_find_type(scope, - AST_TYPE_EXPAND(n).id); + assert(n->k == AST_TYPE_EXPAND); + struct ast *trait = file_scope_find_type(scope, type_expand_id(n)); if (!trait) { semantic_error(scope->fctx, n, "no such type"); return NULL; } - if (trait->node_type != AST_TRAIT) { + if (trait->k != AST_TRAIT_DEF) { 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).raw_body; - body = clone_ast_node(body); + struct ast *body = trait_raw_body(trait); + body = clone_ast(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; + struct type *pa = type_expand_args(n); + foreach_node(pt, trait_params(trait)) { + replace_type_id(body, var_id(pt), pa); + pa = pa->n; } return body; } -static int implements_trait(struct ast_node *body, struct ast_node *id) +static int implements_trait(struct ast *body, char *id) { foreach_node(n, body) { - if (n->node_type != AST_ID) + /* traits don't currently take generic parameters I guess? */ + if (n->k != AST_ID) continue; - if (same_id(n, id)) + if (same_id(id_str(n), id)) return 1; } return 0; } -static void mark_implemented(struct ast_node *n) +static int analyze_struct(struct scope *scope, struct ast *node) { - assert(n->node_type == AST_TYPE_EXPAND); - /** @todo very hacky but good enough for now */ - struct ast_node *id = AST_TYPE_EXPAND(n).id; - n->node_type = AST_ID; - AST_ID(n).id = strdup(AST_ID(id).id); -} - -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; + assert(node->k == AST_STRUCT_DEF); + struct ast *params = struct_params(node); struct scope *struct_scope = create_scope(); if (!struct_scope) return -1; scope_add_scope(node->scope, struct_scope); node->scope = struct_scope; - if (generics) + if (params) ast_set_flags(node, AST_FLAG_GENERIC); - struct ast_node *type = gen_type(AST_TYPE_STRUCT, node, NULL, - node->loc); + struct type *type = tgen_type(TYPE_STRUCT, + NULL, NULL, + node, NULL, + strdup(struct_id(node)), + node->loc); - foreach_node(n, AST_STRUCT(node).body) { - if (n->node_type != AST_TYPE_EXPAND) + foreach_node(n, struct_body(node)) { + if (n->k != AST_TYPE_EXPAND) continue; - if (implements_trait(AST_STRUCT(node).body, - AST_TYPE_EXPAND(n).id)) { - n->node_type = AST_EMPTY; + if (implements_trait(struct_body(node), type_expand_id(n))) { + n->k = AST_EMPTY; continue; } - if (same_id(AST_STRUCT(node).id, AST_TYPE_EXPAND(n).id)) { + if (same_id(struct_id(node), type_expand_id(n))) { semantic_error(scope->fctx, n, "recursive trait implementations not allowed"); return -1; } - struct ast_node *body = analyze_type_expand(scope, n); + struct ast *body = analyze_type_expand(scope, n); if (!body) { - n->node_type = AST_EMPTY; + n->k = AST_EMPTY; continue; } - replace_type_id(body, AST_TYPE_EXPAND(n).id, type); - ast_block_last(body)->next = n->next; - n->next = body; - - mark_implemented(n); + replace_type_id(body, type_expand_id(n), type); + ast_block_last(body)->n = n->n; + n->n = body; } - foreach_node(n, AST_STRUCT(node).body) { - switch (n->node_type) { + foreach_node(n, struct_body(node)) { + switch (n->k) { case AST_EMPTY: continue; case AST_ID: continue; /* prototypes are checked later */ - case AST_PROC: if (!AST_PROC(n).body) continue; + case AST_PROC_DEF: if (!proc_body(n)) continue; default: } @@ -540,9 +518,9 @@ static int analyze_struct(struct scope *scope, struct ast_node *node) return -1; } - foreach_node(n, AST_STRUCT(node).body) { + foreach_node(n, struct_body(node)) { /* also checks prototypes */ - if (n->node_type != AST_PROC) + if (n->k != AST_PROC_DEF) continue; if (analyze_proc(struct_scope, n)) @@ -550,38 +528,47 @@ static int analyze_struct(struct scope *scope, struct ast_node *node) } /* check that all prototypes are implemented */ - foreach_node(n, AST_STRUCT(node).body) { - if (n->node_type != AST_PROC) + foreach_node(n, struct_body(node)) { + if (n->k != AST_PROC_DEF) continue; - if (AST_PROC(n).body) + if (proc_body(n)) continue; - struct ast_node *proc = scope_find_proc(struct_scope, - AST_PROC(n).id); + struct ast *proc = scope_find_proc(struct_scope, proc_id(n)); if (!proc) { semantic_error(scope->fctx, n, "missing implementation"); return -1; } - if (!equiv_nodes(AST_PROC(n).sign, AST_PROC(proc).sign)) { + if (!types_match(n->t, proc->t)) { semantic_error(scope->fctx, n, "mismatched signatures"); semantic_info(scope->fctx, proc, "note: here"); return -1; } } + char *id = strdup(struct_id(node)); + if (same_id(id, "i27")) + node->t = tgen_primitive(TYPE_I27, id, node, node->loc); + else if (same_id(id, "i9")) + node->t = tgen_primitive(TYPE_I9, id, node, node->loc); + else if (same_id(id, "bool")) + node->t = tgen_primitive(TYPE_BOOL, id, node, node->loc); + else + node->t = tgen_struct(id, node, node->loc); + /** @todo there is the possibility that two different traits add the * same prototype, which is reported in traits but not structs? */ return 0; } /* quite a lot of overlap with analyze_struct, kind of ugly I guess */ -static int analyze_trait(struct scope *scope, struct ast_node *node) +static int analyze_trait(struct scope *scope, struct ast *node) { - assert(node->node_type == AST_TRAIT); - struct ast_node *generics = AST_TRAIT(node).params; + assert(node->k == AST_TRAIT_DEF); + struct ast *params = trait_params(node); struct scope *trait_scope = create_scope(); if (!trait_scope) return -1; @@ -589,54 +576,55 @@ static int analyze_trait(struct scope *scope, struct ast_node *node) scope_add_scope(node->scope, trait_scope); node->scope = trait_scope; /** @todo should probably add in aliases for the traits in scope? */ - if (generics) + if (params) ast_set_flags(node, AST_FLAG_GENERIC); - struct ast_node *type = gen_type(AST_TYPE_TRAIT, node, NULL, node->loc); + struct type *type = tgen_type(TYPE_TRAIT, NULL, NULL, + node, NULL, strdup(trait_id(node)), + node->loc); + + /* copy body */ + node->a2 = clone_ast(trait_raw_body(node)); - AST_TRAIT(node).body = clone_ast_node(AST_TRAIT(node).raw_body); /* do type expansions */ - foreach_node(n, AST_TRAIT(node).body) { - if (n->node_type != AST_TYPE_EXPAND) + foreach_node(n, trait_body(node)) { + if (n->k != AST_TYPE_EXPAND) continue; /* don't re-expand already implemented traits */ - if (implements_trait(AST_TRAIT(node).body, - AST_TYPE_EXPAND(n).id)) { + if (implements_trait(trait_body(node), type_expand_id(n))) { /* not sure about this, but at least we don't have stray * type expands everywhere */ - n->node_type = AST_EMPTY; + n->k = AST_EMPTY; continue; } - if (same_id(AST_TRAIT(node).id, AST_TYPE_EXPAND(n).id)) { + if (same_id(trait_id(node), type_expand_id(n))) { semantic_error(scope->fctx, n, "recursive trait implementations not allowed"); return -1; } - struct ast_node *body = analyze_type_expand(scope, n); + struct ast *body = analyze_type_expand(scope, n); if (!body) { - n->node_type = AST_EMPTY; + n->k = AST_EMPTY; continue; } - replace_type_id(body, AST_TYPE_EXPAND(n).id, type); - ast_last_node(body)->next = n->next; - n->next = body; - - mark_implemented(n); + replace_type_id(body, type_expand_id(n), type); + ast_last(body)->n = n->n; + n->n = body; } /* add all procedure definitions to scope */ - foreach_node(n, AST_TRAIT(node).body) { + foreach_node(n, trait_body(node)) { /* kind of a hack but these shouldn't be shown to * analyze_visibility */ - switch (n->node_type) { + switch (n->k) { case AST_EMPTY: continue; case AST_ID: continue; /* prototypes are added later */ - case AST_PROC: if (!AST_PROC(n).body) continue; + case AST_PROC_DEF: if (!proc_body(n)) continue; default: } @@ -649,47 +637,49 @@ static int analyze_trait(struct scope *scope, struct ast_node *node) * would be. Add a prototypes -list to scopes? */ /* add all prototypes that don't have matching definition to scope */ - foreach_node(n, AST_TRAIT(node).body) { - if (n->node_type != AST_PROC) + foreach_node(n, trait_body(node)) { + if (n->k != AST_PROC_DEF) continue; - if (AST_PROC(n).body) + if (proc_body(n)) continue; /* prototypes are checked only if there's no implementation */ - if (scope_find_proc(trait_scope, AST_PROC(n).id)) + if (scope_find_proc(trait_scope, proc_id(n))) continue; if (analyze_visibility(trait_scope, n)) return -1; } - foreach_node(n, AST_TRAIT(node).body) { - if (n->node_type != AST_PROC) + foreach_node(n, trait_body(node)) { + if (n->k != AST_PROC_DEF) continue; if (analyze_proc(trait_scope, n)) return -1; } + node->t = tgen_trait(strdup(struct_id(node)), node, node->loc); + return 0; } -static int analyze_signs(struct scope *scope, struct ast_node *node) +static int analyze_signs(struct scope *scope, struct ast *node) { /** @todo aliases? */ - 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; + switch (node->k) { + case AST_VAR_DEF: return analyze_var(scope, node); break; + case AST_PROC_DEF: return analyze_proc(scope, node); break; + case AST_STRUCT_DEF: return analyze_struct(scope, node); break; + case AST_TRAIT_DEF: return analyze_trait(scope, node); break; default: } return 0; } -static int analyze(struct scope *scope, struct ast_node *tree) +static int analyze(struct scope *scope, struct ast *tree) { foreach_node(node, tree) { if (analyze_visibility(scope, node)) @@ -707,13 +697,13 @@ static int analyze(struct scope *scope, struct ast_node *tree) return -1; printf("//actualized:\n"); - dump_ast_node(0, node); + ast_dump(0, node); } return 0; } -int analyze_root(struct scope *scope, struct ast_node *tree) +int analyze_root(struct scope *scope, struct ast *tree) { if (analyze(scope, tree)) return -1; @@ -721,22 +711,13 @@ int analyze_root(struct scope *scope, struct ast_node *tree) return 0; } -static int structs_match(struct ast_node *a, struct ast_node *b) +static int structs_match(struct type *a, struct type *b) { - /** @todo: iterate over elements in structure */ - /** @todo: check tag as well? */ - return 0; -} - -static int primitives_match(struct ast_node *a, struct ast_node *b) -{ - enum ast_primitive at = AST_PRIMITIVE_TYPE(a).type; - enum ast_primitive bt = AST_PRIMITIVE_TYPE(b).type; - - return at == bt; + /* dunno, let's go with this for now */ + return a->d == b->d; } -int types_match(struct ast_node *a, struct ast_node *b) +int types_match(struct type *a, struct type *b) { if (!a && !b) return 1; @@ -744,111 +725,85 @@ int types_match(struct ast_node *a, struct ast_node *b) if (!a || !b) return 0; - assert(a->node_type == AST_TYPE); - assert(b->node_type == AST_TYPE); - /* if the type kind doesn't match, we're done. */ - if (AST_TYPE(a).kind != AST_TYPE(b).kind) + if (a->k != b->k) return 0; - if (AST_TYPE(a).kind == AST_TYPE_STRUCT) + if (a->k == TYPE_STRUCT) return structs_match(a, b); - if (AST_TYPE(a).kind == AST_TYPE_POINTER) - return types_match(AST_PTR_TYPE(a).base, - AST_PTR_TYPE(b).base); - - if (AST_TYPE(a).kind == AST_TYPE_PRIMITIVE) - return primitives_match(a, b); + if (a->k == TYPE_PTR) + return types_match(ptr_base(a), ptr_base(b)); - return 0; + return 1; } -static int _replace_id(struct ast_node *node, void *data) +static int _replace_id(struct ast *node, void *data) { if (!node) return 0; - if (node->node_type != AST_ID) - return ast_call_on(_replace_id, node, data); + if (node->k != AST_ID) + return 0; - struct ast_node **pair = data; - struct ast_node *id = pair[0]; - struct ast_node *expr = pair[1]; + struct ast **pair = data; + struct ast *id = pair[0]; + struct ast *expr = pair[1]; - if (!same_id(node, id)) - return ast_call_on(_replace_id, node, data); + /* no match, continue */ + if (!same_id(id_str(node), id_str(id))) + return 0; - struct ast_node *clone = clone_ast_node(expr); + struct ast *clone = clone_ast(expr); if (!clone) { internal_error("failed cloning replacement expr"); return -1; } - clone->next = node->next; + clone->n = node->n; clone->scope = node->scope; - *node = *clone; + replace_ast(node, clone); /* a succesful replacement needs no futher replacements, I think */ return 0; } -static int replace_id(struct ast_node *body, struct ast_node *id, - struct ast_node *expr) +static int replace_id(struct ast *body, struct ast *id, + struct ast *expr) { - struct ast_node *pair[2] = {id, expr}; - return ast_call_on(_replace_id, body, pair); + struct ast *pair[2] = {id, expr}; + return ast_visit(_replace_id, NULL, body, pair); } -static int actualize_macro_construct(struct act_state *state, - struct scope *scope, struct ast_node *n) +static int actualize_macro_def(struct act_state *state, + struct scope *scope, struct ast *n) { UNUSED(state); /* macro bodies, arguments, etc aren't expanded upon until the macro is * called, so just try to add it to the local scope */ - assert(n && n->node_type == AST_MACRO_CONSTRUCT); + assert(n && n->k == AST_MACRO_DEF); return scope_add_macro(scope, n); } -struct ast_node *extract_trait(struct ast_node *type) -{ - if (!type) - return 0; - - assert(type->node_type == AST_TYPE); - if (type->_type.kind == AST_TYPE_TRAIT) - return type; - - return extract_trait(type->_type.next); -} - -static void actualize_trait_types(struct ast_node *params, - struct ast_node *args) -{ - /** @todo replace trait types with arg types, should probably be merged - * */ - assert(!args && !params); -} - static int actualize_macro_expand(struct act_state *state, struct scope *scope, - struct ast_node *macro_expand) + struct ast *macro_expand) { - assert(macro_expand->node_type == AST_MACRO_EXPAND); - struct ast_node *id = AST_MACRO_EXPAND(macro_expand).id; - struct ast_node *macro = file_scope_find_macro(scope, id); + assert(macro_expand->k == AST_MACRO_EXPAND); + char *id = macro_expand_id(macro_expand); + struct ast *macro = file_scope_find_macro(scope, id); if (!macro) { semantic_error(scope->fctx, macro_expand, "no such macro"); return -1; } - assert(macro->node_type == AST_MACRO_CONSTRUCT); + assert(macro->k == AST_MACRO_DEF); if (ast_flags(macro, AST_FLAG_VARIADIC)) { semantic_error(scope->fctx, macro, "variadic macros not yet implemented"); return -1; } - struct ast_node *body = clone_ast_node(AST_MACRO_CONSTRUCT(macro).body); + struct ast *body = clone_ast(macro_def_body(macro)); if (!body) { internal_error("failed allocating body for macro expansion"); return -1; @@ -856,63 +811,79 @@ static int actualize_macro_expand(struct act_state *state, /** @todo update all macro IDs to the correct scope */ body->scope = macro_expand->scope; - body->next = macro_expand->next; + body->n = macro_expand->n; - struct ast_node *param = AST_MACRO_CONSTRUCT(macro).params; - struct ast_node *arg = AST_MACRO_EXPAND(macro_expand).args; + struct ast *param = macro_def_params(macro); + struct ast *arg = macro_expand_args(macro_expand); - /* TODO: actual replacements */ while (param && arg) { /* feels slightly hacky, but essentially replace each individual * component in the arg list by breaking it out of the list * temporarily. After the replacement, insert it back into the * list so the cleanup is easier. */ - struct ast_node *next_arg = arg->next; - arg->next = NULL; + struct ast *next_arg = arg->n; + arg->n = NULL; if (replace_id(body, param, arg)) { semantic_error(scope->fctx, macro_expand, "failed replacing params with args"); - arg->next = next_arg; + arg->n = next_arg; return -1; } - param = param->next; - arg = arg->next = next_arg; + param = param->n; + arg = arg->n = next_arg; } + free(macro_expand_id(macro_expand)); *macro_expand = *body; /* actualize the new content */ return actualize(state, scope, macro_expand); } static int actualize_call(struct act_state *state, - struct scope *scope, struct ast_node *call) + struct scope *scope, struct ast *call) { - assert(call && call->node_type == AST_CALL); + assert(call && call->k == AST_CALL); - /* check that arguments exist, make sure they have types etc. */ - /* TODO: procedure callbacks? */ - int ret = actualize(state, scope, AST_CALL(call).args); - if (ret) - return ret; + if (actualize_list(state, scope, call_args(call))) + return -1; - ret = actualize(state, scope, AST_CALL(call).expr); - if (ret) - return ret; + if (actualize_list(state, scope, call_expr(call))) + return -1; - 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", + struct ast *expr = call_expr(call); + if (expr->t->k != TYPE_CALLABLE) { + char *tstr = type_str(expr->t); + semantic_error(scope->fctx, call, "not a callable type: %s", tstr); free(tstr); return -1; } - struct ast_node *sign = expr->type; - set_type(call, AST_SIGN_TYPE(sign).ret); + struct type *callable = expr->t; + struct type *ptypes = callable_ptypes(callable); + struct ast *arg = call_args(call); + foreach_type(p, ptypes) { + if (!arg) { + semantic_error(scope->fctx, call, "too many arguments"); + return -1; + } + + if (!types_match(p, arg->t)) { + type_mismatch(scope, arg, p, arg->t); + return -1; + } + + arg = arg->n; + } + + if (arg) { + semantic_error(scope->fctx, arg, "too many arguments"); + return -1; + } + + set_type(call, callable_rtype(callable)); return 0; } @@ -923,7 +894,7 @@ static void warn_unused_labels(struct act_state *state, struct scope *scope) return; do { - struct ast_node *label = labels->node; + struct ast *label = labels->node; if (!ast_flags(label, AST_FLAG_ACTUAL)) semantic_warn(scope->fctx, label, "unused label"); @@ -939,7 +910,7 @@ static int undefined_gotos(struct act_state *state, struct scope *scope) return ret; do { - struct ast_node *got = gotos->node; + struct ast *got = gotos->node; if (!ast_flags(got, AST_FLAG_ACTUAL)) { semantic_warn(scope->fctx, got, "undefined label"); @@ -952,39 +923,35 @@ static int undefined_gotos(struct act_state *state, struct scope *scope) } static int actualize_proc(struct act_state *state, - struct scope *scope, struct ast_node *proc) + struct scope *scope, struct ast *proc) { + UNUSED(state); /* actualize_proc is called on trait procs as well, but I believe * that's fine? */ - assert(proc && proc->node_type == AST_PROC); - struct ast_node *sign = AST_PROC(proc).sign; + assert(proc && proc->k == AST_PROC_DEF); - /* signature should already be typed etc. */ struct act_state new_state = {0}; - if (actualize(&new_state, proc->scope, sign)) - return -1; - - set_type(proc, sign); + /* params should already have been actualized, should maybe check */ /* actualize body */ new_state.cur_proc = proc; - if (actualize(&new_state, proc->scope, AST_PROC(proc).body)) + if (actualize(&new_state, proc->scope, proc_body(proc))) return -1; if (!act_flags(&new_state, ACT_HAS_RETURN)) { - if (!is_void(AST_SIGN_TYPE(sign).ret)) { + if (!is_void(proc_rtype(proc))) { semantic_error(scope->fctx, proc, "no return with non-void return type"); return -1; } /* add 'implicit' return */ - struct ast_node *body = AST_PROC(proc).body; - struct ast_node *r = gen_return(NULL, NULL_LOC()); + struct ast *body = proc_body(proc); + struct ast *r = gen_return(NULL, NULL, NULL_LOC()); r->scope = body->scope; - ast_append(AST_BLOCK(body).body, r); + ast_append(block_body(body), r); } - else if (ast_block_last(AST_PROC(proc).body)->node_type != AST_RETURN) { + else if (ast_block_last(proc_body(proc))->k != AST_RETURN) { /* TODO: something more sophisticated than this */ semantic_warn(scope->fctx, proc, "unable to determine explicit return for all branches"); @@ -996,63 +963,41 @@ static int actualize_proc(struct act_state *state, return -1; /* if we're main, don't mangle the entry point */ - struct ast_node *id = AST_PROC(proc).id; - if (strcmp("main", AST_ID(id).id) == 0) - ast_set_flags(id, AST_FLAG_NOMANGLE); + char *id = proc_id(proc); + if (strcmp("main", id) == 0) + ast_set_flags(proc, AST_FLAG_NOMANGLE); /* we have successfully actualized the procedure */ return 0; } static int actualize_binop(struct act_state *state, - struct scope *scope, struct ast_node *binop) + struct scope *scope, struct ast *binop) { - assert(binop && binop->node_type == AST_BINOP); - - struct ast_node *left = AST_BINOP(binop).left; - struct ast_node *right = AST_BINOP(binop).right; + assert(binop && is_binop(binop)); - int ret = 0; - ret |= actualize(state, scope, left); - ret |= actualize(state, scope, right); - if (ret) - return ret; + struct ast *left = binop_left(binop); + struct ast *right = binop_right(binop); - if (!left->type) { - semantic_error(scope->fctx, binop, - "unable to detect lefthand type"); + if (actualize(state, scope, left)) return -1; - } - if (!right->type) { - semantic_error(scope->fctx, binop, - "unable to detect righthand type"); + if (actualize(state, scope, right)) return -1; - } - if (!types_match(left->type, right->type)) { - char *left_type = type_str(left); - char *right_type = type_str(right); - semantic_error(scope->fctx, binop, - "type mismatch (%s vs %s)", left_type, - right_type); - free(left_type); - free(right_type); + if (!types_match(left->t, right->t)) { + type_mismatch(scope, binop, left->t, right->t); return -1; } - /* TODO: also check trait types, just because two traits collapse - * to the same actual type doesn't mean that the two trait types - * should be allowed to operate on eachother */ - /* types are the same, so the type of this expression is whichever */ - set_type(binop, left->type); + set_type(binop, left->t); return 0; } static int actualize_block(struct act_state *state, - struct scope *scope, struct ast_node *node) + struct scope *scope, struct ast *node) { struct scope *block_scope = scope; if (!ast_flags(node, AST_FLAG_UNHYGIENIC)) { @@ -1066,20 +1011,21 @@ static int actualize_block(struct act_state *state, } struct act_stack *defers = state->defer_stack; - foreach_node(pt, node->_block.body) { + foreach_node(pt, block_body(node)) { if (actualize(state, block_scope, pt)) return -1; } - if (node->_block.body == NULL) { - node->type = void_type(); - node->_block.body = gen_empty(); + if (block_body(node) == NULL) { + node->t = void_type(); + /* still not a huge fan of directly mucking about with ast slots */ + block_body(node) = gen_empty(NULL_LOC()); return 0; } /* the block type is the last statement in the block's type */ - set_type(node, ast_last_node(node->_block.body)->type); - if (!node->type) { + set_type(node, ast_last(block_body(node))->t); + if (!node->t) { semantic_error(scope->fctx, node, "unable to detect block type"); return -1; @@ -1088,8 +1034,8 @@ static int actualize_block(struct act_state *state, /* TODO: currently defers are sort of duplicated after a return, unsure * if they should be handled here or somewhere else */ if (state->defer_stack) { - node->_block.defers = clone_defers(state, defers); - if (!node->_block.defers) { + block_defers(node) = clone_defers(state, defers); + if (!block_defers(node)) { internal_error("failed cloning defers"); return -1; } @@ -1101,10 +1047,10 @@ static int actualize_block(struct act_state *state, } static int actualize_id(struct act_state *state, - struct scope *scope, struct ast_node *id) + struct scope *scope, struct ast *id) { UNUSED(state); - assert(id && id->node_type == AST_ID); + assert(id && id->k == AST_ID); id->scope = scope; /** @todo vars and procs kind of override eachother, i.e. @@ -1119,16 +1065,16 @@ static int actualize_id(struct act_state *state, * Either add in some syntax to distinguish procedure calls and * pointer calls or make procs and vars share the same namespace. * */ - struct ast_node *decl = file_scope_find_var(scope, id); + struct ast *decl = file_scope_find_var(scope, id_str(id)); if (decl) { - set_type(id, decl->type); + set_type(id, decl->t); decl->uses++; return 0; } - decl = file_scope_find_proc(scope, id); + decl = file_scope_find_proc(scope, id_str(id)); if (decl) { - set_type(id, decl->type); + set_type(id, decl->t); decl->uses++; return 0; } @@ -1138,320 +1084,268 @@ static int actualize_id(struct act_state *state, } static int actualize_var(struct act_state *state, - struct scope *scope, struct ast_node *var) + struct scope *scope, struct ast *var) { - assert(var && var->node_type == AST_VAR); - struct ast_node *init = AST_VAR(var).init; - struct ast_node *type = AST_VAR(var).type; + assert(var && var->k == AST_VAR_DEF); + struct ast *init = var_init(var); + struct type *type = var_type(var); /* one of these must be defined, otherwise the parser fucked up */ assert(type || init); - if (init && actualize(state, scope, init)) + if (init && actualize_list(state, scope, init)) return -1; - if (type && actualize(state, scope, type)) + if (type && actualize_type_list(state, scope, type)) return -1; - if (init && init->node_type == AST_INIT) { - assert(!init->type); + if (init && init->k == AST_INIT) { + assert(!init->t); set_type(init, type); /* TODO: some kind of check_init() */ } if (init && type) { /* make sure the asked type and the actualized types match */ - if (!types_match(init->type, type)) { - char *init_type = type_str(init->type); - char *req_type = type_str(type); - semantic_error(scope->fctx, var, - "type mismatch (%s vs %s)", - req_type, init_type); - free(init_type); - free(req_type); + if (!types_match(init->t, type)) { + type_mismatch(scope, var, init->t, type); return -1; } } - /* this is important for lowering */ - AST_VAR(var).id->scope = scope; var->scope = scope; if (init) /* infer */ - set_type(var, init->type); + set_type(var, init->t); if (type) - /* TODO: should there be some default value? */ + /* TODO: should there be some default zero value? */ /* declare */ set_type(var, type); /* 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 (AST_VAR(var).id && !ast_flags(var, AST_FLAG_MEMBER)) + if (var_id(var) && !ast_flags(var, AST_FLAG_MEMBER)) return scope_add_var(scope, var); - /* TODO: we should make sure the type is fully qualified in bodies */ return 0; } -#define ENTER_ACT() \ - enum act_flags old_flags = state->flags; \ - struct ast_node *old_trait = state->cur_trait; - -#define EXIT_ACT(r) \ - do { \ - state->cur_trait = old_trait; \ - state->flags = old_flags; \ - return r; \ - } while (0); - -static enum ast_primitive id_to_primitive(struct ast_node *id) +static int actualize_tid(struct act_state *state, struct scope *scope, struct type *t) { - const char *name = AST_ID(id).id; - if (strcmp(name, "i27") == 0) - return AST_I27; + UNUSED(state); - if (strcmp(name, "i9") == 0) - return AST_I9; + /* no id means void */ + if (!t->id) { + replace_type(t, void_type()); + return 0; + } - if (strcmp(name, "bool") == 0) - return AST_BOOL; + struct src_loc l = t->loc; - /** @todo some better error reporting should probably be used */ - internal_error( "illegal primitive: %s\n", name); - return AST_VOID; -} + struct ast *def = file_scope_find_type(scope, t->id); + if (!def) { + type_error(scope->fctx, t, "no such type"); + return -1; + } -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; + assert(t->n == NULL); + assert(def->k != AST_TRAIT_DEF); - if (strcmp(name, "i9") == 0) - return 1; + if (def->k == AST_ALIAS_DEF) { + replace_type(t, clone_type_list(def->t)); + t->a = def; + return 0; + } + else if (def->k == AST_STRUCT_DEF) { + replace_type(t, clone_type_list(def->t)); + return 0; + } + else if (def->k == AST_ENUM_DEF) { + replace_type(t, clone_type_list(def->t)); + return 0; + } - if (strcmp(name, "bool") == 0) - return 1; + return -1; +} - /* special case of a special case? - if (strcmp(name, "str")) - return 1; - */ +static int actualize_ptr(struct act_state *state, struct scope *scope, struct type *t) +{ + assert(ptr_base(t)); + if (actualize_type(state, scope, ptr_base(t))) + return -1; return 0; } -static int actualize_type(struct act_state *state, - struct scope *scope, struct ast_node *type) -{ - /* TODO: there's gotta be a better way to handle flags. Maybe macros - * like ENTER_ACTUALIZE and EXIT_ACTUALIZE? */ - /* TODO: get rid of aliases, expand implements, fill out structures, - * etc. */ - assert(type->node_type == AST_TYPE); - ENTER_ACT(); - - if (actualize(state, scope, AST_TYPE(type).next)) { - EXIT_ACT(-1); - } - - switch (AST_TYPE(type).kind) { - case AST_TYPE_ID: { - /* type IDs can really only be aliases to something else, or if - * they're missing, void */ - if (!AST_ID_TYPE(type).id) { - /* no ID means void */ - AST_ID_TYPE(type).id = - gen_id(strdup("void"), NULL_LOC()); - } +static int actualize_callable(struct act_state *state, struct scope *scope, struct type *t) +{ + struct type *ptypes = callable_ptypes(t); + struct type *rtype = callable_rtype(t); - 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); - } + foreach_type(p, ptypes){ + if (actualize_type(state, scope, p)) + return -1; + } - if (exists->node_type == AST_TYPE) { - assert(AST_TYPE(exists).kind == AST_TYPE_PRIMITIVE); - AST_TYPE(type) = AST_TYPE(exists); - break; - } + if (!rtype) + callable_rtype(t) = void_type(); - /* - if (actualize(state, exists->scope, exists)) - EXIT_ACT(-1); - */ + if (actualize_type(state, scope, rtype)) + return -1; - assert(AST_TYPE(type).next == NULL); - if (exists->node_type == AST_ALIAS) { - AST_TYPE(type).aliased = exists; - AST_TYPE(type) = AST_TYPE(exists); - } - else if (exists->node_type == AST_TRAIT) { - /* this is kind of weird, have to think about it */ - *type = *gen_type(AST_TYPE_TRAIT, exists, - NULL, exists->loc); - } - else if (exists->node_type == AST_STRUCT) { - 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) { - *type = *gen_type(AST_TYPE_ENUM, exists, - NULL, exists->loc); - } + return 0; +} - break; +static int actualize_i27(struct act_state *state, struct scope *scope, struct type *t) +{ + /* not much to do */ + if (t->d) + return 0; + + struct ast *def = file_scope_find_type(scope, "i27"); + if (!def) { + error("missing definition of type 'i27'"); + return NULL; } - case AST_TYPE_CONSTRUCT: - /** @todo fully qualified constructs should be added to the - * actual list for code generation */ - semantic_info(scope->fctx, type, - "constructs unimplemented, continuing with compilation to see what breaks"); - break; + t->d = def; + return 0; +} - case AST_TYPE_ARR: - /* TODO: expression should be expandable to integer constant */ - semantic_info(scope->fctx, type, - "arrays unimplemented, continuing with compilation to see what breaks"); - break; +static int actualize_i9(struct act_state *state, struct scope *scope, struct type *t) +{ + /* not much to do */ + if (t->d) + return 0; - case AST_TYPE_POINTER: - assert(AST_PTR_TYPE(type).base); - if (actualize(state, scope, AST_PTR_TYPE(type).base)) - EXIT_ACT(-1); - break; + struct ast *def = file_scope_find_type(scope, "i9"); + if (!def) { + error("missing definition of type 'i9'"); + return NULL; + } - case AST_TYPE_SIGN: { - struct ast_node *params = AST_SIGN_TYPE(type).params; - struct ast_node *ret = AST_SIGN_TYPE(type).ret; + t->d = def; + return 0; +} - foreach_node(p, params){ - if (actualize(state, scope, p)) - EXIT_ACT(-1); - } +static int actualize_bool(struct act_state *state, struct scope *scope, struct type *t) +{ + /* not much to do */ + if (t->d) + return 0; - if (!ret) - ret = AST_SIGN_TYPE(type).ret = void_type(); + struct ast *def = file_scope_find_type(scope, "bool"); + if (!def) { + error("missing definition of type 'bool'"); + return NULL; + } - if (actualize(state, scope, ret)) - EXIT_ACT(-1); + t->d = def; + return 0; +} - break; - } +static int actualize_type(struct act_state *state, + struct scope *scope, + struct type *t) +{ + if (!t) + return 0; - case AST_TYPE_TRAIT: { - assert(ast_flags(type, AST_FLAG_ACTUAL)); - semantic_info(scope->fctx, type, - "FIXME skipping trait type checks"); - break; - } + t->scope = scope; - case AST_TYPE_STRUCT: { - assert(ast_flags(type, AST_FLAG_ACTUAL)); - semantic_info(scope->fctx, type, - "FIXME skipping struct type checks"); - break; - } + if (actualize_type(state, scope, t->n)) + return -1; - case AST_TYPE_PRIMITIVE: { - assert(ast_flags(type, AST_FLAG_ACTUAL)); - semantic_info(scope->fctx, type, - "FIXME skipping primitive type checks"); - break; - } + switch (t->k) { + case TYPE_I27: return actualize_i27(state, scope, t); + case TYPE_I9: return actualize_i9(state, scope, t); + case TYPE_BOOL: return actualize_bool(state, scope, t); + case TYPE_ID: return actualize_tid(state, scope, t); + case TYPE_PTR: return actualize_ptr(state, scope, t); + case TYPE_CALLABLE: return actualize_callable(state, scope, t); + case TYPE_VOID: return 0; /* void is by default actualized */ default: - semantic_error(scope->fctx, type, "unimplemented type"); - EXIT_ACT(-1); + type_info(scope->fctx, t, + "unimplemented type"); + type_info(scope->fctx, t, + "continuing with compilation to see what breaks"); + return 0; } - /* generally speaking */ - EXIT_ACT(0); + return 0; } -static int actualize_empty(struct act_state *state, - struct scope *scope, struct ast_node *node) +static int actualize_type_list(struct act_state *state, + struct scope *scope, + struct type *l) { - UNUSED(state); - /* TODO: converting to void is common enough that it might be worth - * creating a function for */ - struct ast_node *void_id = gen_id(strdup("void"), NULL_LOC()); - if (!void_id) { - internal_error( - "couldn't allocate type id for empty statement\n"); - return -1; - } - - node->type = gen_type(AST_TYPE_ID, void_id, NULL, NULL_LOC()); - if (!node->type) { - internal_error("couldn't allocate type for empty statement\n"); - return -1; + foreach_type(t, l) { + if (actualize_type(state, scope, t)) + return -1; } return 0; } -static int integral_type(struct ast_node *type) +static int actualize_empty(struct act_state *state, + struct scope *scope, struct ast *node) { - assert(type->node_type == AST_TYPE); - if (AST_TYPE(type).kind != AST_TYPE_PRIMITIVE) - return 0; + UNUSED(state); + UNUSED(scope); + node->t = void_type(); + return 0; +} - /* here would be awesome with an enum of our base types */ - enum ast_primitive p = AST_PRIMITIVE_TYPE(type).type; - switch (p) { - case AST_I9: return 1; - case AST_I27: return 1; - default: return 0; +static int integral_type(struct type *type) +{ + switch (type->k) { + case TYPE_I27: + case TYPE_I9: + case TYPE_BOOL: + return true; /* maybe? */ + default: } - return 0; + return false; } -static int pointer_type(struct ast_node *type) +static int pointer_type(struct type *type) { - assert(type->node_type == AST_TYPE); - return type->_type.kind == AST_TYPE_POINTER; + return type->k == TYPE_PTR; } -static int pointer_conversion(struct ast_node *a, struct ast_node *b) +static int pointer_conversion(struct type *a, struct type *b) { - assert(a->node_type == AST_TYPE); - assert(b->node_type == AST_TYPE); if (pointer_type(a)) { - if (AST_TYPE(b).kind != AST_TYPE_PRIMITIVE) + if (!is_primitive(b)) return 0; /* for now */ - return AST_PRIMITIVE_TYPE(b).type == AST_I27; + return b->k == TYPE_I27; } return 0; } -static size_t member_count(struct ast_node *exists) +static size_t member_count(struct ast *exists) { - assert(exists->node_type == AST_STRUCT); - struct ast_node *body = exists->_struct.body; - return ast_list_len(body); + assert(exists->k == AST_STRUCT_DEF); + struct ast *body = struct_body(exists); + + size_t count = 0; + foreach_node(n, body) { + if (n->k == AST_VAR_DEF) + count++; + } + + return count; } -static struct ast_node *lookup_member_idx(struct ast_node *body, - struct ast_node *find, +static struct ast *lookup_member_idx(struct ast *body, + char *find, size_t *idx) { /* micro-optimisation, likely way premature but speeds up selection @@ -1459,27 +1353,26 @@ static struct ast_node *lookup_member_idx(struct ast_node *body, (void)(find); assert(idx); size_t i = *idx; - struct ast_node *m = body; + struct ast *m = body; while (i != 0 && m) { - m = m->next; + m = m->n; i--; } return m; } -static struct ast_node *lookup_member_name(struct ast_node *body, - struct ast_node *find, +static struct ast *lookup_member_name(struct ast *body, + char *find, size_t *idx) { - assert(find->node_type == AST_ID); size_t i = 0; - struct ast_node *m = body; + struct ast *m = body; while (m) { - assert(m->node_type == AST_VAR); - if (same_id(find, AST_VAR(m).id)) + assert(m->k == AST_VAR_DEF); + if (same_id(find, var_id(m))) break; - m = m->next; + m = m->n; i++; } @@ -1489,25 +1382,25 @@ static struct ast_node *lookup_member_name(struct ast_node *body, return m; } -static struct ast_node *lookup_struct_member(struct ast_node *struc, - struct ast_node *find, size_t *idx) +static struct ast *lookup_struct_member(struct ast *struc, + char *find, size_t *idx) { if (find) - return lookup_member_name(struc->_struct.body, find, idx); + return lookup_member_name(struct_body(struc), find, idx); - return lookup_member_idx(struc->_struct.body, find, idx); + return lookup_member_idx(struct_body(struc), find, idx); } -static struct ast_node *lookup_enum_member(struct ast_node *enu, - struct ast_node *find) +static struct ast *lookup_enum_member(struct ast *enu, + char *find) { size_t i = 0; - struct ast_node *m = enu->_enum.body; + struct ast *m = enum_body(enu); while (m) { - assert(m->node_type == AST_VAL); - if (same_id(find, AST_VAL(m).id)) + assert(m->k == AST_VAL); + if (same_id(find, val_id(m))) break; - m = m->next; + m = m->n; i++; } @@ -1515,7 +1408,7 @@ static struct ast_node *lookup_enum_member(struct ast_node *enu, } static int init_struct(struct act_state *state, struct scope *scope, - struct ast_node *exists, struct ast_node *init) + struct ast *exists, struct ast *init) { size_t i = 0; size_t mcount = member_count(exists); @@ -1527,7 +1420,7 @@ static int init_struct(struct act_state *state, struct scope *scope, return -1; } - struct ast_node *args = init->_init.body; + struct ast *args = init_body(init); while (args) { if (i >= mcount) { semantic_error(scope->fctx, args, @@ -1546,15 +1439,15 @@ static int init_struct(struct act_state *state, struct scope *scope, if ((ret = actualize(state, scope, args))) break; - struct ast_node *find = NULL; + char *find = NULL; if (ast_flags(args, AST_FLAG_MEMBER)) - find = args->_var.id; + find = var_id(args); - struct ast_node *member = + struct ast *member = lookup_struct_member(exists, find, &i); if (!member) { - char *sstr = type_str(exists->type); + char *sstr = type_str(exists->t); semantic_error(scope->fctx, args, "no such member in %s", sstr); @@ -1563,9 +1456,9 @@ static int init_struct(struct act_state *state, struct scope *scope, break; } - if (!types_match(args->type, member->type)) { - char *astr = type_str(args->type); - char *mstr = type_str(member->type); + if (!types_match(args->t, member->t)) { + char *astr = type_str(args->t); + char *mstr = type_str(member->t); semantic_error(scope->fctx, args, "%s does not match %s", astr, mstr); free(astr); @@ -1575,7 +1468,7 @@ static int init_struct(struct act_state *state, struct scope *scope, } initd[i] = 1; - args = args->next; + args = args->n; i++; } @@ -1585,108 +1478,70 @@ static int init_struct(struct act_state *state, struct scope *scope, static int actualize_struct_init_cast(struct act_state *state, struct scope *scope, - struct ast_node *init, - struct ast_node *actual) + struct ast *init, + struct type *type) { - struct ast_node *def = AST_STRUCT_TYPE(actual).def; + struct ast *def = type->d; return init_struct(state, scope, def, init); } static int actualize_init_cast(struct act_state *state, - struct scope *scope, struct ast_node *init, - struct ast_node *type) + struct scope *scope, struct ast *init, + struct type *type) { - if (AST_TYPE(type).kind == AST_TYPE_STRUCT) + if (type->k == TYPE_STRUCT) return actualize_struct_init_cast(state, scope, init, type); - semantic_error(scope->fctx, type, + type_error(scope->fctx, type, "type is not a struct"); return -1; } -static int proc_pointer(struct ast_node *type) -{ - if (type->_type.kind != AST_TYPE_POINTER) - return 0; - - struct ast_node *next = type->_type.next; - if (next->_type.kind != AST_TYPE_SIGN) - return 0; - - return 1; -} - -static int proc_choice(struct ast_node *expr, struct ast_node *type) -{ - if (expr->node_type != AST_ID) - return 0; - - if (!proc_pointer(type)) - return 0; - - return 1; -} - -/* still slightly unsure about this, but hey ho */ -static int match_proc(struct act_state *state, struct scope *scope, - struct ast_node *cast) -{ - (void)(state); - semantic_error(scope->fctx, cast, - "procedure signature casts not yet implemented"); - return -1; -} - static int actualize_cast(struct act_state *state, - struct scope *scope, struct ast_node *cast) + struct scope *scope, struct ast *cast) { - assert(cast->node_type == AST_CAST); - struct ast_node *expr = cast->_cast.expr; - struct ast_node *type = cast->_cast.type; + assert(cast->k == AST_CAST); + struct ast *expr = cast_expr(cast); + struct type *type = cast_type(cast); - if (actualize(state, scope, type)) + if (actualize_type(state, scope, type)) return -1; - if (proc_choice(expr, type)) { - set_type(cast, type); - return match_proc(state, scope, cast); - } - if (actualize(state, scope, expr)) return -1; - if (expr->node_type == AST_INIT) { + if (expr->k == AST_INIT) { set_type(cast, type); return actualize_init_cast(state, scope, expr, type); } - if (types_match(expr->type, type)) { + if (types_match(expr->t, type)) { set_type(cast, type); return 0; } - if (integral_type(expr->type) && integral_type(type)) { + if (integral_type(expr->t) && integral_type(type)) { set_type(cast, type); return 0; } - if (pointer_type(expr->type) && pointer_type(type)) { + if (pointer_type(expr->t) && pointer_type(type)) { set_type(cast, type); return 0; } - if (pointer_conversion(expr->type, type) - || pointer_conversion(type, expr->type)) { + if (pointer_conversion(expr->t, type) + || pointer_conversion(type, expr->t)) { set_type(cast, type); return 0; } /* TODO: arrays? */ - char *left_type = type_str(expr); + char *left_type = type_str(expr->t); char *right_type = type_str(type); - semantic_error(scope->fctx, cast, "illegal cast (%s vs %s)", + semantic_error(scope->fctx, cast, "illegal cast: %s vs %s", left_type, right_type); free(left_type); free(right_type); @@ -1694,17 +1549,22 @@ static int actualize_cast(struct act_state *state, } static int actualize_const(struct act_state *state, struct scope *scope, - struct ast_node *cons) + struct ast *cons) { UNUSED(state); - assert(cons->node_type == AST_CONST); - if (AST_CONST(cons).kind == AST_CONST_INTEGER) - cons->type = i27_type(scope); + if (cons->k == AST_CONST_INT) + cons->t = i27_type(scope); + + else if (cons->k == AST_CONST_CHAR) + cons->t = i9_type(scope); - else if (AST_CONST(cons).kind == AST_CONST_STRING) - cons->type = str_type(scope); + else if (cons->k == AST_CONST_BOOL) + cons->t = bool_type(scope); - if (cons->type) + else if (cons->k == AST_CONST_STR) + cons->t = str_type(scope); + + if (cons->t) return 0; semantic_error(scope->fctx, cons, "unimplemented constant"); @@ -1712,13 +1572,13 @@ static int actualize_const(struct act_state *state, struct scope *scope, } static int actualize_alias(struct act_state *state, struct scope *scope, - struct ast_node *alias) + struct ast *alias) { /* I shall have to think about things, as currently very deeply nested * aliases might be a bit cumbersome to work with. Still, this works * well enough I suppose. */ - assert(alias->node_type == AST_ALIAS); - if (actualize(state, scope, AST_ALIAS(alias).type)) { + assert(alias->k == AST_ALIAS_DEF); + if (actualize_type(state, scope, alias_type(alias))) { /* usually we don't want to output errors upon errors, but this * is likely a useful message as it might show where a loop is * occuring */ @@ -1731,9 +1591,9 @@ static int actualize_alias(struct act_state *state, struct scope *scope, } static int actualize_defer(struct act_state *state, - struct scope *scope, struct ast_node *node) + struct scope *scope, struct ast *node) { - struct ast_node *expr = node->_defer.expr; + struct ast *expr = defer_expr(node); /* TODO: should the actualization only happen when the defers are * called? */ if (actualize(state, scope, expr)) @@ -1742,61 +1602,54 @@ static int actualize_defer(struct act_state *state, if (push_defer(state, expr)) return -1; - node->type = void_type(); + node->t = void_type(); return 0; } static int actualize_return(struct act_state *state, struct scope *scope, - struct ast_node *node) + struct ast *node) { act_set_flags(state, ACT_HAS_RETURN); - struct ast_node *expr = node->_return.expr; + struct ast *expr = return_expr(node); if (expr) { if (actualize(state, scope, expr)) return -1; - set_type(node, expr->type); + set_type(node, expr->t); } else { - node->type = void_type(); + node->t = void_type(); } assert(state->cur_proc); - struct ast_node *cur_proc = state->cur_proc; - struct ast_node *sign = AST_PROC(cur_proc).sign; - struct ast_node *ret = AST_SIGN_TYPE(sign).ret; - if (!types_match(node->type, ret)) { - char *rt = type_str(ret); - char *et = type_str(node); - semantic_error(scope->fctx, node, - "return type mismatch: %s", et); - - semantic_info(scope->fctx, ret, - "vs %s", rt); - free(rt); - free(et); + struct ast *cur_proc = state->cur_proc; + struct type *rtype = proc_rtype(cur_proc); + if (!types_match(node->t, rtype)) { + /* hmm, should this be "return type mismatch? */ + type_mismatch(scope, node, rtype, node->t); return -1; } if (state->defer_stack) { - AST_RETURN(node).defers = clone_defers(state, NULL); - if (!node->_return.defers) { + return_defers(node) = clone_defers(state, NULL); + if (!return_defers(node)) { internal_error("failed cloning return defers"); return -1; } } + return 0; } /* Still slightly unsure if this works in all cases, but a good start * nonetheless. */ -static void actualize_goto_defer(struct ast_node *got, struct ast_node *label) +static void actualize_goto_defer(struct ast *got, struct ast *label) { - struct ast_node *goto_defers = AST_GOTO(got).defers; - struct ast_node *label_defers = AST_LABEL(label).defers; + struct ast *goto_defers = goto_defers(got); + struct ast *label_defers = label_defers(label); /* since we're dealing with singly linked lists, keep a reference to one * node before the current goto defer. */ - struct ast_node *prev_defer = NULL; + struct ast *prev_defer = NULL; /* this goto has a defined label */ ast_set_flags(got, AST_FLAG_ACTUAL); @@ -1808,12 +1661,12 @@ static void actualize_goto_defer(struct ast_node *got, struct ast_node *label) /* find first common defer statement */ while (goto_len > label_len) { prev_defer = goto_defers; - goto_defers = goto_defers->next; + goto_defers = goto_defers->n; goto_len--; } while (label_len > goto_len) { - label_defers = label_defers->next; + label_defers = label_defers->n; label_len--; } @@ -1823,15 +1676,15 @@ static void actualize_goto_defer(struct ast_node *got, struct ast_node *label) * unlikely that goto stuff would be a major bottleneck. */ while (!equiv_nodes(goto_defers, label_defers)) { prev_defer = goto_defers; - label_defers = label_defers->next; - goto_defers = goto_defers->next; + label_defers = label_defers->n; + goto_defers = goto_defers->n; } /* only the defers below the common defer should be executed by the goto */ if (goto_defers) { /* fuck, I actually need the one previous to this */ - assert(prev_defer->next == goto_defers); - prev_defer->next = NULL; + assert(prev_defer->n == goto_defers); + prev_defer->n = NULL; } /* nothing to do */ @@ -1839,16 +1692,16 @@ static void actualize_goto_defer(struct ast_node *got, struct ast_node *label) static int actualize_goto(struct act_state *state, struct scope *scope, - struct ast_node *node) + struct ast *node) { - assert(node->node_type == AST_GOTO); + assert(node->k == AST_GOTO); push_goto(state, node); /* clone all defers as we don't know where the label might be */ - node->_goto.defers = clone_defers(state, NULL); - node->type = void_type(); + goto_defers(node) = clone_defers(state, NULL); + node->t = void_type(); - struct ast_node *label = find_label(state, node->_goto.label); + struct ast *label = find_label(state, goto_label(node)); /* this is a jump backwards, i.e. we can already do it */ if (label) actualize_goto_defer(node, label); @@ -1858,24 +1711,24 @@ static int actualize_goto(struct act_state *state, struct scope *scope, } static void actualize_goto_defers(struct act_state *state, - struct ast_node *label) + struct ast *label) { struct act_stack *prev = state->goto_stack, *cur; if (prev) do { cur = prev->next; - struct ast_node *got = prev->node; - if (equiv_nodes(AST_GOTO(got).label, label)) + struct ast *got = prev->node; + if (same_id(goto_label(got), label_id(label))) actualize_goto_defer(got, label); } while ((prev = cur)); } static int actualize_label(struct act_state *state, struct scope *scope, - struct ast_node *node) + struct ast *node) { - assert(node->node_type == AST_LABEL); - struct ast_node *prev = find_label(state, node); + assert(node->k == AST_LABEL); + struct ast *prev = find_label(state, label_id(node)); if (prev) { semantic_error(scope->fctx, node, "label redefined"); semantic_info(scope->fctx, prev, "previous definition"); @@ -1884,60 +1737,65 @@ static int actualize_label(struct act_state *state, struct scope *scope, push_label(state, node); /* clone all defers */ - node->_label.defers = clone_defers(state, NULL); - node->type = void_type(); + label_defers(node) = clone_defers(state, NULL); + node->t = void_type(); actualize_goto_defers(state, node); return 0; } static int actualize_unop(struct act_state *state, - struct scope *scope, struct ast_node *node) + struct scope *scope, struct ast *node) { - assert(node->node_type == AST_UNOP); - struct ast_node *expr = AST_UNOP(node).expr; + assert(is_unop(node)); + struct ast *expr = unop_expr(node); if (actualize(state, scope, expr)) return -1; /* generally speaking */ - set_type(node, expr->type); + set_type(node, expr->t); - switch (node->_unop.op) { + switch (node->k) { case AST_DEREF: { - struct ast_node *type = expr->type; - if (AST_TYPE(type).kind != AST_TYPE_POINTER) { - /* TODO: or array */ + struct type *type = expr->t; + if (type->k!= TYPE_PTR) { + /** @todo or array? */ + char *tstr = type_str(type); semantic_error(scope->fctx, expr, - "trying to dereference something that's not a pointer"); + "not a pointer: %s", + tstr); + free(tstr); return -1; } - set_type(node, AST_PTR_TYPE(type).base); - assert(node->type); + set_type(node, ptr_base(type)); + assert(node->t); break; } case AST_REF: { - node->type = gen_type(AST_TYPE_POINTER, NULL, NULL, - NULL_LOC()); - set_type(node->AST_TYPE(type).next, expr->type); + /** @todo array pointer decay? */ + node->t = tgen_ptr(clone_type(expr->t), node->loc); break; } case AST_LNOT: { - if (AST_TYPE(expr->type).kind != AST_TYPE_PRIMITIVE) { + if (is_primitive(expr->t)) { + char *tstr = type_str(expr->t); semantic_error(scope->fctx, node, - "'!' only implemented for primitive types"); + "'!' only implemented for primitive types: %s", + tstr); + free(tstr); return -1; } - if (AST_PRIMITIVE_TYPE(expr->type).type == AST_VOID) { + if (expr->t->k == TYPE_VOID) { semantic_error(scope->fctx, node, "'!' not implemented for void"); return -1; } - node->type = bool_type(scope); + node->t = bool_type(scope); break; } @@ -1950,79 +1808,90 @@ static int actualize_unop(struct act_state *state, } static int actualize_as(struct act_state *state, - struct scope *scope, struct ast_node *as) + struct scope *scope, struct ast *as) { - assert(as->node_type == AST_AS); - struct ast_node *type = AST_AS(as).type; - if (actualize(state, scope, type)) + assert(as->k == AST_AS); + struct type *type = as->t; + if (actualize_type(state, scope, type)) return -1; set_type(as, type); return 0; } -static int _replace_type_id(struct ast_node *node, void *data) -{ - if (!node) - return 0; +struct replace_data { + char *id; + struct type *replacement; +}; - struct ast_node **pair = data; - struct ast_node *id = pair[0]; - struct ast_node *replacement = pair[1]; +static int _replace_type_id(struct type *type, void *data) +{ + struct replace_data *pair = data; + struct type *replacement = pair->replacement; + char *id = pair->id; - if (node->node_type != AST_TYPE) - goto next; + switch (type->k) { + case TYPE_ID: { + replace_type(type, clone_type(replacement)); + break; + } - switch (AST_TYPE(node).kind) { - case AST_TYPE_ID: *node = *clone_ast_node(replacement); break; - case AST_TYPE_TRAIT: { - struct ast_node *def = AST_TRAIT_TYPE(node).def; + case TYPE_TRAIT: { + struct ast *def = type->d; assert(def); - struct ast_node *name = AST_TRAIT(def).id; + + char *name = trait_id(def); if (same_id(id, name)) - *node = *clone_ast_node(replacement); + replace_type(type, clone_type(replacement)); break; } - case AST_TYPE_STRUCT: { - struct ast_node *def = AST_STRUCT_TYPE(node).def; + case TYPE_STRUCT: { + struct ast *def = type->d; assert(def); - struct ast_node *name = AST_STRUCT(def).id; + char *name = struct_id(def); if (same_id(id, name)) - *node = *clone_ast_node(replacement); + replace_type(type, clone_type(replacement)); break; } + default: } -next: - return ast_call_on(_replace_type_id, node, data); + return 0; } -static int replace_type_id(struct ast_node *nodes, struct ast_node *id, - struct ast_node *replacement) +static int _replace_ast_type_id(struct ast *node, void *data) { - assert(replacement->node_type == AST_TYPE); - struct ast_node *pair[2] = {id, replacement}; - return ast_call_on(_replace_type_id, nodes, pair); + if (!node) + return 0; + + return type_visit_list(_replace_type_id, NULL, node->t, data); +} + +static int replace_type_id(struct ast *nodes, char *id, + struct type *replacement) +{ + struct replace_data pair = {id, replacement}; + return ast_visit(_replace_ast_type_id, NULL, 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) + struct ast *node) { - assert(node->node_type == AST_TRAIT); - foreach_node(n, AST_TRAIT(node).body) { + assert(node->k == AST_TRAIT_DEF); + foreach_node(n, trait_body(node)) { /* 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) + if (n->k != AST_PROC_DEF) continue; /* don't actualize prototypes, duh */ - if (!AST_PROC(n).body) + if (!proc_body(n)) continue; if (actualize(state, node->scope, n)) @@ -2033,18 +1902,18 @@ static int actualize_trait(struct act_state *state, struct scope *scope, } static int actualize_struct(struct act_state *state, - struct scope *scope, struct ast_node *node) + struct scope *scope, struct ast *node) { - assert(node->node_type == AST_STRUCT); - foreach_node(n, AST_STRUCT(node).body) { + assert(node->k == AST_STRUCT_DEF); + foreach_node(n, struct_body(node)) { /* 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) + if (n->k != AST_PROC_DEF) continue; /* don't actualize prototypes, duh */ - if (!AST_PROC(n).body) + if (!proc_body(n)) continue; if (actualize(state, node->scope, n)) @@ -2057,23 +1926,23 @@ static int actualize_struct(struct act_state *state, /* could maybe be renamed, but essentially dot in copper works as either * -> or . in C, so allow structures or traits and single level pointers to * structures or traits. */ -static int has_members(struct ast_node *type) +static int has_members(struct type *type) { /* most likely */ - if (AST_TYPE(type).kind == AST_TYPE_STRUCT) + if (type->k == TYPE_STRUCT) return 1; - if (AST_TYPE(type).kind == AST_TYPE_TRAIT) + if (type->k == TYPE_TRAIT) return 1; - if (AST_TYPE(type).kind == AST_TYPE_CONSTRUCT) + if (type->k == TYPE_CONSTRUCT) return 1; return 0; } static int actualize_dot(struct act_state *state, - struct scope *scope, struct ast_node *node) + struct scope *scope, struct ast *node) { /* TODO: handle enums as well, idea is something like * enum whatever {A_FLAG} @@ -2083,19 +1952,18 @@ static int actualize_dot(struct act_state *state, * possibly also if the expr is of type whatever then .A_FLAG just gets * the corresponding constant? **/ - assert(node->node_type == AST_DOT); - struct ast_node *expr = AST_DOT(node).expr; + assert(node->k == AST_DOT); + struct ast *expr = dot_expr(node); if (actualize(state, scope, expr)) return -1; - struct ast_node *id = AST_DOT(node).id; - struct ast_node *type = expr->type; + char *id = dot_id(node); + struct type *type = expr->t; - 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; + struct ast *def = NULL; + switch (type->k) { + case TYPE_TRAIT: + case TYPE_STRUCT: def = type->d; break; default: { char *tstr = type_str(type); semantic_error(scope->fctx, node, @@ -2106,17 +1974,17 @@ static int actualize_dot(struct act_state *state, } } - struct ast_node *exists = scope_find_var(def->scope, id); + struct ast *exists = scope_find_var(def->scope, id); if (exists) { - assert(exists->type); - set_type(node, exists->type); + assert(exists->t); + set_type(node, exists->t); return 0; } exists = scope_find_proc(def->scope, id); if (exists) { - assert(exists->type); - set_type(node, exists->type); + assert(exists->t); + set_type(node, exists->t); return 0; } @@ -2126,42 +1994,33 @@ static int actualize_dot(struct act_state *state, } static int actualize_init(struct act_state *state, - struct scope *scope, struct ast_node *node) + struct scope *scope, struct ast *node) { - assert(node->node_type == AST_INIT); + assert(node->k == AST_INIT); /* for now just do the types, the named stuff will be checked later */ /* TODO: how to make sure all members are initialized? */ - int ret = 0; - enum act_flags old_flags = state->flags; - ret = actualize(state, scope, node->_init.body); - state->flags = old_flags; - return ret; + return actualize_list(state, scope, init_body(node)); } static int actualize_assign(struct act_state *state, struct scope *scope, - struct ast_node *node) + struct ast *node) { - assert(node->node_type == AST_ASSIGN); - struct ast_node *to = node->_assign.to; + assert(node->k == AST_ASSIGN); + struct ast *to = assign_to(node); if (actualize(state, scope, to)) return -1; - struct ast_node *from = node->_assign.from; - if (actualize(state, scope, from)) + struct ast *from = assign_from(node); + if (actualize_list(state, scope, from)) return -1; - if (from->node_type == AST_INIT) { - set_type(node, to->type); - return actualize_init_cast(state, scope, from, to->type); + if (from->k == AST_INIT) { + set_type(node, to->t); + return actualize_init_cast(state, scope, from, to->t); } - if (!types_match(to->type, from->type)) { - char *tostr = type_str(to->type); - char *fromstr = type_str(from->type); - semantic_error(scope->fctx, node, "type mismatch (%s vs %s)", - tostr, fromstr); - free(tostr); - free(fromstr); + if (!types_match(to->t, from->t)) { + type_mismatch(scope, node, to->t, from->t); return -1; } @@ -2172,84 +2031,77 @@ static int actualize_assign(struct act_state *state, struct scope *scope, return -1; } - set_type(node, to->type); + set_type(node, to->t); return 0; } static int actualize_fetch(struct act_state *state, struct scope *scope, - struct ast_node *fetch) + struct ast *fetch) { - assert(fetch->node_type == AST_FETCH); - struct ast_node *type = fetch->_fetch.type; - if (actualize(state, scope, type)) + assert(fetch->k == AST_FETCH); + struct type *type = fetch_type(fetch); + if (actualize_type(state, scope, type)) return -1; - if (type->_type.kind != AST_TYPE_ENUM) { - semantic_error(scope->fctx, type, "type is not an enum"); + if (type->k != TYPE_ENUM) { + type_error(scope->fctx, type, "type is not an enum"); return -1; } - struct ast_node *id = fetch->_fetch.id; - struct ast_node *def = - file_scope_find_type(scope, AST_ID_TYPE(type).id); + char *id = fetch_id(fetch); + struct ast *def = type->d; assert(def); - struct ast_node *member = lookup_enum_member(def, id); + struct ast *member = lookup_enum_member(def, id); if (!member) { char *estr = type_str(type); - semantic_error(scope->fctx, id, "no such member in enum %s"); + semantic_error(scope->fctx, fetch, "no such member in enum %s"); free(estr); return -1; } - set_type(fetch, def->type); + set_type(fetch, def->t); return 0; } static int actualize_enum(struct act_state *state, struct scope *scope, - struct ast_node *node) + struct ast *node) { - assert(node->node_type == AST_ENUM); - struct ast_node *type = node->_enum.type; + assert(node->k == AST_ENUM_DEF); + struct type *type = enum_type(node); struct scope *enum_scope = node->scope; /* TODO: here we could save space by choosing the smallest type that * fits */ if (!type) { type = i27_type(scope); - node->_enum.type = type; - } else if (actualize(state, enum_scope, type)) + node->t = type; + } else if (actualize_type(state, enum_scope, type)) return -1; long long counter = 0; - node->type = type; - struct ast_node *members = node->_enum.body; + node->t = type; + struct ast *members = enum_body(node); while (members) { set_type(members, type); - if (members->_val.val) { - struct ast_node *val = members->_val.val; + if (val_val(members)) { + struct ast *val = val_val(members); if (actualize(state, enum_scope, val)) return -1; - if (val->node_type != AST_CONST) { + if (val->k != AST_CONST_INT) { semantic_error(scope->fctx, members, "unable to process nonconstant expression"); return -1; } - if (val->_const.kind != AST_CONST_INTEGER) { - semantic_error(scope->fctx, members, - "not expandable to an integer constant"); - return -1; - } - - counter = val->_const.integer; + counter = int_val(val); } else { - members->_val.val = gen_int(counter, NULL_LOC()); + val_val(members) = gen_const_int(counter, NULL_LOC()); } - members = members->next; + members = members->n; counter++; } @@ -2258,21 +2110,21 @@ static int actualize_enum(struct act_state *state, struct scope *scope, } static int actualize_if(struct act_state *state, struct scope *scope, - struct ast_node *node) + struct ast *node) { - assert(node->node_type == AST_IF); - if (actualize(state, scope, AST_IF(node).cond)) + assert(node->k == AST_IF); + if (actualize(state, scope, if_cond(node))) return -1; - if (actualize(state, scope, AST_IF(node).body)) + if (actualize(state, scope, if_body(node))) return -1; - if (actualize(state, scope, AST_IF(node).els)) + if (actualize(state, scope, if_else(node))) return -1; if (ast_flags(node, AST_FLAG_DOEXPR)) { - struct ast_node *tt = ast_last_node(AST_IF(node).body)->type; - struct ast_node *ft = ast_last_node(AST_IF(node).els)->type; + struct type *tt = ast_last(if_body(node))->t; + struct type *ft = ast_last(if_else(node))->t; if (!types_match(tt, ft)) { semantic_error(scope->fctx, node, "mismatched if/else body values"); @@ -2283,143 +2135,124 @@ static int actualize_if(struct act_state *state, struct scope *scope, return 0; } - node->type = void_type(); + node->t = void_type(); return 0; } static int actualize_for(struct act_state *state, struct scope *scope, - struct ast_node *node) + struct ast *node) { - assert(node->node_type == AST_FOR); - if (actualize(state, scope, AST_FOR(node).pre)) + assert(node->k == AST_FOR); + if (actualize_list(state, scope, for_pre(node))) return -1; - if (actualize(state, scope, AST_FOR(node).post)) + if (actualize_list(state, scope, for_post(node))) return -1; - if (actualize(state, scope, AST_FOR(node).cond)) + if (actualize_list(state, scope, for_cond(node))) return -1; - if (actualize(state, scope, AST_FOR(node).body)) + if (actualize_list(state, scope, for_body(node))) return -1; - node->type = void_type(); + node->t = void_type(); return 0; } -static int actualize(struct act_state *state, struct scope *scope, - struct ast_node *node) +static int actualize_comparison(struct act_state *state, struct scope *scope, struct ast *node) { - int ret = 0; - if (!node) - return ret; - - if (!node->scope) - node->scope = scope; + assert(is_comparison(node)); + struct ast *left = comparison_left(node); + struct ast *right = comparison_right(node); - /* 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: - ret |= actualize_proc(state, scope, node); - break; + if (actualize(state, scope, left)) + return -1; - case AST_TRAIT: ret |= actualize_trait(state, scope, node); break; - case AST_ALIAS: ret |= actualize_alias(state, scope, node); break; - case AST_MACRO_CONSTRUCT: ret |= actualize_macro_construct(state, scope, - node); break; - case AST_MACRO_EXPAND: ret |= - actualize_macro_expand(state, scope, node); break; - case AST_CALL: ret |= actualize_call(state, scope, node); break; - case AST_BINOP: ret |= actualize_binop(state, scope, node); break; - case AST_BLOCK: ret |= actualize_block(state, scope, node); break; - case AST_ID: ret |= actualize_id(state, scope, node); break; - case AST_VAR: ret |= actualize_var(state, scope, node); break; - case AST_TYPE: ret |= actualize_type(state, scope, node); break; - case AST_EMPTY: ret |= actualize_empty(state, scope, node); break; - case AST_CAST: ret |= actualize_cast(state, scope, node); break; - case AST_CONST: ret |= actualize_const(state, scope, node); break; - case AST_DEFER: ret |= actualize_defer(state, scope, node); break; - case AST_RETURN: ret |= actualize_return(state, scope, node); break; - case AST_GOTO: ret |= actualize_goto(state, scope, node); break; - case AST_LABEL: ret |= actualize_label(state, scope, node); break; - case AST_UNOP: ret |= actualize_unop(state, scope, node); break; - case AST_AS: ret |= actualize_as(state, scope, node); break; - case AST_STRUCT: ret |= actualize_struct(state, scope, node); break; - case AST_DOT: ret |= actualize_dot(state, scope, node); break; - case AST_INIT: ret |= actualize_init(state, scope, node); break; - case AST_ASSIGN: ret |= actualize_assign(state, scope, node); break; - case AST_FETCH: ret |= actualize_fetch(state, scope, node); break; - case AST_ENUM: ret |= actualize_enum(state, scope, node); break; - case AST_IF: ret |= actualize_if(state, scope, node); break; - case AST_FOR: ret |= actualize_for(state, scope, node); break; + if (actualize(state, scope, right)) + return -1; - default: - /* more like internal_error, maybe? */ - semantic_error(scope->fctx, node, - "unimplemented actualization"); - break; + if (!is_primitive(left->t)) { + type_error(scope->fctx, left->t, "primitive type required"); + return -1; } - return ret; -} -int actualize_main(struct scope *root) -{ - struct ast_node *main_id = gen_id(strdup("main"), NULL_LOC()); - - struct act_state state = {0}; + if (!is_primitive(right->t)) { + type_error(scope->fctx, right->t, "primitive type required"); + return -1; + } - /* skip checking signature for now */ - struct ast_node *main = file_scope_find_proc(root, main_id); - if (!main) { - /* libraries are not really compilable... */ - error("no main"); + if (!types_match(left->t, right->t)) { + type_mismatch(scope, node, left->t, right->t); return -1; } - int ret = actualize(&state, root, main); - destroy_act_state(&state); - return ret; + + set_type(node, bool_type(scope)); + return 0; } -void replace_type(struct ast_node *type, struct ast_node *from, - struct ast_node *to) +static int actualize(struct act_state *state, struct scope *scope, + struct ast *node) { - if (!type) - return; + if (!node) + return 0; - assert(type->node_type == AST_TYPE); - assert(from->node_type == AST_TYPE); - assert(to->node_type == AST_TYPE); + if (!node->scope) + node->scope = scope; - if (types_match(type, from)) { - assert(type->_type.next == NULL); - struct ast_node *clone = clone_ast_node(to); - *type = *clone; - return; + if (is_unop(node)) + return actualize_unop(state, scope, node); + + if (is_binop(node)) + return actualize_binop(state, scope, node); + + if (is_comparison(node)) + return actualize_comparison(state, scope, node); + + if (is_const(node)) + return actualize_const(state, scope, node); + + switch (node->k) { + case AST_PROC_DEF: return actualize_proc(state, scope, node); + case AST_TRAIT_DEF: return actualize_trait(state, scope, node); + case AST_ALIAS_DEF: return actualize_alias(state, scope, node); + case AST_ENUM_DEF: return actualize_enum(state, scope, node); + case AST_MACRO_DEF: return actualize_macro_def(state, scope, node); + case AST_STRUCT_DEF: return actualize_struct(state, scope, node); + case AST_VAR_DEF: return actualize_var(state, scope, node); + case AST_CALL: return actualize_call(state, scope, node); + case AST_BLOCK: return actualize_block(state, scope, node); + case AST_ID: return actualize_id(state, scope, node); + case AST_EMPTY: return actualize_empty(state, scope, node); + case AST_CAST: return actualize_cast(state, scope, node); + case AST_DEFER: return actualize_defer(state, scope, node); + case AST_RETURN: return actualize_return(state, scope, node); + case AST_GOTO: return actualize_goto(state, scope, node); + case AST_LABEL: return actualize_label(state, scope, node); + case AST_AS: return actualize_as(state, scope, node); + case AST_DOT: return actualize_dot(state, scope, node); + case AST_INIT: return actualize_init(state, scope, node); + case AST_ASSIGN: return actualize_assign(state, scope, node); + case AST_FETCH: return actualize_fetch(state, scope, node); + case AST_IF: return actualize_if(state, scope, node); + case AST_FOR: return actualize_for(state, scope, node); + case AST_MACRO_EXPAND: return actualize_macro_expand(state, scope, node); + default: + /* more like internal_error, maybe? */ + semantic_error(scope->fctx, node, + "unimplemented actualization"); + return -1; } - replace_type(type->_type.next, from, to); + return 0; } -void replace_param_types(struct ast_node *param, struct ast_node *param_type, - struct ast_node *arg_type) +static int actualize_list(struct act_state *state, struct scope *scope, + struct ast *l) { - if (arg_type->_type.kind == AST_TYPE_TRAIT) { - replace_param_types(param, param_type, arg_type); - return; + foreach_node(n, l) { + if (actualize(state, scope, n)) + return -1; } - while (param) { - replace_type(param->type, param_type, arg_type); - param = param->next; - } + return 0; } diff --git a/src/ast.c b/src/ast.c index e77714c..18a98bc 100644 --- a/src/ast.c +++ b/src/ast.c @@ -15,585 +15,162 @@ #include #include +#include #include -static struct { - size_t n; - size_t s; - struct ast_node **v; -} ast_nodes = {0}; +static struct vec nodes = {0}; +static struct vec types = {0}; -static void destroy_ast_node(struct ast_node *node) +static void destroy_ast_node(struct ast *n) { - if (!node) + if (!n) return; - switch (node->node_type) { - case AST_ID: free((void *)AST_ID(node).id); break; - case AST_CONST: - if (AST_CONST(node).kind == AST_CONST_STRING) - free((void *)AST_CONST(node).str); - break; - default: - } - - free(node); -} - -void destroy_ast_nodes() -{ - for (size_t i = 0; i < ast_nodes.n; ++i) - destroy_ast_node(ast_nodes.v[i]); + if (n->s) + free(n->s); - free(ast_nodes.v); + free(n); } -/* eventually we might want to pass in a context or something */ -static struct ast_node *create_ast_node() +static void destroy_type(struct type *n) { - if (ast_nodes.s == 0) { - ast_nodes.s = 1; - ast_nodes.n = 0; - ast_nodes.v = calloc(1, sizeof(struct ast_node *)); - } - - else if (ast_nodes.n >= ast_nodes.s) { - ast_nodes.s *= 2; - ast_nodes.v = - realloc(ast_nodes.v, - ast_nodes.s * sizeof(struct ast_node *)); - } - - struct ast_node *n = calloc(1, sizeof(struct ast_node)); - ast_nodes.v[ast_nodes.n++] = n; - return n; -} - -/** @todo alloc should maybe also keep track of all nodes in a vector or - * something and mass free all AST at a time to keep my sanity */ -#define ALLOC_NODE(n, type) \ - struct ast_node *n = create_ast_node(); \ - if (!n) { \ - fprintf(stderr, "failed allocating" type "\n"); \ - return NULL; \ - } - -#define DESTROY_LIST(x) \ - { \ - struct ast_node *prev = x, *cur; \ - if (prev) \ - do { \ - cur = prev->next; \ - destroy_ast_node(prev); \ - } while ((prev = cur)); \ - } - -struct ast_node *gen_arr_access(struct ast_node *base, struct ast_node *idx, - struct src_loc loc) -{ - ALLOC_NODE(n, "arr_access"); - n->node_type = AST_ARR_ACCESS; - AST_ARR_ACCESS(n).base = base; - AST_ARR_ACCESS(n).idx = idx; - n->loc = loc; - return n; -} - -struct ast_node *gen_macro_expand(struct ast_node *id, struct ast_node *args, - struct src_loc loc) -{ - ALLOC_NODE(n, "macro_expand"); - n->node_type = AST_MACRO_EXPAND; - AST_MACRO_EXPAND(n).id = id; - AST_MACRO_EXPAND(n).args = args; - n->loc = loc; - return n; -} - -struct ast_node *gen_type_expand(struct ast_node *id, - struct ast_node *args, - struct src_loc loc) -{ - ALLOC_NODE(n, "type_expand"); - n->node_type = AST_TYPE_EXPAND; - AST_TYPE_EXPAND(n).id = id; - AST_TYPE_EXPAND(n).args = args; - n->loc = loc; - return n; -} - -struct ast_node *gen_binop(enum ast_binops op, - struct ast_node *left, - struct ast_node *right, - struct src_loc loc) -{ - ALLOC_NODE(n, "binop"); - n->node_type = AST_BINOP; - AST_BINOP(n).op = op; - AST_BINOP(n).left = left; - AST_BINOP(n).right = right; - n->loc = loc; - return n; -} - -struct ast_node *gen_unop(enum ast_unops op, struct ast_node *expr, - struct src_loc loc) -{ - ALLOC_NODE(n, "unop"); - n->node_type = AST_UNOP; - AST_UNOP(n).op = op; - AST_UNOP(n).expr = expr; - n->loc = loc; - return n; -} - -struct ast_node *gen_call(struct ast_node *expr, struct ast_node *args, - struct src_loc loc) -{ - ALLOC_NODE(n, "call"); - n->node_type = AST_CALL; - AST_CALL(n).expr = expr; - AST_CALL(n).args = args; - n->loc = loc; - return n; -} - -struct ast_node *gen_id(const char *id, struct src_loc loc) -{ - ALLOC_NODE(n, "id"); - n->node_type = AST_ID; - AST_ID(n).id = id; - n->loc = loc; - return n; -} - -struct ast_node *gen_assign(struct ast_node *to, struct ast_node *from, - struct src_loc loc) -{ - ALLOC_NODE(n, "assign"); - n->node_type = AST_ASSIGN; - AST_ASSIGN(n).to = to; - AST_ASSIGN(n).from = from; - n->loc = loc; - return n; -} - -struct ast_node *gen_init(struct ast_node *body, struct src_loc loc) -{ - ALLOC_NODE(n, "struct init"); - n->node_type = AST_INIT; - AST_INIT(n).body = body; - n->loc = loc; - return n; -} - -struct ast_node *gen_int(long long integer, struct src_loc loc) -{ - ALLOC_NODE(n, "int"); - n->node_type = AST_CONST; - AST_CONST(n).kind = AST_CONST_INTEGER; - AST_CONST(n).integer = integer; - n->loc = loc; - return n; -} - -struct ast_node *gen_string(const char *str, struct src_loc loc) -{ - ALLOC_NODE(n, "string"); - n->node_type = AST_CONST; - AST_CONST(n).kind = AST_CONST_STRING; - AST_CONST(n).str = str; - n->loc = loc; - return n; -} - -struct ast_node *gen_while(struct ast_node *cond, struct ast_node *body, - struct src_loc loc) -{ - ALLOC_NODE(n, "while"); - n->node_type = AST_WHILE; - AST_WHILE(n).cond = cond; - AST_WHILE(n).body = body; - n->loc = loc; - return n; -} - -struct ast_node *gen_for(struct ast_node *pre, struct ast_node *cond, - struct ast_node *post, struct ast_node *body, - struct src_loc loc) -{ - ALLOC_NODE(n, "for"); - n->node_type = AST_FOR; - AST_FOR(n).pre = pre; - AST_FOR(n).cond = cond; - AST_FOR(n).post = post; - AST_FOR(n).body = body; - n->loc = loc; - return n; -} - -struct ast_node *gen_return(struct ast_node *expr, struct src_loc loc) -{ - ALLOC_NODE(n, "return"); - n->node_type = AST_RETURN; - AST_RETURN(n).expr = expr; - n->loc = loc; - return n; -} - -struct ast_node *gen_goto(struct ast_node *label, struct src_loc loc) -{ - ALLOC_NODE(n, "goto"); - n->node_type = AST_GOTO; - AST_GOTO(n).label = label; - n->loc = loc; - return n; -} - -struct ast_node *gen_dot(struct ast_node *expr, struct ast_node *id, - struct src_loc loc) -{ - ALLOC_NODE(n, "dot"); - n->node_type = AST_DOT; - AST_DOT(n).expr = expr; - AST_DOT(n).id = id; - n->loc = loc; - return n; -} - -struct ast_node *gen_label(struct ast_node *id, struct src_loc loc) -{ - ALLOC_NODE(n, "label"); - n->node_type = AST_LABEL; - AST_LABEL(n).id = id; - n->loc = loc; - return n; -} - -struct ast_node *gen_ctrl(enum ast_ctrl_kind kind, struct src_loc loc) -{ - ALLOC_NODE(n, "ctrl"); - n->node_type = AST_CTRL; - AST_CTRL(n).kind = kind; - n->loc = loc; - return n; -} - -struct ast_node *gen_fetch(struct ast_node *id, struct ast_node *type, - struct src_loc loc) -{ - ALLOC_NODE(n, "fetch"); - n->node_type = AST_FETCH; - AST_FETCH(n).id = id; - AST_FETCH(n).type = type; - n->loc = loc; - return n; -} - -struct ast_node *gen_macro_construct(struct ast_node *id, - struct ast_node *params, - struct ast_node *body, - struct src_loc loc) -{ - ALLOC_NODE(n, "macro_construct"); - n->node_type = AST_MACRO_CONSTRUCT; - AST_MACRO_CONSTRUCT(n).id = id; - AST_MACRO_CONSTRUCT(n).params = params; - AST_MACRO_CONSTRUCT(n).body = body; - n->loc = loc; - return n; -} - -struct ast_node *gen_if(struct ast_node *cond, struct ast_node *body, - struct ast_node *els, struct src_loc loc) -{ - ALLOC_NODE(n, "if"); - n->node_type = AST_IF; - AST_IF(n).cond = cond; - AST_IF(n).body = body; - AST_IF(n).els = els; - n->loc = loc; - return n; -} - -struct ast_node *gen_switch(struct ast_node *cond, struct ast_node *cases, - struct src_loc loc) -{ - ALLOC_NODE(n, "switch"); - n->node_type = AST_SWITCH; - AST_SWITCH(n).cond = cond; - AST_SWITCH(n).cases = cases; - n->loc = loc; - return n; -} + if (!n) + return; -struct ast_node *gen_case(struct ast_node *cond, struct ast_node *body, - struct src_loc loc) -{ - ALLOC_NODE(n, "case"); - /* TODO: a macro to map proc name to node type would make sure I don't - * make any dumb mixups... */ - n->node_type = AST_CASE; - AST_CASE(n).cond = cond; - AST_CASE(n).body = body; - /* TODO: where should I check the fallthrough flag? In the - * actualization stage, I guess */ - n->loc = loc; - return n; -} + if (n->id) + free(n->id); -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; + free(n); } -struct ast_node *gen_type(enum ast_type_kind kind, - struct ast_node *t0, - struct ast_node *t1, - struct src_loc loc) +void destroy_ast_nodes() { - ALLOC_NODE(n, "type"); - n->node_type = AST_TYPE; - AST_TYPE(n).kind = kind; - n->loc = loc; - - switch (kind) { - case AST_TYPE_TRAIT: - AST_TRAIT_TYPE(n).def = t0; - break; - - case AST_TYPE_ID: - AST_ID_TYPE(n).id = t0; - break; - - case AST_TYPE_CONSTRUCT: - AST_CONSTRUCT_TYPE(n).id = t0; - AST_CONSTRUCT_TYPE(n).args = t1; - break; - - case AST_TYPE_ARR: - AST_ARR_TYPE(n).size = t0; - AST_ARR_TYPE(n).base = t1; - break; - - case AST_TYPE_POINTER: - AST_PTR_TYPE(n).base = t0; - break; - - case AST_TYPE_STRUCT: - AST_STRUCT_TYPE(n).def = t0; - break; - - case AST_TYPE_ENUM: - AST_ENUM_TYPE(n).def = t0; - break; - - case AST_TYPE_SIGN: - AST_SIGN_TYPE(n).params = t0; - AST_SIGN_TYPE(n).ret = t1; - break; - - default: abort(); + foreach_vec(ni, nodes) { + struct ast *n = vect_at(struct ast *, nodes, ni); + destroy_ast_node(n); } - return n; -} - -struct ast_node *gen_block(struct ast_node *body, struct src_loc loc) -{ - ALLOC_NODE(n, "block"); - n->node_type = AST_BLOCK; - AST_BLOCK(n).body = body; - n->loc = loc; - return n; -} - -struct ast_node *gen_sizeof(struct ast_node *expr, struct src_loc loc) -{ - ALLOC_NODE(n, "sizeof"); - n->node_type = AST_SIZEOF; - AST_SIZEOF(n).expr = expr; - n->loc = loc; - return n; -} - -struct ast_node *gen_as(struct ast_node *type, struct src_loc loc) -{ - ALLOC_NODE(n, "as"); - n->node_type = AST_AS; - AST_AS(n).type = type; - n->loc = loc; - return n; + vec_destroy(&nodes); } -struct ast_node *gen_defer(struct ast_node *expr, struct src_loc loc) +void destroy_types() { - ALLOC_NODE(n, "defer"); - n->node_type = AST_DEFER; - AST_DEFER(n).expr = expr; - n->loc = loc; - return n; -} - -void destroy_defer(struct ast_node *defer) -{ - assert(defer->node_type == AST_DEFER); - destroy_ast_node(AST_DEFER(defer).expr); - free(defer); -} + foreach_vec(ti, types) { + struct type *t = vect_at(struct type *, types, ti); + destroy_type(t); + } -struct ast_node *gen_var(struct ast_node *id, struct ast_node *type, - struct ast_node *init, struct src_loc loc) -{ - ALLOC_NODE(n, "var"); - n->node_type = AST_VAR; - AST_VAR(n).id = id; - AST_VAR(n).type = type; - AST_VAR(n).init = init; - n->loc = loc; - return n; + vec_destroy(&types); } -struct ast_node *gen_proc(struct ast_node *id, struct ast_node *sign, - struct ast_node *body, struct src_loc loc) +void destroy_allocs() { - ALLOC_NODE(n, "proc"); - n->node_type = AST_PROC; - AST_PROC(n).id = id; - AST_PROC(n).sign = sign; - AST_PROC(n).body = body; - n->loc = loc; - return n; + destroy_ast_nodes(); + destroy_types(); } -struct ast_node *gen_struct(struct ast_node *id, - struct ast_node *generics, struct ast_node *body, - struct src_loc loc) +static struct ast *create_empty_ast() { - ALLOC_NODE(n, "struct"); - n->node_type = AST_STRUCT; - AST_STRUCT(n).id = id; - AST_STRUCT(n).generics = generics; - AST_STRUCT(n).body = body; - n->loc = loc; - return n; -} + if (vec_uninit(nodes)) { + nodes = vec_create(sizeof(struct ast *)); + } -struct ast_node *gen_enum(struct ast_node *id, struct ast_node *type, - struct ast_node *body, struct src_loc loc) -{ - ALLOC_NODE(n, "enum"); - n->node_type = AST_ENUM; - AST_ENUM(n).id = id; - AST_ENUM(n).type = type; - AST_ENUM(n).body = body; - n->loc = loc; + struct ast *n = calloc(1, sizeof(struct ast)); + /* just to be safe */ + n->k = AST_EMPTY; + vect_append(struct ast *, nodes, &n); return n; } -struct ast_node *gen_cast(struct ast_node *expr, struct ast_node *type, - struct src_loc loc) +static struct type *create_empty_type() { - ALLOC_NODE(n, "cast"); - n->node_type = AST_CAST; - AST_CAST(n).expr = expr; - AST_CAST(n).type = type; - n->loc = loc; - return n; -} + if (vec_uninit(types)) { + types = vec_create(sizeof(struct type *)); + } -struct ast_node *gen_val(struct ast_node *id, struct ast_node *val, - struct src_loc loc) -{ - ALLOC_NODE(n, "val"); - n->node_type = AST_VAL; - AST_VAL(n).id = id; - AST_VAL(n).val = val; - n->loc = loc; + struct type *n = calloc(1, sizeof(struct type)); + /* just to be safe */ + n->k = TYPE_VOID; + vect_append(struct ast *, types, &n); return n; } -struct ast_node *gen_alias(struct ast_node *id, struct ast_node *type, - struct src_loc loc) +struct ast *gen_ast(enum ast_kind kind, + struct ast *a0, + struct ast *a1, + struct ast *a2, + struct ast *a3, + struct type *t2, + char *s, + long long v, + struct src_loc loc) { - ALLOC_NODE(n, "alias"); - n->node_type = AST_ALIAS; - AST_ALIAS(n).id = id; - AST_ALIAS(n).type = type; + struct ast *n = create_empty_ast(); + n->k = kind; + n->a0 = a0; + n->a1 = a1; + n->a2 = a2; + n->a3 = a3; + n->t2 = t2; + n->s = s; + n->v = v; n->loc = loc; return n; } -struct ast_node *gen_trait(struct ast_node *id, - struct ast_node *params, - struct ast_node *raw_body, - struct ast_node *body, - struct src_loc loc) +struct type *tgen_type(enum type_kind kind, + struct type *t0, + struct type *t1, + struct ast *d, + struct ast *a, + char *id, + struct src_loc loc) { - ALLOC_NODE(n, "trait"); - n->node_type = AST_TRAIT; - AST_TRAIT(n).id = id; - AST_TRAIT(n).params = params; - AST_TRAIT(n).body = body; - AST_TRAIT(n).raw_body = raw_body; + struct type *n = create_empty_type(); + n->k = kind; + n->t0 = t0; + n->t1 = t1; + n->d = d; + n->a = a; + n->id = id; n->loc = loc; return n; } -struct ast_node *gen_import(const char *file, struct src_loc loc) +void ast_set_flags(struct ast *node, enum ast_flags flags) { - ALLOC_NODE(n, "import"); - n->node_type = AST_IMPORT; - AST_IMPORT(n).file = file; - n->loc = loc; - return n; + node->f |= flags; } -struct ast_node *gen_embed(const char *file, struct src_loc loc) +void ast_clear_flags(struct ast *node, enum ast_flags flags) { - ALLOC_NODE(n, "embed"); - n->node_type = AST_EMBED; - AST_EMBED(n).file = file; - n->loc = loc; - return n; + node->f &= ~(flags); } -struct ast_node *gen_empty() +unsigned ast_flags(struct ast *node, enum ast_flags flags) { - ALLOC_NODE(n, "empty"); - n->node_type = AST_EMPTY; - /* TODO: location? */ - return n; + return node->f & flags; } -void ast_set_flags(struct ast_node *node, enum ast_flag flags) +void ast_append(struct ast *list, struct ast *elem) { - node->flags |= flags; -} + struct ast *cur = list; + while (cur->n) + cur = cur->n; -void ast_clear_flags(struct ast_node *node, enum ast_flag flags) -{ - node->flags &= ~(flags); + cur->n = elem; } -void ast_append(struct ast_node *list, struct ast_node *elem) +void type_append(struct type *list, struct type *elem) { - struct ast_node *cur = list; - while (cur->next) - cur = cur->next; + struct type *cur = list; + while (cur->n) + cur = cur->n; - cur->next = elem; + cur->n = elem; } - -static const char *binop_symbol(int op) +static const char *binop_symbol(enum ast_kind op) { switch (op) { case AST_ADD: return "+"; @@ -618,18 +195,20 @@ static const char *binop_symbol(int op) case AST_GE: return ">="; case AST_NE: return "!="; case AST_EQ: return "=="; + default: } return "UNKNOWN"; } -static const char *unop_symbol(int op) +static const char *unop_symbol(enum ast_kind op) { switch (op) { case AST_NEG: return "-"; case AST_LNOT: return "!"; case AST_REF: return "&"; case AST_DEREF: return "*"; + default: } return "UNKNOWN"; @@ -641,19 +220,19 @@ static void dump(int depth, const char *fmt, ...) va_start(args, fmt); printf("//"); for (int i = 0; i < depth; ++i) - putchar('\t'); + printf(" "); vprintf(fmt, args); va_end(args); } -static void dump_flags(struct ast_node *node) +static void dump_flags(struct ast *node) { if (node->scope) printf(" %zu:", node->scope->number); - enum ast_flag flags = node->flags; + enum ast_flags flags = node->f; if (flags & AST_FLAG_MUTABLE) printf(" MUT"); @@ -679,1252 +258,462 @@ static void dump_flags(struct ast_node *node) printf(" FALLTHROUGH"); } -const char *primitive_str(enum ast_primitive type) +const char *primitive_str(struct type *type) { - switch (type) { - case AST_VOID: return "void"; - case AST_I9: return "i9"; - case AST_I27: return "i27"; - case AST_BOOL: return "bool"; + switch (type->k) { + case TYPE_VOID: return "void"; + case TYPE_I9: return "i9"; + case TYPE_I27: return "i27"; + case TYPE_BOOL: return "bool"; default: return "unimp"; } return "unimp"; } -static void __dump_ast(int depth, struct ast_node *node) +void ast_dump(int depth, struct ast *n) { - switch (node->node_type) { - case AST_FETCH: - dump(depth, "{FETCH:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_FETCH(node).id); - dump_ast(depth + 1, AST_FETCH(node).type); - - dump(depth, "}\n"); - break; - - case AST_ASSIGN: - dump(depth, "{ASSIGN:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_ASSIGN(node).to); - dump_ast(depth + 1, AST_ASSIGN(node).from); - - dump(depth, "}\n"); - break; - - case AST_INIT: - dump(depth, "{INIT:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_INIT(node).body); - - dump(depth, "}\n"); - break; - - case AST_SIZEOF: - dump(depth, "{SIZEOF:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_SIZEOF(node).expr); - - dump(depth, "}\n"); - break; - - case AST_DOT: - dump(depth, "{DOT:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_DOT(node).expr); - dump_ast(depth + 1, AST_DOT(node).id); - - dump(depth, "}\n"); - break; - - case AST_GOTO: - dump(depth, "{GOTO:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_GOTO(node).defers); - dump_ast(depth + 1, AST_GOTO(node).label); - - dump(depth, "}\n"); - break; - - case AST_LABEL: - dump(depth, "{LABEL:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_LABEL(node).id); - - dump(depth, "}\n"); - break; - - case AST_BINOP: - dump(depth, "{BINOP:"); - dump_flags(node); - printf(" %s\n", binop_symbol(AST_BINOP(node).op)); - - dump_ast(depth + 1, AST_BINOP(node).left); - dump_ast(depth + 1, AST_BINOP(node).right); - - dump(depth, "}\n"); - break; - - case AST_UNOP: - dump(depth, "{UNOP:"); - dump_flags(node); - printf(" %s\n", unop_symbol(AST_UNOP(node).op)); - - dump_ast(depth + 1, AST_UNOP(node).expr); - - dump(depth, "}\n"); - break; - - case AST_CALL: - dump(depth, "{CALL:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_CALL(node).expr); - dump_ast(depth + 1, AST_CALL(node).args); - - dump(depth, "}\n"); - break; - - case AST_DEFER: - dump(depth, "{DEFER:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_DEFER(node).expr); - - dump(depth, "}\n"); - break; + if (!n) { + dump(depth, "{NULL}\n"); + return; + } - case AST_CAST: - dump(depth, "{CAST:"); - dump_flags(node); - putchar('\n'); +#define DUMP(x) case x: dump(depth, #x); break; + switch (n->k) { + DUMP(AST_FETCH); + DUMP(AST_INIT); + DUMP(AST_ASSIGN); + DUMP(AST_CALL); + DUMP(AST_ARR); + DUMP(AST_SIZEOF); + DUMP(AST_CAST); + DUMP(AST_DEFER); + DUMP(AST_MACRO_DEF); + DUMP(AST_MACRO_EXPAND); + DUMP(AST_TYPE_EXPAND); + DUMP(AST_PROC_DEF); + DUMP(AST_GOTO); + DUMP(AST_LABEL); + DUMP(AST_VAR_DEF); + DUMP(AST_FOR); + DUMP(AST_EMBED); + DUMP(AST_DOT); + DUMP(AST_WHILE); + DUMP(AST_DO_WHILE); + DUMP(AST_BREAK); + DUMP(AST_CONTINUE); + DUMP(AST_RETURN); + DUMP(AST_ALIAS_DEF); + DUMP(AST_TRAIT_DEF); + DUMP(AST_STRUCT_DEF); + DUMP(AST_IF); + DUMP(AST_BLOCK); + DUMP(AST_IMPORT); + DUMP(AST_ENUM_DEF); + DUMP(AST_VAL); + DUMP(AST_SWITCH); + DUMP(AST_CASE); + DUMP(AST_ID); + DUMP(AST_AS); + DUMP(AST_EMPTY); + DUMP(AST_ADD); + DUMP(AST_SUB); + DUMP(AST_MUL); + DUMP(AST_DIV); + DUMP(AST_REM); + DUMP(AST_LAND); + DUMP(AST_LOR); + DUMP(AST_LSHIFT); + DUMP(AST_RSHIFT); + DUMP(AST_ASSIGN_ADD); + DUMP(AST_ASSIGN_SUB); + DUMP(AST_ASSIGN_MUL); + DUMP(AST_ASSIGN_DIV); + DUMP(AST_ASSIGN_REM); + DUMP(AST_ASSIGN_LSHIFT); + DUMP(AST_ASSIGN_RSHIFT); + DUMP(AST_LT); + DUMP(AST_GT); + DUMP(AST_LE); + DUMP(AST_GE); + DUMP(AST_NE); + DUMP(AST_EQ); + DUMP(AST_NEG); + DUMP(AST_LNOT); + DUMP(AST_NOT); + DUMP(AST_REF); + DUMP(AST_DEREF); + DUMP(AST_CONST_INT); + DUMP(AST_CONST_CHAR); + DUMP(AST_CONST_BOOL); + DUMP(AST_CONST_STR); + } +#undef DUMP - dump_ast(depth + 1, AST_CAST(node).expr); - dump_ast(depth + 1, AST_CAST(node).type); + depth++; - dump(depth, "}\n"); - break; + if (n->t) + type_dump_list(n->t); - case AST_MACRO_CONSTRUCT: - dump(depth, "{MACRO_CONSTRUCT:"); - dump_flags(node); - putchar('\n'); + printf("\n"); - dump_ast(depth + 1, AST_MACRO_CONSTRUCT(node).id); - dump_ast(depth + 1, AST_MACRO_CONSTRUCT(node).params); - dump_ast(depth + 1, AST_MACRO_CONSTRUCT(node).body); + if (n->s) + dump(depth, "%s\n", n->s); - dump(depth, "}\n"); - break; + if (n->k == AST_CONST_INT) + dump(depth, "%lli\n", n->v); - case AST_MACRO_EXPAND: - dump(depth, "{MACRO_EXPAND:"); - dump_flags(node); - putchar('\n'); + if (n->a0) + ast_dump_list(depth, n->a0); - dump_ast(depth + 1, AST_MACRO_EXPAND(node).id); - dump_ast(depth + 1, AST_MACRO_EXPAND(node).args); + if (n->a1) + ast_dump_list(depth, n->a1); - dump(depth, "}\n"); - break; + if (n->a2) + ast_dump_list(depth, n->a2); - case AST_PROC: - dump(depth, "{PROC:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_PROC(node).id); - dump_ast(depth + 1, AST_PROC(node).sign); - dump_ast(depth + 1, AST_PROC(node).body); - - dump(depth, "}\n"); - break; - - case AST_VAR: - dump(depth, "{VAR:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_VAR(node).id); - dump_ast(depth + 1, AST_VAR(node).type); - dump_ast(depth + 1, AST_VAR(node).init); - - dump(depth, "}\n"); - break; - - case AST_ID: - dump(depth, "{ID:"); - dump_flags(node); - printf(" %s}\n", AST_ID(node).id); - break; - - case AST_AS: - dump(depth, "{AS:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_AS(node).type); - dump(depth, "}\n"); - break; - - case AST_BLOCK: - dump(depth, "{BLOCK:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_BLOCK(node).body); - dump_ast(depth + 1, AST_BLOCK(node).defers); - - dump(depth, "}\n"); - break; - - case AST_RETURN: - dump(depth, "{RETURN:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_RETURN(node).defers); - dump_ast(depth + 1, AST_RETURN(node).expr); - - dump(depth, "}\n"); - break; - - case AST_TYPE: - dump(depth, "{TYPE:"); - dump_flags(node); - - switch (node->_type.kind) { - case AST_TYPE_PRIMITIVE: - printf(" PRIMITIVE %s\n", - primitive_str(AST_PRIMITIVE_TYPE(node).type)); - break; - - case AST_TYPE_TRAIT: { - printf(" TRAIT\n"); - 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"); - dump_ast(depth + 1, AST_ID_TYPE(node).id); - break; - - case AST_TYPE_ARR: - printf(" ARR\n"); - dump_ast(depth + 1, AST_ARR_TYPE(node).size); - dump_ast(depth + 1, AST_ARR_TYPE(node).base); - break; - - case AST_TYPE_POINTER: - printf(" PTR\n"); - dump_ast(depth + 1, AST_PTR_TYPE(node).base); - break; - - 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(def).id); - break; - } - - case AST_TYPE_ENUM: { - printf(" ENUM\n"); - 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"); - dump_ast(depth + 1, AST_SIGN_TYPE(node).params); - dump_ast(depth + 1, AST_SIGN_TYPE(node).ret); - break; - } - - dump_ast(depth + 1, AST_TYPE(node).next); - - dump(depth, "}\n"); - break; - - case AST_EMPTY: - dump(depth, "{EMPTY:"); - dump_flags(node); - printf("}\n"); - break; - - case AST_FOR: - dump(depth, "{FOR:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_FOR(node).pre); - dump_ast(depth + 1, AST_FOR(node).cond); - dump_ast(depth + 1, AST_FOR(node).post); - dump_ast(depth + 1, AST_FOR(node).body); - - dump(depth, "}\n"); - break; - - case AST_WHILE: - dump(depth, "{WHILE:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_WHILE(node).cond); - dump_ast(depth + 1, AST_WHILE(node).body); - - dump(depth, "}\n"); - break; - - case AST_CTRL: - dump(depth, "{CTRL:"); - dump_flags(node); - switch(AST_CTRL(node).kind) { - case AST_CTRL_BREAK: printf(" BREAK"); break; - case AST_CTRL_CONTINUE: printf(" CONTINUE"); break; - } - printf("}\n"); - break; - - case AST_IF: - dump(depth, "{IF:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_IF(node).cond); - dump_ast(depth + 1, AST_IF(node).body); - dump_ast(depth + 1, AST_IF(node).els); - - dump(depth, "}\n"); - break; - - case AST_IMPORT: - dump(depth, "{IMPORT:"); - dump_flags(node); - printf(" %s}\n", AST_IMPORT(node).file); - break; - - case AST_EMBED: - dump(depth, "{EMBED:"); - dump_flags(node); - printf(" %s}\n", AST_EMBED(node).file); - break; - - case AST_ENUM: - dump(depth, "{ENUM:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_ENUM(node).id); - dump_ast(depth + 1, AST_ENUM(node).type); - dump_ast(depth + 1, AST_ENUM(node).body); - - dump(depth, "}\n"); - break; - - case AST_STRUCT: - dump(depth, "{STRUCT:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_STRUCT(node).id); - dump_ast(depth + 1, AST_STRUCT(node).generics); - dump_ast(depth + 1, AST_STRUCT(node).body); - - dump(depth, "}\n"); - break; - - case AST_VAL: - dump(depth, "{VAL:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_VAL(node).id); - dump_ast(depth + 1, AST_VAL(node).val); - - dump(depth, "}\n"); - break; - - case AST_SWITCH: - dump(depth, "{SWITCH:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_SWITCH(node).cond); - dump_ast(depth + 1, AST_SWITCH(node).cases); - - dump(depth, "}\n"); - break; - - case AST_CASE: - dump(depth, "{CASE:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_CASE(node).cond); - dump_ast(depth + 1, AST_CASE(node).body); - - dump(depth, "}\n"); - break; - - case AST_CONST: - dump(depth, "{CONST:"); - dump_flags(node); - switch (node->_const.kind) { - case AST_CONST_INTEGER: - printf(" %lli", AST_CONST(node).integer); - break; - case AST_CONST_STRING: - printf(" \"%s\"", AST_CONST(node).str); - break; - } - printf("}\n"); - break; - - case AST_ALIAS: - dump(depth, "{ALIAS:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_ALIAS(node).id); - dump_ast(depth + 1, AST_ALIAS(node).type); - - dump(depth, "}\n"); - break; - - case AST_TRAIT: - dump(depth, "{TRAIT:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_TRAIT(node).id); - dump_ast(depth + 1, AST_TRAIT(node).body); - - dump(depth, "}\n"); - break; + if (n->a3) + ast_dump_list(depth, n->a3); - default: - dump(depth, "{UNIMP}\n"); + if (n->t2) { + type_dump_list(n->t2); printf("\n"); } } -void dump_ast_node(int depth, struct ast_node *n) +void ast_dump_list(int depth, struct ast *root) { - if (!n) { + if (!root) { dump(depth, "{NULL}\n"); return; } - __dump_ast(depth, n); + foreach_node(n, root) { + ast_dump(depth, n); + } } -void dump_ast(int depth, struct ast_node *root) +void type_dump(struct type *n) { - if (!root) { - dump(depth, "{NULL}\n"); + if (!n) { + printf(" {NULL}"); return; } - struct ast_node *n = root; - do { - dump_ast_node(depth, n); - } while ((n = n->next)); + printf(" "); + +#define DUMP(x) case x: printf(#x); break; + switch (n->k) { + DUMP(TYPE_VOID); + DUMP(TYPE_BOOL); + DUMP(TYPE_I9); + DUMP(TYPE_I27); + DUMP(TYPE_STR); + DUMP(TYPE_PTR); + DUMP(TYPE_ID); + DUMP(TYPE_CONSTRUCT); + DUMP(TYPE_STRUCT); + DUMP(TYPE_ENUM); + DUMP(TYPE_CALLABLE); + DUMP(TYPE_TRAIT); + } } -struct ast_node *clone_ast_node(struct ast_node *node) +void type_dump_list(struct type *root) { - if (!node) - return NULL; + if (!root) + return; - assert(node->node_type); - struct ast_node *new = NULL; - switch (node->node_type) { - case AST_ARR_ACCESS: - new = gen_arr_access( - clone_ast_node(AST_ARR_ACCESS(node).base), - clone_ast_node(AST_ARR_ACCESS(node).idx), - node->loc); - break; - - case AST_TYPE_EXPAND: - new = gen_type_expand( - clone_ast_node(AST_TYPE_EXPAND(node).id), - clone_ast_node(AST_TYPE_EXPAND(node).args), - node->loc); - break; - - case AST_FETCH: - new = gen_fetch(clone_ast_node(AST_FETCH(node).id), - clone_ast_node(AST_FETCH(node).type), - node->loc); - break; - - case AST_ASSIGN: - new = gen_assign(clone_ast_node(AST_ASSIGN(node).to), - clone_ast_node(AST_ASSIGN(node).from), - node->loc); - break; - - case AST_INIT: new = gen_init(clone_ast_node(AST_INIT(node).body), - node->loc); - break; - - case AST_SIZEOF: new = gen_sizeof(clone_ast_node(AST_SIZEOF(node).expr), - node->loc); - break; - - case AST_DOT: new = gen_dot(clone_ast_node(AST_DOT(node).expr), - clone_ast_node(AST_DOT(node).id), - node->loc); - break; - - case AST_AS: new = gen_as(clone_ast_node(AST_AS(node).type), node->loc); - break; - - case AST_GOTO: new = gen_goto(clone_ast_node(AST_GOTO(node).label), - node->loc); - break; - - case AST_LABEL: new = gen_label(clone_ast_node(AST_LABEL(node).id), - node->loc); - break; - - case AST_BINOP: new = gen_binop(AST_BINOP(node).op, - clone_ast_node(AST_BINOP(node).left), - clone_ast_node(AST_BINOP(node).right), - node->loc); - break; - - case AST_UNOP: new = gen_unop(AST_UNOP(node).op, - clone_ast_node(AST_UNOP(node).expr), - node->loc); - break; - - case AST_CALL: new = gen_call(clone_ast_node(AST_CALL(node).expr), - clone_ast_node(AST_CALL(node).args), - node->loc); - break; - - case AST_DEFER: new = gen_defer(clone_ast_node(AST_DEFER(node).expr), - node->loc); - break; - - case AST_MACRO_CONSTRUCT: new = gen_macro_construct( - clone_ast_node(AST_MACRO_CONSTRUCT(node).id), - clone_ast_node(AST_MACRO_CONSTRUCT(node).params), - clone_ast_node(AST_MACRO_CONSTRUCT(node).body), - node->loc); - break; - - case AST_MACRO_EXPAND: new = gen_macro_expand( - clone_ast_node(AST_MACRO_EXPAND(node).id), - clone_ast_node(AST_MACRO_EXPAND(node).args), - node->loc); - break; - - case AST_CAST: new = gen_cast(clone_ast_node(AST_CAST(node).expr), - clone_ast_node(AST_CAST(node).type), - node->loc); - break; - - case AST_PROC: new = gen_proc(clone_ast_node(AST_PROC(node).id), - clone_ast_node(AST_PROC(node).sign), - clone_ast_node(AST_PROC(node).body), - node->loc); - break; - - case AST_VAR: new = gen_var(clone_ast_node(AST_VAR(node).id), - clone_ast_node(AST_VAR(node).type), - clone_ast_node(AST_VAR(node).init), - node->loc); - break; - - case AST_FOR: new = gen_for(clone_ast_node(AST_FOR(node).pre), - clone_ast_node(AST_FOR(node).cond), - clone_ast_node(AST_FOR(node).post), - clone_ast_node(AST_FOR(node).body), - node->loc); - break; - - case AST_WHILE: new = gen_while(clone_ast_node(AST_WHILE(node).cond), - clone_ast_node(AST_WHILE(node).body), - node->loc); - break; - - case AST_CTRL: new = gen_ctrl(AST_CTRL(node).kind, node->loc); - break; - - case AST_RETURN: new = gen_return(clone_ast_node(AST_RETURN(node).expr), - node->loc); - break; - - case AST_TYPE: - switch (node->_type.kind) { - case AST_TYPE_PRIMITIVE: - new = gen_primitive(AST_PRIMITIVE_TYPE(node).type, - AST_PRIMITIVE_TYPE(node).def, - node->loc); - break; - - case AST_TYPE_TRAIT: - new = gen_type(AST_TYPE_TRAIT, - AST_TRAIT_TYPE(node).def, - NULL, - 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), - NULL, - node->loc); - break; - - case AST_TYPE_ARR: - new = gen_type(AST_TYPE_ARR, - clone_ast_node(AST_ARR_TYPE(node).size), - clone_ast_node(AST_ARR_TYPE(node).base), - node->loc); - break; - - case AST_TYPE_POINTER: - new = gen_type(AST_TYPE_POINTER, - clone_ast_node(AST_PTR_TYPE(node).base), - NULL, - node->loc); - break; - - case AST_TYPE_STRUCT: - new = gen_type(AST_TYPE_STRUCT, - AST_STRUCT_TYPE(node).def, - NULL, node->loc); - break; - - case AST_TYPE_ENUM: - new = gen_type(AST_TYPE_ENUM, - AST_ENUM_TYPE(node).def, - NULL, - node->loc); - break; - - case AST_TYPE_SIGN: - new = gen_type(AST_TYPE_SIGN, - clone_ast_node(AST_SIGN_TYPE( - node).params), - clone_ast_node(AST_SIGN_TYPE(node).ret), - node->loc); - break; - - } - - assert(new); - AST_TYPE(new).next = clone_ast_node(AST_TYPE(node).next); - break; - - case AST_BLOCK: - /* TODO: should defers also be cloned? Probably? */ - new = gen_block(clone_ast_node(AST_BLOCK(node).body), - node->loc); - break; - - case AST_IMPORT: - new = gen_import(strdup(AST_IMPORT(node).file), node->loc); - break; - - case AST_EMBED: - new = gen_embed(strdup(AST_EMBED(node).file), node->loc); - break; - - case AST_ENUM: - new = gen_enum(clone_ast_node(AST_ENUM(node).id), - clone_ast_node(AST_ENUM(node).type), - clone_ast_node(AST_ENUM(node).body), - node->loc); - break; - - case AST_STRUCT: - new = gen_struct(clone_ast_node(AST_STRUCT(node).id), - clone_ast_node(AST_STRUCT(node).generics), - clone_ast_node(AST_STRUCT(node).body), - node->loc); - break; - - case AST_VAL: - new = gen_val(clone_ast_node(AST_VAL(node).id), - clone_ast_node(AST_VAL(node).val), - node->loc); - break; - - case AST_SWITCH: - new = gen_switch(clone_ast_node(AST_SWITCH(node).cond), - clone_ast_node(AST_SWITCH(node).cases), - node->loc); - break; - - case AST_CASE: - new = gen_case(clone_ast_node(AST_CASE(node).cond), - clone_ast_node(AST_CASE(node).body), - node->loc); - break; - - case AST_CONST: - switch (node->_const.kind) { - case AST_CONST_INTEGER: - new = gen_int(AST_CONST(node).integer, node->loc); - break; - - case AST_CONST_STRING: - new = gen_string(strdup(AST_CONST(node).str), - node->loc); - break; - } - break; - - case AST_ID: - new = gen_id(strdup(AST_ID(node).id), node->loc); - break; - - case AST_EMPTY: - new = gen_empty(); - break; - - case AST_ALIAS: - new = gen_alias(clone_ast_node(AST_ALIAS(node).id), - clone_ast_node(AST_ALIAS(node).type), - node->loc); - break; - - case AST_TRAIT: - new = gen_trait(clone_ast_node(AST_TRAIT(node).id), - clone_ast_node(AST_TRAIT(node).params), - clone_ast_node(AST_TRAIT(node).raw_body), - clone_ast_node(AST_TRAIT(node).body), - node->loc); - break; - - case AST_IF: - new = gen_if(clone_ast_node(AST_IF(node).cond), - clone_ast_node(AST_IF(node).body), - clone_ast_node(AST_IF(node).els), - node->loc); - break; + foreach_type(t, root) { + type_dump(t); } - - /* if we run out of memory, this assert is likely a bit dumb... */ - assert(new); - new->scope = node->scope; - new->flags = node->flags; - new->uses = node->uses; - new->loc = node->loc; - new->next = clone_ast_node(node->next); - - if (node->type != node) - node->type = clone_ast_node(node->type); - else - node->type = node; - - return new; } -unsigned ast_flags(struct ast_node *node, enum ast_flag flags) +struct ast *clone_ast(struct ast *n) { - return node->flags & flags; -} + if (!n) + return NULL; -static int call_on_assign(int (*call)(struct ast_node *, - void *), struct ast_node *node, - void *data) -{ - int ret = 0; - ret |= call(AST_ASSIGN(node).to, data); - ret |= call(AST_ASSIGN(node).from, data); - return ret; -} + assert(n->k); + struct ast *new = create_empty_ast(); + new->scope = n->scope; + new->uses = n->uses; + new->loc = n->loc; + new->k = n->k; + new->v = n->v; + new->f = n->f; -static int call_on_init(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - return call(AST_INIT(node).body, data); -} + if (n->t) + new->t = clone_type_list(n->t); -static int call_on_sizeof(int (*call)(struct ast_node *, - void *), struct ast_node *node, - void *data) -{ - return call(AST_SIZEOF(node).expr, data); -} + if (n->s) + new->s = strdup(n->s); -static int call_on_dot(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - int ret = 0; - ret |= call(AST_DOT(node).expr, data); - ret |= call(AST_DOT(node).id, data); - return ret; -} + if (n->a0) + new->a0 = clone_ast_list(n->a0); -static int call_on_as(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - return call(AST_AS(node).type, data); -} + if (n->a1) + new->a1 = clone_ast_list(n->a1); -static int call_on_cast(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - int ret = 0; - ret |= call(AST_CAST(node).expr, data); - ret |= call(AST_CAST(node).type, data); - return ret; -} + if (n->a2) + new->a2 = clone_ast_list(n->a2); -static int call_on_defer(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - return call(AST_DEFER(node).expr, data); -} + if (n->a3) + new->a3 = clone_ast_list(n->a3); -static int call_on_var(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - int ret = 0; - ret |= call(AST_VAR(node).id, data); - ret |= call(AST_VAR(node).type, data); - ret |= call(AST_VAR(node).init, data); - return ret; -} + if (n->t2) + new->t2 = clone_type_list(n->t2); -static int call_on_for(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - int ret = 0; - ret |= call(node->_for.pre, data); - ret |= call(node->_for.cond, data); - ret |= call(node->_for.post, data); - ret |= call(node->_for.body, data); - return ret; + return new; } -static int call_on_while(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) +struct type *clone_type(struct type *n) { - int ret = 0; - ret |= call(node->_while.cond, data); - ret |= call(node->_while.body, data); - return ret; -} + if (!n) + return NULL; -static int call_on_return(int (*call)(struct ast_node *, - void *), struct ast_node *node, - void *data) -{ - return call(node->_return.expr, data); -} + assert(n->k); + struct type *new = create_empty_type(); + new->scope = n->scope; + new->loc = n->loc; + new->k = n->k; + new->a = n->a; + new->d = n->d; -static int call_on_alias(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - int ret = 0; - ret |= call(node->_alias.id, data); - ret |= call(node->_alias.type, data); - return ret; -} + if (n->id) + new->id = strdup(n->id); -static int call_on_trait(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - int ret = 0; - ret |= call(node->_trait.id, data); - ret |= call(node->_trait.body, data); - return ret; -} + if (n->t0) + new->t0 = clone_type_list(n->t0); -static int call_on_if(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - int ret = 0; - ret |= call(node->_if.cond, data); - ret |= call(node->_if.body, data); - ret |= call(node->_if.els, data); - return ret; -} + if (n->t1) + new->t1 = clone_type_list(n->t1); -static int call_on_enum(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - int ret = 0; - ret |= call(node->_enum.id, data); - ret |= call(node->_enum.type, data); - ret |= call(node->_enum.body, data); - return ret; + return new; } -static int call_on_struct(int (*call)(struct ast_node *, - void *), struct ast_node *node, - void *data) +struct ast *clone_ast_list(struct ast *root) { - int ret = 0; - ret |= call(node->_struct.id, data); - ret |= call(node->_struct.generics, data); - ret |= call(node->_struct.body, data); - return ret; -} + struct ast *n = root, *new_root = NULL, *prev = NULL; + while (n) { + struct ast *new = clone_ast(n); -static int call_on_val(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - int ret = 0; - ret |= call(node->_val.id, data); - ret |= call(node->_val.val, data); - return ret; -} + if (prev) prev->n = new; + else new_root = new; -static int call_on_switch(int (*call)(struct ast_node *, - void *), struct ast_node *node, - void *data) -{ - int ret = 0; - ret |= call(node->_switch.cond, data); - ret |= call(node->_switch.cases, data); - return ret; -} + prev = new; + n = n->n; + } -static int call_on_case(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - int ret = 0; - ret |= call(node->_case.cond, data); - ret |= call(node->_case.body, data); - return ret; + return new_root; } -static int call_on_type_id(int (*call)(struct ast_node *, - void *), struct ast_node *node, - void *data) +struct type *clone_type_list(struct type *root) { - return call(AST_ID_TYPE(node).id, data); -} + struct type *n = root, *new_root = NULL, *prev = NULL; + while (n) { + struct type *new = clone_type(n); -static int call_on_type_arr(int (*call)(struct ast_node *, - void *), struct ast_node *node, - void *data) -{ - return call(AST_ARR_TYPE(node).size, data); -} + if (prev) prev->n = new; + else new_root = new; -static int call_on_type_sign(int (*call)(struct ast_node *, - void *), struct ast_node *node, - void *data) -{ - int ret = 0; - ret |= call(AST_SIGN_TYPE(node).params, data); - ret |= call(AST_SIGN_TYPE(node).ret, data); - return ret; + prev = new; + n = n->n; + } + + return new_root; } -static int call_on_type_construct(int (*call)(struct ast_node *, void *), - struct ast_node *node, void *data) +int type_visit(type_callback_t before, type_callback_t after, struct type *n, void *d) { int ret = 0; - ret |= call(AST_CONSTRUCT_TYPE(node).id, data); - ret |= call(AST_CONSTRUCT_TYPE(node).args, data); - return ret; -} + if (!n) + return ret; -static int call_on_type_pointer(int (*call)(struct ast_node *, void *), - struct ast_node *node, void *data) -{ - return call(AST_PTR_TYPE(node).base, data); -} + if (before && (ret = before(n, d))) + return ret; -static int call_on_type(int (*call)(struct ast_node *, void *), - struct ast_node *node, void *data) -{ - int ret = 0; - 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: 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: ret = call_on_type_pointer(call, node, data); - break; - case AST_TYPE_PRIMITIVE: break; - } + if (n->t0 && (ret = type_visit_list(before, after, n->t0, d))) + return ret; - return ret; -} + if (n->t1 && (ret = type_visit_list(before, after, n->t1, d))) + return ret; -static int call_on_goto(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - return call(AST_GOTO(node).label, data); -} + if (after && (ret = after(n, d))) + return ret; -static int call_on_label(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - return call(AST_LABEL(node).id, data); + return ret; } -static int call_on_binop(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) +int ast_visit(ast_callback_t before, ast_callback_t after, struct ast *n, void *d) { int ret = 0; - ret |= call(AST_BINOP(node).left, data); - ret |= call(AST_BINOP(node).right, data); - return ret; -} + if (!n) + return ret; -static int call_on_unop(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - return call(AST_UNOP(node).expr, data); -} + if (before && (ret = before(n, d))) + return ret; -static int call_on_call(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - int ret = 0; - ret |= call(AST_CALL(node).expr, data); - ret |= call(AST_CALL(node).args, data); - return ret; -} + if (n->a0 && (ret = ast_visit_list(before, after, n->a0, d))) + return ret; -static int call_on_macro_construct(int (*call)(struct ast_node *, - void *), struct ast_node *node, - void *data) -{ - int ret = 0; - ret |= call(AST_MACRO_CONSTRUCT(node).id, data); - ret |= call(AST_MACRO_CONSTRUCT(node).params, data); - ret |= call(AST_MACRO_CONSTRUCT(node).body, data); - return ret; -} + if (n->a1 && (ret = ast_visit_list(before, after, n->a1, d))) + return ret; -static int call_on_proc(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - int ret = 0; - ret |= call(node->_proc.id, data); - ret |= call(node->_proc.sign, data); - ret |= call(node->_proc.body, data); - return ret; -} + if (n->a2 && (ret = ast_visit_list(before, after, n->a2, d))) + return ret; -static int call_on_block(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - return call(node->_block.body, data); -} + if (n->a3 && (ret = ast_visit_list(before, after, n->a3, d))) + return ret; -static int call_on_fetch(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - int ret = 0; - ret |= call(node->_fetch.id, data); - ret |= call(node->_fetch.type, data); - return ret; -} + if (after && (ret = after(n, d))) + return ret; -static int call_on_macro_expand(int (*call)(struct ast_node *, - void *), struct ast_node *node, - void *data) -{ - int ret = 0; - ret |= call(node->_macro_expand.id, data); - ret |= call(node->_macro_expand.args, data); return ret; } -static int call_on_type_expand(int (*call)(struct ast_node *, - void *), - struct ast_node *type_expand, void *data) +int ast_visit_list(ast_callback_t before, ast_callback_t after, struct ast *l, void *d) { int ret = 0; - ret |= call(AST_TYPE_EXPAND(type_expand).id, data); - ret |= call(AST_TYPE_EXPAND(type_expand).args, data); + foreach_node(n, l) { + if ((ret = ast_visit(before, after, n, d))) + return ret; + } + return ret; } -/* I guess this works, but it's not exactly optimal as the caller sort of has to - * know when to continue to call on, and when it would cause an infinite loop. - * I.e. a call on an ID that is forwarded results in an infinite loop. - * - * Maybe add in something like ast_continue_call_on() that can check for the - * user if there's any point in continuing? - */ -int ast_call_on(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) +int type_visit_list(type_callback_t before, type_callback_t after, struct type *l, void *d) { int ret = 0; - if (!node) - return ret; - - switch (node->node_type) { - case AST_ARR_ACCESS: - case AST_TYPE_EXPAND: ret = call_on_type_expand(call, node, data); - break; - case AST_FETCH: ret = call_on_fetch(call, node, data); break; - case AST_ASSIGN: ret = call_on_assign(call, node, data); break; - case AST_INIT: ret = call_on_init(call, node, data); break; - case AST_SIZEOF: ret = call_on_sizeof(call, node, data); break; - case AST_DOT: ret = call_on_dot(call, node, data); break; - case AST_AS: ret = call_on_as(call, node, data); break; - case AST_CAST: ret = call_on_cast(call, node, data); break; - case AST_DEFER: ret = call_on_defer(call, node, data); break; - case AST_VAR: ret = call_on_var(call, node, data); break; - case AST_FOR: ret = call_on_for(call, node, data); break; - case AST_WHILE: ret = call_on_while(call, node, data); break; - case AST_RETURN: ret = call_on_return(call, node, data); break; - case AST_ALIAS: ret = call_on_alias(call, node, data); break; - case AST_TRAIT: ret = call_on_trait(call, node, data); break; - case AST_IF: ret = call_on_if(call, node, data); break; - case AST_ENUM: ret = call_on_enum(call, node, data); break; - case AST_STRUCT: ret = call_on_struct(call, node, data); break; - case AST_VAL: ret = call_on_val(call, node, data); break; - case AST_SWITCH: ret = call_on_switch(call, node, data); break; - case AST_CASE: ret = call_on_case(call, node, data); break; - case AST_TYPE: ret = call_on_type(call, node, data); break; - case AST_GOTO: ret = call_on_goto(call, node, data); break; - case AST_LABEL: ret = call_on_label(call, node, data); break; - case AST_BINOP: ret = call_on_binop(call, node, data); break; - case AST_UNOP: ret = call_on_unop(call, node, data); break; - case AST_CALL: ret = call_on_call(call, node, data); break; - case AST_MACRO_CONSTRUCT: ret = - call_on_macro_construct(call, node, data); break; - case AST_MACRO_EXPAND: ret = call_on_macro_expand(call, node, data); - break; - case AST_PROC: ret = call_on_proc(call, node, data); break; - case AST_BLOCK: ret = call_on_block(call, node, data); break; - case AST_EMBED: break; - case AST_CTRL: break; - case AST_IMPORT: break; - case AST_CONST: break; - case AST_ID: break; - case AST_EMPTY: break; + foreach_type(n, l) { + if ((ret = type_visit(before, after, n, d))) + return ret; } - ret |= call(node->next, data); return ret; } -size_t ast_list_len(struct ast_node *node) +size_t ast_list_len(struct ast *node) { size_t count = 0; while (node) { count++; - node = node->next; + node = node->n; } return count; } -struct ast_node *ast_last_node(struct ast_node *list) +struct ast *ast_last(struct ast *list) { if (!list) return NULL; - while (list->next) - list = list->next; + while (list->n) + list = list->n; return list; } -struct ast_node *ast_block_last(struct ast_node *block) +struct ast *ast_block_last(struct ast *block) { - struct ast_node *b = ast_last_node(block); - if (b && b->node_type == AST_BLOCK) - return ast_block_last(b->_block.body); + struct ast *b = ast_last(block); + if (b && b->k == AST_BLOCK) + return ast_block_last(block_body(b)); return b; } -int same_id(struct ast_node *id1, struct ast_node *id2) +int same_id(char *id1, char *id2) { - assert(id1->node_type == AST_ID); - assert(id2->node_type == AST_ID); - return equiv_nodes(id1, id2); + return strcmp(id1, id2) == 0; } -int equiv_nodes(struct ast_node *n1, struct ast_node *n2) +int equiv_types(struct type *n1, struct type *n2) { - if (n1->node_type != n2->node_type) + if (n1 == n2) + return 1; + + if (n1 && !n2) return 0; - switch (n1->node_type) { - case AST_ID: - if (strcmp(AST_ID(n1).id, AST_ID(n2).id) != 0) - return 0; + if (!n1 && n2) + return 0; - break; - default: - internal_error("unimplemented equivalency"); + if (n1->k != n2->k) + return 0; + + if (n1->id && strcmp(n1->id, n2->id) != 0) + return 0; + + if (n1->t0 && !equiv_type_lists(n1->t0, n2->t0)) + return 0; + + if (n1->t1 && !equiv_type_lists(n1->t1, n2->t1)) return 0; - } return 1; } -int equiv_node_chains(struct ast_node *c1, struct ast_node *c2) +int equiv_nodes(struct ast *n1, struct ast *n2) { - if (c1 && !c2) + if (n1 && !n2) return 0; - if (!c1 && c2) + + if (!n1 && n2) return 0; - if (!c1 && !c2) + if (!n1 && !n2) return 1; + if (n1->k != n2->k) + return 0; + + if (n1->s && strcmp(n1->s, n2->s) != 0) + return 0; + + if (n1->a0 && !equiv_node_lists(n1->a0, n2->a0)) + return 0; + + if (n1->a1 && !equiv_node_lists(n1->a1, n2->a1)) + return 0; + + if (n1->a2 && !equiv_node_lists(n1->a2, n2->a2)) + return 0; + + if (n1->a3 && !equiv_node_lists(n1->a3, n2->a3)) + return 0; + + if (n1->t2 && !equiv_type_lists(n1->t2, n2->t2)) + return 0; + + return 1; +} + +int equiv_node_lists(struct ast *c1, struct ast *c2) +{ do { if (!equiv_nodes(c1, c2)) return 0; - c1 = c1->next; - c2 = c2->next; + c1 = c1->n; + c2 = c2->n; } while (c1 && c2); return 1; } + +int equiv_type_lists(struct type *t1, struct type *t2) +{ + do { + if (!equiv_types(t1, t2)) + return 0; + + t1 = t1->n; + t2 = t2->n; + + } while (t1 && t2); + + return 1; +} diff --git a/src/compiler.c b/src/compiler.c index b345e16..5aa6787 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -85,14 +85,14 @@ static int process(struct scope **parent, int public, const char *file) if (!p) return -1; parse(p, file, buf); - struct ast_node *tree = p->tree; + struct ast *tree = p->tree; bool failed = p->failed; destroy_parser(p); if (failed) return -1; - dump_ast(0, tree); + ast_dump_list(0, tree); struct scope *scope = create_scope(); if (!scope) @@ -167,19 +167,19 @@ int compile(const char *input) { struct scope *root = NULL; if (process_file(&root, 0, input)) { destroy_scope(root); - destroy_ast_nodes(); + destroy_allocs(); error("compilation of %s stopped due to errors", input); return ret; } - if ((ret = lower_actuals(root))) { + if ((ret = lower(root))) { destroy_scope(root); - destroy_ast_nodes(); + destroy_allocs(); error("compilation of %s stopped due to errors", input); return ret; } destroy_scope(root); - destroy_ast_nodes(); + destroy_allocs(); return 0; } diff --git a/src/debug.c b/src/debug.c index 2c873a2..f4b481d 100644 --- a/src/debug.c +++ b/src/debug.c @@ -120,7 +120,7 @@ void src_issue(struct src_issue issue, const char *err_msg, ...) va_end(args); } -void semantic_error(struct file_ctx fctx, struct ast_node *node, +void semantic_error(struct file_ctx fctx, struct ast *node, const char *fmt, ...) { va_list args; @@ -133,7 +133,20 @@ void semantic_error(struct file_ctx fctx, struct ast_node *node, va_end(args); } -void semantic_warn(struct file_ctx fctx, struct ast_node *node, const char *fmt, +void type_error(struct file_ctx fctx, struct type *node, + const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + struct src_issue issue; + issue.level = SRC_ERROR; + issue.loc = node->loc; + issue.fctx = fctx; + _issue(issue, fmt, args); + va_end(args); +} + +void semantic_warn(struct file_ctx fctx, struct ast *node, const char *fmt, ...) { va_list args; @@ -146,7 +159,20 @@ void semantic_warn(struct file_ctx fctx, struct ast_node *node, const char *fmt, va_end(args); } -void semantic_info(struct file_ctx fctx, struct ast_node *node, const char *fmt, +void semantic_info(struct file_ctx fctx, struct ast *node, const char *fmt, + ...) +{ + va_list args; + va_start(args, fmt); + struct src_issue issue; + issue.level = SRC_INFO; + issue.loc = node->loc; + issue.fctx = fctx; + _issue(issue, fmt, args); + va_end(args); +} + +void type_info(struct file_ctx fctx, struct type *node, const char *fmt, ...) { va_list args; @@ -185,117 +211,60 @@ void internal_warn(const char *fmt, ...) * @param fp File pointer to write string representation to. * @param type Type to generate string representation for. */ -static void _type_str(FILE *fp, struct ast_node *type) +static void _type_str(FILE *fp, struct type *type) { if (!type) return; - assert(type->node_type == AST_TYPE); - - switch (AST_TYPE(type).kind) { - case AST_TYPE_POINTER: + switch (type->k) { + case TYPE_PTR: fputc('*', fp); - _type_str(fp, AST_PTR_TYPE(type).base); + _type_str(fp, ptr_base(type)); break; - case AST_TYPE_ID: { - struct ast_node *id = AST_ID_TYPE(type).id; - fprintf(fp, "%s", AST_ID(id).id); + case TYPE_ID: { + fprintf(fp, "%s", type->id); break; } - case AST_TYPE_TRAIT: { - struct ast_node *def = AST_TRAIT_TYPE(type).def; - if (AST_TRAIT(def).id) { - struct ast_node *name = AST_TRAIT(def).id; - fprintf(fp, "%s ", AST_ID(name).id); + case TYPE_TRAIT: { + struct ast *def = type->d; + if (trait_id(def)) { + fprintf(fp, "%s ", trait_id(def)); } fprintf(fp, "(trait)"); break; } - case AST_TYPE_STRUCT: { - struct ast_node *def = AST_STRUCT_TYPE(type).def; - if (AST_STRUCT(def).id) { - struct ast_node *name = AST_STRUCT(def).id; - fprintf(fp, "%s ", AST_ID(name).id); + case TYPE_STRUCT: { + struct ast *def = type->d; + if (struct_id(def)) { + fprintf(fp, "%s ", struct_id(def)); } fprintf(fp, "(struct)"); - /** @todo print out anonymous structs with members? */ - break; - } - - case AST_TYPE_PRIMITIVE: { - fprintf(fp, "%s", primitive_str(AST_PRIMITIVE_TYPE(type).type)); break; } default: - fprintf(fp, "NOT YET IMPLEMENTED"); + if (is_primitive(type)) + fprintf(fp, "%s", primitive_str(type)); + else + fprintf(fp, "UNKNOWN TYPE"); } - - _type_str(fp, AST_TYPE(type).next); } -char *type_str(struct ast_node *node) +char *type_str(struct type *t) { /* maybe hacky? */ - if (!node) + if (!t) return strdup("void"); char *buf = NULL; size_t size = 0; + /* hehe */ FILE *memstream = open_memstream(&buf, &size); - /* TODO: improve trait detection */ - /* we were given a plain type, pass it directly along to _type_str */ - if (node->node_type == AST_TYPE) - _type_str(memstream, node); - else - /* otherwise, try to fish out the type of the node */ - _type_str(memstream, node->type); + _type_str(memstream, t); fclose(memstream); return buf; } - -/** - * Workhorse for call_str(). - * - * @param f File pointer to write string representation to. - * @param call Call to generate string representation for. - */ -static void _call_str(FILE *f, struct ast_node *call) -{ - struct ast_node *expr = AST_CALL(call).expr; - if (expr->node_type == AST_ID) { - const char *id_str = AST_ID(expr).id; - fprintf(f, "%s", id_str); - } - - struct ast_node *args = AST_CALL(call).args; - fprintf(f, "("); - - while (args) { - char *type = type_str(args); - fprintf(f, "%s", type); - free(type); - - args = args->next; - if (args) - fprintf(f, ", "); - else - break; - } - - fprintf(f, ")"); -} - -char *call_str(struct ast_node *call) -{ - assert(call->node_type == AST_CALL); - char *buf = NULL; size_t size = 0; - FILE *memstream = open_memstream(&buf, &size); - _call_str(memstream, call); - fclose(memstream); - return buf; -} diff --git a/src/lexer.l b/src/lexer.l index c819328..b38833d 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -84,36 +84,26 @@ STRING \"(\\.|[^"\\])*\" "%" {return REM;} "^" {return XOR;} -'[^'\\]' { - /* regular character constant, 'a' */ - yylval->integer = yytext[1]; - return INT; +"true" { + yylval->integer = 1; + return BOOL; } -'\\x[0-9a-fA-F]+' { - /* hex character constant, '\xef' */ - /* handling is slightly different from C, here it's more or less just - * another way to specify a hex integer */ - yylval->integer = strtoll(yytext + 3, NULL, 16); - return INT; -} - -'\\[0-8]+' { - /* octal character constant, '\033' */ - yylval->integer = strtoll(yytext + 2, NULL, 8); - return INT; +"false" { + yylval->integer = 0; + return BOOL; } -'\\b[01]+' { - /* binary character constant, '\b101' */ - yylval->integer = strtoll(yytext + 3, NULL, 2); - return INT; +'[^'\\]' { + /* regular character constant, 'a' */ + yylval->integer = yytext[1]; + return CHAR; } '\\.' { /* escaped character constant */ yylval->integer = match_escape(yytext[2]); - return INT; + return CHAR; } "?" {return QUESTION;} @@ -185,7 +175,7 @@ STRING \"(\\.|[^"\\])*\" } {ID} { - yylval->str = yytext; + yylval->str = strdup(yytext); return ID; } @@ -194,7 +184,7 @@ STRING \"(\\.|[^"\\])*\" char *s = yytext + strlen(yytext); s[-1] = '\0'; - yylval->str = yytext; + yylval->str = strdup(yytext); return APPLY; } diff --git a/src/lower.c b/src/lower.c index c78ac96..bf36fae 100644 --- a/src/lower.c +++ b/src/lower.c @@ -108,12 +108,17 @@ static bool retval_is_const(struct retval r) return r.kind == CONST_I9 || r.kind == CONST_I27; } -static bool is_i9(struct ast_node *n) +static bool is_small_type(struct type *type) { - if (AST_TYPE(n->type).kind != AST_TYPE_PRIMITIVE) - return false; + switch (type->k) { + case TYPE_I9: + case TYPE_BOOL: + return true; - return AST_PRIMITIVE_TYPE(n->type).type == AST_I9; + default: + } + + return false; } #define retval_create() \ @@ -157,26 +162,27 @@ char *build_str(const char *fmt, ...) { return buf; } -static size_t get_scope_number(struct ast_node *id) +static size_t get_scope_number(struct ast *id) { /** @todo this mirrors what's in actualize.c:actualize_id, same comments * apply */ - struct ast_node *def = file_scope_find_var(id->scope, id); + struct ast *def = file_scope_find_var(id->scope, id->s); if (def) return def->scope->number; - def = file_scope_find_proc(id->scope, id); + def = file_scope_find_proc(id->scope, id->s); if (def) return def->scope->number; return 0; } -static char *mangle_idx(struct ast_node *id, size_t idx) +static char *mangle_idx(struct ast *id, size_t idx) { - assert(id->node_type == AST_ID); assert(id->scope); - const char *name = AST_ID(id).id; + assert(id->s); + + const char *name = id->s; /* oh wait, I need to do a variable lookup on the ID, not use the ID's * scope number, duh */ size_t number = get_scope_number(id); @@ -187,89 +193,74 @@ static char *mangle_idx(struct ast_node *id, size_t idx) return build_str("%s_s%zif%zi", name, number, idx); } -static char *mangle(struct ast_node *id) +static char *mangle(struct ast *id) { return mangle_idx(id, 0); } -static int lower_expr(struct lower_state *s, struct ast_node *e, +static int lower_expr(struct lower_state *s, struct ast *e, struct vec *retval); -static int lower_statement(struct lower_state *s, struct ast_node *n); +static int lower_statement(struct lower_state *s, struct ast *n); -static void output_id(struct ast_node *id) +static void output_ast_id(struct ast *id) { + assert(id->s); char *name = mangle(id); printf("%s", name); free(name); } -static int lower_global_var(struct ast_node *n) +static int lower_global_var(struct ast *n) { /* trivial types are reasonably easy, but stuff like compound types need * a lot of work */ - struct ast_node *type = AST_VAR(n).type; - if (AST_TYPE(type).kind != AST_TYPE_PRIMITIVE) { + struct type *type = var_type(n); + if (is_primitive(type)) { semantic_error(n->scope->fctx, n, "only primitive globals currently implemented"); return -1; } - struct ast_node *id = AST_VAR(n).id; - struct ast_node *init = AST_VAR(n).init; - if (init->node_type != AST_CONST) { + struct ast *init = var_init(n); + if (init->k != AST_CONST_INT) { semantic_error(n->scope->fctx, n, - "constant expressions currently not implemented"); + "only constant expressions currently implemented"); return -1; } - output_id(id); + output_ast_id(n); printf(" = "); /* hmm, this might be useful elsewhere as well */ - switch (AST_PRIMITIVE_TYPE(type).type) { - case AST_I27: printf("i27 %lli", AST_CONST(init).integer); break; - case AST_I9: printf("i9 %lli", AST_CONST(init).integer); break; - default: - semantic_error(n->scope->fctx, n, - "unhandled primitive type"); - return -1; - } - + char *t = is_small_type(type) ? "i9" : "i27"; + printf("%s %lli", t, int_val(init)); printf(";\n"); return 0; } -static int lower_param(struct lower_state *s, struct ast_node *p) +static int lower_param(struct lower_state *s, struct ast *p) { UNUSED(s); - assert(p->node_type == AST_VAR); - struct ast_node *type = AST_VAR(p).type; - if (AST_TYPE(type).kind != AST_TYPE_PRIMITIVE) { + assert(p->k == AST_VAR_DEF); + struct type *type = var_type(p); + if (!is_primitive(type)) { semantic_error(p->scope->fctx, p, "only primitive params currently implemented"); return -1; } - assert(AST_VAR(p).init == NULL); - - switch (AST_PRIMITIVE_TYPE(type).type) { - case AST_I27: printf("i27 "); break; - case AST_I9: printf("i9 "); break; - default: - semantic_error(p->scope->fctx, p, - "unhandled primitive type"); - return -1; - } + assert(var_init(p) == NULL); - struct ast_node *id = AST_VAR(p).id; - output_id(id); + char *t = is_small_type(type) ? "i9" : "i27"; + printf("%s ", t); + output_ast_id(p); printf(","); return 0; } -static int lower_params(struct lower_state *s, struct ast_node *params) +static int lower_params(struct lower_state *s, struct ast *params) { - for (struct ast_node *p = params; p; p = p->next) { + foreach_node(p, params) { if (lower_param(s, p)) return -1; } @@ -277,20 +268,19 @@ static int lower_params(struct lower_state *s, struct ast_node *params) return 0; } -static int lower_var(struct lower_state *s, struct ast_node *v, +static int lower_var(struct lower_state *s, struct ast *v, struct vec *retval) { - assert(v->node_type == AST_VAR); + assert(v->k == AST_VAR_DEF); struct vec input = retval_create(); - if (lower_expr(s, AST_VAR(v).init, &input)) + if (lower_expr(s, var_init(v), &input)) return -1; - struct ast_node *id = AST_VAR(v).id; /* if we have a struct, we should add the member name to the base name * */ foreach_retval(ri, input) { struct retval r = retval_at(input, ri); - char *name = mangle_idx(id, ri); + char *name = mangle_idx(v, ri); /* I assume we're always dealing with i27 for now */ /** @todo qbt could maybe skip the type stuff except for casts */ printf("i27 %s = %s;\n", name, r.s); @@ -355,27 +345,24 @@ static void do_store(struct lower_state *s, struct vec *from, struct vec *to, } } -static int lower_cast(struct lower_state *s, struct ast_node *e, +static int lower_cast(struct lower_state *s, struct ast *e, struct vec *retval) { - assert(e->node_type == AST_CAST); + assert(e->k == AST_CAST); /** @todo make sure actualize removes casts that aren't of these types * */ - assert(AST_TYPE(e->type).kind == AST_TYPE_PRIMITIVE - || AST_TYPE(e->type).kind == AST_TYPE_POINTER); + assert(is_primitive(e->t)); - if (lower_expr(s, AST_CAST(e).expr, retval)) + if (lower_expr(s, cast_expr(e), retval)) return -1; - enum retval_kind kind = REG_I27; - if (is_i9(e)) - kind = REG_I9; + enum retval_kind kind = is_small_type(e->t) ? REG_I9 : REG_I27; foreach_retval(ri, *retval) { struct retval r = retval_at(*retval, ri); /* build new temporary cast result and replace the previous * retval */ - char *s = build_str("%s%s", "cast_", r.s); + char *s = build_str("%s%s", "cast", r.s); printf("%s %s = %s;\n", retval_kind_str(kind), s, r.s); free(r.s); @@ -388,49 +375,35 @@ static int lower_cast(struct lower_state *s, struct ast_node *e, return 0; } -static int lower_const(struct lower_state *s, struct ast_node *c, +static int lower_const(struct lower_state *s, struct ast *c, struct vec *retval) { UNUSED(s); - assert(c->node_type == AST_CONST); - if (AST_CONST(c).kind == AST_CONST_STRING) { - /* requires pushing strings as variables and replacing them with - * references */ - semantic_error(c->scope->fctx, c, - "string constant lowering not yet implemented"); - return -1; - } - - enum retval_kind type = CONST_I27; - if (AST_PRIMITIVE_TYPE(c->type).type == AST_I9) - type = CONST_I9; - - char *str = build_str("%lli", (long long int)AST_CONST(c).integer); - struct retval r = build_retval(type, str); + assert(is_const(c)); + char *str = build_str("%lli", (long long)c->v); + enum retval_kind kind = is_small_type(c->t) ? CONST_I9: CONST_I27; + struct retval r = build_retval(kind, str); vec_append(retval, &r); return 0; } -static int lower_assign(struct lower_state *s, struct ast_node *a, +static int lower_assign(struct lower_state *s, struct ast *a, struct vec *retval) { -#define IS_DEREF(t) (t->node_type == AST_UNOP && AST_UNOP(t).op == AST_DEREF) -#define IS_ARR(t) (t->node_type == AST_ARR_ACCESS) - - assert(a->node_type == AST_ASSIGN); - if (lower_expr(s, AST_ASSIGN(a).from, retval)) + assert(a->k == AST_ASSIGN); + if (lower_expr(s, assign_from(a), retval)) return -1; struct vec loc = retval_create(); struct vec off = retval_create(); - struct ast_node *to = AST_ASSIGN(a).to; - struct ast_node *base = to; - if (IS_DEREF(to)) - base = AST_UNOP(to).expr; - else if (IS_ARR(to)) { - base = AST_ARR_ACCESS(to).base; - if (lower_expr(s, AST_ARR_ACCESS(to).idx, &off)) { + struct ast *to = assign_to(a); + struct ast *base = to; + if (to->k == AST_DEREF) + base = unop_expr(to); + else if (to->k == AST_ARR) { + base = arr_base(to); + if (lower_expr(s, arr_idx(to), &off)) { retval_destroy(&loc); retval_destroy(&off); return -1; @@ -443,10 +416,10 @@ static int lower_assign(struct lower_state *s, struct ast_node *a, return -1; } - if (IS_DEREF(to)) { + if (to->k == AST_DEREF) { do_store(s, retval, &loc, NULL); } - else if (IS_ARR(to)) { + else if (to->k == AST_ARR) { do_store(s, retval, &loc, &off); } else { assert(vec_len(retval) == vec_len(&loc)); @@ -464,29 +437,25 @@ static int lower_assign(struct lower_state *s, struct ast_node *a, #undef IS_ARR } -static int lower_id(struct lower_state *s, struct ast_node *id, +static int lower_id(struct lower_state *s, struct ast *id, struct vec *retval) { UNUSED(s); - assert(id->node_type == AST_ID); + assert(id->k == AST_ID); char *m = mangle(id); - struct ast_node *type = id->type; - if (AST_TYPE(type).kind != AST_TYPE_PRIMITIVE - && AST_TYPE(type).kind != AST_TYPE_POINTER - && AST_TYPE(type).kind != AST_TYPE_SIGN) { + struct type *type = id->t; + if (!is_primitive(type) && type->k != TYPE_CALLABLE) { semantic_error(id->scope->fctx, id, "only primitive ids currently implemented"); return -1; } - enum retval_kind kind = REG_I27; - if (is_i9(id)) - kind = REG_I9; + enum retval_kind kind = is_small_type(type) ? REG_I9 : REG_I27; /* this likely isn't enough and we need to add the & to most things we * want to take the address of */ - if (AST_TYPE(type).kind == AST_TYPE_SIGN) { + if (type->k == TYPE_CALLABLE) { char *o = m; m = build_str("&%s", m); free(o); @@ -497,11 +466,12 @@ static int lower_id(struct lower_state *s, struct ast_node *id, return 0; } -static int lower_return(struct lower_state *s, struct ast_node *r, +static int lower_return(struct lower_state *s, struct ast *r, struct vec *retval) { - assert(r->node_type == AST_RETURN); - if (lower_expr(s, AST_RETURN(r).expr, retval)) + assert(r->k == AST_RETURN); + /** @todo defers, should they be handled here or in ast? */ + if (lower_expr(s, return_expr(r), retval)) return -1; printf("=> ( "); @@ -515,11 +485,11 @@ static int lower_return(struct lower_state *s, struct ast_node *r, return 0; } -static int lower_if(struct lower_state *s, struct ast_node *i, +static int lower_if(struct lower_state *s, struct ast *i, struct vec *retval) { - assert(i->node_type == AST_IF); - if (lower_expr(s, AST_IF(i).cond, retval)) + assert(i->k == AST_IF); + if (lower_expr(s, if_cond(i), retval)) return -1; assert(vec_len(retval) == 1); @@ -533,7 +503,7 @@ static int lower_if(struct lower_state *s, struct ast_node *i, printf("! %s -> %s;\n", (retval_at(*retval, 0)).s, bottom); /* a block counts as a statement in this case */ - if (lower_statement(s, AST_IF(i).body)) { + if (lower_statement(s, if_body(i))) { free(bottom); return -1; } @@ -542,7 +512,7 @@ static int lower_if(struct lower_state *s, struct ast_node *i, printf("%s:\n", bottom); free(bottom); - if (AST_IF(i).els && lower_statement(s, AST_IF(i).els)) { + if (if_else(i) && lower_statement(s, if_else(i))) { free(out); return -1; } @@ -552,11 +522,11 @@ static int lower_if(struct lower_state *s, struct ast_node *i, return 0; } -static int lower_for(struct lower_state *s, struct ast_node *f, +static int lower_for(struct lower_state *s, struct ast *f, struct vec *retval) { - assert(f->node_type == AST_FOR); - if (lower_statement(s, AST_FOR(f).pre)) + assert(f->k == AST_FOR); + if (lower_statement(s, for_pre(f))) return -1; long long uniq = s->uniq++; @@ -570,19 +540,19 @@ static int lower_for(struct lower_state *s, struct ast_node *f, printf("-> %s;\n", out); printf("%s:\n", top); - if (lower_statement(s, AST_FOR(f).body)) { + if (lower_statement(s, for_body(f))) { pop_loop(s); return -1; } printf("%s:\n", bottom); - if (lower_statement(s, AST_FOR(f).post)) { + if (lower_statement(s, for_post(f))) { pop_loop(s); return -1; } printf("%s:\n", out); - if (lower_expr(s, AST_FOR(f).cond, retval)) { + if (lower_expr(s, for_cond(f), retval)) { pop_loop(s); return -1; } @@ -594,7 +564,7 @@ static int lower_for(struct lower_state *s, struct ast_node *f, return 0; } -static int lower_expr_if(struct lower_state *s, struct ast_node *i, +static int lower_expr_if(struct lower_state *s, struct ast *i, struct vec *retval) { semantic_error(i->scope->fctx, i, @@ -602,19 +572,19 @@ static int lower_expr_if(struct lower_state *s, struct ast_node *i, return 0; } -static int lower_binop(struct lower_state *s, struct ast_node *i, +static int lower_binop(struct lower_state *s, struct ast *i, struct vec *retval) { struct vec l = retval_create(); struct vec r = retval_create(); - if (lower_expr(s, AST_BINOP(i).left, &l)) { + if (lower_expr(s, binop_left(i), &l)) { retval_destroy(&l); retval_destroy(&r); return -1; } - if (lower_expr(s, AST_BINOP(i).right, &r)) { + if (lower_expr(s, binop_right(i), &r)) { retval_destroy(&l); retval_destroy(&r); return -1; @@ -623,12 +593,12 @@ static int lower_binop(struct lower_state *s, struct ast_node *i, assert(vec_len(&l) == 1); assert(vec_len(&r) == 1); - char *name = build_str("tmp%lli", (long long)s->uniq++); + char *name = build_str("binop%lli", (long long)s->uniq++); struct retval ret = build_retval(REG_I27, name); vec_append(retval, &ret); char *op = ""; - switch (AST_BINOP(i).op) { + switch (i->k) { case AST_ADD: op = "+"; break; case AST_SUB: op = "-"; break; case AST_MUL: op = "*"; break; @@ -636,6 +606,50 @@ static int lower_binop(struct lower_state *s, struct ast_node *i, case AST_REM: op = "%"; break; case AST_LSHIFT: op = "<<"; break; case AST_RSHIFT: op = ">>"; break; + default: semantic_error(i->scope->fctx, i, + "unimplemented binary operation"); + retval_destroy(&l); + retval_destroy(&r); + return -1; + } + + printf("i27 %s = %s %s %s;\n", name, + (retval_at(l, 0)).s, + op, + (retval_at(r, 0)).s); + + retval_destroy(&l); + retval_destroy(&r); + return 0; +} + +static int lower_comparison(struct lower_state *s, struct ast *i, struct vec *retval) +{ + /* very similar to lower_binop, hmm */ + struct vec l = retval_create(); + struct vec r = retval_create(); + + if (lower_expr(s, comparison_left(i), &l)) { + retval_destroy(&l); + retval_destroy(&r); + return -1; + } + + if (lower_expr(s, comparison_right(i), &r)) { + retval_destroy(&l); + retval_destroy(&r); + return -1; + } + + assert(vec_len(&l) == 1); + assert(vec_len(&r) == 1); + + char *name = build_str("comp%lli", (long long)s->uniq++); + struct retval ret = build_retval(REG_I27, name); + vec_append(retval, &ret); + + char *op = ""; + switch (i->k) { case AST_LT: op = "<"; break; case AST_GT: op = ">"; break; case AST_LE: op = "<="; break; @@ -643,13 +657,13 @@ static int lower_binop(struct lower_state *s, struct ast_node *i, case AST_NE: op = "!="; break; case AST_EQ: op = "=="; break; default: semantic_error(i->scope->fctx, i, - "unimplemented binary operation"); + "unimplemented comparison operation"); retval_destroy(&l); retval_destroy(&r); return -1; } - printf("i27 %s = %s %s %s;\n", name, + printf("i9 %s = %s %s %s;\n", name, (retval_at(l, 0)).s, op, (retval_at(r, 0)).s); @@ -659,20 +673,20 @@ static int lower_binop(struct lower_state *s, struct ast_node *i, return 0; } -static int lower_call(struct lower_state *s, struct ast_node *c, +static int lower_call(struct lower_state *s, struct ast *c, struct vec *retval) { - assert(c->node_type == AST_CALL); + assert(c->k == AST_CALL); struct vec call = retval_create(); - if (lower_expr(s, AST_CALL(c).expr, &call)) { + if (lower_expr(s, call_expr(c), &call)) { retval_destroy(&call); return -1; } /* collect all args */ struct vec args = retval_create(); - foreach_node(a, AST_CALL(c).args) { + foreach_node(a, call_args(c)) { struct vec arg = retval_create(); if (lower_expr(s, a, &arg)) { retval_destroy(&arg); @@ -699,37 +713,51 @@ static int lower_call(struct lower_state *s, struct ast_node *c, printf("%s, ", r.s); } -#define IS_VOID(t) \ - (t->node_type == AST_TYPE && AST_TYPE(t).kind == AST_TYPE_PRIMITIVE && \ - AST_PRIMITIVE_TYPE(t).type == AST_VOID) - - if (!IS_VOID(c->type)) { + if (!is_primitive(c->t) && c->t->k != TYPE_VOID) { semantic_error(c->scope->fctx, c, - "only void return type implemented"); + "only primitive return types implemented"); retval_destroy(&args); return -1; } - printf(") => ();\n"); + printf(") => ( "); + + int i = 0; + foreach_type(t, c->t) { + char *s = build_str("r%i\n", i); + enum retval_kind k = is_small_type(t) ? REG_I9 : REG_I27; + struct retval r = build_retval(k, s); + vec_append(retval, &r); + i++; + } + + printf(" );\n"); retval_destroy(&args); return 0; } -static int lower_expr(struct lower_state *s, struct ast_node *e, +static int lower_expr(struct lower_state *s, struct ast *e, struct vec *retval) { if (!e) return 0; - switch (e->node_type) { + if (is_const(e)) + return lower_const(s, e, retval); + + if (is_binop(e)) + return lower_binop(s, e, retval); + + if (is_comparison(e)) + return lower_comparison(s, e, retval); + + switch (e->k) { + case AST_VAR_DEF: return lower_var(s, e, retval); case AST_ID: return lower_id(s, e, retval); /* var is considered an expression in this case */ - case AST_VAR: return lower_var(s, e, retval); case AST_CAST: return lower_cast(s, e, retval); - case AST_CONST: return lower_const(s, e, retval); case AST_RETURN: return lower_return(s, e, retval); case AST_ASSIGN: return lower_assign(s, e, retval); - case AST_BINOP: return lower_binop(s, e, retval); case AST_CALL: return lower_call(s, e, retval); case AST_IF: return lower_expr_if(s, e, retval); default: @@ -741,25 +769,24 @@ static int lower_expr(struct lower_state *s, struct ast_node *e, return 0; } -static int lower_block(struct lower_state *s, struct ast_node *body) +static int lower_block(struct lower_state *s, struct ast *block) { - assert(body->node_type == AST_BLOCK); - assert(!ast_flags(body, AST_FLAG_DOEXPR)); - struct ast_node *stmt = AST_BLOCK(body).body; - for (; stmt; stmt = stmt->next) { - if (lower_statement(s, stmt)) + assert(block->k == AST_BLOCK); + assert(!ast_flags(block, AST_FLAG_DOEXPR)); + foreach_node(n, block_body(block)) { + if (lower_statement(s, n)) return -1; } return 0; } -static int lower_statement(struct lower_state *s, struct ast_node *n) +static int lower_statement(struct lower_state *s, struct ast *n) { struct vec retval = retval_create(); int ret = 0; - switch (n->node_type) { + switch (n->k) { case AST_RETURN: ret = lower_return(s, n, &retval); break; case AST_IF: ret = lower_if(s, n, &retval); break; case AST_FOR: ret = lower_for(s, n, &retval); break; @@ -771,24 +798,22 @@ static int lower_statement(struct lower_state *s, struct ast_node *n) return ret; } -static int lower_proc(struct ast_node *n) +static int lower_proc(struct ast *n) { - assert(n->node_type == AST_PROC); + assert(n->k == AST_PROC_DEF); /* nobody uses the proc, so no need to do anything */ - if (n->uses == 0 && !ast_flags(AST_PROC(n).id, AST_FLAG_NOMANGLE)) + if (n->uses == 0 && !ast_flags(n, AST_FLAG_NOMANGLE)) return 0; struct lower_state state = create_state(); /* name */ - struct ast_node *id = AST_PROC(n).id; - output_id(id); + output_ast_id(n); /* args */ printf("("); - struct ast_node *sign = AST_PROC(n).sign; - if (lower_params(&state, AST_SIGN_TYPE(sign).params)) { + if (lower_params(&state, proc_params(n))) { destroy_state(&state); return -1; } @@ -799,7 +824,7 @@ static int lower_proc(struct ast_node *n) /* body */ printf("{\n"); - if (lower_block(&state, AST_PROC(n).body)) { + if (lower_block(&state, proc_body(n))) { destroy_state(&state); return -1; } @@ -809,19 +834,13 @@ static int lower_proc(struct ast_node *n) return 0; } -static int lower_actual(struct ast_node *n) -{ - assert(AST_TYPE(n).kind == AST_TYPE_CONSTRUCT); - return 0; -} - -static int _lower_actuals(struct scope *root) +int lower(struct scope *root) { /* go through all child scopes but only do actual work on file-scope * includes are allowed inside procs etc to make something only locally * visible */ for (struct scope *c = root->children; c; c = c->next) { - if (_lower_actuals(c)) + if (lower(c)) return -1; } @@ -842,17 +861,3 @@ static int _lower_actuals(struct scope *root) return 0; } - -int lower_actuals(struct scope *root) -{ - int ret = _lower_actuals(root); - /* actuals are currently global, would it make more sense for them to be - * scope-local? */ - for (struct actual *a = root->actuals; a; a = a->next) { - assert(a->node); - if (lower_actual(a->node)) - return -1; - } - - return ret; -} diff --git a/src/parser.y b/src/parser.y index d6aefc8..51d950c 100644 --- a/src/parser.y +++ b/src/parser.y @@ -11,6 +11,8 @@ #include #include #include +#include + #include %} @@ -26,12 +28,15 @@ %parse-param {void *scanner} {struct parser* parser} %union { - struct ast_node *node; + struct ast *node; + struct type *type; long long integer; char *str; }; %token INT +%token CHAR +%token BOOL %token FLOAT %token STRING %token ID @@ -122,16 +127,18 @@ %left "::" /* why doesn't bison allow <*> for %nterm? would be so much easier */ -%nterm import binop unop decls expr +%nterm import binop unop decls opt_decls expr %nterm while do_while statement statements body references macro %nterm exprs if for case cases switch const -%nterm func_sign type var_decl var +%nterm var_decl var %nterm var_init proc %nterm alias trait enum_val enums enum top unit id %nterm embed param_decl members %nterm top_if const_if const_for defer goto assign %nterm construct construct_args construct_arg -%nterm statelet apply types +%nterm statelet apply + +%nterm types type opt_type %nterm tagged_struct expr_if @@ -140,14 +147,15 @@ %nterm macro_expand type_expand -%nterm type_params type_param opt_for_inits for_inits for_init +%nterm type_params opt_type_params type_param opt_for_inits for_inits for_init /* array stuff */ %nterm arr arr_inits arr_init /* optional stuff */ %nterm opt_exprs proc_decl member opt_members -%nterm opt_statements opt_types opt_type_params +%nterm opt_statements +%nterm opt_types opt_sign_decls sign_decls sign_decl sign_var_decl %{ @@ -209,7 +217,7 @@ static void yyerror(YYLTYPE *yylloc, void *lexer, * @param c Escape character without backslash. * @return Corresponding value. */ -static long long match_escape(char c); +static char match_escape(char c); /** * Similar to strdup() but skips quotation marks that would @@ -219,18 +227,18 @@ static long long match_escape(char c); * @param s String to clone, with quotation marks surrounding it. * @return Identical string but without quotation marks around it. */ -static const char *clone_string(const char *s); +static char *strip(const char *s); %} %start input; %% id - : ID {$$ = gen_id(strdup($1), src_loc(@$));} + : ID {$$ = gen_id($1, src_loc(@$));} apply : APPLY { - $$ = gen_id(strdup($1), src_loc(@$)); + $$ = gen_id($1, src_loc(@$)); } var @@ -238,10 +246,10 @@ var | var_init embed - : "embed" "(" STRING ")" { $$ = gen_embed(clone_string($3), src_loc(@$)); } + : "embed" "(" STRING ")" { $$ = gen_embed(strip($3), src_loc(@$)); } import - : "import" STRING { $$ = gen_import(clone_string($2), src_loc(@$)); } + : "import" STRING { $$ = gen_import(strip($2), src_loc(@$)); } assign : expr "=" expr { $$ = gen_assign($1, $3, src_loc(@$)); } @@ -265,12 +273,12 @@ binop | expr ">>=" expr { $$ = gen_binop(AST_ASSIGN_RSHIFT, $1, $3, src_loc(@$)); } - | expr "<" expr { $$ = gen_binop(AST_LT, $1, $3, src_loc(@$)); } - | expr ">" expr { $$ = gen_binop(AST_GT, $1, $3, src_loc(@$)); } - | expr "<=" expr { $$ = gen_binop(AST_LE, $1, $3, src_loc(@$)); } - | expr ">=" expr { $$ = gen_binop(AST_GE, $1, $3, src_loc(@$)); } - | expr "!=" expr { $$ = gen_binop(AST_NE, $1, $3, src_loc(@$)); } - | expr "==" expr { $$ = gen_binop(AST_EQ, $1, $3, src_loc(@$)); } + | expr "<" expr { $$ = gen_comparison(AST_LT, $1, $3, src_loc(@$)); } + | expr ">" expr { $$ = gen_comparison(AST_GT, $1, $3, src_loc(@$)); } + | expr "<=" expr { $$ = gen_comparison(AST_LE, $1, $3, src_loc(@$)); } + | expr ">=" expr { $$ = gen_comparison(AST_GE, $1, $3, src_loc(@$)); } + | expr "!=" expr { $$ = gen_comparison(AST_NE, $1, $3, src_loc(@$)); } + | expr "==" expr { $$ = gen_comparison(AST_EQ, $1, $3, src_loc(@$)); } unop : "-" expr { $$ = gen_unop(AST_NEG, $2, src_loc(@$)); } @@ -280,15 +288,17 @@ unop arr_init : "=>" const_expr "..." const_expr "=" expr { - $$ = gen_var($2, $4, $6, src_loc(@$)); + assert(0 && "range array init is unimplemented"); + abort(); } | "=>" const_expr "=" expr { - $$ = gen_var($2, NULL, $4, src_loc(@$)); + assert(0 && "range array init is unimplemented"); + abort(); } | expr arr_inits - : arr_init "," arr_inits { $$ = $1; $1->next = $3; } + : arr_init "," arr_inits { $$ = $1; $1->n = $3; } | arr_init arr @@ -298,10 +308,22 @@ param_decl : type { $$ = gen_var(NULL, $1, NULL, src_loc(@$)); } | var_decl +sign_decl + : type + | sign_var_decl + decls - : param_decl "," decls { $$ = $1; $1->next = $3; } + : param_decl "," decls { $$ = $1; $1->n = $3; } | param_decl +sign_decls + : sign_decl "," sign_decls { $$ = $1; $1->n = $3; } + | sign_decl + +opt_decls + : decls + | {$$ = NULL;} + defer : "defer" body { $$ = gen_defer($2, src_loc(@$)); } @@ -352,19 +374,20 @@ const_unop const_expr : "(" const_expr ")" { $$ = $2; } - | INT { $$ = gen_int($1, src_loc(@$)); } + | INT { $$ = gen_const_int($1, src_loc(@$)); } + | CHAR { $$ = gen_const_char($1, src_loc(@$)); } + | BOOL { $$ = gen_const_bool($1, src_loc(@$)); } | const_binop | const_unop | id /* TODO: concatenate multiple strings together? Or is that the lexer's job? */ expr - : expr "." id { $$ = gen_dot($1, $3, src_loc(@$)); } - | "..." id { $$ = $2; } - | INT { $$ = gen_int($1, src_loc(@$)); } - | STRING { - $$ = gen_string(clone_string($1), src_loc(@$)); - } + : expr "." ID { $$ = gen_dot($3, $1, src_loc(@$)); } + | INT { $$ = gen_const_int($1, src_loc(@$)); } + | CHAR { $$ = gen_const_char($1, src_loc(@$)); } + | BOOL { $$ = gen_const_bool($1, src_loc(@$)); } + | STRING {$$ = gen_const_str(strip($1), src_loc(@$));} | "(" expr ")" { $$ = $2; } /* special rule, user is allowed to define new variables in if * statements etc but it should stand out, which is why we require @@ -381,10 +404,10 @@ expr | "do" switch { $$ = $2; ast_set_flags($$, AST_FLAG_DOEXPR); } | "do" "const" switch { $$ = $3; ast_set_flags($$, AST_FLAG_DOEXPR); } | expr "(" opt_exprs ")" { $$ = gen_call($1, $3, src_loc(@$)); } - | expr "[" expr "]" { $$ = gen_arr_access($1, $3, src_loc(@$)); } + | expr "[" expr "]" { $$ = gen_arr($1, $3, src_loc(@$)); } | "sizeof" expr { $$ = gen_sizeof($2, src_loc(@$)); } | expr "as" type { $$ = gen_cast($1, $3, src_loc(@$)); } - | id "::" type { $$ = gen_fetch($1, $3, src_loc(@$)); } + | ID "::" type { $$ = gen_fetch($1, $3, src_loc(@$)); } | macro_expand | construct | assign @@ -404,13 +427,13 @@ do_while } goto - : "goto" id { $$ = gen_goto(gen_label($2, src_loc(@$)), src_loc(@$)); } + : "goto" ID { $$ = gen_goto($[ID], NULL, src_loc(@$)); } statelet - : "return" exprs { $$ = gen_return($2, src_loc(@$)); } - | "return" { $$ = gen_return(NULL, src_loc(@$)); } - | "break" { $$ = gen_ctrl(AST_CTRL_BREAK, src_loc(@$)); } - | "continue" { $$ = gen_ctrl(AST_CTRL_CONTINUE, src_loc(@$)); } + : "return" exprs { $$ = gen_return($2, NULL, src_loc(@$)); } + | "return" { $$ = gen_return(NULL, NULL, src_loc(@$)); } + | "break" { $$ = gen_break(NULL, src_loc(@$)); } + | "continue" { $$ = gen_continue(NULL, src_loc(@$)); } | trait | import | alias @@ -420,7 +443,7 @@ statelet | error { /* TODO: figure out how to destroy any and all possible ast nodes we * may have generated up until the error */ - $$ = gen_empty(); + $$ = gen_empty(src_loc(@$)); parser->failed = true; /* If we're failing to parse a statement in a block, continue by trying to * parse the next statement in the block */ @@ -444,11 +467,11 @@ statement | const | enum | macro - | ";" { $$ = gen_empty(); } - | id ":" { $$ = gen_label($1, src_loc(@$)); } + | ";" { $$ = gen_empty(src_loc(@$)); } + | ID ":" { $$ = gen_label($[ID], NULL, src_loc(@$)); } statements - : statement statements { $$ = $1; $1->next = $2; } + : statement statements { $$ = $1; $1->n = $2; } | statement | statelet @@ -457,49 +480,49 @@ opt_statements | {$$ = NULL;} body - : "{" opt_statements "}" { $$ = gen_block($2, src_loc(@$)); } + : "{" opt_statements "}" { $$ = gen_block($2, NULL, src_loc(@$)); } references - : id "," references { $$ = $1; $$->next = $3; } + : id "," references { $$ = $1; $$->n = $3; } | "..." id { $$ = $2; ast_set_flags($$, AST_FLAG_VARIADIC); } | id macro - : "define" id "(" references ")" body { - $$ = gen_macro_construct($2, $4, $6, src_loc(@$)); + : "define" ID "(" references ")" body { + $$ = gen_macro_def($[ID], $[references], $[body], src_loc(@$)); ast_set_flags($6, AST_FLAG_UNHYGIENIC); } - | "define" id "(" references "..." id ")" body { + | "define" ID "(" references "..." id ")" body { ast_append($4, $6); - $$ = gen_macro_construct($2, $4, $8, src_loc(@$)); + $$ = gen_macro_def($[ID], $4, $8, src_loc(@$)); ast_set_flags($$, AST_FLAG_VARIADIC); ast_set_flags($8, AST_FLAG_UNHYGIENIC); } - | "define" id "(" ")" body { - $$ = gen_macro_construct($2, NULL, $5, src_loc(@$)); + | "define" ID "(" ")" body { + $$ = gen_macro_def($[ID], NULL, $5, src_loc(@$)); ast_set_flags($5, AST_FLAG_UNHYGIENIC); } exprs - : expr "," exprs { $$ = $1; $1->next = $3; } + : expr "," exprs { $$ = $1; $1->n = $3; } | expr construct_arg - : "." id "=" expr { + : "." ID "=" expr { $$ = gen_var($2, NULL, $4, src_loc(@$)); ast_set_flags($$, AST_FLAG_MEMBER); } construct_args - : construct_arg "," construct_args { $$ = $1; $1->next = $3; } + : construct_arg "," construct_args { $$ = $1; $1->n = $3; } | construct_arg construct - : apply "{" construct_args "}" { + : APPLY "{" construct_args "}" { /** @todo add type info? */ $$ = gen_init($3, src_loc(@$)); } - | apply "[" opt_types "]" "{" construct_args "}" { + | APPLY "[" opt_types "]" "{" construct_args "}" { $$ = gen_init($6, src_loc(@$)); } @@ -521,7 +544,7 @@ for_init | var_init for_inits - : for_init "," for_inits { $$ = $1; $$->next = $3; } + : for_init "," for_inits { $$ = $1; $$->n = $3; } | for_init opt_for_inits @@ -543,7 +566,7 @@ case } cases - : case cases { $$ = $1; $1->next = $2; } + : case cases { $$ = $1; $1->n = $2; } | case switch @@ -578,41 +601,43 @@ const : "const" const_if { $$ = $2; ast_set_flags($$, AST_FLAG_CONST); } | "const" const_for { $$ = $2; ast_set_flags($$, AST_FLAG_CONST); } -func_sign - : "(" decls "=>" type ")" { - $$ = gen_type(AST_TYPE_SIGN, $2, $4, src_loc(@$)); - } - | "(" decls ")" { $$ = gen_type(AST_TYPE_SIGN, $2, NULL, src_loc(@$)); } - | "(" decls "=>" ")" { $$ = gen_type(AST_TYPE_SIGN, $2, NULL, src_loc(@$)); } - | "(" "=>" type ")" { $$ = gen_type(AST_TYPE_SIGN, NULL, $3, src_loc(@$)); } - | "(" "=>" ")" { $$ = gen_type(AST_TYPE_SIGN, NULL, NULL, src_loc(@$)); } - | "(" ")" { $$ = gen_type(AST_TYPE_SIGN, NULL, NULL, src_loc(@$)); } +opt_sign_decls + : sign_decls + | {$$ = NULL;} type - : id { $$ = gen_type(AST_TYPE_ID, $1, NULL, src_loc(@$)); } - | "^" func_sign { + : ID { $$ = tgen_id($1, src_loc(@$)); } + | "^" "(" opt_sign_decls "=>" opt_type ")" { /* still not entirely sold on this signature, but it's not terrible I * guess */ - $$ = gen_type(AST_TYPE_POINTER, $2, NULL, src_loc(@$)); + $$ = tgen_callable($[opt_sign_decls], $[opt_type], src_loc(@$)); + } + | "^" "(" opt_sign_decls ")" { + $$ = tgen_callable($[opt_sign_decls], NULL, src_loc(@$)); } | "*" type { - $$ = gen_type(AST_TYPE_POINTER, $2, NULL, src_loc(@$)); + $$ = tgen_ptr($2, src_loc(@$)); } | "[" const_expr "]" type { - $$ = gen_type(AST_TYPE_ARR, $2, $4, src_loc(@$)); + assert(0 && "arrays unimplemented"); + abort(); } | "const" type { $$ = $2; } | "mut" type { - $$ = $2; ast_set_flags($$, AST_FLAG_MUTABLE); + $$ = $2; /* ignored for now */ } - | apply "[" opt_types "]" { - $$ = gen_type(AST_TYPE_CONSTRUCT, $1, $3, src_loc(@$)); + | APPLY "[" opt_types "]" { + $$ = tgen_construct($[APPLY], $[opt_types], src_loc(@$)); } +opt_type + : type + | {$$ = NULL;} + types - : type "," types { $$ = $1; $$->next = $3; } + : type "," types { $$ = $1; $$->n = $3; } | type opt_types @@ -620,32 +645,56 @@ opt_types | { $$ = NULL; } type_expand - : apply "[" opt_types "]" { $$ = gen_type_expand($1, $3, src_loc(@$)); } + : APPLY "[" opt_types "]" { $$ = gen_type_expand($1, $3, src_loc(@$)); } var_decl - : type id { $$ = gen_var($2, $1, NULL, src_loc(@$)); } + : type ID { $$ = gen_var($2, $1, NULL, src_loc(@$)); } + +sign_var_decl + : type ID { $$ = $1; free((void *)$[ID]);} var_init - : var_decl "=" expr { $$ = $1; $$->_var.init = $3; } - | "const" id "=" expr { $$ = gen_var($2, NULL, $4, src_loc(@$)); } - | "mut" id "=" expr { + : var_decl "=" expr { $$ = $1; var_init($$) = $3; } + | "const" ID "=" expr { $$ = gen_var($2, NULL, $4, src_loc(@$)); } + | "mut" ID "=" expr { $$ = gen_var($2, NULL, $4, src_loc(@$)); ast_set_flags($$, AST_FLAG_MUTABLE); } proc_decl - : id func_sign { - $$ = gen_proc($1, $2, NULL, src_loc(@$)); + : ID "(" opt_decls "=>" opt_type ")" { + $$ = gen_proc($[ID], + $[opt_decls], + $[opt_type], + NULL, + src_loc(@$)); + } + | ID "(" opt_decls ")" { + $$ = gen_proc($[ID], $[opt_decls], NULL, NULL, src_loc(@$)); } + proc - : id func_sign body { - $$ = gen_proc($1, $2, $3, src_loc(@$)); - ast_set_flags($$, $2->flags); - ast_set_flags($3, AST_FLAG_UNHYGIENIC); + : ID "(" opt_decls "=>" opt_type ")" body { + $$ = gen_proc($[ID], + $[opt_decls], + $[opt_type], + $[body], + src_loc(@$)); + } + | ID "(" opt_decls ")" body { + $$ = gen_proc($[ID], $[opt_decls], NULL, $[body], src_loc(@$)); + } + | "extern" ID "(" opt_decls "=>" opt_type ")" { + $$ = gen_proc($[ID], + $[opt_decls], + $[opt_type], + NULL, + src_loc(@$)); + + ast_set_flags($$, AST_FLAG_EXTERN); } - | "extern" id func_sign { - /* todo check that we don't have a variadic function */ - $$ = gen_proc($2, $3, NULL, src_loc(@$)); + | "extern" ID "(" opt_decls ")" { + $$ = gen_proc($[ID], $[opt_decls], NULL, NULL, src_loc(@$)); ast_set_flags($$, AST_FLAG_EXTERN); } @@ -657,7 +706,7 @@ member ; members - : member members { $$ = $1; $1->next = $2; } + : member members { $$ = $1; $1->n = $2; } | member opt_members @@ -665,31 +714,31 @@ opt_members | {$$ = NULL;} macro_expand - : apply "(" opt_exprs ")" { + : APPLY "(" opt_exprs ")" { $$ = gen_macro_expand($1, $3, src_loc(@$)); } tagged_struct - : "typedef" id "[" opt_type_params "]" "{" opt_members "}" { + : "typedef" ID "[" opt_type_params "]" "{" opt_members "}" { $$ = gen_struct($2, $4, $7, src_loc(@$)); } - | "typedef" id "{" opt_members "}" { + | "typedef" ID "{" opt_members "}" { $$ = gen_struct($2, NULL, $4, src_loc(@$)); } alias - : "typedef" id type { + : "typedef" ID type { $$ = gen_alias($2, $3, src_loc(@$)); } type_param - : id id { - struct ast_node *t = gen_type(AST_TYPE_ID, $1, NULL, src_loc(@1)); + : ID ID { + struct type *t = tgen_id($1, src_loc(@1)); $$ = gen_var($2, t, NULL, src_loc(@$)); } type_params - : type_param "," type_params { $$ = $1; $1->next = $3; } + : type_param "," type_params { $$ = $1; $1->n = $3; } | type_param opt_type_params @@ -697,28 +746,28 @@ opt_type_params | { $$ = NULL; } trait - : "define" id "[" opt_type_params "]" "{" opt_members "}" { - $$ = gen_trait($2, $4, $7, NULL, src_loc(@$)); + : "define" ID "[" opt_type_params "]" "{" opt_members "}" { + $$ = gen_trait($2, $4, $7, src_loc(@$)); } enum_val - : id { + : ID { $$ = gen_val($1, NULL, src_loc(@$)); } - | id "=" expr { + | ID "=" expr { $$ = gen_val($1, $3, src_loc(@$)); } enums - : enum_val "," enums { $$ = $1; $1->next = $3; } + : enum_val "," enums { $$ = $1; $1->n = $3; } | enum_val "," { $$ = $1; } | enum_val { $$ = $1; } enum - : "enum" id ":" type "{" enums "}" { + : "enum" ID ":" type "{" enums "}" { $$ = gen_enum($2, $4, $6, src_loc(@$)); } - | "enum" id "{" enums "}" { + | "enum" ID "{" enums "}" { $$ = gen_enum($2, NULL, $4, src_loc(@$)); ast_set_flags($$, AST_FLAG_UNTYPED); } @@ -755,9 +804,9 @@ top | "pub" import { $$ = $2; ast_set_flags($2, AST_FLAG_PUBLIC); } | "pub" alias { $$ = $2; ast_set_flags($2, AST_FLAG_PUBLIC); } | "pub" trait { $$ = $2; ast_set_flags($2, AST_FLAG_PUBLIC); } - | ";" { $$ = gen_empty(); } + | ";" { $$ = gen_empty(src_loc(@$)); } | error { - $$ = gen_empty(); + $$ = gen_empty(src_loc(@$)); parser->failed = true; /* ignore any content inside a top level thing and just move onto * the next one */ @@ -770,7 +819,7 @@ top unit : top { $$ = $1; } - | top unit { $$ = $1; $1->next = $2; } + | top unit { $$ = $1; $1->n = $2; } input : unit { parser->tree = $1; } @@ -832,7 +881,7 @@ static void yyerror(YYLTYPE *yylloc, void *lexer, src_issue(issue, msg); } -static long long match_escape(char c) +static char match_escape(char c) { switch (c) { case '\'': return '\''; @@ -849,13 +898,14 @@ static long long match_escape(char c) return c; } -static const char *clone_string(const char *str) +static char *strip(const char *str) { const size_t len = strlen(str) + 1; char *buf = malloc(len); if (!buf) { /* should probably try to handle the error in some way... */ internal_error("failed allocating buffer for string clone"); + free((void *)str); return NULL; } @@ -871,6 +921,7 @@ static const char *clone_string(const char *str) } buf[j] = 0; + free((void *)str); return buf; } diff --git a/src/scope.c b/src/scope.c index 205e858..13c3dce 100644 --- a/src/scope.c +++ b/src/scope.c @@ -74,14 +74,13 @@ void scope_set_flags(struct scope *scope, enum scope_flags flags) scope->flags |= flags; } -int scope_flags(struct scope *scope, enum scope_flags flags) +unsigned scope_flags(struct scope *scope, enum scope_flags flags) { assert(scope); return scope->flags & flags; } -static struct visible *create_visible(struct ast_node *id, - struct ast_node *node) +static struct visible *create_visible(char *id, struct ast *node) { struct visible *visible = calloc(1, sizeof(struct visible)); visible->id = id; @@ -89,8 +88,7 @@ static struct visible *create_visible(struct ast_node *id, return visible; } -struct visible *create_type(struct scope *scope, struct ast_node *id, - struct ast_node *type) +struct visible *create_type(struct scope *scope, char *id, struct ast *type) { struct visible *n = create_visible(id, type); if (!n) @@ -102,8 +100,7 @@ struct visible *create_type(struct scope *scope, struct ast_node *id, return n; } -struct visible *create_var(struct scope *scope, struct ast_node *id, - struct ast_node *var) +struct visible *create_var(struct scope *scope, char *id, struct ast *var) { struct visible *n = create_visible(id, var); if (!n) @@ -115,8 +112,7 @@ struct visible *create_var(struct scope *scope, struct ast_node *id, return n; } -struct visible *create_macro(struct scope *scope, struct ast_node *id, - struct ast_node *macro) +struct visible *create_macro(struct scope *scope, char *id, struct ast *macro) { struct visible *n = create_visible(id, macro); if (!n) @@ -128,8 +124,7 @@ struct visible *create_macro(struct scope *scope, struct ast_node *id, return n; } -struct visible *create_proc(struct scope *scope, struct ast_node *id, - struct ast_node *proc) +struct visible *create_proc(struct scope *scope, char *id, struct ast *proc) { struct visible *n = create_visible(id, proc); if (!n) @@ -141,16 +136,16 @@ struct visible *create_proc(struct scope *scope, struct ast_node *id, return n; } -int scope_add_var(struct scope *scope, struct ast_node *var) +int scope_add_var(struct scope *scope, struct ast *var) { - struct ast_node *exists = file_scope_find_var(scope, AST_VAR(var).id); + struct ast *exists = file_scope_find_var(scope, var_id(var)); if (exists) { semantic_error(scope->fctx, var, "var redefined"); semantic_info(scope->fctx, exists, "previously here"); return -1; } - create_var(scope, AST_VAR(var).id, var); + create_var(scope, var_id(var), var); if (scope->parent && scope_flags(scope, SCOPE_FILE) && ast_flags(var, AST_FLAG_PUBLIC)) return scope_add_var(scope->parent, var); @@ -158,10 +153,9 @@ int scope_add_var(struct scope *scope, struct ast_node *var) return 0; } -int scope_add_type(struct scope *scope, struct ast_node *id, - struct ast_node *type) +int scope_add_type(struct scope *scope, char *id, struct ast *type) { - struct ast_node *exists = file_scope_find_type(scope, id); + struct ast *exists = file_scope_find_type(scope, id); if (exists) { semantic_error(scope->fctx, type, "type redefined"); semantic_info(scope->fctx, exists, "previously here"); @@ -176,12 +170,10 @@ int scope_add_type(struct scope *scope, struct ast_node *id, return 0; } -int scope_add_macro(struct scope *scope, struct ast_node *macro) +int scope_add_macro(struct scope *scope, struct ast *macro) { - assert(macro->node_type == AST_MACRO_CONSTRUCT); - struct ast_node *exists = file_scope_find_macro(scope, - AST_MACRO_CONSTRUCT( - macro).id); + assert(macro->k == AST_MACRO_DEF); + struct ast *exists = file_scope_find_macro(scope, macro_def_id(macro)); if (exists) { semantic_error(scope->fctx, macro, "macro redefined"); semantic_info(scope->fctx, exists, "previously here"); @@ -189,7 +181,7 @@ int scope_add_macro(struct scope *scope, struct ast_node *macro) } /* always add to scope, do resolve checking later */ - create_macro(scope, AST_MACRO_CONSTRUCT(macro).id, macro); + create_macro(scope, macro_def_id(macro), macro); if (scope->parent && scope_flags(scope, SCOPE_FILE) && ast_flags(macro, AST_FLAG_PUBLIC)) return scope_add_macro(scope->parent, macro); @@ -197,11 +189,10 @@ int scope_add_macro(struct scope *scope, struct ast_node *macro) return 0; } -int scope_add_proc(struct scope *scope, struct ast_node *proc) +int scope_add_proc(struct scope *scope, struct ast *proc) { - assert(proc->node_type == AST_PROC); - struct ast_node *exists = - file_scope_find_proc(scope, AST_PROC(proc).id); + assert(proc->k == AST_PROC_DEF); + struct ast *exists = file_scope_find_proc(scope, proc_id(proc)); if (exists) { semantic_error(scope->fctx, proc, "proc redefined"); semantic_info(scope->fctx, exists, "previously here"); @@ -209,7 +200,7 @@ int scope_add_proc(struct scope *scope, struct ast_node *proc) } /* always add to scope, do resolve checking later */ - create_proc(scope, AST_PROC(proc).id, proc); + create_proc(scope, proc_id(proc), proc); if (scope->parent && scope_flags(scope, SCOPE_FILE) && ast_flags(proc, AST_FLAG_PUBLIC)) return scope_add_proc(scope->parent, proc); @@ -217,12 +208,12 @@ int scope_add_proc(struct scope *scope, struct ast_node *proc) return 0; } -int scope_add_trait(struct scope *scope, struct ast_node *trait) +int scope_add_trait(struct scope *scope, struct ast *trait) { - assert(trait->node_type == AST_TRAIT); + assert(trait->k == AST_TRAIT_DEF); - struct ast_node *id = AST_TRAIT(trait).id; - struct ast_node *exists = file_scope_find_type(scope, id); + char *id = trait_id(trait); + struct ast *exists = file_scope_find_type(scope, id); if (exists) { semantic_error(scope->fctx, trait, "type redefined"); semantic_info(scope->fctx, exists, "previously here"); @@ -237,105 +228,96 @@ int scope_add_trait(struct scope *scope, struct ast_node *trait) return 0; } -static struct ast_node *scope_find_visible(struct visible *v, - struct ast_node *id) +static struct ast *scope_find_visible(struct visible *v, char *id) { if (!v) return NULL; - while (v) { - if (same_id(v->id, id)) - return v->node; - - v = v->next; + foreach_visible(n, v) { + struct ast *node = n->node; + if (same_id(node->s, id)) + return node; } return NULL; } -struct ast_node *scope_find_type(struct scope *scope, struct ast_node *type) +struct ast *scope_find_type(struct scope *scope, char *id) { - return scope_find_visible(scope->types, type); + return scope_find_visible(scope->types, id); } -struct ast_node *file_scope_find_type(struct scope *scope, - struct ast_node *type) +struct ast *file_scope_find_type(struct scope *scope, char *id) { - assert(type->node_type == AST_ID); if (!scope) return NULL; - struct ast_node *found = scope_find_type(scope, type); + struct ast *found = scope_find_type(scope, id); if (found) return found; if (!scope_flags(scope, SCOPE_FILE)) - return file_scope_find_type(scope->parent, type); + return file_scope_find_type(scope->parent, id); return NULL; } -struct ast_node *scope_find_macro(struct scope *scope, struct ast_node *macro) +struct ast *scope_find_macro(struct scope *scope, char *id) { - return scope_find_visible(scope->macros, macro); + return scope_find_visible(scope->macros, id); } -struct ast_node *file_scope_find_macro(struct scope *scope, - struct ast_node *macro) +struct ast *file_scope_find_macro(struct scope *scope, char *id) { - assert(macro->node_type == AST_ID); if (!scope) return NULL; - struct ast_node *found = scope_find_macro(scope, macro); + struct ast *found = scope_find_macro(scope, id); if (found) return found; if (!scope_flags(scope, SCOPE_FILE)) - return file_scope_find_macro(scope->parent, macro); + return file_scope_find_macro(scope->parent, id); return NULL; } -struct ast_node *scope_find_proc(struct scope *scope, struct ast_node *proc) +struct ast *scope_find_proc(struct scope *scope, char *id) { - return scope_find_visible(scope->procs, proc); + return scope_find_visible(scope->procs, id); } -struct ast_node *file_scope_find_proc(struct scope *scope, - struct ast_node *proc) +struct ast *file_scope_find_proc(struct scope *scope, char *id) { - assert(proc->node_type == AST_ID); if (!scope) return NULL; - struct ast_node *found = scope_find_proc(scope, proc); + struct ast *found = scope_find_proc(scope, id); if (found) return found; if (!scope_flags(scope, SCOPE_FILE)) - return file_scope_find_proc(scope->parent, proc); + return file_scope_find_proc(scope->parent, id); return NULL; } -struct ast_node *scope_find_var(struct scope *scope, struct ast_node *var) +struct ast *scope_find_var(struct scope *scope, char *id) { - return scope_find_visible(scope->vars, var); + return scope_find_visible(scope->vars, id); } -struct ast_node *file_scope_find_var(struct scope *scope, struct ast_node *var) +struct ast *file_scope_find_var(struct scope *scope, char *id) { - assert(var->node_type == AST_ID); if (!scope) return NULL; - struct ast_node *found = scope_find_var(scope, var); + struct ast *found = scope_find_var(scope, id); if (found) return found; if (!scope_flags(scope, SCOPE_FILE)) - return file_scope_find_var(scope->parent, var); + return file_scope_find_var(scope->parent, id); return NULL; } @@ -355,7 +337,7 @@ void scope_add_scope(struct scope *parent, struct scope *child) parent->children = child; } -static int add_actual(struct actual *actuals, struct ast_node *node) +static int add_actual(struct actual *actuals, struct ast *node) { if (!actuals->node) { /* fill empty first element */ @@ -374,7 +356,7 @@ static int add_actual(struct actual *actuals, struct ast_node *node) return 0; } -int scope_add_actual(struct scope *scope, struct ast_node *node) +int scope_add_actual(struct scope *scope, struct ast *node) { return add_actual(scope->actuals, node); } -- cgit v1.3