Mercurial > repo
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. +*/