blob: 6b17a8d9e41595fcacb195b78eddbebc9dd30726 [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 eval
// TODO: rename this file to fieldset.go
import "cuelang.org/go/internal/core/adt"
// fieldSet represents the fields for a single struct literal, along
// the constraints of fields that may be added.
type fieldSet struct {
// TODO: look at consecutive identical environments to figure out
// what belongs to same definition?
env *adt.Environment
// field marks the optional conjuncts of all explicit fields.
// Required fields are marked as empty
fields []field
// literal map[adt.Feature][]adt.Node
// excluded are all literal fields that already exist.
bulk []bulkField
additional []adt.Expr
isOpen bool // has a ...
}
func (o *fieldSet) OptionalTypes() (mask adt.OptionalType) {
for _, f := range o.fields {
if len(f.optional) > 0 {
mask = adt.HasField
break
}
}
for _, b := range o.bulk {
if b.expr == nil {
mask |= adt.HasDynamic
} else {
mask |= adt.HasPattern
}
}
if o.additional != nil {
mask |= adt.HasAdditional
}
if o.isOpen {
mask |= adt.IsOpen
}
return mask
}
type field struct {
label adt.Feature
optional []adt.Node
}
type bulkField struct {
check fieldMatcher
expr adt.Node // *adt.BulkOptionalField // Conjunct
}
func (o *fieldSet) Accept(c *adt.OpContext, f adt.Feature) bool {
if len(o.additional) > 0 {
return true
}
if o.fieldIndex(f) >= 0 {
return true
}
for _, b := range o.bulk {
if b.check.Match(c, f) {
return true
}
}
return false
}
// MatchAndInsert finds matching optional parts for a given Arc and adds its
// conjuncts. Bulk fields are only applied if no fields match, and additional
// constraints are only added if neither regular nor bulk fields match.
func (o *fieldSet) MatchAndInsert(c *adt.OpContext, arc *adt.Vertex) {
env := o.env
// Match normal fields
p := 0
for ; p < len(o.fields); p++ {
if o.fields[p].label == arc.Label {
break
}
}
if p < len(o.fields) {
for _, e := range o.fields[p].optional {
arc.AddConjunct(adt.MakeConjunct(env, e))
}
return
}
if !arc.Label.IsRegular() {
return
}
bulkEnv := *env
bulkEnv.DynamicLabel = arc.Label
// match bulk optional fields / pattern properties
matched := false
for _, f := range o.bulk {
if f.check.Match(c, arc.Label) {
matched = true
if f.expr != nil {
arc.AddConjunct(adt.MakeConjunct(&bulkEnv, f.expr))
}
}
}
if matched {
return
}
// match others
for _, x := range o.additional {
arc.AddConjunct(adt.MakeConjunct(env, x))
}
}
func (o *fieldSet) fieldIndex(f adt.Feature) int {
for i := range o.fields {
if o.fields[i].label == f {
return i
}
}
return -1
}
func (o *fieldSet) MarkField(c *adt.OpContext, x *adt.Field) {
if o.fieldIndex(x.Label) < 0 {
o.fields = append(o.fields, field{label: x.Label})
}
}
func (o *fieldSet) AddOptional(c *adt.OpContext, x *adt.OptionalField) {
p := o.fieldIndex(x.Label)
if p < 0 {
p = len(o.fields)
o.fields = append(o.fields, field{label: x.Label})
}
o.fields[p].optional = append(o.fields[p].optional, x)
}
func (o *fieldSet) AddDynamic(c *adt.OpContext, env *adt.Environment, x *adt.DynamicField) {
// not in bulk: count as regular field?
o.bulk = append(o.bulk, bulkField{dynamicMatcher{env, x.Key}, nil})
}
func (o *fieldSet) AddBulk(c *adt.OpContext, x *adt.BulkOptionalField) {
v, ok := c.Evaluate(o.env, x.Filter)
if !ok {
// TODO: handle dynamically
return
}
switch f := v.(type) {
case *adt.Num:
// Just assert an error. Lists have not been expanded yet at
// this point, so there is no need to check for existing
//fields.
l, err := adt.MakeLabel(x.Src, c.Int64(f), adt.IntLabel)
if err != nil {
c.AddErr(err)
return
}
o.bulk = append(o.bulk, bulkField{labelMatcher(l), x})
case *adt.Top:
o.bulk = append(o.bulk, bulkField{typeMatcher(adt.TopKind), x})
case *adt.BasicType:
o.bulk = append(o.bulk, bulkField{typeMatcher(f.K), x})
case *adt.String:
l := c.Label(f)
o.bulk = append(o.bulk, bulkField{labelMatcher(l), x})
case adt.Validator:
o.bulk = append(o.bulk, bulkField{validateMatcher{f}, x})
default:
// TODO(err): not allowed type
}
}
func (o *fieldSet) AddEllipsis(c *adt.OpContext, x *adt.Ellipsis) {
expr := x.Value
if x.Value == nil {
o.isOpen = true
expr = &adt.Top{}
}
o.additional = append(o.additional, expr)
}
type fieldMatcher interface {
Match(c *adt.OpContext, f adt.Feature) bool
}
type labelMatcher adt.Feature
func (m labelMatcher) Match(c *adt.OpContext, f adt.Feature) bool {
return adt.Feature(m) == f
}
type typeMatcher adt.Kind
func (m typeMatcher) Match(c *adt.OpContext, f adt.Feature) bool {
switch f.Typ() {
case adt.StringLabel:
return adt.Kind(m)&adt.StringKind != 0
case adt.IntLabel:
return adt.Kind(m)&adt.IntKind != 0
}
return false
}
type validateMatcher struct {
adt.Validator
}
func (m validateMatcher) Match(c *adt.OpContext, f adt.Feature) bool {
v := f.ToValue(c)
return c.Validate(m.Validator, v) == nil
}
type dynamicMatcher struct {
env *adt.Environment
expr adt.Expr
}
func (m dynamicMatcher) Match(c *adt.OpContext, f adt.Feature) bool {
if !f.IsRegular() || !f.IsString() {
return false
}
v, ok := c.Evaluate(m.env, m.expr)
if !ok {
return false
}
s, ok := v.(*adt.String)
if !ok {
return false
}
return f.SelectorString(c) == s.Str
}