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,142 @@
1
+ import { BuiltinFunction } from '../BuiltinFunction.js';
2
+ import { FunctionValue } from '../Values.js';
3
+
4
+ function expectObject(arg, funcName, interpreter, callNode, argPosition = 1) {
5
+ if (typeof arg !== 'object' || arg === null || Array.isArray(arg)) {
6
+ throw interpreter.errorHandler.createRuntimeError(
7
+ `${funcName}() expects an object as argument ${argPosition}. Got '${Array.isArray(arg) ? 'array' : typeof arg}'.`,
8
+ callNode,
9
+ 'TYPE001',
10
+ `Provide a plain object for argument ${argPosition} of ${funcName}().`
11
+ );
12
+ }
13
+ }
14
+
15
+ function expectArray(arg, funcName, interpreter, callNode, argPosition = 1) {
16
+ if (!Array.isArray(arg)) {
17
+ throw interpreter.errorHandler.createRuntimeError(
18
+ `${funcName}() expects an array as argument ${argPosition}. Got '${typeof arg}'.`,
19
+ callNode,
20
+ 'TYPE001',
21
+ `Provide an array for argument ${argPosition} of ${funcName}().`
22
+ );
23
+ }
24
+ }
25
+
26
+ function expectFunction(arg, funcName, interpreter, callNode, argPosition = 1) {
27
+ if (!(arg instanceof FunctionValue)) {
28
+ throw interpreter.errorHandler.createRuntimeError(
29
+ `${funcName}() expects a function as argument ${argPosition}. Got '${typeof arg}'.`,
30
+ callNode,
31
+ 'TYPE001',
32
+ `Provide a function for argument ${argPosition} of ${funcName}().`
33
+ );
34
+ }
35
+ }
36
+
37
+ const objectMerge = new BuiltinFunction('merge', (args, interpreter, callNode) => {
38
+ if (args.length === 0) return {};
39
+ const result = {};
40
+ args.forEach((obj, index) => {
41
+ expectObject(obj, 'merge', interpreter, callNode, index + 1);
42
+ Object.assign(result, obj);
43
+ });
44
+ return result;
45
+ }, [0, Infinity]);
46
+
47
+ const objectPick = new BuiltinFunction('pick', (args, interpreter, callNode) => {
48
+ const [obj, keys] = args;
49
+ expectObject(obj, 'pick', interpreter, callNode, 1);
50
+ expectArray(keys, 'pick', interpreter, callNode, 2);
51
+
52
+ const result = {};
53
+ keys.forEach((key, index) => {
54
+ if (typeof key !== 'string') {
55
+ throw interpreter.errorHandler.createRuntimeError(
56
+ `pick() expects key list to contain strings. Got '${typeof key}' at index ${index}.`,
57
+ callNode,
58
+ 'TYPE001'
59
+ );
60
+ }
61
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
62
+ result[key] = obj[key];
63
+ }
64
+ });
65
+ return result;
66
+ }, 2);
67
+
68
+ const objectOmit = new BuiltinFunction('omit', (args, interpreter, callNode) => {
69
+ const [obj, keys] = args;
70
+ expectObject(obj, 'omit', interpreter, callNode, 1);
71
+ expectArray(keys, 'omit', interpreter, callNode, 2);
72
+
73
+ const excluded = new Set();
74
+ keys.forEach((key, index) => {
75
+ if (typeof key !== 'string') {
76
+ throw interpreter.errorHandler.createRuntimeError(
77
+ `omit() expects key list to contain strings. Got '${typeof key}' at index ${index}.`,
78
+ callNode,
79
+ 'TYPE001'
80
+ );
81
+ }
82
+ excluded.add(key);
83
+ });
84
+
85
+ const result = {};
86
+ Object.keys(obj).forEach((key) => {
87
+ if (!excluded.has(key)) {
88
+ result[key] = obj[key];
89
+ }
90
+ });
91
+
92
+ return result;
93
+ }, 2);
94
+
95
+ const objectMapValues = new BuiltinFunction('map_values', (args, interpreter, callNode) => {
96
+ const [obj, callback] = args;
97
+ expectObject(obj, 'map_values', interpreter, callNode, 1);
98
+ expectFunction(callback, 'map_values', interpreter, callNode, 2);
99
+
100
+ const result = {};
101
+ const keys = Object.keys(obj);
102
+ for (let i = 0; i < keys.length; i++) {
103
+ const key = keys[i];
104
+ const callArgs = [obj[key], key, obj].slice(0, callback.declaration.params.length);
105
+ result[key] = callback.call(interpreter, callArgs, callNode);
106
+ }
107
+ return result;
108
+ }, 2);
109
+
110
+ const objectFromEntries = new BuiltinFunction('from_entries', (args, interpreter, callNode) => {
111
+ const [entries] = args;
112
+ expectArray(entries, 'from_entries', interpreter, callNode, 1);
113
+
114
+ const result = {};
115
+ for (let i = 0; i < entries.length; i++) {
116
+ const entry = entries[i];
117
+ if (!Array.isArray(entry) || entry.length < 2) {
118
+ throw interpreter.errorHandler.createRuntimeError(
119
+ `from_entries() expects each item to be an array [key, value]. Invalid entry at index ${i}.`,
120
+ callNode,
121
+ 'TYPE001'
122
+ );
123
+ }
124
+ result[String(entry[0])] = entry[1];
125
+ }
126
+ return result;
127
+ }, 1);
128
+
129
+ const objectIsEmpty = new BuiltinFunction('is_empty', (args, interpreter, callNode) => {
130
+ const [obj] = args;
131
+ expectObject(obj, 'is_empty', interpreter, callNode, 1);
132
+ return Object.keys(obj).length === 0;
133
+ }, 1);
134
+
135
+ export const objectModule = {
136
+ merge: objectMerge,
137
+ pick: objectPick,
138
+ omit: objectOmit,
139
+ map_values: objectMapValues,
140
+ from_entries: objectFromEntries,
141
+ is_empty: objectIsEmpty,
142
+ };
@@ -0,0 +1,69 @@
1
+ import { BuiltinFunction } from "../BuiltinFunction.js";
2
+
3
+ function expectString(arg, funcName, interpreter, callNode, argPosition = 1) {
4
+ if (typeof arg !== "string") {
5
+ throw interpreter.errorHandler.createRuntimeError(
6
+ `${funcName}() expects a string as argument ${argPosition}. Got '${typeof arg}'.`,
7
+ callNode,
8
+ "TYPE001",
9
+ "Provide a string path value."
10
+ );
11
+ }
12
+ }
13
+
14
+ const pathJoin = new BuiltinFunction(
15
+ "join",
16
+ (args, interpreter, callNode) => {
17
+ if (args.length === 0) {
18
+ throw interpreter.errorHandler.createRuntimeError(
19
+ "path.join() expects at least one argument.",
20
+ callNode,
21
+ "BUILTIN001",
22
+ "Provide one or more path segments."
23
+ );
24
+ }
25
+ args.forEach((segment, index) =>
26
+ expectString(segment, "path.join", interpreter, callNode, index + 1)
27
+ );
28
+ return interpreter.adapter.joinPath(...args);
29
+ },
30
+ [1, Infinity]
31
+ );
32
+
33
+ const pathDirname = new BuiltinFunction(
34
+ "dirname",
35
+ ([targetPath], interpreter, callNode) => {
36
+ expectString(targetPath, "path.dirname", interpreter, callNode, 1);
37
+ return interpreter.adapter.dirname(targetPath);
38
+ },
39
+ 1
40
+ );
41
+
42
+ const pathBasename = new BuiltinFunction(
43
+ "basename",
44
+ ([targetPath, ext], interpreter, callNode) => {
45
+ expectString(targetPath, "path.basename", interpreter, callNode, 1);
46
+ if (ext !== undefined) {
47
+ expectString(ext, "path.basename", interpreter, callNode, 2);
48
+ }
49
+ return interpreter.adapter.basename(targetPath, ext);
50
+ },
51
+ [1, 2]
52
+ );
53
+
54
+ const pathExtname = new BuiltinFunction(
55
+ "extname",
56
+ ([targetPath], interpreter, callNode) => {
57
+ expectString(targetPath, "path.extname", interpreter, callNode, 1);
58
+ return interpreter.adapter.extname(targetPath);
59
+ },
60
+ 1
61
+ );
62
+
63
+ export const pathModule = {
64
+ join: pathJoin,
65
+ dirname: pathDirname,
66
+ basename: pathBasename,
67
+ extname: pathExtname,
68
+ };
69
+
@@ -0,0 +1,134 @@
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
+ );
11
+ }
12
+ }
13
+
14
+ // --- BuiltinFunction Definitions ---
15
+
16
+ const regexMatch = new BuiltinFunction(
17
+ "find_matches",
18
+ (args, interpreter, callNode) => {
19
+ const [pattern, text, flags] = args;
20
+ expectString(pattern, "regex.find_matches", interpreter, callNode, 1);
21
+ expectString(text, "regex.find_matches", interpreter, callNode, 2);
22
+
23
+ let flagsStr = "g"; // Default to global match if not specified
24
+ if (flags !== undefined) {
25
+ expectString(flags, "regex.find_matches", interpreter, callNode, 3);
26
+ flagsStr = flags;
27
+ }
28
+
29
+ try {
30
+ const re = new RegExp(pattern, flagsStr);
31
+ const matches = text.match(re);
32
+
33
+ if (!matches) return null;
34
+
35
+ // text.match with global flag returns array of strings.
36
+ // text.match without global flag returns object with capturing groups.
37
+ // For simplicity in Mimo, let's return a standard array of strings.
38
+ return [...matches];
39
+ } catch (e) {
40
+ throw interpreter.errorHandler.createRuntimeError(
41
+ `Invalid regular expression: ${e.message}`,
42
+ callNode,
43
+ "REGEX001",
44
+ );
45
+ }
46
+ },
47
+ [2, 3], // pattern, text, [flags]
48
+ );
49
+
50
+ const regexTest = new BuiltinFunction(
51
+ "is_match",
52
+ (args, interpreter, callNode) => {
53
+ const [pattern, text, flags] = args;
54
+ expectString(pattern, "regex.is_match", interpreter, callNode, 1);
55
+ expectString(text, "regex.is_match", interpreter, callNode, 2);
56
+
57
+ const flagsStr = flags || "";
58
+
59
+ try {
60
+ const re = new RegExp(pattern, flagsStr);
61
+ return re.test(text);
62
+ } catch (e) {
63
+ throw interpreter.errorHandler.createRuntimeError(
64
+ `Invalid regular expression: ${e.message}`,
65
+ callNode,
66
+ "REGEX001",
67
+ );
68
+ }
69
+ },
70
+ [2, 3],
71
+ );
72
+
73
+ const regexReplace = new BuiltinFunction(
74
+ "replace_all",
75
+ (args, interpreter, callNode) => {
76
+ const [text, pattern, replacement, flags] = args;
77
+ expectString(text, "regex.replace_all", interpreter, callNode, 1);
78
+ expectString(pattern, "regex.replace_all", interpreter, callNode, 2);
79
+ expectString(replacement, "regex.replace_all", interpreter, callNode, 3);
80
+
81
+ // Default to 'g' (replace all) which is usually what people want in regex replace
82
+ const flagsStr = flags || "g";
83
+
84
+ try {
85
+ const re = new RegExp(pattern, flagsStr);
86
+ return text.replace(re, replacement);
87
+ } catch (e) {
88
+ throw interpreter.errorHandler.createRuntimeError(
89
+ `Invalid regular expression: ${e.message}`,
90
+ callNode,
91
+ "REGEX001",
92
+ );
93
+ }
94
+ },
95
+ [3, 4], // text, pattern, replacement, [flags]
96
+ );
97
+
98
+ // Returns the first match and its capturing groups
99
+ const regexExec = new BuiltinFunction(
100
+ "extract",
101
+ (args, interpreter, callNode) => {
102
+ const [pattern, text, flags] = args;
103
+ expectString(pattern, "regex.extract", interpreter, callNode, 1);
104
+ expectString(text, "regex.extract", interpreter, callNode, 2);
105
+
106
+ const flagsStr = flags || "";
107
+
108
+ try {
109
+ const re = new RegExp(pattern, flagsStr);
110
+ const result = re.exec(text);
111
+
112
+ if (!result) return null;
113
+
114
+ // Convert the special RegEx result object/array to a clean Mimo array
115
+ // Index 0 is full match, 1+ are groups
116
+ const output = [...result];
117
+ return output;
118
+ } catch (e) {
119
+ throw interpreter.errorHandler.createRuntimeError(
120
+ `Invalid regular expression: ${e.message}`,
121
+ callNode,
122
+ "REGEX001",
123
+ );
124
+ }
125
+ },
126
+ [2, 3],
127
+ );
128
+
129
+ export const regexModule = {
130
+ find_matches: regexMatch,
131
+ is_match: regexTest,
132
+ replace_all: regexReplace,
133
+ extract: regexExec,
134
+ };
@@ -0,0 +1,260 @@
1
+ import { BuiltinFunction } from '../BuiltinFunction.js';
2
+
3
+ // --- Helper for type checking ---
4
+ function expectString(arg, funcName, interpreter, callNode, argPosition = 1, allowEmpty = true) {
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
+ `Ensure argument ${argPosition} for '${funcName}' is a string.`
11
+ );
12
+ }
13
+ if (!allowEmpty && arg === "") {
14
+ throw interpreter.errorHandler.createRuntimeError(
15
+ `${funcName}() argument ${argPosition} cannot be an empty string.`,
16
+ callNode,
17
+ 'ARG001',
18
+ `Provide a non-empty string for argument ${argPosition} of '${funcName}'.`
19
+ );
20
+ }
21
+ }
22
+
23
+ function expectStringOrNumber(arg, funcName, interpreter, callNode, argPosition = 1) {
24
+ if (typeof arg !== 'string' && typeof arg !== 'number') {
25
+ throw interpreter.errorHandler.createRuntimeError(
26
+ `${funcName}() expects a string or number as argument ${argPosition}. Got '${typeof arg}'.`,
27
+ callNode,
28
+ 'TYPE001',
29
+ `Ensure argument ${argPosition} for '${funcName}' is a string or number.`
30
+ );
31
+ }
32
+ }
33
+
34
+ function expectNumber(arg, funcName, interpreter, callNode, argPosition = 1) {
35
+ if (typeof arg !== 'number') {
36
+ throw interpreter.errorHandler.createRuntimeError(
37
+ `${funcName}() expects a number as argument ${argPosition}. Got '${typeof arg}'.`,
38
+ callNode,
39
+ 'TYPE001',
40
+ `Ensure argument ${argPosition} for '${funcName}' is a number.`
41
+ );
42
+ }
43
+ }
44
+
45
+
46
+ // --- BuiltinFunction Definitions ---
47
+
48
+ // Case Conversion
49
+ const strToUpper = new BuiltinFunction("to_upper", (args, interpreter, callNode) => {
50
+ expectString(args[0], "to_upper", interpreter, callNode, 1);
51
+ return args[0].toUpperCase();
52
+ }, 1);
53
+
54
+ const strToLower = new BuiltinFunction("to_lower", (args, interpreter, callNode) => {
55
+ expectString(args[0], "to_lower", interpreter, callNode, 1);
56
+ return args[0].toLowerCase();
57
+ }, 1);
58
+
59
+ const strToTitleCase = new BuiltinFunction("to_title_case", (args, interpreter, callNode) => {
60
+ expectString(args[0], "to_title_case", interpreter, callNode, 1);
61
+ return args[0].toLowerCase().split(' ').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
62
+ }, 1);
63
+
64
+ const strCapitalize = new BuiltinFunction("capitalize", (args, interpreter, callNode) => {
65
+ expectString(args[0], "capitalize", interpreter, callNode, 1);
66
+ return args[0].charAt(0).toUpperCase() + args[0].slice(1);
67
+ }, 1);
68
+
69
+
70
+ // TODO: Consider 'to_title_case' or 'capitalize' later
71
+
72
+ // Trimming
73
+ const strTrim = new BuiltinFunction("trim", (args, interpreter, callNode) => {
74
+ expectString(args[0], "trim", interpreter, callNode, 1);
75
+ return args[0].trim();
76
+ }, 1);
77
+
78
+ const strTrimStart = new BuiltinFunction("trim_start", (args, interpreter, callNode) => { // or trim_left
79
+ expectString(args[0], "trim_start", interpreter, callNode, 1);
80
+ return args[0].trimStart();
81
+ }, 1);
82
+
83
+ const strTrimEnd = new BuiltinFunction("trim_end", (args, interpreter, callNode) => { // or trim_right
84
+ expectString(args[0], "trim_end", interpreter, callNode, 1);
85
+ return args[0].trimEnd();
86
+ }, 1);
87
+
88
+ // Padding
89
+ const strPadStart = new BuiltinFunction("pad_start", (args, interpreter, callNode) => {
90
+ expectString(args[0], "pad_start", interpreter, callNode, 1);
91
+ expectNumber(args[1], "pad_start", interpreter, callNode, 2);
92
+ const padString = args.length > 2 ? args[2] : " ";
93
+ expectString(padString, "pad_start", interpreter, callNode, 3);
94
+ return args[0].padStart(args[1], padString);
95
+ }, [2, 3]); // string, targetLength, [padString]
96
+
97
+ const strPadEnd = new BuiltinFunction("pad_end", (args, interpreter, callNode) => {
98
+ expectString(args[0], "pad_end", interpreter, callNode, 1);
99
+ expectNumber(args[1], "pad_end", interpreter, callNode, 2);
100
+ const padString = args.length > 2 ? args[2] : " ";
101
+ expectString(padString, "pad_end", interpreter, callNode, 3);
102
+ return args[0].padEnd(args[1], padString);
103
+ }, [2, 3]); // string, targetLength, [padString]
104
+
105
+ // Searching & Checking
106
+ const strContains = new BuiltinFunction("contains", (args, interpreter, callNode) => {
107
+ expectString(args[0], "contains", interpreter, callNode, 1);
108
+ expectString(args[1], "contains", interpreter, callNode, 2, false); // searchString shouldn't be empty typically for 'contains'
109
+ const position = args.length > 2 ? args[2] : 0;
110
+ expectNumber(position, "contains", interpreter, callNode, 3);
111
+ return args[0].includes(args[1], position);
112
+ }, [2, 3]); // string, searchString, [position]
113
+
114
+ const strStartsWith = new BuiltinFunction("starts_with", (args, interpreter, callNode) => {
115
+ expectString(args[0], "starts_with", interpreter, callNode, 1);
116
+ expectString(args[1], "starts_with", interpreter, callNode, 2);
117
+ const position = args.length > 2 ? args[2] : 0;
118
+ expectNumber(position, "starts_with", interpreter, callNode, 3);
119
+ return args[0].startsWith(args[1], position);
120
+ }, [2, 3]); // string, searchString, [position]
121
+
122
+ const strEndsWith = new BuiltinFunction("ends_with", (args, interpreter, callNode) => {
123
+ expectString(args[0], "ends_with", interpreter, callNode, 1);
124
+ expectString(args[1], "ends_with", interpreter, callNode, 2);
125
+ const length = args.length > 2 ? args[2] : args[0].length;
126
+ expectNumber(length, "ends_with", interpreter, callNode, 3);
127
+ return args[0].endsWith(args[1], length);
128
+ }, [2, 3]); // string, searchString, [length]
129
+
130
+ const strIndexOf = new BuiltinFunction("index_of", (args, interpreter, callNode) => {
131
+ expectString(args[0], "index_of", interpreter, callNode, 1);
132
+ expectString(args[1], "index_of", interpreter, callNode, 2);
133
+ const fromIndex = args.length > 2 ? args[2] : 0;
134
+ expectNumber(fromIndex, "index_of", interpreter, callNode, 3);
135
+ return args[0].indexOf(args[1], fromIndex);
136
+ }, [2, 3]); // string, searchValue, [fromIndex]
137
+
138
+ const strLastIndexOf = new BuiltinFunction("last_index_of", (args, interpreter, callNode) => {
139
+ expectString(args[0], "last_index_of", interpreter, callNode, 1);
140
+ expectString(args[1], "last_index_of", interpreter, callNode, 2);
141
+ const fromIndex = args.length > 2 ? args[2] : args[0].length - 1;
142
+ expectNumber(fromIndex, "last_index_of", interpreter, callNode, 3);
143
+ return args[0].lastIndexOf(args[1], fromIndex);
144
+ }, [2, 3]); // string, searchValue, [fromIndex]
145
+
146
+
147
+ // Substring & Slicing
148
+ const strSubstring = new BuiltinFunction("substring", (args, interpreter, callNode) => { // Already have one like this
149
+ expectString(args[0], "substring", interpreter, callNode, 1);
150
+ expectNumber(args[1], "substring", interpreter, callNode, 2); // indexStart
151
+ const indexEnd = args.length > 2 ? args[2] : undefined;
152
+ if (indexEnd !== undefined) expectNumber(indexEnd, "substring", interpreter, callNode, 3);
153
+ return args[0].substring(args[1], indexEnd);
154
+ }, [2, 3]);
155
+
156
+ const strSlice = new BuiltinFunction("slice", (args, interpreter, callNode) => {
157
+ expectString(args[0], "slice", interpreter, callNode, 1);
158
+ expectNumber(args[1], "slice", interpreter, callNode, 2); // beginIndex
159
+ const endIndex = args.length > 2 ? args[2] : undefined;
160
+ if (endIndex !== undefined) expectNumber(endIndex, "slice", interpreter, callNode, 3);
161
+ return args[0].slice(args[1], endIndex);
162
+ }, [2, 3]);
163
+
164
+ // Splitting & Joining (join is already a global built-in for arrays)
165
+ const strSplit = new BuiltinFunction("split", (args, interpreter, callNode) => { // Already have one like this
166
+ expectString(args[0], "split", interpreter, callNode, 1);
167
+ const separator = args.length > 1 ? args[1] : undefined;
168
+ const limit = args.length > 2 ? args[2] : undefined;
169
+ if (separator !== undefined) expectStringOrNumber(separator, "split", interpreter, callNode, 2); // Separator can be regex too, but Mimo is simple
170
+ if (limit !== undefined) expectNumber(limit, "split", interpreter, callNode, 3);
171
+ return args[0].split(separator, limit);
172
+ }, [1, 3]); // string, [separator], [limit]
173
+
174
+ // Replacement
175
+ const strReplace = new BuiltinFunction("replace", (args, interpreter, callNode) => { // Already have one like this
176
+ expectString(args[0], "replace", interpreter, callNode, 1);
177
+ expectStringOrNumber(args[1], "replace", interpreter, callNode, 2); // pattern (can be string or regex in JS)
178
+ expectString(args[2], "replace", interpreter, callNode, 3); // replacement
179
+ // For Mimo, keep it simple: pattern is a string. For regex, a dedicated regex module.
180
+ if (typeof args[1] !== 'string') {
181
+ throw interpreter.errorHandler.createRuntimeError(
182
+ "replace() pattern (arg 2) must be a string for Mimo's string.replace.",
183
+ callNode,
184
+ 'TYPE001',
185
+ "Provide a string for the pattern to replace."
186
+ );
187
+ }
188
+ return args[0].replace(args[1], args[2]);
189
+ }, 3);
190
+
191
+ const strReplaceAll = new BuiltinFunction("replace_all", (args, interpreter, callNode) => {
192
+ expectString(args[0], "replace_all", interpreter, callNode, 1);
193
+ expectStringOrNumber(args[1], "replace_all", interpreter, callNode, 2); // pattern
194
+ expectString(args[2], "replace_all", interpreter, callNode, 3); // replacement
195
+ if (typeof args[1] !== 'string') {
196
+ throw interpreter.errorHandler.createRuntimeError(
197
+ "replace_all() pattern (arg 2) must be a string for Mimo's string.replace_all.",
198
+ callNode,
199
+ 'TYPE001',
200
+ "Provide a string for the pattern to replace."
201
+ );
202
+ }
203
+ if (typeof String.prototype.replaceAll === 'function') { // Modern JS
204
+ return args[0].replaceAll(args[1], args[2]);
205
+ } else { // Fallback for older environments (simple string replacement)
206
+ return args[0].split(args[1]).join(args[2]);
207
+ }
208
+ }, 3);
209
+
210
+ // Other utilities
211
+ const strRepeat = new BuiltinFunction("repeat", (args, interpreter, callNode) => {
212
+ expectString(args[0], "repeat", interpreter, callNode, 1);
213
+ expectNumber(args[1], "repeat", interpreter, callNode, 2);
214
+ return args[0].repeat(args[1]);
215
+ }, 2); // string, count
216
+
217
+ const strCharAt = new BuiltinFunction("char_at", (args, interpreter, callNode) => {
218
+ expectString(args[0], "char_at", interpreter, callNode, 1);
219
+ expectNumber(args[1], "char_at", interpreter, callNode, 2);
220
+ return args[0].charAt(args[1]);
221
+ }, 2); // string, index
222
+
223
+ const strIsEmpty = new BuiltinFunction("is_empty", (args, interpreter, callNode) => {
224
+ expectString(args[0], "is_empty", interpreter, callNode, 1);
225
+ return args[0].length === 0;
226
+ }, 1);
227
+
228
+ const strIsBlank = new BuiltinFunction("is_blank", (args, interpreter, callNode) => {
229
+ expectString(args[0], "is_blank", interpreter, callNode, 1);
230
+ return args[0].trim().length === 0;
231
+ }, 1);
232
+
233
+ // `len` is already a global built-in.
234
+
235
+ // --- Export the module's contents ---
236
+ export const stringModuleExports = {
237
+ to_upper: strToUpper,
238
+ to_lower: strToLower,
239
+ trim: strTrim,
240
+ trim_start: strTrimStart,
241
+ trim_end: strTrimEnd,
242
+ pad_start: strPadStart,
243
+ pad_end: strPadEnd,
244
+ contains: strContains,
245
+ starts_with: strStartsWith,
246
+ ends_with: strEndsWith,
247
+ index_of: strIndexOf,
248
+ last_index_of: strLastIndexOf,
249
+ substring: strSubstring,
250
+ slice: strSlice,
251
+ split: strSplit,
252
+ replace: strReplace,
253
+ replace_all: strReplaceAll,
254
+ repeat: strRepeat,
255
+ char_at: strCharAt,
256
+ is_empty: strIsEmpty,
257
+ is_blank: strIsBlank,
258
+ to_title_case: strToTitleCase,
259
+ capitalize: strCapitalize,
260
+ };
@@ -0,0 +1,46 @@
1
+ export function getMimoType(value) {
2
+ if (value === null || value === undefined) return "null";
3
+ if (Array.isArray(value)) return "array";
4
+ if (value instanceof Date) return "datetime";
5
+ if (typeof value === "object") return "object";
6
+ return typeof value;
7
+ }
8
+
9
+ function levenshteinDistance(a, b) {
10
+ const rows = a.length + 1;
11
+ const cols = b.length + 1;
12
+ const dp = Array.from({ length: rows }, () => Array(cols).fill(0));
13
+
14
+ for (let i = 0; i < rows; i++) dp[i][0] = i;
15
+ for (let j = 0; j < cols; j++) dp[0][j] = j;
16
+
17
+ for (let i = 1; i < rows; i++) {
18
+ for (let j = 1; j < cols; j++) {
19
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
20
+ dp[i][j] = Math.min(
21
+ dp[i - 1][j] + 1,
22
+ dp[i][j - 1] + 1,
23
+ dp[i - 1][j - 1] + cost
24
+ );
25
+ }
26
+ }
27
+
28
+ return dp[a.length][b.length];
29
+ }
30
+
31
+ export function suggestNearestName(name, candidates) {
32
+ const unique = [...new Set((candidates || []).filter(Boolean))];
33
+ if (unique.length === 0) return null;
34
+
35
+ let best = null;
36
+ let bestDistance = Number.POSITIVE_INFINITY;
37
+ for (const candidate of unique) {
38
+ const distance = levenshteinDistance(String(name), String(candidate));
39
+ if (distance < bestDistance) {
40
+ best = candidate;
41
+ bestDistance = distance;
42
+ }
43
+ }
44
+
45
+ return bestDistance <= 2 ? best : null;
46
+ }