cuelang.org/go@v0.13.0/internal/core/export/toposort.go (about) 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 15 package export 16 17 import ( 18 "cmp" 19 "slices" 20 21 "cuelang.org/go/internal/core/adt" 22 "cuelang.org/go/internal/core/toposort" 23 ) 24 25 // TODO: topological sort should go arguably in a more fundamental place as it 26 // may be needed to sort inputs for comprehensions. 27 28 // VertexFeatures returns the feature list of v. The list may include more 29 // features than for which there are arcs and also includes features for 30 // optional fields. It assumes the Structs fields are initialized and evaluated. 31 func VertexFeatures(c *adt.OpContext, v *adt.Vertex) []adt.Feature { 32 if c.TopoSort { 33 return toposort.VertexFeatures(c, v) 34 } else { 35 return vertexFeatures(v) 36 } 37 } 38 39 func vertexFeatures(v *adt.Vertex) []adt.Feature { 40 sets := extractFeatures(v.Structs) 41 m := sortArcs(sets) // TODO: use for convenience. 42 43 // Add features that are not in m. This may happen when fields were 44 // dynamically created. 45 var a []adt.Feature 46 for _, arc := range v.Arcs { 47 if _, ok := m[arc.Label]; !ok { 48 a = append(a, arc.Label) 49 } 50 } 51 52 sets = extractFeatures(v.Structs) 53 if len(a) > 0 { 54 sets = append(sets, a) 55 } 56 57 return sortedArcs(sets) 58 } 59 60 func extractFeatures(in []*adt.StructInfo) (a [][]adt.Feature) { 61 a = make([][]adt.Feature, 0, len(in)) 62 for _, s := range in { 63 sorted := make([]adt.Feature, 0, len(s.Decls)) 64 for _, e := range s.Decls { 65 switch x := e.(type) { 66 case *adt.Field: 67 sorted = append(sorted, x.Label) 68 } 69 } 70 71 // Lists with a single element may still be useful to distinguish 72 // between known and unknown fields: unknown fields are sorted last. 73 if len(sorted) > 0 { 74 a = append(a, sorted) 75 } 76 } 77 return a 78 } 79 80 // VertexFeaturesUnsorted returns the feature list of v. There will be 81 // no duplicate features in the returned list, but there is also no 82 // attempt made to sort the list. 83 func VertexFeaturesUnsorted(v *adt.Vertex) (features []adt.Feature) { 84 seen := make(map[adt.Feature]struct{}) 85 86 for _, s := range v.Structs { 87 for _, decl := range s.Decls { 88 field, ok := decl.(*adt.Field) 89 if !ok { 90 continue 91 } 92 label := field.Label 93 if _, found := seen[label]; found { 94 continue 95 } 96 seen[label] = struct{}{} 97 features = append(features, label) 98 } 99 } 100 101 for _, arc := range v.Arcs { 102 label := arc.Label 103 if _, found := seen[label]; found { 104 continue 105 } 106 seen[label] = struct{}{} 107 features = append(features, label) 108 } 109 110 return features 111 } 112 113 // sortedArcs is like sortArcs, but returns the features of optional and 114 // required fields in an sorted slice. Ultimately, the implementation should 115 // use merge sort everywhere, and this will be the preferred method. Also, 116 // when querying optional fields as well, this helps identifying the optional 117 // fields. 118 func sortedArcs(fronts [][]adt.Feature) []adt.Feature { 119 m := sortArcs(fronts) 120 return sortedArcsFromMap(m) 121 } 122 123 func sortedArcsFromMap(m map[adt.Feature]int) []adt.Feature { 124 a := make([]adt.Feature, 0, len(m)) 125 126 for k := range m { 127 a = append(a, k) 128 } 129 130 slices.SortFunc(a, func(a1, a2 adt.Feature) int { return -cmp.Compare(m[a1], m[a2]) }) 131 132 return a 133 } 134 135 // sortArcs does a topological sort of arcs based on a variant of Kahn's 136 // algorithm. See 137 // https://www.geeksforgeeks.org/topological-sorting-indegree-based-solution/ 138 // 139 // It returns a map from feature to int where the feature with the highest 140 // number should be sorted first. 141 func sortArcs(fronts [][]adt.Feature) map[adt.Feature]int { 142 counts := map[adt.Feature]int{} 143 for _, a := range fronts { 144 if len(a) <= 1 { 145 continue // no dependencies 146 } 147 for _, f := range a[1:] { 148 counts[f]++ 149 } 150 } 151 152 // We could use a Heap instead of simple linear search here if we are 153 // concerned about the time complexity. 154 155 index := -1 156 outer: 157 for { 158 lists: 159 for i, a := range fronts { 160 for len(a) > 0 { 161 f := a[0] 162 n := counts[f] 163 if n > 0 { 164 continue lists 165 } 166 167 // advance list and decrease dependency. 168 a = a[1:] 169 fronts[i] = a 170 if len(a) > 1 && counts[a[0]] > 0 { 171 counts[a[0]]-- 172 } 173 174 if n == 0 { // may be head of other lists as well 175 counts[f] = index 176 index-- 177 } 178 continue outer // progress 179 } 180 } 181 182 for _, a := range fronts { 183 if len(a) > 0 { 184 // Detected a cycle. Fire at will to make progress. 185 counts[a[0]] = 0 186 continue outer 187 } 188 } 189 break 190 } 191 192 return counts 193 }