blob: 452d5ff4a39bee4e9751ba10cad1558445ec5159 [file] [log] [blame]
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +01001// 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
15package cue
16
17import (
18 "cuelang.org/go/cue/ast"
19 "cuelang.org/go/cue/build"
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010020 "cuelang.org/go/internal"
Marcel van Lohuizen66db9202018-12-17 19:02:08 +010021 "golang.org/x/exp/errors/fmt"
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010022)
23
24// An Instance defines a single configuration based on a collection of
25// underlying CUE files.
26type Instance struct {
27 *index
28
29 rootStruct *structLit // the struct to insert root values into
30 rootValue value // the value to evaluate: may add comprehensions
31
32 // scope is used as an additional top-level scope between the package scope
33 // and the predeclared identifiers.
34 scope *structLit
35
36 ImportPath string
37 Dir string
38 Name string
39
40 Incomplete bool // true if Pkg and all its dependencies are free of errors
41 Err error // non-nil if the package had errors
42
43 inst *build.Instance
44
45 complete bool // for cycle detection
46}
47
48// NewInstance creates a new instance. Use Insert to populate the instance.
49func (x *index) NewInstance(p *build.Instance) *Instance {
50 st := &structLit{baseValue: baseValue{nil}}
51 i := &Instance{
52 index: x,
53 rootStruct: st,
54 rootValue: st,
55 inst: p,
56 }
57 if p != nil {
58 i.ImportPath = p.ImportPath
59 i.Dir = p.Dir
60 i.Name = p.PkgName
61 if p.Err != nil {
62 i.setError(p.Err)
63 }
64 }
65 return i
66}
67
68func (inst *Instance) setError(err error) {
69 inst.Err = err
70 inst.Incomplete = true
71}
72
73func (inst *Instance) eval(ctx *context) evaluated {
Marcel van Lohuizen9a0e4132019-05-06 11:39:40 +020074 // TODO: remove manifest here? It may be good here, though not consistent.
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010075 v := ctx.manifest(inst.rootValue)
76 if s, ok := v.(*structLit); ok && s.emit != nil {
77 v = ctx.manifest(s.emit)
78 }
79 // manifest to benefit from validation step.
80 // TODO: consider not doing manifest.
81 return ctx.manifest(v)
82}
83
84func init() {
85 internal.EvalExpr = func(value, expr interface{}) interface{} {
86 v := value.(Value)
87 e := expr.(ast.Expr)
88 ctx := v.idx.newContext()
89 return newValueRoot(ctx, evalExpr(v.idx, v.eval(ctx), e))
90 }
91}
92
93func evalExpr(idx *index, x value, expr ast.Expr) evaluated {
94 if isBottom(x) {
95 return idx.mkErr(x, "error evaluating instance: %v", x)
96 }
97 obj, ok := x.(*structLit)
98 if !ok {
99 return idx.mkErr(obj, "instance is not a struct")
100 }
101
102 v := newVisitor(idx, nil, nil, obj)
103 return eval(idx, v.walk(expr))
104}
105
106func (inst *Instance) evalExpr(ctx *context, expr ast.Expr) evaluated {
107 root := inst.eval(ctx)
108 if isBottom(root) {
109 return ctx.mkErr(root, "error evaluating instance")
110 }
111 obj, ok := root.(*structLit)
112 if !ok {
113 return ctx.mkErr(obj, "instance is not a struct")
114 }
115 v := newVisitor(ctx.index, inst.inst, nil, obj)
116 return v.walk(expr).evalPartial(ctx)
117}
118
119// Value returns the root value of the configuration. If the configuration
120// defines in emit value, it will be that value. Otherwise it will be all
121// top-level values.
122func (inst *Instance) Value() Value {
123 ctx := inst.newContext()
124 return newValueRoot(ctx, inst.eval(ctx))
125}
126
127// Eval evaluates an expression within an existing instance.
128//
129// Expressions may refer to builtin packages if they can be uniquely identified.
130func (inst *Instance) Eval(expr ast.Expr) Value {
131 ctx := inst.newContext()
132 result := inst.evalExpr(ctx, expr)
133 return newValueRoot(ctx, result)
134}
135
136// Merge unifies the given instances into a single one.
137//
138// Errors regarding conflicts are included in the result, but not reported, so
139// that these will only surface during manifestation. This allows
Marcel van Lohuizenf23fbff2018-12-20 12:23:16 +0100140// non-conflicting parts to be used.
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100141func Merge(inst ...*Instance) *Instance {
142 switch len(inst) {
143 case 0:
144 return nil
145 case 1:
146 return inst[0]
147 }
148
Marcel van Lohuizen6c58f252019-04-24 23:08:16 +0200149 values := []value{}
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100150 for _, i := range inst {
151 if i.Err != nil {
152 return i
153 }
Marcel van Lohuizen6c58f252019-04-24 23:08:16 +0200154 values = append(values, i.rootValue)
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100155 }
Marcel van Lohuizen6c58f252019-04-24 23:08:16 +0200156 merged := &mergedValues{values: values}
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100157
158 ctx := inst[0].newContext()
159
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100160 st, ok := ctx.manifest(merged).(*structLit)
161 if !ok {
162 return nil
163 }
164
165 p := &Instance{
166 rootStruct: st,
167 rootValue: merged,
168 index: ctx.index,
169 complete: true,
170 }
171 return p
172}
173
174// Build creates a new instance from the build instances, allowing unbound
175// identifier to bind to the top-level field in inst. The top-level fields in
176// inst take precedence over predeclared identifier and builtin functions.
177func (inst *Instance) Build(p *build.Instance) *Instance {
178 p.Complete()
179
180 idx := inst.index
181
182 i := idx.NewInstance(p)
183 if i.Err != nil {
184 return i
185 }
186
187 ctx := inst.newContext()
188 v, err := newValueRoot(ctx, inst.rootValue).structVal(ctx)
189 if err != nil {
190 i.setError(err)
191 return i
192 }
193 i.scope = v.n
194
195 i.Err = resolveFiles(idx, p)
196 for _, f := range p.Files {
197 i.insertFile(f)
198 }
199 i.complete = true
200
201 return i
202}
203
204// Lookup reports the value starting from the top level struct (not the emitted
205// value), or an error if the path is not found.
206// The empty path returns the top-level configuration struct, regardless of
207// whether an emit value was specified.
208func (inst *Instance) Lookup(path ...string) Value {
209 idx := inst.index
210 ctx := idx.newContext()
211 v := newValueRoot(ctx, inst.rootValue)
212 for _, k := range path {
213 obj, err := v.structVal(ctx)
214 if err != nil {
215 v := err.(*bottom)
Marcel van Lohuizen9bf93c02019-03-26 21:40:25 +0100216 return Value{idx, &valueData{arc: arc{cache: v, v: v}}}
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100217 }
218 v = obj.Lookup(k)
219 }
220 return v
221}
222
223// Fill creates a new instance with the values of the old instance unified with
224// the given value. It is not possible to update the emit value.
225func (inst *Instance) Fill(x interface{}, path ...string) (*Instance, error) {
226 ctx := inst.newContext()
227 root := ctx.manifest(inst.rootValue)
228 for i := len(path) - 1; i >= 0; i-- {
229 x = map[string]interface{}{path[i]: x}
230 }
231 value := convert(ctx, root, x)
232 eval := binOp(ctx, baseValue{}, opUnify, root, value)
233 // TODO: validate recursively?
Marcel van Lohuizen66db9202018-12-17 19:02:08 +0100234 st, ok := eval.(*structLit)
235 if !ok {
236 fmt.Errorf("structure at path did not resolve in struct")
237 }
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100238 inst = &Instance{
239 rootStruct: st,
240 rootValue: st,
241 index: inst.index,
242 inst: nil,
243
244 ImportPath: inst.ImportPath,
245 Name: inst.Name,
246 Incomplete: inst.Incomplete,
247 Err: inst.Err,
248
249 complete: true,
250 }
251 return inst, nil
252}