#include "StdInc.h" #include "ERMInterpreter.h" #include #include "../../lib/CObjectHandler.h" #include "../../lib/CHeroHandler.h" #include "../../lib/CCreatureHandler.h" #include "../../lib/VCMIDirs.h" #include "../../lib/IGameCallback.h" /* * ERMInterpreter.cpp, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * * License: GNU General Public License v2.0 or later * Full text of license available in license.txt file, in main folder * */ namespace spirit = boost::spirit; using namespace VERMInterpreter; using namespace boost::assign; typedef int TUnusedType; using namespace boost::assign; ERMInterpreter *erm; Environment *topDyn; namespace ERMPrinter { //console printer using namespace ERM; struct VarPrinterVisitor : boost::static_visitor<> { void operator()(TVarExpNotMacro const& val) const { logGlobal->debugStream() << val.varsym; if(val.val.is_initialized()) { logGlobal->debugStream() << val.val.get(); } } void operator()(TMacroUsage const& val) const { logGlobal->debugStream() << "$" << val.macro << "&"; } }; void varPrinter(const TVarExp & var) { boost::apply_visitor(VarPrinterVisitor(), var); } struct IExpPrinterVisitor : boost::static_visitor<> { void operator()(int const & constant) const { logGlobal->warnStream() << constant; } void operator()(TVarExp const & var) const { varPrinter(var); } }; void iexpPrinter(const TIexp & exp) { boost::apply_visitor(IExpPrinterVisitor(), exp); } struct IdentifierPrinterVisitor : boost::static_visitor<> { void operator()(TIexp const& iexp) const { iexpPrinter(iexp); } void operator()(TArithmeticOp const& arop) const { iexpPrinter(arop.lhs); logGlobal->debugStream() << " " << arop.opcode << " "; iexpPrinter(arop.rhs); } }; void identifierPrinter(const boost::optional & id) { if(id.is_initialized()) { logGlobal->debugStream() << "identifier: "; for (auto x : id.get()) { logGlobal->debugStream() << "#"; boost::apply_visitor(IdentifierPrinterVisitor(), x); } } } struct ConditionCondPrinterVisitor : boost::static_visitor<> { void operator()(TComparison const& cmp) const { iexpPrinter(cmp.lhs); logGlobal->debugStream() << " " << cmp.compSign << " "; iexpPrinter(cmp.rhs); } void operator()(int const& flag) const { logGlobal->debugStream() << "condflag " << flag; } }; void conditionPrinter(const boost::optional & cond) { if(cond.is_initialized()) { Tcondition condp = cond.get(); logGlobal->debugStream() << " condition: "; boost::apply_visitor(ConditionCondPrinterVisitor(), condp.cond); logGlobal->debugStream() << " cond type: " << condp.ctype; //recursive call if(condp.rhs.is_initialized()) { logGlobal->debugStream() << "rhs: "; boost::optional rhsc = condp.rhs.get().get(); conditionPrinter(rhsc); } else { logGlobal->debugStream() << "no rhs; "; } } } struct BodyVarpPrinterVisitor : boost::static_visitor<> { void operator()(TVarExpNotMacro const& cmp) const { if(cmp.questionMark.is_initialized()) { logGlobal->debugStream() << cmp.questionMark.get(); } if(cmp.val.is_initialized()) { logGlobal->debugStream() << "val:" << cmp.val.get(); } logGlobal->debugStream() << "varsym: |" << cmp.varsym << "|"; } void operator()(TMacroUsage const& cmp) const { logGlobal->debugStream() << "???$$" << cmp.macro << "$$"; } }; struct BodyOptionItemPrinterVisitor : boost::static_visitor<> { void operator()(TVarConcatString const& cmp) const { logGlobal->debugStream() << "+concat\""; varPrinter(cmp.var); logGlobal->debugStream() << " with " << cmp.string.str; } void operator()(TStringConstant const& cmp) const { logGlobal->debugStream() << " \"" << cmp.str << "\" "; } void operator()(TCurriedString const& cmp) const { logGlobal->debugStream() << "cs: "; iexpPrinter(cmp.iexp); logGlobal->debugStream() << " '" << cmp.string.str << "' "; } void operator()(TSemiCompare const& cmp) const { logGlobal->debugStream() << cmp.compSign << "; rhs: "; iexpPrinter(cmp.rhs); } void operator()(TMacroUsage const& cmp) const { logGlobal->debugStream() << "$$" << cmp.macro << "$$"; } void operator()(TMacroDef const& cmp) const { logGlobal->debugStream() << "@@" << cmp.macro << "@@"; } void operator()(TIexp const& cmp) const { iexpPrinter(cmp); } void operator()(TVarpExp const& cmp) const { logGlobal->debugStream() << "varp"; boost::apply_visitor(BodyVarpPrinterVisitor(), cmp.var); } void operator()(spirit::unused_type const& cmp) const { logGlobal->debugStream() << "nothing"; } }; struct BodyOptionVisitor : boost::static_visitor<> { void operator()(TVRLogic const& cmp) const { logGlobal->debugStream() << cmp.opcode << " "; iexpPrinter(cmp.var); } void operator()(TVRArithmetic const& cmp) const { logGlobal->debugStream() << cmp.opcode << " "; iexpPrinter(cmp.rhs); } void operator()(TNormalBodyOption const& cmp) const { logGlobal->debugStream() << cmp.optionCode << "~"; for (auto optList : cmp.params) { boost::apply_visitor(BodyOptionItemPrinterVisitor(), optList); } } }; void bodyPrinter(const Tbody & body) { logGlobal->debugStream() << " body items: "; for (auto bi: body) { logGlobal->debugStream() << " ("; apply_visitor(BodyOptionVisitor(), bi); logGlobal->debugStream() << ") "; } } struct CommandPrinterVisitor : boost::static_visitor<> { void operator()(Ttrigger const& trig) const { logGlobal->debugStream() << "trigger: " << trig.name << " "; identifierPrinter(trig.identifier); conditionPrinter(trig.condition); } void operator()(Tinstruction const& trig) const { logGlobal->debugStream() << "instruction: " << trig.name << " "; identifierPrinter(trig.identifier); conditionPrinter(trig.condition); bodyPrinter(trig.body); } void operator()(Treceiver const& trig) const { logGlobal->debugStream() << "receiver: " << trig.name << " "; identifierPrinter(trig.identifier); conditionPrinter(trig.condition); if(trig.body.is_initialized()) bodyPrinter(trig.body.get()); } void operator()(TPostTrigger const& trig) const { logGlobal->debugStream() << "post trigger: " << trig.name << " "; identifierPrinter(trig.identifier); conditionPrinter(trig.condition); } }; struct LinePrinterVisitor : boost::static_visitor<> { void operator()(Tcommand const& cmd) const { CommandPrinterVisitor un; boost::apply_visitor(un, cmd.cmd); logGlobal->debugStream() << "Line comment: " << cmd.comment; } void operator()(std::string const& comment) const { } void operator()(spirit::unused_type const& nothing) const { } }; void printERM(const TERMline & ast) { logGlobal->debugStream() << ""; boost::apply_visitor(LinePrinterVisitor(), ast); } void printTVExp(const TVExp & exp); struct VOptionPrinterVisitor : boost::static_visitor<> { void operator()(TVExp const& cmd) const { printTVExp(cmd); } void operator()(TSymbol const& cmd) const { for(auto mod : cmd.symModifier) { logGlobal->debugStream() << mod << " "; } logGlobal->debugStream() << cmd.sym; } void operator()(char const& cmd) const { logGlobal->debugStream() << "'" << cmd << "'"; } void operator()(int const& cmd) const { logGlobal->debugStream() << cmd; } void operator()(double const& cmd) const { logGlobal->debugStream() << cmd; } void operator()(TERMline const& cmd) const { printERM(cmd); } void operator()(TStringConstant const& cmd) const { logGlobal->debugStream() << "^" << cmd.str << "^"; } }; void printTVExp(const TVExp & exp) { for (auto mod: exp.modifier) { logGlobal->debugStream() << mod << " "; } logGlobal->debugStream() << "[ "; for (auto opt: exp.children) { boost::apply_visitor(VOptionPrinterVisitor(), opt); logGlobal->debugStream() << " "; } logGlobal->debugStream() << "]"; } struct TLPrinterVisitor : boost::static_visitor<> { void operator()(TVExp const& cmd) const { printTVExp(cmd); } void operator()(TERMline const& cmd) const { printERM(cmd); } }; void printAST(const TLine & ast) { boost::apply_visitor(TLPrinterVisitor(), ast); } } void ERMInterpreter::scanForScripts() { using namespace boost::filesystem; //parser checking if(!exists(VCMIDirs::get().dataPaths().back() + "/Data/s/")) { logGlobal->warnStream() << "Warning: Folder " << VCMIDirs::get().dataPaths().back() << "/Data/s/ doesn't exist!"; return; } directory_iterator enddir; for (directory_iterator dir(VCMIDirs::get().dataPaths().back() + "/Data/s"); dir!=enddir; dir++) { if(is_regular(dir->status())) { std::string name = dir->path().leaf().string(); if( boost::algorithm::ends_with(name, ".erm") || boost::algorithm::ends_with(name, ".verm") ) { ERMParser ep(dir->path().string()); FileInfo * finfo = new FileInfo; finfo->filename = dir->path().string(); std::vector buf = ep.parseFile(); finfo->length = buf.size(); files.push_back(finfo); for(int g=0; g::const_iterator prevIt; for(std::map< LinePointer, ERM::TLine >::const_iterator it = scripts.begin(); it != scripts.end(); ++it) { if(it == scripts.begin() || it->first.file != prevIt->first.file) { logGlobal->debugStream() << "----------------- script " << it->first.file->filename << " ------------------"; } logGlobal->debugStream() << it->first.realLineNum; ERMPrinter::printAST(it->second); prevIt = it; } } struct ScriptScanner : boost::static_visitor<> { ERMInterpreter * interpreter; LinePointer lp; ScriptScanner(ERMInterpreter * interpr, const LinePointer & _lp) : interpreter(interpr), lp(_lp) {} void operator()(TVExp const& cmd) const { // } void operator()(TERMline const& cmd) const { if(cmd.which() == 0) //TCommand { Tcommand tcmd = boost::get(cmd); switch (tcmd.cmd.which()) { case 0: //trigger { Trigger trig; trig.line = lp; interpreter->triggers[ TriggerType(boost::get(tcmd.cmd).name) ].push_back(trig); } break; case 3: //post trigger { Trigger trig; trig.line = lp; interpreter->postTriggers[ TriggerType(boost::get(tcmd.cmd).name) ].push_back(trig); } break; default: break; } } } }; void ERMInterpreter::scanScripts() { for(std::map< LinePointer, ERM::TLine >::const_iterator it = scripts.begin(); it != scripts.end(); ++it) { boost::apply_visitor(ScriptScanner(this, it->first), it->second); } } ERMInterpreter::ERMInterpreter() { erm = this; curFunc = nullptr; curTrigger = nullptr; globalEnv = new Environment(); topDyn = globalEnv; } void ERMInterpreter::executeTrigger( VERMInterpreter::Trigger & trig, int funNum /*= -1*/, std::vector funParams/*=std::vector()*/ ) { //function-related logic if(funNum != -1) { curFunc = getFuncVars(funNum); for(int g=1; g<=FunctionLocalVars::NUM_PARAMETERS; ++g) { curFunc->getParam(g) = g-1 < funParams.size() ? funParams[g-1] : 0; } } else curFunc = getFuncVars(0); //skip the first line LinePointer lp = trig.line; ++lp; for(; lp.isValid(); ++lp) { ERM::TLine curLine = retrieveLine(lp); if(isATrigger(curLine)) break; executeLine(lp); } curFunc = nullptr; } bool ERMInterpreter::isATrigger( const ERM::TLine & line ) { switch(line.which()) { case 0: //v-exp { TVExp vexp = boost::get(line); if(vexp.children.size() == 0) return false; switch (getExpType(vexp.children[0])) { case SYMBOL: { //TODO: what about sym modifiers? //TOOD: macros? ERM::TSymbol sym = boost::get(vexp.children[0]); return sym.sym == triggerSymbol || sym.sym == postTriggerSymbol; } break; case TCMD: return isCMDATrigger( boost::get(vexp.children[0]) ); break; default: return false; break; } } break; case 1: //erm { TERMline ermline = boost::get(line); switch(ermline.which()) { case 0: //tcmd return isCMDATrigger( boost::get(ermline) ); break; default: return false; break; } } break; default: assert(0); //it should never happen break; } assert(0); return false; } ERM::EVOtions ERMInterpreter::getExpType( const ERM::TVOption & opt ) { //MAINTENANCE: keep it correct! return static_cast(opt.which()); } bool ERMInterpreter::isCMDATrigger( const ERM::Tcommand & cmd ) { switch (cmd.cmd.which()) { case 0: //trigger case 3: //post trigger return true; break; default: return false; break; } } ERM::TLine &ERMInterpreter::retrieveLine( LinePointer linePtr ) { return scripts.find(linePtr)->second; } ///////// //code execution template struct StandardBodyOptionItemVisitor : boost::static_visitor<> { typedef OwnerType TReceiverType; OwnerType & owner; explicit StandardBodyOptionItemVisitor(OwnerType & _owner) : owner(_owner) {} virtual void operator()(TVarConcatString const& cmp) const { throw EScriptExecError("String concatenation not allowed in this receiver"); } virtual void operator()(TStringConstant const& cmp) const { throw EScriptExecError("String constant not allowed in this receiver"); } virtual void operator()(TCurriedString const& cmp) const { throw EScriptExecError("Curried string not allowed in this receiver"); } virtual void operator()(TSemiCompare const& cmp) const { throw EScriptExecError("Semi comparison not allowed in this receiver"); } // virtual void operator()(TMacroUsage const& cmp) const // { // throw EScriptExecError("Macro usage not allowed in this receiver"); // } virtual void operator()(TMacroDef const& cmp) const { throw EScriptExecError("Macro definition not allowed in this receiver"); } virtual void operator()(TIexp const& cmp) const { throw EScriptExecError("i-expression not allowed in this receiver"); } virtual void operator()(TVarpExp const& cmp) const { throw EScriptExecError("Varp expression not allowed in this receiver"); } virtual void operator()(spirit::unused_type const& cmp) const { throw EScriptExecError("\'Nothing\' not allowed in this receiver"); } }; template struct StandardReceiverVisitor : boost::static_visitor<> { ERMInterpreter * interp; T identifier; StandardReceiverVisitor(ERMInterpreter * _interpr, T ident) : interp(_interpr), identifier(ident) {} virtual void operator()(TVRLogic const& trig) const { throw EScriptExecError("VR logic not allowed in this receiver!"); } virtual void operator()(TVRArithmetic const& trig) const { throw EScriptExecError("VR arithmetic not allowed in this receiver!"); } virtual void operator()(TNormalBodyOption const& trig) const = 0; template void performOptionTakingOneParamter(const ERM::TNormalBodyOptionList & params) const { if(params.size() == 1) { ERM::TBodyOptionItem boi = params[0]; boost::apply_visitor( OptionPerformer(*const_cast(static_cast(this))), boi); } else throw EScriptExecError("This receiver option takes exactly 1 parameter!"); } template