mirror of
https://github.com/aportelli/LatAnalyze.git
synced 2025-04-05 17:35:55 +01:00
Math interpreter: significant improvement of runtime context handling
This commit is contained in:
parent
0fdd76b19d
commit
bbeaec9ed3
@ -33,21 +33,21 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
cout << "-- Program:" << endl << interpreter << endl;
|
||||
cout << "-- Variable table:" << endl;
|
||||
for (auto &v: context.vTable)
|
||||
for (auto &v: context.getVariableTable())
|
||||
{
|
||||
cout << "'" << v.first << "': " << v.second << endl;
|
||||
}
|
||||
cout << endl;
|
||||
cout << "-- Function table:" << endl;
|
||||
for (auto &f: context.fTable)
|
||||
for (auto &f: context.getFunctionTable())
|
||||
{
|
||||
cout << "'" << f.first << "': " << f.second << endl;
|
||||
}
|
||||
cout << endl;
|
||||
interpreter(context);
|
||||
if (!context.dStack.empty())
|
||||
if (!context.stack().empty())
|
||||
{
|
||||
cout << "-- Result: " << context.dStack.top() << endl;
|
||||
cout << "-- Result: " << context.stack().top() << endl;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
|
@ -53,12 +53,12 @@ void CompiledDoubleFunction::compile(void) const
|
||||
{
|
||||
if (!*isCompiled_)
|
||||
{
|
||||
interpreter_->compile(*(context_));
|
||||
varAddress_->clear();
|
||||
for (Index i = 0; i < getNArg(); ++i)
|
||||
{
|
||||
varAddress_->push_back(context_->vTable.at("x_" + strFrom(i)));
|
||||
varAddress_->push_back(context_->addVariable("x_" + strFrom(i)));
|
||||
}
|
||||
interpreter_->compile(*(context_));
|
||||
*isCompiled_ = true;
|
||||
}
|
||||
}
|
||||
@ -71,13 +71,13 @@ double CompiledDoubleFunction::operator()(const double *arg) const
|
||||
compile();
|
||||
for (unsigned int i = 0; i < getNArg(); ++i)
|
||||
{
|
||||
context_->vMem[(*varAddress_)[i]] = arg[i];
|
||||
context_->setVariable((*varAddress_)[i], arg[i]);
|
||||
}
|
||||
(*interpreter_)(*context_);
|
||||
if (!context_->dStack.empty())
|
||||
if (!context_->stack().empty())
|
||||
{
|
||||
result = context_->dStack.top();
|
||||
context_->dStack.pop();
|
||||
result = context_->stack().top();
|
||||
context_->stack().pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -56,17 +56,17 @@ void CompiledDoubleModel::compile(void) const
|
||||
{
|
||||
if (!*isCompiled_)
|
||||
{
|
||||
interpreter_->compile(*(context_));
|
||||
varAddress_->clear();
|
||||
parAddress_->clear();
|
||||
for (Index i = 0; i < getNArg(); ++i)
|
||||
{
|
||||
varAddress_->push_back(context_->vTable.at("x_" + strFrom(i)));
|
||||
varAddress_->push_back(context_->addVariable("x_" + strFrom(i)));
|
||||
}
|
||||
for (Index j = 0; j < getNPar(); ++j)
|
||||
{
|
||||
parAddress_->push_back(context_->vTable.at("p_" + strFrom(j)));
|
||||
parAddress_->push_back(context_->addVariable("p_" + strFrom(j)));
|
||||
}
|
||||
interpreter_->compile(*(context_));
|
||||
*isCompiled_ = true;
|
||||
}
|
||||
}
|
||||
@ -80,17 +80,17 @@ double CompiledDoubleModel::operator()(const double *arg,
|
||||
compile();
|
||||
for (unsigned int i = 0; i < getNArg(); ++i)
|
||||
{
|
||||
context_->vMem[(*varAddress_)[i]] = arg[i];
|
||||
context_->setVariable((*varAddress_)[i], arg[i]);
|
||||
}
|
||||
for (unsigned int j = 0; j < getNPar(); ++j)
|
||||
{
|
||||
context_->vMem[(*parAddress_)[j]] = par[j];
|
||||
context_->setVariable((*parAddress_)[j], par[j]);
|
||||
}
|
||||
(*interpreter_)(*context_);
|
||||
if (!context_->dStack.empty())
|
||||
if (!context_->stack().empty())
|
||||
{
|
||||
result = context_->dStack.top();
|
||||
context_->dStack.pop();
|
||||
result = context_->stack().top();
|
||||
context_->stack().pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -29,7 +29,6 @@ BEGIN_NAMESPACE
|
||||
/******************************************************************************
|
||||
* compiled double model class *
|
||||
******************************************************************************/
|
||||
|
||||
class CompiledDoubleModel: public DoubleModel
|
||||
{
|
||||
public:
|
||||
|
@ -24,6 +24,193 @@
|
||||
using namespace std;
|
||||
using namespace Latan;
|
||||
|
||||
/******************************************************************************
|
||||
* RunContext implementation *
|
||||
******************************************************************************/
|
||||
// access //////////////////////////////////////////////////////////////////////
|
||||
unsigned int RunContext::addFunction(const string &name, DoubleFunction *init)
|
||||
{
|
||||
try
|
||||
{
|
||||
setFunction(name, init);
|
||||
|
||||
return getFunctionAddress(name);
|
||||
}
|
||||
catch (Exceptions::Definition)
|
||||
{
|
||||
unsigned int address = fTable_.size();
|
||||
|
||||
fMem_.push_back(init);
|
||||
fTable_[name] = address;
|
||||
|
||||
return address;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int RunContext::addVariable(const string &name, double init)
|
||||
{
|
||||
try
|
||||
{
|
||||
setVariable(name, init);
|
||||
|
||||
return getVariableAddress(name);
|
||||
}
|
||||
catch (Exceptions::Definition)
|
||||
{
|
||||
unsigned int address = vTable_.size();
|
||||
|
||||
vMem_.push_back(init);
|
||||
vTable_[name] = address;
|
||||
|
||||
return address;
|
||||
}
|
||||
}
|
||||
|
||||
DoubleFunction * RunContext::getFunction(const string &name) const
|
||||
{
|
||||
return getFunction(getFunctionAddress(name));
|
||||
}
|
||||
|
||||
DoubleFunction * RunContext::getFunction(const unsigned int address) const
|
||||
{
|
||||
if (address >= fTable_.size())
|
||||
{
|
||||
LATAN_ERROR(Range, "function address " + strFrom(address)
|
||||
+ " out of range");
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return fMem_[address];
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int RunContext::getFunctionAddress(const string &name) const
|
||||
{
|
||||
try
|
||||
{
|
||||
return fTable_.at(name);
|
||||
}
|
||||
catch (out_of_range)
|
||||
{
|
||||
LATAN_ERROR(Definition, "undefined function '" + name + "'");
|
||||
}
|
||||
}
|
||||
|
||||
const RunContext::AddressTable & RunContext::getFunctionTable(void) const
|
||||
{
|
||||
return fTable_;
|
||||
}
|
||||
|
||||
unsigned int RunContext::getInsIndex(void) const
|
||||
{
|
||||
return insIndex_;
|
||||
}
|
||||
|
||||
double RunContext::getVariable(const string &name) const
|
||||
{
|
||||
return getVariable(getVariableAddress(name));
|
||||
}
|
||||
|
||||
double RunContext::getVariable(const unsigned int address) const
|
||||
{
|
||||
if (address >= vTable_.size())
|
||||
{
|
||||
LATAN_ERROR(Range, "variable address " + strFrom(address)
|
||||
+ " out of range");
|
||||
|
||||
return 0.;
|
||||
}
|
||||
else
|
||||
{
|
||||
return vMem_[address];
|
||||
}
|
||||
}
|
||||
|
||||
const RunContext::AddressTable & RunContext::getVariableTable(void) const
|
||||
{
|
||||
return vTable_;
|
||||
}
|
||||
|
||||
unsigned int RunContext::getVariableAddress(const string &name) const
|
||||
{
|
||||
try
|
||||
{
|
||||
return vTable_.at(name);
|
||||
}
|
||||
catch (out_of_range)
|
||||
{
|
||||
LATAN_ERROR(Definition, "undefined variable '" + name + "'");
|
||||
}
|
||||
}
|
||||
|
||||
void RunContext::incrementInsIndex(const unsigned int inc)
|
||||
{
|
||||
setInsIndex(getInsIndex() + inc);
|
||||
}
|
||||
|
||||
void RunContext::setFunction(const string &name, DoubleFunction *f)
|
||||
{
|
||||
|
||||
setFunction(getFunctionAddress(name), f);
|
||||
}
|
||||
|
||||
void RunContext::setFunction(const unsigned int address, DoubleFunction *f)
|
||||
{
|
||||
if (address >= fTable_.size())
|
||||
{
|
||||
LATAN_ERROR(Range, "function address " + strFrom(address)
|
||||
+ " out of range");
|
||||
}
|
||||
else
|
||||
{
|
||||
fMem_[address] = f;
|
||||
}
|
||||
}
|
||||
|
||||
void RunContext::setInsIndex(const unsigned index)
|
||||
{
|
||||
insIndex_ = index;
|
||||
}
|
||||
|
||||
void RunContext::setVariable(const string &name, const double value)
|
||||
{
|
||||
setVariable(getVariableAddress(name), value);
|
||||
}
|
||||
|
||||
void RunContext::setVariable(const unsigned int address, const double value)
|
||||
{
|
||||
if (address >= vTable_.size())
|
||||
{
|
||||
LATAN_ERROR(Range, "variable address " + strFrom(address)
|
||||
+ " out of range");
|
||||
}
|
||||
else
|
||||
{
|
||||
vMem_[address] = value;
|
||||
}
|
||||
}
|
||||
|
||||
stack<double> & RunContext::stack(void)
|
||||
{
|
||||
return dStack_;
|
||||
}
|
||||
|
||||
// reset ///////////////////////////////////////////////////////////////////////
|
||||
void RunContext::reset(void)
|
||||
{
|
||||
insIndex_ = 0;
|
||||
while (!dStack_.empty())
|
||||
{
|
||||
dStack_.pop();
|
||||
}
|
||||
vMem_.clear();
|
||||
fMem_.clear();
|
||||
vTable_.clear();
|
||||
fTable_.clear();
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Instruction set *
|
||||
******************************************************************************/
|
||||
@ -58,20 +245,13 @@ void Push::operator()(RunContext &context) const
|
||||
{
|
||||
if (type_ == ArgType::Constant)
|
||||
{
|
||||
context.dStack.push(val_);
|
||||
context.stack().push(val_);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
context.dStack.push(context.vMem[address_]);
|
||||
}
|
||||
catch (out_of_range)
|
||||
{
|
||||
LATAN_ERROR(Range, "unknown variable '" + name_ + "'");
|
||||
}
|
||||
context.stack().push(context.getVariable(address_));
|
||||
}
|
||||
context.insIndex++;
|
||||
context.incrementInsIndex();
|
||||
}
|
||||
|
||||
// Push print //////////////////////////////////////////////////////////////////
|
||||
@ -99,10 +279,10 @@ void Pop::operator()(RunContext &context) const
|
||||
{
|
||||
if (!name_.empty())
|
||||
{
|
||||
context.vMem[address_] = context.dStack.top();
|
||||
context.setVariable(address_, context.stack().top());
|
||||
}
|
||||
context.dStack.pop();
|
||||
context.insIndex++;
|
||||
context.stack().pop();
|
||||
context.incrementInsIndex();
|
||||
}
|
||||
|
||||
// Pop print ///////////////////////////////////////////////////////////////////
|
||||
@ -122,9 +302,9 @@ void Store::operator()(RunContext &context) const
|
||||
{
|
||||
if (!name_.empty())
|
||||
{
|
||||
context.vMem[address_] = context.dStack.top();
|
||||
context.setVariable(address_, context.stack().top());
|
||||
}
|
||||
context.insIndex++;
|
||||
context.incrementInsIndex();
|
||||
}
|
||||
|
||||
// Store print /////////////////////////////////////////////////////////////////
|
||||
@ -142,15 +322,8 @@ Call::Call(const unsigned int address, const string &name)
|
||||
// Call execution //////////////////////////////////////////////////////////////
|
||||
void Call::operator()(RunContext &context) const
|
||||
{
|
||||
try
|
||||
{
|
||||
context.dStack.push((*context.fMem[address_])(context.dStack));
|
||||
}
|
||||
catch (out_of_range)
|
||||
{
|
||||
LATAN_ERROR(Range, "unknown function '" + name_ + "'");
|
||||
}
|
||||
context.insIndex++;
|
||||
context.stack().push((*context.getFunction(address_))(context.stack()));
|
||||
context.incrementInsIndex();
|
||||
}
|
||||
|
||||
// Call print //////////////////////////////////////////////////////////////////
|
||||
@ -166,11 +339,11 @@ void name::operator()(RunContext &context) const\
|
||||
double x[nArg];\
|
||||
for (int i = 0; i < nArg; ++i)\
|
||||
{\
|
||||
x[nArg-1-i] = context.dStack.top();\
|
||||
context.dStack.pop();\
|
||||
x[nArg-1-i] = context.stack().top();\
|
||||
context.stack().pop();\
|
||||
}\
|
||||
context.dStack.push(exp);\
|
||||
context.insIndex++;\
|
||||
context.stack().push(exp);\
|
||||
context.incrementInsIndex();\
|
||||
}\
|
||||
void name::print(ostream &out) const\
|
||||
{\
|
||||
@ -283,26 +456,19 @@ catch (out_of_range)\
|
||||
}\
|
||||
|
||||
// VarNode compile /////////////////////////////////////////////////////////////
|
||||
void VarNode::compile(Program &program, AddressTable &vTable,
|
||||
AddressTable &fTable __unused) const
|
||||
void VarNode::compile(Program &program, RunContext &context) const
|
||||
{
|
||||
unsigned int address;
|
||||
|
||||
GET_ADDRESS(address, vTable, getName());
|
||||
PUSH_INS(program, Push, address, getName());
|
||||
|
||||
PUSH_INS(program, Push, context.getVariableAddress(getName()), getName());
|
||||
}
|
||||
|
||||
// CstNode compile /////////////////////////////////////////////////////////////
|
||||
void CstNode::compile(Program &program, AddressTable &nextVAddress __unused,
|
||||
AddressTable &nextFAddress __unused) const
|
||||
void CstNode::compile(Program &program, RunContext &context __unused) const
|
||||
{
|
||||
PUSH_INS(program, Push, strTo<double>(getName()));
|
||||
}
|
||||
|
||||
// SemicolonNode compile ///////////////////////////////////////////////////////
|
||||
void SemicolonNode::compile(Program &program, AddressTable &vTable,
|
||||
AddressTable &fTable) const
|
||||
void SemicolonNode::compile(Program &program, RunContext &context) const
|
||||
{
|
||||
auto &n = *this;
|
||||
|
||||
@ -314,14 +480,13 @@ void SemicolonNode::compile(Program &program, AddressTable &vTable,
|
||||
|
||||
if (isAssign||isSemiColumn||isKeyword)
|
||||
{
|
||||
n[i].compile(program, vTable, fTable);
|
||||
n[i].compile(program, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AssignNode compile //////////////////////////////////////////////////////////
|
||||
void AssignNode::compile(Program &program, AddressTable &vTable,
|
||||
AddressTable &fTable) const
|
||||
void AssignNode::compile(Program &program, RunContext &context) const
|
||||
{
|
||||
auto &n = *this;
|
||||
|
||||
@ -330,8 +495,8 @@ void AssignNode::compile(Program &program, AddressTable &vTable,
|
||||
bool hasSemicolonParent = isDerivedFrom<SemicolonNode>(getParent());
|
||||
unsigned int address;
|
||||
|
||||
n[1].compile(program, vTable, fTable);
|
||||
GET_ADDRESS(address, vTable, n[0].getName());
|
||||
n[1].compile(program, context);
|
||||
address = context.addVariable(n[0].getName());
|
||||
if (hasSemicolonParent)
|
||||
{
|
||||
PUSH_INS(program, Pop, address, n[0].getName());
|
||||
@ -352,14 +517,13 @@ void AssignNode::compile(Program &program, AddressTable &vTable,
|
||||
#define ELIFNODE(name, nArg) else IFNODE(name, nArg)
|
||||
#define ELSE else
|
||||
|
||||
void MathOpNode::compile(Program &program, AddressTable &vTable,
|
||||
AddressTable &fTable) const
|
||||
void MathOpNode::compile(Program &program, RunContext &context) const
|
||||
{
|
||||
auto &n = *this;
|
||||
|
||||
for (Index i = 0; i < n.getNArg(); ++i)
|
||||
{
|
||||
n[i].compile(program, vTable, fTable);
|
||||
n[i].compile(program, context);
|
||||
}
|
||||
IFNODE("-", 1) PUSH_INS(program, Neg,);
|
||||
ELIFNODE("+", 2) PUSH_INS(program, Add,);
|
||||
@ -371,27 +535,23 @@ void MathOpNode::compile(Program &program, AddressTable &vTable,
|
||||
}
|
||||
|
||||
// FuncNode compile ////////////////////////////////////////////////////////////
|
||||
void FuncNode::compile(Program &program, AddressTable &vTable,
|
||||
AddressTable &fTable) const
|
||||
void FuncNode::compile(Program &program, RunContext &context) const
|
||||
{
|
||||
auto &n = *this;
|
||||
unsigned int address;
|
||||
|
||||
for (Index i = 0; i < n.getNArg(); ++i)
|
||||
{
|
||||
n[i].compile(program, vTable, fTable);
|
||||
n[i].compile(program, context);
|
||||
}
|
||||
GET_ADDRESS(address, fTable, getName());
|
||||
PUSH_INS(program, Call, address, getName());
|
||||
PUSH_INS(program, Call, context.addFunction(getName()), getName());
|
||||
}
|
||||
|
||||
// ReturnNode compile ////////////////////////////////////////////////////////////
|
||||
void ReturnNode::compile(Program &program, AddressTable &vTable,
|
||||
AddressTable &fTable) const
|
||||
void ReturnNode::compile(Program &program, RunContext &context) const
|
||||
{
|
||||
auto &n = *this;
|
||||
|
||||
n[0].compile(program, vTable, fTable);
|
||||
n[0].compile(program, context);
|
||||
program.push_back(nullptr);
|
||||
}
|
||||
|
||||
@ -473,9 +633,9 @@ void MathInterpreter::parse(void)
|
||||
#define ADD_FUNC(context, func)\
|
||||
try\
|
||||
{\
|
||||
(context).fMem[(context).fTable.at(#func)] = &STDMATH_NAMESPACE::func;\
|
||||
(context).setFunction(#func, &STDMATH_NAMESPACE::func);\
|
||||
}\
|
||||
catch (out_of_range)\
|
||||
catch (Exceptions::Definition)\
|
||||
{}
|
||||
|
||||
#define ADD_STDMATH_FUNCS(context)\
|
||||
@ -534,9 +694,7 @@ void MathInterpreter::compile(RunContext &context)
|
||||
{
|
||||
if (root_)
|
||||
{
|
||||
context.vTable.clear();
|
||||
context.fTable.clear();
|
||||
root_->compile(program_, context.vTable, context.fTable);
|
||||
root_->compile(program_, context);
|
||||
for (unsigned int i = 0; i < program_.size(); ++i)
|
||||
{
|
||||
if (!program_[i])
|
||||
@ -547,8 +705,6 @@ void MathInterpreter::compile(RunContext &context)
|
||||
break;
|
||||
}
|
||||
}
|
||||
context.vMem.resize(context.vTable.size());
|
||||
context.fMem.resize(context.fTable.size());
|
||||
ADD_STDMATH_FUNCS(context);
|
||||
}
|
||||
if (!root_||!gotReturn)
|
||||
@ -573,10 +729,10 @@ void MathInterpreter::operator()(RunContext &context)
|
||||
|
||||
void MathInterpreter::execute(RunContext &context) const
|
||||
{
|
||||
context.insIndex = 0;
|
||||
while (context.insIndex != program_.size())
|
||||
context.setInsIndex(0);
|
||||
while (context.getInsIndex() != program_.size())
|
||||
{
|
||||
(*(program_[context.insIndex]))(context);
|
||||
(*(program_[context.getInsIndex()]))(context);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,19 +34,55 @@
|
||||
BEGIN_NAMESPACE
|
||||
|
||||
/******************************************************************************
|
||||
* Instruction classes *
|
||||
* Class for runtime context *
|
||||
******************************************************************************/
|
||||
typedef std::map<std::string, unsigned int> AddressTable;
|
||||
|
||||
struct RunContext
|
||||
class RunContext
|
||||
{
|
||||
unsigned int insIndex;
|
||||
std::stack<double> dStack;
|
||||
std::vector<double> vMem;
|
||||
std::vector<DoubleFunction *> fMem;
|
||||
AddressTable vTable, fTable;
|
||||
public:
|
||||
typedef std::map<std::string, unsigned int> AddressTable;
|
||||
public:
|
||||
// constructor
|
||||
RunContext(void) = default;
|
||||
// destructor
|
||||
~RunContext(void) = default;
|
||||
// access
|
||||
unsigned int addFunction(const std::string &name,
|
||||
DoubleFunction *init = nullptr);
|
||||
unsigned int addVariable(const std::string &name,
|
||||
const double init = 0.);
|
||||
DoubleFunction * getFunction(const std::string &name) const;
|
||||
DoubleFunction * getFunction(const unsigned int address) const;
|
||||
unsigned int getFunctionAddress(const std::string &name) const;
|
||||
const AddressTable & getFunctionTable(void) const;
|
||||
unsigned int getInsIndex(void) const;
|
||||
double getVariable(const std::string &name) const;
|
||||
double getVariable(const unsigned int address) const;
|
||||
unsigned int getVariableAddress(const std::string &name) const;
|
||||
const AddressTable & getVariableTable(void) const;
|
||||
void incrementInsIndex(const unsigned int inc = 1);
|
||||
void setFunction(const std::string &name,
|
||||
DoubleFunction *f);
|
||||
void setFunction(const unsigned int address,
|
||||
DoubleFunction *f);
|
||||
void setInsIndex(const unsigned index);
|
||||
void setVariable(const std::string &name,
|
||||
const double value);
|
||||
void setVariable(const unsigned int address,
|
||||
const double value);
|
||||
std::stack<double> & stack(void);
|
||||
// reset
|
||||
void reset(void);
|
||||
private:
|
||||
unsigned int insIndex_;
|
||||
std::stack<double> dStack_;
|
||||
std::vector<double> vMem_;
|
||||
std::vector<DoubleFunction *> fMem_;
|
||||
AddressTable vTable_, fTable_;
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* Instruction classes *
|
||||
******************************************************************************/
|
||||
// Abstract base
|
||||
class Instruction
|
||||
{
|
||||
@ -171,8 +207,7 @@ public:
|
||||
// operator
|
||||
const ExprNode &operator[](const Index i) const;
|
||||
// compile
|
||||
virtual void compile(Program &program, AddressTable &vTable,
|
||||
AddressTable &fTable) const = 0;
|
||||
virtual void compile(Program &program, RunContext &context) const = 0;
|
||||
private:
|
||||
std::string name_;
|
||||
std::vector<std::unique_ptr<ExprNode>> arg_;
|
||||
@ -186,8 +221,7 @@ class name: public base\
|
||||
{\
|
||||
public:\
|
||||
using base::base;\
|
||||
virtual void compile(Program &program, AddressTable &vTable,\
|
||||
AddressTable &fTable) const;\
|
||||
virtual void compile(Program &program, RunContext &context) const;\
|
||||
}
|
||||
|
||||
DECL_NODE(ExprNode, VarNode);
|
||||
@ -201,8 +235,7 @@ class KeywordNode: public ExprNode
|
||||
{
|
||||
public:
|
||||
using ExprNode::ExprNode;
|
||||
virtual void compile(Program &program, AddressTable &vTable,
|
||||
AddressTable &fTable) const = 0;
|
||||
virtual void compile(Program &program, RunContext &context) const = 0;
|
||||
};
|
||||
|
||||
DECL_NODE(KeywordNode, ReturnNode);
|
||||
|
Loading…
x
Reference in New Issue
Block a user