Skip to content

Commit 3cd4566

Browse files
committed
try to use an alternative lua runtime
1 parent ef0d1a4 commit 3cd4566

File tree

5 files changed

+241
-80
lines changed

5 files changed

+241
-80
lines changed

web_playground/public/index.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
<head>
44
<link rel="stylesheet" href="app.bundle.css" />
55
<meta charset="utf-8" />
6+
<!-- Add the lua scripts as modules, loaded before the main bundle -->
7+
<script type="module" src="js/lua-5.4.7-with-ffi.js"></script>
8+
<script type="module" src="js/lua-interop.js"></script>
69
</head>
710
<style>
811
* {

web_playground/src/index.ts

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { editor, editor as MonacoEditor, IRange, languages, MarkerSeverity, Uri } from "monaco-editor"
22
import { PublishDiagnosticsParams, Range, DidChangeTextDocumentParams, Position, URI } from "vscode-languageserver"
33
import { createEditor } from "./editor"
4-
import { loadLua, prettyPrint } from "./lua"
4+
import { loadLuaWasmoon, loadLuaInterop } from "./lua"
55
import { registerSyntax } from "./syntax"
66
import randomExamples from "./random.json"
77
import { assortedExamples } from "./examples"
@@ -15,15 +15,8 @@ const pathFromURI = (uri: Uri) => {
1515
}
1616

1717
const main = async () => {
18-
const lua = await loadLua()
19-
20-
await lua.doString(`
21-
_G.syntax_typesystem = require("nattlua.syntax.typesystem")
22-
_G.syntax_runtime = require("nattlua.syntax.runtime")
23-
`)
24-
25-
const syntax_typesystem = lua.global.get("syntax_typesystem")
26-
const syntax_runtime = lua.global.get("syntax_runtime")
18+
const { syntax_runtime, syntax_typesystem, lsp, prettyPrint } = await loadLuaWasmoon()
19+
//const { syntax_runtime, syntax_typesystem, lsp, prettyPrint } = await loadLuaInterop()
2720

2821
await registerSyntax(syntax_runtime, syntax_typesystem)
2922

@@ -36,8 +29,6 @@ const main = async () => {
3629
tab.setValue(select.value)
3730
})
3831

39-
const lsp = lua.global.get("lsp")
40-
4132
const callMethodOnServer = (method: string, params: any) => {
4233
console.log("calling", method, params)
4334
let response = lsp.methods[method](params)
@@ -89,11 +80,11 @@ const main = async () => {
8980
}
9081

9182
document.getElementById("random-example").addEventListener("click", () => {
92-
tab.setValue(prettyPrint(lua, getRandomExample()))
83+
tab.setValue(prettyPrint(getRandomExample()))
9384
})
9485

9586
document.getElementById("pretty-print").addEventListener("click", () => {
96-
tab.setValue(prettyPrint(lua, tab.getValue()))
87+
tab.setValue(prettyPrint(tab.getValue()))
9788
})
9889

9990

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
export interface LuaInterop {
2+
newState: () => void;
3+
doString: (s: string) => void;
4+
L: any;
5+
lib: any;
6+
push_js: (L: any, jsValue: any, isArrow?: boolean) => number;
7+
lua_to_js: (L: any, i: number) => any;
8+
luaopen_js: () => void;
9+
[key: string]: any;
10+
}
11+
12+
export type NewLuaFunc = (args?: Record<string, any>) => Promise<LuaInterop>;
13+
14+
let newLuaPromise: Promise<{ newLua: NewLuaFunc }> | null = null;
15+
16+
export async function getLuaInterop(): Promise<{ newLua: NewLuaFunc }> {
17+
if (!newLuaPromise) {
18+
newLuaPromise = new Promise((resolve, reject) => {
19+
const script = document.createElement('script');
20+
script.src = '/js/lua-interop.js';
21+
script.type = 'module';
22+
23+
script.onload = () => {
24+
if (window.newLua) {
25+
resolve({ newLua: window.newLua });
26+
} else {
27+
import('/js/lua-interop.js' as string)
28+
.then(module => {
29+
resolve(module);
30+
})
31+
.catch(error => {
32+
reject(new Error(`Failed to import lua-interop.js: ${error.message}`));
33+
});
34+
}
35+
};
36+
37+
script.onerror = () => {
38+
reject(new Error('Failed to load lua-interop.js'));
39+
};
40+
41+
document.head.appendChild(script);
42+
});
43+
}
44+
45+
return newLuaPromise;
46+
}
47+
48+
declare global {
49+
interface Window {
50+
newLua?: NewLuaFunc;
51+
}
52+
}

web_playground/src/lua.ts

Lines changed: 181 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,49 @@
11
import { LuaEngine, LuaFactory } from "wasmoon"
2-
import { loadLuaModule } from "./util"
2+
import { getLuaInterop } from "./lua-utils/luaInterop"
3+
4+
const loadLuaModule = async (doString: (luaCode: string) => void, p: Promise<{ default: string }>, moduleName: string, chunkName?: string) => {
5+
let { default: code } = await p
6+
7+
if (code.startsWith("#")) {
8+
// remove shebang
9+
const index = code.indexOf("\n")
10+
if (index !== -1) {
11+
code = code.substring(index)
12+
}
13+
}
14+
15+
// I think something broke with moonwasm. There seems to be a limit on how large the string can be.
16+
// This may be taking it too far but I've spent too much time on this already..
17+
18+
const bytes = (new TextEncoder()).encode(code)
19+
let bytesString: string[] = []
20+
let bytesStringIndex = 0
21+
for (let i = 0; i < bytes.length; i++) {
22+
let code = bytes[i]
23+
bytesString[bytesStringIndex] = `\\${code}`
24+
bytesStringIndex++
25+
if (bytesStringIndex > 8000) {
26+
let str = `CHUNKS = CHUNKS or {};CHUNKS[#CHUNKS + 1] = "${bytesString.join("")}"`
27+
doString(str)
28+
bytesString = []
29+
bytesStringIndex = 0
30+
}
31+
}
32+
{
33+
let str = `CHUNKS = CHUNKS or {};CHUNKS[#CHUNKS + 1] = "${bytesString.join("")}"`
34+
doString(str)
35+
}
36+
37+
let str = `
38+
local code = "package.preload['${moduleName}'] = function(...) " .. table.concat(CHUNKS) .. " end"
39+
assert(load(code, "${chunkName}"))(...); CHUNKS = nil
40+
`
41+
doString(str)
42+
}
43+
44+
export const loadLuaWasmoon = async () => {
45+
346

4-
export const loadLua = async () => {
547
const factory = new LuaFactory()
648
const lua = await factory.createEngine({
749
openStandardLibs: true,
@@ -40,31 +82,143 @@ export const loadLua = async () => {
4082
4183
_G.lsp = lsp`)
4284

43-
console.log("OK")
4485

45-
return lua
46-
}
86+
await lua.doString(`
87+
_G.syntax_typesystem = require("nattlua.syntax.typesystem")
88+
_G.syntax_runtime = require("nattlua.syntax.runtime")
89+
`)
90+
91+
const syntax_typesystem = lua.global.get("syntax_typesystem")
92+
const syntax_runtime = lua.global.get("syntax_runtime")
93+
const lsp = lua.global.get("lsp")
94+
console.log("Lua engine initialized successfully")
95+
return {
96+
syntax_typesystem,
97+
syntax_runtime,
98+
lsp,
99+
prettyPrint: (lua: LuaEngine, code: string) => {
100+
lua.doStringSync(`
101+
function _G.prettyPrint(code)
102+
local nl = require("nattlua")
103+
local compiler = nl.Compiler(code, "temp", {
104+
emitter = {
105+
preserve_whitespace = false,
106+
string_quote = "\\"",
107+
no_semicolon = true,
108+
type_annotations = "explicit",
109+
force_parenthesis = true,
110+
comment_type_annotations = false,
111+
},
112+
parser = {
113+
skip_import = true,
114+
}
115+
})
116+
return assert(compiler:Emit())
117+
end
118+
`)
47119

48-
export const prettyPrint = (lua: LuaEngine, code: string) => {
49-
lua.doStringSync(`
50-
function _G.prettyPrint(code)
51-
local nl = require("nattlua")
52-
local compiler = nl.Compiler(code, "temp", {
53-
emitter = {
54-
preserve_whitespace = false,
55-
string_quote = "\\"",
56-
no_semicolon = true,
57-
type_annotations = "explicit",
58-
force_parenthesis = true,
59-
comment_type_annotations = false,
60-
},
61-
parser = {
62-
skip_import = true,
63-
}
64-
})
65-
return assert(compiler:Emit())
66-
end
67-
`)
68-
69-
return lua.global.get("prettyPrint")(code) as string
120+
return lua.global.get("prettyPrint")(code) as string
121+
}
122+
123+
}
70124
}
125+
126+
export const loadLuaInterop = async () => {
127+
const { newLua } = await getLuaInterop();
128+
let lua = await newLua({
129+
print: s => {
130+
console.log('>', s);
131+
},
132+
printErr: s => {
133+
console.log('1>', s);
134+
console.log(new Error().stack);
135+
},
136+
});
137+
lua.newState();
138+
139+
globalThis.lua = lua
140+
141+
let lsp: any
142+
globalThis.loadLSP = (obj) => lsp = obj
143+
144+
let syntax_runtime: any
145+
globalThis.loadSyntaxRuntime = (obj) => syntax_runtime = obj
146+
147+
let syntax_typesystem: any
148+
globalThis.loadSyntaxTypesystem = (obj) => syntax_typesystem = obj
149+
150+
await loadLuaModule((str) => lua.doString(str), import("./../../build_output.lua"), "nattlua")
151+
await lua.doString("for k, v in pairs(package.preload) do print(k,v) end require('nattlua') for k,v in pairs(IMPORTS) do package.preload[k] = v end")
152+
await loadLuaModule((str) => lua.doString(str), import("./../../language_server/lsp.lua"), "lsp", "@language_server/lsp.lua")
153+
154+
await lua.doString(`
155+
local lsp = require("lsp")
156+
157+
local listeners = {}
158+
159+
function lsp.Call(data)
160+
assert(data.method, "missing method")
161+
listeners[data.method](data.params)
162+
end
163+
164+
function lsp.On(method, callback)
165+
listeners[method] = callback
166+
end
167+
168+
for k,v in pairs(lsp.methods) do
169+
lsp.methods[k] = function(params)
170+
print("calling on server", k)
171+
local ok, res = xpcall(function()
172+
return v(params)
173+
end, debug.traceback)
174+
if not ok then
175+
error(res, 2)
176+
end
177+
return res
178+
end
179+
end
180+
181+
jsToLua[0].loadLSP(lsp)`)
182+
183+
184+
await lua.doString(`
185+
jsToLua[0].loadSyntaxRuntime(require("nattlua.syntax.typesystem"))
186+
jsToLua[0].loadSyntaxTypesystem(require("nattlua.syntax.runtime"))
187+
`)
188+
189+
globalThis.syntax_typesystem = syntax_typesystem
190+
globalThis.syntax_runtime = syntax_runtime
191+
globalThis.lsp = lsp
192+
193+
console.log("Lua interop initialized successfully")
194+
return {
195+
syntax_typesystem,
196+
syntax_runtime,
197+
lsp,
198+
prettyPrint: (lua: LuaEngine, code: string) => {
199+
lua.doStringSync(`
200+
function _G.prettyPrint(code)
201+
local nl = require("nattlua")
202+
local compiler = nl.Compiler(code, "temp", {
203+
emitter = {
204+
preserve_whitespace = false,
205+
string_quote = "\\"",
206+
no_semicolon = true,
207+
type_annotations = "explicit",
208+
force_parenthesis = true,
209+
comment_type_annotations = false,
210+
},
211+
parser = {
212+
skip_import = true,
213+
}
214+
})
215+
return assert(compiler:Emit())
216+
end
217+
`)
218+
219+
return lua.global.get("prettyPrint")(code) as string
220+
}
221+
222+
}
223+
224+
}

web_playground/src/util.ts

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -34,42 +34,3 @@ function chunkSubstr(str: string, size: number) {
3434
return chunks
3535
}
3636

37-
export const loadLuaModule = async (doString: (luaCode: string) => void, p: Promise<{ default: string }>, moduleName: string, chunkName?: string) => {
38-
let { default: code } = await p
39-
40-
if (code.startsWith("#")) {
41-
// remove shebang
42-
const index = code.indexOf("\n")
43-
if (index !== -1) {
44-
code = code.substring(index)
45-
}
46-
}
47-
48-
// I think something broke with moonwasm. There seems to be a limit on how large the string can be.
49-
// This may be taking it too far but I've spent too much time on this already..
50-
51-
const bytes = (new TextEncoder()).encode(code)
52-
let bytesString: string[] = []
53-
let bytesStringIndex = 0
54-
for (let i = 0; i < bytes.length; i++) {
55-
let code = bytes[i]
56-
bytesString[bytesStringIndex] = `\\${code}`
57-
bytesStringIndex++
58-
if (bytesStringIndex > 8000) {
59-
let str = `CHUNKS = CHUNKS or {};CHUNKS[#CHUNKS + 1] = "${bytesString.join("")}"`
60-
doString(str)
61-
bytesString = []
62-
bytesStringIndex = 0
63-
}
64-
}
65-
{
66-
let str = `CHUNKS = CHUNKS or {};CHUNKS[#CHUNKS + 1] = "${bytesString.join("")}"`
67-
doString(str)
68-
}
69-
70-
let str = `
71-
local code = "package.preload['${moduleName}'] = function(...) " .. table.concat(CHUNKS) .. " end"
72-
assert(load(code, "${chunkName}"))(...); CHUNKS = nil
73-
`
74-
doString(str)
75-
}

0 commit comments

Comments
 (0)