aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO33
-rw-r--r--include/ek/ast.h15
-rw-r--r--src/actualize.c268
-rw-r--r--src/ast.c70
-rw-r--r--src/lower.c373
-rw-r--r--src/parser.y4
-rw-r--r--tests/struct_call.ek37
7 files changed, 683 insertions, 117 deletions
diff --git a/TODO b/TODO
index 3d19998..f18f2af 100644
--- a/TODO
+++ b/TODO
@@ -25,3 +25,36 @@ OPTIMIZATIONS:
actualized procs immediately instead of keeping them around in memory
+ Simplify IDs to just char arrays, would save on memory
+ Types can just be types, they maybe don't need to be full on AST nodes
+
+LOWERING:
+
+Structure handling in lowering could probably be as follows:
+
+Imagine
+struct something {
+ i27 a;
+ other_struct b;
+};
+
+main ()
+{
+ // something s = {...};
+ i27 s_def = alloc $STRUCT_SIZE;
+ // i27 a = s.a;
+ i27 member_a = struct_def + 0;
+ i27 a << struct_def 0;
+
+ // other_struct c = s.b;
+ i27 c_def = alloc $OTHER_STRUCT_SIZE;
+ i27 member_b = struct_def + 3;
+ c_def blit member_b, $OTHER_STRUCT_SIZE; // has to be added to qbt,
+ // might need to be handled
+ // like store with constant as
+ // 'return'. Or can c_def be
+ // considered an output value
+ // for blit?
+
+ /* calls just dump all registers down I guess, as do returns. No
+ * implicit conversion to pointer and passed as first argument, ufcs
+ * does that for us. */
+}
diff --git a/include/ek/ast.h b/include/ek/ast.h
index de195c6..9770e91 100644
--- a/include/ek/ast.h
+++ b/include/ek/ast.h
@@ -220,6 +220,7 @@ struct type {
/* next */
struct type *n;
+ long long size;
struct src_loc loc;
struct scope *scope;
};
@@ -261,6 +262,10 @@ struct type *tgen_type(enum type_kind kind,
char *id,
struct src_loc loc);
+size_t align3k(size_t o);
+size_t type_size(struct type *t);
+size_t type_offsetof(struct type *t, char *m);
+
#define tgen_primitive(kind, id, def, loc) \
tgen_type(kind, NULL, NULL, def, NULL, id, loc)
@@ -364,17 +369,17 @@ static inline bool is_primitive(struct type *t)
return false;
}
-#define gen_str_type1(k, s, t, a, loc) gen_ast(k, a, NULL, NULL, NULL, t, s, 0, \
+#define gen_str_type1(k, s, t, a, loc) gen_ast(k, a, NULL, NULL, NULL, t, s, -1, \
loc)
#define gen_str_type(k, s, t, loc) gen_str_type1(k, s, t, NULL, loc)
-#define gen_type(k, a, type, loc) gen_ast(k, a, NULL, NULL, NULL, type, NULL, 0, \
+#define gen_type(k, a, type, loc) gen_ast(k, a, NULL, NULL, NULL, type, NULL, -1, \
loc)
-#define gen_str2(k, s, a, b, loc) gen_ast(k, a, b, NULL, NULL, NULL, s, 0, loc)
+#define gen_str2(k, s, a, b, loc) gen_ast(k, a, b, NULL, NULL, NULL, s, -1, loc)
#define gen_str1(k, s, a, loc) gen_str2(k, s, a, NULL, loc)
-#define gen_str(k, s, loc) gen_ast(k, NULL, NULL, NULL, NULL, NULL, s, 0, loc)
+#define gen_str(k, s, loc) gen_ast(k, NULL, NULL, NULL, NULL, NULL, s, -1, loc)
-#define gen4(k, a, b, c, d, loc) gen_ast(k, a, b, c, d, NULL, NULL, 0, loc)
+#define gen4(k, a, b, c, d, loc) gen_ast(k, a, b, c, d, NULL, NULL, -1, loc)
#define gen3(k, a, b, c, loc) gen4(k, a, b, c, NULL, loc)
#define gen2(k, a, b, loc) gen3(k, a, b, NULL, loc)
#define gen1(k, a, loc) gen2(k, a, NULL, loc)
diff --git a/src/actualize.c b/src/actualize.c
index 7f17fbc..71f54a4 100644
--- a/src/actualize.c
+++ b/src/actualize.c
@@ -119,6 +119,10 @@ static int is_lvalue(struct ast *n)
if (n->k == AST_DEREF)
return 1;
+ /* at least generally speaking */
+ if (n->k == AST_DOT)
+ return 1;
+
return 0;
}
@@ -330,17 +334,21 @@ static int eval_const_if(struct scope *scope, struct ast *node)
return -1;
}
+static int actualize_proc_sign(struct scope *scope, struct ast *proc);
+
static int analyze_visibility(struct scope *scope, struct ast *node)
{
if (!node)
return 0;
- node->scope = scope;
-
switch (node->k) {
- case AST_PROC_DEF: return scope_add_proc(scope, node);
- case AST_MACRO_DEF: return scope_add_macro(scope, node);
case AST_VAR_DEF: return scope_add_var(scope, node);
+ case AST_PROC_DEF: {
+ node->scope = create_scope();
+ scope_add_scope(scope, node->scope);
+ return scope_add_proc(scope, node);
+ }
+ case AST_MACRO_DEF: return scope_add_macro(scope, node);
case AST_IMPORT: {
const char *file = import_file(node);
@@ -360,14 +368,15 @@ static int analyze_visibility(struct scope *scope, struct ast *node)
}
case AST_STRUCT_DEF: {
- /* we shouldn't get any anonymous structs at this stage */
- char *id = struct_id(node);
- return scope_add_type(scope, id, node);
+ node->scope = create_scope();
+ scope_add_scope(scope, node->scope);
+ return scope_add_type(scope, struct_id(node), node);
}
case AST_ENUM_DEF: {
- char *id = enum_id(node);
- return scope_add_type(scope, id, node);
+ node->scope = create_scope();
+ scope_add_scope(scope, node->scope);
+ return scope_add_type(scope, enum_id(node), node);
}
case AST_ALIAS_DEF: {
@@ -376,8 +385,9 @@ static int analyze_visibility(struct scope *scope, struct ast *node)
}
case AST_TRAIT_DEF: {
- char *id = trait_id(node);
- return scope_add_type(scope, id, node);
+ node->scope = create_scope();
+ scope_add_scope(scope, node->scope);
+ return scope_add_type(scope, trait_id(node), node);
}
case AST_EMPTY: {
@@ -475,7 +485,8 @@ static struct ast *analyze_type_expand(struct scope *scope,
struct ast *n)
{
assert(n->k == AST_TYPE_EXPAND);
- struct ast *trait = file_scope_find_type(scope, type_expand_id(n));
+ struct act_state state = {0};
+ struct ast *trait = actualized_file_scope_find_type(&state, scope, type_expand_id(n));
if (!trait) {
semantic_error(scope->fctx, n, "no such type");
return NULL;
@@ -486,9 +497,7 @@ static struct ast *analyze_type_expand(struct scope *scope,
return NULL;
}
- semantic_info(scope->fctx, n,
- "FIXME: skipping type param check for now");
- struct ast *body = trait_raw_body(trait);
+ struct ast *body = trait_body(trait);
body = clone_ast(body);
struct type *pa = type_expand_args(n);
@@ -617,6 +626,12 @@ static int analyze_trait(struct scope *scope, struct ast *node)
static int analyze(struct scope *scope, struct ast *tree)
{
+ foreach_node(node, tree) {
+ /* make all defs visible */
+ if (analyze_visibility(scope, node))
+ return -1;
+ }
+
foreach_node(node, tree){
struct act_state state = {0};
if (actualize(&state, scope, node))
@@ -803,14 +818,22 @@ static int maybe_ufcs(struct act_state *state, struct scope *scope,
return 0;
struct ast *expr = dot_expr(dot);
- call_expr(call) = gen_fetch(dot_id(dot), clone_type(expr->t), dot->loc);
+ char *id = strdup(dot_id(dot));
+ call_expr(call) = gen_fetch(id, clone_type(expr->t), dot->loc);
(call_expr(call))->t = clone_type(dot->t);
(call_expr(call))->scope = scope;
- /* hard core type */
- struct ast *ref = gen_unop(AST_REF, expr, dot->loc);
- ref->t = tgen_ptr(clone_type(expr->t), dot->loc);
- ref->scope = scope;
+ struct ast *ref = NULL;
+ struct type *ptypes = callable_ptypes(dot->t);
+ if (ptypes->k == TYPE_PTR) {
+ /* is ufcs expects reference to member, try to take address */
+ ref = gen_unop(AST_REF, expr, dot->loc);
+ ref->t = tgen_ptr(clone_type(expr->t), dot->loc);
+ ref->scope = scope;
+ } else {
+ /* otherwise, try to pass expr as is */
+ ref = expr;
+ }
if (simplify_refderef(state, scope, ref))
return -1;
@@ -904,16 +927,14 @@ static int undefined_gotos(struct act_state *state, struct scope *scope)
return ret;
}
-static int actualize_proc(struct act_state *state,
- struct scope *scope, struct ast *proc)
+static int actualize_proc_sign(struct scope *scope, struct ast *proc)
{
- UNUSED(state);
- /* actualize_proc is called on trait procs as well, but I believe
- * that's fine? */
assert(proc && proc->k == AST_PROC_DEF);
struct act_state new_state = {0};
- /* params should already have been actualized, should maybe check */
+ proc->scope = create_scope();
+ scope_add_scope(scope, proc->scope);
+
if (actualize_list(&new_state, proc->scope, proc_params(proc))) {
destroy_act_state(&new_state);
return -1;
@@ -940,9 +961,22 @@ static int actualize_proc(struct act_state *state,
}
set_type(proc, callable);
- scope_add_proc(scope, proc);
+ return 0;
+}
+
+static int actualize_proc(struct act_state *state,
+ struct scope *scope, struct ast *proc)
+{
+ UNUSED(state);
+ if (actualize_proc_sign(scope, proc))
+ return -1;
+
+ /* external functions and so on */
+ if (!proc_body(proc))
+ return 0;
/* actualize body */
+ struct act_state new_state = {0};
new_state.cur_proc = proc;
if (actualize(&new_state, proc->scope, proc_body(proc))) {
destroy_act_state(&new_state);
@@ -1149,33 +1183,18 @@ static int actualize_tid(struct act_state *state, struct scope *scope,
return 0;
}
- struct ast *def = file_scope_find_type(scope, t->id);
+ struct ast *def = actualized_file_scope_find_type(state, scope, t->id);
if (!def) {
type_error(scope->fctx, t, "no such type");
return -1;
}
- if (!def->t && actualize(state, def->scope, def))
- return -1;
-
assert(t->n == NULL);
- assert(def->k != AST_TRAIT_DEF);
-
- if (def->k == AST_ALIAS_DEF) {
- replace_type(t, clone_type_list(def->t));
+ replace_type(t, clone_type_list(def->t));
+ if (def->k == AST_ALIAS_DEF)
t->a = def;
- return 0;
- }
- else if (def->k == AST_STRUCT_DEF) {
- replace_type(t, clone_type_list(def->t));
- return 0;
- }
- else if (def->k == AST_ENUM_DEF) {
- replace_type(t, clone_type_list(def->t));
- return 0;
- }
- return -1;
+ return 0;
}
static int actualize_ptr(struct act_state *state, struct scope *scope,
@@ -1901,24 +1920,93 @@ static int replace_type_id(struct ast *nodes, char *id,
static int actualize_trait(struct act_state *state, struct scope *scope,
struct ast *node)
{
- UNUSED(scope);
assert(node->k == AST_TRAIT_DEF);
+ struct ast *params = trait_params(node);
+ struct scope *trait_scope = create_scope();
+ if (!trait_scope)
+ return -1;
+
+ scope_add_scope(node->scope, trait_scope);
+ node->scope = trait_scope;
+
+ /** @todo should probably add in aliases for the traits in scope? */
+ if (params)
+ ast_set_flags(node, AST_FLAG_GENERIC);
+
+ node->t = tgen_trait(trait_id(node), node, node->loc);
+
+ /* copy body */
+ node->a2 = clone_ast(trait_raw_body(node));
+
+ /* do type expansions */
foreach_node(n, trait_body(node)) {
- /* there's really only prodcedure body actualization left I
- * guess, as type stuff was taken care of in the analysis phase
+ if (n->k != AST_TYPE_EXPAND)
+ continue;
+
+ /* don't re-expand already implemented traits */
+ if (implements_trait(trait_body(node), type_expand_id(n))) {
+ /* not sure about this, but at least we don't have stray
+ * type expands everywhere */
+ n->k = AST_EMPTY;
+ continue;
+ }
+
+ if (same_id(trait_id(node), type_expand_id(n))) {
+ semantic_error(scope->fctx, n,
+ "recursive trait implementations not allowed");
+ return -1;
+ }
+
+ struct ast *body = analyze_type_expand(scope, n);
+ if (!body) {
+ n->k = AST_EMPTY;
+ continue;
+ }
+
+ replace_type_id(body, type_expand_id(n), node->t);
+ ast_last(body)->n = n->n;
+ n->n = body;
+ }
+
+
+ /** @todo I should really check that there's just one prototype and one
+ * implementation of that prototype, not sure what the best approach
+ * would be. Add a prototypes -list to scopes? */
+
+ /* add all prototypes that don't have matching definition to scope */
+ foreach_node(n, trait_body(node)) {
+ if (n->k == AST_TYPE_EXPAND)
+ continue;
+
+ if (n->k == AST_PROC_DEF && !proc_body(n))
+ continue;
+
+ if (analyze_visibility(trait_scope, n))
+ return -1;
+
+ struct act_state state = {0};
+ if (actualize(&state, trait_scope, n))
+ return -1;
+ }
+
+ foreach_node(n, trait_body(node)) {
+ /* 'actualize' prototypes to make them appear in scope searches
* */
if (n->k != AST_PROC_DEF)
continue;
- /* don't actualize prototypes, duh */
- if (!proc_body(n))
+ if (proc_body(n))
continue;
- if (actualize(state, node->scope, n))
+ struct ast *exists = scope_find_proc(trait_scope, proc_id(n));
+ if (exists)
+ continue;
+
+ if (actualize_proc_sign(trait_scope, n))
return -1;
}
- return 0;
+ return node->t == NULL;
}
static int actualize_struct(struct act_state *state,
@@ -1930,9 +2018,7 @@ static int actualize_struct(struct act_state *state,
if (!struct_scope)
return -1;
- scope_add_type(scope, struct_id(node), node);
-
- scope_add_scope(node->scope, struct_scope);
+ scope_add_scope(scope, struct_scope);
node->scope = struct_scope;
if (params)
ast_set_flags(node, AST_FLAG_GENERIC);
@@ -1990,6 +2076,29 @@ static int actualize_struct(struct act_state *state,
*/
foreach_node(n, struct_body(node)) {
+ /* don't actually actualize type expansion for now, it's just
+ * sticking around to make it easier to check if a type
+ * implements a trait */
+ /* should maybe rename to trait_expand or something, huh */
+ if (n->k == AST_TYPE_EXPAND)
+ continue;
+
+ /* prototypes are handled separately */
+ if (n->k == AST_PROC_DEF && !proc_body(n))
+ continue;
+
+ if (analyze_visibility(struct_scope, n))
+ return -1;
+ }
+
+ foreach_node(n, struct_body(node)) {
+ if (n->k == AST_TYPE_EXPAND)
+ continue;
+
+ /* prototypes are (still) handled separately */
+ if (n->k == AST_PROC_DEF && !proc_body(n))
+ continue;
+
struct act_state state = {0};
if (actualize(&state, struct_scope, n))
return -1;
@@ -2003,6 +2112,9 @@ static int actualize_struct(struct act_state *state,
if (proc_body(n))
continue;
+ if (actualize_proc_sign(struct_scope, n))
+ return -1;
+
struct ast *proc = scope_find_proc(struct_scope, proc_id(n));
if (!proc) {
semantic_error(scope->fctx, n,
@@ -2180,6 +2292,27 @@ static int actualize_assign(struct act_state *state, struct scope *scope,
return 0;
}
+static int actualize_enum_fetch(struct act_state *state, struct scope *scope,
+ struct ast *fetch)
+{
+ char *id = fetch_id(fetch);
+ struct type *type = fetch_type(fetch);
+ struct ast *def = type->d;
+ assert(def);
+
+ struct ast *member = lookup_enum_member(def, id);
+ if (!member) {
+ char *estr = type_str(type);
+ semantic_error(scope->fctx, fetch, "no such member in enum %s");
+ free(estr);
+ return -1;
+ }
+
+ replace_ast(fetch, val_val(member));
+ set_type(fetch, def->t);
+ return 0;
+}
+
static int actualize_fetch(struct act_state *state, struct scope *scope,
struct ast *fetch)
{
@@ -2188,24 +2321,28 @@ static int actualize_fetch(struct act_state *state, struct scope *scope,
if (actualize_type(state, scope, type))
return -1;
- if (type->k != TYPE_ENUM) {
- type_error(scope->fctx, type, "type is not an enum");
+ if (type->k == TYPE_ENUM)
+ return actualize_enum_fetch(state, scope, fetch);
+
+ if (type->k != TYPE_STRUCT && type->k != TYPE_TRAIT && !is_primitive(type)) {
+ char *tstr = type_str(type);
+ semantic_error(scope->fctx, fetch,
+ "illegal fetch type %s", tstr);
+ free(tstr);
return -1;
}
- char *id = fetch_id(fetch);
struct ast *def = type->d;
assert(def);
- struct ast *member = lookup_enum_member(def, id);
+ struct ast *member = scope_find_proc(def->scope, fetch_id(fetch));
if (!member) {
- char *estr = type_str(type);
- semantic_error(scope->fctx, fetch, "no such member in enum %s");
- free(estr);
+ semantic_error(scope->fctx, fetch,
+ "no such proc");
return -1;
}
- set_type(fetch, def->t);
+ set_type(fetch, member->t);
return 0;
}
@@ -2384,8 +2521,6 @@ static int actualize(struct act_state *state, struct scope *scope,
case AST_TRAIT_DEF: ret = actualize_trait(state, scope, node); break;
case AST_ALIAS_DEF: ret = actualize_alias(state, scope, node); break;
case AST_ENUM_DEF: ret = actualize_enum(state, scope, node); break;
- case AST_MACRO_DEF: ret = actualize_macro_def(state, scope, node);
- break;
case AST_STRUCT_DEF: ret = actualize_struct(state, scope, node); break;
case AST_VAR_DEF: ret = actualize_var(state, scope, node); break;
case AST_CALL: ret = actualize_call(state, scope, node); break;
@@ -2406,6 +2541,7 @@ static int actualize(struct act_state *state, struct scope *scope,
case AST_FOR: ret = actualize_for(state, scope, node); break;
case AST_MACRO_EXPAND: ret = actualize_macro_expand(state, scope, node);
break;
+ case AST_MACRO_DEF: ret = actualize_macro_def(state, scope, node); break;
default:
/* more like internal_error, maybe? */
semantic_error(scope->fctx, node,
diff --git a/src/ast.c b/src/ast.c
index b3cf408..df016a8 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -91,6 +91,7 @@ static struct type *create_empty_type()
struct type *n = calloc(1, sizeof(struct type));
/* just to be safe */
n->k = TYPE_VOID;
+ n->size = -1;
vect_append(struct ast *, types, &n);
return n;
}
@@ -379,6 +380,11 @@ struct ast *clone_ast(struct ast *n)
new->v = n->v;
new->f = n->f;
+ /* unsure if this should be a separate step maybe? Generally this is
+ * unwanted, but I might run into exceptions and then it's more
+ * difficult to rebuild the init/actual state... */
+ ast_clear_flags(new, AST_FLAG_INIT | AST_FLAG_ACTUAL);
+
if (n->t)
new->t = clone_type_list(n->t);
@@ -658,3 +664,67 @@ int equiv_type_lists(struct type *t1, struct type *t2)
return 1;
}
+
+size_t align3k(size_t o)
+{
+ size_t rem = o % 3;
+ if (rem)
+ o += rem;
+
+ return o;
+}
+
+static size_t struct_size(struct type *t)
+{
+ if (t->size != -1)
+ return t->size;
+
+ size_t size = 0;
+ foreach_node(n, struct_body(t->d)) {
+ if (n->k != AST_VAR_DEF)
+ continue;
+
+ size_t sz = type_size(n->t);
+ if (sz > 2)
+ size = align3k(size);
+
+ size += sz;
+ }
+
+ t->size = size;
+ return size;
+}
+
+size_t type_size(struct type *t)
+{
+ switch (t->k) {
+ case TYPE_I9: return 1;
+ case TYPE_I27: return 3;
+ case TYPE_PTR: return 3;
+ case TYPE_STRUCT: return struct_size(t);
+ default:
+ }
+
+ assert(0 && "unhandled type to get size of");
+ abort();
+}
+
+size_t type_offsetof(struct type *t, char *m)
+{
+ assert(t->k == TYPE_STRUCT);
+
+ size_t offset = 0;
+ foreach_node(n, struct_body(t->d)) {
+ if (n->k != AST_VAR_DEF)
+ continue;
+
+ if (same_id(var_id(n), m))
+ break;
+
+ size_t sz = type_size(n->t);
+ if (sz > 2)
+ offset = align3k(offset);
+ }
+
+ return offset;
+}
diff --git a/src/lower.c b/src/lower.c
index 50e7ef8..253293f 100644
--- a/src/lower.c
+++ b/src/lower.c
@@ -27,6 +27,9 @@ struct lower_state {
struct vec bottom;
struct vec out;
int64_t uniq;
+
+ struct vec dealloc;
+ size_t deallocs;
};
static struct lower_state create_state()
@@ -35,6 +38,8 @@ static struct lower_state create_state()
state.top = vec_create(sizeof(char *));
state.bottom = vec_create(sizeof(char *));
state.out = vec_create(sizeof(char *));
+ state.dealloc = vec_create(sizeof(char *));
+ state.deallocs = 0;
state.uniq = 0;
return state;
}
@@ -44,10 +49,17 @@ 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);
+ assert(vec_len(&state->dealloc) == 0);
vec_destroy(&state->top);
vec_destroy(&state->bottom);
vec_destroy(&state->out);
+ vec_destroy(&state->dealloc);
+}
+
+static void add_dealloc(struct lower_state *s, char *dealloc)
+{
+ vect_append(char *, s->dealloc, &dealloc);
}
static void push_loop(struct lower_state *s, char *top, char *bottom, char *out)
@@ -254,12 +266,14 @@ static int lower_param(struct lower_state *s, struct ast *p)
char *t = is_small_type(type) ? "i9" : "i27";
printf("%s ", t);
output_ast_id(p);
- printf(",");
+ printf(", ");
return 0;
}
static int lower_params(struct lower_state *s, struct ast *params)
{
+ /** @todo fix structs, struct arguments must be stored to some
+ * structures on the stack */
foreach_node(p, params) {
if (lower_param(s, p))
return -1;
@@ -278,17 +292,12 @@ static int lower_var(struct lower_state *s, struct ast *v,
return -1;
}
- /* 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(v, ri);
- /* I assume we're always dealing with i27 for now */
- /** @todo qbt could maybe skip the type stuff except for casts */
- printf("i27 %s = %s;\n", name, r.s);
- struct retval n = build_retval(REG_I27, name);
- vec_append(retval, &n);
- }
+ char *name = mangle(v);
+ /* if we're a struct, we can just reuse the newly built constant struct,
+ * yay, just rename it */
+ printf("i27 %s = %s;\n", name, (retval_at(input, 0)).s);
+ struct retval n = build_retval(REG_I27, name);
+ vec_append(retval, &n);
retval_destroy(&input);
return 0;
@@ -447,12 +456,6 @@ static int lower_id(struct lower_state *s, struct ast *id,
char *m = mangle(id);
struct type *type = id->t;
- if (!is_primitive(type) && type->k != TYPE_CALLABLE) {
- semantic_error(id->scope->fctx, id,
- "only primitive ids currently implemented");
- return -1;
- }
-
enum retval_kind kind = is_small_type(type) ? REG_I9 : REG_I27;
/* this likely isn't enough and we need to add the & to most things we
@@ -676,6 +679,122 @@ static int lower_comparison(struct lower_state *s, struct ast *i,
return 0;
}
+static int collect_primitive_arg(struct lower_state *s, struct ast *c, struct vec *retval)
+{
+ struct vec arg = retval_create();
+ if (lower_expr(s, c, &arg)) {
+ retval_destroy(&arg);
+ return -1;
+ }
+
+ assert(vec_len(&arg) == 1);
+ struct retval r = retval_at(arg, 0);
+ /* very important! */
+ r.s = strdup(r.s);
+ vec_append(retval, &r);
+
+ retval_destroy(&arg);
+ return 0;
+}
+
+static size_t collect_struct_tmps(struct lower_state *s, struct ast *def, char *name, size_t base, struct vec *retval)
+{
+ size_t offset = base;
+ foreach_node(n, struct_body(def)) {
+ if (n->k != AST_VAR_DEF)
+ continue;
+
+ if (n->t->k == TYPE_STRUCT) {
+ offset += collect_struct_tmps(s, n->t->d, name, offset, retval);
+ continue;
+ }
+
+ size_t sz = type_size(n->t);
+ assert(sz == 1 || sz == 3);
+
+ char *type = sz == 1 ? "i9" : "i27";
+ char *tmp = build_str("callstruct_%zd", s->uniq++);
+ printf("%s %s << %s %zd;\n", type, tmp, name, offset);
+
+ struct retval r = build_retval(sz == 1 ? REG_I9 : REG_I27, tmp);
+ vec_append(retval, &r);
+ offset += sz;
+ }
+
+ assert((offset - base) == type_size(def->t));
+ return offset;
+}
+
+static int collect_struct_arg(struct lower_state *s, struct ast *c, struct vec *retval)
+{
+ struct vec arg = retval_create();
+ if (lower_expr(s, c, &arg)) {
+ retval_destroy(&arg);
+ return -1;
+ }
+
+ struct ast *def = c->t->d;
+ collect_struct_tmps(s, def, (retval_at(arg, 0)).s, 0, retval);
+ retval_destroy(&arg);
+ return 0;
+}
+
+static size_t collect_struct_retvals(struct lower_state *s, struct ast *def, char *rbuf, size_t base, struct vec *stores)
+{
+ size_t offset = base;
+ foreach_node(n, struct_body(def)) {
+ if (n->k != AST_VAR_DEF)
+ continue;
+
+ if (n->t->k == TYPE_STRUCT) {
+ offset += collect_struct_retvals(s, n->t->d, rbuf, offset, stores);
+ continue;
+ }
+
+ size_t sz = type_size(n->t);
+ assert(sz == 1 || sz == 3);
+
+ char *type = sz == 1 ? "i9" : "i27";
+ char *tmp = build_str("callret_%zd", s->uniq++);
+ printf("%s, ", tmp);
+
+ char *store = build_str("%s >> %s %s %zd;\n", tmp, type, rbuf, offset);
+ vec_append(stores, &store);
+ offset += sz;
+ }
+
+ assert(offset - base == type_size(def->t));
+ return offset;
+}
+
+static void collect_struct_rets(struct lower_state *s, struct type *rtype, char *rbuf, struct vec *retval)
+{
+ struct ast *def = rtype->d;
+ struct vec stores = vec_create(sizeof(char *));
+
+ printf("(");
+ collect_struct_retvals(s, def, rbuf, 0, &stores);
+ printf(");\n");
+
+ foreach_vec(si, stores) {
+ char *store = vect_at(char *, stores, si);
+ printf("%s", store);
+ free(store);
+ }
+
+ vec_destroy(&stores);
+ struct retval r = build_retval(REG_I27, rbuf);
+ vec_append(retval, &r);
+}
+
+static void collect_primitive_rets(struct lower_state *s, struct type *rtype, struct vec *retval)
+{
+
+ char *name = build_str("(rv_%zd);\n", s->uniq++);
+ struct retval r = build_retval(is_small_type(rtype) ? REG_I9 : REG_I27, name);
+ vec_append(retval, &r);
+}
+
static int lower_call(struct lower_state *s, struct ast *c,
struct vec *retval)
{
@@ -687,24 +806,29 @@ static int lower_call(struct lower_state *s, struct ast *c,
return -1;
}
+ struct type *rtype = callable_rtype((call_expr(c))->t);
+ char *rbuf = NULL;
+ if (rtype->k == TYPE_STRUCT) {
+ rbuf = build_str("rbuf_%zd", s->uniq++);
+ printf("i27 %s = ^ %zd;\n", rbuf, type_size(rtype));
+ }
+
/* collect all args */
struct vec args = retval_create();
foreach_node(a, call_args(c)) {
- struct vec arg = retval_create();
- if (lower_expr(s, a, &arg)) {
- retval_destroy(&arg);
- retval_destroy(&args);
- return -1;
- }
+ if (a->t->k == TYPE_STRUCT) {
+ if (collect_struct_arg(s, a, &args)) {
+ 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);
+ continue;
}
- retval_destroy(&arg);
+ if (collect_primitive_arg(s, a, &args)) {
+ retval_destroy(&args);
+ return -1;
+ }
}
assert(vec_len(&call) == 1);
@@ -715,27 +839,156 @@ static int lower_call(struct lower_state *s, struct ast *c,
struct retval r = retval_at(args, ri);
printf("%s, ", r.s);
}
+ retval_destroy(&args);
+
+ printf(") => ");
+
+ if (rtype->k == TYPE_STRUCT) {
+ collect_struct_rets(s, rtype, rbuf, retval);
+ }
+ else if (rtype->k != TYPE_VOID) {
+ collect_primitive_rets(s, rtype, retval);
+ }
+ else {
+ printf("();\n");
+ }
+
+ return 0;
+}
+
+static int lower_init(struct lower_state *s, struct ast *init,
+ struct vec *retval)
+{
+ assert(init->k == AST_INIT);
+ char *name = build_str("init_%zi", s->uniq++);
+ size_t size = type_size(init->t);
+ assert(size % 3 == 0);
+
+ /* alloc stack space for struct */
+ printf("i27 %s = ^ %zi;\n", name, size);
+
+ char *dealloc = build_str("^^ %zi;\n", size);
+ add_dealloc(s, dealloc);
+
+ size_t offset = 0;
+ foreach_node(n, init_body(init)) {
+ size_t sz = type_size(n->t);
+
+ /* 2 is a special case where a struct consists of two i9s */
+ if (sz > 2)
+ offset = align3k(offset);
+
+ struct vec val = retval_create();
+ if (lower_expr(s, var_init(n), &val)) {
+ retval_destroy(&val);
+ return -1;
+ }
+
+ printf("i27 %soff = %s + %zi;\n", name, name, offset);
+
+ char *type = is_small_type(n->t) ? "i9" : "i27";
+ struct retval r = retval_at(val, 0);
+ if (n->t->k == TYPE_STRUCT)
+ printf("%soff <<* %zi %s;\n", name, sz, r.s);
+ else
+ printf("%s >> %s %soff;\n", r.s, type, name);
+
+ offset += sz;
+ retval_destroy(&val);
+ }
+
+ struct retval r = build_retval(REG_I27, name);
+ vec_reset(retval);
+ vect_append(struct retval, *retval, &r);
+ assert(offset == size);
+
+ return 0;
+}
+
+static int lower_fetch(struct lower_state *s, struct ast *f, struct vec *retval)
+{
+ /* at this point all fetches should exclusively be about fetching a
+ * specific function from a struct */
+ assert(f->k == AST_FETCH);
+ assert(f->t->k == TYPE_CALLABLE);
+ char *name = mangle(f);
+ struct retval r = build_retval(REG_I27, name);
+ vec_append(retval, &r);
+ return 0;
+}
+
+static int lower_ref(struct lower_state *s, struct ast *r, struct vec *retval)
+{
+ assert(r->k == AST_REF);
+ if (lower_expr(s, unop_expr(r), retval))
+ return -1;
+
+ /* structs are internally references anyway */
+ if (r->t->k == TYPE_STRUCT)
+ return 0;
+
+ struct retval ret = retval_at(*retval, 0);
+ char *ref = build_str("&%s", ret.s);
+ free(ret.s);
+ ret.s = ref;
+ retval_at(*retval, 0) = ret;
+ return 0;
+}
+
+static int lower_deref(struct lower_state *s, struct ast *d, struct vec *retval)
+{
+ assert(d->k == AST_DEREF);
+ struct ast *expr = unop_expr(d);
+ struct vec input = retval_create();
+ if (lower_expr(s, expr, &input)) {
+ retval_destroy(&input);
+ return -1;
+ }
+
+ assert(expr->t->k == TYPE_PTR);
+ char *name = build_str("deref_%zd", s->uniq++);
+ char *base = (retval_at(input, 0)).s;
+ char *type = is_small_type(expr->t) ? "i9" : "i27";
+ printf("%s %s = << %s 0;\n", type, name, base);
+ retval_destroy(&input);
+
+ struct retval r = build_retval(is_small_type(expr->t) ? REG_I9 : REG_I27, name);
+ vect_append(struct retval, *retval, &r);
+ return 0;
+}
- if (!is_primitive(c->t) && c->t->k != TYPE_VOID) {
- semantic_error(c->scope->fctx, c,
- "only primitive return types implemented");
- retval_destroy(&args);
+static int lower_dot(struct lower_state *s, struct ast *d, struct vec *retval)
+{
+ assert(d->k == AST_DOT);
+ assert((dot_expr(d))->t->k == TYPE_STRUCT);
+
+ struct vec input = retval_create();
+ if (lower_expr(s, dot_expr(d), &input)) {
+ retval_destroy(&input);
return -1;
}
- printf(") => ( ");
+ struct type *type = d->t;
+ size_t off = type_offsetof((dot_expr(d))->t, dot_id(d));
+ char *name = build_str("dot_%zd", s->uniq++);
+ char *base = (retval_at(input, 0)).s;
- int i = 0;
- foreach_type(t, c->t) {
- char *s = build_str("r%i\n", i);
- enum retval_kind k = is_small_type(t) ? REG_I9 : REG_I27;
- struct retval r = build_retval(k, s);
- vec_append(retval, &r);
- i++;
+ struct retval r;
+ if (type->k == TYPE_STRUCT) {
+ size_t size = type_size(type);
+ printf("i27 %s = ^ %zd;\n", name, type_size(type));
+ printf("i27 %soff = %s + %zd;\n", name, base, off);
+ printf("%s <<* %zd %soff;\n", name, size, name);
+ r = build_retval(REG_I27, name);
+ }
+ else {
+ char *t = is_small_type(type) ? "i9" : "i27";
+ printf("%s << %s %s %zd;\n", name, t, base, off);
+ r = build_retval(is_small_type(type) ? REG_I9 : REG_I27, name);
}
- printf(" );\n");
- retval_destroy(&args);
+ vec_append(retval, &r);
+ retval_destroy(&input);
return 0;
}
@@ -756,12 +1009,20 @@ static int lower_expr(struct lower_state *s, struct ast *e,
switch (e->k) {
case AST_VAR_DEF: return lower_var(s, e, retval);
+ case AST_INIT: return lower_init(s, e, retval);
case AST_ID: return lower_id(s, e, retval);
/* var is considered an expression in this case */
case AST_CAST: return lower_cast(s, e, retval);
case AST_RETURN: return lower_return(s, e, retval);
case AST_ASSIGN: return lower_assign(s, e, retval);
case AST_CALL: return lower_call(s, e, retval);
+ case AST_FETCH: return lower_fetch(s, e, retval);
+ /* in the AST, it's useful to think of ref as just a
+ * unary operation, but here it's weird enough that it's
+ * not really worth it */
+ case AST_REF: return lower_ref(s, e, retval);
+ case AST_DEREF: return lower_deref(s, e, retval);
+ case AST_DOT: return lower_dot(s, e, retval);
case AST_IF: return lower_expr_if(s, e, retval);
default:
semantic_error(e->scope->fctx, e,
@@ -776,11 +1037,34 @@ static int lower_block(struct lower_state *s, struct ast *block)
{
assert(block->k == AST_BLOCK);
assert(!ast_flags(block, AST_FLAG_DOEXPR));
+
+ /* deallocs_top marks where the dealloc stack was when entering the
+ * block, and deallocs_bottom where the parent block's dealloc stack
+ * is. Stuff like continue and break will probably need this
+ * information, which is why it's attached to lower_state */
+ size_t deallocs_top = vec_len(&s->dealloc);
+ size_t deallocs_parent = s->deallocs;
+
+ s->deallocs = deallocs_top;
foreach_node(n, block_body(block)) {
if (lower_statement(s, n))
return -1;
}
+ assert(deallocs_top <= vec_len(&s->dealloc));
+
+ /* heh, if this block contains a return, the deallocs get placed after
+ * the return. In the case of stack freeing, that's fine, but I'll have
+ * to be careful if I do something more fancy in the future. Qbt should
+ * be able to detect unreachable code, so this is not exactly an issue. */
+ while (vec_len(&s->dealloc) > deallocs_top) {
+ /* perform all deallocs that were queued within this block */
+ char *dealloc = vect_pop(char *, s->dealloc);
+ printf("%s", dealloc);
+ free(dealloc);
+ }
+
+ s->deallocs = deallocs_parent;
return 0;
}
@@ -794,6 +1078,7 @@ static int lower_statement(struct lower_state *s, struct ast *n)
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;
+ case AST_EMPTY: break;
default: ret = lower_expr(s, n, &retval); break;
}
diff --git a/src/parser.y b/src/parser.y
index 1bec7e2..4837a53 100644
--- a/src/parser.y
+++ b/src/parser.y
@@ -706,8 +706,8 @@ member
;
members
- : member members { $$ = $1; $1->n = $2; }
- | member
+ : member members { $$ = $1; $1->n = $2; ast_set_flags($$, AST_FLAG_MEMBER); }
+ | member { $$ = $1; ast_set_flags($$, AST_FLAG_MEMBER); }
opt_members
: members
diff --git a/tests/struct_call.ek b/tests/struct_call.ek
new file mode 100644
index 0000000..d73dfb1
--- /dev/null
+++ b/tests/struct_call.ek
@@ -0,0 +1,37 @@
+typedef i27 {}
+typedef i9 {}
+
+typedef int_pair {
+ i27 a;
+ i27 b;
+}
+
+extern putchar(i9 c);
+
+add_int_pair(int_pair a, int_pair b => int_pair)
+{
+ return int_pair!{.a = a.a + b.a, .b = a.b + b.b};
+}
+
+add_int_pair_self(*int_pair a, *int_pair b)
+{
+ a*.a = a*.a + b*.a;
+ a*.b = a*.b + b*.b;
+}
+
+main()
+{
+ mut a = int_pair!{.a = 1, .b = 2};
+ mut b = int_pair!{.a = 3, .b = 4};
+
+ mut n = add_int_pair(a, b);
+ putchar('0' + n.a as i9);
+ putchar('0' + n.b as i9);
+
+ add_int_pair_self(a&, b&);
+
+ putchar('0' + a.a as i9);
+ putchar('0' + a.b as i9);
+ putchar('0' + b.a as i9);
+ putchar('0' + b.b as i9);
+}