emit.go raw
1 // Package emit reconstructs Go source code from a lattice's bonded AST elements.
2 //
3 // The emitter reads bonded elements and projects them back into source code.
4 // The lattice's structure determines what code is emitted — it writes itself out.
5 //
6 // Body-level elements (assign, return, if, for, expr, etc.) carry their
7 // parent function name and rendered source text separated by \x00. The
8 // emitter groups these by parent to reconstruct function bodies.
9 //
10 // When body elements exist, the emitter produces actual executable code.
11 // When only declarations and literals exist (legacy mode), it falls back
12 // to self-logging skeleton code for behavioral equivalence.
13 package emit
14
15 import (
16 "crypto/rand"
17 "encoding/binary"
18 "fmt"
19 "go/parser"
20 "go/token"
21 "io"
22 "os"
23 "path/filepath"
24 "sort"
25 "strings"
26 "sync"
27
28 "git.mleku.dev/mleku/dendrite/pkg/causal"
29 "git.mleku.dev/mleku/dendrite/pkg/enzyme"
30 "git.mleku.dev/mleku/dendrite/pkg/lattice"
31 )
32
33 // Fragment is a bonded element extracted from the lattice for emission.
34 type Fragment struct {
35 NodeID lattice.NodeID
36 Type string
37 Value string
38 LockIn lattice.LockInDepth
39 }
40
41 // bodyTags are element types that represent statements inside function bodies.
42 var bodyTags = map[string]bool{
43 "assign": true, "return": true, "if": true, "for": true,
44 "switch": true, "select": true, "go": true, "send": true,
45 "expr": true, "defer": true, "decl": true, "branch": true,
46 "case": true, "comm": true,
47 }
48
49 // Harvest collects all bonded elements from a lattice, grouped by file
50 // (if file markers exist) or as a single unnamed group.
51 func Harvest(l *lattice.Lattice) map[string][]Fragment {
52 files := make(map[string][]Fragment)
53 currentFile := ""
54
55 nodes := l.Nodes()
56 sort.Slice(nodes, func(i, j int) bool {
57 return nodes[i].ID() < nodes[j].ID()
58 })
59
60 for _, n := range nodes {
61 if !n.Occupied() {
62 continue
63 }
64 e := n.Occupant()
65
66 // Skip reference material — these elements influence lattice
67 // topology during growth but should not appear in emitted output.
68 if enzyme.IsRef(e) {
69 continue
70 }
71
72 val := ""
73 if e.Value() != nil {
74 val = fmt.Sprintf("%v", e.Value())
75 }
76
77 if e.Type() == "file" {
78 currentFile = val
79 continue
80 }
81
82 // Grammar rules are structural context, not emittable code.
83 if e.Type() == "grammar-rule" {
84 continue
85 }
86
87 f := Fragment{
88 NodeID: n.ID(),
89 Type: e.Type(),
90 Value: val,
91 LockIn: n.LockIn(),
92 }
93 files[currentFile] = append(files[currentFile], f)
94 }
95
96 return files
97 }
98
99 // bodyStmt holds a parsed body-level statement with its parent function.
100 type bodyStmt struct {
101 Parent string
102 Source string
103 LineNum int // original source line number (0 = unknown)
104 NodeID lattice.NodeID
105 LockIn lattice.LockInDepth
106 Tag string
107 }
108
109 // parseBodyValue splits "parent\x00source" from a body element's value.
110 // Returns empty parent if no separator found (legacy element).
111 // Used for fields, directives, and other two-part values.
112 func parseBodyValue(val string) (parent, source string) {
113 idx := strings.IndexByte(val, '\x00')
114 if idx < 0 {
115 return "", val
116 }
117 return val[:idx], val[idx+1:]
118 }
119
120 // parseBodyStmt splits body element values in two supported formats:
121 // - Three-part: "parent\x00linenum\x00source" (new: carries source position)
122 // - Two-part: "parent\x00source" (legacy: no position info)
123 //
124 // Returns empty parent if no separator found.
125 func parseBodyStmt(val string) (parent string, lineNum int, source string) {
126 idx := strings.IndexByte(val, '\x00')
127 if idx < 0 {
128 return "", 0, val
129 }
130 rest := val[idx+1:]
131 parent = val[:idx]
132
133 // Check for three-part format: is there another \x00?
134 idx2 := strings.IndexByte(rest, '\x00')
135 if idx2 >= 0 {
136 // Three-part: parent\x00linenum\x00source
137 lineStr := rest[:idx2]
138 source = rest[idx2+1:]
139 ln := 0
140 for _, ch := range lineStr {
141 if ch >= '0' && ch <= '9' {
142 ln = ln*10 + int(ch-'0')
143 }
144 }
145 return parent, ln, source
146 }
147
148 // Two-part: parent\x00source (legacy)
149 return parent, 0, rest
150 }
151
152 // funcDecl holds information about a function or method declaration.
153 type funcDecl struct {
154 Name string // bare name for matching body statements
155 Signature string // full value from the enzyme (e.g., "main()" or "Foo.Hello() string")
156 IsMethod bool
157 RecvVar string // receiver variable name (e.g., "c", "e") — empty means use "x"
158 NodeID lattice.NodeID
159 }
160
161 // parseFuncName extracts the bare function name from a func/method value.
162 // "main()" → "main", "Foo.Hello() string" → "Hello"
163 func parseFuncName(val string, isMethod bool) string {
164 name := val
165 if isMethod {
166 // "Foo.Hello() string" → "Hello() string"
167 if dot := strings.IndexByte(name, '.'); dot >= 0 {
168 name = name[dot+1:]
169 }
170 }
171 // "main()" → "main"
172 if paren := strings.IndexByte(name, '('); paren >= 0 {
173 name = name[:paren]
174 }
175 return name
176 }
177
178 // EmitGo writes reconstructed Go source from harvested fragments.
179 // Uses "main" as the package name. See EmitGoPackage for arbitrary packages.
180 func EmitGo(fragments []Fragment, w io.Writer) error {
181 return EmitGoPackage(fragments, w, "main")
182 }
183
184 // EmitGoPackage writes reconstructed Go source from harvested fragments
185 // using the specified package name.
186 //
187 // If body elements (assign, return, if, expr, etc.) are present, the emitter
188 // reconstructs actual function bodies from the rendered source text each
189 // element carries. Otherwise it falls back to self-logging skeleton code.
190 func EmitGoPackage(fragments []Fragment, w io.Writer, pkgName string) error {
191 // Separate fragments by role.
192 var (
193 funcDecls []funcDecl
194 structDecls []Fragment // type+struct pairs
195 ifaceDecls []Fragment // type+interface pairs
196 typeDecls []Fragment // standalone types
197 fields []Fragment
198 imports []Fragment
199 bodyStmts []bodyStmt
200 literals []Fragment
201 directives []Fragment
202 varDecls []Fragment // top-level var/const declarations
203 )
204
205 // Track struct and interface names from their dedicated elements.
206 structNames := make(map[string]bool)
207 ifaceNames := make(map[string]bool)
208
209 for _, f := range fragments {
210 switch f.Type {
211 case "func":
212 name := parseFuncName(f.Value, false)
213 funcDecls = append(funcDecls, funcDecl{
214 Name: name, Signature: f.Value, IsMethod: false, NodeID: f.NodeID,
215 })
216 case "method":
217 methodVal := f.Value
218 recvVar := ""
219 // Method value format: "recvVar\x00RecvType.MethodName(...) returns"
220 if idx := strings.IndexByte(methodVal, '\x00'); idx >= 0 {
221 recvVar = methodVal[:idx]
222 methodVal = methodVal[idx+1:]
223 }
224 name := parseFuncName(methodVal, true)
225 funcDecls = append(funcDecls, funcDecl{
226 Name: name, Signature: methodVal, IsMethod: true,
227 RecvVar: recvVar, NodeID: f.NodeID,
228 })
229 case "struct":
230 structDecls = append(structDecls, f)
231 structNames[f.Value] = true
232 case "interface":
233 ifaceDecls = append(ifaceDecls, f)
234 ifaceNames[f.Value] = true
235 case "type":
236 typeDecls = append(typeDecls, f)
237 case "field":
238 fields = append(fields, f)
239 case "import":
240 imports = append(imports, f)
241 case "directive":
242 directives = append(directives, f)
243 case "var":
244 varDecls = append(varDecls, f)
245 default:
246 if strings.HasPrefix(f.Type, "literal:") {
247 literals = append(literals, f)
248 } else if strings.HasPrefix(f.Type, "ident:") {
249 // Ident subtypes contribute to naming but aren't directly emitted.
250 } else if bodyTags[f.Type] {
251 parent, lineNum, source := parseBodyStmt(f.Value)
252 if source != "" {
253 bodyStmts = append(bodyStmts, bodyStmt{
254 Parent: parent,
255 Source: source,
256 LineNum: lineNum,
257 NodeID: f.NodeID,
258 LockIn: f.LockIn,
259 Tag: f.Type,
260 })
261 }
262 }
263 }
264 }
265
266 // Group body statements by parent function, maintaining node ID order.
267 sort.Slice(bodyStmts, func(i, j int) bool {
268 return bodyStmts[i].NodeID < bodyStmts[j].NodeID
269 })
270 funcBodies := make(map[string][]bodyStmt)
271 for _, bs := range bodyStmts {
272 funcBodies[bs.Parent] = append(funcBodies[bs.Parent], bs)
273 }
274
275 // Determine if we have real body content or need legacy self-logging.
276 hasBodyContent := len(bodyStmts) > 0
277
278 // === Build the source ===
279 var b strings.Builder
280 // Always use the caller's package name. When ingesting multi-package
281 // source (e.g. -self), fragments contain package elements from every
282 // file parsed ("axiom", "enzyme", "ed25519", etc). Using those would
283 // produce a non-main package that can't compile as a binary.
284 fmt.Fprintf(&b, "package %s\n\n", pkgName)
285
286 // Determine imports (with alias resolution for name collisions).
287 // Build a set of known package names for reference validation.
288 knownPkgs := make(map[string]bool)
289 if hasBodyContent {
290 neededImports := inferImports(bodyStmts, imports, funcDecls, varDecls, fields)
291 neededImports = aliasCollisions(neededImports)
292 for _, imp := range neededImports {
293 knownPkgs[extractPkgName(imp)] = true
294 }
295 if len(neededImports) > 0 {
296 b.WriteString("import (\n")
297 for _, imp := range neededImports {
298 fmt.Fprintf(&b, "\t%s\n", imp)
299 }
300 b.WriteString(")\n\n")
301 }
302 } else {
303 needsFmt := hasOutputLiterals(literals)
304 if needsFmt {
305 b.WriteString("import \"fmt\"\n\n")
306 knownPkgs["fmt"] = true
307 }
308 }
309
310 // Top-level var/const declarations (with associated directives).
311 // Validate each declaration: must parse, and must not reference
312 // unknown packages (e.g., "hash.Len" when "hash" isn't imported).
313 emittedVars := make(map[string]bool)
314 emittedNames := make(map[string]bool) // track declared names for duplicate detection
315 for _, v := range varDecls {
316 if v.Value == "" || emittedVars[v.Value] {
317 continue
318 }
319 if !isValidDecl(v.Value) {
320 continue
321 }
322 name := extractVarName(v.Value)
323 if emittedNames[name] {
324 continue // duplicate declaration name
325 }
326 if hasUndefinedRefs(v.Value, knownPkgs) {
327 continue
328 }
329 emittedVars[v.Value] = true
330 emittedNames[name] = true
331 emitDirectives(&b, directives, name)
332 fmt.Fprintf(&b, "%s\n\n", v.Value)
333 }
334
335 // Type declarations.
336 emittedTypes := make(map[string]bool)
337 ifaceAliases := make(map[string]bool) // types emitted as "= interface{}"
338 for _, t := range typeDecls {
339 name := t.Value
340 if name == "" || emittedTypes[name] || emittedNames[name] {
341 continue
342 }
343 emittedTypes[name] = true
344
345 // Emit any directives associated with this type.
346 emitDirectives(&b, directives, name)
347
348 if structNames[name] {
349 fmt.Fprintf(&b, "type %s struct {\n", name)
350 for _, f := range fields {
351 parent, fieldDef := parseBodyValue(f.Value)
352 if parent != name {
353 continue // skip fields from other structs
354 }
355 if fieldDef == "" {
356 continue
357 }
358 fmt.Fprintf(&b, "\t%s\n", fieldDef)
359 }
360 b.WriteString("}\n\n")
361 } else if ifaceNames[name] {
362 fmt.Fprintf(&b, "type %s interface {\n", name)
363 // Only include methods whose receiver type matches this
364 // interface name (or has no body — interface method stubs).
365 seen := make(map[string]bool)
366 for _, fd := range funcDecls {
367 if !fd.IsMethod {
368 continue
369 }
370 // Extract receiver type from "RecvType.MethodName(...) returns"
371 sig := fd.Signature
372 recv := ""
373 if dot := strings.IndexByte(sig, '.'); dot >= 0 {
374 recv = sig[:dot]
375 sig = sig[dot+1:]
376 }
377 // Only include if receiver matches this interface, or
378 // there's no body (could be an interface method stub).
379 if recv != name && recv != "*"+name {
380 continue
381 }
382 if _, hasBod := funcBodies[fd.Name]; hasBod {
383 continue
384 }
385 if seen[fd.Name] {
386 continue
387 }
388 seen[fd.Name] = true
389 fmt.Fprintf(&b, "\t%s\n", sig)
390 }
391 b.WriteString("}\n\n")
392 } else {
393 // Type with unknown underlying definition — alias to interface{}.
394 // Track these so we can skip methods with this receiver type
395 // (interface aliases can't be method receivers).
396 ifaceAliases[name] = true
397 fmt.Fprintf(&b, "type %s = interface{}\n\n", name)
398 }
399 }
400
401 // Functions and methods — with actual bodies when available.
402 // Validate signatures before emitting to avoid garbled output.
403 emittedFuncs := make(map[string]bool)
404 for _, fd := range funcDecls {
405 if emittedFuncs[fd.Name] {
406 continue
407 }
408 // Skip non-method functions whose name clashes with a type or var/const.
409 // Go doesn't allow a function and type/var with the same name in one package.
410 if !fd.IsMethod && (emittedTypes[fd.Name] || emittedNames[fd.Name]) {
411 continue
412 }
413
414 var funcLine string
415 var returnSig string // portion after ')' for zero-value return generation
416
417 if fd.IsMethod {
418 sig := fd.Signature
419 recv := ""
420 rest := sig
421 if dot := strings.IndexByte(sig, '.'); dot >= 0 {
422 recv = sig[:dot]
423 rest = sig[dot+1:]
424 }
425 if !strings.Contains(rest, "(") {
426 continue
427 }
428 if recv != "" && !isValidReceiver(recv) {
429 continue
430 }
431 // Skip methods on interface-aliased types (can't have receivers).
432 bareRecv := strings.TrimPrefix(recv, "*")
433 if ifaceAliases[bareRecv] {
434 continue
435 }
436 // Skip if signature references undefined packages.
437 if hasUndefinedRefs(rest, knownPkgs) {
438 continue
439 }
440 if recv != "" {
441 rv := fd.RecvVar
442 if rv == "" {
443 rv = "x"
444 }
445 funcLine = fmt.Sprintf("func (%s %s) %s {\n", rv, recv, rest)
446 } else {
447 funcLine = fmt.Sprintf("func %s {\n", rest)
448 }
449 returnSig = extractReturnSig(rest)
450 } else {
451 if !strings.Contains(fd.Signature, "(") {
452 continue
453 }
454 if hasUndefinedRefs(fd.Signature, knownPkgs) {
455 continue
456 }
457 funcLine = fmt.Sprintf("func %s {\n", fd.Signature)
458 returnSig = extractReturnSig(fd.Signature)
459 }
460
461 emittedFuncs[fd.Name] = true
462 emitDirectives(&b, directives, fd.Name)
463 b.WriteString(funcLine)
464
465 hasBody := false
466 if stmts, ok := funcBodies[fd.Name]; ok && hasBodyContent {
467 emitBody(&b, stmts)
468 hasBody = len(stmts) > 0
469 } else if !hasBodyContent && fd.Name == "main" {
470 emitLegacyMain(&b, literals, typeDecls, funcDecls)
471 hasBody = true
472 }
473
474 // Add zero-value return for functions with return types and no body.
475 if !hasBody && returnSig != "" {
476 zr := zeroReturn(returnSig)
477 if zr != "" {
478 fmt.Fprintf(&b, "\t%s\n", zr)
479 }
480 }
481 b.WriteString("}\n\n")
482 }
483
484 // Ensure main exists (only for package main).
485 if pkgName == "main" && !emittedFuncs["main"] {
486 b.WriteString("func main() {\n")
487 if stmts, ok := funcBodies["main"]; ok {
488 emitBody(&b, stmts)
489 } else if !hasBodyContent {
490 emitLegacyMain(&b, literals, typeDecls, funcDecls)
491 }
492 b.WriteString("}\n\n")
493 }
494
495 // Post-process: try to parse with go/parser and format with go/format.
496 // If parsing succeeds, emit the formatted version (fixes import ordering,
497 // whitespace). If it fails, emit the raw version — the compiler will
498 // report specific errors that feed back into fitness.
499 source := b.String()
500 if formatted, err := sanitizeGoSource(source); err == nil {
501 source = formatted
502 }
503
504 _, err := io.WriteString(w, source)
505 return err
506 }
507
508 // EmitProject reconstructs a multi-file Go module from harvested fragments.
509 // It emits source files and a go.mod with replace directives for all
510 // internal packages. Returns filepath → source code.
511 func EmitProject(files map[string][]Fragment, modPath string) map[string]string {
512 result := make(map[string]string)
513 internalPkgs := make(map[string]bool)
514
515 for filename, frags := range files {
516 // Determine package name from fragments.
517 pkgName := "main"
518 for _, f := range frags {
519 if f.Type == "package" {
520 pkgName = f.Value
521 break
522 }
523 }
524
525 var buf strings.Builder
526 EmitGoPackage(frags, &buf, pkgName)
527
528 // Use filename if provided, otherwise derive from package name.
529 outName := filename
530 if outName == "" {
531 outName = pkgName + ".go"
532 }
533 result[outName] = buf.String()
534
535 // Track subpackage directories for go.mod replace directives.
536 if dir := filepath.Dir(outName); dir != "." && dir != "" {
537 internalPkgs[dir] = true
538 }
539 }
540
541 // Emit go.mod with replace directives.
542 result["go.mod"] = emitGoMod(modPath, internalPkgs)
543 return result
544 }
545
546 // emitGoMod generates a go.mod file with replace directives for all
547 // internal subpackages. All imports resolve locally — no network access.
548 func emitGoMod(modPath string, internalPkgs map[string]bool) string {
549 var b strings.Builder
550 fmt.Fprintf(&b, "module %s\n\ngo 1.24\n", modPath)
551
552 if len(internalPkgs) > 0 {
553 var pkgs []string
554 for pkg := range internalPkgs {
555 pkgs = append(pkgs, pkg)
556 }
557 sort.Strings(pkgs)
558
559 b.WriteString("\nreplace (\n")
560 for _, pkg := range pkgs {
561 fmt.Fprintf(&b, "\t%s/%s => ./%s\n", modPath, pkg, pkg)
562 }
563 b.WriteString(")\n")
564 }
565 return b.String()
566 }
567
568 // emitBody writes the body statements for a function.
569 // It deduplicates, filters compound sub-statements, orders by
570 // dependency DAG, and shuffles non-dependent statements for
571 // anti-fingerprinting.
572 func emitBody(b *strings.Builder, stmts []bodyStmt) {
573 // Deduplicate.
574 var unique []bodyStmt
575 seen := make(map[string]bool)
576 for _, s := range stmts {
577 if seen[s.Source] {
578 continue
579 }
580 seen[s.Source] = true
581 unique = append(unique, s)
582 }
583
584 // Remove inner statements that are contained within compound statements.
585 // Use line numbers when available: a statement is "inner" if its line
586 // number falls within the line range of a compound (multi-line) statement.
587 // Falls back to substring containment when line numbers are absent.
588 // Also filters case/comm clauses that belong inside a switch/select.
589 compoundTags := map[string]bool{"if": true, "for": true, "switch": true, "select": true}
590 innerTags := map[string]bool{"case": true, "comm": true}
591 var filtered []bodyStmt
592 for i, s := range unique {
593 contained := false
594
595 // Case/comm clauses are always inner to switch/select — filter them
596 // if any enclosing compound statement exists.
597 if innerTags[s.Tag] {
598 for j, other := range unique {
599 if i == j {
600 continue
601 }
602 if (s.Tag == "case" && (other.Tag == "switch")) ||
603 (s.Tag == "comm" && other.Tag == "select") {
604 // Check containment by line range or substring.
605 if s.LineNum > 0 && other.LineNum > 0 {
606 otherLines := strings.Count(other.Source, "\n") + 1
607 if s.LineNum > other.LineNum && s.LineNum <= other.LineNum+otherLines-1 {
608 contained = true
609 break
610 }
611 }
612 if strings.Contains(other.Source, s.Source) {
613 contained = true
614 break
615 }
616 }
617 }
618 }
619
620 // Line-number-based filtering for other inner statements.
621 if !contained && s.LineNum > 0 {
622 for j, other := range unique {
623 if i == j || !compoundTags[other.Tag] || other.LineNum == 0 {
624 continue
625 }
626 otherLines := strings.Count(other.Source, "\n") + 1
627 otherEnd := other.LineNum + otherLines - 1
628 if s.LineNum > other.LineNum && s.LineNum <= otherEnd {
629 contained = true
630 break
631 }
632 }
633 }
634
635 // Fallback: substring containment (original heuristic).
636 if !contained {
637 for j, other := range unique {
638 if i != j && len(other.Source) > len(s.Source) && strings.Contains(other.Source, s.Source) {
639 contained = true
640 break
641 }
642 }
643 }
644
645 if !contained {
646 filtered = append(filtered, s)
647 }
648 }
649
650 // Strip orphan break/continue/fallthrough — these are branch statements
651 // from loop/switch bodies that got placed at the top level of a function.
652 // They're only valid inside for/switch/select.
653 hasLoop := false
654 for _, s := range filtered {
655 if s.Tag == "for" || s.Tag == "switch" || s.Tag == "select" {
656 hasLoop = true
657 break
658 }
659 if strings.HasPrefix(strings.TrimSpace(s.Source), "for ") ||
660 strings.HasPrefix(strings.TrimSpace(s.Source), "switch ") ||
661 strings.HasPrefix(strings.TrimSpace(s.Source), "select {") {
662 hasLoop = true
663 break
664 }
665 }
666 if !hasLoop {
667 var clean []bodyStmt
668 for _, s := range filtered {
669 trimmed := strings.TrimSpace(s.Source)
670 if trimmed == "break" || trimmed == "continue" || trimmed == "fallthrough" ||
671 strings.HasPrefix(trimmed, "break ") || strings.HasPrefix(trimmed, "continue ") ||
672 s.Tag == "branch" {
673 continue
674 }
675 clean = append(clean, s)
676 }
677 filtered = clean
678 }
679
680 // Remove duplicate declarations within the function scope.
681 // Go allows re-declaration with := when at least one variable is new,
682 // but duplicate definitions are a common source of compile errors in
683 // emitted code. Keep the first definition, drop subsequent ones.
684 {
685 declaredIds := make(map[string]bool)
686 var deduped []bodyStmt
687 for _, s := range filtered {
688 defs := causal.ExtractDefines(s.Source)
689 if len(defs) == 0 {
690 deduped = append(deduped, s)
691 continue
692 }
693 conflict := false
694 for id := range defs {
695 if declaredIds[id] {
696 conflict = true
697 break
698 }
699 }
700 if conflict {
701 continue // drop duplicate declaration
702 }
703 for id := range defs {
704 declaredIds[id] = true
705 }
706 deduped = append(deduped, s)
707 }
708 filtered = deduped
709 }
710
711 // Order by dependency DAG with non-dependent shuffling.
712 ordered := orderBody(filtered)
713
714 for _, s := range ordered {
715 sublines := strings.Split(s.Source, "\n")
716 for _, sl := range sublines {
717 fmt.Fprintf(b, "\t%s\n", sl)
718 }
719 }
720 }
721
722 // orderBody builds a dependency DAG from define-use analysis,
723 // topologically sorts into levels, and crypto/rand shuffles
724 // within each level for anti-fingerprinting.
725 func orderBody(stmts []bodyStmt) []bodyStmt {
726 if len(stmts) <= 1 {
727 return stmts
728 }
729
730 n := len(stmts)
731
732 // Extract defines and uses for each statement.
733 defines := make([]map[string]bool, n)
734 uses := make([]map[string]bool, n)
735 for i, s := range stmts {
736 defines[i] = causal.ExtractDefines(s.Source)
737 uses[i] = causal.ExtractUses(s.Source)
738 }
739
740 // Build dependency graph: deps[j] contains indices that j depends on.
741 deps := make([][]int, n)
742 for j := range n {
743 for i := range n {
744 if i == j {
745 continue
746 }
747 // j depends on i if j uses something i defines.
748 for id := range uses[j] {
749 if defines[i][id] {
750 deps[j] = append(deps[j], i)
751 break
752 }
753 }
754 }
755 }
756
757 // Topological sort into levels.
758 levels := topoLevels(n, deps)
759
760 // Sort within each level by source line number when available,
761 // falling back to crypto/rand shuffle when line numbers are absent.
762 for _, level := range levels {
763 hasLineNums := false
764 for _, idx := range level {
765 if stmts[idx].LineNum > 0 {
766 hasLineNums = true
767 break
768 }
769 }
770 if hasLineNums {
771 sort.Slice(level, func(a, b int) bool {
772 la, lb := stmts[level[a]].LineNum, stmts[level[b]].LineNum
773 if la != lb {
774 return la < lb
775 }
776 return level[a] < level[b]
777 })
778 } else {
779 cryptoShuffle(level)
780 }
781 }
782
783 // Flatten levels into ordered result.
784 result := make([]bodyStmt, 0, n)
785 for _, level := range levels {
786 for _, idx := range level {
787 result = append(result, stmts[idx])
788 }
789 }
790 return result
791 }
792
793 // topoLevels performs topological sort and groups nodes into levels.
794 // Level 0 has no dependencies, level 1 depends only on level 0, etc.
795 func topoLevels(n int, deps [][]int) [][]int {
796 // Compute in-degree.
797 inDeg := make([]int, n)
798 for j := range n {
799 inDeg[j] = len(deps[j])
800 }
801
802 // Collect level 0 (no dependencies).
803 var levels [][]int
804 remaining := make([]bool, n)
805 for i := range n {
806 remaining[i] = true
807 }
808
809 for {
810 var level []int
811 for i := range n {
812 if !remaining[i] {
813 continue
814 }
815 // Check if all dependencies are already placed.
816 allResolved := true
817 for _, dep := range deps[i] {
818 if remaining[dep] {
819 allResolved = false
820 break
821 }
822 }
823 if allResolved {
824 level = append(level, i)
825 }
826 }
827 if len(level) == 0 {
828 // Remaining nodes have circular dependencies.
829 // Add them in original order.
830 for i := range n {
831 if remaining[i] {
832 level = append(level, i)
833 }
834 }
835 levels = append(levels, level)
836 break
837 }
838 levels = append(levels, level)
839 for _, idx := range level {
840 remaining[idx] = false
841 }
842 }
843 return levels
844 }
845
846 // cryptoShuffle performs Fisher-Yates shuffle using crypto/rand.
847 func cryptoShuffle(indices []int) {
848 for i := len(indices) - 1; i > 0; i-- {
849 var buf [8]byte
850 rand.Read(buf[:])
851 j := int(binary.LittleEndian.Uint64(buf[:]) % uint64(i+1))
852 indices[i], indices[j] = indices[j], indices[i]
853 }
854 }
855
856 // modulePath is the self-import prefix. Imports matching this are trusted.
857 const modulePath = "git.mleku.dev/mleku/dendrite"
858
859 // moduleRootOnce lazily finds the module root directory on disk.
860 var (
861 moduleRootOnce sync.Once
862 moduleRootDir string // absolute path to the dendrite module root, or ""
863 subPkgCache map[string]bool // cache: directory name → exists as sub-package
864 )
865
866 // findModuleRootDir locates the dendrite module root by walking up from cwd
867 // looking for a go.mod that declares the module.
868 func findModuleRootDir() string {
869 dir, err := os.Getwd()
870 if err != nil {
871 return ""
872 }
873 for {
874 modFile := filepath.Join(dir, "go.mod")
875 data, err := os.ReadFile(modFile)
876 if err == nil && strings.Contains(string(data), "module "+modulePath) {
877 return dir
878 }
879 parent := filepath.Dir(dir)
880 if parent == dir {
881 return "" // reached filesystem root
882 }
883 dir = parent
884 }
885 }
886
887 // isValidSubPkg checks if w is an actual sub-package directory in the dendrite
888 // module that contains Go source files (not just sub-directories).
889 // Caches results after the first filesystem scan.
890 func isValidSubPkg(w string) bool {
891 moduleRootOnce.Do(func() {
892 moduleRootDir = findModuleRootDir()
893 subPkgCache = make(map[string]bool)
894 if moduleRootDir != "" {
895 entries, err := os.ReadDir(filepath.Join(moduleRootDir, "pkg"))
896 if err == nil {
897 for _, e := range entries {
898 if !e.IsDir() || strings.HasPrefix(e.Name(), ".") || strings.HasPrefix(e.Name(), "_") {
899 continue
900 }
901 // Only count as a package if it contains .go files.
902 subEntries, err := os.ReadDir(filepath.Join(moduleRootDir, "pkg", e.Name()))
903 if err != nil {
904 continue
905 }
906 for _, se := range subEntries {
907 if !se.IsDir() && strings.HasSuffix(se.Name(), ".go") {
908 subPkgCache[e.Name()] = true
909 break
910 }
911 }
912 }
913 }
914 }
915 })
916 return subPkgCache[w]
917 }
918
919 // inferImports determines which imports are needed based on body statements,
920 // function signatures, var declarations, and field types. Three passes:
921 // 1. Collect candidates (explicit from lattice + inferred from all text)
922 // 2. Trust filter: reject anything that isn't stdlib or a self-import
923 // 3. Usage prune: drop imports whose package name doesn't appear in text
924 func inferImports(stmts []bodyStmt, importFrags []Fragment, funcs []funcDecl, vars []Fragment, fields []Fragment) []string {
925 // Pass 1: Collect candidates.
926 candidates := make(map[string]bool)
927
928 // Explicit imports from the lattice.
929 for _, imp := range importFrags {
930 candidates[imp.Value] = true
931 }
932
933 // Collect ALL text that may reference packages: body statements,
934 // function signatures, var declarations, and struct field types.
935 allText := &strings.Builder{}
936 for _, s := range stmts {
937 allText.WriteString(s.Source)
938 allText.WriteByte('\n')
939 }
940 for _, fd := range funcs {
941 allText.WriteString(fd.Signature)
942 allText.WriteByte('\n')
943 }
944 for _, v := range vars {
945 allText.WriteString(v.Value)
946 allText.WriteByte('\n')
947 }
948 for _, f := range fields {
949 allText.WriteString(f.Value)
950 allText.WriteByte('\n')
951 }
952 text := allText.String()
953
954 // Infer stdlib imports from body text.
955 // Only single-segment packages belong here. Multi-segment paths
956 // like "path/filepath" and "os/exec" are in multiPkgs below.
957 stdPkgs := map[string]string{
958 "fmt": "fmt.", "os": "os.", "io": "io.",
959 "strings": "strings.", "strconv": "strconv.",
960 "log": "log.", "time": "time.", "sync": "sync.",
961 "context": "context.", "math": "math.", "sort": "sort.",
962 "bytes": "bytes.", "errors": "errors.", "path": "path.",
963 "net": "net.", "regexp": "regexp.", "reflect": "reflect.",
964 "testing": "testing.", "embed": "embed.",
965 "bufio": "bufio.", "unicode": "unicode.",
966 "crypto": "crypto.", "hash": "hash.", "encoding": "encoding.",
967 "flag": "flag.", "slices": "slices.", "maps": "maps.",
968 "unsafe": "unsafe.",
969 }
970 // Multi-segment stdlib paths — package name differs from import path.
971 multiPkgs := map[string]string{
972 "net/http": "http.",
973 "encoding/json": "json.",
974 "encoding/hex": "hex.",
975 "encoding/binary": "binary.",
976 "os/exec": "exec.",
977 "os/signal": "signal.",
978 "path/filepath": "filepath.",
979 "crypto/rand": "rand.",
980 "crypto/sha256": "sha256.",
981 "crypto/ed25519": "ed25519.",
982 "go/ast": "ast.",
983 "go/parser": "parser.",
984 "go/token": "token.",
985 "go/format": "format.",
986 "go/printer": "printer.",
987 "math/big": "big.",
988 "math/rand": "rand.",
989 "io/fs": "fs.",
990 }
991
992 for pkg, marker := range stdPkgs {
993 if strings.Contains(text, marker) {
994 candidates[`"`+pkg+`"`] = true
995 }
996 }
997 for path, marker := range multiPkgs {
998 if strings.Contains(text, marker) {
999 candidates[`"`+path+`"`] = true
1000 }
1001 }
1002
1003 // Infer self-module sub-package imports from body text.
1004 // If the body uses "ratio.New(...)" and "ratio" is an actual sub-package
1005 // directory, infer "git.mleku.dev/mleku/dendrite/pkg/ratio" as a candidate.
1006 // Validates against the filesystem to avoid false positives from struct
1007 // field access (e.g., "score.Behav.Float64()" where Behav is a field).
1008 knownStdPkg := make(map[string]bool)
1009 for pkg := range stdPkgs {
1010 knownStdPkg[pkg] = true
1011 }
1012 for _, marker := range multiPkgs {
1013 knownStdPkg[strings.TrimSuffix(marker, ".")] = true
1014 }
1015 words := identifiersInText(text)
1016 for _, w := range words {
1017 if knownStdPkg[w] {
1018 continue
1019 }
1020 if !strings.Contains(text, w+".") {
1021 continue
1022 }
1023 // Must be an actual sub-package directory on disk.
1024 if !isValidSubPkg(w) {
1025 continue
1026 }
1027 candidate := `"` + modulePath + "/pkg/" + w + `"`
1028 if !candidates[candidate] {
1029 candidates[candidate] = true
1030 }
1031 }
1032
1033 // Pass 2+3: Trust filter + usage prune.
1034 var result []string
1035 for imp := range candidates {
1036 bare := strings.Trim(imp, `"`)
1037
1038 // Trust check: only stdlib and self-imports allowed.
1039 if !isStdlib(bare) && !strings.HasPrefix(bare, modulePath) {
1040 continue // untrusted external — drop
1041 }
1042
1043 // Usage check: package name must appear as "pkg." in body text.
1044 pkgName := extractPkgName(imp)
1045 if strings.Contains(text, pkgName+".") {
1046 result = append(result, imp)
1047 }
1048 }
1049 sort.Strings(result)
1050 return result
1051 }
1052
1053 // isStdlib returns true if the import path is a Go standard library package.
1054 // stdlib paths have no dots in the first path segment.
1055 func isStdlib(importPath string) bool {
1056 first := importPath
1057 if idx := strings.IndexByte(importPath, '/'); idx >= 0 {
1058 first = importPath[:idx]
1059 }
1060 return !strings.Contains(first, ".")
1061 }
1062
1063 // extractPkgName returns the package name from an import path.
1064 // "fmt" → "fmt", "net/http" → "http", "git.mleku.dev/mleku/dendrite/pkg/axiom" → "axiom"
1065 func extractPkgName(importPath string) string {
1066 bare := strings.Trim(importPath, `"`)
1067 if idx := strings.LastIndex(bare, "/"); idx >= 0 {
1068 return bare[idx+1:]
1069 }
1070 return bare
1071 }
1072
1073 // hasOutputLiterals checks if any literal fragments look like program output.
1074 func hasOutputLiterals(literals []Fragment) bool {
1075 for _, lit := range literals {
1076 cat, _ := classifyLiteral(lit.Value)
1077 if cat != "" {
1078 return true
1079 }
1080 }
1081 return false
1082 }
1083
1084 // emitLegacyMain writes the self-logging main body (legacy mode).
1085 // Used when no body elements are available.
1086 func emitLegacyMain(b *strings.Builder, literals []Fragment, types []Fragment, funcs []funcDecl) {
1087 typeNames := dedupFragNames(types)
1088 funcNames := make([]string, 0)
1089 methodNames := make([]string, 0)
1090 for _, fd := range funcs {
1091 if fd.IsMethod {
1092 methodNames = append(methodNames, fd.Name)
1093 } else {
1094 funcNames = append(funcNames, fd.Name)
1095 }
1096 }
1097
1098 type scoredLiteral struct {
1099 bare string
1100 cat string
1101 lockIn lattice.LockInDepth
1102 }
1103 var scored []scoredLiteral
1104 seen := make(map[string]bool)
1105 for _, f := range literals {
1106 cat, bare := classifyLiteral(f.Value)
1107 if cat == "" || seen[bare] {
1108 continue
1109 }
1110 seen[bare] = true
1111 scored = append(scored, scoredLiteral{bare, cat, f.LockIn})
1112 }
1113 sort.Slice(scored, func(i, j int) bool {
1114 return scored[j].lockIn.Less(scored[i].lockIn)
1115 })
1116
1117 if len(scored) == 0 {
1118 return
1119 }
1120
1121 b.WriteString("\t// Self-knowledge: the offspring counts its own structure.\n")
1122 fmt.Fprintf(b, "\tnTypes := %d\n", len(typeNames))
1123 fmt.Fprintf(b, "\tnFuncs := %d\n", len(funcNames))
1124 fmt.Fprintf(b, "\tnMethods := %d\n", len(methodNames))
1125 b.WriteString("\tnTotal := nTypes + nFuncs + nMethods\n")
1126 b.WriteString("\t_ = nTotal\n")
1127
1128 for _, lit := range scored {
1129 switch lit.cat {
1130 case "format":
1131 verbs := parseFormatVerbs(lit.bare)
1132 if len(verbs) == 0 {
1133 stripped := stripEscapes(lit.bare)
1134 if stripped != "" {
1135 fmt.Fprintf(b, "\tfmt.Println(%q)\n", stripped)
1136 }
1137 continue
1138 }
1139 args := make([]string, len(verbs))
1140 intArgIdx := 0
1141 intArgs := []string{"nTypes", "nFuncs", "nMethods", "nTotal"}
1142 for i, v := range verbs {
1143 switch v.verb {
1144 case 'd':
1145 args[i] = intArgs[intArgIdx%len(intArgs)]
1146 intArgIdx++
1147 case 's':
1148 if i < len(typeNames) {
1149 args[i] = fmt.Sprintf("%q", typeNames[i])
1150 } else if i < len(funcNames) {
1151 args[i] = fmt.Sprintf("%q", funcNames[i%len(funcNames)])
1152 } else {
1153 args[i] = `""`
1154 }
1155 case 'f':
1156 args[i] = "float64(nTypes) / float64(nTotal+1) * 100"
1157 case 'v':
1158 args[i] = "nTotal"
1159 case 'p':
1160 args[i] = "0"
1161 default:
1162 args[i] = "nTotal"
1163 }
1164 }
1165 fmtStr := cleanFormatString(lit.bare)
1166 fmt.Fprintf(b, "\tfmt.Printf(\"%s\", %s)\n", fmtStr, strings.Join(args, ", "))
1167 case "output":
1168 stripped := stripEscapes(lit.bare)
1169 if stripped != "" {
1170 fmt.Fprintf(b, "\tfmt.Println(%q)\n", stripped)
1171 }
1172 }
1173 }
1174 }
1175
1176 // isOutputLiteral returns true if a string literal looks like program output.
1177 func isOutputLiteral(bare string) bool {
1178 if bare == "" {
1179 return false
1180 }
1181 if strings.Contains(bare, "/") {
1182 return false
1183 }
1184 if len(bare) <= 2 {
1185 return false
1186 }
1187 if !strings.ContainsAny(bare, " :=,.(){}[]!?%") && len(bare) < 20 {
1188 return false
1189 }
1190 if strings.HasPrefix(bare, "json:") || strings.HasPrefix(bare, "yaml:") {
1191 return false
1192 }
1193 return true
1194 }
1195
1196 // classifyLiteral categorizes a string literal for emission.
1197 func classifyLiteral(raw string) (category string, bare string) {
1198 if !strings.HasPrefix(raw, `"`) && !strings.HasPrefix(raw, "`") {
1199 return "", ""
1200 }
1201 bare = strings.Trim(raw, `"`+"`")
1202 if !isOutputLiteral(bare) {
1203 return "", ""
1204 }
1205 if strings.Contains(bare, "%") {
1206 return "format", bare
1207 }
1208 return "output", bare
1209 }
1210
1211 // verbInfo describes a format verb found in a format string.
1212 type verbInfo struct {
1213 verb byte
1214 }
1215
1216 // parseFormatVerbs extracts format verbs from a printf-style format string.
1217 func parseFormatVerbs(s string) []verbInfo {
1218 var verbs []verbInfo
1219 i := 0
1220 for i < len(s) {
1221 if s[i] == '%' && i+1 < len(s) {
1222 i++
1223 if s[i] == '%' {
1224 i++
1225 continue
1226 }
1227 for i < len(s) && strings.ContainsRune("-+# 0", rune(s[i])) {
1228 i++
1229 }
1230 for i < len(s) && s[i] >= '0' && s[i] <= '9' {
1231 i++
1232 }
1233 if i < len(s) && s[i] == '.' {
1234 i++
1235 for i < len(s) && s[i] >= '0' && s[i] <= '9' {
1236 i++
1237 }
1238 }
1239 if i < len(s) {
1240 verbs = append(verbs, verbInfo{verb: s[i]})
1241 i++
1242 }
1243 } else {
1244 i++
1245 }
1246 }
1247 return verbs
1248 }
1249
1250 // cleanFormatString converts a raw format string into a safe Go string
1251 // literal body for embedding inside double quotes.
1252 func cleanFormatString(bare string) string {
1253 var b strings.Builder
1254 b.Grow(len(bare))
1255 for i := 0; i < len(bare); i++ {
1256 switch bare[i] {
1257 case '\\':
1258 b.WriteString(`\\`)
1259 case '\n':
1260 b.WriteString(`\n`)
1261 case '\r':
1262 b.WriteString(`\r`)
1263 case '\t':
1264 b.WriteString(`\t`)
1265 case '"':
1266 b.WriteString(`\"`)
1267 default:
1268 b.WriteByte(bare[i])
1269 }
1270 }
1271 return b.String()
1272 }
1273
1274 // stripEscapes removes Go source escape sequences from a string.
1275 func stripEscapes(s string) string {
1276 s = strings.ReplaceAll(s, `\n`, "")
1277 s = strings.ReplaceAll(s, `\t`, "")
1278 s = strings.ReplaceAll(s, `\r`, "")
1279 for strings.Contains(s, " ") {
1280 s = strings.ReplaceAll(s, " ", " ")
1281 }
1282 return strings.TrimSpace(s)
1283 }
1284
1285 // dedupFragNames returns unique non-empty names from fragments.
1286 func dedupFragNames(frags []Fragment) []string {
1287 seen := make(map[string]bool)
1288 var names []string
1289 for _, f := range frags {
1290 if f.Value != "" && !seen[f.Value] {
1291 seen[f.Value] = true
1292 names = append(names, f.Value)
1293 }
1294 }
1295 return names
1296 }
1297
1298 // dedupNames returns unique non-empty names from a slice of fragments.
1299 // Kept for backward compatibility.
1300 func dedupNames(frags []Fragment) []string {
1301 return dedupFragNames(frags)
1302 }
1303
1304 // extractVarName pulls the variable name from a rendered var declaration.
1305 // "var ownSources embed.FS" → "ownSources"
1306 // Handles leading comments: "var // comment\nGitRef string" → "GitRef"
1307 func extractVarName(decl string) string {
1308 // Process each line looking for the var/const name.
1309 for _, line := range strings.Split(decl, "\n") {
1310 line = strings.TrimSpace(line)
1311 // Skip comment lines.
1312 if strings.HasPrefix(line, "//") || line == "" {
1313 continue
1314 }
1315 // Strip "var " or "const " prefix.
1316 for _, prefix := range []string{"var ", "const "} {
1317 if strings.HasPrefix(line, prefix) {
1318 line = line[len(prefix):]
1319 break
1320 }
1321 }
1322 // First identifier token is the name (strip trailing comma for multi-var).
1323 if idx := strings.IndexAny(line, " =,"); idx > 0 {
1324 return line[:idx]
1325 }
1326 if line != "" {
1327 return line
1328 }
1329 }
1330 return ""
1331 }
1332
1333 // emitDirectives writes any //go: directives associated with the named
1334 // declaration. Each directive's value is "assocName\x00//go:embed ..."
1335 func emitDirectives(b *strings.Builder, directives []Fragment, name string) {
1336 for _, d := range directives {
1337 parent, text := parseBodyValue(d.Value)
1338 if parent == name && text != "" {
1339 fmt.Fprintf(b, "%s\n", text)
1340 }
1341 }
1342 }
1343
1344 // isValidDecl checks whether a var/const declaration string is syntactically
1345 // valid Go. It wraps the declaration in a minimal package and tries to parse it.
1346 func isValidDecl(decl string) bool {
1347 // Quick reject: const/var without a value (iota members outside block).
1348 trimmed := strings.TrimSpace(decl)
1349 if strings.HasPrefix(trimmed, "const ") {
1350 // "const Foo" with no = and no type — iota member, invalid standalone.
1351 rest := strings.TrimPrefix(trimmed, "const ")
1352 // Strip trailing comment.
1353 if ci := strings.Index(rest, "//"); ci >= 0 {
1354 rest = strings.TrimSpace(rest[:ci])
1355 }
1356 // Must have '=' for a standalone const, or be a typed const like "const X Type = val".
1357 if !strings.Contains(rest, "=") {
1358 return false
1359 }
1360 }
1361 // Try to parse the declaration.
1362 src := "package p\n" + decl + "\n"
1363 fset := token.NewFileSet()
1364 _, err := parser.ParseFile(fset, "", src, parser.SkipObjectResolution)
1365 return err == nil
1366 }
1367
1368 // extractReturnSig extracts the return type portion from a function signature.
1369 // "main()" → "", "Foo() string" → "string", "Bar() (int, error)" → "(int, error)"
1370 func extractReturnSig(sig string) string {
1371 // Find the FIRST depth-0 closing ')' — this closes the parameter list.
1372 // Everything after it is the return signature.
1373 depth := 0
1374 paramClose := -1
1375 for i, c := range sig {
1376 if c == '(' {
1377 depth++
1378 } else if c == ')' {
1379 depth--
1380 if depth == 0 {
1381 paramClose = i
1382 break
1383 }
1384 }
1385 }
1386 if paramClose < 0 || paramClose >= len(sig)-1 {
1387 return ""
1388 }
1389 ret := strings.TrimSpace(sig[paramClose+1:])
1390 if ret == "" || ret == "{" {
1391 return ""
1392 }
1393 return ret
1394 }
1395
1396 // zeroReturn generates a return statement with zero values for the given
1397 // return type signature. Handles single types, named returns, and tuples.
1398 func zeroReturn(retSig string) string {
1399 retSig = strings.TrimSpace(retSig)
1400 if retSig == "" {
1401 return ""
1402 }
1403
1404 // Named returns: "(x int, err error)" — just "return" suffices.
1405 if strings.HasPrefix(retSig, "(") {
1406 inner := strings.Trim(retSig, "()")
1407 parts := strings.Split(inner, ",")
1408 // Check if these are named (have both name and type).
1409 for _, p := range parts {
1410 fields := strings.Fields(strings.TrimSpace(p))
1411 if len(fields) >= 2 {
1412 return "return" // named returns — zero-initialized
1413 }
1414 }
1415 // Unnamed tuple: generate zero values for each.
1416 var zeros []string
1417 for _, p := range parts {
1418 zeros = append(zeros, zeroValue(strings.TrimSpace(p)))
1419 }
1420 return "return " + strings.Join(zeros, ", ")
1421 }
1422
1423 // Single return type.
1424 return "return " + zeroValue(retSig)
1425 }
1426
1427 // zeroValue returns the zero-value literal for a Go type.
1428 func zeroValue(typ string) string {
1429 typ = strings.TrimSpace(typ)
1430 switch {
1431 case typ == "string":
1432 return `""`
1433 case typ == "bool":
1434 return "false"
1435 case typ == "error":
1436 return "nil"
1437 case typ == "int" || typ == "int8" || typ == "int16" || typ == "int32" || typ == "int64" ||
1438 typ == "uint" || typ == "uint8" || typ == "uint16" || typ == "uint32" || typ == "uint64" ||
1439 typ == "float32" || typ == "float64" || typ == "byte" || typ == "rune":
1440 return "0"
1441 case strings.HasPrefix(typ, "*") || strings.HasPrefix(typ, "[]") ||
1442 strings.HasPrefix(typ, "map[") || strings.HasPrefix(typ, "chan ") ||
1443 strings.HasPrefix(typ, "func(") || strings.HasPrefix(typ, "<-chan"):
1444 return "nil"
1445 case strings.Contains(typ, "."):
1446 // Package-qualified type — assume it's a struct or interface.
1447 return typ + "{}"
1448 default:
1449 // Unknown type — use zero value by name.
1450 return typ + "{}"
1451 }
1452 }
1453
1454 // identifiersInText extracts unique identifiers from Go source text
1455 // that appear before a dot (potential package references like "ratio.New").
1456 func identifiersInText(text string) []string {
1457 seen := make(map[string]bool)
1458 var result []string
1459 for i := 0; i < len(text)-1; i++ {
1460 if text[i] == '.' && i > 0 {
1461 // Walk back to find the identifier.
1462 j := i - 1
1463 for j >= 0 && isIdentChar(rune(text[j])) {
1464 j--
1465 }
1466 word := text[j+1 : i]
1467 if len(word) >= 2 && !seen[word] {
1468 // Skip single-char receiver variables and common Go keywords.
1469 seen[word] = true
1470 result = append(result, word)
1471 }
1472 }
1473 }
1474 return result
1475 }
1476
1477 // hasUndefinedRefs checks whether a var/const declaration references
1478 // package-qualified identifiers (like "hash.Len" or "_l.Get(...)") where
1479 // the package isn't in the known imports. Returns true if there are
1480 // references that can't be resolved.
1481 func hasUndefinedRefs(decl string, knownPkgs map[string]bool) bool {
1482 // Strip the "var name" / "const name" prefix to get the initializer.
1483 // Look for patterns like "identifier." that suggest package references.
1484 for i := 0; i < len(decl)-1; i++ {
1485 if decl[i] == '.' && i > 0 {
1486 // Walk back to find the identifier before the dot.
1487 j := i - 1
1488 for j >= 0 && isIdentChar(rune(decl[j])) {
1489 j--
1490 }
1491 pkg := decl[j+1 : i]
1492 if pkg == "" || pkg == "x" {
1493 continue // receiver variable, not a package
1494 }
1495 // Skip if it looks like a method call on a known variable
1496 // (single lowercase letter or "err", "ctx", etc.)
1497 if len(pkg) == 1 && pkg[0] >= 'a' && pkg[0] <= 'z' {
1498 continue
1499 }
1500 // Check if this package is in our import set.
1501 if pkg != "" && !knownPkgs[pkg] {
1502 // Check if it's a commonly available identifier
1503 // (builtin types like "reflect", etc.)
1504 if !isBuiltinIdent(pkg) {
1505 return true
1506 }
1507 }
1508 }
1509 }
1510 return false
1511 }
1512
1513 // isBuiltinIdent returns true for Go built-in identifiers and common
1514 // receiver variable names that aren't package references.
1515 func isBuiltinIdent(name string) bool {
1516 switch name {
1517 case "true", "false", "nil", "iota",
1518 "int", "int8", "int16", "int32", "int64",
1519 "uint", "uint8", "uint16", "uint32", "uint64",
1520 "float32", "float64", "complex64", "complex128",
1521 "string", "bool", "byte", "rune", "error", "any",
1522 "make", "new", "len", "cap", "append", "copy", "delete",
1523 "close", "panic", "recover", "print", "println",
1524 "err", "ctx", "ok", "self":
1525 return true
1526 }
1527 return false
1528 }
1529
1530 // isValidReceiver checks that a method receiver type is a simple identifier
1531 // or pointer to identifier (e.g. "Foo" or "*Foo"), not a garbled signature.
1532 func isValidReceiver(recv string) bool {
1533 r := strings.TrimPrefix(recv, "*")
1534 if r == "" {
1535 return false
1536 }
1537 for _, c := range r {
1538 if !isIdentChar(c) {
1539 return false
1540 }
1541 }
1542 return true
1543 }
1544
1545 func isIdentChar(c rune) bool {
1546 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'
1547 }
1548
1549 // aliasCollisions detects import paths that resolve to the same package name
1550 // and adds aliases to resolve collisions. For example, if both "crypto" and
1551 // "git.mleku.dev/mleku/dendrite/pkg/crypto" are imported, the latter becomes
1552 // "dcrypto" aliased.
1553 func aliasCollisions(imports []string) []string {
1554 type entry struct {
1555 path string
1556 pkg string
1557 index int
1558 }
1559 entries := make([]entry, len(imports))
1560 byPkg := make(map[string][]int)
1561
1562 for i, imp := range imports {
1563 bare := strings.Trim(imp, `"`)
1564 pkg := extractPkgName(imp)
1565 entries[i] = entry{path: bare, pkg: pkg, index: i}
1566 byPkg[pkg] = append(byPkg[pkg], i)
1567 }
1568
1569 result := make([]string, len(imports))
1570 copy(result, imports)
1571
1572 for pkg, indices := range byPkg {
1573 if len(indices) <= 1 {
1574 continue
1575 }
1576 // Keep the shortest path (likely stdlib) without alias.
1577 // Alias the others with a prefix.
1578 sort.Slice(indices, func(a, b int) bool {
1579 return len(entries[indices[a]].path) < len(entries[indices[b]].path)
1580 })
1581 for k := 1; k < len(indices); k++ {
1582 idx := indices[k]
1583 alias := fmt.Sprintf("%s%d", pkg, k)
1584 result[idx] = fmt.Sprintf("%s %q", alias, entries[idx].path)
1585 }
1586 }
1587 return result
1588 }
1589
1590 // sanitizeGoSource post-processes emitted Go source:
1591 // 1. Always strip orphan break/continue/goto (valid syntax, invalid semantics)
1592 // 2. If parsing still fails, remove invalid top-level declarations
1593 func sanitizeGoSource(source string) (string, error) {
1594 // Always strip orphan branches — go/parser accepts them as valid syntax,
1595 // but the Go compiler rejects break/continue outside loops.
1596 source = stripOrphanBranches(source)
1597
1598 fset := token.NewFileSet()
1599 _, err := parser.ParseFile(fset, "output.go", source, parser.SkipObjectResolution|parser.AllErrors|parser.ParseComments)
1600 if err == nil {
1601 return source, nil
1602 }
1603
1604 // Parse failed. Salvage by removing invalid top-level declarations.
1605 return repairSource(source)
1606 }
1607
1608 // stripOrphanBranches removes break/continue/goto statements that appear
1609 // outside of for/switch/select blocks. Uses character-level brace counting
1610 // that ignores braces inside string literals and comments to avoid being
1611 // confused by struct literals like `Type{}`.
1612 func stripOrphanBranches(source string) string {
1613 lines := strings.Split(source, "\n")
1614 var result []string
1615
1616 // Track nesting of for loops (for continue) and all loop-like constructs
1617 // (for/switch/select, for break). Continue is ONLY valid in for loops.
1618 forDepth := 0 // for loops only
1619 breakDepth := 0 // for + switch + select
1620 braceDepth := 0
1621 var forBraceStack []int // brace depths where for loops start
1622 var breakBraceStack []int // brace depths where any break-accepting block starts
1623 inBlockComment := false
1624
1625 for _, line := range lines {
1626 trimmed := strings.TrimSpace(line)
1627
1628 // Classify block-opening statements.
1629 isFor := strings.HasPrefix(trimmed, "for ") || trimmed == "for {" ||
1630 strings.HasPrefix(trimmed, "for range ")
1631 isBreakable := isFor ||
1632 strings.HasPrefix(trimmed, "switch ") || trimmed == "switch {" ||
1633 strings.HasPrefix(trimmed, "select ") || trimmed == "select {"
1634
1635 // Count braces at character level, skipping strings and comments.
1636 lineOpens, lineCloses := countBraces(line, &inBlockComment)
1637
1638 if isFor && lineOpens > 0 {
1639 forBraceStack = append(forBraceStack, braceDepth+1)
1640 forDepth++
1641 }
1642 if isBreakable && lineOpens > 0 {
1643 breakBraceStack = append(breakBraceStack, braceDepth+1)
1644 breakDepth++
1645 }
1646
1647 braceDepth += lineOpens - lineCloses
1648
1649 // Pop stacks when we close past their level.
1650 for len(forBraceStack) > 0 && braceDepth < forBraceStack[len(forBraceStack)-1] {
1651 forBraceStack = forBraceStack[:len(forBraceStack)-1]
1652 forDepth--
1653 }
1654 for len(breakBraceStack) > 0 && braceDepth < breakBraceStack[len(breakBraceStack)-1] {
1655 breakBraceStack = breakBraceStack[:len(breakBraceStack)-1]
1656 breakDepth--
1657 }
1658
1659 // Strip orphan branches and goto.
1660 if strings.HasPrefix(trimmed, "goto ") {
1661 continue // always strip goto — labels rarely survive emission
1662 }
1663 // continue is only valid in for loops
1664 if forDepth <= 0 && (trimmed == "continue" || strings.HasPrefix(trimmed, "continue ")) {
1665 continue
1666 }
1667 // break is valid in for/switch/select
1668 if breakDepth <= 0 && (trimmed == "break" || strings.HasPrefix(trimmed, "break ")) {
1669 continue
1670 }
1671 // fallthrough and case clauses only valid in switch
1672 if breakDepth <= 0 {
1673 if trimmed == "fallthrough" || strings.HasPrefix(trimmed, "case ") ||
1674 trimmed == "default:" {
1675 continue
1676 }
1677 }
1678
1679 result = append(result, line)
1680 }
1681
1682 return strings.Join(result, "\n")
1683 }
1684
1685 // countBraces counts `{` and `}` in a line, skipping those inside string
1686 // literals (both "" and ``) and comments. Tracks block comment state
1687 // across lines via the inBlockComment pointer.
1688 func countBraces(line string, inBlockComment *bool) (opens, closes int) {
1689 inString := false
1690 inRawString := false
1691 inLineComment := false
1692 escaped := false
1693
1694 for i := 0; i < len(line); i++ {
1695 c := line[i]
1696
1697 if escaped {
1698 escaped = false
1699 continue
1700 }
1701
1702 if *inBlockComment {
1703 if c == '*' && i+1 < len(line) && line[i+1] == '/' {
1704 *inBlockComment = false
1705 i++ // skip '/'
1706 }
1707 continue
1708 }
1709
1710 if inLineComment {
1711 continue
1712 }
1713
1714 if inString {
1715 if c == '\\' {
1716 escaped = true
1717 } else if c == '"' {
1718 inString = false
1719 }
1720 continue
1721 }
1722
1723 if inRawString {
1724 if c == '`' {
1725 inRawString = false
1726 }
1727 continue
1728 }
1729
1730 // Not inside any string or comment.
1731 switch c {
1732 case '"':
1733 inString = true
1734 case '`':
1735 inRawString = true
1736 case '/':
1737 if i+1 < len(line) {
1738 if line[i+1] == '/' {
1739 inLineComment = true
1740 i++
1741 } else if line[i+1] == '*' {
1742 *inBlockComment = true
1743 i++
1744 }
1745 }
1746 case '{':
1747 opens++
1748 case '}':
1749 closes++
1750 }
1751 }
1752 return
1753 }
1754
1755 // repairSource splits Go source into top-level declaration blocks and
1756 // re-assembles only the ones that parse successfully.
1757 func repairSource(source string) (string, error) {
1758 lines := strings.Split(source, "\n")
1759
1760 var header strings.Builder // package + import
1761 var decls []string // individual top-level declarations
1762 var current strings.Builder
1763
1764 inImport := false
1765 headerDone := false
1766 braceDepth := 0
1767
1768 for _, line := range lines {
1769 trimmed := strings.TrimSpace(line)
1770
1771 // Package and import go into the header.
1772 if !headerDone {
1773 if strings.HasPrefix(trimmed, "package ") {
1774 header.WriteString(line + "\n")
1775 continue
1776 }
1777 if strings.HasPrefix(trimmed, "import") {
1778 inImport = true
1779 header.WriteString(line + "\n")
1780 if strings.Contains(trimmed, "(") && !strings.Contains(trimmed, ")") {
1781 continue
1782 }
1783 if strings.Contains(trimmed, ")") || !strings.Contains(trimmed, "(") {
1784 inImport = false
1785 headerDone = true
1786 }
1787 continue
1788 }
1789 if inImport {
1790 header.WriteString(line + "\n")
1791 if trimmed == ")" {
1792 inImport = false
1793 headerDone = true
1794 }
1795 continue
1796 }
1797 headerDone = true
1798 }
1799
1800 // Track brace depth to find declaration boundaries.
1801 for _, c := range line {
1802 if c == '{' {
1803 braceDepth++
1804 } else if c == '}' {
1805 braceDepth--
1806 }
1807 }
1808
1809 current.WriteString(line + "\n")
1810
1811 // At brace depth 0 and a non-empty line, we've completed a declaration.
1812 if braceDepth <= 0 && trimmed != "" {
1813 decl := current.String()
1814 if strings.TrimSpace(decl) != "" {
1815 decls = append(decls, decl)
1816 }
1817 current.Reset()
1818 braceDepth = 0
1819 }
1820 }
1821 // Flush remaining.
1822 if s := current.String(); strings.TrimSpace(s) != "" {
1823 decls = append(decls, s)
1824 }
1825
1826 // Validate each declaration by attempting to parse it.
1827 // Track declared names to prevent redeclarations across types, vars, and funcs.
1828 var validDecls strings.Builder
1829 hdr := header.String()
1830 declaredNames := make(map[string]bool)
1831 for _, decl := range decls {
1832 test := hdr + "\n" + decl
1833 fset := token.NewFileSet()
1834 _, err := parser.ParseFile(fset, "", test, parser.SkipObjectResolution|parser.AllErrors|parser.ParseComments)
1835 if err != nil {
1836 continue
1837 }
1838 // Extract the declaration name and check for redeclarations.
1839 name := extractDeclName(strings.TrimSpace(decl))
1840 if name != "" && declaredNames[name] {
1841 continue // skip redeclaration
1842 }
1843 if name != "" {
1844 declaredNames[name] = true
1845 }
1846 validDecls.WriteString(decl)
1847 validDecls.WriteString("\n")
1848 }
1849
1850 result := hdr + "\n" + validDecls.String()
1851
1852 // Don't use go/format — it strips unused imports which breaks the
1853 // dependency chain. Return the filtered result directly.
1854 if validDecls.Len() > 0 {
1855 return result, nil
1856 }
1857
1858 return "", fmt.Errorf("no valid declarations found")
1859 }
1860
1861 // extractDeclName extracts the declared name from a top-level declaration.
1862 // "type Foo struct {" → "Foo", "func Bar() {" → "Bar", "var x int" → "x",
1863 // "func (r *T) Method() {" → "" (methods don't conflict with types/vars).
1864 func extractDeclName(decl string) string {
1865 decl = strings.TrimSpace(decl)
1866 switch {
1867 case strings.HasPrefix(decl, "type "):
1868 rest := decl[5:]
1869 if idx := strings.IndexAny(rest, " ={"); idx > 0 {
1870 return rest[:idx]
1871 }
1872 return rest
1873 case strings.HasPrefix(decl, "var "):
1874 rest := decl[4:]
1875 if idx := strings.IndexAny(rest, " ="); idx > 0 {
1876 return rest[:idx]
1877 }
1878 return rest
1879 case strings.HasPrefix(decl, "const "):
1880 rest := decl[6:]
1881 if idx := strings.IndexAny(rest, " ="); idx > 0 {
1882 return rest[:idx]
1883 }
1884 return rest
1885 case strings.HasPrefix(decl, "func "):
1886 rest := decl[5:]
1887 // Method: "func (x *T) Name(..." → skip (methods don't conflict)
1888 if strings.HasPrefix(rest, "(") {
1889 return ""
1890 }
1891 // Plain function: "func Name(..." → extract Name
1892 if idx := strings.IndexByte(rest, '('); idx > 0 {
1893 return rest[:idx]
1894 }
1895 return ""
1896 }
1897 return ""
1898 }
1899