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>
64 lines
1.3 KiB
Go
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
|
|
}
|