@xnoxs/flux-lang 3.1.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 (56) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/README.md +1089 -0
  3. package/bin/flux.js +1397 -0
  4. package/dist/flux.cjs.js +6664 -0
  5. package/dist/flux.esm.js +6674 -0
  6. package/dist/flux.min.js +263 -0
  7. package/index.d.ts +202 -0
  8. package/index.js +26 -0
  9. package/package.json +77 -0
  10. package/scripts/build.js +76 -0
  11. package/src/bundler.js +216 -0
  12. package/src/checker.js +322 -0
  13. package/src/codegen.js +785 -0
  14. package/src/css-preprocessor.js +399 -0
  15. package/src/formatter.js +140 -0
  16. package/src/jsx.js +480 -0
  17. package/src/lexer.js +518 -0
  18. package/src/linter.js +758 -0
  19. package/src/mangler.js +280 -0
  20. package/src/parser.js +1671 -0
  21. package/src/self/bundler.flux +167 -0
  22. package/src/self/bundler.js +187 -0
  23. package/src/self/checker.flux +249 -0
  24. package/src/self/checker.js +338 -0
  25. package/src/self/codegen.flux +555 -0
  26. package/src/self/codegen.js +784 -0
  27. package/src/self/css-preprocessor.flux +373 -0
  28. package/src/self/css-preprocessor.js +387 -0
  29. package/src/self/formatter.flux +93 -0
  30. package/src/self/formatter.js +114 -0
  31. package/src/self/jsx.flux +430 -0
  32. package/src/self/jsx.js +396 -0
  33. package/src/self/lexer.flux +529 -0
  34. package/src/self/lexer.js +709 -0
  35. package/src/self/lexer.stage2.js +700 -0
  36. package/src/self/linter.flux +515 -0
  37. package/src/self/linter.js +804 -0
  38. package/src/self/mangler.flux +253 -0
  39. package/src/self/mangler.js +348 -0
  40. package/src/self/parser.flux +1146 -0
  41. package/src/self/parser.js +1571 -0
  42. package/src/self/sourcemap.flux +66 -0
  43. package/src/self/sourcemap.js +72 -0
  44. package/src/self/stdlib.flux +356 -0
  45. package/src/self/stdlib.js +396 -0
  46. package/src/self/test-runner.flux +201 -0
  47. package/src/self/test-runner.js +132 -0
  48. package/src/self/transpiler.flux +123 -0
  49. package/src/self/transpiler.js +83 -0
  50. package/src/self/type-checker.flux +821 -0
  51. package/src/self/type-checker.js +1106 -0
  52. package/src/sourcemap.js +82 -0
  53. package/src/stdlib.js +436 -0
  54. package/src/test-runner.js +239 -0
  55. package/src/transpiler.js +172 -0
  56. package/src/type-checker.js +1206 -0
@@ -0,0 +1,821 @@
1
+ // ============================================================
2
+ // Flux Self-Hosted Type Checker
3
+ // src/self/type-checker.flux — written in Flux, compiled by stage-0
4
+ //
5
+ // Full TypeScript-level type checking engine.
6
+ // Features: structural typing, union/intersection, nullable,
7
+ // generics, utility types, type narrowing, tuples,
8
+ // conditional types, function types, interface enforcement
9
+ // ============================================================
10
+
11
+ // ── Primitive singletons ──────────────────────────────────────────────────────
12
+ export val T_ANY = Object.freeze({ kind: "any" })
13
+ export val T_UNKNOWN = Object.freeze({ kind: "unknown" })
14
+ export val T_NEVER = Object.freeze({ kind: "never" })
15
+ export val T_VOID = Object.freeze({ kind: "void" })
16
+ export val T_NULL = Object.freeze({ kind: "null" })
17
+ export val T_STRING = Object.freeze({ kind: "primitive", name: "String" })
18
+ export val T_INT = Object.freeze({ kind: "primitive", name: "Int" })
19
+ export val T_FLOAT = Object.freeze({ kind: "primitive", name: "Float" })
20
+ export val T_NUMBER = Object.freeze({ kind: "primitive", name: "Number" })
21
+ export val T_BOOL = Object.freeze({ kind: "primitive", name: "Bool" })
22
+
23
+ // ── Type constructors ─────────────────────────────────────────────────────────
24
+ export fn T_UNION(...members):
25
+ val flat = []
26
+ for m in members:
27
+ if m and m.kind == "union":
28
+ for sub in m.members: flat.push(sub)
29
+ else if m:
30
+ flat.push(m)
31
+ val seen = new Set()
32
+ val deduped = []
33
+ for m in flat:
34
+ val k = typeStr(m)
35
+ if not seen.has(k):
36
+ seen.add(k)
37
+ deduped.push(m)
38
+ if deduped.length == 0: return T_NEVER
39
+ if deduped.length == 1: return deduped[0]
40
+ return { kind: "union", members: deduped }
41
+
42
+ export fn T_INTERSECTION(...members):
43
+ val flat = []
44
+ for m in members:
45
+ if m and m.kind == "intersection":
46
+ for sub in m.members: flat.push(sub)
47
+ else if m:
48
+ flat.push(m)
49
+ if flat.length == 1: return flat[0]
50
+ return { kind: "intersection", members: flat }
51
+
52
+ export fn T_ARRAY(elem):
53
+ return { kind: "generic", name: "Array", args: [elem ?? T_UNKNOWN] }
54
+
55
+ export fn T_TUPLE(types):
56
+ return { kind: "tuple", types: types ?? [] }
57
+
58
+ export fn T_NULLABLE(t):
59
+ return T_UNION(t, T_NULL)
60
+
61
+ export fn T_NAMED(name):
62
+ return { kind: "named", name }
63
+
64
+ export fn T_FN(params, ret):
65
+ return { kind: "fn", params: params ?? [], ret: ret ?? T_VOID }
66
+
67
+ export fn T_OBJECT(shape):
68
+ return { kind: "object", shape: shape ?? new Map() }
69
+
70
+ export fn T_RECORD(key, val_):
71
+ return { kind: "record", key: key ?? T_STRING, val: val_ ?? T_UNKNOWN }
72
+
73
+ export fn T_LITERAL(value, kind):
74
+ return { kind: "literal", value, prim: kind }
75
+
76
+ // ── Type → display string ─────────────────────────────────────────────────────
77
+ export fn typeStr(t):
78
+ if not t: return "unknown"
79
+ if t.kind == "any": return "Any"
80
+ if t.kind == "unknown": return "Unknown"
81
+ if t.kind == "never": return "Never"
82
+ if t.kind == "void": return "Void"
83
+ if t.kind == "null": return "Null"
84
+ if t.kind == "primitive": return t.name
85
+ if t.kind == "literal": return JSON.stringify(t.value)
86
+ if t.kind == "union": return t.members.map(typeStr).join(" | ")
87
+ if t.kind == "intersection": return t.members.map(typeStr).join(" & ")
88
+ if t.kind == "tuple": return "[" + t.types.map(typeStr).join(", ") + "]"
89
+ if t.kind == "object":
90
+ val entries = []
91
+ for pair in (t.shape ?? new Map()).entries():
92
+ entries.push(pair[0] + ': ' + typeStr(pair[1]))
93
+ return '{' + entries.join(', ') + '}'
94
+ if t.kind == "record": return "Record<" + typeStr(t.key) + ", " + typeStr(t.val) + ">"
95
+ if t.kind == "generic":
96
+ if t.args and t.args.length > 0:
97
+ return t.name + "<" + t.args.map(typeStr).join(", ") + ">"
98
+ return t.name
99
+ if t.kind == "named": return t.name
100
+ if t.kind == "fn":
101
+ return "(" + (t.params ?? []).map(typeStr).join(", ") + ") -> " + typeStr(t.ret)
102
+ if t.kind == "keyof": return "keyof " + typeStr(t.inner)
103
+ if t.kind == "conditional":
104
+ return typeStr(t.check) + " extends " + typeStr(t.extends) + " ? " + typeStr(t.then) + " : " + typeStr(t.else)
105
+ return "unknown"
106
+
107
+ // ── Split at top-level separator ──────────────────────────────────────────────
108
+ fn splitAtTopLevel(str, sep):
109
+ val parts = []
110
+ var depth = 0
111
+ var start = 0
112
+ var i = 0
113
+ while i < str.length:
114
+ val c = str[i]
115
+ if '<([{'.includes(c): depth = depth + 1
116
+ else if '>)]}'.includes(c): depth = depth - 1
117
+ else if depth == 0 and str.slice(i, i + sep.length) == sep:
118
+ parts.push(str.slice(start, i))
119
+ start = i + sep.length
120
+ i = i + sep.length - 1
121
+ i = i + 1
122
+ parts.push(str.slice(start))
123
+ return parts.filter(p -> p.trim().length > 0)
124
+
125
+ // ── Primitive type map ─────────────────────────────────────────────────────────
126
+ val PRIMITIVE_MAP = {
127
+ String: T_STRING, string: T_STRING,
128
+ Int: T_INT, int: T_INT,
129
+ Float: T_FLOAT, float: T_FLOAT,
130
+ Number: T_NUMBER, number: T_NUMBER,
131
+ Bool: T_BOOL, boolean: T_BOOL, Boolean: T_BOOL, bool: T_BOOL,
132
+ Void: T_VOID, void: T_VOID,
133
+ Never: T_NEVER, never: T_NEVER,
134
+ Any: T_ANY, any: T_ANY,
135
+ Unknown: T_UNKNOWN, unknown: T_UNKNOWN,
136
+ Null: T_NULL, null: T_NULL, undefined: T_VOID,
137
+ Object: T_NAMED("Object"), object: T_NAMED("Object"),
138
+ Symbol: T_NAMED("Symbol"), symbol: T_NAMED("Symbol"),
139
+ BigInt: T_NAMED("BigInt"), bigint: T_NAMED("BigInt"),
140
+ }
141
+
142
+ // ── Parse annotation string → Type ────────────────────────────────────────────
143
+ export fn parseAnnotation(str):
144
+ if not str: return null
145
+ var s = str.trim()
146
+
147
+ // Strip outer parens
148
+ if s.startsWith("(") and s.endsWith(")"):
149
+ s = s.slice(1, -1).trim()
150
+
151
+ // Union
152
+ val unionParts = splitAtTopLevel(s, " | ")
153
+ if unionParts.length > 1:
154
+ return T_UNION(...unionParts.map(parseAnnotation))
155
+
156
+ // Intersection
157
+ val interParts = splitAtTopLevel(s, " & ")
158
+ if interParts.length > 1:
159
+ return T_INTERSECTION(...interParts.map(parseAnnotation))
160
+
161
+ // Conditional type: T extends U ? A : B
162
+ val condMatch = s.match(/^(\w+)\s+extends\s+(.+?)\s*\?\s*(.+?)\s*:\s*(.+)$/)
163
+ if condMatch:
164
+ return {
165
+ kind: "conditional",
166
+ check: parseAnnotation(condMatch[1]),
167
+ extends: parseAnnotation(condMatch[2]),
168
+ then: parseAnnotation(condMatch[3]),
169
+ else: parseAnnotation(condMatch[4]),
170
+ }
171
+
172
+ // keyof T
173
+ if s.startsWith("keyof "):
174
+ return { kind: "keyof", inner: parseAnnotation(s.slice(6)) }
175
+
176
+ // typeof x
177
+ if s.startsWith("typeof "):
178
+ return { kind: "typeof", name: s.slice(7) }
179
+
180
+ // readonly T
181
+ if s.startsWith("readonly "):
182
+ return parseAnnotation(s.slice(9))
183
+
184
+ // infer T → Any
185
+ if s.startsWith("infer "):
186
+ return T_ANY
187
+
188
+ // Function type: fn(T1, T2) -> Ret OR (T1, T2) -> Ret
189
+ val fnMatch = s.match(/^fn\(([^)]*)\)\s*->\s*(.+)$/) ?? s.match(/^\(([^)]*)\)\s*->\s*(.+)$/)
190
+ if fnMatch:
191
+ val paramStr = fnMatch[1].trim()
192
+ val retStr = fnMatch[2].trim()
193
+ val params = paramStr ? splitAtTopLevel(paramStr, ", ").map(p -> parseAnnotation(p.slice(p.indexOf(":") >= 0 ? p.indexOf(":") + 1 : 0).trim())) : []
194
+ return T_FN(params, parseAnnotation(retStr))
195
+
196
+ // Nullable shorthand: String?
197
+ if s.endsWith("?"):
198
+ return T_NULLABLE(parseAnnotation(s.slice(0, -1)))
199
+
200
+ // Array shorthand: String[]
201
+ if s.endsWith("[]"):
202
+ return T_ARRAY(parseAnnotation(s.slice(0, -2)))
203
+
204
+ // Tuple: [String, Int, Bool]
205
+ if s.startsWith("[") and s.endsWith("]"):
206
+ val inner = s.slice(1, -1)
207
+ if not inner.trim(): return T_TUPLE([])
208
+ val parts = splitAtTopLevel(inner, ", ")
209
+ return T_TUPLE(parts.map(p -> parseAnnotation(p.replace(/^\.\.\./, "").trim())))
210
+
211
+ // Inline object type: { key: Type, key2?: Type }
212
+ if s.startsWith("{") and s.endsWith("}"):
213
+ val inner = s.slice(1, -1).trim()
214
+ val shape = new Map()
215
+ if inner:
216
+ val pairs = splitAtTopLevel(inner, ", ")
217
+ for pair in pairs:
218
+ val idxMatch = pair.match(/^\[(\w+):\s*\w+\]:\s*(.+)$/)
219
+ if idxMatch:
220
+ shape.set("[index]", parseAnnotation(idxMatch[2]))
221
+ continue
222
+ val colonIdx = pair.indexOf(":")
223
+ if colonIdx >= 0:
224
+ val rawKey = pair.slice(0, colonIdx).trim().replace(/^readonly\s+/, "")
225
+ val key = rawKey.replace(/\?$/, "")
226
+ val opt = rawKey.endsWith("?")
227
+ val valType = parseAnnotation(pair.slice(colonIdx + 1).trim())
228
+ shape.set(key, opt ? T_UNION(valType, T_VOID) : valType)
229
+ return T_OBJECT(shape)
230
+
231
+ // Generic / Utility types: Array<T>, Record<K,V>, Partial<T> etc.
232
+ val genMatch = s.match(/^(\w+)<(.+)>$/s)
233
+ if genMatch:
234
+ val gname = genMatch[1]
235
+ val rawArgs = splitAtTopLevel(genMatch[2], ", ")
236
+ val args = rawArgs.map(a -> parseAnnotation(a.trim()))
237
+ if gname == "Array" or gname == "List" or gname == "ReadonlyArray":
238
+ return T_ARRAY(args[0] ?? T_UNKNOWN)
239
+ if gname == "NonNullable":
240
+ return resolveNonNullable(args[0])
241
+ if gname == "Partial": return { kind: "utility", util: "Partial", inner: args[0] }
242
+ if gname == "Required": return { kind: "utility", util: "Required", inner: args[0] }
243
+ if gname == "Readonly": return args[0]
244
+ if gname == "Record": return T_RECORD(args[0] ?? T_STRING, args[1] ?? T_UNKNOWN)
245
+ if gname == "Pick": return { kind: "utility", util: "Pick", inner: args[0], keys: args[1] }
246
+ if gname == "Omit": return { kind: "utility", util: "Omit", inner: args[0], keys: args[1] }
247
+ if gname == "Exclude": return { kind: "utility", util: "Exclude", inner: args[0], keys: args[1] }
248
+ if gname == "Extract": return { kind: "utility", util: "Extract", inner: args[0], keys: args[1] }
249
+ if gname == "ReturnType":return { kind: "utility", util: "ReturnType", inner: args[0] }
250
+ if gname == "Parameters":return { kind: "utility", util: "Parameters", inner: args[0] }
251
+ if gname == "Awaited": return { kind: "utility", util: "Awaited", inner: args[0] }
252
+ return { kind: "generic", name: gname, args }
253
+
254
+ // Primitive or named
255
+ if PRIMITIVE_MAP[s]: return PRIMITIVE_MAP[s]
256
+ return T_NAMED(s)
257
+
258
+ fn resolveNonNullable(t):
259
+ if not t: return T_UNKNOWN
260
+ if t.kind == "union":
261
+ return T_UNION(...t.members.filter(m -> m.kind != "null" and m.kind != "void"))
262
+ if t.kind == "null" or t.kind == "void": return T_NEVER
263
+ return t
264
+
265
+ // ── Basic assignability ───────────────────────────────────────────────────────
266
+ fn basicAssignable(from_, to_):
267
+ if not from_ or not to_: return true
268
+ if to_.kind == "any": return true
269
+ if from_.kind == "any": return true
270
+ if from_.kind == "never": return true
271
+ if to_.kind == "unknown": return true
272
+ if from_.kind == "void" and to_.kind == "void": return true
273
+ if from_.kind == "null" and to_.kind == "null": return true
274
+
275
+ // Null assignable to nullable union
276
+ if from_.kind == "null" and to_.kind == "union":
277
+ return to_.members.some(m -> m.kind == "null" or m.kind == "any" or m.kind == "void")
278
+
279
+ // From union: all members assignable to target
280
+ if from_.kind == "union":
281
+ return from_.members.every(m -> basicAssignable(m, to_))
282
+ // To union: from assignable to at least one
283
+ if to_.kind == "union":
284
+ return to_.members.some(m -> basicAssignable(from_, m))
285
+
286
+ // Intersection
287
+ if from_.kind == "intersection":
288
+ return from_.members.some(m -> basicAssignable(m, to_))
289
+ if to_.kind == "intersection":
290
+ return to_.members.every(m -> basicAssignable(from_, m))
291
+
292
+ // Primitives
293
+ if from_.kind == "primitive" and to_.kind == "primitive":
294
+ if from_.name == to_.name: return true
295
+ if to_.name == "Number" and (from_.name == "Int" or from_.name == "Float" or from_.name == "Number"): return true
296
+ if to_.name == "Float" and from_.name == "Int": return true
297
+ if from_.name == "Bool" and to_.name == "Boolean": return true
298
+ if from_.name == "Boolean" and to_.name == "Bool": return true
299
+ return false
300
+
301
+ // Literal to primitive
302
+ if from_.kind == "literal" and to_.kind == "primitive":
303
+ val lmap = { string: "String", number: "Number", boolean: "Bool" }
304
+ return lmap[from_.prim] == to_.name or (from_.prim == "number" and (to_.name == "Int" or to_.name == "Float" or to_.name == "Number"))
305
+
306
+ // Named types
307
+ if from_.kind == "named" and to_.kind == "named":
308
+ return from_.name == to_.name or to_.name == "Object"
309
+
310
+ // Generic types (covariant args)
311
+ if from_.kind == "generic" and to_.kind == "generic":
312
+ val arrayNames = new Set(["Array", "List", "ReadonlyArray"])
313
+ if from_.name != to_.name:
314
+ if not (arrayNames.has(from_.name) and arrayNames.has(to_.name)): return false
315
+ if from_.args.length != to_.args.length: return false
316
+ return from_.args.every((a, i) -> basicAssignable(a, to_.args[i]))
317
+
318
+ // Tuple types
319
+ if from_.kind == "tuple" and to_.kind == "tuple":
320
+ if from_.types.length != to_.types.length: return false
321
+ return from_.types.every((t, i) -> basicAssignable(t, to_.types[i]))
322
+ // Array to tuple
323
+ if from_.kind == "generic" and from_.name == "Array" and to_.kind == "tuple":
324
+ val elemType = from_.args[0] ?? T_UNKNOWN
325
+ if elemType.kind == "unknown": return true
326
+ return to_.types.every(t -> basicAssignable(elemType, t))
327
+
328
+ // Object structural
329
+ if from_.kind == "object" and to_.kind == "object":
330
+ for pair in to_.shape.entries():
331
+ val key = pair[0]
332
+ val toType = pair[1]
333
+ if not from_.shape.has(key): return false
334
+ if not basicAssignable(from_.shape.get(key), toType): return false
335
+ return true
336
+
337
+ // Object to named (structural checked in class)
338
+ if from_.kind == "object" and to_.kind == "named": return true
339
+
340
+ // Function type
341
+ if from_.kind == "fn" and to_.kind == "fn":
342
+ if to_.ret and from_.ret: return basicAssignable(from_.ret, to_.ret)
343
+ return true
344
+
345
+ // Record
346
+ if from_.kind == "record" and to_.kind == "record":
347
+ return basicAssignable(from_.val, to_.val)
348
+
349
+ // Utility types
350
+ if to_.kind == "utility": return true
351
+ if from_.kind == "utility": return true
352
+
353
+ return false
354
+
355
+ // ── Type merging ──────────────────────────────────────────────────────────────
356
+ fn mergeTypes(a, b):
357
+ if not a or a.kind == "unknown": return b ?? T_UNKNOWN
358
+ if not b or b.kind == "unknown": return a
359
+ if typeStr(a) == typeStr(b): return a
360
+ if a.kind == "any" or b.kind == "any": return T_ANY
361
+ return T_UNION(a, b)
362
+
363
+ // ── Type Environment ──────────────────────────────────────────────────────────
364
+ export class TypeEnv:
365
+ parent: any
366
+ vars: any
367
+ retType: any
368
+ isAsync: bool
369
+
370
+ fn set(name, type_):
371
+ self.vars.set(name, type_)
372
+
373
+ fn has(name):
374
+ var e = self
375
+ while e:
376
+ if e.vars.has(name): return true
377
+ e = e.parent
378
+ return false
379
+
380
+ fn get(name):
381
+ var env = self
382
+ while env:
383
+ if env.vars.has(name): return env.vars.get(name)
384
+ env = env.parent
385
+ return null
386
+
387
+ fn child():
388
+ return new TypeEnv(self, new Map(), self.retType, self.isAsync)
389
+
390
+ fn childFn(retType, isAsync_):
391
+ return new TypeEnv(self, new Map(), retType, isAsync_ ?? false)
392
+
393
+ fn narrow(name, type_):
394
+ val c = new TypeEnv(self, new Map(), self.retType, self.isAsync)
395
+ if name and type_: c.vars.set(name, type_)
396
+ return c
397
+
398
+ // ── Type Check Error ──────────────────────────────────────────────────────────
399
+ class TypeCheckError:
400
+ message: any
401
+ name: any
402
+ hint: any
403
+ line: any
404
+ col: any
405
+
406
+ // ── Main Type Checker ─────────────────────────────────────────────────────────
407
+ export class FluxTypeChecker:
408
+ errors: any[]
409
+ warnings: any[]
410
+ interfaces: any
411
+ types: any
412
+ classes: any
413
+ enums: any
414
+
415
+ fn _err(msg, loc, hint):
416
+ val e = new TypeCheckError()
417
+ e.message = msg
418
+ e.name = "TypeError"
419
+ e.hint = hint ?? null
420
+ if loc:
421
+ e.line = loc.line
422
+ e.col = loc.col
423
+ self.errors.push(e)
424
+
425
+ fn _warn(msg, loc, hint):
426
+ val w = new TypeCheckError()
427
+ w.message = msg
428
+ w.name = "TypeError"
429
+ w.hint = hint ?? null
430
+ if loc:
431
+ w.line = loc.line
432
+ w.col = loc.col
433
+ self.warnings.push(w)
434
+
435
+ fn check(ast):
436
+ self.errors = []
437
+ self.warnings = []
438
+ self.interfaces = new Map()
439
+ self.types = new Map()
440
+ self.classes = new Map()
441
+ self.enums = new Map()
442
+
443
+ self._collectDeclarations(ast.body)
444
+ self._validateImplementations()
445
+
446
+ val env = new TypeEnv(null, new Map(), null, false)
447
+ self._registerBuiltins(env)
448
+ self._checkStmts(ast.body, env)
449
+
450
+ return { errors: self.errors, warnings: self.warnings }
451
+
452
+ fn _collectDeclarations(stmts):
453
+ for node in stmts:
454
+ val n = node.type == "ExportDecl" ? node.decl : node
455
+ if not n: continue
456
+ if n.type == "InterfaceDecl": self.interfaces.set(n.name, n)
457
+ else if n.type == "TypeDecl": self.types.set(n.name, n)
458
+ else if n.type == "ClassDecl": self.classes.set(n.name, n)
459
+ else if n.type == "EnumDecl": self.enums.set(n.name, n)
460
+
461
+ fn _validateImplementations():
462
+ for clsPair in self.classes.entries():
463
+ val cls = clsPair[1]
464
+ for ifaceName in (cls.interfaces ?? []):
465
+ self._checkInterfaceImpl(cls, ifaceName)
466
+
467
+ fn _checkInterfaceImpl(cls, ifaceName):
468
+ val iface = self.interfaces.get(ifaceName)
469
+ if not iface:
470
+ self._warn(
471
+ "Class '" + cls.name + "' implements unknown interface '" + ifaceName + "'",
472
+ cls.loc,
473
+ "Define 'interface " + ifaceName + "' before use"
474
+ )
475
+ return
476
+ for member in iface.members:
477
+ if member.kind == "method":
478
+ val impl = cls.methods.find(m -> m.name == member.name)
479
+ if not impl:
480
+ self._err(
481
+ "Class '" + cls.name + "' does not implement method '" + member.name + "()' required by '" + ifaceName + "'",
482
+ cls.loc,
483
+ "Add 'fn " + member.name + "(...)' to the class"
484
+ )
485
+ else if member.kind == "field" and not member.optional:
486
+ val hasField = cls.fields.find(f -> f.name == member.name)
487
+ val hasMethod = cls.methods.find(m -> m.name == member.name)
488
+ if not hasField and not hasMethod:
489
+ self._err(
490
+ "Class '" + cls.name + "' is missing field '" + member.name + "' required by '" + ifaceName + "'",
491
+ cls.loc,
492
+ "Add '" + member.name + ": " + (member.typeAnn ?? "Any") + "' to the class"
493
+ )
494
+ for superIface in (iface.superInterfaces ?? []):
495
+ self._checkInterfaceImpl(cls, superIface)
496
+
497
+ fn _isStructurallyCompatible(objShape, typeName):
498
+ val iface = self.interfaces.get(typeName)
499
+ if not iface:
500
+ val cls = self.classes.get(typeName)
501
+ if not cls: return false
502
+ for field in cls.fields:
503
+ if not objShape.has(field.name) and not field.optional: return false
504
+ return true
505
+ for member in iface.members:
506
+ if member.kind == "field" and not member.optional:
507
+ if not objShape.has(member.name): return false
508
+ return true
509
+
510
+ fn _isAssignable(from_, to_):
511
+ if not from_ or not to_: return true
512
+ if to_.kind == "any": return true
513
+ if from_.kind == "any": return true
514
+ if from_.kind == "never": return true
515
+ if to_.kind == "unknown": return true
516
+
517
+ // Structural: object literal vs interface/class
518
+ if from_.kind == "object" and to_.kind == "named":
519
+ return self._isStructurallyCompatible(from_.shape, to_.name)
520
+
521
+ // Named satisfies interface (structural check)
522
+ if from_.kind == "named" and to_.kind == "named":
523
+ if from_.name == to_.name: return true
524
+ if to_.name == "Object": return true
525
+ val cls = self.classes.get(from_.name)
526
+ if cls and (cls.interfaces ?? []).includes(to_.name): return true
527
+ if cls and cls.superClass == to_.name: return true
528
+ val iface = self.interfaces.get(from_.name)
529
+ if iface and (iface.superInterfaces ?? []).includes(to_.name): return true
530
+ return false
531
+
532
+ // Utility types
533
+ if to_.kind == "utility":
534
+ if to_.util == "Partial": return true
535
+ if to_.util == "Required": return self._isAssignable(from_, to_.inner)
536
+ if to_.util == "NonNullable":return from_.kind != "null" and from_.kind != "void"
537
+ return true
538
+ if from_.kind == "utility": return true
539
+
540
+ return basicAssignable(from_, to_)
541
+
542
+ fn _analyzeNarrowingCondition(condExpr, env):
543
+ if not condExpr: return null
544
+
545
+ // x != null / x !== null
546
+ if condExpr.type == "BinaryExpr" and (condExpr.op == "!=" or condExpr.op == "!==") and condExpr.left.type == "Identifier" and condExpr.right.type == "NullLit":
547
+ val name = condExpr.left.name
548
+ val t = env.get(name)
549
+ if t and t.kind == "union":
550
+ val narrowed = T_UNION(...t.members.filter(m -> m.kind != "null" and m.kind != "void"))
551
+ return { varName: name, trueType: narrowed, falseType: T_UNION(T_NULL, T_VOID) }
552
+
553
+ // typeof x === "string"
554
+ if condExpr.type == "BinaryExpr" and (condExpr.op == "===" or condExpr.op == "==") and condExpr.left.type == "TypeofExpr" and condExpr.left.operand.type == "Identifier" and condExpr.right.type == "StringLit":
555
+ val name = condExpr.left.operand.name
556
+ val typeMap = { string: T_STRING, number: T_NUMBER, boolean: T_BOOL, undefined: T_VOID }
557
+ val narrowedTrue = typeMap[condExpr.right.value] ?? T_UNKNOWN
558
+ return { varName: name, trueType: narrowedTrue, falseType: env.get(name) ?? T_UNKNOWN }
559
+
560
+ // x instanceof Class
561
+ if condExpr.type == "BinaryExpr" and condExpr.op == "instanceof" and condExpr.left.type == "Identifier":
562
+ val name = condExpr.left.name
563
+ val cls = condExpr.right.type == "Identifier" ? condExpr.right.name : null
564
+ if cls: return { varName: name, trueType: T_NAMED(cls), falseType: env.get(name) ?? T_UNKNOWN }
565
+
566
+ // Boolean narrowing: x (truthy)
567
+ if condExpr.type == "Identifier":
568
+ val name = condExpr.name
569
+ val t = env.get(name)
570
+ if t and t.kind == "union":
571
+ val narrowed = T_UNION(...t.members.filter(m -> m.kind != "null" and m.kind != "void"))
572
+ return { varName: name, trueType: narrowed, falseType: T_UNION(T_NULL, T_VOID) }
573
+
574
+ return null
575
+
576
+ fn _registerBuiltins(env):
577
+ env.set("console", T_NAMED("Console"))
578
+ env.set("process", T_NAMED("Process"))
579
+ env.set("Math", T_NAMED("Math"))
580
+ env.set("JSON", T_NAMED("JSON"))
581
+ env.set("Date", T_NAMED("Date"))
582
+ env.set("Promise", T_NAMED("Promise"))
583
+ env.set("Error", T_NAMED("Error"))
584
+ env.set("Buffer", T_NAMED("Buffer"))
585
+ env.set("RegExp", T_NAMED("RegExp"))
586
+ env.set("Map", T_NAMED("Map"))
587
+ env.set("Set", T_NAMED("Set"))
588
+ env.set("WeakMap", T_NAMED("WeakMap"))
589
+ env.set("WeakSet", T_NAMED("WeakSet"))
590
+ env.set("Symbol", T_NAMED("Symbol"))
591
+ env.set("Object", T_NAMED("Object"))
592
+ env.set("Array", T_NAMED("Array"))
593
+ env.set("String", T_NAMED("String"))
594
+ env.set("Number", T_NAMED("Number"))
595
+ env.set("Boolean", T_NAMED("Boolean"))
596
+ env.set("Function", T_NAMED("Function"))
597
+ env.set("parseInt", T_FN([T_STRING, T_INT], T_INT))
598
+ env.set("parseFloat", T_FN([T_STRING], T_FLOAT))
599
+ env.set("isNaN", T_FN([T_NUMBER], T_BOOL))
600
+ env.set("isFinite", T_FN([T_NUMBER], T_BOOL))
601
+ env.set("require", T_FN([T_STRING], T_ANY))
602
+ env.set("fetch", T_FN([T_STRING], T_ANY))
603
+ env.set("print", T_FN([], T_VOID))
604
+ env.set("undefined", T_VOID)
605
+ env.set("Infinity", T_NUMBER)
606
+ env.set("NaN", T_NUMBER)
607
+ env.set("globalThis", T_NAMED("Object"))
608
+
609
+ fn _inferType(node, env):
610
+ if not node: return T_UNKNOWN
611
+ if node.type == "NumberLit":
612
+ if String(node.value).includes("."): return T_FLOAT
613
+ return T_INT
614
+ if node.type == "StringLit": return T_STRING
615
+ if node.type == "BoolLit": return T_BOOL
616
+ if node.type == "NullLit": return T_NULL
617
+ if node.type == "Identifier":
618
+ val t = env.get(node.name)
619
+ return t ?? T_UNKNOWN
620
+ if node.type == "ArrayExpr":
621
+ if node.items.length == 0: return T_ARRAY(T_UNKNOWN)
622
+ val elemTypes = node.items.map(i -> i ? self._inferType(i, env) : T_UNKNOWN)
623
+ val merged = elemTypes.reduce((a, b) -> mergeTypes(a, b), T_UNKNOWN)
624
+ return T_ARRAY(merged)
625
+ if node.type == "ObjectExpr":
626
+ val shape = new Map()
627
+ for pair in node.pairs:
628
+ if not pair.spread and pair.key:
629
+ shape.set(pair.key, pair.value ? self._inferType(pair.value, env) : T_UNKNOWN)
630
+ return T_OBJECT(shape)
631
+ if node.type == "LambdaExpr":
632
+ val paramTypes = node.params.map(p -> p.typeAnn ? parseAnnotation(p.typeAnn) : T_UNKNOWN)
633
+ val retType = node.retType ? parseAnnotation(node.retType) : T_UNKNOWN
634
+ return T_FN(paramTypes, retType)
635
+ if node.type == "CallExpr" or node.type == "OptCallExpr":
636
+ val calleeType = self._inferType(node.callee, env)
637
+ if calleeType.kind == "fn": return calleeType.ret ?? T_UNKNOWN
638
+ return T_UNKNOWN
639
+ if node.type == "MemberExpr" or node.type == "OptMemberExpr":
640
+ return T_UNKNOWN
641
+ if node.type == "TernaryExpr":
642
+ val thenType = self._inferType(node.then, env)
643
+ val elseType = self._inferType(node.else_, env)
644
+ return mergeTypes(thenType, elseType)
645
+ if node.type == "BinaryExpr":
646
+ if node.op == "+" or node.op == "-" or node.op == "*" or node.op == "/" or node.op == "%":
647
+ return T_NUMBER
648
+ if node.op == "==" or node.op == "!=" or node.op == "===" or node.op == "!==" or node.op == "<" or node.op == ">" or node.op == "<=" or node.op == ">=":
649
+ return T_BOOL
650
+ if node.op == "and" or node.op == "or" or node.op == "&&" or node.op == "||":
651
+ return T_BOOL
652
+ return T_UNKNOWN
653
+ if node.type == "AwaitExpr":
654
+ val inner = self._inferType(node.operand, env)
655
+ return inner
656
+ if node.type == "NewExpr":
657
+ if node.callee and node.callee.type == "Identifier":
658
+ return T_NAMED(node.callee.name)
659
+ return T_UNKNOWN
660
+ if node.type == "TemplateLit": return T_STRING
661
+ if node.type == "CastExpr" or node.type == "AsConstExpr":
662
+ if node.typeAnn: return parseAnnotation(node.typeAnn)
663
+ return self._inferType(node.expr, env)
664
+ return T_UNKNOWN
665
+
666
+ fn _checkStmts(stmts, env):
667
+ for node in stmts: self._checkStmt(node, env)
668
+
669
+ fn _checkStmt(node, env):
670
+ if not node: return
671
+
672
+ if node.type == "VarDecl":
673
+ val declared = node.typeAnn ? parseAnnotation(node.typeAnn) : null
674
+ var actual = null
675
+ if node.init: actual = self._inferType(node.init, env)
676
+ if declared and actual and actual.kind != "unknown":
677
+ if not self._isAssignable(actual, declared):
678
+ self._err(
679
+ "Type '" + typeStr(actual) + "' is not assignable to '" + node.name + ": " + typeStr(declared) + "'",
680
+ node.loc,
681
+ "Change the value to match '" + typeStr(declared) + "' or update the annotation"
682
+ )
683
+ env.set(node.name, declared ?? actual ?? T_UNKNOWN)
684
+
685
+ else if node.type == "DestructureDecl":
686
+ val srcType = node.init ? self._inferType(node.init, env) : T_UNKNOWN
687
+ if node.patternType == "object":
688
+ for p in node.pattern:
689
+ var memberType = T_UNKNOWN
690
+ if srcType.kind == "object" and srcType.shape.has(p.key):
691
+ memberType = srcType.shape.get(p.key)
692
+ env.set(p.alias, memberType)
693
+ else:
694
+ var idx = 0
695
+ for p in node.pattern:
696
+ if p:
697
+ var elemType = T_UNKNOWN
698
+ if srcType.kind == "tuple" and srcType.types[idx]:
699
+ elemType = srcType.types[idx]
700
+ else if srcType.kind == "generic" and srcType.name == "Array":
701
+ elemType = srcType.args[0] ?? T_UNKNOWN
702
+ env.set(p.name, elemType)
703
+ idx = idx + 1
704
+
705
+ else if node.type == "FnDecl":
706
+ val retType = node.retType ? parseAnnotation(node.retType) : null
707
+ val paramTypes = node.params.map(p -> p.typeAnn ? parseAnnotation(p.typeAnn) : T_UNKNOWN)
708
+ if node.name: env.set(node.name, T_FN(paramTypes, retType ?? T_UNKNOWN))
709
+ val fnEnv = env.childFn(retType, node.async)
710
+ var i = 0
711
+ for p in node.params:
712
+ fnEnv.set(p.name, paramTypes[i])
713
+ i = i + 1
714
+ if node.inline:
715
+ val bodyType = self._inferType(node.body, fnEnv)
716
+ if retType and bodyType and bodyType.kind != "unknown":
717
+ if not self._isAssignable(bodyType, retType):
718
+ self._err(
719
+ "Function '" + (node.name ?? "(anon)") + "' returns '" + typeStr(bodyType) + "' but declared '" + typeStr(retType) + "'",
720
+ node.loc,
721
+ "Fix return type or update the annotation"
722
+ )
723
+ else:
724
+ self._checkStmts(node.body, fnEnv)
725
+
726
+ else if node.type == "ClassDecl":
727
+ env.set(node.name, T_NAMED(node.name))
728
+ val clsEnv = env.child()
729
+ val shape = new Map()
730
+ for f in node.fields:
731
+ val ft = f.typeAnn ? parseAnnotation(f.typeAnn) : T_UNKNOWN
732
+ clsEnv.set(f.name, ft)
733
+ shape.set(f.name, ft)
734
+ clsEnv.set("self", T_OBJECT(shape))
735
+ for m in node.methods: self._checkStmt(m, clsEnv)
736
+
737
+ else if node.type == "TypeDecl":
738
+ for v in node.variants:
739
+ if v.fields.length == 0:
740
+ env.set(v.name, T_NAMED(node.name))
741
+ else:
742
+ env.set(v.name, T_FN(v.fields.map(f_ -> T_ANY), T_NAMED(node.name)))
743
+
744
+ else if node.type == "InterfaceDecl":
745
+ env.set(node.name, T_NAMED(node.name))
746
+
747
+ else if node.type == "EnumDecl":
748
+ env.set(node.name, T_NAMED(node.name))
749
+
750
+ else if node.type == "IfStmt":
751
+ self._inferType(node.cond, env)
752
+ val narrowing = self._analyzeNarrowingCondition(node.cond, env)
753
+ val thenEnv = narrowing ? env.narrow(narrowing.varName, narrowing.trueType) : env.child()
754
+ val elseEnv = narrowing ? env.narrow(narrowing.varName, narrowing.falseType) : env.child()
755
+ self._checkStmts(node.then, thenEnv)
756
+ for ei in node.elseifs:
757
+ self._inferType(ei.cond, env)
758
+ self._checkStmts(ei.body, env.child())
759
+ if node.else_: self._checkStmts(node.else_, elseEnv)
760
+
761
+ else if node.type == "ForInStmt":
762
+ val iterType = self._inferType(node.iter, env)
763
+ val inner = env.child()
764
+ if iterType.kind == "generic" and iterType.name == "Array":
765
+ inner.set(node.var, iterType.args[0] ?? T_UNKNOWN)
766
+ else if iterType.kind == "tuple":
767
+ inner.set(node.var, iterType.types[0] ?? T_UNKNOWN)
768
+ else:
769
+ inner.set(node.var, T_UNKNOWN)
770
+ self._checkStmts(node.body, inner)
771
+
772
+ else if node.type == "WhileStmt" or node.type == "DoWhileStmt":
773
+ self._inferType(node.cond, env)
774
+ self._checkStmts(node.body ?? [], env.child())
775
+
776
+ else if node.type == "MatchStmt":
777
+ self._inferType(node.subject, env)
778
+ for arm in node.arms:
779
+ val inner = env.child()
780
+ if arm.pattern.type == "VariantPat":
781
+ for b in arm.pattern.bindings: inner.set(b, T_UNKNOWN)
782
+ self._checkStmts(arm.body, inner)
783
+
784
+ else if node.type == "ReturnStmt":
785
+ if node.value:
786
+ val retType = env.retType
787
+ val retActual = self._inferType(node.value, env)
788
+ if retType and retActual and retActual.kind != "unknown":
789
+ if not self._isAssignable(retActual, retType):
790
+ self._err(
791
+ "Return type '" + typeStr(retActual) + "' is not assignable to declared '" + typeStr(retType) + "'",
792
+ node.loc,
793
+ "Fix the return value or update the return type annotation"
794
+ )
795
+
796
+ else if node.type == "TryCatchStmt":
797
+ self._checkStmts(node.tryBody, env.child())
798
+ if node.catchBody:
799
+ val inner = env.child()
800
+ if node.catchParam: inner.set(node.catchParam, T_ANY)
801
+ self._checkStmts(node.catchBody, inner)
802
+ if node.finallyBody: self._checkStmts(node.finallyBody, env.child())
803
+
804
+ else if node.type == "ThrowStmt":
805
+ if node.value: self._inferType(node.value, env)
806
+
807
+ else if node.type == "ImportDecl":
808
+ if node.defaultName: env.set(node.defaultName, T_ANY)
809
+ if node.namespaceName: env.set(node.namespaceName, T_ANY)
810
+ for n in node.names:
811
+ val nm = typeof n == "string" ? n : n.alias
812
+ env.set(nm, T_ANY)
813
+
814
+ else if node.type == "ExportDecl":
815
+ if node.isDefault:
816
+ self._inferType(node.decl, env)
817
+ else:
818
+ self._checkStmt(node.decl, env)
819
+
820
+ else if node.type == "ExprStmt":
821
+ self._inferType(node.expr, env)