pure-dango 1.8.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.
- package/LICENSE.md +21 -0
- package/package.json +45 -0
- package/src/core/compiler.ts +1269 -0
- package/src/core/interpreter.ts +2042 -0
- package/src/core/parser/handlers.ts +1091 -0
- package/src/core/parser/helpers.ts +157 -0
- package/src/core/parser/main.ts +620 -0
- package/src/core/tokenizer/funnies.ts +43 -0
- package/src/core/tokenizer/tokenizer.ts +334 -0
- package/src/core/utils.ts +534 -0
- package/src/index.ts +190 -0
- package/src/runtime/errors.ts +239 -0
- package/src/runtime/globals.ts +11 -0
- package/src/runtime/libs/ai.pds +56 -0
- package/src/runtime/libs/errors.pds +74 -0
- package/src/runtime/libs/gambling.pds +93 -0
- package/src/runtime/libs/io.pds +371 -0
- package/src/runtime/libs/math.pds +86 -0
- package/src/runtime/libs/std.pds +2 -0
- package/src/runtime/libs/types.pds +1232 -0
- package/src/runtime/stdlib.ts +1483 -0
|
@@ -0,0 +1,1483 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import util from "util";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import readline from "readline";
|
|
5
|
+
import {performance} from "perf_hooks";
|
|
6
|
+
|
|
7
|
+
import
|
|
8
|
+
{
|
|
9
|
+
maxArguments,
|
|
10
|
+
joinStrings,
|
|
11
|
+
bigIntPow,
|
|
12
|
+
interpretEscapeCharacters,
|
|
13
|
+
FON
|
|
14
|
+
} from "../core/utils"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
import
|
|
18
|
+
{
|
|
19
|
+
getBaseDir,
|
|
20
|
+
GF,
|
|
21
|
+
isGFloat,
|
|
22
|
+
gmpPrecision,
|
|
23
|
+
setPrecision,
|
|
24
|
+
isGWrapper,
|
|
25
|
+
GFloat,
|
|
26
|
+
BigIntToGFloat,
|
|
27
|
+
BigIntDivToGFloat,
|
|
28
|
+
gmpInstance,
|
|
29
|
+
executeInCurrentContext
|
|
30
|
+
} from "../core/interpreter";
|
|
31
|
+
|
|
32
|
+
import {precisionToBits} from "gmp-wasm";
|
|
33
|
+
|
|
34
|
+
type Stack = number[] | string[] | null[];
|
|
35
|
+
|
|
36
|
+
export function errorTemplate(name : string, message : string, suggestion? : string) : never
|
|
37
|
+
{
|
|
38
|
+
const body = suggestion ? `${message}, ${suggestion}` : message;
|
|
39
|
+
const formatted = name
|
|
40
|
+
? `[ERROR] ${name}:\n${body}`
|
|
41
|
+
: `[ERROR] ${body}`;
|
|
42
|
+
|
|
43
|
+
const err = new Error(formatted);
|
|
44
|
+
err.name = "";
|
|
45
|
+
throw err;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function applyRounding(value : any, mode : string) : any
|
|
49
|
+
{
|
|
50
|
+
switch (mode)
|
|
51
|
+
{
|
|
52
|
+
case "down" : return value.floor();
|
|
53
|
+
case "up" : return value.ceil();
|
|
54
|
+
case "half_even" : return value.round();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function toFloat(value: any): GFloat
|
|
59
|
+
{
|
|
60
|
+
if (isGFloat(value))
|
|
61
|
+
return value;
|
|
62
|
+
|
|
63
|
+
if (typeof value === "bigint")
|
|
64
|
+
return BigIntToGFloat(value);
|
|
65
|
+
if (typeof value === "number")
|
|
66
|
+
return GF(value.toString());
|
|
67
|
+
if (typeof value === "string" && !isNaN(Number(value)))
|
|
68
|
+
return GF(value);
|
|
69
|
+
|
|
70
|
+
errorTemplate("toFloat", `Cannot convert "${value}" to a Float`, "ensure the value is a BigInt, Float, or numeric string before passing it");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function toBigInt(value: any)
|
|
74
|
+
{
|
|
75
|
+
if (typeof value === "bigint")
|
|
76
|
+
return value;
|
|
77
|
+
|
|
78
|
+
if (isGFloat(value))
|
|
79
|
+
return BigInt(value.inner.toFixed());
|
|
80
|
+
if (typeof value === "number")
|
|
81
|
+
return BigInt(value.toString());
|
|
82
|
+
if (typeof value === "string" && !isNaN(Number(value)))
|
|
83
|
+
return BigInt(value);
|
|
84
|
+
|
|
85
|
+
errorTemplate("toBigInt", `Cannot convert "${value}" to a BigInt`, "ensure the value is a BigInt, Float, or numeric string before passing it");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function simpleRounding(normalFunc: (value: number) => any, GMPFunc: (value: any) => any, args: any[], name: string) : any
|
|
89
|
+
{
|
|
90
|
+
maxArguments(1, args, name);
|
|
91
|
+
|
|
92
|
+
const value = args[0];
|
|
93
|
+
if (!(isGFloat(value) || typeof value === "bigint"))
|
|
94
|
+
errorTemplate(name, `value must be Float or BigInt`, "ensure the value is a BigInt or Float before passing it");
|
|
95
|
+
|
|
96
|
+
const result = typeof value === "bigint"
|
|
97
|
+
? normalFunc(Number(value))
|
|
98
|
+
: GMPFunc(toFloat(value));
|
|
99
|
+
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export const syncFunctions =
|
|
104
|
+
{
|
|
105
|
+
"now" : () : number =>
|
|
106
|
+
{
|
|
107
|
+
return performance.now();
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
"out": (stack: Stack, getTrueValue: Function, ...args: any[]) : void =>
|
|
111
|
+
{
|
|
112
|
+
const map = args.map(item => isGFloat(item)
|
|
113
|
+
? util.inspect(item.inner.toFixed(), {colors: true})
|
|
114
|
+
: util.inspect(item, {colors: true})
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
const joined : string = joinStrings(map);
|
|
118
|
+
const escaped : string = interpretEscapeCharacters(joined);
|
|
119
|
+
process.stdout.write(escaped);
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
"print": (stack: Stack, getTrueValue: Function, ...args: any[]) : void =>
|
|
123
|
+
{
|
|
124
|
+
const map = args.map(item => isGFloat(item) ? item.inner.toFixed() : item);
|
|
125
|
+
const joined = joinStrings(map);
|
|
126
|
+
const escaped = interpretEscapeCharacters(joined);
|
|
127
|
+
console.log(escaped);
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
"concat": (stack: Stack, getTrueValue: Function, ...args: any[]) : string =>
|
|
131
|
+
{
|
|
132
|
+
return joinStrings(args.map(x => isGFloat(x) ? x.inner.toString() : x));
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
"dir": (stack: Stack, getTrueValue: Function, ...args: any[]) : void =>
|
|
136
|
+
{
|
|
137
|
+
maxArguments(2, args, "dir");
|
|
138
|
+
const value : any = FON(args[0]);
|
|
139
|
+
const rawOptions : any = args[1];
|
|
140
|
+
|
|
141
|
+
const options : Record<string, any> = rawOptions?.value ?? {};
|
|
142
|
+
|
|
143
|
+
const depth : number | null = options["depth"] ?? null;
|
|
144
|
+
const colors : boolean = options["colors"] ?? true;
|
|
145
|
+
|
|
146
|
+
console.dir(value, {depth, colors});
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
"root": (stack: Stack, getTrueValue: any, ...args: any[]) : any =>
|
|
150
|
+
{
|
|
151
|
+
maxArguments(3, args, "root");
|
|
152
|
+
const rawBase: any = args[0];
|
|
153
|
+
if (rawBase === undefined)
|
|
154
|
+
errorTemplate("root", `base parameter is undefined`);
|
|
155
|
+
|
|
156
|
+
const rawRoot: any = args[1];
|
|
157
|
+
if (rawRoot === undefined)
|
|
158
|
+
errorTemplate("root", `root parameter is undefined`);
|
|
159
|
+
if (GF(rawRoot).inner.isEqual(2))
|
|
160
|
+
return syncFunctions.sqrt(stack, getTrueValue, rawBase);
|
|
161
|
+
|
|
162
|
+
const base = GF(rawBase);
|
|
163
|
+
const root = GF(rawRoot);
|
|
164
|
+
|
|
165
|
+
if (base.inner.isEqual(GF("1").inner))
|
|
166
|
+
return GF("1");
|
|
167
|
+
if (root.inner.isEqual(GF("1").inner))
|
|
168
|
+
return base;
|
|
169
|
+
if (root.inner.isEqual(GF("0").inner))
|
|
170
|
+
errorTemplate("root", `root parameter cannot be 0`);
|
|
171
|
+
|
|
172
|
+
if (base.inner.isEqual(GF("0").inner) && root.inner.lessThan(GF("0").inner))
|
|
173
|
+
errorTemplate("root", `0 cannot have a negative root`);
|
|
174
|
+
|
|
175
|
+
// x^(1/n)
|
|
176
|
+
const reciprocal = GF("1").inner.div(root.inner);
|
|
177
|
+
return new GFloat(base.inner.pow(reciprocal));
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
"sqrt": (stack: Stack, getTrueValue: Function, ...args: any[]) : any =>
|
|
181
|
+
{
|
|
182
|
+
maxArguments(1, args, "sqrt");
|
|
183
|
+
return new GFloat(toFloat(args[0]).inner.sqrt());
|
|
184
|
+
},
|
|
185
|
+
|
|
186
|
+
"raise": (stack: Stack, getTrueValue: Function, ...args: any[]) : any =>
|
|
187
|
+
{
|
|
188
|
+
maxArguments(3, args, "raise");
|
|
189
|
+
let base : any = getTrueValue(args[0]);
|
|
190
|
+
if (base === undefined)
|
|
191
|
+
errorTemplate("raise", `base parameter is undefined`);
|
|
192
|
+
|
|
193
|
+
let exponent : any = getTrueValue(args[1]);
|
|
194
|
+
if (exponent === undefined)
|
|
195
|
+
errorTemplate("raise", `exponent parameter is undefined`);
|
|
196
|
+
|
|
197
|
+
const useNativeMath = !!args[2];
|
|
198
|
+
|
|
199
|
+
// early returns
|
|
200
|
+
if (base === 1n)
|
|
201
|
+
return 1n;
|
|
202
|
+
if (base === 0n && exponent === 0n)
|
|
203
|
+
errorTemplate("raise", `0 raised to 0 cannot be evaluated`);
|
|
204
|
+
else if (base === 0n)
|
|
205
|
+
return 0n;
|
|
206
|
+
|
|
207
|
+
if (typeof base === "bigint" || typeof exponent === "bigint")
|
|
208
|
+
{
|
|
209
|
+
if (typeof base !== "bigint")
|
|
210
|
+
base = BigInt(base);
|
|
211
|
+
if (typeof exponent !== "bigint")
|
|
212
|
+
exponent = BigInt(exponent);
|
|
213
|
+
|
|
214
|
+
if (useNativeMath)
|
|
215
|
+
return Math.pow(Number(base), Number(exponent));
|
|
216
|
+
else
|
|
217
|
+
return bigIntPow(base, exponent);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const GMPBase = toFloat(base);
|
|
221
|
+
const GMPExponent = toFloat(exponent);
|
|
222
|
+
const result = new GFloat(GMPBase.inner.pow(GMPExponent.inner));
|
|
223
|
+
return useNativeMath
|
|
224
|
+
? BigInt(result.inner.toNumber())
|
|
225
|
+
: result;
|
|
226
|
+
},
|
|
227
|
+
|
|
228
|
+
"strtoFixed": (stack: Stack, getTrueValue: Function, ...args: any[]): string =>
|
|
229
|
+
{
|
|
230
|
+
maxArguments(3, args, "strtoFixed");
|
|
231
|
+
const x : any = args[0];
|
|
232
|
+
if (!isGFloat(x))
|
|
233
|
+
errorTemplate("strtoFixed", `x parameter must be type Float, got "${x}"`, `maybe you forgot to call tofloat()?`);
|
|
234
|
+
|
|
235
|
+
const digits : number = isGFloat(args[1]) ? args[1].inner.toNumber() : Number(args[1]);
|
|
236
|
+
const mode : string | undefined = args[2];
|
|
237
|
+
|
|
238
|
+
if (mode)
|
|
239
|
+
return applyRounding(x, mode).toFixed(digits);
|
|
240
|
+
|
|
241
|
+
const binding = gmpInstance.binding;
|
|
242
|
+
|
|
243
|
+
// pointers
|
|
244
|
+
const expPointer = binding.malloc(8);
|
|
245
|
+
const strPointer = binding.mpfr_get_str(0, expPointer, 10, digits + 1, x.inner.mpfr_t, 0);
|
|
246
|
+
|
|
247
|
+
// read null-terminated string from wasm memory
|
|
248
|
+
const mem = new Uint8Array(binding.mem.buffer);
|
|
249
|
+
let str = "", i = strPointer;
|
|
250
|
+
while (mem[i] !== 0) str += String.fromCharCode(mem[i++]);
|
|
251
|
+
|
|
252
|
+
const exp = new Int32Array(binding.mem.buffer, expPointer, 1)[0];
|
|
253
|
+
|
|
254
|
+
binding.mpfr_free_str(strPointer);
|
|
255
|
+
binding.free(expPointer);
|
|
256
|
+
|
|
257
|
+
const isNeg = str[0] === "-";
|
|
258
|
+
const magnitude = isNeg ? str.slice(1) : str;
|
|
259
|
+
|
|
260
|
+
let result: string;
|
|
261
|
+
if (exp >= magnitude.length)
|
|
262
|
+
result = magnitude + "0".repeat(exp - magnitude.length) + ".0";
|
|
263
|
+
else if (exp <= 0)
|
|
264
|
+
result = "0." + "0".repeat(-exp) + magnitude;
|
|
265
|
+
else
|
|
266
|
+
result = magnitude.slice(0, exp) + "." + magnitude.slice(exp);
|
|
267
|
+
|
|
268
|
+
return (isNeg ? "-" : "") + result;
|
|
269
|
+
},
|
|
270
|
+
|
|
271
|
+
"floattostr": (stack: Stack, getTrueValue: Function, ...args: any[]): string =>
|
|
272
|
+
{
|
|
273
|
+
maxArguments(2, args, "floattostr");
|
|
274
|
+
const x = args[0] as GFloat;
|
|
275
|
+
const digits = isGFloat(args[1]) ? args[1].inner.toNumber() : Number(args[1]);
|
|
276
|
+
|
|
277
|
+
const binding = gmpInstance.binding;
|
|
278
|
+
|
|
279
|
+
const mpfrPow = binding.mpfr_t();
|
|
280
|
+
binding.mpfr_init2(mpfrPow, gmpPrecision + 64);
|
|
281
|
+
binding.mpfr_set_ui(mpfrPow, 10, 0);
|
|
282
|
+
|
|
283
|
+
const mpfrExp = binding.mpfr_t();
|
|
284
|
+
binding.mpfr_init2(mpfrExp, gmpPrecision + 64);
|
|
285
|
+
binding.mpfr_set_ui(mpfrExp, digits + 5, 0);
|
|
286
|
+
binding.mpfr_pow(mpfrPow, mpfrPow, mpfrExp, 0); // mpfrPow = 10^(digits+5)
|
|
287
|
+
binding.mpfr_clear(mpfrExp); binding.mpfr_t_free(mpfrExp);
|
|
288
|
+
|
|
289
|
+
// multiply x by 10^(digits+5)
|
|
290
|
+
const mpfrShifted = binding.mpfr_t();
|
|
291
|
+
binding.mpfr_init2(mpfrShifted, gmpPrecision + 64);
|
|
292
|
+
binding.mpfr_mul(mpfrShifted, x.inner.mpfr_t, mpfrPow, 0);
|
|
293
|
+
binding.mpfr_clear(mpfrPow); binding.mpfr_t_free(mpfrPow);
|
|
294
|
+
|
|
295
|
+
// truncate to integer
|
|
296
|
+
const mpfrInt = binding.mpfr_t();
|
|
297
|
+
binding.mpfr_init2(mpfrInt, gmpPrecision + 64);
|
|
298
|
+
binding.mpfr_floor(mpfrInt, mpfrShifted);
|
|
299
|
+
binding.mpfr_clear(mpfrShifted); binding.mpfr_t_free(mpfrShifted);
|
|
300
|
+
|
|
301
|
+
// convert to mpz
|
|
302
|
+
const mpz = binding.mpz_t();
|
|
303
|
+
binding.mpz_init(mpz);
|
|
304
|
+
binding.mpfr_get_z(mpz, mpfrInt, 0);
|
|
305
|
+
binding.mpfr_clear(mpfrInt); binding.mpfr_t_free(mpfrInt);
|
|
306
|
+
|
|
307
|
+
// convert mpz to string using mpz_to_string
|
|
308
|
+
const str = binding.mpz_to_string(mpz, 10);
|
|
309
|
+
binding.mpz_clear(mpz); binding.mpz_t_free(mpz);
|
|
310
|
+
|
|
311
|
+
const result = str.slice(0, 1) + "." + str.slice(1, digits + 1);
|
|
312
|
+
return result;
|
|
313
|
+
},
|
|
314
|
+
|
|
315
|
+
"bigintdiv": (stack: Stack, getTrueValue: Function, ...args: any[]): GFloat =>
|
|
316
|
+
{
|
|
317
|
+
maxArguments(2, args, "bigintdiv");
|
|
318
|
+
const numerator = args[0];
|
|
319
|
+
const denominator = args[1];
|
|
320
|
+
if (typeof numerator !== "bigint" || typeof denominator !== "bigint")
|
|
321
|
+
errorTemplate("bigintdiv", `both arguments must be BigInt, got "${numerator}" and "${denominator}"`, `maybe you forgot to call tobigint()?`);
|
|
322
|
+
|
|
323
|
+
return BigIntDivToGFloat(numerator, denominator);
|
|
324
|
+
},
|
|
325
|
+
|
|
326
|
+
"bigintmul": (stack: Stack, getTrueValue: Function, ...args: any[]): any =>
|
|
327
|
+
{
|
|
328
|
+
maxArguments(2, args, "bigintmul");
|
|
329
|
+
if (typeof args[0] !== "bigint" || typeof args[1] !== "bigint")
|
|
330
|
+
errorTemplate("bigintmul", `both arguments must be BigInt, got "${args[0]}" and "${args[1]}"`, `maybe you forgot to call tobigint()?`);
|
|
331
|
+
|
|
332
|
+
const binding = gmpInstance.binding;
|
|
333
|
+
|
|
334
|
+
function toMpz(n: bigint): number
|
|
335
|
+
{
|
|
336
|
+
const neg = n < 0n;
|
|
337
|
+
const abs = neg ? -n : n;
|
|
338
|
+
let hex = abs.toString(16);
|
|
339
|
+
if (hex.length % 2) hex = "0" + hex;
|
|
340
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
341
|
+
for (let i = 0; i < bytes.length; i++)
|
|
342
|
+
bytes[i] = parseInt(hex.slice(i*2, i*2+2), 16);
|
|
343
|
+
|
|
344
|
+
const wasmBuf = binding.malloc(bytes.length);
|
|
345
|
+
binding.mem.set(bytes, wasmBuf);
|
|
346
|
+
const mpz = binding.mpz_t();
|
|
347
|
+
binding.mpz_init(mpz);
|
|
348
|
+
binding.mpz_import(mpz, bytes.length, 1, 1, 1, 0, wasmBuf);
|
|
349
|
+
binding.free(wasmBuf);
|
|
350
|
+
if (neg) binding.mpz_neg(mpz, mpz);
|
|
351
|
+
return mpz;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function fromMpz(mpz: number): bigint
|
|
355
|
+
{
|
|
356
|
+
const neg = binding.mpz_sgn(mpz) < 0;
|
|
357
|
+
const absmpz = binding.mpz_t();
|
|
358
|
+
binding.mpz_init(absmpz);
|
|
359
|
+
binding.mpz_abs(absmpz, mpz);
|
|
360
|
+
|
|
361
|
+
const hex = binding.mpz_to_string(absmpz, 16);
|
|
362
|
+
binding.mpz_clear(absmpz);
|
|
363
|
+
binding.mpz_t_free(absmpz);
|
|
364
|
+
|
|
365
|
+
const result = hex ? BigInt("0x" + hex) : 0n;
|
|
366
|
+
return neg ? -result : result;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// convert BigInt to Mpz
|
|
370
|
+
const a = toMpz(args[0]);
|
|
371
|
+
const b = toMpz(args[1]);
|
|
372
|
+
|
|
373
|
+
const mpz = binding.mpz_t();
|
|
374
|
+
binding.mpz_init(mpz);
|
|
375
|
+
binding.mpz_mul(mpz, a, b); // multiply a and b
|
|
376
|
+
|
|
377
|
+
binding.mpz_clear(a); // free a
|
|
378
|
+
binding.mpz_t_free(a);
|
|
379
|
+
|
|
380
|
+
binding.mpz_clear(b);
|
|
381
|
+
binding.mpz_t_free(b);
|
|
382
|
+
|
|
383
|
+
const result = fromMpz(mpz); // convert Mpz to BigInt
|
|
384
|
+
binding.mpz_clear(mpz); // free the mpz
|
|
385
|
+
binding.mpz_t_free(mpz);
|
|
386
|
+
|
|
387
|
+
return result;
|
|
388
|
+
},
|
|
389
|
+
|
|
390
|
+
"chudnovsky": (stack: Stack, getTrueValue: Function, ...args: any[]): any =>
|
|
391
|
+
{
|
|
392
|
+
maxArguments(1, args, "chudnovsky");
|
|
393
|
+
const raw = args[0];
|
|
394
|
+
const n = isGFloat(raw)
|
|
395
|
+
? raw.inner.toNumber()
|
|
396
|
+
: typeof raw === "bigint"
|
|
397
|
+
? Number(raw)
|
|
398
|
+
: raw as number;
|
|
399
|
+
|
|
400
|
+
const P: bigint[] = [];
|
|
401
|
+
const Q: bigint[] = [];
|
|
402
|
+
const T: bigint[] = [];
|
|
403
|
+
|
|
404
|
+
for (let a = 1; a < n; a++)
|
|
405
|
+
{
|
|
406
|
+
const s = BigInt(6 * a);
|
|
407
|
+
const t = BigInt(3 * a);
|
|
408
|
+
const b = BigInt(a);
|
|
409
|
+
|
|
410
|
+
P.push((s-5n)*(s-4n)*(s-3n)*(s-2n)*(s-1n)*s);
|
|
411
|
+
Q.push(262537412640768000n * b*b*b * t*(t-1n)*(t-2n));
|
|
412
|
+
T.push((a%2===0 ? 1n : -1n) * P[P.length-1] * (13591409n + 545140134n * b));
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
while (P.length > 1)
|
|
416
|
+
{
|
|
417
|
+
const nP: bigint[] = [];
|
|
418
|
+
const nQ: bigint[] = [];
|
|
419
|
+
const nT: bigint[] = [];
|
|
420
|
+
|
|
421
|
+
for (let i = 0; i + 1 < P.length; i += 2)
|
|
422
|
+
{
|
|
423
|
+
nP.push(P[i] * P[i+1]);
|
|
424
|
+
nQ.push(Q[i] * Q[i+1]);
|
|
425
|
+
nT.push(Q[i+1] * T[i] + P[i] * T[i+1]);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (P.length % 2 === 1)
|
|
429
|
+
{
|
|
430
|
+
nP.push(P[P.length-1]);
|
|
431
|
+
nQ.push(Q[Q.length-1]);
|
|
432
|
+
nT.push(T[T.length-1]);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
P.length = 0;
|
|
436
|
+
for (let i = 0; i < nP.length; i++) P.push(nP[i]);
|
|
437
|
+
Q.length = 0;
|
|
438
|
+
for (let i = 0; i < nQ.length; i++) Q.push(nQ[i]);
|
|
439
|
+
T.length = 0;
|
|
440
|
+
for (let i = 0; i < nT.length; i++) T.push(nT[i]);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return [P[0], Q[0], T[0]];
|
|
444
|
+
},
|
|
445
|
+
|
|
446
|
+
"ecompute": (stack: Stack, getTrueValue: Function, ...args: any[]): any =>
|
|
447
|
+
{
|
|
448
|
+
maxArguments(1, args, "ecompute");
|
|
449
|
+
const raw = args[0];
|
|
450
|
+
const n = isGFloat(raw) ? raw.inner.toNumber() : Number(raw);
|
|
451
|
+
|
|
452
|
+
let P: bigint[] = [];
|
|
453
|
+
let Q: bigint[] = [];
|
|
454
|
+
|
|
455
|
+
for (let i = 1; i < n; i++)
|
|
456
|
+
{
|
|
457
|
+
P.push(1n);
|
|
458
|
+
Q.push(BigInt(i));
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
while (P.length > 1)
|
|
462
|
+
{
|
|
463
|
+
const nP: bigint[] = [];
|
|
464
|
+
const nQ: bigint[] = [];
|
|
465
|
+
|
|
466
|
+
for (let i = 0; i + 1 < P.length; i += 2)
|
|
467
|
+
{
|
|
468
|
+
nP.push(P[i] * Q[i+1] + P[i+1]);
|
|
469
|
+
nQ.push(Q[i] * Q[i+1]);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
if (P.length % 2 === 1)
|
|
473
|
+
{
|
|
474
|
+
nP.push(P[P.length-1]);
|
|
475
|
+
nQ.push(Q[Q.length-1]);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
P = nP;
|
|
479
|
+
Q = nQ;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
return [P[0], Q[0]];
|
|
483
|
+
},
|
|
484
|
+
|
|
485
|
+
"toSignificance": (stack: Stack, getTrueValue: Function, ...args: any[]) : any =>
|
|
486
|
+
{
|
|
487
|
+
maxArguments(3, args, "toSignificance");
|
|
488
|
+
const x : any = args[0];
|
|
489
|
+
if (!isGFloat(x))
|
|
490
|
+
errorTemplate("toSignificance", `x parameter must be type Float, got "${x}"`, `maybe you forgot to call tofloat()?`);
|
|
491
|
+
|
|
492
|
+
const digits : any = isGFloat(args[1]) ? args[1].inner.toNumber() : Number(args[1]);
|
|
493
|
+
if (!Number.isInteger(digits) || digits <= 0)
|
|
494
|
+
errorTemplate("toSignificance", `digits parameter must be a positive integer, got "${digits}"`);
|
|
495
|
+
|
|
496
|
+
const mode : string | undefined = args[2];
|
|
497
|
+
|
|
498
|
+
// GMP doesn't have toSignificantDigits natively so we shift to get significance, round, and shift back.
|
|
499
|
+
const string = x.inner.toFixed(digits + 10);
|
|
500
|
+
const rounded = GF(string);
|
|
501
|
+
return mode ? applyRounding(rounded, mode) : rounded;
|
|
502
|
+
},
|
|
503
|
+
|
|
504
|
+
"toDecimalPlaces": (stack: Stack, getTrueValue: Function, ...args: any[]) : any =>
|
|
505
|
+
{
|
|
506
|
+
maxArguments(3, args, "toDecimalPlaces");
|
|
507
|
+
const x : any = args[0];
|
|
508
|
+
if (!isGFloat(x))
|
|
509
|
+
errorTemplate("toDecimalPlaces", `x parameter must be type Float, got "${x}"`, `maybe you forgot to call tofloat()?`);
|
|
510
|
+
|
|
511
|
+
const digits : any = isGFloat(args[1]) ? args[1].inner.toNumber() : Number(args[1]);
|
|
512
|
+
if (!Number.isInteger(digits) || digits <= 0)
|
|
513
|
+
errorTemplate("toDecimalPlaces", `digits parameter must be a positive integer, got "${digits}"`);
|
|
514
|
+
|
|
515
|
+
const mode : string | undefined = args[2];
|
|
516
|
+
|
|
517
|
+
const str = x.inner.toFixed(digits);
|
|
518
|
+
const rounded = GF(str);
|
|
519
|
+
return mode ? applyRounding(rounded, mode) : rounded;
|
|
520
|
+
},
|
|
521
|
+
|
|
522
|
+
"exp": (stack: Stack, getTrueValue: Function, ...args: any[]) : any =>
|
|
523
|
+
{
|
|
524
|
+
maxArguments(1, args, "exp");
|
|
525
|
+
const x : any = args[0];
|
|
526
|
+
if (x === undefined)
|
|
527
|
+
errorTemplate("exp", `x parameter is undefined`);
|
|
528
|
+
if (!(isGFloat(x) || typeof x === "bigint"))
|
|
529
|
+
errorTemplate("exp", `x parameter must be Float or BigInt, got "${x}"`, "maybe you forgot to call tofloat() or tobigint()?");
|
|
530
|
+
|
|
531
|
+
return new GFloat(toFloat(x).inner.exp()); // e^x
|
|
532
|
+
},
|
|
533
|
+
|
|
534
|
+
"setPrecision": (stack: Stack, _: any, ...args: any[]) : void =>
|
|
535
|
+
{
|
|
536
|
+
maxArguments(1, args, "setPrecision");
|
|
537
|
+
|
|
538
|
+
let x : number;
|
|
539
|
+
if (isGWrapper(args[0]))
|
|
540
|
+
x = args[0].mpfr_t;
|
|
541
|
+
if (isGFloat(args[0]))
|
|
542
|
+
x = args[0].inner.toNumber();
|
|
543
|
+
else if (typeof args[0] === "bigint")
|
|
544
|
+
x = Number(args[0]);
|
|
545
|
+
else
|
|
546
|
+
x = args[0];
|
|
547
|
+
|
|
548
|
+
setPrecision(x);
|
|
549
|
+
},
|
|
550
|
+
|
|
551
|
+
"toBits": (stack: Stack, _: any, ...args: any[]) : any =>
|
|
552
|
+
{
|
|
553
|
+
maxArguments(1, args, "toBits");
|
|
554
|
+
|
|
555
|
+
let digits: number;
|
|
556
|
+
if (isGFloat(args[0]))
|
|
557
|
+
digits = args[0].inner.toNumber();
|
|
558
|
+
else if (typeof args[0] === "bigint")
|
|
559
|
+
digits = Number(args[0]);
|
|
560
|
+
else
|
|
561
|
+
digits = args[0];
|
|
562
|
+
|
|
563
|
+
const bits = GF(precisionToBits(digits));
|
|
564
|
+
return bits;
|
|
565
|
+
},
|
|
566
|
+
|
|
567
|
+
"getPrecision": () : number =>
|
|
568
|
+
{
|
|
569
|
+
return gmpPrecision;
|
|
570
|
+
},
|
|
571
|
+
|
|
572
|
+
"sine": (stack: Stack, getTrueValue: Function, ...args: any[]) : any =>
|
|
573
|
+
{
|
|
574
|
+
maxArguments(1, args, "sine");
|
|
575
|
+
const x = args[0];
|
|
576
|
+
if (x === undefined)
|
|
577
|
+
errorTemplate("sine", `x parameter is undefined`);
|
|
578
|
+
|
|
579
|
+
return new GFloat(toFloat(x).inner.sin());
|
|
580
|
+
},
|
|
581
|
+
|
|
582
|
+
"cosine": (stack: Stack, getTrueValue: Function, ...args: any[]) : any =>
|
|
583
|
+
{
|
|
584
|
+
maxArguments(1, args, "cosine");
|
|
585
|
+
const x = args[0];
|
|
586
|
+
if (x === undefined)
|
|
587
|
+
errorTemplate("cosine", `x parameter is undefined`);
|
|
588
|
+
|
|
589
|
+
return new GFloat(toFloat(x).inner.cos());
|
|
590
|
+
},
|
|
591
|
+
|
|
592
|
+
"tan": (stack: Stack, getTrueValue: Function, ...args: any[]) : any =>
|
|
593
|
+
{
|
|
594
|
+
maxArguments(1, args, "tan");
|
|
595
|
+
const x = args[0];
|
|
596
|
+
if (x === undefined)
|
|
597
|
+
errorTemplate("tan", `x parameter is undefined`);
|
|
598
|
+
|
|
599
|
+
return new GFloat(toFloat(x).inner.tan());
|
|
600
|
+
},
|
|
601
|
+
|
|
602
|
+
"arcsin": (stack: Stack, getTrueValue: Function, ...args: any[]) : any =>
|
|
603
|
+
{
|
|
604
|
+
maxArguments(1, args, "arcsin");
|
|
605
|
+
const x = args[0];
|
|
606
|
+
if (x === undefined)
|
|
607
|
+
errorTemplate("arcsin", `x parameter is undefined`);
|
|
608
|
+
|
|
609
|
+
const float = toFloat(x);
|
|
610
|
+
if (float.inner.lessThan(GF("-1").inner) || float.inner.greaterThan(GF("1").inner))
|
|
611
|
+
errorTemplate("arcsin", `domain is [-1, 1], got "${float.toString()}"`);
|
|
612
|
+
|
|
613
|
+
return new GFloat(float.inner.asin());
|
|
614
|
+
},
|
|
615
|
+
|
|
616
|
+
"arccos": (stack: Stack, getTrueValue: Function, ...args: any[]) : any =>
|
|
617
|
+
{
|
|
618
|
+
maxArguments(1, args, "arccos");
|
|
619
|
+
const x = args[0];
|
|
620
|
+
if (x === undefined)
|
|
621
|
+
errorTemplate("arccos", `x parameter is undefined`);
|
|
622
|
+
|
|
623
|
+
const float = toFloat(x);
|
|
624
|
+
if (float.inner.lessThan(GF("-1").inner) || float.inner.greaterThan(GF("1").inner))
|
|
625
|
+
errorTemplate("arccos", `domain is [-1, 1], got "${float.toString()}"`);
|
|
626
|
+
|
|
627
|
+
return new GFloat(float.inner.acos());
|
|
628
|
+
},
|
|
629
|
+
|
|
630
|
+
"arctan": (stack: Stack, getTrueValue: Function, ...args: any[]) : any =>
|
|
631
|
+
{
|
|
632
|
+
maxArguments(1, args, "arctan");
|
|
633
|
+
const x = args[0];
|
|
634
|
+
if (x === undefined)
|
|
635
|
+
errorTemplate("arctan", `x parameter is undefined`);
|
|
636
|
+
|
|
637
|
+
return new GFloat(toFloat(x).inner.atan());
|
|
638
|
+
},
|
|
639
|
+
|
|
640
|
+
"random": (stack: Stack, getTrueValue: Function, ...args: any[]) : any =>
|
|
641
|
+
{
|
|
642
|
+
maxArguments(3, args, "random");
|
|
643
|
+
|
|
644
|
+
if (args.length === 0)
|
|
645
|
+
return GF(Math.random().toString());
|
|
646
|
+
|
|
647
|
+
const min = toFloat(args[0]);
|
|
648
|
+
if (args.length === 1)
|
|
649
|
+
errorTemplate("random", `max parameter must be defined after min`);
|
|
650
|
+
|
|
651
|
+
const max = toFloat(args[1]);
|
|
652
|
+
const returnInteger = !!args[2];
|
|
653
|
+
|
|
654
|
+
const range = max.inner.sub(min.inner);
|
|
655
|
+
const result = min.inner.add(GF(Math.random().toString()).inner.mul(range));
|
|
656
|
+
|
|
657
|
+
return returnInteger ? new GFloat(result.floor()) : new GFloat(result);
|
|
658
|
+
},
|
|
659
|
+
|
|
660
|
+
"ceil": (stack: Stack, getTrueValue: Function, ...args: any[]) : any =>
|
|
661
|
+
{
|
|
662
|
+
return simpleRounding((x: number) => Math.ceil(x), x => new GFloat(x.inner.ceil()), args, "ceil");
|
|
663
|
+
},
|
|
664
|
+
|
|
665
|
+
"floor": (stack: Stack, getTrueValue: Function, ...args: any[]) : any =>
|
|
666
|
+
{
|
|
667
|
+
return simpleRounding((x: number) => Math.floor(x), x => new GFloat(x.inner.floor()), args, "floor");
|
|
668
|
+
},
|
|
669
|
+
|
|
670
|
+
"round": (stack: Stack, getTrueValue: Function, ...args: any[]) : any =>
|
|
671
|
+
{
|
|
672
|
+
return simpleRounding((x: number) => Math.round(x), x => new GFloat(x.inner.round()), args, "round");
|
|
673
|
+
},
|
|
674
|
+
|
|
675
|
+
"abs": (stack: Stack, getTrueValue: Function, ...args: any[]) : any =>
|
|
676
|
+
{
|
|
677
|
+
const x = args[0];
|
|
678
|
+
if (typeof x === "bigint")
|
|
679
|
+
return x < 0n ? -x : x; // abs doesn't work on BigInt :3
|
|
680
|
+
|
|
681
|
+
return simpleRounding((x: number) => Math.abs(x), x => new GFloat(x.inner.abs()), args, "abs");
|
|
682
|
+
},
|
|
683
|
+
|
|
684
|
+
"log": (stack: Stack, getTrueValue: Function, ...args: any[]) : any =>
|
|
685
|
+
{
|
|
686
|
+
maxArguments(2, args, "log");
|
|
687
|
+
const rawValue = args[0];
|
|
688
|
+
if (rawValue === undefined)
|
|
689
|
+
errorTemplate("log", `x parameter is undefined`);
|
|
690
|
+
|
|
691
|
+
const x = toFloat(rawValue);
|
|
692
|
+
if (x.inner.lessOrEqual(GF("0").inner))
|
|
693
|
+
errorTemplate("log", `x parameter must be positive, got "${x.toString()}"`);
|
|
694
|
+
|
|
695
|
+
if (args.length === 1)
|
|
696
|
+
return new GFloat(x.inner.log()); // ln(x)
|
|
697
|
+
|
|
698
|
+
const base = toFloat(args[1]);
|
|
699
|
+
|
|
700
|
+
if (base.inner.isEqual(GF("1").inner))
|
|
701
|
+
errorTemplate("log", `base parameter cannot be 1`);
|
|
702
|
+
if (base.inner.lessOrEqual(GF("0").inner))
|
|
703
|
+
errorTemplate("log", `base parameter must be positive`);
|
|
704
|
+
|
|
705
|
+
// log_b = ln(x) / ln(b)
|
|
706
|
+
return new GFloat(x.inner.log().div(base.inner.log()));
|
|
707
|
+
},
|
|
708
|
+
|
|
709
|
+
"min": (stack: Stack, getTrueValue: Function, ...args: any[]) : any =>
|
|
710
|
+
{
|
|
711
|
+
if (args.length === 0)
|
|
712
|
+
errorTemplate("min", `needs at least one argument`);
|
|
713
|
+
|
|
714
|
+
return args.reduce((a, b) =>
|
|
715
|
+
{
|
|
716
|
+
const fa = toFloat(a);
|
|
717
|
+
const fb = toFloat(b);
|
|
718
|
+
return fa.inner.lessThan(fb.inner) ? a : b;
|
|
719
|
+
});
|
|
720
|
+
},
|
|
721
|
+
|
|
722
|
+
"max": (stack: Stack, getTrueValue: Function, ...args: any[]) : any =>
|
|
723
|
+
{
|
|
724
|
+
if (args.length === 0)
|
|
725
|
+
errorTemplate("max", `needs at least one argument`);
|
|
726
|
+
|
|
727
|
+
return args.reduce((a, b) =>
|
|
728
|
+
{
|
|
729
|
+
const fa = toFloat(a);
|
|
730
|
+
const fb = toFloat(b);
|
|
731
|
+
return fa.inner.greaterThan(fb.inner) ? a : b;
|
|
732
|
+
});
|
|
733
|
+
},
|
|
734
|
+
|
|
735
|
+
"len": (stack: Stack, getTrueValue: Function, ...args: any[]) : number =>
|
|
736
|
+
{
|
|
737
|
+
let x : any = args[0];
|
|
738
|
+
|
|
739
|
+
if (x?.type === "object")
|
|
740
|
+
x = Object.entries(x.value);
|
|
741
|
+
else if (!Array.isArray(x) && typeof x !== "string")
|
|
742
|
+
errorTemplate("len", `x parameter must be a String, Array, or Object, got "${x}"`);
|
|
743
|
+
|
|
744
|
+
return x.length;
|
|
745
|
+
},
|
|
746
|
+
|
|
747
|
+
"push": (stack: Stack, getTrueValue: Function, ...args: any[]) : void =>
|
|
748
|
+
{
|
|
749
|
+
const array : any = args[0];
|
|
750
|
+
|
|
751
|
+
if (!Array.isArray(array))
|
|
752
|
+
errorTemplate("push", `First parameter must be an Array, got "${array}"`);
|
|
753
|
+
|
|
754
|
+
for (let i = 1; i < args.length; i++)
|
|
755
|
+
array.push(args[i]);
|
|
756
|
+
},
|
|
757
|
+
|
|
758
|
+
"pop": (stack: Stack, getTrueValue: Function, ...args: any[]) : any =>
|
|
759
|
+
{
|
|
760
|
+
maxArguments(1, args, "pop");
|
|
761
|
+
|
|
762
|
+
const array : any = args[0];
|
|
763
|
+
if (!Array.isArray(array))
|
|
764
|
+
errorTemplate("pop", `First parameter must be an Array, got "${array}"`);
|
|
765
|
+
|
|
766
|
+
return array.pop() ?? null;
|
|
767
|
+
},
|
|
768
|
+
|
|
769
|
+
"typeof": (stack: Stack, getTrueValue: Function, ...args: any[]) : string =>
|
|
770
|
+
{
|
|
771
|
+
maxArguments(1, args, "typeof");
|
|
772
|
+
|
|
773
|
+
const value : any = args[0];
|
|
774
|
+
|
|
775
|
+
if (value === null)
|
|
776
|
+
return "null";
|
|
777
|
+
if (value === undefined)
|
|
778
|
+
return "undefined";
|
|
779
|
+
if (typeof value === "boolean")
|
|
780
|
+
return "boolean";
|
|
781
|
+
|
|
782
|
+
if (isGFloat(value))
|
|
783
|
+
return "float";
|
|
784
|
+
if (typeof value === "bigint")
|
|
785
|
+
return "bigint";
|
|
786
|
+
|
|
787
|
+
if (typeof value === "string")
|
|
788
|
+
return "string";
|
|
789
|
+
if (Array.isArray(value))
|
|
790
|
+
return "array";
|
|
791
|
+
|
|
792
|
+
if (value?.isInstance)
|
|
793
|
+
{
|
|
794
|
+
const initType = syncFunctions.typeof([], () => "", value.properties["0"]);
|
|
795
|
+
return initType || value.class;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
if (value?.type === "class")
|
|
799
|
+
return "class";
|
|
800
|
+
|
|
801
|
+
if (value?.bytecode)
|
|
802
|
+
return "function";
|
|
803
|
+
|
|
804
|
+
if (value?.type === "object")
|
|
805
|
+
return "object";
|
|
806
|
+
|
|
807
|
+
return "unknown";
|
|
808
|
+
},
|
|
809
|
+
|
|
810
|
+
"strascii": (stack: Stack, getTrueValue: Function, ...args: any[]) : number =>
|
|
811
|
+
{
|
|
812
|
+
maxArguments(1, args, "strascii");
|
|
813
|
+
return (args[0] as string).charCodeAt(0);
|
|
814
|
+
},
|
|
815
|
+
|
|
816
|
+
"asciistr": (stack: Stack, getTrueValue: Function, ...args: any[]) : string =>
|
|
817
|
+
{
|
|
818
|
+
maxArguments(1, args, "asciistr");
|
|
819
|
+
return String.fromCharCode(Number(args[0]));
|
|
820
|
+
},
|
|
821
|
+
|
|
822
|
+
"throwerror": (stack: Stack, getTrueValue: Function, ...args: any[]) : void =>
|
|
823
|
+
{
|
|
824
|
+
maxArguments(2, args, "throwerror");
|
|
825
|
+
const name : string = args[0] ?? "Error";
|
|
826
|
+
const message : string = args[1] ?? "";
|
|
827
|
+
|
|
828
|
+
const error = new Error(interpretEscapeCharacters(message));
|
|
829
|
+
error.name = name;
|
|
830
|
+
Object.defineProperty(error, 'name', {value: name});
|
|
831
|
+
throw error;
|
|
832
|
+
},
|
|
833
|
+
|
|
834
|
+
"regularerror": (stack: Stack, getTrueValue: Function, ...args: any[]) : void =>
|
|
835
|
+
{
|
|
836
|
+
const map = args.map(item => isGFloat(item) ? item.inner.toFixed() : item);
|
|
837
|
+
const joined = joinStrings(map);
|
|
838
|
+
const escaped = interpretEscapeCharacters(joined);
|
|
839
|
+
console.error(`\x1B[1m\x1B[31m${escaped}\x1B[0m`);
|
|
840
|
+
},
|
|
841
|
+
|
|
842
|
+
"rf": (stack: Stack, getTrueValue: Function, ...args: any[]) : string =>
|
|
843
|
+
{
|
|
844
|
+
maxArguments(2, args, "rf");
|
|
845
|
+
if (args[0] === undefined)
|
|
846
|
+
errorTemplate("rf", `path parameter is undefined`);
|
|
847
|
+
|
|
848
|
+
const rawPath : string = args[0];
|
|
849
|
+
const resolvedPath : string = path.resolve(getBaseDir(), rawPath);
|
|
850
|
+
|
|
851
|
+
const encoding : string = args[1] ? args[1] : "utf8";
|
|
852
|
+
if (typeof encoding !== "string")
|
|
853
|
+
errorTemplate("rf", `encoding parameter must be a String, got "${encoding}"`);
|
|
854
|
+
|
|
855
|
+
try
|
|
856
|
+
{
|
|
857
|
+
return fs.readFileSync(resolvedPath, encoding as BufferEncoding);
|
|
858
|
+
}
|
|
859
|
+
catch
|
|
860
|
+
{
|
|
861
|
+
errorTemplate("rf", `file "${resolvedPath}" was not found`);
|
|
862
|
+
}
|
|
863
|
+
},
|
|
864
|
+
|
|
865
|
+
"wfraw": (stack: Stack, getTrueValue: Function, ...args: any[]) : void =>
|
|
866
|
+
{
|
|
867
|
+
maxArguments(3, args, "wfraw");
|
|
868
|
+
if (args[0] === undefined)
|
|
869
|
+
errorTemplate("wfraw", `path parameter is undefined`);
|
|
870
|
+
|
|
871
|
+
const rawPath : string = args[0];
|
|
872
|
+
const resolvedPath : string = path.resolve(getBaseDir(), rawPath);
|
|
873
|
+
const content : string = args[1];
|
|
874
|
+
const encoding : string = args[2] ? args[2] : "utf8";
|
|
875
|
+
if (typeof encoding !== "string")
|
|
876
|
+
errorTemplate("wfraw", `encoding parameter must be a String, got "${encoding}"`);
|
|
877
|
+
|
|
878
|
+
try
|
|
879
|
+
{
|
|
880
|
+
fs.writeFileSync(resolvedPath, content, encoding as BufferEncoding);
|
|
881
|
+
}
|
|
882
|
+
catch
|
|
883
|
+
{
|
|
884
|
+
errorTemplate("wfraw", `could not write to file "${resolvedPath}"`);
|
|
885
|
+
}
|
|
886
|
+
},
|
|
887
|
+
|
|
888
|
+
"wf": (stack: Stack, getTrueValue: Function, ...args: any[]) : void =>
|
|
889
|
+
{
|
|
890
|
+
maxArguments(3, args, "wf");
|
|
891
|
+
if (args[0] === undefined)
|
|
892
|
+
errorTemplate("wf", `path parameter is undefined`);
|
|
893
|
+
|
|
894
|
+
const rawPath : string = args[0];
|
|
895
|
+
const resolvedPath : string = path.resolve(getBaseDir(), rawPath);
|
|
896
|
+
const content : string = interpretEscapeCharacters(args[1]);
|
|
897
|
+
const encoding : string = args[2] ? args[2] : "utf8";
|
|
898
|
+
if (typeof encoding !== "string")
|
|
899
|
+
errorTemplate("wf", `encoding parameter must be a String, got "${encoding}"`);
|
|
900
|
+
|
|
901
|
+
try
|
|
902
|
+
{
|
|
903
|
+
fs.writeFileSync(resolvedPath, content, encoding as BufferEncoding);
|
|
904
|
+
}
|
|
905
|
+
catch
|
|
906
|
+
{
|
|
907
|
+
errorTemplate("wf", `could not write to file "${resolvedPath}"`);
|
|
908
|
+
}
|
|
909
|
+
},
|
|
910
|
+
|
|
911
|
+
"ifp": (stack: Stack, getTrueValue: Function, ...args: any[]) : string =>
|
|
912
|
+
{
|
|
913
|
+
maxArguments(1, args, "ifp");
|
|
914
|
+
const fileName : string = args[0];
|
|
915
|
+
|
|
916
|
+
const root = (process as any).pkg
|
|
917
|
+
? path.dirname(process.execPath)
|
|
918
|
+
: path.resolve(path.dirname(process.argv[1]), "..");
|
|
919
|
+
|
|
920
|
+
return path.join(path.resolve(root, "src", "runtime", "libs"), fileName);
|
|
921
|
+
},
|
|
922
|
+
|
|
923
|
+
"af": (stack: Stack, getTrueValue: Function, ...args: any[]) : void =>
|
|
924
|
+
{
|
|
925
|
+
maxArguments(3, args, "af");
|
|
926
|
+
if (args[0] === undefined)
|
|
927
|
+
errorTemplate("af", `path parameter is undefined`);
|
|
928
|
+
|
|
929
|
+
const rawPath : string = args[0];
|
|
930
|
+
const resolvedPath : string = path.resolve(getBaseDir(), rawPath);
|
|
931
|
+
|
|
932
|
+
const content : string = interpretEscapeCharacters(args[1]);
|
|
933
|
+
|
|
934
|
+
const encoding : string = args[2] ? args[2] : "utf8";
|
|
935
|
+
if (typeof encoding !== "string")
|
|
936
|
+
errorTemplate("af", `encoding parameter must be a String, got "${encoding}"`);
|
|
937
|
+
|
|
938
|
+
try
|
|
939
|
+
{
|
|
940
|
+
fs.appendFileSync(resolvedPath, content, encoding as BufferEncoding);
|
|
941
|
+
}
|
|
942
|
+
catch
|
|
943
|
+
{
|
|
944
|
+
errorTemplate("af", `could not append to file "${resolvedPath}"`);
|
|
945
|
+
}
|
|
946
|
+
},
|
|
947
|
+
|
|
948
|
+
"df": (stack: Stack, getTrueValue: Function, ...args: any[]) : void =>
|
|
949
|
+
{
|
|
950
|
+
maxArguments(1, args, "df");
|
|
951
|
+
if (args[0] === undefined)
|
|
952
|
+
errorTemplate("df", `path parameter is undefined`);
|
|
953
|
+
|
|
954
|
+
const rawPath : string = args[0];
|
|
955
|
+
const resolvedPath : string = path.resolve(getBaseDir(), rawPath);
|
|
956
|
+
|
|
957
|
+
try
|
|
958
|
+
{
|
|
959
|
+
fs.unlinkSync(resolvedPath);
|
|
960
|
+
}
|
|
961
|
+
catch
|
|
962
|
+
{
|
|
963
|
+
errorTemplate("df", `could not delete file "${resolvedPath}"`);
|
|
964
|
+
}
|
|
965
|
+
},
|
|
966
|
+
|
|
967
|
+
"fe": (stack: Stack, getTrueValue: Function, ...args: any[]) : number =>
|
|
968
|
+
{
|
|
969
|
+
maxArguments(1, args, "fe");
|
|
970
|
+
if (args[0] === undefined)
|
|
971
|
+
errorTemplate("fe", `path parameter is undefined`);
|
|
972
|
+
|
|
973
|
+
const rawPath : string = args[0];
|
|
974
|
+
const resolvedPath : string = path.resolve(getBaseDir(), rawPath);
|
|
975
|
+
|
|
976
|
+
return fs.existsSync(resolvedPath) ? 1 : 0;
|
|
977
|
+
},
|
|
978
|
+
|
|
979
|
+
"ld": (stack: Stack, getTrueValue: Function, ...args: any[]) : any =>
|
|
980
|
+
{
|
|
981
|
+
maxArguments(1, args, "ld");
|
|
982
|
+
if (args[0] === undefined)
|
|
983
|
+
errorTemplate("ld", `path parameter is undefined`);
|
|
984
|
+
|
|
985
|
+
const rawPath : string = args[0];
|
|
986
|
+
const resolvedPath : string = path.resolve(getBaseDir(), rawPath);
|
|
987
|
+
|
|
988
|
+
try
|
|
989
|
+
{
|
|
990
|
+
return fs.readdirSync(resolvedPath);
|
|
991
|
+
}
|
|
992
|
+
catch
|
|
993
|
+
{
|
|
994
|
+
errorTemplate("ld", `could not read directory "${resolvedPath}"`);
|
|
995
|
+
}
|
|
996
|
+
},
|
|
997
|
+
|
|
998
|
+
"md": (stack: Stack, getTrueValue: Function, ...args: any[]) : void =>
|
|
999
|
+
{
|
|
1000
|
+
maxArguments(1, args, "md");
|
|
1001
|
+
if (args[0] === undefined)
|
|
1002
|
+
errorTemplate("md", `path parameter is undefined`);
|
|
1003
|
+
|
|
1004
|
+
const rawPath : string = args[0];
|
|
1005
|
+
const resolvedPath : string = path.resolve(getBaseDir(), rawPath);
|
|
1006
|
+
try
|
|
1007
|
+
{
|
|
1008
|
+
fs.mkdirSync(resolvedPath);
|
|
1009
|
+
}
|
|
1010
|
+
catch
|
|
1011
|
+
{
|
|
1012
|
+
errorTemplate("md", `could not make directory "${resolvedPath}"`);
|
|
1013
|
+
}
|
|
1014
|
+
},
|
|
1015
|
+
|
|
1016
|
+
"mdr": (stack: Stack, getTrueValue: Function, ...args: any[]) : void =>
|
|
1017
|
+
{
|
|
1018
|
+
maxArguments(1, args, "mdr");
|
|
1019
|
+
if (args[0] === undefined)
|
|
1020
|
+
errorTemplate("mdr", `path parameter is undefined`);
|
|
1021
|
+
|
|
1022
|
+
const rawPath : string = args[0];
|
|
1023
|
+
const resolvedPath : string = path.resolve(getBaseDir(), rawPath);
|
|
1024
|
+
try
|
|
1025
|
+
{
|
|
1026
|
+
fs.mkdirSync(resolvedPath, {recursive: true});
|
|
1027
|
+
}
|
|
1028
|
+
catch
|
|
1029
|
+
{
|
|
1030
|
+
errorTemplate("mdr", `could not make directory "${resolvedPath}"`);
|
|
1031
|
+
}
|
|
1032
|
+
},
|
|
1033
|
+
|
|
1034
|
+
"dd": (stack: Stack, getTrueValue: Function, ...args: any[]) : void =>
|
|
1035
|
+
{
|
|
1036
|
+
maxArguments(1, args, "dd");
|
|
1037
|
+
if (args[0] === undefined)
|
|
1038
|
+
errorTemplate("dd", `path parameter is undefined`);
|
|
1039
|
+
|
|
1040
|
+
const rawPath : string = args[0];
|
|
1041
|
+
const resolvedPath : string = path.resolve(getBaseDir(), rawPath);
|
|
1042
|
+
try
|
|
1043
|
+
{
|
|
1044
|
+
fs.rmSync(resolvedPath, {recursive: true});
|
|
1045
|
+
}
|
|
1046
|
+
catch
|
|
1047
|
+
{
|
|
1048
|
+
errorTemplate("dd", `could not delete directory "${resolvedPath}"`);
|
|
1049
|
+
}
|
|
1050
|
+
},
|
|
1051
|
+
|
|
1052
|
+
"fd": (stack: Stack, getTrueValue: Function, ...args: any[]) : number =>
|
|
1053
|
+
{
|
|
1054
|
+
maxArguments(1, args, "fd");
|
|
1055
|
+
if (args[0] === undefined)
|
|
1056
|
+
errorTemplate("fd", `path parameter is undefined`);
|
|
1057
|
+
|
|
1058
|
+
const rawPath : string = args[0];
|
|
1059
|
+
const resolvedPath : string = path.resolve(getBaseDir(), rawPath);
|
|
1060
|
+
try
|
|
1061
|
+
{
|
|
1062
|
+
return fs.statSync(resolvedPath).isDirectory() ? 1 : 0;
|
|
1063
|
+
}
|
|
1064
|
+
catch
|
|
1065
|
+
{
|
|
1066
|
+
errorTemplate("fd", `could not find directory "${resolvedPath}"`);
|
|
1067
|
+
}
|
|
1068
|
+
},
|
|
1069
|
+
|
|
1070
|
+
"rd": (stack: Stack, getTrueValue: Function, ...args: any[]) : void =>
|
|
1071
|
+
{
|
|
1072
|
+
maxArguments(2, args, "rd");
|
|
1073
|
+
if (args[0] === undefined)
|
|
1074
|
+
errorTemplate("rd", `path parameter is undefined`);
|
|
1075
|
+
if (args[1] === undefined)
|
|
1076
|
+
errorTemplate("rd", `newPath parameter is undefined`);
|
|
1077
|
+
|
|
1078
|
+
const rawPath : string = args[0];
|
|
1079
|
+
const resolvedPath : string = path.resolve(getBaseDir(), rawPath);
|
|
1080
|
+
|
|
1081
|
+
const newPath : string = args[1];
|
|
1082
|
+
const resolvedNewPath : string = path.resolve(getBaseDir(), newPath);
|
|
1083
|
+
try
|
|
1084
|
+
{
|
|
1085
|
+
fs.renameSync(resolvedPath, resolvedNewPath);
|
|
1086
|
+
}
|
|
1087
|
+
catch
|
|
1088
|
+
{
|
|
1089
|
+
errorTemplate("rd", `could not rename directory "${resolvedPath}"`);
|
|
1090
|
+
}
|
|
1091
|
+
},
|
|
1092
|
+
|
|
1093
|
+
"tostr": (stack: Stack, getTrueValue: Function, ...args: any[]) : string =>
|
|
1094
|
+
{
|
|
1095
|
+
maxArguments(1, args, "tostr");
|
|
1096
|
+
const value = args[0];
|
|
1097
|
+
if (value === undefined || value === null)
|
|
1098
|
+
errorTemplate("tostr", `x parameter must be a String, BigInt, or Float`);
|
|
1099
|
+
|
|
1100
|
+
if (isGFloat(value))
|
|
1101
|
+
{
|
|
1102
|
+
const digits = Math.ceil(value.inner.precisionBits / 3.32) + 10;
|
|
1103
|
+
return value.inner.toFixed(digits);
|
|
1104
|
+
}
|
|
1105
|
+
else if (value?.isInstance)
|
|
1106
|
+
{
|
|
1107
|
+
const init = value.properties["0"];
|
|
1108
|
+
if (isGFloat(init))
|
|
1109
|
+
{
|
|
1110
|
+
const digits = Math.ceil(init.inner.precisionBits / 3.32) + 10;
|
|
1111
|
+
return init.inner.toFixed(digits);
|
|
1112
|
+
}
|
|
1113
|
+
if (init !== undefined && init !== null)
|
|
1114
|
+
return String(init);
|
|
1115
|
+
|
|
1116
|
+
return `[${value.class}]`;
|
|
1117
|
+
}
|
|
1118
|
+
else if (value?.type === "class")
|
|
1119
|
+
return `[class ${value.class}]`
|
|
1120
|
+
|
|
1121
|
+
else if (Array.isArray(value) || (typeof value === "object" && value !== null))
|
|
1122
|
+
return JSON.stringify(unwrap(value));
|
|
1123
|
+
else
|
|
1124
|
+
return value.toString();
|
|
1125
|
+
},
|
|
1126
|
+
|
|
1127
|
+
"tojson": (stack: Stack, getTrueValue: Function, ...args: any[]): string =>
|
|
1128
|
+
{
|
|
1129
|
+
maxArguments(1, args, "tojson");
|
|
1130
|
+
return JSON.stringify(unwrap(args[0]));
|
|
1131
|
+
},
|
|
1132
|
+
|
|
1133
|
+
"tofloat": (stack: Stack, getTrueValue: Function, ...args: any[]) : any | number =>
|
|
1134
|
+
{
|
|
1135
|
+
maxArguments(1, args, "tofloat");
|
|
1136
|
+
const x = args[0];
|
|
1137
|
+
if (x === undefined || x === null)
|
|
1138
|
+
errorTemplate("tofloat", `x parameter must be a String, BigInt, or Float`);
|
|
1139
|
+
|
|
1140
|
+
return toFloat(x);
|
|
1141
|
+
},
|
|
1142
|
+
|
|
1143
|
+
"tobigint": (stack: Stack, getTrueValue: Function, ...args: any[]) : any | number =>
|
|
1144
|
+
{
|
|
1145
|
+
maxArguments(1, args, "tobigint");
|
|
1146
|
+
const value = args[0];
|
|
1147
|
+
if (value === undefined || value === null)
|
|
1148
|
+
errorTemplate("tobigint", `x parameter must be a String, BigInt, or Float`);
|
|
1149
|
+
|
|
1150
|
+
return toBigInt(value);
|
|
1151
|
+
},
|
|
1152
|
+
|
|
1153
|
+
"toarr": (stack: Stack, getTrueValue: Function, ...args: any[]) : Array<any> =>
|
|
1154
|
+
{
|
|
1155
|
+
const splitNumber = (value : any) : (number | string)[] =>
|
|
1156
|
+
{
|
|
1157
|
+
const string = isGFloat(value)
|
|
1158
|
+
? value.inner.toFixed()
|
|
1159
|
+
: String(value < 0 ? -value : value).replace("n", "");
|
|
1160
|
+
|
|
1161
|
+
const isNegative = isGFloat(value)
|
|
1162
|
+
? value.inner.isNegative()
|
|
1163
|
+
: value < 0;
|
|
1164
|
+
|
|
1165
|
+
const [integer, float] = string.split('.');
|
|
1166
|
+
|
|
1167
|
+
const digits : (number | string)[] = integer.split('').map(Number);
|
|
1168
|
+
if (float)
|
|
1169
|
+
digits.push('.', ...float.split('').map(Number));
|
|
1170
|
+
if (isNegative)
|
|
1171
|
+
digits[0] = (digits[0] as number) * -1;
|
|
1172
|
+
|
|
1173
|
+
return digits;
|
|
1174
|
+
};
|
|
1175
|
+
|
|
1176
|
+
maxArguments(1, args, "toarr");
|
|
1177
|
+
|
|
1178
|
+
const value : any = args[0];
|
|
1179
|
+
if (value === undefined || value === null)
|
|
1180
|
+
errorTemplate("toarr", `x parameter must be a String, BigInt, or Float, got "${value}"`);
|
|
1181
|
+
|
|
1182
|
+
if (Array.isArray(value))
|
|
1183
|
+
return value;
|
|
1184
|
+
else if (typeof value === "object" && !isGFloat(value))
|
|
1185
|
+
return Object.entries(value);
|
|
1186
|
+
else if (typeof value === "bigint" || isGFloat(value))
|
|
1187
|
+
return splitNumber(value);
|
|
1188
|
+
else if (typeof value === "string")
|
|
1189
|
+
return Array.from(value);
|
|
1190
|
+
|
|
1191
|
+
return [];
|
|
1192
|
+
},
|
|
1193
|
+
|
|
1194
|
+
"tobj": (stack: Stack, getTrueValue: Function, ...args: any[]): any =>
|
|
1195
|
+
{
|
|
1196
|
+
maxArguments(1, args, "tobj");
|
|
1197
|
+
const value = args[0];
|
|
1198
|
+
if (value === undefined || value === null)
|
|
1199
|
+
errorTemplate("tobj", `x parameter must be a String, got "${value}"`, `maybe you forgot to call tostr()?`);
|
|
1200
|
+
|
|
1201
|
+
const str = typeof value === "string" ? value : JSON.stringify(value);
|
|
1202
|
+
|
|
1203
|
+
const wrap = (value : any): any =>
|
|
1204
|
+
{
|
|
1205
|
+
if (Array.isArray(value))
|
|
1206
|
+
return value.map(wrap);
|
|
1207
|
+
else if (value !== null && typeof value === "object")
|
|
1208
|
+
{
|
|
1209
|
+
const wrapped: Record<string, any> = {};
|
|
1210
|
+
for (const key of Object.keys(value))
|
|
1211
|
+
wrapped[key] = wrap(value[key]);
|
|
1212
|
+
return {type: "object", value: wrapped};
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
return value;
|
|
1216
|
+
};
|
|
1217
|
+
|
|
1218
|
+
try
|
|
1219
|
+
{
|
|
1220
|
+
return wrap(JSON.parse(str));
|
|
1221
|
+
}
|
|
1222
|
+
catch (error: any)
|
|
1223
|
+
{
|
|
1224
|
+
errorTemplate("tobj", `failed to parse JSON: ${error.message}`);
|
|
1225
|
+
}
|
|
1226
|
+
},
|
|
1227
|
+
|
|
1228
|
+
"keys": (stack: Stack, getTrueValue: Function, ...args: any[]) : Array<string> =>
|
|
1229
|
+
{
|
|
1230
|
+
maxArguments(1, args, "keys");
|
|
1231
|
+
const value = args[0];
|
|
1232
|
+
if (value?.type !== "object")
|
|
1233
|
+
errorTemplate("keys", `x parameter must be an Object, got "${value}"`, "maybe you forgot to call tobj()?");
|
|
1234
|
+
|
|
1235
|
+
return Object.keys(value.value);
|
|
1236
|
+
},
|
|
1237
|
+
|
|
1238
|
+
"values": (stack: Stack, getTrueValue: Function, ...args: any[]) : Array<string> =>
|
|
1239
|
+
{
|
|
1240
|
+
maxArguments(1, args, "values");
|
|
1241
|
+
const value : any = args[0];
|
|
1242
|
+
if (value?.type !== "object")
|
|
1243
|
+
errorTemplate("values", `x parameter must be an Object, got "${value}"`, "maybe you forgot to call tobj()?");
|
|
1244
|
+
|
|
1245
|
+
return Object.values(value.value);
|
|
1246
|
+
},
|
|
1247
|
+
|
|
1248
|
+
"kill": (stack: Stack, getTrueValue: Function, ...args: any[]) : void =>
|
|
1249
|
+
{
|
|
1250
|
+
maxArguments(1, args, "exit");
|
|
1251
|
+
const code : any = args[0] ?? 0;
|
|
1252
|
+
const exitCode = isGFloat(code) ? code.inner.toNumber() : Number(code);
|
|
1253
|
+
|
|
1254
|
+
if (exitCode !== 0)
|
|
1255
|
+
console.error(`Process exited with error code ${code}`);
|
|
1256
|
+
|
|
1257
|
+
process.exit(exitCode);
|
|
1258
|
+
},
|
|
1259
|
+
|
|
1260
|
+
"getcwd" : () : string => process.cwd(),
|
|
1261
|
+
"getargv": () : string[] => process.argv.slice(4),
|
|
1262
|
+
|
|
1263
|
+
"getenv": (stack: Stack, getTrueValue: Function, ...args: any[]) : string | undefined =>
|
|
1264
|
+
{
|
|
1265
|
+
maxArguments(1, args, "getenv");
|
|
1266
|
+
return process.env[getTrueValue(args[0])];
|
|
1267
|
+
},
|
|
1268
|
+
|
|
1269
|
+
"getpid": () : number => process.pid,
|
|
1270
|
+
|
|
1271
|
+
"__gmpmethods__": (stack: Stack, getTrueValue: Function, ...args: any[]) : any =>
|
|
1272
|
+
{
|
|
1273
|
+
const value = args[0];
|
|
1274
|
+
if (isGFloat(value))
|
|
1275
|
+
return Object.getOwnPropertyNames(Object.getPrototypeOf(value)).join(", ");
|
|
1276
|
+
return "not a gfloat";
|
|
1277
|
+
},
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
const safeStringify = (data : any) : string =>
|
|
1281
|
+
JSON.stringify(
|
|
1282
|
+
data,
|
|
1283
|
+
(_, value) =>
|
|
1284
|
+
{
|
|
1285
|
+
if (typeof value === "bigint")
|
|
1286
|
+
return Number(value);
|
|
1287
|
+
if (
|
|
1288
|
+
value &&
|
|
1289
|
+
typeof value === "object" &&
|
|
1290
|
+
"type" in value &&
|
|
1291
|
+
"value" in value
|
|
1292
|
+
)
|
|
1293
|
+
return value.value;
|
|
1294
|
+
return value;
|
|
1295
|
+
}
|
|
1296
|
+
);
|
|
1297
|
+
|
|
1298
|
+
const unwrap = (value: any): any =>
|
|
1299
|
+
{
|
|
1300
|
+
if (value && typeof value === "object" && "type" in value && "value" in value)
|
|
1301
|
+
return unwrap(value.value);
|
|
1302
|
+
|
|
1303
|
+
if (Array.isArray(value))
|
|
1304
|
+
return value.map(unwrap);
|
|
1305
|
+
|
|
1306
|
+
if (value && typeof value === "object")
|
|
1307
|
+
{
|
|
1308
|
+
const result : any = {};
|
|
1309
|
+
for (const key of Object.keys(value))
|
|
1310
|
+
result[key] = unwrap(value[key]);
|
|
1311
|
+
|
|
1312
|
+
return result;
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
if (typeof value === "bigint")
|
|
1316
|
+
return value.toString();
|
|
1317
|
+
|
|
1318
|
+
return value;
|
|
1319
|
+
};
|
|
1320
|
+
|
|
1321
|
+
export const asyncFunctions =
|
|
1322
|
+
{
|
|
1323
|
+
"input": async (stack: Stack, getTrueValue: Function, ...args: any[]) : Promise<string> =>
|
|
1324
|
+
{
|
|
1325
|
+
let joinedStrings : string = joinStrings(args.map(x => isGFloat(x) ? x.inner.toFixed() : x));
|
|
1326
|
+
|
|
1327
|
+
const rl : readline.Interface = readline.createInterface
|
|
1328
|
+
(
|
|
1329
|
+
{
|
|
1330
|
+
input : process.stdin,
|
|
1331
|
+
output : process.stdout
|
|
1332
|
+
}
|
|
1333
|
+
);
|
|
1334
|
+
|
|
1335
|
+
const prompt : string = interpretEscapeCharacters(joinedStrings);
|
|
1336
|
+
|
|
1337
|
+
const inputValue : string = await new Promise
|
|
1338
|
+
(
|
|
1339
|
+
resolve =>
|
|
1340
|
+
{
|
|
1341
|
+
rl.question(prompt, answer => resolve(answer));
|
|
1342
|
+
}
|
|
1343
|
+
);
|
|
1344
|
+
|
|
1345
|
+
rl.close();
|
|
1346
|
+
|
|
1347
|
+
return inputValue;
|
|
1348
|
+
},
|
|
1349
|
+
|
|
1350
|
+
"sleep": async (stack: Stack, getTrueValue: Function, ...args: any[]) : Promise<void> =>
|
|
1351
|
+
{
|
|
1352
|
+
maxArguments(1, args, "sleep");
|
|
1353
|
+
const ms : any = args[0];
|
|
1354
|
+
if (ms === undefined)
|
|
1355
|
+
errorTemplate("sleep", `ms parameter is undefined`);
|
|
1356
|
+
|
|
1357
|
+
await new Promise
|
|
1358
|
+
(
|
|
1359
|
+
resolve =>
|
|
1360
|
+
setTimeout
|
|
1361
|
+
(
|
|
1362
|
+
resolve,
|
|
1363
|
+
Number(ms)
|
|
1364
|
+
)
|
|
1365
|
+
);
|
|
1366
|
+
},
|
|
1367
|
+
|
|
1368
|
+
"http_get": async (stack: Stack, getTrueValue: Function, ...args: any[]) : Promise<string> =>
|
|
1369
|
+
{
|
|
1370
|
+
maxArguments(2, args, "http_get");
|
|
1371
|
+
|
|
1372
|
+
const url : string = args[0];
|
|
1373
|
+
if (!url || typeof url !== "string")
|
|
1374
|
+
errorTemplate("http_get", `url parameter must be a String, got "${url}"`, `maybe you forgot to call tostr()?`);
|
|
1375
|
+
|
|
1376
|
+
const options : any = args[1] || {};
|
|
1377
|
+
const headers : Record<string, string> = unwrap(options?.headers || {});
|
|
1378
|
+
|
|
1379
|
+
const response = await fetch(url, {method: "GET", headers: headers});
|
|
1380
|
+
|
|
1381
|
+
if (!response.ok)
|
|
1382
|
+
errorTemplate("http_get", `${response.status} ${response.statusText}`);
|
|
1383
|
+
|
|
1384
|
+
try
|
|
1385
|
+
{
|
|
1386
|
+
return await response.text();
|
|
1387
|
+
}
|
|
1388
|
+
catch (error: any)
|
|
1389
|
+
{
|
|
1390
|
+
errorTemplate("http_get", error.message);
|
|
1391
|
+
}
|
|
1392
|
+
},
|
|
1393
|
+
|
|
1394
|
+
"http_post": async (stack: Stack, getTrueValue: Function, ...args: any[]) : Promise<string> =>
|
|
1395
|
+
{
|
|
1396
|
+
maxArguments(3, args, "http_post");
|
|
1397
|
+
|
|
1398
|
+
const url : string = args[0];
|
|
1399
|
+
if (!url || typeof url !== "string")
|
|
1400
|
+
errorTemplate("http_post", `url parameter must be a String, got "${url}"`, `maybe you forgot to call tostr()?`);
|
|
1401
|
+
|
|
1402
|
+
const data : any = args[1];
|
|
1403
|
+
const options : any = args[2] || {};
|
|
1404
|
+
|
|
1405
|
+
const unwrappedOptions = unwrap(options);
|
|
1406
|
+
const headers: Record<string, string> = unwrappedOptions?.headers || {"Content-Type": "application/json"};
|
|
1407
|
+
|
|
1408
|
+
const body = safeStringify(data);
|
|
1409
|
+
const response = await fetch(url, {method: 'POST', headers: headers, body: body});
|
|
1410
|
+
|
|
1411
|
+
if (!response.ok)
|
|
1412
|
+
{
|
|
1413
|
+
const errorBody = await response.text();
|
|
1414
|
+
let message = `${response.status} ${response.statusText}`;
|
|
1415
|
+
try
|
|
1416
|
+
{
|
|
1417
|
+
const parsed = JSON.parse(errorBody);
|
|
1418
|
+
message = parsed?.[0]?.error?.message || parsed?.error?.message || message;
|
|
1419
|
+
}
|
|
1420
|
+
catch {/*notjson*/}
|
|
1421
|
+
|
|
1422
|
+
errorTemplate("http_post", message);
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
try
|
|
1426
|
+
{
|
|
1427
|
+
return await response.text();
|
|
1428
|
+
}
|
|
1429
|
+
catch (error: any)
|
|
1430
|
+
{
|
|
1431
|
+
errorTemplate("http_post", error.message);
|
|
1432
|
+
}
|
|
1433
|
+
},
|
|
1434
|
+
|
|
1435
|
+
"http_request": async (stack: Stack, getTrueValue: Function, ...args: any[]): Promise<string> =>
|
|
1436
|
+
{
|
|
1437
|
+
maxArguments(1, args, "http_request");
|
|
1438
|
+
|
|
1439
|
+
const config : any = args[0];
|
|
1440
|
+
if (!config || typeof config !== "object")
|
|
1441
|
+
errorTemplate("http_request", `config parameter must be an Object, got "${config}"`, `maybe you forgot to call tobj()?`);
|
|
1442
|
+
|
|
1443
|
+
const url : string = config.url;
|
|
1444
|
+
const method : string = config.method || 'GET';
|
|
1445
|
+
const headers : Record<string, string> = unwrap(config.headers || {});
|
|
1446
|
+
const body : any = config.body;
|
|
1447
|
+
|
|
1448
|
+
if (!url)
|
|
1449
|
+
errorTemplate("http_request", `config parameter must include a "url" property`);
|
|
1450
|
+
|
|
1451
|
+
const fetchOptions : RequestInit = {method: method.toUpperCase(), headers: headers};
|
|
1452
|
+
|
|
1453
|
+
if (body && method.toUpperCase() !== "GET")
|
|
1454
|
+
fetchOptions.body = safeStringify(body)
|
|
1455
|
+
|
|
1456
|
+
const response = await fetch(url, fetchOptions);
|
|
1457
|
+
|
|
1458
|
+
if (!response.ok)
|
|
1459
|
+
errorTemplate("http_request", `${response.status} ${response.statusText}`);
|
|
1460
|
+
|
|
1461
|
+
try
|
|
1462
|
+
{
|
|
1463
|
+
return await response.text();
|
|
1464
|
+
}
|
|
1465
|
+
catch (error: any)
|
|
1466
|
+
{
|
|
1467
|
+
errorTemplate("http_request", error.message);
|
|
1468
|
+
}
|
|
1469
|
+
},
|
|
1470
|
+
|
|
1471
|
+
"exec": async (stack: Stack, getTrueValue: Function, ...args: any[]) : Promise<any> =>
|
|
1472
|
+
{
|
|
1473
|
+
maxArguments(2, args, "exec");
|
|
1474
|
+
|
|
1475
|
+
const code : string = args[0];
|
|
1476
|
+
if (typeof code !== "string")
|
|
1477
|
+
errorTemplate("exec", `code parameter must be a String, got "${code}"`);
|
|
1478
|
+
|
|
1479
|
+
const isolateScope : boolean = args[1] ?? false;
|
|
1480
|
+
|
|
1481
|
+
return await executeInCurrentContext(code, isolateScope);
|
|
1482
|
+
}
|
|
1483
|
+
};
|