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,261 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'url';
5
+
6
+ import { Parser } from '../parser/Parser.js';
7
+ import { Lexer } from '../lexer/Lexer.js';
8
+ import { Linter } from './lint/Linter.js';
9
+ import { loadConfig, findConfigFile, mergeRuleConfigs } from './lint/config.js';
10
+
11
+ const DEFAULT_RULES = {
12
+ 'no-unused-vars': true,
13
+ 'prefer-const': true,
14
+ 'no-magic-numbers': false,
15
+ 'no-empty-function': false,
16
+ 'max-depth': false,
17
+ 'no-shadow': false,
18
+ 'consistent-return': false,
19
+ };
20
+
21
+ // ---------------------------------------------------------------------------
22
+ // Helpers
23
+ // ---------------------------------------------------------------------------
24
+
25
+ /** Synchronous fs reader — injected into config helpers so they stay bundler-safe. */
26
+ const fsReadFile = (p) => fs.readFileSync(p, 'utf-8');
27
+
28
+ /**
29
+ * Resolve the merged rule config for a given file path.
30
+ * Priority: DEFAULT_RULES < .mimorc < caller-supplied `rules` overrides.
31
+ */
32
+ function resolveRules(filePath, callerRules = {}) {
33
+ const fileDir = path.dirname(path.resolve(filePath));
34
+ const rootDir = path.resolve('/');
35
+ const pathJoin = path.join.bind(path);
36
+
37
+ const configPath = findConfigFile(fileDir, rootDir, fsReadFile, pathJoin);
38
+ const fileConfig = configPath ? loadConfig(configPath, fsReadFile) : { rules: {} };
39
+
40
+ return mergeRuleConfigs(DEFAULT_RULES, fileConfig.rules, callerRules);
41
+ }
42
+
43
+ /** Parse source into an AST. Throws on syntax errors. */
44
+ function parseSource(source, filePath) {
45
+ const lexer = new Lexer(source, filePath);
46
+ const tokens = [];
47
+ let token;
48
+ while ((token = lexer.nextToken()) !== null) {
49
+ tokens.push(token);
50
+ }
51
+ const parser = new Parser(tokens, filePath);
52
+ return parser.parse();
53
+ }
54
+
55
+ // ---------------------------------------------------------------------------
56
+ // ANSI helpers
57
+ // ---------------------------------------------------------------------------
58
+ const RESET = '\x1b[0m';
59
+ const CYAN = '\x1b[36m';
60
+ const YELLOW = '\x1b[33m';
61
+ const RED = '\x1b[31m';
62
+ const DIM = '\x1b[90m';
63
+
64
+ function severityLabel(severity) {
65
+ return severity === 'error'
66
+ ? `${RED}error${RESET}`
67
+ : `${YELLOW}warning${RESET}`;
68
+ }
69
+
70
+ function squiggleColor(severity) {
71
+ return severity === 'error' ? RED : YELLOW;
72
+ }
73
+
74
+ // ---------------------------------------------------------------------------
75
+ // Public API
76
+ // ---------------------------------------------------------------------------
77
+
78
+ export function lintFile(filePath, options = {}) {
79
+ const { quiet = false, rules = {} } = options;
80
+ const enabledRules = resolveRules(filePath, rules);
81
+
82
+ if (!quiet) {
83
+ console.log(`Linting ${filePath}...`);
84
+ }
85
+ try {
86
+ const source = fs.readFileSync(filePath, 'utf-8');
87
+ const ast = parseSource(source, filePath);
88
+
89
+ const linter = new Linter({ rules: enabledRules });
90
+ const messages = linter.verify(ast, source, filePath);
91
+
92
+ const errors = messages.filter(m => m.severity === 'error');
93
+ const warnings = messages.filter(m => m.severity !== 'error');
94
+
95
+ if (messages.length === 0) {
96
+ if (!quiet) {
97
+ console.log('No problems found.');
98
+ }
99
+ return { ok: true, messages, errorCount: 0, warningCount: 0, file: filePath };
100
+ }
101
+
102
+ console.log(`\nFound ${messages.length} problem(s) in ${filePath}:`);
103
+
104
+ const sourceLines = source.split('\n');
105
+ messages.forEach(msg => {
106
+ const color = squiggleColor(msg.severity);
107
+ console.log(`\n ${CYAN}${filePath}:${msg.line}:${msg.column}${RESET}`);
108
+ console.log(` ${severityLabel(msg.severity)} ${msg.message} ${DIM}${msg.ruleId}${RESET}`);
109
+
110
+ const line = sourceLines[msg.line - 1];
111
+ if (line) {
112
+ console.log(`\n ${msg.line} | ${line}`);
113
+ const padding = ' '.repeat(String(msg.line).length + 3 + msg.column - 1);
114
+ const squiggles = '^'.repeat(Math.max(1, msg.endColumn - msg.column));
115
+ console.log(` ${padding}${color}${squiggles}${RESET}`);
116
+ }
117
+ });
118
+
119
+ return {
120
+ ok: true,
121
+ messages,
122
+ errorCount: errors.length,
123
+ warningCount: warnings.length,
124
+ file: filePath,
125
+ };
126
+
127
+ } catch (err) {
128
+ console.error(`\n${RED}Error linting file ${filePath}:${RESET}`);
129
+ if (err.format) {
130
+ const snippet = fs.readFileSync(filePath, 'utf-8').split('\n')[err.location?.line - 1] || '';
131
+ console.error(err.format(snippet));
132
+ } else {
133
+ console.error(err.message);
134
+ }
135
+ return {
136
+ ok: false,
137
+ messages: [],
138
+ errorCount: 0,
139
+ warningCount: 0,
140
+ file: filePath,
141
+ error: {
142
+ message: err.message,
143
+ line: err.location?.line,
144
+ column: err.location?.column,
145
+ },
146
+ };
147
+ }
148
+ }
149
+
150
+ export function lintFileJson(filePath, options = {}) {
151
+ const { rules = {} } = options;
152
+ const enabledRules = resolveRules(filePath, rules);
153
+
154
+ try {
155
+ const source = fs.readFileSync(filePath, 'utf-8');
156
+ const ast = parseSource(source, filePath);
157
+
158
+ const linter = new Linter({ rules: enabledRules });
159
+ const messages = linter.verify(ast, source, filePath);
160
+
161
+ return {
162
+ ok: true,
163
+ file: filePath,
164
+ messages: messages.map(msg => ({
165
+ line: msg.line,
166
+ column: msg.column,
167
+ endColumn: msg.endColumn,
168
+ message: msg.message,
169
+ ruleId: msg.ruleId,
170
+ severity: msg.severity, // use actual severity, not hard-coded 'warning'
171
+ })),
172
+ };
173
+
174
+ } catch (err) {
175
+ return {
176
+ ok: false,
177
+ file: filePath,
178
+ error: {
179
+ message: err.message,
180
+ line: err.location?.line || 1,
181
+ column: err.location?.column || 1,
182
+ },
183
+ messages: [],
184
+ };
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Parse `--rule:name=true|false` flags from a CLI args array.
190
+ * Supports an optional severity suffix: `--rule:name=error` or `--rule:name=warning`.
191
+ */
192
+ export function parseRuleFlags(args) {
193
+ const rules = {};
194
+ for (const arg of args) {
195
+ if (arg.startsWith('--rule:')) {
196
+ const ruleDef = arg.slice(7);
197
+ const eqIndex = ruleDef.indexOf('=');
198
+ if (eqIndex !== -1) {
199
+ const ruleName = ruleDef.slice(0, eqIndex);
200
+ const ruleValue = ruleDef.slice(eqIndex + 1).toLowerCase();
201
+ if (ruleValue === 'false' || ruleValue === '0') {
202
+ rules[ruleName] = false;
203
+ } else if (ruleValue === 'true' || ruleValue === '1') {
204
+ rules[ruleName] = true;
205
+ } else if (ruleValue === 'error' || ruleValue === 'warning') {
206
+ rules[ruleName] = { severity: ruleValue };
207
+ } else {
208
+ rules[ruleName] = true; // unknown value — treat as enable
209
+ }
210
+ }
211
+ }
212
+ }
213
+ return rules;
214
+ }
215
+
216
+ // ---------------------------------------------------------------------------
217
+ // Main (direct invocation)
218
+ // ---------------------------------------------------------------------------
219
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
220
+ const args = process.argv.slice(2);
221
+ const quiet = args.includes('--quiet');
222
+ const json = args.includes('--json');
223
+ const failOnWarning = args.includes('--fail-on-warning');
224
+ const rules = parseRuleFlags(args);
225
+ const filePaths = args.filter(arg => !arg.startsWith('--'));
226
+
227
+ if (filePaths.length === 0) {
228
+ console.error('Error: No file path provided.');
229
+ console.log('Usage: node tools/linter.js [--fail-on-warning] [--quiet] [--json] [--rule:name=true|false|error|warning] <file1.mimo> ...');
230
+ process.exit(1);
231
+ }
232
+
233
+ if (json) {
234
+ const results = filePaths.map(p => lintFileJson(p, { rules }));
235
+ console.log(JSON.stringify(results.length === 1 ? results[0] : results));
236
+ const hasParseErrors = results.some(r => !r.ok);
237
+ const errorCount = results.reduce((sum, r) => sum + r.messages.filter(m => m.severity === 'error').length, 0);
238
+ const warningCount = results.reduce((sum, r) => sum + r.messages.filter(m => m.severity !== 'error').length, 0);
239
+ if (hasParseErrors || errorCount > 0 || (failOnWarning && warningCount > 0)) {
240
+ process.exit(1);
241
+ }
242
+ } else {
243
+ let hasParseErrors = false;
244
+ let errorCount = 0;
245
+ let warningCount = 0;
246
+
247
+ filePaths.forEach(p => {
248
+ const result = lintFile(p, { quiet, rules });
249
+ if (!result.ok) {
250
+ hasParseErrors = true;
251
+ return;
252
+ }
253
+ errorCount += result.errorCount;
254
+ warningCount += result.warningCount;
255
+ });
256
+
257
+ if (hasParseErrors || errorCount > 0 || (failOnWarning && warningCount > 0)) {
258
+ process.exit(1);
259
+ }
260
+ }
261
+ }
@@ -0,0 +1,93 @@
1
+ import { FunctionValue } from '../interpreter/Values.js';
2
+ import { BuiltinFunction } from '../interpreter/BuiltinFunction.js';
3
+
4
+ /**
5
+ * Formats a Mimo value for REPL output.
6
+ * @param {any} value The value to format
7
+ * @param {boolean} useColors Whether to use ANSI colors
8
+ * @param {number} indent Current indentation level
9
+ * @returns {string} Colored or plain string
10
+ */
11
+ export function formatValue(value, useColors = true, indent = 0) {
12
+ const spacing = " ".repeat(indent);
13
+
14
+ const colors = {
15
+ grey: useColors ? "\x1b[90m" : "",
16
+ cyan: useColors ? "\x1b[36m" : "",
17
+ green: useColors ? "\x1b[32m" : "",
18
+ yellow: useColors ? "\x1b[33m" : "",
19
+ magenta: useColors ? "\x1b[35m" : "",
20
+ blue: useColors ? "\x1b[34m" : "",
21
+ reset: useColors ? "\x1b[0m" : ""
22
+ };
23
+
24
+ if (value === null || value === undefined) return `${colors.grey}null${colors.reset}`;
25
+
26
+ // Handle Functions specially to avoid printing the AST
27
+ if (value instanceof FunctionValue || (value && value.constructor && value.constructor.name === "FunctionValue")) {
28
+ const name = value.name === '<anonymous>' ? 'anonymous' : value.name;
29
+ return `${colors.cyan}[Function: ${name}]${colors.reset}`;
30
+ }
31
+
32
+ if (value instanceof BuiltinFunction || (value && value.constructor && value.constructor.name === "BuiltinFunction")) {
33
+ return `${colors.cyan}[BuiltinFunction: ${value.name}]${colors.reset}`;
34
+ }
35
+
36
+ if (value instanceof Date) {
37
+ return `${colors.green}datetime(${value.toISOString()})${colors.reset}`;
38
+ }
39
+
40
+ const MAX_ITEMS = 20;
41
+ const MAX_KEYS = 20;
42
+
43
+ if (Array.isArray(value)) {
44
+ if (value.length === 0) return `${colors.grey}Array(0) []${colors.reset}`;
45
+
46
+ const shownItems = value.slice(0, MAX_ITEMS).map(v => formatValue(v, useColors, indent + 1));
47
+ const hiddenCount = Math.max(0, value.length - shownItems.length);
48
+ const flatItems = shownItems.join(`${colors.grey}, ${colors.reset}`);
49
+
50
+ if (flatItems.length < 60 && hiddenCount === 0) {
51
+ return `${colors.grey}Array(${value.length}) [${colors.reset}${flatItems}${colors.grey}]${colors.reset}`;
52
+ }
53
+
54
+ const multilineItems = [...shownItems];
55
+ if (hiddenCount > 0) {
56
+ multilineItems.push(`${colors.grey}... ${hiddenCount} more${colors.reset}`);
57
+ }
58
+
59
+ return `${colors.grey}Array(${value.length}) [${colors.reset}\n${spacing} ${multilineItems.join(`${colors.grey},${colors.reset}\n` + spacing + " ")}\n${spacing}${colors.grey}]${colors.reset}`;
60
+ }
61
+
62
+ if (typeof value === "object") {
63
+ const keys = Object.keys(value);
64
+ if (keys.length === 0) return `${colors.grey}{}${colors.reset}`;
65
+
66
+ const shownKeys = keys.slice(0, MAX_KEYS);
67
+ const hiddenCount = Math.max(0, keys.length - shownKeys.length);
68
+
69
+ const pairs = shownKeys.map(k => {
70
+ const val = formatValue(value[k], useColors, indent + 1);
71
+ return `${colors.blue}${k}${colors.reset}${colors.grey}:${colors.reset} ${val}`;
72
+ });
73
+ if (hiddenCount > 0) {
74
+ pairs.push(`${colors.grey}... ${hiddenCount} more${colors.reset}`);
75
+ }
76
+
77
+ const flatPairs = pairs.join(`${colors.grey}, ${colors.reset}`);
78
+
79
+ // Use single-line if short enough
80
+ if (flatPairs.length < 50) {
81
+ return `${colors.grey}{${colors.reset}${flatPairs}${colors.grey}}${colors.reset}`;
82
+ }
83
+
84
+ // Multi-line for large objects
85
+ return `${colors.grey}{${colors.reset}\n${spacing} ${pairs.join(`${colors.grey},${colors.reset}\n` + spacing + " ")}\n${spacing}${colors.grey}}${colors.reset}`;
86
+ }
87
+
88
+ if (typeof value === "string") return `${colors.green}"${value}"${colors.reset}`;
89
+ if (typeof value === "number") return `${colors.yellow}${value}${colors.reset}`;
90
+ if (typeof value === "boolean") return `${colors.magenta}${value}${colors.reset}`;
91
+
92
+ return String(value);
93
+ }
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env bun
2
+ // tools/stamp-version.js
3
+ // Reads the version from package.json and writes it as a static literal into
4
+ // bin/utils/version.js so that `bun build --compile` bakes the correct string
5
+ // into the standalone binary.
6
+ //
7
+ // Run automatically via the `prebuild:mimo` script, or manually:
8
+ // bun tools/stamp-version.js
9
+
10
+ import fs from 'node:fs';
11
+ import path from 'node:path';
12
+ import { fileURLToPath } from 'node:url';
13
+
14
+ const __dir = path.dirname(fileURLToPath(import.meta.url));
15
+ const root = path.resolve(__dir, '..');
16
+
17
+ const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf-8'));
18
+ const version = pkg.version;
19
+
20
+ const target = path.join(root, 'bin', 'utils', 'version.js');
21
+ const content = `// bin/utils/version.js
22
+ // This string is stamped by the \`prebuild:mimo\` script (tools/stamp-version.js).
23
+ // It is a static literal so it survives \`bun build --compile\` and global installs.
24
+ export const VERSION = '${version}';
25
+
26
+ export function getVersion() {
27
+ return VERSION;
28
+ }
29
+ `;
30
+
31
+ fs.writeFileSync(target, content, 'utf-8');
32
+ console.log(`Stamped version ${version} into bin/utils/version.js`);
package/web/index.js ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @file The entry point for the Mimo web bundle.
3
+ */
4
+ import { Mimo } from '../index.web.js';
5
+ import { browserAdapter } from '../adapters/browserAdapter.js';
6
+
7
+ // Expose the Mimo class and the browser adapter to the global scope
8
+ window.Mimo = Mimo;
9
+ window.mimoBrowserAdapter = browserAdapter;
package/bun.lockb DELETED
Binary file
package/cli.js DELETED
@@ -1,84 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import fs from "fs";
4
- import path from "path";
5
- import Mimo from "./index.js";
6
-
7
- // Get the directory of the current script
8
- const __dirname = path.dirname(new URL(import.meta.url).pathname);
9
-
10
- // Read the package.json file
11
- const packageJson = JSON.parse(
12
- fs.readFileSync(path.join(__dirname, "package.json"), "utf-8")
13
- );
14
- const { version } = packageJson;
15
-
16
- // Check if the user asked for the version
17
- if (process.argv.includes("-v") || process.argv.includes("--version")) {
18
- console.log(version);
19
- process.exit(0);
20
- }
21
-
22
- // Check if the user asked for help
23
- if (process.argv.includes("-h") || process.argv.includes("--help")) {
24
- console.log("Usage: mimo [FILENAME] [-o|--output] [-t|--time] [-h|--help] [-q|--quiet] [-d|--debug] [-v|--version]");
25
- console.log("FILENAME: The file to process.");
26
- console.log("-o, --output: Generate output file.");
27
- console.log("-t, --time: Measure execution time.");
28
- console.log("-h, --help: Show this help message.");
29
- console.log("-q, --quiet: Suppress all non-essential output.");
30
- console.log("-d, --debug: Display additional debug information.");
31
- console.log("-v, --version: Display the version number.");
32
- process.exit(0);
33
- }
34
-
35
- // Check if a filename was provided
36
- if (process.argv.length < 3) {
37
- console.error("Error: No file name provided.\n");
38
- console.log("Usage: mimo [FILENAME] [-o|--output] [-t|--time] [-h|--help] [-q|--quiet] [-d|--debug] [-v|--version]");
39
- console.log("Use the --help flag for more information.\n\n");
40
- process.exit(1);
41
- }
42
-
43
- const filename = process.argv[2];
44
- const shouldGenerateOutput = process.argv.includes("-o") || process.argv.includes("--output");
45
- const shouldMeasureTime = process.argv.includes("-t") || process.argv.includes("--time");
46
- const shouldSuppressOutput = process.argv.includes("-q") || process.argv.includes("--quiet");
47
- const shouldDisplayDebugInfo = process.argv.includes("-d") || process.argv.includes("--debug");
48
-
49
- // Check if the file exists
50
- if (!fs.existsSync(filename)) {
51
- console.error(`Error: File '${filename}' does not exist.`);
52
- process.exit(1);
53
- }
54
-
55
- // Read the file
56
- let code;
57
- try {
58
- code = fs.readFileSync(filename, "utf-8");
59
- } catch (err) {
60
- console.error(`Error reading file '${filename}':`, err.message);
61
- process.exit(1);
62
- }
63
-
64
- // Run the code
65
- const mimo = new Mimo();
66
- try {
67
- if (shouldMeasureTime) console.time("Execution time");
68
-
69
- let { program } = await mimo.run(code);
70
-
71
- if (shouldMeasureTime) console.timeEnd("Execution time");
72
-
73
- if (shouldGenerateOutput) {
74
- const outputFilename = path.join(
75
- path.dirname(filename),
76
- path.basename(filename, path.extname(filename)) + ".js"
77
- );
78
- fs.writeFileSync(outputFilename, mimo.toJS(program), "utf-8");
79
- if (!shouldSuppressOutput) console.log(`Output written to: ${outputFilename}`);
80
- }
81
- } catch (err) {
82
- if (shouldDisplayDebugInfo) console.error("Error running code:", err);
83
- process.exit(1);
84
- }
@@ -1,68 +0,0 @@
1
- import { interpretBinary } from "./interpreters/binary.js";
2
- import { interpretCall } from "./interpreters/call.js";
3
- import { interpretIf } from "./interpreters/if.js";
4
- import { interpretTryCatch } from "./interpreters/try-catch.js";
5
- import { interpretWhile } from "./interpreters/while.js";
6
- import { createFunction } from "./utils/createfunction.js";
7
- import { evaluate } from "./utils/evaluate.js";
8
-
9
- export async function interpretStatement(statement, env) {
10
- if (Array.isArray(statement)) {
11
- for (let i = 0; i < statement.length; i++) {
12
- if (await interpretStatement(statement[i], env)) return true;
13
- }
14
- return;
15
- }
16
-
17
- if (typeof statement === "object") {
18
- if (statement.type === "try-catch") {
19
- await interpretTryCatch(statement, env);
20
- } else {
21
- await interpretObjectStatement(statement, env);
22
- }
23
- }
24
- }
25
-
26
- async function interpretObjectStatement(statement, env) {
27
- switch (statement.type) {
28
- case "assignment":
29
- env[statement.target] = evaluate(statement.value, env);
30
- break;
31
- case "binary":
32
- await interpretBinary(statement, env);
33
- break;
34
- case "if":
35
- await interpretIf(statement, env);
36
- break;
37
- case "while":
38
- await interpretWhile(statement, env);
39
- break;
40
- case "function":
41
- env[statement.name] = createFunction(
42
- statement.params,
43
- statement.body,
44
- env
45
- );
46
- break;
47
- case "return":
48
- env["return"] = evaluate(statement.expression, env);
49
- return true;
50
- case "call":
51
- await interpretCall(statement, env);
52
- break;
53
- case "print":
54
- console.log(evaluate(statement.value, env));
55
- break;
56
- default:
57
- console.log("Unknown statement type", statement.type);
58
- break;
59
- }
60
- }
61
-
62
- export async function interpret(program, env = {}) {
63
- let index = 0;
64
- while (index < program.length) {
65
- if (await interpretStatement(program[index++], env)) break;
66
- }
67
- return env;
68
- }
@@ -1,12 +0,0 @@
1
- import { evaluate } from "../utils/evaluate.js";
2
-
3
- export async function interpretBinary(statement, env) {
4
- if (env[statement.target] === undefined) {
5
- env[statement.target] = null;
6
- }
7
- env[statement.target] = await operate(
8
- statement.operator,
9
- await evaluate(statement.left, env),
10
- await evaluate(statement.right, env)
11
- );
12
- }
@@ -1,10 +0,0 @@
1
- import { evaluate } from "../utils/evaluate.js";
2
-
3
- export async function interpretCall(statement, env) {
4
- const func = env[statement.name];
5
- if (typeof func !== "function") {
6
- throw new Error(`${statement.name} is not a function`);
7
- }
8
- const args = statement.args.map((arg) => evaluate(arg, env));
9
- env[statement.target] = await func(...args);
10
- }
@@ -1,10 +0,0 @@
1
- import { interpretStatement } from "../interpreter.js";
2
- import { evaluate } from "../utils/evaluate.js";
3
-
4
- export async function interpretIf(statement, env) {
5
- if (evaluate(statement.condition, env)) {
6
- if (await interpretStatement(statement.consequent, env)) return true;
7
- } else if (statement.alternate) {
8
- if (await interpretStatement(statement.alternate, env)) return true;
9
- }
10
- }
@@ -1,10 +0,0 @@
1
- import { interpretStatement } from "../interpreter.js";
2
-
3
- export async function interpretTryCatch(statement, env) {
4
- try {
5
- await interpretStatement(statement.tryBlock, env);
6
- } catch (error) {
7
- env[statement.error] = error;
8
- await interpretStatement(statement.catchBlock, env);
9
- }
10
- }
@@ -1,8 +0,0 @@
1
- import { interpretStatement } from "../interpreter.js";
2
- import { evaluate } from "../utils/evaluate.js";
3
-
4
- export async function interpretWhile(statement, env) {
5
- while (evaluate(statement.condition, env)) {
6
- if (await interpretStatement(statement.body, env)) return true;
7
- }
8
- }
@@ -1,11 +0,0 @@
1
- import { interpret } from "../interpreter.js";
2
-
3
- export function createFunction(params, body, env) {
4
- return async function (...args) {
5
- const newEnv = { ...env };
6
- params.forEach((param, index) => {
7
- newEnv[param] = args[index] ?? undefined;
8
- });
9
- return (await interpret(body, newEnv))["return"];
10
- };
11
- }
@@ -1,20 +0,0 @@
1
- import { operate } from "./operate.js";
2
-
3
- export const evaluate = (expression, env) =>
4
- expression.type === "literal"
5
- ? expression.value
6
- : expression.type === "variable"
7
- ? env.hasOwnProperty(expression.name)
8
- ? env[expression.name]
9
- : null
10
- : expression.type === "list"
11
- ? expression.elements.map((element) => evaluate(element, env))
12
- : expression.type === "indexAccess"
13
- ? env[expression.name][evaluate(expression.index, env)]
14
- : expression.type === "binary"
15
- ? operate(
16
- expression.operator,
17
- evaluate(expression.left, env),
18
- evaluate(expression.right, env)
19
- )
20
- : null;
@@ -1,23 +0,0 @@
1
- const operations = new Map([
2
- ["+", (left, right) => left + right],
3
- ["-", (left, right) => left - right],
4
- ["*", (left, right) => left * right],
5
- ["/", (left, right) => left / right],
6
- ["%", (left, right) => left % right],
7
- ["**", (left, right) => left ** right],
8
- [">", (left, right) => left > right],
9
- ["<", (left, right) => left < right],
10
- [">=", (left, right) => left >= right],
11
- ["<=", (left, right) => left <= right],
12
- ["==", (left, right) => left == right],
13
- ["!=", (left, right) => left != right],
14
- ["!", (left) => !left],
15
- ]);
16
-
17
- export const operate = (operator, left, right) => {
18
- const operation = operations.get(operator);
19
- if (!operation) {
20
- throw new Error(`Invalid operator: ${operator}`);
21
- }
22
- return operation(left, right);
23
- };