@mirascript/mirascript 0.1.14 → 0.1.16

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 (216) hide show
  1. package/dist/{chunk-Q74RKZ7O.js → chunk-LU4ZKFF6.js} +1420 -607
  2. package/dist/chunk-LU4ZKFF6.js.map +6 -0
  3. package/dist/chunk-RIT53WVY.js +1 -0
  4. package/dist/chunk-RLWIIOH5.js +12 -0
  5. package/dist/chunk-RLWIIOH5.js.map +6 -0
  6. package/dist/{chunk-55FKP56O.js → chunk-YZGL3D7L.js} +741 -1361
  7. package/dist/chunk-YZGL3D7L.js.map +6 -0
  8. package/dist/cli/index.js +11 -67
  9. package/dist/cli/index.js.map +2 -2
  10. package/dist/compiler/compile-fast.d.ts +1 -1
  11. package/dist/compiler/compile-fast.d.ts.map +1 -1
  12. package/dist/compiler/create-script.d.ts +10 -1
  13. package/dist/compiler/create-script.d.ts.map +1 -1
  14. package/dist/compiler/diagnostic.d.ts +1 -1
  15. package/dist/compiler/diagnostic.d.ts.map +1 -1
  16. package/dist/compiler/emit/constants.d.ts +3 -0
  17. package/dist/compiler/emit/constants.d.ts.map +1 -0
  18. package/dist/compiler/emit/consts.d.ts +6 -0
  19. package/dist/compiler/emit/consts.d.ts.map +1 -0
  20. package/dist/compiler/emit/globals.d.ts +17 -0
  21. package/dist/compiler/emit/globals.d.ts.map +1 -0
  22. package/dist/compiler/emit/index.d.ts +58 -0
  23. package/dist/compiler/emit/index.d.ts.map +1 -0
  24. package/dist/compiler/emit/sourcemap.d.ts +6 -0
  25. package/dist/compiler/emit/sourcemap.d.ts.map +1 -0
  26. package/dist/compiler/index.d.ts +3 -2
  27. package/dist/compiler/index.d.ts.map +1 -1
  28. package/dist/compiler/worker.js +1 -1
  29. package/dist/helpers/constants.d.ts +15 -0
  30. package/dist/helpers/constants.d.ts.map +1 -1
  31. package/dist/helpers/convert.d.ts +12 -0
  32. package/dist/helpers/convert.d.ts.map +1 -0
  33. package/dist/{vm → helpers}/error.d.ts +1 -1
  34. package/dist/helpers/error.d.ts.map +1 -0
  35. package/dist/helpers/serialize.d.ts +14 -5
  36. package/dist/helpers/serialize.d.ts.map +1 -1
  37. package/dist/helpers/types.d.ts +54 -0
  38. package/dist/helpers/types.d.ts.map +1 -0
  39. package/dist/index.js +11 -17
  40. package/dist/subtle.d.ts +3 -2
  41. package/dist/subtle.d.ts.map +1 -1
  42. package/dist/subtle.js +21 -15
  43. package/dist/vm/checkpoint.d.ts +9 -0
  44. package/dist/vm/checkpoint.d.ts.map +1 -0
  45. package/dist/vm/helpers.d.ts +4 -10
  46. package/dist/vm/helpers.d.ts.map +1 -1
  47. package/dist/vm/index.d.ts +3 -3
  48. package/dist/vm/index.d.ts.map +1 -1
  49. package/dist/vm/lib/global/bit.d.ts +7 -7
  50. package/dist/vm/lib/global/bit.d.ts.map +1 -1
  51. package/dist/vm/lib/global/debug.d.ts +2 -2
  52. package/dist/vm/lib/global/debug.d.ts.map +1 -1
  53. package/dist/vm/lib/global/index.d.ts +0 -1
  54. package/dist/vm/lib/global/index.d.ts.map +1 -1
  55. package/dist/vm/lib/global/json.d.ts +2 -2
  56. package/dist/vm/lib/global/json.d.ts.map +1 -1
  57. package/dist/vm/lib/global/math-additional.d.ts +1 -1
  58. package/dist/vm/lib/global/math-additional.d.ts.map +1 -1
  59. package/dist/vm/lib/global/math-arr.d.ts +5 -5
  60. package/dist/vm/lib/global/math-arr.d.ts.map +1 -1
  61. package/dist/vm/lib/global/math-unary.d.ts +26 -26
  62. package/dist/vm/lib/global/math-unary.d.ts.map +1 -1
  63. package/dist/vm/lib/global/math.d.ts +3 -3
  64. package/dist/vm/lib/global/math.d.ts.map +1 -1
  65. package/dist/vm/lib/global/sequence/all-any.d.ts +2 -2
  66. package/dist/vm/lib/global/sequence/all-any.d.ts.map +1 -1
  67. package/dist/vm/lib/global/sequence/entries.d.ts +5 -5
  68. package/dist/vm/lib/global/sequence/entries.d.ts.map +1 -1
  69. package/dist/vm/lib/global/sequence/find.d.ts +3 -3
  70. package/dist/vm/lib/global/sequence/find.d.ts.map +1 -1
  71. package/dist/vm/lib/global/sequence/flatten.d.ts +1 -1
  72. package/dist/vm/lib/global/sequence/flatten.d.ts.map +1 -1
  73. package/dist/vm/lib/global/sequence/len.d.ts +1 -1
  74. package/dist/vm/lib/global/sequence/len.d.ts.map +1 -1
  75. package/dist/vm/lib/global/sequence/map-filter.d.ts +3 -3
  76. package/dist/vm/lib/global/sequence/map-filter.d.ts.map +1 -1
  77. package/dist/vm/lib/global/sequence/repeat.d.ts +1 -1
  78. package/dist/vm/lib/global/sequence/repeat.d.ts.map +1 -1
  79. package/dist/vm/lib/global/sequence/reverse.d.ts +1 -1
  80. package/dist/vm/lib/global/sequence/reverse.d.ts.map +1 -1
  81. package/dist/vm/lib/global/sequence/sort.d.ts +2 -2
  82. package/dist/vm/lib/global/sequence/sort.d.ts.map +1 -1
  83. package/dist/vm/lib/global/sequence/with.d.ts +2 -2
  84. package/dist/vm/lib/global/sequence/with.d.ts.map +1 -1
  85. package/dist/vm/lib/global/sequence/zip.d.ts +1 -1
  86. package/dist/vm/lib/global/sequence/zip.d.ts.map +1 -1
  87. package/dist/vm/lib/global/string.d.ts +10 -10
  88. package/dist/vm/lib/global/string.d.ts.map +1 -1
  89. package/dist/vm/lib/global/time.d.ts +3 -13
  90. package/dist/vm/lib/global/time.d.ts.map +1 -1
  91. package/dist/vm/lib/global/to-primitive.d.ts +4 -4
  92. package/dist/vm/lib/global/to-primitive.d.ts.map +1 -1
  93. package/dist/vm/lib/helpers.d.ts +53 -0
  94. package/dist/vm/lib/helpers.d.ts.map +1 -0
  95. package/dist/vm/lib/index.d.ts +4 -0
  96. package/dist/vm/lib/index.d.ts.map +1 -0
  97. package/dist/vm/lib/{_loader.d.ts → loader.d.ts} +5 -5
  98. package/dist/vm/lib/loader.d.ts.map +1 -0
  99. package/dist/vm/lib/mod/index.d.ts +2 -0
  100. package/dist/vm/lib/mod/index.d.ts.map +1 -0
  101. package/dist/vm/lib/mod/matrix.d.ts +15 -0
  102. package/dist/vm/lib/mod/matrix.d.ts.map +1 -0
  103. package/dist/vm/operations.d.ts +4 -6
  104. package/dist/vm/operations.d.ts.map +1 -1
  105. package/dist/vm/types/boundary.d.ts +1 -1
  106. package/dist/vm/types/boundary.d.ts.map +1 -1
  107. package/dist/vm/types/context.d.ts +18 -12
  108. package/dist/vm/types/context.d.ts.map +1 -1
  109. package/dist/vm/types/extern.d.ts +1 -3
  110. package/dist/vm/types/extern.d.ts.map +1 -1
  111. package/dist/vm/types/function.d.ts +1 -6
  112. package/dist/vm/types/function.d.ts.map +1 -1
  113. package/dist/vm/types/index.d.ts +31 -17
  114. package/dist/vm/types/index.d.ts.map +1 -1
  115. package/dist/vm/types/module.d.ts +2 -4
  116. package/dist/vm/types/module.d.ts.map +1 -1
  117. package/dist/vm/types/wrapper.d.ts +0 -2
  118. package/dist/vm/types/wrapper.d.ts.map +1 -1
  119. package/package.json +7 -3
  120. package/src/cli/index.ts +1 -1
  121. package/src/compiler/compile-fast.ts +1 -2
  122. package/src/compiler/create-script.ts +12 -2
  123. package/src/compiler/diagnostic.ts +17 -8
  124. package/src/compiler/emit/constants.ts +2 -0
  125. package/src/compiler/emit/consts.ts +47 -0
  126. package/src/compiler/emit/globals.ts +39 -0
  127. package/src/compiler/{emit.ts → emit/index.ts} +18 -202
  128. package/src/compiler/emit/sourcemap.ts +163 -0
  129. package/src/compiler/index.ts +9 -9
  130. package/src/compiler/worker.ts +1 -1
  131. package/src/helpers/constants.ts +18 -1
  132. package/src/helpers/convert.ts +128 -0
  133. package/src/{vm → helpers}/error.ts +1 -1
  134. package/src/helpers/serialize.ts +72 -25
  135. package/src/helpers/types.ts +267 -0
  136. package/src/subtle.ts +3 -1
  137. package/src/vm/checkpoint.ts +42 -0
  138. package/src/vm/helpers.ts +9 -53
  139. package/src/vm/index.ts +3 -3
  140. package/src/vm/lib/global/bit.ts +8 -9
  141. package/src/vm/lib/global/debug.ts +5 -4
  142. package/src/vm/lib/global/index.ts +0 -1
  143. package/src/vm/lib/global/json.ts +2 -2
  144. package/src/vm/lib/global/math-additional.ts +2 -4
  145. package/src/vm/lib/global/math-arr.ts +1 -1
  146. package/src/vm/lib/global/math-unary.ts +2 -4
  147. package/src/vm/lib/global/math.ts +3 -4
  148. package/src/vm/lib/global/sequence/all-any.ts +11 -6
  149. package/src/vm/lib/global/sequence/entries.ts +3 -3
  150. package/src/vm/lib/global/sequence/find.ts +8 -7
  151. package/src/vm/lib/global/sequence/flatten.ts +2 -3
  152. package/src/vm/lib/global/sequence/len.ts +1 -1
  153. package/src/vm/lib/global/sequence/map-filter.ts +7 -6
  154. package/src/vm/lib/global/sequence/repeat.ts +2 -4
  155. package/src/vm/lib/global/sequence/reverse.ts +1 -1
  156. package/src/vm/lib/global/sequence/sort.ts +6 -5
  157. package/src/vm/lib/global/sequence/with.ts +22 -21
  158. package/src/vm/lib/global/sequence/zip.ts +4 -4
  159. package/src/vm/lib/global/string.ts +16 -27
  160. package/src/vm/lib/global/time.ts +59 -30
  161. package/src/vm/lib/global/to-primitive.ts +27 -18
  162. package/src/vm/lib/{_helpers.ts → helpers.ts} +129 -41
  163. package/src/vm/lib/index.ts +16 -0
  164. package/src/vm/lib/{_loader.ts → loader.ts} +3 -11
  165. package/src/vm/lib/mod/index.ts +1 -0
  166. package/src/vm/lib/{global/mod → mod}/matrix.ts +9 -7
  167. package/src/vm/operations.ts +33 -128
  168. package/src/vm/types/boundary.ts +10 -13
  169. package/src/vm/types/context.ts +36 -28
  170. package/src/vm/types/extern.ts +8 -15
  171. package/src/vm/types/function.ts +4 -19
  172. package/src/vm/types/index.ts +47 -25
  173. package/src/vm/types/module.ts +3 -7
  174. package/src/vm/types/wrapper.ts +1 -5
  175. package/dist/chunk-35JGBXRE.js +0 -1
  176. package/dist/chunk-55FKP56O.js.map +0 -6
  177. package/dist/chunk-Q74RKZ7O.js.map +0 -6
  178. package/dist/compiler/emit.d.ts +0 -5
  179. package/dist/compiler/emit.d.ts.map +0 -1
  180. package/dist/vm/error.d.ts.map +0 -1
  181. package/dist/vm/lib/_helpers.d.ts +0 -35
  182. package/dist/vm/lib/_helpers.d.ts.map +0 -1
  183. package/dist/vm/lib/_loader.d.ts.map +0 -1
  184. package/dist/vm/lib/global/mod/index.d.ts +0 -3
  185. package/dist/vm/lib/global/mod/index.d.ts.map +0 -1
  186. package/dist/vm/lib/global/mod/matrix.d.ts +0 -15
  187. package/dist/vm/lib/global/mod/matrix.d.ts.map +0 -1
  188. package/dist/vm/types/any.d.ts +0 -10
  189. package/dist/vm/types/any.d.ts.map +0 -1
  190. package/dist/vm/types/array.d.ts +0 -12
  191. package/dist/vm/types/array.d.ts.map +0 -1
  192. package/dist/vm/types/callable.d.ts +0 -5
  193. package/dist/vm/types/callable.d.ts.map +0 -1
  194. package/dist/vm/types/const.d.ts +0 -15
  195. package/dist/vm/types/const.d.ts.map +0 -1
  196. package/dist/vm/types/immutable.d.ts +0 -15
  197. package/dist/vm/types/immutable.d.ts.map +0 -1
  198. package/dist/vm/types/primitive.d.ts +0 -7
  199. package/dist/vm/types/primitive.d.ts.map +0 -1
  200. package/dist/vm/types/record.d.ts +0 -20
  201. package/dist/vm/types/record.d.ts.map +0 -1
  202. package/dist/vm/types/script.d.ts +0 -14
  203. package/dist/vm/types/script.d.ts.map +0 -1
  204. package/dist/vm/types/value.d.ts +0 -14
  205. package/dist/vm/types/value.d.ts.map +0 -1
  206. package/src/vm/lib/global/mod/index.ts +0 -4
  207. package/src/vm/types/any.ts +0 -33
  208. package/src/vm/types/array.ts +0 -19
  209. package/src/vm/types/callable.ts +0 -10
  210. package/src/vm/types/const.ts +0 -109
  211. package/src/vm/types/immutable.ts +0 -22
  212. package/src/vm/types/primitive.ts +0 -14
  213. package/src/vm/types/record.ts +0 -53
  214. package/src/vm/types/script.ts +0 -18
  215. package/src/vm/types/value.ts +0 -22
  216. /package/dist/{chunk-35JGBXRE.js.map → chunk-RIT53WVY.js.map} +0 -0
@@ -1,3 +1,20 @@
1
1
  export const REG_IDENTIFIER = /(?:_+|@+|\$+|\p{XID_Start})\p{XID_Continue}*/u;
2
2
  export const REG_ORDINAL =
3
- /(?:0|[1-9]\d{0,8}|1\d{9}|20\d{8}|21[0-3]\d{7}|214[0-6]\d{6}|2147[0-3]\d{5}|21474[0-7]\d{4}|214748[0-2]\d{3}|2147483[0-5]\d{2}|21474836[0-3]\d|214748364[0-7])/;
3
+ /(?:214748364[0-7]|21474836[0-3]\d|2147483[0-5]\d{2}|214748[0-2]\d{3}|21474[0-7]\d{4}|2147[0-3]\d{5}|214[0-6]\d{6}|21[0-3]\d{7}|20\d{8}|1\d{9}|[1-9]\d{0,8}|0)/;
4
+ export const REG_WHITESPACE = /[ \t\v\f\r\n]/u;
5
+ export const REG_HEX = /0[xX][a-fA-F0-9_]*[a-fA-F0-9]/;
6
+ export const REG_OCT = /0[oO][0-7_]*[0-7]/;
7
+ export const REG_BIN = /0[bB][01_]*[01]/;
8
+ export const REG_NUMBER = /\d[\d_]*(?:\.[\d_]+)?(?:[eE][+-]?[\d_]*\d)?/u;
9
+ export const VM_ARRAY_MAX_LENGTH = 2 ** 31 - 1;
10
+
11
+ export const kVmScript = Symbol.for('mirascript.vm.script');
12
+ export const kVmFunction = Symbol.for('mirascript.vm.function');
13
+ export const kVmFunctionProxy = Symbol.for('mirascript.vm.function.proxy');
14
+ export const kVmContext = Symbol.for('mirascript.vm.context');
15
+ export const kVmExtern = Symbol.for('mirascript.vm.extern');
16
+ export const kVmModule = Symbol.for('mirascript.vm.module');
17
+ export const kVmWrapper = Symbol.for('mirascript.vm.wrapper');
18
+
19
+ export const VM_SCRIPT_NAME = `<script_root>`;
20
+ export const VM_FUNCTION_ANONYMOUS_NAME = `<anonymous>`;
@@ -0,0 +1,128 @@
1
+ import type { VmAny, VmRecord } from '../vm/index.js';
2
+ import { display, displayFunction } from './serialize.js';
3
+ import { VmError } from './error.js';
4
+ import { isVmArray, isVmWrapper } from './types.js';
5
+ import { keys, isNaN, isFinite } from './utils.js';
6
+ const { POSITIVE_INFINITY, NEGATIVE_INFINITY } = Number;
7
+
8
+ /** 转换为 number */
9
+ export function toNumber<F = undefined>(value: VmAny, fallback?: F): number | Exclude<F, undefined> {
10
+ if (typeof value === 'number') {
11
+ return value;
12
+ }
13
+ if (typeof value == 'boolean') {
14
+ return value ? 1 : 0;
15
+ }
16
+ if (typeof value == 'string') {
17
+ value = value.trim();
18
+ if (value !== '') {
19
+ if (value === 'inf' || value === '+inf' || value === 'Infinity' || value === '+Infinity') {
20
+ return POSITIVE_INFINITY;
21
+ }
22
+ if (value === '-inf' || value === '-Infinity') {
23
+ return NEGATIVE_INFINITY;
24
+ }
25
+ if (value === 'nan' || value === 'NaN') {
26
+ return Number.NaN;
27
+ }
28
+ const num = Number(value);
29
+ if (!isNaN(num)) return num;
30
+ }
31
+ }
32
+ if (fallback === undefined) {
33
+ throw new VmError(`Failed to convert value to number: ${display(value)}`, Number.NaN);
34
+ }
35
+ return fallback as Exclude<F, undefined>;
36
+ }
37
+
38
+ /** 转换为 boolean */
39
+ export function toBoolean<F = undefined>(value: VmAny, fallback?: F): boolean | Exclude<F, undefined> {
40
+ if (typeof value === 'boolean') return value;
41
+ if (fallback === undefined) {
42
+ throw new VmError(`Failed to convert value to boolean: ${display(value)}`, false);
43
+ }
44
+ return fallback as Exclude<F, undefined>;
45
+ }
46
+
47
+ /** 转换为 string */
48
+ function numberToString(value: number): string {
49
+ if (isNaN(value)) return 'nan';
50
+ if (value === Infinity) return 'inf';
51
+ if (value === -Infinity) return '-inf';
52
+ return String(value);
53
+ }
54
+ /** 转换为 string */
55
+ export function innerToString(value: VmAny, useBraces: boolean): string {
56
+ if (value == null) return 'nil';
57
+ if (typeof value == 'number') return numberToString(value);
58
+ if (typeof value == 'string' || typeof value == 'boolean') return String(value);
59
+ if (typeof value == 'function') return displayFunction(value);
60
+ if (isVmWrapper(value)) return value.toString(useBraces);
61
+ if (isVmArray(value)) {
62
+ const strings: string[] = [];
63
+ for (const item of value) {
64
+ strings.push(innerToString(item, true));
65
+ }
66
+ // 在 join 过程中会自动把 null/undefined 和 empty slot 转为 ''
67
+ // 与 innerToString 行为不一致
68
+ const results = strings.join(', ');
69
+ if (!useBraces) return results;
70
+ return `[${results}]`;
71
+ } else {
72
+ const entries = keys(value satisfies VmRecord)
73
+ .map((key) => `${key}: ${innerToString(value[key], true)}`)
74
+ .join(', ');
75
+ if (!useBraces) return entries;
76
+ return `(${entries})`;
77
+ }
78
+ }
79
+
80
+ /** 转换为 string */
81
+ export function toString<F = undefined>(value: VmAny, fallback?: F): string | Exclude<F, undefined> {
82
+ if (typeof value === 'string') return value;
83
+ if (value == null) return '';
84
+ try {
85
+ return innerToString(value, false);
86
+ } catch (ex) {
87
+ if (fallback === undefined) {
88
+ const e = new VmError(`Failed to convert value to string: ${display(value)}`, '');
89
+ e.cause = ex;
90
+ throw e;
91
+ }
92
+ return fallback as Exclude<F, undefined>;
93
+ }
94
+ }
95
+
96
+ /** 渲染数字 */
97
+ function formatNumber(value: number): string {
98
+ if (!isFinite(value)) return toString(value, undefined as never);
99
+ if (value === 0) return '0';
100
+ const s = value.toString();
101
+ let ps;
102
+ const abs = Math.abs(value);
103
+ if (abs >= 1000 || abs < 0.001) {
104
+ const ps1 = value.toExponential();
105
+ const ps2 = value.toExponential(5);
106
+ ps = ps1.length < ps2.length ? ps1 : ps2;
107
+ } else {
108
+ ps = value.toPrecision(6);
109
+ }
110
+ return ps.length < s.length ? ps : s;
111
+ }
112
+
113
+ /** 格式化为 string */
114
+ export function toFormat(value: VmAny, format: string | null | undefined): string {
115
+ const f = format == null ? '' : format.trim();
116
+
117
+ if (typeof value == 'number') {
118
+ if (/^\.\d+$/.test(f)) {
119
+ let digits = Math.trunc(Number(f.slice(1)));
120
+ if (!(digits <= 100)) digits = 100;
121
+ return value.toFixed(digits);
122
+ } else {
123
+ return formatNumber(value);
124
+ }
125
+ }
126
+
127
+ return toString(value, undefined as never);
128
+ }
@@ -1,4 +1,4 @@
1
- import type { VmAny } from './types/index.js';
1
+ import type { VmAny } from '../vm/types/index.js';
2
2
 
3
3
  /**
4
4
  * VM 预期的错误
@@ -1,19 +1,16 @@
1
+ import type { VmArray, VmExtern, VmFunction, VmModule, VmAny, VmRecord } from '../vm/index.js';
2
+ import { REG_IDENTIFIER, REG_ORDINAL } from './constants.js';
3
+ import { entries, hasOwn, isFinite, isNaN } from '../helpers/utils.js';
1
4
  import {
5
+ getVmFunctionInfo,
2
6
  isVmArray,
3
- isVmRecord,
4
- type VmArray,
5
- type VmExtern,
6
- type VmFunction,
7
- type VmModule,
8
- type VmAny,
9
- type VmRecord,
7
+ isVmArrayLikeRecordByEntires,
8
+ isVmExtern,
10
9
  isVmFunction,
11
10
  isVmModule,
12
- isVmExtern,
13
- isVmArrayLikeRecordByEntires,
14
- } from '../vm/index.js';
15
- import { REG_IDENTIFIER, REG_ORDINAL } from './constants.js';
16
- import { entries, hasOwn, isFinite, isNaN } from '../helpers/utils.js';
11
+ isVmRecord,
12
+ } from './types.js';
13
+ import type { VmWrapper } from '../vm/types/wrapper.js';
17
14
 
18
15
  const REG_IDENTIFIER_FULL = new RegExp(`^${REG_IDENTIFIER.source}$`, REG_IDENTIFIER.flags);
19
16
  const REG_ORDINAL_FULL = new RegExp(`^${REG_ORDINAL.source}$`, REG_ORDINAL.flags);
@@ -43,7 +40,7 @@ export interface SerializeOptions {
43
40
  /** 序列化记录 */
44
41
  serializeRecord: (value: VmRecord, depth: number, options: SerializeOptions) => string;
45
42
  /** 序列化属性名 */
46
- serializePropName: (value: string | number, options: SerializeOptions) => string;
43
+ serializePropName: (value: number | string, options: SerializeOptions) => string;
47
44
  /** 序列化模块 */
48
45
  serializeModule: (value: VmModule, depth: number, options: SerializeOptions) => string;
49
46
  /** 序列化外部值 */
@@ -72,24 +69,33 @@ function isDefaultOptions(options: Partial<SerializeOptions> | undefined): boole
72
69
  return options == null || options === DEFAULT_OPTIONS;
73
70
  }
74
71
 
75
- /** 获取选项 */
76
- function getSerializeOptions(options: Partial<SerializeOptions> | undefined): SerializeOptions {
77
- if (isDefaultOptions(options)) return DEFAULT_OPTIONS;
72
+ /** 合并选项 */
73
+ function mergeOptions(
74
+ base: Readonly<SerializeOptions>,
75
+ options: Partial<SerializeOptions> | null | undefined,
76
+ ): Readonly<SerializeOptions> {
77
+ if (options == null) return base;
78
78
  let opt: SerializeOptions | null = null;
79
79
  for (const key in options) {
80
- if (!hasOwn(options, key) || !hasOwn(DEFAULT_OPTIONS, key)) continue;
80
+ if (!hasOwn(options, key) || !hasOwn(base, key)) continue;
81
81
  const el = options[key as keyof SerializeOptions];
82
82
  if (el == null) continue;
83
- opt ??= { ...DEFAULT_OPTIONS };
83
+ opt ??= { ...base };
84
84
  opt[key as keyof SerializeOptions] = el as never;
85
85
  }
86
- return opt ? Object.freeze(opt) : DEFAULT_OPTIONS;
86
+ return opt ? Object.freeze(opt) : base;
87
+ }
88
+
89
+ /** 获取选项 */
90
+ function getSerializeOptions(options: Partial<SerializeOptions> | undefined): Readonly<SerializeOptions> {
91
+ if (isDefaultOptions(options)) return DEFAULT_OPTIONS;
92
+ return mergeOptions(DEFAULT_OPTIONS, options);
87
93
  }
88
94
 
89
95
  /**
90
96
  * 将 MiraScript 字符串序列化为 MiraScript 字面量。
91
97
  */
92
- function serializeStringImpl(value: string, options: SerializeOptions): string {
98
+ function serializeStringImpl(value: string, options: Readonly<SerializeOptions>): string {
93
99
  if (!/[\p{C}'"`$\\]/u.test(value)) {
94
100
  // 不包含特殊字符
95
101
  const oq = options.serializeStringQuote(`'`, true, options);
@@ -153,7 +159,7 @@ function serializeRecordKeyDefault(key: string): string {
153
159
  }
154
160
 
155
161
  /** 序列化属性名 */
156
- function serializeRecordKeyOpt(value: string, options: SerializeOptions): string {
162
+ function serializeRecordKeyOpt(value: string, options: Readonly<SerializeOptions>): string {
157
163
  if (isDefaultOptions(options)) {
158
164
  return serializeRecordKeyDefault(value);
159
165
  }
@@ -199,7 +205,7 @@ export function serializeNumber(value: number): string {
199
205
  }
200
206
 
201
207
  /** 序列化数组 */
202
- export function serializeArray(value: VmArray, depth: number, options: SerializeOptions): string {
208
+ export function serializeArray(value: VmArray, depth: number, options: Readonly<SerializeOptions>): string {
203
209
  if (depth > options.maxDepth) return `[]`;
204
210
  if (value.length === 0) return '[]';
205
211
  let str = '[';
@@ -228,7 +234,7 @@ function customValueOf(value: VmRecord): VmAny | undefined {
228
234
  }
229
235
 
230
236
  /** 序列化记录 */
231
- export function serializeRecord(value: VmRecord, depth: number, options: SerializeOptions): string {
237
+ export function serializeRecord(value: VmRecord, depth: number, options: Readonly<SerializeOptions>): string {
232
238
  const customValue = customValueOf(value);
233
239
  if (customValue !== undefined) {
234
240
  return serializeImpl(customValue, depth - 1, options);
@@ -259,7 +265,7 @@ export function serializeRecord(value: VmRecord, depth: number, options: Seriali
259
265
  }
260
266
 
261
267
  /** 序列化 */
262
- function serializeImpl(value: VmAny | undefined, depth: number, options: SerializeOptions): string {
268
+ function serializeImpl(value: VmAny | undefined, depth: number, options: Readonly<SerializeOptions>): string {
263
269
  if (value == null) {
264
270
  return options.serializeNil(options);
265
271
  }
@@ -293,8 +299,49 @@ function serializeImpl(value: VmAny | undefined, depth: number, options: Seriali
293
299
  }
294
300
 
295
301
  /**
296
- * 将 MiraScript 值序列化为 MiraScript 字面量字符串,非常量值将被转换为 `nil`。
302
+ * 将 MiraScript 值序列化为 MiraScript 字面量字符串,非常量值默认转换为 `nil`。
297
303
  */
298
304
  export function serialize(value: VmAny, options?: Partial<SerializeOptions>): string {
299
305
  return serializeImpl(value, 0, getSerializeOptions(options));
300
306
  }
307
+
308
+ /** 将 MiraScript function 转化为 MiraScript 字符串 */
309
+ export function displayFunction(value: VmFunction): string {
310
+ try {
311
+ const name = getVmFunctionInfo(value)?.fullName;
312
+ return name ? `<function ${name}>` : `<function>`;
313
+ } catch {
314
+ return `<function>`;
315
+ }
316
+ }
317
+ /** 将 MiraScript module 转化为 MiraScript 字符串 */
318
+ export function displayWrapper(value: VmWrapper<object>, useBraces: boolean, fallback: string): string {
319
+ try {
320
+ return value.toString(useBraces);
321
+ } catch {
322
+ return fallback;
323
+ }
324
+ }
325
+ const DISPLAY_OPTIONS = Object.freeze({
326
+ maxDepth: 3,
327
+ serializeNil,
328
+ serializeBoolean,
329
+ serializeNumber,
330
+ serializeString: serializeStringImpl,
331
+ serializeStringQuote: (value) => value,
332
+ serializeStringEscape: (value) => value,
333
+ serializeStringContent: (value) => value,
334
+ serializeArray,
335
+ serializeRecord,
336
+ serializePropName: String,
337
+ serializeFunction: displayFunction,
338
+ serializeModule: (value) => displayWrapper(value, true, '<module>'),
339
+ serializeExtern: (value) => displayWrapper(value, true, '<extern>'),
340
+ } satisfies SerializeOptions);
341
+ /**
342
+ * 将 MiraScript 值转化为 MiraScript 字符串。
343
+ */
344
+ export function display(value: VmAny, options?: Partial<SerializeOptions>): string {
345
+ const opt = mergeOptions(DISPLAY_OPTIONS, options);
346
+ return serializeImpl(value, 0, opt);
347
+ }
@@ -0,0 +1,267 @@
1
+ import type { VmScript } from '../compiler/index.js';
2
+ import type {
3
+ VmAny,
4
+ VmArray,
5
+ VmConst,
6
+ VmContext,
7
+ VmExtern,
8
+ VmFunction,
9
+ VmFunctionInfo,
10
+ VmFunctionLike,
11
+ VmImmutable,
12
+ VmModule,
13
+ VmPrimitive,
14
+ VmRecord,
15
+ VmValue,
16
+ } from '../vm/types/index.js';
17
+ import type { VmWrapper } from '../vm/types/wrapper.js';
18
+ import {
19
+ kVmContext,
20
+ kVmExtern,
21
+ kVmFunction,
22
+ kVmModule,
23
+ kVmScript,
24
+ kVmWrapper,
25
+ VM_ARRAY_MAX_LENGTH,
26
+ } from './constants.js';
27
+ import { getPrototypeOf, isArray, keys, values } from './utils.js';
28
+
29
+ /** 检查是否为 Mirascript 脚本 */
30
+ export function isVmScript(value: unknown): value is VmScript {
31
+ return typeof value === 'function' && kVmScript in value;
32
+ }
33
+
34
+ /** 检查是否为执行上下文 */
35
+ export function isVmContext(context: unknown): context is VmContext {
36
+ return context != null && typeof context == 'object' && kVmContext in context;
37
+ }
38
+ /** 检查是否为 Mirascript 函数 */
39
+ export function isVmFunction<T extends VmFunctionLike>(value: unknown): value is VmFunction<T> {
40
+ return typeof value == 'function' && kVmFunction in value;
41
+ }
42
+ /** 检查是否为 Mirascript 函数,并获取其信息 */
43
+ export function getVmFunctionInfo(value: unknown): VmFunctionInfo | undefined {
44
+ if (typeof value != 'function') return undefined;
45
+ return (value as VmFunction)[kVmFunction];
46
+ }
47
+ /** 检查值是否为 MiraScript 包装器 */
48
+ export function isVmWrapper<T extends object>(value: unknown): value is VmWrapper<T> {
49
+ return value != null && typeof value == 'object' && kVmWrapper in value;
50
+ }
51
+ /** 检查值是否为 Mirascript 模块 */
52
+ export function isVmModule<T extends Record<string, VmImmutable>>(value: unknown): value is VmModule<T> {
53
+ return value != null && typeof value == 'object' && kVmModule in value;
54
+ }
55
+ /** 检查值是否为 Mirascript 外部值 */
56
+ export function isVmExtern<T extends object>(value: unknown): value is VmExtern<T> {
57
+ return value != null && typeof value == 'object' && kVmExtern in value;
58
+ }
59
+
60
+ /** 检查值是否为 Mirascript 可调用值 */
61
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
62
+ export function isVmCallable<E extends Function, F extends VmFunctionLike>(
63
+ value: unknown,
64
+ ): value is VmFunction<F> | VmExtern<E> {
65
+ return isVmFunction<F>(value) || (isVmExtern<E>(value) && typeof value.value == 'function');
66
+ }
67
+
68
+ /** 检查值是否为 Mirascript 原始值 */
69
+ export function isVmPrimitive(value: unknown): value is VmPrimitive {
70
+ if (value === null || typeof value == 'number' || typeof value == 'string' || typeof value == 'boolean') {
71
+ value as VmPrimitive satisfies typeof value;
72
+ value satisfies VmPrimitive;
73
+ return true;
74
+ }
75
+ return false;
76
+ }
77
+
78
+ /** 检查值是否为 Mirascript 数组 */
79
+ export function isVmArray(value: VmAny): value is VmArray {
80
+ if (!isArray(value)) return false;
81
+ value as VmArray satisfies VmArray;
82
+ return true;
83
+ }
84
+
85
+ /** 检查值是否为 Mirascript 记录 */
86
+ export function isVmRecord(value: VmAny): value is VmRecord {
87
+ if (value == null || typeof value !== 'object') return false;
88
+ if (isVmWrapper(value)) return false;
89
+ if (isVmArray(value)) return false;
90
+ value satisfies VmRecord;
91
+ return true;
92
+ }
93
+
94
+ /** 检查是否为仅包含从 0 开始的连续数字键的 MiraScript 记录 */
95
+ export function isVmArrayLikeRecordByEntires(entries: ReadonlyArray<readonly [string, unknown]>): boolean {
96
+ const { length } = entries;
97
+ if (length === 0) return true;
98
+ if (length > VM_ARRAY_MAX_LENGTH) return false;
99
+ const firstKey = entries[0]![0];
100
+ if (firstKey !== '0') return false;
101
+ const lastKey = entries[length - 1]![0];
102
+ if (lastKey !== String(length - 1)) return false;
103
+ return true;
104
+ }
105
+
106
+ /** 检查是否为仅包含从 0 开始的连续数字键的 MiraScript 记录 */
107
+ export function isVmArrayLikeRecordByKeys(keys: readonly string[]): boolean {
108
+ const { length } = keys;
109
+ if (length === 0) return true;
110
+ if (length > VM_ARRAY_MAX_LENGTH) return false;
111
+ const firstKey = keys[0]!;
112
+ if (firstKey !== '0') return false;
113
+ const lastKey = keys[length - 1]!;
114
+ if (lastKey !== String(length - 1)) return false;
115
+ return true;
116
+ }
117
+
118
+ /** 检查是否为仅包含从 0 开始的连续数字键的 MiraScript 记录 */
119
+ export function isVmArrayLikeRecord(value: VmRecord): boolean {
120
+ return isVmArrayLikeRecordByKeys(keys(value));
121
+ }
122
+
123
+ const MAX_DEPTH = 16;
124
+ /**
125
+ * 检查是否为 Mirascript 数组
126
+ */
127
+ function isVmArrayDeep(value: readonly unknown[], depth: number): value is VmArray {
128
+ // VmArray 应为普通数组
129
+ // Array.prototype
130
+ const proto1: unknown = getPrototypeOf(value);
131
+ if (!isArray(proto1)) return false;
132
+ if (!depth) return true;
133
+ return value.every((item) => isVmConstInner(item, depth));
134
+ }
135
+ /**
136
+ * 检查是否为 Mirascript 记录
137
+ */
138
+ function isVmRecordDeep(value: object, depth: number): value is VmRecord {
139
+ // VmRecord 应为普通对象或空原型对象
140
+ let isRecord;
141
+ // Object.prototype
142
+ const proto1: unknown = getPrototypeOf(value);
143
+ if (proto1 == null) {
144
+ isRecord = true;
145
+ } else {
146
+ // null
147
+ const proto2: unknown = getPrototypeOf(proto1);
148
+ if (proto2 != null) {
149
+ isRecord = false;
150
+ } else {
151
+ isRecord = 'hasOwnProperty' in value;
152
+ }
153
+ }
154
+ if (!isRecord) return false;
155
+ if (!depth) return true;
156
+ return values(value).every((value) => isVmConstInner(value, depth));
157
+ }
158
+ /**
159
+ * 检查是否为 Mirascript 值语义值
160
+ */
161
+ function isVmConstInner(value: unknown, depth: number): value is VmConst {
162
+ if (depth++ >= MAX_DEPTH) return false;
163
+ switch (typeof value) {
164
+ case 'string':
165
+ case 'number':
166
+ case 'boolean':
167
+ case 'undefined': // undefined 在复合类型内部被视为 nil
168
+ return true;
169
+ case 'object':
170
+ if (value == null) return true;
171
+ if (isVmWrapper(value)) return false;
172
+ if (isArray(value)) {
173
+ return isVmArrayDeep(value, depth);
174
+ } else {
175
+ return isVmRecordDeep(value, depth);
176
+ }
177
+ case 'function':
178
+ case 'bigint':
179
+ case 'symbol':
180
+ default:
181
+ return false; // Other types are not valid
182
+ }
183
+ }
184
+
185
+ /**
186
+ * 检查是否为 Mirascript 值语义值
187
+ */
188
+ export function isVmConst(value: VmAny): value is VmConst;
189
+ /**
190
+ * 检查是否为 Mirascript 值语义值
191
+ */
192
+ export function isVmConst(value: unknown, checkDeep: boolean): value is VmConst;
193
+ /**
194
+ * 检查是否为 Mirascript 值语义值
195
+ */
196
+ export function isVmConst(value: unknown, checkDeep = false): value is VmConst {
197
+ switch (typeof value) {
198
+ case 'string':
199
+ case 'number':
200
+ case 'boolean':
201
+ return true;
202
+ case 'object':
203
+ if (value == null) return true;
204
+ if (isVmWrapper(value)) return false;
205
+ if (!checkDeep) {
206
+ if (isArray(value)) {
207
+ return isVmArrayDeep(value, 0);
208
+ } else {
209
+ return isVmRecordDeep(value, 0);
210
+ }
211
+ } else {
212
+ return isVmConstInner(value, 1);
213
+ }
214
+ case 'undefined':
215
+ case 'function':
216
+ case 'bigint':
217
+ case 'symbol':
218
+ default:
219
+ return false; // Other types are not valid
220
+ }
221
+ }
222
+
223
+ /**
224
+ * 检查是否为 Mirascript 不可变值
225
+ */
226
+ export function isVmImmutable(value: VmAny): value is VmImmutable;
227
+ /**
228
+ * 检查是否为 Mirascript 不可变值
229
+ */
230
+ export function isVmImmutable(value: unknown, checkDeep: boolean): value is VmImmutable;
231
+ /**
232
+ * 检查是否为 Mirascript 不可变值
233
+ */
234
+ export function isVmImmutable(value: unknown, checkDeep = false): value is VmImmutable {
235
+ return isVmModule(value) || isVmFunction(value) || isVmConst(value, checkDeep);
236
+ }
237
+
238
+ /** 检查是否为 Mirascript 值 */
239
+ export function isVmAny(value: unknown, checkDeep: boolean): value is VmAny {
240
+ switch (typeof value) {
241
+ case 'string':
242
+ case 'number':
243
+ case 'boolean':
244
+ case 'undefined':
245
+ return true;
246
+ case 'object':
247
+ if (value == null) return true;
248
+ if (isVmWrapper(value)) return true;
249
+ return isVmConst(value, checkDeep);
250
+ case 'function':
251
+ return isVmFunction(value);
252
+ case 'bigint':
253
+ case 'symbol':
254
+ default:
255
+ return false; // Other types are not valid
256
+ }
257
+ }
258
+
259
+ /** 检查是否为 Mirascript 合法值 */
260
+ export function isVmValue(value: VmAny): value is VmValue;
261
+ /** 检查是否为 Mirascript 合法值 */
262
+ export function isVmValue(value: unknown, checkDeep: boolean): value is VmValue;
263
+ /** 检查是否为 Mirascript 合法值 */
264
+ export function isVmValue(value: unknown, checkDeep = false): value is VmValue {
265
+ if (value === undefined) return false;
266
+ return isVmAny(value, checkDeep);
267
+ }
package/src/subtle.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  import { getModule } from '@mirascript/bindings';
2
2
 
3
3
  export * as constants from './helpers/constants.js';
4
+ export * as convert from './helpers/convert.js';
4
5
  export { VmSharedContext, DefaultVmContext } from './vm/types/context.js';
5
6
  export * as operations from './vm/operations.js';
6
7
  export {
8
+ display as serializeForDisplay,
7
9
  serialize,
8
10
  serializeNil,
9
11
  serializeBoolean,
@@ -14,7 +16,7 @@ export {
14
16
  serializeRecord,
15
17
  type SerializeOptions,
16
18
  } from './helpers/serialize.js';
17
- export { lib } from './vm/lib/_loader.js';
19
+ export { lib } from './vm/lib/index.js';
18
20
  export * from './compiler/diagnostic.js';
19
21
  export { generateBytecode, generateBytecodeSync, emitScript } from './compiler/index.js';
20
22
 
@@ -0,0 +1,42 @@
1
+ const MAX_DEPTH = 128;
2
+
3
+ let cpDepth = 0;
4
+ let cp = Number.NaN;
5
+ let cpTimeout = 100; // Default timeout in milliseconds
6
+ /** 检查点 */
7
+ export function Cp(): void {
8
+ if (!cp) {
9
+ cp = Date.now();
10
+ } else if (Date.now() - cp > cpTimeout) {
11
+ throw new RangeError('Execution timeout');
12
+ }
13
+ }
14
+ /** 检查点 */
15
+ export function CpEnter(): void {
16
+ cpDepth++;
17
+ if (cpDepth <= 1) {
18
+ cp = Date.now();
19
+ cpDepth = 1;
20
+ } else if (cpDepth > MAX_DEPTH) {
21
+ throw new RangeError('Maximum call depth exceeded');
22
+ } else {
23
+ Cp();
24
+ }
25
+ }
26
+ /** 检查点 */
27
+ export function CpExit(): void {
28
+ cpDepth--;
29
+ if (cpDepth < 1) {
30
+ cp = Number.NaN;
31
+ cpDepth = 0;
32
+ } else {
33
+ Cp();
34
+ }
35
+ }
36
+ /** 设置检查点超时时间 */
37
+ export function configCheckpoint(timeout = 100): void {
38
+ if (typeof timeout !== 'number' || timeout <= 0 || Number.isNaN(timeout)) {
39
+ throw new RangeError('Invalid timeout value');
40
+ }
41
+ cpTimeout = timeout;
42
+ }