aboutsummaryrefslogtreecommitdiff
path: root/src/analyze.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/analyze.c')
-rw-r--r--src/analyze.c778
1 files changed, 691 insertions, 87 deletions
diff --git a/src/analyze.c b/src/analyze.c
index 0c06096..04b7856 100644
--- a/src/analyze.c
+++ b/src/analyze.c
@@ -1,12 +1,23 @@
/* SPDX-License-Identifier: copyleft-next-0.3.1 */
/* Copyright 2024 Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
+#include <fwd/compiler.h>
#include <fwd/analyze.h>
+#include <fwd/rewrite.h>
+#include <fwd/mod.h>
+
#include <stdlib.h>
+#include <stdarg.h>
#include <string.h>
#include <assert.h>
+#include <dlfcn.h>
+
+enum state_flags {
+ STATE_PROC_REQ_FRAME = (1 << 0)
+};
struct state {
+ enum state_flags flags;
};
static int analyze(struct state *state, struct scope *scope, struct ast *node);
@@ -62,7 +73,13 @@ static int analyze_proc(struct state *state, struct scope *scope,
if (!proc_body(node))
return 0;
- return analyze_known_block(&proc_state, proc_scope, proc_body(node));
+ if (analyze_known_block(&proc_state, proc_scope, proc_body(node)))
+ return -1;
+
+ if (proc_state.flags & STATE_PROC_REQ_FRAME)
+ ast_set_flags(node, AST_REQ_FRAME);
+
+ return 0;
}
static int analyze_unop(struct state *state, struct scope *scope,
@@ -89,6 +106,19 @@ static int analyze_binop(struct state *state, struct scope *scope,
if (analyze(state, scope, rhs))
return -1;
+ /* allow pointer arithmetic, follows C conventions */
+ if (node->k == AST_ADD || node->k == AST_SUB) {
+ if (is_ptr_type(lhs->t->k) && is_int_type(rhs->t->k)) {
+ node->t = lhs->t;
+ return 0;
+ }
+
+ if (is_ptr_type(rhs->t->k) && is_int_type(lhs->t->k)) {
+ node->t = rhs->t;
+ return 0;
+ }
+ }
+
if (!types_match(lhs->t, rhs->t)) {
type_mismatch(scope, node, lhs->t, rhs->t);
@@ -118,17 +148,9 @@ static int analyze_comparison(struct state *state, struct scope *scope,
return -1;
}
- /** @todo check type is some primitive */
- char *tf = strdup("bool");
- if (!tf) {
- internal_error("failed allocating comparison bool str");
- return -1;
- }
-
- node->t = tgen_id(tf, node->loc);
+ node->t = tgen_type(TYPE_BOOL, NULL, NULL, node->loc);
if (!node->t) {
internal_error("failed allocating comparison bool type");
- free(tf);
return -1;
}
@@ -144,28 +166,6 @@ static int analyze_known_block(struct state *state, struct scope *scope, struct
if (analyze_list(state, scope, block_body(node)))
return -1;
- struct ast *err = block_error(node);
- if (!err)
- return 0;
-
- if (error_str(err))
- return 0;
-
- struct ast *id = error_id(err);
- assert(id);
-
- struct ast *def = file_scope_find_symbol(scope, id_str(id));
- if (!def) {
- semantic_error(scope, id, "no such symbol");
- return -1;
- }
-
- struct type *t = def->t;
- if (t->k != TYPE_ERR) {
- semantic_error(scope, id, "invalid error variable type");
- return -1;
- }
-
return 0;
}
@@ -195,10 +195,14 @@ static int analyze_var(struct state *state, struct scope *scope,
static int analyze_let(struct state *state, struct scope *scope,
struct ast *node)
{
- if (analyze(state, scope, let_var(node)))
+ if (analyze(state, scope, let_expr(node)))
return -1;
- if (analyze(state, scope, let_expr(node)))
+ struct ast *var = let_var(node);
+ if (!(var_type(var)))
+ var_type(var) = (let_expr(node))->t;
+
+ if (analyze(state, scope, var))
return -1;
struct type *l = (let_var(node))->t;
@@ -221,16 +225,40 @@ static int analyze_init(struct state *state, struct scope *scope,
if (analyze(state, scope, init_body(node)))
return -1;
- /** @todo check that all parameters match, though that requires that the
- * type is defined in fwd and not in C++, so this might have to wait a
- * bit */
- node->t = tgen_construct(strdup(init_id(node)), init_args(node),
- node->loc);
- if (!node->t) {
- internal_error("failed allocating type for construct");
+ internal_error("init unimplemented");
+ return -1;
+}
+
+static int deduce_closure_types(struct scope *scope, struct ast *node, struct type *type)
+{
+ /* a bit of checking duplication here, hmm */
+ if (node->k != AST_CLOSURE)
+ return 0;
+
+ if (type->k != TYPE_CLOSURE && type->k != TYPE_PURE_CLOSURE) {
+ semantic_error(scope, node, "surprise closure");
+ return -1;
+ }
+
+ struct ast *param = closure_bindings(node);
+ struct type *t = (type->k == TYPE_CLOSURE)
+ ? tclosure_args(type)
+ : tpure_closure_args(type)
+ ;
+
+ if (ast_list_len(param) != type_list_len(t)) {
+ semantic_error(scope, node, "expected %zu closure parameters, got %zu",
+ type_list_len(t), ast_list_len(param));
return -1;
}
+ for (; param && t; param = param->n, t = t->n) {
+ if (var_type(param))
+ continue;
+
+ var_type(param) = t;
+ }
+
return 0;
}
@@ -248,8 +276,6 @@ static int analyze_call(struct state *state, struct scope *scope,
struct type *types = callable_types(expr->t);
struct ast *args = call_args(node);
- if (analyze_list(state, scope, args))
- return -1;
size_t expected = type_list_len(types);
size_t got = ast_list_len(args);
@@ -260,15 +286,24 @@ static int analyze_call(struct state *state, struct scope *scope,
}
struct type *type = types;
- foreach_node(arg, args) {
+ struct ast *arg = args;
+ for (; arg && type; type = type->n, arg = arg->n) {
+ if (deduce_closure_types(scope, arg, type))
+ return -1;
+
+ if (analyze(state, scope, arg))
+ return -1;
+
+ if (arg->k == AST_CLOSURE)
+ state->flags |= STATE_PROC_REQ_FRAME;
+
if (!types_match(type, arg->t)) {
- type_mismatch(scope, node, type, arg->t);
+ type_mismatch(scope, arg, type, arg->t);
return -1;
}
/* clone group info */
arg->t->group = type->group;
- type = type->n;
}
node->t = tgen_void(node->loc);
@@ -277,7 +312,7 @@ static int analyze_call(struct state *state, struct scope *scope,
return -1;
}
- return analyze(state, scope, call_err(node));
+ return 0;
}
static int analyze_ref(struct state *state, struct scope *scope,
@@ -313,7 +348,7 @@ static int analyze_deref(struct state *state, struct scope *scope,
semantic_error(node->scope, node,
"deref of raw ptr not allowed");
semantic_info(node->scope, node,
- "use fwd_null() to convert to ref");
+ "use a nil check to convert to ref");
return -1;
}
@@ -323,7 +358,7 @@ static int analyze_deref(struct state *state, struct scope *scope,
return -1;
}
- node->t = tptr_base(expr->t);
+ node->t = tref_base(expr->t);
return 0;
}
@@ -362,7 +397,6 @@ static int analyze_closure(struct state *state, struct scope *scope,
}
node->scope = closure_scope;
- closure_scope->flags |= SCOPE_PROC;
scope_add_scope(scope, closure_scope);
if (analyze_list(state, closure_scope, closure_bindings(node)))
@@ -396,17 +430,10 @@ static int analyze_int(struct state *state, struct scope *scope,
(void)state;
(void)scope;
- /** @todo do this properly, very hacky, bad bad bad */
- char *i = strdup("int");
- if (!i) {
- internal_error("failed allocating constant int type string");
- return -1;
- }
-
- node->t = tgen_id(i, node->loc);
+ /* start with largest possible and work down */
+ node->t = tgen_type(TYPE_I64, NULL, NULL, node->loc);
if (!node->t) {
internal_error("failed allocating constant int type");
- free(i);
return -1;
}
@@ -443,6 +470,189 @@ static int analyze_str(struct state *state, struct scope *scope,
return 0;
}
+static int analyze_template(struct state *state, struct scope *scope, struct ast *node)
+{
+ struct scope *template_scope = create_scope();
+ scope_add_scope(scope, template_scope);
+
+ /* enough for vec example since traits are as of yet unimplemented */
+
+ node->t = tgen_void(node->loc);
+ return 0;
+}
+
+static int analyze_instance(struct state *state, struct scope *scope, struct ast *node)
+{
+ (void)state;
+
+ struct ast *id = instance_templ(node);
+ struct ast *template = file_scope_find_template(scope, id_str(id));
+ if (!template) {
+ semantic_error(scope, node, "no such template: %s\n", id_str(id));
+ return -1;
+ }
+
+ /** @todo check that type implements trait */
+ struct ast *params = template_type_params(template);
+ struct type *args = instance_type_args(node);
+ if (ast_list_len(params) != type_list_len(args)) {
+ semantic_error(scope, node, "expected %zu types, got %zu\n",
+ ast_list_len(params), type_list_len(args));
+ return -1;
+ }
+
+ /* make a working copy that we modify */
+ struct ast *copy = clone_ast(template);
+ if (!copy) {
+ internal_error("failed copying template body");
+ return -1;
+ }
+
+ if (rewrite_holes(copy, instance_id(node)))
+ return -1;
+
+ /* replace type args */
+ struct ast *p = params;
+ struct type *a = args;
+ for (; p && a; p = p->n, a = a->n) {
+ if (rewrite_types(copy, var_id(p), tid_str(a)))
+ return -1;
+ }
+
+ printf("// --- instance %s ---\n", instance_id(node));
+ ast_dump(0, copy);
+
+ /* analyze newly initialized things (might not work for supertemplates?) */
+ if (analyze_root(scope, template_body(copy)))
+ return -1;
+
+ node->t = tgen_void(node->loc);
+ return 0;
+}
+
+static int analyze_struct(struct state *state, struct scope *scope, struct ast *node)
+{
+ struct scope *struct_scope = create_scope();
+ scope_add_scope(scope, struct_scope);
+ node->scope = struct_scope;
+
+ if (analyze_list(state, struct_scope, struct_body(node)))
+ return -1;
+
+ node->t = tgen_void(node->loc);
+ return 0;
+}
+
+static int analyze_construct(struct state *state, struct scope *scope, struct ast *node)
+{
+ struct ast *def = file_scope_find_type(scope, construct_id(node));
+ if (!def) {
+ semantic_error(scope, node, "no such type: %s", construct_id(node));
+ return -1;
+ }
+
+ if (def->k != AST_STRUCT_DEF) {
+ semantic_error(scope, node, "type is not a struct");
+ return -1;
+ }
+
+ struct ast *params = struct_body(def);
+ struct ast *args = construct_members(node);
+
+ if (ast_list_len(params) != ast_list_len(args)) {
+ semantic_error(scope, node, "expected %zu args, got %zu\n",
+ ast_list_len(params), ast_list_len(args));
+ return -1;
+ }
+
+ foreach_node(arg, args) {
+ struct ast *exists = scope_find_symbol(def->scope, construction_id(arg));
+ if (!exists) {
+ semantic_error(scope, arg, "no member %s in %s\n",
+ construction_id(arg), construct_id(node));
+ return -1;
+ }
+
+ struct ast *expr = construction_expr(arg);
+ if (analyze(state, scope, expr))
+ return -1;
+
+ if (!types_match(exists->t, expr->t)) {
+ type_mismatch(scope, arg, exists->t, expr->t);
+ return -1;
+ }
+
+ /** @todo check for duplicates, preferably not in O(n^2) or with
+ * a memory allocation? */
+ }
+
+ node->t = tgen_id(strdup(construct_id(node)), node->loc);
+ return 0;
+}
+
+static int analyze_explode(struct state *state, struct scope *scope, struct ast *node)
+{
+ if (analyze(state, scope, explode_expr(node)))
+ return -1;
+
+ struct type *type = (explode_expr(node))->t;
+ if (type->k != TYPE_ID) {
+ char *str = type_str(type);
+ semantic_error(scope, explode_expr(node),
+ "expected struct type, got %s\n",
+ str);
+ free(str);
+ return -1;
+ }
+
+ struct ast *def = file_scope_find_type(scope, type->id);
+ if (!def || def->k != AST_STRUCT_DEF) {
+ semantic_error(scope, explode_expr(node),
+ "no such struct type: %s\n",
+ type->id);
+ return -1;
+ }
+
+ struct ast *params = struct_body(def);
+ struct ast *args = explode_deconstruct(node);
+
+ if (ast_list_len(params) != ast_list_len(args)) {
+ semantic_error(scope, node, "expected %zu args, got %zu\n",
+ ast_list_len(params), ast_list_len(args));
+ return -1;
+ }
+
+ foreach_node(arg, args) {
+ struct ast *var = deconstruction_var(arg);
+ struct ast *exists = scope_find_symbol(def->scope, deconstruction_id(arg));
+ if (!exists) {
+ semantic_error(scope, arg, "no member %s in %s\n",
+ deconstruction_id(arg), type->id);
+ return -1;
+ }
+
+ if (!var_type(var))
+ (var_type(var)) = exists->t;
+
+ if (analyze(state, scope, var))
+ return -1;
+
+ if (!types_match(exists->t, var->t)) {
+ type_mismatch(scope, arg, exists->t, var->t);
+ return -1;
+ }
+
+ if (scope_add_symbol(scope, var))
+ return -1;
+
+ /** @todo check for duplicates, preferably not in O(n^2) or with
+ * a memory allocation? */
+ }
+
+ node->t = tgen_void(node->loc);
+ return 0;
+}
+
static int analyze_if(struct state *state, struct scope *scope,
struct ast *node)
{
@@ -464,38 +674,162 @@ static int analyze_if(struct state *state, struct scope *scope,
return 0;
}
-static int analyze_err_branch(struct state *state, struct scope *scope, struct ast *node)
+static int analyze_nil(struct state *state, struct scope *scope, struct ast *node)
{
- struct scope *branch_scope = create_scope();
- if (!branch_scope) {
- internal_error("failed allocating err branch scope");
+ (void)state;
+ (void)scope;
+
+ node->t = tgen_nil(node->loc);
+ return 0;
+}
+
+static bool castable_type(struct type *type)
+{
+ switch (type->k) {
+ case TYPE_PTR:
+ case TYPE_FUNC_PTR:
+ case TYPE_I8:
+ case TYPE_I16:
+ case TYPE_I32:
+ case TYPE_I64:
+ case TYPE_U8:
+ case TYPE_U16:
+ case TYPE_U32:
+ case TYPE_U64:
+ return true;
+
+ default:
+ return false;
+ }
+
+ return false;
+}
+
+static int analyze_as(struct state *state, struct scope *scope, struct ast *node)
+{
+ if (analyze(state, scope, as_expr(node)))
+ return -1;
+
+ if (analyze_type(state, scope, as_type(node)))
+ return -1;
+
+ /* for now, any 'register' size type can be cast to any other. Might
+ * restrict this, like signed to unsigned might be weird if its negative
+ * etc and should be done via a helper function */
+ struct type *expr_type = (as_expr(node))->t;
+ struct type *type = as_type(node);
+ if (!castable_type(expr_type)) {
+ char *s = type_str(expr_type);
+ semantic_error(scope, as_expr(node), "can't cast from %s\n", s);
+ free(s);
return -1;
}
- node->t = tgen_void(node->loc);
+ if (!castable_type(type)) {
+ char *s = type_str(type);
+ type_error(scope, type, "can't cast to %s\n", s);
+ free(s);
+ return -1;
+ }
- scope_add_scope(scope, branch_scope);
- struct ast *var = gen_var(strdup(err_branch_id(node)), NULL, node->loc);
- struct type *err_type = tgen_err(node->loc);
- var->t = err_type;
- scope_add_symbol(branch_scope, var);
- return analyze(state, branch_scope, err_branch_body(node));
+ node->t = type;
+ return 0;
}
-static int analyze_own(struct state *state, struct scope *scope, struct ast *node)
+static int analyze_sizeof(struct state *state, struct scope *scope, struct ast *node)
{
- struct ast *found = file_scope_find_symbol(scope, own_id(node));
- if (!found) {
- semantic_error(scope, node, "no symbol named \"%s\"",
- own_id(node));
+ if (analyze_type(state, scope, sizeof_type(node)))
+ return -1;
+
+ /* hmm, no built-in size_t */
+ node->t = tgen_type(TYPE_U64, NULL, NULL, node->loc);
+ return 0;
+}
+
+static int analyze_nil_check(struct state *state, struct scope *scope, struct ast *node)
+{
+ struct ast *expr = nil_check_expr(node);
+ if (analyze(state, scope, expr))
+ return -1;
+
+ if (!is_ptr_type(expr->t->k)) {
+ char *s = type_str(expr->t);
+ semantic_error(scope, expr, "expected ptr type, got %s", s);
+ free(s);
return -1;
}
- if (is_trivially_copyable(found->t))
- semantic_warn(scope, node, "trivially copyable type is never owned");
+ if (analyze(state, scope, nil_check_body(node)))
+ return -1;
+
+ struct ast *var = nil_check_ref(node);
+ struct type *base = tptr_base(expr->t);
+ struct type *ref = tgen_ref(clone_type(base), node->loc);
+ if (!var_type(var))
+ var_type(var) = ref;
+
+ if (analyze(state, scope, var))
+ return -1;
+
+ if (!types_match(ref, var->t)) {
+ type_mismatch(scope, var, ref, var->t);
+ return -1;
+ }
+
+ if (analyze(state, scope, nil_check_rest(node)))
+ return -1;
+
+ node->t = tgen_void(node->loc);
+ return 0;
+}
+
+static int analyze_forget(struct state *state, struct scope *scope, struct ast *node)
+{
+ struct ast *def = file_scope_find_symbol(scope, forget_id(node));
+ if (!def) {
+ semantic_error(scope, node, "no such symbol");
+ return -1;
+ }
node->t = tgen_void(node->loc);
- return analyze(state, scope, own_body(node));
+ return 0;
+}
+
+static int analyze_put(struct state *state, struct scope *scope, struct ast *put)
+{
+ struct ast *dst = put_dst(put);
+ if (analyze(state, scope, dst))
+ return -1;
+
+ struct type *type = dst->t;
+ if (type->k != TYPE_REF) {
+ char *str = type_str(type);
+ semantic_error(scope, put, "trying to write to non-ref type %s\n", str);
+ free(str);
+ return -1;
+ }
+
+ put->t = tref_base(type);
+ return 0;
+}
+
+static int analyze_write(struct state *state, struct scope *scope, struct ast *write)
+{
+ struct ast *src = write_src(write);
+ if (analyze(state, scope, src))
+ return -1;
+
+ struct ast *dst = write_dst(write);
+ if (analyze(state, scope, dst))
+ return -1;
+
+ if (!types_match(src->t, dst->t)) {
+ type_mismatch(scope, write, src->t, dst->t);
+ return -1;
+ }
+
+ write->t = tgen_void(write->loc);
+ return 0;
}
static int analyze(struct state *state, struct scope *scope, struct ast *node)
@@ -535,7 +869,6 @@ static int analyze(struct state *state, struct scope *scope, struct ast *node)
}
switch (node->k) {
- case AST_ERR_BRANCH: ret = analyze_err_branch(state, scope, node); break;
case AST_CLOSURE: ret = analyze_closure(state, scope, node); break;
case AST_PROC_DEF: ret = analyze_proc(state, scope, node); break;
case AST_VAR_DEF: ret = analyze_var(state, scope, node); break;
@@ -545,17 +878,45 @@ static int analyze(struct state *state, struct scope *scope, struct ast *node)
case AST_CALL: ret = analyze_call(state, scope, node); break;
case AST_REF: ret = analyze_ref(state, scope, node); break;
case AST_LET: ret = analyze_let(state, scope, node); break;
- case AST_OWN: ret = analyze_own(state, scope, node); break;
case AST_ID: ret = analyze_id(state, scope, node); break;
case AST_IF: ret = analyze_if(state, scope, node); break;
+ case AST_NIL_CHECK: ret = analyze_nil_check(state, scope, node); break;
+
case AST_CONST_INT: ret = analyze_int(state, scope, node); break;
case AST_CONST_STR: ret = analyze_str(state, scope, node); break;
+ case AST_INSTANCE: ret = analyze_instance(state, scope, node); break;
+ case AST_TEMPLATE: ret = analyze_template(state, scope, node); break;
+
+ case AST_STRUCT_DEF: ret = analyze_struct(state, scope, node); break;
+ case AST_CONSTRUCT: ret = analyze_construct(state, scope, node); break;
+ case AST_EXPLODE: ret = analyze_explode(state, scope, node); break;
+
+ case AST_SIZEOF: ret = analyze_sizeof(state, scope, node); break;
+ case AST_NIL: ret = analyze_nil(state, scope, node); break;
+ case AST_AS: ret = analyze_as(state, scope, node); break;
+
+ case AST_WRITE: ret = analyze_write(state, scope, node); break;
+ case AST_PUT: ret = analyze_put(state, scope, node); break;
+
+ case AST_FORGET: ret = analyze_forget(state, scope, node); break;
+
+ case AST_TRAIT_DEF:
+ node->t = tgen_void(node->loc);
+ ret = 0;
+ break;
+
case AST_EMPTY:
node->t = tgen_void(node->loc);
ret = 0;
break;
+
+ case AST_IMPORT:
+ node->t = tgen_void(node->loc);
+ ret = 0;
+ break;
+
default:
internal_error("missing ast analysis for %s", ast_str(node->k));
return -1;
@@ -585,11 +946,70 @@ static int analyze_list(struct state *state, struct scope *scope,
static int analyze_type(struct state *state, struct scope *scope,
struct type *type)
{
- /* for now, let's just say all types are fine as they are, specified by
- * the user. */
- (void)state;
- (void)scope;
- (void)type;
+ if (type->t0 && analyze_type_list(state, scope, type->t0))
+ return -1;
+
+ /* temporary */
+ if (!type->id)
+ return 0;
+
+ char *id = type->id;
+
+ /* handle primitives */
+ if (strcmp(id, "i8") == 0) {
+ type->k = TYPE_I8;
+ return 0;
+ }
+
+ if (strcmp(id, "u8") == 0) {
+ type->k = TYPE_U8;
+ return 0;
+ }
+
+ if (strcmp(id, "i16") == 0) {
+ type->k = TYPE_I16;
+ return 0;
+ }
+
+ if (strcmp(id, "u16") == 0) {
+ type->k = TYPE_U16;
+ return 0;
+ }
+
+ if (strcmp(id, "i32") == 0) {
+ type->k = TYPE_I32;
+ return 0;
+ }
+
+ if (strcmp(id, "u32") == 0) {
+ type->k = TYPE_U32;
+ return 0;
+ }
+
+ if (strcmp(id, "i64") == 0) {
+ type->k = TYPE_I64;
+ return 0;
+ }
+
+ if (strcmp(id, "u64") == 0) {
+ type->k = TYPE_U64;
+ return 0;
+ }
+
+ if (strcmp(id, "bool") == 0) {
+ type->k = TYPE_BOOL;
+ return 0;
+ }
+
+ /* check for user-defined types */
+ struct ast *def = file_scope_find_type(scope, id);
+ if (!def) {
+ type_error(scope, type, "no such type: %s", id);
+ return -1;
+ }
+
+ /* don't change type->k since we might want to check up on this again
+ * later */
return 0;
}
@@ -604,6 +1024,168 @@ static int analyze_type_list(struct state *state, struct scope *scope,
return 0;
}
+static int copy_scope(bool reexport, struct scope *to, struct scope *from)
+{
+ foreach(visible, symbol, &from->symbols) {
+ struct ast *def = symbol->data;
+ if (!reexport && def->scope != from)
+ continue;
+
+ if (!ast_flags(def, AST_FLAG_PUBLIC))
+ continue;
+
+ if (scope_add_symbol(to, def))
+ return -1;
+ }
+
+ foreach(visible, type, &from->types) {
+ struct ast *def = type->data;
+ if (!reexport && def->scope != from)
+ continue;
+
+ if (!ast_flags(def, AST_FLAG_PUBLIC))
+ continue;
+
+ if (scope_add_type(to, def))
+ return -1;
+ }
+
+ foreach(visible, trait, &from->traits) {
+ struct ast *def = trait->data;
+ if (!reexport && def->scope != from)
+ continue;
+
+ if (!ast_flags(def, AST_FLAG_PUBLIC))
+ continue;
+
+ if (scope_add_trait(to, def))
+ return -1;
+ }
+
+ foreach(visible, template, &from->templates) {
+ struct ast *def = template->data;
+ if (!reexport && def->scope != from)
+ continue;
+
+ if (!ast_flags(def, AST_FLAG_PUBLIC))
+ continue;
+
+ if (scope_add_template(to, def))
+ return -1;
+ }
+
+ return 0;
+}
+
+/* allowed to be noisy */
+static int try_import_file(struct scope *scope, struct ast *node)
+{
+ const char *file = import_file(node);
+ struct scope *child = compile_file(file);
+ if (!child) {
+ semantic_info(scope, node, "imported here");
+ return -1;
+ }
+
+ if (copy_scope(ast_flags(node, AST_FLAG_PUBLIC), scope, child)) {
+ semantic_info(scope, node, "imported here");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* should be quiet upon failure */
+static int try_import_mod(struct scope *scope, struct ast *node)
+{
+ /** @todo cleanup */
+ void *mod = dlopen(import_file(node), RTLD_LAZY);
+ if (!mod)
+ return -1;
+
+ fwd_open_t fwdopen = dlsym(mod, "fwdopen");
+ if (!fwdopen) {
+ dlclose(mod);
+ return -1;
+ }
+
+ mod_vec_append(&scope->mods, mod);
+ return fwdopen((void *)scope);
+}
+
+static struct type *fwd_type_kind(fwd_type_t type)
+{
+ switch (type) {
+ case FWD_VOID: return tgen_void(NULL_LOC());
+ case FWD_I64: return tgen_type(TYPE_I64, NULL, NULL, NULL_LOC());
+ case FWD_U64: return tgen_type(TYPE_U64, NULL, NULL, NULL_LOC());
+ case FWD_PTR: {
+ /* hack? */
+ return tgen_nil(NULL_LOC());
+ }
+
+ default:
+ break;
+ }
+
+ abort();
+ return NULL;
+}
+
+int fwd_register(struct fwd_state *state, const char *name, fwd_extern_t func, fwd_type_t rtype, ...)
+{
+ struct scope *scope = (void *)state;
+ struct ast *vars = NULL;
+
+ va_list args;
+ va_start(args, rtype);
+
+ size_t idx = 0;
+ while (1) {
+ fwd_type_t type = va_arg(args, enum fwd_type);
+ if (type == FWD_END)
+ break;
+
+ /* eight bytes is likely more than enough, since first three are
+ * var, then we have four bytes for indexes and one trailing
+ * NULL */
+ char *name = calloc(8, sizeof(char));
+ assert(name);
+
+ sprintf(name, "%s%zu", "var", idx);
+ struct ast *new = gen_var(name, fwd_type_kind(type), NULL_LOC());
+
+ if (vars)
+ new->n = vars;
+
+ vars = new;
+ idx++;
+ }
+ va_end(args);
+
+ /* append 'return' as a continuation */
+ if (rtype != FWD_VOID) {
+ char *name = strdup("ret");
+ struct ast *new = gen_var(name,
+ tgen_closure(fwd_type_kind(rtype), NULL_LOC()),
+ NULL_LOC());
+ if (vars)
+ new->n = vars;
+
+ vars = new;
+ }
+
+ vars = reverse_ast_list(vars);
+
+ struct ast *def = gen_proc(strdup(name), vars,
+ fwd_type_kind(rtype), NULL, NULL_LOC());
+
+ if (scope_add_symbol(scope, def))
+ return -1;
+
+ return 0;
+}
+
int analyze_root(struct scope *scope, struct ast *root)
{
foreach_node(node, root) {
@@ -619,8 +1201,7 @@ int analyze_root(struct scope *scope, struct ast *root)
break;
case AST_STRUCT_CONT:
- if (scope_add_chain(scope, node))
- return -1;
+ abort();
break;
case AST_TRAIT_DEF:
@@ -628,6 +1209,29 @@ int analyze_root(struct scope *scope, struct ast *root)
return -1;
break;
+ case AST_IMPORT: {
+ if (!try_import_mod(scope, node))
+ break;
+
+ if (!try_import_file(scope, node))
+ break;
+
+ return -1;
+ }
+
+ case AST_TEMPLATE:
+ if (scope_add_template(scope, node))
+ return -1;
+ break;
+
+ case AST_SUPERTEMPLATE:
+ if (scope_add_template(scope, node))
+ return -1;
+ break;
+
+ case AST_INSTANCE:
+ break;
+
default:
abort();
}