blob: bd5f99b9abbcf720fa30ead19a87f95689bb283c [file] [log] [blame]
// Copyright 2020 CUE Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package adt
import (
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/token"
)
// An Environment links the parent scopes for identifier lookup to a composite
// node. Each conjunct that make up node in the tree can be associated with
// a different environment (although some conjuncts may share an Environment).
type Environment struct {
Up *Environment
Vertex *Vertex
}
// A Vertex is a node in the value tree. It may be a leaf or internal node.
// It may have arcs to represent elements of a fully evaluated struct or list.
//
// For structs, it only contains definitions and concrete fields.
// optional fields are dropped.
//
// It maintains source information such as a list of conjuncts that contributed
// to the value.
type Vertex struct {
Parent *Vertex // Do we need this?
// Label is the feature leading to this vertex.
Label Feature
// Value is the value associated with this vertex. For lists and structs
// this is a sentinel value indicating its kind.
Value Value
// The parent of nodes can be followed to determine the path within the
// configuration of this node.
// Value Value
Arcs []*Vertex // arcs are sorted in display order.
// Conjuncts lists the structs that ultimately formed this Composite value.
// This includes all selected disjuncts. This information is used to compute
// the topological sort of arcs.
Conjuncts []Conjunct
// Structs is a slice of struct literals that contributed to this value.
Structs []*StructLit
}
func (v *Vertex) Kind() Kind {
// This is possible when evaluating comprehensions. It is potentially
// not known at this time what the type is.
if v.Value == nil {
return TopKind
}
return v.Value.Kind()
}
func (v *Vertex) IsList() bool {
_, ok := v.Value.(*ListMarker)
return ok
}
// lookup returns the Arc with label f if it exists or nil otherwise.
func (v *Vertex) lookup(f Feature) *Vertex {
for _, a := range v.Arcs {
if a.Label == f {
return a
}
}
return nil
}
// GetArc returns a Vertex for the outgoing arc with label f. It creates and
// ads one if it doesn't yet exist.
func (v *Vertex) GetArc(f Feature) (arc *Vertex, isNew bool) {
arc = v.lookup(f)
if arc == nil {
arc = &Vertex{Parent: v, Label: f}
v.Arcs = append(v.Arcs, arc)
isNew = true
}
return arc, isNew
}
func (v *Vertex) Source() ast.Node { return nil }
// AddConjunct adds the given Conjuncts to v if it doesn't already exist.
func (v *Vertex) AddConjunct(c Conjunct) *Bottom {
for _, x := range v.Conjuncts {
if x == c {
return nil
}
}
if v.Value != nil {
// This is likely a bug in the evaluator and should not happen.
return &Bottom{Err: errors.Newf(token.NoPos, "cannot add conjunct")}
}
v.Conjuncts = append(v.Conjuncts, c)
return nil
}
// Path computes the sequence of Features leading from the root to of the
// instance to this Vertex.
func (v *Vertex) Path() []Feature {
return appendPath(nil, v)
}
func appendPath(a []Feature, v *Vertex) []Feature {
if v.Parent == nil {
return a
}
a = appendPath(a, v.Parent)
return append(a, v.Label)
}
// An Conjunct is an Environment-Expr pair. The Environment is the starting
// point for reference lookup for any reference contained in X.
type Conjunct struct {
Env *Environment
x Node
}
// TODO(perf): replace with composite literal if this helps performance.
// MakeConjunct creates a conjunct from the given environment and node.
// It panics if x cannot be used as an expression.
func MakeConjunct(env *Environment, x Node) Conjunct {
switch x.(type) {
case Expr, interface{ expr() Expr }:
default:
panic("invalid Node type")
}
return Conjunct{env, x}
}
func (c *Conjunct) Source() ast.Node {
return c.x.Source()
}
func (c *Conjunct) Expr() Expr {
switch x := c.x.(type) {
case Expr:
return x
case interface{ expr() Expr }:
return x.expr()
default:
panic("unreachable")
}
}