1 package unstable
2
3 import "github.com/pelletier/go-toml/v2/internal/characters"
4
5 func scanFollows(b []byte, pattern string) bool {
6 n := len(pattern)
7
8 return len(b) >= n && string(b[:n]) == pattern
9 }
10
11 func scanFollowsMultilineBasicStringDelimiter(b []byte) bool {
12 return scanFollows(b, `"""`)
13 }
14
15 func scanFollowsMultilineLiteralStringDelimiter(b []byte) bool {
16 return scanFollows(b, `'''`)
17 }
18
19 func scanFollowsTrue(b []byte) bool {
20 return scanFollows(b, `true`)
21 }
22
23 func scanFollowsFalse(b []byte) bool {
24 return scanFollows(b, `false`)
25 }
26
27 func scanFollowsInf(b []byte) bool {
28 return scanFollows(b, `inf`)
29 }
30
31 func scanFollowsNan(b []byte) bool {
32 return scanFollows(b, `nan`)
33 }
34
35 func scanUnquotedKey(b []byte) ([]byte, []byte) {
36
37 for i := 0; i < len(b); i++ {
38 if !isUnquotedKeyChar(b[i]) {
39 return b[:i], b[i:]
40 }
41 }
42
43 return b, b[len(b):]
44 }
45
46 func isUnquotedKeyChar(r byte) bool {
47 return (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '-' || r == '_'
48 }
49
50 func scanLiteralString(b []byte) ([]byte, []byte, error) {
51
52
53
54 for i := 1; i < len(b); {
55 switch b[i] {
56 case '\'':
57 return b[:i+1], b[i+1:], nil
58 case '\n', '\r':
59 return nil, nil, NewParserError(b[i:i+1], "literal strings cannot have new lines")
60 }
61 size := characters.Utf8ValidNext(b[i:])
62 if size == 0 {
63 return nil, nil, NewParserError(b[i:i+1], "invalid character")
64 }
65 i += size
66 }
67
68 return nil, nil, NewParserError(b[len(b):], "unterminated literal string")
69 }
70
71 func scanMultilineLiteralString(b []byte) ([]byte, []byte, error) {
72
73
74
75
76
77
78
79
80 for i := 3; i < len(b); {
81 switch b[i] {
82 case '\'':
83 if scanFollowsMultilineLiteralStringDelimiter(b[i:]) {
84 i += 3
85
86
87
88
89
90
91
92 if i >= len(b) || b[i] != '\'' {
93 return b[:i], b[i:], nil
94 }
95 i++
96
97 if i >= len(b) || b[i] != '\'' {
98 return b[:i], b[i:], nil
99 }
100 i++
101
102 if i < len(b) && b[i] == '\'' {
103 return nil, nil, NewParserError(b[i-3:i+1], "''' not allowed in multiline literal string")
104 }
105
106 return b[:i], b[i:], nil
107 }
108 case '\r':
109 if len(b) < i+2 {
110 return nil, nil, NewParserError(b[len(b):], `need a \n after \r`)
111 }
112 if b[i+1] != '\n' {
113 return nil, nil, NewParserError(b[i:i+2], `need a \n after \r`)
114 }
115 i += 2
116 continue
117 }
118 size := characters.Utf8ValidNext(b[i:])
119 if size == 0 {
120 return nil, nil, NewParserError(b[i:i+1], "invalid character")
121 }
122 i += size
123 }
124
125 return nil, nil, NewParserError(b[len(b):], `multiline literal string not terminated by '''`)
126 }
127
128 func scanWindowsNewline(b []byte) ([]byte, []byte, error) {
129 const lenCRLF = 2
130 if len(b) < lenCRLF {
131 return nil, nil, NewParserError(b, "windows new line expected")
132 }
133
134 if b[1] != '\n' {
135 return nil, nil, NewParserError(b, `windows new line should be \r\n`)
136 }
137
138 return b[:lenCRLF], b[lenCRLF:], nil
139 }
140
141 func scanWhitespace(b []byte) ([]byte, []byte) {
142 for i := 0; i < len(b); i++ {
143 switch b[i] {
144 case ' ', '\t':
145 continue
146 default:
147 return b[:i], b[i:]
148 }
149 }
150
151 return b, b[len(b):]
152 }
153
154 func scanComment(b []byte) ([]byte, []byte, error) {
155
156
157
158
159
160
161 for i := 1; i < len(b); {
162 if b[i] == '\n' {
163 return b[:i], b[i:], nil
164 }
165 if b[i] == '\r' {
166 if i+1 < len(b) && b[i+1] == '\n' {
167 return b[:i+1], b[i+1:], nil
168 }
169 return nil, nil, NewParserError(b[i:i+1], "invalid character in comment")
170 }
171 size := characters.Utf8ValidNext(b[i:])
172 if size == 0 {
173 return nil, nil, NewParserError(b[i:i+1], "invalid character in comment")
174 }
175
176 i += size
177 }
178
179 return b, b[len(b):], nil
180 }
181
182 func scanBasicString(b []byte) ([]byte, bool, []byte, error) {
183
184
185
186
187
188 escaped := false
189 i := 1
190
191 for ; i < len(b); i++ {
192 switch b[i] {
193 case '"':
194 return b[:i+1], escaped, b[i+1:], nil
195 case '\n', '\r':
196 return nil, escaped, nil, NewParserError(b[i:i+1], "basic strings cannot have new lines")
197 case '\\':
198 if len(b) < i+2 {
199 return nil, escaped, nil, NewParserError(b[i:i+1], "need a character after \\")
200 }
201 escaped = true
202 i++
203 }
204 }
205
206 return nil, escaped, nil, NewParserError(b[len(b):], `basic string not terminated by "`)
207 }
208
209 func scanMultilineBasicString(b []byte) ([]byte, bool, []byte, error) {
210
211
212
213
214
215
216
217
218
219
220
221 escaped := false
222 i := 3
223
224 for ; i < len(b); i++ {
225 switch b[i] {
226 case '"':
227 if scanFollowsMultilineBasicStringDelimiter(b[i:]) {
228 i += 3
229
230
231
232
233
234
235
236 if i >= len(b) || b[i] != '"' {
237 return b[:i], escaped, b[i:], nil
238 }
239 i++
240
241 if i >= len(b) || b[i] != '"' {
242 return b[:i], escaped, b[i:], nil
243 }
244 i++
245
246 if i < len(b) && b[i] == '"' {
247 return nil, escaped, nil, NewParserError(b[i-3:i+1], `""" not allowed in multiline basic string`)
248 }
249
250 return b[:i], escaped, b[i:], nil
251 }
252 case '\\':
253 if len(b) < i+2 {
254 return nil, escaped, nil, NewParserError(b[len(b):], "need a character after \\")
255 }
256 escaped = true
257 i++
258 case '\r':
259 if len(b) < i+2 {
260 return nil, escaped, nil, NewParserError(b[len(b):], `need a \n after \r`)
261 }
262 if b[i+1] != '\n' {
263 return nil, escaped, nil, NewParserError(b[i:i+2], `need a \n after \r`)
264 }
265 i++
266 }
267 }
268
269 return nil, escaped, nil, NewParserError(b[len(b):], `multiline basic string not terminated by """`)
270 }
271
View as plain text