Ver código fonte

Merge pull request #690 from nullkiller/erm-fix-vr

ERM: fix string concatenations and bit operations
Alexander Shishkin 4 anos atrás
pai
commit
9e422a1664

+ 77 - 14
scripting/erm/ERMInterpreter.cpp

@@ -651,6 +651,22 @@ namespace ERMConverter
 		}
 	};
 
+	struct VR_X : public GetBodyOption
+	{
+		VR_X()
+		{
+		}
+
+		using GetBodyOption::operator();
+
+		std::string operator()(const TIexp & cmp) const override
+		{
+			Variable p = boost::apply_visitor(LVL1IexpToVar(), cmp);
+
+			return p.str();
+		}
+	};
+
 	struct VR : public Receiver
 	{
 		Variable v;
@@ -677,15 +693,12 @@ namespace ERMConverter
 			case '|':
 				opcode = "bit.bor";
 				break;
-			case 'X':
-				opcode = "bit.bxor";
-				break;
 			default:
 				throw EInterpreterError("Wrong opcode in VR logic expression!");
 				break;
 			}
 
-			boost::format fmt("%s = %s %s(%s, %s)");
+			boost::format fmt("%s = %s(%s, %s)");
 			fmt % v.str() % opcode % v.str() % rhs.str();
 			putLine(fmt.str());
 		}
@@ -699,6 +712,8 @@ namespace ERMConverter
 			switch (trig.opcode)
 			{
 			case '+':
+				opcode = v.name[0] == 'z' ? ".." : "+";
+				break;
 			case '-':
 			case '*':
 			case '%':
@@ -759,10 +774,59 @@ namespace ERMConverter
 					putLine(fmt.str());
 				}
 				break;
+			case 'U':
+				{
+					if(!trig.params.is_initialized() || trig.params.get().size() != 1)
+						throw EScriptExecError("VR:H/U need 1 parameter!");
+
+					std::string opt = boost::apply_visitor(VR_S(), trig.params.get()[0]);
+					boost::format fmt("ERM.VR(%s):%c(%s)");
+					fmt % v.str() % (trig.optionCode) % opt;
+					putLine(fmt.str());
+				}
+				break;
 			case 'M': //string operations
 				{
-					//TODO
-					putLine("--VR:M not implemented");
+					if(!trig.params.is_initialized() || trig.params.get().size() < 2)
+						throw EScriptExecError("VR:M needs at least 2 parameters!");
+
+					std::string opt = boost::apply_visitor(VR_X(), trig.params.get()[0]);
+					int paramIndex = 1;
+
+					if(opt == "3")
+					{
+						boost::format fmt("%s = ERM.VR(%s):M3(");
+						fmt % v.str() % v.str();
+						put(fmt.str());
+					}
+					else
+					{
+						auto target = boost::apply_visitor(VR_X(), trig.params.get()[paramIndex++]);
+
+						boost::format fmt("%s = ERM.VR(%s):M%s(");
+						fmt % target % v.str() % opt;
+						put(fmt.str());
+					}
+					
+					for(int i = paramIndex; i < trig.params.get().size(); i++)
+					{
+						opt = boost::apply_visitor(VR_X(), trig.params.get()[i]);
+						if(i > paramIndex) put(",");
+						put(opt);
+					}
+					
+					putLine(")");
+				}
+				break;
+			case 'X': //bit xor
+				{
+					if(!trig.params.is_initialized() || trig.params.get().size() != 1)
+						throw EScriptExecError("VR:X option takes exactly 1 parameter!");
+
+					std::string opt = boost::apply_visitor(VR_X(), trig.params.get()[0]);
+
+					boost::format fmt("%s = bit.bxor(%s, %s)");
+					fmt % v.str() % v.str() % opt;putLine(fmt.str());
 				}
 				break;
 			case 'R': //random variables
@@ -789,16 +853,15 @@ namespace ERMConverter
 					putLine("--VR:T not implemented");
 				}
 				break;
-			case 'U': //search for a substring
-				{
-					//TODO
-					putLine("--VR:U not implemented");
-				}
-				break;
 			case 'V': //convert string to value
 				{
-					//TODO
-					putLine("--VR:V not implemented");
+					if(!trig.params.is_initialized() || trig.params.get().size() != 1)
+						throw EScriptExecError("VR:V option takes exactly 1 parameter!");
+
+					std::string opt = boost::apply_visitor(VR_X(), trig.params.get()[0]);
+					boost::format fmt("%s = tostring(%s)");
+					fmt % v.str() % opt;
+					putLine(fmt.str());
 				}
 				break;
 			default:

+ 1 - 2
scripting/erm/ERMParser.cpp

@@ -367,8 +367,7 @@ namespace ERM
 			trigger %= cmdName >> -identifier >> -condition > qi::lit(";"); /////
 			string %= qi::lexeme['^' >> *(qi::char_ - '^') >> '^'];
 
-//			VRLogic %= qi::char_("&|X") >> iexp;
-			VRLogic %= qi::char_("&") >> iexp;
+			VRLogic %= qi::char_("&|") >> iexp;
 			VRarithmetic %= qi::char_("+*:/%-") >> iexp;
 			semiCompare %= +qi::char_("<=>") >> iexp;
 			curStr %= iexp >> string;

+ 56 - 0
scripts/lib/erm/VR.lua

@@ -18,5 +18,61 @@ function VR:H(flagIndex)
 	self.ERM.F[flagIndex] = v ~= ''
 end
 
+function VR:U(subString)
+	self.ERM.F['1'] = string.find(self.v, subString) > 0
+end
+
+function VR:M1(startIndex, length)
+	return string.sub(self.v, startIndex - 1, startIndex - 1 + length)
+end
+
+function VR:M2(wordIndex)
+	local words = string.gmatch(self.v, "[^%s]+")
+	local i = 0
+
+	for w in words do
+		if i == wordIndex then
+			return w
+		end
+		i = i + 1
+	end
+end
+
+function VR:M3(val, radix)
+	radix = radix or 10
+	
+	if(type(val) ~= "number") then
+		error("The first parameter should be of numeric type")
+	end
+	
+	if(type(radix) ~= "number") then
+		error("The second parameter should be of numeric type. Default value is 10.")
+	end
+	
+	if radix == 10 then
+		return tostring(val)
+	elseif radix == 16 then
+		return string.format("%x", val)
+	else
+		error("The second parameter value is invalid. Only 10 and 16 radix are supported for now.")
+	end
+end
+
+function VR:M4()
+	return string.len(self.v)
+end
+
+function VR:M5()
+	local firstPos = string.find(str, "[^%s]+")
+	
+	return firstPos
+end
+
+function VR:M6()
+	local lastPos = 1 + string.len(self.v) - string.find(string.reverse(self.v), "[^%s]+")
+	
+	return lastPos
+end
+
 
 return VR

+ 18 - 0
test/erm/ERM_VR.cpp

@@ -73,6 +73,24 @@ TEST_F(ERM_VR, H)
 	EXPECT_EQ(f["202"], JsonUtils::boolNode(false)) << actualState.toJson(true);
 }
 
+TEST_F(ERM_VR, U)
+{
+	std::stringstream source;
+	source << "VERM" << std::endl;
+	source << "!?PI;" << std::endl;
+	source << "!!VRz100:S^Test!^;" << std::endl;
+	source << "!!VRz101:S^est^;" << std::endl;
+	source << "!!VRz100:Uz101;" << std::endl;
+
+	JsonNode actualState = runScript(VLC->scriptHandler->erm, source.str());
+
+	SCOPED_TRACE("\n" + subject->code);
+
+	const JsonNode & f = actualState["ERM"]["F"];
+
+	EXPECT_EQ(f["1"], JsonUtils::boolNode(true)) << actualState.toJson(true);
+}
+
 }
 }
 

+ 213 - 5
test/erm/interpretter/ERM_VR.cpp

@@ -60,12 +60,220 @@ TEST(ERM_VR_C, SetIntegers_ShouldGenerateSetStatements)
 
 TEST(ERM_VR_C, GetInteger_ShouldGenerateSetStatement)
 {
-	LuaStrings lua = ErmRunner::convertErmToLua({
-		"!#VRv100:C10/20/40;",
-		"!#VRv100:C?v1;"});
+	LuaStrings lua = ErmRunner::convertErmToLua({"!#VRv100:C?v1;"});
+
+	ASSERT_EQ(lua.lines.size(), 9) << lua.text;
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE], "v['1'] = v['100']");
+}
+
+TEST(ERM_VR_H, CheckEmptyString_ShouldGenerateCheckAndSetStatement)
+{
+	LuaStrings lua = ErmRunner::convertErmToLua({"!#VRz101:H302;"});
+
+	ASSERT_EQ(lua.lines.size(), 9) << lua.text;
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE], "ERM.VR(z['101']):H('302')");
+}
+/* should it work?
+TEST(ERM_VR_H, CheckEmptyStringWithFlagIndexInVariable_ShouldGenerateCheckAndSetStatement)
+{
+	LuaStrings lua = ErmRunner::convertErmToLua({"!#VRz101:Hy1;"});
+
+	ASSERT_EQ(lua.lines.size(), 9) << lua.text;
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE], "ERM.VR(z['101']):H(y['1'])");
+}
+*/
+TEST(ERM_VR_M1, AnyString_ShouldGenerateSubstringAndSetStatement)
+{
+	LuaStrings lua = ErmRunner::convertErmToLua({"!#VRz101:M1/z102/2/5;"});
+
+	ASSERT_EQ(lua.lines.size(), 9) << lua.text;
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE], "z['102'] = ERM.VR(z['101']):M1(2,5)");
+}
+
+TEST(ERM_VR_M1, WithVariables_ShouldGenerateSubstringAndSetStatement)
+{
+	LuaStrings lua = ErmRunner::convertErmToLua({"!#VRz101:M1/z102/y1/y2;"});
+
+	ASSERT_EQ(lua.lines.size(), 9) << lua.text;
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE], "z['102'] = ERM.VR(z['101']):M1(y['1'],y['2'])");
+}
+
+TEST(ERM_VR_M2, AnyString_ShouldGenerateWordSplitAndSetStatement)
+{
+	LuaStrings lua = ErmRunner::convertErmToLua({"!#VRz101:M2/z102/2;"});
+
+	ASSERT_EQ(lua.lines.size(), 9) << lua.text;
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE], "z['102'] = ERM.VR(z['101']):M2(2)");
+}
+
+TEST(ERM_VR_M3, AnyInteger_ShouldGenerateIntToStringConversionAndSetStatement)
+{
+	LuaStrings lua = ErmRunner::convertErmToLua({"!#VRz101:M3/102/16;"});
+
+	ASSERT_EQ(lua.lines.size(), 9) << lua.text;
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE], "z['101'] = ERM.VR(z['101']):M3(102,16)");
+}
+//V
+TEST(ERM_VR_M3, IntegerVariable_ShouldGenerateIntToStringConversionAndSetStatement)
+{
+	LuaStrings lua = ErmRunner::convertErmToLua({"!#VRz101:M3/v1/10;"});
+
+	ASSERT_EQ(lua.lines.size(), 9) << lua.text;
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE], "z['101'] = ERM.VR(z['101']):M3(v['1'],10)");
+}
+
+TEST(ERM_VR_M4, AnyString_ShouldGenerateStringLengthAndSetStatement)
+{
+	LuaStrings lua = ErmRunner::convertErmToLua({"!#VRz101:M4/v2;"});
+
+	ASSERT_EQ(lua.lines.size(), 9) << lua.text;
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE], "v['2'] = ERM.VR(z['101']):M4()");
+}
+
+TEST(ERM_VR_M5, AnyString_ShouldGenerateFindFirstNonSpaceCharPositionAndSetStatement)
+{
+	LuaStrings lua = ErmRunner::convertErmToLua({"!#VRz101:M5/v2;"});
+
+	ASSERT_EQ(lua.lines.size(), 9) << lua.text;
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE], "v['2'] = ERM.VR(z['101']):M5()");
+}
+
+TEST(ERM_VR_M6, AnyString_ShouldGenerateFindLastNonSpaceCharPositionAndSetStatement)
+{
+	LuaStrings lua = ErmRunner::convertErmToLua({"!#VRz101:M6/v2;"});
+
+	ASSERT_EQ(lua.lines.size(), 9) << lua.text;
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE], "v['2'] = ERM.VR(z['101']):M6()");
+}
+
+TEST(ERM_VR_R, AnyVariable_ShouldGenerateRngAndSetStatement)
+{
+	LuaStrings lua = ErmRunner::convertErmToLua({"!#VRv1:R23;"});
+
+	ASSERT_EQ(lua.lines.size(), 9) << lua.text;
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE], "--VR:R not implemented");
+}
+
+TEST(ERM_VR_S_R, AnyVariable_ShouldGenerateRngAndSetStatement)
+{
+	LuaStrings lua = ErmRunner::convertErmToLua({"!#VRv1:R23;"});
+
+	ASSERT_EQ(lua.lines.size(), 9) << lua.text;
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE], "--VR:R not implemented");
+}
+
+TEST(ERM_VR_S, DynamicVariable_ShouldGenerateDynamicSetStatement)
+{
+	LuaStrings lua = ErmRunner::convertErmToLua({"!#VRv1:Svy3;"});
+
+	ASSERT_EQ(lua.lines.size(), 9) << lua.text;
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE], "v['1'] = v[tostring(y['3'])]");
+}
+
+TEST(ERM_VR_T, AnyVariable_ShouldGenerateTimeBasedRngAndSetStatement)
+{
+	LuaStrings lua = ErmRunner::convertErmToLua({"!#VRv1:T23;"});
+
+	ASSERT_EQ(lua.lines.size(), 9) << lua.text;
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE], "--VR:T not implemented");
+}
+
+TEST(ERM_VR_U, StringVariable_ShouldGenerateSubstringFindAndSetStatement)
+{
+	LuaStrings lua = ErmRunner::convertErmToLua({"!#VRz2:Uz3;"});
+
+	ASSERT_EQ(lua.lines.size(), 9) << lua.lines[0];
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE], "ERM.VR(z['2']):U(z['3'])");
+}
+
+TEST(ERM_VR_U, StringConstant_ShouldGenerateSubstringFindAndSetStatement)
+{
+	LuaStrings lua = ErmRunner::convertErmToLua({"!#VRz2:U^teest^;"});
+
+	ASSERT_EQ(lua.lines.size(), 9) << lua.text;
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE], "ERM.VR(z['2']):U([===[teest]===])");
+}
+
+TEST(ERM_VR_BIT, LogicalAndOperator_ShouldGenerateSetStatement)
+{
+	LuaStrings lua = ErmRunner::convertErmToLua({"!#VRv1:&8;"});
+
+	ASSERT_EQ(lua.lines.size(), 9) << lua.text;
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE], "v['1'] = bit.band(v['1'], 8)");
+}
+
+TEST(ERM_VR_BITOR, LogicalOrOperator_ShouldGenerateSetStatement)
+{
+	LuaStrings lua = ErmRunner::convertErmToLua({"!#VRv1:|8;"});
+
+	ASSERT_EQ(lua.lines.size(), 9) << lua.text;
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE], "v['1'] = bit.bor(v['1'], 8)");
+}
+
+TEST(ERM_VR_BITXOR, LogicalXorOperator_ShouldGenerateSetStatement)
+{
+	LuaStrings lua = ErmRunner::convertErmToLua({"!#VRv1:Xv2;"});
+
+	ASSERT_EQ(lua.lines.size(), 9) << lua.text;
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE], "v['1'] = bit.bxor(v['1'], v['2'])");
+}
+
+TEST(ERM_VR_PLUS, PlusOperator_ShouldGenerateSetStatement)
+{
+	LuaStrings lua = ErmRunner::convertErmToLua({"!#VRv1:+8;"});
+
+	ASSERT_EQ(lua.lines.size(), 9) << lua.text;
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE], "v['1'] = v['1'] + 8");
+}
+
+TEST(ERM_VR_PLUS, ConcatenationOperator_ShouldGenerateSetStatement)
+{
+	LuaStrings lua = ErmRunner::convertErmToLua({"!#VRz1:+z3;"});
+
+	ASSERT_EQ(lua.lines.size(), 9) << lua.text;
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE], "z['1'] = z['1'] .. z['3']");
+}
+
+TEST(ERM_VR_MINUS, MinusOperator_ShouldGenerateSetStatement)
+{
+	LuaStrings lua = ErmRunner::convertErmToLua({"!#VRv1:-v2;"});
+
+	ASSERT_EQ(lua.lines.size(), 9) << lua.text;
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE], "v['1'] = v['1'] - v['2']");
+}
+
+TEST(ERM_VR_MULT, MultiplicationOperator_ShouldGenerateSetStatement)
+{
+	LuaStrings lua = ErmRunner::convertErmToLua({"!#VRv1:*vy2;"});
 
-	ASSERT_EQ(lua.lines.size(), 12) << lua.text;
-	EXPECT_EQ(lua.lines[8], "v['1'] = v['100']");
+	ASSERT_EQ(lua.lines.size(), 9) << lua.text;
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE], "v['1'] = v['1'] * v[tostring(y['2'])]");
+}
+
+TEST(ERM_VR_DIV, DivisionOperator_ShouldGenerateSetStatement)
+{
+	LuaStrings lua = ErmRunner::convertErmToLua({"!#VRv1::8;"});
+
+	ASSERT_EQ(lua.lines.size(), 9) << lua.text;
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE], "v['1'] = v['1'] / 8");
+}
+
+TEST(ERM_VR_MOD, ModOperator_ShouldGenerateSetStatement)
+{
+	LuaStrings lua = ErmRunner::convertErmToLua({"!#VRv1:%7;"});
+
+	ASSERT_EQ(lua.lines.size(), 9) << lua.text;
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE], "v['1'] = v['1'] % 7");
+}
+
+TEST(ERM_VR_MINUS, Composition_ShouldGenerateSetStatement)
+{
+	LuaStrings lua = ErmRunner::convertErmToLua({"!#VRv1:S100 -v2 *v3;"});
+
+	ASSERT_EQ(lua.lines.size(), 11) << lua.text;
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE], "v['1'] = 100");
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE + 1], "v['1'] = v['1'] - v['2']");
+	EXPECT_EQ(lua.lines[ErmRunner::REGULAR_INSTRUCTION_FIRST_LINE + 2], "v['1'] = v['1'] * v['3']");
 }
 
 }