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,161 @@
|
|
|
1
|
+
import { BuiltinFunction } from "../BuiltinFunction.js";
|
|
2
|
+
|
|
3
|
+
// --- Helper Functions ---
|
|
4
|
+
function expectString(arg, funcName, interpreter, callNode, argPosition = 1) {
|
|
5
|
+
if (typeof arg !== "string") {
|
|
6
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
7
|
+
`${funcName}() expects a string path as argument ${argPosition}. Got '${typeof arg}'.`,
|
|
8
|
+
callNode,
|
|
9
|
+
"TYPE001",
|
|
10
|
+
'Provide a string path (e.g., "./my_file.txt").'
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// --- BuiltinFunction Definitions ---
|
|
16
|
+
|
|
17
|
+
const fsReadFile = new BuiltinFunction(
|
|
18
|
+
"read_file",
|
|
19
|
+
([filePath], interpreter, callNode) => {
|
|
20
|
+
expectString(filePath, "fs.read_file", interpreter, callNode);
|
|
21
|
+
try {
|
|
22
|
+
return interpreter.adapter.readFileSync(filePath, "utf-8");
|
|
23
|
+
} catch (e) {
|
|
24
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
25
|
+
`Failed to read file '${filePath}': ${e.message}`,
|
|
26
|
+
callNode,
|
|
27
|
+
"FS001",
|
|
28
|
+
"Ensure the file exists and you have read permissions."
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
1
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const fsWriteFile = new BuiltinFunction(
|
|
36
|
+
"write_file",
|
|
37
|
+
([filePath, content], interpreter, callNode) => {
|
|
38
|
+
expectString(filePath, "fs.write_file", interpreter, callNode, 1);
|
|
39
|
+
if (typeof content !== "string") {
|
|
40
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
41
|
+
"fs.write_file() expects string content as its second argument.",
|
|
42
|
+
callNode,
|
|
43
|
+
"TYPE001",
|
|
44
|
+
"Provide the string content to write to the file."
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
interpreter.adapter.writeFileSync(filePath, content, "utf-8");
|
|
49
|
+
return null;
|
|
50
|
+
} catch (e) {
|
|
51
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
52
|
+
`Failed to write to file '${filePath}': ${e.message}`,
|
|
53
|
+
callNode,
|
|
54
|
+
"FS002",
|
|
55
|
+
"Ensure you have write permissions for the directory."
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
2
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const fsFileExists = new BuiltinFunction(
|
|
63
|
+
"exists",
|
|
64
|
+
([filePath], interpreter, callNode) => {
|
|
65
|
+
expectString(filePath, "fs.exists", interpreter, callNode);
|
|
66
|
+
return interpreter.adapter.existsSync(filePath);
|
|
67
|
+
},
|
|
68
|
+
1
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const fsListDir = new BuiltinFunction(
|
|
72
|
+
"list_dir",
|
|
73
|
+
([dirPath], interpreter, callNode) => {
|
|
74
|
+
expectString(dirPath, "fs.list_dir", interpreter, callNode);
|
|
75
|
+
try {
|
|
76
|
+
return interpreter.adapter.readdirSync(dirPath);
|
|
77
|
+
} catch (e) {
|
|
78
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
79
|
+
`Failed to list directory '${dirPath}': ${e.message}`,
|
|
80
|
+
callNode,
|
|
81
|
+
"FS003",
|
|
82
|
+
"Ensure the path is a directory and you have read permissions."
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
1
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const fsMakeDir = new BuiltinFunction(
|
|
90
|
+
"make_dir",
|
|
91
|
+
([dirPath, recursive], interpreter, callNode) => {
|
|
92
|
+
expectString(dirPath, "fs.make_dir", interpreter, callNode);
|
|
93
|
+
const recursiveOption = recursive === true; // Only `true` is recursive
|
|
94
|
+
try {
|
|
95
|
+
interpreter.adapter.mkdirSync(dirPath, { recursive: recursiveOption });
|
|
96
|
+
return true;
|
|
97
|
+
} catch (e) {
|
|
98
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
99
|
+
`Failed to create directory '${dirPath}': ${e.message}`,
|
|
100
|
+
callNode,
|
|
101
|
+
"FS004",
|
|
102
|
+
"Ensure the parent directory exists and you have write permissions."
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
[1, 2] // dirPath, [recursive: boolean]
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
const fsRemoveFile = new BuiltinFunction(
|
|
110
|
+
"remove_file",
|
|
111
|
+
([filePath], interpreter, callNode) => {
|
|
112
|
+
expectString(filePath, "fs.remove_file", interpreter, callNode);
|
|
113
|
+
try {
|
|
114
|
+
interpreter.adapter.unlinkSync(filePath);
|
|
115
|
+
return true;
|
|
116
|
+
} catch (e) {
|
|
117
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
118
|
+
`Failed to remove file '${filePath}': ${e.message}`,
|
|
119
|
+
callNode,
|
|
120
|
+
"FS005",
|
|
121
|
+
"Ensure the file exists and you have write permissions."
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
1
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
const fsRemoveDir = new BuiltinFunction(
|
|
129
|
+
"remove_dir",
|
|
130
|
+
([dirPath, recursive], interpreter, callNode) => {
|
|
131
|
+
expectString(dirPath, "fs.remove_dir", interpreter, callNode);
|
|
132
|
+
const isRecursive = recursive === true;
|
|
133
|
+
try {
|
|
134
|
+
if (isRecursive) {
|
|
135
|
+
interpreter.adapter.rmSync(dirPath, { recursive: true, force: true });
|
|
136
|
+
} else {
|
|
137
|
+
interpreter.adapter.rmdirSync(dirPath);
|
|
138
|
+
}
|
|
139
|
+
return true;
|
|
140
|
+
} catch (e) {
|
|
141
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
142
|
+
`Failed to remove directory '${dirPath}': ${e.message}`,
|
|
143
|
+
callNode,
|
|
144
|
+
"FS006",
|
|
145
|
+
"Ensure the directory exists and you have write permissions. Use the recursive option for non-empty directories."
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
[1, 2] // dirPath, [recursive: boolean]
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
// --- Module Export ---
|
|
153
|
+
export const fsModule = {
|
|
154
|
+
read_file: fsReadFile,
|
|
155
|
+
write_file: fsWriteFile,
|
|
156
|
+
exists: fsFileExists,
|
|
157
|
+
list_dir: fsListDir,
|
|
158
|
+
make_dir: fsMakeDir,
|
|
159
|
+
remove_file: fsRemoveFile,
|
|
160
|
+
remove_dir: fsRemoveDir,
|
|
161
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { BuiltinFunction } from "../BuiltinFunction.js";
|
|
2
|
+
|
|
3
|
+
// --- Helper Functions ---
|
|
4
|
+
function expectString(arg, funcName, interpreter, callNode, argPosition = 1) {
|
|
5
|
+
if (typeof arg !== "string") {
|
|
6
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
7
|
+
`${funcName}() expects a string as argument ${argPosition}. Got '${typeof arg}'.`,
|
|
8
|
+
callNode,
|
|
9
|
+
"TYPE001",
|
|
10
|
+
"Provide a URL string.",
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function expectObject(arg, funcName, interpreter, callNode, argPosition = 1) {
|
|
16
|
+
if (typeof arg !== "object" || arg === null || Array.isArray(arg)) {
|
|
17
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
18
|
+
`${funcName}() expects an object as argument ${argPosition}.`,
|
|
19
|
+
callNode,
|
|
20
|
+
"TYPE001",
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// --- BuiltinFunction Definitions ---
|
|
26
|
+
|
|
27
|
+
const httpGet = new BuiltinFunction(
|
|
28
|
+
"get",
|
|
29
|
+
(args, interpreter, callNode) => {
|
|
30
|
+
const [url] = args;
|
|
31
|
+
expectString(url, "http.get", interpreter, callNode, 1);
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
// Call the adapter's synchronous fetch
|
|
35
|
+
return interpreter.adapter.fetchSync(url, { method: "GET" });
|
|
36
|
+
} catch (e) {
|
|
37
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
38
|
+
`HTTP GET request failed: ${e.message}`,
|
|
39
|
+
callNode,
|
|
40
|
+
"HTTP001",
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
1, // Arity: 1 argument (url)
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const httpPost = new BuiltinFunction(
|
|
48
|
+
"post",
|
|
49
|
+
(args, interpreter, callNode) => {
|
|
50
|
+
const [url, body, headers] = args;
|
|
51
|
+
expectString(url, "http.post", interpreter, callNode, 1);
|
|
52
|
+
|
|
53
|
+
// Mimo allows sending string bodies directly.
|
|
54
|
+
// If the user has an object, they should use json.stringify() first.
|
|
55
|
+
if (typeof body !== "string") {
|
|
56
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
57
|
+
`http.post() body must be a string. Use json.stringify(data) first.`,
|
|
58
|
+
callNode,
|
|
59
|
+
"TYPE001",
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let requestHeaders = { "Content-Type": "application/json" };
|
|
64
|
+
|
|
65
|
+
if (headers !== undefined) {
|
|
66
|
+
expectObject(headers, "http.post", interpreter, callNode, 3);
|
|
67
|
+
// Merge defaults with user headers
|
|
68
|
+
requestHeaders = { ...requestHeaders, ...headers };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
return interpreter.adapter.fetchSync(url, {
|
|
73
|
+
method: "POST",
|
|
74
|
+
body: body,
|
|
75
|
+
headers: requestHeaders,
|
|
76
|
+
});
|
|
77
|
+
} catch (e) {
|
|
78
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
79
|
+
`HTTP POST request failed: ${e.message}`,
|
|
80
|
+
callNode,
|
|
81
|
+
"HTTP002",
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
[2, 3], // Arity: url, body, [headers]
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
// --- Module Export ---
|
|
89
|
+
export const httpModule = {
|
|
90
|
+
get: httpGet,
|
|
91
|
+
post: httpPost,
|
|
92
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { BuiltinFunction } from '../BuiltinFunction.js';
|
|
2
|
+
|
|
3
|
+
// --- Helper Functions ---
|
|
4
|
+
|
|
5
|
+
function expectString(arg, funcName, interpreter, callNode) {
|
|
6
|
+
if (typeof arg !== 'string') {
|
|
7
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
8
|
+
`${funcName}() expects a JSON string as its argument.`,
|
|
9
|
+
callNode, 'TYPE001', 'Provide a string containing valid JSON.'
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// --- BuiltinFunction Definitions ---
|
|
15
|
+
|
|
16
|
+
const jsonParse = new BuiltinFunction("parse",
|
|
17
|
+
([jsonString], interpreter, callNode) => {
|
|
18
|
+
expectString(jsonString, "json.parse", interpreter, callNode);
|
|
19
|
+
try {
|
|
20
|
+
return JSON.parse(jsonString);
|
|
21
|
+
} catch (e) {
|
|
22
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
23
|
+
`Failed to parse JSON string: ${e.message}`,
|
|
24
|
+
callNode, 'JSON001', 'Ensure the string contains valid JSON syntax.'
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
1
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const jsonStringify = new BuiltinFunction("stringify",
|
|
32
|
+
(args, interpreter, callNode) => {
|
|
33
|
+
const [value, indent] = args;
|
|
34
|
+
let indentArg = null;
|
|
35
|
+
|
|
36
|
+
if (args.length > 1) {
|
|
37
|
+
if (typeof indent !== "number" && typeof indent !== "string") {
|
|
38
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
39
|
+
"json.stringify() second argument (indent) must be a number or a string.",
|
|
40
|
+
callNode, 'TYPE001', 'Provide a number for spaces or a string for the indent characters.'
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
if (typeof indent === "number" && indent < 0) {
|
|
44
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
45
|
+
"json.stringify() indent number must be non-negative.",
|
|
46
|
+
callNode, 'ARG001', 'Provide a non-negative number for indentation.'
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
indentArg = indent;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
// The `null` replacer argument is standard for JSON.stringify
|
|
54
|
+
return JSON.stringify(value, null, indentArg);
|
|
55
|
+
} catch (e) {
|
|
56
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
57
|
+
`Failed to stringify value to JSON: ${e.message}`,
|
|
58
|
+
callNode, 'JSON001', 'Ensure the value can be serialized to JSON (e.g., no functions or circular references).'
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
[1, 2] // Arity: value, [indent]
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
// --- Module Export ---
|
|
67
|
+
export const jsonModule = {
|
|
68
|
+
parse: jsonParse,
|
|
69
|
+
stringify: jsonStringify,
|
|
70
|
+
};
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import { BuiltinFunction } from '../BuiltinFunction.js';
|
|
2
|
+
import { ErrorHandler } from '../ErrorHandler.js';
|
|
3
|
+
|
|
4
|
+
// --- Constants ---
|
|
5
|
+
const MIMO_PI = Math.PI;
|
|
6
|
+
const MIMO_E = Math.E;
|
|
7
|
+
|
|
8
|
+
// --- Seeded Randomness ---
|
|
9
|
+
let _currentSeed = null;
|
|
10
|
+
|
|
11
|
+
function seededRandom() {
|
|
12
|
+
if (_currentSeed === null) return Math.random();
|
|
13
|
+
// Park-Miller LCG
|
|
14
|
+
_currentSeed = (_currentSeed * 16807) % 2147483647;
|
|
15
|
+
return (_currentSeed - 1) / 2147483646;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
// --- Helper for type checking ---
|
|
20
|
+
// Helper for type checking --- Updated
|
|
21
|
+
function expectNumber(arg, funcName, interpreter, callNode, argPosition = 1) {
|
|
22
|
+
if (typeof arg !== 'number') {
|
|
23
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
24
|
+
`${funcName}() expects a number as argument ${argPosition}. Got '${typeof arg}'.`,
|
|
25
|
+
callNode, // Pass callNode
|
|
26
|
+
'TYPE001',
|
|
27
|
+
`Ensure argument ${argPosition} for '${funcName}' is a number.`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function expectNumbers(args, funcName, expectedCount, interpreter, callNode) {
|
|
33
|
+
if (args.length < expectedCount) {
|
|
34
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
35
|
+
`${funcName}() expects at least ${expectedCount} arguments, got ${args.length}.`,
|
|
36
|
+
callNode, // Pass callNode
|
|
37
|
+
'BUILTIN001',
|
|
38
|
+
`Provide at least ${expectedCount} arguments for '${funcName}'.`
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
for (let i = 0; i < expectedCount; i++) {
|
|
42
|
+
if (typeof args[i] !== 'number') {
|
|
43
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
44
|
+
`${funcName}() expects a number as argument ${i + 1}. Got '${typeof args[i]}'.`,
|
|
45
|
+
callNode, // Pass callNode
|
|
46
|
+
'TYPE001',
|
|
47
|
+
`Ensure argument ${i + 1} for '${funcName}' is a number.`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// --- BuiltinFunction Definitions ---
|
|
54
|
+
|
|
55
|
+
const mathSin = new BuiltinFunction("sin", (args, interpreter, callNode) => {
|
|
56
|
+
expectNumber(args[0], "sin", interpreter, callNode, 1);
|
|
57
|
+
return Math.sin(args[0]);
|
|
58
|
+
}, 1);
|
|
59
|
+
|
|
60
|
+
const mathCos = new BuiltinFunction("cos", (args, interpreter, callNode) => {
|
|
61
|
+
expectNumber(args[0], "cos", interpreter, callNode, 1);
|
|
62
|
+
return Math.cos(args[0]);
|
|
63
|
+
}, 1);
|
|
64
|
+
|
|
65
|
+
const mathTan = new BuiltinFunction("tan", (args, interpreter, callNode) => {
|
|
66
|
+
expectNumber(args[0], "tan", interpreter, callNode, 1);
|
|
67
|
+
return Math.tan(args[0]);
|
|
68
|
+
}, 1);
|
|
69
|
+
|
|
70
|
+
const mathAsin = new BuiltinFunction("asin", (args, interpreter, callNode) => {
|
|
71
|
+
expectNumber(args[0], "asin", interpreter, callNode, 1);
|
|
72
|
+
return Math.asin(args[0]);
|
|
73
|
+
}, 1);
|
|
74
|
+
|
|
75
|
+
const mathAcos = new BuiltinFunction("acos", (args, interpreter, callNode) => {
|
|
76
|
+
expectNumber(args[0], "acos", interpreter, callNode, 1);
|
|
77
|
+
return Math.acos(args[0]);
|
|
78
|
+
}, 1);
|
|
79
|
+
|
|
80
|
+
const mathAtan = new BuiltinFunction("atan", (args, interpreter, callNode) => {
|
|
81
|
+
expectNumber(args[0], "atan", interpreter, callNode, 1);
|
|
82
|
+
return Math.atan(args[0]);
|
|
83
|
+
}, 1);
|
|
84
|
+
|
|
85
|
+
const mathAtan2 = new BuiltinFunction("atan2", (args, interpreter, callNode) => {
|
|
86
|
+
expectNumbers(args, "atan2", 2, interpreter, callNode);
|
|
87
|
+
return Math.atan2(args[0], args[1]); // y, x
|
|
88
|
+
}, 2);
|
|
89
|
+
|
|
90
|
+
const mathLog = new BuiltinFunction("log", (args, interpreter, callNode) => { // Natural logarithm
|
|
91
|
+
expectNumber(args[0], "log", interpreter, callNode, 1);
|
|
92
|
+
return Math.log(args[0]);
|
|
93
|
+
}, 1);
|
|
94
|
+
|
|
95
|
+
const mathLog10 = new BuiltinFunction("log10", (args, interpreter, callNode) => {
|
|
96
|
+
expectNumber(args[0], "log10", interpreter, callNode, 1);
|
|
97
|
+
return Math.log10(args[0]);
|
|
98
|
+
}, 1);
|
|
99
|
+
|
|
100
|
+
const mathLog2 = new BuiltinFunction("log2", (args, interpreter, callNode) => {
|
|
101
|
+
expectNumber(args[0], "log2", interpreter, callNode, 1);
|
|
102
|
+
return Math.log2(args[0]);
|
|
103
|
+
}, 1);
|
|
104
|
+
|
|
105
|
+
const mathExp = new BuiltinFunction("exp", (args, interpreter, callNode) => {
|
|
106
|
+
expectNumber(args[0], "exp", interpreter, callNode, 1);
|
|
107
|
+
return Math.exp(args[0]);
|
|
108
|
+
}, 1);
|
|
109
|
+
|
|
110
|
+
const mathPow = new BuiltinFunction("pow", (args, interpreter, callNode) => {
|
|
111
|
+
expectNumbers(args, "pow", 2, interpreter, callNode);
|
|
112
|
+
return Math.pow(args[0], args[1]); // base, exponent
|
|
113
|
+
}, 2);
|
|
114
|
+
|
|
115
|
+
const mathSqrt = new BuiltinFunction("sqrt", (args, interpreter, callNode) => {
|
|
116
|
+
expectNumber(args[0], "sqrt", interpreter, callNode, 1);
|
|
117
|
+
return Math.sqrt(args[0]);
|
|
118
|
+
}, 1);
|
|
119
|
+
|
|
120
|
+
const mathCbrt = new BuiltinFunction("cbrt", (args, interpreter, callNode) => {
|
|
121
|
+
expectNumber(args[0], "cbrt", interpreter, callNode, 1);
|
|
122
|
+
return Math.cbrt(args[0]);
|
|
123
|
+
}, 1);
|
|
124
|
+
|
|
125
|
+
const mathFloor = new BuiltinFunction("floor", (args, interpreter, callNode) => {
|
|
126
|
+
expectNumber(args[0], "floor", interpreter, callNode, 1);
|
|
127
|
+
return Math.floor(args[0]);
|
|
128
|
+
}, 1);
|
|
129
|
+
|
|
130
|
+
const mathCeil = new BuiltinFunction("ceil", (args, interpreter, callNode) => {
|
|
131
|
+
expectNumber(args[0], "ceil", interpreter, callNode, 1);
|
|
132
|
+
return Math.ceil(args[0]);
|
|
133
|
+
}, 1);
|
|
134
|
+
|
|
135
|
+
const mathRound = new BuiltinFunction("round", (args, interpreter, callNode) => {
|
|
136
|
+
expectNumber(args[0], "round", interpreter, callNode, 1);
|
|
137
|
+
return Math.round(args[0]);
|
|
138
|
+
}, 1);
|
|
139
|
+
|
|
140
|
+
const mathAbs = new BuiltinFunction("abs", (args, interpreter, callNode) => {
|
|
141
|
+
expectNumber(args[0], "abs", interpreter, callNode, 1);
|
|
142
|
+
return Math.abs(args[0]);
|
|
143
|
+
}, 1);
|
|
144
|
+
|
|
145
|
+
const mathClamp = new BuiltinFunction("clamp", (args, interpreter, callNode) => {
|
|
146
|
+
expectNumbers(args, "clamp", 3, interpreter, callNode);
|
|
147
|
+
const [value, min, max] = args;
|
|
148
|
+
if (min > max) {
|
|
149
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
150
|
+
"clamp() expects min <= max.",
|
|
151
|
+
callNode,
|
|
152
|
+
"ARG001",
|
|
153
|
+
"Ensure the second argument is less than or equal to the third."
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
return Math.min(Math.max(value, min), max);
|
|
157
|
+
}, 3);
|
|
158
|
+
|
|
159
|
+
const mathLerp = new BuiltinFunction("lerp", (args, interpreter, callNode) => {
|
|
160
|
+
expectNumbers(args, "lerp", 3, interpreter, callNode);
|
|
161
|
+
const [a, b, t] = args;
|
|
162
|
+
return a + (b - a) * t;
|
|
163
|
+
}, 3);
|
|
164
|
+
|
|
165
|
+
const mathSum = new BuiltinFunction("sum", (args, interpreter, callNode) => {
|
|
166
|
+
const [arr] = args;
|
|
167
|
+
if (!Array.isArray(arr)) {
|
|
168
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
169
|
+
"sum() expects an array of numbers.",
|
|
170
|
+
callNode,
|
|
171
|
+
"TYPE001",
|
|
172
|
+
"Provide an array as the first argument to sum()."
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
arr.forEach((n, i) => expectNumber(n, "sum", interpreter, callNode, i + 1));
|
|
176
|
+
return arr.reduce((acc, value) => acc + value, 0);
|
|
177
|
+
}, 1);
|
|
178
|
+
|
|
179
|
+
const mathMean = new BuiltinFunction("mean", (args, interpreter, callNode) => {
|
|
180
|
+
const [arr] = args;
|
|
181
|
+
if (!Array.isArray(arr)) {
|
|
182
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
183
|
+
"mean() expects an array of numbers.",
|
|
184
|
+
callNode,
|
|
185
|
+
"TYPE001",
|
|
186
|
+
"Provide an array as the first argument to mean()."
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
if (arr.length === 0) {
|
|
190
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
191
|
+
"mean() cannot be called on an empty array.",
|
|
192
|
+
callNode,
|
|
193
|
+
"ARG001",
|
|
194
|
+
"Provide a non-empty array to mean()."
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
arr.forEach((n, i) => expectNumber(n, "mean", interpreter, callNode, i + 1));
|
|
198
|
+
return arr.reduce((acc, value) => acc + value, 0) / arr.length;
|
|
199
|
+
}, 1);
|
|
200
|
+
|
|
201
|
+
// min/max support array argument and variadic numbers
|
|
202
|
+
const mathMax = new BuiltinFunction("max", (args, interpreter, callNode) => {
|
|
203
|
+
if (args.length === 0) {
|
|
204
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
205
|
+
"max() expects at least one number.",
|
|
206
|
+
callNode,
|
|
207
|
+
'BUILTIN001',
|
|
208
|
+
"Provide at least one number to max()."
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
if (args.length === 1 && Array.isArray(args[0])) {
|
|
212
|
+
const arr = args[0];
|
|
213
|
+
if (arr.length === 0) {
|
|
214
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
215
|
+
"max() cannot be called on an empty array.",
|
|
216
|
+
callNode,
|
|
217
|
+
"ARG001",
|
|
218
|
+
"Provide a non-empty array to max()."
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
arr.forEach((arg, i) => expectNumber(arg, "max", interpreter, callNode, i + 1));
|
|
222
|
+
return Math.max(...arr);
|
|
223
|
+
}
|
|
224
|
+
args.forEach((arg, i) => expectNumber(arg, "max", interpreter, callNode, i + 1));
|
|
225
|
+
return Math.max(...args);
|
|
226
|
+
}, [1, Infinity]); // Arity: 1 to many
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
const mathMin = new BuiltinFunction("min", (args, interpreter, callNode) => {
|
|
231
|
+
if (args.length === 0) {
|
|
232
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
233
|
+
"min() expects at least one number.",
|
|
234
|
+
callNode,
|
|
235
|
+
'BUILTIN001',
|
|
236
|
+
"Provide at least one number to min()."
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
if (args.length === 1 && Array.isArray(args[0])) {
|
|
240
|
+
const arr = args[0];
|
|
241
|
+
if (arr.length === 0) {
|
|
242
|
+
throw interpreter.errorHandler.createRuntimeError(
|
|
243
|
+
"min() cannot be called on an empty array.",
|
|
244
|
+
callNode,
|
|
245
|
+
"ARG001",
|
|
246
|
+
"Provide a non-empty array to min()."
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
arr.forEach((arg, i) => expectNumber(arg, "min", interpreter, callNode, i + 1));
|
|
250
|
+
return Math.min(...arr);
|
|
251
|
+
}
|
|
252
|
+
args.forEach((arg, i) => expectNumber(arg, "min", interpreter, callNode, i + 1));
|
|
253
|
+
return Math.min(...args);
|
|
254
|
+
}, [1, Infinity]); // Arity: 1 to many
|
|
255
|
+
|
|
256
|
+
// random
|
|
257
|
+
const mathRandom = new BuiltinFunction("random", (args, interpreter, callNode) => {
|
|
258
|
+
return seededRandom();
|
|
259
|
+
}, 0); // No arguments, returns a random number between 0 and 1
|
|
260
|
+
|
|
261
|
+
const mathSeed = new BuiltinFunction("seed", (args, interpreter, callNode) => {
|
|
262
|
+
expectNumber(args[0], "seed", interpreter, callNode, 1);
|
|
263
|
+
_currentSeed = Math.abs(Math.floor(args[0])) % 2147483647;
|
|
264
|
+
if (_currentSeed === 0) _currentSeed = 1; // Seed cannot be 0 for this LCG
|
|
265
|
+
return null;
|
|
266
|
+
}, 1);
|
|
267
|
+
|
|
268
|
+
const mathRandInt = new BuiltinFunction("randint", (args, interpreter, callNode) => {
|
|
269
|
+
expectNumbers(args, "randint", 2, interpreter, callNode);
|
|
270
|
+
const min = Math.ceil(args[0]);
|
|
271
|
+
const max = Math.floor(args[1]);
|
|
272
|
+
return Math.floor(seededRandom() * (max - min + 1)) + min;
|
|
273
|
+
}, 2);
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
// --- Export the module's contents ---
|
|
279
|
+
export const mathModuleExports = {
|
|
280
|
+
PI: MIMO_PI,
|
|
281
|
+
E: MIMO_E,
|
|
282
|
+
sin: mathSin,
|
|
283
|
+
cos: mathCos,
|
|
284
|
+
tan: mathTan,
|
|
285
|
+
asin: mathAsin,
|
|
286
|
+
acos: mathAcos,
|
|
287
|
+
atan: mathAtan,
|
|
288
|
+
atan2: mathAtan2,
|
|
289
|
+
log: mathLog,
|
|
290
|
+
log10: mathLog10,
|
|
291
|
+
log2: mathLog2,
|
|
292
|
+
exp: mathExp,
|
|
293
|
+
pow: mathPow,
|
|
294
|
+
sqrt: mathSqrt,
|
|
295
|
+
cbrt: mathCbrt,
|
|
296
|
+
floor: mathFloor,
|
|
297
|
+
ceil: mathCeil,
|
|
298
|
+
round: mathRound,
|
|
299
|
+
abs: mathAbs,
|
|
300
|
+
clamp: mathClamp,
|
|
301
|
+
lerp: mathLerp,
|
|
302
|
+
sum: mathSum,
|
|
303
|
+
mean: mathMean,
|
|
304
|
+
max: mathMax,
|
|
305
|
+
min: mathMin,
|
|
306
|
+
random: mathRandom,
|
|
307
|
+
seed: mathSeed,
|
|
308
|
+
randint: mathRandInt,
|
|
309
|
+
};
|