@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
|
@@ -1691,6 +1691,39 @@ function makeTypeCheckHandler(negated) {
|
|
|
1691
1691
|
};
|
|
1692
1692
|
};
|
|
1693
1693
|
}
|
|
1694
|
+
const OF_OPERAND_BP = 70;
|
|
1695
|
+
function parseOfOperand(ctx, minBp = OF_OPERAND_BP) {
|
|
1696
|
+
let right = ctx.parseExpr(minBp);
|
|
1697
|
+
if (ctx.peek()?.value === 'in') {
|
|
1698
|
+
const inToken = ctx.advance();
|
|
1699
|
+
const scope = ctx.parseExpr(minBp);
|
|
1700
|
+
right = {
|
|
1701
|
+
type: 'binaryExpression',
|
|
1702
|
+
operator: 'in',
|
|
1703
|
+
left: right,
|
|
1704
|
+
right: scope,
|
|
1705
|
+
start: right.start,
|
|
1706
|
+
end: scope.end ?? inToken.end,
|
|
1707
|
+
line: right.line ?? inToken.line,
|
|
1708
|
+
column: right.column ?? inToken.column,
|
|
1709
|
+
};
|
|
1710
|
+
}
|
|
1711
|
+
return right;
|
|
1712
|
+
}
|
|
1713
|
+
const THE_OF_TARGET_BP = OF_OPERAND_BP + 1;
|
|
1714
|
+
function ofHandler(left, token, ctx) {
|
|
1715
|
+
const right = parseOfOperand(ctx);
|
|
1716
|
+
return {
|
|
1717
|
+
type: 'binaryExpression',
|
|
1718
|
+
operator: token.value,
|
|
1719
|
+
left,
|
|
1720
|
+
right,
|
|
1721
|
+
start: left.start,
|
|
1722
|
+
end: right.end ?? token.end,
|
|
1723
|
+
line: left.line ?? token.line,
|
|
1724
|
+
column: left.column ?? token.column,
|
|
1725
|
+
};
|
|
1726
|
+
}
|
|
1694
1727
|
const PARSER_COMPARISON_FRAGMENT = new Map([
|
|
1695
1728
|
['===', leftAssoc(30)],
|
|
1696
1729
|
['!==', leftAssoc(30)],
|
|
@@ -1735,7 +1768,7 @@ const PARSER_COMPARISON_FRAGMENT = new Map([
|
|
|
1735
1768
|
['is greater than or equal to', leftAssoc(30)],
|
|
1736
1769
|
['is less than or equal to', leftAssoc(30)],
|
|
1737
1770
|
['in', leftAssoc(30)],
|
|
1738
|
-
['of',
|
|
1771
|
+
['of', { infix: { bp: [90, OF_OPERAND_BP], handler: ofHandler } }],
|
|
1739
1772
|
['really', leftAssoc(30)],
|
|
1740
1773
|
[
|
|
1741
1774
|
'exists',
|
|
@@ -2232,6 +2265,48 @@ function parseMaybeNamedArgument(ctx) {
|
|
|
2232
2265
|
const value = ctx.parseExpression();
|
|
2233
2266
|
return name !== undefined ? { name, value } : { value };
|
|
2234
2267
|
}
|
|
2268
|
+
function isNakedNamedArgStart(ctx) {
|
|
2269
|
+
if (ctx.check('{'))
|
|
2270
|
+
return false;
|
|
2271
|
+
if (!ctx.checkIdentifierLike())
|
|
2272
|
+
return false;
|
|
2273
|
+
const next = ctx.peekAt(1);
|
|
2274
|
+
return next !== null && next.value === ':';
|
|
2275
|
+
}
|
|
2276
|
+
function parseNakedNamedArgs(ctx, parseValue, command) {
|
|
2277
|
+
const properties = [];
|
|
2278
|
+
const startPos = ctx.getPosition();
|
|
2279
|
+
do {
|
|
2280
|
+
if (!ctx.checkIdentifierLike())
|
|
2281
|
+
break;
|
|
2282
|
+
const keyToken = ctx.advance();
|
|
2283
|
+
const key = {
|
|
2284
|
+
type: 'identifier',
|
|
2285
|
+
name: keyToken.value,
|
|
2286
|
+
start: keyToken.start,
|
|
2287
|
+
end: keyToken.end,
|
|
2288
|
+
line: keyToken.line,
|
|
2289
|
+
column: keyToken.column,
|
|
2290
|
+
};
|
|
2291
|
+
ctx.consume(':', `Expected ':' after property name in ${command} named arguments`);
|
|
2292
|
+
const value = parseValue();
|
|
2293
|
+
if (value) {
|
|
2294
|
+
properties.push({ key, value });
|
|
2295
|
+
}
|
|
2296
|
+
} while (ctx.match(',') && !ctx.isAtEnd());
|
|
2297
|
+
const endPos = ctx.getPosition();
|
|
2298
|
+
return {
|
|
2299
|
+
type: 'objectLiteral',
|
|
2300
|
+
properties,
|
|
2301
|
+
start: startPos.start,
|
|
2302
|
+
end: endPos.end,
|
|
2303
|
+
line: startPos.line,
|
|
2304
|
+
column: startPos.column,
|
|
2305
|
+
};
|
|
2306
|
+
}
|
|
2307
|
+
function isGluedBeepBang(beep, bang) {
|
|
2308
|
+
return beep?.value === 'beep' && bang?.value === '!' && bang.start === beep.end;
|
|
2309
|
+
}
|
|
2235
2310
|
|
|
2236
2311
|
class CommandNodeBuilder {
|
|
2237
2312
|
constructor(name) {
|
|
@@ -2433,6 +2508,8 @@ function parseTriggerCommand(ctx, identifierNode) {
|
|
|
2433
2508
|
const modifiers = {};
|
|
2434
2509
|
while (!isCommandBoundary(ctx)) {
|
|
2435
2510
|
if (ctx.check(KEYWORDS.ON) || ctx.check(KEYWORDS.TO)) {
|
|
2511
|
+
if (modifiers['on'])
|
|
2512
|
+
break;
|
|
2436
2513
|
ctx.advance();
|
|
2437
2514
|
const target = parseOneArgument(ctx, [KEYWORDS.WITH]);
|
|
2438
2515
|
if (target)
|
|
@@ -2573,6 +2650,9 @@ function parseRepeatCommand(ctx, commandToken) {
|
|
|
2573
2650
|
ctx.advance();
|
|
2574
2651
|
loopType = KEYWORDS.FOREVER;
|
|
2575
2652
|
}
|
|
2653
|
+
else if (ctx.checkIsCommand() || ctx.isCommand(ctx.peek().value)) {
|
|
2654
|
+
loopType = KEYWORDS.FOREVER;
|
|
2655
|
+
}
|
|
2576
2656
|
else {
|
|
2577
2657
|
times = ctx.parseExpression();
|
|
2578
2658
|
if (ctx.check(KEYWORDS.TIMES)) {
|
|
@@ -2615,10 +2695,12 @@ function parseRepeatCommand(ctx, commandToken) {
|
|
|
2615
2695
|
loopType = result.terminator;
|
|
2616
2696
|
ctx.advance();
|
|
2617
2697
|
condition = ctx.parseExpression();
|
|
2618
|
-
if (
|
|
2698
|
+
if (ctx.check('end')) {
|
|
2699
|
+
ctx.advance();
|
|
2700
|
+
}
|
|
2701
|
+
else if (!ctx.atEndOfInput()) {
|
|
2619
2702
|
throw new Error('Expected "end" to close repeat block');
|
|
2620
2703
|
}
|
|
2621
|
-
ctx.advance();
|
|
2622
2704
|
}
|
|
2623
2705
|
}
|
|
2624
2706
|
else {
|
|
@@ -2864,7 +2946,7 @@ function parseIfCommand(ctx, commandToken) {
|
|
|
2864
2946
|
}));
|
|
2865
2947
|
}
|
|
2866
2948
|
}
|
|
2867
|
-
if (!consumedElseIf && !ctx.
|
|
2949
|
+
if (!consumedElseIf && !ctx.atEndOfInput()) {
|
|
2868
2950
|
ctx.consume(KEYWORDS.END, "Expected 'end' after if block");
|
|
2869
2951
|
}
|
|
2870
2952
|
}
|
|
@@ -2889,7 +2971,6 @@ function parseIfCommand(ctx, commandToken) {
|
|
|
2889
2971
|
.build();
|
|
2890
2972
|
}
|
|
2891
2973
|
function parseForCommand(ctx, commandToken) {
|
|
2892
|
-
const args = [];
|
|
2893
2974
|
let variable = null;
|
|
2894
2975
|
let collection = null;
|
|
2895
2976
|
if (ctx.check(KEYWORDS.EACH)) {
|
|
@@ -2932,31 +3013,22 @@ function parseForCommand(ctx, commandToken) {
|
|
|
2932
3013
|
}
|
|
2933
3014
|
}
|
|
2934
3015
|
const commands = ctx.parseCommandListUntilEnd('for');
|
|
2935
|
-
|
|
2936
|
-
type: 'identifier',
|
|
2937
|
-
name: 'for',
|
|
2938
|
-
start: commandToken.start,
|
|
2939
|
-
end: commandToken.end,
|
|
2940
|
-
line: commandToken.line,
|
|
2941
|
-
column: commandToken.column,
|
|
2942
|
-
});
|
|
2943
|
-
const forPos = {
|
|
3016
|
+
const pos = {
|
|
2944
3017
|
start: commandToken.start,
|
|
2945
3018
|
end: commandToken.end,
|
|
2946
3019
|
line: commandToken.line,
|
|
2947
3020
|
column: commandToken.column,
|
|
2948
3021
|
};
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
}
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
.withArgs(...args)
|
|
3022
|
+
const modifiers = {
|
|
3023
|
+
loopType: toLegacyExpression(createStringLiteral(KEYWORDS.FOR, pos)),
|
|
3024
|
+
for: toLegacyExpression(createStringLiteral(variable, pos)),
|
|
3025
|
+
in: collection,
|
|
3026
|
+
};
|
|
3027
|
+
if (indexVariable)
|
|
3028
|
+
modifiers['index'] = toLegacyExpression(createStringLiteral(indexVariable, pos));
|
|
3029
|
+
return CommandNodeBuilder.from({ ...commandToken, value: 'repeat' })
|
|
3030
|
+
.withArgs(createBlock(commands, { ...pos, end: pos.end || 0 }))
|
|
3031
|
+
.withModifiers(modifiers)
|
|
2960
3032
|
.endingAt(ctx.getPosition())
|
|
2961
3033
|
.build();
|
|
2962
3034
|
}
|
|
@@ -3012,86 +3084,159 @@ function parseMeasureCommand(ctx, identifierNode) {
|
|
|
3012
3084
|
}
|
|
3013
3085
|
return builder.build();
|
|
3014
3086
|
}
|
|
3015
|
-
const
|
|
3087
|
+
const CONTEXT_REFS = new Set(['me', 'it', 'you', 'result']);
|
|
3016
3088
|
function parseTransitionCommand(ctx, commandToken) {
|
|
3017
|
-
const
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
const
|
|
3022
|
-
const
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
if (
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
target = parsed.object;
|
|
3033
|
-
property = {
|
|
3034
|
-
type: 'string',
|
|
3035
|
-
value: parsed.property.name,
|
|
3036
|
-
start: leadToken.start || 0,
|
|
3037
|
-
end: ctx.getPosition().end,
|
|
3038
|
-
line: leadToken.line,
|
|
3039
|
-
column: leadToken.column,
|
|
3040
|
-
};
|
|
3089
|
+
const pairs = [];
|
|
3090
|
+
do {
|
|
3091
|
+
pairs.push(parseTransitionPair(ctx));
|
|
3092
|
+
} while (startsAnotherPair(ctx));
|
|
3093
|
+
const [first, ...rest] = pairs;
|
|
3094
|
+
const args = first.owner ? [first.owner, first.property] : [first.property];
|
|
3095
|
+
const modifiers = { to: first.to };
|
|
3096
|
+
if (first.from)
|
|
3097
|
+
modifiers['from'] = first.from;
|
|
3098
|
+
if (rest.length > 0)
|
|
3099
|
+
modifiers['pairs'] = pairsNode(rest);
|
|
3100
|
+
for (let i = 0; i < 3 && !ctx.isAtEnd(); i++) {
|
|
3101
|
+
if (ctx.check('over') && !modifiers['over']) {
|
|
3102
|
+
ctx.advance();
|
|
3103
|
+
modifiers['over'] = ctx.parseExpression();
|
|
3041
3104
|
}
|
|
3042
|
-
else {
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
ctx.advance();
|
|
3105
|
+
else if (ctx.check('using') && !modifiers['using']) {
|
|
3106
|
+
ctx.advance();
|
|
3107
|
+
modifiers['using'] = ctx.parseExpression();
|
|
3046
3108
|
}
|
|
3047
|
-
|
|
3048
|
-
const firstToken = ctx.peek();
|
|
3049
|
-
if (!property && (isIdentifierLike(firstToken) || firstToken.value === '*')) {
|
|
3050
|
-
let propertyValue = '';
|
|
3051
|
-
if (ctx.check('*')) {
|
|
3052
|
-
propertyValue = '*';
|
|
3109
|
+
else if (ctx.check(KEYWORDS.WITH) && !modifiers['with']) {
|
|
3053
3110
|
ctx.advance();
|
|
3111
|
+
modifiers['with'] = ctx.parsePrimary();
|
|
3054
3112
|
}
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
propertyValue += hyphenatedName;
|
|
3058
|
-
property = {
|
|
3059
|
-
type: 'string',
|
|
3060
|
-
value: propertyValue,
|
|
3061
|
-
start: firstToken.start || 0,
|
|
3062
|
-
end: ctx.getPosition().end,
|
|
3063
|
-
line: firstToken.line,
|
|
3064
|
-
column: firstToken.column,
|
|
3065
|
-
};
|
|
3113
|
+
else {
|
|
3114
|
+
break;
|
|
3066
3115
|
}
|
|
3067
3116
|
}
|
|
3068
|
-
|
|
3069
|
-
|
|
3117
|
+
return CommandNodeBuilder.from(commandToken)
|
|
3118
|
+
.withArgs(...args)
|
|
3119
|
+
.withModifiers(modifiers)
|
|
3120
|
+
.endingAt(ctx.getPosition())
|
|
3121
|
+
.build();
|
|
3122
|
+
}
|
|
3123
|
+
function parseTransitionPair(ctx) {
|
|
3124
|
+
const { owner, property } = parseTransitionProperty(ctx);
|
|
3125
|
+
let from = null;
|
|
3126
|
+
if (ctx.check('from')) {
|
|
3127
|
+
ctx.advance();
|
|
3128
|
+
from = ctx.parseExpression();
|
|
3070
3129
|
}
|
|
3071
|
-
if (target)
|
|
3072
|
-
args.push(target);
|
|
3073
|
-
args.push(property);
|
|
3074
3130
|
if (!ctx.check(KEYWORDS.TO)) {
|
|
3075
3131
|
throw new Error('Expected "to" keyword after property in transition command');
|
|
3076
3132
|
}
|
|
3077
3133
|
ctx.advance();
|
|
3078
|
-
const
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3134
|
+
const to = ctx.parseExpression();
|
|
3135
|
+
return { owner, property, from, to };
|
|
3136
|
+
}
|
|
3137
|
+
function parseTransitionProperty(ctx) {
|
|
3138
|
+
const lead = ctx.peek();
|
|
3139
|
+
if (!lead || isKeyword(lead, [KEYWORDS.TO, 'from'])) {
|
|
3140
|
+
throw new Error('Transition command requires a CSS property');
|
|
3084
3141
|
}
|
|
3085
|
-
|
|
3142
|
+
const expr = ctx.parseExpression();
|
|
3143
|
+
const split = splitStyleExpression(expr);
|
|
3144
|
+
if (split)
|
|
3145
|
+
return { owner: split.owner, property: propertyNode(split.name, lead, ctx) };
|
|
3146
|
+
let name = '';
|
|
3147
|
+
if (ctx.check('*')) {
|
|
3148
|
+
name = '*';
|
|
3086
3149
|
ctx.advance();
|
|
3087
|
-
const timingFunction = ctx.parsePrimary();
|
|
3088
|
-
modifiers['with'] = timingFunction;
|
|
3089
3150
|
}
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3151
|
+
const hyphenated = parseHyphenatedName(ctx);
|
|
3152
|
+
if (!hyphenated)
|
|
3153
|
+
throw new Error('Transition command requires a CSS property');
|
|
3154
|
+
return { owner: expr, property: propertyNode(name + hyphenated, lead, ctx) };
|
|
3155
|
+
}
|
|
3156
|
+
function splitStyleExpression(node) {
|
|
3157
|
+
const n = node;
|
|
3158
|
+
switch (n.type) {
|
|
3159
|
+
case 'selector':
|
|
3160
|
+
return typeof n.value === 'string' && n.value.startsWith('*')
|
|
3161
|
+
? { owner: null, name: n.value }
|
|
3162
|
+
: null;
|
|
3163
|
+
case 'identifier':
|
|
3164
|
+
return typeof n.name === 'string' && !CONTEXT_REFS.has(n.name)
|
|
3165
|
+
? { owner: null, name: n.name }
|
|
3166
|
+
: null;
|
|
3167
|
+
case 'possessiveExpression':
|
|
3168
|
+
case 'memberExpression':
|
|
3169
|
+
return !n.computed && n.object && typeof n.property?.name === 'string'
|
|
3170
|
+
? { owner: n.object, name: n.property.name }
|
|
3171
|
+
: null;
|
|
3172
|
+
case 'binaryExpression': {
|
|
3173
|
+
if (n.operator !== 'of' || !n.left || !n.right)
|
|
3174
|
+
return null;
|
|
3175
|
+
const inner = splitStyleExpression(n.left);
|
|
3176
|
+
return inner && !inner.owner ? { owner: n.right, name: inner.name } : null;
|
|
3177
|
+
}
|
|
3178
|
+
case 'propertyOfExpression':
|
|
3179
|
+
return n.target && typeof n.property?.name === 'string'
|
|
3180
|
+
? { owner: n.target, name: n.property.name }
|
|
3181
|
+
: null;
|
|
3182
|
+
default:
|
|
3183
|
+
return null;
|
|
3184
|
+
}
|
|
3185
|
+
}
|
|
3186
|
+
function propertyNode(name, lead, ctx) {
|
|
3187
|
+
return {
|
|
3188
|
+
type: 'string',
|
|
3189
|
+
value: name,
|
|
3190
|
+
start: lead.start || 0,
|
|
3191
|
+
end: ctx.getPosition().end,
|
|
3192
|
+
line: lead.line,
|
|
3193
|
+
column: lead.column,
|
|
3194
|
+
};
|
|
3195
|
+
}
|
|
3196
|
+
function startsAnotherPair(ctx) {
|
|
3197
|
+
if (ctx.isAtEnd() || isComment(ctx.peek()))
|
|
3198
|
+
return false;
|
|
3199
|
+
if (isKeyword(ctx.peek(), ['over', 'using', KEYWORDS.WITH]))
|
|
3200
|
+
return false;
|
|
3201
|
+
return !isCommandBoundary(ctx, ['catch', 'finally', 'on']);
|
|
3202
|
+
}
|
|
3203
|
+
function pairsNode(pairs) {
|
|
3204
|
+
const field = (name, value) => ({
|
|
3205
|
+
key: {
|
|
3206
|
+
type: 'identifier',
|
|
3207
|
+
name,
|
|
3208
|
+
start: value.start,
|
|
3209
|
+
end: value.end,
|
|
3210
|
+
line: value.line,
|
|
3211
|
+
column: value.column,
|
|
3212
|
+
},
|
|
3213
|
+
value,
|
|
3214
|
+
});
|
|
3215
|
+
const elements = pairs.map(pair => {
|
|
3216
|
+
const properties = [field('property', pair.property), field('to', pair.to)];
|
|
3217
|
+
if (pair.owner)
|
|
3218
|
+
properties.push(field('owner', pair.owner));
|
|
3219
|
+
if (pair.from)
|
|
3220
|
+
properties.push(field('from', pair.from));
|
|
3221
|
+
return {
|
|
3222
|
+
type: 'objectLiteral',
|
|
3223
|
+
properties,
|
|
3224
|
+
start: (pair.owner ?? pair.property).start,
|
|
3225
|
+
end: pair.to.end,
|
|
3226
|
+
line: (pair.owner ?? pair.property).line,
|
|
3227
|
+
column: (pair.owner ?? pair.property).column,
|
|
3228
|
+
};
|
|
3229
|
+
});
|
|
3230
|
+
const firstEl = elements[0];
|
|
3231
|
+
const lastEl = elements[elements.length - 1];
|
|
3232
|
+
return {
|
|
3233
|
+
type: 'arrayLiteral',
|
|
3234
|
+
elements,
|
|
3235
|
+
start: firstEl.start,
|
|
3236
|
+
end: lastEl.end,
|
|
3237
|
+
line: firstEl.line,
|
|
3238
|
+
column: firstEl.column,
|
|
3239
|
+
};
|
|
3095
3240
|
}
|
|
3096
3241
|
function parseStartCommand(ctx, identifierNode) {
|
|
3097
3242
|
if (!ctx.match('view')) {
|
|
@@ -3211,6 +3356,15 @@ function parseToggleCommand(ctx, identifierNode) {
|
|
|
3211
3356
|
args.push(classArg.expression);
|
|
3212
3357
|
modifiers['as'] = literalModifier(mode);
|
|
3213
3358
|
}
|
|
3359
|
+
else if (classArg?.type === 'binaryExpression' &&
|
|
3360
|
+
classArg.operator === 'of' &&
|
|
3361
|
+
classArg.left?.type === 'selector' &&
|
|
3362
|
+
typeof classArg.left.value === 'string' &&
|
|
3363
|
+
classArg.left.value.startsWith('*') &&
|
|
3364
|
+
classArg.right) {
|
|
3365
|
+
args.push(classArg.left);
|
|
3366
|
+
modifiers['on'] = classArg.right;
|
|
3367
|
+
}
|
|
3214
3368
|
else if (classArg) {
|
|
3215
3369
|
args.push(classArg);
|
|
3216
3370
|
}
|
|
@@ -3388,11 +3542,17 @@ function parseSwapCommand(ctx, identifierNode) {
|
|
|
3388
3542
|
const targetExpr = isDelete ? ctx.parseExpression() : parseOneArgument(ctx, [KEYWORDS.WITH]);
|
|
3389
3543
|
if (targetExpr)
|
|
3390
3544
|
args.push(targetExpr);
|
|
3391
|
-
|
|
3545
|
+
const isMorph = identifierNode.name === 'morph';
|
|
3546
|
+
if (!isDelete &&
|
|
3547
|
+
(consumeOptionalKeyword(ctx, KEYWORDS.WITH) ||
|
|
3548
|
+
(isMorph && consumeOptionalKeyword(ctx, KEYWORDS.TO)))) {
|
|
3392
3549
|
const contentExpr = parseOneArgument(ctx, ['using']);
|
|
3393
3550
|
if (contentExpr)
|
|
3394
3551
|
modifiers['with'] = contentExpr;
|
|
3395
3552
|
}
|
|
3553
|
+
if (isMorph && modifiers['with'] === undefined) {
|
|
3554
|
+
ctx.addError("Expected 'to' and the content to morph into");
|
|
3555
|
+
}
|
|
3396
3556
|
return CommandNodeBuilder.fromIdentifier(identifierNode)
|
|
3397
3557
|
.withArgs(...args)
|
|
3398
3558
|
.withModifiers({ ...modifiers, ...parseViewTransitionTail(ctx) })
|
|
@@ -4153,6 +4313,8 @@ function atArgumentBoundary(ctx, grammar) {
|
|
|
4153
4313
|
const next = ctx.peek();
|
|
4154
4314
|
if (ctx.checkIsCommand() && ctx.peekAt(1)?.value === '(')
|
|
4155
4315
|
return false;
|
|
4316
|
+
if (isGluedBeepBang(next, ctx.peekAt(1)))
|
|
4317
|
+
return false;
|
|
4156
4318
|
if (isCommandBoundary(ctx, ['catch', 'finally', 'on']))
|
|
4157
4319
|
return true;
|
|
4158
4320
|
return isKeyword(next, [...grammar.markers]);
|
|
@@ -4184,10 +4346,13 @@ function parseDeclaredCommand(ctx, commandToken, name, grammar) {
|
|
|
4184
4346
|
}
|
|
4185
4347
|
}
|
|
4186
4348
|
const commaList = new Set(grammar.commaList ?? []);
|
|
4349
|
+
const namedArgs = new Set(grammar.namedArgs ?? []);
|
|
4187
4350
|
while (!ctx.isAtEnd() && isKeyword(ctx.peek(), [...grammar.markers])) {
|
|
4188
4351
|
const keyword = ctx.advance().value;
|
|
4189
4352
|
const key = ctx.resolveKeyword(keyword).toLowerCase();
|
|
4190
|
-
let value =
|
|
4353
|
+
let value = namedArgs.has(key) && isNakedNamedArgStart(ctx)
|
|
4354
|
+
? parseNakedNamedArgs(ctx, () => ctx.parseExpression(), name)
|
|
4355
|
+
: ctx.parseExpression();
|
|
4191
4356
|
if (value && commaList.has(key) && ctx.check(',')) {
|
|
4192
4357
|
const elements = [value];
|
|
4193
4358
|
while (ctx.match(',')) {
|
|
@@ -4352,8 +4517,8 @@ function parseFetchCommand(ctx, commandToken) {
|
|
|
4352
4517
|
}
|
|
4353
4518
|
if (ctx.check('with') && !modifiers['with']) {
|
|
4354
4519
|
ctx.advance();
|
|
4355
|
-
if (
|
|
4356
|
-
modifiers['with'] =
|
|
4520
|
+
if (isNakedNamedArgStart(ctx)) {
|
|
4521
|
+
modifiers['with'] = parseNakedNamedArgs(ctx, () => ctx.parsePrimary(), 'fetch');
|
|
4357
4522
|
}
|
|
4358
4523
|
else {
|
|
4359
4524
|
modifiers['with'] = ctx.parsePrimary();
|
|
@@ -4388,45 +4553,6 @@ function parseFetchCommand(ctx, commandToken) {
|
|
|
4388
4553
|
}
|
|
4389
4554
|
return builder.build();
|
|
4390
4555
|
}
|
|
4391
|
-
function isFetchNakedNamedArgStart(ctx) {
|
|
4392
|
-
if (ctx.check('{'))
|
|
4393
|
-
return false;
|
|
4394
|
-
if (!ctx.checkIdentifierLike())
|
|
4395
|
-
return false;
|
|
4396
|
-
const next = ctx.peekAt(1);
|
|
4397
|
-
return next !== null && next.value === ':';
|
|
4398
|
-
}
|
|
4399
|
-
function parseFetchNakedNamedArgs(ctx) {
|
|
4400
|
-
const properties = [];
|
|
4401
|
-
const startPos = ctx.getPosition();
|
|
4402
|
-
do {
|
|
4403
|
-
if (!ctx.checkIdentifierLike())
|
|
4404
|
-
break;
|
|
4405
|
-
const keyToken = ctx.advance();
|
|
4406
|
-
const key = {
|
|
4407
|
-
type: 'identifier',
|
|
4408
|
-
name: keyToken.value,
|
|
4409
|
-
start: keyToken.start,
|
|
4410
|
-
end: keyToken.end,
|
|
4411
|
-
line: keyToken.line,
|
|
4412
|
-
column: keyToken.column,
|
|
4413
|
-
};
|
|
4414
|
-
ctx.consume(':', "Expected ':' after property name in fetch named arguments");
|
|
4415
|
-
const value = ctx.parsePrimary();
|
|
4416
|
-
if (value) {
|
|
4417
|
-
properties.push({ key, value });
|
|
4418
|
-
}
|
|
4419
|
-
} while (ctx.match(',') && !ctx.isAtEnd());
|
|
4420
|
-
const endPos = ctx.getPosition();
|
|
4421
|
-
return {
|
|
4422
|
-
type: 'objectLiteral',
|
|
4423
|
-
properties,
|
|
4424
|
-
start: startPos.start,
|
|
4425
|
-
end: endPos.end,
|
|
4426
|
-
line: startPos.line,
|
|
4427
|
-
column: startPos.column,
|
|
4428
|
-
};
|
|
4429
|
-
}
|
|
4430
4556
|
function findJsEndBoundary(ctx, startPos) {
|
|
4431
4557
|
const input = ctx.getInputSlice(startPos);
|
|
4432
4558
|
if (!input) {
|
|
@@ -4546,6 +4672,7 @@ function parseTellCommand(ctx, identifierNode) {
|
|
|
4546
4672
|
const commands = [];
|
|
4547
4673
|
while (!ctx.isAtEnd()) {
|
|
4548
4674
|
if (ctx.checkIsCommand()) {
|
|
4675
|
+
const word = ctx.peek().value;
|
|
4549
4676
|
try {
|
|
4550
4677
|
ctx.advance();
|
|
4551
4678
|
const cmd = ctx.parseCommand();
|
|
@@ -4556,7 +4683,8 @@ function parseTellCommand(ctx, identifierNode) {
|
|
|
4556
4683
|
break;
|
|
4557
4684
|
}
|
|
4558
4685
|
}
|
|
4559
|
-
catch {
|
|
4686
|
+
catch (error) {
|
|
4687
|
+
ctx.addError(`'${word}' in a tell body failed to parse: ${error instanceof Error ? error.message : String(error)}`);
|
|
4560
4688
|
break;
|
|
4561
4689
|
}
|
|
4562
4690
|
if (ctx.match(KEYWORDS.AND) || ctx.match(KEYWORDS.THEN)) {
|
|
@@ -4705,7 +4833,6 @@ const ONE_EXPR = (syntax) => ({
|
|
|
4705
4833
|
markers: [],
|
|
4706
4834
|
syntax,
|
|
4707
4835
|
});
|
|
4708
|
-
const BEEP = ONE_EXPR('beep! [<expression>, \u2026]');
|
|
4709
4836
|
const COMMAND_GRAMMAR = {
|
|
4710
4837
|
append: { positional: 'expression', markers: ['to'], syntax: 'append <value> [to <target>]' },
|
|
4711
4838
|
prepend: { positional: 'expression', markers: ['to'], syntax: 'prepend <value> [to <target>]' },
|
|
@@ -4716,8 +4843,7 @@ const COMMAND_GRAMMAR = {
|
|
|
4716
4843
|
syntax: 'make (a|an) <type> [from <args>] [called <name>]',
|
|
4717
4844
|
},
|
|
4718
4845
|
throw: { positional: 'primary', markers: [], syntax: 'throw <error>' },
|
|
4719
|
-
'beep!'
|
|
4720
|
-
beep: BEEP,
|
|
4846
|
+
beep: ONE_EXPR('beep! [<expression>, \u2026]'),
|
|
4721
4847
|
blur: ONE_EXPR('blur [<target>]'),
|
|
4722
4848
|
break: NONE,
|
|
4723
4849
|
breakpoint: NONE,
|
|
@@ -4741,7 +4867,8 @@ const COMMAND_GRAMMAR = {
|
|
|
4741
4867
|
render: {
|
|
4742
4868
|
positional: 'expression',
|
|
4743
4869
|
markers: ['with'],
|
|
4744
|
-
|
|
4870
|
+
namedArgs: ['with'],
|
|
4871
|
+
syntax: 'render <template> [with <name>: <value>, \u2026]',
|
|
4745
4872
|
},
|
|
4746
4873
|
reset: ONE_EXPR('reset [<form>]'),
|
|
4747
4874
|
return: ONE_EXPR('return [<value>]'),
|
|
@@ -5096,7 +5223,10 @@ class Parser {
|
|
|
5096
5223
|
const firstToken = this.peek();
|
|
5097
5224
|
const prefixEntry = Parser.PRATT_TABLE.get(firstToken.value);
|
|
5098
5225
|
let left;
|
|
5099
|
-
if (
|
|
5226
|
+
if (isGluedBeepBang(firstToken, this.tokens[this.current + 1])) {
|
|
5227
|
+
left = this.parseBeepExpression();
|
|
5228
|
+
}
|
|
5229
|
+
else if (prefixEntry?.prefix) {
|
|
5100
5230
|
const token = this.advance();
|
|
5101
5231
|
if (this.isAtEnd()) {
|
|
5102
5232
|
this.addError(`Expected expression after '${token.value}' operator`);
|
|
@@ -5116,6 +5246,9 @@ class Parser {
|
|
|
5116
5246
|
continue;
|
|
5117
5247
|
}
|
|
5118
5248
|
}
|
|
5249
|
+
if (nextToken.value === '*' && this.isSpacedStyleRef(this.current)) {
|
|
5250
|
+
break;
|
|
5251
|
+
}
|
|
5119
5252
|
const infixEntry = Parser.PRATT_TABLE.get(nextToken.value);
|
|
5120
5253
|
if (!infixEntry?.infix) {
|
|
5121
5254
|
if (STOP_TOKENS.has(nextToken.value) || STOP_DELIMITERS.has(nextToken.value)) {
|
|
@@ -5177,6 +5310,35 @@ class Parser {
|
|
|
5177
5310
|
this.prattCache.set(cacheKey, { node: left, endPos: this.current });
|
|
5178
5311
|
return left;
|
|
5179
5312
|
}
|
|
5313
|
+
parseBeepExpression() {
|
|
5314
|
+
const beepToken = this.advance();
|
|
5315
|
+
this.advance();
|
|
5316
|
+
if (this.isAtEnd()) {
|
|
5317
|
+
this.addError("Expected expression after 'beep!'");
|
|
5318
|
+
return this.createErrorNode();
|
|
5319
|
+
}
|
|
5320
|
+
const operand = this.parseExpressionPratt(Parser.BEEP_OPERAND_BP);
|
|
5321
|
+
return {
|
|
5322
|
+
type: 'unaryExpression',
|
|
5323
|
+
operator: 'beep!',
|
|
5324
|
+
operand,
|
|
5325
|
+
argument: operand,
|
|
5326
|
+
prefix: true,
|
|
5327
|
+
start: beepToken.start,
|
|
5328
|
+
end: this.previous().end,
|
|
5329
|
+
line: beepToken.line,
|
|
5330
|
+
column: beepToken.column,
|
|
5331
|
+
};
|
|
5332
|
+
}
|
|
5333
|
+
isStyleRefStar(i) {
|
|
5334
|
+
const star = this.tokens[i];
|
|
5335
|
+
const name = this.tokens[i + 1];
|
|
5336
|
+
return star?.value === '*' && !!name && name.start === star.end && isIdentifierLike(name);
|
|
5337
|
+
}
|
|
5338
|
+
isSpacedStyleRef(i) {
|
|
5339
|
+
const prev = this.tokens[i - 1];
|
|
5340
|
+
return this.isStyleRefStar(i) && !!prev && prev.end < this.tokens[i].start;
|
|
5341
|
+
}
|
|
5180
5342
|
tryParseStringPostfix(left, nextToken) {
|
|
5181
5343
|
const v = nextToken.value;
|
|
5182
5344
|
let unit = null;
|
|
@@ -5379,6 +5541,10 @@ class Parser {
|
|
|
5379
5541
|
const isStop = () => this.check('end') || extraStops.some(s => this.check(s));
|
|
5380
5542
|
debug.parse('\ud83d\udd04 parseCommandListUntilTerminator: Starting (extraStops:', extraStops.join(','), ')');
|
|
5381
5543
|
while (!this.isAtEnd() && !isStop()) {
|
|
5544
|
+
if (this.checkComment()) {
|
|
5545
|
+
this.advance();
|
|
5546
|
+
continue;
|
|
5547
|
+
}
|
|
5382
5548
|
debug.parse('\ud83d\udccd Loop iteration, current token:', this.peek().value, 'kind:', this.peek().kind);
|
|
5383
5549
|
let parsedCommand = false;
|
|
5384
5550
|
const isCommandToken = this.checkIsCommand();
|
|
@@ -5462,7 +5628,7 @@ class Parser {
|
|
|
5462
5628
|
debug.parse('\u2705 Found "end", consuming it');
|
|
5463
5629
|
this.advance();
|
|
5464
5630
|
}
|
|
5465
|
-
else {
|
|
5631
|
+
else if (!this.atEndOfInput()) {
|
|
5466
5632
|
debug.parse('\u274c ERROR: Expected "end" but got:', this.peek().value, 'at position:', this.peek().start);
|
|
5467
5633
|
throw new Error(`Expected "end" to close ${construct} block`);
|
|
5468
5634
|
}
|
|
@@ -5475,7 +5641,7 @@ class Parser {
|
|
|
5475
5641
|
if (this.check('end')) {
|
|
5476
5642
|
this.advance();
|
|
5477
5643
|
}
|
|
5478
|
-
else {
|
|
5644
|
+
else if (!this.atEndOfInput()) {
|
|
5479
5645
|
throw new Error('Expected "end" to close repeat block');
|
|
5480
5646
|
}
|
|
5481
5647
|
}
|
|
@@ -5491,7 +5657,10 @@ class Parser {
|
|
|
5491
5657
|
this.advance();
|
|
5492
5658
|
}
|
|
5493
5659
|
else if (terminator === '') {
|
|
5494
|
-
|
|
5660
|
+
if (!this.atEndOfInput()) {
|
|
5661
|
+
throw new Error('Expected "end", "else", "until", or "while" to close repeat block');
|
|
5662
|
+
}
|
|
5663
|
+
return { commands, terminator: 'end' };
|
|
5495
5664
|
}
|
|
5496
5665
|
return { commands, terminator };
|
|
5497
5666
|
}
|
|
@@ -6075,7 +6244,9 @@ class Parser {
|
|
|
6075
6244
|
parseTopLevelInitBlock() {
|
|
6076
6245
|
const pos = this.getPosition();
|
|
6077
6246
|
const initCommands = this.parseCommandBlock(['end']);
|
|
6078
|
-
this.
|
|
6247
|
+
if (!this.match('end') && !this.atEndOfInput() && !this.checkFeatureStart()) {
|
|
6248
|
+
this.consume('end', "Expected 'end' after init block");
|
|
6249
|
+
}
|
|
6079
6250
|
return {
|
|
6080
6251
|
type: 'initBlock',
|
|
6081
6252
|
commands: initCommands,
|
|
@@ -6251,10 +6422,29 @@ class Parser {
|
|
|
6251
6422
|
...(sourceExpr.column !== undefined && { column: sourceExpr.column }),
|
|
6252
6423
|
};
|
|
6253
6424
|
}
|
|
6425
|
+
isArgumentListOnly(open) {
|
|
6426
|
+
let depth = 0;
|
|
6427
|
+
for (let i = open; i < this.tokens.length; i++) {
|
|
6428
|
+
const token = this.tokens[i];
|
|
6429
|
+
if (token.kind !== 'operator')
|
|
6430
|
+
continue;
|
|
6431
|
+
if (token.value === '(' || token.value === '[' || token.value === '{')
|
|
6432
|
+
depth++;
|
|
6433
|
+
else if (token.value === ')' || token.value === ']' || token.value === '}') {
|
|
6434
|
+
if (--depth === 0)
|
|
6435
|
+
return i === open + 1;
|
|
6436
|
+
}
|
|
6437
|
+
else if (token.value === ',' && depth === 1)
|
|
6438
|
+
return true;
|
|
6439
|
+
}
|
|
6440
|
+
return false;
|
|
6441
|
+
}
|
|
6254
6442
|
tryParsePseudoCommand(callExpr, sourceExpr) {
|
|
6255
6443
|
const methodName = callExpr.callee.name;
|
|
6256
6444
|
const nextToken = this.peek();
|
|
6257
|
-
const
|
|
6445
|
+
const nextIsArticle = nextToken.value.toLowerCase() === 'the';
|
|
6446
|
+
const hasPseudoCommandPattern = nextIsArticle ||
|
|
6447
|
+
Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(nextToken.value.toLowerCase()) ||
|
|
6258
6448
|
(isIdentifier(nextToken) && !this.isCommand(nextToken.value)) ||
|
|
6259
6449
|
isContextVar(nextToken);
|
|
6260
6450
|
if (!hasPseudoCommandPattern) {
|
|
@@ -6280,7 +6470,10 @@ class Parser {
|
|
|
6280
6470
|
});
|
|
6281
6471
|
}
|
|
6282
6472
|
let preposition;
|
|
6283
|
-
if (
|
|
6473
|
+
if (nextIsArticle) {
|
|
6474
|
+
this.advance();
|
|
6475
|
+
}
|
|
6476
|
+
else if (Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(this.peek().value.toLowerCase())) {
|
|
6284
6477
|
preposition = this.advance().value.toLowerCase();
|
|
6285
6478
|
}
|
|
6286
6479
|
let targetExpr;
|
|
@@ -6444,10 +6637,22 @@ class Parser {
|
|
|
6444
6637
|
this.consume(']', "Expected ']' after event condition");
|
|
6445
6638
|
}
|
|
6446
6639
|
let target;
|
|
6640
|
+
let targetExpression;
|
|
6447
6641
|
if (this.match('from')) {
|
|
6448
|
-
|
|
6449
|
-
|
|
6450
|
-
|
|
6642
|
+
if (this.check('the'))
|
|
6643
|
+
this.advance();
|
|
6644
|
+
if (Parser.POSITIONAL_SOURCE_HEADS.has(this.peek().value.toLowerCase())) {
|
|
6645
|
+
const startIndex = this.current;
|
|
6646
|
+
targetExpression = this.parseExpression();
|
|
6647
|
+
target = this.tokens
|
|
6648
|
+
.slice(startIndex, this.current)
|
|
6649
|
+
.map(t => t.value)
|
|
6650
|
+
.join(' ');
|
|
6651
|
+
}
|
|
6652
|
+
else {
|
|
6653
|
+
target = this.advance().value;
|
|
6654
|
+
}
|
|
6655
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Parsed 'from' target: ${target}`);
|
|
6451
6656
|
this.parseTemporalKeywordModifiers(modifiers);
|
|
6452
6657
|
}
|
|
6453
6658
|
let attributeName;
|
|
@@ -6493,7 +6698,7 @@ class Parser {
|
|
|
6493
6698
|
const nextIsOpenParen = this.tokens[this.current + 1]?.value === '(';
|
|
6494
6699
|
const commandName = this.peek().value.toLowerCase();
|
|
6495
6700
|
const isSpecialBodyCommand = commandName === 'js' || commandName === 'tell';
|
|
6496
|
-
if (nextIsOpenParen && !isSpecialBodyCommand) {
|
|
6701
|
+
if (nextIsOpenParen && !isSpecialBodyCommand && this.isArgumentListOnly(this.current + 1)) {
|
|
6497
6702
|
let expr;
|
|
6498
6703
|
const savedError = this.error;
|
|
6499
6704
|
try {
|
|
@@ -6525,7 +6730,7 @@ class Parser {
|
|
|
6525
6730
|
}
|
|
6526
6731
|
}
|
|
6527
6732
|
}
|
|
6528
|
-
else if (this.checkIdentifier()) {
|
|
6733
|
+
else if (this.checkIdentifier() || this.checkEventNamedCall()) {
|
|
6529
6734
|
const token = this.peek();
|
|
6530
6735
|
if (this.isCommand(token.value)) {
|
|
6531
6736
|
const cmd = this.parseCommandWithErrorRecovery();
|
|
@@ -6634,6 +6839,7 @@ class Parser {
|
|
|
6634
6839
|
...(eventParams.length > 0 && { args: eventParams }),
|
|
6635
6840
|
...(condition && { condition: fromLegacyExpression(condition) }),
|
|
6636
6841
|
...(target && { target }),
|
|
6842
|
+
...(targetExpression && { targetExpression: fromLegacyExpression(targetExpression) }),
|
|
6637
6843
|
...(attributeName && { attributeName }),
|
|
6638
6844
|
...(watchTarget && { watchTarget: fromLegacyExpression(watchTarget) }),
|
|
6639
6845
|
...(customEventSource && { customEventSource }),
|
|
@@ -6889,10 +7095,9 @@ class Parser {
|
|
|
6889
7095
|
}
|
|
6890
7096
|
parseCommandCore() {
|
|
6891
7097
|
const commandToken = this.previous();
|
|
6892
|
-
|
|
7098
|
+
const commandName = commandToken.value;
|
|
6893
7099
|
if (commandName === 'beep' && this.check('!')) {
|
|
6894
7100
|
this.advance();
|
|
6895
|
-
commandName = 'beep!';
|
|
6896
7101
|
}
|
|
6897
7102
|
if (commandName === 'fetch') {
|
|
6898
7103
|
return parseFetchCommand(this.getContext(), commandToken);
|
|
@@ -6991,6 +7196,9 @@ class Parser {
|
|
|
6991
7196
|
args.push(this.parsePrimary());
|
|
6992
7197
|
}
|
|
6993
7198
|
const callNode = this.createCallExpression(createIdentifier(funcName, namePos), args);
|
|
7199
|
+
if (funcName === 'closest' && args.length > 0 && this.match('to')) {
|
|
7200
|
+
callNode.closestTo = this.parseExpression();
|
|
7201
|
+
}
|
|
6994
7202
|
if (funcName === 'next' || funcName === 'previous') {
|
|
6995
7203
|
const relativePositional = this.parseRelativePositionalModifiers();
|
|
6996
7204
|
if (relativePositional) {
|
|
@@ -7030,6 +7238,9 @@ class Parser {
|
|
|
7030
7238
|
}
|
|
7031
7239
|
parseTheXofY() {
|
|
7032
7240
|
const positionalKeywords = ['first', 'last', 'next', 'previous', 'random', 'closest'];
|
|
7241
|
+
if (this.isStyleRefStar(this.current)) {
|
|
7242
|
+
return this.parsePrimary();
|
|
7243
|
+
}
|
|
7033
7244
|
if (!this.checkIdentifier()) {
|
|
7034
7245
|
return this.createIdentifier('the');
|
|
7035
7246
|
}
|
|
@@ -7045,7 +7256,7 @@ class Parser {
|
|
|
7045
7256
|
return this.createIdentifier(propertyName);
|
|
7046
7257
|
}
|
|
7047
7258
|
this.advance();
|
|
7048
|
-
const target = this.
|
|
7259
|
+
const target = parseOfOperand(this.makePrattContext(), THE_OF_TARGET_BP);
|
|
7049
7260
|
return {
|
|
7050
7261
|
type: 'propertyOfExpression',
|
|
7051
7262
|
property: { type: 'identifier', name: propertyName },
|
|
@@ -7243,6 +7454,13 @@ class Parser {
|
|
|
7243
7454
|
checkLiteral() {
|
|
7244
7455
|
return this.checkPredicate(isLiteral);
|
|
7245
7456
|
}
|
|
7457
|
+
checkEventNamedCall() {
|
|
7458
|
+
if (this.isAtEnd() || !this.checkIdentifierLike() || this.checkIsCommand())
|
|
7459
|
+
return false;
|
|
7460
|
+
const token = this.peek();
|
|
7461
|
+
const next = this.tokens[this.current + 1];
|
|
7462
|
+
return next?.value === '(' && next.start === token.end;
|
|
7463
|
+
}
|
|
7246
7464
|
checkIdentifierLike() {
|
|
7247
7465
|
return this.checkPredicate(isIdentifierLike);
|
|
7248
7466
|
}
|
|
@@ -7351,6 +7569,22 @@ class Parser {
|
|
|
7351
7569
|
isAtEnd() {
|
|
7352
7570
|
return this.current >= this.tokens.length;
|
|
7353
7571
|
}
|
|
7572
|
+
atEndOfInput() {
|
|
7573
|
+
for (let i = this.current; i < this.tokens.length; i++) {
|
|
7574
|
+
if (!isComment(this.tokens[i]))
|
|
7575
|
+
return false;
|
|
7576
|
+
}
|
|
7577
|
+
return true;
|
|
7578
|
+
}
|
|
7579
|
+
checkFeatureStart() {
|
|
7580
|
+
if (this.isAtEnd())
|
|
7581
|
+
return false;
|
|
7582
|
+
const value = this.peek().value;
|
|
7583
|
+
return (value === 'init' ||
|
|
7584
|
+
value === 'on' ||
|
|
7585
|
+
value === 'def' ||
|
|
7586
|
+
getRegisteredFeature(value) !== undefined);
|
|
7587
|
+
}
|
|
7354
7588
|
peek() {
|
|
7355
7589
|
if (this.isAtEnd()) {
|
|
7356
7590
|
return { kind: 'unknown', value: '', start: 0, end: 0, line: 1, column: 1 };
|
|
@@ -7562,6 +7796,7 @@ class Parser {
|
|
|
7562
7796
|
match: this.match.bind(this),
|
|
7563
7797
|
matchOperator: this.matchOperator.bind(this),
|
|
7564
7798
|
isAtEnd: this.isAtEnd.bind(this),
|
|
7799
|
+
atEndOfInput: this.atEndOfInput.bind(this),
|
|
7565
7800
|
createIdentifier: this.createIdentifier.bind(this),
|
|
7566
7801
|
parseExpression: this.parseExpression.bind(this),
|
|
7567
7802
|
parsePrimary: this.parsePrimary.bind(this),
|
|
@@ -7612,7 +7847,15 @@ Parser.POSTFIX_UNARY_OPERATORS = new Set([
|
|
|
7612
7847
|
'ignoring case',
|
|
7613
7848
|
]);
|
|
7614
7849
|
Parser.PRATT_TABLE = PARSER_TABLE;
|
|
7850
|
+
Parser.BEEP_OPERAND_BP = 80;
|
|
7615
7851
|
Parser.PSEUDO_COMMAND_PREPOSITIONS = ['from', 'on', 'with', 'into', 'at', 'to'];
|
|
7852
|
+
Parser.POSITIONAL_SOURCE_HEADS = new Set([
|
|
7853
|
+
'closest',
|
|
7854
|
+
'next',
|
|
7855
|
+
'previous',
|
|
7856
|
+
'first',
|
|
7857
|
+
'last',
|
|
7858
|
+
]);
|
|
7616
7859
|
|
|
7617
7860
|
class FullParserImpl {
|
|
7618
7861
|
constructor() {
|