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