@hyperfixi/core 3.1.0 → 3.2.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 +24 -9
- package/dist/bundle-generator/index.mjs +24 -9
- package/dist/bundle-generator/parser-templates.d.ts +1 -1
- package/dist/chunks/bridge-Cnj8dQFV.js +2 -0
- package/dist/chunks/browser-modular-BNDYF6Dq.js +2 -0
- package/dist/chunks/index-DQi2o8Lk.js +2 -0
- package/dist/chunks/index-LLuwEQp0.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 +929 -425
- package/dist/commands/index.mjs +929 -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 +1140 -485
- package/dist/index.min.js +1 -1
- package/dist/index.mjs +1140 -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 +387 -153
- package/dist/parser/full-parser.mjs +387 -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 +526 -210
- package/dist/registry/index.mjs +526 -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-DylVo1oZ.js +0 -2
- package/dist/chunks/browser-modular-sLPMdxR7.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')) {
|
|
@@ -3390,11 +3535,17 @@ function parseSwapCommand(ctx, identifierNode) {
|
|
|
3390
3535
|
const targetExpr = isDelete ? ctx.parseExpression() : parseOneArgument(ctx, [KEYWORDS.WITH]);
|
|
3391
3536
|
if (targetExpr)
|
|
3392
3537
|
args.push(targetExpr);
|
|
3393
|
-
|
|
3538
|
+
const isMorph = identifierNode.name === 'morph';
|
|
3539
|
+
if (!isDelete &&
|
|
3540
|
+
(consumeOptionalKeyword(ctx, KEYWORDS.WITH) ||
|
|
3541
|
+
(isMorph && consumeOptionalKeyword(ctx, KEYWORDS.TO)))) {
|
|
3394
3542
|
const contentExpr = parseOneArgument(ctx, ['using']);
|
|
3395
3543
|
if (contentExpr)
|
|
3396
3544
|
modifiers['with'] = contentExpr;
|
|
3397
3545
|
}
|
|
3546
|
+
if (isMorph && modifiers['with'] === undefined) {
|
|
3547
|
+
ctx.addError("Expected 'to' and the content to morph into");
|
|
3548
|
+
}
|
|
3398
3549
|
return CommandNodeBuilder.fromIdentifier(identifierNode)
|
|
3399
3550
|
.withArgs(...args)
|
|
3400
3551
|
.withModifiers({ ...modifiers, ...parseViewTransitionTail(ctx) })
|
|
@@ -4155,6 +4306,8 @@ function atArgumentBoundary(ctx, grammar) {
|
|
|
4155
4306
|
const next = ctx.peek();
|
|
4156
4307
|
if (ctx.checkIsCommand() && ctx.peekAt(1)?.value === '(')
|
|
4157
4308
|
return false;
|
|
4309
|
+
if (isGluedBeepBang(next, ctx.peekAt(1)))
|
|
4310
|
+
return false;
|
|
4158
4311
|
if (isCommandBoundary(ctx, ['catch', 'finally', 'on']))
|
|
4159
4312
|
return true;
|
|
4160
4313
|
return isKeyword(next, [...grammar.markers]);
|
|
@@ -4186,10 +4339,13 @@ function parseDeclaredCommand(ctx, commandToken, name, grammar) {
|
|
|
4186
4339
|
}
|
|
4187
4340
|
}
|
|
4188
4341
|
const commaList = new Set(grammar.commaList ?? []);
|
|
4342
|
+
const namedArgs = new Set(grammar.namedArgs ?? []);
|
|
4189
4343
|
while (!ctx.isAtEnd() && isKeyword(ctx.peek(), [...grammar.markers])) {
|
|
4190
4344
|
const keyword = ctx.advance().value;
|
|
4191
4345
|
const key = ctx.resolveKeyword(keyword).toLowerCase();
|
|
4192
|
-
let value =
|
|
4346
|
+
let value = namedArgs.has(key) && isNakedNamedArgStart(ctx)
|
|
4347
|
+
? parseNakedNamedArgs(ctx, () => ctx.parseExpression(), name)
|
|
4348
|
+
: ctx.parseExpression();
|
|
4193
4349
|
if (value && commaList.has(key) && ctx.check(',')) {
|
|
4194
4350
|
const elements = [value];
|
|
4195
4351
|
while (ctx.match(',')) {
|
|
@@ -4354,8 +4510,8 @@ function parseFetchCommand(ctx, commandToken) {
|
|
|
4354
4510
|
}
|
|
4355
4511
|
if (ctx.check('with') && !modifiers['with']) {
|
|
4356
4512
|
ctx.advance();
|
|
4357
|
-
if (
|
|
4358
|
-
modifiers['with'] =
|
|
4513
|
+
if (isNakedNamedArgStart(ctx)) {
|
|
4514
|
+
modifiers['with'] = parseNakedNamedArgs(ctx, () => ctx.parsePrimary(), 'fetch');
|
|
4359
4515
|
}
|
|
4360
4516
|
else {
|
|
4361
4517
|
modifiers['with'] = ctx.parsePrimary();
|
|
@@ -4390,45 +4546,6 @@ function parseFetchCommand(ctx, commandToken) {
|
|
|
4390
4546
|
}
|
|
4391
4547
|
return builder.build();
|
|
4392
4548
|
}
|
|
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
4549
|
function findJsEndBoundary(ctx, startPos) {
|
|
4433
4550
|
const input = ctx.getInputSlice(startPos);
|
|
4434
4551
|
if (!input) {
|
|
@@ -4548,6 +4665,7 @@ function parseTellCommand(ctx, identifierNode) {
|
|
|
4548
4665
|
const commands = [];
|
|
4549
4666
|
while (!ctx.isAtEnd()) {
|
|
4550
4667
|
if (ctx.checkIsCommand()) {
|
|
4668
|
+
const word = ctx.peek().value;
|
|
4551
4669
|
try {
|
|
4552
4670
|
ctx.advance();
|
|
4553
4671
|
const cmd = ctx.parseCommand();
|
|
@@ -4558,7 +4676,8 @@ function parseTellCommand(ctx, identifierNode) {
|
|
|
4558
4676
|
break;
|
|
4559
4677
|
}
|
|
4560
4678
|
}
|
|
4561
|
-
catch {
|
|
4679
|
+
catch (error) {
|
|
4680
|
+
ctx.addError(`'${word}' in a tell body failed to parse: ${error instanceof Error ? error.message : String(error)}`);
|
|
4562
4681
|
break;
|
|
4563
4682
|
}
|
|
4564
4683
|
if (ctx.match(KEYWORDS.AND) || ctx.match(KEYWORDS.THEN)) {
|
|
@@ -4707,7 +4826,6 @@ const ONE_EXPR = (syntax) => ({
|
|
|
4707
4826
|
markers: [],
|
|
4708
4827
|
syntax,
|
|
4709
4828
|
});
|
|
4710
|
-
const BEEP = ONE_EXPR('beep! [<expression>, \u2026]');
|
|
4711
4829
|
const COMMAND_GRAMMAR = {
|
|
4712
4830
|
append: { positional: 'expression', markers: ['to'], syntax: 'append <value> [to <target>]' },
|
|
4713
4831
|
prepend: { positional: 'expression', markers: ['to'], syntax: 'prepend <value> [to <target>]' },
|
|
@@ -4718,8 +4836,7 @@ const COMMAND_GRAMMAR = {
|
|
|
4718
4836
|
syntax: 'make (a|an) <type> [from <args>] [called <name>]',
|
|
4719
4837
|
},
|
|
4720
4838
|
throw: { positional: 'primary', markers: [], syntax: 'throw <error>' },
|
|
4721
|
-
'beep!'
|
|
4722
|
-
beep: BEEP,
|
|
4839
|
+
beep: ONE_EXPR('beep! [<expression>, \u2026]'),
|
|
4723
4840
|
blur: ONE_EXPR('blur [<target>]'),
|
|
4724
4841
|
break: NONE,
|
|
4725
4842
|
breakpoint: NONE,
|
|
@@ -4743,7 +4860,8 @@ const COMMAND_GRAMMAR = {
|
|
|
4743
4860
|
render: {
|
|
4744
4861
|
positional: 'expression',
|
|
4745
4862
|
markers: ['with'],
|
|
4746
|
-
|
|
4863
|
+
namedArgs: ['with'],
|
|
4864
|
+
syntax: 'render <template> [with <name>: <value>, \u2026]',
|
|
4747
4865
|
},
|
|
4748
4866
|
reset: ONE_EXPR('reset [<form>]'),
|
|
4749
4867
|
return: ONE_EXPR('return [<value>]'),
|
|
@@ -5098,7 +5216,10 @@ class Parser {
|
|
|
5098
5216
|
const firstToken = this.peek();
|
|
5099
5217
|
const prefixEntry = Parser.PRATT_TABLE.get(firstToken.value);
|
|
5100
5218
|
let left;
|
|
5101
|
-
if (
|
|
5219
|
+
if (isGluedBeepBang(firstToken, this.tokens[this.current + 1])) {
|
|
5220
|
+
left = this.parseBeepExpression();
|
|
5221
|
+
}
|
|
5222
|
+
else if (prefixEntry?.prefix) {
|
|
5102
5223
|
const token = this.advance();
|
|
5103
5224
|
if (this.isAtEnd()) {
|
|
5104
5225
|
this.addError(`Expected expression after '${token.value}' operator`);
|
|
@@ -5118,6 +5239,9 @@ class Parser {
|
|
|
5118
5239
|
continue;
|
|
5119
5240
|
}
|
|
5120
5241
|
}
|
|
5242
|
+
if (nextToken.value === '*' && this.isSpacedStyleRef(this.current)) {
|
|
5243
|
+
break;
|
|
5244
|
+
}
|
|
5121
5245
|
const infixEntry = Parser.PRATT_TABLE.get(nextToken.value);
|
|
5122
5246
|
if (!infixEntry?.infix) {
|
|
5123
5247
|
if (STOP_TOKENS.has(nextToken.value) || STOP_DELIMITERS.has(nextToken.value)) {
|
|
@@ -5179,6 +5303,35 @@ class Parser {
|
|
|
5179
5303
|
this.prattCache.set(cacheKey, { node: left, endPos: this.current });
|
|
5180
5304
|
return left;
|
|
5181
5305
|
}
|
|
5306
|
+
parseBeepExpression() {
|
|
5307
|
+
const beepToken = this.advance();
|
|
5308
|
+
this.advance();
|
|
5309
|
+
if (this.isAtEnd()) {
|
|
5310
|
+
this.addError("Expected expression after 'beep!'");
|
|
5311
|
+
return this.createErrorNode();
|
|
5312
|
+
}
|
|
5313
|
+
const operand = this.parseExpressionPratt(Parser.BEEP_OPERAND_BP);
|
|
5314
|
+
return {
|
|
5315
|
+
type: 'unaryExpression',
|
|
5316
|
+
operator: 'beep!',
|
|
5317
|
+
operand,
|
|
5318
|
+
argument: operand,
|
|
5319
|
+
prefix: true,
|
|
5320
|
+
start: beepToken.start,
|
|
5321
|
+
end: this.previous().end,
|
|
5322
|
+
line: beepToken.line,
|
|
5323
|
+
column: beepToken.column,
|
|
5324
|
+
};
|
|
5325
|
+
}
|
|
5326
|
+
isStyleRefStar(i) {
|
|
5327
|
+
const star = this.tokens[i];
|
|
5328
|
+
const name = this.tokens[i + 1];
|
|
5329
|
+
return star?.value === '*' && !!name && name.start === star.end && isIdentifierLike(name);
|
|
5330
|
+
}
|
|
5331
|
+
isSpacedStyleRef(i) {
|
|
5332
|
+
const prev = this.tokens[i - 1];
|
|
5333
|
+
return this.isStyleRefStar(i) && !!prev && prev.end < this.tokens[i].start;
|
|
5334
|
+
}
|
|
5182
5335
|
tryParseStringPostfix(left, nextToken) {
|
|
5183
5336
|
const v = nextToken.value;
|
|
5184
5337
|
let unit = null;
|
|
@@ -5381,6 +5534,10 @@ class Parser {
|
|
|
5381
5534
|
const isStop = () => this.check('end') || extraStops.some(s => this.check(s));
|
|
5382
5535
|
debug.parse('\ud83d\udd04 parseCommandListUntilTerminator: Starting (extraStops:', extraStops.join(','), ')');
|
|
5383
5536
|
while (!this.isAtEnd() && !isStop()) {
|
|
5537
|
+
if (this.checkComment()) {
|
|
5538
|
+
this.advance();
|
|
5539
|
+
continue;
|
|
5540
|
+
}
|
|
5384
5541
|
debug.parse('\ud83d\udccd Loop iteration, current token:', this.peek().value, 'kind:', this.peek().kind);
|
|
5385
5542
|
let parsedCommand = false;
|
|
5386
5543
|
const isCommandToken = this.checkIsCommand();
|
|
@@ -5464,7 +5621,7 @@ class Parser {
|
|
|
5464
5621
|
debug.parse('\u2705 Found "end", consuming it');
|
|
5465
5622
|
this.advance();
|
|
5466
5623
|
}
|
|
5467
|
-
else {
|
|
5624
|
+
else if (!this.atEndOfInput()) {
|
|
5468
5625
|
debug.parse('\u274c ERROR: Expected "end" but got:', this.peek().value, 'at position:', this.peek().start);
|
|
5469
5626
|
throw new Error(`Expected "end" to close ${construct} block`);
|
|
5470
5627
|
}
|
|
@@ -5477,7 +5634,7 @@ class Parser {
|
|
|
5477
5634
|
if (this.check('end')) {
|
|
5478
5635
|
this.advance();
|
|
5479
5636
|
}
|
|
5480
|
-
else {
|
|
5637
|
+
else if (!this.atEndOfInput()) {
|
|
5481
5638
|
throw new Error('Expected "end" to close repeat block');
|
|
5482
5639
|
}
|
|
5483
5640
|
}
|
|
@@ -5493,7 +5650,10 @@ class Parser {
|
|
|
5493
5650
|
this.advance();
|
|
5494
5651
|
}
|
|
5495
5652
|
else if (terminator === '') {
|
|
5496
|
-
|
|
5653
|
+
if (!this.atEndOfInput()) {
|
|
5654
|
+
throw new Error('Expected "end", "else", "until", or "while" to close repeat block');
|
|
5655
|
+
}
|
|
5656
|
+
return { commands, terminator: 'end' };
|
|
5497
5657
|
}
|
|
5498
5658
|
return { commands, terminator };
|
|
5499
5659
|
}
|
|
@@ -6077,7 +6237,9 @@ class Parser {
|
|
|
6077
6237
|
parseTopLevelInitBlock() {
|
|
6078
6238
|
const pos = this.getPosition();
|
|
6079
6239
|
const initCommands = this.parseCommandBlock(['end']);
|
|
6080
|
-
this.
|
|
6240
|
+
if (!this.match('end') && !this.atEndOfInput() && !this.checkFeatureStart()) {
|
|
6241
|
+
this.consume('end', "Expected 'end' after init block");
|
|
6242
|
+
}
|
|
6081
6243
|
return {
|
|
6082
6244
|
type: 'initBlock',
|
|
6083
6245
|
commands: initCommands,
|
|
@@ -6253,10 +6415,29 @@ class Parser {
|
|
|
6253
6415
|
...(sourceExpr.column !== undefined && { column: sourceExpr.column }),
|
|
6254
6416
|
};
|
|
6255
6417
|
}
|
|
6418
|
+
isArgumentListOnly(open) {
|
|
6419
|
+
let depth = 0;
|
|
6420
|
+
for (let i = open; i < this.tokens.length; i++) {
|
|
6421
|
+
const token = this.tokens[i];
|
|
6422
|
+
if (token.kind !== 'operator')
|
|
6423
|
+
continue;
|
|
6424
|
+
if (token.value === '(' || token.value === '[' || token.value === '{')
|
|
6425
|
+
depth++;
|
|
6426
|
+
else if (token.value === ')' || token.value === ']' || token.value === '}') {
|
|
6427
|
+
if (--depth === 0)
|
|
6428
|
+
return i === open + 1;
|
|
6429
|
+
}
|
|
6430
|
+
else if (token.value === ',' && depth === 1)
|
|
6431
|
+
return true;
|
|
6432
|
+
}
|
|
6433
|
+
return false;
|
|
6434
|
+
}
|
|
6256
6435
|
tryParsePseudoCommand(callExpr, sourceExpr) {
|
|
6257
6436
|
const methodName = callExpr.callee.name;
|
|
6258
6437
|
const nextToken = this.peek();
|
|
6259
|
-
const
|
|
6438
|
+
const nextIsArticle = nextToken.value.toLowerCase() === 'the';
|
|
6439
|
+
const hasPseudoCommandPattern = nextIsArticle ||
|
|
6440
|
+
Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(nextToken.value.toLowerCase()) ||
|
|
6260
6441
|
(isIdentifier(nextToken) && !this.isCommand(nextToken.value)) ||
|
|
6261
6442
|
isContextVar(nextToken);
|
|
6262
6443
|
if (!hasPseudoCommandPattern) {
|
|
@@ -6282,7 +6463,10 @@ class Parser {
|
|
|
6282
6463
|
});
|
|
6283
6464
|
}
|
|
6284
6465
|
let preposition;
|
|
6285
|
-
if (
|
|
6466
|
+
if (nextIsArticle) {
|
|
6467
|
+
this.advance();
|
|
6468
|
+
}
|
|
6469
|
+
else if (Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(this.peek().value.toLowerCase())) {
|
|
6286
6470
|
preposition = this.advance().value.toLowerCase();
|
|
6287
6471
|
}
|
|
6288
6472
|
let targetExpr;
|
|
@@ -6446,10 +6630,22 @@ class Parser {
|
|
|
6446
6630
|
this.consume(']', "Expected ']' after event condition");
|
|
6447
6631
|
}
|
|
6448
6632
|
let target;
|
|
6633
|
+
let targetExpression;
|
|
6449
6634
|
if (this.match('from')) {
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
|
|
6635
|
+
if (this.check('the'))
|
|
6636
|
+
this.advance();
|
|
6637
|
+
if (Parser.POSITIONAL_SOURCE_HEADS.has(this.peek().value.toLowerCase())) {
|
|
6638
|
+
const startIndex = this.current;
|
|
6639
|
+
targetExpression = this.parseExpression();
|
|
6640
|
+
target = this.tokens
|
|
6641
|
+
.slice(startIndex, this.current)
|
|
6642
|
+
.map(t => t.value)
|
|
6643
|
+
.join(' ');
|
|
6644
|
+
}
|
|
6645
|
+
else {
|
|
6646
|
+
target = this.advance().value;
|
|
6647
|
+
}
|
|
6648
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Parsed 'from' target: ${target}`);
|
|
6453
6649
|
this.parseTemporalKeywordModifiers(modifiers);
|
|
6454
6650
|
}
|
|
6455
6651
|
let attributeName;
|
|
@@ -6495,7 +6691,7 @@ class Parser {
|
|
|
6495
6691
|
const nextIsOpenParen = this.tokens[this.current + 1]?.value === '(';
|
|
6496
6692
|
const commandName = this.peek().value.toLowerCase();
|
|
6497
6693
|
const isSpecialBodyCommand = commandName === 'js' || commandName === 'tell';
|
|
6498
|
-
if (nextIsOpenParen && !isSpecialBodyCommand) {
|
|
6694
|
+
if (nextIsOpenParen && !isSpecialBodyCommand && this.isArgumentListOnly(this.current + 1)) {
|
|
6499
6695
|
let expr;
|
|
6500
6696
|
const savedError = this.error;
|
|
6501
6697
|
try {
|
|
@@ -6527,7 +6723,7 @@ class Parser {
|
|
|
6527
6723
|
}
|
|
6528
6724
|
}
|
|
6529
6725
|
}
|
|
6530
|
-
else if (this.checkIdentifier()) {
|
|
6726
|
+
else if (this.checkIdentifier() || this.checkEventNamedCall()) {
|
|
6531
6727
|
const token = this.peek();
|
|
6532
6728
|
if (this.isCommand(token.value)) {
|
|
6533
6729
|
const cmd = this.parseCommandWithErrorRecovery();
|
|
@@ -6636,6 +6832,7 @@ class Parser {
|
|
|
6636
6832
|
...(eventParams.length > 0 && { args: eventParams }),
|
|
6637
6833
|
...(condition && { condition: fromLegacyExpression(condition) }),
|
|
6638
6834
|
...(target && { target }),
|
|
6835
|
+
...(targetExpression && { targetExpression: fromLegacyExpression(targetExpression) }),
|
|
6639
6836
|
...(attributeName && { attributeName }),
|
|
6640
6837
|
...(watchTarget && { watchTarget: fromLegacyExpression(watchTarget) }),
|
|
6641
6838
|
...(customEventSource && { customEventSource }),
|
|
@@ -6891,10 +7088,9 @@ class Parser {
|
|
|
6891
7088
|
}
|
|
6892
7089
|
parseCommandCore() {
|
|
6893
7090
|
const commandToken = this.previous();
|
|
6894
|
-
|
|
7091
|
+
const commandName = commandToken.value;
|
|
6895
7092
|
if (commandName === 'beep' && this.check('!')) {
|
|
6896
7093
|
this.advance();
|
|
6897
|
-
commandName = 'beep!';
|
|
6898
7094
|
}
|
|
6899
7095
|
if (commandName === 'fetch') {
|
|
6900
7096
|
return parseFetchCommand(this.getContext(), commandToken);
|
|
@@ -6993,6 +7189,9 @@ class Parser {
|
|
|
6993
7189
|
args.push(this.parsePrimary());
|
|
6994
7190
|
}
|
|
6995
7191
|
const callNode = this.createCallExpression(createIdentifier(funcName, namePos), args);
|
|
7192
|
+
if (funcName === 'closest' && args.length > 0 && this.match('to')) {
|
|
7193
|
+
callNode.closestTo = this.parseExpression();
|
|
7194
|
+
}
|
|
6996
7195
|
if (funcName === 'next' || funcName === 'previous') {
|
|
6997
7196
|
const relativePositional = this.parseRelativePositionalModifiers();
|
|
6998
7197
|
if (relativePositional) {
|
|
@@ -7032,6 +7231,9 @@ class Parser {
|
|
|
7032
7231
|
}
|
|
7033
7232
|
parseTheXofY() {
|
|
7034
7233
|
const positionalKeywords = ['first', 'last', 'next', 'previous', 'random', 'closest'];
|
|
7234
|
+
if (this.isStyleRefStar(this.current)) {
|
|
7235
|
+
return this.parsePrimary();
|
|
7236
|
+
}
|
|
7035
7237
|
if (!this.checkIdentifier()) {
|
|
7036
7238
|
return this.createIdentifier('the');
|
|
7037
7239
|
}
|
|
@@ -7047,7 +7249,7 @@ class Parser {
|
|
|
7047
7249
|
return this.createIdentifier(propertyName);
|
|
7048
7250
|
}
|
|
7049
7251
|
this.advance();
|
|
7050
|
-
const target = this.
|
|
7252
|
+
const target = parseOfOperand(this.makePrattContext(), THE_OF_TARGET_BP);
|
|
7051
7253
|
return {
|
|
7052
7254
|
type: 'propertyOfExpression',
|
|
7053
7255
|
property: { type: 'identifier', name: propertyName },
|
|
@@ -7245,6 +7447,13 @@ class Parser {
|
|
|
7245
7447
|
checkLiteral() {
|
|
7246
7448
|
return this.checkPredicate(isLiteral);
|
|
7247
7449
|
}
|
|
7450
|
+
checkEventNamedCall() {
|
|
7451
|
+
if (this.isAtEnd() || !this.checkIdentifierLike() || this.checkIsCommand())
|
|
7452
|
+
return false;
|
|
7453
|
+
const token = this.peek();
|
|
7454
|
+
const next = this.tokens[this.current + 1];
|
|
7455
|
+
return next?.value === '(' && next.start === token.end;
|
|
7456
|
+
}
|
|
7248
7457
|
checkIdentifierLike() {
|
|
7249
7458
|
return this.checkPredicate(isIdentifierLike);
|
|
7250
7459
|
}
|
|
@@ -7353,6 +7562,22 @@ class Parser {
|
|
|
7353
7562
|
isAtEnd() {
|
|
7354
7563
|
return this.current >= this.tokens.length;
|
|
7355
7564
|
}
|
|
7565
|
+
atEndOfInput() {
|
|
7566
|
+
for (let i = this.current; i < this.tokens.length; i++) {
|
|
7567
|
+
if (!isComment(this.tokens[i]))
|
|
7568
|
+
return false;
|
|
7569
|
+
}
|
|
7570
|
+
return true;
|
|
7571
|
+
}
|
|
7572
|
+
checkFeatureStart() {
|
|
7573
|
+
if (this.isAtEnd())
|
|
7574
|
+
return false;
|
|
7575
|
+
const value = this.peek().value;
|
|
7576
|
+
return (value === 'init' ||
|
|
7577
|
+
value === 'on' ||
|
|
7578
|
+
value === 'def' ||
|
|
7579
|
+
getRegisteredFeature(value) !== undefined);
|
|
7580
|
+
}
|
|
7356
7581
|
peek() {
|
|
7357
7582
|
if (this.isAtEnd()) {
|
|
7358
7583
|
return { kind: 'unknown', value: '', start: 0, end: 0, line: 1, column: 1 };
|
|
@@ -7564,6 +7789,7 @@ class Parser {
|
|
|
7564
7789
|
match: this.match.bind(this),
|
|
7565
7790
|
matchOperator: this.matchOperator.bind(this),
|
|
7566
7791
|
isAtEnd: this.isAtEnd.bind(this),
|
|
7792
|
+
atEndOfInput: this.atEndOfInput.bind(this),
|
|
7567
7793
|
createIdentifier: this.createIdentifier.bind(this),
|
|
7568
7794
|
parseExpression: this.parseExpression.bind(this),
|
|
7569
7795
|
parsePrimary: this.parsePrimary.bind(this),
|
|
@@ -7614,7 +7840,15 @@ Parser.POSTFIX_UNARY_OPERATORS = new Set([
|
|
|
7614
7840
|
'ignoring case',
|
|
7615
7841
|
]);
|
|
7616
7842
|
Parser.PRATT_TABLE = PARSER_TABLE;
|
|
7843
|
+
Parser.BEEP_OPERAND_BP = 80;
|
|
7617
7844
|
Parser.PSEUDO_COMMAND_PREPOSITIONS = ['from', 'on', 'with', 'into', 'at', 'to'];
|
|
7845
|
+
Parser.POSITIONAL_SOURCE_HEADS = new Set([
|
|
7846
|
+
'closest',
|
|
7847
|
+
'next',
|
|
7848
|
+
'previous',
|
|
7849
|
+
'first',
|
|
7850
|
+
'last',
|
|
7851
|
+
]);
|
|
7618
7852
|
|
|
7619
7853
|
class FullParserImpl {
|
|
7620
7854
|
constructor() {
|