@silverbulletmd/silverbullet 2.5.3 → 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 +4 -5
- package/client/asset_bundle/bundle.ts +3 -9
- package/client/data/datastore.ts +4 -5
- package/client/markdown_parser/constants.ts +3 -2
- package/client/plugos/hooks/code_widget.ts +3 -5
- package/client/plugos/hooks/command.ts +8 -8
- package/client/plugos/hooks/document_editor.ts +10 -12
- package/client/plugos/hooks/event.ts +33 -36
- package/client/plugos/hooks/mq.ts +17 -17
- package/client/plugos/hooks/plug_namespace.ts +3 -5
- package/client/plugos/hooks/slash_command.ts +12 -27
- package/client/plugos/hooks/syscall.ts +3 -3
- package/client/plugos/manifest_cache.ts +22 -15
- package/client/plugos/plug.ts +2 -5
- package/client/plugos/plug_compile.ts +67 -65
- package/client/plugos/protocol.ts +28 -28
- package/client/plugos/proxy_fetch.ts +7 -6
- package/client/plugos/sandboxes/worker_sandbox.ts +16 -15
- 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 +63 -60
- package/client/plugos/syscalls/event.ts +9 -12
- package/client/plugos/syscalls/fetch.ts +30 -22
- 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 +3 -11
- package/client/plugos/syscalls/service_registry.ts +1 -4
- package/client/plugos/syscalls/shell.ts +2 -5
- package/client/plugos/syscalls/sync.ts +69 -60
- package/client/plugos/syscalls/system.ts +2 -3
- package/client/plugos/system.ts +4 -10
- package/client/plugos/worker_runtime.ts +4 -3
- package/client/space_lua/aggregates.ts +632 -59
- package/client/space_lua/ast.ts +21 -9
- package/client/space_lua/ast_narrow.ts +4 -2
- package/client/space_lua/eval.ts +842 -536
- package/client/space_lua/labels.ts +6 -11
- package/client/space_lua/liq_null.ts +6 -0
- package/client/space_lua/numeric.ts +5 -8
- package/client/space_lua/parse.ts +290 -169
- package/client/space_lua/query_collection.ts +213 -149
- package/client/space_lua/render_lua_markdown.ts +369 -0
- package/client/space_lua/rp.ts +5 -4
- package/client/space_lua/runtime.ts +245 -142
- package/client/space_lua/stdlib/format.ts +34 -20
- 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 +15 -14
- package/client/space_lua/stdlib/net.ts +25 -15
- package/client/space_lua/stdlib/os.ts +76 -85
- package/client/space_lua/stdlib/pattern.ts +28 -35
- package/client/space_lua/stdlib/prng.ts +15 -12
- package/client/space_lua/stdlib/space_lua.ts +16 -17
- package/client/space_lua/stdlib/string.ts +7 -17
- package/client/space_lua/stdlib/string_pack.ts +23 -19
- package/client/space_lua/stdlib/table.ts +5 -9
- package/client/space_lua/stdlib.ts +20 -30
- package/client/space_lua/tonumber.ts +79 -40
- package/client/space_lua/util.ts +14 -10
- package/dist/plug-compile.js +44 -41
- package/package.json +24 -22
- package/plug-api/lib/async.ts +19 -6
- package/plug-api/lib/crypto.ts +5 -6
- package/plug-api/lib/dates.ts +15 -7
- package/plug-api/lib/json.ts +10 -4
- package/plug-api/lib/ref.ts +18 -18
- package/plug-api/lib/resolve.ts +7 -11
- package/plug-api/lib/tags.ts +13 -4
- package/plug-api/lib/transclusion.ts +6 -17
- package/plug-api/lib/tree.ts +115 -43
- package/plug-api/lib/yaml.ts +25 -15
- package/plug-api/syscalls/asset.ts +1 -1
- package/plug-api/syscalls/config.ts +1 -4
- package/plug-api/syscalls/editor.ts +14 -14
- 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/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/dist/worker_runtime_bundle.js +0 -233
package/client/space_lua/eval.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
LuaExpression,
|
|
5
5
|
LuaLValue,
|
|
6
6
|
LuaStatement,
|
|
7
|
+
LuaTableField,
|
|
7
8
|
NumericType,
|
|
8
9
|
} from "./ast.ts";
|
|
9
10
|
import { LuaAttribute } from "./ast.ts";
|
|
@@ -38,7 +39,11 @@ import {
|
|
|
38
39
|
luaValueToJS,
|
|
39
40
|
singleResult,
|
|
40
41
|
} from "./runtime.ts";
|
|
41
|
-
import {
|
|
42
|
+
import {
|
|
43
|
+
type LuaCollectionQuery,
|
|
44
|
+
type LuaGroupByEntry,
|
|
45
|
+
toCollection,
|
|
46
|
+
} from "./query_collection.ts";
|
|
42
47
|
import {
|
|
43
48
|
coerceNumericPair,
|
|
44
49
|
coerceToNumber,
|
|
@@ -105,8 +110,14 @@ function astNumberKind(e: LuaExpression | undefined): NumericType | undefined {
|
|
|
105
110
|
result = unwrapped.numericType === "int" ? "int" : "float";
|
|
106
111
|
} else if (unwrapped.type === "Binary") {
|
|
107
112
|
const op = unwrapped.operator;
|
|
108
|
-
const numericOp =
|
|
109
|
-
op === "
|
|
113
|
+
const numericOp =
|
|
114
|
+
op === "+" ||
|
|
115
|
+
op === "-" ||
|
|
116
|
+
op === "*" ||
|
|
117
|
+
op === "/" ||
|
|
118
|
+
op === "//" ||
|
|
119
|
+
op === "%" ||
|
|
120
|
+
op === "^";
|
|
110
121
|
|
|
111
122
|
if (numericOp) {
|
|
112
123
|
const lk = astNumberKind(unwrapped.left);
|
|
@@ -176,7 +187,6 @@ function blockMetaOrThrow(
|
|
|
176
187
|
}
|
|
177
188
|
}
|
|
178
189
|
|
|
179
|
-
|
|
180
190
|
function arithVerbFromOperator(op: string): string | null {
|
|
181
191
|
switch (op) {
|
|
182
192
|
case "+":
|
|
@@ -247,67 +257,68 @@ export function luaOp(
|
|
|
247
257
|
sf: LuaStackFrame,
|
|
248
258
|
): any {
|
|
249
259
|
switch (op) {
|
|
250
|
-
case "+":
|
|
251
|
-
|
|
252
|
-
|
|
260
|
+
case "+": {
|
|
261
|
+
// Ultra-fast path: both plain numbers with no float type annotation (int + int)
|
|
262
|
+
if (
|
|
263
|
+
typeof left === "number" &&
|
|
264
|
+
typeof right === "number" &&
|
|
265
|
+
leftType !== "float" &&
|
|
266
|
+
rightType !== "float"
|
|
267
|
+
) {
|
|
268
|
+
return left + right;
|
|
269
|
+
}
|
|
270
|
+
return luaArithGeneric("+", left, right, leftType, rightType, ctx, sf);
|
|
271
|
+
}
|
|
272
|
+
case "-": {
|
|
273
|
+
if (
|
|
274
|
+
typeof left === "number" &&
|
|
275
|
+
typeof right === "number" &&
|
|
276
|
+
leftType !== "float" &&
|
|
277
|
+
rightType !== "float"
|
|
278
|
+
) {
|
|
279
|
+
const r = left - right;
|
|
280
|
+
// Integer subtraction can produce -0 (e.g. 0 - 0), normalize to +0
|
|
281
|
+
return r === 0 ? 0 : r;
|
|
282
|
+
}
|
|
283
|
+
return luaArithGeneric("-", left, right, leftType, rightType, ctx, sf);
|
|
284
|
+
}
|
|
285
|
+
case "*": {
|
|
286
|
+
if (
|
|
287
|
+
typeof left === "number" &&
|
|
288
|
+
typeof right === "number" &&
|
|
289
|
+
leftType !== "float" &&
|
|
290
|
+
rightType !== "float"
|
|
291
|
+
) {
|
|
292
|
+
const r = left * right;
|
|
293
|
+
// Integer multiplication can produce -0 (e.g. 0 * -1), normalize to +0
|
|
294
|
+
return r === 0 ? 0 : r;
|
|
295
|
+
}
|
|
296
|
+
return luaArithGeneric("*", left, right, leftType, rightType, ctx, sf);
|
|
297
|
+
}
|
|
253
298
|
case "/":
|
|
254
299
|
case "^": {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
let result = ar.f(l, r);
|
|
266
|
-
|
|
267
|
-
if (
|
|
268
|
-
ar.special === "sub" &&
|
|
269
|
-
result === 0 &&
|
|
270
|
-
isNegativeZero(result) &&
|
|
271
|
-
resultType === "float"
|
|
272
|
-
) {
|
|
273
|
-
const rhsIsIntZero = r === 0 && rightType === "int";
|
|
274
|
-
if (rhsIsIntZero) {
|
|
275
|
-
result = 0;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
const normalized = normalizeArithmeticResult(result, resultType);
|
|
280
|
-
|
|
281
|
-
// Operators `/` and `^` produce float, wrap only if needed.
|
|
282
|
-
if (op === "/" || op === "^") {
|
|
283
|
-
if (normalized === 0) {
|
|
284
|
-
return makeLuaZero(normalized, "float");
|
|
285
|
-
}
|
|
286
|
-
if (!Number.isFinite(normalized)) {
|
|
287
|
-
return normalized;
|
|
288
|
-
}
|
|
289
|
-
if (!Number.isInteger(normalized)) {
|
|
290
|
-
return normalized;
|
|
291
|
-
}
|
|
292
|
-
return makeLuaFloat(normalized);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
if (normalized === 0) {
|
|
296
|
-
return makeLuaZero(normalized, resultType);
|
|
297
|
-
}
|
|
298
|
-
if (resultType === "float" && Number.isInteger(normalized)) {
|
|
299
|
-
return makeLuaFloat(normalized);
|
|
300
|
-
}
|
|
301
|
-
return normalized;
|
|
302
|
-
} catch (e: any) {
|
|
303
|
-
const meta = evalMetamethod(left, right, ar.metaMethod, ctx, sf);
|
|
304
|
-
if (meta !== undefined) {
|
|
305
|
-
return meta;
|
|
306
|
-
}
|
|
307
|
-
return arithCoercionErrorOrThrow(op, left, right, ctx, sf, e);
|
|
308
|
-
}
|
|
300
|
+
return luaArithGeneric(
|
|
301
|
+
op as NumericArithOp,
|
|
302
|
+
left,
|
|
303
|
+
right,
|
|
304
|
+
leftType,
|
|
305
|
+
rightType,
|
|
306
|
+
ctx,
|
|
307
|
+
sf,
|
|
308
|
+
);
|
|
309
309
|
}
|
|
310
310
|
case "..": {
|
|
311
|
+
// Fast path: string .. string (most common in SilverBullet — key building, templates)
|
|
312
|
+
if (typeof left === "string" && typeof right === "string") {
|
|
313
|
+
return left + right;
|
|
314
|
+
}
|
|
315
|
+
// Fast path: string .. number or number .. string
|
|
316
|
+
if (typeof left === "string" && typeof right === "number") {
|
|
317
|
+
return left + luaFormatNumber(right);
|
|
318
|
+
}
|
|
319
|
+
if (typeof left === "number" && typeof right === "string") {
|
|
320
|
+
return luaFormatNumber(left) + right;
|
|
321
|
+
}
|
|
311
322
|
try {
|
|
312
323
|
const coerce = (v: any): string => {
|
|
313
324
|
if (v === null || v === undefined) {
|
|
@@ -341,27 +352,54 @@ export function luaOp(
|
|
|
341
352
|
}
|
|
342
353
|
}
|
|
343
354
|
case "==": {
|
|
355
|
+
// Fast path for same-type primitives
|
|
356
|
+
if (typeof left === typeof right && typeof left !== "object") {
|
|
357
|
+
return left === right;
|
|
358
|
+
}
|
|
344
359
|
if (luaEquals(left, right)) return true;
|
|
345
360
|
return luaEqWithMetamethod(left, right, ctx, sf);
|
|
346
361
|
}
|
|
347
362
|
case "~=":
|
|
348
363
|
case "!=": {
|
|
364
|
+
if (typeof left === typeof right && typeof left !== "object") {
|
|
365
|
+
return left !== right;
|
|
366
|
+
}
|
|
349
367
|
if (luaEquals(left, right)) {
|
|
350
368
|
return false;
|
|
351
369
|
}
|
|
352
370
|
return !luaEqWithMetamethod(left, right, ctx, sf);
|
|
353
371
|
}
|
|
354
372
|
case "<": {
|
|
373
|
+
// Fast path: both plain numbers
|
|
374
|
+
if (typeof left === "number" && typeof right === "number") {
|
|
375
|
+
return left < right;
|
|
376
|
+
}
|
|
377
|
+
// Fast path: both strings
|
|
378
|
+
if (typeof left === "string" && typeof right === "string") {
|
|
379
|
+
return left < right;
|
|
380
|
+
}
|
|
355
381
|
return luaRelWithMetamethod("<", left, right, ctx, sf);
|
|
356
382
|
}
|
|
357
383
|
case "<=": {
|
|
384
|
+
if (typeof left === "number" && typeof right === "number")
|
|
385
|
+
return left <= right;
|
|
386
|
+
if (typeof left === "string" && typeof right === "string")
|
|
387
|
+
return left <= right;
|
|
358
388
|
return luaRelWithMetamethod("<=", left, right, ctx, sf);
|
|
359
389
|
}
|
|
360
390
|
// Lua: `a>b` is `b<a`, `a>=b` is `b<=a`
|
|
361
391
|
case ">": {
|
|
392
|
+
if (typeof left === "number" && typeof right === "number")
|
|
393
|
+
return left > right;
|
|
394
|
+
if (typeof left === "string" && typeof right === "string")
|
|
395
|
+
return left > right;
|
|
362
396
|
return luaRelWithMetamethod("<", right, left, ctx, sf);
|
|
363
397
|
}
|
|
364
398
|
case ">=": {
|
|
399
|
+
if (typeof left === "number" && typeof right === "number")
|
|
400
|
+
return left >= right;
|
|
401
|
+
if (typeof left === "string" && typeof right === "string")
|
|
402
|
+
return left >= right;
|
|
365
403
|
return luaRelWithMetamethod("<=", right, left, ctx, sf);
|
|
366
404
|
}
|
|
367
405
|
}
|
|
@@ -392,13 +430,79 @@ export function luaOp(
|
|
|
392
430
|
}
|
|
393
431
|
}
|
|
394
432
|
|
|
433
|
+
function luaArithGeneric(
|
|
434
|
+
op: NumericArithOp,
|
|
435
|
+
left: any,
|
|
436
|
+
right: any,
|
|
437
|
+
leftType: NumericType | undefined,
|
|
438
|
+
rightType: NumericType | undefined,
|
|
439
|
+
ctx: ASTCtx,
|
|
440
|
+
sf: LuaStackFrame,
|
|
441
|
+
): any {
|
|
442
|
+
const ar = numericArith[op];
|
|
443
|
+
try {
|
|
444
|
+
const {
|
|
445
|
+
left: l,
|
|
446
|
+
right: r,
|
|
447
|
+
resultType,
|
|
448
|
+
} = coerceNumericPair(left, right, leftType, rightType, op);
|
|
449
|
+
|
|
450
|
+
let result = ar.f(l, r);
|
|
451
|
+
|
|
452
|
+
if (
|
|
453
|
+
ar.special === "sub" &&
|
|
454
|
+
result === 0 &&
|
|
455
|
+
isNegativeZero(result) &&
|
|
456
|
+
resultType === "float"
|
|
457
|
+
) {
|
|
458
|
+
const rhsIsIntZero = r === 0 && rightType === "int";
|
|
459
|
+
if (rhsIsIntZero) {
|
|
460
|
+
result = 0;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
const normalized = normalizeArithmeticResult(result, resultType);
|
|
465
|
+
|
|
466
|
+
// Operators `/` and `^` produce float, wrap only if needed.
|
|
467
|
+
if (op === "/" || op === "^") {
|
|
468
|
+
if (normalized === 0) {
|
|
469
|
+
return makeLuaZero(normalized, "float");
|
|
470
|
+
}
|
|
471
|
+
if (!Number.isFinite(normalized)) {
|
|
472
|
+
return normalized;
|
|
473
|
+
}
|
|
474
|
+
if (!Number.isInteger(normalized)) {
|
|
475
|
+
return normalized;
|
|
476
|
+
}
|
|
477
|
+
return makeLuaFloat(normalized);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
if (normalized === 0) {
|
|
481
|
+
return makeLuaZero(normalized, resultType);
|
|
482
|
+
}
|
|
483
|
+
if (resultType === "float" && Number.isInteger(normalized)) {
|
|
484
|
+
return makeLuaFloat(normalized);
|
|
485
|
+
}
|
|
486
|
+
return normalized;
|
|
487
|
+
} catch (e: any) {
|
|
488
|
+
const meta = evalMetamethod(left, right, ar.metaMethod, ctx, sf);
|
|
489
|
+
if (meta !== undefined) {
|
|
490
|
+
return meta;
|
|
491
|
+
}
|
|
492
|
+
return arithCoercionErrorOrThrow(op, left, right, ctx, sf, e);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
395
496
|
type NumericArithOp = "+" | "-" | "*" | "/" | "^";
|
|
396
497
|
|
|
397
|
-
const numericArith: Record<
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
498
|
+
const numericArith: Record<
|
|
499
|
+
NumericArithOp,
|
|
500
|
+
{
|
|
501
|
+
metaMethod: "__add" | "__sub" | "__mul" | "__div" | "__pow";
|
|
502
|
+
f: (l: number, r: number) => number;
|
|
503
|
+
special?: "sub";
|
|
504
|
+
}
|
|
505
|
+
> = {
|
|
402
506
|
"+": { metaMethod: "__add", f: (l, r) => l + r },
|
|
403
507
|
"-": { metaMethod: "__sub", f: (l, r) => l - r, special: "sub" },
|
|
404
508
|
"*": { metaMethod: "__mul", f: (l, r) => l * r },
|
|
@@ -448,10 +552,7 @@ function luaFloorDiv(
|
|
|
448
552
|
);
|
|
449
553
|
|
|
450
554
|
if (resultType === "int" && right === 0) {
|
|
451
|
-
throw new LuaRuntimeError(
|
|
452
|
-
`attempt to divide by zero`,
|
|
453
|
-
sf.withCtx(ctx),
|
|
454
|
-
);
|
|
555
|
+
throw new LuaRuntimeError(`attempt to divide by zero`, sf.withCtx(ctx));
|
|
455
556
|
}
|
|
456
557
|
|
|
457
558
|
const result = Math.floor(left / right);
|
|
@@ -482,10 +583,7 @@ function luaMod(
|
|
|
482
583
|
);
|
|
483
584
|
|
|
484
585
|
if (resultType === "int" && right === 0) {
|
|
485
|
-
throw new LuaRuntimeError(
|
|
486
|
-
`attempt to perform 'n%0'`,
|
|
487
|
-
sf.withCtx(ctx),
|
|
488
|
-
);
|
|
586
|
+
throw new LuaRuntimeError(`attempt to perform 'n%0'`, sf.withCtx(ctx));
|
|
489
587
|
}
|
|
490
588
|
|
|
491
589
|
const q = Math.floor(left / right);
|
|
@@ -506,10 +604,7 @@ function luaMod(
|
|
|
506
604
|
return normalized;
|
|
507
605
|
}
|
|
508
606
|
|
|
509
|
-
function luaUnaryMinus(
|
|
510
|
-
v: number,
|
|
511
|
-
numType: NumericType | undefined,
|
|
512
|
-
): number {
|
|
607
|
+
function luaUnaryMinus(v: number, numType: NumericType | undefined): number {
|
|
513
608
|
const vType = numType ?? inferNumericType(v);
|
|
514
609
|
|
|
515
610
|
if (v === 0 && vType === "int") {
|
|
@@ -523,17 +618,20 @@ function luaUnaryMinus(
|
|
|
523
618
|
return -v;
|
|
524
619
|
}
|
|
525
620
|
|
|
526
|
-
const operatorsMetaMethods: Record<
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
621
|
+
const operatorsMetaMethods: Record<
|
|
622
|
+
string,
|
|
623
|
+
{
|
|
624
|
+
metaMethod?: string;
|
|
625
|
+
nativeImplementation: (
|
|
626
|
+
a: LuaValue,
|
|
627
|
+
b: LuaValue,
|
|
628
|
+
leftType: NumericType | undefined,
|
|
629
|
+
rightType: NumericType | undefined,
|
|
630
|
+
ctx: ASTCtx,
|
|
631
|
+
sf: LuaStackFrame,
|
|
632
|
+
) => LuaValue;
|
|
633
|
+
}
|
|
634
|
+
> = {
|
|
537
635
|
"//": {
|
|
538
636
|
metaMethod: "__idiv",
|
|
539
637
|
nativeImplementation: (a, b, lt, rt, ctx, sf) =>
|
|
@@ -596,6 +694,168 @@ const operatorsMetaMethods: Record<string, {
|
|
|
596
694
|
},
|
|
597
695
|
};
|
|
598
696
|
|
|
697
|
+
function deriveFieldName(e: LuaExpression): string | undefined {
|
|
698
|
+
switch (e.type) {
|
|
699
|
+
case "Variable":
|
|
700
|
+
return e.name;
|
|
701
|
+
case "PropertyAccess":
|
|
702
|
+
return e.property;
|
|
703
|
+
case "FunctionCall":
|
|
704
|
+
if (e.name) return e.name;
|
|
705
|
+
if (e.prefix.type === "Variable") return e.prefix.name;
|
|
706
|
+
if (e.prefix.type === "PropertyAccess") return e.prefix.property;
|
|
707
|
+
return undefined;
|
|
708
|
+
case "FilteredCall":
|
|
709
|
+
return deriveFieldName(e.call);
|
|
710
|
+
case "AggregateCall":
|
|
711
|
+
return deriveFieldName((e as any).call);
|
|
712
|
+
default:
|
|
713
|
+
return undefined;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
function fieldsToExpression(
|
|
718
|
+
fields: LuaTableField[],
|
|
719
|
+
ctx: ASTCtx,
|
|
720
|
+
): LuaExpression {
|
|
721
|
+
if (fields.length === 1 && fields[0].type === "ExpressionField") {
|
|
722
|
+
return fields[0].value;
|
|
723
|
+
}
|
|
724
|
+
const promoted: LuaTableField[] = fields.map((f) => {
|
|
725
|
+
if (f.type !== "ExpressionField") return f;
|
|
726
|
+
const key = deriveFieldName(f.value);
|
|
727
|
+
if (key) {
|
|
728
|
+
return {
|
|
729
|
+
type: "PropField",
|
|
730
|
+
key,
|
|
731
|
+
value: f.value,
|
|
732
|
+
ctx: f.ctx,
|
|
733
|
+
} as LuaTableField;
|
|
734
|
+
}
|
|
735
|
+
return f;
|
|
736
|
+
});
|
|
737
|
+
return { type: "TableConstructor", fields: promoted, ctx };
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
function fieldsToGroupByEntries(fields: LuaTableField[]): LuaGroupByEntry[] {
|
|
741
|
+
return fields.map((f) => {
|
|
742
|
+
switch (f.type) {
|
|
743
|
+
case "PropField":
|
|
744
|
+
return { expr: f.value, alias: f.key };
|
|
745
|
+
case "ExpressionField":
|
|
746
|
+
return { expr: f.value };
|
|
747
|
+
case "DynamicField":
|
|
748
|
+
return { expr: f.value };
|
|
749
|
+
}
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
type FromSource =
|
|
754
|
+
| { kind: "single"; objectVariable?: string; expression: LuaExpression }
|
|
755
|
+
| { kind: "cross"; sources: { name: string; expression: LuaExpression }[] };
|
|
756
|
+
|
|
757
|
+
function fromFieldsToSource(fields: LuaTableField[], ctx: ASTCtx): FromSource {
|
|
758
|
+
if (fields.length === 1) {
|
|
759
|
+
const f = fields[0];
|
|
760
|
+
if (f.type === "ExpressionField") {
|
|
761
|
+
return { kind: "single", expression: f.value };
|
|
762
|
+
}
|
|
763
|
+
if (f.type === "PropField") {
|
|
764
|
+
return { kind: "single", objectVariable: f.key, expression: f.value };
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
const sources: { name: string; expression: LuaExpression }[] = [];
|
|
769
|
+
for (const f of fields) {
|
|
770
|
+
if (f.type !== "PropField") {
|
|
771
|
+
throw new LuaRuntimeError("Multi-source 'from' requires named sources", {
|
|
772
|
+
ref: ctx,
|
|
773
|
+
} as any);
|
|
774
|
+
}
|
|
775
|
+
sources.push({ name: f.key, expression: f.value });
|
|
776
|
+
}
|
|
777
|
+
return { kind: "cross", sources };
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
async function normalizeToArray(
|
|
781
|
+
collection: LuaValue,
|
|
782
|
+
sf: LuaStackFrame,
|
|
783
|
+
): Promise<any[]> {
|
|
784
|
+
if (collection instanceof LuaTable && collection.empty()) {
|
|
785
|
+
return [];
|
|
786
|
+
}
|
|
787
|
+
if (collection instanceof LuaTable) {
|
|
788
|
+
if (collection.length > 0) {
|
|
789
|
+
const arr: any[] = [];
|
|
790
|
+
for (let i = 1; i <= collection.length; i++) {
|
|
791
|
+
arr.push(collection.rawGet(i));
|
|
792
|
+
}
|
|
793
|
+
return arr;
|
|
794
|
+
}
|
|
795
|
+
return [collection];
|
|
796
|
+
}
|
|
797
|
+
if (Array.isArray(collection)) {
|
|
798
|
+
return collection;
|
|
799
|
+
}
|
|
800
|
+
if (
|
|
801
|
+
typeof collection === "object" &&
|
|
802
|
+
collection !== null &&
|
|
803
|
+
"query" in collection &&
|
|
804
|
+
typeof (collection as any).query === "function"
|
|
805
|
+
) {
|
|
806
|
+
const allItems = await (collection as any).query(
|
|
807
|
+
{ distinct: false },
|
|
808
|
+
new LuaEnv(),
|
|
809
|
+
sf,
|
|
810
|
+
);
|
|
811
|
+
return Array.isArray(allItems) ? allItems : [allItems];
|
|
812
|
+
}
|
|
813
|
+
const jsVal = luaValueToJS(collection, sf);
|
|
814
|
+
return Array.isArray(jsVal) ? jsVal : [jsVal];
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
async function evalCrossJoinSources(
|
|
818
|
+
sources: { name: string; expression: LuaExpression }[],
|
|
819
|
+
env: LuaEnv,
|
|
820
|
+
sf: LuaStackFrame,
|
|
821
|
+
ctx: ASTCtx,
|
|
822
|
+
): Promise<LuaTable[]> {
|
|
823
|
+
// Evaluate each source and normalize to arrays
|
|
824
|
+
const arrays: { name: string; items: any[] }[] = [];
|
|
825
|
+
for (const src of sources) {
|
|
826
|
+
const val = await evalExpression(src.expression, env, sf);
|
|
827
|
+
if (val === null || val === undefined) {
|
|
828
|
+
throw new LuaRuntimeError(
|
|
829
|
+
`Cross-join source '${src.name}' is nil`,
|
|
830
|
+
sf.withCtx(ctx),
|
|
831
|
+
);
|
|
832
|
+
}
|
|
833
|
+
const items = await normalizeToArray(val, sf);
|
|
834
|
+
arrays.push({ name: src.name, items });
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// Cartesian product
|
|
838
|
+
let product: Record<string, any>[] = [{}];
|
|
839
|
+
for (const { name, items } of arrays) {
|
|
840
|
+
const newProduct: Record<string, any>[] = [];
|
|
841
|
+
for (const combo of product) {
|
|
842
|
+
for (const item of items) {
|
|
843
|
+
newProduct.push({ ...combo, [name]: item });
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
product = newProduct;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
// Convert each combination to a `LuaTable` row
|
|
850
|
+
return product.map((combo) => {
|
|
851
|
+
const row = new LuaTable();
|
|
852
|
+
for (const key in combo) {
|
|
853
|
+
void row.rawSet(key, combo[key]);
|
|
854
|
+
}
|
|
855
|
+
return row;
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
|
|
599
859
|
export function evalExpression(
|
|
600
860
|
e: LuaExpression,
|
|
601
861
|
env: LuaEnv,
|
|
@@ -629,14 +889,7 @@ export function evalExpression(
|
|
|
629
889
|
if (b.operator === "and") {
|
|
630
890
|
return evalLogical("and", b.left, b.right, env, sf);
|
|
631
891
|
}
|
|
632
|
-
return evalBinaryWithLR(
|
|
633
|
-
b.operator,
|
|
634
|
-
b.left,
|
|
635
|
-
b.right,
|
|
636
|
-
b.ctx,
|
|
637
|
-
env,
|
|
638
|
-
sf,
|
|
639
|
-
);
|
|
892
|
+
return evalBinaryWithLR(b.operator, b.left, b.right, b.ctx, env, sf);
|
|
640
893
|
}
|
|
641
894
|
case "Unary": {
|
|
642
895
|
const u = asUnary(e);
|
|
@@ -660,59 +913,53 @@ export function evalExpression(
|
|
|
660
913
|
const applyTyped = (typed: TypedValue) => {
|
|
661
914
|
const arg = singleResult(typed.value);
|
|
662
915
|
|
|
663
|
-
return unaryWithMeta(
|
|
664
|
-
|
|
665
|
-
"
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
() => {
|
|
669
|
-
// Numeric-string coercion for unary minus
|
|
670
|
-
if (typeof arg === "string") {
|
|
671
|
-
const n = coerceToNumber(arg);
|
|
672
|
-
if (n === null) {
|
|
673
|
-
throw new LuaRuntimeError(
|
|
674
|
-
"attempt to unm a 'string' with a 'string'",
|
|
675
|
-
sf.withCtx(u.ctx),
|
|
676
|
-
);
|
|
677
|
-
}
|
|
678
|
-
if (n === 0) {
|
|
679
|
-
return 0;
|
|
680
|
-
}
|
|
681
|
-
return -n;
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
const plain = untagNumber(arg);
|
|
685
|
-
if (typeof plain !== "number") {
|
|
916
|
+
return unaryWithMeta(arg, "__unm", u.ctx, sf, () => {
|
|
917
|
+
// Numeric-string coercion for unary minus
|
|
918
|
+
if (typeof arg === "string") {
|
|
919
|
+
const n = coerceToNumber(arg);
|
|
920
|
+
if (n === null) {
|
|
686
921
|
throw new LuaRuntimeError(
|
|
687
|
-
"attempt to
|
|
922
|
+
"attempt to unm a 'string' with a 'string'",
|
|
688
923
|
sf.withCtx(u.ctx),
|
|
689
924
|
);
|
|
690
925
|
}
|
|
926
|
+
if (n === 0) {
|
|
927
|
+
return 0;
|
|
928
|
+
}
|
|
929
|
+
return -n;
|
|
930
|
+
}
|
|
691
931
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
932
|
+
const plain = untagNumber(arg);
|
|
933
|
+
if (typeof plain !== "number") {
|
|
934
|
+
throw new LuaRuntimeError(
|
|
935
|
+
"attempt to perform arithmetic on a table value",
|
|
936
|
+
sf.withCtx(u.ctx),
|
|
937
|
+
);
|
|
938
|
+
}
|
|
695
939
|
|
|
696
|
-
|
|
940
|
+
const argType = isTaggedFloat(arg)
|
|
941
|
+
? "float"
|
|
942
|
+
: astNumberKind(u.argument);
|
|
697
943
|
|
|
698
|
-
|
|
699
|
-
// minus must keep the result float-typed.
|
|
700
|
-
if (isTaggedFloat(arg)) {
|
|
701
|
-
if (out === 0) {
|
|
702
|
-
return makeLuaZero(out, "float");
|
|
703
|
-
}
|
|
704
|
-
return makeLuaFloat(out);
|
|
705
|
-
}
|
|
944
|
+
const out = luaUnaryMinus(plain, argType);
|
|
706
945
|
|
|
707
|
-
|
|
946
|
+
// If the operand is a float-tagged boxed number, unary
|
|
947
|
+
// minus must keep the result float-typed.
|
|
948
|
+
if (isTaggedFloat(arg)) {
|
|
708
949
|
if (out === 0) {
|
|
709
|
-
|
|
710
|
-
return makeLuaZero(out, outType);
|
|
950
|
+
return makeLuaZero(out, "float");
|
|
711
951
|
}
|
|
952
|
+
return makeLuaFloat(out);
|
|
953
|
+
}
|
|
712
954
|
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
955
|
+
// Preserve numeric kind for zero results
|
|
956
|
+
if (out === 0) {
|
|
957
|
+
const outType = argType ?? inferNumericType(plain);
|
|
958
|
+
return makeLuaZero(out, outType);
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
return out;
|
|
962
|
+
});
|
|
716
963
|
};
|
|
717
964
|
|
|
718
965
|
return rpThen(tv as any, applyTyped);
|
|
@@ -727,35 +974,29 @@ export function evalExpression(
|
|
|
727
974
|
}
|
|
728
975
|
case "~": {
|
|
729
976
|
const arg = singleResult(value);
|
|
730
|
-
return unaryWithMeta(
|
|
731
|
-
arg
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
sf,
|
|
735
|
-
() => {
|
|
736
|
-
const intVal = toInteger(arg);
|
|
737
|
-
if (intVal === null) {
|
|
738
|
-
if (typeof arg === "string") {
|
|
739
|
-
throw new LuaRuntimeError(
|
|
740
|
-
`attempt to perform bitwise operation on a string value (constant '${arg}')`,
|
|
741
|
-
sf.withCtx(u.ctx),
|
|
742
|
-
);
|
|
743
|
-
}
|
|
744
|
-
const t = luaTypeName(arg);
|
|
745
|
-
if (t === "number") {
|
|
746
|
-
throw new LuaRuntimeError(
|
|
747
|
-
`number has no integer representation`,
|
|
748
|
-
sf.withCtx(u.ctx),
|
|
749
|
-
);
|
|
750
|
-
}
|
|
977
|
+
return unaryWithMeta(arg, "__bnot", u.ctx, sf, () => {
|
|
978
|
+
const intVal = toInteger(arg);
|
|
979
|
+
if (intVal === null) {
|
|
980
|
+
if (typeof arg === "string") {
|
|
751
981
|
throw new LuaRuntimeError(
|
|
752
|
-
`attempt to perform bitwise operation on a ${
|
|
982
|
+
`attempt to perform bitwise operation on a string value (constant '${arg}')`,
|
|
753
983
|
sf.withCtx(u.ctx),
|
|
754
984
|
);
|
|
755
985
|
}
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
986
|
+
const t = luaTypeName(arg);
|
|
987
|
+
if (t === "number") {
|
|
988
|
+
throw new LuaRuntimeError(
|
|
989
|
+
`number has no integer representation`,
|
|
990
|
+
sf.withCtx(u.ctx),
|
|
991
|
+
);
|
|
992
|
+
}
|
|
993
|
+
throw new LuaRuntimeError(
|
|
994
|
+
`attempt to perform bitwise operation on a ${t} value`,
|
|
995
|
+
sf.withCtx(u.ctx),
|
|
996
|
+
);
|
|
997
|
+
}
|
|
998
|
+
return ~intVal;
|
|
999
|
+
});
|
|
759
1000
|
}
|
|
760
1001
|
case "#": {
|
|
761
1002
|
return luaLengthOp(singleResult(value), u.ctx, sf);
|
|
@@ -779,31 +1020,63 @@ export function evalExpression(
|
|
|
779
1020
|
}
|
|
780
1021
|
case "TableConstructor": {
|
|
781
1022
|
const tc = asTableConstructor(e);
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
1023
|
+
const table = new LuaTable();
|
|
1024
|
+
// Expression fields assign consecutive integer keys starting
|
|
1025
|
+
// at 1 and advance even when the value is `nil`.
|
|
1026
|
+
let nextArrayIndex = 1;
|
|
1027
|
+
|
|
1028
|
+
const processField = (
|
|
1029
|
+
fieldIdx: number,
|
|
1030
|
+
): LuaTable | Promise<LuaTable> => {
|
|
1031
|
+
for (let fi = fieldIdx; fi < tc.fields.length; fi++) {
|
|
1032
|
+
const field = tc.fields[fi];
|
|
788
1033
|
switch (field.type) {
|
|
789
1034
|
case "PropField": {
|
|
790
|
-
const value =
|
|
791
|
-
|
|
1035
|
+
const value = evalExpression(field.value, env, sf);
|
|
1036
|
+
if (isPromise(value)) {
|
|
1037
|
+
return (value as Promise<any>).then((v) => {
|
|
1038
|
+
void table.set(field.key, singleResult(v), sf);
|
|
1039
|
+
return processField(fi + 1);
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
void table.set(field.key, singleResult(value), sf);
|
|
792
1043
|
break;
|
|
793
1044
|
}
|
|
794
1045
|
case "DynamicField": {
|
|
795
|
-
const key =
|
|
796
|
-
const
|
|
797
|
-
|
|
1046
|
+
const key = evalExpression(field.key, env, sf);
|
|
1047
|
+
const val = evalExpression(field.value, env, sf);
|
|
1048
|
+
if (isPromise(key) || isPromise(val)) {
|
|
1049
|
+
return rpThen(key, (k) =>
|
|
1050
|
+
rpThen(val, (v) => {
|
|
1051
|
+
void table.set(singleResult(k), singleResult(v), sf);
|
|
1052
|
+
return processField(fi + 1);
|
|
1053
|
+
}),
|
|
1054
|
+
) as Promise<LuaTable>;
|
|
1055
|
+
}
|
|
1056
|
+
void table.set(singleResult(key), singleResult(val), sf);
|
|
798
1057
|
break;
|
|
799
1058
|
}
|
|
800
1059
|
case "ExpressionField": {
|
|
801
|
-
const value =
|
|
802
|
-
|
|
1060
|
+
const value = evalExpression(field.value, env, sf);
|
|
1061
|
+
if (isPromise(value)) {
|
|
1062
|
+
return (value as Promise<any>).then((v) => {
|
|
1063
|
+
if (v instanceof LuaMultiRes) {
|
|
1064
|
+
const flat = v.flatten();
|
|
1065
|
+
for (let j = 0; j < flat.values.length; j++) {
|
|
1066
|
+
table.rawSetArrayIndex(nextArrayIndex, flat.values[j]);
|
|
1067
|
+
nextArrayIndex++;
|
|
1068
|
+
}
|
|
1069
|
+
} else {
|
|
1070
|
+
table.rawSetArrayIndex(nextArrayIndex, singleResult(v));
|
|
1071
|
+
nextArrayIndex++;
|
|
1072
|
+
}
|
|
1073
|
+
return processField(fi + 1);
|
|
1074
|
+
});
|
|
1075
|
+
}
|
|
803
1076
|
if (value instanceof LuaMultiRes) {
|
|
804
1077
|
const flat = value.flatten();
|
|
805
|
-
for (let
|
|
806
|
-
table.rawSetArrayIndex(nextArrayIndex, flat.values[
|
|
1078
|
+
for (let j = 0; j < flat.values.length; j++) {
|
|
1079
|
+
table.rawSetArrayIndex(nextArrayIndex, flat.values[j]);
|
|
807
1080
|
nextArrayIndex++;
|
|
808
1081
|
}
|
|
809
1082
|
} else {
|
|
@@ -815,7 +1088,9 @@ export function evalExpression(
|
|
|
815
1088
|
}
|
|
816
1089
|
}
|
|
817
1090
|
return table;
|
|
818
|
-
}
|
|
1091
|
+
};
|
|
1092
|
+
|
|
1093
|
+
return processField(0);
|
|
819
1094
|
}
|
|
820
1095
|
case "FunctionDefinition": {
|
|
821
1096
|
const fd = asFunctionDef(e);
|
|
@@ -825,20 +1100,95 @@ export function evalExpression(
|
|
|
825
1100
|
const q = asQueryExpr(e);
|
|
826
1101
|
const findFromClause = q.clauses.find((c) => c.type === "From");
|
|
827
1102
|
if (!findFromClause) {
|
|
828
|
-
throw new LuaRuntimeError(
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
1103
|
+
throw new LuaRuntimeError("No from clause found", sf.withCtx(q.ctx));
|
|
1104
|
+
}
|
|
1105
|
+
const fromSource = fromFieldsToSource(
|
|
1106
|
+
findFromClause.fields,
|
|
1107
|
+
findFromClause.ctx,
|
|
1108
|
+
);
|
|
1109
|
+
|
|
1110
|
+
if (fromSource.kind === "cross") {
|
|
1111
|
+
// Materialize Cartesian product, then query
|
|
1112
|
+
return (async () => {
|
|
1113
|
+
const rows = await evalCrossJoinSources(
|
|
1114
|
+
fromSource.sources,
|
|
1115
|
+
env,
|
|
1116
|
+
sf,
|
|
1117
|
+
findFromClause.ctx,
|
|
1118
|
+
);
|
|
1119
|
+
const collection: any = toCollection(rows);
|
|
1120
|
+
|
|
1121
|
+
// Build up query object
|
|
1122
|
+
const query: LuaCollectionQuery = {
|
|
1123
|
+
objectVariable: undefined,
|
|
1124
|
+
distinct: true,
|
|
1125
|
+
};
|
|
1126
|
+
|
|
1127
|
+
// Map clauses to query parameters
|
|
1128
|
+
for (const clause of q.clauses) {
|
|
1129
|
+
switch (clause.type) {
|
|
1130
|
+
case "Where": {
|
|
1131
|
+
query.where = clause.expression;
|
|
1132
|
+
break;
|
|
1133
|
+
}
|
|
1134
|
+
case "OrderBy": {
|
|
1135
|
+
query.orderBy = clause.orderBy.map((o) => ({
|
|
1136
|
+
expr: o.expression,
|
|
1137
|
+
desc: o.direction === "desc",
|
|
1138
|
+
nulls: o.nulls,
|
|
1139
|
+
using: o.using,
|
|
1140
|
+
}));
|
|
1141
|
+
break;
|
|
1142
|
+
}
|
|
1143
|
+
case "Select": {
|
|
1144
|
+
query.select = fieldsToExpression(clause.fields, clause.ctx);
|
|
1145
|
+
break;
|
|
1146
|
+
}
|
|
1147
|
+
case "Limit": {
|
|
1148
|
+
const limitVal = await evalExpression(clause.limit, env, sf);
|
|
1149
|
+
query.limit = Number(limitVal);
|
|
1150
|
+
if (clause.offset) {
|
|
1151
|
+
const offsetVal = await evalExpression(
|
|
1152
|
+
clause.offset,
|
|
1153
|
+
env,
|
|
1154
|
+
sf,
|
|
1155
|
+
);
|
|
1156
|
+
query.offset = Number(offsetVal);
|
|
1157
|
+
}
|
|
1158
|
+
break;
|
|
1159
|
+
}
|
|
1160
|
+
case "Offset": {
|
|
1161
|
+
const offsetVal = await evalExpression(
|
|
1162
|
+
clause.offset,
|
|
1163
|
+
env,
|
|
1164
|
+
sf,
|
|
1165
|
+
);
|
|
1166
|
+
query.offset = Number(offsetVal);
|
|
1167
|
+
break;
|
|
1168
|
+
}
|
|
1169
|
+
case "GroupBy": {
|
|
1170
|
+
query.groupBy = fieldsToGroupByEntries(clause.fields);
|
|
1171
|
+
break;
|
|
1172
|
+
}
|
|
1173
|
+
case "Having": {
|
|
1174
|
+
query.having = clause.expression;
|
|
1175
|
+
break;
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
return (collection as any)
|
|
1181
|
+
.query(query, env, sf, globalThis.client?.config)
|
|
1182
|
+
.then(jsToLuaValue);
|
|
1183
|
+
})();
|
|
832
1184
|
}
|
|
833
|
-
|
|
834
|
-
|
|
1185
|
+
|
|
1186
|
+
// Single-source
|
|
1187
|
+
const { objectVariable, expression: objectExpression } = fromSource;
|
|
835
1188
|
return Promise.resolve(evalExpression(objectExpression, env, sf)).then(
|
|
836
1189
|
async (collection: LuaValue) => {
|
|
837
1190
|
if (!collection) {
|
|
838
|
-
throw new LuaRuntimeError(
|
|
839
|
-
"Collection is nil",
|
|
840
|
-
sf.withCtx(q.ctx),
|
|
841
|
-
);
|
|
1191
|
+
throw new LuaRuntimeError("Collection is nil", sf.withCtx(q.ctx));
|
|
842
1192
|
}
|
|
843
1193
|
|
|
844
1194
|
// If already a queryable collection (e.g. DataStoreQueryCollection),
|
|
@@ -892,7 +1242,7 @@ export function evalExpression(
|
|
|
892
1242
|
break;
|
|
893
1243
|
}
|
|
894
1244
|
case "Select": {
|
|
895
|
-
query.select = clause.
|
|
1245
|
+
query.select = fieldsToExpression(clause.fields, clause.ctx);
|
|
896
1246
|
break;
|
|
897
1247
|
}
|
|
898
1248
|
case "Limit": {
|
|
@@ -908,8 +1258,17 @@ export function evalExpression(
|
|
|
908
1258
|
}
|
|
909
1259
|
break;
|
|
910
1260
|
}
|
|
1261
|
+
case "Offset": {
|
|
1262
|
+
const offsetVal = await evalExpression(
|
|
1263
|
+
clause.offset,
|
|
1264
|
+
env,
|
|
1265
|
+
sf,
|
|
1266
|
+
);
|
|
1267
|
+
query.offset = Number(offsetVal);
|
|
1268
|
+
break;
|
|
1269
|
+
}
|
|
911
1270
|
case "GroupBy": {
|
|
912
|
-
query.groupBy = clause.
|
|
1271
|
+
query.groupBy = fieldsToGroupByEntries(clause.fields);
|
|
913
1272
|
break;
|
|
914
1273
|
}
|
|
915
1274
|
case "Having": {
|
|
@@ -920,7 +1279,9 @@ export function evalExpression(
|
|
|
920
1279
|
}
|
|
921
1280
|
|
|
922
1281
|
// Always use the possibly-wrapped collection
|
|
923
|
-
return (collection as any)
|
|
1282
|
+
return (collection as any)
|
|
1283
|
+
.query(query, env, sf, globalThis.client?.config)
|
|
1284
|
+
.then(jsToLuaValue);
|
|
924
1285
|
},
|
|
925
1286
|
);
|
|
926
1287
|
}
|
|
@@ -973,13 +1334,10 @@ function evalPrefixExpression(
|
|
|
973
1334
|
return luaGet(table, key, ta.ctx, sf);
|
|
974
1335
|
}
|
|
975
1336
|
|
|
976
|
-
return rpThen(
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
keyV,
|
|
981
|
-
(key) => luaGet(singleResult(obj), singleResult(key), ta.ctx, sf),
|
|
982
|
-
),
|
|
1337
|
+
return rpThen(objV, (obj) =>
|
|
1338
|
+
rpThen(keyV, (key) =>
|
|
1339
|
+
luaGet(singleResult(obj), singleResult(key), ta.ctx, sf),
|
|
1340
|
+
),
|
|
983
1341
|
);
|
|
984
1342
|
}
|
|
985
1343
|
|
|
@@ -989,50 +1347,88 @@ function evalPrefixExpression(
|
|
|
989
1347
|
// Sync-first: evaluate object; avoid Promise when object is sync.
|
|
990
1348
|
const objV = evalPrefixExpression(pa.object, env, sf);
|
|
991
1349
|
if (!isPromise(objV)) {
|
|
992
|
-
return luaGet(objV, pa.property, pa.ctx, sf);
|
|
1350
|
+
return luaGet(singleResult(objV), pa.property, pa.ctx, sf);
|
|
993
1351
|
}
|
|
994
|
-
return rpThen(objV, (obj) =>
|
|
1352
|
+
return rpThen(objV, (obj) =>
|
|
1353
|
+
luaGet(singleResult(obj), pa.property, pa.ctx, sf),
|
|
1354
|
+
);
|
|
995
1355
|
}
|
|
996
1356
|
|
|
997
1357
|
case "FunctionCall": {
|
|
998
1358
|
const fc = asFunctionCall(e);
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
asVariable(fc.prefix).name
|
|
1004
|
-
}')`
|
|
1005
|
-
: `attempt to call a nil value`;
|
|
1359
|
+
|
|
1360
|
+
// `order by` inside function arguments is only valid for aggregate
|
|
1361
|
+
// calls evaluated by the query engine
|
|
1362
|
+
if (fc.orderBy && fc.orderBy.length > 0) {
|
|
1006
1363
|
throw new LuaRuntimeError(
|
|
1007
|
-
|
|
1008
|
-
sf.withCtx(fc.
|
|
1364
|
+
`'order by' is not allowed in non-aggregate function calls`,
|
|
1365
|
+
sf.withCtx(fc.ctx),
|
|
1009
1366
|
);
|
|
1010
1367
|
}
|
|
1011
1368
|
|
|
1012
|
-
|
|
1369
|
+
const prefixValue = evalPrefixExpression(fc.prefix, env, sf);
|
|
1370
|
+
if (prefixValue === null || prefixValue === undefined) {
|
|
1371
|
+
const nilMsg =
|
|
1372
|
+
fc.prefix.type === "Variable"
|
|
1373
|
+
? `attempt to call a nil value (global '${
|
|
1374
|
+
asVariable(fc.prefix).name
|
|
1375
|
+
}')`
|
|
1376
|
+
: `attempt to call a nil value`;
|
|
1377
|
+
throw new LuaRuntimeError(nilMsg, sf.withCtx(fc.prefix.ctx));
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
// Fast path: non-method call with sync prefix
|
|
1381
|
+
if (!fc.name && !isPromise(prefixValue)) {
|
|
1382
|
+
const argsVal = evalExpressions(fc.args, env, sf);
|
|
1383
|
+
if (!isPromise(argsVal)) {
|
|
1384
|
+
return luaCall(prefixValue, argsVal as LuaValue[], fc.ctx, sf);
|
|
1385
|
+
}
|
|
1386
|
+
return (argsVal as Promise<LuaValue[]>).then((args) =>
|
|
1387
|
+
luaCall(prefixValue, args, fc.ctx, sf),
|
|
1388
|
+
);
|
|
1389
|
+
}
|
|
1013
1390
|
|
|
1014
1391
|
const handleFunctionCall = (
|
|
1015
1392
|
calleeVal: LuaValue,
|
|
1393
|
+
selfArgs: LuaValue[],
|
|
1016
1394
|
): LuaValue | Promise<LuaValue> => {
|
|
1017
1395
|
// Normal argument handling for hello:there(a, b, c) type calls
|
|
1018
1396
|
if (fc.name) {
|
|
1019
|
-
|
|
1397
|
+
const self = calleeVal;
|
|
1020
1398
|
calleeVal = luaIndexValue(calleeVal, fc.name, sf);
|
|
1021
1399
|
|
|
1022
1400
|
if (isPromise(calleeVal)) {
|
|
1023
|
-
return (calleeVal as Promise<any>).then(
|
|
1401
|
+
return (calleeVal as Promise<any>).then((cv) =>
|
|
1402
|
+
handleFunctionCall(cv, [self]),
|
|
1403
|
+
);
|
|
1024
1404
|
}
|
|
1405
|
+
selfArgs = [self];
|
|
1025
1406
|
}
|
|
1026
1407
|
|
|
1027
1408
|
const argsVal = evalExpressions(fc.args, env, sf);
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1409
|
+
if (!isPromise(argsVal)) {
|
|
1410
|
+
const allArgs =
|
|
1411
|
+
selfArgs.length > 0
|
|
1412
|
+
? [...selfArgs, ...(argsVal as LuaValue[])]
|
|
1413
|
+
: (argsVal as LuaValue[]);
|
|
1414
|
+
return luaCall(calleeVal, allArgs, fc.ctx, sf);
|
|
1415
|
+
}
|
|
1416
|
+
return (argsVal as Promise<LuaValue[]>).then((args) =>
|
|
1417
|
+
luaCall(
|
|
1418
|
+
calleeVal,
|
|
1419
|
+
selfArgs.length > 0 ? [...selfArgs, ...args] : args,
|
|
1420
|
+
fc.ctx,
|
|
1421
|
+
sf,
|
|
1422
|
+
),
|
|
1423
|
+
);
|
|
1033
1424
|
};
|
|
1034
1425
|
|
|
1035
|
-
|
|
1426
|
+
if (isPromise(prefixValue)) {
|
|
1427
|
+
return (prefixValue as Promise<any>).then((pv) =>
|
|
1428
|
+
handleFunctionCall(pv, []),
|
|
1429
|
+
);
|
|
1430
|
+
}
|
|
1431
|
+
return handleFunctionCall(prefixValue, []);
|
|
1036
1432
|
}
|
|
1037
1433
|
|
|
1038
1434
|
default: {
|
|
@@ -1182,22 +1578,58 @@ function evalBinaryWithLR(
|
|
|
1182
1578
|
: undefined;
|
|
1183
1579
|
const leftVal = evalExpression(leftExpr, env, sf);
|
|
1184
1580
|
|
|
1185
|
-
|
|
1581
|
+
// Sync-first fast path: avoid closure allocation when both operands are sync
|
|
1582
|
+
if (!isPromise(leftVal)) {
|
|
1186
1583
|
const rightVal = evalExpression(rightExpr, env, sf);
|
|
1187
|
-
|
|
1584
|
+
if (!isPromise(rightVal)) {
|
|
1188
1585
|
return luaOp(
|
|
1189
1586
|
op,
|
|
1190
|
-
singleResult(
|
|
1587
|
+
singleResult(leftVal),
|
|
1588
|
+
singleResult(rightVal),
|
|
1589
|
+
leftType,
|
|
1590
|
+
rightType,
|
|
1591
|
+
ctx,
|
|
1592
|
+
sf,
|
|
1593
|
+
);
|
|
1594
|
+
}
|
|
1595
|
+
return (rightVal as Promise<any>).then((rv) =>
|
|
1596
|
+
luaOp(
|
|
1597
|
+
op,
|
|
1598
|
+
singleResult(leftVal),
|
|
1191
1599
|
singleResult(rv),
|
|
1192
1600
|
leftType,
|
|
1193
1601
|
rightType,
|
|
1194
1602
|
ctx,
|
|
1195
1603
|
sf,
|
|
1604
|
+
),
|
|
1605
|
+
);
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
return (leftVal as Promise<any>).then((lv) => {
|
|
1609
|
+
const rightVal = evalExpression(rightExpr, env, sf);
|
|
1610
|
+
if (!isPromise(rightVal)) {
|
|
1611
|
+
return luaOp(
|
|
1612
|
+
op,
|
|
1613
|
+
singleResult(lv),
|
|
1614
|
+
singleResult(rightVal),
|
|
1615
|
+
leftType,
|
|
1616
|
+
rightType,
|
|
1617
|
+
ctx,
|
|
1618
|
+
sf,
|
|
1196
1619
|
);
|
|
1197
|
-
}
|
|
1198
|
-
return
|
|
1199
|
-
|
|
1200
|
-
|
|
1620
|
+
}
|
|
1621
|
+
return (rightVal as Promise<any>).then((rv) =>
|
|
1622
|
+
luaOp(
|
|
1623
|
+
op,
|
|
1624
|
+
singleResult(lv),
|
|
1625
|
+
singleResult(rv),
|
|
1626
|
+
leftType,
|
|
1627
|
+
rightType,
|
|
1628
|
+
ctx,
|
|
1629
|
+
sf,
|
|
1630
|
+
),
|
|
1631
|
+
);
|
|
1632
|
+
});
|
|
1201
1633
|
}
|
|
1202
1634
|
|
|
1203
1635
|
function createBitwiseError(
|
|
@@ -1280,10 +1712,7 @@ function luaEqWithMetamethod(
|
|
|
1280
1712
|
}
|
|
1281
1713
|
|
|
1282
1714
|
const ty = luaTypeName(mm);
|
|
1283
|
-
throw new LuaRuntimeError(
|
|
1284
|
-
`attempt to call a ${ty} value`,
|
|
1285
|
-
sf.withCtx(ctx),
|
|
1286
|
-
);
|
|
1715
|
+
throw new LuaRuntimeError(`attempt to call a ${ty} value`, sf.withCtx(ctx));
|
|
1287
1716
|
};
|
|
1288
1717
|
|
|
1289
1718
|
// Try left __eq first, then right.
|
|
@@ -1340,11 +1769,7 @@ function luaRelWithMetamethod(
|
|
|
1340
1769
|
* - for JavaScript arrays return length,
|
|
1341
1770
|
* - throw error otherwise.
|
|
1342
1771
|
*/
|
|
1343
|
-
function luaLengthOp(
|
|
1344
|
-
val: any,
|
|
1345
|
-
ctx: ASTCtx,
|
|
1346
|
-
sf: LuaStackFrame,
|
|
1347
|
-
): LuaValue {
|
|
1772
|
+
function luaLengthOp(val: any, ctx: ASTCtx, sf: LuaStackFrame): LuaValue {
|
|
1348
1773
|
// Strings: ignore `__len`
|
|
1349
1774
|
if (typeof val === "string") {
|
|
1350
1775
|
return val.length;
|
|
@@ -1391,22 +1816,26 @@ function evalExpressions(
|
|
|
1391
1816
|
env: LuaEnv,
|
|
1392
1817
|
sf: LuaStackFrame,
|
|
1393
1818
|
): Promise<LuaValue[]> | LuaValue[] {
|
|
1394
|
-
|
|
1395
|
-
|
|
1819
|
+
const len = es.length;
|
|
1820
|
+
if (len === 0) return [];
|
|
1821
|
+
|
|
1822
|
+
// Evaluate all arguments (sync-first); avoid .map() closure overhead
|
|
1823
|
+
const parts = new Array(len);
|
|
1824
|
+
for (let i = 0; i < len; i++) {
|
|
1825
|
+
parts[i] = evalExpression(es[i], env, sf);
|
|
1826
|
+
}
|
|
1396
1827
|
const argsVal = rpAll(parts);
|
|
1397
1828
|
|
|
1398
1829
|
// In Lua multi-returns propagate only in tail position of an expression list.
|
|
1399
1830
|
const finalize = (argsResolved: any[]) => {
|
|
1400
|
-
if (argsResolved.length === 0) {
|
|
1401
|
-
return [];
|
|
1402
|
-
}
|
|
1403
1831
|
const out: LuaValue[] = [];
|
|
1832
|
+
const lastIdx = argsResolved.length - 1;
|
|
1404
1833
|
// All but last expression produce a single value
|
|
1405
|
-
for (let i = 0; i <
|
|
1834
|
+
for (let i = 0; i < lastIdx; i++) {
|
|
1406
1835
|
out.push(singleResult(argsResolved[i]));
|
|
1407
1836
|
}
|
|
1408
1837
|
// Last expression preserves multiple results
|
|
1409
|
-
const last = argsResolved[
|
|
1838
|
+
const last = argsResolved[lastIdx];
|
|
1410
1839
|
if (last instanceof LuaMultiRes) {
|
|
1411
1840
|
out.push(...last.flatten().values);
|
|
1412
1841
|
} else {
|
|
@@ -1434,12 +1863,7 @@ function runStatementsNoGoto(
|
|
|
1434
1863
|
idx: number,
|
|
1435
1864
|
): undefined | ControlSignal | Promise<undefined | ControlSignal> => {
|
|
1436
1865
|
for (let i = idx; i < stmts.length; i++) {
|
|
1437
|
-
const result = evalStatement(
|
|
1438
|
-
stmts[i],
|
|
1439
|
-
execEnv,
|
|
1440
|
-
sf,
|
|
1441
|
-
returnOnReturn,
|
|
1442
|
-
);
|
|
1866
|
+
const result = evalStatement(stmts[i], execEnv, sf, returnOnReturn);
|
|
1443
1867
|
if (isPromise(result)) {
|
|
1444
1868
|
return (result as Promise<any>).then((res) => {
|
|
1445
1869
|
if (res !== undefined) {
|
|
@@ -1491,9 +1915,8 @@ function withCloseBoundary(
|
|
|
1491
1915
|
};
|
|
1492
1916
|
|
|
1493
1917
|
const onRejected = (e: any) => {
|
|
1494
|
-
const errObj: LuaValue =
|
|
1495
|
-
? e.message
|
|
1496
|
-
: (e?.message ?? String(e));
|
|
1918
|
+
const errObj: LuaValue =
|
|
1919
|
+
e instanceof LuaRuntimeError ? e.message : (e?.message ?? String(e));
|
|
1497
1920
|
const r = luaCloseFromMark(sf, mark, errObj);
|
|
1498
1921
|
if (isPromise(r)) {
|
|
1499
1922
|
return (r as Promise<void>).then(() => {
|
|
@@ -1536,9 +1959,7 @@ function evalBlockNoClose(
|
|
|
1536
1959
|
if (fnHasGotos === true && !hasLabelHere && !hasGotoFlag) {
|
|
1537
1960
|
const execEnv = b.needsEnv === true ? new LuaEnv(env) : env;
|
|
1538
1961
|
const stmts = b.statements;
|
|
1539
|
-
const runFrom = (
|
|
1540
|
-
i: number,
|
|
1541
|
-
): EvalBlockResult => {
|
|
1962
|
+
const runFrom = (i: number): EvalBlockResult => {
|
|
1542
1963
|
for (; i < stmts.length; i++) {
|
|
1543
1964
|
const r = evalStatement(stmts[i], execEnv, sf, returnOnReturn);
|
|
1544
1965
|
if (isPromise(r)) {
|
|
@@ -1583,9 +2004,7 @@ function evalBlockNoClose(
|
|
|
1583
2004
|
const execEnv = b.needsEnv === true ? new LuaEnv(env) : env;
|
|
1584
2005
|
const stmts = b.statements;
|
|
1585
2006
|
|
|
1586
|
-
const runFrom = (
|
|
1587
|
-
i: number,
|
|
1588
|
-
): EvalBlockResult => {
|
|
2007
|
+
const runFrom = (i: number): EvalBlockResult => {
|
|
1589
2008
|
for (; i < stmts.length; i++) {
|
|
1590
2009
|
const r = evalStatement(stmts[i], execEnv, sf, returnOnReturn);
|
|
1591
2010
|
if (isPromise(r)) {
|
|
@@ -1635,17 +2054,20 @@ export function evalStatement(
|
|
|
1635
2054
|
case "Assignment": {
|
|
1636
2055
|
const a = asAssignment(s);
|
|
1637
2056
|
const valuesRP = evalExpressions(a.expressions, env, sf);
|
|
1638
|
-
const lvaluesRP = evalPromiseValues(
|
|
1639
|
-
.map((lval) => evalLValue(lval, env, sf))
|
|
2057
|
+
const lvaluesRP = evalPromiseValues(
|
|
2058
|
+
a.variables.map((lval) => evalLValue(lval, env, sf)),
|
|
2059
|
+
);
|
|
1640
2060
|
|
|
1641
2061
|
const apply = (values: LuaValue[], lvalues: { env: any; key: any }[]) => {
|
|
2062
|
+
// Create the error-reporting frame once, not per-lvalue
|
|
2063
|
+
let errSf: LuaStackFrame | undefined;
|
|
1642
2064
|
const ps: Promise<any>[] = [];
|
|
1643
2065
|
for (let i = 0; i < lvalues.length; i++) {
|
|
1644
2066
|
const r = luaSet(
|
|
1645
2067
|
lvalues[i].env,
|
|
1646
2068
|
lvalues[i].key,
|
|
1647
2069
|
values[i],
|
|
1648
|
-
sf.withCtx(a.ctx),
|
|
2070
|
+
errSf || (errSf = sf.withCtx(a.ctx)),
|
|
1649
2071
|
);
|
|
1650
2072
|
|
|
1651
2073
|
if (isPromise(r)) {
|
|
@@ -1659,29 +2081,22 @@ export function evalStatement(
|
|
|
1659
2081
|
};
|
|
1660
2082
|
|
|
1661
2083
|
if (!isPromise(valuesRP) && !isPromise(lvaluesRP)) {
|
|
1662
|
-
return apply(
|
|
1663
|
-
valuesRP as LuaValue[],
|
|
1664
|
-
lvaluesRP as LuaLValueContainer[],
|
|
1665
|
-
);
|
|
2084
|
+
return apply(valuesRP as LuaValue[], lvaluesRP as LuaLValueContainer[]);
|
|
1666
2085
|
}
|
|
1667
|
-
if (
|
|
1668
|
-
isPromise(valuesRP) && !isPromise(lvaluesRP)
|
|
1669
|
-
) {
|
|
2086
|
+
if (isPromise(valuesRP) && !isPromise(lvaluesRP)) {
|
|
1670
2087
|
return (valuesRP as Promise<LuaValue[]>).then((values: LuaValue[]) =>
|
|
1671
|
-
apply(values, lvaluesRP as LuaLValueContainer[])
|
|
2088
|
+
apply(values, lvaluesRP as LuaLValueContainer[]),
|
|
1672
2089
|
);
|
|
1673
2090
|
}
|
|
1674
|
-
if (
|
|
1675
|
-
!isPromise(valuesRP) && isPromise(lvaluesRP)
|
|
1676
|
-
) {
|
|
2091
|
+
if (!isPromise(valuesRP) && isPromise(lvaluesRP)) {
|
|
1677
2092
|
return (lvaluesRP as Promise<any[]>).then((lvalues: any[]) =>
|
|
1678
|
-
apply(valuesRP as LuaValue[], lvalues)
|
|
2093
|
+
apply(valuesRP as LuaValue[], lvalues),
|
|
1679
2094
|
);
|
|
1680
2095
|
}
|
|
1681
2096
|
return (valuesRP as Promise<LuaValue[]>).then((values: LuaValue[]) =>
|
|
1682
2097
|
(lvaluesRP as Promise<any[]>).then((lvalues: any[]) =>
|
|
1683
|
-
apply(values, lvalues)
|
|
1684
|
-
)
|
|
2098
|
+
apply(values, lvalues),
|
|
2099
|
+
),
|
|
1685
2100
|
);
|
|
1686
2101
|
}
|
|
1687
2102
|
case "Local": {
|
|
@@ -1730,10 +2145,7 @@ export function evalStatement(
|
|
|
1730
2145
|
|
|
1731
2146
|
const bindAvailable = () => {
|
|
1732
2147
|
while (boundCount < l.names.length && boundCount < out.length) {
|
|
1733
|
-
bindOne(
|
|
1734
|
-
l.names[boundCount],
|
|
1735
|
-
out[boundCount] ?? null,
|
|
1736
|
-
);
|
|
2148
|
+
bindOne(l.names[boundCount], out[boundCount] ?? null);
|
|
1737
2149
|
boundCount++;
|
|
1738
2150
|
}
|
|
1739
2151
|
};
|
|
@@ -1813,9 +2225,8 @@ export function evalStatement(
|
|
|
1813
2225
|
try {
|
|
1814
2226
|
out = evalBlockNoClose(b, env, sf, returnOnReturn);
|
|
1815
2227
|
} catch (e: any) {
|
|
1816
|
-
const errObj: LuaValue =
|
|
1817
|
-
? e.message
|
|
1818
|
-
: (e?.message ?? String(e));
|
|
2228
|
+
const errObj: LuaValue =
|
|
2229
|
+
e instanceof LuaRuntimeError ? e.message : (e?.message ?? String(e));
|
|
1819
2230
|
const r = luaCloseFromMark(sf, mark, errObj);
|
|
1820
2231
|
if (isPromise(r)) {
|
|
1821
2232
|
return (r as Promise<void>).then(() => {
|
|
@@ -1834,10 +2245,7 @@ export function evalStatement(
|
|
|
1834
2245
|
|
|
1835
2246
|
const runFrom = (
|
|
1836
2247
|
i: number,
|
|
1837
|
-
):
|
|
1838
|
-
| undefined
|
|
1839
|
-
| ControlSignal
|
|
1840
|
-
| Promise<undefined | ControlSignal> => {
|
|
2248
|
+
): undefined | ControlSignal | Promise<undefined | ControlSignal> => {
|
|
1841
2249
|
if (i >= conds.length) {
|
|
1842
2250
|
if (iff.elseBlock) {
|
|
1843
2251
|
return evalStatement(iff.elseBlock, env, sf, returnOnReturn);
|
|
@@ -1864,128 +2272,84 @@ export function evalStatement(
|
|
|
1864
2272
|
case "While": {
|
|
1865
2273
|
const w = asWhile(s);
|
|
1866
2274
|
|
|
1867
|
-
|
|
2275
|
+
// Sync-first loop that re-enters sync mode after each async iteration
|
|
2276
|
+
const runSyncFirst = ():
|
|
2277
|
+
| undefined
|
|
2278
|
+
| ControlSignal
|
|
2279
|
+
| Promise<undefined | ControlSignal> => {
|
|
1868
2280
|
while (true) {
|
|
1869
|
-
const c =
|
|
1870
|
-
if (
|
|
1871
|
-
|
|
2281
|
+
const c = evalExpression(w.condition, env, sf);
|
|
2282
|
+
if (isPromise(c)) {
|
|
2283
|
+
return (c as Promise<any>).then((cv) => {
|
|
2284
|
+
if (!luaTruthy(cv)) return;
|
|
2285
|
+
return rpThen(
|
|
2286
|
+
evalStatement(w.block, env, sf, returnOnReturn),
|
|
2287
|
+
(res) => {
|
|
2288
|
+
if (res !== undefined) {
|
|
2289
|
+
return isBreakSignal(res) ? undefined : res;
|
|
2290
|
+
}
|
|
2291
|
+
return runSyncFirst();
|
|
2292
|
+
},
|
|
2293
|
+
);
|
|
2294
|
+
});
|
|
1872
2295
|
}
|
|
2296
|
+
if (!luaTruthy(c)) break;
|
|
1873
2297
|
const r = evalStatement(w.block, env, sf, returnOnReturn);
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
2298
|
+
if (isPromise(r)) {
|
|
2299
|
+
return (r as Promise<any>).then((res) => {
|
|
2300
|
+
if (res !== undefined) {
|
|
2301
|
+
return isBreakSignal(res) ? undefined : res;
|
|
2302
|
+
}
|
|
2303
|
+
return runSyncFirst();
|
|
2304
|
+
});
|
|
2305
|
+
}
|
|
2306
|
+
if (r !== undefined) {
|
|
2307
|
+
if (isBreakSignal(r)) break;
|
|
2308
|
+
return r;
|
|
1880
2309
|
}
|
|
1881
2310
|
}
|
|
1882
2311
|
return;
|
|
1883
2312
|
};
|
|
1884
2313
|
|
|
1885
|
-
|
|
1886
|
-
const c = evalExpression(w.condition, env, sf);
|
|
1887
|
-
if (isPromise(c)) {
|
|
1888
|
-
return (c as Promise<any>).then((cv) => {
|
|
1889
|
-
if (!luaTruthy(cv)) {
|
|
1890
|
-
return;
|
|
1891
|
-
}
|
|
1892
|
-
const r = evalStatement(w.block, env, sf, returnOnReturn);
|
|
1893
|
-
if (isPromise(r)) {
|
|
1894
|
-
return (r as Promise<any>).then((res) => {
|
|
1895
|
-
if (res !== undefined) {
|
|
1896
|
-
if (isBreakSignal(res)) {
|
|
1897
|
-
return;
|
|
1898
|
-
}
|
|
1899
|
-
return res;
|
|
1900
|
-
}
|
|
1901
|
-
return runAsync();
|
|
1902
|
-
});
|
|
1903
|
-
}
|
|
1904
|
-
if (r !== undefined) {
|
|
1905
|
-
if (isBreakSignal(r)) {
|
|
1906
|
-
return;
|
|
1907
|
-
}
|
|
1908
|
-
return r;
|
|
1909
|
-
}
|
|
1910
|
-
return runAsync();
|
|
1911
|
-
});
|
|
1912
|
-
}
|
|
1913
|
-
if (!luaTruthy(c)) {
|
|
1914
|
-
break;
|
|
1915
|
-
}
|
|
1916
|
-
const r = evalStatement(w.block, env, sf, returnOnReturn);
|
|
1917
|
-
if (isPromise(r)) {
|
|
1918
|
-
return (r as Promise<any>).then((res) => {
|
|
1919
|
-
if (res !== undefined) {
|
|
1920
|
-
if (isBreakSignal(res)) {
|
|
1921
|
-
return;
|
|
1922
|
-
}
|
|
1923
|
-
return res;
|
|
1924
|
-
}
|
|
1925
|
-
return runAsync();
|
|
1926
|
-
});
|
|
1927
|
-
}
|
|
1928
|
-
if (r !== undefined) {
|
|
1929
|
-
if (isBreakSignal(r)) {
|
|
1930
|
-
break;
|
|
1931
|
-
}
|
|
1932
|
-
return r;
|
|
1933
|
-
}
|
|
1934
|
-
}
|
|
1935
|
-
return;
|
|
2314
|
+
return runSyncFirst();
|
|
1936
2315
|
}
|
|
1937
2316
|
case "Repeat": {
|
|
1938
|
-
const
|
|
2317
|
+
const rep = asRepeat(s);
|
|
1939
2318
|
|
|
1940
|
-
|
|
2319
|
+
// Sync-first loop that re-enters sync mode after each async iteration
|
|
2320
|
+
const runSyncFirst = ():
|
|
2321
|
+
| undefined
|
|
2322
|
+
| ControlSignal
|
|
2323
|
+
| Promise<undefined | ControlSignal> => {
|
|
1941
2324
|
while (true) {
|
|
1942
|
-
const rr = evalStatement(
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
2325
|
+
const rr = evalStatement(rep.block, env, sf, returnOnReturn);
|
|
2326
|
+
if (isPromise(rr)) {
|
|
2327
|
+
return (rr as Promise<any>).then((res) => {
|
|
2328
|
+
if (res !== undefined) {
|
|
2329
|
+
return isBreakSignal(res) ? undefined : res;
|
|
2330
|
+
}
|
|
2331
|
+
return rpThen(evalExpression(rep.condition, env, sf), (cv) =>
|
|
2332
|
+
luaTruthy(cv) ? undefined : runSyncFirst(),
|
|
2333
|
+
);
|
|
2334
|
+
});
|
|
1949
2335
|
}
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
2336
|
+
if (rr !== undefined) {
|
|
2337
|
+
if (isBreakSignal(rr)) return;
|
|
2338
|
+
return rr;
|
|
1953
2339
|
}
|
|
1954
|
-
}
|
|
1955
|
-
return;
|
|
1956
|
-
};
|
|
1957
2340
|
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
if (isBreakSignal(res)) {
|
|
1964
|
-
return;
|
|
1965
|
-
}
|
|
1966
|
-
return res;
|
|
1967
|
-
}
|
|
1968
|
-
return runAsync();
|
|
1969
|
-
});
|
|
1970
|
-
}
|
|
1971
|
-
if (rr !== undefined) {
|
|
1972
|
-
if (isBreakSignal(rr)) {
|
|
1973
|
-
return;
|
|
2341
|
+
const c = evalExpression(rep.condition, env, sf);
|
|
2342
|
+
if (isPromise(c)) {
|
|
2343
|
+
return (c as Promise<any>).then((cv) =>
|
|
2344
|
+
luaTruthy(cv) ? undefined : runSyncFirst(),
|
|
2345
|
+
);
|
|
1974
2346
|
}
|
|
1975
|
-
|
|
2347
|
+
if (luaTruthy(c)) break;
|
|
1976
2348
|
}
|
|
2349
|
+
return;
|
|
2350
|
+
};
|
|
1977
2351
|
|
|
1978
|
-
|
|
1979
|
-
if (isPromise(c)) {
|
|
1980
|
-
return (c as Promise<any>).then((cv) =>
|
|
1981
|
-
luaTruthy(cv) ? undefined : runAsync()
|
|
1982
|
-
);
|
|
1983
|
-
}
|
|
1984
|
-
if (luaTruthy(c)) {
|
|
1985
|
-
break;
|
|
1986
|
-
}
|
|
1987
|
-
}
|
|
1988
|
-
return;
|
|
2352
|
+
return runSyncFirst();
|
|
1989
2353
|
}
|
|
1990
2354
|
case "Break": {
|
|
1991
2355
|
return { ctrl: "break" };
|
|
@@ -2005,7 +2369,7 @@ export function evalStatement(
|
|
|
2005
2369
|
if (fn.name.colonName) {
|
|
2006
2370
|
// function hello:there() -> function hello.there(self) transformation
|
|
2007
2371
|
body = {
|
|
2008
|
-
...
|
|
2372
|
+
...fn.body,
|
|
2009
2373
|
parameters: ["self", ...fn.body.parameters],
|
|
2010
2374
|
};
|
|
2011
2375
|
propNames = [...fn.name.propNames, fn.name.colonName];
|
|
@@ -2028,17 +2392,14 @@ export function evalStatement(
|
|
|
2028
2392
|
}
|
|
2029
2393
|
case "LocalFunction": {
|
|
2030
2394
|
const lf = asLocalFunction(s);
|
|
2031
|
-
env.setLocal(
|
|
2032
|
-
lf.name,
|
|
2033
|
-
new LuaFunction(lf.body, env),
|
|
2034
|
-
);
|
|
2395
|
+
env.setLocal(lf.name, new LuaFunction(lf.body, env));
|
|
2035
2396
|
return;
|
|
2036
2397
|
}
|
|
2037
2398
|
case "Return": {
|
|
2038
2399
|
const ret = asReturn(s);
|
|
2039
2400
|
|
|
2040
2401
|
const parts = ret.expressions.map((value: LuaExpression) =>
|
|
2041
|
-
evalExpression(value, env, sf)
|
|
2402
|
+
evalExpression(value, env, sf),
|
|
2042
2403
|
);
|
|
2043
2404
|
const valuesRP = rpAll(parts);
|
|
2044
2405
|
|
|
@@ -2086,9 +2447,7 @@ export function evalStatement(
|
|
|
2086
2447
|
const determineLoopType = (): NumericType => {
|
|
2087
2448
|
const startType = astNumberKind(fr.start);
|
|
2088
2449
|
const stepType = fr.step ? astNumberKind(fr.step) : "int";
|
|
2089
|
-
return
|
|
2090
|
-
? "float"
|
|
2091
|
-
: "int";
|
|
2450
|
+
return startType === "float" || stepType === "float" ? "float" : "int";
|
|
2092
2451
|
};
|
|
2093
2452
|
|
|
2094
2453
|
const wrapLoopVar = (i: number, loopType: NumericType) => {
|
|
@@ -2098,53 +2457,59 @@ export function evalStatement(
|
|
|
2098
2457
|
return i;
|
|
2099
2458
|
};
|
|
2100
2459
|
|
|
2101
|
-
const canReuseEnv =
|
|
2102
|
-
fr.capturesLoopVar === false;
|
|
2460
|
+
const canReuseEnv =
|
|
2461
|
+
!fr.block.hasFunctionDef || fr.capturesLoopVar === false;
|
|
2103
2462
|
|
|
2104
2463
|
const executeIteration = canReuseEnv
|
|
2105
2464
|
? (
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2465
|
+
loopEnv: LuaEnv,
|
|
2466
|
+
i: number,
|
|
2467
|
+
loopType: NumericType,
|
|
2468
|
+
): undefined | ControlSignal | Promise<undefined | ControlSignal> => {
|
|
2469
|
+
loopEnv.setLocal(fr.name, wrapLoopVar(i, loopType));
|
|
2470
|
+
return evalStatement(fr.block, loopEnv, sf, returnOnReturn);
|
|
2471
|
+
}
|
|
2113
2472
|
: (
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2473
|
+
_loopEnv: LuaEnv,
|
|
2474
|
+
i: number,
|
|
2475
|
+
loopType: NumericType,
|
|
2476
|
+
): undefined | ControlSignal | Promise<undefined | ControlSignal> => {
|
|
2477
|
+
const localEnv = new LuaEnv(env);
|
|
2478
|
+
localEnv.setLocal(fr.name, wrapLoopVar(i, loopType));
|
|
2479
|
+
return evalStatement(fr.block, localEnv, sf, returnOnReturn);
|
|
2480
|
+
};
|
|
2122
2481
|
|
|
2123
|
-
|
|
2482
|
+
// Continuation that re-enters sync mode after each async iteration
|
|
2483
|
+
const runFromIndex = (
|
|
2124
2484
|
loopEnv: LuaEnv,
|
|
2125
2485
|
end: number,
|
|
2126
2486
|
step: number,
|
|
2127
2487
|
startIndex: number,
|
|
2128
2488
|
loopType: NumericType,
|
|
2129
|
-
) => {
|
|
2489
|
+
): undefined | ControlSignal | Promise<undefined | ControlSignal> => {
|
|
2130
2490
|
if (step === 0) {
|
|
2131
2491
|
throw new LuaRuntimeError("'for' step is zero", sf.withCtx(fr.ctx));
|
|
2132
2492
|
}
|
|
2133
2493
|
|
|
2134
|
-
const shouldContinue =
|
|
2135
|
-
? (i: number) => i <= end
|
|
2136
|
-
: (i: number) => i >= end;
|
|
2494
|
+
const shouldContinue =
|
|
2495
|
+
step > 0 ? (i: number) => i <= end : (i: number) => i >= end;
|
|
2137
2496
|
|
|
2138
2497
|
for (let i = startIndex; shouldContinue(i); i += step) {
|
|
2139
2498
|
const r = executeIteration(loopEnv, i, loopType);
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2499
|
+
if (isPromise(r)) {
|
|
2500
|
+
return (r as Promise<any>).then((res) => {
|
|
2501
|
+
if (res !== undefined) {
|
|
2502
|
+
return isBreakSignal(res) ? undefined : res;
|
|
2503
|
+
}
|
|
2504
|
+
return runFromIndex(loopEnv, end, step, i + step, loopType);
|
|
2505
|
+
});
|
|
2506
|
+
}
|
|
2507
|
+
if (r !== undefined) {
|
|
2508
|
+
if (isBreakSignal(r)) return;
|
|
2509
|
+
return r;
|
|
2146
2510
|
}
|
|
2147
2511
|
}
|
|
2512
|
+
return;
|
|
2148
2513
|
};
|
|
2149
2514
|
|
|
2150
2515
|
const runSyncFirst = (
|
|
@@ -2152,17 +2517,13 @@ export function evalStatement(
|
|
|
2152
2517
|
end: number,
|
|
2153
2518
|
step: number,
|
|
2154
2519
|
loopType: NumericType,
|
|
2155
|
-
):
|
|
2156
|
-
| undefined
|
|
2157
|
-
| ControlSignal
|
|
2158
|
-
| Promise<undefined | ControlSignal> => {
|
|
2520
|
+
): undefined | ControlSignal | Promise<undefined | ControlSignal> => {
|
|
2159
2521
|
if (step === 0) {
|
|
2160
2522
|
throw new LuaRuntimeError("'for' step is zero", sf.withCtx(fr.ctx));
|
|
2161
2523
|
}
|
|
2162
2524
|
|
|
2163
|
-
const shouldContinue =
|
|
2164
|
-
? (i: number) => i <= end
|
|
2165
|
-
: (i: number) => i >= end;
|
|
2525
|
+
const shouldContinue =
|
|
2526
|
+
step > 0 ? (i: number) => i <= end : (i: number) => i >= end;
|
|
2166
2527
|
|
|
2167
2528
|
const loopEnv = new LuaEnv(env);
|
|
2168
2529
|
|
|
@@ -2176,7 +2537,7 @@ export function evalStatement(
|
|
|
2176
2537
|
}
|
|
2177
2538
|
return res;
|
|
2178
2539
|
}
|
|
2179
|
-
return
|
|
2540
|
+
return runFromIndex(loopEnv, end, step, i + step, loopType);
|
|
2180
2541
|
});
|
|
2181
2542
|
}
|
|
2182
2543
|
if (r !== undefined) {
|
|
@@ -2191,11 +2552,7 @@ export function evalStatement(
|
|
|
2191
2552
|
|
|
2192
2553
|
const loopType = determineLoopType();
|
|
2193
2554
|
|
|
2194
|
-
if (
|
|
2195
|
-
!isPromise(startV) &&
|
|
2196
|
-
!isPromise(endV) &&
|
|
2197
|
-
!isPromise(stepV)
|
|
2198
|
-
) {
|
|
2555
|
+
if (!isPromise(startV) && !isPromise(endV) && !isPromise(stepV)) {
|
|
2199
2556
|
return runSyncFirst(
|
|
2200
2557
|
untagNumber(startV) as number,
|
|
2201
2558
|
untagNumber(endV) as number,
|
|
@@ -2222,8 +2579,8 @@ export function evalStatement(
|
|
|
2222
2579
|
fi.expressions.map((e: LuaExpression) => evalExpression(e, env, sf)),
|
|
2223
2580
|
);
|
|
2224
2581
|
|
|
2225
|
-
const canReuseEnv =
|
|
2226
|
-
fi.capturesLoopVar === false;
|
|
2582
|
+
const canReuseEnv =
|
|
2583
|
+
!fi.block.hasFunctionDef || fi.capturesLoopVar === false;
|
|
2227
2584
|
const setIterVars = (
|
|
2228
2585
|
localEnv: LuaEnv,
|
|
2229
2586
|
names: string[],
|
|
@@ -2294,39 +2651,8 @@ export function evalStatement(
|
|
|
2294
2651
|
return new LuaEnv(env);
|
|
2295
2652
|
};
|
|
2296
2653
|
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
while (true) {
|
|
2300
|
-
const callRes = luaCall(
|
|
2301
|
-
iteratorValue,
|
|
2302
|
-
[state, control],
|
|
2303
|
-
fi.ctx,
|
|
2304
|
-
sf,
|
|
2305
|
-
);
|
|
2306
|
-
const iterResult = new LuaMultiRes(
|
|
2307
|
-
isPromise(callRes) ? await callRes : callRes,
|
|
2308
|
-
).flatten();
|
|
2309
|
-
const nextControl = iterResult.values[0];
|
|
2310
|
-
if (nextControl === null || nextControl === undefined) {
|
|
2311
|
-
break;
|
|
2312
|
-
}
|
|
2313
|
-
control = nextControl;
|
|
2314
|
-
|
|
2315
|
-
const localEnv = makeIterEnv();
|
|
2316
|
-
setIterVars(localEnv, fi.names, iterResult.values);
|
|
2317
|
-
|
|
2318
|
-
const r = evalStatement(fi.block, localEnv, sf, returnOnReturn);
|
|
2319
|
-
const res = isPromise(r) ? await r : r;
|
|
2320
|
-
if (res !== undefined) {
|
|
2321
|
-
if (isBreakSignal(res)) {
|
|
2322
|
-
break;
|
|
2323
|
-
}
|
|
2324
|
-
return await finish(res);
|
|
2325
|
-
}
|
|
2326
|
-
}
|
|
2327
|
-
return await finish(undefined);
|
|
2328
|
-
};
|
|
2329
|
-
|
|
2654
|
+
// Sync-first loop that re-enters sync mode after each async iteration
|
|
2655
|
+
const runSyncFirst = (): any => {
|
|
2330
2656
|
while (true) {
|
|
2331
2657
|
const iterCall = luaCall(
|
|
2332
2658
|
iteratorValue,
|
|
@@ -2334,55 +2660,40 @@ export function evalStatement(
|
|
|
2334
2660
|
fi.ctx,
|
|
2335
2661
|
sf,
|
|
2336
2662
|
);
|
|
2337
|
-
if (isPromise(iterCall)) {
|
|
2338
|
-
return (iterCall as Promise<any>).then((itv) => {
|
|
2339
|
-
const iterResult = new LuaMultiRes(itv).flatten();
|
|
2340
|
-
const nextControl = iterResult.values[0];
|
|
2341
|
-
if (nextControl === null || nextControl === undefined) {
|
|
2342
|
-
const r = finish(undefined);
|
|
2343
|
-
if (isPromise(r)) return (r as Promise<void>).then(() => {});
|
|
2344
|
-
return;
|
|
2345
|
-
}
|
|
2346
|
-
control = nextControl;
|
|
2347
2663
|
|
|
2348
|
-
|
|
2349
|
-
|
|
2664
|
+
const afterIterCall = (itv: any): any => {
|
|
2665
|
+
const iterResult = new LuaMultiRes(itv).flatten();
|
|
2666
|
+
const nextControl = iterResult.values[0];
|
|
2667
|
+
if (nextControl === null || nextControl === undefined) {
|
|
2668
|
+
return finish(undefined);
|
|
2669
|
+
}
|
|
2670
|
+
control = nextControl;
|
|
2350
2671
|
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
)
|
|
2357
|
-
|
|
2358
|
-
return (r as Promise<any>).then((res) => {
|
|
2359
|
-
if (res !== undefined) {
|
|
2360
|
-
if (isBreakSignal(res)) {
|
|
2361
|
-
return finish(undefined);
|
|
2362
|
-
}
|
|
2363
|
-
return rpThen(finish(undefined), () => res);
|
|
2364
|
-
}
|
|
2365
|
-
return runAsync();
|
|
2366
|
-
});
|
|
2367
|
-
}
|
|
2368
|
-
if (r !== undefined) {
|
|
2369
|
-
if (isBreakSignal(r)) {
|
|
2672
|
+
const localEnv = makeIterEnv();
|
|
2673
|
+
setIterVars(localEnv, fi.names, iterResult.values);
|
|
2674
|
+
|
|
2675
|
+
const r = evalStatement(fi.block, localEnv, sf, returnOnReturn);
|
|
2676
|
+
return rpThen(r, (res) => {
|
|
2677
|
+
if (res !== undefined) {
|
|
2678
|
+
if (isBreakSignal(res)) {
|
|
2370
2679
|
return finish(undefined);
|
|
2371
2680
|
}
|
|
2372
|
-
return rpThen(finish(undefined), () =>
|
|
2681
|
+
return rpThen(finish(undefined), () => res);
|
|
2373
2682
|
}
|
|
2374
|
-
return
|
|
2375
|
-
})
|
|
2683
|
+
return runSyncFirst();
|
|
2684
|
+
});
|
|
2685
|
+
};
|
|
2686
|
+
|
|
2687
|
+
if (isPromise(iterCall)) {
|
|
2688
|
+
return (iterCall as Promise<any>)
|
|
2689
|
+
.then(afterIterCall)
|
|
2690
|
+
.catch((e: any) => finishErr(e));
|
|
2376
2691
|
}
|
|
2377
2692
|
|
|
2378
2693
|
const iterResult = new LuaMultiRes(iterCall).flatten();
|
|
2379
2694
|
const nextControl = iterResult.values[0];
|
|
2380
2695
|
if (nextControl === null || nextControl === undefined) {
|
|
2381
|
-
|
|
2382
|
-
if (isPromise(r)) {
|
|
2383
|
-
return (r as Promise<void>);
|
|
2384
|
-
}
|
|
2385
|
-
return;
|
|
2696
|
+
return finish(undefined);
|
|
2386
2697
|
}
|
|
2387
2698
|
control = nextControl;
|
|
2388
2699
|
|
|
@@ -2391,15 +2702,17 @@ export function evalStatement(
|
|
|
2391
2702
|
|
|
2392
2703
|
const r = evalStatement(fi.block, localEnv, sf, returnOnReturn);
|
|
2393
2704
|
if (isPromise(r)) {
|
|
2394
|
-
return (r as Promise<any>)
|
|
2395
|
-
|
|
2396
|
-
if (
|
|
2397
|
-
|
|
2705
|
+
return (r as Promise<any>)
|
|
2706
|
+
.then((res) => {
|
|
2707
|
+
if (res !== undefined) {
|
|
2708
|
+
if (isBreakSignal(res)) {
|
|
2709
|
+
return finish(undefined);
|
|
2710
|
+
}
|
|
2711
|
+
return rpThen(finish(undefined), () => res);
|
|
2398
2712
|
}
|
|
2399
|
-
return
|
|
2400
|
-
}
|
|
2401
|
-
|
|
2402
|
-
}).catch((e: any) => finishErr(e));
|
|
2713
|
+
return runSyncFirst();
|
|
2714
|
+
})
|
|
2715
|
+
.catch((e: any) => finishErr(e));
|
|
2403
2716
|
}
|
|
2404
2717
|
if (r !== undefined) {
|
|
2405
2718
|
if (isBreakSignal(r)) {
|
|
@@ -2408,6 +2721,10 @@ export function evalStatement(
|
|
|
2408
2721
|
return rpThen(finish(undefined), () => r);
|
|
2409
2722
|
}
|
|
2410
2723
|
}
|
|
2724
|
+
};
|
|
2725
|
+
|
|
2726
|
+
try {
|
|
2727
|
+
return runSyncFirst();
|
|
2411
2728
|
} catch (e: any) {
|
|
2412
2729
|
return finishErr(e);
|
|
2413
2730
|
}
|
|
@@ -2436,16 +2753,9 @@ function evalLValue(
|
|
|
2436
2753
|
}
|
|
2437
2754
|
case "TableAccess": {
|
|
2438
2755
|
const ta = asLValueTableAccess(lval);
|
|
2439
|
-
const objValue = evalExpression(
|
|
2440
|
-
ta.object,
|
|
2441
|
-
env,
|
|
2442
|
-
sf,
|
|
2443
|
-
);
|
|
2756
|
+
const objValue = evalExpression(ta.object, env, sf);
|
|
2444
2757
|
const keyValue = evalExpression(ta.key, env, sf);
|
|
2445
|
-
if (
|
|
2446
|
-
isPromise(objValue) ||
|
|
2447
|
-
isPromise(keyValue)
|
|
2448
|
-
) {
|
|
2758
|
+
if (isPromise(objValue) || isPromise(keyValue)) {
|
|
2449
2759
|
return Promise.all([
|
|
2450
2760
|
isPromise(objValue) ? objValue : Promise.resolve(objValue),
|
|
2451
2761
|
isPromise(keyValue) ? keyValue : Promise.resolve(keyValue),
|
|
@@ -2461,11 +2771,7 @@ function evalLValue(
|
|
|
2461
2771
|
}
|
|
2462
2772
|
case "PropertyAccess": {
|
|
2463
2773
|
const pa = asLValuePropertyAccess(lval);
|
|
2464
|
-
const objValue = evalExpression(
|
|
2465
|
-
pa.object,
|
|
2466
|
-
env,
|
|
2467
|
-
sf,
|
|
2468
|
-
);
|
|
2774
|
+
const objValue = evalExpression(pa.object, env, sf);
|
|
2469
2775
|
if (isPromise(objValue)) {
|
|
2470
2776
|
return (objValue as Promise<any>).then((ov) => {
|
|
2471
2777
|
return {
|