@@ -34,6 +34,8 @@ type Generator struct {
3434 w io.Writer
3535 varScopes []varScope // stack of variable scopes
3636 recurStack []recurContext // stack of recur contexts for nested loops
37+
38+ imports map [string ]bool // set of imported packages to avoid duplicates
3739}
3840
3941// New creates a new code generator
@@ -43,6 +45,7 @@ func New(w io.Writer) *Generator {
4345 w : w ,
4446 varScopes : []varScope {{nextNum : 0 , names : make (map [string ]string )}},
4547 recurStack : []recurContext {},
48+ imports : make (map [string ]bool ),
4649 }
4750}
4851
@@ -53,41 +56,6 @@ func (g *Generator) Generate(ns *lang.Namespace) error {
5356 var buf bytes.Buffer
5457 g .w = & buf
5558
56- // Check if we need fmt import (for functions with arity checks)
57- needsFmt := false
58- mappings := ns .Mappings ()
59-
60- // Only check vars that are interned in this namespace
61- for seq := mappings .Seq (); seq != nil ; seq = seq .Next () {
62- entry := seq .First ()
63- name , ok := lang .First (entry ).(* lang.Symbol )
64- if ! ok {
65- continue
66- }
67- second , _ := lang .Nth (entry , 1 )
68- vr , ok := second .(* lang.Var )
69- if ! ok {
70- continue
71- }
72-
73- // Skip non-interned mappings
74- if ! (vr .Namespace () == ns && lang .Equals (vr .Symbol (), name )) {
75- continue
76- }
77-
78- if vr .IsBound () {
79- if _ , ok := vr .Get ().(* runtime.Fn ); ok {
80- needsFmt = true
81- break
82- }
83- }
84- }
85-
86- // Write package header
87- if err := g .writeHeader (needsFmt ); err != nil {
88- return err
89- }
90-
9159 g .writef ("func init() {\n " )
9260
9361 g .writef (" ns := lang.FindOrCreateNamespace(lang.NewSymbol(\" %s\" ))\n " , ns .Name ().String ())
@@ -96,6 +64,7 @@ func (g *Generator) Generate(ns *lang.Namespace) error {
9664 // 1. Iterate through ns.Mappings()
9765 // 2. Generate Go code for each var
9866 // 3. Create initialization functions
67+ mappings := ns .Mappings ()
9968 for seq := mappings .Seq (); seq != nil ; seq = seq .Next () {
10069 entry := seq .First ()
10170 name , ok := lang .First (entry ).(* lang.Symbol )
@@ -119,8 +88,12 @@ func (g *Generator) Generate(ns *lang.Namespace) error {
11988
12089 g .writef ("}\n " )
12190
91+ // Write package header
92+ sourceBytes := []byte (g .header ())
93+ sourceBytes = append (sourceBytes , buf .Bytes ()... )
94+
12295 // Format the generated code
123- formatted , err := format .Source (buf . Bytes () )
96+ formatted , err := format .Source (sourceBytes )
12497 if err != nil {
12598 // If formatting fails, write the unformatted code with the error
12699 return fmt .Errorf ("formatting failed: %w\n \n Generated code:\n %s" , err , buf .String ())
@@ -273,6 +246,7 @@ func (g *Generator) generateFn(fn *runtime.Fn) string {
273246
274247 g .writef ("%s := lang.IFnFunc(func(args ...any) any {\n " , fnVar )
275248
249+ g .addImport ("fmt" ) // Import fmt for error formatting
276250 // Check arity
277251 g .writef (" if len(args) != %d {\n " , methodNode .FixedArity )
278252 g .writef (" panic(lang.NewIllegalArgumentError(\" wrong number of arguments (\" + fmt.Sprint(len(args)) + \" )\" ))\n " )
@@ -373,7 +347,6 @@ func (g *Generator) generateASTNode(node *ast.Node) string {
373347 // OpSet
374348 // OpLetFn
375349 // OpQuote
376- // OpGoBuiltin
377350 // OpGo
378351 // OpHostCall
379352 // OpHostInterop
@@ -407,10 +380,7 @@ func (g *Generator) generateASTNode(node *ast.Node) string {
407380 case ast .OpRecur :
408381 return g .generateRecur (node )
409382 case ast .OpGoBuiltin :
410- // For now, just return a reference to the go type
411- // This is used for catch clauses with go/any
412- goBuiltinNode := node .Sub .(* ast.GoBuiltinNode )
413- return fmt .Sprintf ("lang.NewSymbol(\" %s\" )" , goBuiltinNode .Sym .Name ())
383+ return g .generateGoBuiltin (node )
414384 default :
415385 fmt .Printf ("Generating code for AST node: %T %+v\n " , node .Sub , node .Sub )
416386 panic (fmt .Sprintf ("unsupported AST node type %T" , node .Sub ))
@@ -720,9 +690,8 @@ func (g *Generator) generateTry(node *ast.Node) string {
720690 g .writef ("} else " )
721691 }
722692
723- // Simple implementation: check for "any" or assume it catches
724- // In a full implementation, we'd need to check types properly
725- g .writef ("if true { // TODO: implement catchMatches(r, %s)\n " , classExpr )
693+ // Check if the exception matches this catch type
694+ g .writef ("if lang.CatchMatches(r, %s) {\n " , classExpr )
726695
727696 // Create new scope for catch binding
728697 g .pushVarScope ()
@@ -758,25 +727,39 @@ func (g *Generator) generateTry(node *ast.Node) string {
758727 return resultVar
759728}
760729
730+ func (g * Generator ) generateGoBuiltin (node * ast.Node ) string {
731+ goBuiltinNode := node .Sub .(* ast.GoBuiltinNode )
732+ sym := goBuiltinNode .Sym
733+
734+ _ , ok := lang .Builtins [sym .Name ()]
735+ if ! ok {
736+ panic (fmt .Sprintf ("unknown Go builtin: %s" , sym .Name ()))
737+ }
738+
739+ return "lang.Builtins[\" " + sym .Name () + "\" ]"
740+ }
741+
761742////////////////////////////////////////////////////////////////////////////////
762743
763- func (g * Generator ) writeHeader (needsFmt bool ) error {
744+ func (g * Generator ) addImport (pkg string ) {
745+ g .imports [pkg ] = true
746+ }
747+
748+ func (g * Generator ) header () string {
764749 header := `// Code generated by glojure codegen. DO NOT EDIT.
765750
766751package generated
767752
768753import (
754+ "github.com/glojurelang/glojure/pkg/lang"
769755`
770- if needsFmt {
771- header += ` "fmt"
772- `
756+
757+ for pkg := range g . imports {
758+ header += fmt . Sprintf ( " \" %s \" \n " , pkg )
773759 }
774- header += ` "github.com/glojurelang/glojure/pkg/lang"
775- )
776760
777- `
778- _ , err := io .WriteString (g .w , header )
779- return err
761+ header += ")\n "
762+ return header
780763}
781764
782765func (g * Generator ) writef (format string , args ... any ) error {
0 commit comments