src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/parse/np/np.go (about) 1 // Package np provides utilities for working with node paths from a leaf of a 2 // parse tree to the root. 3 package np 4 5 import ( 6 "src.elv.sh/pkg/eval" 7 "src.elv.sh/pkg/parse" 8 ) 9 10 // Path is a path from a leaf in a parse tree to the root. 11 type Path []parse.Node 12 13 // Find finds the path of nodes from the leaf at position p to the root. 14 func Find(root parse.Node, p int) Path { return find(root, p, false) } 15 16 // FindLeft finds the path of nodes from the leaf at position p to the root. If 17 // p points to the start of one node (p == x.From), FindLeft finds the node to 18 // the left instead (y s.t. p == y.To). 19 func FindLeft(root parse.Node, p int) Path { return find(root, p, true) } 20 21 func find(root parse.Node, p int, preferLeft bool) Path { 22 n := root 23 descend: 24 for len(parse.Children(n)) > 0 { 25 for _, ch := range parse.Children(n) { 26 r := ch.Range() 27 if r.From <= p && p < r.To || preferLeft && p == r.To { 28 n = ch 29 continue descend 30 } 31 } 32 return nil 33 } 34 var path []parse.Node 35 for { 36 path = append(path, n) 37 if n == root { 38 break 39 } 40 n = parse.Parent(n) 41 } 42 return path 43 } 44 45 // Match matches against matchers, and returns whether all matches have 46 // succeeded. 47 func (p Path) Match(ms ...Matcher) bool { 48 for _, m := range ms { 49 p2, ok := m.Match(p) 50 if !ok { 51 return false 52 } 53 p = p2 54 } 55 return true 56 } 57 58 // Matcher wraps the Match method. 59 type Matcher interface { 60 // Match takes a slice of nodes and returns the remaining nodes and whether 61 // the match succeeded. 62 Match([]parse.Node) ([]parse.Node, bool) 63 } 64 65 // Typed returns a [Matcher] matching one node of a given type. 66 func Typed[T parse.Node]() Matcher { return typedMatcher[T]{} } 67 68 // Commonly used [Typed] matchers. 69 var ( 70 Chunk = Typed[*parse.Chunk]() 71 Pipeline = Typed[*parse.Pipeline]() 72 Array = Typed[*parse.Array]() 73 Redir = Typed[*parse.Redir]() 74 Sep = Typed[*parse.Sep]() 75 ) 76 77 type typedMatcher[T parse.Node] struct{} 78 79 func (m typedMatcher[T]) Match(ns []parse.Node) ([]parse.Node, bool) { 80 if len(ns) > 0 { 81 if _, ok := ns[0].(T); ok { 82 return ns[1:], true 83 } 84 } 85 return nil, false 86 } 87 88 // Store returns a [Matcher] matching one node of a given type, and stores it 89 // if a match succeeds. 90 func Store[T parse.Node](p *T) Matcher { return storeMatcher[T]{p} } 91 92 type storeMatcher[T parse.Node] struct{ p *T } 93 94 func (m storeMatcher[T]) Match(ns []parse.Node) ([]parse.Node, bool) { 95 if len(ns) > 0 { 96 if n, ok := ns[0].(T); ok { 97 *m.p = n 98 return ns[1:], true 99 } 100 } 101 return nil, false 102 } 103 104 // SimpleExpr returns a [Matcher] matching a "simple expression", which consists 105 // of 3 nodes from the leaf upwards (Primary, Indexing and Compound) and where 106 // the Compound expression can be evaluated statically using ev. 107 func SimpleExpr(data *SimpleExprData, ev *eval.Evaler) Matcher { 108 return simpleExprMatcher{data, ev} 109 } 110 111 // SimpleExprData contains useful data written by the [SimpleExpr] matcher. 112 type SimpleExprData struct { 113 Value string 114 Compound *parse.Compound 115 PrimarType parse.PrimaryType 116 } 117 118 type simpleExprMatcher struct { 119 data *SimpleExprData 120 ev *eval.Evaler 121 } 122 123 func (m simpleExprMatcher) Match(ns []parse.Node) ([]parse.Node, bool) { 124 if len(ns) < 3 { 125 return nil, false 126 } 127 primary, ok := ns[0].(*parse.Primary) 128 if !ok { 129 return nil, false 130 } 131 indexing, ok := ns[1].(*parse.Indexing) 132 if !ok { 133 return nil, false 134 } 135 compound, ok := ns[2].(*parse.Compound) 136 if !ok { 137 return nil, false 138 } 139 value, ok := m.ev.PurelyEvalPartialCompound(compound, indexing.To) 140 if !ok { 141 return nil, false 142 } 143 *m.data = SimpleExprData{value, compound, primary.Type} 144 return ns[3:], true 145 }