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