~lbnz/xr0

1a3fffd4bab6e05a3574af26fc6ead6ce8a0374e — Amisi Kiarie 2 months ago dd87f7c chore/refactor-lex v0.13.0
feat: refine splitting logic; parse.x working

issue: https://github.com/xr0-org/xr0/issues/37
44 files changed, 807 insertions(+), 704 deletions(-)

M Makefile
M include/ast.h
M include/lex.h
M include/state.h
M include/util.h
M src/ast/expr/expr.c
M src/ast/expr/verify.c
M src/ast/function/function.c
M src/ast/lex.l
M src/ast/stmt/stmt.c
M src/ast/stmt/stmt.h
M src/ast/stmt/verify.c
M src/ast/type/type.c
M src/main.c
M src/state/block.c
M src/state/heap.c
M src/state/location.c
M src/state/state.c
M src/state/static.c
M src/util/util.c
M tests/0-basic/130-FAIL-double-free.x.EXPECTED
M tests/0-basic/140-FAIL-sync-free.x.EXPECTED
M tests/0-basic/170-FAIL-double-free.x.EXPECTED
M tests/3-topological/001-valid-sort-matrix.x
M tests/3-topological/002-valid-sort-parse.x
M tests/6-preconditions/100-FAIL-need-alloced-lval.x.EXPECTED
M tests/6-preconditions/101-FAIL-need-alloced-rval.x.EXPECTED
M tests/6-preconditions/102-FAIL-need-clump-lval-freed-ptr.x.EXPECTED
M tests/6-preconditions/103-FAIL-need-clump-lval-assigned-ptr.x.EXPECTED
M tests/7-use-after-free/100-FAIL-lvalue-use-after-free.x.EXPECTED
M tests/7-use-after-free/101-FAIL-rvalue-use-after-free.x.EXPECTED
M tests/7-use-after-free/110-FAIL-freed-ptr-dangling-return.x.EXPECTED
M tests/7-use-after-free/112-FAIL-freed-ptr-dangling-false-claim.x.EXPECTED
M tests/7-use-after-free/120-FAIL-freed-ptr-maybe-dangling-return.x.EXPECTED
M tests/7-use-after-free/200-FAIL-stack-frame-pop-dangling-return.x.EXPECTED
M tests/7-use-after-free/210-FAIL-stack-frame-pop-dangling-assign.x.EXPECTED
M tests/7-use-after-free/300-FAIL-struct-free-ptr-dangling.x.EXPECTED
M tests/8-uninitialised-memory/000-FAIL-uninitialised-memory-rval.x.EXPECTED
M tests/8-uninitialised-memory/001-FAIL-uninitialised-memory-rval.x.EXPECTED
M tests/8-uninitialised-memory/002-FAIL-uninitialised-memory-rval.x.EXPECTED
M tests/8-uninitialised-memory/003-FAIL-uninitialised-memory-rval.x.EXPECTED
M tests/8-uninitialised-memory/004-FAIL-uninitialised-memory-rval.x.EXPECTED
M tests/99-program/100-lex/parse.x
M tests/run
M Makefile => Makefile +5 -5
@@ 89,22 89,22 @@ lex-gen:

lex-leaks: $(XR0V)
	$(VALGRIND) --leak-check=full \
		$(XR0V) -I libx tests/3-program/100-lex/parse.x
		$(XR0V) -I libx tests/99-program/100-lex/parse.x

lex-verbose: $(XR0V) 
	$(VALGRIND) --num-callers=30 \
		$(XR0V) -I libx tests/3-program/100-lex/parse.x
		$(XR0V) -I libx -v tests/99-program/100-lex/parse.x

matrix: $(XR0V)
	$(VALGRIND) $(XR0V) -I libx tests/3-program/000-matrix.x
	$(VALGRIND) $(XR0V) -I libx tests/99-program/000-matrix.x

matrix-leaks: $(XR0V)
	$(VALGRIND) --leak-check=full \
		$(XR0V) -I libx tests/3-program/000-matrix.x
		$(XR0V) -I libx tests/99-program/000-matrix.x

matrix-verbose: $(XR0V) 
	$(VALGRIND) --num-callers=30 \
		$(XR0V) -I libx tests/3-program/000-matrix.x
		$(XR0V) -I libx tests/99-program/000-matrix.x

$(MAIN_0V_OBJ): $(SRC_0V_DIR)/main.c $(OBJECTS)
	@printf 'CC\t$@\n'

M include/ast.h => include/ast.h +3 -3
@@ 217,7 217,7 @@ struct result *
ast_expr_eval(struct ast_expr *, struct state *);

struct result *
ast_expr_absexec(struct ast_expr *, struct state *);
ast_expr_abseval(struct ast_expr *, struct state *);

/* ast_expr_pf_reduce: Reduce an expression to "parameter form", in which its
 * only primitives are constants and parameters (vconsts). */


@@ 409,8 409,8 @@ ast_stmt_verify(struct ast_stmt *, struct state *);
struct error *
ast_stmt_exec(struct ast_stmt *, struct state *);

struct result *
ast_stmt_absexec(struct ast_stmt *stmt, struct state *, bool iscall);
struct error *
ast_stmt_absprocess(struct ast_stmt *stmt, char *fname, struct state *, bool iscall);

struct error *
ast_stmt_setupabsexec(struct ast_stmt *stmt, struct state *);

M include/lex.h => include/lex.h +2 -0
@@ 1,6 1,8 @@
#ifndef XR0_LEX_H
#define XR0_LEX_H

#define XR0_INCLUDES_SEGMENT "libx/"

struct lexememarker {
	int linenum, column;
	char *filename;

M include/state.h => include/state.h +2 -2
@@ 61,10 61,10 @@ state_popframe(struct state *);
void
state_declare(struct state *, struct ast_variable *var, bool isparam);

struct object *
struct object_res
state_getresult(struct state *);

struct object *
struct object_res
state_getobject(struct state *, char *id);

struct ast_type *

M include/util.h => include/util.h +45 -34
@@ 32,40 32,6 @@ void
map_set(struct map *, const char *key, const void *value);


struct strbuilder;

struct strbuilder *
strbuilder_create();

int
strbuilder_printf(struct strbuilder *b, const char *fmt, ...);

int
strbuilder_vprintf(struct strbuilder *b, const char *fmt, va_list ap);

void
strbuilder_putc(struct strbuilder *b, char c);

int
strbuilder_puts(struct strbuilder *b, char *s);

char *
strbuilder_build(struct strbuilder *b);

char *
strbuilder_preview(struct strbuilder *b);

struct error {
	char *msg;
	struct error *inner;
};

struct error *
error_create(char *s);

struct error *
error_prepend(struct error *, char *msg);

/* XXX: string_arr: to be macro */

struct string_arr {


@@ 100,8 66,53 @@ string_arr_contains(struct string_arr *, char *s);
char *
string_arr_str(struct string_arr *);


struct strbuilder;

struct strbuilder *
strbuilder_create();

int
strbuilder_printf(struct strbuilder *b, const char *fmt, ...);

int
strbuilder_vprintf(struct strbuilder *b, const char *fmt, va_list ap);

void
strbuilder_putc(struct strbuilder *b, char c);

int
strbuilder_puts(struct strbuilder *b, char *s);

char *
strbuilder_build(struct strbuilder *b);

char *
strbuilder_preview(struct strbuilder *b);


/* v_printf: Print if Xr0 is in verbose mode. */
int
v_printf(char *fmt, ...);


struct error;

struct error *
error_printf(char *fmt, ...);

struct ast_expr;

struct error *
error_undecideable_cond(struct ast_expr *cond);

struct error *
error_to_undecideable_cond(struct error *);

struct ast_expr *
error_get_undecideable_cond(struct error *);

char *
error_str(struct error *);

#endif

M src/ast/expr/expr.c => src/ast/expr/expr.c +3 -126
@@ 711,13 711,13 @@ ast_expr_alloc_str_build(struct ast_expr *expr, struct strbuilder *b)

	switch (expr->u.alloc.kind) {
	case ALLOC:
		strbuilder_printf(b, ".%s %s;", "malloc", arg);
		strbuilder_printf(b, ".%s(%s)", "malloc", arg);
		break;
	case DEALLOC:
		strbuilder_printf(b, ".%s %s;", "free", arg);
		strbuilder_printf(b, ".%s(%s)", "free", arg);
		break;
	case CLUMP:
		strbuilder_printf(b, ".%s %s;", "clump", arg);
		strbuilder_printf(b, ".%s(%s)", "clump", arg);
		break;
	default:
		assert(false);


@@ 739,8 739,6 @@ ast_expr_alloc_kind(struct ast_expr *expr)
	return expr->u.alloc.kind;
}



void
ast_expr_destroy(struct ast_expr *expr)
{


@@ 1080,127 1078,6 @@ ast_expr_getfuncs(struct ast_expr *expr)
	}
}

static struct ast_stmt_splits
call_splits(struct ast_expr *, struct state *);

static struct ast_stmt_splits
binary_splits(struct ast_expr *, struct state *);

struct ast_stmt_splits
ast_expr_splits(struct ast_expr *e, struct state *s)
{
	switch (ast_expr_kind(e)) {
	case EXPR_CALL:
		return call_splits(e, s);
	case EXPR_ASSIGNMENT:
		return ast_expr_splits(ast_expr_assignment_rval(e), s);
	case EXPR_UNARY:
		return ast_expr_splits(ast_expr_unary_operand(e), s);
	case EXPR_BINARY:
		return binary_splits(e, s);
	case EXPR_INCDEC:
		return ast_expr_splits(ast_expr_incdec_root(e), s);
	case EXPR_STRUCTMEMBER:
		return ast_expr_splits(ast_expr_member_root(e), s);
	case EXPR_CONSTANT:
	case EXPR_IDENTIFIER:
	case EXPR_STRING_LITERAL:
	case EXPR_ARBARG:
	case EXPR_ISDEREFERENCABLE:
	case EXPR_ALLOCATION:
		return (struct ast_stmt_splits) { .n = 0, .cond = NULL, .err = NULL };
	default:
		assert(false);
	}
}

static struct ast_stmt_splits
call_splits(struct ast_expr *expr, struct state *state)
{
	struct error *err;

	struct ast_expr *root = ast_expr_call_root(expr);
	/* TODO: function-valued-expressions */
	char *name = ast_expr_as_identifier(root);

	struct ast_function *f = externals_getfunc(state_getext(state), name);
	if (!f) {
		struct strbuilder *b = strbuilder_create();
		strbuilder_printf(b, "function: `%s' not found", name);
		err = error_create(strbuilder_build(b));
		return (struct ast_stmt_splits) { .n = 0, .cond = NULL, .err = err };
	}

	int nparams = ast_function_nparams(f);
	struct ast_variable **params = ast_function_params(f);

	struct state *s_copy = state_copy(state);
	struct result_arr *args = prepare_arguments(
		ast_expr_call_nargs(expr),
		ast_expr_call_args(expr),
		nparams, params, s_copy
	);

	struct ast_type *ret_type = ast_function_type(f);
	state_pushframe(s_copy, dynamic_str(name), ret_type);

	if ((err = prepare_parameters( nparams, params, args, name, s_copy))) {
		/* Sometimes a param is uninitialised e.g. 
		 *
		 * 	int c;
		 * 	scanf("%d", &c);
		 *
		 * */
		return (struct ast_stmt_splits) { .n = 0, .cond = NULL, .err = err };
	} 
	int n = 0;
	struct ast_expr **cond = NULL;

	struct ast_block *abs = ast_function_abstract(f);

	int ndecls = ast_block_ndecls(abs);
	if (ndecls) {
		struct ast_variable **var = ast_block_decls(abs);
		for (int i = 0; i < ndecls; i++) {
			state_declare(s_copy, var[i], false);
		}
	}

	int nstmts = ast_block_nstmts(abs);
	struct ast_stmt **stmt = ast_block_stmts(abs);
	for (int i = 0; i < nstmts; i++) {
		struct ast_stmt_splits splits = ast_stmt_splits(stmt[i], s_copy);
		for (int j = 0; j < splits.n; j++) {
			cond = realloc(cond, sizeof(struct ast_expr *) * ++n);
			cond[n-1] = splits.cond[j];
		}
		/* XXX: for assignment statements and, well, spare us */
	}

	state_popframe(s_copy);
	result_arr_destroy(args);

	return (struct ast_stmt_splits) { .n = n, .cond = cond, .err = NULL };
}

static struct ast_stmt_splits
binary_splits(struct ast_expr *e, struct state *s)
{
	struct ast_stmt_splits s1 = ast_expr_splits(ast_expr_binary_e1(e), s),
			       s2 = ast_expr_splits(ast_expr_binary_e2(e), s);

	int n = s1.n + s2.n;
	struct ast_expr **cond = malloc(sizeof(struct ast_expr *) * n);
	for (int i = 0; i < s1.n; i++) {
		cond[i] = s1.cond[i];
	}
	for (int i = 0; i < s2.n; i++) {
		cond[i+s1.n] = s2.cond[i];
	}

	return (struct ast_stmt_splits) { .n = n, .cond = cond, .err = NULL };
}

static struct string_arr *
ast_expr_call_getfuncs(struct ast_expr *expr)
{

M src/ast/expr/verify.c => src/ast/expr/verify.c +81 -56
@@ 253,8 253,13 @@ expr_identifier_lvalue(struct ast_expr *expr, struct state *state)
{
	char *id = ast_expr_as_identifier(expr);

	struct object_res res = state_getobject(state, id);
	if (res.err) {
		return (struct lvalue_res) { .err = res.err };
	}

	return (struct lvalue_res) {
		.lval = lvalue_create(state_getobjecttype(state, id), state_getobject(state, id)),
		.lval = lvalue_create(state_getobjecttype(state, id), res.obj),
		.err = NULL
	};
}


@@ 314,6 319,9 @@ expr_structmember_lvalue(struct ast_expr *expr, struct state *state)
{
	struct ast_expr *root = ast_expr_member_root(expr);
	struct lvalue_res root_res = ast_expr_lvalue(root, state);
	if (root_res.err) {
		return (struct lvalue_res) { .err = root_res.err };
	}
	struct object *root_obj = lvalue_object(root_res.lval);
	assert(root_obj);
	char *field = ast_expr_member_field(expr);


@@ 321,11 329,12 @@ expr_structmember_lvalue(struct ast_expr *expr, struct state *state)
		root_obj, lvalue_type(root_res.lval), field, state
	);
	if (!member) {
		/* TODO: lvalue error */
		return (struct lvalue_res) {
			.lval = NULL,
			.err = error_create("lvalue error")
		};
		char *root_str = ast_expr_str(root);
		struct error *e = error_printf(
			"`%s' has no field `%s'", root_str, field
		);
		free(root_str);
		return (struct lvalue_res) { .err = e };
	}
	struct ast_type *t = object_getmembertype(
		root_obj, lvalue_type(root_res.lval), field, state


@@ 450,6 459,7 @@ ast_expr_eval(struct ast_expr *expr, struct state *state)
	case EXPR_ARBARG:
		return arbarg_eval(expr, state);
	default:
		/*printf("expr: %s\n", ast_expr_str(expr));*/
		assert(false);
	}
}


@@ 485,22 495,21 @@ expr_identifier_eval(struct ast_expr *expr, struct state *state)
	}

	char *id = ast_expr_as_identifier(expr);

	/* XXX */
	if (id[0] == '#') {
		return result_value_create(value_literal_create(id));
	}

	struct object *obj = state_getobject(state, id);
	if (!obj) {
		struct strbuilder *b = strbuilder_create();
		strbuilder_printf(b, "unknown idenitfier %s", id);
		return result_error_create(error_create(strbuilder_build(b)));
	struct object_res obj_res = state_getobject(state, id);
	if (obj_res.err) {
		return result_error_create(obj_res.err);
	}
	struct value *val = object_as_value(obj);
	struct value *val = object_as_value(obj_res.obj);
	if (!val) {
		v_printf("state: %s\n", state_str(state));
		struct strbuilder *b = strbuilder_create();
		strbuilder_printf(b, "undefined memory access: %s has no value", id);
		return result_error_create(error_create(strbuilder_build(b)));
		return result_error_create(
			error_printf("undefined memory access: `%s' has no value", id)
		);
	}
	return result_value_create(value_copy(val));
}


@@ 513,7 522,7 @@ hack_identifier_builtin_eval(char *id, struct state *state)
			value_sync_create(ast_expr_identifier_create(dynamic_str(id)))
		);
	}
	return result_error_create(error_create("not built-in"));
	return result_error_create(error_printf("not built-in"));
}

static struct result *


@@ 584,21 593,23 @@ binary_deref_eval(struct ast_expr *expr, struct state *state)
		return result_error_create(deref_res.err);
	}
	if (!deref_res.obj) {
		struct strbuilder *b = strbuilder_create();
		char *s = ast_expr_str(expr);
		strbuilder_printf(b, "undefined indirection: *(%s) has no value", s);
		struct error *e = error_printf(
			"undefined indirection: *(%s) has no value", s
		);
		free(s);
		return result_error_create(error_create(strbuilder_build(b)));
		return result_error_create(e);
	}
	result_destroy(res);

	struct value *v = object_as_value(deref_res.obj);
	if (!v) {
		struct strbuilder *b = strbuilder_create();
		char *s = ast_expr_str(expr);
		strbuilder_printf(b, "undefined indirection: *(%s) has no value", s);
		struct error *e = error_printf(
			"undefined indirection: *(%s) has no value", s
		);
		free(s);
		return result_error_create(error_create(strbuilder_build(b)));
		return result_error_create(e);
	}

	return result_value_create(value_copy(v));


@@ 624,13 635,12 @@ expr_structmember_eval(struct ast_expr *expr, struct state *s)
	char *field = ast_expr_member_field(expr);
	struct object *member = value_struct_member(result_as_value(res), field);
	if (!member) {
		struct strbuilder *b = strbuilder_create();
		char *root_str = ast_expr_str(root);
		strbuilder_printf(
			b, "`%s' has no field `%s'", root_str, field
		struct error *e = error_printf(
			"`%s' has no field `%s'", root_str, field
		);
		free(root_str);
		return result_error_create(error_create(strbuilder_build(b)));
		return result_error_create(e);
	}
	struct value *obj_value = object_as_value(member);
	/* XXX */


@@ 691,19 701,27 @@ expr_call_eval(struct ast_expr *expr, struct state *state)

	struct ast_function *f = externals_getfunc(state_getext(state), name);
	if (!f) {
		struct strbuilder *b = strbuilder_create();
		strbuilder_printf(b, "function `%s' not found", name);
		return result_error_create(error_create(strbuilder_build(b)));
		return result_error_create(error_printf("`%s' not found\n", name));
	}

	int nparams = ast_function_nparams(f);
	struct ast_variable **params = ast_function_params(f);
	struct ast_type *rtype = ast_function_type(f);

	int nargs = ast_expr_call_nargs(expr);
	if (nargs != nparams) {
		return result_error_create(
			error_printf(
				"`%s' given %d arguments instead of %d\n",
				name, nargs, nparams
			)
		);
	}

	struct result_arr *args = prepare_arguments(
		ast_expr_call_nargs(expr),
		ast_expr_call_args(expr),
		nparams, params, state
		nargs, ast_expr_call_args(expr),
		nparams, params,
		state
	);

	state_pushframe(state, dynamic_str(name), rtype);


@@ 713,12 731,20 @@ expr_call_eval(struct ast_expr *expr, struct state *state)

	/* XXX: pass copy so we don't observe */
	if ((err = call_setupverify(f, state_copy(state)))) {
		return result_error_create(err);
		return result_error_create(
			error_printf(
				"`%s' precondition failure\n"
				"\t%w",
				name, err
			)
		);
	}

	struct result *res = call_absexec(expr, state);
	if (result_iserror(res)) {
		return res;
		return result_error_create(
			error_printf("\n\t%w", result_as_error(res))
		);
	}

	/* XXX: pass copy so we don't observe */


@@ 750,9 776,9 @@ call_absexec(struct ast_expr *expr, struct state *s)

	struct ast_function *f = externals_getfunc(state_getext(s), name);
	if (!f) {
		struct strbuilder *b = strbuilder_create();
		strbuilder_printf(b, "function `%s' not found", name);
		return result_error_create(error_create(strbuilder_build(b)));
		return result_error_create(
			error_printf("function `%s' not found", name)
		);
	}
	struct result *res = ast_function_absexec(f, s);
	if (result_iserror(res) || result_hasvalue(res)) {


@@ 787,9 813,9 @@ call_setupverify(struct ast_function *f, struct state *arg_state)
		struct value *param = state_getloc(param_state, id);
		struct value *arg = state_getloc(arg_state, id);
		if ((err = verify_paramspec(param, arg, param_state, arg_state))) {
			struct strbuilder *b = strbuilder_create();
			strbuilder_printf(b, "parameter %s of %s %s", id, fname, err->msg);
			return error_create(strbuilder_build(b));
			return error_printf(
				"parameter `%s' of `%s' %w", id, fname, err
			);
		}
	}
	return NULL;


@@ 803,10 829,10 @@ verify_paramspec(struct value *param, struct value *arg, struct state *param_sta
		return NULL;
	}
	if (!state_islval(arg_state, arg)) {
		return error_create("must be lvalue");
		return error_printf("must be lvalue");
	}
	if (state_isalloc(param_state, param) && !state_isalloc(arg_state, arg)) {
		return error_create("must be heap allocated");
		return error_printf("must be heap allocated");
	}
	struct object_res param_res = state_get(
		param_state, value_as_location(param), false


@@ 826,7 852,7 @@ verify_paramspec(struct value *param, struct value *arg, struct state *param_sta
		return NULL;
	}
	if (!object_hasvalue(arg_res.obj)) {
		return error_create("must be rvalue");
		return error_printf("must be rvalue");
	}
	return verify_paramspec(
		object_as_value(param_res.obj), object_as_value(arg_res.obj),


@@ 934,12 960,10 @@ prepare_parameters(int nparams, struct ast_variable **param,
		}

		if (!result_hasvalue(res)) {
			struct strbuilder *b = strbuilder_create();
			strbuilder_printf(
				b, "parameter `%s' of function `%s' has no value",
			return error_printf(
				"parameter `%s' of `%s' has no value",
				ast_variable_name(param[i]), fname
			);
			return error_create(strbuilder_build(b));
		}

		struct ast_expr *name = ast_expr_identifier_create(


@@ 969,7 993,7 @@ expr_assign_eval(struct ast_expr *expr, struct state *state)
	}
	if (!result_hasvalue(res)) {
		assert(false);
		return result_error_create(error_create("undefined indirection (rvalue)"));
		return result_error_create(error_printf("undefined indirection (rvalue)"));
	}
	struct lvalue_res lval_res = ast_expr_lvalue(lval, state);
	if (lval_res.err) {


@@ 977,11 1001,12 @@ expr_assign_eval(struct ast_expr *expr, struct state *state)
	}
	struct object *obj = lvalue_object(lval_res.lval);
	if (!obj) {
		struct strbuilder *b = strbuilder_create();
		char *s = ast_expr_str(lval);
		strbuilder_printf(b, "undefined indirection: %s is not an lvalue", s);
		struct error *e = error_printf(
			"undefined indirection: %s is not an lvalue", s
		);
		free(s);
		return result_error_create(error_create(strbuilder_build(b)));
		return result_error_create(e);
	}

	object_assign(obj, value_copy(result_as_value(res)));


@@ 1055,7 1080,7 @@ static struct result *
alloc_absexec(struct ast_expr *, struct state *);

struct result *
ast_expr_absexec(struct ast_expr *expr, struct state *state)
ast_expr_abseval(struct ast_expr *expr, struct state *state)
{
	switch (ast_expr_kind(expr)) {
	case EXPR_ASSIGNMENT:


@@ 1125,13 1150,13 @@ assign_absexec(struct ast_expr *expr, struct state *state)
	struct ast_expr *lval = ast_expr_assignment_lval(expr),
			*rval = ast_expr_assignment_rval(expr);

	struct result *res = ast_expr_absexec(rval, state);
	struct result *res = ast_expr_abseval(rval, state);
	if (result_iserror(res)) {
		return res;
	}
	if (!result_hasvalue(res)) {
		assert(false);
		return result_error_create(error_create("undefined indirection (rvalue)"));
		return result_error_create(error_printf("undefined indirection (rvalue)"));
	}
	struct lvalue_res lval_res = ast_expr_lvalue(lval, state);
	if (lval_res.err) {


@@ 1139,7 1164,7 @@ assign_absexec(struct ast_expr *expr, struct state *state)
	}
	struct object *obj = lvalue_object(lval_res.lval);
	if (!obj) {
		return result_error_create(error_create("undefined indirection (lvalue)"));
		return result_error_create(error_printf("undefined indirection (lvalue)"));
	}

	object_assign(obj, value_copy(result_as_value(res)));

M src/ast/function/function.c => src/ast/function/function.c +66 -89
@@ 4,6 4,7 @@
#include <string.h>

#include "ast.h"
#include "lex.h"
#include "function.h"
#include "stmt/stmt.h"
#include "intern.h"


@@ 242,38 243,41 @@ static struct error *
abstract_audit(struct ast_function *f, struct state *actual_state);

static struct error *
split_paths_absverify(struct ast_function *f, struct state *, int index,
		struct ast_stmt_splits *splits);
split_path_absverify(struct ast_function *f, struct state *state, int index,
		struct ast_expr *cond);

static struct error *
path_absverify(struct ast_function *f, struct state *state, int index)
{
	struct error *err = NULL;

	struct ast_block *abs = ast_function_abstract(f);

	char *fname = ast_function_name(f);
	int nstmts = ast_block_nstmts(abs);
	struct ast_stmt **stmt = ast_block_stmts(abs);
	for (int i = index; i < nstmts; i++) {
		struct ast_stmt_splits splits = ast_stmt_splits(stmt[i], state);
		if (splits.err) {
			return splits.err;
		}
		if (splits.n) {
			assert(splits.cond);
			return split_paths_absverify(f, state, i, &splits);
		}
		if (ast_stmt_ispre(stmt[i])) {
			continue;
		}
		struct result *res = ast_stmt_absexec(stmt[i], state, true);
		if (result_iserror(res)) {
			return result_as_error(res);
		struct state *prestate = state_copy(state);
		struct error *err = ast_stmt_absprocess(
			stmt[i], fname, state, true
		);
		if (err) {
			struct error *uc_err = error_to_undecideable_cond(err);
			if (!uc_err) {
				return err;
			}
			return split_path_absverify(
				f, prestate, i,
				error_get_undecideable_cond(uc_err)
			);
		}	
		state_destroy(prestate);
		/* result_destroy(res); */
	}
	
	/* TODO: verify that `result' is of same type as f->result */
	struct error *err = NULL;
	if ((err = abstract_audit(f, state))) {
		return err;
	}


@@ 319,9 323,19 @@ ast_function_precondsinit(struct ast_function *f, struct state *s)
	if (!pre.stmt) {
		return NULL;
	}
	struct result *res = ast_stmt_absexec(pre.stmt, s, true);
	if (result_iserror(res)) {
		return result_as_error(res);
	struct error *err = ast_stmt_absprocess(
		pre.stmt, ast_function_name(f), s, true
	);
	if (err) {
		struct lexememarker *loc = ast_stmt_lexememarker(pre.stmt); 
		assert(loc);
		char *m = lexememarker_str(loc);
		struct error *e = error_printf(
			"%s:%s: %w", 
			m, ast_function_name(f), err
		);
		free(m);
		return e;
	}
	return NULL;
}


@@ 332,9 346,9 @@ inititalise_param(struct ast_variable *param, struct state *state)
	char *name = ast_variable_name(param);
	struct ast_type *t = ast_variable_type(param);

	struct object *obj = state_getobject(state, name);
	assert(obj);
	if (object_hasvalue(obj)) {
	struct object_res res = state_getobject(state, name);
	assert(!res.err);
	if (object_hasvalue(res.obj)) {
		/* must on the clump or heap */
		//struct value *val = object_as_value(obj);	
		//struct location *loc = value_as_location(val);


@@ 345,7 359,7 @@ inititalise_param(struct ast_variable *param, struct state *state)
	} else {
		/* variables that aren't talked about by the preconditions */
		struct value *val = state_vconst(state, t, dynamic_str(name), true);
		object_assign(obj, val);
		object_assign(res.obj, val);
	}
	return NULL;
}


@@ 364,7 378,7 @@ abstract_auditwithstate(struct ast_function *f, struct state *actual_state,
static struct error *
abstract_audit(struct ast_function *f, struct state *abstract_state)
{
	struct error *err;
	struct error *err = NULL;

	struct state *actual_state = state_create_withprops(
		dynamic_str(ast_function_name(f)),


@@ 399,8 413,6 @@ ast_function_setupabsexec(struct ast_function *f, struct state *state)
	return NULL;
}



static struct error *
abstract_auditwithstate(struct ast_function *f, struct state *actual_state,
		struct state *abstract_state)


@@ 414,23 426,6 @@ abstract_auditwithstate(struct ast_function *f, struct state *actual_state,
	return path_verify(f, actual_state, 0, abstract_state);
}

static struct error *
split_path_absverify(struct ast_function *f, struct state *state, int index,
		struct ast_expr *cond);

static struct error *
split_paths_absverify(struct ast_function *f, struct state *state, int index,
		struct ast_stmt_splits *splits)
{
	struct error *err;
	for (int i = 0; i < splits->n; i++) {
		if ((err = split_path_absverify(f, state, index, splits->cond[i]))) {
			return err;
		}
	}
	return NULL;
}

static struct ast_function_arr *
abstract_paths(struct ast_function *f, int index, struct ast_expr *cond);



@@ 467,30 462,33 @@ split_path_absverify(struct ast_function *f, struct state *state, int index, str
}

static struct error *
split_paths_verify(struct ast_function *f, struct state *actual_state,
		int index, struct ast_stmt_splits *splits, struct state *abstract_state);
split_path_verify(struct ast_function *f, struct state *actual_state,
		int index, struct ast_expr *cond, struct state *abstract_state);

static struct error *
path_verify(struct ast_function *f, struct state *actual_state, int index,
		struct state *abstract_state)
{
	struct error *err;

	char *fname = ast_function_name(f);
	int nstmts = ast_block_nstmts(f->body);
	struct ast_stmt **stmt = ast_block_stmts(f->body);
	for (int i = index; i < nstmts; i++) {
		struct ast_stmt_splits splits = ast_stmt_splits(
			stmt[i], actual_state
		struct state *prestate = state_copy(actual_state);
		struct error *err = ast_stmt_process(
			stmt[i], fname, actual_state
		);
		if (splits.n) {
			return split_paths_verify(
				f, actual_state, i, &splits, abstract_state
		if (err) {
			struct error *uc_err = error_to_undecideable_cond(err);
			if (!uc_err) {
				return err;
			}
			return split_path_verify(
				f, prestate, i,
				error_get_undecideable_cond(uc_err),
				abstract_state
			);
		}
		if ((err = ast_stmt_process(stmt[i], fname, actual_state))) {
			return err;
		}
		state_destroy(prestate);
		if (ast_stmt_isterminal(stmt[i], actual_state)) {
			break;
		}


@@ 498,47 496,24 @@ path_verify(struct ast_function *f, struct state *actual_state, int index,
	}
	if (state_hasgarbage(actual_state)) {
		v_printf("actual: %s", state_str(actual_state));
		struct strbuilder *b = strbuilder_create();
		strbuilder_printf(
			b, "%s: garbage on heap", ast_function_name(f)
		return error_printf(
			"%s: garbage on heap", ast_function_name(f)
		);
		return error_create(strbuilder_build(b));
	}
	
	bool equiv = state_equal(actual_state, abstract_state);
	if (!equiv) {
		/*v_printf("actual: %s\n", state_str(actual_state));*/
		/*v_printf("abstract: %s\n", state_str(abstract_state));*/
		struct strbuilder *b = strbuilder_create();
		strbuilder_printf(
			b, "%s: actual and abstract states differ", ast_function_name(f)
		return error_printf(
			"%s: actual and abstract states differ",
			ast_function_name(f)
		);
		return error_create(strbuilder_build(b));
	}

	return NULL;
}

static struct error *
split_path_verify(struct ast_function *f, struct state *actual_state,
		int index, struct ast_expr *cond, struct state *abstract_state);

static struct error *
split_paths_verify(struct ast_function *f, struct state *actual_state,
		int index, struct ast_stmt_splits *splits, struct state *abstract_state)
{
	struct error *err;
	for (int i = 0; i < splits->n; i++) {
		err = split_path_verify(
			f, actual_state, index, splits->cond[i], abstract_state
		);
		if (err) {
			return err;
		}
	}
	return NULL;
}

static struct ast_function_arr *
body_paths(struct ast_function *f, int index, struct ast_expr *);



@@ 590,20 565,22 @@ ast_function_absexec(struct ast_function *f, struct state *state)
		}
	}

	char *fname = ast_function_name(f);
	int nstmts = ast_block_nstmts(f->abstract);
	struct ast_stmt **stmt = ast_block_stmts(f->abstract);
	for (int i = 0; i < nstmts; i++) {
		struct result *res = ast_stmt_absexec(stmt[i], state, false);
		if (result_iserror(res)) {
			return res;
		struct error *err = ast_stmt_absprocess(
			stmt[i], fname, state, false
		);
		if (err) {
			return result_error_create(err);
		}
		/* result_destroy(res); */
	}

	/* wrap result and return */ 
	struct object *obj = state_getresult(state);
	assert(obj);
	return result_value_create(object_as_value(obj));
	struct object_res res = state_getresult(state);
	assert(!res.err);
	return result_value_create(object_as_value(res.obj));
}
static void
recurse_buildgraph(struct map *g, struct map *dedup, char *fname, struct externals *ext);

M src/ast/lex.l => src/ast/lex.l +20 -1
@@ 318,6 318,9 @@ lex_finish()
	map_destroy(table);
}

static char *
strip_iflibx(char *filename);

struct lexememarker *
lexememarker_create(int linenum, int column, char *filename,
		enum linemarker_flag flags)


@@ 325,11 328,27 @@ lexememarker_create(int linenum, int column, char *filename,
	struct lexememarker *loc = malloc(sizeof(struct lexememarker));
	loc->linenum = linenum;
	loc->column = column;
	loc->filename = filename;
	loc->filename = strip_iflibx(filename);
	loc->flags = flags;
	return loc;
}

static char *
strip_iflibx(char *filename)
{
	int includes_len = strlen(XR0_INCLUDES_SEGMENT);

	char *libx_start = strstr(filename, XR0_INCLUDES_SEGMENT);
	if (!libx_start) {
		return filename;
	}
	int len = (strlen(libx_start) - includes_len) + 1;
	char *s = malloc(sizeof(char) * len);
	strcpy(s, libx_start + includes_len);
	free(filename);
	return s;
}

struct lexememarker *
lexememarker_copy(struct lexememarker *loc)
{ return lexememarker_create(

M src/ast/stmt/stmt.c => src/ast/stmt/stmt.c +3 -65
@@ 657,68 657,6 @@ ast_stmt_getfuncs(struct ast_stmt *stmt)
	}
}

static struct ast_stmt_splits
stmt_sel_splits(struct ast_stmt *stmt, struct state *s);

struct ast_stmt_splits
ast_stmt_splits(struct ast_stmt *stmt, struct state *s)
{
	/* TODO: consider expressions with calls */
	switch (stmt->kind) {
	case STMT_NOP:
		return (struct ast_stmt_splits) { .n = 0, .cond = NULL };
	case STMT_EXPR:
		return ast_expr_splits(stmt->u.expr, s);
	case STMT_SELECTION:
		return stmt_sel_splits(stmt, s);
	case STMT_JUMP:
		if (stmt->u.jump.rv) {
			return ast_expr_splits(stmt->u.jump.rv, s);
		}
		return (struct ast_stmt_splits) { .n = 0, .cond = NULL };
	case STMT_LABELLED:
		return ast_stmt_splits(stmt->u.labelled.stmt, s);
	case STMT_ITERATION:
	case STMT_COMPOUND:
	case STMT_COMPOUND_V:
		/* disallowed splits for now */
		return (struct ast_stmt_splits) { .n = 0, .cond = NULL };
	default:
		assert(false);
	}
}

static bool
condexists(struct ast_expr *cond, struct state *);

static struct ast_stmt_splits
stmt_sel_splits(struct ast_stmt *stmt, struct state *s)
{
	struct result *res = ast_expr_pf_reduce(stmt->u.selection.cond, s);
	struct value *v = result_as_value(res);
	struct ast_expr *e = value_to_expr(v);
	if (condexists(e, s) || value_isconstant(v)) {
		return (struct ast_stmt_splits) { .n = 0, .cond = NULL };
	}
	/*printf("cond: %s\n", ast_expr_str(r));*/
	struct ast_expr **cond = malloc(sizeof(struct ast_expr *));
	cond[0] = e;
	return (struct ast_stmt_splits) {
		.n    = 1,
		.cond = cond,
	};
}

static bool
condexists(struct ast_expr *cond, struct state *s)
{
	struct result *res = ast_expr_pf_reduce(cond, s);
	assert(!result_iserror(res) && result_hasvalue(res));
	struct ast_expr *reduced = value_to_expr(result_as_value(res));
	struct props *p = state_getprops(s);
	return props_get(p, reduced) || props_contradicts(p, reduced);
}

static struct string_arr *
ast_stmt_expr_getfuncs(struct ast_stmt *stmt)
{


@@ 810,10 748,10 @@ ast_stmt_preconds_validate(struct ast_stmt *stmt)
static struct error *
preconds_selection_verify(struct ast_stmt *stmt)
{
	struct strbuilder *b = strbuilder_create();
	struct lexememarker *l = ast_stmt_lexememarker(stmt);
	strbuilder_printf(b, "%s setup preconditions must be decidable", lexememarker_str(l));
	return error_create(strbuilder_build(b));
	return error_printf(
		"%s setup preconditions must be decidable", lexememarker_str(l)
	);
}

static struct error *

M src/ast/stmt/stmt.h => src/ast/stmt/stmt.h +0 -9
@@ 30,15 30,6 @@ ast_stmt_getfuncs(struct ast_stmt *stmt);

struct error;

struct ast_stmt_splits {
	int n;
	struct ast_expr **cond;
	struct error *err;
};

struct ast_stmt_splits
ast_stmt_splits(struct ast_stmt *, struct state *);

struct state;

/* TODO: change to more regular tuple */

M src/ast/stmt/verify.c => src/ast/stmt/verify.c +72 -51
@@ 19,23 19,21 @@ ast_stmt_process(struct ast_stmt *stmt, char *fname, struct state *state)

	if (ast_stmt_kind(stmt) == STMT_COMPOUND_V) {
		if ((err = ast_stmt_verify(stmt, state))) {
			struct strbuilder *b = strbuilder_create();
			struct lexememarker *loc = ast_stmt_lexememarker(stmt); 
			assert(loc);
			char *m = lexememarker_str(loc);
			strbuilder_printf(b, "%s: %s", m, err->msg);
			struct error *e = error_printf("%s: %w", m, err);
			free(m);
			return error_create(strbuilder_build(b));
			return e;
		}
	}
	if ((err = ast_stmt_exec(stmt, state))) {
		struct strbuilder *b = strbuilder_create();
		struct lexememarker *loc = ast_stmt_lexememarker(stmt); 
		assert(loc);
		char *m = lexememarker_str(loc);
		strbuilder_printf(b, "%s:%s: cannot exec statement: %s", m, fname, err->msg);
		struct error *e = error_printf("%s:%s: %w", m, fname, err);
		free(m);
		return error_create(strbuilder_build(b));
		return e;
	}
	return NULL;
}


@@ 111,7 109,7 @@ stmt_expr_verify(struct ast_stmt *stmt, struct state *state)
	if (ast_expr_decide(expr, state)) {
		return NULL;
	}
	return error_create("cannot verify statement");
	return error_printf("cannot verify statement");
}

static bool


@@ 138,7 136,7 @@ stmt_iter_verify(struct ast_stmt *stmt, struct state *state)
			*up = ast_stmt_iter_upper_bound(stmt);

	if (!ast_expr_rangedecide(assertion, lw, up, state)) {
		return error_create("could not verify");	
		return error_printf("could not verify");	
	}
	return NULL;
}


@@ 235,6 233,9 @@ static struct ast_stmt *
iter_neteffect(struct ast_stmt *);

static struct error *
ast_stmt_absexec(struct ast_stmt *stmt, struct state *state, bool should_setup);

static struct error *
stmt_iter_exec(struct ast_stmt *stmt, struct state *state)
{
	/* TODO: check internal consistency of iteration */


@@ 244,9 245,9 @@ stmt_iter_exec(struct ast_stmt *stmt, struct state *state)
		return NULL;
	}

	struct result *res = ast_stmt_absexec(neteffect, state, true);
	if (result_iserror(res)) {
		return result_as_error(res);
	struct error *err = ast_stmt_absexec(neteffect, state, true);
	if (err) {
		return err;
	}

	ast_stmt_destroy(neteffect);


@@ 291,40 292,59 @@ stmt_jump_exec(struct ast_stmt *stmt, struct state *state)
		return result_as_error(res);
	}
	if (result_hasvalue(res)) {
		struct object *obj = state_getresult(state); 
		assert(obj);
		object_assign(obj, value_copy(result_as_value(res)));
		struct object_res obj_res = state_getresult(state); 
		assert(!obj_res.err);
		object_assign(obj_res.obj, value_copy(result_as_value(res)));
		/* destroy result if exists */
		
	}
	return NULL;
}

static struct result *
struct error *
ast_stmt_absprocess(struct ast_stmt *stmt, char *fname, struct state *state,
		bool should_setup)
{
	struct error *err = ast_stmt_absexec(stmt, state, should_setup);
	if (!err) {
		return NULL;
	}
	struct lexememarker *loc = ast_stmt_lexememarker(stmt); 
	assert(loc);
	char *m = lexememarker_str(loc);
	struct error *e = error_printf("%s:%s: %w", m, fname, err);
	free(m);
	return e;
}

static struct error *
expr_absexec(struct ast_expr *expr, struct state *state);

static struct error *
labelled_absexec(struct ast_stmt *stmt, struct state *state, bool should_setup);

static struct result *
static struct error *
sel_absexec(struct ast_stmt *stmt, struct state *state, bool should_setup);

static struct result *
static struct error *
iter_absexec(struct ast_stmt *stmt, struct state *state);

static struct result *
static struct error *
comp_absexec(struct ast_stmt *stmt, struct state *state, bool should_setup);

static struct result *
static struct error *
jump_absexec(struct ast_stmt *, struct state *);

struct result *
static struct error *
ast_stmt_absexec(struct ast_stmt *stmt, struct state *state, bool should_setup)
{
	switch (ast_stmt_kind(stmt)) {
	case STMT_NOP:
		return result_value_create(NULL);
		return NULL;
	case STMT_LABELLED:
		return labelled_absexec(stmt, state, should_setup);
	case STMT_EXPR:
		return ast_expr_absexec(ast_stmt_as_expr(stmt), state);
		return expr_absexec(ast_stmt_as_expr(stmt), state);
	case STMT_SELECTION:
		return sel_absexec(stmt, state, should_setup);
	case STMT_ITERATION:


@@ 338,7 358,7 @@ ast_stmt_absexec(struct ast_stmt *stmt, struct state *state, bool should_setup)
	}
}

static struct result *
static struct error *
labelled_absexec(struct ast_stmt *stmt, struct state *state, bool should_setup)
{
	if (!ast_stmt_ispre(stmt)) {


@@ 350,23 370,33 @@ labelled_absexec(struct ast_stmt *stmt, struct state *state, bool should_setup)
	}
	if (!should_setup) {
		/* if abstract is called we don't execute setup */
		return result_value_create(NULL);
		return NULL;
	}
	return ast_stmt_absexec(setup, state, should_setup);
}

static struct result *
static struct error *
expr_absexec(struct ast_expr *expr, struct state *state)
{
	struct result *res = ast_expr_abseval(expr, state);
	if (result_iserror(res)) {
		return result_as_error(res);
	}
	return NULL;
}

static struct error *
sel_absexec(struct ast_stmt *stmt, struct state *state, bool should_setup)
{
	struct decision dec = sel_decide(ast_stmt_sel_cond(stmt), state);
	if (dec.err) {
		return result_error_create(dec.err);
		return dec.err;
	}
	if (dec.decision) {
		return ast_stmt_absexec(ast_stmt_sel_body(stmt), state, should_setup);
	}
	assert(!ast_stmt_sel_nest(stmt));
	return result_value_create(NULL);
	return NULL;
}

struct decision


@@ 398,18 428,9 @@ sel_decide(struct ast_expr *control, struct state *state)
	struct value *zero = value_int_create(0);

	if (!values_comparable(zero, v)) {
		struct strbuilder *b = strbuilder_create();
		char *c_str = ast_expr_str(control);
		char *v_str = value_str(v);
		strbuilder_printf(
			b, "`%s' with value `%s' is undecidable",
			c_str, v_str
		);
		free(v_str);
		free(c_str);
		return (struct decision) {
			.decision = false,
			.err      = error_create(strbuilder_build(b)),
			.err      = error_undecideable_cond(value_to_expr(v))
		};
	}



@@ 421,7 442,7 @@ sel_decide(struct ast_expr *control, struct state *state)
static struct ast_expr *
hack_alloc_from_neteffect(struct ast_stmt *);

static struct result *
static struct error *
iter_absexec(struct ast_stmt *stmt, struct state *state)
{
	struct error *err;


@@ 431,9 452,9 @@ iter_absexec(struct ast_stmt *stmt, struct state *state)
			*up = ast_stmt_iter_upper_bound(stmt);

	if ((err = ast_expr_alloc_rangeprocess(alloc, lw, up, state))) {
		return result_error_create(err);
		return err;
	}
	return result_value_create(NULL);
	return NULL;
}

static struct ast_expr *


@@ 446,26 467,26 @@ hack_alloc_from_neteffect(struct ast_stmt *stmt)
	return ast_stmt_as_expr(ast_block_stmts(block)[0]);
}

static struct result *
static struct error *
comp_absexec(struct ast_stmt *stmt, struct state *state, bool should_setup)
{
	struct ast_block *b = ast_stmt_as_block(stmt);
	struct ast_stmt **stmts = ast_block_stmts(b);
	for (int i = 0; i < ast_block_nstmts(b); i++) {
		struct result *res = ast_stmt_absexec(stmts[i], state, should_setup);
		if (result_iserror(res)) {
			return res;
		struct error *err = ast_stmt_absexec(stmts[i], state, should_setup);
		if (err) {
			return err;
		}
	}
	return result_value_create(NULL);
	return NULL;
}

static struct result *
static struct error *
jump_absexec(struct ast_stmt *stmt, struct state *state)
{
	return ast_expr_absexec(
	return expr_absexec(
		ast_expr_assignment_create(
			ast_expr_identifier_create("return"),
			ast_expr_identifier_create(KEYWORD_RETURN),
			ast_stmt_jump_rv(stmt)
		), 
		state


@@ 516,9 537,9 @@ static struct error *
labelled_setupabsexec(struct ast_stmt *stmt, struct state *state)
{
	/* XXX: dedupe the execution of setups */
	struct result *res = ast_stmt_absexec(stmt, state, true);
	if (result_iserror(res)) {
		return result_as_error(res);
	struct error *err = ast_stmt_absexec(stmt, state, true);
	if (err) {
		return err;
	}
	return NULL;
}

M src/ast/type/type.c => src/ast/type/type.c +1 -1
@@ 61,7 61,7 @@ struct ast_type *
ast_type_create_voidptr()
{
	struct ast_type *t = ast_type_create(TYPE_POINTER, 0);
	t->ptr_type = TYPE_VOID;
	t->ptr_type = ast_type_create(TYPE_VOID, 0);
	return t;
}


M src/main.c => src/main.c +3 -3
@@ 243,7 243,7 @@ pass1(struct ast *root, struct externals *ext)
		assert(ast_function_abstract(f));

		if ((err = ast_function_verify(f, ext))) {
			fprintf(stderr, "%s\n", err->msg);
			fprintf(stderr, "%s\n", error_str(err));
			exit(EXIT_FAILURE);
		}
		v_printf("qed %s\n", ast_function_name(f));


@@ 265,10 265,10 @@ pass_inorder(struct string_arr *order, struct externals *ext)
		assert(ast_function_abstract(f));

		if ((err = ast_function_verify(f, ext))) {
			fprintf(stderr, "%s\n", err->msg);
			fprintf(stderr, "%s\n", error_str(err));
			exit(EXIT_FAILURE);
		}
		fprintf(stderr, "qed %s\n", ast_function_name(f));
		v_printf("qed %s\n", ast_function_name(f));
	}
}


M src/state/block.c => src/state/block.c +2 -2
@@ 223,12 223,12 @@ block_range_dealloc(struct block *b, struct ast_expr *lw, struct ast_expr *up,

	int lw_index = object_arr_index(b->arr, lw, s);
	if (lw_index == -1) {
		return error_create("lower bound not allocated");
		return error_printf("lower bound not allocated");
	}

	int up_index = object_arr_index_upperincl(b->arr, up, s);
	if (up_index == -1) {
		return error_create("upper bound not allocated");
		return error_printf("upper bound not allocated");
	}

	int n = object_arr_nobjects(b->arr);

M src/state/heap.c => src/state/heap.c +1 -1
@@ 125,7 125,7 @@ heap_deallocblock(struct heap *h, int address)
	assert(address < block_arr_nblocks(h->blocks));

	if (h->freed[address]) {
		return error_create("double free");
		return error_printf("double free");
	}
	h->freed[address] = true;
	return NULL;

M src/state/location.c => src/state/location.c +9 -7
@@ 102,8 102,10 @@ location_transfigure(struct location *loc, struct state *compare)
void
location_destroy(struct location *loc)
{
	ast_expr_destroy(loc->offset);
	free(loc);
	if (loc->type != LOCATION_STATIC) {
		ast_expr_destroy(loc->offset);
		free(loc);
	}
}

static bool


@@ 292,7 294,7 @@ location_getblock(struct location *loc, struct static_memory *sm, struct vconst 
	switch (loc->type) {
	case LOCATION_STATIC:
		return (struct block_res) {
			.b = static_memory_getblock( sm, loc->block),
			.b = static_memory_getblock(sm, loc->block),
			.err = NULL
		};
	case LOCATION_AUTOMATIC:


@@ 319,7 321,7 @@ location_auto_getblock(struct location *loc, struct stack *s)
	if (!f) {
		return (struct block_res) {
			.b = NULL,
			.err = error_create("stack frame doesn't exist")
			.err = error_printf("stack frame doesn't exist")
		};
	}
	return (struct block_res) {


@@ 339,7 341,7 @@ struct error *
location_dealloc(struct location *loc, struct heap *heap)
{
	if (loc->type != LOCATION_DYNAMIC) {
		return error_create("not heap location");
		return error_printf("not heap location");
	}
	return heap_deallocblock(heap, loc->block);
}


@@ 353,14 355,14 @@ location_range_dealloc(struct location *loc, struct ast_expr *lw,

	struct block *b = state_getblock(state, loc);
	if (!b) {
		return error_create("cannot get block");
		return error_printf("cannot get block");
	}

	if (!block_range_aredeallocands(b, lw, up, state)) {
		printf("block: %s\n", block_str(b));
		printf("lw: %s, up: %s\n", ast_expr_str(lw), ast_expr_str(up));
		assert(false);
		return error_create("some values not allocated");
		return error_printf("some values not allocated");
	}

	return block_range_dealloc(b, lw, up, state);

M src/state/state.c => src/state/state.c +28 -20
@@ 127,7 127,9 @@ state_str(struct state *state)
	}
	free(clump);
	char *stack = stack_str(state->stack, state);
	strbuilder_printf(b, "%s\n", stack);
	if (strlen(stack) > 0) {
		strbuilder_printf(b, "%s\n", stack);
	}
	free(stack);
	char *props = props_str(state->props, "\t");
	if (strlen(props) > 0) {


@@ 286,9 288,14 @@ state_get(struct state *state, struct location *loc, bool constructive)
		return (struct object_res) { .obj = NULL, .err = res.err };
	}
	if (!res.b) {
		assert(location_type(loc) == LOCATION_DYNAMIC ||
			location_type(loc) == LOCATION_DEREFERENCABLE);
		return (struct object_res) { .obj = NULL, .err = NULL };
		switch (location_type(loc)) {
		case LOCATION_DYNAMIC:
		case LOCATION_DEREFERENCABLE:
		case LOCATION_STATIC:
			return (struct object_res) { .obj = NULL, .err = NULL };
		default:
			assert(false);
		}
	}
	struct object *obj = block_observe(res.b, location_offset(loc), state, constructive);
	return (struct object_res) { .obj = obj, .err = NULL };


@@ 312,7 319,7 @@ state_getblock(struct state *state, struct location *loc)
	return res.b;
}

struct object *
struct object_res
state_getresult(struct state *state)
{
	struct variable *v = stack_getresult(state->stack);


@@ 322,7 329,7 @@ state_getresult(struct state *state)
	if (res.err) {
		assert(false);
	}
	return res.obj;
	return res;
}

static struct ast_type *


@@ 356,7 363,7 @@ state_getloc(struct state *state, char *id)
	return value_ptr_create(variable_location(v));
}

struct object *
struct object_res
state_getobject(struct state *state, char *id)
{
	if (strcmp(id, KEYWORD_RETURN) == 0) {


@@ 365,14 372,12 @@ state_getobject(struct state *state, char *id)

	struct variable *v = stack_getvariable(state->stack, id);
	if (!v) {
		assert(false);
		return (struct object_res) {
			.err = error_printf("unknown variable `%s'", id)
		};
	}

	struct object_res res = state_get(state, variable_location(v), true);
	if (res.err) {
		assert(false);
	}
	return res.obj;
	return state_get(state, variable_location(v), true);
}

struct object_res


@@ 388,9 393,12 @@ state_deref(struct state *state, struct value *ptr_val, struct ast_expr *index)
	struct location *deref = location_with_offset(deref_base, index);
	struct object_res res = state_get(state, deref, true);
	if (res.err) {
		struct strbuilder *b = strbuilder_create();
		strbuilder_printf(b, "undefined indirection: %s", res.err->msg);
		return (struct object_res) { .obj = NULL, .err = error_create(strbuilder_build(b))};
		return (struct object_res) {
			.obj = NULL,
			.err = error_printf(
				"undefined indirection: %s", error_str(res.err)
			)
		};
	}
	/*location_destroy(deref);*/
	return res;


@@ 403,7 411,7 @@ state_range_alloc(struct state *state, struct object *obj,
	/* loc corresponds to, say, `arr`, so we dereference */
	struct value *arr_val = object_as_value(obj);
	if (!arr_val) {
		return error_create("no value");
		return error_printf("no value");
	}

	/* assume pointer */


@@ 416,7 424,7 @@ state_range_alloc(struct state *state, struct object *obj,
		assert(false);
	}
	if (!res.b) {
		return error_create("no block");
		return error_printf("no block");
	}

	/* TODO: prevent creation of virtual block for empty ranges */ 


@@ 436,7 444,7 @@ struct error *
state_dealloc(struct state *state, struct value *val)
{
	if (!value_islocation(val)) {
		return error_create("undefined free of value not pointing at heap");
		return error_printf("undefined free of value not pointing at heap");
	}
	return location_dealloc(value_as_location(val), state->heap);
}


@@ 448,7 456,7 @@ state_range_dealloc(struct state *state, struct object *obj,
	/* obj corresponds to, say, `arr`, so we dereference */
	struct value *arr_val = object_as_value(obj);
	if (!arr_val) {
		return error_create("no value");
		return error_printf("no value");
	}
	struct location *deref = value_as_location(arr_val);
	return location_range_dealloc(deref, lw, up, state);

M src/state/static.c => src/state/static.c +1 -1
@@ 1,3 1,4 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>


@@ 68,7 69,6 @@ pool_copy(struct map *p)
		);
	}
	return pcopy;

}

int

M src/util/util.c => src/util/util.c +137 -27
@@ 176,32 176,6 @@ strbuilder_putc(struct strbuilder *b, char c)
	strbuilder_printf(b, "%c", c);
}

struct error *
error_create(char *s)
{
	struct error *err = calloc(1, sizeof(struct error));
	err->msg = s;
	return err;
}

struct error *
error_prepend(struct error* e, char *prefix)
{
	int new_len = strlen(prefix) + strlen(e->msg) + 1 + 1;
	char *new_msg = (char *) malloc(new_len);

	/* prepend prefix */
	strcpy(new_msg, prefix);
	/* concat original msg */
	strcat(new_msg, e->msg);
	/* add newline character */
	strcat(new_msg, "\n");

	free(e);

	return error_create(new_msg);
}

/* string_arr */

struct string_arr *


@@ 313,7 287,143 @@ v_printf(char *fmt, ...)
	}
	va_list ap;
	va_start(ap, fmt);
	int r = vprintf(fmt, ap);
	int r = vfprintf(stderr, fmt, ap);
	va_end(ap);
	return r;
}

struct error {
	enum error_type {
		ERROR_PRINTF,
		ERROR_UNDECIDEABLE_COND,
	} type;
	union error_contents {
		char *printf;
		struct ast_expr *undecidable_cond;
	} contents;
	struct error *inner;
};

static struct error *
error_to(struct error *err, enum error_type t)
{
	if (err->type == t) {
		return err;
	}
	if (err->inner) {
		return error_to(err->inner, t);
	}
	return NULL;
}

static char *
findnextfmt(char **start);

static struct error *
error_nest(struct error *outer, struct error *inner)
{
	if (!outer->inner) {
		outer->inner = inner;
		return outer;
	}
	return error_nest(outer->inner, inner);
}

struct error *
error_printf(char *fmt, ...)
{
	char *otherfmt;
	struct error *inner;

	struct error *err = calloc(1, sizeof(struct error));
	err->type = ERROR_PRINTF;

	struct strbuilder *b = strbuilder_create();
	va_list ap;
	va_start(ap, fmt);
	for (char *p = fmt; *p; p++) {
		if (*p != '%') {
			strbuilder_putc(b, *p);
			continue;
		}
		/* ⊢ *p == '%' */
		switch (*++p) {
		case 'w':
			inner = va_arg(ap, struct error *);
			strbuilder_printf(
				b, "%s", error_str(inner)
			);
			err = error_nest(err, inner);
			break;
		default:
			otherfmt = findnextfmt(&p);
			strbuilder_vprintf(b, otherfmt, ap);
			free(otherfmt);
			p--; /* prepare for increment */
			break;
		}
	}
	char *s = strbuilder_build(b);
	va_end(ap);

	err->contents.printf = s;
	return err;
}

static char *
findnextfmt(char **p)
{
	char *start = *p;

	char *s = start;
	for (; *s && *s != '%'; s++) {}
	/* s is '\0' or '%' */
	int len = s - start;
	*p = s;
	char *output = malloc(sizeof(char) * (len + 2));
	*output = '%';
	strncpy(output + 1, start, len);
	*(output + len + 1) = '\0';
	return output;
}

struct error *
error_undecideable_cond(struct ast_expr *cond)
{
	assert(cond);

	struct error *err = calloc(1, sizeof(struct error));
	err->type = ERROR_UNDECIDEABLE_COND;
	err->contents.undecidable_cond = cond;
	return err;
}

struct error *
error_to_undecideable_cond(struct error *err)
{
	return error_to(err, ERROR_UNDECIDEABLE_COND);
}

struct ast_expr *
error_get_undecideable_cond(struct error *err)
{
	assert(err->type == ERROR_UNDECIDEABLE_COND);
	return err->contents.undecidable_cond;
}

char *
error_str(struct error *err)
{
	char *error_type_str[] = {
		[ERROR_UNDECIDEABLE_COND] = "undecideable condition"
	};

	switch (err->type) {
	case ERROR_PRINTF:
		return dynamic_str(err->contents.printf);
	case ERROR_UNDECIDEABLE_COND:
		return dynamic_str(error_type_str[err->type]);
	default:
		assert(false);
	}
}

M tests/0-basic/130-FAIL-double-free.x.EXPECTED => tests/0-basic/130-FAIL-double-free.x.EXPECTED +2 -1
@@ 1,1 1,2 @@
0-basic/130-FAIL-double-free.x:10:9:unit: cannot exec statement: double free
0-basic/130-FAIL-double-free.x:10:9:unit: 
	stdlib.h:10:31:free: double free

M tests/0-basic/140-FAIL-sync-free.x.EXPECTED => tests/0-basic/140-FAIL-sync-free.x.EXPECTED +2 -1
@@ 1,1 1,2 @@
0-basic/140-FAIL-sync-free.x:6:9:unit: cannot exec statement: undefined free of value not pointing at heap
0-basic/140-FAIL-sync-free.x:6:9:unit: 
	stdlib.h:10:31:free: undefined free of value not pointing at heap

M tests/0-basic/170-FAIL-double-free.x.EXPECTED => tests/0-basic/170-FAIL-double-free.x.EXPECTED +2 -1
@@ 1,1 1,2 @@
0-basic/170-FAIL-double-free.x:11:9:unit: cannot exec statement: double free
0-basic/170-FAIL-double-free.x:11:9:unit: 
	stdlib.h:10:31:free: double free

M tests/3-topological/001-valid-sort-matrix.x => tests/3-topological/001-valid-sort-matrix.x +3 -3
@@ 44,7 44,7 @@ void
matrix_destroy(struct matrix *m) ~ [
	int i;

	pre: m = matrix_create($, $);
	setup: m = matrix_create($, $);

	for (i = 0; i < m->rows; i++) {
		.free(m->data[i]);


@@ 66,7 66,7 @@ matrix_add(struct matrix *m1, struct matrix *m2) ~ [
	int i;
	struct matrix *sum;

	pre: {
	setup: {
		m1 = matrix_create($, $);
		m2 = matrix_create($, $);
	}


@@ 96,7 96,7 @@ matrix_add(struct matrix *m1, struct matrix *m2) ~ [

void
matrix_print(struct matrix *m) ~ [
	pre: m = matrix_create($, $);
	setup: m = matrix_create($, $);
] {
	int i; int j; int digit;


M tests/3-topological/002-valid-sort-parse.x => tests/3-topological/002-valid-sort-parse.x +5 -5
@@ 24,7 24,7 @@ parse(char *input) ~ [

void
lexer_destroy(struct lexer *l) ~ [
	pre: {
	setup: {
		l = lexer_create(
			$, $,
			$, malloc(1),


@@ 38,7 38,7 @@ lexer_destroy(struct lexer *l) ~ [

void
lexer_print(struct lexer *l) ~ [
	pre: {
	setup: {
		l = lexer_create(
			.clump(1), .clump(1),
			$, .malloc(1),


@@ 86,7 86,7 @@ struct pattern {

void
pattern_print(struct pattern *p) ~ [
	pre: p = pattern_create("", "");
	setup: p = pattern_create("", "");
];

struct token {


@@ 96,7 96,7 @@ struct token {

void
token_print(struct token *t) ~ [
	pre: t = token_create(0, "", "");
	setup: t = token_create(0, "", "");
];

struct lexer {


@@ 142,7 142,7 @@ lexer_print(struct lexer *l)
{
	int i;

	puts("\tpre:");
	puts("\pre:");
	puts(l->pre);

	puts("\tpost:");

M tests/6-preconditions/100-FAIL-need-alloced-lval.x.EXPECTED => tests/6-preconditions/100-FAIL-need-alloced-lval.x.EXPECTED +2 -1
@@ 1,1 1,2 @@
6-preconditions/100-FAIL-need-alloced-lval.x:15:10:main: cannot exec statement: parameter x of func must be heap allocated
6-preconditions/100-FAIL-need-alloced-lval.x:15:10:main: `func' precondition failure
	parameter `x' of `func' must be heap allocated

M tests/6-preconditions/101-FAIL-need-alloced-rval.x.EXPECTED => tests/6-preconditions/101-FAIL-need-alloced-rval.x.EXPECTED +2 -1
@@ 1,1 1,2 @@
6-preconditions/101-FAIL-need-alloced-rval.x:21:13:main: cannot exec statement: parameter x of func must be rvalue
6-preconditions/101-FAIL-need-alloced-rval.x:21:13:main: `func' precondition failure
	parameter `x' of `func' must be rvalue

M tests/6-preconditions/102-FAIL-need-clump-lval-freed-ptr.x.EXPECTED => tests/6-preconditions/102-FAIL-need-clump-lval-freed-ptr.x.EXPECTED +2 -1
@@ 1,1 1,2 @@
6-preconditions/102-FAIL-need-clump-lval-freed-ptr.x:16:9:main: cannot exec statement: parameter x of func must be lvalue
6-preconditions/102-FAIL-need-clump-lval-freed-ptr.x:16:9:main: `func' precondition failure
	parameter `x' of `func' must be lvalue

M tests/6-preconditions/103-FAIL-need-clump-lval-assigned-ptr.x.EXPECTED => tests/6-preconditions/103-FAIL-need-clump-lval-assigned-ptr.x.EXPECTED +2 -1
@@ 1,1 1,2 @@
6-preconditions/103-FAIL-need-clump-lval-assigned-ptr.x:16:9:main: cannot exec statement: parameter x of func must be lvalue
6-preconditions/103-FAIL-need-clump-lval-assigned-ptr.x:16:9:main: `func' precondition failure
	parameter `x' of `func' must be lvalue

M tests/7-use-after-free/100-FAIL-lvalue-use-after-free.x.EXPECTED => tests/7-use-after-free/100-FAIL-lvalue-use-after-free.x.EXPECTED +1 -1
@@ 1,1 1,1 @@
7-use-after-free/100-FAIL-lvalue-use-after-free.x:11:8:func: cannot exec statement: undefined indirection: *(p) is not an lvalue
7-use-after-free/100-FAIL-lvalue-use-after-free.x:11:8:func: undefined indirection: *(p) is not an lvalue

M tests/7-use-after-free/101-FAIL-rvalue-use-after-free.x.EXPECTED => tests/7-use-after-free/101-FAIL-rvalue-use-after-free.x.EXPECTED +1 -1
@@ 1,1 1,1 @@
7-use-after-free/101-FAIL-rvalue-use-after-free.x:12:8:func: cannot exec statement: undefined indirection: *(p+0) has no value
7-use-after-free/101-FAIL-rvalue-use-after-free.x:12:8:func: undefined indirection: *(p+0) has no value

M tests/7-use-after-free/110-FAIL-freed-ptr-dangling-return.x.EXPECTED => tests/7-use-after-free/110-FAIL-freed-ptr-dangling-return.x.EXPECTED +1 -1
@@ 1,1 1,1 @@
7-use-after-free/110-FAIL-freed-ptr-dangling-return.x:21:8:main: cannot exec statement: undefined indirection: *(i+0) has no value
7-use-after-free/110-FAIL-freed-ptr-dangling-return.x:21:8:main: undefined indirection: *(i+0) has no value

M tests/7-use-after-free/112-FAIL-freed-ptr-dangling-false-claim.x.EXPECTED => tests/7-use-after-free/112-FAIL-freed-ptr-dangling-false-claim.x.EXPECTED +1 -1
@@ 1,1 1,1 @@
7-use-after-free/112-FAIL-freed-ptr-dangling-false-claim.x:21:8:main: cannot exec statement: undefined indirection: *(i+0) has no value
7-use-after-free/112-FAIL-freed-ptr-dangling-false-claim.x:21:8:main: undefined indirection: *(i+0) has no value

M tests/7-use-after-free/120-FAIL-freed-ptr-maybe-dangling-return.x.EXPECTED => tests/7-use-after-free/120-FAIL-freed-ptr-maybe-dangling-return.x.EXPECTED +1 -1
@@ 1,1 1,1 @@
7-use-after-free/120-FAIL-freed-ptr-maybe-dangling-return.x:34:8:main: cannot exec statement: undefined indirection: *(j) is not an lvalue
7-use-after-free/120-FAIL-freed-ptr-maybe-dangling-return.x:34:8:main: undefined indirection: *(j) is not an lvalue

M tests/7-use-after-free/200-FAIL-stack-frame-pop-dangling-return.x.EXPECTED => tests/7-use-after-free/200-FAIL-stack-frame-pop-dangling-return.x.EXPECTED +1 -2
@@ 1,2 1,1 @@
7-use-after-free/200-FAIL-stack-frame-pop-dangling-return.x:19:8:main: cannot exec statement: undefined indirection: *(p+0) has no value

7-use-after-free/200-FAIL-stack-frame-pop-dangling-return.x:19:8:main: undefined indirection: *(p+0) has no value

M tests/7-use-after-free/210-FAIL-stack-frame-pop-dangling-assign.x.EXPECTED => tests/7-use-after-free/210-FAIL-stack-frame-pop-dangling-assign.x.EXPECTED +1 -1
@@ 1,1 1,1 @@
7-use-after-free/210-FAIL-stack-frame-pop-dangling-assign.x:27:8:main: cannot exec statement: undefined indirection: stack frame doesn't exist
7-use-after-free/210-FAIL-stack-frame-pop-dangling-assign.x:27:8:main: undefined indirection: stack frame doesn't exist

M tests/7-use-after-free/300-FAIL-struct-free-ptr-dangling.x.EXPECTED => tests/7-use-after-free/300-FAIL-struct-free-ptr-dangling.x.EXPECTED +2 -1
@@ 1,1 1,2 @@
7-use-after-free/300-FAIL-struct-free-ptr-dangling.x:45:18:create_report: cannot exec statement: parameter s of puts must be lvalue
7-use-after-free/300-FAIL-struct-free-ptr-dangling.x:45:18:create_report: `puts' precondition failure
	parameter `s' of `puts' must be lvalue

M tests/8-uninitialised-memory/000-FAIL-uninitialised-memory-rval.x.EXPECTED => tests/8-uninitialised-memory/000-FAIL-uninitialised-memory-rval.x.EXPECTED +1 -1
@@ 1,1 1,1 @@
8-uninitialised-memory/000-FAIL-uninitialised-memory-rval.x:7:7:main: cannot exec statement: undefined memory access: j has no value
8-uninitialised-memory/000-FAIL-uninitialised-memory-rval.x:7:7:main: undefined memory access: `j' has no value

M tests/8-uninitialised-memory/001-FAIL-uninitialised-memory-rval.x.EXPECTED => tests/8-uninitialised-memory/001-FAIL-uninitialised-memory-rval.x.EXPECTED +1 -1
@@ 1,1 1,1 @@
8-uninitialised-memory/001-FAIL-uninitialised-memory-rval.x:7:8:undefined_memory1: cannot exec statement: undefined memory access: p has no value
8-uninitialised-memory/001-FAIL-uninitialised-memory-rval.x:7:8:undefined_memory1: undefined memory access: `p' has no value

M tests/8-uninitialised-memory/002-FAIL-uninitialised-memory-rval.x.EXPECTED => tests/8-uninitialised-memory/002-FAIL-uninitialised-memory-rval.x.EXPECTED +1 -1
@@ 1,1 1,1 @@
8-uninitialised-memory/002-FAIL-uninitialised-memory-rval.x:9:8:undefined_memory2: cannot exec statement: undefined indirection: *(q+0) has no value
8-uninitialised-memory/002-FAIL-uninitialised-memory-rval.x:9:8:undefined_memory2: undefined indirection: *(q+0) has no value

M tests/8-uninitialised-memory/003-FAIL-uninitialised-memory-rval.x.EXPECTED => tests/8-uninitialised-memory/003-FAIL-uninitialised-memory-rval.x.EXPECTED +1 -1
@@ 1,1 1,1 @@
8-uninitialised-memory/003-FAIL-uninitialised-memory-rval.x:10:8:undefined_memory3: cannot exec statement: undefined indirection: *(p+0) has no value
8-uninitialised-memory/003-FAIL-uninitialised-memory-rval.x:10:8:undefined_memory3: undefined indirection: *(p+0) has no value

M tests/8-uninitialised-memory/004-FAIL-uninitialised-memory-rval.x.EXPECTED => tests/8-uninitialised-memory/004-FAIL-uninitialised-memory-rval.x.EXPECTED +1 -1
@@ 1,1 1,1 @@
8-uninitialised-memory/004-FAIL-uninitialised-memory-rval.x:12:9:main: cannot exec statement: undefined memory access: i has no value
8-uninitialised-memory/004-FAIL-uninitialised-memory-rval.x:12:9:main: undefined memory access: `i' has no value

M tests/99-program/100-lex/parse.x => tests/99-program/100-lex/parse.x +285 -169
@@ 8,22 8,135 @@
#define true 1

char *
read_file(char *path) ~ [ .malloc result; ];
read_file(char *path) ~ [
	char *file;

	file = malloc(1);
	*file = $;
	return file;
];

struct pattern {
	char *name; char *pattern;
};

struct pattern_arr {
	int n;
	struct pattern *p;
};

struct pattern_arr *
pattern_arr_create(int n, struct pattern *p) ~ [
	struct pattern_arr *arr;

	arr = malloc(sizeof(struct pattern));
	arr->n = n;
	arr->p = p;
	return arr;
]{
	struct pattern_arr *arr;

	arr = malloc(sizeof(struct pattern));
	arr->n = n;
	arr->p = p;
	return arr;
}

void
pattern_arr_destroy(struct pattern_arr *arr) ~ [
	setup: arr = pattern_arr_create($, malloc(1));
	free(arr->p);
	free(arr);
]{
	int i;

	for (i = 0; i < arr->n; i++) {
		free(arr->p[i].name);
		free(arr->p[i].pattern);
	}
	free(arr->p);
	free(arr);
}

void
pattern_print(struct pattern *p) ~ [
	setup: p = pattern_create(malloc(1), malloc(1));
];

struct token {
	int isliteral;
	char *name; char *action;
};

struct token_arr {
	int n;
	struct token *t;
};

struct token_arr *
token_arr_create(int n, struct token *t) ~ [
	struct token_arr *arr;

	arr = malloc(sizeof(struct token));
	arr->n = n;
	arr->t = t;
	return arr;
]{
	struct token_arr *arr;

	arr = malloc(sizeof(struct token));
	arr->n = n;
	arr->t = t;
	return arr;
}

void
token_arr_destroy(struct token_arr *arr) ~ [
	setup: arr = token_arr_create($, malloc(1));
	free(arr->t);
	free(arr);
]{
	int i;

	for (i = 0; i < arr->n; i++) {
		free(arr->t[i].name);
		free(arr->t[i].action);
	}
	free(arr->t);
	free(arr);
}

void
token_print(struct token *t) ~ [
	setup: t = token_create(0, "", "");
];



struct lexer {
	char *pre; char *post;
	int npat; struct pattern *pattern;
	int ntok; struct token *token;
	struct pattern_arr *patterns;
	struct token_arr *tokens;
};

struct lexer *
lexer_create(char *pre, char *post, int npat, struct pattern *pattern,
		int ntok, struct token *token) ~ [
	.malloc result; 
	result->pre = pre;
	result->post = post;
	result->pattern = pattern;
	result->token = token;
lexer_create(char *pre, char *post, struct pattern_arr *patterns,
		struct token_arr *tokens) ~ [
	struct lexer *l;

	setup: {
		pre = .clump(1);
		post = .clump(1);
		patterns = pattern_arr_create($, malloc(1));
		tokens = token_arr_create($, malloc(1));
	};

	l = malloc(sizeof(struct lexer)); 
	l->pre = pre;
	l->post = post;
	l->patterns = patterns;
	l->tokens = tokens;
	return l;
];




@@ 40,8 153,10 @@ struct stringresult {

struct stringresult
parse_defsraw(char *input) ~ [
	.malloc result.s;
	result.pos = $;
	struct stringresult res;
	res.s = malloc(1);
	res.pos = $;
	return res;
];

/* skipws: skip whitespace */


@@ 51,55 166,55 @@ skipws(char *s);
struct lexer *
parse(char *pos) ~ [
	char *pre; char *post;
	struct pattern *pattern;
	struct token *token;
	pre = malloc(1);
	pattern = malloc(1);
	token = malloc(1);
	post = malloc(1);
	return .exer_create(pre, post, $, pattern, $, token);
	struct pattern_arr *patterns;
	struct token_arr *tokens;

	pre = malloc(1); *pre = $;
	patterns = pattern_arr_create($, malloc(1));
	tokens = token_arr_create($, malloc(1));
	post = malloc(1); *post = $;
	return lexer_create(pre, post, patterns, tokens);
];

void
lexer_destroy(struct lexer *l) ~ [
	pre: {
		l = lexer_create(
			malloc(1), malloc(1),
			$, malloc(1),
			$, malloc(1)
		);
	}
	setup: l = lexer_create(
		malloc(1), malloc(1),
		pattern_arr_create($, malloc(1)),
		token_arr_create($, malloc(1))
	);

	.free l->pre;
	.free l->post;
	.free l->pattern;
	.free l->token;
	.free l;
	free(l->pre);
	free(l->post);
	pattern_arr_destroy(l->patterns);
	token_arr_destroy(l->tokens);
	free(l);
];

void
lexer_print(struct lexer *l) ~ [
	pre: {
		l = lexer_create(
			$, $,
			$, malloc(1),
			$, malloc(1)
		);
	}
	setup: l = lexer_create(
		malloc(1), malloc(1),
		pattern_arr_create($, malloc(1)),
		token_arr_create($, malloc(1))
	);
];

int
main() ~ []
{
main(int argc, char **argv) ~ [
	setup: {
		argv = .clump(sizeof(char *) * 2);
		argv[1] = $;
	}
]{
	char *file;
	struct lexer *l;

	file = read_file("tests/3-program/100-lex/gen.l");
	file = read_file(argv[1]);

	l = parse(file);
	lexer_print(l);
	lexer_destroy(l);

	free(file);
}



@@ 121,39 236,17 @@ read_file(char *path)
	return str;
}

struct pattern {
	char *name; char *pattern;
};

void
pattern_print(struct pattern *p) ~ [
	pre: p = pattern_create("", "");
];

struct token {
	int isliteral;
	char *name; char *action;
};

void
token_print(struct token *t) ~ [
	pre: t = token_create(0, "", "");
];

struct lexer *
lexer_create(char *pre, char *post, int npat, struct pattern *pattern,
		int ntok, struct token *token) 
lexer_create(char *pre, char *post, struct pattern_arr *patterns,
		struct token_arr *tokens)
{
	struct lexer *l;

	l = malloc(sizeof(struct lexer));
	l->pre = pre;
	l->post = post;
	l->pattern = pattern;
	l->npat = npat;
	l->token = token;
	l->ntok = ntok;

	l->patterns = patterns;
	l->tokens = tokens;
	return l;
}



@@ 164,16 257,8 @@ lexer_destroy(struct lexer *l)

	free(l->pre);
	free(l->post);
	for (i = 0; i < l->npat; i++) {
		free(l->pattern[i].name);
		free(l->pattern[i].pattern);
	}
	free(l->pattern);
	for (i = 0; i < l->ntok; i++) {
		free(l->token[i].name);
		free(l->token[i].action);
	}
	free(l->token);
	pattern_arr_destroy(l->patterns);
	token_arr_destroy(l->tokens);
	free(l);
}



@@ 189,25 274,24 @@ lexer_print(struct lexer *l)
	puts(l->post);

	puts("\tpatterns:");
	for (i = 0; i < l->npat; i++) {
	for (i = 0; i < l->patterns->n; i++) {
		putchar('\t');
		putchar('\t');
		pattern_print(&l->pattern[i]);
		pattern_print(&l->patterns->p[i]);
		putchar('\n');
	}
	puts("\ttokens:");
	for (i = 0; i < l->ntok; i++) {
	for (i = 0; i < l->tokens->n; i++) {
		putchar('\t');
		putchar('\t');
		token_print(&l->token[i]);
		token_print(&l->tokens->t[i]);
		putchar('\n');
	}
}

struct defsresult {
	char *pre;
	struct pattern *pattern;
	int npat;
	struct pattern_arr *patterns;
	char *pos;
};



@@ 219,10 303,11 @@ count_patterns(char *pos);

struct defsresult
parse_defs(char *pos) ~ [
	.malloc result.pre;
	.malloc result.pattern;
	result.pos = $;
	result.npat = $;
	struct defsresult res;
	res.pre = malloc(sizeof(char) * 1000);
	res.patterns = pattern_arr_create($, malloc(1));
	res.pos = $;
	return res;
];

int


@@ 232,8 317,7 @@ beginsdefs(char *s)
}

struct rulesresult {
	struct token *token;
	int ntok;
	struct token_arr *tokens;
	char *pos;
};



@@ 242,13 326,15 @@ count_tokens(char *pos);

struct rulesresult
parse_rules(char *pos) ~ [
	.malloc result.token;
	result.pos = $;
	result.ntok = $;
	struct rulesresult res;

	res.tokens = token_arr_create($, malloc(1));
	res.pos = $;
	return res;
];

char *
parse_toeof(char *input) ~ [ .malloc result; ];
parse_toeof(char *input) ~ [ return malloc(sizeof(char) * 1000); ];

struct lexer *
parse(char *pos)


@@ 267,8 353,7 @@ parse(char *pos)
	rules = parse_rules(pos);
	pos = rules.pos;
	post = parse_toeof(pos);
	return lexer_create(defs.pre, post, defs.npat, defs.pattern, rules.ntok,
		rules.token);
	return lexer_create(defs.pre, post, defs.patterns, rules.tokens);
}

int


@@ 278,7 363,7 @@ isboundary(char *s)
}

char *
substr(char *s, int n) ~ [ .malloc result; ]
substr(char *s, int n) ~ [ return malloc(sizeof(char) * 1000); ]
{
	int len;
	char *ss;


@@ 305,7 390,7 @@ skiplinespace(char *s)
}

char *
parse_id(char *input) ~ [ .malloc result; ];
parse_id(char *input) ~ [ return malloc(sizeof(char) * 1000); ];

char *
skipoptions(char *pos)


@@ 345,7 430,7 @@ parse_id(char *input)
}

char *
parse_tonewline(char *input) ~ [ .malloc result; ]
parse_tonewline(char *input) ~ [ return malloc(sizeof(char) * 1000); ]
{
	char *s;
	s = input; /* must be here because not seen with skip loop hack */


@@ 356,16 441,16 @@ parse_tonewline(char *input) ~ [ .malloc result; ]
}

struct patternet {
	struct pattern *pattern;
	int npat;
	struct pattern_arr *patterns;
	char *pos;
};

struct patternet
parse_defsproper(char *input) ~ [
	.malloc result.pattern;
	result.npat = $;
	result.pos = $;
	struct patternet res;
	res.patterns = pattern_arr_create($, malloc(1));
	res.pos = $;
	return res;
];

struct defsresult


@@ 386,8 471,7 @@ parse_defs(char *pos)
	set = parse_defsproper(pos);

	res.pre = raw.s;
	res.pattern = set.pattern;
	res.npat = set.npat;
	res.patterns = set.patterns;
	res.pos = set.pos;
	return res;
}


@@ 413,17 497,24 @@ parse_defsraw(char *input)
}

struct pattern *
pattern_create(char *name, char *pattern) ~ [
	.malloc result;
	result->name = name;
	result->pattern = pattern;
pattern_create(char *name, char *pattern) ~ [	
	struct pattern *res;

	setup: {
		name = .clump(1);
		pattern = .clump(1);
	};

	res = malloc(sizeof(struct pattern));
	res->name = name;
	res->pattern = pattern;
	return res;
]{
	struct pattern *p;

	p = malloc(sizeof(struct pattern));
	p->name = name;
	p->pattern = pattern;

	return p;
}



@@ 443,32 534,39 @@ struct patternresult {
};

struct patternpos {
	struct pattern *p;
	struct pattern *patterns; /* an array */
	char *pos;
};

struct patternpos
parse_defs_n(char *pos, int npat) ~ [
	.malloc result.p;
	result.pos = $;
	struct patternpos res;

	res.patterns = malloc(sizeof(struct pattern) * npat);
	res.pos = $;
	return res;
];

struct patternet
parse_defsproper(char *input)
{
	int n;
	struct patternpos p_pos;
	struct patternet res;
	struct patternpos defs_n;

	res.npat = count_patterns(input);
	defs_n = parse_defs_n(input, res.npat);
	res.pattern = defs_n.p;
	res.pos = defs_n.pos;
	n = count_patterns(input);
	p_pos = parse_defs_n(input, n);
	res.patterns = pattern_arr_create(n, p_pos.patterns);
	res.pos = p_pos.pos;
	return res;
}

struct patternresult
parse_pattern(char *pos) ~ [
	result.p = pattern_create(malloc(1), malloc(1));
	struct patternresult res;

	res.p = pattern_create(malloc(1), malloc(1));
	return res;
];

int


@@ 507,37 605,36 @@ parse_pattern(char *pos)
}

struct patternpos
parse_defs_n(char *pos, int npat)
parse_defs_n(char *pos, int n)
{
	int i;
	struct pattern *p;
	struct patternresult parsed;
	struct patternpos res;

	if (!npat) {
		p = malloc(1);
	}
	if (npat) {
		p = malloc(sizeof(struct pattern) * npat);
		for (i = 0; i < npat; i++) {
			parsed = parse_pattern(pos);
			p[i] = *parsed.p;
			free(parsed.p);
			pos = skipws(parsed.pos);
		}
	p = malloc(sizeof(struct pattern) * n);
	for (i = 0; i < n; i++) {
		parsed = parse_pattern(pos);
		p[i] = *parsed.p;
		free(parsed.p);
		pos = skipws(parsed.pos);
	}

	res.p = p;
	res.patterns = p;
	res.pos = pos;
	return res;
}

struct token *
token_create(int isliteral, char *name, char *action) ~ [
	.malloc result;
	result->isliteral = isliteral;
	result->name = name;
	result->action = action;
	struct token *tk;

	tk = malloc(sizeof(struct token));
	tk->isliteral = isliteral;
	tk->name = name;
	tk->action = action;

	return tk;
]{
	struct token *tk;



@@ 559,26 656,30 @@ token_print(struct token *t)
}

struct tokenpos {
	struct token *t;
	struct token *tokens;
	char *pos;
};

struct tokenpos
parse_rules_n(char *pos, int ntok) ~ [
	.malloc result.t;
	result.pos = $;
	struct tokenpos r;

	r.tokens = malloc(sizeof(struct token) * ntok);
	r.pos = $;
	return r;
];

struct rulesresult
parse_rules(char *pos)
{
	int n;
	struct rulesresult res;
	struct tokenpos rules_n;
	struct tokenpos rules_pos;

	res.ntok = count_tokens(pos);
	rules_n = parse_rules_n(pos, res.ntok);
	res.token = rules_n.t;
	res.pos = rules_n.pos;
	n = count_tokens(pos);
	rules_pos = parse_rules_n(pos, n);
	res.tokens = token_arr_create(n, rules_pos.tokens);
	res.pos = rules_pos.pos;
	return res;
}



@@ 589,8 690,10 @@ struct tokenresult {

struct tokenresult
parse_token(char *pos) ~ [
	result.tk = token_create($, malloc($), malloc($));
	result.pos = $;
	struct tokenresult r;
	r.tk = token_create($, malloc($), malloc($));
	r.pos = $;
	return r;
];

int


@@ 612,27 715,21 @@ count_tokens(char *pos)
}

struct tokenpos
parse_rules_n(char *pos, int ntok)
parse_rules_n(char *pos, int n)
{
	int i;
	struct token *t;
	struct tokenresult parsed;
	struct tokenpos res;

	if (!ntok) {
		t = malloc(1);
	}
	if (ntok) {
		t = malloc(sizeof(struct token) * ntok);
		for (i = 0; i < ntok; i++) {
			parsed = parse_token(pos);
			t[i] = *parsed.tk;
			free(parsed.tk);
			pos = skipws(parsed.pos);
		}
	t = malloc(sizeof(struct token) * n);
	for (i = 0; i < n; i++) {
		parsed = parse_token(pos);
		t[i] = *parsed.tk;
		free(parsed.tk);
		pos = skipws(parsed.pos);
	}

	res.t = t;
	res.tokens = t;
	res.pos = pos;
	return res;
}


@@ 645,15 742,20 @@ struct tknameresult {

struct tknameresult
parse_name(char *pos) ~ [
	.malloc result.name;
	result.isliteral = $;
	result.pos = $;
	struct tknameresult r;

	r.name = malloc(sizeof(char) * 1000);
	r.isliteral = $;
	r.pos = $;
	return r;
];

struct stringresult
parse_action(char *input) ~ [
	.malloc result.s;
	result.pos = $;
	struct stringresult res;
	res.s = malloc(sizeof(char) * 1000);
	res.pos = $;
	return res;
];

struct tokenresult


@@ 672,13 774,27 @@ parse_token(char *pos)
}

struct tknameresult
parse_token_id(char *pos) ~ [ .malloc result.name; ];
parse_token_id(char *pos) ~ [
	struct tknameresult r;
	r.name = malloc(sizeof(char) * 1000);
	return r;
];

struct tknameresult
parse_token_literal(char *input) ~ [ .malloc result.name; ];
parse_token_literal(char *input) ~ [
	struct tknameresult r;

	r.name = malloc(sizeof(char) * 1000);
	return r;
];

struct tknameresult
parse_token_pattern(char *pos) ~ [ .malloc result.name; ];
parse_token_pattern(char *pos) ~ [
	struct tknameresult r;

	r.name = malloc(sizeof(char) * 1000);
	return r;
];

struct tknameresult
parse_name(char *pos)

M tests/run => tests/run +2 -3
@@ 42,10 42,9 @@ do
	if [[ "$f" == *"${fail_prefix}"* || "$folder" == *"$topological_folder"* ]]
	then
		expected_file="${f}.${expected_suffix}"
		expected_output=$(cat $expected_file)
		
		# check if output matches expected_output
		if [[ "${output}" == "$expected_output" ]]
		echo "$output" | diff - $expected_file
		if [[ $? -eq 0 ]]
		then
			npass=$((npass+1))
			printf "$pass"