@mirascript/mirascript 0.1.0 → 0.1.2
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.
- package/dist/chunk-35JGBXRE.js +1 -0
- package/dist/{chunk-5FQWUJIY.js → chunk-3RUWGMBP.js} +20 -6
- package/dist/chunk-3RUWGMBP.js.map +6 -0
- package/dist/{chunk-DCXIWIW5.js → chunk-AOINGBRS.js} +306 -84
- package/dist/chunk-AOINGBRS.js.map +6 -0
- package/dist/cli/index.js +10 -10
- package/dist/cli/index.js.map +1 -1
- package/dist/compiler/diagnostic.d.ts +1 -1
- package/dist/compiler/diagnostic.d.ts.map +1 -1
- package/dist/compiler/generate-bytecode.d.ts +12 -0
- package/dist/compiler/generate-bytecode.d.ts.map +1 -0
- package/dist/compiler/index.d.ts +6 -0
- package/dist/compiler/index.d.ts.map +1 -1
- package/dist/compiler/worker.js +4 -5
- package/dist/compiler/worker.js.map +1 -1
- package/dist/helpers/utils.d.ts +1 -0
- package/dist/helpers/utils.d.ts.map +1 -1
- package/dist/index.js +18 -7
- package/dist/subtle.d.ts +1 -0
- package/dist/subtle.d.ts.map +1 -1
- package/dist/subtle.js +9 -2
- package/dist/vm/error.d.ts.map +1 -1
- package/dist/vm/lib/_helpers.d.ts.map +1 -1
- package/dist/vm/lib/global/sequence/find.d.ts.map +1 -1
- package/dist/vm/operations.d.ts.map +1 -1
- package/dist/vm/types/boundary.d.ts +12 -0
- package/dist/vm/types/boundary.d.ts.map +1 -0
- package/dist/vm/types/context.d.ts.map +1 -1
- package/dist/vm/types/extern.d.ts +5 -7
- package/dist/vm/types/extern.d.ts.map +1 -1
- package/dist/vm/types/function.d.ts +0 -4
- package/dist/vm/types/function.d.ts.map +1 -1
- package/dist/vm/types/index.d.ts +5 -2
- package/dist/vm/types/index.d.ts.map +1 -1
- package/package.json +7 -5
- package/src/compiler/diagnostic.ts +2 -1
- package/src/compiler/emit.ts +4 -0
- package/src/compiler/{compile-bytecode.ts → generate-bytecode.ts} +5 -2
- package/src/compiler/index.ts +7 -6
- package/src/compiler/worker.ts +2 -2
- package/src/helpers/utils.ts +1 -0
- package/src/subtle.ts +1 -0
- package/src/vm/error.ts +15 -3
- package/src/vm/lib/_helpers.ts +3 -3
- package/src/vm/lib/_loader.ts +4 -4
- package/src/vm/lib/global/math.ts +1 -1
- package/src/vm/lib/global/sequence/find.ts +13 -11
- package/src/vm/lib/global/sequence/map-filter.ts +2 -2
- package/src/vm/lib/global/time.ts +1 -1
- package/src/vm/operations.ts +21 -9
- package/src/vm/types/boundary.ts +95 -0
- package/src/vm/types/context.ts +4 -4
- package/src/vm/types/extern.ts +19 -55
- package/src/vm/types/function.ts +2 -37
- package/src/vm/types/index.ts +9 -2
- package/dist/chunk-5FQWUJIY.js.map +0 -6
- package/dist/chunk-BTDGMWFK.js +0 -202
- package/dist/chunk-BTDGMWFK.js.map +0 -6
- package/dist/chunk-DCXIWIW5.js.map +0 -6
- package/dist/chunk-RAPJ3XLV.js +0 -10
- package/dist/compiler/compile-bytecode.d.ts +0 -12
- package/dist/compiler/compile-bytecode.d.ts.map +0 -1
- /package/dist/{chunk-RAPJ3XLV.js.map → chunk-35JGBXRE.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"function.d.ts","sourceRoot":"","sources":["../../../src/vm/types/function.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAGjD,QAAA,MAAM,WAAW,eAAuC,CAAC;
|
|
1
|
+
{"version":3,"file":"function.d.ts","sourceRoot":"","sources":["../../../src/vm/types/function.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAGjD,QAAA,MAAM,WAAW,eAAuC,CAAC;AAEzD;;;;GAIG;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,GAAG,IAAI,EAAE,aAAa,CAAC,OAAO,GAAG,SAAS,CAAC,KAAK,KAAK,GAAG,IAAI,CAAC;AAE3F,oBAAoB;AACpB,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,cAAc,GAAG,cAAc,IAAI,CAAC,GAAG;IAAE,QAAQ,CAAC,CAAC,WAAW,CAAC,EAAE,cAAc,CAAA;CAAE,CAAC;AAEnH,sBAAsB;AACtB,MAAM,WAAW,cAAc;IAC3B,WAAW;IACX,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,aAAa;IACb,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,YAAY;IACZ,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY;IACZ,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,YAAY;IACZ,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,YAAY;IACZ,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY;IACZ,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,YAAY;IACZ,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,oBAAoB;IACpB,QAAQ,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAC;CACtC;AAED,wBAAwB;AACxB,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAClC,IAAI,CAAC,cAAc,EAAE,UAAU,CAAC,GAAG;IAC/B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC9B,CACJ,CAAC;AAEF,0BAA0B;AAC1B,wBAAgB,YAAY,CAAC,CAAC,SAAS,cAAc,EAAE,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAAC,CAAC,CAAC,CAE7F;AAED,iCAAiC;AACjC,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,GAAG,SAAS,CAG5E;AAED,uBAAuB;AACvB,wBAAgB,UAAU,CAAC,CAAC,SAAS,cAAc,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,GAAE,gBAAqB,GAAG,UAAU,CAAC,CAAC,CAAC,CAwCxG"}
|
package/dist/vm/types/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { VmExtern } from './extern.js';
|
|
2
|
-
import type
|
|
2
|
+
import { type VmFunction } from './function.js';
|
|
3
3
|
import { VmModule } from './module.js';
|
|
4
4
|
/** Mirascript 原始值 */
|
|
5
5
|
export type VmPrimitive = null | string | number | boolean;
|
|
@@ -61,9 +61,12 @@ export declare function isVmRecord(value: VmAny): value is VmRecord;
|
|
|
61
61
|
* 检查值是否为 Mirascript 原始值
|
|
62
62
|
*/
|
|
63
63
|
export declare function isVmPrimitive(value: unknown): value is VmPrimitive;
|
|
64
|
-
export { VmExtern
|
|
64
|
+
export { VmExtern } from './extern.js';
|
|
65
|
+
export { wrapToVmValue, unwrapFromVmValue, toVmFunctionProxy, fromVmFunctionProxy } from './boundary.js';
|
|
65
66
|
/** 检查值是否为 Mirascript 外部值 */
|
|
66
67
|
export declare function isVmExtern(value: unknown): value is VmExtern;
|
|
68
|
+
/** 检查值是否为 Mirascript 可调用值 */
|
|
69
|
+
export declare function isVmCallable(value: unknown): value is VmFunction | VmExtern<Function>;
|
|
67
70
|
export { VmFunction, isVmFunction, getVmFunctionInfo, type VmFunctionInfo, type VmFunctionLike, type VmFunctionOption, } from './function.js';
|
|
68
71
|
export { VmModule } from './module.js';
|
|
69
72
|
/** 检查值是否为 Mirascript 模块 */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/vm/types/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/vm/types/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAGvC,qBAAqB;AACrB,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAC3D;;;;GAIG;AACH,MAAM,MAAM,QAAQ,GAAG;IACnB,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;CAC/C,CAAC;AACF;;;GAGG;AACH,MAAM,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;AAEzD,2BAA2B;AAC3B,MAAM,MAAM,OAAO,GAAG,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEvD,2BAA2B;AAC3B,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,UAAU,GAAG,QAAQ,CAAC;AAE1D,0BAA0B;AAC1B,MAAM,MAAM,OAAO,GAAG,WAAW,GAAG,QAAQ,CAAC;AAE7C,6BAA6B;AAC7B,MAAM,MAAM,eAAe,GAAG,SAAS,CAAC;AAExC,kCAAkC;AAClC,MAAM,MAAM,KAAK,GAAG,OAAO,GAAG,eAAe,CAAC;AAE9C,WAAW;AACX,MAAM,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC;AAExC,aAAa;AACb,MAAM,WAAW,UAAU;IACvB,SAAS;IACT,GAAG,EAAE,IAAI,CAAC;IACV,UAAU;IACV,MAAM,EAAE,MAAM,CAAC;IACf,SAAS;IACT,MAAM,EAAE,MAAM,CAAC;IACf,UAAU;IACV,OAAO,EAAE,OAAO,CAAC;IACjB,oBAAoB;IACpB,MAAM,EAAE,QAAQ,CAAC;IACjB,oBAAoB;IACpB,KAAK,EAAE,OAAO,CAAC;IACf,oBAAoB;IACpB,QAAQ,EAAE,UAAU,CAAC;IACrB,aAAa;IACb,MAAM,EAAE,QAAQ,CAAC;IACjB,oBAAoB;IACpB,MAAM,EAAE,QAAQ,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,IAAI,OAAO,CAIxD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,IAAI,QAAQ,CAM1D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAOlE;AAED,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEzG,4BAA4B;AAC5B,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAE5D;AAED,6BAA6B;AAE7B,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAErF;AAED,OAAO,EACH,UAAU,EACV,YAAY,EACZ,iBAAiB,EACjB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,gBAAgB,GACxB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,2BAA2B;AAC3B,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAE5D;AAED,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,eAAe,EAAE,WAAW,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAExH,OAAO,EAAE,KAAK,QAAQ,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAExD,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE5E,eAAO,MAAM,mBAAmB,QAAc,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mirascript/mirascript",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "An expression based scripting language.",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -33,16 +33,18 @@
|
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@commander-js/extra-typings": "^14.0.0",
|
|
35
35
|
"ansi-styles": "^6.2.3",
|
|
36
|
-
"commander": "^14.0.
|
|
36
|
+
"commander": "^14.0.2",
|
|
37
37
|
"js-base64": "^3.7.8",
|
|
38
|
-
"@mirascript/
|
|
39
|
-
"@mirascript/
|
|
38
|
+
"@mirascript/napi": "~0.1.2",
|
|
39
|
+
"@mirascript/wasm": "~0.1.2"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
+
"@types/node": "^24.9.2",
|
|
42
43
|
"@types/sinon": "^17.0.4",
|
|
43
44
|
"ava": "^6.4.1",
|
|
44
45
|
"c8": "^10.1.3",
|
|
45
|
-
"sinon": "^21.0.0"
|
|
46
|
+
"sinon": "^21.0.0",
|
|
47
|
+
"type-fest": "^5.1.0"
|
|
46
48
|
},
|
|
47
49
|
"scripts": {
|
|
48
50
|
"watch": "pnpm build:ts --watch",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Writable } from 'type-fest';
|
|
1
2
|
import { DiagnosticCode, wasm } from '@mirascript/wasm';
|
|
2
3
|
import type { ScriptInput } from './types.js';
|
|
3
4
|
import { isSafeInteger } from '../helpers/utils.js';
|
|
@@ -59,7 +60,7 @@ export interface SourceReference<T extends DiagnosticCode = DiagnosticCode> exte
|
|
|
59
60
|
readonly diagnostic: SourceDiagnostic<T>;
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
/**
|
|
63
|
+
/** 分析诊断信息,{@link diagnostic_position_encoding} 不能设为 `None` */
|
|
63
64
|
export function parseDiagnostics(
|
|
64
65
|
source: ScriptInput,
|
|
65
66
|
diagnostics: Uint32Array,
|
package/src/compiler/emit.ts
CHANGED
|
@@ -43,7 +43,7 @@ function checkModule(): void {
|
|
|
43
43
|
/**
|
|
44
44
|
* 生成 MiraScript 字节码
|
|
45
45
|
*/
|
|
46
|
-
export function
|
|
46
|
+
export function generateBytecodeSync(
|
|
47
47
|
script: ScriptInput,
|
|
48
48
|
options: CompileOptions,
|
|
49
49
|
): [Uint8Array | undefined, Uint32Array] {
|
|
@@ -55,10 +55,13 @@ export function compileBytecodeSync(
|
|
|
55
55
|
/**
|
|
56
56
|
* 生成 MiraScript 字节码
|
|
57
57
|
*/
|
|
58
|
-
export async function
|
|
58
|
+
export async function generateBytecode(
|
|
59
59
|
script: ScriptInput,
|
|
60
60
|
options: CompileOptions,
|
|
61
61
|
): Promise<[Uint8Array | undefined, Uint32Array]> {
|
|
62
|
+
if (options == null) {
|
|
63
|
+
throw new TypeError('options must be provided');
|
|
64
|
+
}
|
|
62
65
|
await loadModule();
|
|
63
66
|
const result = 'compile' in module ? await module.compile(script, options) : module.compileSync(script, options);
|
|
64
67
|
return [result.chunk, result.diagnostics];
|
package/src/compiler/index.ts
CHANGED
|
@@ -5,10 +5,11 @@ import { emit } from './emit.js';
|
|
|
5
5
|
import { createScript } from './create-script.js';
|
|
6
6
|
import { compileFast } from './compile-fast.js';
|
|
7
7
|
import { formatDiagnostic, parseDiagnostics } from './diagnostic.js';
|
|
8
|
-
import {
|
|
8
|
+
import { generateBytecode, generateBytecodeSync, loadModule } from './generate-bytecode.js';
|
|
9
9
|
import { compileWorker } from './worker-manager.js';
|
|
10
10
|
await loadModule();
|
|
11
11
|
|
|
12
|
+
export { generateBytecode, generateBytecodeSync };
|
|
12
13
|
export type { TranspileOptions, ScriptInput, InputMode } from './types.js';
|
|
13
14
|
|
|
14
15
|
// 目前编译速度约 2000kB/s
|
|
@@ -24,7 +25,7 @@ function reportDiagnostic(source: ScriptInput, diagnostics: Uint32Array): never
|
|
|
24
25
|
/**
|
|
25
26
|
* 生成 MiraScript 对应的 JavaScript 代码
|
|
26
27
|
*/
|
|
27
|
-
function
|
|
28
|
+
export function emitScript(
|
|
28
29
|
source: ScriptInput,
|
|
29
30
|
[code, diagnostics]: [Uint8Array | undefined, Uint32Array],
|
|
30
31
|
options: TranspileOptions,
|
|
@@ -45,8 +46,8 @@ export async function compile(this: void, source: ScriptInput, options: Transpil
|
|
|
45
46
|
if (result) return result;
|
|
46
47
|
}
|
|
47
48
|
if (source.length < WORKER_MIN_LEN) {
|
|
48
|
-
const bc = await
|
|
49
|
-
return
|
|
49
|
+
const bc = await generateBytecode(source, options);
|
|
50
|
+
return emitScript(source, bc, options);
|
|
50
51
|
}
|
|
51
52
|
const [target, diagnostics] = await compileWorker(source, options);
|
|
52
53
|
if (target == null) {
|
|
@@ -62,6 +63,6 @@ export function compileSync(this: void, source: ScriptInput, options: TranspileO
|
|
|
62
63
|
const result = compileFast(source, options);
|
|
63
64
|
if (result) return result;
|
|
64
65
|
}
|
|
65
|
-
const bc =
|
|
66
|
-
return
|
|
66
|
+
const bc = generateBytecodeSync(source, options);
|
|
67
|
+
return emitScript(source, bc, options);
|
|
67
68
|
}
|
package/src/compiler/worker.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ScriptInput, TranspileOptions } from './types.js';
|
|
2
2
|
import { emit } from './emit.js';
|
|
3
|
-
import {
|
|
3
|
+
import { generateBytecode } from './generate-bytecode.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* 生成 MiraScript 对应的 JavaScript 代码
|
|
@@ -9,7 +9,7 @@ export async function compile(
|
|
|
9
9
|
script: ScriptInput,
|
|
10
10
|
options: TranspileOptions,
|
|
11
11
|
): Promise<[string | undefined, Uint32Array]> {
|
|
12
|
-
const [bytecode, errors] = await
|
|
12
|
+
const [bytecode, errors] = await generateBytecode(script, options);
|
|
13
13
|
if (bytecode == null) {
|
|
14
14
|
return [undefined, errors];
|
|
15
15
|
}
|
package/src/helpers/utils.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export const { isArray } = Array;
|
|
2
2
|
export const { isFinite, isNaN, isInteger, isSafeInteger } = Number;
|
|
3
3
|
export const { hasOwn, keys, values, entries, create, getPrototypeOf, fromEntries, defineProperty } = Object;
|
|
4
|
+
export const { apply } = Reflect;
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Determines whether an object has an enumerable property with the specified name.
|
package/src/subtle.ts
CHANGED
|
@@ -4,3 +4,4 @@ export * as operations from './vm/operations.js';
|
|
|
4
4
|
export { serialize, serializeString, serializePropName, type SerializeOptions } from './helpers/serialize.js';
|
|
5
5
|
export { lib } from './vm/lib/_loader.js';
|
|
6
6
|
export * from './compiler/diagnostic.js';
|
|
7
|
+
export { generateBytecode, generateBytecodeSync, emitScript } from './compiler/index.js';
|
package/src/vm/error.ts
CHANGED
|
@@ -14,9 +14,21 @@ export class VmError extends Error {
|
|
|
14
14
|
|
|
15
15
|
/** 从其他错误构造 */
|
|
16
16
|
static from(prefix: string, error: unknown, recovered: VmAny): VmError {
|
|
17
|
-
if (prefix
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
if (prefix) {
|
|
18
|
+
if (prefix.endsWith(':')) {
|
|
19
|
+
prefix += ' ';
|
|
20
|
+
} else if (!prefix.endsWith(': ')) {
|
|
21
|
+
prefix += ': ';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
let vmError: VmError;
|
|
25
|
+
if (error instanceof Error) {
|
|
26
|
+
vmError = new VmError(`${prefix}${error.message}`, recovered);
|
|
27
|
+
vmError.stack = error.stack;
|
|
28
|
+
} else {
|
|
29
|
+
vmError = new VmError(`${prefix}${String(error)}`, recovered);
|
|
30
|
+
}
|
|
31
|
+
vmError.cause = error;
|
|
20
32
|
return vmError;
|
|
21
33
|
}
|
|
22
34
|
}
|
package/src/vm/lib/_helpers.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import type { Writable } from 'type-fest';
|
|
1
2
|
import { VmError } from '../error.js';
|
|
2
3
|
import { $ToNumber, $Type } from '../operations.js';
|
|
3
4
|
import {
|
|
4
5
|
isVmArray,
|
|
5
|
-
isVmExtern,
|
|
6
6
|
isVmFunction,
|
|
7
7
|
type VmExtern,
|
|
8
8
|
type VmFunction,
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
type VmConst,
|
|
17
17
|
isVmConst,
|
|
18
18
|
VM_ARRAY_MAX_LENGTH,
|
|
19
|
+
isVmCallable,
|
|
19
20
|
} from '../types/index.js';
|
|
20
21
|
import type { VmFunctionLike, VmFunctionOption } from '../types/function.js';
|
|
21
22
|
import { Cp } from '../helpers.js';
|
|
@@ -126,8 +127,7 @@ export function expectCallable(
|
|
|
126
127
|
recovered: VmAny | (() => VmAny),
|
|
127
128
|
): asserts value is VmFunction | VmExtern {
|
|
128
129
|
required(name, value, recovered);
|
|
129
|
-
|
|
130
|
-
if (!callable) {
|
|
130
|
+
if (!isVmCallable(value)) {
|
|
131
131
|
throwUnexpectedTypeError(name, 'callable', value, recovered);
|
|
132
132
|
}
|
|
133
133
|
}
|
package/src/vm/lib/_loader.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { VmLib, VmLibOption } from './_helpers.js';
|
|
|
6
6
|
import * as global from './global/index.js';
|
|
7
7
|
|
|
8
8
|
for (const [name, value] of entries(global)) {
|
|
9
|
-
VmSharedContext[name] = wrapEntry(name, value as RawValue);
|
|
9
|
+
VmSharedContext[name] = wrapEntry(name, value as RawValue, 'global');
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
/** 原始值 */
|
|
@@ -14,7 +14,7 @@ type RawValue = VmLib | VmConst | VmModule;
|
|
|
14
14
|
/** 包装值 */
|
|
15
15
|
type ToWrappedValue<V extends RawValue> = V extends VmFunctionLike ? VmFunction<V> : V;
|
|
16
16
|
/** 包装值 */
|
|
17
|
-
function wrapEntry<const T extends RawValue>(name: string, value: T): ToWrappedValue<T> {
|
|
17
|
+
function wrapEntry<const T extends RawValue>(name: string, value: T, module: string): ToWrappedValue<T> {
|
|
18
18
|
if (typeof value == 'function') {
|
|
19
19
|
if (value.name !== name) {
|
|
20
20
|
// 如果函数名和导出名不一致,则重命名
|
|
@@ -26,7 +26,7 @@ function wrapEntry<const T extends RawValue>(name: string, value: T): ToWrappedV
|
|
|
26
26
|
return VmFunction(value, {
|
|
27
27
|
isLib: true,
|
|
28
28
|
injectCp: true,
|
|
29
|
-
fullName:
|
|
29
|
+
fullName: `${module}.${name}`,
|
|
30
30
|
...(value as VmLibOption),
|
|
31
31
|
}) as ToWrappedValue<T>;
|
|
32
32
|
} else {
|
|
@@ -43,7 +43,7 @@ export type ToWrappedModule<T extends Record<string, RawValue>> = VmModule<{
|
|
|
43
43
|
export function createModule<const T extends Record<string, RawValue>>(name: string, lib: T): ToWrappedModule<T> {
|
|
44
44
|
const mod = create(null) as Record<string, VmImmutable>;
|
|
45
45
|
for (const [key, value] of entries(lib)) {
|
|
46
|
-
mod[key] = wrapEntry(key, value);
|
|
46
|
+
mod[key] = wrapEntry(key, value, name);
|
|
47
47
|
}
|
|
48
48
|
return new VmModule(name, mod) as ToWrappedModule<T>;
|
|
49
49
|
}
|
|
@@ -15,7 +15,7 @@ export const pow = VmLib((x, y) => _pow($ToNumber(x), $ToNumber(y)), {
|
|
|
15
15
|
returnsType: 'number',
|
|
16
16
|
});
|
|
17
17
|
export const random = VmLib(Math.random, {
|
|
18
|
-
summary: '返回 0
|
|
18
|
+
summary: '返回 [0, 1) 之间的伪随机数',
|
|
19
19
|
params: {},
|
|
20
20
|
paramsType: {},
|
|
21
21
|
returnsType: 'number',
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { entries } from '../../../../helpers/utils.js';
|
|
2
2
|
import { Cp } from '../../../helpers.js';
|
|
3
|
-
import { $Call, $ToBoolean } from '../../../operations.js';
|
|
4
|
-
import { type VmValue, isVmArray } from '../../../types/index.js';
|
|
5
|
-
import { VmLib, expectArrayOrRecord,
|
|
3
|
+
import { $Call, $Same, $ToBoolean } from '../../../operations.js';
|
|
4
|
+
import { type VmValue, isVmArray, isVmCallable } from '../../../types/index.js';
|
|
5
|
+
import { VmLib, expectArrayOrRecord, required } from '../../_helpers.js';
|
|
6
6
|
|
|
7
7
|
export const find = VmLib(
|
|
8
8
|
(data, predicate) => {
|
|
9
9
|
expectArrayOrRecord('data', data, null);
|
|
10
|
-
|
|
11
|
-
const p = (
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
required('predicate', predicate, null);
|
|
11
|
+
const p = isVmCallable(predicate)
|
|
12
|
+
? (value: VmValue, key: string | number, data: VmValue) => {
|
|
13
|
+
const ret = $Call(predicate, [value, key, data]);
|
|
14
|
+
return $ToBoolean(ret);
|
|
15
|
+
}
|
|
16
|
+
: (value: VmValue) => $Same(predicate, value);
|
|
15
17
|
if (isVmArray(data)) {
|
|
16
18
|
const { length } = data;
|
|
17
19
|
for (let i = 0; i < length; i++) {
|
|
@@ -37,13 +39,13 @@ export const find = VmLib(
|
|
|
37
39
|
summary: '查找数组或记录中的键值对,返回第一个满足条件的键值对',
|
|
38
40
|
params: {
|
|
39
41
|
data: '查的数组或记录',
|
|
40
|
-
predicate: '
|
|
42
|
+
predicate: '用于测试每个键值对的函数,或要查找的值',
|
|
41
43
|
},
|
|
42
44
|
paramsType: {
|
|
43
45
|
data: 'array | record',
|
|
44
|
-
predicate: 'fn(value: any, key: number | string | nil, input: type(data)) -> boolean',
|
|
46
|
+
predicate: '(fn(value: any, key: number | string | nil, input: type(data)) -> boolean) | any',
|
|
45
47
|
},
|
|
46
48
|
returnsType: '(string | number, any) | nil',
|
|
47
|
-
examples: ['find([3, 5, 8], fn (v) { v % 2 == 0 }) // (2, 8)'],
|
|
49
|
+
examples: ['find([3, 5, 8], fn (v) { v % 2 == 0 }) // (2, 8)', `find((x: 1, y: 2, z: 3), 2) // ('y', 2)`],
|
|
48
50
|
},
|
|
49
51
|
);
|
|
@@ -49,7 +49,7 @@ export const filter = VmLib(
|
|
|
49
49
|
summary: '过滤数组或记录中的元素,返回满足条件的元素',
|
|
50
50
|
params: {
|
|
51
51
|
data: '要过滤的数组或记录',
|
|
52
|
-
predicate: '
|
|
52
|
+
predicate: '用于测试每个元素的函数',
|
|
53
53
|
},
|
|
54
54
|
paramsType: {
|
|
55
55
|
data: 'array | record',
|
|
@@ -70,7 +70,7 @@ export const filter_map = VmLib(
|
|
|
70
70
|
summary: '对数组或记录中的每个元素应用函数,并返回非 nil 的结果',
|
|
71
71
|
params: {
|
|
72
72
|
data: '要映射的数组或记录',
|
|
73
|
-
f: '
|
|
73
|
+
f: '应用于每个元素的函数',
|
|
74
74
|
},
|
|
75
75
|
paramsType: {
|
|
76
76
|
data: 'array | record',
|
package/src/vm/operations.ts
CHANGED
|
@@ -37,11 +37,11 @@ const isSame = (a: VmValue, b: VmValue): boolean => {
|
|
|
37
37
|
if (b instanceof VmWrapper) return b.same(a);
|
|
38
38
|
// Handle array values
|
|
39
39
|
if (isVmArray(a) && isVmArray(b)) {
|
|
40
|
-
|
|
41
|
-
if (
|
|
40
|
+
const len = a.length;
|
|
41
|
+
if (len !== b.length) {
|
|
42
42
|
return false;
|
|
43
43
|
}
|
|
44
|
-
|
|
44
|
+
// Compare array items
|
|
45
45
|
for (let i = 0; i < len; i++) {
|
|
46
46
|
if (!isSame(a[i] ?? null, b[i] ?? null)) {
|
|
47
47
|
return false;
|
|
@@ -138,7 +138,9 @@ export const $Eq = (a: VmAny, b: VmAny): boolean => {
|
|
|
138
138
|
if (typeof a == 'number' && typeof b == 'number') return a === b;
|
|
139
139
|
return isSame(a, b);
|
|
140
140
|
};
|
|
141
|
-
export const $Neq = (a: VmAny, b: VmAny): boolean =>
|
|
141
|
+
export const $Neq = (a: VmAny, b: VmAny): boolean => {
|
|
142
|
+
return !$Eq(a, b);
|
|
143
|
+
};
|
|
142
144
|
const stringComparer = new Intl.Collator('en', {
|
|
143
145
|
usage: 'sort',
|
|
144
146
|
numeric: false,
|
|
@@ -164,13 +166,17 @@ export const $Aeq = (a: VmAny, b: VmAny): boolean => {
|
|
|
164
166
|
return stringComparer.compare(as, bs) === 0;
|
|
165
167
|
}
|
|
166
168
|
};
|
|
167
|
-
export const $Naeq = (a: VmAny, b: VmAny): boolean =>
|
|
169
|
+
export const $Naeq = (a: VmAny, b: VmAny): boolean => {
|
|
170
|
+
return !$Aeq(a, b);
|
|
171
|
+
};
|
|
168
172
|
export const $Same = (a: VmAny, b: VmAny): boolean => {
|
|
169
173
|
$AssertInit(a);
|
|
170
174
|
$AssertInit(b);
|
|
171
175
|
return isSame(a, b);
|
|
172
176
|
};
|
|
173
|
-
export const $Nsame = (a: VmAny, b: VmAny): boolean =>
|
|
177
|
+
export const $Nsame = (a: VmAny, b: VmAny): boolean => {
|
|
178
|
+
return !$Same(a, b);
|
|
179
|
+
};
|
|
174
180
|
export const $In = (value: VmAny, iterable: VmAny): boolean => {
|
|
175
181
|
$AssertInit(value);
|
|
176
182
|
$AssertInit(iterable);
|
|
@@ -198,9 +204,15 @@ export const $In = (value: VmAny, iterable: VmAny): boolean => {
|
|
|
198
204
|
export const $Concat = (...args: string[]): string => {
|
|
199
205
|
return args.map($Format).join('');
|
|
200
206
|
};
|
|
201
|
-
export const $Pos = (a: VmAny): number =>
|
|
202
|
-
|
|
203
|
-
|
|
207
|
+
export const $Pos = (a: VmAny): number => {
|
|
208
|
+
return $ToNumber(a);
|
|
209
|
+
};
|
|
210
|
+
export const $Neg = (a: VmAny): number => {
|
|
211
|
+
return -$ToNumber(a);
|
|
212
|
+
};
|
|
213
|
+
export const $Not = (a: VmAny): boolean => {
|
|
214
|
+
return !$ToBoolean(a);
|
|
215
|
+
};
|
|
204
216
|
export const $Length = (value: VmAny): number => {
|
|
205
217
|
$AssertInit(value);
|
|
206
218
|
if (isVmArray(value)) return value.length;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { isVmFunction, type VmFunctionLike, type VmFunction } from './function.js';
|
|
2
|
+
import { VmExtern } from './extern.js';
|
|
3
|
+
import { VmWrapper } from './wrapper.js';
|
|
4
|
+
import type { VmAny, VmConst, VmModule, VmPrimitive, VmValue } from './index.js';
|
|
5
|
+
import { $Call } from '../operations.js';
|
|
6
|
+
import { defineProperty, apply } from '../../helpers/utils.js';
|
|
7
|
+
|
|
8
|
+
const kProxy = Symbol.for('mirascript.vm.function.proxy');
|
|
9
|
+
|
|
10
|
+
/** 创建 Mirascript 函数在宿主语言运行的代理 */
|
|
11
|
+
export function toVmFunctionProxy<T extends VmFunctionLike>(fn: VmFunction<T>): T {
|
|
12
|
+
if (!isVmFunction(fn)) return fn;
|
|
13
|
+
|
|
14
|
+
const cached = (fn as unknown as { [kProxy]?: T })[kProxy];
|
|
15
|
+
if (cached != null) return cached;
|
|
16
|
+
|
|
17
|
+
const proxy = (...args: unknown[]) => {
|
|
18
|
+
const ret = $Call(
|
|
19
|
+
fn,
|
|
20
|
+
// 作为函数参数传入的值一定丢失了它的 this
|
|
21
|
+
args.map((v) => wrapToVmValue(v, null)),
|
|
22
|
+
);
|
|
23
|
+
return unwrapFromVmValue(ret);
|
|
24
|
+
};
|
|
25
|
+
defineProperty(fn, kProxy, { value: proxy });
|
|
26
|
+
defineProperty(proxy, kProxy, { value: fn });
|
|
27
|
+
defineProperty(proxy, 'name', {
|
|
28
|
+
value: fn.name,
|
|
29
|
+
configurable: true,
|
|
30
|
+
});
|
|
31
|
+
return proxy as T;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** 解开 Mirascript 函数在宿主语言运行的代理 */
|
|
35
|
+
export function fromVmFunctionProxy<T extends VmFunctionLike>(fn: T): VmFunction<T> | undefined {
|
|
36
|
+
if (isVmFunction(fn)) return fn;
|
|
37
|
+
|
|
38
|
+
const original = (fn as unknown as { [kProxy]?: VmFunction<T> })[kProxy];
|
|
39
|
+
if (original && isVmFunction(original)) return original;
|
|
40
|
+
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** 将宿主语言的值包装为 Mirascript 类型 */
|
|
45
|
+
export function wrapToVmValue(
|
|
46
|
+
value: unknown,
|
|
47
|
+
thisArg: VmExtern | null = null,
|
|
48
|
+
assumeVmValue?: (obj: object) => obj is Exclude<VmConst, VmPrimitive>,
|
|
49
|
+
): VmValue {
|
|
50
|
+
if (value == null) return null;
|
|
51
|
+
switch (typeof value) {
|
|
52
|
+
case 'function': {
|
|
53
|
+
const unwrapped = fromVmFunctionProxy(value as VmFunctionLike);
|
|
54
|
+
if (unwrapped) return unwrapped;
|
|
55
|
+
return new VmExtern(value as () => never, thisArg);
|
|
56
|
+
}
|
|
57
|
+
case 'object': {
|
|
58
|
+
if (value instanceof VmWrapper) return value as VmModule | VmExtern;
|
|
59
|
+
if (value instanceof Date) return value.valueOf();
|
|
60
|
+
if (assumeVmValue?.(value)) return value;
|
|
61
|
+
// Only functions preserve thisArg
|
|
62
|
+
return new VmExtern(value);
|
|
63
|
+
}
|
|
64
|
+
case 'string':
|
|
65
|
+
case 'number':
|
|
66
|
+
case 'boolean':
|
|
67
|
+
return value;
|
|
68
|
+
case 'bigint':
|
|
69
|
+
return Number(value);
|
|
70
|
+
case 'symbol':
|
|
71
|
+
case 'undefined':
|
|
72
|
+
default:
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** 取消宿主语言的值的 Mirascript 包装 */
|
|
78
|
+
export function unwrapFromVmValue(value: VmAny): unknown {
|
|
79
|
+
if (typeof value == 'function') {
|
|
80
|
+
return toVmFunctionProxy(value);
|
|
81
|
+
}
|
|
82
|
+
if (value == null || typeof value != 'object') return value;
|
|
83
|
+
if (!(value instanceof VmExtern)) return value;
|
|
84
|
+
|
|
85
|
+
if (value.thisArg == null || typeof value.value != 'function') {
|
|
86
|
+
return value.value;
|
|
87
|
+
}
|
|
88
|
+
const f = value as VmExtern<(...args: unknown[]) => unknown>;
|
|
89
|
+
const caller = f.thisArg!.value;
|
|
90
|
+
return new Proxy(f.value, {
|
|
91
|
+
apply(target, thisArg, args): unknown {
|
|
92
|
+
return apply(target, caller, args);
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
}
|
package/src/vm/types/context.ts
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
isVmAny,
|
|
8
8
|
type VmFunctionLike,
|
|
9
9
|
} from './index.js';
|
|
10
|
-
import { entries, keys } from '../../helpers/utils.js';
|
|
10
|
+
import { create, entries, keys } from '../../helpers/utils.js';
|
|
11
11
|
import type * as global from '../lib/global/index.js';
|
|
12
12
|
|
|
13
13
|
/** 全局导入的标准库 */
|
|
@@ -36,7 +36,7 @@ export interface VmContext {
|
|
|
36
36
|
}
|
|
37
37
|
/** MiraScript 执行上下文 */
|
|
38
38
|
export type VmContextRecord = Record<string, VmValue | undefined>;
|
|
39
|
-
export const VmSharedContext =
|
|
39
|
+
export const VmSharedContext = create(null) as VmSharedContext;
|
|
40
40
|
|
|
41
41
|
/** 定义在所有 MiraScript 执行上下文中共享的全局变量 */
|
|
42
42
|
export function defineVmContextValue(
|
|
@@ -91,7 +91,7 @@ class ValueVmContext implements VmContext {
|
|
|
91
91
|
has(key: string): boolean {
|
|
92
92
|
return key in this.env;
|
|
93
93
|
}
|
|
94
|
-
constructor(private readonly env: VmContextRecord
|
|
94
|
+
constructor(private readonly env: VmContextRecord) {}
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
/** 以工厂函数为后备的实现 */
|
|
@@ -139,7 +139,7 @@ export function createVmContext(
|
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
const [vmValues, externValues] = args;
|
|
142
|
-
const env =
|
|
142
|
+
const env = create(VmSharedContext) as VmContextRecord;
|
|
143
143
|
if (vmValues) {
|
|
144
144
|
for (const [key, value] of entries(vmValues)) {
|
|
145
145
|
if (!isVmAny(value, false)) continue;
|