feat: SET DELETED filtering, SEEK/LOCATE/CONTINUE, SET command codegen

- skipFilter: skip deleted records in GoTop/GoBottom/Skip when SET DELETED ON
- hbrdd.IsSetDeleted callback: avoids circular import hbrdd→hbrtl
- Parser: capture ON/OFF for boolean SET commands (DELETED, EXACT, SOFTSEEK, etc.)
- Parser: capture TO expr for SET DATE/DECIMALS/EPOCH
- Gengo: emit proper t.Do() calls for 11 SET toggles + 3 value SETs
- stmtSet: was stub (skipToEOL), now calls parseSet()
- RTL: register 11 SET toggle functions (SETDELETED, SETEXACT, etc.)
- RTL: DBLOCATE/DBCONTINUE for sequential search
- RTL: DBSETFILTER/DBCLEARFILTER/DBFILTER
- PadL/PadR: support 3rd param fill character
- Area interface: added SetFound, SetLocate, LocateBlock, filter methods
- MemRDD: implements new Area interface methods
- Comprehensive PRG test: test_search.prg (7 test suites all pass)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-02 22:33:59 +09:00
parent 08c0ef13d4
commit 21fd9dc65c
12 changed files with 651 additions and 30 deletions

View File

@@ -1706,10 +1706,23 @@ func (p *Parser) parseSet() *ast.SetCmd {
var extra string
// SET commands: consume everything until end of line.
// Values like "GR+/B, W+/BG" can't be parsed as expressions.
// SET FILTER TO is special — the condition IS an expression.
// Boolean toggles: SET DELETED ON/OFF, SET EXACT ON/OFF, etc.
// Value settings: SET FILTER TO expr, SET ORDER TO n, SET DATE TO fmt
upperSetting := strings.ToUpper(setting)
if p.match(token.TO) {
// Check for ON/OFF boolean toggle
booleanSets := map[string]bool{
"DELETED": true, "EXACT": true, "SOFTSEEK": true, "EXCLUSIVE": true,
"FIXED": true, "CANCEL": true, "BELL": true, "CONFIRM": true,
"INSERT": true, "ESCAPE": true, "WRAP": true, "INTENSITY": true,
"SCOREBOARD": true, "CONSOLE": true, "ALTERNATE": true, "PRINTER": true,
}
if booleanSets[upperSetting] {
if p.current.Kind != token.NEWLINE && p.current.Kind != token.EOF {
extra = strings.ToUpper(p.expectMethodName().Literal)
}
} else if p.match(token.TO) {
if upperSetting == "FILTER" || upperSetting == "RELATION" || upperSetting == "ORDER" || upperSetting == "INDEX" {
if p.current.Kind != token.NEWLINE && p.current.Kind != token.EOF {
expr = p.parseExpr()
@@ -1718,6 +1731,10 @@ func (p *Parser) parseSet() *ast.SetCmd {
p.advance()
extra = p.expectMethodName().Literal
}
} else if upperSetting == "DATE" || upperSetting == "DECIMALS" || upperSetting == "EPOCH" {
if p.current.Kind != token.NEWLINE && p.current.Kind != token.EOF {
expr = p.parseExpr()
}
}
}

View File

@@ -241,10 +241,7 @@ func (p *Parser) stmtRecallPackZap() ast.Stmt {
}
func (p *Parser) stmtSet() ast.Stmt {
// SET command — skip to EOL (SET COLOR, SET FILTER, SET ORDER, etc.)
p.skipToEndOfLine()
p.expectEndOfStmt()
return &ast.ExprStmt{X: &ast.LiteralExpr{Kind: token.NIL_LIT, Value: "NIL"}}
return p.parseSet()
}
func (p *Parser) stmtDefer() ast.Stmt { return p.parseDefer() }