1// Copyright 2020 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 adt
16
17// MatchAndInsert finds matching optional parts for a given Arc and adds its
18// conjuncts. Bulk fields are only applied if no fields match, and additional
19// constraints are only added if neither regular nor bulk fields match.
20func (o *StructInfo) MatchAndInsert(c *OpContext, arc *Vertex) {
21	env := o.Env
22
23	closeInfo := o.CloseInfo
24	closeInfo.IsClosed = false
25
26	// Match normal fields
27	matched := false
28outer:
29	for _, f := range o.Fields {
30		if f.Label == arc.Label {
31			for _, e := range f.Optional {
32				arc.AddConjunct(MakeConjunct(env, e, closeInfo))
33			}
34			matched = true
35			break outer
36		}
37	}
38
39	f := arc.Label
40	if !f.IsRegular() {
41		return
42	}
43	if int64(f.Index()) == MaxIndex {
44		f = 0
45	}
46
47	var label Value
48	if o.types&HasComplexPattern != 0 && f.IsString() {
49		label = f.ToValue(c)
50	}
51
52	if len(o.Bulk) > 0 {
53		bulkEnv := *env
54		bulkEnv.DynamicLabel = f
55		bulkEnv.Deref = nil
56		bulkEnv.Cycles = nil
57
58		// match bulk optional fields / pattern properties
59		for _, b := range o.Bulk {
60			// if matched && f.additional {
61			// 	continue
62			// }
63			if matchBulk(c, env, b, f, label) {
64				matched = true
65				info := closeInfo.SpawnSpan(b.Value, ConstraintSpan)
66				arc.AddConjunct(MakeConjunct(&bulkEnv, b, info))
67			}
68		}
69	}
70
71	if matched || len(o.Additional) == 0 {
72		return
73	}
74
75	addEnv := *env
76	addEnv.Deref = nil
77	addEnv.Cycles = nil
78
79	// match others
80	for _, x := range o.Additional {
81		info := closeInfo
82		if _, ok := x.(*Top); !ok {
83			info = info.SpawnSpan(x, ConstraintSpan)
84		}
85		arc.AddConjunct(MakeConjunct(&addEnv, x, info))
86	}
87}
88
89// matchBulk reports whether feature f matches the filter of x. It evaluation of
90// the filter is erroneous, it returns false and the error will  be set in c.
91func matchBulk(c *OpContext, env *Environment, x *BulkOptionalField, f Feature, label Value) bool {
92	v := env.evalCached(c, x.Filter)
93
94	// Fast-track certain cases.
95	switch x := v.(type) {
96	case *Bottom:
97		if c.errs == nil {
98			c.AddBottom(x)
99		}
100		return false
101	case *Top:
102		return true
103
104	case *BasicType:
105		return x.K&StringKind != 0
106
107	case *BoundValue:
108		switch x.Kind() {
109		case StringKind:
110			if label == nil {
111				return false
112			}
113			str := label.(*String).Str
114			return x.validateStr(c, str)
115
116		case IntKind:
117			return x.validateInt(c, int64(f.Index()))
118		}
119	}
120
121	if label == nil {
122		return false
123	}
124
125	n := Vertex{}
126	m := MakeRootConjunct(env, v)
127	n.AddConjunct(m)
128	n.AddConjunct(MakeRootConjunct(m.Env, label))
129
130	c.inConstraint++
131	n.Finalize(c)
132	c.inConstraint--
133
134	b, _ := n.BaseValue.(*Bottom)
135	return b == nil
136}
137