cuelang.org/go@v0.10.1/cue/context.go (about)

     1  // Copyright 2018 The 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 cue
    16  
    17  import (
    18  	"cmp"
    19  
    20  	"cuelang.org/go/cue/ast"
    21  	"cuelang.org/go/cue/ast/astutil"
    22  	"cuelang.org/go/cue/build"
    23  	"cuelang.org/go/cue/errors"
    24  	"cuelang.org/go/cue/token"
    25  	"cuelang.org/go/internal/core/adt"
    26  	"cuelang.org/go/internal/core/compile"
    27  	"cuelang.org/go/internal/core/convert"
    28  	"cuelang.org/go/internal/core/debug"
    29  	"cuelang.org/go/internal/core/eval"
    30  	"cuelang.org/go/internal/core/runtime"
    31  )
    32  
    33  // A Context is used for creating CUE Values.
    34  //
    35  // A Context keeps track of loaded instances, indices of internal
    36  // representations of values, and defines the set of supported builtins. Any
    37  // operation that involves two Values should originate from the same Context.
    38  //
    39  // Use [cuelang.org/go/cue/cuecontext.New] to create a new context.
    40  type Context runtime.Runtime
    41  
    42  func (c *Context) runtime() *runtime.Runtime {
    43  	rt := (*runtime.Runtime)(c)
    44  	if !rt.IsInitialized() {
    45  		panic("cue: uninitialized Context: use cuecontext.New instead of zero value")
    46  	}
    47  
    48  	return rt
    49  }
    50  
    51  func (c *Context) ctx() *adt.OpContext {
    52  	return newContext(c.runtime())
    53  }
    54  
    55  // Context reports the Context with which this value was created.
    56  func (v Value) Context() *Context {
    57  	return (*Context)(v.idx)
    58  }
    59  
    60  // A BuildOption defines options for the various build-related methods of
    61  // Context.
    62  type BuildOption func(o *runtime.Config)
    63  
    64  // Scope defines a context in which to resolve unresolved identifiers.
    65  //
    66  // Only one scope may be given. It panics if more than one scope is given
    67  // or if the Context in which scope was created differs from the one where
    68  // this option is used.
    69  func Scope(scope Value) BuildOption {
    70  	return func(o *runtime.Config) {
    71  		if o.Runtime != scope.idx {
    72  			panic("incompatible runtime")
    73  		}
    74  		if o.Scope != nil {
    75  			panic("more than one scope is given")
    76  		}
    77  		o.Scope = valueScope(scope)
    78  	}
    79  }
    80  
    81  // Filename assigns a filename to parsed content.
    82  func Filename(filename string) BuildOption {
    83  	return func(o *runtime.Config) { o.Filename = filename }
    84  }
    85  
    86  // ImportPath defines the import path to use for building CUE. The import path
    87  // influences the scope in which identifiers occurring in the input CUE are
    88  // defined. Passing the empty string is equal to not specifying this option.
    89  //
    90  // This option is typically not necessary when building using a build.Instance,
    91  // but takes precedence otherwise.
    92  func ImportPath(path string) BuildOption {
    93  	return func(o *runtime.Config) { o.ImportPath = path }
    94  }
    95  
    96  // InferBuiltins allows unresolved references to bind to builtin packages with a
    97  // unique package name.
    98  //
    99  // This option is intended for evaluating expressions in a context where import
   100  // statements cannot be used. It is not recommended to use this for evaluating
   101  // CUE files.
   102  func InferBuiltins(elide bool) BuildOption {
   103  	return func(o *runtime.Config) {
   104  		o.Imports = func(x *ast.Ident) (pkgPath string) {
   105  			return o.Runtime.BuiltinPackagePath(x.Name)
   106  		}
   107  	}
   108  }
   109  
   110  func (c *Context) parseOptions(options []BuildOption) (cfg runtime.Config) {
   111  	cfg.Runtime = (*runtime.Runtime)(c)
   112  	for _, f := range options {
   113  		f(&cfg)
   114  	}
   115  	return cfg
   116  }
   117  
   118  // BuildInstance creates a Value from the given build.Instance.
   119  //
   120  // The returned Value will represent an error, accessible through Err, if any
   121  // error occurred.
   122  func (c *Context) BuildInstance(i *build.Instance, options ...BuildOption) Value {
   123  	cfg := c.parseOptions(options)
   124  	v, err := c.runtime().Build(&cfg, i)
   125  	if err != nil {
   126  		return c.makeError(err)
   127  	}
   128  	return c.make(v)
   129  }
   130  
   131  func (c *Context) makeError(err errors.Error) Value {
   132  	b := &adt.Bottom{Err: err}
   133  	node := &adt.Vertex{BaseValue: b}
   134  	node.ForceDone()
   135  	node.AddConjunct(adt.MakeRootConjunct(nil, b))
   136  	return c.make(node)
   137  }
   138  
   139  // BuildInstances creates a Value for each of the given instances and reports
   140  // the combined errors or nil if there were no errors.
   141  func (c *Context) BuildInstances(instances []*build.Instance) ([]Value, error) {
   142  	var errs errors.Error
   143  	var a []Value
   144  	for _, b := range instances {
   145  		v, err := c.runtime().Build(nil, b)
   146  		if err != nil {
   147  			errs = errors.Append(errs, err)
   148  			a = append(a, c.makeError(err))
   149  		} else {
   150  			a = append(a, c.make(v))
   151  		}
   152  	}
   153  	return a, errs
   154  }
   155  
   156  // BuildFile creates a Value from f.
   157  //
   158  // The returned Value will represent an error, accessible through Err, if any
   159  // error occurred.
   160  func (c *Context) BuildFile(f *ast.File, options ...BuildOption) Value {
   161  	cfg := c.parseOptions(options)
   162  	return c.compile(c.runtime().CompileFile(&cfg, f))
   163  }
   164  
   165  func (c *Context) compile(v *adt.Vertex, p *build.Instance) Value {
   166  	if p.Err != nil {
   167  		return c.makeError(p.Err)
   168  	}
   169  	return c.make(v)
   170  }
   171  
   172  // BuildExpr creates a Value from x.
   173  //
   174  // The returned Value will represent an error, accessible through Err, if any
   175  // error occurred.
   176  func (c *Context) BuildExpr(x ast.Expr, options ...BuildOption) Value {
   177  	r := c.runtime()
   178  	cfg := c.parseOptions(options)
   179  
   180  	ctx := c.ctx()
   181  
   182  	// TODO: move to runtime?: it probably does not make sense to treat BuildExpr
   183  	// and the expression resulting from CompileString differently.
   184  	astutil.ResolveExpr(x, errFn)
   185  
   186  	pkgPath := cmp.Or(cfg.ImportPath, anonymousPkg)
   187  
   188  	conjunct, err := compile.Expr(&cfg.Config, r, pkgPath, x)
   189  	if err != nil {
   190  		return c.makeError(err)
   191  	}
   192  	v := adt.Resolve(ctx, conjunct)
   193  
   194  	return c.make(v)
   195  }
   196  
   197  func errFn(pos token.Pos, msg string, args ...interface{}) {}
   198  
   199  // resolveExpr binds unresolved expressions to values in the expression or v.
   200  func resolveExpr(ctx *adt.OpContext, v Value, x ast.Expr) adt.Value {
   201  	cfg := &compile.Config{Scope: valueScope(v)}
   202  
   203  	astutil.ResolveExpr(x, errFn)
   204  
   205  	c, err := compile.Expr(cfg, ctx, anonymousPkg, x)
   206  	if err != nil {
   207  		return &adt.Bottom{Err: err}
   208  	}
   209  	return adt.Resolve(ctx, c)
   210  }
   211  
   212  // anonymousPkg reports a package path that can never resolve to a valid package.
   213  const anonymousPkg = "_"
   214  
   215  // CompileString parses and build a Value from the given source string.
   216  //
   217  // The returned Value will represent an error, accessible through Err, if any
   218  // error occurred.
   219  func (c *Context) CompileString(src string, options ...BuildOption) Value {
   220  	cfg := c.parseOptions(options)
   221  	return c.compile(c.runtime().Compile(&cfg, src))
   222  }
   223  
   224  // CompileBytes parses and build a Value from the given source bytes.
   225  //
   226  // The returned Value will represent an error, accessible through Err, if any
   227  // error occurred.
   228  func (c *Context) CompileBytes(b []byte, options ...BuildOption) Value {
   229  	cfg := c.parseOptions(options)
   230  	return c.compile(c.runtime().Compile(&cfg, b))
   231  }
   232  
   233  // TODO: fs.FS or custom wrapper?
   234  // // CompileFile parses and build a Value from the given source bytes.
   235  // //
   236  // // The returned Value will represent an error, accessible through Err, if any
   237  // // error occurred.
   238  // func (c *Context) CompileFile(f fs.File, options ...BuildOption) Value {
   239  // 	b, err := io.ReadAll(f)
   240  // 	if err != nil {
   241  // 		return c.makeError(errors.Promote(err, "parsing file system file"))
   242  // 	}
   243  // 	return c.compile(c.runtime().Compile("", b))
   244  // }
   245  
   246  func (c *Context) make(v *adt.Vertex) Value {
   247  	opCtx := newContext(c.runtime())
   248  	// TODO: this is currently needed to ensure that node is properly recognized
   249  	// as evaluated. Not dereferencing nodes, however, will have the benefit of
   250  	// retaining more information. Remove the indirection when the code will be
   251  	// able to properly handle this.
   252  	x := newValueRoot(c.runtime(), opCtx, v)
   253  	adt.AddStats(opCtx)
   254  	return x
   255  }
   256  
   257  // An EncodeOption defines options for the various encoding-related methods of
   258  // Context.
   259  type EncodeOption func(*encodeOptions)
   260  
   261  type encodeOptions struct {
   262  	nilIsTop bool
   263  }
   264  
   265  func (o *encodeOptions) process(option []EncodeOption) {
   266  	for _, f := range option {
   267  		f(o)
   268  	}
   269  }
   270  
   271  // NilIsAny indicates whether a nil value is interpreted as null or _.
   272  //
   273  // The default is to interpret nil as _.
   274  func NilIsAny(isAny bool) EncodeOption {
   275  	return func(o *encodeOptions) { o.nilIsTop = isAny }
   276  }
   277  
   278  // Encode converts a Go value to a CUE value.
   279  //
   280  // The returned Value will represent an error, accessible through Err, if any
   281  // error occurred.
   282  //
   283  // Encode traverses the value v recursively. If an encountered value implements
   284  // the json.Marshaler interface and is not a nil pointer, Encode calls its
   285  // MarshalJSON method to produce JSON and convert that to CUE instead. If no
   286  // MarshalJSON method is present but the value implements encoding.TextMarshaler
   287  // instead, Encode calls its MarshalText method and encodes the result as a
   288  // string.
   289  //
   290  // Otherwise, Encode uses the following type-dependent default encodings:
   291  //
   292  // Boolean values encode as CUE booleans.
   293  //
   294  // Floating point, integer, and *big.Int and *big.Float values encode as CUE
   295  // numbers.
   296  //
   297  // String values encode as CUE strings coerced to valid UTF-8, replacing
   298  // sequences of invalid bytes with the Unicode replacement rune as per Unicode's
   299  // and W3C's recommendation.
   300  //
   301  // Array and slice values encode as CUE lists, except that []byte encodes as a
   302  // bytes value, and a nil slice encodes as the null.
   303  //
   304  // Struct values encode as CUE structs. Each exported struct field becomes a
   305  // member of the object, using the field name as the object key, unless the
   306  // field is omitted for one of the reasons given below.
   307  //
   308  // The encoding of each struct field can be customized by the format string
   309  // stored under the "json" key in the struct field's tag. The format string
   310  // gives the name of the field, possibly followed by a comma-separated list of
   311  // options. The name may be empty in order to specify options without overriding
   312  // the default field name.
   313  //
   314  // The "omitempty" option specifies that the field should be omitted from the
   315  // encoding if the field has an empty value, defined as false, 0, a nil pointer,
   316  // a nil interface value, and any empty array, slice, map, or string.
   317  //
   318  // See the documentation for Go's json.Marshal for more details on the field
   319  // tags and their meaning.
   320  //
   321  // Anonymous struct fields are usually encoded as if their inner exported
   322  // fields were fields in the outer struct, subject to the usual Go visibility
   323  // rules amended as described in the next paragraph. An anonymous struct field
   324  // with a name given in its JSON tag is treated as having that name, rather than
   325  // being anonymous. An anonymous struct field of interface type is treated the
   326  // same as having that type as its name, rather than being anonymous.
   327  //
   328  // The Go visibility rules for struct fields are amended for when deciding which
   329  // field to encode or decode. If there are multiple fields at the same level,
   330  // and that level is the least nested (and would therefore be the nesting level
   331  // selected by the usual Go rules), the following extra rules apply:
   332  //
   333  // 1) Of those fields, if any are JSON-tagged, only tagged fields are
   334  // considered, even if there are multiple untagged fields that would otherwise
   335  // conflict.
   336  //
   337  // 2) If there is exactly one field (tagged or not according to the first rule),
   338  // that is selected.
   339  //
   340  // 3) Otherwise there are multiple fields, and all are ignored; no error occurs.
   341  //
   342  // Map values encode as CUE structs. The map's key type must either be a string,
   343  // an integer type, or implement encoding.TextMarshaler. The map keys are sorted
   344  // and used as CUE struct field names by applying the following rules, subject
   345  // to the UTF-8 coercion described for string values above:
   346  //
   347  //   - keys of any string type are used directly
   348  //   - encoding.TextMarshalers are marshaled
   349  //   - integer keys are converted to strings
   350  //
   351  // Pointer values encode as the value pointed to. A nil pointer encodes as the
   352  // null CUE value.
   353  //
   354  // Interface values encode as the value contained in the interface. A nil
   355  // interface value encodes as the null CUE value. The NilIsAny EncodingOption
   356  // can be used to interpret nil as any (_) instead.
   357  //
   358  // Channel, complex, and function values cannot be encoded in CUE. Attempting to
   359  // encode such a value results in the returned value being an error, accessible
   360  // through the Err method.
   361  func (c *Context) Encode(x interface{}, option ...EncodeOption) Value {
   362  	switch v := x.(type) {
   363  	case adt.Value:
   364  		return newValueRoot(c.runtime(), c.ctx(), v)
   365  	}
   366  	var options encodeOptions
   367  	options.process(option)
   368  
   369  	ctx := c.ctx()
   370  	// TODO: is true the right default?
   371  	expr := convert.GoValueToValue(ctx, x, options.nilIsTop)
   372  	n := &adt.Vertex{}
   373  	n.AddConjunct(adt.MakeRootConjunct(nil, expr))
   374  	n.Finalize(ctx)
   375  	return c.make(n)
   376  }
   377  
   378  // Encode converts a Go type to a CUE value.
   379  //
   380  // The returned Value will represent an error, accessible through Err, if any
   381  // error occurred.
   382  func (c *Context) EncodeType(x interface{}, option ...EncodeOption) Value {
   383  	switch v := x.(type) {
   384  	case *adt.Vertex:
   385  		return c.make(v)
   386  	}
   387  
   388  	ctx := c.ctx()
   389  	expr, err := convert.GoTypeToExpr(ctx, x)
   390  	if err != nil {
   391  		return c.makeError(err)
   392  	}
   393  	n := &adt.Vertex{}
   394  	n.AddConjunct(adt.MakeRootConjunct(nil, expr))
   395  	n.Finalize(ctx)
   396  	return c.make(n)
   397  }
   398  
   399  // NewList creates a Value that is a list of the given values.
   400  //
   401  // All Values must be created by c.
   402  func (c *Context) NewList(v ...Value) Value {
   403  	a := make([]adt.Value, len(v))
   404  	for i, x := range v {
   405  		if x.idx != (*runtime.Runtime)(c) {
   406  			panic("values must be from same Context")
   407  		}
   408  		a[i] = x.v
   409  	}
   410  	return c.make(c.ctx().NewList(a...))
   411  }
   412  
   413  // TODO:
   414  
   415  // func (c *Context) NewExpr(op Op, v ...Value) Value {
   416  // 	return Value{}
   417  // }
   418  
   419  // func (c *Context) NewValue(v ...ValueElem) Value {
   420  // 	return Value{}
   421  // }
   422  
   423  // func NewAttr(key string, values ...string) *Attribute {
   424  // 	return &Attribute{}
   425  // }
   426  
   427  // // Clear unloads all previously-loaded imports.
   428  // func (c *Context) Clear() {
   429  // }
   430  
   431  // // Values created up to the point of the Fork will be valid in both runtimes.
   432  // func (c *Context) Fork() *Context {
   433  // 	return nil
   434  // }
   435  
   436  // type ValueElem interface {
   437  // }
   438  
   439  // func NewField(sel Selector, value Value, attrs ...Attribute) ValueElem {
   440  // 	return nil
   441  // }
   442  
   443  // func NewDocComment(text string) ValueElem {
   444  // 	return nil
   445  // }
   446  
   447  // newContext returns a new evaluation context.
   448  func newContext(idx *runtime.Runtime) *adt.OpContext {
   449  	if idx == nil {
   450  		return nil
   451  	}
   452  	return eval.NewContext(idx, nil)
   453  }
   454  
   455  func debugStr(ctx *adt.OpContext, v adt.Node) string {
   456  	return debug.NodeString(ctx, v, nil)
   457  }
   458  
   459  func str(c *adt.OpContext, v adt.Node) string {
   460  	return debugStr(c, v)
   461  }
   462  
   463  // eval returns the evaluated value. This may not be the vertex.
   464  //
   465  // Deprecated: use ctx.value
   466  func (v Value) eval(ctx *adt.OpContext) adt.Value {
   467  	if v.v == nil {
   468  		panic("undefined value")
   469  	}
   470  	x := manifest(ctx, v.v)
   471  	return x.Value()
   472  }
   473  
   474  // TODO: change from Vertex to Vertex.
   475  func manifest(ctx *adt.OpContext, v *adt.Vertex) *adt.Vertex {
   476  	v.Finalize(ctx)
   477  	return v
   478  }