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,165 @@
1
+ // parser/expressions/atomicExpressions.js
2
+ import { TokenType } from "../../lexer/TokenTypes.js";
3
+ import { ASTNode } from "../ASTNodes.js";
4
+ import { parseBlock } from "../parserUtils.js";
5
+ import { parseAnonymousFunction, parseCallExpressionParts } from "../statements/functionParsers.js"; // Import the new function
6
+
7
+ // Forward declaration - will be set by the main parser
8
+ let parseExpression;
9
+
10
+ export function setParseExpression(parseExpressionFn) {
11
+ parseExpression = parseExpressionFn;
12
+ }
13
+
14
+ export function parseAtomicExpression(parser) {
15
+ const token = parser.peek();
16
+
17
+ switch (token?.type) {
18
+ case TokenType.Identifier: {
19
+ const consumedToken = parser.consume();
20
+ return ASTNode.Identifier(consumedToken.value, consumedToken);
21
+ }
22
+ case TokenType.Number: {
23
+ const consumedToken = parser.consume();
24
+ return ASTNode.Literal(Number.parseFloat(consumedToken.value), consumedToken);
25
+ }
26
+ case TokenType.String: {
27
+ const consumedToken = parser.consume();
28
+ return ASTNode.Literal(consumedToken.value, consumedToken);
29
+ }
30
+ case TokenType.Boolean: {
31
+ // The lexer already converts 'true'/'false' strings to actual booleans.
32
+ const consumedToken = parser.consume();
33
+ return ASTNode.Literal(consumedToken.value, consumedToken);
34
+ }
35
+ case TokenType.Null: {
36
+ const consumedToken = parser.consume();
37
+ return ASTNode.Literal(null, consumedToken);
38
+ }
39
+ case TokenType.Backtick:
40
+ return parseTemplateLiteral(parser);
41
+ case TokenType.LBracket:
42
+ return parseArrayLiteral(parser);
43
+ case TokenType.LBrace:
44
+ return parseObjectLiteral(parser);
45
+ case TokenType.LParen: {
46
+ parser.consume(); // consume opening parenthesis
47
+ const expr = parseExpression(parser);
48
+ parser.expect(TokenType.RParen, undefined, 'SYN018', 'Expected a closing parenthesis for grouped expression.');
49
+ return expr;
50
+ }
51
+ case TokenType.Keyword:
52
+ if (token.value === "function" || token.value === "fn") {
53
+ parser.consume(); // CONSUME 'function' or 'fn'
54
+ return parseAnonymousFunction(parser, token.value === "fn");
55
+ }
56
+ if (token.value === "if") {
57
+ const ifToken = parser.consume(); // CONSUME 'if'
58
+ const condition = parseExpression(parser);
59
+ parser.expectKeyword("then", "SYN120", "Expected 'then' after condition in inline if expression.");
60
+ const consequent = parseExpression(parser);
61
+ parser.expectKeyword("else", "SYN121", "Expected 'else' in inline if expression.");
62
+ const alternate = parseExpression(parser);
63
+ return ASTNode.InlineIfExpression(condition, consequent, alternate, ifToken);
64
+ }
65
+ if (token.value === "call") {
66
+ parser.consume(); // CONSUME 'call' keyword
67
+ return parseCallExpressionParts(parser, token);
68
+ }
69
+ parser.error(`Unexpected keyword "${token.value}" in expression context.`, token, 'SYN010');
70
+ break;
71
+ default:
72
+ parser.error(`Unexpected token in expression "${token?.value}".`, token, 'SYN010', 'Expected a literal, identifier, array, object, or function.');
73
+ }
74
+ }
75
+
76
+ export function parseArrayLiteral(parser) {
77
+ const startBracket = parser.expect(TokenType.LBracket, undefined, 'SYN011', 'Expected an opening square bracket to start an array literal.');
78
+ const elements = [];
79
+
80
+ if (parser.peek()?.type !== TokenType.RBracket) {
81
+ if (parser.peek()?.type === TokenType.Spread) {
82
+ const spreadToken = parser.consume();
83
+ const argument = parseExpression(parser);
84
+ elements.push(ASTNode.SpreadElement(argument, spreadToken));
85
+ } else {
86
+ elements.push(parseExpression(parser));
87
+ }
88
+
89
+ while (parser.peek()?.type === TokenType.Comma) {
90
+ parser.consume();
91
+ if (parser.peek()?.type === TokenType.RBracket) break;
92
+
93
+ if (parser.peek()?.type === TokenType.Spread) {
94
+ const spreadToken = parser.consume();
95
+ const argument = parseExpression(parser);
96
+ elements.push(ASTNode.SpreadElement(argument, spreadToken));
97
+ } else {
98
+ elements.push(parseExpression(parser));
99
+ }
100
+ }
101
+ }
102
+
103
+ parser.expect(TokenType.RBracket, undefined, 'SYN012', 'Expected a closing square bracket to end an array literal.');
104
+ return ASTNode.ArrayLiteral(elements, startBracket);
105
+ }
106
+
107
+ export function parseObjectLiteral(parser) {
108
+ const startBrace = parser.expect(TokenType.LBrace, undefined, 'SYN013', 'Expected an opening curly brace to start an object literal.');
109
+ const properties = [];
110
+
111
+ if (parser.peek()?.type !== TokenType.RBrace) {
112
+ const parsePropertyOrSpread = () => {
113
+ if (parser.peek()?.type === TokenType.Spread) {
114
+ const spreadToken = parser.consume();
115
+ const argument = parseExpression(parser);
116
+ properties.push(ASTNode.SpreadElement(argument, spreadToken));
117
+ } else {
118
+ const keyToken = parser.expect(TokenType.Identifier, undefined, 'SYN016', 'Expected a property name (identifier) in object literal.');
119
+ parser.expect(TokenType.Colon, undefined, 'SYN017', 'Expected a colon ":" after property name in object literal.');
120
+ const value = parseExpression(parser);
121
+ properties.push({ key: keyToken.value, value });
122
+ }
123
+ };
124
+
125
+ parsePropertyOrSpread();
126
+
127
+ while (parser.peek()?.type === TokenType.Comma) {
128
+ parser.consume();
129
+ if (parser.peek()?.type === TokenType.RBrace) break;
130
+ parsePropertyOrSpread();
131
+ }
132
+ }
133
+
134
+ parser.expect(TokenType.RBrace, undefined, 'SYN018', 'Expected a closing curly brace to end an object literal.');
135
+ return ASTNode.ObjectLiteral(properties, startBrace);
136
+ }
137
+
138
+
139
+ export function parseTemplateLiteral(parser) {
140
+ const startToken = parser.expect(TokenType.Backtick, '`', 'SYN104', 'Expected backtick ` to start a template literal.');
141
+ const parts = [];
142
+
143
+ while (!parser.isAtEnd() && parser.peek()?.type !== TokenType.Backtick) {
144
+ const token = parser.peek();
145
+
146
+ if (token.type === TokenType.StringFragment) {
147
+ // It's a plain string part, like "Hello, "
148
+ parser.consume();
149
+ if (token.value.length > 0) {
150
+ parts.push(ASTNode.Literal(token.value, token));
151
+ }
152
+ } else if (token.type === TokenType.InterpolationStart) {
153
+ // It's an expression part, like ${name}
154
+ parser.consume(); // consume '${'
155
+ const expr = parseExpression(parser);
156
+ parts.push(expr);
157
+ parser.expect(TokenType.InterpolationEnd, '}', 'SYN105', 'Expected closing brace } after expression in template literal.');
158
+ } else {
159
+ parser.error(`Unexpected token '${token.value}' inside a template literal.`, token, 'SYN106');
160
+ }
161
+ }
162
+
163
+ parser.expect(TokenType.Backtick, '`', 'SYN107', 'Expected backtick ` to end a template literal.');
164
+ return ASTNode.TemplateLiteral(parts, startToken);
165
+ }
File without changes
@@ -0,0 +1,79 @@
1
+ // In: parser/expressions/operatorExpressions.js
2
+
3
+ import { TokenType, END_KEYWORDS } from "../../lexer/TokenTypes.js";
4
+ import { ASTNode } from "../ASTNodes.js";
5
+
6
+ let parseExpression;
7
+ let parsePrimaryExpression;
8
+
9
+ // This new Set contains all keywords that can ONLY start a new statement.
10
+ // If the parser sees one of these after an operand, it knows the expression is over.
11
+ const STATEMENT_START_KEYWORDS = new Set([
12
+ 'set', 'let', 'const', 'global', 'if', 'while', 'for', 'loop',
13
+ 'function', 'call', 'show', 'return', 'try', 'throw', 'match',
14
+ 'import', 'export', 'break', 'continue'
15
+ ]);
16
+
17
+ export function setParseExpression(parseExpressionFn) {
18
+ parseExpression = parseExpressionFn;
19
+ }
20
+
21
+ export function setParsePrimaryExpression(parsePrimaryExpressionFn) {
22
+ parsePrimaryExpression = parsePrimaryExpressionFn;
23
+ }
24
+
25
+ export function parseBinaryOrUnary(parser) {
26
+ const token = parser.peek();
27
+
28
+ const isOperator = (token?.type === TokenType.Operator && !['.', '?.', '|>'].includes(token.value)) ||
29
+ (token?.type === TokenType.Keyword && ['not', 'and', 'or'].includes(token.value));
30
+
31
+ if (!isOperator) {
32
+ return parsePrimaryExpression(parser);
33
+ }
34
+
35
+ const operatorToken = parser.consume();
36
+ const operator = operatorToken.value;
37
+
38
+ // The 'not' operator is always unary
39
+ if (operator === 'not') {
40
+ const argument = parseExpression(parser);
41
+ return ASTNode.UnaryExpression(operator, argument, operatorToken);
42
+ }
43
+
44
+ const left = parseExpression(parser);
45
+
46
+ // ==========================================================
47
+ // THE DEFINITIVE FIX IS IN THE LOGIC BELOW
48
+ // ==========================================================
49
+ const nextToken = parser.peek();
50
+
51
+ // An expression is considered "ended" if we are at the end of the file,
52
+ // or the next token is a structural terminator, or it's a keyword that
53
+ // *must* start a new statement.
54
+ const isEndOfExpression = parser.isAtEnd() ||
55
+ !nextToken ||
56
+ nextToken.type === TokenType.RParen ||
57
+ nextToken.type === TokenType.RBracket ||
58
+ nextToken.type === TokenType.RBrace ||
59
+ nextToken.type === TokenType.Comma ||
60
+ nextToken.type === TokenType.Colon ||
61
+ (nextToken.type === TokenType.Operator && nextToken.value === "->") ||
62
+ (nextToken.type === TokenType.Keyword && (
63
+ END_KEYWORDS.includes(nextToken.value) || // 'end', 'else', 'catch' etc.
64
+ STATEMENT_START_KEYWORDS.has(nextToken.value) // 'show', 'set', 'if', etc.
65
+ ));
66
+
67
+ if (isEndOfExpression) {
68
+ // There is no second operand, so it must be a unary expression.
69
+ // Mimo currently only supports '-' as a unary numeric operator.
70
+ if (operator !== '-') {
71
+ parser.error(`Operator '${operator}' cannot be used as a unary operator.`, operatorToken, 'SYN039', `Did you mean to provide a second argument?`);
72
+ }
73
+ return ASTNode.UnaryExpression(operator, left, operatorToken);
74
+ } else {
75
+ // There's more to the expression, so we parse the second operand.
76
+ const right = parseExpression(parser);
77
+ return ASTNode.BinaryExpression(operator, left, right, operatorToken);
78
+ }
79
+ }
@@ -0,0 +1,77 @@
1
+ // parser/expressions/primaryExpressions.js
2
+ import { TokenType } from "../../lexer/TokenTypes.js";
3
+ import { ASTNode } from "../ASTNodes.js";
4
+ import { parseAtomicExpression } from "./atomicExpressions.js";
5
+
6
+ // Forward declaration - will be set by the main parser
7
+ let parseExpression;
8
+
9
+ export function setParseExpression(parseExpressionFn) {
10
+ parseExpression = parseExpressionFn;
11
+ }
12
+
13
+ export function parsePrimaryExpression(parser) {
14
+ let primaryExpr = parseAtomicExpression(parser);
15
+
16
+ // Handle postfix operations: property access, array access, safe navigation, function calls
17
+ while (true) {
18
+ const nextToken = parser.peek();
19
+ if (!nextToken) break;
20
+
21
+ if (nextToken.type === TokenType.Operator && (nextToken.value === "." || nextToken.value === "?.")) {
22
+ const operatorToken = parser.consume(); // Consume '.' or '?.'
23
+
24
+ // Support Safe Index Access: obj?.[index]
25
+ if (operatorToken.value === "?." && parser.match(TokenType.LBracket)) {
26
+ const indexExpr = parseExpression(parser);
27
+ const endBracket = parser.expect(TokenType.RBracket, undefined, 'SYN041', 'Expected closing square bracket for safe array/object access.');
28
+ primaryExpr = ASTNode.SafeArrayAccess(primaryExpr, indexExpr, operatorToken);
29
+ continue;
30
+ }
31
+
32
+ // Support Safe Call: func?.()
33
+ if (operatorToken.value === "?." && parser.match(TokenType.LParen)) {
34
+ const args = [];
35
+ if (parser.peek()?.type !== TokenType.RParen) {
36
+ do {
37
+ if (parser.peek()?.type === TokenType.Spread) {
38
+ const spreadToken = parser.consume();
39
+ const argument = parseExpression(parser);
40
+ args.push(ASTNode.SpreadElement(argument, spreadToken));
41
+ } else {
42
+ args.push(parseExpression(parser));
43
+ }
44
+ } while (parser.match(TokenType.Comma));
45
+ }
46
+ parser.expect(TokenType.RParen, undefined, 'SYN067', 'Expected a closing parenthesis for safe function call.');
47
+ primaryExpr = ASTNode.SafeCallExpression(primaryExpr, args, operatorToken);
48
+ continue;
49
+ }
50
+
51
+ const property = parser.expect(TokenType.Identifier, undefined, 'SYN042', 'Expected property name after dot operator.');
52
+
53
+ if (operatorToken.value === '?.') {
54
+ primaryExpr = ASTNode.SafePropertyAccess(primaryExpr, property.value, property);
55
+ } else {
56
+ primaryExpr = ASTNode.PropertyAccess(primaryExpr, property.value, property);
57
+ }
58
+ } else if (nextToken.type === TokenType.LBracket) {
59
+ // Array/Object access: arr[index] or obj[key]
60
+ const startBracketToken = parser.consume(); // consume [
61
+ const indexExpr = parseExpression(parser);
62
+ parser.expect(TokenType.RBracket, undefined, 'SYN041', 'Expected closing square bracket for array/object access (e.g., arr[index]).');
63
+ primaryExpr = ASTNode.ArrayAccess(primaryExpr, indexExpr, startBracketToken);
64
+ // The slicing logic with colon is removed here.
65
+ } else {
66
+ break; // No more postfix operators
67
+ }
68
+ }
69
+ return primaryExpr;
70
+ }
71
+
72
+ export function parseRangeLiteral(parser) {
73
+ const start = parseAtomicExpression(parser);
74
+ const rangeToken = parser.expect(TokenType.Range, undefined, 'SYN045', 'Expected range operator (..) between start and end of range.');
75
+ const end = parseAtomicExpression(parser);
76
+ return ASTNode.RangeLiteral(start, end, rangeToken);
77
+ }
@@ -0,0 +1,184 @@
1
+ // parser/parseStatement.js
2
+ import { TokenType } from "../lexer/TokenTypes.js";
3
+ import { parseExpression } from "./parserExpressions.js"; // Needed if expressions can be statements
4
+
5
+ // Import ALL individual statement parser functions here (ensure names match your files)
6
+ import {
7
+ parseBreakStatement,
8
+ parseContinueStatement,
9
+ parseForStatement,
10
+ parseIfStatement,
11
+ parseGuardStatement,
12
+ parseLoopStatement,
13
+ parseTryStatement,
14
+ parseWhileStatement,
15
+ } from "./statements/controlFlowParsers.js";
16
+ import {
17
+ parseCallStatement,
18
+ parseFunctionDeclaration,
19
+ parseDecorators,
20
+ parseReturnStatement,
21
+ parseShowStatement,
22
+ parseThrowStatement,
23
+ } from "./statements/functionParsers.js";
24
+ import { parseMatchStatement } from "./statements/patternMatchParsers.js";
25
+ import {
26
+ parseVariableOrAssignment,
27
+ parseDestructuringStatement,
28
+ } from "./statements/variableParsers.js"; // Corrected import
29
+ import {
30
+ parseImportStatement,
31
+ parseExportStatement,
32
+ } from "./statements/moduleParsers.js";
33
+
34
+ // NOTE: parseAnonymousFunction is used by parseAtomicExpression (an expression parser), not directly here.
35
+ // No imports needed for parseClassDeclaration, parseEnumDeclaration, parseInterfaceDeclaration,
36
+ // parseTypeAliasDeclaration, parseModuleBlock as they are not implemented yet.
37
+ // No parseExpressionStatement import is needed, we will define it if Mimo supports it.
38
+
39
+ /**
40
+ * Parses an expression used as a standalone statement.
41
+ * This is for cases where an expression (like `1+2` or a function call without `call` keyword)
42
+ * is a valid statement in itself.
43
+ * @param {import("./Parser.js").Parser} parser - The parser instance.
44
+ * @returns {object} The AST node for the expression statement.
45
+ */
46
+ function parseExpressionStatement(parser) {
47
+ const expression = parseExpression(parser);
48
+ // Mimo doesn't have an explicit ExpressionStatement AST node in ASTNodes.js
49
+ // so for now, we just return the expression itself.
50
+ // The interpreter will then evaluate this expression as a statement.
51
+ return expression;
52
+ }
53
+
54
+ /**
55
+ * Parses a single Mimo statement. This function acts as a dispatcher.
56
+ * It relies on the passed `parser` instance to access its methods (peek, consume, error, etc.)
57
+ * and other parsing helpers.
58
+ * @param {import("./Parser.js").Parser} parser - The parser instance.
59
+ * @returns {object} The AST node for the parsed statement.
60
+ */
61
+ export function parseStatement(parser) {
62
+ const token = parser.peek(); // Get the current token
63
+ if (!token || parser.isAtEnd()) {
64
+ // Check isAtEnd for robustness
65
+ parser.error(
66
+ "Unexpected end of input.",
67
+ null, // No specific token to point to at EOF
68
+ "SYN000",
69
+ "Expected a statement or expression, but found end of file."
70
+ );
71
+ }
72
+
73
+ if (token.type === TokenType.Keyword) {
74
+ switch (token.value) {
75
+ case "destructure":
76
+ return parseDestructuringStatement(parser);
77
+ case "set":
78
+ case "let":
79
+ case "const":
80
+ case "global":
81
+ // Variable declarations and assignments
82
+ return parseVariableOrAssignment(parser, false, token); // Correctly calls existing function
83
+ case "if":
84
+ return parseIfStatement(parser);
85
+ case "guard":
86
+ return parseGuardStatement(parser);
87
+ case "for":
88
+ return parseForStatement(parser);
89
+ case "while":
90
+ return parseWhileStatement(parser);
91
+ case "loop": // Assuming 'loop' is a keyword for infinite loop or similar
92
+ return parseLoopStatement(parser);
93
+ case "break":
94
+ return parseBreakStatement(parser);
95
+ case "continue":
96
+ return parseContinueStatement(parser);
97
+ case "try":
98
+ return parseTryStatement(parser); // Corrected function name
99
+ case "function":
100
+ return parseFunctionDeclaration(parser, false);
101
+ case "call":
102
+ return parseCallStatement(parser);
103
+ case "return":
104
+ return parseReturnStatement(parser);
105
+ case "show":
106
+ return parseShowStatement(parser);
107
+ case "throw":
108
+ return parseThrowStatement(parser);
109
+ case "match":
110
+ return parseMatchStatement(parser);
111
+ case "import":
112
+ return parseImportStatement(parser); // Direct import parsing
113
+ case "export":
114
+ return parseExportStatement(parser); // Direct export parsing
115
+ default:
116
+ // If a keyword is encountered that doesn't start a known statement,
117
+ // AND it's not a keyword that can start an expression (like 'true', 'false', 'null'),
118
+ // then it's an error.
119
+ // For Mimo, keywords like 'true', 'false', 'null' are literals, not statements.
120
+ // So, if a keyword falls through here, it IS an error.
121
+ parser.error(
122
+ `Unexpected keyword '${token.value}' at the start of a statement.`,
123
+ token,
124
+ "SYN005",
125
+ "Expected a statement keyword (like 'set', 'if', 'function', 'call', 'show', etc.)."
126
+ );
127
+ // This default case should always throw if it's a keyword.
128
+ }
129
+ }
130
+
131
+ // Handle Decorators: @decorator
132
+ if (token.type === TokenType.At) {
133
+ const decorators = parseDecorators(parser);
134
+ const nextToken = parser.peek();
135
+ if (nextToken?.type === TokenType.Keyword) {
136
+ if (nextToken.value === "function") {
137
+ return parseFunctionDeclaration(parser, false, null, decorators);
138
+ } else if (nextToken.value === "export") {
139
+ return parseExportStatement(parser, decorators);
140
+ }
141
+ }
142
+ parser.error("Decorators can only be applied to 'function' or 'export function' declarations.", token, 'SYN143');
143
+ }
144
+
145
+ // Check for LabeledStatement
146
+ if (token.type === TokenType.Identifier) {
147
+ const nextToken = parser.peek(1);
148
+ if (nextToken && nextToken.type === TokenType.Colon) {
149
+ const labelToken = parser.consume(); // Consume identifier
150
+ parser.consume(); // Consume colon
151
+ const statement = parseStatement(parser);
152
+ // Wait, ASTNode is not imported! I'll just use inline object or import it. Let me import ASTNode at the top.
153
+ return {
154
+ type: "LabeledStatement",
155
+ label: labelToken.value,
156
+ statement: statement,
157
+ line: labelToken.line,
158
+ column: labelToken.column,
159
+ start: labelToken.start,
160
+ length: labelToken.length,
161
+ file: labelToken.file,
162
+ };
163
+ }
164
+ }
165
+
166
+ // If the first token is NOT a keyword, it's an expression.
167
+ // This handles cases where expressions (like `* x 2` or `myVar`) are standalone statements.
168
+ // Call parseExpressionStatement to handle it.
169
+ return parseExpressionStatement(parser); // <--- ADD THIS LINE HERE
170
+
171
+ // The original `parser.error` here (SYN001) should now only be reachable if parseExpressionStatement
172
+ // itself fails to parse anything and doesn't throw, or if an unexpected token appears and parser.error is called.
173
+ // But `parseExpressionStatement` should throw its own specific errors if it can't parse an expression.
174
+ // So, this final `parser.error` is effectively redundant if `parseExpressionStatement` is robust.
175
+ // We can remove it and let `parseExpressionStatement`'s errors propagate.
176
+ /*
177
+ parser.error(
178
+ `Unexpected token '${token.value}' (${token.type}) when expecting a statement.`,
179
+ token,
180
+ "SYN001",
181
+ "Expected a statement keyword (like 'set', 'if', 'function', 'call', 'show', etc.) or a valid expression that can form a statement."
182
+ );
183
+ */
184
+ }
@@ -0,0 +1,115 @@
1
+ import { TokenType } from "./../lexer/TokenTypes.js";
2
+ import { ASTNode } from "./ASTNodes.js";
3
+ import { parseBlock } from "./parserUtils.js";
4
+
5
+ // Import atomic expression parsing functions and their setter
6
+ import {
7
+ parseAtomicExpression,
8
+ parseArrayLiteral,
9
+ parseObjectLiteral,
10
+ setParseExpression as setAtomicParseExpression // Alias for atomicExpressions.js's setter
11
+ } from "./expressions/atomicExpressions.js";
12
+
13
+ // Import primary expression parsing function and its setter
14
+ import {
15
+ parsePrimaryExpression,
16
+ setParseExpression as setPrimaryExpressionParseExpression // Alias for primaryExpressions.js's setter
17
+ } from "./expressions/primaryExpressions.js";
18
+
19
+
20
+ // Import operator expression parsing functions and their setters
21
+ import {
22
+ parseBinaryOrUnary,
23
+ setParseExpression as setOperatorExpressionParseExpression, // Alias for operatorExpressions.js's main setter
24
+ setParsePrimaryExpression as setOperatorPrimaryExpressionParsePrimary // Alias for operatorExpressions.js's primary setter
25
+ } from "./expressions/operatorExpressions.js";
26
+
27
+
28
+ /**
29
+ * Parses a callee expression for |>: either an identifier, module.prop access,
30
+ * or an inline-if expression. Optionally consumes extra arguments in parentheses.
31
+ * Returns { callee, args }.
32
+ */
33
+ function parsePipeCallee(parser) {
34
+ // Support inline-if as callee: value |> if cond then fn1 else fn2
35
+ if (parser.peek()?.type === TokenType.Keyword && parser.peek()?.value === "if") {
36
+ const ifToken = parser.consume();
37
+ const condition = parseExpression(parser);
38
+ parser.expectKeyword("then", "SYN130", "Expected 'then' after condition in pipe inline-if.");
39
+ const consequent = parseExpression(parser);
40
+ parser.expectKeyword("else", "SYN131", "Expected 'else' in pipe inline-if.");
41
+ const alternate = parseExpression(parser);
42
+ const callee = ASTNode.InlineIfExpression(condition, consequent, alternate, ifToken);
43
+ // No extra args supported for inline-if callee (the fn itself receives the piped value)
44
+ return { callee, args: [] };
45
+ }
46
+
47
+ // Parse callee: identifier or module.prop
48
+ const firstToken = parser.expect(TokenType.Identifier, undefined, "SYN132", "Expected a function name after '|>'.");
49
+ let callee;
50
+ if (parser.peek()?.type === TokenType.Operator && parser.peek()?.value === ".") {
51
+ parser.consume(); // consume '.'
52
+ const prop = parser.expect(TokenType.Identifier, undefined, "SYN133", "Expected property name after '.' in pipe callee.");
53
+ callee = ASTNode.ModuleAccess(firstToken.value, prop.value, firstToken);
54
+ } else {
55
+ callee = ASTNode.Identifier(firstToken.value, firstToken);
56
+ }
57
+
58
+ // Parse optional extra arguments: value |> func(arg2, arg3)
59
+ const args = [];
60
+ if (parser.peek()?.type === TokenType.LParen) {
61
+ parser.consume(); // consume '('
62
+ if (parser.peek()?.type !== TokenType.RParen) {
63
+ do {
64
+ if (parser.peek()?.type === TokenType.Spread) {
65
+ const spreadToken = parser.consume();
66
+ const argument = parseExpression(parser);
67
+ args.push(ASTNode.SpreadElement(argument, spreadToken));
68
+ } else {
69
+ args.push(parseExpression(parser));
70
+ }
71
+ } while (parser.match(TokenType.Comma));
72
+ }
73
+ parser.expect(TokenType.RParen, undefined, "SYN134", "Expected ')' after pipe arguments.");
74
+ }
75
+
76
+ return { callee, args };
77
+ }
78
+
79
+ // Main entry point for parsing an expression
80
+ function parseExpression(parser) {
81
+ // Parse the left-hand side (standard Mimo prefix expression)
82
+ let left = parseBinaryOrUnary(parser);
83
+
84
+ // Then handle any pipe operators (left-associative)
85
+ while (parser.peek()?.type === TokenType.Operator && parser.peek()?.value === "|>") {
86
+ const pipeToken = parser.consume(); // consume '|>'
87
+ const { callee, args } = parsePipeCallee(parser);
88
+ left = ASTNode.PipeExpression(left, callee, args, pipeToken);
89
+ }
90
+
91
+ return left;
92
+ }
93
+
94
+ /**
95
+ * Sets up the forward references for mutually recursive expression parsing functions.
96
+ * This function MUST be called once, before any expression parsing begins.
97
+ */
98
+ export function setupExpressionParsers() {
99
+ // Pass the top-level parseExpression function to any sub-parsers that need to call it.
100
+ setAtomicParseExpression(parseExpression); // For atomicExpressions.js (e.g., for nested expressions in literals)
101
+ setPrimaryExpressionParseExpression(parseExpression); // For primaryExpressions.js (e.g., for `arr[index]`
102
+ setOperatorExpressionParseExpression(parseExpression); // For operatorExpressions.js (e.g., for `+ left right`)
103
+
104
+ // Pass parsePrimaryExpression to operatorExpressions.js (for `parseBinaryOrUnary` default case)
105
+ setOperatorPrimaryExpressionParsePrimary(parsePrimaryExpression);
106
+
107
+
108
+ }
109
+
110
+ // Export the main parsing functions that other parts of the parser might need to call.
111
+ export {
112
+ parseExpression,
113
+ parsePrimaryExpression,
114
+ parseBinaryOrUnary,
115
+ };
@@ -0,0 +1,19 @@
1
+ import { END_KEYWORDS, TokenType } from "../lexer/TokenTypes.js";
2
+ import { parseStatement } from "./parseStatement.js";
3
+
4
+ export function parseBlock(parser, customEndKeywords = []) {
5
+ const endKeywords = new Set([...END_KEYWORDS, ...customEndKeywords]);
6
+ const statements = [];
7
+
8
+ while (!parser.isAtEnd() && !isBlockEnd(parser, endKeywords)) {
9
+
10
+ statements.push(parseStatement(parser));
11
+ }
12
+
13
+ return statements;
14
+ }
15
+
16
+ export function isBlockEnd(parser, endKeywords) {
17
+ const token = parser.peek();
18
+ return token.type === TokenType.Keyword && endKeywords.has(token.value);
19
+ }