@@ -47,14 +47,20 @@ type liftedValue struct {
4747 varName string
4848}
4949
50+ type varInfo struct {
51+ ns string
52+ sym string
53+ }
54+
5055// Generator handles the conversion of AST nodes to Go code
5156type Generator struct {
5257 originalWriter io.Writer
5358 w io.Writer
5459 varScopes []varScope // stack of variable scopes
5560 recurStack []recurContext // stack of recur contexts for nested loops
5661
57- imports map [string ]string // set of imported packages with their aliases
62+ imports map [string ]string // set of imported packages with their aliases
63+ varVariables map [varInfo ]string // map of vars to their Go variable names
5864
5965 // Fields for handling closures
6066 liftedValues map [liftedKey ]* liftedValue // Dedupe by composite key
@@ -80,6 +86,7 @@ func New(w io.Writer) *Generator {
8086 imports : make (map [string ]string ),
8187 liftedValues : make (map [liftedKey ]* liftedValue ),
8288 liftedCounter : 0 ,
89+ varVariables : make (map [varInfo ]string ),
8390 }
8491}
8592
@@ -138,7 +145,29 @@ func (g *Generator) Generate(ns *lang.Namespace) error {
138145
139146 // Now construct the complete init function
140147 var initBuf bytes.Buffer
141- initBuf .WriteString ("func init() {\n " )
148+ initBuf .WriteString (fmt .Sprintf ("// LoadNS initializes the namespace %q\n " , ns .Name ().String ()))
149+ initBuf .WriteString ("func LoadNS() {\n " )
150+ initBuf .WriteString (`checkDerefMacro := func (v *lang.Var) {
151+ if v.IsMacro() {
152+ panic(lang.NewIllegalArgumentError(fmt.Sprintf("can't take value of macro: %v", v)))
153+ }
154+ }
155+ _ = checkDerefMacro
156+ ` )
157+
158+ // initialize all the vars first
159+ var varNames []string
160+ var inverseVarMap = make (map [string ]varInfo )
161+ for vi , varName := range g .varVariables {
162+ varNames = append (varNames , varName )
163+ inverseVarMap [varName ] = vi
164+ }
165+ sort .Strings (varNames ) // Sort for deterministic output
166+ for _ , varName := range varNames {
167+ vi := inverseVarMap [varName ]
168+ initBuf .WriteString (fmt .Sprintf ("// var %s/%s\n " , vi .ns , vi .sym ))
169+ initBuf .WriteString (fmt .Sprintf ("%s := lang.InternVarName(lang.NewSymbol(%q), lang.NewSymbol(%q))\n " , varName , vi .ns , vi .sym ))
170+ }
142171
143172 // Generate lifted values at the beginning of init() if any
144173 if len (g .liftedValues ) > 0 {
@@ -180,8 +209,8 @@ func (g *Generator) Generate(ns *lang.Namespace) error {
180209 initBuf .WriteString ("}\n " )
181210
182211 // Prepare the final source
183- sourceBytes := []byte (g .header ()) // Package declaration and imports
184- sourceBytes = append (sourceBytes , initBuf .Bytes ()... ) // The complete init function
212+ sourceBytes := []byte (g .header (mungeID ( getLastNSPart ( ns . Name (). String ())))) // File header with package and imports
213+ sourceBytes = append (sourceBytes , initBuf .Bytes ()... ) // The complete init function
185214
186215 // Format the generated code
187216 formatted , err := format .Source (sourceBytes )
@@ -866,18 +895,10 @@ func (g *Generator) generateVarDeref(node *ast.Node) string {
866895 varNamespace := varNode .Var .Namespace ()
867896 varSymbol := varNode .Var .Symbol ()
868897
869- // generate code to look up the var in the namespace
870- nsVar := g .allocateTempVar ()
871- g .writef ("%s := lang.FindNamespace(lang.NewSymbol(\" %s\" ))\n " , nsVar , varNamespace .Name ())
872- // look up the var in the namespace
873- varId := g .allocateTempVar ()
874- g .writef ("%s := %s.FindInternedVar(lang.NewSymbol(\" %s\" ))\n " , varId , nsVar , varSymbol .Name ())
898+ // Look up the var variable
899+ varId := g .allocateVarVariable (varNamespace .Name ().String (), varSymbol .String ())
875900
876- // if macro, panic with 'can't take value of macro: %v'
877- g .writef ("if %s.IsMacro() {\n " , varId )
878- g .writef (" panic(lang.NewIllegalArgumentError(fmt.Sprintf(\" can't take value of macro: %%v\" , %s)))\n " , varId )
879- g .writef ("}\n " )
880- // else, return Get()
901+ g .writef ("checkDerefMacro(%s)\n " , varId )
881902 resultId := g .allocateTempVar ()
882903 g .writef ("%s := %s.Get()\n " , resultId , varId )
883904
@@ -1416,13 +1437,13 @@ func (g *Generator) addImportWithAlias(pkg string) string {
14161437 return alias
14171438}
14181439
1419- func (g * Generator ) header () string {
1420- header := `// Code generated by glojure codegen. DO NOT EDIT.
1440+ func (g * Generator ) header (pkgName string ) string {
1441+ header := fmt . Sprintf ( `// Code generated by glojure codegen. DO NOT EDIT.
14211442
1422- package generated
1443+ package %s
14231444
14241445import (
1425- `
1446+ ` , pkgName )
14261447
14271448 // sort the imports by their package name for deterministic output
14281449 keys := make ([]string , 0 , len (g .imports ))
@@ -1456,7 +1477,7 @@ func (g *Generator) writeAssign(varName, rValue string) {
14561477}
14571478
14581479////////////////////////////////////////////////////////////////////////////////
1459- // Variable Scope Management
1480+ // Variable scope management and other helpers
14601481
14611482// PushVarScope creates a new variable scope
14621483func (g * Generator ) pushVarScope () {
@@ -1587,8 +1608,47 @@ func (g *Generator) allocateTempVar() string {
15871608 return varName
15881609}
15891610
1611+ var (
1612+ replacements = map [rune ]string {
1613+ '!' : "_BANG_" ,
1614+ '?' : "_QMARK_" ,
1615+ '-' : "_" ,
1616+ '+' : "_PLUS_" ,
1617+ '*' : "_STAR_" ,
1618+ '/' : "_SLASH_" ,
1619+ '=' : "_EQ_" ,
1620+ '<' : "_LT_" ,
1621+ '>' : "_GT_" ,
1622+ '&' : "_AMP_" ,
1623+ '%' : "_PCT_" ,
1624+ '$' : "_DOLLAR_" ,
1625+ '^' : "_CARET_" ,
1626+ '~' : "_TILDE_" ,
1627+ '.' : "_DOT_" ,
1628+ ':' : "_COLON_" ,
1629+ '@' : "_AT_" ,
1630+ '#' : "_HASH_" ,
1631+ }
1632+ )
1633+
15901634func mungeID (name string ) string {
1591- return strings .ReplaceAll (name , "-" , "__" )
1635+ var sb strings.Builder
1636+ for _ , ch := range name {
1637+ if repl , ok := replacements [ch ]; ok {
1638+ sb .WriteString (repl )
1639+ } else if (ch >= 'a' && ch <= 'z' ) || (ch >= 'A' && ch <= 'Z' ) || (ch >= '0' && ch <= '9' ) || ch == '_' {
1640+ sb .WriteRune (ch )
1641+ } else {
1642+ // Replace any other non-alphanumeric character with its Unicode code point
1643+ sb .WriteString (fmt .Sprintf ("_U%04X_" , ch ))
1644+ }
1645+ }
1646+ return sb .String ()
1647+ }
1648+
1649+ func getLastNSPart (ns string ) string {
1650+ parts := strings .Split (ns , "." )
1651+ return parts [len (parts )- 1 ]
15921652}
15931653
15941654func (g * Generator ) pushRecurContext (loopID * lang.Symbol , bindings []string , useGoto bool ) {
@@ -1612,3 +1672,13 @@ func (g *Generator) currentRecurContext() *recurContext {
16121672 }
16131673 return & g .recurStack [len (g .recurStack )- 1 ]
16141674}
1675+
1676+ func (g * Generator ) allocateVarVariable (ns , sym string ) string {
1677+ varInfo := varInfo {ns : ns , sym : sym }
1678+ if v , ok := g .varVariables [varInfo ]; ok {
1679+ return v
1680+ }
1681+ varName := mungeID (ns ) + "_" + mungeID (sym )
1682+ g .varVariables [varInfo ] = varName
1683+ return varName
1684+ }
0 commit comments