diff interps/sadol/BDSM2.cpp @ 996:859f9b4339e6

<Gregor> tar xf egobot.tar.xz
author HackBot
date Sun, 09 Dec 2012 19:30:08 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/interps/sadol/BDSM2.cpp	Sun Dec 09 19:30:08 2012 +0000
@@ -0,0 +1,1991 @@
+/*
+ * 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.
+*/