@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
@@ -4138,13 +4138,13 @@ function tokenize(input) {
4138
4138
  continue;
4139
4139
  }
4140
4140
  if (char === '{') {
4141
- addToken(tokenizer, TokenKind.OPERATOR, '{');
4142
4141
  advance(tokenizer);
4142
+ addToken(tokenizer, TokenKind.OPERATOR, '{');
4143
4143
  continue;
4144
4144
  }
4145
4145
  if (char === '}') {
4146
- addToken(tokenizer, TokenKind.OPERATOR, '}');
4147
4146
  advance(tokenizer);
4147
+ addToken(tokenizer, TokenKind.OPERATOR, '}');
4148
4148
  continue;
4149
4149
  }
4150
4150
  if (char === '[') {
@@ -4160,24 +4160,24 @@ function tokenize(input) {
4160
4160
  prevToken.value === ']') &&
4161
4161
  !isEventCondition;
4162
4162
  if (isMemberAccess) {
4163
- addToken(tokenizer, TokenKind.OPERATOR, '[');
4164
4163
  advance(tokenizer);
4164
+ addToken(tokenizer, TokenKind.OPERATOR, '[');
4165
4165
  }
4166
4166
  else {
4167
4167
  if (isEventCondition) {
4168
- addToken(tokenizer, TokenKind.SYMBOL, '[');
4169
4168
  advance(tokenizer);
4169
+ addToken(tokenizer, TokenKind.SYMBOL, '[');
4170
4170
  }
4171
4171
  else {
4172
- addToken(tokenizer, TokenKind.OPERATOR, '[');
4173
4172
  advance(tokenizer);
4173
+ addToken(tokenizer, TokenKind.OPERATOR, '[');
4174
4174
  }
4175
4175
  }
4176
4176
  continue;
4177
4177
  }
4178
4178
  if (char === ']') {
4179
- addToken(tokenizer, TokenKind.OPERATOR, ']');
4180
4179
  advance(tokenizer);
4180
+ addToken(tokenizer, TokenKind.OPERATOR, ']');
4181
4181
  continue;
4182
4182
  }
4183
4183
  if (char === '$') {
@@ -5806,7 +5806,6 @@ const MULTI_WORD_PATTERNS = [
5806
5806
  keywords: ['a', 'an', 'from', 'called'],
5807
5807
  syntax: 'make (a|an) <type> [from <args>] [called <name>]',
5808
5808
  },
5809
- { command: 'send', keywords: ['to'], syntax: 'send <event> to <target>' },
5810
5809
  { command: 'throw', keywords: [], syntax: 'throw <error>' },
5811
5810
  ];
5812
5811
  function getMultiWordPattern(commandName) {
@@ -6107,7 +6106,13 @@ function parseTriggerCommand(ctx, identifierNode) {
6107
6106
  finalArgs.push(ctx.createIdentifier(keyword));
6108
6107
  continue;
6109
6108
  }
6110
- finalArgs.push(ctx.parsePrimary());
6109
+ const before = ctx.savePosition();
6110
+ const expr = ctx.parsePrimary();
6111
+ if (expr)
6112
+ finalArgs.push(expr);
6113
+ if (ctx.savePosition() === before) {
6114
+ ctx.advance();
6115
+ }
6111
6116
  }
6112
6117
  return CommandNodeBuilder.fromIdentifier(identifierNode)
6113
6118
  .withArgs(...finalArgs)
@@ -6326,11 +6331,64 @@ function parseRepeatCommand(ctx, commandToken) {
6326
6331
  }
6327
6332
  return builder.endingAt(ctx.getPosition()).build();
6328
6333
  }
6334
+ function parseIfBranchCommands(ctx, stopAtElse) {
6335
+ const commands = [];
6336
+ const atTerminator = () => ctx.check(KEYWORDS.END) || (stopAtElse && ctx.check(KEYWORDS.ELSE));
6337
+ while (!ctx.isAtEnd() && !atTerminator()) {
6338
+ if (isComment(ctx.peek())) {
6339
+ ctx.advance();
6340
+ continue;
6341
+ }
6342
+ if (!ctx.checkIsCommand() && !ctx.isCommand(ctx.peek().value))
6343
+ break;
6344
+ ctx.advance();
6345
+ const cmd = ctx.parseCommand();
6346
+ if (cmd) {
6347
+ commands.push(cmd);
6348
+ }
6349
+ if (ctx.check(KEYWORDS.THEN) || ctx.check(KEYWORDS.AND) || ctx.check(',')) {
6350
+ ctx.advance();
6351
+ }
6352
+ }
6353
+ return commands;
6354
+ }
6355
+ const OPERAND_INTRODUCERS = new Set([
6356
+ 'is',
6357
+ 'am',
6358
+ 'are',
6359
+ 'not',
6360
+ 'no',
6361
+ 'and',
6362
+ 'or',
6363
+ '==',
6364
+ '===',
6365
+ '!=',
6366
+ '!==',
6367
+ '<',
6368
+ '>',
6369
+ '<=',
6370
+ '>=',
6371
+ '+',
6372
+ '-',
6373
+ '*',
6374
+ '/',
6375
+ 'mod',
6376
+ ]);
6377
+ function isBodyCommandStart(ctx, prevToken, token) {
6378
+ if (prevToken === null) {
6379
+ return false;
6380
+ }
6381
+ if (OPERAND_INTRODUCERS.has(prevToken.value?.toLowerCase?.() ?? '')) {
6382
+ return false;
6383
+ }
6384
+ return ctx.checkIsCommand() || ctx.isCommand(token.value?.toLowerCase());
6385
+ }
6329
6386
  function parseIfCommand(ctx, commandToken) {
6330
6387
  const args = [];
6331
6388
  let hasThen = false;
6332
6389
  const savedPosForThen = ctx.savePosition();
6333
6390
  const maxThenLookahead = 500;
6391
+ let prevForThen = null;
6334
6392
  for (let i = 0; i < maxThenLookahead && !ctx.isAtEnd(); i++) {
6335
6393
  const token = ctx.peek();
6336
6394
  if (token.value === KEYWORDS.THEN) {
@@ -6343,6 +6401,14 @@ function parseIfCommand(ctx, commandToken) {
6343
6401
  token.value === KEYWORDS.ON) {
6344
6402
  break;
6345
6403
  }
6404
+ if (token.line !== undefined &&
6405
+ token.line !== commandToken.line &&
6406
+ isBodyCommandStart(ctx, prevForThen, token)) {
6407
+ break;
6408
+ }
6409
+ if (!isComment(token)) {
6410
+ prevForThen = token;
6411
+ }
6346
6412
  ctx.advance();
6347
6413
  }
6348
6414
  ctx.restorePosition(savedPosForThen);
@@ -6351,9 +6417,14 @@ function parseIfCommand(ctx, commandToken) {
6351
6417
  const savedPosition = ctx.savePosition();
6352
6418
  const ifStatementLine = commandToken.line;
6353
6419
  const maxLookahead = 100;
6420
+ let firstCommandOnIfLine = false;
6421
+ let prevToken = null;
6354
6422
  while (!ctx.isAtEnd() && ctx.current - savedPosition < maxLookahead) {
6355
6423
  const token = ctx.peek();
6356
6424
  const tokenValue = token.value?.toLowerCase();
6425
+ if (firstCommandOnIfLine && token.line !== undefined && token.line !== ifStatementLine) {
6426
+ break;
6427
+ }
6357
6428
  if (tokenValue === KEYWORDS.BEHAVIOR ||
6358
6429
  tokenValue === KEYWORDS.DEF ||
6359
6430
  tokenValue === KEYWORDS.ON) {
@@ -6365,11 +6436,15 @@ function parseIfCommand(ctx, commandToken) {
6365
6436
  }
6366
6437
  break;
6367
6438
  }
6368
- if (ctx.checkIsCommand() || ctx.isCommand(tokenValue)) {
6439
+ if (isBodyCommandStart(ctx, prevToken, token)) {
6369
6440
  if (token.line !== undefined && token.line !== ifStatementLine) {
6370
6441
  hasImplicitMultiLineEnd = true;
6371
6442
  break;
6372
6443
  }
6444
+ firstCommandOnIfLine = true;
6445
+ }
6446
+ if (!isComment(token)) {
6447
+ prevToken = token;
6373
6448
  }
6374
6449
  ctx.advance();
6375
6450
  }
@@ -6384,11 +6459,12 @@ function parseIfCommand(ctx, commandToken) {
6384
6459
  const conditionTokens = [];
6385
6460
  const maxIterations = 20;
6386
6461
  let iterations = 0;
6462
+ let firstConditionParse = true;
6387
6463
  while (!ctx.isAtEnd() &&
6388
- !ctx.checkIsCommand() &&
6389
- !ctx.isCommand(ctx.peek().value) &&
6390
6464
  !ctx.check(KEYWORDS.THEN) &&
6465
+ (firstConditionParse || (!ctx.checkIsCommand() && !ctx.isCommand(ctx.peek().value))) &&
6391
6466
  iterations < maxIterations) {
6467
+ firstConditionParse = false;
6392
6468
  const beforePos = ctx.savePosition();
6393
6469
  conditionTokens.push(ctx.parseLogicalAnd());
6394
6470
  if (ctx.savePosition() === beforePos) {
@@ -6415,22 +6491,8 @@ function parseIfCommand(ctx, commandToken) {
6415
6491
  }
6416
6492
  args.push(condition);
6417
6493
  if (isMultiLine) {
6418
- if (hasThen) {
6419
- ctx.advance();
6420
- }
6421
- const thenCommands = [];
6422
- while (!ctx.isAtEnd() && !ctx.check(KEYWORDS.ELSE) && !ctx.check(KEYWORDS.END)) {
6423
- if (ctx.checkIsCommand() || ctx.isCommand(ctx.peek().value)) {
6424
- ctx.advance();
6425
- const cmd = ctx.parseCommand();
6426
- if (cmd) {
6427
- thenCommands.push(cmd);
6428
- }
6429
- }
6430
- else {
6431
- break;
6432
- }
6433
- }
6494
+ consumeOptionalKeyword(ctx, KEYWORDS.THEN);
6495
+ const thenCommands = parseIfBranchCommands(ctx, true);
6434
6496
  if (thenCommands.length === 0 && ctx.isAtEnd()) {
6435
6497
  throw new Error("Expected command after 'then' in if statement - incomplete conditional");
6436
6498
  }
@@ -6456,19 +6518,7 @@ function parseIfCommand(ctx, commandToken) {
6456
6518
  consumedElseIf = true;
6457
6519
  }
6458
6520
  else {
6459
- const elseCommands = [];
6460
- while (!ctx.isAtEnd() && !ctx.check(KEYWORDS.END)) {
6461
- if (ctx.checkIsCommand() || ctx.isCommand(ctx.peek().value)) {
6462
- ctx.advance();
6463
- const cmd = ctx.parseCommand();
6464
- if (cmd) {
6465
- elseCommands.push(cmd);
6466
- }
6467
- }
6468
- else {
6469
- break;
6470
- }
6471
- }
6521
+ const elseCommands = parseIfBranchCommands(ctx, false);
6472
6522
  args.push(createBlock(elseCommands, {
6473
6523
  start: commandToken.start,
6474
6524
  end: ctx.getPosition().end,
@@ -7290,21 +7340,6 @@ const GO_KEYWORDS = new Set([
7290
7340
  'back',
7291
7341
  'forward',
7292
7342
  ]);
7293
- const GO_URL_STOP = new Set([
7294
- 'in',
7295
- 'then',
7296
- 'and',
7297
- 'else',
7298
- 'end',
7299
- 'otherwise',
7300
- 'when',
7301
- 'where',
7302
- 'catch',
7303
- 'finally',
7304
- ]);
7305
- function isGoURLTerminator(ctx) {
7306
- return ctx.isAtEnd() || GO_URL_STOP.has(ctx.peek().value);
7307
- }
7308
7343
  function stringNode(value, tok) {
7309
7344
  return {
7310
7345
  type: 'string',
@@ -7326,7 +7361,7 @@ function parseGoCommand(ctx, identifierNode) {
7326
7361
  continue;
7327
7362
  }
7328
7363
  if (isNakedURLStart(ctx)) {
7329
- const url = parseBareURLPath(ctx, isGoURLTerminator);
7364
+ const url = parseBareURLPath(ctx);
7330
7365
  if (url) {
7331
7366
  args.push(url);
7332
7367
  continue;
@@ -7466,13 +7501,6 @@ function parseMultiWordCommand(ctx, commandToken, commandName) {
7466
7501
  }
7467
7502
  return builder.build();
7468
7503
  }
7469
- function isFetchURLTerminator(ctx) {
7470
- if (ctx.isAtEnd())
7471
- return true;
7472
- const val = ctx.peek().value;
7473
- return (['as', 'with', '{', 'then', 'end', 'else', 'otherwise', ')'].includes(val) ||
7474
- isCommandBoundary(ctx));
7475
- }
7476
7504
  function isNakedURLStart(ctx) {
7477
7505
  const tok = ctx.peek();
7478
7506
  if (tok.value === '/')
@@ -7483,23 +7511,50 @@ function isNakedURLStart(ctx) {
7483
7511
  }
7484
7512
  return false;
7485
7513
  }
7486
- function parseBareURLPath(ctx, isTerminator = isFetchURLTerminator) {
7514
+ const URL_HARD_STOPS = new Set(['{', ')']);
7515
+ function parseBareURLPath(ctx) {
7487
7516
  const startPos = ctx.savePosition();
7517
+ const firstTok = ctx.peek();
7488
7518
  let path = '';
7489
- while (!ctx.isAtEnd() && !isTerminator(ctx)) {
7490
- path += ctx.advance().value;
7519
+ let prev = null;
7520
+ while (!ctx.isAtEnd()) {
7521
+ const tok = ctx.peek();
7522
+ if (prev && tok.start !== prev.end)
7523
+ break;
7524
+ if (tok.value === '$' && ctx.peekAt(1)?.value === '{') {
7525
+ ctx.advance();
7526
+ let last = ctx.advance();
7527
+ let depth = 1;
7528
+ while (!ctx.isAtEnd() && depth > 0) {
7529
+ last = ctx.advance();
7530
+ if (last.value === '{')
7531
+ depth++;
7532
+ else if (last.value === '}')
7533
+ depth--;
7534
+ }
7535
+ path += ctx.getInputSlice(tok.start, last.end);
7536
+ prev = last;
7537
+ continue;
7538
+ }
7539
+ if (URL_HARD_STOPS.has(tok.value))
7540
+ break;
7541
+ prev = ctx.advance();
7542
+ path += prev.value;
7491
7543
  }
7492
7544
  if (!path || path === '/') {
7493
7545
  ctx.restorePosition(startPos);
7494
7546
  return null;
7495
7547
  }
7496
- return {
7497
- type: 'literal',
7498
- value: path,
7499
- raw: path,
7500
- start: startPos,
7501
- end: ctx.savePosition(),
7548
+ const span = {
7549
+ start: firstTok.start,
7550
+ end: prev ? prev.end : firstTok.end,
7551
+ line: firstTok.line,
7552
+ column: firstTok.column,
7502
7553
  };
7554
+ if (path.includes('${')) {
7555
+ return { type: 'templateLiteral', value: path, ...span };
7556
+ }
7557
+ return { type: 'literal', value: path, raw: path, ...span };
7503
7558
  }
7504
7559
  function parseFetchCommand(ctx, commandToken) {
7505
7560
  const modifiers = {};
@@ -7730,10 +7785,10 @@ function parseTellCommand(ctx, identifierNode) {
7730
7785
  catch {
7731
7786
  break;
7732
7787
  }
7733
- if (ctx.match(KEYWORDS.AND)) {
7788
+ if (ctx.match(KEYWORDS.AND) || ctx.match(KEYWORDS.THEN)) {
7734
7789
  continue;
7735
7790
  }
7736
- if (ctx.check(KEYWORDS.THEN) || ctx.check(KEYWORDS.ELSE) || ctx.check(KEYWORDS.END)) {
7791
+ if (ctx.check(KEYWORDS.ELSE) || ctx.check(KEYWORDS.END)) {
7737
7792
  break;
7738
7793
  }
7739
7794
  if (ctx.checkIsCommand()) {
@@ -7936,6 +7991,7 @@ class Parser {
7936
7991
  const result = this.parseInternal();
7937
7992
  if (this.errors.length > 0) {
7938
7993
  result.errors = [...this.errors];
7994
+ result.recovered = true;
7939
7995
  }
7940
7996
  return result;
7941
7997
  }
@@ -9967,6 +10023,9 @@ class Parser {
9967
10023
  this.advance();
9968
10024
  const cmd = this.parseCommand();
9969
10025
  commands.push(cmd);
10026
+ if (this.match('then', 'and', ',')) {
10027
+ continue;
10028
+ }
9970
10029
  }
9971
10030
  else {
9972
10031
  break;
@@ -1,4 +1,4 @@
1
- import type { ASTNode, ExecutionContext, CommandNode, EventHandlerNode } from '../types/base-types';
1
+ import type { ASTNode, ExecutionContext, CommandNode, EventHandlerNode, DefNode } from '../types/base-types';
2
2
  import type { ExecutionResult, ExecutionSignal, ControlFlowError } from '../types/result';
3
3
  import type { RuntimeHooks } from '../types/hooks';
4
4
  import { HookRegistry } from '../types/hooks';
@@ -73,6 +73,7 @@ export declare class RuntimeBase {
73
73
  protected evaluateExpression(node: ASTNode, context: ExecutionContext): Promise<unknown>;
74
74
  protected evaluateExpressionWithResult(node: ASTNode, context: ExecutionContext): Promise<ExecutionResult<unknown>>;
75
75
  protected executeCommandFromPattern(commandName: string, selector: string, context: ExecutionContext): Promise<unknown>;
76
+ protected installFunction(node: DefNode, context: ExecutionContext): void;
76
77
  protected executeProgram(node: ASTNode & {
77
78
  statements?: ASTNode[];
78
79
  }, context: ExecutionContext): Promise<unknown>;
@@ -120,6 +120,7 @@ export interface ParseResult<T = ASTNode> {
120
120
  ast?: T;
121
121
  error?: ParseError;
122
122
  errors?: ParseError[];
123
+ recovered?: boolean;
123
124
  warnings?: ParseWarning[];
124
125
  tokens: Token[];
125
126
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperfixi/core",
3
- "version": "2.9.1",
3
+ "version": "2.9.3",
4
4
  "description": "Multilingual, tree-shakeable hyperscript",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -187,7 +187,7 @@
187
187
  "build:browser:multilingual": "rollup -c rollup.browser-multilingual.config.mjs",
188
188
  "build:browser:semantic-complete": "rollup -c rollup.browser-semantic-complete.config.mjs",
189
189
  "build:browser:all": "npm run build:browser:dev && npm run build:browser:prod && npm run build:browser:llm && npm run build:browser:lite && npm run build:browser:minimal && npm run build:browser:standard && npm run build:browser:classic && npm run build:browser:classic-i18n && npm run build:browser:multilingual && npm run build:browser:semantic-complete",
190
- "build:browser:modular": "rollup -c rollup.browser-modular.config.mjs",
190
+ "build:browser:modular": "rm -rf dist/chunks && rollup -c rollup.browser-modular.config.mjs",
191
191
  "build:parser-modules": "rollup -c rollup.parser-modules.config.mjs",
192
192
  "snapshot:bundle-size": "node scripts/bundle-size-snapshot.mjs --check",
193
193
  "snapshot:bundle-size:update": "node scripts/bundle-size-snapshot.mjs --update",
@@ -279,14 +279,14 @@
279
279
  "access": "public"
280
280
  },
281
281
  "dependencies": {
282
- "@lokascript/intent": "^2.9.1",
283
- "@lokascript/semantic": "^2.9.1",
282
+ "@lokascript/intent": "^2.9.3",
283
+ "@lokascript/semantic": "^2.9.3",
284
284
  "morphlex": "^1.4.0",
285
285
  "tslib": "^2.8.1"
286
286
  },
287
287
  "peerDependencies": {
288
- "@hyperfixi/patterns-reference": "^2.9.1",
289
- "@lokascript/framework": "^2.9.1"
288
+ "@hyperfixi/patterns-reference": "^2.9.3",
289
+ "@lokascript/framework": "^2.9.3"
290
290
  },
291
291
  "peerDependenciesMeta": {
292
292
  "@hyperfixi/patterns-reference": {