mirror of https://github.com/gobwas/glob.git
166 lines
3.7 KiB
Go
166 lines
3.7 KiB
Go
package ast
|
||
|
||
import (
|
||
"reflect"
|
||
)
|
||
|
||
// Minimize tries to apply some heuristics to minimize number of nodes in given
|
||
// t
|
||
func Minimize(t *Node) *Node {
|
||
switch t.Kind {
|
||
case KindAnyOf:
|
||
return minimizeAnyOf(t)
|
||
default:
|
||
return nil
|
||
}
|
||
}
|
||
|
||
// minimizeAnyOf tries to find common children of given node of AnyOf pattern
|
||
// it searches for common children from left and from right
|
||
// if any common children are found – then it returns new optimized ast t
|
||
// else it returns nil
|
||
func minimizeAnyOf(t *Node) *Node {
|
||
if !SameKind(t.Children, KindPattern) {
|
||
return nil
|
||
}
|
||
|
||
commonLeft, commonRight := CommonChildren(t.Children)
|
||
commonLeftCount, commonRightCount := len(commonLeft), len(commonRight)
|
||
if commonLeftCount == 0 && commonRightCount == 0 { // there are no common parts
|
||
return nil
|
||
}
|
||
|
||
var result []*Node
|
||
if commonLeftCount > 0 {
|
||
result = append(result, NewNode(KindPattern, nil, commonLeft...))
|
||
}
|
||
|
||
var anyOf []*Node
|
||
for _, child := range t.Children {
|
||
reuse := child.Children[commonLeftCount : len(child.Children)-commonRightCount]
|
||
var node *Node
|
||
if len(reuse) == 0 {
|
||
// this pattern is completely reduced by commonLeft and commonRight patterns
|
||
// so it become nothing
|
||
node = NewNode(KindNothing, nil)
|
||
} else {
|
||
node = NewNode(KindPattern, nil, reuse...)
|
||
}
|
||
anyOf = AppendUnique(anyOf, node)
|
||
}
|
||
switch {
|
||
case len(anyOf) == 1 && anyOf[0].Kind != KindNothing:
|
||
result = append(result, anyOf[0])
|
||
case len(anyOf) > 1:
|
||
result = append(result, NewNode(KindAnyOf, nil, anyOf...))
|
||
}
|
||
|
||
if commonRightCount > 0 {
|
||
result = append(result, NewNode(KindPattern, nil, commonRight...))
|
||
}
|
||
|
||
return NewNode(KindPattern, nil, result...)
|
||
}
|
||
|
||
func CommonChildren(nodes []*Node) (commonLeft, commonRight []*Node) {
|
||
if len(nodes) <= 1 {
|
||
return
|
||
}
|
||
|
||
// find node that has least number of children
|
||
idx := OneWithLeastChildren(nodes)
|
||
if idx == -1 {
|
||
return
|
||
}
|
||
tree := nodes[idx]
|
||
treeLength := len(tree.Children)
|
||
|
||
// allocate max able size for rightCommon slice
|
||
// to get ability insert elements in reverse order (from end to start)
|
||
// without sorting
|
||
commonRight = make([]*Node, treeLength)
|
||
lastRight := treeLength // will use this to get results as commonRight[lastRight:]
|
||
|
||
var (
|
||
breakLeft bool
|
||
breakRight bool
|
||
commonTotal int
|
||
)
|
||
for i, j := 0, treeLength-1; commonTotal < treeLength && j >= 0 && !(breakLeft && breakRight); i, j = i+1, j-1 {
|
||
treeLeft := tree.Children[i]
|
||
treeRight := tree.Children[j]
|
||
|
||
for k := 0; k < len(nodes) && !(breakLeft && breakRight); k++ {
|
||
// skip least children node
|
||
if k == idx {
|
||
continue
|
||
}
|
||
|
||
restLeft := nodes[k].Children[i]
|
||
restRight := nodes[k].Children[j+len(nodes[k].Children)-treeLength]
|
||
|
||
breakLeft = breakLeft || !treeLeft.Equal(restLeft)
|
||
|
||
// disable searching for right common parts, if left part is already overlapping
|
||
breakRight = breakRight || (!breakLeft && j <= i)
|
||
breakRight = breakRight || !treeRight.Equal(restRight)
|
||
}
|
||
|
||
if !breakLeft {
|
||
commonTotal++
|
||
commonLeft = append(commonLeft, treeLeft)
|
||
}
|
||
if !breakRight {
|
||
commonTotal++
|
||
lastRight = j
|
||
commonRight[j] = treeRight
|
||
}
|
||
}
|
||
|
||
commonRight = commonRight[lastRight:]
|
||
|
||
return
|
||
}
|
||
|
||
func AppendUnique(target []*Node, val *Node) []*Node {
|
||
for _, n := range target {
|
||
if reflect.DeepEqual(n, val) {
|
||
return target
|
||
}
|
||
}
|
||
return append(target, val)
|
||
}
|
||
|
||
func SameKind(nodes []*Node, kind Kind) bool {
|
||
for _, n := range nodes {
|
||
if n.Kind != kind {
|
||
return false
|
||
}
|
||
}
|
||
return true
|
||
}
|
||
|
||
func OneWithLeastChildren(nodes []*Node) int {
|
||
min := -1
|
||
idx := -1
|
||
for i, n := range nodes {
|
||
if idx == -1 || (len(n.Children) < min) {
|
||
min = len(n.Children)
|
||
idx = i
|
||
}
|
||
}
|
||
return idx
|
||
}
|
||
|
||
func Equal(a, b []*Node) bool {
|
||
if len(a) != len(b) {
|
||
return false
|
||
}
|
||
for i, av := range a {
|
||
if !av.Equal(b[i]) {
|
||
return false
|
||
}
|
||
}
|
||
return true
|
||
}
|