@silverbulletmd/silverbullet 2.4.2 → 2.6.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/README.md +19 -4
- package/client/asset_bundle/bundle.ts +3 -9
- package/client/data/datastore.ts +4 -5
- package/client/markdown_parser/constants.ts +5 -4
- package/client/plugos/hooks/code_widget.ts +3 -8
- package/client/plugos/hooks/command.ts +8 -8
- package/client/plugos/hooks/document_editor.ts +10 -15
- package/client/plugos/hooks/event.ts +33 -36
- package/client/plugos/hooks/mq.ts +17 -17
- package/client/plugos/hooks/plug_namespace.ts +3 -8
- package/client/plugos/hooks/slash_command.ts +13 -28
- package/client/plugos/hooks/syscall.ts +3 -3
- package/client/plugos/manifest_cache.ts +22 -15
- package/client/plugos/plug.ts +2 -6
- package/client/plugos/plug_compile.ts +79 -78
- package/client/plugos/protocol.ts +28 -28
- package/client/plugos/proxy_fetch.ts +7 -6
- package/client/plugos/sandboxes/web_worker_sandbox.ts +1 -1
- package/client/plugos/sandboxes/worker_sandbox.ts +18 -18
- package/client/plugos/syscalls/asset.ts +1 -3
- package/client/plugos/syscalls/code_widget.ts +1 -3
- package/client/plugos/syscalls/config.ts +1 -5
- package/client/plugos/syscalls/datastore.ts +1 -1
- package/client/plugos/syscalls/editor.ts +72 -69
- package/client/plugos/syscalls/event.ts +9 -12
- package/client/plugos/syscalls/fetch.ts +31 -23
- package/client/plugos/syscalls/index.ts +10 -1
- package/client/plugos/syscalls/jsonschema.ts +72 -32
- package/client/plugos/syscalls/language.ts +9 -5
- package/client/plugos/syscalls/markdown.ts +29 -7
- package/client/plugos/syscalls/mq.ts +4 -12
- package/client/plugos/syscalls/service_registry.ts +1 -4
- package/client/plugos/syscalls/shell.ts +2 -5
- package/client/plugos/syscalls/space.ts +1 -1
- package/client/plugos/syscalls/sync.ts +69 -60
- package/client/plugos/syscalls/system.ts +2 -3
- package/client/plugos/system.ts +6 -12
- package/client/plugos/worker_runtime.ts +12 -33
- package/client/space_lua/aggregates.ts +782 -0
- package/client/space_lua/ast.ts +42 -8
- package/client/space_lua/ast_narrow.ts +4 -2
- package/client/space_lua/eval.ts +886 -575
- package/client/space_lua/labels.ts +7 -12
- package/client/space_lua/liq_null.ts +6 -0
- package/client/space_lua/numeric.ts +5 -8
- package/client/space_lua/parse.ts +346 -120
- package/client/space_lua/query_collection.ts +926 -82
- package/client/space_lua/query_env.ts +26 -0
- package/client/space_lua/render_lua_markdown.ts +369 -0
- package/client/space_lua/rp.ts +5 -4
- package/client/space_lua/runtime.ts +288 -155
- package/client/space_lua/stdlib/format.ts +53 -39
- package/client/space_lua/stdlib/js.ts +3 -7
- package/client/space_lua/stdlib/load.ts +1 -3
- package/client/space_lua/stdlib/math.ts +84 -58
- package/client/space_lua/stdlib/net.ts +27 -17
- package/client/space_lua/stdlib/os.ts +81 -85
- package/client/space_lua/stdlib/pattern.ts +695 -0
- package/client/space_lua/stdlib/prng.ts +148 -0
- package/client/space_lua/stdlib/space_lua.ts +17 -23
- package/client/space_lua/stdlib/string.ts +102 -190
- package/client/space_lua/stdlib/string_pack.ts +490 -0
- package/client/space_lua/stdlib/table.ts +76 -16
- package/client/space_lua/stdlib.ts +53 -39
- package/client/space_lua/tonumber.ts +82 -42
- package/client/space_lua/util.ts +53 -15
- package/dist/plug-compile.js +55 -98
- package/package.json +27 -20
- package/plug-api/constants.ts +0 -32
- package/plug-api/lib/async.ts +20 -7
- package/plug-api/lib/crypto.ts +16 -17
- package/plug-api/lib/dates.ts +15 -7
- package/plug-api/lib/json.ts +11 -5
- package/plug-api/lib/limited_map.ts +1 -1
- package/plug-api/lib/native_fetch.ts +2 -0
- package/plug-api/lib/ref.ts +23 -23
- package/plug-api/lib/resolve.ts +7 -11
- package/plug-api/lib/tags.ts +13 -4
- package/plug-api/lib/transclusion.ts +10 -21
- package/plug-api/lib/tree.ts +165 -45
- package/plug-api/lib/yaml.ts +35 -25
- package/plug-api/syscalls/asset.ts +1 -1
- package/plug-api/syscalls/config.ts +1 -4
- package/plug-api/syscalls/editor.ts +15 -15
- package/plug-api/syscalls/jsonschema.ts +1 -3
- package/plug-api/syscalls/lua.ts +3 -9
- package/plug-api/syscalls/mq.ts +1 -4
- package/plug-api/syscalls/shell.ts +4 -1
- package/plug-api/syscalls/space.ts +3 -10
- package/plug-api/syscalls/system.ts +1 -4
- package/plug-api/syscalls/yaml.ts +2 -6
- package/plug-api/system_mock.ts +0 -1
- package/plug-api/types/client.ts +16 -1
- package/plug-api/types/event.ts +6 -4
- package/plug-api/types/manifest.ts +8 -9
- package/plugs/builtin_plugs.ts +2 -2
- package/client/plugos/sandboxes/deno_worker_sandbox.ts +0 -6
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
getMetatable,
|
|
2
3
|
type ILuaFunction,
|
|
3
4
|
isILuaFunction,
|
|
4
5
|
isLuaTable,
|
|
@@ -17,6 +18,7 @@ import {
|
|
|
17
18
|
luaToString,
|
|
18
19
|
luaTypeOf,
|
|
19
20
|
type LuaValue,
|
|
21
|
+
singleResult,
|
|
20
22
|
} from "./runtime.ts";
|
|
21
23
|
import { stringApi } from "./stdlib/string.ts";
|
|
22
24
|
import { tableApi } from "./stdlib/table.ts";
|
|
@@ -32,17 +34,16 @@ import { luaLoad } from "./stdlib/load.ts";
|
|
|
32
34
|
import { cryptoApi } from "./stdlib/crypto.ts";
|
|
33
35
|
import { netApi } from "./stdlib/net.ts";
|
|
34
36
|
import { isTaggedFloat, makeLuaFloat } from "./numeric.ts";
|
|
37
|
+
import { isPromise } from "./rp.ts";
|
|
38
|
+
import { isSqlNull } from "./liq_null.ts";
|
|
35
39
|
|
|
36
40
|
const printFunction = new LuaBuiltinFunction(async (_sf, ...args) => {
|
|
37
|
-
console.log(
|
|
38
|
-
"[Lua]",
|
|
39
|
-
...(await Promise.all(args.map((v) => luaToString(v)))),
|
|
40
|
-
);
|
|
41
|
+
console.log("[Lua]", ...(await Promise.all(args.map((v) => luaToString(v)))));
|
|
41
42
|
});
|
|
42
43
|
|
|
43
44
|
const assertFunction = new LuaBuiltinFunction(
|
|
44
45
|
async (sf, value: any, message?: string) => {
|
|
45
|
-
if (!await value) {
|
|
46
|
+
if (!(await value)) {
|
|
46
47
|
throw new LuaRuntimeError(`Assertion failed: ${message}`, sf);
|
|
47
48
|
}
|
|
48
49
|
},
|
|
@@ -121,23 +122,38 @@ export const eachFunction = new LuaBuiltinFunction(
|
|
|
121
122
|
},
|
|
122
123
|
);
|
|
123
124
|
|
|
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
125
|
const typeFunction = new LuaBuiltinFunction(
|
|
133
126
|
(_sf, value: LuaValue): string | Promise<string> => {
|
|
134
127
|
return luaTypeOf(value);
|
|
135
128
|
},
|
|
136
129
|
);
|
|
137
130
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
131
|
+
// tostring() checks `__tostring` metamethod first (with live SF), then
|
|
132
|
+
// falls back to the default `luaToString` representation.
|
|
133
|
+
const tostringFunction = new LuaBuiltinFunction(
|
|
134
|
+
(sf, value: any): string | Promise<string> => {
|
|
135
|
+
const mt = getMetatable(value, sf);
|
|
136
|
+
if (mt) {
|
|
137
|
+
const mm = mt.rawGet("__tostring");
|
|
138
|
+
if (mm !== undefined && mm !== null) {
|
|
139
|
+
const ctx = sf.astCtx ?? {};
|
|
140
|
+
const r = luaCall(mm, [value], ctx as any, sf);
|
|
141
|
+
const unwrap = (v: any): string => {
|
|
142
|
+
const s = singleResult(v);
|
|
143
|
+
if (typeof s !== "string") {
|
|
144
|
+
throw new LuaRuntimeError("'__tostring' must return a string", sf);
|
|
145
|
+
}
|
|
146
|
+
return s;
|
|
147
|
+
};
|
|
148
|
+
if (isPromise(r)) {
|
|
149
|
+
return (r as Promise<any>).then(unwrap);
|
|
150
|
+
}
|
|
151
|
+
return unwrap(r);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return luaToString(value);
|
|
155
|
+
},
|
|
156
|
+
);
|
|
141
157
|
|
|
142
158
|
const tonumberFunction = new LuaBuiltinFunction(
|
|
143
159
|
(sf, value: LuaValue, base?: number) => {
|
|
@@ -182,10 +198,7 @@ async function pcallBoundary(
|
|
|
182
198
|
sf: LuaStackFrame,
|
|
183
199
|
fn: ILuaFunction,
|
|
184
200
|
args: LuaValue[],
|
|
185
|
-
): Promise<
|
|
186
|
-
| { ok: true; values: LuaValue[] }
|
|
187
|
-
| { ok: false; message: string }
|
|
188
|
-
> {
|
|
201
|
+
): Promise<{ ok: true; values: LuaValue[] } | { ok: false; message: string }> {
|
|
189
202
|
const closeStack = luaEnsureCloseStack(sf);
|
|
190
203
|
const mark = closeStack.length;
|
|
191
204
|
|
|
@@ -253,11 +266,9 @@ const setmetatableFunction = new LuaBuiltinFunction(
|
|
|
253
266
|
},
|
|
254
267
|
);
|
|
255
268
|
|
|
256
|
-
const rawlenFunction = new LuaBuiltinFunction(
|
|
257
|
-
(_sf,
|
|
258
|
-
|
|
259
|
-
},
|
|
260
|
-
);
|
|
269
|
+
const rawlenFunction = new LuaBuiltinFunction((_sf, value: LuaValue) => {
|
|
270
|
+
return luaLen(value, _sf, true);
|
|
271
|
+
});
|
|
261
272
|
|
|
262
273
|
const rawsetFunction = new LuaBuiltinFunction(
|
|
263
274
|
(_sf, table: LuaTable, key: LuaValue, value: LuaValue) => {
|
|
@@ -269,7 +280,8 @@ const rawgetFunction = new LuaBuiltinFunction(
|
|
|
269
280
|
(_sf, table: any, key: LuaValue) => {
|
|
270
281
|
const isArray = Array.isArray(table);
|
|
271
282
|
|
|
272
|
-
const isPlainObj =
|
|
283
|
+
const isPlainObj =
|
|
284
|
+
typeof table === "object" &&
|
|
273
285
|
table !== null &&
|
|
274
286
|
(table as any).constructor === Object;
|
|
275
287
|
|
|
@@ -299,7 +311,7 @@ const rawgetFunction = new LuaBuiltinFunction(
|
|
|
299
311
|
|
|
300
312
|
if (isLuaTable(table)) {
|
|
301
313
|
const v = table.rawGet(key);
|
|
302
|
-
return v === undefined ? null : v;
|
|
314
|
+
return v === undefined || isSqlNull(v) ? null : v;
|
|
303
315
|
}
|
|
304
316
|
|
|
305
317
|
const k = isTaggedFloat(key) ? key.value : key;
|
|
@@ -318,13 +330,11 @@ const rawgetFunction = new LuaBuiltinFunction(
|
|
|
318
330
|
},
|
|
319
331
|
);
|
|
320
332
|
|
|
321
|
-
const rawequalFunction = new LuaBuiltinFunction(
|
|
322
|
-
(
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
},
|
|
327
|
-
);
|
|
333
|
+
const rawequalFunction = new LuaBuiltinFunction((_sf, a: any, b: any) => {
|
|
334
|
+
const av = isTaggedFloat(a) ? a.value : a;
|
|
335
|
+
const bv = isTaggedFloat(b) ? b.value : b;
|
|
336
|
+
return av === bv;
|
|
337
|
+
});
|
|
328
338
|
|
|
329
339
|
const getmetatableFunction = new LuaBuiltinFunction((_sf, table: LuaTable) => {
|
|
330
340
|
return (table as any).metatable;
|
|
@@ -332,12 +342,12 @@ const getmetatableFunction = new LuaBuiltinFunction((_sf, table: LuaTable) => {
|
|
|
332
342
|
|
|
333
343
|
const dofileFunction = new LuaBuiltinFunction(async (sf, filename: string) => {
|
|
334
344
|
const global = sf.threadLocal.get("_GLOBAL") as LuaEnv;
|
|
335
|
-
const file = await luaCall(
|
|
345
|
+
const file = (await luaCall(
|
|
336
346
|
(global.get("space") as any).get("readFile"),
|
|
337
347
|
[filename],
|
|
338
348
|
sf.astCtx!,
|
|
339
349
|
sf,
|
|
340
|
-
) as Uint8Array;
|
|
350
|
+
)) as Uint8Array;
|
|
341
351
|
const code = new TextDecoder().decode(file);
|
|
342
352
|
try {
|
|
343
353
|
const parsedExpr = parse(code);
|
|
@@ -413,7 +423,8 @@ const nextFunction = new LuaBuiltinFunction(
|
|
|
413
423
|
}
|
|
414
424
|
// Find index in the key list
|
|
415
425
|
const idx = keys.indexOf(index);
|
|
416
|
-
if (idx === -1) {
|
|
426
|
+
if (idx === -1) {
|
|
427
|
+
// Not found
|
|
417
428
|
throw new LuaRuntimeError("invalid key to 'next': key not found", sf);
|
|
418
429
|
}
|
|
419
430
|
const key = keys[idx + 1];
|
|
@@ -429,7 +440,7 @@ const nextFunction = new LuaBuiltinFunction(
|
|
|
429
440
|
const someFunction = new LuaBuiltinFunction(async (_sf, value: any) => {
|
|
430
441
|
switch (await luaTypeOf(value)) {
|
|
431
442
|
case "number":
|
|
432
|
-
if (!isFinite(value)) return null;
|
|
443
|
+
if (!Number.isFinite(value)) return null;
|
|
433
444
|
break;
|
|
434
445
|
case "string":
|
|
435
446
|
if (value.trim() === "") return null;
|
|
@@ -446,13 +457,16 @@ export function luaBuildStandardEnv() {
|
|
|
446
457
|
const env = new LuaEnv();
|
|
447
458
|
// _G global
|
|
448
459
|
env.set("_G", env);
|
|
460
|
+
// Lua version string - for now it signals Lua 5.4 compatibility with
|
|
461
|
+
// selective 5.5 features; kept non-standard so callers can distinguish
|
|
462
|
+
// Space Lua from a plain Lua runtime.
|
|
463
|
+
env.set("_VERSION", "Lua 5.4+");
|
|
449
464
|
// Top-level builtins
|
|
450
465
|
env.set("print", printFunction);
|
|
451
466
|
env.set("assert", assertFunction);
|
|
452
467
|
env.set("type", typeFunction);
|
|
453
468
|
env.set("tostring", tostringFunction);
|
|
454
469
|
env.set("tonumber", tonumberFunction);
|
|
455
|
-
env.set("unpack", unpackFunction);
|
|
456
470
|
env.set("select", selectFunction);
|
|
457
471
|
env.set("next", nextFunction);
|
|
458
472
|
// Iterators
|
|
@@ -18,7 +18,8 @@ function skipSpace(s: string, i: number): number {
|
|
|
18
18
|
const n = s.length;
|
|
19
19
|
while (i < n) {
|
|
20
20
|
const c = s.charCodeAt(i);
|
|
21
|
-
if (c === 32 || (c >= 9 && c <= 13)) {
|
|
21
|
+
if (c === 32 || (c >= 9 && c <= 13)) {
|
|
22
|
+
// SP, HT, LF, VT, FF, CR
|
|
22
23
|
i++;
|
|
23
24
|
} else {
|
|
24
25
|
break;
|
|
@@ -28,21 +29,24 @@ function skipSpace(s: string, i: number): number {
|
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
function charToDigitBase(c: number, base: number): number {
|
|
31
|
-
if (c >= 48 && c <= 57) {
|
|
32
|
+
if (c >= 48 && c <= 57) {
|
|
33
|
+
// '0'..'9'
|
|
32
34
|
const v = c - 48;
|
|
33
35
|
if (v < base) {
|
|
34
36
|
return v;
|
|
35
37
|
}
|
|
36
38
|
return -1;
|
|
37
39
|
}
|
|
38
|
-
if (c >= 65 && c <= 90) {
|
|
40
|
+
if (c >= 65 && c <= 90) {
|
|
41
|
+
// 'A'..'Z'
|
|
39
42
|
const v = 10 + (c - 65);
|
|
40
43
|
if (v < base) {
|
|
41
44
|
return v;
|
|
42
45
|
}
|
|
43
46
|
return -1;
|
|
44
47
|
}
|
|
45
|
-
if (c >= 97 && c <= 122) {
|
|
48
|
+
if (c >= 97 && c <= 122) {
|
|
49
|
+
// 'a'..'z'
|
|
46
50
|
const v = 10 + (c - 97);
|
|
47
51
|
if (v < base) {
|
|
48
52
|
return v;
|
|
@@ -64,10 +68,12 @@ function parseIntWithBase(
|
|
|
64
68
|
}
|
|
65
69
|
|
|
66
70
|
let sign = 1;
|
|
67
|
-
if (s.charCodeAt(i) === 45) {
|
|
71
|
+
if (s.charCodeAt(i) === 45) {
|
|
72
|
+
// '-'
|
|
68
73
|
sign = -1;
|
|
69
74
|
i++;
|
|
70
|
-
} else if (s.charCodeAt(i) === 43) {
|
|
75
|
+
} else if (s.charCodeAt(i) === 43) {
|
|
76
|
+
// '+'
|
|
71
77
|
i++;
|
|
72
78
|
}
|
|
73
79
|
|
|
@@ -80,7 +86,8 @@ function parseIntWithBase(
|
|
|
80
86
|
|
|
81
87
|
while (i < n) {
|
|
82
88
|
const c = s.charCodeAt(i);
|
|
83
|
-
const isAlnum =
|
|
89
|
+
const isAlnum =
|
|
90
|
+
(c >= 48 && c <= 57) || // '0'..'9'
|
|
84
91
|
(c >= 65 && c <= 90) || // 'A'..'Z'
|
|
85
92
|
(c >= 97 && c <= 122); // 'a'..'z'
|
|
86
93
|
|
|
@@ -119,10 +126,12 @@ function parseInt(s: string): { ok: boolean; value: number } {
|
|
|
119
126
|
}
|
|
120
127
|
|
|
121
128
|
let neg = false;
|
|
122
|
-
if (s.charCodeAt(i) === 45) {
|
|
129
|
+
if (s.charCodeAt(i) === 45) {
|
|
130
|
+
// '-'
|
|
123
131
|
neg = true;
|
|
124
132
|
i++;
|
|
125
|
-
} else if (s.charCodeAt(i) === 43) {
|
|
133
|
+
} else if (s.charCodeAt(i) === 43) {
|
|
134
|
+
// '+'
|
|
126
135
|
i++;
|
|
127
136
|
}
|
|
128
137
|
|
|
@@ -134,18 +143,23 @@ function parseInt(s: string): { ok: boolean; value: number } {
|
|
|
134
143
|
let any = false;
|
|
135
144
|
|
|
136
145
|
// hex?
|
|
137
|
-
if (s.charCodeAt(i) === 48 && i + 1 < n) {
|
|
146
|
+
if (s.charCodeAt(i) === 48 && i + 1 < n) {
|
|
147
|
+
// '0'
|
|
138
148
|
const x = s.charCodeAt(i + 1);
|
|
139
|
-
if (x === 120 || x === 88) {
|
|
149
|
+
if (x === 120 || x === 88) {
|
|
150
|
+
// 'x' or 'X'
|
|
140
151
|
i += 2;
|
|
141
152
|
while (i < n) {
|
|
142
153
|
const c = s.charCodeAt(i);
|
|
143
154
|
let d = -1;
|
|
144
|
-
if (c >= 48 && c <= 57) {
|
|
155
|
+
if (c >= 48 && c <= 57) {
|
|
156
|
+
// '0'..'9'
|
|
145
157
|
d = c - 48; // '0'
|
|
146
|
-
} else if (c >= 65 && c <= 70) {
|
|
158
|
+
} else if (c >= 65 && c <= 70) {
|
|
159
|
+
// 'A'..'F'
|
|
147
160
|
d = 10 + (c - 65);
|
|
148
|
-
} else if (c >= 97 && c <= 102) {
|
|
161
|
+
} else if (c >= 97 && c <= 102) {
|
|
162
|
+
// 'a'..'f'
|
|
149
163
|
d = 10 + (c - 97);
|
|
150
164
|
} else {
|
|
151
165
|
d = -1;
|
|
@@ -168,7 +182,8 @@ function parseInt(s: string): { ok: boolean; value: number } {
|
|
|
168
182
|
// decimal integer
|
|
169
183
|
while (i < n) {
|
|
170
184
|
const c = s.charCodeAt(i);
|
|
171
|
-
if (c < 48 || c > 57) {
|
|
185
|
+
if (c < 48 || c > 57) {
|
|
186
|
+
// not '0'..'9'
|
|
172
187
|
break;
|
|
173
188
|
}
|
|
174
189
|
acc = acc * 10 + (c - 48);
|
|
@@ -193,10 +208,12 @@ function parseDecFloat(s: string): { ok: boolean; value: number } {
|
|
|
193
208
|
|
|
194
209
|
let sign = 1;
|
|
195
210
|
const c0 = s.charCodeAt(i);
|
|
196
|
-
if (c0 === 45) {
|
|
211
|
+
if (c0 === 45) {
|
|
212
|
+
// '-'
|
|
197
213
|
sign = -1;
|
|
198
214
|
i++;
|
|
199
|
-
} else if (c0 === 43) {
|
|
215
|
+
} else if (c0 === 43) {
|
|
216
|
+
// '+'
|
|
200
217
|
i++;
|
|
201
218
|
}
|
|
202
219
|
|
|
@@ -207,7 +224,8 @@ function parseDecFloat(s: string): { ok: boolean; value: number } {
|
|
|
207
224
|
// integer part
|
|
208
225
|
while (i < n) {
|
|
209
226
|
const c = s.charCodeAt(i);
|
|
210
|
-
if (c < 48 || c > 57) {
|
|
227
|
+
if (c < 48 || c > 57) {
|
|
228
|
+
// not '0'..'9'
|
|
211
229
|
break;
|
|
212
230
|
}
|
|
213
231
|
val = val * 10 + (c - 48);
|
|
@@ -217,12 +235,14 @@ function parseDecFloat(s: string): { ok: boolean; value: number } {
|
|
|
217
235
|
|
|
218
236
|
// fractional part
|
|
219
237
|
if (i < n) {
|
|
220
|
-
if (s.charCodeAt(i) === 46) {
|
|
238
|
+
if (s.charCodeAt(i) === 46) {
|
|
239
|
+
// '.'
|
|
221
240
|
i++;
|
|
222
241
|
let scale = 1;
|
|
223
242
|
while (i < n) {
|
|
224
243
|
const c = s.charCodeAt(i);
|
|
225
|
-
if (c < 48 || c > 57) {
|
|
244
|
+
if (c < 48 || c > 57) {
|
|
245
|
+
// not '0'..'9'
|
|
226
246
|
break;
|
|
227
247
|
}
|
|
228
248
|
scale *= 0.1;
|
|
@@ -238,7 +258,8 @@ function parseDecFloat(s: string): { ok: boolean; value: number } {
|
|
|
238
258
|
let hasExp = false;
|
|
239
259
|
if (i < n) {
|
|
240
260
|
const ec = s.charCodeAt(i);
|
|
241
|
-
if (ec === 101 || ec === 69) {
|
|
261
|
+
if (ec === 101 || ec === 69) {
|
|
262
|
+
// 'e' or 'E'
|
|
242
263
|
hasExp = true;
|
|
243
264
|
i++;
|
|
244
265
|
if (i >= n) {
|
|
@@ -247,10 +268,12 @@ function parseDecFloat(s: string): { ok: boolean; value: number } {
|
|
|
247
268
|
let expSign = 1;
|
|
248
269
|
if (i < n) {
|
|
249
270
|
const es = s.charCodeAt(i);
|
|
250
|
-
if (es === 45) {
|
|
271
|
+
if (es === 45) {
|
|
272
|
+
// '-'
|
|
251
273
|
expSign = -1;
|
|
252
274
|
i++;
|
|
253
|
-
} else if (es === 43) {
|
|
275
|
+
} else if (es === 43) {
|
|
276
|
+
// '+'
|
|
254
277
|
i++;
|
|
255
278
|
}
|
|
256
279
|
}
|
|
@@ -260,7 +283,8 @@ function parseDecFloat(s: string): { ok: boolean; value: number } {
|
|
|
260
283
|
let anyExp = false;
|
|
261
284
|
while (i < n) {
|
|
262
285
|
const c = s.charCodeAt(i);
|
|
263
|
-
if (c < 48 || c > 57) {
|
|
286
|
+
if (c < 48 || c > 57) {
|
|
287
|
+
// not '0'..'9'
|
|
264
288
|
break;
|
|
265
289
|
}
|
|
266
290
|
exp = exp * 10 + (c - 48);
|
|
@@ -283,7 +307,7 @@ function parseDecFloat(s: string): { ok: boolean; value: number } {
|
|
|
283
307
|
return { ok: false, value: 0 };
|
|
284
308
|
}
|
|
285
309
|
|
|
286
|
-
const result = sign * (hasExp ? val *
|
|
310
|
+
const result = sign * (hasExp ? val * 10 ** exp : val);
|
|
287
311
|
return { ok: true, value: result };
|
|
288
312
|
}
|
|
289
313
|
|
|
@@ -297,18 +321,22 @@ function parseHexFloat(s: string): { ok: boolean; value: number } {
|
|
|
297
321
|
|
|
298
322
|
let sign = 1;
|
|
299
323
|
const c0 = s.charCodeAt(i);
|
|
300
|
-
if (c0 === 45) {
|
|
324
|
+
if (c0 === 45) {
|
|
325
|
+
// '-'
|
|
301
326
|
sign = -1;
|
|
302
327
|
i++;
|
|
303
|
-
} else if (c0 === 43) {
|
|
328
|
+
} else if (c0 === 43) {
|
|
329
|
+
// '+'
|
|
304
330
|
i++;
|
|
305
331
|
}
|
|
306
332
|
|
|
307
|
-
if (!(i + 1 < n && s.charCodeAt(i) === 48)) {
|
|
333
|
+
if (!(i + 1 < n && s.charCodeAt(i) === 48)) {
|
|
334
|
+
// '0'
|
|
308
335
|
return { ok: false, value: 0 };
|
|
309
336
|
}
|
|
310
337
|
const x = s.charCodeAt(i + 1);
|
|
311
|
-
if (!(x === 120 || x === 88)) {
|
|
338
|
+
if (!(x === 120 || x === 88)) {
|
|
339
|
+
// 'x' or 'X'
|
|
312
340
|
return { ok: false, value: 0 };
|
|
313
341
|
}
|
|
314
342
|
i += 2;
|
|
@@ -323,11 +351,14 @@ function parseHexFloat(s: string): { ok: boolean; value: number } {
|
|
|
323
351
|
while (i < n) {
|
|
324
352
|
const c = s.charCodeAt(i);
|
|
325
353
|
let d = -1;
|
|
326
|
-
if (c >= 48 && c <= 57) {
|
|
354
|
+
if (c >= 48 && c <= 57) {
|
|
355
|
+
// '0'..'9'
|
|
327
356
|
d = c - 48;
|
|
328
|
-
} else if (c >= 65 && c <= 70) {
|
|
357
|
+
} else if (c >= 65 && c <= 70) {
|
|
358
|
+
// 'A'..'F'
|
|
329
359
|
d = 10 + (c - 65);
|
|
330
|
-
} else if (c >= 97 && c <= 102) {
|
|
360
|
+
} else if (c >= 97 && c <= 102) {
|
|
361
|
+
// 'a'..'f'
|
|
331
362
|
d = 10 + (c - 97);
|
|
332
363
|
} else {
|
|
333
364
|
d = -1;
|
|
@@ -342,17 +373,21 @@ function parseHexFloat(s: string): { ok: boolean; value: number } {
|
|
|
342
373
|
|
|
343
374
|
// optional fractional part
|
|
344
375
|
if (i < n) {
|
|
345
|
-
if (s.charCodeAt(i) === 46) {
|
|
376
|
+
if (s.charCodeAt(i) === 46) {
|
|
377
|
+
// '.'
|
|
346
378
|
sawDot = true;
|
|
347
379
|
i++;
|
|
348
380
|
while (i < n) {
|
|
349
381
|
const c = s.charCodeAt(i);
|
|
350
382
|
let d = -1;
|
|
351
|
-
if (c >= 48 && c <= 57) {
|
|
383
|
+
if (c >= 48 && c <= 57) {
|
|
384
|
+
// '0'..'9'
|
|
352
385
|
d = c - 48;
|
|
353
|
-
} else if (c >= 65 && c <= 70) {
|
|
386
|
+
} else if (c >= 65 && c <= 70) {
|
|
387
|
+
// 'A'..'F'
|
|
354
388
|
d = 10 + (c - 65);
|
|
355
|
-
} else if (c >= 97 && c <= 102) {
|
|
389
|
+
} else if (c >= 97 && c <= 102) {
|
|
390
|
+
// 'a'..'f'
|
|
356
391
|
d = 10 + (c - 97);
|
|
357
392
|
} else {
|
|
358
393
|
d = -1;
|
|
@@ -376,14 +411,17 @@ function parseHexFloat(s: string): { ok: boolean; value: number } {
|
|
|
376
411
|
|
|
377
412
|
if (i < n) {
|
|
378
413
|
const ec = s.charCodeAt(i);
|
|
379
|
-
if (ec === 112 || ec === 80) {
|
|
414
|
+
if (ec === 112 || ec === 80) {
|
|
415
|
+
// 'p' or 'P'
|
|
380
416
|
i++;
|
|
381
417
|
if (i < n) {
|
|
382
418
|
const sc = s.charCodeAt(i);
|
|
383
|
-
if (sc === 45) {
|
|
419
|
+
if (sc === 45) {
|
|
420
|
+
// '-'
|
|
384
421
|
expSign = -1;
|
|
385
422
|
i++;
|
|
386
|
-
} else if (sc === 43) {
|
|
423
|
+
} else if (sc === 43) {
|
|
424
|
+
// '+'
|
|
387
425
|
i++;
|
|
388
426
|
}
|
|
389
427
|
}
|
|
@@ -394,7 +432,8 @@ function parseHexFloat(s: string): { ok: boolean; value: number } {
|
|
|
394
432
|
let anyExp = false;
|
|
395
433
|
while (i < n) {
|
|
396
434
|
const c = s.charCodeAt(i);
|
|
397
|
-
if (c < 48 || c > 57) {
|
|
435
|
+
if (c < 48 || c > 57) {
|
|
436
|
+
// not '0'..'9'
|
|
398
437
|
break;
|
|
399
438
|
}
|
|
400
439
|
exp = exp * 10 + (c - 48);
|
|
@@ -433,8 +472,8 @@ function parseHexFloat(s: string): { ok: boolean; value: number } {
|
|
|
433
472
|
return { ok: false, value: 0 };
|
|
434
473
|
}
|
|
435
474
|
|
|
436
|
-
const frac = fracVal === 0 ? 0 :
|
|
437
|
-
const result = sign * (intVal + frac) *
|
|
475
|
+
const frac = fracVal === 0 ? 0 : fracVal / fracScale;
|
|
476
|
+
const result = sign * (intVal + frac) * 2 ** (expSign * exp);
|
|
438
477
|
return { ok: true, value: result };
|
|
439
478
|
}
|
|
440
479
|
|
|
@@ -460,6 +499,7 @@ export function luaToNumberDetailed(
|
|
|
460
499
|
}
|
|
461
500
|
|
|
462
501
|
{
|
|
502
|
+
// biome-ignore lint/correctness/useParseIntRadix: local parseInt function, not global
|
|
463
503
|
const parsed = parseInt(s);
|
|
464
504
|
if (parsed.ok) {
|
|
465
505
|
const v = parsed.value;
|
package/client/space_lua/util.ts
CHANGED
|
@@ -6,7 +6,7 @@ export function evalPromiseValues(vals: any[]): Promise<any[]> | any[] {
|
|
|
6
6
|
for (let i = 0; i < vals.length; i++) {
|
|
7
7
|
if (isPromise(vals[i])) {
|
|
8
8
|
promises.push(
|
|
9
|
-
(vals[i] as Promise<any>).then((v: any) => promiseResults[i] = v),
|
|
9
|
+
(vals[i] as Promise<any>).then((v: any) => (promiseResults[i] = v)),
|
|
10
10
|
);
|
|
11
11
|
} else {
|
|
12
12
|
promiseResults[i] = vals[i];
|
|
@@ -14,9 +14,8 @@ export function evalPromiseValues(vals: any[]): Promise<any[]> | any[] {
|
|
|
14
14
|
}
|
|
15
15
|
if (promises.length === 0) {
|
|
16
16
|
return promiseResults;
|
|
17
|
-
} else {
|
|
18
|
-
return Promise.all(promises).then(() => promiseResults);
|
|
19
17
|
}
|
|
18
|
+
return Promise.all(promises).then(() => promiseResults);
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
/**
|
|
@@ -33,21 +32,22 @@ async function getPivot(
|
|
|
33
32
|
z: any,
|
|
34
33
|
compare: (a: any, b: any) => Promise<number>,
|
|
35
34
|
) {
|
|
36
|
-
if (await compare(x, y) < 0) {
|
|
37
|
-
if (await compare(y, z) < 0) {
|
|
35
|
+
if ((await compare(x, y)) < 0) {
|
|
36
|
+
if ((await compare(y, z)) < 0) {
|
|
38
37
|
return y;
|
|
39
|
-
}
|
|
38
|
+
}
|
|
39
|
+
if ((await compare(z, x)) < 0) {
|
|
40
40
|
return x;
|
|
41
|
-
} else {
|
|
42
|
-
return z;
|
|
43
41
|
}
|
|
44
|
-
|
|
42
|
+
return z;
|
|
43
|
+
}
|
|
44
|
+
if ((await compare(y, z)) > 0) {
|
|
45
45
|
return y;
|
|
46
|
-
}
|
|
46
|
+
}
|
|
47
|
+
if ((await compare(z, x)) > 0) {
|
|
47
48
|
return x;
|
|
48
|
-
} else {
|
|
49
|
-
return z;
|
|
50
49
|
}
|
|
50
|
+
return z;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
/**
|
|
@@ -65,7 +65,9 @@ export async function asyncQuickSort(
|
|
|
65
65
|
right = arr.length - 1,
|
|
66
66
|
) {
|
|
67
67
|
if (left < right) {
|
|
68
|
-
let i = left,
|
|
68
|
+
let i = left,
|
|
69
|
+
j = right,
|
|
70
|
+
tmp;
|
|
69
71
|
const pivot = await getPivot(
|
|
70
72
|
arr[i],
|
|
71
73
|
arr[i + Math.floor((j - i) / 2)],
|
|
@@ -73,10 +75,10 @@ export async function asyncQuickSort(
|
|
|
73
75
|
compare,
|
|
74
76
|
);
|
|
75
77
|
while (true) {
|
|
76
|
-
while (await compare(arr[i], pivot) < 0) {
|
|
78
|
+
while ((await compare(arr[i], pivot)) < 0) {
|
|
77
79
|
i++;
|
|
78
80
|
}
|
|
79
|
-
while (await compare(pivot, arr[j]) < 0) {
|
|
81
|
+
while ((await compare(pivot, arr[j])) < 0) {
|
|
80
82
|
j--;
|
|
81
83
|
}
|
|
82
84
|
if (i >= j) {
|
|
@@ -94,3 +96,39 @@ export async function asyncQuickSort(
|
|
|
94
96
|
}
|
|
95
97
|
return arr;
|
|
96
98
|
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* iterative async merge sort
|
|
102
|
+
* @param arr tagged array of { val, idx } elements
|
|
103
|
+
* @param compare async comparator returning <0, 0, or >0
|
|
104
|
+
*/
|
|
105
|
+
export async function asyncMergeSort(
|
|
106
|
+
arr: { val: any; idx: number }[],
|
|
107
|
+
compare: (
|
|
108
|
+
a: { val: any; idx: number },
|
|
109
|
+
b: { val: any; idx: number },
|
|
110
|
+
) => Promise<number>,
|
|
111
|
+
): Promise<void> {
|
|
112
|
+
const n = arr.length;
|
|
113
|
+
if (n <= 1) return;
|
|
114
|
+
const work = new Array(n);
|
|
115
|
+
|
|
116
|
+
for (let size = 1; size < n; size *= 2) {
|
|
117
|
+
for (let left = 0; left < n; left += 2 * size) {
|
|
118
|
+
const mid = Math.min(left + size, n);
|
|
119
|
+
const right = Math.min(left + 2 * size, n);
|
|
120
|
+
|
|
121
|
+
let i = left,
|
|
122
|
+
j = mid,
|
|
123
|
+
k = left;
|
|
124
|
+
while (i < mid && j < right) {
|
|
125
|
+
const cmp = await compare(arr[i], arr[j]);
|
|
126
|
+
if (cmp <= 0) work[k++] = arr[i++];
|
|
127
|
+
else work[k++] = arr[j++];
|
|
128
|
+
}
|
|
129
|
+
while (i < mid) work[k++] = arr[i++];
|
|
130
|
+
while (j < right) work[k++] = arr[j++];
|
|
131
|
+
for (let m = left; m < right; m++) arr[m] = work[m];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|