Files
five/compiler/gengo/gen_util.go
Charles KWON OhJun b7028791d6 fix: 5 seek/dbf bugs — 77/77 thorough Harbour compatibility
1. SOFTSEEK: use idx.CurRecNo() for positioning (was checking recNo > 0)
   - SEEK with SET SOFTSEEK ON now positions at next higher key
   - SEEK command reads SET SOFTSEEK at runtime (was compile-time only)
   - rtlDbSeek defaults to GetSetSoftSeek() when no explicit param

2. SET DELETED ON + INDEX: SkipIndexed skips deleted records
   - GoTopIndexed: skip deleted record at top position
   - SkipIndexed: inner loop continues past deleted records

3. Compound key (CITY+NAME): field name TrimSpace before lookup
   - evalKeyExprInner: TrimSpace on fieldName after FIELD-> strip
   - Fixed "CITY " != "CITY" mismatch from + operator splitting

4. SET INDEX TO filename: treated as string, not variable
   - gengo uses exprToString for SET INDEX TO (was emitExpr)
   - Prevents identifier being resolved as local variable

5. hasXBaseCommands: recursive scan into nested blocks
   - BEGIN SEQUENCE, IF, FOR, DO WHILE, SWITCH bodies now scanned
   - Fixes missing hbrdd import for DB commands inside blocks

Thorough test: 77 items (14 sections) covering exact/partial/soft seek,
SET DELETED, duplicate keys, numeric keys, compound keys, empty/single
table, state consistency, order switching, full traversal — all identical.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 14:08:51 +09:00

64 lines
1.3 KiB
Go

// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com)
// All rights reserved.
package gengo
import "five/compiler/ast"
// hasXBaseCommands checks if the file contains any xBase commands.
func hasXBaseCommands(file *ast.File) bool {
for _, d := range file.Decls {
fn, ok := d.(*ast.FuncDecl)
if !ok {
continue
}
if scanStmtsForXBase(fn.Body) {
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.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
}