diff options
| author | Kimplul <kimi.h.kuparinen@gmail.com> | 2025-01-06 01:05:21 +0200 | 
|---|---|---|
| committer | Kimplul <kimi.h.kuparinen@gmail.com> | 2025-01-06 01:05:57 +0200 | 
| commit | 89bac537165bf262594cca343cb45e16a2167145 (patch) | |
| tree | f72a80f1624b12fa3b27c6dc6feedc3e06594e20 | |
| parent | aec19e55ca32f68536a550f100d3f058b8a93c02 (diff) | |
| download | fwd-89bac537165bf262594cca343cb45e16a2167145.tar.gz fwd-89bac537165bf262594cca343cb45e16a2167145.zip | |
implement move checking furthermvcheck
+ Enough that examples still compile, but missing references etc.
| -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 | 
