r - ast_test.go
1 package r
2
3 import (
4 "errors"
5 "reflect"
6 "testing"
7
8 "vimagination.zapto.org/parser"
9 )
10
11 type sourceFn struct {
12 Source string
13 Fn func(*test, Tokens)
14 }
15
16 type test struct {
17 Tokens rParser
18 Output Type
19 Err error
20 }
21
22 func makeTokeniser(tk parser.Tokeniser) *parser.Tokeniser {
23 return &tk
24 }
25
26 func doTests(t *testing.T, tests []sourceFn, fn func(*test) (Type, error)) {
27 t.Helper()
28
29 var err error
30
31 for n, tt := range tests {
32 var ts test
33
34 if ts.Tokens, err = newRParser(makeTokeniser(parser.NewStringTokeniser(tt.Source))); err != nil {
35 t.Errorf("test %d: unexpected error: %s", n+1, err)
36
37 continue
38 }
39
40 tt.Fn(&ts, Tokens(ts.Tokens[:cap(ts.Tokens)]))
41
42 if output, err := fn(&ts); !errors.Is(err, ts.Err) {
43 t.Errorf("test %d: expecting error: %v, got %v", n+1, ts.Err, err)
44 } else if ts.Output != nil && !reflect.DeepEqual(output, ts.Output) {
45 t.Errorf("test %d: expecting \n%+v\n...got...\n%+v", n+1, ts.Output, output)
46 }
47 }
48 }
49
50 func wrapQueryExpressionError(err Error) Error {
51 switch err.Parsing {
52 case "CompoundExpression":
53 err = Error{
54 Err: err,
55 Parsing: "SimpleExpression",
56 Token: err.Token,
57 }
58
59 fallthrough
60 case "SimpleExpression":
61 err = Error{
62 Err: err,
63 Parsing: "IndexOrCallExpression",
64 Token: err.Token,
65 }
66
67 fallthrough
68 case "IndexOrCallExpression":
69 err = Error{
70 Err: err,
71 Parsing: "ScopeExpression",
72 Token: err.Token,
73 }
74
75 fallthrough
76 case "ScopeExpression":
77 err = Error{
78 Err: err,
79 Parsing: "SubsetExpression",
80 Token: err.Token,
81 }
82
83 fallthrough
84 case "SubsetExpression":
85 err = Error{
86 Err: err,
87 Parsing: "ExponentiationExpression",
88 Token: err.Token,
89 }
90
91 fallthrough
92 case "ExponentiationExpression":
93 err = Error{
94 Err: err,
95 Parsing: "UnaryExpression",
96 Token: err.Token,
97 }
98
99 fallthrough
100 case "UnaryExpression":
101 err = Error{
102 Err: err,
103 Parsing: "SequenceExpression",
104 Token: err.Token,
105 }
106
107 fallthrough
108 case "SequenceExpression":
109 err = Error{
110 Err: err,
111 Parsing: "PipeOrSpecialExpression",
112 Token: err.Token,
113 }
114
115 fallthrough
116 case "PipeOrSpecialExpression":
117 err = Error{
118 Err: err,
119 Parsing: "MultiplicationExpression",
120 Token: err.Token,
121 }
122
123 fallthrough
124 case "MultiplicationExpression":
125 err = Error{
126 Err: err,
127 Parsing: "AdditionExpression",
128 Token: err.Token,
129 }
130
131 fallthrough
132 case "AdditionExpression":
133 err = Error{
134 Err: err,
135 Parsing: "RelationalExpression",
136 Token: err.Token,
137 }
138
139 fallthrough
140 case "RelationalExpression":
141 err = Error{
142 Err: err,
143 Parsing: "NotExpression",
144 Token: err.Token,
145 }
146
147 fallthrough
148 case "NotExpression":
149 err = Error{
150 Err: err,
151 Parsing: "AndExpression",
152 Token: err.Token,
153 }
154
155 fallthrough
156 case "AndExpression":
157 err = Error{
158 Err: err,
159 Parsing: "OrExpression",
160 Token: err.Token,
161 }
162
163 fallthrough
164 case "OrExpression":
165 err = Error{
166 Err: err,
167 Parsing: "FormulaeExpression",
168 Token: err.Token,
169 }
170 fallthrough
171 case "FormulaeExpression":
172 err = Error{
173 Err: err,
174 Parsing: "AssignmentExpression",
175 Token: err.Token,
176 }
177 fallthrough
178 case "AssignmentExpression":
179 err = Error{
180 Err: err,
181 Parsing: "QueryExpression",
182 Token: err.Token,
183 }
184 }
185
186 return err
187 }
188
189 func TestParseErrors(t *testing.T) {
190 const (
191 err1 = "Tokens: error at position 2 (1:2):\nunexpected EOF"
192 err2 = "File: error at position 3 (1:3):\nmissing statement terminator"
193 )
194
195 tk := parser.NewStringTokeniser("(")
196 if _, err := Parse(&tk); err == nil {
197 t.Error("test 1: expecting non-nil error")
198 } else if m := err.Error(); m != err1 {
199 t.Errorf("test 1: expecting error %q, got %q", err1, m)
200 }
201
202 tk = parser.NewStringTokeniser("a b")
203 if _, err := Parse(&tk); err == nil {
204 t.Error("test 2: expecting non-nil error")
205 } else if m := err.Error(); m != err2 {
206 t.Errorf("test 2: expecting error %q, got %q", err2, m)
207 }
208 }
209
210 func TestFile(t *testing.T) {
211 doTests(t, []sourceFn{
212 {"a", func(t *test, tk Tokens) { // 1
213 t.Output = File{
214 Statements: []Expression{
215 {
216 QueryExpression: WrapQuery(&SimpleExpression{
217 Identifier: &tk[0],
218 Tokens: tk[:1],
219 }),
220 Tokens: tk[:1],
221 },
222 },
223 Tokens: tk[:1],
224 }
225 }},
226 {"a;b", func(t *test, tk Tokens) { // 2
227 t.Output = File{
228 Statements: []Expression{
229 {
230 QueryExpression: WrapQuery(&SimpleExpression{
231 Identifier: &tk[0],
232 Tokens: tk[:1],
233 }),
234 Tokens: tk[:1],
235 },
236 {
237 QueryExpression: WrapQuery(&SimpleExpression{
238 Identifier: &tk[2],
239 Tokens: tk[2:3],
240 }),
241 Tokens: tk[2:3],
242 },
243 },
244 Tokens: tk[:3],
245 }
246 }},
247 {"a\nb", func(t *test, tk Tokens) { // 3
248 t.Output = File{
249 Statements: []Expression{
250 {
251 QueryExpression: WrapQuery(&SimpleExpression{
252 Identifier: &tk[0],
253 Tokens: tk[:1],
254 }),
255 Tokens: tk[:1],
256 },
257 {
258 QueryExpression: WrapQuery(&SimpleExpression{
259 Identifier: &tk[2],
260 Tokens: tk[2:3],
261 }),
262 Tokens: tk[2:3],
263 },
264 },
265 Tokens: tk[:3],
266 }
267 }},
268 {"in", func(t *test, tk Tokens) { // 4
269 t.Err = Error{
270 Err: Error{
271 Err: wrapQueryExpressionError(Error{
272 Err: ErrInvalidSimpleExpression,
273 Parsing: "SimpleExpression",
274 Token: tk[0],
275 }),
276 Parsing: "Expression",
277 Token: tk[0],
278 },
279 Parsing: "File",
280 Token: tk[0],
281 }
282 }},
283 {"a b", func(t *test, tk Tokens) { // 5
284 t.Err = Error{
285 Err: ErrMissingStatementTerminator,
286 Parsing: "File",
287 Token: tk[2],
288 }
289 }},
290 {"#a comment\na", func(t *test, tk Tokens) { // 6
291 t.Output = File{
292 Statements: []Expression{
293 {
294 QueryExpression: WrapQuery(&SimpleExpression{
295 Identifier: &tk[2],
296 Tokens: tk[2:3],
297 }),
298 Comments: [2]Comments{{tk[0]}, nil},
299 Tokens: tk[:3],
300 },
301 },
302 Tokens: tk[:3],
303 }
304 }},
305 {"#abc\na # def\n\n#ghi\nb #jkl", func(t *test, tk Tokens) { // 7
306 t.Output = File{
307 Statements: []Expression{
308 {
309 QueryExpression: WrapQuery(&SimpleExpression{
310 Identifier: &tk[2],
311 Tokens: tk[2:3],
312 }),
313 Comments: [2]Comments{{tk[0]}, {tk[4]}},
314 Tokens: tk[:5],
315 },
316 {
317 QueryExpression: WrapQuery(&SimpleExpression{
318 Identifier: &tk[9],
319 Tokens: tk[9:10],
320 }),
321 Comments: [2]Comments{{tk[7]}, {tk[11]}},
322 Tokens: tk[7:12],
323 },
324 },
325 Tokens: tk[:12],
326 }
327 }},
328 {"a\n#A comment", func(t *test, tk Tokens) { // 8
329 t.Output = File{
330 Statements: []Expression{
331 {
332 QueryExpression: WrapQuery(&SimpleExpression{
333 Identifier: &tk[0],
334 Tokens: tk[:1],
335 }),
336 Tokens: tk[:1],
337 },
338 },
339 Comments: Comments{tk[2]},
340 Tokens: tk[:3],
341 }
342 }},
343 {"#abc\na # def\n\n#ghi\nb #jkl\n# last", func(t *test, tk Tokens) { // 9
344 t.Output = File{
345 Statements: []Expression{
346 {
347 QueryExpression: WrapQuery(&SimpleExpression{
348 Identifier: &tk[2],
349 Tokens: tk[2:3],
350 }),
351 Comments: [2]Comments{{tk[0]}, {tk[4]}},
352 Tokens: tk[:5],
353 },
354 {
355 QueryExpression: WrapQuery(&SimpleExpression{
356 Identifier: &tk[9],
357 Tokens: tk[9:10],
358 }),
359 Comments: [2]Comments{{tk[7]}, {tk[11], tk[13]}},
360 Tokens: tk[7:14],
361 },
362 },
363 Tokens: tk[:14],
364 }
365 }},
366 {"#abc\na # def\n\n#ghi\nb #jkl\n\n# last", func(t *test, tk Tokens) { // 10
367 t.Output = File{
368 Statements: []Expression{
369 {
370 QueryExpression: WrapQuery(&SimpleExpression{
371 Identifier: &tk[2],
372 Tokens: tk[2:3],
373 }),
374 Comments: [2]Comments{{tk[0]}, {tk[4]}},
375 Tokens: tk[:5],
376 },
377 {
378 QueryExpression: WrapQuery(&SimpleExpression{
379 Identifier: &tk[9],
380 Tokens: tk[9:10],
381 }),
382 Comments: [2]Comments{{tk[7]}, {tk[11]}},
383 Tokens: tk[7:12],
384 },
385 },
386 Comments: Comments{tk[14]},
387 Tokens: tk[:15],
388 }
389 }},
390 }, func(t *test) (Type, error) {
391 var f File
392
393 err := f.parse(&t.Tokens)
394
395 return f, err
396 })
397 }
398