mimo-lang 1.1.0 → 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 +91 -6
- 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 +49 -39
- 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 -11
- 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 -1
- 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 -63
- 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 -35
- package/compiler/parser/parser.js +0 -16
- 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/i.js +0 -30
- package/jsconfig.json +0 -27
- package/webpack.config.js +0 -9
package/.gitattributes
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Auto detect text files and perform LF normalization
|
|
2
|
+
* text=auto
|
|
3
|
+
|
|
4
|
+
# Source code
|
|
5
|
+
*.js text eol=lf
|
|
6
|
+
*.mimo text eol=lf linguist-language=Mimo
|
|
7
|
+
|
|
8
|
+
# Config files
|
|
9
|
+
*.json text eol=lf
|
|
10
|
+
*.md text eol=lf
|
|
11
|
+
*.yml text eol=lf
|
|
12
|
+
*.yaml text eol=lf
|
|
13
|
+
|
|
14
|
+
# Shell scripts
|
|
15
|
+
*.sh text eol=lf
|
|
16
|
+
|
|
17
|
+
# Binary files
|
|
18
|
+
*.png binary
|
|
19
|
+
*.jpg binary
|
|
20
|
+
*.jpeg binary
|
|
21
|
+
*.gif binary
|
|
22
|
+
*.ico binary
|
|
23
|
+
*.woff binary
|
|
24
|
+
*.woff2 binary
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 bethropolis
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,15 +1,100 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="playground/src/lib/assets/mascot.png" alt="Mimo mascot" width="120">
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://www.npmjs.com/package/mimo-lang">
|
|
7
|
+
<img src="https://img.shields.io/npm/v/mimo-lang?color=%23a855f7&label=npm&logo=npm" alt="npm version">
|
|
8
|
+
</a>
|
|
9
|
+
<a href="https://github.com/bethropolis/mimo/releases">
|
|
10
|
+
<img src="https://img.shields.io/github/v/release/bethropolis/mimo?color=%236366f1&label=github&logo=github" alt="GitHub release">
|
|
11
|
+
</a>
|
|
12
|
+
<a href="https://github.com/bethropolis/mimo/blob/main/LICENSE">
|
|
13
|
+
<img src="https://img.shields.io/github/license/bethropolis/mimo?color=%2310b981" alt="License">
|
|
14
|
+
</a>
|
|
15
|
+
<a href="https://bethropolis.github.io/mimo/">
|
|
16
|
+
<img src="https://img.shields.io/badge/docs-online-%23f59e0b?logo=book" alt="Documentation">
|
|
17
|
+
</a>
|
|
18
|
+
</p>
|
|
19
|
+
|
|
20
|
+
<p align="center">
|
|
21
|
+
<strong>A minimal prefix-notation programming language</strong>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
4
27
|
|
|
5
28
|
```bash
|
|
6
|
-
bun install
|
|
29
|
+
bun install -g mimo-lang
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
### Run Programs
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
mimo path/to/program.mimo
|
|
38
|
+
mimo -e "+ 1 2"
|
|
39
|
+
echo "+ 1 2" | mimo
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### REPL
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
mimo repl
|
|
7
46
|
```
|
|
8
47
|
|
|
9
|
-
|
|
48
|
+
### CLI Commands
|
|
10
49
|
|
|
11
50
|
```bash
|
|
12
|
-
|
|
51
|
+
mimo run <file> # Run a file
|
|
52
|
+
mimo repl # Start REPL
|
|
53
|
+
mimo fmt <path> --write # Format files
|
|
54
|
+
mimo lint <path> # Lint files
|
|
55
|
+
mimo test <path> # Run tests
|
|
13
56
|
```
|
|
14
57
|
|
|
15
|
-
|
|
58
|
+
## Syntax
|
|
59
|
+
|
|
60
|
+
```mimo
|
|
61
|
+
function add(a, b)
|
|
62
|
+
return + a b
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
call add(4, 8) -> result
|
|
66
|
+
show result
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
More examples in [`test/source/`](./test/source).
|
|
70
|
+
|
|
71
|
+
## Development
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
bun install
|
|
75
|
+
bun run check
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Playground
|
|
79
|
+
|
|
80
|
+
<p>
|
|
81
|
+
<img src="docs/assets/playground.jpg" alt="Mimo playground" width="600">
|
|
82
|
+
</p>
|
|
83
|
+
|
|
84
|
+
The playground is a web application that allows you to run Mimo code in your browser.
|
|
85
|
+
|
|
86
|
+
## About
|
|
87
|
+
this is just a simple language i created to learn more about how programing languages work.
|
|
88
|
+
contributions are welcome.
|
|
89
|
+
|
|
90
|
+
## Links
|
|
91
|
+
|
|
92
|
+
- [Documentation](https://bethropolis.github.io/mimo/)
|
|
93
|
+
- [Playground](https://bethropolis.github.io/mimo/playground)
|
|
94
|
+
- [GitHub](https://github.com/bethropolis/mimo)
|
|
95
|
+
|
|
96
|
+
## License
|
|
97
|
+
|
|
98
|
+
Released under [MIT](./LICENSE)
|
|
99
|
+
|
|
100
|
+
<p align="center">happy coding 💜</p>
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file An adapter for running Mimo in a web browser environment.
|
|
3
|
+
* It provides browser-compatible implementations for system interactions.
|
|
4
|
+
* File system operations are disabled and will throw errors.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
function fsUnavailable() {
|
|
8
|
+
throw new Error(
|
|
9
|
+
"File system access is not available in the browser environment.",
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const baseBrowserAdapter = {
|
|
14
|
+
// --- File System (Disabled by default) ---
|
|
15
|
+
readFileSync: fsUnavailable,
|
|
16
|
+
readdirSync: fsUnavailable,
|
|
17
|
+
existsSync: () => false, // Always return false, as no real file system exists
|
|
18
|
+
writeFileSync: fsUnavailable,
|
|
19
|
+
mkdirSync: fsUnavailable,
|
|
20
|
+
unlinkSync: fsUnavailable,
|
|
21
|
+
rmdirSync: fsUnavailable,
|
|
22
|
+
rmSync: fsUnavailable,
|
|
23
|
+
|
|
24
|
+
// --- Path (Simplified for URL/string-based paths) ---
|
|
25
|
+
resolvePath: (...segments) => segments.join("/").replace(/\/+/g, "/"),
|
|
26
|
+
dirname: (filePath) => {
|
|
27
|
+
const lastSlash = filePath.lastIndexOf("/");
|
|
28
|
+
return lastSlash === -1 ? "." : filePath.substring(0, lastSlash);
|
|
29
|
+
},
|
|
30
|
+
isAbsolutePath: (filePath) => filePath.startsWith("/"),
|
|
31
|
+
joinPath: (...segments) => segments.join("/"),
|
|
32
|
+
basename: (filePath) => {
|
|
33
|
+
const lastSlash = filePath.lastIndexOf("/");
|
|
34
|
+
return filePath.substring(lastSlash + 1);
|
|
35
|
+
},
|
|
36
|
+
extname: (filePath) => {
|
|
37
|
+
const base = filePath.substring(filePath.lastIndexOf("/") + 1);
|
|
38
|
+
const dot = base.lastIndexOf(".");
|
|
39
|
+
return dot > 0 ? base.substring(dot) : "";
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
// http
|
|
43
|
+
fetchSync: (url, options = {}) => {
|
|
44
|
+
try {
|
|
45
|
+
const xhr = new XMLHttpRequest();
|
|
46
|
+
// The 3rd argument 'false' makes the request Synchronous
|
|
47
|
+
xhr.open(options.method || "GET", url, false);
|
|
48
|
+
|
|
49
|
+
if (options.headers) {
|
|
50
|
+
for (const [key, value] of Object.entries(options.headers)) {
|
|
51
|
+
xhr.setRequestHeader(key, value);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
xhr.send(options.body || null);
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
status: xhr.status,
|
|
59
|
+
body: xhr.responseText,
|
|
60
|
+
};
|
|
61
|
+
} catch (e) {
|
|
62
|
+
throw new Error("HTTP request failed: " + e.message);
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
// --- Process (Mocked) ---
|
|
67
|
+
getArguments: () => [], // No command-line arguments in the browser
|
|
68
|
+
getEnvVariable: () => null, // No environment variables
|
|
69
|
+
getEnvAll: () => ({}),
|
|
70
|
+
exit: (code) => {
|
|
71
|
+
console.warn(
|
|
72
|
+
`Mimo script called exit(${code}), but exit is disabled in the browser.`,
|
|
73
|
+
);
|
|
74
|
+
},
|
|
75
|
+
cwd: () => "/", // The "root" in a browser context
|
|
76
|
+
|
|
77
|
+
// --- Console (Direct Mapping) ---
|
|
78
|
+
log: (...args) => console.log(...args),
|
|
79
|
+
error: (...args) => console.error(...args),
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export function createBrowserAdapter(overrides = {}) {
|
|
83
|
+
return { ...baseBrowserAdapter, ...overrides };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export const browserAdapter = createBrowserAdapter();
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import process from "node:process";
|
|
5
|
+
export const nodeAdapter = {
|
|
6
|
+
readFileSync: (filePath, encoding = "utf-8") =>
|
|
7
|
+
fs.readFileSync(filePath, encoding),
|
|
8
|
+
|
|
9
|
+
readdirSync: (filePath) => fs.readdirSync(filePath),
|
|
10
|
+
|
|
11
|
+
existsSync: (filePath) => fs.existsSync(filePath),
|
|
12
|
+
|
|
13
|
+
writeFileSync: (filePath, data, encoding = "utf-8") =>
|
|
14
|
+
fs.writeFileSync(filePath, data, encoding),
|
|
15
|
+
|
|
16
|
+
mkdirSync: (filePath, options) => {
|
|
17
|
+
if (!fs.existsSync(filePath)) {
|
|
18
|
+
fs.mkdirSync(filePath, options);
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
unlinkSync: (filePath) => fs.unlinkSync(filePath),
|
|
22
|
+
rmdirSync: (filePath) => fs.rmdirSync(filePath),
|
|
23
|
+
|
|
24
|
+
rmSync: (filePath, options) => fs.rmSync(filePath, options),
|
|
25
|
+
|
|
26
|
+
resolvePath: (...segments) => path.resolve(...segments),
|
|
27
|
+
|
|
28
|
+
dirname: (filePath) => path.dirname(filePath),
|
|
29
|
+
|
|
30
|
+
isAbsolutePath: (filePath) => path.isAbsolute(filePath),
|
|
31
|
+
|
|
32
|
+
joinPath: (...segments) => path.join(...segments),
|
|
33
|
+
|
|
34
|
+
basename: (filePath, ext) => path.basename(filePath, ext),
|
|
35
|
+
extname: (filePath) => path.extname(filePath),
|
|
36
|
+
|
|
37
|
+
getArguments: () => process.argv.slice(2),
|
|
38
|
+
|
|
39
|
+
getEnvVariable: (variableName) => process.env[variableName],
|
|
40
|
+
getEnvAll: () => ({ ...process.env }),
|
|
41
|
+
|
|
42
|
+
exit: (code) => process.exit(code),
|
|
43
|
+
|
|
44
|
+
cwd: () => process.cwd(),
|
|
45
|
+
|
|
46
|
+
log: (...args) => console.log(...args),
|
|
47
|
+
error: (...args) => console.error(...args),
|
|
48
|
+
|
|
49
|
+
// --- HTTP Implementation ---
|
|
50
|
+
fetchSync: (url, options = {}) => {
|
|
51
|
+
const method = options.method || "GET";
|
|
52
|
+
const headers = options.headers || {};
|
|
53
|
+
const body = options.body || null;
|
|
54
|
+
|
|
55
|
+
const args = ["-s", "-i", "-X", method];
|
|
56
|
+
|
|
57
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
58
|
+
args.push("-H", `${key}: ${value}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (body) {
|
|
62
|
+
args.push("-d", body);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
args.push(url);
|
|
66
|
+
|
|
67
|
+
const result = spawnSync("curl", args, { encoding: "utf-8" });
|
|
68
|
+
|
|
69
|
+
if (result.error) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
`HTTP Request failed: ${result.error.message}. Ensure 'curl' is installed.`,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (result.status !== 0) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`HTTP Connection failed: ${result.stderr || "Unknown error"}`,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const output = result.stdout;
|
|
82
|
+
const separator = "\r\n\r\n";
|
|
83
|
+
let splitIndex = output.indexOf(separator);
|
|
84
|
+
if (splitIndex === -1) splitIndex = output.indexOf("\n\n");
|
|
85
|
+
|
|
86
|
+
const headerPart = splitIndex !== -1 ? output.substring(0, splitIndex) : "";
|
|
87
|
+
const bodyPart =
|
|
88
|
+
splitIndex !== -1
|
|
89
|
+
? output.substring(splitIndex + separator.length)
|
|
90
|
+
: output;
|
|
91
|
+
|
|
92
|
+
const statusLine = headerPart.split("\n")[0] || "";
|
|
93
|
+
const statusMatch = statusLine.match(/HTTP\/\d(\.\d)?\s+(\d+)/);
|
|
94
|
+
const statusCode = statusMatch ? parseInt(statusMatch[2], 10) : 0;
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
status: statusCode,
|
|
98
|
+
body: bodyPart,
|
|
99
|
+
};
|
|
100
|
+
},
|
|
101
|
+
};
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// bin/cli.js — Mimo Language Toolkit entry point
|
|
3
|
+
// This file is intentionally thin: it parses the top-level command and
|
|
4
|
+
// delegates to the appropriate module in bin/commands/.
|
|
5
|
+
|
|
6
|
+
import { c } from './utils/colors.js';
|
|
7
|
+
import { getVersion } from './utils/version.js';
|
|
8
|
+
|
|
9
|
+
// ── Command registry ──────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
const COMMANDS = {
|
|
12
|
+
help: () => import('./commands/help.js'),
|
|
13
|
+
run: () => import('./commands/run.js'),
|
|
14
|
+
repl: () => import('./commands/repl.js'),
|
|
15
|
+
fmt: () => import('./commands/fmt.js'),
|
|
16
|
+
lint: () => import('./commands/lint.js'),
|
|
17
|
+
test: () => import('./commands/test.js'),
|
|
18
|
+
convert: () => import('./commands/convert.js'),
|
|
19
|
+
doctor: () => import('./commands/doctor.js'),
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// ── Main ──────────────────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
async function main() {
|
|
25
|
+
const args = process.argv.slice(2);
|
|
26
|
+
const command = args[0];
|
|
27
|
+
const rest = args.slice(1);
|
|
28
|
+
|
|
29
|
+
// No args → global help
|
|
30
|
+
if (!command) {
|
|
31
|
+
const { run } = await import('./commands/help.js');
|
|
32
|
+
await run([]);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Version
|
|
37
|
+
if (command === '--version' || command === '-v') {
|
|
38
|
+
console.log(getVersion());
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Help flags → same as `mimo help`
|
|
43
|
+
if (command === '--help' || command === '-h' || command === 'help') {
|
|
44
|
+
const { run } = await import('./commands/help.js');
|
|
45
|
+
await run(rest);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// --eval / -e
|
|
50
|
+
if (command === '--eval' || command === '-e') {
|
|
51
|
+
const { run, help } = await import('./commands/eval.js');
|
|
52
|
+
if (rest.includes('--help')) { help(); return; }
|
|
53
|
+
await run(rest);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// STDIN shorthand: `mimo -`
|
|
58
|
+
if (command === '-') {
|
|
59
|
+
const { run } = await import('./commands/run.js');
|
|
60
|
+
await run(['-']);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Named commands
|
|
65
|
+
if (command in COMMANDS) {
|
|
66
|
+
const { run, help } = await COMMANDS[command]();
|
|
67
|
+
if (rest.includes('--help')) { help(); return; }
|
|
68
|
+
await run(rest);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Default: treat the argument as a file path
|
|
73
|
+
const { run } = await import('./commands/run.js');
|
|
74
|
+
await run(args);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
main().catch((err) => {
|
|
78
|
+
console.error(c.error('Unexpected error: ') + (err?.message ?? String(err)));
|
|
79
|
+
process.exit(1);
|
|
80
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// bin/commands/convert.js
|
|
2
|
+
// Converts Mimo source to another language (JavaScript, Python, Alya, …).
|
|
3
|
+
|
|
4
|
+
import { runConverter } from '../../tools/convert.js';
|
|
5
|
+
import { c } from '../utils/colors.js';
|
|
6
|
+
|
|
7
|
+
export function help() {
|
|
8
|
+
console.log(`
|
|
9
|
+
${c.bold('mimo convert')} — Transpile Mimo code to another language
|
|
10
|
+
|
|
11
|
+
${c.bold('Usage:')}
|
|
12
|
+
mimo convert --in <file> --out <file> --to <target>
|
|
13
|
+
|
|
14
|
+
${c.bold('Options:')}
|
|
15
|
+
--in <file> Input .mimo file
|
|
16
|
+
--out <file> Output file path
|
|
17
|
+
--to <target> Target language: javascript, python, alya
|
|
18
|
+
|
|
19
|
+
${c.bold('Examples:')}
|
|
20
|
+
mimo convert --in app.mimo --out app.js --to javascript
|
|
21
|
+
mimo convert --in app.mimo --out app.py --to python
|
|
22
|
+
`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function run(args) {
|
|
26
|
+
await runConverter(args);
|
|
27
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// bin/commands/doctor.js
|
|
2
|
+
// Validates the Mimo runtime environment, adapter API, and stdlib availability.
|
|
3
|
+
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { spawnSync } from 'node:child_process';
|
|
7
|
+
import { Mimo } from '../../index.js';
|
|
8
|
+
import { nodeAdapter } from '../../adapters/nodeAdapter.js';
|
|
9
|
+
import { c } from '../utils/colors.js';
|
|
10
|
+
|
|
11
|
+
const REQUIRED_ADAPTER_METHODS = [
|
|
12
|
+
'readFileSync',
|
|
13
|
+
'writeFileSync',
|
|
14
|
+
'existsSync',
|
|
15
|
+
'dirname',
|
|
16
|
+
'resolvePath',
|
|
17
|
+
'joinPath',
|
|
18
|
+
'basename',
|
|
19
|
+
'extname',
|
|
20
|
+
'getEnvVariable',
|
|
21
|
+
'getEnvAll',
|
|
22
|
+
'fetchSync',
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
const STDLIB_MODULES = [
|
|
26
|
+
'array', 'assert', 'datetime', 'env', 'fs',
|
|
27
|
+
'http', 'json', 'math', 'object', 'path', 'regex', 'string',
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
export function help() {
|
|
31
|
+
console.log(`
|
|
32
|
+
${c.bold('mimo doctor')} — Validate the Mimo runtime environment
|
|
33
|
+
|
|
34
|
+
${c.bold('Usage:')}
|
|
35
|
+
mimo doctor
|
|
36
|
+
|
|
37
|
+
${c.bold('Checks performed:')}
|
|
38
|
+
- Node adapter API completeness
|
|
39
|
+
- Bun runtime detection
|
|
40
|
+
- Filesystem read/write access in current directory
|
|
41
|
+
- curl availability (required by the http stdlib module)
|
|
42
|
+
- Interpreter smoke test (evaluates \`+ 1 2\`)
|
|
43
|
+
- Stdlib module registry (all built-in modules resolve)
|
|
44
|
+
`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function run(_args) {
|
|
48
|
+
const checks = [];
|
|
49
|
+
const pass = (name, detail) => checks.push({ status: 'PASS', name, detail });
|
|
50
|
+
const warn = (name, detail) => checks.push({ status: 'WARN', name, detail });
|
|
51
|
+
const fail = (name, detail) => checks.push({ status: 'FAIL', name, detail });
|
|
52
|
+
|
|
53
|
+
// 1. Adapter API
|
|
54
|
+
const missing = REQUIRED_ADAPTER_METHODS.filter((m) => typeof nodeAdapter[m] !== 'function');
|
|
55
|
+
if (missing.length === 0) {
|
|
56
|
+
pass('Adapter API', 'All required adapter methods are present.');
|
|
57
|
+
} else {
|
|
58
|
+
fail('Adapter API', `Missing: ${missing.join(', ')}. Add them to adapters/nodeAdapter.js.`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 2. Bun runtime
|
|
62
|
+
if (process.versions?.bun) {
|
|
63
|
+
pass('Bun runtime', `Detected Bun ${process.versions.bun}.`);
|
|
64
|
+
} else {
|
|
65
|
+
warn('Bun runtime', 'Bun version could not be detected.');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 3. Filesystem access
|
|
69
|
+
try {
|
|
70
|
+
const probe = path.join(process.cwd(), '.mimo_doctor_tmp');
|
|
71
|
+
fs.writeFileSync(probe, 'ok', 'utf-8');
|
|
72
|
+
fs.unlinkSync(probe);
|
|
73
|
+
pass('Filesystem access', 'Read/write operations in current directory work.');
|
|
74
|
+
} catch (err) {
|
|
75
|
+
fail('Filesystem access', `Failed RW check in cwd: ${err.message}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 4. curl (needed by http stdlib)
|
|
79
|
+
{
|
|
80
|
+
const curl = spawnSync('curl', ['--version'], { encoding: 'utf-8' });
|
|
81
|
+
if (curl.status === 0) {
|
|
82
|
+
pass('HTTP dependency', 'curl is installed (required by http module).');
|
|
83
|
+
} else {
|
|
84
|
+
warn('HTTP dependency', 'curl not found — `http.get/post` will fail until curl is installed.');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 5. Interpreter smoke test
|
|
89
|
+
{
|
|
90
|
+
const mimo = new Mimo(nodeAdapter);
|
|
91
|
+
try {
|
|
92
|
+
const result = mimo.run('+ 1 2', '/doctor_smoke.mimo');
|
|
93
|
+
pass('Interpreter smoke test', `Basic evaluation works (result: ${result}).`);
|
|
94
|
+
} catch (err) {
|
|
95
|
+
fail('Interpreter smoke test', String(err));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// 6. Stdlib module registry
|
|
100
|
+
{
|
|
101
|
+
const mimo = new Mimo(nodeAdapter);
|
|
102
|
+
const unresolved = [];
|
|
103
|
+
for (const mod of STDLIB_MODULES) {
|
|
104
|
+
try {
|
|
105
|
+
mimo.interpreter.moduleLoader.loadModule(mod, process.cwd());
|
|
106
|
+
} catch {
|
|
107
|
+
unresolved.push(mod);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (unresolved.length === 0) {
|
|
111
|
+
pass('Stdlib module registry', 'All built-in stdlib modules resolved.');
|
|
112
|
+
} else {
|
|
113
|
+
fail('Stdlib module registry', `Failed to resolve: ${unresolved.join(', ')}.`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ── Report ────────────────────────────────────────────────────────────────
|
|
118
|
+
|
|
119
|
+
const statusColor = { PASS: c.green, WARN: c.yellow, FAIL: c.red };
|
|
120
|
+
|
|
121
|
+
console.log(`\n${c.bold('Mimo Doctor Report')} ${c.dim(`(${new Date().toISOString()})`)}\n`);
|
|
122
|
+
for (const item of checks) {
|
|
123
|
+
const badge = statusColor[item.status](`[${item.status}]`);
|
|
124
|
+
console.log(`${badge} ${c.bold(item.name)}: ${item.detail}`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const hasFail = checks.some((ch) => ch.status === 'FAIL');
|
|
128
|
+
const hasWarn = checks.some((ch) => ch.status === 'WARN');
|
|
129
|
+
|
|
130
|
+
if (hasFail) {
|
|
131
|
+
console.log(`\n${c.red(c.bold('Doctor result: FAILED'))}`);
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
if (hasWarn) {
|
|
135
|
+
console.log(`\n${c.yellow(c.bold('Doctor result: PASS WITH WARNINGS'))}`);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
console.log(`\n${c.green(c.bold('Doctor result: PASS'))}`);
|
|
139
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// bin/commands/eval.js
|
|
2
|
+
// Evaluates a string of Mimo code passed directly on the command line.
|
|
3
|
+
|
|
4
|
+
import { Mimo } from '../../index.js';
|
|
5
|
+
import { nodeAdapter } from '../../adapters/nodeAdapter.js';
|
|
6
|
+
import { formatError } from '../utils/formatError.js';
|
|
7
|
+
import { c } from '../utils/colors.js';
|
|
8
|
+
|
|
9
|
+
export function help() {
|
|
10
|
+
console.log(`
|
|
11
|
+
${c.bold('mimo --eval')} — Evaluate a string of Mimo code
|
|
12
|
+
|
|
13
|
+
${c.bold('Usage:')}
|
|
14
|
+
mimo --eval <code>
|
|
15
|
+
mimo -e <code>
|
|
16
|
+
|
|
17
|
+
${c.bold('Examples:')}
|
|
18
|
+
mimo -e "show + 1 2"
|
|
19
|
+
mimo -e 'set x 10 \\n show * x 2'
|
|
20
|
+
`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function run(args) {
|
|
24
|
+
const code = args[0];
|
|
25
|
+
if (!code) {
|
|
26
|
+
console.error(c.error('Error: No code provided to --eval.'));
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
const mimo = new Mimo(nodeAdapter);
|
|
30
|
+
try {
|
|
31
|
+
const result = mimo.run(code, '/eval');
|
|
32
|
+
if (result !== undefined && result !== null) {
|
|
33
|
+
console.log(result);
|
|
34
|
+
}
|
|
35
|
+
} catch (err) {
|
|
36
|
+
console.error(formatError(err, code));
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
}
|