@mirascript/mirascript 0.1.13 → 0.1.15
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-JVFUK7AN.js +2324 -0
- package/dist/chunk-JVFUK7AN.js.map +6 -0
- package/dist/{chunk-IKSSUVRE.js → chunk-NT235HY3.js} +97 -1264
- package/dist/chunk-NT235HY3.js.map +6 -0
- package/dist/cli/index.js +8 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/compiler/emit.d.ts.map +1 -1
- package/dist/compiler/worker.js +1 -1
- package/dist/helpers/constants.d.ts +5 -0
- package/dist/helpers/constants.d.ts.map +1 -1
- package/dist/helpers/serialize.d.ts +2 -2
- package/dist/helpers/serialize.d.ts.map +1 -1
- package/dist/index.js +15 -9
- package/dist/subtle.d.ts +1 -1
- package/dist/subtle.d.ts.map +1 -1
- package/dist/subtle.js +7 -7
- package/dist/vm/lib/_helpers.d.ts +8 -8
- package/dist/vm/lib/global/debug.d.ts +1 -1
- package/dist/vm/lib/global/json.d.ts +2 -2
- package/dist/vm/lib/global/json.d.ts.map +1 -1
- package/dist/vm/lib/global/mod/matrix.d.ts +1 -1
- package/dist/vm/lib/global/mod/matrix.d.ts.map +1 -1
- package/dist/vm/lib/global/sequence/all-any.d.ts +2 -2
- package/dist/vm/lib/global/sequence/entries.d.ts +4 -4
- package/dist/vm/lib/global/sequence/find.d.ts +2 -2
- package/dist/vm/lib/global/sequence/repeat.d.ts +1 -1
- package/dist/vm/lib/global/sequence/with.d.ts +1 -1
- package/dist/vm/lib/global/sequence/with.d.ts.map +1 -1
- package/dist/vm/lib/global/sequence/zip.d.ts +1 -1
- package/dist/vm/operations.d.ts +4 -2
- package/dist/vm/operations.d.ts.map +1 -1
- package/dist/vm/types/any.d.ts +10 -0
- package/dist/vm/types/any.d.ts.map +1 -0
- package/dist/vm/types/array.d.ts +12 -0
- package/dist/vm/types/array.d.ts.map +1 -0
- package/dist/vm/types/callable.d.ts +5 -0
- package/dist/vm/types/callable.d.ts.map +1 -0
- package/dist/vm/types/const.d.ts +15 -0
- package/dist/vm/types/const.d.ts.map +1 -0
- package/dist/vm/types/context.d.ts +8 -3
- package/dist/vm/types/context.d.ts.map +1 -1
- package/dist/vm/types/extern.d.ts +1 -1
- package/dist/vm/types/extern.d.ts.map +1 -1
- package/dist/vm/types/immutable.d.ts +15 -0
- package/dist/vm/types/immutable.d.ts.map +1 -0
- package/dist/vm/types/index.d.ts +17 -47
- package/dist/vm/types/index.d.ts.map +1 -1
- package/dist/vm/types/primitive.d.ts +7 -0
- package/dist/vm/types/primitive.d.ts.map +1 -0
- package/dist/vm/types/record.d.ts +20 -0
- package/dist/vm/types/record.d.ts.map +1 -0
- package/dist/vm/types/value.d.ts +14 -0
- package/dist/vm/types/value.d.ts.map +1 -0
- package/dist/vm/types/wrapper.d.ts +1 -1
- package/dist/vm/types/wrapper.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/compiler/compile-fast.ts +1 -1
- package/src/compiler/emit.ts +131 -17
- package/src/helpers/constants.ts +6 -1
- package/src/helpers/serialize.ts +35 -15
- package/src/subtle.ts +1 -1
- package/src/vm/lib/_helpers.ts +8 -8
- package/src/vm/lib/global/sequence/entries.ts +2 -2
- package/src/vm/lib/global/sequence/find.ts +4 -4
- package/src/vm/lib/global/sequence/map-filter.ts +3 -3
- package/src/vm/lib/global/sequence/with.ts +1 -1
- package/src/vm/lib/global/sequence/zip.ts +1 -1
- package/src/vm/lib/global/time.ts +3 -3
- package/src/vm/operations.ts +7 -7
- package/src/vm/types/any.ts +33 -0
- package/src/vm/types/array.ts +19 -0
- package/src/vm/types/callable.ts +10 -0
- package/src/vm/types/{checker.ts → const.ts} +7 -55
- package/src/vm/types/context.ts +25 -6
- package/src/vm/types/extern.ts +19 -5
- package/src/vm/types/immutable.ts +22 -0
- package/src/vm/types/index.ts +31 -83
- package/src/vm/types/primitive.ts +14 -0
- package/src/vm/types/record.ts +53 -0
- package/src/vm/types/value.ts +22 -0
- package/src/vm/types/wrapper.ts +1 -1
- package/dist/chunk-AGIXQM52.js +0 -916
- package/dist/chunk-AGIXQM52.js.map +0 -6
- package/dist/chunk-IKSSUVRE.js.map +0 -6
- package/dist/vm/types/checker.d.ts +0 -30
- package/dist/vm/types/checker.d.ts.map +0 -1
package/src/helpers/serialize.ts
CHANGED
|
@@ -10,9 +10,10 @@ import {
|
|
|
10
10
|
isVmFunction,
|
|
11
11
|
isVmModule,
|
|
12
12
|
isVmExtern,
|
|
13
|
+
isVmArrayLikeRecordByEntires,
|
|
13
14
|
} from '../vm/index.js';
|
|
14
15
|
import { REG_IDENTIFIER, REG_ORDINAL } from './constants.js';
|
|
15
|
-
import { entries, isFinite, isNaN } from '../helpers/utils.js';
|
|
16
|
+
import { entries, hasOwn, isFinite, isNaN } from '../helpers/utils.js';
|
|
16
17
|
|
|
17
18
|
const REG_IDENTIFIER_FULL = new RegExp(`^${REG_IDENTIFIER.source}$`, REG_IDENTIFIER.flags);
|
|
18
19
|
const REG_ORDINAL_FULL = new RegExp(`^${REG_ORDINAL.source}$`, REG_ORDINAL.flags);
|
|
@@ -42,7 +43,7 @@ export interface SerializeOptions {
|
|
|
42
43
|
/** 序列化记录 */
|
|
43
44
|
serializeRecord: (value: VmRecord, depth: number, options: SerializeOptions) => string;
|
|
44
45
|
/** 序列化属性名 */
|
|
45
|
-
serializePropName: (value:
|
|
46
|
+
serializePropName: (value: number | string, options: SerializeOptions) => string;
|
|
46
47
|
/** 序列化模块 */
|
|
47
48
|
serializeModule: (value: VmModule, depth: number, options: SerializeOptions) => string;
|
|
48
49
|
/** 序列化外部值 */
|
|
@@ -66,17 +67,23 @@ const DEFAULT_OPTIONS = Object.freeze({
|
|
|
66
67
|
serializeExtern: serializeNil,
|
|
67
68
|
} satisfies SerializeOptions);
|
|
68
69
|
|
|
70
|
+
/** 是否为默认选项 */
|
|
71
|
+
function isDefaultOptions(options: Partial<SerializeOptions> | undefined): boolean {
|
|
72
|
+
return options == null || options === DEFAULT_OPTIONS;
|
|
73
|
+
}
|
|
74
|
+
|
|
69
75
|
/** 获取选项 */
|
|
70
76
|
function getSerializeOptions(options: Partial<SerializeOptions> | undefined): SerializeOptions {
|
|
71
|
-
if (options
|
|
72
|
-
|
|
77
|
+
if (isDefaultOptions(options)) return DEFAULT_OPTIONS;
|
|
78
|
+
let opt: SerializeOptions | null = null;
|
|
73
79
|
for (const key in options) {
|
|
80
|
+
if (!hasOwn(options, key) || !hasOwn(DEFAULT_OPTIONS, key)) continue;
|
|
74
81
|
const el = options[key as keyof SerializeOptions];
|
|
75
|
-
if (el
|
|
76
|
-
|
|
77
|
-
|
|
82
|
+
if (el == null) continue;
|
|
83
|
+
opt ??= { ...DEFAULT_OPTIONS };
|
|
84
|
+
opt[key as keyof SerializeOptions] = el as never;
|
|
78
85
|
}
|
|
79
|
-
return Object.freeze(opt);
|
|
86
|
+
return opt ? Object.freeze(opt) : DEFAULT_OPTIONS;
|
|
80
87
|
}
|
|
81
88
|
|
|
82
89
|
/**
|
|
@@ -137,8 +144,19 @@ export function serializeString(value: string, options?: Partial<SerializeOption
|
|
|
137
144
|
return serializeStringImpl(value, getSerializeOptions(options));
|
|
138
145
|
}
|
|
139
146
|
|
|
147
|
+
/** 使用默认选项序列化属性名 */
|
|
148
|
+
function serializeRecordKeyDefault(key: string): string {
|
|
149
|
+
if (REG_ORDINAL_FULL.test(key) || REG_IDENTIFIER_FULL.test(key)) {
|
|
150
|
+
return key;
|
|
151
|
+
}
|
|
152
|
+
return serializeStringImpl(key, DEFAULT_OPTIONS);
|
|
153
|
+
}
|
|
154
|
+
|
|
140
155
|
/** 序列化属性名 */
|
|
141
|
-
function
|
|
156
|
+
function serializeRecordKeyOpt(value: string, options: SerializeOptions): string {
|
|
157
|
+
if (isDefaultOptions(options)) {
|
|
158
|
+
return serializeRecordKeyDefault(value);
|
|
159
|
+
}
|
|
142
160
|
if (REG_ORDINAL_FULL.test(value)) {
|
|
143
161
|
// 合法的数字属性名
|
|
144
162
|
return options.serializePropName(Number(value), options);
|
|
@@ -152,8 +170,11 @@ function serializePropNameImpl(value: string, options: SerializeOptions): string
|
|
|
152
170
|
}
|
|
153
171
|
|
|
154
172
|
/** 序列化属性名 */
|
|
155
|
-
export function
|
|
156
|
-
|
|
173
|
+
export function serializeRecordKey(key: string, options?: Partial<SerializeOptions>): string {
|
|
174
|
+
if (isDefaultOptions(options)) {
|
|
175
|
+
return serializeRecordKeyDefault(key);
|
|
176
|
+
}
|
|
177
|
+
return serializeRecordKeyOpt(key, getSerializeOptions(options));
|
|
157
178
|
}
|
|
158
179
|
|
|
159
180
|
/** 序列化 nil 值 */
|
|
@@ -220,18 +241,17 @@ export function serializeRecord(value: VmRecord, depth: number, options: Seriali
|
|
|
220
241
|
if (k === '0') {
|
|
221
242
|
return `(${serializeImpl(v, depth, options)},)`; // 单个元素数组
|
|
222
243
|
}
|
|
223
|
-
return `(${
|
|
244
|
+
return `(${serializeRecordKeyOpt(k, options)}: ${serializeImpl(v, depth, options)})`;
|
|
224
245
|
}
|
|
225
246
|
|
|
226
|
-
|
|
227
|
-
const omitKey = e.length < 10 && e.every(([key], index) => key === String(index));
|
|
247
|
+
const omitKey = isVmArrayLikeRecordByEntires(e);
|
|
228
248
|
let str = '(';
|
|
229
249
|
for (const [key, val] of e) {
|
|
230
250
|
if (str.length > 1) str += ', ';
|
|
231
251
|
if (omitKey) {
|
|
232
252
|
str += serializeImpl(val, depth, options);
|
|
233
253
|
} else {
|
|
234
|
-
str += `${
|
|
254
|
+
str += `${serializeRecordKeyOpt(key, options)}: ${serializeImpl(val, depth, options)}`;
|
|
235
255
|
}
|
|
236
256
|
}
|
|
237
257
|
str += ')';
|
package/src/subtle.ts
CHANGED
package/src/vm/lib/_helpers.ts
CHANGED
|
@@ -30,7 +30,7 @@ export function throwError(message: string, recovered: VmAny | (() => VmAny)): n
|
|
|
30
30
|
|
|
31
31
|
/** 抛出预期外类型异常 */
|
|
32
32
|
export function throwUnexpectedTypeError(
|
|
33
|
-
name:
|
|
33
|
+
name: number | string,
|
|
34
34
|
expected: string,
|
|
35
35
|
value: VmAny,
|
|
36
36
|
recovered: VmAny | (() => VmAny),
|
|
@@ -49,7 +49,7 @@ export function rethrowError(prefix: string, error: unknown, recovered: VmAny |
|
|
|
49
49
|
|
|
50
50
|
/** 标记参数为必须项 */
|
|
51
51
|
export function required<const T = VmValue>(
|
|
52
|
-
name:
|
|
52
|
+
name: number | string,
|
|
53
53
|
value: T | undefined,
|
|
54
54
|
recovered: VmAny | (() => VmAny),
|
|
55
55
|
): asserts value is T {
|
|
@@ -62,7 +62,7 @@ export function required<const T = VmValue>(
|
|
|
62
62
|
|
|
63
63
|
/** 标记参数为数组 */
|
|
64
64
|
export function expectArray(
|
|
65
|
-
name:
|
|
65
|
+
name: number | string,
|
|
66
66
|
value: VmAny,
|
|
67
67
|
recovered: VmAny | (() => VmAny),
|
|
68
68
|
): asserts value is VmArray {
|
|
@@ -74,7 +74,7 @@ export function expectArray(
|
|
|
74
74
|
|
|
75
75
|
/** 标记参数为记录 */
|
|
76
76
|
export function expectRecord(
|
|
77
|
-
name:
|
|
77
|
+
name: number | string,
|
|
78
78
|
value: VmAny,
|
|
79
79
|
recovered: VmAny | (() => VmAny),
|
|
80
80
|
): asserts value is VmRecord {
|
|
@@ -86,7 +86,7 @@ export function expectRecord(
|
|
|
86
86
|
|
|
87
87
|
/** 标记参数为数组或记录 */
|
|
88
88
|
export function expectArrayOrRecord(
|
|
89
|
-
name:
|
|
89
|
+
name: number | string,
|
|
90
90
|
value: VmAny,
|
|
91
91
|
recovered: VmAny | (() => VmAny),
|
|
92
92
|
): asserts value is VmArray | VmRecord {
|
|
@@ -98,7 +98,7 @@ export function expectArrayOrRecord(
|
|
|
98
98
|
|
|
99
99
|
/** 标记参数为复合类型 */
|
|
100
100
|
export function expectCompound(
|
|
101
|
-
name:
|
|
101
|
+
name: number | string,
|
|
102
102
|
value: VmAny,
|
|
103
103
|
recovered: VmAny | (() => VmAny),
|
|
104
104
|
): asserts value is VmArray | VmRecord | VmModule | VmExtern {
|
|
@@ -110,7 +110,7 @@ export function expectCompound(
|
|
|
110
110
|
|
|
111
111
|
/** 标记参数为常量 */
|
|
112
112
|
export function expectConst(
|
|
113
|
-
name:
|
|
113
|
+
name: number | string,
|
|
114
114
|
value: VmAny,
|
|
115
115
|
recovered: VmAny | (() => VmAny),
|
|
116
116
|
): asserts value is VmConst {
|
|
@@ -122,7 +122,7 @@ export function expectConst(
|
|
|
122
122
|
|
|
123
123
|
/** 标记为可调用 */
|
|
124
124
|
export function expectCallable(
|
|
125
|
-
name:
|
|
125
|
+
name: number | string,
|
|
126
126
|
value: VmAny,
|
|
127
127
|
recovered: VmAny | (() => VmAny),
|
|
128
128
|
): asserts value is VmFunction | VmExtern {
|
|
@@ -22,7 +22,7 @@ export const keys = VmLib(
|
|
|
22
22
|
summary: '返回数组、记录、外部对象或模块的键列表',
|
|
23
23
|
params: { data: '要获取键的数组、记录、外部对象或模块' },
|
|
24
24
|
paramsType: { data: 'array | record | extern | module' },
|
|
25
|
-
returnsType: '
|
|
25
|
+
returnsType: 'number[] | string[]',
|
|
26
26
|
examples: ['keys([10, 20]) // [0, 1]', 'keys((10, 20)) // ["0", "1"]'],
|
|
27
27
|
},
|
|
28
28
|
);
|
|
@@ -61,7 +61,7 @@ export const entries = VmLib(
|
|
|
61
61
|
summary: '返回数组或记录的键值对列表',
|
|
62
62
|
params: { data: '要获取键值对的数组或记录' },
|
|
63
63
|
paramsType: { data: 'array | record' },
|
|
64
|
-
returnsType: '(
|
|
64
|
+
returnsType: '(number, any)[] | (string, any)[]',
|
|
65
65
|
examples: ['entries([1]) // [(0, 1)]', 'entries((a: 1)) // [("a", 1)]'],
|
|
66
66
|
},
|
|
67
67
|
);
|
|
@@ -9,7 +9,7 @@ export const find = VmLib(
|
|
|
9
9
|
expectArrayOrRecord('data', data, null);
|
|
10
10
|
required('predicate', predicate, null);
|
|
11
11
|
const p = isVmCallable(predicate)
|
|
12
|
-
? (value: VmValue, key:
|
|
12
|
+
? (value: VmValue, key: number | string, data: VmValue) => {
|
|
13
13
|
const ret = $Call(predicate, [value, key, data]);
|
|
14
14
|
return $ToBoolean(ret);
|
|
15
15
|
}
|
|
@@ -38,14 +38,14 @@ export const find = VmLib(
|
|
|
38
38
|
{
|
|
39
39
|
summary: '查找数组或记录中的键值对,返回第一个满足条件的键值对',
|
|
40
40
|
params: {
|
|
41
|
-
data: '
|
|
41
|
+
data: '要查找的数组或记录',
|
|
42
42
|
predicate: '用于测试每个键值对的函数,或要查找的值',
|
|
43
43
|
},
|
|
44
44
|
paramsType: {
|
|
45
45
|
data: 'array | record',
|
|
46
|
-
predicate: '(fn(value: any, key: number | string
|
|
46
|
+
predicate: '(fn(value: any, key: number | string, input: type(data)) -> boolean) | any',
|
|
47
47
|
},
|
|
48
|
-
returnsType: '(
|
|
48
|
+
returnsType: '(number | string, any) | nil',
|
|
49
49
|
examples: ['find([3, 5, 8], fn (v) { v % 2 == 0 }) // (2, 8)', `find((x: 1, y: 2, z: 3), 2) // ('y', 2)`],
|
|
50
50
|
},
|
|
51
51
|
);
|
|
@@ -32,7 +32,7 @@ export const map = VmLib(
|
|
|
32
32
|
},
|
|
33
33
|
paramsType: {
|
|
34
34
|
data: 'array | record',
|
|
35
|
-
f: 'fn(value: any, key: number | string
|
|
35
|
+
f: 'fn(value: any, key: number | string, input: type(data)) -> any',
|
|
36
36
|
},
|
|
37
37
|
returnsType: 'type(data)',
|
|
38
38
|
examples: ['map([1, 2, 3], fn (v) { v * v }) // [1, 4, 9]'],
|
|
@@ -53,7 +53,7 @@ export const filter = VmLib(
|
|
|
53
53
|
},
|
|
54
54
|
paramsType: {
|
|
55
55
|
data: 'array | record',
|
|
56
|
-
predicate: 'fn(value: any, key: number | string
|
|
56
|
+
predicate: 'fn(value: any, key: number | string, input: type(data)) -> boolean',
|
|
57
57
|
},
|
|
58
58
|
returnsType: 'type(data)',
|
|
59
59
|
examples: ['filter([1, 2, 3, 4], fn (v) { v % 2 == 0 }) // [2, 4]'],
|
|
@@ -74,7 +74,7 @@ export const filter_map = VmLib(
|
|
|
74
74
|
},
|
|
75
75
|
paramsType: {
|
|
76
76
|
data: 'array | record',
|
|
77
|
-
f: 'fn(value: any, key: number | string
|
|
77
|
+
f: 'fn(value: any, key: number | string, input: type(data)) -> any | nil',
|
|
78
78
|
},
|
|
79
79
|
returnsType: 'type(data)',
|
|
80
80
|
examples: ['filter_map([1, 2, 3], fn (v) { if v % 2 == 0 { v * v } else { nil } }) // [4]'],
|
|
@@ -125,7 +125,7 @@ const _with = VmLib(
|
|
|
125
125
|
},
|
|
126
126
|
paramsType: {
|
|
127
127
|
data: 'array | record',
|
|
128
|
-
'..entries': `[..[
|
|
128
|
+
'..entries': `[..[number | string | (number | string)[], any][]]`,
|
|
129
129
|
},
|
|
130
130
|
returnsType: 'type(data)',
|
|
131
131
|
examples: [
|
|
@@ -15,7 +15,7 @@ export const zip = VmLib(
|
|
|
15
15
|
len = Math.max(len, arr.length);
|
|
16
16
|
}
|
|
17
17
|
if (len === 0) return [];
|
|
18
|
-
const result: Array<Record<
|
|
18
|
+
const result: Array<Record<number | string, VmConst>> = [];
|
|
19
19
|
const isArr = isVmArray(data);
|
|
20
20
|
for (let i = 0; i < len; i++) {
|
|
21
21
|
Cp();
|
|
@@ -19,7 +19,7 @@ export const to_timestamp = VmLib(
|
|
|
19
19
|
{
|
|
20
20
|
summary: '将数据转换为 Unix 毫秒时间戳',
|
|
21
21
|
params: { datetime: '要转换的数据,默认为当前时间' },
|
|
22
|
-
paramsType: { datetime: '
|
|
22
|
+
paramsType: { datetime: 'number | string' },
|
|
23
23
|
returnsType: 'number',
|
|
24
24
|
examples: ['to_timestamp("1970-01-01T00:00:00Z") // 0'],
|
|
25
25
|
},
|
|
@@ -49,7 +49,7 @@ export const to_datetime = VmLib(
|
|
|
49
49
|
datetime: '要转换的数据,默认为当前时间',
|
|
50
50
|
offset: '时区偏移量(单位:小时),默认为 0',
|
|
51
51
|
},
|
|
52
|
-
paramsType: { datetime: '
|
|
52
|
+
paramsType: { datetime: 'number | string', offset: 'number' },
|
|
53
53
|
returnsType: 'Date',
|
|
54
54
|
examples: [
|
|
55
55
|
`
|
|
@@ -73,7 +73,7 @@ export const to_iso8601 = VmLib(
|
|
|
73
73
|
{
|
|
74
74
|
summary: '将数据转换为 ISO 8601 格式的字符串',
|
|
75
75
|
params: { datetime: '要转换的数据,默认为当前时间' },
|
|
76
|
-
paramsType: { datetime: '
|
|
76
|
+
paramsType: { datetime: 'number | string' },
|
|
77
77
|
returnsType: 'string',
|
|
78
78
|
examples: ['to_iso8601(0) // "1970-01-01T00:00:00.000Z"'],
|
|
79
79
|
},
|
package/src/vm/operations.ts
CHANGED
|
@@ -221,7 +221,7 @@ export const $Length = (value: VmAny): number => {
|
|
|
221
221
|
}
|
|
222
222
|
return Number.NaN;
|
|
223
223
|
};
|
|
224
|
-
export const $Omit = (value: VmAny, omitted: ReadonlyArray<
|
|
224
|
+
export const $Omit = (value: VmAny, omitted: ReadonlyArray<number | string>): VmRecord => {
|
|
225
225
|
$AssertInit(value);
|
|
226
226
|
if (value == null || !isVmRecord(value)) return {};
|
|
227
227
|
const result: Record<string, VmConst> = {};
|
|
@@ -234,7 +234,7 @@ export const $Omit = (value: VmAny, omitted: ReadonlyArray<string | number>): Vm
|
|
|
234
234
|
}
|
|
235
235
|
return result;
|
|
236
236
|
};
|
|
237
|
-
export const $Pick = (value: VmAny, picked: ReadonlyArray<
|
|
237
|
+
export const $Pick = (value: VmAny, picked: ReadonlyArray<number | string>): VmRecord => {
|
|
238
238
|
$AssertInit(value);
|
|
239
239
|
if (value == null || !isVmRecord(value)) return {};
|
|
240
240
|
const result: Record<string, VmConst> = {};
|
|
@@ -313,9 +313,9 @@ function numberToString(value: number): string {
|
|
|
313
313
|
}
|
|
314
314
|
|
|
315
315
|
/** 将值转为字符串 */
|
|
316
|
-
function
|
|
316
|
+
export function $InnerToString(value: VmAny, useBraces: boolean): string {
|
|
317
317
|
if (value == null) return 'nil';
|
|
318
|
-
if (isVmWrapper(value)) return value.toString();
|
|
318
|
+
if (isVmWrapper(value)) return value.toString(useBraces);
|
|
319
319
|
if (typeof value == 'function') {
|
|
320
320
|
const name = getVmFunctionInfo(value)?.fullName;
|
|
321
321
|
return name ? `<function ${name}>` : `<function>`;
|
|
@@ -323,7 +323,7 @@ function innerToString(value: VmAny, useBraces: boolean): string {
|
|
|
323
323
|
if (isVmArray(value)) {
|
|
324
324
|
const strings: string[] = [];
|
|
325
325
|
for (const item of value) {
|
|
326
|
-
strings.push(
|
|
326
|
+
strings.push($InnerToString(item, true));
|
|
327
327
|
}
|
|
328
328
|
// 在 join 过程中会自动把 null/undefined 和 empty slot 转为 ''
|
|
329
329
|
// 与 innerToString 行为不一致
|
|
@@ -333,7 +333,7 @@ function innerToString(value: VmAny, useBraces: boolean): string {
|
|
|
333
333
|
}
|
|
334
334
|
if (typeof value == 'object') {
|
|
335
335
|
const entries = keys(value)
|
|
336
|
-
.map((key) => `${key}: ${
|
|
336
|
+
.map((key) => `${key}: ${$InnerToString(value[key], true)}`)
|
|
337
337
|
.join(', ');
|
|
338
338
|
if (!useBraces) return entries;
|
|
339
339
|
return `(${entries})`;
|
|
@@ -347,7 +347,7 @@ export const $ToString = (value: VmAny): string => {
|
|
|
347
347
|
$AssertInit(value);
|
|
348
348
|
if (typeof value == 'string') return value;
|
|
349
349
|
if (value === null) return '';
|
|
350
|
-
return
|
|
350
|
+
return $InnerToString(value, false);
|
|
351
351
|
};
|
|
352
352
|
export const $ToNumber = (value: VmAny): number => {
|
|
353
353
|
$AssertInit(value);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { isVmConst } from './const.js';
|
|
2
|
+
import { isVmFunction } from './function.js';
|
|
3
|
+
import { isVmWrapper } from './wrapper.js';
|
|
4
|
+
import type { VmValue } from './value.js';
|
|
5
|
+
|
|
6
|
+
/** Mirascript 虚拟机内的值(包括未初始化变量) */
|
|
7
|
+
export type VmAny = VmValue | VmUninitialized;
|
|
8
|
+
|
|
9
|
+
/** Mirascript 虚拟机内的未初始化变量 */
|
|
10
|
+
export type VmUninitialized = undefined;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 检查是否为 Mirascript 值
|
|
14
|
+
*/
|
|
15
|
+
export function isVmAny(value: unknown, checkDeep: boolean): value is VmAny {
|
|
16
|
+
switch (typeof value) {
|
|
17
|
+
case 'string':
|
|
18
|
+
case 'number':
|
|
19
|
+
case 'boolean':
|
|
20
|
+
case 'undefined':
|
|
21
|
+
return true;
|
|
22
|
+
case 'object':
|
|
23
|
+
if (value == null) return true;
|
|
24
|
+
if (isVmWrapper(value)) return true;
|
|
25
|
+
return isVmConst(value, checkDeep);
|
|
26
|
+
case 'function':
|
|
27
|
+
return isVmFunction(value);
|
|
28
|
+
case 'bigint':
|
|
29
|
+
case 'symbol':
|
|
30
|
+
default:
|
|
31
|
+
return false; // Other types are not valid
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { isArray } from '../../helpers/utils.js';
|
|
2
|
+
import type { VmAny, VmConst } from './index.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Mirascript 数组
|
|
6
|
+
* 数组中的 `undefined`、`null` 及 <empty slot> 均视作 `nil`
|
|
7
|
+
*/
|
|
8
|
+
export type VmArray = ReadonlyArray<VmConst | undefined>;
|
|
9
|
+
|
|
10
|
+
export const VM_ARRAY_MAX_LENGTH = 2 ** 31 - 1;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 检查值是否为 Mirascript 数组
|
|
14
|
+
*/
|
|
15
|
+
export function isVmArray(value: VmAny): value is VmArray {
|
|
16
|
+
if (!isArray(value)) return false;
|
|
17
|
+
value as VmArray satisfies VmArray;
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { isVmExtern, type VmExtern } from './extern.js';
|
|
2
|
+
import { isVmFunction, type VmFunction, type VmFunctionLike } from './function.js';
|
|
3
|
+
|
|
4
|
+
/** 检查值是否为 Mirascript 可调用值 */
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
6
|
+
export function isVmCallable<E extends Function, F extends VmFunctionLike>(
|
|
7
|
+
value: unknown,
|
|
8
|
+
): value is VmFunction<F> | VmExtern<E> {
|
|
9
|
+
return isVmFunction<F>(value) || (isVmExtern<E>(value) && typeof value.value == 'function');
|
|
10
|
+
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import type { VmArray } from './array.js';
|
|
2
|
+
import type { VmPrimitive } from './primitive.js';
|
|
3
|
+
import type { VmRecord } from './record.js';
|
|
4
|
+
import type { VmAny } from './any.js';
|
|
1
5
|
import { getPrototypeOf, isArray, values } from '../../helpers/utils.js';
|
|
2
|
-
import type { VmAny, VmArray, VmConst, VmImmutable, VmRecord, VmValue } from './index.js';
|
|
3
6
|
import { isVmWrapper } from './wrapper.js';
|
|
4
|
-
|
|
5
|
-
|
|
7
|
+
|
|
8
|
+
/** Mirascript 虚拟机内的值语义值 */
|
|
9
|
+
export type VmConst = VmPrimitive | VmRecord | VmArray;
|
|
6
10
|
|
|
7
11
|
const MAX_DEPTH = 32;
|
|
8
12
|
/**
|
|
@@ -103,55 +107,3 @@ export function isVmConst(value: unknown, checkDeep = false): value is VmConst {
|
|
|
103
107
|
return false; // Other types are not valid
|
|
104
108
|
}
|
|
105
109
|
}
|
|
106
|
-
/**
|
|
107
|
-
* 检查是否为 Mirascript 不可变值
|
|
108
|
-
*/
|
|
109
|
-
export function isVmImmutable(value: VmAny): value is VmImmutable;
|
|
110
|
-
/**
|
|
111
|
-
* 检查是否为 Mirascript 不可变值
|
|
112
|
-
*/
|
|
113
|
-
export function isVmImmutable(value: unknown, checkDeep: boolean): value is VmImmutable;
|
|
114
|
-
/**
|
|
115
|
-
* 检查是否为 Mirascript 不可变值
|
|
116
|
-
*/
|
|
117
|
-
export function isVmImmutable(value: unknown, checkDeep = false): value is VmImmutable {
|
|
118
|
-
return isVmModule(value) || isVmFunction(value) || isVmConst(value, checkDeep);
|
|
119
|
-
}
|
|
120
|
-
/**
|
|
121
|
-
* 检查是否为 Mirascript 合法值
|
|
122
|
-
*/
|
|
123
|
-
export function isVmValue(value: VmAny): value is VmValue;
|
|
124
|
-
/**
|
|
125
|
-
* 检查是否为 Mirascript 合法值
|
|
126
|
-
*/
|
|
127
|
-
export function isVmValue(value: unknown, checkDeep: boolean): value is VmValue;
|
|
128
|
-
/**
|
|
129
|
-
* 检查是否为 Mirascript 合法值
|
|
130
|
-
*/
|
|
131
|
-
export function isVmValue(value: unknown, checkDeep = false): value is VmValue {
|
|
132
|
-
if (value === undefined) return false;
|
|
133
|
-
return isVmAny(value, checkDeep);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* 检查是否为 Mirascript 值
|
|
138
|
-
*/
|
|
139
|
-
export function isVmAny(value: unknown, checkDeep: boolean): value is VmAny {
|
|
140
|
-
switch (typeof value) {
|
|
141
|
-
case 'string':
|
|
142
|
-
case 'number':
|
|
143
|
-
case 'boolean':
|
|
144
|
-
case 'undefined':
|
|
145
|
-
return true;
|
|
146
|
-
case 'object':
|
|
147
|
-
if (value == null) return true;
|
|
148
|
-
if (isVmWrapper(value)) return true;
|
|
149
|
-
return isVmConst(value, checkDeep);
|
|
150
|
-
case 'function':
|
|
151
|
-
return isVmFunction(value);
|
|
152
|
-
case 'bigint':
|
|
153
|
-
case 'symbol':
|
|
154
|
-
default:
|
|
155
|
-
return false; // Other types are not valid
|
|
156
|
-
}
|
|
157
|
-
}
|
package/src/vm/types/context.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from './index.js';
|
|
10
10
|
import { create, entries, keys } from '../../helpers/utils.js';
|
|
11
11
|
import type * as global from '../lib/global/index.js';
|
|
12
|
+
import { VmError } from '../error.js';
|
|
12
13
|
|
|
13
14
|
/** 全局导入的标准库 */
|
|
14
15
|
type GlobalKeys = keyof typeof global;
|
|
@@ -31,15 +32,27 @@ export interface VmContext {
|
|
|
31
32
|
keys(): Iterable<string>;
|
|
32
33
|
/** 描述值,返回 MarkDown 文本,仅在 LSP 中使用 */
|
|
33
34
|
describe?(key: string): string | undefined;
|
|
34
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* 获取指定 key 的值 `global[key]`
|
|
37
|
+
* @throws {VmError} 如果值不存在则抛出异常
|
|
38
|
+
*/
|
|
35
39
|
get(key: string): VmValue;
|
|
36
40
|
/** 查找指定 key 是否存在 `key in global` */
|
|
37
41
|
has(key: string): boolean;
|
|
38
42
|
}
|
|
39
43
|
/** MiraScript 执行上下文 */
|
|
40
|
-
export type VmContextRecord = Record<string, VmValue
|
|
44
|
+
export type VmContextRecord = Record<string, VmValue>;
|
|
45
|
+
/** MiraScript 执行上下文 */
|
|
46
|
+
export type VmContextRecordLoose = Record<string, VmValue | undefined>;
|
|
41
47
|
export const VmSharedContext = create(null) as VmSharedContext;
|
|
42
48
|
|
|
49
|
+
let VmSharedContextKeys: readonly string[] | null = null;
|
|
50
|
+
|
|
51
|
+
/** 全局变量未找到 */
|
|
52
|
+
function globalVarNotFound(name: string): never {
|
|
53
|
+
throw new VmError(`Global variable '${name}' is not defined.`, null);
|
|
54
|
+
}
|
|
55
|
+
|
|
43
56
|
/** 定义在所有 MiraScript 执行上下文中共享的全局变量 */
|
|
44
57
|
export function defineVmContextValue(
|
|
45
58
|
name: string,
|
|
@@ -57,6 +70,7 @@ export function defineVmContextValue(
|
|
|
57
70
|
v = value;
|
|
58
71
|
}
|
|
59
72
|
VmSharedContext[name] = v ?? null;
|
|
73
|
+
VmSharedContextKeys = null;
|
|
60
74
|
}
|
|
61
75
|
|
|
62
76
|
/** 无后备的实现 */
|
|
@@ -64,11 +78,14 @@ export const DefaultVmContext: VmContext = Object.freeze({
|
|
|
64
78
|
[kVmContext]: true as const,
|
|
65
79
|
/** @inheritdoc */
|
|
66
80
|
keys(): Iterable<string> {
|
|
67
|
-
|
|
81
|
+
VmSharedContextKeys ??= Object.freeze(keys(VmSharedContext));
|
|
82
|
+
return VmSharedContextKeys;
|
|
68
83
|
},
|
|
69
84
|
/** @inheritdoc */
|
|
70
85
|
get(key: string): VmValue {
|
|
71
|
-
|
|
86
|
+
const val = VmSharedContext[key];
|
|
87
|
+
if (val === undefined) globalVarNotFound(key);
|
|
88
|
+
return val;
|
|
72
89
|
},
|
|
73
90
|
/** @inheritdoc */
|
|
74
91
|
has(key: string): boolean {
|
|
@@ -87,7 +104,9 @@ class ValueVmContext implements VmContext {
|
|
|
87
104
|
}
|
|
88
105
|
/** @inheritdoc */
|
|
89
106
|
get(key: string): VmValue {
|
|
90
|
-
|
|
107
|
+
const val = this.env[key];
|
|
108
|
+
if (val === undefined) globalVarNotFound(key);
|
|
109
|
+
return val;
|
|
91
110
|
}
|
|
92
111
|
/** @inheritdoc */
|
|
93
112
|
has(key: string): boolean {
|
|
@@ -128,7 +147,7 @@ class FactoryVmContext implements VmContext {
|
|
|
128
147
|
|
|
129
148
|
/** 以值为后备的实现 */
|
|
130
149
|
type CreateVmContextWithValues = readonly [
|
|
131
|
-
vmValues?:
|
|
150
|
+
vmValues?: VmContextRecordLoose | null | undefined,
|
|
132
151
|
externValues?: Record<string, unknown> | null | undefined,
|
|
133
152
|
describer?: ((key: string) => string | undefined) | null | undefined,
|
|
134
153
|
];
|
package/src/vm/types/extern.ts
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { VmError } from '../error.js';
|
|
2
2
|
import { VmWrapper } from './wrapper.js';
|
|
3
3
|
import type { TypeName, VmAny, VmConst, VmPrimitive, VmValue } from './index.js';
|
|
4
|
-
import { getPrototypeOf, hasOwn, apply } from '../../helpers/utils.js';
|
|
4
|
+
import { getPrototypeOf, hasOwn, apply, isArray } from '../../helpers/utils.js';
|
|
5
5
|
import { unwrapFromVmValue, wrapToVmValue } from './boundary.js';
|
|
6
|
+
import { $InnerToString } from '../operations.js';
|
|
6
7
|
|
|
7
8
|
const ObjectPrototype = Object.prototype;
|
|
8
9
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
9
10
|
const ObjectToString = ObjectPrototype.toString;
|
|
10
11
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
11
12
|
const FunctionToString = Function.prototype.toString;
|
|
13
|
+
const ArrayToString = Array.prototype.toString;
|
|
14
|
+
const ArrayMap = Array.prototype.map;
|
|
12
15
|
/** 包装 Mirascript `extern` 类型的对象 */
|
|
13
16
|
export class VmExtern<const T extends object = object> extends VmWrapper<T> {
|
|
14
17
|
constructor(
|
|
@@ -88,16 +91,25 @@ export class VmExtern<const T extends object = object> extends VmWrapper<T> {
|
|
|
88
91
|
return this.value === other.value && this.thisArg === other.thisArg;
|
|
89
92
|
}
|
|
90
93
|
/** @inheritdoc */
|
|
91
|
-
override toString(): string {
|
|
94
|
+
override toString(useBraces: boolean): string {
|
|
92
95
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
93
96
|
const { toString } = this.value;
|
|
94
97
|
if (typeof toString != 'function' || toString === ObjectToString || toString === FunctionToString) {
|
|
95
|
-
return super.toString();
|
|
98
|
+
return super.toString(useBraces);
|
|
99
|
+
}
|
|
100
|
+
if (toString === ArrayToString && isArray(this.value)) {
|
|
101
|
+
const mapped = ArrayMap.call(this.value, (item: unknown) => {
|
|
102
|
+
if (item === undefined) return '';
|
|
103
|
+
return $InnerToString(wrapToVmValue(item ?? null, null), true);
|
|
104
|
+
});
|
|
105
|
+
const str = mapped.join(', ');
|
|
106
|
+
if (useBraces) return `[${str}]`;
|
|
107
|
+
return str;
|
|
96
108
|
}
|
|
97
109
|
try {
|
|
98
110
|
return String(this.value);
|
|
99
111
|
} catch {
|
|
100
|
-
return super.toString();
|
|
112
|
+
return super.toString(useBraces);
|
|
101
113
|
}
|
|
102
114
|
}
|
|
103
115
|
/** @inheritdoc */
|
|
@@ -107,7 +119,9 @@ export class VmExtern<const T extends object = object> extends VmWrapper<T> {
|
|
|
107
119
|
/** @inheritdoc */
|
|
108
120
|
override get describe(): string {
|
|
109
121
|
const tag = ObjectToString.call(this.value).slice(8, -1);
|
|
110
|
-
if (
|
|
122
|
+
if (isArray(this.value)) {
|
|
123
|
+
return `${tag}(${this.value.length})`;
|
|
124
|
+
} else if (tag === 'Object') {
|
|
111
125
|
const proto = getPrototypeOf(this.value);
|
|
112
126
|
if (proto === ObjectPrototype) {
|
|
113
127
|
return 'Object';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { VmAny } from './any.js';
|
|
2
|
+
import { isVmConst, type VmConst } from './const.js';
|
|
3
|
+
import { isVmFunction, type VmFunction } from './function.js';
|
|
4
|
+
import { isVmModule, type VmModule } from './module.js';
|
|
5
|
+
|
|
6
|
+
/** Mirascript 虚拟机内的不可变值 */
|
|
7
|
+
export type VmImmutable = VmConst | VmFunction | VmModule;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 检查是否为 Mirascript 不可变值
|
|
11
|
+
*/
|
|
12
|
+
export function isVmImmutable(value: VmAny): value is VmImmutable;
|
|
13
|
+
/**
|
|
14
|
+
* 检查是否为 Mirascript 不可变值
|
|
15
|
+
*/
|
|
16
|
+
export function isVmImmutable(value: unknown, checkDeep: boolean): value is VmImmutable;
|
|
17
|
+
/**
|
|
18
|
+
* 检查是否为 Mirascript 不可变值
|
|
19
|
+
*/
|
|
20
|
+
export function isVmImmutable(value: unknown, checkDeep = false): value is VmImmutable {
|
|
21
|
+
return isVmModule(value) || isVmFunction(value) || isVmConst(value, checkDeep);
|
|
22
|
+
}
|