gofront 0.0.8 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -12
- package/package.json +8 -4
- package/src/codegen/expressions.js +520 -1717
- package/src/{codegen.js → codegen/index.js} +155 -184
- package/src/codegen/runtime.js +151 -0
- package/src/codegen/statements.js +305 -296
- package/src/codegen/stdlib/builder.js +36 -0
- package/src/codegen/stdlib/bytes.js +62 -0
- package/src/codegen/stdlib/errors.js +17 -0
- package/src/codegen/stdlib/fmt.js +101 -0
- package/src/codegen/stdlib/gom.js +152 -0
- package/src/codegen/stdlib/html.js +17 -0
- package/src/codegen/stdlib/index.js +95 -0
- package/src/codegen/stdlib/io.js +33 -0
- package/src/codegen/stdlib/maps.js +42 -0
- package/src/codegen/stdlib/math.js +53 -0
- package/src/codegen/stdlib/os.js +10 -0
- package/src/codegen/stdlib/path.js +31 -0
- package/src/codegen/stdlib/rand.js +24 -0
- package/src/codegen/stdlib/regexp.js +51 -0
- package/src/codegen/stdlib/slices.js +48 -0
- package/src/codegen/stdlib/sort.js +28 -0
- package/src/codegen/stdlib/strconv.js +30 -0
- package/src/codegen/stdlib/strings.js +83 -0
- package/src/codegen/stdlib/time.js +54 -0
- package/src/codegen/stdlib/unicode.js +22 -0
- package/src/codegen/stdlib/utf8.js +23 -0
- package/src/codegen/templ.js +281 -0
- package/src/compiler.js +102 -62
- package/src/dts-parser.js +355 -371
- package/src/index.js +22 -54
- package/src/lexer.js +200 -286
- package/src/minifier.js +305 -318
- package/src/parser/expressions.js +54 -36
- package/src/{parser.js → parser/index.js} +12 -5
- package/src/parser/statements.js +29 -35
- package/src/parser/types.js +31 -29
- package/src/templ-lexer.js +737 -0
- package/src/templ-parser.js +235 -0
- package/src/tokens.js +109 -0
- package/src/typechecker/assignability.js +258 -0
- package/src/typechecker/expressions.js +559 -549
- package/src/typechecker/index.js +555 -0
- package/src/typechecker/resolve.js +421 -0
- package/src/typechecker/statements.js +267 -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 +83 -0
- package/src/typechecker/types.js +6 -3
- package/src/typechecker.js +0 -1271
|
@@ -17,10 +17,26 @@
|
|
|
17
17
|
// - make(map[K]V) → {}
|
|
18
18
|
// - for range → for...of with .entries()
|
|
19
19
|
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
|
|
20
|
+
import { isComplex, isNumeric } from "../typechecker/types.js";
|
|
21
|
+
import { expressionGenMethods } from "./expressions.js";
|
|
22
|
+
import {
|
|
23
|
+
HELPER_APPEND,
|
|
24
|
+
HELPER_CDIV,
|
|
25
|
+
HELPER_CMUL,
|
|
26
|
+
HELPER_EQUAL,
|
|
27
|
+
HELPER_ERROR,
|
|
28
|
+
HELPER_ERROR_IS,
|
|
29
|
+
HELPER_LEN,
|
|
30
|
+
HELPER_PATH_CLEAN,
|
|
31
|
+
HELPER_S,
|
|
32
|
+
HELPER_SPRINTF,
|
|
33
|
+
HELPER_TIME_FMT,
|
|
34
|
+
HELPER_TIME_PARSE,
|
|
35
|
+
} from "./runtime.js";
|
|
36
|
+
import { buildSourceMap } from "./source-map.js";
|
|
37
|
+
import { statementGenMethods } from "./statements.js";
|
|
38
|
+
import { stdlibGenMethods } from "./stdlib/index.js";
|
|
39
|
+
import { templGenMethods } from "./templ.js";
|
|
24
40
|
|
|
25
41
|
export class CodeGen {
|
|
26
42
|
// jsImports: Map<importPath, string[]> — npm package imports to emit at top of file
|
|
@@ -88,43 +104,15 @@ export class CodeGen {
|
|
|
88
104
|
this.indent--;
|
|
89
105
|
}
|
|
90
106
|
|
|
91
|
-
|
|
92
|
-
// Collect struct names first (needed by zero-value helpers and genCompositeLit)
|
|
93
|
-
for (const d of program.decls) {
|
|
94
|
-
if (d.kind === "TypeDecl" && d.type.kind === "StructType") {
|
|
95
|
-
this.structNames.add(d.name);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Build a map: typeName → [MethodDecl, ...]
|
|
100
|
-
const methods = new Map();
|
|
101
|
-
for (const d of program.decls) {
|
|
102
|
-
if (d.kind === "MethodDecl") {
|
|
103
|
-
const name = d.recvType.name;
|
|
104
|
-
if (!methods.has(name)) methods.set(name, []);
|
|
105
|
-
methods.get(name).push(d);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Collect named non-struct types that have methods (emitted as wrapper classes)
|
|
110
|
-
for (const d of program.decls) {
|
|
111
|
-
if (
|
|
112
|
-
d.kind === "TypeDecl" &&
|
|
113
|
-
d.type.kind !== "StructType" &&
|
|
114
|
-
d.type.kind !== "InterfaceType" &&
|
|
115
|
-
(methods.get(d.name) ?? []).length > 0
|
|
116
|
-
) {
|
|
117
|
-
this.namedWrapperNames.add(d.name);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Emit ESM import statements for npm packages
|
|
107
|
+
_emitJsImports() {
|
|
122
108
|
for (const [importPath, names] of this.jsImports) {
|
|
123
109
|
if (names.length === 0) continue;
|
|
124
110
|
this.line(`import { ${names.join(", ")} } from '${importPath}';`);
|
|
125
111
|
}
|
|
126
112
|
if (this.jsImports.size > 0) this.blank();
|
|
113
|
+
}
|
|
127
114
|
|
|
115
|
+
_emitTypeDecls(program, methods) {
|
|
128
116
|
for (const d of program.decls) {
|
|
129
117
|
if (d.kind === "TypeDecl") {
|
|
130
118
|
this._currentSrcFileIdx = d._srcFileIdx ?? 0;
|
|
@@ -132,7 +120,9 @@ export class CodeGen {
|
|
|
132
120
|
this.blank();
|
|
133
121
|
}
|
|
134
122
|
}
|
|
123
|
+
}
|
|
135
124
|
|
|
125
|
+
_emitVarConstDecls(program) {
|
|
136
126
|
for (const d of program.decls) {
|
|
137
127
|
if (d.kind === "VarDecl") {
|
|
138
128
|
this._currentSrcFileIdx = d._srcFileIdx ?? 0;
|
|
@@ -145,8 +135,28 @@ export class CodeGen {
|
|
|
145
135
|
this.blank();
|
|
146
136
|
}
|
|
147
137
|
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
_callInitAndMain(initNames, program) {
|
|
141
|
+
for (const name of initNames) this.line(`${name}();`);
|
|
142
|
+
if (program.decls.some((d) => d.kind === "FuncDecl" && d.name === "main"))
|
|
143
|
+
this.line("main();");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
generate(program) {
|
|
147
|
+
const methods = this._collectDecls(program);
|
|
148
|
+
this._emitJsImports();
|
|
149
|
+
this._emitTypeDecls(program, methods);
|
|
150
|
+
this._emitVarConstDecls(program);
|
|
151
|
+
const initNames = this._emitFuncDecls(program);
|
|
152
|
+
this._callInitAndMain(initNames, program);
|
|
153
|
+
this._prependHelpers();
|
|
154
|
+
while (this.out[0] === "") this.out.shift();
|
|
155
|
+
return this.out.join("\n");
|
|
156
|
+
}
|
|
148
157
|
|
|
149
|
-
|
|
158
|
+
// Emits FuncDecl and TemplDecl nodes; returns renamed init function names.
|
|
159
|
+
_emitFuncDecls(program) {
|
|
150
160
|
let initCount = 0;
|
|
151
161
|
const initNames = [];
|
|
152
162
|
for (const d of program.decls) {
|
|
@@ -161,121 +171,73 @@ export class CodeGen {
|
|
|
161
171
|
this.genFuncDecl(d);
|
|
162
172
|
}
|
|
163
173
|
this.blank();
|
|
174
|
+
} else if (d.kind === "TemplDecl") {
|
|
175
|
+
this._currentSrcFileIdx = d._srcFileIdx ?? 0;
|
|
176
|
+
this.genTemplDecl(d);
|
|
177
|
+
this.blank();
|
|
164
178
|
}
|
|
165
179
|
}
|
|
180
|
+
return initNames;
|
|
181
|
+
}
|
|
166
182
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
183
|
+
// Collects struct names, method map, and named wrapper names from program decls.
|
|
184
|
+
// Returns the method map (typeName → MethodDecl[]).
|
|
185
|
+
_collectStructNames(program) {
|
|
186
|
+
for (const d of program.decls) {
|
|
187
|
+
if (d.kind === "TypeDecl" && d.type.kind === "StructType")
|
|
188
|
+
this.structNames.add(d.name);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
173
191
|
|
|
174
|
-
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
helpers.push("var __s = __s || function(a) { return a || []; };");
|
|
186
|
-
if (this._usesEqual)
|
|
187
|
-
helpers.push(
|
|
188
|
-
'var __equal = __equal || function __equal(a,b){if(a===b)return true;if(a===null||b===null)return false;if(Array.isArray(a)&&Array.isArray(b)){if(a.length!==b.length)return false;for(let i=0;i<a.length;i++)if(!__equal(a[i],b[i]))return false;return true;}if(typeof a==="object"&&typeof b==="object"){const ka=Object.keys(a),kb=Object.keys(b);if(ka.length!==kb.length)return false;for(const k of ka)if(!__equal(a[k],b[k]))return false;return true;}return false;};',
|
|
189
|
-
);
|
|
190
|
-
if (this._usesCmul)
|
|
191
|
-
helpers.push(
|
|
192
|
-
"var __cmul = __cmul || function(a, b) { return { re: a.re * b.re - a.im * b.im, im: a.re * b.im + a.im * b.re }; };",
|
|
193
|
-
);
|
|
194
|
-
if (this._usesCdiv)
|
|
195
|
-
helpers.push(
|
|
196
|
-
"var __cdiv = __cdiv || function(a, b) { const d = b.re * b.re + b.im * b.im; return { re: (a.re * b.re + a.im * b.im) / d, im: (a.im * b.re - a.re * b.im) / d }; };",
|
|
197
|
-
);
|
|
198
|
-
if (this._usesSprintf)
|
|
199
|
-
helpers.push(
|
|
200
|
-
[
|
|
201
|
-
"var __sprintf = __sprintf || function(f,...a){let i=0;return f.replace(/%([#+\\- 0]*)([0-9]*)\\.?([0-9]*)[sdvftxXqobeEgGw%]/g,(m)=>{",
|
|
202
|
-
"if(m==='%%')return'%';const fl=m.slice(1,-1),verb=m.slice(-1),v=a[i++];",
|
|
203
|
-
"const pad=(s,w,z)=>{w=parseInt(w)||0;if(!w)return s;const p=(z?'0':' ').repeat(Math.max(0,w-s.length));return fl.includes('-')?s+p:p+s;};",
|
|
204
|
-
"const [,flags,width,prec]=m.match(/^%([#+\\- 0]*)([0-9]*)\\.?([0-9]*)/)||[];",
|
|
205
|
-
"const zero=flags?.includes('0')&&!flags?.includes('-');",
|
|
206
|
-
"switch(verb){",
|
|
207
|
-
"case's':return pad(String(v==null?'<nil>':v),width,false);",
|
|
208
|
-
"case'd':return pad(String(Math.trunc(Number(v))),width,zero);",
|
|
209
|
-
"case'v':{if(typeof v==='object'&&v!==null&&'re' in v&&'im' in v){const sign=v.im>=0?'+':'';return pad('('+v.re+sign+v.im+'i)',width,false);}return pad(String(v==null?'<nil>':v),width,false);}",
|
|
210
|
-
"case'f':{const n=Number(v),p=prec!==''?parseInt(prec):6;return pad(n.toFixed(p),width,zero);}",
|
|
211
|
-
"case't':return pad(String(!!v),width,false);",
|
|
212
|
-
"case'x':return pad((Number(v)>>>0).toString(16),width,zero);",
|
|
213
|
-
"case'X':return pad((Number(v)>>>0).toString(16).toUpperCase(),width,zero);",
|
|
214
|
-
"case'o':return pad((Number(v)>>>0).toString(8),width,zero);",
|
|
215
|
-
"case'b':return pad((Number(v)>>>0).toString(2),width,zero);",
|
|
216
|
-
"case'q':return pad('\"'+String(v==null?'':v).replace(/\\\\/g,'\\\\\\\\').replace(/\"/g,'\\\\\"')+'\"',width,false);",
|
|
217
|
-
"case'e':case'E':{const n=Number(v),p=prec!==''?parseInt(prec):6;return pad(n.toExponential(p),width,zero);}",
|
|
218
|
-
"case'g':case'G':{const n=Number(v);return pad(prec!==''?n.toPrecision(parseInt(prec)):String(n),width,zero);}",
|
|
219
|
-
"case'w':return pad(String(v==null?'<nil>':typeof v==='object'&&v.Error?v.Error():v),width,false);",
|
|
220
|
-
"default:return m;}});",
|
|
221
|
-
"};",
|
|
222
|
-
].join(""),
|
|
223
|
-
);
|
|
224
|
-
if (this._usesError)
|
|
225
|
-
helpers.push(
|
|
226
|
-
"var __error = __error || function(msg, cause) { return { Error() { return msg; }, toString() { return msg; }, _msg: msg, _cause: cause ?? null }; };",
|
|
227
|
-
);
|
|
228
|
-
if (this._usesErrorIs)
|
|
229
|
-
helpers.push(
|
|
230
|
-
'var __errorIs = __errorIs || function(err, target) { while (err !== null && err !== undefined) { if (err === target) return true; if (typeof err === "object" && typeof target === "object" && err._msg !== undefined && target._msg !== undefined && err._msg === target._msg) return true; err = err?._cause ?? null; } return false; };',
|
|
231
|
-
);
|
|
232
|
-
if (this._usesPathClean)
|
|
233
|
-
helpers.push(
|
|
234
|
-
'var __pathClean = __pathClean || function(p) { if (!p) return "."; const abs = p.startsWith("/"); const parts = p.split("/").reduce((acc, s) => { if (s === "" || s === ".") return acc; if (s === "..") { if (acc.length && acc[acc.length-1] !== "..") acc.pop(); else if (!abs) acc.push(".."); } else acc.push(s); return acc; }, []); return (abs ? "/" : "") + (parts.join("/") || "."); };',
|
|
235
|
-
);
|
|
236
|
-
if (this._usesTimeFmt)
|
|
237
|
-
helpers.push(
|
|
238
|
-
[
|
|
239
|
-
"var __timeFmt = __timeFmt || function(d, layout) {",
|
|
240
|
-
'const pad = (n, w=2) => String(n).padStart(w, "0");',
|
|
241
|
-
'const DAYS=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];',
|
|
242
|
-
'const MONTHS=["January","February","March","April","May","June","July","August","September","October","November","December"];',
|
|
243
|
-
"return layout.replace(/2006|06|Monday|Mon|January|Jan|01|1|02|2|15|PM|3|04|4|05|5|Z07:00|\\.000|\\.999/g, t => {",
|
|
244
|
-
"switch(t){",
|
|
245
|
-
'case "2006": return d.getFullYear();',
|
|
246
|
-
'case "06": return String(d.getFullYear()).slice(-2);',
|
|
247
|
-
'case "Monday": return DAYS[d.getDay()];',
|
|
248
|
-
'case "Mon": return DAYS[d.getDay()].slice(0,3);',
|
|
249
|
-
'case "January": return MONTHS[d.getMonth()];',
|
|
250
|
-
'case "Jan": return MONTHS[d.getMonth()].slice(0,3);',
|
|
251
|
-
'case "01": return pad(d.getMonth()+1);',
|
|
252
|
-
'case "1": return String(d.getMonth()+1);',
|
|
253
|
-
'case "02": return pad(d.getDate());',
|
|
254
|
-
'case "2": return String(d.getDate());',
|
|
255
|
-
'case "15": return pad(d.getHours());',
|
|
256
|
-
'case "PM": return d.getHours()<12?"AM":"PM";',
|
|
257
|
-
'case "3": return String(d.getHours()%12||12);',
|
|
258
|
-
'case "04": return pad(d.getMinutes());',
|
|
259
|
-
'case "4": return String(d.getMinutes());',
|
|
260
|
-
'case "05": return pad(d.getSeconds());',
|
|
261
|
-
'case "5": return String(d.getSeconds());',
|
|
262
|
-
'case "Z07:00": { const o=-d.getTimezoneOffset(); return o===0?"Z":(o>0?"+":"-")+pad(Math.floor(Math.abs(o)/60))+":"+pad(Math.abs(o)%60); }',
|
|
263
|
-
'case ".000": return "."+pad(d.getMilliseconds(),3);',
|
|
264
|
-
'case ".999": return d.getMilliseconds()>0?"."+String(d.getMilliseconds()).replace(/0+$/,""):"";',
|
|
265
|
-
"default: return t;}});",
|
|
266
|
-
"};",
|
|
267
|
-
].join(""),
|
|
268
|
-
);
|
|
269
|
-
if (this._usesTimeParse)
|
|
270
|
-
helpers.push(
|
|
271
|
-
'var __timeParse = __timeParse || function(layout, value) { try { const d = new Date(value); if (isNaN(d.getTime())) throw 0; return [{_d:d}, null]; } catch { return [{_d:new Date(0)}, "parsing time: cannot parse"]; } };',
|
|
272
|
-
);
|
|
192
|
+
_collectMethodMap(program) {
|
|
193
|
+
const methods = new Map();
|
|
194
|
+
for (const d of program.decls) {
|
|
195
|
+
if (d.kind === "MethodDecl") {
|
|
196
|
+
const name = d.recvType.name;
|
|
197
|
+
if (!methods.has(name)) methods.set(name, []);
|
|
198
|
+
methods.get(name).push(d);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return methods;
|
|
202
|
+
}
|
|
273
203
|
|
|
274
|
-
|
|
204
|
+
_collectNamedWrappers(program, methods) {
|
|
205
|
+
for (const d of program.decls) {
|
|
206
|
+
if (
|
|
207
|
+
d.kind === "TypeDecl" &&
|
|
208
|
+
d.type.kind !== "StructType" &&
|
|
209
|
+
d.type.kind !== "InterfaceType" &&
|
|
210
|
+
(methods.get(d.name) ?? []).length > 0
|
|
211
|
+
) {
|
|
212
|
+
this.namedWrapperNames.add(d.name);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
275
216
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
217
|
+
_collectDecls(program) {
|
|
218
|
+
this._collectStructNames(program);
|
|
219
|
+
const methods = this._collectMethodMap(program);
|
|
220
|
+
this._collectNamedWrappers(program, methods);
|
|
221
|
+
return methods;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
_prependHelpers() {
|
|
225
|
+
const HELPER_MAP = [
|
|
226
|
+
[this._usesLen, HELPER_LEN],
|
|
227
|
+
[this._usesAppend, HELPER_APPEND],
|
|
228
|
+
[this._usesSliceGuard, HELPER_S],
|
|
229
|
+
[this._usesEqual, HELPER_EQUAL],
|
|
230
|
+
[this._usesCmul, HELPER_CMUL],
|
|
231
|
+
[this._usesCdiv, HELPER_CDIV],
|
|
232
|
+
[this._usesSprintf, HELPER_SPRINTF],
|
|
233
|
+
[this._usesError, HELPER_ERROR],
|
|
234
|
+
[this._usesErrorIs, HELPER_ERROR_IS],
|
|
235
|
+
[this._usesPathClean, HELPER_PATH_CLEAN],
|
|
236
|
+
[this._usesTimeFmt, HELPER_TIME_FMT],
|
|
237
|
+
[this._usesTimeParse, HELPER_TIME_PARSE],
|
|
238
|
+
];
|
|
239
|
+
const helpers = HELPER_MAP.filter(([flag]) => flag).map(([, h]) => h);
|
|
240
|
+
if (helpers.length > 0) this.out.unshift(...helpers, "");
|
|
279
241
|
}
|
|
280
242
|
|
|
281
243
|
// Generate a single bundle from multiple programs (same-package multi-file).
|
|
@@ -341,68 +303,75 @@ export class CodeGen {
|
|
|
341
303
|
this.line("}");
|
|
342
304
|
}
|
|
343
305
|
|
|
344
|
-
|
|
306
|
+
_collectStructFields(name, structTypeAst) {
|
|
345
307
|
const fields = [];
|
|
346
308
|
if (this.checker) {
|
|
347
309
|
const resolved = this.checker.types.get(name)?.underlying;
|
|
348
310
|
if (resolved?.kind === "struct") {
|
|
349
|
-
for (const [fName, fType] of resolved.fields.entries())
|
|
311
|
+
for (const [fName, fType] of resolved.fields.entries())
|
|
350
312
|
fields.push({ name: fName, zero: this.zeroValueForType(fType) });
|
|
351
|
-
}
|
|
352
313
|
}
|
|
353
314
|
} else {
|
|
354
|
-
// Fallback (e.g. tests without typechecker)
|
|
355
315
|
for (const f of structTypeAst.fields) {
|
|
356
|
-
if (f.embedded) continue;
|
|
316
|
+
if (f.embedded) continue;
|
|
357
317
|
const zero = this.zeroValueForTypeNode(f.type);
|
|
358
318
|
for (const n of f.names) fields.push({ name: n, zero });
|
|
359
319
|
}
|
|
360
320
|
}
|
|
321
|
+
return fields;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
_genStructConstructor(fields) {
|
|
325
|
+
if (fields.length === 0) {
|
|
326
|
+
this.line("constructor() {}");
|
|
327
|
+
} else {
|
|
328
|
+
const params = fields.map((f) => `${f.name} = ${f.zero}`).join(", ");
|
|
329
|
+
this.line(`constructor({ ${params} } = {}) {`);
|
|
330
|
+
this.indented(() => {
|
|
331
|
+
for (const f of fields) this.line(`this.${f.name} = ${f.name};`);
|
|
332
|
+
});
|
|
333
|
+
this.line("}");
|
|
334
|
+
}
|
|
335
|
+
}
|
|
361
336
|
|
|
337
|
+
genStruct(name, structTypeAst, methodDecls) {
|
|
338
|
+
const fields = this._collectStructFields(name, structTypeAst);
|
|
362
339
|
this.line(`class ${name} {`);
|
|
363
340
|
this.indented(() => {
|
|
364
|
-
|
|
365
|
-
this.line("constructor() {}");
|
|
366
|
-
} else {
|
|
367
|
-
const params = fields.map((f) => `${f.name} = ${f.zero}`).join(", ");
|
|
368
|
-
this.line(`constructor({ ${params} } = {}) {`);
|
|
369
|
-
this.indented(() => {
|
|
370
|
-
for (const f of fields) this.line(`this.${f.name} = ${f.name};`);
|
|
371
|
-
});
|
|
372
|
-
this.line("}");
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// Methods
|
|
341
|
+
this._genStructConstructor(fields);
|
|
376
342
|
for (const m of methodDecls) {
|
|
377
343
|
this.blank();
|
|
378
344
|
this.genMethod(m);
|
|
379
345
|
}
|
|
380
|
-
|
|
381
|
-
// Delegation stubs for promoted embedded methods
|
|
382
|
-
if (this.checker) {
|
|
383
|
-
const resolvedType = this.checker.types.get(name)?.underlying;
|
|
384
|
-
if (resolvedType?.kind === "struct" && resolvedType._embeds) {
|
|
385
|
-
const declared = new Set(methodDecls.map((m) => m.name));
|
|
386
|
-
for (const embed of resolvedType._embeds) {
|
|
387
|
-
const embedName = embed.kind === "named" ? embed.name : null;
|
|
388
|
-
if (!embedName) continue;
|
|
389
|
-
const embedBase = embed.kind === "named" ? embed.underlying : embed;
|
|
390
|
-
if (embedBase?.kind !== "struct" || !embedBase.methods) continue;
|
|
391
|
-
for (const [mName] of embedBase.methods.entries()) {
|
|
392
|
-
if (!declared.has(mName)) {
|
|
393
|
-
this.blank();
|
|
394
|
-
this.line(
|
|
395
|
-
`${mName}(...__a) { return ${embedName}.prototype.${mName}.call(this, ...__a); }`,
|
|
396
|
-
);
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
}
|
|
346
|
+
this._genEmbeddedMethodStubs(name, methodDecls);
|
|
402
347
|
});
|
|
403
348
|
this.line("}");
|
|
404
349
|
}
|
|
405
350
|
|
|
351
|
+
_genSingleEmbedStubs(embed, declared) {
|
|
352
|
+
const embedName = embed.kind === "named" ? embed.name : null;
|
|
353
|
+
if (!embedName) return;
|
|
354
|
+
const embedBase = embed.kind === "named" ? embed.underlying : embed;
|
|
355
|
+
if (embedBase?.kind !== "struct" || !embedBase.methods) return;
|
|
356
|
+
for (const [mName] of embedBase.methods.entries()) {
|
|
357
|
+
if (!declared.has(mName)) {
|
|
358
|
+
this.blank();
|
|
359
|
+
this.line(
|
|
360
|
+
`${mName}(...__a) { return ${embedName}.prototype.${mName}.call(this, ...__a); }`,
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
_genEmbeddedMethodStubs(name, methodDecls) {
|
|
367
|
+
if (!this.checker) return;
|
|
368
|
+
const resolvedType = this.checker.types.get(name)?.underlying;
|
|
369
|
+
if (resolvedType?.kind !== "struct" || !resolvedType._embeds) return;
|
|
370
|
+
const declared = new Set(methodDecls.map((m) => m.name));
|
|
371
|
+
for (const embed of resolvedType._embeds)
|
|
372
|
+
this._genSingleEmbedStubs(embed, declared);
|
|
373
|
+
}
|
|
374
|
+
|
|
406
375
|
genMethod(decl, recvField = null) {
|
|
407
376
|
const params = decl.params.map((p) => p.name).join(", ");
|
|
408
377
|
const asyncPrefix = decl.async ? "async " : "";
|
|
@@ -603,3 +572,5 @@ export class CodeGen {
|
|
|
603
572
|
|
|
604
573
|
Object.assign(CodeGen.prototype, statementGenMethods);
|
|
605
574
|
Object.assign(CodeGen.prototype, expressionGenMethods);
|
|
575
|
+
Object.assign(CodeGen.prototype, stdlibGenMethods);
|
|
576
|
+
Object.assign(CodeGen.prototype, templGenMethods);
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// Runtime helpers emitted by the code generator when the corresponding feature is used.
|
|
2
|
+
// Each export is a single JS statement (var __x = __x || function(...){...};) so that
|
|
3
|
+
// multiple compiled files bundled together can safely repeat the declaration.
|
|
4
|
+
|
|
5
|
+
export const HELPER_LEN = `var __len = __len || function(a) {
|
|
6
|
+
if (a && typeof a === 'object' && !Array.isArray(a)) return Object.keys(a).length;
|
|
7
|
+
return a?.length ?? 0;
|
|
8
|
+
};`;
|
|
9
|
+
|
|
10
|
+
export const HELPER_APPEND =
|
|
11
|
+
"var __append = __append || function(a, ...b) { return a ? [...a, ...b] : b; };";
|
|
12
|
+
|
|
13
|
+
export const HELPER_S = "var __s = __s || function(a) { return a || []; };";
|
|
14
|
+
|
|
15
|
+
export const HELPER_EQUAL = `var __equal = __equal || function __equal(a, b) {
|
|
16
|
+
if (a === b) return true;
|
|
17
|
+
if (a === null || b === null) return false;
|
|
18
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
19
|
+
if (a.length !== b.length) return false;
|
|
20
|
+
for (let i = 0; i < a.length; i++) if (!__equal(a[i], b[i])) return false;
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
if (typeof a === "object" && typeof b === "object") {
|
|
24
|
+
const ka = Object.keys(a), kb = Object.keys(b);
|
|
25
|
+
if (ka.length !== kb.length) return false;
|
|
26
|
+
for (const k of ka) if (!__equal(a[k], b[k])) return false;
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
};`;
|
|
31
|
+
|
|
32
|
+
export const HELPER_CMUL = `var __cmul = __cmul || function(a, b) {
|
|
33
|
+
return { re: a.re * b.re - a.im * b.im, im: a.re * b.im + a.im * b.re };
|
|
34
|
+
};`;
|
|
35
|
+
|
|
36
|
+
export const HELPER_CDIV = `var __cdiv = __cdiv || function(a, b) {
|
|
37
|
+
const d = b.re * b.re + b.im * b.im;
|
|
38
|
+
return { re: (a.re * b.re + a.im * b.im) / d, im: (a.im * b.re - a.re * b.im) / d };
|
|
39
|
+
};`;
|
|
40
|
+
|
|
41
|
+
export const HELPER_SPRINTF = `var __sprintf = __sprintf || function(f, ...a) {
|
|
42
|
+
let i = 0;
|
|
43
|
+
return f.replace(/%([#+\\- 0]*)([0-9]*)\\.?([0-9]*)[sdvftxXqobeEgGw%]/g, (m) => {
|
|
44
|
+
if (m === "%%") return "%";
|
|
45
|
+
const verb = m.slice(-1);
|
|
46
|
+
const v = a[i++];
|
|
47
|
+
const [, flags, width, prec] = m.match(/^%([#+\\- 0]*)([0-9]*)\\.?([0-9]*)/) || [];
|
|
48
|
+
const zero = flags?.includes("0") && !flags?.includes("-");
|
|
49
|
+
const pad = (s, w, z) => {
|
|
50
|
+
w = parseInt(w) || 0;
|
|
51
|
+
if (!w) return s;
|
|
52
|
+
const p = (z ? "0" : " ").repeat(Math.max(0, w - s.length));
|
|
53
|
+
return flags.includes("-") ? s + p : p + s;
|
|
54
|
+
};
|
|
55
|
+
switch (verb) {
|
|
56
|
+
case "s": return pad(String(v == null ? "<nil>" : v), width, false);
|
|
57
|
+
case "d": return pad(String(Math.trunc(Number(v))), width, zero);
|
|
58
|
+
case "v": {
|
|
59
|
+
if (typeof v === "object" && v !== null && "re" in v && "im" in v) {
|
|
60
|
+
const sign = v.im >= 0 ? "+" : "";
|
|
61
|
+
return pad("(" + v.re + sign + v.im + "i)", width, false);
|
|
62
|
+
}
|
|
63
|
+
return pad(String(v == null ? "<nil>" : v), width, false);
|
|
64
|
+
}
|
|
65
|
+
case "f": { const n = Number(v), p = prec !== "" ? parseInt(prec) : 6; return pad(n.toFixed(p), width, zero); }
|
|
66
|
+
case "t": return pad(String(!!v), width, false);
|
|
67
|
+
case "x": return pad((Number(v) >>> 0).toString(16), width, zero);
|
|
68
|
+
case "X": return pad((Number(v) >>> 0).toString(16).toUpperCase(), width, zero);
|
|
69
|
+
case "o": return pad((Number(v) >>> 0).toString(8), width, zero);
|
|
70
|
+
case "b": return pad((Number(v) >>> 0).toString(2), width, zero);
|
|
71
|
+
case "q": return pad('"' + String(v == null ? "" : v).replace(/\\\\/g, "\\\\\\\\").replace(/"/g, '\\\\"') + '"', width, false);
|
|
72
|
+
case "e": case "E": { const n = Number(v), p = prec !== "" ? parseInt(prec) : 6; return pad(n.toExponential(p), width, zero); }
|
|
73
|
+
case "g": case "G": { const n = Number(v); return pad(prec !== "" ? n.toPrecision(parseInt(prec)) : String(n), width, zero); }
|
|
74
|
+
case "w": return pad(String(v == null ? "<nil>" : typeof v === "object" && v.Error ? v.Error() : v), width, false);
|
|
75
|
+
default: return m;
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
};`;
|
|
79
|
+
|
|
80
|
+
export const HELPER_ERROR = `var __error = __error || function(msg, cause) {
|
|
81
|
+
return { Error() { return msg; }, toString() { return msg; }, _msg: msg, _cause: cause ?? null };
|
|
82
|
+
};`;
|
|
83
|
+
|
|
84
|
+
export const HELPER_ERROR_IS = `var __errorIs = __errorIs || function(err, target) {
|
|
85
|
+
while (err !== null && err !== undefined) {
|
|
86
|
+
if (err === target) return true;
|
|
87
|
+
if (typeof err === "object" && typeof target === "object" &&
|
|
88
|
+
err._msg !== undefined && target._msg !== undefined &&
|
|
89
|
+
err._msg === target._msg) return true;
|
|
90
|
+
err = err?._cause ?? null;
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
};`;
|
|
94
|
+
|
|
95
|
+
export const HELPER_PATH_CLEAN = `var __pathClean = __pathClean || function(p) {
|
|
96
|
+
if (!p) return ".";
|
|
97
|
+
const abs = p.startsWith("/");
|
|
98
|
+
const parts = p.split("/").reduce((acc, s) => {
|
|
99
|
+
if (s === "" || s === ".") return acc;
|
|
100
|
+
if (s === "..") {
|
|
101
|
+
if (acc.length && acc[acc.length - 1] !== "..") acc.pop();
|
|
102
|
+
else if (!abs) acc.push("..");
|
|
103
|
+
} else acc.push(s);
|
|
104
|
+
return acc;
|
|
105
|
+
}, []);
|
|
106
|
+
return (abs ? "/" : "") + (parts.join("/") || ".");
|
|
107
|
+
};`;
|
|
108
|
+
|
|
109
|
+
export const HELPER_TIME_FMT = `var __timeFmt = __timeFmt || function(d, layout) {
|
|
110
|
+
const pad = (n, w = 2) => String(n).padStart(w, "0");
|
|
111
|
+
const DAYS = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
|
|
112
|
+
const MONTHS = ["January","February","March","April","May","June","July","August","September","October","November","December"];
|
|
113
|
+
return layout.replace(/2006|06|Monday|Mon|January|Jan|01|1|02|2|15|PM|3|04|4|05|5|Z07:00|\\.000|\\.999/g, t => {
|
|
114
|
+
switch (t) {
|
|
115
|
+
case "2006": return d.getFullYear();
|
|
116
|
+
case "06": return String(d.getFullYear()).slice(-2);
|
|
117
|
+
case "Monday": return DAYS[d.getDay()];
|
|
118
|
+
case "Mon": return DAYS[d.getDay()].slice(0, 3);
|
|
119
|
+
case "January": return MONTHS[d.getMonth()];
|
|
120
|
+
case "Jan": return MONTHS[d.getMonth()].slice(0, 3);
|
|
121
|
+
case "01": return pad(d.getMonth() + 1);
|
|
122
|
+
case "1": return String(d.getMonth() + 1);
|
|
123
|
+
case "02": return pad(d.getDate());
|
|
124
|
+
case "2": return String(d.getDate());
|
|
125
|
+
case "15": return pad(d.getHours());
|
|
126
|
+
case "PM": return d.getHours() < 12 ? "AM" : "PM";
|
|
127
|
+
case "3": return String(d.getHours() % 12 || 12);
|
|
128
|
+
case "04": return pad(d.getMinutes());
|
|
129
|
+
case "4": return String(d.getMinutes());
|
|
130
|
+
case "05": return pad(d.getSeconds());
|
|
131
|
+
case "5": return String(d.getSeconds());
|
|
132
|
+
case "Z07:00": {
|
|
133
|
+
const o = -d.getTimezoneOffset();
|
|
134
|
+
return o === 0 ? "Z" : (o > 0 ? "+" : "-") + pad(Math.floor(Math.abs(o) / 60)) + ":" + pad(Math.abs(o) % 60);
|
|
135
|
+
}
|
|
136
|
+
case ".000": return "." + pad(d.getMilliseconds(), 3);
|
|
137
|
+
case ".999": return d.getMilliseconds() > 0 ? "." + String(d.getMilliseconds()).replace(/0+$/, "") : "";
|
|
138
|
+
default: return t;
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
};`;
|
|
142
|
+
|
|
143
|
+
export const HELPER_TIME_PARSE = `var __timeParse = __timeParse || function(layout, value) {
|
|
144
|
+
try {
|
|
145
|
+
const d = new Date(value);
|
|
146
|
+
if (isNaN(d.getTime())) throw 0;
|
|
147
|
+
return [{ _d: d }, null];
|
|
148
|
+
} catch {
|
|
149
|
+
return [{ _d: new Date(0) }, "parsing time: cannot parse"];
|
|
150
|
+
}
|
|
151
|
+
};`;
|