diff --git a/compiler/gengo/gengo.go b/compiler/gengo/gengo.go index 1957ff1..4cac17a 100644 --- a/compiler/gengo/gengo.go +++ b/compiler/gengo/gengo.go @@ -345,6 +345,44 @@ func (g *Generator) emitTopLevelStatic(vd *ast.VarDecl) { func (g *Generator) emitFuncDecl(fn *ast.FuncDecl) { goName := "HB_" + strings.ToUpper(fn.Name) + + // Emit function-level STATIC variables as package-level Go vars. + // Harbour: STATIC inside FUNCTION persists across calls but is + // scoped to the function. We prefix with funcname to avoid clashes. + for _, d := range fn.Decls { + if vd, ok := d.(*ast.VarDecl); ok && vd.Scope == ast.ScopeStatic { + for _, v := range vd.Vars { + varName := "static_" + strings.ToUpper(fn.Name) + "_" + strings.ToUpper(v.Name) + initVal := "hbrt.MakeNil()" + if v.Init != nil { + initVal = g.exprToGoLiteral(v.Init) + } + g.writeln(fmt.Sprintf("var %s = %s", varName, initVal)) + if g.staticVars == nil { + g.staticVars = make(map[string]string) + } + g.staticVars[strings.ToUpper(v.Name)] = varName + } + } + } + // Also scan body for mid-function STATIC declarations + for _, s := range fn.Body { + if vd, ok := s.(*ast.VarDecl); ok && vd.Scope == ast.ScopeStatic { + for _, v := range vd.Vars { + varName := "static_" + strings.ToUpper(fn.Name) + "_" + strings.ToUpper(v.Name) + initVal := "hbrt.MakeNil()" + if v.Init != nil { + initVal = g.exprToGoLiteral(v.Init) + } + g.writeln(fmt.Sprintf("var %s = %s", varName, initVal)) + if g.staticVars == nil { + g.staticVars = make(map[string]string) + } + g.staticVars[strings.ToUpper(v.Name)] = varName + } + } + } + g.writeln(fmt.Sprintf("func %s(t *hbrt.Thread) {", goName)) g.indent++ @@ -393,6 +431,23 @@ func (g *Generator) emitFuncDecl(fn *ast.FuncDecl) { g.indent-- g.writeln("}") g.writeln("") + + // Remove function-scoped STATIC vars from the map so they don't + // leak into other functions that happen to share a variable name. + for _, d := range fn.Decls { + if vd, ok := d.(*ast.VarDecl); ok && vd.Scope == ast.ScopeStatic { + for _, v := range vd.Vars { + delete(g.staticVars, strings.ToUpper(v.Name)) + } + } + } + for _, s := range fn.Body { + if vd, ok := s.(*ast.VarDecl); ok && vd.Scope == ast.ScopeStatic { + for _, v := range vd.Vars { + delete(g.staticVars, strings.ToUpper(v.Name)) + } + } + } } type localMap map[string]int @@ -893,11 +948,39 @@ func (g *Generator) emitAssign(a *ast.AssignExpr, locals localMap) { } return } - // Check module-level STATIC variable + // Check module-level or function-level STATIC variable upper := strings.ToUpper(ident.Name) if goVar, found := g.staticVars[upper]; found { - g.emitExpr(a.Right) - g.writeln(fmt.Sprintf("%s = t.Pop2()", goVar)) + switch a.Op { + case token.ASSIGN: + g.emitExpr(a.Right) + g.writeln(fmt.Sprintf("%s = t.Pop2()", goVar)) + case token.PLUSEQ: + g.writeln(fmt.Sprintf("t.PushValue(%s)", goVar)) + g.emitExpr(a.Right) + g.writeln("t.Plus()") + g.writeln(fmt.Sprintf("%s = t.Pop2()", goVar)) + case token.MINUSEQ: + g.writeln(fmt.Sprintf("t.PushValue(%s)", goVar)) + g.emitExpr(a.Right) + g.writeln("t.Minus()") + g.writeln(fmt.Sprintf("%s = t.Pop2()", goVar)) + case token.STAREQ: + g.writeln(fmt.Sprintf("t.PushValue(%s)", goVar)) + g.emitExpr(a.Right) + g.writeln("t.Mult()") + g.writeln(fmt.Sprintf("%s = t.Pop2()", goVar)) + case token.SLASHEQ: + g.writeln(fmt.Sprintf("t.PushValue(%s)", goVar)) + g.emitExpr(a.Right) + g.writeln("t.Divide()") + g.writeln(fmt.Sprintf("%s = t.Pop2()", goVar)) + default: + g.writeln(fmt.Sprintf("t.PushValue(%s)", goVar)) + g.emitExpr(a.Right) + g.emitBinaryOp(a.Op) + g.writeln(fmt.Sprintf("%s = t.Pop2()", goVar)) + } return } }