-
Notifications
You must be signed in to change notification settings - Fork 0
/
node_path.go
131 lines (115 loc) · 2.87 KB
/
node_path.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package complete
import (
"reflect"
"github.com/markusbkk/elvish/pkg/parse"
)
type nodePath []parse.Node
// Returns the path of Node's from n to a leaf at position p. Leaf first in the
// returned slice.
func findNodePath(root parse.Node, p int) nodePath {
n := root
descend:
for len(parse.Children(n)) > 0 {
for _, ch := range parse.Children(n) {
if rg := ch.Range(); rg.From <= p && p <= rg.To {
n = ch
continue descend
}
}
return nil
}
var path []parse.Node
for {
path = append(path, n)
if n == root {
break
}
n = parse.Parent(n)
}
return path
}
func (ns nodePath) match(ms ...nodesMatcher) bool {
for _, m := range ms {
ns2, ok := m.matchNodes(ns)
if !ok {
return false
}
ns = ns2
}
return true
}
type nodesMatcher interface {
// Matches from the beginning of nodes. Returns the remaining nodes and
// whether the match succeeded.
matchNodes([]parse.Node) ([]parse.Node, bool)
}
// Matches one node of a given type, without storing it.
//
// TODO: Avoid reflection with generics when Elvish requires Go 1.18.
type typedMatcher struct{ typ reflect.Type }
func typed(n parse.Node) nodesMatcher { return typedMatcher{reflect.TypeOf(n)} }
func (m typedMatcher) matchNodes(ns []parse.Node) ([]parse.Node, bool) {
if len(ns) > 0 && reflect.TypeOf(ns[0]) == m.typ {
return ns[1:], true
}
return nil, false
}
var (
aChunk = typed(&parse.Chunk{})
aPipeline = typed(&parse.Pipeline{})
aArray = typed(&parse.Array{})
aRedir = typed(&parse.Redir{})
aSep = typed(&parse.Sep{})
)
// Matches one node of a certain type, and stores it into a pointer.
//
// TODO: Avoid reflection with generics when Elvish requires Go 1.18.
type storeMatcher struct {
p reflect.Value
typ reflect.Type
}
func store(p interface{}) nodesMatcher {
dst := reflect.ValueOf(p).Elem()
return storeMatcher{dst, dst.Type()}
}
func (m storeMatcher) matchNodes(ns []parse.Node) ([]parse.Node, bool) {
if len(ns) > 0 && reflect.TypeOf(ns[0]) == m.typ {
m.p.Set(reflect.ValueOf(ns[0]))
return ns[1:], true
}
return nil, false
}
// Matches an expression that can be evaluated statically. Consumes 3 nodes
// (Primary, Indexing and Compound).
type simpleExprMatcher struct {
ev PureEvaler
s string
compound *parse.Compound
quote parse.PrimaryType
}
func simpleExpr(ev PureEvaler) *simpleExprMatcher {
return &simpleExprMatcher{ev: ev}
}
func (m *simpleExprMatcher) matchNodes(ns []parse.Node) ([]parse.Node, bool) {
if len(ns) < 3 {
return nil, false
}
primary, ok := ns[0].(*parse.Primary)
if !ok {
return nil, false
}
indexing, ok := ns[1].(*parse.Indexing)
if !ok {
return nil, false
}
compound, ok := ns[2].(*parse.Compound)
if !ok {
return nil, false
}
s, ok := m.ev.PurelyEvalPartialCompound(compound, indexing.To)
if !ok {
return nil, false
}
m.compound, m.quote, m.s = compound, primary.Type, s
return ns[3:], true
}