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,236 @@
1
+ import { Lexer } from "../lexer/Lexer.js";
2
+ import { ASTNode } from "../parser/ASTNodes.js";
3
+ import { Parser } from "../parser/Parser.js";
4
+ import { ErrorHandler } from "./ErrorHandler.js";
5
+ import { Environment } from "./environment.js";
6
+ import { MimoError } from "./MimoError.js";
7
+ import { arrayModule } from "./stdlib/array.js";
8
+ import { assertModule } from "./stdlib/assert.js";
9
+ import { datetimeModule } from "./stdlib/datetime.js";
10
+ import { envModule } from "./stdlib/env.js";
11
+ import { fsModule } from "./stdlib/fs.js";
12
+ import { httpModule } from "./stdlib/http.js";
13
+ import { jsonModule } from "./stdlib/json.js";
14
+ import { mathModuleExports } from "./stdlib/math.js";
15
+ import { objectModule } from "./stdlib/object.js";
16
+ import { pathModule } from "./stdlib/path.js";
17
+ import { regexModule } from "./stdlib/regex.js";
18
+ import { stringModuleExports } from "./stdlib/string.js";
19
+
20
+ const internalStdLibModules = {
21
+ array: arrayModule,
22
+ datetime: datetimeModule,
23
+ env: envModule,
24
+ fs: fsModule,
25
+ http: httpModule,
26
+ json: jsonModule,
27
+ math: mathModuleExports,
28
+ object: objectModule,
29
+ path: pathModule,
30
+ regex: regexModule,
31
+ string: stringModuleExports,
32
+ assert: assertModule,
33
+ };
34
+
35
+ export class ModuleLoader {
36
+ constructor(interpreter) {
37
+ // console.log("ModuleLoader constructor: interpreter received:", interpreter); // Keep logs for now
38
+ this.interpreter = interpreter;
39
+ // console.log("ModuleLoader constructor: this.interpreter after assignment:", this.interpreter); // Keep logs for now
40
+
41
+ this.adapter = interpreter.adapter; // Adapter should be accessible
42
+ this.moduleCache = new Map();
43
+ this.loadingStack = new Set();
44
+
45
+ // Ensure executeModule is always bound to this instance
46
+ this.executeModule = this.executeModule.bind(this); // <--- Add this line
47
+ }
48
+ loadModule(importPath, fromFile) {
49
+ // Check if importPath is a known internal JS-backed stdlib module
50
+ if (Object.hasOwn(internalStdLibModules, importPath)) {
51
+ // If already "loaded" (cached) in moduleCache, return that
52
+ if (this.moduleCache.has(importPath)) {
53
+ return this.moduleCache.get(importPath);
54
+ }
55
+ const exports = internalStdLibModules[importPath];
56
+ this.moduleCache.set(importPath, exports); // Cache it by its name
57
+ return exports;
58
+ }
59
+
60
+ const resolvedPath = this.resolvePath(importPath, fromFile);
61
+
62
+ // Return cached module if already loaded
63
+ if (this.moduleCache.has(resolvedPath)) {
64
+ return this.moduleCache.get(resolvedPath);
65
+ }
66
+
67
+ // Check for circular dependency
68
+ if (this.loadingStack.has(resolvedPath)) {
69
+ const loadingChain = Array.from(this.loadingStack).join(" -> ");
70
+ throw this.interpreter.errorHandler.createRuntimeError(
71
+ `Circular module dependency detected: ${loadingChain} -> ${resolvedPath}.`,
72
+ null, // No specific AST node for this type of error
73
+ "MOD003",
74
+ "Break the circular dependency by redesigning your module imports.",
75
+ );
76
+ }
77
+
78
+ // Load and execute module
79
+ this.loadingStack.add(resolvedPath);
80
+
81
+ try {
82
+ const exports = this.executeModule(resolvedPath);
83
+ this.moduleCache.set(resolvedPath, exports);
84
+ return exports;
85
+ } finally {
86
+ this.loadingStack.delete(resolvedPath);
87
+ }
88
+ }
89
+
90
+ resolvePath(importPath, fromFile) {
91
+ // Use adapter for path operations
92
+ const dir = this.adapter.dirname(fromFile);
93
+ const attemptedPaths = [];
94
+
95
+ let resolved = this.adapter.resolvePath(dir, importPath);
96
+ attemptedPaths.push(resolved);
97
+ if (this.adapter.existsSync(resolved)) {
98
+ return resolved;
99
+ }
100
+
101
+ resolved = this.adapter.resolvePath(dir, `${importPath}.mimo`);
102
+ attemptedPaths.push(resolved);
103
+ if (this.adapter.existsSync(resolved)) {
104
+ return resolved;
105
+ }
106
+ throw this.interpreter.errorHandler.createRuntimeError(
107
+ `Module '${importPath}' not found. Tried paths:\n${attemptedPaths
108
+ .map((p) => ` - ${p}`)
109
+ .join("\n")}`,
110
+ null, // No specific AST node for this path resolution error
111
+ "MOD001",
112
+ "Check the module path and ensure the file exists.",
113
+ );
114
+ }
115
+
116
+ executeModule(filePath) {
117
+ // console.log(
118
+ // "\x1b[36m\n--- Inside ModuleLoader.executeModule for:",
119
+ // filePath,
120
+ // "---\x1b[0m"
121
+ // );
122
+ // console.log(" 'this' in executeModule:", this);
123
+ // console.log(" 'this.interpreter' in executeModule:", this.interpreter);
124
+ if (!this.interpreter) {
125
+ // console.error("CRITICAL: this.interpreter is undefined in executeModule!");
126
+ throw new Error(
127
+ "ModuleLoader critical error: interpreter instance is missing.",
128
+ );
129
+ }
130
+ // console.log(" 'this.interpreter.currentEnv' in executeModule (before change):", this.interpreter.currentEnv);
131
+ // console.log(" 'this.interpreter.globalEnv' in executeModule:", this.interpreter.globalEnv);
132
+
133
+ const originalInterpreterEnv = this.interpreter.currentEnv;
134
+ const originalInterpreterFile = this.interpreter.currentFile;
135
+
136
+ try {
137
+ const source = this.adapter.readFileSync(filePath, "utf8");
138
+ const lexer = new Lexer(source, filePath);
139
+ const tokens = [];
140
+ let token;
141
+ while ((token = lexer.nextToken()) !== null) {
142
+ tokens.push(token);
143
+ }
144
+ const parser = new Parser(tokens, filePath);
145
+ // Ensure parser also has access to the errorHandler for rich syntax errors
146
+ parser.setErrorHandler(this.interpreter.errorHandler); // <--- Crucial: Pass errorHandler to parser
147
+ const ast = parser.parse();
148
+
149
+ const moduleEnv = new Environment(this.interpreter.globalEnv);
150
+ moduleEnv.isModuleRoot = true;
151
+
152
+ // Set up module execution context on the interpreter instance
153
+ this.interpreter.currentEnv = moduleEnv; // <--- The problematic line
154
+ this.interpreter.currentFile = filePath;
155
+ this.interpreter.errorHandler.addSourceFile(filePath, source);
156
+
157
+ // console.log(" 'this.interpreter.currentEnv' in executeModule (after change):", this.interpreter.currentEnv);
158
+
159
+ // Check if module has any explicit exports
160
+ let hasExplicitExports = false;
161
+ for (const statement of ast.body) {
162
+ if (statement.isExported === true) {
163
+ hasExplicitExports = true;
164
+ break;
165
+ }
166
+ }
167
+
168
+ // Execute module code
169
+ this.interpreter.visitNode(ast);
170
+
171
+ // Collect module exports
172
+ const exports = {};
173
+ if (hasExplicitExports) {
174
+ // Only export declarations with isExported = true
175
+ for (const statement of ast.body) {
176
+ if (statement.isExported === true) {
177
+ if (statement.type === "FunctionDeclaration") {
178
+ exports[statement.name] = moduleEnv.lookup(statement.name);
179
+ } else if (statement.type === "VariableDeclaration") {
180
+ exports[statement.identifier] = moduleEnv.lookup(
181
+ statement.identifier,
182
+ );
183
+ }
184
+ // Note: DestructuringAssignment, PropertyAssignment, and BracketAssignment
185
+ // don't currently support isExported flag, but could be added if needed
186
+ }
187
+ }
188
+ } else {
189
+ // Fallback: if no 'export' keywords were used, export everything (current behavior)
190
+ for (const [name, varInfo] of moduleEnv.vars.entries()) {
191
+ exports[name] = varInfo.value;
192
+ }
193
+ }
194
+
195
+ return exports;
196
+ } catch (error) {
197
+ // console.error("MODULE LOADER: Error during module execution for", filePath, ":", error);
198
+ if (error instanceof MimoError) {
199
+ throw error;
200
+ } // Re-throw MimoError as is
201
+ // Wrap any other JS errors. Need to pass an AST node if possible for location.
202
+ throw this.interpreter.errorHandler.createRuntimeError(
203
+ `Error loading module '${filePath}': ${error.message}`,
204
+ null, // No specific AST node for the module file, pass null.
205
+ "MOD004",
206
+ "An unexpected error occurred while loading this module. Check its syntax or dependencies.",
207
+ );
208
+ } finally {
209
+ // Restore previous execution context
210
+ if (this.interpreter) {
211
+ // Check if interpreter exists before restoring (safety)
212
+ this.interpreter.currentEnv = originalInterpreterEnv;
213
+ this.interpreter.currentFile = originalInterpreterFile;
214
+ this.interpreter.errorHandler.clearSourceFile(filePath);
215
+ }
216
+ // console.log(
217
+ // "\x1b[36m--- End ModuleLoader.executeModule for:",
218
+ // filePath,
219
+ // "---\x1b[0m"
220
+ // );
221
+ }
222
+ }
223
+
224
+ getModuleInfo(alias) {
225
+ // Helper method to get available properties for error messages
226
+ const moduleExports = this.interpreter.modules?.get(alias);
227
+ if (!moduleExports) {
228
+ return null;
229
+ }
230
+
231
+ return {
232
+ alias,
233
+ properties: Object.keys(moduleExports).sort(),
234
+ };
235
+ }
236
+ }
@@ -0,0 +1,107 @@
1
+ import { ErrorHandler } from "./ErrorHandler.js";
2
+ import { BaseExecutor } from "./executors/BaseExecutor.js";
3
+ import { VariableExecutor } from "./executors/VariableExecutor.js";
4
+ import { ControlFlowExecutor } from "./executors/ControlFlowExecutor.js";
5
+ import { FunctionExecutor } from "./executors/FunctionExecutor.js";
6
+ import { PatternMatchExecutor } from "./executors/PatternMatchExecutor.js";
7
+ import { MimoError } from "./MimoError.js";
8
+
9
+
10
+ export class StatementExecutor {
11
+ constructor(interpreter) {
12
+ this.interpreter = interpreter;
13
+
14
+ // Initialize specialized executors
15
+ this.baseExecutor = new BaseExecutor(interpreter);
16
+ this.variableExecutor = new VariableExecutor(interpreter);
17
+ this.controlFlowExecutor = new ControlFlowExecutor(interpreter);
18
+ this.functionExecutor = new FunctionExecutor(interpreter);
19
+ this.patternMatchExecutor = new PatternMatchExecutor(interpreter);
20
+ }
21
+
22
+ executeStatement(node) {
23
+ switch (node.type) {
24
+ case "Program":
25
+ return this.baseExecutor.executeProgram(node);
26
+
27
+ // Variable and assignment operations
28
+ case "VariableDeclaration":
29
+ return this.variableExecutor.executeVariableDeclaration(node);
30
+ case "DestructuringAssignment":
31
+ return this.variableExecutor.executeDestructuringAssignment(node);
32
+ case "PropertyAssignment":
33
+ return this.variableExecutor.executePropertyAssignment(node);
34
+ case "BracketAssignment":
35
+ return this.variableExecutor.executeBracketAssignment(node);
36
+
37
+ // Control flow operations
38
+ case "IfStatement":
39
+ return this.controlFlowExecutor.executeIfStatement(node);
40
+ case "GuardStatement":
41
+ return this.controlFlowExecutor.executeGuardStatement(node);
42
+ case "WhileStatement":
43
+ return this.controlFlowExecutor.executeWhileStatement(node);
44
+ case "ForStatement":
45
+ return this.controlFlowExecutor.executeForStatement(node);
46
+ case "LoopStatement":
47
+ return this.controlFlowExecutor.executeLoopStatement(node);
48
+ case "BreakStatement":
49
+ return this.controlFlowExecutor.executeBreakStatement(node);
50
+ case "ContinueStatement":
51
+ return this.controlFlowExecutor.executeContinueStatement(node);
52
+ case "TryStatement":
53
+ return this.controlFlowExecutor.executeTryStatement(node);
54
+ case "LabeledStatement":
55
+ return this.controlFlowExecutor.executeLabeledStatement(node);
56
+
57
+ // Function operations
58
+ case "FunctionDeclaration":
59
+ return this.functionExecutor.executeFunctionDeclaration(node);
60
+ case "CallStatement":
61
+ return this.functionExecutor.executeCallStatement(node);
62
+ case "ReturnStatement":
63
+ return this.functionExecutor.executeReturnStatement(node);
64
+ case "ShowStatement":
65
+ return this.functionExecutor.executeShowStatement(node);
66
+ case "ThrowStatement":
67
+ return this.functionExecutor.executeThrowStatement(node);
68
+
69
+ // Pattern matching operations
70
+ case "MatchStatement":
71
+ return this.patternMatchExecutor.executeMatchStatement(node);
72
+
73
+ // Module system operations
74
+ case "ImportStatement":
75
+ return this.executeImportStatement(node);
76
+
77
+ default:
78
+ throw this.interpreter.errorHandler.createRuntimeError(
79
+ `Unknown statement type: ${node.type}`,
80
+ node,
81
+ 'RUN000',
82
+ 'This indicates a bug in the interpreter. Please report this error.'
83
+ );
84
+ }
85
+ }
86
+
87
+
88
+ executeImportStatement(node) {
89
+ try {
90
+ const exports = this.interpreter.moduleLoader.loadModule(node.path, this.interpreter.currentFile);
91
+ this.interpreter.currentEnv.define(node.alias, exports, 'const');
92
+ } catch (error) {
93
+
94
+ if (error instanceof MimoError) {
95
+ throw error;
96
+ }
97
+
98
+ throw this.interpreter.errorHandler.createRuntimeError(
99
+ error.message,
100
+ node,
101
+ 'MOD001',
102
+ 'Check if the module path is correct and the module exists.'
103
+ );
104
+ }
105
+ return null;
106
+ }
107
+ }
@@ -0,0 +1,82 @@
1
+ export function isTruthy(value) {
2
+ if (value === null || value === undefined) return false;
3
+ if (typeof value === "boolean") return value;
4
+ if (typeof value === "number") return value !== 0;
5
+ if (typeof value === "string") return value.length > 0;
6
+ return true;
7
+ }
8
+
9
+ export function stringify(value, useColors = false) {
10
+ if (value === null || value === undefined) return useColors ? "\x1b[90mnull\x1b[0m" : "null";
11
+
12
+ if (value instanceof Date) {
13
+ const str = `datetime(${value.toISOString()})`;
14
+ return useColors ? `\x1b[32m${str}\x1b[0m` : str;
15
+ }
16
+
17
+ if (Array.isArray(value)) {
18
+ const items = value.map((v) => stringify(v, useColors)).join(useColors ? "\x1b[90m, \x1b[0m" : ", ");
19
+ return useColors ? `\x1b[90m[\x1b[0m${items}\x1b[90m]\x1b[0m` : `[${items}]`;
20
+ }
21
+
22
+ // Handle Function objects to avoid printing the AST in 'show'
23
+ if (value && typeof value === 'object' && (value.constructor.name === "FunctionValue" || value.constructor.name === "BuiltinFunction")) {
24
+ const label = value.name === '<anonymous>' ? 'anonymous function' : `function ${value.name}`;
25
+ return useColors ? `\x1b[36m[${label}]\x1b[0m` : `[${label}]`;
26
+ }
27
+
28
+ if (typeof value === "object" && value !== null) {
29
+ const pairs = Object.entries(value).map(([key, val]) => {
30
+ const k = useColors ? `\x1b[34m${key}\x1b[0m` : key;
31
+ return `${k}: ${stringify(val, useColors)}`;
32
+ });
33
+ return useColors ? `\x1b[90m{\x1b[0m${pairs.join(useColors ? "\x1b[90m, \x1b[0m" : ", ")}\x1b[90m}\x1b[0m` : `{${pairs.join(", ")}}`;
34
+ }
35
+
36
+ if (typeof value === "string") return useColors ? `\x1b[32m"${value}"\x1b[0m` : value;
37
+ if (typeof value === "number") return useColors ? `\x1b[33m${value}\x1b[0m` : String(value);
38
+ if (typeof value === "boolean") return useColors ? `\x1b[35m${value}\x1b[0m` : String(value);
39
+
40
+ return String(value);
41
+ }
42
+
43
+ /**
44
+ * Very basic syntax highlighting for Mimo code.
45
+ * @param {string} code
46
+ * @returns {string} Colored ANSI string
47
+ */
48
+ export function highlightMimoCode(code) {
49
+ // Keywords
50
+ const keywords = ["set", "let", "const", "global", "function", "fn", "return", "if", "else", "while", "for", "in", "try", "catch", "throw", "match", "case", "import", "from", "export", "as", "end", "call", "show"];
51
+ const operators = ["+", "-", "*", "/", "%", "=", "!=", "==", ">", "<", ">=", "<=", "and", "or", "not", "&&", "||", "!", "??", "?."];
52
+
53
+ let highlighted = code;
54
+
55
+ // Highlight strings - avoid highlighting inside highlighted parts
56
+ highlighted = highlighted.replace(/"[^"]*"/g, (match) => `\x1b[32m${match}\x1b[0m`);
57
+
58
+ // Highlight comments
59
+ highlighted = highlighted.replace(/#.*$/gm, (match) => `\x1b[90m${match}\x1b[0m`);
60
+
61
+ // Highlight numbers
62
+ highlighted = highlighted.replace(/\b\d+(\.\d+)?\b/g, (match) => `\x1b[33m${match}\x1b[0m`);
63
+
64
+ // Highlight boolean literals
65
+ highlighted = highlighted.replace(/\b(true|false|null)\b/g, (match) => `\x1b[35m${match}\x1b[0m`);
66
+
67
+ // Highlight keywords
68
+ for (const kw of keywords) {
69
+ const regex = new RegExp(`\\b${kw}\\b`, 'g');
70
+ highlighted = highlighted.replace(regex, (match) => `\x1b[1;36m${match}\x1b[0m`);
71
+ }
72
+
73
+ // Highlight operators
74
+ for (const op of operators) {
75
+ const escapedOp = op.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
76
+ // Avoid replacing inside ANSI escape codes
77
+ const regex = new RegExp(`(?<!\\x1b\\[[0-9;]*)(${escapedOp})(?![0-9;]*m)`, 'g');
78
+ highlighted = highlighted.replace(regex, (match) => `\x1b[31m${match}\x1b[0m`);
79
+ }
80
+
81
+ return highlighted;
82
+ }
@@ -0,0 +1,87 @@
1
+ import { Environment } from './environment.js';
2
+
3
+
4
+
5
+ export class ReturnValue {
6
+ constructor(value) {
7
+ this.value = value;
8
+ }
9
+ }
10
+
11
+ export class BreakException {
12
+ constructor(label = null) {
13
+ this.label = label;
14
+ }
15
+ }
16
+
17
+ export class ContinueException {
18
+ constructor(label = null) {
19
+ this.label = label;
20
+ }
21
+ }
22
+
23
+ export class FunctionValue {
24
+ constructor(declaration, closure) {
25
+ this.declaration = declaration;
26
+ this.closure = closure;
27
+ // Add a name property, defaulting to '<anonymous>' if not declared.
28
+ this.name = declaration.name || '<anonymous>';
29
+ }
30
+
31
+ call(interpreter, args, callNode) {
32
+ const { params, defaults, restParam } = this.declaration;
33
+
34
+ // --- Arity Check ---
35
+ const requiredParamsCount = params.filter(pNode => !defaults[pNode.name]).length;
36
+ if (args.length < requiredParamsCount) {
37
+ throw interpreter.errorHandler.createRuntimeError(
38
+ `Function '${this.name}' expects at least ${requiredParamsCount} arguments but received ${args.length}.`,
39
+ callNode, 'FUNC001'
40
+ );
41
+ }
42
+ if (!restParam && args.length > params.length) {
43
+ throw interpreter.errorHandler.createRuntimeError(
44
+ `Function '${this.name}' expects at most ${params.length} arguments but received ${args.length}.`,
45
+ callNode, 'FUNC002'
46
+ );
47
+ }
48
+
49
+ // --- Environment Setup ---
50
+ const env = new Environment(this.closure, false, true);
51
+
52
+ // Bind regular parameters by iterating over the Identifier nodes
53
+ params.forEach((paramNode, i) => {
54
+ const paramName = paramNode.name;
55
+ let value = args[i]; // Get the passed argument
56
+
57
+ if (value === undefined) {
58
+ // If no argument was passed, check for a default value
59
+ if (defaults[paramName]) {
60
+ value = interpreter.visitNode(defaults[paramName]);
61
+ } else {
62
+ // This case should be caught by arity check, but is a safeguard
63
+ throw new Error(`Interpreter Error: Missing value for required parameter ${paramName}`);
64
+ }
65
+ }
66
+ env.define(paramName, value);
67
+ });
68
+
69
+ // Handle rest parameter if it exists
70
+ if (restParam) {
71
+ const restArgs = args.slice(params.length);
72
+ env.define(restParam.name, restArgs);
73
+ }
74
+
75
+ // --- Execution ---
76
+ interpreter.pushCallStack(this.name, callNode);
77
+ try {
78
+ const result = interpreter.executeBlock(this.declaration.body, env);
79
+ if (result instanceof ReturnValue) {
80
+ return result.value;
81
+ }
82
+ return null; // Implicit return of null
83
+ } finally {
84
+ interpreter.popCallStack();
85
+ }
86
+ }
87
+ }