// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com) // All rights reserved. package gengo import ( "five/compiler/ast" "strings" ) // hasXBaseCommands checks if the file contains any xBase commands. func hasXBaseCommands(file *ast.File) bool { for _, d := range file.Decls { switch decl := d.(type) { case *ast.FuncDecl: if scanStmtsForXBase(decl.Body) { return true } case *ast.MethodDecl: if scanStmtsForXBase(decl.Body) { return true } } } return false } // xbaseFuncNames is the set of RTL functions that require the hbrdd import // in the generated Go code. When a PRG only uses these via function-call // syntax (no xBase commands like USE), the compiler still needs hbrdd. var xbaseFuncNames = map[string]bool{ "DBUSEAREA": true, "DBCREATE": true, "DBAPPEND": true, "DBSKIP": true, "DBGOTO": true, "DBGOTOP": true, "DBGOBOTTOM": true, "DBSEEK": true, "DBDELETE": true, "DBRECALL": true, "DBCLOSEAREA": true, "DBCLOSEALL": true, "DBSELECTAREA": true, "DBCOMMIT": true, "DBPACK": true, "DBZAP": true, "DBRLOCK": true, "DBRUNLOCK": true, "FLOCK": true, "DBUNLOCK": true, "FIELDGET": true, "FIELDPUT": true, "FIELDPOS": true, "FIELDNAME": true, "FCOUNT": true, "RECNO": true, "RECCOUNT": true, "EOF": true, "BOF": true, "FOUND": true, "DELETED": true, "LASTREC": true, "ALIAS": true, "ORDSETFOCUS": true, "ORDCOUNT": true, "ORDNAME": true, "ORDKEY": true, "USED": true, "SELECT": true, "SETDELETED": true, "FIVE_SQL": true, // FiveSql2 entry point also needs hbrdd } func scanExprForXBase(expr ast.Expr) bool { if expr == nil { return false } switch e := expr.(type) { case *ast.CallExpr: if ident, ok := e.Func.(*ast.IdentExpr); ok { if xbaseFuncNames[strings.ToUpper(ident.Name)] { return true } } for _, arg := range e.Args { if scanExprForXBase(arg) { return true } } case *ast.BinaryExpr: return scanExprForXBase(e.Left) || scanExprForXBase(e.Right) case *ast.AssignExpr: return scanExprForXBase(e.Right) case *ast.AliasExpr: // FIELD->NAME always needs hbrdd return true } return false } func scanStmtsForXBase(stmts []ast.Stmt) bool { for _, s := range stmts { switch v := s.(type) { case *ast.UseCmd, *ast.GoCmd, *ast.SkipCmd, *ast.SeekCmd, *ast.ReplaceCmd, *ast.AppendCmd, *ast.DeleteCmd, *ast.SelectCmd, *ast.IndexCmd, *ast.SetCmd: return true case *ast.ExprStmt: if scanExprForXBase(v.X) { return true } case *ast.IfStmt: if scanStmtsForXBase(v.Body) || scanStmtsForXBase(v.ElseBody) { return true } for _, ei := range v.ElseIfs { if scanStmtsForXBase(ei.Body) { return true } } case *ast.ForStmt: if scanStmtsForXBase(v.Body) { return true } case *ast.ForEachStmt: if scanStmtsForXBase(v.Body) { return true } case *ast.DoWhileStmt: if scanStmtsForXBase(v.Body) { return true } case *ast.SeqStmt: if scanStmtsForXBase(v.Body) || scanStmtsForXBase(v.RecoverBody) { return true } case *ast.SwitchStmt: for _, c := range v.Cases { if scanStmtsForXBase(c.Body) { return true } } } } return false }