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:
@@ -491,23 +491,64 @@ func (g *Generator) emitStmt(stmt ast.Stmt, locals localMap) {
|
||||
g.indent--
|
||||
g.writeln("}")
|
||||
case *ast.SetCmd:
|
||||
upper := strings.ToUpper(s.Setting)
|
||||
|
||||
// Boolean SET toggles — call RTL Set function, no workarea needed
|
||||
setFuncMap := map[string]string{
|
||||
"DELETED": "SETDELETED",
|
||||
"EXACT": "SETEXACT",
|
||||
"SOFTSEEK": "SETSOFTSEEK",
|
||||
"EXCLUSIVE": "SETEXCLUSIVE",
|
||||
"FIXED": "SETFIXED",
|
||||
"CANCEL": "SETCANCEL",
|
||||
"BELL": "SETBELL",
|
||||
"CONFIRM": "SETCONFIRM",
|
||||
"INSERT": "SETINSERT",
|
||||
"ESCAPE": "SETESCAPE",
|
||||
"WRAP": "SETWRAP",
|
||||
}
|
||||
if funcName, ok := setFuncMap[upper]; ok {
|
||||
onOff := strings.ToUpper(s.Extra)
|
||||
if onOff == "ON" || onOff == "OFF" {
|
||||
val := "true"
|
||||
if onOff == "OFF" {
|
||||
val = "false"
|
||||
}
|
||||
g.writeln(fmt.Sprintf(`t.PushSymbol(t.VM().FindSymbol(%q))`, funcName))
|
||||
g.writeln("t.PushNil()")
|
||||
g.writeln(fmt.Sprintf("t.PushBool(%s)", val))
|
||||
g.writeln("t.Do(1)")
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// Value SET commands — SET DATE/DECIMALS/EPOCH TO expr
|
||||
valueFuncMap := map[string]string{
|
||||
"DATE": "__SETDATEFORMAT",
|
||||
"DECIMALS": "SETDECIMALS",
|
||||
"EPOCH": "SETEPOCH",
|
||||
}
|
||||
if funcName, ok := valueFuncMap[upper]; ok && s.Expr != nil {
|
||||
g.writeln(fmt.Sprintf(`t.PushSymbol(t.VM().FindSymbol(%q))`, funcName))
|
||||
g.writeln("t.PushNil()")
|
||||
g.emitExpr(s.Expr)
|
||||
g.writeln("t.Do(1)")
|
||||
break
|
||||
}
|
||||
|
||||
// Workarea-specific SET commands
|
||||
g.writeln("{")
|
||||
g.indent++
|
||||
g.writeln("wa := t.WA.(*hbrdd.WorkAreaManager)")
|
||||
g.writeln("if area := wa.Current(); area != nil {")
|
||||
g.indent++
|
||||
upper := strings.ToUpper(s.Setting)
|
||||
switch upper {
|
||||
case "FILTER":
|
||||
if s.Expr != nil {
|
||||
g.writeln("if flt, ok := area.(hbrdd.Filterer); ok {")
|
||||
g.indent++
|
||||
g.emitExpr(s.Expr)
|
||||
g.writeln(`flt.SetFilter(t.Pop2().AsString(), nil)`)
|
||||
g.indent--
|
||||
g.writeln("}")
|
||||
g.writeln(`area.SetFilter(t.Pop2().AsString(), nil)`)
|
||||
} else {
|
||||
g.writeln("if flt, ok := area.(hbrdd.Filterer); ok { flt.ClearFilter() }")
|
||||
g.writeln("area.ClearFilter()")
|
||||
}
|
||||
case "ORDER":
|
||||
if s.Expr != nil {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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() }
|
||||
|
||||
Reference in New Issue
Block a user