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