@hyperfixi/core 2.9.1 → 2.9.3

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.
Files changed (49) hide show
  1. package/dist/bundle-generator/index.js +6 -0
  2. package/dist/bundle-generator/index.mjs +6 -0
  3. package/dist/bundle-generator/parser-templates.d.ts +1 -1
  4. package/dist/chunks/{bridge-BYhqTOnL.js → bridge-DwtNLKK3.js} +2 -2
  5. package/dist/chunks/browser-modular-6HT1r2AH.js +2 -0
  6. package/dist/chunks/{index-DXgJaDj-.js → index-B8jwvuj_.js} +2 -2
  7. package/dist/commands/index.js +133 -74
  8. package/dist/commands/index.mjs +133 -74
  9. package/dist/hyperfixi-browser-classic-i18n.js +1 -1
  10. package/dist/hyperfixi-browser-minimal.js +1 -1
  11. package/dist/hyperfixi-browser-standard.js +1 -1
  12. package/dist/hyperfixi-browser.js +1 -1
  13. package/dist/hyperfixi-classic-i18n.js +1 -1
  14. package/dist/hyperfixi-hx-v4.js +1 -1
  15. package/dist/hyperfixi-hx.js +1 -1
  16. package/dist/hyperfixi-hybrid-complete.js +1 -1
  17. package/dist/hyperfixi-hybrid-hx.js +1 -1
  18. package/dist/hyperfixi-minimal.js +1 -1
  19. package/dist/hyperfixi-multilingual.js +1 -1
  20. package/dist/hyperfixi-standard.js +1 -1
  21. package/dist/hyperfixi.js +1 -1
  22. package/dist/hyperfixi.mjs +1 -1
  23. package/dist/index.js +233 -79
  24. package/dist/index.min.js +1 -1
  25. package/dist/index.mjs +233 -79
  26. package/dist/lokascript-browser-classic-i18n.js +1 -1
  27. package/dist/lokascript-browser-minimal.js +1 -1
  28. package/dist/lokascript-browser-standard.js +1 -1
  29. package/dist/lokascript-browser.js +1 -1
  30. package/dist/lokascript-hybrid-complete.js +1 -1
  31. package/dist/lokascript-hybrid-hx.js +1 -1
  32. package/dist/lokascript-multilingual.js +1 -1
  33. package/dist/parser/command-parsers/utility-commands.d.ts +1 -1
  34. package/dist/parser/full-parser.js +133 -74
  35. package/dist/parser/full-parser.mjs +133 -74
  36. package/dist/parser/hybrid/index.js +3 -0
  37. package/dist/parser/hybrid/index.mjs +3 -0
  38. package/dist/parser/hybrid/parser-core.js +3 -0
  39. package/dist/parser/hybrid/parser-core.mjs +3 -0
  40. package/dist/parser/hybrid-parser.js +3 -0
  41. package/dist/parser/hybrid-parser.mjs +3 -0
  42. package/dist/reference/index.js +1 -0
  43. package/dist/reference/index.mjs +1 -0
  44. package/dist/registry/index.js +133 -74
  45. package/dist/registry/index.mjs +133 -74
  46. package/dist/runtime/runtime-base.d.ts +2 -1
  47. package/dist/types/core.d.ts +1 -0
  48. package/package.json +6 -6
  49. package/dist/chunks/browser-modular-D2BHzZFB.js +0 -2
@@ -4,7 +4,7 @@ export declare function parseCompoundCommand(ctx: ParserContext, identifierNode:
4
4
  export declare function parseRegularCommand(ctx: ParserContext, identifierNode: IdentifierNode): CommandNode;
5
5
  export declare function parseMultiWordCommand(ctx: ParserContext, commandToken: Token, commandName: string): CommandNode | null;
6
6
  export declare function isNakedURLStart(ctx: ParserContext): boolean;
7
- export declare function parseBareURLPath(ctx: ParserContext, isTerminator?: (ctx: ParserContext) => boolean): ASTNode | null;
7
+ export declare function parseBareURLPath(ctx: ParserContext): ASTNode | null;
8
8
  export declare function parseFetchCommand(ctx: ParserContext, commandToken: Token): CommandNode;
9
9
  export declare function parseJsCommand(ctx: ParserContext, identifierNode: IdentifierNode): CommandNode;
10
10
  export declare function parseTellCommand(ctx: ParserContext, identifierNode: IdentifierNode): CommandNode;
@@ -516,13 +516,13 @@ function tokenize(input) {
516
516
  continue;
517
517
  }
518
518
  if (char === '{') {
519
- addToken(tokenizer, TokenKind.OPERATOR, '{');
520
519
  advance(tokenizer);
520
+ addToken(tokenizer, TokenKind.OPERATOR, '{');
521
521
  continue;
522
522
  }
523
523
  if (char === '}') {
524
- addToken(tokenizer, TokenKind.OPERATOR, '}');
525
524
  advance(tokenizer);
525
+ addToken(tokenizer, TokenKind.OPERATOR, '}');
526
526
  continue;
527
527
  }
528
528
  if (char === '[') {
@@ -538,24 +538,24 @@ function tokenize(input) {
538
538
  prevToken.value === ']') &&
539
539
  !isEventCondition;
540
540
  if (isMemberAccess) {
541
- addToken(tokenizer, TokenKind.OPERATOR, '[');
542
541
  advance(tokenizer);
542
+ addToken(tokenizer, TokenKind.OPERATOR, '[');
543
543
  }
544
544
  else {
545
545
  if (isEventCondition) {
546
- addToken(tokenizer, TokenKind.SYMBOL, '[');
547
546
  advance(tokenizer);
547
+ addToken(tokenizer, TokenKind.SYMBOL, '[');
548
548
  }
549
549
  else {
550
- addToken(tokenizer, TokenKind.OPERATOR, '[');
551
550
  advance(tokenizer);
551
+ addToken(tokenizer, TokenKind.OPERATOR, '[');
552
552
  }
553
553
  }
554
554
  continue;
555
555
  }
556
556
  if (char === ']') {
557
- addToken(tokenizer, TokenKind.OPERATOR, ']');
558
557
  advance(tokenizer);
558
+ addToken(tokenizer, TokenKind.OPERATOR, ']');
559
559
  continue;
560
560
  }
561
561
  if (char === '$') {
@@ -2762,7 +2762,6 @@ const MULTI_WORD_PATTERNS = [
2762
2762
  keywords: ['a', 'an', 'from', 'called'],
2763
2763
  syntax: 'make (a|an) <type> [from <args>] [called <name>]',
2764
2764
  },
2765
- { command: 'send', keywords: ['to'], syntax: 'send <event> to <target>' },
2766
2765
  { command: 'throw', keywords: [], syntax: 'throw <error>' },
2767
2766
  ];
2768
2767
  function getMultiWordPattern(commandName) {
@@ -3063,7 +3062,13 @@ function parseTriggerCommand(ctx, identifierNode) {
3063
3062
  finalArgs.push(ctx.createIdentifier(keyword));
3064
3063
  continue;
3065
3064
  }
3066
- finalArgs.push(ctx.parsePrimary());
3065
+ const before = ctx.savePosition();
3066
+ const expr = ctx.parsePrimary();
3067
+ if (expr)
3068
+ finalArgs.push(expr);
3069
+ if (ctx.savePosition() === before) {
3070
+ ctx.advance();
3071
+ }
3067
3072
  }
3068
3073
  return CommandNodeBuilder.fromIdentifier(identifierNode)
3069
3074
  .withArgs(...finalArgs)
@@ -3282,11 +3287,64 @@ function parseRepeatCommand(ctx, commandToken) {
3282
3287
  }
3283
3288
  return builder.endingAt(ctx.getPosition()).build();
3284
3289
  }
3290
+ function parseIfBranchCommands(ctx, stopAtElse) {
3291
+ const commands = [];
3292
+ const atTerminator = () => ctx.check(KEYWORDS.END) || (stopAtElse && ctx.check(KEYWORDS.ELSE));
3293
+ while (!ctx.isAtEnd() && !atTerminator()) {
3294
+ if (isComment(ctx.peek())) {
3295
+ ctx.advance();
3296
+ continue;
3297
+ }
3298
+ if (!ctx.checkIsCommand() && !ctx.isCommand(ctx.peek().value))
3299
+ break;
3300
+ ctx.advance();
3301
+ const cmd = ctx.parseCommand();
3302
+ if (cmd) {
3303
+ commands.push(cmd);
3304
+ }
3305
+ if (ctx.check(KEYWORDS.THEN) || ctx.check(KEYWORDS.AND) || ctx.check(',')) {
3306
+ ctx.advance();
3307
+ }
3308
+ }
3309
+ return commands;
3310
+ }
3311
+ const OPERAND_INTRODUCERS = new Set([
3312
+ 'is',
3313
+ 'am',
3314
+ 'are',
3315
+ 'not',
3316
+ 'no',
3317
+ 'and',
3318
+ 'or',
3319
+ '==',
3320
+ '===',
3321
+ '!=',
3322
+ '!==',
3323
+ '<',
3324
+ '>',
3325
+ '<=',
3326
+ '>=',
3327
+ '+',
3328
+ '-',
3329
+ '*',
3330
+ '/',
3331
+ 'mod',
3332
+ ]);
3333
+ function isBodyCommandStart(ctx, prevToken, token) {
3334
+ if (prevToken === null) {
3335
+ return false;
3336
+ }
3337
+ if (OPERAND_INTRODUCERS.has(prevToken.value?.toLowerCase?.() ?? '')) {
3338
+ return false;
3339
+ }
3340
+ return ctx.checkIsCommand() || ctx.isCommand(token.value?.toLowerCase());
3341
+ }
3285
3342
  function parseIfCommand(ctx, commandToken) {
3286
3343
  const args = [];
3287
3344
  let hasThen = false;
3288
3345
  const savedPosForThen = ctx.savePosition();
3289
3346
  const maxThenLookahead = 500;
3347
+ let prevForThen = null;
3290
3348
  for (let i = 0; i < maxThenLookahead && !ctx.isAtEnd(); i++) {
3291
3349
  const token = ctx.peek();
3292
3350
  if (token.value === KEYWORDS.THEN) {
@@ -3299,6 +3357,14 @@ function parseIfCommand(ctx, commandToken) {
3299
3357
  token.value === KEYWORDS.ON) {
3300
3358
  break;
3301
3359
  }
3360
+ if (token.line !== undefined &&
3361
+ token.line !== commandToken.line &&
3362
+ isBodyCommandStart(ctx, prevForThen, token)) {
3363
+ break;
3364
+ }
3365
+ if (!isComment(token)) {
3366
+ prevForThen = token;
3367
+ }
3302
3368
  ctx.advance();
3303
3369
  }
3304
3370
  ctx.restorePosition(savedPosForThen);
@@ -3307,9 +3373,14 @@ function parseIfCommand(ctx, commandToken) {
3307
3373
  const savedPosition = ctx.savePosition();
3308
3374
  const ifStatementLine = commandToken.line;
3309
3375
  const maxLookahead = 100;
3376
+ let firstCommandOnIfLine = false;
3377
+ let prevToken = null;
3310
3378
  while (!ctx.isAtEnd() && ctx.current - savedPosition < maxLookahead) {
3311
3379
  const token = ctx.peek();
3312
3380
  const tokenValue = token.value?.toLowerCase();
3381
+ if (firstCommandOnIfLine && token.line !== undefined && token.line !== ifStatementLine) {
3382
+ break;
3383
+ }
3313
3384
  if (tokenValue === KEYWORDS.BEHAVIOR ||
3314
3385
  tokenValue === KEYWORDS.DEF ||
3315
3386
  tokenValue === KEYWORDS.ON) {
@@ -3321,11 +3392,15 @@ function parseIfCommand(ctx, commandToken) {
3321
3392
  }
3322
3393
  break;
3323
3394
  }
3324
- if (ctx.checkIsCommand() || ctx.isCommand(tokenValue)) {
3395
+ if (isBodyCommandStart(ctx, prevToken, token)) {
3325
3396
  if (token.line !== undefined && token.line !== ifStatementLine) {
3326
3397
  hasImplicitMultiLineEnd = true;
3327
3398
  break;
3328
3399
  }
3400
+ firstCommandOnIfLine = true;
3401
+ }
3402
+ if (!isComment(token)) {
3403
+ prevToken = token;
3329
3404
  }
3330
3405
  ctx.advance();
3331
3406
  }
@@ -3340,11 +3415,12 @@ function parseIfCommand(ctx, commandToken) {
3340
3415
  const conditionTokens = [];
3341
3416
  const maxIterations = 20;
3342
3417
  let iterations = 0;
3418
+ let firstConditionParse = true;
3343
3419
  while (!ctx.isAtEnd() &&
3344
- !ctx.checkIsCommand() &&
3345
- !ctx.isCommand(ctx.peek().value) &&
3346
3420
  !ctx.check(KEYWORDS.THEN) &&
3421
+ (firstConditionParse || (!ctx.checkIsCommand() && !ctx.isCommand(ctx.peek().value))) &&
3347
3422
  iterations < maxIterations) {
3423
+ firstConditionParse = false;
3348
3424
  const beforePos = ctx.savePosition();
3349
3425
  conditionTokens.push(ctx.parseLogicalAnd());
3350
3426
  if (ctx.savePosition() === beforePos) {
@@ -3371,22 +3447,8 @@ function parseIfCommand(ctx, commandToken) {
3371
3447
  }
3372
3448
  args.push(condition);
3373
3449
  if (isMultiLine) {
3374
- if (hasThen) {
3375
- ctx.advance();
3376
- }
3377
- const thenCommands = [];
3378
- while (!ctx.isAtEnd() && !ctx.check(KEYWORDS.ELSE) && !ctx.check(KEYWORDS.END)) {
3379
- if (ctx.checkIsCommand() || ctx.isCommand(ctx.peek().value)) {
3380
- ctx.advance();
3381
- const cmd = ctx.parseCommand();
3382
- if (cmd) {
3383
- thenCommands.push(cmd);
3384
- }
3385
- }
3386
- else {
3387
- break;
3388
- }
3389
- }
3450
+ consumeOptionalKeyword(ctx, KEYWORDS.THEN);
3451
+ const thenCommands = parseIfBranchCommands(ctx, true);
3390
3452
  if (thenCommands.length === 0 && ctx.isAtEnd()) {
3391
3453
  throw new Error("Expected command after 'then' in if statement - incomplete conditional");
3392
3454
  }
@@ -3412,19 +3474,7 @@ function parseIfCommand(ctx, commandToken) {
3412
3474
  consumedElseIf = true;
3413
3475
  }
3414
3476
  else {
3415
- const elseCommands = [];
3416
- while (!ctx.isAtEnd() && !ctx.check(KEYWORDS.END)) {
3417
- if (ctx.checkIsCommand() || ctx.isCommand(ctx.peek().value)) {
3418
- ctx.advance();
3419
- const cmd = ctx.parseCommand();
3420
- if (cmd) {
3421
- elseCommands.push(cmd);
3422
- }
3423
- }
3424
- else {
3425
- break;
3426
- }
3427
- }
3477
+ const elseCommands = parseIfBranchCommands(ctx, false);
3428
3478
  args.push(createBlock(elseCommands, {
3429
3479
  start: commandToken.start,
3430
3480
  end: ctx.getPosition().end,
@@ -4246,21 +4296,6 @@ const GO_KEYWORDS = new Set([
4246
4296
  'back',
4247
4297
  'forward',
4248
4298
  ]);
4249
- const GO_URL_STOP = new Set([
4250
- 'in',
4251
- 'then',
4252
- 'and',
4253
- 'else',
4254
- 'end',
4255
- 'otherwise',
4256
- 'when',
4257
- 'where',
4258
- 'catch',
4259
- 'finally',
4260
- ]);
4261
- function isGoURLTerminator(ctx) {
4262
- return ctx.isAtEnd() || GO_URL_STOP.has(ctx.peek().value);
4263
- }
4264
4299
  function stringNode(value, tok) {
4265
4300
  return {
4266
4301
  type: 'string',
@@ -4282,7 +4317,7 @@ function parseGoCommand(ctx, identifierNode) {
4282
4317
  continue;
4283
4318
  }
4284
4319
  if (isNakedURLStart(ctx)) {
4285
- const url = parseBareURLPath(ctx, isGoURLTerminator);
4320
+ const url = parseBareURLPath(ctx);
4286
4321
  if (url) {
4287
4322
  args.push(url);
4288
4323
  continue;
@@ -4422,13 +4457,6 @@ function parseMultiWordCommand(ctx, commandToken, commandName) {
4422
4457
  }
4423
4458
  return builder.build();
4424
4459
  }
4425
- function isFetchURLTerminator(ctx) {
4426
- if (ctx.isAtEnd())
4427
- return true;
4428
- const val = ctx.peek().value;
4429
- return (['as', 'with', '{', 'then', 'end', 'else', 'otherwise', ')'].includes(val) ||
4430
- isCommandBoundary(ctx));
4431
- }
4432
4460
  function isNakedURLStart(ctx) {
4433
4461
  const tok = ctx.peek();
4434
4462
  if (tok.value === '/')
@@ -4439,23 +4467,50 @@ function isNakedURLStart(ctx) {
4439
4467
  }
4440
4468
  return false;
4441
4469
  }
4442
- function parseBareURLPath(ctx, isTerminator = isFetchURLTerminator) {
4470
+ const URL_HARD_STOPS = new Set(['{', ')']);
4471
+ function parseBareURLPath(ctx) {
4443
4472
  const startPos = ctx.savePosition();
4473
+ const firstTok = ctx.peek();
4444
4474
  let path = '';
4445
- while (!ctx.isAtEnd() && !isTerminator(ctx)) {
4446
- path += ctx.advance().value;
4475
+ let prev = null;
4476
+ while (!ctx.isAtEnd()) {
4477
+ const tok = ctx.peek();
4478
+ if (prev && tok.start !== prev.end)
4479
+ break;
4480
+ if (tok.value === '$' && ctx.peekAt(1)?.value === '{') {
4481
+ ctx.advance();
4482
+ let last = ctx.advance();
4483
+ let depth = 1;
4484
+ while (!ctx.isAtEnd() && depth > 0) {
4485
+ last = ctx.advance();
4486
+ if (last.value === '{')
4487
+ depth++;
4488
+ else if (last.value === '}')
4489
+ depth--;
4490
+ }
4491
+ path += ctx.getInputSlice(tok.start, last.end);
4492
+ prev = last;
4493
+ continue;
4494
+ }
4495
+ if (URL_HARD_STOPS.has(tok.value))
4496
+ break;
4497
+ prev = ctx.advance();
4498
+ path += prev.value;
4447
4499
  }
4448
4500
  if (!path || path === '/') {
4449
4501
  ctx.restorePosition(startPos);
4450
4502
  return null;
4451
4503
  }
4452
- return {
4453
- type: 'literal',
4454
- value: path,
4455
- raw: path,
4456
- start: startPos,
4457
- end: ctx.savePosition(),
4504
+ const span = {
4505
+ start: firstTok.start,
4506
+ end: prev ? prev.end : firstTok.end,
4507
+ line: firstTok.line,
4508
+ column: firstTok.column,
4458
4509
  };
4510
+ if (path.includes('${')) {
4511
+ return { type: 'templateLiteral', value: path, ...span };
4512
+ }
4513
+ return { type: 'literal', value: path, raw: path, ...span };
4459
4514
  }
4460
4515
  function parseFetchCommand(ctx, commandToken) {
4461
4516
  const modifiers = {};
@@ -4686,10 +4741,10 @@ function parseTellCommand(ctx, identifierNode) {
4686
4741
  catch {
4687
4742
  break;
4688
4743
  }
4689
- if (ctx.match(KEYWORDS.AND)) {
4744
+ if (ctx.match(KEYWORDS.AND) || ctx.match(KEYWORDS.THEN)) {
4690
4745
  continue;
4691
4746
  }
4692
- if (ctx.check(KEYWORDS.THEN) || ctx.check(KEYWORDS.ELSE) || ctx.check(KEYWORDS.END)) {
4747
+ if (ctx.check(KEYWORDS.ELSE) || ctx.check(KEYWORDS.END)) {
4693
4748
  break;
4694
4749
  }
4695
4750
  if (ctx.checkIsCommand()) {
@@ -4892,6 +4947,7 @@ class Parser {
4892
4947
  const result = this.parseInternal();
4893
4948
  if (this.errors.length > 0) {
4894
4949
  result.errors = [...this.errors];
4950
+ result.recovered = true;
4895
4951
  }
4896
4952
  return result;
4897
4953
  }
@@ -6923,6 +6979,9 @@ class Parser {
6923
6979
  this.advance();
6924
6980
  const cmd = this.parseCommand();
6925
6981
  commands.push(cmd);
6982
+ if (this.match('then', 'and', ',')) {
6983
+ continue;
6984
+ }
6926
6985
  }
6927
6986
  else {
6928
6987
  break;