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