-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathflow.go
183 lines (152 loc) · 4.85 KB
/
flow.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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
package bynom
import "context"
// Switch takes the result of the first parser from noms which finished without error.
// If all noms failed the function will return the last error encountered.
func Switch(noms ...Nom) Nom {
const funcName = "Switch"
return func(ctx context.Context, p Plate) (err error) {
var startPos int
if startPos, err = p.TellPosition(ctx); err != nil {
return WrapBreadcrumb(err, funcName, -1)
}
for i, nom := range noms {
if i > 0 {
if err = p.SeekPosition(ctx, startPos); err != nil {
return ExtendBreadcrumb(WrapBreadcrumb(err, funcName, i), startPos, -1)
}
}
if err = nom(ctx, p); err == nil {
break
}
}
if err != nil {
return ExtendBreadcrumb(WrapBreadcrumb(err, funcName, -1), startPos, -1)
}
return
}
}
// When implements conditional parsing. When the parser test finishes without error
// noms run. If one of parsers in noms fails the function fails with that error.
func When(test Nom, noms ...Nom) Nom {
const funcName = "When"
return func(ctx context.Context, p Plate) (err error) {
var startPos int
if startPos, err = p.TellPosition(ctx); err != nil {
return WrapBreadcrumb(err, funcName, -1)
}
if err = test(ctx, p); err != nil {
_ = p.SeekPosition(ctx, startPos)
return ExtendBreadcrumb(WrapBreadcrumb(err, funcName, -1), startPos, -1)
}
for i, nom := range noms {
var nomStartPos int
if nomStartPos, err = p.TellPosition(ctx); err != nil {
return ExtendBreadcrumb(WrapBreadcrumb(err, funcName, i), startPos, -1)
}
if err = nom(ctx, p); err != nil {
var nomErrPos, _ = p.TellPosition(ctx)
return ExtendBreadcrumb(WrapBreadcrumb(err, funcName, i), nomStartPos, nomErrPos)
}
}
return
}
}
// WhenNot implements conditional parsing. When the parser test finishes with non-nil error
// noms run. If one of parsers in noms fails the function fails with that error.
func WhenNot(test Nom, noms ...Nom) Nom {
const funcName = "WhenNot"
return func(ctx context.Context, p Plate) (err error) {
var startPos int
if startPos, err = p.TellPosition(ctx); err != nil {
return WrapBreadcrumb(err, funcName, -1)
}
if err = test(ctx, p); err == nil {
if err = p.SeekPosition(ctx, startPos); err != nil {
return ExtendBreadcrumb(WrapBreadcrumb(err, funcName, -1), startPos, -1)
}
return
} else {
err = nil
}
for i, nom := range noms {
var nomStartPos int
if nomStartPos, err = p.TellPosition(ctx); err != nil {
return ExtendBreadcrumb(WrapBreadcrumb(err, funcName, i), startPos, -1)
}
if err = nom(ctx, p); err != nil {
var nomEndPos, _ = p.TellPosition(ctx)
return ExtendBreadcrumb(WrapBreadcrumb(err, funcName, i), nomStartPos, nomEndPos)
}
}
return
}
}
// Optional runs all parsers noms until all finished or at least one failed.
// If at least one of parsers return non-nil error the function
// will revert back the read position in the plate and return nil.
func Optional(noms ...Nom) Nom {
const funcName = "Optional"
return func(ctx context.Context, p Plate) (err error) {
var startPos int
if startPos, err = p.TellPosition(ctx); err != nil {
return WrapBreadcrumb(err, funcName, -1)
}
for i, nom := range noms {
if err = nom(ctx, p); err != nil {
if err = p.SeekPosition(ctx, startPos); err != nil {
return ExtendBreadcrumb(WrapBreadcrumb(err, funcName, i), startPos, -1)
}
return nil
}
}
return
}
}
// Repeat runs all parsers noms n times.
// If at least one of parsers return non-nil error the function
// will revert back the read position in the plate and return that error.
func Repeat(n int, noms ...Nom) Nom {
const funcName = "Repeat"
return func(ctx context.Context, p Plate) (err error) {
var startPos int
if startPos, err = p.TellPosition(ctx); err != nil {
return WrapBreadcrumb(err, funcName, -1)
}
TimesLoop:
for i := 0; i < n; i++ {
for j, nom := range noms {
var nomStartPos int
if nomStartPos, err = p.TellPosition(ctx); err != nil {
return ExtendBreadcrumb(WrapBreadcrumb(err, funcName, i), startPos, -1)
}
if err = nom(ctx, p); err != nil {
var nomErrPos, _ = p.TellPosition(ctx)
err = WrapBreadcrumb(ExtendBreadcrumb(err, nomStartPos, nomErrPos), funcName, j)
break TimesLoop
}
}
}
if err != nil {
_ = p.SeekPosition(ctx, startPos)
return
}
return
}
}
// Sequence runs all parsers noms until all finished or at least one failed.
func Sequence(noms ...Nom) Nom {
const funcName = "Sequence"
return func(ctx context.Context, p Plate) (err error) {
for i, nom := range noms {
var nomStartPos int
if nomStartPos, err = p.TellPosition(ctx); err != nil {
return WrapBreadcrumb(err, funcName, i)
}
if err = nom(ctx, p); err != nil {
var nomErrPos, _ = p.TellPosition(ctx)
return ExtendBreadcrumb(WrapBreadcrumb(err, funcName, i), nomStartPos, nomErrPos)
}
}
return
}
}