@mirascript/mirascript 0.1.33 → 0.1.35
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-UM7PQV4L.js → chunk-C6YXWSTI.js} +205 -156
- package/dist/chunk-C6YXWSTI.js.map +6 -0
- package/dist/{chunk-BMSHG7ON.js → chunk-HGARNCSR.js} +2 -2
- package/dist/{chunk-NRUNNMNC.js → chunk-PYMZMMRB.js} +18 -20
- package/dist/chunk-PYMZMMRB.js.map +6 -0
- package/dist/{chunk-VZGKLLRC.js → chunk-SEZII6RI.js} +19 -7
- package/dist/chunk-SEZII6RI.js.map +6 -0
- package/dist/cli/index.js +4 -4
- package/dist/compiler/compile-fast.d.ts.map +1 -1
- package/dist/compiler/create-script.d.ts +1 -1
- package/dist/compiler/create-script.d.ts.map +1 -1
- package/dist/compiler/emit/constants.d.ts +1 -1
- package/dist/compiler/emit/constants.d.ts.map +1 -1
- package/dist/compiler/worker.js +1 -1
- package/dist/helpers/serialize.d.ts +5 -4
- package/dist/helpers/serialize.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/subtle.js +3 -3
- package/dist/vm/lib/global/debug.d.ts +1 -1
- package/dist/vm/lib/global/debug.d.ts.map +1 -1
- package/dist/vm/lib/global/math-const.d.ts +6 -0
- package/dist/vm/lib/global/math-const.d.ts.map +1 -1
- package/dist/vm/lib/global/time.d.ts.map +1 -1
- package/dist/vm/lib/helpers.d.ts +1 -1
- package/dist/vm/lib/helpers.d.ts.map +1 -1
- package/dist/vm/operations/call.d.ts +7 -3
- package/dist/vm/operations/call.d.ts.map +1 -1
- package/dist/vm/operations/common.d.ts +2 -1
- package/dist/vm/operations/common.d.ts.map +1 -1
- package/dist/vm/operations/compound.d.ts +20 -10
- package/dist/vm/operations/compound.d.ts.map +1 -1
- package/dist/vm/operations/convert.d.ts +8 -4
- package/dist/vm/operations/convert.d.ts.map +1 -1
- package/dist/vm/operations/helpers.d.ts +8 -6
- package/dist/vm/operations/helpers.d.ts.map +1 -1
- package/dist/vm/operations/operator.d.ts +44 -22
- package/dist/vm/operations/operator.d.ts.map +1 -1
- package/dist/vm/operations/slice.d.ts +4 -2
- package/dist/vm/operations/slice.d.ts.map +1 -1
- package/dist/vm/operations/type-check.d.ts +14 -7
- package/dist/vm/operations/type-check.d.ts.map +1 -1
- package/dist/vm/types/boundary.d.ts +2 -0
- package/dist/vm/types/boundary.d.ts.map +1 -1
- package/dist/vm/types/extern.d.ts +7 -2
- package/dist/vm/types/extern.d.ts.map +1 -1
- package/dist/vm/types/function.d.ts +6 -0
- package/dist/vm/types/function.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/compiler/compile-fast.ts +17 -20
- package/src/compiler/create-script.ts +1 -1
- package/src/compiler/emit/constants.ts +1 -1
- package/src/helpers/serialize.ts +22 -8
- package/src/vm/lib/global/debug.ts +1 -1
- package/src/vm/lib/global/math-const.ts +17 -0
- package/src/vm/lib/global/time.ts +6 -5
- package/src/vm/lib/helpers.ts +2 -1
- package/src/vm/operations/call.ts +30 -7
- package/src/vm/operations/common.ts +3 -2
- package/src/vm/operations/compound.ts +59 -32
- package/src/vm/operations/convert.ts +12 -8
- package/src/vm/operations/helpers.ts +14 -12
- package/src/vm/operations/operator.ts +66 -44
- package/src/vm/operations/slice.ts +6 -4
- package/src/vm/operations/type-check.ts +21 -14
- package/src/vm/types/boundary.ts +27 -11
- package/src/vm/types/extern.ts +3 -1
- package/src/vm/types/function.ts +4 -0
- package/dist/chunk-NRUNNMNC.js.map +0 -6
- package/dist/chunk-UM7PQV4L.js.map +0 -6
- package/dist/chunk-VZGKLLRC.js.map +0 -6
- /package/dist/{chunk-BMSHG7ON.js.map → chunk-HGARNCSR.js.map} +0 -0
package/src/helpers/serialize.ts
CHANGED
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
isVmModule,
|
|
11
11
|
isVmRecord,
|
|
12
12
|
} from './types.js';
|
|
13
|
-
import type { VmWrapper } from '../vm/types/wrapper.js';
|
|
14
13
|
|
|
15
14
|
/** 序列化设置 */
|
|
16
15
|
export interface SerializeOptions {
|
|
@@ -302,7 +301,7 @@ export function serialize(value: VmAny, options?: Partial<SerializeOptions>): st
|
|
|
302
301
|
return serializeImpl(value, 0, getSerializeOptions(options));
|
|
303
302
|
}
|
|
304
303
|
|
|
305
|
-
/** 将 MiraScript
|
|
304
|
+
/** 将 MiraScript 函数转化为 MiraScript 字符串 */
|
|
306
305
|
export function displayFunction(value: VmFunction): string {
|
|
307
306
|
try {
|
|
308
307
|
const name = getVmFunctionInfo(value)?.fullName;
|
|
@@ -312,12 +311,27 @@ export function displayFunction(value: VmFunction): string {
|
|
|
312
311
|
return `<function>`;
|
|
313
312
|
}
|
|
314
313
|
}
|
|
315
|
-
/** 将 MiraScript
|
|
316
|
-
export function
|
|
314
|
+
/** 将 MiraScript 模块转化为 MiraScript 字符串 */
|
|
315
|
+
export function displayModule(value: VmModule): string {
|
|
317
316
|
try {
|
|
318
|
-
return value.toString(
|
|
317
|
+
return value.toString(true);
|
|
318
|
+
/* c8 ignore next 3 */
|
|
319
|
+
} catch {
|
|
320
|
+
return `<module>`;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
/** 将 MiraScript 外部值转化为 MiraScript 字符串 */
|
|
324
|
+
export function displayExtern(value: VmExtern): string {
|
|
325
|
+
try {
|
|
326
|
+
const tag = `<extern ${value.tag}>`;
|
|
327
|
+
const rep = value.toString(true);
|
|
328
|
+
if (rep === tag || rep.length > 50) {
|
|
329
|
+
return tag;
|
|
330
|
+
}
|
|
331
|
+
return `${tag} ${rep}`;
|
|
332
|
+
/* c8 ignore next 3 */
|
|
319
333
|
} catch {
|
|
320
|
-
return
|
|
334
|
+
return `<extern>`;
|
|
321
335
|
}
|
|
322
336
|
}
|
|
323
337
|
const DISPLAY_OPTIONS = Object.freeze({
|
|
@@ -333,8 +347,8 @@ const DISPLAY_OPTIONS = Object.freeze({
|
|
|
333
347
|
serializeRecord,
|
|
334
348
|
serializePropName: String,
|
|
335
349
|
serializeFunction: displayFunction,
|
|
336
|
-
serializeModule:
|
|
337
|
-
serializeExtern:
|
|
350
|
+
serializeModule: displayModule,
|
|
351
|
+
serializeExtern: displayExtern,
|
|
338
352
|
} satisfies SerializeOptions);
|
|
339
353
|
/**
|
|
340
354
|
* 将 MiraScript 值转化为 MiraScript 字符串。
|
|
@@ -114,7 +114,7 @@ export const panic = VmLib(
|
|
|
114
114
|
examples: ['panic("boom");'],
|
|
115
115
|
},
|
|
116
116
|
{
|
|
117
|
-
prefix: ['MiraScript'] as readonly string[],
|
|
117
|
+
prefix: ['MiraScript'] as readonly [prefix: string, ...additional: readonly string[]],
|
|
118
118
|
serializer: defaultSerializer,
|
|
119
119
|
},
|
|
120
120
|
);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DiagnosticCode } from '@mirascript/constants';
|
|
1
2
|
import { VmLib } from '../helpers.js';
|
|
2
3
|
|
|
3
4
|
export const PI = VmLib(Math.PI, {
|
|
@@ -8,6 +9,22 @@ export const E = VmLib(Math.E, {
|
|
|
8
9
|
summary: '自然对数的底数',
|
|
9
10
|
returnsType: 'number',
|
|
10
11
|
});
|
|
12
|
+
export const pi = VmLib(Math.PI, {
|
|
13
|
+
summary: '圆周率',
|
|
14
|
+
returnsType: 'number',
|
|
15
|
+
deprecated: {
|
|
16
|
+
use: 'PI',
|
|
17
|
+
message: DiagnosticCode.PreferUppercaseConstant,
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
export const e = VmLib(Math.E, {
|
|
21
|
+
summary: '自然对数的底数',
|
|
22
|
+
returnsType: 'number',
|
|
23
|
+
deprecated: {
|
|
24
|
+
use: 'E',
|
|
25
|
+
message: DiagnosticCode.PreferUppercaseConstant,
|
|
26
|
+
},
|
|
27
|
+
});
|
|
11
28
|
export const SQRT1_2 = VmLib(Math.SQRT1_2, {
|
|
12
29
|
summary: '1 的平方根除以 2',
|
|
13
30
|
returnsType: 'number',
|
|
@@ -69,21 +69,22 @@ export const to_datetime = VmLib(
|
|
|
69
69
|
};
|
|
70
70
|
},
|
|
71
71
|
{
|
|
72
|
-
summary: '将数据转换为
|
|
72
|
+
summary: '将数据转换为 DateTime 记录',
|
|
73
73
|
params: {
|
|
74
74
|
datetime: '要转换的数据,默认为当前时间',
|
|
75
75
|
offset: '时区偏移量(单位:小时),默认为 0',
|
|
76
76
|
fallback: '转换失败时的返回值',
|
|
77
77
|
},
|
|
78
78
|
paramsType: { datetime: 'number | string', offset: 'number', fallback: 'any' },
|
|
79
|
-
returnsType: '
|
|
79
|
+
returnsType: 'DateTime | type(fallback)',
|
|
80
80
|
examples: [
|
|
81
81
|
`
|
|
82
82
|
to_datetime(0)
|
|
83
83
|
// (
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
//
|
|
84
|
+
// year: 1970, month: 1, day: 1,
|
|
85
|
+
// hour: 0, minute: 0, second: 0,
|
|
86
|
+
// millisecond: 0,
|
|
87
|
+
// dayOfWeek: 4, offset: 0
|
|
87
88
|
// )
|
|
88
89
|
`.trim(),
|
|
89
90
|
],
|
package/src/vm/lib/helpers.ts
CHANGED
|
@@ -274,7 +274,7 @@ export function map(
|
|
|
274
274
|
/** 库函数选项 */
|
|
275
275
|
export type VmLibOption = Pick<
|
|
276
276
|
VmFunctionOption,
|
|
277
|
-
'summary' | 'params' | 'paramsType' | 'returns' | 'returnsType' | 'examples' | 'injectCp'
|
|
277
|
+
'summary' | 'params' | 'paramsType' | 'returns' | 'returnsType' | 'examples' | 'injectCp' | 'deprecated'
|
|
278
278
|
>;
|
|
279
279
|
/** 库函数 */
|
|
280
280
|
export type VmLib<T extends VmFunctionLike | VmConst = VmFunctionLike> = (T extends VmFunctionLike ? T : { value: T }) &
|
|
@@ -296,5 +296,6 @@ export function VmLib<
|
|
|
296
296
|
ret.summary = option.summary;
|
|
297
297
|
ret.examples = option.examples;
|
|
298
298
|
ret.injectCp = option.injectCp ?? false;
|
|
299
|
+
ret.deprecated = option.deprecated ?? undefined;
|
|
299
300
|
return ret as VmLib<T> & P;
|
|
300
301
|
}
|
|
@@ -1,24 +1,47 @@
|
|
|
1
1
|
import { VmError } from '../../helpers/error.js';
|
|
2
2
|
import { display } from '../../helpers/serialize.js';
|
|
3
3
|
import { isVmFunction, isVmExtern, isVmConst } from '../../helpers/types.js';
|
|
4
|
-
import type { VmAny, VmArray, VmValue } from '../types/index.js';
|
|
4
|
+
import type { VmExtern, VmFunction, VmAny, VmArray, VmValue } from '../types/index.js';
|
|
5
5
|
import { $AssertInit } from './common.js';
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
/** 调用返回类型 */
|
|
8
|
+
type CallReturn<T extends VmValue> =
|
|
9
|
+
T extends VmFunction<infer F>
|
|
10
|
+
? F extends (...args: readonly VmAny[]) => infer R
|
|
11
|
+
? // eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
|
12
|
+
R extends void | undefined
|
|
13
|
+
? null
|
|
14
|
+
: R
|
|
15
|
+
: VmValue
|
|
16
|
+
: T extends VmExtern<infer E>
|
|
17
|
+
? E extends (...args: readonly VmAny[]) => infer R
|
|
18
|
+
? // eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
|
19
|
+
R extends void | undefined
|
|
20
|
+
? null
|
|
21
|
+
: R extends VmValue
|
|
22
|
+
? R
|
|
23
|
+
: R extends object
|
|
24
|
+
? VmExtern<R>
|
|
25
|
+
: VmValue
|
|
26
|
+
: VmValue
|
|
27
|
+
: VmValue;
|
|
28
|
+
|
|
29
|
+
/** 调用函数 */
|
|
30
|
+
export function $Call<T extends VmValue, A extends readonly VmValue[]>(func: T, args: A): CallReturn<T> {
|
|
8
31
|
for (const a of args) {
|
|
9
32
|
$AssertInit(a);
|
|
10
33
|
}
|
|
11
34
|
if (isVmExtern(func)) {
|
|
12
|
-
return func.call(args as readonly VmValue[]) ?? null
|
|
35
|
+
return (func.call(args as readonly VmValue[]) ?? null) as CallReturn<T>;
|
|
13
36
|
}
|
|
14
37
|
if (isVmFunction(func)) {
|
|
15
|
-
return func(...(args as readonly VmValue[])) ?? null
|
|
38
|
+
return (func(...(args as readonly VmValue[])) ?? null) as CallReturn<T>;
|
|
16
39
|
}
|
|
17
40
|
throw new VmError(`Value is not callable: ${display(func)}`, null);
|
|
18
|
-
}
|
|
41
|
+
}
|
|
19
42
|
|
|
20
43
|
/** 过滤剩余参数数组 */
|
|
21
|
-
export
|
|
44
|
+
export function $VArgs(varags: VmAny[]): VmArray {
|
|
22
45
|
for (let i = 0, l = varags.length; i < l; i++) {
|
|
23
46
|
const el = varags[i];
|
|
24
47
|
if (!isVmConst(el)) {
|
|
@@ -26,4 +49,4 @@ export const $VArgs = (varags: VmAny[]): VmArray => {
|
|
|
26
49
|
}
|
|
27
50
|
}
|
|
28
51
|
return varags as VmArray;
|
|
29
|
-
}
|
|
52
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { VmError } from '../../helpers/error.js';
|
|
2
2
|
import type { VmAny, VmValue } from '../types/index.js';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
/** 断言值已初始化 */
|
|
5
|
+
export function $AssertInit(value: VmAny): asserts value is VmValue {
|
|
5
6
|
if (value === undefined) throw new VmError(`Uninitialized value`, null);
|
|
6
|
-
}
|
|
7
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { VmError } from '../../helpers/error.js';
|
|
2
|
-
import { hasOwnEnumerable, isNaN, NotNumber, keys, create } from '../../helpers/utils.js';
|
|
2
|
+
import { hasOwnEnumerable, isNaN, NotNumber, keys, create, isFinite } from '../../helpers/utils.js';
|
|
3
3
|
import { toNumber, toString } from '../../helpers/convert/index.js';
|
|
4
4
|
import { display } from '../../helpers/serialize.js';
|
|
5
5
|
import { isVmPrimitive, isVmArray, isVmRecord, isVmFunction, isVmExtern, isVmWrapper } from '../../helpers/types.js';
|
|
6
|
+
import { wrapToVmConst } from '../types/boundary.js';
|
|
6
7
|
import type { VmAny, VmRecord, VmValue, VmConst } from '../types/index.js';
|
|
7
8
|
import { isSame } from './utils.js';
|
|
8
9
|
import { $AssertInit } from './common.js';
|
|
@@ -11,7 +12,8 @@ import { $ToString } from './convert.js';
|
|
|
11
12
|
const { trunc } = Math;
|
|
12
13
|
const { at } = Array.prototype;
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
/** 检查值是否在可迭代对象中 */
|
|
16
|
+
export function $In(value: VmAny, iterable: VmAny): boolean {
|
|
15
17
|
$AssertInit(value);
|
|
16
18
|
$AssertInit(iterable);
|
|
17
19
|
if (iterable == null) return false;
|
|
@@ -31,16 +33,20 @@ export const $In = (value: VmAny, iterable: VmAny): boolean => {
|
|
|
31
33
|
const key = toString(value, undefined);
|
|
32
34
|
if (isVmWrapper(iterable)) return iterable.has(key);
|
|
33
35
|
return hasOwnEnumerable(iterable satisfies VmRecord, key);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** 获取值的长度 */
|
|
39
|
+
export function $Length(value: VmAny): number {
|
|
36
40
|
$AssertInit(value);
|
|
37
41
|
if (isVmArray(value)) return value.length;
|
|
38
42
|
if (isVmRecord(value)) return keys(value).length;
|
|
39
43
|
if (isVmWrapper(value)) return value.keys().length;
|
|
40
44
|
|
|
41
45
|
throw new VmError(`Value has no length: ${display(value)}`, 0);
|
|
42
|
-
}
|
|
43
|
-
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** 删除记录中的指定字段 */
|
|
49
|
+
export function $Omit(value: VmAny, omitted: ReadonlyArray<number | string>): VmRecord {
|
|
44
50
|
$AssertInit(value);
|
|
45
51
|
if (value == null || !isVmRecord(value)) return {};
|
|
46
52
|
const result: Record<string, VmConst> = {};
|
|
@@ -53,8 +59,10 @@ export const $Omit = (value: VmAny, omitted: ReadonlyArray<number | string>): Vm
|
|
|
53
59
|
}
|
|
54
60
|
}
|
|
55
61
|
return result;
|
|
56
|
-
}
|
|
57
|
-
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** 选择记录中的指定字段 */
|
|
65
|
+
export function $Pick(value: VmAny, picked: ReadonlyArray<number | string>): VmRecord {
|
|
58
66
|
$AssertInit(value);
|
|
59
67
|
if (value == null || !isVmRecord(value)) return {};
|
|
60
68
|
const result: Record<string, VmConst> = {};
|
|
@@ -65,15 +73,17 @@ export const $Pick = (value: VmAny, picked: ReadonlyArray<number | string>): VmR
|
|
|
65
73
|
}
|
|
66
74
|
}
|
|
67
75
|
return result;
|
|
68
|
-
}
|
|
69
|
-
|
|
76
|
+
}
|
|
77
|
+
/** 检查是否拥有字段 */
|
|
78
|
+
export function $Has(obj: VmAny, key: VmAny): boolean {
|
|
70
79
|
$AssertInit(obj);
|
|
71
80
|
const pk = $ToString(key);
|
|
72
81
|
if (obj == null || typeof obj != 'object') return false;
|
|
73
82
|
if (isVmWrapper(obj)) return obj.has(pk);
|
|
74
83
|
return hasOwnEnumerable(obj, pk);
|
|
75
|
-
}
|
|
76
|
-
|
|
84
|
+
}
|
|
85
|
+
/** 获取字段 */
|
|
86
|
+
export function $Get(obj: VmAny, key: VmAny): VmValue {
|
|
77
87
|
$AssertInit(obj);
|
|
78
88
|
if (isVmArray(obj)) {
|
|
79
89
|
$AssertInit(key);
|
|
@@ -83,27 +93,39 @@ export const $Get = (obj: VmAny, key: VmAny): VmValue => {
|
|
|
83
93
|
}
|
|
84
94
|
const pk = $ToString(key);
|
|
85
95
|
if (obj == null || typeof obj != 'object') return null;
|
|
86
|
-
if (isVmWrapper(obj))
|
|
96
|
+
if (isVmWrapper(obj)) {
|
|
97
|
+
if (isFinite(key) && isVmExtern(obj) && obj.isArrayLike()) {
|
|
98
|
+
let index = trunc(key as number);
|
|
99
|
+
const { length } = obj.value;
|
|
100
|
+
if (index < 0) index += length;
|
|
101
|
+
if (index >= 0 && index < length) {
|
|
102
|
+
return obj.get(String(index)) ?? null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return obj.get(pk) ?? null;
|
|
106
|
+
}
|
|
87
107
|
if (!hasOwnEnumerable(obj, pk)) return null;
|
|
88
108
|
return obj[pk] ?? null;
|
|
89
|
-
}
|
|
90
|
-
|
|
109
|
+
}
|
|
110
|
+
/** 设置字段 */
|
|
111
|
+
export function $Set(obj: VmAny, key: VmAny, value: VmAny): void {
|
|
91
112
|
$AssertInit(obj);
|
|
92
113
|
$AssertInit(value);
|
|
93
114
|
const pk = $ToString(key);
|
|
94
115
|
if (obj == null) return;
|
|
95
116
|
if (!isVmExtern(obj)) throw new VmError(`Expected extern, got ${display(obj)}`, undefined);
|
|
96
117
|
obj.set(pk, value);
|
|
97
|
-
}
|
|
98
|
-
|
|
118
|
+
}
|
|
119
|
+
/** 获取可迭代对象 */
|
|
120
|
+
export function $Iterable(value: VmAny): Iterable<VmValue | undefined> {
|
|
99
121
|
$AssertInit(value);
|
|
100
122
|
if (isVmWrapper(value)) return value.keys();
|
|
101
123
|
if (isVmArray(value)) return value;
|
|
102
124
|
if (value != null && typeof value == 'object') return keys(value);
|
|
103
125
|
throw new VmError(`Value is not iterable: ${display(value)}`, isVmFunction(value) ? [] : [value]);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export
|
|
126
|
+
}
|
|
127
|
+
/** 展开记录 */
|
|
128
|
+
export function $RecordSpread(record: VmAny): VmRecord | null {
|
|
107
129
|
$AssertInit(record);
|
|
108
130
|
if (record == null || isVmRecord(record)) return record;
|
|
109
131
|
if (isVmArray(record)) {
|
|
@@ -127,23 +149,28 @@ export const $RecordSpread = (record: VmAny): VmRecord | null => {
|
|
|
127
149
|
return result;
|
|
128
150
|
}
|
|
129
151
|
throw new VmError(`Expected record, array, extern or nil, got ${display(record)}`, null);
|
|
130
|
-
}
|
|
152
|
+
}
|
|
131
153
|
|
|
132
|
-
|
|
154
|
+
/** 展开数组 */
|
|
155
|
+
export function $ArraySpread(array: VmAny): Iterable<VmConst | undefined> {
|
|
133
156
|
$AssertInit(array);
|
|
134
157
|
if (array == null) return [];
|
|
135
158
|
if (isVmArray(array)) return array;
|
|
136
|
-
if (isVmExtern(array)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
result.push(item);
|
|
142
|
-
} else {
|
|
143
|
-
result.push(null);
|
|
159
|
+
if (isVmExtern(array)) {
|
|
160
|
+
if (array.isArrayLike()) {
|
|
161
|
+
const result: VmConst[] = [];
|
|
162
|
+
for (let i = 0, len = array.value.length; i < len; i++) {
|
|
163
|
+
const item = array.value[i];
|
|
164
|
+
result.push(wrapToVmConst(item, (v) => array.assumeVmValue(v, i)));
|
|
144
165
|
}
|
|
166
|
+
return result;
|
|
167
|
+
} else if (typeof (array.value as Iterable<unknown>)[Symbol.iterator] == 'function') {
|
|
168
|
+
const result: VmConst[] = [];
|
|
169
|
+
for (const item of array.value as Iterable<unknown>) {
|
|
170
|
+
result.push(wrapToVmConst(item, (v) => array.assumeVmValue(v, Symbol.iterator as never)));
|
|
171
|
+
}
|
|
172
|
+
return result;
|
|
145
173
|
}
|
|
146
|
-
return result;
|
|
147
174
|
}
|
|
148
175
|
throw new VmError(`Expected array, iterable extern or nil, got ${display(array)}`, []);
|
|
149
|
-
}
|
|
176
|
+
}
|
|
@@ -2,22 +2,26 @@ import { toNumber, toString, toBoolean, toFormat } from '../../helpers/convert/i
|
|
|
2
2
|
import type { VmAny } from '../types/index.js';
|
|
3
3
|
import { $AssertInit } from './common.js';
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
/** 转换为布尔值 */
|
|
6
|
+
export function $ToBoolean(value: VmAny): boolean {
|
|
6
7
|
if (typeof value == 'boolean') return value;
|
|
7
8
|
$AssertInit(value);
|
|
8
9
|
return toBoolean(value, undefined);
|
|
9
|
-
}
|
|
10
|
-
|
|
10
|
+
}
|
|
11
|
+
/** 转换为字符串 */
|
|
12
|
+
export function $ToString(value: VmAny): string {
|
|
11
13
|
if (typeof value == 'string') return value;
|
|
12
14
|
$AssertInit(value);
|
|
13
15
|
return toString(value, undefined);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
+
}
|
|
17
|
+
/** 转换为数字 */
|
|
18
|
+
export function $ToNumber(value: VmAny): number {
|
|
16
19
|
if (typeof value == 'number') return value;
|
|
17
20
|
$AssertInit(value);
|
|
18
21
|
return toNumber(value, undefined);
|
|
19
|
-
}
|
|
20
|
-
|
|
22
|
+
}
|
|
23
|
+
/** 格式化值 */
|
|
24
|
+
export function $Format(value: VmAny, format: string | null): string {
|
|
21
25
|
$AssertInit(value);
|
|
22
26
|
return toFormat(value, format);
|
|
23
|
-
}
|
|
27
|
+
}
|
|
@@ -9,31 +9,31 @@ import { $AssertInit } from './common.js';
|
|
|
9
9
|
import { $ToNumber } from './convert.js';
|
|
10
10
|
|
|
11
11
|
/** 构造 record | array 元素 */
|
|
12
|
-
export
|
|
12
|
+
export function $El(value: VmAny): VmConst {
|
|
13
13
|
$AssertInit(value);
|
|
14
14
|
if (!isVmConst(value)) return null;
|
|
15
15
|
return value;
|
|
16
|
-
}
|
|
16
|
+
}
|
|
17
17
|
|
|
18
18
|
const EMPTY = create(null);
|
|
19
19
|
/** 构造 record 可选元素 */
|
|
20
|
-
export
|
|
20
|
+
export function $ElOpt(key: string, value: VmAny): VmConst {
|
|
21
21
|
$AssertInit(value);
|
|
22
22
|
if (value == null || !isVmConst(value)) return EMPTY;
|
|
23
23
|
return { __proto__: null, [key]: value };
|
|
24
|
-
}
|
|
24
|
+
}
|
|
25
25
|
|
|
26
26
|
/** 构造函数和函数表达式 */
|
|
27
|
-
export
|
|
27
|
+
export function $Fn<T extends VmFunctionLike>(name: string | null | undefined, fn: T): VmFunction<T> {
|
|
28
28
|
defineProperty(fn, 'name', { value: name || VM_FUNCTION_ANONYMOUS_NAME });
|
|
29
29
|
return VmFunction(fn, { isLib: false, injectCp: false });
|
|
30
|
-
}
|
|
30
|
+
}
|
|
31
31
|
|
|
32
32
|
/** 读取闭包上值 */
|
|
33
|
-
export
|
|
33
|
+
export function $Upvalue(value: VmAny): VmValue {
|
|
34
34
|
$AssertInit(value);
|
|
35
35
|
return value;
|
|
36
|
-
}
|
|
36
|
+
}
|
|
37
37
|
|
|
38
38
|
const assertArrayLength = (start: number, end: number) => {
|
|
39
39
|
if (end - start > VM_ARRAY_MAX_LENGTH) {
|
|
@@ -43,7 +43,8 @@ const assertArrayLength = (start: number, end: number) => {
|
|
|
43
43
|
const isEmptyRange = (start: number, end: number) => {
|
|
44
44
|
return !isFinite(start) || !isFinite(end) || start > end;
|
|
45
45
|
};
|
|
46
|
-
|
|
46
|
+
/** 构造范围数组 */
|
|
47
|
+
export function $ArrayRange(start: VmAny, end: VmAny): VmArray {
|
|
47
48
|
const s = $ToNumber(start);
|
|
48
49
|
const e = $ToNumber(end);
|
|
49
50
|
if (isEmptyRange(s, e)) return [];
|
|
@@ -53,8 +54,9 @@ export const $ArrayRange = (start: VmAny, end: VmAny): VmArray => {
|
|
|
53
54
|
arr.push(i);
|
|
54
55
|
}
|
|
55
56
|
return arr;
|
|
56
|
-
}
|
|
57
|
-
|
|
57
|
+
}
|
|
58
|
+
/** 构造范围数组(不包含结束值) */
|
|
59
|
+
export function $ArrayRangeExclusive(start: VmAny, end: VmAny): VmArray {
|
|
58
60
|
const s = $ToNumber(start);
|
|
59
61
|
const e = $ToNumber(end);
|
|
60
62
|
if (isEmptyRange(s, e)) return [];
|
|
@@ -64,7 +66,7 @@ export const $ArrayRangeExclusive = (start: VmAny, end: VmAny): VmArray => {
|
|
|
64
66
|
arr.push(i);
|
|
65
67
|
}
|
|
66
68
|
return arr;
|
|
67
|
-
}
|
|
69
|
+
}
|
|
68
70
|
|
|
69
71
|
/** 默认执行上下文 */
|
|
70
72
|
export function $GlobalFallback(): VmContext {
|
|
@@ -6,93 +6,112 @@ import { $ToBoolean, $ToNumber, $ToString } from './convert.js';
|
|
|
6
6
|
import { isSame, overloadNumberString } from './utils.js';
|
|
7
7
|
|
|
8
8
|
// String operations
|
|
9
|
-
|
|
9
|
+
/** 字符串连接 */
|
|
10
|
+
export function $Concat(...args: readonly string[]): string {
|
|
10
11
|
return args.map((a) => toFormat(a, null)).join('');
|
|
11
|
-
}
|
|
12
|
+
}
|
|
12
13
|
|
|
13
14
|
// Unary operations
|
|
14
|
-
|
|
15
|
+
/** 正号 */
|
|
16
|
+
export function $Pos(a: VmAny): number {
|
|
15
17
|
return $ToNumber(a);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
+
}
|
|
19
|
+
/** 负号 */
|
|
20
|
+
export function $Neg(a: VmAny): number {
|
|
18
21
|
return -$ToNumber(a);
|
|
19
|
-
}
|
|
20
|
-
|
|
22
|
+
}
|
|
23
|
+
/** 非 */
|
|
24
|
+
export function $Not(a: VmAny): boolean {
|
|
21
25
|
return !$ToBoolean(a);
|
|
22
|
-
}
|
|
26
|
+
}
|
|
23
27
|
|
|
24
28
|
// Math operations
|
|
25
|
-
|
|
29
|
+
/** 加法 */
|
|
30
|
+
export function $Add(a: VmAny, b: VmAny): number {
|
|
26
31
|
return $ToNumber(a) + $ToNumber(b);
|
|
27
|
-
}
|
|
28
|
-
|
|
32
|
+
}
|
|
33
|
+
/** 减法 */
|
|
34
|
+
export function $Sub(a: VmAny, b: VmAny): number {
|
|
29
35
|
return $ToNumber(a) - $ToNumber(b);
|
|
30
|
-
}
|
|
31
|
-
|
|
36
|
+
}
|
|
37
|
+
/** 乘法 */
|
|
38
|
+
export function $Mul(a: VmAny, b: VmAny): number {
|
|
32
39
|
return $ToNumber(a) * $ToNumber(b);
|
|
33
|
-
}
|
|
34
|
-
|
|
40
|
+
}
|
|
41
|
+
/** 除法 */
|
|
42
|
+
export function $Div(a: VmAny, b: VmAny): number {
|
|
35
43
|
return $ToNumber(a) / $ToNumber(b);
|
|
36
|
-
}
|
|
37
|
-
|
|
44
|
+
}
|
|
45
|
+
/** 取模 */
|
|
46
|
+
export function $Mod(a: VmAny, b: VmAny): number {
|
|
38
47
|
return $ToNumber(a) % $ToNumber(b);
|
|
39
|
-
}
|
|
40
|
-
|
|
48
|
+
}
|
|
49
|
+
/** 乘方 */
|
|
50
|
+
export function $Pow(a: VmAny, b: VmAny): number {
|
|
41
51
|
return $ToNumber(a) ** $ToNumber(b);
|
|
42
|
-
}
|
|
52
|
+
}
|
|
43
53
|
|
|
44
54
|
// Logical operations without short-circuiting
|
|
45
|
-
|
|
55
|
+
/** 与 */
|
|
56
|
+
export function $And(a: VmAny, b: VmAny): boolean {
|
|
46
57
|
return $ToBoolean(a) && $ToBoolean(b);
|
|
47
|
-
}
|
|
48
|
-
|
|
58
|
+
}
|
|
59
|
+
/** 或 */
|
|
60
|
+
export function $Or(a: VmAny, b: VmAny): boolean {
|
|
49
61
|
return $ToBoolean(a) || $ToBoolean(b);
|
|
50
|
-
}
|
|
62
|
+
}
|
|
51
63
|
|
|
52
64
|
// Comparison operations
|
|
53
|
-
|
|
65
|
+
/** 大于 */
|
|
66
|
+
export function $Gt(a: VmAny, b: VmAny): boolean {
|
|
54
67
|
if (overloadNumberString(a, b)) {
|
|
55
68
|
return $ToNumber(a) > $ToNumber(b);
|
|
56
69
|
} else {
|
|
57
70
|
return $ToString(a) > $ToString(b);
|
|
58
71
|
}
|
|
59
|
-
}
|
|
60
|
-
|
|
72
|
+
}
|
|
73
|
+
/** 大于等于 */
|
|
74
|
+
export function $Gte(a: VmAny, b: VmAny): boolean {
|
|
61
75
|
if (overloadNumberString(a, b)) {
|
|
62
76
|
return $ToNumber(a) >= $ToNumber(b);
|
|
63
77
|
} else {
|
|
64
78
|
return $ToString(a) >= $ToString(b);
|
|
65
79
|
}
|
|
66
|
-
}
|
|
67
|
-
|
|
80
|
+
}
|
|
81
|
+
/** 小于 */
|
|
82
|
+
export function $Lt(a: VmAny, b: VmAny): boolean {
|
|
68
83
|
if (overloadNumberString(a, b)) {
|
|
69
84
|
return $ToNumber(a) < $ToNumber(b);
|
|
70
85
|
} else {
|
|
71
86
|
return $ToString(a) < $ToString(b);
|
|
72
87
|
}
|
|
73
|
-
}
|
|
74
|
-
|
|
88
|
+
}
|
|
89
|
+
/** 小于等于 */
|
|
90
|
+
export function $Lte(a: VmAny, b: VmAny): boolean {
|
|
75
91
|
if (overloadNumberString(a, b)) {
|
|
76
92
|
return $ToNumber(a) <= $ToNumber(b);
|
|
77
93
|
} else {
|
|
78
94
|
return $ToString(a) <= $ToString(b);
|
|
79
95
|
}
|
|
80
|
-
}
|
|
96
|
+
}
|
|
81
97
|
|
|
82
98
|
// Equality operations
|
|
83
|
-
|
|
99
|
+
/** 等于 */
|
|
100
|
+
export function $Eq(a: VmAny, b: VmAny): boolean {
|
|
84
101
|
$AssertInit(a);
|
|
85
102
|
$AssertInit(b);
|
|
86
103
|
// Number comparison is a special case to handle NaN correctly
|
|
87
104
|
if (typeof a == 'number' && typeof b == 'number') return a === b;
|
|
88
105
|
return isSame(a, b);
|
|
89
|
-
}
|
|
90
|
-
|
|
106
|
+
}
|
|
107
|
+
/** 不等于 */
|
|
108
|
+
export function $Neq(a: VmAny, b: VmAny): boolean {
|
|
91
109
|
return !$Eq(a, b);
|
|
92
|
-
}
|
|
110
|
+
}
|
|
93
111
|
|
|
94
112
|
const { abs, min } = Math;
|
|
95
|
-
|
|
113
|
+
/** 近似等于 */
|
|
114
|
+
export function $Aeq(a: VmAny, b: VmAny): boolean {
|
|
96
115
|
if (overloadNumberString(a, b)) {
|
|
97
116
|
const an = $ToNumber(a);
|
|
98
117
|
const bn = $ToNumber(b);
|
|
@@ -116,16 +135,19 @@ export const $Aeq = (a: VmAny, b: VmAny): boolean => {
|
|
|
116
135
|
const bn = bi.normalize('NFC');
|
|
117
136
|
return an === bn;
|
|
118
137
|
}
|
|
119
|
-
}
|
|
120
|
-
|
|
138
|
+
}
|
|
139
|
+
/** 不近似等于 */
|
|
140
|
+
export function $Naeq(a: VmAny, b: VmAny): boolean {
|
|
121
141
|
return !$Aeq(a, b);
|
|
122
|
-
}
|
|
142
|
+
}
|
|
123
143
|
|
|
124
|
-
|
|
144
|
+
/** 全等于 */
|
|
145
|
+
export function $Same(a: VmAny, b: VmAny): boolean {
|
|
125
146
|
$AssertInit(a);
|
|
126
147
|
$AssertInit(b);
|
|
127
148
|
return isSame(a, b);
|
|
128
|
-
}
|
|
129
|
-
|
|
149
|
+
}
|
|
150
|
+
/** 不全等于 */
|
|
151
|
+
export function $Nsame(a: VmAny, b: VmAny): boolean {
|
|
130
152
|
return !$Same(a, b);
|
|
131
|
-
}
|
|
153
|
+
}
|