blob: a45fbc672e1a7fe8ed5b62ae8f4cce878bbfb76f [file] [log] [blame]
// Copyright 2019 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 cue
import (
"fmt"
"math/big"
"reflect"
"sort"
"strings"
"cuelang.org/go/cue/ast"
"github.com/cockroachdb/apd"
)
// This file contains functionality for converting Go to CUE.
func convert(ctx *context, src source, x interface{}) evaluated {
switch v := x.(type) {
case evaluated:
return v
case nil:
return &nullLit{src.base()}
case ast.Expr:
x := newVisitorCtx(ctx, nil, nil, nil)
return ctx.manifest(x.walk(v))
case error:
return ctx.mkErr(src, v.Error())
case bool:
return &boolLit{src.base(), v}
case string:
return &stringLit{src.base(), v}
case []byte:
return &bytesLit{src.base(), v}
case int:
return toInt(ctx, src, int64(v))
case int8:
return toInt(ctx, src, int64(v))
case int16:
return toInt(ctx, src, int64(v))
case int32:
return toInt(ctx, src, int64(v))
case int64:
return toInt(ctx, src, int64(v))
case uint:
return toUint(ctx, src, uint64(v))
case uint8:
return toUint(ctx, src, uint64(v))
case uint16:
return toUint(ctx, src, uint64(v))
case uint32:
return toUint(ctx, src, uint64(v))
case uint64:
return toUint(ctx, src, uint64(v))
case float64:
r := newNum(src, floatKind)
r.v.SetString(fmt.Sprintf("%g", v))
return r
case *big.Int:
n := newNum(src, intKind)
n.v.Coeff.Set(v)
if v.Sign() < 0 {
n.v.Coeff.Neg(&n.v.Coeff)
n.v.Negative = true
}
return n
case *big.Rat:
n := newNum(src, numKind)
ctx.Quo(&n.v, apd.NewWithBigInt(v.Num(), 0), apd.NewWithBigInt(v.Denom(), 0))
if !v.IsInt() {
n.k = floatKind
}
return n
case *big.Float:
n := newNum(src, floatKind)
n.v.SetString(v.String())
return n
case *apd.Decimal:
n := newNum(src, floatKind|intKind)
n.v.Set(v)
if !n.isInt(ctx) {
n.k = floatKind
}
return n
case reflect.Value:
if v.CanInterface() {
return convert(ctx, src, v.Interface())
}
default:
value := reflect.ValueOf(v)
switch value.Kind() {
case reflect.Ptr:
if value.IsNil() {
return &nullLit{src.base()}
}
return convert(ctx, src, value.Elem().Interface())
case reflect.Struct:
obj := newStruct(src)
t := value.Type()
for i := 0; i < value.NumField(); i++ {
t := t.Field(i)
if t.PkgPath != "" {
continue
}
sub := convert(ctx, src, value.Field(i).Interface())
// leave errors like we do during normal evaluation or do we
// want to return the error?
name := t.Name
for _, s := range []string{"cue", "json", "protobuf"} {
if tag, ok := t.Tag.Lookup(s); ok {
if p := strings.Index(tag, ","); p >= 0 {
tag = tag[:p]
}
if tag != "" {
name = tag
break
}
}
}
f := ctx.strLabel(name)
obj.arcs = append(obj.arcs, arc{feature: f, v: sub})
}
sort.Sort(obj)
return obj
case reflect.Map:
obj := newStruct(src)
t := value.Type()
if t.Key().Kind() != reflect.String {
return ctx.mkErr(src, "builtin map key not a string, but unsupported type %s", t.Key().String())
}
keys := []string{}
for _, k := range value.MapKeys() {
keys = append(keys, k.String())
}
sort.Strings(keys)
for _, k := range keys {
sub := convert(ctx, src, value.MapIndex(reflect.ValueOf(k)).Interface())
// leave errors like we do during normal evaluation or do we
// want to return the error?
f := ctx.strLabel(k)
obj.arcs = append(obj.arcs, arc{feature: f, v: sub})
}
sort.Sort(obj)
return obj
case reflect.Slice, reflect.Array:
list := &list{baseValue: src.base()}
for i := 0; i < value.Len(); i++ {
x := convert(ctx, src, value.Index(i).Interface())
if isBottom(x) {
return x
}
list.a = append(list.a, x)
}
list.initLit()
// There is no need to set the type of the list, as the list will
// be of fixed size and all elements will already have a defined
// value.
return list
}
}
return ctx.mkErr(src, "builtin returned unsupported type %T", x)
}
func toInt(ctx *context, src source, x int64) evaluated {
n := newNum(src, intKind)
n.v.SetInt64(x)
return n
}
func toUint(ctx *context, src source, x uint64) evaluated {
n := newNum(src, floatKind)
n.v.Coeff.SetUint64(x)
return n
}