2024-09-02 03:19:22 +00:00
|
|
|
#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;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2024-09-02 03:24:01 +00:00
|
|
|
|
2024-09-02 03:19:22 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
2024-09-02 03:24:01 +00:00
|
|
|
typedef std::variant<Undefined,Null,ObjectType,std::string,int64_t,double,bool,char> ScriptType;
|
|
|
|
|
2024-09-02 03:19:22 +00:00
|
|
|
|
|
|
|
|
|
|
|
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))
|
|
|
|
{
|
|
|
|
std::cout << (std::get<bool>(st) ? "true" : "false") << std::endl;
|
|
|
|
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)
|
|
|
|
{
|
2024-09-02 03:24:01 +00:00
|
|
|
|
2024-09-02 03:19:22 +00:00
|
|
|
|
|
|
|
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 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);
|
2024-09-02 03:24:01 +00:00
|
|
|
|
2024-09-02 03:19:22 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
2024-09-02 03:24:01 +00:00
|
|
|
|
2024-09-02 03:19:22 +00:00
|
|
|
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) & 0xFF;
|
|
|
|
value |= ((uint64_t)bytecode[ip++] << 48) & 0xFF;
|
|
|
|
value |= ((uint64_t)bytecode[ip++] << 40) & 0xFF;
|
|
|
|
value |= ((uint64_t)bytecode[ip++] << 32) & 0xFF;
|
|
|
|
value |= ((uint64_t)bytecode[ip++] << 24) & 0xFF;
|
|
|
|
value |= ((uint64_t)bytecode[ip++] << 16) & 0xFF;
|
|
|
|
value |= ((uint64_t)bytecode[ip++] << 8) & 0xFF;
|
|
|
|
value |= (uint64_t)bytecode[ip++] & 0xFF;
|
|
|
|
|
|
|
|
stack.push((int64_t)value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Instruction::PUSH_DOUBLE:
|
|
|
|
{
|
|
|
|
if(ip + 8 <= bytecodeLength)
|
|
|
|
{
|
|
|
|
uint64_t value = ((uint64_t)bytecode[ip++] << 56) & 0xFF;
|
|
|
|
value |= ((uint64_t)bytecode[ip++] << 48) & 0xFF;
|
|
|
|
value |= ((uint64_t)bytecode[ip++] << 40) & 0xFF;
|
|
|
|
value |= ((uint64_t)bytecode[ip++] << 32) & 0xFF;
|
|
|
|
value |= ((uint64_t)bytecode[ip++] << 24) & 0xFF;
|
|
|
|
value |= ((uint64_t)bytecode[ip++] << 16) & 0xFF;
|
|
|
|
value |= ((uint64_t)bytecode[ip++] << 8) & 0xFF;
|
|
|
|
value |= (uint64_t)bytecode[ip++] & 0xFF;
|
|
|
|
|
|
|
|
|
|
|
|
stack.push(*(double*)&value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Instruction::PUSH_STRING:
|
|
|
|
{
|
|
|
|
|
|
|
|
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(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 = (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;
|
2024-09-02 03:24:01 +00:00
|
|
|
|
2024-09-02 03:19:22 +00:00
|
|
|
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<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);
|
2024-09-02 03:24:01 +00:00
|
|
|
|
2024-09-02 03:19:22 +00:00
|
|
|
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 = {};
|
|
|
|
|
2024-09-02 03:24:01 +00:00
|
|
|
|
2024-09-02 03:19:22 +00:00
|
|
|
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)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
2024-09-02 03:24:01 +00:00
|
|
|
|
2024-09-02 03:19:22 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|