/* Resp: A programming language
 * Copyright (C) 2008-2009 David Robillard <dave@drobilla.net>
 *
 * Resp 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.
 *
 * Resp 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 Resp.  If not, see <http://www.gnu.org/licenses/>.
 */

/** @file
 * @brief Interface and type definitions
 */

#ifndef RESP_HPP
#define RESP_HPP

#include <stdarg.h>
#include <stdint.h>
#include <string.h>
#include <iostream>
#include <list>
#include <map>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <vector>
#include <boost/format.hpp>

#define FOREACH(IT, i, c)  for (IT i = (c).begin(); i != (c).end(); ++i)
#define FOREACHP(IT, i, c) for (IT i = (c)->begin(); i != (c)->end(); ++i)
#define THROW_IF(cond, error, ...) { if (cond) throw Error(error, __VA_ARGS__); }

using namespace std;
using boost::format;


/***************************************************************************
 * Basic Utility Classes                                                   *
 ***************************************************************************/

/// Location in textual code
struct Cursor {
	Cursor(const string& n="", unsigned l=1, unsigned c=0) : name(n), line(l), col(c) {}
	operator bool() const { return !(line == 1 && col == 0); }
	string str() const { return (format("%1%:%2%:%3%") % name % line % col).str(); }
	string   name;
	unsigned line;
	unsigned col;
};

/// Compilation error
struct Error {
	Error(Cursor c, const string& m) : loc(c), msg(m) {}
	const string what() const { return (loc ? loc.str() + ": " : "") + "error: " + msg; }
	const Cursor loc;
	const string msg;
};

/// Generic Lexical Environment
template<typename K, typename V>
struct Env : public list< vector< pair<K,V> > > {
	typedef vector< pair<K,V> > Frame;
	Env() : list<Frame>(1) {}
	virtual ~Env() {}
	virtual void push(Frame f=Frame()) { list<Frame>::push_front(f); }
	virtual void pop()                 { list<Frame>::pop_front(); }
	const V& def(const K& k, const V& v) {
		for (typename Frame::iterator b = this->begin()->begin(); b != this->begin()->end(); ++b)
			if (b->first == k)
				return (b->second = v);
		this->front().push_back(make_pair(k, v));
		return v;
	}
	V* ref(const K& key) {
		for (typename Env::iterator f = this->begin(); f != this->end(); ++f)
			for (typename Frame::iterator b = f->begin(); b != f->end(); ++b)
				if (b->first == key)
					return &b->second;
		return NULL;
	}
	bool contains(const Frame& frame, const K& key) const {
		for (typename Frame::const_iterator b = frame.begin(); b != frame.end(); ++b)
			if (b->first == key)
				return true;
		return false;
	}
	bool topLevel(const K& key) const { return contains(this->back(), key); }
	bool innermost(const K& key) const { return contains(this->front(), key); }
};

template<typename K, typename V>
ostream& operator<<(ostream& out, const Env<K,V>& env) {
	out << "(Env" << endl;
	for (typename Env<K,V>::const_reverse_iterator f = env.rbegin(); f != env.rend(); ++f) {
		out << "  (" << endl;
		for (typename Env<K,V>::Frame::const_iterator b = f->begin(); b != f->end(); ++b)
			cout << "    " << b->first << " " << b->second << endl;
		out << "  )" << endl;
	}
	out << ")" << endl;
	return out;
}


/***************************************************************************
 * Lexer: Text (istream) -> S-Expressions (SExp)                           *
 ***************************************************************************/

struct AST;
AST* readExpression(Cursor& cur, std::istream& in);


/***************************************************************************
 * Backend Types                                                           *
 ***************************************************************************/

typedef void* CVal;  ///< Compiled value (opaque)
typedef void* CFunc; ///< Compiled function (opaque)


/***************************************************************************
 * Garbage Collection                                                      *
 ***************************************************************************/

struct Object;

enum Tag {
	T_UNKNOWN = 1<<1,
	T_BOOL    = 1<<2,
	T_FLOAT   = 1<<3,
	T_INT32   = 1<<4,
	T_LEXEME  = 1<<5,
	T_STRING  = 1<<6,
	T_SYMBOL  = 1<<7,
	T_TUPLE   = 1<<8,
	T_TYPE    = 1<<9
};

/// Garbage collector
struct GC {
	typedef std::list<const Object*> Roots;
	typedef std::list<Object*>       Heap;
	GC(size_t pool_size);
	~GC();
	void* alloc(size_t size);
	void  collect(const Roots& roots);
	void  addRoot(const Object* obj) { assert(obj); _roots.push_back(obj); }
	void  lock() { _roots.insert(_roots.end(), _heap.begin(), _heap.end()); }
	const Roots& roots() const { return _roots; }
private:
	void* _pool;
	Heap  _heap;
	Roots _roots;
};

/// Garbage collected object (including AST and runtime data)
struct Object {
	struct Header {
		uint32_t tag; ///< Rightmost bit is mark
	};

	inline Tag  tag()        const { return (Tag)((header()->tag >> 1) << 1); }
	inline void tag(Tag t)         { header()->tag = (t | (marked() ? 1 : 0)); }
	inline bool marked()     const { return (header()->tag & 1); }
	inline void mark(bool b) const {
		if (b)
			header()->tag |= 1;
		else
			header()->tag = ((header()->tag >> 1) << 1);
	}

	static void* operator new(size_t size)  { return pool.alloc(size); }
	static void  operator delete(void* ptr) {}

	// Memory used with placement new MUST always be allocated with pool.alloc!
	static void* operator new(size_t size, void* ptr) { return ptr; }

	static GC pool;

private:
	/// Always allocated with pool.alloc, so this - sizeof(Header) is a valid Header*.
	inline Header* header() const { return (Header*)((char*)this - sizeof(Header)); }
};


/***************************************************************************
 * Abstract Syntax Tree                                                    *
 ***************************************************************************/

struct TEnv;        ///< Type-Time Environment
struct Constraints; ///< Type Constraints
struct Subst;       ///< Type substitutions
struct CEnv;        ///< Compile-Time Environment

struct AST;
extern ostream& operator<<(ostream& out, const AST* ast);

typedef list<AST*> Code;

/// Base class for all AST nodes
struct AST : public Object {
	AST(Tag t, Cursor c=Cursor()) : loc(c) { this->tag(t); }
	virtual ~AST() {}
	bool operator==(const AST& o) const;
	string str() const { ostringstream ss; ss << this; return ss.str(); }
	template<typename T> T       to()       { return dynamic_cast<T>(this); }
	template<typename T> T const to() const { return dynamic_cast<T const>(this); }
	template<typename T> T as() {
		T t = dynamic_cast<T>(this);
		return t ? t : throw Error(loc, "internal error: bad cast");
	}
	template<typename T> T const as() const {
		T const t = dynamic_cast<T const>(this);
		return t ? t : throw Error(loc, "internal error: bad cast");
	}
	Cursor loc;
};

template<typename T>
static T* tup(Cursor c, AST* ast, ...)
{
	va_list args;
	va_start(args, ast);
	T* ret = new T(c, ast, args);
	va_end(args);
	return ret;
}

/// Literal value
template<typename T>
struct ALiteral : public AST {
	ALiteral(Tag tag, T v, Cursor c) : AST(tag, c), val(v) {}
	const T val;
};

/// Lexeme (any atom in the CST, e.g. "a", "3.4", ""hello"", etc.)
struct ALexeme : public AST {
	ALexeme(Cursor c, const string& s) : AST(T_LEXEME, c), cppstr(s) {}
	const string cppstr;
};

/// String, e.g. ""a""
struct AString : public AST {
	AString(Cursor c, const string& s) : AST(T_STRING, c), cppstr(s) {}
	const string cppstr;
};

/// Symbol, e.g. "a"
struct ASymbol : public AST {
	const string cppstr;
private:
	friend class PEnv;
	ASymbol(const string& s, Cursor c) : AST(T_SYMBOL, c), cppstr(s) {}
};

/// Tuple (heterogeneous sequence of fixed length), e.g. "(a b c)"
struct ATuple : public AST {
	ATuple(Cursor c) : AST(T_TUPLE, c), _len(0), _vec(0) {}
	ATuple(const ATuple& exp) : AST(T_TUPLE, exp.loc), _len(exp._len) {
		_vec = (AST**)malloc(sizeof(AST*) * _len);
		memcpy(_vec, exp._vec, sizeof(AST*) * _len);
	}
	ATuple(AST* first, AST* rest, Cursor c=Cursor()) : AST(T_TUPLE, c), _len(2) {
		_vec = (AST**)malloc(sizeof(AST*) * _len);
		_vec[0] = first;
		_vec[1] = rest;
	}
	ATuple(Cursor c, AST* ast, va_list args) : AST(T_TUPLE, c), _len(0), _vec(0) {
		if (!ast) return;
		
		_len = 2;
		_vec = (AST**)malloc(sizeof(AST*) * _len);
		_vec[0] = ast;
		_vec[1] = NULL;

		ATuple* tail = this;
		for (AST* a = va_arg(args, AST*); a; a = va_arg(args, AST*)) {
			ATuple* tup = new ATuple(a, NULL);
			tail->last(tup);
			tail = tup;
		}
	}
	
	~ATuple() { free(_vec); }
	const AST* head()  const { assert(_len > 0); return _vec[0]; }
	AST*&      head()        { assert(_len > 0); return _vec[0]; }
	const AST* last()  const { return _vec[_len - 1]; }
	AST*&      last()        { return _vec[_len - 1]; }
	bool       empty() const { return _len == 0; }

	size_t tup_len() const { return _len; }
	size_t list_len() const {
		size_t ret = 0;
		for (const_iterator i = begin(); i != end(); ++i, ++ret) {}
		return ret;
	}

	const AST* list_last() const {
		for (const_iterator i = begin(); i != end();) {
			const_iterator next = i;
			++next;

			if (next == end())
				return *i;

			i = next;
		}

		return NULL;
	}

	void last(AST* ast) { _vec[_len - 1] = ast; }
	
	struct iterator {
		iterator(ATuple* n) : node(n) {
			assert(!n || n->tup_len() == 0 || n->tup_len() == 2);
			if (!n || n->tup_len() == 0)
				node = NULL;
		}
		inline void increment() {
			if (node->last())
				node = node->last()->as<ATuple*>();
			else
				node = NULL;
		}
		inline iterator& operator++() {
			assert(node);
			increment();
			return *this;
		}
		inline iterator operator++(int) {
			assert(node);
			const iterator ret(node);
			increment();
			return ret;
		}
		inline bool operator==(const iterator& i) const { return node == i.node; }
		inline bool operator!=(const iterator& i) const { return node != i.node; }
		AST*& operator*() { return node->head(); }
		ATuple* node;
	};

	struct const_iterator {
		const_iterator(const ATuple* n) : node(n) {
			assert(!n || n->tup_len() == 0 || n->tup_len() == 2);
			if (!n || n->tup_len() == 0)
				node = NULL;
		}
		const_iterator(const iterator& i) : node(i.node) {}
		inline void increment() {
			if (node->last())
				node = node->last()->as<const ATuple*>();
			else
				node = NULL;
		}
		inline const_iterator& operator++() {
			assert(node);
			increment();
			return *this;
		}
		inline const_iterator operator++(int) {
			assert(node);
			const const_iterator ret(node);
			increment();
			return ret;
		}
		inline bool operator==(const const_iterator& i) const {
			return node == i.node || (!node && i.node->tup_len() == 0);
		}
		inline bool operator!=(const const_iterator& i) const {
			return !operator==(i);
		}
		const AST* operator*() { return node->head(); }
		const ATuple* node;
	};
		
	const_iterator begin() const { assert(_len == 0 || _len == 2); return const_iterator(this); }
	iterator       begin()       { assert(_len == 0 || _len == 2); return iterator(this); }
	const_iterator end()   const { return const_iterator(NULL); }
	iterator       end()         { return iterator(NULL); }

	const_iterator iter_at(unsigned index) const {
		const_iterator i = begin();
		for (unsigned idx = 0; idx != index; ++i, ++idx) {
			assert(i != end());
		}
		return i;
	}

	iterator iter_at(unsigned index) {
		iterator i = begin();
		for (unsigned idx = 0; idx != index; ++i, ++idx) {
			assert(i != end());
		}
		return i;
	}
			
	AST*&      list_ref(unsigned index)       { return *iter_at(index); }
	const AST* list_ref(unsigned index) const { return *iter_at(index); }
	
	const ATuple* prot() const           { return list_ref(1)->as<const ATuple*>(); }
	ATuple*       prot()                 { return list_ref(1)->as<ATuple*>(); }
	void          set_prot(ATuple* prot) { *iter_at(1) = prot; }
	
private:
	size_t _len;
	AST**  _vec;
};

static bool
list_contains(const ATuple* head, const AST* child) {
	if (*head == *child)
		return true;
		
	FOREACHP(ATuple::const_iterator, p, head) {
		if (**p == *child)
			return true;
		
		const ATuple* tup = (*p)->to<const ATuple*>();
		if (tup && list_contains(tup, child))
			return true;
	}

	return false;
}

/// Type Expression, e.g. "Int", "(Fn (Int Int) Float)"
struct AType : public ATuple {
	enum Kind { VAR, NAME, PRIM, EXPR, DOTS };
	AType(ASymbol* s, Kind k) : ATuple(s, NULL, s->loc), kind(k), id(0) { tag(T_TYPE); }
	AType(Cursor c, unsigned i) : ATuple(c), kind(VAR), id(i) { tag(T_TYPE); }
	AType(Cursor c, Kind k=EXPR) : ATuple(c), kind(k), id(0) { tag(T_TYPE); }
	AType(Cursor c, AST* ast, va_list args) : ATuple(c, ast, args), kind(EXPR), id(0) { tag(T_TYPE); }
	AType(AST* first, AST* rest, Cursor c) : ATuple(first, rest, c), kind(EXPR), id(0) { tag(T_TYPE); }
	AType(const AType& copy) : ATuple(copy), kind(copy.kind), id(copy.id) { tag(T_TYPE); }

	bool concrete() const {
		switch (kind) {
		case VAR:  return false;
		case NAME: return false;
		case PRIM: return head()->str() != "Nothing";
		case EXPR:
			FOREACHP(const_iterator, t, this) {
				const AType* kid = (*t)->to<const AType*>();
				if (kid && !kid->concrete())
					return false;
			}
		case DOTS: return false;
		}
		return true;
	}
	Kind     kind;
	unsigned id;
};

// Utility class for easily building lists from left to right
template<typename CT, typename ET> // ConsType, ElementType
struct List {
	List(CT* h=0) : head(h), tail(0) {}
	List(Cursor c, ET* ast, ...) : head(0), tail(0) {
		push_back(ast);
		assert(*head->begin() == ast);
		head->loc = c;
		va_list args;
		va_start(args, ast);
		for (ET* a = va_arg(args, ET*); a; a = va_arg(args, ET*))
			push_back(a);
		va_end(args);
	}
	void push_back(ET* ast) {
		if (!head) {
			head = new CT(ast, NULL, Cursor());
		} else if (!tail) {
			CT* node = new CT(ast, NULL, Cursor());
			head->last(node);
			tail = node;
		} else {
			CT* node = new CT(ast, NULL, Cursor());
			tail->last(node);
			tail = node;
		}
	}
	void push_front(ET* ast) {
		head = new CT(ast, head, Cursor());
	}
	operator CT*() const { return head; }
	CT* head;
	CT* tail;
};

typedef List<AType,  AType> TList;

inline bool
list_equals(const ATuple* lhs, const ATuple* rhs)
{
	if (!rhs || rhs->tup_len() != lhs->tup_len()) return false;
	ATuple::const_iterator l = lhs->begin();
	FOREACHP(ATuple::const_iterator, r, rhs)
		if (!(*(*l++) == *(*r)))
			return false;
	return true;
}

template<typename T>
inline bool
literal_equals(const ALiteral<T>* lhs, const ALiteral<T>* rhs)
{
	return lhs && rhs && lhs->val == rhs->val;
}

inline bool
AST::operator==(const AST& rhs) const
{
	const Tag tag = this->tag();
	if (tag != rhs.tag())
		return false;
		
	switch (tag) {
	case T_BOOL:
		return literal_equals(this->as<const ALiteral<bool>*>(), rhs.as<const ALiteral<bool>*>());
	case T_FLOAT:
		return literal_equals(this->as<const ALiteral<float>*>(), rhs.as<const ALiteral<float>*>());
	case T_INT32:
		return literal_equals(this->as<const ALiteral<int32_t>*>(), rhs.as<const ALiteral<int32_t>*>());
	case T_TUPLE:
	{
		const ATuple* me = this->as<const ATuple*>();
		const ATuple* rt = rhs.to<const ATuple*>();
		return list_equals(me, rt);
	}
	case T_TYPE:
	{
		const AType* me = this->as<const AType*>();
		const AType* rt = rhs.to<const AType*>();
		if (!rt || me->kind != rt->kind) {
			assert(str() != rt->str());
			return false;
		} else
			switch (me->kind) {
			case AType::VAR:   return me->id == rt->id;
			case AType::NAME:  return me->head()->str() == rt->head()->str();
			case AType::PRIM:  return me->head()->str() == rt->head()->str();
			case AType::EXPR:  return list_equals(me, rt);
			case AType::DOTS:  return true;
			}
		return false; // never reached
	}

	case T_UNKNOWN:
	case T_LEXEME:
	case T_STRING:
	case T_SYMBOL:
		return this == &rhs;
	}
	return false;
}

/***************************************************************************
 * Parser: S-Expressions (SExp) -> AST Nodes (AST)                         *
 ***************************************************************************/

/// Parse Time Environment (really just a symbol table)
struct PEnv : private map<const string, ASymbol*> {
	PEnv() : symID(0) {}
	typedef AST* (*PF)(PEnv&, const AST*, void*); ///< Parse Function
	typedef AST* (*MF)(PEnv&, const AST*); ///< Macro Function
	struct Handler { Handler(PF f, void* a=0) : func(f), arg(a) {} PF func; void* arg; };
	map<const string, Handler> aHandlers; ///< Atom parse functions
	map<const string, Handler> lHandlers; ///< List parse functions
	map<const string, MF>      macros; ///< Macro functions
	void reg(bool list, const string& s, const Handler& h) {
		(list ? lHandlers : aHandlers).insert(make_pair(sym(s)->str(), h));
	}
	const Handler* handler(bool list, const string& s) const {
		const map<const string, Handler>& handlers = list ? lHandlers : aHandlers;
		map<string, Handler>::const_iterator i = handlers.find(s);
		return (i != handlers.end()) ? &i->second : NULL;
	}
	void defmac(const string& s, const MF f) {
		macros.insert(make_pair(s, f));
	}
	MF mac(const ALexeme& s) const {
		map<string, MF>::const_iterator i = macros.find(s.cppstr);
		return (i != macros.end()) ? i->second : NULL;
	}
	string gensymstr(const char* s="_") { return (format("%s_%d") % s % symID++).str(); }
	ASymbol* gensym(const char* s="_") { return sym(gensymstr(s)); }
	ASymbol* sym(const string& s, Cursor c=Cursor()) {
		const const_iterator i = find(s);
		if (i != end()) {
			return i->second;
		} else {
			ASymbol* sym = new ASymbol(s, c);
			insert(make_pair(s, sym));
			return sym;
		}
	}
	AST* parse(const AST* exp);

	typedef std::set<std::string> Primitives;
	Primitives primitives;

	unsigned symID;
};


/***************************************************************************
 * Typing                                                                  *
 ***************************************************************************/

/// Type constraint
struct Constraint : public pair<const AType*,const AType*> {
	Constraint(const AType* a, const AType* b)
		: pair<const AType*, const AType*>(a, b) {}
};

/// Type substitution
struct Subst : public list<Constraint> {
	Subst(const AType* s=0, const AType* t=0) {
		if (s && t) { assert(s != t); push_back(Constraint(s, t)); }
	}
	static Subst compose(const Subst& delta, const Subst& gamma);
	void add(const AType* from, const AType* to) { push_back(Constraint(from, to)); }
	const_iterator find(const AType* t) const {
		for (const_iterator j = begin(); j != end(); ++j)
			if (*j->first == *t)
				return j;
		return end();
	}
	const AType* apply(const AType* in) const {
		if (in->kind == AType::EXPR) {
			TList out;
			for (ATuple::const_iterator i = in->begin(); i != in->end(); ++i)
				out.push_back(const_cast<AType*>(apply((*i)->as<const AType*>())));
			out.head->loc = in->loc;
			return out.head;
		} else {
			const_iterator i = find(in);
			if (i != end()) {
				const AType* out = i->second->as<const AType*>();
				if (out->kind == AType::EXPR && !out->concrete())
					out = const_cast<AType*>(apply(out->as<const AType*>()));
				return out;
			} else {
				return new AType(*in);
			}
		}
	}
	bool contains(const AType* type) const {
		if (find(type) != end())
			return true;
		FOREACHP(const_iterator, j, this)
			if (*j->second == *type || list_contains(j->second, type))
				return true;
		return false;
	}
};

inline ostream& operator<<(ostream& out, const Subst& s) {
	for (Subst::const_iterator i = s.begin(); i != s.end(); ++i)
		out << i->first << " => " << i->second << endl;
	return out;
}

/// Type constraint set
struct Constraints : public list<Constraint> {
	Constraints() : list<Constraint>() {}
	Constraints(const Subst& subst) : list<Constraint>() {
		FOREACH(Subst::const_iterator, i, subst)
			push_back(Constraint(new AType(*i->first), new AType(*i->second)));
	}
	Constraints(const_iterator begin, const_iterator end) : list<Constraint>(begin, end) {}
	void constrain(TEnv& tenv, const AST* o, const AType* t);
	Constraints& replace(const AType* s, const AType* t);
};

inline ostream& operator<<(ostream& out, const Constraints& c) {
	for (Constraints::const_iterator i = c.begin(); i != c.end(); ++i)
		out << i->first << " <= " << i->second << endl;
	return out;
}

/// Type-Time Environment
struct TEnv : public Env<const ASymbol*, const AType*> {
	TEnv(PEnv& p)
		: penv(p)
		, varID(1)
		, Fn(new AType(penv.sym("Fn"), AType::PRIM))
		, Tup(new AType(penv.sym("Tup"), AType::NAME))
		, U(new AType(penv.sym("U"), AType::PRIM))
	{
		Object::pool.addRoot(Fn);
	}
	const AType* fresh(const ASymbol* sym) {
		return def(sym, new AType(sym->loc, varID++));
	}
	const AType* var(const AST* ast=0) {
		if (!ast)
			return new AType(Cursor(), varID++);

		Vars::iterator v = vars.find(ast);
		if (v != vars.end())
			return v->second;

		return (vars[ast] = new AType(ast->loc, varID++));
	}
	const AType* named(const string& name) {
		return *ref(penv.sym(name));
	}
	static Subst buildSubst(const AType* fnT, const AType& argsT);

	typedef map<const AST*, const AType*> Vars;

	Vars     vars;
	PEnv&    penv;
	unsigned varID;

	AType* Fn;
	AType* Tup;
	AType* U;
};

Subst unify(const Constraints& c);


/***************************************************************************
 * Code Generation                                                         *
 ***************************************************************************/

/// Compiler backend
struct Engine {
	virtual ~Engine() {}

	typedef const vector<CVal> ValVec;

	virtual CFunc startFunction(
			CEnv&                cenv,
			const std::string&   name,
			const ATuple*        args,
			const AType*         type) = 0;

	virtual void pushFunctionArgs(CEnv& cenv, const ATuple* fn, const AType* type, CFunc f) = 0;

	virtual void  finishFunction(CEnv& cenv, CFunc f, CVal ret)                          = 0;
	virtual void  eraseFunction(CEnv& cenv, CFunc f)                                     = 0;
	virtual CVal  compileTup(CEnv& cenv, const AType* t, CVal rtti, ValVec& f)           = 0;
	virtual CVal  compileDot(CEnv& cenv, CVal tup, int32_t index)                        = 0;
	virtual CVal  compileLiteral(CEnv& cenv, const AST* lit)                             = 0;
	virtual CVal  compileString(CEnv& cenv, const char* str)                             = 0;
	virtual CVal  compileCall(CEnv& cenv, CFunc f, const AType* fT, ValVec& args)        = 0;
	virtual CVal  compilePrimitive(CEnv& cenv, const ATuple* prim)                        = 0;
	virtual CVal  compileIf(CEnv& cenv, const ATuple* aif)                                = 0;
	virtual CVal  compileMatch(CEnv& cenv, const ATuple* match)                           = 0;
	virtual CVal  compileGlobal(CEnv& cenv, const AType* t, const string& sym, CVal val) = 0;
	virtual CVal  getGlobal(CEnv& cenv, const string& sym, CVal val)                     = 0;
	virtual void  writeModule(CEnv& cenv, std::ostream& os)                              = 0;

	virtual const string call(CEnv& cenv, CFunc f, const AType* retT) = 0;
};

Engine* resp_new_llvm_engine();
Engine* resp_new_c_engine();

/// Compile-Time Environment
struct CEnv {
	CEnv(PEnv& p, TEnv& t, Engine* e, ostream& os=std::cout, ostream& es=std::cerr)
		: out(os), err(es), penv(p), tenv(t), currentFn(NULL), _engine(e)
	{}

	~CEnv() { Object::pool.collect(GC::Roots()); }

	typedef Env<const ASymbol*, CVal> Vals;

	Engine* engine() { return _engine; }
	void    push() { code.push(); tenv.push(); vals.push(); }
	void    pop()  { code.pop(); tenv.pop();  vals.pop();  }
	void    lock(const AST* ast) {
		Object::pool.addRoot(ast);
		if (type(ast))
			Object::pool.addRoot(type(ast));
	}
	const AType* type(const AST* ast, const Subst& subst = Subst()) const {
		const AType*   ret = NULL;
		const ASymbol* sym = ast->to<const ASymbol*>();
		if (sym) {
			const AType** rec = tenv.ref(sym);
			if (rec)
				ret = *rec;
		}
		if (!ret)
			ret = tenv.vars[ast];
		if (ret)
			return tsubst.apply(subst.apply(ret))->to<const AType*>();
		return NULL;
	}
	void def(const ASymbol* sym, const AST* c, const AType* t, CVal v) {
		code.def(sym, c);
		tenv.def(sym, t);
		vals.def(sym, v);
	}
	const AST* resolve(const AST* ast) {
		const ASymbol* sym = ast->to<const ASymbol*>();
		const AST**          rec = code.ref(sym);
		return rec ? *rec : ast;
	}
	void setType(AST* ast, const AType* type) {
		const AType* tvar = tenv.var();
		tenv.vars.insert(make_pair(ast, tvar));
		tsubst.add(tvar, type);
	}
	void setTypeSameAs(AST* ast, AST* typedAst) {
		tenv.vars.insert(make_pair(ast, tenv.vars[typedAst]));
	}

	ostream& out;
	ostream& err;
	PEnv&    penv;
	TEnv&    tenv;
	Vals     vals;
	Subst    tsubst;

	Env<const ASymbol*, const AST*> code;

	typedef map<const ATuple*, CFunc> Impls;
	Impls impls;

	CFunc findImpl(const ATuple* fn, const AType* type) {
		Impls::const_iterator i = impls.find(fn);
		return (i != impls.end()) ? i->second : NULL;
	}

	void addImpl(const ATuple* fn, CFunc impl) {
		impls.insert(make_pair(fn, impl));
	}

	map<string,string> args;

	CFunc currentFn; ///< Currently compiling function

	struct FreeVars : public std::vector<ASymbol*> {
		FreeVars(ATuple* f, const std::string& n) : fn(f), implName(n) {}
		ATuple* const      fn;
		const std::string implName;
		int32_t index(ASymbol* sym) {
			const_iterator i = find(begin(), end(), sym);
			if (i != end()) {
				return i - begin() + 1;
			} else {
				push_back(sym);
				return size();
			}
		}
	};
	typedef std::stack<FreeVars> LiftStack;
	LiftStack liftStack;

	typedef map<const ATuple*, std::string> Names;
	Names names;

	const std::string name(const ATuple* fn) const {
		Names::const_iterator i = names.find(fn);
		return (i != names.end()) ? i->second : "";
	}

	void setName(const ATuple* fn, const std::string& name) {
		names.insert(make_pair(fn, name));
	}
private:
	Engine* _engine;
};


/***************************************************************************
 * EVAL/REPL/MAIN                                                          *
 ***************************************************************************/

void pprint(std::ostream& out, const AST* ast, CEnv* cenv, bool types);
void initLang(PEnv& penv, TEnv& tenv);
int  eval(CEnv& cenv, Cursor& cursor, istream& is, bool execute);
int  repl(CEnv& cenv);

void resp_constrain(TEnv& tenv, Constraints& c, const AST* ast) throw(Error);
AST* resp_lift(CEnv& cenv, Code& code, AST* ast) throw();
CVal resp_compile(CEnv& cenv, const AST* ast) throw();

bool is_form(const AST* ast, const std::string& form);
bool is_primitive(const PEnv& penv, const AST* ast);

#endif // RESP_HPP