diff --git a/compiler/gengo/gengo.go b/compiler/gengo/gengo.go index 3e31b41..cc44fbf 100644 --- a/compiler/gengo/gengo.go +++ b/compiler/gengo/gengo.go @@ -232,6 +232,45 @@ func (g *Generator) emitPushSymbol(name string) { g.writeln(fmt.Sprintf("t.PushSymbol(t.GetSym(&%s, %q))", v, name)) } +// negateLiteral produces a new literal that represents -lit. Handles +// INT and DOUBLE (as a textual prefix). Returns (nil, false) for +// non-numeric literals or an already-negative INT whose negation would +// overflow int64. +func negateLiteral(lit *ast.LiteralExpr) (*ast.LiteralExpr, bool) { + switch lit.Kind { + case token.INT: + n, err := strconv.ParseInt(lit.Value, 10, 64) + if err != nil { + return nil, false + } + // Guard: math.MinInt64 has no positive twin — let the VM's + // runtime coerce-to-double path handle it. + if n == -1<<63 { + return nil, false + } + return &ast.LiteralExpr{ + ValuePos: lit.ValuePos, + Kind: token.INT, + Value: strconv.FormatInt(-n, 10), + }, true + case token.DOUBLE: + // Syntactically prefix `-` or flip an existing leading `-`. + if strings.HasPrefix(lit.Value, "-") { + return &ast.LiteralExpr{ + ValuePos: lit.ValuePos, + Kind: token.DOUBLE, + Value: lit.Value[1:], + }, true + } + return &ast.LiteralExpr{ + ValuePos: lit.ValuePos, + Kind: token.DOUBLE, + Value: "-" + lit.Value, + }, true + } + return nil, false +} + // foldLiteralTree recursively folds BinaryExpr subtrees into LiteralExpr // where both operands eventually collapse to literals. Non-foldable // subtrees come back unchanged. Used as a preorder pre-pass so the @@ -1137,6 +1176,22 @@ func (g *Generator) emitAssign(a *ast.AssignExpr, locals localMap) { if idx, found := locals[strings.ToUpper(ident.Name)]; found { switch a.Op { case token.ASSIGN: + // Peephole: `x := x + ` / `x := x - ` → + // LocalAdd. Same result as `x += ` but lets the + // PRG side use the explicit form without penalty. + if be, ok := a.Right.(*ast.BinaryExpr); ok && + (be.Op == token.PLUS || be.Op == token.MINUS) { + if lid, isIdent := be.Left.(*ast.IdentExpr); isIdent { + if selfIdx, found := locals[strings.ToUpper(lid.Name)]; found && selfIdx == idx { + g.emitExpr(be.Right) + if be.Op == token.MINUS { + g.writeln("t.Negate()") + } + g.writeln(fmt.Sprintf("t.LocalAdd(%d)", idx)) + return + } + } + } g.emitExpr(a.Right) g.writeln(fmt.Sprintf("t.PopLocalFast(%d)", idx)) case token.PLUSEQ: @@ -1826,6 +1881,16 @@ func (g *Generator) emitExpr(expr ast.Expr) { g.emitBinaryOp(e.Op) } case *ast.UnaryExpr: + // Fold `-` to a negative literal at compile time so + // `-42` becomes PushInt(-42) instead of PushInt(42) + Negate(). + if e.Op == token.MINUS { + if lit, ok := e.X.(*ast.LiteralExpr); ok { + if neg, ok := negateLiteral(lit); ok { + g.emitLiteral(neg) + break + } + } + } g.emitExpr(e.X) g.emitUnaryOp(e.Op) case *ast.AssignExpr: