forked from lestrrat-go/helium
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstack.go
More file actions
138 lines (115 loc) · 2.62 KB
/
Copy pathstack.go
File metadata and controls
138 lines (115 loc) · 2.62 KB
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
132
133
134
135
136
137
138
package helium
import (
"slices"
"github.com/lestrrat-go/helium/internal/stack"
)
// nodeEntry is a lightweight record for the parser's element stack.
// It stores only the data needed for end-tag matching and SAX callbacks,
// avoiding a full *Element allocation per start tag.
type nodeEntry struct {
local string
prefix string
uri string
qname string
}
func (e *nodeEntry) Name() string {
return e.qname
}
func (e *nodeEntry) LocalName() string {
return e.local
}
func (e *nodeEntry) Prefix() string {
return e.prefix
}
func (e *nodeEntry) URI() string {
return e.uri
}
type nodeStack struct {
stack.Stack[nodeEntry]
}
type inputStack struct {
stack.Stack[any]
}
type nsStack struct {
stack.KeyedStack[nsStackItem]
}
type nsStackItem struct {
prefix string
href string
}
// Appease the sax.Namespace interface
func (i nsStackItem) Prefix() string {
return i.prefix
}
// Appease the sax.Namespace interface
func (i nsStackItem) URI() string {
return i.href
}
func (i nsStackItem) Key() string {
return i.prefix
}
func (s *nsStack) Push(prefix, uri string) {
// Force-append: namespace prefixes may be redeclared on child elements
// (shadowing the parent's binding), so we must allow duplicate keys.
// KeyedStack.Lookup searches from the end, giving correct shadowing.
s.KeyedStack = append(s.KeyedStack, nsStackItem{prefix: prefix, href: uri})
}
func (s *nsStack) Lookup(prefix string) string {
item, ok := s.KeyedStack.Lookup(prefix)
if !ok {
return ""
}
return item.href
}
// LookupInTopN searches only the top n entries of the stack for prefix.
// This is used to detect duplicate namespace declarations on the same
// element without being confused by ancestor bindings (which are valid
// prefix shadowing, not duplicates).
func (s *nsStack) LookupInTopN(prefix string, n int) string {
entries := s.Peek(n)
for _, v := range slices.Backward(entries) {
if v.prefix == prefix {
return v.href
}
}
return ""
}
func (s *nodeStack) Push(e nodeEntry) {
s.Stack.Push(e)
}
func (s *nodeStack) Pop() *nodeEntry {
l := s.Peek(1)
if len(l) != 1 {
return nil
}
e := &l[0]
s.Stack.Pop()
return e
}
func (s *nodeStack) PeekOne() *nodeEntry {
l := s.Peek(1)
if len(l) != 1 {
return nil
}
return &l[0]
}
// the reason we're using any here is that we may have to
// push a ByteCursor or a RuneCursor, and they don't share
// a common API
func (s *inputStack) Push(c any) {
s.Stack.Push(c)
}
func (s *inputStack) Pop() any {
defer s.Stack.Pop()
if e := s.PeekOne(); e != nil {
return e
}
return nil
}
func (s *inputStack) PeekOne() any {
l := s.Peek(1)
if len(l) != 1 {
return nil
}
return l[0]
}