@hyperfixi/core 3.1.1 → 3.3.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 +28 -36
- package/dist/ast/command-slots.d.ts +1 -1
- package/dist/ast/nodes.d.ts +2 -0
- package/dist/ast-utils/index.cjs +193 -48
- package/dist/ast-utils/index.mjs +193 -48
- package/dist/ast-utils/interchange/types.d.ts +19 -1
- package/dist/behaviors/index.cjs +19 -0
- package/dist/behaviors/index.mjs +19 -0
- package/dist/bundle-generator/index.cjs +36 -17
- package/dist/bundle-generator/index.mjs +36 -17
- package/dist/bundle-generator/parser-templates.d.ts +1 -1
- package/dist/chunks/bridge-CI9SOl8u.js +2 -0
- package/dist/chunks/browser-modular-7CpI5VF2.js +2 -0
- package/dist/chunks/index-BwXDEK0m.js +2 -0
- package/dist/chunks/index-DQi2o8Lk.js +2 -0
- package/dist/chunks/index-uCOQ8Qn0.js +2 -0
- package/dist/commands/animation/transition.d.ts +12 -6
- package/dist/commands/helpers/loop-executor.d.ts +2 -2
- package/dist/commands/helpers/numeric-target-parser.d.ts +1 -1
- package/dist/commands/helpers/variable-access.d.ts +5 -2
- package/dist/commands/helpers/write-target.d.ts +5 -0
- package/dist/commands/index.cjs +938 -425
- package/dist/commands/index.mjs +938 -425
- package/dist/commands/templates/render.d.ts +4 -4
- package/dist/commands/utility/beep.d.ts +0 -3
- package/dist/expressions/index.cjs +7 -13
- package/dist/expressions/index.mjs +7 -13
- package/dist/htmx/i18n-hooks.d.ts +4 -1
- package/dist/hyperfixi-browser-classic-i18n.js +1 -1
- package/dist/hyperfixi-browser.js +1 -1
- package/dist/hyperfixi-classic-i18n.js +1 -1
- package/dist/hyperfixi-hx-v4.js +1 -1
- package/dist/hyperfixi-hx.js +1 -1
- package/dist/hyperfixi-hybrid-complete.js +1 -1
- package/dist/hyperfixi-hybrid-hx.js +1 -1
- package/dist/hyperfixi-multilingual.js +1 -1
- package/dist/hyperfixi.js +1 -1
- package/dist/hyperfixi.mjs +1 -1
- package/dist/index.cjs +1149 -485
- package/dist/index.min.js +1 -1
- package/dist/index.mjs +1149 -485
- package/dist/lib/swap-executor.d.ts +1 -1
- package/dist/lokascript-browser-classic-i18n.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/metadata.cjs +9 -9
- package/dist/metadata.d.ts +1 -1
- package/dist/metadata.mjs +9 -9
- package/dist/parser/command-grammar.d.ts +1 -0
- package/dist/parser/full-parser.cjs +396 -153
- package/dist/parser/full-parser.mjs +396 -153
- package/dist/parser/helpers/parsing-helpers.d.ts +3 -0
- package/dist/parser/hybrid/index.cjs +3 -0
- package/dist/parser/hybrid/index.mjs +3 -0
- package/dist/parser/hybrid/parser-core.cjs +3 -0
- package/dist/parser/hybrid/parser-core.mjs +3 -0
- package/dist/parser/hybrid-parser.cjs +3 -0
- package/dist/parser/hybrid-parser.mjs +3 -0
- package/dist/parser/parser-types.d.ts +2 -0
- package/dist/parser/parser.d.ts +9 -0
- package/dist/parser/pratt-parser.d.ts +2 -0
- package/dist/registry/index.cjs +535 -210
- package/dist/registry/index.mjs +535 -210
- package/dist/utils/beep.d.ts +8 -0
- package/dist/version.d.ts +1 -1
- package/package.json +21 -18
- package/vocab/htmx/ar.js +22 -20
- package/vocab/htmx/bn.js +21 -5
- package/vocab/htmx/de.js +33 -10
- package/vocab/htmx/en.js +4 -1
- package/vocab/htmx/es.js +28 -9
- package/vocab/htmx/fr.js +28 -8
- package/vocab/htmx/he.js +22 -1
- package/vocab/htmx/hi.js +28 -7
- package/vocab/htmx/id.js +29 -10
- package/vocab/htmx/it.js +26 -5
- package/vocab/htmx/ja.js +36 -11
- package/vocab/htmx/ko.js +27 -6
- package/vocab/htmx/ms.js +24 -1
- package/vocab/htmx/pl.js +27 -7
- package/vocab/htmx/pt.js +30 -9
- package/vocab/htmx/qu.js +32 -9
- package/vocab/htmx/ru.js +28 -6
- package/vocab/htmx/sw.js +28 -8
- package/vocab/htmx/th.js +19 -1
- package/vocab/htmx/tl.js +24 -1
- package/vocab/htmx/tr.js +34 -8
- package/vocab/htmx/uk.js +28 -6
- package/vocab/htmx/vi.js +27 -19
- package/vocab/htmx/zh.js +28 -9
- package/dist/chunks/bridge-D7qrHhV0.js +0 -2
- package/dist/chunks/browser-modular-C4LFxfoS.js +0 -2
- package/dist/chunks/index-C7lqD4mo.js +0 -2
- package/dist/chunks/index-CAPhqA6k.js +0 -2
- package/dist/chunks/index-CLdRyZaP.js +0 -2
|
@@ -1693,6 +1693,39 @@ function makeTypeCheckHandler(negated) {
|
|
|
1693
1693
|
};
|
|
1694
1694
|
};
|
|
1695
1695
|
}
|
|
1696
|
+
const OF_OPERAND_BP = 70;
|
|
1697
|
+
function parseOfOperand(ctx, minBp = OF_OPERAND_BP) {
|
|
1698
|
+
let right = ctx.parseExpr(minBp);
|
|
1699
|
+
if (ctx.peek()?.value === 'in') {
|
|
1700
|
+
const inToken = ctx.advance();
|
|
1701
|
+
const scope = ctx.parseExpr(minBp);
|
|
1702
|
+
right = {
|
|
1703
|
+
type: 'binaryExpression',
|
|
1704
|
+
operator: 'in',
|
|
1705
|
+
left: right,
|
|
1706
|
+
right: scope,
|
|
1707
|
+
start: right.start,
|
|
1708
|
+
end: scope.end ?? inToken.end,
|
|
1709
|
+
line: right.line ?? inToken.line,
|
|
1710
|
+
column: right.column ?? inToken.column,
|
|
1711
|
+
};
|
|
1712
|
+
}
|
|
1713
|
+
return right;
|
|
1714
|
+
}
|
|
1715
|
+
const THE_OF_TARGET_BP = OF_OPERAND_BP + 1;
|
|
1716
|
+
function ofHandler(left, token, ctx) {
|
|
1717
|
+
const right = parseOfOperand(ctx);
|
|
1718
|
+
return {
|
|
1719
|
+
type: 'binaryExpression',
|
|
1720
|
+
operator: token.value,
|
|
1721
|
+
left,
|
|
1722
|
+
right,
|
|
1723
|
+
start: left.start,
|
|
1724
|
+
end: right.end ?? token.end,
|
|
1725
|
+
line: left.line ?? token.line,
|
|
1726
|
+
column: left.column ?? token.column,
|
|
1727
|
+
};
|
|
1728
|
+
}
|
|
1696
1729
|
const PARSER_COMPARISON_FRAGMENT = new Map([
|
|
1697
1730
|
['===', leftAssoc(30)],
|
|
1698
1731
|
['!==', leftAssoc(30)],
|
|
@@ -1737,7 +1770,7 @@ const PARSER_COMPARISON_FRAGMENT = new Map([
|
|
|
1737
1770
|
['is greater than or equal to', leftAssoc(30)],
|
|
1738
1771
|
['is less than or equal to', leftAssoc(30)],
|
|
1739
1772
|
['in', leftAssoc(30)],
|
|
1740
|
-
['of',
|
|
1773
|
+
['of', { infix: { bp: [90, OF_OPERAND_BP], handler: ofHandler } }],
|
|
1741
1774
|
['really', leftAssoc(30)],
|
|
1742
1775
|
[
|
|
1743
1776
|
'exists',
|
|
@@ -2234,6 +2267,48 @@ function parseMaybeNamedArgument(ctx) {
|
|
|
2234
2267
|
const value = ctx.parseExpression();
|
|
2235
2268
|
return name !== undefined ? { name, value } : { value };
|
|
2236
2269
|
}
|
|
2270
|
+
function isNakedNamedArgStart(ctx) {
|
|
2271
|
+
if (ctx.check('{'))
|
|
2272
|
+
return false;
|
|
2273
|
+
if (!ctx.checkIdentifierLike())
|
|
2274
|
+
return false;
|
|
2275
|
+
const next = ctx.peekAt(1);
|
|
2276
|
+
return next !== null && next.value === ':';
|
|
2277
|
+
}
|
|
2278
|
+
function parseNakedNamedArgs(ctx, parseValue, command) {
|
|
2279
|
+
const properties = [];
|
|
2280
|
+
const startPos = ctx.getPosition();
|
|
2281
|
+
do {
|
|
2282
|
+
if (!ctx.checkIdentifierLike())
|
|
2283
|
+
break;
|
|
2284
|
+
const keyToken = ctx.advance();
|
|
2285
|
+
const key = {
|
|
2286
|
+
type: 'identifier',
|
|
2287
|
+
name: keyToken.value,
|
|
2288
|
+
start: keyToken.start,
|
|
2289
|
+
end: keyToken.end,
|
|
2290
|
+
line: keyToken.line,
|
|
2291
|
+
column: keyToken.column,
|
|
2292
|
+
};
|
|
2293
|
+
ctx.consume(':', `Expected ':' after property name in ${command} named arguments`);
|
|
2294
|
+
const value = parseValue();
|
|
2295
|
+
if (value) {
|
|
2296
|
+
properties.push({ key, value });
|
|
2297
|
+
}
|
|
2298
|
+
} while (ctx.match(',') && !ctx.isAtEnd());
|
|
2299
|
+
const endPos = ctx.getPosition();
|
|
2300
|
+
return {
|
|
2301
|
+
type: 'objectLiteral',
|
|
2302
|
+
properties,
|
|
2303
|
+
start: startPos.start,
|
|
2304
|
+
end: endPos.end,
|
|
2305
|
+
line: startPos.line,
|
|
2306
|
+
column: startPos.column,
|
|
2307
|
+
};
|
|
2308
|
+
}
|
|
2309
|
+
function isGluedBeepBang(beep, bang) {
|
|
2310
|
+
return beep?.value === 'beep' && bang?.value === '!' && bang.start === beep.end;
|
|
2311
|
+
}
|
|
2237
2312
|
|
|
2238
2313
|
class CommandNodeBuilder {
|
|
2239
2314
|
constructor(name) {
|
|
@@ -2435,6 +2510,8 @@ function parseTriggerCommand(ctx, identifierNode) {
|
|
|
2435
2510
|
const modifiers = {};
|
|
2436
2511
|
while (!isCommandBoundary(ctx)) {
|
|
2437
2512
|
if (ctx.check(KEYWORDS.ON) || ctx.check(KEYWORDS.TO)) {
|
|
2513
|
+
if (modifiers['on'])
|
|
2514
|
+
break;
|
|
2438
2515
|
ctx.advance();
|
|
2439
2516
|
const target = parseOneArgument(ctx, [KEYWORDS.WITH]);
|
|
2440
2517
|
if (target)
|
|
@@ -2575,6 +2652,9 @@ function parseRepeatCommand(ctx, commandToken) {
|
|
|
2575
2652
|
ctx.advance();
|
|
2576
2653
|
loopType = KEYWORDS.FOREVER;
|
|
2577
2654
|
}
|
|
2655
|
+
else if (ctx.checkIsCommand() || ctx.isCommand(ctx.peek().value)) {
|
|
2656
|
+
loopType = KEYWORDS.FOREVER;
|
|
2657
|
+
}
|
|
2578
2658
|
else {
|
|
2579
2659
|
times = ctx.parseExpression();
|
|
2580
2660
|
if (ctx.check(KEYWORDS.TIMES)) {
|
|
@@ -2617,10 +2697,12 @@ function parseRepeatCommand(ctx, commandToken) {
|
|
|
2617
2697
|
loopType = result.terminator;
|
|
2618
2698
|
ctx.advance();
|
|
2619
2699
|
condition = ctx.parseExpression();
|
|
2620
|
-
if (
|
|
2700
|
+
if (ctx.check('end')) {
|
|
2701
|
+
ctx.advance();
|
|
2702
|
+
}
|
|
2703
|
+
else if (!ctx.atEndOfInput()) {
|
|
2621
2704
|
throw new Error('Expected "end" to close repeat block');
|
|
2622
2705
|
}
|
|
2623
|
-
ctx.advance();
|
|
2624
2706
|
}
|
|
2625
2707
|
}
|
|
2626
2708
|
else {
|
|
@@ -2866,7 +2948,7 @@ function parseIfCommand(ctx, commandToken) {
|
|
|
2866
2948
|
}));
|
|
2867
2949
|
}
|
|
2868
2950
|
}
|
|
2869
|
-
if (!consumedElseIf && !ctx.
|
|
2951
|
+
if (!consumedElseIf && !ctx.atEndOfInput()) {
|
|
2870
2952
|
ctx.consume(KEYWORDS.END, "Expected 'end' after if block");
|
|
2871
2953
|
}
|
|
2872
2954
|
}
|
|
@@ -2891,7 +2973,6 @@ function parseIfCommand(ctx, commandToken) {
|
|
|
2891
2973
|
.build();
|
|
2892
2974
|
}
|
|
2893
2975
|
function parseForCommand(ctx, commandToken) {
|
|
2894
|
-
const args = [];
|
|
2895
2976
|
let variable = null;
|
|
2896
2977
|
let collection = null;
|
|
2897
2978
|
if (ctx.check(KEYWORDS.EACH)) {
|
|
@@ -2934,31 +3015,22 @@ function parseForCommand(ctx, commandToken) {
|
|
|
2934
3015
|
}
|
|
2935
3016
|
}
|
|
2936
3017
|
const commands = ctx.parseCommandListUntilEnd('for');
|
|
2937
|
-
|
|
2938
|
-
type: 'identifier',
|
|
2939
|
-
name: 'for',
|
|
2940
|
-
start: commandToken.start,
|
|
2941
|
-
end: commandToken.end,
|
|
2942
|
-
line: commandToken.line,
|
|
2943
|
-
column: commandToken.column,
|
|
2944
|
-
});
|
|
2945
|
-
const forPos = {
|
|
3018
|
+
const pos = {
|
|
2946
3019
|
start: commandToken.start,
|
|
2947
3020
|
end: commandToken.end,
|
|
2948
3021
|
line: commandToken.line,
|
|
2949
3022
|
column: commandToken.column,
|
|
2950
3023
|
};
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
}
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
.withArgs(...args)
|
|
3024
|
+
const modifiers = {
|
|
3025
|
+
loopType: toLegacyExpression(createStringLiteral(KEYWORDS.FOR, pos)),
|
|
3026
|
+
for: toLegacyExpression(createStringLiteral(variable, pos)),
|
|
3027
|
+
in: collection,
|
|
3028
|
+
};
|
|
3029
|
+
if (indexVariable)
|
|
3030
|
+
modifiers['index'] = toLegacyExpression(createStringLiteral(indexVariable, pos));
|
|
3031
|
+
return CommandNodeBuilder.from({ ...commandToken, value: 'repeat' })
|
|
3032
|
+
.withArgs(createBlock(commands, { ...pos, end: pos.end || 0 }))
|
|
3033
|
+
.withModifiers(modifiers)
|
|
2962
3034
|
.endingAt(ctx.getPosition())
|
|
2963
3035
|
.build();
|
|
2964
3036
|
}
|
|
@@ -3014,86 +3086,159 @@ function parseMeasureCommand(ctx, identifierNode) {
|
|
|
3014
3086
|
}
|
|
3015
3087
|
return builder.build();
|
|
3016
3088
|
}
|
|
3017
|
-
const
|
|
3089
|
+
const CONTEXT_REFS = new Set(['me', 'it', 'you', 'result']);
|
|
3018
3090
|
function parseTransitionCommand(ctx, commandToken) {
|
|
3019
|
-
const
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
const
|
|
3024
|
-
const
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
if (
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
target = parsed.object;
|
|
3035
|
-
property = {
|
|
3036
|
-
type: 'string',
|
|
3037
|
-
value: parsed.property.name,
|
|
3038
|
-
start: leadToken.start || 0,
|
|
3039
|
-
end: ctx.getPosition().end,
|
|
3040
|
-
line: leadToken.line,
|
|
3041
|
-
column: leadToken.column,
|
|
3042
|
-
};
|
|
3091
|
+
const pairs = [];
|
|
3092
|
+
do {
|
|
3093
|
+
pairs.push(parseTransitionPair(ctx));
|
|
3094
|
+
} while (startsAnotherPair(ctx));
|
|
3095
|
+
const [first, ...rest] = pairs;
|
|
3096
|
+
const args = first.owner ? [first.owner, first.property] : [first.property];
|
|
3097
|
+
const modifiers = { to: first.to };
|
|
3098
|
+
if (first.from)
|
|
3099
|
+
modifiers['from'] = first.from;
|
|
3100
|
+
if (rest.length > 0)
|
|
3101
|
+
modifiers['pairs'] = pairsNode(rest);
|
|
3102
|
+
for (let i = 0; i < 3 && !ctx.isAtEnd(); i++) {
|
|
3103
|
+
if (ctx.check('over') && !modifiers['over']) {
|
|
3104
|
+
ctx.advance();
|
|
3105
|
+
modifiers['over'] = ctx.parseExpression();
|
|
3043
3106
|
}
|
|
3044
|
-
else {
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
ctx.advance();
|
|
3107
|
+
else if (ctx.check('using') && !modifiers['using']) {
|
|
3108
|
+
ctx.advance();
|
|
3109
|
+
modifiers['using'] = ctx.parseExpression();
|
|
3048
3110
|
}
|
|
3049
|
-
|
|
3050
|
-
const firstToken = ctx.peek();
|
|
3051
|
-
if (!property && (isIdentifierLike(firstToken) || firstToken.value === '*')) {
|
|
3052
|
-
let propertyValue = '';
|
|
3053
|
-
if (ctx.check('*')) {
|
|
3054
|
-
propertyValue = '*';
|
|
3111
|
+
else if (ctx.check(KEYWORDS.WITH) && !modifiers['with']) {
|
|
3055
3112
|
ctx.advance();
|
|
3113
|
+
modifiers['with'] = ctx.parsePrimary();
|
|
3056
3114
|
}
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
propertyValue += hyphenatedName;
|
|
3060
|
-
property = {
|
|
3061
|
-
type: 'string',
|
|
3062
|
-
value: propertyValue,
|
|
3063
|
-
start: firstToken.start || 0,
|
|
3064
|
-
end: ctx.getPosition().end,
|
|
3065
|
-
line: firstToken.line,
|
|
3066
|
-
column: firstToken.column,
|
|
3067
|
-
};
|
|
3115
|
+
else {
|
|
3116
|
+
break;
|
|
3068
3117
|
}
|
|
3069
3118
|
}
|
|
3070
|
-
|
|
3071
|
-
|
|
3119
|
+
return CommandNodeBuilder.from(commandToken)
|
|
3120
|
+
.withArgs(...args)
|
|
3121
|
+
.withModifiers(modifiers)
|
|
3122
|
+
.endingAt(ctx.getPosition())
|
|
3123
|
+
.build();
|
|
3124
|
+
}
|
|
3125
|
+
function parseTransitionPair(ctx) {
|
|
3126
|
+
const { owner, property } = parseTransitionProperty(ctx);
|
|
3127
|
+
let from = null;
|
|
3128
|
+
if (ctx.check('from')) {
|
|
3129
|
+
ctx.advance();
|
|
3130
|
+
from = ctx.parseExpression();
|
|
3072
3131
|
}
|
|
3073
|
-
if (target)
|
|
3074
|
-
args.push(target);
|
|
3075
|
-
args.push(property);
|
|
3076
3132
|
if (!ctx.check(KEYWORDS.TO)) {
|
|
3077
3133
|
throw new Error('Expected "to" keyword after property in transition command');
|
|
3078
3134
|
}
|
|
3079
3135
|
ctx.advance();
|
|
3080
|
-
const
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3136
|
+
const to = ctx.parseExpression();
|
|
3137
|
+
return { owner, property, from, to };
|
|
3138
|
+
}
|
|
3139
|
+
function parseTransitionProperty(ctx) {
|
|
3140
|
+
const lead = ctx.peek();
|
|
3141
|
+
if (!lead || isKeyword(lead, [KEYWORDS.TO, 'from'])) {
|
|
3142
|
+
throw new Error('Transition command requires a CSS property');
|
|
3086
3143
|
}
|
|
3087
|
-
|
|
3144
|
+
const expr = ctx.parseExpression();
|
|
3145
|
+
const split = splitStyleExpression(expr);
|
|
3146
|
+
if (split)
|
|
3147
|
+
return { owner: split.owner, property: propertyNode(split.name, lead, ctx) };
|
|
3148
|
+
let name = '';
|
|
3149
|
+
if (ctx.check('*')) {
|
|
3150
|
+
name = '*';
|
|
3088
3151
|
ctx.advance();
|
|
3089
|
-
const timingFunction = ctx.parsePrimary();
|
|
3090
|
-
modifiers['with'] = timingFunction;
|
|
3091
3152
|
}
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3153
|
+
const hyphenated = parseHyphenatedName(ctx);
|
|
3154
|
+
if (!hyphenated)
|
|
3155
|
+
throw new Error('Transition command requires a CSS property');
|
|
3156
|
+
return { owner: expr, property: propertyNode(name + hyphenated, lead, ctx) };
|
|
3157
|
+
}
|
|
3158
|
+
function splitStyleExpression(node) {
|
|
3159
|
+
const n = node;
|
|
3160
|
+
switch (n.type) {
|
|
3161
|
+
case 'selector':
|
|
3162
|
+
return typeof n.value === 'string' && n.value.startsWith('*')
|
|
3163
|
+
? { owner: null, name: n.value }
|
|
3164
|
+
: null;
|
|
3165
|
+
case 'identifier':
|
|
3166
|
+
return typeof n.name === 'string' && !CONTEXT_REFS.has(n.name)
|
|
3167
|
+
? { owner: null, name: n.name }
|
|
3168
|
+
: null;
|
|
3169
|
+
case 'possessiveExpression':
|
|
3170
|
+
case 'memberExpression':
|
|
3171
|
+
return !n.computed && n.object && typeof n.property?.name === 'string'
|
|
3172
|
+
? { owner: n.object, name: n.property.name }
|
|
3173
|
+
: null;
|
|
3174
|
+
case 'binaryExpression': {
|
|
3175
|
+
if (n.operator !== 'of' || !n.left || !n.right)
|
|
3176
|
+
return null;
|
|
3177
|
+
const inner = splitStyleExpression(n.left);
|
|
3178
|
+
return inner && !inner.owner ? { owner: n.right, name: inner.name } : null;
|
|
3179
|
+
}
|
|
3180
|
+
case 'propertyOfExpression':
|
|
3181
|
+
return n.target && typeof n.property?.name === 'string'
|
|
3182
|
+
? { owner: n.target, name: n.property.name }
|
|
3183
|
+
: null;
|
|
3184
|
+
default:
|
|
3185
|
+
return null;
|
|
3186
|
+
}
|
|
3187
|
+
}
|
|
3188
|
+
function propertyNode(name, lead, ctx) {
|
|
3189
|
+
return {
|
|
3190
|
+
type: 'string',
|
|
3191
|
+
value: name,
|
|
3192
|
+
start: lead.start || 0,
|
|
3193
|
+
end: ctx.getPosition().end,
|
|
3194
|
+
line: lead.line,
|
|
3195
|
+
column: lead.column,
|
|
3196
|
+
};
|
|
3197
|
+
}
|
|
3198
|
+
function startsAnotherPair(ctx) {
|
|
3199
|
+
if (ctx.isAtEnd() || isComment(ctx.peek()))
|
|
3200
|
+
return false;
|
|
3201
|
+
if (isKeyword(ctx.peek(), ['over', 'using', KEYWORDS.WITH]))
|
|
3202
|
+
return false;
|
|
3203
|
+
return !isCommandBoundary(ctx, ['catch', 'finally', 'on']);
|
|
3204
|
+
}
|
|
3205
|
+
function pairsNode(pairs) {
|
|
3206
|
+
const field = (name, value) => ({
|
|
3207
|
+
key: {
|
|
3208
|
+
type: 'identifier',
|
|
3209
|
+
name,
|
|
3210
|
+
start: value.start,
|
|
3211
|
+
end: value.end,
|
|
3212
|
+
line: value.line,
|
|
3213
|
+
column: value.column,
|
|
3214
|
+
},
|
|
3215
|
+
value,
|
|
3216
|
+
});
|
|
3217
|
+
const elements = pairs.map(pair => {
|
|
3218
|
+
const properties = [field('property', pair.property), field('to', pair.to)];
|
|
3219
|
+
if (pair.owner)
|
|
3220
|
+
properties.push(field('owner', pair.owner));
|
|
3221
|
+
if (pair.from)
|
|
3222
|
+
properties.push(field('from', pair.from));
|
|
3223
|
+
return {
|
|
3224
|
+
type: 'objectLiteral',
|
|
3225
|
+
properties,
|
|
3226
|
+
start: (pair.owner ?? pair.property).start,
|
|
3227
|
+
end: pair.to.end,
|
|
3228
|
+
line: (pair.owner ?? pair.property).line,
|
|
3229
|
+
column: (pair.owner ?? pair.property).column,
|
|
3230
|
+
};
|
|
3231
|
+
});
|
|
3232
|
+
const firstEl = elements[0];
|
|
3233
|
+
const lastEl = elements[elements.length - 1];
|
|
3234
|
+
return {
|
|
3235
|
+
type: 'arrayLiteral',
|
|
3236
|
+
elements,
|
|
3237
|
+
start: firstEl.start,
|
|
3238
|
+
end: lastEl.end,
|
|
3239
|
+
line: firstEl.line,
|
|
3240
|
+
column: firstEl.column,
|
|
3241
|
+
};
|
|
3097
3242
|
}
|
|
3098
3243
|
function parseStartCommand(ctx, identifierNode) {
|
|
3099
3244
|
if (!ctx.match('view')) {
|
|
@@ -3213,6 +3358,15 @@ function parseToggleCommand(ctx, identifierNode) {
|
|
|
3213
3358
|
args.push(classArg.expression);
|
|
3214
3359
|
modifiers['as'] = literalModifier(mode);
|
|
3215
3360
|
}
|
|
3361
|
+
else if (classArg?.type === 'binaryExpression' &&
|
|
3362
|
+
classArg.operator === 'of' &&
|
|
3363
|
+
classArg.left?.type === 'selector' &&
|
|
3364
|
+
typeof classArg.left.value === 'string' &&
|
|
3365
|
+
classArg.left.value.startsWith('*') &&
|
|
3366
|
+
classArg.right) {
|
|
3367
|
+
args.push(classArg.left);
|
|
3368
|
+
modifiers['on'] = classArg.right;
|
|
3369
|
+
}
|
|
3216
3370
|
else if (classArg) {
|
|
3217
3371
|
args.push(classArg);
|
|
3218
3372
|
}
|
|
@@ -3390,11 +3544,17 @@ function parseSwapCommand(ctx, identifierNode) {
|
|
|
3390
3544
|
const targetExpr = isDelete ? ctx.parseExpression() : parseOneArgument(ctx, [KEYWORDS.WITH]);
|
|
3391
3545
|
if (targetExpr)
|
|
3392
3546
|
args.push(targetExpr);
|
|
3393
|
-
|
|
3547
|
+
const isMorph = identifierNode.name === 'morph';
|
|
3548
|
+
if (!isDelete &&
|
|
3549
|
+
(consumeOptionalKeyword(ctx, KEYWORDS.WITH) ||
|
|
3550
|
+
(isMorph && consumeOptionalKeyword(ctx, KEYWORDS.TO)))) {
|
|
3394
3551
|
const contentExpr = parseOneArgument(ctx, ['using']);
|
|
3395
3552
|
if (contentExpr)
|
|
3396
3553
|
modifiers['with'] = contentExpr;
|
|
3397
3554
|
}
|
|
3555
|
+
if (isMorph && modifiers['with'] === undefined) {
|
|
3556
|
+
ctx.addError("Expected 'to' and the content to morph into");
|
|
3557
|
+
}
|
|
3398
3558
|
return CommandNodeBuilder.fromIdentifier(identifierNode)
|
|
3399
3559
|
.withArgs(...args)
|
|
3400
3560
|
.withModifiers({ ...modifiers, ...parseViewTransitionTail(ctx) })
|
|
@@ -4155,6 +4315,8 @@ function atArgumentBoundary(ctx, grammar) {
|
|
|
4155
4315
|
const next = ctx.peek();
|
|
4156
4316
|
if (ctx.checkIsCommand() && ctx.peekAt(1)?.value === '(')
|
|
4157
4317
|
return false;
|
|
4318
|
+
if (isGluedBeepBang(next, ctx.peekAt(1)))
|
|
4319
|
+
return false;
|
|
4158
4320
|
if (isCommandBoundary(ctx, ['catch', 'finally', 'on']))
|
|
4159
4321
|
return true;
|
|
4160
4322
|
return isKeyword(next, [...grammar.markers]);
|
|
@@ -4186,10 +4348,13 @@ function parseDeclaredCommand(ctx, commandToken, name, grammar) {
|
|
|
4186
4348
|
}
|
|
4187
4349
|
}
|
|
4188
4350
|
const commaList = new Set(grammar.commaList ?? []);
|
|
4351
|
+
const namedArgs = new Set(grammar.namedArgs ?? []);
|
|
4189
4352
|
while (!ctx.isAtEnd() && isKeyword(ctx.peek(), [...grammar.markers])) {
|
|
4190
4353
|
const keyword = ctx.advance().value;
|
|
4191
4354
|
const key = ctx.resolveKeyword(keyword).toLowerCase();
|
|
4192
|
-
let value =
|
|
4355
|
+
let value = namedArgs.has(key) && isNakedNamedArgStart(ctx)
|
|
4356
|
+
? parseNakedNamedArgs(ctx, () => ctx.parseExpression(), name)
|
|
4357
|
+
: ctx.parseExpression();
|
|
4193
4358
|
if (value && commaList.has(key) && ctx.check(',')) {
|
|
4194
4359
|
const elements = [value];
|
|
4195
4360
|
while (ctx.match(',')) {
|
|
@@ -4354,8 +4519,8 @@ function parseFetchCommand(ctx, commandToken) {
|
|
|
4354
4519
|
}
|
|
4355
4520
|
if (ctx.check('with') && !modifiers['with']) {
|
|
4356
4521
|
ctx.advance();
|
|
4357
|
-
if (
|
|
4358
|
-
modifiers['with'] =
|
|
4522
|
+
if (isNakedNamedArgStart(ctx)) {
|
|
4523
|
+
modifiers['with'] = parseNakedNamedArgs(ctx, () => ctx.parsePrimary(), 'fetch');
|
|
4359
4524
|
}
|
|
4360
4525
|
else {
|
|
4361
4526
|
modifiers['with'] = ctx.parsePrimary();
|
|
@@ -4390,45 +4555,6 @@ function parseFetchCommand(ctx, commandToken) {
|
|
|
4390
4555
|
}
|
|
4391
4556
|
return builder.build();
|
|
4392
4557
|
}
|
|
4393
|
-
function isFetchNakedNamedArgStart(ctx) {
|
|
4394
|
-
if (ctx.check('{'))
|
|
4395
|
-
return false;
|
|
4396
|
-
if (!ctx.checkIdentifierLike())
|
|
4397
|
-
return false;
|
|
4398
|
-
const next = ctx.peekAt(1);
|
|
4399
|
-
return next !== null && next.value === ':';
|
|
4400
|
-
}
|
|
4401
|
-
function parseFetchNakedNamedArgs(ctx) {
|
|
4402
|
-
const properties = [];
|
|
4403
|
-
const startPos = ctx.getPosition();
|
|
4404
|
-
do {
|
|
4405
|
-
if (!ctx.checkIdentifierLike())
|
|
4406
|
-
break;
|
|
4407
|
-
const keyToken = ctx.advance();
|
|
4408
|
-
const key = {
|
|
4409
|
-
type: 'identifier',
|
|
4410
|
-
name: keyToken.value,
|
|
4411
|
-
start: keyToken.start,
|
|
4412
|
-
end: keyToken.end,
|
|
4413
|
-
line: keyToken.line,
|
|
4414
|
-
column: keyToken.column,
|
|
4415
|
-
};
|
|
4416
|
-
ctx.consume(':', "Expected ':' after property name in fetch named arguments");
|
|
4417
|
-
const value = ctx.parsePrimary();
|
|
4418
|
-
if (value) {
|
|
4419
|
-
properties.push({ key, value });
|
|
4420
|
-
}
|
|
4421
|
-
} while (ctx.match(',') && !ctx.isAtEnd());
|
|
4422
|
-
const endPos = ctx.getPosition();
|
|
4423
|
-
return {
|
|
4424
|
-
type: 'objectLiteral',
|
|
4425
|
-
properties,
|
|
4426
|
-
start: startPos.start,
|
|
4427
|
-
end: endPos.end,
|
|
4428
|
-
line: startPos.line,
|
|
4429
|
-
column: startPos.column,
|
|
4430
|
-
};
|
|
4431
|
-
}
|
|
4432
4558
|
function findJsEndBoundary(ctx, startPos) {
|
|
4433
4559
|
const input = ctx.getInputSlice(startPos);
|
|
4434
4560
|
if (!input) {
|
|
@@ -4548,6 +4674,7 @@ function parseTellCommand(ctx, identifierNode) {
|
|
|
4548
4674
|
const commands = [];
|
|
4549
4675
|
while (!ctx.isAtEnd()) {
|
|
4550
4676
|
if (ctx.checkIsCommand()) {
|
|
4677
|
+
const word = ctx.peek().value;
|
|
4551
4678
|
try {
|
|
4552
4679
|
ctx.advance();
|
|
4553
4680
|
const cmd = ctx.parseCommand();
|
|
@@ -4558,7 +4685,8 @@ function parseTellCommand(ctx, identifierNode) {
|
|
|
4558
4685
|
break;
|
|
4559
4686
|
}
|
|
4560
4687
|
}
|
|
4561
|
-
catch {
|
|
4688
|
+
catch (error) {
|
|
4689
|
+
ctx.addError(`'${word}' in a tell body failed to parse: ${error instanceof Error ? error.message : String(error)}`);
|
|
4562
4690
|
break;
|
|
4563
4691
|
}
|
|
4564
4692
|
if (ctx.match(KEYWORDS.AND) || ctx.match(KEYWORDS.THEN)) {
|
|
@@ -4707,7 +4835,6 @@ const ONE_EXPR = (syntax) => ({
|
|
|
4707
4835
|
markers: [],
|
|
4708
4836
|
syntax,
|
|
4709
4837
|
});
|
|
4710
|
-
const BEEP = ONE_EXPR('beep! [<expression>, \u2026]');
|
|
4711
4838
|
const COMMAND_GRAMMAR = {
|
|
4712
4839
|
append: { positional: 'expression', markers: ['to'], syntax: 'append <value> [to <target>]' },
|
|
4713
4840
|
prepend: { positional: 'expression', markers: ['to'], syntax: 'prepend <value> [to <target>]' },
|
|
@@ -4718,8 +4845,7 @@ const COMMAND_GRAMMAR = {
|
|
|
4718
4845
|
syntax: 'make (a|an) <type> [from <args>] [called <name>]',
|
|
4719
4846
|
},
|
|
4720
4847
|
throw: { positional: 'primary', markers: [], syntax: 'throw <error>' },
|
|
4721
|
-
'beep!'
|
|
4722
|
-
beep: BEEP,
|
|
4848
|
+
beep: ONE_EXPR('beep! [<expression>, \u2026]'),
|
|
4723
4849
|
blur: ONE_EXPR('blur [<target>]'),
|
|
4724
4850
|
break: NONE,
|
|
4725
4851
|
breakpoint: NONE,
|
|
@@ -4743,7 +4869,8 @@ const COMMAND_GRAMMAR = {
|
|
|
4743
4869
|
render: {
|
|
4744
4870
|
positional: 'expression',
|
|
4745
4871
|
markers: ['with'],
|
|
4746
|
-
|
|
4872
|
+
namedArgs: ['with'],
|
|
4873
|
+
syntax: 'render <template> [with <name>: <value>, \u2026]',
|
|
4747
4874
|
},
|
|
4748
4875
|
reset: ONE_EXPR('reset [<form>]'),
|
|
4749
4876
|
return: ONE_EXPR('return [<value>]'),
|
|
@@ -5098,7 +5225,10 @@ class Parser {
|
|
|
5098
5225
|
const firstToken = this.peek();
|
|
5099
5226
|
const prefixEntry = Parser.PRATT_TABLE.get(firstToken.value);
|
|
5100
5227
|
let left;
|
|
5101
|
-
if (
|
|
5228
|
+
if (isGluedBeepBang(firstToken, this.tokens[this.current + 1])) {
|
|
5229
|
+
left = this.parseBeepExpression();
|
|
5230
|
+
}
|
|
5231
|
+
else if (prefixEntry?.prefix) {
|
|
5102
5232
|
const token = this.advance();
|
|
5103
5233
|
if (this.isAtEnd()) {
|
|
5104
5234
|
this.addError(`Expected expression after '${token.value}' operator`);
|
|
@@ -5118,6 +5248,9 @@ class Parser {
|
|
|
5118
5248
|
continue;
|
|
5119
5249
|
}
|
|
5120
5250
|
}
|
|
5251
|
+
if (nextToken.value === '*' && this.isSpacedStyleRef(this.current)) {
|
|
5252
|
+
break;
|
|
5253
|
+
}
|
|
5121
5254
|
const infixEntry = Parser.PRATT_TABLE.get(nextToken.value);
|
|
5122
5255
|
if (!infixEntry?.infix) {
|
|
5123
5256
|
if (STOP_TOKENS.has(nextToken.value) || STOP_DELIMITERS.has(nextToken.value)) {
|
|
@@ -5179,6 +5312,35 @@ class Parser {
|
|
|
5179
5312
|
this.prattCache.set(cacheKey, { node: left, endPos: this.current });
|
|
5180
5313
|
return left;
|
|
5181
5314
|
}
|
|
5315
|
+
parseBeepExpression() {
|
|
5316
|
+
const beepToken = this.advance();
|
|
5317
|
+
this.advance();
|
|
5318
|
+
if (this.isAtEnd()) {
|
|
5319
|
+
this.addError("Expected expression after 'beep!'");
|
|
5320
|
+
return this.createErrorNode();
|
|
5321
|
+
}
|
|
5322
|
+
const operand = this.parseExpressionPratt(Parser.BEEP_OPERAND_BP);
|
|
5323
|
+
return {
|
|
5324
|
+
type: 'unaryExpression',
|
|
5325
|
+
operator: 'beep!',
|
|
5326
|
+
operand,
|
|
5327
|
+
argument: operand,
|
|
5328
|
+
prefix: true,
|
|
5329
|
+
start: beepToken.start,
|
|
5330
|
+
end: this.previous().end,
|
|
5331
|
+
line: beepToken.line,
|
|
5332
|
+
column: beepToken.column,
|
|
5333
|
+
};
|
|
5334
|
+
}
|
|
5335
|
+
isStyleRefStar(i) {
|
|
5336
|
+
const star = this.tokens[i];
|
|
5337
|
+
const name = this.tokens[i + 1];
|
|
5338
|
+
return star?.value === '*' && !!name && name.start === star.end && isIdentifierLike(name);
|
|
5339
|
+
}
|
|
5340
|
+
isSpacedStyleRef(i) {
|
|
5341
|
+
const prev = this.tokens[i - 1];
|
|
5342
|
+
return this.isStyleRefStar(i) && !!prev && prev.end < this.tokens[i].start;
|
|
5343
|
+
}
|
|
5182
5344
|
tryParseStringPostfix(left, nextToken) {
|
|
5183
5345
|
const v = nextToken.value;
|
|
5184
5346
|
let unit = null;
|
|
@@ -5381,6 +5543,10 @@ class Parser {
|
|
|
5381
5543
|
const isStop = () => this.check('end') || extraStops.some(s => this.check(s));
|
|
5382
5544
|
debug.parse('\ud83d\udd04 parseCommandListUntilTerminator: Starting (extraStops:', extraStops.join(','), ')');
|
|
5383
5545
|
while (!this.isAtEnd() && !isStop()) {
|
|
5546
|
+
if (this.checkComment()) {
|
|
5547
|
+
this.advance();
|
|
5548
|
+
continue;
|
|
5549
|
+
}
|
|
5384
5550
|
debug.parse('\ud83d\udccd Loop iteration, current token:', this.peek().value, 'kind:', this.peek().kind);
|
|
5385
5551
|
let parsedCommand = false;
|
|
5386
5552
|
const isCommandToken = this.checkIsCommand();
|
|
@@ -5464,7 +5630,7 @@ class Parser {
|
|
|
5464
5630
|
debug.parse('\u2705 Found "end", consuming it');
|
|
5465
5631
|
this.advance();
|
|
5466
5632
|
}
|
|
5467
|
-
else {
|
|
5633
|
+
else if (!this.atEndOfInput()) {
|
|
5468
5634
|
debug.parse('\u274c ERROR: Expected "end" but got:', this.peek().value, 'at position:', this.peek().start);
|
|
5469
5635
|
throw new Error(`Expected "end" to close ${construct} block`);
|
|
5470
5636
|
}
|
|
@@ -5477,7 +5643,7 @@ class Parser {
|
|
|
5477
5643
|
if (this.check('end')) {
|
|
5478
5644
|
this.advance();
|
|
5479
5645
|
}
|
|
5480
|
-
else {
|
|
5646
|
+
else if (!this.atEndOfInput()) {
|
|
5481
5647
|
throw new Error('Expected "end" to close repeat block');
|
|
5482
5648
|
}
|
|
5483
5649
|
}
|
|
@@ -5493,7 +5659,10 @@ class Parser {
|
|
|
5493
5659
|
this.advance();
|
|
5494
5660
|
}
|
|
5495
5661
|
else if (terminator === '') {
|
|
5496
|
-
|
|
5662
|
+
if (!this.atEndOfInput()) {
|
|
5663
|
+
throw new Error('Expected "end", "else", "until", or "while" to close repeat block');
|
|
5664
|
+
}
|
|
5665
|
+
return { commands, terminator: 'end' };
|
|
5497
5666
|
}
|
|
5498
5667
|
return { commands, terminator };
|
|
5499
5668
|
}
|
|
@@ -6077,7 +6246,9 @@ class Parser {
|
|
|
6077
6246
|
parseTopLevelInitBlock() {
|
|
6078
6247
|
const pos = this.getPosition();
|
|
6079
6248
|
const initCommands = this.parseCommandBlock(['end']);
|
|
6080
|
-
this.
|
|
6249
|
+
if (!this.match('end') && !this.atEndOfInput() && !this.checkFeatureStart()) {
|
|
6250
|
+
this.consume('end', "Expected 'end' after init block");
|
|
6251
|
+
}
|
|
6081
6252
|
return {
|
|
6082
6253
|
type: 'initBlock',
|
|
6083
6254
|
commands: initCommands,
|
|
@@ -6253,10 +6424,29 @@ class Parser {
|
|
|
6253
6424
|
...(sourceExpr.column !== undefined && { column: sourceExpr.column }),
|
|
6254
6425
|
};
|
|
6255
6426
|
}
|
|
6427
|
+
isArgumentListOnly(open) {
|
|
6428
|
+
let depth = 0;
|
|
6429
|
+
for (let i = open; i < this.tokens.length; i++) {
|
|
6430
|
+
const token = this.tokens[i];
|
|
6431
|
+
if (token.kind !== 'operator')
|
|
6432
|
+
continue;
|
|
6433
|
+
if (token.value === '(' || token.value === '[' || token.value === '{')
|
|
6434
|
+
depth++;
|
|
6435
|
+
else if (token.value === ')' || token.value === ']' || token.value === '}') {
|
|
6436
|
+
if (--depth === 0)
|
|
6437
|
+
return i === open + 1;
|
|
6438
|
+
}
|
|
6439
|
+
else if (token.value === ',' && depth === 1)
|
|
6440
|
+
return true;
|
|
6441
|
+
}
|
|
6442
|
+
return false;
|
|
6443
|
+
}
|
|
6256
6444
|
tryParsePseudoCommand(callExpr, sourceExpr) {
|
|
6257
6445
|
const methodName = callExpr.callee.name;
|
|
6258
6446
|
const nextToken = this.peek();
|
|
6259
|
-
const
|
|
6447
|
+
const nextIsArticle = nextToken.value.toLowerCase() === 'the';
|
|
6448
|
+
const hasPseudoCommandPattern = nextIsArticle ||
|
|
6449
|
+
Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(nextToken.value.toLowerCase()) ||
|
|
6260
6450
|
(isIdentifier(nextToken) && !this.isCommand(nextToken.value)) ||
|
|
6261
6451
|
isContextVar(nextToken);
|
|
6262
6452
|
if (!hasPseudoCommandPattern) {
|
|
@@ -6282,7 +6472,10 @@ class Parser {
|
|
|
6282
6472
|
});
|
|
6283
6473
|
}
|
|
6284
6474
|
let preposition;
|
|
6285
|
-
if (
|
|
6475
|
+
if (nextIsArticle) {
|
|
6476
|
+
this.advance();
|
|
6477
|
+
}
|
|
6478
|
+
else if (Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(this.peek().value.toLowerCase())) {
|
|
6286
6479
|
preposition = this.advance().value.toLowerCase();
|
|
6287
6480
|
}
|
|
6288
6481
|
let targetExpr;
|
|
@@ -6446,10 +6639,22 @@ class Parser {
|
|
|
6446
6639
|
this.consume(']', "Expected ']' after event condition");
|
|
6447
6640
|
}
|
|
6448
6641
|
let target;
|
|
6642
|
+
let targetExpression;
|
|
6449
6643
|
if (this.match('from')) {
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
|
|
6644
|
+
if (this.check('the'))
|
|
6645
|
+
this.advance();
|
|
6646
|
+
if (Parser.POSITIONAL_SOURCE_HEADS.has(this.peek().value.toLowerCase())) {
|
|
6647
|
+
const startIndex = this.current;
|
|
6648
|
+
targetExpression = this.parseExpression();
|
|
6649
|
+
target = this.tokens
|
|
6650
|
+
.slice(startIndex, this.current)
|
|
6651
|
+
.map(t => t.value)
|
|
6652
|
+
.join(' ');
|
|
6653
|
+
}
|
|
6654
|
+
else {
|
|
6655
|
+
target = this.advance().value;
|
|
6656
|
+
}
|
|
6657
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Parsed 'from' target: ${target}`);
|
|
6453
6658
|
this.parseTemporalKeywordModifiers(modifiers);
|
|
6454
6659
|
}
|
|
6455
6660
|
let attributeName;
|
|
@@ -6495,7 +6700,7 @@ class Parser {
|
|
|
6495
6700
|
const nextIsOpenParen = this.tokens[this.current + 1]?.value === '(';
|
|
6496
6701
|
const commandName = this.peek().value.toLowerCase();
|
|
6497
6702
|
const isSpecialBodyCommand = commandName === 'js' || commandName === 'tell';
|
|
6498
|
-
if (nextIsOpenParen && !isSpecialBodyCommand) {
|
|
6703
|
+
if (nextIsOpenParen && !isSpecialBodyCommand && this.isArgumentListOnly(this.current + 1)) {
|
|
6499
6704
|
let expr;
|
|
6500
6705
|
const savedError = this.error;
|
|
6501
6706
|
try {
|
|
@@ -6527,7 +6732,7 @@ class Parser {
|
|
|
6527
6732
|
}
|
|
6528
6733
|
}
|
|
6529
6734
|
}
|
|
6530
|
-
else if (this.checkIdentifier()) {
|
|
6735
|
+
else if (this.checkIdentifier() || this.checkEventNamedCall()) {
|
|
6531
6736
|
const token = this.peek();
|
|
6532
6737
|
if (this.isCommand(token.value)) {
|
|
6533
6738
|
const cmd = this.parseCommandWithErrorRecovery();
|
|
@@ -6636,6 +6841,7 @@ class Parser {
|
|
|
6636
6841
|
...(eventParams.length > 0 && { args: eventParams }),
|
|
6637
6842
|
...(condition && { condition: fromLegacyExpression(condition) }),
|
|
6638
6843
|
...(target && { target }),
|
|
6844
|
+
...(targetExpression && { targetExpression: fromLegacyExpression(targetExpression) }),
|
|
6639
6845
|
...(attributeName && { attributeName }),
|
|
6640
6846
|
...(watchTarget && { watchTarget: fromLegacyExpression(watchTarget) }),
|
|
6641
6847
|
...(customEventSource && { customEventSource }),
|
|
@@ -6891,10 +7097,9 @@ class Parser {
|
|
|
6891
7097
|
}
|
|
6892
7098
|
parseCommandCore() {
|
|
6893
7099
|
const commandToken = this.previous();
|
|
6894
|
-
|
|
7100
|
+
const commandName = commandToken.value;
|
|
6895
7101
|
if (commandName === 'beep' && this.check('!')) {
|
|
6896
7102
|
this.advance();
|
|
6897
|
-
commandName = 'beep!';
|
|
6898
7103
|
}
|
|
6899
7104
|
if (commandName === 'fetch') {
|
|
6900
7105
|
return parseFetchCommand(this.getContext(), commandToken);
|
|
@@ -6993,6 +7198,9 @@ class Parser {
|
|
|
6993
7198
|
args.push(this.parsePrimary());
|
|
6994
7199
|
}
|
|
6995
7200
|
const callNode = this.createCallExpression(createIdentifier(funcName, namePos), args);
|
|
7201
|
+
if (funcName === 'closest' && args.length > 0 && this.match('to')) {
|
|
7202
|
+
callNode.closestTo = this.parseExpression();
|
|
7203
|
+
}
|
|
6996
7204
|
if (funcName === 'next' || funcName === 'previous') {
|
|
6997
7205
|
const relativePositional = this.parseRelativePositionalModifiers();
|
|
6998
7206
|
if (relativePositional) {
|
|
@@ -7032,6 +7240,9 @@ class Parser {
|
|
|
7032
7240
|
}
|
|
7033
7241
|
parseTheXofY() {
|
|
7034
7242
|
const positionalKeywords = ['first', 'last', 'next', 'previous', 'random', 'closest'];
|
|
7243
|
+
if (this.isStyleRefStar(this.current)) {
|
|
7244
|
+
return this.parsePrimary();
|
|
7245
|
+
}
|
|
7035
7246
|
if (!this.checkIdentifier()) {
|
|
7036
7247
|
return this.createIdentifier('the');
|
|
7037
7248
|
}
|
|
@@ -7047,7 +7258,7 @@ class Parser {
|
|
|
7047
7258
|
return this.createIdentifier(propertyName);
|
|
7048
7259
|
}
|
|
7049
7260
|
this.advance();
|
|
7050
|
-
const target = this.
|
|
7261
|
+
const target = parseOfOperand(this.makePrattContext(), THE_OF_TARGET_BP);
|
|
7051
7262
|
return {
|
|
7052
7263
|
type: 'propertyOfExpression',
|
|
7053
7264
|
property: { type: 'identifier', name: propertyName },
|
|
@@ -7245,6 +7456,13 @@ class Parser {
|
|
|
7245
7456
|
checkLiteral() {
|
|
7246
7457
|
return this.checkPredicate(isLiteral);
|
|
7247
7458
|
}
|
|
7459
|
+
checkEventNamedCall() {
|
|
7460
|
+
if (this.isAtEnd() || !this.checkIdentifierLike() || this.checkIsCommand())
|
|
7461
|
+
return false;
|
|
7462
|
+
const token = this.peek();
|
|
7463
|
+
const next = this.tokens[this.current + 1];
|
|
7464
|
+
return next?.value === '(' && next.start === token.end;
|
|
7465
|
+
}
|
|
7248
7466
|
checkIdentifierLike() {
|
|
7249
7467
|
return this.checkPredicate(isIdentifierLike);
|
|
7250
7468
|
}
|
|
@@ -7353,6 +7571,22 @@ class Parser {
|
|
|
7353
7571
|
isAtEnd() {
|
|
7354
7572
|
return this.current >= this.tokens.length;
|
|
7355
7573
|
}
|
|
7574
|
+
atEndOfInput() {
|
|
7575
|
+
for (let i = this.current; i < this.tokens.length; i++) {
|
|
7576
|
+
if (!isComment(this.tokens[i]))
|
|
7577
|
+
return false;
|
|
7578
|
+
}
|
|
7579
|
+
return true;
|
|
7580
|
+
}
|
|
7581
|
+
checkFeatureStart() {
|
|
7582
|
+
if (this.isAtEnd())
|
|
7583
|
+
return false;
|
|
7584
|
+
const value = this.peek().value;
|
|
7585
|
+
return (value === 'init' ||
|
|
7586
|
+
value === 'on' ||
|
|
7587
|
+
value === 'def' ||
|
|
7588
|
+
getRegisteredFeature(value) !== undefined);
|
|
7589
|
+
}
|
|
7356
7590
|
peek() {
|
|
7357
7591
|
if (this.isAtEnd()) {
|
|
7358
7592
|
return { kind: 'unknown', value: '', start: 0, end: 0, line: 1, column: 1 };
|
|
@@ -7564,6 +7798,7 @@ class Parser {
|
|
|
7564
7798
|
match: this.match.bind(this),
|
|
7565
7799
|
matchOperator: this.matchOperator.bind(this),
|
|
7566
7800
|
isAtEnd: this.isAtEnd.bind(this),
|
|
7801
|
+
atEndOfInput: this.atEndOfInput.bind(this),
|
|
7567
7802
|
createIdentifier: this.createIdentifier.bind(this),
|
|
7568
7803
|
parseExpression: this.parseExpression.bind(this),
|
|
7569
7804
|
parsePrimary: this.parsePrimary.bind(this),
|
|
@@ -7614,7 +7849,15 @@ Parser.POSTFIX_UNARY_OPERATORS = new Set([
|
|
|
7614
7849
|
'ignoring case',
|
|
7615
7850
|
]);
|
|
7616
7851
|
Parser.PRATT_TABLE = PARSER_TABLE;
|
|
7852
|
+
Parser.BEEP_OPERAND_BP = 80;
|
|
7617
7853
|
Parser.PSEUDO_COMMAND_PREPOSITIONS = ['from', 'on', 'with', 'into', 'at', 'to'];
|
|
7854
|
+
Parser.POSITIONAL_SOURCE_HEADS = new Set([
|
|
7855
|
+
'closest',
|
|
7856
|
+
'next',
|
|
7857
|
+
'previous',
|
|
7858
|
+
'first',
|
|
7859
|
+
'last',
|
|
7860
|
+
]);
|
|
7618
7861
|
|
|
7619
7862
|
class FullParserImpl {
|
|
7620
7863
|
constructor() {
|