pkg/internal: copy cue builtin code to separate package

Mostly a copy and reorg. Contain minor modifications
to make it compile.

Adds Register function to facilitate registring.

Change-Id: I1494f9f057130277e82cb62d04973488de916473
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/6884
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/pkg/internal/builtin.go b/pkg/internal/builtin.go
new file mode 100644
index 0000000..167bbec
--- /dev/null
+++ b/pkg/internal/builtin.go
@@ -0,0 +1,212 @@
+// 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 (
+	"encoding/json"
+	"fmt"
+
+	"cuelang.org/go/cue/errors"
+	"cuelang.org/go/cue/parser"
+	"cuelang.org/go/cue/token"
+	"cuelang.org/go/internal"
+	"cuelang.org/go/internal/core/adt"
+	"cuelang.org/go/internal/core/compile"
+	"cuelang.org/go/internal/core/convert"
+)
+
+// A Builtin is a Builtin function or constant.
+//
+// A function may return and a constant may be any of the following types:
+//
+//   error (translates to bottom)
+//   nil   (translates to null)
+//   bool
+//   int*
+//   uint*
+//   float64
+//   string
+//   *big.Float
+//   *big.Int
+//
+//   For any of the above, including interface{} and these types recursively:
+//   []T
+//   map[string]T
+//
+type Builtin struct {
+	Name   string
+	Pkg    adt.Feature
+	Params []adt.Kind
+	Result adt.Kind
+	Func   func(c *CallCtxt)
+	Const  string
+}
+
+type Package struct {
+	Native []*Builtin
+	CUE    string
+}
+
+func (p *Package) MustCompile(ctx *adt.OpContext, pkgName string) *adt.Vertex {
+	obj := &adt.Vertex{}
+	pkgLabel := ctx.StringLabel(pkgName)
+	st := &adt.StructLit{}
+	if len(p.Native) > 0 {
+		obj.AddConjunct(adt.MakeConjunct(nil, st))
+	}
+	for _, b := range p.Native {
+		b.Pkg = pkgLabel
+
+		f := ctx.StringLabel(b.Name) // never starts with _
+		// n := &node{baseValue: newBase(imp.Path)}
+		var v adt.Expr = toBuiltin(ctx, b)
+		if b.Const != "" {
+			v = mustParseConstBuiltin(ctx, b.Name, b.Const)
+		}
+		st.Decls = append(st.Decls, &adt.Field{
+			Label: f,
+			Value: v,
+		})
+	}
+
+	// Parse builtin CUE
+	if p.CUE != "" {
+		expr, err := parser.ParseExpr(pkgName, p.CUE)
+		if err != nil {
+			panic(fmt.Errorf("could not parse %v: %v", p.CUE, err))
+		}
+		c, err := compile.Expr(nil, ctx.Runtime, expr)
+		if err != nil {
+			panic(fmt.Errorf("could compile parse %v: %v", p.CUE, err))
+		}
+		obj.AddConjunct(c)
+	}
+
+	// We could compile lazily, but this is easier for debugging.
+	obj.Finalize(ctx)
+	if err := obj.Err(ctx, adt.Finalized); err != nil {
+		panic(err.Err)
+	}
+
+	return obj
+}
+
+func toBuiltin(ctx *adt.OpContext, b *Builtin) *adt.Builtin {
+	x := &adt.Builtin{
+		Params:  b.Params,
+		Result:  b.Result,
+		Package: b.Pkg,
+		Name:    b.Name,
+	}
+	x.Func = func(ctx *adt.OpContext, args []adt.Value) (ret adt.Expr) {
+		// call, _ := ctx.Source().(*ast.CallExpr)
+		c := &CallCtxt{
+			// src:  call,
+			ctx:     ctx,
+			args:    args,
+			builtin: b,
+		}
+		defer func() {
+			var errVal interface{} = c.Err
+			if err := recover(); err != nil {
+				errVal = err
+			}
+			ret = processErr(c, errVal, ret)
+		}()
+		b.Func(c)
+		switch v := c.Ret.(type) {
+		case adt.Value:
+			return v
+		case bottomer:
+			return v.Bottom()
+		}
+		if c.Err != nil {
+			return nil
+		}
+		return convert.GoValueToValue(ctx, c.Ret, true)
+	}
+	return x
+}
+
+// newConstBuiltin parses and creates any CUE expression that does not have
+// fields.
+func mustParseConstBuiltin(ctx adt.Runtime, name, val string) adt.Expr {
+	expr, err := parser.ParseExpr("<builtin:"+name+">", val)
+	if err != nil {
+		panic(err)
+	}
+	c, err := compile.Expr(nil, ctx, expr)
+	if err != nil {
+		panic(err)
+	}
+	return c.Expr()
+
+}
+
+func pos(n adt.Node) (p token.Pos) {
+	if n == nil {
+		return
+	}
+	src := n.Source()
+	if src == nil {
+		return
+	}
+	return src.Pos()
+}
+
+func (x *Builtin) name(ctx *adt.OpContext) string {
+	if x.Pkg == 0 {
+		return x.Name
+	}
+	return fmt.Sprintf("%s.%s", x.Pkg.StringValue(ctx), x.Name)
+}
+
+func (x *Builtin) isValidator() bool {
+	return len(x.Params) == 1 && x.Result == adt.BoolKind
+}
+
+func processErr(call *CallCtxt, errVal interface{}, ret adt.Expr) adt.Expr {
+	ctx := call.ctx
+	switch err := errVal.(type) {
+	case nil:
+	case *callError:
+		ret = err.b
+	case *json.MarshalerError:
+		if err, ok := err.Err.(bottomer); ok {
+			if b := err.Bottom(); b != nil {
+				ret = b
+			}
+		}
+	case bottomer:
+		ret = wrapCallErr(call, err.Bottom())
+	case errors.Error:
+		ret = wrapCallErr(call, &adt.Bottom{Err: err})
+	case error:
+		if call.Err == internal.ErrIncomplete {
+			err := ctx.NewErrf("incomplete value")
+			err.Code = adt.IncompleteError
+			ret = err
+		} else {
+			// TODO: store the underlying error explicitly
+			ret = wrapCallErr(call, &adt.Bottom{Err: errors.Promote(err, "")})
+		}
+	default:
+		// Likely a string passed to panic.
+		ret = wrapCallErr(call, &adt.Bottom{
+			Err: errors.Newf(call.Pos(), "%s", err),
+		})
+	}
+	return ret
+}
diff --git a/pkg/internal/context.go b/pkg/internal/context.go
new file mode 100644
index 0000000..d437aa3
--- /dev/null
+++ b/pkg/internal/context.go
@@ -0,0 +1,269 @@
+// 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/errors"
+	"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 {
+	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) DecimalList(i int) (a []*apd.Decimal) {
+	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 nil
+	}
+	for j := 0; v.Next(); j++ {
+		num, err := v.Value().Decimal()
+		if err != nil {
+			c.errf(c.src, err, "invalid list element %d in argument %d to %s: %v",
+				j, i, c.Name(), err)
+			break
+		}
+		a = append(a, num)
+	}
+	return a
+}
+
+func (c *CallCtxt) StringList(i int) (a []string) {
+	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 nil
+	}
+	for j := 0; v.Next(); j++ {
+		str, err := v.Value().String()
+		if err != nil {
+			c.Err = errors.Wrapf(err, c.Pos(),
+				"element %d of list argument %d", j, i)
+			break
+		}
+		a = append(a, str)
+	}
+	return a
+}
diff --git a/pkg/internal/errors.go b/pkg/internal/errors.go
new file mode 100644
index 0000000..fe11de1
--- /dev/null
+++ b/pkg/internal/errors.go
@@ -0,0 +1,128 @@
+// 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 (
+	"encoding/json"
+	"fmt"
+
+	"cuelang.org/go/cue/errors"
+	"cuelang.org/go/cue/token"
+	"cuelang.org/go/internal"
+	"cuelang.org/go/internal/core/adt"
+)
+
+type bottomer interface {
+	error
+	Bottom() *adt.Bottom
+}
+
+type callError struct {
+	b *adt.Bottom
+}
+
+func (e *callError) Error() string {
+	return fmt.Sprint(e.b)
+}
+
+func (c *CallCtxt) errf(src adt.Node, underlying error, format string, args ...interface{}) {
+	var errs errors.Error
+	if err, ok := underlying.(bottomer); ok {
+		errs = err.Bottom().Err
+	}
+	errs = errors.Wrapf(errs, c.ctx.Pos(), format, args...)
+	c.Err = &callError{&adt.Bottom{Err: errs}}
+}
+
+func (c *CallCtxt) errcf(src adt.Node, code adt.ErrorCode, format string, args ...interface{}) {
+	err := c.ctx.NewErrf(format, args...)
+	err.Code = code
+	c.Err = &callError{err}
+}
+
+func wrapCallErr(c *CallCtxt, b *adt.Bottom) *adt.Bottom {
+	pos := token.NoPos
+	if c.src != nil {
+		if src := c.src.Source(); src != nil {
+			pos = src.Pos()
+		}
+	}
+	const msg = "error in call to %s"
+	return &adt.Bottom{
+		Code: b.Code,
+		Err:  errors.Wrapf(b.Err, pos, msg, c.builtin.name(c.ctx)),
+	}
+}
+
+func (c *CallCtxt) convertError(x interface{}, name string) *adt.Bottom {
+	var err errors.Error
+	switch v := x.(type) {
+	case nil:
+		return nil
+
+	case *adt.Bottom:
+		return v
+
+	case *json.MarshalerError:
+		err = errors.Promote(v, "marshal error")
+
+	case errors.Error:
+		err = v
+
+	case error:
+		if name != "" {
+			err = errors.Newf(c.Pos(), "%s: %v", name, v)
+		} else {
+			err = errors.Newf(c.Pos(), "error in call to %s: %v", c.Name(), v)
+		}
+
+	default:
+		err = errors.Newf(token.NoPos, "%s", name)
+	}
+	if err != internal.ErrIncomplete {
+		return &adt.Bottom{
+			// Wrap to preserve position information.
+			Err: errors.Wrapf(err, c.Pos(), "error in call to %s", c.Name()),
+		}
+	}
+	return &adt.Bottom{
+		Code: adt.IncompleteError,
+		Err:  errors.Newf(c.Pos(), "incomplete values in call to %s", c.Name()),
+	}
+}
+
+func (c *CallCtxt) invalidArgType(arg adt.Expr, i int, typ string, err error) {
+	if ve, ok := err.(bottomer); ok && ve.Bottom().IsIncomplete() {
+		c.Err = ve
+		return
+	}
+	v, ok := arg.(adt.Value)
+	// TODO: make these permanent errors if the value did not originate from
+	// a reference.
+	if !ok {
+		c.errf(c.src, nil,
+			"cannot use incomplete value %s as %s in argument %d to %s: %v",
+			c.ctx.Str(arg), typ, i, c.Name(), err)
+	}
+	if err != nil {
+		c.errf(c.src, err,
+			"cannot use %s (type %s) as %s in argument %d to %s: %v",
+			c.ctx.Str(arg), v.Kind(), typ, i, c.Name(), err)
+	} else {
+		c.errf(c.src, err,
+			"cannot use %s (type %s) as %s in argument %d to %s",
+			c.ctx.Str(arg), v.Kind(), typ, i, c.Name())
+	}
+}