@mirascript/mirascript 0.1.3 → 0.1.5

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-3RUWGMBP.js → chunk-JB6LPPFJ.js} +190 -24
  2. package/dist/chunk-JB6LPPFJ.js.map +6 -0
  3. package/dist/{chunk-MVHCSH3E.js → chunk-OU3K5EYB.js} +61 -131
  4. package/dist/chunk-OU3K5EYB.js.map +6 -0
  5. package/dist/cli/execute.d.ts +1 -1
  6. package/dist/cli/execute.d.ts.map +1 -1
  7. package/dist/cli/index.js +58 -28
  8. package/dist/cli/index.js.map +2 -2
  9. package/dist/cli/print.d.ts.map +1 -1
  10. package/dist/compiler/diagnostic.d.ts +3 -1
  11. package/dist/compiler/diagnostic.d.ts.map +1 -1
  12. package/dist/compiler/emit.d.ts +2 -1
  13. package/dist/compiler/emit.d.ts.map +1 -1
  14. package/dist/compiler/index.d.ts.map +1 -1
  15. package/dist/compiler/worker.d.ts.map +1 -1
  16. package/dist/compiler/worker.js +6 -3
  17. package/dist/compiler/worker.js.map +1 -1
  18. package/dist/helpers/serialize.d.ts +10 -2
  19. package/dist/helpers/serialize.d.ts.map +1 -1
  20. package/dist/index.js +2 -2
  21. package/dist/subtle.d.ts +1 -1
  22. package/dist/subtle.d.ts.map +1 -1
  23. package/dist/subtle.js +17 -7
  24. package/dist/vm/lib/_helpers.d.ts +1 -1
  25. package/dist/vm/lib/_helpers.d.ts.map +1 -1
  26. package/dist/vm/lib/global/bit.d.ts +7 -8
  27. package/dist/vm/lib/global/bit.d.ts.map +1 -1
  28. package/dist/vm/lib/global/debug.d.ts +6 -3
  29. package/dist/vm/lib/global/debug.d.ts.map +1 -1
  30. package/dist/vm/lib/global/json.d.ts +2 -3
  31. package/dist/vm/lib/global/json.d.ts.map +1 -1
  32. package/dist/vm/lib/global/math-additional.d.ts +1 -2
  33. package/dist/vm/lib/global/math-additional.d.ts.map +1 -1
  34. package/dist/vm/lib/global/math-arr.d.ts +5 -6
  35. package/dist/vm/lib/global/math-arr.d.ts.map +1 -1
  36. package/dist/vm/lib/global/math-unary.d.ts +26 -27
  37. package/dist/vm/lib/global/math-unary.d.ts.map +1 -1
  38. package/dist/vm/lib/global/math.d.ts +3 -4
  39. package/dist/vm/lib/global/math.d.ts.map +1 -1
  40. package/dist/vm/lib/global/mod/matrix.d.ts +13 -14
  41. package/dist/vm/lib/global/mod/matrix.d.ts.map +1 -1
  42. package/dist/vm/lib/global/sequence/all-any.d.ts +2 -3
  43. package/dist/vm/lib/global/sequence/all-any.d.ts.map +1 -1
  44. package/dist/vm/lib/global/sequence/entries.d.ts +4 -5
  45. package/dist/vm/lib/global/sequence/entries.d.ts.map +1 -1
  46. package/dist/vm/lib/global/sequence/find.d.ts +2 -3
  47. package/dist/vm/lib/global/sequence/find.d.ts.map +1 -1
  48. package/dist/vm/lib/global/sequence/flatten.d.ts +1 -2
  49. package/dist/vm/lib/global/sequence/flatten.d.ts.map +1 -1
  50. package/dist/vm/lib/global/sequence/len.d.ts +1 -2
  51. package/dist/vm/lib/global/sequence/len.d.ts.map +1 -1
  52. package/dist/vm/lib/global/sequence/map-filter.d.ts +3 -4
  53. package/dist/vm/lib/global/sequence/map-filter.d.ts.map +1 -1
  54. package/dist/vm/lib/global/sequence/repeat.d.ts +1 -2
  55. package/dist/vm/lib/global/sequence/repeat.d.ts.map +1 -1
  56. package/dist/vm/lib/global/sequence/reverse.d.ts +1 -2
  57. package/dist/vm/lib/global/sequence/reverse.d.ts.map +1 -1
  58. package/dist/vm/lib/global/sequence/sort.d.ts +2 -3
  59. package/dist/vm/lib/global/sequence/sort.d.ts.map +1 -1
  60. package/dist/vm/lib/global/sequence/with.d.ts +1 -2
  61. package/dist/vm/lib/global/sequence/with.d.ts.map +1 -1
  62. package/dist/vm/lib/global/sequence/zip.d.ts +1 -2
  63. package/dist/vm/lib/global/sequence/zip.d.ts.map +1 -1
  64. package/dist/vm/lib/global/string.d.ts +10 -11
  65. package/dist/vm/lib/global/string.d.ts.map +1 -1
  66. package/dist/vm/lib/global/time.d.ts +4 -5
  67. package/dist/vm/lib/global/time.d.ts.map +1 -1
  68. package/dist/vm/lib/global/to-primitive.d.ts +4 -5
  69. package/dist/vm/lib/global/to-primitive.d.ts.map +1 -1
  70. package/dist/vm/types/wrapper.d.ts.map +1 -1
  71. package/package.json +7 -5
  72. package/src/cli/execute.ts +15 -7
  73. package/src/cli/index.ts +22 -5
  74. package/src/cli/print.ts +43 -27
  75. package/src/compiler/diagnostic.ts +19 -3
  76. package/src/compiler/emit.ts +45 -19
  77. package/src/compiler/index.ts +10 -2
  78. package/src/compiler/worker.ts +5 -1
  79. package/src/helpers/serialize.ts +6 -6
  80. package/src/subtle.ts +11 -1
  81. package/src/vm/lib/_helpers.ts +8 -3
  82. package/src/vm/lib/global/debug.ts +24 -3
  83. package/src/vm/lib/global/math-arr.ts +1 -1
  84. package/src/vm/types/wrapper.ts +3 -1
  85. package/dist/chunk-3RUWGMBP.js.map +0 -6
  86. package/dist/chunk-MVHCSH3E.js.map +0 -6
@@ -1 +1 @@
1
- {"version":3,"file":"to-primitive.d.ts","sourceRoot":"","sources":["../../../../src/vm/lib/global/to-primitive.ts"],"names":[],"mappings":"AACA,OAAO,EAAY,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAEjD,eAAO,MAAM,SAAS,uEAYrB,CAAC;AAEF,eAAO,MAAM,SAAS,uEAYrB,CAAC;AAEF,eAAO,MAAM,UAAU,wEAYtB,CAAC;AAEF,eAAO,MAAM,MAAM,6HAYlB,CAAC"}
1
+ {"version":3,"file":"to-primitive.d.ts","sourceRoot":"","sources":["../../../../src/vm/lib/global/to-primitive.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,SAAS,mIAYrB,CAAC;AAEF,eAAO,MAAM,SAAS,mIAYrB,CAAC;AAEF,eAAO,MAAM,UAAU,oIAYtB,CAAC;AAEF,eAAO,MAAM,MAAM,yLAYlB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"wrapper.d.ts","sourceRoot":"","sources":["../../../src/vm/types/wrapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAElD;;GAEG;AACH,8BAAsB,SAAS,CAAC,CAAC,SAAS,MAAM;IAChC,QAAQ,CAAC,KAAK,EAAE,CAAC;gBAAR,KAAK,EAAE,CAAC;IAC7B,cAAc;IACd,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAClC,cAAc;IACd,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK;IAChC,WAAW;IACX,QAAQ,CAAC,IAAI,IAAI,MAAM,EAAE;IACzB,aAAa;IACb,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO;IACpC,WAAW;IACX,QAAQ,KAAK,IAAI,IAAI,QAAQ,CAAC;IAC9B,WAAW;IACX,QAAQ,KAAK,QAAQ,IAAI,MAAM,CAAC;IAChC,iCAAiC;IACjC,MAAM,IAAI,SAAS;IAGnB,YAAY;IACZ,QAAQ,IAAI,MAAM;CAGrB;AAID,4BAA4B;AAC5B,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAAC,CAAC,CAAC,CAEnF"}
1
+ {"version":3,"file":"wrapper.d.ts","sourceRoot":"","sources":["../../../src/vm/types/wrapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAElD;;GAEG;AACH,8BAAsB,SAAS,CAAC,CAAC,SAAS,MAAM;IAChC,QAAQ,CAAC,KAAK,EAAE,CAAC;gBAAR,KAAK,EAAE,CAAC;IAC7B,cAAc;IACd,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAClC,cAAc;IACd,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK;IAChC,WAAW;IACX,QAAQ,CAAC,IAAI,IAAI,MAAM,EAAE;IACzB,aAAa;IACb,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO;IACpC,WAAW;IACX,QAAQ,KAAK,IAAI,IAAI,QAAQ,CAAC;IAC9B,WAAW;IACX,QAAQ,KAAK,QAAQ,IAAI,MAAM,CAAC;IAChC,iCAAiC;IACjC,MAAM,IAAI,SAAS;IAGnB,YAAY;IACZ,QAAQ,IAAI,MAAM;CAKrB;AAID,4BAA4B;AAC5B,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAAC,CAAC,CAAC,CAEnF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mirascript/mirascript",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "type": "module",
5
5
  "description": "An expression based scripting language.",
6
6
  "main": "./dist/index.js",
@@ -35,8 +35,10 @@
35
35
  "ansi-styles": "^6.2.3",
36
36
  "commander": "^14.0.2",
37
37
  "js-base64": "^3.7.8",
38
- "@mirascript/napi": "~0.1.3",
39
- "@mirascript/wasm": "~0.1.3"
38
+ "source-map-js": "^1.2.1",
39
+ "supports-color": "^10.2.2",
40
+ "@mirascript/napi": "~0.1.5",
41
+ "@mirascript/wasm": "~0.1.5"
40
42
  },
41
43
  "devDependencies": {
42
44
  "@types/node": "^24.10.0",
@@ -44,13 +46,13 @@
44
46
  "ava": "^6.4.1",
45
47
  "c8": "^10.1.3",
46
48
  "sinon": "^21.0.0",
47
- "type-fest": "^5.1.0"
49
+ "type-fest": "^5.2.0"
48
50
  },
49
51
  "scripts": {
50
52
  "watch": "pnpm build:ts --watch",
51
53
  "build:ts": "pnpm clean && tsc",
52
54
  "build": "pnpm build:ts --emitDeclarationOnly && node ./esbuild.config.js",
53
- "mirascript": "node ./dist/cli/index.js",
55
+ "mirascript": "node --enable-source-maps ./dist/cli/index.js",
54
56
  "clean": "rimraf dist",
55
57
  "test": "c8 ava"
56
58
  }
@@ -1,21 +1,24 @@
1
1
  /* eslint-disable no-console */
2
2
  import styles from 'ansi-styles';
3
+ import supportsColor from 'supports-color';
3
4
  import { compile } from '../index.js';
4
5
  import { createVmContext, VmFunction, type VmValue } from '../vm/index.js';
5
6
  import { debug_print } from '../vm/lib/global/debug.js';
6
7
  import { print } from './print.js';
7
8
 
8
9
  /** 执行脚本 */
9
- export async function execute(script: string, template: boolean, variables: Record<string, VmValue>): Promise<void> {
10
+ export async function execute(
11
+ script: string,
12
+ template: boolean,
13
+ variables: Record<string, VmValue>,
14
+ fileName: string,
15
+ ): Promise<void> {
10
16
  try {
11
- const f = await compile(script, { input_mode: template ? 'Template' : 'Script' });
17
+ const f = await compile(script, { input_mode: template ? 'Template' : 'Script', sourceMap: true, fileName });
12
18
  const r = f(
13
19
  createVmContext({
14
20
  debug_print: VmFunction((...values) => {
15
- console.log(
16
- '\u001B[46;30m MiraScript \u001B[0m',
17
- ...values.map((v) => (typeof v == 'string' ? v : print(v))),
18
- );
21
+ console.log(...debug_print.prefix, ...values.map((v) => (typeof v == 'string' ? v : print(v))));
19
22
  }, debug_print),
20
23
  ...variables,
21
24
  }),
@@ -26,7 +29,12 @@ export async function execute(script: string, template: boolean, variables: Reco
26
29
  console.log(print(r));
27
30
  }
28
31
  } catch (ex) {
29
- console.error(styles.red.open + (ex as Error).message + styles.red.close);
32
+ const { stack } = ex as Error;
33
+ if (supportsColor.stderr) {
34
+ console.error(styles.red.open + stack + styles.red.close);
35
+ } else {
36
+ console.error(stack);
37
+ }
30
38
  process.exitCode = 2;
31
39
  }
32
40
  }
package/src/cli/index.ts CHANGED
@@ -1,11 +1,14 @@
1
1
  /* c8 ignore start */
2
2
  /* eslint-disable no-console */
3
3
  import { readFile, stat } from 'node:fs/promises';
4
- import { program } from '@commander-js/extra-typings';
4
+ import { pathToFileURL } from 'node:url';
5
+ import { InvalidArgumentError, program } from '@commander-js/extra-typings';
5
6
  import { execute } from './execute.js';
6
7
  import pkg from '../../package.json' with { type: 'json' };
7
8
  import { compileSync } from '../compiler/index.js';
8
- import type { VmValue } from '../vm/index.js';
9
+ import { configCheckpoint, type VmValue } from '../vm/index.js';
10
+
11
+ const DEFAULT_TIMEOUT = 3000;
9
12
 
10
13
  program.name(pkg.name.split('/').pop()!).version(pkg.version).description(pkg.description);
11
14
 
@@ -33,12 +36,26 @@ program
33
36
  {} as Record<string, VmValue>,
34
37
  )
35
38
  .option('-t, --template', '使用模板模式')
39
+ .option(
40
+ '--timeout <ms>',
41
+ '脚本执行超时时间(毫秒,0 表示不超时)',
42
+ (v) => {
43
+ const ms = Number.parseFloat(v);
44
+ if (Number.isNaN(ms) || ms < 0) {
45
+ throw new InvalidArgumentError('超时时间必须是非负整数');
46
+ }
47
+ return ms;
48
+ },
49
+ DEFAULT_TIMEOUT,
50
+ )
36
51
  .option('--no-template', '使用脚本模式')
37
52
  .option('-e, --eval <script>', '要执行的脚本')
38
53
  .argument('[script]', '要执行的脚本文件路径(如果提供了 -e 则忽略此参数)')
39
54
  .action(async (script, opt) => {
55
+ configCheckpoint(opt.timeout || Number.POSITIVE_INFINITY);
40
56
  if (opt.eval != null) {
41
- await execute(opt.eval, !!opt.template, opt.variable);
57
+ const template = !!opt.template;
58
+ await execute(opt.eval, template, opt.variable, template ? 'eval.miratpl' : 'eval.mira');
42
59
  return;
43
60
  }
44
61
  if (script) {
@@ -62,9 +79,9 @@ program
62
79
  }
63
80
  return;
64
81
  }
65
- const context = await readFile(script, 'utf8');
82
+ const context = await readFile(script, 'utf-8');
66
83
  const template = opt.template ?? script.endsWith('.miratpl');
67
- await execute(context, template, opt.variable);
84
+ await execute(context, template, opt.variable, pathToFileURL(script).href);
68
85
  return;
69
86
  }
70
87
  program.help({ error: true });
package/src/cli/print.ts CHANGED
@@ -1,40 +1,56 @@
1
1
  import styles from 'ansi-styles';
2
- import { serialize, type SerializeOptions } from '../subtle.js';
2
+ import supportsColor from 'supports-color';
3
+ import {
4
+ serialize,
5
+ serializeNumber,
6
+ serializeBoolean,
7
+ serializeNil,
8
+ type SerializeOptions,
9
+ operations,
10
+ } from '../subtle.js';
3
11
  import type { VmAny, VmRecord } from '../vm/index.js';
4
12
 
13
+ const noColor = !supportsColor.stdout;
14
+
5
15
  const options: Partial<SerializeOptions> = {
6
16
  maxDepth: 3,
7
- serializeNil: () => styles.gray.open + 'nil' + styles.gray.close,
8
- serializeBoolean: (v) => styles.blue.open + (v ? 'true' : 'false') + styles.blue.close,
9
- serializeNumber: (v) =>
10
- styles.yellow.open +
11
- (Number.isNaN(v) ? 'nan' : Number.isFinite(v) ? v.toString() : v > 0 ? 'inf' : '-inf') +
12
- styles.yellow.close,
13
- serializeStringQuote: (v, open) => {
14
- const q = styles.dim.open + v + styles.dim.close;
15
- if (open) {
16
- return styles.green.open + q;
17
- } else {
18
- return q + styles.green.close;
19
- }
20
- },
21
- serializeStringEscape: (v) => styles.bold.open + v + styles.bold.close,
22
- serializeFunction: (v) => styles.cyan.open + `<function ${v.name || 'anonymous'}>` + styles.cyan.close,
23
- serializePropName: (v) => styles.whiteBright.open + String(v) + styles.whiteBright.close,
24
- serializeModule: (v, depth, options) => {
25
- return (
26
- styles.magenta.open +
27
- `<module ${v.name || 'anonymous'}>` +
28
- styles.magenta.close +
29
- ' ' +
30
- options.serializeRecord(v.value as VmRecord, depth, options)
31
- );
32
- },
17
+ serializeNil: noColor ? undefined : () => styles.gray.open + serializeNil() + styles.gray.close,
18
+ serializeBoolean: noColor ? undefined : (v) => styles.blue.open + serializeBoolean(v) + styles.blue.close,
19
+ serializeNumber: noColor ? undefined : (v) => styles.yellow.open + serializeNumber(v) + styles.yellow.close,
20
+ serializeStringQuote: noColor
21
+ ? undefined
22
+ : (v, open) => {
23
+ const q = styles.dim.open + v + styles.dim.close;
24
+ if (open) {
25
+ return styles.green.open + q;
26
+ } else {
27
+ return q + styles.green.close;
28
+ }
29
+ },
30
+ serializeStringEscape: noColor ? undefined : (v) => styles.bold.open + v + styles.bold.close,
31
+ serializePropName: noColor ? undefined : (v) => styles.whiteBright.open + String(v) + styles.whiteBright.close,
32
+ serializeFunction: noColor
33
+ ? operations.$ToString
34
+ : (v) => styles.cyan.open + operations.$ToString(v) + styles.cyan.close,
35
+ serializeModule: noColor
36
+ ? (v, depth, options) => {
37
+ return operations.$ToString(v) + ' ' + options.serializeRecord(v.value as VmRecord, depth, options);
38
+ }
39
+ : (v, depth, options) => {
40
+ return (
41
+ styles.magenta.open +
42
+ operations.$ToString(v) +
43
+ styles.magenta.close +
44
+ ' ' +
45
+ options.serializeRecord(v.value as VmRecord, depth, options)
46
+ );
47
+ },
33
48
  };
34
49
 
35
50
  /** 序列化值 */
36
51
  export function print(value: VmAny, depth = 3): string {
37
52
  if (value === undefined) {
53
+ if (noColor) return '<uninitialized>';
38
54
  return styles.gray.open + '<uninitialized>' + styles.gray.close;
39
55
  }
40
56
  return serialize(value, { ...options, maxDepth: depth });
@@ -77,20 +77,30 @@ interface ParsedDiagnostics {
77
77
  references: SourceReference[];
78
78
  /** 标签引用诊断信息 */
79
79
  tagsReferences: SourceReference[];
80
+
81
+ /** 代码映射信息 */
82
+ sourcemaps: IRange[];
80
83
  }
81
84
 
82
85
  /** 分析诊断信息,{@link diagnostic_position_encoding} 不能设为 `None` */
83
- export function parseDiagnostics(source: ScriptInput, diagnostics: Uint32Array): ParsedDiagnostics {
86
+ export function parseDiagnostics(
87
+ source: ScriptInput,
88
+ diagnostics: Uint32Array,
89
+ filter?: (code: DiagnosticCode) => boolean,
90
+ ): ParsedDiagnostics {
84
91
  const parsed = [];
85
92
  const bufLen = diagnostics.length;
86
93
  for (let i = 0; i < bufLen; i += 5) {
94
+ const code = diagnostics[i + 4]! as DiagnosticCode;
95
+ if (filter && !filter(code)) {
96
+ continue;
97
+ }
87
98
  const startLineNumber = diagnostics[i]!;
88
99
  const startColumn = diagnostics[i + 1]!;
89
100
  const endLineNumber = diagnostics[i + 2]!;
90
101
  const endColumn = diagnostics[i + 3]!;
91
- const error = diagnostics[i + 4]! as DiagnosticCode;
92
102
  parsed.push({
93
- code: error,
103
+ code,
94
104
  range: {
95
105
  startLineNumber,
96
106
  startColumn,
@@ -107,6 +117,7 @@ export function parseDiagnostics(source: ScriptInput, diagnostics: Uint32Array):
107
117
  const _tags: SourceDiagnostic[] = [];
108
118
  const _references: SourceReference[] = [];
109
119
  const _tagsReferences: SourceReference[] = [];
120
+ const _sourcemaps: IRange[] = [];
110
121
  for (let i = 0; i < parsed.length; i++) {
111
122
  const diagnostic = parsed[i]!;
112
123
  const { code } = diagnostic;
@@ -120,7 +131,11 @@ export function parseDiagnostics(source: ScriptInput, diagnostics: Uint32Array):
120
131
  _hints.push(diagnostic);
121
132
  } else if (code > DiagnosticCode.TagStart && code < DiagnosticCode.TagEnd) {
122
133
  _tags.push(diagnostic);
134
+ } else if (code === DiagnosticCode.SourceMap) {
135
+ _sourcemaps.push(diagnostic.range);
136
+ continue;
123
137
  } else {
138
+ // 非法诊断代码,跳过
124
139
  continue;
125
140
  }
126
141
  diagnostic.references = [];
@@ -151,6 +166,7 @@ export function parseDiagnostics(source: ScriptInput, diagnostics: Uint32Array):
151
166
  tags: _tags,
152
167
  references: _references,
153
168
  tagsReferences: _tagsReferences,
169
+ sourcemaps: _sourcemaps,
154
170
  };
155
171
  }
156
172
 
@@ -1,11 +1,18 @@
1
1
  import { OpCode } from '@mirascript/wasm/types';
2
- import { encodeURL } from 'js-base64';
2
+ import { toBase64 } from 'js-base64';
3
3
  import type { VmConst, VmPrimitive } from '../vm/index.js';
4
4
  import type { ScriptInput, TranspileOptions } from './types.js';
5
+ import type { IRange } from './diagnostic.js';
6
+ import { SourceMapGenerator } from 'source-map-js';
5
7
 
6
8
  /** 生成代码 */
7
- export function emit(source: ScriptInput, chunk: Uint8Array, options: TranspileOptions): string {
8
- const gen = new Emitter(source, chunk, options);
9
+ export function emit(
10
+ source: ScriptInput,
11
+ chunk: Uint8Array,
12
+ sourcemaps: readonly IRange[],
13
+ options: TranspileOptions,
14
+ ): string {
15
+ const gen = new Emitter(source, chunk, sourcemaps, options);
9
16
  gen.read();
10
17
  const code = gen.codeLines.join('\n');
11
18
  return code;
@@ -57,7 +64,7 @@ function toJavascript(value: VmConst | undefined): string {
57
64
  return String(value);
58
65
  }
59
66
 
60
- const ORIGIN = `mira://MiraScript`;
67
+ const ORIGIN = `mira://MiraScript/`;
61
68
  let sourceId = 1;
62
69
 
63
70
  /** 创建数组 */
@@ -75,6 +82,7 @@ class Emitter {
75
82
  constructor(
76
83
  readonly source: ScriptInput,
77
84
  readonly chunk: Uint8Array,
85
+ readonly sourcemaps: readonly IRange[],
78
86
  readonly options: TranspileOptions,
79
87
  ) {
80
88
  this.pretty = options.pretty ?? false;
@@ -362,9 +370,9 @@ class Emitter {
362
370
  );
363
371
  if (script) {
364
372
  args.unshift(`global = GlobalFallback()`);
365
- code = `'use strict'; return (function script(${args.join(', ')}) { try { CpEnter(); var ${regs};`;
373
+ code = `'use strict'; return ((${args.join(', ')}) => { try { CpEnter(); var ${regs};`;
366
374
  } else {
367
- code = `${this.wv(reg)} = Function(function (${args.join(', ')}) { try { CpEnter(); var ${regs};`;
375
+ code = `${this.wv(reg)} = Function((${args.join(', ')}) => { try { CpEnter(); var ${regs};`;
368
376
  }
369
377
  if (varg) {
370
378
  code += ` var ${this.wv(argn, -1)} = Vargs(vargs);`;
@@ -688,6 +696,9 @@ class Emitter {
688
696
  code = `continue;`;
689
697
  break;
690
698
  }
699
+ case OpCode.Noop: {
700
+ return;
701
+ }
691
702
  default: {
692
703
  code = `; // ${OpCode[opcode] ?? opcode}`;
693
704
  break;
@@ -742,27 +753,42 @@ class Emitter {
742
753
  const hasSchema = /^\w+:/.test(fileName);
743
754
  if (!hasSchema) {
744
755
  if (fileName.startsWith('/')) {
745
- fileName = fileName.replace(/^\\+\s*/, '');
756
+ fileName = fileName.replace(/^\/+\s*/, '');
746
757
  }
747
758
  if (!fileName) {
748
759
  fileName = `${sourceId++}.${this.options.input_mode === 'Template' ? 'miratpl' : 'mira'}`;
749
760
  }
750
761
  }
751
- const data = {
752
- version: 3,
753
- file: fileName,
754
- sourceRoot: hasSchema ? '' : ORIGIN,
755
- sources: [fileName],
756
- sourcesContent: [ArrayBuffer.isView(this.source) ? null : this.source],
757
- names: [],
758
- mappings: '',
759
- };
762
+ const map = new SourceMapGenerator({
763
+ file: fileName + '.js',
764
+ });
765
+ if (typeof this.source === 'string') {
766
+ map.setSourceContent(fileName, this.source);
767
+ }
768
+ for (let i = 0; i < this.sourcemaps.length; i++) {
769
+ const range = this.sourcemaps[i];
770
+ if (!range) break;
771
+ map.addMapping({
772
+ generated: {
773
+ // 前两行固定为:
774
+ // (function anonymous($Add,$Aeq, ...
775
+ // ) {
776
+ line: i + 3,
777
+ column: 0,
778
+ },
779
+ original: {
780
+ line: range.startLineNumber,
781
+ column: range.startColumn - 1,
782
+ },
783
+ source: fileName,
784
+ });
785
+ }
760
786
  const prefix = '//# ';
761
- const sourceURL = hasSchema ? fileName : `${ORIGIN}/${fileName}.js`;
787
+ const sourceURL = hasSchema ? fileName : `${ORIGIN}${fileName}`;
762
788
  this.codeLines.push(
763
789
  // Prevent source map from being recognized as of this file
764
- `${prefix}sourceURL=${sourceURL}`,
765
- `${prefix}sourceMappingURL=data:application/json;base64,${encodeURL(JSON.stringify(data))}`,
790
+ `${prefix}sourceURL=${sourceURL}.js`,
791
+ `${prefix}sourceMappingURL=data:application/json;base64,${toBase64(map.toString())}`,
766
792
  );
767
793
  }
768
794
  }
@@ -4,7 +4,7 @@ import './types.js';
4
4
  import { emit } from './emit.js';
5
5
  import { createScript } from './create-script.js';
6
6
  import { compileFast } from './compile-fast.js';
7
- import { formatDiagnostic, parseDiagnostics } from './diagnostic.js';
7
+ import { DiagnosticCode, formatDiagnostic, parseDiagnostics } from './diagnostic.js';
8
8
  import { generateBytecode, generateBytecodeSync, loadModule } from './generate-bytecode.js';
9
9
  import { compileWorker } from './worker-manager.js';
10
10
  await loadModule();
@@ -33,7 +33,10 @@ export function emitScript(
33
33
  if (!code) {
34
34
  reportDiagnostic(source, diagnostics);
35
35
  }
36
- const target = emit(source, code, options);
36
+ const sourcemaps = options.sourceMap
37
+ ? parseDiagnostics(source, diagnostics, (c) => c === DiagnosticCode.SourceMap).sourcemaps
38
+ : [];
39
+ const target = emit(source, code, sourcemaps, options);
37
40
  return createScript(source, target);
38
41
  }
39
42
 
@@ -41,6 +44,11 @@ export function emitScript(
41
44
  * 生成 MiraScript 对应的 JavaScript 代码
42
45
  */
43
46
  export async function compile(this: void, source: ScriptInput, options: TranspileOptions = {}): Promise<VmScript> {
47
+ if (options.sourceMap) {
48
+ options.diagnostic_sourcemap = true;
49
+ // https://tc39.es/ecma426/#sec-terms-and-definitions-colun
50
+ options.diagnostic_position_encoding ??= 'Utf16';
51
+ }
44
52
  if (typeof source == 'string') {
45
53
  const result = compileFast(source, options);
46
54
  if (result) return result;
@@ -1,6 +1,7 @@
1
1
  import type { ScriptInput, TranspileOptions } from './types.js';
2
2
  import { emit } from './emit.js';
3
3
  import { generateBytecode } from './generate-bytecode.js';
4
+ import { DiagnosticCode, parseDiagnostics } from './diagnostic.js';
4
5
 
5
6
  /**
6
7
  * 生成 MiraScript 对应的 JavaScript 代码
@@ -13,7 +14,10 @@ export async function compile(
13
14
  if (bytecode == null) {
14
15
  return [undefined, errors];
15
16
  }
16
- const generatedCode = emit(script, bytecode, options);
17
+ const sourcemaps = options.sourceMap
18
+ ? parseDiagnostics(script, errors, (c) => c === DiagnosticCode.SourceMap).sourcemaps
19
+ : [];
20
+ const generatedCode = emit(script, bytecode, sourcemaps, options);
17
21
  return [generatedCode, errors];
18
22
  }
19
23
 
@@ -67,7 +67,7 @@ const DEFAULT_OPTIONS = Object.freeze({
67
67
  } satisfies SerializeOptions);
68
68
 
69
69
  /** 获取选项 */
70
- export function getSerializeOptions(options: Partial<SerializeOptions> | undefined): SerializeOptions {
70
+ function getSerializeOptions(options: Partial<SerializeOptions> | undefined): SerializeOptions {
71
71
  if (options == null) return DEFAULT_OPTIONS;
72
72
  const opt = { ...DEFAULT_OPTIONS };
73
73
  for (const key in options) {
@@ -157,17 +157,17 @@ export function serializePropName(value: string, options?: Partial<SerializeOpti
157
157
  }
158
158
 
159
159
  /** 序列化 nil 值 */
160
- function serializeNil(): string {
160
+ export function serializeNil(): string {
161
161
  return 'nil';
162
162
  }
163
163
 
164
164
  /** 序列化布尔值 */
165
- function serializeBoolean(value: boolean): string {
165
+ export function serializeBoolean(value: boolean): string {
166
166
  return value ? 'true' : 'false';
167
167
  }
168
168
 
169
169
  /** 序列化数字 */
170
- function serializeNumber(value: number): string {
170
+ export function serializeNumber(value: number): string {
171
171
  if (isNaN(value)) return 'nan';
172
172
  if (!isFinite(value)) return value < 0 ? '-inf' : 'inf';
173
173
  if (value === 0) {
@@ -178,7 +178,7 @@ function serializeNumber(value: number): string {
178
178
  }
179
179
 
180
180
  /** 序列化数组 */
181
- function serializeArray(value: VmArray, depth: number, options: SerializeOptions): string {
181
+ export function serializeArray(value: VmArray, depth: number, options: SerializeOptions): string {
182
182
  if (depth > options.maxDepth) return `[]`;
183
183
  if (value.length === 0) return '[]';
184
184
  let str = '[';
@@ -207,7 +207,7 @@ function customValueOf(value: VmRecord): VmAny | undefined {
207
207
  }
208
208
 
209
209
  /** 序列化记录 */
210
- function serializeRecord(value: VmRecord, depth: number, options: SerializeOptions): string {
210
+ export function serializeRecord(value: VmRecord, depth: number, options: SerializeOptions): string {
211
211
  const customValue = customValueOf(value);
212
212
  if (customValue !== undefined) {
213
213
  return serializeImpl(customValue, depth - 1, options);
package/src/subtle.ts CHANGED
@@ -1,7 +1,17 @@
1
1
  export * as constants from './helpers/constants.js';
2
2
  export { VmSharedContext, DefaultVmContext } from './vm/types/context.js';
3
3
  export * as operations from './vm/operations.js';
4
- export { serialize, serializeString, serializePropName, type SerializeOptions } from './helpers/serialize.js';
4
+ export {
5
+ serialize,
6
+ serializeNil,
7
+ serializeBoolean,
8
+ serializeNumber,
9
+ serializeString,
10
+ serializePropName,
11
+ serializeArray,
12
+ serializeRecord,
13
+ type SerializeOptions,
14
+ } from './helpers/serialize.js';
5
15
  export { lib } from './vm/lib/_loader.js';
6
16
  export * from './compiler/diagnostic.js';
7
17
  export { generateBytecode, generateBytecodeSync, emitScript } from './compiler/index.js';
@@ -191,17 +191,22 @@ export type VmLibOption = Pick<
191
191
  export type VmLib<T extends VmFunctionLike = VmFunctionLike> = T & VmLibOption;
192
192
 
193
193
  /** 创建库函数 */
194
- export function VmLib<T extends VmFunctionLike>(fn: T, option: VmLibOption): VmLib<T> {
194
+ export function VmLib<T extends VmFunctionLike, P extends Record<string, unknown>>(
195
+ fn: T,
196
+ option: VmLibOption,
197
+ properties?: P,
198
+ ): VmLib<T> & P {
195
199
  /* c8 ignore next 2 */
196
200
  if (typeof fn != 'function') throw new TypeError('Invalid function');
197
201
  if (isVmFunction(fn)) throw new TypeError('Cannot create VmLib from a VmFunction');
198
202
 
199
- const ret = fn as T & VmLibOption as Writable<VmLibOption>;
203
+ const ret = fn as T & VmLibOption & P as Writable<T & VmLibOption & P>;
204
+ Object.assign(ret, properties);
200
205
  ret.params = option.params;
201
206
  ret.paramsType = option.paramsType;
202
207
  ret.returns = option.returns;
203
208
  ret.returnsType = option.returnsType;
204
209
  ret.summary = option.summary;
205
210
  ret.examples = option.examples;
206
- return ret as T & VmLibOption;
211
+ return ret as T & VmLibOption & P;
207
212
  }
@@ -1,3 +1,4 @@
1
+ import supportsColor from 'supports-color';
1
2
  import { VmError } from '../../error.js';
2
3
  import { $ToString } from '../../operations.js';
3
4
  import type { VmAny } from '../../types/index.js';
@@ -6,7 +7,7 @@ import { VmLib } from '../_helpers.js';
6
7
  export const debug_print = VmLib(
7
8
  (...args) => {
8
9
  // eslint-disable-next-line no-console
9
- console.log('\u001B[46;30m MiraScript \u001B[0m', ...args);
10
+ console.log(...debug_print.prefix, ...args);
10
11
  },
11
12
  {
12
13
  summary: '打印调试信息到控制台',
@@ -15,14 +16,17 @@ export const debug_print = VmLib(
15
16
  returnsType: 'nil',
16
17
  examples: ['debug_print("value:", 42);'],
17
18
  },
19
+ {
20
+ prefix: ['MiraScript'] as readonly string[],
21
+ },
18
22
  );
19
23
 
20
24
  export const panic = VmLib(
21
25
  (message: VmAny) => {
22
26
  // eslint-disable-next-line no-console
23
- if (message === undefined) console.error('\u001B[41;37m MiraScript \u001B[0m');
27
+ if (message === undefined) console.error(...panic.prefix);
24
28
  // eslint-disable-next-line no-console
25
- else console.error('\u001B[41;37m MiraScript \u001B[0m', message);
29
+ else console.error(...panic.prefix, message);
26
30
  const error = message == null ? 'panic' : 'panic: ' + $ToString(message);
27
31
  throw new VmError(error, undefined);
28
32
  },
@@ -33,4 +37,21 @@ export const panic = VmLib(
33
37
  returnsType: 'never',
34
38
  examples: ['panic("boom");'],
35
39
  },
40
+ {
41
+ prefix: ['MiraScript'] as readonly string[],
42
+ },
36
43
  );
44
+
45
+ if (typeof location != 'undefined') {
46
+ const badge = '%cMiraScript';
47
+ const common = 'display: inline-block; padding: 1px 4px; border-radius: 3px;';
48
+ debug_print.prefix = [badge, `${common} background: #007acc; color: #fff;`];
49
+ panic.prefix = [badge, `${common} background: #d23d3d; color: #fff;`];
50
+ } else {
51
+ if (supportsColor.stdout) {
52
+ debug_print.prefix = ['\u001B[44;37m MiraScript \u001B[0m'];
53
+ }
54
+ if (supportsColor.stderr) {
55
+ panic.prefix = ['\u001B[41;37m MiraScript \u001B[0m'];
56
+ }
57
+ }
@@ -36,7 +36,7 @@ export const hypot = VmLib(build(Math.hypot), {
36
36
  export const sum = VmLib(
37
37
  (...values: readonly VmAny[]) => {
38
38
  const numbers = getNumbers(values);
39
- return numbers.reduce((a, b) => a + b, 0);
39
+ return numbers.reduce((a, b) => a + b, -0);
40
40
  },
41
41
  {
42
42
  summary: '返回一组数的总和',
@@ -23,7 +23,9 @@ export abstract class VmWrapper<T extends object> {
23
23
  }
24
24
  /** 转为字符串 */
25
25
  toString(): string {
26
- return `<${this.type} ${this.describe}>`;
26
+ const { type, describe } = this;
27
+ if (!describe) return `<${type}>`;
28
+ return `<${type} ${describe}>`;
27
29
  }
28
30
  }
29
31