-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathscanner.go
131 lines (122 loc) · 2.85 KB
/
scanner.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 iterjson
import (
"bytes"
"fmt"
"iter"
)
func NextToken(in []byte) (token RawToken, remain []byte, err error) {
in = skipSpace(in)
if len(in) == 0 {
return RawToken{}, nil, nil
}
switch in[0] {
case '{', '}', '[', ']', ',', ':':
typ := TokenType(in[0])
return RawToken{typ: typ, raw: in[:1]}, in[1:], nil
case 'n':
return nextTokenConst(in, rNull)
case 'f':
return nextTokenConst(in, rFalse)
case 't':
return nextTokenConst(in, rTrue)
case '"':
return nextTokenString(in)
default:
return nextTokenNumber(in)
}
}
func Scan(in []byte) iter.Seq2[RawToken, error] {
return func(yield func(token RawToken, err error) bool) {
remain := in
for {
token, rm, err := NextToken(remain)
remain = rm
if err != nil {
yield(RawToken{}, err)
return
}
if !yield(token, nil) {
return
}
if len(remain) == 0 {
return
}
}
}
}
func nextTokenConst(in []byte, expect []byte) (token RawToken, remain []byte, err error) {
if len(in) < len(expect) || !bytes.Equal(in[:len(expect)], expect) {
return RawToken{}, in, newTokenError(string(expect), in)
}
typ := TokenType(expect[0])
return RawToken{typ: typ, raw: expect}, in[len(expect):], nil
}
func nextTokenNumber(in []byte) (token RawToken, remain []byte, err error) {
i := 0
for ; i < len(in); i++ {
c := in[i]
if c >= '0' && c <= '9' {
continue
}
switch c {
// https://datatracker.ietf.org/doc/html/rfc8259#section-6
case '+', '-', '.', 'e', 'E':
continue
}
break
}
token, remain = RawToken{typ: TokenNumber, raw: in[:i]}, in[i:]
if i == 0 {
return RawToken{}, in, newTokenError("number", in)
}
return token, remain, nil
}
func nextTokenString(in []byte) (token RawToken, remain []byte, err error) {
for i := 1; i < len(in); i++ {
c := in[i]
switch c {
case '"':
return RawToken{typ: TokenString, raw: in[:i+1]}, in[i+1:], nil
case '\\':
if i+1 >= len(in) {
return RawToken{}, in, newTokenError("string", in)
}
switch in[i+1] {
// https://datatracker.ietf.org/doc/html/rfc8259#section-7
case '"', '\\', '/', 'b', 'f', 'n', 'r', 't':
i++
case 'u':
if i+5 >= len(in) {
return RawToken{}, in, newTokenError("string", in)
}
i += 5
default:
return RawToken{}, in, newTokenError("string", in)
}
}
}
return RawToken{}, in, newTokenError("string", in)
}
func skipSpace(in []byte) []byte {
for i := 0; i < len(in); i++ {
c := in[i]
switch c {
// https://datatracker.ietf.org/doc/html/rfc8259#section-2
case ' ', '\t', '\n', '\r':
continue
default:
return in[i:]
}
}
return nil
}
func snip(in []byte, n int) []byte {
if len(in) <= n {
return in
}
return in[:n]
}
// newTokenError returns an error for an invalid token. Input should not be starts with space.
func newTokenError(name string, in []byte) error {
return fmt.Errorf("expect %s, got %s", name, snip(in, 16))
}