@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,67 @@
1
+ import type { VmScript } from '../vm/index.js';
2
+ import type { ScriptInput, TranspileOptions } from './types.js';
3
+ import './types.js';
4
+ import { emit } from './emit.js';
5
+ import { createScript } from './create-script.js';
6
+ import { compileFast } from './compile-fast.js';
7
+ import { formatDiagnostic, parseDiagnostics } from './diagnostic.js';
8
+ import { compileBytecode, compileBytecodeSync, loadModule } from './compile-bytecode.js';
9
+ import { compileWorker } from './worker-manager.js';
10
+ await loadModule();
11
+
12
+ export type { TranspileOptions, ScriptInput, InputMode } from './types.js';
13
+
14
+ // 目前编译速度约 2000kB/s
15
+ const WORKER_MIN_LEN = typeof Worker != 'function' ? Number.MAX_VALUE : 1024;
16
+
17
+ /** 报告编译错误 */
18
+ function reportDiagnostic(source: ScriptInput, diagnostics: Uint32Array): never {
19
+ const parsed = parseDiagnostics(source, diagnostics);
20
+ const messages = parsed.errors.map(formatDiagnostic);
21
+ throw new Error(`Failed to compile:\n${messages.join('\n')}`);
22
+ }
23
+
24
+ /**
25
+ * 生成 MiraScript 对应的 JavaScript 代码
26
+ */
27
+ function emitImpl(
28
+ source: ScriptInput,
29
+ [code, diagnostics]: [Uint8Array | undefined, Uint32Array],
30
+ options: TranspileOptions,
31
+ ): VmScript {
32
+ if (!code) {
33
+ reportDiagnostic(source, diagnostics);
34
+ }
35
+ const target = emit(source, code, options);
36
+ return createScript(source, target);
37
+ }
38
+
39
+ /**
40
+ * 生成 MiraScript 对应的 JavaScript 代码
41
+ */
42
+ export async function compile(this: void, source: ScriptInput, options: TranspileOptions = {}): Promise<VmScript> {
43
+ if (typeof source == 'string') {
44
+ const result = compileFast(source, options);
45
+ if (result) return result;
46
+ }
47
+ if (source.length < WORKER_MIN_LEN) {
48
+ const bc = await compileBytecode(source, options);
49
+ return emitImpl(source, bc, options);
50
+ }
51
+ const [target, diagnostics] = await compileWorker(source, options);
52
+ if (target == null) {
53
+ reportDiagnostic(source, diagnostics);
54
+ }
55
+ return createScript(source, target);
56
+ }
57
+ /**
58
+ * 生成 MiraScript 对应的 JavaScript 代码
59
+ */
60
+ export function compileSync(this: void, source: ScriptInput, options: TranspileOptions = {}): VmScript {
61
+ if (typeof source == 'string') {
62
+ const result = compileFast(source, options);
63
+ if (result) return result;
64
+ }
65
+ const bc = compileBytecodeSync(source, options);
66
+ return emitImpl(source, bc, options);
67
+ }
@@ -0,0 +1,16 @@
1
+ import type { Config } from '@mirascript/wasm/types';
2
+ export type { InputMode, ScriptInput } from '@mirascript/wasm/types';
3
+
4
+ /** 代码编译选项 */
5
+ export type CompileOptions = Config;
6
+ /** 代码生成选项 */
7
+ export interface GenerateOptions {
8
+ /** 是否美化代码 */
9
+ readonly pretty?: boolean;
10
+ /** 是否生成源映射 */
11
+ readonly sourceMap?: boolean;
12
+ /** 代码文件名 */
13
+ readonly fileName?: string;
14
+ }
15
+ /** 转换选项 */
16
+ export type TranspileOptions = GenerateOptions & CompileOptions;
@@ -0,0 +1,60 @@
1
+ import type { compile } from './worker.js';
2
+
3
+ let worker: Promise<Worker> | undefined;
4
+ /** 获取 worker */
5
+ async function getWorker(): Promise<Worker> {
6
+ if (worker) return worker;
7
+ worker = new Promise((resolve, reject) => {
8
+ const w = new Worker(new URL('#compiler/worker', import.meta.url), {
9
+ type: 'module',
10
+ name: '@mirascript/compiler',
11
+ });
12
+ const onError = (e: ErrorEvent) => {
13
+ cleanUp();
14
+ reject(new Error(`Worker failed to start: ${e.message}`));
15
+ };
16
+ const onMessage = (e: MessageEvent<'ready' | Error>) => {
17
+ if (e.data === 'ready') {
18
+ cleanUp();
19
+ resolve(w);
20
+ } else if (e.data instanceof Error) {
21
+ cleanUp();
22
+ reject(e.data);
23
+ }
24
+ };
25
+ w.addEventListener('error', onError);
26
+ w.addEventListener('message', onMessage);
27
+ const cleanUp = () => {
28
+ w.removeEventListener('error', onError);
29
+ w.removeEventListener('message', onMessage);
30
+ };
31
+ setTimeout(() => {
32
+ onError(new ErrorEvent('error', { message: 'Worker did not respond in time' }));
33
+ }, 30000);
34
+ });
35
+ return worker;
36
+ }
37
+
38
+ /**
39
+ * 生成 MiraScript 对应的 JavaScript 代码
40
+ */
41
+ export async function compileWorker(...args: Parameters<typeof compile>): Promise<[string | undefined, Uint32Array]> {
42
+ const worker = await getWorker();
43
+ const seq = Math.random();
44
+ worker.postMessage([seq, ...args]);
45
+ return await new Promise<[string | undefined, Uint32Array]>((resolve, reject) => {
46
+ const callback = (ev: MessageEvent) => {
47
+ const data = ev.data as [number, string | undefined, Uint32Array] | [number, Error];
48
+ if (!Array.isArray(data)) return;
49
+ const [retSeq, ...rest] = data;
50
+ if (seq !== retSeq) return; // Ignore messages not matching the request
51
+ worker.removeEventListener('message', callback);
52
+ if (rest.length === 2) {
53
+ resolve(rest);
54
+ } else {
55
+ reject(rest[0]);
56
+ }
57
+ };
58
+ worker.addEventListener('message', callback);
59
+ });
60
+ }
@@ -0,0 +1,37 @@
1
+ import type { ScriptInput, TranspileOptions } from './types.js';
2
+ import { emit } from './emit.js';
3
+ import { compileBytecode } from './compile-bytecode.js';
4
+
5
+ /**
6
+ * 生成 MiraScript 对应的 JavaScript 代码
7
+ */
8
+ export async function compile(
9
+ script: ScriptInput,
10
+ options: TranspileOptions,
11
+ ): Promise<[string | undefined, Uint32Array]> {
12
+ const [bytecode, errors] = await compileBytecode(script, options);
13
+ if (bytecode == null) {
14
+ return [undefined, errors];
15
+ }
16
+ const generatedCode = emit(script, bytecode, options);
17
+ return [generatedCode, errors];
18
+ }
19
+
20
+ addEventListener('message', (ev) => {
21
+ const data = ev.data as [number, ...Parameters<typeof compile>];
22
+ if (!Array.isArray(data)) return;
23
+ const [seq, ...args] = data;
24
+ if (typeof seq != 'number' || !args.length) return;
25
+ void compile(...args)
26
+ .then(([script, errors]) => {
27
+ postMessage([seq, script, errors], { transfer: [errors.buffer] });
28
+ })
29
+ .catch((error) => {
30
+ postMessage([seq, error instanceof Error ? error : new Error(String(error))]);
31
+ });
32
+ });
33
+
34
+ compile('{}', {}).then(
35
+ () => postMessage('ready'),
36
+ (ex) => postMessage(ex),
37
+ );
@@ -0,0 +1,3 @@
1
+ export const REG_IDENTIFIER = /(?:_+|@+|\$+|\p{XID_Start})\p{XID_Continue}*/u;
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])/;
@@ -0,0 +1,280 @@
1
+ import {
2
+ isVmArray,
3
+ isVmRecord,
4
+ type VmArray,
5
+ type VmExtern,
6
+ type VmFunction,
7
+ type VmModule,
8
+ type VmAny,
9
+ type VmRecord,
10
+ isVmFunction,
11
+ isVmModule,
12
+ isVmExtern,
13
+ } from '../vm/index.js';
14
+ import { REG_IDENTIFIER, REG_ORDINAL } from './constants.js';
15
+ import { entries, isFinite, isNaN } from '../helpers/utils.js';
16
+
17
+ const REG_IDENTIFIER_FULL = new RegExp(`^${REG_IDENTIFIER.source}$`, REG_IDENTIFIER.flags);
18
+ const REG_ORDINAL_FULL = new RegExp(`^${REG_ORDINAL.source}$`, REG_ORDINAL.flags);
19
+
20
+ /** 序列化设置 */
21
+ export interface SerializeOptions {
22
+ /** 最大递归深度,超过该深度的值将被序列化为 `nil`,默认值为 128 */
23
+ maxDepth: number;
24
+ /** 序列化 nil 值 */
25
+ serializeNil: (options: SerializeOptions) => string;
26
+ /** 序列化布尔值 */
27
+ serializeBoolean: (value: boolean, options: SerializeOptions) => string;
28
+ /** 序列化数字 */
29
+ serializeNumber: (value: number, options: SerializeOptions) => string;
30
+ /** 序列化字符串 */
31
+ serializeString: (value: string, options: SerializeOptions) => string;
32
+ /** 序列化字符串引号 */
33
+ serializeStringQuote: (value: string, open: boolean, options: SerializeOptions) => string;
34
+ /** 序列化字符串转义序列 */
35
+ serializeStringEscape: (value: string, options: SerializeOptions) => string;
36
+ /** 序列化字符串常规内容 */
37
+ serializeStringContent: (value: string, options: SerializeOptions) => string;
38
+ /** 序列化函数 */
39
+ serializeFunction: (value: VmFunction, options: SerializeOptions) => string;
40
+ /** 序列化数组 */
41
+ serializeArray: (value: VmArray, depth: number, options: SerializeOptions) => string;
42
+ /** 序列化记录 */
43
+ serializeRecord: (value: VmRecord, depth: number, options: SerializeOptions) => string;
44
+ /** 序列化属性名 */
45
+ serializePropName: (value: string | number, options: SerializeOptions) => string;
46
+ /** 序列化模块 */
47
+ serializeModule: (value: VmModule, depth: number, options: SerializeOptions) => string;
48
+ /** 序列化外部值 */
49
+ serializeExtern: (value: VmExtern, depth: number, options: SerializeOptions) => string;
50
+ }
51
+
52
+ const DEFAULT_OPTIONS = Object.freeze({
53
+ maxDepth: 128,
54
+ serializeNil,
55
+ serializeBoolean,
56
+ serializeNumber,
57
+ serializeString: serializeStringImpl,
58
+ serializeStringQuote: (value) => value,
59
+ serializeStringEscape: (value) => value,
60
+ serializeStringContent: (value) => value,
61
+ serializeArray,
62
+ serializeRecord,
63
+ serializePropName: String,
64
+ serializeFunction: serializeNil,
65
+ serializeModule: serializeNil,
66
+ serializeExtern: serializeNil,
67
+ } satisfies SerializeOptions);
68
+
69
+ /** 获取选项 */
70
+ export function getSerializeOptions(options: Partial<SerializeOptions> | undefined): SerializeOptions {
71
+ if (options == null) return DEFAULT_OPTIONS;
72
+ const opt = { ...DEFAULT_OPTIONS };
73
+ for (const key in options) {
74
+ const el = options[key as keyof SerializeOptions];
75
+ if (el != null) {
76
+ opt[key as keyof SerializeOptions] = el as never;
77
+ }
78
+ }
79
+ return Object.freeze(opt);
80
+ }
81
+
82
+ /**
83
+ * 将 MiraScript 字符串序列化为 MiraScript 字面量。
84
+ */
85
+ function serializeStringImpl(value: string, options: SerializeOptions): string {
86
+ if (!/[\p{C}'"`$\\]/u.test(value)) {
87
+ // 不包含特殊字符
88
+ const oq = options.serializeStringQuote(`'`, true, options);
89
+ const cq = options.serializeStringQuote(`'`, false, options);
90
+ const c = options.serializeStringContent(value, options);
91
+ return oq + c + cq;
92
+ }
93
+ let ret = options.serializeStringQuote(`'`, true, options);
94
+ for (const char of value) {
95
+ if (char === "'") {
96
+ ret += options.serializeStringEscape(String.raw`\'`, options);
97
+ } else if (char === '\0') {
98
+ ret += options.serializeStringEscape(String.raw`\0`, options);
99
+ } else if (char === '\n') {
100
+ ret += options.serializeStringEscape(String.raw`\n`, options);
101
+ } else if (char === '\r') {
102
+ ret += options.serializeStringEscape(String.raw`\r`, options);
103
+ } else if (char === '\t') {
104
+ ret += options.serializeStringEscape(String.raw`\t`, options);
105
+ } else if (char === '\b') {
106
+ ret += options.serializeStringEscape(String.raw`\b`, options);
107
+ } else if (char === '\f') {
108
+ ret += options.serializeStringEscape(String.raw`\f`, options);
109
+ } else if (char === '\v') {
110
+ ret += options.serializeStringEscape(String.raw`\v`, options);
111
+ } else if (char === '\\') {
112
+ ret += options.serializeStringEscape(String.raw`\\`, options);
113
+ } else if (char === '$') {
114
+ ret += options.serializeStringEscape(String.raw`\$`, options);
115
+ } else if (/\p{C}/u.test(char)) {
116
+ const code = char.codePointAt(0)!;
117
+ if (code <= 0x7f) {
118
+ ret += options.serializeStringEscape(String.raw`\x${code.toString(16).padStart(2, '0')}`, options);
119
+ } else if (code >= 0xd800 && code <= 0xdfff) {
120
+ // 无效的代理对
121
+ ret += options.serializeStringContent('�', options);
122
+ } else {
123
+ ret += options.serializeStringEscape(String.raw`\u{${code.toString(16)}}`, options);
124
+ }
125
+ } else {
126
+ ret += options.serializeStringContent(char, options); // 普通字符直接添加
127
+ }
128
+ }
129
+ ret += options.serializeStringQuote(`'`, false, options);
130
+ return ret;
131
+ }
132
+
133
+ /**
134
+ * 将 MiraScript 字符串序列化为 MiraScript 字面量。
135
+ */
136
+ export function serializeString(value: string, options?: Partial<SerializeOptions>): string {
137
+ return serializeStringImpl(value, getSerializeOptions(options));
138
+ }
139
+
140
+ /** 序列化属性名 */
141
+ function serializePropNameImpl(value: string, options: SerializeOptions): string {
142
+ if (REG_ORDINAL_FULL.test(value)) {
143
+ // 合法的数字属性名
144
+ return options.serializePropName(Number(value), options);
145
+ }
146
+ if (REG_IDENTIFIER_FULL.test(value)) {
147
+ // 合法的标识符
148
+ return options.serializePropName(value, options);
149
+ }
150
+ // 否则,序列化为字符串
151
+ return options.serializeString(value, options);
152
+ }
153
+
154
+ /** 序列化属性名 */
155
+ export function serializePropName(value: string, options?: Partial<SerializeOptions>): string {
156
+ return serializePropNameImpl(value, getSerializeOptions(options));
157
+ }
158
+
159
+ /** 序列化 nil 值 */
160
+ function serializeNil(): string {
161
+ return 'nil';
162
+ }
163
+
164
+ /** 序列化布尔值 */
165
+ function serializeBoolean(value: boolean): string {
166
+ return value ? 'true' : 'false';
167
+ }
168
+
169
+ /** 序列化数字 */
170
+ function serializeNumber(value: number): string {
171
+ if (isNaN(value)) return 'nan';
172
+ if (!isFinite(value)) return value < 0 ? '-inf' : 'inf';
173
+ if (value === 0) {
174
+ if (1 / value < 0) return '-0';
175
+ return '0';
176
+ }
177
+ return String(value);
178
+ }
179
+
180
+ /** 序列化数组 */
181
+ function serializeArray(value: VmArray, depth: number, options: SerializeOptions): string {
182
+ if (depth > options.maxDepth) return `[]`;
183
+ if (value.length === 0) return '[]';
184
+ let str = '[';
185
+ for (let i = 0; i < value.length; i++) {
186
+ if (i > 0) str += ', ';
187
+ str += serializeImpl(value[i], depth, options);
188
+ }
189
+ str += ']';
190
+ return str;
191
+ }
192
+
193
+ // eslint-disable-next-line @typescript-eslint/unbound-method
194
+ const { valueOf } = Object.prototype;
195
+ /**
196
+ * 如果值有自定义的 valueOf 方法,调用它并返回结果,否则返回 undefined。
197
+ */
198
+ function customValueOf(value: VmRecord): VmAny | undefined {
199
+ // eslint-disable-next-line @typescript-eslint/unbound-method
200
+ const thisValueOf = value.valueOf;
201
+ if (typeof thisValueOf != 'function' || thisValueOf === valueOf) {
202
+ return undefined;
203
+ }
204
+ const customValue = thisValueOf.call(value) as VmAny | undefined;
205
+ if (customValue === value) return undefined;
206
+ return customValue;
207
+ }
208
+
209
+ /** 序列化记录 */
210
+ function serializeRecord(value: VmRecord, depth: number, options: SerializeOptions): string {
211
+ const customValue = customValueOf(value);
212
+ if (customValue !== undefined) {
213
+ return serializeImpl(customValue, depth - 1, options);
214
+ }
215
+ if (depth > options.maxDepth) return `()`;
216
+ const e = entries(value);
217
+ if (e.length === 0) return '()';
218
+ if (e.length === 1) {
219
+ const [k, v] = e[0]!;
220
+ if (k === '0') {
221
+ return `(${serializeImpl(v, depth, options)},)`; // 单个元素数组
222
+ }
223
+ return `(${serializePropNameImpl(k, options)}: ${serializeImpl(v, depth, options)})`;
224
+ }
225
+
226
+ // 根据 ES 标准,数字 key 会按顺序枚举
227
+ const omitKey = e.length < 10 && e.every(([key], index) => key === String(index));
228
+ let str = '(';
229
+ for (const [key, val] of e) {
230
+ if (str.length > 1) str += ', ';
231
+ if (omitKey) {
232
+ str += serializeImpl(val, depth, options);
233
+ } else {
234
+ str += `${serializePropNameImpl(key, options)}: ${serializeImpl(val, depth, options)}`;
235
+ }
236
+ }
237
+ str += ')';
238
+ return str;
239
+ }
240
+
241
+ /** 序列化 */
242
+ function serializeImpl(value: VmAny | undefined, depth: number, options: SerializeOptions): string {
243
+ if (value == null) {
244
+ return options.serializeNil(options);
245
+ }
246
+ if (typeof value == 'boolean') {
247
+ return options.serializeBoolean(value, options);
248
+ }
249
+ if (typeof value == 'number') {
250
+ return options.serializeNumber(value, options);
251
+ }
252
+ if (typeof value == 'string') {
253
+ return options.serializeString(value, options);
254
+ }
255
+ if (isVmFunction(value)) {
256
+ return options.serializeFunction(value, options);
257
+ }
258
+ if (isVmModule(value)) {
259
+ return options.serializeModule(value, depth + 1, options);
260
+ }
261
+ if (isVmExtern(value)) {
262
+ return options.serializeExtern(value, depth + 1, options);
263
+ }
264
+ if (isVmArray(value)) {
265
+ return options.serializeArray(value, depth + 1, options);
266
+ }
267
+ if (isVmRecord(value)) {
268
+ return options.serializeRecord(value, depth + 1, options);
269
+ }
270
+ // 不支持序列化的值
271
+ value satisfies never;
272
+ return options.serializeNil(options);
273
+ }
274
+
275
+ /**
276
+ * 将 MiraScript 值序列化为 MiraScript 字面量字符串,非常量值将被转换为 `nil`。
277
+ */
278
+ export function serialize(value: VmAny, options?: Partial<SerializeOptions>): string {
279
+ return serializeImpl(value, 0, getSerializeOptions(options));
280
+ }
@@ -0,0 +1,16 @@
1
+ export const { isArray } = Array;
2
+ export const { isFinite, isNaN, isInteger, isSafeInteger } = Number;
3
+ export const { hasOwn, keys, values, entries, create, getPrototypeOf, fromEntries, defineProperty } = Object;
4
+
5
+ /**
6
+ * Determines whether an object has an enumerable property with the specified name.
7
+ */
8
+ export const hasOwnEnumerable = Function.call.bind<
9
+ object['propertyIsEnumerable'],
10
+ [],
11
+ [o: object, v: PropertyKey],
12
+ boolean
13
+ >(
14
+ // eslint-disable-next-line @typescript-eslint/unbound-method
15
+ Object.prototype.propertyIsEnumerable,
16
+ );
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { serialize } from './helpers/serialize.js';
2
+ export * from './compiler/index.js';
3
+ export * from './vm/index.js';
package/src/subtle.ts ADDED
@@ -0,0 +1,6 @@
1
+ export * as constants from './helpers/constants.js';
2
+ export { VmSharedContext, DefaultVmContext } from './vm/types/context.js';
3
+ export * as operations from './vm/operations.js';
4
+ export { serialize, serializeString, serializePropName, type SerializeOptions } from './helpers/serialize.js';
5
+ export { lib } from './vm/lib/_loader.js';
6
+ export * from './compiler/diagnostic.js';
package/src/vm/env.ts ADDED
@@ -0,0 +1,16 @@
1
+ import * as operations from './operations.js';
2
+ import * as helpers from './helpers.js';
3
+ import { entries } from '../helpers/utils.js';
4
+
5
+ export const keys: string[] = [];
6
+ export const values: unknown[] = [];
7
+
8
+ for (const [key, value] of entries(operations)) {
9
+ keys.push(key);
10
+ values.push(value);
11
+ }
12
+
13
+ for (const [key, value] of entries(helpers)) {
14
+ keys.push(key);
15
+ values.push(value);
16
+ }
@@ -0,0 +1,22 @@
1
+ import type { VmAny } from './types/index.js';
2
+
3
+ /**
4
+ * VM 预期的错误
5
+ */
6
+ export class VmError extends Error {
7
+ constructor(
8
+ message: string,
9
+ readonly recovered: VmAny,
10
+ ) {
11
+ super(message);
12
+ this.name = 'VmError';
13
+ }
14
+
15
+ /** 从其他错误构造 */
16
+ static from(prefix: string, error: unknown, recovered: VmAny): VmError {
17
+ if (prefix && !prefix.endsWith(': ')) prefix += ': ';
18
+ const vmError = new VmError(`${prefix}${error instanceof Error ? error.message : String(error)}`, recovered);
19
+ vmError.stack = error instanceof Error ? error.stack : undefined;
20
+ return vmError;
21
+ }
22
+ }
@@ -0,0 +1,121 @@
1
+ import { $AssertInit, $ToNumber } from './operations.js';
2
+ import type { VmFunctionLike } from './types/function.js';
3
+ import { DefaultVmContext, type VmContext } from './types/context.js';
4
+ import {
5
+ isVmConst,
6
+ VmFunction,
7
+ type VmConst,
8
+ type VmAny,
9
+ type VmArray,
10
+ type VmValue,
11
+ VM_ARRAY_MAX_LENGTH,
12
+ } from './types/index.js';
13
+ import { isFinite } from '../helpers/utils.js';
14
+
15
+ export const Vargs = (varags: VmAny[]): VmArray => {
16
+ for (let i = 0, l = varags.length; i < l; i++) {
17
+ const el = varags[i];
18
+ if (!isVmConst(el)) {
19
+ varags[i] = null;
20
+ }
21
+ }
22
+ return varags as VmArray;
23
+ };
24
+ export const Element = (value: VmAny): VmConst => {
25
+ $AssertInit(value);
26
+ if (!isVmConst(value)) return null;
27
+ return value;
28
+ };
29
+
30
+ export const ElementOpt = (key: string, value: VmAny): VmConst => {
31
+ $AssertInit(value);
32
+ if (value == null || !isVmConst(value)) return {};
33
+ return { [key]: value };
34
+ };
35
+
36
+ export const Function = (fn: VmFunctionLike): VmFunction => {
37
+ return VmFunction(fn, { isLib: false, injectCp: false });
38
+ };
39
+
40
+ export const Upvalue = (value: VmAny): VmValue => {
41
+ $AssertInit(value);
42
+ return value;
43
+ };
44
+
45
+ const assertArrayLength = (start: number, end: number) => {
46
+ if (end - start > VM_ARRAY_MAX_LENGTH) {
47
+ throw new RangeError(`Array length exceeds maximum limit of ${VM_ARRAY_MAX_LENGTH}`);
48
+ }
49
+ };
50
+ const isEmptyRange = (start: number, end: number) => {
51
+ return !isFinite(start) || !isFinite(end) || start > end;
52
+ };
53
+ export const ArrayRange = (start: VmAny, end: VmAny): VmArray => {
54
+ const s = $ToNumber(start);
55
+ const e = $ToNumber(end);
56
+ if (isEmptyRange(s, e)) return [];
57
+ assertArrayLength(s, e);
58
+ const arr = [];
59
+ for (let i = s; i <= e; i++) {
60
+ arr.push(i);
61
+ }
62
+ return arr;
63
+ };
64
+ export const ArrayRangeExclusive = (start: VmAny, end: VmAny): VmArray => {
65
+ const s = $ToNumber(start);
66
+ const e = $ToNumber(end);
67
+ if (isEmptyRange(s, e)) return [];
68
+ assertArrayLength(s, e);
69
+ const arr = [];
70
+ for (let i = s; i < e; i++) {
71
+ arr.push(i);
72
+ }
73
+ return arr;
74
+ };
75
+
76
+ const MAX_DEPTH = 128;
77
+
78
+ let cpDepth = 0;
79
+ let cp = Number.NaN;
80
+ let cpTimeout = 100; // Default timeout in milliseconds
81
+ /** 检查点 */
82
+ export function Cp(): void {
83
+ if (!cp) {
84
+ cp = Date.now();
85
+ } else if (Date.now() - cp > cpTimeout) {
86
+ throw new RangeError('Execution timeout');
87
+ }
88
+ }
89
+ /** 检查点 */
90
+ export function CpEnter(): void {
91
+ cpDepth++;
92
+ if (cpDepth <= 1) {
93
+ cp = Date.now();
94
+ cpDepth = 1;
95
+ } else if (cpDepth > MAX_DEPTH) {
96
+ throw new RangeError('Maximum call depth exceeded');
97
+ } else {
98
+ Cp();
99
+ }
100
+ }
101
+ /** 检查点 */
102
+ export function CpExit(): void {
103
+ cpDepth--;
104
+ if (cpDepth < 1) {
105
+ cp = Number.NaN;
106
+ cpDepth = 0;
107
+ } else {
108
+ Cp();
109
+ }
110
+ }
111
+ /** 设置检查点超时时间 */
112
+ export function configCheckpoint(timeout = 100): void {
113
+ if (typeof timeout !== 'number' || timeout <= 0 || Number.isNaN(timeout)) {
114
+ throw new RangeError('Invalid timeout value');
115
+ }
116
+ cpTimeout = timeout;
117
+ }
118
+ /** 默认执行上下文 */
119
+ export function GlobalFallback(): VmContext {
120
+ return DefaultVmContext;
121
+ }
@@ -0,0 +1,5 @@
1
+ import './lib/_loader.js';
2
+
3
+ export * from './error.js';
4
+ export * from './types/index.js';
5
+ export { configCheckpoint } from './helpers.js';