Skip to content

Commit

Permalink
Merge pull request #286 from xushiwei/r
Browse files Browse the repository at this point in the history
exec/golang: rational number
  • Loading branch information
xushiwei authored Jun 14, 2020
2 parents 7048c56 + 926b7fb commit e58baf8
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 16 deletions.
2 changes: 2 additions & 0 deletions cl/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ func compileExpr(ctx *blockCtx, expr ast.Expr) func() {
return compileSliceLit(ctx, v)
case *ast.FuncLit:
return compileFuncLit(ctx, v)
case *ast.ParenExpr:
return compileExpr(ctx, v.X)
case *ast.ListComprehensionExpr:
return compileListComprehensionExpr(ctx, v)
case *ast.MapComprehensionExpr:
Expand Down
33 changes: 33 additions & 0 deletions cl/expr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1142,3 +1142,36 @@ func TestRational3(t *testing.T) {
}

// -----------------------------------------------------------------------------

var fsTestRational4 = asttest.NewSingleFileFS("/foo", "bar.gop", `
a := 3/4r
b := 5/7r
if a > b {
a = a + 1
}
a
`)

func TestRational4(t *testing.T) {
fset := token.NewFileSet()
pkgs, err := parser.ParseFSDir(fset, fsTestRational4, "/foo", nil, 0)
if err != nil || len(pkgs) != 1 {
t.Fatal("ParseFSDir failed:", err, len(pkgs))
}

bar := pkgs["main"]
b := exec.NewBuilder(nil)
_, _, err = newPackage(b, bar, fset)
if err != nil {
t.Fatal("Compile failed:", err)
}
code := b.Resolve()

ctx := exec.NewContext(code)
ctx.Exec(0, code.Len())
if v := ctx.Get(-1); v.(*big.Rat).Cmp(big.NewRat(7, 4)) != 0 {
t.Fatal("v:", v)
}
}

// -----------------------------------------------------------------------------
6 changes: 3 additions & 3 deletions exec/bytecode/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,9 +200,9 @@ var (

func execQuoBigInt(i Instr, p *Context) {
n := len(p.data)
x := new(big.Rat).SetInt(p.data[n-2].(*big.Int))
y := new(big.Rat).SetInt(p.data[n-1].(*big.Int))
p.data[n-2] = x.Quo(x, y)
x := p.data[n-2].(*big.Int)
y := p.data[n-1].(*big.Int)
p.data[n-2] = new(big.Rat).SetFrac(x, y)
p.data = p.data[:n-1]
}

Expand Down
1 change: 1 addition & 0 deletions exec/golang/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ var (
gopRet = Ident("_gop_ret")
appendIden = Ident("append")
makeIden = Ident("make")
newIden = Ident("new")
nilIden = Ident("nil")
)

Expand Down
144 changes: 131 additions & 13 deletions exec/golang/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package golang
import (
"go/ast"
"go/token"
"math/big"
"reflect"
"strconv"
"strings"
Expand Down Expand Up @@ -110,6 +111,79 @@ func ComplexConst(v complex128) ast.Expr {
return &ast.ParenExpr{X: BinaryOp(token.ADD, x, y)}
}

// NewGopkgType instr
func NewGopkgType(p *Builder, pkgPath, typName string) ast.Expr {
typ := p.GoSymIdent(pkgPath, typName)
args := []ast.Expr{typ}
return &ast.CallExpr{Fun: newIden, Args: args}
}

func valBySetString(p *Builder, typ reflect.Type, x ast.Expr, args ...ast.Expr) ast.Expr {
setString := &ast.SelectorExpr{X: x, Sel: Ident("SetString")}
setStringCall := &ast.CallExpr{Fun: setString, Args: args}
stmt := &ast.AssignStmt{
Lhs: []ast.Expr{gopRet, unnamedVar},
Tok: token.ASSIGN,
Rhs: []ast.Expr{setStringCall},
}
fldOut := &ast.Field{
Names: []*ast.Ident{gopRet},
Type: Type(p, typ),
}
typFun := &ast.FuncType{
Params: &ast.FieldList{Opening: 1, Closing: 1},
Results: &ast.FieldList{Opening: 1, Closing: 1, List: []*ast.Field{fldOut}},
}
stmtReturn := &ast.ReturnStmt{}
return &ast.CallExpr{
Fun: &ast.FuncLit{
Type: typFun,
Body: &ast.BlockStmt{List: []ast.Stmt{stmt, stmtReturn}},
},
}
}

// BigIntConst instr
func BigIntConst(p *Builder, v *big.Int) ast.Expr {
if v.IsInt64() {
newInt := p.GoSymIdent("math/big", "NewInt")
args := []ast.Expr{IntConst(v.Int64())}
return &ast.CallExpr{Fun: newInt, Args: args}
}
bigInt := NewGopkgType(p, "math/big", "Int")
return valBySetString(p, exec.TyBigInt, bigInt, StringConst(v.String()), IntConst(10))
}

// BigRatConst instr
func BigRatConst(p *Builder, v *big.Rat) ast.Expr {
a, b := v.Num(), v.Denom()
if a.IsInt64() && b.IsInt64() {
newRat := p.GoSymIdent("math/big", "NewRat")
args := []ast.Expr{IntConst(a.Int64()), IntConst(b.Int64())}
return &ast.CallExpr{Fun: newRat, Args: args}
}
rat := NewGopkgType(p, "math/big", "Rat")
setFrac := &ast.SelectorExpr{X: rat, Sel: Ident("SetFrac")}
args := []ast.Expr{BigIntConst(p, a), BigIntConst(p, b)}
return &ast.CallExpr{Fun: setFrac, Args: args}
}

// BigFloatConst instr
func BigFloatConst(p *Builder, v *big.Float) ast.Expr {
val, acc := v.Float64()
if acc == big.Exact {
newFloat := p.GoSymIdent("math/big", "NewFloat")
args := []ast.Expr{FloatConst(val)}
return &ast.CallExpr{Fun: newFloat, Args: args}
}
prec := v.Prec()
sval := v.Text('g', int(prec))
bigFlt := NewGopkgType(p, "math/big", "Float")
setPrec := &ast.SelectorExpr{X: bigFlt, Sel: Ident("SetPrec")}
setPrecCall := &ast.CallExpr{Fun: setPrec, Args: []ast.Expr{IntConst(int64(prec))}}
return valBySetString(p, exec.TyBigFloat, setPrecCall, StringConst(sval))
}

// Const instr
func Const(p *Builder, val interface{}) ast.Expr {
if val == nil {
Expand Down Expand Up @@ -145,11 +219,21 @@ func Const(p *Builder, val interface{}) ast.Expr {
}
return expr
}
if kind == reflect.Bool {
switch kind {
case reflect.Bool:
if val.(bool) {
return Ident("true")
}
return Ident("false")
case reflect.Ptr:
switch v.Type() {
case exec.TyBigRat:
return BigRatConst(p, val.(*big.Rat))
case exec.TyBigInt:
return BigIntConst(p, val.(*big.Int))
case exec.TyBigFloat:
return BigFloatConst(p, val.(*big.Float))
}
}
log.Panicln("Const: value type is unknown -", v.Type())
return nil
Expand All @@ -161,37 +245,71 @@ func (p *Builder) Push(val interface{}) *Builder {
return p
}

// UnaryOp instr
func (p *Builder) UnaryOp(tok token.Token) *Builder {
func (p *Builder) bigBuiltinOp(kind exec.Kind, op exec.Operator) *Builder {
val := p.rhs.Pop().(ast.Expr)
if op >= exec.OpLT && op <= exec.OpNE {
x := p.rhs.Pop().(ast.Expr)
bigOp := &ast.SelectorExpr{X: x, Sel: Ident("Cmp")}
bigOpCall := &ast.CallExpr{Fun: bigOp, Args: []ast.Expr{val}}
p.rhs.Push(&ast.BinaryExpr{X: bigOpCall, Y: IntConst(0), Op: opTokens[op]})
return p
}
method := opOpMethods[op]
if method == "" {
log.Panicln("bigBuiltinOp: unsupported op -", op)
}
t := exec.TypeFromKind(kind).Elem()
typ := NewGopkgType(p, t.PkgPath(), t.Name())
bigOp := &ast.SelectorExpr{X: typ, Sel: Ident(method)}
oi := op.GetInfo()
if oi.InSecond == 0 {
p.rhs.Push(&ast.CallExpr{Fun: bigOp, Args: []ast.Expr{val}})
return p
}
x := p.rhs.Pop().(ast.Expr)
p.rhs.Push(&ast.UnaryExpr{Op: tok, X: x})
p.rhs.Push(&ast.CallExpr{Fun: bigOp, Args: []ast.Expr{x, val}})
return p
}

// BinaryOp instr
func (p *Builder) BinaryOp(tok token.Token) *Builder {
y := p.rhs.Pop().(ast.Expr)
x := p.rhs.Pop().(ast.Expr)
p.rhs.Push(&ast.BinaryExpr{Op: tok, X: x, Y: y})
return p
var opOpMethods = [...]string{
exec.OpAdd: "Add",
exec.OpSub: "Sub",
exec.OpMul: "Mul",
exec.OpQuo: "Quo",
exec.OpMod: "Mod",
exec.OpAnd: "And",
exec.OpOr: "Or",
exec.OpXor: "Xor",
exec.OpAndNot: "AndNot",
exec.OpLsh: "Lsh",
exec.OpRsh: "Rsh",
exec.OpNeg: "Neg",
exec.OpBitNot: "Not",
}

// BinaryOp instr
func BinaryOp(tok token.Token, x, y ast.Expr) *ast.BinaryExpr {
func BinaryOp(tok token.Token, x, y ast.Expr) ast.Expr {
return &ast.BinaryExpr{Op: tok, X: x, Y: y}
}

// BuiltinOp instr
func (p *Builder) BuiltinOp(kind exec.Kind, op exec.Operator) *Builder {
if kind >= exec.BigInt {
return p.bigBuiltinOp(kind, op)
}
tok := opTokens[op]
if tok == token.ILLEGAL {
log.Panicln("BuiltinOp: unsupported op -", op)
}
oi := op.GetInfo()
val := p.rhs.Pop().(ast.Expr)
if oi.InSecond == 0 {
return p.UnaryOp(tok)
p.rhs.Push(&ast.UnaryExpr{Op: tok, X: val})
return p
}
return p.BinaryOp(tok)
x := p.rhs.Pop().(ast.Expr)
p.rhs.Push(&ast.BinaryExpr{Op: tok, X: x, Y: val})
return p
}

var opTokens = [...]token.Token{
Expand Down
17 changes: 17 additions & 0 deletions exec/golang/testdata/19.rational/rational.gop
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
a := 1r << 65 // bigint, large than int64
b := 4/5r // bigrat
c := b - 1/3r + 3 * 1/2r
println(a, b, c)

x := 3.14159265358979323846264338327950288419716939937510582097494459r
x *= 2
println(x)

y := 1r
z := (1r << 65) / 60
println(y, z)

if b < c {
b = -b
}
println(b)
44 changes: 44 additions & 0 deletions exec/golang/var.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,52 @@ func (p *Builder) AddrVar(v exec.Var) *Builder {
return p
}

func (p *Builder) bigAddrOp(kind exec.Kind, op exec.AddrOperator) *Builder {
if op == exec.OpAddrVal {
return p
}
method := addropMethods[op]
if method == "" {
log.Panicln("bigAddrOp: unknown op -", op)
}
var expr ast.Expr
var x = p.rhs.Pop()
var val = p.rhs.Pop().(ast.Expr)
switch v := x.(type) {
case *ast.UnaryExpr:
if v.Op != token.AND {
log.Panicln("bigAddrOp: unknown x expr -", reflect.TypeOf(x))
}
bigOp := &ast.SelectorExpr{X: v.X, Sel: Ident(method)}
expr = &ast.CallExpr{Fun: bigOp, Args: []ast.Expr{v.X, val}}
default:
log.Panicln("bigAddrOp: todo")
}
p.rhs.Push(&ast.ExprStmt{X: expr})
return p
}

var addropMethods = [...]string{
exec.OpAddAssign: "Add",
exec.OpSubAssign: "Sub",
exec.OpMulAssign: "Mul",
exec.OpQuoAssign: "Quo",
exec.OpModAssign: "Mod",
exec.OpAndAssign: "And",
exec.OpOrAssign: "Or",
exec.OpXorAssign: "Xor",
exec.OpAndNotAssign: "AndNot",
exec.OpLshAssign: "Lsh",
exec.OpRshAssign: "Rsh",
exec.OpInc: "Add",
exec.OpDec: "Sub",
}

// AddrOp instr
func (p *Builder) AddrOp(kind exec.Kind, op exec.AddrOperator) *Builder {
if kind >= exec.BigInt {
return p.bigAddrOp(kind, op)
}
if op == exec.OpAddrVal {
p.rhs.Push(&ast.StarExpr{
X: p.rhs.Pop().(ast.Expr),
Expand Down

0 comments on commit e58baf8

Please sign in to comment.