diff --git a/compiler/gengo/gengo.go b/compiler/gengo/gengo.go index 8835dae..70265d4 100644 --- a/compiler/gengo/gengo.go +++ b/compiler/gengo/gengo.go @@ -39,6 +39,7 @@ type Generator struct { IsLibrary bool // if true, no main() generated, symbols use unique name hoistedFields []string // field names hoisted outside FOR loop (nil = not hoisting) hoistedDW bool // DO WHILE has hoisted _dwa/_darea + symCache map[string]string // symbol name → cached variable name (nil = not caching) Debug bool // if true, emit t.DebugLine() calls } @@ -922,6 +923,73 @@ func hasWorkareaChange(stmts []ast.Stmt) bool { return false } +// collectSymbols scans AST for all symbol names referenced by function calls. +// Returns unique names for hoisting FindSymbol to function prologue. +func collectSymbols(stmts []ast.Stmt) []string { + seen := map[string]bool{} + var names []string + var walk func([]ast.Stmt) + var walkExpr func(ast.Expr) + + walkExpr = func(e ast.Expr) { + if e == nil { + return + } + switch v := e.(type) { + case *ast.CallExpr: + if ident, ok := v.Func.(*ast.IdentExpr); ok { + name := strings.ToUpper(ident.Name) + if !seen[name] { + seen[name] = true + names = append(names, name) + } + } + for _, a := range v.Args { + walkExpr(a) + } + case *ast.BinaryExpr: + walkExpr(v.Left) + walkExpr(v.Right) + case *ast.UnaryExpr: + walkExpr(v.X) + } + } + + walk = func(stmts []ast.Stmt) { + for _, s := range stmts { + switch v := s.(type) { + case *ast.ExprStmt: + walkExpr(v.X) + case *ast.ReturnStmt: + if v.Value != nil { + walkExpr(v.Value) + } + case *ast.IfStmt: + walkExpr(v.Cond) + walk(v.Body) + walk(v.ElseBody) + case *ast.ForStmt: + walk(v.Body) + case *ast.ForEachStmt: + walk(v.Body) + case *ast.DoWhileStmt: + walkExpr(v.Cond) + walk(v.Body) + case *ast.SeqStmt: + walk(v.Body) + walk(v.RecoverBody) + case *ast.SwitchStmt: + for _, c := range v.Cases { + walk(c.Body) + } + } + } + } + + walk(stmts) + return names +} + // collectReplaceFields scans statements for REPLACE field names. // Returns nil if unsafe to hoist (USE/SELECT/CLOSE found). func collectReplaceFields(stmts []ast.Stmt) []string { @@ -1464,7 +1532,16 @@ func (g *Generator) emitCall(e *ast.CallExpr) { if g.tryEmitInlineRTL(ident.Name, e.Args) { return } - g.writeln(fmt.Sprintf("t.PushSymbol(t.VM().FindSymbol(%q))", strings.ToUpper(ident.Name))) + upper := strings.ToUpper(ident.Name) + if g.symCache != nil { + if varName, ok := g.symCache[upper]; ok { + g.writeln(fmt.Sprintf("t.PushSymbol(%s)", varName)) + } else { + g.writeln(fmt.Sprintf("t.PushSymbol(t.VM().FindSymbol(%q))", upper)) + } + } else { + g.writeln(fmt.Sprintf("t.PushSymbol(t.VM().FindSymbol(%q))", upper)) + } } else { g.emitExpr(e.Func) }