tesses-webserver-cpp/tessesscriptengine.hpp

4233 lines
141 KiB
C++

#pragma once
#include "tesseswebserver.hpp"
#if defined(INCLUDED_FROM_TESSESWEBSERVER_H)
namespace ScriptEngine
#else
namespace Tesses::WebServer::ScriptEngine
#endif
{
class Instruction
{
public:
virtual int Length()=0;
virtual void Write(uint8_t* buffer)=0;
virtual ~Instruction()
{
}
const static uint8_t ADD = 0;
const static uint8_t SUB = 1;
const static uint8_t TIMES = 2;
const static uint8_t DIVIDE = 3;
const static uint8_t MOD = 4;
const static uint8_t LEFTSHIFT = 5;
const static uint8_t RIGHTSHIFT = 6;
const static uint8_t LESSTHAN = 7;
const static uint8_t GREATERTHAN = 8;
const static uint8_t LESSTHANEQUALTO = 9;
const static uint8_t GREATERTHANEQUALTO = 10;
const static uint8_t EQUALS = 11;
const static uint8_t NOTEQUALS = 12;
const static uint8_t BITWISEOR = 13;
const static uint8_t BITWISEAND = 14;
const static uint8_t BITWISENOT = 15;
const static uint8_t EXCLUSIVEOR = 16;
const static uint8_t LOGICALNOT = 17;
const static uint8_t NEGATIVE = 18;
const static uint8_t GETVARIABLE = 19;
const static uint8_t SETVARIABLE = 20;
const static uint8_t GETFIELD = 21;
const static uint8_t SETFIELD = 22;
const static uint8_t GETARRAY = 23;
const static uint8_t SETARRAY = 24;
const static uint8_t PUSH_STRING = 25;
const static uint8_t PUSH_CHAR = 26;
const static uint8_t PUSH_LONG = 27;
const static uint8_t PUSH_DOUBLE = 28;
const static uint8_t SET_LOOP = 29; //for scoping reasons
const static uint8_t GET_LOOP = 30;
const static uint8_t DESTROY_LOOP = 31;
const static uint8_t CALL_FUNC = 32;
const static uint8_t CALL_METHOD = 33;
const static uint8_t PRINT = 34;
const static uint8_t JMPC = 35;
const static uint8_t JMP = 36;
const static uint8_t POP = 37;
const static uint8_t RET = 38;
const static uint8_t SCOPE_BEGIN = 39;
const static uint8_t SCOPE_END = 40;
const static uint8_t GET_GLOBAL_VARIABLE = 41;
const static uint8_t SET_GLOBAL_VARIABLE = 42;
const static uint8_t GET_CALLER_VARIABLE = 43;
const static uint8_t SET_CALLER_VARIABLE = 44;
const static uint8_t DECLARE_VARIABLE = 45;
const static uint8_t PUSH_NULL = 46;
const static uint8_t PUSH_UNDEFINED = 47;
const static uint8_t PUSH_FALSE = 48;
const static uint8_t PUSH_TRUE = 49;
const static uint8_t ITTR = 50;
#if defined(DEBUG)
virtual void Print()
{
}
#endif
};
class Object {
public:
virtual std::string ToString()
{
return "";
}
virtual bool ToBool()
{
return false;
}
virtual bool CanDestroy()
{
return true;
}
virtual ~Object()
{
}
};
class Undefined {
};
class Null
{
};
class ObjectType {
public:
Object* data;
ObjectType(Object* o)
{
this->data = o;
}
};
typedef std::variant<std::string,ObjectType> IttrType;
class IttrKVP {
public:
IttrType ittr;
int64_t index;
};
typedef std::variant<Undefined,Null,ObjectType,IttrKVP,std::string,int64_t,double,bool,char> ScriptType;
class Ittr : public Object {
public:
IttrType ittr;
int64_t index;
};
inline bool Equals(ScriptType& left, ScriptType& right)
{
if(std::holds_alternative<std::string>(left) && std::holds_alternative<std::string>(right))
{
return std::get<std::string>(left) == std::get<std::string>(right);
}
if(std::holds_alternative<int64_t>(left) && std::holds_alternative<int64_t>(right))
{
return std::get<int64_t>(left) == std::get<int64_t>(right);
}
if(std::holds_alternative<char>(left) && std::holds_alternative<char>(right))
{
return std::get<char>(left) == std::get<char>(right);
}
if(std::holds_alternative<double>(left) && std::holds_alternative<double>(right))
{
return std::get<double>(left) == std::get<double>(right);
}
if(std::holds_alternative<int64_t>(left) && std::holds_alternative<double>(right))
{
return (double)std::get<int64_t>(left) == std::get<double>(right);
}
if(std::holds_alternative<double>(left) && std::holds_alternative<int64_t>(right))
{
return std::get<double>(left) == (double)std::get<int64_t>(right);
}
if(std::holds_alternative<bool>(left) && std::holds_alternative<bool>(right))
{
return std::get<bool>(left) == std::get<bool>(right);
}
if(std::holds_alternative<Undefined>(left) && std::holds_alternative<Undefined>(right))
{
return true;
}
if(std::holds_alternative<Null>(left) && std::holds_alternative<Null>(right))
{
return true;
}
if(std::holds_alternative<ObjectType>(left) && std::holds_alternative<ObjectType>(right))
{
auto objLeft = std::get<ObjectType>(left).data;
auto objRight = std::get<ObjectType>(right).data;
if(objLeft == objRight)
return true;
}
return false;
}
inline bool ConvertToBool(ScriptType& st)
{
if(std::holds_alternative<std::string>(st))
{
return std::get<std::string>(st).size() > 0;
}
if(std::holds_alternative<int64_t>(st))
{
return std::get<int64_t>(st) != 0;
}
if(std::holds_alternative<double>(st))
{
return std::get<double>(st) != 0;
}
if(std::holds_alternative<bool>(st))
{
return std::get<bool>(st);
}
if(std::holds_alternative<char>(st))
{
return std::get<char>(st) != 0;
}
if(std::holds_alternative<ObjectType>(st))
{
return std::get<ObjectType>(st).data->ToBool();
}
return false;
}
inline ScriptType IttrGetValue(ScriptType& st);
inline std::string IttrGetKey(ScriptType& st);
inline std::string ConvertToString(ScriptType& st);
inline std::string ConvertToString(ScriptType& st)
{
if(std::holds_alternative<IttrKVP>(st))
{
auto v = IttrGetValue(st);
return IttrGetKey(st) + ": " + ConvertToString(v);
}
if(std::holds_alternative<Null>(st))
{
return "null";
}
if(std::holds_alternative<std::string>(st))
{
return std::get<std::string>(st);
}
if(std::holds_alternative<int64_t>(st))
{
return std::to_string(std::get<int64_t>(st));
}
if(std::holds_alternative<double>(st))
{
return std::to_string(std::get<double>(st));
}
if(std::holds_alternative<bool>(st))
{
return std::get<bool>(st) ? "true" : "false";
}
if(std::holds_alternative<char>(st))
{
return std::string({std::get<char>(st)});
}
if(std::holds_alternative<ObjectType>(st))
{
return std::get<ObjectType>(st).data->ToString();
}
return "undefined";
}
class RootEnvironment;
class Environment {
public:
std::function<void(ScriptType)> print;
virtual bool HasObject(std::string key) = 0;
virtual bool HasObjectRecurse(std::string key) = 0;
virtual ScriptType GetValue(std::string key) = 0;
virtual void SetValue(std::string key,ScriptType val) = 0;
virtual Environment* GetRoot()=0;
virtual Environment* GetParent()=0;
virtual Environment* GetGlobal()
{
return this->GetRoot()->GetGlobal();
}
virtual void Declare(std::string key,ScriptType val)
{
SetValue(key,val);
}
Environment* GetSub();
virtual ~Environment()
{
}
};
class SubEnvironment : public Environment
{
Environment* parent;
public:
std::map<std::string,ScriptType> items;
SubEnvironment(Environment* env)
{
this->parent = env;
}
Environment* GetParent()
{
return parent;
}
Environment* GetRoot()
{
return parent->GetRoot();
}
bool HasObject(std::string key)
{
return items.count(key) > 0;
}
bool HasObjectRecurse(std::string key)
{
if(items.count(key) > 0) return true;
return this->parent->HasObjectRecurse(key);
}
ScriptType GetValue(std::string key)
{
if(HasObject(key))
{
return items[key];
}
return this->parent->GetValue(key);
}
void SetValue(std::string key,ScriptType val)
{
if(HasObject(key))
{
items[key] = val;
return;
}
if(this->parent->HasObjectRecurse(key))
{
this->parent->SetValue(key,val);
}
items[key] = val;
}
void Declare(std::string key,ScriptType val)
{
this->items[key] = val;
}
};
inline Environment* Environment::GetSub()
{
SubEnvironment* env = new SubEnvironment(this);
env->print = this->print;
return env;
}
class CallableObject : public Object
{
public:
bool useGlobalEnv=false;
virtual bool FirstArgumentIsThis()
{
return false;
}
bool CanDestroy()
{
return false;
}
virtual ScriptType Execute(Environment* callerEnv,std::vector<ScriptType> args)
{
return Null();
}
ScriptType Execute(Environment* callerEnv,std::initializer_list<ScriptType> args)
{
return Execute(callerEnv,std::vector<ScriptType>(args));
}
std::string ToString()
{
return "";
}
bool ToBool()
{
return true;
}
};
class ExternalFunction : public CallableObject {
public:
std::function<ScriptType(Environment*,std::vector<ScriptType>)> callback;
bool firstArgIsThis;
bool FirstArgumentIsThis()
{
return firstArgIsThis;
}
ExternalFunction(std::function<ScriptType(Environment*,std::vector<ScriptType>)> callback,bool firstArgIsThis=false)
{
this->callback = callback;
this->firstArgIsThis = firstArgIsThis;
}
ScriptType Execute(Environment* callerEnv,std::vector<ScriptType> args)
{
return callback(callerEnv,args);
}
~ExternalFunction()
{
}
};
class InternalFunction;
class List : public Object {
public:
std::vector<ScriptType> items;
std::string ToString()
{
return "";
}
bool ToBool()
{
return items.size() > 0;
}
};
class Dictionary : public Object {
public:
std::vector<std::pair<std::string,ScriptType>> items;
std::string ToString()
{
return "";
}
bool ToBool()
{
return items.size() > 0;
}
bool HasValue(std::string key)
{
for(auto item : items)
if(item.first == key) return true;
return false;
}
ScriptType GetValue(std::string key)
{
for(auto item : items)
if(item.first == key) return item.second;
return Undefined();
}
void SetValue(std::string key, ScriptType& value)
{
if(std::holds_alternative<Undefined>(value))
{
for(auto item = items.cbegin();item <= items.cend();item++)
{
auto myItem = *item;
if(myItem.first == key)
{
items.erase(item);
return;
}
}
}
else
{
for(auto item : items)
{
if(item.first == key)
{
item.second = value;
return;
}
}
items.push_back(std::pair<std::string,ScriptType>(key,value));
}
}
};
inline ScriptType CreateIttrFor(ScriptType& st)
{
if(std::holds_alternative<std::string>(st))
{
Ittr* ittr =new Ittr();
ittr->index = -1;
ittr->ittr = std::get<std::string>(st);
ObjectType t(ittr);
return t;
}
if(std::holds_alternative<ObjectType>(st))
{
auto ot = std::get<ObjectType>(st).data;
auto ls = dynamic_cast<List*>(ot);
auto dict = dynamic_cast<Dictionary*>(ot);
if(ls != nullptr)
{
Ittr* ittr=new Ittr();
ittr->index = -1;
ittr->ittr = ObjectType(ls);
ObjectType t(ittr);
return t;
}
if(dict != nullptr)
{
Ittr* ittr=new Ittr();
ittr->index = -1;
ittr->ittr = ObjectType(dict);
ObjectType t(ittr);
return t;
}
}
return Null();
}
inline bool IttrMoveNext(ScriptType& st)
{
if(std::holds_alternative<ObjectType>(st))
{
auto objIttr = std::get<ObjectType>(st).data;
auto ittr = dynamic_cast<Ittr*>(objIttr);
if(ittr == nullptr) return false;
if(std::holds_alternative<std::string>(ittr->ittr))
{
std::string str = std::get<std::string>(ittr->ittr);
if(ittr->index >= -1 && ittr->index + 1 < str.size())
{
(ittr->index)++;
return true;
}
}
if(std::holds_alternative<ObjectType>(ittr->ittr))
{
auto ot = std::get<ObjectType>(ittr->ittr).data;
auto ls = dynamic_cast<List*>(ot);
auto dict = dynamic_cast<Dictionary*>(ot);
if(ls != nullptr)
{
if(ittr->index >= -1 && ittr->index + 1 < ls->items.size())
{
(ittr->index)++;
return true;
}
}
if(dict != nullptr)
{
if(ittr->index >= -1 && ittr->index + 1 < dict->items.size())
{
(ittr->index)++;
return true;
}
}
}
}
return false;
}
inline ScriptType IttrCurrent(ScriptType& st)
{
if(std::holds_alternative<ObjectType>(st))
{
auto objIttr = std::get<ObjectType>(st).data;
auto ittr = dynamic_cast<Ittr*>(objIttr);
if(ittr == nullptr) return Null();
if(std::holds_alternative<std::string>(ittr->ittr))
{
std::string str = std::get<std::string>(ittr->ittr);
if(ittr->index >= 0 && ittr->index < str.size())
{
return str[ittr->index];
}
}
if(std::holds_alternative<ObjectType>(ittr->ittr))
{
auto ot = std::get<ObjectType>(ittr->ittr).data;
auto ls = dynamic_cast<List*>(ot);
auto dict = dynamic_cast<Dictionary*>(ot);
if(ls != nullptr)
{
if(ittr->index >= 0 && ittr->index < ls->items.size())
{
return ls->items[ittr->index];
}
}
if(dict != nullptr)
{
if(ittr->index >= 0 && ittr->index < dict->items.size())
{
IttrKVP kvp;
kvp.index = ittr->index;
kvp.ittr = ittr->ittr;
return kvp;
}
}
}
}
return Null();
}
inline std::string IttrGetKey(ScriptType& st)
{
if(std::holds_alternative<IttrKVP>(st))
{
auto ittr = std::get<IttrKVP>(st);
if(std::holds_alternative<ObjectType>(ittr.ittr))
{
auto ot = std::get<ObjectType>(ittr.ittr).data;
auto dict = dynamic_cast<Dictionary*>(ot);
if(dict != nullptr)
{
if(ittr.index >= 0 && ittr.index < dict->items.size())
{
return dict->items[ittr.index].first;
}
}
}
}
return std::string({});
}
inline ScriptType IttrGetValue(ScriptType& st)
{
if(std::holds_alternative<IttrKVP>(st))
{
auto ittr = std::get<IttrKVP>(st);
if(std::holds_alternative<ObjectType>(ittr.ittr))
{
auto ot = std::get<ObjectType>(ittr.ittr).data;
auto dict = dynamic_cast<Dictionary*>(ot);
if(dict != nullptr)
{
if(ittr.index >= 0 && ittr.index < dict->items.size())
{
return dict->items[ittr.index].second;
}
}
}
}
return Null();
}
inline ScriptType TypeOf(Environment* env, std::vector<ScriptType> args);
class RootEnvironment : public Environment
{
std::vector<CallableObject*> fns;
public:
RootEnvironment()
{
DeclareFunctions();
}
Environment* global=nullptr;
std::map<std::string,ScriptType> items;
Environment* GetRoot()
{
return this;
}
Environment* GetGlobal()
{
if(global != nullptr) return global;
return this;
}
Environment* GetParent()
{
return this;
}
bool HasObject(std::string key)
{
return items.count(key) > 0;
}
bool HasObjectRecurse(std::string key)
{
return items.count(key) > 0;
}
ScriptType GetValue(std::string key)
{
return items[key];
}
void SetValue(std::string key,ScriptType val)
{
items[key] = val;
}
void Free(ScriptType t,bool tree=false)
{
if(std::holds_alternative<ObjectType>(t))
{
auto myObj=std::get<ObjectType>(t);
if(tree)
{
auto ls = dynamic_cast<List*>(myObj.data);
auto dict = dynamic_cast<Dictionary*>(myObj.data);
if(ls != nullptr)
{
for(auto item : ls->items)
Free(item,true);
}
if(dict != nullptr)
{
for(auto item : dict->items)
Free(item.second,true);
}
}
if(myObj.data != nullptr && myObj.data->CanDestroy())
delete myObj.data;
}
}
void DeclareFunctions()
{
DeclareFunction("dictionary",[this](Environment* env, std::vector<ScriptType> args)-> ScriptType {
Dictionary* dict=new Dictionary();
ObjectType ot(dict);
return ot;
});
DeclareFunction("list",[this](Environment* env, std::vector<ScriptType> args)-> ScriptType {
List* list=new List();
ObjectType ot(list);
return ot;
});
DeclareFunction("typeof",TypeOf);
DeclareFunction("free",[this](Environment* env, std::vector<ScriptType> args)-> ScriptType {
if(args.size() > 0)
{
Free(args[0]);
}
return Null();
});
DeclareFunction("free_tree",[this](Environment* env, std::vector<ScriptType> args)-> ScriptType {
if(args.size() > 0)
{
Free(args[0],true);
}
return Null();
});
}
void DeclareFunction(std::string name,std::function<ScriptType(Environment* env, std::vector<ScriptType>)> method)
{
ExternalFunction* item = new ExternalFunction(method);
fns.push_back(item);
ObjectType ot(item);
SetValue(name,ot);
}
~RootEnvironment()
{
for(auto item : fns)
delete item;
}
};
class ChunkExecuter
{
uint8_t* bytecode;
size_t bytecodeLength;
std::vector<std::string>* args;
size_t ip=0;
std::stack<ScriptType> stack;
Environment* env;
public:
ChunkExecuter(Environment* env,uint8_t* bytecode,size_t bytecodeLength,std::vector<std::string>* args)
{
this->bytecode = bytecode;
this->bytecodeLength = bytecodeLength;
this->args = args;
this->env = env;
}
ScriptType Pop()
{
if(stack.empty()) throw std::exception();
ScriptType t = stack.top();
stack.pop();
return t;
}
template<typename T>
T PopSpecific()
{
auto p = Pop();
if(std::holds_alternative<T>(p))
{
return std::get<T>(p);
}
else
{
std::vector<ScriptType> args;
args.push_back(p);
auto v = TypeOf(env,args);
this->env->print("IP: ");
this->env->print((int64_t)ip);
this->env->print("Unexpected type: ");
this->env->print(v);
this->env->print(" with value ");
this->env->print(p);
this->env->print("\n");
throw std::exception();
}
}
int64_t PopLong()
{
auto val = PopSpecific<int64_t>();
return val;
}
std::string PopString()
{
auto val = PopSpecific<std::string>();
return val;
}
ScriptType Mod(ScriptType& left,ScriptType& right)
{
if(std::holds_alternative<int64_t>(left) && std::holds_alternative<int64_t>(right))
{
return std::get<int64_t>(left) % std::get<int64_t>(right);
}
if(std::holds_alternative<double>(left) && std::holds_alternative<int64_t>(right))
{
return fmod(std::get<double>(left), (double)std::get<int64_t>(right));
}
if(std::holds_alternative<int64_t>(left) && std::holds_alternative<double>(right))
{
return fmod((double)std::get<int64_t>(left), std::get<double>(right));
}
if(std::holds_alternative<double>(left) && std::holds_alternative<double>(right))
{
return fmod(std::get<double>(left),std::get<double>(right));
}
return Null();
}
ScriptType Divide(ScriptType& left,ScriptType& right)
{
if(std::holds_alternative<int64_t>(left) && std::holds_alternative<int64_t>(right))
{
return std::get<int64_t>(left) / std::get<int64_t>(right);
}
if(std::holds_alternative<double>(left) && std::holds_alternative<int64_t>(right))
{
return std::get<double>(left) / (double)std::get<int64_t>(right);
}
if(std::holds_alternative<int64_t>(left) && std::holds_alternative<double>(right))
{
return (double)std::get<int64_t>(left) / std::get<double>(right);
}
if(std::holds_alternative<double>(left) && std::holds_alternative<double>(right))
{
return std::get<double>(left) / std::get<double>(right);
}
return Null();
}
ScriptType Times(ScriptType& left,ScriptType& right)
{
if(std::holds_alternative<int64_t>(left) && std::holds_alternative<int64_t>(right))
{
return std::get<int64_t>(left) * std::get<int64_t>(right);
}
if(std::holds_alternative<double>(left) && std::holds_alternative<int64_t>(right))
{
return std::get<double>(left) * (double)std::get<int64_t>(right);
}
if(std::holds_alternative<int64_t>(left) && std::holds_alternative<double>(right))
{
return (double)std::get<int64_t>(left) * std::get<double>(right);
}
if(std::holds_alternative<double>(left) && std::holds_alternative<double>(right))
{
return std::get<double>(left) * std::get<double>(right);
}
return Null();
}
ScriptType GreaterThan(ScriptType& left,ScriptType& right)
{
if(std::holds_alternative<int64_t>(left) && std::holds_alternative<int64_t>(right))
{
return std::get<int64_t>(left) > std::get<int64_t>(right);
}
if(std::holds_alternative<double>(left) && std::holds_alternative<int64_t>(right))
{
return std::get<double>(left) > (double)std::get<int64_t>(right);
}
if(std::holds_alternative<int64_t>(left) && std::holds_alternative<double>(right))
{
return (double)std::get<int64_t>(left) > std::get<double>(right);
}
if(std::holds_alternative<double>(left) && std::holds_alternative<double>(right))
{
return std::get<double>(left) > std::get<double>(right);
}
return Null();
}
ScriptType LessThan(ScriptType& left,ScriptType& right)
{
if(std::holds_alternative<int64_t>(left) && std::holds_alternative<int64_t>(right))
{
return std::get<int64_t>(left) < std::get<int64_t>(right);
}
if(std::holds_alternative<double>(left) && std::holds_alternative<int64_t>(right))
{
return std::get<double>(left) < (double)std::get<int64_t>(right);
}
if(std::holds_alternative<int64_t>(left) && std::holds_alternative<double>(right))
{
return (double)std::get<int64_t>(left) < std::get<double>(right);
}
if(std::holds_alternative<double>(left) && std::holds_alternative<double>(right))
{
return std::get<double>(left) < std::get<double>(right);
}
return Null();
}
ScriptType GreaterThanEqualsTo(ScriptType& left,ScriptType& right)
{
if(std::holds_alternative<int64_t>(left) && std::holds_alternative<int64_t>(right))
{
return std::get<int64_t>(left) >= std::get<int64_t>(right);
}
if(std::holds_alternative<double>(left) && std::holds_alternative<int64_t>(right))
{
return std::get<double>(left) >= (double)std::get<int64_t>(right);
}
if(std::holds_alternative<int64_t>(left) && std::holds_alternative<double>(right))
{
return (double)std::get<int64_t>(left) >= std::get<double>(right);
}
if(std::holds_alternative<double>(left) && std::holds_alternative<double>(right))
{
return std::get<double>(left) >= std::get<double>(right);
}
return Null();
}
ScriptType LessThanEqualsTo(ScriptType& left,ScriptType& right)
{
if(std::holds_alternative<int64_t>(left) && std::holds_alternative<int64_t>(right))
{
return std::get<int64_t>(left) <= std::get<int64_t>(right);
}
if(std::holds_alternative<double>(left) && std::holds_alternative<int64_t>(right))
{
return std::get<double>(left) <= (double)std::get<int64_t>(right);
}
if(std::holds_alternative<int64_t>(left) && std::holds_alternative<double>(right))
{
return (double)std::get<int64_t>(left) <= std::get<double>(right);
}
if(std::holds_alternative<double>(left) && std::holds_alternative<double>(right))
{
return std::get<double>(left) <= std::get<double>(right);
}
return Null();
}
ScriptType Sub(ScriptType& left,ScriptType& right)
{
if(std::holds_alternative<int64_t>(left) && std::holds_alternative<int64_t>(right))
{
return std::get<int64_t>(left) - std::get<int64_t>(right);
}
if(std::holds_alternative<double>(left) && std::holds_alternative<int64_t>(right))
{
return std::get<double>(left) - (double)std::get<int64_t>(right);
}
if(std::holds_alternative<int64_t>(left) && std::holds_alternative<double>(right))
{
return (double)std::get<int64_t>(left) - std::get<double>(right);
}
if(std::holds_alternative<double>(left) && std::holds_alternative<double>(right))
{
return std::get<double>(left) - std::get<double>(right);
}
return Null();
}
ScriptType Add(ScriptType& left,ScriptType& right)
{
if(std::holds_alternative<int64_t>(left) && std::holds_alternative<int64_t>(right))
{
return std::get<int64_t>(left) + std::get<int64_t>(right);
}
if(std::holds_alternative<double>(left) && std::holds_alternative<int64_t>(right))
{
return std::get<double>(left) + (double)std::get<int64_t>(right);
}
if(std::holds_alternative<int64_t>(left) && std::holds_alternative<double>(right))
{
return (double)std::get<int64_t>(left) + std::get<double>(right);
}
if(std::holds_alternative<double>(left) && std::holds_alternative<double>(right))
{
return std::get<double>(left) + std::get<double>(right);
}
if(std::holds_alternative<std::string>(left) && std::holds_alternative<std::string>(right))
{
return std::get<std::string>(left) + std::get<std::string>(right);
}
if(std::holds_alternative<int64_t>(left) && std::holds_alternative<std::string>(right))
{
return std::to_string(std::get<int64_t>(left)) + std::get<std::string>(right);
}
if(std::holds_alternative<std::string>(left) && std::holds_alternative<int64_t>(right))
{
return std::get<std::string>(left) + std::to_string(std::get<int64_t>(right));
}
if(std::holds_alternative<double>(left) && std::holds_alternative<std::string>(right))
{
return std::to_string(std::get<double>(left)) + std::get<std::string>(right);
}
if(std::holds_alternative<std::string>(left) && std::holds_alternative<double>(right))
{
return std::get<std::string>(left) + std::to_string(std::get<double>(right));
}
if(std::holds_alternative<std::string>(left) && std::holds_alternative<char>(right))
{
return std::get<std::string>(left) + std::string({std::get<char>(right)});
}
if(std::holds_alternative<char>(left) && std::holds_alternative<std::string>(right))
{
return std::string({std::get<char>(left)}) + std::get<std::string>(right);
}
return Null();
}
ScriptType CallMethod(ScriptType& member, std::string key, std::vector<ScriptType>& args)
{
if(std::holds_alternative<Undefined>(member))
{
if(key == "toString")
{
return "undefined";
}
}
if(std::holds_alternative<Null>(member))
{
if(key == "toString")
{
return "null";
}
}
if(std::holds_alternative<ObjectType>(member))
{
auto obj = std::get<ObjectType>(member).data;
auto ls = dynamic_cast<List*>(obj);
auto dict = dynamic_cast<Dictionary*>(obj);
auto ittr = dynamic_cast<Ittr*>(obj);
if(ittr != nullptr)
{
if(key == "current")
{
return IttrCurrent(member);
}
if(key == "movenext")
{
return IttrMoveNext(member);
}
}
if(ls != nullptr)
{
if(key == "add" && args.size() > 0)
{
ls->items.push_back(args[0]);
}
else if(key == "remove" && args.size() > 0)
{
for(auto ittr = ls->items.cbegin();ittr<ls->items.cend();ittr++)
{
auto item = *ittr;
if(Equals(item,args[0]))
{
ls->items.erase(ittr);
break;
}
}
}
else if(key == "indexof" && args.size() > 0)
{
int64_t index = -1;
for(auto ittr = ls->items.cbegin();ittr<ls->items.cend();ittr++)
{
auto item = *ittr;
if(Equals(item,args[0]))
{
break;
}
index++;
}
return index;
}
else if(key == "insert" && args.size() > 1)
{
auto index = args[0];
if(std::holds_alternative<int64_t>(index))
{
auto myIndex=std::get<int64_t>(index);
if(myIndex >= 0 && myIndex <= ls->items.size())
{
ls->items.insert(ls->items.cbegin() + myIndex,{args[1]});
}
}
}
else if(key == "removeat" && args.size() > 0)
{
auto index = args[0];
if(std::holds_alternative<int64_t>(index))
{
auto myIndex=std::get<int64_t>(index);
if(myIndex >= 0 && myIndex < ls->items.size())
{
ls->items.erase(ls->items.cbegin() + myIndex);
}
}
}
else if(key == "count" || key == "length")
{
return (int64_t)ls->items.size();
}
else if(key == "clear") {
ls->items.clear();
}
else if(key == "at" && args.size() > 0)
{
auto index = args[0];
if(std::holds_alternative<int64_t>(index))
{
auto myIndex=std::get<int64_t>(index);
if(myIndex >= 0 && myIndex < ls->items.size())
{
return ls->items.at((size_t)myIndex);
}
}
}
else if(key == "set" && args.size() > 1)
{
auto index = args[0];
if(std::holds_alternative<int64_t>(index))
{
auto myIndex=std::get<int64_t>(index);
if(myIndex >= 0 && myIndex < ls->items.size())
{
return ls->items[(size_t)myIndex]=args[1];
}
}
}
}
if(dict != nullptr)
{
auto item = dict->GetValue(key);
if(std::holds_alternative<ObjectType>(item))
{
auto fnt = std::get<ObjectType>(item).data;
auto fn = dynamic_cast<CallableObject*>(fnt);
if(fn != nullptr)
{
if(fn->FirstArgumentIsThis())
args.insert(args.cbegin(),{member});
return fn->Execute(this->env,args);
}
}
if(key == "toString" && args.size() == 0)
{
return std::string({});
}
}
}
if(std::holds_alternative<int64_t>(member))
{
if(key == "toString")
{
return std::to_string(std::get<int64_t>(member));
}
if(key == "toDouble")
{
return (double)std::get<int64_t>(member);
}
if(key == "toDoubleBits")
{
return std::bit_cast<double>(std::get<int64_t>(member));
}
if(key == "toChar")
{
return (char)std::get<int64_t>(member);
}
}
if(std::holds_alternative<std::string>(member))
{
std::string myStr=std::get<std::string>(member);
if(key == "toString")
{
return myStr;
}
if(key == "length" || key == "count")
{
return (int64_t)myStr.size();
}
if(key == "toLower")
{
std::transform(myStr.begin(),myStr.end(),myStr.begin(),::tolower);
return myStr;
}
if(key == "toUpper")
{
std::transform(myStr.begin(),myStr.end(),myStr.begin(),::toupper);
return myStr;
}
}
if(std::holds_alternative<char>(member))
{
if(key == "toString")
{
return std::string({std::get<char>(member)});
}
if(key == "toLong")
{
return (int64_t)std::get<char>(member);
}
if(key == "toLower")
{
return (char)std::tolower(std::get<char>(member));
}
if(key == "toUpper")
{
return (char)std::toupper(std::get<char>(member));
}
}
if(std::holds_alternative<double>(member))
{
if(key == "toString")
{
return std::to_string(std::get<double>(member));
}
if(key == "toLong")
{
return (int64_t)std::get<double>(member);
}
if(key == "toLongBits")
{
return std::bit_cast<int64_t>(std::get<double>(member));
}
}
return Undefined();
}
ScriptType SetField(ScriptType& instance,std::string name,ScriptType& value)
{
if(std::holds_alternative<ObjectType>(instance))
{
auto inst = std::get<ObjectType>(instance).data;
auto dict = dynamic_cast<Dictionary*>(inst);
if(dict != nullptr)
{
auto myFuncObj = dict->GetValue(std::string("set") + name);
if(std::holds_alternative<ObjectType>(myFuncObj))
{
auto myFuncObj2 = std::get<ObjectType>(myFuncObj).data;
auto callbl = dynamic_cast<CallableObject*>(myFuncObj2);
if(callbl != nullptr)
{
if(callbl->FirstArgumentIsThis())
{
return callbl->Execute(env,{instance,value});
}
else
{
return callbl->Execute(env,{value});
}
}
}
dict->SetValue(name,value);
}
}
return value;
}
ScriptType GetField(ScriptType& instance, std::string name)
{
if(std::holds_alternative<std::string>(instance))
{
auto str = std::get<std::string>(instance);
if(name == "length" || name == "count")
{
return (int64_t)str.size();
}
}
if(std::holds_alternative<IttrKVP>(instance))
{
if(name == "key")
{
return IttrGetKey(instance);
}
if(name == "value")
{
return IttrGetValue(instance);
}
}
if(std::holds_alternative<ObjectType>(instance))
{
auto inst = std::get<ObjectType>(instance).data;
auto ls = dynamic_cast<List*>(inst);
auto dict = dynamic_cast<Dictionary*>(inst);
if(ls != nullptr)
{
if(name == "length" || name == "count")
{
return (int64_t)ls->items.size();
}
}
if(dict != nullptr)
{
auto myFuncObj = dict->GetValue(std::string("get") + name);
if(std::holds_alternative<ObjectType>(myFuncObj))
{
auto myFuncObj2 = std::get<ObjectType>(myFuncObj).data;
auto callbl = dynamic_cast<CallableObject*>(myFuncObj2);
if(callbl != nullptr)
{
if(callbl->FirstArgumentIsThis())
{
return callbl->Execute(env,{instance});
}
else
{
return callbl->Execute(env,{});
}
}
}
return dict->GetValue(name);
}
}
return Undefined();
}
ScriptType Execute(Environment* callerEnv,std::vector<ScriptType> __args)
{
// std::cout << __args.size() << " " << this->args->size() << std::endl;
if(__args.size() != this->args->size())
{
callerEnv->print("Expected " + std::to_string((int64_t)this->args->size()) + " args, got " + std::to_string((int64_t)__args.size()) + " args.\n");
throw std::exception();
}
for(size_t i = 0;i<__args.size();i++)
{
env->Declare(this->args->at(i),__args[i]);
}
int scopes=0;
std::stack<int> loops;
while(ip < bytecodeLength)
{
uint8_t instruction = bytecode[ip];
ip++;
switch(instruction)
{
case Instruction::PUSH_FALSE:
{
stack.push(false);
}
break;
case Instruction::PUSH_TRUE:
{
stack.push(true);
}
break;
case Instruction::PUSH_NULL:
{
stack.push(Null());
}
break;
case Instruction::PUSH_UNDEFINED:
{
stack.push(Undefined());
}
break;
case Instruction::PUSH_CHAR:
{
if(ip + 1 <= bytecodeLength)
{
stack.push((char)bytecode[ip++]);
}
else
{
throw std::exception();
}
}
break;
case Instruction::CALL_FUNC:
{
int64_t lng=PopLong();
std::vector<ScriptType> _args;
for(int i = 0;i<lng;i++)
{
_args.insert(_args.cbegin(),{Pop()});
}
auto fCall = Pop();
auto myF=dynamic_cast<CallableObject*>(std::get<ObjectType>(fCall).data);
if(myF != nullptr)
{
stack.push(myF->Execute(env,_args));
}
else
{
stack.push(Null());
}
}
break;
case Instruction::CALL_METHOD:
{
int64_t lng=PopLong();
std::vector<ScriptType> _args;
for(int i = 0;i<lng;i++)
{
_args.insert(_args.cbegin(),{Pop()});
}
std::string key = PopString();
auto member = Pop();
stack.push(CallMethod(member,key,_args));
}
break;
case Instruction::EQUALS:
{
auto right = Pop();
auto left = Pop();
stack.push(Equals(left,right));
}
break;
case Instruction::NOTEQUALS:
{
auto right = Pop();
auto left = Pop();
bool v = !Equals(left,right);
stack.push(v);
}
break;
case Instruction::LESSTHAN:
{
auto right = Pop();
auto left = Pop();
stack.push(LessThan(left,right));
}
break;
case Instruction::GREATERTHAN:
{
auto right = Pop();
auto left = Pop();
stack.push(GreaterThan(left,right));
}
break;
case Instruction::LESSTHANEQUALTO:
{
auto right = Pop();
auto left = Pop();
stack.push(LessThanEqualsTo(left,right));
}
break;
case Instruction::GREATERTHANEQUALTO:
{
auto right = Pop();
auto left = Pop();
stack.push(GreaterThanEqualsTo(left,right));
}
break;
case Instruction::ADD:
{
auto right = Pop();
auto left = Pop();
stack.push(Add(left,right));
}
break;
case Instruction::SUB:
{
auto right = Pop();
auto left = Pop();
stack.push(Sub(left,right));
}
break;
case Instruction::TIMES:
{
auto right = Pop();
auto left = Pop();
stack.push(Times(left,right));
}
break;
case Instruction::DIVIDE:
{
auto right = Pop();
auto left = Pop();
stack.push(Divide(left,right));
}
break;
case Instruction::MOD:
{
auto right = Pop();
auto left = Pop();
stack.push(Mod(left,right));
}
break;
case Instruction::PUSH_LONG:
{
if(ip + 8 <= bytecodeLength)
{
uint64_t value = ((uint64_t)bytecode[ip++] << 56);
value |= ((uint64_t)bytecode[ip++] << 48);
value |= ((uint64_t)bytecode[ip++] << 40);
value |= ((uint64_t)bytecode[ip++] << 32);
value |= ((uint64_t)bytecode[ip++] << 24);
value |= ((uint64_t)bytecode[ip++] << 16);
value |= ((uint64_t)bytecode[ip++] << 8);
value |= (uint64_t)bytecode[ip++];
stack.push((int64_t)value);
}
}
break;
case Instruction::PUSH_DOUBLE:
{
if(ip + 8 <= bytecodeLength)
{
uint64_t value = ((uint64_t)bytecode[ip++] << 56);
value |= ((uint64_t)bytecode[ip++] << 48);
value |= ((uint64_t)bytecode[ip++] << 40);
value |= ((uint64_t)bytecode[ip++] << 32);
value |= ((uint64_t)bytecode[ip++] << 24);
value |= ((uint64_t)bytecode[ip++] << 16);
value |= ((uint64_t)bytecode[ip++] << 8);
value |= (uint64_t)bytecode[ip++];
stack.push(*(double*)&value);
}
}
break;
case Instruction::PUSH_STRING:
{
if(ip + 4 <= bytecodeLength)
{
size_t len = ((size_t)bytecode[ip++] << 24);
len |= ((size_t)bytecode[ip++] << 16);
len |= ((size_t)bytecode[ip++] << 8);
len |= (size_t)bytecode[ip++];;
if(ip + len <= bytecodeLength)
{
std::string str((char*)bytecode+ip,len);
ip+=len;
stack.push(str);
}
}
}
break;
case Instruction::GET_CALLER_VARIABLE:
{
auto str = PopString();
auto v=callerEnv->GetValue(str);
stack.push(v);
}
break;
case Instruction::SET_CALLER_VARIABLE:
{
auto v = Pop();
auto str = PopString();
callerEnv->SetValue(str,v);
stack.push(v);
}
break;
case Instruction::GET_GLOBAL_VARIABLE:
{
auto str = PopString();
auto v = env->GetGlobal()->GetValue(str);
stack.push(v);
}
break;
case Instruction::SET_GLOBAL_VARIABLE:
{
auto v = Pop();
auto str = PopString();
env->GetGlobal()->SetValue(str,v);
stack.push(v);
}
break;
case Instruction::GETVARIABLE:
{
auto str = PopString();
auto v = env->GetValue(str);
stack.push(v);
}
break;
case Instruction::SETVARIABLE:
{
auto v = Pop();
auto str = PopString();
env->SetValue(str,v);
stack.push(v);
}
break;
case Instruction::DECLARE_VARIABLE:
{
auto v = Pop();
auto str = PopString();
env->Declare(str,v);
stack.push(v);
}
break;
case Instruction::PRINT:
{
auto v = Pop();
callerEnv->print(v);
}
break;
case Instruction::SCOPE_BEGIN:
{
scopes++;
env = env->GetSub();
}
break;
case Instruction::SCOPE_END:
{
Environment* env2 = env;
env=env2->GetParent();
if(env != env2)
{
delete env2;
}
scopes--;
}
break;
case Instruction::SET_LOOP:
{
loops.push(scopes);
}
break;
case Instruction::GET_LOOP:
{
int myLoops = loops.top();
while(scopes > myLoops)
{
Environment* env2 = env;
env=env2->GetParent();
if(env != env2)
{
delete env2;
}
scopes--;
}
}
break;
case Instruction::DESTROY_LOOP:
{
int myLoops = loops.top();
loops.pop();
while(scopes > myLoops)
{
Environment* env2 = env;
env=env2->GetParent();
if(env != env2)
{
delete env2;
}
scopes--;
}
}
break;
case Instruction::RET:
{
auto val = Pop();
while(scopes > 0)
{
Environment* env2 = env;
env=env2->GetParent();
if(env != env2)
{
delete env2;
}
scopes--;
}
return val;
}
break;
case Instruction::JMP:
{
if(ip + 4 <= bytecodeLength)
{
size_t len = ((size_t)bytecode[ip++] << 24);
len |= ((size_t)bytecode[ip++] << 16);
len |= ((size_t)bytecode[ip++] << 8);
len |= (size_t)bytecode[ip++];
if(len < bytecodeLength)
ip = len;
else
{
std::cout << "Jump failed" << std::endl;
throw std::exception();
}
}
}
break;
case Instruction::JMPC:
{
if(ip + 4 <= bytecodeLength)
{
size_t len = ((size_t)bytecode[ip++] << 24);
len |= ((size_t)bytecode[ip++] << 16);
len |= ((size_t)bytecode[ip++] << 8);
len |= (size_t)bytecode[ip++];
if(len < bytecodeLength)
{
auto v = Pop();
if(ConvertToBool(v))
ip = len;
}
else
{
std::cout << "Jump failed" << std::endl;
throw std::exception();
}
}
}
break;
case Instruction::ITTR:
{
auto ittr=Pop();
stack.push(CreateIttrFor(ittr));
}
break;
case Instruction::GETFIELD:
{
std::string name = PopString();
auto instance = Pop();
stack.push(GetField(instance,name));
}
break;
case Instruction::SETFIELD:
{
auto val = Pop();
std::string name = PopString();
auto instance = Pop();
stack.push(SetField(instance,name,val));
}
break;
}
}
while(scopes > 0)
{
Environment* env2 = env;
env=env2->GetParent();
if(env != env2)
{
delete env2;
}
scopes--;
}
return Null();
}
};
class InternalFunction : public CallableObject
{
public:
bool isRoot=false;
uint8_t* bytecode;
size_t bytecodeLength;
std::vector<std::string> args;
Environment* env;
bool FirstArgumentIsThis()
{
return args.size() > 0 && args[0] == "this";
}
ScriptType Execute(Environment* callerEnv,std::vector<ScriptType> args)
{
Environment* env2 = isRoot ? env : env->GetSub();
ChunkExecuter exec(env2,bytecode,bytecodeLength,&this->args);
auto ret=exec.Execute(callerEnv,args);
if(!isRoot) delete env2;
return ret;
}
};
class LexToken {
public:
std::string text;
bool isString;
bool isChar;
LexToken(std::string text)
{
this->text = text;
this->isString = false;
this->isChar = false;
}
LexToken(std::string text,bool isString)
{
this->text = text;
this->isString = isString;
this->isChar = !isChar;
}
LexToken(const LexToken& tkn)
{
this->text = tkn.text;
this->isString = tkn.isString;
this->isChar = tkn.isChar;
}
};
class Node {
public:
virtual bool IsExpression() {
return false;
}
virtual ~Node()
{
}
};
class Expression : public Node {
public:
bool IsExpression()
{
return true;
}
};
class OneExpression : public Expression {
public:
Node* node;
OneExpression(Node* node)
{
this->node = node;
}
};
class TwoExpression : public Expression {
public:
Node* left;
Node* right;
TwoExpression(Node* left,Node* right)
{
this->left = left;
this->right = right;
}
};
#define LIST_OF_CONST_EXPRESSIONS \
NODE(bool,Bool) \
NODE(std::string,String) \
NODE(int64_t,Long) \
NODE(double,Double) \
NODE(char,Char)
#define LIST_OF_ONE_EXPRESSIONS \
NODE(PostfixIncrement) \
NODE(PostfixDecrement) \
NODE(PrefixIncrement) \
NODE(PrefixDecrement) \
NODE(BitwiseNot) \
NODE(LogicalNot) \
NODE(Negative) \
NODE(CompoundAssign) \
NODE(Global) \
NODE(Caller) \
NODE(Var)
#define LIST_OF_TWO_EXPRESSIONS \
NODE(Add) \
NODE(Sub) \
NODE(Times) \
NODE(Divide) \
NODE(Mod) \
NODE(LeftShift) \
NODE(RightShift) \
NODE(LessThan) \
NODE(GreaterThan) \
NODE(LessThanEqualTo) \
NODE(GreaterThanEqualTo) \
NODE(Equals) \
NODE(NotEquals) \
NODE(BitwiseOr) \
NODE(BitwiseAnd) \
NODE(ExclusiveOr) \
NODE(LogicalOr) \
NODE(LogicalAnd) \
NODE(Assign)
#define NODE(_node) class _node##Expression : public OneExpression { \
public: \
_node##Expression(Node* n) : OneExpression(n) {} ~_node##Expression() {delete node;} \
};
LIST_OF_ONE_EXPRESSIONS
#undef NODE
#define NODE(node) class node##Expression : public TwoExpression { \
public: \
node##Expression(Node* n,Node* n2) : TwoExpression(n,n2) {} ~node##Expression() {delete left; delete right;} \
};
LIST_OF_TWO_EXPRESSIONS
#undef NODE
#define NODE(ttype,tname) class Const##tname##Expression : public Expression { \
public: \
ttype value; \
Const##tname##Expression(ttype v) \
{ \
this->value = v; \
}\
};
LIST_OF_CONST_EXPRESSIONS
#undef NODE
class ConstNullExpression : public Expression {};
class ConstUndefinedExpression : public Expression {};
class GetVariableExpression : public Expression {
public:
std::string name;
GetVariableExpression(std::string name)
{
this->name = name;
}
};
class IfNode : public Node {
public:
Node* cond;
Node* yes;
Node* no;
IfNode(Node* cond,Node* yes,Node* no)
{
this->cond = cond;
this->yes = yes;
this->no = no;
}
~IfNode()
{
delete cond;
delete yes;
delete no;
}
};
class TernaryExpression : public Expression {
public:
Node* cond;
Node* yes;
Node* no;
TernaryExpression(Node* cond,Node* yes,Node* no)
{
this->cond = cond;
this->yes = yes;
this->no = no;
}
~TernaryExpression()
{
delete cond;
delete yes;
delete no;
}
};
class WhileNode : public Node {
public:
Node* cond;
Node* body;
bool isDo;
WhileNode(Node* cond,Node* body, bool isDo)
{
this->cond = cond;
this->body = body;
this->isDo = isDo;
}
~WhileNode()
{
delete cond;
delete body;
}
};
class ForNode : public Node {
public:
Node* init;
Node* cond;
Node* inc;
Node* body;
ForNode(Node* init,Node* cond,Node* inc,Node* body)
{
this->init = init;
this->cond = cond;
this->inc = inc;
this->body = body;
}
~ForNode()
{
delete init;
delete cond;
delete inc;
delete body;
}
};
class PrintNode : public Node {
public:
Node* expr;
PrintNode(Node* expr)
{
this->expr = expr;
}
~PrintNode()
{
delete expr;
}
};
class EachNode : public Node {
public:
Node* item;
Node* list;
Node* body;
EachNode(Node* item,Node* list,Node* body)
{
this->item = item;
this->list = list;
this->body = body;
}
~EachNode()
{
delete item;
delete list;
delete body;
}
};
class FunctionDeclarationNode : public Node {
public:
Node* nameAndArgs;
Node* body;
FunctionDeclarationNode(Node* nameAndArgs,Node* body)
{
this->nameAndArgs = nameAndArgs;
this->body = body;
}
~FunctionDeclarationNode()
{
delete nameAndArgs;
delete body;
}
};
class FunctionCallExpression : public Expression
{
public:
std::vector<Node*> args;
Node* name;
FunctionCallExpression(Node* name) {
this->name = name;
}
~FunctionCallExpression()
{
for(auto item : args)
{
delete item;
}
delete name;
}
};
class GetMemberExpression : public Expression
{
public:
std::string name;
Node* parent;
GetMemberExpression(Node* parent,std::string name)
{
this->parent = parent;
this->name = name;
}
~GetMemberExpression()
{
delete parent;
}
};
class GetArrayExpression : public Expression
{
public:
Node* expr;
Node* parent;
GetArrayExpression(Node* parent,Node* expr)
{
this->parent = parent;
this->expr = expr;
}
~GetArrayExpression()
{
delete expr;
delete parent;
}
};
class ReturnNode : public Node {
public:
Node* retVal;
ReturnNode(Node* retVal)
{
this->retVal = retVal;
}
~ReturnNode()
{
delete retVal;
}
};
class BreakNode : public Node {};
class ContinueNode : public Node {};
class ScopeNode : public Node {
public:
std::vector<Node*> items;
bool rootScope;
~ScopeNode()
{
for(auto item : items)
delete item;
}
};
class ScriptParser
{
size_t i = 0;
std::vector<LexToken> tokens;
std::filesystem::path path;
static std::string Template(std::filesystem::path file)
{
//convert anything out of <!--tws --> to print ""; statements
std::string myRealBuf = {};
bool inCode = false;
std::string buffer={};
FILE* fs = fopen(file.c_str(),"rb");
if(fs == NULL) return "";
uint8_t sliding_buffer[7];
size_t len=0;
auto advance = [&sliding_buffer,&len,&fs]()-> bool {
if(len > 0)
{
len--;
for(size_t i = 0;i<len;i++)
{
sliding_buffer[i] = sliding_buffer[i+1];
}
}
if(len < 7)
len+= fread(sliding_buffer+len,1,7-len,fs);
return len > 0;
};
auto flush = [&buffer,&myRealBuf,&inCode]() -> void {
if(buffer.size() == 0) return;
if(inCode)
{
myRealBuf.append(buffer);
}
else
{
myRealBuf.append("print \"");
for(auto item : buffer)
{
switch(item)
{
case '\0':
myRealBuf.append("\\0");
break;
case '\n':
myRealBuf.append("\\n");
break;
case '\r':
myRealBuf.append("\\r");
break;
case '\f':
myRealBuf.append("\\f");
break;
case '\t':
myRealBuf.append("\\t");
break;
case '\v':
myRealBuf.append("\\v");
break;
case '\b':
myRealBuf.append("\\b");
break;
case '\a':
myRealBuf.append("\\a");
break;
case '\x1B':
myRealBuf.append("\\e");
break;
case '\"':
case '\'':
case '\\':
myRealBuf.push_back('\\');
myRealBuf.push_back(item);
break;
default:
myRealBuf.push_back(item);
break;
}
}
myRealBuf.append("\";");
}
buffer = {};
};
while(advance())
{
if(memcmp(sliding_buffer,"<!--tws",7) == 0)
{
flush();
inCode=true;
len = 0;
if(advance() == false) break;
}
if(inCode && memcmp(sliding_buffer,"-->",3) == 0)
{
flush();
inCode=false;
for(int i = 0;i<3;i++)
if(advance() == false) break;
if(len == 0) break;
}
buffer.push_back((char)sliding_buffer[0]);
}
flush();
fclose(fs);
return myRealBuf;
}
static void Lex(std::filesystem::path file,std::vector<LexToken>& tokens)
{
std::string text = Template(file);
size_t offset = 0;
int peeked = -1;
auto peek = [&text,&offset,&peeked]() -> int {
if(peeked > -1) return peeked;
if(offset < text.size())
{
peeked = text[offset];
offset++;
return peeked;
}
return -1;
};
auto read = [&text,&offset,&peeked]() -> int {
if(peeked != -1)
{
int myPeek = peeked;
peeked=-1;
return myPeek;
}
if(offset < text.size())
{
int c = text[offset];
offset++;
return c;
}
return -1;
};
int myRead=-1;
int myPeek=-1;
std::string buf={};
auto flush = [&buf,&tokens]()-> void {
if(buf.size() > 0)
{
tokens.push_back(LexToken(buf));
buf.clear();
}
};
auto read_chr_ = [&read]()-> std::pair<int,bool>
{
int myC = read();
if(myC == '\\')
{
myC = read();
if(myC == 'n')
{
return std::pair<int,bool>('\n',true);
}
else if(myC == 'r')
{
return std::pair<int,bool>('\r',true);
}
else if(myC == 'v')
{
return std::pair<int,bool>('\v',true);
}
else if(myC == 't')
{
return std::pair<int,bool>('\t',true);
}
else if(myC == 'b')
{
return std::pair<int,bool>('\b',true);
}
else if(myC == 'a')
{
return std::pair<int,bool>('\a',true);
}
else if(myC == 'e')
{
return std::pair<int,bool>('\x1B',true);
}
else if(myC == '0')
{
return std::pair<int,bool>('\0',true);
}
else if(myC == 'x')
{
int x1 = read();
int x2 = read();
return std::pair<int,bool>(((HttpUtils::hex_char_to_nibble(x1) << 4) & 0xF) | (HttpUtils::hex_char_to_nibble(x2) & 0xF),true);
}
else {
return std::pair<int,bool>(myC,true);
}
}
return std::pair<int,bool>(myC,false);
};
auto read_string = [&read_chr_,&tokens]()-> void {
std::pair<int,bool> c;
std::string myStr = {};
while((c = read_chr_()).first != -1)
{
if(c.first == '\"' && !c.second) break;
myStr.push_back((char)c.first);
}
tokens.push_back(LexToken(myStr,true));
};
auto read_char = [&read_chr_,&tokens,&read]()-> void {
auto v = read_chr_();
if(v.first != -1)
{
tokens.push_back(LexToken(std::string({(char)v.first}),false));
}
read();
};
while((myRead = read()) != -1)
{
myPeek = peek();
switch(myRead)
{
case '/':
flush();
if(myPeek == '/')
{
while(true)
{
myRead = read();
if(myRead == -1) throw std::exception();
if(myRead == '\n') break;
}
}
else if(myPeek == '*')
{
read();
while(true)
{
myRead = read();
if(myRead == -1) throw std::exception();
if(myRead == '*')
{
myRead = read();
if(myRead == '/') break;
}
}
}
else if(myPeek == '=')
{
read();
tokens.push_back(LexToken(std::string({(char)myRead,(char)myPeek})));
}
else
{
tokens.push_back(LexToken(std::string({(char)myRead})));
}
break;
case '\"':
flush();
read_string();
break;
case '\'':
flush();
read_char();
break;
case '<':
case '>':
{
if(myPeek == myRead)
{
read();
flush();
int myPeek2 = peek();
if(myPeek2 == '=')
{
read();
tokens.push_back(LexToken(std::string({(char)myRead,(char)myPeek,'='})));
}
else{
tokens.push_back(LexToken(std::string({(char)myRead,(char)myPeek})));
}
}
else if(myPeek == '=')
{
read();
flush();
tokens.push_back(LexToken(std::string({(char)myRead,(char)myPeek})));
}
else
{
flush();
tokens.push_back(LexToken(std::string({(char)myRead})));
}
}
break;
case '+':
case '-':
case '|':
case '&':
{
if(myPeek == myRead)
{
read();
flush();
tokens.push_back(LexToken(std::string({(char)myRead,(char)myPeek})));
}
else if(myPeek == '=')
{
read();
flush();
tokens.push_back(LexToken(std::string({(char)myRead,(char)myPeek})));
}
else
{
flush();
tokens.push_back(LexToken(std::string({(char)myRead})));
}
}
break;
case '^':
case '*':
case '%':
case '=':
case '!':
{
if(myPeek == '=')
{
read();
flush();
tokens.push_back(LexToken(std::string({(char)myRead,(char)myPeek})));
}
else
{
flush();
tokens.push_back(LexToken(std::string({(char)myRead})));
}
}
break;
case '?':
case '.':
case ';':
case ':':
case '(':
case ')':
case '[':
case ']':
case '{':
case '}':
case ',':
flush();
tokens.push_back(LexToken(std::string({(char)myRead})));
break;
case '\r':
case '\n':
case '\t':
case ' ':
flush();
break;
default:
buf.push_back((char)myRead);
break;
}
}
}
bool NextTokenIs(std::string val, bool take=true)
{
//std::cout << val << " == " << tokens[i].text << std::endl;
if(i >= tokens.size()) return false;
if(!tokens[i].isChar && !tokens[i].isString && tokens[i].text == val)
{
if(take) i++;
return true;
}
return false;
}
ScriptParser(std::filesystem::path path)
{
Lex(path,tokens);
this->path = path;
}
void Ensure(std::string val)
{
if(i >= tokens.size()) throw std::exception();
if(!tokens[i].isChar && !tokens[i].isString && tokens[i].text == val)
{
i++;
return;
}
throw std::exception();
}
bool IsAnyOf(std::initializer_list<std::string> tkns, std::string& txt)
{
if(this->i >= this->tokens.size()) return false;
for(auto item : tkns)
{
if(this->tokens[i].isString) continue;
if(this->tokens[i].isChar) continue;
if(this->tokens[i].text.compare(item) == 0)
{
this->i++;
txt = item;
return true;
}
}
return false;
}
Node* ParseValue()
{
if(i>=tokens.size()) throw std::exception();
if(tokens[i].isString)
{
auto val=tokens[i].text;
i++;
return new ConstStringExpression(val);
}
if(tokens[i].isChar)
{
auto val = tokens[i].text;
i++;
return new ConstCharExpression(val[0]);
}
std::string token = tokens[i].text;
i++;
bool hasNumber=true;
int64_t lngNum = 0;
if(token.size() == 1 && token[0] == '0')
{
lngNum = 0;
}
else
if(token.size() > 0 && token[0] == '0')
{
if(token.size() > 1 && token[1] == 'x')
{
lngNum = std::stoll(token.substr(2),nullptr,16);
}
else if(token.size() > 1 && token[1] == 'b')
{
lngNum = std::stoll(token.substr(2),nullptr,2);
}
else
{
lngNum = std::stoll(token.substr(1),nullptr,8);
}
}
else if(token.size() > 0 && token[0] >= '0' && token[0] <= '9')
{
lngNum=std::stoll(token,nullptr,10);
}
else
{
hasNumber = false;
}
Node* val = nullptr;
if(hasNumber && this->NextTokenIs(".",false) && i+1 < tokens.size() && !tokens[i+1].isChar && !tokens[i+1].isString)
{
std::string myToken = tokens[i+1].text;
if(myToken.size() > 0 && myToken[0] >= '0' && myToken[0] <= '9')
{
i+=2;
std::string myN = std::to_string(lngNum) + "." + myToken;
double v = std::stod(myN,nullptr);
val = new ConstDoubleExpression(v);
}
else
{
val = new ConstLongExpression(lngNum);
}
}
else if(hasNumber)
{
val = new ConstLongExpression(lngNum);
}
else {
if(token == "(")
{
val = ParseExpression();
Ensure(")");
}
if(token == "true")
{
val = new ConstBoolExpression(true);
}
else if(token == "false")
{
val = new ConstBoolExpression(false);
}
else if(token == "null")
{
val = new ConstNullExpression();
}
else if(token == "undefined")
{
val = new ConstUndefinedExpression();
}
else if(token == "var")
{
if(i>=tokens.size()) throw std::exception();
if(tokens[i].isString) throw std::exception();
if(tokens[i].isChar) throw std::exception();
val = new VarExpression(new GetVariableExpression(tokens[i].text));
i++;
}
else if(token == "global")
{
if(i>=tokens.size()) throw std::exception();
if(tokens[i].isString) throw std::exception();
if(tokens[i].isChar) throw std::exception();
val = new GlobalExpression(new GetVariableExpression(tokens[i].text));
i++;
}
else if(token == "caller")
{
if(i>=tokens.size()) throw std::exception();
if(tokens[i].isString) throw std::exception();
if(tokens[i].isChar) throw std::exception();
val = new CallerExpression(new GetVariableExpression(tokens[i].text));
i++;
}
else {
val = new GetVariableExpression(token);
}
}
std::string myT;
while(IsAnyOf({".","[","("},myT))
{
if(myT == ".")
{
if(i>=tokens.size()) throw std::exception();
if(tokens[i].isChar || tokens[i].isString) throw std::exception();
std::string name = tokens[i].text;
i++;
val = new GetMemberExpression(val, name);
}
if(myT == "[")
{
Node* expr=ParseExpression();
Ensure("]");
val = new GetArrayExpression(val,expr);
}
if(myT == "(")
{
auto fcall = new FunctionCallExpression(val);
while(!NextTokenIs(")",false))
{
fcall->args.push_back(ParseExpression());
if(!NextTokenIs(")",false))
Ensure(",");
}
Ensure(")");
val = fcall;
}
}
return val;
}
Node* ParseUnary()
{
if(NextTokenIs("-"))
{
return new NegativeExpression(ParseUnary());
}
if(NextTokenIs("~"))
{
return new BitwiseNotExpression(ParseUnary());
}
if(NextTokenIs("!"))
{
return new LogicalNotExpression(ParseUnary());
}
if(NextTokenIs("--"))
{
return new PrefixDecrementExpression(ParseUnary());
}
if(NextTokenIs("++"))
{
return new PrefixIncrementExpression(ParseUnary());
}
return ParseValue();
}
Node* ParseFactor()
{
Node* v = ParseUnary();
std::string tok;
while(IsAnyOf({"*","/","%"},tok))
{
if(tok == "*")
v = new TimesExpression(v,ParseUnary());
if(tok == "/")
v = new DivideExpression(v,ParseUnary());
if(tok == "%")
v = new ModExpression(v,ParseUnary());
}
return v;
}
Node* ParseSum()
{
Node* v = ParseFactor();
std::string tok;
while(IsAnyOf({"+","-"},tok))
{
if(tok == "+")
v = new AddExpression(v,ParseFactor());
if(tok == "-")
v = new SubExpression(v,ParseFactor());
}
return v;
}
Node* ParseShift()
{
Node* v = ParseSum();
std::string tok;
while(IsAnyOf({"<<",">>"},tok))
{
if(tok == "<<")
v = new LeftShiftExpression(v,ParseSum());
if(tok == ">>")
v = new RightShiftExpression(v,ParseSum());
}
return v;
}
Node* ParseRel()
{
Node* v = ParseShift();
std::string tok;
while(IsAnyOf({"<",">","<=",">="},tok))
{
if(tok == "<")
v = new LessThanExpression(v,ParseShift());
if(tok == ">")
v = new GreaterThanExpression(v,ParseShift());
if(tok == "<=")
v = new LessThanEqualToExpression(v,ParseShift());
if(tok == ">=")
v = new GreaterThanEqualToExpression(v,ParseShift());
}
return v;
}
Node* ParseEq()
{
Node* v = ParseRel();
std::string tok;
while(IsAnyOf({"!=","=="},tok))
{
if(tok == "!=")
v = new NotEqualsExpression(v,ParseRel());
if(tok == "==")
v = new EqualsExpression(v,ParseRel());
}
return v;
}
Node* ParseBAnd()
{
Node* v = ParseEq();
while(NextTokenIs("&"))
{
v = new BitwiseAndExpression(v,ParseEq());
}
return v;
}
Node* ParseXOr()
{
Node* v = ParseBAnd();
while(NextTokenIs("^"))
{
v = new ExclusiveOrExpression(v,ParseBAnd());
}
return v;
}
Node* ParseBOr()
{
Node* v = ParseXOr();
while(NextTokenIs("|"))
{
v = new BitwiseOrExpression(v,ParseXOr());
}
return v;
}
Node* ParseLAnd()
{
Node* v = ParseBOr();
while(NextTokenIs("&&"))
{
v = new LogicalAndExpression(v,ParseBOr());
}
return v;
}
Node* ParseLOr()
{
Node* v = ParseLAnd();
while(NextTokenIs("||"))
{
v = new LogicalOrExpression(v,ParseLAnd());
}
return v;
}
Node* ParseTernary()
{
Node* cond = ParseLOr();
if(NextTokenIs("?"))
{
Node* yes = ParseTernary();
Ensure(":");
Node* no = ParseTernary();
return new TernaryExpression(cond,yes,no);
}
return cond;
}
Node* ParseExpression()
{
Node* v = ParseTernary();
std::string t;
while(IsAnyOf({"=","+=","-=","*=","/=","%=","<<=",">>=","&=","^=","|="},t))
{
if(t == "=")
{
v = new AssignExpression(v,ParseTernary());
}
if (t == "+=")
{
v = new CompoundAssignExpression(new AddExpression(v,ParseTernary()));
}
if (t == "-=")
{
v = new CompoundAssignExpression(new SubExpression(v,ParseTernary()));
}
if (t == "*=")
{
v = new CompoundAssignExpression(new TimesExpression(v,ParseTernary()));
}
if (t == "/=")
{
v = new CompoundAssignExpression(new DivideExpression(v,ParseTernary()));
}
if (t == "%=")
{
v = new CompoundAssignExpression(new ModExpression(v,ParseTernary()));
}
if (t == "<<=")
{
v = new CompoundAssignExpression(new LeftShiftExpression(v,ParseTernary()));
}
if (t == ">>=")
{
v = new CompoundAssignExpression(new RightShiftExpression(v,ParseTernary()));
}
if (t == "&=")
{
v = new CompoundAssignExpression(new BitwiseAndExpression(v,ParseTernary()));
}
if (t == "^=")
{
v = new CompoundAssignExpression(new ExclusiveOrExpression(v,ParseTernary()));
}
if (t == "|=")
{
v = new CompoundAssignExpression(new BitwiseOrExpression(v,ParseTernary()));
}
}
return v;
}
Node* ParseNode(bool rootNode=false)
{
if(NextTokenIs("{") || rootNode)
{
ScopeNode* sn = new ScopeNode();
sn->rootScope = rootNode;
while(this->i < this->tokens.size() && !NextTokenIs("}"))
{
sn->items.push_back(ParseNode());
NextTokenIs(";");
}
return sn;
}
if(NextTokenIs("include"))
{
if(i < tokens.size())
{
std::filesystem::path par = this->path.parent_path();
std::string file = tokens[i].text;
i++;
Ensure(";");
ScriptParser p(par / file);
return p.ParseNode(true);
}
}
if(NextTokenIs("print"))
{
Node* n=new PrintNode(ParseExpression());
Ensure(";");
return n;
}
if(NextTokenIs("return"))
{
Node* n=new ReturnNode(ParseExpression());
Ensure(";");
return n;
}
if(NextTokenIs("break"))
{
Ensure(";");
return new BreakNode();
}
if(NextTokenIs("continue"))
{
Ensure(";");
return new ContinueNode();
}
if(NextTokenIs("if"))
{
Ensure("(");
Node* cond = ParseExpression();
Ensure(")");
Node* yes = nullptr;
if(!NextTokenIs("else",false))
{
yes = ParseNode();
NextTokenIs(";");
}
Node* no = nullptr;
if(NextTokenIs("else"))
{
no = ParseNode();
NextTokenIs(";");
}
return new IfNode(cond,yes,no);
}
if(NextTokenIs("for"))
{
Ensure("(");
Node* init = nullptr;
Node* cond = nullptr;
Node* inc = nullptr;
if(!NextTokenIs(";",false))
{
init = ParseExpression();
}
Ensure(";");
if(!NextTokenIs(";",false))
{
cond = ParseExpression();
}
Ensure(";");
if(!NextTokenIs(")",false))
{
inc = ParseExpression();
}
Ensure(")");
Node* body = ParseNode();
NextTokenIs(";");
return new ForNode(init,cond,inc,body);
}
if(NextTokenIs("while"))
{
Ensure("(");
Node* cond = ParseExpression();
Ensure(")");
Node* body = ParseNode();
NextTokenIs(";");
return new WhileNode(cond,body,false);
}
if(NextTokenIs("do"))
{
Ensure("(");
Node* cond = ParseExpression();
Ensure(")");
Node* body = ParseNode();
NextTokenIs(";");
return new WhileNode(cond,body,true);
}
if(NextTokenIs("each"))
{
Ensure("(");
Node* list = ParseExpression();
Node* item = nullptr;
if(NextTokenIs(":"))
{
item = list;
list = ParseExpression();
}
else
{
item = new GetVariableExpression("item");
}
Ensure(")");
Node* body = ParseNode();
NextTokenIs(";");
return new EachNode(item,list,body);
}
if(NextTokenIs("func"))
{
Node* fn = ParseExpression();
Node* body = ParseNode();
NextTokenIs(";");
return new FunctionDeclarationNode(fn,body);
}
Node* n= ParseExpression();
Ensure(";");
return n;
}
public:
static Node* Parse(std::filesystem::path path)
{
ScriptParser p(path);
return p.ParseNode(true);
}
};
inline ScriptType TypeOf(Environment* env, std::vector<ScriptType> args)
{
if(args.size() > 0)
{
auto item = args[0];
if(std::holds_alternative<char>(item))
{
return "char";
}
if(std::holds_alternative<std::string>(item))
{
return "string";
}
if(std::holds_alternative<int64_t>(item))
{
return "long";
}
if(std::holds_alternative<double>(item))
{
return "double";
}
if(std::holds_alternative<IttrKVP>(item))
{
return "ittrkvp";
}
if(std::holds_alternative<bool>(item))
{
return "bool";
}
if(std::holds_alternative<Undefined>(item))
{
return "undefined";
}
if(std::holds_alternative<Null>(item))
{
return "null";
}
if(std::holds_alternative<ObjectType>(item))
{
auto ot = std::get<ObjectType>(item).data;
auto ls = dynamic_cast<List*>(ot);
auto dict = dynamic_cast<Dictionary*>(ot);
auto ifunc = dynamic_cast<InternalFunction*>(ot);
auto efunc = dynamic_cast<ExternalFunction*>(ot);
auto ittr = dynamic_cast<Ittr*>(ot);
if(ittr != nullptr) return "ittr";
if(ls != nullptr) return "list";
if(dict != nullptr) return "dictionary";
if(ifunc != nullptr) return "internal_function";
if(efunc != nullptr) return "external_function";
}
}
return Null();
}
class BytecodeFile {
public:
std::map<std::string,InternalFunction*> functions;
InternalFunction* rootFunction;
~BytecodeFile()
{
delete rootFunction;
for(auto item : functions)
{
delete item.second;
}
}
};
#if defined(DEBUG)
#define INSTRUCTION_NAME(INAME) case Instruction:: INAME : printf("%s",#INAME); break;
#endif
class SimpleInstruction : public Instruction
{
public:
uint8_t instruction;
SimpleInstruction(uint8_t instruction)
{
this->instruction = instruction;
}
int Length()
{
return 1;
}
void Write(uint8_t* buffer)
{
buffer[0] = instruction;
}
#if defined(DEBUG)
void Print()
{
switch(instruction)
{
INSTRUCTION_NAME(ADD)
INSTRUCTION_NAME(SUB)
INSTRUCTION_NAME(TIMES)
INSTRUCTION_NAME(DIVIDE)
INSTRUCTION_NAME(MOD)
INSTRUCTION_NAME(LEFTSHIFT)
INSTRUCTION_NAME(RIGHTSHIFT)
INSTRUCTION_NAME(LESSTHAN)
INSTRUCTION_NAME(GREATERTHAN)
INSTRUCTION_NAME(GREATERTHANEQUALTO)
INSTRUCTION_NAME(LESSTHANEQUALTO)
INSTRUCTION_NAME(PRINT)
INSTRUCTION_NAME(SCOPE_BEGIN)
INSTRUCTION_NAME(SCOPE_END)
INSTRUCTION_NAME(POP)
INSTRUCTION_NAME(GETVARIABLE)
INSTRUCTION_NAME(SETVARIABLE)
INSTRUCTION_NAME(GETFIELD)
INSTRUCTION_NAME(SETFIELD)
INSTRUCTION_NAME(CALL_METHOD)
INSTRUCTION_NAME(CALL_FUNC)
INSTRUCTION_NAME(DESTROY_LOOP)
INSTRUCTION_NAME(ITTR)
INSTRUCTION_NAME(SET_LOOP)
INSTRUCTION_NAME(GET_LOOP)
default:
printf("UNKNOWN: %i\n",(int)instruction);
break;
}
printf("\n");
}
#endif
};
class LabelInstruction : public Instruction
{
std::string label;
public:
LabelInstruction(std::string label)
{
this->label = label;
}
std::string GetLabel()
{
return label;
}
void SetLabel(std::string lbl)
{
label = lbl;
}
int Length()
{
return 0;
}
void Write(uint8_t* buffer)
{
}
#if defined(DEBUG)
void Print()
{
printf("%s:\n",label.c_str());
}
#endif
};
class LabelableInstruction : public Instruction
{
public:
virtual std::string GetLabel()=0;
virtual void SetLabel(std::string lbl)=0;
virtual int GetPosition()=0;
virtual void SetPosition(int pos)=0;
};
class JumpInstruction : public LabelableInstruction
{
std::string label;
int position;
uint8_t instr;
public:
JumpInstruction(uint8_t instr,std::string label)
{
this->instr = instr;
this->label = label;
this->position = 0;
}
std::string GetLabel()
{
return label;
}
void SetLabel(std::string lbl)
{
label = lbl;
}
int GetPosition()
{
return position;
}
void SetPosition(int pos)
{
this->position = pos;
}
int Length() {return 5;}
void Write(uint8_t* buffer)
{
buffer[0] = instr;
buffer[1] = (uint8_t)((position >> 24) & 0xFF);
buffer[2] = (uint8_t)((position >> 16) & 0xFF);
buffer[3] = (uint8_t)((position >> 8) & 0xFF);
buffer[4] = (uint8_t)(position & 0xFF);
}
#if defined(DEBUG)
void Print()
{
switch(instr)
{
INSTRUCTION_NAME(JMP)
INSTRUCTION_NAME(JMPC)
}
printf(" %s [%i]\n", label.c_str(),this->position);
}
#endif
};
class LongInstruction : public Instruction {
public:
int64_t value;
LongInstruction(int64_t value)
{
this->value = value;
}
int Length()
{
return 9;
}
void Write(uint8_t* buffer)
{
buffer[0] = Instruction::PUSH_LONG;
buffer[1] = (uint8_t)((value >> 56) & 0xFF);
buffer[2] = (uint8_t)((value >> 48) & 0xFF);
buffer[3] = (uint8_t)((value >> 40) & 0xFF);
buffer[4] = (uint8_t)((value >> 32) & 0xFF);
buffer[5] = (uint8_t)((value >> 24) & 0xFF);
buffer[6] = (uint8_t)((value >> 16) & 0xFF);
buffer[7] = (uint8_t)((value >> 8) & 0xFF);
buffer[8] = (uint8_t)(value & 0xFF);
}
#if defined(DEBUG)
void Print()
{
std::cout << "PUSH_LONG " << value << std::endl;
}
#endif
};
class CharInstruction : public Instruction {
public:
char value;
CharInstruction(char c)
{
this->value = c;
}
int Length()
{
return 2;
}
void Write(uint8_t* buffer)
{
buffer[0] = Instruction::PUSH_CHAR;
buffer[1] = value;
}
#if defined(DEBUG)
void Print()
{
std::cout << "PUSH_CHAR \'" << value << "\'" << std::endl;
}
#endif
};
class StringInstruction : public Instruction {
public:
std::string value;
StringInstruction(std::string value)
{
this->value = value;
}
int Length()
{
return 5+value.size();
}
void Write(uint8_t* buffer)
{
buffer[0] = Instruction::PUSH_STRING;
auto sz = this->value.size();
buffer[1] = (uint8_t)((sz >> 24) & 0xFF);
buffer[2] = (uint8_t)((sz >> 16) & 0xFF);
buffer[3] = (uint8_t)((sz >> 8) & 0xFF);
buffer[4] = (uint8_t)(sz & 0xFF);
for(size_t i = 0;i<sz;i++)
{
buffer[i+5] = (uint8_t)value[i];
}
}
#if defined(DEBUG)
void Print()
{
std::cout << "PUSH_STRING \"" << value << "\"" << std::endl;
}
#endif
};
class BytecodeCompiler
{
std::string _brk={};
std::string _cont = {};
std::stack<std::string> __eachs;
int jmpId = 0;
std::string NewJumpId()
{
std::string s = "__compGen" + std::to_string(jmpId);
jmpId++;
return s;
}
void ExpressionPop(Node* n,std::vector<Instruction*>& instructions)
{
if(n == nullptr) return;
if(n->IsExpression())
instructions.push_back(new SimpleInstruction(Instruction::POP));
}
Node* n;
void GenerateFree(Node* _n,std::vector<Instruction*>& instructions)
{
GetVariableExpression fnName("free");
FunctionCallExpression fcE(&fnName);
fcE.args.push_back(_n);
Compile(&fcE,instructions);
instructions.push_back(new SimpleInstruction(Instruction::POP));
fcE.args.clear();
fcE.name=nullptr;
}
void Compile(Node* n,std::vector<Instruction*>& instructions)
{
if(n==nullptr) return;
auto addExpr = dynamic_cast<AddExpression*>(n);
auto subExpr = dynamic_cast<SubExpression*>(n);
auto timesExpr = dynamic_cast<TimesExpression*>(n);
auto divExpr = dynamic_cast<DivideExpression*>(n);
auto modExpr = dynamic_cast<ModExpression*>(n);
auto lsExpr = dynamic_cast<LeftShiftExpression*>(n);
auto rsExpr = dynamic_cast<RightShiftExpression*>(n);
auto ltExpr = dynamic_cast<LessThanExpression*>(n);
auto gtExpr = dynamic_cast<GreaterThanExpression*>(n);
auto lteExpr = dynamic_cast<LessThanEqualToExpression*>(n);
auto gteExpr = dynamic_cast<GreaterThanEqualToExpression*>(n);
auto funcDecl = dynamic_cast<FunctionDeclarationNode*>(n);
auto varDecl = dynamic_cast<VarExpression*>(n);
auto lnotExpr = dynamic_cast<LogicalNotExpression*>(n);
auto scope = dynamic_cast<ScopeNode*>(n);
auto print = dynamic_cast<PrintNode*>(n);
auto lng = dynamic_cast<ConstLongExpression*>(n);
auto str = dynamic_cast<ConstStringExpression*>(n);
auto assign = dynamic_cast<AssignExpression*>(n);
auto compound = dynamic_cast<CompoundAssignExpression*>(n);
auto varGet = dynamic_cast<GetVariableExpression*>(n);
auto memGet = dynamic_cast<GetMemberExpression*>(n);
auto arrGet = dynamic_cast<GetArrayExpression*>(n);
auto gblGet = dynamic_cast<GlobalExpression*>(n);
auto callerGet = dynamic_cast<CallerExpression*>(n);
auto fCall = dynamic_cast<FunctionCallExpression*>(n);
auto ifNode = dynamic_cast<IfNode*>(n);
auto ternary = dynamic_cast<TernaryExpression*>(n);
auto whileNode = dynamic_cast<WhileNode*>(n);
auto breakNode = dynamic_cast<BreakNode*>(n);
auto cont = dynamic_cast<ContinueNode*>(n);
auto eachLoop = dynamic_cast<EachNode*>(n);
auto forLoop = dynamic_cast<ForNode*>(n);
auto lOr = dynamic_cast<LogicalOrExpression*>(n);
auto lAnd = dynamic_cast<LogicalAndExpression*>(n);
auto retNode = dynamic_cast<ReturnNode*>(n);
auto boolExpr = dynamic_cast<ConstBoolExpression*>(n);
auto nullExpr = dynamic_cast<ConstNullExpression*>(n);
auto undefinedExpr = dynamic_cast<ConstUndefinedExpression*>(n);
auto chrExpr = dynamic_cast<ConstCharExpression*>(n);
auto equalsExpr = dynamic_cast<EqualsExpression*>(n);
auto notEqualsExpr = dynamic_cast<NotEqualsExpression*>(n);
if(equalsExpr != nullptr)
{
Compile(equalsExpr->left,instructions);
Compile(equalsExpr->right,instructions);
instructions.push_back(new SimpleInstruction(Instruction::EQUALS));
}
if(notEqualsExpr != nullptr)
{
Compile(notEqualsExpr->left,instructions);
Compile(notEqualsExpr->right,instructions);
instructions.push_back(new SimpleInstruction(Instruction::NOTEQUALS));
}
if(chrExpr != nullptr)
{
instructions.push_back(new CharInstruction(chrExpr->value));
}
if(boolExpr != nullptr)
{
instructions.push_back(new SimpleInstruction(boolExpr->value ? Instruction::PUSH_TRUE : Instruction::PUSH_FALSE));
}
if(nullExpr != nullptr)
{
instructions.push_back(new SimpleInstruction(Instruction::PUSH_NULL));
}
if(undefinedExpr != nullptr)
{
instructions.push_back(new SimpleInstruction(Instruction::PUSH_UNDEFINED));
}
if(retNode != nullptr)
{
while(!this->__eachs.empty())
{
auto item = this->__eachs.top();
GetVariableExpression gve(item);
GenerateFree(&gve,instructions);
this->__eachs.pop();
}
Compile(retNode->retVal,instructions);
instructions.push_back(new SimpleInstruction(Instruction::RET));
}
if(lnotExpr != nullptr)
{
Compile(lnotExpr->node,instructions);
instructions.push_back(new SimpleInstruction(Instruction::LOGICALNOT));
}
if(lOr != nullptr)
{
std::string end = this->NewJumpId();
std::string pushTrue = this->NewJumpId();
Compile(lOr->left,instructions);
instructions.push_back(new JumpInstruction(Instruction::JMPC,pushTrue));
Compile(lOr->right,instructions);
instructions.push_back(new JumpInstruction(Instruction::JMP,end));
instructions.push_back(new LabelInstruction(pushTrue));
instructions.push_back(new SimpleInstruction(Instruction::PUSH_TRUE));
instructions.push_back(new LabelInstruction(end));
}
if(lAnd != nullptr)
{
std::string end = this->NewJumpId();
std::string pushFalse = this->NewJumpId();
std::string hndl = this->NewJumpId();
Compile(lAnd->left,instructions);
instructions.push_back(new JumpInstruction(Instruction::JMPC,hndl));
instructions.push_back(new JumpInstruction(Instruction::JMP,pushFalse));
instructions.push_back(new LabelInstruction(hndl));
Compile(lAnd->right,instructions);
instructions.push_back(new JumpInstruction(Instruction::JMP,end));
instructions.push_back(new LabelInstruction(pushFalse));
instructions.push_back(new SimpleInstruction(Instruction::PUSH_FALSE));
instructions.push_back(new LabelInstruction(end));
}
if(breakNode != nullptr)
{
if( _brk.size() > 0)
{
instructions.push_back(new SimpleInstruction(Instruction::GET_LOOP));
instructions.push_back(new JumpInstruction(Instruction::JMP,_brk));
} else {
throw std::exception();
}
}
if(cont != nullptr)
{
if(_cont.size() > 0)
{
instructions.push_back(new SimpleInstruction(Instruction::GET_LOOP));
instructions.push_back(new JumpInstruction(Instruction::JMP, _cont));
}
else {
throw std::exception();
}
}
if(forLoop != nullptr)
{
instructions.push_back(new SimpleInstruction(Instruction::SET_LOOP));
Compile(forLoop->init,instructions);
ExpressionPop(forLoop->init,instructions);
std::string oldcont = _cont;
std::string oldbrk = _brk;
std::string end = NewJumpId();
std::string begin = NewJumpId();
std::string yes = NewJumpId();
std::string cont = NewJumpId();
_cont = cont;
_brk = end;
instructions.push_back(new LabelInstruction(begin));
Compile(forLoop->cond,instructions);
instructions.push_back(new JumpInstruction(Instruction::JMPC,yes));
instructions.push_back(new JumpInstruction(Instruction::JMP,end));
instructions.push_back(new LabelInstruction(yes));
Compile(forLoop->body,instructions);
ExpressionPop(forLoop->body,instructions);
instructions.push_back(new LabelInstruction(cont));
Compile(forLoop->inc,instructions);
ExpressionPop(forLoop->inc,instructions);
instructions.push_back(new JumpInstruction(Instruction::JMP,begin));
instructions.push_back(new LabelInstruction(end)); //used for break
instructions.push_back(new SimpleInstruction(Instruction::DESTROY_LOOP));
_cont = oldcont;
_brk = oldbrk;
}
if(eachLoop != nullptr)
{
instructions.push_back(new SimpleInstruction(Instruction::SET_LOOP));
std::string eachIttr = NewJumpId();
__eachs.push(eachIttr);
instructions.push_back(new StringInstruction(eachIttr));
Compile(eachLoop->list,instructions);
instructions.push_back(new SimpleInstruction(Instruction::ITTR));
instructions.push_back(new SimpleInstruction(Instruction::SETVARIABLE));
instructions.push_back(new SimpleInstruction(Instruction::POP));
std::string oldcont = _cont;
std::string oldbrk = _brk;
std::string yes = NewJumpId();
std::string end = NewJumpId();
std::string begin = NewJumpId();
_cont = begin;
_brk = end;
instructions.push_back(new LabelInstruction(begin));
GetVariableExpression var(eachIttr);
GetMemberExpression mem(&var,"movenext");
FunctionCallExpression ce(&mem);
Compile(&ce,instructions);
ce.name = nullptr;
mem.parent=nullptr;
instructions.push_back(new JumpInstruction(Instruction::JMPC,yes));
instructions.push_back(new JumpInstruction(Instruction::JMP,end));
instructions.push_back(new LabelInstruction(yes));
GetMemberExpression mem2(&var,"current");
FunctionCallExpression ce2(&mem2);
AssignExpression assignExpr(eachLoop->item,&ce2);
Compile(&assignExpr,instructions);
ExpressionPop(&assignExpr,instructions);
mem2.parent=nullptr;
ce2.name=nullptr;
assignExpr.left=nullptr;
assignExpr.right = nullptr;
Compile(eachLoop->body,instructions);
ExpressionPop(eachLoop->body,instructions);
instructions.push_back(new JumpInstruction(Instruction::JMP,begin));
instructions.push_back(new LabelInstruction(end));
__eachs.pop();
GenerateFree(&var,instructions);
instructions.push_back(new SimpleInstruction(Instruction::DESTROY_LOOP));
_cont = oldcont;
_brk = oldbrk;
}
if(whileNode != nullptr)
{
if(whileNode->isDo)
{
instructions.push_back(new SimpleInstruction(Instruction::SET_LOOP));
std::string oldcont = _cont;
std::string oldbrk = _brk;
std::string end = NewJumpId();
std::string begin = NewJumpId();
_cont = begin;
_brk = end;
instructions.push_back(new LabelInstruction(begin));
Compile(whileNode->body,instructions);
ExpressionPop(whileNode->body,instructions);
Compile(whileNode->cond,instructions);
instructions.push_back(new JumpInstruction(Instruction::JMPC,begin));
instructions.push_back(new LabelInstruction(end)); //used for break
instructions.push_back(new SimpleInstruction(Instruction::DESTROY_LOOP));
_cont = oldcont;
_brk = oldbrk;
}
else
{
instructions.push_back(new SimpleInstruction(Instruction::SET_LOOP));
std::string oldcont = _cont;
std::string oldbrk = _brk;
std::string yes = NewJumpId();
std::string end = NewJumpId();
std::string begin = NewJumpId();
_cont = begin;
_brk = end;
instructions.push_back(new LabelInstruction(begin));
Compile(whileNode->cond,instructions);
instructions.push_back(new JumpInstruction(Instruction::JMPC,yes));
instructions.push_back(new JumpInstruction(Instruction::JMP,end));
instructions.push_back(new LabelInstruction(yes));
Compile(whileNode->body,instructions);
ExpressionPop(whileNode->body,instructions);
instructions.push_back(new JumpInstruction(Instruction::JMP,begin));
instructions.push_back(new LabelInstruction(end));
instructions.push_back(new SimpleInstruction(Instruction::DESTROY_LOOP));
_cont = oldcont;
_brk = oldbrk;
}
}
if(ifNode != nullptr)
{
std::string yes = NewJumpId();
std::string end = NewJumpId();
Compile(ifNode->cond,instructions);
instructions.push_back(new JumpInstruction(Instruction::JMPC,yes));
Compile(ifNode->no,instructions);
ExpressionPop(ifNode->no,instructions);
instructions.push_back(new JumpInstruction(Instruction::JMP,end));
instructions.push_back(new LabelInstruction(yes));
Compile(ifNode->yes,instructions);
ExpressionPop(ifNode->yes,instructions);
instructions.push_back(new LabelInstruction(end));
}
if(ternary != nullptr)
{
std::string yes = NewJumpId();
std::string end = NewJumpId();
Compile(ternary->cond,instructions);
instructions.push_back(new JumpInstruction(Instruction::JMPC,yes));
Compile(ternary->no,instructions);
instructions.push_back(new JumpInstruction(Instruction::JMP,end));
instructions.push_back(new LabelInstruction(yes));
Compile(ternary->yes,instructions);
instructions.push_back(new LabelInstruction(end));
}
if(compound != nullptr)
{
auto twoNode = dynamic_cast<TwoExpression*>(compound->node);
if(twoNode != nullptr)
{
AssignExpression aE(twoNode->left,twoNode);
Compile(&aE,instructions);
aE.left = nullptr;
aE.right = nullptr;
}
}
if(addExpr != nullptr)
{
Compile(addExpr->left,instructions);
Compile(addExpr->right,instructions);
instructions.push_back(new SimpleInstruction(Instruction::ADD));
}
if(subExpr != nullptr)
{
Compile(subExpr->left,instructions);
Compile(subExpr->right,instructions);
instructions.push_back(new SimpleInstruction(Instruction::SUB));
}
if(timesExpr != nullptr)
{
Compile(timesExpr->left,instructions);
Compile(timesExpr->right,instructions);
instructions.push_back(new SimpleInstruction(Instruction::TIMES));
}
if(divExpr != nullptr)
{
Compile(divExpr->left,instructions);
Compile(divExpr->right,instructions);
instructions.push_back(new SimpleInstruction(Instruction::DIVIDE));
}
if(modExpr != nullptr)
{
Compile(modExpr->left,instructions);
Compile(modExpr->right,instructions);
instructions.push_back(new SimpleInstruction(Instruction::MOD));
}
if(lsExpr != nullptr)
{
Compile(lsExpr->left,instructions);
Compile(lsExpr->right,instructions);
instructions.push_back(new SimpleInstruction(Instruction::LEFTSHIFT));
}
if(rsExpr != nullptr)
{
Compile(rsExpr->left,instructions);
Compile(rsExpr->right,instructions);
instructions.push_back(new SimpleInstruction(Instruction::RIGHTSHIFT));
}
if(ltExpr != nullptr)
{
Compile(ltExpr->left,instructions);
Compile(ltExpr->right,instructions);
instructions.push_back(new SimpleInstruction(Instruction::LESSTHAN));
}
if(gtExpr != nullptr)
{
Compile(gtExpr->left,instructions);
Compile(gtExpr->right,instructions);
instructions.push_back(new SimpleInstruction(Instruction::GREATERTHAN));
}
if(lteExpr != nullptr)
{
Compile(lteExpr->left,instructions);
Compile(lteExpr->right,instructions);
instructions.push_back(new SimpleInstruction(Instruction::LESSTHANEQUALTO));
}
if(gteExpr != nullptr)
{
Compile(gteExpr->left,instructions);
Compile(gteExpr->right,instructions);
instructions.push_back(new SimpleInstruction(Instruction::GREATERTHANEQUALTO));
}
if(funcDecl != nullptr)
{
auto fcall = dynamic_cast<FunctionCallExpression*>(funcDecl->nameAndArgs);
if(fcall != nullptr)
{
auto fname = dynamic_cast<GetVariableExpression*>(fcall->name);
if(fname != nullptr)
{
auto fn = new InternalFunction();
this->file->functions[fname->name] = fn;
for(auto arg : fcall->args)
{
auto argTXT = dynamic_cast<GetVariableExpression*>(arg);
if(argTXT != nullptr)
{
fn->args.push_back(argTXT->name);
}
}
Compile(funcDecl->body,fn);
}
}
}
if(scope != nullptr)
{
if(!scope->rootScope)
instructions.push_back(new SimpleInstruction(Instruction::SCOPE_BEGIN));
for(auto no : scope->items)
{
Compile(no,instructions);
ExpressionPop(no,instructions);
}
if(!scope->rootScope)
instructions.push_back(new SimpleInstruction(Instruction::SCOPE_END));
}
if(lng != nullptr)
{
instructions.push_back(new LongInstruction(lng->value));
}
if(str != nullptr)
{
instructions.push_back(new StringInstruction(str->value));
}
if(print != nullptr)
{
Compile(print->expr,instructions);
instructions.push_back(new SimpleInstruction(Instruction::PRINT));
}
if(varDecl != nullptr)
{
auto gvn = dynamic_cast<GetVariableExpression*>(varDecl->node);
if(gvn != nullptr)
{
instructions.push_back(new StringInstruction(gvn->name));
instructions.push_back(new SimpleInstruction(Instruction::PUSH_NULL));
instructions.push_back(new SimpleInstruction(Instruction::DECLARE_VARIABLE));
}
}
if(assign != nullptr)
{
GetVariableExpression* gVar = dynamic_cast<GetVariableExpression*>(assign->left);
GetMemberExpression* gMem = dynamic_cast<GetMemberExpression*>(assign->left);
GetArrayExpression* gArray = dynamic_cast<GetArrayExpression*>(assign->left);
VarExpression* vDecl = dynamic_cast<VarExpression*>(assign->left);
GlobalExpression* gGbl = dynamic_cast<GlobalExpression*>(assign->left);
CallerExpression* gCaller = dynamic_cast<CallerExpression*>(assign->left);
if(gVar != nullptr)
{
instructions.push_back(new StringInstruction(gVar->name));
Compile(assign->right,instructions);
instructions.push_back(new SimpleInstruction(Instruction::SETVARIABLE));
}
if(gMem != nullptr)
{
Compile(gMem->parent,instructions);
instructions.push_back(new StringInstruction(gMem->name));
Compile(assign->right,instructions);
instructions.push_back(new SimpleInstruction(Instruction::SETFIELD));
}
if(gArray != nullptr)
{
Compile(gArray->parent,instructions);
Compile(gArray->expr,instructions);
Compile(assign->right,instructions);
instructions.push_back(new SimpleInstruction(Instruction::SETARRAY));
}
if(gGbl != nullptr)
{
gVar = dynamic_cast<GetVariableExpression*>(gGbl->node);
if(gVar != nullptr)
{
instructions.push_back(new StringInstruction(gVar->name));
Compile(assign->right,instructions);
instructions.push_back(new SimpleInstruction(Instruction::SET_GLOBAL_VARIABLE));
}
}
if(gCaller != nullptr)
{
gVar = dynamic_cast<GetVariableExpression*>(gCaller->node);
if(gVar != nullptr)
{
instructions.push_back(new StringInstruction(gVar->name));
Compile(assign->right,instructions);
instructions.push_back(new SimpleInstruction(Instruction::SET_CALLER_VARIABLE));
}
}
if(vDecl != nullptr)
{
gVar = dynamic_cast<GetVariableExpression*>(vDecl->node);
if(gVar != nullptr)
{
instructions.push_back(new StringInstruction(gVar->name));
Compile(assign->right,instructions);
instructions.push_back(new SimpleInstruction(Instruction::DECLARE_VARIABLE));
}
}
}
if(varGet != nullptr)
{
instructions.push_back(new StringInstruction(varGet->name));
instructions.push_back(new SimpleInstruction(Instruction::GETVARIABLE));
}
if(gblGet != nullptr)
{
varGet = dynamic_cast<GetVariableExpression*>(gblGet->node);
if(varGet != nullptr)
{
instructions.push_back(new StringInstruction(varGet->name));
instructions.push_back(new SimpleInstruction(Instruction::GET_GLOBAL_VARIABLE));
}
}
if(callerGet != nullptr)
{
varGet = dynamic_cast<GetVariableExpression*>(callerGet->node);
if(varGet != nullptr)
{
instructions.push_back(new StringInstruction(varGet->name));
instructions.push_back(new SimpleInstruction(Instruction::GET_CALLER_VARIABLE));
}
}
if(memGet != nullptr)
{
Compile(memGet->parent,instructions);
instructions.push_back(new StringInstruction(memGet->name));
instructions.push_back(new SimpleInstruction(Instruction::GETFIELD));
}
if(arrGet != nullptr)
{
Compile(arrGet->parent,instructions);
Compile(arrGet->expr,instructions);
instructions.push_back(new SimpleInstruction(Instruction::GETARRAY));
}
if(fCall != nullptr)
{
auto field = dynamic_cast<GetMemberExpression*>(fCall->name);
if(field != nullptr)
{
Compile(field->parent,instructions);
instructions.push_back(new StringInstruction(field->name));
for(auto item : fCall->args)
Compile(item,instructions);
instructions.push_back(new LongInstruction(fCall->args.size()));
instructions.push_back(new SimpleInstruction(Instruction::CALL_METHOD));
}
else
{
Compile(fCall->name,instructions);
for(auto item : fCall->args)
Compile(item,instructions);
instructions.push_back(new LongInstruction(fCall->args.size()));
instructions.push_back(new SimpleInstruction(Instruction::CALL_FUNC));
}
}
}
void Compile(Node* n, InternalFunction* func)
{
std::vector<Instruction*> instructions;
Compile(n,instructions);
int length=0;
std::map<std::string,int> lbls;
for(auto item : instructions)
{
auto lbl = dynamic_cast<LabelInstruction*>(item);
if(lbl != nullptr)
lbls[lbl->GetLabel()] = length;
length += item->Length();
}
func->bytecodeLength = length;
func->bytecode = new uint8_t[length];
uint8_t* buffer = func->bytecode;
#if defined(DEBUG)
printf("INSTRUCTIONS:\n\n");
#endif
size_t len2=0;
for(auto item : instructions)
{
auto lblable = dynamic_cast<LabelableInstruction*>(item);
if(lblable != nullptr)
{
lblable->SetPosition(lbls[lblable->GetLabel()]);
}
item->Write(buffer);
buffer += item->Length();
#if defined(DEBUG)
printf("OFFSET %i\n",(int)len2);
item->Print();
#endif
len2+=item->Length();
delete item;
}
#if defined(DEBUG)
printf("\n\nEND OF INSTRUCTIONS\n\n");
#endif
instructions.clear();
}
public:
BytecodeFile* file;
BytecodeCompiler(Node* root)
{
this->n = root;
file=new BytecodeFile();
file->rootFunction = new InternalFunction();
file->rootFunction->isRoot=true;
}
void Compile()
{
Compile(n,file->rootFunction);
}
};
}