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
@@ -0,0 +1,106 @@
1
+ import { TokenType } from "../../lexer/TokenTypes.js";
2
+ import { ASTNode } from "../ASTNodes.js";
3
+ import { parseExpression } from "../parserExpressions.js";
4
+ import { parseBlock, isBlockEnd } from "../parserUtils.js";
5
+
6
+ export function parseIfStatement(parser) {
7
+ const ifToken = parser.expectKeyword("if", 'SYN046', 'Expected "if" keyword to start an if statement.');
8
+ const condition = parseExpression(parser);
9
+ // parser.expect(TokenType.Keyword, "then", 'SYNXXX', 'Expected "then" keyword after if condition.'); // If your language uses "then"
10
+ const consequent = parseBlock(parser, ["else", "end"]);
11
+
12
+ let alternate = null;
13
+ if (parser.matchKeyword("else")) {
14
+ if (parser.peek()?.value === "if" && parser.peek()?.type === TokenType.Keyword) {
15
+ alternate = parseIfStatement(parser); // Handles "else if"
16
+ } else {
17
+ alternate = parseBlock(parser, ["end"]);
18
+ }
19
+ }
20
+
21
+ parser.expectKeyword("end", 'SYN047', 'Expected "end" keyword to close if statement.');
22
+ return ASTNode.IfStatement(condition, consequent, alternate, ifToken);
23
+ }
24
+
25
+ export function parseGuardStatement(parser) {
26
+ const guardToken = parser.expectKeyword("guard", "SYN120", 'Expected "guard" keyword to start an guard statement.');
27
+ const condition = parseExpression(parser);
28
+ parser.expectKeyword("else", "SYN121", 'Expected "else" keyword after guard condition.');
29
+ const alternate = parseBlock(parser, ["end"]);
30
+ parser.expectKeyword("end", "SYN122", 'Expected "end" keyword to close guard statement.');
31
+ return ASTNode.GuardStatement(condition, alternate, guardToken);
32
+ }
33
+
34
+ export function parseWhileStatement(parser) {
35
+ const whileToken = parser.expect(TokenType.Keyword);
36
+ const condition = parseExpression(parser);
37
+ const body = parseBlock(parser);
38
+
39
+ parser.expect(TokenType.Keyword, "end");
40
+ return ASTNode.WhileStatement(condition, body, whileToken);
41
+ }
42
+
43
+ export function parseForStatement(parser) {
44
+ const forToken = parser.expectKeyword("for", 'SYN048', 'Expected "for" keyword to start a for loop.');
45
+ const loopVar = parser.parseIdentifier('SYN048A', 'Expected an identifier for the loop variable.');
46
+
47
+ parser.expectKeyword("in", 'SYN049', 'Expected "in" keyword in for loop (e.g., for item in iterable).');
48
+
49
+ const iterable = parseExpression(parser);
50
+
51
+ const body = parseBlock(parser, ["end"]);
52
+ parser.expectKeyword("end", 'SYN050', 'Expected "end" keyword to close for loop.');
53
+
54
+ return ASTNode.ForStatement(loopVar, iterable, body, forToken);
55
+ }
56
+
57
+ export function parseLoopStatement(parser) {
58
+ const loopToken = parser.expect(TokenType.Keyword);
59
+ const body = parseBlock(parser);
60
+ parser.expect(TokenType.Keyword, "end");
61
+
62
+ return ASTNode.LoopStatement(body, null, loopToken);
63
+ }
64
+
65
+ export function parseBreakStatement(parser) {
66
+ const breakToken = parser.expect(TokenType.Keyword);
67
+
68
+ // Check for optional label
69
+ let label = null;
70
+ if (parser.peek()?.type === TokenType.Identifier) {
71
+ label = parser.expect(TokenType.Identifier).value;
72
+ }
73
+
74
+ return ASTNode.BreakStatement(label, breakToken);
75
+ }
76
+
77
+ export function parseContinueStatement(parser) {
78
+ const continueToken = parser.expect(TokenType.Keyword);
79
+
80
+ // Check for optional label
81
+ let label = null;
82
+ if (parser.peek()?.type === TokenType.Identifier) {
83
+ label = parser.expect(TokenType.Identifier).value;
84
+ }
85
+
86
+ return ASTNode.ContinueStatement(label, continueToken);
87
+ }
88
+
89
+ export function parseTryStatement(parser) {
90
+ const tryToken = parser.expectKeyword("try", 'SYN051', 'Expected "try" keyword to start a try-catch block.');
91
+ const tryBlock = parseBlock(parser, ["catch", "end"]);
92
+
93
+ let catchVar = null;
94
+ let catchBlock = []; // Initialize as empty array
95
+ if (parser.matchKeyword("catch")) {
96
+ // Optional: allow specifying a variable for the caught error
97
+ if (parser.peek()?.type === TokenType.Identifier) {
98
+ catchVar = parser.parseIdentifier('SYN051A', 'Expected an identifier for the catch variable.');
99
+ }
100
+ catchBlock = parseBlock(parser, ["end"]);
101
+ }
102
+
103
+ parser.expectKeyword("end", 'SYN052', 'Expected "end" keyword to close try-catch block.');
104
+ return ASTNode.TryStatement(tryBlock, catchVar, catchBlock, tryToken);
105
+ }
106
+
@@ -0,0 +1,314 @@
1
+ import { MimoError } from "../../interpreter/MimoError.js";
2
+ import { TokenType } from "../../lexer/TokenTypes.js";
3
+ import { ASTNode } from "../ASTNodes.js";
4
+ import { parseExpression } from "../parserExpressions.js";
5
+ import { isBlockEnd, parseBlock } from "../parserUtils.js";
6
+
7
+ function parseParameterIdentifier(parser) {
8
+ const token = parser.peek();
9
+ if (token?.type === TokenType.Identifier || (token?.type === TokenType.Keyword && token.value === "fn")) {
10
+ parser.consume();
11
+ return ASTNode.Identifier(token.value, token);
12
+ }
13
+ parser.error('Expected a parameter name (identifier).', token, 'SYN021');
14
+ }
15
+
16
+ export function parseDecorator(parser) {
17
+ const atToken = parser.expect(TokenType.At, undefined, 'SYN140', 'Expected "@" at the start of a decorator.');
18
+ const nameToken = parser.expect(TokenType.Identifier, undefined, 'SYN141', 'Expected decorator name after "@".');
19
+ const name = ASTNode.Identifier(nameToken.value, nameToken);
20
+
21
+ let args = null;
22
+ if (parser.match(TokenType.LParen)) {
23
+ args = [];
24
+ if (parser.peek()?.type !== TokenType.RParen) {
25
+ do {
26
+ args.push(parseExpression(parser));
27
+ } while (parser.match(TokenType.Comma));
28
+ }
29
+ parser.expect(TokenType.RParen, undefined, 'SYN142', 'Expected closing parenthesis after decorator arguments.');
30
+ }
31
+
32
+ return ASTNode.Decorator(name, args, atToken);
33
+ }
34
+
35
+ export function parseDecorators(parser) {
36
+ const decorators = [];
37
+ while (parser.peek()?.type === TokenType.At) {
38
+ decorators.push(parseDecorator(parser));
39
+ }
40
+ return decorators;
41
+ }
42
+
43
+ export function parseFunctionDeclaration(parser, isExported = false, exportToken = null, decorators = []) {
44
+ // THIS IS THE FIX: This function now consumes the 'function' keyword itself.
45
+ const funcToken = parser.expectKeyword("function", 'SYNXXX', 'Expected "function" keyword.');
46
+
47
+ // The AST node's location should be the 'export' token if it exists, otherwise the 'function' token.
48
+ const astNodeLocationToken = exportToken || funcToken;
49
+
50
+ const nameToken = parser.expect(TokenType.Identifier, undefined, 'SYN054', 'Expected a function name (identifier).');
51
+ const name = nameToken.value;
52
+
53
+ parser.expect(TokenType.LParen, undefined, 'SYN055', 'Expected an opening parenthesis for function parameters.');
54
+
55
+ const params = [];
56
+ const defaults = {};
57
+ let restParam = null;
58
+ let hasEncounteredDefault = false;
59
+ let hasEncounteredRest = false;
60
+
61
+ if (parser.peek()?.type !== TokenType.RParen && parser.peek()?.value !== "->") {
62
+ do {
63
+ if (hasEncounteredRest) {
64
+ parser.error("No parameters allowed after a rest parameter.", parser.peek(), 'SYN060');
65
+ }
66
+
67
+ if (parser.match(TokenType.Spread)) {
68
+ const spreadToken = parser.peek(-1);
69
+ const paramNameToken = parser.expect(TokenType.Identifier, undefined, 'SYN056', 'Expected an identifier for rest parameter.');
70
+
71
+ restParam = ASTNode.Identifier(paramNameToken.value, spreadToken);
72
+ hasEncounteredRest = true;
73
+ if (parser.peek()?.type === TokenType.Comma) {
74
+ parser.error("Rest parameter must be the last parameter.", parser.peek(-1), 'SYN063');
75
+ }
76
+ break;
77
+ } else {
78
+ const paramNode = parseParameterIdentifier(parser);
79
+ params.push(paramNode); // Push the entire node
80
+
81
+ if (parser.match(TokenType.Colon)) {
82
+ defaults[paramNode.name] = parseExpression(parser); // Key by name
83
+ hasEncounteredDefault = true;
84
+ } else if (hasEncounteredDefault && !hasEncounteredRest) {
85
+ parser.error("Parameter without default value cannot follow parameter with default value.", paramNode, 'SYN_DEFAULT_ORDER');
86
+ }
87
+ }
88
+ } while (parser.match(TokenType.Comma));
89
+ }
90
+
91
+ parser.expect(TokenType.RParen, undefined, 'SYN026', 'Expected a closing parenthesis for function parameters.');
92
+
93
+ const body = parseBlock(parser);
94
+ const endToken = parser.expect(TokenType.Keyword, "end", 'SYN027', 'Expected "end" keyword to close function declaration.');
95
+
96
+ return ASTNode.FunctionDeclaration(
97
+ name,
98
+ params,
99
+ defaults,
100
+ restParam,
101
+ body,
102
+ isExported, // Pass isExported flag
103
+ decorators, // Pass practitioners decorators
104
+ astNodeLocationToken, // Pass the correct location token
105
+ endToken
106
+ );
107
+ }
108
+
109
+ // In: parser/statements/functionParsers.js
110
+
111
+ export function parseAnonymousFunction(parser, isFn = false) {
112
+ const funcToken = parser.peek(-1); // The `function` or `fn` keyword was just consumed.
113
+
114
+ if (!isFn) {
115
+ parser.expect(TokenType.LParen, undefined, 'SYN020', 'Expected an opening parenthesis for function parameters.');
116
+ }
117
+
118
+ const params = [];
119
+ const defaults = {};
120
+ let restParam = null;
121
+ let hasEncounteredDefault = false;
122
+ let hasEncounteredRest = false;
123
+
124
+ if (isFn) {
125
+ while (parser.peek() && (parser.peek().type !== TokenType.Operator || parser.peek().value !== "->")) {
126
+ if (hasEncounteredRest) {
127
+ parser.error("No parameters allowed after a rest parameter.", parser.peek(), 'SYN060');
128
+ }
129
+
130
+ if (parser.match(TokenType.Spread)) {
131
+ const spreadToken = parser.peek(-1);
132
+ const paramNameToken = parser.expect(TokenType.Identifier, undefined, 'SYN056', 'Expected an identifier for rest parameter.');
133
+ restParam = ASTNode.Identifier(paramNameToken.value, spreadToken);
134
+ hasEncounteredRest = true;
135
+ break;
136
+ }
137
+
138
+ const paramNode = parseParameterIdentifier(parser);
139
+ params.push(paramNode);
140
+
141
+ if (parser.match(TokenType.Colon)) {
142
+ defaults[paramNode.name] = parseExpression(parser);
143
+ hasEncounteredDefault = true;
144
+ } else if (hasEncounteredDefault && !hasEncounteredRest) {
145
+ parser.error("Parameter without default value cannot follow parameter with default value.", paramNode, 'SYN_DEFAULT_ORDER');
146
+ }
147
+ }
148
+
149
+ parser.expect(TokenType.Operator, "->", 'SYN136', 'Expected "->" after fn parameters.');
150
+ const expression = parseExpression(parser);
151
+ const body = [ASTNode.ReturnStatement(expression, funcToken)];
152
+
153
+ return ASTNode.AnonymousFunction(
154
+ params,
155
+ defaults,
156
+ restParam,
157
+ body,
158
+ funcToken,
159
+ true
160
+ );
161
+ }
162
+
163
+ if (parser.peek()?.type !== TokenType.RParen) {
164
+ do {
165
+ if (parser.peek()?.type === TokenType.Operator && parser.peek()?.value === "->") {
166
+ parser.consume();
167
+ break;
168
+ }
169
+ if (hasEncounteredRest) {
170
+ parser.error("No parameters allowed after a rest parameter.", parser.peek(), 'SYN060');
171
+ }
172
+
173
+ if (parser.match(TokenType.Spread)) {
174
+ const spreadToken = parser.peek(-1);
175
+ const paramNameToken = parser.expect(TokenType.Identifier, undefined, 'SYN056', 'Expected an identifier for rest parameter.');
176
+ restParam = ASTNode.Identifier(paramNameToken.value, spreadToken);
177
+ hasEncounteredRest = true;
178
+ if (parser.peek()?.type === TokenType.Comma) {
179
+ parser.error("Rest parameter must be the last parameter.", parser.peek(-1), 'SYN063');
180
+ }
181
+ break;
182
+ } else {
183
+ const paramNode = parseParameterIdentifier(parser);
184
+ params.push(paramNode); // Push the entire Identifier node
185
+
186
+ if (parser.match(TokenType.Colon)) {
187
+ defaults[paramNode.name] = parseExpression(parser);
188
+ hasEncounteredDefault = true;
189
+ } else if (hasEncounteredDefault && !hasEncounteredRest) {
190
+ parser.error("Parameter without default value cannot follow parameter with default value.", paramNode, 'SYN_DEFAULT_ORDER');
191
+ }
192
+ }
193
+
194
+ // Separator handling: '->' is always allowed. Comma is only for standard 'function' syntax.
195
+ if (parser.match(TokenType.Operator, "->")) {
196
+ // Arrow found — done with params, move to body
197
+ break;
198
+ } else if (parser.match(TokenType.Comma)) {
199
+ if (isFn) {
200
+ parser.error(
201
+ "Comma-separated parameters are not supported in 'fn' syntax. Use '->' to separate parameters from the body.",
202
+ parser.peek(-1),
203
+ 'SYN135',
204
+ "Write: fn x y -> expression end or use 'function' keyword for multi-param functions."
205
+ );
206
+ }
207
+ // If !isFn, we just consumed the comma normally, which is correct for standard syntax.
208
+ }
209
+ } while (parser.peek()?.type !== TokenType.RParen && parser.peek()?.value !== "->");
210
+
211
+ }
212
+
213
+ if (!isFn) {
214
+ parser.expect(TokenType.RParen, undefined, 'SYN026', 'Expected a closing parenthesis for function parameters.');
215
+ }
216
+
217
+ const body = parseBlock(parser);
218
+ const endToken = parser.expect(TokenType.Keyword, "end", 'SYN027', 'Expected "end" keyword to close function declaration.');
219
+
220
+ return ASTNode.AnonymousFunction(
221
+ params,
222
+ defaults,
223
+ restParam,
224
+ body,
225
+ funcToken,
226
+ false
227
+ );
228
+ }
229
+
230
+ export function parseCallExpressionParts(parser, callToken) {
231
+ let callee;
232
+ // Allow module access like MyModule.myFunction or simple identifier
233
+ const firstToken = parser.expect(TokenType.Identifier, undefined, 'SYN064', 'Expected a function name (identifier) or module name after "call".');
234
+
235
+ if (parser.match(TokenType.Operator, ".")) {
236
+ const propertyToken = parser.expect(TokenType.Identifier, undefined, 'SYN065', 'Expected a property name (identifier) after "." for module access.');
237
+ callee = ASTNode.ModuleAccess(firstToken.value, propertyToken.value, firstToken); // firstToken is start of module.prop
238
+ } else {
239
+ callee = ASTNode.Identifier(firstToken.value, firstToken);
240
+ }
241
+
242
+ parser.expect(TokenType.LParen, undefined, 'SYN066', 'Expected an opening parenthesis for function arguments.');
243
+ const args = [];
244
+
245
+ if (parser.peek()?.type !== TokenType.RParen) {
246
+ do {
247
+ if (parser.peek()?.type === TokenType.Spread) {
248
+ const spreadToken = parser.consume();
249
+ const argument = parseExpression(parser);
250
+ args.push(ASTNode.SpreadElement(argument, spreadToken));
251
+ } else {
252
+ args.push(parseExpression(parser));
253
+ }
254
+ } while (parser.match(TokenType.Comma));
255
+ }
256
+
257
+ parser.expect(TokenType.RParen, undefined, 'SYN067', 'Expected a closing parenthesis for function arguments.');
258
+
259
+ return ASTNode.CallExpression(callee, args, callToken); // Returns a CallExpression AST node
260
+ }
261
+
262
+ export function parseCallStatement(parser) {
263
+ const callToken = parser.expectKeyword("call", 'SYN063', 'Expected "call" keyword to initiate a function call.');
264
+
265
+ // Use the new reusable function to parse the `callee(args)` part
266
+ const callExpression = parseCallExpressionParts(parser, callToken); // Pass the callToken for location
267
+
268
+ let destination = null;
269
+ if (parser.match(TokenType.Operator, "->")) {
270
+ const destinationToken = parser.expect(TokenType.Identifier, undefined, 'SYN069', 'Expected an identifier for the assignment destination after "->".');
271
+ destination = ASTNode.Identifier(destinationToken.value, destinationToken); // Store as ASTNode.Identifier
272
+ }
273
+
274
+ // Now, create the CallStatement AST node.
275
+ // It will embed the CallExpression.
276
+ return ASTNode.CallStatement(
277
+ callExpression.callee, // The function being called
278
+ callExpression.arguments, // The arguments to the call
279
+ destination, // The optional destination for the result
280
+ callToken // The original 'call' token for location
281
+ );
282
+ }
283
+
284
+ export function parseReturnStatement(parser) {
285
+ const returnToken = parser.expectKeyword("return", 'SYN070', 'Expected "return" keyword.');
286
+ let argument = null;
287
+
288
+ // If the next token is the end of a block, there is no return value.
289
+ // This handles `return end` or `return else` etc.
290
+ const nextToken = parser.peek();
291
+ if (
292
+ parser.isAtEnd() ||
293
+ (nextToken.type === TokenType.Keyword && ['end', 'else', 'catch', 'case', 'default'].includes(nextToken.value))
294
+ ) {
295
+ // This is a `return` statement with no value, so argument remains null.
296
+ } else {
297
+ // Otherwise, there MUST be an expression to return.
298
+ argument = parseExpression(parser);
299
+ }
300
+
301
+ return ASTNode.ReturnStatement(argument, returnToken);
302
+ }
303
+
304
+ export function parseShowStatement(parser) {
305
+ const showToken = parser.expectKeyword("show", 'SYN071', 'Expected "show" keyword.');
306
+ const expression = parseExpression(parser);
307
+ return ASTNode.ShowStatement(expression, showToken);
308
+ }
309
+
310
+ export function parseThrowStatement(parser) {
311
+ const throwToken = parser.expectKeyword("throw", 'SYN072', 'Expected "throw" keyword.');
312
+ const argument = parseExpression(parser);
313
+ return ASTNode.ThrowStatement(argument, throwToken);
314
+ }
@@ -0,0 +1,57 @@
1
+ import { TokenType } from "../../lexer/TokenTypes.js";
2
+ import { ASTNode } from "../ASTNodes.js";
3
+ import { parseVariableOrAssignment } from "./variableParsers.js";
4
+ import { parseFunctionDeclaration } from "./functionParsers.js";
5
+
6
+
7
+ export function parseImportStatement(parser) {
8
+ const importToken = parser.expectKeyword("import", 'SYN073', 'Expected "import" keyword.');
9
+ // now we expect: import <Identifier> from <String> [as <Identifier>]
10
+ const nameToken = parser.expect(TokenType.Identifier, undefined, 'SYN073a', 'Expected an identifier for the module to import.');
11
+
12
+ parser.expectKeyword("from", 'SYN074a', 'Expected "from" keyword after imported name.');
13
+
14
+ const pathToken = parser.expect(TokenType.String, undefined, 'SYN074', 'Expected a string literal for the module path (e.g., "my_module").');
15
+
16
+ let aliasToken = null;
17
+ if (parser.matchKeyword("as")) {
18
+ aliasToken = parser.expect(TokenType.Identifier, undefined, 'SYN076', 'Expected an identifier for the module alias.');
19
+ }
20
+
21
+ return ASTNode.ImportStatement(
22
+ pathToken.value,
23
+ aliasToken ? aliasToken.value : nameToken.value,
24
+ importToken
25
+ );
26
+ }
27
+
28
+ export function parseExportStatement(parser, decorators = []) {
29
+ const exportToken = parser.expectKeyword("export", 'SYN077', 'Expected "export" keyword.');
30
+
31
+ const nextToken = parser.peek();
32
+ if (!nextToken || parser.isAtEnd()) { // Check isAtEnd as well
33
+ parser.error("Unexpected end of input after 'export'.", exportToken, 'SYN078', "Expected a variable or function declaration to export.");
34
+ }
35
+ if (nextToken.type === TokenType.Keyword) {
36
+ switch (nextToken.value) {
37
+ case "set":
38
+ case "let":
39
+ case "const":
40
+ case "global":
41
+ // Pass exportToken as the location token for the AST node
42
+ return parseVariableOrAssignment(parser, true, exportToken);
43
+ case "function":
44
+ // Pass exportToken as the location token for the AST node
45
+ return parseFunctionDeclaration(parser, true, exportToken, decorators);
46
+ default:
47
+ parser.error(
48
+ `Cannot export statement of type '${nextToken.value}'. Expected a declaration.`,
49
+ nextToken,
50
+ 'SYN079',
51
+ "Only 'set', 'let', 'const', 'global', or 'function' declarations can be exported."
52
+ );
53
+ }
54
+ }
55
+ // If not a keyword, it must be an error because export must be followed by a declaration keyword.
56
+ parser.error("Expected a declaration keyword (set, let, const, global, function) after 'export'.", nextToken, 'SYN080', 'Only variable and function declarations can be exported.');
57
+ }
@@ -0,0 +1,124 @@
1
+ import { TokenType } from "../../lexer/TokenTypes.js";
2
+ import { ASTNode } from "../ASTNodes.js";
3
+ import { parseExpression } from "../parserExpressions.js";
4
+ import { parseBlock, isBlockEnd } from "../parserUtils.js";
5
+
6
+ export function parseMatchStatement(parser) {
7
+ const matchToken = parser.expectKeyword("match", 'SYN081', 'Expected "match" keyword to start a match statement.');
8
+ const discriminant = parseExpression(parser);
9
+
10
+ const cases = [];
11
+
12
+ // Parse case clauses
13
+ while (
14
+ parser.peek()?.type === TokenType.Keyword &&
15
+ (parser.peek()?.value === "case" || parser.peek()?.value === "default")
16
+ ) {
17
+ if (parser.peek()?.value === "case") {
18
+ const caseToken = parser.expectKeyword("case", 'SYN082', 'Expected "case" keyword for a match clause.');
19
+ const pattern = parsePattern(parser);
20
+
21
+ let guard = null;
22
+ if (parser.matchKeyword("when")) {
23
+ guard = parseExpression(parser);
24
+ }
25
+
26
+ parser.expect(TokenType.Colon, undefined, 'SYN083', 'Expected a colon (:) after the pattern in a match clause.');
27
+ const consequent = parseBlock(parser, ["case", "default", "end"]);
28
+
29
+ cases.push(ASTNode.CaseClause(pattern, guard, consequent, caseToken));
30
+ } else if (parser.peek()?.value === "default") {
31
+ const defaultToken = parser.expectKeyword("default", 'SYN084', 'Expected "default" keyword for the fallback match clause.');
32
+ parser.expect(TokenType.Colon, undefined, 'SYN085', 'Expected a colon (:) after "default" in a match clause.');
33
+ const consequent = parseBlock(parser, ["case", "default", "end"]);
34
+
35
+ cases.push(ASTNode.CaseClause(null, null, consequent, defaultToken)); // null pattern and guard for default
36
+ }
37
+ }
38
+ if (cases.length === 0) {
39
+ parser.error("Match statement must have at least one 'case' or 'default' clause.", matchToken, 'SYN085A', "Add at least one 'case ... : ...' or 'default: ...' block.");
40
+ }
41
+
42
+ parser.expectKeyword("end", 'SYN086', 'Expected "end" keyword to close match statement.');
43
+ return ASTNode.MatchStatement(discriminant, cases, matchToken);
44
+ }
45
+
46
+ export function parsePattern(parser) {
47
+ const token = parser.peek();
48
+
49
+ if (token?.type === TokenType.LBracket) {
50
+ // Array pattern: [a, b] or [1, 2, 3]
51
+ const bracketToken = parser.expect(TokenType.LBracket, undefined, 'SYN087', 'Expected an opening square bracket to start an array pattern.');
52
+ const elements = [];
53
+
54
+ if (parser.peek()?.type !== TokenType.RBracket) {
55
+ do {
56
+ elements.push(parsePatternElement(parser));
57
+ } while (parser.match(TokenType.Comma));
58
+ }
59
+
60
+ parser.expect(TokenType.RBracket, undefined, 'SYN088', 'Expected a closing square bracket to end an array pattern.');
61
+ return ASTNode.ArrayPattern(elements, bracketToken);
62
+ }
63
+ // Simple pattern: literal, identifier
64
+ return parsePatternElement(parser);
65
+ }
66
+
67
+ export function parsePatternElement(parser) {
68
+ const token = parser.peek();
69
+ if (!token) {
70
+ parser.error("Unexpected end of input while parsing pattern element.", parser.peek(-1) || parser.tokens[0], 'SYN088A', "A pattern element (literal, identifier, or array) is expected here.");
71
+ }
72
+
73
+ switch (token.type) {
74
+ case TokenType.LBracket: // Nested array pattern
75
+ {
76
+ const bracketToken = parser.expect(TokenType.LBracket, undefined, 'SYN089', 'Expected an opening square bracket for nested array pattern.');
77
+ const elements = [];
78
+ if (parser.peek()?.type !== TokenType.RBracket) {
79
+ do {
80
+ elements.push(parsePatternElement(parser));
81
+ } while (parser.match(TokenType.Comma));
82
+ }
83
+ parser.expect(TokenType.RBracket, undefined, 'SYN090', 'Expected a closing square bracket for nested array pattern.');
84
+ return ASTNode.ArrayPattern(elements, bracketToken);
85
+ }
86
+ case TokenType.Number:
87
+ {
88
+ const numToken = parser.consume();
89
+ // Ensure the number is an integer for pattern matching if required by language spec
90
+ // For now, allowing any number literal.
91
+ return ASTNode.Literal(Number.parseFloat(numToken.value), numToken); // Or parseInt if only integers allowed
92
+ }
93
+ case TokenType.String:
94
+ {
95
+ const strToken = parser.consume();
96
+ return ASTNode.Literal(strToken.value, strToken);
97
+ }
98
+ case TokenType.Boolean: // Lexer produces this for true/false literals
99
+ {
100
+ const boolToken = parser.consume();
101
+ return ASTNode.Literal(boolToken.value === "true" || boolToken.value === true, boolToken);
102
+ }
103
+ case TokenType.Null: // Lexer produces this for null literal
104
+ {
105
+ const nullToken = parser.consume();
106
+ return ASTNode.Literal(null, nullToken);
107
+ }
108
+ case TokenType.Identifier:
109
+ {
110
+ // Could be a variable to bind or a named constant to match (e.g. if you have enums or consts)
111
+ // For now, assume it's a variable to bind.
112
+ const idToken = parser.consume();
113
+ return ASTNode.Identifier(idToken.value, idToken);
114
+ }
115
+ default:
116
+ parser.error(
117
+ `Unexpected token '${token.value}' (${token.type}) in pattern. Expected a literal, identifier, or array pattern.`,
118
+ token,
119
+ 'SYN091',
120
+ 'Patterns can be simple values (like 10, "hello", true), identifiers (to bind values), or array patterns (like [a, b]).'
121
+ );
122
+ }
123
+ }
124
+