gofront 0.0.6 → 0.0.8
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 +104 -33
- package/package.json +12 -5
- package/src/codegen/expressions.js +603 -9
- package/src/codegen/source-map.js +8 -5
- package/src/codegen/statements.js +30 -26
- package/src/codegen.js +119 -8
- package/src/compiler.js +2 -1
- package/src/dev-server.js +1 -0
- package/src/index.js +1 -1
- package/src/parser.js +0 -10
- package/src/resolver.js +29 -0
- package/src/typechecker/expressions.js +38 -9
- package/src/typechecker/stdlib.js +1067 -0
- package/src/typechecker/types.js +13 -33
- package/src/typechecker.js +25 -601
|
@@ -16,9 +16,10 @@ function vlqEncode(value) {
|
|
|
16
16
|
return result;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export function buildSourceMap(sources, mappings) {
|
|
20
|
-
// sources:
|
|
21
|
-
// mappings:
|
|
19
|
+
export function buildSourceMap(sources, mappings, sourcesContent) {
|
|
20
|
+
// sources: string[]
|
|
21
|
+
// mappings: Array<{ genLine: number, srcLine: number, srcFileIdx?: number }> (0-based)
|
|
22
|
+
// sourcesContent: string[] | undefined — original file contents, embedded for DevTools
|
|
22
23
|
// Emits one segment per generated line, column 0 → source line (delta-encoded).
|
|
23
24
|
const lines = [];
|
|
24
25
|
let prevSrcLine = 0;
|
|
@@ -44,10 +45,12 @@ export function buildSourceMap(sources, mappings) {
|
|
|
44
45
|
lines.push(""); // no mapping for this generated line
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
|
-
|
|
48
|
+
const map = {
|
|
48
49
|
version: 3,
|
|
49
50
|
sources,
|
|
50
51
|
names: [],
|
|
51
52
|
mappings: lines.join(";"),
|
|
52
|
-
}
|
|
53
|
+
};
|
|
54
|
+
if (sourcesContent) map.sourcesContent = sourcesContent;
|
|
55
|
+
return JSON.stringify(map);
|
|
53
56
|
}
|
|
@@ -7,6 +7,30 @@ export const statementGenMethods = {
|
|
|
7
7
|
for (const stmt of block.stmts) this.genStmt(stmt);
|
|
8
8
|
},
|
|
9
9
|
|
|
10
|
+
// Shared helper for comma-ok map index in both DefineStmt and AssignStmt.
|
|
11
|
+
// isDefine=true → emit `let v = m[k]; let ok = k in m;`
|
|
12
|
+
// isDefine=false → emit `v = k in m ? m[k] : zero; ok = k in m;`
|
|
13
|
+
_genCommaOkMapIndex(stmt, rhsNode, isDefine) {
|
|
14
|
+
const mapExpr = this.genExpr(rhsNode.expr);
|
|
15
|
+
const keyExpr = this.genExpr(rhsNode.index);
|
|
16
|
+
const [vName, okName] = stmt.lhs.map((e) => e.name ?? this.genExpr(e));
|
|
17
|
+
const decl = isDefine ? "let " : "";
|
|
18
|
+
if (vName !== "_") {
|
|
19
|
+
if (isDefine) {
|
|
20
|
+
this.line(`let ${vName} = ${mapExpr}[${keyExpr}];`);
|
|
21
|
+
} else {
|
|
22
|
+
const zero = rhsNode._mapValueType
|
|
23
|
+
? this.zeroValueForType(rhsNode._mapValueType)
|
|
24
|
+
: "undefined";
|
|
25
|
+
this.line(
|
|
26
|
+
`${vName} = (${keyExpr}) in ${mapExpr} ? ${mapExpr}[${keyExpr}] : ${zero};`,
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (okName !== "_")
|
|
31
|
+
this.line(`${decl}${okName} = (${keyExpr}) in ${mapExpr};`);
|
|
32
|
+
},
|
|
33
|
+
|
|
10
34
|
genStmt(stmt) {
|
|
11
35
|
// Record source mapping for the first line this statement produces
|
|
12
36
|
const srcLine = stmt._line ?? null;
|
|
@@ -31,15 +55,7 @@ export const statementGenMethods = {
|
|
|
31
55
|
rhsNode?.kind === "IndexExpr" &&
|
|
32
56
|
rhsNode.expr?._type?.kind === "map"
|
|
33
57
|
) {
|
|
34
|
-
|
|
35
|
-
const keyExpr = this.genExpr(rhsNode.index);
|
|
36
|
-
const [vName, okName] = stmt.lhs.map(
|
|
37
|
-
(e) => e.name ?? this.genExpr(e),
|
|
38
|
-
);
|
|
39
|
-
if (vName !== "_")
|
|
40
|
-
this.line(`let ${vName} = ${mapExpr}[${keyExpr}];`);
|
|
41
|
-
if (okName !== "_")
|
|
42
|
-
this.line(`let ${okName} = (${keyExpr}) in ${mapExpr};`);
|
|
58
|
+
this._genCommaOkMapIndex(stmt, rhsNode, true);
|
|
43
59
|
break;
|
|
44
60
|
}
|
|
45
61
|
const rhs = stmt.rhs.map((e) => this.genExpr(e));
|
|
@@ -117,22 +133,7 @@ export const statementGenMethods = {
|
|
|
117
133
|
}
|
|
118
134
|
// comma-ok map index: v, ok = m["key"]
|
|
119
135
|
if (stmt._commaOkMap) {
|
|
120
|
-
|
|
121
|
-
const mapExpr = this.genExpr(rhsNode.expr);
|
|
122
|
-
const keyExpr = this.genExpr(rhsNode.index);
|
|
123
|
-
const [vName, okName] = stmt.lhs.map(
|
|
124
|
-
(e) => e.name ?? this.genExpr(e),
|
|
125
|
-
);
|
|
126
|
-
if (vName !== "_") {
|
|
127
|
-
const zero = rhsNode._mapValueType
|
|
128
|
-
? this.zeroValueForType(rhsNode._mapValueType)
|
|
129
|
-
: "undefined";
|
|
130
|
-
this.line(
|
|
131
|
-
`${vName} = (${keyExpr}) in ${mapExpr} ? ${mapExpr}[${keyExpr}] : ${zero};`,
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
if (okName !== "_")
|
|
135
|
-
this.line(`${okName} = (${keyExpr}) in ${mapExpr};`);
|
|
136
|
+
this._genCommaOkMapIndex(stmt, stmt.rhs[0], false);
|
|
136
137
|
break;
|
|
137
138
|
}
|
|
138
139
|
// comma-ok type assertion: v, ok = x.(T)
|
|
@@ -383,8 +384,11 @@ export const statementGenMethods = {
|
|
|
383
384
|
const init = stmt.init;
|
|
384
385
|
const range = init.rhs[0];
|
|
385
386
|
const lhs = init.lhs.map((e) => e.name ?? this.genExpr(e));
|
|
386
|
-
const iteree = this.genExpr(range.expr);
|
|
387
387
|
const iterType = range.expr._type;
|
|
388
|
+
const wrapField = this._namedWrapperField(iterType, range.expr);
|
|
389
|
+
const iteree = wrapField
|
|
390
|
+
? `${this.genExpr(range.expr)}.${wrapField}`
|
|
391
|
+
: this.genExpr(range.expr);
|
|
388
392
|
|
|
389
393
|
let iterExpr;
|
|
390
394
|
if (
|
package/src/codegen.js
CHANGED
|
@@ -35,6 +35,7 @@ export class CodeGen {
|
|
|
35
35
|
this.out = [];
|
|
36
36
|
this.indent = 0;
|
|
37
37
|
this.structNames = new Set();
|
|
38
|
+
this.namedWrapperNames = new Set();
|
|
38
39
|
this.jsImports = jsImports;
|
|
39
40
|
this.bundledPackages = bundledPackages;
|
|
40
41
|
this.namedReturnVars = null; // names of current function's named return vars
|
|
@@ -51,6 +52,9 @@ export class CodeGen {
|
|
|
51
52
|
this._usesCdiv = false;
|
|
52
53
|
this._usesError = false;
|
|
53
54
|
this._usesErrorIs = false;
|
|
55
|
+
this._usesPathClean = false;
|
|
56
|
+
this._usesTimeFmt = false;
|
|
57
|
+
this._usesTimeParse = false;
|
|
54
58
|
// Iterator (range-over-func) context
|
|
55
59
|
this._inIteratorBody = false;
|
|
56
60
|
this._iterDepth = 0;
|
|
@@ -92,7 +96,7 @@ export class CodeGen {
|
|
|
92
96
|
}
|
|
93
97
|
}
|
|
94
98
|
|
|
95
|
-
// Build a map:
|
|
99
|
+
// Build a map: typeName → [MethodDecl, ...]
|
|
96
100
|
const methods = new Map();
|
|
97
101
|
for (const d of program.decls) {
|
|
98
102
|
if (d.kind === "MethodDecl") {
|
|
@@ -102,6 +106,18 @@ export class CodeGen {
|
|
|
102
106
|
}
|
|
103
107
|
}
|
|
104
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
|
+
|
|
105
121
|
// Emit ESM import statements for npm packages
|
|
106
122
|
for (const [importPath, names] of this.jsImports) {
|
|
107
123
|
if (names.length === 0) continue;
|
|
@@ -213,6 +229,47 @@ export class CodeGen {
|
|
|
213
229
|
helpers.push(
|
|
214
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; };',
|
|
215
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
|
+
);
|
|
216
273
|
|
|
217
274
|
if (helpers.length > 0) this.out.unshift(...helpers, "");
|
|
218
275
|
|
|
@@ -235,9 +292,10 @@ export class CodeGen {
|
|
|
235
292
|
|
|
236
293
|
// Returns a source map JSON string for the last generate() call.
|
|
237
294
|
// sources: string[] of source filenames (relative to the output file).
|
|
238
|
-
|
|
295
|
+
// sourcesContent: string[] of original file contents (embedded for DevTools breakpoints).
|
|
296
|
+
getSourceMap(sources, sourcesContent) {
|
|
239
297
|
const srcArray = Array.isArray(sources) ? sources : [sources];
|
|
240
|
-
return buildSourceMap(srcArray, this._srcMappings);
|
|
298
|
+
return buildSourceMap(srcArray, this._srcMappings, sourcesContent);
|
|
241
299
|
}
|
|
242
300
|
|
|
243
301
|
// ── Type declarations ────────────────────────────────────────
|
|
@@ -248,13 +306,41 @@ export class CodeGen {
|
|
|
248
306
|
} else if (decl.type.kind === "InterfaceType") {
|
|
249
307
|
// Interfaces are compile-time only — no JS output needed.
|
|
250
308
|
this.line(`// interface ${decl.name} (compile-time only)`);
|
|
309
|
+
} else if (methodDecls.length > 0) {
|
|
310
|
+
// Named non-struct type with methods — emit an ES6 wrapper class.
|
|
311
|
+
this.genNamedTypeClass(decl.name, methodDecls);
|
|
251
312
|
} else {
|
|
252
|
-
// Type alias — no JS equivalent needed unless it's used as a constructor.
|
|
253
|
-
// e.g. type MyInt int → just a comment
|
|
254
313
|
this.line(`// type ${decl.name} = ${this.typeComment(decl.type)}`);
|
|
255
314
|
}
|
|
256
315
|
}
|
|
257
316
|
|
|
317
|
+
genNamedTypeClass(name, methodDecls) {
|
|
318
|
+
const namedType = this.checker?.types.get(name);
|
|
319
|
+
const underlying = namedType?.underlying;
|
|
320
|
+
let field, ctorDefault;
|
|
321
|
+
if (underlying?.kind === "func") {
|
|
322
|
+
field = "_fn";
|
|
323
|
+
ctorDefault = "null";
|
|
324
|
+
} else if (underlying?.kind === "map") {
|
|
325
|
+
field = "_map";
|
|
326
|
+
ctorDefault = "{}";
|
|
327
|
+
} else {
|
|
328
|
+
field = "_items";
|
|
329
|
+
ctorDefault = "[]";
|
|
330
|
+
}
|
|
331
|
+
this.line(`class ${name} {`);
|
|
332
|
+
this.indented(() => {
|
|
333
|
+
this.line(
|
|
334
|
+
`constructor(${field} = ${ctorDefault}) { this.${field} = ${field}; }`,
|
|
335
|
+
);
|
|
336
|
+
for (const m of methodDecls) {
|
|
337
|
+
this.blank();
|
|
338
|
+
this.genMethod(m, field);
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
this.line("}");
|
|
342
|
+
}
|
|
343
|
+
|
|
258
344
|
genStruct(name, structTypeAst, methodDecls) {
|
|
259
345
|
const fields = [];
|
|
260
346
|
if (this.checker) {
|
|
@@ -317,23 +403,45 @@ export class CodeGen {
|
|
|
317
403
|
this.line("}");
|
|
318
404
|
}
|
|
319
405
|
|
|
320
|
-
genMethod(decl) {
|
|
406
|
+
genMethod(decl, recvField = null) {
|
|
321
407
|
const params = decl.params.map((p) => p.name).join(", ");
|
|
322
408
|
const asyncPrefix = decl.async ? "async " : "";
|
|
323
409
|
this.line(`${asyncPrefix}${decl.name}(${params}) {`);
|
|
324
410
|
const prevBoxed = this._boxedVars;
|
|
325
411
|
this._boxedVars = new Set();
|
|
326
412
|
this._scanAddressTaken(decl.body);
|
|
413
|
+
const prevUnwrapped = this._unwrappedRecv;
|
|
327
414
|
this.indented(() => {
|
|
328
415
|
if (decl.recvName && decl.recvName !== "_") {
|
|
329
|
-
|
|
416
|
+
if (recvField) {
|
|
417
|
+
this.line(`const ${decl.recvName} = this.${recvField};`);
|
|
418
|
+
this._unwrappedRecv = decl.recvName;
|
|
419
|
+
} else {
|
|
420
|
+
this.line(`const ${decl.recvName} = this;`);
|
|
421
|
+
}
|
|
330
422
|
}
|
|
331
423
|
this._withNamedReturns(decl, () => this._genBody(decl.body));
|
|
332
424
|
});
|
|
425
|
+
this._unwrappedRecv = prevUnwrapped;
|
|
333
426
|
this._boxedVars = prevBoxed;
|
|
334
427
|
this.line("}");
|
|
335
428
|
}
|
|
336
429
|
|
|
430
|
+
// Returns the wrapper field name ("_fn", "_items", "_map") if `type` is a named
|
|
431
|
+
// non-struct type emitted as a wrapper class, or null otherwise.
|
|
432
|
+
// Pass `expr` so we can skip unwrapping when the expression is the method receiver
|
|
433
|
+
// (which was already unwrapped to `this.<field>` at the top of the method body).
|
|
434
|
+
_namedWrapperField(type, expr = null) {
|
|
435
|
+
if (type?.kind !== "named") return null;
|
|
436
|
+
if (!this.namedWrapperNames.has(type.name)) return null;
|
|
437
|
+
if (expr?.kind === "Ident" && expr.name === this._unwrappedRecv)
|
|
438
|
+
return null;
|
|
439
|
+
const u = type.underlying;
|
|
440
|
+
if (u?.kind === "func") return "_fn";
|
|
441
|
+
if (u?.kind === "map") return "_map";
|
|
442
|
+
return "_items";
|
|
443
|
+
}
|
|
444
|
+
|
|
337
445
|
// ── Function declarations ────────────────────────────────────
|
|
338
446
|
|
|
339
447
|
genFuncDecl(decl, nameOverride) {
|
|
@@ -471,7 +579,10 @@ export class CodeGen {
|
|
|
471
579
|
}
|
|
472
580
|
} else {
|
|
473
581
|
const zero = spec.type ? this.zeroValueForTypeNode(spec.type) : "null";
|
|
474
|
-
for (const name of spec.names)
|
|
582
|
+
for (const name of spec.names) {
|
|
583
|
+
const val = this._boxedVars.has(name) ? `{ value: ${zero} }` : zero;
|
|
584
|
+
this.line(`let ${name} = ${val};`);
|
|
585
|
+
}
|
|
475
586
|
}
|
|
476
587
|
}
|
|
477
588
|
}
|
package/src/compiler.js
CHANGED
|
@@ -170,7 +170,8 @@ export function compileFiles(files, options = {}) {
|
|
|
170
170
|
if (options.sourceMap) {
|
|
171
171
|
const outputDir = options.outputDir ?? fromDir;
|
|
172
172
|
const sourceFiles = files.map((f) => relative(outputDir, f));
|
|
173
|
-
const
|
|
173
|
+
const sourcesContent = files.map((f) => readFileSync(f, "utf8"));
|
|
174
|
+
const map = codegen.getSourceMap(sourceFiles, sourcesContent);
|
|
174
175
|
const b64 = Buffer.from(map).toString("base64");
|
|
175
176
|
js += `\n//# sourceMappingURL=data:application/json;base64,${b64}`;
|
|
176
177
|
}
|
package/src/dev-server.js
CHANGED
|
@@ -10,6 +10,7 @@ const MIME = {
|
|
|
10
10
|
".js": "application/javascript; charset=utf-8",
|
|
11
11
|
".css": "text/css; charset=utf-8",
|
|
12
12
|
".json": "application/json; charset=utf-8",
|
|
13
|
+
".go": "text/plain; charset=utf-8",
|
|
13
14
|
".svg": "image/svg+xml",
|
|
14
15
|
".png": "image/png",
|
|
15
16
|
".jpg": "image/jpeg",
|
package/src/index.js
CHANGED
|
@@ -232,7 +232,7 @@ function runCompile() {
|
|
|
232
232
|
if (sourceMap) {
|
|
233
233
|
const outputDir = outputFile ? dirname(resolve(outputFile)) : resolve(".");
|
|
234
234
|
const srcPath = relative(outputDir, inputPath);
|
|
235
|
-
const map = cg.getSourceMap(srcPath);
|
|
235
|
+
const map = cg.getSourceMap(srcPath, [source]);
|
|
236
236
|
const b64 = Buffer.from(map).toString("base64");
|
|
237
237
|
js += `\n//# sourceMappingURL=data:application/json;base64,${b64}`;
|
|
238
238
|
}
|
package/src/parser.js
CHANGED
|
@@ -308,16 +308,6 @@ export class Parser {
|
|
|
308
308
|
return this.looksLikeType(next) || next.type === T.ELLIPSIS;
|
|
309
309
|
}
|
|
310
310
|
|
|
311
|
-
// Heuristic: peek2 is a param-name separator if it's a comma, rparen, or a type token
|
|
312
|
-
isParamType(tok) {
|
|
313
|
-
if (!tok) return false;
|
|
314
|
-
// T.RPAREN is intentionally excluded: when peek2 is ")", the current IDENT
|
|
315
|
-
// is the type of an unnamed param (e.g. func(int)), not a param name.
|
|
316
|
-
return (
|
|
317
|
-
tok.type === T.COMMA || tok.type === T.ELLIPSIS || this.looksLikeType(tok)
|
|
318
|
-
);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
311
|
looksLikeType(tok) {
|
|
322
312
|
return (
|
|
323
313
|
tok.type === T.LBRACKET ||
|
package/src/resolver.js
CHANGED
|
@@ -25,11 +25,39 @@ function findNodeModules(startDir) {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
// Built-in stdlib package paths that are always available as globals — no resolution needed.
|
|
29
|
+
const BUILTIN_PACKAGES = new Set([
|
|
30
|
+
"fmt",
|
|
31
|
+
"strings",
|
|
32
|
+
"bytes",
|
|
33
|
+
"strconv",
|
|
34
|
+
"sort",
|
|
35
|
+
"math",
|
|
36
|
+
"math/rand",
|
|
37
|
+
"errors",
|
|
38
|
+
"time",
|
|
39
|
+
"unicode",
|
|
40
|
+
"unicode/utf8",
|
|
41
|
+
"os",
|
|
42
|
+
"slices",
|
|
43
|
+
"html",
|
|
44
|
+
"io",
|
|
45
|
+
"gom",
|
|
46
|
+
"maps",
|
|
47
|
+
"regexp",
|
|
48
|
+
"path",
|
|
49
|
+
"path/filepath",
|
|
50
|
+
]);
|
|
51
|
+
|
|
28
52
|
// Returns true for local relative paths (start with ./ or ../)
|
|
29
53
|
export function isLocalPath(importPath) {
|
|
30
54
|
return importPath.startsWith("./") || importPath.startsWith("../");
|
|
31
55
|
}
|
|
32
56
|
|
|
57
|
+
export function isBuiltinPackage(importPath) {
|
|
58
|
+
return BUILTIN_PACKAGES.has(importPath);
|
|
59
|
+
}
|
|
60
|
+
|
|
33
61
|
// Resolve a local relative import path to an absolute directory of .go files.
|
|
34
62
|
// Returns the absolute dir path, or null if it doesn't contain any .go files.
|
|
35
63
|
export function resolveGwDir(importPath, fromFile) {
|
|
@@ -85,6 +113,7 @@ export function resolveAll(imports, fromFile, parseDts) {
|
|
|
85
113
|
for (const { path } of imp.imports) {
|
|
86
114
|
if (path.startsWith("js:")) continue; // handled separately
|
|
87
115
|
if (isLocalPath(path)) continue; // local .go packages — handled by compiler
|
|
116
|
+
if (isBuiltinPackage(path)) continue; // built-in stdlib namespaces — always available
|
|
88
117
|
|
|
89
118
|
if (resolved.has(path)) continue;
|
|
90
119
|
|
|
@@ -111,8 +111,16 @@ export const expressionCheckMethods = {
|
|
|
111
111
|
if (expr.expr._isTypeRef) {
|
|
112
112
|
let base = baseType.kind === "named" ? baseType.underlying : baseType;
|
|
113
113
|
base = this.resolveType(base);
|
|
114
|
+
let methodType = null;
|
|
114
115
|
if (base?.kind === "struct" && base.methods?.has(expr.field)) {
|
|
115
|
-
|
|
116
|
+
methodType = base.methods.get(expr.field);
|
|
117
|
+
} else if (
|
|
118
|
+
baseType.kind === "named" &&
|
|
119
|
+
baseType.methods?.has(expr.field)
|
|
120
|
+
) {
|
|
121
|
+
methodType = baseType.methods.get(expr.field);
|
|
122
|
+
}
|
|
123
|
+
if (methodType) {
|
|
116
124
|
// Return a func type with the receiver prepended to params
|
|
117
125
|
expr._isMethodExpr = true;
|
|
118
126
|
expr._methodExprRecv = baseType;
|
|
@@ -129,7 +137,10 @@ export const expressionCheckMethods = {
|
|
|
129
137
|
if (ft?.kind === "func") {
|
|
130
138
|
let base = baseType.kind === "named" ? baseType.underlying : baseType;
|
|
131
139
|
base = this.resolveType(base);
|
|
132
|
-
if (
|
|
140
|
+
if (
|
|
141
|
+
(base?.kind === "struct" && base.methods?.has(expr.field)) ||
|
|
142
|
+
(baseType.kind === "named" && baseType.methods?.has(expr.field))
|
|
143
|
+
) {
|
|
133
144
|
expr._isMethodValue = true;
|
|
134
145
|
}
|
|
135
146
|
}
|
|
@@ -140,9 +151,14 @@ export const expressionCheckMethods = {
|
|
|
140
151
|
const bt = this.checkExpr(expr.expr, scope);
|
|
141
152
|
this.checkExpr(expr.index, scope);
|
|
142
153
|
if (isAny(bt)) return ANY;
|
|
143
|
-
|
|
154
|
+
// Named types (e.g. type Nums []int) are indexable via their underlying
|
|
155
|
+
const btu =
|
|
156
|
+
bt.kind === "named"
|
|
157
|
+
? (this.resolveType(bt.underlying) ?? bt.underlying)
|
|
158
|
+
: bt;
|
|
159
|
+
if (btu.kind === "slice" || btu.kind === "array") {
|
|
144
160
|
// Compile-time bounds checking for arrays
|
|
145
|
-
if (
|
|
161
|
+
if (btu.kind === "array" && btu.size != null) {
|
|
146
162
|
const idx = this._constIntValue(expr.index);
|
|
147
163
|
if (idx !== null) {
|
|
148
164
|
if (idx < 0) {
|
|
@@ -150,7 +166,7 @@ export const expressionCheckMethods = {
|
|
|
150
166
|
`invalid array index ${idx} (index must not be negative)`,
|
|
151
167
|
expr,
|
|
152
168
|
);
|
|
153
|
-
} else if (idx >=
|
|
169
|
+
} else if (idx >= btu.size) {
|
|
154
170
|
this.err(
|
|
155
171
|
`invalid array index ${idx} (out of bounds for ${typeStr(bt)})`,
|
|
156
172
|
expr,
|
|
@@ -158,11 +174,11 @@ export const expressionCheckMethods = {
|
|
|
158
174
|
}
|
|
159
175
|
}
|
|
160
176
|
}
|
|
161
|
-
return
|
|
177
|
+
return btu.elem;
|
|
162
178
|
}
|
|
163
|
-
if (
|
|
164
|
-
expr._mapValueType =
|
|
165
|
-
return
|
|
179
|
+
if (btu.kind === "map") {
|
|
180
|
+
expr._mapValueType = btu.value; // for codegen zero-value fallback
|
|
181
|
+
return btu.value;
|
|
166
182
|
}
|
|
167
183
|
if (isString(bt)) return INT; // byte
|
|
168
184
|
return this.err(`Cannot index type ${typeStr(bt)}`, expr);
|
|
@@ -410,6 +426,19 @@ export const expressionCheckMethods = {
|
|
|
410
426
|
|
|
411
427
|
if (isAny(fnType)) return ANY;
|
|
412
428
|
|
|
429
|
+
// Named type used as a constructor/conversion: Greeter(fn), NodeFunc(fn)
|
|
430
|
+
// The callee is a type name, not a callable variable.
|
|
431
|
+
if (fnType.kind === "named" && expr.func._isTypeRef) {
|
|
432
|
+
expr._isTypeConversion = true;
|
|
433
|
+
expr._conversionTargetType = fnType;
|
|
434
|
+
return fnType;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Named func types (e.g. type Handler func(int) int) are callable via variable
|
|
438
|
+
if (fnType.kind === "named" && fnType.underlying?.kind === "func") {
|
|
439
|
+
fnType = fnType.underlying;
|
|
440
|
+
}
|
|
441
|
+
|
|
413
442
|
// Generic function inference
|
|
414
443
|
if (fnType.kind === "generic") {
|
|
415
444
|
const typeArgs = this.inferTypeArgs(fnType, argTypes);
|