| // 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" |
| "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 { |
| src adt.Expr |
| 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 := cue.MakeValue(c.ctx, c.args[i]) |
| // TODO: remove default |
| // v, _ = v.Default() |
| if !v.IsConcrete() { |
| c.errcf(c.src, adt.IncompleteError, "non-concrete argument %d", i) |
| } |
| return v |
| } |
| |
| func (c *CallCtxt) Struct(i int) *cue.Struct { |
| v := cue.MakeValue(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 := cue.MakeValue(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(c.src, 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 := cue.MakeValue(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(c.src, 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 := cue.MakeValue(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 := cue.MakeValue(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 := cue.MakeValue(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 := cue.MakeValue(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 := cue.MakeValue(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 := cue.MakeValue(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 := cue.MakeValue(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 := cue.MakeValue(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 := cue.MakeValue(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 := cue.MakeValue(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 type element %d (%s) of number list argument %d", j, k, i) |
| c.Err = &callError{err} |
| return a |
| } |
| |
| err := c.ctx.NewErrf( |
| "non-concrete number value for element %d of number list argument %d", 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 type element %d (%s) of string list argument %d", j, k, i) |
| c.Err = &callError{err} |
| return a |
| } |
| |
| err := c.ctx.NewErrf( |
| "non-concrete string value for element %d of string list argument %d", j, i) |
| err.Code = adt.IncompleteError |
| c.Err = &callError{err} |
| return nil |
| } |
| } |
| return a |
| } |