@mirascript/mirascript 0.1.13 → 0.1.15

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 (86) hide show
  1. package/dist/chunk-JVFUK7AN.js +2324 -0
  2. package/dist/chunk-JVFUK7AN.js.map +6 -0
  3. package/dist/{chunk-IKSSUVRE.js → chunk-NT235HY3.js} +97 -1264
  4. package/dist/chunk-NT235HY3.js.map +6 -0
  5. package/dist/cli/index.js +8 -7
  6. package/dist/cli/index.js.map +1 -1
  7. package/dist/compiler/emit.d.ts.map +1 -1
  8. package/dist/compiler/worker.js +1 -1
  9. package/dist/helpers/constants.d.ts +5 -0
  10. package/dist/helpers/constants.d.ts.map +1 -1
  11. package/dist/helpers/serialize.d.ts +2 -2
  12. package/dist/helpers/serialize.d.ts.map +1 -1
  13. package/dist/index.js +15 -9
  14. package/dist/subtle.d.ts +1 -1
  15. package/dist/subtle.d.ts.map +1 -1
  16. package/dist/subtle.js +7 -7
  17. package/dist/vm/lib/_helpers.d.ts +8 -8
  18. package/dist/vm/lib/global/debug.d.ts +1 -1
  19. package/dist/vm/lib/global/json.d.ts +2 -2
  20. package/dist/vm/lib/global/json.d.ts.map +1 -1
  21. package/dist/vm/lib/global/mod/matrix.d.ts +1 -1
  22. package/dist/vm/lib/global/mod/matrix.d.ts.map +1 -1
  23. package/dist/vm/lib/global/sequence/all-any.d.ts +2 -2
  24. package/dist/vm/lib/global/sequence/entries.d.ts +4 -4
  25. package/dist/vm/lib/global/sequence/find.d.ts +2 -2
  26. package/dist/vm/lib/global/sequence/repeat.d.ts +1 -1
  27. package/dist/vm/lib/global/sequence/with.d.ts +1 -1
  28. package/dist/vm/lib/global/sequence/with.d.ts.map +1 -1
  29. package/dist/vm/lib/global/sequence/zip.d.ts +1 -1
  30. package/dist/vm/operations.d.ts +4 -2
  31. package/dist/vm/operations.d.ts.map +1 -1
  32. package/dist/vm/types/any.d.ts +10 -0
  33. package/dist/vm/types/any.d.ts.map +1 -0
  34. package/dist/vm/types/array.d.ts +12 -0
  35. package/dist/vm/types/array.d.ts.map +1 -0
  36. package/dist/vm/types/callable.d.ts +5 -0
  37. package/dist/vm/types/callable.d.ts.map +1 -0
  38. package/dist/vm/types/const.d.ts +15 -0
  39. package/dist/vm/types/const.d.ts.map +1 -0
  40. package/dist/vm/types/context.d.ts +8 -3
  41. package/dist/vm/types/context.d.ts.map +1 -1
  42. package/dist/vm/types/extern.d.ts +1 -1
  43. package/dist/vm/types/extern.d.ts.map +1 -1
  44. package/dist/vm/types/immutable.d.ts +15 -0
  45. package/dist/vm/types/immutable.d.ts.map +1 -0
  46. package/dist/vm/types/index.d.ts +17 -47
  47. package/dist/vm/types/index.d.ts.map +1 -1
  48. package/dist/vm/types/primitive.d.ts +7 -0
  49. package/dist/vm/types/primitive.d.ts.map +1 -0
  50. package/dist/vm/types/record.d.ts +20 -0
  51. package/dist/vm/types/record.d.ts.map +1 -0
  52. package/dist/vm/types/value.d.ts +14 -0
  53. package/dist/vm/types/value.d.ts.map +1 -0
  54. package/dist/vm/types/wrapper.d.ts +1 -1
  55. package/dist/vm/types/wrapper.d.ts.map +1 -1
  56. package/package.json +2 -2
  57. package/src/compiler/compile-fast.ts +1 -1
  58. package/src/compiler/emit.ts +131 -17
  59. package/src/helpers/constants.ts +6 -1
  60. package/src/helpers/serialize.ts +35 -15
  61. package/src/subtle.ts +1 -1
  62. package/src/vm/lib/_helpers.ts +8 -8
  63. package/src/vm/lib/global/sequence/entries.ts +2 -2
  64. package/src/vm/lib/global/sequence/find.ts +4 -4
  65. package/src/vm/lib/global/sequence/map-filter.ts +3 -3
  66. package/src/vm/lib/global/sequence/with.ts +1 -1
  67. package/src/vm/lib/global/sequence/zip.ts +1 -1
  68. package/src/vm/lib/global/time.ts +3 -3
  69. package/src/vm/operations.ts +7 -7
  70. package/src/vm/types/any.ts +33 -0
  71. package/src/vm/types/array.ts +19 -0
  72. package/src/vm/types/callable.ts +10 -0
  73. package/src/vm/types/{checker.ts → const.ts} +7 -55
  74. package/src/vm/types/context.ts +25 -6
  75. package/src/vm/types/extern.ts +19 -5
  76. package/src/vm/types/immutable.ts +22 -0
  77. package/src/vm/types/index.ts +31 -83
  78. package/src/vm/types/primitive.ts +14 -0
  79. package/src/vm/types/record.ts +53 -0
  80. package/src/vm/types/value.ts +22 -0
  81. package/src/vm/types/wrapper.ts +1 -1
  82. package/dist/chunk-AGIXQM52.js +0 -916
  83. package/dist/chunk-AGIXQM52.js.map +0 -6
  84. package/dist/chunk-IKSSUVRE.js.map +0 -6
  85. package/dist/vm/types/checker.d.ts +0 -30
  86. package/dist/vm/types/checker.d.ts.map +0 -1
@@ -10,9 +10,10 @@ import {
10
10
  isVmFunction,
11
11
  isVmModule,
12
12
  isVmExtern,
13
+ isVmArrayLikeRecordByEntires,
13
14
  } from '../vm/index.js';
14
15
  import { REG_IDENTIFIER, REG_ORDINAL } from './constants.js';
15
- import { entries, isFinite, isNaN } from '../helpers/utils.js';
16
+ import { entries, hasOwn, isFinite, isNaN } from '../helpers/utils.js';
16
17
 
17
18
  const REG_IDENTIFIER_FULL = new RegExp(`^${REG_IDENTIFIER.source}$`, REG_IDENTIFIER.flags);
18
19
  const REG_ORDINAL_FULL = new RegExp(`^${REG_ORDINAL.source}$`, REG_ORDINAL.flags);
@@ -42,7 +43,7 @@ export interface SerializeOptions {
42
43
  /** 序列化记录 */
43
44
  serializeRecord: (value: VmRecord, depth: number, options: SerializeOptions) => string;
44
45
  /** 序列化属性名 */
45
- serializePropName: (value: string | number, options: SerializeOptions) => string;
46
+ serializePropName: (value: number | string, options: SerializeOptions) => string;
46
47
  /** 序列化模块 */
47
48
  serializeModule: (value: VmModule, depth: number, options: SerializeOptions) => string;
48
49
  /** 序列化外部值 */
@@ -66,17 +67,23 @@ const DEFAULT_OPTIONS = Object.freeze({
66
67
  serializeExtern: serializeNil,
67
68
  } satisfies SerializeOptions);
68
69
 
70
+ /** 是否为默认选项 */
71
+ function isDefaultOptions(options: Partial<SerializeOptions> | undefined): boolean {
72
+ return options == null || options === DEFAULT_OPTIONS;
73
+ }
74
+
69
75
  /** 获取选项 */
70
76
  function getSerializeOptions(options: Partial<SerializeOptions> | undefined): SerializeOptions {
71
- if (options == null) return DEFAULT_OPTIONS;
72
- const opt = { ...DEFAULT_OPTIONS };
77
+ if (isDefaultOptions(options)) return DEFAULT_OPTIONS;
78
+ let opt: SerializeOptions | null = null;
73
79
  for (const key in options) {
80
+ if (!hasOwn(options, key) || !hasOwn(DEFAULT_OPTIONS, key)) continue;
74
81
  const el = options[key as keyof SerializeOptions];
75
- if (el != null) {
76
- opt[key as keyof SerializeOptions] = el as never;
77
- }
82
+ if (el == null) continue;
83
+ opt ??= { ...DEFAULT_OPTIONS };
84
+ opt[key as keyof SerializeOptions] = el as never;
78
85
  }
79
- return Object.freeze(opt);
86
+ return opt ? Object.freeze(opt) : DEFAULT_OPTIONS;
80
87
  }
81
88
 
82
89
  /**
@@ -137,8 +144,19 @@ export function serializeString(value: string, options?: Partial<SerializeOption
137
144
  return serializeStringImpl(value, getSerializeOptions(options));
138
145
  }
139
146
 
147
+ /** 使用默认选项序列化属性名 */
148
+ function serializeRecordKeyDefault(key: string): string {
149
+ if (REG_ORDINAL_FULL.test(key) || REG_IDENTIFIER_FULL.test(key)) {
150
+ return key;
151
+ }
152
+ return serializeStringImpl(key, DEFAULT_OPTIONS);
153
+ }
154
+
140
155
  /** 序列化属性名 */
141
- function serializePropNameImpl(value: string, options: SerializeOptions): string {
156
+ function serializeRecordKeyOpt(value: string, options: SerializeOptions): string {
157
+ if (isDefaultOptions(options)) {
158
+ return serializeRecordKeyDefault(value);
159
+ }
142
160
  if (REG_ORDINAL_FULL.test(value)) {
143
161
  // 合法的数字属性名
144
162
  return options.serializePropName(Number(value), options);
@@ -152,8 +170,11 @@ function serializePropNameImpl(value: string, options: SerializeOptions): string
152
170
  }
153
171
 
154
172
  /** 序列化属性名 */
155
- export function serializePropName(value: string, options?: Partial<SerializeOptions>): string {
156
- return serializePropNameImpl(value, getSerializeOptions(options));
173
+ export function serializeRecordKey(key: string, options?: Partial<SerializeOptions>): string {
174
+ if (isDefaultOptions(options)) {
175
+ return serializeRecordKeyDefault(key);
176
+ }
177
+ return serializeRecordKeyOpt(key, getSerializeOptions(options));
157
178
  }
158
179
 
159
180
  /** 序列化 nil 值 */
@@ -220,18 +241,17 @@ export function serializeRecord(value: VmRecord, depth: number, options: Seriali
220
241
  if (k === '0') {
221
242
  return `(${serializeImpl(v, depth, options)},)`; // 单个元素数组
222
243
  }
223
- return `(${serializePropNameImpl(k, options)}: ${serializeImpl(v, depth, options)})`;
244
+ return `(${serializeRecordKeyOpt(k, options)}: ${serializeImpl(v, depth, options)})`;
224
245
  }
225
246
 
226
- // 根据 ES 标准,数字 key 会按顺序枚举
227
- const omitKey = e.length < 10 && e.every(([key], index) => key === String(index));
247
+ const omitKey = isVmArrayLikeRecordByEntires(e);
228
248
  let str = '(';
229
249
  for (const [key, val] of e) {
230
250
  if (str.length > 1) str += ', ';
231
251
  if (omitKey) {
232
252
  str += serializeImpl(val, depth, options);
233
253
  } else {
234
- str += `${serializePropNameImpl(key, options)}: ${serializeImpl(val, depth, options)}`;
254
+ str += `${serializeRecordKeyOpt(key, options)}: ${serializeImpl(val, depth, options)}`;
235
255
  }
236
256
  }
237
257
  str += ')';
package/src/subtle.ts CHANGED
@@ -9,7 +9,7 @@ export {
9
9
  serializeBoolean,
10
10
  serializeNumber,
11
11
  serializeString,
12
- serializePropName,
12
+ serializeRecordKey,
13
13
  serializeArray,
14
14
  serializeRecord,
15
15
  type SerializeOptions,
@@ -30,7 +30,7 @@ export function throwError(message: string, recovered: VmAny | (() => VmAny)): n
30
30
 
31
31
  /** 抛出预期外类型异常 */
32
32
  export function throwUnexpectedTypeError(
33
- name: string | number,
33
+ name: number | string,
34
34
  expected: string,
35
35
  value: VmAny,
36
36
  recovered: VmAny | (() => VmAny),
@@ -49,7 +49,7 @@ export function rethrowError(prefix: string, error: unknown, recovered: VmAny |
49
49
 
50
50
  /** 标记参数为必须项 */
51
51
  export function required<const T = VmValue>(
52
- name: string | number,
52
+ name: number | string,
53
53
  value: T | undefined,
54
54
  recovered: VmAny | (() => VmAny),
55
55
  ): asserts value is T {
@@ -62,7 +62,7 @@ export function required<const T = VmValue>(
62
62
 
63
63
  /** 标记参数为数组 */
64
64
  export function expectArray(
65
- name: string | number,
65
+ name: number | string,
66
66
  value: VmAny,
67
67
  recovered: VmAny | (() => VmAny),
68
68
  ): asserts value is VmArray {
@@ -74,7 +74,7 @@ export function expectArray(
74
74
 
75
75
  /** 标记参数为记录 */
76
76
  export function expectRecord(
77
- name: string | number,
77
+ name: number | string,
78
78
  value: VmAny,
79
79
  recovered: VmAny | (() => VmAny),
80
80
  ): asserts value is VmRecord {
@@ -86,7 +86,7 @@ export function expectRecord(
86
86
 
87
87
  /** 标记参数为数组或记录 */
88
88
  export function expectArrayOrRecord(
89
- name: string | number,
89
+ name: number | string,
90
90
  value: VmAny,
91
91
  recovered: VmAny | (() => VmAny),
92
92
  ): asserts value is VmArray | VmRecord {
@@ -98,7 +98,7 @@ export function expectArrayOrRecord(
98
98
 
99
99
  /** 标记参数为复合类型 */
100
100
  export function expectCompound(
101
- name: string | number,
101
+ name: number | string,
102
102
  value: VmAny,
103
103
  recovered: VmAny | (() => VmAny),
104
104
  ): asserts value is VmArray | VmRecord | VmModule | VmExtern {
@@ -110,7 +110,7 @@ export function expectCompound(
110
110
 
111
111
  /** 标记参数为常量 */
112
112
  export function expectConst(
113
- name: string | number,
113
+ name: number | string,
114
114
  value: VmAny,
115
115
  recovered: VmAny | (() => VmAny),
116
116
  ): asserts value is VmConst {
@@ -122,7 +122,7 @@ export function expectConst(
122
122
 
123
123
  /** 标记为可调用 */
124
124
  export function expectCallable(
125
- name: string | number,
125
+ name: number | string,
126
126
  value: VmAny,
127
127
  recovered: VmAny | (() => VmAny),
128
128
  ): asserts value is VmFunction | VmExtern {
@@ -22,7 +22,7 @@ export const keys = VmLib(
22
22
  summary: '返回数组、记录、外部对象或模块的键列表',
23
23
  params: { data: '要获取键的数组、记录、外部对象或模块' },
24
24
  paramsType: { data: 'array | record | extern | module' },
25
- returnsType: '(string | number)[]',
25
+ returnsType: 'number[] | string[]',
26
26
  examples: ['keys([10, 20]) // [0, 1]', 'keys((10, 20)) // ["0", "1"]'],
27
27
  },
28
28
  );
@@ -61,7 +61,7 @@ export const entries = VmLib(
61
61
  summary: '返回数组或记录的键值对列表',
62
62
  params: { data: '要获取键值对的数组或记录' },
63
63
  paramsType: { data: 'array | record' },
64
- returnsType: '(string | number, any)[]',
64
+ returnsType: '(number, any)[] | (string, any)[]',
65
65
  examples: ['entries([1]) // [(0, 1)]', 'entries((a: 1)) // [("a", 1)]'],
66
66
  },
67
67
  );
@@ -9,7 +9,7 @@ export const find = VmLib(
9
9
  expectArrayOrRecord('data', data, null);
10
10
  required('predicate', predicate, null);
11
11
  const p = isVmCallable(predicate)
12
- ? (value: VmValue, key: string | number, data: VmValue) => {
12
+ ? (value: VmValue, key: number | string, data: VmValue) => {
13
13
  const ret = $Call(predicate, [value, key, data]);
14
14
  return $ToBoolean(ret);
15
15
  }
@@ -38,14 +38,14 @@ export const find = VmLib(
38
38
  {
39
39
  summary: '查找数组或记录中的键值对,返回第一个满足条件的键值对',
40
40
  params: {
41
- data: '查的数组或记录',
41
+ data: '要查找的数组或记录',
42
42
  predicate: '用于测试每个键值对的函数,或要查找的值',
43
43
  },
44
44
  paramsType: {
45
45
  data: 'array | record',
46
- predicate: '(fn(value: any, key: number | string | nil, input: type(data)) -> boolean) | any',
46
+ predicate: '(fn(value: any, key: number | string, input: type(data)) -> boolean) | any',
47
47
  },
48
- returnsType: '(string | number, any) | nil',
48
+ returnsType: '(number | string, any) | nil',
49
49
  examples: ['find([3, 5, 8], fn (v) { v % 2 == 0 }) // (2, 8)', `find((x: 1, y: 2, z: 3), 2) // ('y', 2)`],
50
50
  },
51
51
  );
@@ -32,7 +32,7 @@ export const map = VmLib(
32
32
  },
33
33
  paramsType: {
34
34
  data: 'array | record',
35
- f: 'fn(value: any, key: number | string | nil, input: type(data)) -> any',
35
+ f: 'fn(value: any, key: number | string, input: type(data)) -> any',
36
36
  },
37
37
  returnsType: 'type(data)',
38
38
  examples: ['map([1, 2, 3], fn (v) { v * v }) // [1, 4, 9]'],
@@ -53,7 +53,7 @@ export const filter = VmLib(
53
53
  },
54
54
  paramsType: {
55
55
  data: 'array | record',
56
- predicate: 'fn(value: any, key: number | string | nil, input: type(data)) -> boolean',
56
+ predicate: 'fn(value: any, key: number | string, input: type(data)) -> boolean',
57
57
  },
58
58
  returnsType: 'type(data)',
59
59
  examples: ['filter([1, 2, 3, 4], fn (v) { v % 2 == 0 }) // [2, 4]'],
@@ -74,7 +74,7 @@ export const filter_map = VmLib(
74
74
  },
75
75
  paramsType: {
76
76
  data: 'array | record',
77
- f: 'fn(value: any, key: number | string | nil, input: type(data)) -> any | nil',
77
+ f: 'fn(value: any, key: number | string, input: type(data)) -> any | nil',
78
78
  },
79
79
  returnsType: 'type(data)',
80
80
  examples: ['filter_map([1, 2, 3], fn (v) { if v % 2 == 0 { v * v } else { nil } }) // [4]'],
@@ -125,7 +125,7 @@ const _with = VmLib(
125
125
  },
126
126
  paramsType: {
127
127
  data: 'array | record',
128
- '..entries': `[..[string | number | (string | number)[], any][]]`,
128
+ '..entries': `[..[number | string | (number | string)[], any][]]`,
129
129
  },
130
130
  returnsType: 'type(data)',
131
131
  examples: [
@@ -15,7 +15,7 @@ export const zip = VmLib(
15
15
  len = Math.max(len, arr.length);
16
16
  }
17
17
  if (len === 0) return [];
18
- const result: Array<Record<string | number, VmConst>> = [];
18
+ const result: Array<Record<number | string, VmConst>> = [];
19
19
  const isArr = isVmArray(data);
20
20
  for (let i = 0; i < len; i++) {
21
21
  Cp();
@@ -19,7 +19,7 @@ export const to_timestamp = VmLib(
19
19
  {
20
20
  summary: '将数据转换为 Unix 毫秒时间戳',
21
21
  params: { datetime: '要转换的数据,默认为当前时间' },
22
- paramsType: { datetime: 'string | number' },
22
+ paramsType: { datetime: 'number | string' },
23
23
  returnsType: 'number',
24
24
  examples: ['to_timestamp("1970-01-01T00:00:00Z") // 0'],
25
25
  },
@@ -49,7 +49,7 @@ export const to_datetime = VmLib(
49
49
  datetime: '要转换的数据,默认为当前时间',
50
50
  offset: '时区偏移量(单位:小时),默认为 0',
51
51
  },
52
- paramsType: { datetime: 'string | number', offset: 'number' },
52
+ paramsType: { datetime: 'number | string', offset: 'number' },
53
53
  returnsType: 'Date',
54
54
  examples: [
55
55
  `
@@ -73,7 +73,7 @@ export const to_iso8601 = VmLib(
73
73
  {
74
74
  summary: '将数据转换为 ISO 8601 格式的字符串',
75
75
  params: { datetime: '要转换的数据,默认为当前时间' },
76
- paramsType: { datetime: 'string | number' },
76
+ paramsType: { datetime: 'number | string' },
77
77
  returnsType: 'string',
78
78
  examples: ['to_iso8601(0) // "1970-01-01T00:00:00.000Z"'],
79
79
  },
@@ -221,7 +221,7 @@ export const $Length = (value: VmAny): number => {
221
221
  }
222
222
  return Number.NaN;
223
223
  };
224
- export const $Omit = (value: VmAny, omitted: ReadonlyArray<string | number>): VmRecord => {
224
+ export const $Omit = (value: VmAny, omitted: ReadonlyArray<number | string>): VmRecord => {
225
225
  $AssertInit(value);
226
226
  if (value == null || !isVmRecord(value)) return {};
227
227
  const result: Record<string, VmConst> = {};
@@ -234,7 +234,7 @@ export const $Omit = (value: VmAny, omitted: ReadonlyArray<string | number>): Vm
234
234
  }
235
235
  return result;
236
236
  };
237
- export const $Pick = (value: VmAny, picked: ReadonlyArray<string | number>): VmRecord => {
237
+ export const $Pick = (value: VmAny, picked: ReadonlyArray<number | string>): VmRecord => {
238
238
  $AssertInit(value);
239
239
  if (value == null || !isVmRecord(value)) return {};
240
240
  const result: Record<string, VmConst> = {};
@@ -313,9 +313,9 @@ function numberToString(value: number): string {
313
313
  }
314
314
 
315
315
  /** 将值转为字符串 */
316
- function innerToString(value: VmAny, useBraces: boolean): string {
316
+ export function $InnerToString(value: VmAny, useBraces: boolean): string {
317
317
  if (value == null) return 'nil';
318
- if (isVmWrapper(value)) return value.toString();
318
+ if (isVmWrapper(value)) return value.toString(useBraces);
319
319
  if (typeof value == 'function') {
320
320
  const name = getVmFunctionInfo(value)?.fullName;
321
321
  return name ? `<function ${name}>` : `<function>`;
@@ -323,7 +323,7 @@ function innerToString(value: VmAny, useBraces: boolean): string {
323
323
  if (isVmArray(value)) {
324
324
  const strings: string[] = [];
325
325
  for (const item of value) {
326
- strings.push(innerToString(item, true));
326
+ strings.push($InnerToString(item, true));
327
327
  }
328
328
  // 在 join 过程中会自动把 null/undefined 和 empty slot 转为 ''
329
329
  // 与 innerToString 行为不一致
@@ -333,7 +333,7 @@ function innerToString(value: VmAny, useBraces: boolean): string {
333
333
  }
334
334
  if (typeof value == 'object') {
335
335
  const entries = keys(value)
336
- .map((key) => `${key}: ${innerToString(value[key], true)}`)
336
+ .map((key) => `${key}: ${$InnerToString(value[key], true)}`)
337
337
  .join(', ');
338
338
  if (!useBraces) return entries;
339
339
  return `(${entries})`;
@@ -347,7 +347,7 @@ export const $ToString = (value: VmAny): string => {
347
347
  $AssertInit(value);
348
348
  if (typeof value == 'string') return value;
349
349
  if (value === null) return '';
350
- return innerToString(value, false);
350
+ return $InnerToString(value, false);
351
351
  };
352
352
  export const $ToNumber = (value: VmAny): number => {
353
353
  $AssertInit(value);
@@ -0,0 +1,33 @@
1
+ import { isVmConst } from './const.js';
2
+ import { isVmFunction } from './function.js';
3
+ import { isVmWrapper } from './wrapper.js';
4
+ import type { VmValue } from './value.js';
5
+
6
+ /** Mirascript 虚拟机内的值(包括未初始化变量) */
7
+ export type VmAny = VmValue | VmUninitialized;
8
+
9
+ /** Mirascript 虚拟机内的未初始化变量 */
10
+ export type VmUninitialized = undefined;
11
+
12
+ /**
13
+ * 检查是否为 Mirascript 值
14
+ */
15
+ export function isVmAny(value: unknown, checkDeep: boolean): value is VmAny {
16
+ switch (typeof value) {
17
+ case 'string':
18
+ case 'number':
19
+ case 'boolean':
20
+ case 'undefined':
21
+ return true;
22
+ case 'object':
23
+ if (value == null) return true;
24
+ if (isVmWrapper(value)) return true;
25
+ return isVmConst(value, checkDeep);
26
+ case 'function':
27
+ return isVmFunction(value);
28
+ case 'bigint':
29
+ case 'symbol':
30
+ default:
31
+ return false; // Other types are not valid
32
+ }
33
+ }
@@ -0,0 +1,19 @@
1
+ import { isArray } from '../../helpers/utils.js';
2
+ import type { VmAny, VmConst } from './index.js';
3
+
4
+ /**
5
+ * Mirascript 数组
6
+ * 数组中的 `undefined`、`null` 及 <empty slot> 均视作 `nil`
7
+ */
8
+ export type VmArray = ReadonlyArray<VmConst | undefined>;
9
+
10
+ export const VM_ARRAY_MAX_LENGTH = 2 ** 31 - 1;
11
+
12
+ /**
13
+ * 检查值是否为 Mirascript 数组
14
+ */
15
+ export function isVmArray(value: VmAny): value is VmArray {
16
+ if (!isArray(value)) return false;
17
+ value as VmArray satisfies VmArray;
18
+ return true;
19
+ }
@@ -0,0 +1,10 @@
1
+ import { isVmExtern, type VmExtern } from './extern.js';
2
+ import { isVmFunction, type VmFunction, type VmFunctionLike } from './function.js';
3
+
4
+ /** 检查值是否为 Mirascript 可调用值 */
5
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
6
+ export function isVmCallable<E extends Function, F extends VmFunctionLike>(
7
+ value: unknown,
8
+ ): value is VmFunction<F> | VmExtern<E> {
9
+ return isVmFunction<F>(value) || (isVmExtern<E>(value) && typeof value.value == 'function');
10
+ }
@@ -1,8 +1,12 @@
1
+ import type { VmArray } from './array.js';
2
+ import type { VmPrimitive } from './primitive.js';
3
+ import type { VmRecord } from './record.js';
4
+ import type { VmAny } from './any.js';
1
5
  import { getPrototypeOf, isArray, values } from '../../helpers/utils.js';
2
- import type { VmAny, VmArray, VmConst, VmImmutable, VmRecord, VmValue } from './index.js';
3
6
  import { isVmWrapper } from './wrapper.js';
4
- import { isVmModule } from './module.js';
5
- import { isVmFunction } from './function.js';
7
+
8
+ /** Mirascript 虚拟机内的值语义值 */
9
+ export type VmConst = VmPrimitive | VmRecord | VmArray;
6
10
 
7
11
  const MAX_DEPTH = 32;
8
12
  /**
@@ -103,55 +107,3 @@ export function isVmConst(value: unknown, checkDeep = false): value is VmConst {
103
107
  return false; // Other types are not valid
104
108
  }
105
109
  }
106
- /**
107
- * 检查是否为 Mirascript 不可变值
108
- */
109
- export function isVmImmutable(value: VmAny): value is VmImmutable;
110
- /**
111
- * 检查是否为 Mirascript 不可变值
112
- */
113
- export function isVmImmutable(value: unknown, checkDeep: boolean): value is VmImmutable;
114
- /**
115
- * 检查是否为 Mirascript 不可变值
116
- */
117
- export function isVmImmutable(value: unknown, checkDeep = false): value is VmImmutable {
118
- return isVmModule(value) || isVmFunction(value) || isVmConst(value, checkDeep);
119
- }
120
- /**
121
- * 检查是否为 Mirascript 合法值
122
- */
123
- export function isVmValue(value: VmAny): value is VmValue;
124
- /**
125
- * 检查是否为 Mirascript 合法值
126
- */
127
- export function isVmValue(value: unknown, checkDeep: boolean): value is VmValue;
128
- /**
129
- * 检查是否为 Mirascript 合法值
130
- */
131
- export function isVmValue(value: unknown, checkDeep = false): value is VmValue {
132
- if (value === undefined) return false;
133
- return isVmAny(value, checkDeep);
134
- }
135
-
136
- /**
137
- * 检查是否为 Mirascript 值
138
- */
139
- export function isVmAny(value: unknown, checkDeep: boolean): value is VmAny {
140
- switch (typeof value) {
141
- case 'string':
142
- case 'number':
143
- case 'boolean':
144
- case 'undefined':
145
- return true;
146
- case 'object':
147
- if (value == null) return true;
148
- if (isVmWrapper(value)) return true;
149
- return isVmConst(value, checkDeep);
150
- case 'function':
151
- return isVmFunction(value);
152
- case 'bigint':
153
- case 'symbol':
154
- default:
155
- return false; // Other types are not valid
156
- }
157
- }
@@ -9,6 +9,7 @@ import {
9
9
  } from './index.js';
10
10
  import { create, entries, keys } from '../../helpers/utils.js';
11
11
  import type * as global from '../lib/global/index.js';
12
+ import { VmError } from '../error.js';
12
13
 
13
14
  /** 全局导入的标准库 */
14
15
  type GlobalKeys = keyof typeof global;
@@ -31,15 +32,27 @@ export interface VmContext {
31
32
  keys(): Iterable<string>;
32
33
  /** 描述值,返回 MarkDown 文本,仅在 LSP 中使用 */
33
34
  describe?(key: string): string | undefined;
34
- /** 获取指定 key 的值 `global[key]` */
35
+ /**
36
+ * 获取指定 key 的值 `global[key]`
37
+ * @throws {VmError} 如果值不存在则抛出异常
38
+ */
35
39
  get(key: string): VmValue;
36
40
  /** 查找指定 key 是否存在 `key in global` */
37
41
  has(key: string): boolean;
38
42
  }
39
43
  /** MiraScript 执行上下文 */
40
- export type VmContextRecord = Record<string, VmValue | undefined>;
44
+ export type VmContextRecord = Record<string, VmValue>;
45
+ /** MiraScript 执行上下文 */
46
+ export type VmContextRecordLoose = Record<string, VmValue | undefined>;
41
47
  export const VmSharedContext = create(null) as VmSharedContext;
42
48
 
49
+ let VmSharedContextKeys: readonly string[] | null = null;
50
+
51
+ /** 全局变量未找到 */
52
+ function globalVarNotFound(name: string): never {
53
+ throw new VmError(`Global variable '${name}' is not defined.`, null);
54
+ }
55
+
43
56
  /** 定义在所有 MiraScript 执行上下文中共享的全局变量 */
44
57
  export function defineVmContextValue(
45
58
  name: string,
@@ -57,6 +70,7 @@ export function defineVmContextValue(
57
70
  v = value;
58
71
  }
59
72
  VmSharedContext[name] = v ?? null;
73
+ VmSharedContextKeys = null;
60
74
  }
61
75
 
62
76
  /** 无后备的实现 */
@@ -64,11 +78,14 @@ export const DefaultVmContext: VmContext = Object.freeze({
64
78
  [kVmContext]: true as const,
65
79
  /** @inheritdoc */
66
80
  keys(): Iterable<string> {
67
- return keys(VmSharedContext);
81
+ VmSharedContextKeys ??= Object.freeze(keys(VmSharedContext));
82
+ return VmSharedContextKeys;
68
83
  },
69
84
  /** @inheritdoc */
70
85
  get(key: string): VmValue {
71
- return VmSharedContext[key] ?? null;
86
+ const val = VmSharedContext[key];
87
+ if (val === undefined) globalVarNotFound(key);
88
+ return val;
72
89
  },
73
90
  /** @inheritdoc */
74
91
  has(key: string): boolean {
@@ -87,7 +104,9 @@ class ValueVmContext implements VmContext {
87
104
  }
88
105
  /** @inheritdoc */
89
106
  get(key: string): VmValue {
90
- return this.env[key] ?? null;
107
+ const val = this.env[key];
108
+ if (val === undefined) globalVarNotFound(key);
109
+ return val;
91
110
  }
92
111
  /** @inheritdoc */
93
112
  has(key: string): boolean {
@@ -128,7 +147,7 @@ class FactoryVmContext implements VmContext {
128
147
 
129
148
  /** 以值为后备的实现 */
130
149
  type CreateVmContextWithValues = readonly [
131
- vmValues?: VmContextRecord | null | undefined,
150
+ vmValues?: VmContextRecordLoose | null | undefined,
132
151
  externValues?: Record<string, unknown> | null | undefined,
133
152
  describer?: ((key: string) => string | undefined) | null | undefined,
134
153
  ];
@@ -1,14 +1,17 @@
1
1
  import { VmError } from '../error.js';
2
2
  import { VmWrapper } from './wrapper.js';
3
3
  import type { TypeName, VmAny, VmConst, VmPrimitive, VmValue } from './index.js';
4
- import { getPrototypeOf, hasOwn, apply } from '../../helpers/utils.js';
4
+ import { getPrototypeOf, hasOwn, apply, isArray } from '../../helpers/utils.js';
5
5
  import { unwrapFromVmValue, wrapToVmValue } from './boundary.js';
6
+ import { $InnerToString } from '../operations.js';
6
7
 
7
8
  const ObjectPrototype = Object.prototype;
8
9
  // eslint-disable-next-line @typescript-eslint/unbound-method
9
10
  const ObjectToString = ObjectPrototype.toString;
10
11
  // eslint-disable-next-line @typescript-eslint/unbound-method
11
12
  const FunctionToString = Function.prototype.toString;
13
+ const ArrayToString = Array.prototype.toString;
14
+ const ArrayMap = Array.prototype.map;
12
15
  /** 包装 Mirascript `extern` 类型的对象 */
13
16
  export class VmExtern<const T extends object = object> extends VmWrapper<T> {
14
17
  constructor(
@@ -88,16 +91,25 @@ export class VmExtern<const T extends object = object> extends VmWrapper<T> {
88
91
  return this.value === other.value && this.thisArg === other.thisArg;
89
92
  }
90
93
  /** @inheritdoc */
91
- override toString(): string {
94
+ override toString(useBraces: boolean): string {
92
95
  // eslint-disable-next-line @typescript-eslint/unbound-method
93
96
  const { toString } = this.value;
94
97
  if (typeof toString != 'function' || toString === ObjectToString || toString === FunctionToString) {
95
- return super.toString();
98
+ return super.toString(useBraces);
99
+ }
100
+ if (toString === ArrayToString && isArray(this.value)) {
101
+ const mapped = ArrayMap.call(this.value, (item: unknown) => {
102
+ if (item === undefined) return '';
103
+ return $InnerToString(wrapToVmValue(item ?? null, null), true);
104
+ });
105
+ const str = mapped.join(', ');
106
+ if (useBraces) return `[${str}]`;
107
+ return str;
96
108
  }
97
109
  try {
98
110
  return String(this.value);
99
111
  } catch {
100
- return super.toString();
112
+ return super.toString(useBraces);
101
113
  }
102
114
  }
103
115
  /** @inheritdoc */
@@ -107,7 +119,9 @@ export class VmExtern<const T extends object = object> extends VmWrapper<T> {
107
119
  /** @inheritdoc */
108
120
  override get describe(): string {
109
121
  const tag = ObjectToString.call(this.value).slice(8, -1);
110
- if (tag === 'Object') {
122
+ if (isArray(this.value)) {
123
+ return `${tag}(${this.value.length})`;
124
+ } else if (tag === 'Object') {
111
125
  const proto = getPrototypeOf(this.value);
112
126
  if (proto === ObjectPrototype) {
113
127
  return 'Object';
@@ -0,0 +1,22 @@
1
+ import type { VmAny } from './any.js';
2
+ import { isVmConst, type VmConst } from './const.js';
3
+ import { isVmFunction, type VmFunction } from './function.js';
4
+ import { isVmModule, type VmModule } from './module.js';
5
+
6
+ /** Mirascript 虚拟机内的不可变值 */
7
+ export type VmImmutable = VmConst | VmFunction | VmModule;
8
+
9
+ /**
10
+ * 检查是否为 Mirascript 不可变值
11
+ */
12
+ export function isVmImmutable(value: VmAny): value is VmImmutable;
13
+ /**
14
+ * 检查是否为 Mirascript 不可变值
15
+ */
16
+ export function isVmImmutable(value: unknown, checkDeep: boolean): value is VmImmutable;
17
+ /**
18
+ * 检查是否为 Mirascript 不可变值
19
+ */
20
+ export function isVmImmutable(value: unknown, checkDeep = false): value is VmImmutable {
21
+ return isVmModule(value) || isVmFunction(value) || isVmConst(value, checkDeep);
22
+ }