@aster-cloud/aster-lang-ts 1.0.32 → 1.0.33

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 (121) hide show
  1. package/dist/scripts/corpus-loader.d.ts +10 -4
  2. package/dist/scripts/corpus-loader.d.ts.map +1 -1
  3. package/dist/scripts/corpus-loader.js +24 -9
  4. package/dist/scripts/corpus-loader.js.map +1 -1
  5. package/dist/scripts/lexicon-completeness.d.ts +42 -0
  6. package/dist/scripts/lexicon-completeness.d.ts.map +1 -0
  7. package/dist/scripts/lexicon-completeness.js +57 -0
  8. package/dist/scripts/lexicon-completeness.js.map +1 -0
  9. package/dist/scripts/lsp-client-helper.d.ts +7 -1
  10. package/dist/scripts/lsp-client-helper.d.ts.map +1 -1
  11. package/dist/scripts/lsp-client-helper.js +6 -1
  12. package/dist/scripts/lsp-client-helper.js.map +1 -1
  13. package/dist/scripts/verify-lexicon-sync.js +14 -44
  14. package/dist/scripts/verify-lexicon-sync.js.map +1 -1
  15. package/dist/src/lsp/codeaction.d.ts.map +1 -1
  16. package/dist/src/lsp/codeaction.js +12 -4
  17. package/dist/src/lsp/codeaction.js.map +1 -1
  18. package/dist/src/lsp/diagnostics.d.ts +2 -0
  19. package/dist/src/lsp/diagnostics.d.ts.map +1 -1
  20. package/dist/src/lsp/diagnostics.js +4 -0
  21. package/dist/src/lsp/diagnostics.js.map +1 -1
  22. package/dist/src/lsp/navigation/hover.d.ts.map +1 -1
  23. package/dist/src/lsp/navigation/hover.js +23 -0
  24. package/dist/src/lsp/navigation/hover.js.map +1 -1
  25. package/dist/src/lsp/navigation/references.d.ts +3 -1
  26. package/dist/src/lsp/navigation/references.d.ts.map +1 -1
  27. package/dist/src/lsp/navigation/references.js +16 -6
  28. package/dist/src/lsp/navigation/references.js.map +1 -1
  29. package/dist/src/lsp/navigation/rename.d.ts +1 -1
  30. package/dist/src/lsp/navigation/rename.d.ts.map +1 -1
  31. package/dist/src/lsp/navigation/rename.js +31 -42
  32. package/dist/src/lsp/navigation/rename.js.map +1 -1
  33. package/dist/src/lsp/navigation/shared.d.ts +34 -2
  34. package/dist/src/lsp/navigation/shared.d.ts.map +1 -1
  35. package/dist/src/lsp/navigation/shared.js +88 -10
  36. package/dist/src/lsp/navigation/shared.js.map +1 -1
  37. package/dist/src/lsp/navigation.js +2 -2
  38. package/dist/src/lsp/navigation.js.map +1 -1
  39. package/dist/src/lsp/parse-document.d.ts +11 -0
  40. package/dist/src/lsp/parse-document.d.ts.map +1 -0
  41. package/dist/src/lsp/parse-document.js +51 -0
  42. package/dist/src/lsp/parse-document.js.map +1 -0
  43. package/dist/src/lsp/server.js +18 -23
  44. package/dist/src/lsp/server.js.map +1 -1
  45. package/dist/src/lsp/settings.d.ts +35 -0
  46. package/dist/src/lsp/settings.d.ts.map +1 -0
  47. package/dist/src/lsp/settings.js +31 -0
  48. package/dist/src/lsp/settings.js.map +1 -0
  49. package/dist/src/lsp/source-positions.d.ts +47 -0
  50. package/dist/src/lsp/source-positions.d.ts.map +1 -0
  51. package/dist/src/lsp/source-positions.js +92 -0
  52. package/dist/src/lsp/source-positions.js.map +1 -0
  53. package/dist/src/lsp/workspace/document-indexer.d.ts +3 -1
  54. package/dist/src/lsp/workspace/document-indexer.d.ts.map +1 -1
  55. package/dist/src/lsp/workspace/document-indexer.js +5 -7
  56. package/dist/src/lsp/workspace/document-indexer.js.map +1 -1
  57. package/dist/src/lsp/workspace/symbol-finder.d.ts +8 -1
  58. package/dist/src/lsp/workspace/symbol-finder.d.ts.map +1 -1
  59. package/dist/src/lsp/workspace/symbol-finder.js +10 -13
  60. package/dist/src/lsp/workspace/symbol-finder.js.map +1 -1
  61. package/dist/src/parser/constraint-parser.d.ts.map +1 -1
  62. package/dist/src/parser/constraint-parser.js +18 -7
  63. package/dist/src/parser/constraint-parser.js.map +1 -1
  64. package/dist/src/parser/decl-parser.d.ts.map +1 -1
  65. package/dist/src/parser/decl-parser.js +6 -5
  66. package/dist/src/parser/decl-parser.js.map +1 -1
  67. package/dist/src/parser/expr-stmt-parser.d.ts.map +1 -1
  68. package/dist/src/parser/expr-stmt-parser.js +42 -37
  69. package/dist/src/parser/expr-stmt-parser.js.map +1 -1
  70. package/dist/src/parser/import-parser.d.ts.map +1 -1
  71. package/dist/src/parser/import-parser.js +2 -1
  72. package/dist/src/parser/import-parser.js.map +1 -1
  73. package/dist/src/parser/keyword-display.d.ts +40 -0
  74. package/dist/src/parser/keyword-display.d.ts.map +1 -0
  75. package/dist/src/parser/keyword-display.js +73 -0
  76. package/dist/src/parser/keyword-display.js.map +1 -0
  77. package/dist/src/parser/type-parser.d.ts.map +1 -1
  78. package/dist/src/parser/type-parser.js +5 -4
  79. package/dist/src/parser/type-parser.js.map +1 -1
  80. package/dist/src/parser.d.ts.map +1 -1
  81. package/dist/src/parser.js +1 -0
  82. package/dist/src/parser.js.map +1 -1
  83. package/dist/test/integration/lsp/lsp-rename-safety.test.d.ts +2 -0
  84. package/dist/test/integration/lsp/lsp-rename-safety.test.d.ts.map +1 -0
  85. package/dist/test/integration/lsp/lsp-rename-safety.test.js +114 -0
  86. package/dist/test/integration/lsp/lsp-rename-safety.test.js.map +1 -0
  87. package/dist/test/integration/lsp/lsp-source-positions.test.d.ts +2 -0
  88. package/dist/test/integration/lsp/lsp-source-positions.test.d.ts.map +1 -0
  89. package/dist/test/integration/lsp/lsp-source-positions.test.js +161 -0
  90. package/dist/test/integration/lsp/lsp-source-positions.test.js.map +1 -0
  91. package/dist/test/integration/lsp/lsp-zh-inmemory-navigation.test.d.ts +2 -0
  92. package/dist/test/integration/lsp/lsp-zh-inmemory-navigation.test.d.ts.map +1 -0
  93. package/dist/test/integration/lsp/lsp-zh-inmemory-navigation.test.js +79 -0
  94. package/dist/test/integration/lsp/lsp-zh-inmemory-navigation.test.js.map +1 -0
  95. package/dist/test/unit/lexicon-completeness.test.d.ts +2 -0
  96. package/dist/test/unit/lexicon-completeness.test.d.ts.map +1 -0
  97. package/dist/test/unit/lexicon-completeness.test.js +42 -0
  98. package/dist/test/unit/lexicon-completeness.test.js.map +1 -0
  99. package/dist/test/unit/lsp/codeaction.test.js +28 -0
  100. package/dist/test/unit/lsp/codeaction.test.js.map +1 -1
  101. package/dist/test/unit/lsp/lsp-locale-fixes.test.d.ts +2 -0
  102. package/dist/test/unit/lsp/lsp-locale-fixes.test.d.ts.map +1 -0
  103. package/dist/test/unit/lsp/lsp-locale-fixes.test.js +105 -0
  104. package/dist/test/unit/lsp/lsp-locale-fixes.test.js.map +1 -0
  105. package/dist/test/unit/lsp/parse-document.test.d.ts +2 -0
  106. package/dist/test/unit/lsp/parse-document.test.d.ts.map +1 -0
  107. package/dist/test/unit/lsp/parse-document.test.js +30 -0
  108. package/dist/test/unit/lsp/parse-document.test.js.map +1 -0
  109. package/dist/test/unit/parser/keyword-display.test.d.ts +8 -0
  110. package/dist/test/unit/parser/keyword-display.test.d.ts.map +1 -0
  111. package/dist/test/unit/parser/keyword-display.test.js +27 -0
  112. package/dist/test/unit/parser/keyword-display.test.js.map +1 -0
  113. package/dist/test/unit/parser/localized-diagnostic-keywords.test.d.ts +15 -0
  114. package/dist/test/unit/parser/localized-diagnostic-keywords.test.d.ts.map +1 -0
  115. package/dist/test/unit/parser/localized-diagnostic-keywords.test.js +98 -0
  116. package/dist/test/unit/parser/localized-diagnostic-keywords.test.js.map +1 -0
  117. package/dist/test/unit/parser/prefix-operator-call.test.js +8 -6
  118. package/dist/test/unit/parser/prefix-operator-call.test.js.map +1 -1
  119. package/dist/test/unit/scripts/corpus-loader.test.js +44 -9
  120. package/dist/test/unit/scripts/corpus-loader.test.js.map +1 -1
  121. package/package.json +1 -1
@@ -1,3 +1,5 @@
1
+ import { kw, kwKind, kwKindq, kwq } from './keyword-display.js';
2
+ import { SemanticTokenKind } from '../config/token-kind.js';
1
3
  import { kwParts, tokLowerAt, tryReadTranslatedIdent } from './context.js';
2
4
  import { TokenKind, KW } from '../frontend/tokens.js';
3
5
  import { Node } from '../ast/ast.js';
@@ -225,11 +227,11 @@ function skipInlineElseLayout(ctx) {
225
227
  function parseInlineIf(ctx, error, cond) {
226
228
  skipInlineThenLayout(ctx);
227
229
  if (!ctx.isKeyword(KW.THEN))
228
- error("Expected 'then' in inline if");
230
+ error(`Expected ${kwq(ctx, KW.THEN)} in inline if`);
229
231
  ctx.nextWord();
230
232
  // then 分支:单个 return(内联,不带句点;句点由整条 inline-if 末尾的 return 承担)。
231
233
  if (!ctx.isKeyword(KW.RETURN))
232
- error("Expected 'return' after 'then'");
234
+ error(`Expected ${kwq(ctx, KW.RETURN)} after ${kwq(ctx, KW.THEN)}`);
233
235
  const thenRetTok = ctx.peek();
234
236
  ctx.nextWord();
235
237
  const thenExpr = parseExpr(ctx, error);
@@ -258,7 +260,7 @@ function parseInlineIf(ctx, error, cond) {
258
260
  else {
259
261
  // else return X.(末尾 return,带句点)。
260
262
  if (!ctx.isKeyword(KW.RETURN))
261
- error("Expected 'return' or 'if' after 'else'");
263
+ error(`Expected ${kwq(ctx, KW.RETURN)} or ${kwq(ctx, KW.IF)} after ${kwKindq(ctx, SemanticTokenKind.OTHERWISE, 'else')}`);
262
264
  const elseRetTok = ctx.peek();
263
265
  ctx.nextWord();
264
266
  const elseExpr = parseExpr(ctx, error);
@@ -359,7 +361,7 @@ export function parseStatement(ctx, error, reportOutcomeError = error) {
359
361
  ctx.nextWord();
360
362
  const nameTok = ctx.peek();
361
363
  const name = parseIdent(ctx, error);
362
- expectKeyword(ctx, error, KW.BE, "Use 'be' in bindings: 'Let x be ...'.");
364
+ expectKeyword(ctx, error, KW.BE, `Use ${kwq(ctx, KW.BE)} in bindings: '${kw(ctx, KW.LET, 'Let')} x ${kw(ctx, KW.BE)} ...'.`);
363
365
  // Special-case lambda block form to avoid trailing '.'
364
366
  if ((ctx.isKeyword('a') && tokLowerAt(ctx, ctx.index + 1) === 'function') || ctx.isKeyword('function')) {
365
367
  if (ctx.isKeyword('a'))
@@ -367,7 +369,7 @@ export function parseStatement(ctx, error, reportOutcomeError = error) {
367
369
  const functionTok = ctx.nextWord(); // 'function'
368
370
  const params = parseParamList(ctx, error);
369
371
  expectCommaOr(ctx);
370
- expectKeyword(ctx, error, KW.PRODUCE, "Expected 'produce' and return type");
372
+ expectKeyword(ctx, error, KW.PRODUCE, `Expected ${kwq(ctx, KW.PRODUCE)} and return type`);
371
373
  let retType;
372
374
  if (ctx.at(TokenKind.COLON)) {
373
375
  retType = Node.TypeName('Unknown');
@@ -403,7 +405,7 @@ export function parseStatement(ctx, error, reportOutcomeError = error) {
403
405
  const setTok = ctx.peek();
404
406
  ctx.nextWord();
405
407
  const name = parseIdent(ctx, error);
406
- expectKeyword(ctx, error, KW.TO_WORD, "Use 'to' in assignments: 'Set x to ...'.");
408
+ expectKeyword(ctx, error, KW.TO_WORD, `Use ${kwq(ctx, KW.TO_WORD)} in assignments: '${kw(ctx, KW.SET, 'Set')} x ${kw(ctx, KW.TO_WORD)} ...'.`);
407
409
  const expr = parseExpr(ctx, error);
408
410
  expectPeriodEnd(ctx, error);
409
411
  const nd = Node.Set(name, expr);
@@ -521,9 +523,9 @@ export function parseStatement(ctx, error, reportOutcomeError = error) {
521
523
  }
522
524
  if (ctx.isKeyword(KW.WITHIN)) {
523
525
  ctx.nextWord();
524
- expectKeyword(ctx, error, KW.SCOPE, "Expected 'scope' after 'Within'");
526
+ expectKeyword(ctx, error, KW.SCOPE, `Expected ${kwq(ctx, KW.SCOPE)} after ${kwq(ctx, KW.WITHIN, 'Within')}`);
525
527
  if (!ctx.at(TokenKind.COLON))
526
- error("Expected ':' after 'scope'");
528
+ error(`Expected ':' after ${kwq(ctx, KW.SCOPE)}`);
527
529
  ctx.next();
528
530
  expectNewline(ctx, error);
529
531
  return parseBlock(ctx, error); // Lowering later
@@ -531,8 +533,8 @@ export function parseStatement(ctx, error, reportOutcomeError = error) {
531
533
  if (ctx.isKeyword(KW.START)) {
532
534
  const startTok = ctx.nextWord();
533
535
  const name = parseIdent(ctx, error);
534
- expectKeyword(ctx, error, KW.AS, "Expected 'as' after name");
535
- expectKeyword(ctx, error, KW.ASYNC, "Expected 'async'");
536
+ expectKeyword(ctx, error, KW.AS, `Expected ${kwq(ctx, KW.AS)} after name`);
537
+ expectKeyword(ctx, error, KW.ASYNC, `Expected ${kwq(ctx, KW.ASYNC)}`);
536
538
  const expr = parseExpr(ctx, error);
537
539
  expectPeriodEnd(ctx, error);
538
540
  const nd = Node.Start(name, expr);
@@ -565,9 +567,9 @@ export function parseStatement(ctx, error, reportOutcomeError = error) {
565
567
  }
566
568
  function parseWorkflow(ctx, error) {
567
569
  const workflowTok = ctx.peek();
568
- expectKeyword(ctx, error, KW.WORKFLOW, "Expected 'workflow'");
570
+ expectKeyword(ctx, error, KW.WORKFLOW, `Expected ${kwq(ctx, KW.WORKFLOW)}`);
569
571
  if (!ctx.at(TokenKind.COLON))
570
- error("Expected ':' after 'workflow'");
572
+ error(`Expected ':' after ${kwq(ctx, KW.WORKFLOW)}`);
571
573
  ctx.next();
572
574
  expectNewline(ctx, error);
573
575
  if (!ctx.at(TokenKind.INDENT))
@@ -603,14 +605,14 @@ function parseWorkflow(ctx, error) {
603
605
  }
604
606
  function parseStep(ctx, error) {
605
607
  const stepTok = ctx.peek();
606
- expectKeyword(ctx, error, KW.STEP, "Expected 'step'");
608
+ expectKeyword(ctx, error, KW.STEP, `Expected ${kwq(ctx, KW.STEP)}`);
607
609
  const name = parseIdent(ctx, error);
608
610
  const dependencies = [];
609
611
  if (ctx.isKeyword(KW.DEPENDS)) {
610
612
  ctx.nextWord();
611
- expectKeyword(ctx, error, KW.ON, "Expected 'on' after 'depends'");
613
+ expectKeyword(ctx, error, KW.ON, `Expected ${kwq(ctx, KW.ON)} after ${kwq(ctx, KW.DEPENDS)}`);
612
614
  if (!ctx.at(TokenKind.LBRACKET))
613
- error("Expected '[' after 'depends on'");
615
+ error(`Expected '[' after '${kw(ctx, KW.DEPENDS)} ${kw(ctx, KW.ON)}'`);
614
616
  ctx.next();
615
617
  if (!ctx.at(TokenKind.RBRACKET)) {
616
618
  while (true) {
@@ -642,7 +644,7 @@ function parseStep(ctx, error) {
642
644
  if (ctx.isKeyword(KW.COMPENSATE)) {
643
645
  ctx.nextWord();
644
646
  if (!ctx.at(TokenKind.COLON))
645
- error("Expected ':' after 'compensate'");
647
+ error(`Expected ':' after ${kwq(ctx, KW.COMPENSATE)}`);
646
648
  ctx.next();
647
649
  expectNewline(ctx, error);
648
650
  compensate = parseBlock(ctx, error);
@@ -654,9 +656,9 @@ function parseStep(ctx, error) {
654
656
  return step;
655
657
  }
656
658
  function parseRetryPolicy(ctx, error) {
657
- expectKeyword(ctx, error, KW.RETRY, "Expected 'retry'");
659
+ expectKeyword(ctx, error, KW.RETRY, `Expected ${kwq(ctx, KW.RETRY)}`);
658
660
  if (!ctx.at(TokenKind.COLON))
659
- error("Expected ':' after 'retry'");
661
+ error(`Expected ':' after ${kwq(ctx, KW.RETRY)}`);
660
662
  ctx.next();
661
663
  expectNewline(ctx, error);
662
664
  if (!ctx.at(TokenKind.INDENT))
@@ -669,20 +671,20 @@ function parseRetryPolicy(ctx, error) {
669
671
  if (ctx.isKeywordSeq(MAX_ATTEMPTS_PARTS)) {
670
672
  ctx.nextWords(MAX_ATTEMPTS_PARTS);
671
673
  if (!ctx.at(TokenKind.COLON))
672
- error("Expected ':' after 'max attempts'");
674
+ error(`Expected ':' after ${kwq(ctx, KW.MAX_ATTEMPTS)}`);
673
675
  ctx.next();
674
676
  if (!ctx.at(TokenKind.INT))
675
- error("Expected integer after 'max attempts'");
677
+ error(`Expected integer after ${kwq(ctx, KW.MAX_ATTEMPTS)}`);
676
678
  const attempts = ctx.next().value;
677
679
  if (attempts <= 0)
678
- error("'max attempts' must be greater than zero");
680
+ error(`${kwq(ctx, KW.MAX_ATTEMPTS)} must be greater than zero`);
679
681
  maxAttempts = attempts;
680
682
  expectPeriodEnd(ctx, error);
681
683
  }
682
684
  else if (ctx.isKeyword(KW.BACKOFF)) {
683
685
  ctx.nextWord();
684
686
  if (!ctx.at(TokenKind.COLON))
685
- error("Expected ':' after 'backoff'");
687
+ error(`Expected ':' after ${kwq(ctx, KW.BACKOFF)}`);
686
688
  ctx.next();
687
689
  const modeTok = ctx.nextWord();
688
690
  const mode = String(modeTok.value ?? '').toLowerCase();
@@ -699,15 +701,15 @@ function parseRetryPolicy(ctx, error) {
699
701
  }
700
702
  ctx.next();
701
703
  if (maxAttempts === null)
702
- error("Retry section must include 'max attempts'");
704
+ error(`Retry section must include ${kwq(ctx, KW.MAX_ATTEMPTS)}`);
703
705
  if (backoff === null)
704
- error("Retry section must include 'backoff'");
706
+ error(`Retry section must include ${kwq(ctx, KW.BACKOFF)}`);
705
707
  return { maxAttempts, backoff };
706
708
  }
707
709
  function parseTimeout(ctx, error) {
708
- expectKeyword(ctx, error, KW.TIMEOUT, "Expected 'timeout'");
710
+ expectKeyword(ctx, error, KW.TIMEOUT, `Expected ${kwq(ctx, KW.TIMEOUT)}`);
709
711
  if (!ctx.at(TokenKind.COLON))
710
- error("Expected ':' after 'timeout'");
712
+ error(`Expected ':' after ${kwq(ctx, KW.TIMEOUT)}`);
711
713
  ctx.next();
712
714
  if (!ctx.at(TokenKind.INT))
713
715
  error('Expected integer timeout value');
@@ -716,6 +718,8 @@ function parseTimeout(ctx, error) {
716
718
  if (seconds < 0)
717
719
  error('Timeout value must be non-negative');
718
720
  if (!(ctx.isKeyword('seconds') || ctx.isKeyword('second'))) {
721
+ // 'seconds' 不做本地化:所有 locale 的词表都没有时间单位的本地拼写,
722
+ // zh/de 作者同样只能写英文 seconds,报错里的 'seconds' 正是作者该写的词。
719
723
  error("Timeout must specify time unit 'seconds'");
720
724
  }
721
725
  ctx.nextWord();
@@ -739,7 +743,7 @@ function parseCases(ctx, error) {
739
743
  while (!ctx.at(TokenKind.DEDENT)) {
740
744
  if (!ctx.isKeyword(KW.WHEN)) {
741
745
  // 改进的错误消息,提示复合模式关系
742
- error("Expected 'When' (若...为 pattern requires '为' after '若')");
746
+ error(`Expected ${kwq(ctx, KW.WHEN, 'When')} (若...为 pattern requires '为' after '若')`);
743
747
  }
744
748
  const whenTok = ctx.nextWord();
745
749
  const pat = parsePattern(ctx, error);
@@ -816,12 +820,13 @@ function parseIfExpr(ctx, error) {
816
820
  const cond = parseOr(ctx, error);
817
821
  skipInlineThenLayout(ctx);
818
822
  if (!ctx.isKeyword(KW.THEN))
819
- error("Expected 'then' in if-expression");
823
+ error(`Expected ${kwq(ctx, KW.THEN)} in if-expression`);
820
824
  ctx.nextWord(); // 消费 THEN
821
825
  const thenE = parseExpr(ctx, error);
822
826
  skipInlineElseLayout(ctx);
823
827
  if (!ctx.isKeyword(KW.ELSE) && !ctx.isKeyword(KW.OTHERWISE)) {
824
- error("Expected 'else' in if-expression (else is mandatory for expression-level if)");
828
+ error(`Expected ${kwKindq(ctx, SemanticTokenKind.OTHERWISE, 'else')} in if-expression ` +
829
+ `(${kwKind(ctx, SemanticTokenKind.OTHERWISE, 'else')} is mandatory for expression-level if)`);
825
830
  }
826
831
  ctx.nextWord(); // 消费 ELSE / OTHERWISE
827
832
  const elseE = parseExpr(ctx, error);
@@ -1293,7 +1298,7 @@ function parseApplyOrPrimary(ctx, error) {
1293
1298
  if (ctx.isKeyword(KW.APPLY) && applyLooksLikeCall(ctx)) {
1294
1299
  const applyTok = ctx.nextWord();
1295
1300
  const fn = parseCallTargetName(ctx, error);
1296
- expectKeyword(ctx, error, KW.TO_WORD, "Use 'apply <fn> to <arg>' (expected 'to').");
1301
+ expectKeyword(ctx, error, KW.TO_WORD, `Use '${kw(ctx, KW.APPLY)} <fn> ${kw(ctx, KW.TO_WORD)} <arg>' (expected ${kwq(ctx, KW.TO_WORD)}).`);
1297
1302
  const arg = parseExpr(ctx, error);
1298
1303
  const call = Node.Call(fn, [arg]);
1299
1304
  assignSpan(call, spanFromSources(applyTok, arg));
@@ -1332,7 +1337,7 @@ function applyLooksLikeCall(ctx) {
1332
1337
  function parseCallTargetName(ctx, error) {
1333
1338
  const startTok = ctx.peek();
1334
1339
  if (!ctx.at(TokenKind.IDENT) && !ctx.at(TokenKind.TYPE_IDENT)) {
1335
- error("Expected a function name after 'apply'.");
1340
+ error(`Expected a function name after ${kwq(ctx, KW.APPLY)}.`);
1336
1341
  }
1337
1342
  const consumed = [ctx.peek()];
1338
1343
  let full = ctx.at(TokenKind.IDENT) ? parseIdent(ctx, error) : ctx.next().value;
@@ -1402,7 +1407,7 @@ function operatorWordBeforeParen(ctx, english) {
1402
1407
  function parseOperatorWordCall(ctx, error, word, english) {
1403
1408
  const symbol = english ? null : OPERATOR_WORD_SYMBOLS.get(word);
1404
1409
  if (!symbol) {
1405
- error(`运算符词 '${word}' 不能用作前缀调用;只有符号运算符 ${PREFIX_SYMBOL_LIST} 可以写成 op(a, b)`);
1410
+ error(`运算符词 ${kwq(ctx, word)} 不能用作前缀调用;只有符号运算符 ${PREFIX_SYMBOL_LIST} 可以写成 op(a, b)`);
1406
1411
  }
1407
1412
  return parsePrefixOperatorCall(ctx, error, symbol, kwParts(word).length);
1408
1413
  }
@@ -1434,7 +1439,7 @@ function parsePrimary(ctx, error) {
1434
1439
  const functionTok = ctx.nextWord(); // 'function'
1435
1440
  const params = parseParamList(ctx, error);
1436
1441
  expectCommaOr(ctx);
1437
- expectKeyword(ctx, error, KW.PRODUCE, "Expected 'produce' and return type");
1442
+ expectKeyword(ctx, error, KW.PRODUCE, `Expected ${kwq(ctx, KW.PRODUCE)} and return type`);
1438
1443
  let retType;
1439
1444
  if (ctx.at(TokenKind.COLON)) {
1440
1445
  retType = Node.TypeName('Unknown');
@@ -1658,11 +1663,11 @@ function parsePrimary(ctx, error) {
1658
1663
  const nameTok = ctx.peek();
1659
1664
  const name = parseIdent(ctx, error);
1660
1665
  if (!ctx.isKeyword(KW.SET))
1661
- error("Expected 'set to' in construction");
1666
+ error(`Expected '${kw(ctx, KW.SET)} ${kw(ctx, KW.TO_WORD)}' in construction`);
1662
1667
  const equalsTok = ctx.peek();
1663
1668
  ctx.nextWord();
1664
1669
  if (!ctx.isKeyword(KW.TO_WORD))
1665
- error("Expected 'to' after 'set' in construction");
1670
+ error(`Expected ${kwq(ctx, KW.TO_WORD)} after ${kwq(ctx, KW.SET)} in construction`);
1666
1671
  ctx.nextWord();
1667
1672
  const e = parseExpr(ctx, error);
1668
1673
  const fld = {
@@ -1718,7 +1723,7 @@ function parsePrimary(ctx, error) {
1718
1723
  // 仅拦截裸类型名(无点链后缀),点链形态(如 SomeType.method(...))不受影响。
1719
1724
  if (full === typeName && ctx.declaredRecordTypes.has(typeName)) {
1720
1725
  error(`Cannot construct '${typeName}' with positional arguments. ` +
1721
- `Use named fields instead: '${typeName} with <field> set to <value> and ...'`);
1726
+ `Use named fields instead: '${typeName} ${kw(ctx, KW.WITH)} <field> ${kw(ctx, KW.SET)} ${kw(ctx, KW.TO_WORD)} <value> ${kw(ctx, KW.AND)} ...'`);
1722
1727
  }
1723
1728
  const args = parseArgList(ctx, error);
1724
1729
  const call = Node.Call(target, args);
@@ -1756,7 +1761,7 @@ function parsePrimary(ctx, error) {
1756
1761
  // 同 TYPE_IDENT 分支:拦截对已声明类型的位置式构造(无大小写语言里类型名是 IDENT)。
1757
1762
  if (full === name && ctx.declaredRecordTypes.has(name)) {
1758
1763
  error(`Cannot construct '${name}' with positional arguments. ` +
1759
- `Use named fields instead: '${name} with <field> set to <value> and ...'`);
1764
+ `Use named fields instead: '${name} ${kw(ctx, KW.WITH)} <field> ${kw(ctx, KW.SET)} ${kw(ctx, KW.TO_WORD)} <value> ${kw(ctx, KW.AND)} ...'`);
1760
1765
  }
1761
1766
  const args = parseArgList(ctx, error);
1762
1767
  const call = Node.Call(target, args);