diff --git a/compiler/gengo/gengo.go b/compiler/gengo/gengo.go index 44cb1e7..055630f 100644 --- a/compiler/gengo/gengo.go +++ b/compiler/gengo/gengo.go @@ -617,8 +617,20 @@ func (g *Generator) emitStmt(stmt ast.Stmt, locals localMap) { g.indent++ keyStr := exprToString(s.KeyExpr) g.writeln(fmt.Sprintf("_keyExpr := %q", keyStr)) - fileStr := exprToString(s.File) - g.writeln(fmt.Sprintf("_file := %q", fileStr)) + + // File expression: if it contains a function call, evaluate at + // runtime — Harbour `INDEX ON ... TO ( cExpr )` semantics. Prior + // behavior was static exprToString which serialized calls like + // `Lower(cTable) + "_pk.ntx"` into the literal filename string. + // Detect via containsCall; preserve static path for simple + // `test.ntx` style identifiers. + if containsCall(s.File) { + g.emitExpr(s.File) + g.writeln("_file := t.Pop2().AsString()") + } else { + fileStr := exprToString(s.File) + g.writeln(fmt.Sprintf("_file := %q", fileStr)) + } forExpr := `""` if s.ForCond != nil { forExpr = fmt.Sprintf("%q", exprToString(s.ForCond)) @@ -1783,6 +1795,26 @@ func (g *Generator) emitIndexKeyExpr(expr ast.Expr) { } // exprToString extracts a string representation from an AST expression. +// containsCall reports whether an expression contains any function call. +// Used to distinguish runtime-evaluated INDEX ON filenames +// (`TO ( Lower(cTable) + "_pk.ntx" )`) from static ones (`TO test.ntx`). +func containsCall(expr ast.Expr) bool { + if expr == nil { + return false + } + switch e := expr.(type) { + case *ast.CallExpr: + return true + case *ast.BinaryExpr: + return containsCall(e.Left) || containsCall(e.Right) + case *ast.UnaryExpr: + return containsCall(e.X) + case *ast.SendExpr: + return true + } + return false +} + // Used for INDEX ON key and filename, where idents are field/file names, not variables. func exprToString(expr ast.Expr) string { switch e := expr.(type) {