@silverbulletmd/silverbullet 2.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +18 -0
- package/README.md +98 -0
- package/client/asset_bundle/bundle.ts +95 -0
- package/client/data/datastore.ts +85 -0
- package/client/data/kv_primitives.ts +25 -0
- package/client/markdown_parser/constants.ts +13 -0
- package/client/plugos/event.ts +36 -0
- package/client/plugos/eventhook.ts +8 -0
- package/client/plugos/hooks/code_widget.ts +59 -0
- package/client/plugos/hooks/command.ts +104 -0
- package/client/plugos/hooks/document_editor.ts +77 -0
- package/client/plugos/hooks/event.ts +187 -0
- package/client/plugos/hooks/mq.ts +154 -0
- package/client/plugos/hooks/plug_namespace.ts +85 -0
- package/client/plugos/hooks/slash_command.ts +192 -0
- package/client/plugos/hooks/syscall.ts +66 -0
- package/client/plugos/manifest_cache.ts +67 -0
- package/client/plugos/plug.ts +99 -0
- package/client/plugos/plug_compile.ts +202 -0
- package/client/plugos/protocol.ts +40 -0
- package/client/plugos/proxy_fetch.ts +53 -0
- package/client/plugos/sandboxes/deno_worker_sandbox.ts +6 -0
- package/client/plugos/sandboxes/sandbox.ts +14 -0
- package/client/plugos/sandboxes/web_worker_sandbox.ts +17 -0
- package/client/plugos/sandboxes/worker_sandbox.ts +132 -0
- package/client/plugos/syscalls/asset.ts +35 -0
- package/client/plugos/syscalls/clientStore.ts +21 -0
- package/client/plugos/syscalls/client_code_widget.ts +12 -0
- package/client/plugos/syscalls/code_widget.ts +24 -0
- package/client/plugos/syscalls/config.ts +46 -0
- package/client/plugos/syscalls/datastore.ts +89 -0
- package/client/plugos/syscalls/editor.ts +673 -0
- package/client/plugos/syscalls/event.ts +36 -0
- package/client/plugos/syscalls/fetch.ts +128 -0
- package/client/plugos/syscalls/index.ts +102 -0
- package/client/plugos/syscalls/jsonschema.ts +69 -0
- package/client/plugos/syscalls/language.ts +23 -0
- package/client/plugos/syscalls/lua.ts +58 -0
- package/client/plugos/syscalls/markdown.ts +84 -0
- package/client/plugos/syscalls/mq.ts +52 -0
- package/client/plugos/syscalls/service_registry.ts +43 -0
- package/client/plugos/syscalls/shell.ts +39 -0
- package/client/plugos/syscalls/space.ts +139 -0
- package/client/plugos/syscalls/sync.ts +77 -0
- package/client/plugos/syscalls/system.ts +150 -0
- package/client/plugos/system.ts +201 -0
- package/client/plugos/types.ts +60 -0
- package/client/plugos/util.ts +14 -0
- package/client/plugos/worker_runtime.ts +195 -0
- package/client/space_lua/ast.ts +328 -0
- package/client/space_lua/ast_narrow.ts +81 -0
- package/client/space_lua/eval.ts +2478 -0
- package/client/space_lua/labels.ts +416 -0
- package/client/space_lua/numeric.ts +240 -0
- package/client/space_lua/parse.ts +1522 -0
- package/client/space_lua/query_collection.ts +232 -0
- package/client/space_lua/rp.ts +27 -0
- package/client/space_lua/runtime.ts +1702 -0
- package/client/space_lua/stdlib/crypto.ts +10 -0
- package/client/space_lua/stdlib/encoding.ts +19 -0
- package/client/space_lua/stdlib/format.ts +770 -0
- package/client/space_lua/stdlib/js.ts +73 -0
- package/client/space_lua/stdlib/load.ts +52 -0
- package/client/space_lua/stdlib/math.ts +193 -0
- package/client/space_lua/stdlib/net.ts +113 -0
- package/client/space_lua/stdlib/os.ts +368 -0
- package/client/space_lua/stdlib/space_lua.ts +153 -0
- package/client/space_lua/stdlib/string.ts +286 -0
- package/client/space_lua/stdlib/table.ts +401 -0
- package/client/space_lua/stdlib.ts +489 -0
- package/client/space_lua/tonumber.ts +501 -0
- package/client/space_lua/util.ts +96 -0
- package/dist/plug-compile.js +1513 -0
- package/package.json +120 -0
- package/plug-api/constants.ts +42 -0
- package/plug-api/lib/async.ts +162 -0
- package/plug-api/lib/crypto.ts +202 -0
- package/plug-api/lib/dates.ts +13 -0
- package/plug-api/lib/json.ts +136 -0
- package/plug-api/lib/limited_map.ts +72 -0
- package/plug-api/lib/memory_cache.ts +21 -0
- package/plug-api/lib/native_fetch.ts +6 -0
- package/plug-api/lib/ref.ts +275 -0
- package/plug-api/lib/resolve.ts +90 -0
- package/plug-api/lib/tags.ts +15 -0
- package/plug-api/lib/transclusion.ts +122 -0
- package/plug-api/lib/tree.ts +232 -0
- package/plug-api/lib/yaml.ts +284 -0
- package/plug-api/syscall.ts +15 -0
- package/plug-api/syscalls/asset.ts +36 -0
- package/plug-api/syscalls/client_store.ts +33 -0
- package/plug-api/syscalls/code_widget.ts +8 -0
- package/plug-api/syscalls/config.ts +58 -0
- package/plug-api/syscalls/datastore.ts +96 -0
- package/plug-api/syscalls/editor.ts +517 -0
- package/plug-api/syscalls/event.ts +47 -0
- package/plug-api/syscalls/index.ts +77 -0
- package/plug-api/syscalls/jsonschema.ts +25 -0
- package/plug-api/syscalls/language.ts +23 -0
- package/plug-api/syscalls/lua.ts +20 -0
- package/plug-api/syscalls/markdown.ts +38 -0
- package/plug-api/syscalls/mq.ts +79 -0
- package/plug-api/syscalls/shell.ts +14 -0
- package/plug-api/syscalls/space.ts +212 -0
- package/plug-api/syscalls/sync.ts +28 -0
- package/plug-api/syscalls/system.ts +102 -0
- package/plug-api/syscalls/yaml.ts +28 -0
- package/plug-api/syscalls.ts +21 -0
- package/plug-api/system_mock.ts +89 -0
- package/plug-api/types/client.ts +116 -0
- package/plug-api/types/config.ts +22 -0
- package/plug-api/types/datastore.ts +28 -0
- package/plug-api/types/event.ts +27 -0
- package/plug-api/types/index.ts +56 -0
- package/plug-api/types/manifest.ts +98 -0
- package/plug-api/types/namespace.ts +6 -0
- package/plugs/builtin_plugs.ts +14 -0
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ILuaFunction,
|
|
3
|
+
isILuaFunction,
|
|
4
|
+
isLuaTable,
|
|
5
|
+
LuaBuiltinFunction,
|
|
6
|
+
luaCall,
|
|
7
|
+
luaCloseFromMark,
|
|
8
|
+
luaEnsureCloseStack,
|
|
9
|
+
LuaEnv,
|
|
10
|
+
luaGet,
|
|
11
|
+
luaKeys,
|
|
12
|
+
luaLen,
|
|
13
|
+
LuaMultiRes,
|
|
14
|
+
LuaRuntimeError,
|
|
15
|
+
type LuaStackFrame,
|
|
16
|
+
type LuaTable,
|
|
17
|
+
luaToString,
|
|
18
|
+
luaTypeOf,
|
|
19
|
+
type LuaValue,
|
|
20
|
+
} from "./runtime.ts";
|
|
21
|
+
import { stringApi } from "./stdlib/string.ts";
|
|
22
|
+
import { tableApi } from "./stdlib/table.ts";
|
|
23
|
+
import { osApi } from "./stdlib/os.ts";
|
|
24
|
+
import { jsApi } from "./stdlib/js.ts";
|
|
25
|
+
import { spaceluaApi } from "./stdlib/space_lua.ts";
|
|
26
|
+
import { mathApi } from "./stdlib/math.ts";
|
|
27
|
+
import { parse } from "./parse.ts";
|
|
28
|
+
import { evalStatement } from "./eval.ts";
|
|
29
|
+
import { encodingApi } from "./stdlib/encoding.ts";
|
|
30
|
+
import { luaToNumberDetailed } from "./tonumber.ts";
|
|
31
|
+
import { luaLoad } from "./stdlib/load.ts";
|
|
32
|
+
import { cryptoApi } from "./stdlib/crypto.ts";
|
|
33
|
+
import { netApi } from "./stdlib/net.ts";
|
|
34
|
+
import { isTaggedFloat, makeLuaFloat } from "./numeric.ts";
|
|
35
|
+
|
|
36
|
+
const printFunction = new LuaBuiltinFunction(async (_sf, ...args) => {
|
|
37
|
+
console.log(
|
|
38
|
+
"[Lua]",
|
|
39
|
+
...(await Promise.all(args.map((v) => luaToString(v)))),
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const assertFunction = new LuaBuiltinFunction(
|
|
44
|
+
async (sf, value: any, message?: string) => {
|
|
45
|
+
if (!await value) {
|
|
46
|
+
throw new LuaRuntimeError(`Assertion failed: ${message}`, sf);
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const ipairsFunction = new LuaBuiltinFunction((sf, t: LuaTable | any[]) => {
|
|
52
|
+
let i = 0;
|
|
53
|
+
|
|
54
|
+
return async () => {
|
|
55
|
+
i = i + 1;
|
|
56
|
+
|
|
57
|
+
const v = await luaGet(t, i, sf.astCtx ?? null, sf);
|
|
58
|
+
if (v === null || v === undefined) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return new LuaMultiRes([i, v]);
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const pairsFunction = new LuaBuiltinFunction(
|
|
67
|
+
(sf, t: LuaTable | any[] | Record<string, any>) => {
|
|
68
|
+
// Respect `__pairs` metamethod for Lua tables
|
|
69
|
+
if (isLuaTable(t)) {
|
|
70
|
+
const mt = (t as any).metatable as LuaTable | null | undefined;
|
|
71
|
+
if (mt) {
|
|
72
|
+
const mm = mt.get("__pairs", sf);
|
|
73
|
+
if (mm && (typeof mm === "function" || isILuaFunction(mm))) {
|
|
74
|
+
// __pairs must return (iter, state, control, closing)
|
|
75
|
+
return luaCall(mm, [t], sf.astCtx ?? {}, sf);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let keys: (string | number)[];
|
|
81
|
+
if (Array.isArray(t)) {
|
|
82
|
+
keys = Array.from({ length: t.length }, (_, i) => i + 1); // For arrays, generate 1-based indices
|
|
83
|
+
} else if (isLuaTable(t) || t instanceof LuaEnv) {
|
|
84
|
+
keys = t.keys();
|
|
85
|
+
} else {
|
|
86
|
+
// For plain JavaScript objects case, note: this will also include keys from the prototype
|
|
87
|
+
keys = [];
|
|
88
|
+
for (const key in t) {
|
|
89
|
+
keys.push(key);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
let i = 0;
|
|
94
|
+
const iter = async () => {
|
|
95
|
+
if (i >= keys.length) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const key = keys[i];
|
|
99
|
+
i++;
|
|
100
|
+
const value = await luaGet(t, key, sf.astCtx ?? null, sf);
|
|
101
|
+
return new LuaMultiRes([key, value]);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// Must return (iter, state, control) for generic for
|
|
105
|
+
return new LuaMultiRes([iter, t, null]);
|
|
106
|
+
},
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
export const eachFunction = new LuaBuiltinFunction(
|
|
110
|
+
(sf, ar: LuaTable | any[]) => {
|
|
111
|
+
let i = 1;
|
|
112
|
+
const length = (ar as any).length;
|
|
113
|
+
return async () => {
|
|
114
|
+
if (i > length) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const result = await luaGet(ar, i, sf.astCtx ?? null, sf);
|
|
118
|
+
i++;
|
|
119
|
+
return result;
|
|
120
|
+
};
|
|
121
|
+
},
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
const unpackFunction = new LuaBuiltinFunction(async (sf, t: LuaTable) => {
|
|
125
|
+
const values: LuaValue[] = [];
|
|
126
|
+
for (let i = 1; i <= (t as any).length; i++) {
|
|
127
|
+
values.push(await luaGet(t, i, sf.astCtx ?? null, sf));
|
|
128
|
+
}
|
|
129
|
+
return new LuaMultiRes(values);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const typeFunction = new LuaBuiltinFunction(
|
|
133
|
+
(_sf, value: LuaValue): string | Promise<string> => {
|
|
134
|
+
return luaTypeOf(value);
|
|
135
|
+
},
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
const tostringFunction = new LuaBuiltinFunction((_sf, value: any) => {
|
|
139
|
+
return luaToString(value);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const tonumberFunction = new LuaBuiltinFunction(
|
|
143
|
+
(sf, value: LuaValue, base?: number) => {
|
|
144
|
+
if (base !== undefined) {
|
|
145
|
+
if (!(typeof base === "number" && base >= 2 && base <= 36)) {
|
|
146
|
+
throw new LuaRuntimeError(
|
|
147
|
+
"bad argument #2 to 'tonumber' (base out of range)",
|
|
148
|
+
sf,
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (typeof value === "number") {
|
|
154
|
+
return value;
|
|
155
|
+
}
|
|
156
|
+
if (isTaggedFloat(value)) {
|
|
157
|
+
return value;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (typeof value !== "string") {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const result = luaToNumberDetailed(value, base);
|
|
165
|
+
if (result === null) {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (result.numericType === "float") {
|
|
170
|
+
return makeLuaFloat(result.value);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return result.value;
|
|
174
|
+
},
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
const errorFunction = new LuaBuiltinFunction((sf, message: string) => {
|
|
178
|
+
throw new LuaRuntimeError(message, sf);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
async function pcallBoundary(
|
|
182
|
+
sf: LuaStackFrame,
|
|
183
|
+
fn: ILuaFunction,
|
|
184
|
+
args: LuaValue[],
|
|
185
|
+
): Promise<
|
|
186
|
+
| { ok: true; values: LuaValue[] }
|
|
187
|
+
| { ok: false; message: string }
|
|
188
|
+
> {
|
|
189
|
+
const closeStack = luaEnsureCloseStack(sf);
|
|
190
|
+
const mark = closeStack.length;
|
|
191
|
+
|
|
192
|
+
const errMsgOf = (e: any): string =>
|
|
193
|
+
e instanceof LuaRuntimeError ? e.message : (e?.message ?? String(e));
|
|
194
|
+
|
|
195
|
+
try {
|
|
196
|
+
const r = await luaCall(fn, args, sf.astCtx!, sf);
|
|
197
|
+
await luaCloseFromMark(sf, mark, null);
|
|
198
|
+
const values = r instanceof LuaMultiRes ? r.flatten().values : [r];
|
|
199
|
+
return { ok: true, values };
|
|
200
|
+
} catch (e: any) {
|
|
201
|
+
const msg = errMsgOf(e);
|
|
202
|
+
try {
|
|
203
|
+
await luaCloseFromMark(sf, mark, msg);
|
|
204
|
+
return { ok: false, message: msg };
|
|
205
|
+
} catch (closeErr: any) {
|
|
206
|
+
return { ok: false, message: errMsgOf(closeErr) };
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const pcallFunction = new LuaBuiltinFunction(
|
|
212
|
+
async (sf, fn: ILuaFunction, ...args) => {
|
|
213
|
+
// To-be-closed variables must be closed when unwinding to the
|
|
214
|
+
// protected call boundary. Space Lua uses a per-thread close
|
|
215
|
+
// stack, so we snapshot its length and close anything pushed
|
|
216
|
+
// after that.
|
|
217
|
+
//
|
|
218
|
+
// The protected call boundary must be established *before*
|
|
219
|
+
// evaluating the function and its arguments. Otherwise, any
|
|
220
|
+
// `<close>` locals created while evaluating `pcall`'s arguments
|
|
221
|
+
// will be wrongly treated as "inside" the protected call, and
|
|
222
|
+
// `pcall` may end up closing them (or affecting close ordering).
|
|
223
|
+
//
|
|
224
|
+
// `threadState` is read-only on the stack frame; do not reassign!
|
|
225
|
+
const res = await pcallBoundary(sf, fn, args);
|
|
226
|
+
if (res.ok) {
|
|
227
|
+
return new LuaMultiRes([true, ...res.values]);
|
|
228
|
+
}
|
|
229
|
+
return new LuaMultiRes([false, res.message]);
|
|
230
|
+
},
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
const xpcallFunction = new LuaBuiltinFunction(
|
|
234
|
+
async (sf, fn: ILuaFunction, errorHandler: ILuaFunction, ...args) => {
|
|
235
|
+
// Same semantic as `pcall` (see comments there)
|
|
236
|
+
const res = await pcallBoundary(sf, fn, args);
|
|
237
|
+
if (res.ok) {
|
|
238
|
+
return new LuaMultiRes([true, ...res.values]);
|
|
239
|
+
}
|
|
240
|
+
const hr = await luaCall(errorHandler, [res.message], sf.astCtx!, sf);
|
|
241
|
+
const outVals = hr instanceof LuaMultiRes ? hr.flatten().values : [hr];
|
|
242
|
+
return new LuaMultiRes([false, ...outVals]);
|
|
243
|
+
},
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
const setmetatableFunction = new LuaBuiltinFunction(
|
|
247
|
+
(sf, table: LuaTable, metatable: LuaTable) => {
|
|
248
|
+
if (!metatable) {
|
|
249
|
+
throw new LuaRuntimeError("metatable cannot be set to nil", sf);
|
|
250
|
+
}
|
|
251
|
+
table.metatable = metatable;
|
|
252
|
+
return table;
|
|
253
|
+
},
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
const rawlenFunction = new LuaBuiltinFunction(
|
|
257
|
+
(_sf, value: LuaValue) => {
|
|
258
|
+
return luaLen(value, _sf);
|
|
259
|
+
},
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
const rawsetFunction = new LuaBuiltinFunction(
|
|
263
|
+
(_sf, table: LuaTable, key: LuaValue, value: LuaValue) => {
|
|
264
|
+
return (table as any).rawSet(key, value);
|
|
265
|
+
},
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
const rawgetFunction = new LuaBuiltinFunction(
|
|
269
|
+
(_sf, table: any, key: LuaValue) => {
|
|
270
|
+
const isArray = Array.isArray(table);
|
|
271
|
+
|
|
272
|
+
const isPlainObj = typeof table === "object" &&
|
|
273
|
+
table !== null &&
|
|
274
|
+
(table as any).constructor === Object;
|
|
275
|
+
|
|
276
|
+
if (!isLuaTable(table) && !isArray && !isPlainObj) {
|
|
277
|
+
let typeName = "userdata";
|
|
278
|
+
if (table === null || table === undefined) {
|
|
279
|
+
typeName = "nil";
|
|
280
|
+
} else if (typeof table === "boolean") {
|
|
281
|
+
typeName = "boolean";
|
|
282
|
+
} else if (typeof table === "number" || isTaggedFloat(table)) {
|
|
283
|
+
typeName = "number";
|
|
284
|
+
} else if (typeof table === "string") {
|
|
285
|
+
typeName = "string";
|
|
286
|
+
} else if (
|
|
287
|
+
typeof table === "function" ||
|
|
288
|
+
(typeof table === "object" &&
|
|
289
|
+
table !== null &&
|
|
290
|
+
typeof (table as any).call === "function")
|
|
291
|
+
) {
|
|
292
|
+
typeName = "function";
|
|
293
|
+
}
|
|
294
|
+
throw new LuaRuntimeError(
|
|
295
|
+
`bad argument #1 to 'rawget' (table expected, got ${typeName})`,
|
|
296
|
+
_sf,
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (isLuaTable(table)) {
|
|
301
|
+
const v = table.rawGet(key);
|
|
302
|
+
return v === undefined ? null : v;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const k = isTaggedFloat(key) ? key.value : key;
|
|
306
|
+
|
|
307
|
+
if (isArray) {
|
|
308
|
+
if (typeof k === "number") {
|
|
309
|
+
const v = (table as any[])[k - 1];
|
|
310
|
+
return v === undefined ? null : v;
|
|
311
|
+
}
|
|
312
|
+
const v = (table as Record<string, any>)[k];
|
|
313
|
+
return v === undefined ? null : v;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const v = (table as Record<string | number, any>)[k as any];
|
|
317
|
+
return v === undefined ? null : v;
|
|
318
|
+
},
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
const rawequalFunction = new LuaBuiltinFunction(
|
|
322
|
+
(_sf, a: any, b: any) => {
|
|
323
|
+
const av = isTaggedFloat(a) ? a.value : a;
|
|
324
|
+
const bv = isTaggedFloat(b) ? b.value : b;
|
|
325
|
+
return av === bv;
|
|
326
|
+
},
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
const getmetatableFunction = new LuaBuiltinFunction((_sf, table: LuaTable) => {
|
|
330
|
+
return (table as any).metatable;
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
const dofileFunction = new LuaBuiltinFunction(async (sf, filename: string) => {
|
|
334
|
+
const global = sf.threadLocal.get("_GLOBAL") as LuaEnv;
|
|
335
|
+
const file = await luaCall(
|
|
336
|
+
(global.get("space") as any).get("readFile"),
|
|
337
|
+
[filename],
|
|
338
|
+
sf.astCtx!,
|
|
339
|
+
sf,
|
|
340
|
+
) as Uint8Array;
|
|
341
|
+
const code = new TextDecoder().decode(file);
|
|
342
|
+
try {
|
|
343
|
+
const parsedExpr = parse(code);
|
|
344
|
+
const env = new LuaEnv(global);
|
|
345
|
+
await evalStatement(parsedExpr, env, sf.withCtx(parsedExpr.ctx));
|
|
346
|
+
} catch (e: any) {
|
|
347
|
+
throw new LuaRuntimeError(
|
|
348
|
+
`Error evaluating "${filename}": ${e.message}`,
|
|
349
|
+
sf,
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* From the Lua docs:
|
|
356
|
+
*
|
|
357
|
+
* If index is a number, returns all arguments after argument number
|
|
358
|
+
* index; a negative number indexes from the end (-1 is the last
|
|
359
|
+
* argument). Otherwise, index must be the string "#", and select
|
|
360
|
+
* returns the total number of extra arguments it received.
|
|
361
|
+
*/
|
|
362
|
+
const selectFunction = new LuaBuiltinFunction(
|
|
363
|
+
(_sf, index: number | "#", ...args: LuaValue[]) => {
|
|
364
|
+
if (index === "#") {
|
|
365
|
+
return args.length;
|
|
366
|
+
}
|
|
367
|
+
if (typeof index === "number") {
|
|
368
|
+
if (index >= 0) {
|
|
369
|
+
return new LuaMultiRes(args.slice(index - 1));
|
|
370
|
+
}
|
|
371
|
+
return new LuaMultiRes(args.slice(args.length + index));
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* From the Lua docs:
|
|
378
|
+
*
|
|
379
|
+
* Allows a program to traverse all fields of a table. Its first
|
|
380
|
+
* argument is a table and its second argument is an index in this
|
|
381
|
+
* table. A call to next returns the next index of the table and its
|
|
382
|
+
* associated value. When called with nil as its second argument, next
|
|
383
|
+
* returns an initial index and its associated value. When called with
|
|
384
|
+
* the last index, or with nil in an empty table, next returns nil. If
|
|
385
|
+
* the second argument is absent, then it is interpreted as nil. In
|
|
386
|
+
* particular, you can use next(t) to check whether a table is empty.
|
|
387
|
+
*
|
|
388
|
+
* The order in which the indices are enumerated is not specified, even
|
|
389
|
+
* for numeric indices. (To traverse a table in numerical order, use
|
|
390
|
+
* a numerical for.)
|
|
391
|
+
*
|
|
392
|
+
* You should not assign any value to a non-existent field in a table
|
|
393
|
+
* during its traversal. You may however modify existing fields. In
|
|
394
|
+
* particular, you may set existing fields to nil.
|
|
395
|
+
*/
|
|
396
|
+
const nextFunction = new LuaBuiltinFunction(
|
|
397
|
+
(sf, table: LuaTable | Record<string, any>, index: number | null = null) => {
|
|
398
|
+
if (!table) {
|
|
399
|
+
// When nil value
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
const keys = luaKeys(table);
|
|
403
|
+
|
|
404
|
+
// Empty table -> null return value
|
|
405
|
+
if (keys.length === 0) {
|
|
406
|
+
return null;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (index === null) {
|
|
410
|
+
// Return the first key, value
|
|
411
|
+
const key = keys[0];
|
|
412
|
+
return new LuaMultiRes([key, luaGet(table, key, sf.astCtx ?? null, sf)]);
|
|
413
|
+
}
|
|
414
|
+
// Find index in the key list
|
|
415
|
+
const idx = keys.indexOf(index);
|
|
416
|
+
if (idx === -1) { // Not found
|
|
417
|
+
throw new LuaRuntimeError("invalid key to 'next': key not found", sf);
|
|
418
|
+
}
|
|
419
|
+
const key = keys[idx + 1];
|
|
420
|
+
if (key === undefined) {
|
|
421
|
+
// When called with the last key, should return nil
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
return new LuaMultiRes([key, luaGet(table, key, sf.astCtx ?? null, sf)]);
|
|
425
|
+
},
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
// Non-standard, but useful
|
|
429
|
+
const someFunction = new LuaBuiltinFunction(async (_sf, value: any) => {
|
|
430
|
+
switch (await luaTypeOf(value)) {
|
|
431
|
+
case "number":
|
|
432
|
+
if (!isFinite(value)) return null;
|
|
433
|
+
break;
|
|
434
|
+
case "string":
|
|
435
|
+
if (value.trim() === "") return null;
|
|
436
|
+
break;
|
|
437
|
+
case "table":
|
|
438
|
+
if (luaKeys(value).length === 0) return null;
|
|
439
|
+
}
|
|
440
|
+
return value;
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
const loadFunction = new LuaBuiltinFunction((sf, s) => luaLoad(s, sf));
|
|
444
|
+
|
|
445
|
+
export function luaBuildStandardEnv() {
|
|
446
|
+
const env = new LuaEnv();
|
|
447
|
+
// _G global
|
|
448
|
+
env.set("_G", env);
|
|
449
|
+
// Top-level builtins
|
|
450
|
+
env.set("print", printFunction);
|
|
451
|
+
env.set("assert", assertFunction);
|
|
452
|
+
env.set("type", typeFunction);
|
|
453
|
+
env.set("tostring", tostringFunction);
|
|
454
|
+
env.set("tonumber", tonumberFunction);
|
|
455
|
+
env.set("unpack", unpackFunction);
|
|
456
|
+
env.set("select", selectFunction);
|
|
457
|
+
env.set("next", nextFunction);
|
|
458
|
+
// Iterators
|
|
459
|
+
env.set("pairs", pairsFunction);
|
|
460
|
+
env.set("ipairs", ipairsFunction);
|
|
461
|
+
// meta table stuff
|
|
462
|
+
env.set("setmetatable", setmetatableFunction);
|
|
463
|
+
env.set("getmetatable", getmetatableFunction);
|
|
464
|
+
env.set("rawlen", rawlenFunction);
|
|
465
|
+
env.set("rawset", rawsetFunction);
|
|
466
|
+
env.set("rawget", rawgetFunction);
|
|
467
|
+
env.set("rawequal", rawequalFunction);
|
|
468
|
+
env.set("dofile", dofileFunction);
|
|
469
|
+
// Error handling
|
|
470
|
+
env.set("error", errorFunction);
|
|
471
|
+
env.set("pcall", pcallFunction);
|
|
472
|
+
env.set("xpcall", xpcallFunction);
|
|
473
|
+
// Evaluation
|
|
474
|
+
env.set("load", loadFunction);
|
|
475
|
+
// APIs
|
|
476
|
+
env.set("string", stringApi);
|
|
477
|
+
env.set("table", tableApi);
|
|
478
|
+
env.set("os", osApi);
|
|
479
|
+
env.set("js", jsApi);
|
|
480
|
+
env.set("math", mathApi);
|
|
481
|
+
// Non-standard
|
|
482
|
+
env.set("each", eachFunction);
|
|
483
|
+
env.set("spacelua", spaceluaApi);
|
|
484
|
+
env.set("encoding", encodingApi);
|
|
485
|
+
env.set("crypto", cryptoApi);
|
|
486
|
+
env.set("net", netApi);
|
|
487
|
+
env.set("some", someFunction);
|
|
488
|
+
return env;
|
|
489
|
+
}
|