@manifesto-ai/compiler 1.6.2 → 1.8.0

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 (201) hide show
  1. package/README.md +67 -18
  2. package/dist/chunk-BH25NHMN.js +74 -0
  3. package/dist/chunk-BH25NHMN.js.map +1 -0
  4. package/dist/chunk-D62NIFP4.js +33 -0
  5. package/dist/chunk-D62NIFP4.js.map +1 -0
  6. package/dist/chunk-MKLDAZ2Z.js +6920 -0
  7. package/dist/chunk-MKLDAZ2Z.js.map +1 -0
  8. package/dist/esbuild.d.ts +8 -0
  9. package/dist/esbuild.js +14 -0
  10. package/dist/esbuild.js.map +1 -0
  11. package/dist/index.d.ts +2810 -11
  12. package/dist/index.js +2425 -43
  13. package/dist/index.js.map +1 -1
  14. package/dist/node-loader.d.ts +18 -0
  15. package/dist/node-loader.js +47 -0
  16. package/dist/node-loader.js.map +1 -0
  17. package/dist/rollup.d.ts +8 -0
  18. package/dist/rollup.js +14 -0
  19. package/dist/rollup.js.map +1 -0
  20. package/dist/rspack.d.ts +7 -0
  21. package/dist/rspack.js +14 -0
  22. package/dist/rspack.js.map +1 -0
  23. package/dist/unplugin-6wnvFiEo.d.ts +17 -0
  24. package/dist/vite.d.ts +8 -17
  25. package/dist/vite.js +13 -33
  26. package/dist/vite.js.map +1 -1
  27. package/dist/webpack.d.ts +8 -0
  28. package/dist/webpack.js +14 -0
  29. package/dist/webpack.js.map +1 -0
  30. package/package.json +40 -22
  31. package/dist/analyzer/index.d.ts +0 -6
  32. package/dist/analyzer/index.d.ts.map +0 -1
  33. package/dist/analyzer/index.js +0 -6
  34. package/dist/analyzer/index.js.map +0 -1
  35. package/dist/analyzer/scope.d.ts +0 -77
  36. package/dist/analyzer/scope.d.ts.map +0 -1
  37. package/dist/analyzer/scope.js +0 -296
  38. package/dist/analyzer/scope.js.map +0 -1
  39. package/dist/analyzer/validator.d.ts +0 -60
  40. package/dist/analyzer/validator.d.ts.map +0 -1
  41. package/dist/analyzer/validator.js +0 -439
  42. package/dist/analyzer/validator.js.map +0 -1
  43. package/dist/api/compile-mel-patch-collector.d.ts +0 -32
  44. package/dist/api/compile-mel-patch-collector.d.ts.map +0 -1
  45. package/dist/api/compile-mel-patch-collector.js +0 -425
  46. package/dist/api/compile-mel-patch-collector.js.map +0 -1
  47. package/dist/api/compile-mel-patch-expr.d.ts +0 -9
  48. package/dist/api/compile-mel-patch-expr.d.ts.map +0 -1
  49. package/dist/api/compile-mel-patch-expr.js +0 -179
  50. package/dist/api/compile-mel-patch-expr.js.map +0 -1
  51. package/dist/api/compile-mel-patch-location.d.ts +0 -10
  52. package/dist/api/compile-mel-patch-location.d.ts.map +0 -1
  53. package/dist/api/compile-mel-patch-location.js +0 -48
  54. package/dist/api/compile-mel-patch-location.js.map +0 -1
  55. package/dist/api/compile-mel-patch.d.ts +0 -6
  56. package/dist/api/compile-mel-patch.d.ts.map +0 -1
  57. package/dist/api/compile-mel-patch.js +0 -244
  58. package/dist/api/compile-mel-patch.js.map +0 -1
  59. package/dist/api/compile-mel.d.ts +0 -126
  60. package/dist/api/compile-mel.d.ts.map +0 -1
  61. package/dist/api/compile-mel.js +0 -114
  62. package/dist/api/compile-mel.js.map +0 -1
  63. package/dist/api/index.d.ts +0 -10
  64. package/dist/api/index.d.ts.map +0 -1
  65. package/dist/api/index.js +0 -9
  66. package/dist/api/index.js.map +0 -1
  67. package/dist/diagnostics/codes.d.ts +0 -25
  68. package/dist/diagnostics/codes.d.ts.map +0 -1
  69. package/dist/diagnostics/codes.js +0 -154
  70. package/dist/diagnostics/codes.js.map +0 -1
  71. package/dist/diagnostics/index.d.ts +0 -6
  72. package/dist/diagnostics/index.d.ts.map +0 -1
  73. package/dist/diagnostics/index.js +0 -6
  74. package/dist/diagnostics/index.js.map +0 -1
  75. package/dist/diagnostics/types.d.ts +0 -67
  76. package/dist/diagnostics/types.d.ts.map +0 -1
  77. package/dist/diagnostics/types.js +0 -58
  78. package/dist/diagnostics/types.js.map +0 -1
  79. package/dist/evaluation/context.d.ts +0 -91
  80. package/dist/evaluation/context.d.ts.map +0 -1
  81. package/dist/evaluation/context.js +0 -53
  82. package/dist/evaluation/context.js.map +0 -1
  83. package/dist/evaluation/evaluate-expr.d.ts +0 -24
  84. package/dist/evaluation/evaluate-expr.d.ts.map +0 -1
  85. package/dist/evaluation/evaluate-expr.js +0 -577
  86. package/dist/evaluation/evaluate-expr.js.map +0 -1
  87. package/dist/evaluation/evaluate-patch.d.ts +0 -123
  88. package/dist/evaluation/evaluate-patch.d.ts.map +0 -1
  89. package/dist/evaluation/evaluate-patch.js +0 -202
  90. package/dist/evaluation/evaluate-patch.js.map +0 -1
  91. package/dist/evaluation/evaluate-runtime-patch.d.ts +0 -86
  92. package/dist/evaluation/evaluate-runtime-patch.d.ts.map +0 -1
  93. package/dist/evaluation/evaluate-runtime-patch.js +0 -185
  94. package/dist/evaluation/evaluate-runtime-patch.js.map +0 -1
  95. package/dist/evaluation/index.d.ts +0 -15
  96. package/dist/evaluation/index.d.ts.map +0 -1
  97. package/dist/evaluation/index.js +0 -13
  98. package/dist/evaluation/index.js.map +0 -1
  99. package/dist/generator/index.d.ts +0 -7
  100. package/dist/generator/index.d.ts.map +0 -1
  101. package/dist/generator/index.js +0 -7
  102. package/dist/generator/index.js.map +0 -1
  103. package/dist/generator/ir.d.ts +0 -348
  104. package/dist/generator/ir.d.ts.map +0 -1
  105. package/dist/generator/ir.js +0 -715
  106. package/dist/generator/ir.js.map +0 -1
  107. package/dist/generator/lowering.d.ts +0 -11
  108. package/dist/generator/lowering.d.ts.map +0 -1
  109. package/dist/generator/lowering.js +0 -369
  110. package/dist/generator/lowering.js.map +0 -1
  111. package/dist/generator/normalizer.d.ts +0 -16
  112. package/dist/generator/normalizer.d.ts.map +0 -1
  113. package/dist/generator/normalizer.js +0 -181
  114. package/dist/generator/normalizer.js.map +0 -1
  115. package/dist/index.d.ts.map +0 -1
  116. package/dist/lexer/index.d.ts +0 -7
  117. package/dist/lexer/index.d.ts.map +0 -1
  118. package/dist/lexer/index.js +0 -7
  119. package/dist/lexer/index.js.map +0 -1
  120. package/dist/lexer/lexer.d.ts +0 -59
  121. package/dist/lexer/lexer.d.ts.map +0 -1
  122. package/dist/lexer/lexer.js +0 -433
  123. package/dist/lexer/lexer.js.map +0 -1
  124. package/dist/lexer/source-location.d.ts +0 -41
  125. package/dist/lexer/source-location.d.ts.map +0 -1
  126. package/dist/lexer/source-location.js +0 -33
  127. package/dist/lexer/source-location.js.map +0 -1
  128. package/dist/lexer/tokens.d.ts +0 -47
  129. package/dist/lexer/tokens.d.ts.map +0 -1
  130. package/dist/lexer/tokens.js +0 -73
  131. package/dist/lexer/tokens.js.map +0 -1
  132. package/dist/loader.d.ts +0 -23
  133. package/dist/loader.d.ts.map +0 -1
  134. package/dist/loader.js +0 -62
  135. package/dist/loader.js.map +0 -1
  136. package/dist/lowering/context.d.ts +0 -96
  137. package/dist/lowering/context.d.ts.map +0 -1
  138. package/dist/lowering/context.js +0 -42
  139. package/dist/lowering/context.js.map +0 -1
  140. package/dist/lowering/errors.d.ts +0 -84
  141. package/dist/lowering/errors.d.ts.map +0 -1
  142. package/dist/lowering/errors.js +0 -81
  143. package/dist/lowering/errors.js.map +0 -1
  144. package/dist/lowering/index.d.ts +0 -20
  145. package/dist/lowering/index.d.ts.map +0 -1
  146. package/dist/lowering/index.js +0 -13
  147. package/dist/lowering/index.js.map +0 -1
  148. package/dist/lowering/lower-expr.d.ts +0 -76
  149. package/dist/lowering/lower-expr.d.ts.map +0 -1
  150. package/dist/lowering/lower-expr.js +0 -366
  151. package/dist/lowering/lower-expr.js.map +0 -1
  152. package/dist/lowering/lower-patch.d.ts +0 -231
  153. package/dist/lowering/lower-patch.d.ts.map +0 -1
  154. package/dist/lowering/lower-patch.js +0 -146
  155. package/dist/lowering/lower-patch.js.map +0 -1
  156. package/dist/lowering/lower-runtime-patch.d.ts +0 -100
  157. package/dist/lowering/lower-runtime-patch.d.ts.map +0 -1
  158. package/dist/lowering/lower-runtime-patch.js +0 -49
  159. package/dist/lowering/lower-runtime-patch.js.map +0 -1
  160. package/dist/mel-module.d.ts +0 -13
  161. package/dist/mel-module.d.ts.map +0 -1
  162. package/dist/mel-module.js +0 -33
  163. package/dist/mel-module.js.map +0 -1
  164. package/dist/parser/ast.d.ts +0 -344
  165. package/dist/parser/ast.d.ts.map +0 -1
  166. package/dist/parser/ast.js +0 -24
  167. package/dist/parser/ast.js.map +0 -1
  168. package/dist/parser/index.d.ts +0 -7
  169. package/dist/parser/index.d.ts.map +0 -1
  170. package/dist/parser/index.js +0 -7
  171. package/dist/parser/index.js.map +0 -1
  172. package/dist/parser/parser.d.ts +0 -92
  173. package/dist/parser/parser.d.ts.map +0 -1
  174. package/dist/parser/parser.js +0 -892
  175. package/dist/parser/parser.js.map +0 -1
  176. package/dist/parser/precedence.d.ts +0 -44
  177. package/dist/parser/precedence.d.ts.map +0 -1
  178. package/dist/parser/precedence.js +0 -69
  179. package/dist/parser/precedence.js.map +0 -1
  180. package/dist/renderer/expr-node.d.ts +0 -172
  181. package/dist/renderer/expr-node.d.ts.map +0 -1
  182. package/dist/renderer/expr-node.js +0 -218
  183. package/dist/renderer/expr-node.js.map +0 -1
  184. package/dist/renderer/fragment.d.ts +0 -84
  185. package/dist/renderer/fragment.d.ts.map +0 -1
  186. package/dist/renderer/fragment.js +0 -172
  187. package/dist/renderer/fragment.js.map +0 -1
  188. package/dist/renderer/index.d.ts +0 -23
  189. package/dist/renderer/index.d.ts.map +0 -1
  190. package/dist/renderer/index.js +0 -27
  191. package/dist/renderer/index.js.map +0 -1
  192. package/dist/renderer/patch-op.d.ts +0 -82
  193. package/dist/renderer/patch-op.d.ts.map +0 -1
  194. package/dist/renderer/patch-op.js +0 -204
  195. package/dist/renderer/patch-op.js.map +0 -1
  196. package/dist/renderer/type-expr.d.ts +0 -61
  197. package/dist/renderer/type-expr.d.ts.map +0 -1
  198. package/dist/renderer/type-expr.js +0 -131
  199. package/dist/renderer/type-expr.js.map +0 -1
  200. package/dist/vite.d.ts.map +0 -1
  201. package/loader.cjs +0 -22
@@ -1,892 +0,0 @@
1
- /**
2
- * MEL Parser
3
- * Recursive descent parser with Pratt parsing for expressions
4
- * Based on MEL SPEC v0.3.3 Section 4
5
- */
6
- import { mergeLocations } from "../lexer/source-location.js";
7
- import { getBinaryPrecedence, tokenToBinaryOp, isRightAssociative, } from "./precedence.js";
8
- /**
9
- * Parser for MEL source code
10
- */
11
- export class Parser {
12
- tokens;
13
- current = 0;
14
- diagnostics = [];
15
- constructor(tokens) {
16
- this.tokens = tokens;
17
- }
18
- /**
19
- * Parse tokens into an AST
20
- */
21
- parse() {
22
- try {
23
- const program = this.parseProgram();
24
- return { program, diagnostics: this.diagnostics };
25
- }
26
- catch (e) {
27
- // Unrecoverable error
28
- return { program: null, diagnostics: this.diagnostics };
29
- }
30
- }
31
- // ============ Program Structure ============
32
- parseProgram() {
33
- const start = this.peek().location;
34
- const imports = [];
35
- // Parse imports (if any)
36
- while (this.check("IMPORT")) {
37
- imports.push(this.parseImport());
38
- }
39
- // Parse domain
40
- const domain = this.parseDomain();
41
- return {
42
- kind: "program",
43
- imports,
44
- domain,
45
- location: mergeLocations(start, domain.location),
46
- };
47
- }
48
- parseImport() {
49
- const start = this.consume("IMPORT", "Expected 'import'").location;
50
- this.consume("LBRACE", "Expected '{' after 'import'");
51
- const names = [];
52
- do {
53
- names.push(this.consume("IDENTIFIER", "Expected identifier").lexeme);
54
- } while (this.match("COMMA"));
55
- this.consume("RBRACE", "Expected '}' after import names");
56
- this.consume("FROM", "Expected 'from' after import names");
57
- const fromToken = this.consume("STRING", "Expected string after 'from'");
58
- return {
59
- kind: "import",
60
- names,
61
- from: fromToken.value,
62
- location: mergeLocations(start, fromToken.location),
63
- };
64
- }
65
- parseDomain() {
66
- const start = this.consume("DOMAIN", "Expected 'domain'").location;
67
- const name = this.consume("IDENTIFIER", "Expected domain name").lexeme;
68
- this.consume("LBRACE", "Expected '{' after domain name");
69
- // v0.3.3: Parse type declarations first
70
- const types = [];
71
- while (this.check("TYPE") && !this.isAtEnd()) {
72
- types.push(this.parseTypeDecl());
73
- }
74
- const members = [];
75
- while (!this.check("RBRACE") && !this.isAtEnd()) {
76
- // v0.3.3: type declarations can appear anywhere in domain
77
- if (this.check("TYPE")) {
78
- types.push(this.parseTypeDecl());
79
- }
80
- else {
81
- const member = this.parseDomainMember();
82
- if (member)
83
- members.push(member);
84
- }
85
- }
86
- const end = this.consume("RBRACE", "Expected '}' to close domain").location;
87
- return {
88
- kind: "domain",
89
- name,
90
- types,
91
- members,
92
- location: mergeLocations(start, end),
93
- };
94
- }
95
- /**
96
- * v0.3.3: Parse type declaration
97
- * Syntax: type Name = TypeExpr
98
- */
99
- parseTypeDecl() {
100
- const start = this.consume("TYPE", "Expected 'type'").location;
101
- const name = this.consume("IDENTIFIER", "Expected type name").lexeme;
102
- this.consume("EQ", "Expected '=' after type name");
103
- const typeExpr = this.parseTypeExpr();
104
- return {
105
- kind: "typeDecl",
106
- name,
107
- typeExpr,
108
- location: mergeLocations(start, typeExpr.location),
109
- };
110
- }
111
- parseDomainMember() {
112
- if (this.check("STATE"))
113
- return this.parseState();
114
- if (this.check("COMPUTED"))
115
- return this.parseComputed();
116
- if (this.check("ACTION"))
117
- return this.parseAction();
118
- this.error(`Unexpected token '${this.peek().lexeme}'. Expected 'state', 'computed', or 'action'.`);
119
- this.advance(); // Skip the bad token
120
- return null;
121
- }
122
- // ============ State ============
123
- parseState() {
124
- const start = this.consume("STATE", "Expected 'state'").location;
125
- this.consume("LBRACE", "Expected '{' after 'state'");
126
- const fields = [];
127
- while (!this.check("RBRACE") && !this.isAtEnd()) {
128
- fields.push(this.parseStateField());
129
- }
130
- const end = this.consume("RBRACE", "Expected '}' to close state block").location;
131
- return {
132
- kind: "state",
133
- fields,
134
- location: mergeLocations(start, end),
135
- };
136
- }
137
- parseStateField() {
138
- const nameToken = this.consume("IDENTIFIER", "Expected field name");
139
- this.consume("COLON", "Expected ':' after field name");
140
- const typeExpr = this.parseTypeExpr();
141
- let initializer;
142
- if (this.match("EQ")) {
143
- initializer = this.parseExpression();
144
- }
145
- return {
146
- kind: "stateField",
147
- name: nameToken.lexeme,
148
- typeExpr,
149
- initializer,
150
- location: mergeLocations(nameToken.location, initializer?.location ?? typeExpr.location),
151
- };
152
- }
153
- // ============ Computed ============
154
- parseComputed() {
155
- const start = this.consume("COMPUTED", "Expected 'computed'").location;
156
- const name = this.consume("IDENTIFIER", "Expected computed name").lexeme;
157
- this.consume("EQ", "Expected '=' after computed name");
158
- const expression = this.parseExpression();
159
- return {
160
- kind: "computed",
161
- name,
162
- expression,
163
- location: mergeLocations(start, expression.location),
164
- };
165
- }
166
- // ============ Action ============
167
- parseAction() {
168
- const start = this.consume("ACTION", "Expected 'action'").location;
169
- const name = this.consume("IDENTIFIER", "Expected action name").lexeme;
170
- this.consume("LPAREN", "Expected '(' after action name");
171
- const params = [];
172
- if (!this.check("RPAREN")) {
173
- do {
174
- params.push(this.parseParam());
175
- } while (this.match("COMMA"));
176
- }
177
- this.consume("RPAREN", "Expected ')' after parameters");
178
- // v0.3.2: Parse optional 'available when <Expr>'
179
- let available;
180
- if (this.match("AVAILABLE")) {
181
- this.consume("WHEN", "Expected 'when' after 'available'");
182
- available = this.parseExpression();
183
- }
184
- this.consume("LBRACE", "Expected '{' to start action body");
185
- const body = [];
186
- while (!this.check("RBRACE") && !this.isAtEnd()) {
187
- const stmt = this.parseGuardedStmt();
188
- if (stmt)
189
- body.push(stmt);
190
- }
191
- const end = this.consume("RBRACE", "Expected '}' to close action").location;
192
- return {
193
- kind: "action",
194
- name,
195
- params,
196
- available,
197
- body,
198
- location: mergeLocations(start, end),
199
- };
200
- }
201
- parseParam() {
202
- const nameToken = this.consume("IDENTIFIER", "Expected parameter name");
203
- this.consume("COLON", "Expected ':' after parameter name");
204
- const typeExpr = this.parseTypeExpr();
205
- return {
206
- kind: "param",
207
- name: nameToken.lexeme,
208
- typeExpr,
209
- location: mergeLocations(nameToken.location, typeExpr.location),
210
- };
211
- }
212
- // ============ Statements ============
213
- parseGuardedStmt() {
214
- if (this.check("WHEN"))
215
- return this.parseWhenStmt();
216
- if (this.check("ONCE"))
217
- return this.parseOnceStmt();
218
- if (this.isOnceIntentContext())
219
- return this.parseOnceIntentStmt();
220
- this.error(`Unexpected token '${this.peek().lexeme}'. Expected 'when', 'once', or 'onceIntent'.`);
221
- this.advance();
222
- return null;
223
- }
224
- parseWhenStmt() {
225
- const start = this.consume("WHEN", "Expected 'when'").location;
226
- const condition = this.parseExpression();
227
- this.consume("LBRACE", "Expected '{' after when condition");
228
- const body = [];
229
- while (!this.check("RBRACE") && !this.isAtEnd()) {
230
- const stmt = this.parseInnerStmt();
231
- if (stmt)
232
- body.push(stmt);
233
- }
234
- const end = this.consume("RBRACE", "Expected '}' to close when block").location;
235
- return {
236
- kind: "when",
237
- condition,
238
- body,
239
- location: mergeLocations(start, end),
240
- };
241
- }
242
- parseOnceStmt() {
243
- const start = this.consume("ONCE", "Expected 'once'").location;
244
- this.consume("LPAREN", "Expected '(' after 'once'");
245
- const marker = this.parsePath();
246
- this.consume("RPAREN", "Expected ')' after marker");
247
- let condition;
248
- if (this.match("WHEN")) {
249
- condition = this.parseExpression();
250
- }
251
- this.consume("LBRACE", "Expected '{' to start once block");
252
- const body = [];
253
- while (!this.check("RBRACE") && !this.isAtEnd()) {
254
- const stmt = this.parseInnerStmt();
255
- if (stmt)
256
- body.push(stmt);
257
- }
258
- const end = this.consume("RBRACE", "Expected '}' to close once block").location;
259
- return {
260
- kind: "once",
261
- marker,
262
- condition,
263
- body,
264
- location: mergeLocations(start, end),
265
- };
266
- }
267
- parseOnceIntentStmt() {
268
- const startToken = this.consume("IDENTIFIER", "Expected 'onceIntent'");
269
- let condition;
270
- if (this.match("WHEN")) {
271
- condition = this.parseExpression();
272
- }
273
- this.consume("LBRACE", "Expected '{' to start onceIntent block");
274
- const body = [];
275
- while (!this.check("RBRACE") && !this.isAtEnd()) {
276
- const stmt = this.parseInnerStmt();
277
- if (stmt)
278
- body.push(stmt);
279
- }
280
- const end = this.consume("RBRACE", "Expected '}' to close onceIntent block").location;
281
- return {
282
- kind: "onceIntent",
283
- condition,
284
- body,
285
- location: mergeLocations(startToken.location, end),
286
- };
287
- }
288
- parseInnerStmt() {
289
- if (this.check("PATCH"))
290
- return this.parsePatchStmt();
291
- if (this.check("EFFECT"))
292
- return this.parseEffectStmt();
293
- if (this.check("WHEN"))
294
- return this.parseWhenStmt();
295
- if (this.check("ONCE"))
296
- return this.parseOnceStmt();
297
- if (this.isOnceIntentContext())
298
- return this.parseOnceIntentStmt();
299
- if (this.check("FAIL"))
300
- return this.parseFailStmt(); // v0.3.2
301
- if (this.check("STOP"))
302
- return this.parseStopStmt(); // v0.3.2
303
- this.error(`Unexpected token '${this.peek().lexeme}'. Expected 'patch', 'effect', 'when', 'once', 'onceIntent', 'fail', or 'stop'.`);
304
- this.advance();
305
- return null;
306
- }
307
- parsePatchStmt() {
308
- const start = this.consume("PATCH", "Expected 'patch'").location;
309
- const path = this.parsePath();
310
- let op;
311
- let value;
312
- let end;
313
- if (this.match("UNSET")) {
314
- op = "unset";
315
- end = this.previous().location;
316
- }
317
- else if (this.match("MERGE")) {
318
- op = "merge";
319
- value = this.parseExpression();
320
- end = value.location;
321
- }
322
- else {
323
- this.consume("EQ", "Expected '=', 'unset', or 'merge' after path");
324
- op = "set";
325
- value = this.parseExpression();
326
- end = value.location;
327
- }
328
- return {
329
- kind: "patch",
330
- path,
331
- op,
332
- value,
333
- location: mergeLocations(start, end),
334
- };
335
- }
336
- parseEffectStmt() {
337
- const start = this.consume("EFFECT", "Expected 'effect'").location;
338
- // Effect type: identifier.identifier...
339
- let effectType = this.consume("IDENTIFIER", "Expected effect type").lexeme;
340
- while (this.match("DOT")) {
341
- effectType += "." + this.consume("IDENTIFIER", "Expected identifier after '.'").lexeme;
342
- }
343
- this.consume("LPAREN", "Expected '(' after effect type");
344
- this.consume("LBRACE", "Expected '{' for effect arguments");
345
- const args = [];
346
- while (!this.check("RBRACE") && !this.isAtEnd()) {
347
- args.push(this.parseEffectArg());
348
- this.match("COMMA"); // Optional trailing comma
349
- }
350
- this.consume("RBRACE", "Expected '}' after effect arguments");
351
- const end = this.consume("RPAREN", "Expected ')' to close effect").location;
352
- return {
353
- kind: "effect",
354
- effectType,
355
- args,
356
- location: mergeLocations(start, end),
357
- };
358
- }
359
- parseEffectArg() {
360
- const nameToken = this.match("IDENTIFIER", "FAIL")
361
- ? this.previous()
362
- : this.consume("IDENTIFIER", "Expected argument name");
363
- this.consume("COLON", "Expected ':' after argument name");
364
- // 'into', 'pass', 'fail' are path arguments
365
- const isPath = ["into", "pass", "fail"].includes(nameToken.lexeme);
366
- const value = isPath ? this.parsePath() : this.parseExpression();
367
- return {
368
- kind: "effectArg",
369
- name: nameToken.lexeme,
370
- value,
371
- isPath,
372
- location: mergeLocations(nameToken.location, value.location),
373
- };
374
- }
375
- /**
376
- * v0.3.2: Parse fail statement
377
- * FailStmt ::= 'fail' StringLiteral ('with' Expr)?
378
- */
379
- parseFailStmt() {
380
- const start = this.consume("FAIL", "Expected 'fail'").location;
381
- const codeToken = this.consume("STRING", "Expected error code string after 'fail'");
382
- const code = codeToken.value;
383
- let message;
384
- let end = codeToken.location;
385
- if (this.match("WITH")) {
386
- message = this.parseExpression();
387
- end = message.location;
388
- }
389
- return {
390
- kind: "fail",
391
- code,
392
- message,
393
- location: mergeLocations(start, end),
394
- };
395
- }
396
- /**
397
- * v0.3.2: Parse stop statement
398
- * StopStmt ::= 'stop' StringLiteral
399
- */
400
- parseStopStmt() {
401
- const start = this.consume("STOP", "Expected 'stop'").location;
402
- const reasonToken = this.consume("STRING", "Expected reason string after 'stop'");
403
- const reason = reasonToken.value;
404
- return {
405
- kind: "stop",
406
- reason,
407
- location: mergeLocations(start, reasonToken.location),
408
- };
409
- }
410
- // ============ Types ============
411
- parseTypeExpr() {
412
- let type = this.parseBaseType();
413
- // Union type: T | U | V
414
- if (this.check("PIPE")) {
415
- const types = [type];
416
- while (this.match("PIPE")) {
417
- types.push(this.parseBaseType());
418
- }
419
- type = {
420
- kind: "unionType",
421
- types,
422
- location: mergeLocations(types[0].location, types[types.length - 1].location),
423
- };
424
- }
425
- return type;
426
- }
427
- parseBaseType() {
428
- // v0.3.3: Object type: { field: Type, ... }
429
- if (this.check("LBRACE")) {
430
- return this.parseObjectType();
431
- }
432
- // Literal types: "string" | 42 | true
433
- if (this.check("STRING")) {
434
- const token = this.advance();
435
- return {
436
- kind: "literalType",
437
- value: token.value,
438
- location: token.location,
439
- };
440
- }
441
- if (this.check("NUMBER")) {
442
- const token = this.advance();
443
- return {
444
- kind: "literalType",
445
- value: token.value,
446
- location: token.location,
447
- };
448
- }
449
- if (this.check("TRUE") || this.check("FALSE")) {
450
- const token = this.advance();
451
- return {
452
- kind: "literalType",
453
- value: token.kind === "TRUE",
454
- location: token.location,
455
- };
456
- }
457
- if (this.check("NULL")) {
458
- const token = this.advance();
459
- return {
460
- kind: "literalType",
461
- value: null,
462
- location: token.location,
463
- };
464
- }
465
- // Named type: Array<T>, Record<K, V>, or simple type
466
- const nameToken = this.consume("IDENTIFIER", "Expected type name");
467
- if (this.match("LT")) {
468
- // Generic type
469
- if (nameToken.lexeme === "Array") {
470
- const elementType = this.parseTypeExpr();
471
- const end = this.consume("GT", "Expected '>' after array element type").location;
472
- return {
473
- kind: "arrayType",
474
- elementType,
475
- location: mergeLocations(nameToken.location, end),
476
- };
477
- }
478
- else if (nameToken.lexeme === "Record") {
479
- const keyType = this.parseTypeExpr();
480
- this.consume("COMMA", "Expected ',' between Record type parameters");
481
- const valueType = this.parseTypeExpr();
482
- const end = this.consume("GT", "Expected '>' after Record value type").location;
483
- return {
484
- kind: "recordType",
485
- keyType,
486
- valueType,
487
- location: mergeLocations(nameToken.location, end),
488
- };
489
- }
490
- else {
491
- this.error(`Unknown generic type '${nameToken.lexeme}'`);
492
- // Consume until >
493
- while (!this.check("GT") && !this.isAtEnd())
494
- this.advance();
495
- this.match("GT");
496
- }
497
- }
498
- return {
499
- kind: "simpleType",
500
- name: nameToken.lexeme,
501
- location: nameToken.location,
502
- };
503
- }
504
- /**
505
- * v0.3.3: Parse object type
506
- * Syntax: { field: Type, field?: Type, ... }
507
- */
508
- parseObjectType() {
509
- const start = this.consume("LBRACE", "Expected '{'").location;
510
- const fields = [];
511
- while (!this.check("RBRACE") && !this.isAtEnd()) {
512
- const nameToken = this.consume("IDENTIFIER", "Expected field name");
513
- const optional = this.match("QUESTION");
514
- this.consume("COLON", "Expected ':' after field name");
515
- const typeExpr = this.parseTypeExpr();
516
- fields.push({
517
- kind: "typeField",
518
- name: nameToken.lexeme,
519
- typeExpr,
520
- optional,
521
- location: mergeLocations(nameToken.location, typeExpr.location),
522
- });
523
- // Optional comma between fields
524
- this.match("COMMA");
525
- }
526
- const end = this.consume("RBRACE", "Expected '}' to close object type").location;
527
- return {
528
- kind: "objectType",
529
- fields,
530
- location: mergeLocations(start, end),
531
- };
532
- }
533
- // ============ Expressions (Pratt Parser) ============
534
- parseExpression(minPrecedence = 0 /* Precedence.NONE */) {
535
- let left = this.parsePrimary();
536
- while (true) {
537
- const precedence = getBinaryPrecedence(this.peek().kind);
538
- if (precedence <= minPrecedence)
539
- break;
540
- // Handle ternary specially
541
- if (this.peek().kind === "QUESTION") {
542
- left = this.parseTernary(left);
543
- continue;
544
- }
545
- const op = tokenToBinaryOp(this.peek().kind);
546
- if (!op)
547
- break;
548
- this.advance(); // Consume operator
549
- const nextPrecedence = isRightAssociative(this.previous().kind)
550
- ? precedence - 1
551
- : precedence;
552
- const right = this.parseExpression(nextPrecedence);
553
- left = {
554
- kind: "binary",
555
- operator: op,
556
- left,
557
- right,
558
- location: mergeLocations(left.location, right.location),
559
- };
560
- }
561
- return left;
562
- }
563
- parseTernary(condition) {
564
- this.consume("QUESTION", "Expected '?'");
565
- const consequent = this.parseExpression();
566
- this.consume("COLON", "Expected ':' in ternary expression");
567
- const alternate = this.parseExpression(1 /* Precedence.TERNARY */ - 1);
568
- return {
569
- kind: "ternary",
570
- condition,
571
- consequent,
572
- alternate,
573
- location: mergeLocations(condition.location, alternate.location),
574
- };
575
- }
576
- parsePrimary() {
577
- // Unary operators
578
- if (this.check("BANG") || (this.check("MINUS") && this.isUnaryContext())) {
579
- const op = this.advance();
580
- const operand = this.parsePrimary();
581
- return {
582
- kind: "unary",
583
- operator: op.kind === "BANG" ? "!" : "-",
584
- operand,
585
- location: mergeLocations(op.location, operand.location),
586
- };
587
- }
588
- // Grouping
589
- if (this.match("LPAREN")) {
590
- const expr = this.parseExpression();
591
- this.consume("RPAREN", "Expected ')' after expression");
592
- return expr;
593
- }
594
- // Object literal
595
- if (this.check("LBRACE")) {
596
- return this.parseObjectLiteral();
597
- }
598
- // Array literal
599
- if (this.check("LBRACKET")) {
600
- return this.parseArrayLiteral();
601
- }
602
- // Literals
603
- if (this.check("NUMBER")) {
604
- const token = this.advance();
605
- return {
606
- kind: "literal",
607
- value: token.value,
608
- literalType: "number",
609
- location: token.location,
610
- };
611
- }
612
- if (this.check("STRING")) {
613
- const token = this.advance();
614
- return {
615
- kind: "literal",
616
- value: token.value,
617
- literalType: "string",
618
- location: token.location,
619
- };
620
- }
621
- if (this.check("TRUE") || this.check("FALSE")) {
622
- const token = this.advance();
623
- return {
624
- kind: "literal",
625
- value: token.kind === "TRUE",
626
- literalType: "boolean",
627
- location: token.location,
628
- };
629
- }
630
- if (this.check("NULL")) {
631
- const token = this.advance();
632
- return {
633
- kind: "literal",
634
- value: null,
635
- literalType: "null",
636
- location: token.location,
637
- };
638
- }
639
- // System identifiers
640
- if (this.check("SYSTEM_IDENT")) {
641
- const token = this.advance();
642
- // Parse $system.uuid → ["system", "uuid"]
643
- const path = token.lexeme.slice(1).split(".");
644
- return this.parsePostfix({
645
- kind: "systemIdent",
646
- path,
647
- location: token.location,
648
- });
649
- }
650
- // v0.3.2: $acc removed - reduce pattern deprecated
651
- if (this.check("ITEM")) {
652
- const token = this.advance();
653
- return this.parsePostfix({
654
- kind: "iterationVar",
655
- name: "item",
656
- location: token.location,
657
- });
658
- }
659
- // Identifier or function call
660
- if (this.check("IDENTIFIER")) {
661
- const token = this.advance();
662
- // Function call
663
- if (this.check("LPAREN")) {
664
- return this.parseFunctionCall(token);
665
- }
666
- // Plain identifier
667
- return this.parsePostfix({
668
- kind: "identifier",
669
- name: token.lexeme,
670
- location: token.location,
671
- });
672
- }
673
- this.error(`Unexpected token '${this.peek().lexeme}'`);
674
- return {
675
- kind: "literal",
676
- value: null,
677
- literalType: "null",
678
- location: this.peek().location,
679
- };
680
- }
681
- parseFunctionCall(nameToken) {
682
- this.consume("LPAREN", "Expected '(' for function call");
683
- const args = [];
684
- if (!this.check("RPAREN")) {
685
- do {
686
- args.push(this.parseExpression());
687
- } while (this.match("COMMA"));
688
- }
689
- const end = this.consume("RPAREN", "Expected ')' after arguments").location;
690
- return this.parsePostfix({
691
- kind: "functionCall",
692
- name: nameToken.lexeme,
693
- args,
694
- location: mergeLocations(nameToken.location, end),
695
- });
696
- }
697
- parsePostfix(expr) {
698
- while (true) {
699
- if (this.match("DOT")) {
700
- const prop = this.consume("IDENTIFIER", "Expected property name after '.'");
701
- expr = {
702
- kind: "propertyAccess",
703
- object: expr,
704
- property: prop.lexeme,
705
- location: mergeLocations(expr.location, prop.location),
706
- };
707
- }
708
- else if (this.match("LBRACKET")) {
709
- const index = this.parseExpression();
710
- const end = this.consume("RBRACKET", "Expected ']' after index").location;
711
- expr = {
712
- kind: "indexAccess",
713
- object: expr,
714
- index,
715
- location: mergeLocations(expr.location, end),
716
- };
717
- }
718
- else {
719
- break;
720
- }
721
- }
722
- return expr;
723
- }
724
- parseObjectLiteral() {
725
- const start = this.consume("LBRACE", "Expected '{'").location;
726
- const properties = [];
727
- while (!this.check("RBRACE") && !this.isAtEnd()) {
728
- const keyToken = this.consume("IDENTIFIER", "Expected property name");
729
- this.consume("COLON", "Expected ':' after property name");
730
- const value = this.parseExpression();
731
- properties.push({
732
- kind: "objectProperty",
733
- key: keyToken.lexeme,
734
- value,
735
- location: mergeLocations(keyToken.location, value.location),
736
- });
737
- if (!this.check("RBRACE")) {
738
- this.consume("COMMA", "Expected ',' between properties");
739
- }
740
- }
741
- const end = this.consume("RBRACE", "Expected '}' to close object").location;
742
- return {
743
- kind: "objectLiteral",
744
- properties,
745
- location: mergeLocations(start, end),
746
- };
747
- }
748
- parseArrayLiteral() {
749
- const start = this.consume("LBRACKET", "Expected '['").location;
750
- const elements = [];
751
- while (!this.check("RBRACKET") && !this.isAtEnd()) {
752
- elements.push(this.parseExpression());
753
- if (!this.check("RBRACKET")) {
754
- this.consume("COMMA", "Expected ',' between elements");
755
- }
756
- }
757
- const end = this.consume("RBRACKET", "Expected ']' to close array").location;
758
- return {
759
- kind: "arrayLiteral",
760
- elements,
761
- location: mergeLocations(start, end),
762
- };
763
- }
764
- // ============ Path ============
765
- parsePath() {
766
- const segments = [];
767
- const start = this.peek().location;
768
- // First segment must be identifier
769
- const first = this.consume("IDENTIFIER", "Expected identifier");
770
- segments.push({
771
- kind: "propertySegment",
772
- name: first.lexeme,
773
- location: first.location,
774
- });
775
- // Subsequent segments: .prop or [index]
776
- while (true) {
777
- if (this.match("DOT")) {
778
- const prop = this.consume("IDENTIFIER", "Expected property name");
779
- segments.push({
780
- kind: "propertySegment",
781
- name: prop.lexeme,
782
- location: prop.location,
783
- });
784
- }
785
- else if (this.match("LBRACKET")) {
786
- const index = this.parseExpression();
787
- const end = this.consume("RBRACKET", "Expected ']'").location;
788
- segments.push({
789
- kind: "indexSegment",
790
- index,
791
- location: mergeLocations(index.location, end),
792
- });
793
- }
794
- else {
795
- break;
796
- }
797
- }
798
- const last = segments[segments.length - 1];
799
- return {
800
- kind: "path",
801
- segments,
802
- location: mergeLocations(start, last.location),
803
- };
804
- }
805
- // ============ Helpers ============
806
- isUnaryContext() {
807
- // Minus is unary at start of expression or after operators
808
- if (this.current === 0)
809
- return true;
810
- const prev = this.previous();
811
- const unaryPrecedingTokens = [
812
- "LPAREN", "LBRACKET", "LBRACE", "COMMA", "COLON", "EQ",
813
- "PLUS", "MINUS", "STAR", "SLASH", "PERCENT",
814
- "EQ_EQ", "BANG_EQ", "LT", "LT_EQ", "GT", "GT_EQ",
815
- "AMP_AMP", "PIPE_PIPE", "BANG", "QUESTION", "QUESTION_QUESTION",
816
- ];
817
- return unaryPrecedingTokens.includes(prev.kind);
818
- }
819
- peek() {
820
- return this.tokens[this.current];
821
- }
822
- peekNext() {
823
- if (this.current + 1 >= this.tokens.length) {
824
- return this.tokens[this.tokens.length - 1];
825
- }
826
- return this.tokens[this.current + 1];
827
- }
828
- previous() {
829
- return this.tokens[this.current - 1];
830
- }
831
- isAtEnd() {
832
- return this.peek().kind === "EOF";
833
- }
834
- advance() {
835
- if (!this.isAtEnd())
836
- this.current++;
837
- return this.previous();
838
- }
839
- check(kind) {
840
- if (this.isAtEnd())
841
- return false;
842
- return this.peek().kind === kind;
843
- }
844
- isOnceIntentContext() {
845
- if (!this.check("IDENTIFIER"))
846
- return false;
847
- const token = this.peek();
848
- if (token.lexeme !== "onceIntent")
849
- return false;
850
- const next = this.peekNext();
851
- return next.kind === "LBRACE" || next.kind === "WHEN";
852
- }
853
- match(...kinds) {
854
- for (const kind of kinds) {
855
- if (this.check(kind)) {
856
- this.advance();
857
- return true;
858
- }
859
- }
860
- return false;
861
- }
862
- consume(kind, message) {
863
- if (this.check(kind))
864
- return this.advance();
865
- throw this.errorAtCurrent(message);
866
- }
867
- error(message) {
868
- this.diagnostics.push({
869
- severity: "error",
870
- code: "MEL_PARSER",
871
- message,
872
- location: this.previous().location,
873
- });
874
- }
875
- errorAtCurrent(message) {
876
- this.diagnostics.push({
877
- severity: "error",
878
- code: "MEL_PARSER",
879
- message,
880
- location: this.peek().location,
881
- });
882
- return new Error(message);
883
- }
884
- }
885
- /**
886
- * Parse tokens into an AST
887
- */
888
- export function parse(tokens) {
889
- const parser = new Parser(tokens);
890
- return parser.parse();
891
- }
892
- //# sourceMappingURL=parser.js.map