#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 ScriptType; inline bool Equals(ScriptType& left, ScriptType& right) { if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) == std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) == std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) == std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) == std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return (double)std::get(left) == std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) == (double)std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) == std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return true; } if(std::holds_alternative(left) && std::holds_alternative(right)) { return true; } if(std::holds_alternative(left) && std::holds_alternative(right)) { auto objLeft = std::get(left).data; auto objRight = std::get(right).data; if(objLeft == objRight) return true; } return false; } inline bool ConvertToBool(ScriptType& st) { if(std::holds_alternative(st)) { return std::get(st).size() > 0; } if(std::holds_alternative(st)) { return std::get(st) != 0; } if(std::holds_alternative(st)) { return std::get(st) != 0; } if(std::holds_alternative(st)) { std::cout << (std::get(st) ? "true" : "false") << std::endl; return std::get(st); } if(std::holds_alternative(st)) { return std::get(st) != 0; } if(std::holds_alternative(st)) { return std::get(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(st)) { return "null"; } if(std::holds_alternative(st)) { return std::get(st); } if(std::holds_alternative(st)) { return std::to_string(std::get(st)); } if(std::holds_alternative(st)) { return std::to_string(std::get(st)); } if(std::holds_alternative(st)) { return std::get(st) ? "true" : "false"; } if(std::holds_alternative(st)) { return std::string({std::get(st)}); } if(std::holds_alternative(st)) { return std::get(st).data->ToString(); } return "undefined"; } class RootEnvironment; class Environment { public: std::function 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 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 args) { return Null(); } ScriptType Execute(Environment* callerEnv,std::initializer_list args) { return Execute(callerEnv,std::vector(args)); } std::string ToString() { return ""; } bool ToBool() { return true; } }; class ExternalFunction : public CallableObject { public: std::function)> callback; bool firstArgIsThis; bool FirstArgumentIsThis() { return firstArgIsThis; } ExternalFunction(std::function)> callback,bool firstArgIsThis=false) { this->callback = callback; this->firstArgIsThis = firstArgIsThis; } ScriptType Execute(Environment* callerEnv,std::vector args) { return callback(callerEnv,args); } ~ExternalFunction() { } }; class InternalFunction; class List : public Object { public: std::vector items; std::string ToString() { return ""; } bool ToBool() { return items.size() > 0; } }; class Dictionary : public Object { public: std::vector> 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(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(key,value)); } } }; inline ScriptType TypeOf(Environment* env, std::vector args); class RootEnvironment : public Environment { std::vector fns; public: RootEnvironment() { DeclareFunctions(); } Environment* global=nullptr; std::map 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(t)) { auto myObj=std::get(t); if(tree) { auto ls = dynamic_cast(myObj.data); auto dict = dynamic_cast(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 args)-> ScriptType { Dictionary* dict=new Dictionary(); ObjectType ot(dict); return ot; }); DeclareFunction("list",[this](Environment* env, std::vector args)-> ScriptType { List* list=new List(); ObjectType ot(list); return ot; }); DeclareFunction("typeof",TypeOf); DeclareFunction("free",[this](Environment* env, std::vector args)-> ScriptType { if(args.size() > 0) { Free(args[0]); } return Null(); }); DeclareFunction("free_tree",[this](Environment* env, std::vector args)-> ScriptType { if(args.size() > 0) { Free(args[0],true); } return Null(); }); } void DeclareFunction(std::string name,std::function)> 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* args; size_t ip=0; std::stack stack; Environment* env; public: ChunkExecuter(Environment* env,uint8_t* bytecode,size_t bytecodeLength,std::vector* 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 T PopSpecific() { auto p = Pop(); if(std::holds_alternative(p)) { return std::get(p); } else { std::vector 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(); return val; } std::string PopString() { auto val = PopSpecific(); return val; } ScriptType Mod(ScriptType& left,ScriptType& right) { if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) % std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return fmod(std::get(left), (double)std::get(right)); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return fmod((double)std::get(left), std::get(right)); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return fmod(std::get(left),std::get(right)); } return Null(); } ScriptType Divide(ScriptType& left,ScriptType& right) { if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) / std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) / (double)std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return (double)std::get(left) / std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) / std::get(right); } return Null(); } ScriptType Times(ScriptType& left,ScriptType& right) { if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) * std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) * (double)std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return (double)std::get(left) * std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) * std::get(right); } return Null(); } ScriptType GreaterThan(ScriptType& left,ScriptType& right) { if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) > std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) > (double)std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return (double)std::get(left) > std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) > std::get(right); } return Null(); } ScriptType LessThan(ScriptType& left,ScriptType& right) { if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) < std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) < (double)std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return (double)std::get(left) < std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) < std::get(right); } return Null(); } ScriptType GreaterThanEqualsTo(ScriptType& left,ScriptType& right) { if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) >= std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) >= (double)std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return (double)std::get(left) >= std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) >= std::get(right); } return Null(); } ScriptType LessThanEqualsTo(ScriptType& left,ScriptType& right) { if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) <= std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) <= (double)std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return (double)std::get(left) <= std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) <= std::get(right); } return Null(); } ScriptType Sub(ScriptType& left,ScriptType& right) { if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) - std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) - (double)std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return (double)std::get(left) - std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) - std::get(right); } return Null(); } ScriptType Add(ScriptType& left,ScriptType& right) { if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) + std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) + (double)std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return (double)std::get(left) + std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) + std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) + std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::to_string(std::get(left)) + std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) + std::to_string(std::get(right)); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::to_string(std::get(left)) + std::get(right); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) + std::to_string(std::get(right)); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::get(left) + std::string({std::get(right)}); } if(std::holds_alternative(left) && std::holds_alternative(right)) { return std::string({std::get(left)}) + std::get(right); } return Null(); } ScriptType CallMethod(ScriptType& member, std::string key, std::vector& args) { if(std::holds_alternative(member)) { if(key == "toString") { return "undefined"; } } if(std::holds_alternative(member)) { if(key == "toString") { return "null"; } } if(std::holds_alternative(member)) { auto obj = std::get(member).data; auto ls = dynamic_cast(obj); auto dict = dynamic_cast(obj); 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();ittritems.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();ittritems.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(index)) { auto myIndex=std::get(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(index)) { auto myIndex=std::get(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(index)) { auto myIndex=std::get(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(index)) { auto myIndex=std::get(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(item)) { auto fnt = std::get(item).data; auto fn = dynamic_cast(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(member)) { if(key == "toString") { return std::to_string(std::get(member)); } if(key == "toDouble") { return (double)std::get(member); } if(key == "toDoubleBits") { return std::bit_cast(std::get(member)); } if(key == "toChar") { return (char)std::get(member); } } if(std::holds_alternative(member)) { std::string myStr=std::get(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(member)) { if(key == "toString") { return std::string({std::get(member)}); } if(key == "toLong") { return (int64_t)std::get(member); } if(key == "toLower") { return (char)std::tolower(std::get(member)); } if(key == "toUpper") { return (char)std::toupper(std::get(member)); } } if(std::holds_alternative(member)) { if(key == "toString") { return std::to_string(std::get(member)); } if(key == "toLong") { return (int64_t)std::get(member); } if(key == "toLongBits") { return std::bit_cast(std::get(member)); } } return Undefined(); } ScriptType SetField(ScriptType& instance,std::string name,ScriptType& value) { if(std::holds_alternative(instance)) { auto inst = std::get(instance).data; auto dict = dynamic_cast(inst); if(dict != nullptr) { auto myFuncObj = dict->GetValue(std::string("set") + name); if(std::holds_alternative(myFuncObj)) { auto myFuncObj2 = std::get(myFuncObj).data; auto callbl = dynamic_cast(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(instance)) { auto str = std::get(instance); if(name == "length" || name == "count") { return (int64_t)str.size(); } } if(std::holds_alternative(instance)) { auto inst = std::get(instance).data; auto ls = dynamic_cast(inst); auto dict = dynamic_cast(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(myFuncObj)) { auto myFuncObj2 = std::get(myFuncObj).data; auto callbl = dynamic_cast(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 __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 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 _args; for(int i = 0;i(std::get(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 _args; for(int i = 0;iGetValue(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 = (bytecode[ip++] << 24) & 0xFF; len |= (bytecode[ip++] << 16) & 0xFF; len |= (bytecode[ip++] << 8) & 0xFF; len |= bytecode[ip++] & 0xFF; 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 = (bytecode[ip++] << 24) & 0xFF; len |= (bytecode[ip++] << 16) & 0xFF; len |= (bytecode[ip++] << 8) & 0xFF; len |= bytecode[ip++] & 0xFF; if(len < bytecodeLength) { auto v = Pop(); if(ConvertToBool(v)) ip = len; } else { std::cout << "Jump failed" << std::endl; throw std::exception(); } } } 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 args; Environment* env; bool FirstArgumentIsThis() { return args.size() > 0 && args[0] == "this"; } ScriptType Execute(Environment* callerEnv,std::vector 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 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 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 items; bool rootScope; ~ScopeNode() { for(auto item : items) delete item; } }; class ScriptParser { size_t i = 0; std::vector tokens; std::filesystem::path path; static std::string Template(std::filesystem::path file) { //convert anything out of 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 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,"",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& 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 myC = read(); if(myC == '\\') { myC = read(); if(myC == 'n') { return std::pair('\n',true); } else if(myC == 'r') { return std::pair('\r',true); } else if(myC == 'v') { return std::pair('\v',true); } else if(myC == 't') { return std::pair('\t',true); } else if(myC == 'b') { return std::pair('\b',true); } else if(myC == 'a') { return std::pair('\a',true); } else if(myC == 'e') { return std::pair('\x1B',true); } else if(myC == '0') { return std::pair('\0',true); } else if(myC == 'x') { int x1 = read(); int x2 = read(); return std::pair(((HttpUtils::hex_char_to_nibble(x1) << 4) & 0xF) | (HttpUtils::hex_char_to_nibble(x2) & 0xF),true); } else { return std::pair(myC,true); } } return std::pair(myC,false); }; auto read_string = [&read_chr_,&tokens]()-> void { std::pair 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 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("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 args) { if(args.size() > 0) { auto item = args[0]; if(std::holds_alternative(item)) { return "char"; } if(std::holds_alternative(item)) { return "string"; } if(std::holds_alternative(item)) { return "long"; } if(std::holds_alternative(item)) { return "double"; } if(std::holds_alternative(item)) { return "bool"; } if(std::holds_alternative(item)) { return "undefined"; } if(std::holds_alternative(item)) { return "null"; } if(std::holds_alternative(item)) { auto ot = std::get(item).data; auto ls = dynamic_cast(ot); auto dict = dynamic_cast(ot); auto ifunc = dynamic_cast(ot); auto efunc = dynamic_cast(ot); 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 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& instructions) { if(n == nullptr) return; if(n->IsExpression()) instructions.push_back(new SimpleInstruction(Instruction::POP)); } Node* n; void GenerateFree(Node* _n,std::vector& 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& instructions) { if(n==nullptr) return; auto addExpr = dynamic_cast(n); auto subExpr = dynamic_cast(n); auto timesExpr = dynamic_cast(n); auto divExpr = dynamic_cast(n); auto modExpr = dynamic_cast(n); auto lsExpr = dynamic_cast(n); auto rsExpr = dynamic_cast(n); auto ltExpr = dynamic_cast(n); auto gtExpr = dynamic_cast(n); auto lteExpr = dynamic_cast(n); auto gteExpr = dynamic_cast(n); auto funcDecl = dynamic_cast(n); auto varDecl = dynamic_cast(n); auto lnotExpr = dynamic_cast(n); auto scope = dynamic_cast(n); auto print = dynamic_cast(n); auto lng = dynamic_cast(n); auto str = dynamic_cast(n); auto assign = dynamic_cast(n); auto compound = dynamic_cast(n); auto varGet = dynamic_cast(n); auto memGet = dynamic_cast(n); auto arrGet = dynamic_cast(n); auto gblGet = dynamic_cast(n); auto callerGet = dynamic_cast(n); auto fCall = dynamic_cast(n); auto ifNode = dynamic_cast(n); auto ternary = dynamic_cast(n); auto whileNode = dynamic_cast(n); auto breakNode = dynamic_cast(n); auto cont = dynamic_cast(n); auto forLoop = dynamic_cast(n); auto lOr = dynamic_cast(n); auto lAnd = dynamic_cast(n); auto retNode = dynamic_cast(n); auto boolExpr = dynamic_cast(n); auto nullExpr = dynamic_cast(n); auto undefinedExpr = dynamic_cast(n); auto chrExpr = dynamic_cast(n); auto equalsExpr = dynamic_cast(n); auto notEqualsExpr = dynamic_cast(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) { 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(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(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(funcDecl->nameAndArgs); if(fcall != nullptr) { auto fname = dynamic_cast(fcall->name); if(fname != nullptr) { auto fn = new InternalFunction(); this->file->functions[fname->name] = fn; for(auto arg : fcall->args) { auto argTXT = dynamic_cast(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(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(assign->left); GetMemberExpression* gMem = dynamic_cast(assign->left); GetArrayExpression* gArray = dynamic_cast(assign->left); VarExpression* vDecl = dynamic_cast(assign->left); GlobalExpression* gGbl = dynamic_cast(assign->left); CallerExpression* gCaller = dynamic_cast(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(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(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(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(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(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(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 instructions; Compile(n,instructions); int length=0; std::map lbls; for(auto item : instructions) { auto lbl = dynamic_cast(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(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); } }; }