gofront 0.0.5 → 0.0.7

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.
@@ -101,13 +101,22 @@ export function isGeneric(t) {
101
101
  return t?.kind === "generic";
102
102
  }
103
103
 
104
- export function isNumeric(t) {
105
- if (!t) return false;
106
- if (t.kind === "untyped") return t.base === "int" || t.base === "float64";
107
- if (t.kind === "basic") return t.name === "int" || t.name === "float64";
108
- if (t.kind === "named") return isNumeric(t.underlying);
109
- return false;
104
+ function makeBasicPredicate(...names) {
105
+ const set = new Set(names);
106
+ const pred = (t) => {
107
+ if (!t) return false;
108
+ if (t.kind === "untyped") return set.has(t.base);
109
+ if (t.kind === "basic") return set.has(t.name);
110
+ if (t.kind === "named") return pred(t.underlying);
111
+ return false;
112
+ };
113
+ return pred;
110
114
  }
115
+
116
+ export const isNumeric = makeBasicPredicate("int", "float64");
117
+ export const isString = makeBasicPredicate("string");
118
+ export const isBool = makeBasicPredicate("bool");
119
+
111
120
  export function isComplex(t) {
112
121
  if (!t) return false;
113
122
  if (t.kind === "basic" && (t.name === "complex128" || t.name === "complex64"))
@@ -119,20 +128,6 @@ export function isComplex(t) {
119
128
  export function isComplexOrNumeric(t) {
120
129
  return isNumeric(t) || isComplex(t);
121
130
  }
122
- export function isString(t) {
123
- if (!t) return false;
124
- if (t.kind === "untyped") return t.base === "string";
125
- if (t.kind === "basic") return t.name === "string";
126
- if (t.kind === "named") return isString(t.underlying);
127
- return false;
128
- }
129
- export function isBool(t) {
130
- if (!t) return false;
131
- if (t.kind === "untyped") return t.base === "bool";
132
- if (t.kind === "basic") return t.name === "bool";
133
- if (t.kind === "named") return isBool(t.underlying);
134
- return false;
135
- }
136
131
  export function isAny(t) {
137
132
  return t?.kind === "basic" && t.name === "any";
138
133
  }
@@ -3,11 +3,13 @@
3
3
  //
4
4
  // Split into sub-modules under typechecker/:
5
5
  // types.js — shared type constants, predicates, Scope
6
+ // stdlib.js — browser globals + all built-in package registrations
6
7
  // statements.js — checkBlock, checkStmt
7
8
  // expressions.js — checkExpr, checkCall, checkBuiltin, checkCompositeLit
8
9
 
9
10
  import { expressionCheckMethods } from "./typechecker/expressions.js";
10
11
  import { statementCheckMethods } from "./typechecker/statements.js";
12
+ import { setupGlobals } from "./typechecker/stdlib.js";
11
13
  import {
12
14
  ANY,
13
15
  BASIC_TYPES,
@@ -59,396 +61,7 @@ export class TypeChecker {
59
61
  }
60
62
 
61
63
  _setupGlobals() {
62
- // Browser globals — typed as 'any' so any access/call is permitted
63
- const browserGlobals = [
64
- "console",
65
- "document",
66
- "window",
67
- "navigator",
68
- "location",
69
- "history",
70
- "screen",
71
- "performance",
72
- "crypto",
73
- "indexedDB",
74
- "fetch",
75
- "setTimeout",
76
- "setInterval",
77
- "clearTimeout",
78
- "clearInterval",
79
- "requestAnimationFrame",
80
- "cancelAnimationFrame",
81
- "Math",
82
- "JSON",
83
- "Date",
84
- "RegExp",
85
- "Promise",
86
- "Error",
87
- "Symbol",
88
- "String",
89
- "Number",
90
- "Boolean",
91
- "Array",
92
- "Object",
93
- "parseInt",
94
- "parseFloat",
95
- "isNaN",
96
- "isFinite",
97
- "encodeURIComponent",
98
- "decodeURIComponent",
99
- "atob",
100
- "btoa",
101
- "alert",
102
- "confirm",
103
- "prompt",
104
- "localStorage",
105
- "sessionStorage",
106
- "WebSocket",
107
- "Worker",
108
- "HTMLElement",
109
- "Element",
110
- "Event",
111
- "CustomEvent",
112
- "URL",
113
- "URLSearchParams",
114
- "FormData",
115
- "Headers",
116
- "Request",
117
- "Response",
118
- "Blob",
119
- "File",
120
- "FileReader",
121
- "ArrayBuffer",
122
- "Uint8Array",
123
- "TextEncoder",
124
- "TextDecoder",
125
- "WebGLRenderingContext",
126
- "WebGL2RenderingContext",
127
- "GPUDevice",
128
- "GPUAdapter",
129
- ];
130
- for (const g of browserGlobals) this.globals.define(g, ANY);
131
-
132
- // Built-in functions
133
- // fmt package — string formatting (variadic: format string + any args)
134
- const fmtVariadic = (ret) => ({
135
- kind: "func",
136
- params: [STRING, ANY],
137
- returns: [ret],
138
- variadic: true,
139
- });
140
- this.globals.define("fmt", {
141
- kind: "namespace",
142
- name: "fmt",
143
- members: {
144
- Sprintf: fmtVariadic(STRING),
145
- Errorf: fmtVariadic(ERROR),
146
- Printf: fmtVariadic(VOID),
147
- Println: fmtVariadic(VOID),
148
- Print: fmtVariadic(VOID),
149
- },
150
- });
151
-
152
- // strings package
153
- const strFn1 = (ret) => ({
154
- kind: "func",
155
- params: [STRING],
156
- returns: [ret],
157
- });
158
- const strFn2 = (p2, ret) => ({
159
- kind: "func",
160
- params: [STRING, p2],
161
- returns: [ret],
162
- });
163
- const strFn3 = (p2, p3, ret) => ({
164
- kind: "func",
165
- params: [STRING, p2, p3],
166
- returns: [ret],
167
- });
168
- this.globals.define("strings", {
169
- kind: "namespace",
170
- name: "strings",
171
- members: {
172
- Contains: strFn2(STRING, BOOL),
173
- HasPrefix: strFn2(STRING, BOOL),
174
- HasSuffix: strFn2(STRING, BOOL),
175
- Index: strFn2(STRING, INT),
176
- LastIndex: strFn2(STRING, INT),
177
- Count: strFn2(STRING, INT),
178
- Repeat: strFn2(INT, STRING),
179
- Replace: {
180
- kind: "func",
181
- params: [STRING, STRING, STRING, INT],
182
- returns: [STRING],
183
- },
184
- ReplaceAll: strFn3(STRING, STRING, STRING),
185
- ToUpper: strFn1(STRING),
186
- ToLower: strFn1(STRING),
187
- TrimSpace: strFn1(STRING),
188
- Trim: strFn2(STRING, STRING),
189
- TrimPrefix: strFn2(STRING, STRING),
190
- TrimSuffix: strFn2(STRING, STRING),
191
- TrimLeft: strFn2(STRING, STRING),
192
- TrimRight: strFn2(STRING, STRING),
193
- Split: strFn2(STRING, { kind: "slice", elem: STRING }),
194
- Join: {
195
- kind: "func",
196
- params: [{ kind: "slice", elem: STRING }, STRING],
197
- returns: [STRING],
198
- },
199
- EqualFold: strFn2(STRING, BOOL),
200
- },
201
- });
202
-
203
- // bytes package — operates on []byte (JS arrays of numbers)
204
- const BYTE_SLICE = { kind: "slice", elem: INT };
205
- const byFn1 = (ret) => ({
206
- kind: "func",
207
- params: [BYTE_SLICE],
208
- returns: [ret],
209
- });
210
- const byFn2 = (p2, ret) => ({
211
- kind: "func",
212
- params: [BYTE_SLICE, p2],
213
- returns: [ret],
214
- });
215
- this.globals.define("bytes", {
216
- kind: "namespace",
217
- name: "bytes",
218
- members: {
219
- Contains: byFn2(BYTE_SLICE, BOOL),
220
- HasPrefix: byFn2(BYTE_SLICE, BOOL),
221
- HasSuffix: byFn2(BYTE_SLICE, BOOL),
222
- Index: byFn2(BYTE_SLICE, INT),
223
- Count: byFn2(BYTE_SLICE, INT),
224
- Repeat: byFn2(INT, BYTE_SLICE),
225
- Replace: {
226
- kind: "func",
227
- params: [BYTE_SLICE, BYTE_SLICE, BYTE_SLICE, INT],
228
- returns: [BYTE_SLICE],
229
- },
230
- ToUpper: byFn1(BYTE_SLICE),
231
- ToLower: byFn1(BYTE_SLICE),
232
- TrimSpace: byFn1(BYTE_SLICE),
233
- Trim: byFn2(STRING, BYTE_SLICE),
234
- Equal: byFn2(BYTE_SLICE, BOOL),
235
- Split: byFn2(BYTE_SLICE, { kind: "slice", elem: BYTE_SLICE }),
236
- Join: {
237
- kind: "func",
238
- params: [{ kind: "slice", elem: BYTE_SLICE }, BYTE_SLICE],
239
- returns: [BYTE_SLICE],
240
- },
241
- },
242
- });
243
-
244
- // strconv package
245
- this.globals.define("strconv", {
246
- kind: "namespace",
247
- name: "strconv",
248
- members: {
249
- Itoa: { kind: "func", params: [INT], returns: [STRING] },
250
- Atoi: {
251
- kind: "func",
252
- params: [STRING],
253
- returns: [INT, ERROR],
254
- },
255
- FormatFloat: {
256
- kind: "func",
257
- params: [FLOAT64, INT, INT, INT],
258
- returns: [STRING],
259
- },
260
- FormatBool: { kind: "func", params: [BOOL], returns: [STRING] },
261
- FormatInt: { kind: "func", params: [INT, INT], returns: [STRING] },
262
- ParseFloat: {
263
- kind: "func",
264
- params: [STRING, INT],
265
- returns: [FLOAT64, ERROR],
266
- },
267
- ParseInt: {
268
- kind: "func",
269
- params: [STRING, INT, INT],
270
- returns: [INT, ERROR],
271
- },
272
- ParseBool: {
273
- kind: "func",
274
- params: [STRING],
275
- returns: [BOOL, ERROR],
276
- },
277
- },
278
- });
279
-
280
- // sort package
281
- this.globals.define("sort", {
282
- kind: "namespace",
283
- name: "sort",
284
- members: {
285
- Ints: {
286
- kind: "func",
287
- params: [{ kind: "slice", elem: INT }],
288
- returns: [VOID],
289
- },
290
- Float64s: {
291
- kind: "func",
292
- params: [{ kind: "slice", elem: FLOAT64 }],
293
- returns: [VOID],
294
- },
295
- Strings: {
296
- kind: "func",
297
- params: [{ kind: "slice", elem: STRING }],
298
- returns: [VOID],
299
- },
300
- Slice: {
301
- kind: "func",
302
- params: [ANY, ANY],
303
- returns: [VOID],
304
- },
305
- SliceStable: {
306
- kind: "func",
307
- params: [ANY, ANY],
308
- returns: [VOID],
309
- },
310
- SliceIsSorted: {
311
- kind: "func",
312
- params: [ANY, ANY],
313
- returns: [BOOL],
314
- },
315
- },
316
- });
317
-
318
- // math package
319
- this.globals.define("math", {
320
- kind: "namespace",
321
- name: "math",
322
- members: {
323
- Abs: { kind: "func", params: [FLOAT64], returns: [FLOAT64] },
324
- Floor: { kind: "func", params: [FLOAT64], returns: [FLOAT64] },
325
- Ceil: { kind: "func", params: [FLOAT64], returns: [FLOAT64] },
326
- Round: { kind: "func", params: [FLOAT64], returns: [FLOAT64] },
327
- Sqrt: { kind: "func", params: [FLOAT64], returns: [FLOAT64] },
328
- Cbrt: { kind: "func", params: [FLOAT64], returns: [FLOAT64] },
329
- Pow: {
330
- kind: "func",
331
- params: [FLOAT64, FLOAT64],
332
- returns: [FLOAT64],
333
- },
334
- Log: { kind: "func", params: [FLOAT64], returns: [FLOAT64] },
335
- Log2: { kind: "func", params: [FLOAT64], returns: [FLOAT64] },
336
- Log10: { kind: "func", params: [FLOAT64], returns: [FLOAT64] },
337
- Sin: { kind: "func", params: [FLOAT64], returns: [FLOAT64] },
338
- Cos: { kind: "func", params: [FLOAT64], returns: [FLOAT64] },
339
- Tan: { kind: "func", params: [FLOAT64], returns: [FLOAT64] },
340
- Min: {
341
- kind: "func",
342
- params: [FLOAT64, FLOAT64],
343
- returns: [FLOAT64],
344
- },
345
- Max: {
346
- kind: "func",
347
- params: [FLOAT64, FLOAT64],
348
- returns: [FLOAT64],
349
- },
350
- Mod: {
351
- kind: "func",
352
- params: [FLOAT64, FLOAT64],
353
- returns: [FLOAT64],
354
- },
355
- Inf: { kind: "func", params: [INT], returns: [FLOAT64] },
356
- IsNaN: { kind: "func", params: [FLOAT64], returns: [BOOL] },
357
- IsInf: {
358
- kind: "func",
359
- params: [FLOAT64, INT],
360
- returns: [BOOL],
361
- },
362
- NaN: { kind: "func", params: [], returns: [FLOAT64] },
363
- // Constants (typed as func with no params for namespace member access)
364
- Pi: FLOAT64,
365
- E: FLOAT64,
366
- MaxFloat64: FLOAT64,
367
- SmallestNonzeroFloat64: FLOAT64,
368
- MaxInt: INT,
369
- MinInt: INT,
370
- },
371
- });
372
-
373
- // errors package
374
- this.globals.define("errors", {
375
- kind: "namespace",
376
- name: "errors",
377
- members: {
378
- New: { kind: "func", params: [STRING], returns: [ERROR] },
379
- Is: { kind: "func", params: [ERROR, ERROR], returns: [BOOL] },
380
- Unwrap: { kind: "func", params: [ERROR], returns: [ERROR] },
381
- },
382
- });
383
-
384
- // time package (partial — JS-friendly subset)
385
- this.globals.define("time", {
386
- kind: "namespace",
387
- name: "time",
388
- members: {
389
- Now: { kind: "func", params: [], returns: [ANY] },
390
- Since: { kind: "func", params: [ANY], returns: [ANY] },
391
- Sleep: { kind: "func", params: [ANY], returns: [VOID], async: true },
392
- Millisecond: INT,
393
- Second: INT,
394
- Minute: INT,
395
- Hour: INT,
396
- },
397
- });
398
-
399
- // unicode package
400
- const runeToStr = { kind: "func", params: [INT], returns: [BOOL] };
401
- const runeTrans = { kind: "func", params: [INT], returns: [INT] };
402
- this.globals.define("unicode", {
403
- kind: "namespace",
404
- name: "unicode",
405
- members: {
406
- IsLetter: runeToStr,
407
- IsDigit: runeToStr,
408
- IsSpace: runeToStr,
409
- IsUpper: runeToStr,
410
- IsLower: runeToStr,
411
- IsPunct: runeToStr,
412
- IsControl: runeToStr,
413
- IsPrint: runeToStr,
414
- IsGraphic: runeToStr,
415
- ToUpper: runeTrans,
416
- ToLower: runeTrans,
417
- },
418
- });
419
-
420
- // os package (JS-friendly subset)
421
- this.globals.define("os", {
422
- kind: "namespace",
423
- name: "os",
424
- members: {
425
- Exit: { kind: "func", params: [INT], returns: [VOID] },
426
- Args: { kind: "slice", elem: STRING },
427
- Getenv: { kind: "func", params: [STRING], returns: [STRING] },
428
- Stdout: ANY,
429
- Stderr: ANY,
430
- Stdin: ANY,
431
- },
432
- });
433
-
434
- this.globals.define("append", { kind: "builtin", name: "append" });
435
- this.globals.define("len", { kind: "builtin", name: "len" });
436
- this.globals.define("cap", { kind: "builtin", name: "cap" });
437
- this.globals.define("make", { kind: "builtin", name: "make" });
438
- this.globals.define("delete", { kind: "builtin", name: "delete" });
439
- this.globals.define("print", { kind: "builtin", name: "print" });
440
- this.globals.define("println", { kind: "builtin", name: "println" });
441
- this.globals.define("panic", { kind: "builtin", name: "panic" });
442
- this.globals.define("recover", { kind: "builtin", name: "recover" });
443
- this.globals.define("new", { kind: "builtin", name: "new" });
444
- this.globals.define("copy", { kind: "builtin", name: "copy" });
445
- this.globals.define("error", { kind: "builtin", name: "error" });
446
- this.globals.define("min", { kind: "builtin", name: "min" });
447
- this.globals.define("max", { kind: "builtin", name: "max" });
448
- this.globals.define("clear", { kind: "builtin", name: "clear" });
449
- this.globals.define("complex", { kind: "builtin", name: "complex" });
450
- this.globals.define("real", { kind: "builtin", name: "real" });
451
- this.globals.define("imag", { kind: "builtin", name: "imag" });
64
+ setupGlobals(this.globals, this.types);
452
65
  }
453
66
 
454
67
  addDefinitions(types, values) {
@@ -473,7 +86,10 @@ export class TypeChecker {
473
86
  members,
474
87
  _gofront: true,
475
88
  });
476
- for (const [name, type] of types) this.types.set(name, type);
89
+ for (const [name, type] of types) {
90
+ this.types.set(name, type);
91
+ this.types.set(`${pkgName}.${name}`, type);
92
+ }
477
93
  }
478
94
 
479
95
  // Register an import for unused-import detection.
@@ -677,6 +293,8 @@ export class TypeChecker {
677
293
  if (underlying.kind === "struct") {
678
294
  underlying.name = decl.name;
679
295
  underlying.methods = new Map();
296
+ } else if (underlying.kind !== "interface") {
297
+ named.methods = new Map();
680
298
  }
681
299
  if (underlying.kind === "interface") {
682
300
  underlying.name = decl.name;
@@ -745,7 +363,7 @@ export class TypeChecker {
745
363
  this.globals.define(decl.name, funcType);
746
364
  }
747
365
  } else {
748
- // Method: attach to struct type
366
+ // Method: attach to struct or named non-struct type
749
367
  const recvTypeName =
750
368
  decl.recvType.kind === "GenericTypeName"
751
369
  ? decl.recvType.name
@@ -765,6 +383,8 @@ export class TypeChecker {
765
383
  const base = recvType?.underlying ?? recvType;
766
384
  if (base?.kind === "struct") {
767
385
  base.methods.set(decl.name, funcType);
386
+ } else if (recvType?.kind === "named" && recvType.methods) {
387
+ recvType.methods.set(decl.name, funcType);
768
388
  }
769
389
  }
770
390
  }
@@ -1360,6 +980,19 @@ export class TypeChecker {
1360
980
  fieldType(baseType, field, node) {
1361
981
  baseType = this.resolveType(baseType);
1362
982
  if (!baseType || isAny(baseType)) return ANY;
983
+ // Transparent pointer dereference for named-type method calls: (*T).Method works like T.Method
984
+ // (only unwrap when the pointer's base is a named type that has methods)
985
+ if (
986
+ baseType.kind === "pointer" &&
987
+ field !== "value" &&
988
+ baseType.base?.kind === "named"
989
+ ) {
990
+ baseType = this.resolveType(baseType.base);
991
+ }
992
+ // Check methods on named type itself (for non-struct named types)
993
+ if (baseType.kind === "named" && baseType.methods?.has(field)) {
994
+ return baseType.methods.get(field);
995
+ }
1363
996
  let base = baseType.kind === "named" ? baseType.underlying : baseType;
1364
997
  base = this.resolveType(base);
1365
998
  if (base?.kind === "struct") {
@@ -1597,9 +1230,16 @@ export class TypeChecker {
1597
1230
  implements(srcType, iface, _node) {
1598
1231
  let base = srcType.kind === "named" ? srcType.underlying : srcType;
1599
1232
  base = this.resolveType(base);
1600
- if (base?.kind !== "struct") return false;
1233
+ // For non-struct named types, methods live on the named type itself
1234
+ const methodMap =
1235
+ base?.kind === "struct"
1236
+ ? base.methods
1237
+ : srcType.kind === "named"
1238
+ ? srcType.methods
1239
+ : null;
1240
+ if (!methodMap) return false;
1601
1241
  for (const [name, required] of iface.methods) {
1602
- const actual = base.methods?.get(name);
1242
+ const actual = methodMap.get(name);
1603
1243
  if (!actual) return false;
1604
1244
 
1605
1245
  // Check parameter count matches