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