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 }