view interps/sadol/BDSM2.cpp @ 12500:e48c08805365 draft default tip

<b_jonas> ` learn \'The password of the month is Cthulhuquagdonic Mothraquagdonic Narwhalicorn.\' # https://logs.esolangs.org/libera-esolangs/2024-04.html#lKE Infinite craft
author HackEso <hackeso@esolangs.org>
date Wed, 01 May 2024 06:39:10 +0000
parents 859f9b4339e6
children
line wrap: on
line source

/*
 * BDSM2
 * Author: Adam Sawicki
 * http://www.regedit.risp.pl
 * mailto:regedit@risp.pl
 */
#include "pch.hpp"
#include <iostream>
#include <cstdio>
#include "Console.hpp"
#include "Source.hpp"

// array of elements of type T indexed by 'A'..'Z' or 'a'..'z'
// 'A' and 'a' means the same elements etc.
template <typename T>
class CharIndexedArray
{
private:
	T m_Arr[26];

public:
	T & operator [] (char ch)
	{
		if (ch >= 'a' && ch <= 'z')
			return m_Arr[ch-'a'];
		else if (ch >= 'A' && ch <= 'Z')
			return m_Arr[ch-'A'];
		else
			Error("Invalid char array index: "+CharToStr(ch));
		return m_Arr[0];
	}
	const T & operator [] (char ch) const
	{
		if (ch >= 'a' && ch <= 'z')
			return m_Arr[ch-'a'];
		else if (ch >= 'A' && ch <= 'Z')
			return m_Arr[ch-'A'];
		else
			Error("Invalid char array index: "+CharToStr(ch));
		return m_Arr[0];
	}
};

//==============================================================================

class Value
{
public:
	enum TYPE {
		TYPE_NONE,
		TYPE_INTEGER,
		TYPE_DOUBLE,
		TYPE_STRING,
		TYPE_LIST,
	};

	typedef std::vector<Value> ValueVector;

	// return:
	// value < 0 if v1 < v2
	// value == 0 if v1 == v2
	// value > 0 if v1 > v2
	static int ValueCmp(const Value &v1, const Value &v2);

private:
	struct StringRealValue
	{
	private:
		int m_ReferenceCounter;

	public:
		string m_String;

		// create new value with counter 1, empty
		StringRealValue() : m_ReferenceCounter(1) { }
		// create new value with counter 1, initialized
		StringRealValue(const string &String) : m_ReferenceCounter(1), m_String(String) { }
		// create new value with counter 1 based on existing value
		StringRealValue(const StringRealValue &v) : m_ReferenceCounter(1), m_String(v.m_String) { }
		void AddReference() { m_ReferenceCounter++; }
		int GetReferenceCount() { return m_ReferenceCounter; }
		// dec reference counter, free object if 0
		static void Destroy(StringRealValue *v);
	};

	struct ListRealValue
	{
	private:
		int m_ReferenceCounter;

	public:
		ValueVector m_List;

		// create new value with counter 1, empty
		ListRealValue() : m_ReferenceCounter(1) { }
		// create new value with counter 1, initialized
		ListRealValue(const ValueVector &List) : m_ReferenceCounter(1), m_List(List) { }
		// create new value with counter 1 based on existing value
		ListRealValue(const ListRealValue &v) : m_ReferenceCounter(1), m_List(v.m_List) { }
		void AddReference() { m_ReferenceCounter++; }
		int GetReferenceCount() { return m_ReferenceCounter; }
		// dec reference counter, free object if 0
		static void Destroy(ListRealValue *v);
	};

	TYPE m_Type;
	union {
		int m_Integer;
		double m_Double;
		StringRealValue *m_String;
		ListRealValue *m_List;
	};

	void CreateCopy(const Value &v);
	void Destroy();

	void ConvertToInteger(Value *v) const;
	void ConvertToDouble(Value *v) const;
	void ConvertToString(Value *v) const;
	void ConvertToList(Value *v) const;

public:
	// create uninitialized value
	Value() : m_Type(TYPE_NONE) { }
	// create initialized, empty value with given type
	// Integer or Double has undefined value, String or List is empty.
	Value(TYPE Type);
	// copy constructor
	Value(const Value &v);
	// create initialized value
	Value(int Integer) : m_Type(TYPE_INTEGER), m_Integer(Integer) { }
	Value(double Double) : m_Type(TYPE_DOUBLE), m_Double(Double) { }
	Value(const string &String) : m_Type(TYPE_STRING), m_String(new StringRealValue(String)) { }
	Value(const ValueVector &List) : m_Type(TYPE_STRING), m_List(new ListRealValue(List)) { }
	~Value();

	Value & operator = (const Value &v);
	Value & operator = (TYPE Type);
	Value & operator = (int Integer);
	Value & operator = (double Double);
	Value & operator = (const ValueVector &List);
	Value & operator = (const string &String);

	TYPE GetType() const { return m_Type; }
	string GetTypeName() const;
	// only if type is Integer
	const int & ReadInteger() const { return m_Integer; }
	int & AcceessInteger() { return m_Integer; }
	// only if type is Double
	const double & ReadDouble() const { return m_Double; }
	double & AccessDouble() { return m_Double; }
	// only if type is List
	// use if don't want to modify - performance!
	const ValueVector & ReadList() const { return m_List->m_List; }
	// only if type is String
	// use if don't want to modify - performance!
	const string & ReadString() const { return m_String->m_String; }
	// only if type is List
	ValueVector & AccessList();
	// only if type is String
	string & AccessString();

	void ConsoleOut() const;

	void ConvertTo(Value *v, TYPE Type) const;
	bool ConvertToBool() const;
	int ConvertToInteger() const;
	double ConvertToDouble() const;
	void ConvertToString(string *s) const;
};

int Value::ValueCmp(const Value &v1, const Value &v2)
{
	// list comparison
	if (v1.GetType() == Value::TYPE_LIST || v2.GetType() == Value::TYPE_LIST)
	{
		Value w1, w2;
		v1.ConvertTo(&w1, Value::TYPE_LIST);
		v2.ConvertTo(&w2, Value::TYPE_LIST);

		int R;
		for (size_t i = 0; true; i++)
		{
			if (i == w1.ReadList().size() && i == w2.ReadList().size())
				return 0;
			else if (i == w1.ReadList().size())
				return -1;
			else if (i == w2.ReadList().size())
				return +1;
			else
			{
				R = ValueCmp(w1.ReadList()[i], w2.ReadList()[i]);
				if (R != 0)
					return R;
			}
		}
	}
	// string comparison
	else if (v1.GetType() == Value::TYPE_STRING || v2.GetType() == Value::TYPE_STRING)
	{
		Value w1, w2;
		v1.ConvertTo(&w1, Value::TYPE_STRING);
		v2.ConvertTo(&w2, Value::TYPE_STRING);

		if (w1.ReadString() == w2.ReadString())
			return 0;
		else if (w1.ReadString() < w2.ReadString())
			return -1;
		else
			return +1;
	}
	// double comparison
	else if (v1.GetType() == Value::TYPE_DOUBLE || v2.GetType() == Value::TYPE_DOUBLE)
	{
		Value w1, w2;
		v1.ConvertTo(&w1, Value::TYPE_DOUBLE);
		v2.ConvertTo(&w2, Value::TYPE_DOUBLE);

		if (w1.ReadDouble() == w2.ReadDouble())
			return 0;
		else if (w1.ReadDouble() < w2.ReadDouble())
			return -1;
		else
			return +1;
	}
	// integer comparison
	else
	{
		// both must be integer
		return v1.ReadInteger() - v2.ReadInteger();
	}
}

void Value::StringRealValue::Destroy(StringRealValue *v)
{
	v->m_ReferenceCounter--;
	if (v->m_ReferenceCounter == 0)
		delete v;
}

void Value::ListRealValue::Destroy(ListRealValue *v)
{
	v->m_ReferenceCounter--;
	if (v->m_ReferenceCounter == 0)
		delete v;
}

void Value::CreateCopy(const Value &v)
{
	m_Type = v.m_Type;

	switch (v.m_Type)
	{
	case TYPE_INTEGER:
		m_Integer = v.m_Integer;
		break;
	case TYPE_DOUBLE:
		m_Double = v.m_Double;
		break;
	// add reference to the same real value object
	case TYPE_STRING:
		m_String = v.m_String;
		m_String->AddReference();
		break;
	case TYPE_LIST:
		m_List = v.m_List;
		m_List->AddReference();
		break;
	}
}

void Value::Destroy()
{
	// dec reference, destroy if needed
	switch (m_Type)
	{
	case TYPE_STRING:
		StringRealValue::Destroy(m_String);
		break;
	case TYPE_LIST:
		ListRealValue::Destroy(m_List);
		break;
	}
}

void Value::ConvertToInteger(Value *v) const
{
	*v = ConvertToInteger();
}

void Value::ConvertToDouble(Value *v) const
{
	*v = ConvertToDouble();
}

void Value::ConvertToString(Value *v) const
{
	switch (m_Type)
	{
	case TYPE_INTEGER:
		{
			string s;
			IntToStr(ReadInteger(), &s);
			*v = s;
		}
		break;
	case TYPE_DOUBLE:
		*v = DoubleToStr(ReadDouble(), 'g');
		break;
	case TYPE_STRING:
		*v = *this;
		break;
	case TYPE_LIST:
		Error("Cannot convert list to string");
		break;
	case TYPE_NONE:
		Error("Cannot convert uninitialized value to string");
	}
}

void Value::ConvertToList(Value *v) const
{
	switch (m_Type)
	{
	case TYPE_INTEGER:
	case TYPE_DOUBLE:
	case TYPE_STRING:
		*v = Value::TYPE_LIST;
		v->AccessList().push_back(*this);
		break;
	case TYPE_LIST:
		*v = *this;
		break;
	case TYPE_NONE:
		Error("Cannot convert uninitialized value to list");
	}
}

Value::Value(TYPE Type) : m_Type(Type)
{
	switch (Type)
	{
	case TYPE_STRING:
		m_String = new StringRealValue();
		break;
	case TYPE_LIST:
		m_List = new ListRealValue();
		break;
	}
}

Value::Value(const Value &v)
{
	CreateCopy(v);
}

Value::~Value()
{
	Destroy();
}

Value & Value::operator = (const Value &v)
{
	if (&v != this)
	{
		// as destructor
		Destroy();
		// as constructor
		CreateCopy(v);
	}
	return *this;
}

Value & Value::operator = (TYPE Type)
{
	// as destructor
	Destroy();
	// as constructor
	m_Type = Type;
	switch (Type)
	{
	case TYPE_STRING:
		m_String = new StringRealValue();
		break;
	case TYPE_LIST:
		m_List = new ListRealValue();
		break;
	}

	return *this;
}

Value & Value::operator = (int Integer)
{
	// as destructor
	Destroy();
	// as constructor
	m_Type = TYPE_INTEGER;
	m_Integer = Integer;

	return *this;
}

Value & Value::operator = (double Double)
{
	// as destructor
	Destroy();
	// as constructor
	m_Type = TYPE_DOUBLE;
	m_Double = Double;

	return *this;
}

Value & Value::operator = (const ValueVector &List)
{
	// as destructor
	Destroy();
	// as constructor
	m_Type = TYPE_LIST;
	m_List = new ListRealValue(List);

	return *this;
}

Value & Value::operator = (const string &String)
{
	// as destructor
	Destroy();
	// as constructor
	m_Type = TYPE_STRING;
	m_String = new StringRealValue(String);

	return *this;
}

string Value::GetTypeName() const
{
	switch (m_Type)
	{
	case TYPE_NONE:
		return "none";
	case TYPE_INTEGER:
		return "integer";
	case TYPE_DOUBLE:
		return "double";
	case TYPE_STRING:
		return "string";
	case TYPE_LIST:
		return "list";
	default:
		return "unknown";
	}
}

Value::ValueVector & Value::AccessList()
{
	if (m_List->GetReferenceCount() > 1)
	{
		// copy on write!
		ListRealValue *l = new ListRealValue(*m_List);
		ListRealValue::Destroy(m_List);
		m_List = l;
	}
	return m_List->m_List;
}

string & Value::AccessString()
{
	if (m_String->GetReferenceCount() > 1)
	{
		// copy on write!
		StringRealValue *s = new StringRealValue(*m_String);
		StringRealValue::Destroy(m_String);
		m_String = s;
	}
	return m_String->m_String;
}

void Value::ConsoleOut() const
{
	switch (m_Type)
	{
	case TYPE_INTEGER:
		g_Console.OutInteger(ReadInteger());
		break;
	case TYPE_DOUBLE:
		g_Console.OutDouble(ReadDouble());
		break;
	case TYPE_STRING:
		g_Console.OutString(ReadString());
		break;
	case TYPE_LIST:
		{
			g_Console.OutString("(");
			for (size_t i = 0; i < ReadList().size(); i++)
			{
				if (i > 0)
					g_Console.OutString(",");
				ReadList()[i].ConsoleOut();
			}
			g_Console.OutString(")");
		}
		break;
	case TYPE_NONE:
		Error("Cannot print uninitialized value", ERR_IO);
	}
}

void Value::ConvertTo(Value *v, TYPE Type) const
{
	switch (Type)
	{
	case TYPE_INTEGER:
		ConvertToInteger(v);
		break;
	case TYPE_DOUBLE:
		ConvertToDouble(v);
		break;
	case TYPE_STRING:
		ConvertToString(v);
		break;
	case TYPE_LIST:
		ConvertToList(v);
		break;
	case TYPE_NONE:
		Error("Cannot convert to uninitialized value");
	}
}

bool Value::ConvertToBool() const
{
	switch (m_Type)
	{
	case TYPE_INTEGER:
		return (m_Integer >= 1);
	case TYPE_DOUBLE:
		return (m_Double >= 1.0);
	case TYPE_STRING:
		return !ReadString().empty();
	case TYPE_LIST:
		return !ReadList().empty();
	case TYPE_NONE:
		Error("Cannot convert uninitialized value to bool");
	}
	return false;
}

int Value::ConvertToInteger() const
{
	switch (m_Type)
	{
	case TYPE_INTEGER:
		return ReadInteger();
	case TYPE_DOUBLE:
		return Round(ReadDouble());
	case TYPE_STRING:
		return (int)ReadString().size();
	case TYPE_LIST:
		return (int)ReadList().size();
	case TYPE_NONE:
		Error("Cannot convert uninitialized value to integer");
	}
	return 0;
}

double Value::ConvertToDouble() const
{
	switch (m_Type)
	{
	case TYPE_INTEGER:
		return (double)ReadInteger();
	case TYPE_DOUBLE:
		return ReadDouble();
	case TYPE_STRING:
		return (double)ReadString().size();
	case TYPE_LIST:
		return (double)ReadList().size();
	case TYPE_NONE:
		Error("Cannot convert uninitialized value to double");
	}
	return 0.0;
}

void Value::ConvertToString(string *s) const
{
	switch (m_Type)
	{
	case TYPE_INTEGER:
		IntToStr(ReadInteger(), s);
		break;
	case TYPE_DOUBLE:
		*s = DoubleToStr(ReadDouble(), 'g');
		break;
	case TYPE_STRING:
		*s = ReadString();
		break;
	case TYPE_LIST:
		{
			string s2;
			*s = "(";
			for (size_t i = 0; i < ReadList().size(); i++)
			{
				if (i > 0)
					*s += ",";
				ReadList()[i].ConvertToString(&s2);
				*s += s2;
			}
			*s += ")";
		}
		break;
	case TYPE_NONE:
		Error("Cannot convert uninitialized value to string");
	}
}

//==============================================================================

struct LValue
{
	enum TYPE
	{
		TYPE_VALUE,
		TYPE_STRCHAR,
	};

	TYPE m_Type;
	union
	{
		// only if value
		Value *m_Value;
		// only if str char
		string *m_String;
	};
	// only if str char
	size_t m_StringIndex;

	LValue() { }
	LValue(Value *a_Value) : m_Type(TYPE_VALUE), m_Value(a_Value) { }
	LValue(string *a_String, size_t a_StringIndex) : m_Type(TYPE_STRCHAR), m_String(a_String), m_StringIndex(a_StringIndex) { }
};

//==============================================================================

// Contains char indexed array of value pointers.
// Auto-init and auto-destroy.
struct VarContext
{
	CharIndexedArray<Value*> m_Values;

	VarContext()
	{
		for (char ch = 'A'; ch < 'Z'; ch++)
			m_Values[ch] = 0;
	}
	~VarContext()
	{
		for (char ch = 'A'; ch < 'Z'; ch++)
			delete m_Values[ch];
	}
};

// Stack of var contexts
class VarContextStack
{
private:
	std::vector<VarContext*> m_Stack;
	Value m_DefaultValue;

public:
	VarContextStack();

	void PushNew();
	void Pop();

	const Value & ReadVar(char ch) const;
	Value & AccessVar(char ch);
};

VarContextStack::VarContextStack() : m_DefaultValue((int)0)
{
}

void VarContextStack::PushNew()
{
	m_Stack.push_back(new VarContext);
}

void VarContextStack::Pop()
{
	delete m_Stack.back();
	m_Stack.pop_back();
}

const Value & VarContextStack::ReadVar(char ch) const
{
	for (std::vector<VarContext*>::const_reverse_iterator rit = m_Stack.rbegin(); rit != m_Stack.rend(); ++rit)
		if ((*rit)->m_Values[ch])
			return *((*rit)->m_Values[ch]);
	return m_DefaultValue;
}

Value & VarContextStack::AccessVar(char ch)
{
	if (m_Stack.back()->m_Values[ch] == 0)
		m_Stack.back()->m_Values[ch] = new Value(m_DefaultValue);
	return *m_Stack.back()->m_Values[ch];
}

// Only in Execute call sequence.
VarContextStack g_VarContextStack;
VarContext g_GlobalVars;

//==============================================================================

struct Function;
struct Node;

struct Scope
{
private:
	// zero pointer list
	void InitLocalFunctions();

public:
	Scope *m_Parent;
	Node *m_Node;
	CharIndexedArray<Function*> m_LocalFunctions;

	Scope(Scope *a_Parent, Node *a_Node) : m_Parent(a_Parent), m_Node(a_Node) { InitLocalFunctions(); }
	virtual ~Scope();

	void ParseToEnd(Source &src);
	// Parse given source as single expression, fill in this structure's fields
	void Parse(Source &src);
	// Value is list of arguments
	void Execute(Value *result, Value *args);
	void Optimize();
};

struct Function : public Scope
{
	// -1 means variable arg count
	int m_ArgCount;

	Function(Scope *a_Parent, Node *a_Node, int a_ArgCount) : Scope(a_Parent, a_Node), m_ArgCount(a_ArgCount) { }
};



void Scope::InitLocalFunctions()
{
	for (char ch = 'a'; ch <= 'z'; ch++)
		m_LocalFunctions[ch] = 0;
}



// Used only in Parse call sequence.
std::vector<Scope*> g_ScopeStack;
CharIndexedArray<Function*> g_GlobalFunctions;

//==============================================================================

struct Node
{
private:
	void NodeError(const string &msg, ERR_PLACE place = ERR_GENERAL);
	// free subnodes and clear subnode list
	void ClearSubnodes();

	int Parse_Integer(Source &src);
	// does +
	void OpAdd(Value *result, const Value &v1, const Value &v2);
	// does - * %
	void OpSubMulMod(char Op, Value *result, const Value &v1, const Value &v2);
	// does / ^
	void OpDivPow(char Op, Value *result, const Value &v1, const Value &v2);
	// does \ { blablabla - placeholder not to end line with \ }
	void OpFloor(Value *result, const Value &v);
	// does '
	void OpAposConverter(int Kind, Value *result, const Value &v);
	// does = < >
	void OpComparison(char Op, Value *result, const Value &v1, const Value &v2);

public:
	enum TYPE {
		TYPE_SYMBOL,
		TYPE_CONSTANT,
	};

	TYPE m_Type;
	Source::Iterator m_SrcIterator;

	// only if symbol:
	// If 'A'..'Z' or 'a'..'z', m_Function == 0 means variable, != 0 means function call.
	char m_Ch;
	std::vector<Node*> m_Sub;
	union {
		// only if m_Ch == 'a'..'z' or 'A'..'Z' and this is a function call:
		// Function that should be called (weak pointer).
		Function *m_Function;
		// only if m_Ch == ';' => 0, 1, 2, 3
		// or m_Ch == '\'' => 1, 2
		int m_ExtraInt;
	};

	// only if constant:
	Value m_Value;

	Node();
	~Node();
	// Parse given source to the end as sequence of commands, fill in this structure's fields
	void ParseToEnd(Source &src);
	// Parse given source as single expression, fill in this structure's fields
	void Parse(Source &src);
	// Value is list of arguments.
	void Execute(Value *result, Value *args);
	LValue GetLValue(Value *args);
	void Optimize();
};

Scope::~Scope()
{
	for (char ch = 'a'; ch <= 'z'; ch++)
		delete m_LocalFunctions[ch];
	delete m_Node;
}

void Scope::ParseToEnd(Source &src)
{
	g_ScopeStack.push_back(this);

	m_Node = new Node;
	m_Node->ParseToEnd(src);

	g_ScopeStack.pop_back();
}

void Scope::Parse(Source &src)
{
	g_ScopeStack.push_back(this);

	m_Node = new Node;
	m_Node->Parse(src);

	g_ScopeStack.pop_back();
}

void Scope::Execute(Value *result, Value *args)
{
	g_ScopeStack.push_back(this);
	g_VarContextStack.PushNew();

	m_Node->Execute(result, args);

	g_VarContextStack.Pop();
	g_ScopeStack.pop_back();
}

void Scope::Optimize()
{
	// TODO - scope stack
	m_Node->Optimize();
}

void Node::NodeError(const string &msg, ERR_PLACE place)
{
	Error(msg + " (" + m_SrcIterator.ToString() + ')', place);
}

void Node::ClearSubnodes()
{
	for (size_t i = 0; i < m_Sub.size(); i++)
		delete m_Sub[i];
	m_Sub.clear();
}

int Node::Parse_Integer(Source &src)
{
	src.Skip();
	src.SaveErrPos();
	Node *node = new Node();
	node->Parse(src);
	node->Optimize();
	if (node->m_Type != Node::TYPE_CONSTANT)
		src.SrcError("Cannot evaluate number in compilation time", ERR_PARSE);
	src.DropErrPos();
	int R = node->m_Value.ConvertToInteger();
	delete node;

	return R;
}

void Node::OpAdd(Value *result, const Value &v1, const Value &v2)
{
	// 0 = list cat
	// 1 = add as lists
	// 2 = add as strings
	// 3 = add as double
	// 4 = add as integer
	int Type = -1;

	if (v1.GetType() == Value::TYPE_LIST && v2.GetType() == Value::TYPE_LIST)
		Type = 0;
	else if (v1.GetType() == Value::TYPE_LIST || v2.GetType() == Value::TYPE_LIST)
		Type = 1;
	else if (v1.GetType() == Value::TYPE_STRING || v2.GetType() == Value::TYPE_STRING)
		Type = 2;
	else if (v1.GetType() == Value::TYPE_DOUBLE || v2.GetType() == Value::TYPE_DOUBLE)
		Type = 3;
	else
		Type = 4;

	switch (Type)
	{
	case 0:
		*result = v1;
		for (size_t i = 0; i < v2.ReadList().size(); i++)
			result->AccessList().push_back(v2.ReadList()[i]);
		break;
	case 1:
		{
			Value w2;
			v1.ConvertTo(result, Value::TYPE_LIST);
			v2.ConvertTo(&w2, Value::TYPE_LIST);
			for (size_t i = 0; i < w2.ReadList().size(); i++)
				result->AccessList().push_back(w2.ReadList()[i]);
		}
		break;
	case 2:
		{
			Value w2;
			v1.ConvertTo(result, Value::TYPE_STRING);
			v2.ConvertTo(&w2, Value::TYPE_STRING);
			result->AccessString() += w2.ReadString();
		}
		break;
	case 3:
		{
			Value w2;
			v1.ConvertTo(result, Value::TYPE_DOUBLE);
			v2.ConvertTo(&w2, Value::TYPE_DOUBLE);
			result->AccessDouble() += w2.ReadDouble();
		}
		break;
	case 4:
		*result = v1.ReadInteger() + v2.ReadInteger();
		break;
	}
}

void Node::OpSubMulMod(char Op, Value *result, const Value &v1, const Value &v2)
{
	// integer
	if (v1.GetType() == Value::TYPE_INTEGER && v2.GetType() == Value::TYPE_INTEGER)
	{
		int i1 = v1.ReadInteger();
		int i2 = v2.ReadInteger();
		switch (Op)
		{
		case '-':
			*result = i1 - i2;
			break;
		case '*':
			*result = i1 * i2;
			break;
		case '%':
			if (i2 == 0)
				NodeError("Integer division '%' by 0");
			*result = i1 % i2;
			break;
		}
	}
	// double
	else if ( (v1.GetType() == Value::TYPE_INTEGER || v1.GetType() == Value::TYPE_DOUBLE) &&
		(v2.GetType() == Value::TYPE_INTEGER || v2.GetType() == Value::TYPE_DOUBLE) )
	{
		double d1 = v1.ConvertToDouble();
		double d2 = v2.ConvertToDouble();
		switch (Op)
		{
		case '-':
			*result = d1 - d2;
			break;
		case '*':
			*result = d1 * d2;
			break;
		case '%':
			if (d2 == 0.0)
				NodeError("Double division '%' by 0");
			*result = fmod(d1, d2);
			break;
		}
	}
	else
		NodeError("Invalid argument types for '"+CharToStr(Op)+"': "+v1.GetTypeName()+", "+v2.GetTypeName());
}

void Node::OpDivPow(char Op, Value *result, const Value &v1, const Value &v2)
{
	double d1 = v1.ConvertToDouble();
	double d2 = v2.ConvertToDouble();

	switch (Op)
	{
	case '/':
		if (d2 == 0.0)
			NodeError("Double division '/' by 0");
		*result = d1 / d2;
		break;
	case '^':
		*result = pow(d1, d2);
		break;
	}
}

void Node::OpFloor(Value *result, const Value &v)
{
	*result = floor(v.ConvertToDouble());
}

void Node::OpAposConverter(int Kind, Value *result, const Value &v)
{
	if (Kind == 1)
	{
		string s;
		v.ConvertToString(&s);
		if (s.empty())
			NodeError("Cannot convert empty string to ASCII code in '''");
		*result = (int)(unsigned int)(unsigned char)s[0];
	}
	else if (Kind == 2)
	{
		int i = v.ConvertToInteger();
		if (i < 0 || i >= 256)
			NodeError("Cannot convert integer value "+IntToStr(i)+" into ASCII character");
		*result = Value::TYPE_STRING;
		result->AccessString() += (char)(unsigned char)(unsigned int)i;
	}
}

void Node::OpComparison(char Op, Value *result, const Value &v1, const Value &v2)
{
	int R = Value::ValueCmp(v1, v2);
	
	switch (Op)
	{
	case '=':
		*result = (int)( (R == 0) ? 1 : 0 );
		break;
	case '>':
		*result = (int)( (R > 0) ? 1 : 0 );
		break;
	case '<':
		*result = (int)( (R < 0) ? 1 : 0 );
		break;
	}
}

Node::Node() : m_Function(0)
{
}

Node::~Node()
{
	ClearSubnodes();
}

void Node::ParseToEnd(Source &src)
{
	// This node is ( symbol
	m_Type = TYPE_SYMBOL;
	m_SrcIterator = src.GetIterator();
	m_Ch = '(';

	Node *node;
	for (;;)
	{
		src.Skip();

		if (src.End())
			break;

		m_Sub.push_back(node = new Node);
		node->Parse(src);
	}
}

void Node::Parse(Source &src)
{
	src.Skip();

	m_Type = TYPE_SYMBOL;
	m_SrcIterator = src.GetIterator();
	m_Ch = src.GetS();

	// zero args
	if (m_Ch >= '0' && m_Ch <= '9' || m_Ch == '_')
	{
	}
	// one arg
	else if (m_Ch == '[' || m_Ch == '!' || m_Ch == '`' || m_Ch == '\\')
	{
		Node *node = new Node;
		m_Sub.push_back(node);
		node->Parse(src);
	}
	// direct string
	else if (m_Ch == '"')
	{
		int CharCount = Parse_Integer(src);
		m_Type = TYPE_CONSTANT;
		m_Value = Value::TYPE_STRING;
		for (int i = 0; i < CharCount; i++)
			m_Value.AccessString() += src.GetS();
	}
	// function definition
	else if (m_Ch == '~')
	{
		// name
		char func_name;
		src.Skip();
		func_name = src.GetS();
		if (!( func_name >= 'a' && func_name <= 'z' || func_name >= 'A' && func_name <= 'Z' ))
			src.SrcError("Invalid function name: '" + CharToStr(func_name) + "'", ERR_PARSE);

		// argument count
		int func_arg_count = Parse_Integer(src);
		if (func_arg_count < -1)
			NodeError("Invalid argument count for function '"+CharToStr(func_name)+"': "+IntToStr(func_arg_count), ERR_PARSE);

		// - global
		if (func_name >= 'A' && func_name <= 'Z')
		{
			Function *func = new Function(g_ScopeStack.front(), new Node, func_arg_count);
			// save function
			if (g_GlobalFunctions[func_name])
				NodeError("Global function redefinition: '"+CharToStr(func_name)+"'", ERR_PARSE);
			g_GlobalFunctions[func_name] = func;
			// parse body
			func->Parse(src);
			func->Optimize();
		}
		// - local
		else
		{
			Function *func = new Function(g_ScopeStack.back(), new Node, func_arg_count);
			// save function
			if (g_ScopeStack.back()->m_LocalFunctions[func_name])
				NodeError("Local function redefinition: '"+CharToStr(func_name)+"'", ERR_PARSE);
			g_ScopeStack.back()->m_LocalFunctions[func_name] = func;
			// parse body
			func->Parse(src);
			func->Optimize();
		}

		// this expression result
		m_Type = TYPE_CONSTANT;
		m_Value = (int)1;
	}
	// two args
	else if (m_Ch == '+' || m_Ch == '-' || m_Ch == '*' || m_Ch == '/' ||
		m_Ch == '^' || m_Ch == '%' || m_Ch == ':' ||
		m_Ch == '#' || m_Ch == ']' || m_Ch == '@' || m_Ch == '=' ||
		m_Ch == '>' || m_Ch == '<' || m_Ch == '&' || m_Ch == '|')
	{
		Node *node1 = new Node;
		Node *node2 = new Node;
		m_Sub.push_back(node1);
		m_Sub.push_back(node2);
		node1->Parse(src);
		node2->Parse(src);
	}
	// three args
	else if (m_Ch == '?')
	{
		Node *node1 = new Node;
		Node *node2 = new Node;
		Node *node3 = new Node;
		m_Sub.push_back(node1);
		m_Sub.push_back(node2);
		m_Sub.push_back(node3);
		node1->Parse(src);
		node2->Parse(src);
		node3->Parse(src);
	}
	// function call or variable
	else if (m_Ch >= 'a' && m_Ch <= 'z' || m_Ch >= 'A' && m_Ch <= 'Z')
	{
		// if function, fill in the m_Function
		m_Function = 0;
		// - global
		if (m_Ch >= 'A' && m_Ch <= 'Z')
			// assign proper pointer or 0
			m_Function = g_GlobalFunctions[m_Ch];
		// - local
		else
		{
			Scope *scope = g_ScopeStack.back();
			while (scope != 0)
			{
				if (scope->m_LocalFunctions[m_Ch])
				{
					m_Function = scope->m_LocalFunctions[m_Ch];
					break;
				}
				scope = scope->m_Parent;
			}
		}
		
		// this is function call
		if (m_Function)
		{
			// pointer to function is already saved in m_Function
			// argument count
			int arg_count = m_Function->m_ArgCount;
			if (arg_count == -1)
				arg_count = Parse_Integer(src);
			// arguments
			Node *node;
			for (int i = 0; i < arg_count; i++)
			{
				node = new Node;
				node->Parse(src);
				m_Sub.push_back(node);
			}
		}
		// this is variable - do nothing
		// m_Function stays 0
	}
	// variable arg count
	else if (m_Ch == ',' || m_Ch == '.' || m_Ch == '(' || m_Ch == '$') {
		int SubnodeCount = Parse_Integer(src);
		Node *node;
		for (int i = 0; i < SubnodeCount; i++)
		{
			node = new Node;
			node->Parse(src);
			m_Sub.push_back(node);
		}
	}
	else if (m_Ch == ';')
	{
		m_ExtraInt = Parse_Integer(src);
	}
	else if (m_Ch == '\'')
	{
		m_ExtraInt = Parse_Integer(src);
		if (m_ExtraInt == 0)
		{
			m_Type = TYPE_CONSTANT;
			m_Value = (int)(unsigned int)(unsigned char)src.GetS();
		}
		else if (m_ExtraInt == 1 || m_ExtraInt == 2)
		{
			// one subnode
			Node *node = new Node;
			node->Parse(src);
			m_Sub.push_back(node);
		}
		else
			NodeError("Invalid numeric constant in ''': "+IntToStr(m_ExtraInt), ERR_PARSE);
	}
	else
		// Erroneous character
		src.SrcError("Unsupported character: '" + CharToStr(m_Ch) + "'", ERR_PARSE);
}

void Node::Execute(Value *result, Value *args)
{
	// Value
	if (m_Type == TYPE_CONSTANT)
	{
		*result = m_Value;
		return;
	}

	// Code symbol
	if (m_Ch >= '0' && m_Ch <= '9')
	{
		*result = (int)(unsigned char)(m_Ch - '0');
	}
	// function call or variable
	else if (m_Ch >= 'a' && m_Ch <= 'z' || m_Ch >= 'A' && m_Ch <= 'Z')
	{
		// function call
		if (m_Function)
		{
			// construct argument list
			Value CallArgs = Value(Value::TYPE_LIST);
			Value CallArg;
			for (size_t i = 0; i < m_Sub.size(); i++)
			{
				m_Sub[i]->Execute(&CallArg, args);
				CallArgs.AccessList().push_back(CallArg);
			}
			// call function
			m_Function->Execute(result, &CallArgs);
		}
		// variable
		else
		{
			// global variable
			if (m_Ch >= 'A' && m_Ch <= 'Z')
			{
				if (g_GlobalVars.m_Values[m_Ch])
					*result = *g_GlobalVars.m_Values[m_Ch];
				else
					*result = (int)0;
			}
			// local variable
			else
				*result = g_VarContextStack.ReadVar(m_Ch);
		}
	}
	else if (m_Ch == '$')
	{
		Value element;
		*result = Value::TYPE_LIST;
		for (size_t i = 0; i < m_Sub.size(); i++)
		{
			m_Sub[i]->Execute(&element, args);
			result->AccessList().push_back(element);
		}
	}
	else if (m_Ch == ':')
	{
		m_Sub[1]->Execute(result, args);
		LValue lval = m_Sub[0]->GetLValue(args);
		// assign to a value
		if (lval.m_Type == LValue::TYPE_VALUE)
			*lval.m_Value = *result;
		// assign to a string character
		else
		{
			// string with length 1 is expected
			Value str_val;
			result->ConvertTo(&str_val, Value::TYPE_STRING);
			if (str_val.ReadString().length() != 1)
				NodeError("Cannot assing to string character string with length: "+Size_tToStr(str_val.ReadString().length()), ERR_EXECUTE);
			(*lval.m_String)[lval.m_StringIndex] = str_val.ReadString()[0];
		}
	}
	else if (m_Ch == '#')
	{
		Value val_l, val_n;
		m_Sub[1]->Execute(&val_n, args);
		m_Sub[0]->Execute(&val_l, args);
		int index = val_n.ConvertToInteger();
		if (val_l.GetType() == Value::TYPE_LIST)
		{
			if (index == -1)
				*result = (int)val_l.ReadList().size();
			else if (index >= 0 && index < (int)val_l.ReadList().size())
				*result = val_l.ReadList()[index];
			else
				NodeError("Cannot extract element from list with '#' on index: "+IntToStr(index)+" - index out of bounds (list size: "+Size_tToStr(val_l.ReadList().size())+")", ERR_EXECUTE);
		}
		else if (val_l.GetType() == Value::TYPE_STRING)
		{
			if (index == -1)
				*result = (int)val_l.ReadString().size();
			else if (index >= 0 && index < (int)val_l.ReadString().size())
				*result = CharToStr(val_l.ReadString()[index]);
			else
				NodeError("Cannot extract element from string with '#' on index: "+IntToStr(index)+" - index out of bounds (string size: "+Size_tToStr(val_l.ReadString().size())+")", ERR_EXECUTE);
		}
		else
			NodeError("Cannot extract element from "+val_l.GetTypeName()+" with '#' on index: "+IntToStr(index)+" - invalid type", ERR_EXECUTE);
	}
	else if (m_Ch == '(')
	{
		*result = (int)0;
		for (size_t i = 0; i < m_Sub.size(); i++)
			m_Sub[i]->Execute(result, args);
	}
	else if (m_Ch == '@')
	{
		*result = (int)0;
		Value condition_value;
		for (;;)
		{
			m_Sub[0]->Execute(&condition_value, args);
			if (!condition_value.ConvertToBool())
				break;
			m_Sub[1]->Execute(result, args);
		}
	}
	else if (m_Ch == '?')
	{
		Value ConditionValue;
		m_Sub[0]->Execute(&ConditionValue, args);
		if (ConditionValue.ConvertToBool())
			m_Sub[1]->Execute(result, args);
		else
			m_Sub[2]->Execute(result, args);
	}
	else if (m_Ch == '!') {
		m_Sub[0]->Execute(result, args);
		result->ConsoleOut();
	}
	else if (m_Ch == ';')
	{
		switch (m_ExtraInt)
		{
		case 0:
			*result = Value::TYPE_STRING;
			result->AccessString() += g_Console.InChar();
			break;
		case 1:
			*result = g_Console.InInteger();
			break;
		case 2:
			*result = g_Console.InDouble();
			break;
		case 3:
			*result = Value::TYPE_STRING;
			g_Console.InString(&result->AccessString());
			break;
		}
	}
	else if (m_Ch == '=' || m_Ch == '<' || m_Ch == '>')
	{
		Value v1, v2;
		m_Sub[0]->Execute(&v1, args);
		m_Sub[1]->Execute(&v2, args);
		OpComparison(m_Ch, result, v1, v2);
	}
	else if (m_Ch == '+')
	{
		Value v1, v2;
		m_Sub[0]->Execute(&v1, args);
		m_Sub[1]->Execute(&v2, args);
		OpAdd(result, v1, v2);
	}
	else if (m_Ch == '-' || m_Ch == '*' || m_Ch == '%')
	{
		Value v1, v2;
		m_Sub[0]->Execute(&v1, args);
		m_Sub[1]->Execute(&v2, args);
		OpSubMulMod(m_Ch, result, v1, v2);
	}
	else if (m_Ch == '/' || m_Ch == '^')
	{
		Value v1, v2;
		m_Sub[0]->Execute(&v1, args);
		m_Sub[1]->Execute(&v2, args);
		OpDivPow(m_Ch, result, v1, v2);
	}
	else if (m_Ch == '\\')
	{
		Value v;
		m_Sub[0]->Execute(&v, args);
		OpFloor(result, v);
	}
	else if (m_Ch == '&')
	{
		Value V;
		bool B;
		m_Sub[0]->Execute(&V, args);
		B = V.ConvertToBool();
		if (B)
		{
			m_Sub[1]->Execute(&V, args);
			B = V.ConvertToBool();
		}
		*result = (int)(B ? 1 : 0);
	}
	else if (m_Ch == '|')
	{
		Value V;
		bool B;
		m_Sub[0]->Execute(&V, args);
		B = ! V.ConvertToBool();
		if (B)
		{
			m_Sub[1]->Execute(&V, args);
			B = ! V.ConvertToBool();
		}
		*result = (int)(B ? 0 : 1);
	}
	else if (m_Ch == '[')
	{
		LValue lval = m_Sub[0]->GetLValue(args);
		if (lval.m_Type != LValue::TYPE_VALUE)
			NodeError("Tyme mismatch in '[' - list or string expected", ERR_EXECUTE);
		if (lval.m_Value->GetType() == Value::TYPE_LIST)
		{
			if (lval.m_Value->ReadList().empty())
				NodeError("Empty list for '['", ERR_EXECUTE);
			*result = lval.m_Value->ReadList().back();
			lval.m_Value->AccessList().pop_back();
		}
		else if (lval.m_Value->GetType() == Value::TYPE_STRING)
		{
			if (lval.m_Value->ReadString().empty())
				NodeError("Empty string for '['", ERR_EXECUTE);
			*result = CharToStr(lval.m_Value->ReadString()[lval.m_Value->ReadString().size()-1]);
			lval.m_Value->AccessString().erase(lval.m_Value->ReadString().size()-1);
		}
		else
			NodeError("Invalid l-value for '[' ("+lval.m_Value->GetTypeName()+") - list or string expected", ERR_EXECUTE);
	}
	else if (m_Ch == ']')
	{
		Value NewValue;
		m_Sub[1]->Execute(&NewValue, args);
		LValue lval = m_Sub[0]->GetLValue(args);

		if (lval.m_Type != LValue::TYPE_VALUE)
			NodeError("Type mismatch in ']' - list or string expected", ERR_EXECUTE);

		if (lval.m_Value->GetType() == Value::TYPE_LIST)
			lval.m_Value->AccessList().push_back(NewValue);
		else if (lval.m_Value->GetType() == Value::TYPE_STRING)
		{
			if (NewValue.GetType() != Value::TYPE_STRING || NewValue.ReadString().size() != 1)
				NodeError("Invalid argument for ']' - single character expected", ERR_EXECUTE);
			lval.m_Value->AccessString() += NewValue.ReadString()[0];
		}
		else
			NodeError("Invalid l-value for ']' ("+lval.m_Value->GetTypeName()+") - list or string expected", ERR_EXECUTE);

		*result = *lval.m_Value;
	}
	else if (m_Ch == '_')
		*result = *args;
	else if (m_Ch == '\'')
	{
		Value v;
		m_Sub[0]->Execute(&v, args);
		OpAposConverter(m_ExtraInt, result, v);
	}
	else if (m_Ch == '`')
	{
		// read string
		Value str;
		m_Sub[0]->Execute(&str, args);
		if (str.GetType() != Value::TYPE_STRING)
			NodeError("String expected for '`' - found: "+str.GetTypeName(), ERR_EXECUTE);
		// parse code
		Source src(&str.ReadString());
		Node *node = new Node;
		node->ParseToEnd(src);
		node->Optimize();
		node->Execute(result, args);
		delete node;
	}
	else
		NodeError("Unknown command: '"+CharToStr(m_Ch)+"'", ERR_EXECUTE);
}

LValue Node::GetLValue(Value *args)
{
	// variable
	if (m_Ch >= 'a' && m_Ch <= 'z' || m_Ch >= 'A' && m_Ch <= 'Z')
	{
		// function - error
		if (m_Function)
			NodeError("Cannot return '"+CharToStr(m_Ch)+"' as l-value - it is a function call", ERR_EXECUTE);
		// global variable
		if (m_Ch >= 'A' && m_Ch <= 'Z')
		{
			if (!g_GlobalVars.m_Values[m_Ch])
				g_GlobalVars.m_Values[m_Ch] = new Value((int)0);
			return LValue(g_GlobalVars.m_Values[m_Ch]);
		}
		// local variable
		else
			return LValue(&g_VarContextStack.AccessVar(m_Ch));
	}
	else if (m_Ch == '_')
		return LValue(args);
	else if (m_Ch == '(')
	{
		if (m_Sub.size() == 0)
			NodeError("Cannot return empty '(' as l-value", ERR_EXECUTE);
		return m_Sub.back()->GetLValue(args);
	}
	else if (m_Ch == '@')
	{
		LValue lval;
		bool OK = false;
		Value condition_value;
		for (;;)
		{
			m_Sub[0]->Execute(&condition_value, args);
			if (!condition_value.ConvertToBool())
				break;
			lval = m_Sub[1]->GetLValue(args);
			OK = true;
		}
		if (!OK)
			NodeError("Cannot return loop with 0 iterations as l-value", ERR_EXECUTE);
		return lval;
	}
	else if (m_Ch == '#')
	{
		LValue result;
		Value val_n;
		m_Sub[1]->Execute(&val_n, args);
		LValue val_l = m_Sub[0]->GetLValue(args);
		int index = val_n.ConvertToInteger();

		if (val_l.m_Type == LValue::TYPE_STRCHAR)
		{
			if (index == 0)
				result = val_l;
			else
				NodeError("Cannot return element of string's element as l-value if index "+IntToStr(index)+" is not 0", ERR_EXECUTE);
		}
		else
		{
			if (val_l.m_Value->GetType() == Value::TYPE_LIST)
			{
				if (index >= 0 && index < (int)val_l.m_Value->ReadList().size())
				{
					result.m_Type = LValue::TYPE_VALUE;
					result.m_Value = &val_l.m_Value->AccessList()[index];
				}
				else
					NodeError("Cannot return element of list as l-value - index "+IntToStr(index)+" out of bounds", ERR_EXECUTE);
			}
			else if (val_l.m_Value->GetType() == Value::TYPE_STRING)
			{
				if (index >= 0 && index < (int)val_l.m_Value->ReadString().size())
				{
					result.m_Type = LValue::TYPE_STRCHAR;
					result.m_String = &val_l.m_Value->AccessString();
					result.m_StringIndex = index;
				}
				else
					NodeError("Cannot return element of string as l-value - index "+IntToStr(index)+" out of bounds", ERR_EXECUTE);
			}
			else
				NodeError("Cannot return element of "+val_l.m_Value->GetTypeName()+" as l-value", ERR_EXECUTE);
		}
		return result;
	}
	else if (m_Ch == ']')
	{
		Value NewValue;
		m_Sub[1]->Execute(&NewValue, args);
		LValue lval = m_Sub[0]->GetLValue(args);

		if (lval.m_Type != LValue::TYPE_VALUE)
			NodeError("Type mismatch in ']' - list or string expected", ERR_EXECUTE);

		if (lval.m_Value->GetType() == Value::TYPE_LIST)
			lval.m_Value->AccessList().push_back(NewValue);
		else if (lval.m_Value->GetType() == Value::TYPE_STRING)
		{
			if (NewValue.GetType() != Value::TYPE_STRING || NewValue.ReadString().size() != 1)
				NodeError("Invalid argument for ']' - single character expected", ERR_EXECUTE);
			lval.m_Value->AccessString() += NewValue.ReadString()[0];
		}
		else
			NodeError("Invalid l-value for ']' ("+lval.m_Value->GetTypeName()+") - list or string expected", ERR_EXECUTE);

		return lval;
	}
	else if (m_Ch == '?')
	{
		Value ConditionValue;
		m_Sub[0]->Execute(&ConditionValue, args);
		if (ConditionValue.ConvertToBool())
			return m_Sub[1]->GetLValue(args);
		else
			return m_Sub[2]->GetLValue(args);
	}
	else
	{
		NodeError("Cannot obtain l-value from symbol: '"+CharToStr(m_Ch)+"'", ERR_EXECUTE);
		return LValue();
	}
}

void Node::Optimize()
{
	if (m_Type == TYPE_CONSTANT)
		return;

	// optimize subnodes
	for (size_t i = 0; i < m_Sub.size(); i++)
		m_Sub[i]->Optimize();

	// optimize this node
	if (m_Ch >= '0' && m_Ch <= '9') {
		m_Type = TYPE_CONSTANT;
		m_Value = (int)(unsigned char)(m_Ch - '0');
	}
	else if (m_Ch == ',')
	{
		m_Type = TYPE_CONSTANT;
		m_Value = (int)0;
		for (size_t i = 0; i < m_Sub.size(); i++)
		{
			if (m_Sub[i]->m_Type != TYPE_CONSTANT)
				m_Sub[i]->NodeError("Cannot evaluate digit value for ',' in compilation time for digit index: " + Size_tToStr(i), ERR_OPTIMIZE);
			m_Value.AcceessInteger() = m_Value.AcceessInteger() * 10 + m_Sub[i]->m_Value.ConvertToInteger();
		}
		ClearSubnodes();
	}
	else if (m_Ch == '.')
	{
		m_Type = TYPE_CONSTANT;
		m_Value = (double)0.0;
		double factor = 0.1;
		for (size_t i = 0; i < m_Sub.size(); i++)
		{
			if (m_Sub[i]->m_Type != Node::TYPE_CONSTANT)
				m_Sub[i]->NodeError("Cannot evaluate digit value for '.' in compilation time for digit index: " + Size_tToStr(i), ERR_OPTIMIZE);
			m_Value.AccessDouble() += m_Sub[i]->m_Value.ConvertToInteger() * factor;
			factor *= 0.1;
		}
		ClearSubnodes();
	}
	else if (m_Ch == '(')
	{
		for (int i = (int)m_Sub.size()-2; i >= 0; i--)
		{
			if (m_Sub[i]->m_Type == TYPE_CONSTANT)
			{
				delete m_Sub[i];
				m_Sub.erase(m_Sub.begin()+i);
			}
		}
	}
	else if (m_Ch == '+')
	{
		if (m_Sub[0]->m_Type == TYPE_CONSTANT && m_Sub[1]->m_Type == TYPE_CONSTANT)
		{
			m_Type = TYPE_CONSTANT;
			OpAdd(&m_Value, m_Sub[0]->m_Value, m_Sub[1]->m_Value);
			ClearSubnodes();
		}
	}
	else if (m_Ch == '-' || m_Ch == '*' || m_Ch == '%')
	{
		if (m_Sub[0]->m_Type == TYPE_CONSTANT && m_Sub[1]->m_Type == TYPE_CONSTANT)
		{
			m_Type = TYPE_CONSTANT;
			OpSubMulMod(m_Ch, &m_Value, m_Sub[0]->m_Value, m_Sub[1]->m_Value);
			ClearSubnodes();
		}
	}
	else if (m_Ch == '/' || m_Ch == '^')
	{
		if (m_Sub[0]->m_Type == TYPE_CONSTANT && m_Sub[1]->m_Type == TYPE_CONSTANT)
		{
			m_Type = TYPE_CONSTANT;
			OpDivPow(m_Ch, &m_Value, m_Sub[0]->m_Value, m_Sub[1]->m_Value);
			ClearSubnodes();
		}
	}
	else if (m_Ch == '\\')
	{
		if (m_Sub[0]->m_Type == TYPE_CONSTANT)
		{
			m_Type = TYPE_CONSTANT;
			OpFloor(&m_Value, m_Sub[0]->m_Value);
			ClearSubnodes();
		}
	}
	else if (m_Ch == '\'')
	{
		if (m_Sub[0]->m_Type == TYPE_CONSTANT)
		{
			m_Type = TYPE_CONSTANT;
			OpAposConverter(m_ExtraInt, &m_Value, m_Sub[0]->m_Value);
			ClearSubnodes();
		}
	}
	else if (m_Ch == '@')
	{
		if (m_Sub[0]->m_Type == TYPE_CONSTANT && m_Sub[0]->m_Value.ConvertToBool() == false)
		{
			m_Type = TYPE_CONSTANT;
			m_Value = (int)0;
			ClearSubnodes();
		}
	}
	else if (m_Ch == '#')
	{
		if (m_Sub[0]->m_Type == TYPE_CONSTANT && m_Sub[1]->m_Type == TYPE_CONSTANT)
		{
			m_Type = TYPE_CONSTANT;
			Value &val_l = m_Sub[0]->m_Value;
			Value &val_n = m_Sub[1]->m_Value;
			int index = val_n.ConvertToInteger();
			if (val_l.GetType() == Value::TYPE_LIST)
			{
				if (index == -1)
					m_Value = (int)val_l.ReadList().size();
				else if (index >= 0 && index < (int)val_l.ReadList().size())
					m_Value = val_l.ReadList()[index];
				else
					NodeError("Cannot extract element from list with '#' on index: "+IntToStr(index)+" - index out of bounds", ERR_OPTIMIZE);
			}
			else if (val_l.GetType() == Value::TYPE_STRING)
			{
				if (index == -1)
					m_Value = (int)val_l.ReadString().size();
				else if (index >= 0 && index < (int)val_l.ReadString().size())
					m_Value = CharToStr(val_l.ReadString()[index]);
				else
					NodeError("Cannot extract element from string with '#' on index: "+IntToStr(index)+" - index out of bounds", ERR_OPTIMIZE);
			}
			else
				NodeError("Cannot extract element from "+val_l.GetTypeName()+" with '#' on index: "+IntToStr(index)+" - invalid type", ERR_OPTIMIZE);
			ClearSubnodes();
		}
	}
	else if (m_Ch == '$')
	{
		size_t i;
		for (i = 0; i < m_Sub.size(); i++)
			if (m_Sub[i]->m_Type != TYPE_CONSTANT)
				return;

		m_Type = TYPE_CONSTANT;
		m_Value = Value::TYPE_LIST;
		Value element;
		for (i = 0; i < m_Sub.size(); i++)
			m_Value.AccessList().push_back(m_Sub[i]->m_Value);

		ClearSubnodes();
	}
	else if (m_Ch == '&')
	{
		bool Const1 = (m_Sub[0]->m_Type == TYPE_CONSTANT);
		bool Const2 = (m_Sub[1]->m_Type == TYPE_CONSTANT);

		if (Const1 && Const2)
		{
			m_Type = TYPE_CONSTANT;
			m_Value = (int)(m_Sub[0]->m_Value.ConvertToBool() && m_Sub[1]->m_Value.ConvertToBool() ? 1 : 0);
			ClearSubnodes();
		}
		else if (Const1 && m_Sub[0]->m_Value.ConvertToBool() == false)
		{
			m_Type = TYPE_CONSTANT;
			m_Value = (int)0;
			ClearSubnodes();
		}
	}
	else if (m_Ch == '|')
	{
		bool Const1 = (m_Sub[0]->m_Type == TYPE_CONSTANT);
		bool Const2 = (m_Sub[1]->m_Type == TYPE_CONSTANT);

		if (Const1 && Const2)
		{
			m_Type = TYPE_CONSTANT;
			m_Value = (int)(m_Sub[0]->m_Value.ConvertToBool() || m_Sub[1]->m_Value.ConvertToBool() ? 1 : 0);
			ClearSubnodes();
		}
		else if (Const1 && m_Sub[0]->m_Value.ConvertToBool() == true)
		{
			m_Type = TYPE_CONSTANT;
			m_Value = (int)1;
			ClearSubnodes();
		}
	}
	else if (m_Ch == '=' || m_Ch == '>' || m_Ch == '<')
	{
		if (m_Sub[0]->m_Type == TYPE_CONSTANT && m_Sub[1]->m_Type == TYPE_CONSTANT)
		{
			m_Type = TYPE_CONSTANT;
			OpComparison(m_Ch, &m_Value, m_Sub[0]->m_Value, m_Sub[1]->m_Value);
			ClearSubnodes();
		}
	}
	else if (m_Ch == '[')
	{
		if (m_Sub[0]->m_Type == TYPE_CONSTANT)
		{
			m_Type = TYPE_CONSTANT;

			if (m_Sub[0]->m_Value.GetType() == Value::TYPE_LIST)
			{
				if (m_Sub[0]->m_Value.ReadList().empty())
					NodeError("Empty list for '['", ERR_OPTIMIZE);
				m_Value = m_Sub[0]->m_Value.ReadList().back();
			}
			else if (m_Sub[0]->m_Value.GetType() == Value::TYPE_STRING)
			{
				if (m_Sub[0]->m_Value.ReadString().empty())
					NodeError("Empty string for '['", ERR_OPTIMIZE);
				m_Value = CharToStr(m_Sub[0]->m_Value.ReadString()[m_Sub[0]->m_Value.ReadString().size()-1]);
			}
			else
				NodeError("Invalid l-value for '[' ("+m_Sub[0]->m_Value.GetTypeName()+") - list or string expected", ERR_OPTIMIZE);

			ClearSubnodes();
		}
	}
	else if (m_Ch == '?')
	{
		if (m_Sub[0]->m_Type == TYPE_CONSTANT && m_Sub[1]->m_Type == TYPE_CONSTANT && m_Sub[2]->m_Type == TYPE_CONSTANT)
		{
			m_Type = TYPE_CONSTANT;

			if (m_Sub[0]->m_Value.ConvertToBool())
				m_Value = m_Sub[1]->m_Value;
			else
				m_Value = m_Sub[2]->m_Value;

			ClearSubnodes();
		}
	}
}

//==============================================================================

Scope *g_GlobalScope;

void Intro()
{
	std::cerr << "Badly Developed SADOL Machine\n"
		"Version: 2.0, 21 Dec 2005\n"
		"\n"
		"Copyright (C) 2005 Adam Sawicki\n"
		"All rights reserved\n"
		"\n"
		"Implementation of the SADOL esoteric programming language interpreter.\n"
		"\n"
		"Usage:\n"
		"  BDSM <source-file>\n";
}

void Interpret(const string &Src)
{
//	std::cout << Src << std::endl;

	for (char ch = 'a'; ch < 'z'; ch++)
		g_GlobalFunctions[ch] = 0;

	Source src_obj(&Src);

	g_GlobalScope = new Scope(0, new Node);
	g_GlobalScope->ParseToEnd(src_obj);
	g_GlobalScope->Optimize();
	Value VOut;
	Value VArgs = Value(Value::TYPE_LIST);
	g_GlobalScope->Execute(&VOut, &VArgs);
	delete g_GlobalScope;
}

void Go(const char *FileName)
{
	// Open source file
	FILE *file = fopen(FileName, "rb");
	if (file == 0)
		Error("Cannot open file: "+string(FileName));

	// Read source into string
	char buf[BUF_SIZE];
	size_t count;
	string src;
	for (;;) {
		count = fread(buf, 1, BUF_SIZE, file);
		if (count > 0) {
			src.append(buf, count);
		}
		else break;
	}

	// Close source file
	fclose(file);

	// Interpret!
	Interpret(src);
}

int main(int argc, char **argv)
{
	if (argc == 2)
	{
		Go(argv[1]);
		return 0;
	}
	else {
		Intro();
		return -1;
	}
}

/* When error, every function can call Error.
*/