/
tree.go
132 lines (113 loc) · 2.07 KB
/
tree.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
132
package sb
import (
"fmt"
"hash"
)
var (
MoreThanOneValue = fmt.Errorf("more than one value in stream")
)
type Tree struct {
*Token
Paired *Tree
Hash []byte
Subs []*Tree
}
type TreeOption interface {
IsTreeOption()
}
type WithHash struct {
NewHashState func() hash.Hash
}
func (WithHash) IsTreeOption() {}
type TapTree struct {
Func func(*Tree)
}
func (TapTree) IsTreeOption() {}
func TreeFromStream(
stream Stream,
options ...TreeOption,
) (*Tree, error) {
root := new(Tree)
stack := []*Tree{
root,
}
var hash []byte
var tap func(*Tree)
for _, option := range options {
switch option := option.(type) {
case WithHash:
s := stream
stream = Tee(s, HashFunc(
option.NewHashState,
nil,
func(h []byte, _ *Token) error {
if len(h) > 0 {
hash = h
} else {
hash = nil
}
return nil
},
nil,
))
case TapTree:
tap = option.Func
}
}
for {
var token Token
err := stream.Next(&token)
if err != nil { // NOCOVER
return nil, err
}
if !token.Valid() {
break
}
node := &Tree{
Token: &token,
Hash: hash,
}
if tap != nil {
tap(node)
}
parent := stack[len(stack)-1]
if parent.Token != nil &&
parent.Kind == KindTypeName &&
len(parent.Subs) > 0 {
// filled type name node
stack = stack[:len(stack)-1]
parent = stack[len(stack)-1]
}
parent.Subs = append(parent.Subs, node)
switch token.Kind {
case KindArray, KindObject, KindMap, KindTuple:
stack = append(stack, node)
case KindTypeName:
stack = append(stack, node)
case KindArrayEnd, KindObjectEnd, KindMapEnd, KindTupleEnd:
if len(stack) == 1 {
return nil, UnexpectedEndToken
}
node.Paired = parent
stack = stack[:len(stack)-1]
}
}
if len(root.Subs) > 1 {
return nil, MoreThanOneValue
}
if len(root.Subs) == 1 {
root = root.Subs[0]
}
root.Hash = hash
if tap != nil {
tap(root)
}
return root, nil
}
func MustTreeFromStream(stream Stream, options ...TreeOption) *Tree {
t, err := TreeFromStream(stream, options...)
if err != nil { // NOCOVER
panic(err)
}
return t
}