浏览代码

* significantly improved verm interpreter - if, comparisons, lambda expressions are working!

mateuszb 14 年之前
父节点
当前提交
73a05d62fd
共有 3 个文件被更改,包括 419 次插入34 次删除
  1. 1 1
      AUTHORS
  2. 230 26
      lib/ERMInterpreter.cpp
  3. 188 7
      lib/ERMInterpreter.h

+ 1 - 1
AUTHORS

@@ -5,7 +5,7 @@ Micha
 maintenance, reverse engineering, general support.
 
 Mateusz B. aka Tow dragon,                <[email protected]>
-   * general suport, battle support, support for many Heroes 3 config files, reverse engineering
+   * general suport, battle support, support for many Heroes 3 config files, reverse engineering, ERM/VERM parser and interpreter
 
 Stefan Pavlov aka Ste,            <[email protected]>
    * minor fixes in pregame

+ 230 - 26
lib/ERMInterpreter.cpp

@@ -32,6 +32,7 @@ typedef int TUnusedType;
 using namespace boost::assign;
 
 ERMInterpreter *erm;
+Environment *topDyn;
 
 namespace ERMPrinter
 {
@@ -471,10 +472,11 @@ void ERMInterpreter::scanScripts()
 
 ERMInterpreter::ERMInterpreter()
 {
-	erm	 = this;
+	erm = this;
 	curFunc = NULL;
 	curTrigger = NULL;
 	globalEnv = new Environment();
+	topDyn = globalEnv;
 }
 
 void ERMInterpreter::executeTrigger( VERMInterpreter::Trigger & trig, int funNum /*= -1*/, std::vector<int> funParams/*=std::vector<int>()*/ )
@@ -2138,12 +2140,17 @@ int & VERMInterpreter::TriggerLocalVars::getYvar( int num )
 	return yvar[num-1];
 }
 
-bool VERMInterpreter::Environment::isBound( const std::string & name, bool globalOnly ) const
+bool VERMInterpreter::Environment::isBound( const std::string & name, EIsBoundMode mode ) const
 {
 	std::map<std::string, VOption>::const_iterator it = symbols.find(name);
-	if(globalOnly && parent)
+	if(mode == LOCAL_ONLY)
 	{
-		return parent->isBound(name, globalOnly);
+		return it != symbols.end();
+	}
+
+	if(mode == GLOBAL_ONLY && parent)
+	{
+		return parent->isBound(name, mode);
 	}
 
 	//we have it; if globalOnly is true, lexical parent is false here so we are global env
@@ -2152,7 +2159,7 @@ bool VERMInterpreter::Environment::isBound( const std::string & name, bool globa
 
 	//here, we don;t have it; but parent can have
 	if(parent)
-		return parent->isBound(name, globalOnly);
+		return parent->isBound(name, mode);
 
 	return false;
 }
@@ -2174,7 +2181,7 @@ VOption & VERMInterpreter::Environment::retrieveValue( const std::string & name
 
 bool VERMInterpreter::Environment::unbind( const std::string & name, EUnbindMode mode )
 {
-	if(isBound(name, false))
+	if(isBound(name, ANYWHERE))
 	{
 		if(symbols.find(name) != symbols.end()) //result of isBound could be from higher lexical env
 			symbols.erase(symbols.find(name));
@@ -2196,6 +2203,24 @@ void VERMInterpreter::Environment::localBind( std::string name, const VOption &
 	symbols[name] = sym;
 }
 
+void VERMInterpreter::Environment::setPatent( Environment * _parent )
+{
+	parent = _parent;
+}
+
+Environment * VERMInterpreter::Environment::getPatent() const
+{
+	return parent;
+}
+
+void VERMInterpreter::Environment::bindAtFirstHit( std::string name, const VOption & sym )
+{
+	if(isBound(name, Environment::LOCAL_ONLY) || !parent)
+		localBind(name, sym);
+	else
+		parent->bindAtFirstHit(name, sym);
+}
+
 int & VERMInterpreter::FunctionLocalVars::getParam( int num )
 {
 	if(num < 1 || num > NUM_PARAMETERS)
@@ -2426,6 +2451,10 @@ struct VOptionPrinter : boost::static_visitor<>
 	{
 		tlog4 << "--erm command (will be supported in future versions)--";
 	}
+	void operator()(VFunc const& opt) const
+	{
+		tlog4 << "function";
+	}
 };
 
 
@@ -2442,10 +2471,17 @@ struct VNodeEvaluator : boost::static_visitor<VOption>
 	VOption operator()(VNode const& opt) const
 	{
 		//otherwise...
-		return VNIL();
+		VNode tmpn(exp);
+		tmpn.children.car() = erm->eval(opt);
+		VFunc fun = getAs<VFunc>(tmpn.children.car().getAsItem());
+		return fun(tmpn.children.cdr());
 	}
 	VOption operator()(VSymbol const& opt) const
 	{
+		std::map<std::string, VFunc::Eopt> symToFunc = boost::assign::map_list_of
+			("<", VFunc::LT)("<=", VFunc::LE)(">", VFunc::GT)(">=", VFunc::GE)("+", VFunc::ADD)("-", VFunc::SUB)
+			("*", VFunc::MULT)("/", VFunc::DIV)("%", VFunc::MOD);
+
 		//check keywords
 		if(opt.text == "quote")
 		{
@@ -2474,6 +2510,20 @@ struct VNodeEvaluator : boost::static_visitor<VOption>
 					throw EVermScriptExecError("this if form needs at least three arguments");
 			}
 		}
+		else if(opt.text == "lambda")
+		{
+			if(exp.children.size() <= 2)
+			{
+				throw EVermScriptExecError("Too few arguments for lambda special form");
+			}
+			VFunc ret(exp.children.cdr().getAsCDR().getAsList());
+			VNode arglist = getAs<VNode>(exp.children[1]);
+			for(int g=0; g<arglist.children.size(); ++g)
+			{
+				ret.args.push_back(getAs<VSymbol>(arglist.children[g]));
+			}
+			return ret;
+		}
 		else if(opt.text == "print")
 		{
 			if(exp.children.size() == 2)
@@ -2490,7 +2540,13 @@ struct VNodeEvaluator : boost::static_visitor<VOption>
 			if(exp.children.size() != 3)
 				throw EVermScriptExecError("setq special form takes exactly 3 arguments");
 
-			env.localBind( getAs<std::string>(getAs<TLiteral>(exp.children[1]) ), exp.children[2]);
+			env.bindAtFirstHit( getAs<std::string>(getAs<TLiteral>(exp.children[1]) ), exp.children[2]);
+		}
+		//"apply" part of eval, a bit blurred in this implementation but this way it looks good too
+		else if(symToFunc.find(opt.text) != symToFunc.end())
+		{
+			VFunc f(symToFunc[opt.text]);
+			return f(erm->evalEach(exp.children.cdr()));
 		}
 
 
@@ -2504,6 +2560,10 @@ struct VNodeEvaluator : boost::static_visitor<VOption>
 	{
 		throw EVermScriptExecError("ERM command does not evaluate to a function");
 	}
+	VOption operator()(VFunc const& opt) const
+	{
+		return opt;
+	}
 };
 
 struct VEvaluator : boost::static_visitor<VOption>
@@ -2517,8 +2577,13 @@ struct VEvaluator : boost::static_visitor<VOption>
 	}
 	VOption operator()(VNode const& opt) const
 	{
-		VOption & car = const_cast<VNode&>(opt).children.car().getAsItem();
-		return boost::apply_visitor(VNodeEvaluator(env, const_cast<VNode&>(opt)), car);
+		if(opt.children.size() == 0)
+			return VNIL();
+		else
+		{
+			VOption & car = const_cast<VNode&>(opt).children.car().getAsItem();
+			return boost::apply_visitor(VNodeEvaluator(env, const_cast<VNode&>(opt)), car);
+		}
 	}
 	VOption operator()(VSymbol const& opt) const
 	{
@@ -2532,6 +2597,10 @@ struct VEvaluator : boost::static_visitor<VOption>
 	{
 		return VNIL();
 	}
+	VOption operator()(VFunc const& opt) const
+	{
+		return opt;
+	}
 };
 
 VOption ERMInterpreter::eval( VOption line, Environment * env /*= NULL*/ )
@@ -2540,7 +2609,9 @@ VOption ERMInterpreter::eval( VOption line, Environment * env /*= NULL*/ )
 // 		return;
 // 
 // 	VOption & car = line.children.car().getAsItem();
-	return boost::apply_visitor(VEvaluator(env ? *env : *globalEnv), line);
+	tlog1 << "\tevaluating ";
+	printVOption(line);
+	return boost::apply_visitor(VEvaluator(env ? *env : *topDyn), line);
 
 }
 
@@ -2585,7 +2656,7 @@ namespace VERMInterpreter
 
 	VNode::VNode( const ERM::TSymbol & sym )
 	{
-		children.car() = OptionConverterVisitor()(sym);
+		children.car() = VSymbol(sym.sym);
 		processModifierList(sym.symModifier);
 	}
 
@@ -2630,14 +2701,14 @@ namespace VERMInterpreter
 		switch (state)
 		{
 		case CAR:
-			if(parent.size() <= basePos)
-				parent.push_back(opt);
+			if(parent->size() <= basePos)
+				parent->push_back(opt);
 			else
-				parent[basePos] = opt;
+				(*parent)[basePos] = opt;
 			break;
-		case CDR:
-			parent.resize(basePos+2);
-			parent[basePos+1] = opt;
+		case NORM:
+			parent->resize(basePos+1);
+			(*parent)[basePos] = opt;
 			break;
 		default://should never happen
 			break;
@@ -2652,9 +2723,9 @@ namespace VERMInterpreter
 		case CAR:
 			//TODO: implement me
 			break;
-		case CDR:
-			parent.resize(basePos+1);
-			parent.insert(parent.begin()+basePos+1, opt.begin(), opt.end());
+		case NORM:
+			parent->resize(basePos+1);
+			parent->insert(parent->begin()+basePos, opt.begin(), opt.end());
 			break;
 		default://should never happen
 			break;
@@ -2668,11 +2739,11 @@ namespace VERMInterpreter
 	VOption & VermTreeIterator::getAsItem()
 	{
 		if(state == CAR)
-			return parent[basePos];
+			return (*parent)[basePos];
 		else
 			throw EInterpreterError("iterator is not in car state, cannot get as list");
 	}
-	VermTreeIterator VermTreeIterator::getAsList()
+	VermTreeIterator VermTreeIterator::getAsCDR()
 	{
 		VermTreeIterator ret = *this;
 		ret.basePos++;
@@ -2680,12 +2751,23 @@ namespace VERMInterpreter
 	}
 	VOption & VermTreeIterator::getIth( int i )
 	{
-		return parent[basePos + i];
+		return (*parent)[basePos + i];
 	}
 	size_t VermTreeIterator::size() const
 	{
-		return parent.size() - basePos;
+		return parent->size() - basePos;
+	}
+
+	VERMInterpreter::VOptionList VermTreeIterator::getAsList()
+	{
+		VOptionList ret;
+		for(int g = basePos; g<parent->size(); ++g)
+		{
+			ret.push_back((*parent)[g]);
+		}
+		return ret;
 	}
+
 	VOption OptionConverterVisitor::operator()( ERM::TVExp const& cmd ) const
 	{
 		return VNode(cmd);
@@ -2726,7 +2808,7 @@ namespace VERMInterpreter
 	VermTreeIterator VOptionList::cdr()
 	{
 		VermTreeIterator ret(*this);
-		ret.state = VermTreeIterator::CDR;
+		ret.basePos = 1;
 		return ret;
 	}
 
@@ -2737,4 +2819,126 @@ namespace VERMInterpreter
 		return ret;
 	}
 
+
+	VERMInterpreter::VOption VFunc::operator()( VermTreeIterator params )
+	{
+		switch(option)
+		{
+		case DEFAULT:
+			{
+				if(params.size() != args.size())
+				{
+					throw EVermScriptExecError("Expected " + boost::lexical_cast<std::string>(args.size()) + " arguments!");
+				}
+				IntroduceDynamicEnv dyn;
+				for(int i=0; i<args.size(); ++i)
+				{
+					topDyn->localBind(args[i].text, params.getIth(i));
+				}
+				//execute
+				VOptionList ret = erm->evalEach(body);
+				return ret[ret.size()-1];
+			}
+			break;
+		case LT:
+			{
+				if(params.size() != 2)
+					throw EVermScriptExecError("< special function takes exactly 2 arguments");
+
+				TLiteral lhs = getAs<TLiteral>(params.getIth(0)),
+					rhs = getAs<TLiteral>(params.getIth(1));
+				if(lhs < rhs)
+					return lhs;
+				else
+					return VNIL();
+			}
+			break;
+		case LE:
+			{
+				if(params.size() != 2)
+					throw EVermScriptExecError("<= special function takes exactly 2 arguments");
+
+				TLiteral lhs = getAs<TLiteral>(params.getIth(0)),
+					rhs = getAs<TLiteral>(params.getIth(1));
+				if(lhs <= rhs)
+					return lhs;
+				else
+					return VNIL();
+			}
+			break;
+		case GT:
+			{
+				if(params.size() != 2)
+					throw EVermScriptExecError("> special function takes exactly 2 arguments");
+
+				TLiteral lhs = getAs<TLiteral>(params.getIth(0)),
+					rhs = getAs<TLiteral>(params.getIth(1));
+				if(lhs >= rhs)
+					return lhs;
+				else
+					return VNIL();
+			}
+			break;
+		case GE:
+			{
+				if(params.size() != 2)
+					throw EVermScriptExecError(">= special function takes exactly 2 arguments");
+
+				TLiteral lhs = getAs<TLiteral>(params.getIth(0)),
+					rhs = getAs<TLiteral>(params.getIth(1));
+				if(lhs >= rhs)
+					return lhs;
+				else
+					return VNIL();
+			}
+			break;
+		default:
+			throw EInterpreterError("VFunc in forbidden mode!");
+			break;
+		}
+
+	}
+
+
+	IntroduceDynamicEnv::IntroduceDynamicEnv()
+	{
+		Environment * nen = new Environment();
+		nen->setPatent(topDyn);
+		topDyn = nen;
+	}
+
+	IntroduceDynamicEnv::~IntroduceDynamicEnv()
+	{
+		topDyn->setPatent(topDyn->getPatent());
+	}
+
+	bool operator<=(const TLiteral & t1, const TLiteral & t2)
+	{
+		if(t1.type() == t2.type())
+		{
+			return boost::apply_visitor(_opLEvis(t1), t2);
+		}
+		throw EVermScriptExecError("These types are incomparable!");
+	}
+	bool operator>(const TLiteral & t1, const TLiteral & t2)
+	{
+		if(t1.type() == t2.type())
+		{
+			return boost::apply_visitor(_opGTvis(t1), t2);
+		}
+		throw EVermScriptExecError("These types are incomparable!");
+	}
+	bool operator>=(const TLiteral & t1, const TLiteral & t2)
+	{
+		if(t1.type() == t2.type())
+		{
+			return boost::apply_visitor(_opGEvis(t1), t2);
+		}
+		throw EVermScriptExecError("These types are incomparable!");
+	}
+	void printVOption(const VOption & opt)
+	{
+		boost::apply_visitor(_VOPTPrinter(), opt);
+		tlog1 << "\n";
+	}
 }

+ 188 - 7
lib/ERMInterpreter.h

@@ -4,7 +4,6 @@
 #include "IGameEventsReceiver.h"
 #include "ERMScriptModule.h"
 
-
 /*
  * ERMInterpreter.h, part of VCMI engine
  *
@@ -304,8 +303,85 @@ namespace VERMInterpreter
 
 	typedef boost::variant<char, double, int, std::string> TLiteral;
 
+	//for operator <, but this one seems to be implemented in boost alerady
+	struct _opLTvis : boost::static_visitor<bool>
+	{
+		const TLiteral & lhs;
+		_opLTvis(const TLiteral & _lhs) : lhs(_lhs)
+		{}
+
+		template<typename OP>
+		bool operator()(OP const & rhs) const
+		{
+			return boost::get<OP>(lhs) < rhs;
+		}
+	};
+
+// 	bool operator<(const TLiteral & t1, const TLiteral & t2)
+// 	{
+// 		if(t1.type() == t2.type())
+// 		{
+// 			return boost::apply_visitor(_opLTvis(t1), t2);
+// 		}
+// 		throw EVermScriptExecError("These types are incomparable!");
+// 	}
 
-	typedef boost::variant<VNIL, boost::recursive_wrapper<VNode>, VSymbol, TLiteral, ERM::Tcommand> VOption; //options in v-expression, VNIl should be the default
+
+	//for operator <=
+	struct _opLEvis : boost::static_visitor<bool>
+	{
+		const TLiteral & lhs;
+		_opLEvis(const TLiteral & _lhs) : lhs(_lhs)
+		{}
+
+		template<typename OP>
+		bool operator()(OP const & rhs) const
+		{
+			return boost::get<OP>(lhs) <= rhs;
+		}
+	};
+
+	bool operator<=(const TLiteral & t1, const TLiteral & t2);
+
+	//operator >
+	struct _opGTvis : boost::static_visitor<bool>
+	{
+		const TLiteral & lhs;
+		_opGTvis(const TLiteral & _lhs) : lhs(_lhs)
+		{}
+
+		template<typename OP>
+		bool operator()(OP const & rhs) const
+		{
+			return boost::get<OP>(lhs) <= rhs;
+		}
+	};
+
+	bool operator>(const TLiteral & t1, const TLiteral & t2);
+
+	//operator >=
+
+	struct _opGEvis : boost::static_visitor<bool>
+	{
+		const TLiteral & lhs;
+		_opGEvis(const TLiteral & _lhs) : lhs(_lhs)
+		{}
+
+		template<typename OP>
+		bool operator()(OP const & rhs) const
+		{
+			return boost::get<OP>(lhs) <= rhs;
+		}
+	};
+
+	bool operator>=(const TLiteral & t1, const TLiteral & t2);
+
+	//VFunc
+	struct VFunc;
+
+	//VOption & stuff
+
+	typedef boost::variant<VNIL, boost::recursive_wrapper<VNode>, VSymbol, TLiteral, ERM::Tcommand, boost::recursive_wrapper<VFunc> > VOption; //options in v-expression, VNIl should be the default
 
 	template<typename T, typename SecType>
 	T& getAs(SecType & opt)
@@ -368,6 +444,7 @@ namespace VERMInterpreter
 // 		{}
 // 	};
 
+	
 
 	///main environment class, manages symbols
 	class Environment
@@ -377,7 +454,12 @@ namespace VERMInterpreter
 		Environment * parent;
 
 	public:
-		bool isBound(const std::string & name, bool globalOnly) const;
+		Environment() : parent(NULL)
+		{}
+		void setPatent(Environment * _parent);
+		Environment * getPatent() const;
+		enum EIsBoundMode {GLOBAL_ONLY, LOCAL_ONLY, ANYWHERE};
+		bool isBound(const std::string & name, EIsBoundMode mode) const;
 
 		VOption & retrieveValue(const std::string & name);
 
@@ -386,26 +468,51 @@ namespace VERMInterpreter
 		bool unbind(const std::string & name, EUnbindMode mode);
 
 		void localBind(std::string name, const VOption & sym);
+		void bindAtFirstHit(std::string name, const VOption & sym); //if symbol is locally defines, it gets overwritten; otherwise it is bind globally
+	};
+
+	//this class just introduces a new dynamic range when instantiated, nothing more
+	class IntroduceDynamicEnv
+	{
+	public:
+		IntroduceDynamicEnv();
+		~IntroduceDynamicEnv();
 	};
 
 	struct VermTreeIterator
 	{
 	private:
 		friend struct VOptionList;
-		VOptionList & parent;
-		enum Estate {CAR, CDR} state;
+		VOptionList * parent;
+		enum Estate {NORM, CAR} state;
 		int basePos; //car/cdr offset
 	public:
-		VermTreeIterator(VOptionList & _parent) : parent(_parent), basePos(0)
+		VermTreeIterator(VOptionList & _parent) : parent(&_parent), basePos(0), state(NORM)
+		{}
+		VermTreeIterator() : parent(NULL), state(NORM)
 		{}
 
 		VermTreeIterator & operator=(const VOption & opt);
 		VermTreeIterator & operator=(const std::vector<VOption> & opt);
 		VermTreeIterator & operator=(const VOptionList & opt);
 		VOption & getAsItem();
-		VermTreeIterator getAsList();
+		VermTreeIterator getAsCDR();
+		VOptionList getAsList();
 		VOption & getIth(int i);
 		size_t size() const;
+
+		VermTreeIterator& operator=(const VermTreeIterator & rhs)
+		{
+			if(this == &rhs)
+			{
+				return *this;
+			}
+			parent = rhs.parent;
+			state = rhs.state;
+			basePos = rhs.basePos;
+
+			return *this;
+		}
 	};
 
 	struct VOptionList : public std::vector<VOption>
@@ -418,6 +525,30 @@ namespace VERMInterpreter
 		bool isNil() const;
 	};
 
+	struct VFunc
+	{
+		enum Eopt {DEFAULT, LT, GT, LE, GE, ADD, SUB, MULT, DIV, MOD} option;
+		std::vector<VSymbol> args;
+		VOptionList body;
+		VFunc(const VOptionList & _body) : body(_body), option(DEFAULT)
+		{}
+		VFunc(Eopt func) : option(func)
+		{}
+		VFunc& operator=(const VFunc & rhs)
+		{
+			if(this == &rhs)
+			{
+				return *this;
+			}
+			args = rhs.args;
+			body = rhs.body;
+
+			return *this;
+		}
+
+		VOption operator()(VermTreeIterator params);
+	};
+
 	struct OptionConverterVisitor : boost::static_visitor<VOption>
 	{
 		VOption operator()(ERM::TVExp const& cmd) const;
@@ -441,6 +572,56 @@ namespace VERMInterpreter
 		VNode( const VOption & first, const VOptionList & rest); //merges given arguments into [a, rest];
 		void setVnode( const VOption & first, const VOptionList & rest);
 	};
+
+	//v printer
+	struct _VLITPrinter : boost::static_visitor<void>
+	{
+		void operator()(const std::string & par) const
+		{
+			tlog1 << "^" << par << "^";
+		}
+		template<typename T>
+		void operator()(const T & par) const
+		{
+			tlog1 << par;
+		}
+	};
+
+	struct _VOPTPrinter : boost::static_visitor<void>
+	{
+		void operator()(VNIL const& opt) const
+		{
+			tlog1 << "[]";
+		}
+		void operator()(VNode const& opt) const
+		{
+			tlog1 << "[";
+			for(int g=0; g<opt.children.size(); ++g)
+			{
+				boost::apply_visitor(_VOPTPrinter(), opt.children[g]);
+				tlog1 << " ";
+			}
+			tlog1 << "]";
+		}
+		void operator()(VSymbol const& opt) const
+		{
+			tlog1 << opt.text;
+		}
+		void operator()(TLiteral const& opt) const
+		{
+			boost::apply_visitor(_VLITPrinter(), opt);
+		}
+		void operator()(ERM::Tcommand const& opt) const
+		{
+			tlog1 << "--erm--";
+		}
+		void operator()(VFunc const& opt) const
+		{
+			tlog1 << "function";
+		}
+	};
+
+	void printVOption(const VOption & opt);
 }
 
 class ERMInterpreter;