@mirascript/mirascript 0.1.0

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 (191) hide show
  1. package/dist/chunk-5FQWUJIY.js +766 -0
  2. package/dist/chunk-5FQWUJIY.js.map +6 -0
  3. package/dist/chunk-BTDGMWFK.js +202 -0
  4. package/dist/chunk-BTDGMWFK.js.map +6 -0
  5. package/dist/chunk-DCXIWIW5.js +3419 -0
  6. package/dist/chunk-DCXIWIW5.js.map +6 -0
  7. package/dist/chunk-RAPJ3XLV.js +10 -0
  8. package/dist/chunk-RAPJ3XLV.js.map +6 -0
  9. package/dist/cli/execute.d.ts +4 -0
  10. package/dist/cli/execute.d.ts.map +1 -0
  11. package/dist/cli/index.d.ts +2 -0
  12. package/dist/cli/index.d.ts.map +1 -0
  13. package/dist/cli/index.js +191 -0
  14. package/dist/cli/index.js.map +6 -0
  15. package/dist/cli/print.d.ts +4 -0
  16. package/dist/cli/print.d.ts.map +1 -0
  17. package/dist/compiler/compile-bytecode.d.ts +12 -0
  18. package/dist/compiler/compile-bytecode.d.ts.map +1 -0
  19. package/dist/compiler/compile-fast.d.ts +7 -0
  20. package/dist/compiler/compile-fast.d.ts.map +1 -0
  21. package/dist/compiler/create-script.d.ts +7 -0
  22. package/dist/compiler/create-script.d.ts.map +1 -0
  23. package/dist/compiler/diagnostic.d.ts +56 -0
  24. package/dist/compiler/diagnostic.d.ts.map +1 -0
  25. package/dist/compiler/emit.d.ts +4 -0
  26. package/dist/compiler/emit.d.ts.map +1 -0
  27. package/dist/compiler/index.d.ts +13 -0
  28. package/dist/compiler/index.d.ts.map +1 -0
  29. package/dist/compiler/types.d.ts +16 -0
  30. package/dist/compiler/types.d.ts.map +1 -0
  31. package/dist/compiler/worker-manager.d.ts +6 -0
  32. package/dist/compiler/worker-manager.d.ts.map +1 -0
  33. package/dist/compiler/worker.d.ts +6 -0
  34. package/dist/compiler/worker.d.ts.map +1 -0
  35. package/dist/compiler/worker.js +34 -0
  36. package/dist/compiler/worker.js.map +6 -0
  37. package/dist/helpers/constants.d.ts +3 -0
  38. package/dist/helpers/constants.d.ts.map +1 -0
  39. package/dist/helpers/serialize.d.ts +45 -0
  40. package/dist/helpers/serialize.d.ts.map +1 -0
  41. package/dist/helpers/utils.d.ts +36 -0
  42. package/dist/helpers/utils.d.ts.map +1 -0
  43. package/dist/index.d.ts +4 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +61 -0
  46. package/dist/index.js.map +6 -0
  47. package/dist/subtle.d.ts +7 -0
  48. package/dist/subtle.d.ts.map +1 -0
  49. package/dist/subtle.js +30 -0
  50. package/dist/subtle.js.map +6 -0
  51. package/dist/vm/env.d.ts +3 -0
  52. package/dist/vm/env.d.ts.map +1 -0
  53. package/dist/vm/error.d.ts +11 -0
  54. package/dist/vm/error.d.ts.map +1 -0
  55. package/dist/vm/helpers.d.ts +21 -0
  56. package/dist/vm/helpers.d.ts.map +1 -0
  57. package/dist/vm/index.d.ts +5 -0
  58. package/dist/vm/index.d.ts.map +1 -0
  59. package/dist/vm/lib/_helpers.d.ts +35 -0
  60. package/dist/vm/lib/_helpers.d.ts.map +1 -0
  61. package/dist/vm/lib/_loader.d.ts +16 -0
  62. package/dist/vm/lib/_loader.d.ts.map +1 -0
  63. package/dist/vm/lib/global/bit.d.ts +9 -0
  64. package/dist/vm/lib/global/bit.d.ts.map +1 -0
  65. package/dist/vm/lib/global/debug.d.ts +5 -0
  66. package/dist/vm/lib/global/debug.d.ts.map +1 -0
  67. package/dist/vm/lib/global/index.d.ts +10 -0
  68. package/dist/vm/lib/global/index.d.ts.map +1 -0
  69. package/dist/vm/lib/global/json.d.ts +4 -0
  70. package/dist/vm/lib/global/json.d.ts.map +1 -0
  71. package/dist/vm/lib/global/math-additional.d.ts +3 -0
  72. package/dist/vm/lib/global/math-additional.d.ts.map +1 -0
  73. package/dist/vm/lib/global/math-arr.d.ts +8 -0
  74. package/dist/vm/lib/global/math-arr.d.ts.map +1 -0
  75. package/dist/vm/lib/global/math-const.d.ts +3 -0
  76. package/dist/vm/lib/global/math-const.d.ts.map +1 -0
  77. package/dist/vm/lib/global/math-unary.d.ts +29 -0
  78. package/dist/vm/lib/global/math-unary.d.ts.map +1 -0
  79. package/dist/vm/lib/global/math.d.ts +9 -0
  80. package/dist/vm/lib/global/math.d.ts.map +1 -0
  81. package/dist/vm/lib/global/mod/index.d.ts +3 -0
  82. package/dist/vm/lib/global/mod/index.d.ts.map +1 -0
  83. package/dist/vm/lib/global/mod/matrix.d.ts +16 -0
  84. package/dist/vm/lib/global/mod/matrix.d.ts.map +1 -0
  85. package/dist/vm/lib/global/sequence/all-any.d.ts +4 -0
  86. package/dist/vm/lib/global/sequence/all-any.d.ts.map +1 -0
  87. package/dist/vm/lib/global/sequence/entries.d.ts +12 -0
  88. package/dist/vm/lib/global/sequence/entries.d.ts.map +1 -0
  89. package/dist/vm/lib/global/sequence/find.d.ts +10 -0
  90. package/dist/vm/lib/global/sequence/find.d.ts.map +1 -0
  91. package/dist/vm/lib/global/sequence/flatten.d.ts +3 -0
  92. package/dist/vm/lib/global/sequence/flatten.d.ts.map +1 -0
  93. package/dist/vm/lib/global/sequence/index.d.ts +12 -0
  94. package/dist/vm/lib/global/sequence/index.d.ts.map +1 -0
  95. package/dist/vm/lib/global/sequence/len.d.ts +3 -0
  96. package/dist/vm/lib/global/sequence/len.d.ts.map +1 -0
  97. package/dist/vm/lib/global/sequence/map-filter.d.ts +9 -0
  98. package/dist/vm/lib/global/sequence/map-filter.d.ts.map +1 -0
  99. package/dist/vm/lib/global/sequence/repeat.d.ts +4 -0
  100. package/dist/vm/lib/global/sequence/repeat.d.ts.map +1 -0
  101. package/dist/vm/lib/global/sequence/reverse.d.ts +3 -0
  102. package/dist/vm/lib/global/sequence/reverse.d.ts.map +1 -0
  103. package/dist/vm/lib/global/sequence/sort.d.ts +5 -0
  104. package/dist/vm/lib/global/sequence/sort.d.ts.map +1 -0
  105. package/dist/vm/lib/global/sequence/with.d.ts +5 -0
  106. package/dist/vm/lib/global/sequence/with.d.ts.map +1 -0
  107. package/dist/vm/lib/global/sequence/zip.d.ts +4 -0
  108. package/dist/vm/lib/global/sequence/zip.d.ts.map +1 -0
  109. package/dist/vm/lib/global/string.d.ts +12 -0
  110. package/dist/vm/lib/global/string.d.ts.map +1 -0
  111. package/dist/vm/lib/global/time.d.ts +15 -0
  112. package/dist/vm/lib/global/time.d.ts.map +1 -0
  113. package/dist/vm/lib/global/to-primitive.d.ts +6 -0
  114. package/dist/vm/lib/global/to-primitive.d.ts.map +1 -0
  115. package/dist/vm/operations.d.ts +49 -0
  116. package/dist/vm/operations.d.ts.map +1 -0
  117. package/dist/vm/types/checker.d.ts +30 -0
  118. package/dist/vm/types/checker.d.ts.map +1 -0
  119. package/dist/vm/types/context.d.ts +43 -0
  120. package/dist/vm/types/context.d.ts.map +1 -0
  121. package/dist/vm/types/extern.d.ts +32 -0
  122. package/dist/vm/types/extern.d.ts.map +1 -0
  123. package/dist/vm/types/function.d.ts +49 -0
  124. package/dist/vm/types/function.d.ts.map +1 -0
  125. package/dist/vm/types/index.d.ts +75 -0
  126. package/dist/vm/types/index.d.ts.map +1 -0
  127. package/dist/vm/types/module.d.ts +25 -0
  128. package/dist/vm/types/module.d.ts.map +1 -0
  129. package/dist/vm/types/script.d.ts +14 -0
  130. package/dist/vm/types/script.d.ts.map +1 -0
  131. package/dist/vm/types/wrapper.d.ts +25 -0
  132. package/dist/vm/types/wrapper.d.ts.map +1 -0
  133. package/package.json +55 -0
  134. package/src/cli/execute.ts +32 -0
  135. package/src/cli/index.ts +73 -0
  136. package/src/cli/print.ts +41 -0
  137. package/src/compiler/compile-bytecode.ts +65 -0
  138. package/src/compiler/compile-fast.ts +81 -0
  139. package/src/compiler/create-script.ts +34 -0
  140. package/src/compiler/diagnostic.ts +175 -0
  141. package/src/compiler/emit.ts +764 -0
  142. package/src/compiler/index.ts +67 -0
  143. package/src/compiler/types.ts +16 -0
  144. package/src/compiler/worker-manager.ts +60 -0
  145. package/src/compiler/worker.ts +37 -0
  146. package/src/helpers/constants.ts +3 -0
  147. package/src/helpers/serialize.ts +280 -0
  148. package/src/helpers/utils.ts +16 -0
  149. package/src/index.ts +3 -0
  150. package/src/subtle.ts +6 -0
  151. package/src/vm/env.ts +16 -0
  152. package/src/vm/error.ts +22 -0
  153. package/src/vm/helpers.ts +121 -0
  154. package/src/vm/index.ts +5 -0
  155. package/src/vm/lib/_helpers.ts +215 -0
  156. package/src/vm/lib/_loader.ts +51 -0
  157. package/src/vm/lib/global/bit.ts +93 -0
  158. package/src/vm/lib/global/debug.ts +36 -0
  159. package/src/vm/lib/global/index.ts +9 -0
  160. package/src/vm/lib/global/json.ts +45 -0
  161. package/src/vm/lib/global/math-additional.ts +71 -0
  162. package/src/vm/lib/global/math-arr.ts +62 -0
  163. package/src/vm/lib/global/math-const.ts +2 -0
  164. package/src/vm/lib/global/math-unary.ts +171 -0
  165. package/src/vm/lib/global/math.ts +27 -0
  166. package/src/vm/lib/global/mod/index.ts +4 -0
  167. package/src/vm/lib/global/mod/matrix.ts +579 -0
  168. package/src/vm/lib/global/sequence/all-any.ts +73 -0
  169. package/src/vm/lib/global/sequence/entries.ts +67 -0
  170. package/src/vm/lib/global/sequence/find.ts +49 -0
  171. package/src/vm/lib/global/sequence/flatten.ts +16 -0
  172. package/src/vm/lib/global/sequence/index.ts +11 -0
  173. package/src/vm/lib/global/sequence/len.ts +15 -0
  174. package/src/vm/lib/global/sequence/map-filter.ts +82 -0
  175. package/src/vm/lib/global/sequence/repeat.ts +28 -0
  176. package/src/vm/lib/global/sequence/reverse.ts +17 -0
  177. package/src/vm/lib/global/sequence/sort.ts +88 -0
  178. package/src/vm/lib/global/sequence/with.ts +43 -0
  179. package/src/vm/lib/global/sequence/zip.ts +40 -0
  180. package/src/vm/lib/global/string.ts +149 -0
  181. package/src/vm/lib/global/time.ts +73 -0
  182. package/src/vm/lib/global/to-primitive.ts +58 -0
  183. package/src/vm/operations.ts +497 -0
  184. package/src/vm/types/checker.ts +164 -0
  185. package/src/vm/types/context.ts +161 -0
  186. package/src/vm/types/extern.ts +166 -0
  187. package/src/vm/types/function.ts +136 -0
  188. package/src/vm/types/index.ts +124 -0
  189. package/src/vm/types/module.ts +40 -0
  190. package/src/vm/types/script.ts +18 -0
  191. package/src/vm/types/wrapper.ts +28 -0
@@ -0,0 +1,73 @@
1
+ import { $ToString, $ToNumber } from '../../operations.js';
2
+ import { VmLib } from '../_helpers.js';
3
+ import { isFinite } from '../../../helpers/utils.js';
4
+
5
+ export const to_timestamp = VmLib(
6
+ (datetime) => {
7
+ if (datetime == null) {
8
+ return Date.now();
9
+ }
10
+ if (typeof datetime == 'number') {
11
+ return new Date(datetime).getTime();
12
+ }
13
+ const str = $ToString(datetime);
14
+ if (!str) return Number.NaN;
15
+ const num = $ToNumber(str);
16
+ if (isFinite(num)) return num;
17
+ return Date.parse(str);
18
+ },
19
+ {
20
+ summary: '将数据转换为 Unix 毫秒时间戳',
21
+ params: { datetime: '要转换的数据,默认为当前时间' },
22
+ paramsType: { datetime: 'string | number' },
23
+ returnsType: 'number',
24
+ examples: ['to_timestamp("1970-01-01T00:00:00Z") // 0'],
25
+ },
26
+ );
27
+
28
+ export const to_datetime = VmLib(
29
+ (datetime, offset) => {
30
+ const timestamp = to_timestamp(datetime);
31
+ if (!isFinite(timestamp)) return null;
32
+ const o = $ToNumber(offset ?? 0) || 0;
33
+ const dateOffset = new Date(timestamp + o * 1000 * 60 * 60);
34
+ return {
35
+ year: dateOffset.getUTCFullYear(),
36
+ month: dateOffset.getUTCMonth() + 1,
37
+ day: dateOffset.getUTCDate(),
38
+ hour: dateOffset.getUTCHours(),
39
+ minute: dateOffset.getUTCMinutes(),
40
+ second: dateOffset.getUTCSeconds(),
41
+ millisecond: dateOffset.getUTCMilliseconds(),
42
+ dayOfWeek: dateOffset.getUTCDay(),
43
+ offset: o,
44
+ };
45
+ },
46
+ {
47
+ summary: '将数据转换为 Date 对象',
48
+ params: {
49
+ datetime: '要转换的数据,默认为当前时间',
50
+ offset: '时区偏移量(单位:小时),默认为 0',
51
+ },
52
+ paramsType: { datetime: 'string | number', offset: 'number' },
53
+ returnsType: 'Date',
54
+ examples: [
55
+ 'to_datetime(0) // (year: 1970, month: 1, day: 1, hour: 0, minute: 0, second: 0, millisecond: 0, dayOfWeek: 4, offset: 0)',
56
+ ],
57
+ },
58
+ );
59
+
60
+ export const to_iso8601 = VmLib(
61
+ (datetime) => {
62
+ const timestamp = to_timestamp(datetime);
63
+ if (!isFinite(timestamp)) return null;
64
+ return new Date(timestamp).toISOString();
65
+ },
66
+ {
67
+ summary: '将数据转换为 ISO 8601 格式的字符串',
68
+ params: { datetime: '要转换的数据,默认为当前时间' },
69
+ paramsType: { datetime: 'string | number' },
70
+ returnsType: 'string',
71
+ examples: ['to_iso8601(0) // "1970-01-01T00:00:00.000Z"'],
72
+ },
73
+ );
@@ -0,0 +1,58 @@
1
+ import { $ToString, $ToNumber, $ToBoolean, $Format } from '../../operations.js';
2
+ import { required, VmLib } from '../_helpers.js';
3
+
4
+ export const to_string = VmLib(
5
+ (data) => {
6
+ required('data', data, '');
7
+ return $ToString(data);
8
+ },
9
+ {
10
+ summary: '将数据转换为字符串',
11
+ params: { data: '要转换的数据' },
12
+ paramsType: { data: 'any' },
13
+ returnsType: 'string',
14
+ examples: ['to_string([1, 2]) // "1, 2"'],
15
+ },
16
+ );
17
+
18
+ export const to_number = VmLib(
19
+ (data) => {
20
+ required('data', data, Number.NaN);
21
+ return $ToNumber(data);
22
+ },
23
+ {
24
+ summary: '将数据转换为数字',
25
+ params: { data: '要转换的数据' },
26
+ paramsType: { data: 'any' },
27
+ returnsType: 'number',
28
+ examples: ['to_number("1.5") // 1.5'],
29
+ },
30
+ );
31
+
32
+ export const to_boolean = VmLib(
33
+ (data) => {
34
+ required('data', data, false);
35
+ return $ToBoolean(data);
36
+ },
37
+ {
38
+ summary: '将数据转换为布尔值',
39
+ params: { data: '要转换的数据' },
40
+ paramsType: { data: 'any' },
41
+ returnsType: 'boolean',
42
+ examples: ['to_boolean(nil) // false'],
43
+ },
44
+ );
45
+
46
+ export const format = VmLib(
47
+ (data, format) => {
48
+ required('data', data, '');
49
+ return $Format(data, format);
50
+ },
51
+ {
52
+ summary: '将数据格式化为指定格式的字符串',
53
+ params: { data: '要格式化的数据', format: '格式字符串' },
54
+ paramsType: { data: 'any', format: 'string' },
55
+ returnsType: 'string',
56
+ examples: ['format(12, ".3") // "12.000"'],
57
+ },
58
+ );
@@ -0,0 +1,497 @@
1
+ import { VmError } from './error.js';
2
+ import {
3
+ isVmArray,
4
+ VmModule,
5
+ VmExtern,
6
+ isVmRecord,
7
+ getVmFunctionInfo,
8
+ isVmPrimitive,
9
+ type TypeName,
10
+ type VmAny,
11
+ type VmImmutable,
12
+ type VmRecord,
13
+ type VmValue,
14
+ isVmFunction,
15
+ type VmArray,
16
+ type VmConst,
17
+ isVmExtern,
18
+ type VmPrimitive,
19
+ type VmFunction,
20
+ } from './types/index.js';
21
+ import { VmWrapper } from './types/wrapper.js';
22
+ import { hasOwnEnumerable, isNaN, isSafeInteger, keys, create } from '../helpers/utils.js';
23
+
24
+ const { abs, min, trunc, ceil } = Math;
25
+ const { slice, at } = Array.prototype;
26
+ const { POSITIVE_INFINITY, NEGATIVE_INFINITY } = Number;
27
+
28
+ const isSame = (a: VmValue, b: VmValue): boolean => {
29
+ // Check for NaN
30
+ if (typeof a == 'number' && typeof b == 'number') return a === b || (isNaN(a) && isNaN(b));
31
+ // Check all primitive types, and fast path for reference equality
32
+ if (a === b) return true;
33
+ // Any primitives and functions arrive here are not equal
34
+ if (a == null || typeof a != 'object' || b == null || typeof b != 'object') return false;
35
+ // Handle wrapper values
36
+ if (a instanceof VmWrapper) return a.same(b);
37
+ if (b instanceof VmWrapper) return b.same(a);
38
+ // Handle array values
39
+ if (isVmArray(a) && isVmArray(b)) {
40
+ // Compare array items
41
+ if (a.length !== b.length) {
42
+ return false;
43
+ }
44
+ const len = a.length;
45
+ for (let i = 0; i < len; i++) {
46
+ if (!isSame(a[i] ?? null, b[i] ?? null)) {
47
+ return false;
48
+ }
49
+ }
50
+ return true;
51
+ }
52
+ // Handle record values
53
+ if (!isVmArray(a) && !isVmArray(b)) {
54
+ // Compare record fields
55
+ const aKeys = keys(a);
56
+ const bKeys = keys(b);
57
+ if (aKeys.length !== bKeys.length) {
58
+ return false;
59
+ }
60
+ for (const key of aKeys) {
61
+ if (!hasOwnEnumerable(b, key)) {
62
+ return false;
63
+ }
64
+ if (!isSame(a[key] ?? null, b[key] ?? null)) {
65
+ return false;
66
+ }
67
+ }
68
+ return true;
69
+ }
70
+ return false;
71
+ };
72
+
73
+ const overloadNumberString = (a: VmAny, b: VmAny): boolean => {
74
+ if (typeof a == 'number' || typeof b == 'number') return true;
75
+ if (typeof a == 'string' || typeof b == 'string') return false;
76
+ return true;
77
+ };
78
+
79
+ // Math operations
80
+ export const $Add = (a: VmAny, b: VmAny): number => {
81
+ return $ToNumber(a) + $ToNumber(b);
82
+ };
83
+ export const $Sub = (a: VmAny, b: VmAny): number => {
84
+ return $ToNumber(a) - $ToNumber(b);
85
+ };
86
+ export const $Mul = (a: VmAny, b: VmAny): number => {
87
+ return $ToNumber(a) * $ToNumber(b);
88
+ };
89
+ export const $Div = (a: VmAny, b: VmAny): number => {
90
+ return $ToNumber(a) / $ToNumber(b);
91
+ };
92
+ export const $Mod = (a: VmAny, b: VmAny): number => {
93
+ return $ToNumber(a) % $ToNumber(b);
94
+ };
95
+ export const $Pow = (a: VmAny, b: VmAny): number => {
96
+ return $ToNumber(a) ** $ToNumber(b);
97
+ };
98
+ // Logical operations without short-circuiting
99
+ export const $And = (a: VmAny, b: VmAny): boolean => {
100
+ return $ToBoolean(a) && $ToBoolean(b);
101
+ };
102
+ export const $Or = (a: VmAny, b: VmAny): boolean => {
103
+ return $ToBoolean(a) || $ToBoolean(b);
104
+ };
105
+ // Comparison operations
106
+ export const $Gt = (a: VmAny, b: VmAny): boolean => {
107
+ if (overloadNumberString(a, b)) {
108
+ return $ToNumber(a) > $ToNumber(b);
109
+ } else {
110
+ return $ToString(a) > $ToString(b);
111
+ }
112
+ };
113
+ export const $Gte = (a: VmAny, b: VmAny): boolean => {
114
+ if (overloadNumberString(a, b)) {
115
+ return $ToNumber(a) >= $ToNumber(b);
116
+ } else {
117
+ return $ToString(a) >= $ToString(b);
118
+ }
119
+ };
120
+ export const $Lt = (a: VmAny, b: VmAny): boolean => {
121
+ if (overloadNumberString(a, b)) {
122
+ return $ToNumber(a) < $ToNumber(b);
123
+ } else {
124
+ return $ToString(a) < $ToString(b);
125
+ }
126
+ };
127
+ export const $Lte = (a: VmAny, b: VmAny): boolean => {
128
+ if (overloadNumberString(a, b)) {
129
+ return $ToNumber(a) <= $ToNumber(b);
130
+ } else {
131
+ return $ToString(a) <= $ToString(b);
132
+ }
133
+ };
134
+ export const $Eq = (a: VmAny, b: VmAny): boolean => {
135
+ $AssertInit(a);
136
+ $AssertInit(b);
137
+ // Number comparison is a special case to handle NaN correctly
138
+ if (typeof a == 'number' && typeof b == 'number') return a === b;
139
+ return isSame(a, b);
140
+ };
141
+ export const $Neq = (a: VmAny, b: VmAny): boolean => !$Eq(a, b);
142
+ const stringComparer = new Intl.Collator('en', {
143
+ usage: 'sort',
144
+ numeric: false,
145
+ sensitivity: 'base',
146
+ });
147
+ export const $Aeq = (a: VmAny, b: VmAny): boolean => {
148
+ if (overloadNumberString(a, b)) {
149
+ const an = $ToNumber(a);
150
+ const bn = $ToNumber(b);
151
+ const EPS = 1e-15;
152
+ if (isNaN(an) || isNaN(bn)) return false;
153
+ // Since Inf - Inf is NaN, we must check for equality first
154
+ if (an === bn) return true;
155
+ const absoluteDifference = abs(an - bn);
156
+ if (absoluteDifference < EPS) return true;
157
+ const base = min(abs(an), abs(bn));
158
+ return absoluteDifference < base * EPS;
159
+ } else {
160
+ // For strings, we use localeCompare for case-insensitive accent-insensitive comparison
161
+ const as = $ToString(a);
162
+ const bs = $ToString(b);
163
+ if (as === bs) return true;
164
+ return stringComparer.compare(as, bs) === 0;
165
+ }
166
+ };
167
+ export const $Naeq = (a: VmAny, b: VmAny): boolean => !$Aeq(a, b);
168
+ export const $Same = (a: VmAny, b: VmAny): boolean => {
169
+ $AssertInit(a);
170
+ $AssertInit(b);
171
+ return isSame(a, b);
172
+ };
173
+ export const $Nsame = (a: VmAny, b: VmAny): boolean => !$Same(a, b);
174
+ export const $In = (value: VmAny, iterable: VmAny): boolean => {
175
+ $AssertInit(value);
176
+ $AssertInit(iterable);
177
+ if (iterable == null) return false;
178
+ if (isVmArray(iterable)) {
179
+ if (value == null) {
180
+ // array may have empty slots
181
+ for (const item of iterable) {
182
+ if (item == null) return true;
183
+ }
184
+ return false;
185
+ }
186
+ // JS %SameValueZero is same with `isSame` in this context
187
+ if (isVmPrimitive(value)) return iterable.includes(value);
188
+ // value is not null here, so it's ok to skip empty slots, since `isSame(null, something)` is always false
189
+ value satisfies NonNullable<VmValue>;
190
+ return iterable.some((item = null) => isSame(item, value));
191
+ }
192
+ // iterable is a record or an extern here, value should be a string
193
+ if (iterable instanceof VmWrapper) return iterable.has($ToString(value));
194
+ if (typeof iterable == 'object') return hasOwnEnumerable(iterable, $ToString(value));
195
+ iterable satisfies VmPrimitive | VmFunction;
196
+ return false;
197
+ };
198
+ export const $Concat = (...args: string[]): string => {
199
+ return args.map($Format).join('');
200
+ };
201
+ export const $Pos = (a: VmAny): number => $ToNumber(a);
202
+ export const $Neg = (a: VmAny): number => -$ToNumber(a);
203
+ export const $Not = (a: VmAny): boolean => !$ToBoolean(a);
204
+ export const $Length = (value: VmAny): number => {
205
+ $AssertInit(value);
206
+ if (isVmArray(value)) return value.length;
207
+ if (isVmRecord(value)) return keys(value).length;
208
+ if (value instanceof VmWrapper) {
209
+ return value.keys().length;
210
+ }
211
+ return Number.NaN;
212
+ };
213
+ export const $Omit = (value: VmAny, omitted: ReadonlyArray<string | number>): VmRecord => {
214
+ $AssertInit(value);
215
+ if (value == null || !isVmRecord(value)) return {};
216
+ const result: Record<string, VmConst> = {};
217
+ const valueKeys = keys(value);
218
+ const omittedSet = new Set(omitted.map($ToString));
219
+ for (const key of valueKeys) {
220
+ if (!omittedSet.has(key)) {
221
+ result[key] = value[key] ?? null;
222
+ }
223
+ }
224
+ return result;
225
+ };
226
+ export const $Pick = (value: VmAny, picked: ReadonlyArray<string | number>): VmRecord => {
227
+ $AssertInit(value);
228
+ if (value == null || !isVmRecord(value)) return {};
229
+ const result: Record<string, VmConst> = {};
230
+ for (const key of picked) {
231
+ const k = $ToString(key);
232
+ if (hasOwnEnumerable(value, k)) {
233
+ result[k] = value[k] ?? null;
234
+ }
235
+ }
236
+ return result;
237
+ };
238
+
239
+ const sliceCore = (value: VmArray, start: number, end: number, exclusive: boolean): VmArray => {
240
+ const { length } = value;
241
+
242
+ if (isNaN(start)) start = 0;
243
+ else if (start < 0) start = length + start;
244
+ if (isNaN(end)) end = exclusive ? length : length - 1;
245
+ else if (end < 0) end = length + end;
246
+
247
+ start = ceil(start);
248
+ if (exclusive || !isSafeInteger(end)) {
249
+ end = ceil(end);
250
+ } else {
251
+ end = end + 1;
252
+ }
253
+ return slice.call(value, start, end) as VmArray;
254
+ };
255
+ export const $Slice = (value: VmAny, start: VmAny, end: VmAny): VmArray => {
256
+ $AssertInit(value);
257
+ if (!isVmArray(value)) throw new VmError(`Expected array, got ${$Type(value)}`, []);
258
+ const s = start != null ? $ToNumber(start) : 0;
259
+ const e = end != null ? $ToNumber(end) : value.length - 1;
260
+ return sliceCore(value, s, e, false);
261
+ };
262
+ export const $SliceExclusive = (value: VmAny, start: VmAny, end: VmAny): VmArray => {
263
+ $AssertInit(value);
264
+ if (!isVmArray(value)) throw new VmError(`Expected array, got ${$Type(value)}`, []);
265
+ const s = start != null ? $ToNumber(start) : 0;
266
+ const e = end != null ? $ToNumber(end) : value.length;
267
+ return sliceCore(value, s, e, true);
268
+ };
269
+ export const $AssertInit: (value: VmAny) => asserts value is VmValue = (value) => {
270
+ if (value === undefined) throw new VmError(`Uninitialized value`, null);
271
+ };
272
+ export const $Call = (func: VmValue, args: readonly VmAny[]): VmValue => {
273
+ for (const a of args) {
274
+ $AssertInit(a);
275
+ }
276
+ if (func instanceof VmExtern) {
277
+ return func.call(args as readonly VmValue[]) ?? null;
278
+ }
279
+ if (isVmFunction(func)) {
280
+ return func(...(args as readonly VmValue[])) ?? null;
281
+ }
282
+ throw new VmError(`Expected callable, got ${$Type(func)}`, null);
283
+ };
284
+ export const $Type = (value: VmAny): TypeName => {
285
+ if (value === undefined || value === null) return 'nil';
286
+ if (value instanceof VmExtern) return 'extern';
287
+ if (value instanceof VmModule) return 'module';
288
+ if (isVmArray(value)) return 'array';
289
+ if (typeof value == 'object') return 'record';
290
+ return typeof value as TypeName;
291
+ };
292
+ export const $ToBoolean = (value: VmAny): boolean => {
293
+ $AssertInit(value);
294
+ return value != null && value !== false;
295
+ };
296
+ /** 将值转为字符串 */
297
+ function numberToString(value: number): string {
298
+ if (isNaN(value)) return 'nan';
299
+ if (value === Infinity) return 'inf';
300
+ if (value === -Infinity) return '-inf';
301
+ return String(value);
302
+ }
303
+
304
+ /** 将值转为字符串 */
305
+ function innerToString(value: VmAny, useBraces: boolean): string {
306
+ if (value == null) return 'nil';
307
+ if (value instanceof VmWrapper) return value.toString();
308
+ if (typeof value == 'function') {
309
+ const name = getVmFunctionInfo(value)?.fullName;
310
+ return name ? `<function ${name}>` : `<function>`;
311
+ }
312
+ if (isVmArray(value)) {
313
+ const strings: string[] = [];
314
+ for (const item of value) {
315
+ strings.push(innerToString(item, true));
316
+ }
317
+ // 在 join 过程中会自动把 null/undefined 和 empty slot 转为 ''
318
+ // 与 innerToString 行为不一致
319
+ const results = strings.join(', ');
320
+ if (!useBraces) return results;
321
+ return `[${results}]`;
322
+ }
323
+ if (typeof value == 'object') {
324
+ const entries = keys(value)
325
+ .map((key) => `${key}: ${innerToString(value[key], true)}`)
326
+ .join(', ');
327
+ if (!useBraces) return entries;
328
+ return `(${entries})`;
329
+ }
330
+ if (typeof value == 'number') {
331
+ return numberToString(value);
332
+ }
333
+ return String(value);
334
+ }
335
+ export const $ToString = (value: VmAny): string => {
336
+ $AssertInit(value);
337
+ if (typeof value == 'string') return value;
338
+ if (value === null) return '';
339
+ return innerToString(value, false);
340
+ };
341
+ export const $ToNumber = (value: VmAny): number => {
342
+ $AssertInit(value);
343
+ if (typeof value == 'number') return value;
344
+ if (value == null) return 0;
345
+ if (typeof value == 'string') {
346
+ value = value.trim();
347
+ if (value === '') return 0;
348
+ if (value === 'inf' || value === '+inf' || value === 'Infinity' || value === '+Infinity') {
349
+ return POSITIVE_INFINITY;
350
+ }
351
+ if (value === '-inf' || value === '-Infinity') {
352
+ return NEGATIVE_INFINITY;
353
+ }
354
+ return Number(value);
355
+ }
356
+ if (typeof value == 'boolean') return value ? 1 : 0;
357
+ return Number.NaN;
358
+ };
359
+ export const $IsBoolean = (value: VmAny): value is boolean => {
360
+ $AssertInit(value);
361
+ return typeof value == 'boolean';
362
+ };
363
+ export const $IsNumber = (value: VmAny): value is number => {
364
+ $AssertInit(value);
365
+ return typeof value == 'number';
366
+ };
367
+ export const $IsString = (value: VmAny): value is string => {
368
+ $AssertInit(value);
369
+ return typeof value == 'string';
370
+ };
371
+ export const $IsRecord = (value: VmAny): value is VmRecord => {
372
+ $AssertInit(value);
373
+ return isVmRecord(value);
374
+ };
375
+ export const $IsArray = (value: VmAny): value is VmArray => {
376
+ $AssertInit(value);
377
+ return isVmArray(value);
378
+ };
379
+ export const $AssertNonNil = (value: VmAny): asserts value is NonNullable<VmValue> => {
380
+ $AssertInit(value);
381
+ if (value !== null) return;
382
+ throw new VmError('Expected non-nil value', null);
383
+ };
384
+ export const $Has = (obj: VmAny, key: VmAny): boolean => {
385
+ $AssertInit(obj);
386
+ const pk = $ToString(key);
387
+ if (obj == null || typeof obj != 'object') return false;
388
+ if (obj instanceof VmWrapper) return obj.has(pk);
389
+ return hasOwnEnumerable(obj, pk);
390
+ };
391
+ export const $Get = (obj: VmAny, key: VmAny): VmValue => {
392
+ $AssertInit(obj);
393
+ if (isVmArray(obj)) {
394
+ const index = $ToNumber(key);
395
+ if (isNaN(index)) return null;
396
+ return (at.call(obj, trunc(index)) as VmConst | undefined) ?? null;
397
+ }
398
+ const pk = $ToString(key);
399
+ if (obj == null || typeof obj != 'object') return null;
400
+ if (obj instanceof VmWrapper) return obj.get(pk) ?? null;
401
+ if (!hasOwnEnumerable(obj, pk)) return null;
402
+ return (obj as Record<string, VmImmutable>)[pk] ?? null;
403
+ };
404
+ export const $Set = (obj: VmAny, key: VmAny, value: VmAny): void => {
405
+ $AssertInit(obj);
406
+ $AssertInit(value);
407
+ const pk = $ToString(key);
408
+ if (obj == null) return;
409
+ if (!isVmExtern(obj)) throw new VmError(`Expected extern, got ${$Type(obj)}`, undefined);
410
+ obj.set(pk, value);
411
+ };
412
+ export const $Iterable = (value: VmAny): Iterable<VmValue | undefined> => {
413
+ $AssertInit(value);
414
+ if (value instanceof VmWrapper) return value.keys();
415
+ if (isVmArray(value)) return value;
416
+ if (value != null && typeof value == 'object') return keys(value);
417
+ throw new VmError(`Value is not iterable`, isVmFunction(value) ? [] : [value]);
418
+ };
419
+
420
+ export const $RecordSpread = (record: VmAny): VmRecord | null => {
421
+ $AssertInit(record);
422
+ if (record == null || isVmRecord(record)) return record;
423
+ if (isVmArray(record)) {
424
+ const result: Record<string, VmConst> = {};
425
+ const len = record.length;
426
+ for (let i = 0; i < len; i++) {
427
+ const item = record[i];
428
+ result[i] = item ?? null;
429
+ }
430
+ return result;
431
+ }
432
+ if (isVmExtern(record)) {
433
+ const result: Record<string, VmConst> = create(null);
434
+ for (const key of record.keys()) {
435
+ const value = record.get(key) ?? null;
436
+ // 当前只有 Primitive 不会进行二次包装
437
+ if (isVmPrimitive(value)) {
438
+ result[key] = value;
439
+ }
440
+ }
441
+ return result;
442
+ }
443
+ throw new VmError(`Expected record, array, extern or nil, got ${$Type(record)}`, null);
444
+ };
445
+
446
+ export const $ArraySpread = (array: VmAny): Iterable<VmConst | undefined> => {
447
+ $AssertInit(array);
448
+ if (array == null) return [];
449
+ if (isVmArray(array)) return array;
450
+ if (isVmExtern(array) && typeof (array.value as Iterable<unknown>)[Symbol.iterator] == 'function') {
451
+ const result: VmConst[] = [];
452
+ for (const item of array.value as Iterable<unknown>) {
453
+ // 当前只有 Primitive 不会进行二次包装
454
+ if (isVmPrimitive(item)) {
455
+ result.push(item);
456
+ } else {
457
+ result.push(null);
458
+ }
459
+ }
460
+ return result;
461
+ }
462
+ throw new VmError(`Expected array, iterable extern or nil, got ${$Type(array)}`, []);
463
+ };
464
+
465
+ /** 渲染数字 */
466
+ function formatNumber(value: number): string {
467
+ if (!Number.isFinite(value)) return numberToString(value);
468
+ if (value === 0) return '0';
469
+ const s = value.toString();
470
+ let ps;
471
+ const abs = Math.abs(value);
472
+ if (abs >= 1000 || abs < 0.001) {
473
+ const ps1 = value.toExponential();
474
+ const ps2 = value.toExponential(5);
475
+ ps = ps1.length < ps2.length ? ps1 : ps2;
476
+ } else {
477
+ ps = value.toPrecision(6);
478
+ }
479
+ return ps.length < s.length ? ps : s;
480
+ }
481
+
482
+ export const $Format = (value: VmAny, format: VmAny): string => {
483
+ $AssertInit(value);
484
+ const f = format == null ? '' : typeof format == 'string' ? format.trim() : $ToString(format);
485
+
486
+ if (typeof value == 'number') {
487
+ if (/^\.\d+$/.test(f)) {
488
+ let digits = Math.trunc(Number(f.slice(1)));
489
+ if (!(digits <= 100)) digits = 100;
490
+ return value.toFixed(digits);
491
+ } else {
492
+ return formatNumber(value);
493
+ }
494
+ }
495
+
496
+ return $ToString(value);
497
+ };