@mirascript/mirascript 0.1.62-alpha.9 → 0.1.64

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.
Files changed (90) hide show
  1. package/README.md +46 -0
  2. package/dist/{chunk-6CAGNGY6.js → chunk-ITTCGBBX.js} +271 -269
  3. package/dist/chunk-ITTCGBBX.js.map +6 -0
  4. package/dist/{chunk-VRGQ23AK.js → chunk-QN3IOQRQ.js} +44 -38
  5. package/dist/chunk-QN3IOQRQ.js.map +6 -0
  6. package/dist/compiler/emit/consts.d.ts.map +1 -1
  7. package/dist/compiler/emit-script.d.ts +7 -2
  8. package/dist/compiler/emit-script.d.ts.map +1 -1
  9. package/dist/compiler/index.d.ts +2 -0
  10. package/dist/compiler/index.d.ts.map +1 -1
  11. package/dist/compiler/worker.js +1 -1
  12. package/dist/helpers/constants.d.ts +2 -10
  13. package/dist/helpers/constants.d.ts.map +1 -1
  14. package/dist/helpers/serialize.d.ts.map +1 -1
  15. package/dist/helpers/types.d.ts +5 -1
  16. package/dist/helpers/types.d.ts.map +1 -1
  17. package/dist/helpers/utils.d.ts +7 -1
  18. package/dist/helpers/utils.d.ts.map +1 -1
  19. package/dist/index.js +7 -4
  20. package/dist/index.js.map +1 -1
  21. package/dist/subtle.js +3 -3
  22. package/dist/subtle.js.map +1 -1
  23. package/dist/vm/checkpoint.d.ts +3 -3
  24. package/dist/vm/checkpoint.d.ts.map +1 -1
  25. package/dist/vm/lib/global/debug.d.ts.map +1 -1
  26. package/dist/vm/lib/global/sequence/entries.d.ts.map +1 -1
  27. package/dist/vm/lib/global/sequence/with.d.ts.map +1 -1
  28. package/dist/vm/operations/array-range.d.ts +6 -0
  29. package/dist/vm/operations/array-range.d.ts.map +1 -0
  30. package/dist/vm/operations/call.d.ts.map +1 -1
  31. package/dist/vm/operations/common.d.ts +1 -1
  32. package/dist/vm/operations/common.d.ts.map +1 -1
  33. package/dist/vm/operations/compound.d.ts +10 -10
  34. package/dist/vm/operations/compound.d.ts.map +1 -1
  35. package/dist/vm/operations/convert.d.ts +4 -4
  36. package/dist/vm/operations/convert.d.ts.map +1 -1
  37. package/dist/vm/operations/helpers.d.ts +7 -13
  38. package/dist/vm/operations/helpers.d.ts.map +1 -1
  39. package/dist/vm/operations/index.d.ts +1 -0
  40. package/dist/vm/operations/index.d.ts.map +1 -1
  41. package/dist/vm/operations/operator.d.ts +22 -22
  42. package/dist/vm/operations/operator.d.ts.map +1 -1
  43. package/dist/vm/operations/slice.d.ts +2 -2
  44. package/dist/vm/operations/slice.d.ts.map +1 -1
  45. package/dist/vm/operations/type-check.d.ts +7 -7
  46. package/dist/vm/operations/type-check.d.ts.map +1 -1
  47. package/dist/vm/operations/utils.d.ts +2 -2
  48. package/dist/vm/operations/utils.d.ts.map +1 -1
  49. package/dist/vm/types/boundary.d.ts +1 -1
  50. package/dist/vm/types/boundary.d.ts.map +1 -1
  51. package/dist/vm/types/context.d.ts.map +1 -1
  52. package/dist/vm/types/function.d.ts +0 -4
  53. package/dist/vm/types/function.d.ts.map +1 -1
  54. package/dist/vm/types/index.d.ts +1 -1
  55. package/dist/vm/types/index.d.ts.map +1 -1
  56. package/package.json +21 -8
  57. package/src/compiler/diagnostic.ts +2 -2
  58. package/src/compiler/emit/consts.ts +1 -0
  59. package/src/compiler/emit/index.ts +5 -5
  60. package/src/compiler/emit-script.ts +15 -6
  61. package/src/compiler/index.ts +3 -2
  62. package/src/helpers/analyze.ts +1 -1
  63. package/src/helpers/constants.ts +3 -11
  64. package/src/helpers/serialize.ts +1 -1
  65. package/src/helpers/types.ts +16 -0
  66. package/src/helpers/utils.ts +2 -1
  67. package/src/vm/checkpoint.ts +6 -6
  68. package/src/vm/lib/global/debug.ts +0 -1
  69. package/src/vm/lib/global/sequence/entries.ts +2 -0
  70. package/src/vm/lib/global/sequence/map-filter.ts +4 -4
  71. package/src/vm/lib/global/sequence/with.ts +3 -2
  72. package/src/vm/lib/loader.ts +2 -2
  73. package/src/vm/operations/array-range.ts +37 -0
  74. package/src/vm/operations/call.ts +8 -7
  75. package/src/vm/operations/common.ts +2 -2
  76. package/src/vm/operations/compound.ts +24 -25
  77. package/src/vm/operations/convert.ts +8 -8
  78. package/src/vm/operations/helpers.ts +17 -51
  79. package/src/vm/operations/index.ts +1 -0
  80. package/src/vm/operations/operator.ts +64 -54
  81. package/src/vm/operations/slice.ts +5 -4
  82. package/src/vm/operations/type-check.ts +16 -19
  83. package/src/vm/operations/utils.ts +4 -4
  84. package/src/vm/types/boundary.ts +2 -2
  85. package/src/vm/types/context.ts +1 -2
  86. package/src/vm/types/function.ts +2 -21
  87. package/src/vm/types/index.ts +1 -0
  88. package/bench/index.ts +0 -45
  89. package/dist/chunk-6CAGNGY6.js.map +0 -6
  90. package/dist/chunk-VRGQ23AK.js.map +0 -6
package/package.json CHANGED
@@ -1,9 +1,22 @@
1
1
  {
2
2
  "name": "@mirascript/mirascript",
3
- "version": "0.1.62-alpha.9",
3
+ "version": "0.1.64",
4
4
  "author": "CloudPSS",
5
5
  "license": "MIT",
6
6
  "description": "An expression based scripting language.",
7
+ "keywords": [
8
+ "mirascript",
9
+ "language",
10
+ "compiler",
11
+ "runtime",
12
+ "vm"
13
+ ],
14
+ "homepage": "https://mira.cloudpss.net",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/CloudPSS/MiraScript.git",
18
+ "directory": "packages/mirascript"
19
+ },
7
20
  "type": "module",
8
21
  "main": "./dist/index.js",
9
22
  "types": "./dist/index.d.ts",
@@ -29,15 +42,15 @@
29
42
  "@cloudpss/worker": "^0.1.11",
30
43
  "source-map-js": "^1.2.1",
31
44
  "supports-color": "^10.2.2",
32
- "@mirascript/constants": "~0.1.62-alpha.9",
33
- "@mirascript/bindings": "~0.1.62-alpha.9"
45
+ "@mirascript/bindings": "~0.1.64",
46
+ "@mirascript/constants": "~0.1.64"
34
47
  },
35
48
  "devDependencies": {
36
- "@types/node": "^25.6.0",
37
- "ava": "^7.0.0",
49
+ "@types/node": "^26.0.1",
50
+ "ava": "^8.0.1",
38
51
  "c8": "^11.0.0",
39
- "tinybench": "^6.0.0",
40
- "type-fest": "^5.5.0"
52
+ "tinybench": "^6.0.2",
53
+ "type-fest": "^5.7.0"
41
54
  },
42
55
  "scripts": {
43
56
  "watch": "pnpm build:ts --watch",
@@ -45,7 +58,7 @@
45
58
  "build": "pnpm build:ts --emitDeclarationOnly && node ./esbuild.config.js",
46
59
  "deps": "pnpm dlx madge --circular --warning ./src",
47
60
  "clean": "rimraf dist",
48
- "test": "c8 ava",
61
+ "test": "FORCE_COLOR=1 c8 ava --no-worker-threads",
49
62
  "bench": "node ./bench/index.ts"
50
63
  }
51
64
  }
@@ -116,10 +116,10 @@ export function parseDiagnostics(
116
116
  diagnostics: Uint32Array,
117
117
  filter?: (code: DiagnosticCode) => boolean,
118
118
  ): ParsedDiagnostics {
119
- const parsed = [];
119
+ const parsed: Array<Writable<SourceDiagnostic & SourceReference>> = [];
120
120
  const bufLen = diagnostics.length;
121
121
  for (let i = 0; i < bufLen; i += 5) {
122
- const code = diagnostics[i + 4]! as DiagnosticCode;
122
+ const code: DiagnosticCode = diagnostics[i + 4]!;
123
123
  if (filter && !filter(code)) {
124
124
  continue;
125
125
  }
@@ -1,3 +1,4 @@
1
+ /* eslint-disable no-useless-assignment */
1
2
  import type { VmPrimitive } from '../../vm/index.js';
2
3
  const { stringify } = JSON;
3
4
  /** 将值转为 JS 字面量 */
@@ -193,7 +193,7 @@ export class Emitter {
193
193
  const opcode = opcode_raw & 0x7f;
194
194
  const wide = opcode_raw >= 0x80;
195
195
  const read = () => this.readParam(wide);
196
- let code = '';
196
+ let code: string;
197
197
  switch (opcode) {
198
198
  case OpCode.Field: {
199
199
  const field = read();
@@ -232,7 +232,7 @@ export class Emitter {
232
232
  const opcode = opcode_raw & 0x7f;
233
233
  const wide = opcode_raw >= 0x80;
234
234
  const read = () => this.readParam(wide);
235
- let code = '';
235
+ let code: string;
236
236
  switch (opcode) {
237
237
  case OpCode.FieldOpt:
238
238
  case OpCode.Field: {
@@ -299,7 +299,7 @@ export class Emitter {
299
299
  const opcode = opcode_raw & 0x7f;
300
300
  const wide = opcode_raw >= 0x80;
301
301
  const read = () => this.readParam(wide);
302
- let code = '';
302
+ let code: string;
303
303
  switch (opcode) {
304
304
  case OpCode.Item: {
305
305
  const value = read();
@@ -355,7 +355,7 @@ export class Emitter {
355
355
  const read = () => this.readParam(wide);
356
356
  const readIndex = () => this.readIndex(wide);
357
357
  const ident = this.ident();
358
- let code = '';
358
+ let code: string;
359
359
  let reg = 0;
360
360
  switch (opcode) {
361
361
  case OpCode.FuncVarg:
@@ -641,7 +641,7 @@ export class Emitter {
641
641
  }
642
642
  case OpCode.Module: {
643
643
  reg = read();
644
- const nameIdx = read();
644
+ const nameIdx = readIndex();
645
645
  const name = this.constLits[nameIdx];
646
646
  code = `${this.wv(reg)} = $Module(${name}, {`;
647
647
  break;
@@ -4,11 +4,20 @@ import { emit } from './emit/index.js';
4
4
  import { createScript, type VmScript } from './create-script.js';
5
5
  import { DiagnosticCode, formatDiagnostics, parseDiagnostics } from './diagnostic.js';
6
6
 
7
- /** 报告编译错误 */
8
- export function reportDiagnostic(source: ScriptInput, diagnostics: Uint32Array, fileName: string | undefined): never {
9
- const parsed = parseDiagnostics(source, diagnostics);
10
- const messages = formatDiagnostics(parsed.errors, source, fileName);
11
- throw new Error(`Failed to compile:\n${messages.join('\n')}`);
7
+ /** 编译错误 */
8
+ export class CompileError extends Error {
9
+ constructor(
10
+ source: ScriptInput,
11
+ diagnostics: Uint32Array,
12
+ readonly fileName: string | undefined,
13
+ ) {
14
+ const parsed = parseDiagnostics(source, diagnostics);
15
+ const messages = formatDiagnostics(parsed.errors, source, fileName);
16
+ super(`Failed to compile:\n${messages.join('\n')}`);
17
+ this.diagnostics = messages;
18
+ }
19
+ readonly diagnostics: readonly string[];
20
+ override readonly name = 'CompileError';
12
21
  }
13
22
 
14
23
  /**
@@ -20,7 +29,7 @@ export function emitScript(
20
29
  options: TranspileOptions,
21
30
  ): VmScript {
22
31
  if (!code) {
23
- reportDiagnostic(source, diagnostics, options.fileName);
32
+ throw new CompileError(source, diagnostics, options.fileName);
24
33
  }
25
34
  const sourcemaps = options.sourceMap
26
35
  ? parseDiagnostics(source, diagnostics, (c) => c === DiagnosticCode.SourceMap).sourcemaps
@@ -4,10 +4,11 @@ import { createScript, type VmScript } from './create-script.js';
4
4
  import { compileFast } from './compile-fast.js';
5
5
  import { generateBytecode, generateBytecodeSync } from './generate-bytecode.js';
6
6
  import { compileWorker } from './worker-manager.js';
7
- import { emitScript, reportDiagnostic } from './emit-script.js';
7
+ import { emitScript, CompileError } from './emit-script.js';
8
8
 
9
9
  export * from './types.js';
10
10
  export type { VmScript };
11
+ export { CompileError };
11
12
 
12
13
  // 目前编译速度约 2000kB/s
13
14
  const WORKER_MIN_LEN = typeof Worker != 'function' ? Number.MAX_VALUE : 1024;
@@ -39,7 +40,7 @@ export async function compile(this: void, source: ScriptInput, options?: Transpi
39
40
  }
40
41
  const [target, diagnostics] = await compileWorker(source, options);
41
42
  if (target == null) {
42
- reportDiagnostic(source, diagnostics, options.fileName);
43
+ throw new CompileError(source, diagnostics, options.fileName);
43
44
  }
44
45
  return createScript(source, options.input_mode ?? 'Script', target);
45
46
  }
@@ -1,6 +1,6 @@
1
+ import { REG_IDENTIFIER } from '@mirascript/constants';
1
2
  import { generateBytecodeSync } from '../compiler/generate-bytecode.js';
2
3
  import { DiagnosticCode, parseDiagnostics } from '../compiler/diagnostic.js';
3
- import { REG_IDENTIFIER } from './constants.js';
4
4
  import type { InputMode } from '../compiler/types.js';
5
5
 
6
6
  /** 一个访问链,第一个元素为全局变量名称 */
@@ -1,14 +1,4 @@
1
- export const REG_IDENTIFIER = /(?:_+|@+|\$+|\p{XID_Start})\p{XID_Continue}*/u;
2
- export const REG_ORDINAL =
3
- /(?:214748364[0-7]|21474836[0-3]\d|2147483[0-5]\d{2}|214748[0-2]\d{3}|21474[0-7]\d{4}|2147[0-3]\d{5}|214[0-6]\d{6}|21[0-3]\d{7}|20\d{8}|1\d{9}|[1-9]\d{0,8}|0)/;
4
- export const REG_IDENTIFIER_FULL = new RegExp(`^${REG_IDENTIFIER.source}$`, REG_IDENTIFIER.flags);
5
- export const REG_ORDINAL_FULL = new RegExp(`^${REG_ORDINAL.source}$`, REG_ORDINAL.flags);
6
- export const REG_WHITESPACE = /[ \t\v\f\r\n]/u;
7
- export const REG_HEX = /0[xX][a-fA-F0-9_]*[a-fA-F0-9]/;
8
- export const REG_OCT = /0[oO][0-7_]*[0-7]/;
9
- export const REG_BIN = /0[bB][01_]*[01]/;
10
- export const REG_NUMBER = /\d[\d_]*(?:\.[\d_]+)?(?:[eE][+-]?[\d_]*\d)?/u;
11
- export const VM_ARRAY_MAX_LENGTH = 0x800_0000; // 128 MB, v8's max array length
1
+ export const VM_ARRAY_MAX_LENGTH = 0x100_0000; // 16 M
12
2
 
13
3
  export const kVmScript = Symbol.for('mirascript.vm.script');
14
4
  export const kVmFunction = Symbol.for('mirascript.vm.function');
@@ -20,3 +10,5 @@ export const kVmWrapper = Symbol.for('mirascript.vm.wrapper');
20
10
 
21
11
  export const VM_SCRIPT_NAME = `<script_root>`;
22
12
  export const VM_FUNCTION_ANONYMOUS_NAME = `<anonymous>`;
13
+
14
+ export * from '@mirascript/constants';
@@ -1,5 +1,5 @@
1
+ import { REG_IDENTIFIER_FULL, REG_ORDINAL_FULL } from '@mirascript/constants';
1
2
  import type { VmArray, VmExtern, VmFunction, VmModule, VmAny, VmRecord } from '../vm/index.js';
2
- import { REG_IDENTIFIER_FULL, REG_ORDINAL_FULL } from './constants.js';
3
3
  import { entries, hasOwn, isFinite, isNaN } from '../helpers/utils.js';
4
4
  import {
5
5
  getVmFunctionInfo,
@@ -12,6 +12,7 @@ import type {
12
12
  VmModule,
13
13
  VmPrimitive,
14
14
  VmRecord,
15
+ VmTypeName,
15
16
  VmValue,
16
17
  } from '../vm/types/index.js';
17
18
  import type { VmWrapper } from '../vm/types/wrapper.js';
@@ -68,6 +69,7 @@ export function isVmCallable<E extends Function, F extends VmFunctionLike>(
68
69
  /** 检查值是否为 Mirascript 原始值 */
69
70
  export function isVmPrimitive(value: unknown): value is VmPrimitive {
70
71
  if (value === null || typeof value == 'number' || typeof value == 'string' || typeof value == 'boolean') {
72
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
71
73
  value as VmPrimitive satisfies typeof value;
72
74
  value satisfies VmPrimitive;
73
75
  return true;
@@ -265,3 +267,17 @@ export function isVmValue(value: unknown, checkDeep = false): value is VmValue {
265
267
  if (value === undefined) return false;
266
268
  return isVmAny(value, checkDeep);
267
269
  }
270
+
271
+ /** 获取 MiraScript 类型 */
272
+ export function getVmType(value: VmValue): VmTypeName;
273
+ /** 获取 MiraScript 类型 */
274
+ export function getVmType(value: VmAny): VmTypeName | 'uninitialized';
275
+ /** 获取 MiraScript 类型 */
276
+ export function getVmType(value: VmAny): VmTypeName | 'uninitialized' {
277
+ if (value === undefined) return 'uninitialized';
278
+ if (value === null) return 'nil';
279
+ if (isVmWrapper(value)) return value.type;
280
+ if (isVmArray(value)) return 'array';
281
+ if (typeof value == 'object') return 'record';
282
+ return typeof value as VmTypeName;
283
+ }
@@ -8,7 +8,8 @@ export const {
8
8
  POSITIVE_INFINITY: PositiveInfinity,
9
9
  NEGATIVE_INFINITY: NegativeInfinity,
10
10
  } = Number;
11
- export const { hasOwn, keys, values, entries, create, fromEntries, defineProperty, getOwnPropertyNames } = Object;
11
+ export const { hasOwn, keys, values, entries, create, fromEntries, defineProperty, getOwnPropertyNames, freeze } =
12
+ Object;
12
13
  export const { apply, getPrototypeOf } = Reflect;
13
14
 
14
15
  /**
@@ -18,7 +18,7 @@ let cpDepth = 0;
18
18
  let cp = CP_UNSET | 0;
19
19
  let cpCounter = 0;
20
20
  /** 检查点 */
21
- export function Cp(): void {
21
+ export const Cp = (): void => {
22
22
  // 不是每次都查时间
23
23
  if ((cpCounter = (cpCounter + 1) | 0) % (cpInterval | 0) !== 0) {
24
24
  return;
@@ -29,9 +29,9 @@ export function Cp(): void {
29
29
  } else if ((timestamp() | 0) - (cp | 0) >= (cpTimeout | 0)) {
30
30
  throw new RangeError('Execution timed out');
31
31
  }
32
- }
32
+ };
33
33
  /** 检查点 */
34
- export function CpEnter(): void {
34
+ export const CpEnter = (): void => {
35
35
  cpDepth = (cpDepth | 0) + 1;
36
36
  if ((cpDepth | 0) <= 1) {
37
37
  cp = CP_UNSET | 0;
@@ -42,9 +42,9 @@ export function CpEnter(): void {
42
42
  } else {
43
43
  Cp();
44
44
  }
45
- }
45
+ };
46
46
  /** 检查点 */
47
- export function CpExit(): void {
47
+ export const CpExit = (): void => {
48
48
  cpDepth = (cpDepth | 0) - 1;
49
49
  if ((cpDepth | 0) < 1) {
50
50
  cp = CP_UNSET | 0;
@@ -53,7 +53,7 @@ export function CpExit(): void {
53
53
  } else {
54
54
  Cp();
55
55
  }
56
- }
56
+ };
57
57
 
58
58
  const INT_MAX = 0x7fff_ffff;
59
59
  /** 设置检查点超时时间 */
@@ -112,7 +112,6 @@ const printParser: PrintOptions['parser'] = function (args) {
112
112
  formats.push('');
113
113
  templates.push(i < values.length - 1 ? ' ' : '');
114
114
  }
115
- valIndex++;
116
115
  }
117
116
  return {
118
117
  templates,
@@ -31,6 +31,8 @@ export const values = VmLib(
31
31
  (data) => {
32
32
  expectArrayOrRecord('data', data, []);
33
33
  if (isVmArray(data)) {
34
+ // holes & undefined values will be kept as-is,
35
+ // and handled by further processing if needed
34
36
  return data;
35
37
  }
36
38
  return _values(data);
@@ -36,7 +36,7 @@ export const map = VmLib(
36
36
  f: 'fn(value: any, key: number | string, input: type(data)) -> any',
37
37
  },
38
38
  returnsType: 'type(data)',
39
- examples: ['map([1, 2, 3], fn (v) { v * v }) // [1, 4, 9]'],
39
+ examples: ['map([1, 2, 3], fn { it * it }) // [1, 4, 9]'],
40
40
  },
41
41
  );
42
42
 
@@ -57,7 +57,7 @@ export const filter = VmLib(
57
57
  predicate: 'fn(value: any, key: number | string, input: type(data)) -> boolean',
58
58
  },
59
59
  returnsType: 'type(data)',
60
- examples: ['filter([1, 2, 3, 4], fn (v) { v % 2 == 0 }) // [2, 4]'],
60
+ examples: ['filter([1, 2, 3, 4], fn { it % 2 == 0 }) // [2, 4]'],
61
61
  },
62
62
  );
63
63
 
@@ -68,7 +68,7 @@ export const filter_map = VmLib(
68
68
  return ret ?? undefined;
69
69
  }),
70
70
  {
71
- summary: '对数组或记录中的每个元素应用函数,并返回非 nil 的结果',
71
+ summary: '对数组或记录中的每个元素应用函数,并返回非 `nil` 的结果',
72
72
  params: {
73
73
  data: '要映射的数组或记录',
74
74
  f: '应用于每个元素的函数',
@@ -78,6 +78,6 @@ export const filter_map = VmLib(
78
78
  f: 'fn(value: any, key: number | string, input: type(data)) -> any | nil',
79
79
  },
80
80
  returnsType: 'type(data)',
81
- examples: ['filter_map([1, 2, 3], fn (v) { if v % 2 == 0 { v * v } else { nil } }) // [4]'],
81
+ examples: ['filter_map([1, 2, 3], fn {\n if it % 2 == 0 { it * it } else { nil } \n}) // [4]'],
82
82
  },
83
83
  );
@@ -39,7 +39,7 @@ const withInner = (obj: VmConst | undefined, key: VmArray, keyIndex: number, val
39
39
  }
40
40
  if (isArray(result)) {
41
41
  const index = arrIndex(k);
42
- while (index > result.length) {
42
+ while (index >= result.length) {
43
43
  result.push(null);
44
44
  }
45
45
  result[index] = withInner(result[index], key, keyIndex + 1, value);
@@ -64,7 +64,8 @@ const normalizeEntries = (data: VmConst, entries: Array<VmValue | undefined>): M
64
64
  if (isVmArray(key)) {
65
65
  if (key.length === 0 || key.includes(null) || key.includes(undefined)) {
66
66
  continue;
67
- } else if (key.length === 1) {
67
+ }
68
+ if (key.length === 1) {
68
69
  key = key[0]!;
69
70
  }
70
71
  }
@@ -59,8 +59,8 @@ export type ToWrappedModule<T extends Record<string, RawValue>> = VmBuiltinModul
59
59
 
60
60
  /** 创建模块 */
61
61
  export function createModule<const T extends Record<string, RawValue>>(name: string, lib: T): ToWrappedModule<T> {
62
- const mod = create(null) as Record<string, VmImmutable>;
63
- const descriptions = create(null) as Record<string, string | undefined>;
62
+ const mod: Record<string, VmImmutable> = create(null);
63
+ const descriptions: Record<string, string | undefined> = create(null);
64
64
  for (const [key, value] of entries(lib)) {
65
65
  const [wrappedValue, description] = wrapEntry(key, value, name);
66
66
  mod[key] = wrappedValue;
@@ -0,0 +1,37 @@
1
+ import { isFinite } from '../../helpers/utils.js';
2
+ import { VM_ARRAY_MAX_LENGTH } from '../../helpers/constants.js';
3
+ import type { VmAny, VmArray } from '../types/index.js';
4
+ import { $ToNumber } from './convert.js';
5
+
6
+ const assertArrayLength = (len: number) => {
7
+ if (len > VM_ARRAY_MAX_LENGTH) {
8
+ throw new RangeError(`Array length exceeds maximum limit of ${VM_ARRAY_MAX_LENGTH}`);
9
+ }
10
+ };
11
+ const isEmptyRange = (start: number, end: number) => {
12
+ return !isFinite(start) || !isFinite(end) || start > end;
13
+ };
14
+ /** 构造范围数组 */
15
+ export const $ArrayRange = (start: VmAny, end: VmAny): VmArray => {
16
+ const s = $ToNumber(start);
17
+ const e = $ToNumber(end);
18
+ if (isEmptyRange(s, e)) return [];
19
+ assertArrayLength(e - s + 1);
20
+ const arr = [];
21
+ for (let i = s; i <= e; i++) {
22
+ arr.push(i);
23
+ }
24
+ return arr;
25
+ };
26
+ /** 构造范围数组(不包含结束值) */
27
+ export const $ArrayRangeExclusive = (start: VmAny, end: VmAny): VmArray => {
28
+ const s = $ToNumber(start);
29
+ const e = $ToNumber(end);
30
+ if (isEmptyRange(s, e)) return [];
31
+ assertArrayLength(e - s);
32
+ const arr = [];
33
+ for (let i = s; i < e; i++) {
34
+ arr.push(i);
35
+ }
36
+ return arr;
37
+ };
@@ -1,6 +1,6 @@
1
1
  import { VmError } from '../../helpers/error.js';
2
2
  import { display } from '../../helpers/serialize.js';
3
- import { isVmFunction, isVmExtern, isVmConst } from '../../helpers/types.js';
3
+ import { isVmExtern, isVmConst } from '../../helpers/types.js';
4
4
  import type { VmExtern, VmFunction, VmAny, VmArray, VmValue } from '../types/index.js';
5
5
  import { $AssertInit } from './common.js';
6
6
 
@@ -28,14 +28,15 @@ type CallReturn<T extends VmValue> =
28
28
 
29
29
  /** 调用函数 */
30
30
  export function $Call<T extends VmValue, A extends readonly VmValue[]>(func: T, args: A): CallReturn<T> {
31
- for (const a of args) {
32
- $AssertInit(a);
31
+ const argsLen = args.length;
32
+ for (let i = 0; i < argsLen; i++) {
33
+ $AssertInit(args[i]);
33
34
  }
34
- if (isVmExtern(func)) {
35
- return (func.call(args as readonly VmValue[]) ?? null) as CallReturn<T>;
35
+ if (typeof func == 'function') {
36
+ return (func(...args) ?? null) as CallReturn<T>;
36
37
  }
37
- if (isVmFunction(func)) {
38
- return (func(...(args as readonly VmValue[])) ?? null) as CallReturn<T>;
38
+ if (isVmExtern(func)) {
39
+ return (func.call(args) ?? null) as CallReturn<T>;
39
40
  }
40
41
  throw new VmError(`Value is not callable: ${display(func)}`, null);
41
42
  }
@@ -2,6 +2,6 @@ import { VmError } from '../../helpers/error.js';
2
2
  import type { VmValue } from '../types/index.js';
3
3
 
4
4
  /** 断言值已初始化 */
5
- export function $AssertInit<T extends VmValue>(value: T | undefined): asserts value is T {
5
+ export const $AssertInit: <T extends VmValue>(value: T | undefined) => asserts value is T = (value) => {
6
6
  if (value === undefined) throw new VmError(`Uninitialized value`, null);
7
- }
7
+ };
@@ -1,5 +1,5 @@
1
1
  import { VmError } from '../../helpers/error.js';
2
- import { hasOwnEnumerable, isNaN, NotNumber, keys, create, isFinite } from '../../helpers/utils.js';
2
+ import { hasOwnEnumerable, 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 {
@@ -22,7 +22,7 @@ const { trunc } = Math;
22
22
  const { at } = Array.prototype;
23
23
 
24
24
  /** 检查值是否在可迭代对象中 */
25
- export function $In(value: VmAny, iterable: VmAny): boolean {
25
+ export const $In = (value: VmAny, iterable: VmAny): boolean => {
26
26
  $AssertInit(value);
27
27
  $AssertInit(iterable);
28
28
  if (iterable == null) return false;
@@ -42,22 +42,22 @@ export function $In(value: VmAny, iterable: VmAny): boolean {
42
42
  const key = toString(value, undefined);
43
43
  if (isVmWrapper(iterable)) return iterable.has(key);
44
44
  return hasOwnEnumerable(iterable satisfies VmRecord, key);
45
- }
45
+ };
46
46
 
47
47
  /** 获取值的长度 */
48
- export function $Length(value: VmAny): number {
48
+ export const $Length = (value: VmAny): number => {
49
49
  $AssertInit(value);
50
50
  if (isVmArray(value)) return value.length;
51
51
  if (isVmRecord(value)) return keys(value).length;
52
52
  if (isVmWrapper(value)) return value.keys().length;
53
53
 
54
54
  throw new VmError(`Value has no length: ${display(value)}`, 0);
55
- }
55
+ };
56
56
 
57
57
  /** 删除记录中的指定字段 */
58
- export function $Omit(value: VmAny, omitted: ReadonlyArray<number | string>): VmRecord {
58
+ export const $Omit = (value: VmAny, omitted: ReadonlyArray<number | string>): VmRecord => {
59
59
  $AssertInit(value);
60
- if (value == null || !isVmRecord(value)) return {};
60
+ if (!isVmRecord(value)) return {};
61
61
  const result: Record<string, VmConst> = {};
62
62
  const valueKeys = keys(value);
63
63
  const omittedSet = new Set(omitted.map($ToString));
@@ -68,12 +68,12 @@ export function $Omit(value: VmAny, omitted: ReadonlyArray<number | string>): Vm
68
68
  }
69
69
  }
70
70
  return result;
71
- }
71
+ };
72
72
 
73
73
  /** 选择记录中的指定字段 */
74
- export function $Pick(value: VmAny, picked: ReadonlyArray<number | string>): VmRecord {
74
+ export const $Pick = (value: VmAny, picked: ReadonlyArray<number | string>): VmRecord => {
75
75
  $AssertInit(value);
76
- if (value == null || !isVmRecord(value)) return {};
76
+ if (!isVmRecord(value)) return {};
77
77
  const result: Record<string, VmConst> = {};
78
78
  for (const key of picked) {
79
79
  const k = $ToString(key);
@@ -82,23 +82,23 @@ export function $Pick(value: VmAny, picked: ReadonlyArray<number | string>): VmR
82
82
  }
83
83
  }
84
84
  return result;
85
- }
85
+ };
86
86
  /** 检查是否拥有字段 */
87
- export function $Has(obj: VmAny, key: VmAny): boolean {
87
+ export const $Has = (obj: VmAny, key: VmAny): boolean => {
88
88
  $AssertInit(obj);
89
89
  const pk = $ToString(key);
90
90
  if (obj == null || typeof obj != 'object') return false;
91
91
  if (isVmWrapper(obj)) return obj.has(pk);
92
92
  return hasOwnEnumerable(obj, pk);
93
- }
93
+ };
94
94
 
95
95
  /** 获取字段 */
96
- export function $Get(obj: VmAny, key: VmAny): VmValue {
96
+ export const $Get = (obj: VmAny, key: VmAny): VmValue => {
97
97
  $AssertInit(obj);
98
98
  if (isVmArray(obj)) {
99
99
  $AssertInit(key);
100
100
  const index = toNumber(key, NotNumber);
101
- if (isNaN(index)) return null;
101
+ if (!isFinite(index)) return null;
102
102
  return $El((at.call(obj, trunc(index)) ?? null) as VmAny);
103
103
  }
104
104
  const pk = $ToString(key);
@@ -116,26 +116,25 @@ export function $Get(obj: VmAny, key: VmAny): VmValue {
116
116
  }
117
117
  if (!hasOwnEnumerable(obj, pk)) return null;
118
118
  return $El(obj[pk] ?? null);
119
- }
119
+ };
120
120
  /** 设置字段 */
121
- export function $Set(obj: VmAny, key: VmAny, value: VmAny): void {
121
+ export const $Set = (obj: VmAny, key: VmAny, value: VmAny): void => {
122
122
  $AssertInit(obj);
123
123
  $AssertInit(value);
124
124
  const pk = $ToString(key);
125
- if (obj == null) return;
126
125
  if (!isVmExtern(obj)) throw new VmError(`Expected extern, got ${display(obj)}`, undefined);
127
126
  obj.set(pk, value);
128
- }
127
+ };
129
128
  /** 获取可迭代对象 */
130
- export function $Iterable(value: VmAny): Iterable<VmValue | undefined> {
129
+ export const $Iterable = (value: VmAny): Iterable<VmValue | undefined> => {
131
130
  $AssertInit(value);
132
131
  if (isVmWrapper(value)) return value.keys();
133
132
  if (isVmArray(value)) return value;
134
133
  if (value != null && typeof value == 'object') return keys(value);
135
134
  throw new VmError(`Value is not iterable: ${display(value)}`, isVmFunction(value) ? [] : [value]);
136
- }
135
+ };
137
136
  /** 展开记录 */
138
- export function $RecordSpread(record: VmAny): VmRecord | null {
137
+ export const $RecordSpread = (record: VmAny): VmRecord | null => {
139
138
  $AssertInit(record);
140
139
  if (record == null || isVmRecord(record)) return record;
141
140
  if (isVmArray(record)) {
@@ -158,10 +157,10 @@ export function $RecordSpread(record: VmAny): VmRecord | null {
158
157
  return result;
159
158
  }
160
159
  throw new VmError(`Expected record, array, extern or nil, got ${display(record)}`, null);
161
- }
160
+ };
162
161
 
163
162
  /** 展开数组 */
164
- export function $ArraySpread(array: VmAny): Iterable<VmConst | undefined> {
163
+ export const $ArraySpread = (array: VmAny): Iterable<VmConst | undefined> => {
165
164
  $AssertInit(array);
166
165
  if (array == null) return [];
167
166
  if (isVmArray(array)) return array;
@@ -182,4 +181,4 @@ export function $ArraySpread(array: VmAny): Iterable<VmConst | undefined> {
182
181
  }
183
182
  }
184
183
  throw new VmError(`Expected array, iterable extern or nil, got ${display(array)}`, []);
185
- }
184
+ };
@@ -3,25 +3,25 @@ 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
+ export const $ToBoolean = (value: VmAny): boolean => {
7
7
  if (typeof value == 'boolean') return value;
8
8
  $AssertInit(value);
9
9
  return toBoolean(value, undefined);
10
- }
10
+ };
11
11
  /** 转换为字符串 */
12
- export function $ToString(value: VmAny): string {
12
+ export const $ToString = (value: VmAny): string => {
13
13
  if (typeof value == 'string') return value;
14
14
  $AssertInit(value);
15
15
  return toString(value, undefined);
16
- }
16
+ };
17
17
  /** 转换为数字 */
18
- export function $ToNumber(value: VmAny): number {
18
+ export const $ToNumber = (value: VmAny): number => {
19
19
  if (typeof value == 'number') return value;
20
20
  $AssertInit(value);
21
21
  return toNumber(value, undefined);
22
- }
22
+ };
23
23
  /** 格式化值 */
24
- export function $Format(value: VmAny, format: string | null): string {
24
+ export const $Format = (value: VmAny, format: string | null): string => {
25
25
  $AssertInit(value);
26
26
  return toFormat(value, format);
27
- }
27
+ };