gofront 0.0.3 → 0.0.5
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/README.md +110 -135
- package/package.json +2 -3
- package/src/codegen/expressions.js +529 -234
- package/src/codegen/statements.js +259 -14
- package/src/codegen.js +98 -9
- package/src/dts-parser.js +1 -1
- package/src/index.js +13 -12
- package/src/lexer.js +24 -1
- package/src/minifier.js +698 -0
- package/src/parser/expressions.js +115 -15
- package/src/parser/statements.js +4 -4
- package/src/parser/types.js +108 -3
- package/src/parser.js +93 -18
- package/src/typechecker/expressions.js +370 -13
- package/src/typechecker/statements.js +103 -5
- package/src/typechecker/types.js +96 -28
- package/src/typechecker.js +658 -22
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
// CodeGen expression methods and helpers — installed as a mixin on CodeGen.prototype.
|
|
2
2
|
|
|
3
|
+
import { ERROR, isComplex } from "../typechecker/types.js";
|
|
4
|
+
|
|
5
|
+
const globalError = ERROR;
|
|
6
|
+
|
|
3
7
|
const INT_TYPE_NAMES = new Set([
|
|
4
8
|
"int",
|
|
5
9
|
"uint",
|
|
@@ -23,17 +27,68 @@ export const expressionGenMethods = {
|
|
|
23
27
|
if (expr.litKind === "STRING") return JSON.stringify(expr.value);
|
|
24
28
|
return expr.value; // int, float, bool, nil(null)
|
|
25
29
|
|
|
30
|
+
case "ImagLit":
|
|
31
|
+
return `{ re: 0, im: ${expr.value} }`;
|
|
32
|
+
|
|
26
33
|
case "Ident":
|
|
34
|
+
if (this._boxedVars.has(expr.name) && !expr._isAddressOf)
|
|
35
|
+
return `${expr.name}.value`;
|
|
27
36
|
return expr.name;
|
|
28
37
|
|
|
29
38
|
case "UnaryExpr": {
|
|
30
39
|
const op = expr.op === "^" ? "~" : expr.op; // bitwise NOT
|
|
31
|
-
//
|
|
32
|
-
if (op === "
|
|
40
|
+
// Address-of: &x
|
|
41
|
+
if (op === "&") {
|
|
42
|
+
if (expr.operand.kind === "Ident") {
|
|
43
|
+
// Mark so Ident codegen does NOT append .value
|
|
44
|
+
expr.operand._isAddressOf = true;
|
|
45
|
+
// For boxed vars, &x returns the box itself
|
|
46
|
+
if (this._boxedVars.has(expr.operand.name)) {
|
|
47
|
+
return this.genExpr(expr.operand);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// For non-boxed vars (structs, etc.), wrap in { value: x }
|
|
51
|
+
return `{ value: ${this.genExpr(expr.operand)} }`;
|
|
52
|
+
}
|
|
53
|
+
// Dereference: *p → p.value
|
|
54
|
+
if (op === "*") {
|
|
55
|
+
return `${this.genExpr(expr.operand)}.value`;
|
|
56
|
+
}
|
|
57
|
+
// Unary minus/plus on complex
|
|
58
|
+
if ((op === "-" || op === "+") && isComplex(expr.operand._type)) {
|
|
59
|
+
const inner = this.genExpr(expr.operand);
|
|
60
|
+
if (op === "-") return `{ re: -${inner}.re, im: -${inner}.im }`;
|
|
61
|
+
return inner;
|
|
62
|
+
}
|
|
33
63
|
return `${op}${this.genExpr(expr.operand)}`;
|
|
34
64
|
}
|
|
35
65
|
|
|
36
66
|
case "BinaryExpr": {
|
|
67
|
+
// Complex binary operations
|
|
68
|
+
if (
|
|
69
|
+
isComplex(expr._type) ||
|
|
70
|
+
isComplex(expr.left._type) ||
|
|
71
|
+
isComplex(expr.right._type)
|
|
72
|
+
) {
|
|
73
|
+
const l = this._genComplexOperand(expr.left);
|
|
74
|
+
const r = this._genComplexOperand(expr.right);
|
|
75
|
+
switch (expr.op) {
|
|
76
|
+
case "+":
|
|
77
|
+
return `{ re: ${l}.re + ${r}.re, im: ${l}.im + ${r}.im }`;
|
|
78
|
+
case "-":
|
|
79
|
+
return `{ re: ${l}.re - ${r}.re, im: ${l}.im - ${r}.im }`;
|
|
80
|
+
case "*":
|
|
81
|
+
this._usesCmul = true;
|
|
82
|
+
return `__cmul(${l}, ${r})`;
|
|
83
|
+
case "/":
|
|
84
|
+
this._usesCdiv = true;
|
|
85
|
+
return `__cdiv(${l}, ${r})`;
|
|
86
|
+
case "==":
|
|
87
|
+
return `(${l}.re === ${r}.re && ${l}.im === ${r}.im)`;
|
|
88
|
+
case "!=":
|
|
89
|
+
return `(${l}.re !== ${r}.re || ${l}.im !== ${r}.im)`;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
37
92
|
const l = this.genExpr(expr.left);
|
|
38
93
|
const r = this.genExpr(expr.right);
|
|
39
94
|
// Integer division: only when both sides are int
|
|
@@ -48,6 +103,15 @@ export const expressionGenMethods = {
|
|
|
48
103
|
if (expr.op === "&^") {
|
|
49
104
|
return `${l} & ~${r}`;
|
|
50
105
|
}
|
|
106
|
+
// Struct/array equality: use __equal for value comparison
|
|
107
|
+
if (expr.op === "==" || expr.op === "!=") {
|
|
108
|
+
const lt = expr.left._type;
|
|
109
|
+
if (this._isStructOrArrayType(lt)) {
|
|
110
|
+
this._usesEqual = true;
|
|
111
|
+
const cmp = `__equal(${l}, ${r})`;
|
|
112
|
+
return expr.op === "==" ? cmp : `!${cmp}`;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
51
115
|
// Go == and != are strict — map to JS === / !==
|
|
52
116
|
const op =
|
|
53
117
|
expr.op === "==" ? "===" : expr.op === "!=" ? "!==" : expr.op;
|
|
@@ -57,7 +121,15 @@ export const expressionGenMethods = {
|
|
|
57
121
|
case "CallExpr":
|
|
58
122
|
return this.genCall(expr);
|
|
59
123
|
|
|
124
|
+
case "InstantiationExpr":
|
|
125
|
+
return this.genExpr(expr.expr); // type erasure
|
|
126
|
+
|
|
60
127
|
case "SelectorExpr": {
|
|
128
|
+
// Method expression: TypeName.MethodName → (recv, ...args) => recv.method(...args)
|
|
129
|
+
if (expr._isMethodExpr) {
|
|
130
|
+
const method = expr.field;
|
|
131
|
+
return `((recv, ...args) => recv.${method}(...args))`;
|
|
132
|
+
}
|
|
61
133
|
// math constants
|
|
62
134
|
if (expr.expr.kind === "Ident" && expr.expr.name === "math") {
|
|
63
135
|
switch (expr.field) {
|
|
@@ -75,6 +147,13 @@ export const expressionGenMethods = {
|
|
|
75
147
|
return "Number.MIN_SAFE_INTEGER";
|
|
76
148
|
}
|
|
77
149
|
}
|
|
150
|
+
// os constants/vars
|
|
151
|
+
if (expr.expr.kind === "Ident" && expr.expr.name === "os") {
|
|
152
|
+
switch (expr.field) {
|
|
153
|
+
case "Args":
|
|
154
|
+
return "process.argv";
|
|
155
|
+
}
|
|
156
|
+
}
|
|
78
157
|
// time constants (nanosecond durations)
|
|
79
158
|
if (expr.expr.kind === "Ident" && expr.expr.name === "time") {
|
|
80
159
|
switch (expr.field) {
|
|
@@ -91,11 +170,17 @@ export const expressionGenMethods = {
|
|
|
91
170
|
const base = this.genExpr(expr.expr);
|
|
92
171
|
// Bundled GoFront packages are inlined — drop the qualifier.
|
|
93
172
|
if (this.bundledPackages.has(base)) return expr.field;
|
|
94
|
-
// Pointer types
|
|
173
|
+
// Pointer types: route through .value
|
|
95
174
|
if (expr.expr._type?.kind === "pointer" && expr.field !== "value") {
|
|
96
|
-
|
|
175
|
+
const sel = `${base}.value.${expr.field}`;
|
|
176
|
+
if (expr._isMethodValue && !expr._callee)
|
|
177
|
+
return `${sel}.bind(${base}.value)`;
|
|
178
|
+
return sel;
|
|
97
179
|
}
|
|
98
|
-
|
|
180
|
+
const sel = `${base}.${expr.field}`;
|
|
181
|
+
// Method used as value (not called) — bind the receiver
|
|
182
|
+
if (expr._isMethodValue && !expr._callee) return `${sel}.bind(${base})`;
|
|
183
|
+
return sel;
|
|
99
184
|
}
|
|
100
185
|
|
|
101
186
|
case "IndexExpr": {
|
|
@@ -110,6 +195,14 @@ export const expressionGenMethods = {
|
|
|
110
195
|
}
|
|
111
196
|
return `(${base}[${idx}] ?? ${zero})`;
|
|
112
197
|
}
|
|
198
|
+
// String indexing returns a byte (charCodeAt), not a JS character
|
|
199
|
+
const exprType = expr.expr._type;
|
|
200
|
+
const isStr =
|
|
201
|
+
(exprType?.kind === "basic" && exprType.name === "string") ||
|
|
202
|
+
(exprType?.kind === "untyped" && exprType.base === "string");
|
|
203
|
+
if (isStr && !expr._lvalue) {
|
|
204
|
+
return `${base}.charCodeAt(${idx})`;
|
|
205
|
+
}
|
|
113
206
|
return `${base}[${idx}]`;
|
|
114
207
|
}
|
|
115
208
|
|
|
@@ -129,10 +222,14 @@ export const expressionGenMethods = {
|
|
|
129
222
|
const params = expr.params.map((p) => p.name).join(", ");
|
|
130
223
|
const asyncPrefix = expr.async ? "async " : "";
|
|
131
224
|
const saved = this.out;
|
|
225
|
+
const prevBoxed = this._boxedVars;
|
|
226
|
+
this._boxedVars = new Set(prevBoxed); // inherit parent's boxed vars (closures)
|
|
227
|
+
this._scanAddressTaken(expr.body);
|
|
132
228
|
this.out = [];
|
|
133
229
|
this.indented(() => this._genBody(expr.body));
|
|
134
230
|
const body = this.out.join("\n");
|
|
135
231
|
this.out = saved;
|
|
232
|
+
this._boxedVars = prevBoxed;
|
|
136
233
|
return `${asyncPrefix}function(${params}) {\n${body}\n${" ".repeat(this.indent)}}`;
|
|
137
234
|
}
|
|
138
235
|
|
|
@@ -140,6 +237,26 @@ export const expressionGenMethods = {
|
|
|
140
237
|
const inner = this.genExpr(expr.expr);
|
|
141
238
|
const t = expr.targetType;
|
|
142
239
|
|
|
240
|
+
// Complex type conversions
|
|
241
|
+
if (t?.name === "complex128" || t?.name === "complex64") {
|
|
242
|
+
const srcType = expr.expr._type;
|
|
243
|
+
if (isComplex(srcType)) return inner; // complex→complex identity
|
|
244
|
+
return `{ re: ${inner}, im: 0 }`; // numeric→complex
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Array type conversion: [N]T(slice) → slice(0, N)
|
|
248
|
+
if (t?.kind === "ArrayType") {
|
|
249
|
+
const srcType = expr.expr._type;
|
|
250
|
+
const srcResolved =
|
|
251
|
+
srcType?.kind === "named" ? srcType.underlying : srcType;
|
|
252
|
+
if (srcResolved?.kind === "slice") {
|
|
253
|
+
const size =
|
|
254
|
+
t.size?.value !== undefined ? Number(t.size.value) : t.size;
|
|
255
|
+
return `${inner}.slice(0, ${size})`;
|
|
256
|
+
}
|
|
257
|
+
return inner;
|
|
258
|
+
}
|
|
259
|
+
|
|
143
260
|
// Slice type conversions
|
|
144
261
|
if (t?.kind === "SliceType") {
|
|
145
262
|
const elem = t.elem?.name;
|
|
@@ -162,6 +279,11 @@ export const expressionGenMethods = {
|
|
|
162
279
|
}
|
|
163
280
|
|
|
164
281
|
const target = t?.name;
|
|
282
|
+
// error("msg") → __error("msg")
|
|
283
|
+
if (target === "error") {
|
|
284
|
+
this._usesError = true;
|
|
285
|
+
return `__error(${inner})`;
|
|
286
|
+
}
|
|
165
287
|
switch (target) {
|
|
166
288
|
case "string": {
|
|
167
289
|
// Go string(65) → "A" (Unicode code point), not "65"
|
|
@@ -169,6 +291,14 @@ export const expressionGenMethods = {
|
|
|
169
291
|
if (srcType && this.isIntType(srcType)) {
|
|
170
292
|
return `String.fromCodePoint(${inner})`;
|
|
171
293
|
}
|
|
294
|
+
// string([]byte) → decode byte array to string
|
|
295
|
+
if (
|
|
296
|
+
srcType &&
|
|
297
|
+
srcType.kind === "slice" &&
|
|
298
|
+
srcType.elem?.name === "int"
|
|
299
|
+
) {
|
|
300
|
+
return `${inner}.map(c => String.fromCharCode(c)).join("")`;
|
|
301
|
+
}
|
|
172
302
|
return `String(${inner})`;
|
|
173
303
|
}
|
|
174
304
|
case "int":
|
|
@@ -209,12 +339,16 @@ export const expressionGenMethods = {
|
|
|
209
339
|
},
|
|
210
340
|
|
|
211
341
|
genCall(expr) {
|
|
342
|
+
// Unwrap InstantiationExpr for function calls: Foo[int](42) → Foo(42)
|
|
343
|
+
const funcExpr =
|
|
344
|
+
expr.func.kind === "InstantiationExpr" ? expr.func.expr : expr.func;
|
|
212
345
|
// Handle built-ins that need special JS translation
|
|
213
|
-
if (
|
|
214
|
-
switch (
|
|
346
|
+
if (funcExpr.kind === "Ident") {
|
|
347
|
+
switch (funcExpr.name) {
|
|
215
348
|
case "append":
|
|
216
349
|
return this.genAppend(expr);
|
|
217
350
|
case "len": {
|
|
351
|
+
if (expr._constLen != null) return String(expr._constLen);
|
|
218
352
|
const arg = expr.args[0];
|
|
219
353
|
const t = arg?._type;
|
|
220
354
|
const js = this.genExpr(arg);
|
|
@@ -250,9 +384,9 @@ export const expressionGenMethods = {
|
|
|
250
384
|
case "recover":
|
|
251
385
|
return `(typeof __panic !== "undefined" && __panic !== null ? (() => { const __r = __panic.message ?? String(__panic); __panic = null; return __r; })() : null)`;
|
|
252
386
|
case "error": {
|
|
253
|
-
|
|
387
|
+
this._usesError = true;
|
|
254
388
|
const arg = this.genExpr(expr.args[0]);
|
|
255
|
-
return arg
|
|
389
|
+
return `__error(${arg})`;
|
|
256
390
|
}
|
|
257
391
|
case "min": {
|
|
258
392
|
const args = expr.args.map((a) => this.genExpr(a)).join(", ");
|
|
@@ -274,225 +408,50 @@ export const expressionGenMethods = {
|
|
|
274
408
|
}
|
|
275
409
|
return `(${js}).length = 0`;
|
|
276
410
|
}
|
|
411
|
+
case "complex": {
|
|
412
|
+
const re = this.genExpr(expr.args[0]);
|
|
413
|
+
const im = this.genExpr(expr.args[1]);
|
|
414
|
+
return `{ re: ${re}, im: ${im} }`;
|
|
415
|
+
}
|
|
416
|
+
case "real":
|
|
417
|
+
return `(${this.genExpr(expr.args[0])}).re`;
|
|
418
|
+
case "imag":
|
|
419
|
+
return `(${this.genExpr(expr.args[0])}).im`;
|
|
277
420
|
}
|
|
278
421
|
}
|
|
279
422
|
|
|
280
|
-
// error.Error() →
|
|
423
|
+
// error.Error() → real method call
|
|
281
424
|
if (
|
|
282
425
|
expr.func.kind === "SelectorExpr" &&
|
|
283
426
|
expr.func.field === "Error" &&
|
|
284
|
-
expr.func.expr._type
|
|
427
|
+
expr.func.expr._type &&
|
|
428
|
+
(expr.func.expr._type === globalError ||
|
|
429
|
+
expr.func.expr._type?.name === "error" ||
|
|
430
|
+
(expr.func.expr._type?.kind === "named" &&
|
|
431
|
+
expr.func.expr._type?.underlying?.kind === "interface"))
|
|
285
432
|
) {
|
|
286
|
-
return this.genExpr(expr.func.expr)
|
|
433
|
+
return `${this.genExpr(expr.func.expr)}.Error()`;
|
|
287
434
|
}
|
|
288
435
|
|
|
289
436
|
// fmt.Sprintf / fmt.Printf / fmt.Println / fmt.Print / fmt.Errorf
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
expr.func.expr.name
|
|
294
|
-
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
case "Sprintf":
|
|
298
|
-
this._usesSprintf = true;
|
|
299
|
-
return `__sprintf(${fmtArgs})`;
|
|
300
|
-
case "Errorf":
|
|
301
|
-
this._usesSprintf = true;
|
|
302
|
-
return `__sprintf(${fmtArgs})`;
|
|
303
|
-
case "Printf":
|
|
304
|
-
this._usesSprintf = true;
|
|
305
|
-
return `process?.stdout?.write(__sprintf(${fmtArgs}))`;
|
|
306
|
-
case "Println":
|
|
307
|
-
this._usesSprintf = true;
|
|
308
|
-
return `console.log(__sprintf(${fmtArgs}))`;
|
|
309
|
-
case "Print":
|
|
310
|
-
this._usesSprintf = true;
|
|
311
|
-
return `process?.stdout?.write(__sprintf(${fmtArgs}))`;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// strings.* — direct JS string method mappings
|
|
316
|
-
if (
|
|
317
|
-
expr.func.kind === "SelectorExpr" &&
|
|
318
|
-
expr.func.expr.kind === "Ident" &&
|
|
319
|
-
expr.func.expr.name === "strings"
|
|
320
|
-
) {
|
|
321
|
-
const a = expr.args.map((e) => this.genExpr(e));
|
|
322
|
-
switch (expr.func.field) {
|
|
323
|
-
case "Contains":
|
|
324
|
-
return `${a[0]}.includes(${a[1]})`;
|
|
325
|
-
case "HasPrefix":
|
|
326
|
-
return `${a[0]}.startsWith(${a[1]})`;
|
|
327
|
-
case "HasSuffix":
|
|
328
|
-
return `${a[0]}.endsWith(${a[1]})`;
|
|
329
|
-
case "Index":
|
|
330
|
-
return `${a[0]}.indexOf(${a[1]})`;
|
|
331
|
-
case "LastIndex":
|
|
332
|
-
return `${a[0]}.lastIndexOf(${a[1]})`;
|
|
333
|
-
case "Count":
|
|
334
|
-
return `${a[0]}.split(${a[1]}).length - 1`;
|
|
335
|
-
case "Repeat":
|
|
336
|
-
return `${a[0]}.repeat(${a[1]})`;
|
|
337
|
-
case "Replace":
|
|
338
|
-
return `${a[0]}.replace(${a[1]}, ${a[2]})`;
|
|
339
|
-
case "ReplaceAll":
|
|
340
|
-
return `${a[0]}.replaceAll(${a[1]}, ${a[2]})`;
|
|
341
|
-
case "ToUpper":
|
|
342
|
-
return `${a[0]}.toUpperCase()`;
|
|
343
|
-
case "ToLower":
|
|
344
|
-
return `${a[0]}.toLowerCase()`;
|
|
345
|
-
case "TrimSpace":
|
|
346
|
-
return `${a[0]}.trim()`;
|
|
347
|
-
case "Trim":
|
|
348
|
-
return `${a[0]}.replace(new RegExp(\`^[\${${a[1]}}]+|[\${${a[1]}}]+$\`, "g"), "")`;
|
|
349
|
-
case "TrimPrefix":
|
|
350
|
-
return `(${a[0]}.startsWith(${a[1]}) ? ${a[0]}.slice(${a[1]}.length) : ${a[0]})`;
|
|
351
|
-
case "TrimSuffix":
|
|
352
|
-
return `(${a[0]}.endsWith(${a[1]}) ? ${a[0]}.slice(0, -${a[1]}.length) : ${a[0]})`;
|
|
353
|
-
case "TrimLeft":
|
|
354
|
-
return `${a[0]}.replace(new RegExp(\`^[\${${a[1]}}]+\`), "")`;
|
|
355
|
-
case "TrimRight":
|
|
356
|
-
return `${a[0]}.replace(new RegExp(\`[\${${a[1]}}]+$\`), "")`;
|
|
357
|
-
case "Split":
|
|
358
|
-
return `${a[0]}.split(${a[1]})`;
|
|
359
|
-
case "Join":
|
|
360
|
-
return `${a[0]}.join(${a[1]})`;
|
|
361
|
-
case "EqualFold":
|
|
362
|
-
return `${a[0]}.toLowerCase() === ${a[1]}.toLowerCase()`;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// strconv.* — number/string conversion
|
|
367
|
-
if (
|
|
368
|
-
expr.func.kind === "SelectorExpr" &&
|
|
369
|
-
expr.func.expr.kind === "Ident" &&
|
|
370
|
-
expr.func.expr.name === "strconv"
|
|
371
|
-
) {
|
|
372
|
-
const a = expr.args.map((e) => this.genExpr(e));
|
|
373
|
-
switch (expr.func.field) {
|
|
374
|
-
case "Itoa":
|
|
375
|
-
return `String(${a[0]})`;
|
|
376
|
-
case "Atoi":
|
|
377
|
-
return `(Number.isNaN(Number(${a[0]})) ? [0, "invalid syntax"] : [Number(${a[0]}) | 0, null])`;
|
|
378
|
-
case "FormatBool":
|
|
379
|
-
return `String(${a[0]})`;
|
|
380
|
-
case "FormatInt":
|
|
381
|
-
return `(${a[0]}).toString(${a[1]})`;
|
|
382
|
-
case "FormatFloat":
|
|
383
|
-
return `String(${a[0]})`;
|
|
384
|
-
case "ParseFloat":
|
|
385
|
-
return `(Number.isNaN(Number(${a[0]})) ? [0, "invalid syntax"] : [Number(${a[0]}), null])`;
|
|
386
|
-
case "ParseInt":
|
|
387
|
-
return `(Number.isNaN(parseInt(${a[0]}, ${a[1]} || 10)) ? [0, "invalid syntax"] : [parseInt(${a[0]}, ${a[1]} || 10), null])`;
|
|
388
|
-
case "ParseBool":
|
|
389
|
-
return `(${a[0]} === "true" || ${a[0]} === "1" ? [true, null] : ${a[0]} === "false" || ${a[0]} === "0" ? [false, null] : [false, "invalid syntax"])`;
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// sort.* — in-place sorting
|
|
394
|
-
if (
|
|
395
|
-
expr.func.kind === "SelectorExpr" &&
|
|
396
|
-
expr.func.expr.kind === "Ident" &&
|
|
397
|
-
expr.func.expr.name === "sort"
|
|
398
|
-
) {
|
|
399
|
-
const a = expr.args.map((e) => this.genExpr(e));
|
|
400
|
-
switch (expr.func.field) {
|
|
401
|
-
case "Ints":
|
|
402
|
-
case "Float64s":
|
|
403
|
-
return `${a[0]}.sort((a, b) => a - b)`;
|
|
404
|
-
case "Strings":
|
|
405
|
-
return `${a[0]}.sort()`;
|
|
406
|
-
case "Slice":
|
|
407
|
-
return `${a[0]}.sort((a, b) => ${a[1]}(a, b) ? -1 : ${a[1]}(b, a) ? 1 : 0)`;
|
|
408
|
-
case "SliceStable":
|
|
409
|
-
return `${a[0]}.sort((a, b) => ${a[1]}(a, b) ? -1 : ${a[1]}(b, a) ? 1 : 0)`;
|
|
410
|
-
case "SliceIsSorted":
|
|
411
|
-
return `${a[0]}.every((v, i, a) => i === 0 || ${a[1]}(a[i - 1], v))`;
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
// math.* — math functions
|
|
416
|
-
if (
|
|
417
|
-
expr.func.kind === "SelectorExpr" &&
|
|
418
|
-
expr.func.expr.kind === "Ident" &&
|
|
419
|
-
expr.func.expr.name === "math"
|
|
420
|
-
) {
|
|
421
|
-
const a = expr.args.map((e) => this.genExpr(e));
|
|
422
|
-
switch (expr.func.field) {
|
|
423
|
-
case "Abs":
|
|
424
|
-
return `Math.abs(${a[0]})`;
|
|
425
|
-
case "Floor":
|
|
426
|
-
return `Math.floor(${a[0]})`;
|
|
427
|
-
case "Ceil":
|
|
428
|
-
return `Math.ceil(${a[0]})`;
|
|
429
|
-
case "Round":
|
|
430
|
-
return `Math.round(${a[0]})`;
|
|
431
|
-
case "Sqrt":
|
|
432
|
-
return `Math.sqrt(${a[0]})`;
|
|
433
|
-
case "Cbrt":
|
|
434
|
-
return `Math.cbrt(${a[0]})`;
|
|
435
|
-
case "Pow":
|
|
436
|
-
return `Math.pow(${a[0]}, ${a[1]})`;
|
|
437
|
-
case "Log":
|
|
438
|
-
return `Math.log(${a[0]})`;
|
|
439
|
-
case "Log2":
|
|
440
|
-
return `Math.log2(${a[0]})`;
|
|
441
|
-
case "Log10":
|
|
442
|
-
return `Math.log10(${a[0]})`;
|
|
443
|
-
case "Sin":
|
|
444
|
-
return `Math.sin(${a[0]})`;
|
|
445
|
-
case "Cos":
|
|
446
|
-
return `Math.cos(${a[0]})`;
|
|
447
|
-
case "Tan":
|
|
448
|
-
return `Math.tan(${a[0]})`;
|
|
449
|
-
case "Min":
|
|
450
|
-
return `Math.min(${a[0]}, ${a[1]})`;
|
|
451
|
-
case "Max":
|
|
452
|
-
return `Math.max(${a[0]}, ${a[1]})`;
|
|
453
|
-
case "Mod":
|
|
454
|
-
return `${a[0]} % ${a[1]}`;
|
|
455
|
-
case "Inf":
|
|
456
|
-
return `(${a[0]} >= 0 ? Infinity : -Infinity)`;
|
|
457
|
-
case "IsNaN":
|
|
458
|
-
return `Number.isNaN(${a[0]})`;
|
|
459
|
-
case "IsInf":
|
|
460
|
-
return `(${a[1]} > 0 ? ${a[0]} === Infinity : ${a[1]} < 0 ? ${a[0]} === -Infinity : !Number.isFinite(${a[0]}))`;
|
|
461
|
-
case "NaN":
|
|
462
|
-
return "NaN";
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
// errors.New — identity (errors are plain strings)
|
|
467
|
-
if (
|
|
468
|
-
expr.func.kind === "SelectorExpr" &&
|
|
469
|
-
expr.func.expr.kind === "Ident" &&
|
|
470
|
-
expr.func.expr.name === "errors"
|
|
471
|
-
) {
|
|
472
|
-
const a = expr.args.map((e) => this.genExpr(e));
|
|
473
|
-
if (expr.func.field === "New") return a[0];
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
// time.* — partial JS-friendly time support
|
|
477
|
-
if (
|
|
478
|
-
expr.func.kind === "SelectorExpr" &&
|
|
479
|
-
expr.func.expr.kind === "Ident" &&
|
|
480
|
-
expr.func.expr.name === "time"
|
|
481
|
-
) {
|
|
482
|
-
const a = expr.args.map((e) => this.genExpr(e));
|
|
483
|
-
switch (expr.func.field) {
|
|
484
|
-
case "Now":
|
|
485
|
-
return "Date.now()";
|
|
486
|
-
case "Since":
|
|
487
|
-
return `(Date.now() - ${a[0]})`;
|
|
488
|
-
case "Sleep":
|
|
489
|
-
return `await new Promise(r => setTimeout(r, ${a[0]} / 1000000))`;
|
|
490
|
-
}
|
|
437
|
+
// Standard library namespace dispatch — all follow the pattern:
|
|
438
|
+
// pkg.Func(args) → inline JS
|
|
439
|
+
if (expr.func.kind === "SelectorExpr" && expr.func.expr.kind === "Ident") {
|
|
440
|
+
const ns = expr.func.expr.name;
|
|
441
|
+
const fn = expr.func.field;
|
|
442
|
+
const result = this._genStdlibCall(ns, fn, expr);
|
|
443
|
+
if (result !== undefined) return result;
|
|
491
444
|
}
|
|
492
445
|
|
|
446
|
+
// Mark the callee so SelectorExpr knows it's being called, not used as a value
|
|
447
|
+
expr.func._callee = true;
|
|
493
448
|
const rawFn = this.genExpr(expr.func);
|
|
494
449
|
// Wrap function literals in parens so `function(){}()` → `(function(){})()`
|
|
495
450
|
const fn = expr.func.kind === "FuncLit" ? `(${rawFn})` : rawFn;
|
|
451
|
+
// Multi-value forwarding: f(g()) where g() returns multiple values → f(...g())
|
|
452
|
+
if (expr._multiForward) {
|
|
453
|
+
return `${fn}(...${this.genExpr(expr.args[0])})`;
|
|
454
|
+
}
|
|
496
455
|
const args = expr.args
|
|
497
456
|
.map((a) => (a._spread ? `...${this.genExpr(a)}` : this.genExpr(a)))
|
|
498
457
|
.join(", ");
|
|
@@ -537,34 +496,40 @@ export const expressionGenMethods = {
|
|
|
537
496
|
return "{}";
|
|
538
497
|
},
|
|
539
498
|
|
|
540
|
-
// Render
|
|
541
|
-
// Handles embedded-spread (_isEmbedInit) and
|
|
499
|
+
// Render struct elements as a JS field list string.
|
|
500
|
+
// Handles positional (_positionalField), embedded-spread (_isEmbedInit), and keyed (KeyValueExpr).
|
|
542
501
|
_genStructFields(elems) {
|
|
543
502
|
return elems
|
|
544
|
-
.
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
503
|
+
.map((e) => {
|
|
504
|
+
if (e._positionalField) {
|
|
505
|
+
return `${e._positionalField}: ${this.genExpr(e)}`;
|
|
506
|
+
}
|
|
507
|
+
if (e.kind !== "KeyValueExpr") return null;
|
|
508
|
+
if (e._isEmbedInit) return `...${this.genExpr(e.value)}`;
|
|
509
|
+
return `${e.key.name ?? this.genExpr(e.key)}: ${this.genExpr(e.value)}`;
|
|
510
|
+
})
|
|
511
|
+
.filter((s) => s !== null)
|
|
550
512
|
.join(", ");
|
|
551
513
|
},
|
|
552
514
|
|
|
553
515
|
genCompositeLit(expr) {
|
|
554
516
|
const t = expr.typeExpr;
|
|
555
517
|
|
|
556
|
-
// Implicit composite literal: {X: 1} inside a slice/map — type inferred from context
|
|
518
|
+
// Implicit composite literal: {X: 1} or {1, 2} inside a slice/map — type inferred from context
|
|
557
519
|
if (t === null) {
|
|
558
|
-
|
|
559
|
-
|
|
520
|
+
const typeName = expr._type?.name ?? expr._type?.underlying?.name;
|
|
521
|
+
const hasPositional = expr.elems.some((e) => e._positionalField);
|
|
522
|
+
const hasKeyed = expr.elems.some((e) => e.kind === "KeyValueExpr");
|
|
523
|
+
if (hasPositional || hasKeyed) {
|
|
560
524
|
if (typeName && this.structNames.has(typeName)) {
|
|
561
525
|
return `new ${typeName}({ ${this._genStructFields(expr.elems)} })`;
|
|
562
526
|
}
|
|
563
527
|
const fields = expr.elems
|
|
564
|
-
.map(
|
|
565
|
-
(e)
|
|
566
|
-
`${e.
|
|
567
|
-
|
|
528
|
+
.map((e) => {
|
|
529
|
+
if (e._positionalField)
|
|
530
|
+
return `${e._positionalField}: ${this.genExpr(e)}`;
|
|
531
|
+
return `${e.key.name ?? this.genExpr(e.key)}: ${this.genExpr(e.value)}`;
|
|
532
|
+
})
|
|
568
533
|
.join(", ");
|
|
569
534
|
return `{ ${fields} }`;
|
|
570
535
|
}
|
|
@@ -573,11 +538,18 @@ export const expressionGenMethods = {
|
|
|
573
538
|
|
|
574
539
|
const typeName = this.getTypeName(t);
|
|
575
540
|
|
|
576
|
-
// Struct: new Foo({ X: 1, Y: 2 })
|
|
541
|
+
// Struct: new Foo({ X: 1, Y: 2 }) — keyed or positional
|
|
577
542
|
if (typeName && this.structNames.has(typeName)) {
|
|
578
543
|
return `new ${typeName}({ ${this._genStructFields(expr.elems)} })`;
|
|
579
544
|
}
|
|
580
545
|
|
|
546
|
+
// Fallback: named type that's not a struct but has positional elements → treat as struct
|
|
547
|
+
if (expr.elems.some((e) => e._positionalField)) {
|
|
548
|
+
if (typeName) {
|
|
549
|
+
return `new ${typeName}({ ${this._genStructFields(expr.elems)} })`;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
581
553
|
// Slice/array: [1, 2, 3]
|
|
582
554
|
if (t?.kind === "SliceType" || t?.kind === "ArrayType") {
|
|
583
555
|
const elems = expr.elems
|
|
@@ -620,10 +592,18 @@ export const expressionGenMethods = {
|
|
|
620
592
|
|
|
621
593
|
// ── Helpers ───────────────────────────────────────────────────
|
|
622
594
|
|
|
595
|
+
_genComplexOperand(expr) {
|
|
596
|
+
if (isComplex(expr._type)) return this.genExpr(expr);
|
|
597
|
+
return `{ re: ${this.genExpr(expr)}, im: 0 }`;
|
|
598
|
+
},
|
|
599
|
+
|
|
623
600
|
getTypeName(typeNode) {
|
|
624
601
|
if (!typeNode) return null;
|
|
625
602
|
if (typeNode.kind === "TypeName") return typeNode.name;
|
|
603
|
+
if (typeNode.kind === "GenericTypeName") return typeNode.name;
|
|
626
604
|
if (typeNode.kind === "Ident") return typeNode.name;
|
|
605
|
+
if (typeNode.kind === "InstantiationExpr")
|
|
606
|
+
return this.getTypeName(typeNode.expr);
|
|
627
607
|
if (typeNode.kind === "SelectorExpr") return typeNode.field;
|
|
628
608
|
return null;
|
|
629
609
|
},
|
|
@@ -664,6 +644,9 @@ export const expressionGenMethods = {
|
|
|
664
644
|
case "byte":
|
|
665
645
|
case "rune":
|
|
666
646
|
return "0";
|
|
647
|
+
case "complex64":
|
|
648
|
+
case "complex128":
|
|
649
|
+
return "{ re: 0, im: 0 }";
|
|
667
650
|
case "string":
|
|
668
651
|
return '""';
|
|
669
652
|
case "bool":
|
|
@@ -710,10 +693,8 @@ export const expressionGenMethods = {
|
|
|
710
693
|
zeroValueForType(t) {
|
|
711
694
|
if (!t) return "null";
|
|
712
695
|
switch (t.kind) {
|
|
713
|
-
case "basic":
|
|
714
|
-
|
|
715
|
-
return basic !== null ? basic : "null";
|
|
716
|
-
}
|
|
696
|
+
case "basic":
|
|
697
|
+
return this._zeroForBasicName(t.name) ?? "null";
|
|
717
698
|
case "slice":
|
|
718
699
|
return "null";
|
|
719
700
|
case "map":
|
|
@@ -760,7 +741,7 @@ export const expressionGenMethods = {
|
|
|
760
741
|
case "nil":
|
|
761
742
|
return `${val} === null`;
|
|
762
743
|
case "error":
|
|
763
|
-
return `typeof ${val} === "
|
|
744
|
+
return `(typeof ${val} === "object" && ${val} !== null && typeof ${val}.Error === "function")`;
|
|
764
745
|
default:
|
|
765
746
|
if (this.structNames.has(typeNode.name))
|
|
766
747
|
return `${val} instanceof ${typeNode.name}`;
|
|
@@ -788,6 +769,13 @@ export const expressionGenMethods = {
|
|
|
788
769
|
return "null";
|
|
789
770
|
},
|
|
790
771
|
|
|
772
|
+
// Returns true if the type is a struct or array (uses value comparison with __equal).
|
|
773
|
+
_isStructOrArrayType(t) {
|
|
774
|
+
if (!t) return false;
|
|
775
|
+
const base = t.kind === "named" ? t.underlying : t;
|
|
776
|
+
return base?.kind === "struct" || base?.kind === "array";
|
|
777
|
+
},
|
|
778
|
+
|
|
791
779
|
typeComment(typeNode) {
|
|
792
780
|
if (!typeNode) return "unknown";
|
|
793
781
|
switch (typeNode.kind) {
|
|
@@ -801,4 +789,311 @@ export const expressionGenMethods = {
|
|
|
801
789
|
return typeNode.kind;
|
|
802
790
|
}
|
|
803
791
|
},
|
|
792
|
+
|
|
793
|
+
// Dispatch table for stdlib namespace calls (pkg.Func → inline JS).
|
|
794
|
+
// Returns the generated JS string, or undefined if not handled.
|
|
795
|
+
_genStdlibCall(ns, fn, expr) {
|
|
796
|
+
const a = () => expr.args.map((e) => this.genExpr(e));
|
|
797
|
+
switch (ns) {
|
|
798
|
+
case "fmt":
|
|
799
|
+
return this._genFmt(fn, a, expr);
|
|
800
|
+
case "strings":
|
|
801
|
+
return this._genStrings(fn, a);
|
|
802
|
+
case "bytes":
|
|
803
|
+
return this._genBytes(fn, a);
|
|
804
|
+
case "strconv":
|
|
805
|
+
return this._genStrconv(fn, a);
|
|
806
|
+
case "sort":
|
|
807
|
+
return this._genSort(fn, a);
|
|
808
|
+
case "math":
|
|
809
|
+
return this._genMath(fn, a);
|
|
810
|
+
case "unicode":
|
|
811
|
+
return this._genUnicode(fn, a);
|
|
812
|
+
case "os":
|
|
813
|
+
return this._genOs(fn, a);
|
|
814
|
+
case "errors":
|
|
815
|
+
return this._genErrors(fn, a);
|
|
816
|
+
case "time":
|
|
817
|
+
return this._genTime(fn, a);
|
|
818
|
+
default:
|
|
819
|
+
return undefined;
|
|
820
|
+
}
|
|
821
|
+
},
|
|
822
|
+
|
|
823
|
+
_genFmt(fn, _a, expr) {
|
|
824
|
+
const fmtArgs = expr.args.map((e) => this.genExpr(e)).join(", ");
|
|
825
|
+
switch (fn) {
|
|
826
|
+
case "Sprintf":
|
|
827
|
+
this._usesSprintf = true;
|
|
828
|
+
return `__sprintf(${fmtArgs})`;
|
|
829
|
+
case "Errorf": {
|
|
830
|
+
this._usesSprintf = true;
|
|
831
|
+
this._usesError = true;
|
|
832
|
+
const fmtStr = expr.args[0];
|
|
833
|
+
if (fmtStr?.kind === "BasicLit" && fmtStr.value?.includes("%w")) {
|
|
834
|
+
const lastArg = this.genExpr(expr.args[expr.args.length - 1]);
|
|
835
|
+
return `__error(__sprintf(${fmtArgs}), ${lastArg})`;
|
|
836
|
+
}
|
|
837
|
+
return `__error(__sprintf(${fmtArgs}))`;
|
|
838
|
+
}
|
|
839
|
+
case "Printf":
|
|
840
|
+
case "Print":
|
|
841
|
+
this._usesSprintf = true;
|
|
842
|
+
return `process?.stdout?.write(__sprintf(${fmtArgs}))`;
|
|
843
|
+
case "Println":
|
|
844
|
+
this._usesSprintf = true;
|
|
845
|
+
return `console.log(__sprintf(${fmtArgs}))`;
|
|
846
|
+
default:
|
|
847
|
+
return undefined;
|
|
848
|
+
}
|
|
849
|
+
},
|
|
850
|
+
|
|
851
|
+
_genStrings(fn, a) {
|
|
852
|
+
const args = a();
|
|
853
|
+
switch (fn) {
|
|
854
|
+
case "Contains":
|
|
855
|
+
return `${args[0]}.includes(${args[1]})`;
|
|
856
|
+
case "HasPrefix":
|
|
857
|
+
return `${args[0]}.startsWith(${args[1]})`;
|
|
858
|
+
case "HasSuffix":
|
|
859
|
+
return `${args[0]}.endsWith(${args[1]})`;
|
|
860
|
+
case "Index":
|
|
861
|
+
return `${args[0]}.indexOf(${args[1]})`;
|
|
862
|
+
case "LastIndex":
|
|
863
|
+
return `${args[0]}.lastIndexOf(${args[1]})`;
|
|
864
|
+
case "Count":
|
|
865
|
+
return `${args[0]}.split(${args[1]}).length - 1`;
|
|
866
|
+
case "Repeat":
|
|
867
|
+
return `${args[0]}.repeat(${args[1]})`;
|
|
868
|
+
case "Replace":
|
|
869
|
+
return `${args[0]}.replace(${args[1]}, ${args[2]})`;
|
|
870
|
+
case "ReplaceAll":
|
|
871
|
+
return `${args[0]}.replaceAll(${args[1]}, ${args[2]})`;
|
|
872
|
+
case "ToUpper":
|
|
873
|
+
return `${args[0]}.toUpperCase()`;
|
|
874
|
+
case "ToLower":
|
|
875
|
+
return `${args[0]}.toLowerCase()`;
|
|
876
|
+
case "TrimSpace":
|
|
877
|
+
return `${args[0]}.trim()`;
|
|
878
|
+
case "Trim":
|
|
879
|
+
return `${args[0]}.replace(new RegExp(\`^[\${${args[1]}}]+|[\${${args[1]}}]+$\`, "g"), "")`;
|
|
880
|
+
case "TrimPrefix":
|
|
881
|
+
return `(${args[0]}.startsWith(${args[1]}) ? ${args[0]}.slice(${args[1]}.length) : ${args[0]})`;
|
|
882
|
+
case "TrimSuffix":
|
|
883
|
+
return `(${args[0]}.endsWith(${args[1]}) ? ${args[0]}.slice(0, -${args[1]}.length) : ${args[0]})`;
|
|
884
|
+
case "TrimLeft":
|
|
885
|
+
return `${args[0]}.replace(new RegExp(\`^[\${${args[1]}}]+\`), "")`;
|
|
886
|
+
case "TrimRight":
|
|
887
|
+
return `${args[0]}.replace(new RegExp(\`[\${${args[1]}}]+$\`), "")`;
|
|
888
|
+
case "Split":
|
|
889
|
+
return `${args[0]}.split(${args[1]})`;
|
|
890
|
+
case "Join":
|
|
891
|
+
return `${args[0]}.join(${args[1]})`;
|
|
892
|
+
case "EqualFold":
|
|
893
|
+
return `${args[0]}.toLowerCase() === ${args[1]}.toLowerCase()`;
|
|
894
|
+
default:
|
|
895
|
+
return undefined;
|
|
896
|
+
}
|
|
897
|
+
},
|
|
898
|
+
|
|
899
|
+
_genBytes(fn, a) {
|
|
900
|
+
const __bs = `(b => String.fromCharCode(...b))`;
|
|
901
|
+
const __sb = `(s => [...s].map(c => c.charCodeAt(0)))`;
|
|
902
|
+
const args = a();
|
|
903
|
+
switch (fn) {
|
|
904
|
+
case "Contains":
|
|
905
|
+
return `${__bs}(${args[0]}).includes(${__bs}(${args[1]}))`;
|
|
906
|
+
case "HasPrefix":
|
|
907
|
+
return `((b, p) => { for (let i = 0; i < p.length; i++) if (b[i] !== p[i]) return false; return b.length >= p.length; })(${args[0]}, ${args[1]})`;
|
|
908
|
+
case "HasSuffix":
|
|
909
|
+
return `((b, s) => { const off = b.length - s.length; if (off < 0) return false; for (let i = 0; i < s.length; i++) if (b[off + i] !== s[i]) return false; return true; })(${args[0]}, ${args[1]})`;
|
|
910
|
+
case "Index":
|
|
911
|
+
return `${__bs}(${args[0]}).indexOf(${__bs}(${args[1]}))`;
|
|
912
|
+
case "Count":
|
|
913
|
+
return `${__bs}(${args[0]}).split(${__bs}(${args[1]})).length - 1`;
|
|
914
|
+
case "Repeat":
|
|
915
|
+
return `${__sb}(${__bs}(${args[0]}).repeat(${args[1]}))`;
|
|
916
|
+
case "Replace":
|
|
917
|
+
return `((b, o, n, cnt) => { let s = ${__bs}(b), os = ${__bs}(o), ns = ${__bs}(n); if (cnt < 0) return ${__sb}(s.replaceAll(os, ns)); for (let i = 0; i < cnt; i++) s = s.replace(os, ns); return ${__sb}(s); })(${args[0]}, ${args[1]}, ${args[2]}, ${args[3]})`;
|
|
918
|
+
case "ToUpper":
|
|
919
|
+
return `${__sb}(${__bs}(${args[0]}).toUpperCase())`;
|
|
920
|
+
case "ToLower":
|
|
921
|
+
return `${__sb}(${__bs}(${args[0]}).toLowerCase())`;
|
|
922
|
+
case "TrimSpace":
|
|
923
|
+
return `${__sb}(${__bs}(${args[0]}).trim())`;
|
|
924
|
+
case "Trim":
|
|
925
|
+
return `${__sb}(${__bs}(${args[0]}).replace(new RegExp(\`^[\${${args[1]}}]+|[\${${args[1]}}]+$\`, "g"), ""))`;
|
|
926
|
+
case "Equal":
|
|
927
|
+
return `((a, b) => { if (a.length !== b.length) return false; for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false; return true; })(${args[0]}, ${args[1]})`;
|
|
928
|
+
case "Split":
|
|
929
|
+
return `${__bs}(${args[0]}).split(${__bs}(${args[1]})).map(p => ${__sb}(p))`;
|
|
930
|
+
case "Join":
|
|
931
|
+
return `${__sb}(${args[0]}.map(p => ${__bs}(p)).join(${__bs}(${args[1]})))`;
|
|
932
|
+
default:
|
|
933
|
+
return undefined;
|
|
934
|
+
}
|
|
935
|
+
},
|
|
936
|
+
|
|
937
|
+
_genStrconv(fn, a) {
|
|
938
|
+
const args = a();
|
|
939
|
+
switch (fn) {
|
|
940
|
+
case "Itoa":
|
|
941
|
+
return `String(${args[0]})`;
|
|
942
|
+
case "Atoi":
|
|
943
|
+
return `(Number.isNaN(Number(${args[0]})) ? [0, "invalid syntax"] : [Number(${args[0]}) | 0, null])`;
|
|
944
|
+
case "FormatBool":
|
|
945
|
+
return `String(${args[0]})`;
|
|
946
|
+
case "FormatInt":
|
|
947
|
+
return `(${args[0]}).toString(${args[1]})`;
|
|
948
|
+
case "FormatFloat":
|
|
949
|
+
return `String(${args[0]})`;
|
|
950
|
+
case "ParseFloat":
|
|
951
|
+
return `(Number.isNaN(Number(${args[0]})) ? [0, "invalid syntax"] : [Number(${args[0]}), null])`;
|
|
952
|
+
case "ParseInt":
|
|
953
|
+
return `(Number.isNaN(parseInt(${args[0]}, ${args[1]} || 10)) ? [0, "invalid syntax"] : [parseInt(${args[0]}, ${args[1]} || 10), null])`;
|
|
954
|
+
case "ParseBool":
|
|
955
|
+
return `(${args[0]} === "true" || ${args[0]} === "1" ? [true, null] : ${args[0]} === "false" || ${args[0]} === "0" ? [false, null] : [false, "invalid syntax"])`;
|
|
956
|
+
default:
|
|
957
|
+
return undefined;
|
|
958
|
+
}
|
|
959
|
+
},
|
|
960
|
+
|
|
961
|
+
_genSort(fn, a) {
|
|
962
|
+
const args = a();
|
|
963
|
+
switch (fn) {
|
|
964
|
+
case "Ints":
|
|
965
|
+
case "Float64s":
|
|
966
|
+
return `${args[0]}.sort((a, b) => a - b)`;
|
|
967
|
+
case "Strings":
|
|
968
|
+
return `${args[0]}.sort()`;
|
|
969
|
+
case "Slice":
|
|
970
|
+
case "SliceStable":
|
|
971
|
+
return `${args[0]}.sort((a, b) => ${args[1]}(a, b) ? -1 : ${args[1]}(b, a) ? 1 : 0)`;
|
|
972
|
+
case "SliceIsSorted":
|
|
973
|
+
return `${args[0]}.every((v, i, a) => i === 0 || ${args[1]}(a[i - 1], v))`;
|
|
974
|
+
default:
|
|
975
|
+
return undefined;
|
|
976
|
+
}
|
|
977
|
+
},
|
|
978
|
+
|
|
979
|
+
_genMath(fn, a) {
|
|
980
|
+
const args = a();
|
|
981
|
+
switch (fn) {
|
|
982
|
+
case "Abs":
|
|
983
|
+
return `Math.abs(${args[0]})`;
|
|
984
|
+
case "Floor":
|
|
985
|
+
return `Math.floor(${args[0]})`;
|
|
986
|
+
case "Ceil":
|
|
987
|
+
return `Math.ceil(${args[0]})`;
|
|
988
|
+
case "Round":
|
|
989
|
+
return `Math.round(${args[0]})`;
|
|
990
|
+
case "Sqrt":
|
|
991
|
+
return `Math.sqrt(${args[0]})`;
|
|
992
|
+
case "Cbrt":
|
|
993
|
+
return `Math.cbrt(${args[0]})`;
|
|
994
|
+
case "Pow":
|
|
995
|
+
return `Math.pow(${args[0]}, ${args[1]})`;
|
|
996
|
+
case "Log":
|
|
997
|
+
return `Math.log(${args[0]})`;
|
|
998
|
+
case "Log2":
|
|
999
|
+
return `Math.log2(${args[0]})`;
|
|
1000
|
+
case "Log10":
|
|
1001
|
+
return `Math.log10(${args[0]})`;
|
|
1002
|
+
case "Sin":
|
|
1003
|
+
return `Math.sin(${args[0]})`;
|
|
1004
|
+
case "Cos":
|
|
1005
|
+
return `Math.cos(${args[0]})`;
|
|
1006
|
+
case "Tan":
|
|
1007
|
+
return `Math.tan(${args[0]})`;
|
|
1008
|
+
case "Min":
|
|
1009
|
+
return `Math.min(${args[0]}, ${args[1]})`;
|
|
1010
|
+
case "Max":
|
|
1011
|
+
return `Math.max(${args[0]}, ${args[1]})`;
|
|
1012
|
+
case "Mod":
|
|
1013
|
+
return `${args[0]} % ${args[1]}`;
|
|
1014
|
+
case "Inf":
|
|
1015
|
+
return `(${args[0]} >= 0 ? Infinity : -Infinity)`;
|
|
1016
|
+
case "IsNaN":
|
|
1017
|
+
return `Number.isNaN(${args[0]})`;
|
|
1018
|
+
case "IsInf":
|
|
1019
|
+
return `(${args[1]} > 0 ? ${args[0]} === Infinity : ${args[1]} < 0 ? ${args[0]} === -Infinity : !Number.isFinite(${args[0]}))`;
|
|
1020
|
+
case "NaN":
|
|
1021
|
+
return "NaN";
|
|
1022
|
+
default:
|
|
1023
|
+
return undefined;
|
|
1024
|
+
}
|
|
1025
|
+
},
|
|
1026
|
+
|
|
1027
|
+
_genUnicode(fn, a) {
|
|
1028
|
+
const args = a();
|
|
1029
|
+
const cp = `String.fromCodePoint(${args[0]})`;
|
|
1030
|
+
switch (fn) {
|
|
1031
|
+
case "IsLetter":
|
|
1032
|
+
return `/\\p{L}/u.test(${cp})`;
|
|
1033
|
+
case "IsDigit":
|
|
1034
|
+
return `/\\p{Nd}/u.test(${cp})`;
|
|
1035
|
+
case "IsSpace":
|
|
1036
|
+
return `/\\s/.test(${cp})`;
|
|
1037
|
+
case "IsUpper":
|
|
1038
|
+
return `((__c) => __c === __c.toUpperCase() && /\\p{L}/u.test(__c))(${cp})`;
|
|
1039
|
+
case "IsLower":
|
|
1040
|
+
return `((__c) => __c === __c.toLowerCase() && /\\p{L}/u.test(__c))(${cp})`;
|
|
1041
|
+
case "IsPunct":
|
|
1042
|
+
return `/\\p{P}/u.test(${cp})`;
|
|
1043
|
+
case "IsControl":
|
|
1044
|
+
return `/\\p{Cc}/u.test(${cp})`;
|
|
1045
|
+
case "IsPrint":
|
|
1046
|
+
return `!/\\p{Cc}/u.test(${cp})`;
|
|
1047
|
+
case "IsGraphic":
|
|
1048
|
+
return `!/\\p{Cc}/u.test(${cp})`;
|
|
1049
|
+
case "ToUpper":
|
|
1050
|
+
return `${cp}.toUpperCase().codePointAt(0)`;
|
|
1051
|
+
case "ToLower":
|
|
1052
|
+
return `${cp}.toLowerCase().codePointAt(0)`;
|
|
1053
|
+
default:
|
|
1054
|
+
return undefined;
|
|
1055
|
+
}
|
|
1056
|
+
},
|
|
1057
|
+
|
|
1058
|
+
_genOs(fn, a) {
|
|
1059
|
+
const args = a();
|
|
1060
|
+
switch (fn) {
|
|
1061
|
+
case "Exit":
|
|
1062
|
+
return `process.exit(${args[0]})`;
|
|
1063
|
+
case "Getenv":
|
|
1064
|
+
return `(process.env[${args[0]}] ?? "")`;
|
|
1065
|
+
default:
|
|
1066
|
+
return undefined;
|
|
1067
|
+
}
|
|
1068
|
+
},
|
|
1069
|
+
|
|
1070
|
+
_genErrors(fn, a) {
|
|
1071
|
+
const args = a();
|
|
1072
|
+
switch (fn) {
|
|
1073
|
+
case "New":
|
|
1074
|
+
this._usesError = true;
|
|
1075
|
+
return `__error(${args[0]})`;
|
|
1076
|
+
case "Is":
|
|
1077
|
+
this._usesErrorIs = true;
|
|
1078
|
+
return `__errorIs(${args[0]}, ${args[1]})`;
|
|
1079
|
+
case "Unwrap":
|
|
1080
|
+
return `(${args[0]}?._cause ?? null)`;
|
|
1081
|
+
default:
|
|
1082
|
+
return undefined;
|
|
1083
|
+
}
|
|
1084
|
+
},
|
|
1085
|
+
|
|
1086
|
+
_genTime(fn, a) {
|
|
1087
|
+
const args = a();
|
|
1088
|
+
switch (fn) {
|
|
1089
|
+
case "Now":
|
|
1090
|
+
return "Date.now()";
|
|
1091
|
+
case "Since":
|
|
1092
|
+
return `(Date.now() - ${args[0]})`;
|
|
1093
|
+
case "Sleep":
|
|
1094
|
+
return `await new Promise(r => setTimeout(r, ${args[0]} / 1000000))`;
|
|
1095
|
+
default:
|
|
1096
|
+
return undefined;
|
|
1097
|
+
}
|
|
1098
|
+
},
|
|
804
1099
|
};
|