1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-04-15 11:46:56 +02:00

* checking ERM conditions should work now

This commit is contained in:
mateuszb 2011-05-02 18:39:57 +00:00
parent 36d29424f0
commit 86a7cd12ff
2 changed files with 349 additions and 77 deletions

View File

@ -633,9 +633,11 @@ struct ERMExecEnvironment
: ermGlobalEnv(erm), trigEnv(trig), funcVars(funvars)
{}
template<typename T>
T* getVar(std::string toFollow, boost::optional<int> initVal)
IexpValStr getVar(std::string toFollow, boost::optional<int> initVal)
{
IexpValStr ret;
ret.type = IexpValStr::WRONGVAL;
int initV;
bool hasInit = false;
if(initVal.is_initialized())
@ -650,7 +652,19 @@ struct ERMExecEnvironment
endNum = 1;
//TODO: support
}
T* ret;
if(toFollow.size() == 0)
{
if(hasInit)
{
ret.val.val = initV;
ret.type = IexpValStr::INT;
}
else
throw EIexpProblem("No input to getVar!");
return ret;
}
//now we have at least one element in toFollow
for(int b=toFollow.size()-1; b>=endNum; --b)
{
bool retIt = b == endNum+1; //if we should return the value are currently at
@ -674,14 +688,20 @@ struct ERMExecEnvironment
if(initV > 0 && initV <= FunctionLocalVars::NUM_FLOATINGS)
{
if(funcVars)
ret = (T*)funcVars->floats + initV - 1;
{
ret.val.flvar = &funcVars->getFloat(initV);
ret.type = IexpValStr::FLOATVAR;
}
else
throw EIexpProblem("Function context not available!");
}
else if(initV < 0 && initV >= -TriggerLocalVars::EVAR_NUM)
{
if(trigEnv)
ret = (T*)trigEnv->ermLocalVars.evar - initV + 1; //minus is important!
{
ret.val.flvar = &trigEnv->ermLocalVars.getEvar(initV);
ret.type = IexpValStr::FLOATVAR;
}
else
throw EIexpProblem("No trigger context available!");
}
@ -697,7 +717,10 @@ struct ERMExecEnvironment
else if(cr >= 'f' && cr <= 't')
{
if(retIt)
ret = &ermGlobalEnv->getQuickVar(cr);
{
ret.val.integervar = &ermGlobalEnv->getQuickVar(cr);
ret.type = IexpValStr::INTVAR;
}
else
{
if(hasInit)
@ -713,15 +736,13 @@ struct ERMExecEnvironment
{
if(hasInit)
{
if(initV > 0 && initV <= ERMEnvironment::NUM_STANDARDS)
if(retIt)
{
if(retIt)
ret = ermGlobalEnv->standardVars + initV - 1;
else
initV = ermGlobalEnv->standardVars[initV-1];
ret.val.integervar = &ermGlobalEnv->getStandardVar(initV);
ret.type = IexpValStr::INTVAR;
}
else
throw EIexpProblem("standard variable index out of range");
initV = ermGlobalEnv->getStandardVar(initV);
}
else
throw EIexpProblem("standard variable cannot be used in this context!");
@ -734,19 +755,17 @@ struct ERMExecEnvironment
{
if(hasInit)
{
if(initV > 0 && initV <= FunctionLocalVars::NUM_PARAMETERS)
if(funcVars)
{
if(funcVars)
if(retIt)
{
if(retIt)
ret = funcVars->params + initV-1;
else
initV = funcVars->params[initV-1];
ret.val.integervar = &funcVars->getParam(initV);
ret.type = IexpValStr::INTVAR;
}
else throw EIexpProblem("Function parameters cannot be used outside a function!");
else
initV = funcVars->getParam(initV);
}
else
throw EIexpProblem("Parameter number out of range");
else throw EIexpProblem("Function parameters cannot be used outside a function!");
}
else
throw EIexpProblem("Specify which function parameter should be used");
@ -760,9 +779,12 @@ struct ERMExecEnvironment
if(funcVars)
{
if(retIt)
ret = funcVars->locals + initV-1;
{
ret.val.integervar = &funcVars->getLocal(initV);
ret.type = IexpValStr::INTVAR;
}
else
initV = funcVars->params[initV - 1];
initV = funcVars->getLocal(initV);
}
else
throw EIexpProblem("Function local variables cannot be used outside a function!");
@ -772,9 +794,12 @@ struct ERMExecEnvironment
if(trigEnv)
{
if(retIt)
ret = trigEnv->ermLocalVars.yvar - initV + 1;
{
ret.val.integervar = &trigEnv->ermLocalVars.getYvar(initV);
ret.type = IexpValStr::INTVAR;
}
else
initV = trigEnv->ermLocalVars.yvar[-initV + 1];
initV = trigEnv->ermLocalVars.getYvar(initV);
}
else
throw EIexpProblem("Trigger local variables cannot be used outside triggers!");
@ -792,15 +817,17 @@ struct ERMExecEnvironment
if(retIt)
{
//these C-style casts are here just to shut up compiler errors
if(initV > 0 && initV <= ermGlobalEnv->NUM_STRINGS)
if(initV > 0 )
{
ret = (T*)ermGlobalEnv->strings + initV - 1;
ret.val.stringvar = &ermGlobalEnv->getZVar(initV);
ret.type = IexpValStr::STRINGVAR;
}
else if(initV < 0 && initV >= -FunctionLocalVars::NUM_STRINGS)
else if(initV < 0)
{
if(funcVars)
{
ret = (T*)funcVars->strings + initV - 1;
ret.val.stringvar = &funcVars->getString(initV);
ret.type = IexpValStr::STRINGVAR;
}
else
throw EIexpProblem("Function local string variables cannot be used outside functions!");
@ -829,76 +856,71 @@ namespace IexpDisemboweler
enum EDir{GET, SET};
}
template<typename T>
struct LVL2IexpDisemboweler : boost::static_visitor<>
struct LVL2IexpDisemboweler : boost::static_visitor<IexpValStr>
{
T * inout;
IexpDisemboweler::EDir dir;
/*const*/ ERMExecEnvironment * env;
LVL2IexpDisemboweler(T * in_out, /*const*/ ERMExecEnvironment * _env, IexpDisemboweler::EDir _dir)
: inout(in_out), env(_env), dir(_dir) //writes value to given var
LVL2IexpDisemboweler(/*const*/ ERMExecEnvironment * _env, IexpDisemboweler::EDir _dir)
: env(_env), dir(_dir) //writes value to given var
{}
void processNotMacro(const TVarExpNotMacro & val) const
IexpValStr processNotMacro(const TVarExpNotMacro & val) const
{
if(val.questionMark.is_initialized())
throw EIexpProblem("Question marks ('?') are not allowed in getter i-expressions");
//const-cast just to do some code-reuse...
*inout = *const_cast<ERMExecEnvironment*>(env)->getVar<T>(val.varsym, val.val);
return env->getVar(val.varsym, val.val);
}
void operator()(TVarExpNotMacro const& val) const
IexpValStr operator()(TVarExpNotMacro const& val) const
{
processNotMacro(val);
return processNotMacro(val);
}
void operator()(TMacroUsage const& val) const
IexpValStr operator()(TMacroUsage const& val) const
{
std::map<std::string, ERM::TVarExpNotMacro>::const_iterator it =
env->ermGlobalEnv->macroBindings.find(val .macro);
if(it == env->ermGlobalEnv->macroBindings.end())
throw EUsageOfUndefinedMacro(val.macro);
else
processNotMacro(it->second);
return processNotMacro(it->second);
}
};
template<typename T>
struct LVL1IexpDisemboweler : boost::static_visitor<>
struct LVL1IexpDisemboweler : boost::static_visitor<IexpValStr>
{
T * inout;
IexpDisemboweler::EDir dir;
/*const*/ ERMExecEnvironment * env;
LVL1IexpDisemboweler(T * in_out, /*const*/ ERMExecEnvironment * _env, IexpDisemboweler::EDir _dir)
: inout(in_out), env(_env), dir(_dir) //writes value to given var
LVL1IexpDisemboweler(/*const*/ ERMExecEnvironment * _env, IexpDisemboweler::EDir _dir)
: env(_env), dir(_dir) //writes value to given var
{}
void operator()(int const & constant) const
IexpValStr operator()(int const & constant) const
{
if(dir == IexpDisemboweler::GET)
{
*inout = constant;
IexpValStr ret;
ret.val.val = constant;
ret.type = IexpValStr::INT;
}
else
{
throw EIexpProblem("Cannot set a constant!");
}
}
void operator()(TVarExp const & var) const
IexpValStr operator()(TVarExp const & var) const
{
boost::apply_visitor(LVL2IexpDisemboweler<T>(inout, env, dir), var);
return boost::apply_visitor(LVL2IexpDisemboweler(env, dir), var);
}
};
template<typename T>
T ERMInterpreter::getIexp( const ERM::TIexp & iexp, /*const*/ Trigger * trig /*= NULL*/, /*const*/ FunctionLocalVars * fun /*= NULL*/) const
IexpValStr ERMInterpreter::getIexp( const ERM::TIexp & iexp, /*const*/ Trigger * trig /*= NULL*/, /*const*/ FunctionLocalVars * fun /*= NULL*/) const
{
T ret;
ERMExecEnvironment env(ermGlobalEnv, trig, fun);
boost::apply_visitor(LVL1IexpDisemboweler<T>(&ret, &env, IexpDisemboweler::GET), iexp);
return ret;
return boost::apply_visitor(LVL1IexpDisemboweler(&env, IexpDisemboweler::GET), iexp);
}
void ERMInterpreter::executeTriggerType( VERMInterpreter::TriggerType tt, bool pre, const std::map< int, std::vector<int> > & identifier )
@ -936,6 +958,131 @@ ERM::Ttrigger ERMInterpreter::retrieveTrigger( ERM::TLine line )
throw ELineProblem("Given line is not an ERM trigger!");
}
template<typename T>
bool compareExp(const T & lhs, const T & rhs, std::string op)
{
if(op == "<")
{
return lhs < rhs;
}
else if(op == ">")
{
return lhs > rhs;
}
else if(op == ">=" || op == "=>")
{
return lhs >= rhs;
}
else if(op == "<=" || op == "=<")
{
return lhs <= rhs;
}
else if(op == "==")
{
return lhs == rhs;
}
else if(op == "<>" || op == "><")
{
return lhs != rhs;
}
else
throw EScriptExecError(std::string("Wrong comparison sign: ") + op);
}
struct ConditionDisemboweler : boost::static_visitor<bool>
{
ConditionDisemboweler(ERMInterpreter * _ei) : ei(_ei)
{}
bool operator()(TComparison const & cmp) const
{
IexpValStr lhs = ei->getIexp(cmp.lhs),
rhs = ei->getIexp(cmp.rhs);
switch (lhs.type)
{
case IexpValStr::FLOATVAR:
switch (rhs.type)
{
case IexpValStr::FLOATVAR:
return compareExp(*lhs.val.flvar, *rhs.val.flvar, cmp.compSign);
break;
default:
throw EScriptExecError("Incompatible types for comparison");
}
break;
case IexpValStr::INT:
switch (rhs.type)
{
case IexpValStr::INT:
return compareExp(lhs.val.val, rhs.val.val, cmp.compSign);
break;
case IexpValStr::INTVAR:
return compareExp(lhs.val.val, *rhs.val.integervar, cmp.compSign);
default:
throw EScriptExecError("Incompatible types for comparison");
}
break;
case IexpValStr::INTVAR:
switch (rhs.type)
{
case IexpValStr::INT:
return compareExp(*lhs.val.integervar, rhs.val.val, cmp.compSign);
break;
case IexpValStr::INTVAR:
return compareExp(*lhs.val.integervar, *rhs.val.integervar, cmp.compSign);
default:
throw EScriptExecError("Incompatible types for comparison");
}
break;
case IexpValStr::STRINGVAR:
switch (rhs.type)
{
case IexpValStr::STRINGVAR:
return compareExp(*lhs.val.stringvar, *rhs.val.stringvar, cmp.compSign);
break;
default:
throw EScriptExecError("Incompatible types for comparison");
}
break;
default:
throw EScriptExecError("Wrong type of left iexp!");
}
//we should never reach this place
}
bool operator()(int const & flag) const
{
return ei->ermGlobalEnv->getFlag(flag);
}
private:
ERMInterpreter * ei;
};
bool ERMInterpreter::checkCondition( ERM::Tcondition cond )
{
bool ret = boost::apply_visitor(ConditionDisemboweler(this), cond.cond);
if(cond.rhs.is_initialized())
{ //taking care of rhs expression
bool rhs = checkCondition(cond.rhs.get().get());
switch (cond.ctype)
{
case '&':
ret &= rhs;
break;
case '|':
ret |= rhs;
break;
case 'X':
ret ^= rhs;
break;
default:
throw EInterpreterProblem(std::string("Strange - wrong condition connection (") + cond.ctype + ") !");
break;
}
}
return ret;
}
const std::string ERMInterpreter::triggerSymbol = "trigger";
const std::string ERMInterpreter::postTriggerSymbol = "postTrigger";
const std::string ERMInterpreter::defunSymbol = "defun";
@ -953,7 +1100,16 @@ struct TriggerIdMatchHelper : boost::static_visitor<>
void operator()(TIexp const& iexp) const
{
ret = interpreter->getIexp<int>(iexp, trig);
IexpValStr val = interpreter->getIexp(iexp, trig);
switch(val.type)
{
case IexpValStr::INT:
ret = val.val.val;
case IexpValStr::INTVAR:
ret = *val.val.integervar;
default:
throw EScriptExecError("Incompatible i-exp type!");
}
}
void operator()(TArithmeticOp const& arop) const
{
@ -963,6 +1119,8 @@ struct TriggerIdMatchHelper : boost::static_visitor<>
bool TriggerIdentifierMatch::tryMatch( Trigger * interptrig ) const
{
bool ret = true;
const ERM::Ttrigger & trig = ERMInterpreter::retrieveTrigger(ermEnv->retrieveLine(interptrig->line));
if(trig.identifier.is_initialized())
{
@ -970,7 +1128,7 @@ bool TriggerIdentifierMatch::tryMatch( Trigger * interptrig ) const
ERM::Tidentifier tid = trig.identifier.get();
std::map< int, std::vector<int> >::const_iterator it = matchToIt.find(tid.size());
if(it == matchToIt.end())
return false;
ret = false;
else
{
const std::vector<int> & pattern = it->second;
@ -980,17 +1138,29 @@ bool TriggerIdentifierMatch::tryMatch( Trigger * interptrig ) const
boost::apply_visitor(TriggerIdMatchHelper(val, ermEnv, interptrig), tid[g]);
if(pattern[g] != val)
{
return false;
ret = false;
}
}
return true;
ret = true;
}
}
else
{
if(allowNoIdetifier)
ret = allowNoIdetifier;
}
//check condition
if(ret)
{
if(trig.condition.is_initialized())
{
return ermEnv->checkCondition(trig.condition.get());
}
else //no condition
return true;
}
else
return false;
}
VERMInterpreter::ERMEnvironment::ERMEnvironment()
@ -1004,6 +1174,36 @@ VERMInterpreter::ERMEnvironment::ERMEnvironment()
flags[g] = false;
}
int & VERMInterpreter::ERMEnvironment::getQuickVar( const char letter )
{
assert(letter >= 'f' && letter <= 't'); //it should be check by another function, just making sure here
return quickVars[letter - 'f'];
}
int & VERMInterpreter::ERMEnvironment::getStandardVar( int num )
{
if(num < 1 || num > NUM_STANDARDS)
throw EScriptExecError("Number of standard variable out of bounds");
return standardVars[num-1];
}
std::string & VERMInterpreter::ERMEnvironment::getZVar( int num )
{
if(num < 1 || num > NUM_STRINGS)
throw EScriptExecError("Number of string variable out of bounds");
return strings[num-1];
}
bool & VERMInterpreter::ERMEnvironment::getFlag( int num )
{
if(num < 1 || num > NUM_FLAGS)
throw EScriptExecError("Number of flag out of bounds");
return flags[num-1];
}
VERMInterpreter::TriggerLocalVars::TriggerLocalVars()
{
for(int g=0; g<EVAR_NUM; ++g)
@ -1012,6 +1212,23 @@ VERMInterpreter::TriggerLocalVars::TriggerLocalVars()
yvar[g] = 0;
}
double & VERMInterpreter::TriggerLocalVars::getEvar( int num )
{
num = -num;
if(num < 1 || num > EVAR_NUM)
throw EScriptExecError("Number of trigger local floating point variable out of bounds");
return evar[num-1];
}
int & VERMInterpreter::TriggerLocalVars::getYvar( int num )
{
if(num < 1 || num > YVAR_NUM)
throw EScriptExecError("Number of trigger local variable out of bounds");
return yvar[num-1];
}
bool VERMInterpreter::Environment::isBound( const std::string & name, bool globalOnly ) const
{
std::map<std::string, TVOption>::const_iterator it = symbols.find(name);
@ -1064,3 +1281,36 @@ bool VERMInterpreter::Environment::unbind( const std::string & name, EUnbindMode
//neither bound nor have lexical parent
return false;
}
int & VERMInterpreter::FunctionLocalVars::getParam( int num )
{
if(num < 1 || num > NUM_PARAMETERS)
throw EScriptExecError("Number of parameter out of bounds");
return params[num-1];
}
int & VERMInterpreter::FunctionLocalVars::getLocal( int num )
{
if(num < 1 || num > NUM_LOCALS)
throw EScriptExecError("Number of local variable out of bounds");
return locals[num-1];
}
std::string & VERMInterpreter::FunctionLocalVars::getString( int num )
{
num = -num; //we deal with negative indices
if(num < 1 || num > NUM_PARAMETERS)
throw EScriptExecError("Number of function local string variable out of bounds");
return strings[num-1];
}
double & VERMInterpreter::FunctionLocalVars::getFloat( int num )
{
if(num < 1 || num > NUM_FLOATINGS)
throw EScriptExecError("Number of float var out of bounds");
return floats[num-1];
}

View File

@ -125,25 +125,33 @@ namespace VERMInterpreter
struct TriggerLocalVars
{
static const int EVAR_NUM = 100; //number of evar locals
double evar[EVAR_NUM]; //negative indices
static const int YVAR_NUM = 100; //number of yvar locals
int yvar[YVAR_NUM];
TriggerLocalVars();
double & getEvar(int num);
int & getYvar(int num);
private:
double evar[EVAR_NUM]; //negative indices
int yvar[YVAR_NUM];
};
struct FunctionLocalVars
{
static const int NUM_PARAMETERS = 16; //number of function parameters
int params[NUM_PARAMETERS]; //x-vars
static const int NUM_LOCALS = 100;
int locals[NUM_LOCALS]; //y-vars
static const int NUM_STRINGS = 10;
std::string strings[NUM_STRINGS]; //z-vars (negative indices)
static const int NUM_FLOATINGS = 100;
int & getParam(int num);
int & getLocal(int num);
std::string & getString(int num);
double & getFloat(int num);
private:
int params[NUM_PARAMETERS]; //x-vars
int locals[NUM_LOCALS]; //y-vars
std::string strings[NUM_STRINGS]; //z-vars (negative indices)
double floats[NUM_FLOATINGS]; //e-vars (positive indices)
};
@ -151,22 +159,22 @@ namespace VERMInterpreter
{
ERMEnvironment();
static const int NUM_QUICKS = 't' - 'f' + 1; //it should be 15
int quickVars[NUM_QUICKS]; //referenced by letter ('f' to 't' inclusive)
int & getQuickVar(const char letter)
{
assert(letter >= 'f' && letter <= 't'); //it should be check by another function, just makign sure here
return quickVars[letter - 'f'];
}
int & getQuickVar(const char letter);
int & getStandardVar(int num);
std::string & getZVar(int num);
bool & getFlag(int num);
static const int NUM_STANDARDS = 1000;
int standardVars[NUM_STANDARDS]; //v-vars
static const int NUM_STRINGS = 1000;
std::string strings[NUM_STRINGS]; //z-vars (positive indices)
std::map<std::string, ERM::TVarExpNotMacro> macroBindings;
static const int NUM_FLAGS = 1000;
private:
int quickVars[NUM_QUICKS]; //referenced by letter ('f' to 't' inclusive)
int standardVars[NUM_STANDARDS]; //v-vars
std::string strings[NUM_STRINGS]; //z-vars (positive indices)
bool flags[NUM_FLAGS];
};
@ -294,11 +302,24 @@ struct TriggerIdentifierMatch
bool tryMatch(VERMInterpreter::Trigger * interptrig) const;
};
struct IexpValStr
{
union
{
int val;
int * integervar;
double * flvar;
std::string * stringvar;
} val;
enum {WRONGVAL, INT, INTVAR, FLOATVAR, STRINGVAR} type;
};
class ERMInterpreter
{
friend class ScriptScanner;
friend class TriggerIdMatchHelper;
friend class TriggerIdentifierMatch;
friend class ConditionDisemboweler;
std::vector<VERMInterpreter::FileInfo*> files;
std::vector< VERMInterpreter::FileInfo* > fileInfos;
@ -314,7 +335,7 @@ class ERMInterpreter
template<typename T> void setIexp(const ERM::TIexp & iexp, const T & val, VERMInterpreter::Trigger * trig = NULL);
template<typename T> T getIexp(const ERM::TIexp & iexp, /*const*/ VERMInterpreter::Trigger * trig = NULL, /*const*/ VERMInterpreter::FunctionLocalVars * fun = NULL) const;
IexpValStr getIexp(const ERM::TIexp & iexp, /*const*/ VERMInterpreter::Trigger * trig = NULL, /*const*/ VERMInterpreter::FunctionLocalVars * fun = NULL) const;
static const std::string triggerSymbol, postTriggerSymbol, defunSymbol;
@ -333,4 +354,5 @@ public:
void scanScripts(); //scans for functions, triggers etc.
ERMInterpreter();
bool checkCondition( ERM::Tcondition cond );
};