@mirascript/mirascript 0.1.78 → 0.1.79
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-VNQQF2W7.js → chunk-BPFOJ4Q6.js} +381 -432
- package/dist/chunk-BPFOJ4Q6.js.map +6 -0
- package/dist/{chunk-BITOQYYQ.js → chunk-LSPCAN32.js} +204 -149
- package/dist/chunk-LSPCAN32.js.map +6 -0
- package/dist/compiler/emit/constants.d.ts +1 -1
- package/dist/compiler/emit/constants.d.ts.map +1 -1
- package/dist/compiler/emit/consts.d.ts.map +1 -1
- package/dist/compiler/worker.js +1 -1
- package/dist/helpers/convert/{to-boolean.d.ts → boolean.d.ts} +1 -1
- package/dist/helpers/convert/boolean.d.ts.map +1 -0
- package/dist/helpers/convert/{to-format.d.ts → format.d.ts} +1 -1
- package/dist/helpers/convert/format.d.ts.map +1 -0
- package/dist/helpers/convert/index.d.ts +4 -4
- package/dist/helpers/convert/index.d.ts.map +1 -1
- package/dist/helpers/convert/{to-number.d.ts → number.d.ts} +1 -1
- package/dist/helpers/convert/number.d.ts.map +1 -0
- package/dist/helpers/convert/{to-string.d.ts → string.d.ts} +1 -1
- package/dist/helpers/convert/string.d.ts.map +1 -0
- package/dist/helpers/serialize.d.ts.map +1 -1
- package/dist/helpers/types/array-like.d.ts +8 -0
- package/dist/helpers/types/array-like.d.ts.map +1 -0
- package/dist/helpers/types/basic.d.ts +26 -0
- package/dist/helpers/types/basic.d.ts.map +1 -0
- package/dist/helpers/types/composed.d.ts +12 -0
- package/dist/helpers/types/composed.d.ts.map +1 -0
- package/dist/helpers/types/const.d.ts +6 -0
- package/dist/helpers/types/const.d.ts.map +1 -0
- package/dist/helpers/types/get-type.d.ts +6 -0
- package/dist/helpers/types/get-type.d.ts.map +1 -0
- package/dist/helpers/types/index.d.ts +6 -0
- package/dist/helpers/types/index.d.ts.map +1 -0
- package/dist/helpers/types/proto.d.ts +5 -0
- package/dist/helpers/types/proto.d.ts.map +1 -0
- package/dist/helpers/utils.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/subtle.js +3 -3
- package/dist/vm/lib/global/json.d.ts +1 -1
- package/dist/vm/lib/global/json.d.ts.map +1 -1
- package/dist/vm/lib/helpers.d.ts.map +1 -1
- package/dist/vm/lib/mod/index.d.ts +1 -1
- package/dist/vm/lib/mod/index.d.ts.map +1 -1
- package/dist/vm/lib/mod/matrix/bin-ops.d.ts +8 -0
- package/dist/vm/lib/mod/matrix/bin-ops.d.ts.map +1 -0
- package/dist/vm/lib/mod/matrix/fill.d.ts +6 -0
- package/dist/vm/lib/mod/matrix/fill.d.ts.map +1 -0
- package/dist/vm/lib/mod/matrix/helpers.d.ts +6 -0
- package/dist/vm/lib/mod/matrix/helpers.d.ts.map +1 -0
- package/dist/vm/lib/mod/matrix/index.d.ts +6 -0
- package/dist/vm/lib/mod/matrix/index.d.ts.map +1 -0
- package/dist/vm/lib/mod/matrix/invert.d.ts +3 -0
- package/dist/vm/lib/mod/matrix/invert.d.ts.map +1 -0
- package/dist/vm/lib/mod/matrix/size.d.ts +2 -0
- package/dist/vm/lib/mod/matrix/size.d.ts.map +1 -0
- package/dist/vm/lib/mod/matrix/transpose.d.ts +2 -0
- package/dist/vm/lib/mod/matrix/transpose.d.ts.map +1 -0
- package/dist/vm/operations/array-range.d.ts +3 -3
- package/dist/vm/operations/array-range.d.ts.map +1 -1
- package/dist/vm/operations/call.d.ts +1 -1
- package/dist/vm/operations/call.d.ts.map +1 -1
- package/dist/vm/operations/compound.d.ts +1 -13
- package/dist/vm/operations/compound.d.ts.map +1 -1
- package/dist/vm/operations/index.d.ts +3 -0
- package/dist/vm/operations/index.d.ts.map +1 -1
- package/dist/vm/operations/iterable.d.ts +6 -0
- package/dist/vm/operations/iterable.d.ts.map +1 -0
- package/dist/vm/operations/record.d.ts +6 -0
- package/dist/vm/operations/record.d.ts.map +1 -0
- package/dist/vm/operations/slice.d.ts.map +1 -1
- package/dist/vm/operations/spread.d.ts +6 -0
- package/dist/vm/operations/spread.d.ts.map +1 -0
- package/dist/vm/operations/utils.d.ts.map +1 -1
- package/dist/vm/types/extern/access.d.ts +5 -0
- package/dist/vm/types/extern/access.d.ts.map +1 -0
- package/dist/vm/types/{extern.d.ts → extern/index.d.ts} +3 -3
- package/dist/vm/types/extern/index.d.ts.map +1 -0
- package/dist/vm/types/extern/tag.d.ts +3 -0
- package/dist/vm/types/extern/tag.d.ts.map +1 -0
- package/dist/vm/types/extern/utils.d.ts +13 -0
- package/dist/vm/types/extern/utils.d.ts.map +1 -0
- package/dist/vm/types/index.d.ts +3 -3
- package/dist/vm/types/index.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/compiler/diagnostic.ts +2 -2
- package/src/compiler/emit/constants.ts +1 -1
- package/src/compiler/emit/consts.ts +0 -1
- package/src/compiler/emit/index.ts +1 -1
- package/src/compiler/emit/sourcemap.ts +1 -1
- package/src/compiler/load-module.ts +5 -1
- package/src/helpers/convert/{to-boolean.ts → boolean.ts} +1 -1
- package/src/helpers/convert/{to-format.ts → format.ts} +1 -1
- package/src/helpers/convert/index.ts +4 -4
- package/src/helpers/convert/{to-number.ts → number.ts} +15 -26
- package/src/helpers/convert/{to-string.ts → string.ts} +2 -2
- package/src/helpers/serialize.ts +42 -36
- package/src/helpers/types/array-like.ts +32 -0
- package/src/helpers/types/basic.ts +82 -0
- package/src/helpers/types/composed.ts +27 -0
- package/src/helpers/types/const.ts +44 -0
- package/src/helpers/types/get-type.ts +16 -0
- package/src/helpers/types/index.ts +5 -0
- package/src/helpers/types/proto.ts +21 -0
- package/src/helpers/utils.ts +7 -1
- package/src/vm/lib/global/sequence/all-any.ts +1 -1
- package/src/vm/lib/global/sequence/find.ts +1 -1
- package/src/vm/lib/global/sequence/group.ts +1 -1
- package/src/vm/lib/global/sequence/map-filter.ts +1 -1
- package/src/vm/lib/global/sequence/new.ts +1 -1
- package/src/vm/lib/global/sequence/sort.ts +1 -1
- package/src/vm/lib/global/sequence/unique.ts +1 -1
- package/src/vm/lib/global/sequence/with.ts +1 -1
- package/src/vm/lib/global/time.ts +1 -1
- package/src/vm/lib/helpers.ts +8 -1
- package/src/vm/lib/mod/index.ts +1 -1
- package/src/vm/lib/mod/matrix/bin-ops.ts +291 -0
- package/src/vm/lib/mod/matrix/fill.ts +109 -0
- package/src/vm/lib/mod/matrix/helpers.ts +26 -0
- package/src/vm/lib/mod/matrix/index.ts +5 -0
- package/src/vm/lib/mod/matrix/invert.ts +122 -0
- package/src/vm/lib/mod/matrix/size.ts +15 -0
- package/src/vm/lib/mod/matrix/transpose.ts +32 -0
- package/src/vm/operations/array-range.ts +15 -27
- package/src/vm/operations/call.ts +2 -2
- package/src/vm/operations/compound.ts +4 -131
- package/src/vm/operations/helpers.ts +1 -1
- package/src/vm/operations/index.ts +3 -0
- package/src/vm/operations/iterable.ts +40 -0
- package/src/vm/operations/record.ts +33 -0
- package/src/vm/operations/slice.ts +23 -22
- package/src/vm/operations/spread.ts +57 -0
- package/src/vm/operations/type-check.ts +1 -1
- package/src/vm/operations/utils.ts +45 -37
- package/src/vm/types/boundary.ts +2 -2
- package/src/vm/types/extern/access.ts +24 -0
- package/src/vm/types/extern/index.ts +124 -0
- package/src/vm/types/extern/tag.ts +58 -0
- package/src/vm/types/extern/utils.ts +55 -0
- package/src/vm/types/function.ts +1 -1
- package/src/vm/types/index.ts +3 -3
- package/dist/chunk-BITOQYYQ.js.map +0 -6
- package/dist/chunk-VNQQF2W7.js.map +0 -6
- package/dist/helpers/convert/to-boolean.d.ts.map +0 -1
- package/dist/helpers/convert/to-format.d.ts.map +0 -1
- package/dist/helpers/convert/to-number.d.ts.map +0 -1
- package/dist/helpers/convert/to-string.d.ts.map +0 -1
- package/dist/helpers/types.d.ts +0 -58
- package/dist/helpers/types.d.ts.map +0 -1
- package/dist/vm/lib/mod/matrix.d.ts +0 -15
- package/dist/vm/lib/mod/matrix.d.ts.map +0 -1
- package/dist/vm/types/extern.d.ts.map +0 -1
- package/src/helpers/types.ts +0 -283
- package/src/vm/lib/mod/matrix.ts +0 -590
- package/src/vm/types/extern.ts +0 -197
|
@@ -1,28 +1,9 @@
|
|
|
1
1
|
import { VmError } from '../../helpers/error.js';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
NotNumber,
|
|
5
|
-
keys,
|
|
6
|
-
create,
|
|
7
|
-
isFinite,
|
|
8
|
-
keysLength,
|
|
9
|
-
getRecord,
|
|
10
|
-
setRecord,
|
|
11
|
-
} from '../../helpers/utils.js';
|
|
12
|
-
import { toNumber, toString } from '../../helpers/convert/index.js';
|
|
2
|
+
import { hasOwnEnumerable, NotNumber, isFinite, getRecord, keysLength } from '../../helpers/utils.js';
|
|
3
|
+
import { toNumber } from '../../helpers/convert/index.js';
|
|
13
4
|
import { display } from '../../helpers/serialize.js';
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
isVmArray,
|
|
17
|
-
isVmRecord,
|
|
18
|
-
isVmFunction,
|
|
19
|
-
isVmExtern,
|
|
20
|
-
isVmWrapper,
|
|
21
|
-
isVmConst,
|
|
22
|
-
} from '../../helpers/types.js';
|
|
23
|
-
import { wrapToVmConst } from '../types/boundary.js';
|
|
24
|
-
import type { VmAny, VmRecord, VmValue, VmConst } from '../types/index.js';
|
|
25
|
-
import { isSame } from './utils.js';
|
|
5
|
+
import { isVmArray, isVmRecord, isVmExtern, isVmWrapper } from '../../helpers/types/index.js';
|
|
6
|
+
import type { VmAny, VmValue } from '../types/index.js';
|
|
26
7
|
import { $AssertInit } from './common.js';
|
|
27
8
|
import { $ToString } from './convert.js';
|
|
28
9
|
import { $El } from './helpers.js';
|
|
@@ -30,29 +11,6 @@ import { $El } from './helpers.js';
|
|
|
30
11
|
const { trunc } = Math;
|
|
31
12
|
const { at } = Array.prototype;
|
|
32
13
|
|
|
33
|
-
/** 检查值是否在可迭代对象中 */
|
|
34
|
-
export const $In = (value: VmAny, iterable: VmAny): boolean => {
|
|
35
|
-
$AssertInit(value);
|
|
36
|
-
$AssertInit(iterable);
|
|
37
|
-
if (iterable == null) return false;
|
|
38
|
-
if (typeof iterable != 'object') return false;
|
|
39
|
-
if (isVmArray(iterable)) {
|
|
40
|
-
if (value == null) {
|
|
41
|
-
// array may have empty slots
|
|
42
|
-
for (const item of iterable) if (item == null) return true;
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
// JS %SameValueZero is same with `isSame` in this context
|
|
46
|
-
if (isVmPrimitive(value)) return iterable.includes(value);
|
|
47
|
-
// value is not null here, so it's ok to skip empty slots, since `isSame(null, something)` is always false
|
|
48
|
-
return iterable.some((item = null) => isSame(item, value satisfies NonNullable<VmValue>));
|
|
49
|
-
}
|
|
50
|
-
// iterable is a record or an extern here, value should be a string
|
|
51
|
-
const key = toString(value, undefined);
|
|
52
|
-
if (isVmWrapper(iterable)) return iterable.has(key);
|
|
53
|
-
return hasOwnEnumerable(iterable satisfies VmRecord, key);
|
|
54
|
-
};
|
|
55
|
-
|
|
56
14
|
/** 获取值的长度 */
|
|
57
15
|
export const $Length = (value: VmAny): number => {
|
|
58
16
|
$AssertInit(value);
|
|
@@ -63,34 +21,6 @@ export const $Length = (value: VmAny): number => {
|
|
|
63
21
|
throw new VmError(`Value has no length: ${display(value)}`, 0);
|
|
64
22
|
};
|
|
65
23
|
|
|
66
|
-
/** 删除记录中的指定字段 */
|
|
67
|
-
export const $Omit = (value: VmAny, omitted: ReadonlyArray<number | string>): VmRecord => {
|
|
68
|
-
$AssertInit(value);
|
|
69
|
-
if (!isVmRecord(value)) return {};
|
|
70
|
-
const result: Record<string, VmConst> = {};
|
|
71
|
-
const valueKeys = keys(value);
|
|
72
|
-
const omittedSet = new Set(omitted.map($ToString));
|
|
73
|
-
for (const key of valueKeys) {
|
|
74
|
-
if (!omittedSet.has(key)) {
|
|
75
|
-
setRecord(result, key, value[key]);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
return result;
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
/** 选择记录中的指定字段 */
|
|
82
|
-
export const $Pick = (value: VmAny, picked: ReadonlyArray<number | string>): VmRecord => {
|
|
83
|
-
$AssertInit(value);
|
|
84
|
-
if (!isVmRecord(value)) return {};
|
|
85
|
-
const result: Record<string, VmConst> = {};
|
|
86
|
-
for (const key of picked) {
|
|
87
|
-
const k = $ToString(key);
|
|
88
|
-
if (hasOwnEnumerable(value, k)) {
|
|
89
|
-
setRecord(result, k, value[k]);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return result;
|
|
93
|
-
};
|
|
94
24
|
/** 检查是否拥有字段 */
|
|
95
25
|
export const $Has = (obj: VmAny, key: VmAny): boolean => {
|
|
96
26
|
$AssertInit(obj);
|
|
@@ -132,60 +62,3 @@ export const $Set = (obj: VmAny, key: VmAny, value: VmAny): void => {
|
|
|
132
62
|
if (!isVmExtern(obj)) throw new VmError(`Expected extern, got ${display(obj)}`, undefined);
|
|
133
63
|
obj.set(pk, value);
|
|
134
64
|
};
|
|
135
|
-
/** 获取可迭代对象 */
|
|
136
|
-
export const $Iterable = (value: VmAny): Iterable<VmValue | undefined> => {
|
|
137
|
-
$AssertInit(value);
|
|
138
|
-
if (isVmWrapper(value)) return value.keys();
|
|
139
|
-
if (isVmArray(value)) return value;
|
|
140
|
-
if (value != null && typeof value == 'object') return keys(value);
|
|
141
|
-
throw new VmError(`Value is not iterable: ${display(value)}`, isVmFunction(value) ? [] : [value]);
|
|
142
|
-
};
|
|
143
|
-
/** 展开记录 */
|
|
144
|
-
export const $RecordSpread = (record: VmAny): VmRecord | null => {
|
|
145
|
-
$AssertInit(record);
|
|
146
|
-
if (record == null || isVmRecord(record)) return record;
|
|
147
|
-
if (isVmArray(record)) {
|
|
148
|
-
const result: Record<string, VmConst> = {};
|
|
149
|
-
const len = record.length;
|
|
150
|
-
for (let i = 0; i < len; i++) {
|
|
151
|
-
const item = record[i];
|
|
152
|
-
result[i] = item ?? null;
|
|
153
|
-
}
|
|
154
|
-
return result;
|
|
155
|
-
}
|
|
156
|
-
if (isVmExtern(record)) {
|
|
157
|
-
const result: Record<string, VmConst> = create(null);
|
|
158
|
-
for (const key of record.keys()) {
|
|
159
|
-
const value = record.get(key) ?? null;
|
|
160
|
-
if (isVmConst(value)) {
|
|
161
|
-
result[key] = value;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
return result;
|
|
165
|
-
}
|
|
166
|
-
throw new VmError(`Expected record, array, extern or nil, got ${display(record)}`, null);
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
/** 展开数组 */
|
|
170
|
-
export const $ArraySpread = (array: VmAny): Iterable<VmConst | undefined> => {
|
|
171
|
-
$AssertInit(array);
|
|
172
|
-
if (array == null) return [];
|
|
173
|
-
if (isVmArray(array)) return array;
|
|
174
|
-
if (isVmExtern(array)) {
|
|
175
|
-
if (array.isArrayLike()) {
|
|
176
|
-
const result: VmConst[] = [];
|
|
177
|
-
for (let i = 0, len = array.value.length; i < len; i++) {
|
|
178
|
-
const item = array.value[i];
|
|
179
|
-
result.push(wrapToVmConst(item, (v) => array.assumeVmValue(v, i)));
|
|
180
|
-
}
|
|
181
|
-
return result;
|
|
182
|
-
} else if (typeof (array.value as Iterable<unknown>)[Symbol.iterator] == 'function') {
|
|
183
|
-
const result: VmConst[] = [];
|
|
184
|
-
for (const item of array.value as Iterable<unknown>) {
|
|
185
|
-
result.push(wrapToVmConst(item, (v) => array.assumeVmValue(v, Symbol.iterator as never)));
|
|
186
|
-
}
|
|
187
|
-
return result;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
throw new VmError(`Expected array, iterable extern or nil, got ${display(array)}`, []);
|
|
191
|
-
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { create, defineProperty, entries } from '../../helpers/utils.js';
|
|
2
2
|
import { VM_FUNCTION_ANONYMOUS_NAME } from '../../helpers/constants.js';
|
|
3
|
-
import { isVmConst } from '../../helpers/types.js';
|
|
3
|
+
import { isVmConst } from '../../helpers/types/index.js';
|
|
4
4
|
import type { VmFunctionLike } from '../types/function.js';
|
|
5
5
|
import { DefaultVmContext, type VmContext } from '../types/context.js';
|
|
6
6
|
import type { VmConst, VmAny, VmValue, VmImmutable } from '../types/index.js';
|
|
@@ -5,6 +5,9 @@ export * from './operator.js';
|
|
|
5
5
|
export * from './slice.js';
|
|
6
6
|
export * from './helpers.js';
|
|
7
7
|
export * from './compound.js';
|
|
8
|
+
export * from './spread.js';
|
|
9
|
+
export * from './record.js';
|
|
10
|
+
export * from './iterable.js';
|
|
8
11
|
export * from './cp.js';
|
|
9
12
|
export * from './call.js';
|
|
10
13
|
export * from './array-range.js';
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { VmError } from '../../helpers/error.js';
|
|
2
|
+
import { hasOwnEnumerable, keys } from '../../helpers/utils.js';
|
|
3
|
+
import { toString } from '../../helpers/convert/index.js';
|
|
4
|
+
import { display } from '../../helpers/serialize.js';
|
|
5
|
+
import { isVmPrimitive, isVmArray, isVmFunction, isVmWrapper } from '../../helpers/types/index.js';
|
|
6
|
+
import type { VmAny, VmRecord, VmValue } from '../types/index.js';
|
|
7
|
+
import { isSame } from './utils.js';
|
|
8
|
+
import { $AssertInit } from './common.js';
|
|
9
|
+
|
|
10
|
+
/** 检查值是否在可迭代对象中 */
|
|
11
|
+
export const $In = (value: VmAny, iterable: VmAny): boolean => {
|
|
12
|
+
$AssertInit(value);
|
|
13
|
+
$AssertInit(iterable);
|
|
14
|
+
if (iterable == null) return false;
|
|
15
|
+
if (typeof iterable != 'object') return false;
|
|
16
|
+
if (isVmArray(iterable)) {
|
|
17
|
+
if (value == null) {
|
|
18
|
+
// array may have empty slots
|
|
19
|
+
for (const item of iterable) if (item == null) return true;
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
// JS %SameValueZero is same with `isSame` in this context
|
|
23
|
+
if (isVmPrimitive(value)) return iterable.includes(value);
|
|
24
|
+
// value is not null here, so it's ok to skip empty slots, since `isSame(null, something)` is always false
|
|
25
|
+
return iterable.some((item = null) => isSame(item, value satisfies NonNullable<VmValue>));
|
|
26
|
+
}
|
|
27
|
+
// iterable is a record or an extern here, value should be a string
|
|
28
|
+
const key = toString(value, undefined);
|
|
29
|
+
if (isVmWrapper(iterable)) return iterable.has(key);
|
|
30
|
+
return hasOwnEnumerable(iterable satisfies VmRecord, key);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/** 获取可迭代对象 */
|
|
34
|
+
export const $Iterable = (value: VmAny): Iterable<VmValue | undefined> => {
|
|
35
|
+
$AssertInit(value);
|
|
36
|
+
if (isVmWrapper(value)) return value.keys();
|
|
37
|
+
if (isVmArray(value)) return value;
|
|
38
|
+
if (value != null && typeof value == 'object') return keys(value);
|
|
39
|
+
throw new VmError(`Value is not iterable: ${display(value)}`, isVmFunction(value) ? [] : [value]);
|
|
40
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { hasOwnEnumerable, keys, setRecord } from '../../helpers/utils.js';
|
|
2
|
+
import { isVmRecord } from '../../helpers/types/index.js';
|
|
3
|
+
import type { VmAny, VmRecord, VmConst } from '../types/index.js';
|
|
4
|
+
import { $AssertInit } from './common.js';
|
|
5
|
+
|
|
6
|
+
/** 删除记录中的指定字段 */
|
|
7
|
+
export const $Omit = (value: VmAny, omitted: ReadonlyArray<number | string>): VmRecord => {
|
|
8
|
+
$AssertInit(value);
|
|
9
|
+
if (!isVmRecord(value)) return {};
|
|
10
|
+
const result: Record<string, VmConst> = {};
|
|
11
|
+
const valueKeys = keys(value);
|
|
12
|
+
const omittedSet = new Set(omitted.map(String));
|
|
13
|
+
for (const key of valueKeys) {
|
|
14
|
+
if (!omittedSet.has(key)) {
|
|
15
|
+
setRecord(result, key, value[key]);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return result;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/** 选择记录中的指定字段 */
|
|
22
|
+
export const $Pick = (value: VmAny, picked: ReadonlyArray<number | string>): VmRecord => {
|
|
23
|
+
$AssertInit(value);
|
|
24
|
+
if (!isVmRecord(value)) return {};
|
|
25
|
+
const result: Record<string, VmConst> = {};
|
|
26
|
+
for (const key of picked) {
|
|
27
|
+
const k = String(key);
|
|
28
|
+
if (hasOwnEnumerable(value, k)) {
|
|
29
|
+
setRecord(result, k, value[k]);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
@@ -1,44 +1,45 @@
|
|
|
1
1
|
import { VmError } from '../../helpers/error.js';
|
|
2
2
|
import { display } from '../../helpers/serialize.js';
|
|
3
|
+
import { toNumber } from '../../helpers/convert/number.js';
|
|
3
4
|
import { isNaN, isSafeInteger } from '../../helpers/utils.js';
|
|
4
|
-
import { isVmArray } from '../../helpers/types.js';
|
|
5
|
+
import { isVmArray } from '../../helpers/types/index.js';
|
|
5
6
|
import type { VmAny, VmArray } from '../types/index.js';
|
|
6
7
|
import { $AssertInit } from './common.js';
|
|
7
|
-
import { $ToNumber } from './convert.js';
|
|
8
8
|
const { ceil } = Math;
|
|
9
9
|
const { slice } = Array.prototype;
|
|
10
10
|
|
|
11
|
-
const sliceCore = (value:
|
|
11
|
+
const sliceCore = (value: VmAny, start: VmAny, end: VmAny, exclusive: boolean): VmArray => {
|
|
12
|
+
$AssertInit(value);
|
|
13
|
+
$AssertInit(start);
|
|
14
|
+
$AssertInit(end);
|
|
15
|
+
|
|
16
|
+
if (!isVmArray(value)) {
|
|
17
|
+
throw new VmError(`Expected array, got ${display(value)}`, []);
|
|
18
|
+
}
|
|
12
19
|
const { length } = value;
|
|
20
|
+
let s = start != null ? toNumber(start) : 0;
|
|
21
|
+
let e = end != null ? toNumber(end) : length - (exclusive ? 0 : 1);
|
|
13
22
|
|
|
14
|
-
if (isNaN(
|
|
15
|
-
else if (
|
|
23
|
+
if (isNaN(s)) s = 0;
|
|
24
|
+
else if (s < 0) s = length + s;
|
|
16
25
|
|
|
17
|
-
if (isNaN(
|
|
18
|
-
else if (
|
|
26
|
+
if (isNaN(e)) e = exclusive ? length : length - 1;
|
|
27
|
+
else if (e < 0) e = length + e;
|
|
19
28
|
|
|
20
|
-
|
|
21
|
-
if (exclusive || !isSafeInteger(
|
|
22
|
-
|
|
29
|
+
s = ceil(s);
|
|
30
|
+
if (exclusive || !isSafeInteger(e)) {
|
|
31
|
+
e = ceil(e);
|
|
23
32
|
} else {
|
|
24
|
-
|
|
33
|
+
e = e + 1;
|
|
25
34
|
}
|
|
26
|
-
return slice.call(value,
|
|
35
|
+
return slice.call(value, s, e) satisfies unknown[] as VmArray;
|
|
27
36
|
};
|
|
28
37
|
|
|
29
38
|
/** 获取数组切片 */
|
|
30
39
|
export const $Slice = (value: VmAny, start: VmAny, end: VmAny): VmArray => {
|
|
31
|
-
|
|
32
|
-
if (!isVmArray(value)) throw new VmError(`Expected array, got ${display(value)}`, []);
|
|
33
|
-
const s = start != null ? $ToNumber(start) : 0;
|
|
34
|
-
const e = end != null ? $ToNumber(end) : value.length - 1;
|
|
35
|
-
return sliceCore(value, s, e, false);
|
|
40
|
+
return sliceCore(value, start, end, false);
|
|
36
41
|
};
|
|
37
42
|
/** 获取数组切片(不包含结束位置) */
|
|
38
43
|
export const $SliceExclusive = (value: VmAny, start: VmAny, end: VmAny): VmArray => {
|
|
39
|
-
|
|
40
|
-
if (!isVmArray(value)) throw new VmError(`Expected array, got ${display(value)}`, []);
|
|
41
|
-
const s = start != null ? $ToNumber(start) : 0;
|
|
42
|
-
const e = end != null ? $ToNumber(end) : value.length;
|
|
43
|
-
return sliceCore(value, s, e, true);
|
|
44
|
+
return sliceCore(value, start, end, true);
|
|
44
45
|
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { VmError } from '../../helpers/error.js';
|
|
2
|
+
import { create } from '../../helpers/utils.js';
|
|
3
|
+
import { display } from '../../helpers/serialize.js';
|
|
4
|
+
import { isVmArray, isVmRecord, isVmExtern, isVmConst } from '../../helpers/types/index.js';
|
|
5
|
+
import { wrapToVmConst } from '../types/boundary.js';
|
|
6
|
+
import type { VmAny, VmRecord, VmConst } from '../types/index.js';
|
|
7
|
+
import { $AssertInit } from './common.js';
|
|
8
|
+
|
|
9
|
+
/** 展开记录 */
|
|
10
|
+
export const $RecordSpread = (record: VmAny): VmRecord | null => {
|
|
11
|
+
$AssertInit(record);
|
|
12
|
+
if (record == null || isVmRecord(record)) return record;
|
|
13
|
+
if (isVmArray(record)) {
|
|
14
|
+
const result: Record<string, VmConst> = {};
|
|
15
|
+
const len = record.length;
|
|
16
|
+
for (let i = 0; i < len; i++) {
|
|
17
|
+
const item = record[i];
|
|
18
|
+
result[i] = item ?? null;
|
|
19
|
+
}
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
if (isVmExtern(record)) {
|
|
23
|
+
const result: Record<string, VmConst> = create(null);
|
|
24
|
+
for (const key of record.keys()) {
|
|
25
|
+
const value = record.get(key) ?? null;
|
|
26
|
+
if (isVmConst(value)) {
|
|
27
|
+
result[key] = value;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
throw new VmError(`Expected record, array, extern or nil, got ${display(record)}`, null);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/** 展开数组 */
|
|
36
|
+
export const $ArraySpread = (array: VmAny): Iterable<VmConst | undefined> => {
|
|
37
|
+
$AssertInit(array);
|
|
38
|
+
if (array == null) return [];
|
|
39
|
+
if (isVmArray(array)) return array;
|
|
40
|
+
if (isVmExtern(array)) {
|
|
41
|
+
if (array.isArrayLike()) {
|
|
42
|
+
const result: VmConst[] = [];
|
|
43
|
+
for (let i = 0, len = array.value.length; i < len; i++) {
|
|
44
|
+
const item = array.value[i];
|
|
45
|
+
result.push(wrapToVmConst(item, (v) => array.assumeVmValue(v, i)));
|
|
46
|
+
}
|
|
47
|
+
return result;
|
|
48
|
+
} else if (typeof (array.value as Iterable<unknown>)[Symbol.iterator] == 'function') {
|
|
49
|
+
const result: VmConst[] = [];
|
|
50
|
+
for (const item of array.value as Iterable<unknown>) {
|
|
51
|
+
result.push(wrapToVmConst(item, (v) => array.assumeVmValue(v, Symbol.iterator as never)));
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
throw new VmError(`Expected array, iterable extern or nil, got ${display(array)}`, []);
|
|
57
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { VmError } from '../../helpers/error.js';
|
|
2
|
-
import { getVmType, isVmArray, isVmRecord } from '../../helpers/types.js';
|
|
2
|
+
import { getVmType, isVmArray, isVmRecord } from '../../helpers/types/index.js';
|
|
3
3
|
import type { VmTypeName, VmAny, VmArray, VmRecord, VmValue } from '../types/index.js';
|
|
4
4
|
import { $AssertInit } from './common.js';
|
|
5
5
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import type { VmAny, VmValue } from '../types/index.js';
|
|
1
|
+
import { isNaN, keys, getRecord } from '../../helpers/utils.js';
|
|
2
|
+
import { getVmType, isVmWrapper } from '../../helpers/types/index.js';
|
|
3
|
+
import type { VmAny, VmArray, VmRecord, VmValue } from '../types/index.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* 确定操作的重载
|
|
@@ -12,6 +12,38 @@ export const overloadNumberString = (a: VmAny, b: VmAny): boolean => {
|
|
|
12
12
|
return true;
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
+
const isArraySame = (a: VmArray, b: VmArray): boolean => {
|
|
16
|
+
const len = a.length;
|
|
17
|
+
if (len !== b.length) return false;
|
|
18
|
+
|
|
19
|
+
for (let i = 0; i < len; i++) {
|
|
20
|
+
if (!isSame(a[i] ?? null, b[i] ?? null)) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return true;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const isRecordSame = (a: VmRecord, b: VmRecord): boolean => {
|
|
28
|
+
const aKeys = keys(a);
|
|
29
|
+
const bKeys = keys(b);
|
|
30
|
+
|
|
31
|
+
const len = aKeys.length;
|
|
32
|
+
if (len !== bKeys.length) return false;
|
|
33
|
+
if (len === 0) return true;
|
|
34
|
+
|
|
35
|
+
for (let i = 0; i < len; i++) {
|
|
36
|
+
const key = aKeys[i]!;
|
|
37
|
+
const av = a[key] ?? null;
|
|
38
|
+
const bv = getRecord(b, key);
|
|
39
|
+
// Key not found in b
|
|
40
|
+
if (bv === undefined) return false;
|
|
41
|
+
// Key found in b, but values are not the same
|
|
42
|
+
if (!isSame(av, bv)) return false;
|
|
43
|
+
}
|
|
44
|
+
return true;
|
|
45
|
+
};
|
|
46
|
+
|
|
15
47
|
/**
|
|
16
48
|
* 检查两个 VmValue 是否相同
|
|
17
49
|
*/
|
|
@@ -24,43 +56,19 @@ export const isSame = (a: VmValue, b: VmValue): boolean => {
|
|
|
24
56
|
if (a === b) return true;
|
|
25
57
|
// Any primitives and functions arrive here are not equal
|
|
26
58
|
if (a == null || typeof a != 'object' || b == null || typeof b != 'object') return false;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (
|
|
59
|
+
const aType = getVmType(a);
|
|
60
|
+
const bType = getVmType(b);
|
|
61
|
+
if (aType !== bType) return false;
|
|
30
62
|
// Handle array values
|
|
31
|
-
if (
|
|
32
|
-
|
|
33
|
-
if (len !== b.length) {
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
|
-
// Compare array items
|
|
37
|
-
for (let i = 0; i < len; i++) {
|
|
38
|
-
if (!isSame(a[i] ?? null, b[i] ?? null)) {
|
|
39
|
-
return false;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return true;
|
|
63
|
+
if (aType === 'array') {
|
|
64
|
+
return isArraySame(a as VmArray, b as VmArray);
|
|
43
65
|
}
|
|
44
66
|
// Handle record values
|
|
45
|
-
if (
|
|
46
|
-
|
|
47
|
-
const aKeys = keys(a);
|
|
48
|
-
const bKeys = keys(b);
|
|
49
|
-
|
|
50
|
-
const len = aKeys.length;
|
|
51
|
-
if (len !== bKeys.length) return false;
|
|
52
|
-
|
|
53
|
-
if (len === 0) return true;
|
|
54
|
-
|
|
55
|
-
let i = len;
|
|
56
|
-
while (i--) {
|
|
57
|
-
const key = aKeys[i]!;
|
|
58
|
-
if (!hasOwnEnumerable(b, key)) return false;
|
|
59
|
-
const av = a[key] ?? null;
|
|
60
|
-
const bv = b[key] ?? null;
|
|
61
|
-
if (!isSame(av, bv)) return false;
|
|
62
|
-
}
|
|
63
|
-
return true;
|
|
67
|
+
if (aType === 'record') {
|
|
68
|
+
return isRecordSame(a as VmRecord, b as VmRecord);
|
|
64
69
|
}
|
|
70
|
+
// Handle wrapper values
|
|
71
|
+
if (isVmWrapper(a)) return a.same(b);
|
|
72
|
+
if (isVmWrapper(b)) return b.same(a);
|
|
65
73
|
return false;
|
|
66
74
|
};
|
package/src/vm/types/boundary.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { defineProperty, apply } from '../../helpers/utils.js';
|
|
2
|
-
import { isVmExtern, isVmFunction, isVmWrapper } from '../../helpers/types.js';
|
|
2
|
+
import { isVmExtern, isVmFunction, isVmWrapper } from '../../helpers/types/index.js';
|
|
3
3
|
import { kVmFunctionProxy } from '../../helpers/constants.js';
|
|
4
4
|
import { $Call } from '../operations/call.js';
|
|
5
5
|
import type { VmAny, VmConst, VmModule, VmPrimitive, VmValue } from './index.js';
|
|
6
6
|
import type { VmFunctionLike, VmFunction } from './function.js';
|
|
7
|
-
import { VmExtern } from './extern.js';
|
|
7
|
+
import { VmExtern } from './extern/index.js';
|
|
8
8
|
|
|
9
9
|
/** 创建 Mirascript 函数在宿主语言运行的代理 */
|
|
10
10
|
export function toVmFunctionProxy<T extends VmFunctionLike>(fn: VmFunction<T>): T {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { hasOwn } from '../../../helpers/utils.js';
|
|
2
|
+
|
|
3
|
+
const pFunction = Function.prototype;
|
|
4
|
+
const pArray = Array.prototype;
|
|
5
|
+
const pObject = Object.prototype;
|
|
6
|
+
/**
|
|
7
|
+
* Can property be accessed on the extern object?
|
|
8
|
+
*/
|
|
9
|
+
export function canAccessProperty(value: object, key: string, read: boolean): boolean {
|
|
10
|
+
// __proto__ and other “private” properties are not accessible
|
|
11
|
+
if (key.startsWith('_')) return false;
|
|
12
|
+
// Function-specific properties are not accessible
|
|
13
|
+
if (typeof value == 'function' && (key === 'prototype' || key === 'arguments' || key === 'caller')) return false;
|
|
14
|
+
if (hasOwn(value, key)) return true;
|
|
15
|
+
if (!read) return true;
|
|
16
|
+
if (!(key in value)) return false;
|
|
17
|
+
if (key === 'constructor') return false; // constructor is not accessible
|
|
18
|
+
// property is not readable if it is the same as the prototype's property
|
|
19
|
+
const prop = (value as Record<string, unknown>)[key];
|
|
20
|
+
if (key in pFunction && prop === pFunction[key as keyof (() => void)]) return false;
|
|
21
|
+
if (key in pArray && prop === pArray[key as keyof unknown[]]) return false;
|
|
22
|
+
if (key in pObject && prop === pObject[key as keyof object]) return false;
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { VmError } from '../../../helpers/error.js';
|
|
2
|
+
import { apply } from '../../../helpers/utils.js';
|
|
3
|
+
import { innerToString } from '../../../helpers/convert/string.js';
|
|
4
|
+
import { isVmExtern } from '../../../helpers/types/index.js';
|
|
5
|
+
import { kVmExtern } from '../../../helpers/constants.js';
|
|
6
|
+
import type { VmTypeName, VmAny, VmConst, VmPrimitive, VmValue } from '../index.js';
|
|
7
|
+
import { VmWrapper } from '../wrapper.js';
|
|
8
|
+
import { unwrapFromVmValue, wrapToVmValue } from '../boundary.js';
|
|
9
|
+
import { getTag } from './tag.js';
|
|
10
|
+
import { getKeys, isArrayLike, hasCustomToString } from './utils.js';
|
|
11
|
+
import { canAccessProperty } from './access.js';
|
|
12
|
+
|
|
13
|
+
const ArrayMap = Array.prototype.map;
|
|
14
|
+
|
|
15
|
+
/** 包装 Mirascript `extern` 类型的对象 */
|
|
16
|
+
export class VmExtern<const T extends object = object> extends VmWrapper<T> {
|
|
17
|
+
constructor(
|
|
18
|
+
/** 包装值 */
|
|
19
|
+
value: T,
|
|
20
|
+
/** 当 {@link value} 是函数时,绑定的 this 参数 */
|
|
21
|
+
readonly thisArg: ThisParameterType<T> | null = null,
|
|
22
|
+
) {
|
|
23
|
+
super(value);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Check if the object has a property
|
|
28
|
+
* This method will be used in {@link get}, {@link set}, {@link has}, and {@link keys} methods
|
|
29
|
+
*/
|
|
30
|
+
protected access(key: string, read: boolean): boolean {
|
|
31
|
+
return canAccessProperty(this.value, key, read);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** 决定是否对属性进行包装 */
|
|
35
|
+
assumeVmValue(value: object, key: keyof T | undefined): value is Exclude<VmConst, VmPrimitive> {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** @inheritdoc */
|
|
40
|
+
override has(key: string): boolean {
|
|
41
|
+
return this.access(key, true);
|
|
42
|
+
}
|
|
43
|
+
/** @inheritdoc */
|
|
44
|
+
override get(key: string): VmAny {
|
|
45
|
+
if (!(key in this.value)) return undefined;
|
|
46
|
+
if (!this.access(key, true)) return undefined;
|
|
47
|
+
const prop = (this.value as Record<string, unknown>)[key];
|
|
48
|
+
return wrapToVmValue(prop, this.value, (v) => this.assumeVmValue(v, key as keyof T));
|
|
49
|
+
}
|
|
50
|
+
/** Set a property on the object */
|
|
51
|
+
set(key: string, value: VmValue): boolean {
|
|
52
|
+
if (!this.access(key, false)) return false;
|
|
53
|
+
const prop = unwrapFromVmValue(value, true);
|
|
54
|
+
(this.value as Record<string, unknown>)[key] = prop;
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
/** Call extern value */
|
|
58
|
+
call(args: readonly VmValue[]): VmAny {
|
|
59
|
+
const { value } = this;
|
|
60
|
+
if (typeof value != 'function') {
|
|
61
|
+
throw VmError.from(`Not a callable extern`, null, null);
|
|
62
|
+
}
|
|
63
|
+
const caller = this.thisArg;
|
|
64
|
+
const unwrappedArgs = args.map((arg) => unwrapFromVmValue(arg, true));
|
|
65
|
+
let ret: unknown;
|
|
66
|
+
try {
|
|
67
|
+
ret = apply(value, caller, unwrappedArgs);
|
|
68
|
+
} catch (ex) {
|
|
69
|
+
throw VmError.from(`Callable extern`, ex, null);
|
|
70
|
+
}
|
|
71
|
+
return wrapToVmValue(ret, null, (obj) => this.assumeVmValue(obj, undefined));
|
|
72
|
+
}
|
|
73
|
+
/** @inheritdoc */
|
|
74
|
+
override keys(includeNonEnumerable = false): string[] {
|
|
75
|
+
const keys = getKeys(this.value, includeNonEnumerable);
|
|
76
|
+
return keys.filter((key) => this.access(key, true));
|
|
77
|
+
}
|
|
78
|
+
/** @inheritdoc */
|
|
79
|
+
override same(other: VmAny): boolean {
|
|
80
|
+
if (!isVmExtern(other)) return false;
|
|
81
|
+
return this.value === other.value && this.thisArg === other.thisArg;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Should this extern be treated as array-like?
|
|
85
|
+
*
|
|
86
|
+
* By default, this method returns true if the wrapped value is an Array or a TypedArray.
|
|
87
|
+
*/
|
|
88
|
+
isArrayLike(): this is VmExtern<ArrayLike<unknown>> {
|
|
89
|
+
return isArrayLike(this.value);
|
|
90
|
+
}
|
|
91
|
+
/** @inheritdoc */
|
|
92
|
+
override toString(useBraces: boolean): string {
|
|
93
|
+
if (hasCustomToString(this.value)) {
|
|
94
|
+
// Use the wrapped object's toString method
|
|
95
|
+
return String(this.value);
|
|
96
|
+
}
|
|
97
|
+
if (this.isArrayLike()) {
|
|
98
|
+
// Handle array-like externs specially when using default toString
|
|
99
|
+
const mapped = ArrayMap.call(this.value, (item: unknown) => {
|
|
100
|
+
if (item === undefined) return '';
|
|
101
|
+
return innerToString(wrapToVmValue(item ?? null, null, null), true);
|
|
102
|
+
});
|
|
103
|
+
const str = mapped.join(', ');
|
|
104
|
+
if (useBraces) return `[${str}]`;
|
|
105
|
+
return str;
|
|
106
|
+
}
|
|
107
|
+
// When the toString method is not overridden or invalid, provide a better default representation
|
|
108
|
+
return super.toString(useBraces);
|
|
109
|
+
}
|
|
110
|
+
/** @inheritdoc */
|
|
111
|
+
override get type(): VmTypeName {
|
|
112
|
+
return 'extern';
|
|
113
|
+
}
|
|
114
|
+
/** @inheritdoc */
|
|
115
|
+
override get tag(): string {
|
|
116
|
+
const tag = getTag(this.value);
|
|
117
|
+
if (this.isArrayLike()) {
|
|
118
|
+
return `${tag}(${this.value.length})`;
|
|
119
|
+
}
|
|
120
|
+
return tag;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
Object.defineProperty(VmExtern.prototype, kVmExtern, { value: true });
|