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.
@@ -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
+ };