@hyperfixi/core 2.9.1 → 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.
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
@@ -514,13 +514,13 @@ function tokenize(input) {
514
514
  continue;
515
515
  }
516
516
  if (char === '{') {
517
- addToken(tokenizer, TokenKind.OPERATOR, '{');
518
517
  advance(tokenizer);
518
+ addToken(tokenizer, TokenKind.OPERATOR, '{');
519
519
  continue;
520
520
  }
521
521
  if (char === '}') {
522
- addToken(tokenizer, TokenKind.OPERATOR, '}');
523
522
  advance(tokenizer);
523
+ addToken(tokenizer, TokenKind.OPERATOR, '}');
524
524
  continue;
525
525
  }
526
526
  if (char === '[') {
@@ -536,24 +536,24 @@ function tokenize(input) {
536
536
  prevToken.value === ']') &&
537
537
  !isEventCondition;
538
538
  if (isMemberAccess) {
539
- addToken(tokenizer, TokenKind.OPERATOR, '[');
540
539
  advance(tokenizer);
540
+ addToken(tokenizer, TokenKind.OPERATOR, '[');
541
541
  }
542
542
  else {
543
543
  if (isEventCondition) {
544
- addToken(tokenizer, TokenKind.SYMBOL, '[');
545
544
  advance(tokenizer);
545
+ addToken(tokenizer, TokenKind.SYMBOL, '[');
546
546
  }
547
547
  else {
548
- addToken(tokenizer, TokenKind.OPERATOR, '[');
549
548
  advance(tokenizer);
549
+ addToken(tokenizer, TokenKind.OPERATOR, '[');
550
550
  }
551
551
  }
552
552
  continue;
553
553
  }
554
554
  if (char === ']') {
555
- addToken(tokenizer, TokenKind.OPERATOR, ']');
556
555
  advance(tokenizer);
556
+ addToken(tokenizer, TokenKind.OPERATOR, ']');
557
557
  continue;
558
558
  }
559
559
  if (char === '$') {
@@ -2760,7 +2760,6 @@ const MULTI_WORD_PATTERNS = [
2760
2760
  keywords: ['a', 'an', 'from', 'called'],
2761
2761
  syntax: 'make (a|an) <type> [from <args>] [called <name>]',
2762
2762
  },
2763
- { command: 'send', keywords: ['to'], syntax: 'send <event> to <target>' },
2764
2763
  { command: 'throw', keywords: [], syntax: 'throw <error>' },
2765
2764
  ];
2766
2765
  function getMultiWordPattern(commandName) {
@@ -3061,7 +3060,13 @@ function parseTriggerCommand(ctx, identifierNode) {
3061
3060
  finalArgs.push(ctx.createIdentifier(keyword));
3062
3061
  continue;
3063
3062
  }
3064
- finalArgs.push(ctx.parsePrimary());
3063
+ const before = ctx.savePosition();
3064
+ const expr = ctx.parsePrimary();
3065
+ if (expr)
3066
+ finalArgs.push(expr);
3067
+ if (ctx.savePosition() === before) {
3068
+ ctx.advance();
3069
+ }
3065
3070
  }
3066
3071
  return CommandNodeBuilder.fromIdentifier(identifierNode)
3067
3072
  .withArgs(...finalArgs)
@@ -3280,11 +3285,64 @@ function parseRepeatCommand(ctx, commandToken) {
3280
3285
  }
3281
3286
  return builder.endingAt(ctx.getPosition()).build();
3282
3287
  }
3288
+ function parseIfBranchCommands(ctx, stopAtElse) {
3289
+ const commands = [];
3290
+ const atTerminator = () => ctx.check(KEYWORDS.END) || (stopAtElse && ctx.check(KEYWORDS.ELSE));
3291
+ while (!ctx.isAtEnd() && !atTerminator()) {
3292
+ if (isComment(ctx.peek())) {
3293
+ ctx.advance();
3294
+ continue;
3295
+ }
3296
+ if (!ctx.checkIsCommand() && !ctx.isCommand(ctx.peek().value))
3297
+ break;
3298
+ ctx.advance();
3299
+ const cmd = ctx.parseCommand();
3300
+ if (cmd) {
3301
+ commands.push(cmd);
3302
+ }
3303
+ if (ctx.check(KEYWORDS.THEN) || ctx.check(KEYWORDS.AND) || ctx.check(',')) {
3304
+ ctx.advance();
3305
+ }
3306
+ }
3307
+ return commands;
3308
+ }
3309
+ const OPERAND_INTRODUCERS = new Set([
3310
+ 'is',
3311
+ 'am',
3312
+ 'are',
3313
+ 'not',
3314
+ 'no',
3315
+ 'and',
3316
+ 'or',
3317
+ '==',
3318
+ '===',
3319
+ '!=',
3320
+ '!==',
3321
+ '<',
3322
+ '>',
3323
+ '<=',
3324
+ '>=',
3325
+ '+',
3326
+ '-',
3327
+ '*',
3328
+ '/',
3329
+ 'mod',
3330
+ ]);
3331
+ function isBodyCommandStart(ctx, prevToken, token) {
3332
+ if (prevToken === null) {
3333
+ return false;
3334
+ }
3335
+ if (OPERAND_INTRODUCERS.has(prevToken.value?.toLowerCase?.() ?? '')) {
3336
+ return false;
3337
+ }
3338
+ return ctx.checkIsCommand() || ctx.isCommand(token.value?.toLowerCase());
3339
+ }
3283
3340
  function parseIfCommand(ctx, commandToken) {
3284
3341
  const args = [];
3285
3342
  let hasThen = false;
3286
3343
  const savedPosForThen = ctx.savePosition();
3287
3344
  const maxThenLookahead = 500;
3345
+ let prevForThen = null;
3288
3346
  for (let i = 0; i < maxThenLookahead && !ctx.isAtEnd(); i++) {
3289
3347
  const token = ctx.peek();
3290
3348
  if (token.value === KEYWORDS.THEN) {
@@ -3297,6 +3355,14 @@ function parseIfCommand(ctx, commandToken) {
3297
3355
  token.value === KEYWORDS.ON) {
3298
3356
  break;
3299
3357
  }
3358
+ if (token.line !== undefined &&
3359
+ token.line !== commandToken.line &&
3360
+ isBodyCommandStart(ctx, prevForThen, token)) {
3361
+ break;
3362
+ }
3363
+ if (!isComment(token)) {
3364
+ prevForThen = token;
3365
+ }
3300
3366
  ctx.advance();
3301
3367
  }
3302
3368
  ctx.restorePosition(savedPosForThen);
@@ -3305,9 +3371,14 @@ function parseIfCommand(ctx, commandToken) {
3305
3371
  const savedPosition = ctx.savePosition();
3306
3372
  const ifStatementLine = commandToken.line;
3307
3373
  const maxLookahead = 100;
3374
+ let firstCommandOnIfLine = false;
3375
+ let prevToken = null;
3308
3376
  while (!ctx.isAtEnd() && ctx.current - savedPosition < maxLookahead) {
3309
3377
  const token = ctx.peek();
3310
3378
  const tokenValue = token.value?.toLowerCase();
3379
+ if (firstCommandOnIfLine && token.line !== undefined && token.line !== ifStatementLine) {
3380
+ break;
3381
+ }
3311
3382
  if (tokenValue === KEYWORDS.BEHAVIOR ||
3312
3383
  tokenValue === KEYWORDS.DEF ||
3313
3384
  tokenValue === KEYWORDS.ON) {
@@ -3319,11 +3390,15 @@ function parseIfCommand(ctx, commandToken) {
3319
3390
  }
3320
3391
  break;
3321
3392
  }
3322
- if (ctx.checkIsCommand() || ctx.isCommand(tokenValue)) {
3393
+ if (isBodyCommandStart(ctx, prevToken, token)) {
3323
3394
  if (token.line !== undefined && token.line !== ifStatementLine) {
3324
3395
  hasImplicitMultiLineEnd = true;
3325
3396
  break;
3326
3397
  }
3398
+ firstCommandOnIfLine = true;
3399
+ }
3400
+ if (!isComment(token)) {
3401
+ prevToken = token;
3327
3402
  }
3328
3403
  ctx.advance();
3329
3404
  }
@@ -3338,11 +3413,12 @@ function parseIfCommand(ctx, commandToken) {
3338
3413
  const conditionTokens = [];
3339
3414
  const maxIterations = 20;
3340
3415
  let iterations = 0;
3416
+ let firstConditionParse = true;
3341
3417
  while (!ctx.isAtEnd() &&
3342
- !ctx.checkIsCommand() &&
3343
- !ctx.isCommand(ctx.peek().value) &&
3344
3418
  !ctx.check(KEYWORDS.THEN) &&
3419
+ (firstConditionParse || (!ctx.checkIsCommand() && !ctx.isCommand(ctx.peek().value))) &&
3345
3420
  iterations < maxIterations) {
3421
+ firstConditionParse = false;
3346
3422
  const beforePos = ctx.savePosition();
3347
3423
  conditionTokens.push(ctx.parseLogicalAnd());
3348
3424
  if (ctx.savePosition() === beforePos) {
@@ -3369,22 +3445,8 @@ function parseIfCommand(ctx, commandToken) {
3369
3445
  }
3370
3446
  args.push(condition);
3371
3447
  if (isMultiLine) {
3372
- if (hasThen) {
3373
- ctx.advance();
3374
- }
3375
- const thenCommands = [];
3376
- while (!ctx.isAtEnd() && !ctx.check(KEYWORDS.ELSE) && !ctx.check(KEYWORDS.END)) {
3377
- if (ctx.checkIsCommand() || ctx.isCommand(ctx.peek().value)) {
3378
- ctx.advance();
3379
- const cmd = ctx.parseCommand();
3380
- if (cmd) {
3381
- thenCommands.push(cmd);
3382
- }
3383
- }
3384
- else {
3385
- break;
3386
- }
3387
- }
3448
+ consumeOptionalKeyword(ctx, KEYWORDS.THEN);
3449
+ const thenCommands = parseIfBranchCommands(ctx, true);
3388
3450
  if (thenCommands.length === 0 && ctx.isAtEnd()) {
3389
3451
  throw new Error("Expected command after 'then' in if statement - incomplete conditional");
3390
3452
  }
@@ -3410,19 +3472,7 @@ function parseIfCommand(ctx, commandToken) {
3410
3472
  consumedElseIf = true;
3411
3473
  }
3412
3474
  else {
3413
- const elseCommands = [];
3414
- while (!ctx.isAtEnd() && !ctx.check(KEYWORDS.END)) {
3415
- if (ctx.checkIsCommand() || ctx.isCommand(ctx.peek().value)) {
3416
- ctx.advance();
3417
- const cmd = ctx.parseCommand();
3418
- if (cmd) {
3419
- elseCommands.push(cmd);
3420
- }
3421
- }
3422
- else {
3423
- break;
3424
- }
3425
- }
3475
+ const elseCommands = parseIfBranchCommands(ctx, false);
3426
3476
  args.push(createBlock(elseCommands, {
3427
3477
  start: commandToken.start,
3428
3478
  end: ctx.getPosition().end,
@@ -4244,21 +4294,6 @@ const GO_KEYWORDS = new Set([
4244
4294
  'back',
4245
4295
  'forward',
4246
4296
  ]);
4247
- const GO_URL_STOP = new Set([
4248
- 'in',
4249
- 'then',
4250
- 'and',
4251
- 'else',
4252
- 'end',
4253
- 'otherwise',
4254
- 'when',
4255
- 'where',
4256
- 'catch',
4257
- 'finally',
4258
- ]);
4259
- function isGoURLTerminator(ctx) {
4260
- return ctx.isAtEnd() || GO_URL_STOP.has(ctx.peek().value);
4261
- }
4262
4297
  function stringNode(value, tok) {
4263
4298
  return {
4264
4299
  type: 'string',
@@ -4280,7 +4315,7 @@ function parseGoCommand(ctx, identifierNode) {
4280
4315
  continue;
4281
4316
  }
4282
4317
  if (isNakedURLStart(ctx)) {
4283
- const url = parseBareURLPath(ctx, isGoURLTerminator);
4318
+ const url = parseBareURLPath(ctx);
4284
4319
  if (url) {
4285
4320
  args.push(url);
4286
4321
  continue;
@@ -4420,13 +4455,6 @@ function parseMultiWordCommand(ctx, commandToken, commandName) {
4420
4455
  }
4421
4456
  return builder.build();
4422
4457
  }
4423
- function isFetchURLTerminator(ctx) {
4424
- if (ctx.isAtEnd())
4425
- return true;
4426
- const val = ctx.peek().value;
4427
- return (['as', 'with', '{', 'then', 'end', 'else', 'otherwise', ')'].includes(val) ||
4428
- isCommandBoundary(ctx));
4429
- }
4430
4458
  function isNakedURLStart(ctx) {
4431
4459
  const tok = ctx.peek();
4432
4460
  if (tok.value === '/')
@@ -4437,23 +4465,50 @@ function isNakedURLStart(ctx) {
4437
4465
  }
4438
4466
  return false;
4439
4467
  }
4440
- function parseBareURLPath(ctx, isTerminator = isFetchURLTerminator) {
4468
+ const URL_HARD_STOPS = new Set(['{', ')']);
4469
+ function parseBareURLPath(ctx) {
4441
4470
  const startPos = ctx.savePosition();
4471
+ const firstTok = ctx.peek();
4442
4472
  let path = '';
4443
- while (!ctx.isAtEnd() && !isTerminator(ctx)) {
4444
- path += ctx.advance().value;
4473
+ let prev = null;
4474
+ while (!ctx.isAtEnd()) {
4475
+ const tok = ctx.peek();
4476
+ if (prev && tok.start !== prev.end)
4477
+ break;
4478
+ if (tok.value === '$' && ctx.peekAt(1)?.value === '{') {
4479
+ ctx.advance();
4480
+ let last = ctx.advance();
4481
+ let depth = 1;
4482
+ while (!ctx.isAtEnd() && depth > 0) {
4483
+ last = ctx.advance();
4484
+ if (last.value === '{')
4485
+ depth++;
4486
+ else if (last.value === '}')
4487
+ depth--;
4488
+ }
4489
+ path += ctx.getInputSlice(tok.start, last.end);
4490
+ prev = last;
4491
+ continue;
4492
+ }
4493
+ if (URL_HARD_STOPS.has(tok.value))
4494
+ break;
4495
+ prev = ctx.advance();
4496
+ path += prev.value;
4445
4497
  }
4446
4498
  if (!path || path === '/') {
4447
4499
  ctx.restorePosition(startPos);
4448
4500
  return null;
4449
4501
  }
4450
- return {
4451
- type: 'literal',
4452
- value: path,
4453
- raw: path,
4454
- start: startPos,
4455
- end: ctx.savePosition(),
4502
+ const span = {
4503
+ start: firstTok.start,
4504
+ end: prev ? prev.end : firstTok.end,
4505
+ line: firstTok.line,
4506
+ column: firstTok.column,
4456
4507
  };
4508
+ if (path.includes('${')) {
4509
+ return { type: 'templateLiteral', value: path, ...span };
4510
+ }
4511
+ return { type: 'literal', value: path, raw: path, ...span };
4457
4512
  }
4458
4513
  function parseFetchCommand(ctx, commandToken) {
4459
4514
  const modifiers = {};
@@ -4684,10 +4739,10 @@ function parseTellCommand(ctx, identifierNode) {
4684
4739
  catch {
4685
4740
  break;
4686
4741
  }
4687
- if (ctx.match(KEYWORDS.AND)) {
4742
+ if (ctx.match(KEYWORDS.AND) || ctx.match(KEYWORDS.THEN)) {
4688
4743
  continue;
4689
4744
  }
4690
- if (ctx.check(KEYWORDS.THEN) || ctx.check(KEYWORDS.ELSE) || ctx.check(KEYWORDS.END)) {
4745
+ if (ctx.check(KEYWORDS.ELSE) || ctx.check(KEYWORDS.END)) {
4691
4746
  break;
4692
4747
  }
4693
4748
  if (ctx.checkIsCommand()) {
@@ -4890,6 +4945,7 @@ class Parser {
4890
4945
  const result = this.parseInternal();
4891
4946
  if (this.errors.length > 0) {
4892
4947
  result.errors = [...this.errors];
4948
+ result.recovered = true;
4893
4949
  }
4894
4950
  return result;
4895
4951
  }
@@ -6921,6 +6977,9 @@ class Parser {
6921
6977
  this.advance();
6922
6978
  const cmd = this.parseCommand();
6923
6979
  commands.push(cmd);
6980
+ if (this.match('then', 'and', ',')) {
6981
+ continue;
6982
+ }
6924
6983
  }
6925
6984
  else {
6926
6985
  break;
@@ -421,6 +421,9 @@ class HybridParser {
421
421
  if (cmdMap[normalized]) {
422
422
  return cmdMap[normalized]();
423
423
  }
424
+ if (this.match('catch', 'finally')) {
425
+ throw new Error(`'${this.peek().value}' needs the full parser (use hyperfixi.js)`);
426
+ }
424
427
  if (!this.isAtEnd() && !this.match('then', 'and', 'end', 'else')) {
425
428
  this.advance();
426
429
  }
@@ -419,6 +419,9 @@ class HybridParser {
419
419
  if (cmdMap[normalized]) {
420
420
  return cmdMap[normalized]();
421
421
  }
422
+ if (this.match('catch', 'finally')) {
423
+ throw new Error(`'${this.peek().value}' needs the full parser (use hyperfixi.js)`);
424
+ }
422
425
  if (!this.isAtEnd() && !this.match('then', 'and', 'end', 'else')) {
423
426
  this.advance();
424
427
  }
@@ -415,6 +415,9 @@ class HybridParser {
415
415
  if (cmdMap[normalized]) {
416
416
  return cmdMap[normalized]();
417
417
  }
418
+ if (this.match('catch', 'finally')) {
419
+ throw new Error(`'${this.peek().value}' needs the full parser (use hyperfixi.js)`);
420
+ }
418
421
  if (!this.isAtEnd() && !this.match('then', 'and', 'end', 'else')) {
419
422
  this.advance();
420
423
  }
@@ -413,6 +413,9 @@ class HybridParser {
413
413
  if (cmdMap[normalized]) {
414
414
  return cmdMap[normalized]();
415
415
  }
416
+ if (this.match('catch', 'finally')) {
417
+ throw new Error(`'${this.peek().value}' needs the full parser (use hyperfixi.js)`);
418
+ }
416
419
  if (!this.isAtEnd() && !this.match('then', 'and', 'end', 'else')) {
417
420
  this.advance();
418
421
  }
@@ -421,6 +421,9 @@ class HybridParser {
421
421
  if (cmdMap[normalized]) {
422
422
  return cmdMap[normalized]();
423
423
  }
424
+ if (this.match('catch', 'finally')) {
425
+ throw new Error(`'${this.peek().value}' needs the full parser (use hyperfixi.js)`);
426
+ }
424
427
  if (!this.isAtEnd() && !this.match('then', 'and', 'end', 'else')) {
425
428
  this.advance();
426
429
  }
@@ -419,6 +419,9 @@ class HybridParser {
419
419
  if (cmdMap[normalized]) {
420
420
  return cmdMap[normalized]();
421
421
  }
422
+ if (this.match('catch', 'finally')) {
423
+ throw new Error(`'${this.peek().value}' needs the full parser (use hyperfixi.js)`);
424
+ }
422
425
  if (!this.isAtEnd() && !this.match('then', 'and', 'end', 'else')) {
423
426
  this.advance();
424
427
  }
@@ -177,6 +177,7 @@ const commands = {
177
177
  examples: [
178
178
  'fetch /api/data as json',
179
179
  'fetch https://example.com/api as json',
180
+ 'fetch /api/${id} as json',
180
181
  'fetch /api/users as html',
181
182
  'fetch /api/submit with method:"POST"',
182
183
  ],
@@ -175,6 +175,7 @@ const commands = {
175
175
  examples: [
176
176
  'fetch /api/data as json',
177
177
  'fetch https://example.com/api as json',
178
+ 'fetch /api/${id} as json',
178
179
  'fetch /api/users as html',
179
180
  'fetch /api/submit with method:"POST"',
180
181
  ],