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.
- package/.gitattributes +24 -0
- package/LICENSE +21 -0
- package/README.md +71 -39
- package/adapters/browserAdapter.js +86 -0
- package/adapters/nodeAdapter.js +101 -0
- package/bin/cli.js +80 -0
- package/bin/commands/convert.js +27 -0
- package/bin/commands/doctor.js +139 -0
- package/bin/commands/eval.js +39 -0
- package/bin/commands/fmt.js +109 -0
- package/bin/commands/help.js +72 -0
- package/bin/commands/lint.js +117 -0
- package/bin/commands/repl.js +24 -0
- package/bin/commands/run.js +64 -0
- package/bin/commands/test.js +126 -0
- package/bin/utils/colors.js +38 -0
- package/bin/utils/formatError.js +47 -0
- package/bin/utils/fs.js +57 -0
- package/bin/utils/version.js +8 -0
- package/build.js +18 -0
- package/bun.lock +74 -0
- package/index.js +48 -77
- package/index.web.js +364 -0
- package/interpreter/BuiltinFunction.js +32 -0
- package/interpreter/ErrorHandler.js +120 -0
- package/interpreter/ExpressionEvaluator.js +106 -0
- package/interpreter/Interpreter.js +172 -0
- package/interpreter/MimoError.js +112 -0
- package/interpreter/ModuleLoader.js +236 -0
- package/interpreter/StatementExecutor.js +107 -0
- package/interpreter/Utils.js +82 -0
- package/interpreter/Values.js +87 -0
- package/interpreter/coreBuiltins.js +490 -0
- package/interpreter/environment.js +99 -0
- package/interpreter/evaluators/binaryExpressionEvaluator.js +111 -0
- package/interpreter/evaluators/collectionEvaluator.js +151 -0
- package/interpreter/evaluators/functionCallEvaluator.js +76 -0
- package/interpreter/evaluators/literalEvaluator.js +27 -0
- package/interpreter/evaluators/moduleAccessEvaluator.js +25 -0
- package/interpreter/evaluators/templateLiteralEvaluator.js +20 -0
- package/interpreter/executors/BaseExecutor.js +37 -0
- package/interpreter/executors/ControlFlowExecutor.js +206 -0
- package/interpreter/executors/FunctionExecutor.js +126 -0
- package/interpreter/executors/PatternMatchExecutor.js +93 -0
- package/interpreter/executors/VariableExecutor.js +144 -0
- package/interpreter/index.js +8 -0
- package/interpreter/stdlib/array/accessFunctions.js +61 -0
- package/interpreter/stdlib/array/arrayUtils.js +36 -0
- package/interpreter/stdlib/array/higherOrderFunctions.js +285 -0
- package/interpreter/stdlib/array/searchFunctions.js +77 -0
- package/interpreter/stdlib/array/setFunctions.js +49 -0
- package/interpreter/stdlib/array/transformationFunctions.js +68 -0
- package/interpreter/stdlib/array.js +85 -0
- package/interpreter/stdlib/assert.js +143 -0
- package/interpreter/stdlib/datetime.js +170 -0
- package/interpreter/stdlib/env.js +54 -0
- package/interpreter/stdlib/fs.js +161 -0
- package/interpreter/stdlib/http.js +92 -0
- package/interpreter/stdlib/json.js +70 -0
- package/interpreter/stdlib/math.js +309 -0
- package/interpreter/stdlib/object.js +142 -0
- package/interpreter/stdlib/path.js +69 -0
- package/interpreter/stdlib/regex.js +134 -0
- package/interpreter/stdlib/string.js +260 -0
- package/interpreter/suggestions.js +46 -0
- package/lexer/Lexer.js +245 -0
- package/lexer/TokenTypes.js +131 -0
- package/lexer/createToken.js +11 -0
- package/lexer/tokenizers/commentTokenizer.js +45 -0
- package/lexer/tokenizers/literalTokenizer.js +163 -0
- package/lexer/tokenizers/symbolTokenizer.js +69 -0
- package/lexer/tokenizers/whitespaceTokenizer.js +36 -0
- package/package.json +29 -13
- package/parser/ASTNodes.js +448 -0
- package/parser/Parser.js +188 -0
- package/parser/expressions/atomicExpressions.js +165 -0
- package/parser/expressions/conditionalExpressions.js +0 -0
- package/parser/expressions/operatorExpressions.js +79 -0
- package/parser/expressions/primaryExpressions.js +77 -0
- package/parser/parseStatement.js +184 -0
- package/parser/parserExpressions.js +115 -0
- package/parser/parserUtils.js +19 -0
- package/parser/statements/controlFlowParsers.js +106 -0
- package/parser/statements/functionParsers.js +314 -0
- package/parser/statements/moduleParsers.js +57 -0
- package/parser/statements/patternMatchParsers.js +124 -0
- package/parser/statements/variableParsers.js +155 -0
- package/repl.js +325 -0
- package/test.js +47 -0
- package/tools/PrettyPrinter.js +3 -0
- package/tools/convert/Args.js +46 -0
- package/tools/convert/Registry.js +91 -0
- package/tools/convert/Transpiler.js +78 -0
- package/tools/convert/plugins/README.md +66 -0
- package/tools/convert/plugins/alya/index.js +10 -0
- package/tools/convert/plugins/alya/to_alya.js +289 -0
- package/tools/convert/plugins/alya/visitors/expressions.js +257 -0
- package/tools/convert/plugins/alya/visitors/statements.js +403 -0
- package/tools/convert/plugins/base_converter.js +228 -0
- package/tools/convert/plugins/javascript/index.js +10 -0
- package/tools/convert/plugins/javascript/mimo_runtime.js +265 -0
- package/tools/convert/plugins/javascript/to_js.js +155 -0
- package/tools/convert/plugins/javascript/visitors/expressions.js +197 -0
- package/tools/convert/plugins/javascript/visitors/patterns.js +102 -0
- package/tools/convert/plugins/javascript/visitors/statements.js +236 -0
- package/tools/convert/plugins/python/index.js +10 -0
- package/tools/convert/plugins/python/mimo_runtime.py +811 -0
- package/tools/convert/plugins/python/to_py.js +329 -0
- package/tools/convert/plugins/python/visitors/expressions.js +272 -0
- package/tools/convert/plugins/python/visitors/patterns.js +100 -0
- package/tools/convert/plugins/python/visitors/statements.js +257 -0
- package/tools/convert.js +102 -0
- package/tools/format/CommentAttacher.js +190 -0
- package/tools/format/CommentLexer.js +152 -0
- package/tools/format/Printer.js +849 -0
- package/tools/format/config.js +107 -0
- package/tools/formatter.js +169 -0
- package/tools/lint/Linter.js +391 -0
- package/tools/lint/config.js +114 -0
- package/tools/lint/rules/consistent-return.js +62 -0
- package/tools/lint/rules/max-depth.js +56 -0
- package/tools/lint/rules/no-empty-function.js +45 -0
- package/tools/lint/rules/no-magic-numbers.js +46 -0
- package/tools/lint/rules/no-shadow.js +113 -0
- package/tools/lint/rules/no-unused-vars.js +26 -0
- package/tools/lint/rules/prefer-const.js +19 -0
- package/tools/linter.js +261 -0
- package/tools/replFormatter.js +93 -0
- package/tools/stamp-version.js +32 -0
- package/web/index.js +9 -0
- package/bun.lockb +0 -0
- package/cli.js +0 -84
- package/compiler/execute/interpreter.js +0 -68
- package/compiler/execute/interpreters/binary.js +0 -12
- package/compiler/execute/interpreters/call.js +0 -10
- package/compiler/execute/interpreters/if.js +0 -10
- package/compiler/execute/interpreters/try-catch.js +0 -10
- package/compiler/execute/interpreters/while.js +0 -8
- package/compiler/execute/utils/createfunction.js +0 -11
- package/compiler/execute/utils/evaluate.js +0 -20
- package/compiler/execute/utils/operate.js +0 -23
- package/compiler/lexer/processToken.js +0 -40
- package/compiler/lexer/tokenTypes.js +0 -4
- package/compiler/lexer/tokenizer.js +0 -74
- package/compiler/parser/expression/comparison.js +0 -18
- package/compiler/parser/expression/identifier.js +0 -29
- package/compiler/parser/expression/number.js +0 -10
- package/compiler/parser/expression/operator.js +0 -21
- package/compiler/parser/expression/punctuation.js +0 -31
- package/compiler/parser/expression/string.js +0 -6
- package/compiler/parser/parseExpression.js +0 -27
- package/compiler/parser/parseStatement.js +0 -34
- package/compiler/parser/parser.js +0 -45
- package/compiler/parser/statement/call.js +0 -26
- package/compiler/parser/statement/function.js +0 -29
- package/compiler/parser/statement/if.js +0 -34
- package/compiler/parser/statement/return.js +0 -10
- package/compiler/parser/statement/set.js +0 -11
- package/compiler/parser/statement/show.js +0 -10
- package/compiler/parser/statement/try-catch.js +0 -25
- package/compiler/parser/statement/while.js +0 -22
- package/converter/go/convert.js +0 -110
- package/converter/js/convert.js +0 -107
- package/jsconfig.json +0 -27
- 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
|
+
}
|