@hyperfixi/core 2.3.0 → 2.4.0
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 +8 -11
- package/dist/api/dom-processor.d.ts +8 -4
- package/dist/api/hyperscript-api.d.ts +5 -1
- package/dist/ast-utils/index.js +25320 -94
- package/dist/ast-utils/index.mjs +25320 -94
- package/dist/ast-utils/interchange/types.d.ts +7 -1
- package/dist/behaviors/index.js +54 -100
- package/dist/behaviors/index.mjs +54 -100
- package/dist/bundle-generator/index.js +44 -6
- package/dist/bundle-generator/index.mjs +44 -6
- package/dist/bundle-generator/parser-templates.d.ts +1 -1
- package/dist/bundle-generator/template-capabilities.d.ts +1 -1
- package/dist/chunks/bridge-C4d3blZX.js +2 -0
- package/dist/chunks/browser-modular-BwIRlrTM.js +2 -0
- package/dist/chunks/feature-eventsource-BpZvPy_K.js +2 -0
- package/dist/chunks/{feature-sockets-ClOH7vk7.js → feature-sockets-CrYvjZ4j.js} +2 -2
- package/dist/chunks/feature-webworker-BSYguEIW.js +2 -0
- package/dist/chunks/index-Beno_SBy.js +2 -0
- package/dist/commands/advanced/async.d.ts +6 -2
- package/dist/commands/advanced/js.d.ts +1 -1
- package/dist/commands/animation/start-view-transition.d.ts +24 -0
- package/dist/commands/async/fetch.d.ts +6 -1
- package/dist/commands/control-flow/repeat.d.ts +2 -0
- package/dist/commands/data/clear.d.ts +23 -0
- package/dist/commands/data/set.d.ts +6 -0
- package/dist/commands/dom/close.d.ts +19 -0
- package/dist/commands/dom/empty.d.ts +19 -0
- package/dist/commands/dom/open.d.ts +21 -0
- package/dist/commands/dom/reset.d.ts +19 -0
- package/dist/commands/dom/select.d.ts +19 -0
- package/dist/commands/dom/swap.d.ts +7 -4
- package/dist/commands/events/trigger.d.ts +1 -1
- package/dist/commands/execution/blur.d.ts +19 -0
- package/dist/commands/execution/call.d.ts +1 -2
- package/dist/commands/execution/focus.d.ts +19 -0
- package/dist/commands/helpers/element-resolution.d.ts +2 -2
- package/dist/commands/helpers/event-waiting.d.ts +1 -1
- package/dist/commands/helpers/numeric-target-parser.d.ts +7 -0
- package/dist/commands/index.d.ts +34 -2
- package/dist/commands/index.js +19374 -4848
- package/dist/commands/index.mjs +19342 -4849
- package/dist/commands/navigation/go.d.ts +3 -0
- package/dist/commands/navigation/scroll-to.d.ts +26 -0
- package/dist/commands/utility/beep.d.ts +2 -2
- package/dist/commands/utility/breakpoint.d.ts +19 -0
- package/dist/commands/utility/pick.d.ts +11 -2
- package/dist/compatibility/browser-bundle-modular.d.ts +2 -2
- package/dist/compatibility/browser-bundle-multilingual.d.ts +1 -1
- package/dist/compatibility/browser-bundle-semantic-complete.d.ts +3 -3
- package/dist/compatibility/browser-bundle.d.ts +13 -6
- package/dist/compatibility/browser-modular.d.ts +1 -3
- package/dist/core/expression-evaluator.d.ts +4 -4
- package/dist/core/expression-registry.d.ts +8 -0
- package/dist/expressions/bundles/common-expressions.d.ts +2 -2
- package/dist/expressions/bundles/core-expressions.d.ts +2 -2
- package/dist/expressions/bundles/full-expressions.d.ts +2 -2
- package/dist/expressions/bundles/index.d.ts +3 -3
- package/dist/expressions/collection/index.d.ts +35 -0
- package/dist/expressions/conversion/impl/index.d.ts +1 -1
- package/dist/expressions/index.d.ts +4 -3
- package/dist/expressions/index.js +1117 -1590
- package/dist/expressions/index.mjs +1113 -1586
- package/dist/expressions/logical/index.d.ts +2 -0
- package/dist/expressions/mathematical/index.d.ts +11 -0
- package/dist/expressions/shared/index.d.ts +1 -1
- package/dist/expressions/shared/number-utils.d.ts +1 -0
- package/dist/htmx/htmx-attribute-processor.d.ts +37 -1
- package/dist/htmx/htmx-translator.d.ts +2 -0
- package/dist/htmx/i18n-hooks.d.ts +15 -0
- package/dist/htmx/i18n-orchestrator.d.ts +15 -0
- package/dist/htmx/lang-resolver.d.ts +3 -0
- package/dist/htmx/sse.d.ts +60 -0
- package/dist/htmx/ws.d.ts +59 -0
- package/dist/hyperfixi-browser-classic-i18n.js +2 -0
- package/dist/hyperfixi-browser-minimal.js +1 -0
- package/dist/hyperfixi-browser-standard.js +2 -0
- package/dist/hyperfixi-browser.js +2 -0
- package/dist/hyperfixi-classic-i18n.js +1 -1
- package/dist/hyperfixi-hx-v4.js +1 -0
- package/dist/hyperfixi-hx.js +1 -1
- package/dist/hyperfixi-hybrid-complete.js +1 -1
- package/dist/hyperfixi-hybrid-hx.js +1 -0
- package/dist/hyperfixi-minimal.js +1 -1
- package/dist/hyperfixi-multilingual.js +1 -1
- package/dist/hyperfixi-standard.js +1 -1
- package/dist/hyperfixi.js +1 -1
- package/dist/hyperfixi.mjs +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +43613 -45063
- package/dist/index.min.js +1 -1
- package/dist/index.mjs +43610 -45064
- package/dist/lib/index.d.ts +2 -2
- package/dist/lib/morph-adapter.d.ts +0 -13
- package/dist/lib/swap-executor.d.ts +0 -10
- package/dist/lib/view-transitions.d.ts +1 -30
- package/dist/lokascript-browser-classic-i18n.js +1 -1
- package/dist/lokascript-browser-minimal.js +1 -1
- package/dist/lokascript-browser-standard.js +1 -1
- package/dist/lokascript-browser.js +1 -1
- package/dist/lokascript-hybrid-complete.js +1 -1
- package/dist/lokascript-hybrid-hx.js +1 -1
- package/dist/lokascript-multilingual.js +1 -1
- package/dist/lsp-metadata.d.ts +9 -4
- package/dist/lsp-metadata.js +187 -3
- package/dist/lsp-metadata.mjs +185 -4
- package/dist/metadata.d.ts +1 -1
- package/dist/metadata.js +3 -3
- package/dist/metadata.mjs +3 -3
- package/dist/multilingual/bridge.d.ts +1 -1
- package/dist/multilingual/index.js +79 -22
- package/dist/multilingual/index.mjs +79 -22
- package/dist/parser/command-parsers/animation-commands.d.ts +1 -0
- package/dist/parser/command-parsers/utility-commands.d.ts +1 -0
- package/dist/parser/extensions.d.ts +51 -0
- package/dist/parser/full-parser.js +1224 -899
- package/dist/parser/full-parser.mjs +1224 -899
- package/dist/parser/helpers/ast-helpers.d.ts +1 -0
- package/dist/parser/helpers/parsing-helpers.d.ts +4 -0
- package/dist/parser/hybrid/index.js +7 -0
- package/dist/parser/hybrid/index.mjs +7 -0
- package/dist/parser/hybrid/parser-core.js +7 -0
- package/dist/parser/hybrid/parser-core.mjs +7 -0
- package/dist/parser/hybrid/tokenizer.js +7 -0
- package/dist/parser/hybrid/tokenizer.mjs +7 -0
- package/dist/parser/hybrid-parser.js +7 -0
- package/dist/parser/hybrid-parser.mjs +7 -0
- package/dist/parser/parser-types.d.ts +8 -28
- package/dist/parser/parser.d.ts +3 -7
- package/dist/parser/pratt-parser.d.ts +0 -3
- package/dist/parser/runtime.d.ts +4 -0
- package/dist/parser/semantic-integration.d.ts +17 -0
- package/dist/parser/types.d.ts +7 -1
- package/dist/reference/index.js +91 -0
- package/dist/reference/index.mjs +91 -0
- package/dist/registry/index.js +12866 -5876
- package/dist/registry/index.mjs +12866 -5876
- package/dist/registry/universal-types.d.ts +2 -1
- package/dist/runtime/command-adapter.d.ts +23 -16
- package/dist/runtime/plugin.d.ts +14 -0
- package/dist/runtime/runtime-base.d.ts +32 -7
- package/dist/runtime/runtime-factory.d.ts +3 -3
- package/dist/runtime/runtime.d.ts +2 -2
- package/dist/test-setup.d.ts +1 -0
- package/dist/types/base-types.d.ts +3 -0
- package/dist/types/feature-types.d.ts +1 -1
- package/dist/types/index.d.ts +2 -2
- package/package.json +26 -20
- package/vocab/htmx/ar.js +60 -0
- package/vocab/htmx/bn.js +49 -0
- package/vocab/htmx/de.js +60 -0
- package/vocab/htmx/en.js +21 -0
- package/vocab/htmx/es.js +60 -0
- package/vocab/htmx/fr.js +59 -0
- package/vocab/htmx/he.js +40 -0
- package/vocab/htmx/hi.js +60 -0
- package/vocab/htmx/id.js +57 -0
- package/vocab/htmx/it.js +58 -0
- package/vocab/htmx/ja.js +60 -0
- package/vocab/htmx/ko.js +60 -0
- package/vocab/htmx/ms.js +35 -0
- package/vocab/htmx/pl.js +60 -0
- package/vocab/htmx/pt.js +60 -0
- package/vocab/htmx/qu.js +60 -0
- package/vocab/htmx/ru.js +60 -0
- package/vocab/htmx/sw.js +59 -0
- package/vocab/htmx/th.js +49 -0
- package/vocab/htmx/tl.js +33 -0
- package/vocab/htmx/tr.js +60 -0
- package/vocab/htmx/uk.js +60 -0
- package/vocab/htmx/vi.js +51 -0
- package/vocab/htmx/zh.js +60 -0
- package/dist/bundles/test-minimal.d.ts +0 -3
- package/dist/bundles/test-standard.d.ts +0 -3
- package/dist/chunks/bridge-BlRqsZT4.js +0 -2
- package/dist/chunks/browser-modular-AbV0Ql4i.js +0 -2
- package/dist/chunks/feature-eventsource-B5F2-H1r.js +0 -2
- package/dist/chunks/feature-webworker-3bAp0ac9.js +0 -2
- package/dist/chunks/index-BDYQHwCF.js +0 -2
- package/dist/compatibility/browser-bundle-minimal.d.ts +0 -8
- package/dist/compatibility/browser-bundle-standard.d.ts +0 -8
- package/dist/compatibility/hyperscript-tests/test-adapter.d.ts +0 -13
- package/dist/core/base-expression-evaluator.d.ts +0 -74
- package/dist/core/binary-expression-evaluator.d.ts +0 -7
- package/dist/core/call-expression-evaluator.d.ts +0 -7
- package/dist/core/configurable-expression-evaluator.d.ts +0 -5
- package/dist/core/lazy-expression-evaluator.d.ts +0 -22
- package/dist/core/parser.d.ts +0 -21
- package/dist/core/selector-evaluator.d.ts +0 -15
- package/dist/core/template-literal-evaluator.d.ts +0 -5
- package/dist/expressions/comparison/index.d.ts +0 -80
- package/dist/expressions/comparison/utils.d.ts +0 -2
- package/dist/expressions/conversion/impl/bridge.d.ts +0 -117
- package/dist/expressions/logical/impl/pattern-matching.d.ts +0 -58
- package/dist/expressions/positional/impl/bridge.d.ts +0 -95
- package/dist/expressions/property/index.d.ts +0 -55
- package/dist/expressions/references/impl/bridge.d.ts +0 -54
- package/dist/extensions/index.d.ts +0 -3
- package/dist/extensions/tailwind.d.ts +0 -22
- package/dist/mod.d.ts +0 -63
- package/dist/parser/expression-parser.d.ts +0 -6
- package/dist/runtime/runtime-experimental.d.ts +0 -18
- package/dist/scripts/code-generator.d.ts +0 -64
- package/dist/scripts/generate-missing-commands.d.ts +0 -4
|
@@ -40,19 +40,26 @@ const COMMANDS = new Set([
|
|
|
40
40
|
'append',
|
|
41
41
|
'async',
|
|
42
42
|
'beep',
|
|
43
|
+
'blur',
|
|
43
44
|
'break',
|
|
45
|
+
'breakpoint',
|
|
44
46
|
'call',
|
|
47
|
+
'clear',
|
|
48
|
+
'close',
|
|
45
49
|
'continue',
|
|
46
50
|
'copy',
|
|
47
51
|
'decrement',
|
|
48
52
|
'default',
|
|
53
|
+
'empty',
|
|
49
54
|
'exit',
|
|
50
55
|
'fetch',
|
|
56
|
+
'focus',
|
|
51
57
|
'for',
|
|
52
58
|
'get',
|
|
53
59
|
'go',
|
|
54
60
|
'halt',
|
|
55
61
|
'hide',
|
|
62
|
+
'open',
|
|
56
63
|
'if',
|
|
57
64
|
'increment',
|
|
58
65
|
'install',
|
|
@@ -69,11 +76,15 @@ const COMMANDS = new Set([
|
|
|
69
76
|
'render',
|
|
70
77
|
'repeat',
|
|
71
78
|
'replace',
|
|
79
|
+
'reset',
|
|
72
80
|
'return',
|
|
81
|
+
'scroll',
|
|
82
|
+
'select',
|
|
73
83
|
'send',
|
|
74
84
|
'set',
|
|
75
85
|
'settle',
|
|
76
86
|
'show',
|
|
87
|
+
'start',
|
|
77
88
|
'swap',
|
|
78
89
|
'take',
|
|
79
90
|
'tell',
|
|
@@ -99,6 +110,8 @@ const COMPOUND_COMMANDS = new Set([
|
|
|
99
110
|
'measure',
|
|
100
111
|
'js',
|
|
101
112
|
'tell',
|
|
113
|
+
'pick',
|
|
114
|
+
'start',
|
|
102
115
|
'swap',
|
|
103
116
|
'morph',
|
|
104
117
|
'push',
|
|
@@ -217,11 +230,20 @@ const COMPARISON_OPERATORS = new Set([
|
|
|
217
230
|
'>=',
|
|
218
231
|
'is',
|
|
219
232
|
'is not',
|
|
233
|
+
'am',
|
|
220
234
|
'is a',
|
|
221
235
|
'is an',
|
|
222
236
|
'is not a',
|
|
223
237
|
'is not an',
|
|
238
|
+
'precedes',
|
|
239
|
+
'does not precede',
|
|
240
|
+
'follows',
|
|
241
|
+
'does not follow',
|
|
224
242
|
'contains',
|
|
243
|
+
'starts with',
|
|
244
|
+
'ends with',
|
|
245
|
+
'does not start with',
|
|
246
|
+
'does not end with',
|
|
225
247
|
'has',
|
|
226
248
|
'have',
|
|
227
249
|
'does not contain',
|
|
@@ -236,6 +258,8 @@ const COMPARISON_OPERATORS = new Set([
|
|
|
236
258
|
'is not empty',
|
|
237
259
|
'is in',
|
|
238
260
|
'is not in',
|
|
261
|
+
'is between',
|
|
262
|
+
'is not between',
|
|
239
263
|
'equals',
|
|
240
264
|
'in',
|
|
241
265
|
'is equal to',
|
|
@@ -247,6 +271,11 @@ const COMPARISON_OPERATORS = new Set([
|
|
|
247
271
|
'is greater than or equal to',
|
|
248
272
|
'is less than or equal to',
|
|
249
273
|
'really equals',
|
|
274
|
+
'ignoring case',
|
|
275
|
+
'sorted by',
|
|
276
|
+
'mapped to',
|
|
277
|
+
'split by',
|
|
278
|
+
'joined by',
|
|
250
279
|
]);
|
|
251
280
|
const DOM_EVENTS = new Set([
|
|
252
281
|
'click',
|
|
@@ -1786,205 +1815,639 @@ SemanticIntegrationAdapter.SKIP_SEMANTIC_COMMANDS = new Set([
|
|
|
1786
1815
|
'tell',
|
|
1787
1816
|
]);
|
|
1788
1817
|
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
}
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
}
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
return false;
|
|
1824
|
-
return !token.value.startsWith('<');
|
|
1825
|
-
}
|
|
1826
|
-
function isLiteral(token) {
|
|
1827
|
-
return (token.kind === TokenKind.STRING ||
|
|
1828
|
-
token.kind === TokenKind.NUMBER ||
|
|
1829
|
-
token.kind === TokenKind.TEMPLATE);
|
|
1830
|
-
}
|
|
1831
|
-
function isReference(token) {
|
|
1832
|
-
return token.kind === TokenKind.IDENTIFIER;
|
|
1833
|
-
}
|
|
1834
|
-
function isTimeExpression(token) {
|
|
1835
|
-
return token.kind === TokenKind.TIME;
|
|
1836
|
-
}
|
|
1837
|
-
function isSymbol(token) {
|
|
1838
|
-
return token.kind === TokenKind.SYMBOL;
|
|
1839
|
-
}
|
|
1840
|
-
function isComment(token) {
|
|
1841
|
-
return token.kind === TokenKind.COMMENT;
|
|
1842
|
-
}
|
|
1843
|
-
function isIdentifier(token) {
|
|
1844
|
-
if (token.kind !== TokenKind.IDENTIFIER)
|
|
1845
|
-
return false;
|
|
1846
|
-
const lowerValue = token.value.toLowerCase();
|
|
1847
|
-
return (!COMMANDS.has(lowerValue) &&
|
|
1848
|
-
!TOKENIZER_KEYWORDS.has(lowerValue) &&
|
|
1849
|
-
!DOM_EVENTS.has(lowerValue) &&
|
|
1850
|
-
!CONTEXT_VARS.has(lowerValue));
|
|
1851
|
-
}
|
|
1852
|
-
function isString(token) {
|
|
1853
|
-
return token.kind === TokenKind.STRING;
|
|
1854
|
-
}
|
|
1855
|
-
function isNumber(token) {
|
|
1856
|
-
return token.kind === TokenKind.NUMBER;
|
|
1857
|
-
}
|
|
1858
|
-
function isBoolean(token) {
|
|
1859
|
-
if (token.kind === TokenKind.IDENTIFIER) {
|
|
1860
|
-
const v = token.value;
|
|
1861
|
-
return v === 'true' || v === 'false' || v === 'null' || v === 'undefined';
|
|
1818
|
+
const STOP_TOKENS = new Set([
|
|
1819
|
+
'then',
|
|
1820
|
+
'end',
|
|
1821
|
+
'to',
|
|
1822
|
+
'into',
|
|
1823
|
+
'on',
|
|
1824
|
+
'with',
|
|
1825
|
+
'from',
|
|
1826
|
+
'in',
|
|
1827
|
+
'by',
|
|
1828
|
+
'for',
|
|
1829
|
+
'while',
|
|
1830
|
+
'until',
|
|
1831
|
+
'unless',
|
|
1832
|
+
'else',
|
|
1833
|
+
'catch',
|
|
1834
|
+
'finally',
|
|
1835
|
+
]);
|
|
1836
|
+
const STOP_DELIMITERS = new Set([')', ']', '}', ',']);
|
|
1837
|
+
function mergeFragments(...fragments) {
|
|
1838
|
+
const merged = new Map();
|
|
1839
|
+
for (const fragment of fragments) {
|
|
1840
|
+
for (const [key, entry] of fragment) {
|
|
1841
|
+
const existing = merged.get(key);
|
|
1842
|
+
if (existing) {
|
|
1843
|
+
merged.set(key, {
|
|
1844
|
+
prefix: entry.prefix ?? existing.prefix,
|
|
1845
|
+
infix: entry.infix ?? existing.infix,
|
|
1846
|
+
});
|
|
1847
|
+
}
|
|
1848
|
+
else {
|
|
1849
|
+
merged.set(key, { ...entry });
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1862
1852
|
}
|
|
1863
|
-
return
|
|
1864
|
-
}
|
|
1865
|
-
function isTemplateLiteral(token) {
|
|
1866
|
-
return token.kind === TokenKind.TEMPLATE;
|
|
1867
|
-
}
|
|
1868
|
-
function isQueryReference(token) {
|
|
1869
|
-
return token.kind === TokenKind.SELECTOR && token.value.startsWith('<');
|
|
1870
|
-
}
|
|
1871
|
-
function isIdSelector(token) {
|
|
1872
|
-
return token.kind === TokenKind.SELECTOR && token.value.startsWith('#');
|
|
1873
|
-
}
|
|
1874
|
-
function isClassSelector(token) {
|
|
1875
|
-
return token.kind === TokenKind.SELECTOR && token.value.startsWith('.');
|
|
1876
|
-
}
|
|
1877
|
-
function isCssSelector(token) {
|
|
1878
|
-
if (token.kind !== TokenKind.SELECTOR)
|
|
1879
|
-
return false;
|
|
1880
|
-
return !token.value.startsWith('#') && !token.value.startsWith('.');
|
|
1881
|
-
}
|
|
1882
|
-
function isBasicOperator(token) {
|
|
1883
|
-
if (token.kind !== TokenKind.OPERATOR)
|
|
1884
|
-
return false;
|
|
1885
|
-
const lowerValue = token.value.toLowerCase();
|
|
1886
|
-
return !LOGICAL_OPERATORS.has(lowerValue) && !COMPARISON_OPERATORS.has(lowerValue);
|
|
1887
|
-
}
|
|
1888
|
-
function isCommandTerminator(token) {
|
|
1889
|
-
const value = token.value.toLowerCase();
|
|
1890
|
-
return (value === 'then' || value === 'and' || value === 'else' || value === 'end' || value === 'on');
|
|
1891
|
-
}
|
|
1892
|
-
|
|
1893
|
-
function createLiteral(value, raw, pos) {
|
|
1894
|
-
return {
|
|
1895
|
-
type: 'literal',
|
|
1896
|
-
value,
|
|
1897
|
-
raw,
|
|
1898
|
-
start: pos.start,
|
|
1899
|
-
end: pos.end,
|
|
1900
|
-
line: pos.line,
|
|
1901
|
-
column: pos.column,
|
|
1902
|
-
};
|
|
1903
|
-
}
|
|
1904
|
-
function createIdentifier(name, pos) {
|
|
1905
|
-
return {
|
|
1906
|
-
type: 'identifier',
|
|
1907
|
-
name,
|
|
1908
|
-
start: pos.start,
|
|
1909
|
-
end: pos.end,
|
|
1910
|
-
line: pos.line,
|
|
1911
|
-
column: pos.column,
|
|
1912
|
-
};
|
|
1913
|
-
}
|
|
1914
|
-
function createBinaryExpression(operator, left, right, pos) {
|
|
1915
|
-
return {
|
|
1916
|
-
type: 'binaryExpression',
|
|
1917
|
-
operator,
|
|
1918
|
-
left,
|
|
1919
|
-
right,
|
|
1920
|
-
start: pos.start,
|
|
1921
|
-
end: pos.end,
|
|
1922
|
-
line: pos.line,
|
|
1923
|
-
column: pos.column,
|
|
1924
|
-
};
|
|
1925
|
-
}
|
|
1926
|
-
function createUnaryExpression(operator, argument, prefix, pos) {
|
|
1927
|
-
return {
|
|
1928
|
-
type: 'unaryExpression',
|
|
1929
|
-
operator,
|
|
1930
|
-
argument,
|
|
1931
|
-
prefix,
|
|
1932
|
-
start: pos.start,
|
|
1933
|
-
end: pos.end,
|
|
1934
|
-
line: pos.line,
|
|
1935
|
-
column: pos.column,
|
|
1936
|
-
};
|
|
1937
|
-
}
|
|
1938
|
-
function createCallExpression(callee, args, pos) {
|
|
1939
|
-
return {
|
|
1940
|
-
type: 'callExpression',
|
|
1941
|
-
callee,
|
|
1942
|
-
arguments: args,
|
|
1943
|
-
start: pos.start,
|
|
1944
|
-
end: pos.end,
|
|
1945
|
-
line: pos.line,
|
|
1946
|
-
column: pos.column,
|
|
1947
|
-
};
|
|
1853
|
+
return merged;
|
|
1948
1854
|
}
|
|
1949
|
-
function
|
|
1855
|
+
function leftAssoc(bp, handler) {
|
|
1950
1856
|
return {
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1857
|
+
infix: {
|
|
1858
|
+
bp: [bp, bp + 1],
|
|
1859
|
+
handler: handler ??
|
|
1860
|
+
((left, token, ctx) => {
|
|
1861
|
+
const right = ctx.parseExpr(bp + 1);
|
|
1862
|
+
return {
|
|
1863
|
+
type: 'binaryExpression',
|
|
1864
|
+
operator: token.value,
|
|
1865
|
+
left,
|
|
1866
|
+
right,
|
|
1867
|
+
start: left.start,
|
|
1868
|
+
end: right.end ?? token.end,
|
|
1869
|
+
line: left.line ?? token.line,
|
|
1870
|
+
column: left.column ?? token.column,
|
|
1871
|
+
};
|
|
1872
|
+
}),
|
|
1873
|
+
},
|
|
1959
1874
|
};
|
|
1960
1875
|
}
|
|
1961
|
-
function
|
|
1876
|
+
function rightAssoc(bp, handler) {
|
|
1962
1877
|
return {
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1878
|
+
infix: {
|
|
1879
|
+
bp: [bp + 1, bp],
|
|
1880
|
+
handler: ((left, token, ctx) => {
|
|
1881
|
+
const right = ctx.parseExpr(bp);
|
|
1882
|
+
return {
|
|
1883
|
+
type: 'binaryExpression',
|
|
1884
|
+
operator: token.value,
|
|
1885
|
+
left,
|
|
1886
|
+
right,
|
|
1887
|
+
start: left.start,
|
|
1888
|
+
end: right.end ?? token.end,
|
|
1889
|
+
line: left.line ?? token.line,
|
|
1890
|
+
column: left.column ?? token.column,
|
|
1891
|
+
};
|
|
1892
|
+
}),
|
|
1893
|
+
},
|
|
1969
1894
|
};
|
|
1970
1895
|
}
|
|
1971
|
-
function
|
|
1896
|
+
function prefix(bp, handler) {
|
|
1972
1897
|
return {
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1898
|
+
prefix: {
|
|
1899
|
+
bp,
|
|
1900
|
+
handler: handler ??
|
|
1901
|
+
((token, ctx) => {
|
|
1902
|
+
const operand = ctx.parseExpr(bp);
|
|
1903
|
+
return {
|
|
1904
|
+
type: 'unaryExpression',
|
|
1905
|
+
operator: token.value,
|
|
1906
|
+
operand,
|
|
1907
|
+
argument: operand,
|
|
1908
|
+
prefix: true,
|
|
1909
|
+
start: token.start,
|
|
1910
|
+
end: operand.end ?? token.end,
|
|
1911
|
+
line: token.line,
|
|
1912
|
+
column: token.column,
|
|
1913
|
+
};
|
|
1914
|
+
}),
|
|
1915
|
+
},
|
|
1980
1916
|
};
|
|
1981
1917
|
}
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1918
|
+
const CORE_FRAGMENT = new Map([
|
|
1919
|
+
['or', leftAssoc(10)],
|
|
1920
|
+
['||', leftAssoc(10)],
|
|
1921
|
+
['and', leftAssoc(20)],
|
|
1922
|
+
['&&', leftAssoc(20)],
|
|
1923
|
+
['==', leftAssoc(30)],
|
|
1924
|
+
['!=', leftAssoc(30)],
|
|
1925
|
+
['<', leftAssoc(30)],
|
|
1926
|
+
['>', leftAssoc(30)],
|
|
1927
|
+
['<=', leftAssoc(30)],
|
|
1928
|
+
['>=', leftAssoc(30)],
|
|
1929
|
+
['is', leftAssoc(30)],
|
|
1930
|
+
['matches', leftAssoc(30)],
|
|
1931
|
+
['contains', leftAssoc(30)],
|
|
1932
|
+
['starts with', leftAssoc(30)],
|
|
1933
|
+
['ends with', leftAssoc(30)],
|
|
1934
|
+
['does not start with', leftAssoc(30)],
|
|
1935
|
+
['does not end with', leftAssoc(30)],
|
|
1936
|
+
[
|
|
1937
|
+
'is between',
|
|
1938
|
+
leftAssoc(30, (left, _token, ctx) => {
|
|
1939
|
+
const min = ctx.parseExpr(31);
|
|
1940
|
+
const next = ctx.peek();
|
|
1941
|
+
if (!next || next.value.toLowerCase() !== 'and') {
|
|
1942
|
+
throw new Error(`between requires 'and' between min and max operands, got: ${next?.value ?? '<end>'}`);
|
|
1943
|
+
}
|
|
1944
|
+
ctx.advance();
|
|
1945
|
+
const max = ctx.parseExpr(31);
|
|
1946
|
+
return {
|
|
1947
|
+
type: 'betweenExpression',
|
|
1948
|
+
value: left,
|
|
1949
|
+
min,
|
|
1950
|
+
max,
|
|
1951
|
+
negated: false,
|
|
1952
|
+
start: left.start,
|
|
1953
|
+
end: max.end,
|
|
1954
|
+
};
|
|
1955
|
+
}),
|
|
1956
|
+
],
|
|
1957
|
+
[
|
|
1958
|
+
'is not between',
|
|
1959
|
+
leftAssoc(30, (left, _token, ctx) => {
|
|
1960
|
+
const min = ctx.parseExpr(31);
|
|
1961
|
+
const next = ctx.peek();
|
|
1962
|
+
if (!next || next.value.toLowerCase() !== 'and') {
|
|
1963
|
+
throw new Error(`between requires 'and' between min and max operands, got: ${next?.value ?? '<end>'}`);
|
|
1964
|
+
}
|
|
1965
|
+
ctx.advance();
|
|
1966
|
+
const max = ctx.parseExpr(31);
|
|
1967
|
+
return {
|
|
1968
|
+
type: 'betweenExpression',
|
|
1969
|
+
value: left,
|
|
1970
|
+
min,
|
|
1971
|
+
max,
|
|
1972
|
+
negated: true,
|
|
1973
|
+
start: left.start,
|
|
1974
|
+
end: max.end,
|
|
1975
|
+
};
|
|
1976
|
+
}),
|
|
1977
|
+
],
|
|
1978
|
+
[
|
|
1979
|
+
'ignoring case',
|
|
1980
|
+
{
|
|
1981
|
+
infix: {
|
|
1982
|
+
bp: [25, 26],
|
|
1983
|
+
handler: (left, _token, _ctx) => {
|
|
1984
|
+
left.ignoringCase = true;
|
|
1985
|
+
return left;
|
|
1986
|
+
},
|
|
1987
|
+
},
|
|
1988
|
+
},
|
|
1989
|
+
],
|
|
1990
|
+
[
|
|
1991
|
+
'where',
|
|
1992
|
+
leftAssoc(28, (left, _token, ctx) => {
|
|
1993
|
+
const predicate = ctx.parseExpr(29);
|
|
1994
|
+
return {
|
|
1995
|
+
type: 'collectionExpression',
|
|
1996
|
+
operator: 'where',
|
|
1997
|
+
collection: left,
|
|
1998
|
+
right: predicate,
|
|
1999
|
+
start: left.start,
|
|
2000
|
+
end: predicate.end,
|
|
2001
|
+
};
|
|
2002
|
+
}),
|
|
2003
|
+
],
|
|
2004
|
+
[
|
|
2005
|
+
'sorted by',
|
|
2006
|
+
leftAssoc(28, (left, _token, ctx) => {
|
|
2007
|
+
const keyExpr = ctx.parseExpr(29);
|
|
2008
|
+
let order = 'asc';
|
|
2009
|
+
const next = ctx.peek();
|
|
2010
|
+
if (next && typeof next.value === 'string') {
|
|
2011
|
+
const v = next.value.toLowerCase();
|
|
2012
|
+
if (v === 'asc' || v === 'ascending') {
|
|
2013
|
+
ctx.advance();
|
|
2014
|
+
order = 'asc';
|
|
2015
|
+
}
|
|
2016
|
+
else if (v === 'desc' || v === 'descending') {
|
|
2017
|
+
ctx.advance();
|
|
2018
|
+
order = 'desc';
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
return {
|
|
2022
|
+
type: 'collectionExpression',
|
|
2023
|
+
operator: 'sorted by',
|
|
2024
|
+
collection: left,
|
|
2025
|
+
right: keyExpr,
|
|
2026
|
+
order,
|
|
2027
|
+
start: left.start,
|
|
2028
|
+
end: keyExpr.end,
|
|
2029
|
+
};
|
|
2030
|
+
}),
|
|
2031
|
+
],
|
|
2032
|
+
[
|
|
2033
|
+
'mapped to',
|
|
2034
|
+
leftAssoc(28, (left, _token, ctx) => {
|
|
2035
|
+
const expr = ctx.parseExpr(29);
|
|
2036
|
+
return {
|
|
2037
|
+
type: 'collectionExpression',
|
|
2038
|
+
operator: 'mapped to',
|
|
2039
|
+
collection: left,
|
|
2040
|
+
right: expr,
|
|
2041
|
+
start: left.start,
|
|
2042
|
+
end: expr.end,
|
|
2043
|
+
};
|
|
2044
|
+
}),
|
|
2045
|
+
],
|
|
2046
|
+
[
|
|
2047
|
+
'split by',
|
|
2048
|
+
leftAssoc(28, (left, _token, ctx) => {
|
|
2049
|
+
const sep = ctx.parseExpr(29);
|
|
2050
|
+
return {
|
|
2051
|
+
type: 'collectionExpression',
|
|
2052
|
+
operator: 'split by',
|
|
2053
|
+
collection: left,
|
|
2054
|
+
right: sep,
|
|
2055
|
+
start: left.start,
|
|
2056
|
+
end: sep.end,
|
|
2057
|
+
};
|
|
2058
|
+
}),
|
|
2059
|
+
],
|
|
2060
|
+
[
|
|
2061
|
+
'joined by',
|
|
2062
|
+
leftAssoc(28, (left, _token, ctx) => {
|
|
2063
|
+
const sep = ctx.parseExpr(29);
|
|
2064
|
+
return {
|
|
2065
|
+
type: 'collectionExpression',
|
|
2066
|
+
operator: 'joined by',
|
|
2067
|
+
collection: left,
|
|
2068
|
+
right: sep,
|
|
2069
|
+
start: left.start,
|
|
2070
|
+
end: sep.end,
|
|
2071
|
+
};
|
|
2072
|
+
}),
|
|
2073
|
+
],
|
|
2074
|
+
['+', { ...leftAssoc(40), ...prefix(80) }],
|
|
2075
|
+
['-', { ...leftAssoc(40), ...prefix(80) }],
|
|
2076
|
+
['*', leftAssoc(50)],
|
|
2077
|
+
['/', leftAssoc(50)],
|
|
2078
|
+
['%', leftAssoc(50)],
|
|
2079
|
+
['mod', leftAssoc(50)],
|
|
2080
|
+
['^', rightAssoc(60)],
|
|
2081
|
+
['**', rightAssoc(60)],
|
|
2082
|
+
[
|
|
2083
|
+
'as',
|
|
2084
|
+
leftAssoc(70, (left, token, ctx) => {
|
|
2085
|
+
const targetType = ctx.parseExpr(71);
|
|
2086
|
+
const peeked = ctx.peek();
|
|
2087
|
+
if (peeked &&
|
|
2088
|
+
peeked.value === ':' &&
|
|
2089
|
+
targetType &&
|
|
2090
|
+
targetType.type === 'identifier') {
|
|
2091
|
+
ctx.advance();
|
|
2092
|
+
const suffix = ctx.advance();
|
|
2093
|
+
if (suffix && suffix.value !== undefined) {
|
|
2094
|
+
targetType.name = `${targetType.name}:${suffix.value}`;
|
|
2095
|
+
targetType.end = suffix.end;
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
return {
|
|
2099
|
+
type: 'asExpression',
|
|
2100
|
+
expression: left,
|
|
2101
|
+
targetType,
|
|
2102
|
+
start: left.start,
|
|
2103
|
+
end: targetType.end ?? token.end,
|
|
2104
|
+
line: left.line ?? token.line,
|
|
2105
|
+
column: left.column ?? token.column,
|
|
2106
|
+
};
|
|
2107
|
+
}),
|
|
2108
|
+
],
|
|
2109
|
+
['not', prefix(80)],
|
|
2110
|
+
['!', prefix(80)],
|
|
2111
|
+
['no', prefix(80)],
|
|
2112
|
+
]);
|
|
2113
|
+
function makeTypeCheckHandler(negated) {
|
|
2114
|
+
return (left, _token, ctx) => {
|
|
2115
|
+
const typeToken = ctx.advance();
|
|
2116
|
+
if (!typeToken || !typeToken.value) {
|
|
2117
|
+
throw new Error(`Type check requires a type name after 'is ${negated ? 'not ' : ''}a/an', got: <end>`);
|
|
2118
|
+
}
|
|
2119
|
+
let nullOk = true;
|
|
2120
|
+
const peeked = ctx.peek();
|
|
2121
|
+
if (peeked && peeked.value === '!') {
|
|
2122
|
+
ctx.advance();
|
|
2123
|
+
nullOk = false;
|
|
2124
|
+
}
|
|
2125
|
+
return {
|
|
2126
|
+
type: 'typeCheckExpression',
|
|
2127
|
+
value: left,
|
|
2128
|
+
typeName: typeToken.value,
|
|
2129
|
+
nullOk,
|
|
2130
|
+
negated,
|
|
2131
|
+
start: left.start,
|
|
2132
|
+
end: typeToken.end,
|
|
2133
|
+
};
|
|
2134
|
+
};
|
|
2135
|
+
}
|
|
2136
|
+
const PARSER_COMPARISON_FRAGMENT = new Map([
|
|
2137
|
+
['===', leftAssoc(30)],
|
|
2138
|
+
['!==', leftAssoc(30)],
|
|
2139
|
+
['is not', leftAssoc(30)],
|
|
2140
|
+
['am', leftAssoc(30)],
|
|
2141
|
+
['is in', leftAssoc(30)],
|
|
2142
|
+
['is not in', leftAssoc(30)],
|
|
2143
|
+
['precedes', leftAssoc(30)],
|
|
2144
|
+
['does not precede', leftAssoc(30)],
|
|
2145
|
+
['follows', leftAssoc(30)],
|
|
2146
|
+
['does not follow', leftAssoc(30)],
|
|
2147
|
+
['is a', leftAssoc(30, makeTypeCheckHandler(false))],
|
|
2148
|
+
['is an', leftAssoc(30, makeTypeCheckHandler(false))],
|
|
2149
|
+
['is not a', leftAssoc(30, makeTypeCheckHandler(true))],
|
|
2150
|
+
['is not an', leftAssoc(30, makeTypeCheckHandler(true))],
|
|
2151
|
+
['has', leftAssoc(30)],
|
|
2152
|
+
['have', leftAssoc(30)],
|
|
2153
|
+
['match', leftAssoc(30)],
|
|
2154
|
+
['include', leftAssoc(30)],
|
|
2155
|
+
['includes', leftAssoc(30)],
|
|
2156
|
+
['equals', leftAssoc(30)],
|
|
2157
|
+
['does not contain', leftAssoc(30)],
|
|
2158
|
+
['does not include', leftAssoc(30)],
|
|
2159
|
+
['is equal to', leftAssoc(30)],
|
|
2160
|
+
['is not equal to', leftAssoc(30)],
|
|
2161
|
+
['is really equal to', leftAssoc(30)],
|
|
2162
|
+
['is not really equal to', leftAssoc(30)],
|
|
2163
|
+
['really equals', leftAssoc(30)],
|
|
2164
|
+
['is greater than', leftAssoc(30)],
|
|
2165
|
+
['is less than', leftAssoc(30)],
|
|
2166
|
+
['is greater than or equal to', leftAssoc(30)],
|
|
2167
|
+
['is less than or equal to', leftAssoc(30)],
|
|
2168
|
+
['in', leftAssoc(30)],
|
|
2169
|
+
['of', leftAssoc(30)],
|
|
2170
|
+
['really', leftAssoc(30)],
|
|
2171
|
+
[
|
|
2172
|
+
'exists',
|
|
2173
|
+
{
|
|
2174
|
+
prefix: {
|
|
2175
|
+
bp: 80,
|
|
2176
|
+
handler: (token, ctx) => ({
|
|
2177
|
+
type: 'unaryExpression',
|
|
2178
|
+
operator: token.value,
|
|
2179
|
+
operand: ctx.parseExpr(80),
|
|
2180
|
+
start: token.start,
|
|
2181
|
+
}),
|
|
2182
|
+
},
|
|
2183
|
+
infix: {
|
|
2184
|
+
bp: [30, 31],
|
|
2185
|
+
handler: (left, token) => ({
|
|
2186
|
+
type: 'unaryExpression',
|
|
2187
|
+
operator: token.value,
|
|
2188
|
+
operand: left,
|
|
2189
|
+
prefix: false,
|
|
2190
|
+
start: left.start,
|
|
2191
|
+
}),
|
|
2192
|
+
},
|
|
2193
|
+
},
|
|
2194
|
+
],
|
|
2195
|
+
[
|
|
2196
|
+
'does not exist',
|
|
2197
|
+
{
|
|
2198
|
+
infix: {
|
|
2199
|
+
bp: [30, 31],
|
|
2200
|
+
handler: (left, token) => ({
|
|
2201
|
+
type: 'unaryExpression',
|
|
2202
|
+
operator: token.value,
|
|
2203
|
+
operand: left,
|
|
2204
|
+
prefix: false,
|
|
2205
|
+
start: left.start,
|
|
2206
|
+
}),
|
|
2207
|
+
},
|
|
2208
|
+
},
|
|
2209
|
+
],
|
|
2210
|
+
[
|
|
2211
|
+
'is empty',
|
|
2212
|
+
{
|
|
2213
|
+
infix: {
|
|
2214
|
+
bp: [30, 31],
|
|
2215
|
+
handler: (left, token) => ({
|
|
2216
|
+
type: 'unaryExpression',
|
|
2217
|
+
operator: token.value,
|
|
2218
|
+
operand: left,
|
|
2219
|
+
prefix: false,
|
|
2220
|
+
start: left.start,
|
|
2221
|
+
}),
|
|
2222
|
+
},
|
|
2223
|
+
},
|
|
2224
|
+
],
|
|
2225
|
+
[
|
|
2226
|
+
'is not empty',
|
|
2227
|
+
{
|
|
2228
|
+
infix: {
|
|
2229
|
+
bp: [30, 31],
|
|
2230
|
+
handler: (left, token) => ({
|
|
2231
|
+
type: 'unaryExpression',
|
|
2232
|
+
operator: token.value,
|
|
2233
|
+
operand: left,
|
|
2234
|
+
prefix: false,
|
|
2235
|
+
start: left.start,
|
|
2236
|
+
}),
|
|
2237
|
+
},
|
|
2238
|
+
},
|
|
2239
|
+
],
|
|
2240
|
+
['some', prefix(80)],
|
|
2241
|
+
]);
|
|
2242
|
+
const ASSIGNMENT_FRAGMENT = new Map([
|
|
2243
|
+
['=', rightAssoc(5)],
|
|
2244
|
+
]);
|
|
2245
|
+
const PARSER_TABLE = mergeFragments(CORE_FRAGMENT, PARSER_COMPARISON_FRAGMENT, ASSIGNMENT_FRAGMENT);
|
|
2246
|
+
|
|
2247
|
+
const FEATURE_REGISTRY = new Map();
|
|
2248
|
+
function getRegisteredFeature(name) {
|
|
2249
|
+
return FEATURE_REGISTRY.get(name.toLowerCase());
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2252
|
+
function isCommand(token) {
|
|
2253
|
+
if (token.kind !== TokenKind.IDENTIFIER)
|
|
2254
|
+
return false;
|
|
2255
|
+
return COMMANDS.has(token.value.toLowerCase());
|
|
2256
|
+
}
|
|
2257
|
+
function isKeyword$1(token) {
|
|
2258
|
+
if (token.kind !== TokenKind.IDENTIFIER)
|
|
2259
|
+
return false;
|
|
2260
|
+
return TOKENIZER_KEYWORDS.has(token.value.toLowerCase());
|
|
2261
|
+
}
|
|
2262
|
+
function isEvent(token) {
|
|
2263
|
+
if (token.kind !== TokenKind.IDENTIFIER)
|
|
2264
|
+
return false;
|
|
2265
|
+
return DOM_EVENTS.has(token.value.toLowerCase());
|
|
2266
|
+
}
|
|
2267
|
+
function isContextVar(token) {
|
|
2268
|
+
if (token.kind !== TokenKind.IDENTIFIER)
|
|
2269
|
+
return false;
|
|
2270
|
+
return CONTEXT_VARS.has(token.value.toLowerCase());
|
|
2271
|
+
}
|
|
2272
|
+
function isComparisonOperator(token) {
|
|
2273
|
+
if (token.kind === TokenKind.OPERATOR) {
|
|
2274
|
+
return COMPARISON_OPERATORS.has(token.value.toLowerCase());
|
|
2275
|
+
}
|
|
2276
|
+
return COMPARISON_OPERATORS.has(token.value.toLowerCase());
|
|
2277
|
+
}
|
|
2278
|
+
function isIdentifierLike(token) {
|
|
2279
|
+
return token.kind === TokenKind.IDENTIFIER;
|
|
2280
|
+
}
|
|
2281
|
+
function isSelector(token) {
|
|
2282
|
+
return token.kind === TokenKind.SELECTOR;
|
|
2283
|
+
}
|
|
2284
|
+
function isBasicSelector(token) {
|
|
2285
|
+
if (token.kind !== TokenKind.SELECTOR)
|
|
2286
|
+
return false;
|
|
2287
|
+
return !token.value.startsWith('<');
|
|
2288
|
+
}
|
|
2289
|
+
function isLiteral(token) {
|
|
2290
|
+
return (token.kind === TokenKind.STRING ||
|
|
2291
|
+
token.kind === TokenKind.NUMBER ||
|
|
2292
|
+
token.kind === TokenKind.TEMPLATE);
|
|
2293
|
+
}
|
|
2294
|
+
function isReference(token) {
|
|
2295
|
+
return token.kind === TokenKind.IDENTIFIER;
|
|
2296
|
+
}
|
|
2297
|
+
function isTimeExpression(token) {
|
|
2298
|
+
return token.kind === TokenKind.TIME;
|
|
2299
|
+
}
|
|
2300
|
+
function isSymbol(token) {
|
|
2301
|
+
return token.kind === TokenKind.SYMBOL;
|
|
2302
|
+
}
|
|
2303
|
+
function isComment(token) {
|
|
2304
|
+
return token.kind === TokenKind.COMMENT;
|
|
2305
|
+
}
|
|
2306
|
+
function isIdentifier(token) {
|
|
2307
|
+
if (token.kind !== TokenKind.IDENTIFIER)
|
|
2308
|
+
return false;
|
|
2309
|
+
const lowerValue = token.value.toLowerCase();
|
|
2310
|
+
return (!COMMANDS.has(lowerValue) &&
|
|
2311
|
+
!TOKENIZER_KEYWORDS.has(lowerValue) &&
|
|
2312
|
+
!DOM_EVENTS.has(lowerValue) &&
|
|
2313
|
+
!CONTEXT_VARS.has(lowerValue));
|
|
2314
|
+
}
|
|
2315
|
+
function isString(token) {
|
|
2316
|
+
return token.kind === TokenKind.STRING;
|
|
2317
|
+
}
|
|
2318
|
+
function isNumber(token) {
|
|
2319
|
+
return token.kind === TokenKind.NUMBER;
|
|
2320
|
+
}
|
|
2321
|
+
function isBoolean(token) {
|
|
2322
|
+
if (token.kind === TokenKind.IDENTIFIER) {
|
|
2323
|
+
const v = token.value;
|
|
2324
|
+
return v === 'true' || v === 'false' || v === 'null' || v === 'undefined';
|
|
2325
|
+
}
|
|
2326
|
+
return false;
|
|
2327
|
+
}
|
|
2328
|
+
function isTemplateLiteral(token) {
|
|
2329
|
+
return token.kind === TokenKind.TEMPLATE;
|
|
2330
|
+
}
|
|
2331
|
+
function isQueryReference(token) {
|
|
2332
|
+
return token.kind === TokenKind.SELECTOR && token.value.startsWith('<');
|
|
2333
|
+
}
|
|
2334
|
+
function isIdSelector(token) {
|
|
2335
|
+
return token.kind === TokenKind.SELECTOR && token.value.startsWith('#');
|
|
2336
|
+
}
|
|
2337
|
+
function isClassSelector(token) {
|
|
2338
|
+
return token.kind === TokenKind.SELECTOR && token.value.startsWith('.');
|
|
2339
|
+
}
|
|
2340
|
+
function isCssSelector(token) {
|
|
2341
|
+
if (token.kind !== TokenKind.SELECTOR)
|
|
2342
|
+
return false;
|
|
2343
|
+
return !token.value.startsWith('#') && !token.value.startsWith('.');
|
|
2344
|
+
}
|
|
2345
|
+
function isBasicOperator(token) {
|
|
2346
|
+
if (token.kind !== TokenKind.OPERATOR)
|
|
2347
|
+
return false;
|
|
2348
|
+
const lowerValue = token.value.toLowerCase();
|
|
2349
|
+
return !LOGICAL_OPERATORS.has(lowerValue) && !COMPARISON_OPERATORS.has(lowerValue);
|
|
2350
|
+
}
|
|
2351
|
+
function isCommandTerminator(token) {
|
|
2352
|
+
const value = token.value.toLowerCase();
|
|
2353
|
+
return (value === 'then' || value === 'and' || value === 'else' || value === 'end' || value === 'on');
|
|
2354
|
+
}
|
|
2355
|
+
|
|
2356
|
+
function createLiteral(value, raw, pos) {
|
|
2357
|
+
return {
|
|
2358
|
+
type: 'literal',
|
|
2359
|
+
value,
|
|
2360
|
+
raw,
|
|
2361
|
+
start: pos.start,
|
|
2362
|
+
end: pos.end,
|
|
2363
|
+
line: pos.line,
|
|
2364
|
+
column: pos.column,
|
|
2365
|
+
};
|
|
2366
|
+
}
|
|
2367
|
+
function createIdentifier(name, pos) {
|
|
2368
|
+
return {
|
|
2369
|
+
type: 'identifier',
|
|
2370
|
+
name,
|
|
2371
|
+
start: pos.start,
|
|
2372
|
+
end: pos.end,
|
|
2373
|
+
line: pos.line,
|
|
2374
|
+
column: pos.column,
|
|
2375
|
+
};
|
|
2376
|
+
}
|
|
2377
|
+
function createBinaryExpression(operator, left, right, pos) {
|
|
2378
|
+
return {
|
|
2379
|
+
type: 'binaryExpression',
|
|
2380
|
+
operator,
|
|
2381
|
+
left,
|
|
2382
|
+
right,
|
|
2383
|
+
start: pos.start,
|
|
2384
|
+
end: pos.end,
|
|
2385
|
+
line: pos.line,
|
|
2386
|
+
column: pos.column,
|
|
2387
|
+
};
|
|
2388
|
+
}
|
|
2389
|
+
function createUnaryExpression(operator, argument, prefix, pos) {
|
|
2390
|
+
return {
|
|
2391
|
+
type: 'unaryExpression',
|
|
2392
|
+
operator,
|
|
2393
|
+
argument,
|
|
2394
|
+
prefix,
|
|
2395
|
+
start: pos.start,
|
|
2396
|
+
end: pos.end,
|
|
2397
|
+
line: pos.line,
|
|
2398
|
+
column: pos.column,
|
|
2399
|
+
};
|
|
2400
|
+
}
|
|
2401
|
+
function createCallExpression(callee, args, pos) {
|
|
2402
|
+
return {
|
|
2403
|
+
type: 'callExpression',
|
|
2404
|
+
callee,
|
|
2405
|
+
arguments: args,
|
|
2406
|
+
start: pos.start,
|
|
2407
|
+
end: pos.end,
|
|
2408
|
+
line: pos.line,
|
|
2409
|
+
column: pos.column,
|
|
2410
|
+
};
|
|
2411
|
+
}
|
|
2412
|
+
function createMemberExpression(object, property, computed, pos) {
|
|
2413
|
+
return {
|
|
2414
|
+
type: 'memberExpression',
|
|
2415
|
+
object,
|
|
2416
|
+
property,
|
|
2417
|
+
computed,
|
|
2418
|
+
start: pos.start,
|
|
2419
|
+
end: pos.end,
|
|
2420
|
+
line: pos.line,
|
|
2421
|
+
column: pos.column,
|
|
2422
|
+
};
|
|
2423
|
+
}
|
|
2424
|
+
function createSelector(value, pos) {
|
|
2425
|
+
return {
|
|
2426
|
+
type: 'selector',
|
|
2427
|
+
value,
|
|
2428
|
+
start: pos.start,
|
|
2429
|
+
end: pos.end,
|
|
2430
|
+
line: pos.line,
|
|
2431
|
+
column: pos.column,
|
|
2432
|
+
};
|
|
2433
|
+
}
|
|
2434
|
+
function createPossessiveExpression(object, property, pos) {
|
|
2435
|
+
return {
|
|
2436
|
+
type: 'possessiveExpression',
|
|
2437
|
+
object,
|
|
2438
|
+
property,
|
|
2439
|
+
start: pos.start,
|
|
2440
|
+
end: pos.end,
|
|
2441
|
+
line: pos.line,
|
|
2442
|
+
column: pos.column,
|
|
2443
|
+
};
|
|
2444
|
+
}
|
|
2445
|
+
function createBlock(commands, pos) {
|
|
2446
|
+
return {
|
|
2447
|
+
type: 'block',
|
|
2448
|
+
commands,
|
|
2449
|
+
start: pos.start,
|
|
2450
|
+
end: pos.end,
|
|
1988
2451
|
line: pos.line,
|
|
1989
2452
|
column: pos.column,
|
|
1990
2453
|
};
|
|
@@ -2070,6 +2533,18 @@ function createErrorCommandNode(pos, message, source) {
|
|
|
2070
2533
|
column: pos.column,
|
|
2071
2534
|
};
|
|
2072
2535
|
}
|
|
2536
|
+
function createPartialCommandNode(name, pos) {
|
|
2537
|
+
return {
|
|
2538
|
+
type: 'command',
|
|
2539
|
+
name,
|
|
2540
|
+
args: [],
|
|
2541
|
+
partial: true,
|
|
2542
|
+
start: pos.start,
|
|
2543
|
+
end: pos.end,
|
|
2544
|
+
line: pos.line,
|
|
2545
|
+
column: pos.column,
|
|
2546
|
+
};
|
|
2547
|
+
}
|
|
2073
2548
|
function createProgramNode(statements) {
|
|
2074
2549
|
debug.parse(`✅ createProgramNode: Called with ${statements.length} statements`);
|
|
2075
2550
|
if (statements.length === 0) {
|
|
@@ -2182,6 +2657,23 @@ function consumeOptionalKeyword(ctx, keyword) {
|
|
|
2182
2657
|
}
|
|
2183
2658
|
return false;
|
|
2184
2659
|
}
|
|
2660
|
+
function parseMaybeNamedArgument(ctx) {
|
|
2661
|
+
const checkpoint = ctx.savePosition();
|
|
2662
|
+
let name;
|
|
2663
|
+
if (ctx.checkIdentifierLike()) {
|
|
2664
|
+
const possible = ctx.peek().value;
|
|
2665
|
+
ctx.advance();
|
|
2666
|
+
if (ctx.check(':')) {
|
|
2667
|
+
ctx.advance();
|
|
2668
|
+
name = possible;
|
|
2669
|
+
}
|
|
2670
|
+
else {
|
|
2671
|
+
ctx.restorePosition(checkpoint);
|
|
2672
|
+
}
|
|
2673
|
+
}
|
|
2674
|
+
const value = ctx.parseExpression();
|
|
2675
|
+
return name !== undefined ? { name, value } : { value };
|
|
2676
|
+
}
|
|
2185
2677
|
|
|
2186
2678
|
class CommandNodeBuilder {
|
|
2187
2679
|
constructor(name) {
|
|
@@ -2329,26 +2821,13 @@ function parseTriggerCommand(ctx, identifierNode) {
|
|
|
2329
2821
|
ctx.advance();
|
|
2330
2822
|
const detailArgs = [];
|
|
2331
2823
|
while (!ctx.isAtEnd() && !ctx.check(')')) {
|
|
2332
|
-
const
|
|
2333
|
-
|
|
2334
|
-
if (ctx.checkIdentifierLike()) {
|
|
2335
|
-
const possibleName = ctx.peek().value;
|
|
2336
|
-
ctx.advance();
|
|
2337
|
-
if (ctx.check(':')) {
|
|
2338
|
-
ctx.advance();
|
|
2339
|
-
paramName = possibleName;
|
|
2340
|
-
}
|
|
2341
|
-
else {
|
|
2342
|
-
ctx.restorePosition(checkpoint);
|
|
2343
|
-
}
|
|
2344
|
-
}
|
|
2345
|
-
const value = ctx.parseExpression();
|
|
2346
|
-
if (paramName !== undefined) {
|
|
2824
|
+
const { name, value } = parseMaybeNamedArgument(ctx);
|
|
2825
|
+
if (name !== undefined) {
|
|
2347
2826
|
detailArgs.push({
|
|
2348
2827
|
type: 'objectLiteral',
|
|
2349
2828
|
properties: [
|
|
2350
2829
|
{
|
|
2351
|
-
key: { type: 'identifier', name
|
|
2830
|
+
key: { type: 'identifier', name },
|
|
2352
2831
|
value: value,
|
|
2353
2832
|
},
|
|
2354
2833
|
],
|
|
@@ -2393,12 +2872,13 @@ function parseTriggerCommand(ctx, identifierNode) {
|
|
|
2393
2872
|
}
|
|
2394
2873
|
}
|
|
2395
2874
|
const finalArgs = [...allArgs];
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2875
|
+
while (!isCommandBoundary(ctx)) {
|
|
2876
|
+
if (ctx.check('on') || ctx.check('to')) {
|
|
2877
|
+
const keyword = ctx.advance().value;
|
|
2878
|
+
finalArgs.push(ctx.createIdentifier(keyword));
|
|
2879
|
+
continue;
|
|
2401
2880
|
}
|
|
2881
|
+
finalArgs.push(ctx.parsePrimary());
|
|
2402
2882
|
}
|
|
2403
2883
|
return CommandNodeBuilder.fromIdentifier(identifierNode)
|
|
2404
2884
|
.withArgs(...finalArgs)
|
|
@@ -2543,7 +3023,35 @@ function parseRepeatCommand(ctx, commandToken) {
|
|
|
2543
3023
|
indexVariable = KEYWORDS.INDEX;
|
|
2544
3024
|
}
|
|
2545
3025
|
}
|
|
2546
|
-
|
|
3026
|
+
let commands;
|
|
3027
|
+
let elseCommands = null;
|
|
3028
|
+
let bottomTested = false;
|
|
3029
|
+
if (loopType === KEYWORDS.FOREVER) {
|
|
3030
|
+
const result = ctx.parseRepeatBody();
|
|
3031
|
+
commands = result.commands;
|
|
3032
|
+
if (result.terminator === 'else') {
|
|
3033
|
+
ctx.advance();
|
|
3034
|
+
elseCommands = ctx.parseCommandListUntilEnd();
|
|
3035
|
+
}
|
|
3036
|
+
else if (result.terminator === 'until' || result.terminator === 'while') {
|
|
3037
|
+
bottomTested = true;
|
|
3038
|
+
loopType = result.terminator;
|
|
3039
|
+
ctx.advance();
|
|
3040
|
+
condition = ctx.parseExpression();
|
|
3041
|
+
if (!ctx.check('end')) {
|
|
3042
|
+
throw new Error('Expected "end" to close repeat block');
|
|
3043
|
+
}
|
|
3044
|
+
ctx.advance();
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
3047
|
+
else {
|
|
3048
|
+
const result = ctx.parseCommandListUntilEndOrElse();
|
|
3049
|
+
commands = result.commands;
|
|
3050
|
+
if (result.hasElse) {
|
|
3051
|
+
ctx.advance();
|
|
3052
|
+
elseCommands = ctx.parseCommandListUntilEnd();
|
|
3053
|
+
}
|
|
3054
|
+
}
|
|
2547
3055
|
args.push({
|
|
2548
3056
|
type: 'identifier',
|
|
2549
3057
|
name: loopType,
|
|
@@ -2575,11 +3083,19 @@ function parseRepeatCommand(ctx, commandToken) {
|
|
|
2575
3083
|
if (indexVariable) {
|
|
2576
3084
|
args.push(createStringLiteral(indexVariable, pos));
|
|
2577
3085
|
}
|
|
2578
|
-
args.push(createBlock(commands, { ...pos, end: pos.end || 0 }));
|
|
2579
|
-
|
|
2580
|
-
.
|
|
2581
|
-
|
|
2582
|
-
|
|
3086
|
+
args.push(createBlock(commands, { ...pos, end: pos.end || 0 }));
|
|
3087
|
+
if (elseCommands !== null) {
|
|
3088
|
+
args.push(createBlock(elseCommands, { ...pos, end: pos.end || 0 }));
|
|
3089
|
+
}
|
|
3090
|
+
const builder = CommandNodeBuilder.from(commandToken).withArgs(...args);
|
|
3091
|
+
if (bottomTested) {
|
|
3092
|
+
builder.withModifier('bottomTested', {
|
|
3093
|
+
type: 'literal',
|
|
3094
|
+
value: true,
|
|
3095
|
+
...pos,
|
|
3096
|
+
});
|
|
3097
|
+
}
|
|
3098
|
+
return builder.endingAt(ctx.getPosition()).build();
|
|
2583
3099
|
}
|
|
2584
3100
|
function parseIfCommand(ctx, commandToken) {
|
|
2585
3101
|
const args = [];
|
|
@@ -2930,6 +3446,25 @@ function parseTransitionCommand(ctx, commandToken) {
|
|
|
2930
3446
|
.endingAt(ctx.getPosition())
|
|
2931
3447
|
.build();
|
|
2932
3448
|
}
|
|
3449
|
+
function parseStartCommand(ctx, identifierNode) {
|
|
3450
|
+
if (!ctx.match('view')) {
|
|
3451
|
+
throw new Error("start: expected 'view transition' (only `start view transition ... end` is supported)");
|
|
3452
|
+
}
|
|
3453
|
+
if (!ctx.match('transition')) {
|
|
3454
|
+
throw new Error("start view: expected 'transition'");
|
|
3455
|
+
}
|
|
3456
|
+
const modifiers = {};
|
|
3457
|
+
if (ctx.match('using')) {
|
|
3458
|
+
const nameExpr = ctx.parsePrimary();
|
|
3459
|
+
modifiers.transitionName = nameExpr;
|
|
3460
|
+
}
|
|
3461
|
+
const body = ctx.parseCommandListUntilEnd();
|
|
3462
|
+
return CommandNodeBuilder.fromIdentifier(identifierNode)
|
|
3463
|
+
.withArgs(...body)
|
|
3464
|
+
.withModifiers(modifiers)
|
|
3465
|
+
.endingAt(ctx.getPosition())
|
|
3466
|
+
.build();
|
|
3467
|
+
}
|
|
2933
3468
|
|
|
2934
3469
|
function parseRemoveCommand(ctx, identifierNode) {
|
|
2935
3470
|
const args = [];
|
|
@@ -3071,7 +3606,6 @@ const SWAP_STRATEGY_KEYWORDS = [
|
|
|
3071
3606
|
'morphouter',
|
|
3072
3607
|
];
|
|
3073
3608
|
function parseSwapCommand(ctx, identifierNode) {
|
|
3074
|
-
console.log('[PARSER DEBUG] parseSwapCommand called');
|
|
3075
3609
|
const args = [];
|
|
3076
3610
|
let strategyKeyword = null;
|
|
3077
3611
|
if (!ctx.isAtEnd()) {
|
|
@@ -3222,21 +3756,7 @@ function parseInstallCommand(ctx, commandToken) {
|
|
|
3222
3756
|
ctx.advance();
|
|
3223
3757
|
const params = [];
|
|
3224
3758
|
while (!ctx.isAtEnd() && !ctx.check(')')) {
|
|
3225
|
-
|
|
3226
|
-
let paramName;
|
|
3227
|
-
if (ctx.checkIdentifierLike()) {
|
|
3228
|
-
const possibleName = ctx.peek().value;
|
|
3229
|
-
ctx.advance();
|
|
3230
|
-
if (ctx.check(':')) {
|
|
3231
|
-
ctx.advance();
|
|
3232
|
-
paramName = possibleName;
|
|
3233
|
-
}
|
|
3234
|
-
else {
|
|
3235
|
-
ctx.restorePosition(checkpoint);
|
|
3236
|
-
}
|
|
3237
|
-
}
|
|
3238
|
-
const value = ctx.parseExpression();
|
|
3239
|
-
params.push(paramName !== undefined ? { name: paramName, value } : { value });
|
|
3759
|
+
params.push(parseMaybeNamedArgument(ctx));
|
|
3240
3760
|
if (ctx.check(',')) {
|
|
3241
3761
|
ctx.advance();
|
|
3242
3762
|
}
|
|
@@ -3514,6 +4034,10 @@ function parseCompoundCommand(ctx, identifierNode) {
|
|
|
3514
4034
|
return parseJsCommand(ctx, identifierNode);
|
|
3515
4035
|
case 'tell':
|
|
3516
4036
|
return parseTellCommand(ctx, identifierNode);
|
|
4037
|
+
case 'pick':
|
|
4038
|
+
return parsePickCommand(ctx, identifierNode);
|
|
4039
|
+
case 'start':
|
|
4040
|
+
return parseStartCommand(ctx, identifierNode);
|
|
3517
4041
|
case 'swap':
|
|
3518
4042
|
case 'morph':
|
|
3519
4043
|
return parseSwapCommand(ctx, identifierNode);
|
|
@@ -3621,7 +4145,7 @@ function parseFetchCommand(ctx, commandToken) {
|
|
|
3621
4145
|
if (!ctx.isAtEnd() && ctx.check('{')) {
|
|
3622
4146
|
modifiers['with'] = ctx.parsePrimary();
|
|
3623
4147
|
}
|
|
3624
|
-
for (let i = 0; i <
|
|
4148
|
+
for (let i = 0; i < 3 && !ctx.isAtEnd(); i++) {
|
|
3625
4149
|
if (ctx.check('as') && !modifiers['as']) {
|
|
3626
4150
|
ctx.advance();
|
|
3627
4151
|
if (!ctx.isAtEnd()) {
|
|
@@ -3640,491 +4164,335 @@ function parseFetchCommand(ctx, commandToken) {
|
|
|
3640
4164
|
}
|
|
3641
4165
|
continue;
|
|
3642
4166
|
}
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
}
|
|
3649
|
-
return builder.build();
|
|
3650
|
-
}
|
|
3651
|
-
function isFetchNakedNamedArgStart(ctx) {
|
|
3652
|
-
if (ctx.check('{'))
|
|
3653
|
-
return false;
|
|
3654
|
-
if (!ctx.checkIdentifierLike())
|
|
3655
|
-
return false;
|
|
3656
|
-
const next = ctx.peekAt(1);
|
|
3657
|
-
return next !== null && next.value === ':';
|
|
3658
|
-
}
|
|
3659
|
-
function parseFetchNakedNamedArgs(ctx) {
|
|
3660
|
-
const properties = [];
|
|
3661
|
-
const startPos = ctx.getPosition();
|
|
3662
|
-
do {
|
|
3663
|
-
if (!ctx.checkIdentifierLike())
|
|
3664
|
-
break;
|
|
3665
|
-
const keyToken = ctx.advance();
|
|
3666
|
-
const key = {
|
|
3667
|
-
type: 'identifier',
|
|
3668
|
-
name: keyToken.value,
|
|
3669
|
-
start: keyToken.start,
|
|
3670
|
-
end: keyToken.end,
|
|
3671
|
-
line: keyToken.line,
|
|
3672
|
-
column: keyToken.column,
|
|
3673
|
-
};
|
|
3674
|
-
ctx.consume(':', "Expected ':' after property name in fetch named arguments");
|
|
3675
|
-
const value = ctx.parsePrimary();
|
|
3676
|
-
if (value) {
|
|
3677
|
-
properties.push({ key, value });
|
|
3678
|
-
}
|
|
3679
|
-
} while (ctx.match(',') && !ctx.isAtEnd());
|
|
3680
|
-
const endPos = ctx.getPosition();
|
|
3681
|
-
return {
|
|
3682
|
-
type: 'objectLiteral',
|
|
3683
|
-
properties,
|
|
3684
|
-
start: startPos.start,
|
|
3685
|
-
end: endPos.end,
|
|
3686
|
-
line: startPos.line,
|
|
3687
|
-
column: startPos.column,
|
|
3688
|
-
};
|
|
3689
|
-
}
|
|
3690
|
-
function findJsEndBoundary(ctx, startPos) {
|
|
3691
|
-
const input = ctx.getInputSlice(startPos);
|
|
3692
|
-
if (!input) {
|
|
3693
|
-
return startPos;
|
|
3694
|
-
}
|
|
3695
|
-
let i = 0;
|
|
3696
|
-
while (i < input.length) {
|
|
3697
|
-
const ch = input[i];
|
|
3698
|
-
if (ch === "'" || ch === '\u2019' || ch === '\u2018') {
|
|
3699
|
-
i++;
|
|
3700
|
-
while (i < input.length && input[i] !== ch) {
|
|
3701
|
-
if (input[i] === '\\')
|
|
3702
|
-
i++;
|
|
3703
|
-
i++;
|
|
3704
|
-
}
|
|
3705
|
-
i++;
|
|
3706
|
-
continue;
|
|
3707
|
-
}
|
|
3708
|
-
if (ch === '"') {
|
|
3709
|
-
i++;
|
|
3710
|
-
while (i < input.length && input[i] !== '"') {
|
|
3711
|
-
if (input[i] === '\\')
|
|
3712
|
-
i++;
|
|
3713
|
-
i++;
|
|
3714
|
-
}
|
|
3715
|
-
i++;
|
|
3716
|
-
continue;
|
|
3717
|
-
}
|
|
3718
|
-
if (ch === '`') {
|
|
3719
|
-
i++;
|
|
3720
|
-
while (i < input.length && input[i] !== '`') {
|
|
3721
|
-
if (input[i] === '\\')
|
|
3722
|
-
i++;
|
|
3723
|
-
i++;
|
|
3724
|
-
}
|
|
3725
|
-
i++;
|
|
3726
|
-
continue;
|
|
3727
|
-
}
|
|
3728
|
-
if (ch === '/' && i + 1 < input.length && input[i + 1] === '/') {
|
|
3729
|
-
i += 2;
|
|
3730
|
-
while (i < input.length && input[i] !== '\n')
|
|
3731
|
-
i++;
|
|
3732
|
-
continue;
|
|
3733
|
-
}
|
|
3734
|
-
if (ch === '/' && i + 1 < input.length && input[i + 1] === '*') {
|
|
3735
|
-
i += 2;
|
|
3736
|
-
while (i < input.length &&
|
|
3737
|
-
!(input[i] === '*' && i + 1 < input.length && input[i + 1] === '/'))
|
|
3738
|
-
i++;
|
|
3739
|
-
i += 2;
|
|
3740
|
-
continue;
|
|
3741
|
-
}
|
|
3742
|
-
if ((ch === 'e' || ch === 'E') &&
|
|
3743
|
-
i + 3 <= input.length &&
|
|
3744
|
-
input.slice(i, i + 3).toLowerCase() === 'end') {
|
|
3745
|
-
const before = i === 0 || !/[a-zA-Z0-9_]/.test(input[i - 1]);
|
|
3746
|
-
const after = i + 3 >= input.length || !/[a-zA-Z0-9_]/.test(input[i + 3]);
|
|
3747
|
-
if (before && after) {
|
|
3748
|
-
return startPos + i;
|
|
3749
|
-
}
|
|
3750
|
-
}
|
|
3751
|
-
i++;
|
|
3752
|
-
}
|
|
3753
|
-
return startPos + input.length;
|
|
3754
|
-
}
|
|
3755
|
-
function parseJsCommand(ctx, identifierNode) {
|
|
3756
|
-
const parameters = [];
|
|
3757
|
-
if (ctx.match('(')) {
|
|
3758
|
-
while (!ctx.check(')') && !ctx.isAtEnd()) {
|
|
3759
|
-
if (ctx.checkIdentifierLike()) {
|
|
3760
|
-
parameters.push(ctx.advance().value);
|
|
3761
|
-
}
|
|
3762
|
-
ctx.match(',');
|
|
3763
|
-
}
|
|
3764
|
-
ctx.consume(')', 'Expected ) after js parameters');
|
|
3765
|
-
}
|
|
3766
|
-
const jsCodeStart = ctx.peek().start;
|
|
3767
|
-
const jsCodeEnd = findJsEndBoundary(ctx, jsCodeStart);
|
|
3768
|
-
while (!ctx.isAtEnd() && !ctx.check(KEYWORDS.END)) {
|
|
3769
|
-
if (ctx.peek().start >= jsCodeEnd)
|
|
3770
|
-
break;
|
|
3771
|
-
ctx.advance();
|
|
3772
|
-
}
|
|
3773
|
-
ctx.consume(KEYWORDS.END, 'Expected end after js code body');
|
|
3774
|
-
const rawSlice = ctx.getInputSlice(jsCodeStart, jsCodeEnd);
|
|
3775
|
-
const code = rawSlice.trim();
|
|
3776
|
-
const codeNode = {
|
|
3777
|
-
type: 'literal',
|
|
3778
|
-
value: code,
|
|
3779
|
-
start: identifierNode.start,
|
|
3780
|
-
end: ctx.getPosition().end,
|
|
3781
|
-
};
|
|
3782
|
-
const paramsNode = {
|
|
3783
|
-
type: 'arrayLiteral',
|
|
3784
|
-
elements: parameters.map(p => ({
|
|
3785
|
-
type: 'literal',
|
|
3786
|
-
value: p,
|
|
3787
|
-
start: identifierNode.start,
|
|
3788
|
-
end: identifierNode.end,
|
|
3789
|
-
})),
|
|
3790
|
-
start: identifierNode.start,
|
|
3791
|
-
end: ctx.getPosition().end,
|
|
3792
|
-
};
|
|
3793
|
-
return CommandNodeBuilder.fromIdentifier(identifierNode)
|
|
3794
|
-
.withArgs(codeNode, paramsNode)
|
|
3795
|
-
.endingAt(ctx.getPosition())
|
|
3796
|
-
.build();
|
|
3797
|
-
}
|
|
3798
|
-
function parseTellCommand(ctx, identifierNode) {
|
|
3799
|
-
const target = ctx.parseExpression();
|
|
3800
|
-
if (!target) {
|
|
3801
|
-
throw new Error('tell command requires a target expression');
|
|
3802
|
-
}
|
|
3803
|
-
const commands = [];
|
|
3804
|
-
while (!ctx.isAtEnd()) {
|
|
3805
|
-
if (ctx.checkIsCommand()) {
|
|
3806
|
-
try {
|
|
4167
|
+
if (ctx.check('do') && !modifiers['doNotThrow']) {
|
|
4168
|
+
const n1 = ctx.peekAt(1);
|
|
4169
|
+
const n2 = ctx.peekAt(2);
|
|
4170
|
+
if (n1?.value === 'not' && n2?.value === 'throw') {
|
|
4171
|
+
const doToken = ctx.advance();
|
|
3807
4172
|
ctx.advance();
|
|
3808
|
-
const
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
}
|
|
3819
|
-
if (ctx.match(KEYWORDS.AND)) {
|
|
3820
|
-
continue;
|
|
3821
|
-
}
|
|
3822
|
-
if (ctx.check(KEYWORDS.THEN) || ctx.check(KEYWORDS.ELSE) || ctx.check(KEYWORDS.END)) {
|
|
3823
|
-
break;
|
|
3824
|
-
}
|
|
3825
|
-
if (ctx.checkIsCommand()) {
|
|
3826
|
-
continue;
|
|
3827
|
-
}
|
|
3828
|
-
break;
|
|
3829
|
-
}
|
|
3830
|
-
else {
|
|
3831
|
-
break;
|
|
3832
|
-
}
|
|
3833
|
-
}
|
|
3834
|
-
if (commands.length === 0) {
|
|
3835
|
-
throw new Error('tell command requires at least one command after the target');
|
|
3836
|
-
}
|
|
3837
|
-
return CommandNodeBuilder.fromIdentifier(identifierNode)
|
|
3838
|
-
.withArgs(target, ...commands)
|
|
3839
|
-
.endingAt(ctx.getPosition())
|
|
3840
|
-
.build();
|
|
3841
|
-
}
|
|
3842
|
-
|
|
3843
|
-
const STOP_TOKENS = new Set([
|
|
3844
|
-
'then',
|
|
3845
|
-
'end',
|
|
3846
|
-
'to',
|
|
3847
|
-
'into',
|
|
3848
|
-
'on',
|
|
3849
|
-
'with',
|
|
3850
|
-
'from',
|
|
3851
|
-
'in',
|
|
3852
|
-
'by',
|
|
3853
|
-
'for',
|
|
3854
|
-
'while',
|
|
3855
|
-
'until',
|
|
3856
|
-
'unless',
|
|
3857
|
-
'else',
|
|
3858
|
-
'catch',
|
|
3859
|
-
'finally',
|
|
3860
|
-
]);
|
|
3861
|
-
const STOP_DELIMITERS = new Set([')', ']', '}', ',']);
|
|
3862
|
-
function mergeFragments(...fragments) {
|
|
3863
|
-
const merged = new Map();
|
|
3864
|
-
for (const fragment of fragments) {
|
|
3865
|
-
for (const [key, entry] of fragment) {
|
|
3866
|
-
const existing = merged.get(key);
|
|
3867
|
-
if (existing) {
|
|
3868
|
-
merged.set(key, {
|
|
3869
|
-
prefix: entry.prefix ?? existing.prefix,
|
|
3870
|
-
infix: entry.infix ?? existing.infix,
|
|
3871
|
-
});
|
|
3872
|
-
}
|
|
3873
|
-
else {
|
|
3874
|
-
merged.set(key, { ...entry });
|
|
4173
|
+
const throwToken = ctx.advance();
|
|
4174
|
+
modifiers['doNotThrow'] = {
|
|
4175
|
+
type: 'literal',
|
|
4176
|
+
value: true,
|
|
4177
|
+
start: doToken.start,
|
|
4178
|
+
end: throwToken.end,
|
|
4179
|
+
line: doToken.line,
|
|
4180
|
+
column: doToken.column,
|
|
4181
|
+
};
|
|
4182
|
+
continue;
|
|
3875
4183
|
}
|
|
3876
4184
|
}
|
|
4185
|
+
break;
|
|
3877
4186
|
}
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
bp: [bp, bp + 1],
|
|
3884
|
-
handler: handler ??
|
|
3885
|
-
((left, token, ctx) => ({
|
|
3886
|
-
type: 'binaryExpression',
|
|
3887
|
-
operator: token.value,
|
|
3888
|
-
left,
|
|
3889
|
-
right: ctx.parseExpr(bp + 1),
|
|
3890
|
-
start: left.start,
|
|
3891
|
-
})),
|
|
3892
|
-
},
|
|
3893
|
-
};
|
|
4187
|
+
const builder = CommandNodeBuilder.from(commandToken).withArgs(url).endingAt(ctx.getPosition());
|
|
4188
|
+
if (Object.keys(modifiers).length > 0) {
|
|
4189
|
+
builder.withModifiers(modifiers);
|
|
4190
|
+
}
|
|
4191
|
+
return builder.build();
|
|
3894
4192
|
}
|
|
3895
|
-
function
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
left,
|
|
3903
|
-
right: ctx.parseExpr(bp),
|
|
3904
|
-
start: left.start,
|
|
3905
|
-
})),
|
|
3906
|
-
},
|
|
3907
|
-
};
|
|
4193
|
+
function isFetchNakedNamedArgStart(ctx) {
|
|
4194
|
+
if (ctx.check('{'))
|
|
4195
|
+
return false;
|
|
4196
|
+
if (!ctx.checkIdentifierLike())
|
|
4197
|
+
return false;
|
|
4198
|
+
const next = ctx.peekAt(1);
|
|
4199
|
+
return next !== null && next.value === ':';
|
|
3908
4200
|
}
|
|
3909
|
-
function
|
|
4201
|
+
function parseFetchNakedNamedArgs(ctx) {
|
|
4202
|
+
const properties = [];
|
|
4203
|
+
const startPos = ctx.getPosition();
|
|
4204
|
+
do {
|
|
4205
|
+
if (!ctx.checkIdentifierLike())
|
|
4206
|
+
break;
|
|
4207
|
+
const keyToken = ctx.advance();
|
|
4208
|
+
const key = {
|
|
4209
|
+
type: 'identifier',
|
|
4210
|
+
name: keyToken.value,
|
|
4211
|
+
start: keyToken.start,
|
|
4212
|
+
end: keyToken.end,
|
|
4213
|
+
line: keyToken.line,
|
|
4214
|
+
column: keyToken.column,
|
|
4215
|
+
};
|
|
4216
|
+
ctx.consume(':', "Expected ':' after property name in fetch named arguments");
|
|
4217
|
+
const value = ctx.parsePrimary();
|
|
4218
|
+
if (value) {
|
|
4219
|
+
properties.push({ key, value });
|
|
4220
|
+
}
|
|
4221
|
+
} while (ctx.match(',') && !ctx.isAtEnd());
|
|
4222
|
+
const endPos = ctx.getPosition();
|
|
3910
4223
|
return {
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
operand: ctx.parseExpr(bp),
|
|
3918
|
-
start: token.start,
|
|
3919
|
-
})),
|
|
3920
|
-
},
|
|
4224
|
+
type: 'objectLiteral',
|
|
4225
|
+
properties,
|
|
4226
|
+
start: startPos.start,
|
|
4227
|
+
end: endPos.end,
|
|
4228
|
+
line: startPos.line,
|
|
4229
|
+
column: startPos.column,
|
|
3921
4230
|
};
|
|
3922
4231
|
}
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
}
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
const
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
'
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
}
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
}
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4232
|
+
function findJsEndBoundary(ctx, startPos) {
|
|
4233
|
+
const input = ctx.getInputSlice(startPos);
|
|
4234
|
+
if (!input) {
|
|
4235
|
+
return startPos;
|
|
4236
|
+
}
|
|
4237
|
+
let i = 0;
|
|
4238
|
+
while (i < input.length) {
|
|
4239
|
+
const ch = input[i];
|
|
4240
|
+
if (ch === "'" || ch === '\u2019' || ch === '\u2018') {
|
|
4241
|
+
i++;
|
|
4242
|
+
while (i < input.length && input[i] !== ch) {
|
|
4243
|
+
if (input[i] === '\\')
|
|
4244
|
+
i++;
|
|
4245
|
+
i++;
|
|
4246
|
+
}
|
|
4247
|
+
i++;
|
|
4248
|
+
continue;
|
|
4249
|
+
}
|
|
4250
|
+
if (ch === '"') {
|
|
4251
|
+
i++;
|
|
4252
|
+
while (i < input.length && input[i] !== '"') {
|
|
4253
|
+
if (input[i] === '\\')
|
|
4254
|
+
i++;
|
|
4255
|
+
i++;
|
|
4256
|
+
}
|
|
4257
|
+
i++;
|
|
4258
|
+
continue;
|
|
4259
|
+
}
|
|
4260
|
+
if (ch === '`') {
|
|
4261
|
+
i++;
|
|
4262
|
+
while (i < input.length && input[i] !== '`') {
|
|
4263
|
+
if (input[i] === '\\')
|
|
4264
|
+
i++;
|
|
4265
|
+
i++;
|
|
4266
|
+
}
|
|
4267
|
+
i++;
|
|
4268
|
+
continue;
|
|
4269
|
+
}
|
|
4270
|
+
if (ch === '/' && i + 1 < input.length && input[i + 1] === '/') {
|
|
4271
|
+
i += 2;
|
|
4272
|
+
while (i < input.length && input[i] !== '\n')
|
|
4273
|
+
i++;
|
|
4274
|
+
continue;
|
|
4275
|
+
}
|
|
4276
|
+
if (ch === '/' && i + 1 < input.length && input[i + 1] === '*') {
|
|
4277
|
+
i += 2;
|
|
4278
|
+
while (i < input.length &&
|
|
4279
|
+
!(input[i] === '*' && i + 1 < input.length && input[i + 1] === '/'))
|
|
4280
|
+
i++;
|
|
4281
|
+
i += 2;
|
|
4282
|
+
continue;
|
|
4283
|
+
}
|
|
4284
|
+
if ((ch === 'e' || ch === 'E') &&
|
|
4285
|
+
i + 3 <= input.length &&
|
|
4286
|
+
input.slice(i, i + 3).toLowerCase() === 'end') {
|
|
4287
|
+
const before = i === 0 || !/[a-zA-Z0-9_]/.test(input[i - 1]);
|
|
4288
|
+
const after = i + 3 >= input.length || !/[a-zA-Z0-9_]/.test(input[i + 3]);
|
|
4289
|
+
if (before && after) {
|
|
4290
|
+
return startPos + i;
|
|
4291
|
+
}
|
|
4292
|
+
}
|
|
4293
|
+
i++;
|
|
4294
|
+
}
|
|
4295
|
+
return startPos + input.length;
|
|
4296
|
+
}
|
|
4297
|
+
function parseJsCommand(ctx, identifierNode) {
|
|
4298
|
+
const parameters = [];
|
|
4299
|
+
if (ctx.match('(')) {
|
|
4300
|
+
while (!ctx.check(')') && !ctx.isAtEnd()) {
|
|
4301
|
+
if (ctx.checkIdentifierLike()) {
|
|
4302
|
+
parameters.push(ctx.advance().value);
|
|
4303
|
+
}
|
|
4304
|
+
ctx.match(',');
|
|
4305
|
+
}
|
|
4306
|
+
ctx.consume(')', 'Expected ) after js parameters');
|
|
4307
|
+
}
|
|
4308
|
+
const jsCodeStart = ctx.peek().start;
|
|
4309
|
+
const jsCodeEnd = findJsEndBoundary(ctx, jsCodeStart);
|
|
4310
|
+
while (!ctx.isAtEnd() && !ctx.check(KEYWORDS.END)) {
|
|
4311
|
+
if (ctx.peek().start >= jsCodeEnd)
|
|
4312
|
+
break;
|
|
4313
|
+
ctx.advance();
|
|
4314
|
+
}
|
|
4315
|
+
ctx.consume(KEYWORDS.END, 'Expected end after js code body');
|
|
4316
|
+
const rawSlice = ctx.getInputSlice(jsCodeStart, jsCodeEnd);
|
|
4317
|
+
const code = rawSlice.trim();
|
|
4318
|
+
const codeNode = {
|
|
4319
|
+
type: 'literal',
|
|
4320
|
+
value: code,
|
|
4321
|
+
start: identifierNode.start,
|
|
4322
|
+
end: ctx.getPosition().end,
|
|
4323
|
+
};
|
|
4324
|
+
const paramsNode = {
|
|
4325
|
+
type: 'arrayLiteral',
|
|
4326
|
+
elements: parameters.map(p => ({
|
|
4327
|
+
type: 'literal',
|
|
4328
|
+
value: p,
|
|
4329
|
+
start: identifierNode.start,
|
|
4330
|
+
end: identifierNode.end,
|
|
4331
|
+
})),
|
|
4332
|
+
start: identifierNode.start,
|
|
4333
|
+
end: ctx.getPosition().end,
|
|
4334
|
+
};
|
|
4335
|
+
return CommandNodeBuilder.fromIdentifier(identifierNode)
|
|
4336
|
+
.withArgs(codeNode, paramsNode)
|
|
4337
|
+
.endingAt(ctx.getPosition())
|
|
4338
|
+
.build();
|
|
4339
|
+
}
|
|
4340
|
+
function parseTellCommand(ctx, identifierNode) {
|
|
4341
|
+
const target = ctx.parseExpression();
|
|
4342
|
+
if (!target) {
|
|
4343
|
+
throw new Error('tell command requires a target expression');
|
|
4344
|
+
}
|
|
4345
|
+
const commands = [];
|
|
4346
|
+
while (!ctx.isAtEnd()) {
|
|
4347
|
+
if (ctx.checkIsCommand()) {
|
|
4348
|
+
try {
|
|
4349
|
+
ctx.advance();
|
|
4350
|
+
const cmd = ctx.parseCommand();
|
|
4351
|
+
if (cmd) {
|
|
4352
|
+
commands.push(cmd);
|
|
4353
|
+
}
|
|
4354
|
+
else {
|
|
4355
|
+
break;
|
|
4356
|
+
}
|
|
4357
|
+
}
|
|
4358
|
+
catch {
|
|
4359
|
+
break;
|
|
4360
|
+
}
|
|
4361
|
+
if (ctx.match(KEYWORDS.AND)) {
|
|
4362
|
+
continue;
|
|
4363
|
+
}
|
|
4364
|
+
if (ctx.check(KEYWORDS.THEN) || ctx.check(KEYWORDS.ELSE) || ctx.check(KEYWORDS.END)) {
|
|
4365
|
+
break;
|
|
4366
|
+
}
|
|
4367
|
+
if (ctx.checkIsCommand()) {
|
|
4368
|
+
continue;
|
|
4369
|
+
}
|
|
4370
|
+
break;
|
|
4371
|
+
}
|
|
4372
|
+
else {
|
|
4373
|
+
break;
|
|
4374
|
+
}
|
|
4375
|
+
}
|
|
4376
|
+
if (commands.length === 0) {
|
|
4377
|
+
throw new Error('tell command requires at least one command after the target');
|
|
4378
|
+
}
|
|
4379
|
+
return CommandNodeBuilder.fromIdentifier(identifierNode)
|
|
4380
|
+
.withArgs(target, ...commands)
|
|
4381
|
+
.endingAt(ctx.getPosition())
|
|
4382
|
+
.build();
|
|
4383
|
+
}
|
|
4384
|
+
function parsePickCommand(ctx, identifierNode) {
|
|
4385
|
+
const builder = CommandNodeBuilder.fromIdentifier(identifierNode);
|
|
4386
|
+
consumeOptionalKeyword(ctx, KEYWORDS.THE);
|
|
4387
|
+
const variantToken = ctx.peek();
|
|
4388
|
+
const variantName = variantToken.value;
|
|
4389
|
+
const makeStringLiteral = (value) => ({
|
|
4390
|
+
type: 'literal',
|
|
4391
|
+
value,
|
|
4392
|
+
start: identifierNode.start,
|
|
4393
|
+
end: identifierNode.end,
|
|
4394
|
+
});
|
|
4395
|
+
const consumeSource = () => {
|
|
4396
|
+
if (!ctx.match('of', 'from')) {
|
|
4397
|
+
throw new Error(`pick: expected 'of' or 'from' before source expression`);
|
|
4398
|
+
}
|
|
4399
|
+
return ctx.parseExpression();
|
|
4400
|
+
};
|
|
4401
|
+
if (variantName === 'first') {
|
|
4402
|
+
ctx.advance();
|
|
4403
|
+
const count = ctx.parsePrimary();
|
|
4404
|
+
const source = consumeSource();
|
|
4405
|
+
return builder
|
|
4406
|
+
.withArgs(source)
|
|
4407
|
+
.withModifier('variant', makeStringLiteral('first'))
|
|
4408
|
+
.withModifier('count', count)
|
|
4409
|
+
.endingAt(ctx.getPosition())
|
|
4410
|
+
.build();
|
|
4411
|
+
}
|
|
4412
|
+
if (variantName === 'last') {
|
|
4413
|
+
ctx.advance();
|
|
4414
|
+
const count = ctx.parsePrimary();
|
|
4415
|
+
const source = consumeSource();
|
|
4416
|
+
return builder
|
|
4417
|
+
.withArgs(source)
|
|
4418
|
+
.withModifier('variant', makeStringLiteral('last'))
|
|
4419
|
+
.withModifier('count', count)
|
|
4420
|
+
.endingAt(ctx.getPosition())
|
|
4421
|
+
.build();
|
|
4422
|
+
}
|
|
4423
|
+
if (variantName === 'random') {
|
|
4424
|
+
ctx.advance();
|
|
4425
|
+
let countNode;
|
|
4426
|
+
if (!ctx.check('of') && !ctx.check('from')) {
|
|
4427
|
+
countNode = ctx.parsePrimary();
|
|
4428
|
+
}
|
|
4429
|
+
const source = consumeSource();
|
|
4430
|
+
const b = builder.withArgs(source).withModifier('variant', makeStringLiteral('random'));
|
|
4431
|
+
if (countNode)
|
|
4432
|
+
b.withModifier('count', countNode);
|
|
4433
|
+
return b.endingAt(ctx.getPosition()).build();
|
|
4434
|
+
}
|
|
4435
|
+
if (variantName === 'item' ||
|
|
4436
|
+
variantName === 'items' ||
|
|
4437
|
+
variantName === 'character' ||
|
|
4438
|
+
variantName === 'characters') {
|
|
4439
|
+
ctx.advance();
|
|
4440
|
+
ctx.match('at', 'from');
|
|
4441
|
+
let rangeStart;
|
|
4442
|
+
if (ctx.match('start')) {
|
|
4443
|
+
rangeStart = makeStringLiteral('start');
|
|
4444
|
+
}
|
|
4445
|
+
else {
|
|
4446
|
+
rangeStart = ctx.parsePrimary();
|
|
4447
|
+
}
|
|
4448
|
+
let rangeEnd;
|
|
4449
|
+
let endIsEndKeyword = false;
|
|
4450
|
+
if (ctx.match('to') || ctx.match('..')) {
|
|
4451
|
+
if (ctx.match('end')) {
|
|
4452
|
+
endIsEndKeyword = true;
|
|
4453
|
+
}
|
|
4454
|
+
else {
|
|
4455
|
+
rangeEnd = ctx.parsePrimary();
|
|
4456
|
+
}
|
|
4457
|
+
}
|
|
4458
|
+
let mode = 'default';
|
|
4459
|
+
if (ctx.match('inclusive'))
|
|
4460
|
+
mode = 'inclusive';
|
|
4461
|
+
else if (ctx.match('exclusive'))
|
|
4462
|
+
mode = 'exclusive';
|
|
4463
|
+
const source = consumeSource();
|
|
4464
|
+
const b = builder
|
|
4465
|
+
.withArgs(source)
|
|
4466
|
+
.withModifier('variant', makeStringLiteral('range'))
|
|
4467
|
+
.withModifier('rangeStart', rangeStart)
|
|
4468
|
+
.withModifier('rangeMode', makeStringLiteral(mode));
|
|
4469
|
+
if (endIsEndKeyword) {
|
|
4470
|
+
b.withModifier('rangeEnd', makeStringLiteral('end'));
|
|
4471
|
+
}
|
|
4472
|
+
else if (rangeEnd) {
|
|
4473
|
+
b.withModifier('rangeEnd', rangeEnd);
|
|
4474
|
+
}
|
|
4475
|
+
return b.endingAt(ctx.getPosition()).build();
|
|
4476
|
+
}
|
|
4477
|
+
if (variantName === 'match' || variantName === 'matches') {
|
|
4478
|
+
ctx.advance();
|
|
4479
|
+
ctx.match('of');
|
|
4480
|
+
const regex = ctx.parsePrimary();
|
|
4481
|
+
let flags;
|
|
4482
|
+
if (ctx.matchOperator('|')) {
|
|
4483
|
+
flags = ctx.advance().value;
|
|
4484
|
+
}
|
|
4485
|
+
const source = consumeSource();
|
|
4486
|
+
const b = builder
|
|
4487
|
+
.withArgs(source)
|
|
4488
|
+
.withModifier('variant', makeStringLiteral(variantName === 'match' ? 'match' : 'matches'))
|
|
4489
|
+
.withModifier('regex', regex);
|
|
4490
|
+
if (flags)
|
|
4491
|
+
b.withModifier('flags', makeStringLiteral(flags));
|
|
4492
|
+
return b.endingAt(ctx.getPosition()).build();
|
|
4493
|
+
}
|
|
4494
|
+
return parseRegularCommand(ctx, identifierNode);
|
|
4495
|
+
}
|
|
4128
4496
|
|
|
4129
4497
|
function parseTimeToMs(timeStr) {
|
|
4130
4498
|
if (timeStr.endsWith('ms'))
|
|
@@ -4216,7 +4584,13 @@ class Parser {
|
|
|
4216
4584
|
warnings: this.warnings,
|
|
4217
4585
|
};
|
|
4218
4586
|
}
|
|
4219
|
-
|
|
4587
|
+
const topToken = this.peek();
|
|
4588
|
+
const topPluginFeature = topToken && getRegisteredFeature(topToken.value) ? topToken.value : null;
|
|
4589
|
+
if (this.check('init') ||
|
|
4590
|
+
this.check('on') ||
|
|
4591
|
+
this.check('def') ||
|
|
4592
|
+
this.checkComment() ||
|
|
4593
|
+
topPluginFeature !== null) {
|
|
4220
4594
|
const statements = [];
|
|
4221
4595
|
while (!this.isAtEnd()) {
|
|
4222
4596
|
if (this.checkComment()) {
|
|
@@ -4245,6 +4619,16 @@ class Parser {
|
|
|
4245
4619
|
}
|
|
4246
4620
|
}
|
|
4247
4621
|
else {
|
|
4622
|
+
const tok = this.peek();
|
|
4623
|
+
const pluginParse = tok ? getRegisteredFeature(tok.value) : undefined;
|
|
4624
|
+
if (pluginParse) {
|
|
4625
|
+
const featureToken = this.advance();
|
|
4626
|
+
const featureNode = pluginParse(this.getContext(), featureToken);
|
|
4627
|
+
if (featureNode) {
|
|
4628
|
+
statements.push(featureNode);
|
|
4629
|
+
}
|
|
4630
|
+
continue;
|
|
4631
|
+
}
|
|
4248
4632
|
break;
|
|
4249
4633
|
}
|
|
4250
4634
|
}
|
|
@@ -4364,7 +4748,14 @@ class Parser {
|
|
|
4364
4748
|
};
|
|
4365
4749
|
}
|
|
4366
4750
|
if (!this.isAtEnd()) {
|
|
4367
|
-
|
|
4751
|
+
const next = this.peek();
|
|
4752
|
+
const valueLikeKinds = ['number', 'string', 'identifier'];
|
|
4753
|
+
if (ast && valueLikeKinds.includes(next.kind)) {
|
|
4754
|
+
this.addError(`Unexpected token: ${next.value} (missing operator between values? expected one of +, -, *, /, etc.)`);
|
|
4755
|
+
}
|
|
4756
|
+
else {
|
|
4757
|
+
this.addError(`Unexpected token: ${next.value}`);
|
|
4758
|
+
}
|
|
4368
4759
|
return {
|
|
4369
4760
|
success: false,
|
|
4370
4761
|
node: ast || this.createErrorNode(),
|
|
@@ -4442,7 +4833,8 @@ class Parser {
|
|
|
4442
4833
|
try {
|
|
4443
4834
|
this.parseExpressionPratt(0);
|
|
4444
4835
|
}
|
|
4445
|
-
catch {
|
|
4836
|
+
catch (recoveryErr) {
|
|
4837
|
+
debug.parse('arrow body discarded after error:', recoveryErr instanceof Error ? recoveryErr.message : String(recoveryErr));
|
|
4446
4838
|
}
|
|
4447
4839
|
}
|
|
4448
4840
|
return this.createErrorNode();
|
|
@@ -4501,143 +4893,8 @@ class Parser {
|
|
|
4501
4893
|
atEnd: () => self.current >= self.tokens.length,
|
|
4502
4894
|
};
|
|
4503
4895
|
}
|
|
4504
|
-
parseAssignment() {
|
|
4505
|
-
let expr = this.parseLogicalOr();
|
|
4506
|
-
if (this.match('=')) {
|
|
4507
|
-
if (this.check('>')) {
|
|
4508
|
-
this.advance();
|
|
4509
|
-
this.addError('Arrow functions (=>) are not supported in hyperscript. ' +
|
|
4510
|
-
'Use "js ... end" blocks for JavaScript callbacks.');
|
|
4511
|
-
if (!this.isAtEnd()) {
|
|
4512
|
-
try {
|
|
4513
|
-
this.parseExpression();
|
|
4514
|
-
}
|
|
4515
|
-
catch {
|
|
4516
|
-
}
|
|
4517
|
-
}
|
|
4518
|
-
return this.createErrorNode();
|
|
4519
|
-
}
|
|
4520
|
-
const operator = this.previous().value;
|
|
4521
|
-
const right = this.parseAssignment();
|
|
4522
|
-
expr = this.createBinaryExpression(operator, expr, right);
|
|
4523
|
-
}
|
|
4524
|
-
return expr;
|
|
4525
|
-
}
|
|
4526
|
-
parseLogicalOr() {
|
|
4527
|
-
let expr = this.parseLogicalAnd();
|
|
4528
|
-
while (this.match('or')) {
|
|
4529
|
-
const operator = this.previous().value;
|
|
4530
|
-
const right = this.parseLogicalAnd();
|
|
4531
|
-
expr = this.createBinaryExpression(operator, expr, right);
|
|
4532
|
-
}
|
|
4533
|
-
return expr;
|
|
4534
|
-
}
|
|
4535
4896
|
parseLogicalAnd() {
|
|
4536
|
-
|
|
4537
|
-
while (this.match('and')) {
|
|
4538
|
-
const operator = this.previous().value;
|
|
4539
|
-
const right = this.parseEquality();
|
|
4540
|
-
expr = this.createBinaryExpression(operator, expr, right);
|
|
4541
|
-
}
|
|
4542
|
-
return expr;
|
|
4543
|
-
}
|
|
4544
|
-
parseEquality() {
|
|
4545
|
-
let expr = this.parseComparison();
|
|
4546
|
-
while (this.matchComparisonOperator() ||
|
|
4547
|
-
this.match('is', 'match', 'matches', 'contains', 'include', 'includes', 'in', 'of', 'as', 'really')) {
|
|
4548
|
-
const operator = this.previous().value;
|
|
4549
|
-
if (Parser.POSTFIX_UNARY_OPERATORS.has(operator)) {
|
|
4550
|
-
expr = this.createUnaryExpression(operator, expr, false);
|
|
4551
|
-
continue;
|
|
4552
|
-
}
|
|
4553
|
-
const right = this.parseComparison();
|
|
4554
|
-
expr = this.createBinaryExpression(operator, expr, right);
|
|
4555
|
-
}
|
|
4556
|
-
return expr;
|
|
4557
|
-
}
|
|
4558
|
-
parseComparison() {
|
|
4559
|
-
let expr = this.parseAddition();
|
|
4560
|
-
while (this.matchComparisonOperator()) {
|
|
4561
|
-
const operator = this.previous().value;
|
|
4562
|
-
if (Parser.POSTFIX_UNARY_OPERATORS.has(operator)) {
|
|
4563
|
-
expr = this.createUnaryExpression(operator, expr, false);
|
|
4564
|
-
continue;
|
|
4565
|
-
}
|
|
4566
|
-
const right = this.parseAddition();
|
|
4567
|
-
expr = this.createBinaryExpression(operator, expr, right);
|
|
4568
|
-
}
|
|
4569
|
-
return expr;
|
|
4570
|
-
}
|
|
4571
|
-
parseAddition() {
|
|
4572
|
-
let expr = this.parseMultiplication();
|
|
4573
|
-
while (this.match('+', '-') || this.matchOperator('+') || this.matchOperator('-')) {
|
|
4574
|
-
const operator = this.previous().value;
|
|
4575
|
-
if (this.check('+') || this.check('-')) {
|
|
4576
|
-
this.addError(`Invalid operator combination: ${operator}${this.peek().value}`);
|
|
4577
|
-
return expr;
|
|
4578
|
-
}
|
|
4579
|
-
if (this.isAtEnd()) {
|
|
4580
|
-
this.addError(`Expected expression after '${operator}' operator`);
|
|
4581
|
-
return expr;
|
|
4582
|
-
}
|
|
4583
|
-
const right = this.parseMultiplication();
|
|
4584
|
-
expr = this.createBinaryExpression(operator, expr, right);
|
|
4585
|
-
}
|
|
4586
|
-
return expr;
|
|
4587
|
-
}
|
|
4588
|
-
parseMultiplication() {
|
|
4589
|
-
let expr = this.parseUnary();
|
|
4590
|
-
while (this.match('*', '/', '%', 'mod')) {
|
|
4591
|
-
const operator = this.previous().value;
|
|
4592
|
-
if (this.check('*') ||
|
|
4593
|
-
this.check('/') ||
|
|
4594
|
-
this.check('%') ||
|
|
4595
|
-
this.check('+') ||
|
|
4596
|
-
this.check('-')) {
|
|
4597
|
-
const nextOp = this.peek().value;
|
|
4598
|
-
if (operator === '*' && nextOp === '*') {
|
|
4599
|
-
this.addError(`Unexpected token: ${nextOp}`);
|
|
4600
|
-
}
|
|
4601
|
-
else {
|
|
4602
|
-
this.addError(`Invalid operator combination: ${operator}${nextOp}`);
|
|
4603
|
-
}
|
|
4604
|
-
return expr;
|
|
4605
|
-
}
|
|
4606
|
-
if (this.isAtEnd()) {
|
|
4607
|
-
this.addError(`Expected expression after '${operator}' operator`);
|
|
4608
|
-
return expr;
|
|
4609
|
-
}
|
|
4610
|
-
const right = this.parseUnary();
|
|
4611
|
-
expr = this.createBinaryExpression(operator, expr, right);
|
|
4612
|
-
}
|
|
4613
|
-
return expr;
|
|
4614
|
-
}
|
|
4615
|
-
parseUnary() {
|
|
4616
|
-
if (this.match('not', 'no', 'exists', 'some', '-', '+')) {
|
|
4617
|
-
const operator = this.previous().value;
|
|
4618
|
-
if (this.isAtEnd()) {
|
|
4619
|
-
this.addError(`Expected expression after '${operator}' operator`);
|
|
4620
|
-
return this.createErrorNode();
|
|
4621
|
-
}
|
|
4622
|
-
const expr = this.parseUnary();
|
|
4623
|
-
return this.createUnaryExpression(operator, expr, true);
|
|
4624
|
-
}
|
|
4625
|
-
if (this.check('does') &&
|
|
4626
|
-
this.current + 1 < this.tokens.length &&
|
|
4627
|
-
this.tokens[this.current + 1].value === 'not' &&
|
|
4628
|
-
this.current + 2 < this.tokens.length &&
|
|
4629
|
-
this.tokens[this.current + 2].value === 'exist') {
|
|
4630
|
-
this.advance();
|
|
4631
|
-
this.advance();
|
|
4632
|
-
this.advance();
|
|
4633
|
-
if (this.isAtEnd()) {
|
|
4634
|
-
this.addError(`Expected expression after 'does not exist' operator`);
|
|
4635
|
-
return this.createErrorNode();
|
|
4636
|
-
}
|
|
4637
|
-
const expr = this.parseUnary();
|
|
4638
|
-
return this.createUnaryExpression('does not exist', expr, true);
|
|
4639
|
-
}
|
|
4640
|
-
return this.parseImplicitBinary();
|
|
4897
|
+
return this.parseExpressionPratt(11);
|
|
4641
4898
|
}
|
|
4642
4899
|
parseImplicitBinary() {
|
|
4643
4900
|
let expr = this.parseCall();
|
|
@@ -4785,10 +5042,11 @@ class Parser {
|
|
|
4785
5042
|
parseTriggerCommand(identifierNode) {
|
|
4786
5043
|
return parseTriggerCommand(this.getContext(), identifierNode);
|
|
4787
5044
|
}
|
|
4788
|
-
|
|
5045
|
+
parseCommandListUntilTerminator(extraStops) {
|
|
4789
5046
|
const commands = [];
|
|
4790
|
-
|
|
4791
|
-
|
|
5047
|
+
const isStop = () => this.check('end') || extraStops.some(s => this.check(s));
|
|
5048
|
+
debug.parse('🔄 parseCommandListUntilTerminator: Starting (extraStops:', extraStops.join(','), ')');
|
|
5049
|
+
while (!this.isAtEnd() && !isStop()) {
|
|
4792
5050
|
debug.parse('📍 Loop iteration, current token:', this.peek().value, 'kind:', this.peek().kind);
|
|
4793
5051
|
let parsedCommand = false;
|
|
4794
5052
|
const isCommandToken = this.checkIsCommand();
|
|
@@ -4800,7 +5058,7 @@ class Parser {
|
|
|
4800
5058
|
try {
|
|
4801
5059
|
const cmd = this.parseCommand();
|
|
4802
5060
|
if (this.error && this.error !== savedError) {
|
|
4803
|
-
debug.parse('⚠️
|
|
5061
|
+
debug.parse('⚠️ parseCommandListUntilTerminator: Command parsing added error, restoring. Error was:', this.error.message);
|
|
4804
5062
|
this.error = savedError;
|
|
4805
5063
|
}
|
|
4806
5064
|
if (cmd) {
|
|
@@ -4810,7 +5068,7 @@ class Parser {
|
|
|
4810
5068
|
}
|
|
4811
5069
|
}
|
|
4812
5070
|
catch (error) {
|
|
4813
|
-
debug.parse('⚠️
|
|
5071
|
+
debug.parse('⚠️ parseCommandListUntilTerminator: Command parsing threw, restoring:', error instanceof Error ? error.message : String(error));
|
|
4814
5072
|
this.error = savedError;
|
|
4815
5073
|
}
|
|
4816
5074
|
}
|
|
@@ -4823,7 +5081,7 @@ class Parser {
|
|
|
4823
5081
|
}
|
|
4824
5082
|
debug.parse('📍 After parsing command, current token:', this.peek().value);
|
|
4825
5083
|
while (!this.isAtEnd() &&
|
|
4826
|
-
!
|
|
5084
|
+
!isStop() &&
|
|
4827
5085
|
!this.checkIsCommand() &&
|
|
4828
5086
|
!this.isCommand(this.peek().value) &&
|
|
4829
5087
|
!this.check('then') &&
|
|
@@ -4845,6 +5103,21 @@ class Parser {
|
|
|
4845
5103
|
break;
|
|
4846
5104
|
}
|
|
4847
5105
|
}
|
|
5106
|
+
let terminator = 'end';
|
|
5107
|
+
for (const s of extraStops) {
|
|
5108
|
+
if (this.check(s)) {
|
|
5109
|
+
terminator = s;
|
|
5110
|
+
break;
|
|
5111
|
+
}
|
|
5112
|
+
}
|
|
5113
|
+
if (!this.check('end') && !extraStops.some(s => this.check(s))) {
|
|
5114
|
+
terminator = '';
|
|
5115
|
+
}
|
|
5116
|
+
debug.parse('✅ parseCommandListUntilTerminator: parsed', commands.length, 'commands (terminator:', terminator, ')');
|
|
5117
|
+
return { commands, terminator };
|
|
5118
|
+
}
|
|
5119
|
+
parseCommandListUntilEnd() {
|
|
5120
|
+
const { commands } = this.parseCommandListUntilTerminator([]);
|
|
4848
5121
|
debug.parse('🔍 After loop, checking for "end". Current token:', this.peek().value);
|
|
4849
5122
|
if (this.check('end')) {
|
|
4850
5123
|
debug.parse('✅ Found "end", consuming it');
|
|
@@ -4854,9 +5127,35 @@ class Parser {
|
|
|
4854
5127
|
debug.parse('❌ ERROR: Expected "end" but got:', this.peek().value, 'at position:', this.peek().start);
|
|
4855
5128
|
throw new Error('Expected "end" to close repeat block');
|
|
4856
5129
|
}
|
|
4857
|
-
debug.parse('✅ parseCommandListUntilEnd: Successfully parsed', commands.length, 'commands');
|
|
4858
5130
|
return commands;
|
|
4859
5131
|
}
|
|
5132
|
+
parseCommandListUntilEndOrElse() {
|
|
5133
|
+
const { commands, terminator } = this.parseCommandListUntilTerminator(['else']);
|
|
5134
|
+
const hasElse = terminator === 'else';
|
|
5135
|
+
if (!hasElse) {
|
|
5136
|
+
if (this.check('end')) {
|
|
5137
|
+
this.advance();
|
|
5138
|
+
}
|
|
5139
|
+
else {
|
|
5140
|
+
throw new Error('Expected "end" to close repeat block');
|
|
5141
|
+
}
|
|
5142
|
+
}
|
|
5143
|
+
return { commands, hasElse };
|
|
5144
|
+
}
|
|
5145
|
+
parseRepeatBody() {
|
|
5146
|
+
const { commands, terminator } = this.parseCommandListUntilTerminator([
|
|
5147
|
+
'else',
|
|
5148
|
+
'until',
|
|
5149
|
+
'while',
|
|
5150
|
+
]);
|
|
5151
|
+
if (terminator === 'end') {
|
|
5152
|
+
this.advance();
|
|
5153
|
+
}
|
|
5154
|
+
else if (terminator === '') {
|
|
5155
|
+
throw new Error('Expected "end", "else", "until", or "while" to close repeat block');
|
|
5156
|
+
}
|
|
5157
|
+
return { commands, terminator };
|
|
5158
|
+
}
|
|
4860
5159
|
parseRepeatCommand(commandToken) {
|
|
4861
5160
|
return parseRepeatCommand(this.getContext(), commandToken);
|
|
4862
5161
|
}
|
|
@@ -4894,9 +5193,15 @@ class Parser {
|
|
|
4894
5193
|
expr = this.finishCall(expr);
|
|
4895
5194
|
}
|
|
4896
5195
|
else if (this.match('.')) {
|
|
4897
|
-
const name = this.
|
|
5196
|
+
const name = this.consumeIdentifierLike("Expected property name after '.' - malformed member access");
|
|
4898
5197
|
expr = this.createMemberExpression(expr, this.createIdentifier(name.value), false);
|
|
4899
5198
|
}
|
|
5199
|
+
else if (this.match('?.')) {
|
|
5200
|
+
const name = this.consumeIdentifierLike("Expected property name after '?.' - malformed optional access");
|
|
5201
|
+
const memberNode = this.createMemberExpression(expr, this.createIdentifier(name.value), false);
|
|
5202
|
+
memberNode.optional = true;
|
|
5203
|
+
expr = memberNode;
|
|
5204
|
+
}
|
|
4900
5205
|
else if (this.match('[')) {
|
|
4901
5206
|
const index = this.parseExpression();
|
|
4902
5207
|
this.consume(']', "Expected ']' after array index");
|
|
@@ -4996,7 +5301,9 @@ class Parser {
|
|
|
4996
5301
|
if (this.matchQueryReference()) {
|
|
4997
5302
|
const queryValue = this.previous().value;
|
|
4998
5303
|
const selector = queryValue.slice(1, -2).trim();
|
|
4999
|
-
|
|
5304
|
+
const node = this.createSelector(selector);
|
|
5305
|
+
node.fromQuery = true;
|
|
5306
|
+
return node;
|
|
5000
5307
|
}
|
|
5001
5308
|
if (this.matchSelector()) {
|
|
5002
5309
|
return this.createSelector(this.previous().value);
|
|
@@ -5107,15 +5414,23 @@ class Parser {
|
|
|
5107
5414
|
}
|
|
5108
5415
|
this.current = hashPos;
|
|
5109
5416
|
}
|
|
5417
|
+
if (!this.isAtEnd() &&
|
|
5418
|
+
!this.checkBasicOperator() &&
|
|
5419
|
+
!this.check('then') &&
|
|
5420
|
+
!this.check('else') &&
|
|
5421
|
+
!this.check('end')) {
|
|
5422
|
+
return this.parseNavigationFunction(token.value);
|
|
5423
|
+
}
|
|
5110
5424
|
return this.createIdentifier(token.value);
|
|
5111
5425
|
}
|
|
5112
|
-
|
|
5426
|
+
const nextIsDotChain = this.check('.') || this.check('?.');
|
|
5427
|
+
if (token.value === 'my' && !nextIsDotChain) {
|
|
5113
5428
|
return this.parseContextPropertyAccess('me');
|
|
5114
5429
|
}
|
|
5115
|
-
if (token.value === 'its' && !
|
|
5430
|
+
if (token.value === 'its' && !nextIsDotChain) {
|
|
5116
5431
|
return this.parseContextPropertyAccess('it');
|
|
5117
5432
|
}
|
|
5118
|
-
if (token.value === 'your' && !
|
|
5433
|
+
if (token.value === 'your' && !nextIsDotChain) {
|
|
5119
5434
|
return this.parseContextPropertyAccess('you');
|
|
5120
5435
|
}
|
|
5121
5436
|
if (token.value === 'the') {
|
|
@@ -5329,18 +5644,11 @@ class Parser {
|
|
|
5329
5644
|
this.advance();
|
|
5330
5645
|
const args = [];
|
|
5331
5646
|
if (!this.check(')')) {
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
if (token.value === '(')
|
|
5336
|
-
depth++;
|
|
5337
|
-
if (token.value === ')')
|
|
5338
|
-
depth--;
|
|
5339
|
-
}
|
|
5340
|
-
}
|
|
5341
|
-
else {
|
|
5342
|
-
this.advance();
|
|
5647
|
+
do {
|
|
5648
|
+
args.push(this.parseExpression());
|
|
5649
|
+
} while (this.match(','));
|
|
5343
5650
|
}
|
|
5651
|
+
this.consume(')', "Expected ')' after constructor arguments");
|
|
5344
5652
|
return {
|
|
5345
5653
|
type: 'callExpression',
|
|
5346
5654
|
callee: {
|
|
@@ -5599,6 +5907,16 @@ class Parser {
|
|
|
5599
5907
|
parseEventHandler() {
|
|
5600
5908
|
debug.parse(`🔧 parseEventHandler: ENTRY - parsing event handler`);
|
|
5601
5909
|
const eventNames = [];
|
|
5910
|
+
let firstOnceAlias = false;
|
|
5911
|
+
if (this.check('first') && !this.checkIsCommand() && this.current + 1 < this.tokens.length) {
|
|
5912
|
+
const peek2 = this.tokens[this.current + 1];
|
|
5913
|
+
const peek2Value = peek2?.value?.toLowerCase();
|
|
5914
|
+
if (peek2Value && peek2Value !== 'of' && peek2Value !== 'in' && peek2Value !== 'from') {
|
|
5915
|
+
this.advance();
|
|
5916
|
+
firstOnceAlias = true;
|
|
5917
|
+
debug.parse(`🔧 parseEventHandler: Parsed 'first' as .once alias`);
|
|
5918
|
+
}
|
|
5919
|
+
}
|
|
5602
5920
|
const event = this.parseEventNameWithNamespace("Expected event name after 'on'");
|
|
5603
5921
|
eventNames.push(event);
|
|
5604
5922
|
debug.parse(`🔧 parseEventHandler: Parsed first event name: ${event}`);
|
|
@@ -5697,6 +6015,9 @@ class Parser {
|
|
|
5697
6015
|
this.addError(`Expected 'at' after '${modName}'`);
|
|
5698
6016
|
}
|
|
5699
6017
|
}
|
|
6018
|
+
if (firstOnceAlias) {
|
|
6019
|
+
modifiers.once = true;
|
|
6020
|
+
}
|
|
5700
6021
|
if (Object.keys(modifiers).length > 0) {
|
|
5701
6022
|
debug.parse(`🔧 parseEventHandler: Parsed modifiers:`, modifiers);
|
|
5702
6023
|
}
|
|
@@ -6195,7 +6516,8 @@ class Parser {
|
|
|
6195
6516
|
column: commandToken.column,
|
|
6196
6517
|
};
|
|
6197
6518
|
const result = this.parseCompoundCommand(identifierNode);
|
|
6198
|
-
return result ||
|
|
6519
|
+
return (result ||
|
|
6520
|
+
createPartialCommandNode(lowerName, this.getPosition()));
|
|
6199
6521
|
}
|
|
6200
6522
|
const args = [];
|
|
6201
6523
|
if ((commandName === 'increment' || commandName === 'decrement') && !this.isAtEnd()) {
|
|
@@ -6705,6 +7027,34 @@ class Parser {
|
|
|
6705
7027
|
column: startColumn,
|
|
6706
7028
|
};
|
|
6707
7029
|
}
|
|
7030
|
+
const ATTR_OPS = new Set(['=', '~=', '|=', '^=', '$=', '*=']);
|
|
7031
|
+
const lookhead = this.tokens[this.current];
|
|
7032
|
+
const lookhead2 = this.tokens[this.current + 1];
|
|
7033
|
+
const isAttrSelector = lookhead?.kind === 'identifier' &&
|
|
7034
|
+
(lookhead2?.value === ']' ||
|
|
7035
|
+
(lookhead2 &&
|
|
7036
|
+
ATTR_OPS.has(lookhead2.value) &&
|
|
7037
|
+
this.tokens[this.current + 2]?.kind === 'string'));
|
|
7038
|
+
if (isAttrSelector) {
|
|
7039
|
+
const attrName = this.advance().value;
|
|
7040
|
+
let css = '[' + attrName;
|
|
7041
|
+
if (this.check(']')) {
|
|
7042
|
+
this.advance();
|
|
7043
|
+
css += ']';
|
|
7044
|
+
}
|
|
7045
|
+
else {
|
|
7046
|
+
const op = this.advance().value;
|
|
7047
|
+
const stringTok = this.advance();
|
|
7048
|
+
const raw = stringTok.value;
|
|
7049
|
+
const unquoted = (raw.startsWith('"') && raw.endsWith('"')) || (raw.startsWith("'") && raw.endsWith("'"))
|
|
7050
|
+
? raw.slice(1, -1)
|
|
7051
|
+
: raw;
|
|
7052
|
+
css += op + '"' + unquoted + '"';
|
|
7053
|
+
this.consume(']', "Expected ']' after attribute selector");
|
|
7054
|
+
css += ']';
|
|
7055
|
+
}
|
|
7056
|
+
return this.createSelector(css);
|
|
7057
|
+
}
|
|
6708
7058
|
const elements = [];
|
|
6709
7059
|
if (!this.check(']')) {
|
|
6710
7060
|
do {
|
|
@@ -6754,41 +7104,15 @@ class Parser {
|
|
|
6754
7104
|
matchOperator: this.matchOperator.bind(this),
|
|
6755
7105
|
isAtEnd: this.isAtEnd.bind(this),
|
|
6756
7106
|
createIdentifier: this.createIdentifier.bind(this),
|
|
6757
|
-
createLiteral: this.createLiteral.bind(this),
|
|
6758
|
-
createSelector: this.createSelector.bind(this),
|
|
6759
|
-
createBinaryExpression: this.createBinaryExpression.bind(this),
|
|
6760
|
-
createUnaryExpression: this.createUnaryExpression.bind(this),
|
|
6761
|
-
createMemberExpression: this.createMemberExpression.bind(this),
|
|
6762
|
-
createPossessiveExpression: this.createPossessiveExpression.bind(this),
|
|
6763
|
-
createCallExpression: this.createCallExpression.bind(this),
|
|
6764
|
-
createErrorNode: this.createErrorNode.bind(this),
|
|
6765
|
-
createProgramNode: this.createProgramNode.bind(this),
|
|
6766
|
-
createCommandFromIdentifier: this.createCommandFromIdentifier.bind(this),
|
|
6767
7107
|
parseExpression: this.parseExpression.bind(this),
|
|
6768
7108
|
parsePrimary: this.parsePrimary.bind(this),
|
|
6769
|
-
parseCall: this.parseCall.bind(this),
|
|
6770
|
-
parseAssignment: this.parseAssignment.bind(this),
|
|
6771
|
-
parseLogicalOr: this.parseLogicalOr.bind(this),
|
|
6772
7109
|
parseLogicalAnd: this.parseLogicalAnd.bind(this),
|
|
6773
|
-
parseEquality: this.parseEquality.bind(this),
|
|
6774
|
-
parseComparison: this.parseComparison.bind(this),
|
|
6775
|
-
parseAddition: this.parseAddition.bind(this),
|
|
6776
|
-
parseMultiplication: this.parseMultiplication.bind(this),
|
|
6777
|
-
parseImplicitBinary: this.parseImplicitBinary.bind(this),
|
|
6778
|
-
parseConditional: this.parseConditional.bind(this),
|
|
6779
|
-
parseConditionalBranch: this.parseConditionalBranch.bind(this),
|
|
6780
|
-
parseEventHandler: this.parseEventHandler.bind(this),
|
|
6781
|
-
parseBehaviorDefinition: this.parseBehaviorDefinition.bind(this),
|
|
6782
|
-
parseNavigationFunction: this.parseNavigationFunction.bind(this),
|
|
6783
|
-
parseMyPropertyAccess: this.parseMyPropertyAccess.bind(this),
|
|
6784
|
-
parseDollarExpression: this.parseDollarExpression.bind(this),
|
|
6785
|
-
parseHyperscriptSelector: this.parseHyperscriptSelector.bind(this),
|
|
6786
|
-
parseAttributeOrArrayLiteral: this.parseAttributeOrArrayLiteral.bind(this),
|
|
6787
|
-
parseObjectLiteral: this.parseObjectLiteral.bind(this),
|
|
6788
7110
|
parseCSSObjectLiteral: this.parseCSSObjectLiteral.bind(this),
|
|
6789
7111
|
parseCommand: this.parseCommand.bind(this),
|
|
6790
7112
|
parseCommandSequence: this.parseCommandSequence.bind(this),
|
|
6791
7113
|
parseCommandListUntilEnd: this.parseCommandListUntilEnd.bind(this),
|
|
7114
|
+
parseCommandListUntilEndOrElse: this.parseCommandListUntilEndOrElse.bind(this),
|
|
7115
|
+
parseRepeatBody: this.parseRepeatBody.bind(this),
|
|
6792
7116
|
getPosition: this.getPosition.bind(this),
|
|
6793
7117
|
addError: this.addError.bind(this),
|
|
6794
7118
|
addWarning: this.addWarning.bind(this),
|
|
@@ -6831,6 +7155,7 @@ Parser.POSTFIX_UNARY_OPERATORS = new Set([
|
|
|
6831
7155
|
'does not exist',
|
|
6832
7156
|
'is empty',
|
|
6833
7157
|
'is not empty',
|
|
7158
|
+
'ignoring case',
|
|
6834
7159
|
]);
|
|
6835
7160
|
Parser.PRATT_TABLE = PARSER_TABLE;
|
|
6836
7161
|
Parser.PSEUDO_COMMAND_PREPOSITIONS = ['from', 'on', 'with', 'into', 'at', 'to'];
|