blob: a8ff4fddaf3e3220e2e7afd3c5f982c2a0a85a75 [file] [log] [blame]
// 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 internal
import (
"io"
"math/big"
"cuelang.org/go/cue"
"cuelang.org/go/cue/token"
"cuelang.org/go/internal/core/adt"
"cuelang.org/go/internal/value"
"github.com/cockroachdb/apd/v2"
)
// CallCtxt is passed to builtin implementations that need to use a cue.Value. This is an internal type. It's interface may change.
type CallCtxt struct {
ctx *adt.OpContext
builtin *Builtin
Err interface{}
Ret interface{}
args []adt.Value
}
func (c *CallCtxt) Pos() token.Pos {
return c.ctx.Pos()
}
func (c *CallCtxt) Name() string {
return c.builtin.name(c.ctx)
}
// Do returns whether the call should be done.
func (c *CallCtxt) Do() bool {
return c.Err == nil
}
func (c *CallCtxt) Value(i int) cue.Value {
v := value.Make(c.ctx, c.args[i])
// TODO: remove default
// v, _ = v.Default()
if !v.IsConcrete() {
c.errcf(adt.IncompleteError, "non-concrete argument %d", i)
}
return v
}
func (c *CallCtxt) Struct(i int) *cue.Struct {
v := value.Make(c.ctx, c.args[i])
s, err := v.Struct()
if err != nil {
c.invalidArgType(c.args[i], i, "struct", err)
return nil
}
return s
}
func (c *CallCtxt) Int(i int) int { return int(c.intValue(i, 64, "int64")) }
func (c *CallCtxt) Int8(i int) int8 { return int8(c.intValue(i, 8, "int8")) }
func (c *CallCtxt) Int16(i int) int16 { return int16(c.intValue(i, 16, "int16")) }
func (c *CallCtxt) Int32(i int) int32 { return int32(c.intValue(i, 32, "int32")) }
func (c *CallCtxt) Rune(i int) rune { return rune(c.intValue(i, 32, "rune")) }
func (c *CallCtxt) Int64(i int) int64 { return int64(c.intValue(i, 64, "int64")) }
func (c *CallCtxt) intValue(i, bits int, typ string) int64 {
arg := c.args[i]
x := value.Make(c.ctx, arg)
n, err := x.Int(nil)
if err != nil {
c.invalidArgType(arg, i, typ, err)
return 0
}
if n.BitLen() > bits {
c.errf(err, "int %s overflows %s in argument %d in call to %s",
n, typ, i, c.Name())
}
res, _ := x.Int64()
return res
}
func (c *CallCtxt) Uint(i int) uint { return uint(c.uintValue(i, 64, "uint64")) }
func (c *CallCtxt) Uint8(i int) uint8 { return uint8(c.uintValue(i, 8, "uint8")) }
func (c *CallCtxt) Byte(i int) uint8 { return byte(c.uintValue(i, 8, "byte")) }
func (c *CallCtxt) Uint16(i int) uint16 { return uint16(c.uintValue(i, 16, "uint16")) }
func (c *CallCtxt) Uint32(i int) uint32 { return uint32(c.uintValue(i, 32, "uint32")) }
func (c *CallCtxt) Uint64(i int) uint64 { return uint64(c.uintValue(i, 64, "uint64")) }
func (c *CallCtxt) uintValue(i, bits int, typ string) uint64 {
x := value.Make(c.ctx, c.args[i])
n, err := x.Int(nil)
if err != nil || n.Sign() < 0 {
c.invalidArgType(c.args[i], i, typ, err)
return 0
}
if n.BitLen() > bits {
c.errf(err, "int %s overflows %s in argument %d in call to %s",
n, typ, i, c.Name())
}
res, _ := x.Uint64()
return res
}
func (c *CallCtxt) Decimal(i int) *apd.Decimal {
x := value.Make(c.ctx, c.args[i])
if _, err := x.MantExp(nil); err != nil {
c.invalidArgType(c.args[i], i, "Decimal", err)
return nil
}
return &c.args[i].(*adt.Num).X
}
func (c *CallCtxt) Float64(i int) float64 {
x := value.Make(c.ctx, c.args[i])
res, err := x.Float64()
if err != nil {
c.invalidArgType(c.args[i], i, "float64", err)
return 0
}
return res
}
func (c *CallCtxt) BigInt(i int) *big.Int {
x := value.Make(c.ctx, c.args[i])
n, err := x.Int(nil)
if err != nil {
c.invalidArgType(c.args[i], i, "int", err)
return nil
}
return n
}
var ten = big.NewInt(10)
func (c *CallCtxt) BigFloat(i int) *big.Float {
x := value.Make(c.ctx, c.args[i])
var mant big.Int
exp, err := x.MantExp(&mant)
if err != nil {
c.invalidArgType(c.args[i], i, "float", err)
return nil
}
f := &big.Float{}
f.SetInt(&mant)
if exp != 0 {
var g big.Float
e := big.NewInt(int64(exp))
f.Mul(f, g.SetInt(e.Exp(ten, e, nil)))
}
return f
}
func (c *CallCtxt) String(i int) string {
// TODO: use Evaluate instead.
x := value.Make(c.ctx, c.args[i])
v, err := x.String()
if err != nil {
c.invalidArgType(c.args[i], i, "string", err)
return ""
}
return v
}
func (c *CallCtxt) Bytes(i int) []byte {
x := value.Make(c.ctx, c.args[i])
v, err := x.Bytes()
if err != nil {
c.invalidArgType(c.args[i], i, "bytes", err)
return nil
}
return v
}
func (c *CallCtxt) Reader(i int) io.Reader {
x := value.Make(c.ctx, c.args[i])
// TODO: optimize for string and bytes cases
r, err := x.Reader()
if err != nil {
c.invalidArgType(c.args[i], i, "bytes|string", err)
return nil
}
return r
}
func (c *CallCtxt) Bool(i int) bool {
x := value.Make(c.ctx, c.args[i])
b, err := x.Bool()
if err != nil {
c.invalidArgType(c.args[i], i, "bool", err)
return false
}
return b
}
func (c *CallCtxt) List(i int) (a []cue.Value) {
arg := c.args[i]
x := value.Make(c.ctx, arg)
v, err := x.List()
if err != nil {
c.invalidArgType(c.args[i], i, "list", err)
return a
}
for v.Next() {
a = append(a, v.Value())
}
return a
}
func (c *CallCtxt) Iter(i int) (a cue.Iterator) {
arg := c.args[i]
x := value.Make(c.ctx, arg)
v, err := x.List()
if err != nil {
c.invalidArgType(c.args[i], i, "list", err)
}
return v
}
func (c *CallCtxt) getList(i int) *adt.Vertex {
x := c.args[i]
switch v, ok := x.(*adt.Vertex); {
case ok && v.IsList():
v.Finalize(c.ctx)
return v
case v != nil:
x = v.Value()
}
if x.Kind()&adt.ListKind == 0 {
var err error
if b, ok := x.(*adt.Bottom); ok {
err = &callError{b}
}
c.invalidArgType(c.args[i], i, "list", err)
} else {
err := c.ctx.NewErrf("non-concrete list for argument %d", i)
err.Code = adt.IncompleteError
c.Err = &callError{err}
}
return nil
}
func (c *CallCtxt) DecimalList(i int) (a []*apd.Decimal) {
v := c.getList(i)
if v == nil {
return nil
}
for j, w := range v.Elems() {
w.Finalize(c.ctx) // defensive
switch x := w.Value().(type) {
case *adt.Num:
a = append(a, &x.X)
case *adt.Bottom:
if x.IsIncomplete() {
c.Err = x
return nil
}
default:
if k := w.Kind(); k&adt.NumKind == 0 {
err := c.ctx.NewErrf(
"invalid list element %d in argument %d to call: cannot use value %s (%s) as number",
j, i, w, k)
c.Err = &callError{err}
return a
}
err := c.ctx.NewErrf(
"non-concrete value %s for element %d of number list argument %d",
w, j, i)
err.Code = adt.IncompleteError
c.Err = &callError{err}
return nil
}
}
return a
}
func (c *CallCtxt) StringList(i int) (a []string) {
v := c.getList(i)
if v == nil {
return nil
}
for j, w := range v.Elems() {
w.Finalize(c.ctx) // defensive
switch x := w.Value().(type) {
case *adt.String:
a = append(a, x.Str)
case *adt.Bottom:
if x.IsIncomplete() {
c.Err = x
return nil
}
default:
if k := w.Kind(); k&adt.StringKind == 0 {
err := c.ctx.NewErrf(
"invalid list element %d in argument %d to call: cannot use value %s (%s) as string",
j, i, w, k)
c.Err = &callError{err}
return a
}
err := c.ctx.NewErrf(
"non-concrete value %s for element %d of string list argument %d",
w, j, i)
err.Code = adt.IncompleteError
c.Err = &callError{err}
return nil
}
}
return a
}