%header {
#include "grammar.hpp"
}

%source {
typedef std::pair<std::vector<solar::symbol_t>, std::vector<std::string>> rhs_t;


static inline void free_string(std::string *v) {
	delete v;
}

static inline void free_symbol(solar::symbol_t *v) {
	delete v;
}

static inline void free_rule(solar::rule_t *v) {
	delete v;
}
}


%type SYMBOL {std::string *} str
%destructor SYMBOL free_string

%type SYMBOL_UC {std::string *} str
%destructor SYMBOL_UC free_string

%type SYMBOL_LC {std::string *} str
%destructor SYMBOL_LC free_string

%type BLOCK {std::string *} str
%destructor BLOCK free_string

%type SQBLOCK {std::string *} str
%destructor SQBLOCK free_string

%type STRING {std::string *} str
%destructor STRING free_string

%type CHAR {char} c


%type rule {solar::rule_t *}
%destructor rule free_rule

%type rhs {rhs_t *}

%type action {std::string *}
%destructor action free_string

%type symbol {solar::symbol_t *}
%destructor symbol free_symbol

%type term {solar::symbol_t *}
%destructor term free_symbol

%type varname {std::string *}
%destructor varname free_string


%extra_arg {__attribute__((unused)) solar::grammar_t *} grammar


grammar |=;
grammar |= grammar directive;


directive |= "%type" SYMBOL_LC(nonterm) BLOCK(type) {
	grammar->nonterm_types.insert(std::make_pair(*nonterm, *type));
}

directive |= "%type" term(term) BLOCK(type) varname(name) {
	grammar->term_types.insert(std::make_pair(*term, std::make_pair(*type, *name)));
}

directive |= "%destructor" symbol(sym) varname(name) {
	grammar->destructors.insert(std::make_pair(*sym, *name));
}

directive |= "%source" BLOCK(block) {
	grammar->source_block = *block;
}

directive |= "%header" BLOCK(block) {
	grammar->header_block = *block;
}

directive |= "%extra_arg" BLOCK(type) varname(name) {
	grammar->extra_args.push_back(std::make_pair(*type, *name));
}

directive |= rule(rule) {
	if (grammar->rules.empty()) {
		solar::item_t init("");
		init.get_rhs().push_back(solar::symbol_t::make_nonterm(rule->item.get_lhs().c_str()));
		grammar->rules.emplace_back(solar::rule_t {std::move(init), std::vector<std::string>(), std::string()});
	}

	grammar->rules.push_back(*rule);
}


rule |= SYMBOL_LC(lhs) "|=" rhs(rhs) action(action) {
	auto *ret = new solar::rule_t {solar::item_t(*lhs, rhs->first), rhs->second, *action};
	delete rhs;
	return ret;
}


rhs |= [new rhs_t()]

rhs |= rhs(rhs) symbol(sym) {
	rhs->first.push_back(*sym);
	rhs->second.emplace_back();

	return rhs;
}

rhs |= rhs(rhs) symbol(sym) '(' varname(var) ')' {
	rhs->first.push_back(*sym);
	rhs->second.push_back(*var);

	return rhs;
}

rhs |= rhs(rhs) STRING(str) {
	for (char c : *str) {
		rhs->first.push_back(solar::symbol_t::make_char(c));
		rhs->second.emplace_back();
	}

	return rhs;
}


action |= ';' [new std::string]
action |= BLOCK(v) [new std::string(*v)]
action |= SQBLOCK(v) [new std::string("\n\treturn " + *v + ";\n")]


symbol |= term(v) [new solar::symbol_t(*v)]
symbol |= SYMBOL_LC(v) [new solar::symbol_t(solar::symbol_t::make_nonterm(*v))]

term |= SYMBOL_UC(v) [new solar::symbol_t(solar::symbol_t::make_term(*v))]
term |= CHAR(v) [new solar::symbol_t(solar::symbol_t::make_char(v))]

varname |= SYMBOL_LC(v) [new std::string(*v)]
varname |= SYMBOL_UC(v) [new std::string(*v)]
varname |= SYMBOL(v)[new std::string(*v)]