gofront 0.0.9 → 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.
Files changed (50) hide show
  1. package/README.md +6 -6
  2. package/package.json +7 -4
  3. package/src/codegen/expressions.js +520 -1717
  4. package/src/codegen/index.js +576 -0
  5. package/src/codegen/runtime.js +151 -0
  6. package/src/codegen/statements.js +305 -296
  7. package/src/codegen/stdlib/builder.js +36 -0
  8. package/src/codegen/stdlib/bytes.js +62 -0
  9. package/src/codegen/stdlib/errors.js +17 -0
  10. package/src/codegen/stdlib/fmt.js +101 -0
  11. package/src/codegen/stdlib/gom.js +152 -0
  12. package/src/codegen/stdlib/html.js +17 -0
  13. package/src/codegen/stdlib/index.js +95 -0
  14. package/src/codegen/stdlib/io.js +33 -0
  15. package/src/codegen/stdlib/maps.js +42 -0
  16. package/src/codegen/stdlib/math.js +53 -0
  17. package/src/codegen/stdlib/os.js +10 -0
  18. package/src/codegen/stdlib/path.js +31 -0
  19. package/src/codegen/stdlib/rand.js +24 -0
  20. package/src/codegen/stdlib/regexp.js +51 -0
  21. package/src/codegen/stdlib/slices.js +48 -0
  22. package/src/codegen/stdlib/sort.js +28 -0
  23. package/src/codegen/stdlib/strconv.js +30 -0
  24. package/src/codegen/stdlib/strings.js +83 -0
  25. package/src/codegen/stdlib/time.js +54 -0
  26. package/src/codegen/stdlib/unicode.js +22 -0
  27. package/src/codegen/stdlib/utf8.js +23 -0
  28. package/src/codegen/templ.js +281 -0
  29. package/src/compiler.js +80 -74
  30. package/src/dts-parser.js +355 -371
  31. package/src/index.js +13 -49
  32. package/src/lexer.js +186 -272
  33. package/src/minifier.js +305 -318
  34. package/src/parser/expressions.js +51 -34
  35. package/src/{parser.js → parser/index.js} +4 -4
  36. package/src/parser/statements.js +24 -34
  37. package/src/parser/types.js +31 -29
  38. package/src/templ-parser.js +2 -2
  39. package/src/tokens.js +109 -0
  40. package/src/typechecker/assignability.js +258 -0
  41. package/src/typechecker/expressions.js +559 -549
  42. package/src/typechecker/index.js +555 -0
  43. package/src/typechecker/resolve.js +421 -0
  44. package/src/typechecker/statements.js +267 -280
  45. package/src/typechecker/stdlib/core.js +592 -0
  46. package/src/typechecker/stdlib/extended.js +332 -0
  47. package/src/typechecker/stdlib.js +9 -1060
  48. package/src/typechecker/termination.js +83 -0
  49. package/src/codegen.js +0 -882
  50. package/src/typechecker.js +0 -1299
@@ -0,0 +1,421 @@
1
+ // Type resolution methods — resolveTypeNode, resolveType, fieldType, generics.
2
+ // Installed as a mixin on TypeChecker.prototype.
3
+
4
+ import {
5
+ ANY,
6
+ BASIC_TYPES,
7
+ defaultType,
8
+ isAny,
9
+ Scope,
10
+ typeStr,
11
+ VOID,
12
+ } from "./types.js";
13
+
14
+ // Dispatch table for substituteType — keyed by type.kind.
15
+ const SUBSTITUTE_DISPATCH = {
16
+ basic: (_s, type) => type,
17
+ untyped: (_s, type) => type,
18
+ slice: (s, type, map) => ({
19
+ kind: "slice",
20
+ elem: s.substituteType(type.elem, map),
21
+ }),
22
+ array: (s, type, map) => ({
23
+ kind: "array",
24
+ size: type.size,
25
+ elem: s.substituteType(type.elem, map),
26
+ }),
27
+ map: (s, type, map) => ({
28
+ kind: "map",
29
+ key: s.substituteType(type.key, map),
30
+ value: s.substituteType(type.value, map),
31
+ }),
32
+ pointer: (s, type, map) => ({
33
+ kind: "pointer",
34
+ base: s.substituteType(type.base, map),
35
+ }),
36
+ tuple: (s, type, map) => ({
37
+ kind: "tuple",
38
+ types: type.types.map((t) => s.substituteType(t, map)),
39
+ }),
40
+ func: (s, type, map) => ({
41
+ kind: "func",
42
+ params: type.params.map((p) => s.substituteType(p, map)),
43
+ returns: type.returns.map((r) => s.substituteType(r, map)),
44
+ variadic: type.variadic,
45
+ async: type.async,
46
+ }),
47
+ struct: (s, type, map) => {
48
+ const fields = new Map();
49
+ const methods = new Map();
50
+ for (const [k, v] of type.fields) fields.set(k, s.substituteType(v, map));
51
+ for (const [k, v] of type.methods) methods.set(k, s.substituteType(v, map));
52
+ return {
53
+ kind: "struct",
54
+ name: type.name,
55
+ fields,
56
+ methods,
57
+ _embeds: type._embeds,
58
+ };
59
+ },
60
+ interface: (s, type, map) => {
61
+ const methods = new Map();
62
+ for (const [k, v] of type.methods) methods.set(k, s.substituteType(v, map));
63
+ return { kind: "interface", name: type.name, methods };
64
+ },
65
+ named: (s, type, map) => ({
66
+ kind: "named",
67
+ name: type.name,
68
+ underlying: s.substituteType(type.underlying, map),
69
+ }),
70
+ };
71
+
72
+ export const resolveMethods = {
73
+ resolveTypeNode(node, scope) {
74
+ if (!node) return ANY;
75
+ switch (node.kind) {
76
+ case "TypeName": {
77
+ if (BASIC_TYPES[node.name]) return BASIC_TYPES[node.name];
78
+ if (node.name === "comparable")
79
+ return { kind: "basic", name: "comparable" };
80
+ if (scope) {
81
+ const fromScope = scope.lookup(node.name);
82
+ if (fromScope?.kind === "typeParam") return fromScope;
83
+ }
84
+ const named = this.types.get(node.name);
85
+ if (named) return named;
86
+ return this.err(`Unknown type '${node.name}'`, node);
87
+ }
88
+ case "GenericTypeName": {
89
+ const base = this.types.get(node.name);
90
+ if (!base) return this.err(`Unknown type '${node.name}'`, node);
91
+ if (base.kind === "named" && base._generic)
92
+ return this.instantiateGenericType(
93
+ base._generic,
94
+ node.typeArgs,
95
+ scope,
96
+ );
97
+ return this.err(`Type '${node.name}' is not generic`, node);
98
+ }
99
+ case "TypeParam": {
100
+ const constraint = node.constraint
101
+ ? this.resolveTypeNode(node.constraint, scope)
102
+ : ANY;
103
+ return { kind: "typeParam", name: node.name, constraint };
104
+ }
105
+ case "UnionConstraint":
106
+ return node;
107
+ case "SliceType":
108
+ return { kind: "slice", elem: this.resolveTypeNode(node.elem, scope) };
109
+ case "MapType":
110
+ return {
111
+ kind: "map",
112
+ key: this.resolveTypeNode(node.key, scope),
113
+ value: this.resolveTypeNode(node.value, scope),
114
+ };
115
+ case "PointerType":
116
+ return {
117
+ kind: "pointer",
118
+ base: this.resolveTypeNode(node.base, scope),
119
+ };
120
+ case "StructType":
121
+ return this._resolveStructType(node, scope);
122
+ case "InterfaceType":
123
+ return this._resolveInterfaceType(node, scope);
124
+ case "Ident":
125
+ return this.resolveTypeNode(
126
+ { kind: "TypeName", name: node.name },
127
+ scope,
128
+ );
129
+ case "ArrayType":
130
+ return this._resolveArrayTypeNode(node, scope);
131
+ case "FuncType":
132
+ return this._resolveFuncTypeNode(node, scope);
133
+ case "TupleType":
134
+ return {
135
+ kind: "tuple",
136
+ types: node.types.map((t) => this.resolveTypeNode(t, scope)),
137
+ };
138
+ default:
139
+ return ANY;
140
+ }
141
+ },
142
+
143
+ _resolveArrayTypeNode(node, scope) {
144
+ return {
145
+ kind: "array",
146
+ size: node.inferLen
147
+ ? null
148
+ : node.size?.value !== undefined
149
+ ? Number(node.size.value)
150
+ : node.size,
151
+ elem: this.resolveTypeNode(node.elem, scope),
152
+ };
153
+ },
154
+
155
+ _resolveFuncTypeNode(node, scope) {
156
+ const params = node.params.map((p) => this.resolveTypeNode(p.type, scope));
157
+ const returns = node.returnType
158
+ ? [this.resolveTypeNode(node.returnType, scope)]
159
+ : [VOID];
160
+ return { kind: "func", params, returns };
161
+ },
162
+
163
+ resolveTypeNodeName(node, scope) {
164
+ if (!node) return ANY;
165
+ if (node.kind === "TypeName" || node.kind === "GenericTypeName") {
166
+ const named = this.types.get(node.name);
167
+ if (named) return named;
168
+ }
169
+ return this.resolveTypeNode(node, scope);
170
+ },
171
+
172
+ _resolveStructType(node, scope) {
173
+ const fields = new Map();
174
+ const embeds = [];
175
+ for (const f of node.fields) {
176
+ const ft = this.resolveTypeNode(f.type, scope);
177
+ if (f.embedded) {
178
+ const base = ft.kind === "named" ? ft.underlying : ft;
179
+ if (base?.kind === "struct") {
180
+ for (const [k, v] of base.fields.entries()) fields.set(k, v);
181
+ }
182
+ embeds.push(ft);
183
+ } else {
184
+ for (const n of f.names) fields.set(n, ft);
185
+ }
186
+ }
187
+ return { kind: "struct", fields, methods: new Map(), _embeds: embeds };
188
+ },
189
+
190
+ _resolveInterfaceType(node, scope) {
191
+ const methods = new Map();
192
+ for (const m of node.methods) {
193
+ const params = m.params.map((p) => this.resolveTypeNode(p.type, scope));
194
+ const returns = m.returnType
195
+ ? [this.resolveTypeNode(m.returnType, scope)]
196
+ : [VOID];
197
+ const isVariadic =
198
+ m.params.length > 0 && m.params[m.params.length - 1].variadic;
199
+ const mType = { kind: "func", params, returns };
200
+ if (isVariadic) mType.variadic = true;
201
+ methods.set(m.name, mType);
202
+ }
203
+ if (node.embeds) {
204
+ for (const embed of node.embeds) {
205
+ const resolved = this.resolveTypeNode(embed, scope);
206
+ const base =
207
+ resolved?.kind === "named" ? resolved.underlying : resolved;
208
+ if (base?.kind === "interface") {
209
+ for (const [mName, mType] of base.methods) {
210
+ if (!methods.has(mName)) methods.set(mName, mType);
211
+ }
212
+ } else if (!isAny(resolved)) {
213
+ this.err(
214
+ `cannot embed non-interface type ${typeStr(resolved)}`,
215
+ embed,
216
+ );
217
+ }
218
+ }
219
+ }
220
+ return { kind: "interface", methods };
221
+ },
222
+
223
+ // ── Generics — substitution, instantiation, inference ────────────
224
+
225
+ substituteType(type, map) {
226
+ if (!type) return type;
227
+ if (type.kind === "typeParam") return map.get(type.name) ?? type;
228
+ const gen = SUBSTITUTE_DISPATCH[type.kind];
229
+ return gen ? gen(this, type, map) : type;
230
+ },
231
+
232
+ instantiateGenericFunc(genericType, typeArgs) {
233
+ const map = new Map();
234
+ for (let i = 0; i < genericType.typeParams.length; i++)
235
+ map.set(genericType.typeParams[i].name, typeArgs[i] ?? ANY);
236
+ return this.substituteType(genericType.underlying, map);
237
+ },
238
+
239
+ instantiateGenericType(generic, typeArgNodes, scope) {
240
+ const map = new Map();
241
+ const typeArgs = typeArgNodes.map((n) => this.resolveTypeNode(n, scope));
242
+ for (let i = 0; i < generic.typeParams.length; i++)
243
+ map.set(generic.typeParams[i].name, typeArgs[i] ?? ANY);
244
+ const typeScope = new Scope(this.globals);
245
+ for (const [name, type] of map) typeScope.define(name, type);
246
+ const underlying = this.resolveTypeNode(generic.declNode.type, typeScope);
247
+ const instantiated = this.substituteType(underlying, map);
248
+ if (instantiated.kind === "struct") {
249
+ instantiated.name = generic.declNode.name;
250
+ if (!instantiated.methods) instantiated.methods = new Map();
251
+ if (generic.methods) {
252
+ for (const [mName, mType] of generic.methods)
253
+ instantiated.methods.set(mName, this.substituteType(mType, map));
254
+ }
255
+ }
256
+ return {
257
+ kind: "named",
258
+ name: generic.declNode.name,
259
+ underlying: instantiated,
260
+ };
261
+ },
262
+
263
+ inferTypeArgs(genericType, argTypes) {
264
+ const map = new Map();
265
+ const params = genericType.underlying.params;
266
+ for (let i = 0; i < params.length && i < argTypes.length; i++)
267
+ this._inferFromPair(params[i], argTypes[i], map);
268
+ for (const tp of genericType.typeParams) {
269
+ if (!map.has(tp.name)) return null;
270
+ }
271
+ return genericType.typeParams.map((tp) => defaultType(map.get(tp.name)));
272
+ },
273
+
274
+ _inferFromFuncPair(paramType, argType, map) {
275
+ for (
276
+ let i = 0;
277
+ i < paramType.params.length && i < argType.params.length;
278
+ i++
279
+ )
280
+ this._inferFromPair(paramType.params[i], argType.params[i], map);
281
+ for (
282
+ let i = 0;
283
+ i < paramType.returns.length && i < argType.returns.length;
284
+ i++
285
+ )
286
+ this._inferFromPair(paramType.returns[i], argType.returns[i], map);
287
+ },
288
+
289
+ _inferFromStructuredPair(paramType, argType, map) {
290
+ if (paramType.kind === "slice" && argType.kind === "slice")
291
+ this._inferFromPair(paramType.elem, argType.elem, map);
292
+ if (paramType.kind === "map" && argType.kind === "map") {
293
+ this._inferFromPair(paramType.key, argType.key, map);
294
+ this._inferFromPair(paramType.value, argType.value, map);
295
+ }
296
+ if (paramType.kind === "func" && argType.kind === "func")
297
+ this._inferFromFuncPair(paramType, argType, map);
298
+ },
299
+
300
+ _inferFromPair(paramType, argType, map) {
301
+ if (!paramType || !argType) return;
302
+ if (paramType.kind === "typeParam") {
303
+ if (!map.has(paramType.name)) map.set(paramType.name, argType);
304
+ return;
305
+ }
306
+ this._inferFromStructuredPair(paramType, argType, map);
307
+ },
308
+
309
+ _checkUnionConstraint(typeArg, constraint, node) {
310
+ for (const term of constraint.terms) {
311
+ if (term.approx) return;
312
+ const termType = this.resolveTypeNode(term.type, this.globals);
313
+ if (typeStr(typeArg) === typeStr(termType)) return;
314
+ }
315
+ this.err(`type ${typeStr(typeArg)} does not satisfy constraint`, node);
316
+ },
317
+
318
+ _checkInterfaceConstraint(typeArg, base, constraint, node) {
319
+ if (base.methods.size === 0 && !base.unionConstraint) return;
320
+ if (base.unionConstraint) {
321
+ this.checkConstraint(typeArg, base.unionConstraint, node);
322
+ return;
323
+ }
324
+ if (!this.implements(typeArg, base, node))
325
+ this.err(
326
+ `type ${typeStr(typeArg)} does not satisfy constraint ${typeStr(constraint)}`,
327
+ node,
328
+ );
329
+ },
330
+
331
+ checkConstraint(typeArg, constraint, node) {
332
+ if (!constraint || isAny(constraint)) return;
333
+ if (constraint.kind === "basic" && constraint.name === "comparable") return;
334
+ if (constraint.kind === "UnionConstraint") {
335
+ this._checkUnionConstraint(typeArg, constraint, node);
336
+ return;
337
+ }
338
+ const base =
339
+ constraint.kind === "named" ? constraint.underlying : constraint;
340
+ if (base?.kind === "interface")
341
+ this._checkInterfaceConstraint(typeArg, base, constraint, node);
342
+ },
343
+
344
+ resolveType(t) {
345
+ if (!t) return ANY;
346
+ if (t.kind === "basic" && t.alias) {
347
+ const found = this.types.get(t.alias);
348
+ if (found) return found;
349
+ }
350
+ return t;
351
+ },
352
+
353
+ // ── Field type lookup ─────────────────────────────────────────────
354
+
355
+ fieldType(baseType, field, node) {
356
+ baseType = this.resolveType(baseType);
357
+ if (!baseType || isAny(baseType)) return ANY;
358
+ if (this._isTransparentPointerField(baseType, field))
359
+ baseType = this.resolveType(baseType.base);
360
+ if (baseType.kind === "named" && baseType.methods?.has(field))
361
+ return baseType.methods.get(field);
362
+ let base = baseType.kind === "named" ? baseType.underlying : baseType;
363
+ base = this.resolveType(base);
364
+ return this._dispatchFieldType(base, baseType, field, node);
365
+ },
366
+
367
+ _isTransparentPointerField(baseType, field) {
368
+ return (
369
+ baseType.kind === "pointer" &&
370
+ field !== "value" &&
371
+ baseType.base?.kind === "named"
372
+ );
373
+ },
374
+
375
+ _dispatchFieldType(base, baseType, field, node) {
376
+ switch (base?.kind) {
377
+ case "struct":
378
+ return this._fieldTypeStruct(base, baseType, field, node);
379
+ case "interface":
380
+ return this._fieldTypeInterface(base, baseType, field, node);
381
+ case "namespace":
382
+ return this._fieldTypeNamespace(base, field, node);
383
+ default:
384
+ if (base && !isAny(base))
385
+ return this._fieldTypeBadAccess(base, baseType, field, node);
386
+ return ANY;
387
+ }
388
+ },
389
+
390
+ _fieldTypeStruct(base, baseType, field, node) {
391
+ if (base.fields?.has(field)) return base.fields.get(field);
392
+ if (base.methods?.has(field)) return base.methods.get(field);
393
+ return this.err(`No field '${field}' on ${typeStr(baseType)}`, node);
394
+ },
395
+
396
+ _fieldTypeInterface(base, baseType, field, node) {
397
+ if (base.methods?.has(field)) return base.methods.get(field);
398
+ return this.err(`No method '${field}' on ${typeStr(baseType)}`, node);
399
+ },
400
+
401
+ _fieldTypeBadAccess(base, baseType, field, node) {
402
+ if (base.kind === "pointer" && field === "value") return base.base ?? ANY;
403
+ const badKinds = ["basic", "slice", "array", "map", "func", "tuple"];
404
+ if (badKinds.includes(base.kind))
405
+ return this.err(
406
+ `${typeStr(baseType ?? base)} has no field or method '${field}'`,
407
+ node,
408
+ );
409
+ return ANY;
410
+ },
411
+
412
+ _fieldTypeNamespace(base, field, node) {
413
+ if (base._gofront && field.length > 0 && field[0] >= "a" && field[0] <= "z")
414
+ return this.err(
415
+ `cannot refer to unexported name ${base.name}.${field}`,
416
+ node,
417
+ );
418
+ if (field in base.members) return base.members[field];
419
+ return this.err(`No member '${field}' in namespace ${base.name}`, node);
420
+ },
421
+ };