gofront 0.0.9 → 1.0.1
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 +5 -5
- package/package.json +7 -4
- package/src/cli-core.js +65 -0
- package/src/codegen/expressions.js +523 -1717
- package/src/codegen/index.js +576 -0
- package/src/codegen/runtime.js +151 -0
- package/src/codegen/statements.js +308 -296
- package/src/codegen/stdlib/builder.js +39 -0
- package/src/codegen/stdlib/bytes.js +65 -0
- package/src/codegen/stdlib/errors.js +20 -0
- package/src/codegen/stdlib/fmt.js +104 -0
- package/src/codegen/stdlib/gom.js +155 -0
- package/src/codegen/stdlib/html.js +20 -0
- package/src/codegen/stdlib/index.js +98 -0
- package/src/codegen/stdlib/io.js +36 -0
- package/src/codegen/stdlib/maps.js +45 -0
- package/src/codegen/stdlib/math.js +56 -0
- package/src/codegen/stdlib/os.js +13 -0
- package/src/codegen/stdlib/path.js +34 -0
- package/src/codegen/stdlib/rand.js +27 -0
- package/src/codegen/stdlib/regexp.js +54 -0
- package/src/codegen/stdlib/slices.js +51 -0
- package/src/codegen/stdlib/sort.js +31 -0
- package/src/codegen/stdlib/strconv.js +33 -0
- package/src/codegen/stdlib/strings.js +86 -0
- package/src/codegen/stdlib/time.js +57 -0
- package/src/codegen/stdlib/unicode.js +25 -0
- package/src/codegen/stdlib/utf8.js +26 -0
- package/src/codegen/templ.js +284 -0
- package/src/compiler.js +142 -77
- package/src/dts-parser.js +355 -371
- package/src/index.js +32 -162
- package/src/lexer.js +186 -272
- package/src/minifier.js +305 -318
- package/src/{parser.js → parser/declarations.js} +18 -175
- package/src/parser/expressions.js +51 -34
- package/src/parser/index.js +161 -0
- package/src/parser/statements.js +24 -34
- package/src/parser/types.js +31 -29
- package/src/templ-parser.js +2 -2
- package/src/tokens.js +109 -0
- package/src/typechecker/assignability.js +261 -0
- package/src/typechecker/expressions.js +568 -549
- package/src/typechecker/index.js +556 -0
- package/src/typechecker/resolve.js +428 -0
- package/src/typechecker/statements.js +270 -280
- package/src/typechecker/stdlib/core.js +592 -0
- package/src/typechecker/stdlib/extended.js +332 -0
- package/src/typechecker/stdlib.js +9 -1060
- package/src/typechecker/termination.js +86 -0
- package/src/typechecker/types.js +1 -0
- package/src/codegen.js +0 -882
- package/src/typechecker.js +0 -1299
|
@@ -2,6 +2,59 @@
|
|
|
2
2
|
|
|
3
3
|
import { ERROR, isComplex } from "../typechecker/types.js";
|
|
4
4
|
|
|
5
|
+
/** @typedef {import('./index.js').CodeGen} CodeGen */
|
|
6
|
+
|
|
7
|
+
// Namespace constants: pkg.Field → JS literal
|
|
8
|
+
const NS_CONSTANTS = {
|
|
9
|
+
math: {
|
|
10
|
+
Pi: "Math.PI",
|
|
11
|
+
E: "Math.E",
|
|
12
|
+
MaxFloat64: "Number.MAX_VALUE",
|
|
13
|
+
SmallestNonzeroFloat64: "Number.MIN_VALUE",
|
|
14
|
+
MaxInt: "Number.MAX_SAFE_INTEGER",
|
|
15
|
+
MinInt: "Number.MIN_SAFE_INTEGER",
|
|
16
|
+
},
|
|
17
|
+
io: {
|
|
18
|
+
EOF: '"EOF"',
|
|
19
|
+
Discard:
|
|
20
|
+
"{ WriteString(s) { return s.length; }, Write(b) { return b.length; } }",
|
|
21
|
+
},
|
|
22
|
+
os: { Args: "process.argv" },
|
|
23
|
+
time: {
|
|
24
|
+
Millisecond: "1000000",
|
|
25
|
+
Second: "1000000000",
|
|
26
|
+
Minute: "60000000000",
|
|
27
|
+
Hour: "3600000000000",
|
|
28
|
+
RFC3339: '"2006-01-02T15:04:05Z07:00"',
|
|
29
|
+
RFC3339Nano: '"2006-01-02T15:04:05.999999999Z07:00"',
|
|
30
|
+
DateOnly: '"2006-01-02"',
|
|
31
|
+
TimeOnly: '"15:04:05"',
|
|
32
|
+
DateTime: '"2006-01-02 15:04:05"',
|
|
33
|
+
UTC: "null",
|
|
34
|
+
Local: "null",
|
|
35
|
+
January: "1",
|
|
36
|
+
February: "2",
|
|
37
|
+
March: "3",
|
|
38
|
+
April: "4",
|
|
39
|
+
May: "5",
|
|
40
|
+
June: "6",
|
|
41
|
+
July: "7",
|
|
42
|
+
August: "8",
|
|
43
|
+
September: "9",
|
|
44
|
+
October: "10",
|
|
45
|
+
November: "11",
|
|
46
|
+
December: "12",
|
|
47
|
+
Sunday: "0",
|
|
48
|
+
Monday: "1",
|
|
49
|
+
Tuesday: "2",
|
|
50
|
+
Wednesday: "3",
|
|
51
|
+
Thursday: "4",
|
|
52
|
+
Friday: "5",
|
|
53
|
+
Saturday: "6",
|
|
54
|
+
},
|
|
55
|
+
utf8: { RuneError: "0xFFFD", MaxRune: "0x10FFFF", UTFMax: "4" },
|
|
56
|
+
};
|
|
57
|
+
|
|
5
58
|
const INT_TYPE_NAMES = new Set([
|
|
6
59
|
"int",
|
|
7
60
|
"uint",
|
|
@@ -18,553 +71,326 @@ const INT_TYPE_NAMES = new Set([
|
|
|
18
71
|
"rune",
|
|
19
72
|
]);
|
|
20
73
|
|
|
74
|
+
// Zero-value JS literals for basic Go types.
|
|
75
|
+
const ZERO_FOR_BASIC = {
|
|
76
|
+
int: "0",
|
|
77
|
+
uint: "0",
|
|
78
|
+
int8: "0",
|
|
79
|
+
int16: "0",
|
|
80
|
+
int32: "0",
|
|
81
|
+
int64: "0",
|
|
82
|
+
uint8: "0",
|
|
83
|
+
uint16: "0",
|
|
84
|
+
uint32: "0",
|
|
85
|
+
uint64: "0",
|
|
86
|
+
uintptr: "0",
|
|
87
|
+
float32: "0",
|
|
88
|
+
float64: "0",
|
|
89
|
+
byte: "0",
|
|
90
|
+
rune: "0",
|
|
91
|
+
complex64: "{ re: 0, im: 0 }",
|
|
92
|
+
complex128: "{ re: 0, im: 0 }",
|
|
93
|
+
string: '""',
|
|
94
|
+
bool: "false",
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Set of all numeric type names (for type-assertion codegen).
|
|
98
|
+
const TYPEOF_NUMBER_NAMES = new Set([...INT_TYPE_NAMES, "float32", "float64"]);
|
|
99
|
+
|
|
100
|
+
// Builtin function codegen — keyed by builtin name, value is (self, expr) => string.
|
|
101
|
+
const BUILTIN_GEN = {
|
|
102
|
+
append: (s, e) => s.genAppend(e),
|
|
103
|
+
len: (s, e) => s._genBuiltinLen(e),
|
|
104
|
+
cap: (s, e) => `${s.genExpr(e.args[0])}.length`,
|
|
105
|
+
make: (s, e) => s.genMake(e),
|
|
106
|
+
delete: (s, e) => {
|
|
107
|
+
const [m, k] = e.args.map((a) => s.genExpr(a));
|
|
108
|
+
return `(delete ${m}[${k}])`;
|
|
109
|
+
},
|
|
110
|
+
copy: (s, e) => {
|
|
111
|
+
const [dst, src] = e.args.map((a) => s.genExpr(a));
|
|
112
|
+
return `((__cd,__cs)=>{const n=Math.min(__cd.length,__cs.length);__cd.splice(0,n,...__cs.slice(0,n));return n;})(${dst},${src})`;
|
|
113
|
+
},
|
|
114
|
+
new: (s, e) => `{ value: ${s.zeroValueForExpr(e.args[0])} }`,
|
|
115
|
+
print: (s, e) => `console.log(${e.args.map((a) => s.genExpr(a)).join(", ")})`,
|
|
116
|
+
println: (s, e) =>
|
|
117
|
+
`console.log(${e.args.map((a) => s.genExpr(a)).join(", ")})`,
|
|
118
|
+
panic: (s, e) => `(() => { throw new Error(${s.genExpr(e.args[0])}); })()`,
|
|
119
|
+
recover: () =>
|
|
120
|
+
`(typeof __panic !== "undefined" && __panic !== null ? (() => { const __r = __panic.message ?? String(__panic); __panic = null; return __r; })() : null)`,
|
|
121
|
+
error: (s, e) => {
|
|
122
|
+
s._usesError = true;
|
|
123
|
+
return `__error(${s.genExpr(e.args[0])})`;
|
|
124
|
+
},
|
|
125
|
+
min: (s, e) => `Math.min(${e.args.map((a) => s.genExpr(a)).join(", ")})`,
|
|
126
|
+
max: (s, e) => `Math.max(${e.args.map((a) => s.genExpr(a)).join(", ")})`,
|
|
127
|
+
clear: (s, e) => s._genBuiltinClear(e),
|
|
128
|
+
complex: (s, e) =>
|
|
129
|
+
`{ re: ${s.genExpr(e.args[0])}, im: ${s.genExpr(e.args[1])} }`,
|
|
130
|
+
real: (s, e) => `(${s.genExpr(e.args[0])}).re`,
|
|
131
|
+
imag: (s, e) => `(${s.genExpr(e.args[0])}).im`,
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// Method-name dispatch for genExpr — string values add no static call edges.
|
|
135
|
+
const EXPR_GEN_DELEGATE = {
|
|
136
|
+
UnaryExpr: "_genUnaryExpr",
|
|
137
|
+
BinaryExpr: "_genBinaryExpr",
|
|
138
|
+
CallExpr: "genCall",
|
|
139
|
+
SelectorExpr: "_genSelectorExpr",
|
|
140
|
+
IndexExpr: "_genIndexExpr",
|
|
141
|
+
SliceExpr: "_genSliceExpr",
|
|
142
|
+
CompositeLit: "genCompositeLit",
|
|
143
|
+
FuncLit: "_genFuncLit",
|
|
144
|
+
TypeConversion: "_genTypeConversion",
|
|
145
|
+
TypeAssertExpr: "_genTypeAssertExpr",
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/** @type {ThisType<CodeGen>} */
|
|
21
149
|
export const expressionGenMethods = {
|
|
22
150
|
genExpr(expr) {
|
|
23
151
|
switch (expr.kind) {
|
|
24
152
|
case "BasicLit":
|
|
25
153
|
if (expr.litKind === "STRING") return JSON.stringify(expr.value);
|
|
26
154
|
return expr.value; // int, float, bool, nil(null)
|
|
27
|
-
|
|
28
155
|
case "ImagLit":
|
|
29
156
|
return `{ re: 0, im: ${expr.value} }`;
|
|
30
|
-
|
|
31
157
|
case "Ident":
|
|
32
158
|
if (this._boxedVars.has(expr.name) && !expr._isAddressOf)
|
|
33
159
|
return `${expr.name}.value`;
|
|
34
160
|
return expr.name;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const op = expr.op === "^" ? "~" : expr.op; // bitwise NOT
|
|
38
|
-
// Address-of: &x
|
|
39
|
-
if (op === "&") {
|
|
40
|
-
if (expr.operand.kind === "Ident") {
|
|
41
|
-
// Mark so Ident codegen does NOT append .value
|
|
42
|
-
expr.operand._isAddressOf = true;
|
|
43
|
-
// For boxed vars, &x returns the box itself
|
|
44
|
-
if (this._boxedVars.has(expr.operand.name)) {
|
|
45
|
-
return this.genExpr(expr.operand);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
// For non-boxed vars (structs, etc.), wrap in { value: x }
|
|
49
|
-
return `{ value: ${this.genExpr(expr.operand)} }`;
|
|
50
|
-
}
|
|
51
|
-
// Dereference: *p → p.value
|
|
52
|
-
if (op === "*") {
|
|
53
|
-
return `${this.genExpr(expr.operand)}.value`;
|
|
54
|
-
}
|
|
55
|
-
// Unary minus/plus on complex
|
|
56
|
-
if ((op === "-" || op === "+") && isComplex(expr.operand._type)) {
|
|
57
|
-
const inner = this.genExpr(expr.operand);
|
|
58
|
-
if (op === "-") return `{ re: -${inner}.re, im: -${inner}.im }`;
|
|
59
|
-
return inner;
|
|
60
|
-
}
|
|
61
|
-
return `${op}${this.genExpr(expr.operand)}`;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
case "BinaryExpr": {
|
|
65
|
-
// Complex binary operations
|
|
66
|
-
if (
|
|
67
|
-
isComplex(expr._type) ||
|
|
68
|
-
isComplex(expr.left._type) ||
|
|
69
|
-
isComplex(expr.right._type)
|
|
70
|
-
) {
|
|
71
|
-
const l = this._genComplexOperand(expr.left);
|
|
72
|
-
const r = this._genComplexOperand(expr.right);
|
|
73
|
-
switch (expr.op) {
|
|
74
|
-
case "+":
|
|
75
|
-
return `{ re: ${l}.re + ${r}.re, im: ${l}.im + ${r}.im }`;
|
|
76
|
-
case "-":
|
|
77
|
-
return `{ re: ${l}.re - ${r}.re, im: ${l}.im - ${r}.im }`;
|
|
78
|
-
case "*":
|
|
79
|
-
this._usesCmul = true;
|
|
80
|
-
return `__cmul(${l}, ${r})`;
|
|
81
|
-
case "/":
|
|
82
|
-
this._usesCdiv = true;
|
|
83
|
-
return `__cdiv(${l}, ${r})`;
|
|
84
|
-
case "==":
|
|
85
|
-
return `(${l}.re === ${r}.re && ${l}.im === ${r}.im)`;
|
|
86
|
-
case "!=":
|
|
87
|
-
return `(${l}.re !== ${r}.re || ${l}.im !== ${r}.im)`;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
const l = this.genExpr(expr.left);
|
|
91
|
-
const r = this.genExpr(expr.right);
|
|
92
|
-
// Integer division: only when both sides are int
|
|
93
|
-
if (
|
|
94
|
-
expr.op === "/" &&
|
|
95
|
-
this.isIntType(expr.left._type) &&
|
|
96
|
-
this.isIntType(expr.right._type)
|
|
97
|
-
) {
|
|
98
|
-
return `Math.trunc(${l} / ${r})`;
|
|
99
|
-
}
|
|
100
|
-
// Go &^ (bit clear / AND NOT) → JS & ~
|
|
101
|
-
if (expr.op === "&^") {
|
|
102
|
-
return `${l} & ~${r}`;
|
|
103
|
-
}
|
|
104
|
-
// Struct/array equality: use __equal for value comparison
|
|
105
|
-
if (expr.op === "==" || expr.op === "!=") {
|
|
106
|
-
const lt = expr.left._type;
|
|
107
|
-
if (this._isStructOrArrayType(lt)) {
|
|
108
|
-
this._usesEqual = true;
|
|
109
|
-
const cmp = `__equal(${l}, ${r})`;
|
|
110
|
-
return expr.op === "==" ? cmp : `!${cmp}`;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
// Go == and != are strict — map to JS === / !==
|
|
114
|
-
const op =
|
|
115
|
-
expr.op === "==" ? "===" : expr.op === "!=" ? "!==" : expr.op;
|
|
116
|
-
return `${l} ${op} ${r}`;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
case "CallExpr":
|
|
120
|
-
return this.genCall(expr);
|
|
121
|
-
|
|
161
|
+
case "AwaitExpr":
|
|
162
|
+
return `await ${this.genExpr(expr.expr)}`;
|
|
122
163
|
case "InstantiationExpr":
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (expr
|
|
128
|
-
|
|
129
|
-
return `((recv, ...args) => recv.${method}(...args))`;
|
|
130
|
-
}
|
|
131
|
-
// math constants
|
|
132
|
-
if (expr.expr.kind === "Ident" && expr.expr.name === "math") {
|
|
133
|
-
switch (expr.field) {
|
|
134
|
-
case "Pi":
|
|
135
|
-
return "Math.PI";
|
|
136
|
-
case "E":
|
|
137
|
-
return "Math.E";
|
|
138
|
-
case "MaxFloat64":
|
|
139
|
-
return "Number.MAX_VALUE";
|
|
140
|
-
case "SmallestNonzeroFloat64":
|
|
141
|
-
return "Number.MIN_VALUE";
|
|
142
|
-
case "MaxInt":
|
|
143
|
-
return "Number.MAX_SAFE_INTEGER";
|
|
144
|
-
case "MinInt":
|
|
145
|
-
return "Number.MIN_SAFE_INTEGER";
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
// io constants
|
|
149
|
-
if (expr.expr.kind === "Ident" && expr.expr.name === "io") {
|
|
150
|
-
switch (expr.field) {
|
|
151
|
-
case "EOF":
|
|
152
|
-
return '"EOF"';
|
|
153
|
-
case "Discard":
|
|
154
|
-
return "{ WriteString(s) { return s.length; }, Write(b) { return b.length; } }";
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
// os constants/vars
|
|
158
|
-
if (expr.expr.kind === "Ident" && expr.expr.name === "os") {
|
|
159
|
-
switch (expr.field) {
|
|
160
|
-
case "Args":
|
|
161
|
-
return "process.argv";
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
// time constants (nanosecond durations and layout strings)
|
|
165
|
-
if (expr.expr.kind === "Ident" && expr.expr.name === "time") {
|
|
166
|
-
switch (expr.field) {
|
|
167
|
-
case "Millisecond":
|
|
168
|
-
return "1000000";
|
|
169
|
-
case "Second":
|
|
170
|
-
return "1000000000";
|
|
171
|
-
case "Minute":
|
|
172
|
-
return "60000000000";
|
|
173
|
-
case "Hour":
|
|
174
|
-
return "3600000000000";
|
|
175
|
-
case "RFC3339":
|
|
176
|
-
return '"2006-01-02T15:04:05Z07:00"';
|
|
177
|
-
case "RFC3339Nano":
|
|
178
|
-
return '"2006-01-02T15:04:05.999999999Z07:00"';
|
|
179
|
-
case "DateOnly":
|
|
180
|
-
return '"2006-01-02"';
|
|
181
|
-
case "TimeOnly":
|
|
182
|
-
return '"15:04:05"';
|
|
183
|
-
case "DateTime":
|
|
184
|
-
return '"2006-01-02 15:04:05"';
|
|
185
|
-
case "UTC":
|
|
186
|
-
case "Local":
|
|
187
|
-
return "null";
|
|
188
|
-
case "January":
|
|
189
|
-
return "1";
|
|
190
|
-
case "February":
|
|
191
|
-
return "2";
|
|
192
|
-
case "March":
|
|
193
|
-
return "3";
|
|
194
|
-
case "April":
|
|
195
|
-
return "4";
|
|
196
|
-
case "May":
|
|
197
|
-
return "5";
|
|
198
|
-
case "June":
|
|
199
|
-
return "6";
|
|
200
|
-
case "July":
|
|
201
|
-
return "7";
|
|
202
|
-
case "August":
|
|
203
|
-
return "8";
|
|
204
|
-
case "September":
|
|
205
|
-
return "9";
|
|
206
|
-
case "October":
|
|
207
|
-
return "10";
|
|
208
|
-
case "November":
|
|
209
|
-
return "11";
|
|
210
|
-
case "December":
|
|
211
|
-
return "12";
|
|
212
|
-
case "Sunday":
|
|
213
|
-
return "0";
|
|
214
|
-
case "Monday":
|
|
215
|
-
return "1";
|
|
216
|
-
case "Tuesday":
|
|
217
|
-
return "2";
|
|
218
|
-
case "Wednesday":
|
|
219
|
-
return "3";
|
|
220
|
-
case "Thursday":
|
|
221
|
-
return "4";
|
|
222
|
-
case "Friday":
|
|
223
|
-
return "5";
|
|
224
|
-
case "Saturday":
|
|
225
|
-
return "6";
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
// utf8 constants
|
|
229
|
-
if (expr.expr.kind === "Ident" && expr.expr.name === "utf8") {
|
|
230
|
-
switch (expr.field) {
|
|
231
|
-
case "RuneError":
|
|
232
|
-
return "0xFFFD";
|
|
233
|
-
case "MaxRune":
|
|
234
|
-
return "0x10FFFF";
|
|
235
|
-
case "UTFMax":
|
|
236
|
-
return "4";
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
const base = this.genExpr(expr.expr);
|
|
240
|
-
// Bundled GoFront packages are inlined — drop the qualifier.
|
|
241
|
-
if (this.bundledPackages.has(base)) return expr.field;
|
|
242
|
-
// Pointer types: route through .value
|
|
243
|
-
if (expr.expr._type?.kind === "pointer" && expr.field !== "value") {
|
|
244
|
-
const sel = `${base}.value.${expr.field}`;
|
|
245
|
-
if (expr._isMethodValue && !expr._callee)
|
|
246
|
-
return `${sel}.bind(${base}.value)`;
|
|
247
|
-
return sel;
|
|
248
|
-
}
|
|
249
|
-
const sel = `${base}.${expr.field}`;
|
|
250
|
-
// Method used as value (not called) — bind the receiver
|
|
251
|
-
if (expr._isMethodValue && !expr._callee) return `${sel}.bind(${base})`;
|
|
252
|
-
return sel;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
case "IndexExpr": {
|
|
256
|
-
const exprType = expr.expr._type;
|
|
257
|
-
const wrapField = this._namedWrapperField(exprType, expr.expr);
|
|
258
|
-
const base = wrapField
|
|
259
|
-
? `${this.genExpr(expr.expr)}.${wrapField}`
|
|
260
|
-
: this.genExpr(expr.expr);
|
|
261
|
-
const idx = this.genExpr(expr.index);
|
|
262
|
-
if (expr._mapValueType && !expr._lvalue) {
|
|
263
|
-
const zero = this.zeroValueForType(expr._mapValueType);
|
|
264
|
-
// Use an IIFE to avoid double-evaluating base/key when either
|
|
265
|
-
// could be a function call or other expression with side effects.
|
|
266
|
-
if (this._hasCallExpr(expr.expr) || this._hasCallExpr(expr.index)) {
|
|
267
|
-
return `((__m, __k) => __m[__k] ?? ${zero})(${base}, ${idx})`;
|
|
268
|
-
}
|
|
269
|
-
return `(${base}[${idx}] ?? ${zero})`;
|
|
270
|
-
}
|
|
271
|
-
// String indexing returns a byte (charCodeAt), not a JS character
|
|
272
|
-
const isStr =
|
|
273
|
-
(exprType?.kind === "basic" && exprType.name === "string") ||
|
|
274
|
-
(exprType?.kind === "untyped" && exprType.base === "string");
|
|
275
|
-
if (isStr && !expr._lvalue) {
|
|
276
|
-
return `${base}.charCodeAt(${idx})`;
|
|
277
|
-
}
|
|
278
|
-
return `${base}[${idx}]`;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
case "SliceExpr": {
|
|
282
|
-
const base = this.genExpr(expr.expr);
|
|
283
|
-
const lo = expr.low ? this.genExpr(expr.low) : "";
|
|
284
|
-
const hi = expr.high ? this.genExpr(expr.high) : "";
|
|
285
|
-
if (!lo && !hi) return `${base}.slice()`;
|
|
286
|
-
if (!hi) return `${base}.slice(${lo})`;
|
|
287
|
-
if (!lo) return `${base}.slice(0, ${hi})`;
|
|
288
|
-
return `${base}.slice(${lo}, ${hi})`;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
case "CompositeLit":
|
|
292
|
-
return this.genCompositeLit(expr);
|
|
293
|
-
|
|
294
|
-
case "FuncLit": {
|
|
295
|
-
const params = expr.params.map((p) => p.name).join(", ");
|
|
296
|
-
const asyncPrefix = expr.async ? "async " : "";
|
|
297
|
-
const saved = this.out;
|
|
298
|
-
const prevBoxed = this._boxedVars;
|
|
299
|
-
this._boxedVars = new Set(prevBoxed); // inherit parent's boxed vars (closures)
|
|
300
|
-
this._scanAddressTaken(expr.body);
|
|
301
|
-
this.out = [];
|
|
302
|
-
this.indented(() => this._genBody(expr.body));
|
|
303
|
-
const body = this.out.join("\n");
|
|
304
|
-
this.out = saved;
|
|
305
|
-
this._boxedVars = prevBoxed;
|
|
306
|
-
return `${asyncPrefix}function(${params}) {\n${body}\n${" ".repeat(this.indent)}}`;
|
|
164
|
+
case "RangeExpr":
|
|
165
|
+
return this.genExpr(expr.expr);
|
|
166
|
+
default: {
|
|
167
|
+
const m = EXPR_GEN_DELEGATE[expr.kind];
|
|
168
|
+
if (m) return this[m](expr);
|
|
169
|
+
throw new Error(`CodeGen: unhandled expression kind '${expr.kind}'`);
|
|
307
170
|
}
|
|
171
|
+
}
|
|
172
|
+
},
|
|
308
173
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
174
|
+
_genUnaryExpr(expr) {
|
|
175
|
+
const op = expr.op === "^" ? "~" : expr.op; // bitwise NOT
|
|
176
|
+
// Address-of: &x
|
|
177
|
+
if (op === "&") {
|
|
178
|
+
if (expr.operand.kind === "Ident") {
|
|
179
|
+
// Mark so Ident codegen does NOT append .value
|
|
180
|
+
expr.operand._isAddressOf = true;
|
|
181
|
+
// For boxed vars, &x returns the box itself
|
|
182
|
+
if (this._boxedVars.has(expr.operand.name)) {
|
|
183
|
+
return this.genExpr(expr.operand);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// For non-boxed vars (structs, etc.), wrap in { value: x }
|
|
187
|
+
return `{ value: ${this.genExpr(expr.operand)} }`;
|
|
188
|
+
}
|
|
189
|
+
// Dereference: *p → p.value
|
|
190
|
+
if (op === "*") return `${this.genExpr(expr.operand)}.value`;
|
|
191
|
+
// Unary minus/plus on complex
|
|
192
|
+
if ((op === "-" || op === "+") && isComplex(expr.operand._type)) {
|
|
193
|
+
const inner = this.genExpr(expr.operand);
|
|
194
|
+
if (op === "-") return `{ re: -${inner}.re, im: -${inner}.im }`;
|
|
195
|
+
return inner;
|
|
196
|
+
}
|
|
197
|
+
return `${op}${this.genExpr(expr.operand)}`;
|
|
198
|
+
},
|
|
319
199
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
200
|
+
_genSelectorExpr(expr) {
|
|
201
|
+
if (expr._isMethodExpr) {
|
|
202
|
+
return `((recv, ...args) => recv.${expr.field}(...args))`;
|
|
203
|
+
}
|
|
204
|
+
if (expr.expr.kind === "Ident") {
|
|
205
|
+
const nsConst = NS_CONSTANTS[expr.expr.name]?.[expr.field];
|
|
206
|
+
if (nsConst !== undefined) return nsConst;
|
|
207
|
+
}
|
|
208
|
+
const base = this.genExpr(expr.expr);
|
|
209
|
+
if (this.bundledPackages.has(base)) return expr.field;
|
|
210
|
+
if (expr.expr._type?.kind === "pointer" && expr.field !== "value")
|
|
211
|
+
return this._genPointerSelectorField(expr, base);
|
|
212
|
+
const sel = `${base}.${expr.field}`;
|
|
213
|
+
if (expr._isMethodValue && !expr._callee) return `${sel}.bind(${base})`;
|
|
214
|
+
return sel;
|
|
215
|
+
},
|
|
332
216
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
}
|
|
340
|
-
// []rune(s) → Unicode code point array (only when source is a string)
|
|
341
|
-
if (elem === "rune" || elem === "int32" || elem === "int") {
|
|
342
|
-
const srcType = expr.expr._type;
|
|
343
|
-
if (
|
|
344
|
-
(srcType?.kind === "basic" && srcType?.name === "string") ||
|
|
345
|
-
(srcType?.kind === "untyped" && srcType?.base === "string")
|
|
346
|
-
) {
|
|
347
|
-
return `Array.from(${inner}, __c => __c.codePointAt(0))`;
|
|
348
|
-
}
|
|
349
|
-
return `Array.from(${inner})`;
|
|
350
|
-
}
|
|
351
|
-
return `Array.from(${inner})`;
|
|
352
|
-
}
|
|
217
|
+
_genPointerSelectorField(expr, base) {
|
|
218
|
+
const sel = `${base}.value.${expr.field}`;
|
|
219
|
+
if (expr._isMethodValue && !expr._callee)
|
|
220
|
+
return `${sel}.bind(${base}.value)`;
|
|
221
|
+
return sel;
|
|
222
|
+
},
|
|
353
223
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
const srcType = expr.expr._type;
|
|
368
|
-
if (srcType && this.isIntType(srcType)) {
|
|
369
|
-
return `String.fromCodePoint(${inner})`;
|
|
370
|
-
}
|
|
371
|
-
// string([]byte) → decode byte array to string
|
|
372
|
-
if (
|
|
373
|
-
srcType &&
|
|
374
|
-
srcType.kind === "slice" &&
|
|
375
|
-
srcType.elem?.name === "int"
|
|
376
|
-
) {
|
|
377
|
-
return `${inner}.map(c => String.fromCharCode(c)).join("")`;
|
|
378
|
-
}
|
|
379
|
-
return `String(${inner})`;
|
|
380
|
-
}
|
|
381
|
-
case "int":
|
|
382
|
-
case "byte":
|
|
383
|
-
case "rune":
|
|
384
|
-
return `Math.trunc(Number(${inner}))`;
|
|
385
|
-
case "float64":
|
|
386
|
-
return `Number(${inner})`;
|
|
387
|
-
case "bool":
|
|
388
|
-
return `Boolean(${inner})`;
|
|
389
|
-
default:
|
|
390
|
-
return inner;
|
|
391
|
-
}
|
|
392
|
-
}
|
|
224
|
+
_genIndexExpr(expr) {
|
|
225
|
+
const exprType = expr.expr._type;
|
|
226
|
+
const wrapField = this._namedWrapperField(exprType, expr.expr);
|
|
227
|
+
const base = wrapField
|
|
228
|
+
? `${this.genExpr(expr.expr)}.${wrapField}`
|
|
229
|
+
: this.genExpr(expr.expr);
|
|
230
|
+
const idx = this.genExpr(expr.index);
|
|
231
|
+
if (expr._mapValueType && !expr._lvalue)
|
|
232
|
+
return this._genMapIndexExpr(expr, base, idx);
|
|
233
|
+
if (this._isStringExprType(exprType) && !expr._lvalue)
|
|
234
|
+
return `${base}.charCodeAt(${idx})`;
|
|
235
|
+
return `${base}[${idx}]`;
|
|
236
|
+
},
|
|
393
237
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
return `(${check} ? ${val} : (() => { throw new Error("interface conversion: type assertion failed"); })())`;
|
|
401
|
-
}
|
|
402
|
-
// comma-ok: emit [value-or-zero, runtimeTypeCheck]
|
|
403
|
-
const zero = this.zeroValueForTypeNode(expr.type);
|
|
404
|
-
return `(${check} ? [${val}, true] : [${zero}, false])`;
|
|
405
|
-
}
|
|
238
|
+
_genMapIndexExpr(expr, base, idx) {
|
|
239
|
+
const zero = this.zeroValueForType(expr._mapValueType);
|
|
240
|
+
if (this._hasCallExpr(expr.expr) || this._hasCallExpr(expr.index))
|
|
241
|
+
return `((__m, __k) => __m[__k] ?? ${zero})(${base}, ${idx})`;
|
|
242
|
+
return `(${base}[${idx}] ?? ${zero})`;
|
|
243
|
+
},
|
|
406
244
|
|
|
407
|
-
|
|
408
|
-
|
|
245
|
+
_isStringExprType(exprType) {
|
|
246
|
+
return (
|
|
247
|
+
(exprType?.kind === "basic" && exprType.name === "string") ||
|
|
248
|
+
(exprType?.kind === "untyped" && exprType.base === "string")
|
|
249
|
+
);
|
|
250
|
+
},
|
|
409
251
|
|
|
410
|
-
|
|
411
|
-
|
|
252
|
+
_genSliceExpr(expr) {
|
|
253
|
+
const base = this.genExpr(expr.expr);
|
|
254
|
+
const lo = expr.low ? this.genExpr(expr.low) : "";
|
|
255
|
+
const hi = expr.high ? this.genExpr(expr.high) : "";
|
|
256
|
+
if (!lo && !hi) return `${base}.slice()`;
|
|
257
|
+
if (!hi) return `${base}.slice(${lo})`;
|
|
258
|
+
if (!lo) return `${base}.slice(0, ${hi})`;
|
|
259
|
+
return `${base}.slice(${lo}, ${hi})`;
|
|
260
|
+
},
|
|
412
261
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
262
|
+
_genFuncLit(expr) {
|
|
263
|
+
const params = expr.params.map((p) => p.name).join(", ");
|
|
264
|
+
const asyncPrefix = expr.async ? "async " : "";
|
|
265
|
+
const saved = this.out;
|
|
266
|
+
const prevBoxed = this._boxedVars;
|
|
267
|
+
this._boxedVars = new Set(prevBoxed); // inherit parent's boxed vars (closures)
|
|
268
|
+
this._scanAddressTaken(expr.body);
|
|
269
|
+
this.out = [];
|
|
270
|
+
this.indented(() => this._genBody(expr.body));
|
|
271
|
+
const body = this.out.join("\n");
|
|
272
|
+
this.out = saved;
|
|
273
|
+
this._boxedVars = prevBoxed;
|
|
274
|
+
return `${asyncPrefix}function(${params}) {\n${body}\n${" ".repeat(this.indent)}}`;
|
|
275
|
+
},
|
|
276
|
+
|
|
277
|
+
_genTypeAssertExpr(expr) {
|
|
278
|
+
const val = this.genExpr(expr.expr);
|
|
279
|
+
const check = this._typeCheckExpr(expr.type, val);
|
|
280
|
+
if (!expr._commaOk) {
|
|
281
|
+
// plain assertion: panic if check fails (matches Go behavior)
|
|
282
|
+
if (check === "true") return val; // can't check at runtime — pass through
|
|
283
|
+
return `(${check} ? ${val} : (() => { throw new Error("interface conversion: type assertion failed"); })())`;
|
|
284
|
+
}
|
|
285
|
+
// comma-ok: emit [value-or-zero, runtimeTypeCheck]
|
|
286
|
+
const zero = this.zeroValueForTypeNode(expr.type);
|
|
287
|
+
return `(${check} ? [${val}, true] : [${zero}, false])`;
|
|
288
|
+
},
|
|
289
|
+
|
|
290
|
+
_genTypeConversionCall(expr) {
|
|
291
|
+
const name = expr._conversionTargetType?.name;
|
|
292
|
+
const arg = this.genExpr(expr.args[0]);
|
|
293
|
+
if (name && this.namedWrapperNames.has(name)) return `new ${name}(${arg})`;
|
|
294
|
+
return arg;
|
|
416
295
|
},
|
|
417
296
|
|
|
418
297
|
genCall(expr) {
|
|
419
|
-
|
|
420
|
-
if (expr._isTypeConversion) {
|
|
421
|
-
const name = expr._conversionTargetType?.name;
|
|
422
|
-
const arg = this.genExpr(expr.args[0]);
|
|
423
|
-
if (name && this.namedWrapperNames.has(name)) {
|
|
424
|
-
return `new ${name}(${arg})`;
|
|
425
|
-
}
|
|
426
|
-
return arg; // non-wrapper named types: identity
|
|
427
|
-
}
|
|
298
|
+
if (expr._isTypeConversion) return this._genTypeConversionCall(expr);
|
|
428
299
|
|
|
429
|
-
// Unwrap InstantiationExpr for function calls: Foo[int](42) → Foo(42)
|
|
430
300
|
const funcExpr =
|
|
431
301
|
expr.func.kind === "InstantiationExpr" ? expr.func.expr : expr.func;
|
|
432
|
-
// Handle built-ins that need special JS translation
|
|
433
302
|
if (funcExpr.kind === "Ident") {
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
return this.genAppend(expr);
|
|
437
|
-
case "len": {
|
|
438
|
-
if (expr._constLen != null) return String(expr._constLen);
|
|
439
|
-
const arg = expr.args[0];
|
|
440
|
-
const t = arg?._type;
|
|
441
|
-
const wrapField = this._namedWrapperField(t, arg);
|
|
442
|
-
if (wrapField) {
|
|
443
|
-
return `${this.genExpr(arg)}.${wrapField}.length`;
|
|
444
|
-
}
|
|
445
|
-
const js = this.genExpr(arg);
|
|
446
|
-
if (t?.kind === "map") return `Object.keys(${js}).length`;
|
|
447
|
-
this._usesLen = true;
|
|
448
|
-
return `__len(${js})`;
|
|
449
|
-
}
|
|
450
|
-
case "cap":
|
|
451
|
-
return `${this.genExpr(expr.args[0])}.length`;
|
|
452
|
-
case "make":
|
|
453
|
-
return this.genMake(expr);
|
|
454
|
-
case "delete": {
|
|
455
|
-
const [m, k] = expr.args.map((a) => this.genExpr(a));
|
|
456
|
-
return `(delete ${m}[${k}])`;
|
|
457
|
-
}
|
|
458
|
-
case "copy": {
|
|
459
|
-
const [dst, src] = expr.args.map((a) => this.genExpr(a));
|
|
460
|
-
return `((__cd,__cs)=>{const n=Math.min(__cd.length,__cs.length);__cd.splice(0,n,...__cs.slice(0,n));return n;})(${dst},${src})`;
|
|
461
|
-
}
|
|
462
|
-
case "new": {
|
|
463
|
-
const zero = this.zeroValueForExpr(expr.args[0]);
|
|
464
|
-
return `{ value: ${zero} }`;
|
|
465
|
-
}
|
|
466
|
-
case "print":
|
|
467
|
-
case "println": {
|
|
468
|
-
const args = expr.args.map((a) => this.genExpr(a)).join(", ");
|
|
469
|
-
return `console.log(${args})`;
|
|
470
|
-
}
|
|
471
|
-
case "panic": {
|
|
472
|
-
const arg = this.genExpr(expr.args[0]);
|
|
473
|
-
return `(() => { throw new Error(${arg}); })()`;
|
|
474
|
-
}
|
|
475
|
-
case "recover":
|
|
476
|
-
return `(typeof __panic !== "undefined" && __panic !== null ? (() => { const __r = __panic.message ?? String(__panic); __panic = null; return __r; })() : null)`;
|
|
477
|
-
case "error": {
|
|
478
|
-
this._usesError = true;
|
|
479
|
-
const arg = this.genExpr(expr.args[0]);
|
|
480
|
-
return `__error(${arg})`;
|
|
481
|
-
}
|
|
482
|
-
case "min": {
|
|
483
|
-
const args = expr.args.map((a) => this.genExpr(a)).join(", ");
|
|
484
|
-
return `Math.min(${args})`;
|
|
485
|
-
}
|
|
486
|
-
case "max": {
|
|
487
|
-
const args = expr.args.map((a) => this.genExpr(a)).join(", ");
|
|
488
|
-
return `Math.max(${args})`;
|
|
489
|
-
}
|
|
490
|
-
case "clear": {
|
|
491
|
-
const arg = expr.args[0];
|
|
492
|
-
const t = arg?._type;
|
|
493
|
-
const js = this.genExpr(arg);
|
|
494
|
-
if (
|
|
495
|
-
t?.kind === "map" ||
|
|
496
|
-
(t?.kind === "named" && t.underlying?.kind === "map")
|
|
497
|
-
) {
|
|
498
|
-
return `((__m) => { for (const __k in __m) delete __m[__k]; })(${js})`;
|
|
499
|
-
}
|
|
500
|
-
return `(${js}).length = 0`;
|
|
501
|
-
}
|
|
502
|
-
case "complex": {
|
|
503
|
-
const re = this.genExpr(expr.args[0]);
|
|
504
|
-
const im = this.genExpr(expr.args[1]);
|
|
505
|
-
return `{ re: ${re}, im: ${im} }`;
|
|
506
|
-
}
|
|
507
|
-
case "real":
|
|
508
|
-
return `(${this.genExpr(expr.args[0])}).re`;
|
|
509
|
-
case "imag":
|
|
510
|
-
return `(${this.genExpr(expr.args[0])}).im`;
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
// error.Error() → real method call
|
|
515
|
-
if (
|
|
516
|
-
expr.func.kind === "SelectorExpr" &&
|
|
517
|
-
expr.func.field === "Error" &&
|
|
518
|
-
expr.func.expr._type &&
|
|
519
|
-
(expr.func.expr._type === ERROR ||
|
|
520
|
-
expr.func.expr._type?.name === "error" ||
|
|
521
|
-
(expr.func.expr._type?.kind === "named" &&
|
|
522
|
-
expr.func.expr._type?.underlying?.kind === "interface"))
|
|
523
|
-
) {
|
|
524
|
-
return `${this.genExpr(expr.func.expr)}.Error()`;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
// fmt.Sprintf / fmt.Printf / fmt.Println / fmt.Print / fmt.Errorf
|
|
528
|
-
// Standard library namespace dispatch — all follow the pattern:
|
|
529
|
-
// pkg.Func(args) → inline JS
|
|
530
|
-
if (expr.func.kind === "SelectorExpr" && expr.func.expr.kind === "Ident") {
|
|
531
|
-
const ns = expr.func.expr.name;
|
|
532
|
-
const fn = expr.func.field;
|
|
533
|
-
const result = this._genStdlibCall(ns, fn, expr);
|
|
534
|
-
if (result !== undefined) return result;
|
|
303
|
+
const builtin = this._genBuiltinIdent(funcExpr.name, expr);
|
|
304
|
+
if (builtin !== undefined) return builtin;
|
|
535
305
|
}
|
|
536
306
|
|
|
537
|
-
// strings.Builder / bytes.Buffer / regexp.Regexp method dispatch
|
|
538
307
|
if (expr.func.kind === "SelectorExpr") {
|
|
539
|
-
const
|
|
540
|
-
|
|
541
|
-
if (recvName === "strings.Builder" || recvName === "bytes.Buffer") {
|
|
542
|
-
const typeName = recvType?.name ?? recvType?.base?.name;
|
|
543
|
-
return this._genBuilderCall(typeName, expr.func.field, expr);
|
|
544
|
-
}
|
|
545
|
-
if (recvName === "regexp.Regexp") {
|
|
546
|
-
return this._genRegexpMethodCall(expr.func.field, expr);
|
|
547
|
-
}
|
|
548
|
-
if (recvName === "time.Time") {
|
|
549
|
-
return this._genTimeMethodCall(expr.func.field, expr);
|
|
550
|
-
}
|
|
308
|
+
const result = this._genSelectorCall(expr);
|
|
309
|
+
if (result !== undefined) return result;
|
|
551
310
|
}
|
|
552
311
|
|
|
553
|
-
// Mark the callee so SelectorExpr knows it's being called, not used as a value
|
|
554
312
|
expr.func._callee = true;
|
|
555
313
|
const rawFn = this.genExpr(expr.func);
|
|
556
|
-
// Wrap function literals in parens so `function(){}()` → `(function(){})()`
|
|
557
314
|
const fn = expr.func.kind === "FuncLit" ? `(${rawFn})` : rawFn;
|
|
558
|
-
|
|
559
|
-
if (expr._multiForward) {
|
|
560
|
-
return `${fn}(...${this.genExpr(expr.args[0])})`;
|
|
561
|
-
}
|
|
315
|
+
if (expr._multiForward) return `${fn}(...${this.genExpr(expr.args[0])})`;
|
|
562
316
|
const args = expr.args
|
|
563
317
|
.map((a) => (a._spread ? `...${this.genExpr(a)}` : this.genExpr(a)))
|
|
564
318
|
.join(", ");
|
|
565
319
|
return `${fn}(${args})`;
|
|
566
320
|
},
|
|
567
321
|
|
|
322
|
+
_isErrorInterfaceType(t) {
|
|
323
|
+
return t?.kind === "named" && t?.underlying?.kind === "interface";
|
|
324
|
+
},
|
|
325
|
+
|
|
326
|
+
_isErrorType(t) {
|
|
327
|
+
return (
|
|
328
|
+
t && (t === ERROR || t?.name === "error" || this._isErrorInterfaceType(t))
|
|
329
|
+
);
|
|
330
|
+
},
|
|
331
|
+
|
|
332
|
+
_isErrorMethodCall(expr) {
|
|
333
|
+
if (expr.func.field !== "Error") return false;
|
|
334
|
+
return this._isErrorType(expr.func.expr._type);
|
|
335
|
+
},
|
|
336
|
+
|
|
337
|
+
_resolveRecvName(recvType) {
|
|
338
|
+
return recvType?.name ?? recvType?.base?.name;
|
|
339
|
+
},
|
|
340
|
+
|
|
341
|
+
_genReceiverTypeCall(recvName, expr) {
|
|
342
|
+
if (recvName === "strings.Builder" || recvName === "bytes.Buffer")
|
|
343
|
+
return this._genBuilderCall(recvName, expr.func.field, expr);
|
|
344
|
+
if (recvName === "regexp.Regexp")
|
|
345
|
+
return this._genRegexpMethodCall(expr.func.field, expr);
|
|
346
|
+
if (recvName === "time.Time")
|
|
347
|
+
return this._genTimeMethodCall(expr.func.field, expr);
|
|
348
|
+
return undefined;
|
|
349
|
+
},
|
|
350
|
+
|
|
351
|
+
_genSelectorCall(expr) {
|
|
352
|
+
if (this._isErrorMethodCall(expr))
|
|
353
|
+
return `${this.genExpr(expr.func.expr)}.Error()`;
|
|
354
|
+
if (expr.func.expr.kind === "Ident") {
|
|
355
|
+
const ns = expr.func.expr.name;
|
|
356
|
+
const fn = expr.func.field;
|
|
357
|
+
const result = this._genStdlibCall(ns, fn, expr);
|
|
358
|
+
if (result !== undefined) return result;
|
|
359
|
+
}
|
|
360
|
+
const recvType = expr.func.expr._type;
|
|
361
|
+
const recvName = this._resolveRecvName(recvType);
|
|
362
|
+
return this._genReceiverTypeCall(recvName, expr);
|
|
363
|
+
},
|
|
364
|
+
|
|
365
|
+
_genBuiltinLen(expr) {
|
|
366
|
+
if (expr._constLen != null) return String(expr._constLen);
|
|
367
|
+
const arg = expr.args[0];
|
|
368
|
+
const t = arg?._type;
|
|
369
|
+
const wrapField = this._namedWrapperField(t, arg);
|
|
370
|
+
if (wrapField) return `${this.genExpr(arg)}.${wrapField}.length`;
|
|
371
|
+
const js = this.genExpr(arg);
|
|
372
|
+
if (t?.kind === "map") return `Object.keys(${js}).length`;
|
|
373
|
+
this._usesLen = true;
|
|
374
|
+
return `__len(${js})`;
|
|
375
|
+
},
|
|
376
|
+
|
|
377
|
+
_genBuiltinClear(expr) {
|
|
378
|
+
const arg = expr.args[0];
|
|
379
|
+
const t = arg?._type;
|
|
380
|
+
const js = this.genExpr(arg);
|
|
381
|
+
if (
|
|
382
|
+
t?.kind === "map" ||
|
|
383
|
+
(t?.kind === "named" && t.underlying?.kind === "map")
|
|
384
|
+
)
|
|
385
|
+
return `((__m) => { for (const __k in __m) delete __m[__k]; })(${js})`;
|
|
386
|
+
return `(${js}).length = 0`;
|
|
387
|
+
},
|
|
388
|
+
|
|
389
|
+
_genBuiltinIdent(name, expr) {
|
|
390
|
+
const gen = BUILTIN_GEN[name];
|
|
391
|
+
return gen ? gen(this, expr) : undefined;
|
|
392
|
+
},
|
|
393
|
+
|
|
568
394
|
genAppend(expr) {
|
|
569
395
|
const retType = expr._type;
|
|
570
396
|
const wrapField = this._namedWrapperField(retType, expr.args[0]);
|
|
@@ -631,74 +457,55 @@ export const expressionGenMethods = {
|
|
|
631
457
|
.join(", ");
|
|
632
458
|
},
|
|
633
459
|
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
// Implicit composite literal: {X: 1} or {1, 2} inside a slice/map — type inferred from context
|
|
638
|
-
if (t === null) {
|
|
639
|
-
const typeName = expr._type?.name ?? expr._type?.underlying?.name;
|
|
640
|
-
const hasPositional = expr.elems.some((e) => e._positionalField);
|
|
641
|
-
const hasKeyed = expr.elems.some((e) => e.kind === "KeyValueExpr");
|
|
642
|
-
if (hasPositional || hasKeyed) {
|
|
643
|
-
if (typeName && this.structNames.has(typeName)) {
|
|
644
|
-
return `new ${typeName}({ ${this._genStructFields(expr.elems)} })`;
|
|
645
|
-
}
|
|
646
|
-
const fields = expr.elems
|
|
647
|
-
.map((e) => {
|
|
648
|
-
if (e._positionalField)
|
|
649
|
-
return `${e._positionalField}: ${this.genExpr(e)}`;
|
|
650
|
-
return `${e.key.name ?? this.genExpr(e.key)}: ${this.genExpr(e.value)}`;
|
|
651
|
-
})
|
|
652
|
-
.join(", ");
|
|
653
|
-
return `{ ${fields} }`;
|
|
654
|
-
}
|
|
655
|
-
return `[${expr.elems.map((e) => this.genExpr(e)).join(", ")}]`;
|
|
656
|
-
}
|
|
460
|
+
_isSliceOrArrayType(t) {
|
|
461
|
+
return t?.kind === "SliceType" || t?.kind === "ArrayType";
|
|
462
|
+
},
|
|
657
463
|
|
|
658
|
-
|
|
464
|
+
_isMapTypeNode(t) {
|
|
465
|
+
return t?.kind === "MapType";
|
|
466
|
+
},
|
|
659
467
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
468
|
+
_genMapLitEntries(elems) {
|
|
469
|
+
return elems
|
|
470
|
+
.map((e) => {
|
|
471
|
+
if (e.kind === "KeyValueExpr") {
|
|
472
|
+
const k =
|
|
473
|
+
e.key.litKind === "STRING"
|
|
474
|
+
? JSON.stringify(e.key.value)
|
|
475
|
+
: `[${this.genExpr(e.key)}]`;
|
|
476
|
+
return `${k}: ${this.genExpr(e.value)}`;
|
|
477
|
+
}
|
|
478
|
+
return this.genExpr(e);
|
|
479
|
+
})
|
|
480
|
+
.join(", ");
|
|
481
|
+
},
|
|
664
482
|
|
|
665
|
-
|
|
666
|
-
if (
|
|
667
|
-
const
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
.map((e) => {
|
|
672
|
-
if (e.kind === "KeyValueExpr") {
|
|
673
|
-
const k =
|
|
674
|
-
e.key.litKind === "STRING"
|
|
675
|
-
? JSON.stringify(e.key.value)
|
|
676
|
-
: `[${this.genExpr(e.key)}]`;
|
|
677
|
-
return `${k}: ${this.genExpr(e.value)}`;
|
|
678
|
-
}
|
|
679
|
-
return this.genExpr(e);
|
|
680
|
-
})
|
|
681
|
-
.join(", ");
|
|
682
|
-
return `new ${typeName}({ ${entries} })`;
|
|
683
|
-
}
|
|
684
|
-
// Default: slice
|
|
685
|
-
const elems = expr.elems
|
|
686
|
-
.map((e) =>
|
|
687
|
-
e.kind === "KeyValueExpr" ? this.genExpr(e.value) : this.genExpr(e),
|
|
483
|
+
_genFallbackCompositeLit(expr) {
|
|
484
|
+
if (expr.elems.length > 0 && expr.elems[0]?.kind === "KeyValueExpr") {
|
|
485
|
+
const fields = expr.elems
|
|
486
|
+
.map(
|
|
487
|
+
(e) =>
|
|
488
|
+
`${e.key.name ?? this.genExpr(e.key)}: ${this.genExpr(e.value)}`,
|
|
688
489
|
)
|
|
689
490
|
.join(", ");
|
|
690
|
-
return `
|
|
491
|
+
return `{ ${fields} }`;
|
|
691
492
|
}
|
|
493
|
+
return `[${expr.elems.map((e) => this.genExpr(e)).join(", ")}]`;
|
|
494
|
+
},
|
|
692
495
|
|
|
693
|
-
|
|
496
|
+
genCompositeLit(expr) {
|
|
497
|
+
const t = expr.typeExpr;
|
|
498
|
+
if (t === null) return this._genImplicitCompositeLit(expr);
|
|
499
|
+
const typeName = this.getTypeName(t);
|
|
500
|
+
if (typeName && this.structNames.has(typeName))
|
|
501
|
+
return `new ${typeName}({ ${this._genStructFields(expr.elems)} })`;
|
|
502
|
+
if (typeName && this.namedWrapperNames.has(typeName))
|
|
503
|
+
return this._genNamedWrapperLit(typeName, expr);
|
|
694
504
|
if (expr.elems.some((e) => e._positionalField)) {
|
|
695
|
-
if (typeName)
|
|
505
|
+
if (typeName)
|
|
696
506
|
return `new ${typeName}({ ${this._genStructFields(expr.elems)} })`;
|
|
697
|
-
}
|
|
698
507
|
}
|
|
699
|
-
|
|
700
|
-
// Slice/array: [1, 2, 3]
|
|
701
|
-
if (t?.kind === "SliceType" || t?.kind === "ArrayType") {
|
|
508
|
+
if (this._isSliceOrArrayType(t)) {
|
|
702
509
|
const elems = expr.elems
|
|
703
510
|
.map((e) =>
|
|
704
511
|
e.kind === "KeyValueExpr" ? this.genExpr(e.value) : this.genExpr(e),
|
|
@@ -706,9 +513,35 @@ export const expressionGenMethods = {
|
|
|
706
513
|
.join(", ");
|
|
707
514
|
return `[${elems}]`;
|
|
708
515
|
}
|
|
516
|
+
if (this._isMapTypeNode(t))
|
|
517
|
+
return `{ ${this._genMapLitEntries(expr.elems)} }`;
|
|
518
|
+
return this._genFallbackCompositeLit(expr);
|
|
519
|
+
},
|
|
520
|
+
|
|
521
|
+
_genImplicitCompositeLit(expr) {
|
|
522
|
+
const typeName = expr._type?.name ?? expr._type?.underlying?.name;
|
|
523
|
+
const hasPositional = expr.elems.some((e) => e._positionalField);
|
|
524
|
+
const hasKeyed = expr.elems.some((e) => e.kind === "KeyValueExpr");
|
|
525
|
+
if (hasPositional || hasKeyed) {
|
|
526
|
+
if (typeName && this.structNames.has(typeName)) {
|
|
527
|
+
return `new ${typeName}({ ${this._genStructFields(expr.elems)} })`;
|
|
528
|
+
}
|
|
529
|
+
const fields = expr.elems
|
|
530
|
+
.map((e) => {
|
|
531
|
+
if (e._positionalField)
|
|
532
|
+
return `${e._positionalField}: ${this.genExpr(e)}`;
|
|
533
|
+
return `${e.key.name ?? this.genExpr(e.key)}: ${this.genExpr(e.value)}`;
|
|
534
|
+
})
|
|
535
|
+
.join(", ");
|
|
536
|
+
return `{ ${fields} }`;
|
|
537
|
+
}
|
|
538
|
+
return `[${expr.elems.map((e) => this.genExpr(e)).join(", ")}]`;
|
|
539
|
+
},
|
|
709
540
|
|
|
710
|
-
|
|
711
|
-
|
|
541
|
+
_genNamedWrapperLit(typeName, expr) {
|
|
542
|
+
const namedType = this.checker?.types.get(typeName);
|
|
543
|
+
const u = namedType?.underlying;
|
|
544
|
+
if (u?.kind === "map") {
|
|
712
545
|
const entries = expr.elems
|
|
713
546
|
.map((e) => {
|
|
714
547
|
if (e.kind === "KeyValueExpr") {
|
|
@@ -721,20 +554,15 @@ export const expressionGenMethods = {
|
|
|
721
554
|
return this.genExpr(e);
|
|
722
555
|
})
|
|
723
556
|
.join(", ");
|
|
724
|
-
return `{ ${entries} }`;
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
// Fallback: key-value pairs → plain object, positional → array
|
|
728
|
-
if (expr.elems.length > 0 && expr.elems[0]?.kind === "KeyValueExpr") {
|
|
729
|
-
const fields = expr.elems
|
|
730
|
-
.map(
|
|
731
|
-
(e) =>
|
|
732
|
-
`${e.key.name ?? this.genExpr(e.key)}: ${this.genExpr(e.value)}`,
|
|
733
|
-
)
|
|
734
|
-
.join(", ");
|
|
735
|
-
return `{ ${fields} }`;
|
|
557
|
+
return `new ${typeName}({ ${entries} })`;
|
|
736
558
|
}
|
|
737
|
-
|
|
559
|
+
// Default: slice
|
|
560
|
+
const elems = expr.elems
|
|
561
|
+
.map((e) =>
|
|
562
|
+
e.kind === "KeyValueExpr" ? this.genExpr(e.value) : this.genExpr(e),
|
|
563
|
+
)
|
|
564
|
+
.join(", ");
|
|
565
|
+
return `new ${typeName}([${elems}])`;
|
|
738
566
|
},
|
|
739
567
|
|
|
740
568
|
// ── Helpers ───────────────────────────────────────────────────
|
|
@@ -775,33 +603,14 @@ export const expressionGenMethods = {
|
|
|
775
603
|
|
|
776
604
|
// Returns the JS zero-value literal for a basic type name, or null if not a basic type.
|
|
777
605
|
_zeroForBasicName(name) {
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
case "uint16":
|
|
787
|
-
case "uint32":
|
|
788
|
-
case "uint64":
|
|
789
|
-
case "uintptr":
|
|
790
|
-
case "float32":
|
|
791
|
-
case "float64":
|
|
792
|
-
case "byte":
|
|
793
|
-
case "rune":
|
|
794
|
-
return "0";
|
|
795
|
-
case "complex64":
|
|
796
|
-
case "complex128":
|
|
797
|
-
return "{ re: 0, im: 0 }";
|
|
798
|
-
case "string":
|
|
799
|
-
return '""';
|
|
800
|
-
case "bool":
|
|
801
|
-
return "false";
|
|
802
|
-
default:
|
|
803
|
-
return null;
|
|
804
|
-
}
|
|
606
|
+
return Object.hasOwn(ZERO_FOR_BASIC, name) ? ZERO_FOR_BASIC[name] : null;
|
|
607
|
+
},
|
|
608
|
+
|
|
609
|
+
_zeroForNamedType(name) {
|
|
610
|
+
if (name === "strings.Builder") return '{ _buf: "" }';
|
|
611
|
+
if (name === "bytes.Buffer") return "{ _buf: [] }";
|
|
612
|
+
if (this.structNames.has(name)) return `new ${name}()`;
|
|
613
|
+
return "null";
|
|
805
614
|
},
|
|
806
615
|
|
|
807
616
|
zeroValueForTypeNode(typeNode) {
|
|
@@ -810,11 +619,7 @@ export const expressionGenMethods = {
|
|
|
810
619
|
case "TypeName": {
|
|
811
620
|
const basic = this._zeroForBasicName(typeNode.name);
|
|
812
621
|
if (basic !== null) return basic;
|
|
813
|
-
|
|
814
|
-
if (typeNode.name === "bytes.Buffer") return "{ _buf: [] }";
|
|
815
|
-
if (this.structNames.has(typeNode.name))
|
|
816
|
-
return `new ${typeNode.name}()`;
|
|
817
|
-
return "null";
|
|
622
|
+
return this._zeroForNamedType(typeNode.name);
|
|
818
623
|
}
|
|
819
624
|
case "SliceType":
|
|
820
625
|
return "null";
|
|
@@ -856,10 +661,7 @@ export const expressionGenMethods = {
|
|
|
856
661
|
return `{ ${fields} }`;
|
|
857
662
|
}
|
|
858
663
|
case "named":
|
|
859
|
-
|
|
860
|
-
if (t.name === "bytes.Buffer") return "{ _buf: [] }";
|
|
861
|
-
if (this.structNames.has(t.name)) return `new ${t.name}()`;
|
|
862
|
-
return "null";
|
|
664
|
+
return this._zeroForNamedType(t.name);
|
|
863
665
|
default:
|
|
864
666
|
return "null";
|
|
865
667
|
}
|
|
@@ -869,36 +671,15 @@ export const expressionGenMethods = {
|
|
|
869
671
|
_typeCheckExpr(typeNode, val) {
|
|
870
672
|
if (!typeNode) return "true";
|
|
871
673
|
if (typeNode.kind === "TypeName") {
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
case "uint32":
|
|
882
|
-
case "uint64":
|
|
883
|
-
case "uintptr":
|
|
884
|
-
case "float32":
|
|
885
|
-
case "float64":
|
|
886
|
-
case "byte":
|
|
887
|
-
case "rune":
|
|
888
|
-
return `typeof ${val} === "number"`;
|
|
889
|
-
case "string":
|
|
890
|
-
return `typeof ${val} === "string"`;
|
|
891
|
-
case "bool":
|
|
892
|
-
return `typeof ${val} === "boolean"`;
|
|
893
|
-
case "nil":
|
|
894
|
-
return `${val} === null`;
|
|
895
|
-
case "error":
|
|
896
|
-
return `(typeof ${val} === "object" && ${val} !== null && typeof ${val}.Error === "function")`;
|
|
897
|
-
default:
|
|
898
|
-
if (this.structNames.has(typeNode.name))
|
|
899
|
-
return `${val} instanceof ${typeNode.name}`;
|
|
900
|
-
return "true"; // unknown type — can't check at runtime
|
|
901
|
-
}
|
|
674
|
+
const name = typeNode.name;
|
|
675
|
+
if (TYPEOF_NUMBER_NAMES.has(name)) return `typeof ${val} === "number"`;
|
|
676
|
+
if (name === "string") return `typeof ${val} === "string"`;
|
|
677
|
+
if (name === "bool") return `typeof ${val} === "boolean"`;
|
|
678
|
+
if (name === "nil") return `${val} === null`;
|
|
679
|
+
if (name === "error")
|
|
680
|
+
return `(typeof ${val} === "object" && ${val} !== null && typeof ${val}.Error === "function")`;
|
|
681
|
+
if (this.structNames.has(name)) return `${val} instanceof ${name}`;
|
|
682
|
+
return "true"; // unknown type — can't check at runtime
|
|
902
683
|
}
|
|
903
684
|
return "true";
|
|
904
685
|
},
|
|
@@ -928,1099 +709,124 @@ export const expressionGenMethods = {
|
|
|
928
709
|
return base?.kind === "struct" || base?.kind === "array";
|
|
929
710
|
},
|
|
930
711
|
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
const
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
case "Len":
|
|
971
|
-
return `${base}._buf.length`;
|
|
972
|
-
case "Reset":
|
|
973
|
-
return `(${base}._buf = [])`;
|
|
974
|
-
case "Grow":
|
|
975
|
-
return "undefined";
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
return undefined;
|
|
980
|
-
},
|
|
981
|
-
|
|
982
|
-
_genRegexp(fn, a) {
|
|
983
|
-
// Converts a Go regexp pattern string (which may contain inline flags like (?i))
|
|
984
|
-
// into a JS RegExp, extracting the flags into the second constructor argument.
|
|
985
|
-
const makeRegExp = (pat) =>
|
|
986
|
-
`((p) => { const m = /^\\(\\?([gimsuy]+)\\)/.exec(p); return m ? new RegExp(p.slice(m[0].length), m[1]) : new RegExp(p); })(${pat})`;
|
|
987
|
-
switch (fn) {
|
|
988
|
-
case "MustCompile":
|
|
989
|
-
return makeRegExp(a()[0]);
|
|
990
|
-
case "Compile":
|
|
991
|
-
return `[${makeRegExp(a()[0])}, null]`;
|
|
992
|
-
case "MatchString": {
|
|
993
|
-
const [pat, s] = a();
|
|
994
|
-
return `[new RegExp(${pat}).test(${s}), null]`;
|
|
995
|
-
}
|
|
996
|
-
case "QuoteMeta":
|
|
997
|
-
return `${a()[0]}.replace(/[.*+?^\${}()|[\\]\\\\]/g, '\\\\$&')`;
|
|
998
|
-
}
|
|
999
|
-
return undefined;
|
|
1000
|
-
},
|
|
1001
|
-
|
|
1002
|
-
_genRegexpMethodCall(method, expr) {
|
|
1003
|
-
const recv = expr.func.expr;
|
|
1004
|
-
// *Regexp is a plain JS RegExp — no .value boxing needed
|
|
1005
|
-
const re = this.genExpr(recv);
|
|
1006
|
-
const args = expr.args.map((a) => this.genExpr(a));
|
|
1007
|
-
|
|
1008
|
-
switch (method) {
|
|
1009
|
-
case "MatchString":
|
|
1010
|
-
return `${re}.test(${args[0]})`;
|
|
1011
|
-
case "FindString":
|
|
1012
|
-
return `(${re}.exec(${args[0]})?.[0] ?? "")`;
|
|
1013
|
-
case "FindStringIndex":
|
|
1014
|
-
return `((m => m ? [m.index, m.index + m[0].length] : null)(${re}.exec(${args[0]})))`;
|
|
1015
|
-
case "FindAllString":
|
|
1016
|
-
return `[...${args[0]}.matchAll(new RegExp(${re}.source, ${re}.flags.includes("g") ? ${re}.flags : ${re}.flags + "g"))].slice(0, ${args[1]} < 0 ? undefined : ${args[1]}).map(m => m[0])`;
|
|
1017
|
-
case "FindStringSubmatch":
|
|
1018
|
-
return `[...(${re}.exec(${args[0]}) ?? [])]`;
|
|
1019
|
-
case "FindAllStringSubmatch":
|
|
1020
|
-
return `[...${args[0]}.matchAll(new RegExp(${re}.source, ${re}.flags.includes("g") ? ${re}.flags : ${re}.flags + "g"))].slice(0, ${args[1]} < 0 ? undefined : ${args[1]}).map(m => [...m])`;
|
|
1021
|
-
case "ReplaceAllString":
|
|
1022
|
-
return `${args[0]}.replaceAll(new RegExp(${re}.source, ${re}.flags.includes("g") ? ${re}.flags : ${re}.flags + "g"), ${args[1]})`;
|
|
1023
|
-
case "ReplaceAllLiteralString":
|
|
1024
|
-
return `${args[0]}.replaceAll(new RegExp(${re}.source, ${re}.flags.includes("g") ? ${re}.flags : ${re}.flags + "g"), ${args[1]}.replace(/\\$/g, '$$$$'))`;
|
|
1025
|
-
case "Split":
|
|
1026
|
-
return `${args[0]}.split(${re}).slice(0, ${args[1]} < 0 ? undefined : ${args[1]})`;
|
|
1027
|
-
case "String":
|
|
1028
|
-
return `${re}.source`;
|
|
1029
|
-
}
|
|
1030
|
-
return undefined;
|
|
1031
|
-
},
|
|
1032
|
-
|
|
1033
|
-
_genSlices(fn, a) {
|
|
1034
|
-
switch (fn) {
|
|
1035
|
-
case "Contains": {
|
|
1036
|
-
const [s, v] = a();
|
|
1037
|
-
return `${s}.includes(${v})`;
|
|
1038
|
-
}
|
|
1039
|
-
case "Index": {
|
|
1040
|
-
const [s, v] = a();
|
|
1041
|
-
return `${s}.indexOf(${v})`;
|
|
1042
|
-
}
|
|
1043
|
-
case "Equal": {
|
|
1044
|
-
this._usesEqual = true;
|
|
1045
|
-
const [a1, b1] = a();
|
|
1046
|
-
return `__equal(${a1}, ${b1})`;
|
|
1047
|
-
}
|
|
1048
|
-
case "Compare": {
|
|
1049
|
-
const [a1, b1] = a();
|
|
1050
|
-
return `((a, b) => { for (let i = 0; i < a.length && i < b.length; i++) { if (a[i] < b[i]) return -1; if (a[i] > b[i]) return 1; } return a.length - b.length; })(${a1}, ${b1})`;
|
|
1051
|
-
}
|
|
1052
|
-
case "Sort": {
|
|
1053
|
-
const [s] = a();
|
|
1054
|
-
return `${s}.sort((a, b) => a < b ? -1 : a > b ? 1 : 0)`;
|
|
1055
|
-
}
|
|
1056
|
-
case "SortFunc": {
|
|
1057
|
-
const [s, cmp] = a();
|
|
1058
|
-
return `${s}.sort(${cmp})`;
|
|
1059
|
-
}
|
|
1060
|
-
case "SortStableFunc": {
|
|
1061
|
-
const [s, cmp] = a();
|
|
1062
|
-
return `${s}.sort(${cmp})`;
|
|
1063
|
-
}
|
|
1064
|
-
case "IsSorted": {
|
|
1065
|
-
const [s] = a();
|
|
1066
|
-
return `((s) => { for (let i = 1; i < s.length; i++) { if (s[i] < s[i-1]) return false; } return true; })(${s})`;
|
|
1067
|
-
}
|
|
1068
|
-
case "IsSortedFunc": {
|
|
1069
|
-
const [s, cmp] = a();
|
|
1070
|
-
return `((s, f) => { for (let i = 1; i < s.length; i++) { if (f(s[i], s[i-1]) < 0) return false; } return true; })(${s}, ${cmp})`;
|
|
1071
|
-
}
|
|
1072
|
-
case "Reverse": {
|
|
1073
|
-
const [s] = a();
|
|
1074
|
-
return `${s}.reverse()`;
|
|
1075
|
-
}
|
|
1076
|
-
case "Max": {
|
|
1077
|
-
const [s] = a();
|
|
1078
|
-
return `Math.max(...${s})`;
|
|
1079
|
-
}
|
|
1080
|
-
case "Min": {
|
|
1081
|
-
const [s] = a();
|
|
1082
|
-
return `Math.min(...${s})`;
|
|
1083
|
-
}
|
|
1084
|
-
case "MaxFunc": {
|
|
1085
|
-
const [s, cmp] = a();
|
|
1086
|
-
return `((s, f) => s.reduce((m, x) => f(x, m) > 0 ? x : m))(${s}, ${cmp})`;
|
|
1087
|
-
}
|
|
1088
|
-
case "MinFunc": {
|
|
1089
|
-
const [s, cmp] = a();
|
|
1090
|
-
return `((s, f) => s.reduce((m, x) => f(x, m) < 0 ? x : m))(${s}, ${cmp})`;
|
|
1091
|
-
}
|
|
1092
|
-
case "Clone": {
|
|
1093
|
-
const [s] = a();
|
|
1094
|
-
return `${s}.slice()`;
|
|
1095
|
-
}
|
|
1096
|
-
case "Compact": {
|
|
1097
|
-
const [s] = a();
|
|
1098
|
-
return `((s) => s.filter((v, i) => i === 0 || v !== s[i-1]))(${s})`;
|
|
1099
|
-
}
|
|
1100
|
-
case "CompactFunc": {
|
|
1101
|
-
const [s, eq] = a();
|
|
1102
|
-
return `((s, f) => s.filter((v, i) => i === 0 || !f(v, s[i-1])))(${s}, ${eq})`;
|
|
1103
|
-
}
|
|
1104
|
-
case "Concat": {
|
|
1105
|
-
const args = a();
|
|
1106
|
-
return `[].concat(${args.join(", ")})`;
|
|
1107
|
-
}
|
|
1108
|
-
case "Delete": {
|
|
1109
|
-
const [s, i, j] = a();
|
|
1110
|
-
return `[...${s}.slice(0, ${i}), ...${s}.slice(${j})]`;
|
|
1111
|
-
}
|
|
1112
|
-
case "DeleteFunc": {
|
|
1113
|
-
const [s, fn2] = a();
|
|
1114
|
-
return `${s}.filter((v) => !${fn2}(v))`;
|
|
1115
|
-
}
|
|
1116
|
-
case "Insert": {
|
|
1117
|
-
const args = a();
|
|
1118
|
-
const s = args[0];
|
|
1119
|
-
const i = args[1];
|
|
1120
|
-
const vs = args.slice(2);
|
|
1121
|
-
return `[...${s}.slice(0, ${i}), ${vs.join(", ")}, ...${s}.slice(${i})]`;
|
|
1122
|
-
}
|
|
1123
|
-
case "Replace": {
|
|
1124
|
-
const args = a();
|
|
1125
|
-
const s = args[0];
|
|
1126
|
-
const i = args[1];
|
|
1127
|
-
const j = args[2];
|
|
1128
|
-
const vs = args.slice(3);
|
|
1129
|
-
return `[...${s}.slice(0, ${i}), ${vs.join(", ")}, ...${s}.slice(${j})]`;
|
|
1130
|
-
}
|
|
1131
|
-
case "Grow":
|
|
1132
|
-
case "Clip": {
|
|
1133
|
-
const [s] = a();
|
|
1134
|
-
return `${s}.slice()`;
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
return undefined;
|
|
1138
|
-
},
|
|
1139
|
-
|
|
1140
|
-
_genIo(fn, _a, expr) {
|
|
1141
|
-
switch (fn) {
|
|
1142
|
-
case "WriteString": {
|
|
1143
|
-
const writerArg = expr.args[0];
|
|
1144
|
-
const writerType = writerArg._type;
|
|
1145
|
-
const typeName =
|
|
1146
|
-
writerType?.name ??
|
|
1147
|
-
(writerType?.kind === "pointer" ? writerType.base?.name : null);
|
|
1148
|
-
const w = this.genExpr(writerArg);
|
|
1149
|
-
const s = this.genExpr(expr.args[1]);
|
|
1150
|
-
const isPtr = writerType?.kind === "pointer";
|
|
1151
|
-
const base = isPtr ? `${w}.value` : w;
|
|
1152
|
-
if (typeName === "strings.Builder") {
|
|
1153
|
-
return `((b,s) => { b._buf += s; return [s.length, null]; })(${base}, ${s})`;
|
|
1154
|
-
}
|
|
1155
|
-
if (typeName === "bytes.Buffer") {
|
|
1156
|
-
return `((b,s) => { b._buf.push(...new TextEncoder().encode(s)); return [s.length, null]; })(${base}, ${s})`;
|
|
1157
|
-
}
|
|
1158
|
-
// Generic: auto-dereference pointer, then dispatch on concrete writer type.
|
|
1159
|
-
// strings.Builder has { _buf: string }, bytes.Buffer has { _buf: [] }.
|
|
1160
|
-
return `((b,s) => { const w = b?.value ?? b; if (typeof w.WriteString === "function") return [w.WriteString(s), null]; if (typeof w._buf === "string") { w._buf += s; return [s.length, null]; } if (Array.isArray(w._buf)) { w._buf.push(...new TextEncoder().encode(s)); return [s.length, null]; } return [0, "io.WriteString: unsupported writer"]; })(${w}, ${s})`;
|
|
1161
|
-
}
|
|
1162
|
-
case "ReadAll": {
|
|
1163
|
-
const r = this.genExpr(expr.args[0]);
|
|
1164
|
-
return `((r) => { const w = r?.value ?? r; if (typeof w._src === "string") { const s = w._src.slice(w._pos); w._pos = w._src.length; return [[...s].map(c => c.charCodeAt(0)), null]; } if (Array.isArray(w._src)) { const b = w._src.slice(w._pos); w._pos = w._src.length; return [b, null]; } const chunks = []; const buf = new Array(4096); let err = null; while (!err) { const [n, e] = w.Read(buf); if (n > 0) chunks.push(...buf.slice(0, n)); err = e; } return [chunks, err === "EOF" ? null : err]; })(${r})`;
|
|
1165
|
-
}
|
|
1166
|
-
}
|
|
1167
|
-
return undefined;
|
|
1168
|
-
},
|
|
1169
|
-
|
|
1170
|
-
_genHtml(fn, a) {
|
|
1171
|
-
switch (fn) {
|
|
1172
|
-
case "EscapeString": {
|
|
1173
|
-
const [s] = a();
|
|
1174
|
-
return `${s}.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")`;
|
|
1175
|
-
}
|
|
1176
|
-
case "UnescapeString": {
|
|
1177
|
-
const [s] = a();
|
|
1178
|
-
return `${s}.replace(/'/g,"'").replace(/"/g,'"').replace(/>/g,">").replace(/</g,"<").replace(/&/g,"&")`;
|
|
1179
|
-
}
|
|
1180
|
-
}
|
|
1181
|
-
return undefined;
|
|
1182
|
-
},
|
|
1183
|
-
|
|
1184
|
-
_genMaps(fn, a) {
|
|
1185
|
-
switch (fn) {
|
|
1186
|
-
case "Keys": {
|
|
1187
|
-
const [m] = a();
|
|
1188
|
-
return `Object.keys(${m})`;
|
|
1189
|
-
}
|
|
1190
|
-
case "Values": {
|
|
1191
|
-
const [m] = a();
|
|
1192
|
-
return `Object.values(${m})`;
|
|
1193
|
-
}
|
|
1194
|
-
case "Clone": {
|
|
1195
|
-
const [m] = a();
|
|
1196
|
-
return `({...${m}})`;
|
|
1197
|
-
}
|
|
1198
|
-
case "Copy": {
|
|
1199
|
-
const [dst, src] = a();
|
|
1200
|
-
return `Object.assign(${dst}, ${src})`;
|
|
1201
|
-
}
|
|
1202
|
-
case "Equal": {
|
|
712
|
+
_genComplexBinary(expr) {
|
|
713
|
+
const l = this._genComplexOperand(expr.left);
|
|
714
|
+
const r = this._genComplexOperand(expr.right);
|
|
715
|
+
switch (expr.op) {
|
|
716
|
+
case "+":
|
|
717
|
+
return `{ re: ${l}.re + ${r}.re, im: ${l}.im + ${r}.im }`;
|
|
718
|
+
case "-":
|
|
719
|
+
return `{ re: ${l}.re - ${r}.re, im: ${l}.im - ${r}.im }`;
|
|
720
|
+
case "*":
|
|
721
|
+
this._usesCmul = true;
|
|
722
|
+
return `__cmul(${l}, ${r})`;
|
|
723
|
+
case "/":
|
|
724
|
+
this._usesCdiv = true;
|
|
725
|
+
return `__cdiv(${l}, ${r})`;
|
|
726
|
+
case "==":
|
|
727
|
+
return `(${l}.re === ${r}.re && ${l}.im === ${r}.im)`;
|
|
728
|
+
case "!=":
|
|
729
|
+
return `(${l}.re !== ${r}.re || ${l}.im !== ${r}.im)`;
|
|
730
|
+
}
|
|
731
|
+
},
|
|
732
|
+
|
|
733
|
+
_genBinaryExpr(expr) {
|
|
734
|
+
if (
|
|
735
|
+
isComplex(expr._type) ||
|
|
736
|
+
isComplex(expr.left._type) ||
|
|
737
|
+
isComplex(expr.right._type)
|
|
738
|
+
)
|
|
739
|
+
return this._genComplexBinary(expr);
|
|
740
|
+
const l = this.genExpr(expr.left);
|
|
741
|
+
const r = this.genExpr(expr.right);
|
|
742
|
+
if (
|
|
743
|
+
expr.op === "/" &&
|
|
744
|
+
this.isIntType(expr.left._type) &&
|
|
745
|
+
this.isIntType(expr.right._type)
|
|
746
|
+
)
|
|
747
|
+
return `Math.trunc(${l} / ${r})`;
|
|
748
|
+
if (expr.op === "&^") return `${l} & ~${r}`;
|
|
749
|
+
if (expr.op === "==" || expr.op === "!=") {
|
|
750
|
+
if (this._isStructOrArrayType(expr.left._type)) {
|
|
1203
751
|
this._usesEqual = true;
|
|
1204
|
-
const
|
|
1205
|
-
return
|
|
1206
|
-
}
|
|
1207
|
-
case "EqualFunc": {
|
|
1208
|
-
const [m1, m2, eq] = a();
|
|
1209
|
-
return `((a, b, f) => { const ka = Object.keys(a); if (ka.length !== Object.keys(b).length) return false; return ka.every(k => k in b && f(a[k], b[k])); })(${m1}, ${m2}, ${eq})`;
|
|
1210
|
-
}
|
|
1211
|
-
case "Delete": {
|
|
1212
|
-
const [m, k] = a();
|
|
1213
|
-
return `(delete ${m}[${k}], undefined)`;
|
|
1214
|
-
}
|
|
1215
|
-
case "DeleteFunc": {
|
|
1216
|
-
const [m, fn2] = a();
|
|
1217
|
-
return `Object.keys(${m}).forEach(k => { if (${fn2}(k, ${m}[k])) delete ${m}[k]; })`;
|
|
752
|
+
const cmp = `__equal(${l}, ${r})`;
|
|
753
|
+
return expr.op === "==" ? cmp : `!${cmp}`;
|
|
1218
754
|
}
|
|
1219
755
|
}
|
|
1220
|
-
|
|
756
|
+
const op = expr.op === "==" ? "===" : expr.op === "!=" ? "!==" : expr.op;
|
|
757
|
+
return `${l} ${op} ${r}`;
|
|
1221
758
|
},
|
|
1222
759
|
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
case "SliceType":
|
|
1229
|
-
return `[]${this.typeComment(typeNode.elem)}`;
|
|
1230
|
-
case "MapType":
|
|
1231
|
-
return `map[${this.typeComment(typeNode.key)}]${this.typeComment(typeNode.value)}`;
|
|
1232
|
-
default:
|
|
1233
|
-
return typeNode.kind;
|
|
760
|
+
_genTypeConversion(expr) {
|
|
761
|
+
const inner = this.genExpr(expr.expr);
|
|
762
|
+
const t = expr.targetType;
|
|
763
|
+
if (t?.name === "complex128" || t?.name === "complex64") {
|
|
764
|
+
return isComplex(expr.expr._type) ? inner : `{ re: ${inner}, im: 0 }`;
|
|
1234
765
|
}
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
return this._genFmt(fn, a, expr);
|
|
1244
|
-
case "strings":
|
|
1245
|
-
return this._genStrings(fn, a);
|
|
1246
|
-
case "bytes":
|
|
1247
|
-
return this._genBytes(fn, a);
|
|
1248
|
-
case "strconv":
|
|
1249
|
-
return this._genStrconv(fn, a);
|
|
1250
|
-
case "sort":
|
|
1251
|
-
return this._genSort(fn, a);
|
|
1252
|
-
case "math":
|
|
1253
|
-
return this._genMath(fn, a);
|
|
1254
|
-
case "unicode":
|
|
1255
|
-
return this._genUnicode(fn, a);
|
|
1256
|
-
case "os":
|
|
1257
|
-
return this._genOs(fn, a);
|
|
1258
|
-
case "errors":
|
|
1259
|
-
return this._genErrors(fn, a);
|
|
1260
|
-
case "time":
|
|
1261
|
-
return this._genTime(fn, a);
|
|
1262
|
-
case "regexp":
|
|
1263
|
-
return this._genRegexp(fn, a);
|
|
1264
|
-
case "slices":
|
|
1265
|
-
return this._genSlices(fn, a);
|
|
1266
|
-
case "maps":
|
|
1267
|
-
return this._genMaps(fn, a);
|
|
1268
|
-
case "html":
|
|
1269
|
-
return this._genHtml(fn, a);
|
|
1270
|
-
case "io":
|
|
1271
|
-
return this._genIo(fn, a, expr);
|
|
1272
|
-
case "rand":
|
|
1273
|
-
return this._genRand(fn, a);
|
|
1274
|
-
case "utf8":
|
|
1275
|
-
return this._genUtf8(fn, a);
|
|
1276
|
-
case "path":
|
|
1277
|
-
return this._genPath(fn, a, expr);
|
|
1278
|
-
case "gom":
|
|
1279
|
-
return this._genGom(fn, expr);
|
|
1280
|
-
default:
|
|
1281
|
-
return undefined;
|
|
766
|
+
if (t?.kind === "ArrayType")
|
|
767
|
+
return this._genArrayTypeConversion(expr, inner, t);
|
|
768
|
+
if (t?.kind === "SliceType")
|
|
769
|
+
return this._genSliceTypeConversion(expr, inner, t);
|
|
770
|
+
const target = t?.name;
|
|
771
|
+
if (target === "error") {
|
|
772
|
+
this._usesError = true;
|
|
773
|
+
return `__error(${inner})`;
|
|
1282
774
|
}
|
|
775
|
+
if (target && this.namedWrapperNames.has(target))
|
|
776
|
+
return `new ${target}(${inner})`;
|
|
777
|
+
return this._genPrimitiveConversion(expr, inner, target);
|
|
1283
778
|
},
|
|
1284
779
|
|
|
1285
|
-
|
|
1286
|
-
const
|
|
1287
|
-
expr.
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
Article: "article",
|
|
1294
|
-
Aside: "aside",
|
|
1295
|
-
Header: "header",
|
|
1296
|
-
Footer: "footer",
|
|
1297
|
-
Main: "main",
|
|
1298
|
-
Nav: "nav",
|
|
1299
|
-
Figure: "figure",
|
|
1300
|
-
H1: "h1",
|
|
1301
|
-
H2: "h2",
|
|
1302
|
-
H3: "h3",
|
|
1303
|
-
H4: "h4",
|
|
1304
|
-
H5: "h5",
|
|
1305
|
-
H6: "h6",
|
|
1306
|
-
Span: "span",
|
|
1307
|
-
A: "a",
|
|
1308
|
-
Strong: "strong",
|
|
1309
|
-
Em: "em",
|
|
1310
|
-
Code: "code",
|
|
1311
|
-
Pre: "pre",
|
|
1312
|
-
Small: "small",
|
|
1313
|
-
Mark: "mark",
|
|
1314
|
-
P: "p",
|
|
1315
|
-
Br: "br",
|
|
1316
|
-
Hr: "hr",
|
|
1317
|
-
Ul: "ul",
|
|
1318
|
-
Ol: "ol",
|
|
1319
|
-
Li: "li",
|
|
1320
|
-
Dl: "dl",
|
|
1321
|
-
Dt: "dt",
|
|
1322
|
-
Dd: "dd",
|
|
1323
|
-
Form: "form",
|
|
1324
|
-
Input: "input",
|
|
1325
|
-
Button: "button",
|
|
1326
|
-
Textarea: "textarea",
|
|
1327
|
-
Select: "select",
|
|
1328
|
-
Option: "option",
|
|
1329
|
-
Label: "label",
|
|
1330
|
-
Fieldset: "fieldset",
|
|
1331
|
-
Legend: "legend",
|
|
1332
|
-
Img: "img",
|
|
1333
|
-
Video: "video",
|
|
1334
|
-
Audio: "audio",
|
|
1335
|
-
Canvas: "canvas",
|
|
1336
|
-
Table: "table",
|
|
1337
|
-
Thead: "thead",
|
|
1338
|
-
Tbody: "tbody",
|
|
1339
|
-
Tfoot: "tfoot",
|
|
1340
|
-
Tr: "tr",
|
|
1341
|
-
Th: "th",
|
|
1342
|
-
Td: "td",
|
|
1343
|
-
};
|
|
1344
|
-
if (elementTags[fn]) {
|
|
1345
|
-
const tag = elementTags[fn];
|
|
1346
|
-
const args = a();
|
|
1347
|
-
if (args.length === 0)
|
|
1348
|
-
return `(()=>({Mount(p){const e=document.createElement("${tag}");p.appendChild(e);}}))()`;
|
|
1349
|
-
return (
|
|
1350
|
-
`((...c)=>({Mount(p){const e=document.createElement("${tag}");c.forEach(n=>n?.Mount?.(e));p.appendChild(e);}}))` +
|
|
1351
|
-
`(${args.join(",")})`
|
|
1352
|
-
);
|
|
780
|
+
_genArrayTypeConversion(expr, inner, t) {
|
|
781
|
+
const srcResolved =
|
|
782
|
+
expr.expr._type?.kind === "named"
|
|
783
|
+
? expr.expr._type.underlying
|
|
784
|
+
: expr.expr._type;
|
|
785
|
+
if (srcResolved?.kind === "slice") {
|
|
786
|
+
const size = t.size?.value !== undefined ? Number(t.size.value) : t.size;
|
|
787
|
+
return `${inner}.slice(0, ${size})`;
|
|
1353
788
|
}
|
|
1354
|
-
|
|
1355
|
-
For: "for",
|
|
1356
|
-
Name: "name",
|
|
1357
|
-
Value: "value",
|
|
1358
|
-
Target: "target",
|
|
1359
|
-
Rel: "rel",
|
|
1360
|
-
Alt: "alt",
|
|
1361
|
-
Title: "title",
|
|
1362
|
-
Lang: "lang",
|
|
1363
|
-
Action: "action",
|
|
1364
|
-
Method: "method",
|
|
1365
|
-
AutoComplete: "autocomplete",
|
|
1366
|
-
Draggable: "draggable",
|
|
1367
|
-
Role: "role",
|
|
1368
|
-
StyleAttr: "style",
|
|
1369
|
-
};
|
|
1370
|
-
if (attrHelpers[fn]) {
|
|
1371
|
-
const [v] = a();
|
|
1372
|
-
return `((v)=>({Mount(e){e.setAttribute("${attrHelpers[fn]}",v)}}))(${v})`;
|
|
1373
|
-
}
|
|
1374
|
-
if (fn === "AriaLabel") {
|
|
1375
|
-
const [v] = a();
|
|
1376
|
-
return `((v)=>({Mount(e){e.setAttribute("aria-label",v)}}))(${v})`;
|
|
1377
|
-
}
|
|
1378
|
-
const boolAttrs = {
|
|
1379
|
-
Disabled: "disabled",
|
|
1380
|
-
Checked: "checked",
|
|
1381
|
-
Selected: "selected",
|
|
1382
|
-
Readonly: "readonly",
|
|
1383
|
-
};
|
|
1384
|
-
if (boolAttrs[fn])
|
|
1385
|
-
return `({Mount(e){e.setAttribute("${boolAttrs[fn]}","")}})`;
|
|
1386
|
-
|
|
1387
|
-
const args = a();
|
|
1388
|
-
switch (fn) {
|
|
1389
|
-
case "El": {
|
|
1390
|
-
const [tag, ...children] = args;
|
|
1391
|
-
if (children.length === 0)
|
|
1392
|
-
return `((t)=>({Mount(p){const e=document.createElement(t);p.appendChild(e);}})) (${tag})`;
|
|
1393
|
-
return (
|
|
1394
|
-
`((t,...c)=>({Mount(p){const e=document.createElement(t);c.forEach(n=>n?.Mount?.(e));p.appendChild(e);}}))` +
|
|
1395
|
-
`(${tag},${children.join(",")})`
|
|
1396
|
-
);
|
|
1397
|
-
}
|
|
1398
|
-
case "Text": {
|
|
1399
|
-
const [s] = args;
|
|
1400
|
-
return `((s)=>({Mount(p){p.appendChild(document.createTextNode(s))}}))(${s})`;
|
|
1401
|
-
}
|
|
1402
|
-
case "Attr": {
|
|
1403
|
-
const [name, value] = args;
|
|
1404
|
-
return `((n,v)=>({Mount(e){e.setAttribute(n,v)}}))(${name},${value})`;
|
|
1405
|
-
}
|
|
1406
|
-
case "Class":
|
|
1407
|
-
return `((v)=>({Mount(e){e.className=v}}))(${args[0]})`;
|
|
1408
|
-
case "Type":
|
|
1409
|
-
return `((v)=>({Mount(e){e.type=v}}))(${args[0]})`;
|
|
1410
|
-
case "Href":
|
|
1411
|
-
return `((v)=>({Mount(e){e.href=v}}))(${args[0]})`;
|
|
1412
|
-
case "Src":
|
|
1413
|
-
return `((v)=>({Mount(e){e.src=v}}))(${args[0]})`;
|
|
1414
|
-
case "Placeholder":
|
|
1415
|
-
return `((v)=>({Mount(e){e.placeholder=v}}))(${args[0]})`;
|
|
1416
|
-
case "DataAttr":
|
|
1417
|
-
return `((k,v)=>({Mount(e){e.setAttribute("data-"+k,v)}}))(${args[0]},${args[1]})`;
|
|
1418
|
-
case "If":
|
|
1419
|
-
return `((c,n)=>c?n:{Mount(){}})(${args[0]},${args[1]})`;
|
|
1420
|
-
case "Map":
|
|
1421
|
-
return `((s,f)=>{const c=s.map(f);return{_items:c,Mount(p){c.forEach(n=>n.Mount(p))}}})(${args[0]},${args[1]})`;
|
|
1422
|
-
case "Style":
|
|
1423
|
-
return `((s)=>({Mount(p){const e=document.createElement("style");e.textContent=s;p.appendChild(e);}})) (${args[0]})`;
|
|
1424
|
-
case "Mount":
|
|
1425
|
-
return `((sel,n)=>{const e=document.querySelector(sel);e.innerHTML="";n.Mount(e)})(${args[0]},${args[1]})`;
|
|
1426
|
-
case "MountTo":
|
|
1427
|
-
return `((sel,n)=>{const e=document.querySelector(sel);n.Mount(e)})(${args[0]},${args[1]})`;
|
|
1428
|
-
}
|
|
1429
|
-
return undefined;
|
|
789
|
+
return inner;
|
|
1430
790
|
},
|
|
1431
791
|
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
return `__sprintf(${fmtArgs})`;
|
|
1438
|
-
case "Errorf": {
|
|
1439
|
-
this._usesSprintf = true;
|
|
1440
|
-
this._usesError = true;
|
|
1441
|
-
const fmtStr = expr.args[0];
|
|
1442
|
-
if (fmtStr?.kind === "BasicLit" && fmtStr.value?.includes("%w")) {
|
|
1443
|
-
const lastArg = this.genExpr(expr.args[expr.args.length - 1]);
|
|
1444
|
-
return `__error(__sprintf(${fmtArgs}), ${lastArg})`;
|
|
1445
|
-
}
|
|
1446
|
-
return `__error(__sprintf(${fmtArgs}))`;
|
|
1447
|
-
}
|
|
1448
|
-
case "Printf":
|
|
1449
|
-
case "Print":
|
|
1450
|
-
this._usesSprintf = true;
|
|
1451
|
-
return `process?.stdout?.write(__sprintf(${fmtArgs}))`;
|
|
1452
|
-
case "Println":
|
|
1453
|
-
this._usesSprintf = true;
|
|
1454
|
-
return `console.log(__sprintf(${fmtArgs}))`;
|
|
1455
|
-
case "Fprintf":
|
|
1456
|
-
case "Fprintln":
|
|
1457
|
-
case "Fprint": {
|
|
1458
|
-
this._usesSprintf = true;
|
|
1459
|
-
const writerArg = expr.args[0];
|
|
1460
|
-
const rest = expr.args.slice(1);
|
|
1461
|
-
const restJs = rest.map((e) => this.genExpr(e)).join(", ");
|
|
1462
|
-
// Resolve the writer to its underlying buffer expression
|
|
1463
|
-
const writerType = writerArg._type;
|
|
1464
|
-
const targetTypeName =
|
|
1465
|
-
writerType?.name ??
|
|
1466
|
-
(writerType?.kind === "pointer" ? writerType.base?.name : null);
|
|
1467
|
-
let buf;
|
|
1468
|
-
if (targetTypeName === "strings.Builder") {
|
|
1469
|
-
const w = this.genExpr(writerArg);
|
|
1470
|
-
buf =
|
|
1471
|
-
writerType?.kind === "pointer" ? `${w}.value._buf` : `${w}._buf`;
|
|
1472
|
-
} else if (targetTypeName === "bytes.Buffer") {
|
|
1473
|
-
const w = this.genExpr(writerArg);
|
|
1474
|
-
const base = writerType?.kind === "pointer" ? `${w}.value` : w;
|
|
1475
|
-
buf = null;
|
|
1476
|
-
if (fn === "Fprintf") {
|
|
1477
|
-
return `((__b,__s)=>{ __b._buf.push(...new TextEncoder().encode(__s)); })(${base}, __sprintf(${restJs}))`;
|
|
1478
|
-
}
|
|
1479
|
-
const valJs =
|
|
1480
|
-
fn === "Fprintln"
|
|
1481
|
-
? `__sprintf("%v\\n", ${restJs})`
|
|
1482
|
-
: `__sprintf("%v", ${restJs})`;
|
|
1483
|
-
return `((__b,__s)=>{ __b._buf.push(...new TextEncoder().encode(__s)); })(${base}, ${valJs})`;
|
|
1484
|
-
} else {
|
|
1485
|
-
// Generic io.Writer fallback — call .WriteString if available
|
|
1486
|
-
const w = this.genExpr(writerArg);
|
|
1487
|
-
const formatted =
|
|
1488
|
-
fn === "Fprintf"
|
|
1489
|
-
? `__sprintf(${restJs})`
|
|
1490
|
-
: fn === "Fprintln"
|
|
1491
|
-
? `__sprintf("%v\\n", ${restJs})`
|
|
1492
|
-
: `__sprintf("%v", ${restJs})`;
|
|
1493
|
-
return `${w}.WriteString(${formatted})`;
|
|
1494
|
-
}
|
|
1495
|
-
// strings.Builder path
|
|
1496
|
-
if (fn === "Fprintf") return `(${buf} += __sprintf(${restJs}))`;
|
|
1497
|
-
if (fn === "Fprintln")
|
|
1498
|
-
return `(${buf} += __sprintf("%v\\n", ${restJs}))`;
|
|
1499
|
-
return `(${buf} += __sprintf("%v", ${restJs}))`;
|
|
1500
|
-
}
|
|
1501
|
-
case "Sscan": {
|
|
1502
|
-
const strArg = this.genExpr(expr.args[0]);
|
|
1503
|
-
const restArgs = expr.args
|
|
1504
|
-
.slice(1)
|
|
1505
|
-
.map((e) => this.genExpr(e))
|
|
1506
|
-
.join(", ");
|
|
1507
|
-
return `((str, ...ptrs) => { const tokens = str.trim().split(/\\s+/).filter(Boolean); let n = 0; for (let i = 0; i < ptrs.length && i < tokens.length; i++) { const t = tokens[i]; const p = ptrs[i]; if (typeof p.value === "number") p.value = Number(t); else if (typeof p.value === "boolean") p.value = t === "true"; else p.value = t; n++; } return [n, n < ptrs.length ? "unexpected EOF" : null]; })(${strArg}, ${restArgs})`;
|
|
1508
|
-
}
|
|
1509
|
-
case "Sscanln": {
|
|
1510
|
-
const strArg = this.genExpr(expr.args[0]);
|
|
1511
|
-
const restArgs = expr.args
|
|
1512
|
-
.slice(1)
|
|
1513
|
-
.map((e) => this.genExpr(e))
|
|
1514
|
-
.join(", ");
|
|
1515
|
-
return `((str, ...ptrs) => { const line = str.split("\\n")[0]; const tokens = line.trim().split(/\\s+/).filter(Boolean); let n = 0; for (let i = 0; i < ptrs.length && i < tokens.length; i++) { const t = tokens[i]; const p = ptrs[i]; if (typeof p.value === "number") p.value = Number(t); else if (typeof p.value === "boolean") p.value = t === "true"; else p.value = t; n++; } return [n, n < ptrs.length ? "unexpected EOF" : null]; })(${strArg}, ${restArgs})`;
|
|
1516
|
-
}
|
|
1517
|
-
case "Sscanf": {
|
|
1518
|
-
const strArg = this.genExpr(expr.args[0]);
|
|
1519
|
-
const fmtArg = this.genExpr(expr.args[1]);
|
|
1520
|
-
const restArgs = expr.args
|
|
1521
|
-
.slice(2)
|
|
1522
|
-
.map((e) => this.genExpr(e))
|
|
1523
|
-
.join(", ");
|
|
1524
|
-
return `((str, _fmt, ...ptrs) => { const tokens = str.trim().split(/\\s+/).filter(Boolean); let n = 0; for (let i = 0; i < ptrs.length && i < tokens.length; i++) { const t = tokens[i]; const p = ptrs[i]; if (typeof p.value === "number") p.value = Number(t); else if (typeof p.value === "boolean") p.value = t === "true"; else p.value = t; n++; } return [n, n < ptrs.length ? "input does not match format" : null]; })(${strArg}, ${fmtArg}, ${restArgs})`;
|
|
1525
|
-
}
|
|
1526
|
-
default:
|
|
1527
|
-
return undefined;
|
|
1528
|
-
}
|
|
792
|
+
_isStringSource(srcType) {
|
|
793
|
+
return (
|
|
794
|
+
(srcType?.kind === "basic" && srcType?.name === "string") ||
|
|
795
|
+
(srcType?.kind === "untyped" && srcType?.base === "string")
|
|
796
|
+
);
|
|
1529
797
|
},
|
|
1530
798
|
|
|
1531
|
-
|
|
1532
|
-
const
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
return
|
|
1538
|
-
case "HasSuffix":
|
|
1539
|
-
return `${args[0]}.endsWith(${args[1]})`;
|
|
1540
|
-
case "Index":
|
|
1541
|
-
return `${args[0]}.indexOf(${args[1]})`;
|
|
1542
|
-
case "LastIndex":
|
|
1543
|
-
return `${args[0]}.lastIndexOf(${args[1]})`;
|
|
1544
|
-
case "Count":
|
|
1545
|
-
return `((s, sep) => sep === "" ? s.length + 1 : s.split(sep).length - 1)(${args[0]}, ${args[1]})`;
|
|
1546
|
-
case "Repeat":
|
|
1547
|
-
return `${args[0]}.repeat(${args[1]})`;
|
|
1548
|
-
case "Replace":
|
|
1549
|
-
return `${args[0]}.replace(${args[1]}, ${args[2]})`;
|
|
1550
|
-
case "ReplaceAll":
|
|
1551
|
-
return `${args[0]}.replaceAll(${args[1]}, ${args[2]})`;
|
|
1552
|
-
case "ToUpper":
|
|
1553
|
-
return `${args[0]}.toUpperCase()`;
|
|
1554
|
-
case "ToLower":
|
|
1555
|
-
return `${args[0]}.toLowerCase()`;
|
|
1556
|
-
case "TrimSpace":
|
|
1557
|
-
return `${args[0]}.trim()`;
|
|
1558
|
-
case "Trim":
|
|
1559
|
-
return `${args[0]}.replace(new RegExp(\`^[\${${args[1]}}]+|[\${${args[1]}}]+$\`, "g"), "")`;
|
|
1560
|
-
case "TrimPrefix":
|
|
1561
|
-
return `((s, pre) => s.startsWith(pre) ? s.slice(pre.length) : s)(${args[0]}, ${args[1]})`;
|
|
1562
|
-
case "TrimSuffix":
|
|
1563
|
-
return `((s, suf) => !suf.length || !s.endsWith(suf) ? s : s.slice(0, -suf.length))(${args[0]}, ${args[1]})`;
|
|
1564
|
-
case "TrimLeft":
|
|
1565
|
-
return `${args[0]}.replace(new RegExp(\`^[\${${args[1]}}]+\`), "")`;
|
|
1566
|
-
case "TrimRight":
|
|
1567
|
-
return `${args[0]}.replace(new RegExp(\`[\${${args[1]}}]+$\`), "")`;
|
|
1568
|
-
case "Split":
|
|
1569
|
-
return `${args[0]}.split(${args[1]})`;
|
|
1570
|
-
case "Join":
|
|
1571
|
-
return `${args[0]}.join(${args[1]})`;
|
|
1572
|
-
case "EqualFold":
|
|
1573
|
-
return `${args[0]}.toLowerCase() === ${args[1]}.toLowerCase()`;
|
|
1574
|
-
case "Fields":
|
|
1575
|
-
return `(${args[0]}).trim() === '' ? [] : (${args[0]}).trim().split(/\\s+/)`;
|
|
1576
|
-
case "Cut":
|
|
1577
|
-
return `((s, sep) => { const i = s.indexOf(sep); return i < 0 ? [s, "", false] : [s.slice(0, i), s.slice(i + sep.length), true]; })(${args[0]}, ${args[1]})`;
|
|
1578
|
-
case "CutPrefix":
|
|
1579
|
-
return `(${args[0]}).startsWith(${args[1]}) ? [(${args[0]}).slice((${args[1]}).length), true] : [${args[0]}, false]`;
|
|
1580
|
-
case "CutSuffix":
|
|
1581
|
-
return `(${args[0]}).endsWith(${args[1]}) ? [(${args[0]}).slice(0, -(${args[1]}).length), true] : [${args[0]}, false]`;
|
|
1582
|
-
case "SplitN":
|
|
1583
|
-
return `((s, sep, n) => { if (n === 0) return []; if (n < 0) return s.split(sep); const r = []; let cur = s; for (let i = 1; i < n && cur.length; i++) { const j = cur.indexOf(sep); if (j < 0) break; r.push(cur.slice(0, j)); cur = cur.slice(j + sep.length); } r.push(cur); return r; })(${args[0]}, ${args[1]}, ${args[2]})`;
|
|
1584
|
-
case "SplitAfter":
|
|
1585
|
-
return `((s, sep) => { if (sep === "") return [...s]; const r = []; let cur = s; while (cur.length) { const j = cur.indexOf(sep); if (j < 0) { r.push(cur); break; } r.push(cur.slice(0, j + sep.length)); cur = cur.slice(j + sep.length); } return r; })(${args[0]}, ${args[1]})`;
|
|
1586
|
-
case "SplitAfterN":
|
|
1587
|
-
return `((s, sep, n) => { if (n === 0) return []; if (n < 0 || sep === "") return ((s, sep) => { if (sep === "") return [...s]; const r = []; let cur = s; while (cur.length) { const j = cur.indexOf(sep); if (j < 0) { r.push(cur); break; } r.push(cur.slice(0, j + sep.length)); cur = cur.slice(j + sep.length); } return r; })(s, sep); const r = []; let cur = s; for (let i = 1; i < n && cur.length; i++) { const j = cur.indexOf(sep); if (j < 0) break; r.push(cur.slice(0, j + sep.length)); cur = cur.slice(j + sep.length); } r.push(cur); return r; })(${args[0]}, ${args[1]}, ${args[2]})`;
|
|
1588
|
-
case "IndexAny":
|
|
1589
|
-
return `((s, chars) => { let m = -1; for (const c of chars) { const i = s.indexOf(c); if (i >= 0 && (m < 0 || i < m)) m = i; } return m; })(${args[0]}, ${args[1]})`;
|
|
1590
|
-
case "LastIndexAny":
|
|
1591
|
-
return `((s, chars) => { let m = -1; for (const c of chars) { const i = s.lastIndexOf(c); if (i > m) m = i; } return m; })(${args[0]}, ${args[1]})`;
|
|
1592
|
-
case "ContainsAny":
|
|
1593
|
-
return `[...(${args[1]})].some(c => (${args[0]}).includes(c))`;
|
|
1594
|
-
case "ContainsRune":
|
|
1595
|
-
return `(${args[0]}).includes(String.fromCodePoint(${args[1]}))`;
|
|
1596
|
-
case "IndexRune":
|
|
1597
|
-
return `(${args[0]}).indexOf(String.fromCodePoint(${args[1]}))`;
|
|
1598
|
-
case "IndexByte":
|
|
1599
|
-
return `(${args[0]}).indexOf(String.fromCharCode(${args[1]}))`;
|
|
1600
|
-
case "LastIndexByte":
|
|
1601
|
-
return `(${args[0]}).lastIndexOf(String.fromCharCode(${args[1]}))`;
|
|
1602
|
-
case "Map":
|
|
1603
|
-
return `[...(${args[1]})].map(c => String.fromCodePoint((${args[0]})(c.codePointAt(0)))).join("")`;
|
|
1604
|
-
case "Title":
|
|
1605
|
-
return `(${args[0]}).replace(/\\b\\w/g, c => c.toUpperCase())`;
|
|
1606
|
-
case "ToTitle":
|
|
1607
|
-
return `(${args[0]}).toUpperCase()`;
|
|
1608
|
-
case "TrimFunc":
|
|
1609
|
-
return `((s, f) => { let l = 0, r = s.length; while (l < r && f(s.codePointAt(l))) l++; while (r > l && f(s.codePointAt(r - 1))) r--; return s.slice(l, r); })(${args[0]}, ${args[1]})`;
|
|
1610
|
-
case "TrimLeftFunc":
|
|
1611
|
-
return `((s, f) => { let l = 0; while (l < s.length && f(s.codePointAt(l))) l++; return s.slice(l); })(${args[0]}, ${args[1]})`;
|
|
1612
|
-
case "TrimRightFunc":
|
|
1613
|
-
return `((s, f) => { let r = s.length; while (r > 0 && f(s.codePointAt(r - 1))) r--; return s.slice(0, r); })(${args[0]}, ${args[1]})`;
|
|
1614
|
-
case "IndexFunc":
|
|
1615
|
-
return `((s, f) => { for (let i = 0; i < s.length; i++) { const cp = s.codePointAt(i); if (f(cp)) return i; if (cp > 0xFFFF) i++; } return -1; })(${args[0]}, ${args[1]})`;
|
|
1616
|
-
case "LastIndexFunc":
|
|
1617
|
-
return `((s, f) => { for (let i = s.length - 1; i >= 0; i--) { const cp = s.codePointAt(i); if (f(cp)) return i; } return -1; })(${args[0]}, ${args[1]})`;
|
|
1618
|
-
case "NewReader":
|
|
1619
|
-
return `{_src: ${args[0]}, _pos: 0, Read(p) { const n = Math.min(p.length, this._src.length - this._pos); for (let i = 0; i < n; i++) p[i] = this._src.charCodeAt(this._pos + i); this._pos += n; return [n, n === 0 ? "EOF" : null]; }, Len() { return this._src.length - this._pos; }, Reset(s) { this._src = s; this._pos = 0; }}`;
|
|
1620
|
-
case "NewReplacer": {
|
|
1621
|
-
const allArgs = args.join(", ");
|
|
1622
|
-
return `((...pairs) => { const p = []; for (let i = 0; i < pairs.length; i += 2) p.push([pairs[i], pairs[i+1]]); return { _p: p, Replace(s) { let r = s; for (const [o, n] of this._p) r = r.split(o).join(n); return r; } }; })(${allArgs})`;
|
|
1623
|
-
}
|
|
1624
|
-
default:
|
|
1625
|
-
return undefined;
|
|
799
|
+
_genSliceTypeConversion(expr, inner, t) {
|
|
800
|
+
const elem = t.elem?.name;
|
|
801
|
+
if (elem === "byte" || elem === "uint8")
|
|
802
|
+
return `Array.from(new TextEncoder().encode(${inner}))`;
|
|
803
|
+
if (elem === "rune" || elem === "int32" || elem === "int") {
|
|
804
|
+
if (this._isStringSource(expr.expr._type))
|
|
805
|
+
return `Array.from(${inner}, __c => __c.codePointAt(0))`;
|
|
1626
806
|
}
|
|
807
|
+
return `Array.from(${inner})`;
|
|
1627
808
|
},
|
|
1628
809
|
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
return `(
|
|
1638
|
-
case "HasSuffix":
|
|
1639
|
-
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]})`;
|
|
1640
|
-
case "Index":
|
|
1641
|
-
return `${__bs}(${args[0]}).indexOf(${__bs}(${args[1]}))`;
|
|
1642
|
-
case "Count":
|
|
1643
|
-
return `((b, sep) => sep.length === 0 ? b.length + 1 : ${__bs}(b).split(${__bs}(sep)).length - 1)(${args[0]}, ${args[1]})`;
|
|
1644
|
-
case "Repeat":
|
|
1645
|
-
return `${__sb}(${__bs}(${args[0]}).repeat(${args[1]}))`;
|
|
1646
|
-
case "Replace":
|
|
1647
|
-
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]})`;
|
|
1648
|
-
case "ToUpper":
|
|
1649
|
-
return `${__sb}(${__bs}(${args[0]}).toUpperCase())`;
|
|
1650
|
-
case "ToLower":
|
|
1651
|
-
return `${__sb}(${__bs}(${args[0]}).toLowerCase())`;
|
|
1652
|
-
case "TrimSpace":
|
|
1653
|
-
return `${__sb}(${__bs}(${args[0]}).trim())`;
|
|
1654
|
-
case "Trim":
|
|
1655
|
-
return `${__sb}(${__bs}(${args[0]}).replace(new RegExp(\`^[\${${args[1]}}]+|[\${${args[1]}}]+$\`, "g"), ""))`;
|
|
1656
|
-
case "Equal":
|
|
1657
|
-
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]})`;
|
|
1658
|
-
case "Split":
|
|
1659
|
-
return `${__bs}(${args[0]}).split(${__bs}(${args[1]})).map(p => ${__sb}(p))`;
|
|
1660
|
-
case "Join":
|
|
1661
|
-
return `${__sb}(${args[0]}.map(p => ${__bs}(p)).join(${__bs}(${args[1]})))`;
|
|
1662
|
-
case "ReplaceAll":
|
|
1663
|
-
return `((b, o, n) => { const r = []; let i = 0; while (i <= b.length - o.length) { if (o.every((v, j) => b[i + j] === v)) { r.push(...n); i += o.length; } else r.push(b[i++]); } return r.concat(b.slice(i)); })(${args[0]}, ${args[1]}, ${args[2]})`;
|
|
1664
|
-
case "TrimPrefix":
|
|
1665
|
-
return `((b, p) => p.every((v, i) => b[i] === v) ? b.slice(p.length) : b.slice())(${args[0]}, ${args[1]})`;
|
|
1666
|
-
case "TrimSuffix":
|
|
1667
|
-
return `((b, s) => s.length && s.every((v, i) => b[b.length - s.length + i] === v) ? b.slice(0, -s.length) : b.slice())(${args[0]}, ${args[1]})`;
|
|
1668
|
-
case "TrimLeft":
|
|
1669
|
-
return `((b, c) => { let i = 0; while (i < b.length && c.includes(String.fromCharCode(b[i]))) i++; return b.slice(i); })(${args[0]}, ${args[1]})`;
|
|
1670
|
-
case "TrimRight":
|
|
1671
|
-
return `((b, c) => { let i = b.length; while (i > 0 && c.includes(String.fromCharCode(b[i - 1]))) i--; return b.slice(0, i); })(${args[0]}, ${args[1]})`;
|
|
1672
|
-
case "TrimFunc":
|
|
1673
|
-
return `((b, f) => { let l = 0, r = b.length; while (l < r && f(b[l])) l++; while (r > l && f(b[r - 1])) r--; return b.slice(l, r); })(${args[0]}, ${args[1]})`;
|
|
1674
|
-
case "IndexByte":
|
|
1675
|
-
return `(${args[0]}).indexOf(${args[1]})`;
|
|
1676
|
-
case "LastIndex":
|
|
1677
|
-
return `((b, s) => { for (let i = b.length - s.length; i >= 0; i--) if (s.every((v, j) => b[i + j] === v)) return i; return -1; })(${args[0]}, ${args[1]})`;
|
|
1678
|
-
case "LastIndexByte":
|
|
1679
|
-
return `(${args[0]}).lastIndexOf(${args[1]})`;
|
|
1680
|
-
case "Fields":
|
|
1681
|
-
return `((b) => { const s = String.fromCharCode(...b).trim(); return s === '' ? [] : s.split(/\\s+/).map(w => [...w].map(c => c.charCodeAt(0))); })(${args[0]})`;
|
|
1682
|
-
case "Cut":
|
|
1683
|
-
return `((b, sep) => { for (let i = 0; i <= b.length - sep.length; i++) if (sep.every((v, j) => b[i + j] === v)) return [b.slice(0, i), b.slice(i + sep.length), true]; return [b.slice(), [], false]; })(${args[0]}, ${args[1]})`;
|
|
1684
|
-
case "ContainsAny":
|
|
1685
|
-
return `[...(${args[1]})].some(c => (${args[0]}).includes(c.charCodeAt(0)))`;
|
|
1686
|
-
case "ContainsRune":
|
|
1687
|
-
return `(${args[0]}).includes(${args[1]})`;
|
|
1688
|
-
case "Map":
|
|
1689
|
-
return `(${args[1]}).map(v => (${args[0]})(v))`;
|
|
1690
|
-
case "SplitN":
|
|
1691
|
-
return `((b, sep, n) => { if (n === 0) return []; if (n < 0) return ((b, sep) => { const r = []; let i = 0; while (i <= b.length) { const j = b.findIndex((_, k) => k >= i && sep.every((v, l) => b[k + l] === sep[l])); if (j < 0 || j >= b.length) break; r.push(b.slice(i, j)); i = j + sep.length; } r.push(b.slice(i)); return r; })(b, sep); const r = []; let i = 0; for (let c = 1; c < n; c++) { const j = b.findIndex((_, k) => k >= i && sep.every((v, l) => b[k + l] === sep[l])); if (j < 0) break; r.push(b.slice(i, j)); i = j + sep.length; } r.push(b.slice(i)); return r; })(${args[0]}, ${args[1]}, ${args[2]})`;
|
|
1692
|
-
case "NewReader":
|
|
1693
|
-
return `{_src: ${args[0]}, _pos: 0, Read(p) { const n = Math.min(p.length, this._src.length - this._pos); for (let i = 0; i < n; i++) p[i] = this._src[this._pos + i]; this._pos += n; return [n, n === 0 ? "EOF" : null]; }, Len() { return this._src.length - this._pos; }, Reset(b) { this._src = b; this._pos = 0; }}`;
|
|
1694
|
-
default:
|
|
1695
|
-
return undefined;
|
|
1696
|
-
}
|
|
1697
|
-
},
|
|
1698
|
-
|
|
1699
|
-
_genStrconv(fn, a) {
|
|
1700
|
-
const args = a();
|
|
1701
|
-
switch (fn) {
|
|
1702
|
-
case "Itoa":
|
|
1703
|
-
return `String(${args[0]})`;
|
|
1704
|
-
case "Atoi":
|
|
1705
|
-
return `(Number.isNaN(Number(${args[0]})) ? [0, "invalid syntax"] : [Number(${args[0]}) | 0, null])`;
|
|
1706
|
-
case "FormatBool":
|
|
1707
|
-
return `String(${args[0]})`;
|
|
1708
|
-
case "FormatInt":
|
|
1709
|
-
return `(${args[0]}).toString(${args[1]})`;
|
|
1710
|
-
case "FormatFloat":
|
|
1711
|
-
return `String(${args[0]})`;
|
|
1712
|
-
case "ParseFloat":
|
|
1713
|
-
return `(Number.isNaN(Number(${args[0]})) ? [0, "invalid syntax"] : [Number(${args[0]}), null])`;
|
|
1714
|
-
case "ParseInt":
|
|
1715
|
-
return `(Number.isNaN(parseInt(${args[0]}, ${args[1]} || 10)) ? [0, "invalid syntax"] : [parseInt(${args[0]}, ${args[1]} || 10), null])`;
|
|
1716
|
-
case "ParseBool":
|
|
1717
|
-
return `(${args[0]} === "true" || ${args[0]} === "1" ? [true, null] : ${args[0]} === "false" || ${args[0]} === "0" ? [false, null] : [false, "invalid syntax"])`;
|
|
1718
|
-
case "Quote":
|
|
1719
|
-
return `JSON.stringify(${args[0]})`;
|
|
1720
|
-
case "Unquote":
|
|
1721
|
-
return `((s) => { try { const v = JSON.parse(s); return [v, null]; } catch(e) { return ["", "invalid syntax"]; } })(${args[0]})`;
|
|
1722
|
-
case "AppendInt":
|
|
1723
|
-
return `[...(${args[0]}), ...new TextEncoder().encode((${args[1]}).toString(${args[2]}))]`;
|
|
1724
|
-
case "AppendFloat":
|
|
1725
|
-
return `[...(${args[0]}), ...new TextEncoder().encode(${args[1]}.toFixed(${args[3]} < 0 ? 6 : ${args[3]}))]`;
|
|
1726
|
-
default:
|
|
1727
|
-
return undefined;
|
|
1728
|
-
}
|
|
1729
|
-
},
|
|
1730
|
-
|
|
1731
|
-
_genSort(fn, a) {
|
|
1732
|
-
const args = a();
|
|
1733
|
-
switch (fn) {
|
|
1734
|
-
case "Ints":
|
|
1735
|
-
case "Float64s":
|
|
1736
|
-
return `${args[0]}.sort((a, b) => a - b)`;
|
|
1737
|
-
case "Strings":
|
|
1738
|
-
return `${args[0]}.sort()`;
|
|
1739
|
-
case "Slice":
|
|
1740
|
-
case "SliceStable":
|
|
1741
|
-
return `${args[0]}.sort((a, b) => ${args[1]}(a, b) ? -1 : ${args[1]}(b, a) ? 1 : 0)`;
|
|
1742
|
-
case "SliceIsSorted":
|
|
1743
|
-
return `${args[0]}.every((v, i, a) => i === 0 || ${args[1]}(a[i - 1], v))`;
|
|
1744
|
-
case "Search":
|
|
1745
|
-
return `((n, f) => { let lo = 0, hi = n; while (lo < hi) { const mid = (lo + hi) >>> 1; if (f(mid)) hi = mid; else lo = mid + 1; } return lo; })(${args[0]}, ${args[1]})`;
|
|
1746
|
-
case "IntsAreSorted":
|
|
1747
|
-
case "Float64sAreSorted":
|
|
1748
|
-
case "StringsAreSorted":
|
|
1749
|
-
return `(${args[0]}).every((v, i, a) => i === 0 || a[i - 1] <= v)`;
|
|
1750
|
-
default:
|
|
1751
|
-
return undefined;
|
|
1752
|
-
}
|
|
1753
|
-
},
|
|
1754
|
-
|
|
1755
|
-
_genMath(fn, a) {
|
|
1756
|
-
const args = a();
|
|
1757
|
-
switch (fn) {
|
|
1758
|
-
case "Abs":
|
|
1759
|
-
return `Math.abs(${args[0]})`;
|
|
1760
|
-
case "Floor":
|
|
1761
|
-
return `Math.floor(${args[0]})`;
|
|
1762
|
-
case "Ceil":
|
|
1763
|
-
return `Math.ceil(${args[0]})`;
|
|
1764
|
-
case "Round":
|
|
1765
|
-
return `Math.round(${args[0]})`;
|
|
1766
|
-
case "Sqrt":
|
|
1767
|
-
return `Math.sqrt(${args[0]})`;
|
|
1768
|
-
case "Cbrt":
|
|
1769
|
-
return `Math.cbrt(${args[0]})`;
|
|
1770
|
-
case "Pow":
|
|
1771
|
-
return `Math.pow(${args[0]}, ${args[1]})`;
|
|
1772
|
-
case "Log":
|
|
1773
|
-
return `Math.log(${args[0]})`;
|
|
1774
|
-
case "Log2":
|
|
1775
|
-
return `Math.log2(${args[0]})`;
|
|
1776
|
-
case "Log10":
|
|
1777
|
-
return `Math.log10(${args[0]})`;
|
|
1778
|
-
case "Sin":
|
|
1779
|
-
return `Math.sin(${args[0]})`;
|
|
1780
|
-
case "Cos":
|
|
1781
|
-
return `Math.cos(${args[0]})`;
|
|
1782
|
-
case "Tan":
|
|
1783
|
-
return `Math.tan(${args[0]})`;
|
|
1784
|
-
case "Min":
|
|
1785
|
-
return `Math.min(${args[0]}, ${args[1]})`;
|
|
1786
|
-
case "Max":
|
|
1787
|
-
return `Math.max(${args[0]}, ${args[1]})`;
|
|
1788
|
-
case "Mod":
|
|
1789
|
-
return `${args[0]} % ${args[1]}`;
|
|
1790
|
-
case "Inf":
|
|
1791
|
-
return `(${args[0]} >= 0 ? Infinity : -Infinity)`;
|
|
1792
|
-
case "IsNaN":
|
|
1793
|
-
return `Number.isNaN(${args[0]})`;
|
|
1794
|
-
case "IsInf":
|
|
1795
|
-
return `(${args[1]} > 0 ? ${args[0]} === Infinity : ${args[1]} < 0 ? ${args[0]} === -Infinity : !Number.isFinite(${args[0]}))`;
|
|
1796
|
-
case "NaN":
|
|
1797
|
-
return "NaN";
|
|
1798
|
-
case "Atan":
|
|
1799
|
-
return `Math.atan(${args[0]})`;
|
|
1800
|
-
case "Atan2":
|
|
1801
|
-
return `Math.atan2(${args[0]}, ${args[1]})`;
|
|
1802
|
-
case "Asin":
|
|
1803
|
-
return `Math.asin(${args[0]})`;
|
|
1804
|
-
case "Acos":
|
|
1805
|
-
return `Math.acos(${args[0]})`;
|
|
1806
|
-
case "Exp":
|
|
1807
|
-
return `Math.exp(${args[0]})`;
|
|
1808
|
-
case "Exp2":
|
|
1809
|
-
return `Math.pow(2, ${args[0]})`;
|
|
1810
|
-
case "Trunc":
|
|
1811
|
-
return `Math.trunc(${args[0]})`;
|
|
1812
|
-
case "Hypot":
|
|
1813
|
-
return `Math.hypot(${args[0]}, ${args[1]})`;
|
|
1814
|
-
case "Signbit":
|
|
1815
|
-
return `(${args[0]} < 0 || Object.is(${args[0]}, -0))`;
|
|
1816
|
-
case "Copysign":
|
|
1817
|
-
return `(Math.abs(${args[0]}) * (${args[1]} < 0 || Object.is(${args[1]}, -0) ? -1 : 1))`;
|
|
1818
|
-
case "Dim":
|
|
1819
|
-
return `Math.max(${args[0]} - ${args[1]}, 0)`;
|
|
1820
|
-
case "Remainder":
|
|
1821
|
-
return `(${args[0]} - Math.round(${args[0]} / ${args[1]}) * ${args[1]})`;
|
|
1822
|
-
default:
|
|
1823
|
-
return undefined;
|
|
1824
|
-
}
|
|
1825
|
-
},
|
|
1826
|
-
|
|
1827
|
-
_genUnicode(fn, a) {
|
|
1828
|
-
const args = a();
|
|
1829
|
-
const cp = `String.fromCodePoint(${args[0]})`;
|
|
1830
|
-
switch (fn) {
|
|
1831
|
-
case "IsLetter":
|
|
1832
|
-
return `/\\p{L}/u.test(${cp})`;
|
|
1833
|
-
case "IsDigit":
|
|
1834
|
-
return `/\\p{Nd}/u.test(${cp})`;
|
|
1835
|
-
case "IsSpace":
|
|
1836
|
-
return `/\\s/.test(${cp})`;
|
|
1837
|
-
case "IsUpper":
|
|
1838
|
-
return `((__c) => __c === __c.toUpperCase() && /\\p{L}/u.test(__c))(${cp})`;
|
|
1839
|
-
case "IsLower":
|
|
1840
|
-
return `((__c) => __c === __c.toLowerCase() && /\\p{L}/u.test(__c))(${cp})`;
|
|
1841
|
-
case "IsPunct":
|
|
1842
|
-
return `/\\p{P}/u.test(${cp})`;
|
|
1843
|
-
case "IsControl":
|
|
1844
|
-
return `/\\p{Cc}/u.test(${cp})`;
|
|
1845
|
-
case "IsPrint":
|
|
1846
|
-
return `!/\\p{Cc}/u.test(${cp})`;
|
|
1847
|
-
case "IsGraphic":
|
|
1848
|
-
return `!/\\p{Cc}/u.test(${cp})`;
|
|
1849
|
-
case "ToUpper":
|
|
1850
|
-
return `${cp}.toUpperCase().codePointAt(0)`;
|
|
1851
|
-
case "ToLower":
|
|
1852
|
-
return `${cp}.toLowerCase().codePointAt(0)`;
|
|
1853
|
-
default:
|
|
1854
|
-
return undefined;
|
|
1855
|
-
}
|
|
1856
|
-
},
|
|
1857
|
-
|
|
1858
|
-
_genOs(fn, a) {
|
|
1859
|
-
const args = a();
|
|
1860
|
-
switch (fn) {
|
|
1861
|
-
case "Exit":
|
|
1862
|
-
return `process.exit(${args[0]})`;
|
|
1863
|
-
case "Getenv":
|
|
1864
|
-
return `(process.env[${args[0]}] ?? "")`;
|
|
1865
|
-
default:
|
|
1866
|
-
return undefined;
|
|
1867
|
-
}
|
|
1868
|
-
},
|
|
1869
|
-
|
|
1870
|
-
_genErrors(fn, a) {
|
|
1871
|
-
const args = a();
|
|
1872
|
-
switch (fn) {
|
|
1873
|
-
case "New":
|
|
1874
|
-
this._usesError = true;
|
|
1875
|
-
return `__error(${args[0]})`;
|
|
1876
|
-
case "Is":
|
|
1877
|
-
this._usesErrorIs = true;
|
|
1878
|
-
return `__errorIs(${args[0]}, ${args[1]})`;
|
|
1879
|
-
case "Unwrap":
|
|
1880
|
-
return `(${args[0]}?._cause ?? null)`;
|
|
1881
|
-
default:
|
|
1882
|
-
return undefined;
|
|
1883
|
-
}
|
|
1884
|
-
},
|
|
1885
|
-
|
|
1886
|
-
_genTimeMethodCall(method, expr) {
|
|
1887
|
-
const recv = expr.func.expr;
|
|
1888
|
-
const recvJs = this.genExpr(recv);
|
|
1889
|
-
const args = expr.args.map((a) => this.genExpr(a));
|
|
1890
|
-
switch (method) {
|
|
1891
|
-
case "Format":
|
|
1892
|
-
this._usesTimeFmt = true;
|
|
1893
|
-
return `__timeFmt(${recvJs}._d, ${args[0]})`;
|
|
1894
|
-
case "String":
|
|
1895
|
-
this._usesTimeFmt = true;
|
|
1896
|
-
return `__timeFmt(${recvJs}._d, "2006-01-02T15:04:05Z07:00")`;
|
|
1897
|
-
case "Year":
|
|
1898
|
-
return `${recvJs}._d.getFullYear()`;
|
|
1899
|
-
case "Month":
|
|
1900
|
-
return `${recvJs}._d.getMonth() + 1`;
|
|
1901
|
-
case "Day":
|
|
1902
|
-
return `${recvJs}._d.getDate()`;
|
|
1903
|
-
case "Hour":
|
|
1904
|
-
return `${recvJs}._d.getHours()`;
|
|
1905
|
-
case "Minute":
|
|
1906
|
-
return `${recvJs}._d.getMinutes()`;
|
|
1907
|
-
case "Second":
|
|
1908
|
-
return `${recvJs}._d.getSeconds()`;
|
|
1909
|
-
case "Weekday":
|
|
1910
|
-
return `${recvJs}._d.getDay()`;
|
|
1911
|
-
case "Unix":
|
|
1912
|
-
return `Math.floor(${recvJs}._d.getTime() / 1000)`;
|
|
1913
|
-
case "UnixMilli":
|
|
1914
|
-
return `${recvJs}._d.getTime()`;
|
|
1915
|
-
case "Add":
|
|
1916
|
-
return `{_d: new Date(${recvJs}._d.getTime() + (${args[0]}))}`;
|
|
1917
|
-
case "Sub":
|
|
1918
|
-
return `${recvJs}._d.getTime() - (${args[0]})._d.getTime()`;
|
|
1919
|
-
case "Before":
|
|
1920
|
-
return `${recvJs}._d < (${args[0]})._d`;
|
|
1921
|
-
case "After":
|
|
1922
|
-
return `${recvJs}._d > (${args[0]})._d`;
|
|
1923
|
-
case "Equal":
|
|
1924
|
-
return `${recvJs}._d.getTime() === (${args[0]})._d.getTime()`;
|
|
1925
|
-
default:
|
|
1926
|
-
return undefined;
|
|
1927
|
-
}
|
|
1928
|
-
},
|
|
1929
|
-
|
|
1930
|
-
_genTime(fn, a) {
|
|
1931
|
-
const args = a();
|
|
1932
|
-
switch (fn) {
|
|
1933
|
-
case "Now":
|
|
1934
|
-
return "{_d: new Date()}";
|
|
1935
|
-
case "Since":
|
|
1936
|
-
return `(Date.now() - (${args[0]})._d.getTime())`;
|
|
1937
|
-
case "Sleep":
|
|
1938
|
-
return `await new Promise(r => setTimeout(r, ${args[0]} / 1000000))`;
|
|
1939
|
-
case "Parse":
|
|
1940
|
-
this._usesTimeParse = true;
|
|
1941
|
-
return `__timeParse(${args[0]}, ${args[1]})`;
|
|
1942
|
-
case "Unix":
|
|
1943
|
-
return `{_d: new Date((${args[0]}) * 1000)}`;
|
|
1944
|
-
case "Date":
|
|
1945
|
-
return `{_d: new Date(${args[0]}, ${args[1]} - 1, ${args[2]}, ${args[3]}, ${args[4]}, ${args[5]})}`;
|
|
1946
|
-
default:
|
|
1947
|
-
return undefined;
|
|
1948
|
-
}
|
|
1949
|
-
},
|
|
1950
|
-
|
|
1951
|
-
_genRand(fn, a) {
|
|
1952
|
-
const args = a();
|
|
1953
|
-
switch (fn) {
|
|
1954
|
-
case "Intn":
|
|
1955
|
-
case "Int63n":
|
|
1956
|
-
case "Int31n":
|
|
1957
|
-
return `Math.floor(Math.random() * ${args[0]})`;
|
|
1958
|
-
case "Float64":
|
|
1959
|
-
case "Float32":
|
|
1960
|
-
return "Math.random()";
|
|
1961
|
-
case "Int":
|
|
1962
|
-
case "Int31":
|
|
1963
|
-
return "Math.floor(Math.random() * 2147483647)";
|
|
1964
|
-
case "Int63":
|
|
1965
|
-
return "Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)";
|
|
1966
|
-
case "Seed":
|
|
1967
|
-
return "(void 0)";
|
|
1968
|
-
case "Shuffle":
|
|
1969
|
-
return `((n, f) => { for (let i = n - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); f(i, j); } })(${args[0]}, ${args[1]})`;
|
|
1970
|
-
case "Perm":
|
|
1971
|
-
return `((n) => { const a = Array.from({length: n}, (_, i) => i); for (let i = n - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [a[i], a[j]] = [a[j], a[i]]; } return a; })(${args[0]})`;
|
|
1972
|
-
default:
|
|
1973
|
-
return undefined;
|
|
1974
|
-
}
|
|
1975
|
-
},
|
|
1976
|
-
|
|
1977
|
-
_genUtf8(fn, a) {
|
|
1978
|
-
const args = a();
|
|
1979
|
-
switch (fn) {
|
|
1980
|
-
case "RuneCountInString":
|
|
1981
|
-
return `[...(${args[0]})].length`;
|
|
1982
|
-
case "RuneLen":
|
|
1983
|
-
return `((r) => r < 0 ? -1 : r <= 0x7F ? 1 : r <= 0x7FF ? 2 : r <= 0xFFFF ? (r >= 0xD800 && r <= 0xDFFF ? -1 : 3) : r <= 0x10FFFF ? 4 : -1)(${args[0]})`;
|
|
1984
|
-
case "ValidString":
|
|
1985
|
-
return `/[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|(?<![\\uD800-\\uDBFF])[\\uDC00-\\uDFFF]/.test(${args[0]}) === false`;
|
|
1986
|
-
case "ValidRune":
|
|
1987
|
-
return `(${args[0]}) >= 0 && (${args[0]}) <= 0x10FFFF && !((${args[0]}) >= 0xD800 && (${args[0]}) <= 0xDFFF)`;
|
|
1988
|
-
case "DecodeRuneInString":
|
|
1989
|
-
return `((s) => { if (!s) return [0xFFFD, 0]; const cp = s.codePointAt(0); return [cp, cp > 0xFFFF ? 2 : 1]; })(${args[0]})`;
|
|
1990
|
-
case "DecodeLastRuneInString":
|
|
1991
|
-
return `((s) => { if (!s) return [0xFFFD, 0]; const i = s.length > 1 && s.charCodeAt(s.length - 1) >= 0xDC00 && s.charCodeAt(s.length - 1) <= 0xDFFF ? s.length - 2 : s.length - 1; const cp = s.codePointAt(i); return [cp, cp > 0xFFFF ? 2 : 1]; })(${args[0]})`;
|
|
1992
|
-
case "FullRuneInString":
|
|
1993
|
-
return `(${args[0]}).length > 0`;
|
|
1994
|
-
default:
|
|
1995
|
-
return undefined;
|
|
1996
|
-
}
|
|
1997
|
-
},
|
|
1998
|
-
|
|
1999
|
-
_genPath(fn, a, expr) {
|
|
2000
|
-
const args = a();
|
|
2001
|
-
switch (fn) {
|
|
2002
|
-
case "Base":
|
|
2003
|
-
return `((p) => { if (!p) return "."; const stripped = p.replace(/\\/+$/, ""); if (!stripped) return "/"; const i = stripped.lastIndexOf("/"); return i < 0 ? stripped : stripped.slice(i + 1) || "/"; })(${args[0]})`;
|
|
2004
|
-
case "Dir":
|
|
2005
|
-
return `((p) => { const i = p.lastIndexOf("/"); if (i < 0) return "."; if (i === 0) return "/"; return p.slice(0, i); })(${args[0]})`;
|
|
2006
|
-
case "Ext":
|
|
2007
|
-
return `((p) => { const b = p.slice(p.lastIndexOf("/") + 1); const i = b.lastIndexOf("."); return i <= 0 ? "" : b.slice(i); })(${args[0]})`;
|
|
2008
|
-
case "Join": {
|
|
2009
|
-
this._usesPathClean = true;
|
|
2010
|
-
const allArgs = expr.args.map((e) => this.genExpr(e)).join(", ");
|
|
2011
|
-
return `__pathClean([${allArgs}].filter(x => x !== "").join("/"))`;
|
|
810
|
+
_genPrimitiveConversion(expr, inner, target) {
|
|
811
|
+
switch (target) {
|
|
812
|
+
case "string": {
|
|
813
|
+
const srcType = expr.expr._type;
|
|
814
|
+
if (srcType && this.isIntType(srcType))
|
|
815
|
+
return `String.fromCodePoint(${inner})`;
|
|
816
|
+
if (srcType?.kind === "slice" && srcType.elem?.name === "int")
|
|
817
|
+
return `${inner}.map(c => String.fromCharCode(c)).join("")`;
|
|
818
|
+
return `String(${inner})`;
|
|
2012
819
|
}
|
|
2013
|
-
case "
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
return `((pat, name) => { try { const sp=/[.+^$()|[\\]\\\\]/g; const re = new RegExp("^" + pat.replace(sp, "\\\\$&").replace(/\\*/g, "[^/]*").replace(/\\?/g, "[^/]") + "$"); return [re.test(name), null]; } catch(e) { return [false, "syntax error in pattern"]; } })(${args[0]}, ${args[1]})`;
|
|
820
|
+
case "int":
|
|
821
|
+
case "byte":
|
|
822
|
+
case "rune":
|
|
823
|
+
return `Math.trunc(Number(${inner}))`;
|
|
824
|
+
case "float64":
|
|
825
|
+
return `Number(${inner})`;
|
|
826
|
+
case "bool":
|
|
827
|
+
return `Boolean(${inner})`;
|
|
2022
828
|
default:
|
|
2023
|
-
return
|
|
829
|
+
return inner;
|
|
2024
830
|
}
|
|
2025
831
|
},
|
|
2026
832
|
};
|