From d9af603eb379f0587b2e4e73fa57464aa716bde6 Mon Sep 17 00:00:00 2001 From: Marcel van Lohuizen Date: Fri, 30 Apr 2021 11:43:07 +0200 Subject: [PATCH] cue: add InferBuiltins EncodeOption This functionality is currently partially internal or deprecated. This adds this functionality to the new API. This also removes a cyclic dependency in package dependencies that needs to be removed for the implementation of structure sharing. This moves resolveExpr to below BuildExpr, as their bodies are almost the same (functionally the same at the moment). This also removes dead code. Expresses the old CompileExpr in terms of the new BuildExpr. This is mostly done as a verification and to allow automatic rewrites in the future. Change-Id: I7d63aa097dd8f652b9b0b0181a2b148c8bb1f2d4 Reviewed-on: https://cue-review.googlesource.com/c/cue/+/9571 Reviewed-by: CUE cueckoo Reviewed-by: Marcel van Lohuizen Reviewed-by: Paul Jolly --- cmd/cue/cmd/common.go | 7 +++- cue/build.go | 20 +++++++--- cue/context.go | 49 +++++++++++++++++++++++- cue/instance.go | 70 +++------------------------------- cue/query.go | 19 --------- internal/core/runtime/build.go | 9 ----- internal/value/value.go | 38 ------------------ pkg/encoding/yaml/manual.go | 17 ++++----- 8 files changed, 79 insertions(+), 150 deletions(-) diff --git a/cmd/cue/cmd/common.go b/cmd/cue/cmd/common.go index 1840f9709..eddf60963 100644 --- a/cmd/cue/cmd/common.go +++ b/cmd/cue/cmd/common.go @@ -350,8 +350,11 @@ func (i *expressionIter) value() cue.Value { if len(i.expr) == 0 { return i.iter.value() } - // TODO: replace with FillPath. - return value.EvalExpr(i.iter.value(), i.expr[i.i]) + v := i.iter.value() + return v.Context().BuildExpr(i.expr[i.i], + cue.Scope(v), + cue.InferBuiltins(true), + ) } type config struct { diff --git a/cue/build.go b/cue/build.go index 9c603af81..f4cd6b25c 100644 --- a/cue/build.go +++ b/cue/build.go @@ -16,6 +16,7 @@ package cue import ( "cuelang.org/go/cue/ast" + "cuelang.org/go/cue/ast/astutil" "cuelang.org/go/cue/build" "cuelang.org/go/cue/errors" "cuelang.org/go/internal/core/adt" @@ -78,11 +79,22 @@ func (r *hiddenRuntime) CompileFile(file *ast.File) (*Instance, error) { // // Deprecated: use BuildExpr. The use of Instance is being phased out. func (r *hiddenRuntime) CompileExpr(expr ast.Expr) (*Instance, error) { - v, p, err := r.runtime().CompileExpr(nil, expr) + f, err := astutil.ToFile(expr) if err != nil { return nil, err } - return r.complete(p, v) + v := (*Context)(r).BuildExpr(expr) + err = v.Err() + inst := &Instance{ + index: r.runtime(), + root: v.v, + inst: &build.Instance{ + Files: []*ast.File{f}, + }, + Err: errors.Promote(err, ""), + Incomplete: err != nil, + } + return inst, err } // Parse parses a CUE source value into a CUE Instance. The source code may be @@ -149,7 +161,3 @@ func (r *hiddenRuntime) FromExpr(expr ast.Expr) (*Instance, error) { Decls: []ast.Decl{&ast.EmbedDecl{Expr: expr}}, }) } - -func isBuiltin(s string) bool { - return runtime.SharedRuntime.IsBuiltinPackage(s) -} diff --git a/cue/context.go b/cue/context.go index e84f4fb36..536056398 100644 --- a/cue/context.go +++ b/cue/context.go @@ -16,9 +16,12 @@ package cue import ( "cuelang.org/go/cue/ast" + "cuelang.org/go/cue/ast/astutil" "cuelang.org/go/cue/build" "cuelang.org/go/cue/errors" + "cuelang.org/go/cue/token" "cuelang.org/go/internal/core/adt" + "cuelang.org/go/internal/core/compile" "cuelang.org/go/internal/core/convert" "cuelang.org/go/internal/core/debug" "cuelang.org/go/internal/core/eval" @@ -78,6 +81,23 @@ func Filename(filename string) BuildOption { return func(o *runtime.Config) { o.Filename = filename } } +// InferBuiltins allows unresolved references to bind to builtin packages with a +// unique package name. +// +// This option is intended for evaluating expressions in a context where import +// statements cannot be used. It is not recommended to use this for evaluating +// CUE files. +func InferBuiltins(elide bool) BuildOption { + return func(o *runtime.Config) { + o.Imports = func(x *ast.Ident) (pkgPath string) { + if !o.Runtime.IsBuiltinPackage(x.Name) { + return "" + } + return x.Name + } + } +} + func (c *Context) parseOptions(options []BuildOption) (cfg runtime.Config) { cfg.Runtime = (*runtime.Runtime)(c) for _, f := range options { @@ -143,14 +163,39 @@ func (c *Context) compile(v *adt.Vertex, p *build.Instance) Value { // The returned Value will represent an error, accessible through Err, if any // error occurred. func (c *Context) BuildExpr(x ast.Expr, options ...BuildOption) Value { + r := c.runtime() cfg := c.parseOptions(options) - v, p, err := c.runtime().CompileExpr(&cfg, x) + + ctx := c.ctx() + + astutil.ResolveExpr(x, errFn) + conjunct, err := compile.Expr(&cfg.Config, r, anonymousPkg, x) if err != nil { - return c.makeError(p.Err) + return c.makeError(err) } + v := adt.Resolve(ctx, conjunct) + return c.make(v) } +func errFn(pos token.Pos, msg string, args ...interface{}) {} + +// resolveExpr binds unresolved expressions to values in the expression or v. +func resolveExpr(ctx *adt.OpContext, v *adt.Vertex, x ast.Expr) adt.Value { + cfg := &compile.Config{Scope: v} + + astutil.ResolveExpr(x, errFn) + + c, err := compile.Expr(cfg, ctx, anonymousPkg, x) + if err != nil { + return &adt.Bottom{Err: err} + } + return adt.Resolve(ctx, c) +} + +// anonymousPkg reports a package path that can never resolve to a valid package. +const anonymousPkg = "_" + // CompileString parses and build a Value from the given source string. // // The returned Value will represent an error, accessible through Err, if any diff --git a/cue/instance.go b/cue/instance.go index df01f0afc..934084447 100644 --- a/cue/instance.go +++ b/cue/instance.go @@ -190,66 +190,6 @@ func (inst *Instance) eval(ctx *adt.OpContext) adt.Value { return v } -// pkgID reports a package path that can never resolve to a valid package. -func pkgID() string { - return "_" -} - -// evalExpr evaluates expr within scope. -func evalExpr(ctx *adt.OpContext, scope *adt.Vertex, expr ast.Expr) adt.Value { - cfg := &compile.Config{ - Scope: scope, - Imports: func(x *ast.Ident) (pkgPath string) { - if !isBuiltin(x.Name) { - return "" - } - return x.Name - }, - } - - c, err := compile.Expr(cfg, ctx, pkgID(), expr) - if err != nil { - return &adt.Bottom{Err: err} - } - return adt.Resolve(ctx, c) - - // scope.Finalize(ctx) // TODO: not appropriate here. - // switch s := scope.Value.(type) { - // case *bottom: - // return s - // case *adt.StructMarker: - // default: - // return ctx.mkErr(scope, "instance is not a struct, found %s", scope.Kind()) - // } - - // c := ctx - - // x, err := compile.Expr(&compile.Config{Scope: scope}, c.Runtime, expr) - // if err != nil { - // return c.NewErrf("could not evaluate %s: %v", c.Str(x), err) - // } - - // env := &adt.Environment{Vertex: scope} - - // switch v := x.(type) { - // case adt.Value: - // return v - // case adt.Resolver: - // r, err := c.Resolve(env, v) - // if err != nil { - // return err - // } - // return r - - // case adt.Evaluator: - // e, _ := c.Evaluate(env, x) - // return e - - // } - - // return c.NewErrf("could not evaluate %s", c.Str(x)) -} - // ID returns the package identifier that uniquely qualifies module and // package name. func (inst *Instance) ID() string { @@ -278,12 +218,12 @@ func (inst *Instance) Value() Value { // Eval evaluates an expression within an existing instance. // // Expressions may refer to builtin packages if they can be uniquely identified. +// +// Deprecated: use +// inst.Value().Context().BuildExpr(expr, Scope(inst.Value), InferBuiltins(true)) func (inst *hiddenInstance) Eval(expr ast.Expr) Value { - ctx := newContext(inst.index) - v := inst.root - v.Finalize(ctx) - result := evalExpr(ctx, v, expr) - return newValueRoot(inst.index, ctx, result) + v := inst.Value() + return v.Context().BuildExpr(expr, Scope(v), InferBuiltins(true)) } // DO NOT USE. diff --git a/cue/query.go b/cue/query.go index a4cec1fd9..44285270b 100644 --- a/cue/query.go +++ b/cue/query.go @@ -15,11 +15,7 @@ package cue import ( - "cuelang.org/go/cue/ast" - "cuelang.org/go/cue/ast/astutil" - "cuelang.org/go/cue/token" "cuelang.org/go/internal/core/adt" - "cuelang.org/go/internal/core/compile" ) // This file contains query-related code. @@ -38,21 +34,6 @@ func getScopePrefix(v Value, p Path) *adt.Vertex { return v.v } -func errFn(pos token.Pos, msg string, args ...interface{}) {} - -// resolveExpr binds unresolved expressions to values in the expression or v. -func resolveExpr(ctx *adt.OpContext, v *adt.Vertex, x ast.Expr) adt.Value { - cfg := &compile.Config{Scope: v} - - astutil.ResolveExpr(x, errFn) - - c, err := compile.Expr(cfg, ctx, pkgID(), x) - if err != nil { - return &adt.Bottom{Err: err} - } - return adt.Resolve(ctx, c) -} - // LookupPath reports the value for path p relative to v. func (v Value) LookupPath(p Path) Value { if v.v == nil { diff --git a/internal/core/runtime/build.go b/internal/core/runtime/build.go index e38101c8b..1dcb820e9 100644 --- a/internal/core/runtime/build.go +++ b/internal/core/runtime/build.go @@ -114,15 +114,6 @@ func (r *Runtime) CompileFile(cfg *Config, file *ast.File) (*adt.Vertex, *build. return v, p } -func (r *Runtime) CompileExpr(cfg *Config, expr ast.Expr) (*adt.Vertex, *build.Instance, error) { - f, err := astutil.ToFile(expr) - if err != nil { - return nil, nil, err - } - v, p := r.CompileFile(cfg, f) - return v, p, p.Err -} - func (x *Runtime) buildSpec(cfg *Config, b *build.Instance, spec *ast.ImportSpec) (errs errors.Error) { info, err := astutil.ParseImportSpec(spec) if err != nil { diff --git a/internal/value/value.go b/internal/value/value.go index 35e208ce1..5e0586476 100644 --- a/internal/value/value.go +++ b/internal/value/value.go @@ -20,10 +20,8 @@ import ( "strings" "cuelang.org/go/cue" - "cuelang.org/go/cue/ast" "cuelang.org/go/cue/errors" "cuelang.org/go/internal/core/adt" - "cuelang.org/go/internal/core/compile" "cuelang.org/go/internal/core/convert" "cuelang.org/go/internal/core/eval" "cuelang.org/go/internal/core/runtime" @@ -97,39 +95,3 @@ func FromGoType(r *cue.Context, x interface{}) cue.Value { n.AddConjunct(adt.MakeRootConjunct(nil, expr)) return r.Encode(n) } - -// EvalExpr evaluates an expression within an existing struct value. -// Identifiers only resolve to values defined within the struct. -// -// Expressions may refer to builtin packages if they can be uniquely identified -func EvalExpr(value cue.Value, expr ast.Expr) cue.Value { - r, scope := ToInternal(value) - ctx := eval.NewContext(r, nil) - - cfg := &compile.Config{ - Scope: scope, - Imports: func(x *ast.Ident) (pkgPath string) { - if !isBuiltin(x.Name) { - return "" - } - return x.Name - }, - } - - c, err := compile.Expr(cfg, ctx, pkgID(), expr) - if err != nil { - return MakeError(r, err) - } - v := adt.Resolve(ctx, c) - - return (*cue.Context)(r).Encode(v) -} - -func isBuiltin(s string) bool { - return runtime.SharedRuntime.IsBuiltinPackage(s) -} - -// pkgID reports a package path that can never resolve to a valid package. -func pkgID() string { - return "_" -} diff --git a/pkg/encoding/yaml/manual.go b/pkg/encoding/yaml/manual.go index f1fde157e..c01876319 100644 --- a/pkg/encoding/yaml/manual.go +++ b/pkg/encoding/yaml/manual.go @@ -23,7 +23,6 @@ import ( "cuelang.org/go/internal" cueyaml "cuelang.org/go/internal/encoding/yaml" "cuelang.org/go/internal/third_party/yaml" - "cuelang.org/go/internal/value" ) // Marshal returns the YAML encoding of v. @@ -84,7 +83,7 @@ func Validate(b []byte, v cue.Value) (bool, error) { if err != nil { return false, err } - r := value.ConvertToRuntime(v.Context()) + r := v.Context() for { expr, err := d.Decode() if err != nil { @@ -94,8 +93,8 @@ func Validate(b []byte, v cue.Value) (bool, error) { return false, err } - inst, err := r.CompileExpr(expr) - if err != nil { + x := r.BuildExpr(expr) + if err := x.Err(); err != nil { return false, err } @@ -108,7 +107,7 @@ func Validate(b []byte, v cue.Value) (bool, error) { // if err := v.Subsume(inst.Value(), cue.Final()); err != nil { // return false, err // } - x := v.Unify(inst.Value()) + x = v.Unify(x) if err := x.Err(); err != nil { return false, err } @@ -128,7 +127,7 @@ func ValidatePartial(b []byte, v cue.Value) (bool, error) { if err != nil { return false, err } - r := value.ConvertToRuntime(v.Context()) + r := v.Context() for { expr, err := d.Decode() if err != nil { @@ -138,12 +137,12 @@ func ValidatePartial(b []byte, v cue.Value) (bool, error) { return false, err } - inst, err := r.CompileExpr(expr) - if err != nil { + x := r.BuildExpr(expr) + if err := x.Err(); err != nil { return false, err } - if x := v.Unify(inst.Value()); x.Err() != nil { + if x := v.Unify(x); x.Err() != nil { return false, x.Err() } }