...

Source file src/cuelang.org/go/internal/core/convert/go.go

Documentation: cuelang.org/go/internal/core/convert

     1  // Copyright 2019 CUE Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package convert allows converting to and from Go values and Types.
    16  package convert
    17  
    18  import (
    19  	"encoding"
    20  	"encoding/json"
    21  	"fmt"
    22  	"math/big"
    23  	"reflect"
    24  	"sort"
    25  	"strconv"
    26  	"strings"
    27  
    28  	"github.com/cockroachdb/apd/v3"
    29  	"golang.org/x/text/encoding/unicode"
    30  
    31  	"cuelang.org/go/cue/ast"
    32  	"cuelang.org/go/cue/ast/astutil"
    33  	"cuelang.org/go/cue/errors"
    34  	"cuelang.org/go/cue/parser"
    35  	"cuelang.org/go/cue/token"
    36  	"cuelang.org/go/internal"
    37  	"cuelang.org/go/internal/core/adt"
    38  	"cuelang.org/go/internal/core/compile"
    39  	internaljson "cuelang.org/go/internal/encoding/json"
    40  	"cuelang.org/go/internal/types"
    41  )
    42  
    43  // This file contains functionality for converting Go to CUE.
    44  //
    45  // The code in this file is a prototype implementation and is far from
    46  // optimized.
    47  
    48  func GoValueToValue(ctx *adt.OpContext, x interface{}, nilIsTop bool) adt.Value {
    49  	v := GoValueToExpr(ctx, nilIsTop, x)
    50  	// TODO: return Value
    51  	return toValue(v)
    52  }
    53  
    54  func GoTypeToExpr(ctx *adt.OpContext, x interface{}) (adt.Expr, errors.Error) {
    55  	v := convertGoType(ctx, reflect.TypeOf(x))
    56  	if err := ctx.Err(); err != nil {
    57  		return v, err.Err
    58  	}
    59  	return v, nil
    60  }
    61  
    62  func toValue(e adt.Expr) adt.Value {
    63  	if v, ok := e.(adt.Value); ok {
    64  		return v
    65  	}
    66  	obj := &adt.Vertex{}
    67  	obj.AddConjunct(adt.MakeRootConjunct(nil, e))
    68  	return obj
    69  }
    70  
    71  func compileExpr(ctx *adt.OpContext, expr ast.Expr) adt.Value {
    72  	c, err := compile.Expr(nil, ctx, pkgID(), expr)
    73  	if err != nil {
    74  		return &adt.Bottom{Err: errors.Promote(err, "compile")}
    75  	}
    76  	return adt.Resolve(ctx, c)
    77  }
    78  
    79  // parseTag parses a CUE expression from a cue tag.
    80  func parseTag(ctx *adt.OpContext, obj *ast.StructLit, field, tag string) ast.Expr {
    81  	tag, _ = splitTag(tag)
    82  	if tag == "" {
    83  		return topSentinel
    84  	}
    85  	expr, err := parser.ParseExpr("<field:>", tag)
    86  	if err != nil {
    87  		err := errors.Promote(err, "parser")
    88  		ctx.AddErr(errors.Wrapf(err, ctx.Pos(),
    89  			"invalid tag %q for field %q", tag, field))
    90  		return &ast.BadExpr{}
    91  	}
    92  	return expr
    93  }
    94  
    95  // splitTag splits a cue tag into cue and options.
    96  func splitTag(tag string) (cue string, options string) {
    97  	q := strings.LastIndexByte(tag, '"')
    98  	if c := strings.IndexByte(tag[q+1:], ','); c >= 0 {
    99  		return tag[:q+1+c], tag[q+1+c+1:]
   100  	}
   101  	return tag, ""
   102  }
   103  
   104  // TODO: should we allow mapping names in cue tags? This only seems like a good
   105  // idea if we ever want to allow mapping CUE to a different name than JSON.
   106  var tagsWithNames = []string{"json", "yaml", "protobuf"}
   107  
   108  func getName(f *reflect.StructField) string {
   109  	name := f.Name
   110  	if f.Anonymous {
   111  		name = ""
   112  	}
   113  	for _, s := range tagsWithNames {
   114  		if tag, ok := f.Tag.Lookup(s); ok {
   115  			if p := strings.IndexByte(tag, ','); p >= 0 {
   116  				tag = tag[:p]
   117  			}
   118  			if tag != "" {
   119  				name = tag
   120  				break
   121  			}
   122  		}
   123  	}
   124  	return name
   125  }
   126  
   127  // isOptional indicates whether a field should be marked as optional.
   128  func isOptional(f *reflect.StructField) bool {
   129  	isOptional := false
   130  	switch f.Type.Kind() {
   131  	case reflect.Ptr, reflect.Map, reflect.Chan, reflect.Interface, reflect.Slice:
   132  		// Note: it may be confusing to distinguish between an empty slice and
   133  		// a nil slice. However, it is also surprising to not be able to specify
   134  		// a default value for a slice. So for now we will allow it.
   135  		isOptional = true
   136  	}
   137  	if tag, ok := f.Tag.Lookup("cue"); ok {
   138  		// TODO: only if first field is not empty.
   139  		_, opt := splitTag(tag)
   140  		isOptional = false
   141  		for _, f := range strings.Split(opt, ",") {
   142  			switch f {
   143  			case "opt":
   144  				isOptional = true
   145  			case "req":
   146  				return false
   147  			}
   148  		}
   149  	} else if tag, ok = f.Tag.Lookup("json"); ok {
   150  		isOptional = false
   151  		for _, f := range strings.Split(tag, ",")[1:] {
   152  			if f == "omitempty" {
   153  				return true
   154  			}
   155  		}
   156  	}
   157  	return isOptional
   158  }
   159  
   160  // isOmitEmpty means that the zero value is interpreted as undefined.
   161  func isOmitEmpty(f *reflect.StructField) bool {
   162  	isOmitEmpty := false
   163  	switch f.Type.Kind() {
   164  	case reflect.Ptr, reflect.Map, reflect.Chan, reflect.Interface, reflect.Slice:
   165  		// Note: it may be confusing to distinguish between an empty slice and
   166  		// a nil slice. However, it is also surprising to not be able to specify
   167  		// a default value for a slice. So for now we will allow it.
   168  		isOmitEmpty = true
   169  
   170  	default:
   171  		// TODO: we can also infer omit empty if a type cannot be nil if there
   172  		// is a constraint that unconditionally disallows the zero value.
   173  	}
   174  	tag, ok := f.Tag.Lookup("json")
   175  	if ok {
   176  		isOmitEmpty = false
   177  		for _, f := range strings.Split(tag, ",")[1:] {
   178  			if f == "omitempty" {
   179  				return true
   180  			}
   181  		}
   182  	}
   183  	return isOmitEmpty
   184  }
   185  
   186  // parseJSON parses JSON into a CUE value. b must be valid JSON.
   187  func parseJSON(ctx *adt.OpContext, b []byte) adt.Value {
   188  	expr, err := parser.ParseExpr("json", b)
   189  	if err != nil {
   190  		panic(err) // cannot happen
   191  	}
   192  	return compileExpr(ctx, expr)
   193  }
   194  
   195  func GoValueToExpr(ctx *adt.OpContext, nilIsTop bool, x interface{}) adt.Expr {
   196  	e := convertRec(ctx, nilIsTop, x)
   197  	if e == nil {
   198  		return ctx.AddErrf("unsupported Go type (%T)", x)
   199  	}
   200  	return e
   201  }
   202  
   203  func isNil(x reflect.Value) bool {
   204  	switch x.Kind() {
   205  	// Only check for supported types; ignore func and chan.
   206  	case reflect.Ptr, reflect.Map, reflect.Slice, reflect.Interface:
   207  		return x.IsNil()
   208  	}
   209  	return false
   210  }
   211  
   212  func convertRec(ctx *adt.OpContext, nilIsTop bool, x interface{}) adt.Value {
   213  	if t := (&types.Value{}); types.CastValue(t, x) {
   214  		// TODO: panic if nto the same runtime.
   215  		return t.V
   216  	}
   217  	src := ctx.Source()
   218  	switch v := x.(type) {
   219  	case nil:
   220  		if nilIsTop {
   221  			ident, _ := ctx.Source().(*ast.Ident)
   222  			return &adt.Top{Src: ident}
   223  		}
   224  		return &adt.Null{Src: ctx.Source()}
   225  
   226  	case *ast.File:
   227  		x, err := compile.Files(nil, ctx, pkgID(), v)
   228  		if err != nil {
   229  			return &adt.Bottom{Err: errors.Promote(err, "compile")}
   230  		}
   231  		if len(x.Conjuncts) != 1 {
   232  			panic("unexpected length")
   233  		}
   234  		return x
   235  
   236  	case ast.Expr:
   237  		return compileExpr(ctx, v)
   238  
   239  	case *big.Int:
   240  		v2 := new(apd.BigInt).SetMathBigInt(v)
   241  		return &adt.Num{Src: src, K: adt.IntKind, X: *apd.NewWithBigInt(v2, 0)}
   242  
   243  	case *big.Rat:
   244  		// should we represent this as a binary operation?
   245  		n := &adt.Num{Src: src, K: adt.IntKind}
   246  		_, err := internal.BaseContext.Quo(&n.X,
   247  			apd.NewWithBigInt(new(apd.BigInt).SetMathBigInt(v.Num()), 0),
   248  			apd.NewWithBigInt(new(apd.BigInt).SetMathBigInt(v.Denom()), 0),
   249  		)
   250  		if err != nil {
   251  			return ctx.AddErrf("could not convert *big.Rat: %v", err)
   252  		}
   253  		if !v.IsInt() {
   254  			n.K = adt.FloatKind
   255  		}
   256  		return n
   257  
   258  	case *big.Float:
   259  		n := &adt.Num{Src: src, K: adt.FloatKind}
   260  		_, _, err := n.X.SetString(v.String())
   261  		if err != nil {
   262  			return ctx.AddErr(errors.Promote(err, "invalid float"))
   263  		}
   264  		return n
   265  
   266  	case *apd.Decimal:
   267  		// TODO: should we allow an "int" bit to be set here? It is a bit
   268  		// tricky, as we would also need to pass down the result of rounding.
   269  		// So more likely an API must return explicitly whether a value is
   270  		// a float or an int after all.
   271  		// The code to autodetect whether something is an integer can be done
   272  		// with this:
   273  		kind := adt.FloatKind
   274  		var d apd.Decimal
   275  		res, _ := internal.BaseContext.RoundToIntegralExact(&d, v)
   276  		if !res.Inexact() {
   277  			kind = adt.IntKind
   278  			v = &d
   279  		}
   280  		n := &adt.Num{Src: ctx.Source(), K: kind}
   281  		n.X = *v
   282  		return n
   283  
   284  	case json.Marshaler:
   285  		b, err := v.MarshalJSON()
   286  		if err != nil {
   287  			return ctx.AddErr(errors.Promote(err, "json.Marshaler"))
   288  		}
   289  
   290  		return parseJSON(ctx, b)
   291  
   292  	case encoding.TextMarshaler:
   293  		b, err := v.MarshalText()
   294  		if err != nil {
   295  			return ctx.AddErr(errors.Promote(err, "encoding.TextMarshaler"))
   296  		}
   297  		b, err = internaljson.Marshal(string(b))
   298  		if err != nil {
   299  			return ctx.AddErr(errors.Promote(err, "json"))
   300  		}
   301  		return parseJSON(ctx, b)
   302  
   303  	case error:
   304  		var errs errors.Error
   305  		switch x := v.(type) {
   306  		case errors.Error:
   307  			errs = x
   308  		default:
   309  			errs = ctx.Newf("%s", x.Error())
   310  		}
   311  		return &adt.Bottom{Err: errs}
   312  	case bool:
   313  		return &adt.Bool{Src: ctx.Source(), B: v}
   314  	case string:
   315  		s, _ := unicode.UTF8.NewEncoder().String(v)
   316  		return &adt.String{Src: ctx.Source(), Str: s}
   317  	case []byte:
   318  		return &adt.Bytes{Src: ctx.Source(), B: v}
   319  	case int:
   320  		return toInt(ctx, int64(v))
   321  	case int8:
   322  		return toInt(ctx, int64(v))
   323  	case int16:
   324  		return toInt(ctx, int64(v))
   325  	case int32:
   326  		return toInt(ctx, int64(v))
   327  	case int64:
   328  		return toInt(ctx, int64(v))
   329  	case uint:
   330  		return toUint(ctx, uint64(v))
   331  	case uint8:
   332  		return toUint(ctx, uint64(v))
   333  	case uint16:
   334  		return toUint(ctx, uint64(v))
   335  	case uint32:
   336  		return toUint(ctx, uint64(v))
   337  	case uint64:
   338  		return toUint(ctx, uint64(v))
   339  	case uintptr:
   340  		return toUint(ctx, uint64(v))
   341  	case float64:
   342  		n := &adt.Num{Src: src, K: adt.FloatKind}
   343  		_, _, err := n.X.SetString(fmt.Sprint(v))
   344  		if err != nil {
   345  			return ctx.AddErr(errors.Promote(err, "invalid float"))
   346  		}
   347  		return n
   348  	case float32:
   349  		n := &adt.Num{Src: src, K: adt.FloatKind}
   350  		_, _, err := n.X.SetString(fmt.Sprint(v))
   351  		if err != nil {
   352  			return ctx.AddErr(errors.Promote(err, "invalid float"))
   353  		}
   354  		return n
   355  
   356  	case reflect.Value:
   357  		if v.CanInterface() {
   358  			return convertRec(ctx, nilIsTop, v.Interface())
   359  		}
   360  
   361  	default:
   362  		value := reflect.ValueOf(v)
   363  		switch value.Kind() {
   364  		case reflect.Bool:
   365  			return &adt.Bool{Src: ctx.Source(), B: value.Bool()}
   366  
   367  		case reflect.String:
   368  			str := value.String()
   369  			str, _ = unicode.UTF8.NewEncoder().String(str)
   370  			// TODO: here and above: allow to fail on invalid strings.
   371  			// if !utf8.ValidString(str) {
   372  			// 	return ctx.AddErrf("cannot convert result to string: invalid UTF-8")
   373  			// }
   374  			return &adt.String{Src: ctx.Source(), Str: str}
   375  
   376  		case reflect.Int, reflect.Int8, reflect.Int16,
   377  			reflect.Int32, reflect.Int64:
   378  			return toInt(ctx, value.Int())
   379  
   380  		case reflect.Uint, reflect.Uint8, reflect.Uint16,
   381  			reflect.Uint32, reflect.Uint64, reflect.Uintptr:
   382  			return toUint(ctx, value.Uint())
   383  
   384  		case reflect.Float32, reflect.Float64:
   385  			return convertRec(ctx, nilIsTop, value.Float())
   386  
   387  		case reflect.Ptr:
   388  			if value.IsNil() {
   389  				if nilIsTop {
   390  					ident, _ := ctx.Source().(*ast.Ident)
   391  					return &adt.Top{Src: ident}
   392  				}
   393  				return &adt.Null{Src: ctx.Source()}
   394  			}
   395  			return convertRec(ctx, nilIsTop, value.Elem().Interface())
   396  
   397  		case reflect.Struct:
   398  			obj := &adt.StructLit{Src: src}
   399  			v := &adt.Vertex{}
   400  			env := ctx.Env(0)
   401  			if env == nil {
   402  				env = &adt.Environment{}
   403  			}
   404  			// There is no closedness or cycle info for Go structs, so we
   405  			// pass an empty CloseInfo.
   406  			v.AddStruct(obj, env, adt.CloseInfo{})
   407  			v.SetValue(ctx, &adt.StructMarker{})
   408  
   409  			t := value.Type()
   410  			for i := 0; i < value.NumField(); i++ {
   411  				sf := t.Field(i)
   412  				if sf.PkgPath != "" {
   413  					continue
   414  				}
   415  				val := value.Field(i)
   416  				if !nilIsTop && isNil(val) {
   417  					continue
   418  				}
   419  				if tag, _ := sf.Tag.Lookup("json"); tag == "-" {
   420  					continue
   421  				}
   422  				if isOmitEmpty(&sf) && val.IsZero() {
   423  					continue
   424  				}
   425  				sub := convertRec(ctx, nilIsTop, val.Interface())
   426  				if sub == nil {
   427  					// mimic behavior of encoding/json: skip fields of unsupported types
   428  					continue
   429  				}
   430  				if _, ok := sub.(*adt.Bottom); ok {
   431  					return sub
   432  				}
   433  
   434  				// leave errors like we do during normal evaluation or do we
   435  				// want to return the error?
   436  				name := getName(&sf)
   437  				if name == "-" {
   438  					continue
   439  				}
   440  				if sf.Anonymous && name == "" {
   441  					arc, ok := sub.(*adt.Vertex)
   442  					if ok {
   443  						v.Arcs = append(v.Arcs, arc.Arcs...)
   444  					}
   445  					continue
   446  				}
   447  
   448  				f := ctx.StringLabel(name)
   449  				obj.Decls = append(obj.Decls, &adt.Field{Label: f, Value: sub})
   450  				arc, ok := sub.(*adt.Vertex)
   451  				if ok {
   452  					a := *arc
   453  					arc = &a
   454  					arc.Label = f
   455  				} else {
   456  					arc = &adt.Vertex{Label: f, BaseValue: sub}
   457  					arc.ForceDone()
   458  					arc.AddConjunct(adt.MakeRootConjunct(nil, sub))
   459  				}
   460  				v.Arcs = append(v.Arcs, arc)
   461  			}
   462  
   463  			return v
   464  
   465  		case reflect.Map:
   466  			v := &adt.Vertex{BaseValue: &adt.StructMarker{}}
   467  			v.SetValue(ctx, &adt.StructMarker{})
   468  
   469  			t := value.Type()
   470  			switch key := t.Key(); key.Kind() {
   471  			default:
   472  				if !key.Implements(textMarshaler) {
   473  					return ctx.AddErrf("unsupported Go type for map key (%v)", key)
   474  				}
   475  				fallthrough
   476  			case reflect.String,
   477  				reflect.Int, reflect.Int8, reflect.Int16,
   478  				reflect.Int32, reflect.Int64,
   479  				reflect.Uint, reflect.Uint8, reflect.Uint16,
   480  				reflect.Uint32, reflect.Uint64, reflect.Uintptr:
   481  
   482  				keys := value.MapKeys()
   483  				sort.Slice(keys, func(i, j int) bool {
   484  					return fmt.Sprint(keys[i]) < fmt.Sprint(keys[j])
   485  				})
   486  				for _, k := range keys {
   487  					val := value.MapIndex(k)
   488  					// if isNil(val) {
   489  					// 	continue
   490  					// }
   491  
   492  					sub := convertRec(ctx, nilIsTop, val.Interface())
   493  					// mimic behavior of encoding/json: report error of
   494  					// unsupported type.
   495  					if sub == nil {
   496  						return ctx.AddErrf("unsupported Go type (%T)", val.Interface())
   497  					}
   498  					if isBottom(sub) {
   499  						return sub
   500  					}
   501  
   502  					s := fmt.Sprint(k)
   503  					f := ctx.StringLabel(s)
   504  					arc, ok := sub.(*adt.Vertex)
   505  					if ok {
   506  						a := *arc
   507  						arc = &a
   508  						arc.Label = f
   509  					} else {
   510  						arc = &adt.Vertex{Label: f, BaseValue: sub}
   511  						arc.ForceDone()
   512  						arc.AddConjunct(adt.MakeRootConjunct(nil, sub))
   513  					}
   514  					v.Arcs = append(v.Arcs, arc)
   515  				}
   516  			}
   517  
   518  			return v
   519  
   520  		case reflect.Slice, reflect.Array:
   521  			var values []adt.Value
   522  
   523  			for i := 0; i < value.Len(); i++ {
   524  				val := value.Index(i)
   525  				x := convertRec(ctx, nilIsTop, val.Interface())
   526  				if x == nil {
   527  					return ctx.AddErrf("unsupported Go type (%T)",
   528  						val.Interface())
   529  				}
   530  				if isBottom(x) {
   531  					return x
   532  				}
   533  				values = append(values, x)
   534  			}
   535  
   536  			return ctx.NewList(values...)
   537  		}
   538  	}
   539  	return nil
   540  }
   541  
   542  func toInt(ctx *adt.OpContext, x int64) adt.Value {
   543  	n := &adt.Num{Src: ctx.Source(), K: adt.IntKind}
   544  	n.X = *apd.New(x, 0)
   545  	return n
   546  }
   547  
   548  func toUint(ctx *adt.OpContext, x uint64) adt.Value {
   549  	n := &adt.Num{Src: ctx.Source(), K: adt.IntKind}
   550  	n.X.Coeff.SetUint64(x)
   551  	return n
   552  }
   553  
   554  func convertGoType(ctx *adt.OpContext, t reflect.Type) adt.Expr {
   555  	// TODO: this can be much more efficient.
   556  	// TODO: synchronize
   557  	return goTypeToValue(ctx, true, t)
   558  }
   559  
   560  var (
   561  	jsonMarshaler = reflect.TypeOf(new(json.Marshaler)).Elem()
   562  	textMarshaler = reflect.TypeOf(new(encoding.TextMarshaler)).Elem()
   563  	topSentinel   = ast.NewIdent("_")
   564  )
   565  
   566  // goTypeToValue converts a Go Type to a value.
   567  //
   568  // TODO: if this value will always be unified with a concrete type in Go, then
   569  // many of the fields may be omitted.
   570  func goTypeToValue(ctx *adt.OpContext, allowNullDefault bool, t reflect.Type) adt.Expr {
   571  	if _, t, ok := ctx.LoadType(t); ok {
   572  		return t
   573  	}
   574  
   575  	_, v := goTypeToValueRec(ctx, allowNullDefault, t)
   576  	if v == nil {
   577  		return ctx.AddErrf("unsupported Go type (%v)", t)
   578  	}
   579  	return v
   580  }
   581  
   582  func goTypeToValueRec(ctx *adt.OpContext, allowNullDefault bool, t reflect.Type) (e ast.Expr, expr adt.Expr) {
   583  	if src, t, ok := ctx.LoadType(t); ok {
   584  		return src, t
   585  	}
   586  
   587  	switch reflect.Zero(t).Interface().(type) {
   588  	case *big.Int, big.Int:
   589  		e = ast.NewIdent("int")
   590  		goto store
   591  
   592  	case *big.Float, big.Float, *big.Rat, big.Rat:
   593  		e = ast.NewIdent("number")
   594  		goto store
   595  
   596  	case *apd.Decimal, apd.Decimal:
   597  		e = ast.NewIdent("number")
   598  		goto store
   599  	}
   600  
   601  	// Even if this is for types that we know cast to a certain type, it can't
   602  	// hurt to return top, as in these cases the concrete values will be
   603  	// strict instances and there cannot be any tags that further constrain
   604  	// the values.
   605  	if t.Implements(jsonMarshaler) || t.Implements(textMarshaler) {
   606  		e = topSentinel
   607  		goto store
   608  	}
   609  
   610  	switch k := t.Kind(); k {
   611  	case reflect.Ptr:
   612  		elem := t.Elem()
   613  		for elem.Kind() == reflect.Ptr {
   614  			elem = elem.Elem()
   615  		}
   616  		e, _ = goTypeToValueRec(ctx, false, elem)
   617  		if allowNullDefault {
   618  			e = wrapOrNull(e)
   619  		}
   620  
   621  	case reflect.Interface:
   622  		switch t.Name() {
   623  		case "error":
   624  			// This is really null | _|_. There is no error if the error is null.
   625  			e = ast.NewNull()
   626  		default:
   627  			e = topSentinel // `_`
   628  		}
   629  
   630  	case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
   631  		reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   632  		e = compile.LookupRange(t.Kind().String()).Source().(ast.Expr)
   633  
   634  	case reflect.Uint, reflect.Uintptr:
   635  		e = compile.LookupRange("uint64").Source().(ast.Expr)
   636  
   637  	case reflect.Int:
   638  		e = compile.LookupRange("int64").Source().(ast.Expr)
   639  
   640  	case reflect.String:
   641  		e = ast.NewIdent("__string")
   642  
   643  	case reflect.Bool:
   644  		e = ast.NewIdent("__bool")
   645  
   646  	case reflect.Float32, reflect.Float64:
   647  		e = ast.NewIdent("__number")
   648  
   649  	case reflect.Struct:
   650  		obj := &ast.StructLit{}
   651  
   652  		// TODO: dirty trick: set this to a temporary Vertex and then update the
   653  		// arcs and conjuncts of this vertex below. This will allow circular
   654  		// references. Maybe have a special kind of "hardlink" reference.
   655  		ctx.StoreType(t, obj, nil)
   656  
   657  		for i := 0; i < t.NumField(); i++ {
   658  			f := t.Field(i)
   659  			if f.PkgPath != "" {
   660  				continue
   661  			}
   662  			_, ok := f.Tag.Lookup("cue")
   663  			elem, _ := goTypeToValueRec(ctx, !ok, f.Type)
   664  			if isBad(elem) {
   665  				continue // Ignore fields for unsupported types
   666  			}
   667  
   668  			// leave errors like we do during normal evaluation or do we
   669  			// want to return the error?
   670  			name := getName(&f)
   671  			if name == "-" {
   672  				continue
   673  			}
   674  
   675  			if tag, ok := f.Tag.Lookup("cue"); ok {
   676  				v := parseTag(ctx, obj, name, tag)
   677  				if isBad(v) {
   678  					return v, nil
   679  				}
   680  				elem = ast.NewBinExpr(token.AND, elem, v)
   681  			}
   682  			// TODO: if an identifier starts with __ (or otherwise is not a
   683  			// valid CUE name), make it a string and create a map to a new
   684  			// name for references.
   685  
   686  			// The GO JSON decoder always allows a value to be undefined.
   687  			d := &ast.Field{Label: ast.NewIdent(name), Value: elem}
   688  			if isOptional(&f) {
   689  				internal.SetConstraint(d, token.OPTION)
   690  			}
   691  			obj.Elts = append(obj.Elts, d)
   692  		}
   693  
   694  		// TODO: should we validate references here? Can be done using
   695  		// astutil.ToFile and astutil.Resolve.
   696  
   697  		e = obj
   698  
   699  	case reflect.Array, reflect.Slice:
   700  		if t.Elem().Kind() == reflect.Uint8 {
   701  			e = ast.NewIdent("__bytes")
   702  		} else {
   703  			elem, _ := goTypeToValueRec(ctx, allowNullDefault, t.Elem())
   704  			if elem == nil {
   705  				b := ctx.AddErrf("unsupported Go type (%v)", t.Elem())
   706  				return &ast.BadExpr{}, b
   707  			}
   708  
   709  			if t.Kind() == reflect.Array {
   710  				e = ast.NewBinExpr(token.MUL,
   711  					ast.NewLit(token.INT, strconv.Itoa(t.Len())),
   712  					ast.NewList(elem))
   713  			} else {
   714  				e = ast.NewList(&ast.Ellipsis{Type: elem})
   715  			}
   716  		}
   717  		if k == reflect.Slice {
   718  			e = wrapOrNull(e)
   719  		}
   720  
   721  	case reflect.Map:
   722  		switch key := t.Key(); key.Kind() {
   723  		case reflect.String, reflect.Int, reflect.Int8, reflect.Int16,
   724  			reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8,
   725  			reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
   726  		default:
   727  			b := ctx.AddErrf("unsupported Go type for map key (%v)", key)
   728  			return &ast.BadExpr{}, b
   729  		}
   730  
   731  		v, x := goTypeToValueRec(ctx, allowNullDefault, t.Elem())
   732  		if v == nil {
   733  			b := ctx.AddErrf("unsupported Go type (%v)", t.Elem())
   734  			return &ast.BadExpr{}, b
   735  		}
   736  		if isBad(v) {
   737  			return v, x
   738  		}
   739  
   740  		e = ast.NewStruct(&ast.Field{
   741  			Label: ast.NewList(ast.NewIdent("__string")),
   742  			Value: v,
   743  		})
   744  
   745  		e = wrapOrNull(e)
   746  	}
   747  
   748  store:
   749  	// TODO: store error if not nil?
   750  	if e != nil {
   751  		f := &ast.File{Decls: []ast.Decl{&ast.EmbedDecl{Expr: e}}}
   752  		astutil.Resolve(f, func(_ token.Pos, msg string, args ...interface{}) {
   753  			ctx.AddErrf(msg, args...)
   754  		})
   755  		var x adt.Expr
   756  		c, err := compile.Expr(nil, ctx, pkgID(), e)
   757  		if err != nil {
   758  			b := &adt.Bottom{Err: err}
   759  			ctx.AddBottom(b)
   760  			x = b
   761  		} else {
   762  			x = c.Expr()
   763  		}
   764  		ctx.StoreType(t, e, x)
   765  		return e, x
   766  	}
   767  	return e, nil
   768  }
   769  
   770  func isBottom(x adt.Node) bool {
   771  	if x == nil {
   772  		return true
   773  	}
   774  	b, _ := x.(*adt.Bottom)
   775  	return b != nil
   776  }
   777  
   778  func isBad(x ast.Expr) bool {
   779  	if x == nil {
   780  		return true
   781  	}
   782  	if bad, _ := x.(*ast.BadExpr); bad != nil {
   783  		return true
   784  	}
   785  	return false
   786  }
   787  
   788  func wrapOrNull(e ast.Expr) ast.Expr {
   789  	switch x := e.(type) {
   790  	case *ast.BasicLit:
   791  		if x.Kind == token.NULL {
   792  			return x
   793  		}
   794  	case *ast.BadExpr:
   795  		return e
   796  	}
   797  	return makeNullable(e, true)
   798  }
   799  
   800  func makeNullable(e ast.Expr, nullIsDefault bool) ast.Expr {
   801  	var null ast.Expr = ast.NewNull()
   802  	if nullIsDefault {
   803  		null = &ast.UnaryExpr{Op: token.MUL, X: null}
   804  	}
   805  	return ast.NewBinExpr(token.OR, null, e)
   806  }
   807  
   808  // pkgID returns a package path that can never resolve to an existing package.
   809  func pkgID() string {
   810  	return "_"
   811  }
   812  

View as plain text