/* Tuplr: A programming language
 * Copyright (C) 2008-2009 David Robillard <dave@drobilla.net>
 *
 * Tuplr is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * Tuplr is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General
 * Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Tuplr.  If not, see <http://www.gnu.org/licenses/>.
 */

/** @file
 * @brief Parsing (build a code AST from a textual AST)
 */

#include "tuplr.hpp"

using namespace std;


/***************************************************************************
 * Macro Functions                                                         *
 ***************************************************************************/

inline AST*
macDef(PEnv& penv, const AST* exp)
{
	const ATuple* tup = exp->to<const ATuple*>();
	ATuple::const_iterator i = tup->begin();
	THROW_IF(i == tup->end(), tup->loc, "Unexpected end of `def' macro call");
	const AST* name = *(++i);
	THROW_IF(i == tup->end(), name->loc, "Unexpected end of `def' macro call");
	if (name->to<const AString*>()) {
		return const_cast<AST*>(exp);
	} else {
		const ATuple* pat = name->to<const ATuple*>();
		name = pat->at(0);
		// (def (f x) y) => (def f (fn (x) y))
		ATuple* argsExp = new ATuple(exp->loc);
		ATuple::const_iterator j = pat->begin();
		for (++j; j != pat->end(); ++j)
			argsExp->push_back(*j);
		const AST* body = *(++i);
		ATuple* fnExp = new ATuple(body->loc);
		fnExp->push_back(new AString(exp->loc, "fn"));
		fnExp->push_back(argsExp);
		for (; i != tup->end(); ++i)
			fnExp->push_back(*i);
		ATuple* ret = new ATuple(exp->loc);
		ret->push_back(const_cast<AST*>(tup->front()));
		ret->push_back(const_cast<AST*>(name));
		ret->push_back(fnExp);
		return ret;
	}
}


/***************************************************************************
 * Parser Functions                                                        *
 ***************************************************************************/

template<typename C>
inline AST*
parseCall(PEnv& penv, const AST* exp, void* arg)
{
	return new C(penv.parseTuple(exp->to<const ATuple*>()));
}

template<typename T>
inline AST*
parseLiteral(PEnv& penv, const AST* exp, void* arg)
{
	return new ALiteral<T>(*reinterpret_cast<T*>(arg), exp->loc);
}

inline AST*
parseFn(PEnv& penv, const AST* exp, void* arg)
{
	const ATuple* texp = exp->to<const ATuple*>();
	ATuple::const_iterator a = texp->begin();
	THROW_IF(++a == texp->end(), exp->loc, "Unexpected end of `fn' form");
	ATuple* prot = penv.parseTuple((*a++)->to<const ATuple*>());
	AFn* ret = tup<AFn>(exp->loc, penv.sym("fn"), prot, 0);
	while (a != texp->end())
		ret->push_back(penv.parse(*a++));
	return ret;
}


/***************************************************************************
 * Language Definition                                                     *
 ***************************************************************************/

/// Set up language
void
initLang(PEnv& penv, TEnv& tenv)
{
	// Types
	tenv.def(penv.sym("Nothing"), make_pair((AST*)0, new AType(penv.sym("Nothing"))));
	tenv.def(penv.sym("Bool"),    make_pair((AST*)0, new AType(penv.sym("Bool"))));
	tenv.def(penv.sym("Int"),     make_pair((AST*)0, new AType(penv.sym("Int"))));
	tenv.def(penv.sym("Float"),   make_pair((AST*)0, new AType(penv.sym("Float"))));
	tenv.def(penv.sym("String"),   make_pair((AST*)0, new AType(penv.sym("String"))));

	// Literals
	static bool trueVal  = true;
	static bool falseVal = false;
	penv.reg(false, "#t", PEnv::Handler(parseLiteral<bool>, &trueVal));
	penv.reg(false, "#f", PEnv::Handler(parseLiteral<bool>, &falseVal));

	// Macros
	penv.defmac("def",  macDef);

	// Special forms
	penv.reg(true, "fn",  PEnv::Handler(parseFn));
	penv.reg(true, "if",  PEnv::Handler(parseCall<AIf>));
	penv.reg(true, "def", PEnv::Handler(parseCall<ADef>));

	// Numeric primitives
	penv.reg(true, "+",   PEnv::Handler(parseCall<APrimitive>));
	penv.reg(true, "-",   PEnv::Handler(parseCall<APrimitive>));
	penv.reg(true, "*",   PEnv::Handler(parseCall<APrimitive>));
	penv.reg(true, "/",   PEnv::Handler(parseCall<APrimitive>));
	penv.reg(true, "%",   PEnv::Handler(parseCall<APrimitive>));
	penv.reg(true, "and", PEnv::Handler(parseCall<APrimitive>));
	penv.reg(true, "or",  PEnv::Handler(parseCall<APrimitive>));
	penv.reg(true, "xor", PEnv::Handler(parseCall<APrimitive>));
	penv.reg(true, "=",   PEnv::Handler(parseCall<APrimitive>));
	penv.reg(true, "!=",  PEnv::Handler(parseCall<APrimitive>));
	penv.reg(true, ">",   PEnv::Handler(parseCall<APrimitive>));
	penv.reg(true, ">=",  PEnv::Handler(parseCall<APrimitive>));
	penv.reg(true, "<",   PEnv::Handler(parseCall<APrimitive>));
	penv.reg(true, "<=",  PEnv::Handler(parseCall<APrimitive>));
}