@hyperfixi/core 2.9.0 → 2.9.2
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/dist/ast-utils/index.js +905 -882
- package/dist/ast-utils/index.mjs +905 -882
- package/dist/bundle-generator/index.js +6 -0
- package/dist/bundle-generator/index.mjs +6 -0
- package/dist/bundle-generator/parser-templates.d.ts +1 -1
- package/dist/chunks/{bridge-D9JLmPkk.js → bridge-DwtNLKK3.js} +2 -2
- package/dist/chunks/browser-modular-6HT1r2AH.js +2 -0
- package/dist/chunks/feature-eventsource-Bxg_UNZo.js +2 -0
- package/dist/chunks/feature-sockets-CcTxD5sT.js +2 -0
- package/dist/chunks/feature-webworker-DoqZ0Grg.js +2 -0
- package/dist/chunks/{index-6DUg7Qjm.js → index-B8jwvuj_.js} +2 -2
- package/dist/commands/index.js +299 -184
- package/dist/commands/index.mjs +299 -184
- package/dist/hyperfixi-browser-classic-i18n.js +1 -1
- package/dist/hyperfixi-browser-minimal.js +1 -1
- package/dist/hyperfixi-browser-standard.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-minimal.js +1 -1
- package/dist/hyperfixi-multilingual.js +1 -1
- package/dist/hyperfixi-standard.js +1 -1
- package/dist/hyperfixi.js +1 -1
- package/dist/hyperfixi.mjs +1 -1
- package/dist/index.js +1731 -1474
- package/dist/index.min.js +1 -1
- package/dist/index.mjs +1731 -1474
- package/dist/lokascript-browser-classic-i18n.js +1 -1
- package/dist/lokascript-browser-minimal.js +1 -1
- package/dist/lokascript-browser-standard.js +1 -1
- package/dist/lokascript-browser.js +1 -1
- package/dist/lokascript-hybrid-complete.js +1 -1
- package/dist/lokascript-hybrid-hx.js +1 -1
- package/dist/lokascript-multilingual.js +1 -1
- package/dist/lsp-metadata.js +5 -5
- package/dist/lsp-metadata.mjs +5 -5
- package/dist/metadata.js +25 -25
- package/dist/metadata.mjs +25 -25
- package/dist/multilingual/index.js +17 -17
- package/dist/multilingual/index.mjs +17 -17
- package/dist/parser/command-parsers/utility-commands.d.ts +2 -1
- package/dist/parser/full-parser.js +286 -178
- package/dist/parser/full-parser.mjs +286 -178
- package/dist/parser/hybrid/index.js +3 -0
- package/dist/parser/hybrid/index.mjs +3 -0
- package/dist/parser/hybrid/parser-core.js +3 -0
- package/dist/parser/hybrid/parser-core.mjs +3 -0
- package/dist/parser/hybrid-parser.js +3 -0
- package/dist/parser/hybrid-parser.mjs +3 -0
- package/dist/parser/parser.d.ts +1 -0
- package/dist/reference/index.js +2 -0
- package/dist/reference/index.mjs +2 -0
- package/dist/registry/index.js +286 -178
- package/dist/registry/index.mjs +286 -178
- package/dist/runtime/runtime-base.d.ts +2 -1
- package/dist/types/base-types.d.ts +3 -0
- package/dist/types/core.d.ts +1 -0
- package/package.json +6 -6
- package/dist/chunks/browser-modular-CPiVQXM0.js +0 -2
- package/dist/chunks/feature-eventsource-BpZvPy_K.js +0 -2
- package/dist/chunks/feature-sockets-CrYvjZ4j.js +0 -2
- package/dist/chunks/feature-webworker-BSYguEIW.js +0 -2
package/dist/commands/index.js
CHANGED
|
@@ -153,7 +153,7 @@ function resolveElement(target, context, commandName) {
|
|
|
153
153
|
if (!target) {
|
|
154
154
|
const me = context.me;
|
|
155
155
|
if (!me) {
|
|
156
|
-
throw new Error(withCmd(commandName, 'No target element
|
|
156
|
+
throw new Error(withCmd(commandName, 'No target element \u2014 provide explicit target or ensure context.me is set'));
|
|
157
157
|
}
|
|
158
158
|
return asHTMLElement(me);
|
|
159
159
|
}
|
|
@@ -179,7 +179,7 @@ function resolveElement(target, context, commandName) {
|
|
|
179
179
|
}
|
|
180
180
|
const doc = context.me?.ownerDocument ?? (typeof document !== 'undefined' ? document : null);
|
|
181
181
|
if (!doc) {
|
|
182
|
-
throw new Error(withCmd(commandName, 'DOM not available
|
|
182
|
+
throw new Error(withCmd(commandName, 'DOM not available \u2014 cannot resolve element selector'));
|
|
183
183
|
}
|
|
184
184
|
const element = doc.querySelector(trimmed);
|
|
185
185
|
if (!element) {
|
|
@@ -635,39 +635,39 @@ const DEBUG = {
|
|
|
635
635
|
const debug = {
|
|
636
636
|
command: (...args) => {
|
|
637
637
|
if (DEBUG.commands)
|
|
638
|
-
console.log('
|
|
638
|
+
console.log('\ud83d\udd27', ...args);
|
|
639
639
|
},
|
|
640
640
|
event: (...args) => {
|
|
641
641
|
if (DEBUG.events)
|
|
642
|
-
console.log('
|
|
642
|
+
console.log('\ud83c\udfaf', ...args);
|
|
643
643
|
},
|
|
644
644
|
parse: (...args) => {
|
|
645
645
|
if (DEBUG.parsing)
|
|
646
|
-
console.log('
|
|
646
|
+
console.log('\ud83d\udcdd', ...args);
|
|
647
647
|
},
|
|
648
648
|
expr: (...args) => {
|
|
649
649
|
if (DEBUG.expressions)
|
|
650
|
-
console.log('
|
|
650
|
+
console.log('\ud83d\udd0d', ...args);
|
|
651
651
|
},
|
|
652
652
|
expressions: (...args) => {
|
|
653
653
|
if (DEBUG.expressions)
|
|
654
|
-
console.log('
|
|
654
|
+
console.log('\ud83d\udd0d', ...args);
|
|
655
655
|
},
|
|
656
656
|
style: (...args) => {
|
|
657
657
|
if (DEBUG.styles)
|
|
658
|
-
console.log('
|
|
658
|
+
console.log('\ud83c\udfa8', ...args);
|
|
659
659
|
},
|
|
660
660
|
runtime: (...args) => {
|
|
661
661
|
if (DEBUG.runtime)
|
|
662
|
-
console.log('
|
|
662
|
+
console.log('\ud83d\ude80', ...args);
|
|
663
663
|
},
|
|
664
664
|
loop: (...args) => {
|
|
665
665
|
if (DEBUG.loops)
|
|
666
|
-
console.log('
|
|
666
|
+
console.log('\ud83d\udd01', ...args);
|
|
667
667
|
},
|
|
668
668
|
async: (...args) => {
|
|
669
669
|
if (DEBUG.async)
|
|
670
|
-
console.log('
|
|
670
|
+
console.log('\u23f3', ...args);
|
|
671
671
|
},
|
|
672
672
|
};
|
|
673
673
|
|
|
@@ -6220,6 +6220,7 @@ function tokenize(input) {
|
|
|
6220
6220
|
const prevToken = tokenizer.tokens[tokenizer.tokens.length - 1];
|
|
6221
6221
|
const isPossessive = nextChar === 's' &&
|
|
6222
6222
|
prevToken &&
|
|
6223
|
+
!/\s/.test(tokenizer.input[tokenizer.position - 1] ?? ' ') &&
|
|
6223
6224
|
(prevToken.kind === TokenKind.IDENTIFIER ||
|
|
6224
6225
|
prevToken.kind === TokenKind.SELECTOR ||
|
|
6225
6226
|
prevToken.value === ']' ||
|
|
@@ -6311,13 +6312,13 @@ function tokenize(input) {
|
|
|
6311
6312
|
continue;
|
|
6312
6313
|
}
|
|
6313
6314
|
if (char === '{') {
|
|
6314
|
-
addToken(tokenizer, TokenKind.OPERATOR, '{');
|
|
6315
6315
|
advance(tokenizer);
|
|
6316
|
+
addToken(tokenizer, TokenKind.OPERATOR, '{');
|
|
6316
6317
|
continue;
|
|
6317
6318
|
}
|
|
6318
6319
|
if (char === '}') {
|
|
6319
|
-
addToken(tokenizer, TokenKind.OPERATOR, '}');
|
|
6320
6320
|
advance(tokenizer);
|
|
6321
|
+
addToken(tokenizer, TokenKind.OPERATOR, '}');
|
|
6321
6322
|
continue;
|
|
6322
6323
|
}
|
|
6323
6324
|
if (char === '[') {
|
|
@@ -6333,24 +6334,24 @@ function tokenize(input) {
|
|
|
6333
6334
|
prevToken.value === ']') &&
|
|
6334
6335
|
!isEventCondition;
|
|
6335
6336
|
if (isMemberAccess) {
|
|
6336
|
-
addToken(tokenizer, TokenKind.OPERATOR, '[');
|
|
6337
6337
|
advance(tokenizer);
|
|
6338
|
+
addToken(tokenizer, TokenKind.OPERATOR, '[');
|
|
6338
6339
|
}
|
|
6339
6340
|
else {
|
|
6340
6341
|
if (isEventCondition) {
|
|
6341
|
-
addToken(tokenizer, TokenKind.SYMBOL, '[');
|
|
6342
6342
|
advance(tokenizer);
|
|
6343
|
+
addToken(tokenizer, TokenKind.SYMBOL, '[');
|
|
6343
6344
|
}
|
|
6344
6345
|
else {
|
|
6345
|
-
addToken(tokenizer, TokenKind.OPERATOR, '[');
|
|
6346
6346
|
advance(tokenizer);
|
|
6347
|
+
addToken(tokenizer, TokenKind.OPERATOR, '[');
|
|
6347
6348
|
}
|
|
6348
6349
|
}
|
|
6349
6350
|
continue;
|
|
6350
6351
|
}
|
|
6351
6352
|
if (char === ']') {
|
|
6352
|
-
addToken(tokenizer, TokenKind.OPERATOR, ']');
|
|
6353
6353
|
advance(tokenizer);
|
|
6354
|
+
addToken(tokenizer, TokenKind.OPERATOR, ']');
|
|
6354
6355
|
continue;
|
|
6355
6356
|
}
|
|
6356
6357
|
if (char === '$') {
|
|
@@ -7944,9 +7945,9 @@ function createPartialCommandNode(name, pos) {
|
|
|
7944
7945
|
};
|
|
7945
7946
|
}
|
|
7946
7947
|
function createProgramNode(statements) {
|
|
7947
|
-
debug.parse(
|
|
7948
|
+
debug.parse(`\u2705 createProgramNode: Called with ${statements.length} statements`);
|
|
7948
7949
|
if (statements.length === 0) {
|
|
7949
|
-
debug.parse(
|
|
7950
|
+
debug.parse(`\u2705 createProgramNode: Returning error node (0 statements)`);
|
|
7950
7951
|
return {
|
|
7951
7952
|
type: 'identifier',
|
|
7952
7953
|
name: '__ERROR__',
|
|
@@ -7957,7 +7958,7 @@ function createProgramNode(statements) {
|
|
|
7957
7958
|
};
|
|
7958
7959
|
}
|
|
7959
7960
|
if (statements.length === 1) {
|
|
7960
|
-
debug.parse(
|
|
7961
|
+
debug.parse(`\u2705 createProgramNode: Returning single statement (type=${statements[0].type})`);
|
|
7961
7962
|
return statements[0];
|
|
7962
7963
|
}
|
|
7963
7964
|
const programNode = {
|
|
@@ -7968,7 +7969,7 @@ function createProgramNode(statements) {
|
|
|
7968
7969
|
line: statements[0]?.line || 1,
|
|
7969
7970
|
column: statements[0]?.column || 1,
|
|
7970
7971
|
};
|
|
7971
|
-
debug.parse(
|
|
7972
|
+
debug.parse(`\u2705 createProgramNode: Returning Program node with ${statements.length} statements, type=${programNode.type}`);
|
|
7972
7973
|
return programNode;
|
|
7973
7974
|
}
|
|
7974
7975
|
|
|
@@ -7979,7 +7980,6 @@ const MULTI_WORD_PATTERNS = [
|
|
|
7979
7980
|
keywords: ['a', 'an', 'from', 'called'],
|
|
7980
7981
|
syntax: 'make (a|an) <type> [from <args>] [called <name>]',
|
|
7981
7982
|
},
|
|
7982
|
-
{ command: 'send', keywords: ['to'], syntax: 'send <event> to <target>' },
|
|
7983
7983
|
{ command: 'throw', keywords: [], syntax: 'throw <error>' },
|
|
7984
7984
|
];
|
|
7985
7985
|
function getMultiWordPattern(commandName) {
|
|
@@ -8280,7 +8280,13 @@ function parseTriggerCommand(ctx, identifierNode) {
|
|
|
8280
8280
|
finalArgs.push(ctx.createIdentifier(keyword));
|
|
8281
8281
|
continue;
|
|
8282
8282
|
}
|
|
8283
|
-
|
|
8283
|
+
const before = ctx.savePosition();
|
|
8284
|
+
const expr = ctx.parsePrimary();
|
|
8285
|
+
if (expr)
|
|
8286
|
+
finalArgs.push(expr);
|
|
8287
|
+
if (ctx.savePosition() === before) {
|
|
8288
|
+
ctx.advance();
|
|
8289
|
+
}
|
|
8284
8290
|
}
|
|
8285
8291
|
return CommandNodeBuilder.fromIdentifier(identifierNode)
|
|
8286
8292
|
.withArgs(...finalArgs)
|
|
@@ -8357,37 +8363,37 @@ function parseRepeatCommand(ctx, commandToken) {
|
|
|
8357
8363
|
ctx.advance();
|
|
8358
8364
|
loopType = 'until-event';
|
|
8359
8365
|
const eventToken = ctx.peek();
|
|
8360
|
-
debug.parse('
|
|
8366
|
+
debug.parse('\ud83d\udccd Parsing event name, current token:', {
|
|
8361
8367
|
value: eventToken.value,
|
|
8362
8368
|
kind: eventToken.kind,
|
|
8363
8369
|
});
|
|
8364
8370
|
if (isIdentifierLike(eventToken) || isEvent(eventToken)) {
|
|
8365
8371
|
eventName = eventToken.value;
|
|
8366
8372
|
ctx.advance();
|
|
8367
|
-
debug.parse('
|
|
8373
|
+
debug.parse('\u2705 Got event name:', eventName, 'Next token:', ctx.peek().value);
|
|
8368
8374
|
}
|
|
8369
8375
|
else {
|
|
8370
8376
|
throw new Error('Expected event name after "event"');
|
|
8371
8377
|
}
|
|
8372
|
-
debug.parse('
|
|
8378
|
+
debug.parse('\ud83d\udd0d Checking for "from", current token:', ctx.peek().value);
|
|
8373
8379
|
if (ctx.check(KEYWORDS$1.FROM)) {
|
|
8374
|
-
debug.parse('
|
|
8380
|
+
debug.parse('\u2705 Found "from", advancing...');
|
|
8375
8381
|
ctx.advance();
|
|
8376
|
-
debug.parse('
|
|
8382
|
+
debug.parse('\ud83d\udccd After consuming "from", current token:', ctx.peek().value);
|
|
8377
8383
|
if (consumeOptionalKeyword(ctx, KEYWORDS$1.THE)) {
|
|
8378
|
-
debug.parse('
|
|
8384
|
+
debug.parse('\u2705 Found "the", advancing...');
|
|
8379
8385
|
}
|
|
8380
8386
|
const beforePrimary = ctx.peek();
|
|
8381
|
-
debug.parse('
|
|
8387
|
+
debug.parse('\ud83d\udd0d Before parsePrimary for event target:', {
|
|
8382
8388
|
value: beforePrimary.value,
|
|
8383
8389
|
kind: beforePrimary.kind,
|
|
8384
8390
|
position: beforePrimary.start,
|
|
8385
8391
|
});
|
|
8386
8392
|
eventTarget = ctx.parsePrimary();
|
|
8387
|
-
debug.parse('
|
|
8393
|
+
debug.parse('\u2705 After parsePrimary, eventTarget:', eventTarget);
|
|
8388
8394
|
}
|
|
8389
8395
|
else {
|
|
8390
|
-
debug.parse('
|
|
8396
|
+
debug.parse('\u274c No "from" found, skipping target parsing');
|
|
8391
8397
|
}
|
|
8392
8398
|
}
|
|
8393
8399
|
else {
|
|
@@ -8499,11 +8505,64 @@ function parseRepeatCommand(ctx, commandToken) {
|
|
|
8499
8505
|
}
|
|
8500
8506
|
return builder.endingAt(ctx.getPosition()).build();
|
|
8501
8507
|
}
|
|
8508
|
+
function parseIfBranchCommands(ctx, stopAtElse) {
|
|
8509
|
+
const commands = [];
|
|
8510
|
+
const atTerminator = () => ctx.check(KEYWORDS$1.END) || (stopAtElse && ctx.check(KEYWORDS$1.ELSE));
|
|
8511
|
+
while (!ctx.isAtEnd() && !atTerminator()) {
|
|
8512
|
+
if (isComment(ctx.peek())) {
|
|
8513
|
+
ctx.advance();
|
|
8514
|
+
continue;
|
|
8515
|
+
}
|
|
8516
|
+
if (!ctx.checkIsCommand() && !ctx.isCommand(ctx.peek().value))
|
|
8517
|
+
break;
|
|
8518
|
+
ctx.advance();
|
|
8519
|
+
const cmd = ctx.parseCommand();
|
|
8520
|
+
if (cmd) {
|
|
8521
|
+
commands.push(cmd);
|
|
8522
|
+
}
|
|
8523
|
+
if (ctx.check(KEYWORDS$1.THEN) || ctx.check(KEYWORDS$1.AND) || ctx.check(',')) {
|
|
8524
|
+
ctx.advance();
|
|
8525
|
+
}
|
|
8526
|
+
}
|
|
8527
|
+
return commands;
|
|
8528
|
+
}
|
|
8529
|
+
const OPERAND_INTRODUCERS = new Set([
|
|
8530
|
+
'is',
|
|
8531
|
+
'am',
|
|
8532
|
+
'are',
|
|
8533
|
+
'not',
|
|
8534
|
+
'no',
|
|
8535
|
+
'and',
|
|
8536
|
+
'or',
|
|
8537
|
+
'==',
|
|
8538
|
+
'===',
|
|
8539
|
+
'!=',
|
|
8540
|
+
'!==',
|
|
8541
|
+
'<',
|
|
8542
|
+
'>',
|
|
8543
|
+
'<=',
|
|
8544
|
+
'>=',
|
|
8545
|
+
'+',
|
|
8546
|
+
'-',
|
|
8547
|
+
'*',
|
|
8548
|
+
'/',
|
|
8549
|
+
'mod',
|
|
8550
|
+
]);
|
|
8551
|
+
function isBodyCommandStart(ctx, prevToken, token) {
|
|
8552
|
+
if (prevToken === null) {
|
|
8553
|
+
return false;
|
|
8554
|
+
}
|
|
8555
|
+
if (OPERAND_INTRODUCERS.has(prevToken.value?.toLowerCase?.() ?? '')) {
|
|
8556
|
+
return false;
|
|
8557
|
+
}
|
|
8558
|
+
return ctx.checkIsCommand() || ctx.isCommand(token.value?.toLowerCase());
|
|
8559
|
+
}
|
|
8502
8560
|
function parseIfCommand(ctx, commandToken) {
|
|
8503
8561
|
const args = [];
|
|
8504
8562
|
let hasThen = false;
|
|
8505
8563
|
const savedPosForThen = ctx.savePosition();
|
|
8506
8564
|
const maxThenLookahead = 500;
|
|
8565
|
+
let prevForThen = null;
|
|
8507
8566
|
for (let i = 0; i < maxThenLookahead && !ctx.isAtEnd(); i++) {
|
|
8508
8567
|
const token = ctx.peek();
|
|
8509
8568
|
if (token.value === KEYWORDS$1.THEN) {
|
|
@@ -8516,6 +8575,14 @@ function parseIfCommand(ctx, commandToken) {
|
|
|
8516
8575
|
token.value === KEYWORDS$1.ON) {
|
|
8517
8576
|
break;
|
|
8518
8577
|
}
|
|
8578
|
+
if (token.line !== undefined &&
|
|
8579
|
+
token.line !== commandToken.line &&
|
|
8580
|
+
isBodyCommandStart(ctx, prevForThen, token)) {
|
|
8581
|
+
break;
|
|
8582
|
+
}
|
|
8583
|
+
if (!isComment(token)) {
|
|
8584
|
+
prevForThen = token;
|
|
8585
|
+
}
|
|
8519
8586
|
ctx.advance();
|
|
8520
8587
|
}
|
|
8521
8588
|
ctx.restorePosition(savedPosForThen);
|
|
@@ -8524,9 +8591,14 @@ function parseIfCommand(ctx, commandToken) {
|
|
|
8524
8591
|
const savedPosition = ctx.savePosition();
|
|
8525
8592
|
const ifStatementLine = commandToken.line;
|
|
8526
8593
|
const maxLookahead = 100;
|
|
8594
|
+
let firstCommandOnIfLine = false;
|
|
8595
|
+
let prevToken = null;
|
|
8527
8596
|
while (!ctx.isAtEnd() && ctx.current - savedPosition < maxLookahead) {
|
|
8528
8597
|
const token = ctx.peek();
|
|
8529
8598
|
const tokenValue = token.value?.toLowerCase();
|
|
8599
|
+
if (firstCommandOnIfLine && token.line !== undefined && token.line !== ifStatementLine) {
|
|
8600
|
+
break;
|
|
8601
|
+
}
|
|
8530
8602
|
if (tokenValue === KEYWORDS$1.BEHAVIOR ||
|
|
8531
8603
|
tokenValue === KEYWORDS$1.DEF ||
|
|
8532
8604
|
tokenValue === KEYWORDS$1.ON) {
|
|
@@ -8538,11 +8610,15 @@ function parseIfCommand(ctx, commandToken) {
|
|
|
8538
8610
|
}
|
|
8539
8611
|
break;
|
|
8540
8612
|
}
|
|
8541
|
-
if (ctx
|
|
8613
|
+
if (isBodyCommandStart(ctx, prevToken, token)) {
|
|
8542
8614
|
if (token.line !== undefined && token.line !== ifStatementLine) {
|
|
8543
8615
|
hasImplicitMultiLineEnd = true;
|
|
8544
8616
|
break;
|
|
8545
8617
|
}
|
|
8618
|
+
firstCommandOnIfLine = true;
|
|
8619
|
+
}
|
|
8620
|
+
if (!isComment(token)) {
|
|
8621
|
+
prevToken = token;
|
|
8546
8622
|
}
|
|
8547
8623
|
ctx.advance();
|
|
8548
8624
|
}
|
|
@@ -8557,11 +8633,12 @@ function parseIfCommand(ctx, commandToken) {
|
|
|
8557
8633
|
const conditionTokens = [];
|
|
8558
8634
|
const maxIterations = 20;
|
|
8559
8635
|
let iterations = 0;
|
|
8636
|
+
let firstConditionParse = true;
|
|
8560
8637
|
while (!ctx.isAtEnd() &&
|
|
8561
|
-
!ctx.checkIsCommand() &&
|
|
8562
|
-
!ctx.isCommand(ctx.peek().value) &&
|
|
8563
8638
|
!ctx.check(KEYWORDS$1.THEN) &&
|
|
8639
|
+
(firstConditionParse || (!ctx.checkIsCommand() && !ctx.isCommand(ctx.peek().value))) &&
|
|
8564
8640
|
iterations < maxIterations) {
|
|
8641
|
+
firstConditionParse = false;
|
|
8565
8642
|
const beforePos = ctx.savePosition();
|
|
8566
8643
|
conditionTokens.push(ctx.parseLogicalAnd());
|
|
8567
8644
|
if (ctx.savePosition() === beforePos) {
|
|
@@ -8588,22 +8665,8 @@ function parseIfCommand(ctx, commandToken) {
|
|
|
8588
8665
|
}
|
|
8589
8666
|
args.push(condition);
|
|
8590
8667
|
if (isMultiLine) {
|
|
8591
|
-
|
|
8592
|
-
|
|
8593
|
-
}
|
|
8594
|
-
const thenCommands = [];
|
|
8595
|
-
while (!ctx.isAtEnd() && !ctx.check(KEYWORDS$1.ELSE) && !ctx.check(KEYWORDS$1.END)) {
|
|
8596
|
-
if (ctx.checkIsCommand() || ctx.isCommand(ctx.peek().value)) {
|
|
8597
|
-
ctx.advance();
|
|
8598
|
-
const cmd = ctx.parseCommand();
|
|
8599
|
-
if (cmd) {
|
|
8600
|
-
thenCommands.push(cmd);
|
|
8601
|
-
}
|
|
8602
|
-
}
|
|
8603
|
-
else {
|
|
8604
|
-
break;
|
|
8605
|
-
}
|
|
8606
|
-
}
|
|
8668
|
+
consumeOptionalKeyword(ctx, KEYWORDS$1.THEN);
|
|
8669
|
+
const thenCommands = parseIfBranchCommands(ctx, true);
|
|
8607
8670
|
if (thenCommands.length === 0 && ctx.isAtEnd()) {
|
|
8608
8671
|
throw new Error("Expected command after 'then' in if statement - incomplete conditional");
|
|
8609
8672
|
}
|
|
@@ -8629,19 +8692,7 @@ function parseIfCommand(ctx, commandToken) {
|
|
|
8629
8692
|
consumedElseIf = true;
|
|
8630
8693
|
}
|
|
8631
8694
|
else {
|
|
8632
|
-
const elseCommands =
|
|
8633
|
-
while (!ctx.isAtEnd() && !ctx.check(KEYWORDS$1.END)) {
|
|
8634
|
-
if (ctx.checkIsCommand() || ctx.isCommand(ctx.peek().value)) {
|
|
8635
|
-
ctx.advance();
|
|
8636
|
-
const cmd = ctx.parseCommand();
|
|
8637
|
-
if (cmd) {
|
|
8638
|
-
elseCommands.push(cmd);
|
|
8639
|
-
}
|
|
8640
|
-
}
|
|
8641
|
-
else {
|
|
8642
|
-
break;
|
|
8643
|
-
}
|
|
8644
|
-
}
|
|
8695
|
+
const elseCommands = parseIfBranchCommands(ctx, false);
|
|
8645
8696
|
args.push(createBlock(elseCommands, {
|
|
8646
8697
|
start: commandToken.start,
|
|
8647
8698
|
end: ctx.getPosition().end,
|
|
@@ -9463,21 +9514,6 @@ const GO_KEYWORDS = new Set([
|
|
|
9463
9514
|
'back',
|
|
9464
9515
|
'forward',
|
|
9465
9516
|
]);
|
|
9466
|
-
const GO_URL_STOP = new Set([
|
|
9467
|
-
'in',
|
|
9468
|
-
'then',
|
|
9469
|
-
'and',
|
|
9470
|
-
'else',
|
|
9471
|
-
'end',
|
|
9472
|
-
'otherwise',
|
|
9473
|
-
'when',
|
|
9474
|
-
'where',
|
|
9475
|
-
'catch',
|
|
9476
|
-
'finally',
|
|
9477
|
-
]);
|
|
9478
|
-
function isGoURLTerminator(ctx) {
|
|
9479
|
-
return ctx.isAtEnd() || GO_URL_STOP.has(ctx.peek().value);
|
|
9480
|
-
}
|
|
9481
9517
|
function stringNode(value, tok) {
|
|
9482
9518
|
return {
|
|
9483
9519
|
type: 'string',
|
|
@@ -9488,16 +9524,6 @@ function stringNode(value, tok) {
|
|
|
9488
9524
|
column: tok.column,
|
|
9489
9525
|
};
|
|
9490
9526
|
}
|
|
9491
|
-
function isNakedURLStart(ctx) {
|
|
9492
|
-
const tok = ctx.peek();
|
|
9493
|
-
if (tok.value === '/')
|
|
9494
|
-
return true;
|
|
9495
|
-
if (ctx.checkIdentifierLike()) {
|
|
9496
|
-
const next = ctx.peekAt(1);
|
|
9497
|
-
return !!next && next.value === ':' && next.start === tok.end;
|
|
9498
|
-
}
|
|
9499
|
-
return false;
|
|
9500
|
-
}
|
|
9501
9527
|
function parseGoCommand(ctx, identifierNode) {
|
|
9502
9528
|
const args = [];
|
|
9503
9529
|
while (!isCommandBoundary(ctx, ['when', 'where', 'catch', 'finally'])) {
|
|
@@ -9509,7 +9535,7 @@ function parseGoCommand(ctx, identifierNode) {
|
|
|
9509
9535
|
continue;
|
|
9510
9536
|
}
|
|
9511
9537
|
if (isNakedURLStart(ctx)) {
|
|
9512
|
-
const url = parseBareURLPath(ctx
|
|
9538
|
+
const url = parseBareURLPath(ctx);
|
|
9513
9539
|
if (url) {
|
|
9514
9540
|
args.push(url);
|
|
9515
9541
|
continue;
|
|
@@ -9649,35 +9675,65 @@ function parseMultiWordCommand(ctx, commandToken, commandName) {
|
|
|
9649
9675
|
}
|
|
9650
9676
|
return builder.build();
|
|
9651
9677
|
}
|
|
9652
|
-
function
|
|
9653
|
-
|
|
9678
|
+
function isNakedURLStart(ctx) {
|
|
9679
|
+
const tok = ctx.peek();
|
|
9680
|
+
if (tok.value === '/')
|
|
9654
9681
|
return true;
|
|
9655
|
-
|
|
9656
|
-
|
|
9657
|
-
|
|
9682
|
+
if (ctx.checkIdentifierLike()) {
|
|
9683
|
+
const next = ctx.peekAt(1);
|
|
9684
|
+
return !!next && next.value === ':' && next.start === tok.end;
|
|
9685
|
+
}
|
|
9686
|
+
return false;
|
|
9658
9687
|
}
|
|
9659
|
-
|
|
9688
|
+
const URL_HARD_STOPS = new Set(['{', ')']);
|
|
9689
|
+
function parseBareURLPath(ctx) {
|
|
9660
9690
|
const startPos = ctx.savePosition();
|
|
9691
|
+
const firstTok = ctx.peek();
|
|
9661
9692
|
let path = '';
|
|
9662
|
-
|
|
9663
|
-
|
|
9693
|
+
let prev = null;
|
|
9694
|
+
while (!ctx.isAtEnd()) {
|
|
9695
|
+
const tok = ctx.peek();
|
|
9696
|
+
if (prev && tok.start !== prev.end)
|
|
9697
|
+
break;
|
|
9698
|
+
if (tok.value === '$' && ctx.peekAt(1)?.value === '{') {
|
|
9699
|
+
ctx.advance();
|
|
9700
|
+
let last = ctx.advance();
|
|
9701
|
+
let depth = 1;
|
|
9702
|
+
while (!ctx.isAtEnd() && depth > 0) {
|
|
9703
|
+
last = ctx.advance();
|
|
9704
|
+
if (last.value === '{')
|
|
9705
|
+
depth++;
|
|
9706
|
+
else if (last.value === '}')
|
|
9707
|
+
depth--;
|
|
9708
|
+
}
|
|
9709
|
+
path += ctx.getInputSlice(tok.start, last.end);
|
|
9710
|
+
prev = last;
|
|
9711
|
+
continue;
|
|
9712
|
+
}
|
|
9713
|
+
if (URL_HARD_STOPS.has(tok.value))
|
|
9714
|
+
break;
|
|
9715
|
+
prev = ctx.advance();
|
|
9716
|
+
path += prev.value;
|
|
9664
9717
|
}
|
|
9665
9718
|
if (!path || path === '/') {
|
|
9666
9719
|
ctx.restorePosition(startPos);
|
|
9667
9720
|
return null;
|
|
9668
9721
|
}
|
|
9669
|
-
|
|
9670
|
-
|
|
9671
|
-
|
|
9672
|
-
|
|
9673
|
-
|
|
9674
|
-
end: ctx.savePosition(),
|
|
9722
|
+
const span = {
|
|
9723
|
+
start: firstTok.start,
|
|
9724
|
+
end: prev ? prev.end : firstTok.end,
|
|
9725
|
+
line: firstTok.line,
|
|
9726
|
+
column: firstTok.column,
|
|
9675
9727
|
};
|
|
9728
|
+
if (path.includes('${')) {
|
|
9729
|
+
return { type: 'templateLiteral', value: path, ...span };
|
|
9730
|
+
}
|
|
9731
|
+
return { type: 'literal', value: path, raw: path, ...span };
|
|
9676
9732
|
}
|
|
9677
9733
|
function parseFetchCommand(ctx, commandToken) {
|
|
9678
9734
|
const modifiers = {};
|
|
9679
9735
|
let url = null;
|
|
9680
|
-
if (!ctx.isAtEnd() && ctx
|
|
9736
|
+
if (!ctx.isAtEnd() && isNakedURLStart(ctx)) {
|
|
9681
9737
|
url = parseBareURLPath(ctx);
|
|
9682
9738
|
}
|
|
9683
9739
|
if (!url) {
|
|
@@ -9903,10 +9959,10 @@ function parseTellCommand(ctx, identifierNode) {
|
|
|
9903
9959
|
catch {
|
|
9904
9960
|
break;
|
|
9905
9961
|
}
|
|
9906
|
-
if (ctx.match(KEYWORDS$1.AND)) {
|
|
9962
|
+
if (ctx.match(KEYWORDS$1.AND) || ctx.match(KEYWORDS$1.THEN)) {
|
|
9907
9963
|
continue;
|
|
9908
9964
|
}
|
|
9909
|
-
if (ctx.check(KEYWORDS$1.
|
|
9965
|
+
if (ctx.check(KEYWORDS$1.ELSE) || ctx.check(KEYWORDS$1.END)) {
|
|
9910
9966
|
break;
|
|
9911
9967
|
}
|
|
9912
9968
|
if (ctx.checkIsCommand()) {
|
|
@@ -10109,6 +10165,7 @@ class Parser {
|
|
|
10109
10165
|
const result = this.parseInternal();
|
|
10110
10166
|
if (this.errors.length > 0) {
|
|
10111
10167
|
result.errors = [...this.errors];
|
|
10168
|
+
result.recovered = true;
|
|
10112
10169
|
}
|
|
10113
10170
|
return result;
|
|
10114
10171
|
}
|
|
@@ -10243,26 +10300,26 @@ class Parser {
|
|
|
10243
10300
|
const commandSequence = this.parseCommandSequence();
|
|
10244
10301
|
if (commandSequence) {
|
|
10245
10302
|
if (!this.isAtEnd() && this.check('on')) {
|
|
10246
|
-
debug.parse('
|
|
10303
|
+
debug.parse('\u2705 PARSER: Found event handlers after command sequence, parsing as program');
|
|
10247
10304
|
const statements = [commandSequence];
|
|
10248
|
-
debug.parse(
|
|
10305
|
+
debug.parse(`\u2705 PARSER: Starting with ${statements.length} statement(s) from command sequence`);
|
|
10249
10306
|
let eventHandlerCount = 0;
|
|
10250
10307
|
while (!this.isAtEnd() && this.check('on')) {
|
|
10251
|
-
debug.parse(
|
|
10308
|
+
debug.parse(`\u2705 PARSER: Parsing event handler #${eventHandlerCount + 1}, current token: ${this.peek().value}`);
|
|
10252
10309
|
this.advance();
|
|
10253
10310
|
const eventHandler = this.parseEventHandler();
|
|
10254
|
-
debug.parse(
|
|
10311
|
+
debug.parse(`\u2705 PARSER: parseEventHandler returned:`, eventHandler
|
|
10255
10312
|
? `type=${eventHandler.type}, event=${eventHandler.event}`
|
|
10256
10313
|
: 'null');
|
|
10257
10314
|
if (eventHandler) {
|
|
10258
10315
|
statements.push(eventHandler);
|
|
10259
10316
|
eventHandlerCount++;
|
|
10260
|
-
debug.parse(
|
|
10317
|
+
debug.parse(`\u2705 PARSER: Added event handler, now have ${statements.length} statements total`);
|
|
10261
10318
|
}
|
|
10262
|
-
debug.parse(
|
|
10319
|
+
debug.parse(`\u2705 PARSER: After parsing event handler, current token: ${this.isAtEnd() ? 'END' : this.peek().value}, isAtEnd=${this.isAtEnd()}, check('on')=${this.check('on')}`);
|
|
10263
10320
|
if (!this.isAtEnd() && !this.check('on')) {
|
|
10264
10321
|
if (!this.isAtEnd()) {
|
|
10265
|
-
debug.parse(
|
|
10322
|
+
debug.parse(`\u26a0\ufe0f PARSER: Unexpected token after event handlers: ${this.peek().value}`);
|
|
10266
10323
|
this.addError(`Unexpected token after event handlers: ${this.peek().value}`);
|
|
10267
10324
|
return {
|
|
10268
10325
|
success: false,
|
|
@@ -10271,11 +10328,11 @@ class Parser {
|
|
|
10271
10328
|
error: this.error,
|
|
10272
10329
|
};
|
|
10273
10330
|
}
|
|
10274
|
-
debug.parse(
|
|
10331
|
+
debug.parse(`\u2705 PARSER: No more event handlers, at end of input`);
|
|
10275
10332
|
break;
|
|
10276
10333
|
}
|
|
10277
10334
|
}
|
|
10278
|
-
debug.parse(
|
|
10335
|
+
debug.parse(`\u2705 PARSER: Finished parsing, creating Program node with ${statements.length} statements`);
|
|
10279
10336
|
if (this.error) {
|
|
10280
10337
|
return {
|
|
10281
10338
|
success: false,
|
|
@@ -10670,41 +10727,41 @@ class Parser {
|
|
|
10670
10727
|
parseCommandListUntilTerminator(extraStops) {
|
|
10671
10728
|
const commands = [];
|
|
10672
10729
|
const isStop = () => this.check('end') || extraStops.some(s => this.check(s));
|
|
10673
|
-
debug.parse('
|
|
10730
|
+
debug.parse('\ud83d\udd04 parseCommandListUntilTerminator: Starting (extraStops:', extraStops.join(','), ')');
|
|
10674
10731
|
while (!this.isAtEnd() && !isStop()) {
|
|
10675
|
-
debug.parse('
|
|
10732
|
+
debug.parse('\ud83d\udccd Loop iteration, current token:', this.peek().value, 'kind:', this.peek().kind);
|
|
10676
10733
|
let parsedCommand = false;
|
|
10677
10734
|
const isCommandToken = this.checkIsCommand();
|
|
10678
10735
|
const isCommandIdentifier = !isCommandToken && this.checkIdentifier() && this.isCommand(this.peek().value);
|
|
10679
10736
|
if (isCommandToken || isCommandIdentifier) {
|
|
10680
|
-
debug.parse(isCommandToken ? '
|
|
10737
|
+
debug.parse(isCommandToken ? '\u2705 Found COMMAND token:' : '\u2705 Found IDENTIFIER that is a command:', this.peek().value);
|
|
10681
10738
|
this.advance();
|
|
10682
10739
|
const savedError = this.error;
|
|
10683
10740
|
try {
|
|
10684
10741
|
const cmd = this.parseCommand();
|
|
10685
10742
|
if (this.error && this.error !== savedError) {
|
|
10686
|
-
debug.parse('
|
|
10743
|
+
debug.parse('\u26a0\ufe0f parseCommandListUntilTerminator: Command parsing added error, restoring. Error was:', this.error.message);
|
|
10687
10744
|
this.error = savedError;
|
|
10688
10745
|
}
|
|
10689
10746
|
if (cmd) {
|
|
10690
|
-
debug.parse('
|
|
10747
|
+
debug.parse('\u2705 Parsed command:', cmd.name);
|
|
10691
10748
|
commands.push(cmd);
|
|
10692
10749
|
parsedCommand = true;
|
|
10693
10750
|
}
|
|
10694
10751
|
}
|
|
10695
10752
|
catch (error) {
|
|
10696
|
-
debug.parse('
|
|
10753
|
+
debug.parse('\u26a0\ufe0f parseCommandListUntilTerminator: Command parsing threw, restoring:', error instanceof Error ? error.message : String(error));
|
|
10697
10754
|
this.error = savedError;
|
|
10698
10755
|
}
|
|
10699
10756
|
}
|
|
10700
10757
|
else if (this.checkIdentifier()) {
|
|
10701
|
-
debug.parse('
|
|
10758
|
+
debug.parse('\u274c IDENTIFIER is not a command:', this.peek().value);
|
|
10702
10759
|
}
|
|
10703
10760
|
if (!parsedCommand) {
|
|
10704
|
-
debug.parse('
|
|
10761
|
+
debug.parse('\u274c No command parsed, breaking. Current token:', this.peek().value);
|
|
10705
10762
|
break;
|
|
10706
10763
|
}
|
|
10707
|
-
debug.parse('
|
|
10764
|
+
debug.parse('\ud83d\udccd After parsing command, current token:', this.peek().value);
|
|
10708
10765
|
while (!this.isAtEnd() &&
|
|
10709
10766
|
!isStop() &&
|
|
10710
10767
|
!this.checkIsCommand() &&
|
|
@@ -10712,19 +10769,19 @@ class Parser {
|
|
|
10712
10769
|
!this.check('then') &&
|
|
10713
10770
|
!this.check('and') &&
|
|
10714
10771
|
!this.check(',')) {
|
|
10715
|
-
debug.parse('
|
|
10772
|
+
debug.parse('\u26a0\ufe0f Skipping unexpected token:', this.peek().value);
|
|
10716
10773
|
this.advance();
|
|
10717
10774
|
}
|
|
10718
10775
|
if (this.match('then', 'and', ',')) {
|
|
10719
|
-
debug.parse('
|
|
10776
|
+
debug.parse('\u2705 Found separator, continuing');
|
|
10720
10777
|
continue;
|
|
10721
10778
|
}
|
|
10722
10779
|
else if (this.checkIsCommand() || this.isCommand(this.peek().value)) {
|
|
10723
|
-
debug.parse('
|
|
10780
|
+
debug.parse('\u2705 Next token is a command, continuing without separator');
|
|
10724
10781
|
continue;
|
|
10725
10782
|
}
|
|
10726
10783
|
else {
|
|
10727
|
-
debug.parse('
|
|
10784
|
+
debug.parse('\ud83d\udccd No separator and no command, breaking. Current token:', this.peek().value);
|
|
10728
10785
|
break;
|
|
10729
10786
|
}
|
|
10730
10787
|
}
|
|
@@ -10738,18 +10795,18 @@ class Parser {
|
|
|
10738
10795
|
if (!this.check('end') && !extraStops.some(s => this.check(s))) {
|
|
10739
10796
|
terminator = '';
|
|
10740
10797
|
}
|
|
10741
|
-
debug.parse('
|
|
10798
|
+
debug.parse('\u2705 parseCommandListUntilTerminator: parsed', commands.length, 'commands (terminator:', terminator, ')');
|
|
10742
10799
|
return { commands, terminator };
|
|
10743
10800
|
}
|
|
10744
10801
|
parseCommandListUntilEnd() {
|
|
10745
10802
|
const { commands } = this.parseCommandListUntilTerminator([]);
|
|
10746
|
-
debug.parse('
|
|
10803
|
+
debug.parse('\ud83d\udd0d After loop, checking for "end". Current token:', this.peek().value);
|
|
10747
10804
|
if (this.check('end')) {
|
|
10748
|
-
debug.parse('
|
|
10805
|
+
debug.parse('\u2705 Found "end", consuming it');
|
|
10749
10806
|
this.advance();
|
|
10750
10807
|
}
|
|
10751
10808
|
else {
|
|
10752
|
-
debug.parse('
|
|
10809
|
+
debug.parse('\u274c ERROR: Expected "end" but got:', this.peek().value, 'at position:', this.peek().start);
|
|
10753
10810
|
throw new Error('Expected "end" to close repeat block');
|
|
10754
10811
|
}
|
|
10755
10812
|
return commands;
|
|
@@ -11422,6 +11479,23 @@ class Parser {
|
|
|
11422
11479
|
this.consume(')', "Expected ')' after parameter list");
|
|
11423
11480
|
}
|
|
11424
11481
|
const bodyCommands = this.parseCommandBlock(['end', 'catch', 'finally']);
|
|
11482
|
+
const { errorSymbol, errorHandler, finallyHandler } = this.parseErrorAndFinallyBlocks();
|
|
11483
|
+
this.consume('end', "Expected 'end' after function definition");
|
|
11484
|
+
return {
|
|
11485
|
+
type: 'def',
|
|
11486
|
+
name: funcName,
|
|
11487
|
+
params,
|
|
11488
|
+
body: bodyCommands,
|
|
11489
|
+
...(errorSymbol !== undefined && { errorSymbol }),
|
|
11490
|
+
...(errorHandler !== undefined && { errorHandler }),
|
|
11491
|
+
...(finallyHandler !== undefined && { finallyHandler }),
|
|
11492
|
+
start: pos.start,
|
|
11493
|
+
end: this.getPosition().end,
|
|
11494
|
+
line: pos.line,
|
|
11495
|
+
column: pos.column,
|
|
11496
|
+
};
|
|
11497
|
+
}
|
|
11498
|
+
parseErrorAndFinallyBlocks() {
|
|
11425
11499
|
let errorSymbol;
|
|
11426
11500
|
let errorHandler;
|
|
11427
11501
|
if (this.check('catch')) {
|
|
@@ -11436,19 +11510,10 @@ class Parser {
|
|
|
11436
11510
|
this.advance();
|
|
11437
11511
|
finallyHandler = this.parseCommandBlock(['end']);
|
|
11438
11512
|
}
|
|
11439
|
-
this.consume('end', "Expected 'end' after function definition");
|
|
11440
11513
|
return {
|
|
11441
|
-
type: 'def',
|
|
11442
|
-
name: funcName,
|
|
11443
|
-
params,
|
|
11444
|
-
body: bodyCommands,
|
|
11445
11514
|
...(errorSymbol !== undefined && { errorSymbol }),
|
|
11446
11515
|
...(errorHandler !== undefined && { errorHandler }),
|
|
11447
11516
|
...(finallyHandler !== undefined && { finallyHandler }),
|
|
11448
|
-
start: pos.start,
|
|
11449
|
-
end: this.getPosition().end,
|
|
11450
|
-
line: pos.line,
|
|
11451
|
-
column: pos.column,
|
|
11452
11517
|
};
|
|
11453
11518
|
}
|
|
11454
11519
|
parseEventNameWithNamespace(errorMessage) {
|
|
@@ -11593,13 +11658,13 @@ class Parser {
|
|
|
11593
11658
|
try {
|
|
11594
11659
|
const cmd = this.parseCommand();
|
|
11595
11660
|
if (this.error && this.error !== savedError) {
|
|
11596
|
-
debug.parse('
|
|
11661
|
+
debug.parse('\u26a0\ufe0f Command parsing added error, restoring error state. Error was:', this.error.message);
|
|
11597
11662
|
this.error = savedError;
|
|
11598
11663
|
}
|
|
11599
11664
|
return cmd;
|
|
11600
11665
|
}
|
|
11601
11666
|
catch (error) {
|
|
11602
|
-
debug.parse('
|
|
11667
|
+
debug.parse('\u26a0\ufe0f Command parsing threw exception, restoring error state:', error instanceof Error ? error.message : String(error));
|
|
11603
11668
|
this.error = savedError;
|
|
11604
11669
|
return null;
|
|
11605
11670
|
}
|
|
@@ -11616,12 +11681,12 @@ class Parser {
|
|
|
11616
11681
|
const timeToken = this.advance();
|
|
11617
11682
|
const delayMs = parseTimeToMs(timeToken.value);
|
|
11618
11683
|
modifiers[modifierKey] = delayMs;
|
|
11619
|
-
debug.parse(
|
|
11684
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Parsed '${modName} at ${timeToken.value}' (${delayMs}ms)`);
|
|
11620
11685
|
}
|
|
11621
11686
|
else if (this.matchNumber()) {
|
|
11622
11687
|
const delayMs = parseInt(this.previous().value, 10);
|
|
11623
11688
|
modifiers[modifierKey] = delayMs;
|
|
11624
|
-
debug.parse(
|
|
11689
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Parsed '${modName} at ${delayMs}'`);
|
|
11625
11690
|
}
|
|
11626
11691
|
else {
|
|
11627
11692
|
this.addError(`Expected time expression after '${modName} at', got: ${this.peek().value}`);
|
|
@@ -11632,7 +11697,7 @@ class Parser {
|
|
|
11632
11697
|
}
|
|
11633
11698
|
}
|
|
11634
11699
|
parseEventHandler() {
|
|
11635
|
-
debug.parse(
|
|
11700
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: ENTRY - parsing event handler`);
|
|
11636
11701
|
const eventNames = [];
|
|
11637
11702
|
let firstOnceAlias = false;
|
|
11638
11703
|
if (this.check('first') && !this.checkIsCommand() && this.current + 1 < this.tokens.length) {
|
|
@@ -11641,27 +11706,27 @@ class Parser {
|
|
|
11641
11706
|
if (peek2Value && peek2Value !== 'of' && peek2Value !== 'in' && peek2Value !== 'from') {
|
|
11642
11707
|
this.advance();
|
|
11643
11708
|
firstOnceAlias = true;
|
|
11644
|
-
debug.parse(
|
|
11709
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Parsed 'first' as .once alias`);
|
|
11645
11710
|
}
|
|
11646
11711
|
}
|
|
11647
11712
|
const event = this.parseEventNameWithNamespace("Expected event name after 'on'");
|
|
11648
11713
|
eventNames.push(event);
|
|
11649
|
-
debug.parse(
|
|
11714
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Parsed first event name: ${event}`);
|
|
11650
11715
|
while (this.check('or')) {
|
|
11651
11716
|
this.advance();
|
|
11652
|
-
debug.parse(
|
|
11717
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Found 'or', parsing additional event name`);
|
|
11653
11718
|
const additionalEvent = this.parseEventNameWithNamespace("Expected event name after 'or'");
|
|
11654
11719
|
eventNames.push(additionalEvent);
|
|
11655
|
-
debug.parse(
|
|
11720
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Parsed additional event name: ${additionalEvent}`);
|
|
11656
11721
|
}
|
|
11657
|
-
debug.parse(
|
|
11722
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Total events parsed: ${eventNames.join(', ')}`);
|
|
11658
11723
|
let customEventSource;
|
|
11659
11724
|
if (this.registryIntegration) {
|
|
11660
11725
|
const firstEvent = eventNames[0];
|
|
11661
11726
|
if (this.registryIntegration.hasEventSource(firstEvent)) {
|
|
11662
11727
|
const source = this.registryIntegration.getEventSource(firstEvent);
|
|
11663
11728
|
customEventSource = source?.name;
|
|
11664
|
-
debug.parse(
|
|
11729
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Detected custom event source '${customEventSource}' for event '${firstEvent}'`);
|
|
11665
11730
|
}
|
|
11666
11731
|
}
|
|
11667
11732
|
const eventParams = [];
|
|
@@ -11673,7 +11738,7 @@ class Parser {
|
|
|
11673
11738
|
} while (this.match(','));
|
|
11674
11739
|
}
|
|
11675
11740
|
this.consume(')', "Expected ')' after event parameters");
|
|
11676
|
-
debug.parse(
|
|
11741
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Parsed event parameters: ${eventParams.join(', ')}`);
|
|
11677
11742
|
}
|
|
11678
11743
|
const modifiers = {};
|
|
11679
11744
|
while (this.check('.')) {
|
|
@@ -11682,15 +11747,15 @@ class Parser {
|
|
|
11682
11747
|
const modName = modToken.value.toLowerCase();
|
|
11683
11748
|
if (modName === 'once') {
|
|
11684
11749
|
modifiers.once = true;
|
|
11685
|
-
debug.parse(
|
|
11750
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Parsed modifier '.once'`);
|
|
11686
11751
|
}
|
|
11687
11752
|
else if (modName === 'prevent') {
|
|
11688
11753
|
modifiers.prevent = true;
|
|
11689
|
-
debug.parse(
|
|
11754
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Parsed modifier '.prevent'`);
|
|
11690
11755
|
}
|
|
11691
11756
|
else if (modName === 'stop') {
|
|
11692
11757
|
modifiers.stop = true;
|
|
11693
|
-
debug.parse(
|
|
11758
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Parsed modifier '.stop'`);
|
|
11694
11759
|
}
|
|
11695
11760
|
else if (modName === 'debounce' || modName === 'throttle') {
|
|
11696
11761
|
if (this.check('(')) {
|
|
@@ -11702,11 +11767,11 @@ class Parser {
|
|
|
11702
11767
|
}
|
|
11703
11768
|
if (modName === 'debounce') {
|
|
11704
11769
|
modifiers.debounce = delayMs;
|
|
11705
|
-
debug.parse(
|
|
11770
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Parsed modifier '.debounce(${delayMs})'`);
|
|
11706
11771
|
}
|
|
11707
11772
|
else {
|
|
11708
11773
|
modifiers.throttle = delayMs;
|
|
11709
|
-
debug.parse(
|
|
11774
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Parsed modifier '.throttle(${delayMs})'`);
|
|
11710
11775
|
}
|
|
11711
11776
|
this.consume(')', `Expected ')' after ${modName} delay`);
|
|
11712
11777
|
}
|
|
@@ -11715,7 +11780,7 @@ class Parser {
|
|
|
11715
11780
|
}
|
|
11716
11781
|
}
|
|
11717
11782
|
else {
|
|
11718
|
-
debug.parse(
|
|
11783
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Warning - unknown modifier '.${modName}'`);
|
|
11719
11784
|
}
|
|
11720
11785
|
}
|
|
11721
11786
|
this.parseTemporalKeywordModifiers(modifiers);
|
|
@@ -11723,7 +11788,7 @@ class Parser {
|
|
|
11723
11788
|
modifiers.once = true;
|
|
11724
11789
|
}
|
|
11725
11790
|
if (Object.keys(modifiers).length > 0) {
|
|
11726
|
-
debug.parse(
|
|
11791
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Parsed modifiers:`, modifiers);
|
|
11727
11792
|
}
|
|
11728
11793
|
let condition;
|
|
11729
11794
|
if (this.match('[')) {
|
|
@@ -11734,7 +11799,7 @@ class Parser {
|
|
|
11734
11799
|
if (this.match('from')) {
|
|
11735
11800
|
const targetToken = this.advance();
|
|
11736
11801
|
target = targetToken.value;
|
|
11737
|
-
debug.parse(
|
|
11802
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Parsed 'from' target: ${target} (kind: ${targetToken.kind})`);
|
|
11738
11803
|
this.parseTemporalKeywordModifiers(modifiers);
|
|
11739
11804
|
}
|
|
11740
11805
|
let attributeName;
|
|
@@ -11746,32 +11811,36 @@ class Parser {
|
|
|
11746
11811
|
else {
|
|
11747
11812
|
attributeName = attrToken.value;
|
|
11748
11813
|
}
|
|
11749
|
-
debug.parse(
|
|
11814
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Parsed attribute name: ${attributeName}`);
|
|
11750
11815
|
}
|
|
11751
11816
|
let watchTarget;
|
|
11752
11817
|
if (this.match('in')) {
|
|
11753
|
-
debug.parse(
|
|
11818
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Found 'in' keyword, parsing watch target`);
|
|
11754
11819
|
watchTarget = this.parseExpression();
|
|
11755
|
-
debug.parse(
|
|
11820
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Parsed watch target expression`);
|
|
11756
11821
|
}
|
|
11757
11822
|
const commands = [];
|
|
11758
|
-
debug.parse(
|
|
11823
|
+
debug.parse(`\u2705 parseEventHandler: About to parse commands, current token: ${this.isAtEnd() ? 'END' : this.peek().value}, isAtEnd: ${this.isAtEnd()}`);
|
|
11759
11824
|
while (!this.isAtEnd()) {
|
|
11760
|
-
debug.parse(
|
|
11825
|
+
debug.parse(`\u2705 parseEventHandler: Loop iteration, current token: ${this.peek().value}, kind: ${this.peek().kind}`);
|
|
11761
11826
|
if (this.check('on')) {
|
|
11762
|
-
debug.parse(
|
|
11827
|
+
debug.parse(`\u2705 parseEventHandler: Stopping command parsing, found next event handler 'on'`);
|
|
11763
11828
|
break;
|
|
11764
11829
|
}
|
|
11765
11830
|
if (this.checkComment()) {
|
|
11766
|
-
debug.parse(
|
|
11831
|
+
debug.parse(`\u2705 parseEventHandler: Skipping comment token: ${this.peek().value}`);
|
|
11767
11832
|
this.advance();
|
|
11768
11833
|
continue;
|
|
11769
11834
|
}
|
|
11770
11835
|
if (this.check('end')) {
|
|
11771
|
-
debug.parse(
|
|
11836
|
+
debug.parse(`\u2705 parseEventHandler: Stopping command parsing, found 'end' keyword`);
|
|
11772
11837
|
this.advance();
|
|
11773
11838
|
break;
|
|
11774
11839
|
}
|
|
11840
|
+
if (this.check('catch') || this.check('finally')) {
|
|
11841
|
+
debug.parse(`\u2705 parseEventHandler: Stopping command parsing, found '${this.peek().value}'`);
|
|
11842
|
+
break;
|
|
11843
|
+
}
|
|
11775
11844
|
if (this.checkIsCommand()) {
|
|
11776
11845
|
const nextIsOpenParen = this.tokens[this.current + 1]?.value === '(';
|
|
11777
11846
|
const commandName = this.peek().value.toLowerCase();
|
|
@@ -11800,7 +11869,7 @@ class Parser {
|
|
|
11800
11869
|
const cmd = this.parseCommandWithErrorRecovery();
|
|
11801
11870
|
if (cmd) {
|
|
11802
11871
|
commands.push(cmd);
|
|
11803
|
-
debug.parse(
|
|
11872
|
+
debug.parse(`\u2705 parseEventHandler: Parsed command, next token: ${this.isAtEnd() ? 'END' : this.peek().value}`);
|
|
11804
11873
|
}
|
|
11805
11874
|
}
|
|
11806
11875
|
}
|
|
@@ -11818,7 +11887,7 @@ class Parser {
|
|
|
11818
11887
|
expr = this.parseExpression();
|
|
11819
11888
|
}
|
|
11820
11889
|
catch (error) {
|
|
11821
|
-
debug.parse('
|
|
11890
|
+
debug.parse('\u26a0\ufe0f Expression parsing error, restoring error state:', error instanceof Error ? error.message : String(error));
|
|
11822
11891
|
this.error = savedError;
|
|
11823
11892
|
break;
|
|
11824
11893
|
}
|
|
@@ -11865,7 +11934,9 @@ class Parser {
|
|
|
11865
11934
|
!this.check('then') &&
|
|
11866
11935
|
!this.check('and') &&
|
|
11867
11936
|
!this.check(',') &&
|
|
11868
|
-
!this.check('on')
|
|
11937
|
+
!this.check('on') &&
|
|
11938
|
+
!this.check('catch') &&
|
|
11939
|
+
!this.check('finally')) {
|
|
11869
11940
|
this.advance();
|
|
11870
11941
|
}
|
|
11871
11942
|
if (this.match('then', 'and', ',')) {
|
|
@@ -11878,12 +11949,24 @@ class Parser {
|
|
|
11878
11949
|
break;
|
|
11879
11950
|
}
|
|
11880
11951
|
}
|
|
11952
|
+
let errorSymbol;
|
|
11953
|
+
let errorHandler;
|
|
11954
|
+
let finallyHandler;
|
|
11955
|
+
if (!this.isAtEnd() && (this.check('catch') || this.check('finally'))) {
|
|
11956
|
+
({ errorSymbol, errorHandler, finallyHandler } = this.parseErrorAndFinallyBlocks());
|
|
11957
|
+
if (this.check('end')) {
|
|
11958
|
+
this.advance();
|
|
11959
|
+
}
|
|
11960
|
+
}
|
|
11881
11961
|
const pos = this.getPosition();
|
|
11882
11962
|
const node = {
|
|
11883
11963
|
type: 'eventHandler',
|
|
11884
11964
|
event: eventNames.length === 1 ? eventNames[0] : eventNames.join('|'),
|
|
11885
11965
|
events: eventNames,
|
|
11886
11966
|
commands,
|
|
11967
|
+
...(errorSymbol !== undefined && { errorSymbol }),
|
|
11968
|
+
...(errorHandler !== undefined && { errorHandler }),
|
|
11969
|
+
...(finallyHandler !== undefined && { finallyHandler }),
|
|
11887
11970
|
...(eventParams.length > 0 && { args: eventParams }),
|
|
11888
11971
|
...(condition && { condition }),
|
|
11889
11972
|
...(target && { target }),
|
|
@@ -11896,7 +11979,7 @@ class Parser {
|
|
|
11896
11979
|
line: pos.line,
|
|
11897
11980
|
column: pos.column,
|
|
11898
11981
|
};
|
|
11899
|
-
debug.parse(
|
|
11982
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Created node with events: ${eventNames.join(', ')}, attributeName: ${attributeName || 'none'}, watchTarget: ${watchTarget ? 'yes' : 'none'}`);
|
|
11900
11983
|
return node;
|
|
11901
11984
|
}
|
|
11902
11985
|
parseBehaviorDefinition() {
|
|
@@ -11961,11 +12044,33 @@ class Parser {
|
|
|
11961
12044
|
}
|
|
11962
12045
|
}
|
|
11963
12046
|
}
|
|
11964
|
-
const handlerCommands = this.
|
|
12047
|
+
const { commands: handlerCommands, terminator } = this.parseCommandListUntilTerminator([
|
|
12048
|
+
'catch',
|
|
12049
|
+
'finally',
|
|
12050
|
+
]);
|
|
12051
|
+
let handlerErrorSymbol;
|
|
12052
|
+
let handlerErrorHandler;
|
|
12053
|
+
let handlerFinallyHandler;
|
|
12054
|
+
if (terminator === 'catch' || terminator === 'finally') {
|
|
12055
|
+
({
|
|
12056
|
+
errorSymbol: handlerErrorSymbol,
|
|
12057
|
+
errorHandler: handlerErrorHandler,
|
|
12058
|
+
finallyHandler: handlerFinallyHandler,
|
|
12059
|
+
} = this.parseErrorAndFinallyBlocks());
|
|
12060
|
+
}
|
|
12061
|
+
if (this.check('end')) {
|
|
12062
|
+
this.advance();
|
|
12063
|
+
}
|
|
12064
|
+
else {
|
|
12065
|
+
throw new Error('Expected "end" to close behavior event handler');
|
|
12066
|
+
}
|
|
11965
12067
|
const handlerNode = {
|
|
11966
12068
|
type: 'eventHandler',
|
|
11967
12069
|
event: eventName,
|
|
11968
12070
|
commands: handlerCommands,
|
|
12071
|
+
...(handlerErrorSymbol !== undefined && { errorSymbol: handlerErrorSymbol }),
|
|
12072
|
+
...(handlerErrorHandler !== undefined && { errorHandler: handlerErrorHandler }),
|
|
12073
|
+
...(handlerFinallyHandler !== undefined && { finallyHandler: handlerFinallyHandler }),
|
|
11969
12074
|
...(eventSource !== undefined && { target: eventSource }),
|
|
11970
12075
|
...(eventArgs.length > 0 && { args: eventArgs }),
|
|
11971
12076
|
start: handlerPos.start,
|
|
@@ -12020,7 +12125,7 @@ class Parser {
|
|
|
12020
12125
|
this.error.message.includes('unclosed parenthes') ||
|
|
12021
12126
|
this.error.message.includes('Unclosed parenthes');
|
|
12022
12127
|
if (!isCriticalError) {
|
|
12023
|
-
debug.parse('
|
|
12128
|
+
debug.parse('\u26a0\ufe0f parseCommandSequence: Command parsing added non-critical error, restoring error state. Error was:', this.error.message);
|
|
12024
12129
|
this.error = savedError;
|
|
12025
12130
|
}
|
|
12026
12131
|
}
|
|
@@ -12030,11 +12135,11 @@ class Parser {
|
|
|
12030
12135
|
!this.isCommand(this.peek().value) &&
|
|
12031
12136
|
!this.check('then') &&
|
|
12032
12137
|
!this.check('on')) {
|
|
12033
|
-
debug.parse('
|
|
12138
|
+
debug.parse('\u26a0\ufe0f parseCommandSequence: Skipping unexpected token:', this.peek().value);
|
|
12034
12139
|
this.advance();
|
|
12035
12140
|
}
|
|
12036
12141
|
if (this.check('on')) {
|
|
12037
|
-
debug.parse('
|
|
12142
|
+
debug.parse('\u2705 parseCommandSequence: Found "on" token, stopping command sequence to allow event handler parsing');
|
|
12038
12143
|
break;
|
|
12039
12144
|
}
|
|
12040
12145
|
if (this.match('then')) {
|
|
@@ -12092,6 +12197,9 @@ class Parser {
|
|
|
12092
12197
|
this.advance();
|
|
12093
12198
|
const cmd = this.parseCommand();
|
|
12094
12199
|
commands.push(cmd);
|
|
12200
|
+
if (this.match('then', 'and', ',')) {
|
|
12201
|
+
continue;
|
|
12202
|
+
}
|
|
12095
12203
|
}
|
|
12096
12204
|
else {
|
|
12097
12205
|
break;
|
|
@@ -12478,7 +12586,7 @@ class Parser {
|
|
|
12478
12586
|
if (!this.check(')') && callee.type === 'identifier') {
|
|
12479
12587
|
const funcName = callee.name;
|
|
12480
12588
|
if (CommandClassification.isCSSFunction(funcName)) {
|
|
12481
|
-
debug.parse(
|
|
12589
|
+
debug.parse(`\ud83d\udca1 Tip: CSS functions like ${funcName}() should be quoted for clean parsing. ` +
|
|
12482
12590
|
`Use '${funcName}(...)' or \`${funcName}(...)\` instead.`);
|
|
12483
12591
|
}
|
|
12484
12592
|
}
|
|
@@ -12780,7 +12888,7 @@ class Parser {
|
|
|
12780
12888
|
if (this.checkIsCommand() || (this.isCommand(token.value) && !this.isKeyword(token.value))) {
|
|
12781
12889
|
return;
|
|
12782
12890
|
}
|
|
12783
|
-
debug.parse('
|
|
12891
|
+
debug.parse('\u26a0\ufe0f synchronize: Skipping token:', token.value);
|
|
12784
12892
|
this.advance();
|
|
12785
12893
|
}
|
|
12786
12894
|
}
|
|
@@ -16767,8 +16875,15 @@ let FetchCommand = (() => {
|
|
|
16767
16875
|
}
|
|
16768
16876
|
async parseURL(arg, evaluator, context) {
|
|
16769
16877
|
const v = await evaluator.evaluate(arg, context);
|
|
16770
|
-
if (typeof v !== 'string' || !v)
|
|
16878
|
+
if (typeof v !== 'string' || !v) {
|
|
16879
|
+
const node = arg;
|
|
16880
|
+
if ((v === undefined || v === null) && node?.type === 'identifier' && node.name) {
|
|
16881
|
+
throw new Error(`fetch: URL expression "${node.name}" evaluated to ${String(v)} \u2014 ` +
|
|
16882
|
+
`check the variable is set, or quote it if you meant a literal URL ` +
|
|
16883
|
+
`(fetch 'https://example.com/path')`);
|
|
16884
|
+
}
|
|
16771
16885
|
throw new Error('fetch: URL must be a non-empty string');
|
|
16886
|
+
}
|
|
16772
16887
|
return v;
|
|
16773
16888
|
}
|
|
16774
16889
|
parseResponseType(asNode) {
|
|
@@ -16987,7 +17102,7 @@ let SetCommand = (() => {
|
|
|
16987
17102
|
if (typeof firstValue !== 'string') {
|
|
16988
17103
|
const isMember = firstArg?.type === 'memberExpression' || firstArg?.type === 'propertyAccess';
|
|
16989
17104
|
const hint = isMember
|
|
16990
|
-
? ` (a property chain evaluated to ${firstValue === null ? 'null' : typeof firstValue}
|
|
17105
|
+
? ` (a property chain evaluated to ${firstValue === null ? 'null' : typeof firstValue} \u2014 check that all intermediate objects exist)`
|
|
16991
17106
|
: '';
|
|
16992
17107
|
throw new Error(`set command target must be a string or object literal${hint}`);
|
|
16993
17108
|
}
|
|
@@ -18065,7 +18180,7 @@ let BeepCommand = (() => {
|
|
|
18065
18180
|
return { expressionCount: 0, debugged: true, outputs: [] };
|
|
18066
18181
|
}
|
|
18067
18182
|
const outputs = [];
|
|
18068
|
-
console.group('
|
|
18183
|
+
console.group('\ud83d\udd14 beep! Debug Output');
|
|
18069
18184
|
for (const expression of expressions) {
|
|
18070
18185
|
const output = this.debugExpression(expression);
|
|
18071
18186
|
outputs.push(output);
|
|
@@ -18078,7 +18193,7 @@ let BeepCommand = (() => {
|
|
|
18078
18193
|
return { expressionCount: expressions.length, debugged: true, outputs };
|
|
18079
18194
|
}
|
|
18080
18195
|
debugContext(context) {
|
|
18081
|
-
console.group('
|
|
18196
|
+
console.group('\ud83d\udd14 beep! Context Debug');
|
|
18082
18197
|
console.log('me:', context.me);
|
|
18083
18198
|
console.log('it:', context.it);
|
|
18084
18199
|
console.log('you:', context.you);
|