perf(gengo): fold .NOT. <literal> at compile time

`.NOT. .T.` / `.NOT. .F.` emit PushBool directly instead of
pushing the source bool and calling Not(). boolLiteralValue also
sees through an outer NOT, so `IF !.F.` now triggers the full
dead-branch pass (no PopLogical wrapper either).

FiveSql2 43/43, Harbour compat 56/56.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-18 08:42:08 +09:00
parent 1b6d913905
commit c3a9eb33a4

View File

@@ -1265,9 +1265,17 @@ func (g *Generator) emitCallAsStmt(call *ast.CallExpr, locals localMap) {
g.writeln(fmt.Sprintf("t.Do(%d)", len(call.Args)))
}
// boolLiteralValue returns (value, true) if e is a bare .T./.F. literal.
// Used by emitIf to skip dead branches.
// boolLiteralValue returns (value, true) if e reduces to a .T./.F.
// literal at compile time. Sees through an outer `.NOT.` so expressions
// like `!.F.` also collapse. Used by emitIf to skip dead branches and
// by the AND/OR short-circuit emitter.
func boolLiteralValue(e ast.Expr) (bool, bool) {
if u, ok := e.(*ast.UnaryExpr); ok && u.Op == token.NOT {
if v, ok := boolLiteralValue(u.X); ok {
return !v, true
}
return false, false
}
lit, ok := e.(*ast.LiteralExpr)
if !ok {
return false, false
@@ -1988,6 +1996,17 @@ func (g *Generator) emitExpr(expr ast.Expr) {
}
}
}
// Fold `.NOT. .T.` → .F. and `.NOT. .F.` → .T. at compile time.
if e.Op == token.NOT {
if v, ok := boolLiteralValue(e.X); ok {
if v {
g.writeln("t.PushBool(false)")
} else {
g.writeln("t.PushBool(true)")
}
break
}
}
g.emitExpr(e.X)
g.emitUnaryOp(e.Op)
case *ast.AssignExpr: