aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO27
-rw-r--r--examples/ptrs.fwd7
-rw-r--r--examples/vec.fwd150
-rw-r--r--include/fwd/ast.h61
-rw-r--r--mod/mem.c12
-rw-r--r--src/analyze.c240
-rw-r--r--src/ast.c16
-rw-r--r--src/debug.c10
-rw-r--r--src/lower.c221
-rw-r--r--src/move.c51
-rw-r--r--src/parser.y48
11 files changed, 628 insertions, 215 deletions
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..e1c6a24
--- /dev/null
+++ b/TODO
@@ -0,0 +1,27 @@
++ Some kind of "unreachable" might be nice to help the move checker? for
+instance,
+
+ if abc {
+ release(var);
+ fwdpanic("!!!");
+ }
+
+ use(var)
+
+might be nicer to write than
+
+ if abc {
+ release(var);
+ fwdpanic("!!!");
+ } else {
+ use(var);
+ }
+
+or the current alternative
+
+ guard(abc) => {
+ release(var);
+ fwdpanic("!!!");
+ } => ;
+
+ use (var);
diff --git a/examples/ptrs.fwd b/examples/ptrs.fwd
index a0f8aac..a1efc84 100644
--- a/examples/ptrs.fwd
+++ b/examples/ptrs.fwd
@@ -1,7 +1,6 @@
-fwd_null(auto x, () null, (&auto) ok);
-fwd_copy(auto x, (auto, auto) ok);
-fwd_println(auto x);
-fwd_intalloc((*int) ok);
+extern fwd_copy(auto x, (auto, auto) ok);
+extern fwd_println(auto x);
+extern fwd_intalloc((*int) ok);
main()
{
diff --git a/examples/vec.fwd b/examples/vec.fwd
index 1cc10b3..6c854d5 100644
--- a/examples/vec.fwd
+++ b/examples/vec.fwd
@@ -1,51 +1,125 @@
+import "../mod/libfwdio.so"
import "../mod/libfwdmem.so"
import "../mod/libfwdutil.so"
-/* trait */
-any = {}
+vec {
+ u64 n;
+ u64 s;
+ *i64 buf;
+}
+
+init_vec(u64 n, (vec) ok)
+{
+ ok([n => n, 0 as u64 => s, * => buf] vec);
+}
+
+next_s_vec(u64 s, (u64) ok)
+{
+ if s == 0 as u64 {
+ ok(1 as u64);
+ } else {
+ ok(2 as u64 * s);
+ }
+}
-/* template */
-vec[any type]() {
- <> {
- i64 s;
- i64 n;
- *[]type buf;
+reserve_vec(vec v, u64 c, (vec) ok)
+{
+ v => [n => n, s => s, buf => buf];
+ n + c => nn;
+
+ /* expand if we run out of space */
+ if nn > s {
+ next_s_vec(s) => s;
+
+ /* note that fwdrealloc creates a new closure, meaning that it
+ * takes ownership of v.buf, so we can't back out and do a
+ * single ok(v) at the end of the function */
+ fwdrealloc(buf, s * sizeof(i64)) => nbuf;
+ ok([nn => n, s => s, nbuf => buf] vec);
+ } else {
+ ok([nn => n, s => s, buf => buf] vec);
}
+}
+
+set_vec(vec v, i64 i, i64 e, (vec) ok)
+{
+ v => [n => n, s => s, buf => buf];
+ buf + (n - 1 as u64) => nbuf;
+ nil nbuf {fwdpanic("should never happen");} => dst;
+ e => dst*;
+ ok([n => n, s => s, nbuf => buf] vec);
+}
+
+n_vec(vec v, (vec, u64) ok)
+{
+ v => [n => n, s => s, buf => buf];
+ ok([n => n, s => s, buf => buf] vec, n);
+}
+
+append_vec(vec v, i64 e, (vec) ok)
+{
+ n_vec(v) => v, n;
+ reserve_vec(v, n + 1 as u64) => v;
+ set_vec(v, n as i64 - 1, e) => v;
+ ok(v);
+}
+
+at_vec(vec v, u64 i, (vec, &i64) ok)
+{
+ v => [n => n, s => s, buf => buf];
+ if i >= n {fwdpanic("bounds error");}
+ fwdptradd(buf, (i * sizeof(i64)) as i64) => *i64 buf;
+ nil buf {fwdpanic("should never happen");} => &i64 bufr;
+ ok([n => n, s => s, buf => buf] vec, bufr);
+}
+
+destroy_vec(vec v)
+{
+ v => [n => n, s => s, buf => buf];
+ fwdfree(buf);
+ nil n;
+ nil s;
+}
- <>create((<>) next)
- {
- next((0 => s, 0 => n, * => buf)<>);
+populate_vec(i64 i, i64 n, vec v, (vec) ok)
+{
+ if i < n {
+ append_vec(v, i) => vec v;
+ populate_vec(i + 1, n, v, ok);
+ } else {
+ ok(v);
}
+}
- <>append(<> v, type n, (<>) next)
- {
- v.n = v.n + 1;
- if v.s == 0 {
- v.s = 1;
- }
-
- if v.n > v.s {
- if v.s == 0 {
- v.s = 1;
- }
-
- v.s = v.s * 2;
- fwdrealloc(v.buf, v.s as u64 * sizeof(type)) => ptr;
- v.buf = ptr;
- }
-
- /* in a real implementation this would be moved to after
- * fwdrealloc I guess, but this is fine for now */
- nil v.buf {fwdpanic("no buf for vector");} => buf;
- buf*[v.n - 1] = n;
- next(v);
+guard(bool c, () err | () ok)
+{
+ if c {
+ err();
+ } else {
+ ok();
}
+}
- <>destroy(<> v)
- {
- fwdfree(v.buf);
- nil v;
+check_vec(i64 i, i64 n, vec v, (vec) ok)
+{
+ if i < n {
+ at_vec(v, i as u64) => v, elem;
+
+ guard(elem* != i) => {
+ destroy_vec(v);
+ fwdpanic("vec built wrong");
+ } => ;
+
+ check_vec(i + 1, n, v, ok);
+ } else {
+ ok(v);
}
}
-u8vec = vec[u8]()
+main()
+{
+ init_vec(0 as u64) => vec v;
+ populate_vec(0, 1000000, v) => vec v;
+ check_vec(0, 1000000, v) => vec v;
+ destroy_vec(v);
+}
diff --git a/include/fwd/ast.h b/include/fwd/ast.h
index 044a27e..4c8d412 100644
--- a/include/fwd/ast.h
+++ b/include/fwd/ast.h
@@ -32,10 +32,11 @@ struct src_loc {
struct ast;
enum type_kind {
- TYPE_ID = 1, TYPE_REF, TYPE_PTR, TYPE_ARR, TYPE_CLOSURE,
+ TYPE_ID = 1, TYPE_REF, TYPE_PTR, TYPE_CLOSURE,
TYPE_PURE_CLOSURE, TYPE_FUNC_PTR, TYPE_VOID, TYPE_NIL,
TYPE_I8, TYPE_I16, TYPE_I32, TYPE_I64,
- TYPE_U8, TYPE_U16, TYPE_U32, TYPE_U64
+ TYPE_U8, TYPE_U16, TYPE_U32, TYPE_U64,
+ TYPE_BOOL
};
static inline bool is_int_type(enum type_kind k)
@@ -104,6 +105,8 @@ enum ast_kind {
AST_IMPORT,
/** Creating new variable */
AST_LET,
+ AST_PUT,
+ AST_WRITE,
/** If statement */
AST_IF,
/** Nil check */
@@ -116,8 +119,6 @@ enum ast_kind {
AST_PROC_DEF,
/** Variable declaration/definition. */
AST_VAR_DEF,
- /** Dot. \c a.b; */
- AST_DOT,
/* Closure */
AST_CLOSURE,
/** Construct new type, something!{} */
@@ -171,10 +172,11 @@ enum ast_kind {
/** Sizeof */
AST_SIZEOF,
AST_AS,
- AST_ARR,
- AST_ASSIGN,
AST_CONSTRUCTION,
AST_CONSTRUCT,
+ AST_DECONSTRUCTION,
+ AST_DECONSTRUCT,
+ AST_EXPLODE,
AST_SELF,
AST_FORGET,
AST_PASTE,
@@ -202,6 +204,7 @@ enum ast_flag {
AST_FLAG_NOMOVES = (1 << 2),
AST_FLAG_PUBLIC = (1 << 3),
AST_REQ_FRAME = (1 << 4),
+ AST_FLAG_LOWERED = (1 << 5)
};
struct ast {
@@ -323,9 +326,7 @@ static inline bool is_const(struct ast *x)
static inline bool is_lvalue(struct ast *node)
{
switch (node->k) {
- case AST_ARR:
case AST_DEREF:
- case AST_DOT:
case AST_ID:
return true;
@@ -398,10 +399,6 @@ static inline bool is_trivially_copyable(struct type *type)
#define tgen_nil(loc) \
tgen_type(TYPE_NIL, NULL, NULL, loc)
-#define tarr_base(x) return_t0(x, TYPE_ARR)
-#define tgen_arr(base, len, loc) \
- tgen_type(TYPE_ARR, base, len, loc)
-
#define tptr_base(x) return_t0(x, TYPE_PTR)
#define tgen_ptr(base, loc) \
tgen_type(TYPE_PTR, base, NULL, loc)
@@ -414,7 +411,7 @@ static inline bool is_trivially_copyable(struct type *type)
#define tgen_closure(args, loc) \
tgen_type(TYPE_CLOSURE, args, NULL, loc)
-#define tpure_closure_args(x) return_t0(x, TYPE_CLOSURE)
+#define tpure_closure_args(x) return_t0(x, TYPE_PURE_CLOSURE)
#define tgen_pure_closure(args, loc) \
tgen_type(TYPE_PURE_CLOSURE, args, NULL, loc)
@@ -447,6 +444,15 @@ static inline bool is_trivially_copyable(struct type *type)
#define gen_construct(id, members, loc) \
gen_str1(AST_CONSTRUCT, id, members, loc)
+#define deconstruction_id(x) return_s(x, AST_DECONSTRUCTION)
+#define deconstruction_var(x) return_a0(x, AST_DECONSTRUCTION)
+#define gen_deconstruction(id, expr, loc) \
+ gen_str1(AST_DECONSTRUCTION, id, expr, loc)
+
+#define deconstruct_members(x) return_a0(x, AST_DECONSTRUCT)
+#define gen_deconstruct(members, loc) \
+ gen1(AST_DECONSTRUCT, members, loc)
+
#define gen_paste(l, r, loc) \
gen2(AST_PASTE, l, r, loc)
@@ -481,16 +487,6 @@ static inline bool is_trivially_copyable(struct type *type)
#define gen_sizeof(type, loc) \
gen_ast(AST_SIZEOF, NULL, NULL, NULL, NULL, type, NULL, 0, 0., loc)
-#define assign_expr(x) return_a1(x, AST_ASSIGN)
-#define assign_target(x) return_a0(x, AST_ASSIGN)
-#define gen_assign(l, r, loc) \
- gen2(AST_ASSIGN, l, r, loc)
-
-#define arr_base(x) return_a0(x, AST_ARR)
-#define arr_idx(x) return_a1(x, AST_ARR)
-#define gen_arr(base, idx, loc) \
- gen2(AST_ARR, base, idx, loc)
-
#define unop_expr(x) ({assert(is_unop(x)); x->a0;})
#define gen_unop(op, expr, loc) \
gen1(op, expr, loc)
@@ -507,11 +503,6 @@ static inline bool is_trivially_copyable(struct type *type)
#define gen_proc(id, params, rtype, body, loc) \
gen_ast(AST_PROC_DEF, params, body, NULL, NULL, rtype, id, 0, 0., loc)
-#define dot_id(x) return_s(x, AST_DOT)
-#define dot_expr(x) return_a0(x, AST_DOT)
-#define gen_dot(id, expr, loc) \
- gen_str1(AST_DOT, id, expr, loc)
-
#define var_id(x) return_s(x, AST_VAR_DEF)
#define var_type(x) return_t2(x, AST_VAR_DEF)
#define gen_var(id, type, loc) \
@@ -570,6 +561,20 @@ static inline bool is_trivially_copyable(struct type *type)
#define gen_let(var, from, loc) \
gen2(AST_LET, var, from, loc)
+#define put_dst(x) return_a0(x, AST_PUT)
+#define gen_put(dst, loc) \
+ gen1(AST_PUT, dst, loc)
+
+#define write_dst(x) return_a0(x, AST_WRITE)
+#define write_src(x) return_a1(x, AST_WRITE)
+#define gen_write(dst, src, loc) \
+ gen2(AST_WRITE, dst, src, loc)
+
+#define explode_deconstruct(x) return_a0(x, AST_EXPLODE)
+#define explode_expr(x) return_a1(x, AST_EXPLODE)
+#define gen_explode(deconstruct, expr, loc) \
+ gen2(AST_EXPLODE, deconstruct, expr, loc)
+
#define init_args(x) return_t2(x, AST_INIT)
#define init_body(x) return_a0(x, AST_INIT)
#define init_id(x) return_s(x, AST_INIT)
diff --git a/mod/mem.c b/mod/mem.c
index 5303ffa..96d6d0e 100644
--- a/mod/mem.c
+++ b/mod/mem.c
@@ -24,6 +24,15 @@ long fwdfree(fwd_extern_args_t args)
return 0;
}
+long fwdptradd(fwd_extern_args_t args)
+{
+ assert(args.argc == 2);
+ void *ptr = FWD_ARG(args, 0, void *, FWD_PTR);
+ int64_t add = FWD_ARG_T(args, 1, int64_t);
+ FWD_RET(FWD_PTR, (char *)ptr + add);
+ return 0;
+}
+
int fwdopen(fwd_state_t *state)
{
FWD_REGISTER(state, fwdmalloc,
@@ -34,5 +43,8 @@ int fwdopen(fwd_state_t *state)
FWD_REGISTER(state, fwdfree,
FWD_VOID, FWD_PTR);
+
+ FWD_REGISTER(state, fwdptradd,
+ FWD_PTR, FWD_PTR, FWD_T(int64_t));
return 0;
}
diff --git a/src/analyze.c b/src/analyze.c
index 4f98ac6..2dc039a 100644
--- a/src/analyze.c
+++ b/src/analyze.c
@@ -106,6 +106,19 @@ static int analyze_binop(struct state *state, struct scope *scope,
if (analyze(state, scope, rhs))
return -1;
+ /* allow pointer arithmetic, follows C conventions */
+ if (node->k == AST_ADD || node->k == AST_SUB) {
+ if (is_ptr_type(lhs->t->k) && is_int_type(rhs->t->k)) {
+ node->t = lhs->t;
+ return 0;
+ }
+
+ if (is_ptr_type(rhs->t->k) && is_int_type(lhs->t->k)) {
+ node->t = rhs->t;
+ return 0;
+ }
+ }
+
if (!types_match(lhs->t, rhs->t)) {
type_mismatch(scope, node, lhs->t, rhs->t);
@@ -135,17 +148,9 @@ static int analyze_comparison(struct state *state, struct scope *scope,
return -1;
}
- /** @todo check type is some primitive */
- char *tf = strdup("bool");
- if (!tf) {
- internal_error("failed allocating comparison bool str");
- return -1;
- }
-
- node->t = tgen_id(tf, node->loc);
+ node->t = tgen_type(TYPE_BOOL, NULL, NULL, node->loc);
if (!node->t) {
internal_error("failed allocating comparison bool type");
- free(tf);
return -1;
}
@@ -190,10 +195,14 @@ static int analyze_var(struct state *state, struct scope *scope,
static int analyze_let(struct state *state, struct scope *scope,
struct ast *node)
{
- if (analyze(state, scope, let_var(node)))
+ if (analyze(state, scope, let_expr(node)))
return -1;
- if (analyze(state, scope, let_expr(node)))
+ struct ast *var = let_var(node);
+ if (!(var_type(var)))
+ var_type(var) = (let_expr(node))->t;
+
+ if (analyze(state, scope, var))
return -1;
struct type *l = (let_var(node))->t;
@@ -232,7 +241,11 @@ static int deduce_closure_types(struct scope *scope, struct ast *node, struct ty
}
struct ast *param = closure_bindings(node);
- struct type *t = tclosure_args(type);
+ struct type *t = (type->k == TYPE_CLOSURE)
+ ? tclosure_args(type)
+ : tpure_closure_args(type)
+ ;
+
if (ast_list_len(param) != type_list_len(t)) {
semantic_error(scope, node, "expected %zu closure parameters, got %zu",
type_list_len(t), ast_list_len(param));
@@ -535,7 +548,7 @@ static int analyze_construct(struct state *state, struct scope *scope, struct as
{
struct ast *def = file_scope_find_type(scope, construct_id(node));
if (!def) {
- semantic_error(scope, node, "no such type");
+ semantic_error(scope, node, "no such type: %s", construct_id(node));
return -1;
}
@@ -578,6 +591,69 @@ static int analyze_construct(struct state *state, struct scope *scope, struct as
return 0;
}
+static int analyze_explode(struct state *state, struct scope *scope, struct ast *node)
+{
+ if (analyze(state, scope, explode_expr(node)))
+ return -1;
+
+ struct type *type = (explode_expr(node))->t;
+ if (type->k != TYPE_ID) {
+ char *str = type_str(type);
+ semantic_error(scope, explode_expr(node),
+ "expected struct type, got %s\n",
+ str);
+ free(str);
+ return -1;
+ }
+
+ struct ast *def = file_scope_find_type(scope, type->id);
+ if (!def || def->k != AST_STRUCT_DEF) {
+ semantic_error(scope, explode_expr(node),
+ "no such struct type: %s\n",
+ type->id);
+ return -1;
+ }
+
+ struct ast *params = struct_body(def);
+ struct ast *args = explode_deconstruct(node);
+
+ if (ast_list_len(params) != ast_list_len(args)) {
+ semantic_error(scope, node, "expected %zu args, got %zu\n",
+ ast_list_len(params), ast_list_len(args));
+ return -1;
+ }
+
+ foreach_node(arg, args) {
+ struct ast *var = deconstruction_var(arg);
+ struct ast *exists = scope_find_symbol(def->scope, deconstruction_id(arg));
+ if (!exists) {
+ semantic_error(scope, arg, "no member %s in %s\n",
+ deconstruction_id(arg), type->id);
+ return -1;
+ }
+
+ if (!var_type(var))
+ (var_type(var)) = exists->t;
+
+ if (analyze(state, scope, var))
+ return -1;
+
+ if (!types_match(exists->t, var->t)) {
+ type_mismatch(scope, arg, exists->t, var->t);
+ return -1;
+ }
+
+ if (scope_add_symbol(scope, var))
+ return -1;
+
+ /** @todo check for duplicates, preferably not in O(n^2) or with
+ * a memory allocation? */
+ }
+
+ node->t = tgen_void(node->loc);
+ return 0;
+}
+
static int analyze_if(struct state *state, struct scope *scope,
struct ast *node)
{
@@ -661,66 +737,6 @@ static int analyze_as(struct state *state, struct scope *scope, struct ast *node
return 0;
}
-static int analyze_assign(struct state *state, struct scope *scope, struct ast *node)
-{
- struct ast *expr = assign_expr(node);
- if (analyze(state, scope, expr))
- return -1;
-
- struct ast *target = assign_target(node);
- if (analyze(state, scope, target))
- return -1;
-
-
- if (!types_match(expr->t, target->t)) {
- type_mismatch(scope, node, target->t, expr->t);
- return -1;
- }
-
- if (!is_lvalue(target)) {
- semantic_error(scope, target, "not an lvalue");
- return -1;
- }
-
- node->t = target->t;
- return 0;
-}
-
-static int analyze_dot(struct state *state, struct scope *scope, struct ast *node)
-{
- struct ast *expr = dot_expr(node);
- if (analyze(state, scope, expr))
- return -1;
-
- struct type *type = expr->t;
- if (type->k != TYPE_ID) {
- char *s = type_str(type);
- semantic_error(scope, node, "type %s does not have any members", s);
- free(s);
- return -1;
- }
-
- struct ast *def = file_scope_find_type(scope, tid_str(type));
- if (!def) {
- semantic_error(scope, node, "no such type: %s", tid_str(type));
- return -1;
- }
-
- if (def->k != AST_STRUCT_DEF) {
- semantic_error(scope, node, "not a struct type: %s", tid_str(type));
- return -1;
- }
-
- struct ast *member = scope_find_symbol(def->scope, dot_id(node));
- if (!member) {
- semantic_error(scope, node, "no such member: %s", dot_id(node));
- return -1;
- }
-
- node->t = member->t;
- return 0;
-}
-
static int analyze_sizeof(struct state *state, struct scope *scope, struct ast *node)
{
if (analyze_type(state, scope, sizeof_type(node)))
@@ -731,34 +747,6 @@ static int analyze_sizeof(struct state *state, struct scope *scope, struct ast *
return 0;
}
-static int analyze_arr(struct state *state, struct scope *scope, struct ast *node)
-{
- struct ast *base = arr_base(node);
- if (analyze(state, scope, base))
- return -1;
-
- if (base->t->k != TYPE_ARR) {
- char *s = type_str(base->t);
- semantic_error(scope, base, "expected array type, got %s", s);
- free(s);
- return -1;
- }
-
- struct ast *idx = arr_idx(node);
- if (analyze(state, scope, idx))
- return -1;
-
- if (!is_int_type(idx->t->k)) {
- char *s = type_str(idx->t);
- semantic_error(scope, idx, "expected int type, got %s", s);
- free(s);
- return -1;
- }
-
- node->t = tarr_base(base->t);
- return 0;
-}
-
static int analyze_nil_check(struct state *state, struct scope *scope, struct ast *node)
{
struct ast *expr = nil_check_expr(node);
@@ -805,6 +793,43 @@ static int analyze_forget(struct state *state, struct scope *scope, struct ast *
return 0;
}
+static int analyze_put(struct state *state, struct scope *scope, struct ast *put)
+{
+ struct ast *dst = put_dst(put);
+ if (analyze(state, scope, dst))
+ return -1;
+
+ struct type *type = dst->t;
+ if (type->k != TYPE_REF) {
+ char *str = type_str(type);
+ semantic_error(scope, put, "trying to write to non-ref type %s\n", str);
+ free(str);
+ return -1;
+ }
+
+ put->t = tref_base(type);
+ return 0;
+}
+
+static int analyze_write(struct state *state, struct scope *scope, struct ast *write)
+{
+ struct ast *src = write_src(write);
+ if (analyze(state, scope, src))
+ return -1;
+
+ struct ast *dst = write_dst(write);
+ if (analyze(state, scope, dst))
+ return -1;
+
+ if (!types_match(src->t, dst->t)) {
+ type_mismatch(scope, write, src->t, dst->t);
+ return -1;
+ }
+
+ write->t = tgen_void(write->loc);
+ return 0;
+}
+
static int analyze(struct state *state, struct scope *scope, struct ast *node)
{
if (!node)
@@ -864,14 +889,14 @@ static int analyze(struct state *state, struct scope *scope, struct ast *node)
case AST_STRUCT_DEF: ret = analyze_struct(state, scope, node); break;
case AST_CONSTRUCT: ret = analyze_construct(state, scope, node); break;
+ case AST_EXPLODE: ret = analyze_explode(state, scope, node); break;
case AST_SIZEOF: ret = analyze_sizeof(state, scope, node); break;
case AST_NIL: ret = analyze_nil(state, scope, node); break;
case AST_AS: ret = analyze_as(state, scope, node); break;
- case AST_ASSIGN: ret = analyze_assign(state, scope, node); break;
- case AST_ARR: ret = analyze_arr(state, scope, node); break;
- case AST_DOT: ret = analyze_dot(state, scope, node); break;
+ case AST_WRITE: ret = analyze_write(state, scope, node); break;
+ case AST_PUT: ret = analyze_put(state, scope, node); break;
case AST_FORGET: ret = analyze_forget(state, scope, node); break;
@@ -919,7 +944,7 @@ static int analyze_list(struct state *state, struct scope *scope,
static int analyze_type(struct state *state, struct scope *scope,
struct type *type)
{
- if (type->t0 && analyze_type(state, scope, type->t0))
+ if (type->t0 && analyze_type_list(state, scope, type->t0))
return -1;
/* temporary */
@@ -969,6 +994,11 @@ static int analyze_type(struct state *state, struct scope *scope,
return 0;
}
+ if (strcmp(id, "bool") == 0) {
+ type->k = TYPE_BOOL;
+ return 0;
+ }
+
/* check for user-defined types */
struct ast *def = file_scope_find_type(scope, id);
if (!def) {
diff --git a/src/ast.c b/src/ast.c
index f8b3447..9515775 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -215,11 +215,12 @@ void ast_dump(int depth, struct ast *n)
DUMP(AST_IF);
DUMP(AST_NIL);
DUMP(AST_LET);
+ DUMP(AST_PUT);
+ DUMP(AST_WRITE);
DUMP(AST_INIT);
DUMP(AST_CALL);
DUMP(AST_PROC_DEF);
DUMP(AST_VAR_DEF);
- DUMP(AST_DOT);
DUMP(AST_BLOCK);
DUMP(AST_ID);
DUMP(AST_EMPTY);
@@ -247,10 +248,11 @@ void ast_dump(int depth, struct ast *n)
DUMP(AST_BNEG);
DUMP(AST_SIZEOF);
DUMP(AST_AS);
- DUMP(AST_ARR);
- DUMP(AST_ASSIGN);
DUMP(AST_CONSTRUCTION);
DUMP(AST_CONSTRUCT);
+ DUMP(AST_DECONSTRUCTION);
+ DUMP(AST_DECONSTRUCT);
+ DUMP(AST_EXPLODE);
DUMP(AST_SELF);
DUMP(AST_FORGET);
DUMP(AST_PASTE);
@@ -661,11 +663,12 @@ const char *ast_str(enum ast_kind k)
CASE(AST_IF);
CASE(AST_NIL);
CASE(AST_LET);
+ CASE(AST_PUT);
+ CASE(AST_WRITE);
CASE(AST_INIT);
CASE(AST_CALL);
CASE(AST_PROC_DEF);
CASE(AST_VAR_DEF);
- CASE(AST_DOT);
CASE(AST_BLOCK);
CASE(AST_ID);
CASE(AST_EMPTY);
@@ -693,8 +696,6 @@ const char *ast_str(enum ast_kind k)
CASE(AST_BNEG);
CASE(AST_SIZEOF);
CASE(AST_AS);
- CASE(AST_ARR);
- CASE(AST_ASSIGN);
CASE(AST_CONSTRUCTION);
CASE(AST_CONSTRUCT);
CASE(AST_SELF);
@@ -710,6 +711,9 @@ const char *ast_str(enum ast_kind k)
CASE(AST_CONST_BOOL);
CASE(AST_CONST_FLOAT);
CASE(AST_CONST_STR);
+ CASE(AST_DECONSTRUCT);
+ CASE(AST_DECONSTRUCTION);
+ CASE(AST_EXPLODE);
}
#undef CASE
diff --git a/src/debug.c b/src/debug.c
index 8ef4293..538fdfe 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -222,6 +222,10 @@ static void _type_str(FILE *f, struct type *type)
fprintf(f, "u64");
break;
+ case TYPE_BOOL:
+ fprintf(f, "bool");
+ break;
+
case TYPE_VOID:
fprintf(f, "void");
break;
@@ -230,12 +234,6 @@ static void _type_str(FILE *f, struct type *type)
fprintf(f, "nil");
break;
- case TYPE_ARR:
- /** @todo length? */
- fprintf(f, "[]");
- _type_list_str(f, tarr_base(type));
- break;
-
case TYPE_PTR:
fprintf(f, "*");
_type_list_str(f, tptr_base(type));
diff --git a/src/lower.c b/src/lower.c
index e29365d..9689928 100644
--- a/src/lower.c
+++ b/src/lower.c
@@ -27,7 +27,11 @@
static inline void mangle(FILE *f, struct ast *id)
{
- assert(id->k == AST_PROC_DEF || id->k == AST_VAR_DEF || id->k == AST_ID);
+ assert(id->k == AST_STRUCT_DEF
+ || id->k == AST_PROC_DEF
+ || id->k == AST_VAR_DEF
+ || id->k == AST_ID);
+
assert(id->s && id->scope);
fprintf(f, "%s_s%zu", id->s, id->scope->number);
}
@@ -150,6 +154,12 @@ static void add_defn(struct state *state, char *defn)
string_vec_append(state->defns, defn);
}
+static bool type_lowered(struct ast *type)
+{
+ assert(type->k == AST_STRUCT_DEF);
+ return ast_flags(type, AST_FLAG_LOWERED);
+}
+
static void add_type(struct state *state, char *type)
{
string_vec_append(state->types, type);
@@ -160,8 +170,10 @@ static int lower_block(struct state *state, struct ast *block, bool last);
static int lower_params(struct state *state, struct ast *params);
static int lower_expr(struct state *state, struct ast *expr);
static int lower_proc(struct state *state, struct ast *proc);
+static int lower_type_def(struct state *state, struct ast *type);
+static char *lower_type_str(struct state *state, struct scope *scope, struct type *type);
-static void _type_str(FILE *f, struct type *type)
+static int _type_str(FILE *f, struct state *state, struct scope *scope, struct type *type)
{
assert(type);
switch (type->k) {
@@ -201,14 +213,40 @@ static void _type_str(FILE *f, struct type *type)
fprintf(f, "fwd_closure_t");
break;
+ case TYPE_PURE_CLOSURE:
+ fprintf(f, "fwd_closure_t");
+ break;
+
+ case TYPE_ID: {
+ struct ast *def = file_scope_find_type(scope, type_str(type));
+
+ if (!ast_flags(def, AST_FLAG_LOWERED))
+ if (lower_type_def(state, def))
+ return -1;
+
+ char *name = mangle2(def);
+ fprintf(f, "%s", name);
+ free(name);
+ break;
+ }
+
+ case TYPE_PTR: {
+ char *rest = lower_type_str(state, scope, tptr_base(type));
+ fprintf(f, "%s*", rest);
+ free(rest);
+ break;
+ }
+
default:
internal_error("unhandled type lowering for %s", type_str(type));
abort();
break;
}
+
+ return 0;
}
-static char *lower_type_str(struct type *type)
+static char *lower_type_str(struct state *state, struct scope *scope, struct type *type)
{
assert(type);
@@ -216,9 +254,15 @@ static char *lower_type_str(struct type *type)
FILE *f = open_memstream(&type_buf, &type_len);
assert(f);
- _type_str(f, type);
+ int r = _type_str(f, state, scope, type);
fclose(f);
assert(type_buf);
+
+ if (r) {
+ free(type_buf);
+ return NULL;
+ }
+
return type_buf;
}
@@ -243,7 +287,7 @@ static int lower_closure_call(struct state *state, struct ast *call, struct ast
foreach_node(a, call_args(call)) {
returning |= a->k == AST_CLOSURE;
- char *type = lower_type_str(a->t);
+ char *type = lower_type_str(state, a->scope, a->t);
fprintf(ctx, " %s a%zu;\n", type, idx);
free(type);
@@ -269,7 +313,7 @@ out:
q, q);
}
else {
- fprintf(state->code, "stack = %s_call(stack, %s_ctx);\n",
+ fprintf(state->code, "stack = %s(stack, %s_ctx);\n",
q, q);
}
@@ -340,10 +384,26 @@ static int lower_comparison(struct state *state, struct ast *expr)
fprintf(state->code, " <= ");
break;
+ case AST_GE:
+ fprintf(state->code, " >= ");
+ break;
+
case AST_LT:
fprintf(state->code, " < ");
break;
+ case AST_GT:
+ fprintf(state->code, " > ");
+ break;
+
+ case AST_EQ:
+ fprintf(state->code, " == ");
+ break;
+
+ case AST_NE:
+ fprintf(state->code, " != ");
+ break;
+
default:
internal_error("unhandled lowering for comparison %s", ast_str(expr->k));
abort();
@@ -366,8 +426,12 @@ static int lower_binop(struct state *state, struct ast *expr)
fprintf(state->code, " + ");
break;
+ case AST_MUL:
+ fprintf(state->code, " * ");
+ break;
+
default:
- internal_error("unhandled comparison %s", ast_str(expr->k));
+ internal_error("unhandled binop %s", ast_str(expr->k));
abort();
}
@@ -406,6 +470,41 @@ static int lower_expr(struct state *state, struct ast *expr)
fprintf(state->code, "%lld", (long long)int_val(expr));
break;
}
+
+ case AST_NIL: {
+ fprintf(state->code, "NULL");
+ break;
+ }
+
+ case AST_CONSTRUCT: {
+ char *name = lower_type_str(state, expr->scope, expr->t);
+ fprintf(state->code, "(%s){", name);
+ free(name);
+
+ foreach_node(n, construct_members(expr)) {
+ fprintf(state->code, ".%s = ", construction_id(n));
+
+ if (lower_expr(state, construction_expr(n)))
+ return -1;
+
+ fprintf(state->code, ", ");
+ }
+ fprintf(state->code, "}");
+ break;
+ }
+
+ case AST_AS: {
+ char *name = lower_type_str(state, expr->scope, expr->t);
+ fprintf(state->code, "(%s)(", name);
+ free(name);
+
+ if (lower_expr(state, as_expr(expr)))
+ return -1;
+
+ fprintf(state->code, ")");
+ break;
+ }
+
default:
internal_error("unhandled expr lowering: %s", ast_str(expr->k));
abort();
@@ -445,7 +544,7 @@ static int lower_call(struct state *state, struct ast *call, bool last)
foreach_node(a, call_args(call)) {
returning |= a->k == AST_CLOSURE;
- char *type = lower_type_str(a->t);
+ char *type = lower_type_str(state, a->scope, a->t);
fprintf(ctx, " %s a%zu;\n", type, idx);
free(type);
@@ -508,11 +607,72 @@ static int lower_if(struct state *state, struct ast *stmt, bool last)
return lower_block(state, if_else(stmt), last);
}
+static int lower_explode(struct state *state, struct ast *stmt, bool last)
+{
+ /* not significant to us */
+ (void)last;
+
+ int u = uniq(state);
+ struct ast *expr = explode_expr(stmt);
+
+ char *type = lower_type_str(state, expr->scope, expr->t);
+
+ indent(state);
+ fprintf(state->code, "%s explode_%d = ", type, u);
+ free(type);
+
+ if (lower_expr(state, expr))
+ return -1;
+
+ fprintf(state->code, ";\n");
+
+ foreach_node(node, explode_deconstruct(stmt)) {
+ struct ast *var = deconstruction_var(node);
+
+ char *type = lower_type_str(state, var->scope, var->t);
+ char *name = mangle2(var);
+
+ indent(state);
+ fprintf(state->code, "%s %s = explode_%d.%s;\n",
+ type, name, u, deconstruction_id(node));
+
+ free(type);
+ free(name);
+ }
+
+ return 0;
+}
+
+static int lower_let(struct state *state, struct ast *stmt, bool last)
+{
+ /* not relevant */
+ (void)last;
+
+ struct ast *var = let_var(stmt);
+ char *type = lower_type_str(state, var->scope, var->t);
+ char *name = mangle2(var);
+
+ indent(state);
+ fprintf(state->code, "%s %s = ", type, name);
+
+ free(type);
+ free(name);
+
+ if (lower_expr(state, let_expr(stmt)))
+ return -1;
+
+ fprintf(state->code, ";\n");
+ return 0;
+}
+
static int lower_stmt(struct state *state, struct ast *stmt, bool last)
{
switch (stmt->k) {
case AST_CALL: return lower_call(state, stmt, last);
case AST_IF: return lower_if(state, stmt, last);
+ case AST_EXPLODE: return lower_explode(state, stmt, last);
+ case AST_LET: return lower_let(state, stmt, last);
+ case AST_EMPTY: break;
default:
internal_error("unhandled statement kind %s", ast_str(stmt->k));
abort();
@@ -548,7 +708,7 @@ static int lower_block(struct state *state, struct ast *block, bool last)
static int lower_param(struct state *state, struct ast *param)
{
- char *type = lower_type_str(param->t);
+ char *type = lower_type_str(state, param->scope, param->t);
char *name = mangle2(param);
fprintf(state->ctx, " %s %s;\n", type, name);
@@ -644,7 +804,7 @@ static int lower_extern_proc(struct state *state, struct ast *proc)
idx + 1, fwd_typeid(p->t), fwd_typeparam(p->t), idx);
- char *type_str = lower_type_str(p->t);
+ char *type_str = lower_type_str(state, p->scope, p->t);
fprintf(new_state.ctx, " %s a%zu;\n", type_str, idx);
free(type_str);
idx++;
@@ -679,7 +839,7 @@ static int lower_extern_proc(struct state *state, struct ast *proc)
static int lower_param_copy(struct state *state, struct ast *param, FILE *f, size_t idx)
{
- char *type = lower_type_str(param->t);
+ char *type = lower_type_str(state, param->scope, param->t);
fprintf(f, " %s a%zu;\n", type, idx);
free(type);
@@ -716,6 +876,45 @@ static int lower_param_copies(struct state *state, struct ast *params)
return 0;
}
+static int lower_type_def(struct state *state, struct ast *type)
+{
+ assert(type->k == AST_STRUCT_DEF);
+ if (type_lowered(type))
+ return 0;
+
+ ast_flags(type, AST_FLAG_LOWERED);
+
+ char *decl_buf = NULL; size_t decl_len = 0;
+ FILE *decl = open_memstream(&decl_buf, &decl_len);
+ assert(decl);
+
+ char *name = mangle2(type);
+ fprintf(decl, "struct %s;\n", name);
+ fclose(decl);
+
+ char *defn_buf = NULL; size_t defn_len = 0;
+ FILE *defn = open_memstream(&defn_buf, &defn_len);
+ assert(defn);
+
+ fprintf(defn, "typedef struct %s {\n", name);
+
+ foreach_node(n, struct_body(type)) {
+ assert(n->k == AST_VAR_DEF);
+ char *t = lower_type_str(state, n->scope, var_type(n));
+ fprintf(defn, "\t%s %s;\n", t, var_id(n));
+ free(t);
+ }
+
+ fprintf(defn, "} %s;\n", name);
+
+ fclose(defn);
+ free(name);
+
+ add_type(state, decl_buf);
+ add_type(state, defn_buf);
+ return 0;
+}
+
static int lower_proc(struct state *state, struct ast *proc)
{
if (proc_lowered(state, proc))
diff --git a/src/move.c b/src/move.c
index 9e8cfa0..2a55d3d 100644
--- a/src/move.c
+++ b/src/move.c
@@ -418,6 +418,7 @@ static int mvcheck_if(struct state *state, struct ast *node)
return -1;
}
+ if (if_else(node))
if (mvcheck(&else_state, if_else(node))) {
destroy_state(&body_state);
destroy_state(&else_state);
@@ -480,6 +481,46 @@ static int mvcheck_statements(struct state *state, struct ast *nodes)
return 0;
}
+static int mvcheck_construct(struct state *state, struct ast *node)
+{
+ foreach_node(expr, construct_members(node)) {
+ if (mvcheck(state, expr))
+ return -1;
+ }
+
+ return 0;
+}
+
+static int mvcheck_construction(struct state *state, struct ast *node)
+{
+ return mvcheck(state, construction_expr(node));
+}
+
+static int mvcheck_as(struct state *state, struct ast *as)
+{
+ return mvcheck(state, as_expr(as));
+}
+
+static int mvcheck_forget(struct state *state, struct ast *node)
+{
+ struct ast *def = file_scope_find_symbol(node->scope, forget_id(node));
+ assert(def);
+
+ /* act as if a move has happened */
+ insert_move(state, def, node);
+ return 0;
+}
+
+static int mvcheck_explode(struct state *state, struct ast *node)
+{
+ return mvcheck(state, explode_expr(node));
+}
+
+static int mvcheck_write(struct state *state, struct ast *node)
+{
+ return mvcheck(state, write_src(node));
+}
+
static int mvcheck(struct state *state, struct ast *node)
{
if (is_unop(node))
@@ -502,12 +543,22 @@ static int mvcheck(struct state *state, struct ast *node)
case AST_IF: return mvcheck_if (state, node);
case AST_REF: return mvcheck_ref (state, node);
case AST_DEREF: return mvcheck_deref (state, node);
+ case AST_CONSTRUCT: return mvcheck_construct (state, node);
+ case AST_AS: return mvcheck_as (state, node);
+ case AST_FORGET: return mvcheck_forget (state, node);
+ case AST_EXPLODE: return mvcheck_explode (state, node);
+ case AST_WRITE: return mvcheck_write (state, node);
+ case AST_CONSTRUCTION: return mvcheck_construction(state, node);
+ case AST_NIL_CHECK:
+ case AST_SIZEOF:
+ case AST_STRUCT_DEF:
case AST_EMPTY:
case AST_IMPORT:
case AST_CONST_INT:
case AST_CONST_STR:
case AST_CONST_FLOAT:
case AST_CONST_CHAR:
+ case AST_NIL:
return 0;
default: break;
}
diff --git a/src/parser.y b/src/parser.y
index 023c10a..9607e71 100644
--- a/src/parser.y
+++ b/src/parser.y
@@ -101,7 +101,7 @@
%nterm <node> exprs statements closures trailing_closure
%nterm <node> opt_exprs opt_statements opt_trailing_closure
%nterm <node> rev_exprs rev_closures rev_statements
-%nterm <node> let if nil unop binop construct import
+%nterm <node> let put if nil unop binop construct import
%nterm <node> opt_vars vars rev_vars var
%nterm <node> opt_params params rev_params param
@@ -113,12 +113,12 @@
%nterm <node> opt_elements elements rev_elements element
%nterm <node> instance template supertemplate
%nterm <node> opt_constructions constructions construction
-%nterm <node> id impl forget assign
+%nterm <node> opt_deconstructions deconstructions deconstruction
+%nterm <node> id impl forget
+%nterm <node> explode
%nterm <type> type rev_types types opt_types
-
-
%{
/** Modifies the signature of yylex to fit our parser better. */
@@ -262,10 +262,6 @@ unop
type
: ID { $$ = tgen_id($1, src_loc(@$)); }
| "(" opt_types ")" { $$ = tgen_closure($2, src_loc(@$)); }
- | "[" "]" type {
- /* TODO static sizes? */
- $$ = tgen_arr($3, NULL, src_loc(@$));
- }
| "&&" type {
if ($2->k == TYPE_CLOSURE) {
$$ = $2; $$->k = TYPE_PURE_CLOSURE;
@@ -315,8 +311,21 @@ opt_constructions
: constructions
| { $$ = NULL; }
+deconstruction
+ : ID "=>" var {
+ $$ = gen_deconstruction($1, $3, src_loc(@$));
+ }
+
+deconstructions
+ : deconstructions "," deconstruction { $$ = $3; $$->n = $1; }
+ | deconstruction
+
+opt_deconstructions
+ : deconstructions
+ | { $$ = NULL; }
+
construct
- : "(" opt_constructions ")" ID {
+ : "[" opt_constructions "]" ID {
$$ = gen_construct($4, $2, src_loc(@$));
}
@@ -325,8 +334,6 @@ id
expr
: "sizeof" "(" type ")" { $$ = gen_sizeof($3, src_loc(@$)); }
- | expr "." ID { $$ = gen_dot($3, $1, src_loc(@$)); }
- | expr "[" expr "]" { $$ = gen_arr($1, $3, src_loc(@$)); }
| STRING { $$ = gen_const_str(strip($1), src_loc(@$)); }
| FLOAT { $$ = gen_const_float($1, src_loc(@$)); }
| BOOL { $$ = gen_const_bool($1, src_loc(@$)); }
@@ -403,8 +410,18 @@ call
$$ = gen_call($[expr], $[opt_exprs], src_loc(@$));
}
+put
+ : put "*" { $$ = gen_put($1, src_loc(@$)); }
+ | id "*" { $$ = gen_put($1, src_loc(@$)); }
+
let
: expr "=>" var ";" { $$ = gen_let($3, $1, src_loc(@$)); }
+ | expr "=>" put ";" { $$ = gen_write($3, $1, src_loc(@$)); }
+
+explode
+ : expr "=>" "[" opt_deconstructions "]" {
+ $$ = gen_explode($4, $1, src_loc(@$));
+ }
if
: "if" expr body { $$ = gen_if($2, $3, NULL, src_loc(@$)); }
@@ -413,6 +430,8 @@ if
nil
: "nil" expr body "=>" var ";" {
+ /** @todo should nil check define new scope for created
+ * variable? */
$$ = gen_nil_check($2, $3, $5, src_loc(@$));
}
@@ -421,16 +440,11 @@ forget
$$ = gen_forget($2, src_loc(@$));
}
-assign
- : expr "=" expr ";" {
- $$ = gen_assign($1, $3, src_loc(@$));
- }
-
statement
: call
| forget
- | assign
| body
+ | explode
| let
| nil
| if