aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2024-04-07 01:28:45 +0300
committerKimplul <kimi.h.kuparinen@gmail.com>2024-04-07 01:28:45 +0300
commit170f3ddc3d8967c0b5d4755c0221c396db215b2f (patch)
tree00314209cd7878b9d5b3fe6fc2e0c7297f76dec8 /src
parent2bdf1f8b1856bca8091d66a7a447e6c90993c297 (diff)
downloadek-170f3ddc3d8967c0b5d4755c0221c396db215b2f.tar.gz
ek-170f3ddc3d8967c0b5d4755c0221c396db215b2f.zip
implement some initial qbt backend stuff
Diffstat (limited to 'src')
-rw-r--r--src/actualize.c325
-rw-r--r--src/asm.c102
-rw-r--r--src/ast.c199
-rw-r--r--src/compiler.c20
-rw-r--r--src/lower.c858
-rw-r--r--src/main.c13
-rw-r--r--src/ops.c487
-rw-r--r--src/parser.y10
-rw-r--r--src/scope.c15
-rw-r--r--src/vec.c60
10 files changed, 1271 insertions, 818 deletions
diff --git a/src/actualize.c b/src/actualize.c
index 3460851..744dc1a 100644
--- a/src/actualize.c
+++ b/src/actualize.c
@@ -18,7 +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);
+static int replace_type_id(struct ast_node *nodes, struct ast_node *id,
+ struct ast_node *replacement);
struct act_stack {
struct ast_node *node;
@@ -85,11 +86,14 @@ static struct ast_node *void_type()
return v;
}
-/* 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 *i27 = gen_id(strdup("i27"), NULL_LOC());
struct ast_node *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);
@@ -97,10 +101,29 @@ static struct ast_node *i27_type(struct scope *scope)
return a;
}
+static struct ast_node *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);
+ 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;
+}
+
static struct ast_node *str_type(struct scope *scope)
{
- struct ast_node *str = gen_id("str", NULL_LOC());
+ struct ast_node *str = gen_id(strdup("str"), NULL_LOC());
struct ast_node *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);
@@ -110,8 +133,12 @@ static struct ast_node *str_type(struct scope *scope)
static struct ast_node *bool_type(struct scope *scope)
{
- struct ast_node *b = gen_id("bool", NULL_LOC());
+ struct ast_node *b = gen_id(strdup("bool"), NULL_LOC());
struct ast_node *def = file_scope_find_type(scope, b);
+ 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);
@@ -155,7 +182,8 @@ static struct ast_node *find_label(struct act_state *state,
if (prev)
do {
cur = prev->next;
- if (same_id(AST_LABEL(prev->node).id, AST_LABEL(label).id))
+ if (same_id(AST_LABEL(prev->node).id,
+ AST_LABEL(label).id))
return prev->node;
} while ((prev = cur));
@@ -314,7 +342,8 @@ static int analyze_visibility(struct scope *scope, struct ast_node *node)
case AST_IMPORT: {
const char *file = AST_IMPORT(node).file;
ret |= process_file(&scope,
- ast_flags(node, AST_FLAG_PUBLIC), file);
+ (int)ast_flags(node, AST_FLAG_PUBLIC),
+ file);
break;
}
@@ -379,8 +408,17 @@ static int analyze_var(struct scope *scope, struct ast_node *node)
return actualize(&state, scope, node);
}
+static void set_type(struct ast_node *node, struct ast_node *type)
+{
+ assert(type->node_type == AST_TYPE);
+ node->type = clone_ast_node(type);
+}
+
static int analyze_proc(struct scope *scope, struct ast_node *node)
{
+ /* not sure if this is the best place for this */
+ AST_PROC(node).id->scope = scope;
+
struct scope *proc_scope = create_scope();
scope_add_scope(scope, proc_scope);
node->scope = proc_scope;
@@ -389,14 +427,16 @@ static int analyze_proc(struct scope *scope, struct ast_node *node)
struct act_state state = {0};
int ret = actualize(&state, proc_scope, sign);
- node->type = sign;
+ set_type(node, sign);
return ret;
}
-static struct ast_node *analyze_type_expand(struct scope *scope, struct ast_node *n)
+static struct ast_node *analyze_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);
+ 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;
@@ -407,7 +447,8 @@ static struct ast_node *analyze_type_expand(struct scope *scope, struct ast_node
return NULL;
}
- semantic_info(scope->fctx, n, "FIXME: skipping type param check for now");
+ 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);
@@ -454,20 +495,22 @@ static int analyze_struct(struct scope *scope, struct ast_node *node)
if (generics)
ast_set_flags(node, AST_FLAG_GENERIC);
- struct ast_node *type = gen_type(AST_TYPE_STRUCT, node, NULL, node->loc);
+ struct ast_node *type = gen_type(AST_TYPE_STRUCT, node, NULL,
+ node->loc);
foreach_node(n, AST_STRUCT(node).body) {
if (n->node_type != AST_TYPE_EXPAND)
continue;
- if (implements_trait(AST_STRUCT(node).body, AST_TYPE_EXPAND(n).id)) {
+ if (implements_trait(AST_STRUCT(node).body,
+ AST_TYPE_EXPAND(n).id)) {
n->node_type = AST_EMPTY;
continue;
}
if (same_id(AST_STRUCT(node).id, AST_TYPE_EXPAND(n).id)) {
semantic_error(scope->fctx, n,
- "recursive trait implementations not allowed");
+ "recursive trait implementations not allowed");
return -1;
}
@@ -488,7 +531,7 @@ static int analyze_struct(struct scope *scope, struct ast_node *node)
switch (n->node_type) {
case AST_EMPTY: continue;
case AST_ID: continue;
- /* prototypes are checked later */
+ /* prototypes are checked later */
case AST_PROC: if (!AST_PROC(n).body) continue;
default:
}
@@ -514,9 +557,11 @@ static int analyze_struct(struct scope *scope, struct ast_node *node)
if (AST_PROC(n).body)
continue;
- struct ast_node *proc = scope_find_proc(struct_scope, AST_PROC(n).id);
+ struct ast_node *proc = scope_find_proc(struct_scope,
+ AST_PROC(n).id);
if (!proc) {
- semantic_error(scope->fctx, n, "missing implementation");
+ semantic_error(scope->fctx, n,
+ "missing implementation");
return -1;
}
@@ -556,7 +601,8 @@ static int analyze_trait(struct scope *scope, struct ast_node *node)
continue;
/* don't re-expand already implemented traits */
- if (implements_trait(AST_TRAIT(node).body, AST_TYPE_EXPAND(n).id)) {
+ if (implements_trait(AST_TRAIT(node).body,
+ AST_TYPE_EXPAND(n).id)) {
/* not sure about this, but at least we don't have stray
* type expands everywhere */
n->node_type = AST_EMPTY;
@@ -565,7 +611,7 @@ static int analyze_trait(struct scope *scope, struct ast_node *node)
if (same_id(AST_TRAIT(node).id, AST_TYPE_EXPAND(n).id)) {
semantic_error(scope->fctx, n,
- "recursive trait implementations not allowed");
+ "recursive trait implementations not allowed");
return -1;
}
@@ -589,7 +635,7 @@ static int analyze_trait(struct scope *scope, struct ast_node *node)
switch (n->node_type) {
case AST_EMPTY: continue;
case AST_ID: continue;
- /* prototypes are added later */
+ /* prototypes are added later */
case AST_PROC: if (!AST_PROC(n).body) continue;
default:
}
@@ -619,9 +665,9 @@ static int analyze_trait(struct scope *scope, struct ast_node *node)
}
foreach_node(n, AST_TRAIT(node).body) {
- if (n->node_type != AST_PROC);
+ if (n->node_type != AST_PROC)
+ continue;
- struct act_state state = {0};
if (analyze_proc(trait_scope, n))
return -1;
}
@@ -645,7 +691,6 @@ static int analyze_signs(struct scope *scope, struct ast_node *node)
static int analyze(struct scope *scope, struct ast_node *tree)
{
- struct ast_node *node = tree, *next;
foreach_node(node, tree) {
if (analyze_visibility(scope, node))
return -1;
@@ -661,7 +706,7 @@ static int analyze(struct scope *scope, struct ast_node *tree)
if (actualize(&state, scope, node))
return -1;
- printf("actualized:\n");
+ printf("//actualized:\n");
dump_ast_node(0, node);
}
@@ -784,31 +829,6 @@ static void actualize_trait_types(struct ast_node *params,
assert(!args && !params);
}
-static int actualize_proc_call(struct act_state *state,
- struct scope *scope, struct ast_node *call,
- struct ast_node *proc)
-{
- /* clone procedure definition to
- * replace trait types with actual types and actualize it */
- struct ast_node *def = clone_ast_node(proc);
- if (!def) {
- /* internal error */
- internal_error("failed allocating actualization");
- return -1;
- }
-
- struct ast_node *sign = AST_PROC(def).sign;
- struct ast_node *params = AST_SIGN_TYPE(sign).params;
- struct ast_node *args = AST_CALL(call).args;
- actualize_trait_types(params, args);
-
- if (actualize(state, def->scope, def))
- return -1;
-
- call->type = AST_SIGN_TYPE(sign).ret;
- return 0;
-}
-
static int actualize_macro_expand(struct act_state *state,
struct scope *scope,
struct ast_node *macro_expand)
@@ -885,13 +905,14 @@ static int actualize_call(struct act_state *state,
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);
+ semantic_info(scope->fctx, call, "not a callable type: %s",
+ tstr);
free(tstr);
return -1;
}
struct ast_node *sign = expr->type;
- call->type = AST_SIGN_TYPE(sign).ret;
+ set_type(call, AST_SIGN_TYPE(sign).ret);
return 0;
}
@@ -943,7 +964,7 @@ static int actualize_proc(struct act_state *state,
if (actualize(&new_state, proc->scope, sign))
return -1;
- proc->type = sign;
+ set_type(proc, sign);
/* actualize body */
new_state.cur_proc = proc;
@@ -974,6 +995,11 @@ static int actualize_proc(struct act_state *state,
if (undefined_gotos(&new_state, scope))
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);
+
/* we have successfully actualized the procedure */
return 0;
}
@@ -1020,8 +1046,7 @@ static int actualize_binop(struct act_state *state,
* should be allowed to operate on eachother */
/* types are the same, so the type of this expression is whichever */
- binop->type = left->type;
-
+ set_type(binop, left->type);
return 0;
}
@@ -1041,11 +1066,19 @@ static int actualize_block(struct act_state *state,
}
struct act_stack *defers = state->defer_stack;
- if (actualize(state, block_scope, node->_block.body))
- return -1;
+ foreach_node(pt, node->_block.body) {
+ if (actualize(state, block_scope, pt))
+ return -1;
+ }
+
+ if (node->_block.body == NULL) {
+ node->type = void_type();
+ node->_block.body = gen_empty();
+ return 0;
+ }
/* the block type is the last statement in the block's type */
- node->type = ast_last_node(node->_block.body)->type;
+ set_type(node, ast_last_node(node->_block.body)->type);
if (!node->type) {
semantic_error(scope->fctx, node,
"unable to detect block type");
@@ -1072,6 +1105,8 @@ static int actualize_id(struct act_state *state,
{
UNUSED(state);
assert(id && id->node_type == AST_ID);
+ id->scope = scope;
+
/** @todo vars and procs kind of override eachother, i.e.
* do_something(){..}
* ^() do_something;
@@ -1086,13 +1121,15 @@ static int actualize_id(struct act_state *state,
* */
struct ast_node *decl = file_scope_find_var(scope, id);
if (decl) {
- id->type = decl->type;
+ set_type(id, decl->type);
+ decl->uses++;
return 0;
}
decl = file_scope_find_proc(scope, id);
if (decl) {
- id->type = decl->type;
+ set_type(id, decl->type);
+ decl->uses++;
return 0;
}
@@ -1117,7 +1154,7 @@ static int actualize_var(struct act_state *state,
if (init && init->node_type == AST_INIT) {
assert(!init->type);
- init->type = type;
+ set_type(init, type);
/* TODO: some kind of check_init() */
}
@@ -1135,14 +1172,18 @@ static int actualize_var(struct act_state *state,
}
}
+ /* this is important for lowering */
+ AST_VAR(var).id->scope = scope;
+ var->scope = scope;
+
if (init)
/* infer */
- var->type = init->type;
+ set_type(var, init->type);
if (type)
/* TODO: should there be some default value? */
/* declare */
- var->type = type;
+ 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
@@ -1196,9 +1237,9 @@ static int struct_is_primitive(struct ast_node *s)
return 1;
/* special case of a special case?
- if (strcmp(name, "str"))
- return 1;
- */
+ if (strcmp(name, "str"))
+ return 1;
+ */
return 0;
}
@@ -1228,7 +1269,8 @@ static int actualize_type(struct act_state *state,
}
struct ast_node *exists = file_scope_find_type(scope,
- AST_ID_TYPE(type).id);
+ AST_ID_TYPE(
+ type).id);
if (!exists) {
semantic_error(scope->fctx, type, "no such type");
EXIT_ACT(-1);
@@ -1241,9 +1283,9 @@ static int actualize_type(struct act_state *state,
}
/*
- if (actualize(state, exists->scope, exists))
- EXIT_ACT(-1);
- */
+ if (actualize(state, exists->scope, exists))
+ EXIT_ACT(-1);
+ */
assert(AST_TYPE(type).next == NULL);
if (exists->node_type == AST_ALIAS) {
@@ -1253,28 +1295,30 @@ 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 */
*type = *gen_type(AST_TYPE_TRAIT, exists,
- NULL, exists->loc);
+ 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);
+ id_to_primitive(AST_STRUCT(exists).id),
+ exists,
+ exists->loc);
} else {
*type = *gen_type(AST_TYPE_STRUCT, exists,
- NULL, exists->loc);
+ NULL, exists->loc);
}
}
else if (exists->node_type == AST_ENUM) {
*type = *gen_type(AST_TYPE_ENUM, exists,
- NULL, exists->loc);
+ NULL, exists->loc);
}
break;
}
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;
@@ -1311,19 +1355,22 @@ static int actualize_type(struct act_state *state,
case AST_TYPE_TRAIT: {
assert(ast_flags(type, AST_FLAG_ACTUAL));
- semantic_info(scope->fctx, type, "FIXME skipping trait type checks");
+ 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");
+ 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");
+ semantic_info(scope->fctx, type,
+ "FIXME skipping primitive type checks");
break;
}
@@ -1344,7 +1391,8 @@ static int actualize_empty(struct act_state *state,
* 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");
+ internal_error(
+ "couldn't allocate type id for empty statement\n");
return -1;
}
@@ -1601,7 +1649,7 @@ static int actualize_cast(struct act_state *state,
return -1;
if (proc_choice(expr, type)) {
- cast->type = type;
+ set_type(cast, type);
return match_proc(state, scope, cast);
}
@@ -1609,28 +1657,28 @@ static int actualize_cast(struct act_state *state,
return -1;
if (expr->node_type == AST_INIT) {
- cast->type = type;
+ set_type(cast, type);
return actualize_init_cast(state, scope, expr, type);
}
if (types_match(expr->type, type)) {
- cast->type = type;
+ set_type(cast, type);
return 0;
}
if (integral_type(expr->type) && integral_type(type)) {
- cast->type = type;
+ set_type(cast, type);
return 0;
}
if (pointer_type(expr->type) && pointer_type(type)) {
- cast->type = type;
+ set_type(cast, type);
return 0;
}
if (pointer_conversion(expr->type, type)
|| pointer_conversion(type, expr->type)) {
- cast->type = type;
+ set_type(cast, type);
return 0;
}
@@ -1650,19 +1698,17 @@ static int actualize_const(struct act_state *state, struct scope *scope,
{
UNUSED(state);
assert(cons->node_type == AST_CONST);
- if (AST_CONST(cons).kind == AST_CONST_INTEGER) {
- /* error checking would be doog */
+ if (AST_CONST(cons).kind == AST_CONST_INTEGER)
cons->type = i27_type(scope);
- return 0;
- }
- if (AST_CONST(cons).kind == AST_CONST_STRING) {
+ else if (AST_CONST(cons).kind == AST_CONST_STRING)
cons->type = str_type(scope);
+
+ if (cons->type)
return 0;
- }
semantic_error(scope->fctx, cons, "unimplemented constant");
- return 0;
+ return 1;
}
static int actualize_alias(struct act_state *state, struct scope *scope,
@@ -1709,7 +1755,7 @@ static int actualize_return(struct act_state *state, struct scope *scope,
if (actualize(state, scope, expr))
return -1;
- node->type = expr->type;
+ set_type(node, expr->type);
}
else {
node->type = void_type();
@@ -1726,7 +1772,7 @@ static int actualize_return(struct act_state *state, struct scope *scope,
"return type mismatch: %s", et);
semantic_info(scope->fctx, ret,
- "vs %s", rt);
+ "vs %s", rt);
free(rt);
free(et);
return -1;
@@ -1854,7 +1900,7 @@ static int actualize_unop(struct act_state *state,
return -1;
/* generally speaking */
- node->type = expr->type;
+ set_type(node, expr->type);
switch (node->_unop.op) {
case AST_DEREF: {
@@ -1866,7 +1912,7 @@ static int actualize_unop(struct act_state *state,
return -1;
}
- node->type = AST_PTR_TYPE(type).base;
+ set_type(node, AST_PTR_TYPE(type).base);
assert(node->type);
break;
}
@@ -1874,18 +1920,20 @@ static int actualize_unop(struct act_state *state,
case AST_REF: {
node->type = gen_type(AST_TYPE_POINTER, NULL, NULL,
NULL_LOC());
- node->AST_TYPE(type).next = expr->type;
+ set_type(node->AST_TYPE(type).next, expr->type);
break;
}
case AST_LNOT: {
if (AST_TYPE(expr->type).kind != AST_TYPE_PRIMITIVE) {
- semantic_error(scope->fctx, node, "'!' only implemented for primitive types");
+ 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");
+ semantic_error(scope->fctx, node,
+ "'!' not implemented for void");
return -1;
}
@@ -1909,7 +1957,7 @@ static int actualize_as(struct act_state *state,
if (actualize(state, scope, type))
return -1;
- as->type = type;
+ set_type(as, type);
return 0;
}
@@ -1952,7 +2000,8 @@ next:
return ast_call_on(_replace_type_id, node, data);
}
-static int replace_type_id(struct ast_node *nodes, struct ast_node *id, struct ast_node *replacement)
+static int replace_type_id(struct ast_node *nodes, struct ast_node *id,
+ struct ast_node *replacement)
{
assert(replacement->node_type == AST_TYPE);
struct ast_node *pair[2] = {id, replacement};
@@ -2050,29 +2099,29 @@ static int actualize_dot(struct act_state *state,
default: {
char *tstr = type_str(type);
semantic_error(scope->fctx, node,
- "illegal type in dot expression: %s",
- tstr);
+ "illegal type in dot expression: %s",
+ tstr);
free(tstr);
- return -1;
- }
+ return -1;
+ }
}
struct ast_node *exists = scope_find_var(def->scope, id);
if (exists) {
assert(exists->type);
- node->type = exists->type;
+ set_type(node, exists->type);
return 0;
}
exists = scope_find_proc(def->scope, id);
if (exists) {
assert(exists->type);
- node->type = exists->type;
+ set_type(node, exists->type);
return 0;
}
semantic_error(scope->fctx, node,
- "does not have member");
+ "does not have member");
return -1;
}
@@ -2102,7 +2151,7 @@ static int actualize_assign(struct act_state *state, struct scope *scope,
return -1;
if (from->node_type == AST_INIT) {
- node->type = to->type;
+ set_type(node, to->type);
return actualize_init_cast(state, scope, from, to->type);
}
@@ -2123,7 +2172,7 @@ static int actualize_assign(struct act_state *state, struct scope *scope,
return -1;
}
- node->type = to->type;
+ set_type(node, to->type);
return 0;
}
@@ -2153,7 +2202,7 @@ static int actualize_fetch(struct act_state *state, struct scope *scope,
return -1;
}
- fetch->type = def->type;
+ set_type(fetch, def->type);
return 0;
}
@@ -2176,7 +2225,7 @@ static int actualize_enum(struct act_state *state, struct scope *scope,
node->type = type;
struct ast_node *members = node->_enum.body;
while (members) {
- members->type = type;
+ set_type(members, type);
if (members->_val.val) {
struct ast_node *val = members->_val.val;
if (actualize(state, enum_scope, val))
@@ -2208,6 +2257,56 @@ static int actualize_enum(struct act_state *state, struct scope *scope,
return 0;
}
+static int actualize_if(struct act_state *state, struct scope *scope,
+ struct ast_node *node)
+{
+ assert(node->node_type == AST_IF);
+ if (actualize(state, scope, AST_IF(node).cond))
+ return -1;
+
+ if (actualize(state, scope, AST_IF(node).body))
+ return -1;
+
+ if (actualize(state, scope, AST_IF(node).els))
+ 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;
+ if (!types_match(tt, ft)) {
+ semantic_error(scope->fctx, node,
+ "mismatched if/else body values");
+ return -1;
+ }
+
+ set_type(node, tt);
+ return 0;
+ }
+
+ node->type = void_type();
+ return 0;
+}
+
+static int actualize_for(struct act_state *state, struct scope *scope,
+ struct ast_node *node)
+{
+ assert(node->node_type == AST_FOR);
+ if (actualize(state, scope, AST_FOR(node).pre))
+ return -1;
+
+ if (actualize(state, scope, AST_FOR(node).post))
+ return -1;
+
+ if (actualize(state, scope, AST_FOR(node).cond))
+ return -1;
+
+ if (actualize(state, scope, AST_FOR(node).body))
+ return -1;
+
+ node->type = void_type();
+ return 0;
+}
+
static int actualize(struct act_state *state, struct scope *scope,
struct ast_node *node)
{
@@ -2261,6 +2360,8 @@ static int actualize(struct act_state *state, struct scope *scope,
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;
default:
/* more like internal_error, maybe? */
diff --git a/src/asm.c b/src/asm.c
deleted file mode 100644
index 8e49aa9..0000000
--- a/src/asm.c
+++ /dev/null
@@ -1,102 +0,0 @@
-#include <ek/ops.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <assert.h>
-
-/* I guess using the zero register might be okay in some scenarios, but for now
- * I'll just keep it an illegal register */
-#define ASSERT_REG(x) {assert(x->kind == LOC_REG); assert( \
- x->reg > 0 && x->reg < 81);}
-#define ASSERT_MEM(x) {assert(x->kind == LOC_MEM); assert( \
- x->reg > 0 && x->reg < 81);}
-
-static int print_comment(struct op *op, FILE *f)
-{
- fprintf(f, "/* %s */\n", op->string);
- return 0;
-}
-
-static int print_label(struct op *op, FILE *f)
-{
- fprintf(f, "%s:\n", op->string);
- return 0;
-}
-
-static int print_li(struct op *op, FILE *f)
-{
- struct loc *o = &op->outputs;
- assert(o->next == NULL);
- /* unsure if this will always hold, but for now */
- ASSERT_REG(o);
-
- fprintf(f, "li x%zd, %lld\n", o->reg, op->constant);
- return 0;
-}
-
-static int print_mv(struct op *op, FILE *f)
-{
- struct loc *i = &op->inputs;
- struct loc *o = &op->outputs;
- assert(i->next == NULL);
- assert(o->next == NULL);
- ASSERT_REG(i);
- ASSERT_REG(o);
-
- fprintf(f, "mv x%zd, x%zd\n", o->reg, i->reg);
- return 0;
-}
-
-static int print_stt(struct op *op, FILE *f)
-{
- struct loc *i = &op->inputs;
- struct loc *o = &op->outputs;
- assert(i->next == NULL);
- assert(o->next == NULL);
- ASSERT_REG(i);
- ASSERT_MEM(o);
-
- fprintf(f, "st t, x%zd, %lld(x%zd)\n", i->reg, o->off, o->reg);
- return 0;
-}
-
-static int print_ret(struct op *op, FILE *f)
-{
- (void)op;
- /* technically speaking ret takes a number of inputs, but they should be
- * marshaled into registers with moves etc. so don't worry about them
- * here */
- fprintf(f, "jalr x0, 0(x21)\n");
- /* eventually add in proper ret alias to assembly language once I go
- * through calling conventions etc. */
- return 0;
-}
-
-static int print_op(struct op *op, FILE *f)
-{
- int ret = 0;
- switch (op->opcode) {
- case OP_COMMENT: ret = print_comment(op, f); break;
- case OP_LABEL: ret = print_label(op, f); break;
- case OP_LI: ret = print_li(op, f); break;
- case OP_MV: ret = print_mv(op, f); break;
- case OP_STT: ret = print_stt(op, f); break;
- case OP_RET: ret = print_ret(op, f); break;
- default: abort();
- }
-
- return ret;
-}
-
-int print_asm(struct ops *ops, FILE *f)
-{
- int ret = 0;
- struct op *op = ops->base;
- while (op) {
- if ((ret = print_op(op, f)))
- break;
-
- op = op->next;
- }
-
- return ret;
-}
diff --git a/src/ast.c b/src/ast.c
index a79ffcf..e77714c 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -136,7 +136,8 @@ struct ast_node *gen_binop(enum ast_binops op,
return n;
}
-struct ast_node *gen_unop(enum ast_unops op, struct ast_node *expr, struct src_loc loc)
+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;
@@ -166,7 +167,8 @@ struct ast_node *gen_id(const char *id, struct src_loc loc)
return n;
}
-struct ast_node *gen_assign(struct ast_node *to, struct ast_node *from, struct src_loc loc)
+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;
@@ -205,7 +207,8 @@ struct ast_node *gen_string(const char *str, struct src_loc loc)
return n;
}
-struct ast_node *gen_while(struct ast_node *cond, struct ast_node *body, struct src_loc loc)
+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;
@@ -216,7 +219,8 @@ struct ast_node *gen_while(struct ast_node *cond, struct ast_node *body, struct
}
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)
+ struct ast_node *post, struct ast_node *body,
+ struct src_loc loc)
{
ALLOC_NODE(n, "for");
n->node_type = AST_FOR;
@@ -246,7 +250,8 @@ struct ast_node *gen_goto(struct ast_node *label, struct src_loc loc)
return n;
}
-struct ast_node *gen_dot(struct ast_node *expr, struct ast_node *id, struct src_loc loc)
+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;
@@ -274,7 +279,8 @@ struct ast_node *gen_ctrl(enum ast_ctrl_kind kind, struct src_loc loc)
return n;
}
-struct ast_node *gen_fetch(struct ast_node *id, struct ast_node *type, struct src_loc loc)
+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;
@@ -287,7 +293,7 @@ struct ast_node *gen_fetch(struct ast_node *id, struct ast_node *type, struct sr
struct ast_node *gen_macro_construct(struct ast_node *id,
struct ast_node *params,
struct ast_node *body,
- struct src_loc loc)
+ struct src_loc loc)
{
ALLOC_NODE(n, "macro_construct");
n->node_type = AST_MACRO_CONSTRUCT;
@@ -310,7 +316,8 @@ struct ast_node *gen_if(struct ast_node *cond, struct ast_node *body,
return n;
}
-struct ast_node *gen_switch(struct ast_node *cond, struct ast_node *cases, struct src_loc loc)
+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;
@@ -320,7 +327,8 @@ struct ast_node *gen_switch(struct ast_node *cond, struct ast_node *cases, struc
return n;
}
-struct ast_node *gen_case(struct ast_node *cond, struct ast_node *body, struct src_loc loc)
+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
@@ -334,7 +342,8 @@ 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 ast_node *def, 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;
@@ -465,7 +474,8 @@ struct ast_node *gen_proc(struct ast_node *id, struct ast_node *sign,
}
struct ast_node *gen_struct(struct ast_node *id,
- struct ast_node *generics, struct ast_node *body, struct src_loc loc)
+ struct ast_node *generics, struct ast_node *body,
+ struct src_loc loc)
{
ALLOC_NODE(n, "struct");
n->node_type = AST_STRUCT;
@@ -488,7 +498,8 @@ struct ast_node *gen_enum(struct ast_node *id, struct ast_node *type,
return n;
}
-struct ast_node *gen_cast(struct ast_node *expr, struct ast_node *type, struct src_loc loc)
+struct ast_node *gen_cast(struct ast_node *expr, struct ast_node *type,
+ struct src_loc loc)
{
ALLOC_NODE(n, "cast");
n->node_type = AST_CAST;
@@ -498,7 +509,8 @@ struct ast_node *gen_cast(struct ast_node *expr, struct ast_node *type, struct s
return n;
}
-struct ast_node *gen_val(struct ast_node *id, struct ast_node *val, struct src_loc loc)
+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;
@@ -508,7 +520,8 @@ struct ast_node *gen_val(struct ast_node *id, struct ast_node *val, struct src_l
return n;
}
-struct ast_node *gen_alias(struct ast_node *id, struct ast_node *type, struct src_loc loc)
+struct ast_node *gen_alias(struct ast_node *id, struct ast_node *type,
+ struct src_loc loc)
{
ALLOC_NODE(n, "alias");
n->node_type = AST_ALIAS;
@@ -519,10 +532,10 @@ struct ast_node *gen_alias(struct ast_node *id, struct ast_node *type, struct sr
}
struct ast_node *gen_trait(struct ast_node *id,
- struct ast_node *params,
+ struct ast_node *params,
struct ast_node *raw_body,
- struct ast_node *body,
- struct src_loc loc)
+ struct ast_node *body,
+ struct src_loc loc)
{
ALLOC_NODE(n, "trait");
n->node_type = AST_TRAIT;
@@ -626,6 +639,7 @@ static void dump(int depth, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
+ printf("//");
for (int i = 0; i < depth; ++i)
putchar('\t');
@@ -1171,33 +1185,37 @@ struct ast_node *clone_ast_node(struct ast_node *node)
case AST_FETCH:
new = gen_fetch(clone_ast_node(AST_FETCH(node).id),
clone_ast_node(AST_FETCH(node).type),
- node->loc);
+ 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);
+ node->loc);
break;
- case AST_INIT: new = gen_init(clone_ast_node(AST_INIT(node).body), node->loc);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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,
@@ -1208,7 +1226,7 @@ struct ast_node *clone_ast_node(struct ast_node *node)
case AST_UNOP: new = gen_unop(AST_UNOP(node).op,
clone_ast_node(AST_UNOP(node).expr),
- node->loc);
+ node->loc);
break;
case AST_CALL: new = gen_call(clone_ast_node(AST_CALL(node).expr),
@@ -1216,7 +1234,8 @@ struct ast_node *clone_ast_node(struct ast_node *node)
node->loc);
break;
- case AST_DEFER: new = gen_defer(clone_ast_node(AST_DEFER(node).expr), node->loc);
+ case AST_DEFER: new = gen_defer(clone_ast_node(AST_DEFER(node).expr),
+ node->loc);
break;
case AST_MACRO_CONSTRUCT: new = gen_macro_construct(
@@ -1234,7 +1253,7 @@ struct ast_node *clone_ast_node(struct ast_node *node)
case AST_CAST: new = gen_cast(clone_ast_node(AST_CAST(node).expr),
clone_ast_node(AST_CAST(node).type),
- node->loc);
+ node->loc);
break;
case AST_PROC: new = gen_proc(clone_ast_node(AST_PROC(node).id),
@@ -1244,36 +1263,36 @@ struct ast_node *clone_ast_node(struct ast_node *node)
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);
+ 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);
+ 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);
+ 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);
+ case AST_RETURN: new = gen_return(clone_ast_node(AST_RETURN(node).expr),
+ node->loc);
break;
case AST_TYPE:
- /* oh, if a node has a ->type it probably isn't cloned
- * correctly... */
switch (node->_type.kind) {
case AST_TYPE_PRIMITIVE:
new = gen_primitive(AST_PRIMITIVE_TYPE(node).type,
- AST_PRIMITIVE_TYPE(node).def, node->loc);
+ AST_PRIMITIVE_TYPE(node).def,
+ node->loc);
break;
case AST_TYPE_TRAIT:
@@ -1285,9 +1304,11 @@ struct ast_node *clone_ast_node(struct ast_node *node)
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);
+ clone_ast_node(AST_CONSTRUCT_TYPE(
+ node).id),
+ clone_ast_node(AST_CONSTRUCT_TYPE(
+ node).args),
+ node->loc);
break;
case AST_TYPE_ID:
@@ -1306,9 +1327,9 @@ struct ast_node *clone_ast_node(struct ast_node *node)
case AST_TYPE_POINTER:
new = gen_type(AST_TYPE_POINTER,
- clone_ast_node(AST_PTR_TYPE(node).base),
- NULL,
- node->loc);
+ clone_ast_node(AST_PTR_TYPE(node).base),
+ NULL,
+ node->loc);
break;
case AST_TYPE_STRUCT:
@@ -1326,7 +1347,8 @@ struct ast_node *clone_ast_node(struct ast_node *node)
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).params),
clone_ast_node(AST_SIGN_TYPE(node).ret),
node->loc);
break;
@@ -1339,7 +1361,8 @@ struct ast_node *clone_ast_node(struct ast_node *node)
case AST_BLOCK:
/* TODO: should defers also be cloned? Probably? */
- new = gen_block(clone_ast_node(AST_BLOCK(node).body), node->loc);
+ new = gen_block(clone_ast_node(AST_BLOCK(node).body),
+ node->loc);
break;
case AST_IMPORT:
@@ -1354,32 +1377,32 @@ struct ast_node *clone_ast_node(struct ast_node *node)
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);
+ 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);
+ 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);
+ 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);
+ 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);
+ node->loc);
break;
case AST_CONST:
@@ -1389,7 +1412,8 @@ struct ast_node *clone_ast_node(struct ast_node *node)
break;
case AST_CONST_STRING:
- new = gen_string(strdup(AST_CONST(node).str), node->loc);
+ new = gen_string(strdup(AST_CONST(node).str),
+ node->loc);
break;
}
break;
@@ -1405,13 +1429,13 @@ struct ast_node *clone_ast_node(struct ast_node *node)
case AST_ALIAS:
new = gen_alias(clone_ast_node(AST_ALIAS(node).id),
clone_ast_node(AST_ALIAS(node).type),
- node->loc);
+ 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).raw_body),
clone_ast_node(AST_TRAIT(node).body),
node->loc);
break;
@@ -1420,7 +1444,7 @@ struct ast_node *clone_ast_node(struct ast_node *node)
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);
+ node->loc);
break;
}
@@ -1428,23 +1452,26 @@ struct ast_node *clone_ast_node(struct ast_node *node)
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);
- /* some special case handled type references for us */
- if (!new->type)
- new->type = node->type;
+ if (node->type != node)
+ node->type = clone_ast_node(node->type);
+ else
+ node->type = node;
return new;
}
-int ast_flags(struct ast_node *node, enum ast_flag flags)
+unsigned ast_flags(struct ast_node *node, enum ast_flag flags)
{
return node->flags & flags;
}
static int call_on_assign(int (*call)(struct ast_node *,
- void *), struct ast_node *node, void *data)
+ void *), struct ast_node *node,
+ void *data)
{
int ret = 0;
ret |= call(AST_ASSIGN(node).to, data);
@@ -1459,7 +1486,8 @@ static int call_on_init(int (*call)(struct ast_node *,
}
static int call_on_sizeof(int (*call)(struct ast_node *,
- void *), struct ast_node *node, void *data)
+ void *), struct ast_node *node,
+ void *data)
{
return call(AST_SIZEOF(node).expr, data);
}
@@ -1525,7 +1553,8 @@ static int call_on_while(int (*call)(struct ast_node *,
}
static int call_on_return(int (*call)(struct ast_node *,
- void *), struct ast_node *node, void *data)
+ void *), struct ast_node *node,
+ void *data)
{
return call(node->_return.expr, data);
}
@@ -1569,7 +1598,8 @@ static int call_on_enum(int (*call)(struct ast_node *,
}
static int call_on_struct(int (*call)(struct ast_node *,
- void *), struct ast_node *node, void *data)
+ void *), struct ast_node *node,
+ void *data)
{
int ret = 0;
ret |= call(node->_struct.id, data);
@@ -1588,7 +1618,8 @@ static int call_on_val(int (*call)(struct ast_node *,
}
static int call_on_switch(int (*call)(struct ast_node *,
- void *), struct ast_node *node, void *data)
+ void *), struct ast_node *node,
+ void *data)
{
int ret = 0;
ret |= call(node->_switch.cond, data);
@@ -1606,19 +1637,22 @@ static int call_on_case(int (*call)(struct ast_node *,
}
static int call_on_type_id(int (*call)(struct ast_node *,
- void *), struct ast_node *node, void *data)
+ void *), struct ast_node *node,
+ void *data)
{
return call(AST_ID_TYPE(node).id, data);
}
static int call_on_type_arr(int (*call)(struct ast_node *,
- void *), struct ast_node *node, void *data)
+ void *), struct ast_node *node,
+ void *data)
{
return call(AST_ARR_TYPE(node).size, data);
}
static int call_on_type_sign(int (*call)(struct ast_node *,
- void *), struct ast_node *node, void *data)
+ void *), struct ast_node *node,
+ void *data)
{
int ret = 0;
ret |= call(AST_SIGN_TYPE(node).params, data);
@@ -1627,7 +1661,7 @@ static int call_on_type_sign(int (*call)(struct ast_node *,
}
static int call_on_type_construct(int (*call)(struct ast_node *, void *),
- struct ast_node *node, void *data)
+ struct ast_node *node, void *data)
{
int ret = 0;
ret |= call(AST_CONSTRUCT_TYPE(node).id, data);
@@ -1636,13 +1670,13 @@ static int call_on_type_construct(int (*call)(struct ast_node *, void *),
}
static int call_on_type_pointer(int (*call)(struct ast_node *, void *),
- struct ast_node *node, void *data)
+ struct ast_node *node, void *data)
{
return call(AST_PTR_TYPE(node).base, data);
}
static int call_on_type(int (*call)(struct ast_node *, void *),
- struct ast_node *node, void *data)
+ struct ast_node *node, void *data)
{
int ret = 0;
switch (AST_TYPE(node).kind) {
@@ -1652,8 +1686,10 @@ static int call_on_type(int (*call)(struct ast_node *, void *),
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_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;
}
@@ -1697,7 +1733,8 @@ static int call_on_call(int (*call)(struct ast_node *,
}
static int call_on_macro_construct(int (*call)(struct ast_node *,
- void *), struct ast_node *node, void *data)
+ void *), struct ast_node *node,
+ void *data)
{
int ret = 0;
ret |= call(AST_MACRO_CONSTRUCT(node).id, data);
@@ -1732,7 +1769,8 @@ static int call_on_fetch(int (*call)(struct ast_node *,
}
static int call_on_macro_expand(int (*call)(struct ast_node *,
- void *), struct ast_node *node, void *data)
+ void *), struct ast_node *node,
+ void *data)
{
int ret = 0;
ret |= call(node->_macro_expand.id, data);
@@ -1741,7 +1779,8 @@ static int call_on_macro_expand(int (*call)(struct ast_node *,
}
static int call_on_type_expand(int (*call)(struct ast_node *,
- void *), struct ast_node *type_expand, void *data)
+ void *),
+ struct ast_node *type_expand, void *data)
{
int ret = 0;
ret |= call(AST_TYPE_EXPAND(type_expand).id, data);
@@ -1855,14 +1894,14 @@ int equiv_nodes(struct ast_node *n1, struct ast_node *n2)
return 0;
switch (n1->node_type) {
- case AST_ID:
- if (strcmp(AST_ID(n1).id, AST_ID(n2).id) != 0)
- return 0;
-
- break;
- default:
- internal_error("unimplemented equivalency");
+ case AST_ID:
+ if (strcmp(AST_ID(n1).id, AST_ID(n2).id) != 0)
return 0;
+
+ break;
+ default:
+ internal_error("unimplemented equivalency");
+ return 0;
}
return 1;
diff --git a/src/compiler.c b/src/compiler.c
index 76b420a..b345e16 100644
--- a/src/compiler.c
+++ b/src/compiler.c
@@ -21,8 +21,8 @@
#include <ek/parser.h>
#include <ek/debug.h>
#include <ek/scope.h>
+#include <ek/lower.h>
#include <ek/path.h>
-#include <ek/ops.h>
#include <ek/res.h>
/**
@@ -140,8 +140,6 @@ int process_file(struct scope **scope, int public, const char *file)
if (!cwd)
goto out;
- debug("(cwd:%s)(dir:%s)(base:%s)", cwd, dir, base);
-
if (*dir != 0 && chdir(dir)) {
error("couldn't change to directory %s: %s", dir, strerror(
errno));
@@ -152,7 +150,8 @@ int process_file(struct scope **scope, int public, const char *file)
goto out;
if (chdir(cwd)) {
- error("couldn't change back to directory %s: %s\n", cwd, strerror(
+ error("couldn't change back to directory %s: %s\n", cwd,
+ strerror(
errno));
goto out;
}
@@ -163,7 +162,7 @@ out:
return res;
}
-int compile(const char *input, const char *output) {
+int compile(const char *input) {
int ret = -1;
struct scope *root = NULL;
if (process_file(&root, 0, input)) {
@@ -173,16 +172,7 @@ int compile(const char *input, const char *output) {
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))) {
+ if ((ret = lower_actuals(root))) {
destroy_scope(root);
destroy_ast_nodes();
error("compilation of %s stopped due to errors", input);
diff --git a/src/lower.c b/src/lower.c
new file mode 100644
index 0000000..c78ac96
--- /dev/null
+++ b/src/lower.c
@@ -0,0 +1,858 @@
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <assert.h>
+
+#include <ek/lower.h>
+#include <ek/scope.h>
+#include <ek/vec.h>
+
+#define UNUSED(x) (void)x
+
+enum retval_kind {
+ REG_I27,
+ REG_I9,
+ CONST_I9,
+ CONST_I27,
+};
+
+struct retval {
+ enum retval_kind kind;
+ char *s;
+};
+
+struct lower_state {
+ struct vec top;
+ struct vec bottom;
+ struct vec out;
+ int64_t uniq;
+};
+
+static struct lower_state create_state()
+{
+ struct lower_state state;
+ state.top = vec_create(sizeof(char *));
+ state.bottom = vec_create(sizeof(char *));
+ state.out = vec_create(sizeof(char *));
+ state.uniq = 0;
+ return state;
+}
+
+static void destroy_state(struct lower_state *state)
+{
+ assert(vec_len(&state->top) == 0);
+ assert(vec_len(&state->bottom) == 0);
+ assert(vec_len(&state->out) == 0);
+
+ vec_destroy(&state->top);
+ vec_destroy(&state->bottom);
+ vec_destroy(&state->out);
+}
+
+static void push_loop(struct lower_state *s, char *top, char *bottom, char *out)
+{
+ vect_append(char *, s->top, &top);
+ vect_append(char *, s->bottom, &bottom);
+ vect_append(char *, s->out, &out);
+}
+
+static void pop_loop(struct lower_state *s)
+{
+ char *top = vect_pop(char *, s->top);
+ char *bottom = vect_pop(char *, s->bottom);
+ char *out = vect_pop(char *, s->out);
+
+ free(top);
+ free(bottom);
+ free(out);
+}
+
+#define label_peek(v) \
+ vect_back(char *, v)
+
+static int64_t retval_width(struct retval r)
+{
+ switch (r.kind) {
+ case REG_I27: return 3;
+ case REG_I9: return 1;
+ case CONST_I9: return 1;
+ case CONST_I27: return 3;
+ default: abort();
+ }
+
+ return 0;
+}
+
+static const char *retval_kind_str(enum retval_kind kind)
+{
+ switch (kind) {
+ case REG_I27: return "i27";
+ case REG_I9: return "i9";
+ case CONST_I9: return "i9";
+ case CONST_I27: return "i27";
+ default: abort();
+ }
+
+ return 0;
+}
+
+static const char *retval_type_str(struct retval r)
+{
+ /* I guess it saves a bit on typing? */
+ return retval_kind_str(r.kind);
+}
+
+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)
+{
+ if (AST_TYPE(n->type).kind != AST_TYPE_PRIMITIVE)
+ return false;
+
+ return AST_PRIMITIVE_TYPE(n->type).type == AST_I9;
+}
+
+#define retval_create() \
+ vec_create(sizeof(struct retval))
+
+#define foreach_retval(ri, retval) \
+ foreach_vec(ri, retval)
+
+#define retval_at(rv, ri) \
+ vect_at(struct retval, rv, ri)
+
+static void retval_destroy(struct vec *retval)
+{
+ foreach_retval(ri, *retval) {
+ struct retval s = retval_at(*retval, ri);
+ free(s.s);
+ }
+
+ vec_destroy(retval);
+}
+
+static struct retval build_retval(enum retval_kind kind, char *s)
+{
+ return (struct retval){.kind = kind, .s = s};
+}
+
+static __attribute__((format (printf, 1, 2)))
+char *build_str(const char *fmt, ...) {
+ va_list args1, args2;
+ va_start(args1, fmt);
+ va_copy(args2, args1);
+
+ /* I don't expect this to fail, although I guess it could */
+ size_t size = (size_t)vsnprintf(NULL, 0, fmt, args1);
+ va_end(args1);
+
+ char *buf = malloc(size + 1);
+ vsnprintf(buf, size + 1, fmt, args2);
+
+ va_end(args2);
+ return buf;
+}
+
+static size_t get_scope_number(struct ast_node *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);
+ if (def)
+ return def->scope->number;
+
+ def = file_scope_find_proc(id->scope, id);
+ if (def)
+ return def->scope->number;
+
+ return 0;
+}
+
+static char *mangle_idx(struct ast_node *id, size_t idx)
+{
+ assert(id->node_type == AST_ID);
+ assert(id->scope);
+ const char *name = AST_ID(id).id;
+ /* 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);
+
+ if (ast_flags(id, AST_FLAG_NOMANGLE))
+ return strdup(name);
+
+ return build_str("%s_s%zif%zi", name, number, idx);
+}
+
+static char *mangle(struct ast_node *id)
+{
+ return mangle_idx(id, 0);
+}
+
+static int lower_expr(struct lower_state *s, struct ast_node *e,
+ struct vec *retval);
+static int lower_statement(struct lower_state *s, struct ast_node *n);
+
+static void output_id(struct ast_node *id)
+{
+ char *name = mangle(id);
+ printf("%s", name);
+ free(name);
+}
+
+static int lower_global_var(struct ast_node *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) {
+ 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) {
+ semantic_error(n->scope->fctx, n,
+ "constant expressions currently not implemented");
+ return -1;
+ }
+
+ output_id(id);
+ 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;
+ }
+
+ printf(";\n");
+ return 0;
+}
+
+static int lower_param(struct lower_state *s, struct ast_node *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) {
+ 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;
+ }
+
+ struct ast_node *id = AST_VAR(p).id;
+ output_id(id);
+ printf(",");
+ return 0;
+}
+
+static int lower_params(struct lower_state *s, struct ast_node *params)
+{
+ for (struct ast_node *p = params; p; p = p->next) {
+ if (lower_param(s, p))
+ return -1;
+ }
+
+ return 0;
+}
+
+static int lower_var(struct lower_state *s, struct ast_node *v,
+ struct vec *retval)
+{
+ assert(v->node_type == AST_VAR);
+ struct vec input = retval_create();
+ if (lower_expr(s, AST_VAR(v).init, &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);
+ /* 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);
+ struct retval n = build_retval(REG_I27, name);
+ vec_append(retval, &n);
+ }
+
+ retval_destroy(&input);
+ return 0;
+}
+
+static void do_const_store(struct lower_state *s, struct vec *from,
+ struct retval t, struct retval o)
+{
+ UNUSED(s);
+ /* I know, kind of silly to swap back and forth between string/int but
+ * good enough for now */
+ int64_t addr = strtoll(t.s, 0, 0) + strtoll(o.s, 0, 0);
+ foreach_retval(ri, *from) {
+ struct retval r = retval_at(*from, ri);
+ /* doesn't really take into account possible padding etc, should
+ * probably fix at some point */
+ printf("%s >> %s (%zi);\n",
+ r.s, retval_type_str(r), addr);
+
+ addr += retval_width(r);
+ }
+}
+
+static void do_store(struct lower_state *s, struct vec *from, struct vec *to,
+ struct vec *off)
+{
+ assert(vec_len(to) == 1);
+ struct retval t = retval_at(*to, 0);
+
+ struct retval o = build_retval(CONST_I27, "0");
+ if (off)
+ o = retval_at(*off, 0);
+
+ if (retval_is_const(t) && retval_is_const(o)) {
+ do_const_store(s, from, t, o);
+ return;
+ }
+
+ if (retval_is_const(t)) {
+ /* o must be register, so swap around for the format to make
+ * sense */
+ struct retval tmp = t; t = o; o = tmp;
+ }
+
+ assert(retval_is_const(o));
+
+ int64_t addr = strtoll(o.s, 0, 0);
+ foreach_retval(ri, *from) {
+ struct retval r = retval_at(*from, ri);
+ /* doesn't really take into account possible padding etc, should
+ * probably fix at some point */
+ printf("%s >> %s %s %zi;\n",
+ r.s, retval_type_str(r), t.s, addr);
+
+ addr += retval_width(r);
+ }
+}
+
+static int lower_cast(struct lower_state *s, struct ast_node *e,
+ struct vec *retval)
+{
+ assert(e->node_type == 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);
+
+ if (lower_expr(s, AST_CAST(e).expr, retval))
+ return -1;
+
+ enum retval_kind kind = REG_I27;
+ if (is_i9(e))
+ kind = REG_I9;
+
+ 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);
+
+ printf("%s %s = %s;\n", retval_kind_str(kind), s, r.s);
+ free(r.s);
+
+ r.s = s;
+ r.kind = kind;
+ retval_at(*retval, ri) = r;
+ }
+
+ return 0;
+}
+
+static int lower_const(struct lower_state *s, struct ast_node *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);
+ vec_append(retval, &r);
+ return 0;
+}
+
+static int lower_assign(struct lower_state *s, struct ast_node *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))
+ 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)) {
+ retval_destroy(&loc);
+ retval_destroy(&off);
+ return -1;
+ }
+ }
+
+ if (lower_expr(s, base, &loc)) {
+ retval_destroy(&loc);
+ retval_destroy(&off);
+ return -1;
+ }
+
+ if (IS_DEREF(to)) {
+ do_store(s, retval, &loc, NULL);
+ }
+ else if (IS_ARR(to)) {
+ do_store(s, retval, &loc, &off);
+ } else {
+ assert(vec_len(retval) == vec_len(&loc));
+ foreach_retval(ri, *retval) {
+ struct retval to = retval_at(loc, ri);
+ struct retval from = retval_at(*retval, ri);
+ printf("i27 %s = %s;\n", to.s, from.s);
+ }
+ }
+
+ retval_destroy(&loc);
+ retval_destroy(&off);
+ return 0;
+#undef IS_DEREF
+#undef IS_ARR
+}
+
+static int lower_id(struct lower_state *s, struct ast_node *id,
+ struct vec *retval)
+{
+ UNUSED(s);
+ assert(id->node_type == 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) {
+ 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;
+
+ /* 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) {
+ char *o = m;
+ m = build_str("&%s", m);
+ free(o);
+ }
+
+ struct retval r = build_retval(kind, m);
+ vec_append(retval, &r);
+ return 0;
+}
+
+static int lower_return(struct lower_state *s, struct ast_node *r,
+ struct vec *retval)
+{
+ assert(r->node_type == AST_RETURN);
+ if (lower_expr(s, AST_RETURN(r).expr, retval))
+ return -1;
+
+ printf("=> ( ");
+
+ foreach_retval(ri, *retval) {
+ struct retval r = retval_at(*retval, ri);
+ printf("%s, ", r.s);
+ }
+
+ printf(" );\n");
+ return 0;
+}
+
+static int lower_if(struct lower_state *s, struct ast_node *i,
+ struct vec *retval)
+{
+ assert(i->node_type == AST_IF);
+ if (lower_expr(s, AST_IF(i).cond, retval))
+ return -1;
+
+ assert(vec_len(retval) == 1);
+ /* helps readability a little bit */
+ long long uniq = s->uniq++;
+ printf("if%lli:\n", uniq);
+
+ char *bottom = build_str("if_else%lli", uniq);
+ char *out = build_str("if_out%lli", uniq);
+
+ 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)) {
+ free(bottom);
+ return -1;
+ }
+
+ printf("-> %s;\n", out);
+ printf("%s:\n", bottom);
+ free(bottom);
+
+ if (AST_IF(i).els && lower_statement(s, AST_IF(i).els)) {
+ free(out);
+ return -1;
+ }
+
+ printf("%s:\n", out);
+ free(out);
+ return 0;
+}
+
+static int lower_for(struct lower_state *s, struct ast_node *f,
+ struct vec *retval)
+{
+ assert(f->node_type == AST_FOR);
+ if (lower_statement(s, AST_FOR(f).pre))
+ return -1;
+
+ long long uniq = s->uniq++;
+
+ char *top = build_str("for_top%lli", uniq);
+ char *bottom = build_str("for_bottom%lli", uniq);
+ char *out = build_str("for_out%lli", uniq);
+
+ push_loop(s, top, bottom, out);
+
+ printf("-> %s;\n", out);
+
+ printf("%s:\n", top);
+ if (lower_statement(s, AST_FOR(f).body)) {
+ pop_loop(s);
+ return -1;
+ }
+
+ printf("%s:\n", bottom);
+ if (lower_statement(s, AST_FOR(f).post)) {
+ pop_loop(s);
+ return -1;
+ }
+
+ printf("%s:\n", out);
+ if (lower_expr(s, AST_FOR(f).cond, retval)) {
+ pop_loop(s);
+ return -1;
+ }
+
+ assert(vec_len(retval) == 1);
+ printf("%s -> %s;\n", (retval_at(*retval, 0)).s, top);
+
+ pop_loop(s);
+ return 0;
+}
+
+static int lower_expr_if(struct lower_state *s, struct ast_node *i,
+ struct vec *retval)
+{
+ semantic_error(i->scope->fctx, i,
+ "expr if unimplemented");
+ return 0;
+}
+
+static int lower_binop(struct lower_state *s, struct ast_node *i,
+ struct vec *retval)
+{
+ struct vec l = retval_create();
+ struct vec r = retval_create();
+
+ if (lower_expr(s, AST_BINOP(i).left, &l)) {
+ retval_destroy(&l);
+ retval_destroy(&r);
+ return -1;
+ }
+
+ if (lower_expr(s, AST_BINOP(i).right, &r)) {
+ retval_destroy(&l);
+ retval_destroy(&r);
+ return -1;
+ }
+
+ assert(vec_len(&l) == 1);
+ assert(vec_len(&r) == 1);
+
+ char *name = build_str("tmp%lli", (long long)s->uniq++);
+ struct retval ret = build_retval(REG_I27, name);
+ vec_append(retval, &ret);
+
+ char *op = "";
+ switch (AST_BINOP(i).op) {
+ case AST_ADD: op = "+"; break;
+ case AST_SUB: op = "-"; break;
+ case AST_MUL: op = "*"; break;
+ case AST_DIV: op = "/"; break;
+ case AST_REM: op = "%"; break;
+ case AST_LSHIFT: op = "<<"; break;
+ case AST_RSHIFT: op = ">>"; break;
+ case AST_LT: op = "<"; break;
+ case AST_GT: op = ">"; break;
+ case AST_LE: op = "<="; break;
+ case AST_GE: op = ">="; break;
+ case AST_NE: op = "!="; break;
+ case AST_EQ: 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_call(struct lower_state *s, struct ast_node *c,
+ struct vec *retval)
+{
+ assert(c->node_type == AST_CALL);
+
+ struct vec call = retval_create();
+ if (lower_expr(s, AST_CALL(c).expr, &call)) {
+ retval_destroy(&call);
+ return -1;
+ }
+
+ /* collect all args */
+ struct vec args = retval_create();
+ foreach_node(a, AST_CALL(c).args) {
+ struct vec arg = retval_create();
+ if (lower_expr(s, a, &arg)) {
+ retval_destroy(&arg);
+ retval_destroy(&args);
+ return -1;
+ }
+
+ foreach_retval(ri, arg) {
+ struct retval r = retval_at(arg, ri);
+ /* very important! */
+ r.s = strdup(r.s);
+ vec_append(&args, &r);
+ }
+
+ retval_destroy(&arg);
+ }
+
+ assert(vec_len(&call) == 1);
+ printf("%s (", (retval_at(call, 0)).s);
+ retval_destroy(&call);
+
+ foreach_retval(ri, args) {
+ struct retval r = retval_at(args, ri);
+ 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)) {
+ semantic_error(c->scope->fctx, c,
+ "only void return type implemented");
+ retval_destroy(&args);
+ return -1;
+ }
+
+ printf(") => ();\n");
+ retval_destroy(&args);
+ return 0;
+}
+
+static int lower_expr(struct lower_state *s, struct ast_node *e,
+ struct vec *retval)
+{
+ if (!e)
+ return 0;
+
+ switch (e->node_type) {
+ 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:
+ semantic_error(e->scope->fctx, e,
+ "unhandled expr in lowering");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int lower_block(struct lower_state *s, struct ast_node *body)
+{
+ 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))
+ return -1;
+ }
+
+ return 0;
+}
+
+static int lower_statement(struct lower_state *s, struct ast_node *n)
+{
+ struct vec retval = retval_create();
+
+ int ret = 0;
+ switch (n->node_type) {
+ 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;
+ case AST_BLOCK: ret = lower_block(s, n); break;
+ default: ret = lower_expr(s, n, &retval); break;
+ }
+
+ retval_destroy(&retval);
+ return ret;
+}
+
+static int lower_proc(struct ast_node *n)
+{
+ assert(n->node_type == AST_PROC);
+ /* nobody uses the proc, so no need to do anything */
+ if (n->uses == 0 && !ast_flags(AST_PROC(n).id, AST_FLAG_NOMANGLE))
+ return 0;
+
+ struct lower_state state = create_state();
+
+ /* name */
+ struct ast_node *id = AST_PROC(n).id;
+ output_id(id);
+
+ /* args */
+ printf("(");
+
+ struct ast_node *sign = AST_PROC(n).sign;
+ if (lower_params(&state, AST_SIGN_TYPE(sign).params)) {
+ destroy_state(&state);
+ return -1;
+ }
+
+ /* no return type currently supported by qbt */
+ printf(")\n");
+
+ /* body */
+ printf("{\n");
+
+ if (lower_block(&state, AST_PROC(n).body)) {
+ destroy_state(&state);
+ return -1;
+ }
+
+ printf("}\n");
+ destroy_state(&state);
+ 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)
+{
+ /* 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))
+ return -1;
+ }
+
+ if (!scope_flags(root, SCOPE_FILE))
+ return 0;
+
+ for (struct visible *v = root->vars; v; v = v->next) {
+ assert(v->node);
+ if (lower_global_var(v->node))
+ return -1;
+ }
+
+ for (struct visible *p = root->procs; p; p = p->next) {
+ assert(p->node);
+ if (lower_proc(p->node))
+ return -1;
+ }
+
+ 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/main.c b/src/main.c
index 142a575..b3e2b2b 100644
--- a/src/main.c
+++ b/src/main.c
@@ -22,13 +22,11 @@
* some way to make flag handling more generic
*/
static const char *cmdline_usage =
- "ek compiler usage:\n"
+ "ek frontend usage:\n"
" ek [-I <dir>...] [-o <outfile>] infile\n"
" -h Show usage (this)\n"
" -I <dir> Add directory to import path\n"
" infile Top file(s) to compile\n"
- " -o <outfile> Name of output assembly file\n"
- " (infile minus file extension if not given)\n"
;
/** Print usage of compiler. */
@@ -50,13 +48,8 @@ static void usage()
int main(int argc, char *argv[])
{
int opt;
- const char *output = "e.t";
- while ((opt = getopt(argc, argv, "hI:o:")) != -1) {
+ while ((opt = getopt(argc, argv, "hI:")) != -1) {
switch (opt) {
- case 'o':
- output = optarg;
- break;
-
case 'I':
add_import_path(optarg);
break;
@@ -84,5 +77,5 @@ int main(int argc, char *argv[])
}
const char *input = argv[optind];
- return compile(input, output);
+ return compile(input);
}
diff --git a/src/ops.c b/src/ops.c
deleted file mode 100644
index cc2650c..0000000
--- a/src/ops.c
+++ /dev/null
@@ -1,487 +0,0 @@
-#include <ek/ops.h>
-#include <ek/scope.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-
-/* hopefully not too difficult to follow what's goind on, but to start with we
- * assume we have an effectively infinite amount of virtual registers and we
- * move everything down into them. Typically the top output is used as input in
- * some other step. */
-
-static void set_reg(struct loc *loc, size_t reg)
-{
- loc->kind = LOC_REG;
- loc->reg = reg;
-}
-
-static void set_mem(struct loc *loc, size_t reg, long long off, size_t width)
-{
- loc->kind = LOC_MEM;
- loc->reg = reg;
- loc->off = off;
- loc->width = width;
-}
-
-static size_t trivial_type_width(struct ast_node *type)
-{
- switch (AST_TYPE(type).kind) {
- case AST_TYPE_POINTER: return 3;
- case AST_TYPE_PRIMITIVE: {
- if (AST_PRIMITIVE_TYPE(type).type == AST_I27)
- return 3;
-
- if (AST_PRIMITIVE_TYPE(type).type == AST_I9)
- return 1;
-
- abort();
- break;
- }
- default: abort();
- }
- return 3;
-}
-
-static struct ops *create_ops()
-{
- struct ops *ops = calloc(1, sizeof(struct ops));
-
- struct op *op = calloc(1, sizeof(struct op));
- op->opcode = OP_COMMENT;
- op->string = strdup("start");
-
- ops->base = op;
- ops->head = op;
- return ops;
-}
-
-static void destroy_ops(struct ops *ops)
-{
- if (!ops)
- return;
-
- struct op *op = ops->base;
- while (op) {
- struct op *prev = op;
- op = op->next;
-
- switch (prev->opcode) {
- case OP_COMMENT: free((void *)prev->string); break;
- case OP_LABEL: free((void *)prev->string); break;
- default:
- }
-
- free(prev);
- }
-
- free(ops);
-}
-
-static size_t next_virtual_reg()
-{
- static size_t reg = 1;
- return reg++;
-}
-
-static int lower_op(struct ast_node *n, struct ops *ops);
-
-#define HEAD_OUTPUTS(ops) ops->head->outputs
-static struct op *op_head(struct ops *ops)
-{
- return ops->head;
-}
-
-static struct op *append_op(struct ops *ops, enum opcode opcode)
-{
- static size_t i = 1;
- struct op *op = op_head(ops);
- struct op *n = calloc(1, sizeof(struct op));
- n->opcode = opcode;
- op->next = n;
- op->loc = i++;
- ops->head = n;
- return n;
-}
-
-/* this should really only be called after lifetime analysis, should I add in
- * some checks against incorrect use...? */
-static struct op *insert_op_after(struct op *op, enum opcode opcode)
-{
- struct op *n = calloc(1, sizeof(struct op));
- n->opcode = opcode;
- n->next = op->next;
- op->next = n;
- return n;
-}
-
-static int lower_proc(struct ast_node *n, struct ops *ops)
-{
- struct op *op = append_op(ops, OP_LABEL);
- /** @todo name mangling */
- struct ast_node *id = AST_PROC(n).id;
- op->string = strdup(AST_ID(id).id);
- int ret = lower_op(AST_PROC(n).body, ops);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static int lower_block(struct ast_node *n, struct ops *ops)
-{
- struct ast_node *b = AST_BLOCK(n).body;
- while (b) {
- int ret = lower_op(b, ops);
- if (ret)
- return ret;
-
- b = b->next;
- }
-
- return 0;
-}
-
-static int lower_var(struct ast_node *n, struct ops *ops)
-{
- struct ast_node *d = scope_find_var(n->scope, AST_VAR(n).id);
- /* structs should be handled as well, would it be better to try and fit
- * them into regs or just dump them on the stack to make sure we don't
- * immediately run out of registers? */
- struct ast_node *type = d->type;
- if (AST_TYPE(type).kind != AST_TYPE_PRIMITIVE
- && AST_TYPE(type).kind != AST_TYPE_POINTER) {
- semantic_error(n->scope->fctx, n,
- "only trivial type lowering implemented");
- return -1;
- }
-
- d->reg = next_virtual_reg();
-
- if (AST_VAR(n).init) {
- int ret = lower_op(AST_VAR(n).init, ops);
- if (ret)
- return ret;
-
- struct loc inputs = HEAD_OUTPUTS(ops);
- /* work with only primitive types for now */
- assert(inputs.next == NULL);
-
- struct op *op = append_op(ops, OP_MV);
- op->inputs = inputs;
- set_reg(&op->outputs, d->reg);
- }
-
- return 0;
-}
-
-static int lower_cast(struct ast_node *n, struct ops *ops)
-{
- /** @todo lob off extra trits or something? */
- return lower_op(AST_CAST(n).expr, ops);
-}
-
-static int lower_const(struct ast_node *n, struct ops *ops)
-{
- if (AST_CONST(n).kind != AST_CONST_INTEGER) {
- semantic_error(n->scope->fctx, n,
- "only integer constant lowering implemented");
- return -1;
- }
-
- struct op *op = append_op(ops, OP_LI);
- op->constant = AST_CONST(n).integer;
- set_reg(&op->outputs, next_virtual_reg());
- return 0;
-}
-
-static int lower_assign(struct ast_node *n, struct ops *ops)
-{
- int ret = lower_op(AST_ASSIGN(n).from, ops);
- if (ret)
- return ret;
-
- struct loc from = HEAD_OUTPUTS(ops);
-
- ret = lower_op(AST_ASSIGN(n).to, ops);
- if (ret)
- return ret;
-
- struct loc to = HEAD_OUTPUTS(ops);
- struct op *op = append_op(ops, OP_MV);
- op->inputs = from;
- op->outputs = to;
- return 0;
-}
-
-static int lower_unop(struct ast_node *n, struct ops *ops)
-{
- if (AST_UNOP(n).op != AST_DEREF) {
- semantic_error(n->scope->fctx, n,
- "unop lowering not implemented");
- return -1;
- }
-
- if (AST_UNOP(n).op == AST_DEREF) {
- int ret = lower_op(AST_UNOP(n).expr, ops);
- if (ret)
- return ret;
-
- struct loc *l = &HEAD_OUTPUTS(ops);
- if (l->kind == LOC_MEM) {
- /* load value from memory and use it as the next step
- * location */
- struct op *op = append_op(ops, OP_LDW);
- op->inputs = *l;
- /* hard coded 3 for now, pointer is three trytes */
- set_mem(&op->outputs, next_virtual_reg(), 0, 3);
- return 0;
- }
-
- /* use the value as if it was a memory location */
- size_t w = trivial_type_width(n->type);
- set_mem(l, l->reg, 0, w);
- return 0;
- }
-
- return -1;
-}
-
-static int lower_id(struct ast_node *n, struct ops *ops)
-{
- struct ast_node *d = scope_find_var(n->scope, n);
- assert(d->reg);
-
- /* feels like a massive hack, but gives fairly readable debug output so
- * I'll keep it for now. Eventually once I figure out what I'm doing
- * this whole system will probably have to be rewritten anyway. */
- struct op *op = append_op(ops, OP_COMMENT);
- op->string = strdup(AST_ID(n).id);
- set_reg(&op->outputs, d->reg);
- return 0;
-}
-
-static int lower_ret(struct ast_node *n, struct ops *ops)
-{
- if (AST_RETURN(n).expr) {
- int ret = lower_op(AST_RETURN(n).expr, ops);
- if (ret)
- return ret;
- }
-
- struct op *op = append_op(ops, OP_RET);
- if (AST_RETURN(n).expr)
- set_reg(&op->inputs, HEAD_OUTPUTS(ops).reg);
-
- return 0;
-}
-
-static int lower_op(struct ast_node *n, struct ops *ops)
-{
- int ret = 0;
- switch (n->node_type) {
- case AST_PROC: ret = lower_proc(n, ops); break;
- case AST_BLOCK: ret = lower_block(n, ops); break;
- case AST_VAR: ret = lower_var(n, ops); break;
- case AST_CAST: ret = lower_cast(n, ops); break;
- case AST_CONST: ret = lower_const(n, ops); break;
- case AST_ASSIGN: ret = lower_assign(n, ops); break;
- case AST_UNOP: ret = lower_unop(n, ops); break;
- case AST_ID: ret = lower_id(n, ops); break;
- case AST_RETURN: ret = lower_ret(n, ops); break;
- default:
- semantic_error(n->scope->fctx, n, "unimplemented lowering");
- return -1;
- }
-
- if (ret)
- return ret;
-
- return 0;
-}
-
-static void print_locs(struct loc *locs)
-{
- if (locs->kind == LOC_NONE)
- return;
-
- printf(" ( ");
-
- while (locs) {
- if (locs->kind == LOC_MEM)
- printf("%lld(", locs->off);
-
- printf("r%zd", locs->reg);
-
- if (locs->kind == LOC_MEM)
- printf(", %zd)", locs->width);
-
- printf(" ");
- locs = locs->next;
- }
-
- printf(")");
-}
-
-static void print_op(struct op *op)
-{
- if (!op)
- return;
-
- print_locs(&op->outputs);
- putchar(' ');
- switch (op->opcode) {
- case OP_LABEL: printf("%s:", op->string); break;
- case OP_COMMENT: printf("/* %s */", op->string); break;
- case OP_LI: printf("li %lld", op->constant); break;
- case OP_LA: printf("la %s", op->string); break;
- case OP_ADD: printf("add"); break;
- case OP_ADDI: printf("addi %lld", op->constant); break;
- case OP_STT: printf("stt"); break;
- case OP_LDT: printf("ldt"); break;
- case OP_STW: printf("stw"); break;
- case OP_LDW: printf("ldw"); break;
- case OP_RET: printf("ret"); break;
- case OP_MV: printf("mv"); break;
- default: printf("unimp"); break;
- }
- putchar(' ');
- print_locs(&op->inputs);
- printf("\n");
-}
-
-static void print_ops(struct ops *ops)
-{
- if (!ops)
- return;
-
- struct op *base = ops->base;
- while (base) {
- print_op(base);
- base = base->next;
- }
-}
-
-static enum opcode st_opc(struct loc *loc)
-{
- switch (loc->width) {
- case 1: return OP_STT;
- case 3: return OP_STW;
- }
-
- abort();
- return OP_STW;
-}
-
-static enum opcode ld_opc(struct loc *loc)
-{
- switch (loc->width) {
- case 1: return OP_LDT;
- case 3: return OP_LDW;
- }
-
- abort();
- return OP_LDW;
-}
-
-static int realize_moves(struct ops *ops)
-{
- /* completely arbitrary and *will* have to be made better in the near
- * future */
- static const size_t tmp_reg = 9;
- struct op *prev = NULL;
- struct op *op = ops->base;
- for (; op; prev = op, op = op->next) {
- if (op->opcode != OP_MV) {
- continue;
- }
-
- struct loc *i = &op->inputs;
- struct loc *o = &op->outputs;
- assert(i->next == NULL);
- assert(i->next == NULL);
-
- if (i->kind == LOC_REG && o->kind == LOC_REG) {
- /* if move is between the same register, skip it */
- if (i->reg == o->reg && prev)
- prev->next = op->next;
-
- }
- else if (i->kind == LOC_REG && o->kind == LOC_MEM) {
- op->opcode = st_opc(o);
- }
- else if (i->kind == LOC_MEM && o->kind == LOC_REG) {
- op->opcode = ld_opc(i);
- }
- else if (i->kind == LOC_MEM && o->kind == LOC_MEM) {
- op->opcode = ld_opc(i);
- /* our input/output are references, so call this before
- * setting registers for our original operation. Also,
- * at this point the lifetime stuff is finished, so no
- * big deal that our node IDs change */
- struct op *n = insert_op_after(op, st_opc(o));
- set_mem(&n->outputs, o->reg, o->off, o->width);
- set_reg(&n->inputs, tmp_reg);
- set_reg(&op->outputs, tmp_reg);
- }
- }
-
- return 0;
-}
-
-static int alloc_regs(struct ops *ops)
-{
- /** @todo analyze lifetime, for now just convert moves to correct ldst
- * etc. */
- /** @todo lifetime analysis could be done by looping over all ops, and
- * when we encounter a virtual register we haven't seen before, add it
- * to a list. When we encounter it used again, extend its lifetime to
- * wherever we are in the function. */
- return realize_moves(ops);
-}
-
-int lower_ops(struct scope *root, const char *fname)
-{
- FILE *f = fopen(fname, "w");
-
- /* main should probably be mangled here as well */
- fprintf(f, "jal x21, main\n");
- /* tell simulator to turn off (very much temp) */
- fprintf(f, "li x1, 3\n");
- fprintf(f, "csrrw mpower, x0, x1\n");
-
- /* this can potentially be parallelized in the future */
- int ret = 0;
- for (struct actual *a = root->actuals; a; a = a->next) {
- assert(a->node);
-
- struct ops *ops = create_ops();
- if ((ret = lower_op(a->node, ops))) {
- destroy_ops(ops);
- break;
- }
-
- printf("Lowered ops before lifetime analysis:\n");
- print_ops(ops);
-
- if ((ret = alloc_regs(ops))) {
- destroy_ops(ops);
- break;
- }
-
- printf("Lowered ops after lifetime analysis:\n");
- print_ops(ops);
-
- if ((ret = print_asm(ops, f))) {
- /* kind of silly as of now but eh */
- destroy_ops(ops);
- break;
- }
-
- destroy_ops(ops);
- }
-
- fclose(f);
- return ret;
-}
-
diff --git a/src/parser.y b/src/parser.y
index 14fbb50..d6aefc8 100644
--- a/src/parser.y
+++ b/src/parser.y
@@ -375,11 +375,11 @@ expr
* "do" looks a bit cleaner. There is the slight annoyance that a very
* long do {} ... might have a 'while'; at the end, not sure if do ...
* while should be removed from the language altogether or what */
- | "do" body { $$ = $2; }
- | "do" expr_if { $$ = $2; }
- | "do" "const" expr_if { $$ = $3; }
- | "do" switch { $$ = $2; }
- | "do" "const" switch { $$ = $3; }
+ | "do" body { $$ = $2; ast_set_flags($$, AST_FLAG_DOEXPR); }
+ | "do" expr_if { $$ = $2; ast_set_flags($$, AST_FLAG_DOEXPR); }
+ | "do" "const" expr_if { $$ = $3; ast_set_flags($$, AST_FLAG_DOEXPR); }
+ | "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(@$)); }
| "sizeof" expr { $$ = gen_sizeof($2, src_loc(@$)); }
diff --git a/src/scope.c b/src/scope.c
index 48d3bc5..205e858 100644
--- a/src/scope.c
+++ b/src/scope.c
@@ -32,7 +32,7 @@ struct scope *create_scope()
return scope;
}
-void destroy_visible(struct scope *scope, struct visible *visible)
+void destroy_visible(struct visible *visible)
{
struct visible *prev = visible, *cur;
if (prev)
@@ -53,10 +53,10 @@ void destroy_scope(struct scope *scope)
free((void *)scope->fctx.fname);
}
- destroy_visible(scope, scope->vars);
- destroy_visible(scope, scope->procs);
- destroy_visible(scope, scope->macros);
- destroy_visible(scope, scope->types);
+ destroy_visible(scope->vars);
+ destroy_visible(scope->procs);
+ destroy_visible(scope->macros);
+ destroy_visible(scope->types);
struct scope *prev = scope->children, *cur;
if (prev)
@@ -179,7 +179,8 @@ int scope_add_type(struct scope *scope, struct ast_node *id,
int scope_add_macro(struct scope *scope, struct ast_node *macro)
{
assert(macro->node_type == AST_MACRO_CONSTRUCT);
- struct ast_node *exists = file_scope_find_macro(scope, AST_MACRO_CONSTRUCT(
+ struct ast_node *exists = file_scope_find_macro(scope,
+ AST_MACRO_CONSTRUCT(
macro).id);
if (exists) {
semantic_error(scope->fctx, macro, "macro redefined");
@@ -362,7 +363,7 @@ static int add_actual(struct actual *actuals, struct ast_node *node)
return 0;
}
- /* TODO: check that there isn't already an actual like ours? */
+ /* TODO: check that there isn't already an actual like ours */
struct actual *actual = calloc(1, sizeof(struct actual));
if (!actual)
return -1;
diff --git a/src/vec.c b/src/vec.c
new file mode 100644
index 0000000..3f1aac6
--- /dev/null
+++ b/src/vec.c
@@ -0,0 +1,60 @@
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#include <ek/vec.h>
+
+struct vec vec_create(size_t ns)
+{
+ return (struct vec) {
+ .n = 0,
+ .s = 1,
+ .ns = ns,
+ .buf = malloc(ns),
+ };
+}
+
+size_t vec_len(struct vec *v)
+{
+ return v->n;
+}
+
+void *vec_at(struct vec *v, size_t i)
+{
+ assert(i < v->n && "out of vector bounds");
+ return v->buf + i * v->ns;
+}
+
+void *vec_back(struct vec *v)
+{
+ assert(v->n);
+ return v->buf + (v->n - 1) * v->ns;
+}
+
+void *vec_pop(struct vec *v)
+{
+ assert(v->n && "attempting to pop empty vector");
+ v->n--;
+ return v->buf + v->n * v->ns;
+}
+
+void vec_append(struct vec *v, void *n)
+{
+ v->n++;
+ if (v->n >= v->s) {
+ v->s *= 2;
+ v->buf = realloc(v->buf, v->s * v->ns);
+ }
+
+ void *p = vec_at(v, v->n - 1);
+ memcpy(p, n, v->ns);
+}
+
+void vec_reset(struct vec *v)
+{
+ v->n = 0;
+}
+
+void vec_destroy(struct vec *v) {
+ free(v->buf);
+}