mimo-lang 1.1.1 → 2.0.6

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 (165) hide show
  1. package/.gitattributes +24 -0
  2. package/LICENSE +21 -0
  3. package/README.md +71 -39
  4. package/adapters/browserAdapter.js +86 -0
  5. package/adapters/nodeAdapter.js +101 -0
  6. package/bin/cli.js +80 -0
  7. package/bin/commands/convert.js +27 -0
  8. package/bin/commands/doctor.js +139 -0
  9. package/bin/commands/eval.js +39 -0
  10. package/bin/commands/fmt.js +109 -0
  11. package/bin/commands/help.js +72 -0
  12. package/bin/commands/lint.js +117 -0
  13. package/bin/commands/repl.js +24 -0
  14. package/bin/commands/run.js +64 -0
  15. package/bin/commands/test.js +126 -0
  16. package/bin/utils/colors.js +38 -0
  17. package/bin/utils/formatError.js +47 -0
  18. package/bin/utils/fs.js +57 -0
  19. package/bin/utils/version.js +8 -0
  20. package/build.js +18 -0
  21. package/bun.lock +74 -0
  22. package/index.js +48 -77
  23. package/index.web.js +364 -0
  24. package/interpreter/BuiltinFunction.js +32 -0
  25. package/interpreter/ErrorHandler.js +120 -0
  26. package/interpreter/ExpressionEvaluator.js +106 -0
  27. package/interpreter/Interpreter.js +172 -0
  28. package/interpreter/MimoError.js +112 -0
  29. package/interpreter/ModuleLoader.js +236 -0
  30. package/interpreter/StatementExecutor.js +107 -0
  31. package/interpreter/Utils.js +82 -0
  32. package/interpreter/Values.js +87 -0
  33. package/interpreter/coreBuiltins.js +490 -0
  34. package/interpreter/environment.js +99 -0
  35. package/interpreter/evaluators/binaryExpressionEvaluator.js +111 -0
  36. package/interpreter/evaluators/collectionEvaluator.js +151 -0
  37. package/interpreter/evaluators/functionCallEvaluator.js +76 -0
  38. package/interpreter/evaluators/literalEvaluator.js +27 -0
  39. package/interpreter/evaluators/moduleAccessEvaluator.js +25 -0
  40. package/interpreter/evaluators/templateLiteralEvaluator.js +20 -0
  41. package/interpreter/executors/BaseExecutor.js +37 -0
  42. package/interpreter/executors/ControlFlowExecutor.js +206 -0
  43. package/interpreter/executors/FunctionExecutor.js +126 -0
  44. package/interpreter/executors/PatternMatchExecutor.js +93 -0
  45. package/interpreter/executors/VariableExecutor.js +144 -0
  46. package/interpreter/index.js +8 -0
  47. package/interpreter/stdlib/array/accessFunctions.js +61 -0
  48. package/interpreter/stdlib/array/arrayUtils.js +36 -0
  49. package/interpreter/stdlib/array/higherOrderFunctions.js +285 -0
  50. package/interpreter/stdlib/array/searchFunctions.js +77 -0
  51. package/interpreter/stdlib/array/setFunctions.js +49 -0
  52. package/interpreter/stdlib/array/transformationFunctions.js +68 -0
  53. package/interpreter/stdlib/array.js +85 -0
  54. package/interpreter/stdlib/assert.js +143 -0
  55. package/interpreter/stdlib/datetime.js +170 -0
  56. package/interpreter/stdlib/env.js +54 -0
  57. package/interpreter/stdlib/fs.js +161 -0
  58. package/interpreter/stdlib/http.js +92 -0
  59. package/interpreter/stdlib/json.js +70 -0
  60. package/interpreter/stdlib/math.js +309 -0
  61. package/interpreter/stdlib/object.js +142 -0
  62. package/interpreter/stdlib/path.js +69 -0
  63. package/interpreter/stdlib/regex.js +134 -0
  64. package/interpreter/stdlib/string.js +260 -0
  65. package/interpreter/suggestions.js +46 -0
  66. package/lexer/Lexer.js +245 -0
  67. package/lexer/TokenTypes.js +131 -0
  68. package/lexer/createToken.js +11 -0
  69. package/lexer/tokenizers/commentTokenizer.js +45 -0
  70. package/lexer/tokenizers/literalTokenizer.js +163 -0
  71. package/lexer/tokenizers/symbolTokenizer.js +69 -0
  72. package/lexer/tokenizers/whitespaceTokenizer.js +36 -0
  73. package/package.json +29 -13
  74. package/parser/ASTNodes.js +448 -0
  75. package/parser/Parser.js +188 -0
  76. package/parser/expressions/atomicExpressions.js +165 -0
  77. package/parser/expressions/conditionalExpressions.js +0 -0
  78. package/parser/expressions/operatorExpressions.js +79 -0
  79. package/parser/expressions/primaryExpressions.js +77 -0
  80. package/parser/parseStatement.js +184 -0
  81. package/parser/parserExpressions.js +115 -0
  82. package/parser/parserUtils.js +19 -0
  83. package/parser/statements/controlFlowParsers.js +106 -0
  84. package/parser/statements/functionParsers.js +314 -0
  85. package/parser/statements/moduleParsers.js +57 -0
  86. package/parser/statements/patternMatchParsers.js +124 -0
  87. package/parser/statements/variableParsers.js +155 -0
  88. package/repl.js +325 -0
  89. package/test.js +47 -0
  90. package/tools/PrettyPrinter.js +3 -0
  91. package/tools/convert/Args.js +46 -0
  92. package/tools/convert/Registry.js +91 -0
  93. package/tools/convert/Transpiler.js +78 -0
  94. package/tools/convert/plugins/README.md +66 -0
  95. package/tools/convert/plugins/alya/index.js +10 -0
  96. package/tools/convert/plugins/alya/to_alya.js +289 -0
  97. package/tools/convert/plugins/alya/visitors/expressions.js +257 -0
  98. package/tools/convert/plugins/alya/visitors/statements.js +403 -0
  99. package/tools/convert/plugins/base_converter.js +228 -0
  100. package/tools/convert/plugins/javascript/index.js +10 -0
  101. package/tools/convert/plugins/javascript/mimo_runtime.js +265 -0
  102. package/tools/convert/plugins/javascript/to_js.js +155 -0
  103. package/tools/convert/plugins/javascript/visitors/expressions.js +197 -0
  104. package/tools/convert/plugins/javascript/visitors/patterns.js +102 -0
  105. package/tools/convert/plugins/javascript/visitors/statements.js +236 -0
  106. package/tools/convert/plugins/python/index.js +10 -0
  107. package/tools/convert/plugins/python/mimo_runtime.py +811 -0
  108. package/tools/convert/plugins/python/to_py.js +329 -0
  109. package/tools/convert/plugins/python/visitors/expressions.js +272 -0
  110. package/tools/convert/plugins/python/visitors/patterns.js +100 -0
  111. package/tools/convert/plugins/python/visitors/statements.js +257 -0
  112. package/tools/convert.js +102 -0
  113. package/tools/format/CommentAttacher.js +190 -0
  114. package/tools/format/CommentLexer.js +152 -0
  115. package/tools/format/Printer.js +849 -0
  116. package/tools/format/config.js +107 -0
  117. package/tools/formatter.js +169 -0
  118. package/tools/lint/Linter.js +391 -0
  119. package/tools/lint/config.js +114 -0
  120. package/tools/lint/rules/consistent-return.js +62 -0
  121. package/tools/lint/rules/max-depth.js +56 -0
  122. package/tools/lint/rules/no-empty-function.js +45 -0
  123. package/tools/lint/rules/no-magic-numbers.js +46 -0
  124. package/tools/lint/rules/no-shadow.js +113 -0
  125. package/tools/lint/rules/no-unused-vars.js +26 -0
  126. package/tools/lint/rules/prefer-const.js +19 -0
  127. package/tools/linter.js +261 -0
  128. package/tools/replFormatter.js +93 -0
  129. package/tools/stamp-version.js +32 -0
  130. package/web/index.js +9 -0
  131. package/bun.lockb +0 -0
  132. package/cli.js +0 -84
  133. package/compiler/execute/interpreter.js +0 -68
  134. package/compiler/execute/interpreters/binary.js +0 -12
  135. package/compiler/execute/interpreters/call.js +0 -10
  136. package/compiler/execute/interpreters/if.js +0 -10
  137. package/compiler/execute/interpreters/try-catch.js +0 -10
  138. package/compiler/execute/interpreters/while.js +0 -8
  139. package/compiler/execute/utils/createfunction.js +0 -11
  140. package/compiler/execute/utils/evaluate.js +0 -20
  141. package/compiler/execute/utils/operate.js +0 -23
  142. package/compiler/lexer/processToken.js +0 -40
  143. package/compiler/lexer/tokenTypes.js +0 -4
  144. package/compiler/lexer/tokenizer.js +0 -74
  145. package/compiler/parser/expression/comparison.js +0 -18
  146. package/compiler/parser/expression/identifier.js +0 -29
  147. package/compiler/parser/expression/number.js +0 -10
  148. package/compiler/parser/expression/operator.js +0 -21
  149. package/compiler/parser/expression/punctuation.js +0 -31
  150. package/compiler/parser/expression/string.js +0 -6
  151. package/compiler/parser/parseExpression.js +0 -27
  152. package/compiler/parser/parseStatement.js +0 -34
  153. package/compiler/parser/parser.js +0 -45
  154. package/compiler/parser/statement/call.js +0 -26
  155. package/compiler/parser/statement/function.js +0 -29
  156. package/compiler/parser/statement/if.js +0 -34
  157. package/compiler/parser/statement/return.js +0 -10
  158. package/compiler/parser/statement/set.js +0 -11
  159. package/compiler/parser/statement/show.js +0 -10
  160. package/compiler/parser/statement/try-catch.js +0 -25
  161. package/compiler/parser/statement/while.js +0 -22
  162. package/converter/go/convert.js +0 -110
  163. package/converter/js/convert.js +0 -107
  164. package/jsconfig.json +0 -27
  165. package/vite.config.js +0 -17
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "mimo-lang",
3
- "version": "1.1.1",
4
- "description": "A programming language made in javascript mostly for learning purposes.",
3
+ "version": "2.0.6",
4
+ "description": "A minimal prefix-notation programming language",
5
5
  "keywords": [
6
- "programing language"
6
+ "programming-language",
7
+ "prefix-notation",
8
+ "language",
9
+ "interpreter"
7
10
  ],
8
11
  "author": "bethropolis",
9
12
  "license": "MIT",
@@ -11,20 +14,33 @@
11
14
  "module": "index.js",
12
15
  "type": "module",
13
16
  "bin": {
14
- "mimo": "./cli.js"
17
+ "mimo": "./bin/cli.js"
15
18
  },
16
19
  "scripts": {
17
- "mimo": "bun cli.js",
18
- "dev": "bun index.js",
19
- "test": "node test/index.js",
20
- "build": "vite build"
20
+ "mimo": "bun bin/cli.js",
21
+ "dev": "bun test.js",
22
+ "build": "bun build.js",
23
+ "prebuild:mimo": "bun tools/stamp-version.js",
24
+ "build:mimo": "bun build ./bin/cli.js --compile --outfile mimo",
25
+ "build:all": "bun build.js && bun run ext:package && bun run web:build",
26
+ "convert": "bun tools/convert.js",
27
+ "test": "bun bin/cli.js test test/source",
28
+ "lint": "bun bin/cli.js lint test/source",
29
+ "lint:strict": "bun bin/cli.js lint --fail-on-warning test/source/all.mimo",
30
+ "format:check": "bun bin/cli.js fmt --check test/source",
31
+ "format:write": "bun bin/cli.js fmt --write test/source",
32
+ "check": "bun run lint:strict && bun run test",
33
+ "ext:validate": "bun run --cwd extensions/mimo-vscode validate",
34
+ "ext:test": "bun run --cwd extensions/mimo-vscode test",
35
+ "ext:check": "bun run --cwd extensions/mimo-vscode check",
36
+ "ext:package": "bun run --cwd extensions/mimo-vscode package",
37
+ "web:dev": "bun run --cwd playground dev --open",
38
+ "web:build": "bun run --cwd playground build",
39
+ "web:preview": "bun run --cwd playground preview"
21
40
  },
22
41
  "devDependencies": {
23
- "@types/bun": "latest"
24
- },
25
- "dependencies": {
26
- "js-beautify": "^1.15.1",
27
- "vite": "^5.2.10"
42
+ "@types/bun": "latest",
43
+ "esbuild": "^0.25.5"
28
44
  },
29
45
  "repository": {
30
46
  "type": "git",
@@ -0,0 +1,448 @@
1
+ export const ASTNode = {
2
+ Program: (body, firstToken) => ({ // Add firstToken parameter
3
+ type: "Program",
4
+ body,
5
+ // Ensure these properties come from the token
6
+ line: firstToken.line,
7
+ column: firstToken.column,
8
+ start: firstToken.start, // Add start
9
+ length: firstToken.length, // Add length
10
+ file: firstToken.file,
11
+ }),
12
+ VariableDeclaration: (identifier, value, kind, isExported, token) => ({
13
+ type: "VariableDeclaration",
14
+ identifier,
15
+ value,
16
+ kind,
17
+ isExported,
18
+ line: token.line,
19
+ column: token.column,
20
+ start: token.start, // Add start
21
+ length: token.length, // Add length
22
+ file: token.file,
23
+ }),
24
+ BinaryExpression: (operator, left, right, token) => ({
25
+ type: "BinaryExpression",
26
+ operator,
27
+ left,
28
+ right,
29
+ line: token.line,
30
+ column: token.column,
31
+ start: token.start,
32
+ length: token.length,
33
+ file: token.file,
34
+ }),
35
+ UnaryExpression: (operator, argument, token) => ({
36
+ type: "UnaryExpression",
37
+ operator,
38
+ argument,
39
+ line: token.line,
40
+ column: token.column,
41
+ start: token.start,
42
+ length: token.length,
43
+ file: token.file,
44
+ }),
45
+ IfStatement: (condition, consequent, alternate, token) => ({
46
+ type: "IfStatement",
47
+ condition,
48
+ consequent,
49
+ alternate,
50
+ line: token.line,
51
+ column: token.column,
52
+ start: token.start,
53
+ length: token.length,
54
+ file: token.file,
55
+ }),
56
+ GuardStatement: (condition, alternate, token) => ({
57
+ type: "GuardStatement",
58
+ condition,
59
+ alternate, // The else block that must exit
60
+ line: token.line,
61
+ column: token.column,
62
+ start: token.start,
63
+ length: token.length,
64
+ file: token.file,
65
+ }),
66
+ InlineIfExpression: (condition, consequent, alternate, token) => ({
67
+ type: "InlineIfExpression",
68
+ condition,
69
+ consequent, // Single expression here, not a block
70
+ alternate, // Single expression here, not a block
71
+ line: token.line,
72
+ column: token.column,
73
+ start: token.start,
74
+ length: token.length,
75
+ file: token.file,
76
+ }),
77
+ PipeExpression: (left, callee, args, token) => ({
78
+ type: "PipeExpression",
79
+ left, // The value being piped
80
+ callee, // The function to pipe into (Identifier, ModuleAccess, or InlineIfExpression)
81
+ args, // Any extra arguments after the piped value
82
+ line: token.line,
83
+ column: token.column,
84
+ start: token.start,
85
+ length: token.length,
86
+ file: token.file,
87
+ }),
88
+ WhileStatement: (condition, body, token) => ({
89
+ type: "WhileStatement",
90
+ condition,
91
+ body,
92
+ line: token.line,
93
+ column: token.column,
94
+ start: token.start,
95
+ length: token.length,
96
+ file: token.file,
97
+ }),
98
+ ForStatement: (variable, iterable, body, token) => ({
99
+ type: "ForStatement",
100
+ variable,
101
+ iterable,
102
+ body,
103
+ line: token.line,
104
+ column: token.column,
105
+ start: token.start,
106
+ length: token.length,
107
+ file: token.file,
108
+ }),
109
+ RangeLiteral: (start, end, token) => ({
110
+ type: "RangeLiteral",
111
+ start,
112
+ end,
113
+ line: token.line,
114
+ column: token.column,
115
+ length: token.length,
116
+ file: token.file,
117
+ }),
118
+ TryStatement: (tryBlock, catchVar, catchBlock, token) => ({
119
+ type: "TryStatement",
120
+ tryBlock,
121
+ catchVar,
122
+ catchBlock,
123
+ line: token.line,
124
+ column: token.column,
125
+ start: token.start,
126
+ length: token.length,
127
+ file: token.file,
128
+ }),
129
+ ThrowStatement: (argument, token) => ({
130
+ type: "ThrowStatement",
131
+ argument,
132
+ line: token.line,
133
+ column: token.column,
134
+ start: token.start,
135
+ length: token.length,
136
+ file: token.file,
137
+ }),
138
+ FunctionDeclaration: (name, params, defaults, restParam, body, isExported, decorators, token) => ({
139
+ type: "FunctionDeclaration",
140
+ name,
141
+ params, // This should be an array of strings (parameter names)
142
+ defaults,
143
+ restParam, // This should be a string (rest parameter name) or null
144
+ body,
145
+ isExported,
146
+ decorators, // Array of Decorator nodes
147
+ line: token.line,
148
+ column: token.column,
149
+ start: token.start,
150
+ length: token.length,
151
+ file: token.file,
152
+ }),
153
+ Decorator: (name, args, token) => ({
154
+ type: "Decorator",
155
+ name, // Identifier node
156
+ arguments: args, // Array of expressions or null
157
+ line: token.line,
158
+ column: token.column,
159
+ start: token.start,
160
+ length: token.length,
161
+ file: token.file,
162
+ }),
163
+ AnonymousFunction: (params, defaults, restParam, body, token, isFn = false) => ({
164
+ type: "AnonymousFunction",
165
+ params,
166
+ defaults,
167
+ restParam,
168
+ body,
169
+ isFn,
170
+ line: token.line,
171
+ column: token.column,
172
+ start: token.start,
173
+ length: token.length,
174
+ file: token.file,
175
+ }),
176
+ CallStatement: (callee, args, destination, token) => ({
177
+ type: "CallStatement",
178
+ callee,
179
+ arguments: args,
180
+ destination,
181
+ line: token.line,
182
+ column: token.column,
183
+ start: token.start,
184
+ length: token.length,
185
+ file: token.file,
186
+ }),
187
+ CallExpression: (callee, args, token) => ({
188
+ type: "CallExpression",
189
+ callee,
190
+ arguments: args,
191
+ line: token.line,
192
+ column: token.column,
193
+ start: token.start,
194
+ length: token.length,
195
+ file: token.file,
196
+ }),
197
+ SafeCallExpression: (callee, args, token) => ({
198
+ type: "SafeCallExpression",
199
+ callee,
200
+ arguments: args,
201
+ line: token.line,
202
+ column: token.column,
203
+ start: token.start,
204
+ length: token.length,
205
+ file: token.file,
206
+ }),
207
+ ShowStatement: (expression, token) => ({
208
+ type: "ShowStatement",
209
+ expression,
210
+ line: token.line,
211
+ column: token.column,
212
+ start: token.start,
213
+ length: token.length,
214
+ file: token.file,
215
+ }),
216
+ ReturnStatement: (argument, token) => ({
217
+ type: "ReturnStatement",
218
+ argument,
219
+ line: token.line,
220
+ column: token.column,
221
+ start: token.start,
222
+ length: token.length,
223
+ file: token.file,
224
+ }),
225
+ Identifier: (name, token) => ({
226
+ type: "Identifier",
227
+ name,
228
+ line: token.line,
229
+ column: token.column,
230
+ start: token.start,
231
+ length: token.length,
232
+ file: token.file,
233
+ }),
234
+ Literal: (value, token) => ({
235
+ type: "Literal",
236
+ value,
237
+ line: token.line,
238
+ column: token.column,
239
+ start: token.start,
240
+ length: token.length,
241
+ file: token.file,
242
+ }),
243
+ ArrayLiteral: (elements, token) => ({
244
+ type: "ArrayLiteral",
245
+ elements,
246
+ line: token.line,
247
+ column: token.column,
248
+ start: token.start,
249
+ length: token.length,
250
+ file: token.file,
251
+ }),
252
+ ObjectLiteral: (properties, token) => ({
253
+ type: "ObjectLiteral",
254
+ properties,
255
+ line: token.line,
256
+ column: token.column,
257
+ start: token.start,
258
+ length: token.length,
259
+ file: token.file,
260
+ }),
261
+ ArrayAccess: (object, index, token) => ({
262
+ type: "ArrayAccess",
263
+ object,
264
+ index,
265
+ line: token.line,
266
+ column: token.column,
267
+ start: token.start,
268
+ length: token.length,
269
+ file: token.file,
270
+ }),
271
+ SafeArrayAccess: (object, index, token) => ({
272
+ type: "SafeArrayAccess",
273
+ object,
274
+ index,
275
+ line: token.line,
276
+ column: token.column,
277
+ start: token.start,
278
+ length: token.length,
279
+ file: token.file,
280
+ }),
281
+ PropertyAccess: (object, property, token) => ({
282
+ type: "PropertyAccess",
283
+ object,
284
+ property,
285
+ line: token.line,
286
+ column: token.column,
287
+ start: token.start,
288
+ length: token.length,
289
+ file: token.file,
290
+ }),
291
+ SafePropertyAccess: (object, property, token) => ({
292
+ type: "SafePropertyAccess",
293
+ object,
294
+ property,
295
+ line: token.line,
296
+ column: token.column,
297
+ start: token.start,
298
+ length: token.length,
299
+ file: token.file,
300
+ }),
301
+ PropertyAssignment: (object, property, value, token) => ({
302
+ type: "PropertyAssignment",
303
+ object,
304
+ property,
305
+ value,
306
+ line: token.line,
307
+ column: token.column,
308
+ start: token.start,
309
+ length: token.length,
310
+ file: token.file,
311
+ }),
312
+ BracketAssignment: (object, index, value, token) => ({
313
+ type: "BracketAssignment",
314
+ object,
315
+ index,
316
+ value,
317
+ line: token.line,
318
+ column: token.column,
319
+ start: token.start,
320
+ length: token.length,
321
+ file: token.file,
322
+ }),
323
+ SpreadElement: (argument, token) => ({
324
+ type: "SpreadElement",
325
+ argument,
326
+ line: token.line,
327
+ column: token.column,
328
+ start: token.start,
329
+ length: token.length,
330
+ file: token.file,
331
+ }),
332
+ DestructuringAssignment: (pattern, expression, token) => ({
333
+ type: "DestructuringAssignment",
334
+ pattern,
335
+ expression,
336
+ line: token.line,
337
+ column: token.column,
338
+ start: token.start,
339
+ length: token.length,
340
+ file: token.file,
341
+ }),
342
+ BreakStatement: (label, token) => ({
343
+ type: "BreakStatement",
344
+ label,
345
+ line: token.line,
346
+ column: token.column,
347
+ start: token.start,
348
+ length: token.length,
349
+ file: token.file,
350
+ }),
351
+ ContinueStatement: (label, token) => ({
352
+ type: "ContinueStatement",
353
+ label,
354
+ line: token.line,
355
+ column: token.column,
356
+ start: token.start,
357
+ length: token.length,
358
+ file: token.file,
359
+ }),
360
+ LoopStatement: (body, label, token) => ({
361
+ type: "LoopStatement",
362
+ body,
363
+ label,
364
+ line: token.line,
365
+ column: token.column,
366
+ start: token.start,
367
+ length: token.length,
368
+ file: token.file,
369
+ }),
370
+ LabeledStatement: (label, statement, token) => ({
371
+ type: "LabeledStatement",
372
+ label,
373
+ statement,
374
+ line: token.line,
375
+ column: token.column,
376
+ start: token.start,
377
+ length: token.length,
378
+ file: token.file,
379
+ }),
380
+ MatchStatement: (discriminant, cases, token) => ({
381
+ type: "MatchStatement",
382
+ discriminant,
383
+ cases,
384
+ line: token.line,
385
+ column: token.column,
386
+ start: token.start,
387
+ length: token.length,
388
+ file: token.file,
389
+ }),
390
+ CaseClause: (pattern, guard, consequent, token) => ({
391
+ type: "CaseClause",
392
+ pattern,
393
+ guard,
394
+ consequent,
395
+ line: token.line,
396
+ column: token.column,
397
+ start: token.start,
398
+ length: token.length,
399
+ file: token.file,
400
+ }),
401
+ ArrayPattern: (elements, token) => ({
402
+ type: "ArrayPattern",
403
+ elements,
404
+ line: token.line,
405
+ column: token.column,
406
+ start: token.start,
407
+ length: token.length,
408
+ file: token.file,
409
+ }),
410
+ ObjectPattern: (properties, token) => ({
411
+ type: "ObjectPattern",
412
+ properties, // An array of Identifier nodes
413
+ line: token.line,
414
+ column: token.column,
415
+ start: token.start,
416
+ length: token.length,
417
+ file: token.file,
418
+ }),
419
+ ImportStatement: (path, alias, token) => ({
420
+ type: "ImportStatement",
421
+ path,
422
+ alias,
423
+ line: token.line,
424
+ column: token.column,
425
+ start: token.start,
426
+ length: token.length,
427
+ file: token.file,
428
+ }),
429
+ ModuleAccess: (module, property, token) => ({
430
+ type: "ModuleAccess",
431
+ module,
432
+ property,
433
+ line: token.line,
434
+ column: token.column,
435
+ start: token.start,
436
+ length: token.length,
437
+ file: token.file,
438
+ }),
439
+ TemplateLiteral: (parts, token) => ({
440
+ type: "TemplateLiteral",
441
+ parts,
442
+ line: token.line,
443
+ column: token.column,
444
+ start: token.start,
445
+ length: token.length,
446
+ file: token.file,
447
+ }),
448
+ };
@@ -0,0 +1,188 @@
1
+ // parser.js
2
+ import { TokenType } from "./../lexer/TokenTypes.js";
3
+ import { setupExpressionParsers } from "./parserExpressions.js";
4
+ import { MimoError } from "../interpreter/MimoError.js";
5
+ import { parseStatement } from "./parseStatement.js";
6
+ import { ASTNode } from "./ASTNodes.js";
7
+
8
+ export class Parser {
9
+ constructor(tokens, filePath = "unknown") {
10
+ this.tokens = tokens;
11
+ this.current = 0;
12
+ this.filePath = filePath; // Store filePath
13
+ this.errorHandler = null; // To be set by Interpreter or CLI for source context
14
+ }
15
+
16
+ // Method to set the error handler (which contains source code context)
17
+ setErrorHandler(errorHandler) {
18
+ this.errorHandler = errorHandler;
19
+ }
20
+
21
+ error(message, token = this.peek(), code = "SYN000", suggestion = "") {
22
+ let errorToken = token;
23
+ // If no valid token is provided or peek() returned undefined,
24
+ // try to use the token that was *just consumed* or a dummy EOF token.
25
+ if (
26
+ !errorToken ||
27
+ errorToken.type === undefined ||
28
+ errorToken.line === undefined
29
+ ) {
30
+ // Fallback to the last consumed token, or end-of-file dummy token
31
+ errorToken = this.tokens[this.current - 1] || {
32
+ // Last successfully consumed token
33
+ type: "EOF",
34
+ value: "",
35
+ line: 1,
36
+ column: 1,
37
+ start: 0,
38
+ length: 0,
39
+ file: this.filePath,
40
+ }; // Fallback
41
+ }
42
+ // Ensure the errorToken has the `file` property for the ErrorHandler
43
+ errorToken.file = errorToken.file || this.filePath;
44
+
45
+ // Use the ErrorHandler instance to create the MimoError
46
+ if (this.errorHandler) {
47
+ throw this.errorHandler.createSyntaxError(
48
+ message,
49
+ errorToken,
50
+ code,
51
+ suggestion
52
+ );
53
+ } else {
54
+ // Fallback: create a basic MimoError if no errorHandler is set
55
+ throw new MimoError("SyntaxError", code, message, suggestion, {
56
+ line: errorToken.line,
57
+ column: errorToken.column,
58
+ file: errorToken.file,
59
+ });
60
+ }
61
+ }
62
+
63
+ expect(type, value, code = "SYN000", suggestion = "Syntax error.") {
64
+ const token = this.peek();
65
+ if (token.type === type && (value === undefined || token.value === value)) {
66
+ return this.consume();
67
+ }
68
+ const displayValue = value ? `'${value}' (${type})` : type;
69
+ const displayGot = token.value
70
+ ? `'${token.value}' (${token.type})`
71
+ : token.type;
72
+ this.error(
73
+ `Expected ${displayValue} but got ${displayGot}.`,
74
+ token,
75
+ code,
76
+ suggestion
77
+ );
78
+ }
79
+
80
+ expectKeyword(keyword, code = "SYN000", suggestion = "Syntax error.") {
81
+ const token = this.peek();
82
+ if (token.type === TokenType.Keyword) {
83
+ // Handle both single keyword and array of keywords
84
+ if (Array.isArray(keyword)) {
85
+ if (keyword.includes(token.value)) {
86
+ return this.consume();
87
+ }
88
+ // If none of the keywords match, create error with all expected keywords
89
+ const expectedKeywords = keyword.map((k) => `'${k}'`).join(", ");
90
+ this.error(
91
+ `Expected one of ${expectedKeywords} but got '${token.value}' (${token.type}).`,
92
+ token,
93
+ code,
94
+ suggestion
95
+ );
96
+ } else {
97
+ // Single keyword case
98
+ if (token.value === keyword) {
99
+ return this.consume();
100
+ }
101
+ this.error(
102
+ `Expected '${keyword}' (keyword) but got '${token.value}' (${token.type}).`,
103
+ token,
104
+ code,
105
+ suggestion
106
+ );
107
+ }
108
+ }
109
+ this.error(
110
+ `Expected keyword but got '${token.value}' (${token.type}).`,
111
+ token,
112
+ code,
113
+ suggestion
114
+ );
115
+ }
116
+
117
+ match(type, value) {
118
+ const token = this.peek();
119
+ if (token && token.type === type && (!value || token.value === value)) {
120
+ this.consume();
121
+ return true;
122
+ }
123
+ return false;
124
+ }
125
+
126
+ matchKeyword(keyword) {
127
+ return this.match(TokenType.Keyword, keyword);
128
+ }
129
+
130
+ isAtEnd() {
131
+ return this.current >= this.tokens.length;
132
+ }
133
+
134
+ peek(index = 0) {
135
+ return this.tokens[this.current + index];
136
+ }
137
+
138
+ consume() {
139
+ const token = this.tokens[this.current];
140
+ this.current++;
141
+ return token;
142
+ }
143
+
144
+ // New: parseIdentifier method
145
+ parseIdentifier(code = "SYN000", suggestion = "Expected an identifier.") {
146
+ const token = this.peek();
147
+ if (token.type === TokenType.Identifier) {
148
+ this.consume();
149
+ return ASTNode.Identifier(token.value, token); // Pass full token for location
150
+ }
151
+ this.error(
152
+ `Expected an identifier but got ${token.type} '${token.value}'.`,
153
+ token,
154
+ code,
155
+ suggestion
156
+ );
157
+ }
158
+
159
+ parse() {
160
+ setupExpressionParsers();
161
+
162
+ const statements = [];
163
+ const programStartToken = this.tokens[0] || {
164
+ type: "ProgramStart",
165
+ value: "",
166
+ line: 1,
167
+ column: 1,
168
+ start: 0,
169
+ length: 0,
170
+ file: this.filePath,
171
+ };
172
+
173
+ while (!this.isAtEnd()) {
174
+ // This is important: skip newlines/semicolons between statements
175
+ // if your grammar allows them freely and they're not handled by indentation.
176
+ // For Mimo, typically, whitespace including newlines is skipped by lexer.
177
+ // So, this loop is often not needed here. But if your parser explicitly expects them, keep.
178
+ // For simplicity and matching current grammar assumptions, let's keep it minimal.
179
+ // If your lexer provides TokenType.Newline:
180
+ // while (this.match(TokenType.Newline));
181
+
182
+ if (this.isAtEnd()) break;
183
+
184
+ statements.push(parseStatement(this)); // <--- Call the imported function and pass 'this'
185
+ }
186
+ return ASTNode.Program(statements, programStartToken);
187
+ }
188
+ }