diff options
| -rw-r--r-- | TODO | 27 | ||||
| -rw-r--r-- | examples/ptrs.fwd | 7 | ||||
| -rw-r--r-- | examples/vec.fwd | 150 | ||||
| -rw-r--r-- | include/fwd/ast.h | 61 | ||||
| -rw-r--r-- | mod/mem.c | 12 | ||||
| -rw-r--r-- | src/analyze.c | 240 | ||||
| -rw-r--r-- | src/ast.c | 16 | ||||
| -rw-r--r-- | src/debug.c | 10 | ||||
| -rw-r--r-- | src/lower.c | 221 | ||||
| -rw-r--r-- | src/move.c | 51 | ||||
| -rw-r--r-- | src/parser.y | 48 |
11 files changed, 628 insertions, 215 deletions
@@ -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) @@ -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) { @@ -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)) @@ -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 |
