diff options
-rw-r--r-- | examples/fib.fwd | 10 | ||||
-rw-r--r-- | examples/guard.fwd | 14 | ||||
-rw-r--r-- | examples/opt_group.fwd | 14 | ||||
-rw-r--r-- | examples/uniq.fwd | 3 | ||||
-rw-r--r-- | include/fwd/ast.h | 10 | ||||
-rw-r--r-- | include/fwd/conts.h | 8 | ||||
-rw-r--r-- | include/fwd/sptree.h | 10 | ||||
-rw-r--r-- | lib/fwdlib.hpp | 5 | ||||
-rw-r--r-- | src/analyze.c | 31 | ||||
-rw-r--r-- | src/ast.c | 14 | ||||
-rw-r--r-- | src/debug.c | 3 | ||||
-rw-r--r-- | src/lexer.l | 1 | ||||
-rw-r--r-- | src/move.c | 191 | ||||
-rw-r--r-- | src/parser.y | 4 |
14 files changed, 277 insertions, 41 deletions
diff --git a/examples/fib.fwd b/examples/fib.fwd index be2b63f..e5014af 100644 --- a/examples/fib.fwd +++ b/examples/fib.fwd @@ -5,17 +5,19 @@ fib(int n, (int) res) { - if n < 2 { + fwd_copy(n) => int n1, int n2; + if n1 < 2 { res(1); } else { - fib(n - 1) => int f1; - fib(n - 2) => int f2; + fib(n2 - 1) => int f1; + fib(n2 - 2) => int f2; res(f1 + f2); } } /* 'extern' println */ -fwd_println(int n); +fwd_println(auto n); +fwd_copy(auto n, (auto, auto) n1); main() { diff --git a/examples/guard.fwd b/examples/guard.fwd index cf9578e..df42ef3 100644 --- a/examples/guard.fwd +++ b/examples/guard.fwd @@ -2,6 +2,12 @@ * eventually be replaced but this is good enough to play around with */ fwd_println(auto s); +/* this is a hack, just creates two copies of whatever is passed to it. + * Primitive types should probably be excluded from the move checking, and more + * complex types would presumably implement some kind of .copy(), but at the + * moment that's still TODO */ +fwd_copy(auto n, (auto, auto) n1); + guard(bool cond, () err, () ok) { if cond { @@ -13,15 +19,17 @@ guard(bool cond, () err, () ok) try_print_one(int a) { - guard(a < 1) => { + fwd_copy(a) => int a1, int a2; + guard(a1 < 1) => { fwd_println("smaller than 1"); } => ; - guard(a > 1) => { + fwd_copy(a2) => int a3, int a4; + guard(a3 > 1) => { fwd_println("larger than 1"); } => ; - fwd_println(a); + fwd_println(a4); } main() diff --git a/examples/opt_group.fwd b/examples/opt_group.fwd new file mode 100644 index 0000000..e520c70 --- /dev/null +++ b/examples/opt_group.fwd @@ -0,0 +1,14 @@ +do_something(() a | () b) +{ + a(); + + /* should fail, since either a or b should be called, but not both */ + b(); +} + +main() +{ + do_something() + => {} + => {} +} diff --git a/examples/uniq.fwd b/examples/uniq.fwd index 9eb0092..c1edc1b 100644 --- a/examples/uniq.fwd +++ b/examples/uniq.fwd @@ -5,8 +5,7 @@ fwd_getline( (optional![string]) next); fwd_some(optional![string] o, - (string) something, - () nothing); + (string) something | () nothing); fwd_insert(unordered_set![string] set, string line, (unordered_set![string]) next); diff --git a/include/fwd/ast.h b/include/fwd/ast.h index 1da5b88..f8ad67a 100644 --- a/include/fwd/ast.h +++ b/include/fwd/ast.h @@ -47,6 +47,9 @@ struct type { /* next */ struct type *n; + /* opt group */ + long group; + struct src_loc loc; }; @@ -141,6 +144,10 @@ struct ast { struct type *t; enum ast_flag flags; + + /* optional group */ + struct ast *ol; + struct ast *or; }; static inline bool ast_flags(struct ast *node, enum ast_flag flags) @@ -325,7 +332,6 @@ static inline bool is_const(struct ast *x) #define var_id(x) return_s(x, AST_VAR_DEF) #define var_type(x) return_t2(x, AST_VAR_DEF) -#define var_init(x) return_a0(x, AST_VAR_DEF) #define gen_var(id, type, loc) \ gen_ast(AST_VAR_DEF, NULL, NULL, NULL, NULL, type, id, 0, 0., loc) @@ -467,4 +473,6 @@ static inline struct type *callable_types(struct type *t) return tcallable_args(t); } +void opt_group(struct ast *a, struct ast *b); + #endif /* AST_H */ diff --git a/include/fwd/conts.h b/include/fwd/conts.h index 862bd31..c1e4d24 100644 --- a/include/fwd/conts.h +++ b/include/fwd/conts.h @@ -7,8 +7,8 @@ #define CONTAINER_OF(ptr, type, member) \ (type *)((char *)(ptr) - offsetof(type, member)) -#define foreach(name, i, s)\ - for (auto i = CONTS_JOIN(name, begin)(s);\ - !CONTS_JOIN(name, end)(s, i);\ - i = CONTS_JOIN(name, next)(i)) +#define foreach(name, i, s) \ + for (auto i = CONTS_JOIN(name, begin)(s); \ + !CONTS_JOIN(name, end)(s, i); \ + i = CONTS_JOIN(name, next)(i)) #endif /* CONTS_H */ diff --git a/include/fwd/sptree.h b/include/fwd/sptree.h index 75b88fc..e305c0f 100644 --- a/include/fwd/sptree.h +++ b/include/fwd/sptree.h @@ -400,12 +400,18 @@ static inline void SPTREE(remove)(struct SPROOT *s, SPTREE_TYPE data) return; SPTREE(remove_found)(s, found); + struct SPNODE *del = CONTAINER_OF(found, struct SPNODE, data); + free(del); } static inline void SPTREE(destroy)(struct SPROOT *s) { - while (s->root) - SPTREE(remove_found)(s, &s->root->data); + while (s->root) { + SPTREE_TYPE *top = &s->root->data; + SPTREE(remove_found)(s, top); + struct SPNODE *del = CONTAINER_OF(top, struct SPNODE, data); + free(del); + } } #undef SPTREE diff --git a/lib/fwdlib.hpp b/lib/fwdlib.hpp index 5c52f65..bb5084c 100644 --- a/lib/fwdlib.hpp +++ b/lib/fwdlib.hpp @@ -35,6 +35,11 @@ static void fwd_some(auto option, auto ok, auto fail) return fail(); } +static void fwd_copy(auto n, auto next) +{ + return next(n, n); +} + static void fwd_insert(auto container, auto elem, auto next) { container.insert(std::move(elem)); diff --git a/src/analyze.c b/src/analyze.c index 077e7d8..ab08f01 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -2,6 +2,7 @@ /* Copyright 2024 Kim Kuparinen < kimi.h.kuparinen@gmail.com > */ #include <fwd/analyze.h> +#include <stdlib.h> #include <string.h> #include <assert.h> @@ -41,7 +42,17 @@ static int analyze_proc(struct state *state, struct scope *scope, return -1; } + size_t group = 0; foreach_node(param, proc_params(node)) { + if (param->ol == NULL && param->or == NULL) + group++; /* no opt group */ + + if (param->ol == NULL && param->or) + group++; /* start of new group */ + + /* otherwise same or ending group, don't increment */ + + param->t->group = group; type_append(&callable->t0, clone_type(param->t)); } @@ -113,6 +124,7 @@ static int analyze_comparison(struct state *state, struct scope *scope, node->t = tgen_id(tf, node->loc); if (!node->t) { internal_error("failed allocating comparison bool type"); + free(tf); return -1; } @@ -132,7 +144,12 @@ static int analyze_block(struct state *state, struct scope *scope, if (analyze_list(state, block_scope, block_body(node))) return -1; - node->t = ast_last(block_body(node))->t; + struct ast *last = ast_last(block_body(node)); + if (last) + node->t = last->t; + else + node->t = tgen_void(node->loc); + return 0; } @@ -179,7 +196,8 @@ static int analyze_init(struct state *state, struct scope *scope, /** @todo check that all parameters match, though that requires that the * type is defined in fwd and not in C++, so this might have to wait a * bit */ - node->t = tgen_construct(init_id(node), init_args(node), node->loc); + node->t = tgen_construct(strdup(init_id(node)), init_args(node), + node->loc); if (!node->t) { internal_error("failed allocating type for construct"); return -1; @@ -220,6 +238,8 @@ static int analyze_call(struct state *state, struct scope *scope, return -1; } + /* clone group info */ + arg->t->group = type->group; type = type->n; } @@ -305,6 +325,7 @@ static int analyze_int(struct state *state, struct scope *scope, node->t = tgen_id(i, node->loc); if (!node->t) { internal_error("failed allocating constant int type"); + free(i); return -1; } @@ -324,6 +345,7 @@ static int analyze_str(struct state *state, struct scope *scope, struct type *ch = tgen_id(i, node->loc); if (!ch) { internal_error("failed allocating constant char type"); + free(i); return -1; } @@ -417,6 +439,11 @@ static int analyze(struct state *state, struct scope *scope, struct ast *node) break; case AST_CONST_STR: ret = analyze_str (state, scope, node); break; + + case AST_EMPTY: + node->t = tgen_void(node->loc); + ret = 0; + break; default: internal_error("missing ast analysis for %s", ast_str(node->k)); return -1; @@ -296,6 +296,8 @@ struct ast *clone_ast(struct ast *n) if (n->a3) new->a3 = clone_ast_list(n->a3); + /** @todo rebuild opt group? */ + return new; } @@ -331,6 +333,7 @@ struct type *clone_type(struct type *type) if (type->t0 && !(new->t0 = clone_type_list(type->t0))) return NULL; + new->group = type->group; return new; } @@ -542,7 +545,10 @@ void fix_closures(struct ast *root) } struct ast *next = root->n; - struct ast *block = gen_block(next, next->loc); + if (!next) + next = gen_empty(root->loc); + + struct ast *block = gen_block(next, root->loc); closure_body(arg) = block; root->n = NULL; root = next; @@ -648,3 +654,9 @@ const char *ast_str(enum ast_kind k) return "UNKNOWN"; } + +void opt_group(struct ast *a, struct ast *b) +{ + a->or = b; + b->ol = a; +} diff --git a/src/debug.c b/src/debug.c index f72fe9b..e65919b 100644 --- a/src/debug.c +++ b/src/debug.c @@ -154,6 +154,9 @@ static void _type_str(FILE *f, struct type *t); static void _type_list_str(FILE *f, struct type *types) { + if (!types) + return; + _type_str(f, types); foreach_type(type, types->n) { diff --git a/src/lexer.l b/src/lexer.l index a150798..ccae93e 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -110,6 +110,7 @@ STRING \"(\\.|[^"\\])*\" "'" {return SQUOTE;} "&" {return AND;} +"|" {return BAR;} "~" {return TILDE;} "=" {return TO;} @@ -47,18 +47,23 @@ static void insert_move(struct state *state, struct ast *def, struct ast *use) moved_insert(&state->moved, pair); } +static void merge(struct state *to, struct state *from) +{ + if (moved_len(&from->moved) == 0) + return; + + foreach(moved, n, &from->moved) { + moved_insert(&to->moved, *n); + } +} + static void push_up(struct state *state) { struct state *parent = state->parent; if (!parent) return; - if (moved_len(&state->moved) == 0) - return; - - foreach(moved, n, &state->moved) { - moved_insert(&parent->moved, *n); - } + merge(parent, state); } static int mvcheck(struct state *state, struct ast *node); @@ -82,19 +87,42 @@ static int mvcheck_block(struct state *state, struct ast *node) return mvcheck_list(state, block_body(node)); } -static int mvcheck_list(struct state *state, struct ast *nodes) +static int mvcheck_call(struct state *state, struct ast *node) { - foreach_node(node, nodes) { - if (mvcheck(state, node)) - return -1; - } + if (mvcheck(state, call_expr(node))) + return -1; - return 0; -} + struct ast *args = call_args(node); + if (!args) + return 0; -static int mvcheck_call(struct state *state, struct ast *node) -{ - return mvcheck_list(state, call_args(node)); + int ret = 0; + long prev_group = args->t->group; + struct state group_state = create_state(state); + + foreach_node(arg, call_args(node)) { + struct state arg_state = create_state(state); + ret = mvcheck(&arg_state, arg); + + long group = arg->t->group; + if (prev_group != group) { + /* previous group ended, push states up */ + push_up(&group_state); + + /* something like reset_state would maybe me more clear? */ + destroy_state(&group_state); + group_state = create_state(state); + } + + merge(&group_state, &arg_state); + destroy_state(&arg_state); + prev_group = group; + if (ret) + break; + } + + destroy_state(&group_state); + return ret; } static int mvcheck_closure(struct state *state, struct ast *node) @@ -107,29 +135,148 @@ static int mvcheck_closure(struct state *state, struct ast *node) return ret; } +static void opt_group_left(struct state *state, struct ast *def, + struct ast *node) +{ + if (!def) + return; + + insert_move(state, def, node); + opt_group_left(state, def->ol, node); +} + +static void opt_group_right(struct state *state, struct ast *def, + struct ast *node) +{ + if (!def) + return; + + insert_move(state, def, node); + opt_group_right(state, def->or, node); +} + static int mvcheck_id(struct state *state, struct ast *node) { struct ast *def = file_scope_find_symbol(node->scope, id_str(node)); assert(def); + /* moves only apply to variables, functions are 'eternal' */ + if (def->k != AST_VAR_DEF) + return 0; + struct ast_pair *prev = find_move(state, def); if (prev) { + /* error messages for opt groups could be improved */ move_error(node, prev->use); return -1; } insert_move(state, def, node); + + if (def->or || def->ol) { + /* we're part of an opt group, add all other members as if they + * were moved as well */ + opt_group_left(state, def->ol, node); + opt_group_right(state, def->or, node); + } + + return 0; +} + +static int mvcheck_let(struct state *state, struct ast *node) +{ + return mvcheck(state, let_expr(node)); +} + +static int mvcheck_init(struct state *state, struct ast *node) +{ + return mvcheck_list(state, init_body(node)); +} + +static int mvcheck_if(struct state *state, struct ast *node) +{ + if (mvcheck(state, if_cond(node))) + return -1; + + struct state body_state = create_state(state); + struct state else_state = create_state(state); + + if (mvcheck(&body_state, if_body(node))) { + destroy_state(&body_state); + destroy_state(&else_state); + return -1; + } + + if (mvcheck(&else_state, if_else(node))) { + destroy_state(&body_state); + destroy_state(&else_state); + return -1; + } + + push_up(&body_state); + push_up(&else_state); + + destroy_state(&body_state); + destroy_state(&else_state); + return 0; +} + +static int mvcheck_unop(struct state *state, struct ast *node) +{ + return mvcheck(state, unop_expr(node)); +} + +static int mvcheck_binop(struct state *state, struct ast *node) +{ + if (mvcheck(state, binop_left(node))) + return -1; + + return mvcheck(state, binop_right(node)); +} + +static int mvcheck_comparison(struct state *state, struct ast *node) +{ + if (mvcheck(state, comparison_left(node))) + return -1; + + return mvcheck(state, comparison_right(node)); +} + +static int mvcheck_list(struct state *state, struct ast *nodes) +{ + foreach_node(node, nodes) { + if (mvcheck(state, node)) + return -1; + } + return 0; } static int mvcheck(struct state *state, struct ast *node) { + if (is_unop(node)) + return mvcheck_unop(state, node); + + if (is_binop(node)) + return mvcheck_binop(state, node); + + if (is_comparison(node)) + return mvcheck_comparison(state, node); + switch (node->k) { - case AST_PROC_DEF: return mvcheck_proc (state, node); - case AST_BLOCK: return mvcheck_block (state, node); - case AST_CALL: return mvcheck_call (state, node); - case AST_CLOSURE: return mvcheck_closure (state, node); - case AST_ID: return mvcheck_id (state, node); + case AST_PROC_DEF: return mvcheck_proc (state, node); + case AST_BLOCK: return mvcheck_block (state, node); + case AST_CALL: return mvcheck_call (state, node); + case AST_CLOSURE: return mvcheck_closure (state, node); + case AST_LET: return mvcheck_let (state, node); + case AST_ID: return mvcheck_id (state, node); + case AST_INIT: return mvcheck_init (state, node); + case AST_IF: return mvcheck_if (state, node); + case AST_CONST_INT: + case AST_CONST_STR: + case AST_CONST_FLOAT: + case AST_CONST_CHAR: + return 0; default: break; } @@ -143,6 +290,8 @@ int mvcheck_root(struct ast *root) struct state state = create_state(NULL); if (mvcheck(&state, node)) return -1; + + destroy_state(&state); } return 0; diff --git a/src/parser.y b/src/parser.y index 9f532b9..dd177d0 100644 --- a/src/parser.y +++ b/src/parser.y @@ -53,6 +53,7 @@ %token PLUS "+" %token XOR "^" %token AND "&" +%token BAR "|" %token TILDE "~" %token LT "<" %token GT ">" @@ -183,6 +184,7 @@ var rev_vars : rev_vars "," var { $$ = $3; $$->n = $1; } + | rev_vars "|" var { $$ = $3; $$->n = $1; opt_group($1, $3); } | var vars @@ -230,7 +232,7 @@ type | "*" type { $$ = tgen_ptr($2, src_loc(@$)); } rev_types - : rev_types "," type { $$ = $3; $3->n = $$; } + : rev_types "," type { $$ = $3; $$->n = $1; } | type types |