blob: 4d7935a18895ee867dc0eeafa0606e7564993815 [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 export
import (
"sort"
"cuelang.org/go/internal/core/adt"
)
// TODO: topological sort should go arguably in a more fundamental place as it
// may be needed to sort inputs for comprehensions.
// VertexFeatures returns the feature list of v. The list may include more
// features than for which there are arcs and also includes features for
// optional fields. It assumes the Structs fields is properly initialized.
func VertexFeatures(v *adt.Vertex) []adt.Feature {
sets := extractFeatures(v.Structs)
m := sortArcs(sets) // TODO: use for convenience.
// Add features that are not in m. This may happen when fields were
// dynamically created.
var a []adt.Feature
for _, arc := range v.Arcs {
if _, ok := m[arc.Label]; !ok {
a = append(a, arc.Label)
}
}
sets = extractFeatures(v.Structs)
if len(a) > 0 {
sets = append(sets, a)
}
return sortedArcs(sets)
}
// func structFeatures(a []*adt.StructLit) []adt.Feature {
// sets := extractFeatures(a)
// return sortedArcs(sets)
// }
func (e *exporter) sortedArcs(v *adt.Vertex) (sorted []*adt.Vertex) {
a := extractFeatures(v.Structs)
if len(a) == 0 {
return v.Arcs
}
sorted = make([]*adt.Vertex, len(v.Arcs))
copy(sorted, v.Arcs)
m := sortArcs(a)
sort.SliceStable(sorted, func(i, j int) bool {
if m[sorted[i].Label] == 0 {
return m[sorted[j].Label] != 0
}
return m[sorted[i].Label] > m[sorted[j].Label]
})
return sorted
}
// TODO: remove
func (e *exporter) extractFeatures(in []*adt.StructInfo) (a [][]adt.Feature) {
return extractFeatures(in)
}
func extractFeatures(in []*adt.StructInfo) (a [][]adt.Feature) {
for _, s := range in {
sorted := []adt.Feature{}
for _, e := range s.StructLit.Decls {
switch x := e.(type) {
case *adt.Field:
sorted = append(sorted, x.Label)
case *adt.OptionalField:
sorted = append(sorted, x.Label)
}
}
// Lists with a single element may still be useful to distinguish
// between known and unknown fields: unknown fields are sorted last.
if len(sorted) > 0 {
a = append(a, sorted)
}
}
return a
}
// sortedArcs is like sortArcs, but returns a the features of optional and
// required fields in an sorted slice. Ultimately, the implementation should
// use merge sort everywhere, and this will be the preferred method. Also,
// when querying optional fields as well, this helps identifying the optional
// fields.
func sortedArcs(fronts [][]adt.Feature) []adt.Feature {
m := sortArcs(fronts)
return sortedArcsFromMap(m)
}
func sortedArcsFromMap(m map[adt.Feature]int) []adt.Feature {
a := make([]adt.Feature, 0, len(m))
for k := range m {
a = append(a, k)
}
sort.Slice(a, func(i, j int) bool { return m[a[i]] > m[a[j]] })
return a
}
// sortArcs does a topological sort of arcs based on a variant of Kahn's
// algorithm. See
// https://www.geeksforgeeks.org/topological-sorting-indegree-based-solution/
//
// It returns a map from feature to int where the feature with the highest
// number should be sorted first.
func sortArcs(fronts [][]adt.Feature) map[adt.Feature]int {
counts := map[adt.Feature]int{}
for _, a := range fronts {
if len(a) <= 1 {
continue // no dependencies
}
for _, f := range a[1:] {
counts[f]++
}
}
// We could use a Heap instead of simple linear search here if we are
// concerned about the time complexity.
index := -1
outer:
for {
lists:
for i, a := range fronts {
for len(a) > 0 {
f := a[0]
n := counts[f]
if n > 0 {
continue lists
}
// advance list and decrease dependency.
a = a[1:]
fronts[i] = a
if len(a) > 1 && counts[a[0]] > 0 {
counts[a[0]]--
}
if n == 0 { // may be head of other lists as well
counts[f] = index
index--
}
continue outer // progress
}
}
for _, a := range fronts {
if len(a) > 0 {
// Detected a cycle. Fire at will to make progress.
counts[a[0]] = 0
continue outer
}
}
break
}
return counts
}