redscript-mc 3.0.1 → 3.0.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 (225) hide show
  1. package/.github/workflows/ci.yml +1 -0
  2. package/README.md +119 -313
  3. package/README.zh.md +118 -314
  4. package/ROADMAP.md +5 -5
  5. package/dist/data/impl_test/function/counter/get.mcfunction +5 -0
  6. package/dist/data/impl_test/function/counter/inc.mcfunction +7 -0
  7. package/dist/data/impl_test/function/counter/new.mcfunction +4 -0
  8. package/dist/data/impl_test/function/load.mcfunction +1 -0
  9. package/dist/data/impl_test/function/test_impl.mcfunction +10 -0
  10. package/dist/data/minecraft/tags/function/load.json +5 -0
  11. package/dist/data/playground/function/load.mcfunction +1 -0
  12. package/dist/data/playground/function/start.mcfunction +4 -0
  13. package/dist/data/playground/function/start__say_macro_t1.mcfunction +1 -0
  14. package/dist/data/playground/function/stop.mcfunction +5 -0
  15. package/dist/data/playground/function/stop__say_macro_t0.mcfunction +1 -0
  16. package/dist/data/stdlib_queue8_test/function/__queue_append_apply.mcfunction +4 -0
  17. package/dist/data/stdlib_queue8_test/function/__queue_peek_apply.mcfunction +4 -0
  18. package/dist/data/stdlib_queue8_test/function/__queue_size_raw_apply.mcfunction +4 -0
  19. package/dist/data/stdlib_queue8_test/function/load.mcfunction +1 -0
  20. package/dist/data/stdlib_queue8_test/function/queue_clear.mcfunction +6 -0
  21. package/dist/data/stdlib_queue8_test/function/queue_empty__merge_1.mcfunction +5 -0
  22. package/dist/data/stdlib_queue8_test/function/queue_empty__then_0.mcfunction +5 -0
  23. package/dist/data/stdlib_queue8_test/function/queue_peek__merge_1.mcfunction +13 -0
  24. package/dist/data/stdlib_queue8_test/function/queue_peek__then_0.mcfunction +5 -0
  25. package/dist/data/stdlib_queue8_test/function/queue_pop__merge_1.mcfunction +15 -0
  26. package/dist/data/stdlib_queue8_test/function/queue_pop__then_0.mcfunction +5 -0
  27. package/dist/data/stdlib_queue8_test/function/queue_push__const_11.mcfunction +6 -0
  28. package/dist/data/stdlib_queue8_test/function/queue_push__const_22.mcfunction +6 -0
  29. package/dist/data/stdlib_queue8_test/function/queue_size.mcfunction +13 -0
  30. package/dist/data/stdlib_queue8_test/function/test_queue_push_and_size.mcfunction +13 -0
  31. package/dist/data/test/function/load.mcfunction +1 -0
  32. package/dist/data/test/function/say_at.mcfunction +6 -0
  33. package/dist/data/test/function/test.mcfunction +4 -0
  34. package/dist/pack.mcmeta +6 -0
  35. package/dist/package.json +1 -1
  36. package/dist/src/__tests__/formatter-extra.test.d.ts +7 -0
  37. package/dist/src/__tests__/formatter-extra.test.js +123 -0
  38. package/dist/src/__tests__/global-vars.test.d.ts +13 -0
  39. package/dist/src/__tests__/global-vars.test.js +156 -0
  40. package/dist/src/__tests__/lint/new-rules.test.d.ts +9 -0
  41. package/dist/src/__tests__/lint/new-rules.test.js +402 -0
  42. package/dist/src/__tests__/lsp-rename.test.d.ts +8 -0
  43. package/dist/src/__tests__/lsp-rename.test.js +157 -0
  44. package/dist/src/__tests__/mc-integration/say-fstring.test.d.ts +11 -0
  45. package/dist/src/__tests__/mc-integration/say-fstring.test.js +220 -0
  46. package/dist/src/__tests__/mc-integration/stdlib-coverage-2.test.js +1 -1
  47. package/dist/src/__tests__/mc-integration/stdlib-coverage-3.test.js +1 -1
  48. package/dist/src/__tests__/mc-integration/stdlib-coverage-4.test.js +1 -1
  49. package/dist/src/__tests__/mc-integration/stdlib-coverage-5.test.js +1 -1
  50. package/dist/src/__tests__/mc-integration/stdlib-coverage-6.test.js +1 -1
  51. package/dist/src/__tests__/mc-integration/stdlib-coverage-7.test.js +1 -1
  52. package/dist/src/__tests__/mc-integration/stdlib-coverage-8.test.js +1 -1
  53. package/dist/src/__tests__/mc-syntax.test.js +4 -1
  54. package/dist/src/__tests__/monomorphize-coverage.test.d.ts +9 -0
  55. package/dist/src/__tests__/monomorphize-coverage.test.js +204 -0
  56. package/dist/src/__tests__/optimizer-cse.test.d.ts +7 -0
  57. package/dist/src/__tests__/optimizer-cse.test.js +226 -0
  58. package/dist/src/__tests__/parser.test.js +4 -13
  59. package/dist/src/__tests__/repl-server-extra.test.js +6 -7
  60. package/dist/src/__tests__/repl-server.test.js +5 -7
  61. package/dist/src/__tests__/stdlib/queue.test.js +6 -6
  62. package/dist/src/cli.js +0 -0
  63. package/dist/src/lexer/index.js +2 -1
  64. package/dist/src/lint/index.d.ts +12 -5
  65. package/dist/src/lint/index.js +730 -5
  66. package/dist/src/lsp/main.js +0 -0
  67. package/dist/src/mc-test/client.d.ts +21 -0
  68. package/dist/src/mc-test/client.js +34 -0
  69. package/dist/src/mir/lower.js +108 -6
  70. package/dist/src/optimizer/interprocedural.js +37 -2
  71. package/dist/src/parser/decl-parser.d.ts +19 -0
  72. package/dist/src/parser/decl-parser.js +323 -0
  73. package/dist/src/parser/expr-parser.d.ts +46 -0
  74. package/dist/src/parser/expr-parser.js +759 -0
  75. package/dist/src/parser/index.d.ts +8 -129
  76. package/dist/src/parser/index.js +13 -2262
  77. package/dist/src/parser/stmt-parser.d.ts +28 -0
  78. package/dist/src/parser/stmt-parser.js +577 -0
  79. package/dist/src/parser/type-parser.d.ts +20 -0
  80. package/dist/src/parser/type-parser.js +257 -0
  81. package/dist/src/parser/utils.d.ts +34 -0
  82. package/dist/src/parser/utils.js +141 -0
  83. package/docs/dev/README-mc-integration-tests.md +141 -0
  84. package/docs/lint-rules.md +162 -0
  85. package/docs/stdlib/bigint.md +2 -0
  86. package/editors/vscode/README.md +63 -41
  87. package/editors/vscode/out/extension.js +1881 -1776
  88. package/editors/vscode/out/lsp-server.js +4257 -3651
  89. package/editors/vscode/package-lock.json +3 -3
  90. package/editors/vscode/package.json +1 -1
  91. package/examples/loops-demo.mcrs +87 -0
  92. package/package.json +1 -1
  93. package/redscript-docs/docs/en/stdlib/advanced.md +629 -0
  94. package/redscript-docs/docs/en/stdlib/bigint.md +316 -0
  95. package/redscript-docs/docs/en/stdlib/bits.md +292 -0
  96. package/redscript-docs/docs/en/stdlib/bossbar.md +177 -0
  97. package/redscript-docs/docs/en/stdlib/calculus.md +289 -0
  98. package/redscript-docs/docs/en/stdlib/color.md +353 -0
  99. package/redscript-docs/docs/en/stdlib/combat.md +88 -0
  100. package/redscript-docs/docs/en/stdlib/cooldown.md +82 -0
  101. package/redscript-docs/docs/en/stdlib/dialog.md +155 -0
  102. package/redscript-docs/docs/en/stdlib/easing.md +558 -0
  103. package/redscript-docs/docs/en/stdlib/ecs.md +475 -0
  104. package/redscript-docs/docs/en/stdlib/effects.md +324 -0
  105. package/redscript-docs/docs/en/stdlib/events.md +3 -0
  106. package/redscript-docs/docs/en/stdlib/expr.md +45 -0
  107. package/redscript-docs/docs/en/stdlib/fft.md +141 -0
  108. package/redscript-docs/docs/en/stdlib/geometry.md +430 -0
  109. package/redscript-docs/docs/en/stdlib/graph.md +259 -0
  110. package/redscript-docs/docs/en/stdlib/heap.md +185 -0
  111. package/redscript-docs/docs/en/stdlib/interactions.md +179 -0
  112. package/redscript-docs/docs/en/stdlib/inventory.md +97 -0
  113. package/redscript-docs/docs/en/stdlib/linalg.md +557 -0
  114. package/redscript-docs/docs/en/stdlib/list.md +559 -0
  115. package/redscript-docs/docs/en/stdlib/map.md +140 -0
  116. package/redscript-docs/docs/en/stdlib/math.md +193 -0
  117. package/redscript-docs/docs/en/stdlib/math_hp.md +149 -0
  118. package/redscript-docs/docs/en/stdlib/matrix.md +403 -0
  119. package/redscript-docs/docs/en/stdlib/mobs.md +965 -0
  120. package/redscript-docs/docs/en/stdlib/noise.md +244 -0
  121. package/redscript-docs/docs/en/stdlib/ode.md +253 -0
  122. package/redscript-docs/docs/en/stdlib/parabola.md +342 -0
  123. package/redscript-docs/docs/en/stdlib/particles.md +311 -0
  124. package/redscript-docs/docs/en/stdlib/pathfind.md +255 -0
  125. package/redscript-docs/docs/en/stdlib/physics.md +493 -0
  126. package/redscript-docs/docs/en/stdlib/player.md +78 -0
  127. package/redscript-docs/docs/en/stdlib/quaternion.md +673 -0
  128. package/redscript-docs/docs/en/stdlib/queue.md +134 -0
  129. package/redscript-docs/docs/en/stdlib/random.md +223 -0
  130. package/redscript-docs/docs/en/stdlib/result.md +143 -0
  131. package/redscript-docs/docs/en/stdlib/scheduler.md +183 -0
  132. package/redscript-docs/docs/en/stdlib/set_int.md +190 -0
  133. package/redscript-docs/docs/en/stdlib/sets.md +101 -0
  134. package/redscript-docs/docs/en/stdlib/signal.md +400 -0
  135. package/redscript-docs/docs/en/stdlib/sort.md +104 -0
  136. package/redscript-docs/docs/en/stdlib/spawn.md +147 -0
  137. package/redscript-docs/docs/en/stdlib/state.md +142 -0
  138. package/redscript-docs/docs/en/stdlib/strings.md +154 -0
  139. package/redscript-docs/docs/en/stdlib/tags.md +3451 -0
  140. package/redscript-docs/docs/en/stdlib/teams.md +153 -0
  141. package/redscript-docs/docs/en/stdlib/timer.md +246 -0
  142. package/redscript-docs/docs/en/stdlib/vec.md +158 -0
  143. package/redscript-docs/docs/en/stdlib/world.md +298 -0
  144. package/redscript-docs/docs/zh/stdlib/advanced.md +615 -0
  145. package/redscript-docs/docs/zh/stdlib/bigint.md +316 -0
  146. package/redscript-docs/docs/zh/stdlib/bits.md +292 -0
  147. package/redscript-docs/docs/zh/stdlib/bossbar.md +170 -0
  148. package/redscript-docs/docs/zh/stdlib/calculus.md +287 -0
  149. package/redscript-docs/docs/zh/stdlib/color.md +353 -0
  150. package/redscript-docs/docs/zh/stdlib/combat.md +88 -0
  151. package/redscript-docs/docs/zh/stdlib/cooldown.md +84 -0
  152. package/redscript-docs/docs/zh/stdlib/dialog.md +152 -0
  153. package/redscript-docs/docs/zh/stdlib/easing.md +558 -0
  154. package/redscript-docs/docs/zh/stdlib/ecs.md +472 -0
  155. package/redscript-docs/docs/zh/stdlib/effects.md +324 -0
  156. package/redscript-docs/docs/zh/stdlib/events.md +3 -0
  157. package/redscript-docs/docs/zh/stdlib/expr.md +37 -0
  158. package/redscript-docs/docs/zh/stdlib/fft.md +128 -0
  159. package/redscript-docs/docs/zh/stdlib/geometry.md +430 -0
  160. package/redscript-docs/docs/zh/stdlib/graph.md +259 -0
  161. package/redscript-docs/docs/zh/stdlib/heap.md +185 -0
  162. package/redscript-docs/docs/zh/stdlib/interactions.md +160 -0
  163. package/redscript-docs/docs/zh/stdlib/inventory.md +94 -0
  164. package/redscript-docs/docs/zh/stdlib/linalg.md +543 -0
  165. package/redscript-docs/docs/zh/stdlib/list.md +561 -0
  166. package/redscript-docs/docs/zh/stdlib/map.md +132 -0
  167. package/redscript-docs/docs/zh/stdlib/math.md +193 -0
  168. package/redscript-docs/docs/zh/stdlib/math_hp.md +143 -0
  169. package/redscript-docs/docs/zh/stdlib/matrix.md +396 -0
  170. package/redscript-docs/docs/zh/stdlib/mobs.md +965 -0
  171. package/redscript-docs/docs/zh/stdlib/noise.md +244 -0
  172. package/redscript-docs/docs/zh/stdlib/ode.md +243 -0
  173. package/redscript-docs/docs/zh/stdlib/parabola.md +337 -0
  174. package/redscript-docs/docs/zh/stdlib/particles.md +307 -0
  175. package/redscript-docs/docs/zh/stdlib/pathfind.md +255 -0
  176. package/redscript-docs/docs/zh/stdlib/physics.md +493 -0
  177. package/redscript-docs/docs/zh/stdlib/player.md +78 -0
  178. package/redscript-docs/docs/zh/stdlib/quaternion.md +669 -0
  179. package/redscript-docs/docs/zh/stdlib/queue.md +124 -0
  180. package/redscript-docs/docs/zh/stdlib/random.md +222 -0
  181. package/redscript-docs/docs/zh/stdlib/result.md +147 -0
  182. package/redscript-docs/docs/zh/stdlib/scheduler.md +173 -0
  183. package/redscript-docs/docs/zh/stdlib/set_int.md +180 -0
  184. package/redscript-docs/docs/zh/stdlib/sets.md +107 -0
  185. package/redscript-docs/docs/zh/stdlib/signal.md +373 -0
  186. package/redscript-docs/docs/zh/stdlib/sort.md +104 -0
  187. package/redscript-docs/docs/zh/stdlib/spawn.md +142 -0
  188. package/redscript-docs/docs/zh/stdlib/state.md +134 -0
  189. package/redscript-docs/docs/zh/stdlib/strings.md +107 -0
  190. package/redscript-docs/docs/zh/stdlib/tags.md +3451 -0
  191. package/redscript-docs/docs/zh/stdlib/teams.md +150 -0
  192. package/redscript-docs/docs/zh/stdlib/timer.md +254 -0
  193. package/redscript-docs/docs/zh/stdlib/vec.md +158 -0
  194. package/redscript-docs/docs/zh/stdlib/world.md +289 -0
  195. package/src/__tests__/formatter-extra.test.ts +139 -0
  196. package/src/__tests__/global-vars.test.ts +171 -0
  197. package/src/__tests__/lint/new-rules.test.ts +437 -0
  198. package/src/__tests__/lsp-rename.test.ts +171 -0
  199. package/src/__tests__/mc-integration/say-fstring.test.ts +211 -0
  200. package/src/__tests__/mc-integration/stdlib-coverage-2.test.ts +1 -1
  201. package/src/__tests__/mc-integration/stdlib-coverage-3.test.ts +1 -1
  202. package/src/__tests__/mc-integration/stdlib-coverage-4.test.ts +1 -1
  203. package/src/__tests__/mc-integration/stdlib-coverage-5.test.ts +1 -1
  204. package/src/__tests__/mc-integration/stdlib-coverage-6.test.ts +1 -1
  205. package/src/__tests__/mc-integration/stdlib-coverage-7.test.ts +1 -1
  206. package/src/__tests__/mc-integration/stdlib-coverage-8.test.ts +1 -1
  207. package/src/__tests__/mc-syntax.test.ts +3 -0
  208. package/src/__tests__/monomorphize-coverage.test.ts +220 -0
  209. package/src/__tests__/optimizer-cse.test.ts +250 -0
  210. package/src/__tests__/parser.test.ts +4 -13
  211. package/src/__tests__/repl-server-extra.test.ts +6 -6
  212. package/src/__tests__/repl-server.test.ts +5 -6
  213. package/src/__tests__/stdlib/queue.test.ts +6 -6
  214. package/src/lexer/index.ts +2 -1
  215. package/src/lint/index.ts +713 -5
  216. package/src/mc-test/client.ts +40 -0
  217. package/src/mir/lower.ts +111 -2
  218. package/src/optimizer/interprocedural.ts +40 -2
  219. package/src/parser/decl-parser.ts +349 -0
  220. package/src/parser/expr-parser.ts +838 -0
  221. package/src/parser/index.ts +17 -2558
  222. package/src/parser/stmt-parser.ts +585 -0
  223. package/src/parser/type-parser.ts +276 -0
  224. package/src/parser/utils.ts +173 -0
  225. package/src/stdlib/queue.mcrs +19 -6
@@ -0,0 +1,759 @@
1
+ "use strict";
2
+ /**
3
+ * ExprParser — expression parsing (binary, unary, postfix, primary).
4
+ * Extends TypeParser so expression methods can call type methods.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.ExprParser = void 0;
8
+ const lexer_1 = require("../lexer");
9
+ const type_parser_1 = require("./type-parser");
10
+ const utils_1 = require("./utils");
11
+ // ---------------------------------------------------------------------------
12
+ // Entity type name set
13
+ // ---------------------------------------------------------------------------
14
+ const ENTITY_TYPE_NAMES = new Set([
15
+ 'entity', 'Player', 'Mob', 'HostileMob', 'PassiveMob', 'Zombie', 'Skeleton',
16
+ 'Creeper', 'Spider', 'Enderman', 'Blaze', 'Witch', 'Slime', 'ZombieVillager',
17
+ 'Husk', 'Drowned', 'Stray', 'WitherSkeleton', 'CaveSpider', 'Pig', 'Cow',
18
+ 'Sheep', 'Chicken', 'Villager', 'WanderingTrader', 'ArmorStand', 'Item', 'Arrow',
19
+ ]);
20
+ function computeIsSingle(raw) {
21
+ if (/^@[spr](\[|$)/.test(raw))
22
+ return true;
23
+ if (/[\[,\s]limit=1[,\]\s]/.test(raw))
24
+ return true;
25
+ return false;
26
+ }
27
+ class ExprParser extends type_parser_1.TypeParser {
28
+ // -------------------------------------------------------------------------
29
+ // Expressions (Precedence Climbing)
30
+ // -------------------------------------------------------------------------
31
+ parseExpr() {
32
+ return this.parseAssignment();
33
+ }
34
+ parseAssignment() {
35
+ const left = this.parseBinaryExpr(1);
36
+ const token = this.peek();
37
+ if (token.kind === '=' || token.kind === '+=' || token.kind === '-=' ||
38
+ token.kind === '*=' || token.kind === '/=' || token.kind === '%=') {
39
+ const op = this.advance().kind;
40
+ if (left.kind === 'ident') {
41
+ const value = this.parseAssignment();
42
+ return this.withLoc({ kind: 'assign', target: left.name, op, value }, this.getLocToken(left) ?? token);
43
+ }
44
+ if (left.kind === 'member') {
45
+ const value = this.parseAssignment();
46
+ return this.withLoc({ kind: 'member_assign', obj: left.obj, field: left.field, op, value }, this.getLocToken(left) ?? token);
47
+ }
48
+ if (left.kind === 'index') {
49
+ const value = this.parseAssignment();
50
+ return this.withLoc({ kind: 'index_assign', obj: left.obj, index: left.index, op, value }, this.getLocToken(left) ?? token);
51
+ }
52
+ }
53
+ return left;
54
+ }
55
+ parseBinaryExpr(minPrec) {
56
+ let left = this.parseUnaryExpr();
57
+ while (true) {
58
+ const op = this.peek().kind;
59
+ if (!utils_1.BINARY_OPS.has(op))
60
+ break;
61
+ const prec = utils_1.PRECEDENCE[op];
62
+ if (prec < minPrec)
63
+ break;
64
+ const opToken = this.advance();
65
+ if (op === 'is') {
66
+ const entityType = this.parseEntityTypeName();
67
+ left = this.withLoc({ kind: 'is_check', expr: left, entityType }, this.getLocToken(left) ?? opToken);
68
+ continue;
69
+ }
70
+ const right = this.parseBinaryExpr(prec + 1);
71
+ left = this.withLoc({ kind: 'binary', op: op, left, right }, this.getLocToken(left) ?? opToken);
72
+ }
73
+ return left;
74
+ }
75
+ parseUnaryExpr() {
76
+ if (this.match('!')) {
77
+ const bangToken = this.tokens[this.pos - 1];
78
+ const operand = this.parseUnaryExpr();
79
+ return this.withLoc({ kind: 'unary', op: '!', operand }, bangToken);
80
+ }
81
+ if (this.check('-') && !this.isSubtraction()) {
82
+ const minusToken = this.advance();
83
+ const operand = this.parseUnaryExpr();
84
+ return this.withLoc({ kind: 'unary', op: '-', operand }, minusToken);
85
+ }
86
+ return this.parsePostfixExpr();
87
+ }
88
+ parseEntityTypeName() {
89
+ const token = this.expect('ident');
90
+ if (ENTITY_TYPE_NAMES.has(token.value)) {
91
+ return token.value;
92
+ }
93
+ this.error(`Unknown entity type '${token.value}'. Valid types: ${[...ENTITY_TYPE_NAMES].slice(0, 6).join(', ')}, ...`);
94
+ }
95
+ isSubtraction() {
96
+ if (this.pos === 0)
97
+ return false;
98
+ const prev = this.tokens[this.pos - 1];
99
+ return ['int_lit', 'float_lit', 'ident', ')', ']'].includes(prev.kind);
100
+ }
101
+ parsePostfixExpr() {
102
+ let expr = this.parsePrimaryExpr();
103
+ while (true) {
104
+ // Generic call: ident<Type>(args)
105
+ if (expr.kind === 'ident' && this.check('<')) {
106
+ const typeArgs = this.tryParseTypeArgs();
107
+ if (typeArgs !== null && this.check('(')) {
108
+ const openParenToken = this.peek();
109
+ this.advance(); // consume '('
110
+ const args = this.parseArgs();
111
+ this.expect(')');
112
+ expr = this.withLoc({ kind: 'call', fn: expr.name, args, typeArgs }, this.getLocToken(expr) ?? openParenToken);
113
+ continue;
114
+ }
115
+ }
116
+ // Function call
117
+ if (this.match('(')) {
118
+ const openParenToken = this.tokens[this.pos - 1];
119
+ if (expr.kind === 'ident') {
120
+ const args = this.parseArgs();
121
+ this.expect(')');
122
+ expr = this.withLoc({ kind: 'call', fn: expr.name, args }, this.getLocToken(expr) ?? openParenToken);
123
+ continue;
124
+ }
125
+ if (expr.kind === 'member') {
126
+ if (expr.field === 'unwrap_or') {
127
+ const defaultExpr = this.parseExpr();
128
+ this.expect(')');
129
+ expr = this.withLoc({ kind: 'unwrap_or', opt: expr.obj, default_: defaultExpr }, this.getLocToken(expr) ?? openParenToken);
130
+ continue;
131
+ }
132
+ const methodMap = {
133
+ 'tag': '__entity_tag',
134
+ 'untag': '__entity_untag',
135
+ 'has_tag': '__entity_has_tag',
136
+ 'push': '__array_push',
137
+ 'pop': '__array_pop',
138
+ 'add': 'set_add',
139
+ 'contains': 'set_contains',
140
+ 'remove': 'set_remove',
141
+ 'clear': 'set_clear',
142
+ };
143
+ const internalFn = methodMap[expr.field];
144
+ if (internalFn) {
145
+ const args = this.parseArgs();
146
+ this.expect(')');
147
+ expr = this.withLoc({ kind: 'call', fn: internalFn, args: [expr.obj, ...args] }, this.getLocToken(expr) ?? openParenToken);
148
+ continue;
149
+ }
150
+ const args = this.parseArgs();
151
+ this.expect(')');
152
+ expr = this.withLoc({ kind: 'call', fn: expr.field, args: [expr.obj, ...args] }, this.getLocToken(expr) ?? openParenToken);
153
+ continue;
154
+ }
155
+ const args = this.parseArgs();
156
+ this.expect(')');
157
+ expr = this.withLoc({ kind: 'invoke', callee: expr, args }, this.getLocToken(expr) ?? openParenToken);
158
+ continue;
159
+ }
160
+ // Array index access
161
+ if (this.match('[')) {
162
+ const index = this.parseExpr();
163
+ this.expect(']');
164
+ expr = this.withLoc({ kind: 'index', obj: expr, index }, this.getLocToken(expr) ?? this.tokens[this.pos - 1]);
165
+ continue;
166
+ }
167
+ // Member access
168
+ if (this.match('.')) {
169
+ const field = this.expect('ident').value;
170
+ expr = this.withLoc({ kind: 'member', obj: expr, field }, this.getLocToken(expr) ?? this.tokens[this.pos - 1]);
171
+ continue;
172
+ }
173
+ // Type cast: expr as Type
174
+ if (this.check('as') && this.isTypeCastAs()) {
175
+ const asToken = this.advance();
176
+ const targetType = this.parseType();
177
+ expr = this.withLoc({ kind: 'type_cast', expr, targetType }, this.getLocToken(expr) ?? asToken);
178
+ continue;
179
+ }
180
+ break;
181
+ }
182
+ return expr;
183
+ }
184
+ isTypeCastAs() {
185
+ const next = this.tokens[this.pos + 1];
186
+ if (!next)
187
+ return false;
188
+ const typeStartTokens = new Set(['int', 'bool', 'float', 'fixed', 'string', 'void', 'BlockPos', '(']);
189
+ if (typeStartTokens.has(next.kind))
190
+ return true;
191
+ if (next.kind === 'ident' && (next.value === 'double' || next.value === 'byte' || next.value === 'short' ||
192
+ next.value === 'long' || next.value === 'selector' || next.value === 'Option'))
193
+ return true;
194
+ return false;
195
+ }
196
+ parseArgs() {
197
+ const args = [];
198
+ if (!this.check(')')) {
199
+ do {
200
+ args.push(this.parseExpr());
201
+ } while (this.match(','));
202
+ }
203
+ return args;
204
+ }
205
+ parsePrimaryExpr() {
206
+ const token = this.peek();
207
+ if (token.kind === 'ident' && this.peek(1).kind === '::') {
208
+ const typeToken = this.advance();
209
+ this.expect('::');
210
+ const memberToken = this.expect('ident');
211
+ if (this.check('(')) {
212
+ const isNamedArgs = this.peek(1).kind === 'ident' && this.peek(2).kind === ':';
213
+ if (isNamedArgs) {
214
+ this.advance(); // consume '('
215
+ const args = [];
216
+ while (!this.check(')') && !this.check('eof')) {
217
+ const fieldName = this.expect('ident').value;
218
+ this.expect(':');
219
+ const value = this.parseExpr();
220
+ args.push({ name: fieldName, value });
221
+ if (!this.match(','))
222
+ break;
223
+ }
224
+ this.expect(')');
225
+ return this.withLoc({ kind: 'enum_construct', enumName: typeToken.value, variant: memberToken.value, args }, typeToken);
226
+ }
227
+ this.advance(); // consume '('
228
+ const args = this.parseArgs();
229
+ this.expect(')');
230
+ return this.withLoc({ kind: 'static_call', type: typeToken.value, method: memberToken.value, args }, typeToken);
231
+ }
232
+ return this.withLoc({ kind: 'path_expr', enumName: typeToken.value, variant: memberToken.value }, typeToken);
233
+ }
234
+ if (token.kind === 'ident' && this.peek(1).kind === '=>') {
235
+ return this.parseSingleParamLambda();
236
+ }
237
+ if (token.kind === 'int_lit') {
238
+ this.advance();
239
+ return this.withLoc({ kind: 'int_lit', value: parseInt(token.value, 10) }, token);
240
+ }
241
+ if (token.kind === 'float_lit') {
242
+ this.advance();
243
+ return this.withLoc({ kind: 'float_lit', value: parseFloat(token.value) }, token);
244
+ }
245
+ if (token.kind === 'rel_coord') {
246
+ this.advance();
247
+ return this.withLoc({ kind: 'rel_coord', value: token.value }, token);
248
+ }
249
+ if (token.kind === 'local_coord') {
250
+ this.advance();
251
+ return this.withLoc({ kind: 'local_coord', value: token.value }, token);
252
+ }
253
+ if (token.kind === 'byte_lit') {
254
+ this.advance();
255
+ return this.withLoc({ kind: 'byte_lit', value: parseInt(token.value.slice(0, -1), 10) }, token);
256
+ }
257
+ if (token.kind === 'short_lit') {
258
+ this.advance();
259
+ return this.withLoc({ kind: 'short_lit', value: parseInt(token.value.slice(0, -1), 10) }, token);
260
+ }
261
+ if (token.kind === 'long_lit') {
262
+ this.advance();
263
+ return this.withLoc({ kind: 'long_lit', value: parseInt(token.value.slice(0, -1), 10) }, token);
264
+ }
265
+ if (token.kind === 'double_lit') {
266
+ this.advance();
267
+ return this.withLoc({ kind: 'double_lit', value: parseFloat(token.value.slice(0, -1)) }, token);
268
+ }
269
+ if (token.kind === 'string_lit') {
270
+ this.advance();
271
+ return this.parseStringExpr(token);
272
+ }
273
+ if (token.kind === 'f_string') {
274
+ this.advance();
275
+ return this.parseFStringExpr(token);
276
+ }
277
+ if (token.kind === 'mc_name') {
278
+ this.advance();
279
+ return this.withLoc({ kind: 'mc_name', value: token.value.slice(1) }, token);
280
+ }
281
+ if (token.kind === 'true') {
282
+ this.advance();
283
+ return this.withLoc({ kind: 'bool_lit', value: true }, token);
284
+ }
285
+ if (token.kind === 'false') {
286
+ this.advance();
287
+ return this.withLoc({ kind: 'bool_lit', value: false }, token);
288
+ }
289
+ if (token.kind === 'range_lit') {
290
+ this.advance();
291
+ return this.withLoc({ kind: 'range_lit', range: this.parseRangeValue(token.value) }, token);
292
+ }
293
+ if (token.kind === 'selector') {
294
+ this.advance();
295
+ return this.withLoc({
296
+ kind: 'selector',
297
+ raw: token.value,
298
+ isSingle: computeIsSingle(token.value),
299
+ sel: this.parseSelectorValue(token.value),
300
+ }, token);
301
+ }
302
+ if (token.kind === 'ident' && this.peek(1).kind === '{' &&
303
+ this.peek(2).kind === 'ident' && this.peek(3).kind === ':') {
304
+ this.advance();
305
+ return this.parseStructLit();
306
+ }
307
+ if (token.kind === 'ident' && token.value === 'Some' && this.peek(1).kind === '(') {
308
+ this.advance();
309
+ this.advance();
310
+ const value = this.parseExpr();
311
+ this.expect(')');
312
+ return this.withLoc({ kind: 'some_lit', value }, token);
313
+ }
314
+ if (token.kind === 'ident' && token.value === 'None') {
315
+ this.advance();
316
+ return this.withLoc({ kind: 'none_lit' }, token);
317
+ }
318
+ if (token.kind === 'ident') {
319
+ this.advance();
320
+ return this.withLoc({ kind: 'ident', name: token.value }, token);
321
+ }
322
+ if (token.kind === '(') {
323
+ if (this.isBlockPosLiteral()) {
324
+ return this.parseBlockPos();
325
+ }
326
+ if (this.isLambdaStart()) {
327
+ return this.parseLambdaExpr();
328
+ }
329
+ this.advance();
330
+ const first = this.parseExpr();
331
+ if (this.match(',')) {
332
+ const elements = [first];
333
+ if (!this.check(')')) {
334
+ do {
335
+ elements.push(this.parseExpr());
336
+ } while (this.match(','));
337
+ }
338
+ this.expect(')');
339
+ return this.withLoc({ kind: 'tuple_lit', elements }, token);
340
+ }
341
+ this.expect(')');
342
+ return first;
343
+ }
344
+ if (token.kind === '{') {
345
+ return this.parseStructLit();
346
+ }
347
+ if (token.kind === '[') {
348
+ return this.parseArrayLit();
349
+ }
350
+ this.error(`Unexpected token '${token.value || token.kind}'. Expected an expression (identifier, literal, '(', '[', or '{')`);
351
+ }
352
+ parseLiteralExpr() {
353
+ if (this.check('-')) {
354
+ this.advance();
355
+ const token = this.peek();
356
+ if (token.kind === 'int_lit') {
357
+ this.advance();
358
+ return this.withLoc({ kind: 'int_lit', value: -Number(token.value) }, token);
359
+ }
360
+ if (token.kind === 'float_lit') {
361
+ this.advance();
362
+ return this.withLoc({ kind: 'float_lit', value: -Number(token.value) }, token);
363
+ }
364
+ this.error('Expected number after unary minus (-). Const values must be numeric or string literals');
365
+ }
366
+ const expr = this.parsePrimaryExpr();
367
+ if (expr.kind === 'int_lit' ||
368
+ expr.kind === 'float_lit' ||
369
+ expr.kind === 'bool_lit' ||
370
+ expr.kind === 'str_lit') {
371
+ return expr;
372
+ }
373
+ this.error('Const value must be a literal');
374
+ }
375
+ // -------------------------------------------------------------------------
376
+ // Lambda
377
+ // -------------------------------------------------------------------------
378
+ parseSingleParamLambda() {
379
+ const paramToken = this.expect('ident');
380
+ const params = [{ name: paramToken.value }];
381
+ this.expect('=>');
382
+ return this.finishLambdaExpr(params, paramToken);
383
+ }
384
+ parseLambdaExpr() {
385
+ const openParenToken = this.expect('(');
386
+ const params = [];
387
+ if (!this.check(')')) {
388
+ do {
389
+ const name = this.expect('ident').value;
390
+ let type;
391
+ if (this.match(':')) {
392
+ type = this.parseType();
393
+ }
394
+ params.push({ name, type });
395
+ } while (this.match(','));
396
+ }
397
+ this.expect(')');
398
+ let returnType;
399
+ if (this.match('->')) {
400
+ returnType = this.parseType();
401
+ }
402
+ this.expect('=>');
403
+ return this.finishLambdaExpr(params, openParenToken, returnType);
404
+ }
405
+ finishLambdaExpr(params, token, returnType) {
406
+ const body = this.check('{') ? this.parseBlock() : this.parseExpr();
407
+ return this.withLoc({ kind: 'lambda', params, returnType, body }, token);
408
+ }
409
+ // -------------------------------------------------------------------------
410
+ // String interpolation
411
+ // -------------------------------------------------------------------------
412
+ parseStringExpr(token) {
413
+ // Plain string literals: no interpolation. "${...}" is treated as literal text.
414
+ // Only f"..." strings (f_string token) support {expr} interpolation.
415
+ return this.withLoc({ kind: 'str_lit', value: token.value }, token);
416
+ }
417
+ parseFStringExpr(token) {
418
+ const parts = [];
419
+ let current = '';
420
+ let index = 0;
421
+ while (index < token.value.length) {
422
+ if (token.value[index] === '{') {
423
+ if (current) {
424
+ parts.push({ kind: 'text', value: current });
425
+ current = '';
426
+ }
427
+ index++;
428
+ let depth = 1;
429
+ let exprSource = '';
430
+ let inString = false;
431
+ while (index < token.value.length && depth > 0) {
432
+ const char = token.value[index];
433
+ if (char === '"' && token.value[index - 1] !== '\\') {
434
+ inString = !inString;
435
+ }
436
+ if (!inString) {
437
+ if (char === '{')
438
+ depth++;
439
+ else if (char === '}') {
440
+ depth--;
441
+ if (depth === 0) {
442
+ index++;
443
+ break;
444
+ }
445
+ }
446
+ }
447
+ if (depth > 0)
448
+ exprSource += char;
449
+ index++;
450
+ }
451
+ if (depth !== 0)
452
+ this.error('Unterminated f-string interpolation');
453
+ parts.push({ kind: 'expr', expr: this.parseEmbeddedExpr(exprSource) });
454
+ continue;
455
+ }
456
+ current += token.value[index];
457
+ index++;
458
+ }
459
+ if (current)
460
+ parts.push({ kind: 'text', value: current });
461
+ return this.withLoc({ kind: 'f_string', parts }, token);
462
+ }
463
+ parseEmbeddedExpr(source) {
464
+ // Lazy import to break circular dependency at runtime — Parser extends ExprParser
465
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
466
+ const { Parser } = require('./index');
467
+ const tokens = new lexer_1.Lexer(source, this.filePath).tokenize();
468
+ const parser = new Parser(tokens, source, this.filePath);
469
+ const expr = parser.parseExpr();
470
+ if (!parser.check('eof')) {
471
+ parser.error(`Unexpected token '${parser.peek().kind}' in string interpolation`);
472
+ }
473
+ return expr;
474
+ }
475
+ // -------------------------------------------------------------------------
476
+ // Struct / Array / BlockPos literals
477
+ // -------------------------------------------------------------------------
478
+ parseStructLit() {
479
+ const braceToken = this.expect('{');
480
+ const fields = [];
481
+ if (!this.check('}')) {
482
+ do {
483
+ const name = this.expect('ident').value;
484
+ this.expect(':');
485
+ const value = this.parseExpr();
486
+ fields.push({ name, value });
487
+ } while (this.match(','));
488
+ }
489
+ this.expect('}');
490
+ return this.withLoc({ kind: 'struct_lit', fields }, braceToken);
491
+ }
492
+ parseArrayLit() {
493
+ const bracketToken = this.expect('[');
494
+ const elements = [];
495
+ if (!this.check(']')) {
496
+ do {
497
+ elements.push(this.parseExpr());
498
+ } while (this.match(','));
499
+ }
500
+ this.expect(']');
501
+ return this.withLoc({ kind: 'array_lit', elements }, bracketToken);
502
+ }
503
+ isBlockPosLiteral() {
504
+ if (!this.check('('))
505
+ return false;
506
+ let offset = 1;
507
+ for (let i = 0; i < 3; i++) {
508
+ const consumed = this.coordComponentTokenLength(offset);
509
+ if (consumed === 0)
510
+ return false;
511
+ offset += consumed;
512
+ if (i < 2) {
513
+ if (this.peek(offset).kind !== ',')
514
+ return false;
515
+ offset += 1;
516
+ }
517
+ }
518
+ return this.peek(offset).kind === ')';
519
+ }
520
+ coordComponentTokenLength(offset) {
521
+ const token = this.peek(offset);
522
+ if (token.kind === 'int_lit')
523
+ return 1;
524
+ if (token.kind === '-') {
525
+ return this.peek(offset + 1).kind === 'int_lit' ? 2 : 0;
526
+ }
527
+ if (token.kind === 'rel_coord' || token.kind === 'local_coord')
528
+ return 1;
529
+ return 0;
530
+ }
531
+ parseBlockPos() {
532
+ const openParenToken = this.expect('(');
533
+ const x = this.parseCoordComponent();
534
+ this.expect(',');
535
+ const y = this.parseCoordComponent();
536
+ this.expect(',');
537
+ const z = this.parseCoordComponent();
538
+ this.expect(')');
539
+ return this.withLoc({ kind: 'blockpos', x, y, z }, openParenToken);
540
+ }
541
+ parseCoordComponent() {
542
+ const token = this.peek();
543
+ if (token.kind === 'rel_coord') {
544
+ this.advance();
545
+ return { kind: 'relative', offset: this.parseCoordOffsetFromValue(token.value.slice(1)) };
546
+ }
547
+ if (token.kind === 'local_coord') {
548
+ this.advance();
549
+ return { kind: 'local', offset: this.parseCoordOffsetFromValue(token.value.slice(1)) };
550
+ }
551
+ return { kind: 'absolute', value: this.parseSignedCoordOffset(true) };
552
+ }
553
+ parseCoordOffsetFromValue(value) {
554
+ if (value === '' || value === undefined)
555
+ return 0;
556
+ return parseFloat(value);
557
+ }
558
+ parseSignedCoordOffset(requireValue = false) {
559
+ let sign = 1;
560
+ if (this.match('-'))
561
+ sign = -1;
562
+ if (this.check('int_lit'))
563
+ return sign * parseInt(this.advance().value, 10);
564
+ if (requireValue)
565
+ this.error('Expected integer coordinate component');
566
+ return 0;
567
+ }
568
+ // -------------------------------------------------------------------------
569
+ // Selector parsing (also used by stmt-parser)
570
+ // -------------------------------------------------------------------------
571
+ parseSelector() {
572
+ const token = this.expect('selector');
573
+ return this.parseSelectorValue(token.value);
574
+ }
575
+ parseSelectorOrVarSelector() {
576
+ if (this.check('selector')) {
577
+ return { selector: this.parseSelector() };
578
+ }
579
+ const varToken = this.expect('ident');
580
+ const varName = varToken.value;
581
+ if (this.check('[')) {
582
+ this.advance();
583
+ let filterStr = '';
584
+ let depth = 1;
585
+ while (depth > 0 && !this.check('eof')) {
586
+ if (this.check('['))
587
+ depth++;
588
+ else if (this.check(']'))
589
+ depth--;
590
+ if (depth > 0) {
591
+ filterStr += this.peek().value ?? this.peek().kind;
592
+ this.advance();
593
+ }
594
+ }
595
+ this.expect(']');
596
+ const filters = this.parseSelectorFilters(filterStr);
597
+ return { varName, filters };
598
+ }
599
+ return { varName };
600
+ }
601
+ parseSelectorValue(value) {
602
+ const bracketIndex = value.indexOf('[');
603
+ if (bracketIndex === -1) {
604
+ return { kind: value };
605
+ }
606
+ const kind = value.slice(0, bracketIndex);
607
+ const paramsStr = value.slice(bracketIndex + 1, -1);
608
+ const filters = this.parseSelectorFilters(paramsStr);
609
+ return { kind, filters };
610
+ }
611
+ parseSelectorFilters(paramsStr) {
612
+ const filters = {};
613
+ const parts = this.splitSelectorParams(paramsStr);
614
+ for (const part of parts) {
615
+ const eqIndex = part.indexOf('=');
616
+ if (eqIndex === -1)
617
+ continue;
618
+ const key = part.slice(0, eqIndex).trim();
619
+ const val = part.slice(eqIndex + 1).trim();
620
+ switch (key) {
621
+ case 'type':
622
+ filters.type = val;
623
+ break;
624
+ case 'distance':
625
+ filters.distance = this.parseRangeValue(val);
626
+ break;
627
+ case 'tag':
628
+ if (val.startsWith('!')) {
629
+ filters.notTag = filters.notTag ?? [];
630
+ filters.notTag.push(val.slice(1));
631
+ }
632
+ else {
633
+ filters.tag = filters.tag ?? [];
634
+ filters.tag.push(val);
635
+ }
636
+ break;
637
+ case 'limit':
638
+ filters.limit = parseInt(val, 10);
639
+ break;
640
+ case 'sort':
641
+ filters.sort = val;
642
+ break;
643
+ case 'nbt':
644
+ filters.nbt = val;
645
+ break;
646
+ case 'gamemode':
647
+ filters.gamemode = val;
648
+ break;
649
+ case 'scores':
650
+ filters.scores = this.parseScoresFilter(val);
651
+ break;
652
+ case 'x':
653
+ filters.x = this.parseRangeValue(val);
654
+ break;
655
+ case 'y':
656
+ filters.y = this.parseRangeValue(val);
657
+ break;
658
+ case 'z':
659
+ filters.z = this.parseRangeValue(val);
660
+ break;
661
+ case 'x_rotation':
662
+ filters.x_rotation = this.parseRangeValue(val);
663
+ break;
664
+ case 'y_rotation':
665
+ filters.y_rotation = this.parseRangeValue(val);
666
+ break;
667
+ }
668
+ }
669
+ return filters;
670
+ }
671
+ splitSelectorParams(str) {
672
+ const parts = [];
673
+ let current = '';
674
+ let depth = 0;
675
+ for (const char of str) {
676
+ if (char === '{' || char === '[')
677
+ depth++;
678
+ else if (char === '}' || char === ']')
679
+ depth--;
680
+ else if (char === ',' && depth === 0) {
681
+ parts.push(current.trim());
682
+ current = '';
683
+ continue;
684
+ }
685
+ current += char;
686
+ }
687
+ if (current.trim())
688
+ parts.push(current.trim());
689
+ return parts;
690
+ }
691
+ parseScoresFilter(val) {
692
+ const scores = {};
693
+ const inner = val.slice(1, -1);
694
+ const parts = inner.split(',');
695
+ for (const part of parts) {
696
+ const [name, range] = part.split('=').map(s => s.trim());
697
+ scores[name] = this.parseRangeValue(range);
698
+ }
699
+ return scores;
700
+ }
701
+ parseRangeValue(value) {
702
+ if (value.startsWith('..=')) {
703
+ const rest = value.slice(3);
704
+ if (!rest)
705
+ return {};
706
+ return { max: parseInt(rest, 10) };
707
+ }
708
+ if (value.startsWith('..')) {
709
+ const rest = value.slice(2);
710
+ if (!rest)
711
+ return {};
712
+ return { max: parseInt(rest, 10) };
713
+ }
714
+ const inclIdx = value.indexOf('..=');
715
+ if (inclIdx !== -1) {
716
+ const min = parseInt(value.slice(0, inclIdx), 10);
717
+ const rest = value.slice(inclIdx + 3);
718
+ if (!rest)
719
+ return { min };
720
+ return { min, max: parseInt(rest, 10) };
721
+ }
722
+ const dotIndex = value.indexOf('..');
723
+ if (dotIndex !== -1) {
724
+ const min = parseInt(value.slice(0, dotIndex), 10);
725
+ const rest = value.slice(dotIndex + 2);
726
+ if (!rest)
727
+ return { min };
728
+ return { min, max: parseInt(rest, 10) };
729
+ }
730
+ const val = parseInt(value, 10);
731
+ return { min: val, max: val };
732
+ }
733
+ // -------------------------------------------------------------------------
734
+ // Coord token (used by stmt-parser for execute subcommands)
735
+ // -------------------------------------------------------------------------
736
+ parseCoordToken() {
737
+ const token = this.peek();
738
+ if (token.kind === 'rel_coord' || token.kind === 'local_coord' ||
739
+ token.kind === 'int_lit' || token.kind === 'float_lit' ||
740
+ token.kind === '-' || token.kind === 'ident') {
741
+ return this.advance().value;
742
+ }
743
+ return this.error(`Expected coordinate, got ${token.kind}`);
744
+ }
745
+ parseBlockId() {
746
+ let id = this.advance().value;
747
+ if (this.match(':'))
748
+ id += ':' + this.advance().value;
749
+ if (this.check('[')) {
750
+ id += this.advance().value;
751
+ while (!this.check(']') && !this.check('eof'))
752
+ id += this.advance().value;
753
+ id += this.advance().value;
754
+ }
755
+ return id;
756
+ }
757
+ }
758
+ exports.ExprParser = ExprParser;
759
+ //# sourceMappingURL=expr-parser.js.map