@silverbulletmd/silverbullet 2.9.0 → 2.10.0
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/README.md +36 -23
- package/client/plugos/hooks/syscall.ts +24 -10
- package/client/plugos/syscalls/asset.ts +61 -23
- package/client/plugos/syscalls/clientStore.ts +32 -6
- package/client/plugos/syscalls/client_code_widget.ts +9 -4
- package/client/plugos/syscalls/code_widget.ts +26 -11
- package/client/plugos/syscalls/config.ts +146 -30
- package/client/plugos/syscalls/datastore.ts +146 -40
- package/client/plugos/syscalls/editor.ts +1432 -711
- package/client/plugos/syscalls/event.ts +46 -13
- package/client/plugos/syscalls/fetch.ts +123 -82
- package/client/plugos/syscalls/index.ts +242 -116
- package/client/plugos/syscalls/jsonschema.ts +107 -8
- package/client/plugos/syscalls/language.ts +38 -12
- package/client/plugos/syscalls/markdown.ts +166 -50
- package/client/plugos/syscalls/mq.ts +93 -20
- package/client/plugos/syscalls/schema_introspection.ts +33 -0
- package/client/plugos/syscalls/service_registry.ts +74 -20
- package/client/plugos/syscalls/shell.ts +51 -20
- package/client/plugos/syscalls/space.ts +169 -113
- package/client/plugos/syscalls/sync.ts +50 -19
- package/client/plugos/syscalls/system.ts +236 -139
- package/client/plugos/system.ts +21 -5
- package/client/space_lua/api_documentation.ts +107 -0
- package/client/space_lua/ast.ts +13 -0
- package/client/space_lua/eval.ts +12 -4
- package/client/space_lua/parse.ts +243 -108
- package/client/space_lua/pretty_print.ts +128 -54
- package/client/space_lua/query_collection.ts +4 -4
- package/client/space_lua/render_widget.ts +156 -0
- package/client/space_lua/runtime.ts +83 -22
- package/client/space_lua/stdlib/crypto.ts +8 -3
- package/client/space_lua/stdlib/encoding.ts +27 -9
- package/client/space_lua/stdlib/js.ts +137 -22
- package/client/space_lua/stdlib/load.ts +24 -15
- package/client/space_lua/stdlib/math.ts +323 -118
- package/client/space_lua/stdlib/net.ts +57 -9
- package/client/space_lua/stdlib/os.ts +111 -44
- package/client/space_lua/stdlib/space_lua.ts +344 -20
- package/client/space_lua/stdlib/string.ts +282 -104
- package/client/space_lua/stdlib/string_pack.ts +58 -19
- package/client/space_lua/stdlib/table.ts +195 -40
- package/client/space_lua/stdlib.ts +335 -93
- package/client/space_lua/syscalls.ts +270 -0
- package/dist/plug-compile.js +1 -1
- package/package.json +3 -3
- package/plug-api/lib/ref.ts +96 -0
- package/plug-api/lib/shortcut.ts +2 -1
- package/plug-api/syscalls/config.ts +5 -3
- package/plug-api/syscalls/editor.ts +16 -0
- package/plug-api/syscalls/index.ts +17 -1
- package/plug-api/syscalls/jsonschema.ts +9 -0
- package/plug-api/syscalls/lua.ts +7 -0
- package/plug-api/syscalls/shell.ts +0 -1
- package/plug-api/syscalls/system.ts +7 -0
- package/plug-api/system_mock.ts +2 -2
- package/plug-api/types/index.ts +52 -1
- package/plug-api/types/manifest.ts +22 -4
- package/plug-api/ui/cx.ts +1 -3
- package/plug-api/ui/index.ts +3 -0
- package/plug-api/ui/panel_styles.ts +4 -3
- package/plug-api/ui/slugify.ts +44 -0
- package/client/plugos/syscalls/lua.ts +0 -81
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
LuaMultiRes,
|
|
15
15
|
LuaRuntimeError,
|
|
16
16
|
type LuaStackFrame,
|
|
17
|
-
|
|
17
|
+
LuaTable,
|
|
18
18
|
luaToString,
|
|
19
19
|
luaTypeOf,
|
|
20
20
|
type LuaValue,
|
|
@@ -37,35 +37,70 @@ import { isTaggedFloat, makeLuaFloat } from "./numeric.ts";
|
|
|
37
37
|
import { isPromise } from "./rp.ts";
|
|
38
38
|
import { isSqlNull } from "./sliq_null.ts";
|
|
39
39
|
|
|
40
|
-
const printFunction = new LuaBuiltinFunction(
|
|
41
|
-
|
|
40
|
+
const printFunction = new LuaBuiltinFunction({
|
|
41
|
+
callback: async (_sf, ...args) => {
|
|
42
|
+
console.log(
|
|
43
|
+
"[Lua]",
|
|
44
|
+
...(await Promise.all(args.map((v) => luaToString(v)))),
|
|
45
|
+
);
|
|
46
|
+
},
|
|
47
|
+
description:
|
|
48
|
+
"Prints string representations of its arguments to the runtime log.",
|
|
49
|
+
signatures: ["print(...)"],
|
|
50
|
+
parameters: [{ name: "...", description: "Values to print." }],
|
|
51
|
+
examples: [{ code: 'print("Hello, world!")' }],
|
|
42
52
|
});
|
|
43
53
|
|
|
44
|
-
const assertFunction = new LuaBuiltinFunction(
|
|
45
|
-
async (sf, value: any, message?: string) => {
|
|
54
|
+
const assertFunction = new LuaBuiltinFunction({
|
|
55
|
+
callback: async (sf, value: any, message?: string) => {
|
|
46
56
|
if (!(await value)) {
|
|
47
57
|
throw new LuaRuntimeError(`Assertion failed: ${message}`, sf);
|
|
48
58
|
}
|
|
49
59
|
},
|
|
50
|
-
|
|
60
|
+
description:
|
|
61
|
+
"Raises an error when a value is falsy; otherwise completes successfully.",
|
|
62
|
+
parameters: [
|
|
63
|
+
{ name: "value", description: "Condition to test." },
|
|
64
|
+
{
|
|
65
|
+
name: "message",
|
|
66
|
+
type: "string",
|
|
67
|
+
description: "Error detail.",
|
|
68
|
+
optional: true,
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
examples: [{ code: 'assert(user ~= nil, "user is required")' }],
|
|
72
|
+
});
|
|
51
73
|
|
|
52
|
-
const ipairsFunction = new LuaBuiltinFunction(
|
|
53
|
-
|
|
74
|
+
const ipairsFunction = new LuaBuiltinFunction({
|
|
75
|
+
callback: (sf, t: LuaTable | any[]) => {
|
|
76
|
+
let i = 0;
|
|
54
77
|
|
|
55
|
-
|
|
56
|
-
|
|
78
|
+
return async () => {
|
|
79
|
+
i = i + 1;
|
|
57
80
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
81
|
+
const v = await luaGet(t, i, sf.astCtx ?? null, sf);
|
|
82
|
+
if (v === null || v === undefined) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
62
85
|
|
|
63
|
-
|
|
64
|
-
|
|
86
|
+
return new LuaMultiRes([i, v]);
|
|
87
|
+
};
|
|
88
|
+
},
|
|
89
|
+
description:
|
|
90
|
+
"Returns an iterator over consecutive integer keys starting at 1 and stopping at the first `nil`.",
|
|
91
|
+
parameters: [{ name: "table", type: "table" }],
|
|
92
|
+
returns: [
|
|
93
|
+
{ type: "function", description: "Iterator yielding index and value." },
|
|
94
|
+
],
|
|
95
|
+
examples: [
|
|
96
|
+
{
|
|
97
|
+
code: 'for i, fruit in ipairs({"apple", "banana"}) do\n print(i, fruit)\nend',
|
|
98
|
+
},
|
|
99
|
+
],
|
|
65
100
|
});
|
|
66
101
|
|
|
67
|
-
const pairsFunction = new LuaBuiltinFunction(
|
|
68
|
-
(sf, t: LuaTable | any[] | Record<string, any>) => {
|
|
102
|
+
const pairsFunction = new LuaBuiltinFunction({
|
|
103
|
+
callback: (sf, t: LuaTable | any[] | Record<string, any>) => {
|
|
69
104
|
// Respect `__pairs` metamethod for Lua tables
|
|
70
105
|
if (isLuaTable(t)) {
|
|
71
106
|
const mt = (t as any).metatable as LuaTable | null | undefined;
|
|
@@ -105,10 +140,24 @@ const pairsFunction = new LuaBuiltinFunction(
|
|
|
105
140
|
// Must return (iter, state, control) for generic for
|
|
106
141
|
return new LuaMultiRes([iter, t, null]);
|
|
107
142
|
},
|
|
108
|
-
|
|
143
|
+
description:
|
|
144
|
+
"Returns an iterator over all table key-value pairs, respecting `__pairs`.",
|
|
145
|
+
parameters: [{ name: "table", type: "table" }],
|
|
146
|
+
returns: [
|
|
147
|
+
{
|
|
148
|
+
type: "function",
|
|
149
|
+
description: "Iterator plus its state and initial control value.",
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
examples: [
|
|
153
|
+
{
|
|
154
|
+
code: 'for key, value in pairs({name = "Ada", age = 36}) do\n print(key, value)\nend',
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
});
|
|
109
158
|
|
|
110
|
-
export const eachFunction = new LuaBuiltinFunction(
|
|
111
|
-
(sf, ar: LuaTable | any[]) => {
|
|
159
|
+
export const eachFunction = new LuaBuiltinFunction({
|
|
160
|
+
callback: (sf, ar: LuaTable | any[]) => {
|
|
112
161
|
let i = 1;
|
|
113
162
|
const length = (ar as any).length;
|
|
114
163
|
return async () => {
|
|
@@ -120,18 +169,30 @@ export const eachFunction = new LuaBuiltinFunction(
|
|
|
120
169
|
return result;
|
|
121
170
|
};
|
|
122
171
|
},
|
|
123
|
-
|
|
172
|
+
description:
|
|
173
|
+
"Returns a Space Lua iterator over array-like values without yielding indices.",
|
|
174
|
+
parameters: [{ name: "table", type: "table" }],
|
|
175
|
+
returns: [{ type: "function", description: "Iterator yielding values." }],
|
|
176
|
+
examples: [
|
|
177
|
+
{
|
|
178
|
+
code: 'for fruit in each({"apple", "banana"}) do\n print(fruit)\nend',
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
});
|
|
124
182
|
|
|
125
|
-
const typeFunction = new LuaBuiltinFunction(
|
|
126
|
-
(_sf, value: LuaValue): string | Promise<string> => {
|
|
183
|
+
const typeFunction = new LuaBuiltinFunction({
|
|
184
|
+
callback: (_sf, value: LuaValue): string | Promise<string> => {
|
|
127
185
|
return luaTypeOf(value);
|
|
128
186
|
},
|
|
129
|
-
|
|
187
|
+
description: "Returns the Lua type name of a value.",
|
|
188
|
+
parameters: [{ name: "value" }],
|
|
189
|
+
returns: [{ type: "string" }],
|
|
190
|
+
});
|
|
130
191
|
|
|
131
192
|
// tostring() checks `__tostring` metamethod first (with live SF), then
|
|
132
193
|
// falls back to the default `luaToString` representation.
|
|
133
|
-
const tostringFunction = new LuaBuiltinFunction(
|
|
134
|
-
(sf, value: any): string | Promise<string> => {
|
|
194
|
+
const tostringFunction = new LuaBuiltinFunction({
|
|
195
|
+
callback: (sf, value: any): string | Promise<string> => {
|
|
135
196
|
const mt = getMetatable(value, sf);
|
|
136
197
|
if (mt) {
|
|
137
198
|
const mm = mt.rawGet("__tostring");
|
|
@@ -153,10 +214,14 @@ const tostringFunction = new LuaBuiltinFunction(
|
|
|
153
214
|
}
|
|
154
215
|
return luaToString(value);
|
|
155
216
|
},
|
|
156
|
-
|
|
217
|
+
description:
|
|
218
|
+
"Converts a value to a string, respecting its `__tostring` metamethod.",
|
|
219
|
+
parameters: [{ name: "value" }],
|
|
220
|
+
returns: [{ type: "string" }],
|
|
221
|
+
});
|
|
157
222
|
|
|
158
|
-
const tonumberFunction = new LuaBuiltinFunction(
|
|
159
|
-
(sf, value: LuaValue, base?: number) => {
|
|
223
|
+
const tonumberFunction = new LuaBuiltinFunction({
|
|
224
|
+
callback: (sf, value: LuaValue, base?: number) => {
|
|
160
225
|
if (base !== undefined) {
|
|
161
226
|
if (!(typeof base === "number" && base >= 2 && base <= 36)) {
|
|
162
227
|
throw new LuaRuntimeError(
|
|
@@ -188,10 +253,26 @@ const tonumberFunction = new LuaBuiltinFunction(
|
|
|
188
253
|
|
|
189
254
|
return result.value;
|
|
190
255
|
},
|
|
191
|
-
|
|
256
|
+
description:
|
|
257
|
+
"Converts a number or numeric string to a Lua number, optionally in a base from 2 through 36.",
|
|
258
|
+
signatures: [
|
|
259
|
+
"tonumber(value): number|nil",
|
|
260
|
+
"tonumber(value, base): integer|nil",
|
|
261
|
+
],
|
|
262
|
+
parameters: [
|
|
263
|
+
{ name: "value", type: "number|string" },
|
|
264
|
+
{ name: "base", type: "integer", optional: true },
|
|
265
|
+
],
|
|
266
|
+
returns: [{ type: "number|nil" }],
|
|
267
|
+
examples: [{ code: 'print(tonumber("2a", 16)) -- 42' }],
|
|
268
|
+
});
|
|
192
269
|
|
|
193
|
-
const errorFunction = new LuaBuiltinFunction(
|
|
194
|
-
|
|
270
|
+
const errorFunction = new LuaBuiltinFunction({
|
|
271
|
+
callback: (sf, message: string) => {
|
|
272
|
+
throw new LuaRuntimeError(message, sf);
|
|
273
|
+
},
|
|
274
|
+
description: "Raises a Lua runtime error with the supplied message.",
|
|
275
|
+
parameters: [{ name: "message", type: "string" }],
|
|
195
276
|
});
|
|
196
277
|
|
|
197
278
|
async function pcallBoundary(
|
|
@@ -221,8 +302,8 @@ async function pcallBoundary(
|
|
|
221
302
|
}
|
|
222
303
|
}
|
|
223
304
|
|
|
224
|
-
const pcallFunction = new LuaBuiltinFunction(
|
|
225
|
-
async (sf, fn: ILuaFunction, ...args) => {
|
|
305
|
+
const pcallFunction = new LuaBuiltinFunction({
|
|
306
|
+
callback: async (sf, fn: ILuaFunction, ...args) => {
|
|
226
307
|
// To-be-closed variables must be closed when unwinding to the
|
|
227
308
|
// protected call boundary. Space Lua uses a per-thread close
|
|
228
309
|
// stack, so we snapshot its length and close anything pushed
|
|
@@ -241,10 +322,29 @@ const pcallFunction = new LuaBuiltinFunction(
|
|
|
241
322
|
}
|
|
242
323
|
return new LuaMultiRes([false, res.message]);
|
|
243
324
|
},
|
|
244
|
-
|
|
325
|
+
description:
|
|
326
|
+
"Calls a function in protected mode and returns a success flag followed by results or an error message.",
|
|
327
|
+
signatures: ["pcall(function, ...): boolean, ..."],
|
|
328
|
+
parameters: [
|
|
329
|
+
{ name: "function", type: "function" },
|
|
330
|
+
{ name: "...", description: "Arguments passed to the function." },
|
|
331
|
+
],
|
|
332
|
+
returns: [
|
|
333
|
+
{ type: "boolean", description: "Whether the call succeeded." },
|
|
334
|
+
{ description: "Call results or error message." },
|
|
335
|
+
],
|
|
336
|
+
examples: [
|
|
337
|
+
{ code: "local ok, result = pcall(function() return mightFail() end)" },
|
|
338
|
+
],
|
|
339
|
+
});
|
|
245
340
|
|
|
246
|
-
const xpcallFunction = new LuaBuiltinFunction(
|
|
247
|
-
async (
|
|
341
|
+
const xpcallFunction = new LuaBuiltinFunction({
|
|
342
|
+
callback: async (
|
|
343
|
+
sf,
|
|
344
|
+
fn: ILuaFunction,
|
|
345
|
+
errorHandler: ILuaFunction,
|
|
346
|
+
...args
|
|
347
|
+
) => {
|
|
248
348
|
// Same semantic as `pcall` (see comments there)
|
|
249
349
|
const res = await pcallBoundary(sf, fn, args);
|
|
250
350
|
if (res.ok) {
|
|
@@ -254,30 +354,69 @@ const xpcallFunction = new LuaBuiltinFunction(
|
|
|
254
354
|
const outVals = hr instanceof LuaMultiRes ? hr.flatten().values : [hr];
|
|
255
355
|
return new LuaMultiRes([false, ...outVals]);
|
|
256
356
|
},
|
|
257
|
-
|
|
357
|
+
description:
|
|
358
|
+
"Calls a function in protected mode and transforms any error with an error handler.",
|
|
359
|
+
signatures: ["xpcall(function, errorHandler, ...): boolean, ..."],
|
|
360
|
+
parameters: [
|
|
361
|
+
{ name: "function", type: "function" },
|
|
362
|
+
{ name: "errorHandler", type: "function" },
|
|
363
|
+
{ name: "...", description: "Arguments passed to the function." },
|
|
364
|
+
],
|
|
365
|
+
returns: [
|
|
366
|
+
{ type: "boolean", description: "Whether the call succeeded." },
|
|
367
|
+
{ description: "Call results or handler results." },
|
|
368
|
+
],
|
|
369
|
+
examples: [
|
|
370
|
+
{
|
|
371
|
+
code: 'local ok, message = xpcall(riskyOperation, function(err)\n return "Operation failed: " .. tostring(err)\nend)',
|
|
372
|
+
},
|
|
373
|
+
],
|
|
374
|
+
});
|
|
258
375
|
|
|
259
|
-
const setmetatableFunction = new LuaBuiltinFunction(
|
|
260
|
-
(sf, table: LuaTable, metatable: LuaTable) => {
|
|
376
|
+
const setmetatableFunction = new LuaBuiltinFunction({
|
|
377
|
+
callback: (sf, table: LuaTable, metatable: LuaTable) => {
|
|
261
378
|
if (!metatable) {
|
|
262
379
|
throw new LuaRuntimeError("metatable cannot be set to nil", sf);
|
|
263
380
|
}
|
|
264
381
|
table.metatable = metatable;
|
|
265
382
|
return table;
|
|
266
383
|
},
|
|
267
|
-
|
|
384
|
+
description: "Sets a table's metatable and returns the table.",
|
|
385
|
+
parameters: [
|
|
386
|
+
{ name: "table", type: "table" },
|
|
387
|
+
{ name: "metatable", type: "table" },
|
|
388
|
+
],
|
|
389
|
+
returns: [{ type: "table" }],
|
|
390
|
+
});
|
|
268
391
|
|
|
269
|
-
const rawlenFunction = new LuaBuiltinFunction(
|
|
270
|
-
|
|
392
|
+
const rawlenFunction = new LuaBuiltinFunction({
|
|
393
|
+
callback: (_sf, value: LuaValue) => luaLen(value, _sf, true),
|
|
394
|
+
description: "Returns a string or table length without invoking `__len`.",
|
|
395
|
+
parameters: [{ name: "value", type: "string|table" }],
|
|
396
|
+
returns: [{ type: "integer" }],
|
|
271
397
|
});
|
|
272
398
|
|
|
273
|
-
const rawsetFunction = new LuaBuiltinFunction(
|
|
274
|
-
(_sf, table: LuaTable, key: LuaValue, value: LuaValue) => {
|
|
399
|
+
const rawsetFunction = new LuaBuiltinFunction({
|
|
400
|
+
callback: (_sf, table: LuaTable, key: LuaValue, value: LuaValue) => {
|
|
275
401
|
return (table as any).rawSet(key, value);
|
|
276
402
|
},
|
|
277
|
-
|
|
403
|
+
description:
|
|
404
|
+
"Sets a table key without invoking `__newindex` and returns the table.",
|
|
405
|
+
parameters: [
|
|
406
|
+
{ name: "table", type: "table" },
|
|
407
|
+
{ name: "key" },
|
|
408
|
+
{ name: "value" },
|
|
409
|
+
],
|
|
410
|
+
returns: [{ type: "table" }],
|
|
411
|
+
examples: [
|
|
412
|
+
{
|
|
413
|
+
code: 'local t = setmetatable({}, {__newindex = function() error("blocked") end})\nrawset(t, "name", "Ada")',
|
|
414
|
+
},
|
|
415
|
+
],
|
|
416
|
+
});
|
|
278
417
|
|
|
279
|
-
const rawgetFunction = new LuaBuiltinFunction(
|
|
280
|
-
(_sf, table: any, key: LuaValue) => {
|
|
418
|
+
const rawgetFunction = new LuaBuiltinFunction({
|
|
419
|
+
callback: (_sf, table: any, key: LuaValue) => {
|
|
281
420
|
const isArray = Array.isArray(table);
|
|
282
421
|
|
|
283
422
|
const isPlainObj =
|
|
@@ -328,37 +467,58 @@ const rawgetFunction = new LuaBuiltinFunction(
|
|
|
328
467
|
const v = (table as Record<string | number, any>)[k as any];
|
|
329
468
|
return v === undefined ? null : v;
|
|
330
469
|
},
|
|
331
|
-
|
|
470
|
+
description: "Reads a table key without invoking `__index`.",
|
|
471
|
+
parameters: [{ name: "table", type: "table" }, { name: "key" }],
|
|
472
|
+
returns: [{ description: "Stored value or `nil`." }],
|
|
473
|
+
});
|
|
332
474
|
|
|
333
|
-
const rawequalFunction = new LuaBuiltinFunction(
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
475
|
+
const rawequalFunction = new LuaBuiltinFunction({
|
|
476
|
+
callback: (_sf, a: any, b: any) => {
|
|
477
|
+
const av = isTaggedFloat(a) ? a.value : a;
|
|
478
|
+
const bv = isTaggedFloat(b) ? b.value : b;
|
|
479
|
+
return av === bv;
|
|
480
|
+
},
|
|
481
|
+
description: "Tests two values for equality without invoking `__eq`.",
|
|
482
|
+
parameters: [{ name: "a" }, { name: "b" }],
|
|
483
|
+
returns: [{ type: "boolean" }],
|
|
337
484
|
});
|
|
338
485
|
|
|
339
|
-
const getmetatableFunction = new LuaBuiltinFunction(
|
|
340
|
-
|
|
486
|
+
const getmetatableFunction = new LuaBuiltinFunction({
|
|
487
|
+
callback: (_sf, table: LuaTable) => (table as any).metatable,
|
|
488
|
+
description: "Returns a table's metatable, or `nil` when none is set.",
|
|
489
|
+
parameters: [{ name: "table", type: "table" }],
|
|
490
|
+
returns: [{ type: "table|nil" }],
|
|
341
491
|
});
|
|
342
492
|
|
|
343
|
-
const dofileFunction = new LuaBuiltinFunction(
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
)) as Uint8Array;
|
|
351
|
-
const code = new TextDecoder().decode(file);
|
|
352
|
-
try {
|
|
353
|
-
const parsedExpr = parseBlock(code);
|
|
354
|
-
const env = new LuaEnv(global);
|
|
355
|
-
await evalStatement(parsedExpr, env, sf.withCtx(parsedExpr.ctx));
|
|
356
|
-
} catch (e: any) {
|
|
357
|
-
throw new LuaRuntimeError(
|
|
358
|
-
`Error evaluating "${filename}": ${e.message}`,
|
|
493
|
+
const dofileFunction = new LuaBuiltinFunction({
|
|
494
|
+
callback: async (sf, filename: string) => {
|
|
495
|
+
const global = sf.threadLocal.get("_GLOBAL") as LuaEnv;
|
|
496
|
+
const file = (await luaCall(
|
|
497
|
+
(global.get("space") as any).get("readFile"),
|
|
498
|
+
[filename],
|
|
499
|
+
sf.astCtx!,
|
|
359
500
|
sf,
|
|
360
|
-
);
|
|
361
|
-
|
|
501
|
+
)) as Uint8Array;
|
|
502
|
+
const code = new TextDecoder().decode(file);
|
|
503
|
+
try {
|
|
504
|
+
const parsedExpr = parseBlock(code);
|
|
505
|
+
const env = new LuaEnv(global);
|
|
506
|
+
await evalStatement(parsedExpr, env, sf.withCtx(parsedExpr.ctx));
|
|
507
|
+
} catch (e: any) {
|
|
508
|
+
throw new LuaRuntimeError(
|
|
509
|
+
`Error evaluating "${filename}": ${e.message}`,
|
|
510
|
+
sf,
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
},
|
|
514
|
+
description: "Reads and executes a Lua source file from the current space.",
|
|
515
|
+
parameters: [
|
|
516
|
+
{
|
|
517
|
+
name: "path",
|
|
518
|
+
type: "string",
|
|
519
|
+
description: "Space-relative Lua file path.",
|
|
520
|
+
},
|
|
521
|
+
],
|
|
362
522
|
});
|
|
363
523
|
|
|
364
524
|
/**
|
|
@@ -369,8 +529,8 @@ const dofileFunction = new LuaBuiltinFunction(async (sf, filename: string) => {
|
|
|
369
529
|
* argument). Otherwise, index must be the string "#", and select
|
|
370
530
|
* returns the total number of extra arguments it received.
|
|
371
531
|
*/
|
|
372
|
-
const selectFunction = new LuaBuiltinFunction(
|
|
373
|
-
(_sf, index: number | "#", ...args: LuaValue[]) => {
|
|
532
|
+
const selectFunction = new LuaBuiltinFunction({
|
|
533
|
+
callback: (_sf, index: number | "#", ...args: LuaValue[]) => {
|
|
374
534
|
if (index === "#") {
|
|
375
535
|
return args.length;
|
|
376
536
|
}
|
|
@@ -381,7 +541,19 @@ const selectFunction = new LuaBuiltinFunction(
|
|
|
381
541
|
return new LuaMultiRes(args.slice(args.length + index));
|
|
382
542
|
}
|
|
383
543
|
},
|
|
384
|
-
|
|
544
|
+
description:
|
|
545
|
+
"Returns the count of extra arguments or all arguments from a selected position onward.",
|
|
546
|
+
signatures: ['select("#", ...): integer', "select(index, ...): ..."],
|
|
547
|
+
parameters: [
|
|
548
|
+
{
|
|
549
|
+
name: "index",
|
|
550
|
+
type: "integer|string",
|
|
551
|
+
description: "One-based index, negative index from the end, or `#`.",
|
|
552
|
+
},
|
|
553
|
+
{ name: "..." },
|
|
554
|
+
],
|
|
555
|
+
returns: [{ description: "Argument count or selected argument values." }],
|
|
556
|
+
});
|
|
385
557
|
|
|
386
558
|
/**
|
|
387
559
|
* From the Lua docs:
|
|
@@ -403,8 +575,12 @@ const selectFunction = new LuaBuiltinFunction(
|
|
|
403
575
|
* during its traversal. You may however modify existing fields. In
|
|
404
576
|
* particular, you may set existing fields to nil.
|
|
405
577
|
*/
|
|
406
|
-
const nextFunction = new LuaBuiltinFunction(
|
|
407
|
-
|
|
578
|
+
const nextFunction = new LuaBuiltinFunction({
|
|
579
|
+
callback: (
|
|
580
|
+
sf,
|
|
581
|
+
table: LuaTable | Record<string, any>,
|
|
582
|
+
index: number | null = null,
|
|
583
|
+
) => {
|
|
408
584
|
if (!table) {
|
|
409
585
|
// When nil value
|
|
410
586
|
return null;
|
|
@@ -434,24 +610,84 @@ const nextFunction = new LuaBuiltinFunction(
|
|
|
434
610
|
}
|
|
435
611
|
return new LuaMultiRes([key, luaGet(table, key, sf.astCtx ?? null, sf)]);
|
|
436
612
|
},
|
|
437
|
-
|
|
613
|
+
description:
|
|
614
|
+
"Returns the next table key and value after a given key, or the first pair when the key is omitted.",
|
|
615
|
+
parameters: [
|
|
616
|
+
{ name: "table", type: "table" },
|
|
617
|
+
{ name: "index", description: "Previous key.", optional: true },
|
|
618
|
+
],
|
|
619
|
+
returns: [
|
|
620
|
+
{ description: "Next key or `nil`." },
|
|
621
|
+
{ description: "Value at the next key." },
|
|
622
|
+
],
|
|
623
|
+
});
|
|
438
624
|
|
|
439
625
|
// Non-standard, but useful
|
|
440
|
-
const someFunction = new LuaBuiltinFunction(
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
626
|
+
const someFunction = new LuaBuiltinFunction({
|
|
627
|
+
callback: async (_sf, value: any) => {
|
|
628
|
+
switch (await luaTypeOf(value)) {
|
|
629
|
+
case "number":
|
|
630
|
+
if (!Number.isFinite(value)) return null;
|
|
631
|
+
break;
|
|
632
|
+
case "string":
|
|
633
|
+
if (value.trim() === "") return null;
|
|
634
|
+
break;
|
|
635
|
+
case "table":
|
|
636
|
+
if (luaKeys(value).length === 0) return null;
|
|
637
|
+
}
|
|
638
|
+
return value;
|
|
639
|
+
},
|
|
640
|
+
description:
|
|
641
|
+
"Returns `nil` for empty Space Lua values and otherwise returns the value unchanged.",
|
|
642
|
+
parameters: [
|
|
643
|
+
{
|
|
644
|
+
name: "value",
|
|
645
|
+
description:
|
|
646
|
+
"Value to normalize; blank strings, empty tables, infinities, and NaN are empty.",
|
|
647
|
+
},
|
|
648
|
+
],
|
|
649
|
+
returns: [{ description: "Original value or `nil`." }],
|
|
650
|
+
examples: [
|
|
651
|
+
{
|
|
652
|
+
code: 'print(some(" ") or "empty")\nprint(some({}) or "empty")\nprint(some(0))',
|
|
653
|
+
},
|
|
654
|
+
],
|
|
452
655
|
});
|
|
453
656
|
|
|
454
|
-
const loadFunction = new LuaBuiltinFunction(
|
|
657
|
+
const loadFunction = new LuaBuiltinFunction({
|
|
658
|
+
callback: (sf, s) => luaLoad(s, sf),
|
|
659
|
+
description:
|
|
660
|
+
"Compiles Lua source into a callable chunk without executing it.",
|
|
661
|
+
parameters: [
|
|
662
|
+
{ name: "chunk", type: "string", description: "Lua source code." },
|
|
663
|
+
],
|
|
664
|
+
returns: [
|
|
665
|
+
{ type: "function|nil", description: "Compiled chunk or `nil`." },
|
|
666
|
+
{ type: "string", description: "Compilation error when unsuccessful." },
|
|
667
|
+
],
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
function annotateBuiltinApi(
|
|
671
|
+
value: unknown,
|
|
672
|
+
path: string,
|
|
673
|
+
page: string,
|
|
674
|
+
seen = new WeakSet<object>(),
|
|
675
|
+
): void {
|
|
676
|
+
if (!value || typeof value !== "object" || seen.has(value)) return;
|
|
677
|
+
seen.add(value);
|
|
678
|
+
if (isILuaFunction(value)) {
|
|
679
|
+
value.info ??= { kind: "builtin" };
|
|
680
|
+
value.info.name ??= path;
|
|
681
|
+
value.info.see ??= page;
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
if (value instanceof LuaTable) {
|
|
685
|
+
for (const key of value.keys()) {
|
|
686
|
+
if (typeof key !== "string") continue;
|
|
687
|
+
annotateBuiltinApi(value.rawGet(key), `${path}.${key}`, page, seen);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
455
691
|
|
|
456
692
|
export function luaBuildStandardEnv() {
|
|
457
693
|
const env = new LuaEnv();
|
|
@@ -499,5 +735,11 @@ export function luaBuildStandardEnv() {
|
|
|
499
735
|
env.set("crypto", cryptoApi);
|
|
500
736
|
env.set("net", netApi);
|
|
501
737
|
env.set("some", someFunction);
|
|
738
|
+
|
|
739
|
+
for (const name of env.keys()) {
|
|
740
|
+
const value = env.get(name);
|
|
741
|
+
const page = value instanceof LuaTable ? `API/${name}` : "API/global";
|
|
742
|
+
annotateBuiltinApi(value, name, page);
|
|
743
|
+
}
|
|
502
744
|
return env;
|
|
503
745
|
}
|