#ifndef HAREC_EXPR_H
#define HAREC_EXPR_H
#include <stdint.h>
#include "lex.h"
#include "types.h"

struct scope;
struct scope_object;

enum expr_type {
	EXPR_ACCESS,
	EXPR_ALLOC,
	EXPR_APPEND,
	EXPR_ASSERT,
	EXPR_ASSIGN,
	EXPR_BINARITHM,
	EXPR_BINDING,
	EXPR_BREAK,
	EXPR_CALL,
	EXPR_CAST,
	EXPR_COMPOUND,
	EXPR_CONTINUE,
	EXPR_DEFER,
	EXPR_DEFINE,
	EXPR_DELETE,
	EXPR_FOR,
	EXPR_FREE,
	EXPR_IF,
	EXPR_INSERT,
	EXPR_LEN,
	EXPR_MEASURE = EXPR_LEN, // for use in AST
	EXPR_LITERAL,
	EXPR_MATCH,
	EXPR_PROPAGATE,
	EXPR_RETURN,
	EXPR_SLICE,
	EXPR_STRUCT,
	EXPR_SWITCH,
	EXPR_TUPLE,
	EXPR_UNARITHM,
	EXPR_UNDEFINED,
	EXPR_VAARG,
	EXPR_VAEND,
	EXPR_VASTART,
	EXPR_YIELD,
};

struct expressions {
	struct expression *expr;
	struct expressions *next;
};

enum access_type {
	ACCESS_IDENTIFIER,
	ACCESS_INDEX,
	ACCESS_FIELD,
	ACCESS_TUPLE,
};

struct expression_access {
	enum access_type type;
	union {
		struct scope_object *object;
		struct {
			struct expression *array;
			struct expression *index;
			bool bounds_checked;
		};
		struct {
			struct expression *_struct;
			const struct struct_field *field;
		};
		struct {
			struct expression *tuple;
			const struct type_tuple *tvalue;
			size_t tindex;
		};
	};
};

enum alloc_kind {
	ALLOC_OBJECT,	// alloc(42)
	ALLOC_CAP,	// alloc([], 42)
	ALLOC_LEN,	// alloc([0...], 42)
	ALLOC_COPY,	// alloc(x...);
};

struct expression_alloc {
	enum alloc_kind kind;
	const struct type *allocation_result;
	struct expression *init;
	struct expression *cap;
};

struct expression_append {
	struct expression *object;
	struct expression *value;
	struct expression *length;
	bool is_static, is_multi;
};

enum fixed_aborts {
	ABORT_OOB = 0,
	ABORT_TYPE_ASSERTION = 1,
	ABORT_UNREACHABLE = 2,
	ABORT_CAP_TOO_SMALL = 3,
	ABORT_ANON_ASSERTION_FAILED = 4,
	ABORT_PROPAGATE_ERROR_OCCURRED = 5,
};

struct expression_assert {
	struct expression *cond;
	struct expression *message;
	enum fixed_aborts fixed_reason;
};

enum binarithm_operator {
	BIN_BAND,	// &
	BIN_BOR,	// |
	BIN_DIV,	// /
	BIN_GREATER,	// >
	BIN_GREATEREQ,	// >=
	BIN_LAND,	// &&
	BIN_LEQUAL,	// ==
	BIN_LESS,	// <
	BIN_LESSEQ,	// <=
	BIN_LOR,	// ||
	BIN_LSHIFT,	// <<
	BIN_LXOR,	// ^^
	BIN_MINUS,	// -
	BIN_MODULO,	// %
	BIN_NEQUAL,	// !=
	BIN_PLUS,	// +
	BIN_RSHIFT,	// >>
	BIN_TIMES,	// *
	BIN_BXOR,	// ^
	BIN_LAST = BIN_BXOR,
};

struct expression_assign {
	enum binarithm_operator op;
	struct expression *object, *value;
};

struct expression_binarithm {
	enum binarithm_operator op;
	struct expression *lvalue, *rvalue;
};

struct binding_unpack {
	const struct scope_object *object;
	size_t offset;
	struct binding_unpack *next;
};

struct expression_binding {
	const struct scope_object *object;
	struct binding_unpack *unpack;
	struct expression *initializer;
	struct expression_binding *next;
};

enum cast_kind {
	C_CAST,
	C_ASSERTION,
	C_TEST,
};

struct expression_cast {
	enum cast_kind kind;
	const struct type *secondary;
	struct expression *value;
	bool lowered;
};

struct call_argument {
	struct expression *value;
	struct call_argument *next;
};

struct expression_call {
	struct expression *lvalue;
	struct call_argument *args;
};

struct expression_compound {
	const char *label;
	struct scope *scope;
	struct expressions exprs;
};

struct array_literal {
	struct expression *value;
	struct array_literal *next;
};

// Invariant: these are sorted by field offset
struct struct_literal {
	const struct struct_field *field;
	struct expression *value;
	struct struct_literal *next;
};

struct tuple_literal {
	const struct type_tuple *field;
	struct expression *value;
	struct tuple_literal *next;
};

struct tagged_literal {
	const struct type *tag;
	struct expression *value;
};

struct expression_literal {
	// If non-null, ival is an offset from this object's address
	const struct scope_object *object;
	union {
		bool bval;
		double fval;
		int64_t ival;
		uint64_t uval;
		uint32_t rune;
		struct {
			size_t len;
			char *value;
		} string;
		struct {
			union {
				// if object is null
				struct array_literal *array;
				// if object is non-null
				size_t offset;
			};
			size_t start;
			size_t len;
			size_t cap;
		} slice;
		struct array_literal *array;
		struct struct_literal *_struct;
		struct tuple_literal *tuple;
		struct tagged_literal tagged;
	};
};

struct expression_control {
	const char *label;
	const struct scope *scope;
	struct expression *value; // Only set for yield and break
};

struct expression_defer {
	struct scope *scope;
	struct expression *deferred;
};

struct expression_delete {
	struct expression *expr;
	bool is_static;
};

enum for_kind {
	FOR_ACCUMULATOR,
	FOR_EACH_VALUE,
	FOR_EACH_POINTER,
	FOR_EACH_ITERATOR,
};

struct expression_for {
	enum for_kind kind;
	const char *label;
	struct scope *scope;
	struct expression *bindings;
	struct expression *cond;
	struct expression *afterthought;
	struct expression *body;
	struct expression *else_branch;
};

struct expression_free {
	struct expression *expr;
};

struct expression_if {
	struct expression *cond;
	struct expression *true_branch, *false_branch;
};

struct match_case {
	const struct scope_object *object;	// NULL if not bound
	const struct type *type;		// NULL if default
	struct expression *value;
	struct match_case *next;
};

struct expression_len {
	struct expression *value;
};

struct expression_match {
	struct expression *value;
	struct match_case *cases;
};

struct expression_return {
	struct expression *value;
};

struct expression_slice {
	struct expression *object;
	struct expression *start, *end;
	bool bounds_checked;
};

struct case_option {
	struct expression *value;
	struct case_option *next;
};

struct switch_case {
	struct case_option *options; // NULL for default case
	struct expression *value;
	struct switch_case *next;
};

struct expression_switch {
	struct expression *value;
	struct switch_case *cases;
};

struct expr_struct_field {
	const struct struct_field *field;
	struct expression *value;
	struct expr_struct_field *next;
};

struct expression_struct {
	struct expr_struct_field *fields;
	bool autofill, undefined;
};

struct expression_tuple {
	struct expression *value;
	struct expression_tuple *next;
};

enum unarithm_operator {
	UN_ADDRESS,	// &
	UN_BNOT,	// ~
	UN_DEREF,	// *
	UN_LNOT,	// !
	UN_MINUS,	// -
};

struct expression_unarithm {
	enum unarithm_operator op;
	struct expression *operand;
};

struct expression_vaarg {
	struct expression *ap;
};

struct expression {
	const struct type *result;
	enum expr_type type;
	struct location loc; // For fixed aborts
	union {
		struct expression_access access;
		struct expression_alloc alloc;
		struct expression_append append; // and insert
		struct expression_assert assert;
		struct expression_assign assign;
		struct expression_binarithm binarithm;
		struct expression_binding binding;
		struct expression_call call;
		struct expression_cast cast;
		struct expression_compound compound;
		struct expression_defer defer;
		struct expression_delete delete;
		struct expression_control control;
		struct expression_for _for;
		struct expression_free free;
		struct expression_if _if;
		struct expression_len len;
		struct expression_literal literal;
		struct expression_match match;
		struct expression_return _return;
		struct expression_switch _switch;
		struct expression_struct _struct;
		struct expression_slice slice;
		struct expression_tuple tuple;
		struct expression_unarithm unarithm;
		struct expression_vaarg vaarg;
		void *user;
	};
};

uint32_t expr_hash(const struct expression *expr);
bool expr_equal(const struct expression *a, const struct expression *b);

#endif
