@mirascript/mirascript 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/dist/chunk-5FQWUJIY.js +766 -0
  2. package/dist/chunk-5FQWUJIY.js.map +6 -0
  3. package/dist/chunk-BTDGMWFK.js +202 -0
  4. package/dist/chunk-BTDGMWFK.js.map +6 -0
  5. package/dist/chunk-DCXIWIW5.js +3419 -0
  6. package/dist/chunk-DCXIWIW5.js.map +6 -0
  7. package/dist/chunk-RAPJ3XLV.js +10 -0
  8. package/dist/chunk-RAPJ3XLV.js.map +6 -0
  9. package/dist/cli/execute.d.ts +4 -0
  10. package/dist/cli/execute.d.ts.map +1 -0
  11. package/dist/cli/index.d.ts +2 -0
  12. package/dist/cli/index.d.ts.map +1 -0
  13. package/dist/cli/index.js +191 -0
  14. package/dist/cli/index.js.map +6 -0
  15. package/dist/cli/print.d.ts +4 -0
  16. package/dist/cli/print.d.ts.map +1 -0
  17. package/dist/compiler/compile-bytecode.d.ts +12 -0
  18. package/dist/compiler/compile-bytecode.d.ts.map +1 -0
  19. package/dist/compiler/compile-fast.d.ts +7 -0
  20. package/dist/compiler/compile-fast.d.ts.map +1 -0
  21. package/dist/compiler/create-script.d.ts +7 -0
  22. package/dist/compiler/create-script.d.ts.map +1 -0
  23. package/dist/compiler/diagnostic.d.ts +56 -0
  24. package/dist/compiler/diagnostic.d.ts.map +1 -0
  25. package/dist/compiler/emit.d.ts +4 -0
  26. package/dist/compiler/emit.d.ts.map +1 -0
  27. package/dist/compiler/index.d.ts +13 -0
  28. package/dist/compiler/index.d.ts.map +1 -0
  29. package/dist/compiler/types.d.ts +16 -0
  30. package/dist/compiler/types.d.ts.map +1 -0
  31. package/dist/compiler/worker-manager.d.ts +6 -0
  32. package/dist/compiler/worker-manager.d.ts.map +1 -0
  33. package/dist/compiler/worker.d.ts +6 -0
  34. package/dist/compiler/worker.d.ts.map +1 -0
  35. package/dist/compiler/worker.js +34 -0
  36. package/dist/compiler/worker.js.map +6 -0
  37. package/dist/helpers/constants.d.ts +3 -0
  38. package/dist/helpers/constants.d.ts.map +1 -0
  39. package/dist/helpers/serialize.d.ts +45 -0
  40. package/dist/helpers/serialize.d.ts.map +1 -0
  41. package/dist/helpers/utils.d.ts +36 -0
  42. package/dist/helpers/utils.d.ts.map +1 -0
  43. package/dist/index.d.ts +4 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +61 -0
  46. package/dist/index.js.map +6 -0
  47. package/dist/subtle.d.ts +7 -0
  48. package/dist/subtle.d.ts.map +1 -0
  49. package/dist/subtle.js +30 -0
  50. package/dist/subtle.js.map +6 -0
  51. package/dist/vm/env.d.ts +3 -0
  52. package/dist/vm/env.d.ts.map +1 -0
  53. package/dist/vm/error.d.ts +11 -0
  54. package/dist/vm/error.d.ts.map +1 -0
  55. package/dist/vm/helpers.d.ts +21 -0
  56. package/dist/vm/helpers.d.ts.map +1 -0
  57. package/dist/vm/index.d.ts +5 -0
  58. package/dist/vm/index.d.ts.map +1 -0
  59. package/dist/vm/lib/_helpers.d.ts +35 -0
  60. package/dist/vm/lib/_helpers.d.ts.map +1 -0
  61. package/dist/vm/lib/_loader.d.ts +16 -0
  62. package/dist/vm/lib/_loader.d.ts.map +1 -0
  63. package/dist/vm/lib/global/bit.d.ts +9 -0
  64. package/dist/vm/lib/global/bit.d.ts.map +1 -0
  65. package/dist/vm/lib/global/debug.d.ts +5 -0
  66. package/dist/vm/lib/global/debug.d.ts.map +1 -0
  67. package/dist/vm/lib/global/index.d.ts +10 -0
  68. package/dist/vm/lib/global/index.d.ts.map +1 -0
  69. package/dist/vm/lib/global/json.d.ts +4 -0
  70. package/dist/vm/lib/global/json.d.ts.map +1 -0
  71. package/dist/vm/lib/global/math-additional.d.ts +3 -0
  72. package/dist/vm/lib/global/math-additional.d.ts.map +1 -0
  73. package/dist/vm/lib/global/math-arr.d.ts +8 -0
  74. package/dist/vm/lib/global/math-arr.d.ts.map +1 -0
  75. package/dist/vm/lib/global/math-const.d.ts +3 -0
  76. package/dist/vm/lib/global/math-const.d.ts.map +1 -0
  77. package/dist/vm/lib/global/math-unary.d.ts +29 -0
  78. package/dist/vm/lib/global/math-unary.d.ts.map +1 -0
  79. package/dist/vm/lib/global/math.d.ts +9 -0
  80. package/dist/vm/lib/global/math.d.ts.map +1 -0
  81. package/dist/vm/lib/global/mod/index.d.ts +3 -0
  82. package/dist/vm/lib/global/mod/index.d.ts.map +1 -0
  83. package/dist/vm/lib/global/mod/matrix.d.ts +16 -0
  84. package/dist/vm/lib/global/mod/matrix.d.ts.map +1 -0
  85. package/dist/vm/lib/global/sequence/all-any.d.ts +4 -0
  86. package/dist/vm/lib/global/sequence/all-any.d.ts.map +1 -0
  87. package/dist/vm/lib/global/sequence/entries.d.ts +12 -0
  88. package/dist/vm/lib/global/sequence/entries.d.ts.map +1 -0
  89. package/dist/vm/lib/global/sequence/find.d.ts +10 -0
  90. package/dist/vm/lib/global/sequence/find.d.ts.map +1 -0
  91. package/dist/vm/lib/global/sequence/flatten.d.ts +3 -0
  92. package/dist/vm/lib/global/sequence/flatten.d.ts.map +1 -0
  93. package/dist/vm/lib/global/sequence/index.d.ts +12 -0
  94. package/dist/vm/lib/global/sequence/index.d.ts.map +1 -0
  95. package/dist/vm/lib/global/sequence/len.d.ts +3 -0
  96. package/dist/vm/lib/global/sequence/len.d.ts.map +1 -0
  97. package/dist/vm/lib/global/sequence/map-filter.d.ts +9 -0
  98. package/dist/vm/lib/global/sequence/map-filter.d.ts.map +1 -0
  99. package/dist/vm/lib/global/sequence/repeat.d.ts +4 -0
  100. package/dist/vm/lib/global/sequence/repeat.d.ts.map +1 -0
  101. package/dist/vm/lib/global/sequence/reverse.d.ts +3 -0
  102. package/dist/vm/lib/global/sequence/reverse.d.ts.map +1 -0
  103. package/dist/vm/lib/global/sequence/sort.d.ts +5 -0
  104. package/dist/vm/lib/global/sequence/sort.d.ts.map +1 -0
  105. package/dist/vm/lib/global/sequence/with.d.ts +5 -0
  106. package/dist/vm/lib/global/sequence/with.d.ts.map +1 -0
  107. package/dist/vm/lib/global/sequence/zip.d.ts +4 -0
  108. package/dist/vm/lib/global/sequence/zip.d.ts.map +1 -0
  109. package/dist/vm/lib/global/string.d.ts +12 -0
  110. package/dist/vm/lib/global/string.d.ts.map +1 -0
  111. package/dist/vm/lib/global/time.d.ts +15 -0
  112. package/dist/vm/lib/global/time.d.ts.map +1 -0
  113. package/dist/vm/lib/global/to-primitive.d.ts +6 -0
  114. package/dist/vm/lib/global/to-primitive.d.ts.map +1 -0
  115. package/dist/vm/operations.d.ts +49 -0
  116. package/dist/vm/operations.d.ts.map +1 -0
  117. package/dist/vm/types/checker.d.ts +30 -0
  118. package/dist/vm/types/checker.d.ts.map +1 -0
  119. package/dist/vm/types/context.d.ts +43 -0
  120. package/dist/vm/types/context.d.ts.map +1 -0
  121. package/dist/vm/types/extern.d.ts +32 -0
  122. package/dist/vm/types/extern.d.ts.map +1 -0
  123. package/dist/vm/types/function.d.ts +49 -0
  124. package/dist/vm/types/function.d.ts.map +1 -0
  125. package/dist/vm/types/index.d.ts +75 -0
  126. package/dist/vm/types/index.d.ts.map +1 -0
  127. package/dist/vm/types/module.d.ts +25 -0
  128. package/dist/vm/types/module.d.ts.map +1 -0
  129. package/dist/vm/types/script.d.ts +14 -0
  130. package/dist/vm/types/script.d.ts.map +1 -0
  131. package/dist/vm/types/wrapper.d.ts +25 -0
  132. package/dist/vm/types/wrapper.d.ts.map +1 -0
  133. package/package.json +55 -0
  134. package/src/cli/execute.ts +32 -0
  135. package/src/cli/index.ts +73 -0
  136. package/src/cli/print.ts +41 -0
  137. package/src/compiler/compile-bytecode.ts +65 -0
  138. package/src/compiler/compile-fast.ts +81 -0
  139. package/src/compiler/create-script.ts +34 -0
  140. package/src/compiler/diagnostic.ts +175 -0
  141. package/src/compiler/emit.ts +764 -0
  142. package/src/compiler/index.ts +67 -0
  143. package/src/compiler/types.ts +16 -0
  144. package/src/compiler/worker-manager.ts +60 -0
  145. package/src/compiler/worker.ts +37 -0
  146. package/src/helpers/constants.ts +3 -0
  147. package/src/helpers/serialize.ts +280 -0
  148. package/src/helpers/utils.ts +16 -0
  149. package/src/index.ts +3 -0
  150. package/src/subtle.ts +6 -0
  151. package/src/vm/env.ts +16 -0
  152. package/src/vm/error.ts +22 -0
  153. package/src/vm/helpers.ts +121 -0
  154. package/src/vm/index.ts +5 -0
  155. package/src/vm/lib/_helpers.ts +215 -0
  156. package/src/vm/lib/_loader.ts +51 -0
  157. package/src/vm/lib/global/bit.ts +93 -0
  158. package/src/vm/lib/global/debug.ts +36 -0
  159. package/src/vm/lib/global/index.ts +9 -0
  160. package/src/vm/lib/global/json.ts +45 -0
  161. package/src/vm/lib/global/math-additional.ts +71 -0
  162. package/src/vm/lib/global/math-arr.ts +62 -0
  163. package/src/vm/lib/global/math-const.ts +2 -0
  164. package/src/vm/lib/global/math-unary.ts +171 -0
  165. package/src/vm/lib/global/math.ts +27 -0
  166. package/src/vm/lib/global/mod/index.ts +4 -0
  167. package/src/vm/lib/global/mod/matrix.ts +579 -0
  168. package/src/vm/lib/global/sequence/all-any.ts +73 -0
  169. package/src/vm/lib/global/sequence/entries.ts +67 -0
  170. package/src/vm/lib/global/sequence/find.ts +49 -0
  171. package/src/vm/lib/global/sequence/flatten.ts +16 -0
  172. package/src/vm/lib/global/sequence/index.ts +11 -0
  173. package/src/vm/lib/global/sequence/len.ts +15 -0
  174. package/src/vm/lib/global/sequence/map-filter.ts +82 -0
  175. package/src/vm/lib/global/sequence/repeat.ts +28 -0
  176. package/src/vm/lib/global/sequence/reverse.ts +17 -0
  177. package/src/vm/lib/global/sequence/sort.ts +88 -0
  178. package/src/vm/lib/global/sequence/with.ts +43 -0
  179. package/src/vm/lib/global/sequence/zip.ts +40 -0
  180. package/src/vm/lib/global/string.ts +149 -0
  181. package/src/vm/lib/global/time.ts +73 -0
  182. package/src/vm/lib/global/to-primitive.ts +58 -0
  183. package/src/vm/operations.ts +497 -0
  184. package/src/vm/types/checker.ts +164 -0
  185. package/src/vm/types/context.ts +161 -0
  186. package/src/vm/types/extern.ts +166 -0
  187. package/src/vm/types/function.ts +136 -0
  188. package/src/vm/types/index.ts +124 -0
  189. package/src/vm/types/module.ts +40 -0
  190. package/src/vm/types/script.ts +18 -0
  191. package/src/vm/types/wrapper.ts +28 -0
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@mirascript/mirascript",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "An expression based scripting language.",
6
+ "main": "./dist/index.js",
7
+ "bin": "./dist/cli/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "@mirascript/dev": "./src/index.ts",
12
+ "default": "./dist/index.js"
13
+ },
14
+ "./subtle": {
15
+ "@mirascript/dev": "./src/subtle.ts",
16
+ "default": "./dist/subtle.js"
17
+ },
18
+ "./cli": {
19
+ "@mirascript/dev": "./src/cli/index.ts",
20
+ "default": "./dist/cli/index.js"
21
+ }
22
+ },
23
+ "imports": {
24
+ "#compiler/worker": {
25
+ "@mirascript/dev": "./src/compiler/worker.ts",
26
+ "default": "./dist/compiler/worker.js"
27
+ },
28
+ "#compiler-bundle": {
29
+ "node": "@mirascript/napi",
30
+ "default": "@mirascript/wasm"
31
+ }
32
+ },
33
+ "dependencies": {
34
+ "@commander-js/extra-typings": "^14.0.0",
35
+ "ansi-styles": "^6.2.3",
36
+ "commander": "^14.0.1",
37
+ "js-base64": "^3.7.8",
38
+ "@mirascript/wasm": "~0.1.0",
39
+ "@mirascript/napi": "~0.1.0"
40
+ },
41
+ "devDependencies": {
42
+ "@types/sinon": "^17.0.4",
43
+ "ava": "^6.4.1",
44
+ "c8": "^10.1.3",
45
+ "sinon": "^21.0.0"
46
+ },
47
+ "scripts": {
48
+ "watch": "pnpm build:ts --watch",
49
+ "build:ts": "pnpm clean && tsc",
50
+ "build": "pnpm build:ts --emitDeclarationOnly && node ./esbuild.config.js",
51
+ "mirascript": "node ./dist/cli/index.js",
52
+ "clean": "rimraf dist",
53
+ "test": "c8 ava"
54
+ }
55
+ }
@@ -0,0 +1,32 @@
1
+ /* eslint-disable no-console */
2
+ import styles from 'ansi-styles';
3
+ import { compile } from '../index.js';
4
+ import { createVmContext, VmFunction, type VmValue } from '../vm/index.js';
5
+ import { debug_print } from '../vm/lib/global/debug.js';
6
+ import { print } from './print.js';
7
+
8
+ /** 执行脚本 */
9
+ export async function execute(script: string, template: boolean, variables: Record<string, VmValue>): Promise<void> {
10
+ try {
11
+ const f = await compile(script, { input_mode: template ? 'Template' : 'Script' });
12
+ const r = f(
13
+ createVmContext({
14
+ 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
+ );
19
+ }, debug_print),
20
+ ...variables,
21
+ }),
22
+ );
23
+ if (template) {
24
+ console.log(r);
25
+ } else {
26
+ console.log(print(r));
27
+ }
28
+ } catch (ex) {
29
+ console.error(styles.red.open + (ex as Error).message + styles.red.close);
30
+ process.exitCode = 2;
31
+ }
32
+ }
@@ -0,0 +1,73 @@
1
+ /* c8 ignore start */
2
+ /* eslint-disable no-console */
3
+ import { readFile, stat } from 'node:fs/promises';
4
+ import { program } from '@commander-js/extra-typings';
5
+ import { execute } from './execute.js';
6
+ import pkg from '../../package.json' with { type: 'json' };
7
+ import { compileSync } from '../compiler/index.js';
8
+ import type { VmValue } from '../vm/index.js';
9
+
10
+ program.name(pkg.name.split('/').pop()!).version(pkg.version).description(pkg.description);
11
+
12
+ program
13
+ .option(
14
+ '-v, --variable <key=value>',
15
+ '设置全局变量,可以多次使用',
16
+ (v, p) => {
17
+ p = { ...p };
18
+ const i = v.indexOf('=');
19
+ if (i < 0) {
20
+ p[v] = true;
21
+ } else {
22
+ const key = v.slice(0, i).trim();
23
+ const value = v.slice(i + 1).trim();
24
+ try {
25
+ const pv = compileSync(`return (${value});`)();
26
+ p[key] = pv ?? value;
27
+ } catch {
28
+ p[key] = value;
29
+ }
30
+ }
31
+ return p;
32
+ },
33
+ {} as Record<string, VmValue>,
34
+ )
35
+ .option('-t, --template', '使用模板模式')
36
+ .option('--no-template', '使用脚本模式')
37
+ .option('-e, --eval <script>', '要执行的脚本')
38
+ .argument('[script]', '要执行的脚本文件路径(如果提供了 -e 则忽略此参数)')
39
+ .action(async (script, opt) => {
40
+ if (opt.eval != null) {
41
+ await execute(opt.eval, !!opt.template, opt.variable);
42
+ return;
43
+ }
44
+ if (script) {
45
+ try {
46
+ const s = await stat(script);
47
+ if (!s.isFile()) {
48
+ console.error(`脚本路径不是文件: ${script}`);
49
+ process.exitCode = 2;
50
+ return;
51
+ }
52
+ } catch (ex) {
53
+ if ((ex as NodeJS.ErrnoException).code === 'ENOENT') {
54
+ console.error(`脚本文件不存在: ${script}`);
55
+ process.exitCode = 2;
56
+ } else if ((ex as NodeJS.ErrnoException).code === 'EACCES') {
57
+ console.error(`权限不足: ${(ex as NodeJS.ErrnoException).message}`);
58
+ process.exitCode = 3;
59
+ } else {
60
+ console.error(`无法访问脚本文件: ${(ex as NodeJS.ErrnoException).message}`);
61
+ process.exitCode = 1;
62
+ }
63
+ return;
64
+ }
65
+ const context = await readFile(script, 'utf8');
66
+ const template = opt.template ?? script.endsWith('.miratpl');
67
+ await execute(context, template, opt.variable);
68
+ return;
69
+ }
70
+ program.help({ error: true });
71
+ });
72
+
73
+ await program.parseAsync();
@@ -0,0 +1,41 @@
1
+ import styles from 'ansi-styles';
2
+ import { serialize, type SerializeOptions } from '../subtle.js';
3
+ import type { VmAny, VmRecord } from '../vm/index.js';
4
+
5
+ const options: Partial<SerializeOptions> = {
6
+ 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
+ },
33
+ };
34
+
35
+ /** 序列化值 */
36
+ export function print(value: VmAny, depth = 3): string {
37
+ if (value === undefined) {
38
+ return styles.gray.open + '<uninitialized>' + styles.gray.close;
39
+ }
40
+ return serialize(value, { ...options, maxDepth: depth });
41
+ }
@@ -0,0 +1,65 @@
1
+ import type { CompileOptions, ScriptInput } from './types.js';
2
+ import * as wasm from '@mirascript/wasm';
3
+
4
+ /** 字节码模块 */
5
+ type BcModule = typeof import('@mirascript/wasm') | typeof import('@mirascript/napi');
6
+
7
+ let module: BcModule;
8
+ let loading: Promise<void> | undefined;
9
+
10
+ /** 加载模块 */
11
+ export async function loadModule(): Promise<void> {
12
+ if (module != null) return;
13
+ if (loading) return loading;
14
+ const p = (async () => {
15
+ let mod: BcModule;
16
+ try {
17
+ mod = await import('#compiler-bundle');
18
+ /* c8 ignore next 5 */
19
+ } catch (ex) {
20
+ // eslint-disable-next-line no-console
21
+ console.warn('Failed to load compiler bundle, falling back to @mirascript/wasm');
22
+ mod = wasm;
23
+ }
24
+ await wasm.ready;
25
+ module = mod;
26
+ })();
27
+ void p.finally(() => {
28
+ if (loading === p) {
29
+ loading = undefined;
30
+ }
31
+ });
32
+ loading = p;
33
+ return p;
34
+ }
35
+
36
+ /** 检查模块加载情况 */
37
+ function checkModule(): void {
38
+ if (module == null) {
39
+ throw new Error('MiraScript compiler module is not loaded.');
40
+ }
41
+ }
42
+
43
+ /**
44
+ * 生成 MiraScript 字节码
45
+ */
46
+ export function compileBytecodeSync(
47
+ script: ScriptInput,
48
+ options: CompileOptions,
49
+ ): [Uint8Array | undefined, Uint32Array] {
50
+ checkModule();
51
+ const result = module.compileSync(script, options);
52
+ return [result.chunk, result.diagnostics];
53
+ }
54
+
55
+ /**
56
+ * 生成 MiraScript 字节码
57
+ */
58
+ export async function compileBytecode(
59
+ script: ScriptInput,
60
+ options: CompileOptions,
61
+ ): Promise<[Uint8Array | undefined, Uint32Array]> {
62
+ await loadModule();
63
+ const result = 'compile' in module ? await module.compile(script, options) : module.compileSync(script, options);
64
+ return [result.chunk, result.diagnostics];
65
+ }
@@ -0,0 +1,81 @@
1
+ import { wrapScript } from './create-script.js';
2
+ import type { TranspileOptions } from './types.js';
3
+ import type { VmScript } from '../vm/types/index.js';
4
+ import { GlobalFallback } from '../vm/helpers.js';
5
+ import { isFinite } from '../helpers/utils.js';
6
+
7
+ const REG_NUMBER_FULL = /^\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
8
+ // 只识别特殊变量名,其他标识符可能有与关键字冲突等情况,需要编译器处理
9
+ const REG_IDENTIFIER_FAST = /^(?:\$+|@+)[a-zA-Z0-9_]*$/;
10
+
11
+ // 为避免结果不一致,只对常量进行处理
12
+
13
+ // 根据目前的 benchmark 结果:
14
+ // 对长度满足 FAST_SCRIPT_MAX_LEN 的代码:
15
+ // 未命中时:产生大约 5% 的损失
16
+ // 命中时:提升性能 12 倍
17
+ // 对长度超过 FAST_SCRIPT_MAX_LEN 的代码(未调用此函数):未产生可观测的影响
18
+
19
+ const FAST_SCRIPT_MAX_LEN = 32;
20
+
21
+ /**
22
+ * 对短代码进行编译
23
+ */
24
+ function compileScriptFast(code: string, options: TranspileOptions): VmScript | undefined {
25
+ if (code.length > FAST_SCRIPT_MAX_LEN) return undefined; // 超过长度限制,直接返回 undefined
26
+
27
+ const trimmedCode = code.trim();
28
+ if (!trimmedCode) {
29
+ return wrapScript(code, () => null);
30
+ }
31
+ switch (trimmedCode) {
32
+ case 'nil':
33
+ return wrapScript(code, () => null);
34
+ case 'true':
35
+ return wrapScript(code, () => true);
36
+ case 'false':
37
+ return wrapScript(code, () => false);
38
+ case 'nan':
39
+ return wrapScript(code, () => 0 / 0);
40
+ case 'inf':
41
+ case '+inf':
42
+ return wrapScript(code, () => 1 / 0);
43
+ case '-inf':
44
+ return wrapScript(code, () => -1 / 0);
45
+ }
46
+ if (REG_IDENTIFIER_FAST.test(trimmedCode)) {
47
+ // 直接返回标识符
48
+ const id = trimmedCode;
49
+ return wrapScript(code, (global = GlobalFallback()) => global.get(id) ?? null);
50
+ }
51
+ if (REG_NUMBER_FULL.test(trimmedCode)) {
52
+ const num = Number(trimmedCode);
53
+ if (!isFinite(num)) return undefined;
54
+ // 直接返回数字
55
+ return wrapScript(code, () => num);
56
+ }
57
+ return undefined;
58
+ }
59
+
60
+ const FAST_TEMPLATE_MAX_LEN = 1024;
61
+
62
+ /**
63
+ * 对短代码进行编译
64
+ */
65
+ function compileTemplateFast(code: string, options: TranspileOptions): VmScript | undefined {
66
+ if (code.length > FAST_TEMPLATE_MAX_LEN) return undefined; // 超过长度限制,直接返回 undefined
67
+
68
+ if (!code.includes('$')) {
69
+ // 不包含插值的模板
70
+ return wrapScript(code, () => code);
71
+ }
72
+ return undefined;
73
+ }
74
+
75
+ /**
76
+ * 对短代码进行编译
77
+ */
78
+ export function compileFast(code: string, options: TranspileOptions): VmScript | undefined {
79
+ if (options.sourceMap) return undefined; // 不支持源映射
80
+ return (options.input_mode === 'Template' ? compileTemplateFast : compileScriptFast)(code, options);
81
+ }
@@ -0,0 +1,34 @@
1
+ import { defineProperty } from '../helpers/utils.js';
2
+ import { keys, values } from '../vm/env.js';
3
+ import type { VmScript, VmScriptLike } from '../vm/types/script.js';
4
+ import type { ScriptInput } from './types.js';
5
+
6
+ const kVmScript = Symbol.for('mirascript.vm.script');
7
+
8
+ /** 生成 JS 函数 */
9
+ export function wrapScript(source: ScriptInput, script: VmScriptLike): VmScript {
10
+ /* c8 ignore next 3 */
11
+ if (kVmScript in script) {
12
+ return script as VmScriptLike as VmScript;
13
+ }
14
+ defineProperty(script, kVmScript, { value: true });
15
+ if (typeof source === 'string') {
16
+ defineProperty(script, 'source', { value: source, configurable: true });
17
+ } else if (source instanceof Uint8Array) {
18
+ defineProperty(script, 'source', { value: '<buffer>', configurable: true });
19
+ }
20
+ return script as VmScript;
21
+ }
22
+
23
+ /** 生成 JS 函数 */
24
+ export function createScript(source: ScriptInput, code: string): VmScript {
25
+ let script;
26
+ try {
27
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval, @typescript-eslint/no-unsafe-call
28
+ script = new Function(...keys, code)(...values) as VmScriptLike;
29
+ /* c8 ignore next 3 */
30
+ } catch (error) {
31
+ throw new Error(`Failed to create script`, { cause: error });
32
+ }
33
+ return wrapScript(source, script);
34
+ }
@@ -0,0 +1,175 @@
1
+ import { DiagnosticCode, wasm } from '@mirascript/wasm';
2
+ import type { ScriptInput } from './types.js';
3
+ import { isSafeInteger } from '../helpers/utils.js';
4
+
5
+ export { DiagnosticCode };
6
+
7
+ const diagnosticMessages = new Map<DiagnosticCode, string | undefined>();
8
+ /** 获取 {@link DiagnosticCode} 对应的消息 */
9
+ export function getDiagnosticMessage(code: DiagnosticCode): string | undefined {
10
+ if (!isSafeInteger(code) || code < 0 || code >= 0xffff) {
11
+ throw new RangeError(`Invalid DiagnosticCode: ${code}`);
12
+ }
13
+ if (diagnosticMessages.has(code)) {
14
+ return diagnosticMessages.get(code);
15
+ }
16
+ const msg = wasm.get_diagnostic_message(code);
17
+ diagnosticMessages.set(code, msg);
18
+ return msg;
19
+ }
20
+
21
+ /**
22
+ * A range in the editor. This interface is suitable for serialization.
23
+ */
24
+ export interface IRange {
25
+ /**
26
+ * Line number on which the range starts (starts at 1).
27
+ */
28
+ readonly startLineNumber: number;
29
+ /**
30
+ * Column on which the range starts in line `startLineNumber` (starts at 1).
31
+ */
32
+ readonly startColumn: number;
33
+ /**
34
+ * Line number on which the range ends.
35
+ */
36
+ readonly endLineNumber: number;
37
+ /**
38
+ * Column on which the range ends in line `endLineNumber`.
39
+ */
40
+ readonly endColumn: number;
41
+ }
42
+
43
+ /** 源代码诊断信息 */
44
+ interface SourceDiagnosticBase<T extends DiagnosticCode = DiagnosticCode> {
45
+ /** 代码 */
46
+ readonly code: T;
47
+ /** 位置 */
48
+ readonly range: IRange;
49
+ }
50
+
51
+ /** 源代码诊断信息 */
52
+ export interface SourceDiagnostic<T extends DiagnosticCode = DiagnosticCode> extends SourceDiagnosticBase<T> {
53
+ /** 引用 */
54
+ readonly references: ReadonlyArray<SourceReference<T>>;
55
+ }
56
+ /** 源代码引用信息 */
57
+ export interface SourceReference<T extends DiagnosticCode = DiagnosticCode> extends SourceDiagnosticBase<T> {
58
+ /** 反向引用 */
59
+ readonly diagnostic: SourceDiagnostic<T>;
60
+ }
61
+
62
+ /** 分析诊断信息 */
63
+ export function parseDiagnostics(
64
+ source: ScriptInput,
65
+ diagnostics: Uint32Array,
66
+ ): {
67
+ errors: SourceDiagnostic[];
68
+ warnings: SourceDiagnostic[];
69
+ infos: SourceDiagnostic[];
70
+ hints: SourceDiagnostic[];
71
+ tags: SourceDiagnostic[];
72
+
73
+ references: SourceReference[];
74
+ tagsReferences: SourceReference[];
75
+ } {
76
+ const parsed = [];
77
+ const bufLen = diagnostics.length;
78
+ for (let i = 0; i < bufLen; i += 5) {
79
+ const startLineNumber = diagnostics[i]!;
80
+ const startColumn = diagnostics[i + 1]!;
81
+ const endLineNumber = diagnostics[i + 2]!;
82
+ const endColumn = diagnostics[i + 3]!;
83
+ const error = diagnostics[i + 4]! as DiagnosticCode;
84
+ parsed.push({
85
+ code: error,
86
+ range: {
87
+ startLineNumber,
88
+ startColumn,
89
+ endLineNumber,
90
+ endColumn,
91
+ },
92
+ } as Writable<SourceDiagnostic & SourceReference>);
93
+ }
94
+
95
+ const _errors: SourceDiagnostic[] = [];
96
+ const _warnings: SourceDiagnostic[] = [];
97
+ const _infos: SourceDiagnostic[] = [];
98
+ const _hints: SourceDiagnostic[] = [];
99
+ const _tags: SourceDiagnostic[] = [];
100
+ const _references: SourceReference[] = [];
101
+ const _tagsReferences: SourceReference[] = [];
102
+ for (let i = 0; i < parsed.length; i++) {
103
+ const diagnostic = parsed[i]!;
104
+ const { code } = diagnostic;
105
+ if (code > DiagnosticCode.ErrorStart && code < DiagnosticCode.ErrorEnd) {
106
+ _errors.push(diagnostic);
107
+ } else if (code > DiagnosticCode.WarningStart && code < DiagnosticCode.WarningEnd) {
108
+ _warnings.push(diagnostic);
109
+ } else if (code > DiagnosticCode.InfoStart && code < DiagnosticCode.InfoEnd) {
110
+ _infos.push(diagnostic);
111
+ } else if (code > DiagnosticCode.HintStart && code < DiagnosticCode.HintEnd) {
112
+ _hints.push(diagnostic);
113
+ } else if (code > DiagnosticCode.TagStart && code < DiagnosticCode.TagEnd) {
114
+ _tags.push(diagnostic);
115
+ } else {
116
+ continue;
117
+ }
118
+ diagnostic.references = [];
119
+ while (i + 1 < parsed.length) {
120
+ const ref = parsed[i + 1]!;
121
+ let isRef = false;
122
+ if (ref.code > DiagnosticCode.TagRefStart && ref.code < DiagnosticCode.TagRefEnd) {
123
+ isRef = true;
124
+ _tagsReferences.push(ref);
125
+ }
126
+ if (ref.code > DiagnosticCode.ReferenceStart && ref.code < DiagnosticCode.ReferenceEnd) {
127
+ isRef = true;
128
+ _references.push(ref);
129
+ }
130
+ if (!isRef) {
131
+ break;
132
+ }
133
+ i++;
134
+ ref.diagnostic = diagnostic;
135
+ (diagnostic.references as SourceReference[]).push(ref);
136
+ }
137
+ }
138
+ return {
139
+ errors: _errors,
140
+ warnings: _warnings,
141
+ infos: _infos,
142
+ hints: _hints,
143
+ tags: _tags,
144
+ references: _references,
145
+ tagsReferences: _tagsReferences,
146
+ };
147
+ }
148
+
149
+ /** 生成诊断 range 的字符串 */
150
+ function formatRange(range: {
151
+ startLineNumber: number;
152
+ startColumn: number;
153
+ endLineNumber: number;
154
+ endColumn: number;
155
+ }): string {
156
+ if (range.startLineNumber === range.endLineNumber) {
157
+ if (range.startColumn === range.endColumn) {
158
+ return `${range.startLineNumber}:${range.startColumn}`;
159
+ }
160
+ return `${range.startLineNumber}:${range.startColumn}-${range.endColumn}`;
161
+ }
162
+ return `${range.startLineNumber}:${range.startColumn}-${range.endLineNumber}:${range.endColumn}`;
163
+ }
164
+
165
+ /** 生成诊断消息的字符串 */
166
+ export function formatDiagnostic(diagnostic: SourceDiagnostic): string {
167
+ const range = formatRange(diagnostic.range);
168
+ const codeName = DiagnosticCode[diagnostic.code] || `Unknown(${diagnostic.code})`;
169
+ let message = getDiagnosticMessage(diagnostic.code);
170
+ for (const ref of diagnostic.references) {
171
+ const refRange = formatRange(ref.range);
172
+ message += `\n (${refRange}): ${getDiagnosticMessage(ref.code)}`;
173
+ }
174
+ return ` ${codeName}(${range}): ${message}`;
175
+ }