
#include "ml.h"
#include "ml_io.h"
#include "ml_date.h"
#include "ml_string.h"
#include "ml_util.h"
#include "ml_profiler.h"
#include "ml_usql.h"

#include "clib/csvparser.h"
#include "clib/sslclient.h"
#include "clib/json11.h"
#include "clib/printf.h"

#include <cmath>
#include <map>
#include <string>
#include <vector>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <ctime>
#include <chrono>
#include <thread>
#include <mutex>


#define TOO_FEW_ARGS "too few arguments to function"
#define TOO_MANY_ARGS "too many arguments to function"
#define INVALID_ARGUMENT "invalid argument"
#define INVALID_ARGUMENT_NIL "invalid nil argument"
#define MISMATCHED_TYPES "mismatched types"
#define CALL_NON_FUNCTION "called non-function"
#define UNKNOWN_ERROR "unknown exception"
#define INVALID_LAMBDA "invalid lambda"
#define INVALID_BIN_OP "invalid binary operation"
#define INVALID_ORDER "cannot order expression"
#define BAD_CAST "cannot cast"
#define ATOM_NOT_DEFINED "atom not defined"
#define INTERNAL_ERROR "internal virtual machine error"
#define INDEX_OUT_OF_RANGE "index out of range"
#define MALFORMED_PROGRAM "malformed program"
#define NOT_IMPLEMENTED_YET_ERROR "not implemented yet"


#define STRING_TYPE "string"
#define INT_TYPE "int"
#define FLOAT_TYPE "float"
#define NIL_TYPE "nil"
#define FUNCTION_TYPE "function"
#define ATOM_TYPE "atom"
#define QUOTE_TYPE "quote"
#define LIST_TYPE "list"



// Convert an object to a string using a string stream conveniently
#if __linux
#define to_string(x) static_cast<std::ostringstream&>((std::ostringstream() << std::dec << x )).str()
#else
#define to_string(x) static_cast<std::ostringstream>((std::ostringstream() << std::dec << x )).str()
#endif

// Is this character a valid lisp symbol character
bool is_symbol(char ch) {
	return (isalpha(ch) || ispunct(ch)) && ch != '(' && ch != ')' && ch != '"' && ch != '\'';
}


MlValue::MlValue() : type(NIL) {}

MlValue::MlValue(long i) : type(INT) { stack_data.i = i; }

MlValue::MlValue(double f) : type(FLOAT) { stack_data.f = f; }

MlValue::MlValue(bool b) {
	if (b) {
		type = INT;
		stack_data.i = 1;
	}
	else { type = NIL; }
}

MlValue::MlValue(const std::vector<MlValue> &list) : type(LIST), list(list) {}

MlValue MlValue::quote(const MlValue &quoted) {
	MlValue result;
	result.type = QUOTE;

	// The first position in the list is
	// used to store the quoted expression.
	result.list.push_back(quoted);
	return result;
}

MlValue MlValue::atom(const std::string &s) {
	MlValue result;
	result.type = ATOM;

	// We use the `str` member to store the atom.
	result.str = s;
	return result;
}

MlValue MlValue::string(const std::string &s) {
	MlValue result;
	result.type = STRING;

	// We use the `str` member to store the string.
	result.str = s;
	return result;
}

MlValue MlValue::nil() {
	return MlValue();
}

// Construct a lambda function
MlValue::MlValue(const std::vector<MlValue> &params, MlValue ret, MlEnvironment const &env) : type(LAMBDA) {
	// We store the params and the result in the list member
	// instead of having dedicated members. This is to save memory.
	list.push_back(MlValue(params));
	list.push_back(ret);

//	// Lambdas capture only variables that they know they will use.
	std::vector<std::string> used_atoms = ret.get_used_atoms();
	for (size_t i = 0; i < used_atoms.size(); i++) {
		// If the environment has a symbol that this lambda uses, capture it.
		if (env.has(used_atoms[i]))
			lambda_scope.set(used_atoms[i], env.get(used_atoms[i]));
	}
}

// Construct a builtin function
MlValue::MlValue(const std::string &name, Builtin b) : type(BUILTIN) {
	// Store the name of the builtin function in the str member
	// to save memory, and use the builtin function slot in the union
	// to store the function pointer.
	str = name;
	stack_data.b = b;
}


// Get all of the atoms used in a given MlValue
std::vector<std::string> MlValue::get_used_atoms() {
	std::vector<std::string> result, tmp;
	switch (type) {
		case QUOTE:
			// The data for a quote is stored in the
			// first slot of the list member.
			return list[0].get_used_atoms();
		case ATOM:
			// If this is an atom, add it to the list
			// of used atoms in this expression.
			result.push_back(as_atom());
			return result;
		case LAMBDA:
			// If this is a lambda, get the list of used atoms in the body
			// of the expression.
			return list[1].get_used_atoms();
		case LIST:
			// If this is a list, add each of the atoms used in all
			// of the elements in the list.
			for (size_t i = 0; i < list.size(); i++) {
				// Get the atoms used in the element
				tmp = list[i].get_used_atoms();
				// Add the used atoms to the current list of used atoms
				result.insert(result.end(), tmp.begin(), tmp.end());
			}
			return result;
		default:
			return result;
	}
}

// Is this a builtin function?
bool MlValue::is_builtin() const {
	return type == BUILTIN;
}

bool MlValue::is_number() const {
	return type == INT || type == FLOAT;
}

bool MlValue::is_string() const {
	return type == STRING;
}

// Get the "truthy" boolean value of this value.
bool MlValue::as_bool() const {
	return type != NIL && *this != MlValue(0l);        // TODO remove 0 as false
}

// Get this item's integer value
long MlValue::as_int() const {
	return cast_to_int().stack_data.i;
}

// Get this item's floating point value
double MlValue::as_float() const {
	return cast_to_float().stack_data.f;
}

// Get this item's string value
std::string MlValue::as_string() const {
	// If this item is not a string, throw a cast error.
	if (type != STRING)
		throw MlError(*this, MlEnvironment(), BAD_CAST);
	return str;
}

// Get this item's atom value
std::string MlValue::as_atom() const {
	// If this item is not an atom, throw a cast error.
	if (type != ATOM)
		throw MlError(*this, MlEnvironment(), BAD_CAST);
	return str;
}

// Get this item's list value
std::vector<MlValue> MlValue::as_list() const {
	// If this item is not a list, throw a cast error.
	if (type != LIST)
		throw MlError(*this, MlEnvironment(), BAD_CAST);
	return list;
}

// Push an item to the end of this list
void MlValue::push(MlValue val) {
	// If this item is not a list, you cannot push to it.
	if (type != LIST)
		throw MlError(*this, MlEnvironment(), MISMATCHED_TYPES);

	list.push_back(val);
}

// Push an item from the end of this list
MlValue MlValue::pop() {
	// If this item is not a list, you cannot pop from it.
	if (type != LIST)
		throw MlError(*this, MlEnvironment(), MISMATCHED_TYPES);

	// Remember the last item in the list
	MlValue result = list[list.size() - 1];
	// Remove it from this instance
	list.pop_back();
	// Return the remembered value
	return result;
}


// Cast this to an integer value
MlValue MlValue::cast_to_int() const {
	switch (type) {
		case INT:
			return *this;
		case FLOAT:
			return MlValue(long(stack_data.f));
		case STRING:
			return MlValue(std::stol(str));
		default:
			throw MlError(*this, MlEnvironment(), BAD_CAST);
	}
}

// Cast this to a floating point value
MlValue MlValue::cast_to_float() const {
	switch (type) {
		case FLOAT:
			return *this;
		case INT:
			return MlValue(float(stack_data.i));
		case STRING:
			return MlValue(std::stod(str));
		default:
			throw MlError(*this, MlEnvironment(), BAD_CAST);
	}
}

// Cast this to a string
MlValue MlValue::cast_to_string() const {
	switch (type) {
		case INT:
			return MlValue::string(to_string(stack_data.i));
		case FLOAT:
			return MlValue::string(to_string(stack_data.f));
		case STRING:
			return *this;
		default:
			throw MlError(*this, MlEnvironment(), BAD_CAST);
	}
}


bool MlValue::operator==(MlValue other) const {
	// If either of these values are floats, promote the
	// other to a float, and then compare for equality.
	if (type == FLOAT && other.type == INT) return *this == other.cast_to_float();
	else if (type == INT && other.type == FLOAT) return this->cast_to_float() == other;
		// If the values types aren't equal, then they cannot be equal.
	else if (type != other.type) return false;

	switch (type) {
		case FLOAT:
			return stack_data.f == other.stack_data.f;
		case INT:
			return stack_data.i == other.stack_data.i;
		case BUILTIN:
			return stack_data.b == other.stack_data.b;
		case STRING:
		case ATOM:
			// Both atoms and strings store their
			// data in the str member.
			return str == other.str;
		case LAMBDA:
		case LIST:
			// Both lambdas and lists store their
			// data in the list member.
			return list == other.list;
		case QUOTE:
			// The values for quotes are stored in the
			// first slot of the list member.
			return list[0] == other.list[0];
		case NIL:
			return other.type == NIL;
		default:
			return true;
	}
}

bool MlValue::operator!=(const MlValue &other) const {
	return !(*this == other);
}


bool MlValue::operator>=(const MlValue &other) const {
	return !(*this < other);
}

bool MlValue::operator<=(const MlValue &other) const {
	return (*this == other) || (*this < other);
}

bool MlValue::operator>(const MlValue &other) const {
	return !(*this <= other);
}

bool MlValue::operator<(const MlValue &other) const {
	// Other type must be a float or an int
	if (other.type != FLOAT && other.type != INT)
		throw MlError(*this, MlEnvironment(), INVALID_BIN_OP);

	switch (type) {
		case FLOAT:
			// If this is a float, promote the other value to a float and compare.
			return stack_data.f < other.cast_to_float().stack_data.f;
		case INT:
			// If the other value is a float, promote this value to a float and compare.
			if (other.type == FLOAT)
				return cast_to_float().stack_data.f < other.stack_data.f;
				// Otherwise compare the integer values
			else return stack_data.i < other.stack_data.i;
		default:
			// Only allow comparisons between integers and floats
			throw MlError(*this, MlEnvironment(), INVALID_ORDER);
	}
}

MlValue MlValue::operator+(const MlValue &other) const {
	if (other.type == NIL)
		throw MlError(*this, MlEnvironment(), INVALID_ARGUMENT_NIL);

	// Other type must be a float or an int
	if ((is_number() || other.is_number()) &&
	    !(is_number() && other.is_number()))
		throw MlError(*this, MlEnvironment(), INVALID_BIN_OP);

	switch (type) {
		case FLOAT:
			// If one is a float, promote the other by default and do
			// float addition.
			return MlValue(stack_data.f + other.cast_to_float().stack_data.f);
		case INT:
			// If the other type is a float, go ahead and promote this expression
			// before continuing with the addition.
			if (other.type == FLOAT)
				return MlValue(cast_to_float() + other.stack_data.f);
				// Otherwise, do integer addition.
			else return MlValue(stack_data.i + other.stack_data.i);
		case STRING:
			// If the other value is also a string, do the concat
			if (other.type == STRING)
				return MlValue::string(str + other.str);
				// We throw an error if we try to concat anything of non-string type
			else throw MlError(*this, MlEnvironment(), INVALID_BIN_OP);
		case LIST:
			// If the other value is also a list, do the concat
			if (other.type == LIST) {
				// Maintain the value that will be returned
				MlValue result = *this;
				// Add each item in the other list to the end of this list
				for (size_t i = 0; i < other.list.size(); i++)
					result.push(other.list[i]);
				return result;

			} else throw MlError(*this, MlEnvironment(), INVALID_BIN_OP);
		default:
			throw MlError(*this, MlEnvironment(), INVALID_BIN_OP);
	}
}

MlValue MlValue::operator-(const MlValue &other) const {
	if (other.type == NIL)
		throw MlError(*this, MlEnvironment(), INVALID_ARGUMENT_NIL);

	// Other type must be a float or an int
	if (other.type != FLOAT && other.type != INT)
		throw MlError(*this, MlEnvironment(), INVALID_BIN_OP);

	switch (type) {
		case FLOAT:
			// If one is a float, promote the other by default and do
			// float subtraction.
			return MlValue(stack_data.f - other.cast_to_float().stack_data.f);
		case INT:
			// If the other type is a float, go ahead and promote this expression
			// before continuing with the subtraction
			if (other.type == FLOAT)
				return MlValue(cast_to_float().stack_data.f - other.stack_data.f);
				// Otherwise, do integer subtraction.
			else return MlValue(stack_data.i - other.stack_data.i);
		default:
			// This operation was done on an unsupported type
			throw MlError(*this, MlEnvironment(), INVALID_BIN_OP);
	}
}

MlValue MlValue::operator*(const MlValue &other) const {
	if (other.type == NIL)
		throw MlError(*this, MlEnvironment(), INVALID_ARGUMENT_NIL);

	// Other type must be a float or an int
	if (other.type != FLOAT && other.type != INT)
		throw MlError(*this, MlEnvironment(), INVALID_BIN_OP);

	switch (type) {
		case FLOAT:
			return MlValue(stack_data.f * other.cast_to_float().stack_data.f);
		case INT:
			// If the other type is a float, go ahead and promote this expression
			// before continuing with the product
			if (other.type == FLOAT)
				return MlValue(cast_to_float().stack_data.f * other.stack_data.f);
				// Otherwise, do integer multiplication.
			else return MlValue(stack_data.i * other.stack_data.i);
		default:
			// This operation was done on an unsupported type
			throw MlError(*this, MlEnvironment(), INVALID_BIN_OP);
	}
}

MlValue MlValue::operator/(const MlValue &other) const {
	if (other.type == NIL)
		throw MlError(*this, MlEnvironment(), INVALID_ARGUMENT_NIL);

	// Other type must be a float or an int
	if (other.type != FLOAT && other.type != INT)
		throw MlError(*this, MlEnvironment(), INVALID_BIN_OP);

	switch (type) {
		case FLOAT:
			return MlValue(stack_data.f / other.cast_to_float().stack_data.f);
		case INT:
			// If the other type is a float, go ahead and promote this expression
			// before continuing with the product
			if (other.type == FLOAT)
				return MlValue(cast_to_float().stack_data.f / other.stack_data.f);
				// Otherwise, do integer multiplication.
			else return MlValue(stack_data.i / other.stack_data.i);
		default:
			// This operation was done on an unsupported type
			throw MlError(*this, MlEnvironment(), INVALID_BIN_OP);
	}
}

MlValue MlValue::operator%(const MlValue &other) const {
	if (other.type == NIL)
		throw MlError(*this, MlEnvironment(), INVALID_ARGUMENT_NIL);

	// Other type must be a float or an int
	if (other.type != FLOAT && other.type != INT)
		throw MlError(*this, MlEnvironment(), INVALID_BIN_OP);

	switch (type) {
		// If we support libm, we can find the remainder of floating point values.
		case FLOAT:
			return MlValue(fmod(stack_data.f, other.cast_to_float().stack_data.f));
		case INT:
			if (other.type == FLOAT)
				return MlValue(fmod(cast_to_float().stack_data.f, other.stack_data.f));
			else return MlValue(stack_data.i % other.stack_data.i);
		default:
			// This operation was done on an unsupported type
			throw MlError(*this, MlEnvironment(), INVALID_BIN_OP);
	}
}

// Get the name of the type of this value
std::string MlValue::get_type_name() const {
	switch (type) {
		case QUOTE:
			return QUOTE_TYPE;
		case ATOM:
			return ATOM_TYPE;
		case INT:
			return INT_TYPE;
		case FLOAT:
			return FLOAT_TYPE;
		case LIST:
			return LIST_TYPE;
		case STRING:
			return STRING_TYPE;
		case BUILTIN:
		case LAMBDA:
			// Instead of differentiating between
			// lambda and builtin types, we group them together.
			// This is because they are both callable.
			return FUNCTION_TYPE;
		case NIL:
			return NIL_TYPE;
		default:
			// We don't know the name of this type.
			// This isn't the users fault, this is just unhandled.
			// This should never be reached.
			throw MlError(*this, MlEnvironment(), INTERNAL_ERROR);
	}
}

std::string MlValue::display() const {
	std::string result;
	switch (type) {
		case QUOTE:
			return "'" + list[0].debug();
		case ATOM:
			return str;
		case INT:
			return to_string(stack_data.i);
		case FLOAT:
			return to_string(stack_data.f);
		case STRING:
			return str;
		case LAMBDA:
			for (size_t i = 0; i < list.size(); i++) {
				result += list[i].debug();
				if (i < list.size() - 1) result += " ";
			}
			return "(lambda " + result + ")";
		case LIST:
			for (size_t i = 0; i < list.size(); i++) {
				result += list[i].debug();
				if (i < list.size() - 1) result += " ";
			}
			return "(" + result + ")";
		case BUILTIN:
			return "<" + str + " at " + to_string(long(stack_data.b)) + ">";
		case NIL:
			return "nil";
		default:
			// We don't know how to display whatever type this is.
			// This isn't the users fault, this is just unhandled.
			// This should never be reached.
			throw MlError(*this, MlEnvironment(), INTERNAL_ERROR);
	}
}

std::string MlValue::debug() const {
	std::string result;
	switch (type) {
		case QUOTE:
			return "'" + list[0].debug();
		case ATOM:
			return str;
		case INT:
			return to_string(stack_data.i);
		case FLOAT:
			return to_string(stack_data.f);
		case STRING:
			for (size_t i = 0; i < str.length(); i++) {
				if (str[i] == '"') result += "\\\"";
				else result.push_back(str[i]);
			}
			return "\"" + result + "\"";
		case LAMBDA:
			for (size_t i = 0; i < list.size(); i++) {
				result += list[i].debug();
				if (i < list.size() - 1) result += " ";
			}
			return "(lambda " + result + ")";
		case LIST:
			for (size_t i = 0; i < list.size(); i++) {
				result += list[i].debug();
				if (i < list.size() - 1) result += " ";
			}
			return "(" + result + ")";
		case BUILTIN:
			return "<" + str + " at " + to_string(long(stack_data.b)) + ">";
		case NIL:
			return "nil";
		default:
			// We don't know how to debug whatever type this is.
			// This isn't the users fault, this is just unhandled.
			// This should never be reached.
			throw MlError(*this, MlEnvironment(), INTERNAL_ERROR);
	}
}

std::ostream &operator<<(std::ostream &os, MlValue const &v) {
	return os << v.display();
}


MlError::MlError(const MlValue &v, MlEnvironment const &env, const char *msg) : env(env), msg(msg) {
	cause = new MlValue;
	*cause = v;
}

MlError::MlError(MlError const &other) : env(other.env), msg(other.msg) {
	cause = new MlValue(*other.cause);
}

MlError::~MlError() {
	delete cause;
}

std::string MlError::description() const {
	// return "error: the expression `" + cause->debug() + "` failed in scope " + to_string(env) + " with message \"" + msg + "\"";
	return MlPerfMon::instance().callstack() +
	       "error: the expression `" + cause->debug() + "` with message \"" + msg + "\"";
}

void MlEnvironment::combine(MlEnvironment const &other) {
	// Normally, I would use the `insert` method of the `map` class,
	// but it doesn't overwrite previously declared values for keys.
	std::map<std::string, MlValue>::const_iterator itr = other.defs.begin();
	for (; itr != other.defs.end(); itr++) {
		// Iterate through the keys and assign each value.
		defs[itr->first] = itr->second;
	}
}

std::ostream &operator<<(std::ostream &os, MlEnvironment const &e) {
	std::map<std::string, MlValue>::const_iterator itr = e.defs.begin();
	os << "{ ";
	for (; itr != e.defs.end(); itr++) {
		os << '\'' << itr->first << "' : " << itr->second.debug() << ", ";
	}
	return os << "}";
}

void MlEnvironment::set(const std::string &name, MlValue value) {
	defs[name] = value;
}


void MlEnvironment::setX(const std::string &name, MlValue value) {
	MlEnvironment *e = this;
	while (e != nullptr) {
		std::map<std::string, MlValue>::const_iterator itr = e->defs.find(name);
		if (itr != e->defs.end()) {
			e->set(name, value);
			return;
		}
		e = e->parent_scope;
	}
	// not found so define
	this->set(name, value);
}


MlValue MlValue::apply(std::vector<MlValue> args, MlEnvironment &env) {
	MlEnvironment e;
	std::vector<MlValue> params;
	switch (type) {
		case LAMBDA:
			// Get the list of parameter atoms
			params = list[0].list;
			if (params.size() != args.size())
				throw MlError(MlValue(args), env,
					      args.size() > params.size() ? TOO_MANY_ARGS : TOO_FEW_ARGS);

			// Get the captured scope from the lambda
			e = lambda_scope;
			// And make this scope the parent scope
			e.set_parent_scope(&env);

			// Iterate through the list of parameters and
			// insert the arguments into the scope.
			for (size_t i = 0; i < params.size(); i++) {
				if (params[i].type != ATOM)
					throw MlError(*this, env, INVALID_LAMBDA);
				// Set the parameter name into the scope.
				e.set(params[i].str, args[i]);
			}

			// Evaluate the function body with the function scope
			return list[1].eval(e);
		case BUILTIN:
			// Here, we call the builtin function with the current scope.
			// This allows us to write special forms without syntactic sugar.
			// For functions that are not special forms, we just evaluate
			// the arguments before we run the function.
			return (stack_data.b)(args, env);
		default:
			// We can only call lambdas and builtins
			throw MlError(*this, env, CALL_NON_FUNCTION);
	}
}


MlValue MlValue::eval(MlEnvironment &env) {
	std::vector<MlValue> args;
	MlValue function;
	MlValue res;
	MlEnvironment e;
	switch (type) {
		case QUOTE:
			return list[0];
		case ATOM:
			return env.get(str);
		case LIST:
			if (list.size() < 1)
				return MlValue::nil();

			args = std::vector<MlValue>(list.begin() + 1, list.end());

			function = list[0].eval(env);
			if (function.type == BUILTIN || function.type == LAMBDA) {
				// Only evaluate our arguments if it's not builtin!
				// Builtin functions can be special forms, so we
				// leave them to evaluate their arguments.
				if (!function.is_builtin())
					for (size_t i = 0; i < args.size(); i++)
						args[i] = args[i].eval(env);

				MlPerfMon::instance().add_method_call(
					    function.type == LAMBDA ? "lambda" : function.str);
				res = function.apply(args, env);
				MlPerfMon::instance().end_method_call();
				return res;
			}

		default:
			return *this;
	}
}

void skip_whitespace(std::string &s, int &ptr);

void erase_comments(std::string &s, int &ptr) {
	while (s[ptr] == ';') {
		int save_ptr = ptr;
		while (s[save_ptr] != '\n' && save_ptr < int(s.length())) { save_ptr++; }
		s.erase(ptr, save_ptr - ptr);

		skip_whitespace(s, ptr);
	}
}

void skip_whitespace(std::string &s, int &ptr) {
	while (isspace(s[ptr])) { ptr++; }

	erase_comments(s, ptr);
}

// Parse a single value and increment the pointer
// to the beginning of the next value to parse.
MlValue parse(std::string &s, int &ptr) {
	skip_whitespace(s, ptr);

	if (s[ptr] == ';')
		throw std::runtime_error(INTERNAL_ERROR);


	if (s == "") {
		return MlValue();

	} else if (s[ptr] == '\'') {
		// If this is a quote
		ptr++;
		return MlValue::quote(parse(s, ptr));

	} else if (s[ptr] == '(') {
		// If this is a list
		skip_whitespace(s, ++ptr);

		MlValue result = MlValue(std::vector<MlValue>());

		while (s[ptr] != ')')
			result.push(parse(s, ptr));

		skip_whitespace(s, ++ptr);
		return result;

	} else if (isdigit(s[ptr]) || (s[ptr] == '-' && isdigit(s[ptr + 1]))) {
		// If this is a number
		bool negate = s[ptr] == '-';
		if (negate) ptr++;

		int save_ptr = ptr;
		while (isdigit(s[ptr]) || s[ptr] == '.') ptr++;
		std::string n = s.substr(save_ptr, ptr - save_ptr);
		skip_whitespace(s, ptr);

		if (n.find('.') != std::string::npos)
			return MlValue((negate ? -1l : 1l) * atof(n.c_str()));
		else return MlValue((negate ? -1l : 1l) * atol(n.c_str()));

	} else if (s[ptr] == '\"') {
		// If this is a string
		int n = 1;
		while (s[ptr + n] != '\"') {
			if (ptr + n >= int(s.length()))
				throw std::runtime_error(MALFORMED_PROGRAM);

			if (s[ptr + n] == '\\') n++;
			n++;
		}

		std::string x = s.substr(ptr + 1, n - 1);
		ptr += n + 1;
		skip_whitespace(s, ptr);

		// Iterate over the characters in the string, and
		// replace escaped characters with their intended values.
		for (size_t i = 0; i < x.size(); i++) {
			if (x[i] == '\\' && x[i + 1] == '\\')
				x.replace(i, 2, "\\");
			else if (x[i] == '\\' && x[i + 1] == '"')
				x.replace(i, 2, "\"");
			else if (x[i] == '\\' && x[i + 1] == 'n')
				x.replace(i, 2, "\n");
			else if (x[i] == '\\' && x[i + 1] == 't')
				x.replace(i, 2, "\t");
		}

		return MlValue::string(x);

	} else if (s[ptr] == '@') {
		ptr++;
		skip_whitespace(s, ptr);
		return MlValue();

	} else if (is_symbol(s[ptr])) {
		// If this is a string
		int n = 0;
		while (is_symbol(s[ptr + n])) {
			n++;
		}

		std::string x = s.substr(ptr, n);
		ptr += n;
		skip_whitespace(s, ptr);
		if (x == "nil")
			return MlValue::nil();
		else
			return MlValue::atom(x);

	} else {
		throw std::runtime_error(MALFORMED_PROGRAM);
	}
}

// Parse an entire program and get its list of expressions.
std::vector<MlValue> parse(std::string s) {
	int i = 0, last_i = -1;
	std::vector<MlValue> result;
	// While the parser is making progress (while the pointer is moving right)
	// and the pointer hasn't reached the end of the string,
	while (last_i != i && i <= int(s.length() - 1)) {
		// Parse another expression and add it to the list.
		last_i = i;
		result.push_back(parse(s, i));
	}

	// If the whole string wasn't parsed, the program must be bad.
	if (i < int(s.length()))
		throw std::runtime_error(MALFORMED_PROGRAM);

	// Return the list of values parsed.
	return result;
}

// Execute code in an environment
MlValue run(const std::string &code, MlEnvironment &env) {
	// Parse the code
	std::vector<MlValue> parsed = parse(code);
	// Iterate over the expressions and evaluate them
	// in this environment.
	for (size_t i = 0; i < parsed.size() - 1; i++)
		parsed[i].eval(env);

	// Return the result of the last expression.
	return parsed[parsed.size() - 1].eval(env);
}

// This namespace contains all the definitions of builtin functions
namespace builtin {
// This function is NOT a builtin function, but it is used
// by almost all of them.
//
// Special forms are just builtin functions that don't evaluate
// their arguments. To make a regular builtin that evaluates its
// arguments, we just call this function in our builtin definition.
void eval_args(std::vector<MlValue> &args, MlEnvironment &env) {
	for (size_t i = 0; i < args.size(); i++)
		args[i] = args[i].eval(env);
}

// Create a lambda function (SPECIAL FORM)
MlValue lambda(std::vector<MlValue> args, MlEnvironment &env) {
	if (args.size() < 2)
		throw MlError(MlValue("lambda", lambda), env, TOO_FEW_ARGS);

	if (args[0].get_type_name() != LIST_TYPE)
		throw MlError(MlValue("lambda", lambda), env, INVALID_LAMBDA);

	return MlValue(args[0].as_list(), args[1], env);
}

// if-else (SPECIAL FORM)
MlValue if_then_else(std::vector<MlValue> args, MlEnvironment &env) {
	if (args.size() != 2 && args.size() != 3)
		throw MlError(MlValue("if", if_then_else), env, args.size() > 3 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	if (args[0].eval(env).as_bool())
		return args[1].eval(env);
	else if (args.size() == 3)
		return args[2].eval(env);

	return MlValue(0l);
}

// Define a variable with a value (SPECIAL FORM)
MlValue define(std::vector<MlValue> args, MlEnvironment &env) {
	if (args.size() != 2)
		throw MlError(MlValue("define", define), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	MlValue result = args[1].eval(env);
	env.set(args[0].display(), result);
	return result;
}

// Define a function with parameters and a result expression (SPECIAL FORM)
MlValue defun(std::vector<MlValue> args, MlEnvironment &env) {
	if (args.size() != 3)
		throw MlError(MlValue("defun", defun), env, args.size() > 3 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	if (args[1].get_type_name() != LIST_TYPE)
		throw MlError(MlValue("defun", defun), env, INVALID_LAMBDA);

	MlValue f = MlValue(args[1].as_list(), args[2], env);
	env.set(args[0].display(), f);
	return f;
}

// Sets (if exists) or define a variable with a value (SPECIAL FORM)
MlValue setx(std::vector<MlValue> args, MlEnvironment &env) {
	if (args.size() != 2)
		throw MlError(MlValue("set!", define), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	MlValue result = args[1].eval(env);
	env.setX(args[0].display(), result);
	return result;
}

// Loop over a list of expressions with a condition (SPECIAL FORM)
MlValue while_loop(std::vector<MlValue> args, MlEnvironment &env) {
	MlValue acc;
	while (args[0].eval(env).as_bool()) {
		for (size_t i = 1; i < args.size() - 1; i++)
			args[i].eval(env);
		acc = args[args.size() - 1].eval(env);
	}
	return acc;
}

// Iterate through a list of values in a list (SPECIAL FORM)
MlValue for_loop(std::vector<MlValue> args, MlEnvironment &env) {
	MlValue acc;
	std::vector<MlValue> list = args[1].eval(env).as_list();

	for (size_t i = 0; i < list.size(); i++) {
		env.set(args[0].as_atom(), list[i]);

		for (size_t j = 2; j < args.size() - 1; j++)
			args[j].eval(env);

		acc = args[args.size() - 1].eval(env);
	}
	return acc;
}

// Evaluate a block of expressions in the current environment (SPECIAL FORM)
MlValue do_block(std::vector<MlValue> args, MlEnvironment &env) {
	MlValue acc;
	for (auto &arg : args)
		acc = arg.eval(env);
	return acc;
}

// Evaluate a block of expressions in a new environment (SPECIAL FORM)
MlValue scope(std::vector<MlValue> args, MlEnvironment &env) {
	MlEnvironment e = env;
	MlValue acc;
	for (auto &arg : args)
		acc = arg.eval(e);
	return acc;
}

// Quote an expression (SPECIAL FORM)
MlValue quote(std::vector<MlValue> args, MlEnvironment &env) {
	std::vector<MlValue> v;
	for (const auto &arg : args)
		v.push_back(arg);
	return MlValue(v);
}

// Evaluate logical and on a list of expressions (SPECIAL FORM)
MlValue do_and(std::vector<MlValue> args, MlEnvironment &env) {
	if (args.size() < 2)
		throw MlError(MlValue("and", do_and), env, TOO_FEW_ARGS);

	for (size_t i = 0; i < args.size(); i++)
		if (!args[i].eval(env).as_bool()) return MlValue::nil();

	return MlValue{1l};
}

// Evaluate logical or on a list of expressions (SPECIAL FORM)
MlValue do_or(std::vector<MlValue> args, MlEnvironment &env) {
	if (args.size() < 2)
		throw MlError(MlValue("or", do_or), env, TOO_FEW_ARGS);

	for (size_t i = 0; i < args.size(); i++)
		if (args[i].eval(env).as_bool()) return MlValue{1l};

	return MlValue::nil();
}

// Exit the program with an integer code
MlValue exit(std::vector<MlValue> args, MlEnvironment &env) {
	// Is not a special form, so we can evaluate our args.
	eval_args(args, env);

	std::exit(args.size() < 1 ? 0 : args[0].cast_to_int().as_int());
	return MlValue();        // will not be called :-)
}

// Print several values and return the last one
MlValue print(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() < 1)
		throw MlError(MlValue("print", print), env, TOO_FEW_ARGS);

	MlValue acc;
	for (size_t i = 0; i < args.size(); i++) {
		acc = args[i];
		std::cout << acc.display();
		if (i < args.size() - 1)
			std::cout << " ";
	}
	std::cout << std::endl;
	return acc;
}

// Get user input with an optional prompt
MlValue input(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() > 1)
		throw MlError(MlValue("input", input), env, TOO_MANY_ARGS);

	if (!args.empty())
		std::cout << args[0];

	std::string s;
	std::getline(std::cin, s);
	return MlValue::string(s);
}

// Get a random number between two numbers inclusively
MlValue random(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 2)
		throw MlError(MlValue("random", random), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	long low = args[0].as_int(), high = args[1].as_int();
	return MlValue(rand() % (high - low + 1) + low);
}

// Parse CSV string
MlValue parse_csv(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	// TODO add support for more params specifying options
	if (args.size() != 1)
		throw MlError(MlValue("parse-csv", parse_csv), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	CsvParser csv(true);
	return csv.parseCSV(args[0].as_string());
}

// Get the contents of a file
MlValue read_file(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 1)
		throw MlError(MlValue("read-file", read_file), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	return MlValue::string(read_file_contents(args[0].as_string()));
}

// Reads file line by line and call passed lambda
MlValue read_file_lines(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 2)
		throw MlError(MlValue("read-file-lines", read_file_lines), env,
			      args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
	// TODO check args[1].is_lambda

	long lines_nr = 0;
	std::vector<MlValue> lambda_par{MlValue::nil()};
	std::ifstream file(args[0].as_string());
	if (file.is_open()) {
		std::string line;
		while (std::getline(file, line)) {
			lines_nr++;
			lambda_par[0] = MlValue::string(line);
			args[1].apply(lambda_par, env);
		}
		file.close();
	}

	return MlValue{lines_nr};
}

// Write a string to a file
MlValue write_file(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 2)
		throw MlError(MlValue("write-file", write_file), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	return MlValue((long) write_file_contents(args[0].as_string(), args[1].as_string()));
}

// Read URL to (code content)
MlValue read_url(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	// PERF optimize it for memory usage and performance
	if (args.size() < 1 || args.size() > 2)
		throw MlError(MlValue("read_url", read_url), env, args.size() < 1 ? TOO_FEW_ARGS : TOO_MANY_ARGS);

	std::unordered_map<std::string, std::string> headers = {};
	HttpClient client;

	if (args.size() == 2) {
		for (const auto &hdr_val_pair: args[1].as_list()) {
			// TODO check its 2 string elements list
			const auto &pair = hdr_val_pair.as_list();
			headers[pair[0].as_string()] = pair[1].as_string();
		}
	}

	std::pair<long, std::string> result = client.doGetRequest(args[0].as_string(), headers);

	return std::vector<MlValue>{MlValue(result.first), MlValue::string(result.second)};
}

// Parse JSON string
MlValue parse_json(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	// TODO add support for more params specifying options
	if (args.size() != 1)
		throw MlError(MlValue("parse-json", parse_json), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	std::string str = args[0].as_string();
	std::string err;
	auto json = json11::Json::parse(str, err);

	if (!err.empty()) {
		// TODO handle error
		return MlValue::string("ERROR json parsing: " + err);
	}

	return json.ivalualize();
}

// Get current time as secs from epoch
MlValue get_universal_time(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 0)
		throw MlError(MlValue("get-universal-time", get_universal_time), env, TOO_MANY_ARGS);

	return MlValue(now());
}

// Converts date to formated string.
MlValue date_to_str(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 2)
		throw MlError(MlValue("date_to_str", date_to_str), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	return MlValue::string(date_to_string(args[0].as_int(), args[1].as_string()));
}

// Converst string to time of secs since epoch
MlValue str_to_date(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 2)
		throw MlError(MlValue("str-to-date", str_to_date), env,
			      args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	return MlValue(string_to_date(args[0].as_string(), args[1].as_string()));
}

// Add number of units to date. A unit is one of 'year', 'month', 'day', 'hour', 'minute' or 'second'
MlValue date_add(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 3)
		throw MlError(MlValue("date-add", date_add), env, args.size() > 3 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	return MlValue(add_to_date(args[0].as_int(), args[1].as_int(), args[2].as_string()));
}


// Execute system command
MlValue system_cmd(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	// TODO add support for more params constructing options as one string
	if (args.size() != 1)
		throw MlError(MlValue("system-cmd", system_cmd), env,
			      args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	return exec_system_cmd(args[0].as_string());
}


// list directory
MlValue ls_dir(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 1)
		throw MlError(MlValue("ls-dir", ls_dir), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	return list_dir(args[0].as_string());
}

// is_path file
MlValue is_file(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 1)
		throw MlError(MlValue("is-file?", is_file), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	return MlValue(is_path_file(args[0].as_string()));
}

// is_path directory
MlValue is_dir(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 1)
		throw MlError(MlValue("is-dir?", is_dir), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	return MlValue(is_path_dir(args[0].as_string()));
}

// Read a file and execute its code
MlValue include(std::vector<MlValue> args, MlEnvironment &env) {
	// Import is technically not a special form, it's more of a macro.
	// We can evaluate our arguments.
	eval_args(args, env);

	if (args.size() != 1)
		throw MlError(MlValue("include", include), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	MlEnvironment e;
	MlValue result = run(read_file_contents(args[0].as_string()), e);
	env.combine(e);
	return result;
}


// Evaluate a value as code
MlValue eval(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 1)
		throw MlError(MlValue("eval", eval), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
	else return args[0].eval(env);
}

// Create a list of values
MlValue list(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	return MlValue(args);
}

// Sum multiple values
MlValue sum(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() < 2)
		throw MlError(MlValue("+", sum), env, TOO_FEW_ARGS);

	MlValue acc = args[0];
	for (size_t i = 1; i < args.size(); i++)
		acc = acc + args[i];
	return acc;
}

// Subtract two values
MlValue subtract(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 2)
		throw MlError(MlValue("-", subtract), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
	return args[0] - args[1];
}

// Multiply several values
MlValue product(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() < 2)
		throw MlError(MlValue("*", product), env, TOO_FEW_ARGS);

	MlValue acc = args[0];
	for (size_t i = 1; i < args.size(); i++)
		acc = acc * args[i];
	return acc;
}

// Divide two values
MlValue divide(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 2)
		throw MlError(MlValue("/", divide), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	if ((args[1].get_type_name() == "int" && args[1] == 0l) ||
	    (args[1].get_type_name() == "float" && args[1] == 0.0))
		throw std::invalid_argument("divide by zero.");

	return args[0] / args[1];
}

// Get the remainder of values
MlValue remainder(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 2)
		throw MlError(MlValue("%", remainder), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
	return args[0] % args[1];
}

// Are two values equal?
MlValue eq(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 2)
		throw MlError(MlValue("=", eq), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
	return MlValue(args[0] == args[1]);
}

// Are two values not equal?
MlValue neq(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 2)
		throw MlError(MlValue("!=", neq), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
	return MlValue(args[0] != args[1]);
}

// Is one number greater than another?
MlValue greater(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 2)
		throw MlError(MlValue(">", greater), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
	return MlValue(args[0] > args[1]);
}

// Is one number less than another?
MlValue less(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 2)
		throw MlError(MlValue("<", less), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
	return MlValue(args[0] < args[1]);
}

// Is one number greater than or equal to another?
MlValue greater_eq(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 2)
		throw MlError(MlValue(">=", greater_eq), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
	return MlValue(args[0] >= args[1]);
}

// Is one number less than or equal to another?
MlValue less_eq(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 2)
		throw MlError(MlValue("<=", less_eq), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
	return MlValue(args[0] <= args[1]);
}

// Get the type name of a value
MlValue get_type_name(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 1)
		throw MlError(MlValue("type", get_type_name), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	return MlValue::string(args[0].get_type_name());
}

// Cast an item to a float
MlValue cast_to_float(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 1)
		throw MlError(MlValue(FLOAT_TYPE, cast_to_float), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	return args[0].cast_to_float();
}

// Cast an item to an int
MlValue cast_to_int(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 1)
		throw MlError(MlValue(INT_TYPE, cast_to_int), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	return args[0].cast_to_int();
}

// Cast an item to a string
MlValue cast_to_string(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 1)
		throw MlError(MlValue(STRING_TYPE, cast_to_string), env,
			      args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	return args[0].cast_to_string();
}

// Index a list
MlValue index(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 2)
		throw MlError(MlValue("index", index), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	std::vector<MlValue> list = args[0].as_list();
	long i = args[1].as_int();
	if (list.empty() || i >= list.size())
		throw MlError(list, env, INDEX_OUT_OF_RANGE);

	return list[i];
}

// Insert a value into a list
MlValue insert(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 3)
		throw MlError(MlValue("insert", insert), env, args.size() > 3 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	std::vector<MlValue> list = args[0].as_list();
	long i = args[1].as_int();
	if (i > list.size())
		throw MlError(list, env, INDEX_OUT_OF_RANGE);

	list.insert(list.begin() + args[1].as_int(), args[2]);
	return MlValue(list);
}

// Remove a value at an index from a list
MlValue remove(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 2)
		throw MlError(MlValue("remove", remove), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	std::vector<MlValue> list = args[0].as_list();
	long i = args[1].as_int();
	if (list.empty() || i >= list.size())
		throw MlError(list, env, INDEX_OUT_OF_RANGE);

	list.erase(list.begin() + i);
	return MlValue(list);
}

// Get the length of a list
MlValue len(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 1)
		throw MlError(MlValue("len", len), env, args.size() > 1 ?
							TOO_MANY_ARGS : TOO_FEW_ARGS
		);

	return MlValue(long(args[0].as_list().size()));
}

// Add an item to the end of a list
MlValue push(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() == 0)
		throw MlError(MlValue("push", push), env, TOO_FEW_ARGS);

	for (size_t i = 1; i < args.size(); i++)
		args[0].push(args[i]);
	return args[0];
}

MlValue pop(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 1)
		throw MlError(MlValue("pop", pop), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
	return args[0].pop();
}

MlValue head(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 1)
		throw MlError(MlValue("head", head), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	std::vector<MlValue> list = args[0].as_list();
	if (list.empty())
		throw MlError(MlValue("head", head), env, INDEX_OUT_OF_RANGE);

	return list[0];
}

MlValue tail(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 1)
		throw MlError(MlValue("tail", tail), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	std::vector<MlValue> result, list = args[0].as_list();

	for (size_t i = 1; i < list.size(); i++)
		result.push_back(list[i]);

	return MlValue(result);
}

MlValue parse(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 1)
		throw MlError(MlValue("parse", parse), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
	if (args[0].get_type_name() != STRING_TYPE)
		throw MlError(args[0], env, INVALID_ARGUMENT);
	std::vector<MlValue> parsed = ::parse(args[0].as_string());

	return MlValue(parsed);
}

// Replace a substring with a replacement string in a source string
MlValue string_replace(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 3)
		throw MlError(MlValue("string-replace", string_replace), env,
			      args.size() > 3 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	std::string src = args[0].as_string();
	replace_substring(src, args[1].as_string(), args[2].as_string());
	return MlValue::string(src);
}

// Returns true if where contains regex
MlValue string_regex(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 2)                // if (args.size() < 2 || args.size() > 3)
		throw MlError(MlValue("string-regex?", string_regex), env,
			      args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	return MlValue((long) regexp_search(args[0].as_string(), args[1].as_string()));
}

// Splits string by regexp and returns list containing splited parts
MlValue string_split(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 2)
		throw MlError(MlValue("string-split", string_split), env,
			      args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	// TODO do it more efficient
	std::vector<std::string> elements = regexp_strsplit(args[0].as_string(), args[1].as_string());
	std::vector<MlValue> result{};

	for (size_t i = 0; i < elements.size(); i++)
		result.push_back(MlValue::string(elements[i]));

	return MlValue(result);
}

// converts string to upper or lower case
MlValue string_case(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 2)
		throw MlError(MlValue("string-case", string_case), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	return MlValue::string(string_lucase(args[0].as_string(), args[1].as_string()));
}

MlValue string_len(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 1)
		throw MlError(MlValue("string-len", string_len), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	return MlValue{(long) args[0].as_string().size()};
}

MlValue string_substr(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() < 1 || args.size() > 3)
		throw MlError(MlValue("string-substr", string_substr), env,
			      args.size() > 3 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	const std::string &str = args[0].as_string();
	long pos = args.size() > 1 ? args[1].as_int() : 0;
	long count = args.size() > 2 ? args[2].as_int() : str.size();

	return MlValue::string(string_substring(str, pos, count));
}

MlValue string_find(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() < 2 || args.size() > 3)
		throw MlError(MlValue("string-find", string_find), env, args.size() > 3 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	size_t start_pos = args.size() > 2 ? args[2].as_int() : 0;
	size_t pos = string_find_substr(args[0].as_string(), args[1].as_string(), start_pos);

	return pos == -1 ? MlValue::nil() : MlValue((long) pos);
}


// trims characters " \n\r\t" from left or right or both ends of a string
MlValue string_rltrim(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 3)
		throw MlError(MlValue("string_rltrim", string_rltrim), env,
			      args.size() > 3 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	return MlValue::string(string_trim(args[0].as_string(), args[1].as_string(), args[2].as_string()));
}

MlValue string_pad(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 4)
		throw MlError(MlValue("string_pad", string_pad), env, args.size() > 4 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	// TODO validate len > 0 etc
	return MlValue::string(string_padd(args[0].as_string(), args[1].as_int(), args[2].as_string()[0],
					   (args[3].as_string() == "rpad")));
}

MlValue display(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 1)
		throw MlError(MlValue("display", display), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	return MlValue::string(args[0].display());
}

MlValue debug(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 1)
		throw MlError(MlValue("debug", debug), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	return MlValue::string(args[0].debug());
}

MlValue sprintf(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() < 1 || args.size() > 2)
		throw MlError(MlValue("sprintf", sprintf), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	return MlValue::string(
		    mini_sprintf(args[0].as_string(), args.size() == 2 ? args[1].as_list() : std::vector<MlValue>{}));
}

// >>> (map (lambda (x) (+ x 10)) '(1 2 3 4 5 6))
//  => (11 12 13 14 15 16)
MlValue map_list(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 2)
		throw MlError(MlValue("map_list", map_list), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	std::vector<MlValue> result, l = args[1].as_list(), tmp;
	for (size_t i = 0; i < l.size(); i++) {
		tmp.push_back(l[i]);
		result.push_back(args[0].apply(tmp, env));
		tmp.clear();
	}
	return MlValue(result);
}

//>>> (filter (lambda (x) (> x 2)) '(1 2 3 4 5))
// => (3 4 5)
MlValue filter_list(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 2)
		throw MlError(MlValue("filter_list", filter_list), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	std::vector<MlValue> result, l = args[1].as_list(), tmp;
	for (size_t i = 0; i < l.size(); i++) {
		tmp.push_back(l[i]);
		if (args[0].apply(tmp, env).as_bool())
			result.push_back(l[i]);
		tmp.clear();
	}
	return MlValue(result);
}

// >>> (reduce (lambda (x y) (+ (* x 10) y)) 0 '(1 2 3 4))
//  => 1234
MlValue reduce_list(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 3)
		throw MlError(MlValue("reduce_list", reduce_list), env, args.size() > 3 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	std::vector<MlValue> l = args[2].as_list(), tmp;
	MlValue acc = args[1];
	for (size_t i = 0; i < l.size(); i++) {
		tmp.push_back(acc);
		tmp.push_back(l[i]);
		acc = args[0].apply(tmp, env);
		tmp.clear();
	}
	return acc;
}

// >>> (range 1 5)
//  => (1 2 3 4)
MlValue range(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 2)
		throw MlError(MlValue("range", range), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	std::vector<MlValue> result;
	MlValue low = args[0], high = args[1];
	if (low.get_type_name() != INT_TYPE && low.get_type_name() != FLOAT_TYPE)
		throw MlError(low, env, MISMATCHED_TYPES);
	if (high.get_type_name() != INT_TYPE && high.get_type_name() != FLOAT_TYPE)
		throw MlError(high, env, MISMATCHED_TYPES);

	if (low >= high) return MlValue(result);

	while (low < high) {
		result.push_back(low);
		low = low + MlValue(1l);
	}
	return MlValue(result);
}

// Benchmarks a block of expressions in the current environment (SPECIAL FORM)
MlValue benchmark(std::vector<MlValue> args, MlEnvironment &env) {
	// TODO add some memory stats
	using namespace std::chrono;
	high_resolution_clock::time_point t1 = high_resolution_clock::now();

	MlValue acc;
	for (size_t i = 1; i < args.size(); i++)
		acc = args[i].eval(env);

	high_resolution_clock::time_point t2 = high_resolution_clock::now();
	duration<double, std::milli> time_span = t2 - t1;

	std::cerr << args[0].as_string() << " " << time_span.count() << " ms" << std::endl;

	return acc;
}


std::mutex register_mutex;
std::vector<std::thread> threads_register;
std::vector<std::mutex> mutexes_register;
std::mutex interpreter_mutex;

MlValue thread_create(std::vector<MlValue> args, MlEnvironment &env) {
	auto functor = [](std::vector<MlValue> args, MlEnvironment &env) -> void {
	    try {
		    for (size_t i = 0; i < args.size(); i++)
			    MlValue acc = args[i].eval(env);
	    } catch (std::exception &e) {
		    std::cerr << "thread exception: " << e.what() << std::endl;
	    }
	};

	std::thread th(functor, args, std::ref(env));
	auto th_id = th.get_id();

	std::lock_guard<std::mutex> lockGuard(register_mutex);
	threads_register.push_back(std::move(th));

	static_assert(sizeof(std::thread::id) == sizeof(uint64_t),
		      "size of thead::id is not equal to the size of uint_64");
	uint64_t *ptr = (uint64_t *) &th_id;
	long tid = (*ptr);

	return MlValue(tid);
}

MlValue thread_under_lock(std::vector<MlValue> args, MlEnvironment &env) {
	if (args.size() != 2)
		throw MlError(MlValue("thread_under_lock", thread_under_lock), env,
			      args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	if (args[0].as_string() != "ilock")
		throw MlError(MlValue("thread_under_lock", thread_under_lock), env, UNKNOWN_ERROR);

	std::lock_guard<std::mutex> lockGuard(interpreter_mutex);
	MlValue acc;
	for (size_t i = 0; i < args.size(); i++)
		acc = args[i].eval(env);

	return acc;
}

MlValue thread_sleep(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 1)
		throw MlError(MlValue("thread-sleep", thread_sleep), env,
			      args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	std::this_thread::sleep_for(std::chrono::milliseconds(args[0].as_int()));
	return args[0];
}

MlValue threads_join(std::vector<MlValue> args, MlEnvironment &env) {
	if (args.size() != 0)
		throw MlError(MlValue("threads-join", threads_join), env, TOO_MANY_ARGS);

	std::lock_guard<std::mutex> lockGuard(register_mutex);
	for (auto &th : threads_register)
		if (th.joinable()) th.join();

	return MlValue::nil();
}


MlValue try_block(std::vector<MlValue> args, MlEnvironment &env) {
	if (args.size() < 2 || args.size() > 3)
		throw MlError(MlValue("try", try_block), env, args.size() > 3 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	MlValue value;
	size_t cs_posisition;        // unroll stack position to state when try begun to prevent "forgotten" entries in case of exception

	// try block
	try {
		cs_posisition = MlPerfMon::instance().get_callstack_position();
		value = args[0].eval(env);
		// catch block
	} catch (std::exception &e) {
		MlPerfMon::instance().restore_callstack_position(cs_posisition);
		env.set("ml-exception", MlValue::string(e.what()));
		value = args[1].eval(env);
	}
	// finally block
	if (args.size() == 3) {
		args[2].eval(env);
	}

	return value;
}

MlValue throw_exception(std::vector<MlValue> args, MlEnvironment &env) {
	if (args.size() != 1)
		throw MlError(MlValue("throw", throw_exception), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	throw std::runtime_error(args[0].as_string());
}

MlValue usql(std::vector<MlValue> args, MlEnvironment &env) {
	eval_args(args, env);

	if (args.size() != 1)
		throw MlError(MlValue("usql", throw_exception), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);

	return uSQL::instance().execute(args[0].as_string());
}

}	// namespace builtin

void load_std_lib(MlEnvironment &env) {
	run(STDLIB_LOADER, env);
}

// Does this environment, or its parent environment, have a variable?
bool MlEnvironment::has(const std::string &name) const {
	// Find the value in the map
	std::map<std::string, MlValue>::const_iterator itr = defs.find(name);
	if (itr != defs.end())
		// If it was found
		return true;
	else if (parent_scope != nullptr)
		// If it was not found in the current environment,
		// try to find it in the parent environment
		return parent_scope->has(name);
	else return false;
}

// Get the value associated with this name in this scope
MlValue MlEnvironment::get(const std::string &name) const {
	// Special forms
	if (name == "if") return MlValue("if", builtin::if_then_else);
	if (name == "define") return MlValue("define", builtin::define);
	if (name == "do") return MlValue("do", builtin::do_block);
	if (name == "for") return MlValue("for", builtin::for_loop);
	if (name == "while") return MlValue("while", builtin::while_loop);
	if (name == "scope") return MlValue("scope", builtin::scope);
	if (name == "quote") return MlValue("quote", builtin::quote);
	if (name == "defun") return MlValue("defun", builtin::defun);
	if (name == "lambda") return MlValue("lambda", builtin::lambda);
	if (name == "and") return MlValue("and", builtin::do_and);
	if (name == "or") return MlValue("or", builtin::do_or);
	if (name == "set!") return MlValue("set!", builtin::setx);

	// Comparison operations
	if (name == "=") return MlValue("=", builtin::eq);
	if (name == "!=") return MlValue("!=", builtin::neq);
	if (name == ">") return MlValue(">", builtin::greater);
	if (name == "<") return MlValue("<", builtin::less);
	if (name == ">=") return MlValue(">=", builtin::greater_eq);
	if (name == "<=") return MlValue("<=", builtin::less_eq);

	// Meta operations
	if (name == "eval") return MlValue("eval", builtin::eval);
	if (name == "type") return MlValue("type", builtin::get_type_name);
	if (name == "parse") return MlValue("parse", builtin::parse);

	// Arithmetic operations
	if (name == "+") return MlValue("+", builtin::sum);
	if (name == "-") return MlValue("-", builtin::subtract);
	if (name == "*") return MlValue("*", builtin::product);
	if (name == "/") return MlValue("/", builtin::divide);
	if (name == "%") return MlValue("%", builtin::remainder);

	// List operations
	if (name == "list") return MlValue("list", builtin::list);
	if (name == "insert") return MlValue("insert", builtin::insert);
	if (name == "index") return MlValue("index", builtin::index);
	if (name == "remove") return MlValue("remove", builtin::remove);
	if (name == "len") return MlValue("len", builtin::len);
	if (name == "push") return MlValue("push", builtin::push);
	if (name == "pop") return MlValue("pop", builtin::pop);
	if (name == "head") return MlValue("head", builtin::head);
	if (name == "tail") return MlValue("tail", builtin::tail);
	if (name == "first") return MlValue("first", builtin::head);
	if (name == "last") return MlValue("last", builtin::pop);
	if (name == "range") return MlValue("range", builtin::range);

	// Functional operations
	if (name == "map") return MlValue("map", builtin::map_list);
	if (name == "filter") return MlValue("filter", builtin::filter_list);
	if (name == "reduce") return MlValue("reduce", builtin::reduce_list);

	// IO operations
	if (name == "exit") return MlValue("exit", builtin::exit);
	if (name == "quit") return MlValue("quit", builtin::exit);
	if (name == "print") return MlValue("print", builtin::print);
	if (name == "input") return MlValue("input", builtin::input);
	if (name == "random") return MlValue("random", builtin::random);
	if (name == "include") return MlValue("include", builtin::include);
	if (name == "read-file") return MlValue("read-file", builtin::read_file);
	if (name == "read-file-lines") return MlValue("read-file-lines", builtin::read_file_lines);
	if (name == "write-file") return MlValue("write-file", builtin::write_file);
	if (name == "read-url") return MlValue("read-url", builtin::read_url);
	if (name == "system-cmd") return MlValue("system-cmd", builtin::system_cmd);
	if (name == "ls-dir") return MlValue("ls-dir", builtin::ls_dir);
	if (name == "is-file?") return MlValue("is-file?", builtin::is_file);
	if (name == "is-dir?") return MlValue("is-dir?", builtin::is_dir);

	// parsing operations
	if (name == "parse-csv") return MlValue("parse-csv", builtin::parse_csv);
	if (name == "parse-json") return MlValue("parse-json", builtin::parse_json);

	// Datetime operations
	if (name == "get-universal-time") return MlValue("get-universal-time", builtin::get_universal_time);
	if (name == "date-to-str") return MlValue("date-to-str", builtin::date_to_str);
	if (name == "str-to-date") return MlValue("str-to-date", builtin::str_to_date);
	if (name == "date-add") return MlValue("date-add", builtin::date_add);

	// String operations
	if (name == "debug") return MlValue("debug", builtin::debug);
	if (name == "sprintf") return MlValue("sprintf", builtin::sprintf);
	if (name == "display") return MlValue("display", builtin::display);
	if (name == "string-replace") return MlValue("string-replace", builtin::string_replace);
	if (name == "string-regex?") return MlValue("string-regex?", builtin::string_regex);
	if (name == "string-split") return MlValue("string-split", builtin::string_split);
	if (name == "string-pad") return MlValue("string-pad", builtin::string_pad);
	if (name == "string-rltrim") return MlValue("string-rltrim", builtin::string_rltrim);
	if (name == "string-case") return MlValue("string-case", builtin::string_case);
	if (name == "string-len") return MlValue("string-len", builtin::string_len);
	if (name == "string-substr") return MlValue("string-substr", builtin::string_substr);
	if (name == "string-find") return MlValue("string-find", builtin::string_find);

	// Casting operations
	if (name == "int") return MlValue("int", builtin::cast_to_int);
	if (name == "float") return MlValue("float", builtin::cast_to_float);
	if (name == "string") return MlValue("string", builtin::cast_to_string);

	// Other special forms
	if (name == "benchmark") return MlValue("benchmark", builtin::benchmark);

	// Threading operations
	if (name == "thread-create") return MlValue("thread-create", builtin::thread_create);
	if (name == "thread-under-lock") return MlValue("thread-under-lock", builtin::thread_under_lock);
	if (name == "thread-sleep") return MlValue("thread-sleep", builtin::thread_sleep);
	if (name == "threads-join") return MlValue("threads-join", builtin::threads_join);

	// Exceptions
	if (name == "try") return MlValue("try", builtin::try_block);
	if (name == "throw") return MlValue("throw", builtin::throw_exception);

	// Usql
	if (name == "usql") return MlValue("usql", builtin::usql);

	std::map<std::string, MlValue>::const_iterator itr = defs.find(name);
	if (itr != defs.end()) return itr->second;
	else if (parent_scope != nullptr) {
		itr = parent_scope->defs.find(name);
		if (itr != parent_scope->defs.end()) return itr->second;
		else return parent_scope->get(name);
	}

	throw MlError(MlValue::atom(name), *this, ATOM_NOT_DEFINED);
}

// Get vector of executables in this scope
std::vector<std::string> MlEnvironment::get_lambdas_list() const {
	std::vector<std::string> lambdas{128};

	for (auto it = defs.begin(); it != defs.end(); it++) {
		if (it->second.get_type_name() == FUNCTION_TYPE) {
			lambdas.push_back(it->first);
		}
	}

	return lambdas;
}


void repl(MlEnvironment &env) {
	std::string code;
	std::string input;
	MlValue tmp;
	std::vector<MlValue> parsed;

	setup_linenoise(env);

	while (true) {
		char *line = linenoise(">>> ");
		if (line == nullptr) break;

		linenoise_line_read(line);

		input = std::string(line);

		if (input == "!quit" || input == "!q")
			break;
		else if (input == "!env" || input == "!e")
			std::cout << env << std::endl;
		else if (input == "!export" || input == "!x") {
			std::cout << "File to export to: ";
			std::getline(std::cin, input);

			write_file_contents(input, code);
		} else if (input != "") {
			try {
				tmp = run(input, env);
				std::cout << " => " << tmp.debug() << std::endl;
				code += input + "\n";
			} catch (MlError &e) {
				std::cerr << e.description() << std::endl;
				MlPerfMon::instance().clear_callstack();
			} catch (std::exception &e) {
				std::cerr << e.what() << std::endl;
			}
		}
	}

	close_linenoise();
}


bool cmdOptionExists(char **begin, char **end, const std::string &option) {
	return std::find(begin, end, option) != end;
}

std::vector<std::string> getCmdOption(char *argv[], int argc, const std::string &option) {
	std::vector<std::string> tokens;
	for (int i = 1; i < argc; ++i) {
		if (option == argv[i] && i + 1 < argc) {
			i++;
			tokens.push_back(std::string(argv[i]));
		}
	}
	return tokens;
}

int main(int argc, char *argv[]) {
	MlEnvironment env;
	std::vector<MlValue> args;
	for (int i = 0; i < argc; i++)
		args.push_back(MlValue::string(argv[i]));
	env.set("cmd-args", MlValue(args));

	srand(time(NULL));
	try {
		// performance monitor on
		if (cmdOptionExists(argv, argv + argc, "-p"))
			MlPerfMon::instance().turnOn();
		// skip loading std lib
		if (!cmdOptionExists(argv, argv + argc, "-b")) {
			load_std_lib(env);
		}
		// help
		if (cmdOptionExists(argv, argv + argc, "-h")) {
			std::cout
				    << "Usage:\n\t-h print this help\n\t-b skip stdlib loading\n\t-c code - runs code passed on command line\n\t-f source_file - executes code in file\n\t-i runs repl\n\t-p prints profile info at the end\n\t-v prints version string\n\n";
			return 0;
		}
		// version
		if (cmdOptionExists(argv, argv + argc, "-v")) {
			std::cout << VERSION << std::endl;
			return 0;
		}
		// passed code
		if (cmdOptionExists(argv, argv + argc, "-c")) {
			std::vector<std::string> codes = getCmdOption(argv, argc, "-c");
			for (size_t i = 0; i < codes.size(); i++)
				run(codes[i], env);
			// run files
		} else if (cmdOptionExists(argv, argv + argc, "-f")) {
			std::vector<std::string> files = getCmdOption(argv, argc, "-f");
			for (size_t i = 0; i < files.size(); i++)
				run(read_file_contents(files[i]), env);
			// just one parameter - filename
		} else if (argc == 2 && !cmdOptionExists(argv, argv + argc, "-b")) {
			std::string file_content = read_file_contents(argv[1]);
			if (file_content.find("#!") == 0)        // shebang ?
				file_content.erase(0, file_content.find("\n") +
						      1);                // TODO mac osx newline??
			run(file_content, env);
			// repl
		} else {
			repl(env);
		}

		MlPerfMon::instance().print_results();

		return 0;

	} catch (MlError &e) {
		std::cerr << e.description() << std::endl;
	} catch (const std::exception &e) {
		std::cerr << MlPerfMon::instance().callstack() << e.what() << std::endl;
		MlPerfMon::instance().clear_callstack();
	}

	return 1;
}
