@silverbulletmd/silverbullet 2.4.2 → 2.5.3
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 +20 -4
- package/client/markdown_parser/constants.ts +2 -2
- package/client/plugos/hooks/code_widget.ts +0 -3
- package/client/plugos/hooks/document_editor.ts +0 -3
- package/client/plugos/hooks/event.ts +1 -1
- package/client/plugos/hooks/mq.ts +1 -1
- package/client/plugos/hooks/plug_namespace.ts +0 -3
- package/client/plugos/hooks/slash_command.ts +2 -2
- package/client/plugos/plug.ts +0 -1
- package/client/plugos/plug_compile.ts +28 -29
- package/client/plugos/proxy_fetch.ts +1 -1
- package/client/plugos/sandboxes/web_worker_sandbox.ts +1 -1
- package/client/plugos/sandboxes/worker_sandbox.ts +2 -3
- package/client/plugos/syscalls/editor.ts +12 -12
- package/client/plugos/syscalls/fetch.ts +1 -1
- package/client/plugos/syscalls/jsonschema.ts +1 -1
- package/client/plugos/syscalls/mq.ts +1 -1
- package/client/plugos/syscalls/space.ts +1 -1
- package/client/plugos/system.ts +2 -2
- package/client/plugos/worker_runtime.ts +8 -30
- package/client/space_lua/aggregates.ts +209 -0
- package/client/space_lua/ast.ts +24 -2
- package/client/space_lua/eval.ts +58 -53
- package/client/space_lua/labels.ts +1 -1
- package/client/space_lua/parse.ts +117 -12
- package/client/space_lua/query_collection.ts +850 -70
- package/client/space_lua/query_env.ts +26 -0
- package/client/space_lua/runtime.ts +47 -17
- package/client/space_lua/stdlib/format.ts +19 -19
- package/client/space_lua/stdlib/math.ts +73 -48
- package/client/space_lua/stdlib/net.ts +2 -2
- package/client/space_lua/stdlib/os.ts +5 -0
- package/client/space_lua/stdlib/pattern.ts +702 -0
- package/client/space_lua/stdlib/prng.ts +145 -0
- package/client/space_lua/stdlib/space_lua.ts +3 -8
- package/client/space_lua/stdlib/string.ts +103 -181
- package/client/space_lua/stdlib/string_pack.ts +486 -0
- package/client/space_lua/stdlib/table.ts +73 -9
- package/client/space_lua/stdlib.ts +38 -14
- package/client/space_lua/tonumber.ts +3 -2
- package/client/space_lua/util.ts +43 -9
- package/dist/plug-compile.js +23 -69
- package/dist/worker_runtime_bundle.js +233 -0
- package/package.json +10 -5
- package/plug-api/constants.ts +0 -32
- package/plug-api/lib/async.ts +2 -2
- package/plug-api/lib/crypto.ts +11 -11
- package/plug-api/lib/json.ts +1 -1
- package/plug-api/lib/limited_map.ts +1 -1
- package/plug-api/lib/native_fetch.ts +2 -0
- package/plug-api/lib/ref.ts +5 -5
- package/plug-api/lib/transclusion.ts +5 -5
- package/plug-api/lib/tree.ts +50 -2
- package/plug-api/lib/yaml.ts +10 -10
- package/plug-api/syscalls/editor.ts +1 -1
- package/plug-api/system_mock.ts +0 -1
- package/client/plugos/sandboxes/deno_worker_sandbox.ts +0 -6
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { LuaEnv, luaGet, luaKeys, type LuaStackFrame } from "./runtime.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Build an environment for evaluating per-item expressions in queries.
|
|
5
|
+
* Extracted to its own module to avoid circular imports between
|
|
6
|
+
* query_collection.ts and aggregates.ts.
|
|
7
|
+
*/
|
|
8
|
+
export function buildItemEnv(
|
|
9
|
+
objectVariable: string | undefined,
|
|
10
|
+
item: any,
|
|
11
|
+
env: LuaEnv,
|
|
12
|
+
sf: LuaStackFrame,
|
|
13
|
+
): LuaEnv {
|
|
14
|
+
const itemEnv = new LuaEnv(env);
|
|
15
|
+
if (!objectVariable) {
|
|
16
|
+
// Inject all item keys as variables
|
|
17
|
+
for (const key of luaKeys(item)) {
|
|
18
|
+
itemEnv.setLocal(key, luaGet(item, key, sf.astCtx ?? null, sf));
|
|
19
|
+
}
|
|
20
|
+
// As well as _
|
|
21
|
+
itemEnv.setLocal("_", item);
|
|
22
|
+
} else {
|
|
23
|
+
itemEnv.setLocal(objectVariable, item);
|
|
24
|
+
}
|
|
25
|
+
return itemEnv;
|
|
26
|
+
}
|
|
@@ -128,7 +128,7 @@ export function luaIsCallable(
|
|
|
128
128
|
}
|
|
129
129
|
if (v instanceof LuaTable) {
|
|
130
130
|
const mt = getMetatable(v, sf);
|
|
131
|
-
if (mt
|
|
131
|
+
if (mt?.has("__call")) {
|
|
132
132
|
const mm = mt.get("__call", sf);
|
|
133
133
|
return !!mm && (typeof mm === "function" || isILuaFunction(mm));
|
|
134
134
|
}
|
|
@@ -614,7 +614,7 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
614
614
|
|
|
615
615
|
if (init && !Array.isArray(init)) {
|
|
616
616
|
for (const k in init) {
|
|
617
|
-
if (Object.
|
|
617
|
+
if (Object.hasOwn(init, k)) {
|
|
618
618
|
this.stringKeys[k] = (init as any)[k];
|
|
619
619
|
}
|
|
620
620
|
}
|
|
@@ -689,7 +689,7 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
689
689
|
keys(): any[] {
|
|
690
690
|
const keys: any[] = [];
|
|
691
691
|
for (const k in this.stringKeys) {
|
|
692
|
-
if (Object.
|
|
692
|
+
if (Object.hasOwn(this.stringKeys, k)) {
|
|
693
693
|
keys.push(k);
|
|
694
694
|
}
|
|
695
695
|
}
|
|
@@ -706,7 +706,7 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
706
706
|
|
|
707
707
|
empty(): boolean {
|
|
708
708
|
for (const k in this.stringKeys) {
|
|
709
|
-
if (Object.
|
|
709
|
+
if (Object.hasOwn(this.stringKeys, k)) {
|
|
710
710
|
return false;
|
|
711
711
|
}
|
|
712
712
|
}
|
|
@@ -927,6 +927,20 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
927
927
|
const errSf = sf || LuaStackFrame.lostFrame;
|
|
928
928
|
const ctx = sf?.astCtx ?? EMPTY_CTX;
|
|
929
929
|
|
|
930
|
+
if (key === null || key === undefined) {
|
|
931
|
+
throw new LuaRuntimeError(
|
|
932
|
+
"table index is nil",
|
|
933
|
+
errSf,
|
|
934
|
+
);
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
if (typeof key === "number" && Number.isNaN(key)) {
|
|
938
|
+
throw new LuaRuntimeError(
|
|
939
|
+
"table index is NaN",
|
|
940
|
+
errSf,
|
|
941
|
+
);
|
|
942
|
+
}
|
|
943
|
+
|
|
930
944
|
if (this.has(key)) {
|
|
931
945
|
return this.rawSet(key, value, numType);
|
|
932
946
|
}
|
|
@@ -1121,9 +1135,9 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
1121
1135
|
if (typeof key === "string") {
|
|
1122
1136
|
result += key;
|
|
1123
1137
|
} else {
|
|
1124
|
-
result +=
|
|
1138
|
+
result += `[${key}]`;
|
|
1125
1139
|
}
|
|
1126
|
-
result +=
|
|
1140
|
+
result += ` = ${await luaToString(this.get(key))}`;
|
|
1127
1141
|
}
|
|
1128
1142
|
result += "}";
|
|
1129
1143
|
return result;
|
|
@@ -1243,21 +1257,24 @@ export function luaGet(
|
|
|
1243
1257
|
errSf,
|
|
1244
1258
|
);
|
|
1245
1259
|
}
|
|
1260
|
+
|
|
1261
|
+
// In Lua reading with a nil key returns nil silently
|
|
1246
1262
|
if (key === null || key === undefined) {
|
|
1247
|
-
|
|
1248
|
-
`attempt to index with a nil key`,
|
|
1249
|
-
errSf,
|
|
1250
|
-
);
|
|
1263
|
+
return null;
|
|
1251
1264
|
}
|
|
1252
1265
|
|
|
1253
1266
|
if (obj instanceof LuaTable || obj instanceof LuaEnv) {
|
|
1254
1267
|
return obj.get(key, sf);
|
|
1255
1268
|
}
|
|
1269
|
+
// Native JS array access: normalize undefined → null (Lua nil).
|
|
1270
|
+
// Without this, accessing an out-of-bounds index on a JS array
|
|
1271
|
+
// leaks JS undefined into the Lua runtime, breaking nil checks
|
|
1272
|
+
// such as `tags[1] ~= nil` on an empty array.
|
|
1256
1273
|
if (typeof key === "number") {
|
|
1257
|
-
return (obj as any[])[key - 1];
|
|
1274
|
+
return (obj as any[])[key - 1] ?? null;
|
|
1258
1275
|
}
|
|
1259
1276
|
if (isTaggedFloat(key)) {
|
|
1260
|
-
return (obj as any[])[key.value - 1];
|
|
1277
|
+
return (obj as any[])[key.value - 1] ?? null;
|
|
1261
1278
|
}
|
|
1262
1279
|
// Native JS object
|
|
1263
1280
|
const k = toNumKey(key);
|
|
@@ -1275,7 +1292,8 @@ export function luaGet(
|
|
|
1275
1292
|
export function luaLen(
|
|
1276
1293
|
obj: any,
|
|
1277
1294
|
sf?: LuaStackFrame,
|
|
1278
|
-
|
|
1295
|
+
raw = false,
|
|
1296
|
+
): number | Promise<number> {
|
|
1279
1297
|
if (typeof obj === "string") {
|
|
1280
1298
|
return obj.length;
|
|
1281
1299
|
}
|
|
@@ -1283,6 +1301,18 @@ export function luaLen(
|
|
|
1283
1301
|
return obj.length;
|
|
1284
1302
|
}
|
|
1285
1303
|
if (obj instanceof LuaTable) {
|
|
1304
|
+
// Check __len metamethod unless raw access is requested
|
|
1305
|
+
if (!raw) {
|
|
1306
|
+
const mt = getMetatable(obj, sf || LuaStackFrame.lostFrame);
|
|
1307
|
+
const mm = mt ? mt.rawGet("__len") : null;
|
|
1308
|
+
if (mm !== undefined && mm !== null) {
|
|
1309
|
+
const r = luaCall(mm, [obj], (sf?.astCtx ?? {}) as ASTCtx, sf);
|
|
1310
|
+
if (isPromise(r)) {
|
|
1311
|
+
return (r as Promise<any>).then((v: any) => Number(singleResult(v)));
|
|
1312
|
+
}
|
|
1313
|
+
return Number(singleResult(r));
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1286
1316
|
return obj.rawLength;
|
|
1287
1317
|
}
|
|
1288
1318
|
|
|
@@ -1462,7 +1492,7 @@ export class LuaRuntimeError extends Error {
|
|
|
1462
1492
|
);
|
|
1463
1493
|
|
|
1464
1494
|
// Add position indicator
|
|
1465
|
-
const pointer = " ".repeat(column)
|
|
1495
|
+
const pointer = `${" ".repeat(column)}^`;
|
|
1466
1496
|
|
|
1467
1497
|
traceStr += `* ${ctx.ref || "(unknown source)"} @ ${line}:${column}:\n` +
|
|
1468
1498
|
` ${codeLine}\n` +
|
|
@@ -1549,7 +1579,7 @@ export function luaToString(
|
|
|
1549
1579
|
const strVal = await luaToString(val, visited);
|
|
1550
1580
|
result += strVal;
|
|
1551
1581
|
}
|
|
1552
|
-
return result
|
|
1582
|
+
return `${result}}`;
|
|
1553
1583
|
}
|
|
1554
1584
|
|
|
1555
1585
|
// Handle objects
|
|
@@ -1576,7 +1606,7 @@ export function luaToString(
|
|
|
1576
1606
|
}
|
|
1577
1607
|
|
|
1578
1608
|
export function luaFormatNumber(n: number, kind?: "int" | "float"): string {
|
|
1579
|
-
if (kind !== "float" && Number.isInteger(n) && isFinite(n)) {
|
|
1609
|
+
if (kind !== "float" && Number.isInteger(n) && Number.isFinite(n)) {
|
|
1580
1610
|
return String(n);
|
|
1581
1611
|
}
|
|
1582
1612
|
if (n !== n) return "-nan";
|
|
@@ -1589,7 +1619,7 @@ export function luaFormatNumber(n: number, kind?: "int" | "float"): string {
|
|
|
1589
1619
|
const s = luaFormat("%.14g", n);
|
|
1590
1620
|
// Guarantee `.01 suffix for integer-valued floats
|
|
1591
1621
|
if (s.indexOf(".") === -1 && s.indexOf("e") === -1) {
|
|
1592
|
-
return s
|
|
1622
|
+
return `${s}.0`;
|
|
1593
1623
|
}
|
|
1594
1624
|
return s;
|
|
1595
1625
|
}
|
|
@@ -128,8 +128,8 @@ function pad(s: string, width: number, flags: number, numPad: boolean): string {
|
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
function addSign(s: string, flags: number): string {
|
|
131
|
-
if (flags & FLAG_PLUS) return
|
|
132
|
-
if (flags & FLAG_SPACE) return
|
|
131
|
+
if (flags & FLAG_PLUS) return `+${s}`;
|
|
132
|
+
if (flags & FLAG_SPACE) return ` ${s}`;
|
|
133
133
|
return s;
|
|
134
134
|
}
|
|
135
135
|
|
|
@@ -200,7 +200,7 @@ function formatInt(n: number, spec: FormatSpec): string {
|
|
|
200
200
|
|
|
201
201
|
let result: string;
|
|
202
202
|
if (neg) {
|
|
203
|
-
result =
|
|
203
|
+
result = `-${prefix}${digits}`;
|
|
204
204
|
} else {
|
|
205
205
|
result = addSign(prefix + digits, spec.flags);
|
|
206
206
|
}
|
|
@@ -216,7 +216,7 @@ function formatFloat(n: number, spec: FormatSpec): string {
|
|
|
216
216
|
const lower = code | 32; // to lowercase
|
|
217
217
|
|
|
218
218
|
// Lua convention
|
|
219
|
-
if (!isFinite(n)) {
|
|
219
|
+
if (!Number.isFinite(n)) {
|
|
220
220
|
let s: string;
|
|
221
221
|
if (n !== n) {
|
|
222
222
|
s = upper ? "-NAN" : "-nan";
|
|
@@ -275,9 +275,9 @@ function formatFloat(n: number, spec: FormatSpec): string {
|
|
|
275
275
|
const EIdx = body.indexOf("E");
|
|
276
276
|
const expIdx = eIdx !== -1 ? eIdx : EIdx;
|
|
277
277
|
if (expIdx !== -1) {
|
|
278
|
-
body = body.slice(0, expIdx)
|
|
278
|
+
body = `${body.slice(0, expIdx)}.${body.slice(expIdx)}`;
|
|
279
279
|
} else {
|
|
280
|
-
body = body
|
|
280
|
+
body = `${body}.`;
|
|
281
281
|
}
|
|
282
282
|
}
|
|
283
283
|
}
|
|
@@ -287,16 +287,16 @@ function formatFloat(n: number, spec: FormatSpec): string {
|
|
|
287
287
|
if (body.indexOf(".") === -1) {
|
|
288
288
|
const expIdx = findExpIndex(body);
|
|
289
289
|
if (expIdx !== -1) {
|
|
290
|
-
body = body.slice(0, expIdx)
|
|
290
|
+
body = `${body.slice(0, expIdx)}.${body.slice(expIdx)}`;
|
|
291
291
|
} else {
|
|
292
|
-
body = body
|
|
292
|
+
body = `${body}.`;
|
|
293
293
|
}
|
|
294
294
|
}
|
|
295
295
|
}
|
|
296
296
|
|
|
297
297
|
let result: string;
|
|
298
298
|
if (neg) {
|
|
299
|
-
result =
|
|
299
|
+
result = `-${body}`;
|
|
300
300
|
} else {
|
|
301
301
|
result = addSign(body, spec.flags);
|
|
302
302
|
}
|
|
@@ -322,7 +322,7 @@ function ensureExpTwoDigits(s: string): string {
|
|
|
322
322
|
const digitStart = signIdx + 1;
|
|
323
323
|
const expLen = s.length - digitStart;
|
|
324
324
|
if (expLen < 2) {
|
|
325
|
-
return s.slice(0, digitStart)
|
|
325
|
+
return `${s.slice(0, digitStart)}0${s.slice(digitStart)}`;
|
|
326
326
|
}
|
|
327
327
|
return s;
|
|
328
328
|
}
|
|
@@ -353,7 +353,7 @@ function formatHexFloat(n: number, spec: FormatSpec): string {
|
|
|
353
353
|
const code = spec.spec;
|
|
354
354
|
const upper = code === 65; // 'A'
|
|
355
355
|
|
|
356
|
-
if (!isFinite(n)) {
|
|
356
|
+
if (!Number.isFinite(n)) {
|
|
357
357
|
let s: string;
|
|
358
358
|
if (n !== n) {
|
|
359
359
|
s = upper ? "-NAN" : "-nan";
|
|
@@ -373,7 +373,7 @@ function formatHexFloat(n: number, spec: FormatSpec): string {
|
|
|
373
373
|
if (abs === 0) {
|
|
374
374
|
const prec = spec.hasPrec ? spec.prec : 0;
|
|
375
375
|
if (prec > 0) {
|
|
376
|
-
body =
|
|
376
|
+
body = `0x0.${"0".repeat(prec)}p+0`;
|
|
377
377
|
} else {
|
|
378
378
|
body = "0x0p+0";
|
|
379
379
|
}
|
|
@@ -395,14 +395,14 @@ function formatHexFloat(n: number, spec: FormatSpec): string {
|
|
|
395
395
|
}
|
|
396
396
|
}
|
|
397
397
|
if (!hasDot) {
|
|
398
|
-
body = body.slice(0, pIdx)
|
|
398
|
+
body = `${body.slice(0, pIdx)}.${body.slice(pIdx)}`;
|
|
399
399
|
}
|
|
400
400
|
}
|
|
401
401
|
}
|
|
402
402
|
|
|
403
403
|
let result: string;
|
|
404
404
|
if (neg) {
|
|
405
|
-
result =
|
|
405
|
+
result = `-${body}`;
|
|
406
406
|
} else {
|
|
407
407
|
result = addSign(body, spec.flags);
|
|
408
408
|
}
|
|
@@ -474,9 +474,9 @@ function hexFloatBody(abs: number, spec: FormatSpec): string {
|
|
|
474
474
|
|
|
475
475
|
const expSign = exponent >= 0 ? "+" : "";
|
|
476
476
|
if (fracHex.length > 0) {
|
|
477
|
-
return
|
|
477
|
+
return `0x${firstDigit}.${fracHex}p${expSign}${exponent}`;
|
|
478
478
|
}
|
|
479
|
-
return
|
|
479
|
+
return `0x${firstDigit}p${expSign}${exponent}`;
|
|
480
480
|
}
|
|
481
481
|
|
|
482
482
|
// Convert 52-bit value to 13 hex digits, zero-padded
|
|
@@ -581,7 +581,7 @@ function quoteString(s: string): string {
|
|
|
581
581
|
if (ds.length < 3) out += "0".repeat(3 - ds.length);
|
|
582
582
|
out += ds;
|
|
583
583
|
} else {
|
|
584
|
-
out +=
|
|
584
|
+
out += `\\${c.toString()}`;
|
|
585
585
|
}
|
|
586
586
|
} else {
|
|
587
587
|
out += String.fromCharCode(c);
|
|
@@ -643,7 +643,7 @@ function toPointer(v: unknown): string {
|
|
|
643
643
|
id = nextId++;
|
|
644
644
|
stringIds.set(key, id);
|
|
645
645
|
}
|
|
646
|
-
return
|
|
646
|
+
return `0x${id.toString(16).padStart(14, "0")}`;
|
|
647
647
|
}
|
|
648
648
|
|
|
649
649
|
const obj = v as object;
|
|
@@ -652,7 +652,7 @@ function toPointer(v: unknown): string {
|
|
|
652
652
|
id = nextId++;
|
|
653
653
|
objectIds.set(obj, id);
|
|
654
654
|
}
|
|
655
|
-
return
|
|
655
|
+
return `0x${id.toString(16).padStart(14, "0")}`;
|
|
656
656
|
}
|
|
657
657
|
|
|
658
658
|
function formatPointer(v: unknown, spec: FormatSpec): string {
|
|
@@ -4,7 +4,11 @@ import {
|
|
|
4
4
|
LuaRuntimeError,
|
|
5
5
|
LuaTable,
|
|
6
6
|
} from "../runtime.ts";
|
|
7
|
-
import { isNegativeZero, isTaggedFloat } from "../numeric.ts";
|
|
7
|
+
import { isNegativeZero, isTaggedFloat, makeLuaFloat } from "../numeric.ts";
|
|
8
|
+
import { LuaPRNG } from "./prng.ts";
|
|
9
|
+
|
|
10
|
+
// One PRNG per module load, auto-seeded at startup
|
|
11
|
+
const prng = new LuaPRNG();
|
|
8
12
|
|
|
9
13
|
// Fast unwrap: avoids function call overhead for the common plain-number case
|
|
10
14
|
function untagNumber(x: any): number {
|
|
@@ -40,6 +44,28 @@ export const mathApi = new LuaTable({
|
|
|
40
44
|
}
|
|
41
45
|
return null;
|
|
42
46
|
}),
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* If the value x is representable as a Lua integer, returns an integer
|
|
50
|
+
* with that value. Otherwise returns nil.
|
|
51
|
+
* Strings are NOT accepted — only Lua number values.
|
|
52
|
+
*/
|
|
53
|
+
tointeger: new LuaBuiltinFunction((_sf, x?: any) => {
|
|
54
|
+
if (typeof x === "number") {
|
|
55
|
+
return Number.isInteger(x) && Number.isFinite(x) ? x : null;
|
|
56
|
+
}
|
|
57
|
+
if (isTaggedFloat(x)) {
|
|
58
|
+
const n = x.value;
|
|
59
|
+
return Number.isInteger(n) && Number.isFinite(n) ? n : null;
|
|
60
|
+
}
|
|
61
|
+
if (typeof x === "string") {
|
|
62
|
+
const n = untagNumber(x); // Number(x) coerces the string
|
|
63
|
+
if (Number.isNaN(n) || !Number.isFinite(n) || !Number.isInteger(n)) return null;
|
|
64
|
+
return n;
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}),
|
|
68
|
+
|
|
43
69
|
/**
|
|
44
70
|
* When called without arguments, returns a pseudo-random float with
|
|
45
71
|
* uniform distribution in the range [0,1). When called with two
|
|
@@ -52,51 +78,21 @@ export const mathApi = new LuaTable({
|
|
|
52
78
|
random: new LuaBuiltinFunction((_sf, m?: number, n?: number) => {
|
|
53
79
|
if (m !== undefined) m = untagNumber(m);
|
|
54
80
|
if (n !== undefined) n = untagNumber(n);
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (!Number.isInteger(m)) {
|
|
61
|
-
throw new LuaRuntimeError(
|
|
62
|
-
"bad argument #1 to 'math.random' (integer expected)",
|
|
63
|
-
_sf,
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (n === undefined) {
|
|
68
|
-
if (m! == 0) {
|
|
69
|
-
const high = Math.floor(Math.random() * 0x100000000);
|
|
70
|
-
const low = Math.floor(Math.random() * 0x100000000);
|
|
71
|
-
let result = (BigInt(high) << 32n) | BigInt(low);
|
|
72
|
-
if (result & (1n << 63n)) {
|
|
73
|
-
result -= 1n << 64n;
|
|
74
|
-
}
|
|
75
|
-
return result;
|
|
76
|
-
}
|
|
77
|
-
if (m! < 1) {
|
|
78
|
-
throw new LuaRuntimeError(
|
|
79
|
-
"bad argument #1 to 'math.random' (interval is empty)",
|
|
80
|
-
_sf,
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
return Math.floor(Math.random() * m!) + 1;
|
|
81
|
+
try {
|
|
82
|
+
return prng.random(m, n);
|
|
83
|
+
} catch (e: any) {
|
|
84
|
+
throw new LuaRuntimeError(e.message, _sf);
|
|
84
85
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
"bad argument #1 to 'math.random' (interval is empty)",
|
|
96
|
-
_sf,
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
return Math.floor(Math.random() * (n! - m! + 1)) + m!;
|
|
86
|
+
}),
|
|
87
|
+
/**
|
|
88
|
+
* Seeds the pseudo-random generator. With no arguments, uses a
|
|
89
|
+
* time-based seed. Returns the two seed integers used (Lua 5.4 contract).
|
|
90
|
+
*/
|
|
91
|
+
randomseed: new LuaBuiltinFunction((_sf, x?: number, y?: number) => {
|
|
92
|
+
if (x !== undefined) x = untagNumber(x);
|
|
93
|
+
if (y !== undefined) y = untagNumber(y);
|
|
94
|
+
const [s1, s2] = prng.randomseed(x, y);
|
|
95
|
+
return new LuaMultiRes([s1, s2]);
|
|
100
96
|
}),
|
|
101
97
|
|
|
102
98
|
// Basic functions
|
|
@@ -117,10 +113,38 @@ export const mathApi = new LuaTable({
|
|
|
117
113
|
modf: new LuaBuiltinFunction((_sf, x: number) => {
|
|
118
114
|
const xn = untagNumber(x);
|
|
119
115
|
const int = Math.trunc(xn);
|
|
120
|
-
|
|
116
|
+
// Guarantee that the `frac` part is always Lua float
|
|
117
|
+
const frac = makeLuaFloat(xn - int);
|
|
121
118
|
return new LuaMultiRes([int, frac]);
|
|
122
119
|
}),
|
|
123
120
|
|
|
121
|
+
// Returns m and e such that x = m * 2^e, 0.5 <= |m| < 1 (or m=0 when x=0).
|
|
122
|
+
// e is an integer. Mirrors C99/Lua.
|
|
123
|
+
// Special cases: frexp(0) = (0, 0); frexp(+-inf/nan) = (x, 0).
|
|
124
|
+
frexp: new LuaBuiltinFunction((_sf, x: number) => {
|
|
125
|
+
const xn = untagNumber(x);
|
|
126
|
+
if (xn === 0 || !Number.isFinite(xn) || Number.isNaN(xn)) {
|
|
127
|
+
return new LuaMultiRes([xn, 0]);
|
|
128
|
+
}
|
|
129
|
+
const abs = Math.abs(xn);
|
|
130
|
+
let e = Math.floor(Math.log2(abs)) + 1;
|
|
131
|
+
let m = xn / 2 ** e;
|
|
132
|
+
if (Math.abs(m) >= 1.0) {
|
|
133
|
+
e += 1;
|
|
134
|
+
m /= 2;
|
|
135
|
+
}
|
|
136
|
+
if (Math.abs(m) < 0.5) {
|
|
137
|
+
e -= 1;
|
|
138
|
+
m *= 2;
|
|
139
|
+
}
|
|
140
|
+
return new LuaMultiRes([m, e]);
|
|
141
|
+
}),
|
|
142
|
+
|
|
143
|
+
// Returns m * 2^e (the inverse of frexp). Mirrors C99/Lua.
|
|
144
|
+
ldexp: new LuaBuiltinFunction((_sf, m: number, e: number) =>
|
|
145
|
+
untagNumber(m) * 2 ** untagNumber(e)
|
|
146
|
+
),
|
|
147
|
+
|
|
124
148
|
// Power and logarithms
|
|
125
149
|
exp: new LuaBuiltinFunction((_sf, x: number) => Math.exp(untagNumber(x))),
|
|
126
150
|
log: new LuaBuiltinFunction((_sf, x: number, base?: number) => {
|
|
@@ -129,8 +153,9 @@ export const mathApi = new LuaTable({
|
|
|
129
153
|
}
|
|
130
154
|
return Math.log(untagNumber(x)) / Math.log(untagNumber(base));
|
|
131
155
|
}),
|
|
156
|
+
// Power function (deprecated in Lua 5.4 but retained for compatibility)
|
|
132
157
|
pow: new LuaBuiltinFunction((_sf, x: number, y: number) =>
|
|
133
|
-
|
|
158
|
+
untagNumber(x) ** untagNumber(y)
|
|
134
159
|
),
|
|
135
160
|
sqrt: new LuaBuiltinFunction((_sf, x: number) => Math.sqrt(untagNumber(x))),
|
|
136
161
|
|
|
@@ -147,7 +172,7 @@ export const mathApi = new LuaTable({
|
|
|
147
172
|
return Math.atan2(untagNumber(y), untagNumber(x));
|
|
148
173
|
}),
|
|
149
174
|
|
|
150
|
-
// Hyperbolic functions
|
|
175
|
+
// Hyperbolic functions (deprecated in Lua 5.4 but retained for compatibility)
|
|
151
176
|
cosh: new LuaBuiltinFunction((_sf, x: number) => Math.cosh(untagNumber(x))),
|
|
152
177
|
sinh: new LuaBuiltinFunction((_sf, x: number) => Math.sinh(untagNumber(x))),
|
|
153
178
|
tanh: new LuaBuiltinFunction((_sf, x: number) => Math.tanh(untagNumber(x))),
|
|
@@ -66,7 +66,7 @@ export const netApi = new LuaTable({
|
|
|
66
66
|
(uri: string, options: { uri?: string; encoding?: string } = {}) => {
|
|
67
67
|
options.uri = uri;
|
|
68
68
|
return client.clientSystem.serviceRegistry.invokeBestMatch(
|
|
69
|
-
|
|
69
|
+
`net.readURI:${uri}`,
|
|
70
70
|
options,
|
|
71
71
|
);
|
|
72
72
|
},
|
|
@@ -74,7 +74,7 @@ export const netApi = new LuaTable({
|
|
|
74
74
|
writeURI: new LuaNativeJSFunction(
|
|
75
75
|
(uri: string, content: string | Uint8Array) => {
|
|
76
76
|
return client.clientSystem.serviceRegistry.invokeBestMatch(
|
|
77
|
-
|
|
77
|
+
`net.writeURI:${uri}`,
|
|
78
78
|
{ uri, content },
|
|
79
79
|
);
|
|
80
80
|
},
|
|
@@ -365,4 +365,9 @@ export const osApi = new LuaTable({
|
|
|
365
365
|
return luaFormatTime(fmt, d, utc);
|
|
366
366
|
},
|
|
367
367
|
),
|
|
368
|
+
|
|
369
|
+
// Returns an approximation of CPU time used by the program in seconds.
|
|
370
|
+
clock: new LuaBuiltinFunction((_sf): number => {
|
|
371
|
+
return performance.now() / 1000.0;
|
|
372
|
+
}),
|
|
368
373
|
});
|