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,77 @@
1
+ import { BuiltinFunction } from "../../BuiltinFunction.js";
2
+ import { expectArray, expectNumber } from "./arrayUtils.js";
3
+
4
+ // --- Search and Query Functions ---
5
+
6
+ export const arrayIncludes = new BuiltinFunction(
7
+ "includes",
8
+ (args, interpreter, callNode) => {
9
+ const [arr, valueToFind, fromIndexArg] = args;
10
+ expectArray(arr, "includes", interpreter, callNode, 1);
11
+ // valueToFind can be any Mimo value
12
+
13
+ let fromIndex = 0;
14
+ if (fromIndexArg !== undefined) {
15
+ if (typeof fromIndexArg !== "number") {
16
+ throw interpreter.errorHandler.createRuntimeError(
17
+ `includes() fromIndex (arg 3) must be a number.`,
18
+ callNode,
19
+ 'ARG001',
20
+ `Provide a number for the fromIndex parameter.`
21
+ );
22
+ }
23
+ fromIndex = fromIndexArg;
24
+ }
25
+
26
+ // Handle negative fromIndex
27
+ if (fromIndex < 0) {
28
+ fromIndex = Math.max(0, arr.length + fromIndex);
29
+ }
30
+
31
+ for (let i = fromIndex; i < arr.length; i++) {
32
+ const currentValue = arr[i];
33
+ if (Number.isNaN(valueToFind) && Number.isNaN(currentValue)) {
34
+ return true;
35
+ }
36
+ if (currentValue === valueToFind) {
37
+ return true;
38
+ }
39
+ }
40
+ return false;
41
+ },
42
+ [2, 3], // array, valueToFind, [fromIndex]
43
+ );
44
+
45
+ export const arrayIndexOf = new BuiltinFunction(
46
+ "index_of",
47
+ (args, interpreter, callNode) => {
48
+ const [arr, valueToFind, fromIndexArg] = args;
49
+ expectArray(arr, "index_of", interpreter, callNode, 1);
50
+ // valueToFind can be any Mimo value
51
+
52
+ let fromIndex = 0;
53
+ if (fromIndexArg !== undefined) {
54
+ expectNumber(fromIndexArg, "index_of", interpreter, callNode, 3);
55
+ fromIndex = fromIndexArg;
56
+ }
57
+
58
+ return arr.indexOf(valueToFind, fromIndex);
59
+ },
60
+ [2, 3], // array, valueToFind, [fromIndex]
61
+ );
62
+
63
+ export const arrayLastIndexOf = new BuiltinFunction(
64
+ "last_index_of",
65
+ (args, interpreter, callNode) => {
66
+ const [arr, valueToFind, fromIndexArg] = args;
67
+ expectArray(arr, "last_index_of", interpreter, callNode, 1);
68
+
69
+ let fromIndex = arr.length - 1;
70
+ if (fromIndexArg !== undefined) {
71
+ expectNumber(fromIndexArg, "last_index_of", interpreter, callNode, 3);
72
+ fromIndex = fromIndexArg;
73
+ }
74
+ return arr.lastIndexOf(valueToFind, fromIndex);
75
+ },
76
+ [2, 3], // array, valueToFind, [fromIndex]
77
+ );
@@ -0,0 +1,49 @@
1
+ import { BuiltinFunction } from '../../BuiltinFunction.js';
2
+ import { expectArray } from './arrayUtils.js';
3
+
4
+ export const arrayUnique = new BuiltinFunction("unique",
5
+ (args, interpreter, callNode) => {
6
+ const [arr] = args;
7
+ expectArray(arr, "unique", interpreter, callNode, 1);
8
+
9
+ // Use a Set to get unique values, then convert back to array
10
+ // Note: This works for primitives. For objects/arrays, it checks reference equality.
11
+ return [...new Set(arr)];
12
+ },
13
+ 1
14
+ );
15
+
16
+ export const arrayIntersection = new BuiltinFunction("intersection",
17
+ (args, interpreter, callNode) => {
18
+ const [arr1, arr2] = args;
19
+ expectArray(arr1, "intersection", interpreter, callNode, 1);
20
+ expectArray(arr2, "intersection", interpreter, callNode, 2);
21
+
22
+ const set2 = new Set(arr2);
23
+ return arr1.filter(item => set2.has(item));
24
+ },
25
+ 2
26
+ );
27
+
28
+ export const arrayUnion = new BuiltinFunction("union",
29
+ (args, interpreter, callNode) => {
30
+ const [arr1, arr2] = args;
31
+ expectArray(arr1, "union", interpreter, callNode, 1);
32
+ expectArray(arr2, "union", interpreter, callNode, 2);
33
+
34
+ return [...new Set([...arr1, ...arr2])];
35
+ },
36
+ 2
37
+ );
38
+
39
+ export const arrayDifference = new BuiltinFunction("difference",
40
+ (args, interpreter, callNode) => {
41
+ const [arr1, arr2] = args;
42
+ expectArray(arr1, "difference", interpreter, callNode, 1);
43
+ expectArray(arr2, "difference", interpreter, callNode, 2);
44
+
45
+ const set2 = new Set(arr2);
46
+ return arr1.filter(item => !set2.has(item));
47
+ },
48
+ 2
49
+ );
@@ -0,0 +1,68 @@
1
+ import { BuiltinFunction } from '../../BuiltinFunction.js';
2
+ import { expectArray } from './arrayUtils.js';
3
+
4
+ // --- Transformation Functions ---
5
+
6
+ export const arraySort = new BuiltinFunction("sort",
7
+ (args, interpreter, callNode) => {
8
+ const [arr] = args;
9
+ expectArray(arr, "sort", interpreter, callNode, 1);
10
+
11
+ const newArray = [...arr];
12
+ const allNumbers = newArray.every(item => typeof item === 'number');
13
+ const allStrings = newArray.every(item => typeof item === 'string');
14
+
15
+ if (allNumbers) {
16
+ newArray.sort((a, b) => a - b);
17
+ } else if (allStrings) {
18
+ newArray.sort();
19
+ } else {
20
+ // Mixed types: convert to strings for comparison
21
+ newArray.sort((a, b) => String(a).localeCompare(String(b)));
22
+ }
23
+
24
+ return newArray;
25
+ },
26
+ 1
27
+ );
28
+
29
+ export const arrayReverse = new BuiltinFunction("reverse",
30
+ (args, interpreter, callNode) => {
31
+ const [arr] = args;
32
+ expectArray(arr, "reverse", interpreter, callNode, 1);
33
+ const newArray = [...arr];
34
+ return newArray.reverse();
35
+ },
36
+ 1
37
+ );
38
+
39
+ export const arrayShuffle = new BuiltinFunction("shuffle",
40
+ (args, interpreter, callNode) => {
41
+ const [arr] = args;
42
+ expectArray(arr, "shuffle", interpreter, callNode, 1);
43
+ const newArray = [...arr];
44
+ for (let i = newArray.length - 1; i > 0; i--) {
45
+ const j = Math.floor(Math.random() * (i + 1));
46
+ [newArray[i], newArray[j]] = [newArray[j], newArray[i]];
47
+ }
48
+ return newArray;
49
+ },
50
+ 1
51
+ );
52
+
53
+ export const arrayConcat = new BuiltinFunction("concat",
54
+
55
+ (args, interpreter, callNode) => {
56
+ if (args.length === 0) {
57
+ return [];
58
+ }
59
+ let resultArray = [];
60
+ for (let i = 0; i < args.length; i++) {
61
+ const currentArg = args[i];
62
+ expectArray(currentArg, "concat", interpreter, callNode, i + 1);
63
+ resultArray = resultArray.concat(currentArg);
64
+ }
65
+ return resultArray;
66
+ },
67
+ [1, Infinity] // At least 1 array, but can concatenate many
68
+ );
@@ -0,0 +1,85 @@
1
+ import { BuiltinFunction } from '../BuiltinFunction.js';
2
+
3
+ // Import all categorized array functions
4
+ import {
5
+ arrayMap,
6
+ arrayFilter,
7
+ arrayReduce,
8
+ arrayForEach,
9
+ arrayFind,
10
+ arrayFindIndex,
11
+ arrayFlat,
12
+ arrayFlatMap,
13
+ arrayGroupBy,
14
+ arrayZip,
15
+ arrayChunk,
16
+ arrayCount
17
+ } from './array/higherOrderFunctions.js';
18
+
19
+ import {
20
+ arrayIncludes,
21
+ arrayIndexOf,
22
+ arrayLastIndexOf
23
+ } from './array/searchFunctions.js';
24
+
25
+ import {
26
+ arraySlice,
27
+ arrayFirst,
28
+ arrayLast,
29
+ arrayIsEmpty
30
+ } from './array/accessFunctions.js';
31
+
32
+ import {
33
+ arraySort,
34
+ arrayReverse,
35
+ arrayShuffle,
36
+ arrayConcat
37
+ } from './array/transformationFunctions.js';
38
+
39
+ import {
40
+ arrayUnique,
41
+ arrayIntersection,
42
+ arrayUnion,
43
+ arrayDifference
44
+ } from './array/setFunctions.js';
45
+
46
+ // --- Module Definition ---
47
+ export const arrayModule = {
48
+ map: arrayMap,
49
+ filter: arrayFilter,
50
+ reduce: arrayReduce,
51
+ flat: arrayFlat,
52
+ flat_map: arrayFlatMap,
53
+ group_by: arrayGroupBy,
54
+ zip: arrayZip,
55
+ chunk: arrayChunk,
56
+ count: arrayCount,
57
+ for_each: arrayForEach,
58
+ find: arrayFind,
59
+ find_index: arrayFindIndex,
60
+ includes: arrayIncludes,
61
+ index_of: arrayIndexOf,
62
+ last_index_of: arrayLastIndexOf,
63
+ slice: arraySlice,
64
+ first: arrayFirst,
65
+ last: arrayLast,
66
+ is_empty: arrayIsEmpty,
67
+ sort: arraySort,
68
+ reverse: arrayReverse,
69
+ shuffle: arrayShuffle,
70
+ concat: arrayConcat,
71
+ unique: arrayUnique,
72
+ intersection: arrayIntersection,
73
+ union: arrayUnion,
74
+ difference: arrayDifference,
75
+ };
76
+
77
+
78
+ // --- Initialize Function ---
79
+ export function initializeArrayModule(environment) {
80
+ const arrayModuleObject = {};
81
+ for (const [name, func] of Object.entries(arrayModule)) {
82
+ arrayModuleObject[name] = func;
83
+ }
84
+ environment.define('Array', arrayModuleObject);
85
+ }
@@ -0,0 +1,143 @@
1
+ import { BuiltinFunction } from "../BuiltinFunction.js";
2
+ import { stringify } from "../Utils.js";
3
+
4
+ // --- Helper: Deep Equality ---
5
+ function isDeepEqual(a, b) {
6
+ if (a === b) return true;
7
+
8
+ if (
9
+ typeof a !== "object" ||
10
+ a === null ||
11
+ typeof b !== "object" ||
12
+ b === null
13
+ ) {
14
+ return false;
15
+ }
16
+
17
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
18
+
19
+ const keysA = Object.keys(a);
20
+ const keysB = Object.keys(b);
21
+
22
+ if (keysA.length !== keysB.length) return false;
23
+
24
+ for (const key of keysA) {
25
+ if (!keysB.includes(key) || !isDeepEqual(a[key], b[key])) {
26
+ return false;
27
+ }
28
+ }
29
+
30
+ return true;
31
+ }
32
+
33
+ // --- Builtin Functions ---
34
+
35
+ const assertEq = new BuiltinFunction(
36
+ "eq",
37
+ (args, interpreter, callNode) => {
38
+ const [actual, expected, message] = args;
39
+ if (!isDeepEqual(actual, expected)) {
40
+ const msg = message ? `: ${message}` : "";
41
+ throw interpreter.errorHandler.createRuntimeError(
42
+ `Assertion Failed${msg}.\n Expected: ${stringify(expected)}\n Actual: ${stringify(actual)}`,
43
+ callNode,
44
+ "ASSERT_FAIL",
45
+ );
46
+ }
47
+ return true;
48
+ },
49
+ [2, 3], // actual, expected, [message]
50
+ );
51
+
52
+ const assertNeq = new BuiltinFunction(
53
+ "neq",
54
+ (args, interpreter, callNode) => {
55
+ const [actual, expected, message] = args;
56
+ if (isDeepEqual(actual, expected)) {
57
+ const msg = message ? `: ${message}` : "";
58
+ throw interpreter.errorHandler.createRuntimeError(
59
+ `Assertion Failed${msg}. Expected values to be different.`,
60
+ callNode,
61
+ "ASSERT_FAIL",
62
+ );
63
+ }
64
+ return true;
65
+ },
66
+ [2, 3],
67
+ );
68
+
69
+ const assertTrue = new BuiltinFunction(
70
+ "true",
71
+ (args, interpreter, callNode) => {
72
+ const [condition, message] = args;
73
+ if (condition !== true) {
74
+ const msg = message ? `: ${message}` : "";
75
+ throw interpreter.errorHandler.createRuntimeError(
76
+ `Assertion Failed${msg}. Expected true, got ${stringify(condition)}`,
77
+ callNode,
78
+ "ASSERT_FAIL",
79
+ );
80
+ }
81
+ return true;
82
+ },
83
+ [1, 2],
84
+ );
85
+
86
+ const assertFalse = new BuiltinFunction(
87
+ "false",
88
+ (args, interpreter, callNode) => {
89
+ const [condition, message] = args;
90
+ if (condition !== false) {
91
+ const msg = message ? `: ${message}` : "";
92
+ throw interpreter.errorHandler.createRuntimeError(
93
+ `Assertion Failed${msg}. Expected false, got ${stringify(condition)}`,
94
+ callNode,
95
+ "ASSERT_FAIL",
96
+ );
97
+ }
98
+ return true;
99
+ },
100
+ [1, 2],
101
+ );
102
+
103
+ // Check if a function throws an error
104
+ const assertThrows = new BuiltinFunction(
105
+ "throws",
106
+ (args, interpreter, callNode) => {
107
+ const [fn, message] = args;
108
+
109
+ // The argument must be a callable Mimo function
110
+ if (!fn || typeof fn.call !== "function") {
111
+ throw interpreter.errorHandler.createRuntimeError(
112
+ `assert.throws expects a function as the first argument.`,
113
+ callNode,
114
+ "TYPE001",
115
+ );
116
+ }
117
+
118
+ try {
119
+ // Attempt to execute the function with no arguments
120
+ fn.call(interpreter, [], callNode);
121
+ } catch (error) {
122
+ // It threw an error, which is what we wanted!
123
+ return true;
124
+ }
125
+
126
+ // If we get here, it didn't throw
127
+ const msg = message ? `: ${message}` : "";
128
+ throw interpreter.errorHandler.createRuntimeError(
129
+ `Assertion Failed${msg}. Expected function to throw an error, but it did not.`,
130
+ callNode,
131
+ "ASSERT_FAIL",
132
+ );
133
+ },
134
+ [1, 2],
135
+ );
136
+
137
+ export const assertModule = {
138
+ eq: assertEq,
139
+ neq: assertNeq,
140
+ true: assertTrue,
141
+ false: assertFalse,
142
+ throws: assertThrows,
143
+ };
@@ -0,0 +1,170 @@
1
+ import { BuiltinFunction } from '../BuiltinFunction.js';
2
+
3
+ // --- Helper for Type Checking ---
4
+ function expectDate(arg, funcName, interpreter, callNode) {
5
+ if (!(arg instanceof Date)) {
6
+ throw interpreter.errorHandler.createRuntimeError(
7
+ `${funcName}() expects a datetime object as its first argument.`,
8
+ callNode, 'TYPE001', 'Use a value returned from datetime.now() or datetime.from_timestamp().'
9
+ );
10
+ }
11
+ }
12
+
13
+ // --- BuiltinFunction Definitions ---
14
+
15
+ const dtNow = new BuiltinFunction("now",
16
+ () => {
17
+ // Returns a new JavaScript Date object representing the current moment.
18
+ return new Date();
19
+ },
20
+ 0
21
+ );
22
+
23
+ const dtGetTimestamp = new BuiltinFunction("get_timestamp",
24
+ (args, interpreter, callNode) => {
25
+ const [dateObj] = args;
26
+ expectDate(dateObj, "datetime.get_timestamp", interpreter, callNode);
27
+ // Returns the number of milliseconds since the ECMAScript epoch.
28
+ return dateObj.getTime();
29
+ },
30
+ 1
31
+ );
32
+
33
+ const dtFromTimestamp = new BuiltinFunction("from_timestamp",
34
+ ([timestamp], interpreter, callNode) => {
35
+ if (typeof timestamp !== 'number') {
36
+ throw interpreter.errorHandler.createRuntimeError(
37
+ `datetime.from_timestamp() expects a number (milliseconds). Got '${typeof timestamp}'.`,
38
+ callNode, 'TYPE001', 'Provide a numeric timestamp.'
39
+ );
40
+ }
41
+ // Returns a new Date object from the timestamp.
42
+ return new Date(timestamp);
43
+ },
44
+ 1
45
+ );
46
+
47
+ const dtToISOString = new BuiltinFunction("to_iso_string",
48
+ ([dateObj], interpreter, callNode) => {
49
+ expectDate(dateObj, "datetime.to_iso_string", interpreter, callNode);
50
+ // Returns a string in simplified extended ISO format (ISO 8601), which is always 24 or 27 characters long.
51
+ return dateObj.toISOString();
52
+ },
53
+ 1
54
+ );
55
+
56
+ const dtFormat = new BuiltinFunction("format",
57
+ (args, interpreter, callNode) => {
58
+ const [dateObj, formatStr] = args;
59
+ expectDate(dateObj, "datetime.format", interpreter, callNode);
60
+ if (typeof formatStr !== 'string') {
61
+ throw interpreter.errorHandler.createRuntimeError(
62
+ `datetime.format() expects a format string as its second argument. Got '${typeof formatStr}'.`,
63
+ callNode, 'TYPE001', 'Provide a format string (e.g., "YYYY-MM-DD").'
64
+ );
65
+ }
66
+
67
+ const year = dateObj.getFullYear();
68
+ const month = String(dateObj.getMonth() + 1).padStart(2, '0'); // Month is 0-indexed
69
+ const day = String(dateObj.getDate()).padStart(2, '0');
70
+ const hours = String(dateObj.getHours()).padStart(2, '0');
71
+ const minutes = String(dateObj.getMinutes()).padStart(2, '0');
72
+ const seconds = String(dateObj.getSeconds()).padStart(2, '0');
73
+
74
+ let formatted = formatStr;
75
+ formatted = formatted.replace(/YYYY/g, year);
76
+ formatted = formatted.replace(/MM/g, month);
77
+ formatted = formatted.replace(/DD/g, day);
78
+ formatted = formatted.replace(/hh/g, hours);
79
+ formatted = formatted.replace(/mm/g, minutes);
80
+ formatted = formatted.replace(/ss/g, seconds);
81
+
82
+ return formatted;
83
+ },
84
+ 2
85
+ );
86
+
87
+ const dtAdd = new BuiltinFunction("add",
88
+ (args, interpreter, callNode) => {
89
+ const [dateObj, amount, unit] = args;
90
+ expectDate(dateObj, "datetime.add", interpreter, callNode);
91
+ if (typeof amount !== 'number') {
92
+ throw interpreter.errorHandler.createRuntimeError(
93
+ `datetime.add() expects a numeric amount. Got '${typeof amount}'.`,
94
+ callNode, 'TYPE001', 'Provide a numeric amount to add.'
95
+ );
96
+ }
97
+ if (typeof unit !== 'string') {
98
+ throw interpreter.errorHandler.createRuntimeError(
99
+ `datetime.add() expects a string unit. Got '${typeof unit}'.`,
100
+ callNode, 'TYPE001', 'Provide a string unit (e.g., "days").'
101
+ );
102
+ }
103
+
104
+ const newDate = new Date(dateObj.getTime());
105
+ switch (unit.toLowerCase()) {
106
+ case 'years': newDate.setFullYear(newDate.getFullYear() + amount); break;
107
+ case 'months': newDate.setMonth(newDate.getMonth() + amount); break;
108
+ case 'days': newDate.setDate(newDate.getDate() + amount); break;
109
+ case 'hours': newDate.setHours(newDate.getHours() + amount); break;
110
+ case 'minutes': newDate.setMinutes(newDate.getMinutes() + amount); break;
111
+ case 'seconds': newDate.setSeconds(newDate.getSeconds() + amount); break;
112
+ default:
113
+ throw interpreter.errorHandler.createRuntimeError(
114
+ `Unknown unit '${unit}' for datetime.add().`,
115
+ callNode, 'ARG001', 'Supported units: years, months, days, hours, minutes, seconds.'
116
+ );
117
+ }
118
+ return newDate;
119
+ },
120
+ 3
121
+ );
122
+
123
+ const dtSubtract = new BuiltinFunction("subtract",
124
+ (args, interpreter, callNode) => {
125
+ const [dateObj, amount, unit] = args;
126
+ expectDate(dateObj, "datetime.subtract", interpreter, callNode);
127
+ if (typeof amount !== 'number') {
128
+ throw interpreter.errorHandler.createRuntimeError(
129
+ `datetime.subtract() expects a numeric amount. Got '${typeof amount}'.`,
130
+ callNode, 'TYPE001', 'Provide a numeric amount to subtract.'
131
+ );
132
+ }
133
+ if (typeof unit !== 'string') {
134
+ throw interpreter.errorHandler.createRuntimeError(
135
+ `datetime.subtract() expects a string unit. Got '${typeof unit}'.`,
136
+ callNode, 'TYPE001', 'Provide a string unit (e.g., "days").'
137
+ );
138
+ }
139
+
140
+ const newDate = new Date(dateObj.getTime());
141
+ switch (unit.toLowerCase()) {
142
+ case 'years': newDate.setFullYear(newDate.getFullYear() - amount); break;
143
+ case 'months': newDate.setMonth(newDate.getMonth() - amount); break;
144
+ case 'days': newDate.setDate(newDate.getDate() - amount); break;
145
+ case 'hours': newDate.setHours(newDate.getHours() - amount); break;
146
+ case 'minutes': newDate.setMinutes(newDate.getMinutes() - amount); break;
147
+ case 'seconds': newDate.setSeconds(newDate.getSeconds() - amount); break;
148
+ default:
149
+ throw interpreter.errorHandler.createRuntimeError(
150
+ `Unknown unit '${unit}' for datetime.subtract().`,
151
+ callNode, 'ARG001', 'Supported units: years, months, days, hours, minutes, seconds.'
152
+ );
153
+ }
154
+ return newDate;
155
+ },
156
+ 3
157
+ );
158
+
159
+
160
+
161
+ // --- Module Export ---
162
+ export const datetimeModule = {
163
+ now: dtNow,
164
+ get_timestamp: dtGetTimestamp,
165
+ from_timestamp: dtFromTimestamp,
166
+ to_iso_string: dtToISOString,
167
+ format: dtFormat,
168
+ add: dtAdd,
169
+ subtract: dtSubtract,
170
+ };
@@ -0,0 +1,54 @@
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 an environment variable name as a string."
10
+ );
11
+ }
12
+ }
13
+
14
+ const envGet = new BuiltinFunction(
15
+ "get",
16
+ ([name, fallback], interpreter, callNode) => {
17
+ expectString(name, "env.get", interpreter, callNode, 1);
18
+ const value = interpreter.adapter.getEnvVariable(name);
19
+ if (value === undefined || value === null) {
20
+ return fallback === undefined ? null : fallback;
21
+ }
22
+ return value;
23
+ },
24
+ [1, 2]
25
+ );
26
+
27
+ const envHas = new BuiltinFunction(
28
+ "has",
29
+ ([name], interpreter, callNode) => {
30
+ expectString(name, "env.has", interpreter, callNode, 1);
31
+ const value = interpreter.adapter.getEnvVariable(name);
32
+ return value !== undefined && value !== null;
33
+ },
34
+ 1
35
+ );
36
+
37
+ const envAll = new BuiltinFunction(
38
+ "all",
39
+ (args, interpreter) => {
40
+ const adapterAll = interpreter.adapter.getEnvAll;
41
+ if (typeof adapterAll === "function") {
42
+ return adapterAll();
43
+ }
44
+ return {};
45
+ },
46
+ 0
47
+ );
48
+
49
+ export const envModule = {
50
+ get: envGet,
51
+ has: envHas,
52
+ all: envAll,
53
+ };
54
+