@mirascript/mirascript 0.1.2 → 0.1.3

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 (37) hide show
  1. package/dist/{chunk-AOINGBRS.js → chunk-MVHCSH3E.js} +154 -65
  2. package/dist/chunk-MVHCSH3E.js.map +6 -0
  3. package/dist/cli/index.js +3 -3
  4. package/dist/compiler/diagnostic.d.ts +12 -3
  5. package/dist/compiler/diagnostic.d.ts.map +1 -1
  6. package/dist/index.js +3 -1
  7. package/dist/subtle.js +1 -1
  8. package/dist/vm/lib/_helpers.d.ts.map +1 -1
  9. package/dist/vm/lib/global/sequence/with.d.ts +2 -2
  10. package/dist/vm/lib/global/sequence/with.d.ts.map +1 -1
  11. package/dist/vm/operations.d.ts.map +1 -1
  12. package/dist/vm/types/boundary.d.ts.map +1 -1
  13. package/dist/vm/types/checker.d.ts +1 -1
  14. package/dist/vm/types/checker.d.ts.map +1 -1
  15. package/dist/vm/types/context.d.ts +14 -6
  16. package/dist/vm/types/context.d.ts.map +1 -1
  17. package/dist/vm/types/extern.d.ts +2 -0
  18. package/dist/vm/types/extern.d.ts.map +1 -1
  19. package/dist/vm/types/index.d.ts +6 -10
  20. package/dist/vm/types/index.d.ts.map +1 -1
  21. package/dist/vm/types/module.d.ts +2 -0
  22. package/dist/vm/types/module.d.ts.map +1 -1
  23. package/dist/vm/types/wrapper.d.ts +2 -0
  24. package/dist/vm/types/wrapper.d.ts.map +1 -1
  25. package/package.json +4 -4
  26. package/src/compiler/diagnostic.ts +14 -12
  27. package/src/vm/lib/_helpers.ts +2 -10
  28. package/src/vm/lib/global/sequence/with.ts +111 -17
  29. package/src/vm/operations.ts +23 -24
  30. package/src/vm/types/boundary.ts +4 -4
  31. package/src/vm/types/checker.ts +8 -15
  32. package/src/vm/types/context.ts +28 -16
  33. package/src/vm/types/extern.ts +11 -2
  34. package/src/vm/types/index.ts +7 -26
  35. package/src/vm/types/module.ts +7 -0
  36. package/src/vm/types/wrapper.ts +7 -0
  37. package/dist/chunk-AOINGBRS.js.map +0 -6
@@ -167,11 +167,7 @@ export function map(
167
167
  Cp();
168
168
  const ret = mapper(data[i] ?? null, i, data);
169
169
  if (ret === undefined) continue;
170
- if (isVmConst(ret)) {
171
- result.push(ret);
172
- } else {
173
- result.push(null);
174
- }
170
+ result.push(ret);
175
171
  }
176
172
  return result;
177
173
  } else {
@@ -180,11 +176,7 @@ export function map(
180
176
  Cp();
181
177
  const ret = mapper(value ?? null, key, data);
182
178
  if (ret === undefined) continue;
183
- if (isVmConst(ret)) {
184
- e.push([key, ret]);
185
- } else {
186
- e.push([key, null]);
187
- }
179
+ e.push([key, ret]);
188
180
  }
189
181
  return fromEntries(e);
190
182
  }
@@ -1,43 +1,137 @@
1
+ import { isArray, isSafeInteger } from '../../../../helpers/utils.js';
1
2
  import { Element } from '../../../helpers.js';
2
3
  import { $ToNumber, $ToString } from '../../../operations.js';
3
- import { isVmArray, type VmConst } from '../../../types/index.js';
4
- import { VmLib, expectArrayOrRecord, throwError } from '../../_helpers.js';
5
- import { isSafeInteger } from '../../../../helpers/utils.js';
4
+ import {
5
+ isVmArray,
6
+ isVmRecord,
7
+ VM_ARRAY_MAX_LENGTH,
8
+ type VmArray,
9
+ type VmConst,
10
+ type VmValue,
11
+ } from '../../../types/index.js';
12
+ import { VmLib, expectArrayOrRecord, expectConst, throwError } from '../../_helpers.js';
13
+
14
+ const arrIndex = (index: NonNullable<VmConst>): number => {
15
+ const idx = Math.trunc($ToNumber(index));
16
+ if (!isSafeInteger(idx) || idx < 0 || idx >= VM_ARRAY_MAX_LENGTH) {
17
+ return -1;
18
+ }
19
+ return idx;
20
+ };
21
+
22
+ const withInner = (obj: VmConst | undefined, key: VmArray, keyIndex: number, value: VmConst): VmConst => {
23
+ if (keyIndex >= key.length) {
24
+ return value;
25
+ }
26
+ const k = key[keyIndex]!;
27
+ let result: Array<VmConst | undefined> | Record<string, VmConst | undefined>;
28
+ if (isVmArray(obj)) {
29
+ result = [...obj];
30
+ } else if (isVmRecord(obj)) {
31
+ result = { ...obj };
32
+ } else if (arrIndex(k) === k) {
33
+ result = [];
34
+ } else {
35
+ result = {};
36
+ }
37
+ if (isArray(result)) {
38
+ const index = arrIndex(k);
39
+ while (index > result.length) {
40
+ result.push(null);
41
+ }
42
+ result[index] = withInner(result[index], key, keyIndex + 1, value);
43
+ } else {
44
+ const prop = $ToString(k);
45
+ result[prop] = withInner(result[prop], key, keyIndex + 1, value);
46
+ }
47
+ return result;
48
+ };
49
+
50
+ const normalizeEntries = (data: VmConst, entries: Array<VmValue | undefined>): Map<NonNullable<VmConst>, VmConst> => {
51
+ if (entries.length % 2 !== 0) {
52
+ throwError('Expected even number of entries', data);
53
+ }
54
+ const entryData = new Map<NonNullable<VmConst>, VmConst>();
55
+ for (let i = 0; i < entries.length; i += 2) {
56
+ let key = entries[i]!;
57
+ expectConst('key', key, data);
58
+ if (key == null) {
59
+ continue;
60
+ }
61
+ if (isVmArray(key)) {
62
+ if (key.length === 0 || key.includes(null) || key.includes(undefined)) {
63
+ continue;
64
+ } else if (key.length === 1) {
65
+ key = key[0]!;
66
+ }
67
+ }
68
+ const value = entries[i + 1]!;
69
+ entryData.set(key, Element(value));
70
+ }
71
+ return entryData;
72
+ };
6
73
 
7
74
  const _with = VmLib(
8
75
  (data, ...entries) => {
9
76
  expectArrayOrRecord('data', data, data);
10
- if (entries.length % 2 !== 0) {
11
- throwError('Expected even number of entries', data);
77
+ if (entries.length === 0) {
78
+ return data;
12
79
  }
80
+
81
+ const entryData = normalizeEntries(data, entries);
13
82
  if (isVmArray(data)) {
14
83
  const result: Array<VmConst | undefined> = [...data];
15
- for (let i = 0; i < entries.length; i += 2) {
16
- const index = Math.trunc($ToNumber(entries[i]));
17
- if (!isSafeInteger(index) || index < 0) continue;
18
- const value = entries[i + 1];
84
+ for (const [key, value] of entryData) {
85
+ let index: number;
86
+ let val: VmConst;
87
+ if (isVmArray(key)) {
88
+ index = arrIndex(key[0]!);
89
+ if (index < 0) continue;
90
+ val = withInner(result[index], key, 1, value);
91
+ } else {
92
+ index = arrIndex(key);
93
+ if (index < 0) continue;
94
+ val = value;
95
+ }
19
96
  while (index > result.length) {
20
97
  result.push(null);
21
98
  }
22
- result[index] = Element(value);
99
+ result[index] = val;
23
100
  }
24
101
  return result;
25
102
  } else {
26
103
  const result: Record<string, VmConst | undefined> = { ...data };
27
- for (let i = 0; i < entries.length; i += 2) {
28
- const key = $ToString(entries[i]);
29
- const value = entries[i + 1];
30
- result[key] = Element(value);
104
+ for (const [key, value] of entryData) {
105
+ let prop: string;
106
+ let val: VmConst;
107
+ if (isVmArray(key)) {
108
+ const firstKey = key[0]!;
109
+ prop = $ToString(firstKey);
110
+ val = withInner(result[prop], key, 1, value);
111
+ } else {
112
+ prop = $ToString(key);
113
+ val = value;
114
+ }
115
+ result[prop] = val;
31
116
  }
32
117
  return result;
33
118
  }
34
119
  },
35
120
  {
36
121
  summary: '在数组或记录中设置多个键值对',
37
- params: { data: '要设置的数组或记录', '..entries': '要设置的键值对,成对出现' },
38
- paramsType: { data: 'array | record', '..entries': '[..[string | number, any][]]' },
122
+ params: {
123
+ data: '要设置的数组或记录',
124
+ '..entries': '要设置的键值对,成对出现',
125
+ },
126
+ paramsType: {
127
+ data: 'array | record',
128
+ '..entries': `[..[string | number | (string | number)[], any][]]`,
129
+ },
39
130
  returnsType: 'type(data)',
40
- examples: ['with([10, 20], 2, 99) // [10, 20, 99]', 'with((a: 1), "b", 2) // (a: 1, b: 2)'],
131
+ examples: [
132
+ `with([10, 20], 2, 99, 3 ,100) // [10, 20, 99, 100]`,
133
+ `(a: 1)::with(["b", 1], 2) // (a: 1, b: [nil, 2])`,
134
+ ],
41
135
  },
42
136
  );
43
137
  export { _with as 'with' };
@@ -1,24 +1,23 @@
1
1
  import { VmError } from './error.js';
2
2
  import {
3
+ isVmPrimitive,
3
4
  isVmArray,
4
- VmModule,
5
- VmExtern,
6
5
  isVmRecord,
6
+ isVmFunction,
7
+ isVmExtern,
8
+ isVmModule,
9
+ isVmWrapper,
7
10
  getVmFunctionInfo,
8
- isVmPrimitive,
9
11
  type TypeName,
10
12
  type VmAny,
11
13
  type VmImmutable,
12
14
  type VmRecord,
13
15
  type VmValue,
14
- isVmFunction,
15
16
  type VmArray,
16
17
  type VmConst,
17
- isVmExtern,
18
18
  type VmPrimitive,
19
19
  type VmFunction,
20
20
  } from './types/index.js';
21
- import { VmWrapper } from './types/wrapper.js';
22
21
  import { hasOwnEnumerable, isNaN, isSafeInteger, keys, create } from '../helpers/utils.js';
23
22
 
24
23
  const { abs, min, trunc, ceil } = Math;
@@ -33,8 +32,8 @@ const isSame = (a: VmValue, b: VmValue): boolean => {
33
32
  // Any primitives and functions arrive here are not equal
34
33
  if (a == null || typeof a != 'object' || b == null || typeof b != 'object') return false;
35
34
  // Handle wrapper values
36
- if (a instanceof VmWrapper) return a.same(b);
37
- if (b instanceof VmWrapper) return b.same(a);
35
+ if (isVmWrapper(a)) return a.same(b);
36
+ if (isVmWrapper(b)) return b.same(a);
38
37
  // Handle array values
39
38
  if (isVmArray(a) && isVmArray(b)) {
40
39
  const len = a.length;
@@ -141,11 +140,6 @@ export const $Eq = (a: VmAny, b: VmAny): boolean => {
141
140
  export const $Neq = (a: VmAny, b: VmAny): boolean => {
142
141
  return !$Eq(a, b);
143
142
  };
144
- const stringComparer = new Intl.Collator('en', {
145
- usage: 'sort',
146
- numeric: false,
147
- sensitivity: 'base',
148
- });
149
143
  export const $Aeq = (a: VmAny, b: VmAny): boolean => {
150
144
  if (overloadNumberString(a, b)) {
151
145
  const an = $ToNumber(a);
@@ -159,11 +153,16 @@ export const $Aeq = (a: VmAny, b: VmAny): boolean => {
159
153
  const base = min(abs(an), abs(bn));
160
154
  return absoluteDifference < base * EPS;
161
155
  } else {
162
- // For strings, we use localeCompare for case-insensitive accent-insensitive comparison
156
+ // For strings, we use normalized case-insensitive comparison
163
157
  const as = $ToString(a);
164
158
  const bs = $ToString(b);
165
159
  if (as === bs) return true;
166
- return stringComparer.compare(as, bs) === 0;
160
+ const ai = as.toLowerCase();
161
+ const bi = bs.toLowerCase();
162
+ if (ai === bi) return true;
163
+ const an = ai.normalize('NFC');
164
+ const bn = bi.normalize('NFC');
165
+ return an === bn;
167
166
  }
168
167
  };
169
168
  export const $Naeq = (a: VmAny, b: VmAny): boolean => {
@@ -196,7 +195,7 @@ export const $In = (value: VmAny, iterable: VmAny): boolean => {
196
195
  return iterable.some((item = null) => isSame(item, value));
197
196
  }
198
197
  // iterable is a record or an extern here, value should be a string
199
- if (iterable instanceof VmWrapper) return iterable.has($ToString(value));
198
+ if (isVmWrapper(iterable)) return iterable.has($ToString(value));
200
199
  if (typeof iterable == 'object') return hasOwnEnumerable(iterable, $ToString(value));
201
200
  iterable satisfies VmPrimitive | VmFunction;
202
201
  return false;
@@ -217,7 +216,7 @@ export const $Length = (value: VmAny): number => {
217
216
  $AssertInit(value);
218
217
  if (isVmArray(value)) return value.length;
219
218
  if (isVmRecord(value)) return keys(value).length;
220
- if (value instanceof VmWrapper) {
219
+ if (isVmWrapper(value)) {
221
220
  return value.keys().length;
222
221
  }
223
222
  return Number.NaN;
@@ -285,7 +284,7 @@ export const $Call = (func: VmValue, args: readonly VmAny[]): VmValue => {
285
284
  for (const a of args) {
286
285
  $AssertInit(a);
287
286
  }
288
- if (func instanceof VmExtern) {
287
+ if (isVmExtern(func)) {
289
288
  return func.call(args as readonly VmValue[]) ?? null;
290
289
  }
291
290
  if (isVmFunction(func)) {
@@ -295,8 +294,8 @@ export const $Call = (func: VmValue, args: readonly VmAny[]): VmValue => {
295
294
  };
296
295
  export const $Type = (value: VmAny): TypeName => {
297
296
  if (value === undefined || value === null) return 'nil';
298
- if (value instanceof VmExtern) return 'extern';
299
- if (value instanceof VmModule) return 'module';
297
+ if (isVmExtern(value)) return 'extern';
298
+ if (isVmModule(value)) return 'module';
300
299
  if (isVmArray(value)) return 'array';
301
300
  if (typeof value == 'object') return 'record';
302
301
  return typeof value as TypeName;
@@ -316,7 +315,7 @@ function numberToString(value: number): string {
316
315
  /** 将值转为字符串 */
317
316
  function innerToString(value: VmAny, useBraces: boolean): string {
318
317
  if (value == null) return 'nil';
319
- if (value instanceof VmWrapper) return value.toString();
318
+ if (isVmWrapper(value)) return value.toString();
320
319
  if (typeof value == 'function') {
321
320
  const name = getVmFunctionInfo(value)?.fullName;
322
321
  return name ? `<function ${name}>` : `<function>`;
@@ -397,7 +396,7 @@ export const $Has = (obj: VmAny, key: VmAny): boolean => {
397
396
  $AssertInit(obj);
398
397
  const pk = $ToString(key);
399
398
  if (obj == null || typeof obj != 'object') return false;
400
- if (obj instanceof VmWrapper) return obj.has(pk);
399
+ if (isVmWrapper(obj)) return obj.has(pk);
401
400
  return hasOwnEnumerable(obj, pk);
402
401
  };
403
402
  export const $Get = (obj: VmAny, key: VmAny): VmValue => {
@@ -409,7 +408,7 @@ export const $Get = (obj: VmAny, key: VmAny): VmValue => {
409
408
  }
410
409
  const pk = $ToString(key);
411
410
  if (obj == null || typeof obj != 'object') return null;
412
- if (obj instanceof VmWrapper) return obj.get(pk) ?? null;
411
+ if (isVmWrapper(obj)) return obj.get(pk) ?? null;
413
412
  if (!hasOwnEnumerable(obj, pk)) return null;
414
413
  return (obj as Record<string, VmImmutable>)[pk] ?? null;
415
414
  };
@@ -423,7 +422,7 @@ export const $Set = (obj: VmAny, key: VmAny, value: VmAny): void => {
423
422
  };
424
423
  export const $Iterable = (value: VmAny): Iterable<VmValue | undefined> => {
425
424
  $AssertInit(value);
426
- if (value instanceof VmWrapper) return value.keys();
425
+ if (isVmWrapper(value)) return value.keys();
427
426
  if (isVmArray(value)) return value;
428
427
  if (value != null && typeof value == 'object') return keys(value);
429
428
  throw new VmError(`Value is not iterable`, isVmFunction(value) ? [] : [value]);
@@ -1,6 +1,6 @@
1
1
  import { isVmFunction, type VmFunctionLike, type VmFunction } from './function.js';
2
- import { VmExtern } from './extern.js';
3
- import { VmWrapper } from './wrapper.js';
2
+ import { isVmExtern, VmExtern } from './extern.js';
3
+ import { isVmWrapper } from './wrapper.js';
4
4
  import type { VmAny, VmConst, VmModule, VmPrimitive, VmValue } from './index.js';
5
5
  import { $Call } from '../operations.js';
6
6
  import { defineProperty, apply } from '../../helpers/utils.js';
@@ -55,7 +55,7 @@ export function wrapToVmValue(
55
55
  return new VmExtern(value as () => never, thisArg);
56
56
  }
57
57
  case 'object': {
58
- if (value instanceof VmWrapper) return value as VmModule | VmExtern;
58
+ if (isVmWrapper(value)) return value as VmModule | VmExtern;
59
59
  if (value instanceof Date) return value.valueOf();
60
60
  if (assumeVmValue?.(value)) return value;
61
61
  // Only functions preserve thisArg
@@ -80,7 +80,7 @@ export function unwrapFromVmValue(value: VmAny): unknown {
80
80
  return toVmFunctionProxy(value);
81
81
  }
82
82
  if (value == null || typeof value != 'object') return value;
83
- if (!(value instanceof VmExtern)) return value;
83
+ if (!isVmExtern(value)) return value;
84
84
 
85
85
  if (value.thisArg == null || typeof value.value != 'function') {
86
86
  return value.value;
@@ -1,15 +1,8 @@
1
1
  import { getPrototypeOf, isArray, values } from '../../helpers/utils.js';
2
- import {
3
- isVmFunction,
4
- VmModule,
5
- type VmAny,
6
- type VmArray,
7
- type VmConst,
8
- type VmImmutable,
9
- type VmRecord,
10
- type VmValue,
11
- } from './index.js';
12
- import { VmWrapper } from './wrapper.js';
2
+ import type { VmAny, VmArray, VmConst, VmImmutable, VmRecord, VmValue } from './index.js';
3
+ import { isVmWrapper } from './wrapper.js';
4
+ import { isVmModule } from './module.js';
5
+ import { isVmFunction } from './function.js';
13
6
 
14
7
  const MAX_DEPTH = 32;
15
8
  /**
@@ -59,7 +52,7 @@ function isVmConstInner(value: unknown, depth: number): value is VmConst {
59
52
  return true;
60
53
  case 'object':
61
54
  if (value == null) return true;
62
- if (value instanceof VmWrapper) return false;
55
+ if (isVmWrapper(value)) return false;
63
56
  if (isArray(value)) {
64
57
  return isVmArrayDeep(value, depth);
65
58
  } else {
@@ -92,7 +85,7 @@ export function isVmConst(value: unknown, checkDeep = false): value is VmConst {
92
85
  return true;
93
86
  case 'object':
94
87
  if (value == null) return true;
95
- if (value instanceof VmWrapper) return false;
88
+ if (isVmWrapper(value)) return false;
96
89
  if (!checkDeep) {
97
90
  if (isArray(value)) {
98
91
  return isVmArrayDeep(value, 0);
@@ -122,7 +115,7 @@ export function isVmImmutable(value: unknown, checkDeep: boolean): value is VmIm
122
115
  * 检查是否为 Mirascript 不可变值
123
116
  */
124
117
  export function isVmImmutable(value: unknown, checkDeep = false): value is VmImmutable {
125
- return value instanceof VmModule || isVmFunction(value) || isVmConst(value, checkDeep);
118
+ return isVmModule(value) || isVmFunction(value) || isVmConst(value, checkDeep);
126
119
  }
127
120
  /**
128
121
  * 检查是否为 Mirascript 合法值
@@ -152,7 +145,7 @@ export function isVmAny(value: unknown, checkDeep: boolean): value is VmAny {
152
145
  return true;
153
146
  case 'object':
154
147
  if (value == null) return true;
155
- if (value instanceof VmWrapper) return true;
148
+ if (isVmWrapper(value)) return true;
156
149
  return isVmConst(value, checkDeep);
157
150
  case 'function':
158
151
  return isVmFunction(value);
@@ -29,6 +29,8 @@ export interface VmContext {
29
29
  readonly [kVmContext]: true;
30
30
  /** 枚举所有 key,仅在 LSP 中使用 */
31
31
  keys(): Iterable<string>;
32
+ /** 描述值,返回 MarkDown 文本,仅在 LSP 中使用 */
33
+ describe?(key: string): string | undefined;
32
34
  /** 获取指定 key 的值 `global[key]` */
33
35
  get(key: string): VmValue;
34
36
  /** 查找指定 key 是否存在 `key in global` */
@@ -91,7 +93,11 @@ class ValueVmContext implements VmContext {
91
93
  has(key: string): boolean {
92
94
  return key in this.env;
93
95
  }
94
- constructor(private readonly env: VmContextRecord) {}
96
+ constructor(
97
+ private readonly env: VmContextRecord,
98
+ /** @inheritdoc */
99
+ readonly describe?: (key: string) => string | undefined,
100
+ ) {}
95
101
  }
96
102
 
97
103
  /** 以工厂函数为后备的实现 */
@@ -115,30 +121,36 @@ class FactoryVmContext implements VmContext {
115
121
  constructor(
116
122
  private readonly getter: (key: string) => VmValue | undefined,
117
123
  private readonly enumerator?: () => Iterable<string>,
124
+ /** @inheritdoc */
125
+ readonly describe?: (key: string) => string | undefined,
118
126
  ) {}
119
127
  }
120
128
 
129
+ /** 以值为后备的实现 */
130
+ type CreateVmContextWithValues = readonly [
131
+ vmValues?: VmContextRecord | null | undefined,
132
+ externValues?: Record<string, unknown> | null | undefined,
133
+ describer?: ((key: string) => string | undefined) | null | undefined,
134
+ ];
135
+ /** 以工厂函数为后备的实现 */
136
+ type CreateVmContextWithFactory = readonly [
137
+ getter: (key: string) => VmValue | undefined,
138
+ enumerator?: (() => Iterable<string>) | null | undefined,
139
+ describer?: ((key: string) => string | undefined) | null | undefined,
140
+ ];
141
+
121
142
  /** 创建用于执行脚本的执行上下文 */
122
- export function createVmContext(
123
- ...args:
124
- | readonly [
125
- vmValues?: VmContextRecord | null | undefined,
126
- externValues?: Record<string, unknown> | null | undefined,
127
- ]
128
- | readonly [
129
- getter: (key: string) => VmValue | undefined,
130
- enumerator?: (() => Iterable<string>) | null | undefined,
131
- ]
132
- ): VmContext {
143
+ export function createVmContext(...args: CreateVmContextWithValues | CreateVmContextWithFactory): VmContext {
133
144
  if (args[0] == null && args[1] == null) {
134
145
  return { ...DefaultVmContext };
135
146
  }
136
147
 
137
148
  if (typeof args[0] == 'function') {
138
- return new FactoryVmContext(args[0], args[1] as (() => Iterable<string>) | undefined);
149
+ const [getter, enumerator, describer] = args as CreateVmContextWithFactory;
150
+ return new FactoryVmContext(getter, enumerator ?? undefined, describer ?? undefined);
139
151
  }
140
152
 
141
- const [vmValues, externValues] = args;
153
+ const [vmValues, externValues, describer] = args as CreateVmContextWithValues;
142
154
  const env = create(VmSharedContext) as VmContextRecord;
143
155
  if (vmValues) {
144
156
  for (const [key, value] of entries(vmValues)) {
@@ -147,11 +159,11 @@ export function createVmContext(
147
159
  }
148
160
  }
149
161
  if (externValues) {
150
- for (const [key, value] of entries(externValues as Record<string, unknown>)) {
162
+ for (const [key, value] of entries(externValues)) {
151
163
  env[key] = value == null ? null : wrapToVmValue(value, null);
152
164
  }
153
165
  }
154
- return new ValueVmContext(env);
166
+ return new ValueVmContext(env, describer ?? undefined);
155
167
  }
156
168
 
157
169
  /** 检查是否为执行上下文 */
@@ -7,6 +7,8 @@ import { unwrapFromVmValue, wrapToVmValue } from './boundary.js';
7
7
  const ObjectPrototype = Object.prototype;
8
8
  // eslint-disable-next-line @typescript-eslint/unbound-method
9
9
  const ObjectToString = ObjectPrototype.toString;
10
+ // eslint-disable-next-line @typescript-eslint/unbound-method
11
+ const FunctionToString = Function.prototype.toString;
10
12
  /** 包装 Mirascript `extern` 类型的对象 */
11
13
  export class VmExtern<const T extends object = object> extends VmWrapper<T> {
12
14
  constructor(
@@ -82,14 +84,14 @@ export class VmExtern<const T extends object = object> extends VmWrapper<T> {
82
84
  }
83
85
  /** @inheritdoc */
84
86
  override same(other: VmAny): boolean {
85
- if (!(other instanceof VmExtern)) return false;
87
+ if (!isVmExtern(other)) return false;
86
88
  return this.value === other.value && this.thisArg === other.thisArg;
87
89
  }
88
90
  /** @inheritdoc */
89
91
  override toString(): string {
90
92
  // eslint-disable-next-line @typescript-eslint/unbound-method
91
93
  const { toString } = this.value;
92
- if (typeof toString != 'function' || toString === ObjectToString) {
94
+ if (typeof toString != 'function' || toString === ObjectToString || toString === FunctionToString) {
93
95
  return super.toString();
94
96
  }
95
97
  try {
@@ -128,3 +130,10 @@ export class VmExtern<const T extends object = object> extends VmWrapper<T> {
128
130
  return tag;
129
131
  }
130
132
  }
133
+
134
+ const kVmExtern = Symbol.for('mirascript.vm.extern');
135
+ Object.defineProperty(VmExtern.prototype, kVmExtern, { value: true });
136
+ /** 检查值是否为 Mirascript 外部值 */
137
+ export function isVmExtern<T extends object>(value: unknown): value is VmExtern<T> {
138
+ return value != null && typeof value == 'object' && kVmExtern in value;
139
+ }
@@ -1,8 +1,8 @@
1
1
  import { isArray } from '../../helpers/utils.js';
2
- import { VmExtern } from './extern.js';
3
- import { isVmFunction, type VmFunction } from './function.js';
4
- import { VmModule } from './module.js';
5
- import { VmWrapper } from './wrapper.js';
2
+ import { isVmExtern, VmExtern } from './extern.js';
3
+ import { isVmFunction, VmFunction } from './function.js';
4
+ import { VmModule, isVmModule } from './module.js';
5
+ import { isVmWrapper } from './wrapper.js';
6
6
 
7
7
  /** Mirascript 原始值 */
8
8
  export type VmPrimitive = null | string | number | boolean;
@@ -74,7 +74,7 @@ export function isVmArray(value: VmAny): value is VmArray {
74
74
  */
75
75
  export function isVmRecord(value: VmAny): value is VmRecord {
76
76
  if (value == null || typeof value !== 'object') return false;
77
- if (value instanceof VmWrapper) return false;
77
+ if (isVmWrapper(value)) return false;
78
78
  if (isVmArray(value)) return false;
79
79
  value satisfies VmRecord;
80
80
  return true;
@@ -92,35 +92,16 @@ export function isVmPrimitive(value: unknown): value is VmPrimitive {
92
92
  return false;
93
93
  }
94
94
 
95
- export { VmExtern } from './extern.js';
95
+ export { VmFunction, isVmFunction, VmExtern, isVmExtern, VmModule, isVmModule, isVmWrapper };
96
96
  export { wrapToVmValue, unwrapFromVmValue, toVmFunctionProxy, fromVmFunctionProxy } from './boundary.js';
97
97
 
98
- /** 检查值是否为 Mirascript 外部值 */
99
- export function isVmExtern(value: unknown): value is VmExtern {
100
- return value instanceof VmExtern;
101
- }
102
-
103
98
  /** 检查值是否为 Mirascript 可调用值 */
104
99
  // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
105
100
  export function isVmCallable(value: unknown): value is VmFunction | VmExtern<Function> {
106
101
  return isVmFunction(value) || (isVmExtern(value) && typeof value.value == 'function');
107
102
  }
108
103
 
109
- export {
110
- VmFunction,
111
- isVmFunction,
112
- getVmFunctionInfo,
113
- type VmFunctionInfo,
114
- type VmFunctionLike,
115
- type VmFunctionOption,
116
- } from './function.js';
117
-
118
- export { VmModule } from './module.js';
119
-
120
- /** 检查值是否为 Mirascript 模块 */
121
- export function isVmModule(value: unknown): value is VmModule {
122
- return value instanceof VmModule;
123
- }
104
+ export { getVmFunctionInfo, type VmFunctionInfo, type VmFunctionLike, type VmFunctionOption } from './function.js';
124
105
 
125
106
  export { type VmContext, type VmSharedContext, isVmContext, defineVmContextValue, createVmContext } from './context.js';
126
107
 
@@ -38,3 +38,10 @@ export class VmModule<const T extends Record<string, VmValue> = Record<string, V
38
38
  return this.name;
39
39
  }
40
40
  }
41
+
42
+ const kVmModule = Symbol.for('mirascript.vm.module');
43
+ Object.defineProperty(VmModule.prototype, kVmModule, { value: true });
44
+ /** 检查值是否为 Mirascript 模块 */
45
+ export function isVmModule<T extends Record<string, VmValue>>(value: unknown): value is VmModule<T> {
46
+ return value != null && typeof value == 'object' && kVmModule in value;
47
+ }
@@ -26,3 +26,10 @@ export abstract class VmWrapper<T extends object> {
26
26
  return `<${this.type} ${this.describe}>`;
27
27
  }
28
28
  }
29
+
30
+ const kVmWrapper = Symbol.for('mirascript.vm.wrapper');
31
+ Object.defineProperty(VmWrapper.prototype, kVmWrapper, { value: true });
32
+ /** 检查值是否为 MiraScript 包装器 */
33
+ export function isVmWrapper<T extends object>(value: unknown): value is VmWrapper<T> {
34
+ return value != null && typeof value == 'object' && kVmWrapper in value;
35
+ }