use lexical::lex;
use haast = lexical::hare::ast;
use strings;

// A formattable expression.
//
// 	foo.bar
// 	"foo"
// 	i + 1
// 	i + 1, z
export type fmt_expr = struct {
	values: []*haast::expr,
};

// An if expression.
//
// 	if (foo)
export type if_expr = struct {
	cond: *haast::expr,
};

// An for expression.
//
// 	for (let foo = 0; foo < bar; baz)
// 	for (let line => next_line())
// 	for (let number .. [1, 2, 3])
// 	for (let ptr &.. [1, 2, 3])
export type for_expr = struct {
	kind: haast::for_kind,
	bindings: nullable *haast::expr,
	cond: nullable *haast::expr,
	afterthought: nullable *haast::expr,
};

// A switch expression.
//
// 	switch (foo)
export type switch_expr = struct {
	value: *haast::expr,
};

// A match expression.
//
// 	match (foo)
export type match_expr = struct {
	value: *haast::expr,
};

// A match case.
//
//	case
//	case type
//	case let name: type
export type match_case = struct {
	name: str,
	_type: nullable *haast::_type, // null for default case
};

// A switch case.
//
// 	case
// 	case value
export type switch_case = struct {
	options: []*haast::expr, // [] for default case
};

// A variable binding expression.
//
// 	const foo: int = bar, ...
export type binding_expr = struct {
	kind: haast::binding_kind,
	bindings: []haast::binding,
};

// A hare-template expression.
export type expr = struct {
	start: lex::location,
	end: lex::location,
	expr: (fmt_expr | if_expr | for_expr | switch_expr |
		match_expr | match_case | switch_case | binding_expr),
};

// Frees resources associated with a hare-template [[expr]]ession.
export fn expr_finish(e: nullable *expr) void = {
	match (e) {
	case null => void;
	case let e: *expr =>
		match (e.expr) {
		case let e: fmt_expr =>
			for (let e .. e.values) {
				haast::expr_finish(e);
				free(e);
			};
			free(e.values);
		case let e: if_expr =>
			haast::expr_finish(e.cond);
			free(e.cond);
		case let e: for_expr =>
			haast::expr_finish(e.bindings);
			free(e.bindings);
			haast::expr_finish(e.cond);
			free(e.cond);
			haast::expr_finish(e.afterthought);
			free(e.afterthought);
		case let e: switch_expr =>
			haast::expr_finish(e.value);
			free(e.value);
		case let e: match_expr =>
			haast::expr_finish(e.value);
			free(e.value);
		case let e: match_case =>
			haast::type_finish(e._type);
			free(e._type);
			free(e.name);
		case let e: switch_case =>
			for (let e .. e.options) {
				haast::expr_finish(e);
				free(e);
			};
			free(e.options);
		case let e: binding_expr =>
			for (let e .. e.bindings) {
				strings::freeall(e.names);
				haast::type_finish(e._type);
				free(e._type);
				haast::expr_finish(e.init);
				free(e.init);
			};
			free(e.bindings);
		case => abort();
		};
	};
};
