internal/core/adt: move decimal logic into one place
Or at least a start.
This prepares for the new numbering system, where
1 and 1.0 can be used interchangeably.
Change-Id: Ic311be58d59f75dc533b1bcda2ceb5bda7915056
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/7881
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/context.go b/cue/context.go
index 1afa832..33d213d 100644
--- a/cue/context.go
+++ b/cue/context.go
@@ -18,28 +18,18 @@
"cuelang.org/go/internal/core/adt"
"cuelang.org/go/internal/core/debug"
"cuelang.org/go/internal/core/eval"
- "github.com/cockroachdb/apd/v2"
)
// context manages evaluation state.
type context struct {
opCtx *adt.OpContext
- *apd.Context
*index
}
-var baseContext apd.Context
-
-func init() {
- baseContext = apd.BaseContext
- baseContext.Precision = 24
-}
-
// newContext returns a new evaluation context.
func (idx *index) newContext() *context {
c := &context{
- Context: &baseContext,
- index: idx,
+ index: idx,
}
if idx != nil {
c.opCtx = eval.NewContext(idx.Runtime, nil)
diff --git a/internal/core/adt/binop.go b/internal/core/adt/binop.go
index 98eab96..4c87a49 100644
--- a/internal/core/adt/binop.go
+++ b/internal/core/adt/binop.go
@@ -16,19 +16,9 @@
import (
"bytes"
- "math/big"
"strings"
-
- "github.com/cockroachdb/apd/v2"
)
-var apdCtx apd.Context
-
-func init() {
- apdCtx = apd.BaseContext
- apdCtx.Precision = 24
-}
-
// BinOp handles all operations except AndOp and OrOp. This includes processing
// unary comparators such as '<4' and '=~"foo"'.
//
@@ -169,7 +159,7 @@
case AddOp:
switch {
case leftKind&NumKind != 0 && rightKind&NumKind != 0:
- return numOp(c, apdCtx.Add, left, right, AddOp)
+ return c.Add(c.Num(left, op), c.Num(right, op))
case leftKind == StringKind && rightKind == StringKind:
return c.NewString(c.StringValue(left) + c.StringValue(right))
@@ -217,13 +207,13 @@
}
case SubtractOp:
- return numOp(c, apdCtx.Sub, left, right, op)
+ return c.Sub(c.Num(left, op), c.Num(right, op))
case MultiplyOp:
switch {
// float
case leftKind&NumKind != 0 && rightKind&NumKind != 0:
- return numOp(c, apdCtx.Mul, left, right, op)
+ return c.Mul(c.Num(left, op), c.Num(right, op))
case leftKind == StringKind && rightKind == IntKind:
const as = "string multiplication"
@@ -275,47 +265,27 @@
case FloatQuotientOp:
if leftKind&NumKind != 0 && rightKind&NumKind != 0 {
- v := numOp(c, apdCtx.Quo, left, right, op)
- if n, ok := v.(*Num); ok {
- n.K = FloatKind
- }
- return v
+ return c.Quo(c.Num(left, op), c.Num(right, op))
}
case IntDivideOp:
if leftKind&IntKind != 0 && rightKind&IntKind != 0 {
- y := c.Num(right, op)
- if y.X.IsZero() {
- return c.NewErrf("division by zero")
- }
- return intOp(c, (*big.Int).Div, c.Num(left, op), y)
+ return c.IntDiv(c.Num(left, op), c.Num(right, op))
}
case IntModuloOp:
if leftKind&IntKind != 0 && rightKind&IntKind != 0 {
- y := c.Num(right, op)
- if y.X.IsZero() {
- return c.NewErrf("division by zero")
- }
- return intOp(c, (*big.Int).Mod, c.Num(left, op), y)
+ return c.IntMod(c.Num(left, op), c.Num(right, op))
}
case IntQuotientOp:
if leftKind&IntKind != 0 && rightKind&IntKind != 0 {
- y := c.Num(right, op)
- if y.X.IsZero() {
- return c.NewErrf("division by zero")
- }
- return intOp(c, (*big.Int).Quo, c.Num(left, op), y)
+ return c.IntQuo(c.Num(left, op), c.Num(right, op))
}
case IntRemainderOp:
if leftKind&IntKind != 0 && rightKind&IntKind != 0 {
- y := c.Num(right, op)
- if y.X.IsZero() {
- return c.NewErrf("division by zero")
- }
- return intOp(c, (*big.Int).Rem, c.Num(left, op), y)
+ return c.IntRem(c.Num(left, op), c.Num(right, op))
}
}
@@ -341,45 +311,3 @@
}
return c.newBool(result)
}
-
-type numFunc func(z, x, y *apd.Decimal) (apd.Condition, error)
-
-func numOp(c *OpContext, fn numFunc, a, b Value, op Op) Value {
- var d apd.Decimal
- x := c.Num(a, op)
- y := c.Num(b, op)
- cond, err := fn(&d, &x.X, &y.X)
- if err != nil {
- return c.NewErrf("failed arithmetic: %v", err)
- }
- if cond.DivisionByZero() {
- return c.NewErrf("division by zero")
- }
- k := x.Kind() & y.Kind()
- if k == 0 {
- k = FloatKind
- }
- return c.NewNum(&d, k)
-}
-
-type intFunc func(z, x, y *big.Int) *big.Int
-
-func intOp(c *OpContext, fn intFunc, a, b *Num) Value {
- var d apd.Decimal
-
- var x, y apd.Decimal
- _, _ = apdCtx.RoundToIntegralValue(&x, &a.X)
- if x.Negative {
- x.Coeff.Neg(&x.Coeff)
- }
- _, _ = apdCtx.RoundToIntegralValue(&y, &b.X)
- if y.Negative {
- y.Coeff.Neg(&y.Coeff)
- }
- fn(&d.Coeff, &x.Coeff, &y.Coeff)
- if d.Coeff.Sign() < 0 {
- d.Coeff.Neg(&d.Coeff)
- d.Negative = true
- }
- return c.NewNum(&d, IntKind)
-}
diff --git a/internal/core/adt/context.go b/internal/core/adt/context.go
index bac1a27..54a4f03 100644
--- a/internal/core/adt/context.go
+++ b/internal/core/adt/context.go
@@ -976,9 +976,9 @@
}
}
-// NewNum creates a new number of the given kind. It reports an error value
+// newNum creates a new number of the given kind. It reports an error value
// instead if any error occurred.
-func (c *OpContext) NewNum(d *apd.Decimal, k Kind, sources ...Node) Value {
+func (c *OpContext) newNum(d *apd.Decimal, k Kind, sources ...Node) Value {
if c.HasErr() {
return c.Err()
}
diff --git a/internal/core/adt/decimal.go b/internal/core/adt/decimal.go
new file mode 100644
index 0000000..e7eba38
--- /dev/null
+++ b/internal/core/adt/decimal.go
@@ -0,0 +1,131 @@
+// Copyright 2020 CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package adt
+
+import (
+ "math/big"
+
+ "github.com/cockroachdb/apd/v2"
+)
+
+var apdCtx apd.Context
+
+func init() {
+ apdCtx = apd.BaseContext
+ apdCtx.Precision = 24
+}
+
+func (n *Num) Impl() *apd.Decimal {
+ return &n.X
+}
+
+func (n *Num) Negative() bool {
+ return n.X.Negative
+}
+
+func (a *Num) Cmp(b *Num) int {
+ return a.X.Cmp(&b.X)
+}
+
+func (c *OpContext) Add(a, b *Num) Value {
+ return numOp(c, apdCtx.Add, a, b)
+}
+
+func (c *OpContext) Sub(a, b *Num) Value {
+ return numOp(c, apdCtx.Sub, a, b)
+}
+
+func (c *OpContext) Mul(a, b *Num) Value {
+ return numOp(c, apdCtx.Mul, a, b)
+}
+
+func (c *OpContext) Quo(a, b *Num) Value {
+ v := numOp(c, apdCtx.Quo, a, b)
+ if n, ok := v.(*Num); ok {
+ n.K = FloatKind
+ }
+ return v
+}
+
+func (c *OpContext) Pow(a, b *Num) Value {
+ return numOp(c, apdCtx.Pow, a, b)
+}
+
+type numFunc func(z, x, y *apd.Decimal) (apd.Condition, error)
+
+func numOp(c *OpContext, fn numFunc, x, y *Num) Value {
+ var d apd.Decimal
+
+ cond, err := fn(&d, &x.X, &y.X)
+
+ if err != nil {
+ return c.NewErrf("failed arithmetic: %v", err)
+ }
+
+ if cond.DivisionByZero() {
+ return c.NewErrf("division by zero")
+ }
+
+ k := x.Kind() & y.Kind()
+ if k == 0 {
+ k = FloatKind
+ }
+ return c.newNum(&d, k)
+}
+
+func (c *OpContext) IntDiv(a, b *Num) Value {
+ return intDivOp(c, (*big.Int).Div, a, b)
+}
+
+func (c *OpContext) IntMod(a, b *Num) Value {
+ return intDivOp(c, (*big.Int).Mod, a, b)
+}
+
+func (c *OpContext) IntQuo(a, b *Num) Value {
+ return intDivOp(c, (*big.Int).Quo, a, b)
+}
+
+func (c *OpContext) IntRem(a, b *Num) Value {
+ return intDivOp(c, (*big.Int).Rem, a, b)
+}
+
+type intFunc func(z, x, y *big.Int) *big.Int
+
+func intDivOp(c *OpContext, fn intFunc, a, b *Num) Value {
+ if b.X.IsZero() {
+ return c.NewErrf("division by zero")
+ }
+
+ var x, y apd.Decimal
+ _, _ = apdCtx.RoundToIntegralValue(&x, &a.X)
+ if x.Negative {
+ x.Coeff.Neg(&x.Coeff)
+ }
+ _, _ = apdCtx.RoundToIntegralValue(&y, &b.X)
+ if y.Negative {
+ y.Coeff.Neg(&y.Coeff)
+ }
+
+ var d apd.Decimal
+
+ fn(&d.Coeff, &x.Coeff, &y.Coeff)
+
+ if d.Coeff.Sign() < 0 {
+ d.Coeff.Neg(&d.Coeff)
+ d.Negative = true
+ }
+
+ return c.newNum(&d, IntKind)
+}
diff --git a/internal/core/adt/simplify.go b/internal/core/adt/simplify.go
index 2fd6480..bd5edc3 100644
--- a/internal/core/adt/simplify.go
+++ b/internal/core/adt/simplify.go
@@ -82,17 +82,17 @@
// Readjust bounds for integers.
if x.Op == GreaterEqualOp {
// >=3.4 ==> >=4
- _, _ = apd.BaseContext.Ceil(&lo, &a.X)
+ _, _ = apdCtx.Ceil(&lo, &a.X)
} else {
// >3.4 ==> >3
- _, _ = apd.BaseContext.Floor(&lo, &a.X)
+ _, _ = apdCtx.Floor(&lo, &a.X)
}
if y.Op == LessEqualOp {
// <=2.3 ==> <= 2
- _, _ = apd.BaseContext.Floor(&hi, &b.X)
+ _, _ = apdCtx.Floor(&hi, &b.X)
} else {
// <2.3 ==> < 3
- _, _ = apd.BaseContext.Ceil(&hi, &b.X)
+ _, _ = apdCtx.Ceil(&hi, &b.X)
}
}
@@ -133,23 +133,23 @@
case diff == 1:
if k&FloatKind == 0 {
if x.Op == GreaterEqualOp && y.Op == LessThanOp {
- return ctx.NewNum(&lo, k&NumKind, x, y)
+ return ctx.newNum(&lo, k&NumKind, x, y)
}
if x.Op == GreaterThanOp && y.Op == LessEqualOp {
- return ctx.NewNum(&hi, k&NumKind, x, y)
+ return ctx.newNum(&hi, k&NumKind, x, y)
}
}
case diff == 2:
if k&FloatKind == 0 && x.Op == GreaterThanOp && y.Op == LessThanOp {
_, _ = apd.BaseContext.Add(&d, d.SetInt64(1), &lo)
- return ctx.NewNum(&d, k&NumKind, x, y)
+ return ctx.newNum(&d, k&NumKind, x, y)
}
case diff == 0:
if x.Op == GreaterEqualOp && y.Op == LessEqualOp {
- return ctx.NewNum(&lo, k&NumKind, x, y)
+ return ctx.newNum(&lo, k&NumKind, x, y)
}
fallthrough
diff --git a/internal/core/compile/builtin.go b/internal/core/compile/builtin.go
index 7a0af90..c1edc7e 100644
--- a/internal/core/compile/builtin.go
+++ b/internal/core/compile/builtin.go
@@ -15,11 +15,8 @@
package compile
import (
- "math/big"
-
"cuelang.org/go/cue/errors"
"cuelang.org/go/internal/core/adt"
- "github.com/cockroachdb/apd/v2"
)
// This file contains predeclared builtins.
@@ -153,7 +150,7 @@
Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
const name = "argument to div builtin"
- return intDivOp(c, (*big.Int).Div, name, args)
+ return intDivOp(c, (*adt.OpContext).IntDiv, name, args)
},
}
@@ -164,7 +161,7 @@
Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
const name = "argument to mod builtin"
- return intDivOp(c, (*big.Int).Mod, name, args)
+ return intDivOp(c, (*adt.OpContext).IntMod, name, args)
},
}
@@ -175,7 +172,7 @@
Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
const name = "argument to quo builtin"
- return intDivOp(c, (*big.Int).Quo, name, args)
+ return intDivOp(c, (*adt.OpContext).IntQuo, name, args)
},
}
@@ -186,18 +183,11 @@
Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
const name = "argument to rem builtin"
- return intDivOp(c, (*big.Int).Rem, name, args)
+ return intDivOp(c, (*adt.OpContext).IntRem, name, args)
},
}
-var apdCtx apd.Context
-
-func init() {
- apdCtx = apd.BaseContext
- apdCtx.Precision = 24
-}
-
-type intFunc func(z, x, y *big.Int) *big.Int
+type intFunc func(c *adt.OpContext, x, y *adt.Num) adt.Value
func intDivOp(c *adt.OpContext, fn intFunc, name string, args []adt.Value) adt.Value {
a := c.Num(args[0], name)
@@ -207,28 +197,5 @@
return nil
}
- if b.X.IsZero() {
- return c.NewErrf("division by zero")
- }
-
- var x, y apd.Decimal
- _, _ = apdCtx.RoundToIntegralValue(&x, &a.X)
- if x.Negative {
- x.Coeff.Neg(&x.Coeff)
- }
- _, _ = apdCtx.RoundToIntegralValue(&y, &b.X)
- if y.Negative {
- y.Coeff.Neg(&y.Coeff)
- }
-
- var d apd.Decimal
-
- fn(&d.Coeff, &x.Coeff, &y.Coeff)
-
- if d.Coeff.Sign() < 0 {
- d.Coeff.Neg(&d.Coeff)
- d.Negative = true
- }
-
- return c.NewNum(&d, adt.IntKind)
+ return fn(c, a, b)
}