@aster-cloud/aster-lang-ts 1.0.6 → 1.0.9

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 (102) hide show
  1. package/dist/src/ast/ast.d.ts +1 -0
  2. package/dist/src/ast/ast.d.ts.map +1 -1
  3. package/dist/src/ast/ast.js +1 -0
  4. package/dist/src/ast/ast.js.map +1 -1
  5. package/dist/src/config/lexicons/de-DE.d.ts.map +1 -1
  6. package/dist/src/config/lexicons/de-DE.js +1 -0
  7. package/dist/src/config/lexicons/de-DE.js.map +1 -1
  8. package/dist/src/config/lexicons/en-US.d.ts.map +1 -1
  9. package/dist/src/config/lexicons/en-US.js +1 -0
  10. package/dist/src/config/lexicons/en-US.js.map +1 -1
  11. package/dist/src/config/lexicons/hi-IN.d.ts.map +1 -1
  12. package/dist/src/config/lexicons/hi-IN.js +1 -0
  13. package/dist/src/config/lexicons/hi-IN.js.map +1 -1
  14. package/dist/src/config/lexicons/template.d.ts.map +1 -1
  15. package/dist/src/config/lexicons/template.js +1 -0
  16. package/dist/src/config/lexicons/template.js.map +1 -1
  17. package/dist/src/config/lexicons/zh-CN.d.ts.map +1 -1
  18. package/dist/src/config/lexicons/zh-CN.js +1 -0
  19. package/dist/src/config/lexicons/zh-CN.js.map +1 -1
  20. package/dist/src/config/semantic.d.ts +1 -0
  21. package/dist/src/config/semantic.d.ts.map +1 -1
  22. package/dist/src/config/semantic.js +1 -0
  23. package/dist/src/config/semantic.js.map +1 -1
  24. package/dist/src/config/token-kind.d.ts +3 -0
  25. package/dist/src/config/token-kind.d.ts.map +1 -1
  26. package/dist/src/config/token-kind.js +3 -0
  27. package/dist/src/config/token-kind.js.map +1 -1
  28. package/dist/src/core/core_ir.d.ts +1 -0
  29. package/dist/src/core/core_ir.d.ts.map +1 -1
  30. package/dist/src/core/core_ir.js +1 -0
  31. package/dist/src/core/core_ir.js.map +1 -1
  32. package/dist/src/core/interpreter.d.ts.map +1 -1
  33. package/dist/src/core/interpreter.js +161 -0
  34. package/dist/src/core/interpreter.js.map +1 -1
  35. package/dist/src/core/pretty_core.d.ts.map +1 -1
  36. package/dist/src/core/pretty_core.js +1 -0
  37. package/dist/src/core/pretty_core.js.map +1 -1
  38. package/dist/src/core/visitor.d.ts.map +1 -1
  39. package/dist/src/core/visitor.js +1 -0
  40. package/dist/src/core/visitor.js.map +1 -1
  41. package/dist/src/diagnostics/diagnostics.d.ts +2 -0
  42. package/dist/src/diagnostics/diagnostics.d.ts.map +1 -1
  43. package/dist/src/diagnostics/diagnostics.js +6 -0
  44. package/dist/src/diagnostics/diagnostics.js.map +1 -1
  45. package/dist/src/diagnostics/error_codes.d.ts +1 -0
  46. package/dist/src/diagnostics/error_codes.d.ts.map +1 -1
  47. package/dist/src/diagnostics/error_codes.js +2 -0
  48. package/dist/src/diagnostics/error_codes.js.map +1 -1
  49. package/dist/src/frontend/lexer.d.ts +7 -0
  50. package/dist/src/frontend/lexer.d.ts.map +1 -1
  51. package/dist/src/frontend/lexer.js +37 -2
  52. package/dist/src/frontend/lexer.js.map +1 -1
  53. package/dist/src/lower_to_core.js +2 -0
  54. package/dist/src/lower_to_core.js.map +1 -1
  55. package/dist/src/parser/expr-stmt-parser.d.ts.map +1 -1
  56. package/dist/src/parser/expr-stmt-parser.js +219 -41
  57. package/dist/src/parser/expr-stmt-parser.js.map +1 -1
  58. package/dist/src/typecheck/expression.d.ts.map +1 -1
  59. package/dist/src/typecheck/expression.js +55 -0
  60. package/dist/src/typecheck/expression.js.map +1 -1
  61. package/dist/src/typecheck/type_system.d.ts.map +1 -1
  62. package/dist/src/typecheck/type_system.js +9 -1
  63. package/dist/src/typecheck/type_system.js.map +1 -1
  64. package/dist/src/types/base.d.ts +10 -0
  65. package/dist/src/types/base.d.ts.map +1 -1
  66. package/dist/src/types.d.ts +10 -2
  67. package/dist/src/types.d.ts.map +1 -1
  68. package/dist/src/types.js +1 -0
  69. package/dist/src/types.js.map +1 -1
  70. package/dist/test/unit/alias-poem-story.test.d.ts +2 -0
  71. package/dist/test/unit/alias-poem-story.test.d.ts.map +1 -0
  72. package/dist/test/unit/alias-poem-story.test.js +106 -0
  73. package/dist/test/unit/alias-poem-story.test.js.map +1 -0
  74. package/dist/test/unit/apply-call.test.d.ts +2 -0
  75. package/dist/test/unit/apply-call.test.d.ts.map +1 -0
  76. package/dist/test/unit/apply-call.test.js +79 -0
  77. package/dist/test/unit/apply-call.test.js.map +1 -0
  78. package/dist/test/unit/date-builtins.test.d.ts +2 -0
  79. package/dist/test/unit/date-builtins.test.d.ts.map +1 -0
  80. package/dist/test/unit/date-builtins.test.js +74 -0
  81. package/dist/test/unit/date-builtins.test.js.map +1 -0
  82. package/dist/test/unit/decimal-literals.test.d.ts +2 -0
  83. package/dist/test/unit/decimal-literals.test.d.ts.map +1 -0
  84. package/dist/test/unit/decimal-literals.test.js +76 -0
  85. package/dist/test/unit/decimal-literals.test.js.map +1 -0
  86. package/dist/test/unit/decimal-mixing-typecheck.test.d.ts +2 -0
  87. package/dist/test/unit/decimal-mixing-typecheck.test.d.ts.map +1 -0
  88. package/dist/test/unit/decimal-mixing-typecheck.test.js +80 -0
  89. package/dist/test/unit/decimal-mixing-typecheck.test.js.map +1 -0
  90. package/dist/test/unit/decimal-round-divide.test.d.ts +2 -0
  91. package/dist/test/unit/decimal-round-divide.test.d.ts.map +1 -0
  92. package/dist/test/unit/decimal-round-divide.test.js +53 -0
  93. package/dist/test/unit/decimal-round-divide.test.js.map +1 -0
  94. package/dist/test/unit/multiline-continuation.test.d.ts +2 -0
  95. package/dist/test/unit/multiline-continuation.test.d.ts.map +1 -0
  96. package/dist/test/unit/multiline-continuation.test.js +87 -0
  97. package/dist/test/unit/multiline-continuation.test.js.map +1 -0
  98. package/dist/test/unit/tides-ballad.test.d.ts +2 -0
  99. package/dist/test/unit/tides-ballad.test.d.ts.map +1 -0
  100. package/dist/test/unit/tides-ballad.test.js +80 -0
  101. package/dist/test/unit/tides-ballad.test.js.map +1 -0
  102. package/package.json +3 -2
@@ -710,6 +710,48 @@ function isListSeparatorAnd(ctx) {
710
710
  }
711
711
  return false;
712
712
  }
713
+ /**
714
+ * 等缩进多行表达式续行(ADR 0026)。二元运算循环判断「是否还有下一个运算符」时调用:
715
+ * 若当前是 NEWLINE,先跨过连续 NEWLINE(**遇 INDENT/DEDENT/EOF 即放弃**,绝不消费缩进
716
+ * token),再用调用方的 `isOpAtCursor()`(在游标处、offset 0 判定本层运算符)测试续行行首;
717
+ * 命中则保留已消费的 NEWLINE、返回 true;未命中则回退游标、返回 false(一个 token 都不动)。
718
+ *
719
+ * 安全性(Codex 审查 019f157b):只跨等缩进的 NEWLINE,**绝不碰 INDENT/DEDENT**——缩进栈不动、
720
+ * 块结构零风险。只在已处于 expr 循环内、续行行首确为本层运算符时触发,与 block 发起新语句不冲突。
721
+ *
722
+ * @param isOpAtCursor 谓词:在当前游标(offset 0)判断是否为本层运算符起点(复用各循环原有判定)。
723
+ */
724
+ function tryContinuation(ctx, isOpAtCursor) {
725
+ if (!ctx.at(TokenKind.NEWLINE))
726
+ return false;
727
+ const saved = ctx.index;
728
+ while (ctx.at(TokenKind.NEWLINE))
729
+ ctx.next();
730
+ // 跨过 NEWLINE 后若是缩进变化(INDENT/DEDENT)或文件尾,则非等缩进续行——回退。
731
+ if (ctx.at(TokenKind.INDENT) || ctx.at(TokenKind.DEDENT) || ctx.at(TokenKind.EOF)) {
732
+ ctx.index = saved;
733
+ return false;
734
+ }
735
+ if (!isOpAtCursor()) {
736
+ ctx.index = saved;
737
+ return false;
738
+ }
739
+ return true; // 已停在续行行首的运算符上,NEWLINE 已消费。
740
+ }
741
+ /**
742
+ * 取右操作数前跳过等缩进续行换行(支持行尾运算符续行 `a then\n b`)。仅当其后确有可解析的操作数
743
+ * (非 INDENT/DEDENT/EOF/DOT)时才跳,避免误吞语句边界换行;否则回退不动。
744
+ */
745
+ function skipOperandContinuation(ctx) {
746
+ if (!ctx.at(TokenKind.NEWLINE))
747
+ return;
748
+ const saved = ctx.index;
749
+ while (ctx.at(TokenKind.NEWLINE))
750
+ ctx.next();
751
+ if (ctx.at(TokenKind.INDENT) || ctx.at(TokenKind.DEDENT) || ctx.at(TokenKind.EOF) || ctx.at(TokenKind.DOT)) {
752
+ ctx.index = saved;
753
+ }
754
+ }
713
755
  /**
714
756
  * 解析逻辑或(or)
715
757
  * 优先级最低;左结合;短路语义在求值器处理。
@@ -717,8 +759,9 @@ function isListSeparatorAnd(ctx) {
717
759
  */
718
760
  function parseOr(ctx, error) {
719
761
  let left = parseAnd(ctx, error);
720
- while (ctx.isKeyword(KW.OR)) {
762
+ while (ctx.isKeyword(KW.OR) || tryContinuation(ctx, () => ctx.isKeyword(KW.OR))) {
721
763
  const opTok = ctx.nextWord();
764
+ skipOperandContinuation(ctx);
722
765
  const right = parseAnd(ctx, error);
723
766
  const target = assignTokenSpan(Node.Name('or'), opTok);
724
767
  const call = Node.Call(target, [left, right]);
@@ -733,8 +776,10 @@ function parseOr(ctx, error) {
733
776
  */
734
777
  function parseAnd(ctx, error) {
735
778
  let left = parseNot(ctx, error);
736
- while (ctx.isKeyword(KW.AND) && !isListSeparatorAnd(ctx)) {
779
+ const isAndOp = () => ctx.isKeyword(KW.AND) && !isListSeparatorAnd(ctx);
780
+ while (isAndOp() || tryContinuation(ctx, isAndOp)) {
737
781
  const opTok = ctx.nextWord();
782
+ skipOperandContinuation(ctx);
738
783
  const right = parseNot(ctx, error);
739
784
  const target = assignTokenSpan(Node.Name('and'), opTok);
740
785
  const call = Node.Call(target, [left, right]);
@@ -781,6 +826,7 @@ function parseComparison(ctx, error) {
781
826
  // 辅助:消费符号运算符并构造 Call 节点
782
827
  const consumeSymbol = (irName) => {
783
828
  const opTok = ctx.next(); // 消费符号 token
829
+ skipOperandContinuation(ctx); // ADR 0026:行尾运算符续行。
784
830
  const right = parseAddition(ctx, error);
785
831
  const target = assignTokenSpan(Node.Name(irName), opTok);
786
832
  const call = Node.Call(target, [left, right]);
@@ -796,6 +842,7 @@ function parseComparison(ctx, error) {
796
842
  else {
797
843
  ctx.nextWord();
798
844
  }
845
+ skipOperandContinuation(ctx); // ADR 0026:行尾运算符续行。
799
846
  const right = parseAddition(ctx, error);
800
847
  const target = assignTokenSpan(Node.Name(irName), opTok);
801
848
  const call = Node.Call(target, [left, right]);
@@ -823,8 +870,37 @@ function parseComparison(ctx, error) {
823
870
  }
824
871
  }
825
872
  };
873
+ // 本层(比较)运算符在游标处的判定:覆盖所有符号 + 词形分支(含可选 `is` 前缀形)。
874
+ const isCompareOp = () => {
875
+ if (ctx.at(TokenKind.LT) || ctx.at(TokenKind.GT) || ctx.at(TokenKind.LTE) || ctx.at(TokenKind.GTE) ||
876
+ ctx.at(TokenKind.NEQ) || ctx.at(TokenKind.EQ) || ctx.at(TokenKind.EQUALS))
877
+ return true;
878
+ if (ctx.isKeywordSeq(isNotEqualToParts) || ctx.isKeywordSeq(notEqualToParts) ||
879
+ ctx.isKeyword(KW.LESS_THAN) || ctx.isKeywordSeq(lessThanParts) ||
880
+ ctx.isKeyword(KW.GREATER_THAN) || ctx.isKeywordSeq(greaterThanParts) ||
881
+ ctx.isKeywordSeq(isEqualToParts) || ctx.isKeyword(KW.EQUALS_TO) || ctx.isKeywordSeq(equalsToParts) ||
882
+ ctx.isKeyword(KW.AT_LEAST) || ctx.isKeywordSeq(atLeastParts) ||
883
+ ctx.isKeyword(KW.AT_MOST) || ctx.isKeywordSeq(atMostParts) ||
884
+ ctx.isKeyword(KW.UNDER) || ctx.isKeyword(KW.MORE_THAN) || ctx.isKeywordSeq(moreThanParts) || ctx.isKeyword(KW.OVER))
885
+ return true;
886
+ // 可选 `is` 前缀形:is at least / is at most / is greater than / ...
887
+ for (const parts of isComparatorPrefixParts) {
888
+ if (ctx.isKeywordSeq(['is', ...parts]))
889
+ return true;
890
+ }
891
+ return false;
892
+ };
826
893
  let more = true;
894
+ let consumedComparison = false; // ADR 0026:续行不扩大「链式比较」面(Java comparisonExpr 单比较)。
827
895
  while (more) {
896
+ // ADR 0026:等缩进续行——内联无比较运算符时尝试跨 NEWLINE 续接。但**不跨行续接第二个比较**
897
+ // (Codex 审查 019f157b §5):Java comparisonExpr 是单比较非链式,若 TS 跨行续接第二个比较会
898
+ // 与 Java 分歧。故续行只在尚未消费过比较时尝试(首个比较的行尾/行首续行仍支持,仅禁「比较\n比较」)。
899
+ if (!isCompareOp() && (consumedComparison || !tryContinuation(ctx, isCompareOp))) {
900
+ more = false;
901
+ continue;
902
+ }
903
+ consumedComparison = true;
828
904
  tryAbsorbIsPrefix();
829
905
  // 符号运算符:<, >, <=, >=, !=, ==, =
830
906
  if (ctx.at(TokenKind.LT)) {
@@ -897,43 +973,39 @@ function parseComparison(ctx, error) {
897
973
  */
898
974
  function parseAddition(ctx, error) {
899
975
  let left = parseMultiplication(ctx, error);
976
+ // 本层(加减)运算符在游标处的判定:符号 + / -,或词形 plus / minus。
977
+ const isAddOp = () => ctx.at(TokenKind.PLUS) || ctx.at(TokenKind.MINUS) || ctx.isKeyword(KW.PLUS) || ctx.isKeyword(KW.MINUS);
900
978
  let more = true;
901
979
  while (more) {
980
+ // ADR 0026:等缩进续行——内联无运算符时,尝试跨 NEWLINE 续接(命中则游标已停在运算符上)。
981
+ if (!isAddOp() && !tryContinuation(ctx, isAddOp)) {
982
+ more = false;
983
+ continue;
984
+ }
985
+ let opTok;
986
+ let irName;
902
987
  if (ctx.at(TokenKind.PLUS)) {
903
- const opTok = ctx.next();
904
- const right = parseMultiplication(ctx, error);
905
- const target = assignTokenSpan(Node.Name('+'), opTok);
906
- const call = Node.Call(target, [left, right]);
907
- assignSpan(call, spanFromSources(left, opTok, right));
908
- left = call;
988
+ opTok = ctx.next();
989
+ irName = '+';
909
990
  }
910
991
  else if (ctx.at(TokenKind.MINUS)) {
911
- const opTok = ctx.next();
912
- const right = parseMultiplication(ctx, error);
913
- const target = assignTokenSpan(Node.Name('-'), opTok);
914
- const call = Node.Call(target, [left, right]);
915
- assignSpan(call, spanFromSources(left, opTok, right));
916
- left = call;
992
+ opTok = ctx.next();
993
+ irName = '-';
917
994
  }
918
995
  else if (ctx.isKeyword(KW.PLUS)) {
919
- const opTok = ctx.nextWord();
920
- const right = parseMultiplication(ctx, error);
921
- const target = assignTokenSpan(Node.Name('+'), opTok);
922
- const call = Node.Call(target, [left, right]);
923
- assignSpan(call, spanFromSources(left, opTok, right));
924
- left = call;
925
- }
926
- else if (ctx.isKeyword(KW.MINUS)) {
927
- const opTok = ctx.nextWord();
928
- const right = parseMultiplication(ctx, error);
929
- const target = assignTokenSpan(Node.Name('-'), opTok);
930
- const call = Node.Call(target, [left, right]);
931
- assignSpan(call, spanFromSources(left, opTok, right));
932
- left = call;
996
+ opTok = ctx.nextWord();
997
+ irName = '+';
933
998
  }
934
999
  else {
935
- more = false;
1000
+ opTok = ctx.nextWord();
1001
+ irName = '-';
936
1002
  }
1003
+ skipOperandContinuation(ctx); // 行尾运算符续行:取右操作数前跳等缩进换行。
1004
+ const right = parseMultiplication(ctx, error);
1005
+ const target = assignTokenSpan(Node.Name(irName), opTok);
1006
+ const call = Node.Call(target, [left, right]);
1007
+ assignSpan(call, spanFromSources(left, opTok, right));
1008
+ left = call;
937
1009
  }
938
1010
  return left;
939
1011
  }
@@ -944,14 +1016,25 @@ function parseAddition(ctx, error) {
944
1016
  * @returns Expression 节点
945
1017
  */
946
1018
  function parseMultiplication(ctx, error) {
947
- let left = parsePrimary(ctx, error);
1019
+ let left = parseApplyOrPrimary(ctx, error);
948
1020
  const dividedByParts = kwParts(KW.DIVIDED_BY);
949
1021
  const intDividedByParts = kwParts(KW.INTEGER_DIVIDED_BY);
1022
+ // 本层(乘除模)运算符在游标处的判定:符号 * /,或词形 times / divided by / integer divided by / modulo。
1023
+ const isMulOp = () => ctx.at(TokenKind.STAR) || ctx.at(TokenKind.SLASH) ||
1024
+ ctx.isKeyword(KW.TIMES) || ctx.isKeyword(KW.MODULO) ||
1025
+ ctx.isKeyword(KW.INTEGER_DIVIDED_BY) || ctx.isKeywordSeq(intDividedByParts) ||
1026
+ ctx.isKeyword(KW.DIVIDED_BY) || ctx.isKeywordSeq(dividedByParts);
950
1027
  let more = true;
951
1028
  while (more) {
1029
+ // ADR 0026:等缩进续行——内联无运算符时,尝试跨 NEWLINE 续接。
1030
+ if (!isMulOp() && !tryContinuation(ctx, isMulOp)) {
1031
+ more = false;
1032
+ continue;
1033
+ }
952
1034
  if (ctx.at(TokenKind.STAR)) {
953
1035
  const opTok = ctx.next();
954
- const right = parsePrimary(ctx, error);
1036
+ skipOperandContinuation(ctx);
1037
+ const right = parseApplyOrPrimary(ctx, error);
955
1038
  const target = assignTokenSpan(Node.Name('*'), opTok);
956
1039
  const call = Node.Call(target, [left, right]);
957
1040
  assignSpan(call, spanFromSources(left, opTok, right));
@@ -959,7 +1042,8 @@ function parseMultiplication(ctx, error) {
959
1042
  }
960
1043
  else if (ctx.at(TokenKind.SLASH)) {
961
1044
  const opTok = ctx.next();
962
- const right = parsePrimary(ctx, error);
1045
+ skipOperandContinuation(ctx);
1046
+ const right = parseApplyOrPrimary(ctx, error);
963
1047
  const target = assignTokenSpan(Node.Name('/'), opTok);
964
1048
  const call = Node.Call(target, [left, right]);
965
1049
  assignSpan(call, spanFromSources(left, opTok, right));
@@ -967,7 +1051,8 @@ function parseMultiplication(ctx, error) {
967
1051
  }
968
1052
  else if (ctx.isKeyword(KW.TIMES)) {
969
1053
  const opTok = ctx.nextWord();
970
- const right = parsePrimary(ctx, error);
1054
+ skipOperandContinuation(ctx);
1055
+ const right = parseApplyOrPrimary(ctx, error);
971
1056
  const target = assignTokenSpan(Node.Name('*'), opTok);
972
1057
  const call = Node.Call(target, [left, right]);
973
1058
  assignSpan(call, spanFromSources(left, opTok, right));
@@ -983,7 +1068,8 @@ function parseMultiplication(ctx, error) {
983
1068
  else {
984
1069
  ctx.nextWord();
985
1070
  }
986
- const right = parsePrimary(ctx, error);
1071
+ skipOperandContinuation(ctx);
1072
+ const right = parseApplyOrPrimary(ctx, error);
987
1073
  const target = assignTokenSpan(Node.Name('//'), opTok);
988
1074
  const call = Node.Call(target, [left, right]);
989
1075
  assignSpan(call, spanFromSources(left, opTok, right));
@@ -997,24 +1083,23 @@ function parseMultiplication(ctx, error) {
997
1083
  else {
998
1084
  ctx.nextWord();
999
1085
  }
1000
- const right = parsePrimary(ctx, error);
1086
+ skipOperandContinuation(ctx);
1087
+ const right = parseApplyOrPrimary(ctx, error);
1001
1088
  const target = assignTokenSpan(Node.Name('/'), opTok);
1002
1089
  const call = Node.Call(target, [left, right]);
1003
1090
  assignSpan(call, spanFromSources(left, opTok, right));
1004
1091
  left = call;
1005
1092
  }
1006
- else if (ctx.isKeyword(KW.MODULO)) {
1007
- // 取模,降级为 Call(Name('%'))。单词关键字,无子序列冲突。
1093
+ else {
1094
+ // KW.MODULO(已由 isMulOp 保证命中其一)。取模,降级为 Call(Name('%'))。
1008
1095
  const opTok = ctx.nextWord();
1009
- const right = parsePrimary(ctx, error);
1096
+ skipOperandContinuation(ctx);
1097
+ const right = parseApplyOrPrimary(ctx, error);
1010
1098
  const target = assignTokenSpan(Node.Name('%'), opTok);
1011
1099
  const call = Node.Call(target, [left, right]);
1012
1100
  assignSpan(call, spanFromSources(left, opTok, right));
1013
1101
  left = call;
1014
1102
  }
1015
- else {
1016
- more = false;
1017
- }
1018
1103
  }
1019
1104
  return left;
1020
1105
  }
@@ -1024,6 +1109,92 @@ function parseMultiplication(ctx, error) {
1024
1109
  * @param error 错误报告函数
1025
1110
  * @returns Expression 节点
1026
1111
  */
1112
+ /**
1113
+ * 无括号单参调用 `apply <fn> to <arg>`(ADR 0027)。在 parsePrimary 之上的一层(与 Java
1114
+ * unaryExpr 的 applyExpr 同层,保 parity):
1115
+ * - `apply` 是**软关键词**:仅当其后形如 `<callTarget> to`(名/限定名 + to)时才作调用引入词;
1116
+ * 否则(如名为 apply 的变量引用)回退当普通 primary。这样现有 `Rule apply given …` 不破。
1117
+ * - `<fn>` 用 parseCallTargetName(仅裸名/限定名点链,**不吃调用后缀/构造**),与 Java callTarget
1118
+ * 一致——否则 TS 会吃 `apply f(y) to x` 而 Java 不会 → 分歧。
1119
+ * - `<arg>` 取顶层 parseExpr(贪婪):`apply f to a plus b` ≡ `Call(f, [a plus b])`。
1120
+ * lower 成现有 Call(fn, [arg]),零新节点。
1121
+ */
1122
+ // 所有规范关键词的小写文本集合(token 进 parser 前已 canonicalize 成英文 KW 值)。
1123
+ const ALL_KEYWORDS = new Set(Object.values(KW).map((k) => k.toLowerCase()));
1124
+ // apply 调用目标段(callTarget segment)允许的**软关键词**——与 Java AsterParser.g4
1125
+ // `callTargetSegment : IDENT | TYPE_IDENT | MAP | structKeywordName` 的放行集逐一对齐
1126
+ // (Codex 审查 019f1639 第三处分歧)。普通标识符天然允许;这里只额外放行这些词性上是
1127
+ // 关键词、但在标识符位置当软关键字的词。其余硬关键词(and/or/not/with/to/given/produce/set/…)
1128
+ // **不**可作目标名——否则 TS(上下文关键词模型,把它们 lex 成 IDENT)会接受、Java 拒绝。
1129
+ const APPLY_TARGET_SOFT_KEYWORDS = new Set([
1130
+ KW.LET, KW.MATCH, KW.IF, KW.RETURN, KW.RULE, KW.DEFINE, KW.WHEN,
1131
+ KW.START, KW.OTHERWISE, KW.ELSE, KW.THEN, KW.APPLY,
1132
+ // 字面量:这些是 Java structKeywordName/MAP 成员,但 TS 的 KW 对象未必有同名键。
1133
+ 'wait', 'map', 'max', 'attempts',
1134
+ ].map((k) => k.toLowerCase()));
1135
+ /** 该词可否作 apply 调用目标的一段名字?非关键词→可;软关键词→可;其余硬关键词→否(对齐 Java)。 */
1136
+ function isValidCallTargetWord(word) {
1137
+ const lower = word.toLowerCase();
1138
+ return !ALL_KEYWORDS.has(lower) || APPLY_TARGET_SOFT_KEYWORDS.has(lower);
1139
+ }
1140
+ function parseApplyOrPrimary(ctx, error) {
1141
+ if (ctx.isKeyword(KW.APPLY) && applyLooksLikeCall(ctx)) {
1142
+ const applyTok = ctx.nextWord();
1143
+ const fn = parseCallTargetName(ctx, error);
1144
+ expectKeyword(ctx, error, KW.TO_WORD, "Use 'apply <fn> to <arg>' (expected 'to').");
1145
+ const arg = parseExpr(ctx, error);
1146
+ const call = Node.Call(fn, [arg]);
1147
+ assignSpan(call, spanFromSources(applyTok, arg));
1148
+ return call;
1149
+ }
1150
+ return parsePrimary(ctx, error);
1151
+ }
1152
+ /**
1153
+ * 软关键词消歧:`apply` 之后是否形如 `<名>(.<名>)* to`?是 → 当调用引入词;否 → apply 是普通名。
1154
+ * 只前瞻不移动游标。
1155
+ */
1156
+ function applyLooksLikeCall(ctx) {
1157
+ // offset 0 = apply。期望 offset 1 是名(IDENT/TYPE_IDENT),随后零或多个 `.名`,再是 `to`。
1158
+ // 每段名字须是合法调用目标词(对齐 Java callTargetSegment 放行集)——TS 把 and/or/with/to 等
1159
+ // 硬关键词 lex 成 IDENT,若不在此过滤,`apply and to x` 会 TS 收、Java 拒(Codex 019f1639 #3)。
1160
+ let off = 1;
1161
+ const seg = ctx.peek(off);
1162
+ if (seg.kind !== TokenKind.IDENT && seg.kind !== TokenKind.TYPE_IDENT)
1163
+ return false;
1164
+ if (typeof seg.value !== 'string' || !isValidCallTargetWord(seg.value))
1165
+ return false;
1166
+ off++;
1167
+ while (ctx.peek(off).kind === TokenKind.DOT &&
1168
+ (ctx.peek(off + 1).kind === TokenKind.IDENT || ctx.peek(off + 1).kind === TokenKind.TYPE_IDENT)) {
1169
+ const part = ctx.peek(off + 1);
1170
+ if (typeof part.value !== 'string' || !isValidCallTargetWord(part.value))
1171
+ return false;
1172
+ off += 2;
1173
+ }
1174
+ // 其后须是 `to`(TO_WORD)。tokLowerAt 按非 trivia 顺序,与 peek 偏移口径需一致——用 peek 文本判定。
1175
+ const t = ctx.peek(off);
1176
+ return (t.kind === TokenKind.IDENT || t.kind === TokenKind.KEYWORD) &&
1177
+ typeof t.value === 'string' && t.value.toLowerCase() === KW.TO_WORD;
1178
+ }
1179
+ /** 解析调用目标名:裸名或限定名点链(不吃 `(`、不构造 Call/Construct)。与 Java callTarget 对齐。 */
1180
+ function parseCallTargetName(ctx, error) {
1181
+ const startTok = ctx.peek();
1182
+ if (!ctx.at(TokenKind.IDENT) && !ctx.at(TokenKind.TYPE_IDENT)) {
1183
+ error("Expected a function name after 'apply'.");
1184
+ }
1185
+ const consumed = [ctx.peek()];
1186
+ let full = ctx.at(TokenKind.IDENT) ? parseIdent(ctx, error) : ctx.next().value;
1187
+ while (ctx.at(TokenKind.DOT) &&
1188
+ (ctx.peek(1).kind === TokenKind.IDENT || ctx.peek(1).kind === TokenKind.TYPE_IDENT)) {
1189
+ consumed.push(ctx.next()); // dot
1190
+ const partTok = ctx.peek();
1191
+ full += '.' + (ctx.at(TokenKind.IDENT) ? parseIdent(ctx, error) : ctx.next().value);
1192
+ consumed.push(partTok);
1193
+ }
1194
+ const target = Node.Name(full);
1195
+ assignSpan(target, spanFromSources(startTok, consumed[consumed.length - 1]));
1196
+ return target;
1197
+ }
1027
1198
  function parsePrimary(ctx, error) {
1028
1199
  // Minimal: construction, literals, names, Ok/Err/Some/None, call with dotted names and parens args
1029
1200
  // Lambda (block form): 'a function' (or 'function') ... 'produce' Type ':' \n Block
@@ -1187,6 +1358,13 @@ function parsePrimary(ctx, error) {
1187
1358
  assignSpan(node, spanFromToken(tok));
1188
1359
  return node;
1189
1360
  }
1361
+ if (ctx.at(TokenKind.DECIMAL)) {
1362
+ const tok = ctx.next();
1363
+ // tok.value 已是 lexer 规范化的 canonical 十进制字符串(ADR 0025)。
1364
+ const node = Node.Decimal(tok.value);
1365
+ assignSpan(node, spanFromToken(tok));
1366
+ return node;
1367
+ }
1190
1368
  if (ctx.isKeyword(KW.AWAIT)) {
1191
1369
  const awaitTok = ctx.nextWord();
1192
1370
  const args = parseArgList(ctx, error);