@mirascript/mirascript 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-5FQWUJIY.js +766 -0
- package/dist/chunk-5FQWUJIY.js.map +6 -0
- package/dist/chunk-BTDGMWFK.js +202 -0
- package/dist/chunk-BTDGMWFK.js.map +6 -0
- package/dist/chunk-DCXIWIW5.js +3419 -0
- package/dist/chunk-DCXIWIW5.js.map +6 -0
- package/dist/chunk-RAPJ3XLV.js +10 -0
- package/dist/chunk-RAPJ3XLV.js.map +6 -0
- package/dist/cli/execute.d.ts +4 -0
- package/dist/cli/execute.d.ts.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +191 -0
- package/dist/cli/index.js.map +6 -0
- package/dist/cli/print.d.ts +4 -0
- package/dist/cli/print.d.ts.map +1 -0
- package/dist/compiler/compile-bytecode.d.ts +12 -0
- package/dist/compiler/compile-bytecode.d.ts.map +1 -0
- package/dist/compiler/compile-fast.d.ts +7 -0
- package/dist/compiler/compile-fast.d.ts.map +1 -0
- package/dist/compiler/create-script.d.ts +7 -0
- package/dist/compiler/create-script.d.ts.map +1 -0
- package/dist/compiler/diagnostic.d.ts +56 -0
- package/dist/compiler/diagnostic.d.ts.map +1 -0
- package/dist/compiler/emit.d.ts +4 -0
- package/dist/compiler/emit.d.ts.map +1 -0
- package/dist/compiler/index.d.ts +13 -0
- package/dist/compiler/index.d.ts.map +1 -0
- package/dist/compiler/types.d.ts +16 -0
- package/dist/compiler/types.d.ts.map +1 -0
- package/dist/compiler/worker-manager.d.ts +6 -0
- package/dist/compiler/worker-manager.d.ts.map +1 -0
- package/dist/compiler/worker.d.ts +6 -0
- package/dist/compiler/worker.d.ts.map +1 -0
- package/dist/compiler/worker.js +34 -0
- package/dist/compiler/worker.js.map +6 -0
- package/dist/helpers/constants.d.ts +3 -0
- package/dist/helpers/constants.d.ts.map +1 -0
- package/dist/helpers/serialize.d.ts +45 -0
- package/dist/helpers/serialize.d.ts.map +1 -0
- package/dist/helpers/utils.d.ts +36 -0
- package/dist/helpers/utils.d.ts.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +61 -0
- package/dist/index.js.map +6 -0
- package/dist/subtle.d.ts +7 -0
- package/dist/subtle.d.ts.map +1 -0
- package/dist/subtle.js +30 -0
- package/dist/subtle.js.map +6 -0
- package/dist/vm/env.d.ts +3 -0
- package/dist/vm/env.d.ts.map +1 -0
- package/dist/vm/error.d.ts +11 -0
- package/dist/vm/error.d.ts.map +1 -0
- package/dist/vm/helpers.d.ts +21 -0
- package/dist/vm/helpers.d.ts.map +1 -0
- package/dist/vm/index.d.ts +5 -0
- package/dist/vm/index.d.ts.map +1 -0
- package/dist/vm/lib/_helpers.d.ts +35 -0
- package/dist/vm/lib/_helpers.d.ts.map +1 -0
- package/dist/vm/lib/_loader.d.ts +16 -0
- package/dist/vm/lib/_loader.d.ts.map +1 -0
- package/dist/vm/lib/global/bit.d.ts +9 -0
- package/dist/vm/lib/global/bit.d.ts.map +1 -0
- package/dist/vm/lib/global/debug.d.ts +5 -0
- package/dist/vm/lib/global/debug.d.ts.map +1 -0
- package/dist/vm/lib/global/index.d.ts +10 -0
- package/dist/vm/lib/global/index.d.ts.map +1 -0
- package/dist/vm/lib/global/json.d.ts +4 -0
- package/dist/vm/lib/global/json.d.ts.map +1 -0
- package/dist/vm/lib/global/math-additional.d.ts +3 -0
- package/dist/vm/lib/global/math-additional.d.ts.map +1 -0
- package/dist/vm/lib/global/math-arr.d.ts +8 -0
- package/dist/vm/lib/global/math-arr.d.ts.map +1 -0
- package/dist/vm/lib/global/math-const.d.ts +3 -0
- package/dist/vm/lib/global/math-const.d.ts.map +1 -0
- package/dist/vm/lib/global/math-unary.d.ts +29 -0
- package/dist/vm/lib/global/math-unary.d.ts.map +1 -0
- package/dist/vm/lib/global/math.d.ts +9 -0
- package/dist/vm/lib/global/math.d.ts.map +1 -0
- package/dist/vm/lib/global/mod/index.d.ts +3 -0
- package/dist/vm/lib/global/mod/index.d.ts.map +1 -0
- package/dist/vm/lib/global/mod/matrix.d.ts +16 -0
- package/dist/vm/lib/global/mod/matrix.d.ts.map +1 -0
- package/dist/vm/lib/global/sequence/all-any.d.ts +4 -0
- package/dist/vm/lib/global/sequence/all-any.d.ts.map +1 -0
- package/dist/vm/lib/global/sequence/entries.d.ts +12 -0
- package/dist/vm/lib/global/sequence/entries.d.ts.map +1 -0
- package/dist/vm/lib/global/sequence/find.d.ts +10 -0
- package/dist/vm/lib/global/sequence/find.d.ts.map +1 -0
- package/dist/vm/lib/global/sequence/flatten.d.ts +3 -0
- package/dist/vm/lib/global/sequence/flatten.d.ts.map +1 -0
- package/dist/vm/lib/global/sequence/index.d.ts +12 -0
- package/dist/vm/lib/global/sequence/index.d.ts.map +1 -0
- package/dist/vm/lib/global/sequence/len.d.ts +3 -0
- package/dist/vm/lib/global/sequence/len.d.ts.map +1 -0
- package/dist/vm/lib/global/sequence/map-filter.d.ts +9 -0
- package/dist/vm/lib/global/sequence/map-filter.d.ts.map +1 -0
- package/dist/vm/lib/global/sequence/repeat.d.ts +4 -0
- package/dist/vm/lib/global/sequence/repeat.d.ts.map +1 -0
- package/dist/vm/lib/global/sequence/reverse.d.ts +3 -0
- package/dist/vm/lib/global/sequence/reverse.d.ts.map +1 -0
- package/dist/vm/lib/global/sequence/sort.d.ts +5 -0
- package/dist/vm/lib/global/sequence/sort.d.ts.map +1 -0
- package/dist/vm/lib/global/sequence/with.d.ts +5 -0
- package/dist/vm/lib/global/sequence/with.d.ts.map +1 -0
- package/dist/vm/lib/global/sequence/zip.d.ts +4 -0
- package/dist/vm/lib/global/sequence/zip.d.ts.map +1 -0
- package/dist/vm/lib/global/string.d.ts +12 -0
- package/dist/vm/lib/global/string.d.ts.map +1 -0
- package/dist/vm/lib/global/time.d.ts +15 -0
- package/dist/vm/lib/global/time.d.ts.map +1 -0
- package/dist/vm/lib/global/to-primitive.d.ts +6 -0
- package/dist/vm/lib/global/to-primitive.d.ts.map +1 -0
- package/dist/vm/operations.d.ts +49 -0
- package/dist/vm/operations.d.ts.map +1 -0
- package/dist/vm/types/checker.d.ts +30 -0
- package/dist/vm/types/checker.d.ts.map +1 -0
- package/dist/vm/types/context.d.ts +43 -0
- package/dist/vm/types/context.d.ts.map +1 -0
- package/dist/vm/types/extern.d.ts +32 -0
- package/dist/vm/types/extern.d.ts.map +1 -0
- package/dist/vm/types/function.d.ts +49 -0
- package/dist/vm/types/function.d.ts.map +1 -0
- package/dist/vm/types/index.d.ts +75 -0
- package/dist/vm/types/index.d.ts.map +1 -0
- package/dist/vm/types/module.d.ts +25 -0
- package/dist/vm/types/module.d.ts.map +1 -0
- package/dist/vm/types/script.d.ts +14 -0
- package/dist/vm/types/script.d.ts.map +1 -0
- package/dist/vm/types/wrapper.d.ts +25 -0
- package/dist/vm/types/wrapper.d.ts.map +1 -0
- package/package.json +55 -0
- package/src/cli/execute.ts +32 -0
- package/src/cli/index.ts +73 -0
- package/src/cli/print.ts +41 -0
- package/src/compiler/compile-bytecode.ts +65 -0
- package/src/compiler/compile-fast.ts +81 -0
- package/src/compiler/create-script.ts +34 -0
- package/src/compiler/diagnostic.ts +175 -0
- package/src/compiler/emit.ts +764 -0
- package/src/compiler/index.ts +67 -0
- package/src/compiler/types.ts +16 -0
- package/src/compiler/worker-manager.ts +60 -0
- package/src/compiler/worker.ts +37 -0
- package/src/helpers/constants.ts +3 -0
- package/src/helpers/serialize.ts +280 -0
- package/src/helpers/utils.ts +16 -0
- package/src/index.ts +3 -0
- package/src/subtle.ts +6 -0
- package/src/vm/env.ts +16 -0
- package/src/vm/error.ts +22 -0
- package/src/vm/helpers.ts +121 -0
- package/src/vm/index.ts +5 -0
- package/src/vm/lib/_helpers.ts +215 -0
- package/src/vm/lib/_loader.ts +51 -0
- package/src/vm/lib/global/bit.ts +93 -0
- package/src/vm/lib/global/debug.ts +36 -0
- package/src/vm/lib/global/index.ts +9 -0
- package/src/vm/lib/global/json.ts +45 -0
- package/src/vm/lib/global/math-additional.ts +71 -0
- package/src/vm/lib/global/math-arr.ts +62 -0
- package/src/vm/lib/global/math-const.ts +2 -0
- package/src/vm/lib/global/math-unary.ts +171 -0
- package/src/vm/lib/global/math.ts +27 -0
- package/src/vm/lib/global/mod/index.ts +4 -0
- package/src/vm/lib/global/mod/matrix.ts +579 -0
- package/src/vm/lib/global/sequence/all-any.ts +73 -0
- package/src/vm/lib/global/sequence/entries.ts +67 -0
- package/src/vm/lib/global/sequence/find.ts +49 -0
- package/src/vm/lib/global/sequence/flatten.ts +16 -0
- package/src/vm/lib/global/sequence/index.ts +11 -0
- package/src/vm/lib/global/sequence/len.ts +15 -0
- package/src/vm/lib/global/sequence/map-filter.ts +82 -0
- package/src/vm/lib/global/sequence/repeat.ts +28 -0
- package/src/vm/lib/global/sequence/reverse.ts +17 -0
- package/src/vm/lib/global/sequence/sort.ts +88 -0
- package/src/vm/lib/global/sequence/with.ts +43 -0
- package/src/vm/lib/global/sequence/zip.ts +40 -0
- package/src/vm/lib/global/string.ts +149 -0
- package/src/vm/lib/global/time.ts +73 -0
- package/src/vm/lib/global/to-primitive.ts +58 -0
- package/src/vm/operations.ts +497 -0
- package/src/vm/types/checker.ts +164 -0
- package/src/vm/types/context.ts +161 -0
- package/src/vm/types/extern.ts +166 -0
- package/src/vm/types/function.ts +136 -0
- package/src/vm/types/index.ts +124 -0
- package/src/vm/types/module.ts +40 -0
- package/src/vm/types/script.ts +18 -0
- package/src/vm/types/wrapper.ts +28 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { $ToString, $ToNumber } from '../../operations.js';
|
|
2
|
+
import { VmLib } from '../_helpers.js';
|
|
3
|
+
import { isFinite } from '../../../helpers/utils.js';
|
|
4
|
+
|
|
5
|
+
export const to_timestamp = VmLib(
|
|
6
|
+
(datetime) => {
|
|
7
|
+
if (datetime == null) {
|
|
8
|
+
return Date.now();
|
|
9
|
+
}
|
|
10
|
+
if (typeof datetime == 'number') {
|
|
11
|
+
return new Date(datetime).getTime();
|
|
12
|
+
}
|
|
13
|
+
const str = $ToString(datetime);
|
|
14
|
+
if (!str) return Number.NaN;
|
|
15
|
+
const num = $ToNumber(str);
|
|
16
|
+
if (isFinite(num)) return num;
|
|
17
|
+
return Date.parse(str);
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
summary: '将数据转换为 Unix 毫秒时间戳',
|
|
21
|
+
params: { datetime: '要转换的数据,默认为当前时间' },
|
|
22
|
+
paramsType: { datetime: 'string | number' },
|
|
23
|
+
returnsType: 'number',
|
|
24
|
+
examples: ['to_timestamp("1970-01-01T00:00:00Z") // 0'],
|
|
25
|
+
},
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
export const to_datetime = VmLib(
|
|
29
|
+
(datetime, offset) => {
|
|
30
|
+
const timestamp = to_timestamp(datetime);
|
|
31
|
+
if (!isFinite(timestamp)) return null;
|
|
32
|
+
const o = $ToNumber(offset ?? 0) || 0;
|
|
33
|
+
const dateOffset = new Date(timestamp + o * 1000 * 60 * 60);
|
|
34
|
+
return {
|
|
35
|
+
year: dateOffset.getUTCFullYear(),
|
|
36
|
+
month: dateOffset.getUTCMonth() + 1,
|
|
37
|
+
day: dateOffset.getUTCDate(),
|
|
38
|
+
hour: dateOffset.getUTCHours(),
|
|
39
|
+
minute: dateOffset.getUTCMinutes(),
|
|
40
|
+
second: dateOffset.getUTCSeconds(),
|
|
41
|
+
millisecond: dateOffset.getUTCMilliseconds(),
|
|
42
|
+
dayOfWeek: dateOffset.getUTCDay(),
|
|
43
|
+
offset: o,
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
summary: '将数据转换为 Date 对象',
|
|
48
|
+
params: {
|
|
49
|
+
datetime: '要转换的数据,默认为当前时间',
|
|
50
|
+
offset: '时区偏移量(单位:小时),默认为 0',
|
|
51
|
+
},
|
|
52
|
+
paramsType: { datetime: 'string | number', offset: 'number' },
|
|
53
|
+
returnsType: 'Date',
|
|
54
|
+
examples: [
|
|
55
|
+
'to_datetime(0) // (year: 1970, month: 1, day: 1, hour: 0, minute: 0, second: 0, millisecond: 0, dayOfWeek: 4, offset: 0)',
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
export const to_iso8601 = VmLib(
|
|
61
|
+
(datetime) => {
|
|
62
|
+
const timestamp = to_timestamp(datetime);
|
|
63
|
+
if (!isFinite(timestamp)) return null;
|
|
64
|
+
return new Date(timestamp).toISOString();
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
summary: '将数据转换为 ISO 8601 格式的字符串',
|
|
68
|
+
params: { datetime: '要转换的数据,默认为当前时间' },
|
|
69
|
+
paramsType: { datetime: 'string | number' },
|
|
70
|
+
returnsType: 'string',
|
|
71
|
+
examples: ['to_iso8601(0) // "1970-01-01T00:00:00.000Z"'],
|
|
72
|
+
},
|
|
73
|
+
);
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { $ToString, $ToNumber, $ToBoolean, $Format } from '../../operations.js';
|
|
2
|
+
import { required, VmLib } from '../_helpers.js';
|
|
3
|
+
|
|
4
|
+
export const to_string = VmLib(
|
|
5
|
+
(data) => {
|
|
6
|
+
required('data', data, '');
|
|
7
|
+
return $ToString(data);
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
summary: '将数据转换为字符串',
|
|
11
|
+
params: { data: '要转换的数据' },
|
|
12
|
+
paramsType: { data: 'any' },
|
|
13
|
+
returnsType: 'string',
|
|
14
|
+
examples: ['to_string([1, 2]) // "1, 2"'],
|
|
15
|
+
},
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
export const to_number = VmLib(
|
|
19
|
+
(data) => {
|
|
20
|
+
required('data', data, Number.NaN);
|
|
21
|
+
return $ToNumber(data);
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
summary: '将数据转换为数字',
|
|
25
|
+
params: { data: '要转换的数据' },
|
|
26
|
+
paramsType: { data: 'any' },
|
|
27
|
+
returnsType: 'number',
|
|
28
|
+
examples: ['to_number("1.5") // 1.5'],
|
|
29
|
+
},
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
export const to_boolean = VmLib(
|
|
33
|
+
(data) => {
|
|
34
|
+
required('data', data, false);
|
|
35
|
+
return $ToBoolean(data);
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
summary: '将数据转换为布尔值',
|
|
39
|
+
params: { data: '要转换的数据' },
|
|
40
|
+
paramsType: { data: 'any' },
|
|
41
|
+
returnsType: 'boolean',
|
|
42
|
+
examples: ['to_boolean(nil) // false'],
|
|
43
|
+
},
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
export const format = VmLib(
|
|
47
|
+
(data, format) => {
|
|
48
|
+
required('data', data, '');
|
|
49
|
+
return $Format(data, format);
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
summary: '将数据格式化为指定格式的字符串',
|
|
53
|
+
params: { data: '要格式化的数据', format: '格式字符串' },
|
|
54
|
+
paramsType: { data: 'any', format: 'string' },
|
|
55
|
+
returnsType: 'string',
|
|
56
|
+
examples: ['format(12, ".3") // "12.000"'],
|
|
57
|
+
},
|
|
58
|
+
);
|
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
import { VmError } from './error.js';
|
|
2
|
+
import {
|
|
3
|
+
isVmArray,
|
|
4
|
+
VmModule,
|
|
5
|
+
VmExtern,
|
|
6
|
+
isVmRecord,
|
|
7
|
+
getVmFunctionInfo,
|
|
8
|
+
isVmPrimitive,
|
|
9
|
+
type TypeName,
|
|
10
|
+
type VmAny,
|
|
11
|
+
type VmImmutable,
|
|
12
|
+
type VmRecord,
|
|
13
|
+
type VmValue,
|
|
14
|
+
isVmFunction,
|
|
15
|
+
type VmArray,
|
|
16
|
+
type VmConst,
|
|
17
|
+
isVmExtern,
|
|
18
|
+
type VmPrimitive,
|
|
19
|
+
type VmFunction,
|
|
20
|
+
} from './types/index.js';
|
|
21
|
+
import { VmWrapper } from './types/wrapper.js';
|
|
22
|
+
import { hasOwnEnumerable, isNaN, isSafeInteger, keys, create } from '../helpers/utils.js';
|
|
23
|
+
|
|
24
|
+
const { abs, min, trunc, ceil } = Math;
|
|
25
|
+
const { slice, at } = Array.prototype;
|
|
26
|
+
const { POSITIVE_INFINITY, NEGATIVE_INFINITY } = Number;
|
|
27
|
+
|
|
28
|
+
const isSame = (a: VmValue, b: VmValue): boolean => {
|
|
29
|
+
// Check for NaN
|
|
30
|
+
if (typeof a == 'number' && typeof b == 'number') return a === b || (isNaN(a) && isNaN(b));
|
|
31
|
+
// Check all primitive types, and fast path for reference equality
|
|
32
|
+
if (a === b) return true;
|
|
33
|
+
// Any primitives and functions arrive here are not equal
|
|
34
|
+
if (a == null || typeof a != 'object' || b == null || typeof b != 'object') return false;
|
|
35
|
+
// Handle wrapper values
|
|
36
|
+
if (a instanceof VmWrapper) return a.same(b);
|
|
37
|
+
if (b instanceof VmWrapper) return b.same(a);
|
|
38
|
+
// Handle array values
|
|
39
|
+
if (isVmArray(a) && isVmArray(b)) {
|
|
40
|
+
// Compare array items
|
|
41
|
+
if (a.length !== b.length) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
const len = a.length;
|
|
45
|
+
for (let i = 0; i < len; i++) {
|
|
46
|
+
if (!isSame(a[i] ?? null, b[i] ?? null)) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
// Handle record values
|
|
53
|
+
if (!isVmArray(a) && !isVmArray(b)) {
|
|
54
|
+
// Compare record fields
|
|
55
|
+
const aKeys = keys(a);
|
|
56
|
+
const bKeys = keys(b);
|
|
57
|
+
if (aKeys.length !== bKeys.length) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
for (const key of aKeys) {
|
|
61
|
+
if (!hasOwnEnumerable(b, key)) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
if (!isSame(a[key] ?? null, b[key] ?? null)) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
return false;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const overloadNumberString = (a: VmAny, b: VmAny): boolean => {
|
|
74
|
+
if (typeof a == 'number' || typeof b == 'number') return true;
|
|
75
|
+
if (typeof a == 'string' || typeof b == 'string') return false;
|
|
76
|
+
return true;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Math operations
|
|
80
|
+
export const $Add = (a: VmAny, b: VmAny): number => {
|
|
81
|
+
return $ToNumber(a) + $ToNumber(b);
|
|
82
|
+
};
|
|
83
|
+
export const $Sub = (a: VmAny, b: VmAny): number => {
|
|
84
|
+
return $ToNumber(a) - $ToNumber(b);
|
|
85
|
+
};
|
|
86
|
+
export const $Mul = (a: VmAny, b: VmAny): number => {
|
|
87
|
+
return $ToNumber(a) * $ToNumber(b);
|
|
88
|
+
};
|
|
89
|
+
export const $Div = (a: VmAny, b: VmAny): number => {
|
|
90
|
+
return $ToNumber(a) / $ToNumber(b);
|
|
91
|
+
};
|
|
92
|
+
export const $Mod = (a: VmAny, b: VmAny): number => {
|
|
93
|
+
return $ToNumber(a) % $ToNumber(b);
|
|
94
|
+
};
|
|
95
|
+
export const $Pow = (a: VmAny, b: VmAny): number => {
|
|
96
|
+
return $ToNumber(a) ** $ToNumber(b);
|
|
97
|
+
};
|
|
98
|
+
// Logical operations without short-circuiting
|
|
99
|
+
export const $And = (a: VmAny, b: VmAny): boolean => {
|
|
100
|
+
return $ToBoolean(a) && $ToBoolean(b);
|
|
101
|
+
};
|
|
102
|
+
export const $Or = (a: VmAny, b: VmAny): boolean => {
|
|
103
|
+
return $ToBoolean(a) || $ToBoolean(b);
|
|
104
|
+
};
|
|
105
|
+
// Comparison operations
|
|
106
|
+
export const $Gt = (a: VmAny, b: VmAny): boolean => {
|
|
107
|
+
if (overloadNumberString(a, b)) {
|
|
108
|
+
return $ToNumber(a) > $ToNumber(b);
|
|
109
|
+
} else {
|
|
110
|
+
return $ToString(a) > $ToString(b);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
export const $Gte = (a: VmAny, b: VmAny): boolean => {
|
|
114
|
+
if (overloadNumberString(a, b)) {
|
|
115
|
+
return $ToNumber(a) >= $ToNumber(b);
|
|
116
|
+
} else {
|
|
117
|
+
return $ToString(a) >= $ToString(b);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
export const $Lt = (a: VmAny, b: VmAny): boolean => {
|
|
121
|
+
if (overloadNumberString(a, b)) {
|
|
122
|
+
return $ToNumber(a) < $ToNumber(b);
|
|
123
|
+
} else {
|
|
124
|
+
return $ToString(a) < $ToString(b);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
export const $Lte = (a: VmAny, b: VmAny): boolean => {
|
|
128
|
+
if (overloadNumberString(a, b)) {
|
|
129
|
+
return $ToNumber(a) <= $ToNumber(b);
|
|
130
|
+
} else {
|
|
131
|
+
return $ToString(a) <= $ToString(b);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
export const $Eq = (a: VmAny, b: VmAny): boolean => {
|
|
135
|
+
$AssertInit(a);
|
|
136
|
+
$AssertInit(b);
|
|
137
|
+
// Number comparison is a special case to handle NaN correctly
|
|
138
|
+
if (typeof a == 'number' && typeof b == 'number') return a === b;
|
|
139
|
+
return isSame(a, b);
|
|
140
|
+
};
|
|
141
|
+
export const $Neq = (a: VmAny, b: VmAny): boolean => !$Eq(a, b);
|
|
142
|
+
const stringComparer = new Intl.Collator('en', {
|
|
143
|
+
usage: 'sort',
|
|
144
|
+
numeric: false,
|
|
145
|
+
sensitivity: 'base',
|
|
146
|
+
});
|
|
147
|
+
export const $Aeq = (a: VmAny, b: VmAny): boolean => {
|
|
148
|
+
if (overloadNumberString(a, b)) {
|
|
149
|
+
const an = $ToNumber(a);
|
|
150
|
+
const bn = $ToNumber(b);
|
|
151
|
+
const EPS = 1e-15;
|
|
152
|
+
if (isNaN(an) || isNaN(bn)) return false;
|
|
153
|
+
// Since Inf - Inf is NaN, we must check for equality first
|
|
154
|
+
if (an === bn) return true;
|
|
155
|
+
const absoluteDifference = abs(an - bn);
|
|
156
|
+
if (absoluteDifference < EPS) return true;
|
|
157
|
+
const base = min(abs(an), abs(bn));
|
|
158
|
+
return absoluteDifference < base * EPS;
|
|
159
|
+
} else {
|
|
160
|
+
// For strings, we use localeCompare for case-insensitive accent-insensitive comparison
|
|
161
|
+
const as = $ToString(a);
|
|
162
|
+
const bs = $ToString(b);
|
|
163
|
+
if (as === bs) return true;
|
|
164
|
+
return stringComparer.compare(as, bs) === 0;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
export const $Naeq = (a: VmAny, b: VmAny): boolean => !$Aeq(a, b);
|
|
168
|
+
export const $Same = (a: VmAny, b: VmAny): boolean => {
|
|
169
|
+
$AssertInit(a);
|
|
170
|
+
$AssertInit(b);
|
|
171
|
+
return isSame(a, b);
|
|
172
|
+
};
|
|
173
|
+
export const $Nsame = (a: VmAny, b: VmAny): boolean => !$Same(a, b);
|
|
174
|
+
export const $In = (value: VmAny, iterable: VmAny): boolean => {
|
|
175
|
+
$AssertInit(value);
|
|
176
|
+
$AssertInit(iterable);
|
|
177
|
+
if (iterable == null) return false;
|
|
178
|
+
if (isVmArray(iterable)) {
|
|
179
|
+
if (value == null) {
|
|
180
|
+
// array may have empty slots
|
|
181
|
+
for (const item of iterable) {
|
|
182
|
+
if (item == null) return true;
|
|
183
|
+
}
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
// JS %SameValueZero is same with `isSame` in this context
|
|
187
|
+
if (isVmPrimitive(value)) return iterable.includes(value);
|
|
188
|
+
// value is not null here, so it's ok to skip empty slots, since `isSame(null, something)` is always false
|
|
189
|
+
value satisfies NonNullable<VmValue>;
|
|
190
|
+
return iterable.some((item = null) => isSame(item, value));
|
|
191
|
+
}
|
|
192
|
+
// iterable is a record or an extern here, value should be a string
|
|
193
|
+
if (iterable instanceof VmWrapper) return iterable.has($ToString(value));
|
|
194
|
+
if (typeof iterable == 'object') return hasOwnEnumerable(iterable, $ToString(value));
|
|
195
|
+
iterable satisfies VmPrimitive | VmFunction;
|
|
196
|
+
return false;
|
|
197
|
+
};
|
|
198
|
+
export const $Concat = (...args: string[]): string => {
|
|
199
|
+
return args.map($Format).join('');
|
|
200
|
+
};
|
|
201
|
+
export const $Pos = (a: VmAny): number => $ToNumber(a);
|
|
202
|
+
export const $Neg = (a: VmAny): number => -$ToNumber(a);
|
|
203
|
+
export const $Not = (a: VmAny): boolean => !$ToBoolean(a);
|
|
204
|
+
export const $Length = (value: VmAny): number => {
|
|
205
|
+
$AssertInit(value);
|
|
206
|
+
if (isVmArray(value)) return value.length;
|
|
207
|
+
if (isVmRecord(value)) return keys(value).length;
|
|
208
|
+
if (value instanceof VmWrapper) {
|
|
209
|
+
return value.keys().length;
|
|
210
|
+
}
|
|
211
|
+
return Number.NaN;
|
|
212
|
+
};
|
|
213
|
+
export const $Omit = (value: VmAny, omitted: ReadonlyArray<string | number>): VmRecord => {
|
|
214
|
+
$AssertInit(value);
|
|
215
|
+
if (value == null || !isVmRecord(value)) return {};
|
|
216
|
+
const result: Record<string, VmConst> = {};
|
|
217
|
+
const valueKeys = keys(value);
|
|
218
|
+
const omittedSet = new Set(omitted.map($ToString));
|
|
219
|
+
for (const key of valueKeys) {
|
|
220
|
+
if (!omittedSet.has(key)) {
|
|
221
|
+
result[key] = value[key] ?? null;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return result;
|
|
225
|
+
};
|
|
226
|
+
export const $Pick = (value: VmAny, picked: ReadonlyArray<string | number>): VmRecord => {
|
|
227
|
+
$AssertInit(value);
|
|
228
|
+
if (value == null || !isVmRecord(value)) return {};
|
|
229
|
+
const result: Record<string, VmConst> = {};
|
|
230
|
+
for (const key of picked) {
|
|
231
|
+
const k = $ToString(key);
|
|
232
|
+
if (hasOwnEnumerable(value, k)) {
|
|
233
|
+
result[k] = value[k] ?? null;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return result;
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const sliceCore = (value: VmArray, start: number, end: number, exclusive: boolean): VmArray => {
|
|
240
|
+
const { length } = value;
|
|
241
|
+
|
|
242
|
+
if (isNaN(start)) start = 0;
|
|
243
|
+
else if (start < 0) start = length + start;
|
|
244
|
+
if (isNaN(end)) end = exclusive ? length : length - 1;
|
|
245
|
+
else if (end < 0) end = length + end;
|
|
246
|
+
|
|
247
|
+
start = ceil(start);
|
|
248
|
+
if (exclusive || !isSafeInteger(end)) {
|
|
249
|
+
end = ceil(end);
|
|
250
|
+
} else {
|
|
251
|
+
end = end + 1;
|
|
252
|
+
}
|
|
253
|
+
return slice.call(value, start, end) as VmArray;
|
|
254
|
+
};
|
|
255
|
+
export const $Slice = (value: VmAny, start: VmAny, end: VmAny): VmArray => {
|
|
256
|
+
$AssertInit(value);
|
|
257
|
+
if (!isVmArray(value)) throw new VmError(`Expected array, got ${$Type(value)}`, []);
|
|
258
|
+
const s = start != null ? $ToNumber(start) : 0;
|
|
259
|
+
const e = end != null ? $ToNumber(end) : value.length - 1;
|
|
260
|
+
return sliceCore(value, s, e, false);
|
|
261
|
+
};
|
|
262
|
+
export const $SliceExclusive = (value: VmAny, start: VmAny, end: VmAny): VmArray => {
|
|
263
|
+
$AssertInit(value);
|
|
264
|
+
if (!isVmArray(value)) throw new VmError(`Expected array, got ${$Type(value)}`, []);
|
|
265
|
+
const s = start != null ? $ToNumber(start) : 0;
|
|
266
|
+
const e = end != null ? $ToNumber(end) : value.length;
|
|
267
|
+
return sliceCore(value, s, e, true);
|
|
268
|
+
};
|
|
269
|
+
export const $AssertInit: (value: VmAny) => asserts value is VmValue = (value) => {
|
|
270
|
+
if (value === undefined) throw new VmError(`Uninitialized value`, null);
|
|
271
|
+
};
|
|
272
|
+
export const $Call = (func: VmValue, args: readonly VmAny[]): VmValue => {
|
|
273
|
+
for (const a of args) {
|
|
274
|
+
$AssertInit(a);
|
|
275
|
+
}
|
|
276
|
+
if (func instanceof VmExtern) {
|
|
277
|
+
return func.call(args as readonly VmValue[]) ?? null;
|
|
278
|
+
}
|
|
279
|
+
if (isVmFunction(func)) {
|
|
280
|
+
return func(...(args as readonly VmValue[])) ?? null;
|
|
281
|
+
}
|
|
282
|
+
throw new VmError(`Expected callable, got ${$Type(func)}`, null);
|
|
283
|
+
};
|
|
284
|
+
export const $Type = (value: VmAny): TypeName => {
|
|
285
|
+
if (value === undefined || value === null) return 'nil';
|
|
286
|
+
if (value instanceof VmExtern) return 'extern';
|
|
287
|
+
if (value instanceof VmModule) return 'module';
|
|
288
|
+
if (isVmArray(value)) return 'array';
|
|
289
|
+
if (typeof value == 'object') return 'record';
|
|
290
|
+
return typeof value as TypeName;
|
|
291
|
+
};
|
|
292
|
+
export const $ToBoolean = (value: VmAny): boolean => {
|
|
293
|
+
$AssertInit(value);
|
|
294
|
+
return value != null && value !== false;
|
|
295
|
+
};
|
|
296
|
+
/** 将值转为字符串 */
|
|
297
|
+
function numberToString(value: number): string {
|
|
298
|
+
if (isNaN(value)) return 'nan';
|
|
299
|
+
if (value === Infinity) return 'inf';
|
|
300
|
+
if (value === -Infinity) return '-inf';
|
|
301
|
+
return String(value);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/** 将值转为字符串 */
|
|
305
|
+
function innerToString(value: VmAny, useBraces: boolean): string {
|
|
306
|
+
if (value == null) return 'nil';
|
|
307
|
+
if (value instanceof VmWrapper) return value.toString();
|
|
308
|
+
if (typeof value == 'function') {
|
|
309
|
+
const name = getVmFunctionInfo(value)?.fullName;
|
|
310
|
+
return name ? `<function ${name}>` : `<function>`;
|
|
311
|
+
}
|
|
312
|
+
if (isVmArray(value)) {
|
|
313
|
+
const strings: string[] = [];
|
|
314
|
+
for (const item of value) {
|
|
315
|
+
strings.push(innerToString(item, true));
|
|
316
|
+
}
|
|
317
|
+
// 在 join 过程中会自动把 null/undefined 和 empty slot 转为 ''
|
|
318
|
+
// 与 innerToString 行为不一致
|
|
319
|
+
const results = strings.join(', ');
|
|
320
|
+
if (!useBraces) return results;
|
|
321
|
+
return `[${results}]`;
|
|
322
|
+
}
|
|
323
|
+
if (typeof value == 'object') {
|
|
324
|
+
const entries = keys(value)
|
|
325
|
+
.map((key) => `${key}: ${innerToString(value[key], true)}`)
|
|
326
|
+
.join(', ');
|
|
327
|
+
if (!useBraces) return entries;
|
|
328
|
+
return `(${entries})`;
|
|
329
|
+
}
|
|
330
|
+
if (typeof value == 'number') {
|
|
331
|
+
return numberToString(value);
|
|
332
|
+
}
|
|
333
|
+
return String(value);
|
|
334
|
+
}
|
|
335
|
+
export const $ToString = (value: VmAny): string => {
|
|
336
|
+
$AssertInit(value);
|
|
337
|
+
if (typeof value == 'string') return value;
|
|
338
|
+
if (value === null) return '';
|
|
339
|
+
return innerToString(value, false);
|
|
340
|
+
};
|
|
341
|
+
export const $ToNumber = (value: VmAny): number => {
|
|
342
|
+
$AssertInit(value);
|
|
343
|
+
if (typeof value == 'number') return value;
|
|
344
|
+
if (value == null) return 0;
|
|
345
|
+
if (typeof value == 'string') {
|
|
346
|
+
value = value.trim();
|
|
347
|
+
if (value === '') return 0;
|
|
348
|
+
if (value === 'inf' || value === '+inf' || value === 'Infinity' || value === '+Infinity') {
|
|
349
|
+
return POSITIVE_INFINITY;
|
|
350
|
+
}
|
|
351
|
+
if (value === '-inf' || value === '-Infinity') {
|
|
352
|
+
return NEGATIVE_INFINITY;
|
|
353
|
+
}
|
|
354
|
+
return Number(value);
|
|
355
|
+
}
|
|
356
|
+
if (typeof value == 'boolean') return value ? 1 : 0;
|
|
357
|
+
return Number.NaN;
|
|
358
|
+
};
|
|
359
|
+
export const $IsBoolean = (value: VmAny): value is boolean => {
|
|
360
|
+
$AssertInit(value);
|
|
361
|
+
return typeof value == 'boolean';
|
|
362
|
+
};
|
|
363
|
+
export const $IsNumber = (value: VmAny): value is number => {
|
|
364
|
+
$AssertInit(value);
|
|
365
|
+
return typeof value == 'number';
|
|
366
|
+
};
|
|
367
|
+
export const $IsString = (value: VmAny): value is string => {
|
|
368
|
+
$AssertInit(value);
|
|
369
|
+
return typeof value == 'string';
|
|
370
|
+
};
|
|
371
|
+
export const $IsRecord = (value: VmAny): value is VmRecord => {
|
|
372
|
+
$AssertInit(value);
|
|
373
|
+
return isVmRecord(value);
|
|
374
|
+
};
|
|
375
|
+
export const $IsArray = (value: VmAny): value is VmArray => {
|
|
376
|
+
$AssertInit(value);
|
|
377
|
+
return isVmArray(value);
|
|
378
|
+
};
|
|
379
|
+
export const $AssertNonNil = (value: VmAny): asserts value is NonNullable<VmValue> => {
|
|
380
|
+
$AssertInit(value);
|
|
381
|
+
if (value !== null) return;
|
|
382
|
+
throw new VmError('Expected non-nil value', null);
|
|
383
|
+
};
|
|
384
|
+
export const $Has = (obj: VmAny, key: VmAny): boolean => {
|
|
385
|
+
$AssertInit(obj);
|
|
386
|
+
const pk = $ToString(key);
|
|
387
|
+
if (obj == null || typeof obj != 'object') return false;
|
|
388
|
+
if (obj instanceof VmWrapper) return obj.has(pk);
|
|
389
|
+
return hasOwnEnumerable(obj, pk);
|
|
390
|
+
};
|
|
391
|
+
export const $Get = (obj: VmAny, key: VmAny): VmValue => {
|
|
392
|
+
$AssertInit(obj);
|
|
393
|
+
if (isVmArray(obj)) {
|
|
394
|
+
const index = $ToNumber(key);
|
|
395
|
+
if (isNaN(index)) return null;
|
|
396
|
+
return (at.call(obj, trunc(index)) as VmConst | undefined) ?? null;
|
|
397
|
+
}
|
|
398
|
+
const pk = $ToString(key);
|
|
399
|
+
if (obj == null || typeof obj != 'object') return null;
|
|
400
|
+
if (obj instanceof VmWrapper) return obj.get(pk) ?? null;
|
|
401
|
+
if (!hasOwnEnumerable(obj, pk)) return null;
|
|
402
|
+
return (obj as Record<string, VmImmutable>)[pk] ?? null;
|
|
403
|
+
};
|
|
404
|
+
export const $Set = (obj: VmAny, key: VmAny, value: VmAny): void => {
|
|
405
|
+
$AssertInit(obj);
|
|
406
|
+
$AssertInit(value);
|
|
407
|
+
const pk = $ToString(key);
|
|
408
|
+
if (obj == null) return;
|
|
409
|
+
if (!isVmExtern(obj)) throw new VmError(`Expected extern, got ${$Type(obj)}`, undefined);
|
|
410
|
+
obj.set(pk, value);
|
|
411
|
+
};
|
|
412
|
+
export const $Iterable = (value: VmAny): Iterable<VmValue | undefined> => {
|
|
413
|
+
$AssertInit(value);
|
|
414
|
+
if (value instanceof VmWrapper) return value.keys();
|
|
415
|
+
if (isVmArray(value)) return value;
|
|
416
|
+
if (value != null && typeof value == 'object') return keys(value);
|
|
417
|
+
throw new VmError(`Value is not iterable`, isVmFunction(value) ? [] : [value]);
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
export const $RecordSpread = (record: VmAny): VmRecord | null => {
|
|
421
|
+
$AssertInit(record);
|
|
422
|
+
if (record == null || isVmRecord(record)) return record;
|
|
423
|
+
if (isVmArray(record)) {
|
|
424
|
+
const result: Record<string, VmConst> = {};
|
|
425
|
+
const len = record.length;
|
|
426
|
+
for (let i = 0; i < len; i++) {
|
|
427
|
+
const item = record[i];
|
|
428
|
+
result[i] = item ?? null;
|
|
429
|
+
}
|
|
430
|
+
return result;
|
|
431
|
+
}
|
|
432
|
+
if (isVmExtern(record)) {
|
|
433
|
+
const result: Record<string, VmConst> = create(null);
|
|
434
|
+
for (const key of record.keys()) {
|
|
435
|
+
const value = record.get(key) ?? null;
|
|
436
|
+
// 当前只有 Primitive 不会进行二次包装
|
|
437
|
+
if (isVmPrimitive(value)) {
|
|
438
|
+
result[key] = value;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return result;
|
|
442
|
+
}
|
|
443
|
+
throw new VmError(`Expected record, array, extern or nil, got ${$Type(record)}`, null);
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
export const $ArraySpread = (array: VmAny): Iterable<VmConst | undefined> => {
|
|
447
|
+
$AssertInit(array);
|
|
448
|
+
if (array == null) return [];
|
|
449
|
+
if (isVmArray(array)) return array;
|
|
450
|
+
if (isVmExtern(array) && typeof (array.value as Iterable<unknown>)[Symbol.iterator] == 'function') {
|
|
451
|
+
const result: VmConst[] = [];
|
|
452
|
+
for (const item of array.value as Iterable<unknown>) {
|
|
453
|
+
// 当前只有 Primitive 不会进行二次包装
|
|
454
|
+
if (isVmPrimitive(item)) {
|
|
455
|
+
result.push(item);
|
|
456
|
+
} else {
|
|
457
|
+
result.push(null);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
return result;
|
|
461
|
+
}
|
|
462
|
+
throw new VmError(`Expected array, iterable extern or nil, got ${$Type(array)}`, []);
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
/** 渲染数字 */
|
|
466
|
+
function formatNumber(value: number): string {
|
|
467
|
+
if (!Number.isFinite(value)) return numberToString(value);
|
|
468
|
+
if (value === 0) return '0';
|
|
469
|
+
const s = value.toString();
|
|
470
|
+
let ps;
|
|
471
|
+
const abs = Math.abs(value);
|
|
472
|
+
if (abs >= 1000 || abs < 0.001) {
|
|
473
|
+
const ps1 = value.toExponential();
|
|
474
|
+
const ps2 = value.toExponential(5);
|
|
475
|
+
ps = ps1.length < ps2.length ? ps1 : ps2;
|
|
476
|
+
} else {
|
|
477
|
+
ps = value.toPrecision(6);
|
|
478
|
+
}
|
|
479
|
+
return ps.length < s.length ? ps : s;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
export const $Format = (value: VmAny, format: VmAny): string => {
|
|
483
|
+
$AssertInit(value);
|
|
484
|
+
const f = format == null ? '' : typeof format == 'string' ? format.trim() : $ToString(format);
|
|
485
|
+
|
|
486
|
+
if (typeof value == 'number') {
|
|
487
|
+
if (/^\.\d+$/.test(f)) {
|
|
488
|
+
let digits = Math.trunc(Number(f.slice(1)));
|
|
489
|
+
if (!(digits <= 100)) digits = 100;
|
|
490
|
+
return value.toFixed(digits);
|
|
491
|
+
} else {
|
|
492
|
+
return formatNumber(value);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
return $ToString(value);
|
|
497
|
+
};
|