@silverbulletmd/silverbullet 2.4.2 → 2.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -4
- package/client/asset_bundle/bundle.ts +3 -9
- package/client/data/datastore.ts +4 -5
- package/client/markdown_parser/constants.ts +5 -4
- package/client/plugos/hooks/code_widget.ts +3 -8
- package/client/plugos/hooks/command.ts +8 -8
- package/client/plugos/hooks/document_editor.ts +10 -15
- package/client/plugos/hooks/event.ts +33 -36
- package/client/plugos/hooks/mq.ts +17 -17
- package/client/plugos/hooks/plug_namespace.ts +3 -8
- package/client/plugos/hooks/slash_command.ts +13 -28
- package/client/plugos/hooks/syscall.ts +3 -3
- package/client/plugos/manifest_cache.ts +22 -15
- package/client/plugos/plug.ts +2 -6
- package/client/plugos/plug_compile.ts +79 -78
- package/client/plugos/protocol.ts +28 -28
- package/client/plugos/proxy_fetch.ts +7 -6
- package/client/plugos/sandboxes/web_worker_sandbox.ts +1 -1
- package/client/plugos/sandboxes/worker_sandbox.ts +18 -18
- package/client/plugos/syscalls/asset.ts +1 -3
- package/client/plugos/syscalls/code_widget.ts +1 -3
- package/client/plugos/syscalls/config.ts +1 -5
- package/client/plugos/syscalls/datastore.ts +1 -1
- package/client/plugos/syscalls/editor.ts +72 -69
- package/client/plugos/syscalls/event.ts +9 -12
- package/client/plugos/syscalls/fetch.ts +31 -23
- package/client/plugos/syscalls/index.ts +10 -1
- package/client/plugos/syscalls/jsonschema.ts +72 -32
- package/client/plugos/syscalls/language.ts +9 -5
- package/client/plugos/syscalls/markdown.ts +29 -7
- package/client/plugos/syscalls/mq.ts +4 -12
- package/client/plugos/syscalls/service_registry.ts +1 -4
- package/client/plugos/syscalls/shell.ts +2 -5
- package/client/plugos/syscalls/space.ts +1 -1
- package/client/plugos/syscalls/sync.ts +69 -60
- package/client/plugos/syscalls/system.ts +2 -3
- package/client/plugos/system.ts +6 -12
- package/client/plugos/worker_runtime.ts +12 -33
- package/client/space_lua/aggregates.ts +782 -0
- package/client/space_lua/ast.ts +42 -8
- package/client/space_lua/ast_narrow.ts +4 -2
- package/client/space_lua/eval.ts +886 -575
- package/client/space_lua/labels.ts +7 -12
- package/client/space_lua/liq_null.ts +6 -0
- package/client/space_lua/numeric.ts +5 -8
- package/client/space_lua/parse.ts +346 -120
- package/client/space_lua/query_collection.ts +926 -82
- package/client/space_lua/query_env.ts +26 -0
- package/client/space_lua/render_lua_markdown.ts +369 -0
- package/client/space_lua/rp.ts +5 -4
- package/client/space_lua/runtime.ts +288 -155
- package/client/space_lua/stdlib/format.ts +53 -39
- package/client/space_lua/stdlib/js.ts +3 -7
- package/client/space_lua/stdlib/load.ts +1 -3
- package/client/space_lua/stdlib/math.ts +84 -58
- package/client/space_lua/stdlib/net.ts +27 -17
- package/client/space_lua/stdlib/os.ts +81 -85
- package/client/space_lua/stdlib/pattern.ts +695 -0
- package/client/space_lua/stdlib/prng.ts +148 -0
- package/client/space_lua/stdlib/space_lua.ts +17 -23
- package/client/space_lua/stdlib/string.ts +102 -190
- package/client/space_lua/stdlib/string_pack.ts +490 -0
- package/client/space_lua/stdlib/table.ts +76 -16
- package/client/space_lua/stdlib.ts +53 -39
- package/client/space_lua/tonumber.ts +82 -42
- package/client/space_lua/util.ts +53 -15
- package/dist/plug-compile.js +55 -98
- package/package.json +27 -20
- package/plug-api/constants.ts +0 -32
- package/plug-api/lib/async.ts +20 -7
- package/plug-api/lib/crypto.ts +16 -17
- package/plug-api/lib/dates.ts +15 -7
- package/plug-api/lib/json.ts +11 -5
- package/plug-api/lib/limited_map.ts +1 -1
- package/plug-api/lib/native_fetch.ts +2 -0
- package/plug-api/lib/ref.ts +23 -23
- package/plug-api/lib/resolve.ts +7 -11
- package/plug-api/lib/tags.ts +13 -4
- package/plug-api/lib/transclusion.ts +10 -21
- package/plug-api/lib/tree.ts +165 -45
- package/plug-api/lib/yaml.ts +35 -25
- package/plug-api/syscalls/asset.ts +1 -1
- package/plug-api/syscalls/config.ts +1 -4
- package/plug-api/syscalls/editor.ts +15 -15
- package/plug-api/syscalls/jsonschema.ts +1 -3
- package/plug-api/syscalls/lua.ts +3 -9
- package/plug-api/syscalls/mq.ts +1 -4
- package/plug-api/syscalls/shell.ts +4 -1
- package/plug-api/syscalls/space.ts +3 -10
- package/plug-api/syscalls/system.ts +1 -4
- package/plug-api/syscalls/yaml.ts +2 -6
- package/plug-api/system_mock.ts +0 -1
- package/plug-api/types/client.ts +16 -1
- package/plug-api/types/event.ts +6 -4
- package/plug-api/types/manifest.ts +8 -9
- package/plugs/builtin_plugs.ts +2 -2
- package/client/plugos/sandboxes/deno_worker_sandbox.ts +0 -6
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";
|
|
@@ -19,6 +20,7 @@ import {
|
|
|
19
20
|
luaEnsureCloseStack,
|
|
20
21
|
LuaEnv,
|
|
21
22
|
luaEquals,
|
|
23
|
+
luaFormatNumber,
|
|
22
24
|
LuaFunction,
|
|
23
25
|
luaGet,
|
|
24
26
|
luaIndexValue,
|
|
@@ -38,8 +40,9 @@ import {
|
|
|
38
40
|
singleResult,
|
|
39
41
|
} from "./runtime.ts";
|
|
40
42
|
import {
|
|
41
|
-
ArrayQueryCollection,
|
|
42
43
|
type LuaCollectionQuery,
|
|
44
|
+
type LuaGroupByEntry,
|
|
45
|
+
toCollection,
|
|
43
46
|
} from "./query_collection.ts";
|
|
44
47
|
import {
|
|
45
48
|
coerceNumericPair,
|
|
@@ -107,8 +110,14 @@ function astNumberKind(e: LuaExpression | undefined): NumericType | undefined {
|
|
|
107
110
|
result = unwrapped.numericType === "int" ? "int" : "float";
|
|
108
111
|
} else if (unwrapped.type === "Binary") {
|
|
109
112
|
const op = unwrapped.operator;
|
|
110
|
-
const numericOp =
|
|
111
|
-
op === "
|
|
113
|
+
const numericOp =
|
|
114
|
+
op === "+" ||
|
|
115
|
+
op === "-" ||
|
|
116
|
+
op === "*" ||
|
|
117
|
+
op === "/" ||
|
|
118
|
+
op === "//" ||
|
|
119
|
+
op === "%" ||
|
|
120
|
+
op === "^";
|
|
112
121
|
|
|
113
122
|
if (numericOp) {
|
|
114
123
|
const lk = astNumberKind(unwrapped.left);
|
|
@@ -178,18 +187,6 @@ function blockMetaOrThrow(
|
|
|
178
187
|
}
|
|
179
188
|
}
|
|
180
189
|
|
|
181
|
-
// Queryable guard to avoid `(collection as any).query` usage
|
|
182
|
-
type Queryable = {
|
|
183
|
-
query: (
|
|
184
|
-
q: LuaCollectionQuery,
|
|
185
|
-
env: LuaEnv,
|
|
186
|
-
sf: LuaStackFrame,
|
|
187
|
-
) => Promise<any>;
|
|
188
|
-
};
|
|
189
|
-
function isQueryable(x: unknown): x is Queryable {
|
|
190
|
-
return !!x && typeof (x as any).query === "function";
|
|
191
|
-
}
|
|
192
|
-
|
|
193
190
|
function arithVerbFromOperator(op: string): string | null {
|
|
194
191
|
switch (op) {
|
|
195
192
|
case "+":
|
|
@@ -250,7 +247,7 @@ function arithCoercionErrorOrThrow(
|
|
|
250
247
|
throw e;
|
|
251
248
|
}
|
|
252
249
|
|
|
253
|
-
function luaOp(
|
|
250
|
+
export function luaOp(
|
|
254
251
|
op: string,
|
|
255
252
|
left: any,
|
|
256
253
|
right: any,
|
|
@@ -260,67 +257,68 @@ function luaOp(
|
|
|
260
257
|
sf: LuaStackFrame,
|
|
261
258
|
): any {
|
|
262
259
|
switch (op) {
|
|
263
|
-
case "+":
|
|
264
|
-
|
|
265
|
-
|
|
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
|
+
}
|
|
266
298
|
case "/":
|
|
267
299
|
case "^": {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
let result = ar.f(l, r);
|
|
279
|
-
|
|
280
|
-
if (
|
|
281
|
-
ar.special === "sub" &&
|
|
282
|
-
result === 0 &&
|
|
283
|
-
isNegativeZero(result) &&
|
|
284
|
-
resultType === "float"
|
|
285
|
-
) {
|
|
286
|
-
const rhsIsIntZero = r === 0 && rightType === "int";
|
|
287
|
-
if (rhsIsIntZero) {
|
|
288
|
-
result = 0;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
const normalized = normalizeArithmeticResult(result, resultType);
|
|
293
|
-
|
|
294
|
-
// Operators `/` and `^` produce float, wrap only if needed.
|
|
295
|
-
if (op === "/" || op === "^") {
|
|
296
|
-
if (normalized === 0) {
|
|
297
|
-
return makeLuaZero(normalized, "float");
|
|
298
|
-
}
|
|
299
|
-
if (!Number.isFinite(normalized)) {
|
|
300
|
-
return normalized;
|
|
301
|
-
}
|
|
302
|
-
if (!Number.isInteger(normalized)) {
|
|
303
|
-
return normalized;
|
|
304
|
-
}
|
|
305
|
-
return makeLuaFloat(normalized);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
if (normalized === 0) {
|
|
309
|
-
return makeLuaZero(normalized, resultType);
|
|
310
|
-
}
|
|
311
|
-
if (resultType === "float" && Number.isInteger(normalized)) {
|
|
312
|
-
return makeLuaFloat(normalized);
|
|
313
|
-
}
|
|
314
|
-
return normalized;
|
|
315
|
-
} catch (e: any) {
|
|
316
|
-
const meta = evalMetamethod(left, right, ar.metaMethod, ctx, sf);
|
|
317
|
-
if (meta !== undefined) {
|
|
318
|
-
return meta;
|
|
319
|
-
}
|
|
320
|
-
return arithCoercionErrorOrThrow(op, left, right, ctx, sf, e);
|
|
321
|
-
}
|
|
300
|
+
return luaArithGeneric(
|
|
301
|
+
op as NumericArithOp,
|
|
302
|
+
left,
|
|
303
|
+
right,
|
|
304
|
+
leftType,
|
|
305
|
+
rightType,
|
|
306
|
+
ctx,
|
|
307
|
+
sf,
|
|
308
|
+
);
|
|
322
309
|
}
|
|
323
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
|
+
}
|
|
324
322
|
try {
|
|
325
323
|
const coerce = (v: any): string => {
|
|
326
324
|
if (v === null || v === undefined) {
|
|
@@ -333,10 +331,10 @@ function luaOp(
|
|
|
333
331
|
return v as string;
|
|
334
332
|
}
|
|
335
333
|
if (typeof v === "number") {
|
|
336
|
-
return
|
|
334
|
+
return luaFormatNumber(v);
|
|
337
335
|
}
|
|
338
336
|
if (isTaggedFloat(v)) {
|
|
339
|
-
return
|
|
337
|
+
return luaFormatNumber(v.value, "float");
|
|
340
338
|
}
|
|
341
339
|
const t = luaTypeName(v);
|
|
342
340
|
throw new LuaRuntimeError(
|
|
@@ -354,27 +352,54 @@ function luaOp(
|
|
|
354
352
|
}
|
|
355
353
|
}
|
|
356
354
|
case "==": {
|
|
355
|
+
// Fast path for same-type primitives
|
|
356
|
+
if (typeof left === typeof right && typeof left !== "object") {
|
|
357
|
+
return left === right;
|
|
358
|
+
}
|
|
357
359
|
if (luaEquals(left, right)) return true;
|
|
358
360
|
return luaEqWithMetamethod(left, right, ctx, sf);
|
|
359
361
|
}
|
|
360
362
|
case "~=":
|
|
361
363
|
case "!=": {
|
|
364
|
+
if (typeof left === typeof right && typeof left !== "object") {
|
|
365
|
+
return left !== right;
|
|
366
|
+
}
|
|
362
367
|
if (luaEquals(left, right)) {
|
|
363
368
|
return false;
|
|
364
369
|
}
|
|
365
370
|
return !luaEqWithMetamethod(left, right, ctx, sf);
|
|
366
371
|
}
|
|
367
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
|
+
}
|
|
368
381
|
return luaRelWithMetamethod("<", left, right, ctx, sf);
|
|
369
382
|
}
|
|
370
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;
|
|
371
388
|
return luaRelWithMetamethod("<=", left, right, ctx, sf);
|
|
372
389
|
}
|
|
373
390
|
// Lua: `a>b` is `b<a`, `a>=b` is `b<=a`
|
|
374
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;
|
|
375
396
|
return luaRelWithMetamethod("<", right, left, ctx, sf);
|
|
376
397
|
}
|
|
377
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;
|
|
378
403
|
return luaRelWithMetamethod("<=", right, left, ctx, sf);
|
|
379
404
|
}
|
|
380
405
|
}
|
|
@@ -405,13 +430,79 @@ function luaOp(
|
|
|
405
430
|
}
|
|
406
431
|
}
|
|
407
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
|
+
|
|
408
496
|
type NumericArithOp = "+" | "-" | "*" | "/" | "^";
|
|
409
497
|
|
|
410
|
-
const numericArith: Record<
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
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
|
+
> = {
|
|
415
506
|
"+": { metaMethod: "__add", f: (l, r) => l + r },
|
|
416
507
|
"-": { metaMethod: "__sub", f: (l, r) => l - r, special: "sub" },
|
|
417
508
|
"*": { metaMethod: "__mul", f: (l, r) => l * r },
|
|
@@ -461,10 +552,7 @@ function luaFloorDiv(
|
|
|
461
552
|
);
|
|
462
553
|
|
|
463
554
|
if (resultType === "int" && right === 0) {
|
|
464
|
-
throw new LuaRuntimeError(
|
|
465
|
-
`attempt to divide by zero`,
|
|
466
|
-
sf.withCtx(ctx),
|
|
467
|
-
);
|
|
555
|
+
throw new LuaRuntimeError(`attempt to divide by zero`, sf.withCtx(ctx));
|
|
468
556
|
}
|
|
469
557
|
|
|
470
558
|
const result = Math.floor(left / right);
|
|
@@ -495,10 +583,7 @@ function luaMod(
|
|
|
495
583
|
);
|
|
496
584
|
|
|
497
585
|
if (resultType === "int" && right === 0) {
|
|
498
|
-
throw new LuaRuntimeError(
|
|
499
|
-
`attempt to perform 'n%0'`,
|
|
500
|
-
sf.withCtx(ctx),
|
|
501
|
-
);
|
|
586
|
+
throw new LuaRuntimeError(`attempt to perform 'n%0'`, sf.withCtx(ctx));
|
|
502
587
|
}
|
|
503
588
|
|
|
504
589
|
const q = Math.floor(left / right);
|
|
@@ -519,10 +604,7 @@ function luaMod(
|
|
|
519
604
|
return normalized;
|
|
520
605
|
}
|
|
521
606
|
|
|
522
|
-
function luaUnaryMinus(
|
|
523
|
-
v: number,
|
|
524
|
-
numType: NumericType | undefined,
|
|
525
|
-
): number {
|
|
607
|
+
function luaUnaryMinus(v: number, numType: NumericType | undefined): number {
|
|
526
608
|
const vType = numType ?? inferNumericType(v);
|
|
527
609
|
|
|
528
610
|
if (v === 0 && vType === "int") {
|
|
@@ -536,17 +618,20 @@ function luaUnaryMinus(
|
|
|
536
618
|
return -v;
|
|
537
619
|
}
|
|
538
620
|
|
|
539
|
-
const operatorsMetaMethods: Record<
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
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
|
+
> = {
|
|
550
635
|
"//": {
|
|
551
636
|
metaMethod: "__idiv",
|
|
552
637
|
nativeImplementation: (a, b, lt, rt, ctx, sf) =>
|
|
@@ -609,6 +694,168 @@ const operatorsMetaMethods: Record<string, {
|
|
|
609
694
|
},
|
|
610
695
|
};
|
|
611
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
|
+
|
|
612
859
|
export function evalExpression(
|
|
613
860
|
e: LuaExpression,
|
|
614
861
|
env: LuaEnv,
|
|
@@ -642,14 +889,7 @@ export function evalExpression(
|
|
|
642
889
|
if (b.operator === "and") {
|
|
643
890
|
return evalLogical("and", b.left, b.right, env, sf);
|
|
644
891
|
}
|
|
645
|
-
return evalBinaryWithLR(
|
|
646
|
-
b.operator,
|
|
647
|
-
b.left,
|
|
648
|
-
b.right,
|
|
649
|
-
b.ctx,
|
|
650
|
-
env,
|
|
651
|
-
sf,
|
|
652
|
-
);
|
|
892
|
+
return evalBinaryWithLR(b.operator, b.left, b.right, b.ctx, env, sf);
|
|
653
893
|
}
|
|
654
894
|
case "Unary": {
|
|
655
895
|
const u = asUnary(e);
|
|
@@ -673,59 +913,53 @@ export function evalExpression(
|
|
|
673
913
|
const applyTyped = (typed: TypedValue) => {
|
|
674
914
|
const arg = singleResult(typed.value);
|
|
675
915
|
|
|
676
|
-
return unaryWithMeta(
|
|
677
|
-
|
|
678
|
-
"
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
() => {
|
|
682
|
-
// Numeric-string coercion for unary minus
|
|
683
|
-
if (typeof arg === "string") {
|
|
684
|
-
const n = coerceToNumber(arg);
|
|
685
|
-
if (n === null) {
|
|
686
|
-
throw new LuaRuntimeError(
|
|
687
|
-
"attempt to unm a 'string' with a 'string'",
|
|
688
|
-
sf.withCtx(u.ctx),
|
|
689
|
-
);
|
|
690
|
-
}
|
|
691
|
-
if (n === 0) {
|
|
692
|
-
return 0;
|
|
693
|
-
}
|
|
694
|
-
return -n;
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
const plain = untagNumber(arg);
|
|
698
|
-
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) {
|
|
699
921
|
throw new LuaRuntimeError(
|
|
700
|
-
"attempt to
|
|
922
|
+
"attempt to unm a 'string' with a 'string'",
|
|
701
923
|
sf.withCtx(u.ctx),
|
|
702
924
|
);
|
|
703
925
|
}
|
|
926
|
+
if (n === 0) {
|
|
927
|
+
return 0;
|
|
928
|
+
}
|
|
929
|
+
return -n;
|
|
930
|
+
}
|
|
704
931
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
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
|
+
}
|
|
708
939
|
|
|
709
|
-
|
|
940
|
+
const argType = isTaggedFloat(arg)
|
|
941
|
+
? "float"
|
|
942
|
+
: astNumberKind(u.argument);
|
|
710
943
|
|
|
711
|
-
|
|
712
|
-
// minus must keep the result float-typed.
|
|
713
|
-
if (isTaggedFloat(arg)) {
|
|
714
|
-
if (out === 0) {
|
|
715
|
-
return makeLuaZero(out, "float");
|
|
716
|
-
}
|
|
717
|
-
return makeLuaFloat(out);
|
|
718
|
-
}
|
|
944
|
+
const out = luaUnaryMinus(plain, argType);
|
|
719
945
|
|
|
720
|
-
|
|
946
|
+
// If the operand is a float-tagged boxed number, unary
|
|
947
|
+
// minus must keep the result float-typed.
|
|
948
|
+
if (isTaggedFloat(arg)) {
|
|
721
949
|
if (out === 0) {
|
|
722
|
-
|
|
723
|
-
return makeLuaZero(out, outType);
|
|
950
|
+
return makeLuaZero(out, "float");
|
|
724
951
|
}
|
|
952
|
+
return makeLuaFloat(out);
|
|
953
|
+
}
|
|
725
954
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
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
|
+
});
|
|
729
963
|
};
|
|
730
964
|
|
|
731
965
|
return rpThen(tv as any, applyTyped);
|
|
@@ -740,35 +974,29 @@ export function evalExpression(
|
|
|
740
974
|
}
|
|
741
975
|
case "~": {
|
|
742
976
|
const arg = singleResult(value);
|
|
743
|
-
return unaryWithMeta(
|
|
744
|
-
arg
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
sf,
|
|
748
|
-
() => {
|
|
749
|
-
const intVal = toInteger(arg);
|
|
750
|
-
if (intVal === null) {
|
|
751
|
-
if (typeof arg === "string") {
|
|
752
|
-
throw new LuaRuntimeError(
|
|
753
|
-
`attempt to perform bitwise operation on a string value (constant '${arg}')`,
|
|
754
|
-
sf.withCtx(u.ctx),
|
|
755
|
-
);
|
|
756
|
-
}
|
|
757
|
-
const t = luaTypeName(arg);
|
|
758
|
-
if (t === "number") {
|
|
759
|
-
throw new LuaRuntimeError(
|
|
760
|
-
`number has no integer representation`,
|
|
761
|
-
sf.withCtx(u.ctx),
|
|
762
|
-
);
|
|
763
|
-
}
|
|
977
|
+
return unaryWithMeta(arg, "__bnot", u.ctx, sf, () => {
|
|
978
|
+
const intVal = toInteger(arg);
|
|
979
|
+
if (intVal === null) {
|
|
980
|
+
if (typeof arg === "string") {
|
|
764
981
|
throw new LuaRuntimeError(
|
|
765
|
-
`attempt to perform bitwise operation on a ${
|
|
982
|
+
`attempt to perform bitwise operation on a string value (constant '${arg}')`,
|
|
766
983
|
sf.withCtx(u.ctx),
|
|
767
984
|
);
|
|
768
985
|
}
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
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
|
+
});
|
|
772
1000
|
}
|
|
773
1001
|
case "#": {
|
|
774
1002
|
return luaLengthOp(singleResult(value), u.ctx, sf);
|
|
@@ -792,31 +1020,63 @@ export function evalExpression(
|
|
|
792
1020
|
}
|
|
793
1021
|
case "TableConstructor": {
|
|
794
1022
|
const tc = asTableConstructor(e);
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
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];
|
|
801
1033
|
switch (field.type) {
|
|
802
1034
|
case "PropField": {
|
|
803
|
-
const value =
|
|
804
|
-
|
|
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);
|
|
805
1043
|
break;
|
|
806
1044
|
}
|
|
807
1045
|
case "DynamicField": {
|
|
808
|
-
const key =
|
|
809
|
-
const
|
|
810
|
-
|
|
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);
|
|
811
1057
|
break;
|
|
812
1058
|
}
|
|
813
1059
|
case "ExpressionField": {
|
|
814
|
-
const value =
|
|
815
|
-
|
|
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
|
+
}
|
|
816
1076
|
if (value instanceof LuaMultiRes) {
|
|
817
1077
|
const flat = value.flatten();
|
|
818
|
-
for (let
|
|
819
|
-
table.rawSetArrayIndex(nextArrayIndex, flat.values[
|
|
1078
|
+
for (let j = 0; j < flat.values.length; j++) {
|
|
1079
|
+
table.rawSetArrayIndex(nextArrayIndex, flat.values[j]);
|
|
820
1080
|
nextArrayIndex++;
|
|
821
1081
|
}
|
|
822
1082
|
} else {
|
|
@@ -828,7 +1088,9 @@ export function evalExpression(
|
|
|
828
1088
|
}
|
|
829
1089
|
}
|
|
830
1090
|
return table;
|
|
831
|
-
}
|
|
1091
|
+
};
|
|
1092
|
+
|
|
1093
|
+
return processField(0);
|
|
832
1094
|
}
|
|
833
1095
|
case "FunctionDefinition": {
|
|
834
1096
|
const fd = asFunctionDef(e);
|
|
@@ -838,37 +1100,125 @@ export function evalExpression(
|
|
|
838
1100
|
const q = asQueryExpr(e);
|
|
839
1101
|
const findFromClause = q.clauses.find((c) => c.type === "From");
|
|
840
1102
|
if (!findFromClause) {
|
|
841
|
-
throw new LuaRuntimeError(
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
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
|
+
})();
|
|
845
1184
|
}
|
|
846
|
-
|
|
847
|
-
|
|
1185
|
+
|
|
1186
|
+
// Single-source
|
|
1187
|
+
const { objectVariable, expression: objectExpression } = fromSource;
|
|
848
1188
|
return Promise.resolve(evalExpression(objectExpression, env, sf)).then(
|
|
849
1189
|
async (collection: LuaValue) => {
|
|
850
1190
|
if (!collection) {
|
|
851
|
-
throw new LuaRuntimeError(
|
|
852
|
-
"Collection is nil",
|
|
853
|
-
sf.withCtx(q.ctx),
|
|
854
|
-
);
|
|
1191
|
+
throw new LuaRuntimeError("Collection is nil", sf.withCtx(q.ctx));
|
|
855
1192
|
}
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
collection
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
1193
|
+
|
|
1194
|
+
// If already a queryable collection (e.g. DataStoreQueryCollection),
|
|
1195
|
+
// use directly - skip all LuaTable/JS conversion.
|
|
1196
|
+
if (
|
|
1197
|
+
typeof collection === "object" &&
|
|
1198
|
+
collection !== null &&
|
|
1199
|
+
"query" in collection &&
|
|
1200
|
+
typeof (collection as any).query === "function"
|
|
1201
|
+
) {
|
|
1202
|
+
// Already queryable, use as-is
|
|
1203
|
+
} else if (collection instanceof LuaTable && collection.empty()) {
|
|
1204
|
+
// Empty table → empty array
|
|
1205
|
+
collection = toCollection([]);
|
|
1206
|
+
} else if (collection instanceof LuaTable) {
|
|
1207
|
+
if (collection.length > 0) {
|
|
1208
|
+
// Array-like table: extract array items, keep as LuaTables
|
|
1209
|
+
const arr: any[] = [];
|
|
1210
|
+
for (let i = 1; i <= collection.length; i++) {
|
|
1211
|
+
arr.push(collection.rawGet(i));
|
|
1212
|
+
}
|
|
1213
|
+
collection = toCollection(arr);
|
|
1214
|
+
} else {
|
|
1215
|
+
// Record-like table (no array part): treat as singleton
|
|
1216
|
+
collection = toCollection([collection]);
|
|
869
1217
|
}
|
|
870
|
-
|
|
1218
|
+
} else {
|
|
1219
|
+
collection = toCollection(luaValueToJS(collection, sf));
|
|
871
1220
|
}
|
|
1221
|
+
|
|
872
1222
|
// Build up query object
|
|
873
1223
|
const query: LuaCollectionQuery = {
|
|
874
1224
|
objectVariable,
|
|
@@ -886,11 +1236,13 @@ export function evalExpression(
|
|
|
886
1236
|
query.orderBy = clause.orderBy.map((o) => ({
|
|
887
1237
|
expr: o.expression,
|
|
888
1238
|
desc: o.direction === "desc",
|
|
1239
|
+
nulls: o.nulls,
|
|
1240
|
+
using: o.using,
|
|
889
1241
|
}));
|
|
890
1242
|
break;
|
|
891
1243
|
}
|
|
892
1244
|
case "Select": {
|
|
893
|
-
query.select = clause.
|
|
1245
|
+
query.select = fieldsToExpression(clause.fields, clause.ctx);
|
|
894
1246
|
break;
|
|
895
1247
|
}
|
|
896
1248
|
case "Limit": {
|
|
@@ -906,12 +1258,30 @@ export function evalExpression(
|
|
|
906
1258
|
}
|
|
907
1259
|
break;
|
|
908
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
|
+
}
|
|
1270
|
+
case "GroupBy": {
|
|
1271
|
+
query.groupBy = fieldsToGroupByEntries(clause.fields);
|
|
1272
|
+
break;
|
|
1273
|
+
}
|
|
1274
|
+
case "Having": {
|
|
1275
|
+
query.having = clause.expression;
|
|
1276
|
+
break;
|
|
1277
|
+
}
|
|
909
1278
|
}
|
|
910
1279
|
}
|
|
911
1280
|
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
1281
|
+
// Always use the possibly-wrapped collection
|
|
1282
|
+
return (collection as any)
|
|
1283
|
+
.query(query, env, sf, globalThis.client?.config)
|
|
1284
|
+
.then(jsToLuaValue);
|
|
915
1285
|
},
|
|
916
1286
|
);
|
|
917
1287
|
}
|
|
@@ -964,13 +1334,10 @@ function evalPrefixExpression(
|
|
|
964
1334
|
return luaGet(table, key, ta.ctx, sf);
|
|
965
1335
|
}
|
|
966
1336
|
|
|
967
|
-
return rpThen(
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
keyV,
|
|
972
|
-
(key) => luaGet(singleResult(obj), singleResult(key), ta.ctx, sf),
|
|
973
|
-
),
|
|
1337
|
+
return rpThen(objV, (obj) =>
|
|
1338
|
+
rpThen(keyV, (key) =>
|
|
1339
|
+
luaGet(singleResult(obj), singleResult(key), ta.ctx, sf),
|
|
1340
|
+
),
|
|
974
1341
|
);
|
|
975
1342
|
}
|
|
976
1343
|
|
|
@@ -980,50 +1347,88 @@ function evalPrefixExpression(
|
|
|
980
1347
|
// Sync-first: evaluate object; avoid Promise when object is sync.
|
|
981
1348
|
const objV = evalPrefixExpression(pa.object, env, sf);
|
|
982
1349
|
if (!isPromise(objV)) {
|
|
983
|
-
return luaGet(objV, pa.property, pa.ctx, sf);
|
|
1350
|
+
return luaGet(singleResult(objV), pa.property, pa.ctx, sf);
|
|
984
1351
|
}
|
|
985
|
-
return rpThen(objV, (obj) =>
|
|
1352
|
+
return rpThen(objV, (obj) =>
|
|
1353
|
+
luaGet(singleResult(obj), pa.property, pa.ctx, sf),
|
|
1354
|
+
);
|
|
986
1355
|
}
|
|
987
1356
|
|
|
988
1357
|
case "FunctionCall": {
|
|
989
1358
|
const fc = asFunctionCall(e);
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
asVariable(fc.prefix).name
|
|
995
|
-
}')`
|
|
996
|
-
: `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) {
|
|
997
1363
|
throw new LuaRuntimeError(
|
|
998
|
-
|
|
999
|
-
sf.withCtx(fc.
|
|
1364
|
+
`'order by' is not allowed in non-aggregate function calls`,
|
|
1365
|
+
sf.withCtx(fc.ctx),
|
|
1000
1366
|
);
|
|
1001
1367
|
}
|
|
1002
1368
|
|
|
1003
|
-
|
|
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
|
+
}
|
|
1004
1390
|
|
|
1005
1391
|
const handleFunctionCall = (
|
|
1006
1392
|
calleeVal: LuaValue,
|
|
1393
|
+
selfArgs: LuaValue[],
|
|
1007
1394
|
): LuaValue | Promise<LuaValue> => {
|
|
1008
1395
|
// Normal argument handling for hello:there(a, b, c) type calls
|
|
1009
1396
|
if (fc.name) {
|
|
1010
|
-
|
|
1397
|
+
const self = calleeVal;
|
|
1011
1398
|
calleeVal = luaIndexValue(calleeVal, fc.name, sf);
|
|
1012
1399
|
|
|
1013
1400
|
if (isPromise(calleeVal)) {
|
|
1014
|
-
return (calleeVal as Promise<any>).then(
|
|
1401
|
+
return (calleeVal as Promise<any>).then((cv) =>
|
|
1402
|
+
handleFunctionCall(cv, [self]),
|
|
1403
|
+
);
|
|
1015
1404
|
}
|
|
1405
|
+
selfArgs = [self];
|
|
1016
1406
|
}
|
|
1017
1407
|
|
|
1018
1408
|
const argsVal = evalExpressions(fc.args, env, sf);
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
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
|
+
);
|
|
1024
1424
|
};
|
|
1025
1425
|
|
|
1026
|
-
|
|
1426
|
+
if (isPromise(prefixValue)) {
|
|
1427
|
+
return (prefixValue as Promise<any>).then((pv) =>
|
|
1428
|
+
handleFunctionCall(pv, []),
|
|
1429
|
+
);
|
|
1430
|
+
}
|
|
1431
|
+
return handleFunctionCall(prefixValue, []);
|
|
1027
1432
|
}
|
|
1028
1433
|
|
|
1029
1434
|
default: {
|
|
@@ -1173,22 +1578,58 @@ function evalBinaryWithLR(
|
|
|
1173
1578
|
: undefined;
|
|
1174
1579
|
const leftVal = evalExpression(leftExpr, env, sf);
|
|
1175
1580
|
|
|
1176
|
-
|
|
1581
|
+
// Sync-first fast path: avoid closure allocation when both operands are sync
|
|
1582
|
+
if (!isPromise(leftVal)) {
|
|
1177
1583
|
const rightVal = evalExpression(rightExpr, env, sf);
|
|
1178
|
-
|
|
1584
|
+
if (!isPromise(rightVal)) {
|
|
1179
1585
|
return luaOp(
|
|
1180
1586
|
op,
|
|
1181
|
-
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),
|
|
1182
1599
|
singleResult(rv),
|
|
1183
1600
|
leftType,
|
|
1184
1601
|
rightType,
|
|
1185
1602
|
ctx,
|
|
1186
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,
|
|
1187
1619
|
);
|
|
1188
|
-
}
|
|
1189
|
-
return
|
|
1190
|
-
|
|
1191
|
-
|
|
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
|
+
});
|
|
1192
1633
|
}
|
|
1193
1634
|
|
|
1194
1635
|
function createBitwiseError(
|
|
@@ -1271,10 +1712,7 @@ function luaEqWithMetamethod(
|
|
|
1271
1712
|
}
|
|
1272
1713
|
|
|
1273
1714
|
const ty = luaTypeName(mm);
|
|
1274
|
-
throw new LuaRuntimeError(
|
|
1275
|
-
`attempt to call a ${ty} value`,
|
|
1276
|
-
sf.withCtx(ctx),
|
|
1277
|
-
);
|
|
1715
|
+
throw new LuaRuntimeError(`attempt to call a ${ty} value`, sf.withCtx(ctx));
|
|
1278
1716
|
};
|
|
1279
1717
|
|
|
1280
1718
|
// Try left __eq first, then right.
|
|
@@ -1331,11 +1769,7 @@ function luaRelWithMetamethod(
|
|
|
1331
1769
|
* - for JavaScript arrays return length,
|
|
1332
1770
|
* - throw error otherwise.
|
|
1333
1771
|
*/
|
|
1334
|
-
function luaLengthOp(
|
|
1335
|
-
val: any,
|
|
1336
|
-
ctx: ASTCtx,
|
|
1337
|
-
sf: LuaStackFrame,
|
|
1338
|
-
): LuaValue {
|
|
1772
|
+
function luaLengthOp(val: any, ctx: ASTCtx, sf: LuaStackFrame): LuaValue {
|
|
1339
1773
|
// Strings: ignore `__len`
|
|
1340
1774
|
if (typeof val === "string") {
|
|
1341
1775
|
return val.length;
|
|
@@ -1382,22 +1816,26 @@ function evalExpressions(
|
|
|
1382
1816
|
env: LuaEnv,
|
|
1383
1817
|
sf: LuaStackFrame,
|
|
1384
1818
|
): Promise<LuaValue[]> | LuaValue[] {
|
|
1385
|
-
|
|
1386
|
-
|
|
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
|
+
}
|
|
1387
1827
|
const argsVal = rpAll(parts);
|
|
1388
1828
|
|
|
1389
1829
|
// In Lua multi-returns propagate only in tail position of an expression list.
|
|
1390
1830
|
const finalize = (argsResolved: any[]) => {
|
|
1391
|
-
if (argsResolved.length === 0) {
|
|
1392
|
-
return [];
|
|
1393
|
-
}
|
|
1394
1831
|
const out: LuaValue[] = [];
|
|
1832
|
+
const lastIdx = argsResolved.length - 1;
|
|
1395
1833
|
// All but last expression produce a single value
|
|
1396
|
-
for (let i = 0; i <
|
|
1834
|
+
for (let i = 0; i < lastIdx; i++) {
|
|
1397
1835
|
out.push(singleResult(argsResolved[i]));
|
|
1398
1836
|
}
|
|
1399
1837
|
// Last expression preserves multiple results
|
|
1400
|
-
const last = argsResolved[
|
|
1838
|
+
const last = argsResolved[lastIdx];
|
|
1401
1839
|
if (last instanceof LuaMultiRes) {
|
|
1402
1840
|
out.push(...last.flatten().values);
|
|
1403
1841
|
} else {
|
|
@@ -1410,9 +1848,9 @@ function evalExpressions(
|
|
|
1410
1848
|
}
|
|
1411
1849
|
|
|
1412
1850
|
type EvalBlockResult =
|
|
1413
|
-
|
|
|
1851
|
+
| undefined
|
|
1414
1852
|
| ControlSignal
|
|
1415
|
-
| Promise<
|
|
1853
|
+
| Promise<undefined | ControlSignal>;
|
|
1416
1854
|
|
|
1417
1855
|
function runStatementsNoGoto(
|
|
1418
1856
|
stmts: LuaStatement[],
|
|
@@ -1420,17 +1858,12 @@ function runStatementsNoGoto(
|
|
|
1420
1858
|
sf: LuaStackFrame,
|
|
1421
1859
|
returnOnReturn: boolean,
|
|
1422
1860
|
startIdx: number,
|
|
1423
|
-
):
|
|
1861
|
+
): undefined | ControlSignal | Promise<undefined | ControlSignal> {
|
|
1424
1862
|
const processFrom = (
|
|
1425
1863
|
idx: number,
|
|
1426
|
-
):
|
|
1864
|
+
): undefined | ControlSignal | Promise<undefined | ControlSignal> => {
|
|
1427
1865
|
for (let i = idx; i < stmts.length; i++) {
|
|
1428
|
-
const result = evalStatement(
|
|
1429
|
-
stmts[i],
|
|
1430
|
-
execEnv,
|
|
1431
|
-
sf,
|
|
1432
|
-
returnOnReturn,
|
|
1433
|
-
);
|
|
1866
|
+
const result = evalStatement(stmts[i], execEnv, sf, returnOnReturn);
|
|
1434
1867
|
if (isPromise(result)) {
|
|
1435
1868
|
return (result as Promise<any>).then((res) => {
|
|
1436
1869
|
if (res !== undefined) {
|
|
@@ -1482,9 +1915,8 @@ function withCloseBoundary(
|
|
|
1482
1915
|
};
|
|
1483
1916
|
|
|
1484
1917
|
const onRejected = (e: any) => {
|
|
1485
|
-
const errObj: LuaValue =
|
|
1486
|
-
? e.message
|
|
1487
|
-
: (e?.message ?? String(e));
|
|
1918
|
+
const errObj: LuaValue =
|
|
1919
|
+
e instanceof LuaRuntimeError ? e.message : (e?.message ?? String(e));
|
|
1488
1920
|
const r = luaCloseFromMark(sf, mark, errObj);
|
|
1489
1921
|
if (isPromise(r)) {
|
|
1490
1922
|
return (r as Promise<void>).then(() => {
|
|
@@ -1527,9 +1959,7 @@ function evalBlockNoClose(
|
|
|
1527
1959
|
if (fnHasGotos === true && !hasLabelHere && !hasGotoFlag) {
|
|
1528
1960
|
const execEnv = b.needsEnv === true ? new LuaEnv(env) : env;
|
|
1529
1961
|
const stmts = b.statements;
|
|
1530
|
-
const runFrom = (
|
|
1531
|
-
i: number,
|
|
1532
|
-
): EvalBlockResult => {
|
|
1962
|
+
const runFrom = (i: number): EvalBlockResult => {
|
|
1533
1963
|
for (; i < stmts.length; i++) {
|
|
1534
1964
|
const r = evalStatement(stmts[i], execEnv, sf, returnOnReturn);
|
|
1535
1965
|
if (isPromise(r)) {
|
|
@@ -1574,9 +2004,7 @@ function evalBlockNoClose(
|
|
|
1574
2004
|
const execEnv = b.needsEnv === true ? new LuaEnv(env) : env;
|
|
1575
2005
|
const stmts = b.statements;
|
|
1576
2006
|
|
|
1577
|
-
const runFrom = (
|
|
1578
|
-
i: number,
|
|
1579
|
-
): EvalBlockResult => {
|
|
2007
|
+
const runFrom = (i: number): EvalBlockResult => {
|
|
1580
2008
|
for (; i < stmts.length; i++) {
|
|
1581
2009
|
const r = evalStatement(stmts[i], execEnv, sf, returnOnReturn);
|
|
1582
2010
|
if (isPromise(r)) {
|
|
@@ -1621,22 +2049,25 @@ export function evalStatement(
|
|
|
1621
2049
|
env: LuaEnv,
|
|
1622
2050
|
sf: LuaStackFrame,
|
|
1623
2051
|
returnOnReturn = false,
|
|
1624
|
-
):
|
|
2052
|
+
): undefined | ControlSignal | Promise<undefined | ControlSignal> {
|
|
1625
2053
|
switch (s.type) {
|
|
1626
2054
|
case "Assignment": {
|
|
1627
2055
|
const a = asAssignment(s);
|
|
1628
2056
|
const valuesRP = evalExpressions(a.expressions, env, sf);
|
|
1629
|
-
const lvaluesRP = evalPromiseValues(
|
|
1630
|
-
.map((lval) => evalLValue(lval, env, sf))
|
|
2057
|
+
const lvaluesRP = evalPromiseValues(
|
|
2058
|
+
a.variables.map((lval) => evalLValue(lval, env, sf)),
|
|
2059
|
+
);
|
|
1631
2060
|
|
|
1632
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;
|
|
1633
2064
|
const ps: Promise<any>[] = [];
|
|
1634
2065
|
for (let i = 0; i < lvalues.length; i++) {
|
|
1635
2066
|
const r = luaSet(
|
|
1636
2067
|
lvalues[i].env,
|
|
1637
2068
|
lvalues[i].key,
|
|
1638
2069
|
values[i],
|
|
1639
|
-
sf.withCtx(a.ctx),
|
|
2070
|
+
errSf || (errSf = sf.withCtx(a.ctx)),
|
|
1640
2071
|
);
|
|
1641
2072
|
|
|
1642
2073
|
if (isPromise(r)) {
|
|
@@ -1650,29 +2081,22 @@ export function evalStatement(
|
|
|
1650
2081
|
};
|
|
1651
2082
|
|
|
1652
2083
|
if (!isPromise(valuesRP) && !isPromise(lvaluesRP)) {
|
|
1653
|
-
return apply(
|
|
1654
|
-
valuesRP as LuaValue[],
|
|
1655
|
-
lvaluesRP as LuaLValueContainer[],
|
|
1656
|
-
);
|
|
2084
|
+
return apply(valuesRP as LuaValue[], lvaluesRP as LuaLValueContainer[]);
|
|
1657
2085
|
}
|
|
1658
|
-
if (
|
|
1659
|
-
isPromise(valuesRP) && !isPromise(lvaluesRP)
|
|
1660
|
-
) {
|
|
2086
|
+
if (isPromise(valuesRP) && !isPromise(lvaluesRP)) {
|
|
1661
2087
|
return (valuesRP as Promise<LuaValue[]>).then((values: LuaValue[]) =>
|
|
1662
|
-
apply(values, lvaluesRP as LuaLValueContainer[])
|
|
2088
|
+
apply(values, lvaluesRP as LuaLValueContainer[]),
|
|
1663
2089
|
);
|
|
1664
2090
|
}
|
|
1665
|
-
if (
|
|
1666
|
-
!isPromise(valuesRP) && isPromise(lvaluesRP)
|
|
1667
|
-
) {
|
|
2091
|
+
if (!isPromise(valuesRP) && isPromise(lvaluesRP)) {
|
|
1668
2092
|
return (lvaluesRP as Promise<any[]>).then((lvalues: any[]) =>
|
|
1669
|
-
apply(valuesRP as LuaValue[], lvalues)
|
|
2093
|
+
apply(valuesRP as LuaValue[], lvalues),
|
|
1670
2094
|
);
|
|
1671
2095
|
}
|
|
1672
2096
|
return (valuesRP as Promise<LuaValue[]>).then((values: LuaValue[]) =>
|
|
1673
2097
|
(lvaluesRP as Promise<any[]>).then((lvalues: any[]) =>
|
|
1674
|
-
apply(values, lvalues)
|
|
1675
|
-
)
|
|
2098
|
+
apply(values, lvalues),
|
|
2099
|
+
),
|
|
1676
2100
|
);
|
|
1677
2101
|
}
|
|
1678
2102
|
case "Local": {
|
|
@@ -1721,10 +2145,7 @@ export function evalStatement(
|
|
|
1721
2145
|
|
|
1722
2146
|
const bindAvailable = () => {
|
|
1723
2147
|
while (boundCount < l.names.length && boundCount < out.length) {
|
|
1724
|
-
bindOne(
|
|
1725
|
-
l.names[boundCount],
|
|
1726
|
-
out[boundCount] ?? null,
|
|
1727
|
-
);
|
|
2148
|
+
bindOne(l.names[boundCount], out[boundCount] ?? null);
|
|
1728
2149
|
boundCount++;
|
|
1729
2150
|
}
|
|
1730
2151
|
};
|
|
@@ -1773,7 +2194,7 @@ export function evalStatement(
|
|
|
1773
2194
|
return rpThen(rp, onValue) as any;
|
|
1774
2195
|
};
|
|
1775
2196
|
|
|
1776
|
-
return runFrom(0)
|
|
2197
|
+
return runFrom(0) as undefined | Promise<undefined>;
|
|
1777
2198
|
}
|
|
1778
2199
|
case "Semicolon": {
|
|
1779
2200
|
return;
|
|
@@ -1804,9 +2225,8 @@ export function evalStatement(
|
|
|
1804
2225
|
try {
|
|
1805
2226
|
out = evalBlockNoClose(b, env, sf, returnOnReturn);
|
|
1806
2227
|
} catch (e: any) {
|
|
1807
|
-
const errObj: LuaValue =
|
|
1808
|
-
? e.message
|
|
1809
|
-
: (e?.message ?? String(e));
|
|
2228
|
+
const errObj: LuaValue =
|
|
2229
|
+
e instanceof LuaRuntimeError ? e.message : (e?.message ?? String(e));
|
|
1810
2230
|
const r = luaCloseFromMark(sf, mark, errObj);
|
|
1811
2231
|
if (isPromise(r)) {
|
|
1812
2232
|
return (r as Promise<void>).then(() => {
|
|
@@ -1825,10 +2245,7 @@ export function evalStatement(
|
|
|
1825
2245
|
|
|
1826
2246
|
const runFrom = (
|
|
1827
2247
|
i: number,
|
|
1828
|
-
):
|
|
1829
|
-
| void
|
|
1830
|
-
| ControlSignal
|
|
1831
|
-
| Promise<void | ControlSignal> => {
|
|
2248
|
+
): undefined | ControlSignal | Promise<undefined | ControlSignal> => {
|
|
1832
2249
|
if (i >= conds.length) {
|
|
1833
2250
|
if (iff.elseBlock) {
|
|
1834
2251
|
return evalStatement(iff.elseBlock, env, sf, returnOnReturn);
|
|
@@ -1855,132 +2272,84 @@ export function evalStatement(
|
|
|
1855
2272
|
case "While": {
|
|
1856
2273
|
const w = asWhile(s);
|
|
1857
2274
|
|
|
1858
|
-
|
|
2275
|
+
// Sync-first loop that re-enters sync mode after each async iteration
|
|
2276
|
+
const runSyncFirst = ():
|
|
2277
|
+
| undefined
|
|
2278
|
+
| ControlSignal
|
|
2279
|
+
| Promise<undefined | ControlSignal> => {
|
|
1859
2280
|
while (true) {
|
|
1860
|
-
const c =
|
|
1861
|
-
if (
|
|
1862
|
-
|
|
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
|
+
});
|
|
1863
2295
|
}
|
|
2296
|
+
if (!luaTruthy(c)) break;
|
|
1864
2297
|
const r = evalStatement(w.block, env, sf, returnOnReturn);
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
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;
|
|
1871
2309
|
}
|
|
1872
2310
|
}
|
|
1873
2311
|
return;
|
|
1874
2312
|
};
|
|
1875
2313
|
|
|
1876
|
-
|
|
1877
|
-
const c = evalExpression(w.condition, env, sf);
|
|
1878
|
-
if (isPromise(c)) {
|
|
1879
|
-
return (c as Promise<any>).then((cv) => {
|
|
1880
|
-
if (!luaTruthy(cv)) {
|
|
1881
|
-
return;
|
|
1882
|
-
}
|
|
1883
|
-
try {
|
|
1884
|
-
const r = evalStatement(w.block, env, sf, returnOnReturn);
|
|
1885
|
-
if (isPromise(r)) {
|
|
1886
|
-
return (r as Promise<any>).then((res) => {
|
|
1887
|
-
if (res !== undefined) {
|
|
1888
|
-
if (isBreakSignal(res)) {
|
|
1889
|
-
return;
|
|
1890
|
-
}
|
|
1891
|
-
return res;
|
|
1892
|
-
}
|
|
1893
|
-
return runAsync();
|
|
1894
|
-
});
|
|
1895
|
-
}
|
|
1896
|
-
if (r !== undefined) {
|
|
1897
|
-
if (isBreakSignal(r)) {
|
|
1898
|
-
return;
|
|
1899
|
-
}
|
|
1900
|
-
return r;
|
|
1901
|
-
}
|
|
1902
|
-
return runAsync();
|
|
1903
|
-
} catch (e: any) {
|
|
1904
|
-
throw e;
|
|
1905
|
-
}
|
|
1906
|
-
});
|
|
1907
|
-
}
|
|
1908
|
-
if (!luaTruthy(c)) {
|
|
1909
|
-
break;
|
|
1910
|
-
}
|
|
1911
|
-
const r = evalStatement(w.block, env, sf, returnOnReturn);
|
|
1912
|
-
if (isPromise(r)) {
|
|
1913
|
-
return (r as Promise<any>).then((res) => {
|
|
1914
|
-
if (res !== undefined) {
|
|
1915
|
-
if (isBreakSignal(res)) {
|
|
1916
|
-
return;
|
|
1917
|
-
}
|
|
1918
|
-
return res;
|
|
1919
|
-
}
|
|
1920
|
-
return runAsync();
|
|
1921
|
-
});
|
|
1922
|
-
}
|
|
1923
|
-
if (r !== undefined) {
|
|
1924
|
-
if (isBreakSignal(r)) {
|
|
1925
|
-
break;
|
|
1926
|
-
}
|
|
1927
|
-
return r;
|
|
1928
|
-
}
|
|
1929
|
-
}
|
|
1930
|
-
return;
|
|
2314
|
+
return runSyncFirst();
|
|
1931
2315
|
}
|
|
1932
2316
|
case "Repeat": {
|
|
1933
|
-
const
|
|
2317
|
+
const rep = asRepeat(s);
|
|
1934
2318
|
|
|
1935
|
-
|
|
2319
|
+
// Sync-first loop that re-enters sync mode after each async iteration
|
|
2320
|
+
const runSyncFirst = ():
|
|
2321
|
+
| undefined
|
|
2322
|
+
| ControlSignal
|
|
2323
|
+
| Promise<undefined | ControlSignal> => {
|
|
1936
2324
|
while (true) {
|
|
1937
|
-
const rr = evalStatement(
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
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
|
+
});
|
|
1944
2335
|
}
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
2336
|
+
if (rr !== undefined) {
|
|
2337
|
+
if (isBreakSignal(rr)) return;
|
|
2338
|
+
return rr;
|
|
1948
2339
|
}
|
|
1949
|
-
}
|
|
1950
|
-
return;
|
|
1951
|
-
};
|
|
1952
2340
|
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
if (isBreakSignal(res)) {
|
|
1959
|
-
return;
|
|
1960
|
-
}
|
|
1961
|
-
return res;
|
|
1962
|
-
}
|
|
1963
|
-
return runAsync();
|
|
1964
|
-
});
|
|
1965
|
-
}
|
|
1966
|
-
if (rr !== undefined) {
|
|
1967
|
-
if (isBreakSignal(rr)) {
|
|
1968
|
-
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
|
+
);
|
|
1969
2346
|
}
|
|
1970
|
-
|
|
2347
|
+
if (luaTruthy(c)) break;
|
|
1971
2348
|
}
|
|
2349
|
+
return;
|
|
2350
|
+
};
|
|
1972
2351
|
|
|
1973
|
-
|
|
1974
|
-
if (isPromise(c)) {
|
|
1975
|
-
return (c as Promise<any>).then((cv) =>
|
|
1976
|
-
luaTruthy(cv) ? undefined : runAsync()
|
|
1977
|
-
);
|
|
1978
|
-
}
|
|
1979
|
-
if (luaTruthy(c)) {
|
|
1980
|
-
break;
|
|
1981
|
-
}
|
|
1982
|
-
}
|
|
1983
|
-
return;
|
|
2352
|
+
return runSyncFirst();
|
|
1984
2353
|
}
|
|
1985
2354
|
case "Break": {
|
|
1986
2355
|
return { ctrl: "break" };
|
|
@@ -2000,7 +2369,7 @@ export function evalStatement(
|
|
|
2000
2369
|
if (fn.name.colonName) {
|
|
2001
2370
|
// function hello:there() -> function hello.there(self) transformation
|
|
2002
2371
|
body = {
|
|
2003
|
-
...
|
|
2372
|
+
...fn.body,
|
|
2004
2373
|
parameters: ["self", ...fn.body.parameters],
|
|
2005
2374
|
};
|
|
2006
2375
|
propNames = [...fn.name.propNames, fn.name.colonName];
|
|
@@ -2023,17 +2392,14 @@ export function evalStatement(
|
|
|
2023
2392
|
}
|
|
2024
2393
|
case "LocalFunction": {
|
|
2025
2394
|
const lf = asLocalFunction(s);
|
|
2026
|
-
env.setLocal(
|
|
2027
|
-
lf.name,
|
|
2028
|
-
new LuaFunction(lf.body, env),
|
|
2029
|
-
);
|
|
2395
|
+
env.setLocal(lf.name, new LuaFunction(lf.body, env));
|
|
2030
2396
|
return;
|
|
2031
2397
|
}
|
|
2032
2398
|
case "Return": {
|
|
2033
2399
|
const ret = asReturn(s);
|
|
2034
2400
|
|
|
2035
2401
|
const parts = ret.expressions.map((value: LuaExpression) =>
|
|
2036
|
-
evalExpression(value, env, sf)
|
|
2402
|
+
evalExpression(value, env, sf),
|
|
2037
2403
|
);
|
|
2038
2404
|
const valuesRP = rpAll(parts);
|
|
2039
2405
|
|
|
@@ -2081,9 +2447,7 @@ export function evalStatement(
|
|
|
2081
2447
|
const determineLoopType = (): NumericType => {
|
|
2082
2448
|
const startType = astNumberKind(fr.start);
|
|
2083
2449
|
const stepType = fr.step ? astNumberKind(fr.step) : "int";
|
|
2084
|
-
return
|
|
2085
|
-
? "float"
|
|
2086
|
-
: "int";
|
|
2450
|
+
return startType === "float" || stepType === "float" ? "float" : "int";
|
|
2087
2451
|
};
|
|
2088
2452
|
|
|
2089
2453
|
const wrapLoopVar = (i: number, loopType: NumericType) => {
|
|
@@ -2093,53 +2457,59 @@ export function evalStatement(
|
|
|
2093
2457
|
return i;
|
|
2094
2458
|
};
|
|
2095
2459
|
|
|
2096
|
-
const canReuseEnv =
|
|
2097
|
-
fr.capturesLoopVar === false;
|
|
2460
|
+
const canReuseEnv =
|
|
2461
|
+
!fr.block.hasFunctionDef || fr.capturesLoopVar === false;
|
|
2098
2462
|
|
|
2099
2463
|
const executeIteration = canReuseEnv
|
|
2100
2464
|
? (
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
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
|
+
}
|
|
2108
2472
|
: (
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
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
|
+
};
|
|
2117
2481
|
|
|
2118
|
-
|
|
2482
|
+
// Continuation that re-enters sync mode after each async iteration
|
|
2483
|
+
const runFromIndex = (
|
|
2119
2484
|
loopEnv: LuaEnv,
|
|
2120
2485
|
end: number,
|
|
2121
2486
|
step: number,
|
|
2122
2487
|
startIndex: number,
|
|
2123
2488
|
loopType: NumericType,
|
|
2124
|
-
) => {
|
|
2489
|
+
): undefined | ControlSignal | Promise<undefined | ControlSignal> => {
|
|
2125
2490
|
if (step === 0) {
|
|
2126
2491
|
throw new LuaRuntimeError("'for' step is zero", sf.withCtx(fr.ctx));
|
|
2127
2492
|
}
|
|
2128
2493
|
|
|
2129
|
-
const shouldContinue =
|
|
2130
|
-
? (i: number) => i <= end
|
|
2131
|
-
: (i: number) => i >= end;
|
|
2494
|
+
const shouldContinue =
|
|
2495
|
+
step > 0 ? (i: number) => i <= end : (i: number) => i >= end;
|
|
2132
2496
|
|
|
2133
2497
|
for (let i = startIndex; shouldContinue(i); i += step) {
|
|
2134
2498
|
const r = executeIteration(loopEnv, i, loopType);
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
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;
|
|
2141
2510
|
}
|
|
2142
2511
|
}
|
|
2512
|
+
return;
|
|
2143
2513
|
};
|
|
2144
2514
|
|
|
2145
2515
|
const runSyncFirst = (
|
|
@@ -2147,17 +2517,13 @@ export function evalStatement(
|
|
|
2147
2517
|
end: number,
|
|
2148
2518
|
step: number,
|
|
2149
2519
|
loopType: NumericType,
|
|
2150
|
-
):
|
|
2151
|
-
| void
|
|
2152
|
-
| ControlSignal
|
|
2153
|
-
| Promise<void | ControlSignal> => {
|
|
2520
|
+
): undefined | ControlSignal | Promise<undefined | ControlSignal> => {
|
|
2154
2521
|
if (step === 0) {
|
|
2155
2522
|
throw new LuaRuntimeError("'for' step is zero", sf.withCtx(fr.ctx));
|
|
2156
2523
|
}
|
|
2157
2524
|
|
|
2158
|
-
const shouldContinue =
|
|
2159
|
-
? (i: number) => i <= end
|
|
2160
|
-
: (i: number) => i >= end;
|
|
2525
|
+
const shouldContinue =
|
|
2526
|
+
step > 0 ? (i: number) => i <= end : (i: number) => i >= end;
|
|
2161
2527
|
|
|
2162
2528
|
const loopEnv = new LuaEnv(env);
|
|
2163
2529
|
|
|
@@ -2171,7 +2537,7 @@ export function evalStatement(
|
|
|
2171
2537
|
}
|
|
2172
2538
|
return res;
|
|
2173
2539
|
}
|
|
2174
|
-
return
|
|
2540
|
+
return runFromIndex(loopEnv, end, step, i + step, loopType);
|
|
2175
2541
|
});
|
|
2176
2542
|
}
|
|
2177
2543
|
if (r !== undefined) {
|
|
@@ -2186,11 +2552,7 @@ export function evalStatement(
|
|
|
2186
2552
|
|
|
2187
2553
|
const loopType = determineLoopType();
|
|
2188
2554
|
|
|
2189
|
-
if (
|
|
2190
|
-
!isPromise(startV) &&
|
|
2191
|
-
!isPromise(endV) &&
|
|
2192
|
-
!isPromise(stepV)
|
|
2193
|
-
) {
|
|
2555
|
+
if (!isPromise(startV) && !isPromise(endV) && !isPromise(stepV)) {
|
|
2194
2556
|
return runSyncFirst(
|
|
2195
2557
|
untagNumber(startV) as number,
|
|
2196
2558
|
untagNumber(endV) as number,
|
|
@@ -2217,8 +2579,8 @@ export function evalStatement(
|
|
|
2217
2579
|
fi.expressions.map((e: LuaExpression) => evalExpression(e, env, sf)),
|
|
2218
2580
|
);
|
|
2219
2581
|
|
|
2220
|
-
const canReuseEnv =
|
|
2221
|
-
fi.capturesLoopVar === false;
|
|
2582
|
+
const canReuseEnv =
|
|
2583
|
+
!fi.block.hasFunctionDef || fi.capturesLoopVar === false;
|
|
2222
2584
|
const setIterVars = (
|
|
2223
2585
|
localEnv: LuaEnv,
|
|
2224
2586
|
names: string[],
|
|
@@ -2289,39 +2651,8 @@ export function evalStatement(
|
|
|
2289
2651
|
return new LuaEnv(env);
|
|
2290
2652
|
};
|
|
2291
2653
|
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
while (true) {
|
|
2295
|
-
const callRes = luaCall(
|
|
2296
|
-
iteratorValue,
|
|
2297
|
-
[state, control],
|
|
2298
|
-
fi.ctx,
|
|
2299
|
-
sf,
|
|
2300
|
-
);
|
|
2301
|
-
const iterResult = new LuaMultiRes(
|
|
2302
|
-
isPromise(callRes) ? await callRes : callRes,
|
|
2303
|
-
).flatten();
|
|
2304
|
-
const nextControl = iterResult.values[0];
|
|
2305
|
-
if (nextControl === null || nextControl === undefined) {
|
|
2306
|
-
break;
|
|
2307
|
-
}
|
|
2308
|
-
control = nextControl;
|
|
2309
|
-
|
|
2310
|
-
const localEnv = makeIterEnv();
|
|
2311
|
-
setIterVars(localEnv, fi.names, iterResult.values);
|
|
2312
|
-
|
|
2313
|
-
const r = evalStatement(fi.block, localEnv, sf, returnOnReturn);
|
|
2314
|
-
const res = isPromise(r) ? await r : r;
|
|
2315
|
-
if (res !== undefined) {
|
|
2316
|
-
if (isBreakSignal(res)) {
|
|
2317
|
-
break;
|
|
2318
|
-
}
|
|
2319
|
-
return await finish(res);
|
|
2320
|
-
}
|
|
2321
|
-
}
|
|
2322
|
-
return await finish(undefined);
|
|
2323
|
-
};
|
|
2324
|
-
|
|
2654
|
+
// Sync-first loop that re-enters sync mode after each async iteration
|
|
2655
|
+
const runSyncFirst = (): any => {
|
|
2325
2656
|
while (true) {
|
|
2326
2657
|
const iterCall = luaCall(
|
|
2327
2658
|
iteratorValue,
|
|
@@ -2329,55 +2660,40 @@ export function evalStatement(
|
|
|
2329
2660
|
fi.ctx,
|
|
2330
2661
|
sf,
|
|
2331
2662
|
);
|
|
2332
|
-
if (isPromise(iterCall)) {
|
|
2333
|
-
return (iterCall as Promise<any>).then((itv) => {
|
|
2334
|
-
const iterResult = new LuaMultiRes(itv).flatten();
|
|
2335
|
-
const nextControl = iterResult.values[0];
|
|
2336
|
-
if (nextControl === null || nextControl === undefined) {
|
|
2337
|
-
const r = finish(undefined);
|
|
2338
|
-
if (isPromise(r)) return (r as Promise<void>).then(() => {});
|
|
2339
|
-
return;
|
|
2340
|
-
}
|
|
2341
|
-
control = nextControl;
|
|
2342
2663
|
|
|
2343
|
-
|
|
2344
|
-
|
|
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;
|
|
2345
2671
|
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
)
|
|
2352
|
-
|
|
2353
|
-
return (r as Promise<any>).then((res) => {
|
|
2354
|
-
if (res !== undefined) {
|
|
2355
|
-
if (isBreakSignal(res)) {
|
|
2356
|
-
return finish(undefined);
|
|
2357
|
-
}
|
|
2358
|
-
return rpThen(finish(undefined), () => res);
|
|
2359
|
-
}
|
|
2360
|
-
return runAsync();
|
|
2361
|
-
});
|
|
2362
|
-
}
|
|
2363
|
-
if (r !== undefined) {
|
|
2364
|
-
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)) {
|
|
2365
2679
|
return finish(undefined);
|
|
2366
2680
|
}
|
|
2367
|
-
return rpThen(finish(undefined), () =>
|
|
2681
|
+
return rpThen(finish(undefined), () => res);
|
|
2368
2682
|
}
|
|
2369
|
-
return
|
|
2370
|
-
})
|
|
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));
|
|
2371
2691
|
}
|
|
2372
2692
|
|
|
2373
2693
|
const iterResult = new LuaMultiRes(iterCall).flatten();
|
|
2374
2694
|
const nextControl = iterResult.values[0];
|
|
2375
2695
|
if (nextControl === null || nextControl === undefined) {
|
|
2376
|
-
|
|
2377
|
-
if (isPromise(r)) {
|
|
2378
|
-
return (r as Promise<void>);
|
|
2379
|
-
}
|
|
2380
|
-
return;
|
|
2696
|
+
return finish(undefined);
|
|
2381
2697
|
}
|
|
2382
2698
|
control = nextControl;
|
|
2383
2699
|
|
|
@@ -2386,15 +2702,17 @@ export function evalStatement(
|
|
|
2386
2702
|
|
|
2387
2703
|
const r = evalStatement(fi.block, localEnv, sf, returnOnReturn);
|
|
2388
2704
|
if (isPromise(r)) {
|
|
2389
|
-
return (r as Promise<any>)
|
|
2390
|
-
|
|
2391
|
-
if (
|
|
2392
|
-
|
|
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);
|
|
2393
2712
|
}
|
|
2394
|
-
return
|
|
2395
|
-
}
|
|
2396
|
-
|
|
2397
|
-
}).catch((e: any) => finishErr(e));
|
|
2713
|
+
return runSyncFirst();
|
|
2714
|
+
})
|
|
2715
|
+
.catch((e: any) => finishErr(e));
|
|
2398
2716
|
}
|
|
2399
2717
|
if (r !== undefined) {
|
|
2400
2718
|
if (isBreakSignal(r)) {
|
|
@@ -2403,6 +2721,10 @@ export function evalStatement(
|
|
|
2403
2721
|
return rpThen(finish(undefined), () => r);
|
|
2404
2722
|
}
|
|
2405
2723
|
}
|
|
2724
|
+
};
|
|
2725
|
+
|
|
2726
|
+
try {
|
|
2727
|
+
return runSyncFirst();
|
|
2406
2728
|
} catch (e: any) {
|
|
2407
2729
|
return finishErr(e);
|
|
2408
2730
|
}
|
|
@@ -2431,16 +2753,9 @@ function evalLValue(
|
|
|
2431
2753
|
}
|
|
2432
2754
|
case "TableAccess": {
|
|
2433
2755
|
const ta = asLValueTableAccess(lval);
|
|
2434
|
-
const objValue = evalExpression(
|
|
2435
|
-
ta.object,
|
|
2436
|
-
env,
|
|
2437
|
-
sf,
|
|
2438
|
-
);
|
|
2756
|
+
const objValue = evalExpression(ta.object, env, sf);
|
|
2439
2757
|
const keyValue = evalExpression(ta.key, env, sf);
|
|
2440
|
-
if (
|
|
2441
|
-
isPromise(objValue) ||
|
|
2442
|
-
isPromise(keyValue)
|
|
2443
|
-
) {
|
|
2758
|
+
if (isPromise(objValue) || isPromise(keyValue)) {
|
|
2444
2759
|
return Promise.all([
|
|
2445
2760
|
isPromise(objValue) ? objValue : Promise.resolve(objValue),
|
|
2446
2761
|
isPromise(keyValue) ? keyValue : Promise.resolve(keyValue),
|
|
@@ -2456,11 +2771,7 @@ function evalLValue(
|
|
|
2456
2771
|
}
|
|
2457
2772
|
case "PropertyAccess": {
|
|
2458
2773
|
const pa = asLValuePropertyAccess(lval);
|
|
2459
|
-
const objValue = evalExpression(
|
|
2460
|
-
pa.object,
|
|
2461
|
-
env,
|
|
2462
|
-
sf,
|
|
2463
|
-
);
|
|
2774
|
+
const objValue = evalExpression(pa.object, env, sf);
|
|
2464
2775
|
if (isPromise(objValue)) {
|
|
2465
2776
|
return (objValue as Promise<any>).then((ov) => {
|
|
2466
2777
|
return {
|