...

Source file src/cuelang.org/go/cue/context.go

Documentation: cuelang.org/go/cue

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

View as plain text