@mirascript/mirascript 0.1.43 → 0.1.45

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 (75) hide show
  1. package/dist/{chunk-DNM6YMFU.js → chunk-CZOIPBED.js} +331 -205
  2. package/dist/chunk-CZOIPBED.js.map +6 -0
  3. package/dist/{chunk-GBWG7BQZ.js → chunk-UI6VQ263.js} +116 -42
  4. package/dist/chunk-UI6VQ263.js.map +6 -0
  5. package/dist/compiler/diagnostic.d.ts +4 -0
  6. package/dist/compiler/diagnostic.d.ts.map +1 -1
  7. package/dist/compiler/emit/constants.d.ts +3 -0
  8. package/dist/compiler/emit/constants.d.ts.map +1 -1
  9. package/dist/compiler/emit/index.d.ts +6 -1
  10. package/dist/compiler/emit/index.d.ts.map +1 -1
  11. package/dist/compiler/emit/sourcemap.d.ts +2 -1
  12. package/dist/compiler/emit/sourcemap.d.ts.map +1 -1
  13. package/dist/compiler/worker.js +1 -1
  14. package/dist/index.js +2 -2
  15. package/dist/subtle.js +6 -2
  16. package/dist/subtle.js.map +1 -1
  17. package/dist/vm/lib/global/debug.d.ts +11 -35
  18. package/dist/vm/lib/global/debug.d.ts.map +1 -1
  19. package/dist/vm/lib/global/index.d.ts +1 -1
  20. package/dist/vm/lib/global/index.d.ts.map +1 -1
  21. package/dist/vm/lib/global/math/arr.d.ts +7 -0
  22. package/dist/vm/lib/global/math/arr.d.ts.map +1 -0
  23. package/dist/vm/lib/global/math/const.d.ts +31 -0
  24. package/dist/vm/lib/global/math/const.d.ts.map +1 -0
  25. package/dist/vm/lib/global/math/gamma.d.ts +3 -0
  26. package/dist/vm/lib/global/math/gamma.d.ts.map +1 -0
  27. package/dist/vm/lib/global/math/index.d.ts +8 -0
  28. package/dist/vm/lib/global/math/index.d.ts.map +1 -0
  29. package/dist/vm/lib/global/math/unary.d.ts +28 -0
  30. package/dist/vm/lib/global/math/unary.d.ts.map +1 -0
  31. package/dist/vm/lib/helpers.d.ts.map +1 -1
  32. package/dist/vm/lib/loader.d.ts.map +1 -1
  33. package/dist/vm/operations/common.d.ts +2 -2
  34. package/dist/vm/operations/common.d.ts.map +1 -1
  35. package/dist/vm/operations/helpers.d.ts +7 -2
  36. package/dist/vm/operations/helpers.d.ts.map +1 -1
  37. package/dist/vm/operations/type-check.d.ts +1 -1
  38. package/dist/vm/operations/type-check.d.ts.map +1 -1
  39. package/dist/vm/types/function.d.ts +2 -2
  40. package/dist/vm/types/function.d.ts.map +1 -1
  41. package/dist/vm/types/wrapper.d.ts +0 -2
  42. package/dist/vm/types/wrapper.d.ts.map +1 -1
  43. package/package.json +5 -5
  44. package/src/compiler/diagnostic.ts +56 -31
  45. package/src/compiler/emit/constants.ts +3 -0
  46. package/src/compiler/emit/index.ts +62 -5
  47. package/src/compiler/emit/sourcemap.ts +4 -3
  48. package/src/vm/lib/global/debug.ts +75 -61
  49. package/src/vm/lib/global/index.ts +1 -1
  50. package/src/vm/lib/global/{math-arr.ts → math/arr.ts} +2 -2
  51. package/src/vm/lib/global/{math-const.ts → math/const.ts} +1 -1
  52. package/src/vm/lib/global/math/gamma.ts +245 -0
  53. package/src/vm/lib/global/{math.ts → math/index.ts} +5 -5
  54. package/src/vm/lib/global/{math-unary.ts → math/unary.ts} +2 -2
  55. package/src/vm/lib/global/string.ts +1 -1
  56. package/src/vm/lib/helpers.ts +12 -10
  57. package/src/vm/lib/loader.ts +3 -13
  58. package/src/vm/operations/common.ts +2 -2
  59. package/src/vm/operations/helpers.ts +16 -3
  60. package/src/vm/operations/type-check.ts +1 -1
  61. package/src/vm/types/function.ts +19 -6
  62. package/src/vm/types/wrapper.ts +0 -4
  63. package/dist/chunk-DNM6YMFU.js.map +0 -6
  64. package/dist/chunk-GBWG7BQZ.js.map +0 -6
  65. package/dist/vm/lib/global/math-additional.d.ts +0 -2
  66. package/dist/vm/lib/global/math-additional.d.ts.map +0 -1
  67. package/dist/vm/lib/global/math-arr.d.ts +0 -7
  68. package/dist/vm/lib/global/math-arr.d.ts.map +0 -1
  69. package/dist/vm/lib/global/math-const.d.ts +0 -31
  70. package/dist/vm/lib/global/math-const.d.ts.map +0 -1
  71. package/dist/vm/lib/global/math-unary.d.ts +0 -28
  72. package/dist/vm/lib/global/math-unary.d.ts.map +0 -1
  73. package/dist/vm/lib/global/math.d.ts +0 -8
  74. package/dist/vm/lib/global/math.d.ts.map +0 -1
  75. package/src/vm/lib/global/math-additional.ts +0 -69
@@ -5,7 +5,7 @@ import type { ScriptInput, TranspileOptions } from '../types.js';
5
5
  import type { IRange } from '../diagnostic.js';
6
6
  import { readConsts, toJsLiteral } from './consts.js';
7
7
  import { createSourceMap } from './sourcemap.js';
8
- import { SCRIPT_PREFIX } from './constants.js';
8
+ import { SCRIPT_PREFIX, SCRIPT_PREFIX_NO_GLOBAL, type ScriptPrefix } from './constants.js';
9
9
 
10
10
  /** 生成代码 */
11
11
  export function emit(
@@ -77,6 +77,8 @@ export class Emitter {
77
77
  return ' '.repeat(this.identCounter + len);
78
78
  }
79
79
 
80
+ /** 是否使用全局变量 */
81
+ private useGlobal = false;
80
82
  /** 读取全局变量 */
81
83
  private rg(constIdx: number): string {
82
84
  const constName = this.constVals[constIdx]!;
@@ -86,6 +88,7 @@ export class Emitter {
86
88
  } else {
87
89
  lit = toJsLiteral(toString(constName, undefined));
88
90
  }
91
+ this.useGlobal = true;
89
92
  return `global.get(${lit})`;
90
93
  }
91
94
 
@@ -182,6 +185,45 @@ export class Emitter {
182
185
  return this.readBlockEnd(OpCode.IfEnd);
183
186
  }
184
187
 
188
+ /** 读取 module */
189
+ private readModule(obj: number): void {
190
+ this.identCounter++;
191
+ while (this.codeOffset < this.codeSize) {
192
+ const opcode_raw = this.codeReader.getUint8(this.codeOffset++);
193
+ const opcode = opcode_raw & 0x7f;
194
+ const wide = opcode_raw >= 0x80;
195
+ const read = () => this.readParam(wide);
196
+ let code = '';
197
+ switch (opcode) {
198
+ case OpCode.Field: {
199
+ const field = read();
200
+ const field_name = this.constLits[field];
201
+ /* c8 ignore next 3 */
202
+ if (!field_name) {
203
+ throw new Error(`Unknown field ${field}`);
204
+ }
205
+ const value = read();
206
+ code = `[${field_name}]: () => $Upvalue(${this.rv(value)}),`;
207
+ break;
208
+ }
209
+ case OpCode.Freeze: {
210
+ this.identCounter--;
211
+ code = `});`;
212
+ break;
213
+ }
214
+ default: {
215
+ code = `// ?${OpCode[opcode] ?? opcode}`;
216
+ break;
217
+ }
218
+ }
219
+ const ident = this.ident();
220
+ this.codeLines.push(ident + code);
221
+ if (opcode === OpCode.Freeze) {
222
+ return;
223
+ }
224
+ }
225
+ }
226
+
185
227
  /** 读取 record */
186
228
  private readRecord(obj: number): void {
187
229
  this.identCounter++;
@@ -337,7 +379,7 @@ export class Emitter {
337
379
  }
338
380
  const script = startFunc && !varg && argn === 0;
339
381
  if (script) {
340
- code = `${SCRIPT_PREFIX} var ${regs};`;
382
+ code = `var ${regs};`;
341
383
  } else {
342
384
  if (this.options.sourceMap) {
343
385
  this.functions.push(this.codeLines.length);
@@ -401,6 +443,7 @@ export class Emitter {
401
443
  case OpCode.InGlobal: {
402
444
  reg = read();
403
445
  const left = read();
446
+ this.useGlobal = true;
404
447
  code = `${this.wv(reg)} = global.has($ToString(${this.rv(left)}));`;
405
448
  break;
406
449
  }
@@ -540,6 +583,7 @@ export class Emitter {
540
583
  case OpCode.GetGlobalDyn: {
541
584
  reg = read();
542
585
  const name = read();
586
+ this.useGlobal = true;
543
587
  code = `${this.wv(reg)} = global.get($ToString(${this.rv(name)}));`;
544
588
  break;
545
589
  }
@@ -595,6 +639,13 @@ export class Emitter {
595
639
  code = `${this.wv(reg)} = $SliceExclusive(${this.rv(obj)}, ${this.rv(start)}, ${this.rv(end)});`;
596
640
  break;
597
641
  }
642
+ case OpCode.Module: {
643
+ reg = read();
644
+ const nameIdx = read();
645
+ const name = this.constLits[nameIdx];
646
+ code = `${this.wv(reg)} = $Module(${name}, {`;
647
+ break;
648
+ }
598
649
  case OpCode.Record: {
599
650
  reg = read();
600
651
  code = `${this.wv(reg)} = ({`;
@@ -704,6 +755,10 @@ export class Emitter {
704
755
  this.readBlockEnd(OpCode.LoopEnd);
705
756
  break;
706
757
  }
758
+ case OpCode.Module: {
759
+ this.readModule(reg);
760
+ break;
761
+ }
707
762
  case OpCode.Record: {
708
763
  this.readRecord(reg);
709
764
  break;
@@ -719,12 +774,14 @@ export class Emitter {
719
774
  read(): void {
720
775
  this.readConsts();
721
776
  this.readCode();
722
- this.addSourceMap();
777
+ const prefix: ScriptPrefix = this.useGlobal ? SCRIPT_PREFIX : SCRIPT_PREFIX_NO_GLOBAL;
778
+ this.codeLines[0] = `${prefix} ${this.codeLines[0]}`;
779
+ this.addSourceMap(prefix);
723
780
  }
724
781
  /** 添加源映射 */
725
- addSourceMap(): void {
782
+ addSourceMap(prefix: ScriptPrefix): void {
726
783
  if (this.options.sourceMap) {
727
- createSourceMap(this.source, this.sourcemaps, this.codeLines, this.functions, this.options);
784
+ createSourceMap(this.source, prefix, this.sourcemaps, this.codeLines, this.functions, this.options);
728
785
  }
729
786
  }
730
787
  }
@@ -1,7 +1,7 @@
1
+ import { SourceMapGenerator } from 'source-map-js';
1
2
  import type { IRange } from '../diagnostic.js';
2
3
  import type { ScriptInput, TranspileOptions } from '../types.js';
3
- import { SourceMapGenerator } from 'source-map-js';
4
- import { SCRIPT_PREFIX } from './constants.js';
4
+ import type { ScriptPrefix } from './constants.js';
5
5
  import { toJsLiteral } from './consts.js';
6
6
 
7
7
  const ORIGIN = `mira://MiraScript/`;
@@ -38,6 +38,7 @@ let sourceId = 1;
38
38
  /** 创建源映射 */
39
39
  export function createSourceMap(
40
40
  source: ScriptInput | undefined,
41
+ scriptPrefix: ScriptPrefix,
41
42
  sourcemaps: readonly IRange[],
42
43
  codeLines: string[],
43
44
  functions: readonly number[],
@@ -82,7 +83,7 @@ export function createSourceMap(
82
83
  map.addMapping({
83
84
  generated: {
84
85
  line: SOURCE_OFFSET,
85
- column: SCRIPT_PREFIX.length - 'CpEnter();'.length,
86
+ column: scriptPrefix.length - 'CpEnter();'.length,
86
87
  },
87
88
  original: {
88
89
  line: 1,
@@ -62,18 +62,24 @@ function buildFormatString(
62
62
  }
63
63
  }
64
64
 
65
- /** 默认的格式化函数 */
66
- const printFormatter: PrintOptions['formatter'] = function (args) {
65
+ /** 默认解析函数 */
66
+ const printParser: PrintOptions['parser'] = function (args) {
67
67
  const [format, values] = buildFormatString(this, args);
68
68
  const templates: string[] = [];
69
69
  const formats: SerializeFormat[] = [];
70
70
  let valIndex = 0;
71
71
  if (format.includes('%')) {
72
72
  const parts = format.split(/(%[%\w])/g);
73
+ let prevIsTemplate = true;
73
74
  for (let i = 0; i < parts.length; i++) {
74
75
  if (i % 2 === 0) {
75
76
  // Regular string part
76
- templates.push(parts[i]!);
77
+ if (prevIsTemplate && templates.length) {
78
+ templates[templates.length - 1] += parts[i]!;
79
+ } else {
80
+ templates.push(parts[i]!);
81
+ }
82
+ prevIsTemplate = true;
77
83
  continue;
78
84
  }
79
85
  // Specifier part
@@ -85,8 +91,10 @@ const printFormatter: PrintOptions['formatter'] = function (args) {
85
91
  } else {
86
92
  templates[templates.length - 1] += specifier;
87
93
  }
94
+ prevIsTemplate = true;
88
95
  continue;
89
96
  }
97
+ prevIsTemplate = false;
90
98
  formats.push(specifier as SerializeFormat);
91
99
  valIndex++;
92
100
  }
@@ -113,69 +121,82 @@ const printFormatter: PrintOptions['formatter'] = function (args) {
113
121
  };
114
122
  };
115
123
 
116
- /** 默认的输出函数 */
117
- const createPrinter = (consoleMethod: (...args: unknown[]) => void): PrintOptions['printer'] => {
118
- return function ({ templates, formats, values }: FormatResult) {
119
- let format = '';
120
- const formattedValues: unknown[] = [];
121
- let needAwait = false;
122
- for (let i = 0; i < templates.length; i++) {
123
- format += templates[i]!;
124
- if (i < values.length) {
125
- const f = formats[i] ?? '';
126
- const v = values[i]!;
127
- const serialized = serializeValue(this, v, f);
128
- if (serialized == null) {
129
- format += f || (typeof v == 'string' ? '%s' : '%o');
130
- formattedValues.push(v);
131
- } else if (typeof serialized == 'string') {
132
- format += '%s';
133
- formattedValues.push(serialized);
134
- } else {
135
- needAwait = true;
136
- format += '%s';
137
- formattedValues.push(serialized);
138
- }
124
+ /** 默认格式化函数 */
125
+ const printFormatter: PrintOptions['formatter'] = function ({
126
+ templates,
127
+ formats,
128
+ values,
129
+ }): unknown[] | PromiseLike<unknown[]> {
130
+ let format = '';
131
+ const formattedValues: unknown[] = [];
132
+ let needAwait = false;
133
+ for (let i = 0; i < templates.length; i++) {
134
+ format += templates[i]!.replaceAll('%', '%%');
135
+ if (i < values.length) {
136
+ const f = formats[i] ?? '';
137
+ const v = values[i]!;
138
+ const serialized = serializeValue(this, v, f);
139
+ if (serialized == null) {
140
+ format += f || (typeof v == 'string' ? '%s' : '%o');
141
+ formattedValues.push(v);
142
+ } else if (typeof serialized == 'string') {
143
+ format += '%s';
144
+ formattedValues.push(serialized);
145
+ } else {
146
+ needAwait = true;
147
+ format += '%s';
148
+ formattedValues.push(serialized);
139
149
  }
140
150
  }
141
- if (!needAwait) {
142
- consoleMethod(format, ...formattedValues);
143
- } else {
144
- void Promise.all(formattedValues).then((resolvedValues) => {
145
- consoleMethod(format, ...resolvedValues);
146
- });
147
- }
151
+ }
152
+ if (!needAwait) {
153
+ return [format, ...formattedValues];
154
+ } else {
155
+ return Promise.all(formattedValues).then((resolvedValues) => {
156
+ return [format, ...resolvedValues];
157
+ });
158
+ }
159
+ };
160
+
161
+ /** 默认的输出选项 */
162
+ const createPrintOptions = (consoleMethod: (...args: unknown[]) => void): PrintOptions => {
163
+ return {
164
+ prefix: ['MiraScript'],
165
+ serializer: defaultSerializer,
166
+ parser: printParser,
167
+ formatter: printFormatter,
168
+ logger: consoleMethod,
148
169
  };
149
170
  };
150
171
 
151
172
  /** 打印输出选项 */
152
- interface PrintOptions {
173
+ type PrintOptions = {
153
174
  /** 输出时的固定前缀 */
154
- prefix: readonly [prefix: string, ...additional: readonly string[]];
175
+ prefix: readonly [prefix?: string, ...additional: readonly string[]];
155
176
  /**
156
177
  * 序列化函数
157
178
  * @param arg 要序列化的值
158
179
  * @param format 序列化格式
159
- * @returns 序列化后的字符串,或 null 表示直接将原值传递给控制台
180
+ * @returns 序列化后的字符串,或 null 表示直接将原值传递给 {@link printer}
160
181
  */
161
182
  serializer: (this: PrintOptions, arg: VmAny, format: SerializeFormat) => string | null | PromiseLike<string>;
162
- /** 格式化函数 */
163
- formatter: (this: PrintOptions, args: readonly VmAny[]) => FormatResult;
164
- /** 输出函数 */
165
- printer: (this: PrintOptions, format: FormatResult) => void;
166
- }
167
-
168
- const printOptions: PrintOptions = {
169
- prefix: ['MiraScript'] as readonly [prefix: string, ...additional: readonly string[]],
170
- serializer: defaultSerializer,
171
- formatter: printFormatter,
172
- printer: () => undefined,
183
+ /** 解析函数,解析输入并使用 {@link serializer} 序列化值,供 {@link formatter} 使用 */
184
+ parser: (this: PrintOptions, args: readonly VmAny[]) => FormatResult;
185
+ /** 格式化函数,进一步处理解析结果,供 {@link logger} 使用 */
186
+ formatter: (this: PrintOptions, format: FormatResult) => unknown[] | PromiseLike<unknown[]>;
187
+ /** 打印函数,打印格式化结果 */
188
+ logger: (this: PrintOptions, ...args: unknown[]) => void;
173
189
  };
174
190
 
191
+ /** 进行输出 */
192
+ function doPrint(opt: PrintOptions, args: readonly VmAny[]): void {
193
+ const formatResult = opt.parser(args);
194
+ void Promise.resolve(opt.formatter(formatResult)).then((printed) => opt.logger(...printed));
195
+ }
196
+
175
197
  export const debug_print = VmLib(
176
198
  (...args) => {
177
- const formatResult = debug_print.formatter(args);
178
- debug_print.printer(formatResult);
199
+ doPrint(debug_print, args);
179
200
  },
180
201
  {
181
202
  summary: '打印调试信息到控制台',
@@ -184,17 +205,13 @@ export const debug_print = VmLib(
184
205
  returnsType: 'nil',
185
206
  examples: ['debug_print("value:", 42);'],
186
207
  },
187
- {
188
- ...printOptions,
189
- // eslint-disable-next-line no-console
190
- printer: createPrinter(console.log),
191
- },
208
+ // eslint-disable-next-line no-console
209
+ createPrintOptions(console.log.bind(console)),
192
210
  );
193
211
 
194
212
  export const panic = VmLib(
195
213
  (message: VmAny) => {
196
- const formatResult = message === undefined ? panic.formatter([]) : panic.formatter([message]);
197
- panic.printer(formatResult);
214
+ doPrint(panic, message === undefined ? [] : [message]);
198
215
  const mgsStr = toString(message, null);
199
216
  const error = !mgsStr ? 'panic' : 'panic: ' + mgsStr;
200
217
  throw new VmError(error, undefined);
@@ -206,11 +223,8 @@ export const panic = VmLib(
206
223
  returnsType: 'never',
207
224
  examples: ['panic("boom");'],
208
225
  },
209
- {
210
- ...printOptions,
211
- // eslint-disable-next-line no-console
212
- printer: createPrinter(console.error),
213
- },
226
+ // eslint-disable-next-line no-console
227
+ createPrintOptions(console.error.bind(console)),
214
228
  );
215
229
 
216
230
  if (typeof location != 'undefined') {
@@ -1,4 +1,4 @@
1
- export * from './math.js';
1
+ export * from './math/index.js';
2
2
  export * from './bit.js';
3
3
  export * from './sequence/index.js';
4
4
  export * from './debug.js';
@@ -1,5 +1,5 @@
1
- import type { VmAny } from '../../types/index.js';
2
- import { getNumbers, VmLib } from '../helpers.js';
1
+ import type { VmAny } from '../../../types/index.js';
2
+ import { getNumbers, VmLib } from '../../helpers.js';
3
3
 
4
4
  /** 生成函数 */
5
5
  function build(f: (...values: readonly number[]) => number): (...values: readonly VmAny[]) => number {
@@ -1,5 +1,5 @@
1
1
  import { DiagnosticCode } from '@mirascript/constants';
2
- import { VmLib } from '../helpers.js';
2
+ import { VmLib } from '../../helpers.js';
3
3
 
4
4
  export const PI = VmLib(Math.PI, {
5
5
  summary: '圆周率',
@@ -0,0 +1,245 @@
1
+ /* eslint-disable no-loss-of-precision */
2
+
3
+ import { NotNumber, PositiveInfinity, isNaN } from '../../../../helpers/utils.js';
4
+ import { expectNumber, VmLib } from '../../helpers.js';
5
+ const { PI, exp, floor, cos, sin, pow, trunc } = Math;
6
+
7
+ const MEM = new DataView(new ArrayBuffer(8));
8
+ /**
9
+ * Reinterpret the bits of a 64-bit float as a BigInt (unsigned 64-bit integer).
10
+ * This mirrors Rust's `f64::to_bits()`.
11
+ */
12
+ function f64ToBits(value: number): bigint {
13
+ MEM.setFloat64(0, value, false);
14
+ return MEM.getBigUint64(0, false);
15
+ }
16
+
17
+ /**
18
+ * Reinterpret a BigInt (unsigned 64-bit integer) as a 64-bit float.
19
+ * This mirrors Rust's `f64::from_bits()`.
20
+ */
21
+ function f64FromBits(bits: bigint): number {
22
+ MEM.setBigUint64(0, bits, false);
23
+ return MEM.getFloat64(0, false);
24
+ }
25
+
26
+ // From https://crates.io/crates/libm
27
+
28
+ /*
29
+ "A Precision Approximation of the Gamma Function" - Cornelius Lanczos (1964)
30
+ "Lanczos Implementation of the Gamma Function" - Paul Godfrey (2001)
31
+ "An Analysis of the Lanczos Gamma Approximation" - Glendon Ralph Pugh (2004)
32
+
33
+ approximation method:
34
+
35
+ (x - 0.5) S(x)
36
+ Gamma(x) = (x + g - 0.5) * ----------------
37
+ exp(x + g - 0.5)
38
+
39
+ with
40
+ a1 a2 a3 aN
41
+ S(x) ~= [ a0 + ----- + ----- + ----- + ... + ----- ]
42
+ x + 1 x + 2 x + 3 x + N
43
+
44
+ with a0, a1, a2, a3,.. aN constants which depend on g.
45
+ for x < 0 the following reflection formula is used:
46
+
47
+ Gamma(x)*Gamma(-x) = -pi/(x sin(pi x))
48
+
49
+ most ideas and constants are from boost and python
50
+ */
51
+
52
+ /**
53
+ * sin(pi * x) with x > 0, if sin(pi*x)==0 the sign is arbitrary.
54
+ */
55
+ function sinpi(x: number): number {
56
+ /* argument reduction: x = |x| mod 2 */
57
+ /* spurious inexact when x is odd int */
58
+ x = x * 0.5;
59
+ x = 2 * (x - floor(x));
60
+
61
+ /* reduce x into [-.25,.25] */
62
+ let n = trunc(4 * x); // truncate to integer
63
+ n = trunc((n + 1) / 2); // integer division
64
+ x -= n * 0.5;
65
+
66
+ x *= PI;
67
+
68
+ switch (n) {
69
+ case 1:
70
+ return cos(x);
71
+ case 2:
72
+ return sin(-x);
73
+ case 3:
74
+ return -cos(x);
75
+ default:
76
+ // 0
77
+ return sin(x);
78
+ }
79
+ }
80
+
81
+ const N = 12;
82
+
83
+ // static const double g = 6.024680040776729583740234375;
84
+ const GMHALF = 5.524_680_040_776_729_583_740_234_375 as const;
85
+
86
+ const SNUM = [
87
+ 23_531_376_880.410_759_688_572_007_674_451_636_754_734_846_804_94,
88
+ 42_919_803_642.649_098_768_957_899_047_001_988_850_926_355_848_959,
89
+ 35_711_959_237.355_668_049_440_185_451_547_166_705_960_488_635_843,
90
+ 17_921_034_426.037_209_699_919_755_754_458_931_112_671_403_265_39,
91
+ 6_039_542_586.352_028_005_064_291_644_307_297_921_069_938_842_070_8,
92
+ 1_439_720_407.311_721_673_663_223_072_794_912_393_971_548_578_677_2,
93
+ 248_874_557.862_054_156_511_460_386_413_229_423_216_321_251_278_01,
94
+ 31_426_415.585_400_194_380_614_231_628_318_205_362_874_684_987_64,
95
+ 2_876_370.628_935_372_441_225_409_051_620_849_613_599_114_537_876_8,
96
+ 186_056.265_395_223_495_040_294_989_716_045_699_282_207_842_363_28,
97
+ 8071.672_002_365_816_210_638_002_902_272_250_613_821_851_632_502_4,
98
+ 210.824_277_751_579_345_872_509_733_920_713_362_711_669_695_802_91,
99
+ 2.506_628_274_631_000_270_164_908_177_133_837_338_626_431_079_340_8,
100
+ ] as const;
101
+
102
+ const SDEN = [
103
+ 0, 39_916_800, 120_543_840, 150_917_976, 105_258_076, 45_995_730, 13_339_535, 2_637_558, 357_423, 32670, 1925, 66,
104
+ 1,
105
+ ] as const;
106
+
107
+ /**
108
+ * S(x) rational function for positive x.
109
+ */
110
+ function s(x: number): number {
111
+ let num = 0;
112
+ let den = 0;
113
+
114
+ /* to avoid overflow handle large x differently */
115
+ if (x < 8) {
116
+ for (let i = N; i >= 0; i--) {
117
+ num = num * x + SNUM[i]!;
118
+ den = den * x + SDEN[i]!;
119
+ }
120
+ } else {
121
+ for (let i = 0; i <= N; i++) {
122
+ num = num / x + SNUM[i]!;
123
+ den = den / x + SDEN[i]!;
124
+ }
125
+ }
126
+
127
+ return num / den;
128
+ }
129
+
130
+ /**
131
+ * The [Gamma function](https://en.wikipedia.org/wiki/Gamma_function) (f64).
132
+ */
133
+ function tgamma(x: number): number {
134
+ const u = f64ToBits(x);
135
+ const ix = Number((u >> 32n) & 0x7fff_ffffn);
136
+ const sign = u >> 63n !== 0n;
137
+
138
+ /* special cases */
139
+ if (ix >= 0x7ff0_0000) {
140
+ /* tgamma(nan)=nan, tgamma(inf)=inf, tgamma(-inf)=nan with invalid */
141
+ return x + PositiveInfinity;
142
+ }
143
+
144
+ if (ix < (0x3ff - 54) << 20) {
145
+ /* |x| < 2^-54: tgamma(x) ~ 1/x, +-0 raises div-by-zero */
146
+ return 1 / x;
147
+ }
148
+
149
+ /* integer arguments */
150
+ /* raise inexact when non-integer */
151
+ if (x === floor(x)) {
152
+ if (sign) {
153
+ return NotNumber;
154
+ }
155
+ if (x <= FACT_MAX) {
156
+ return fact(trunc(x) - 1);
157
+ }
158
+ }
159
+
160
+ /* x >= 172: tgamma(x)=inf with overflow */
161
+ /* x =< -184: tgamma(x)=+-0 with underflow */
162
+ if (ix >= 0x4067_0000) {
163
+ /* |x| >= 184 */
164
+ if (sign) {
165
+ if (floor(x) * 0.5 === floor(x * 0.5)) {
166
+ return 0;
167
+ } else {
168
+ return -0;
169
+ }
170
+ }
171
+ const x1p1023 = f64FromBits(0x7fe0_0000_0000_0000n); // 2^1023
172
+ return x * x1p1023; // overflow to Infinity
173
+ }
174
+
175
+ const absx = sign ? -x : x;
176
+
177
+ /* handle the error of x + g - 0.5 */
178
+ const y = absx + GMHALF;
179
+ let dy: number;
180
+ if (absx > GMHALF) {
181
+ dy = y - absx;
182
+ dy -= GMHALF;
183
+ } else {
184
+ dy = y - GMHALF;
185
+ dy -= absx;
186
+ }
187
+
188
+ let z = absx - 0.5;
189
+ let r = s(absx) * exp(-y);
190
+
191
+ if (x < 0) {
192
+ /* reflection formula for negative x */
193
+ /* sinpi(absx) is not 0, integers are already handled */
194
+ r = -PI / (sinpi(absx) * absx * r);
195
+ dy = -dy;
196
+ z = -z;
197
+ }
198
+
199
+ r += (dy * (GMHALF + 0.5) * r) / y;
200
+ const zHalf = pow(y, 0.5 * z);
201
+ return r * zHalf * zHalf;
202
+ }
203
+
204
+ export const gamma = VmLib(
205
+ (x) => {
206
+ const n = expectNumber('x', x);
207
+ return tgamma(n);
208
+ },
209
+ {
210
+ summary: '返回 Gamma 函数的值',
211
+ params: { x: '要计算 Gamma 函数的数值' },
212
+ paramsType: { x: 'number' },
213
+ returnsType: 'number',
214
+ examples: ['gamma(5) // 24'],
215
+ },
216
+ );
217
+
218
+ const FACT_MAX = 171;
219
+ const FACT: Array<number | undefined> = [1, 1];
220
+ /** 计算整数的阶乘 */
221
+ function fact(n: number): number {
222
+ const cached = FACT[n];
223
+ if (cached != null) return cached;
224
+ let r = 1n;
225
+ for (let i = 2; i <= n; i++) {
226
+ r *= BigInt(i);
227
+ FACT[i] = Number(r);
228
+ }
229
+ return FACT[n]!;
230
+ }
231
+
232
+ export const factorial = VmLib(
233
+ (x): number => {
234
+ const n = expectNumber('x', x);
235
+ if (isNaN(n) || n < 0) return NotNumber;
236
+ return tgamma(n + 1);
237
+ },
238
+ {
239
+ summary: '返回一个数的阶乘',
240
+ params: { x: '要计算阶乘的数值' },
241
+ paramsType: { x: 'number' },
242
+ returnsType: 'number',
243
+ examples: ['factorial(5) // 120'],
244
+ },
245
+ );
@@ -1,4 +1,4 @@
1
- import { expectNumber, VmLib } from '../helpers.js';
1
+ import { expectNumber, VmLib } from '../../helpers.js';
2
2
  const { atan2: _atan2, pow: _pow, random: _random } = Math;
3
3
 
4
4
  export const atan2 = VmLib((x, y) => _atan2(expectNumber(0, x), expectNumber(1, y)), {
@@ -20,7 +20,7 @@ export const random = VmLib(() => _random(), {
20
20
  returnsType: 'number',
21
21
  });
22
22
 
23
- export * from './math-arr.js';
24
- export * from './math-const.js';
25
- export * from './math-unary.js';
26
- export * from './math-additional.js';
23
+ export * from './arr.js';
24
+ export * from './const.js';
25
+ export * from './unary.js';
26
+ export { gamma, factorial } from './gamma.js';
@@ -1,5 +1,5 @@
1
- import type { VmAny } from '../../index.js';
2
- import { expectNumber, VmLib } from '../helpers.js';
1
+ import type { VmAny } from '../../../index.js';
2
+ import { expectNumber, VmLib } from '../../helpers.js';
3
3
 
4
4
  /** 生成函数 */
5
5
  function build(f: (x: number) => number): (x: VmAny) => number {
@@ -2,7 +2,7 @@ import { expectArray, expectString, VmLib } from '../helpers.js';
2
2
 
3
3
  export const chars = VmLib(
4
4
  (str) => {
5
- return [...expectString('str', str)];
5
+ return Array.from(expectString('str', str));
6
6
  },
7
7
  {
8
8
  summary: '将字符串转换为字符数组',