// 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 // 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 *StructInfo) MatchAndInsert(c *OpContext, arc *Vertex) { env := o.Env closeInfo := o.CloseInfo closeInfo.IsClosed = false // Match normal fields matched := false // TODO: this could be lookup up more efficiently in the outer Vertex now. // Keep this logic for now, though. for _, f := range o.Fields { if f.Label == arc.Label { matched = true break } } f := arc.Label if !f.IsRegular() { return } var label Value if int64(f.Index()) == MaxIndex { f = 0 } else if o.types&HasComplexPattern != 0 && f.IsString() { label = f.ToValue(c) } if len(o.Bulk) > 0 { bulkEnv := *env bulkEnv.DynamicLabel = f // match bulk optional fields / pattern properties for _, b := range o.Bulk { // if matched && f.additional { // continue // } // Mark the current arc as cyclic while evaluating pattern // expressions, but not while adding conjuncts. // TODO: make MatchAndInsert return a list of conjuncts instead? // TODO: it could be that we can set the cycle before calling // MatchAndInsert after the renewed implementation of disjunctions. saved := arc.BaseValue arc.BaseValue = cycle match := matchBulk(c, env, b, f, label) arc.BaseValue = saved if match { matched = true info := closeInfo.SpawnSpan(b.Value, ConstraintSpan) arc.AddConjunct(MakeConjunct(&bulkEnv, b, info)) } } } if matched || len(o.Additional) == 0 { return } // match others for _, x := range o.Additional { info := closeInfo if _, ok := x.expr().(*Top); !ok { info = info.SpawnSpan(x, ConstraintSpan) } // TODO: consider moving in above block (2 lines up). arc.AddConjunct(MakeConjunct(env, x, info)) } } // matchBulk reports whether feature f matches the filter of x. It evaluation of // the filter is erroneous, it returns false and the error will be set in c. func matchBulk(c *OpContext, env *Environment, p *BulkOptionalField, f Feature, label Value) bool { v := env.evalCached(c, p.Filter) v = Unwrap(v) // Fast-track certain cases. switch x := v.(type) { case *Bottom: if x == cycle { err := c.NewPosf(pos(p.Filter), "cyclic pattern constraint") for _, c := range c.vertex.Conjuncts { err.AddPosition(c.Elem()) } c.AddBottom(&Bottom{ Err: err, }) } if c.errs == nil { c.AddBottom(x) } return false case *Top: return true case *BasicType: return x.K&StringKind != 0 case *BoundValue: switch x.Kind() { case StringKind: if label == nil { return false } str := label.(*String).Str return x.validateStr(c, str) case IntKind: return x.validateInt(c, int64(f.Index())) } } if label == nil { return false } n := Vertex{ IsDynamic: true, } m := MakeConjunct(env, v, c.ci) n.AddConjunct(m) n.AddConjunct(MakeConjunct(m.Env, label, c.ci)) c.inConstraint++ n.Finalize(c) c.inConstraint-- b, _ := n.BaseValue.(*Bottom) return b == nil }