@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
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
cleanTree,
|
|
5
5
|
type ParseTree,
|
|
6
6
|
} from "@silverbulletmd/silverbullet/lib/tree";
|
|
7
|
+
// @ts-expect-error - Local generated JavaScript file without type definitions
|
|
7
8
|
import { parser } from "./parse-lua.js";
|
|
8
9
|
import { styleTags, tags as t } from "@lezer/highlight";
|
|
9
10
|
import { indentNodeProp, LRLanguage } from "@codemirror/language";
|
|
@@ -35,7 +36,7 @@ const luaStyleTags = styleTags({
|
|
|
35
36
|
CompareOp: t.operator,
|
|
36
37
|
"true false": t.bool,
|
|
37
38
|
Comment: t.lineComment,
|
|
38
|
-
"return break goto do end while repeat until function local if then else elseif in for nil or and not query from where limit select order by desc":
|
|
39
|
+
"return break goto do end while repeat until function local if then else elseif in for nil or and not query from where limit offset select order by desc asc nulls first last group having filter using":
|
|
39
40
|
t.keyword,
|
|
40
41
|
});
|
|
41
42
|
|
|
@@ -51,10 +52,7 @@ const customIndent = indentNodeProp.add({
|
|
|
51
52
|
export const luaLanguage = LRLanguage.define({
|
|
52
53
|
name: "space-lua",
|
|
53
54
|
parser: parser.configure({
|
|
54
|
-
props: [
|
|
55
|
-
luaStyleTags,
|
|
56
|
-
customIndent,
|
|
57
|
-
],
|
|
55
|
+
props: [luaStyleTags, customIndent],
|
|
58
56
|
}),
|
|
59
57
|
languageData: {
|
|
60
58
|
commentTokens: { line: "--", block: { open: "--[[", close: "--]]" } },
|
|
@@ -96,8 +94,9 @@ function expressionHasFunctionDef(e: LuaExpression): boolean {
|
|
|
96
94
|
}
|
|
97
95
|
return false;
|
|
98
96
|
case "Binary":
|
|
99
|
-
return
|
|
100
|
-
expressionHasFunctionDef(e.right)
|
|
97
|
+
return (
|
|
98
|
+
expressionHasFunctionDef(e.left) || expressionHasFunctionDef(e.right)
|
|
99
|
+
);
|
|
101
100
|
case "Unary":
|
|
102
101
|
return expressionHasFunctionDef(e.argument);
|
|
103
102
|
case "Parenthesized":
|
|
@@ -118,8 +117,9 @@ function expressionHasFunctionDef(e: LuaExpression): boolean {
|
|
|
118
117
|
}
|
|
119
118
|
return false;
|
|
120
119
|
case "TableAccess":
|
|
121
|
-
return
|
|
122
|
-
expressionHasFunctionDef(e.key)
|
|
120
|
+
return (
|
|
121
|
+
expressionHasFunctionDef(e.object) || expressionHasFunctionDef(e.key)
|
|
122
|
+
);
|
|
123
123
|
case "PropertyAccess":
|
|
124
124
|
return expressionHasFunctionDef(e.object);
|
|
125
125
|
case "Query":
|
|
@@ -127,10 +127,23 @@ function expressionHasFunctionDef(e: LuaExpression): boolean {
|
|
|
127
127
|
const c = e.clauses[i];
|
|
128
128
|
switch (c.type) {
|
|
129
129
|
case "From":
|
|
130
|
-
|
|
130
|
+
case "Select":
|
|
131
|
+
case "GroupBy":
|
|
132
|
+
for (const f of c.fields) {
|
|
133
|
+
switch (f.type) {
|
|
134
|
+
case "DynamicField":
|
|
135
|
+
if (expressionHasFunctionDef(f.key)) return true;
|
|
136
|
+
if (expressionHasFunctionDef(f.value)) return true;
|
|
137
|
+
break;
|
|
138
|
+
case "PropField":
|
|
139
|
+
case "ExpressionField":
|
|
140
|
+
if (expressionHasFunctionDef(f.value)) return true;
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
131
144
|
break;
|
|
132
145
|
case "Where":
|
|
133
|
-
case "
|
|
146
|
+
case "Having":
|
|
134
147
|
if (expressionHasFunctionDef(c.expression)) return true;
|
|
135
148
|
break;
|
|
136
149
|
case "Limit":
|
|
@@ -142,11 +155,30 @@ function expressionHasFunctionDef(e: LuaExpression): boolean {
|
|
|
142
155
|
if (expressionHasFunctionDef(c.orderBy[j].expression)) {
|
|
143
156
|
return true;
|
|
144
157
|
}
|
|
158
|
+
if (
|
|
159
|
+
c.orderBy[j].using &&
|
|
160
|
+
typeof c.orderBy[j].using !== "string"
|
|
161
|
+
) {
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
145
164
|
}
|
|
146
165
|
break;
|
|
147
166
|
}
|
|
148
167
|
}
|
|
149
168
|
return false;
|
|
169
|
+
case "FilteredCall":
|
|
170
|
+
return (
|
|
171
|
+
expressionHasFunctionDef(e.call) || expressionHasFunctionDef(e.filter)
|
|
172
|
+
);
|
|
173
|
+
case "AggregateCall":
|
|
174
|
+
return (
|
|
175
|
+
expressionHasFunctionDef((e as any).call) ||
|
|
176
|
+
((e as any).orderBy as LuaOrderBy[]).some(
|
|
177
|
+
(ob) =>
|
|
178
|
+
expressionHasFunctionDef(ob.expression) ||
|
|
179
|
+
(ob.using && typeof ob.using !== "string"),
|
|
180
|
+
)
|
|
181
|
+
);
|
|
150
182
|
default:
|
|
151
183
|
return false;
|
|
152
184
|
}
|
|
@@ -170,8 +202,10 @@ function exprReferencesNames(e: LuaExpression, names: Set<string>): boolean {
|
|
|
170
202
|
case "Variable":
|
|
171
203
|
return names.has(e.name);
|
|
172
204
|
case "Binary":
|
|
173
|
-
return
|
|
174
|
-
exprReferencesNames(e.
|
|
205
|
+
return (
|
|
206
|
+
exprReferencesNames(e.left, names) ||
|
|
207
|
+
exprReferencesNames(e.right, names)
|
|
208
|
+
);
|
|
175
209
|
case "Unary":
|
|
176
210
|
return exprReferencesNames(e.argument, names);
|
|
177
211
|
case "Parenthesized":
|
|
@@ -183,8 +217,10 @@ function exprReferencesNames(e: LuaExpression, names: Set<string>): boolean {
|
|
|
183
217
|
}
|
|
184
218
|
return false;
|
|
185
219
|
case "TableAccess":
|
|
186
|
-
return
|
|
187
|
-
exprReferencesNames(e.
|
|
220
|
+
return (
|
|
221
|
+
exprReferencesNames(e.object, names) ||
|
|
222
|
+
exprReferencesNames(e.key, names)
|
|
223
|
+
);
|
|
188
224
|
case "PropertyAccess":
|
|
189
225
|
return exprReferencesNames(e.object, names);
|
|
190
226
|
case "TableConstructor":
|
|
@@ -209,10 +245,23 @@ function exprReferencesNames(e: LuaExpression, names: Set<string>): boolean {
|
|
|
209
245
|
const c = e.clauses[i];
|
|
210
246
|
switch (c.type) {
|
|
211
247
|
case "From":
|
|
212
|
-
|
|
248
|
+
case "Select":
|
|
249
|
+
case "GroupBy":
|
|
250
|
+
for (const f of c.fields) {
|
|
251
|
+
switch (f.type) {
|
|
252
|
+
case "DynamicField":
|
|
253
|
+
if (exprReferencesNames(f.key, names)) return true;
|
|
254
|
+
if (exprReferencesNames(f.value, names)) return true;
|
|
255
|
+
break;
|
|
256
|
+
case "PropField":
|
|
257
|
+
case "ExpressionField":
|
|
258
|
+
if (exprReferencesNames(f.value, names)) return true;
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
213
262
|
break;
|
|
214
263
|
case "Where":
|
|
215
|
-
case "
|
|
264
|
+
case "Having":
|
|
216
265
|
if (exprReferencesNames(c.expression, names)) return true;
|
|
217
266
|
break;
|
|
218
267
|
case "Limit":
|
|
@@ -224,11 +273,31 @@ function exprReferencesNames(e: LuaExpression, names: Set<string>): boolean {
|
|
|
224
273
|
if (exprReferencesNames(c.orderBy[j].expression, names)) {
|
|
225
274
|
return true;
|
|
226
275
|
}
|
|
276
|
+
if (
|
|
277
|
+
typeof c.orderBy[j].using === "string" &&
|
|
278
|
+
names.has(c.orderBy[j].using as string)
|
|
279
|
+
) {
|
|
280
|
+
return true;
|
|
281
|
+
}
|
|
227
282
|
}
|
|
228
283
|
break;
|
|
229
284
|
}
|
|
230
285
|
}
|
|
231
286
|
return false;
|
|
287
|
+
case "FilteredCall":
|
|
288
|
+
return (
|
|
289
|
+
exprReferencesNames(e.call, names) ||
|
|
290
|
+
exprReferencesNames(e.filter, names)
|
|
291
|
+
);
|
|
292
|
+
case "AggregateCall": {
|
|
293
|
+
const ac = e as any;
|
|
294
|
+
if (exprReferencesNames(ac.call, names)) return true;
|
|
295
|
+
for (const ob of ac.orderBy as LuaOrderBy[]) {
|
|
296
|
+
if (exprReferencesNames(ob.expression, names)) return true;
|
|
297
|
+
if (typeof ob.using === "string" && names.has(ob.using)) return true;
|
|
298
|
+
}
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
232
301
|
default:
|
|
233
302
|
return false;
|
|
234
303
|
}
|
|
@@ -241,8 +310,10 @@ function lvalueReferencesNames(lv: LuaLValue, names: Set<string>): boolean {
|
|
|
241
310
|
case "PropertyAccess":
|
|
242
311
|
return exprReferencesNames(lv.object as LuaExpression, names);
|
|
243
312
|
case "TableAccess":
|
|
244
|
-
return
|
|
245
|
-
exprReferencesNames(lv.
|
|
313
|
+
return (
|
|
314
|
+
exprReferencesNames(lv.object as LuaExpression, names) ||
|
|
315
|
+
exprReferencesNames(lv.key, names)
|
|
316
|
+
);
|
|
246
317
|
}
|
|
247
318
|
}
|
|
248
319
|
|
|
@@ -375,10 +446,7 @@ function blockCapturesNames(block: LuaBlock, names: Set<string>): boolean {
|
|
|
375
446
|
return false;
|
|
376
447
|
}
|
|
377
448
|
|
|
378
|
-
function statementCapturesNames(
|
|
379
|
-
s: LuaStatement,
|
|
380
|
-
names: Set<string>,
|
|
381
|
-
): boolean {
|
|
449
|
+
function statementCapturesNames(s: LuaStatement, names: Set<string>): boolean {
|
|
382
450
|
switch (s.type) {
|
|
383
451
|
case "Local": {
|
|
384
452
|
const exprs = (s as any).expressions as LuaExpression[] | undefined;
|
|
@@ -471,8 +539,9 @@ function exprCapturesNames(e: LuaExpression, names: Set<string>): boolean {
|
|
|
471
539
|
case "FunctionDefinition":
|
|
472
540
|
return functionBodyCapturesNames(e.body, names);
|
|
473
541
|
case "Binary":
|
|
474
|
-
return
|
|
475
|
-
exprCapturesNames(e.right, names)
|
|
542
|
+
return (
|
|
543
|
+
exprCapturesNames(e.left, names) || exprCapturesNames(e.right, names)
|
|
544
|
+
);
|
|
476
545
|
case "Unary":
|
|
477
546
|
return exprCapturesNames(e.argument, names);
|
|
478
547
|
case "Parenthesized":
|
|
@@ -484,8 +553,9 @@ function exprCapturesNames(e: LuaExpression, names: Set<string>): boolean {
|
|
|
484
553
|
}
|
|
485
554
|
return false;
|
|
486
555
|
case "TableAccess":
|
|
487
|
-
return
|
|
488
|
-
exprCapturesNames(e.key, names)
|
|
556
|
+
return (
|
|
557
|
+
exprCapturesNames(e.object, names) || exprCapturesNames(e.key, names)
|
|
558
|
+
);
|
|
489
559
|
case "PropertyAccess":
|
|
490
560
|
return exprCapturesNames(e.object, names);
|
|
491
561
|
case "TableConstructor":
|
|
@@ -508,10 +578,23 @@ function exprCapturesNames(e: LuaExpression, names: Set<string>): boolean {
|
|
|
508
578
|
const c = e.clauses[i];
|
|
509
579
|
switch (c.type) {
|
|
510
580
|
case "From":
|
|
511
|
-
|
|
581
|
+
case "Select":
|
|
582
|
+
case "GroupBy":
|
|
583
|
+
for (const f of c.fields) {
|
|
584
|
+
switch (f.type) {
|
|
585
|
+
case "DynamicField":
|
|
586
|
+
if (exprCapturesNames(f.key, names)) return true;
|
|
587
|
+
if (exprCapturesNames(f.value, names)) return true;
|
|
588
|
+
break;
|
|
589
|
+
case "PropField":
|
|
590
|
+
case "ExpressionField":
|
|
591
|
+
if (exprCapturesNames(f.value, names)) return true;
|
|
592
|
+
break;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
512
595
|
break;
|
|
513
596
|
case "Where":
|
|
514
|
-
case "
|
|
597
|
+
case "Having":
|
|
515
598
|
if (exprCapturesNames(c.expression, names)) return true;
|
|
516
599
|
break;
|
|
517
600
|
case "Limit":
|
|
@@ -523,11 +606,31 @@ function exprCapturesNames(e: LuaExpression, names: Set<string>): boolean {
|
|
|
523
606
|
if (exprCapturesNames(c.orderBy[j].expression, names)) {
|
|
524
607
|
return true;
|
|
525
608
|
}
|
|
609
|
+
const u = c.orderBy[j].using;
|
|
610
|
+
if (u && typeof u !== "string") {
|
|
611
|
+
if (functionBodyCapturesNames(u, names)) return true;
|
|
612
|
+
}
|
|
526
613
|
}
|
|
527
614
|
break;
|
|
528
615
|
}
|
|
529
616
|
}
|
|
530
617
|
return false;
|
|
618
|
+
case "FilteredCall":
|
|
619
|
+
return (
|
|
620
|
+
exprCapturesNames(e.call, names) || exprCapturesNames(e.filter, names)
|
|
621
|
+
);
|
|
622
|
+
case "AggregateCall": {
|
|
623
|
+
const ac = e as any;
|
|
624
|
+
if (exprCapturesNames(ac.call, names)) return true;
|
|
625
|
+
for (const ob of ac.orderBy as LuaOrderBy[]) {
|
|
626
|
+
if (exprCapturesNames(ob.expression, names)) return true;
|
|
627
|
+
const u = ob.using;
|
|
628
|
+
if (u && typeof u !== "string") {
|
|
629
|
+
if (functionBodyCapturesNames(u, names)) return true;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
return false;
|
|
633
|
+
}
|
|
531
634
|
default:
|
|
532
635
|
return false;
|
|
533
636
|
}
|
|
@@ -537,7 +640,7 @@ function parseBlock(t: ParseTree, ctx: ASTCtx): LuaBlock {
|
|
|
537
640
|
if (t.type !== "Block") {
|
|
538
641
|
throw new Error(`Expected Block, got ${t.type}`);
|
|
539
642
|
}
|
|
540
|
-
const stmtNodes = t.children!.filter((c) => c
|
|
643
|
+
const stmtNodes = t.children!.filter((c) => c?.type);
|
|
541
644
|
const statements = stmtNodes.map((s) => parseStatement(s, ctx));
|
|
542
645
|
const block: LuaBlock = { type: "Block", statements, ctx: context(t, ctx) };
|
|
543
646
|
|
|
@@ -595,7 +698,8 @@ function parseBlock(t: ParseTree, ctx: ASTCtx): LuaBlock {
|
|
|
595
698
|
case "FunctionCallStatement": {
|
|
596
699
|
if (!hasFunctionDef) {
|
|
597
700
|
const call = (s as any).call as LuaFunctionCallExpression;
|
|
598
|
-
hasFunctionDef =
|
|
701
|
+
hasFunctionDef =
|
|
702
|
+
expressionHasFunctionDef(call.prefix) ||
|
|
599
703
|
expressionsHaveFunctionDef(call.args);
|
|
600
704
|
}
|
|
601
705
|
break;
|
|
@@ -662,7 +766,8 @@ function parseBlock(t: ParseTree, ctx: ASTCtx): LuaBlock {
|
|
|
662
766
|
hasCloseHere = hasCloseHere || !!child.hasCloseHere;
|
|
663
767
|
hasFunctionDef = hasFunctionDef || !!child.hasFunctionDef;
|
|
664
768
|
if (!hasFunctionDef) {
|
|
665
|
-
hasFunctionDef =
|
|
769
|
+
hasFunctionDef =
|
|
770
|
+
expressionHasFunctionDef((s as any).start) ||
|
|
666
771
|
expressionHasFunctionDef((s as any).end) ||
|
|
667
772
|
((s as any).step
|
|
668
773
|
? expressionHasFunctionDef((s as any).step)
|
|
@@ -765,7 +870,7 @@ function parseStatement(t: ParseTree, ctx: ASTCtx): LuaStatement {
|
|
|
765
870
|
from?: number;
|
|
766
871
|
to?: number;
|
|
767
872
|
}[] = [];
|
|
768
|
-
let elseBlock: LuaBlock | undefined
|
|
873
|
+
let elseBlock: LuaBlock | undefined;
|
|
769
874
|
for (let i = 0; i < t.children!.length; i += 4) {
|
|
770
875
|
const child = t.children![i];
|
|
771
876
|
if (!child || !child.children || !child.children[0]) {
|
|
@@ -855,8 +960,8 @@ function parseStatement(t: ParseTree, ctx: ASTCtx): LuaStatement {
|
|
|
855
960
|
case "Assign":
|
|
856
961
|
return {
|
|
857
962
|
type: "Assignment",
|
|
858
|
-
variables: t
|
|
859
|
-
.filter((c) => c.type && c.type !== ",")
|
|
963
|
+
variables: t
|
|
964
|
+
.children![0].children!.filter((c) => c.type && c.type !== ",")
|
|
860
965
|
.map((lvalue) => parseLValue(lvalue, ctx)),
|
|
861
966
|
expressions: parseExpList(t.children![2], ctx),
|
|
862
967
|
ctx: context(t, ctx),
|
|
@@ -900,9 +1005,7 @@ function parseStatement(t: ParseTree, ctx: ASTCtx): LuaStatement {
|
|
|
900
1005
|
console.error(t);
|
|
901
1006
|
throw new Error(
|
|
902
1007
|
`Unknown statement type: ${
|
|
903
|
-
t.children![0]
|
|
904
|
-
? t.children![0].text
|
|
905
|
-
: String(t.type)
|
|
1008
|
+
t.children![0]?.text ? t.children![0].text : String(t.type)
|
|
906
1009
|
}`,
|
|
907
1010
|
);
|
|
908
1011
|
}
|
|
@@ -913,28 +1016,44 @@ function parseFunctionCall(
|
|
|
913
1016
|
ctx: ASTCtx,
|
|
914
1017
|
): LuaFunctionCallExpression {
|
|
915
1018
|
if (t.children![1] && t.children![1].type === ":") {
|
|
916
|
-
|
|
1019
|
+
const { args, aggOrderBy } = parseFunctionArgsWithOrderBy(
|
|
1020
|
+
t.children!.slice(3),
|
|
1021
|
+
ctx,
|
|
1022
|
+
);
|
|
1023
|
+
const result: LuaFunctionCallExpression = {
|
|
917
1024
|
type: "FunctionCall",
|
|
918
1025
|
prefix: parsePrefixExpression(t.children![0], ctx),
|
|
919
1026
|
name: t.children![2].children![0].text!,
|
|
920
|
-
args
|
|
1027
|
+
args,
|
|
921
1028
|
ctx: context(t, ctx),
|
|
922
1029
|
};
|
|
1030
|
+
if (aggOrderBy) {
|
|
1031
|
+
(result as any).orderBy = aggOrderBy;
|
|
1032
|
+
}
|
|
1033
|
+
return result;
|
|
923
1034
|
}
|
|
924
|
-
|
|
1035
|
+
const { args, aggOrderBy } = parseFunctionArgsWithOrderBy(
|
|
1036
|
+
t.children!.slice(1),
|
|
1037
|
+
ctx,
|
|
1038
|
+
);
|
|
1039
|
+
const result: LuaFunctionCallExpression = {
|
|
925
1040
|
type: "FunctionCall",
|
|
926
1041
|
prefix: parsePrefixExpression(t.children![0], ctx),
|
|
927
|
-
args
|
|
1042
|
+
args,
|
|
928
1043
|
ctx: context(t, ctx),
|
|
929
1044
|
};
|
|
1045
|
+
if (aggOrderBy) {
|
|
1046
|
+
(result as any).orderBy = aggOrderBy;
|
|
1047
|
+
}
|
|
1048
|
+
return result;
|
|
930
1049
|
}
|
|
931
1050
|
|
|
932
1051
|
function parseAttNames(t: ParseTree, ctx: ASTCtx): LuaAttName[] {
|
|
933
1052
|
if (t.type !== "AttNameList") {
|
|
934
1053
|
throw new Error(`Expected AttNameList, got ${t.type}`);
|
|
935
1054
|
}
|
|
936
|
-
return t
|
|
937
|
-
.filter((c) => c.type && c.type !== ",")
|
|
1055
|
+
return t
|
|
1056
|
+
.children!.filter((c) => c.type && c.type !== ",")
|
|
938
1057
|
.map((att) => parseAttName(att, ctx));
|
|
939
1058
|
}
|
|
940
1059
|
|
|
@@ -995,7 +1114,7 @@ function parseFunctionName(t: ParseTree, ctx: ASTCtx): LuaFunctionName {
|
|
|
995
1114
|
throw new Error(`Expected FunctionName, got ${t.type}`);
|
|
996
1115
|
}
|
|
997
1116
|
const propNames: string[] = [];
|
|
998
|
-
let colonName: string | undefined
|
|
1117
|
+
let colonName: string | undefined;
|
|
999
1118
|
for (let i = 0; i < t.children!.length; i += 2) {
|
|
1000
1119
|
const prop = t.children![i];
|
|
1001
1120
|
propNames.push(prop.children![0].text!);
|
|
@@ -1016,8 +1135,8 @@ function parseNameList(t: ParseTree): string[] {
|
|
|
1016
1135
|
if (t.type !== "NameList") {
|
|
1017
1136
|
throw new Error(`Expected NameList, got ${t.type}`);
|
|
1018
1137
|
}
|
|
1019
|
-
return t
|
|
1020
|
-
.filter((c) => c.type === "Name")
|
|
1138
|
+
return t
|
|
1139
|
+
.children!.filter((c) => c.type === "Name")
|
|
1021
1140
|
.map((c) => c.children![0].text!);
|
|
1022
1141
|
}
|
|
1023
1142
|
|
|
@@ -1025,8 +1144,8 @@ function parseExpList(t: ParseTree, ctx: ASTCtx): LuaExpression[] {
|
|
|
1025
1144
|
if (t.type !== "ExpList") {
|
|
1026
1145
|
throw new Error(`Expected ExpList, got ${t.type}`);
|
|
1027
1146
|
}
|
|
1028
|
-
return t
|
|
1029
|
-
.filter((c) => c.type && c.type !== ",")
|
|
1147
|
+
return t
|
|
1148
|
+
.children!.filter((c) => c.type && c.type !== ",")
|
|
1030
1149
|
.map((e) => parseExpression(e, ctx));
|
|
1031
1150
|
}
|
|
1032
1151
|
|
|
@@ -1045,44 +1164,46 @@ function parseString(s: string): string {
|
|
|
1045
1164
|
}
|
|
1046
1165
|
return text;
|
|
1047
1166
|
}
|
|
1048
|
-
return s
|
|
1049
|
-
|
|
1050
|
-
(
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1167
|
+
return s
|
|
1168
|
+
.slice(1, -1)
|
|
1169
|
+
.replace(
|
|
1170
|
+
/\\(x[0-9a-fA-F]{2}|u\{[0-9a-fA-F]+\}|[abfnrtv\\'"n])/g,
|
|
1171
|
+
(match, capture) => {
|
|
1172
|
+
switch (capture) {
|
|
1173
|
+
case "a":
|
|
1174
|
+
return "\x07"; // Bell
|
|
1175
|
+
case "b":
|
|
1176
|
+
return "\b"; // Backspace
|
|
1177
|
+
case "f":
|
|
1178
|
+
return "\f"; // Form feed
|
|
1179
|
+
case "n":
|
|
1180
|
+
return "\n"; // Newline
|
|
1181
|
+
case "r":
|
|
1182
|
+
return "\r"; // Carriage return
|
|
1183
|
+
case "t":
|
|
1184
|
+
return "\t"; // Horizontal tab
|
|
1185
|
+
case "v":
|
|
1186
|
+
return "\v"; // Vertical tab
|
|
1187
|
+
case "\\":
|
|
1188
|
+
return "\\"; // Backslash
|
|
1189
|
+
case '"':
|
|
1190
|
+
return '"'; // Double quote
|
|
1191
|
+
case "'":
|
|
1192
|
+
return "'"; // Single quote
|
|
1193
|
+
default:
|
|
1194
|
+
// Handle hexadecimal \x00
|
|
1195
|
+
if (capture.startsWith("x")) {
|
|
1196
|
+
return String.fromCharCode(parseInt(capture.slice(1), 16));
|
|
1197
|
+
}
|
|
1198
|
+
// Handle unicode \u{XXXX}
|
|
1199
|
+
if (capture.startsWith("u{")) {
|
|
1200
|
+
const codePoint = parseInt(capture.slice(2, -1), 16);
|
|
1201
|
+
return String.fromCodePoint(codePoint);
|
|
1202
|
+
}
|
|
1203
|
+
return match; // return the original match if nothing fits
|
|
1204
|
+
}
|
|
1205
|
+
},
|
|
1206
|
+
);
|
|
1086
1207
|
}
|
|
1087
1208
|
|
|
1088
1209
|
function parseExpression(t: ParseTree, ctx: ASTCtx): LuaExpression {
|
|
@@ -1103,8 +1224,9 @@ function parseExpression(t: ParseTree, ctx: ASTCtx): LuaExpression {
|
|
|
1103
1224
|
return {
|
|
1104
1225
|
type: "Number",
|
|
1105
1226
|
// Use the integer parser fox 0x literals
|
|
1227
|
+
// biome-ignore lint/correctness/useParseIntRadix: hex strings need auto-detect radix
|
|
1106
1228
|
value: text.includes("x") ? parseInt(text) : parseFloat(text),
|
|
1107
|
-
numericType: /[
|
|
1229
|
+
numericType: /[.eEpP]/.test(text) ? "float" : "int",
|
|
1108
1230
|
ctx: context(t, ctx),
|
|
1109
1231
|
};
|
|
1110
1232
|
}
|
|
@@ -1174,10 +1296,10 @@ function parseExpression(t: ParseTree, ctx: ASTCtx): LuaExpression {
|
|
|
1174
1296
|
case "TableConstructor":
|
|
1175
1297
|
return {
|
|
1176
1298
|
type: "TableConstructor",
|
|
1177
|
-
fields: t
|
|
1178
|
-
.slice(1, -1)
|
|
1299
|
+
fields: t
|
|
1300
|
+
.children!.slice(1, -1)
|
|
1179
1301
|
.filter((c) =>
|
|
1180
|
-
["FieldExp", "FieldProp", "FieldDynamic"].includes(c.type!)
|
|
1302
|
+
["FieldExp", "FieldProp", "FieldDynamic"].includes(c.type!),
|
|
1181
1303
|
)
|
|
1182
1304
|
.map((tf) => parseTableField(tf, ctx)),
|
|
1183
1305
|
ctx: context(t, ctx),
|
|
@@ -1190,12 +1312,36 @@ function parseExpression(t: ParseTree, ctx: ASTCtx): LuaExpression {
|
|
|
1190
1312
|
clauses: t.children!.slice(2, -1).map((c) => parseQueryClause(c, ctx)),
|
|
1191
1313
|
ctx: context(t, ctx),
|
|
1192
1314
|
};
|
|
1315
|
+
case "FilteredCall": {
|
|
1316
|
+
const call = parseFunctionCall(t.children![0], ctx);
|
|
1317
|
+
const filterExpr = parseExpression(t.children![4], ctx);
|
|
1318
|
+
return {
|
|
1319
|
+
type: "FilteredCall",
|
|
1320
|
+
call,
|
|
1321
|
+
filter: filterExpr,
|
|
1322
|
+
ctx: context(t, ctx),
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1193
1325
|
default:
|
|
1194
1326
|
console.error(t);
|
|
1195
1327
|
throw new Error(`Unknown expression type: ${t.type}`);
|
|
1196
1328
|
}
|
|
1197
1329
|
}
|
|
1198
1330
|
|
|
1331
|
+
function parseFieldList(t: ParseTree, ctx: ASTCtx): LuaTableField[] {
|
|
1332
|
+
if (t.type !== "FieldList") {
|
|
1333
|
+
throw new Error(`Expected FieldList, got ${t.type}`);
|
|
1334
|
+
}
|
|
1335
|
+
return t
|
|
1336
|
+
.children!.filter(
|
|
1337
|
+
(c) =>
|
|
1338
|
+
c.type === "FieldExp" ||
|
|
1339
|
+
c.type === "FieldProp" ||
|
|
1340
|
+
c.type === "FieldDynamic",
|
|
1341
|
+
)
|
|
1342
|
+
.map((c) => parseTableField(c, ctx));
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1199
1345
|
function parseQueryClause(t: ParseTree, ctx: ASTCtx): LuaQueryClause {
|
|
1200
1346
|
if (t.type !== "QueryClause") {
|
|
1201
1347
|
throw new Error(`Expected QueryClause, got ${t.type}`);
|
|
@@ -1203,18 +1349,14 @@ function parseQueryClause(t: ParseTree, ctx: ASTCtx): LuaQueryClause {
|
|
|
1203
1349
|
t = t.children![0];
|
|
1204
1350
|
switch (t.type) {
|
|
1205
1351
|
case "FromClause": {
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
name: t.children![1].children![0].text!,
|
|
1211
|
-
expression: parseExpression(t.children![3], ctx),
|
|
1212
|
-
ctx: context(t, ctx),
|
|
1213
|
-
};
|
|
1352
|
+
// children: ckw<"from">, FieldList
|
|
1353
|
+
const fieldListNode = t.children!.find((c) => c.type === "FieldList");
|
|
1354
|
+
if (!fieldListNode) {
|
|
1355
|
+
throw new Error("FromClause missing FieldList");
|
|
1214
1356
|
}
|
|
1215
1357
|
return {
|
|
1216
1358
|
type: "From",
|
|
1217
|
-
|
|
1359
|
+
fields: parseFieldList(fieldListNode, ctx),
|
|
1218
1360
|
ctx: context(t, ctx),
|
|
1219
1361
|
};
|
|
1220
1362
|
}
|
|
@@ -1236,16 +1378,18 @@ function parseQueryClause(t: ParseTree, ctx: ASTCtx): LuaQueryClause {
|
|
|
1236
1378
|
ctx: context(t, ctx),
|
|
1237
1379
|
};
|
|
1238
1380
|
}
|
|
1381
|
+
case "OffsetClause": {
|
|
1382
|
+
return {
|
|
1383
|
+
type: "Offset",
|
|
1384
|
+
offset: parseExpression(t.children![1], ctx),
|
|
1385
|
+
ctx: context(t, ctx),
|
|
1386
|
+
};
|
|
1387
|
+
}
|
|
1239
1388
|
case "OrderByClause": {
|
|
1240
1389
|
const orderBy: LuaOrderBy[] = [];
|
|
1241
1390
|
for (const child of t.children!) {
|
|
1242
1391
|
if (child.type === "OrderBy") {
|
|
1243
|
-
orderBy.push(
|
|
1244
|
-
type: "Order",
|
|
1245
|
-
expression: parseExpression(child.children![0], ctx),
|
|
1246
|
-
direction: child.children![1]?.type === "desc" ? "desc" : "asc",
|
|
1247
|
-
ctx: context(child, ctx),
|
|
1248
|
-
});
|
|
1392
|
+
orderBy.push(parseOrderByNode(child, ctx));
|
|
1249
1393
|
}
|
|
1250
1394
|
}
|
|
1251
1395
|
return {
|
|
@@ -1255,8 +1399,32 @@ function parseQueryClause(t: ParseTree, ctx: ASTCtx): LuaQueryClause {
|
|
|
1255
1399
|
};
|
|
1256
1400
|
}
|
|
1257
1401
|
case "SelectClause": {
|
|
1402
|
+
// children: ckw<"select">, FieldList
|
|
1403
|
+
const fieldListNode = t.children!.find((c) => c.type === "FieldList");
|
|
1404
|
+
if (!fieldListNode) {
|
|
1405
|
+
throw new Error("SelectClause missing FieldList");
|
|
1406
|
+
}
|
|
1258
1407
|
return {
|
|
1259
1408
|
type: "Select",
|
|
1409
|
+
fields: parseFieldList(fieldListNode, ctx),
|
|
1410
|
+
ctx: context(t, ctx),
|
|
1411
|
+
};
|
|
1412
|
+
}
|
|
1413
|
+
case "GroupByClause": {
|
|
1414
|
+
// children: ckw<"group">, ckw<"by">, FieldList
|
|
1415
|
+
const fieldListNode = t.children!.find((c) => c.type === "FieldList");
|
|
1416
|
+
if (!fieldListNode) {
|
|
1417
|
+
throw new Error("GroupByClause missing FieldList");
|
|
1418
|
+
}
|
|
1419
|
+
return {
|
|
1420
|
+
type: "GroupBy",
|
|
1421
|
+
fields: parseFieldList(fieldListNode, ctx),
|
|
1422
|
+
ctx: context(t, ctx),
|
|
1423
|
+
};
|
|
1424
|
+
}
|
|
1425
|
+
case "HavingClause": {
|
|
1426
|
+
return {
|
|
1427
|
+
type: "Having",
|
|
1260
1428
|
expression: parseExpression(t.children![1], ctx),
|
|
1261
1429
|
ctx: context(t, ctx),
|
|
1262
1430
|
};
|
|
@@ -1267,10 +1435,70 @@ function parseQueryClause(t: ParseTree, ctx: ASTCtx): LuaQueryClause {
|
|
|
1267
1435
|
}
|
|
1268
1436
|
}
|
|
1269
1437
|
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1438
|
+
// Parse a single OrderBy node (shared by query OrderByClause and AggOrderBy)
|
|
1439
|
+
function parseOrderByNode(child: ParseTree, ctx: ASTCtx): LuaOrderBy {
|
|
1440
|
+
const kids = child.children!;
|
|
1441
|
+
let direction: "asc" | "desc" = "asc";
|
|
1442
|
+
let nulls: "first" | "last" | undefined;
|
|
1443
|
+
let usingVal: string | LuaFunctionBody | undefined;
|
|
1444
|
+
for (let i = 1; i < kids.length; i++) {
|
|
1445
|
+
const typ = kids[i].type;
|
|
1446
|
+
if (typ === "desc") direction = "desc";
|
|
1447
|
+
else if (typ === "asc") direction = "asc";
|
|
1448
|
+
else if (typ === "first") nulls = "first";
|
|
1449
|
+
else if (typ === "last") nulls = "last";
|
|
1450
|
+
else if (typ === "using") {
|
|
1451
|
+
const next = kids[i + 1];
|
|
1452
|
+
if (next.type === "function") {
|
|
1453
|
+
usingVal = parseFunctionBody(kids[i + 2], ctx);
|
|
1454
|
+
i += 2;
|
|
1455
|
+
} else {
|
|
1456
|
+
usingVal = next.children![0].text!;
|
|
1457
|
+
i++;
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
const ob: LuaOrderBy = {
|
|
1462
|
+
type: "Order",
|
|
1463
|
+
expression: parseExpression(kids[0], ctx),
|
|
1464
|
+
direction,
|
|
1465
|
+
ctx: context(child, ctx),
|
|
1466
|
+
};
|
|
1467
|
+
if (nulls) ob.nulls = nulls;
|
|
1468
|
+
if (usingVal !== undefined) ob.using = usingVal;
|
|
1469
|
+
return ob;
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
// Parse an AggOrderBy node into LuaOrderBy[]
|
|
1473
|
+
function parseAggOrderBy(t: ParseTree, ctx: ASTCtx): LuaOrderBy[] {
|
|
1474
|
+
if (t.type !== "AggOrderBy") {
|
|
1475
|
+
throw new Error(`Expected AggOrderBy, got ${t.type}`);
|
|
1476
|
+
}
|
|
1477
|
+
const orderBy: LuaOrderBy[] = [];
|
|
1478
|
+
for (const child of t.children!) {
|
|
1479
|
+
if (child.type === "OrderBy") {
|
|
1480
|
+
orderBy.push(parseOrderByNode(child, ctx));
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
return orderBy;
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
// Parse function args, extracting AggOrderBy if present inside funcParams
|
|
1487
|
+
function parseFunctionArgsWithOrderBy(
|
|
1488
|
+
ts: ParseTree[],
|
|
1489
|
+
ctx: ASTCtx,
|
|
1490
|
+
): { args: LuaExpression[]; aggOrderBy?: LuaOrderBy[] } {
|
|
1491
|
+
let aggOrderBy: LuaOrderBy[] | undefined;
|
|
1492
|
+
const args: LuaExpression[] = [];
|
|
1493
|
+
for (const t of ts) {
|
|
1494
|
+
if (!t.type || [",", "(", ")"].includes(t.type)) continue;
|
|
1495
|
+
if (t.type === "AggOrderBy") {
|
|
1496
|
+
aggOrderBy = parseAggOrderBy(t, ctx);
|
|
1497
|
+
} else {
|
|
1498
|
+
args.push(parseExpression(t, ctx));
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
return { args, aggOrderBy };
|
|
1274
1502
|
}
|
|
1275
1503
|
|
|
1276
1504
|
function parseFunctionBody(t: ParseTree, ctx: ASTCtx): LuaFunctionBody {
|
|
@@ -1279,8 +1507,10 @@ function parseFunctionBody(t: ParseTree, ctx: ASTCtx): LuaFunctionBody {
|
|
|
1279
1507
|
}
|
|
1280
1508
|
return {
|
|
1281
1509
|
type: "FunctionBody",
|
|
1282
|
-
parameters: t
|
|
1283
|
-
.
|
|
1510
|
+
parameters: t
|
|
1511
|
+
.children![1].children!.filter(
|
|
1512
|
+
(c) => c.type && ["Name", "Ellipsis"].includes(c.type),
|
|
1513
|
+
)
|
|
1284
1514
|
.map((c) => c.children![0].text!),
|
|
1285
1515
|
block: parseBlock(t.children![3], ctx),
|
|
1286
1516
|
ctx: context(t, ctx),
|
|
@@ -1371,7 +1601,7 @@ export function stripLuaComments(s: string): string {
|
|
|
1371
1601
|
if (s[j] === "[") {
|
|
1372
1602
|
// Found long string start
|
|
1373
1603
|
const openBracket = s.substring(i, j + 1);
|
|
1374
|
-
const closeBracket =
|
|
1604
|
+
const closeBracket = `]${"=".repeat(equalsCount)}]`;
|
|
1375
1605
|
result += openBracket;
|
|
1376
1606
|
i = j + 1;
|
|
1377
1607
|
|
|
@@ -1424,7 +1654,7 @@ export function stripLuaComments(s: string): string {
|
|
|
1424
1654
|
}
|
|
1425
1655
|
if (s[j] === "[") {
|
|
1426
1656
|
// Found long comment start
|
|
1427
|
-
const closeBracket =
|
|
1657
|
+
const closeBracket = `]${"=".repeat(equalsCount)}]`;
|
|
1428
1658
|
// Replace opening bracket with spaces
|
|
1429
1659
|
result += " ".repeat(j - i + 1);
|
|
1430
1660
|
i = j + 1;
|
|
@@ -1468,9 +1698,7 @@ export function parse(s: string, ctx: ASTCtx = {}): LuaBlock {
|
|
|
1468
1698
|
if (e && typeof e === "object" && "astCtx" in e) {
|
|
1469
1699
|
throw new LuaRuntimeError(
|
|
1470
1700
|
e.message,
|
|
1471
|
-
LuaStackFrame.lostFrame.withCtx(
|
|
1472
|
-
(e as any).astCtx as ASTCtx,
|
|
1473
|
-
),
|
|
1701
|
+
LuaStackFrame.lostFrame.withCtx((e as any).astCtx as ASTCtx),
|
|
1474
1702
|
);
|
|
1475
1703
|
}
|
|
1476
1704
|
throw e;
|
|
@@ -1514,9 +1742,7 @@ function luaUnexpectedSymbolMessage(src: string, from: number): string {
|
|
|
1514
1742
|
/**
|
|
1515
1743
|
* Helper function to parse a Lua expression string
|
|
1516
1744
|
*/
|
|
1517
|
-
export function parseExpressionString(
|
|
1518
|
-
expr: string,
|
|
1519
|
-
): LuaExpression {
|
|
1745
|
+
export function parseExpressionString(expr: string): LuaExpression {
|
|
1520
1746
|
const parsedLua = parse(`_(${expr})`) as LuaBlock;
|
|
1521
1747
|
return (parsedLua.statements[0] as LuaFunctionCallStatement).call.args[0];
|
|
1522
1748
|
}
|