Skip to content

Commit 5dd97a2

Browse files
authored
More lua fixes & improvements (#1160)
1 parent 009be45 commit 5dd97a2

11 files changed

Lines changed: 795 additions & 26 deletions

File tree

.github/workflows/build.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,14 @@ jobs:
8484
shell: bash
8585
run: ./gradlew packageSlimCompilerDist --no-daemon --stacktrace
8686

87+
- name: Prepare bundled Lua runtime (Linux)
88+
if: runner.os == 'Linux'
89+
shell: bash
90+
run: |
91+
if [[ -f src/test/resources/lua53 ]]; then
92+
chmod +x src/test/resources/lua53
93+
fi
94+
8795
- name: Run tests
8896
shell: bash
8997
run: ./gradlew test --no-daemon --stacktrace

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public class ExprTranslation {
1212

1313
public static final String TYPE_ID = "__typeId__";
1414
public static final String WURST_SUPERTYPES = "__wurst_supertypes";
15+
private static final String WURST_ABORT_THREAD_SENTINEL = "__wurst_abort_thread";
1516

1617
public static LuaExpr translate(ImAlloc e, LuaTranslator tr) {
1718
ImClass c = e.getClazz().getClassDef();
@@ -51,7 +52,7 @@ public static LuaExpr translate(ImFuncRef e, LuaTranslator tr) {
5152
LuaAst.LuaExprFunctionCall(tr.luaFunc.getFor(e.getFunc()), LuaAst.LuaExprlist(LuaAst.LuaExprVarAccess(tempDots)))))
5253
),
5354
// LuaAst.LuaLiteral("function(err) " + errorFuncName(tr) + "(tostring(err)) end")
54-
LuaAst.LuaLiteral("function(err) xpcall(function() " + callErrorFunc(tr, "tostring(err)") + " end, function(err2) BJDebugMsg(\"error reporting error: \" .. tostring(err2)) BJDebugMsg(\"while reporting: \" .. tostring(err)) end) end")
55+
LuaAst.LuaLiteral("function(err) if err == \"" + WURST_ABORT_THREAD_SENTINEL + "\" then return end xpcall(function() " + callErrorFunc(tr, "tostring(err)") + " end, function(err2) if err2 == \"" + WURST_ABORT_THREAD_SENTINEL + "\" then return end BJDebugMsg(\"error reporting error: \" .. tostring(err2)) BJDebugMsg(\"while reporting: \" .. tostring(err)) end) end")
5556
// unfortunately BJDebugMsg(debug.traceback()) is not working
5657
)
5758
),
@@ -73,6 +74,12 @@ private static String callErrorFunc(LuaTranslator tr, String msg) {
7374

7475
public static LuaExpr translate(ImFunctionCall e, LuaTranslator tr) {
7576
LuaFunction f = tr.luaFunc.getFor(e.getFunc());
77+
if ("I2S".equals(f.getName()) && isIntentionalThreadAbortCall(e)) {
78+
return LuaAst.LuaExprFunctionCallByName("error", LuaAst.LuaExprlist(
79+
LuaAst.LuaExprStringVal(WURST_ABORT_THREAD_SENTINEL),
80+
LuaAst.LuaExprIntVal("0")
81+
));
82+
}
7683
if (f.getName().equals(ImTranslator.$DEBUG_PRINT)) {
7784
f.setName("BJDebugMsg");
7885
} else if (f.getName().equals("I2S")) {
@@ -81,6 +88,27 @@ public static LuaExpr translate(ImFunctionCall e, LuaTranslator tr) {
8188
return LuaAst.LuaExprFunctionCall(f, tr.translateExprList(e.getArguments()));
8289
}
8390

91+
private static boolean isIntentionalThreadAbortCall(ImFunctionCall e) {
92+
if (e.getArguments().size() != 1) {
93+
return false;
94+
}
95+
ImExpr arg = e.getArguments().get(0);
96+
if (!(arg instanceof ImOperatorCall)) {
97+
return false;
98+
}
99+
ImOperatorCall op = (ImOperatorCall) arg;
100+
if (op.getOp() != WurstOperator.DIV_INT) {
101+
return false;
102+
}
103+
if (op.getArguments().size() != 2) {
104+
return false;
105+
}
106+
ImExpr left = op.getArguments().get(0);
107+
ImExpr right = op.getArguments().get(1);
108+
return (left instanceof ImIntVal && ((ImIntVal) left).getValI() == 1)
109+
&& (right instanceof ImIntVal && ((ImIntVal) right).getValI() == 0);
110+
}
111+
84112
public static LuaExpr translate(ImInstanceof e, LuaTranslator tr) {
85113
return
86114
LuaAst.LuaExprFunctionCall(tr.instanceOfFunction, LuaAst.LuaExprlist(

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaNatives.java

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ public class LuaNatives {
5050

5151
addNative(Collections.singletonList("S2I"), f -> {
5252
f.getParams().add(LuaAst.LuaVariable("x", LuaAst.LuaNoExpr()));
53-
f.getBody().add(LuaAst.LuaLiteral("return tonumber(x)"));
53+
f.getBody().add(LuaAst.LuaLiteral("local m = string.match(tostring(x), \"^[%+%-]?%d+\")"));
54+
f.getBody().add(LuaAst.LuaLiteral("if m then return tonumber(m) else return 0 end"));
5455
});
5556

5657
addNative("Player", f -> {
@@ -63,7 +64,11 @@ public class LuaNatives {
6364
f.getBody().add(LuaAst.LuaLiteral("return x.id"));
6465
});
6566

66-
addNative("GetRandomReal", f -> f.getBody().add(LuaAst.LuaLiteral("return math.random")));
67+
addNative("GetRandomReal", f -> {
68+
f.getParams().add(LuaAst.LuaVariable("l", LuaAst.LuaNoExpr()));
69+
f.getParams().add(LuaAst.LuaVariable("h", LuaAst.LuaNoExpr()));
70+
f.getBody().add(LuaAst.LuaLiteral("return l + math.random() * (h - l)"));
71+
});
6772

6873
addNative("GetRandomInt", f -> {
6974
f.getParams().add(LuaAst.LuaVariable("l", LuaAst.LuaNoExpr()));
@@ -90,30 +95,83 @@ public class LuaNatives {
9095
addNative("TriggerEvaluate", f -> {
9196
f.getParams().add(LuaAst.LuaVariable("t", LuaAst.LuaNoExpr()));
9297
f.getBody().add(LuaAst.LuaLiteral("for i,a in ipairs(t.actions) do a() end"));
98+
f.getBody().add(LuaAst.LuaLiteral("return true"));
9399
});
94100

95101
addNative("R2I", f -> {
96102
f.getParams().add(LuaAst.LuaVariable("x", LuaAst.LuaNoExpr()));
97-
f.getBody().add(LuaAst.LuaLiteral("return math.floor(x)"));
103+
f.getBody().add(LuaAst.LuaLiteral("return math.modf(x)"));
98104
});
99105

100-
addNative("InitHashtable", f -> f.getBody().add(LuaAst.LuaLiteral("return {}")));
106+
addNative(Arrays.asList("InitHashtable", "__wurst_InitHashtable"), f -> f.getBody().add(LuaAst.LuaLiteral("return {}")));
101107

102-
addNative(Arrays.asList("SaveInteger", "SaveBoolean", "SaveReal", "SaveStr", "SaveBoolean"), f -> {
108+
addNative(Arrays.asList(
109+
"SaveInteger", "SaveBoolean", "SaveReal", "SaveStr",
110+
"__wurst_SaveInteger", "__wurst_SaveBoolean", "__wurst_SaveReal", "__wurst_SaveStr"), f -> {
103111
f.getParams().add(LuaAst.LuaVariable("h", LuaAst.LuaNoExpr()));
104112
f.getParams().add(LuaAst.LuaVariable("p", LuaAst.LuaNoExpr()));
105113
f.getParams().add(LuaAst.LuaVariable("c", LuaAst.LuaNoExpr()));
106114
f.getParams().add(LuaAst.LuaVariable("i", LuaAst.LuaNoExpr()));
107115
f.getBody().add(LuaAst.LuaLiteral("if not h[p] then h[p] = {} end h[p][c] = i"));
108116
});
109117

110-
addNative(Arrays.asList("LoadInteger", "LoadBoolean", "LoadReal", "LoadStr", "LoadBoolean"), f -> {
118+
addNative(Arrays.asList(
119+
"LoadInteger", "LoadBoolean", "LoadReal", "LoadStr",
120+
"__wurst_LoadInteger", "__wurst_LoadBoolean", "__wurst_LoadReal", "__wurst_LoadStr"), f -> {
111121
f.getParams().add(LuaAst.LuaVariable("h", LuaAst.LuaNoExpr()));
112122
f.getParams().add(LuaAst.LuaVariable("p", LuaAst.LuaNoExpr()));
113123
f.getParams().add(LuaAst.LuaVariable("c", LuaAst.LuaNoExpr()));
114124
f.getBody().add(LuaAst.LuaLiteral("if not h[p] then return nil end return h[p][c]"));
115125
});
116126

127+
addNative(Arrays.asList(
128+
"HaveSavedInteger", "HaveSavedBoolean", "HaveSavedReal", "HaveSavedString",
129+
"__wurst_HaveSavedInteger", "__wurst_HaveSavedBoolean", "__wurst_HaveSavedReal", "__wurst_HaveSavedString"), f -> {
130+
f.getParams().add(LuaAst.LuaVariable("h", LuaAst.LuaNoExpr()));
131+
f.getParams().add(LuaAst.LuaVariable("p", LuaAst.LuaNoExpr()));
132+
f.getParams().add(LuaAst.LuaVariable("c", LuaAst.LuaNoExpr()));
133+
f.getBody().add(LuaAst.LuaLiteral("return h[p] ~= nil and h[p][c] ~= nil"));
134+
});
135+
136+
addNative(Arrays.asList("FlushChildHashtable", "__wurst_FlushChildHashtable"), f -> {
137+
f.getParams().add(LuaAst.LuaVariable("h", LuaAst.LuaNoExpr()));
138+
f.getParams().add(LuaAst.LuaVariable("p", LuaAst.LuaNoExpr()));
139+
f.getBody().add(LuaAst.LuaLiteral("h[p] = nil"));
140+
});
141+
142+
addNative(Arrays.asList("FlushParentHashtable", "__wurst_FlushParentHashtable"), f -> {
143+
f.getParams().add(LuaAst.LuaVariable("h", LuaAst.LuaNoExpr()));
144+
f.getBody().add(LuaAst.LuaLiteral("for k in pairs(h) do h[k] = nil end"));
145+
});
146+
147+
addNative(Arrays.asList(
148+
"RemoveSavedInteger", "RemoveSavedBoolean", "RemoveSavedReal", "RemoveSavedString",
149+
"__wurst_RemoveSavedInteger", "__wurst_RemoveSavedBoolean", "__wurst_RemoveSavedReal", "__wurst_RemoveSavedString"), f -> {
150+
f.getParams().add(LuaAst.LuaVariable("h", LuaAst.LuaNoExpr()));
151+
f.getParams().add(LuaAst.LuaVariable("p", LuaAst.LuaNoExpr()));
152+
f.getParams().add(LuaAst.LuaVariable("c", LuaAst.LuaNoExpr()));
153+
f.getBody().add(LuaAst.LuaLiteral("if h[p] then h[p][c] = nil end"));
154+
});
155+
156+
addNative("typeIdToTypeName", f -> {
157+
f.getParams().add(LuaAst.LuaVariable("typeId", LuaAst.LuaNoExpr()));
158+
f.getBody().add(LuaAst.LuaLiteral("return \"\""));
159+
});
160+
161+
addNative("maxTypeId", f -> {
162+
f.getBody().add(LuaAst.LuaLiteral("return 0"));
163+
});
164+
165+
addNative("instanceCount", f -> {
166+
f.getParams().add(LuaAst.LuaVariable("typeId", LuaAst.LuaNoExpr()));
167+
f.getBody().add(LuaAst.LuaLiteral("return 0"));
168+
});
169+
170+
addNative("maxInstanceCount", f -> {
171+
f.getParams().add(LuaAst.LuaVariable("typeId", LuaAst.LuaNoExpr()));
172+
f.getBody().add(LuaAst.LuaLiteral("return 0"));
173+
});
174+
117175
}
118176

119177
private static void addNative(String name, Consumer<LuaFunction> g) {

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@
2121
import static de.peeeq.wurstscript.translation.lua.translation.ExprTranslation.WURST_SUPERTYPES;
2222

2323
public class LuaTranslator {
24+
private static final Set<String> HASHTABLE_NATIVE_NAMES = new HashSet<>(Arrays.asList(
25+
"InitHashtable",
26+
"SaveInteger", "SaveBoolean", "SaveReal", "SaveStr",
27+
"LoadInteger", "LoadBoolean", "LoadReal", "LoadStr",
28+
"HaveSavedInteger", "HaveSavedBoolean", "HaveSavedReal", "HaveSavedString",
29+
"FlushChildHashtable", "FlushParentHashtable",
30+
"RemoveSavedInteger", "RemoveSavedBoolean", "RemoveSavedReal", "RemoveSavedString"
31+
));
2432

2533
final ImProg prog;
2634
final LuaCompilationUnit luaModel;
@@ -74,7 +82,7 @@ public LuaVariable initFor(ImVar a) {
7482

7583
@Override
7684
public LuaFunction initFor(ImFunction a) {
77-
String name = a.getName();
85+
String name = remapNativeName(a.getName());
7886
if (!a.isExtern() && !a.isBj() && !a.isNative() && !isFixedEntryPoint(a)) {
7987
name = uniqueName(name);
8088
} else if (isFixedEntryPoint(a)) {
@@ -126,9 +134,9 @@ public LuaMethod initFor(ImClass a) {
126134

127135
LuaFunction stringConcatFunction = LuaAst.LuaFunction(uniqueName("stringConcat"), LuaAst.LuaParams(), LuaAst.LuaStatements());
128136

129-
LuaFunction toIndexFunction = LuaAst.LuaFunction(uniqueName("objectToIndex"), LuaAst.LuaParams(), LuaAst.LuaStatements());
137+
LuaFunction toIndexFunction = LuaAst.LuaFunction(uniqueName("__wurst_objectToIndex"), LuaAst.LuaParams(), LuaAst.LuaStatements());
130138

131-
LuaFunction fromIndexFunction = LuaAst.LuaFunction(uniqueName("objectFromIndex"), LuaAst.LuaParams(), LuaAst.LuaStatements());
139+
LuaFunction fromIndexFunction = LuaAst.LuaFunction(uniqueName("__wurst_objectFromIndex"), LuaAst.LuaParams(), LuaAst.LuaStatements());
132140

133141
LuaFunction instanceOfFunction = LuaAst.LuaFunction(uniqueName("isInstanceOf"), LuaAst.LuaParams(), LuaAst.LuaStatements());
134142

@@ -163,6 +171,13 @@ public LuaTranslator(ImProg prog, ImTranslator imTr) {
163171
luaModel = LuaAst.LuaCompilationUnit();
164172
}
165173

174+
private String remapNativeName(String name) {
175+
if (HASHTABLE_NATIVE_NAMES.contains(name)) {
176+
return "__wurst_" + name;
177+
}
178+
return name;
179+
}
180+
166181
protected String uniqueName(String name) {
167182
int i = 0;
168183
String rname = name;
@@ -176,10 +191,6 @@ protected String uniqueName(String name) {
176191
public LuaCompilationUnit translate() {
177192
collectPredefinedNames();
178193

179-
RemoveGarbage.removeGarbage(prog);
180-
prog.flatten(imTr);
181-
182-
183194
normalizeMethodNames();
184195
normalizeFieldNames();
185196

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/StmtTranslation.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,11 @@ public static void translate(ImSet s, List<LuaStatement> res, LuaTranslator tr)
5050
public static void translate(ImVarargLoop loop, List<LuaStatement> res, LuaTranslator tr) {
5151
LuaVariable loopVar = tr.luaVar.getFor(loop.getLoopVar());
5252
// res.add(loopVar);
53+
String argsName = tr.uniqueName("__args");
5354
LuaVariable i = LuaAst.LuaVariable(tr.uniqueName("i"), LuaAst.LuaExprIntVal("0"));
54-
res.add(LuaAst.LuaLiteral("local __args = table.pack(...)"));
55-
res.add(LuaAst.LuaLiteral("for " + i.getName() + "=1,__args.n do"));
56-
res.add(LuaAst.LuaAssignment(LuaAst.LuaExprVarAccess(loopVar), LuaAst.LuaExprArrayAccess(LuaAst.LuaLiteral("__args"), LuaAst.LuaExprlist(LuaAst.LuaExprVarAccess(i)))));
55+
res.add(LuaAst.LuaLiteral("local " + argsName + " = table.pack(...)"));
56+
res.add(LuaAst.LuaLiteral("for " + i.getName() + "=1," + argsName + ".n do"));
57+
res.add(LuaAst.LuaAssignment(LuaAst.LuaExprVarAccess(loopVar), LuaAst.LuaExprArrayAccess(LuaAst.LuaLiteral(argsName), LuaAst.LuaExprlist(LuaAst.LuaExprVarAccess(i)))));
5758
tr.translateStatements(res, loop.getBody());
5859
res.add(LuaAst.LuaLiteral("end"));
5960
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package tests.wurstscript.tests;
2+
3+
import de.peeeq.wurstscript.luaAst.LuaAst;
4+
import de.peeeq.wurstscript.luaAst.LuaFunction;
5+
import de.peeeq.wurstscript.translation.lua.translation.LuaNatives;
6+
import org.testng.annotations.Test;
7+
8+
import static org.testng.AssertJUnit.assertTrue;
9+
10+
public class LuaNativesTests {
11+
12+
private static String renderNative(String name) {
13+
LuaFunction f = LuaAst.LuaFunction(name, LuaAst.LuaParams(), LuaAst.LuaStatements());
14+
LuaNatives.get(f);
15+
StringBuilder sb = new StringBuilder();
16+
f.print(sb, 0);
17+
return sb.toString();
18+
}
19+
20+
@Test
21+
public void s2iUsesPrefixIntegerParsing() {
22+
String rendered = renderNative("S2I");
23+
assertTrue(rendered.contains("string.match(tostring(x), \"^[%+%-]?%d+\")"));
24+
assertTrue(rendered.contains("return tonumber(m)"));
25+
}
26+
27+
@Test
28+
public void r2iUsesTruncationTowardZero() {
29+
String rendered = renderNative("R2I");
30+
assertTrue(rendered.contains("return math.modf(x)"));
31+
}
32+
33+
@Test
34+
public void getRandomRealUsesRangeFormula() {
35+
String rendered = renderNative("GetRandomReal");
36+
assertTrue(rendered.contains("return l + math.random() * (h - l)"));
37+
}
38+
39+
@Test
40+
public void triggerEvaluateReturnsBoolInFallback() {
41+
String rendered = renderNative("TriggerEvaluate");
42+
assertTrue(rendered.contains("for i,a in ipairs(t.actions) do a() end"));
43+
assertTrue(rendered.contains("return true"));
44+
}
45+
}
46+
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package tests.wurstscript.tests;
2+
3+
import org.testng.annotations.Test;
4+
5+
import java.io.File;
6+
import java.io.IOException;
7+
import java.lang.reflect.InvocationTargetException;
8+
import java.lang.reflect.Method;
9+
import java.nio.charset.StandardCharsets;
10+
11+
import static org.testng.Assert.assertTrue;
12+
import static org.testng.Assert.fail;
13+
14+
public class LuaSyntaxCheckTests {
15+
16+
@Test
17+
public void luacSyntaxErrorsAreDetected() throws Exception {
18+
WurstScriptTest helper = new WurstScriptTest();
19+
String luacExecutable = invokeGetLuacExecutable(helper);
20+
21+
File invalidLua = File.createTempFile("wurst-invalid-lua-", ".lua");
22+
invalidLua.deleteOnExit();
23+
java.nio.file.Files.writeString(invalidLua.toPath(), "function broken(\n", StandardCharsets.UTF_8);
24+
25+
Method checkLuaSyntax = WurstScriptTest.class.getDeclaredMethod("checkLuaSyntax", String.class, File.class);
26+
checkLuaSyntax.setAccessible(true);
27+
try {
28+
checkLuaSyntax.invoke(helper, luacExecutable, invalidLua);
29+
fail("Expected syntax check to fail for invalid Lua source.");
30+
} catch (InvocationTargetException ite) {
31+
Throwable cause = ite.getCause();
32+
assertTrue(cause instanceof IOException,
33+
"Expected IOException but got: " + (cause == null ? "null" : cause.getClass().getName()));
34+
assertTrue(cause.getMessage() != null && cause.getMessage().contains("Lua syntax check failed"),
35+
"Expected syntax check failure message, but got: " + cause.getMessage());
36+
}
37+
}
38+
39+
private String invokeGetLuacExecutable(WurstScriptTest helper) throws Exception {
40+
Method getLuacExecutable = WurstScriptTest.class.getDeclaredMethod("getLuacExecutable");
41+
getLuacExecutable.setAccessible(true);
42+
Object result = getLuacExecutable.invoke(helper);
43+
return (String) result;
44+
}
45+
}
46+

0 commit comments

Comments
 (0)