aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--examples/std.ek6
-rw-r--r--hmm.t54
-rw-r--r--include/ek/ast.h13
-rw-r--r--src/actualize.c196
-rw-r--r--src/ast.c37
-rw-r--r--src/parser.y5
-rw-r--r--tests/trait_expand.ek7
-rw-r--r--tests/trait_multiple_expand.ek18
-rw-r--r--tests/trait_recursive_expand.ek8
-rw-r--r--tests/type_expand.ek11
11 files changed, 254 insertions, 102 deletions
diff --git a/.gitignore b/.gitignore
index f532928..192d923 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,5 +3,6 @@ docs/output
build
gen
ek
+*.t
!gen/.gitkeep
!include/ek
diff --git a/examples/std.ek b/examples/std.ek
index f9eb58c..dbf3a8c 100644
--- a/examples/std.ek
+++ b/examples/std.ek
@@ -92,6 +92,10 @@ pub typedef string {
*i9 buf;
}
+pub define string(s) {
+ {.len = sizeof(s), .buf = s} as string;
+}
+
/* result import */
pub typedef result[any T] {
*i9 err;
@@ -116,7 +120,7 @@ pub define fmt[] {
str(*fmt p => string) {
/* is this a loop? allowed? */
/* alternative would be {.len = 0, .buf = ""} as string I guess*/
- const r = p.fmt("".str());
+ const r = p.fmt(string!(""));
if r.err() {
abort("error converting to string");
}
diff --git a/hmm.t b/hmm.t
deleted file mode 100644
index 5c3d76c..0000000
--- a/hmm.t
+++ /dev/null
@@ -1,54 +0,0 @@
-/* common.ek */
-typedef any {}
-
-/* vec.ek */
-typedef vec {
- i27 size;
- i27 cap;
- *any buf;
-}
-
-insert(*vec v, i27 i, typeof(*v.buf) e)
-{
- ...
-}
-
-/* define local vector type */
-define vec!(t)
-{
- struct vec [vec] {*t buf;}
-}
-
-/* map.ek */
-typedef hashable {
- /* pointers? */
- i27 hash(hashable);
-}
-
-typedef map {
- i27 size;
- i27 cap;
- *hashable keys;
- *any values;
-}
-
-// vs. append(*map h, typeof h.keys[0] k, typeof h.values[0] e)
-// vs. append(*map h, ?h.keys[0] k, ?h.values[0] e)
-// vs. append(*map h, #h.keys[0] k, #h.values[0] e)
-// vs. append(*map h, :h.keys[0] k, :h.values[0] e)
-// vs something else, hmm
-append(*map h, `h.keys[0] k, `h.values[0] e)
-{
- ...
-}
-
-/* main.ek */
-main()
-{
- vec!(i27) v;
- // should automatically find the correct insert (unless the same
- // elements as the vec typedef are used somewhere else)
- insert(v, 0, 20);
- // otherwise, force search as vec?
- insert(v as vec, 0, 20);
-}
diff --git a/include/ek/ast.h b/include/ek/ast.h
index ab727eb..7ab03ae 100644
--- a/include/ek/ast.h
+++ b/include/ek/ast.h
@@ -370,20 +370,16 @@ struct trait_implemented {
struct trait_implemented *next;
};
-/**
- * A trait declaration.
- *
- * @todo should templates take decls or should it just be for structures?
- */
+/** A trait definition. */
struct ast_trait {
/** Name of trait. */
struct ast_node *id;
/** Parameters to construct concrete type from trait. */
struct ast_node *params;
+ /** Raw body before expansion. */
+ struct ast_node *raw_body;
/** Body of trait. */
struct ast_node *body;
- /** List of types that implement this trait. */
- struct trait_implemented *impl_by;
};
/** Cast. */
@@ -676,6 +672,7 @@ struct ast_struct {
struct ast_node *generics;
/** Body. */
struct ast_node *body;
+ struct trait_implemented *implemented_by;
};
/** Enum member constant value. */
@@ -1116,7 +1113,7 @@ struct ast_node *gen_alias(struct ast_node *id, struct ast_node *type, struct sr
* @return Corresponding AST node.
*/
struct ast_node *gen_trait(struct ast_node *id, struct ast_node *params,
- struct ast_node *body, struct src_loc loc);
+ struct ast_node *raw_body, struct ast_node *body, struct src_loc loc);
/**
* Generate import;
diff --git a/src/actualize.c b/src/actualize.c
index 20ac5ef..60e5108 100644
--- a/src/actualize.c
+++ b/src/actualize.c
@@ -366,7 +366,7 @@ static int analyze_visibility(struct scope *scope, struct ast_node *node)
default:
ret = -1;
- semantic_error(scope->fctx, node, "unknown top element\n");
+ semantic_error(scope->fctx, node, "unknown top element");
break;
};
@@ -393,7 +393,7 @@ static int analyze_proc(struct scope *scope, struct ast_node *node)
return ret;
}
-static struct ast_node *do_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);
@@ -408,7 +408,7 @@ static struct ast_node *do_type_expand(struct scope *scope, struct ast_node *n)
}
semantic_info(scope->fctx, n, "FIXME: skipping type param check for now");
- struct ast_node *body = AST_TRAIT(trait).body;
+ struct ast_node *body = AST_TRAIT(trait).raw_body;
body = clone_ast_node(body);
struct ast_node *pa = AST_TYPE_EXPAND(n).args;
@@ -419,6 +419,28 @@ static struct ast_node *do_type_expand(struct scope *scope, struct ast_node *n)
return body;
}
+static int implements_trait(struct ast_node *body, struct ast_node *id)
+{
+ foreach_node(n, body) {
+ if (n->node_type != AST_ID)
+ continue;
+
+ if (same_id(n, id))
+ return 1;
+ }
+
+ return 0;
+}
+
+static void mark_implemented(struct ast_node *n)
+{
+ assert(n->node_type == AST_TYPE_EXPAND);
+ /** @todo very hacky but good enough for now */
+ struct ast_node *id = AST_TYPE_EXPAND(n).id;
+ n->node_type = AST_ID;
+ AST_ID(n).id = strdup(AST_ID(id).id);
+}
+
static int analyze_struct(struct scope *scope, struct ast_node *node)
{
assert(node->node_type == AST_STRUCT);
@@ -432,28 +454,52 @@ 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);
+
foreach_node(n, AST_STRUCT(node).body) {
- /* function prototypes are checked later */
- if (n->node_type == AST_PROC && !AST_PROC(n).body)
+ if (n->node_type != AST_TYPE_EXPAND)
continue;
- if (n->node_type == AST_TYPE_EXPAND) {
- struct ast_node *body = do_type_expand(scope, n);
- if (!body)
- return -1;
+ 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");
+ return -1;
+ }
- replace_type_id(body, AST_TYPE_EXPAND(n).id, AST_STRUCT(node).id);
- *n = *body;
+ struct ast_node *body = analyze_type_expand(scope, n);
+ if (!body) {
+ n->node_type = AST_EMPTY;
continue;
}
+ replace_type_id(body, AST_TYPE_EXPAND(n).id, type);
+ ast_block_last(body)->next = n->next;
+ n->next = body;
+
+ mark_implemented(n);
+ }
+
+ foreach_node(n, AST_STRUCT(node).body) {
+ switch (n->node_type) {
+ case AST_EMPTY: continue;
+ case AST_ID: continue;
+ /* prototypes are checked later */
+ case AST_PROC: if (!AST_PROC(n).body) continue;
+ default:
+ }
+
if (analyze_visibility(struct_scope, n))
return -1;
}
foreach_node(n, AST_STRUCT(node).body) {
/* also checks prototypes */
- if (n->node_type == AST_PROC)
+ if (n->node_type != AST_PROC)
continue;
if (analyze_proc(struct_scope, n))
@@ -465,7 +511,7 @@ static int analyze_struct(struct scope *scope, struct ast_node *node)
if (n->node_type != AST_PROC)
continue;
- if (!AST_PROC(n).body)
+ if (AST_PROC(n).body)
continue;
struct ast_node *proc = scope_find_proc(struct_scope, AST_PROC(n).id);
@@ -481,10 +527,12 @@ static int analyze_struct(struct scope *scope, struct ast_node *node)
}
}
-
+ /** @todo there is the possibility that two different traits add the
+ * same prototype, which is reported in traits but not structs? */
return 0;
}
+/* quite a lot of overlap with analyze_struct, kind of ugly I guess */
static int analyze_trait(struct scope *scope, struct ast_node *node)
{
assert(node->node_type == AST_TRAIT);
@@ -495,25 +543,84 @@ static int analyze_trait(struct scope *scope, struct ast_node *node)
scope_add_scope(node->scope, trait_scope);
node->scope = trait_scope;
+ /** @todo should probably add in aliases for the traits in scope? */
if (generics)
ast_set_flags(node, AST_FLAG_GENERIC);
+ struct ast_node *type = gen_type(AST_TYPE_TRAIT, node, NULL, node->loc);
+
+ AST_TRAIT(node).body = clone_ast_node(AST_TRAIT(node).raw_body);
+ /* do type expansions */
foreach_node(n, AST_TRAIT(node).body) {
- if (n->node_type == AST_TYPE_EXPAND) {
- struct ast_node *body = do_type_expand(scope, n);
- if (!body)
- return -1;
+ if (n->node_type != AST_TYPE_EXPAND)
+ continue;
- replace_type_id(body, AST_TYPE_EXPAND(n).id, AST_STRUCT(node).id);
- *n = *body;
+ /* don't re-expand already implemented traits */
+ 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;
continue;
}
+ if (same_id(AST_TRAIT(node).id, AST_TYPE_EXPAND(n).id)) {
+ semantic_error(scope->fctx, n,
+ "recursive trait implementations not allowed");
+ return -1;
+ }
+
+ struct ast_node *body = analyze_type_expand(scope, n);
+ if (!body) {
+ n->node_type = AST_EMPTY;
+ continue;
+ }
+
+ replace_type_id(body, AST_TYPE_EXPAND(n).id, type);
+ ast_last_node(body)->next = n->next;
+ n->next = body;
+
+ mark_implemented(n);
+ }
+
+ /* add all procedure definitions to scope */
+ foreach_node(n, AST_TRAIT(node).body) {
+ /* kind of a hack but these shouldn't be shown to
+ * analyze_visibility */
+ switch (n->node_type) {
+ case AST_EMPTY: continue;
+ case AST_ID: continue;
+ /* prototypes are added later */
+ case AST_PROC: if (!AST_PROC(n).body) continue;
+ default:
+ }
+
if (analyze_visibility(trait_scope, n))
return -1;
}
+ /** @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, AST_TRAIT(node).body) {
+ if (n->node_type != AST_PROC)
+ continue;
+
+ if (AST_PROC(n).body)
+ continue;
+
+ /* prototypes are checked only if there's no implementation */
+ if (scope_find_proc(trait_scope, AST_PROC(n).id))
+ continue;
+
+ if (analyze_visibility(trait_scope, n))
+ return -1;
+ }
+
+ foreach_node(n, AST_TRAIT(node).body) {
+ if (n->node_type != AST_PROC);
+
struct act_state state = {0};
if (analyze_proc(trait_scope, n))
return -1;
@@ -524,6 +631,7 @@ static int analyze_trait(struct scope *scope, struct ast_node *node)
static int analyze_signs(struct scope *scope, struct ast_node *node)
{
+ /** @todo aliases? */
switch (node->node_type) {
case AST_VAR: return analyze_var(scope, node); break;
case AST_PROC: return analyze_proc(scope, node); break;
@@ -554,7 +662,7 @@ static int analyze(struct scope *scope, struct ast_node *tree)
return -1;
printf("actualized:\n");
- dump_ast(0, node);
+ dump_ast_node(0, node);
}
return 0;
@@ -964,8 +1072,18 @@ static int actualize_id(struct act_state *state,
{
UNUSED(state);
assert(id && id->node_type == AST_ID);
- /** @todo at the moment we always assume an ID is a variable, but stuff
- * like function callbacks should be added in the future */
+ /** @todo vars and procs kind of override eachother, i.e.
+ * do_something(){..}
+ * ^() do_something;
+ *
+ * do_something_else(){
+ * ...
+ * do_something(); // calls variable at the moment
+ * }
+ *
+ * Either add in some syntax to distinguish procedure calls and
+ * pointer calls or make procs and vars share the same namespace.
+ * */
struct ast_node *decl = file_scope_find_var(scope, id);
if (decl) {
id->type = decl->type;
@@ -1804,19 +1922,39 @@ static int _replace_type_id(struct ast_node *node, void *data)
struct ast_node *id = pair[0];
struct ast_node *replacement = pair[1];
- switch (node->node_type) {
- case AST_VAR: replace_id(AST_VAR(node).type, id, replacement); break;
- /* I think this might also change some extra stuff we might not want,
- * hmm */
- case AST_TYPE: replace_id(node, id, replacement); break;
+ if (node->node_type != AST_TYPE)
+ goto next;
+
+ switch (AST_TYPE(node).kind) {
+ case AST_TYPE_ID: *node = *clone_ast_node(replacement); break;
+ case AST_TYPE_TRAIT: {
+ struct ast_node *def = AST_TRAIT_TYPE(node).def;
+ assert(def);
+ struct ast_node *name = AST_TRAIT(def).id;
+ if (same_id(id, name))
+ *node = *clone_ast_node(replacement);
+ break;
+ }
+
+ case AST_TYPE_STRUCT: {
+ struct ast_node *def = AST_STRUCT_TYPE(node).def;
+ assert(def);
+
+ struct ast_node *name = AST_STRUCT(def).id;
+ if (same_id(id, name))
+ *node = *clone_ast_node(replacement);
+ break;
+ }
default:
}
+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)
{
+ assert(replacement->node_type == AST_TYPE);
struct ast_node *pair[2] = {id, replacement};
return ast_call_on(_replace_type_id, nodes, pair);
}
@@ -1848,7 +1986,7 @@ static int actualize_trait(struct act_state *state, struct scope *scope,
static int actualize_struct(struct act_state *state,
struct scope *scope, struct ast_node *node)
{
- assert(node->node_type == AST_TRAIT);
+ assert(node->node_type == AST_STRUCT);
foreach_node(n, AST_STRUCT(node).body) {
/* there's really only prodcedure body actualization left I
* guess, as type stuff was taken care of in the analysis phase
diff --git a/src/ast.c b/src/ast.c
index 08b3fb9..a79ffcf 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -518,14 +518,18 @@ struct ast_node *gen_alias(struct ast_node *id, struct ast_node *type, struct sr
return n;
}
-struct ast_node *gen_trait(struct ast_node *id, struct ast_node *params,
- struct ast_node *body, struct src_loc loc)
+struct ast_node *gen_trait(struct ast_node *id,
+ struct ast_node *params,
+ struct ast_node *raw_body,
+ struct ast_node *body,
+ struct src_loc loc)
{
ALLOC_NODE(n, "trait");
n->node_type = AST_TRAIT;
AST_TRAIT(n).id = id;
AST_TRAIT(n).params = params;
AST_TRAIT(n).body = body;
+ AST_TRAIT(n).raw_body = raw_body;
n->loc = loc;
return n;
}
@@ -1119,6 +1123,16 @@ static void __dump_ast(int depth, struct ast_node *node)
}
}
+void dump_ast_node(int depth, struct ast_node *n)
+{
+ if (!n) {
+ dump(depth, "{NULL}\n");
+ return;
+ }
+
+ __dump_ast(depth, n);
+}
+
void dump_ast(int depth, struct ast_node *root)
{
if (!root) {
@@ -1128,7 +1142,7 @@ void dump_ast(int depth, struct ast_node *root)
struct ast_node *n = root;
do {
- __dump_ast(depth, n);
+ dump_ast_node(depth, n);
} while ((n = n->next));
}
@@ -1292,7 +1306,7 @@ struct ast_node *clone_ast_node(struct ast_node *node)
case AST_TYPE_POINTER:
new = gen_type(AST_TYPE_POINTER,
- AST_PTR_TYPE(node).base,
+ clone_ast_node(AST_PTR_TYPE(node).base),
NULL,
node->loc);
break;
@@ -1397,6 +1411,7 @@ struct ast_node *clone_ast_node(struct ast_node *node)
case AST_TRAIT:
new = gen_trait(clone_ast_node(AST_TRAIT(node).id),
clone_ast_node(AST_TRAIT(node).params),
+ clone_ast_node(AST_TRAIT(node).raw_body),
clone_ast_node(AST_TRAIT(node).body),
node->loc);
break;
@@ -1483,9 +1498,9 @@ static int call_on_var(int (*call)(struct ast_node *,
void *), struct ast_node *node, void *data)
{
int ret = 0;
- ret |= call(node->_var.id, data);
- ret |= call(node->_var.type, data);
- ret |= call(node->_var.init, data);
+ ret |= call(AST_VAR(node).id, data);
+ ret |= call(AST_VAR(node).type, data);
+ ret |= call(AST_VAR(node).init, data);
return ret;
}
@@ -1620,6 +1635,12 @@ static int call_on_type_construct(int (*call)(struct ast_node *, void *),
return ret;
}
+static int call_on_type_pointer(int (*call)(struct ast_node *, void *),
+ struct ast_node *node, void *data)
+{
+ return call(AST_PTR_TYPE(node).base, data);
+}
+
static int call_on_type(int (*call)(struct ast_node *, void *),
struct ast_node *node, void *data)
{
@@ -1632,7 +1653,7 @@ static int call_on_type(int (*call)(struct ast_node *, void *),
case AST_TYPE_STRUCT: break;
case AST_TYPE_SIGN: ret = call_on_type_sign(call, node, data); break;
case AST_TYPE_CONSTRUCT: ret = call_on_type_construct(call, node, data); break;
- case AST_TYPE_POINTER: break;
+ case AST_TYPE_POINTER: ret = call_on_type_pointer(call, node, data); break;
case AST_TYPE_PRIMITIVE: break;
}
diff --git a/src/parser.y b/src/parser.y
index ddddcb8..c92ecf1 100644
--- a/src/parser.y
+++ b/src/parser.y
@@ -672,7 +672,8 @@ alias
type_param
: id id {
- $$ = gen_var($2, $1, NULL, src_loc(@$));
+ struct ast_node *t = gen_type(AST_TYPE_ID, $1, NULL, src_loc(@1));
+ $$ = gen_var($2, t, NULL, src_loc(@$));
}
type_params
@@ -685,7 +686,7 @@ opt_type_params
trait
: "define" id "[" opt_type_params "]" "{" opt_members "}" {
- $$ = gen_trait($2, $4, $7, src_loc(@$));
+ $$ = gen_trait($2, $4, $7, NULL, src_loc(@$));
}
enum_val
diff --git a/tests/trait_expand.ek b/tests/trait_expand.ek
new file mode 100644
index 0000000..eca1e16
--- /dev/null
+++ b/tests/trait_expand.ek
@@ -0,0 +1,7 @@
+define b[] {
+ a![];
+}
+
+define a[] {
+ a(*a a);
+}
diff --git a/tests/trait_multiple_expand.ek b/tests/trait_multiple_expand.ek
new file mode 100644
index 0000000..285a6ea
--- /dev/null
+++ b/tests/trait_multiple_expand.ek
@@ -0,0 +1,18 @@
+define b[] {
+ c![];
+ a![];
+}
+
+define a[] {
+ c![];
+ a(*a a);
+}
+
+define c[] {
+ c(*c c);
+}
+
+typedef d {
+ b![];
+ c(*d d) {}
+}
diff --git a/tests/trait_recursive_expand.ek b/tests/trait_recursive_expand.ek
new file mode 100644
index 0000000..53012bf
--- /dev/null
+++ b/tests/trait_recursive_expand.ek
@@ -0,0 +1,8 @@
+define b[] {
+ a![];
+}
+
+define a[] {
+ b![];
+ a(*a a);
+}
diff --git a/tests/type_expand.ek b/tests/type_expand.ek
new file mode 100644
index 0000000..e58d0a0
--- /dev/null
+++ b/tests/type_expand.ek
@@ -0,0 +1,11 @@
+define trait[] {
+ trait_func(*trait a, *trait b);
+}
+
+typedef a[] {
+ trait![];
+}
+
+main() {
+ a![] some_var;
+}