~lbnz/xr0

a64f2ef593ff285d5b33ec737befe08e02a8deeb — Xr0 Team 6 months ago 01b49c7 chore/refactor-state-structure
chore: move functionality of verify into ast and
convert `EXPR_MEMORY` into `STMT_ALLOCATION`

todo:
	- https://todo.sr.ht/~lbnz/xr0/10
	- https://todo.sr.ht/~lbnz/xr0/14
M Makefile => Makefile +8 -14
@@ 1,6 1,7 @@
# commands
CC = gcc -g -Wreturn-type -std=gnu11
CFLAGS = -I include -Wall
CFLAGS = -I include -I src/ast -Wall
VALGRIND = valgrind --fullpath-after=`pwd`/src/

LEX = lex
YACC = bison -yvd


@@ 11,7 12,6 @@ BUILD_DIR = build
INCLUDE_DIR = include
SRC_DIR = src
EXT_DIR = $(SRC_DIR)/ext
VERIFY_DIR = $(SRC_DIR)/verify
STATE_DIR = $(SRC_DIR)/state
OBJECT_DIR = $(SRC_DIR)/object
VALUE_DIR = $(SRC_DIR)/value


@@ 24,7 24,6 @@ XR0V = $(BIN_DIR)/0v

# build artifacts
MAIN_OBJ = $(BUILD_DIR)/main.o
VERIFY_OBJ = $(BUILD_DIR)/verify.o

STATE_OBJ = $(BUILD_DIR)/state.o
STACK_OBJ = $(BUILD_DIR)/stack.o


@@ 50,7 49,6 @@ XR0_OBJECTS = $(AST_OBJ) \
	      $(GRAM_OBJ) \
	      $(STATE_OBJ) \
	      $(UTIL_OBJ) \
	      $(VERIFY_OBJ) \
	      $(MATH_OBJ)

STATE_OBJECTS = $(VALUE_OBJ) \


@@ 68,24 66,20 @@ $(XR0V): $(MAIN_OBJ) $(BIN_DIR)
	@$(CC) $(CFLAGS) -o $@ $(MAIN_OBJ) $(OBJECTS)

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

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

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

$(MAIN_OBJ): main.c $(OBJECTS)
	@printf 'CC\t$@\n'
	@$(CC) $(CFLAGS) -o $@ -c main.c

$(VERIFY_OBJ): $(VERIFY_DIR)/verify.c $(STATE_OBJ)
	@printf 'CC\t$@\n'
	@$(CC) $(CFLAGS) -o $@ -c $(VERIFY_DIR)/verify.c

$(STATE_OBJ): $(STATE_DIR)/state.c $(STATE_OBJECTS)
	@printf 'CC\t$@\n'
	@$(CC) $(CFLAGS) -o $@ -c $(STATE_DIR)/state.c


@@ 122,7 116,7 @@ $(UTIL_OBJ): $(UTIL_DIR)/util.c $(BUILD_DIR)
	@printf 'CC\t$@\n'
	@$(CC) $(CFLAGS) -o $@ -c $(UTIL_DIR)/util.c

$(AST_OBJ): $(AST_DIR)/ast.c $(MATH_OBJ)
$(AST_OBJ): $(AST_DIR)/ast.c $(AST_DIR)/expr/expr.h $(MATH_OBJ)
	@printf 'CC\t$@\n'
	@$(CC) $(CFLAGS) -o $@ -c $(AST_DIR)/ast.c



@@ 161,10 155,10 @@ test: $(RUNTEST) $(TESTFILES) $(XR0V)
	@./tests/run

check: $(RUNTEST) $(TESTFILES) $(XR0V)
	valgrind $(XR0V) -I libx $(filter-out $@,$(MAKECMDGOALS))
	$(VALGRIND) $(XR0V) -I libx $(filter-out $@,$(MAKECMDGOALS))

check-verbose: $(RUNTEST) $(TESTFILES) $(XR0V)
	valgrind --num-callers=30 \
	$(VALGRIND) --num-callers=30 \
		$(XR0V) -v -I libx $(filter-out $@,$(MAKECMDGOALS))

clean:

M include/ast.h => include/ast.h +152 -217
@@ 2,76 2,7 @@
#define XR0_AST_H
#include <stdbool.h>

struct ast_expr {
	enum ast_expr_kind {
		EXPR_IDENTIFIER		= 1 << 0,
		EXPR_CONSTANT		= 1 << 1,
		EXPR_STRING_LITERAL	= 1 << 2,
		EXPR_BRACKETED		= 1 << 3,
		EXPR_ITERATION		= 1 << 4,

		EXPR_CALL		= 1 << 5,
		EXPR_INCDEC		= 1 << 6,

		EXPR_STRUCTMEMBER	= 1 << 7,

		EXPR_UNARY		= 1 << 8,
		EXPR_BINARY		= 1 << 9,

		EXPR_ASSIGNMENT		= 1 << 10,

		EXPR_MEMORY		= 1 << 11,
		EXPR_ASSERTION		= 1 << 12,
		EXPR_ARBARG		= 1 << 13,
	} kind;
	struct ast_expr *root;
	union {
		char *string; /* identifier, literal, assertion */
		int constant;
		struct {
			int n;
			struct ast_expr **arg;
		} call;
		struct {
			int inc, pre;
		} incdec;
		enum ast_unary_operator {
			UNARY_OP_ADDRESS		= 1 << 0,
			UNARY_OP_DEREFERENCE		= 1 << 1,
			UNARY_OP_POSITIVE		= 1 << 2,
			UNARY_OP_NEGATIVE		= 1 << 3,
			UNARY_OP_ONES_COMPLEMENT	= 1 << 4,
			UNARY_OP_BANG			= 1 << 5
		} unary_op;
		struct {
			enum ast_binary_operator {
				BINARY_OP_EQV	= 1 << 0,
				BINARY_OP_IMPL	= 1 << 1,
				BINARY_OP_FLLW	= 1 << 2,
				
				BINARY_OP_EQ	= 1 << 3,
				BINARY_OP_NE	= 1 << 4,

				BINARY_OP_LT	= 1 << 5,
				BINARY_OP_GT	= 1 << 6,
				BINARY_OP_LE	= 1 << 7,
				BINARY_OP_GE	= 1 << 8,				

				BINARY_OP_ADDITION	= 1 << 9,
				BINARY_OP_SUBTRACTION	= 1 << 10
			} op;
			struct ast_expr *e1, *e2;
		} binary;
		struct ast_expr *assignment_value;
		struct {
			enum effect_kind {
				EFFECT_ALLOC		= 1 << 0,
				EFFECT_DEALLOC		= 1 << 1,
				EFFECT_UNDEFINED	= 1 << 2
			} kind;
		} memory;
	} u;
};
struct ast_expr;

struct ast_expr *
ast_expr_identifier_create(char *);


@@ 130,60 61,64 @@ ast_expr_member_root(struct ast_expr *);
char *
ast_expr_member_field(struct ast_expr *);

enum ast_unary_operator;

struct ast_expr *
ast_expr_unary_create(struct ast_expr *, enum ast_unary_operator);

enum ast_unary_operator
ast_expr_unary_op(struct ast_expr *);

bool
ast_expr_unary_isdereference(struct ast_expr *);

struct ast_expr *
ast_expr_unary_operand(struct ast_expr *);

struct ast_expr *
ast_expr_binary_create(struct ast_expr *e1, enum ast_binary_operator,
		struct ast_expr *e2);
ast_expr_eq_create(struct ast_expr *, struct ast_expr *);

struct ast_expr *
ast_expr_binary_e1(struct ast_expr *);
ast_expr_ne_create(struct ast_expr *, struct ast_expr *);

struct ast_expr *
ast_expr_binary_e2(struct ast_expr *);
ast_expr_lt_create(struct ast_expr *, struct ast_expr *);

enum ast_binary_operator
ast_expr_binary_op(struct ast_expr *);
struct ast_expr *
ast_expr_gt_create(struct ast_expr *, struct ast_expr *);

struct ast_expr *
ast_expr_assignment_create(struct ast_expr *root, struct ast_expr *value);
ast_expr_le_create(struct ast_expr *, struct ast_expr *);

struct ast_expr *
ast_expr_assignment_lval(struct ast_expr *expr);
ast_expr_ge_create(struct ast_expr *, struct ast_expr *);

struct ast_expr *
ast_expr_assignment_rval(struct ast_expr *expr);
ast_expr_sum_create(struct ast_expr *, struct ast_expr *);

struct ast_expr *
ast_expr_memory_create(enum effect_kind, struct ast_expr *expr);
ast_expr_difference_create(struct ast_expr *, struct ast_expr *);

struct ast_expr *
ast_expr_memory_root(struct ast_expr *expr);
ast_expr_binary_e1(struct ast_expr *);

bool
ast_expr_memory_isalloc(struct ast_expr *expr);
struct ast_expr *
ast_expr_binary_e2(struct ast_expr *);

bool
ast_expr_memory_isunalloc(struct ast_expr *expr);
struct ast_expr *
ast_expr_assignment_create(struct ast_expr *root, struct ast_expr *value);

bool
ast_expr_memory_isundefined(struct ast_expr *expr);
struct ast_expr *
ast_expr_assignment_lval(struct ast_expr *expr);

enum effect_kind
ast_expr_memory_kind(struct ast_expr *expr);
struct ast_expr *
ast_expr_assignment_rval(struct ast_expr *expr);

struct ast_expr *
ast_expr_assertion_create(struct ast_expr *assertand);
ast_expr_isdeallocand_create(struct ast_expr *assertand);

struct ast_expr *
ast_expr_assertion_assertand(struct ast_expr *expr);
ast_expr_isdeallocand_assertand(struct ast_expr *expr);

struct ast_expr *
ast_expr_arbarg_create();


@@ 197,24 132,41 @@ ast_expr_str(struct ast_expr *);
struct ast_expr *
ast_expr_copy(struct ast_expr *);

enum ast_expr_kind
ast_expr_kind(struct ast_expr *);

bool
ast_expr_equal(struct ast_expr *e1, struct ast_expr *e2);

struct math_state;

bool
ast_expr_eval(struct ast_expr *e);
ast_expr_matheval(struct ast_expr *e);

struct state;

bool
ast_expr_decide(struct ast_expr *, struct state *);

bool
ast_expr_rangedecide(struct ast_expr *, struct ast_expr *lw,
		struct ast_expr *up, struct state *);

struct error *
ast_expr_exec(struct ast_expr *, struct state *);

struct lvalue;

struct lvalue *
ast_expr_lvalue(struct ast_expr *, struct state *);

struct result *
ast_expr_eval(struct ast_expr *, struct state *);

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

struct ast_stmt;

struct ast_block {
	int ndecl, nstmt;
	struct ast_variable **decl;
	struct ast_stmt **stmt;
};
struct ast_block;
struct ast_variable;

struct ast_block *
ast_block_create(


@@ 243,47 195,14 @@ ast_block_nstmts(struct ast_block *b);
struct ast_stmt **
ast_block_stmts(struct ast_block *b);

struct ast_stmt {
	enum ast_stmt_kind {
		STMT_NOP		= 1 << 0,
		STMT_LABELLED		= 1 << 1,
		STMT_COMPOUND		= 1 << 2,
		STMT_COMPOUND_V		= 1 << 3,
		STMT_EXPR		= 1 << 4,
		STMT_SELECTION		= 1 << 5,
		STMT_ITERATION		= 1 << 6,
		STMT_ITERATION_E	= 1 << 7,
		STMT_JUMP		= 1 << 8,
	} kind;
	union {
		struct {
			char *label;
			struct ast_stmt *stmt;
		} labelled;
		struct ast_block *compound;
		struct {
			bool isswitch;
			struct ast_expr *cond;
			struct ast_stmt *body;
			struct ast_stmt *nest;
		} selection;
		struct {
			struct ast_stmt *init, *cond, *body;
			struct ast_expr *iter;
			struct ast_block *abstract;
		} iteration;
		struct ast_expr *expr;
		struct {
			enum ast_jump_kind {
				JUMP_RETURN	= 1 << 0,
			} kind;
			struct ast_expr *rv;
		} jump;
	} u;

	struct lexememarker *loc;
enum ast_jump_kind {
	JUMP_RETURN	= 1 << 0,
};

struct ast_stmt;

struct lexememarker;

struct ast_stmt *
ast_stmt_create_labelled(struct lexememarker *, char *label, struct ast_stmt *);



@@ 358,6 277,18 @@ ast_stmt_create_jump(struct lexememarker *, enum ast_jump_kind, struct ast_expr 
struct ast_expr *
ast_stmt_jump_rv(struct ast_stmt *stmt);

struct ast_stmt *
ast_stmt_create_alloc(struct lexememarker *, struct ast_expr *arg);

struct ast_stmt *
ast_stmt_create_dealloc(struct lexememarker *, struct ast_expr *arg);

struct ast_expr *
ast_stmt_alloc_arg(struct ast_stmt *);

bool
ast_stmt_alloc_isalloc(struct ast_stmt *);

void
ast_stmt_destroy(struct ast_stmt *);



@@ 370,9 301,6 @@ ast_stmt_str(struct ast_stmt *);
bool
ast_stmt_equal(struct ast_stmt *, struct ast_stmt *);

enum ast_stmt_kind
ast_stmt_kind(struct ast_stmt *);

/* ast_stmt_as_block: Wrap in block if single statement. */
struct ast_block *
ast_stmt_as_block(struct ast_stmt *);


@@ 383,55 311,57 @@ ast_stmt_as_v_block(struct ast_stmt *);
struct ast_expr *
ast_stmt_as_expr(struct ast_stmt *);

struct ast_type {
	enum ast_type_modifier {
		/* storage class */
		MOD_EXTERN	= 1 << 0,
		MOD_STATIC	= 1 << 1,
		MOD_AUTO	= 1 << 2,
		MOD_REGISTER	= 1 << 3,

		/* qualifier */
		MOD_CONST	= 1 << 4,
		MOD_VOLATILE	= 1 << 5,
	} mod;
	enum ast_type_base { /* base type */
		TYPE_VOID,
		TYPE_CHAR,
		TYPE_SHORT,
		TYPE_INT,
		TYPE_LONG,
		TYPE_FLOAT,
		TYPE_DOUBLE,
		TYPE_SIGNED,
		TYPE_UNSIGNED,

		TYPE_POINTER,
		TYPE_ARRAY,

		TYPE_TYPEDEF,

		TYPE_STRUCT,
		TYPE_UNION,
		TYPE_ENUM,
	} base;
	union {
		struct ast_type *ptr_type;
		struct {
			struct ast_type *type;
			int length;
		} arr;
		struct {
			struct ast_type *type;
			char *name;
		} _typedef;
		struct {
			char *tag;
			struct ast_variable_arr *members;
		} structunion;
	} u;
bool
ast_stmt_issetup(struct ast_stmt *);

struct error *
ast_stmt_process(struct ast_stmt *, struct state *);

struct error *
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 *state);


enum ast_type_modifier {
	/* storage class */
	MOD_EXTERN	= 1 << 0,
	MOD_STATIC	= 1 << 1,
	MOD_AUTO	= 1 << 2,
	MOD_REGISTER	= 1 << 3,

	/* qualifier */
	MOD_CONST	= 1 << 4,
	MOD_VOLATILE	= 1 << 5,
};

enum ast_type_base { /* base type */
	TYPE_VOID,
	TYPE_CHAR,
	TYPE_SHORT,
	TYPE_INT,
	TYPE_LONG,
	TYPE_FLOAT,
	TYPE_DOUBLE,
	TYPE_SIGNED,
	TYPE_UNSIGNED,

	TYPE_POINTER,
	TYPE_ARRAY,

	TYPE_TYPEDEF,

	TYPE_STRUCT,
	TYPE_UNION,
	TYPE_ENUM,
};

struct ast_type;

struct ast_type *
ast_type_create(enum ast_type_base base, enum ast_type_modifier mod);



@@ 446,6 376,8 @@ ast_type_create_arr(struct ast_type *type, int length);
struct ast_type *
ast_type_create_typedef(struct ast_type *type, char *name);

struct ast_variable_arr;

struct ast_type *
ast_type_create_struct(char *tag, struct ast_variable_arr *);



@@ 462,6 394,9 @@ struct ast_type *
ast_type_create_struct_partial(char *tag);

void
ast_type_mod_or(struct ast_type *, enum ast_type_modifier);

void
ast_type_destroy(struct ast_type *);

char *


@@ 496,10 431,7 @@ ast_variable_arr_v(struct ast_variable_arr *);
struct ast_variable_arr *
ast_variable_arr_copy(struct ast_variable_arr *arr);

struct ast_variable {
	char *name;
	struct ast_type *type;
};
struct ast_variable;

struct ast_variable *
ast_variable_create(char *name, struct ast_type *type);


@@ 522,19 454,7 @@ ast_variable_name(struct ast_variable *);
struct ast_type *
ast_variable_type(struct ast_variable *);

struct ast_function {
	bool isaxiom;

	struct ast_type *ret;

	char *name;
	/* parameters */
	int nparam;
	struct ast_variable **param;

	struct ast_block *abstract, 
			 *body;
};
struct ast_function;

/* ast_function_create: name must be allocated on the heap */
struct ast_function *


@@ 578,28 498,43 @@ ast_function_nparams(struct ast_function *f);
struct ast_variable **
ast_function_params(struct ast_function *f);

struct ast_externdecl {
	enum ast_externdecl_kind {
		EXTERN_FUNCTION = 1 << 0,
		EXTERN_VARIABLE	= 1 << 1,
		EXTERN_TYPE	= 1 << 2,
	} kind;
	union {
		struct ast_function *function;
		struct ast_variable *variable;
		struct ast_type *type;
	} u;
struct externals;
struct error;

struct error *
ast_function_verify(struct ast_function *, struct externals *);

struct result;

struct result *
ast_function_absexec(struct ast_function *, struct state *state);

enum ast_externdecl_kind {
	EXTERN_FUNCTION = 1 << 0,
	EXTERN_VARIABLE	= 1 << 1,
	EXTERN_TYPE	= 1 << 2,
};

struct ast_externdecl;

struct ast_externdecl *
ast_functiondecl_create(struct ast_function *);

struct ast_function *
ast_externdecl_as_function(struct ast_externdecl *);

struct ast_externdecl *
ast_variabledecl_create(struct ast_variable *);

struct ast_externdecl *
ast_typedecl_create(struct ast_type *);

enum ast_externdecl_kind
ast_externdecl_kind(struct ast_externdecl *);

void
ast_externdecl_install(struct ast_externdecl *decl, struct externals *ext);

void
ast_externdecl_destroy(struct ast_externdecl *);


M include/object.h => include/object.h +2 -0
@@ 5,6 5,8 @@ struct range;
struct value;
struct object;

struct ast_expr;

struct object *
object_value_create(struct ast_expr *offset, struct value *);


M include/value.h => include/value.h +1 -1
@@ 84,7 84,7 @@ value_references(struct value *, struct location *, struct state *);
enum ast_binary_operator;

bool
value_compare(struct value *v1, enum ast_binary_operator, struct value *v2);
value_equal(struct value *v1, struct value *v2);

enum number_value_type {
	NUMBER_VALUE_CONSTANT,

M include/verify.h => include/verify.h +0 -3
@@ 1,7 1,4 @@
#ifndef XR0_VERIFY_H
#define XR0_VERIFY_H

struct error *
function_verify(struct ast_function *, struct externals *);

#endif

M main.c => main.c +6 -36
@@ 177,41 177,13 @@ struct ast *root;
static bool
should_verify(struct ast_externdecl *decl)
{
	if (decl->kind != EXTERN_FUNCTION) {
	if (ast_externdecl_kind(decl) != EXTERN_FUNCTION) {
		/* TODO: install global var in table */
		return false;
	}
	return !ast_function_isaxiom(decl->u.function);
	return !ast_function_isaxiom(ast_externdecl_as_function(decl));
}

static void
install_declaration(struct ast_externdecl *decl, struct externals *ext)
{
	struct ast_function *f;
	struct ast_variable *v;
	struct ast_type *t;

	switch (decl->kind) {
	case EXTERN_FUNCTION:
		f = decl->u.function;
		externals_declarefunc(ext, f->name, f);
		break;
	case EXTERN_VARIABLE:
		printf("variable: %s\n", decl->u.variable->name);
		v = decl->u.variable;
		externals_declarevar(ext, v->name, v);
		break;
	case EXTERN_TYPE:
		t = decl->u.type;
		assert(t->base == TYPE_STRUCT && t->u.structunion.tag);
		externals_declaretype(ext, t->u.structunion.tag, t);
		break;
	default:
		assert(false);
	}
}


void
pass1(struct ast *root, struct externals *ext)
{


@@ 225,16 197,14 @@ pass1(struct ast *root, struct externals *ext)
	 */
	for (int i = 0; i < root->n; i++) {
		struct ast_externdecl *decl = root->decl[i];
		install_declaration(decl, ext);
		ast_externdecl_install(decl, ext);
		if (!should_verify(decl)) {
			continue;
		}
		struct ast_function *f = decl->u.function;
		struct ast_function *f = ast_externdecl_as_function(decl);
		/* XXX: ensure that verified functions always have an abstract */
		if (!f->abstract) {
			f->abstract = ast_block_create(NULL, 0, NULL, 0);
		}
		if ((err = function_verify(f, ext))) {
		assert(ast_function_abstract(f));
		if ((err = ast_function_verify(f, ext))) {
			fprintf(stderr, "%s", err->msg);
			exit(EXIT_FAILURE);
		}

M src/ast/ast.c => src/ast/ast.c +77 -2022
@@ 7,2078 7,133 @@
#include "math.h"
#include "util.h"

static struct ast_expr *
ast_expr_create()
{
	return malloc(sizeof(struct ast_expr));
}

struct ast_expr *
ast_expr_identifier_create(char *s)
{
	struct ast_expr *expr = ast_expr_create();
	expr->kind = EXPR_IDENTIFIER;
	expr->u.string = s;
	return expr;
}

char *
ast_expr_as_identifier(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_IDENTIFIER);
	return expr->u.string;
}

static void
ast_expr_destroy_identifier(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_IDENTIFIER);
	free(expr->u.string);
}

struct ast_expr *
ast_expr_constant_create(int k)
{
	/* TODO: generalise for all constant cases */
	struct ast_expr *expr = ast_expr_create();
	expr->kind = EXPR_CONSTANT;
	expr->u.constant = k;
	return expr;
}

int
ast_expr_as_constant(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_CONSTANT);
	return expr->u.constant;
}

struct ast_expr *
ast_expr_literal_create(char *s)
{
	struct ast_expr *expr = ast_expr_create();
	expr->kind = EXPR_STRING_LITERAL;
	expr->u.string = s;
	return expr;
}

char *
ast_expr_as_literal(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_STRING_LITERAL);
	return expr->u.string;
}

static void
ast_expr_destroy_literal(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_STRING_LITERAL);
	free(expr->u.string);
}

struct ast_expr *
ast_expr_bracketed_create(struct ast_expr *root)
{
	struct ast_expr *expr = ast_expr_create();
	expr->kind = EXPR_BRACKETED;
	expr->root = root;
	return expr;
}

static void
ast_expr_bracketed_str_build(struct ast_expr *expr, struct strbuilder *b)
{
	char *root = ast_expr_str(expr->root);
	strbuilder_printf(b, "(%s)", root);
	free(root);
}


struct ast_expr *
ast_expr_iteration_create()
{
	struct ast_expr *expr = ast_expr_create();
	expr->kind = EXPR_ITERATION;
	return expr;
}

struct ast_expr *
ast_expr_call_create(struct ast_expr *root, int narg, struct ast_expr **arg)
{
	struct ast_expr *expr = ast_expr_create();
	expr->kind = EXPR_CALL;
	expr->root = root;
	expr->u.call.n = narg;
	expr->u.call.arg = arg;
	return expr;
}

struct ast_expr *
ast_expr_call_root(struct ast_expr *expr)
{
	return expr->root;
}

int
ast_expr_call_nargs(struct ast_expr *expr)
{
	return expr->u.call.n;
}

struct ast_expr **
ast_expr_call_args(struct ast_expr *expr)
{
	return expr->u.call.arg;
}

static void
ast_expr_call_str_build(struct ast_expr *expr, struct strbuilder *b)
{
	char *root = ast_expr_str(expr->root);
	strbuilder_printf(b, "%s(", root);
	for (int i = 0; i < expr->u.call.n; i++) {
		char *arg = ast_expr_str(expr->u.call.arg[i]);
		strbuilder_printf(b, "%s%s", arg,
			(i + 1 < expr->u.call.n) ? ", " : "");
		free(arg);
	}
	strbuilder_printf(b, ")");
	free(root);
}

static void
ast_expr_destroy_call(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_CALL);
	ast_expr_destroy(expr->root);
	for (int i = 0; i < expr->u.call.n; i++) {
		ast_expr_destroy(expr->u.call.arg[i]);
	}
	free(expr->u.call.arg);
}

static struct ast_expr *
ast_expr_copy_call(struct ast_expr *expr)
{
	struct ast_expr **arg = malloc(sizeof(struct ast_expr *) * expr->u.call.n);
	for (int i = 0; i < expr->u.call.n; i++) {
		arg[i] = ast_expr_copy(expr->u.call.arg[i]);
	}
	return ast_expr_call_create(
		ast_expr_copy(expr->root),
		expr->u.call.n,
		arg
	);
}

struct ast_expr *
ast_expr_incdec_create(struct ast_expr *root, bool inc, bool pre)
{
	struct ast_expr *expr = ast_expr_create();
	expr->kind = EXPR_INCDEC;
	expr->root = root;
	expr->u.incdec.inc = inc;
	expr->u.incdec.pre = pre;
	return expr;
}

struct ast_expr *
ast_expr_incdec_to_assignment(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_INCDEC);

	return ast_expr_assignment_create(
		ast_expr_copy(expr->root),
		ast_expr_binary_create(
			ast_expr_copy(expr->root),
			expr->u.incdec.inc ? BINARY_OP_ADDITION : BINARY_OP_SUBTRACTION,
			ast_expr_constant_create(1)
		)
	);
}

bool
ast_expr_incdec_pre(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_INCDEC);

	return expr->u.incdec.pre;
}

struct ast_expr *
ast_expr_incdec_root(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_INCDEC);

	return expr->root;
}

static void
ast_expr_destroy_incdec(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_INCDEC);
	ast_expr_destroy(expr->root);
}

static void
ast_expr_incdec_str_build(struct ast_expr *expr, struct strbuilder *b)
{
	char *root = ast_expr_str(expr->root);
	char *op = expr->u.incdec.inc ? "++" : "--";
	if (expr->u.incdec.pre) {
		strbuilder_printf(b, "%s%s", op, root);
	} else {
		strbuilder_printf(b, "%s%s", root, op);
	}
	free(root);
}

struct ast_expr *
ast_expr_member_create(struct ast_expr *_struct, char *field)
{
	struct ast_expr *expr = ast_expr_create();
	expr->kind = EXPR_STRUCTMEMBER;
	expr->root = _struct;
	expr->u.string = field;
	return expr;
}

struct ast_expr *
ast_expr_member_root(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_STRUCTMEMBER);
	return expr->root;
}

char *
ast_expr_member_field(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_STRUCTMEMBER);
	return expr->u.string;
}

static void
ast_expr_member_deref_str_build(struct ast_expr *root, char *member,
		struct strbuilder *b);

static void
ast_expr_member_str_build(struct ast_expr *expr, struct strbuilder *b)
{
	struct ast_expr *root = expr->root;

	if (root->kind == EXPR_UNARY) {
		return ast_expr_member_deref_str_build(root, expr->u.string, b);
	}

	char *r = ast_expr_str(root);
	strbuilder_printf(b, "%s.%s", r, expr->u.string);
	free(root);
}

static void
ast_expr_member_deref_str_build(struct ast_expr *root, char *member,
		struct strbuilder *b)
{
	assert(ast_expr_unary_op(root) == UNARY_OP_DEREFERENCE);

	struct ast_expr *inner = ast_expr_unary_operand(root); /* e1+e2 */
	struct ast_expr *e1 = ast_expr_binary_e1(inner),
			*e2 = ast_expr_binary_e2(inner);

	char *left = ast_expr_str(e1);
	if (e2->kind == EXPR_CONSTANT && ast_expr_as_constant(e2) == 0) { 
		strbuilder_printf(b, "%s->%s", left, member);
	} else {
		char *index = ast_expr_str(e2);
		strbuilder_printf(b, "%s[%s].%s", left, index, member);
		free(index);
	}
	free(left);

}

struct ast_expr *
ast_expr_unary_create(struct ast_expr *root, enum ast_unary_operator op)
{
	struct ast_expr *expr = ast_expr_create();
	expr->kind = EXPR_UNARY;
	expr->root = root;
	expr->u.unary_op = op;
	return expr;
}

static void
ast_expr_destroy_unary(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_UNARY);
	ast_expr_destroy(expr->root);
}

enum ast_unary_operator
ast_expr_unary_op(struct ast_expr *expr)
{
	return expr->u.unary_op;
}

struct ast_expr *
ast_expr_unary_operand(struct ast_expr *expr)
{
	return expr->root;
}

static void
ast_expr_unary_str_build(struct ast_expr *expr, struct strbuilder *b)
{
	enum ast_unary_operator op = expr->u.unary_op;
	assert(UNARY_OP_ADDRESS <= op && op <= UNARY_OP_BANG);

	const char opchar[] = {
		[UNARY_OP_ADDRESS]		= '&',
		[UNARY_OP_DEREFERENCE]		= '*',
		[UNARY_OP_POSITIVE]		= '+',
		[UNARY_OP_NEGATIVE]		= '-',
		[UNARY_OP_ONES_COMPLEMENT]	= '~',
		[UNARY_OP_BANG]			= '!',
	};

	char *root = ast_expr_str(expr->root);
	strbuilder_printf(b, "%c(%s)", opchar[op], root);
	free(root);
}

struct ast_expr *
ast_expr_binary_create(struct ast_expr *e1, enum ast_binary_operator op,
		struct ast_expr *e2)
{
	struct ast_expr *expr = ast_expr_create();
	expr->kind = EXPR_BINARY;
	expr->u.binary.e1 = e1;
	expr->u.binary.op = op;
	expr->u.binary.e2 = e2;
	return expr;
}

struct ast_expr *
ast_expr_binary_e1(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_BINARY);
	return expr->u.binary.e1;
}

struct ast_expr *
ast_expr_binary_e2(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_BINARY);
	return expr->u.binary.e2;
}

enum ast_binary_operator
ast_expr_binary_op(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_BINARY);
	return expr->u.binary.op;
}

static void
ast_expr_destroy_binary(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_BINARY);
	ast_expr_destroy(expr->u.binary.e1);
	ast_expr_destroy(expr->u.binary.e2);
}

static struct math_expr *
math_expr(struct ast_expr *);

static void
ast_expr_binary_str_build(struct ast_expr *expr, struct strbuilder *b)
{
	const char *opstr[] = {
		[BINARY_OP_EQV]		= "===",
		[BINARY_OP_IMPL]	= "==>",
		[BINARY_OP_FLLW]	= "<==",

		[BINARY_OP_EQ]		= "==",
		[BINARY_OP_NE]		= "!=",

		[BINARY_OP_LT]		= "<",
		[BINARY_OP_GT]		= ">",
		[BINARY_OP_LE]		= "<=",
		[BINARY_OP_GE]		= ">=",

		[BINARY_OP_ADDITION]	= "+",
		[BINARY_OP_SUBTRACTION]	= "-",
	};
	char *e1 = ast_expr_str(expr->u.binary.e1),
	     *e2 = ast_expr_str(expr->u.binary.e2);
	strbuilder_printf(b, "%s%s%s", e1, opstr[expr->u.binary.op], e2);
	free(e1);
	free(e2);
}

struct ast_expr *
ast_expr_assignment_create(struct ast_expr *root, struct ast_expr *value)
{
	struct ast_expr *expr = ast_expr_create();
	expr->kind = EXPR_ASSIGNMENT;
	expr->root = root;
	expr->u.assignment_value = value;
	return expr;
}

struct ast_expr *
ast_expr_assignment_lval(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_ASSIGNMENT);
	return expr->root;
}

struct ast_expr *
ast_expr_assignment_rval(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_ASSIGNMENT);
	return expr->u.assignment_value;
}

static void
ast_expr_destroy_assignment(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_ASSIGNMENT);
	ast_expr_destroy(expr->root);
	ast_expr_destroy(expr->u.assignment_value);
}

static void
ast_expr_assignment_str_build(struct ast_expr *expr, struct strbuilder *b)
{
	char *root = ast_expr_str(expr->root),
	     *value = ast_expr_str(expr->u.assignment_value);
	strbuilder_printf(b, "%s = %s", root, value);
	free(value);
	free(root);
}

struct ast_expr *
ast_expr_memory_create(enum effect_kind kind, struct ast_expr *expr)
{
	struct ast_expr *new = ast_expr_create();
	new->kind = EXPR_MEMORY;
	new->root = expr;
	new->u.memory.kind = kind;
	return new;
}

struct ast_expr *
ast_expr_memory_root(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_MEMORY);
	return expr->root;
}

bool
ast_expr_memory_isalloc(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_MEMORY);
	return expr->u.memory.kind == EFFECT_ALLOC;
}

bool
ast_expr_memory_isunalloc(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_MEMORY);
	return expr->u.memory.kind == EFFECT_DEALLOC;
}

bool
ast_expr_memory_isundefined(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_MEMORY);
	return expr->u.memory.kind == EFFECT_UNDEFINED;
}

enum effect_kind
ast_expr_memory_kind(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_MEMORY);
	return expr->u.memory.kind;
}

struct effect_mapping {
	enum effect_kind kind;
	const char* string;
};

struct effect_mapping effect_string_map[] = {
	{EFFECT_ALLOC, "alloc"},
	{EFFECT_DEALLOC, "dealloc"},
	{EFFECT_UNDEFINED, "undefined"}
};

const char*
get_effect_string(enum effect_kind kind)
{
	int len = sizeof(effect_string_map) / sizeof(effect_string_map[0]);
	for (int i = 0; i < len; i++) {
		if (effect_string_map[i].kind == kind) {
			return effect_string_map[i].string;
		}
	} 
	return "unknown";
}

static void
ast_expr_memory_str_build(struct ast_expr *expr, struct strbuilder *b)
{
	assert(ast_expr_kind(expr) == EXPR_MEMORY);

	if (ast_expr_memory_kind(expr) == EFFECT_UNDEFINED) {
		strbuilder_printf(b, "undefined");
		return;
	}

	char *root = ast_expr_str(expr->root);

	strbuilder_printf(
		b, "%s %s",
		get_effect_string(ast_expr_memory_kind(expr)),
		root
	);
	free(root);
}

struct ast_expr *
ast_expr_assertion_create(struct ast_expr *assertand)
{
	struct ast_expr *new = ast_expr_create();
	new->kind = EXPR_ASSERTION;
	new->root = assertand;
	return new;
}

struct ast_expr *
ast_expr_assertion_assertand(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_ASSERTION);
	return expr->root;
}

static void
ast_expr_assertion_str_build(struct ast_expr *expr, struct strbuilder *b)
{
	char *root = ast_expr_str(expr->root);
	strbuilder_printf(b, "@%s", root);
	free(root);
}

struct ast_expr *
ast_expr_arbarg_create()
{
	struct ast_expr *expr = ast_expr_create();
	expr->kind = EXPR_ARBARG;
	return expr;
}

void
ast_expr_destroy(struct ast_expr *expr)
{
	switch (expr->kind) {
	case EXPR_IDENTIFIER:
		ast_expr_destroy_identifier(expr);
		break;
	case EXPR_STRING_LITERAL:
		ast_expr_destroy_literal(expr);
		break;
	case EXPR_BRACKETED:
		ast_expr_destroy(expr->root);
		break;
	case EXPR_CALL:
		ast_expr_destroy_call(expr);
		break;
	case EXPR_INCDEC:
		ast_expr_destroy_incdec(expr);
		break;
	case EXPR_STRUCTMEMBER:
		ast_expr_destroy(expr->root); free(expr->u.string);
		break;
	case EXPR_UNARY:
		ast_expr_destroy_unary(expr);
		break;
	case EXPR_BINARY:
		ast_expr_destroy_binary(expr);
		break;
	case EXPR_ASSIGNMENT:
		ast_expr_destroy_assignment(expr);
		break;
	case EXPR_CONSTANT:
		break;
	case EXPR_MEMORY:
		expr->root ? ast_expr_destroy(expr->root) : 0;
		break;
	case EXPR_ASSERTION:
		ast_expr_destroy(expr->root);
		break;
	case EXPR_ARBARG:
		break;
	default:
		assert(false);
	}
	free(expr);
}

char *
ast_expr_str(struct ast_expr *expr)
{
	struct strbuilder *b = strbuilder_create();
	switch (expr->kind) {
	case EXPR_IDENTIFIER:
		strbuilder_printf(b, expr->u.string);
		break;
	case EXPR_CONSTANT:
		strbuilder_printf(b, "%d", expr->u.constant);
		break;
	case EXPR_STRING_LITERAL:
		strbuilder_printf(b, "\"%s\"", expr->u.string);
		break;
	case EXPR_BRACKETED:
		ast_expr_bracketed_str_build(expr, b);
		break;
	case EXPR_CALL:
		ast_expr_call_str_build(expr, b);
		break;
	case EXPR_INCDEC:
		ast_expr_incdec_str_build(expr, b);
		break;
	case EXPR_STRUCTMEMBER:
		ast_expr_member_str_build(expr, b);
		break;
	case EXPR_UNARY:
		ast_expr_unary_str_build(expr, b);
		break;
	case EXPR_BINARY:
		ast_expr_binary_str_build(expr, b);
		break;
	case EXPR_ASSIGNMENT:
		ast_expr_assignment_str_build(expr, b);
		break;
	case EXPR_MEMORY:
		ast_expr_memory_str_build(expr, b);
		break;
	case EXPR_ASSERTION:
		ast_expr_assertion_str_build(expr, b);
		break;
	case EXPR_ARBARG:
		strbuilder_putc(b, '$');
		break;
	default:
		assert(false);
	}
	return strbuilder_build(b);
}

struct ast_expr *
ast_expr_copy(struct ast_expr *expr)
{
	assert(expr);
	switch (expr->kind) {
	case EXPR_IDENTIFIER:
		return ast_expr_identifier_create(dynamic_str(expr->u.string));
	case EXPR_CONSTANT:
		return ast_expr_constant_create(expr->u.constant);
	case EXPR_STRING_LITERAL:
		return ast_expr_literal_create(dynamic_str(expr->u.string));
	case EXPR_BRACKETED:
		return ast_expr_bracketed_create(ast_expr_copy(expr->root));
	case EXPR_CALL:
		return ast_expr_copy_call(expr);
	case EXPR_INCDEC:
		return ast_expr_incdec_create(
			ast_expr_copy(expr->root),
			expr->u.incdec.inc,
			expr->u.incdec.pre
		);
	case EXPR_STRUCTMEMBER:
		return ast_expr_member_create(
			ast_expr_copy(expr->root), dynamic_str(expr->u.string)
		);
	case EXPR_UNARY:
		return ast_expr_unary_create(
			ast_expr_copy(expr->root),
			expr->u.unary_op
		);
	case EXPR_BINARY:
		return ast_expr_binary_create(
			ast_expr_copy(expr->u.binary.e1),
			expr->u.binary.op,
			ast_expr_copy(expr->u.binary.e2)
		);
	case EXPR_ASSIGNMENT:
		return ast_expr_assignment_create(
			ast_expr_copy(expr->root),
			ast_expr_copy(expr->u.assignment_value)
		);
	case EXPR_MEMORY:
		return ast_expr_memory_create(
			expr->u.memory.kind,
			expr->root ? ast_expr_copy(expr->root) : NULL
		);
	case EXPR_ASSERTION:
		return ast_expr_assertion_create(
			ast_expr_copy(expr->root)
		);
	case EXPR_ARBARG:
		return ast_expr_arbarg_create();
	default:
		assert(false);
	}
}

enum ast_expr_kind
ast_expr_kind(struct ast_expr *expr)
{
	return expr->kind;
}

bool
ast_expr_equal(struct ast_expr *e1, struct ast_expr *e2)
{
	if (!e1 || !e2) {
		return false;
	}
	if (e1->kind != e2->kind) {
		return false;	
	}
	switch (e1->kind) {
	case EXPR_CONSTANT:
		return e1->u.constant == e2->u.constant;
	case EXPR_IDENTIFIER:
		return strcmp(ast_expr_as_identifier(e1), ast_expr_as_identifier(e2)) == 0;
	case EXPR_ASSIGNMENT:
		return ast_expr_equal(e1->root, e2->root)
			&& ast_expr_equal(e1->u.assignment_value, e2->u.assignment_value); 
	case EXPR_BINARY:
		return ast_expr_binary_op(e1) == ast_expr_binary_op(e2) &&
			ast_expr_equal(ast_expr_binary_e1(e1), ast_expr_binary_e1(e2)) && 
			ast_expr_equal(ast_expr_binary_e2(e1), ast_expr_binary_e2(e2));
	default:
		assert(false);
	}
}

static bool
eval_prop(struct math_expr *e1, enum ast_binary_operator, struct math_expr *e2);

bool
ast_expr_eval(struct ast_expr *e)
{
	assert(e->kind == EXPR_BINARY);

	struct math_expr *e1 = math_expr(e->u.binary.e1),
			 *e2 = math_expr(e->u.binary.e2);

	bool val = eval_prop(e1, e->u.binary.op, e2);

	math_expr_destroy(e2);
	math_expr_destroy(e1);

	return val;
}

static bool
eval_prop(struct math_expr *e1, enum ast_binary_operator op, struct math_expr *e2)
{
	switch (op) {
	case BINARY_OP_EQ:
		return math_eq(e1, e2);
	case BINARY_OP_NE:
		return !math_eq(e1, e2);
	case BINARY_OP_LT:
		return math_lt(e1, e2);
	case BINARY_OP_GT:
		return math_gt(e1, e2);
	case BINARY_OP_LE:
		return math_le(e1, e2);
	case BINARY_OP_GE:
		return math_ge(e1, e2);
	default:
		assert(false);
	}
}

static struct math_expr *
binary_e2(struct ast_expr *e2, enum ast_binary_operator op);

static struct math_expr *
math_expr(struct ast_expr *e)
{
	switch (e->kind) {
	case EXPR_IDENTIFIER:
		return math_expr_atom_create(
			math_atom_variable_create(dynamic_str(e->u.string))
		);
	case EXPR_CONSTANT:
		if (e->u.constant < 0) {
			return math_expr_neg_create(
				math_expr_atom_create(
					math_atom_nat_create(-e->u.constant)
				)
			);
		}
		return math_expr_atom_create(
			math_atom_nat_create(e->u.constant)
		);
	case EXPR_BINARY:
		return math_expr_sum_create(
			math_expr(e->u.binary.e1),
			binary_e2(e->u.binary.e2, e->u.binary.op)
		);
	default:
		assert(false);
	}
}

static struct math_expr *
binary_e2(struct ast_expr *e2, enum ast_binary_operator op)
{
	switch (op) {
	case BINARY_OP_ADDITION:
		return math_expr(e2);
	case BINARY_OP_SUBTRACTION:
		return math_expr_neg_create(math_expr(e2));
	default:
		assert(false);
	}
}

struct ast_block *
ast_block_create(struct ast_variable **decl, int ndecl, 
	struct ast_stmt **stmt, int nstmt)
{
	struct ast_block *b = malloc(sizeof(struct ast_block));
	b->decl = decl;
	b->ndecl = ndecl;
	b->stmt = stmt;
	b->nstmt = nstmt;
	return b;
}

void
ast_block_destroy(struct ast_block *b)
{
	for (int i = 0; i < b->ndecl; i++) {
		ast_variable_destroy(b->decl[i]);
	}
	free(b->decl);
	for (int i = 0; i < b->nstmt; i++) {
		ast_stmt_destroy(b->stmt[i]);
	}
	free(b->stmt);
	free(b);
}

static struct ast_variable **
copy_var_arr(int len, struct ast_variable **);

static struct ast_stmt **
copy_stmt_arr(int len, struct ast_stmt **);

struct ast_block *
ast_block_copy(struct ast_block *b)
{
	assert(b);
	return ast_block_create(
		copy_var_arr(b->ndecl, b->decl),
		b->ndecl,
		copy_stmt_arr(b->nstmt, b->stmt),
		b->nstmt
	);
}

static struct ast_variable **
copy_var_arr(int len, struct ast_variable **var)
{
	assert(len == 0 || var);
	if (len == 0) {
		return NULL;
	}
	struct ast_variable **new = malloc(sizeof(struct ast_variable *) * len); 
	for (int i = 0; i < len; i++) {
		new[i] = ast_variable_copy(var[i]);
	}
	return new;
}

static struct ast_stmt **
copy_stmt_arr(int len, struct ast_stmt **stmt)
{
	assert(len == 0 || stmt);
	if (len == 0) {
		return NULL;
	}
	struct ast_stmt **new = malloc(sizeof(struct ast_stmt *) * len); 
	for (int i = 0; i < len; i++) {
		new[i] = ast_stmt_copy(stmt[i]);
	}
	return new;
}

char *
ast_block_str(struct ast_block *b)
{
	struct strbuilder *sb = strbuilder_create();
	for (int i = 0; i < b->ndecl; i++) {
		char *s = ast_variable_str(b->decl[i]);
		strbuilder_printf(sb, "%s;\n", s);
		free(s);
	}
	for (int i = 0; i < b->nstmt; i++) {
		char *s = ast_stmt_str(b->stmt[i]);
		strbuilder_printf(sb, "%s\n", s);
		free(s);
	}
	return strbuilder_build(sb);
}

int
ast_block_ndecls(struct ast_block *b)
{
	return b->ndecl;
}

struct ast_variable **
ast_block_decls(struct ast_block *b)
{
	assert(b->ndecl > 0 || !b->decl);
	return b->decl;
}

int
ast_block_nstmts(struct ast_block *b)
{
	return b->nstmt;
}

struct ast_stmt **
ast_block_stmts(struct ast_block *b)
{
	assert(b->nstmt > 0 || !b->stmt);
	return b->stmt;
}

static struct ast_stmt *
ast_stmt_create(struct lexememarker *loc)
{
	struct ast_stmt *stmt = calloc(1, sizeof(struct ast_stmt));
	stmt->loc = loc;
	return stmt;
}

struct ast_stmt *
ast_stmt_create_labelled(struct lexememarker *loc, char *label,
		struct ast_stmt *substmt)
{
	struct ast_stmt *stmt = ast_stmt_create(loc);
	stmt->kind = STMT_LABELLED;
	stmt->u.labelled.label = label;
	stmt->u.labelled.stmt = substmt;
	return stmt;
}

char *
ast_stmt_labelled_label(struct ast_stmt *stmt)
{
	assert(stmt->kind == STMT_LABELLED);

	return stmt->u.labelled.label;
}

struct ast_stmt *
ast_stmt_labelled_stmt(struct ast_stmt *stmt)
{
	assert(stmt->kind == STMT_LABELLED);

	return stmt->u.labelled.stmt;
}

static void
ast_stmt_labelled_sprint(struct ast_stmt *stmt, struct strbuilder *b)
{
	char *s = ast_stmt_str(stmt->u.labelled.stmt);
	strbuilder_printf(b, "%s: %s", stmt->u.labelled.label, s);
	free(s);
}

struct ast_stmt *
ast_stmt_create_nop(struct lexememarker *loc)
{
	struct ast_stmt *stmt = ast_stmt_create(loc);
	stmt->kind = STMT_NOP;
	return stmt;
}

static void
ast_stmt_nop_sprint(struct ast_stmt *stmt, struct strbuilder *b)
{
	strbuilder_printf(b, ";");
}

struct ast_stmt *
ast_stmt_create_expr(struct lexememarker *loc, struct ast_expr *expr)
{
	struct ast_stmt *stmt = ast_stmt_create(loc);
	stmt->kind = STMT_EXPR;
	stmt->u.expr = expr;
	return stmt;
}

static void
ast_stmt_expr_sprint(struct ast_stmt *stmt, struct strbuilder *b)
{
	assert(stmt->kind == STMT_EXPR);
	char *s = ast_expr_str(stmt->u.expr);
	strbuilder_printf(b, "%s;", s);
	free(s);
}

struct ast_stmt *
ast_stmt_create_compound(struct lexememarker *loc, struct ast_block *b)
{
	struct ast_stmt *stmt = ast_stmt_create(loc);
	stmt->kind = STMT_COMPOUND;
	stmt->u.compound = b;
	return stmt;
}

struct ast_block *
ast_stmt_as_block(struct ast_stmt *stmt)
{
	assert(stmt->kind == STMT_COMPOUND);
	return stmt->u.compound;
}

static void
ast_stmt_compound_sprint(struct ast_stmt *stmt, struct strbuilder *b)
{
	assert(stmt->kind == STMT_COMPOUND || stmt->kind == STMT_COMPOUND_V);
	char *s = ast_block_str(stmt->u.compound);
	strbuilder_printf(b, s);
	free(s);
}

struct ast_stmt *
ast_stmt_create_compound_v(struct lexememarker *loc, struct ast_block *b)
{
	struct ast_stmt *stmt = ast_stmt_create(loc);
	stmt->kind = STMT_COMPOUND_V;
	stmt->u.compound = b;
	return stmt;
}

struct ast_stmt *
ast_stmt_create_jump(struct lexememarker *loc, enum ast_jump_kind kind,
		struct ast_expr *rv)
{
	struct ast_stmt *stmt = ast_stmt_create(loc);
	stmt->kind = STMT_JUMP;
	stmt->u.jump.kind = JUMP_RETURN;
	stmt->u.jump.rv = rv;
	return stmt;
}

struct ast_expr *
ast_stmt_jump_rv(struct ast_stmt *stmt)
{
	assert(stmt->kind == STMT_JUMP && stmt->u.jump.kind == JUMP_RETURN);
	return stmt->u.jump.rv;
}

static void
ast_stmt_destroy_jump(struct ast_stmt *stmt)
{
	struct ast_expr *rv = stmt->u.jump.rv;
	if (!rv) {
		return;
	}
	assert(stmt->u.jump.kind == JUMP_RETURN);
	ast_expr_destroy(rv);
}

struct ast_stmt *
ast_stmt_create_sel(struct lexememarker *loc, bool isswitch, struct ast_expr *cond,
		struct ast_stmt *body, struct ast_stmt *nest)
{
	assert(!isswitch); /* XXX */
	assert(cond);
	struct ast_stmt *stmt = ast_stmt_create(loc);
	stmt->kind = STMT_SELECTION;
	stmt->u.selection.isswitch = isswitch;
	stmt->u.selection.cond = cond;
	stmt->u.selection.body = body;
	stmt->u.selection.nest = nest;
	return stmt;
}

struct ast_expr *
ast_stmt_sel_cond(struct ast_stmt *stmt)
{
	assert(stmt->kind == STMT_SELECTION);
	return stmt->u.selection.cond;
}

struct ast_stmt *
ast_stmt_sel_body(struct ast_stmt *stmt)
{
	assert(stmt->kind == STMT_SELECTION);
	return stmt->u.selection.body;
}

struct ast_stmt *
ast_stmt_sel_nest(struct ast_stmt *stmt)
{
	assert(stmt->kind == STMT_SELECTION);
	return stmt->u.selection.nest;
}

static void
ast_stmt_sel_sprint(struct ast_stmt *stmt, struct strbuilder *b)
{
	assert(stmt->kind == STMT_SELECTION);
	char *cond	= ast_expr_str(stmt->u.selection.cond),
	     *body	= ast_stmt_str(stmt->u.selection.body);

	/* XXX: we only support simple IF for now */
	strbuilder_printf(
		b,
		"if (%s) { %s }",
		cond, body
	);

	struct ast_stmt *nest_stmt = stmt->u.selection.nest;
	if (nest_stmt) {
		char *nest = ast_stmt_str(nest_stmt);
		strbuilder_printf(
			b,
			" else %s",
			nest
		);
		free(nest);
	}

	free(cond); free(body);
}

struct ast_stmt *
ast_stmt_create_iter(struct lexememarker *loc,
		struct ast_stmt *init, struct ast_stmt *cond,
		struct ast_expr *iter, struct ast_block *abstract,
		struct ast_stmt *body)
{
	assert(init && cond && iter && abstract && body);
	struct ast_stmt *stmt = ast_stmt_create(loc);
	stmt->kind = STMT_ITERATION;
	stmt->u.iteration.init = init;
	stmt->u.iteration.cond = cond;
	stmt->u.iteration.iter = iter;
	stmt->u.iteration.body = body;
	stmt->u.iteration.abstract = abstract;
	return stmt;
}

struct ast_stmt *
ast_stmt_iter_init(struct ast_stmt *stmt)
{
	assert(stmt->kind == STMT_ITERATION);
	return stmt->u.iteration.init;
}

struct ast_stmt *
ast_stmt_iter_cond(struct ast_stmt *stmt)
{
	assert(stmt->kind == STMT_ITERATION);
	return stmt->u.iteration.cond;
}

struct ast_expr *
ast_stmt_iter_iter(struct ast_stmt *stmt)
{
	assert(stmt->kind == STMT_ITERATION);
	return stmt->u.iteration.iter;
}

struct ast_block *
ast_stmt_iter_abstract(struct ast_stmt *stmt)
{
	assert(stmt->kind == STMT_ITERATION);
	return stmt->u.iteration.abstract;
}

struct ast_stmt *
ast_stmt_iter_body(struct ast_stmt *stmt)
{
	assert(stmt->kind == STMT_ITERATION);
	return stmt->u.iteration.body;
}

struct ast_expr *
ast_stmt_iter_lower_bound(struct ast_stmt *stmt)
{
	assert(stmt->kind == STMT_ITERATION);
	struct ast_stmt *init = stmt->u.iteration.init;
	assert(init->kind == STMT_EXPR);
	struct ast_expr *expr = init->u.expr;
	assert(expr->kind == EXPR_ASSIGNMENT);
	return expr->u.assignment_value;
}

struct ast_expr *
ast_stmt_iter_upper_bound(struct ast_stmt *stmt)
{
	assert(stmt->kind == STMT_ITERATION);
	struct ast_stmt *cond = stmt->u.iteration.cond;
	assert(cond->kind == STMT_EXPR);
	struct ast_expr *expr = cond->u.expr;
	assert(expr->kind == EXPR_BINARY);
	return expr->u.binary.e2;
}

static struct ast_stmt *
ast_stmt_copy_iter(struct ast_stmt *stmt)
{
	stmt->kind = STMT_ITERATION;
	struct ast_stmt *copy = ast_stmt_copy(stmt);
	stmt->kind = STMT_ITERATION_E;
	return ast_stmt_create_iter_e(copy);
}

static void
ast_stmt_iter_sprint(struct ast_stmt *stmt, struct strbuilder *b)
{
	assert(stmt->kind == STMT_ITERATION);
	char *init = ast_stmt_str(stmt->u.iteration.init),
	     *cond = ast_stmt_str(stmt->u.iteration.cond),
	     *body = ast_stmt_str(stmt->u.iteration.body),
	     *iter = ast_expr_str(stmt->u.iteration.iter);

	char *abs = stmt->u.iteration.abstract ?
		ast_block_str(stmt->u.iteration.abstract) : "";

	strbuilder_printf(
		b,
		"for (%s %s %s) [%s] { %s }",
		init, cond, iter, abs, body
	);

	free(init); free(cond); free(body); free(iter);
}

struct ast_stmt *
ast_stmt_create_iter_e(struct ast_stmt *stmt)
{
	/* TODO: determine where loc should go */
	assert(stmt->kind == STMT_ITERATION);
	stmt->kind = STMT_ITERATION_E;
	return stmt;
}

static void
ast_stmt_iter_e_sprint(struct ast_stmt *stmt, struct strbuilder *b)
{
	assert(stmt->kind == STMT_ITERATION_E);
	stmt->kind = STMT_ITERATION;
	char *s = ast_stmt_str(stmt);
	stmt->kind = STMT_ITERATION_E;
	strbuilder_printf(b, ".%s", s);
	free(s);
}

static void
ast_stmt_jump_sprint(struct ast_stmt *stmt, struct strbuilder *b)
{
	assert(stmt->kind == STMT_JUMP);
	char *rv = ast_expr_str(stmt->u.jump.rv);

	strbuilder_printf(
		b,
		"return %s;\n",
		rv
	);

	free(rv);
}
#include "expr/expr.c"
#include "block.c"
#include "stmt/stmt.c"
#include "type.c"
#include "variable.c"
#include "function.c"
#include "externdecl.c"

static struct ast_expr *
ast_expr_copy_ifnotnull(struct ast_expr *expr)
struct ast *
ast_create(struct ast_externdecl *decl)
{
	return expr ? ast_expr_copy(expr) : NULL;
	struct ast *node = calloc(1, sizeof(struct ast));
	return ast_append(node, decl);
}

void
ast_stmt_destroy(struct ast_stmt *stmt)
{
	switch (stmt->kind) {
	case STMT_LABELLED:
		free(stmt->u.labelled.label);
		ast_stmt_destroy(stmt->u.labelled.stmt);
		break;
	case STMT_NOP:
		break;
	case STMT_COMPOUND:
	case STMT_COMPOUND_V:
		ast_block_destroy(stmt->u.compound);
		break;
	case STMT_SELECTION:
		ast_expr_destroy(stmt->u.selection.cond);
		ast_stmt_destroy(stmt->u.selection.body);
		if (stmt->u.selection.nest) {
			ast_stmt_destroy(stmt->u.selection.nest);
		}
		break;
	case STMT_ITERATION:
	case STMT_ITERATION_E:
		ast_stmt_destroy(stmt->u.iteration.init);
		ast_stmt_destroy(stmt->u.iteration.cond);
		ast_stmt_destroy(stmt->u.iteration.body);
		ast_expr_destroy(stmt->u.iteration.iter);
		ast_block_destroy(stmt->u.iteration.abstract);
		break;
	case STMT_EXPR:
		ast_expr_destroy(stmt->u.expr);
		break;
	case STMT_JUMP:
		ast_stmt_destroy_jump(stmt);
		break;
	default:
		assert(false);
		break;
	}
	if (stmt->loc) {
		lexememarker_destroy(stmt->loc);
	}
	free(stmt);
}

struct ast_stmt *
ast_stmt_copy(struct ast_stmt *stmt)
{
	struct lexememarker *loc = stmt->loc
		? lexememarker_copy(stmt->loc)
		: NULL;
	switch (stmt->kind) {
	case STMT_LABELLED:
		return ast_stmt_create_labelled(
			loc,
			dynamic_str(stmt->u.labelled.label),
			ast_stmt_copy(stmt->u.labelled.stmt)
		);
	case STMT_NOP:
		return ast_stmt_create_nop(loc);
	case STMT_EXPR:
		return ast_stmt_create_expr(loc, ast_expr_copy(stmt->u.expr));
	case STMT_COMPOUND:
		return ast_stmt_create_compound(
			loc, ast_block_copy(stmt->u.compound)
		);
	case STMT_COMPOUND_V:
		return ast_stmt_create_compound_v(
			loc, ast_block_copy(stmt->u.compound)
		);
	case STMT_SELECTION:
		return ast_stmt_create_sel(
			loc,
			stmt->u.selection.isswitch,
			ast_expr_copy(stmt->u.selection.cond),
			ast_stmt_copy(stmt->u.selection.body),
			stmt->u.selection.nest
				? ast_stmt_copy(stmt->u.selection.nest)
				: NULL
		);
	case STMT_ITERATION:
		return ast_stmt_create_iter(
			loc,
			ast_stmt_copy(stmt->u.iteration.init),
			ast_stmt_copy(stmt->u.iteration.cond),
			ast_expr_copy(stmt->u.iteration.iter),
			ast_block_copy(stmt->u.iteration.abstract),
			ast_stmt_copy(stmt->u.iteration.body)
		);
	case STMT_ITERATION_E:
		return ast_stmt_copy_iter(stmt);
	case STMT_JUMP:
		return ast_stmt_create_jump(
			loc, stmt->u.jump.kind,
			ast_expr_copy_ifnotnull(stmt->u.jump.rv)
		);
	default:
		assert(false);
	}
}

char *
ast_stmt_str(struct ast_stmt *stmt)
{
	struct strbuilder *b = strbuilder_create();
	switch (stmt->kind) {
	case STMT_LABELLED:
		ast_stmt_labelled_sprint(stmt, b);
		break;
	case STMT_NOP:
		ast_stmt_nop_sprint(stmt, b);
		break;
	case STMT_EXPR:
		ast_stmt_expr_sprint(stmt, b);
		break;
	case STMT_COMPOUND:
		ast_stmt_compound_sprint(stmt, b);
		break;
	case STMT_COMPOUND_V:
		ast_stmt_compound_sprint(stmt, b);
		break;
	case STMT_SELECTION:
		ast_stmt_sel_sprint(stmt, b);
		break;
	case STMT_ITERATION:
		ast_stmt_iter_sprint(stmt, b);
		break;
	case STMT_ITERATION_E:
		ast_stmt_iter_e_sprint(stmt, b);
		break;
	case STMT_JUMP:
		ast_stmt_jump_sprint(stmt, b);
		break;
	default:
		assert(false);
	}
	return strbuilder_build(b);
}

bool
ast_stmt_equal(struct ast_stmt *s1, struct ast_stmt *s2)
ast_destroy(struct ast *node)
{
	if (!s1 || !s2) {
		return false;
	}
	if (ast_stmt_kind(s1) != ast_stmt_kind(s2)) {
		return false;
	}
	switch (ast_stmt_kind(s1)) {
	case STMT_EXPR:
		return ast_expr_equal(ast_stmt_as_expr(s1), ast_stmt_as_expr(s2));
	default:
		assert(false);
	for (int i = 0; i < node->n; i++) {
		ast_externdecl_destroy(node->decl[i]);
	}
	free(node->decl);
	free(node);
}

enum ast_stmt_kind
ast_stmt_kind(struct ast_stmt *stmt)
{
	return stmt->kind;
}

struct ast_block *
ast_stmt_as_v_block(struct ast_stmt *stmt)
{
	assert(stmt->kind == STMT_COMPOUND_V);
	return stmt->u.compound;
}

struct ast_expr *
ast_stmt_as_expr(struct ast_stmt *stmt)
{
	assert(stmt->kind == STMT_EXPR);
	return stmt->u.expr;
}

struct ast_type *
ast_type_create(enum ast_type_base base, enum ast_type_modifier mod)
{
	struct ast_type *t = malloc(sizeof(struct ast_type));
	assert(t);
	t->base = base;
	t->mod = mod;
	return t;
}

struct ast_type *
ast_type_create_ptr(struct ast_type *ref)
{
	assert(ref);
	struct ast_type *t = ast_type_create(TYPE_POINTER, 0);
	t->u.ptr_type = ref;
	return t;
}

struct ast_type *
ast_type_create_arr(struct ast_type *base, int length)
{
	assert(base);
	struct ast_type *t = ast_type_create(TYPE_ARRAY, 0);
	t->u.arr.type = base;
	t->u.arr.length = length;
	return t;
}

struct ast_type *
ast_type_create_typedef(struct ast_type *base, char *name)
{
	assert(base);
	struct ast_type *t = ast_type_create(TYPE_TYPEDEF, 0);
	t->u._typedef.type = base;
	t->u._typedef.name = name;
	return t;
}

struct ast_type *
ast_type_create_struct(char *tag, struct ast_variable_arr *members)
{
	struct ast_type *t = ast_type_create(TYPE_STRUCT, 0);
	t->u.structunion.tag = tag;
	t->u.structunion.members = members;
	return t;
}

struct ast_variable_arr *
ast_type_struct_members(struct ast_type *t)
{
	assert(t->base == TYPE_STRUCT);

	return t->u.structunion.members;
}

char *
ast_type_struct_tag(struct ast_type *t)
{
	assert(t->base == TYPE_STRUCT);

	return t->u.structunion.tag;
}

struct ast_type *
ast_type_create_struct_anonym(struct ast_variable_arr *members)
{
	return ast_type_create_struct(NULL, members);
}

struct ast_type *
ast_type_create_struct_partial(char *tag)
{
	return ast_type_create_struct(tag, NULL);
}

struct ast_type *
ast_type_copy_struct(struct ast_type *old)
struct ast *
ast_append(struct ast *node, struct ast_externdecl *decl)
{
	assert(old->base == TYPE_STRUCT);

	struct ast_type *new = ast_type_create(TYPE_STRUCT, old->mod);
	new->u.structunion.tag = old->u.structunion.tag
		? dynamic_str(old->u.structunion.tag)
		: NULL;
	new->u.structunion.members = old->u.structunion.members
		? ast_variable_arr_copy(old->u.structunion.members)
		: NULL;
	return new;
	node->decl = realloc(node->decl,
		sizeof(struct ast_externdecl *) * ++node->n);
	node->decl[node->n-1] = decl;
	return node;
}


struct ast_variable_arr {
	int n;
	struct ast_variable **v;
struct result {
	struct value *val;
	struct error *err;
};

struct ast_variable_arr *
ast_variable_arr_create()
{
	return calloc(1, sizeof(struct ast_variable_arr));
}

void
ast_variable_arr_append(struct ast_variable_arr *arr, struct ast_variable *v)
{
	arr->v = realloc(arr->v, sizeof(struct ast_variable *) * ++arr->n);
	arr->v[arr->n-1] = v;
}

void
ast_variable_arr_destroy(struct ast_variable_arr *arr)
{
	for (int i = 0; i < arr->n; i++) {
		ast_variable_destroy(arr->v[i]);
	}
	free(arr);
}

int
ast_variable_arr_n(struct ast_variable_arr *arr)
{
	return arr->n;
}

struct ast_variable **
ast_variable_arr_v(struct ast_variable_arr *arr)
{
	return arr->v;
}

struct ast_variable_arr *
ast_variable_arr_copy(struct ast_variable_arr *old)
{
	struct ast_variable_arr *new = ast_variable_arr_create();
	for (int i = 0; i < old->n; i++) {
		ast_variable_arr_append(new, ast_variable_copy(old->v[i]));
	}
	return new;
}

void
ast_type_destroy(struct ast_type *t)
{
	switch (t->base) {
	case TYPE_TYPEDEF:
		/* XXX: typedef broken type not populating */
		assert(false);
		assert(t->u._typedef.type);
		ast_type_destroy(t->u._typedef.type);
		assert(t->u._typedef.name);
		free(t->u._typedef.name);
		break;
	case TYPE_POINTER:
		assert(t->u.ptr_type);
		ast_type_destroy(t->u.ptr_type);
		break;
	case TYPE_ARRAY:
		assert(t->u.arr.type);
		ast_type_destroy(t->u.arr.type);
	default:
		break;
	}
	free(t);
}

struct ast_type *
ast_type_copy(struct ast_type *t)
{
	assert(t);
	switch (t->base) {
	case TYPE_TYPEDEF:
		return ast_type_create_typedef(
			ast_type_copy(t->u._typedef.type),
			t->u._typedef.name
				? dynamic_str(t->u._typedef.name)
				: NULL
		);
	case TYPE_POINTER:
		return ast_type_create_ptr(
			ast_type_copy(t->u.ptr_type)
		);
	case TYPE_ARRAY:
		return ast_type_create_arr(
			ast_type_copy(t->u.arr.type),
			t->u.arr.length
		);
	case TYPE_STRUCT:
		return ast_type_copy_struct(t);

	case TYPE_VOID:
	case TYPE_INT:
	case TYPE_CHAR:
		return ast_type_create(t->base, t->mod);

	default:
		assert(false);
	}
}

static void
ast_type_str_build_ptr(struct strbuilder *b, struct ast_type *t);

static void
ast_type_str_build_arr(struct strbuilder *b, struct ast_type *t);

static void
ast_type_str_build_typedef(struct strbuilder *b, struct ast_type *t);

static void
ast_type_str_build_struct(struct strbuilder *b, struct ast_type *t);

char *
ast_type_str(struct ast_type *t)
{
	assert(t);
	/* XXX */
	const char *modstr[] = {
		[MOD_EXTERN]	= "extern",
		[MOD_AUTO]	= "auto",
		[MOD_STATIC]	= "static",
		[MOD_REGISTER]	= "register",

		[MOD_CONST]	= "const",
		[MOD_VOLATILE]	= "volatile",
	};
	const int modlen = 6;
	const char *basestr[] = {
		[TYPE_VOID]	= "void",
		[TYPE_CHAR]	= "char",
		[TYPE_SHORT]	= "short",
		[TYPE_INT]	= "int",
		[TYPE_LONG]	= "long",
		[TYPE_FLOAT]	= "float",
		[TYPE_DOUBLE]	= "double",
		[TYPE_SIGNED]	= "signed",
		[TYPE_UNSIGNED]	= "unsigned",
	};
	struct strbuilder *b = strbuilder_create();
	int nmods = 0;
	for (int i = 0; i < modlen; i++) {
		if (1 << i & t->mod) {
			nmods++;
		}
	}
	for (int i = 0; i < modlen; i++) {
		int mod = 1 << i;
		if (mod & t->mod) {
			char *space = --nmods ? " " : "";
			strbuilder_printf(b, "%s%s", modstr[mod], space);
		}
	}
	switch (t->base) {
	case TYPE_TYPEDEF:
		ast_type_str_build_typedef(b, t);
		break;
	case TYPE_POINTER:
		ast_type_str_build_ptr(b, t);
		break;
	case TYPE_ARRAY:
		ast_type_str_build_arr(b, t);
		break;
	case TYPE_STRUCT:
		ast_type_str_build_struct(b, t);
		break;
	default:
		strbuilder_printf(b, basestr[t->base]);
		break;
	}
	return strbuilder_build(b);
}

static void
ast_type_str_build_ptr(struct strbuilder *b, struct ast_type *t)
{
	char *base = ast_type_str(t->u.ptr_type);
	bool space = t->u.ptr_type->base != TYPE_POINTER;
	strbuilder_printf(b, "%s%s*", base, space ? " " : "");
	free(base);
}

static void
ast_type_str_build_arr(struct strbuilder *b, struct ast_type *t)
{
	char *base = ast_type_str(t->u.arr.type);
	strbuilder_printf(b, "%s[%d]", base, t->u.arr.length);
	free(base);
}

static void
ast_type_str_build_typedef(struct strbuilder *b, struct ast_type *t)
{
	char *base = ast_type_str(t->u._typedef.type);
	strbuilder_printf(b, "typedef %s %s", base, t->u._typedef.type);
	free(base);
}

static void
ast_type_str_build_struct(struct strbuilder *b, struct ast_type *t)
{
	char *tag = t->u.structunion.tag;
	struct ast_variable_arr *members = t->u.structunion.members;

	assert(tag || members);

	strbuilder_printf(b, "struct ");

	if (tag) {
		strbuilder_printf(b, tag);
	}

	if (!members) {
		return;
	}

	strbuilder_printf(b, " { ");
	int n = ast_variable_arr_n(members);
	struct ast_variable **v = ast_variable_arr_v(members);
	for (int i = 0; i < n; i++) {
		char *s = ast_variable_str(v[i]);
		strbuilder_printf(b, "%s; ", s);
		free(s);
	}
	strbuilder_printf(b, "}");
}

enum ast_type_base
ast_type_base(struct ast_type *t)
{
	return t->base;
}

struct ast_type *
ast_type_ptr_type(struct ast_type *t)
{
	assert(t->base == TYPE_POINTER);
	return t->u.ptr_type;
}

struct ast_variable *
ast_variable_create(char *name, struct ast_type *type)
{
	struct ast_variable *v = malloc(sizeof(struct ast_variable));
	v->name = name;
	v->type = type;
	return v;
}

void
ast_variable_destroy(struct ast_variable *v)
{
	ast_type_destroy(v->type);
	free(v->name);
	free(v);
}

struct ast_variable *
ast_variable_copy(struct ast_variable *v)
{
	assert(v);
	return ast_variable_create(
		dynamic_str(v->name), ast_type_copy(v->type)
	);
}

struct ast_variable **
ast_variables_copy(int n, struct ast_variable **v)
{
	assert(v);
	struct ast_variable **new = calloc(n, sizeof(struct variable *));
	for (int i = 0; i < n; i++) {
		new[i] = ast_variable_copy(v[i]);
	}
	return new;
}


char *
ast_variable_str(struct ast_variable *v)
{
	struct strbuilder *b = strbuilder_create();
	char *t = ast_type_str(v->type);
	
	strbuilder_printf(b, "%s %s", t, v->name);
	free(t);
	return strbuilder_build(b);
}

char *
ast_variable_name(struct ast_variable *v)
struct result *
result_error_create(struct error *err)
{
	return v->name;
}
	assert(err);

struct ast_type *
ast_variable_type(struct ast_variable *v)
{
	return v->type;
	struct result *r = malloc(sizeof(struct result));
	r->val = NULL;
	r->err = err;
	return r;
}

struct ast_function *
ast_function_create(
	bool isaxiom,
	struct ast_type *ret,
	char *name, 
	int nparam,
	struct ast_variable **param,
	struct ast_block *abstract, 
	struct ast_block *body)
struct result *
result_value_create(struct value *val)
{
	struct ast_function *f = malloc(sizeof(struct ast_function));
	f->isaxiom = isaxiom;
	f->ret = ret;
	f->name = name;
	f->nparam = nparam;
	f->param = param;
	assert(abstract);
	f->abstract = abstract;
	f->body = body;
	return f;
	struct result *r = malloc(sizeof(struct result));
	r->val = val;
	r->err = NULL;
	return r;
}

void
ast_function_destroy(struct ast_function *f)
result_destroy(struct result *res)
{
	ast_type_destroy(f->ret);
	for (int i = 0; i < f->nparam; i++) {
		ast_variable_destroy(f->param[i]);
	assert(!res->err);
	if (res->val) {
		value_destroy(res->val);
	}
	ast_block_destroy(f->abstract);
	if (f->body) {
		ast_block_destroy(f->body);
	}
	free(f->param);
	free(f->name);
	free(f);
}

char *
ast_function_str(struct ast_function *f)
{
	struct strbuilder *b = strbuilder_create();
	strbuilder_printf(b, "func");
	if (f->isaxiom) {
		strbuilder_printf(b, " <axiom>");
	}
	strbuilder_printf(b, " `%s'", f->name);
	char *ret = ast_type_str(f->ret);
	strbuilder_printf(b, " returns %s ", ret);
	free(ret);
	strbuilder_printf(b, "takes [");
	for (int i = 0; i < f->nparam; i++) {
		char *v = ast_variable_str(f->param[i]);
		char *space = (i + 1 < f->nparam) ? ", " : "";
		strbuilder_printf(b, "%s%s", v, space);
		free(v);
	}
	strbuilder_printf(b, "] has abstract:\n%s", ast_block_str(f->abstract));
	return strbuilder_build(b);
}

char *
ast_function_name(struct ast_function *f)
{
	return f->name;
}

struct ast_function *
ast_function_copy(struct ast_function *f)
{
	assert(f);
	struct ast_variable **param = malloc(sizeof(struct ast_variable *) * f->nparam);
	for (int i = 0; i < f->nparam; i++) {
		param[i] = ast_variable_copy(f->param[i]);
	}
	return ast_function_create(
		f->isaxiom,
		ast_type_copy(f->ret),
		dynamic_str(f->name),
		f->nparam,
		param,
		ast_block_copy(f->abstract),
		f->body ? ast_block_copy(f->body) : NULL
	);
	free(res);
}

bool
ast_function_isaxiom(struct ast_function *f)
{
	return f->isaxiom;
}

struct ast_type *
ast_function_type(struct ast_function *f)
{
	return f->ret;
}

struct ast_block *
ast_function_body(struct ast_function *f)
result_iserror(struct result *res)
{
	assert(f->body);
	return f->body;
	return res->err;
}

struct ast_block *
ast_function_abstract(struct ast_function *f)
struct error *
result_as_error(struct result *res)
{
	assert(f->abstract);
	return f->abstract;
	assert(res->err);
	return res->err;
}

int
ast_function_nparams(struct ast_function *f)
struct value *
result_as_value(struct result *res)
{
	return f->nparam;
	assert(!res->err && res->val);
	return res->val;
}

struct ast_variable **
ast_function_params(struct ast_function *f)
bool
result_hasvalue(struct result *res)
{
	return f->param;
	assert(!result_iserror(res));
	return res->val; /* implicit cast */
}

struct ast_externdecl *
ast_functiondecl_create(struct ast_function *f)
{
	struct ast_externdecl *decl = malloc(sizeof(struct ast_externdecl));
	decl->kind = EXTERN_FUNCTION;
	decl->u.function = f;
	return decl;
}

struct ast_externdecl *
ast_variabledecl_create(struct ast_variable *v)
{
	struct ast_externdecl *decl = malloc(sizeof(struct ast_externdecl));
	decl->kind = EXTERN_VARIABLE;
	decl->u.variable = v;
	return decl;
}
struct lvalue {
	struct ast_type *t;
	struct object *obj;
};

struct ast_externdecl *
ast_typedecl_create(struct ast_type *t)
struct lvalue *
lvalue_create(struct ast_type *t, struct object *obj)
{
	struct ast_externdecl *decl = malloc(sizeof(struct ast_externdecl));
	decl->kind = EXTERN_TYPE;
	decl->u.type = t;
	return decl;
	struct lvalue *l = malloc(sizeof(struct lvalue));
	l->t = t;
	l->obj = obj;
	return l;
}

void
ast_externdecl_destroy(struct ast_externdecl *decl)
{
	switch (decl->kind) {
	case EXTERN_FUNCTION:
		ast_function_destroy(decl->u.function);
		break;
	case EXTERN_VARIABLE:
		ast_variable_destroy(decl->u.variable);
		break;
	case EXTERN_TYPE:
		ast_type_destroy(decl->u.type);
		break;
	default:
		assert(false);
	}
	free(decl);
}

struct ast *
ast_create(struct ast_externdecl *decl)
lvalue_destroy(struct lvalue *l)
{
	struct ast *node = calloc(1, sizeof(struct ast));
	return ast_append(node, decl);
	ast_type_destroy(l->t);
	object_destroy(l->obj);
	free(l);
}

void
ast_destroy(struct ast *node)
struct ast_type *
lvalue_type(struct lvalue *l)
{
	for (int i = 0; i < node->n; i++) {
		ast_externdecl_destroy(node->decl[i]);
	}
	free(node->decl);
	free(node);
	return l->t;
}

struct ast *
ast_append(struct ast *node, struct ast_externdecl *decl)
struct object *
lvalue_object(struct lvalue *l)
{
	node->decl = realloc(node->decl,
		sizeof(struct ast_externdecl *) * ++node->n);
	node->decl[node->n-1] = decl;
	return node;
	return l->obj;
}

A src/ast/block.c => src/ast/block.c +127 -0
@@ 0,0 1,127 @@
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "ast.h"
#include "util.h"

struct ast_block {
	int ndecl, nstmt;
	struct ast_variable **decl;
	struct ast_stmt **stmt;
};

struct ast_block *
ast_block_create(struct ast_variable **decl, int ndecl, 
	struct ast_stmt **stmt, int nstmt)
{
	struct ast_block *b = malloc(sizeof(struct ast_block));
	b->decl = decl;
	b->ndecl = ndecl;
	b->stmt = stmt;
	b->nstmt = nstmt;
	return b;
}

void
ast_block_destroy(struct ast_block *b)
{
	for (int i = 0; i < b->ndecl; i++) {
		ast_variable_destroy(b->decl[i]);
	}
	free(b->decl);
	for (int i = 0; i < b->nstmt; i++) {
		ast_stmt_destroy(b->stmt[i]);
	}
	free(b->stmt);
	free(b);
}

static struct ast_variable **
copy_var_arr(int len, struct ast_variable **);

static struct ast_stmt **
copy_stmt_arr(int len, struct ast_stmt **);

struct ast_block *
ast_block_copy(struct ast_block *b)
{
	assert(b);
	return ast_block_create(
		copy_var_arr(b->ndecl, b->decl),
		b->ndecl,
		copy_stmt_arr(b->nstmt, b->stmt),
		b->nstmt
	);
}

static struct ast_variable **
copy_var_arr(int len, struct ast_variable **var)
{
	assert(len == 0 || var);
	if (len == 0) {
		return NULL;
	}
	struct ast_variable **new = malloc(sizeof(struct ast_variable *) * len); 
	for (int i = 0; i < len; i++) {
		new[i] = ast_variable_copy(var[i]);
	}
	return new;
}

static struct ast_stmt **
copy_stmt_arr(int len, struct ast_stmt **stmt)
{
	assert(len == 0 || stmt);
	if (len == 0) {
		return NULL;
	}
	struct ast_stmt **new = malloc(sizeof(struct ast_stmt *) * len); 
	for (int i = 0; i < len; i++) {
		new[i] = ast_stmt_copy(stmt[i]);
	}
	return new;
}

char *
ast_block_str(struct ast_block *b)
{
	struct strbuilder *sb = strbuilder_create();
	for (int i = 0; i < b->ndecl; i++) {
		char *s = ast_variable_str(b->decl[i]);
		strbuilder_printf(sb, "%s;\n", s);
		free(s);
	}
	for (int i = 0; i < b->nstmt; i++) {
		char *s = ast_stmt_str(b->stmt[i]);
		strbuilder_printf(sb, "%s\n", s);
		free(s);
	}
	return strbuilder_build(sb);
}

int
ast_block_ndecls(struct ast_block *b)
{
	return b->ndecl;
}

struct ast_variable **
ast_block_decls(struct ast_block *b)
{
	assert(b->ndecl > 0 || !b->decl);
	return b->decl;
}

int
ast_block_nstmts(struct ast_block *b)
{
	return b->nstmt;
}

struct ast_stmt **
ast_block_stmts(struct ast_block *b)
{
	assert(b->nstmt > 0 || !b->stmt);
	return b->stmt;
}

A src/ast/expr/expr.c => src/ast/expr/expr.c +809 -0
@@ 0,0 1,809 @@
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "ast.h"
#include "math.h"
#include "util.h"
#include "expr.h"

static struct ast_expr *
ast_expr_create()
{
	return malloc(sizeof(struct ast_expr));
}

struct ast_expr *
ast_expr_identifier_create(char *s)
{
	struct ast_expr *expr = ast_expr_create();
	expr->kind = EXPR_IDENTIFIER;
	expr->u.string = s;
	return expr;
}

char *
ast_expr_as_identifier(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_IDENTIFIER);
	return expr->u.string;
}

static void
ast_expr_destroy_identifier(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_IDENTIFIER);
	free(expr->u.string);
}

struct ast_expr *
ast_expr_constant_create(int k)
{
	/* TODO: generalise for all constant cases */
	struct ast_expr *expr = ast_expr_create();
	expr->kind = EXPR_CONSTANT;
	expr->u.constant = k;
	return expr;
}

int
ast_expr_as_constant(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_CONSTANT);
	return expr->u.constant;
}

struct ast_expr *
ast_expr_literal_create(char *s)
{
	struct ast_expr *expr = ast_expr_create();
	expr->kind = EXPR_STRING_LITERAL;
	expr->u.string = s;
	return expr;
}

char *
ast_expr_as_literal(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_STRING_LITERAL);
	return expr->u.string;
}

static void
ast_expr_destroy_literal(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_STRING_LITERAL);
	free(expr->u.string);
}

struct ast_expr *
ast_expr_bracketed_create(struct ast_expr *root)
{
	struct ast_expr *expr = ast_expr_create();
	expr->kind = EXPR_BRACKETED;
	expr->root = root;
	return expr;
}

static void
ast_expr_bracketed_str_build(struct ast_expr *expr, struct strbuilder *b)
{
	char *root = ast_expr_str(expr->root);
	strbuilder_printf(b, "(%s)", root);
	free(root);
}


struct ast_expr *
ast_expr_iteration_create()
{
	struct ast_expr *expr = ast_expr_create();
	expr->kind = EXPR_ITERATION;
	return expr;
}

struct ast_expr *
ast_expr_call_create(struct ast_expr *root, int narg, struct ast_expr **arg)
{
	struct ast_expr *expr = ast_expr_create();
	expr->kind = EXPR_CALL;
	expr->root = root;
	expr->u.call.n = narg;
	expr->u.call.arg = arg;
	return expr;
}

struct ast_expr *
ast_expr_call_root(struct ast_expr *expr)
{
	return expr->root;
}

int
ast_expr_call_nargs(struct ast_expr *expr)
{
	return expr->u.call.n;
}

struct ast_expr **
ast_expr_call_args(struct ast_expr *expr)
{
	return expr->u.call.arg;
}

static void
ast_expr_call_str_build(struct ast_expr *expr, struct strbuilder *b)
{
	char *root = ast_expr_str(expr->root);
	strbuilder_printf(b, "%s(", root);
	for (int i = 0; i < expr->u.call.n; i++) {
		char *arg = ast_expr_str(expr->u.call.arg[i]);
		strbuilder_printf(b, "%s%s", arg,
			(i + 1 < expr->u.call.n) ? ", " : "");
		free(arg);
	}
	strbuilder_printf(b, ")");
	free(root);
}

static void
ast_expr_destroy_call(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_CALL);
	ast_expr_destroy(expr->root);
	for (int i = 0; i < expr->u.call.n; i++) {
		ast_expr_destroy(expr->u.call.arg[i]);
	}
	free(expr->u.call.arg);
}

static struct ast_expr *
ast_expr_copy_call(struct ast_expr *expr)
{
	struct ast_expr **arg = malloc(sizeof(struct ast_expr *) * expr->u.call.n);
	for (int i = 0; i < expr->u.call.n; i++) {
		arg[i] = ast_expr_copy(expr->u.call.arg[i]);
	}
	return ast_expr_call_create(
		ast_expr_copy(expr->root),
		expr->u.call.n,
		arg
	);
}

struct ast_expr *
ast_expr_incdec_create(struct ast_expr *root, bool inc, bool pre)
{
	struct ast_expr *expr = ast_expr_create();
	expr->kind = EXPR_INCDEC;
	expr->root = root;
	expr->u.incdec.inc = inc;
	expr->u.incdec.pre = pre;
	return expr;
}

struct ast_expr *
ast_expr_incdec_to_assignment(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_INCDEC);

	return ast_expr_assignment_create(
		ast_expr_copy(expr->root),
		ast_expr_binary_create(
			ast_expr_copy(expr->root),
			expr->u.incdec.inc ? BINARY_OP_ADDITION : BINARY_OP_SUBTRACTION,
			ast_expr_constant_create(1)
		)
	);
}

bool
ast_expr_incdec_pre(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_INCDEC);

	return expr->u.incdec.pre;
}

struct ast_expr *
ast_expr_incdec_root(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_INCDEC);

	return expr->root;
}

static void
ast_expr_destroy_incdec(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_INCDEC);
	ast_expr_destroy(expr->root);
}

static void
ast_expr_incdec_str_build(struct ast_expr *expr, struct strbuilder *b)
{
	char *root = ast_expr_str(expr->root);
	char *op = expr->u.incdec.inc ? "++" : "--";
	if (expr->u.incdec.pre) {
		strbuilder_printf(b, "%s%s", op, root);
	} else {
		strbuilder_printf(b, "%s%s", root, op);
	}
	free(root);
}

struct ast_expr *
ast_expr_member_create(struct ast_expr *_struct, char *field)
{
	struct ast_expr *expr = ast_expr_create();
	expr->kind = EXPR_STRUCTMEMBER;
	expr->root = _struct;
	expr->u.string = field;
	return expr;
}

struct ast_expr *
ast_expr_member_root(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_STRUCTMEMBER);
	return expr->root;
}

char *
ast_expr_member_field(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_STRUCTMEMBER);
	return expr->u.string;
}

static void
ast_expr_member_deref_str_build(struct ast_expr *root, char *member,
		struct strbuilder *b);

static void
ast_expr_member_str_build(struct ast_expr *expr, struct strbuilder *b)
{
	struct ast_expr *root = expr->root;

	if (root->kind == EXPR_UNARY) {
		return ast_expr_member_deref_str_build(root, expr->u.string, b);
	}

	char *r = ast_expr_str(root);
	strbuilder_printf(b, "%s.%s", r, expr->u.string);
	free(root);
}

static void
ast_expr_member_deref_str_build(struct ast_expr *root, char *member,
		struct strbuilder *b)
{
	assert(ast_expr_unary_op(root) == UNARY_OP_DEREFERENCE);

	struct ast_expr *inner = ast_expr_unary_operand(root); /* e1+e2 */
	struct ast_expr *e1 = ast_expr_binary_e1(inner),
			*e2 = ast_expr_binary_e2(inner);

	char *left = ast_expr_str(e1);
	if (e2->kind == EXPR_CONSTANT && ast_expr_as_constant(e2) == 0) { 
		strbuilder_printf(b, "%s->%s", left, member);
	} else {
		char *index = ast_expr_str(e2);
		strbuilder_printf(b, "%s[%s].%s", left, index, member);
		free(index);
	}
	free(left);

}

struct ast_expr *
ast_expr_unary_create(struct ast_expr *root, enum ast_unary_operator op)
{
	struct ast_expr *expr = ast_expr_create();
	expr->kind = EXPR_UNARY;
	expr->root = root;
	expr->u.unary_op = op;
	return expr;
}

static void
ast_expr_destroy_unary(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_UNARY);

	ast_expr_destroy(expr->root);
}

enum ast_unary_operator
ast_expr_unary_op(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_UNARY);

	return expr->u.unary_op;
}

struct ast_expr *
ast_expr_unary_operand(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_UNARY);

	return expr->root;
}

bool
ast_expr_unary_isdereference(struct ast_expr *expr)
{
	return ast_expr_unary_op(expr) == UNARY_OP_DEREFERENCE;
}


static void
ast_expr_unary_str_build(struct ast_expr *expr, struct strbuilder *b)
{
	enum ast_unary_operator op = expr->u.unary_op;
	assert(UNARY_OP_ADDRESS <= op && op <= UNARY_OP_BANG);

	const char opchar[] = {
		[UNARY_OP_ADDRESS]		= '&',
		[UNARY_OP_DEREFERENCE]		= '*',
		[UNARY_OP_POSITIVE]		= '+',
		[UNARY_OP_NEGATIVE]		= '-',
		[UNARY_OP_ONES_COMPLEMENT]	= '~',
		[UNARY_OP_BANG]			= '!',
	};

	char *root = ast_expr_str(expr->root);
	strbuilder_printf(b, "%c(%s)", opchar[op], root);
	free(root);
}

struct ast_expr *
ast_expr_binary_create(struct ast_expr *e1, enum ast_binary_operator op,
		struct ast_expr *e2)
{
	struct ast_expr *expr = ast_expr_create();
	expr->kind = EXPR_BINARY;
	expr->u.binary.e1 = e1;
	expr->u.binary.op = op;
	expr->u.binary.e2 = e2;
	return expr;
}

struct ast_expr *
ast_expr_eq_create(struct ast_expr *e1, struct ast_expr *e2)
{
	return ast_expr_binary_create(e1, BINARY_OP_EQ, e2);
}

struct ast_expr *
ast_expr_ne_create(struct ast_expr *e1, struct ast_expr *e2)
{
	return ast_expr_binary_create(e1, BINARY_OP_NE, e2);
}

struct ast_expr *
ast_expr_lt_create(struct ast_expr *e1, struct ast_expr *e2)
{
	return ast_expr_binary_create(e1, BINARY_OP_LT, e2);
}

struct ast_expr *
ast_expr_gt_create(struct ast_expr *e1, struct ast_expr *e2)
{
	return ast_expr_binary_create(e1, BINARY_OP_GT, e2);
}

struct ast_expr *
ast_expr_le_create(struct ast_expr *e1, struct ast_expr *e2)
{
	return ast_expr_binary_create(e1, BINARY_OP_LE, e2);
}

struct ast_expr *
ast_expr_ge_create(struct ast_expr *e1, struct ast_expr *e2)
{
	return ast_expr_binary_create(e1, BINARY_OP_GE, e2);
}

struct ast_expr *
ast_expr_sum_create(struct ast_expr *e1, struct ast_expr *e2)
{
	return ast_expr_binary_create(e1, BINARY_OP_ADDITION, e2);
}

struct ast_expr *
ast_expr_difference_create(struct ast_expr *e1, struct ast_expr *e2)
{
	return ast_expr_binary_create(e1, BINARY_OP_SUBTRACTION, e2);
}

struct ast_expr *
ast_expr_binary_e1(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_BINARY);
	return expr->u.binary.e1;
}

struct ast_expr *
ast_expr_binary_e2(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_BINARY);
	return expr->u.binary.e2;
}

enum ast_binary_operator
ast_expr_binary_op(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_BINARY);
	return expr->u.binary.op;
}

static void
ast_expr_destroy_binary(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_BINARY);
	ast_expr_destroy(expr->u.binary.e1);
	ast_expr_destroy(expr->u.binary.e2);
}

static struct math_expr *
math_expr(struct ast_expr *);

static void
ast_expr_binary_str_build(struct ast_expr *expr, struct strbuilder *b)
{
	const char *opstr[] = {
		[BINARY_OP_EQ]		= "==",
		[BINARY_OP_NE]		= "!=",

		[BINARY_OP_LT]		= "<",
		[BINARY_OP_GT]		= ">",
		[BINARY_OP_LE]		= "<=",
		[BINARY_OP_GE]		= ">=",

		[BINARY_OP_ADDITION]	= "+",
		[BINARY_OP_SUBTRACTION]	= "-",
	};
	char *e1 = ast_expr_str(expr->u.binary.e1),
	     *e2 = ast_expr_str(expr->u.binary.e2);
	strbuilder_printf(b, "%s%s%s", e1, opstr[expr->u.binary.op], e2);
	free(e1);
	free(e2);
}

struct ast_expr *
ast_expr_assignment_create(struct ast_expr *root, struct ast_expr *value)
{
	struct ast_expr *expr = ast_expr_create();
	expr->kind = EXPR_ASSIGNMENT;
	expr->root = root;
	expr->u.assignment_value = value;
	return expr;
}

struct ast_expr *
ast_expr_assignment_lval(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_ASSIGNMENT);
	return expr->root;
}

struct ast_expr *
ast_expr_assignment_rval(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_ASSIGNMENT);
	return expr->u.assignment_value;
}

static void
ast_expr_destroy_assignment(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_ASSIGNMENT);
	ast_expr_destroy(expr->root);
	ast_expr_destroy(expr->u.assignment_value);
}

static void
ast_expr_assignment_str_build(struct ast_expr *expr, struct strbuilder *b)
{
	char *root = ast_expr_str(expr->root),
	     *value = ast_expr_str(expr->u.assignment_value);
	strbuilder_printf(b, "%s = %s", root, value);
	free(value);
	free(root);
}

struct ast_expr *
ast_expr_isdeallocand_create(struct ast_expr *assertand)
{
	struct ast_expr *new = ast_expr_create();
	new->kind = EXPR_ISDEALLOCAND;
	new->root = assertand;
	return new;
}

struct ast_expr *
ast_expr_isdeallocand_assertand(struct ast_expr *expr)
{
	assert(expr->kind == EXPR_ISDEALLOCAND);
	return expr->root;
}

static void
ast_expr_isdeallocand_str_build(struct ast_expr *expr, struct strbuilder *b)
{
	char *root = ast_expr_str(expr->root);
	strbuilder_printf(b, "@%s", root);
	free(root);
}

struct ast_expr *
ast_expr_arbarg_create()
{
	struct ast_expr *expr = ast_expr_create();
	expr->kind = EXPR_ARBARG;
	return expr;
}

void
ast_expr_destroy(struct ast_expr *expr)
{
	switch (expr->kind) {
	case EXPR_IDENTIFIER:
		ast_expr_destroy_identifier(expr);
		break;
	case EXPR_STRING_LITERAL:
		ast_expr_destroy_literal(expr);
		break;
	case EXPR_BRACKETED:
		ast_expr_destroy(expr->root);
		break;
	case EXPR_CALL:
		ast_expr_destroy_call(expr);
		break;
	case EXPR_INCDEC:
		ast_expr_destroy_incdec(expr);
		break;
	case EXPR_STRUCTMEMBER:
		ast_expr_destroy(expr->root); free(expr->u.string);
		break;
	case EXPR_UNARY:
		ast_expr_destroy_unary(expr);
		break;
	case EXPR_BINARY:
		ast_expr_destroy_binary(expr);
		break;
	case EXPR_ASSIGNMENT:
		ast_expr_destroy_assignment(expr);
		break;
	case EXPR_CONSTANT:
		break;
	case EXPR_ISDEALLOCAND:
		ast_expr_destroy(expr->root);
		break;
	case EXPR_ARBARG:
		break;
	default:
		assert(false);
	}
	free(expr);
}

char *
ast_expr_str(struct ast_expr *expr)
{
	struct strbuilder *b = strbuilder_create();
	switch (expr->kind) {
	case EXPR_IDENTIFIER:
		strbuilder_printf(b, expr->u.string);
		break;
	case EXPR_CONSTANT:
		strbuilder_printf(b, "%d", expr->u.constant);
		break;
	case EXPR_STRING_LITERAL:
		strbuilder_printf(b, "\"%s\"", expr->u.string);
		break;
	case EXPR_BRACKETED:
		ast_expr_bracketed_str_build(expr, b);
		break;
	case EXPR_CALL:
		ast_expr_call_str_build(expr, b);
		break;
	case EXPR_INCDEC:
		ast_expr_incdec_str_build(expr, b);
		break;
	case EXPR_STRUCTMEMBER:
		ast_expr_member_str_build(expr, b);
		break;
	case EXPR_UNARY:
		ast_expr_unary_str_build(expr, b);
		break;
	case EXPR_BINARY:
		ast_expr_binary_str_build(expr, b);
		break;
	case EXPR_ASSIGNMENT:
		ast_expr_assignment_str_build(expr, b);
		break;
	case EXPR_ISDEALLOCAND:
		ast_expr_isdeallocand_str_build(expr, b);
		break;
	case EXPR_ARBARG:
		strbuilder_putc(b, '$');
		break;
	default:
		assert(false);
	}
	return strbuilder_build(b);
}

struct ast_expr *
ast_expr_copy(struct ast_expr *expr)
{
	assert(expr);
	switch (expr->kind) {
	case EXPR_IDENTIFIER:
		return ast_expr_identifier_create(dynamic_str(expr->u.string));
	case EXPR_CONSTANT:
		return ast_expr_constant_create(expr->u.constant);
	case EXPR_STRING_LITERAL:
		return ast_expr_literal_create(dynamic_str(expr->u.string));
	case EXPR_BRACKETED:
		return ast_expr_bracketed_create(ast_expr_copy(expr->root));
	case EXPR_CALL:
		return ast_expr_copy_call(expr);
	case EXPR_INCDEC:
		return ast_expr_incdec_create(
			ast_expr_copy(expr->root),
			expr->u.incdec.inc,
			expr->u.incdec.pre
		);
	case EXPR_STRUCTMEMBER:
		return ast_expr_member_create(
			ast_expr_copy(expr->root), dynamic_str(expr->u.string)
		);
	case EXPR_UNARY:
		return ast_expr_unary_create(
			ast_expr_copy(expr->root),
			expr->u.unary_op
		);
	case EXPR_BINARY:
		return ast_expr_binary_create(
			ast_expr_copy(expr->u.binary.e1),
			expr->u.binary.op,
			ast_expr_copy(expr->u.binary.e2)
		);
	case EXPR_ASSIGNMENT:
		return ast_expr_assignment_create(
			ast_expr_copy(expr->root),
			ast_expr_copy(expr->u.assignment_value)
		);
	case EXPR_ISDEALLOCAND:
		return ast_expr_isdeallocand_create(
			ast_expr_copy(expr->root)
		);
	case EXPR_ARBARG:
		return ast_expr_arbarg_create();
	default:
		assert(false);
	}
}

enum ast_expr_kind
ast_expr_kind(struct ast_expr *expr)
{
	return expr->kind;
}

bool
ast_expr_equal(struct ast_expr *e1, struct ast_expr *e2)
{
	if (!e1 || !e2) {
		return false;
	}
	if (e1->kind != e2->kind) {
		return false;	
	}
	switch (e1->kind) {
	case EXPR_CONSTANT:
		return e1->u.constant == e2->u.constant;
	case EXPR_IDENTIFIER:
		return strcmp(ast_expr_as_identifier(e1), ast_expr_as_identifier(e2)) == 0;
	case EXPR_ASSIGNMENT:
		return ast_expr_equal(e1->root, e2->root)
			&& ast_expr_equal(e1->u.assignment_value, e2->u.assignment_value); 
	case EXPR_BINARY:
		return ast_expr_binary_op(e1) == ast_expr_binary_op(e2) &&
			ast_expr_equal(ast_expr_binary_e1(e1), ast_expr_binary_e1(e2)) && 
			ast_expr_equal(ast_expr_binary_e2(e1), ast_expr_binary_e2(e2));
	default:
		assert(false);
	}
}

static bool
eval_prop(struct math_expr *e1, enum ast_binary_operator, struct math_expr *e2);

bool
ast_expr_matheval(struct ast_expr *e)
{
	assert(e->kind == EXPR_BINARY);

	struct math_expr *e1 = math_expr(e->u.binary.e1),
			 *e2 = math_expr(e->u.binary.e2);

	bool val = eval_prop(e1, e->u.binary.op, e2);

	math_expr_destroy(e2);
	math_expr_destroy(e1);

	return val;
}

static bool
eval_prop(struct math_expr *e1, enum ast_binary_operator op, struct math_expr *e2)
{
	switch (op) {
	case BINARY_OP_EQ:
		return math_eq(e1, e2);
	case BINARY_OP_NE:
		return !math_eq(e1, e2);
	case BINARY_OP_LT:
		return math_lt(e1, e2);
	case BINARY_OP_GT:
		return math_gt(e1, e2);
	case BINARY_OP_LE:
		return math_le(e1, e2);
	case BINARY_OP_GE:
		return math_ge(e1, e2);
	default:
		assert(false);
	}
}

static struct math_expr *
binary_e2(struct ast_expr *e2, enum ast_binary_operator op);

static struct math_expr *
math_expr(struct ast_expr *e)
{
	switch (e->kind) {
	case EXPR_IDENTIFIER:
		return math_expr_atom_create(
			math_atom_variable_create(dynamic_str(e->u.string))
		);
	case EXPR_CONSTANT:
		if (e->u.constant < 0) {
			return math_expr_neg_create(
				math_expr_atom_create(
					math_atom_nat_create(-e->u.constant)
				)
			);
		}
		return math_expr_atom_create(
			math_atom_nat_create(e->u.constant)
		);
	case EXPR_BINARY:
		return math_expr_sum_create(
			math_expr(e->u.binary.e1),
			binary_e2(e->u.binary.e2, e->u.binary.op)
		);
	default:
		assert(false);
	}
}

static struct math_expr *
binary_e2(struct ast_expr *e2, enum ast_binary_operator op)
{
	switch (op) {
	case BINARY_OP_ADDITION:
		return math_expr(e2);
	case BINARY_OP_SUBTRACTION:
		return math_expr_neg_create(math_expr(e2));
	default:
		assert(false);
	}
}

#include "verify.c"

A src/ast/expr/expr.h => src/ast/expr/expr.h +70 -0
@@ 0,0 1,70 @@
#ifndef XR0_AST_EXPR_H
#define XR0_AST_EXPR_H

struct ast_expr {
	enum ast_expr_kind {
		EXPR_IDENTIFIER		= 1 << 0,
		EXPR_CONSTANT		= 1 << 1,
		EXPR_STRING_LITERAL	= 1 << 2,
		EXPR_BRACKETED		= 1 << 3,
		EXPR_ITERATION		= 1 << 4,

		EXPR_CALL		= 1 << 5,
		EXPR_INCDEC		= 1 << 6,

		EXPR_STRUCTMEMBER	= 1 << 7,

		EXPR_UNARY		= 1 << 8,
		EXPR_BINARY		= 1 << 9,

		EXPR_ASSIGNMENT		= 1 << 10,

		EXPR_ISDEALLOCAND	= 1 << 11,
		EXPR_ARBARG		= 1 << 12,
	} kind;
	struct ast_expr *root;
	union {
		char *string; /* identifier, literal, assertion */
		int constant;
		struct {
			int n;
			struct ast_expr **arg;
		} call;
		struct {
			int inc, pre;
		} incdec;
		enum ast_unary_operator {
			UNARY_OP_ADDRESS		= 1 << 0,
			UNARY_OP_DEREFERENCE		= 1 << 1,
			UNARY_OP_POSITIVE		= 1 << 2,
			UNARY_OP_NEGATIVE		= 1 << 3,
			UNARY_OP_ONES_COMPLEMENT	= 1 << 4,
			UNARY_OP_BANG			= 1 << 5,
		} unary_op;
		struct {
			enum ast_binary_operator {
				BINARY_OP_EQ	= 1 << 0,
				BINARY_OP_NE	= 1 << 1,

				BINARY_OP_LT	= 1 << 2,
				BINARY_OP_GT	= 1 << 3,
				BINARY_OP_LE	= 1 << 4,
				BINARY_OP_GE	= 1 << 5,

				BINARY_OP_ADDITION	= 1 << 6,
				BINARY_OP_SUBTRACTION	= 1 << 7,
			} op;
			struct ast_expr *e1, *e2;
		} binary;
		struct ast_expr *assignment_value;
	} u;
};

enum ast_expr_kind
ast_expr_kind(struct ast_expr *);

struct ast_expr *
ast_expr_binary_create(struct ast_expr *e1, enum ast_binary_operator,
		struct ast_expr *e2);

#endif

A src/ast/expr/verify.c => src/ast/expr/verify.c +631 -0
@@ 0,0 1,631 @@
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "ast.h"
#include "expr.h"
#include "ext.h"
#include "intern.h"
#include "math.h"
#include "object.h"
#include "state.h"
#include "util.h"
#include "value.h"

static bool
expr_unary_decide(struct ast_expr *expr, struct state *state);

static bool
expr_isdeallocand_decide(struct ast_expr *expr, struct state *state);

static bool
expr_binary_decide(struct ast_expr *expr, struct state *state);

bool
ast_expr_decide(struct ast_expr *expr, struct state *state)
{
	switch (ast_expr_kind(expr)) {
	case EXPR_UNARY:
		return expr_unary_decide(expr, state);
	case EXPR_ISDEALLOCAND:
		return expr_isdeallocand_decide(expr, state);
	case EXPR_BINARY:
		return expr_binary_decide(expr, state);
	default:
		assert(false);
	}
}

static bool
expr_unary_decide(struct ast_expr *expr, struct state *state)
{
	struct ast_expr *operand = ast_expr_unary_operand(expr);
	switch (ast_expr_unary_op(expr)) {
	case UNARY_OP_BANG:
		return !ast_expr_decide(operand, state);
	default:
		assert(false);
	}
}

static bool
unary_rangedecide(struct ast_expr *expr, struct ast_expr *lw,
		struct ast_expr *up, struct state *);

static bool
expr_isdeallocand_rangedecide(struct ast_expr *expr, struct ast_expr *lw,
		struct ast_expr *up, struct state *);

bool
ast_expr_rangedecide(struct ast_expr *expr, struct ast_expr *lw,
		struct ast_expr *up, struct state *state)
{
	switch (ast_expr_kind(expr)) {
	case EXPR_UNARY:
		/* recurses for `!@` cases */
		return unary_rangedecide(expr, lw, up, state);
	case EXPR_ISDEALLOCAND:
		return expr_isdeallocand_rangedecide(expr, lw, up, state);
	default:
		assert(false);
	}
}

static bool
unary_rangedecide(struct ast_expr *expr, struct ast_expr *lw,
		struct ast_expr *up, struct state *state)
{
	struct ast_expr *operand = ast_expr_unary_operand(expr);
	switch (ast_expr_unary_op(expr)) {
	case UNARY_OP_BANG:
		return !ast_expr_rangedecide(operand, lw, up, state);
	default:
		assert(false);
	}
}

static bool
expr_isdeallocand_rangedecide(struct ast_expr *expr, struct ast_expr *lw,
		struct ast_expr *up, struct state *state)
{
	struct ast_expr *acc = ast_expr_isdeallocand_assertand(expr); /* `*(arr+offset)` */

	assert(ast_expr_unary_op(acc) == UNARY_OP_DEREFERENCE);

	struct ast_expr *inner = ast_expr_unary_operand(acc); /* `arr+offset` */
	struct ast_expr *i = ast_expr_identifier_create(dynamic_str("i"));
	struct ast_expr *j = ast_expr_identifier_create(dynamic_str("j"));

	assert(
		ast_expr_equal(ast_expr_binary_e2(inner), i) ||
		ast_expr_equal(ast_expr_binary_e2(inner), j)
	);

	ast_expr_destroy(j);
	ast_expr_destroy(i);

	struct object *obj = lvalue_object(
		ast_expr_lvalue(ast_expr_binary_e1(acc), state)
	);
	assert(obj);

	return state_range_aredeallocands(state, obj, lw, up);
}




struct error *
ast_expr_exec(struct ast_expr *expr, struct state *state)
{
	struct result *res = ast_expr_eval(expr, state);
	if (result_iserror(res)) {
		return result_as_error(res);
	}
	result_destroy(res);
	return NULL;
}

struct lvalue *
expr_identifier_lvalue(struct ast_expr *expr, struct state *state);

struct lvalue *
expr_access_lvalue(struct ast_expr *expr, struct state *state);

struct lvalue *
expr_unary_lvalue(struct ast_expr *expr, struct state *state);

struct lvalue *
expr_structmember_lvalue(struct ast_expr *expr, struct state *state);

struct lvalue *
ast_expr_lvalue(struct ast_expr *expr, struct state *state)
{
	switch (ast_expr_kind(expr)) {
	case EXPR_IDENTIFIER:
		return expr_identifier_lvalue(expr, state);
	case EXPR_UNARY:
		return expr_unary_lvalue(expr, state);
	case EXPR_STRUCTMEMBER:
		return expr_structmember_lvalue(expr, state);
	default:
		assert(false);
	}
}

struct lvalue *
expr_identifier_lvalue(struct ast_expr *expr, struct state *state)
{
	char *id = ast_expr_as_identifier(expr);
	return lvalue_create(
		state_getobjecttype(state, id),
		state_getobject(state, id)
	);
}

struct lvalue *
expr_unary_lvalue(struct ast_expr *expr, struct state *state)
{
	assert(ast_expr_unary_op(expr) == UNARY_OP_DEREFERENCE);
	struct ast_expr *inner = ast_expr_unary_operand(expr);

	struct lvalue *root = ast_expr_lvalue(ast_expr_binary_e1(inner), state);
	struct object *root_obj = lvalue_object(root);
	if (!root_obj) { /* `root` freed */
		return NULL;
	}
	struct ast_type *t = ast_type_ptr_type(lvalue_type(root));

	struct value *root_val = object_as_value(root_obj);
	assert(root_val);
	struct object *obj = state_deref(
		state, root_val, ast_expr_binary_e2(inner)
	);

	return lvalue_create(t, obj);
}

struct lvalue *
expr_structmember_lvalue(struct ast_expr *expr, struct state *state)
{
	struct lvalue *root = ast_expr_lvalue(ast_expr_member_root(expr), state);
	struct object *root_obj = lvalue_object(root);
	assert(root_obj);
	struct object *obj = object_getmember(
		root_obj,
		lvalue_type(root),
		ast_expr_member_field(expr),
		state
	);
	struct ast_type *t = object_getmembertype(
		root_obj,
		lvalue_type(root),
		ast_expr_member_field(expr),
		state
	);
	return lvalue_create(t, obj);
}


static struct object *
hack_object_from_assertion(struct ast_expr *expr, struct state *state)
{	
	/* get assertand */
	struct ast_expr *assertand = ast_expr_isdeallocand_assertand(expr);

	/* get `assertand' variable */
	struct object *obj = lvalue_object(ast_expr_lvalue(assertand, state));
	assert(obj);
	return obj;
}

static bool
expr_isdeallocand_decide(struct ast_expr *expr, struct state *state)
{
	struct object *obj = hack_object_from_assertion(expr, state);
	bool isdeallocand = state_addresses_deallocand(state, obj);
	return isdeallocand;
}

struct result *
ast_expr_eval(struct ast_expr *expr, struct state *state);

static bool
value_compare(struct value *, enum ast_binary_operator, struct value *);

static bool
expr_binary_decide(struct ast_expr *expr, struct state *state)
{
	struct result *root = ast_expr_eval(ast_expr_binary_e1(expr), state),
		      *last = ast_expr_eval(ast_expr_binary_e2(expr), state);

	assert(!result_iserror(root) && !result_iserror(last));

	return value_compare(
		result_as_value(root),
		ast_expr_binary_op(expr),
		result_as_value(last)
	);
}

static bool
value_compare(struct value *v1, enum ast_binary_operator op, struct value *v2)
{
	switch (op) {
	case BINARY_OP_EQ:
		return value_equal(v1, v2);
	case BINARY_OP_NE:
		return !value_compare(v1, BINARY_OP_EQ, v2);
	default:
		assert(false);
	}
}

/* ast_expr_eval */

static struct result *
expr_constant_eval(struct ast_expr *expr, struct state *state);

static struct result *
expr_literal_eval(struct ast_expr *expr, struct state *state);

static struct result *
expr_identifier_eval(struct ast_expr *expr, struct state *state);

static struct result *
expr_unary_eval(struct ast_expr *expr, struct state *state);

static struct result *
expr_structmember_eval(struct ast_expr *expr, struct state *state);

static struct result *
expr_call_eval(struct ast_expr *expr, struct state *state);

static struct result *
expr_assign_eval(struct ast_expr *expr, struct state *state);

static struct result *
expr_incdec_eval(struct ast_expr *expr, struct state *state);

static struct result *
expr_binary_eval(struct ast_expr *expr, struct state *state);

struct result *
ast_expr_eval(struct ast_expr *expr, struct state *state)
{
	/* TODO: verify preconditions of expr (statement) are satisfied */
	/* now add postconditions */
	switch (ast_expr_kind(expr)) {
	case EXPR_CONSTANT:
		return expr_constant_eval(expr, state);
	case EXPR_STRING_LITERAL:
		return expr_literal_eval(expr, state);
	case EXPR_IDENTIFIER:
		return expr_identifier_eval(expr, state);
	case EXPR_UNARY:
		return expr_unary_eval(expr, state);
	case EXPR_STRUCTMEMBER:
		return expr_structmember_eval(expr, state);
	case EXPR_CALL:
		return expr_call_eval(expr, state);
	case EXPR_ASSIGNMENT:
		return expr_assign_eval(expr, state);
	case EXPR_INCDEC:
		return expr_incdec_eval(expr, state);
	case EXPR_BINARY:
		return expr_binary_eval(expr, state);
	default:
		assert(false);
	}
}

static struct result *
expr_literal_eval(struct ast_expr *expr, struct state *state)
{
	return result_value_create(
		value_literal_create(ast_expr_as_literal(expr))
	);
}

static struct result *
expr_constant_eval(struct ast_expr *expr, struct state *state)
{
	return result_value_create(
		value_int_create(ast_expr_as_constant(expr))
	);
}

static struct result *
expr_identifier_eval(struct ast_expr *expr, struct state *state)
{
	struct object *obj = state_getobject(state, ast_expr_as_identifier(expr));
	if (!obj) {
		return result_error_create(error_create("no object"));
	}
	struct value *val = object_as_value(obj);
	if (!val) {
		return result_error_create(error_create("no value"));
	}
	return result_value_create(value_copy(val));
}

static struct result *
expr_unary_eval(struct ast_expr *expr, struct state *state)
{
	assert(ast_expr_unary_op(expr) == UNARY_OP_DEREFERENCE);

	struct ast_expr *inner = ast_expr_unary_operand(expr); /* arr+offset */

	struct result *res = ast_expr_eval(ast_expr_binary_e1(inner), state);
	if (result_iserror(res)) {
		return res;
	}
	struct value *arr = result_as_value(res);
	assert(arr);
	struct object *obj = state_deref(state, arr, ast_expr_binary_e2(inner));
	assert(obj);
	result_destroy(res);

	struct value *v = object_as_value(obj);
	assert(v);

	return result_value_create(value_copy(v));
}

static struct result *
expr_structmember_eval(struct ast_expr *expr, struct state *s)
{
	struct result *res = ast_expr_eval(ast_expr_member_root(expr), s);
	if (result_iserror(res)) {
		return res;
	}
	struct value *v = value_copy(object_as_value(
		value_struct_member(
			result_as_value(res),
			ast_expr_member_field(expr)
		)
	));
	result_destroy(res);
	return result_value_create(v);
}

/* expr_call_eval */

struct ast_function *
expr_as_func(struct ast_expr *expr, struct state *state);

struct result_arr {
	int n;
	struct result **res;
};

struct result_arr *
result_arr_create()
{
	return calloc(1, sizeof(struct result_arr));
}

void
result_arr_destroy(struct result_arr *arr)
{
	for (int i = 0; i < arr->n; i++) {
		result_destroy(arr->res[i]);
	}
	free(arr);
}

void
result_arr_append(struct result_arr *arr, struct result *res)
{
	arr->res = realloc(arr->res, sizeof(struct result *) * ++arr->n);
	arr->res[arr->n-1] = res;
}

static struct result *
prepare_argument(struct ast_expr *arg, struct ast_variable *param, struct state *);

struct result_arr *
prepare_arguments(struct ast_expr *call, struct state *state)
{
	struct result_arr *args = result_arr_create();

	int nargs = ast_expr_call_nargs(call);
	struct ast_expr **arg = ast_expr_call_args(call);

	struct ast_function *f = expr_as_func(call, state);
	int nparams = ast_function_nparams(f);
	struct ast_variable **param = ast_function_params(f);

	assert(nargs == nparams);

	for (int i = 0; i < nargs; i++) {
		result_arr_append(
			args, prepare_argument(arg[i], param[i], state)
		);
	}

	return args;
}

static struct result *
prepare_argument(struct ast_expr *arg, struct ast_variable *param, struct state *s)
{
	if (ast_expr_kind(arg) != EXPR_ARBARG) {
		return ast_expr_eval(arg, s);
	}
	assert(ast_type_base(ast_variable_type(param)) == TYPE_INT);
	return result_value_create(state_vconst(s));
}


static struct result *
call_eval_inframe(struct ast_expr *expr, struct state *state, struct result_arr *args);

static struct result *
expr_call_eval(struct ast_expr *expr, struct state *state)
{
	struct result_arr *args = prepare_arguments(expr, state);
	struct ast_function *f = expr_as_func(expr, state);
	state_pushframe(
		state, dynamic_str(ast_function_name(f)), ast_function_type(f)
	);
	struct result *res = call_eval_inframe(expr, state, args);
	if (result_iserror(res)) {
		return res;
	}
	result_arr_destroy(args);
	if (result_hasvalue(res)) { /* preserve value through pop */
		res = result_value_create(value_copy(result_as_value(res)));
	}
	state_popframe(state);
	return res;
}

/* call_type */

struct ast_function *
expr_as_func(struct ast_expr *expr, struct state *state)
{
	struct ast_expr *root = ast_expr_call_root(expr);
	/* TODO: allow function-valued expressions */
	return externals_getfunc(state_getext(state), ast_expr_as_identifier(root));
}

/* call_eval_inframe */

static struct error *
prepare_parameters(struct ast_function *f, struct result_arr *args,
		struct state *state);

static struct result *
call_eval_inframe(struct ast_expr *expr, struct state *state, struct result_arr *args)
{
	struct ast_function *f = expr_as_func(expr, state);
	assert(f);

	struct error *err = prepare_parameters(f, args, state);
	if (err) {
		return result_error_create(err);
	}

	return ast_function_absexec(f, state);
}

/* prepare_parameters: Allocate arguments in call expression and assign them to
 * their respective parameters. */
static struct error *
prepare_parameters(struct ast_function *f, struct result_arr *args,
		struct state *state)
{
	struct ast_variable **param = ast_function_params(f);

	assert(ast_function_nparams(f) == args->n);

	for (int i = 0; i < args->n; i++) {
		state_declare(state, param[i], true);

		struct result *res = args->res[i];
		if (result_iserror(res)) {
			struct strbuilder *b = strbuilder_create();
			strbuilder_printf(
				b, "param `%s': ",
				ast_variable_name(param[i])
			);
			return error_prepend(
				result_as_error(res), strbuilder_build(b)
			);
		}

		if (!result_hasvalue(res)) {
			continue;
		}

		struct ast_expr *name = ast_expr_identifier_create(
			dynamic_str(ast_variable_name(param[i]))
		);
		struct object *obj = lvalue_object(ast_expr_lvalue(name, state));
		ast_expr_destroy(name);

		object_assign(obj, value_copy(result_as_value(res)));
	}
	return NULL;
}

static struct result *
expr_assign_eval(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_eval(rval, state);
	if (result_hasvalue(res)) {
		struct object *obj = lvalue_object(ast_expr_lvalue(lval, state));
		assert(obj);
		object_assign(obj, value_copy(result_as_value(res)));
	}
	return res;
}

static struct result *
expr_incdec_eval(struct ast_expr *expr, struct state *state)
{
	struct ast_expr *assign = ast_expr_incdec_to_assignment(expr);

	struct result *res;

	if (ast_expr_incdec_pre(expr)) { /* ++i */
		res = expr_assign_eval(assign, state);
	} else { /* i++ */
		res = ast_expr_eval(ast_expr_incdec_root(expr), state);
		/* assign and ignore result */ 
		result_destroy(expr_assign_eval(assign, state));
	}

	ast_expr_destroy(assign);

	return res;
}

static struct result *
expr_binary_eval(struct ast_expr *expr, struct state *state)
{
	struct ast_expr *e1 = ast_expr_binary_e1(expr),
			*e2 = ast_expr_binary_e2(expr);
	struct result *res1 = ast_expr_eval(e1, state),
		      *res2 = ast_expr_eval(e2, state);
	if (result_iserror(res1)) {
		return res1;
	}
	if (result_iserror(res2)) {
		return res2;
	}
	return result_value_create(
		value_int_sync_create(
			ast_expr_binary_create(
				value_to_expr(result_as_value(res1)),
				ast_expr_binary_op(expr),
				value_to_expr(result_as_value(res2))
			)
		)
	);
}

static struct result *
assign_absexec(struct ast_expr *expr, struct state *state);

struct result *
ast_expr_absexec(struct ast_expr *expr, struct state *state)
{
	switch (ast_expr_kind(expr)) {
	case EXPR_ASSIGNMENT:
		return assign_absexec(expr, state);
	default:
		assert(false);
	}
}

static struct result *
assign_absexec(struct ast_expr *expr, struct state *state)
{
	return expr_assign_eval(expr, state);
}

A src/ast/externdecl.c => src/ast/externdecl.c +101 -0
@@ 0,0 1,101 @@
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "ast.h"
#include "ext.h"
#include "util.h"

struct ast_externdecl {
	enum ast_externdecl_kind kind;
	union {
		struct ast_function *function;
		struct ast_variable *variable;
		struct ast_type *type;
	} u;
};

struct ast_externdecl *
ast_functiondecl_create(struct ast_function *f)
{
	struct ast_externdecl *decl = malloc(sizeof(struct ast_externdecl));
	decl->kind = EXTERN_FUNCTION;
	decl->u.function = f;
	return decl;
}

struct ast_function *
ast_externdecl_as_function(struct ast_externdecl *decl)
{
	assert(decl->kind == EXTERN_FUNCTION);
	return decl->u.function;
}

struct ast_externdecl *
ast_variabledecl_create(struct ast_variable *v)
{
	struct ast_externdecl *decl = malloc(sizeof(struct ast_externdecl));
	decl->kind = EXTERN_VARIABLE;
	decl->u.variable = v;
	return decl;
}

struct ast_externdecl *
ast_typedecl_create(struct ast_type *t)
{
	struct ast_externdecl *decl = malloc(sizeof(struct ast_externdecl));
	decl->kind = EXTERN_TYPE;
	decl->u.type = t;
	return decl;
}

enum ast_externdecl_kind
ast_externdecl_kind(struct ast_externdecl *decl)
{
	return decl->kind;
}

void
ast_externdecl_install(struct ast_externdecl *decl, struct externals *ext)
{
	struct ast_function *f;
	struct ast_variable *v;
	struct ast_type *t;

	switch (decl->kind) {
	case EXTERN_FUNCTION:
		f = decl->u.function;
		externals_declarefunc(ext, ast_function_name(f), f);
		break;
	case EXTERN_VARIABLE:
		v = decl->u.variable;
		externals_declarevar(ext, ast_variable_name(v), v);
		break;
	case EXTERN_TYPE:
		t = decl->u.type;
		assert(ast_type_base(t) == TYPE_STRUCT && ast_type_struct_tag(t));
		externals_declaretype(ext, ast_type_struct_tag(t), t);
		break;
	default:
		assert(false);
	}
}

void
ast_externdecl_destroy(struct ast_externdecl *decl)
{
	switch (decl->kind) {
	case EXTERN_FUNCTION:
		ast_function_destroy(decl->u.function);
		break;
	case EXTERN_VARIABLE:
		ast_variable_destroy(decl->u.variable);
		break;
	case EXTERN_TYPE:
		ast_type_destroy(decl->u.type);
		break;
	default:
		assert(false);
	}
	free(decl);
}

A src/ast/function.c => src/ast/function.c +289 -0
@@ 0,0 1,289 @@
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "ast.h"
#include "intern.h"
#include "object.h"
#include "state.h"
#include "util.h"

struct ast_function {
	bool isaxiom;

	struct ast_type *ret;

	char *name;
	/* parameters */
	int nparam;
	struct ast_variable **param;

	struct ast_block *abstract, 
			 *body;
};

struct ast_function *
ast_function_create(
	bool isaxiom,
	struct ast_type *ret,
	char *name, 
	int nparam,
	struct ast_variable **param,
	struct ast_block *abstract, 
	struct ast_block *body)
{
	struct ast_function *f = malloc(sizeof(struct ast_function));
	f->isaxiom = isaxiom;
	f->ret = ret;
	f->name = name;
	f->nparam = nparam;
	f->param = param;
	assert(abstract);
	f->abstract = abstract;
	f->body = body;
	return f;
}

void
ast_function_destroy(struct ast_function *f)
{
	ast_type_destroy(f->ret);
	for (int i = 0; i < f->nparam; i++) {
		ast_variable_destroy(f->param[i]);
	}
	ast_block_destroy(f->abstract);
	if (f->body) {
		ast_block_destroy(f->body);
	}
	free(f->param);
	free(f->name);
	free(f);
}

char *
ast_function_str(struct ast_function *f)
{
	struct strbuilder *b = strbuilder_create();
	strbuilder_printf(b, "func");
	if (f->isaxiom) {
		strbuilder_printf(b, " <axiom>");
	}
	strbuilder_printf(b, " `%s'", f->name);
	char *ret = ast_type_str(f->ret);
	strbuilder_printf(b, " returns %s ", ret);
	free(ret);
	strbuilder_printf(b, "takes [");
	for (int i = 0; i < f->nparam; i++) {
		char *v = ast_variable_str(f->param[i]);
		char *space = (i + 1 < f->nparam) ? ", " : "";
		strbuilder_printf(b, "%s%s", v, space);
		free(v);
	}
	strbuilder_printf(b, "] has abstract:\n%s", ast_block_str(f->abstract));
	return strbuilder_build(b);
}

char *
ast_function_name(struct ast_function *f)
{
	return f->name;
}

struct ast_function *
ast_function_copy(struct ast_function *f)
{
	assert(f);
	struct ast_variable **param = malloc(sizeof(struct ast_variable *) * f->nparam);
	for (int i = 0; i < f->nparam; i++) {
		param[i] = ast_variable_copy(f->param[i]);
	}
	return ast_function_create(
		f->isaxiom,
		ast_type_copy(f->ret),
		dynamic_str(f->name),
		f->nparam,
		param,
		ast_block_copy(f->abstract),
		f->body ? ast_block_copy(f->body) : NULL
	);
}

bool
ast_function_isaxiom(struct ast_function *f)
{
	return f->isaxiom;
}

struct ast_type *
ast_function_type(struct ast_function *f)
{
	return f->ret;
}

struct ast_block *
ast_function_body(struct ast_function *f)
{
	assert(f->body);
	return f->body;
}

struct ast_block *
ast_function_abstract(struct ast_function *f)
{
	assert(f->abstract);
	return f->abstract;
}

int
ast_function_nparams(struct ast_function *f)
{
	return f->nparam;
}

struct ast_variable **
ast_function_params(struct ast_function *f)
{
	return f->param;
}

struct error *
path_verify(struct ast_function *f, struct state *state, struct externals *);

struct error *
ast_function_verify(struct ast_function *f, struct externals *ext)
{
	struct state *state = state_create(
		dynamic_str(ast_function_name(f)), ext, ast_function_type(f)
	);
	struct error *err = path_verify(f, state, ext);
	state_destroy(state);
	return err;
}

static struct error *
abstract_audit(struct ast_function *f, struct state *actual_state,
		struct externals *);

static struct error *
parameterise_state(struct state *s, struct ast_function *f);

struct error *
path_verify(struct ast_function *f, struct state *state, struct externals *ext)
{
	struct error *err = NULL;

	struct ast_block *body = ast_function_body(f);

	if ((err = parameterise_state(state, f))) {
		return err;
	}

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

	int nstmts = ast_block_nstmts(body);
	struct ast_stmt **stmt = ast_block_stmts(body);
	for (int i = 0; i < nstmts; i++) {
		if ((err = ast_stmt_process(stmt[i], state))) {
			return err;
		}
	}
	state_undeclarevars(state);
	/* TODO: verify that `result' is of same type as f->result */
	if ((err = abstract_audit(f, state, ext))) {
		return error_prepend(err, "qed error: ");
	}
	return NULL;
}

static struct error *
parameterise_state(struct state *s, struct ast_function *f)
{
	/* declare params and locals in stack frame */
	struct ast_variable **param = ast_function_params(f);
	int nparams = ast_function_nparams(f);
	for (int i = 0; i < nparams; i++) {
		struct ast_variable *p = param[i];
		state_declare(s, p, true);
		if (ast_type_base(ast_variable_type(p)) == TYPE_INT) {
			struct object *obj = state_getobject(s, ast_variable_name(p));
			assert(obj);
			object_assign(obj, state_vconst(s));
		}
	}

	struct ast_block *abs = ast_function_abstract(f);
	int nstmts = ast_block_nstmts(abs);
	struct ast_stmt **stmt = ast_block_stmts(abs);
	for (int i = 0; i < nstmts; i++) {
		if (ast_stmt_issetup(stmt[i])) {
			struct error *err = NULL;
			if ((err = ast_stmt_exec(stmt[i], s))) {
				return err;
			}
		}
	}

	return NULL;
}


static struct error *
abstract_audit(struct ast_function *f, struct state *actual_state,
		struct externals *ext)
{
	struct error *err = NULL;

	/*printf("actual: %s\n", state_str(actual_state));*/
	if (!state_hasgarbage(actual_state)) {
		return error_create("garbage on heap");
	}

	struct state *alleged_state = state_create(
		dynamic_str(ast_function_name(f)), ext, ast_function_type(f)
	);
	if ((err = parameterise_state(alleged_state, f))) {
		return err;
	}

	/* mutates alleged_state */
	struct result *res = ast_function_absexec(f, alleged_state);
	if (result_iserror(res)) {
		return result_as_error(res);
	}

	/*printf("actual: %s\n", state_str(actual_state));*/
	/*printf("alleged: %s\n", state_str(alleged_state));*/

	bool equiv = state_equal(actual_state, alleged_state);

	state_destroy(alleged_state); /* actual_state handled by caller */ 
	
	if (!equiv) {
		/* XXX: print states */
		return error_create("actual and alleged states differ");
	}

	return NULL;
}

struct result *
ast_function_absexec(struct ast_function *f, struct state *state)
{
	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);
		if (result_iserror(res)) {
			return res;
		}
		result_destroy(res);
	}
	/* wrap result and return */ 
	struct object *obj = state_getresult(state);
	assert(obj);
	return result_value_create(object_as_value(obj));
}

M src/ast/gram.y => src/ast/gram.y +23 -47
@@ 5,6 5,7 @@
#include <string.h>
#include <assert.h>
#include "ast.h"
#include "expr/expr.h"
#include "gram_util.h"
#include "lex.h"
#include "util.h"


@@ 47,19 48,6 @@ strip_quotes(char *s)
	return t;
}

int
effectfromid(char *id)
{
	if (strcmp(id, "alloc") == 0) {
		return EFFECT_ALLOC;
	} else if (strcmp(id, "dealloc") == 0) {
		return EFFECT_DEALLOC;
	} else if (strcmp(id, "undefined") == 0) {
		return EFFECT_UNDEFINED;
	}
	assert(false);
}

struct stmt_array
stmt_array_create(struct ast_stmt *v)
{


@@ 116,7 104,7 @@ variable_array_create(struct ast_variable *v)

%token IDENTIFIER CONSTANT CHAR_LITERAL STRING_LITERAL SIZEOF
%token PTR_OP INC_OP DEC_OP LEFT_OP RIGHT_OP LE_OP GE_OP EQ_OP NE_OP
%token EQV_OP IMPL_OP FLLW_OP
%token ISDEALLOCAND_OP
%token AND_OP OR_OP MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN ADD_ASSIGN
%token SUB_ASSIGN LEFT_ASSIGN RIGHT_ASSIGN AND_ASSIGN
%token XOR_ASSIGN OR_ASSIGN TYPE_NAME


@@ 125,10 113,11 @@ variable_array_create(struct ast_variable *v)
%token TYPEDEF EXTERN STATIC AUTO REGISTER
%token CHAR SHORT INT LONG SIGNED UNSIGNED FLOAT DOUBLE CONST VOLATILE VOID
%token STRUCT UNION ENUM ELLIPSIS
%token AXIOM LEMMA SFUNC ASSERT
%token AXIOM LEMMA SFUNC
%token ARB_ARG

%token CASE DEFAULT IF ELSE SWITCH WHILE DO FOR SOME GOTO CONTINUE BREAK RETURN
%token ALLOC DEALLOC

%start translation_unit



@@ 179,18 168,18 @@ variable_array_create(struct ast_variable *v)
%type <block> block compound_statement compound_verification_statement
%type <block> optional_compound_verification
%type <block_statement> block_statement
%type <binary_operator> connective equality_operator relational_operator
%type <binary_operator> equality_operator relational_operator
%type <boolean> struct_or_union
%type <declarator> declarator init_declarator init_declarator_list
%type <direct_function_declarator> direct_function_declarator

%type <expr> expression assignment_expression conditional_expression unary_expression
%type <expr> justified_expression postfix_expression primary_expression
%type <expr> postfix_expression primary_expression
%type <expr> logical_or_expression logical_and_expression inclusive_or_expression
%type <expr> exclusive_or_expression and_expression equality_expression
%type <expr> relational_expression shift_expression additive_expression
%type <expr> multiplicative_expression cast_expression
%type <expr> memory_expression
%type <expr> isdeallocand_expression

%type <expr_array> argument_expression_list



@@ 200,7 189,7 @@ variable_array_create(struct ast_variable *v)
%type <integer> pointer
%type <statement> statement expression_statement selection_statement jump_statement 
%type <statement> labelled_statement iteration_statement for_iteration_statement
%type <statement> iteration_effect_statement
%type <statement> iteration_effect_statement allocation_statement
%type <stmt_array> statement_list
%type <string> identifier direct_declarator
%type <type> declaration_specifiers type_specifier struct_or_union_specifier 


@@ 266,24 255,15 @@ argument_expression_list
		{ $$ = expr_array_append(&$1, $3); }
	;

memory_expression
isdeallocand_expression
	: postfix_expression 
	| '.' identifier {
		$$ = ast_expr_memory_create(effectfromid($2), NULL);
		free($2);
	| ISDEALLOCAND_OP isdeallocand_expression {
		$$ = ast_expr_isdeallocand_create($2);
	}
	| '.' identifier memory_expression	{
		$$ = ast_expr_memory_create(effectfromid($2), $3);
		free($2);
	}
	| ASSERT memory_expression {
		$$ = ast_expr_assertion_create($2);
	}

	;

unary_expression
	: memory_expression
	: isdeallocand_expression
	| INC_OP unary_expression
		{ $$ = ast_expr_incdec_create($2, true, true); }
	| DEC_OP unary_expression


@@ 382,21 362,9 @@ logical_or_expression
	/*| logical_or_expression OR_OP logical_and_expression*/
	;

connective
	: EQV_OP 	{ $$ = BINARY_OP_EQV; }
	| IMPL_OP	{ $$ = BINARY_OP_IMPL; }
	/*| FLLW_OP	{ $$ = BINARY_OP_FLLW; }*/
	;

justified_expression
	: logical_or_expression
	| justified_expression connective justification logical_or_expression
		{ $$ = ast_expr_binary_create($1, $2, $4); }
	;

conditional_expression
	: justified_expression
	/*| justified_expression '?' expression ':' conditional_expression*/
	: logical_or_expression
	/*| logical_or_expression '?' expression ':' conditional_expression*/
	;

assignment_expression


@@ 451,7 419,7 @@ declaration_specifiers
		$$ = $2;
	}
	| declaration_modifier declaration_specifiers {
		$2->mod |= $1;
		ast_type_mod_or($2, $1);
		$$ = $2;
	}
	;


@@ 706,6 674,7 @@ statement
		{ $$ = ast_stmt_create_iter_e($1); }
	| compound_verification_statement
		{ $$ = ast_stmt_create_compound_v(lexloc(), $1); }
	| allocation_statement
	;

labelled_statement


@@ 746,6 715,13 @@ compound_verification_statement
	| '[' block ']'	{ $$ = $2; }
	;

allocation_statement
	: ALLOC postfix_expression ';'
		{ $$ = ast_stmt_create_alloc(lexloc(), $2); }
	| DEALLOC postfix_expression ';'
		{ $$ = ast_stmt_create_dealloc(lexloc(), $2); }
	;

declaration_list
	: declaration
		{ $$ = variable_array_create($1); }

A src/ast/intern.h => src/ast/intern.h +46 -0
@@ 0,0 1,46 @@
#ifndef XR0_AST_RESULT_H
#define XR0_AST_RESULT_H
#include <stdbool.h>

struct value;
struct error;

struct result;

struct result *
result_error_create(struct error *err);

struct result *
result_value_create(struct value *val);

void
result_destroy(struct result *);

bool
result_iserror(struct result *);

struct error *
result_as_error(struct result *);

struct value *
result_as_value(struct result *);

bool
result_hasvalue(struct result *);

struct object;
struct ast_type;

struct lvalue *
lvalue_create(struct ast_type *, struct object *);

void
lvalue_destroy(struct lvalue *);

struct ast_type *
lvalue_type(struct lvalue *);

struct object *
lvalue_object(struct lvalue *);

#endif

M src/ast/lex.l => src/ast/lex.l +3 -4
@@ 30,6 30,8 @@ check_type();
%%
"#"			{ preproc(); }
"/*"			{ comment(); }
".alloc"		{ count(); return(ALLOC); }
".dealloc"		{ count(); return(DEALLOC); }

"auto"			{ count(); return(AUTO); }
"axiom"			{ count(); return(AXIOM); }


@@ 103,9 105,6 @@ L?\"(\\.|[^\\"])*\"	{ count(); return(STRING_LITERAL); }
">="			{ count(); return(GE_OP); }
"=="			{ count(); return(EQ_OP); }
"!="			{ count(); return(NE_OP); }
"==="			{ count(); return(EQV_OP); }
"==>"			{ count(); return(IMPL_OP); }
"<=="			{ count(); return(FLLW_OP); }
";"			{ count(); return(';'); }
("{"|"<%")		{ count(); return('{'); }
("}"|"%>")		{ count(); return('}'); }


@@ 130,7 129,7 @@ L?\"(\\.|[^\\"])*\"	{ count(); return(STRING_LITERAL); }
"^"			{ count(); return('^'); }
"|"			{ count(); return('|'); }
"?"			{ count(); return('?'); }
"@"			{ count(); return(ASSERT); }
"@"			{ count(); return(ISDEALLOCAND_OP); }
"$"			{ count(); return(ARB_ARG); }

[ \t\v\n\f]		{ count(); }

A src/ast/stmt/stmt.c => src/ast/stmt/stmt.c +645 -0
@@ 0,0 1,645 @@
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "ast.h"
#include "lex.h"
#include "util.h"
#include "stmt.h"

struct ast_stmt {
	enum ast_stmt_kind kind;
	union {
		struct {
			char *label;
			struct ast_stmt *stmt;
		} labelled;
		struct ast_block *compound;
		struct {
			bool isswitch;
			struct ast_expr *cond;
			struct ast_stmt *body;
			struct ast_stmt *nest;
		} selection;
		struct {
			struct ast_stmt *init, *cond, *body;
			struct ast_expr *iter;
			struct ast_block *abstract;
		} iteration;
		struct ast_e