@hyperfixi/core 3.1.1 → 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-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')) {
|
|
@@ -3388,11 +3533,17 @@ function parseSwapCommand(ctx, identifierNode) {
|
|
|
3388
3533
|
const targetExpr = isDelete ? ctx.parseExpression() : parseOneArgument(ctx, [KEYWORDS.WITH]);
|
|
3389
3534
|
if (targetExpr)
|
|
3390
3535
|
args.push(targetExpr);
|
|
3391
|
-
|
|
3536
|
+
const isMorph = identifierNode.name === 'morph';
|
|
3537
|
+
if (!isDelete &&
|
|
3538
|
+
(consumeOptionalKeyword(ctx, KEYWORDS.WITH) ||
|
|
3539
|
+
(isMorph && consumeOptionalKeyword(ctx, KEYWORDS.TO)))) {
|
|
3392
3540
|
const contentExpr = parseOneArgument(ctx, ['using']);
|
|
3393
3541
|
if (contentExpr)
|
|
3394
3542
|
modifiers['with'] = contentExpr;
|
|
3395
3543
|
}
|
|
3544
|
+
if (isMorph && modifiers['with'] === undefined) {
|
|
3545
|
+
ctx.addError("Expected 'to' and the content to morph into");
|
|
3546
|
+
}
|
|
3396
3547
|
return CommandNodeBuilder.fromIdentifier(identifierNode)
|
|
3397
3548
|
.withArgs(...args)
|
|
3398
3549
|
.withModifiers({ ...modifiers, ...parseViewTransitionTail(ctx) })
|
|
@@ -4153,6 +4304,8 @@ function atArgumentBoundary(ctx, grammar) {
|
|
|
4153
4304
|
const next = ctx.peek();
|
|
4154
4305
|
if (ctx.checkIsCommand() && ctx.peekAt(1)?.value === '(')
|
|
4155
4306
|
return false;
|
|
4307
|
+
if (isGluedBeepBang(next, ctx.peekAt(1)))
|
|
4308
|
+
return false;
|
|
4156
4309
|
if (isCommandBoundary(ctx, ['catch', 'finally', 'on']))
|
|
4157
4310
|
return true;
|
|
4158
4311
|
return isKeyword(next, [...grammar.markers]);
|
|
@@ -4184,10 +4337,13 @@ function parseDeclaredCommand(ctx, commandToken, name, grammar) {
|
|
|
4184
4337
|
}
|
|
4185
4338
|
}
|
|
4186
4339
|
const commaList = new Set(grammar.commaList ?? []);
|
|
4340
|
+
const namedArgs = new Set(grammar.namedArgs ?? []);
|
|
4187
4341
|
while (!ctx.isAtEnd() && isKeyword(ctx.peek(), [...grammar.markers])) {
|
|
4188
4342
|
const keyword = ctx.advance().value;
|
|
4189
4343
|
const key = ctx.resolveKeyword(keyword).toLowerCase();
|
|
4190
|
-
let value =
|
|
4344
|
+
let value = namedArgs.has(key) && isNakedNamedArgStart(ctx)
|
|
4345
|
+
? parseNakedNamedArgs(ctx, () => ctx.parseExpression(), name)
|
|
4346
|
+
: ctx.parseExpression();
|
|
4191
4347
|
if (value && commaList.has(key) && ctx.check(',')) {
|
|
4192
4348
|
const elements = [value];
|
|
4193
4349
|
while (ctx.match(',')) {
|
|
@@ -4352,8 +4508,8 @@ function parseFetchCommand(ctx, commandToken) {
|
|
|
4352
4508
|
}
|
|
4353
4509
|
if (ctx.check('with') && !modifiers['with']) {
|
|
4354
4510
|
ctx.advance();
|
|
4355
|
-
if (
|
|
4356
|
-
modifiers['with'] =
|
|
4511
|
+
if (isNakedNamedArgStart(ctx)) {
|
|
4512
|
+
modifiers['with'] = parseNakedNamedArgs(ctx, () => ctx.parsePrimary(), 'fetch');
|
|
4357
4513
|
}
|
|
4358
4514
|
else {
|
|
4359
4515
|
modifiers['with'] = ctx.parsePrimary();
|
|
@@ -4388,45 +4544,6 @@ function parseFetchCommand(ctx, commandToken) {
|
|
|
4388
4544
|
}
|
|
4389
4545
|
return builder.build();
|
|
4390
4546
|
}
|
|
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
4547
|
function findJsEndBoundary(ctx, startPos) {
|
|
4431
4548
|
const input = ctx.getInputSlice(startPos);
|
|
4432
4549
|
if (!input) {
|
|
@@ -4546,6 +4663,7 @@ function parseTellCommand(ctx, identifierNode) {
|
|
|
4546
4663
|
const commands = [];
|
|
4547
4664
|
while (!ctx.isAtEnd()) {
|
|
4548
4665
|
if (ctx.checkIsCommand()) {
|
|
4666
|
+
const word = ctx.peek().value;
|
|
4549
4667
|
try {
|
|
4550
4668
|
ctx.advance();
|
|
4551
4669
|
const cmd = ctx.parseCommand();
|
|
@@ -4556,7 +4674,8 @@ function parseTellCommand(ctx, identifierNode) {
|
|
|
4556
4674
|
break;
|
|
4557
4675
|
}
|
|
4558
4676
|
}
|
|
4559
|
-
catch {
|
|
4677
|
+
catch (error) {
|
|
4678
|
+
ctx.addError(`'${word}' in a tell body failed to parse: ${error instanceof Error ? error.message : String(error)}`);
|
|
4560
4679
|
break;
|
|
4561
4680
|
}
|
|
4562
4681
|
if (ctx.match(KEYWORDS.AND) || ctx.match(KEYWORDS.THEN)) {
|
|
@@ -4705,7 +4824,6 @@ const ONE_EXPR = (syntax) => ({
|
|
|
4705
4824
|
markers: [],
|
|
4706
4825
|
syntax,
|
|
4707
4826
|
});
|
|
4708
|
-
const BEEP = ONE_EXPR('beep! [<expression>, \u2026]');
|
|
4709
4827
|
const COMMAND_GRAMMAR = {
|
|
4710
4828
|
append: { positional: 'expression', markers: ['to'], syntax: 'append <value> [to <target>]' },
|
|
4711
4829
|
prepend: { positional: 'expression', markers: ['to'], syntax: 'prepend <value> [to <target>]' },
|
|
@@ -4716,8 +4834,7 @@ const COMMAND_GRAMMAR = {
|
|
|
4716
4834
|
syntax: 'make (a|an) <type> [from <args>] [called <name>]',
|
|
4717
4835
|
},
|
|
4718
4836
|
throw: { positional: 'primary', markers: [], syntax: 'throw <error>' },
|
|
4719
|
-
'beep!'
|
|
4720
|
-
beep: BEEP,
|
|
4837
|
+
beep: ONE_EXPR('beep! [<expression>, \u2026]'),
|
|
4721
4838
|
blur: ONE_EXPR('blur [<target>]'),
|
|
4722
4839
|
break: NONE,
|
|
4723
4840
|
breakpoint: NONE,
|
|
@@ -4741,7 +4858,8 @@ const COMMAND_GRAMMAR = {
|
|
|
4741
4858
|
render: {
|
|
4742
4859
|
positional: 'expression',
|
|
4743
4860
|
markers: ['with'],
|
|
4744
|
-
|
|
4861
|
+
namedArgs: ['with'],
|
|
4862
|
+
syntax: 'render <template> [with <name>: <value>, \u2026]',
|
|
4745
4863
|
},
|
|
4746
4864
|
reset: ONE_EXPR('reset [<form>]'),
|
|
4747
4865
|
return: ONE_EXPR('return [<value>]'),
|
|
@@ -5096,7 +5214,10 @@ class Parser {
|
|
|
5096
5214
|
const firstToken = this.peek();
|
|
5097
5215
|
const prefixEntry = Parser.PRATT_TABLE.get(firstToken.value);
|
|
5098
5216
|
let left;
|
|
5099
|
-
if (
|
|
5217
|
+
if (isGluedBeepBang(firstToken, this.tokens[this.current + 1])) {
|
|
5218
|
+
left = this.parseBeepExpression();
|
|
5219
|
+
}
|
|
5220
|
+
else if (prefixEntry?.prefix) {
|
|
5100
5221
|
const token = this.advance();
|
|
5101
5222
|
if (this.isAtEnd()) {
|
|
5102
5223
|
this.addError(`Expected expression after '${token.value}' operator`);
|
|
@@ -5116,6 +5237,9 @@ class Parser {
|
|
|
5116
5237
|
continue;
|
|
5117
5238
|
}
|
|
5118
5239
|
}
|
|
5240
|
+
if (nextToken.value === '*' && this.isSpacedStyleRef(this.current)) {
|
|
5241
|
+
break;
|
|
5242
|
+
}
|
|
5119
5243
|
const infixEntry = Parser.PRATT_TABLE.get(nextToken.value);
|
|
5120
5244
|
if (!infixEntry?.infix) {
|
|
5121
5245
|
if (STOP_TOKENS.has(nextToken.value) || STOP_DELIMITERS.has(nextToken.value)) {
|
|
@@ -5177,6 +5301,35 @@ class Parser {
|
|
|
5177
5301
|
this.prattCache.set(cacheKey, { node: left, endPos: this.current });
|
|
5178
5302
|
return left;
|
|
5179
5303
|
}
|
|
5304
|
+
parseBeepExpression() {
|
|
5305
|
+
const beepToken = this.advance();
|
|
5306
|
+
this.advance();
|
|
5307
|
+
if (this.isAtEnd()) {
|
|
5308
|
+
this.addError("Expected expression after 'beep!'");
|
|
5309
|
+
return this.createErrorNode();
|
|
5310
|
+
}
|
|
5311
|
+
const operand = this.parseExpressionPratt(Parser.BEEP_OPERAND_BP);
|
|
5312
|
+
return {
|
|
5313
|
+
type: 'unaryExpression',
|
|
5314
|
+
operator: 'beep!',
|
|
5315
|
+
operand,
|
|
5316
|
+
argument: operand,
|
|
5317
|
+
prefix: true,
|
|
5318
|
+
start: beepToken.start,
|
|
5319
|
+
end: this.previous().end,
|
|
5320
|
+
line: beepToken.line,
|
|
5321
|
+
column: beepToken.column,
|
|
5322
|
+
};
|
|
5323
|
+
}
|
|
5324
|
+
isStyleRefStar(i) {
|
|
5325
|
+
const star = this.tokens[i];
|
|
5326
|
+
const name = this.tokens[i + 1];
|
|
5327
|
+
return star?.value === '*' && !!name && name.start === star.end && isIdentifierLike(name);
|
|
5328
|
+
}
|
|
5329
|
+
isSpacedStyleRef(i) {
|
|
5330
|
+
const prev = this.tokens[i - 1];
|
|
5331
|
+
return this.isStyleRefStar(i) && !!prev && prev.end < this.tokens[i].start;
|
|
5332
|
+
}
|
|
5180
5333
|
tryParseStringPostfix(left, nextToken) {
|
|
5181
5334
|
const v = nextToken.value;
|
|
5182
5335
|
let unit = null;
|
|
@@ -5379,6 +5532,10 @@ class Parser {
|
|
|
5379
5532
|
const isStop = () => this.check('end') || extraStops.some(s => this.check(s));
|
|
5380
5533
|
debug.parse('\ud83d\udd04 parseCommandListUntilTerminator: Starting (extraStops:', extraStops.join(','), ')');
|
|
5381
5534
|
while (!this.isAtEnd() && !isStop()) {
|
|
5535
|
+
if (this.checkComment()) {
|
|
5536
|
+
this.advance();
|
|
5537
|
+
continue;
|
|
5538
|
+
}
|
|
5382
5539
|
debug.parse('\ud83d\udccd Loop iteration, current token:', this.peek().value, 'kind:', this.peek().kind);
|
|
5383
5540
|
let parsedCommand = false;
|
|
5384
5541
|
const isCommandToken = this.checkIsCommand();
|
|
@@ -5462,7 +5619,7 @@ class Parser {
|
|
|
5462
5619
|
debug.parse('\u2705 Found "end", consuming it');
|
|
5463
5620
|
this.advance();
|
|
5464
5621
|
}
|
|
5465
|
-
else {
|
|
5622
|
+
else if (!this.atEndOfInput()) {
|
|
5466
5623
|
debug.parse('\u274c ERROR: Expected "end" but got:', this.peek().value, 'at position:', this.peek().start);
|
|
5467
5624
|
throw new Error(`Expected "end" to close ${construct} block`);
|
|
5468
5625
|
}
|
|
@@ -5475,7 +5632,7 @@ class Parser {
|
|
|
5475
5632
|
if (this.check('end')) {
|
|
5476
5633
|
this.advance();
|
|
5477
5634
|
}
|
|
5478
|
-
else {
|
|
5635
|
+
else if (!this.atEndOfInput()) {
|
|
5479
5636
|
throw new Error('Expected "end" to close repeat block');
|
|
5480
5637
|
}
|
|
5481
5638
|
}
|
|
@@ -5491,7 +5648,10 @@ class Parser {
|
|
|
5491
5648
|
this.advance();
|
|
5492
5649
|
}
|
|
5493
5650
|
else if (terminator === '') {
|
|
5494
|
-
|
|
5651
|
+
if (!this.atEndOfInput()) {
|
|
5652
|
+
throw new Error('Expected "end", "else", "until", or "while" to close repeat block');
|
|
5653
|
+
}
|
|
5654
|
+
return { commands, terminator: 'end' };
|
|
5495
5655
|
}
|
|
5496
5656
|
return { commands, terminator };
|
|
5497
5657
|
}
|
|
@@ -6075,7 +6235,9 @@ class Parser {
|
|
|
6075
6235
|
parseTopLevelInitBlock() {
|
|
6076
6236
|
const pos = this.getPosition();
|
|
6077
6237
|
const initCommands = this.parseCommandBlock(['end']);
|
|
6078
|
-
this.
|
|
6238
|
+
if (!this.match('end') && !this.atEndOfInput() && !this.checkFeatureStart()) {
|
|
6239
|
+
this.consume('end', "Expected 'end' after init block");
|
|
6240
|
+
}
|
|
6079
6241
|
return {
|
|
6080
6242
|
type: 'initBlock',
|
|
6081
6243
|
commands: initCommands,
|
|
@@ -6251,10 +6413,29 @@ class Parser {
|
|
|
6251
6413
|
...(sourceExpr.column !== undefined && { column: sourceExpr.column }),
|
|
6252
6414
|
};
|
|
6253
6415
|
}
|
|
6416
|
+
isArgumentListOnly(open) {
|
|
6417
|
+
let depth = 0;
|
|
6418
|
+
for (let i = open; i < this.tokens.length; i++) {
|
|
6419
|
+
const token = this.tokens[i];
|
|
6420
|
+
if (token.kind !== 'operator')
|
|
6421
|
+
continue;
|
|
6422
|
+
if (token.value === '(' || token.value === '[' || token.value === '{')
|
|
6423
|
+
depth++;
|
|
6424
|
+
else if (token.value === ')' || token.value === ']' || token.value === '}') {
|
|
6425
|
+
if (--depth === 0)
|
|
6426
|
+
return i === open + 1;
|
|
6427
|
+
}
|
|
6428
|
+
else if (token.value === ',' && depth === 1)
|
|
6429
|
+
return true;
|
|
6430
|
+
}
|
|
6431
|
+
return false;
|
|
6432
|
+
}
|
|
6254
6433
|
tryParsePseudoCommand(callExpr, sourceExpr) {
|
|
6255
6434
|
const methodName = callExpr.callee.name;
|
|
6256
6435
|
const nextToken = this.peek();
|
|
6257
|
-
const
|
|
6436
|
+
const nextIsArticle = nextToken.value.toLowerCase() === 'the';
|
|
6437
|
+
const hasPseudoCommandPattern = nextIsArticle ||
|
|
6438
|
+
Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(nextToken.value.toLowerCase()) ||
|
|
6258
6439
|
(isIdentifier(nextToken) && !this.isCommand(nextToken.value)) ||
|
|
6259
6440
|
isContextVar(nextToken);
|
|
6260
6441
|
if (!hasPseudoCommandPattern) {
|
|
@@ -6280,7 +6461,10 @@ class Parser {
|
|
|
6280
6461
|
});
|
|
6281
6462
|
}
|
|
6282
6463
|
let preposition;
|
|
6283
|
-
if (
|
|
6464
|
+
if (nextIsArticle) {
|
|
6465
|
+
this.advance();
|
|
6466
|
+
}
|
|
6467
|
+
else if (Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(this.peek().value.toLowerCase())) {
|
|
6284
6468
|
preposition = this.advance().value.toLowerCase();
|
|
6285
6469
|
}
|
|
6286
6470
|
let targetExpr;
|
|
@@ -6444,10 +6628,22 @@ class Parser {
|
|
|
6444
6628
|
this.consume(']', "Expected ']' after event condition");
|
|
6445
6629
|
}
|
|
6446
6630
|
let target;
|
|
6631
|
+
let targetExpression;
|
|
6447
6632
|
if (this.match('from')) {
|
|
6448
|
-
|
|
6449
|
-
|
|
6450
|
-
|
|
6633
|
+
if (this.check('the'))
|
|
6634
|
+
this.advance();
|
|
6635
|
+
if (Parser.POSITIONAL_SOURCE_HEADS.has(this.peek().value.toLowerCase())) {
|
|
6636
|
+
const startIndex = this.current;
|
|
6637
|
+
targetExpression = this.parseExpression();
|
|
6638
|
+
target = this.tokens
|
|
6639
|
+
.slice(startIndex, this.current)
|
|
6640
|
+
.map(t => t.value)
|
|
6641
|
+
.join(' ');
|
|
6642
|
+
}
|
|
6643
|
+
else {
|
|
6644
|
+
target = this.advance().value;
|
|
6645
|
+
}
|
|
6646
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Parsed 'from' target: ${target}`);
|
|
6451
6647
|
this.parseTemporalKeywordModifiers(modifiers);
|
|
6452
6648
|
}
|
|
6453
6649
|
let attributeName;
|
|
@@ -6493,7 +6689,7 @@ class Parser {
|
|
|
6493
6689
|
const nextIsOpenParen = this.tokens[this.current + 1]?.value === '(';
|
|
6494
6690
|
const commandName = this.peek().value.toLowerCase();
|
|
6495
6691
|
const isSpecialBodyCommand = commandName === 'js' || commandName === 'tell';
|
|
6496
|
-
if (nextIsOpenParen && !isSpecialBodyCommand) {
|
|
6692
|
+
if (nextIsOpenParen && !isSpecialBodyCommand && this.isArgumentListOnly(this.current + 1)) {
|
|
6497
6693
|
let expr;
|
|
6498
6694
|
const savedError = this.error;
|
|
6499
6695
|
try {
|
|
@@ -6525,7 +6721,7 @@ class Parser {
|
|
|
6525
6721
|
}
|
|
6526
6722
|
}
|
|
6527
6723
|
}
|
|
6528
|
-
else if (this.checkIdentifier()) {
|
|
6724
|
+
else if (this.checkIdentifier() || this.checkEventNamedCall()) {
|
|
6529
6725
|
const token = this.peek();
|
|
6530
6726
|
if (this.isCommand(token.value)) {
|
|
6531
6727
|
const cmd = this.parseCommandWithErrorRecovery();
|
|
@@ -6634,6 +6830,7 @@ class Parser {
|
|
|
6634
6830
|
...(eventParams.length > 0 && { args: eventParams }),
|
|
6635
6831
|
...(condition && { condition: fromLegacyExpression(condition) }),
|
|
6636
6832
|
...(target && { target }),
|
|
6833
|
+
...(targetExpression && { targetExpression: fromLegacyExpression(targetExpression) }),
|
|
6637
6834
|
...(attributeName && { attributeName }),
|
|
6638
6835
|
...(watchTarget && { watchTarget: fromLegacyExpression(watchTarget) }),
|
|
6639
6836
|
...(customEventSource && { customEventSource }),
|
|
@@ -6889,10 +7086,9 @@ class Parser {
|
|
|
6889
7086
|
}
|
|
6890
7087
|
parseCommandCore() {
|
|
6891
7088
|
const commandToken = this.previous();
|
|
6892
|
-
|
|
7089
|
+
const commandName = commandToken.value;
|
|
6893
7090
|
if (commandName === 'beep' && this.check('!')) {
|
|
6894
7091
|
this.advance();
|
|
6895
|
-
commandName = 'beep!';
|
|
6896
7092
|
}
|
|
6897
7093
|
if (commandName === 'fetch') {
|
|
6898
7094
|
return parseFetchCommand(this.getContext(), commandToken);
|
|
@@ -6991,6 +7187,9 @@ class Parser {
|
|
|
6991
7187
|
args.push(this.parsePrimary());
|
|
6992
7188
|
}
|
|
6993
7189
|
const callNode = this.createCallExpression(createIdentifier(funcName, namePos), args);
|
|
7190
|
+
if (funcName === 'closest' && args.length > 0 && this.match('to')) {
|
|
7191
|
+
callNode.closestTo = this.parseExpression();
|
|
7192
|
+
}
|
|
6994
7193
|
if (funcName === 'next' || funcName === 'previous') {
|
|
6995
7194
|
const relativePositional = this.parseRelativePositionalModifiers();
|
|
6996
7195
|
if (relativePositional) {
|
|
@@ -7030,6 +7229,9 @@ class Parser {
|
|
|
7030
7229
|
}
|
|
7031
7230
|
parseTheXofY() {
|
|
7032
7231
|
const positionalKeywords = ['first', 'last', 'next', 'previous', 'random', 'closest'];
|
|
7232
|
+
if (this.isStyleRefStar(this.current)) {
|
|
7233
|
+
return this.parsePrimary();
|
|
7234
|
+
}
|
|
7033
7235
|
if (!this.checkIdentifier()) {
|
|
7034
7236
|
return this.createIdentifier('the');
|
|
7035
7237
|
}
|
|
@@ -7045,7 +7247,7 @@ class Parser {
|
|
|
7045
7247
|
return this.createIdentifier(propertyName);
|
|
7046
7248
|
}
|
|
7047
7249
|
this.advance();
|
|
7048
|
-
const target = this.
|
|
7250
|
+
const target = parseOfOperand(this.makePrattContext(), THE_OF_TARGET_BP);
|
|
7049
7251
|
return {
|
|
7050
7252
|
type: 'propertyOfExpression',
|
|
7051
7253
|
property: { type: 'identifier', name: propertyName },
|
|
@@ -7243,6 +7445,13 @@ class Parser {
|
|
|
7243
7445
|
checkLiteral() {
|
|
7244
7446
|
return this.checkPredicate(isLiteral);
|
|
7245
7447
|
}
|
|
7448
|
+
checkEventNamedCall() {
|
|
7449
|
+
if (this.isAtEnd() || !this.checkIdentifierLike() || this.checkIsCommand())
|
|
7450
|
+
return false;
|
|
7451
|
+
const token = this.peek();
|
|
7452
|
+
const next = this.tokens[this.current + 1];
|
|
7453
|
+
return next?.value === '(' && next.start === token.end;
|
|
7454
|
+
}
|
|
7246
7455
|
checkIdentifierLike() {
|
|
7247
7456
|
return this.checkPredicate(isIdentifierLike);
|
|
7248
7457
|
}
|
|
@@ -7351,6 +7560,22 @@ class Parser {
|
|
|
7351
7560
|
isAtEnd() {
|
|
7352
7561
|
return this.current >= this.tokens.length;
|
|
7353
7562
|
}
|
|
7563
|
+
atEndOfInput() {
|
|
7564
|
+
for (let i = this.current; i < this.tokens.length; i++) {
|
|
7565
|
+
if (!isComment(this.tokens[i]))
|
|
7566
|
+
return false;
|
|
7567
|
+
}
|
|
7568
|
+
return true;
|
|
7569
|
+
}
|
|
7570
|
+
checkFeatureStart() {
|
|
7571
|
+
if (this.isAtEnd())
|
|
7572
|
+
return false;
|
|
7573
|
+
const value = this.peek().value;
|
|
7574
|
+
return (value === 'init' ||
|
|
7575
|
+
value === 'on' ||
|
|
7576
|
+
value === 'def' ||
|
|
7577
|
+
getRegisteredFeature(value) !== undefined);
|
|
7578
|
+
}
|
|
7354
7579
|
peek() {
|
|
7355
7580
|
if (this.isAtEnd()) {
|
|
7356
7581
|
return { kind: 'unknown', value: '', start: 0, end: 0, line: 1, column: 1 };
|
|
@@ -7562,6 +7787,7 @@ class Parser {
|
|
|
7562
7787
|
match: this.match.bind(this),
|
|
7563
7788
|
matchOperator: this.matchOperator.bind(this),
|
|
7564
7789
|
isAtEnd: this.isAtEnd.bind(this),
|
|
7790
|
+
atEndOfInput: this.atEndOfInput.bind(this),
|
|
7565
7791
|
createIdentifier: this.createIdentifier.bind(this),
|
|
7566
7792
|
parseExpression: this.parseExpression.bind(this),
|
|
7567
7793
|
parsePrimary: this.parsePrimary.bind(this),
|
|
@@ -7612,7 +7838,15 @@ Parser.POSTFIX_UNARY_OPERATORS = new Set([
|
|
|
7612
7838
|
'ignoring case',
|
|
7613
7839
|
]);
|
|
7614
7840
|
Parser.PRATT_TABLE = PARSER_TABLE;
|
|
7841
|
+
Parser.BEEP_OPERAND_BP = 80;
|
|
7615
7842
|
Parser.PSEUDO_COMMAND_PREPOSITIONS = ['from', 'on', 'with', 'into', 'at', 'to'];
|
|
7843
|
+
Parser.POSITIONAL_SOURCE_HEADS = new Set([
|
|
7844
|
+
'closest',
|
|
7845
|
+
'next',
|
|
7846
|
+
'previous',
|
|
7847
|
+
'first',
|
|
7848
|
+
'last',
|
|
7849
|
+
]);
|
|
7616
7850
|
|
|
7617
7851
|
class FullParserImpl {
|
|
7618
7852
|
constructor() {
|