Skip to content

Commit d2490df

Browse files
authored
Merge pull request #480 from Pieter12345/lexer_minus_fix
Fixed invalid minus sign interpretation.
2 parents 35527f9 + b3f6bec commit d2490df

2 files changed

Lines changed: 146 additions & 16 deletions

File tree

src/main/java/com/laytonsmith/core/MethodScriptCompiler.java

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -798,23 +798,32 @@ public static TokenStream lex(String script, File file, boolean inPureMScript, b
798798

799799
// Convert "-" + number to -number if allowed.
800800
it.previous(); // Select 't' <--.
801-
if(it.hasPrevious()) {
801+
if(it.hasPrevious() && t.type == TType.UNKNOWN) {
802802
Token prev1 = it.previous(); // Select 'prev1' <--.
803-
if(it.hasPrevious()) {
804-
Token prev2 = it.previous(); // Select 'prev2' <--.
805-
if(t.type == TType.UNKNOWN && prev1.type.isPlusMinus() // Convert "± UNKNOWN".
806-
&& !prev2.type.isIdentifier() // Don't convert "number/string/var ± ...".
807-
&& prev2.type != TType.FUNC_END // Don't convert "func() ± ...".
808-
&& !IVAR_PATTERN.matcher(t.val()).matches() // Don't convert "± @var".
809-
&& !VAR_PATTERN.matcher(t.val()).matches()) { // Don't convert "± $var".
810-
// It is a negative/positive number: Absorb the sign.
811-
t.value = prev1.value + t.value;
812-
it.next(); // Select 'prev2' -->.
813-
it.next(); // Select 'prev1' -->.
814-
it.remove(); // Remove 'prev1'.
815-
} else {
816-
it.next(); // Select 'prev2' -->.
817-
it.next(); // Select 'prev1' -->.
803+
if(prev1.type.isPlusMinus()) {
804+
805+
// Find the first non-whitespace token before the '-'.
806+
Token prevNonWhitespace = null;
807+
while(it.hasPrevious()) {
808+
if(it.previous().type != TType.WHITESPACE) {
809+
prevNonWhitespace = it.next();
810+
break;
811+
}
812+
}
813+
while(it.next() != prev1) { // Skip until selection is at 'prev1 -->'.
814+
}
815+
816+
if(prevNonWhitespace != null) {
817+
// Convert "±UNKNOWN" if the '±' is used as a sign (and not an add/subtract operation).
818+
if(!prevNonWhitespace.type.isIdentifier() // Don't convert "number/string/var ± ...".
819+
&& prevNonWhitespace.type != TType.FUNC_END // Don't convert "func() ± ...".
820+
&& prevNonWhitespace.type != TType.RSQUARE_BRACKET // Don't convert "] ± ..." (arrays).
821+
&& !IVAR_PATTERN.matcher(t.val()).matches() // Don't convert "± @var".
822+
&& !VAR_PATTERN.matcher(t.val()).matches()) { // Don't convert "± $var".
823+
// It is a negative/positive number: Absorb the sign.
824+
t.value = prev1.value + t.value;
825+
it.remove(); // Remove 'prev1'.
826+
}
818827
}
819828
} else {
820829
it.next(); // Select 'prev1' -->.

src/test/java/com/laytonsmith/core/MethodScriptCompilerTest.java

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,4 +1118,125 @@ private void ambigousCommandRegistrationHelper(String cmd1, String cmd2, boolean
11181118
}
11191119
}
11201120

1121+
@Test
1122+
public void testMinusSignHandling() throws Exception {
1123+
// This tests a specific lexer part where token TType.MINUS ('-' sign) is merged with the next token
1124+
// based on whether the MINUS is used as a negation sign or a mathematical operator.
1125+
1126+
// "... - 2".
1127+
this.verifyExecute("msg(5 - 2)", "3");
1128+
this.verifyExecute("msg(5- 2)", "3");
1129+
this.verifyExecute("msg(5 -2)", "3");
1130+
this.verifyExecute("msg(5-2)", "3");
1131+
this.verifyExecute("msg(0x05 - 2)", "3");
1132+
this.verifyExecute("msg(0x05- 2)", "3");
1133+
this.verifyExecute("msg(0x05 -2)", "3");
1134+
this.verifyExecute("msg(0x05-2)", "3");
1135+
this.verifyExecute("@arr = array(5); msg(@arr[0] - 2)", "3");
1136+
this.verifyExecute("@arr = array(5); msg(@arr[0]- 2)", "3");
1137+
this.verifyExecute("@arr = array(5); msg(@arr[0] -2)", "3");
1138+
this.verifyExecute("@arr = array(5); msg(@arr[0]-2)", "3");
1139+
this.verifyExecute("@a = 5; msg(@a - 2)", "3");
1140+
this.verifyExecute("@a = 5; msg(@a- 2)", "3");
1141+
this.verifyExecute("@a = 5; msg(@a -2)", "3");
1142+
this.verifyExecute("@a = 5; msg(@a-2)", "3");
1143+
this.verifyExecute("msg(abs(5) - 2)", "3");
1144+
this.verifyExecute("msg(abs(5)- 2)", "3");
1145+
this.verifyExecute("msg(abs(5) -2)", "3");
1146+
this.verifyExecute("msg(abs(5)-2)", "3");
1147+
1148+
// "2 - ...".
1149+
this.verifyExecute("msg(2 - 0x05)", "-3");
1150+
this.verifyExecute("msg(2- 0x05)", "-3");
1151+
this.verifyExecute("msg(2 -0x05)", "-3");
1152+
this.verifyExecute("msg(2-0x05)", "-3");
1153+
this.verifyExecute("@arr = array(5); msg(2 - @arr[0])", "-3");
1154+
this.verifyExecute("@arr = array(5); msg(2- @arr[0])", "-3");
1155+
this.verifyExecute("@arr = array(5); msg(2 -@arr[0])", "-3");
1156+
this.verifyExecute("@arr = array(5); msg(2-@arr[0])", "-3");
1157+
this.verifyExecute("@a = 5; msg(2 - @a)", "-3");
1158+
this.verifyExecute("@a = 5; msg(2- @a)", "-3");
1159+
this.verifyExecute("@a = 5; msg(2 -@a)", "-3");
1160+
this.verifyExecute("@a = 5; msg(2-@a)", "-3");
1161+
this.verifyExecute("msg(2 - abs(5))", "-3");
1162+
this.verifyExecute("msg(2- abs(5))", "-3");
1163+
this.verifyExecute("msg(2 -abs(5))", "-3");
1164+
this.verifyExecute("msg(2-abs(5))", "-3");
1165+
1166+
// "- something".
1167+
this.verifyExecute("msg(-5)", "-5");
1168+
this.verifyExecute("msg(typeof(-5))", "int");
1169+
this.verifyExecute("msg(- 5)", "-5");
1170+
this.verifyExecute("msg(typeof(- 5))", "int");
1171+
this.verifyExecute("@a = 5; msg(-@a)", "-5");
1172+
this.verifyExecute("@a = 5; msg(typeof(-@a))", "int");
1173+
this.verifyExecute("@a = 5; msg(- @a)", "-5");
1174+
this.verifyExecute("@a = 5; msg(typeof(- @a))", "int");
1175+
}
1176+
1177+
@Test
1178+
public void testPlusSignHandling() throws Exception {
1179+
// This tests a specific lexer part where token TType.PLUS ('+' sign) is merged with the next token
1180+
// based on whether the PLUS is used as a positive sign or a mathematical operator.
1181+
1182+
// "... + 2".
1183+
this.verifyExecute("msg(5 + 2)", "7");
1184+
this.verifyExecute("msg(5+ 2)", "7");
1185+
this.verifyExecute("msg(5 +2)", "7");
1186+
this.verifyExecute("msg(5+2)", "7");
1187+
this.verifyExecute("msg(0x05 + 2)", "7");
1188+
this.verifyExecute("msg(0x05+ 2)", "7");
1189+
this.verifyExecute("msg(0x05 +2)", "7");
1190+
this.verifyExecute("msg(0x05+2)", "7");
1191+
this.verifyExecute("@arr = array(5); msg(@arr[0] + 2)", "7");
1192+
this.verifyExecute("@arr = array(5); msg(@arr[0]+ 2)", "7");
1193+
this.verifyExecute("@arr = array(5); msg(@arr[0] +2)", "7");
1194+
this.verifyExecute("@arr = array(5); msg(@arr[0]+2)", "7");
1195+
this.verifyExecute("@a = 5; msg(@a + 2)", "7");
1196+
this.verifyExecute("@a = 5; msg(@a+ 2)", "7");
1197+
this.verifyExecute("@a = 5; msg(@a +2)", "7");
1198+
this.verifyExecute("@a = 5; msg(@a+2)", "7");
1199+
this.verifyExecute("msg(abs(5) + 2)", "7");
1200+
this.verifyExecute("msg(abs(5)+ 2)", "7");
1201+
this.verifyExecute("msg(abs(5) +2)", "7");
1202+
this.verifyExecute("msg(abs(5)+2)", "7");
1203+
1204+
// "2 + ...".
1205+
this.verifyExecute("msg(2 + 0x05)", "7");
1206+
this.verifyExecute("msg(2+ 0x05)", "7");
1207+
this.verifyExecute("msg(2 +0x05)", "7");
1208+
this.verifyExecute("msg(2+0x05)", "7");
1209+
this.verifyExecute("@arr = array(5); msg(2 + @arr[0])", "7");
1210+
this.verifyExecute("@arr = array(5); msg(2+ @arr[0])", "7");
1211+
this.verifyExecute("@arr = array(5); msg(2 +@arr[0])", "7");
1212+
this.verifyExecute("@arr = array(5); msg(2+@arr[0])", "7");
1213+
this.verifyExecute("@a = 5; msg(2 + @a)", "7");
1214+
this.verifyExecute("@a = 5; msg(2+ @a)", "7");
1215+
this.verifyExecute("@a = 5; msg(2 +@a)", "7");
1216+
this.verifyExecute("@a = 5; msg(2+@a)", "7");
1217+
this.verifyExecute("msg(2 + abs(5))", "7");
1218+
this.verifyExecute("msg(2+ abs(5))", "7");
1219+
this.verifyExecute("msg(2 +abs(5))", "7");
1220+
this.verifyExecute("msg(2+abs(5))", "7");
1221+
1222+
// "+ something".
1223+
this.verifyExecute("msg(+5)", "5");
1224+
this.verifyExecute("msg(typeof(+5))", "int");
1225+
this.verifyExecute("msg(+ 5)", "5");
1226+
this.verifyExecute("msg(typeof(+ 5))", "int");
1227+
this.verifyExecute("@a = 5; msg(+@a)", "5");
1228+
this.verifyExecute("@a = 5; msg(typeof(+@a))", "int");
1229+
this.verifyExecute("@a = 5; msg(+ @a)", "5");
1230+
this.verifyExecute("@a = 5; msg(typeof(+ @a))", "int");
1231+
}
1232+
1233+
private void verifyExecute(String script, String expectedResponse) throws ConfigCompileException, ConfigCompileGroupException {
1234+
MCPlayer temp = this.env.getEnv(CommandHelperEnvironment.class).GetPlayer();
1235+
MCPlayer player = StaticTest.GetOnlinePlayer();
1236+
this.env.getEnv(CommandHelperEnvironment.class).SetPlayer(player);
1237+
MethodScriptCompiler.execute(MethodScriptCompiler.compile(
1238+
MethodScriptCompiler.lex(script, null, true)), this.env, null, null);
1239+
verify(player).sendMessage(expectedResponse);
1240+
this.env.getEnv(CommandHelperEnvironment.class).SetPlayer(temp);
1241+
}
11211242
}

0 commit comments

Comments
 (0)