@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.
- package/dist/{chunk-DNM6YMFU.js → chunk-CZOIPBED.js} +331 -205
- package/dist/chunk-CZOIPBED.js.map +6 -0
- package/dist/{chunk-GBWG7BQZ.js → chunk-UI6VQ263.js} +116 -42
- package/dist/chunk-UI6VQ263.js.map +6 -0
- package/dist/compiler/diagnostic.d.ts +4 -0
- package/dist/compiler/diagnostic.d.ts.map +1 -1
- package/dist/compiler/emit/constants.d.ts +3 -0
- package/dist/compiler/emit/constants.d.ts.map +1 -1
- package/dist/compiler/emit/index.d.ts +6 -1
- package/dist/compiler/emit/index.d.ts.map +1 -1
- package/dist/compiler/emit/sourcemap.d.ts +2 -1
- package/dist/compiler/emit/sourcemap.d.ts.map +1 -1
- package/dist/compiler/worker.js +1 -1
- package/dist/index.js +2 -2
- package/dist/subtle.js +6 -2
- package/dist/subtle.js.map +1 -1
- package/dist/vm/lib/global/debug.d.ts +11 -35
- package/dist/vm/lib/global/debug.d.ts.map +1 -1
- package/dist/vm/lib/global/index.d.ts +1 -1
- package/dist/vm/lib/global/index.d.ts.map +1 -1
- package/dist/vm/lib/global/math/arr.d.ts +7 -0
- package/dist/vm/lib/global/math/arr.d.ts.map +1 -0
- package/dist/vm/lib/global/math/const.d.ts +31 -0
- package/dist/vm/lib/global/math/const.d.ts.map +1 -0
- package/dist/vm/lib/global/math/gamma.d.ts +3 -0
- package/dist/vm/lib/global/math/gamma.d.ts.map +1 -0
- package/dist/vm/lib/global/math/index.d.ts +8 -0
- package/dist/vm/lib/global/math/index.d.ts.map +1 -0
- package/dist/vm/lib/global/math/unary.d.ts +28 -0
- package/dist/vm/lib/global/math/unary.d.ts.map +1 -0
- package/dist/vm/lib/helpers.d.ts.map +1 -1
- package/dist/vm/lib/loader.d.ts.map +1 -1
- package/dist/vm/operations/common.d.ts +2 -2
- package/dist/vm/operations/common.d.ts.map +1 -1
- package/dist/vm/operations/helpers.d.ts +7 -2
- package/dist/vm/operations/helpers.d.ts.map +1 -1
- package/dist/vm/operations/type-check.d.ts +1 -1
- package/dist/vm/operations/type-check.d.ts.map +1 -1
- package/dist/vm/types/function.d.ts +2 -2
- package/dist/vm/types/function.d.ts.map +1 -1
- package/dist/vm/types/wrapper.d.ts +0 -2
- package/dist/vm/types/wrapper.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/compiler/diagnostic.ts +56 -31
- package/src/compiler/emit/constants.ts +3 -0
- package/src/compiler/emit/index.ts +62 -5
- package/src/compiler/emit/sourcemap.ts +4 -3
- package/src/vm/lib/global/debug.ts +75 -61
- package/src/vm/lib/global/index.ts +1 -1
- package/src/vm/lib/global/{math-arr.ts → math/arr.ts} +2 -2
- package/src/vm/lib/global/{math-const.ts → math/const.ts} +1 -1
- package/src/vm/lib/global/math/gamma.ts +245 -0
- package/src/vm/lib/global/{math.ts → math/index.ts} +5 -5
- package/src/vm/lib/global/{math-unary.ts → math/unary.ts} +2 -2
- package/src/vm/lib/global/string.ts +1 -1
- package/src/vm/lib/helpers.ts +12 -10
- package/src/vm/lib/loader.ts +3 -13
- package/src/vm/operations/common.ts +2 -2
- package/src/vm/operations/helpers.ts +16 -3
- package/src/vm/operations/type-check.ts +1 -1
- package/src/vm/types/function.ts +19 -6
- package/src/vm/types/wrapper.ts +0 -4
- package/dist/chunk-DNM6YMFU.js.map +0 -6
- package/dist/chunk-GBWG7BQZ.js.map +0 -6
- package/dist/vm/lib/global/math-additional.d.ts +0 -2
- package/dist/vm/lib/global/math-additional.d.ts.map +0 -1
- package/dist/vm/lib/global/math-arr.d.ts +0 -7
- package/dist/vm/lib/global/math-arr.d.ts.map +0 -1
- package/dist/vm/lib/global/math-const.d.ts +0 -31
- package/dist/vm/lib/global/math-const.d.ts.map +0 -1
- package/dist/vm/lib/global/math-unary.d.ts +0 -28
- package/dist/vm/lib/global/math-unary.d.ts.map +0 -1
- package/dist/vm/lib/global/math.d.ts +0 -8
- package/dist/vm/lib/global/math.d.ts.map +0 -1
- 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 =
|
|
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.
|
|
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 {
|
|
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:
|
|
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
|
|
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.
|
|
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
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
|
|
173
|
+
type PrintOptions = {
|
|
153
174
|
/** 输出时的固定前缀 */
|
|
154
|
-
prefix: readonly [prefix
|
|
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
|
-
|
|
164
|
-
/**
|
|
165
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,5 +1,5 @@
|
|
|
1
|
-
import type { VmAny } from '
|
|
2
|
-
import { getNumbers, VmLib } from '
|
|
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 {
|
|
@@ -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 '
|
|
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 './
|
|
24
|
-
export * from './
|
|
25
|
-
export * from './
|
|
26
|
-
export
|
|
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 '
|
|
2
|
-
import { expectNumber, VmLib } from '
|
|
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 {
|