@silverbulletmd/silverbullet 2.4.2 → 2.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -4
- package/client/markdown_parser/constants.ts +2 -2
- package/client/plugos/hooks/code_widget.ts +0 -3
- package/client/plugos/hooks/document_editor.ts +0 -3
- package/client/plugos/hooks/event.ts +1 -1
- package/client/plugos/hooks/mq.ts +1 -1
- package/client/plugos/hooks/plug_namespace.ts +0 -3
- package/client/plugos/hooks/slash_command.ts +2 -2
- package/client/plugos/plug.ts +0 -1
- package/client/plugos/plug_compile.ts +28 -29
- package/client/plugos/proxy_fetch.ts +1 -1
- package/client/plugos/sandboxes/web_worker_sandbox.ts +1 -1
- package/client/plugos/sandboxes/worker_sandbox.ts +2 -3
- package/client/plugos/syscalls/editor.ts +12 -12
- package/client/plugos/syscalls/fetch.ts +1 -1
- package/client/plugos/syscalls/jsonschema.ts +1 -1
- package/client/plugos/syscalls/mq.ts +1 -1
- package/client/plugos/syscalls/space.ts +1 -1
- package/client/plugos/system.ts +2 -2
- package/client/plugos/worker_runtime.ts +8 -30
- package/client/space_lua/aggregates.ts +209 -0
- package/client/space_lua/ast.ts +24 -2
- package/client/space_lua/eval.ts +58 -53
- package/client/space_lua/labels.ts +1 -1
- package/client/space_lua/parse.ts +117 -12
- package/client/space_lua/query_collection.ts +850 -70
- package/client/space_lua/query_env.ts +26 -0
- package/client/space_lua/runtime.ts +47 -17
- package/client/space_lua/stdlib/format.ts +19 -19
- package/client/space_lua/stdlib/math.ts +73 -48
- package/client/space_lua/stdlib/net.ts +2 -2
- package/client/space_lua/stdlib/os.ts +5 -0
- package/client/space_lua/stdlib/pattern.ts +702 -0
- package/client/space_lua/stdlib/prng.ts +145 -0
- package/client/space_lua/stdlib/space_lua.ts +3 -8
- package/client/space_lua/stdlib/string.ts +103 -181
- package/client/space_lua/stdlib/string_pack.ts +486 -0
- package/client/space_lua/stdlib/table.ts +73 -9
- package/client/space_lua/stdlib.ts +38 -14
- package/client/space_lua/tonumber.ts +3 -2
- package/client/space_lua/util.ts +43 -9
- package/dist/plug-compile.js +23 -69
- package/dist/worker_runtime_bundle.js +233 -0
- package/package.json +10 -5
- package/plug-api/constants.ts +0 -32
- package/plug-api/lib/async.ts +2 -2
- package/plug-api/lib/crypto.ts +11 -11
- package/plug-api/lib/json.ts +1 -1
- package/plug-api/lib/limited_map.ts +1 -1
- package/plug-api/lib/native_fetch.ts +2 -0
- package/plug-api/lib/ref.ts +5 -5
- package/plug-api/lib/transclusion.ts +5 -5
- package/plug-api/lib/tree.ts +50 -2
- package/plug-api/lib/yaml.ts +10 -10
- package/plug-api/syscalls/editor.ts +1 -1
- package/plug-api/system_mock.ts +0 -1
- package/client/plugos/sandboxes/deno_worker_sandbox.ts +0 -6
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aggregate function definitions and execution for LIQ.
|
|
3
|
+
*
|
|
4
|
+
* Built-in aggregates (sum, count, min, max, avg, array_agg) are
|
|
5
|
+
* implemented in TypeScript for speed. Users can override any builtin
|
|
6
|
+
* via `aggregate.define` or `aggregate.update`.
|
|
7
|
+
*
|
|
8
|
+
* Builtins implement ILuaFunction via plain objects rather than
|
|
9
|
+
* LuaBuiltinFunction instances. This avoids ES module TDZ issues:
|
|
10
|
+
* `class` exports are not available during circular module init,
|
|
11
|
+
* but `interface`/`type` imports are.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { ILuaFunction, LuaStackFrame } from "./runtime.ts";
|
|
15
|
+
import {
|
|
16
|
+
luaCall,
|
|
17
|
+
type LuaEnv,
|
|
18
|
+
LuaTable,
|
|
19
|
+
luaTruthy,
|
|
20
|
+
type LuaValue,
|
|
21
|
+
} from "./runtime.ts";
|
|
22
|
+
import type { LuaExpression } from "./ast.ts";
|
|
23
|
+
import { buildItemEnv } from "./query_env.ts";
|
|
24
|
+
|
|
25
|
+
export interface AggregateSpec {
|
|
26
|
+
name: string;
|
|
27
|
+
description?: string;
|
|
28
|
+
initialize: LuaValue; // ILuaFunction
|
|
29
|
+
iterate: LuaValue; // ILuaFunction
|
|
30
|
+
finish?: LuaValue; // ILuaFunction | undefined
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Helper to build an ILuaFunction from a plain function. Equivalent to
|
|
34
|
+
// LuaBuiltinFunction but without referencing the class.
|
|
35
|
+
function aggFn(
|
|
36
|
+
fn: (sf: LuaStackFrame, ...args: LuaValue[]) => LuaValue,
|
|
37
|
+
): ILuaFunction {
|
|
38
|
+
return {
|
|
39
|
+
call(sf: LuaStackFrame, ...args: LuaValue[]) {
|
|
40
|
+
return fn(sf, ...args);
|
|
41
|
+
},
|
|
42
|
+
asString() {
|
|
43
|
+
return "<builtin aggregate>";
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Built-in aggregate specs
|
|
49
|
+
const builtinAggregates: Record<string, AggregateSpec> = {
|
|
50
|
+
sum: {
|
|
51
|
+
name: "sum",
|
|
52
|
+
description: "Sum of numeric values",
|
|
53
|
+
initialize: aggFn((_sf) => 0),
|
|
54
|
+
iterate: aggFn((_sf, state: any, value: any) => {
|
|
55
|
+
if (value === null || value === undefined) return state;
|
|
56
|
+
return (state as number) + (value as number);
|
|
57
|
+
}),
|
|
58
|
+
},
|
|
59
|
+
count: {
|
|
60
|
+
name: "count",
|
|
61
|
+
description: "Count of values; count() with no argument counts all rows",
|
|
62
|
+
initialize: aggFn((_sf) => 0),
|
|
63
|
+
iterate: aggFn((_sf, state: any, value: any) => {
|
|
64
|
+
if (value === null || value === undefined) return state;
|
|
65
|
+
return (state as number) + 1;
|
|
66
|
+
}),
|
|
67
|
+
},
|
|
68
|
+
min: {
|
|
69
|
+
name: "min",
|
|
70
|
+
description: "Minimum value",
|
|
71
|
+
initialize: aggFn((_sf) => null),
|
|
72
|
+
iterate: aggFn((_sf, state: any, value: any) => {
|
|
73
|
+
if (value === null || value === undefined) return state;
|
|
74
|
+
if (state === null || value < state) return value;
|
|
75
|
+
return state;
|
|
76
|
+
}),
|
|
77
|
+
},
|
|
78
|
+
max: {
|
|
79
|
+
name: "max",
|
|
80
|
+
description: "Maximum value",
|
|
81
|
+
initialize: aggFn((_sf) => null),
|
|
82
|
+
iterate: aggFn((_sf, state: any, value: any) => {
|
|
83
|
+
if (value === null || value === undefined) return state;
|
|
84
|
+
if (state === null || value > state) return value;
|
|
85
|
+
return state;
|
|
86
|
+
}),
|
|
87
|
+
},
|
|
88
|
+
avg: {
|
|
89
|
+
name: "avg",
|
|
90
|
+
description: "Average of numeric values",
|
|
91
|
+
initialize: aggFn((_sf) => ({ sum: 0, count: 0 })),
|
|
92
|
+
iterate: aggFn((_sf, state: any, value: any) => {
|
|
93
|
+
if (value === null || value === undefined) return state;
|
|
94
|
+
state.sum += value as number;
|
|
95
|
+
state.count += 1;
|
|
96
|
+
return state;
|
|
97
|
+
}),
|
|
98
|
+
finish: aggFn((_sf, state: any) => {
|
|
99
|
+
if (state.count === 0) return null;
|
|
100
|
+
return state.sum / state.count;
|
|
101
|
+
}),
|
|
102
|
+
},
|
|
103
|
+
array_agg: {
|
|
104
|
+
name: "array_agg",
|
|
105
|
+
description: "Collect values into an array",
|
|
106
|
+
initialize: aggFn((_sf) => new LuaTable()),
|
|
107
|
+
iterate: aggFn((_sf, state: any, value: any) => {
|
|
108
|
+
(state as LuaTable).rawSetArrayIndex(
|
|
109
|
+
(state as LuaTable).length + 1,
|
|
110
|
+
value,
|
|
111
|
+
);
|
|
112
|
+
return state;
|
|
113
|
+
}),
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const noCtx = {};
|
|
118
|
+
|
|
119
|
+
function buildAggCtx(name: string): LuaTable {
|
|
120
|
+
const ctx = new LuaTable();
|
|
121
|
+
ctx.rawSet("name", name);
|
|
122
|
+
const clientConfig = globalThis.client?.config;
|
|
123
|
+
const aggConfig = clientConfig
|
|
124
|
+
? clientConfig.get(`aggregateConfig.${name}`, {})
|
|
125
|
+
: {};
|
|
126
|
+
ctx.rawSet("config", aggConfig);
|
|
127
|
+
return ctx;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function getAggregateSpec(name: string): AggregateSpec | null {
|
|
131
|
+
const clientConfig = globalThis.client?.config;
|
|
132
|
+
if (clientConfig) {
|
|
133
|
+
const spec: any = clientConfig.get(`aggregates.${name}`, null);
|
|
134
|
+
if (spec) {
|
|
135
|
+
let candidate: AggregateSpec | null = null;
|
|
136
|
+
if (spec instanceof LuaTable) {
|
|
137
|
+
const init = spec.rawGet("initialize");
|
|
138
|
+
const iter = spec.rawGet("iterate");
|
|
139
|
+
if (init && iter) {
|
|
140
|
+
candidate = {
|
|
141
|
+
name: spec.rawGet("name") ?? name,
|
|
142
|
+
description: spec.rawGet("description"),
|
|
143
|
+
initialize: init,
|
|
144
|
+
iterate: iter,
|
|
145
|
+
finish: spec.rawGet("finish"),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
} else if (spec.initialize && spec.iterate) {
|
|
149
|
+
candidate = spec as AggregateSpec;
|
|
150
|
+
}
|
|
151
|
+
if (candidate) return candidate;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return builtinAggregates[name] ?? null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Execute an aggregate function over a group of items.
|
|
159
|
+
*/
|
|
160
|
+
export async function executeAggregate(
|
|
161
|
+
spec: AggregateSpec,
|
|
162
|
+
items: LuaTable,
|
|
163
|
+
valueExpr: LuaExpression | null,
|
|
164
|
+
objectVariable: string | undefined,
|
|
165
|
+
env: LuaEnv,
|
|
166
|
+
sf: LuaStackFrame,
|
|
167
|
+
evalExprFn: (
|
|
168
|
+
e: LuaExpression,
|
|
169
|
+
env: LuaEnv,
|
|
170
|
+
sf: LuaStackFrame,
|
|
171
|
+
) => Promise<LuaValue> | LuaValue,
|
|
172
|
+
filterExpr?: LuaExpression,
|
|
173
|
+
): Promise<LuaValue> {
|
|
174
|
+
const ctx = buildAggCtx(spec.name);
|
|
175
|
+
|
|
176
|
+
// Initialize
|
|
177
|
+
let state = await luaCall(spec.initialize, [ctx], noCtx, sf);
|
|
178
|
+
|
|
179
|
+
// Iterate
|
|
180
|
+
const len = items.length;
|
|
181
|
+
for (let i = 1; i <= len; i++) {
|
|
182
|
+
const item = items.rawGet(i);
|
|
183
|
+
|
|
184
|
+
// Filter
|
|
185
|
+
if (filterExpr) {
|
|
186
|
+
const filterEnv = buildItemEnv(objectVariable, item, env, sf);
|
|
187
|
+
const filterResult = await evalExprFn(filterExpr, filterEnv, sf);
|
|
188
|
+
if (!luaTruthy(filterResult)) {
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
let value: LuaValue;
|
|
194
|
+
if (valueExpr === null) {
|
|
195
|
+
value = item;
|
|
196
|
+
} else {
|
|
197
|
+
const itemEnv = buildItemEnv(objectVariable, item, env, sf);
|
|
198
|
+
value = await evalExprFn(valueExpr, itemEnv, sf);
|
|
199
|
+
}
|
|
200
|
+
state = await luaCall(spec.iterate, [state, value, ctx], noCtx, sf);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Finish
|
|
204
|
+
if (spec.finish) {
|
|
205
|
+
state = await luaCall(spec.finish, [state, ctx], noCtx, sf);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return state;
|
|
209
|
+
}
|
package/client/space_lua/ast.ts
CHANGED
|
@@ -175,7 +175,8 @@ export type LuaExpression =
|
|
|
175
175
|
| LuaUnaryExpression
|
|
176
176
|
| LuaTableConstructor
|
|
177
177
|
| LuaFunctionDefinition
|
|
178
|
-
| LuaQueryExpression
|
|
178
|
+
| LuaQueryExpression
|
|
179
|
+
| LuaFilteredCallExpression;
|
|
179
180
|
|
|
180
181
|
export type LuaNilLiteral = {
|
|
181
182
|
type: "Nil";
|
|
@@ -281,6 +282,13 @@ export type LuaFunctionDefinition = {
|
|
|
281
282
|
body: LuaFunctionBody;
|
|
282
283
|
} & ASTContext;
|
|
283
284
|
|
|
285
|
+
// Aggregate with per-row filter
|
|
286
|
+
export type LuaFilteredCallExpression = {
|
|
287
|
+
type: "FilteredCall";
|
|
288
|
+
call: LuaFunctionCallExpression;
|
|
289
|
+
filter: LuaExpression;
|
|
290
|
+
} & ASTContext;
|
|
291
|
+
|
|
284
292
|
// Query stuff
|
|
285
293
|
export type LuaQueryExpression = {
|
|
286
294
|
type: "Query";
|
|
@@ -292,7 +300,9 @@ export type LuaQueryClause =
|
|
|
292
300
|
| LuaWhereClause
|
|
293
301
|
| LuaLimitClause
|
|
294
302
|
| LuaOrderByClause
|
|
295
|
-
| LuaSelectClause
|
|
303
|
+
| LuaSelectClause
|
|
304
|
+
| LuaGroupByClause
|
|
305
|
+
| LuaHavingClause;
|
|
296
306
|
|
|
297
307
|
export type LuaFromClause = {
|
|
298
308
|
type: "From";
|
|
@@ -320,9 +330,21 @@ export type LuaOrderBy = {
|
|
|
320
330
|
type: "Order";
|
|
321
331
|
expression: LuaExpression;
|
|
322
332
|
direction: "asc" | "desc";
|
|
333
|
+
nulls?: "first" | "last";
|
|
334
|
+
using?: string | LuaFunctionBody;
|
|
323
335
|
} & ASTContext;
|
|
324
336
|
|
|
325
337
|
export type LuaSelectClause = {
|
|
326
338
|
type: "Select";
|
|
327
339
|
expression: LuaExpression;
|
|
328
340
|
} & ASTContext;
|
|
341
|
+
|
|
342
|
+
export type LuaGroupByClause = {
|
|
343
|
+
type: "GroupBy";
|
|
344
|
+
expressions: LuaExpression[];
|
|
345
|
+
} & ASTContext;
|
|
346
|
+
|
|
347
|
+
export type LuaHavingClause = {
|
|
348
|
+
type: "Having";
|
|
349
|
+
expression: LuaExpression;
|
|
350
|
+
} & ASTContext;
|
package/client/space_lua/eval.ts
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
luaEnsureCloseStack,
|
|
20
20
|
LuaEnv,
|
|
21
21
|
luaEquals,
|
|
22
|
+
luaFormatNumber,
|
|
22
23
|
LuaFunction,
|
|
23
24
|
luaGet,
|
|
24
25
|
luaIndexValue,
|
|
@@ -37,10 +38,7 @@ import {
|
|
|
37
38
|
luaValueToJS,
|
|
38
39
|
singleResult,
|
|
39
40
|
} from "./runtime.ts";
|
|
40
|
-
import {
|
|
41
|
-
ArrayQueryCollection,
|
|
42
|
-
type LuaCollectionQuery,
|
|
43
|
-
} from "./query_collection.ts";
|
|
41
|
+
import { type LuaCollectionQuery, toCollection } from "./query_collection.ts";
|
|
44
42
|
import {
|
|
45
43
|
coerceNumericPair,
|
|
46
44
|
coerceToNumber,
|
|
@@ -178,17 +176,6 @@ function blockMetaOrThrow(
|
|
|
178
176
|
}
|
|
179
177
|
}
|
|
180
178
|
|
|
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
179
|
|
|
193
180
|
function arithVerbFromOperator(op: string): string | null {
|
|
194
181
|
switch (op) {
|
|
@@ -250,7 +237,7 @@ function arithCoercionErrorOrThrow(
|
|
|
250
237
|
throw e;
|
|
251
238
|
}
|
|
252
239
|
|
|
253
|
-
function luaOp(
|
|
240
|
+
export function luaOp(
|
|
254
241
|
op: string,
|
|
255
242
|
left: any,
|
|
256
243
|
right: any,
|
|
@@ -333,10 +320,10 @@ function luaOp(
|
|
|
333
320
|
return v as string;
|
|
334
321
|
}
|
|
335
322
|
if (typeof v === "number") {
|
|
336
|
-
return
|
|
323
|
+
return luaFormatNumber(v);
|
|
337
324
|
}
|
|
338
325
|
if (isTaggedFloat(v)) {
|
|
339
|
-
return
|
|
326
|
+
return luaFormatNumber(v.value, "float");
|
|
340
327
|
}
|
|
341
328
|
const t = luaTypeName(v);
|
|
342
329
|
throw new LuaRuntimeError(
|
|
@@ -853,22 +840,35 @@ export function evalExpression(
|
|
|
853
840
|
sf.withCtx(q.ctx),
|
|
854
841
|
);
|
|
855
842
|
}
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
collection
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
843
|
+
|
|
844
|
+
// If already a queryable collection (e.g. DataStoreQueryCollection),
|
|
845
|
+
// use directly - skip all LuaTable/JS conversion.
|
|
846
|
+
if (
|
|
847
|
+
typeof collection === "object" &&
|
|
848
|
+
collection !== null &&
|
|
849
|
+
"query" in collection &&
|
|
850
|
+
typeof (collection as any).query === "function"
|
|
851
|
+
) {
|
|
852
|
+
// Already queryable, use as-is
|
|
853
|
+
} else if (collection instanceof LuaTable && collection.empty()) {
|
|
854
|
+
// Empty table → empty array
|
|
855
|
+
collection = toCollection([]);
|
|
856
|
+
} else if (collection instanceof LuaTable) {
|
|
857
|
+
if (collection.length > 0) {
|
|
858
|
+
// Array-like table: extract array items, keep as LuaTables
|
|
859
|
+
const arr: any[] = [];
|
|
860
|
+
for (let i = 1; i <= collection.length; i++) {
|
|
861
|
+
arr.push(collection.rawGet(i));
|
|
862
|
+
}
|
|
863
|
+
collection = toCollection(arr);
|
|
864
|
+
} else {
|
|
865
|
+
// Record-like table (no array part): treat as singleton
|
|
866
|
+
collection = toCollection([collection]);
|
|
869
867
|
}
|
|
870
|
-
|
|
868
|
+
} else {
|
|
869
|
+
collection = toCollection(luaValueToJS(collection, sf));
|
|
871
870
|
}
|
|
871
|
+
|
|
872
872
|
// Build up query object
|
|
873
873
|
const query: LuaCollectionQuery = {
|
|
874
874
|
objectVariable,
|
|
@@ -886,6 +886,8 @@ export function evalExpression(
|
|
|
886
886
|
query.orderBy = clause.orderBy.map((o) => ({
|
|
887
887
|
expr: o.expression,
|
|
888
888
|
desc: o.direction === "desc",
|
|
889
|
+
nulls: o.nulls,
|
|
890
|
+
using: o.using,
|
|
889
891
|
}));
|
|
890
892
|
break;
|
|
891
893
|
}
|
|
@@ -906,12 +908,19 @@ export function evalExpression(
|
|
|
906
908
|
}
|
|
907
909
|
break;
|
|
908
910
|
}
|
|
911
|
+
case "GroupBy": {
|
|
912
|
+
query.groupBy = clause.expressions;
|
|
913
|
+
break;
|
|
914
|
+
}
|
|
915
|
+
case "Having": {
|
|
916
|
+
query.having = clause.expression;
|
|
917
|
+
break;
|
|
918
|
+
}
|
|
909
919
|
}
|
|
910
920
|
}
|
|
911
921
|
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
);
|
|
922
|
+
// Always use the possibly-wrapped collection
|
|
923
|
+
return (collection as any).query(query, env, sf).then(jsToLuaValue);
|
|
915
924
|
},
|
|
916
925
|
);
|
|
917
926
|
}
|
|
@@ -1410,9 +1419,9 @@ function evalExpressions(
|
|
|
1410
1419
|
}
|
|
1411
1420
|
|
|
1412
1421
|
type EvalBlockResult =
|
|
1413
|
-
|
|
|
1422
|
+
| undefined
|
|
1414
1423
|
| ControlSignal
|
|
1415
|
-
| Promise<
|
|
1424
|
+
| Promise<undefined | ControlSignal>;
|
|
1416
1425
|
|
|
1417
1426
|
function runStatementsNoGoto(
|
|
1418
1427
|
stmts: LuaStatement[],
|
|
@@ -1420,10 +1429,10 @@ function runStatementsNoGoto(
|
|
|
1420
1429
|
sf: LuaStackFrame,
|
|
1421
1430
|
returnOnReturn: boolean,
|
|
1422
1431
|
startIdx: number,
|
|
1423
|
-
):
|
|
1432
|
+
): undefined | ControlSignal | Promise<undefined | ControlSignal> {
|
|
1424
1433
|
const processFrom = (
|
|
1425
1434
|
idx: number,
|
|
1426
|
-
):
|
|
1435
|
+
): undefined | ControlSignal | Promise<undefined | ControlSignal> => {
|
|
1427
1436
|
for (let i = idx; i < stmts.length; i++) {
|
|
1428
1437
|
const result = evalStatement(
|
|
1429
1438
|
stmts[i],
|
|
@@ -1621,7 +1630,7 @@ export function evalStatement(
|
|
|
1621
1630
|
env: LuaEnv,
|
|
1622
1631
|
sf: LuaStackFrame,
|
|
1623
1632
|
returnOnReturn = false,
|
|
1624
|
-
):
|
|
1633
|
+
): undefined | ControlSignal | Promise<undefined | ControlSignal> {
|
|
1625
1634
|
switch (s.type) {
|
|
1626
1635
|
case "Assignment": {
|
|
1627
1636
|
const a = asAssignment(s);
|
|
@@ -1773,7 +1782,7 @@ export function evalStatement(
|
|
|
1773
1782
|
return rpThen(rp, onValue) as any;
|
|
1774
1783
|
};
|
|
1775
1784
|
|
|
1776
|
-
return runFrom(0)
|
|
1785
|
+
return runFrom(0) as undefined | Promise<undefined>;
|
|
1777
1786
|
}
|
|
1778
1787
|
case "Semicolon": {
|
|
1779
1788
|
return;
|
|
@@ -1826,9 +1835,9 @@ export function evalStatement(
|
|
|
1826
1835
|
const runFrom = (
|
|
1827
1836
|
i: number,
|
|
1828
1837
|
):
|
|
1829
|
-
|
|
|
1838
|
+
| undefined
|
|
1830
1839
|
| ControlSignal
|
|
1831
|
-
| Promise<
|
|
1840
|
+
| Promise<undefined | ControlSignal> => {
|
|
1832
1841
|
if (i >= conds.length) {
|
|
1833
1842
|
if (iff.elseBlock) {
|
|
1834
1843
|
return evalStatement(iff.elseBlock, env, sf, returnOnReturn);
|
|
@@ -1855,7 +1864,7 @@ export function evalStatement(
|
|
|
1855
1864
|
case "While": {
|
|
1856
1865
|
const w = asWhile(s);
|
|
1857
1866
|
|
|
1858
|
-
const runAsync = async (): Promise<
|
|
1867
|
+
const runAsync = async (): Promise<undefined | ControlSignal> => {
|
|
1859
1868
|
while (true) {
|
|
1860
1869
|
const c = await evalExpression(w.condition, env, sf);
|
|
1861
1870
|
if (!luaTruthy(c)) {
|
|
@@ -1880,7 +1889,6 @@ export function evalStatement(
|
|
|
1880
1889
|
if (!luaTruthy(cv)) {
|
|
1881
1890
|
return;
|
|
1882
1891
|
}
|
|
1883
|
-
try {
|
|
1884
1892
|
const r = evalStatement(w.block, env, sf, returnOnReturn);
|
|
1885
1893
|
if (isPromise(r)) {
|
|
1886
1894
|
return (r as Promise<any>).then((res) => {
|
|
@@ -1900,9 +1908,6 @@ export function evalStatement(
|
|
|
1900
1908
|
return r;
|
|
1901
1909
|
}
|
|
1902
1910
|
return runAsync();
|
|
1903
|
-
} catch (e: any) {
|
|
1904
|
-
throw e;
|
|
1905
|
-
}
|
|
1906
1911
|
});
|
|
1907
1912
|
}
|
|
1908
1913
|
if (!luaTruthy(c)) {
|
|
@@ -1932,7 +1937,7 @@ export function evalStatement(
|
|
|
1932
1937
|
case "Repeat": {
|
|
1933
1938
|
const r = asRepeat(s);
|
|
1934
1939
|
|
|
1935
|
-
const runAsync = async (): Promise<
|
|
1940
|
+
const runAsync = async (): Promise<undefined | ControlSignal> => {
|
|
1936
1941
|
while (true) {
|
|
1937
1942
|
const rr = evalStatement(r.block, env, sf, returnOnReturn);
|
|
1938
1943
|
const res = isPromise(rr) ? await rr : rr;
|
|
@@ -2101,7 +2106,7 @@ export function evalStatement(
|
|
|
2101
2106
|
loopEnv: LuaEnv,
|
|
2102
2107
|
i: number,
|
|
2103
2108
|
loopType: NumericType,
|
|
2104
|
-
):
|
|
2109
|
+
): undefined | ControlSignal | Promise<undefined | ControlSignal> => {
|
|
2105
2110
|
loopEnv.setLocal(fr.name, wrapLoopVar(i, loopType));
|
|
2106
2111
|
return evalStatement(fr.block, loopEnv, sf, returnOnReturn);
|
|
2107
2112
|
}
|
|
@@ -2109,7 +2114,7 @@ export function evalStatement(
|
|
|
2109
2114
|
_loopEnv: LuaEnv,
|
|
2110
2115
|
i: number,
|
|
2111
2116
|
loopType: NumericType,
|
|
2112
|
-
):
|
|
2117
|
+
): undefined | ControlSignal | Promise<undefined | ControlSignal> => {
|
|
2113
2118
|
const localEnv = new LuaEnv(env);
|
|
2114
2119
|
localEnv.setLocal(fr.name, wrapLoopVar(i, loopType));
|
|
2115
2120
|
return evalStatement(fr.block, localEnv, sf, returnOnReturn);
|
|
@@ -2148,9 +2153,9 @@ export function evalStatement(
|
|
|
2148
2153
|
step: number,
|
|
2149
2154
|
loopType: NumericType,
|
|
2150
2155
|
):
|
|
2151
|
-
|
|
|
2156
|
+
| undefined
|
|
2152
2157
|
| ControlSignal
|
|
2153
|
-
| Promise<
|
|
2158
|
+
| Promise<undefined | ControlSignal> => {
|
|
2154
2159
|
if (step === 0) {
|
|
2155
2160
|
throw new LuaRuntimeError("'for' step is zero", sf.withCtx(fr.ctx));
|
|
2156
2161
|
}
|
|
@@ -122,7 +122,7 @@ function resolveFunction(root: LuaBlock): FunctionMeta {
|
|
|
122
122
|
|
|
123
123
|
while (searchBlock) {
|
|
124
124
|
const meta = blockMeta.get(searchBlock);
|
|
125
|
-
if (meta
|
|
125
|
+
if (meta?.labels.has(target)) {
|
|
126
126
|
labelIndex = meta.labels.get(target);
|
|
127
127
|
labelDefBlock = searchBlock;
|
|
128
128
|
break;
|