Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@ jobs:
shell: bash
run: ./gradlew packageSlimCompilerDist --no-daemon --stacktrace

- name: Prepare bundled Lua runtime (Linux)
if: runner.os == 'Linux'
shell: bash
run: |
if [[ -f src/test/resources/lua53 ]]; then
chmod +x src/test/resources/lua53
fi

- name: Run tests
shell: bash
run: ./gradlew test --no-daemon --stacktrace
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class ExprTranslation {

public static final String TYPE_ID = "__typeId__";
public static final String WURST_SUPERTYPES = "__wurst_supertypes";
private static final String WURST_ABORT_THREAD_SENTINEL = "__wurst_abort_thread";

public static LuaExpr translate(ImAlloc e, LuaTranslator tr) {
ImClass c = e.getClazz().getClassDef();
Expand Down Expand Up @@ -51,7 +52,7 @@ public static LuaExpr translate(ImFuncRef e, LuaTranslator tr) {
LuaAst.LuaExprFunctionCall(tr.luaFunc.getFor(e.getFunc()), LuaAst.LuaExprlist(LuaAst.LuaExprVarAccess(tempDots)))))
),
// LuaAst.LuaLiteral("function(err) " + errorFuncName(tr) + "(tostring(err)) end")
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")
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")
// unfortunately BJDebugMsg(debug.traceback()) is not working
)
),
Expand All @@ -73,6 +74,12 @@ private static String callErrorFunc(LuaTranslator tr, String msg) {

public static LuaExpr translate(ImFunctionCall e, LuaTranslator tr) {
LuaFunction f = tr.luaFunc.getFor(e.getFunc());
if ("I2S".equals(f.getName()) && isIntentionalThreadAbortCall(e)) {
return LuaAst.LuaExprFunctionCallByName("error", LuaAst.LuaExprlist(
LuaAst.LuaExprStringVal(WURST_ABORT_THREAD_SENTINEL),
LuaAst.LuaExprIntVal("0")
));
}
if (f.getName().equals(ImTranslator.$DEBUG_PRINT)) {
f.setName("BJDebugMsg");
} else if (f.getName().equals("I2S")) {
Expand All @@ -81,6 +88,27 @@ public static LuaExpr translate(ImFunctionCall e, LuaTranslator tr) {
return LuaAst.LuaExprFunctionCall(f, tr.translateExprList(e.getArguments()));
}

private static boolean isIntentionalThreadAbortCall(ImFunctionCall e) {
if (e.getArguments().size() != 1) {
return false;
}
ImExpr arg = e.getArguments().get(0);
if (!(arg instanceof ImOperatorCall)) {
return false;
}
ImOperatorCall op = (ImOperatorCall) arg;
if (op.getOp() != WurstOperator.DIV_INT) {
return false;
}
if (op.getArguments().size() != 2) {
return false;
}
ImExpr left = op.getArguments().get(0);
ImExpr right = op.getArguments().get(1);
return (left instanceof ImIntVal && ((ImIntVal) left).getValI() == 1)
&& (right instanceof ImIntVal && ((ImIntVal) right).getValI() == 0);
}

public static LuaExpr translate(ImInstanceof e, LuaTranslator tr) {
return
LuaAst.LuaExprFunctionCall(tr.instanceOfFunction, LuaAst.LuaExprlist(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ public class LuaNatives {

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

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

addNative("GetRandomReal", f -> f.getBody().add(LuaAst.LuaLiteral("return math.random")));
addNative("GetRandomReal", f -> {
f.getParams().add(LuaAst.LuaVariable("l", LuaAst.LuaNoExpr()));
f.getParams().add(LuaAst.LuaVariable("h", LuaAst.LuaNoExpr()));
f.getBody().add(LuaAst.LuaLiteral("return l + math.random() * (h - l)"));
});

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

addNative("R2I", f -> {
f.getParams().add(LuaAst.LuaVariable("x", LuaAst.LuaNoExpr()));
f.getBody().add(LuaAst.LuaLiteral("return math.floor(x)"));
f.getBody().add(LuaAst.LuaLiteral("return math.modf(x)"));
});

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

addNative(Arrays.asList("SaveInteger", "SaveBoolean", "SaveReal", "SaveStr", "SaveBoolean"), f -> {
addNative(Arrays.asList(
"SaveInteger", "SaveBoolean", "SaveReal", "SaveStr",
"__wurst_SaveInteger", "__wurst_SaveBoolean", "__wurst_SaveReal", "__wurst_SaveStr"), f -> {
f.getParams().add(LuaAst.LuaVariable("h", LuaAst.LuaNoExpr()));
f.getParams().add(LuaAst.LuaVariable("p", LuaAst.LuaNoExpr()));
f.getParams().add(LuaAst.LuaVariable("c", LuaAst.LuaNoExpr()));
f.getParams().add(LuaAst.LuaVariable("i", LuaAst.LuaNoExpr()));
f.getBody().add(LuaAst.LuaLiteral("if not h[p] then h[p] = {} end h[p][c] = i"));
});

addNative(Arrays.asList("LoadInteger", "LoadBoolean", "LoadReal", "LoadStr", "LoadBoolean"), f -> {
addNative(Arrays.asList(
"LoadInteger", "LoadBoolean", "LoadReal", "LoadStr",
"__wurst_LoadInteger", "__wurst_LoadBoolean", "__wurst_LoadReal", "__wurst_LoadStr"), f -> {
f.getParams().add(LuaAst.LuaVariable("h", LuaAst.LuaNoExpr()));
f.getParams().add(LuaAst.LuaVariable("p", LuaAst.LuaNoExpr()));
f.getParams().add(LuaAst.LuaVariable("c", LuaAst.LuaNoExpr()));
f.getBody().add(LuaAst.LuaLiteral("if not h[p] then return nil end return h[p][c]"));
});

addNative(Arrays.asList(
"HaveSavedInteger", "HaveSavedBoolean", "HaveSavedReal", "HaveSavedString",
"__wurst_HaveSavedInteger", "__wurst_HaveSavedBoolean", "__wurst_HaveSavedReal", "__wurst_HaveSavedString"), f -> {
f.getParams().add(LuaAst.LuaVariable("h", LuaAst.LuaNoExpr()));
f.getParams().add(LuaAst.LuaVariable("p", LuaAst.LuaNoExpr()));
f.getParams().add(LuaAst.LuaVariable("c", LuaAst.LuaNoExpr()));
f.getBody().add(LuaAst.LuaLiteral("return h[p] ~= nil and h[p][c] ~= nil"));
});

addNative(Arrays.asList("FlushChildHashtable", "__wurst_FlushChildHashtable"), f -> {
f.getParams().add(LuaAst.LuaVariable("h", LuaAst.LuaNoExpr()));
f.getParams().add(LuaAst.LuaVariable("p", LuaAst.LuaNoExpr()));
f.getBody().add(LuaAst.LuaLiteral("h[p] = nil"));
});

addNative(Arrays.asList("FlushParentHashtable", "__wurst_FlushParentHashtable"), f -> {
f.getParams().add(LuaAst.LuaVariable("h", LuaAst.LuaNoExpr()));
f.getBody().add(LuaAst.LuaLiteral("for k in pairs(h) do h[k] = nil end"));
});

addNative(Arrays.asList(
"RemoveSavedInteger", "RemoveSavedBoolean", "RemoveSavedReal", "RemoveSavedString",
"__wurst_RemoveSavedInteger", "__wurst_RemoveSavedBoolean", "__wurst_RemoveSavedReal", "__wurst_RemoveSavedString"), f -> {
f.getParams().add(LuaAst.LuaVariable("h", LuaAst.LuaNoExpr()));
f.getParams().add(LuaAst.LuaVariable("p", LuaAst.LuaNoExpr()));
f.getParams().add(LuaAst.LuaVariable("c", LuaAst.LuaNoExpr()));
f.getBody().add(LuaAst.LuaLiteral("if h[p] then h[p][c] = nil end"));
});

addNative("typeIdToTypeName", f -> {
f.getParams().add(LuaAst.LuaVariable("typeId", LuaAst.LuaNoExpr()));
f.getBody().add(LuaAst.LuaLiteral("return \"\""));
});

addNative("maxTypeId", f -> {
f.getBody().add(LuaAst.LuaLiteral("return 0"));
});

addNative("instanceCount", f -> {
f.getParams().add(LuaAst.LuaVariable("typeId", LuaAst.LuaNoExpr()));
f.getBody().add(LuaAst.LuaLiteral("return 0"));
});

addNative("maxInstanceCount", f -> {
f.getParams().add(LuaAst.LuaVariable("typeId", LuaAst.LuaNoExpr()));
f.getBody().add(LuaAst.LuaLiteral("return 0"));
});

}

private static void addNative(String name, Consumer<LuaFunction> g) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@
import static de.peeeq.wurstscript.translation.lua.translation.ExprTranslation.WURST_SUPERTYPES;

public class LuaTranslator {
private static final Set<String> HASHTABLE_NATIVE_NAMES = new HashSet<>(Arrays.asList(
"InitHashtable",
"SaveInteger", "SaveBoolean", "SaveReal", "SaveStr",
"LoadInteger", "LoadBoolean", "LoadReal", "LoadStr",
"HaveSavedInteger", "HaveSavedBoolean", "HaveSavedReal", "HaveSavedString",
"FlushChildHashtable", "FlushParentHashtable",
"RemoveSavedInteger", "RemoveSavedBoolean", "RemoveSavedReal", "RemoveSavedString"
));

final ImProg prog;
final LuaCompilationUnit luaModel;
Expand Down Expand Up @@ -74,7 +82,7 @@ public LuaVariable initFor(ImVar a) {

@Override
public LuaFunction initFor(ImFunction a) {
String name = a.getName();
String name = remapNativeName(a.getName());
if (!a.isExtern() && !a.isBj() && !a.isNative() && !isFixedEntryPoint(a)) {
name = uniqueName(name);
} else if (isFixedEntryPoint(a)) {
Expand Down Expand Up @@ -126,9 +134,9 @@ public LuaMethod initFor(ImClass a) {

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

LuaFunction toIndexFunction = LuaAst.LuaFunction(uniqueName("objectToIndex"), LuaAst.LuaParams(), LuaAst.LuaStatements());
LuaFunction toIndexFunction = LuaAst.LuaFunction(uniqueName("__wurst_objectToIndex"), LuaAst.LuaParams(), LuaAst.LuaStatements());

LuaFunction fromIndexFunction = LuaAst.LuaFunction(uniqueName("objectFromIndex"), LuaAst.LuaParams(), LuaAst.LuaStatements());
LuaFunction fromIndexFunction = LuaAst.LuaFunction(uniqueName("__wurst_objectFromIndex"), LuaAst.LuaParams(), LuaAst.LuaStatements());

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

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

private String remapNativeName(String name) {
if (HASHTABLE_NATIVE_NAMES.contains(name)) {
return "__wurst_" + name;
}
return name;
}

protected String uniqueName(String name) {
int i = 0;
String rname = name;
Expand All @@ -176,10 +191,6 @@ protected String uniqueName(String name) {
public LuaCompilationUnit translate() {
collectPredefinedNames();

RemoveGarbage.removeGarbage(prog);
prog.flatten(imTr);


normalizeMethodNames();
normalizeFieldNames();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,11 @@ public static void translate(ImSet s, List<LuaStatement> res, LuaTranslator tr)
public static void translate(ImVarargLoop loop, List<LuaStatement> res, LuaTranslator tr) {
LuaVariable loopVar = tr.luaVar.getFor(loop.getLoopVar());
// res.add(loopVar);
String argsName = tr.uniqueName("__args");
LuaVariable i = LuaAst.LuaVariable(tr.uniqueName("i"), LuaAst.LuaExprIntVal("0"));
res.add(LuaAst.LuaLiteral("local __args = table.pack(...)"));
res.add(LuaAst.LuaLiteral("for " + i.getName() + "=1,__args.n do"));
res.add(LuaAst.LuaAssignment(LuaAst.LuaExprVarAccess(loopVar), LuaAst.LuaExprArrayAccess(LuaAst.LuaLiteral("__args"), LuaAst.LuaExprlist(LuaAst.LuaExprVarAccess(i)))));
res.add(LuaAst.LuaLiteral("local " + argsName + " = table.pack(...)"));
res.add(LuaAst.LuaLiteral("for " + i.getName() + "=1," + argsName + ".n do"));
res.add(LuaAst.LuaAssignment(LuaAst.LuaExprVarAccess(loopVar), LuaAst.LuaExprArrayAccess(LuaAst.LuaLiteral(argsName), LuaAst.LuaExprlist(LuaAst.LuaExprVarAccess(i)))));
tr.translateStatements(res, loop.getBody());
res.add(LuaAst.LuaLiteral("end"));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package tests.wurstscript.tests;

import de.peeeq.wurstscript.luaAst.LuaAst;
import de.peeeq.wurstscript.luaAst.LuaFunction;
import de.peeeq.wurstscript.translation.lua.translation.LuaNatives;
import org.testng.annotations.Test;

import static org.testng.AssertJUnit.assertTrue;

public class LuaNativesTests {

private static String renderNative(String name) {
LuaFunction f = LuaAst.LuaFunction(name, LuaAst.LuaParams(), LuaAst.LuaStatements());
LuaNatives.get(f);
StringBuilder sb = new StringBuilder();
f.print(sb, 0);
return sb.toString();
}

@Test
public void s2iUsesPrefixIntegerParsing() {
String rendered = renderNative("S2I");
assertTrue(rendered.contains("string.match(tostring(x), \"^[%+%-]?%d+\")"));
assertTrue(rendered.contains("return tonumber(m)"));
}

@Test
public void r2iUsesTruncationTowardZero() {
String rendered = renderNative("R2I");
assertTrue(rendered.contains("return math.modf(x)"));
}

@Test
public void getRandomRealUsesRangeFormula() {
String rendered = renderNative("GetRandomReal");
assertTrue(rendered.contains("return l + math.random() * (h - l)"));
}

@Test
public void triggerEvaluateReturnsBoolInFallback() {
String rendered = renderNative("TriggerEvaluate");
assertTrue(rendered.contains("for i,a in ipairs(t.actions) do a() end"));
assertTrue(rendered.contains("return true"));
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package tests.wurstscript.tests;

import org.testng.annotations.Test;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;

import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;

public class LuaSyntaxCheckTests {

@Test
public void luacSyntaxErrorsAreDetected() throws Exception {
WurstScriptTest helper = new WurstScriptTest();
String luacExecutable = invokeGetLuacExecutable(helper);

File invalidLua = File.createTempFile("wurst-invalid-lua-", ".lua");
invalidLua.deleteOnExit();
java.nio.file.Files.writeString(invalidLua.toPath(), "function broken(\n", StandardCharsets.UTF_8);

Method checkLuaSyntax = WurstScriptTest.class.getDeclaredMethod("checkLuaSyntax", String.class, File.class);
checkLuaSyntax.setAccessible(true);
try {
checkLuaSyntax.invoke(helper, luacExecutable, invalidLua);
fail("Expected syntax check to fail for invalid Lua source.");
} catch (InvocationTargetException ite) {
Throwable cause = ite.getCause();
assertTrue(cause instanceof IOException,
"Expected IOException but got: " + (cause == null ? "null" : cause.getClass().getName()));
assertTrue(cause.getMessage() != null && cause.getMessage().contains("Lua syntax check failed"),
"Expected syntax check failure message, but got: " + cause.getMessage());
}
}

private String invokeGetLuacExecutable(WurstScriptTest helper) throws Exception {
Method getLuacExecutable = WurstScriptTest.class.getDeclaredMethod("getLuacExecutable");
getLuacExecutable.setAccessible(true);
Object result = getLuacExecutable.invoke(helper);
return (String) result;
}
}

Loading
Loading