OLD | NEW |
| (Empty) |
1 library petitparser.test.core_test; | |
2 | |
3 import 'dart:math' as math; | |
4 import 'package:test/test.dart' hide anyOf; | |
5 | |
6 import 'package:petitparser/petitparser.dart'; | |
7 | |
8 void expectSuccess(Parser parser, dynamic input, dynamic expected, | |
9 [int position]) { | |
10 var result = parser.parse(input); | |
11 expect(result.isSuccess, isTrue); | |
12 expect(result.isFailure, isFalse); | |
13 expect(result.value, expected); | |
14 expect(result.position, position != null ? position : input.length); | |
15 } | |
16 | |
17 void expectFailure(Parser parser, dynamic input, | |
18 [int position = 0, String message]) { | |
19 var result = parser.parse(input); | |
20 expect(result.isFailure, isTrue); | |
21 expect(result.isSuccess, isFalse); | |
22 expect(result.position, position); | |
23 if (message != null) { | |
24 expect(result.message, message); | |
25 } | |
26 } | |
27 | |
28 class ListGrammarDefinition extends GrammarDefinition { | |
29 start() => ref(list).end(); | |
30 list() => ref(element) & char(',') & ref(list) | ref(element); | |
31 element() => digit().plus().flatten(); | |
32 } | |
33 | |
34 class ListParserDefinition extends ListGrammarDefinition { | |
35 element() => super.element().map((value) => int.parse(value)); | |
36 } | |
37 | |
38 class TokenizedListGrammarDefinition extends GrammarDefinition { | |
39 start() => ref(list).end(); | |
40 list() => ref(element) & ref(token, char(',')) & ref(list) | ref(element); | |
41 element() => ref(token, digit().plus()); | |
42 token(p) => p.flatten().trim(); | |
43 } | |
44 | |
45 class BuggedGrammarDefinition extends GrammarDefinition { | |
46 start() => epsilon(); | |
47 | |
48 directRecursion1() => ref(directRecursion1); | |
49 | |
50 indirectRecursion1() => ref(indirectRecursion2); | |
51 indirectRecursion2() => ref(indirectRecursion3); | |
52 indirectRecursion3() => ref(indirectRecursion1); | |
53 | |
54 delegation1() => ref(delegation2); | |
55 delegation2() => ref(delegation3); | |
56 delegation3() => epsilon(); | |
57 } | |
58 | |
59 class LambdaGrammarDefinition extends GrammarDefinition { | |
60 start() => ref(expression).end(); | |
61 expression() => ref(variable) | ref(abstraction) | ref(application); | |
62 | |
63 variable() => (letter() & word().star()).flatten().trim(); | |
64 abstraction() => token('\\') & ref(variable) & token('.') & ref(expression); | |
65 application() => token('(') & ref(expression) & ref(expression) & token(')'); | |
66 | |
67 token(value) => char(value).trim(); | |
68 } | |
69 | |
70 class ExpressionGrammarDefinition extends GrammarDefinition { | |
71 start() => ref(terms).end(); | |
72 terms() => ref(addition) | ref(factors); | |
73 | |
74 addition() => ref(factors).separatedBy(token(char('+') | char('-'))); | |
75 factors() => ref(multiplication) | ref(power); | |
76 | |
77 multiplication() => ref(power).separatedBy(token(char('*') | char('/'))); | |
78 power() => ref(primary).separatedBy(char('^').trim()); | |
79 | |
80 primary() => ref(number) | ref(parentheses); | |
81 number() => token(char('-').optional() & digit().plus() & (char('.') & digit()
.plus()).optional()); | |
82 | |
83 parentheses() => token('(') & ref(terms) & token(')'); | |
84 token(value) => value is String ? char(value).trim() : value.flatten().trim(); | |
85 } | |
86 | |
87 class PluggableCompositeParser extends CompositeParser { | |
88 final Function _function; | |
89 | |
90 PluggableCompositeParser(this._function) : super(); | |
91 | |
92 void initialize() { | |
93 _function(this); | |
94 } | |
95 } | |
96 | |
97 main() { | |
98 group('parsers', () { | |
99 test('and()', () { | |
100 var parser = char('a').and(); | |
101 expectSuccess(parser, 'a', 'a', 0); | |
102 expectFailure(parser, 'b', 0, '"a" expected'); | |
103 expectFailure(parser, ''); | |
104 }); | |
105 test('or() operator', () { | |
106 var parser = char('a') | char('b'); | |
107 expectSuccess(parser, 'a', 'a'); | |
108 expectSuccess(parser, 'b', 'b'); | |
109 expectFailure(parser, 'c'); | |
110 expectFailure(parser, ''); | |
111 }); | |
112 test('or() of two', () { | |
113 var parser = char('a').or(char('b')); | |
114 expectSuccess(parser, 'a', 'a'); | |
115 expectSuccess(parser, 'b', 'b'); | |
116 expectFailure(parser, 'c'); | |
117 expectFailure(parser, ''); | |
118 }); | |
119 test('or() of three', () { | |
120 var parser = char('a').or(char('b')).or(char('c')); | |
121 expectSuccess(parser, 'a', 'a'); | |
122 expectSuccess(parser, 'b', 'b'); | |
123 expectSuccess(parser, 'c', 'c'); | |
124 expectFailure(parser, 'd'); | |
125 expectFailure(parser, ''); | |
126 }); | |
127 test('end()', () { | |
128 var parser = char('a').end(); | |
129 expectFailure(parser, '', 0, '"a" expected'); | |
130 expectSuccess(parser, 'a', 'a'); | |
131 expectFailure(parser, 'aa', 1, 'end of input expected'); | |
132 }); | |
133 test('epsilon()', () { | |
134 var parser = epsilon(); | |
135 expectSuccess(parser, '', null); | |
136 expectSuccess(parser, 'a', null, 0); | |
137 }); | |
138 test('failure()', () { | |
139 var parser = failure('failure'); | |
140 expectFailure(parser, '', 0, 'failure'); | |
141 expectFailure(parser, 'a', 0, 'failure'); | |
142 }); | |
143 test('flatten()', () { | |
144 var parser = digit().plus().flatten(); | |
145 expectFailure(parser, ''); | |
146 expectFailure(parser, 'a'); | |
147 expectSuccess(parser, '1', '1'); | |
148 expectSuccess(parser, '12', '12'); | |
149 expectSuccess(parser, '123', '123'); | |
150 expectSuccess(parser, '1234', '1234'); | |
151 }); | |
152 test('token() on string', () { | |
153 var parser = digit().plus().token(); | |
154 expectFailure(parser, ''); | |
155 expectFailure(parser, 'a'); | |
156 var token = parser.parse('123').value; | |
157 expect(token.value, ['1', '2', '3']); | |
158 expect(token.buffer, '123'); | |
159 expect(token.start, 0); | |
160 expect(token.stop, 3); | |
161 expect(token.input, '123'); | |
162 expect(token.length, 3); | |
163 expect(token.line, 1); | |
164 expect(token.column, 1); | |
165 expect(token.toString(), 'Token[1:1]: [1, 2, 3]'); | |
166 }); | |
167 test('token() on list', () { | |
168 var parser = any().plus().token(); | |
169 var token = parser.parse([1, 2, 3]).value; | |
170 expect(token.value, [1, 2, 3]); | |
171 expect(token.buffer, [1, 2, 3]); | |
172 expect(token.start, 0); | |
173 expect(token.stop, 3); | |
174 expect(token.input, [1, 2, 3]); | |
175 expect(token.length, 3); | |
176 expect(token.toString(), 'Token[0]: [1, 2, 3]'); | |
177 }); | |
178 test('map()', () { | |
179 var parser = digit().map((String each) { | |
180 return each.codeUnitAt(0) - '0'.codeUnitAt(0); | |
181 }); | |
182 expectSuccess(parser, '1', 1); | |
183 expectSuccess(parser, '4', 4); | |
184 expectSuccess(parser, '9', 9); | |
185 expectFailure(parser, ''); | |
186 expectFailure(parser, 'a'); | |
187 }); | |
188 test('pick(1)', () { | |
189 var parser = digit().seq(letter()).pick(1); | |
190 expectSuccess(parser, '1a', 'a'); | |
191 expectSuccess(parser, '2b', 'b'); | |
192 expectFailure(parser, ''); | |
193 expectFailure(parser, '1', 1, 'letter expected'); | |
194 expectFailure(parser, '12', 1, 'letter expected'); | |
195 }); | |
196 test('pick(-1)', () { | |
197 var parser = digit().seq(letter()).pick(-1); | |
198 expectSuccess(parser, '1a', 'a'); | |
199 expectSuccess(parser, '2b', 'b'); | |
200 expectFailure(parser, ''); | |
201 expectFailure(parser, '1', 1, 'letter expected'); | |
202 expectFailure(parser, '12', 1, 'letter expected'); | |
203 }); | |
204 test('permute([1, 0])', () { | |
205 var parser = digit().seq(letter()).permute([1, 0]); | |
206 expectSuccess(parser, '1a', ['a', '1']); | |
207 expectSuccess(parser, '2b', ['b', '2']); | |
208 expectFailure(parser, ''); | |
209 expectFailure(parser, '1', 1, 'letter expected'); | |
210 expectFailure(parser, '12', 1, 'letter expected'); | |
211 }); | |
212 test('permute([-1, 0])', () { | |
213 var parser = digit().seq(letter()).permute([-1, 0]); | |
214 expectSuccess(parser, '1a', ['a', '1']); | |
215 expectSuccess(parser, '2b', ['b', '2']); | |
216 expectFailure(parser, ''); | |
217 expectFailure(parser, '1', 1, 'letter expected'); | |
218 expectFailure(parser, '12', 1, 'letter expected'); | |
219 }); | |
220 test('not()', () { | |
221 var parser = char('a').not('not "a" expected'); | |
222 expectFailure(parser, 'a', 0, 'not "a" expected'); | |
223 expectSuccess(parser, 'b', null, 0); | |
224 expectSuccess(parser, '', null); | |
225 }); | |
226 test('neg()', () { | |
227 var parser = digit().neg('no digit expected'); | |
228 expectFailure(parser, '1', 0, 'no digit expected'); | |
229 expectFailure(parser, '9', 0, 'no digit expected'); | |
230 expectSuccess(parser, 'a', 'a'); | |
231 expectSuccess(parser, ' ', ' '); | |
232 expectFailure(parser, '', 0, 'input expected'); | |
233 }); | |
234 test('optional()', () { | |
235 var parser = char('a').optional(); | |
236 expectSuccess(parser, 'a', 'a'); | |
237 expectSuccess(parser, 'b', null, 0); | |
238 expectSuccess(parser, '', null); | |
239 }); | |
240 test('plus()', () { | |
241 var parser = char('a').plus(); | |
242 expectFailure(parser, '', 0, '"a" expected'); | |
243 expectSuccess(parser, 'a', ['a']); | |
244 expectSuccess(parser, 'aa', ['a', 'a']); | |
245 expectSuccess(parser, 'aaa', ['a', 'a', 'a']); | |
246 }); | |
247 test('plusGreedy()', () { | |
248 var parser = word().plusGreedy(digit()); | |
249 expectFailure(parser, '', 0, 'letter or digit expected'); | |
250 expectFailure(parser, 'a', 1, 'digit expected'); | |
251 expectFailure(parser, 'ab', 1, 'digit expected'); | |
252 expectFailure(parser, '1', 1, 'digit expected'); | |
253 expectSuccess(parser, 'a1', ['a'], 1); | |
254 expectSuccess(parser, 'ab1', ['a', 'b'], 2); | |
255 expectSuccess(parser, 'abc1', ['a', 'b', 'c'], 3); | |
256 expectSuccess(parser, '12', ['1'], 1); | |
257 expectSuccess(parser, 'a12', ['a', '1'], 2); | |
258 expectSuccess(parser, 'ab12', ['a', 'b', '1'], 3); | |
259 expectSuccess(parser, 'abc12', ['a', 'b', 'c', '1'], 4); | |
260 expectSuccess(parser, '123', ['1', '2'], 2); | |
261 expectSuccess(parser, 'a123', ['a', '1', '2'], 3); | |
262 expectSuccess(parser, 'ab123', ['a', 'b', '1', '2'], 4); | |
263 expectSuccess(parser, 'abc123', ['a', 'b', 'c', '1', '2'], 5); | |
264 }); | |
265 test('plusLazy()', () { | |
266 var parser = word().plusLazy(digit()); | |
267 expectFailure(parser, ''); | |
268 expectFailure(parser, 'a', 1, 'digit expected'); | |
269 expectFailure(parser, 'ab', 2, 'digit expected'); | |
270 expectFailure(parser, '1', 1, 'digit expected'); | |
271 expectSuccess(parser, 'a1', ['a'], 1); | |
272 expectSuccess(parser, 'ab1', ['a', 'b'], 2); | |
273 expectSuccess(parser, 'abc1', ['a', 'b', 'c'], 3); | |
274 expectSuccess(parser, '12', ['1'], 1); | |
275 expectSuccess(parser, 'a12', ['a'], 1); | |
276 expectSuccess(parser, 'ab12', ['a', 'b'], 2); | |
277 expectSuccess(parser, 'abc12', ['a', 'b', 'c'], 3); | |
278 expectSuccess(parser, '123', ['1'], 1); | |
279 expectSuccess(parser, 'a123', ['a'], 1); | |
280 expectSuccess(parser, 'ab123', ['a', 'b'], 2); | |
281 expectSuccess(parser, 'abc123', ['a', 'b', 'c'], 3); | |
282 }); | |
283 test('times()', () { | |
284 var parser = char('a').times(2); | |
285 expectFailure(parser, '', 0, '"a" expected'); | |
286 expectFailure(parser, 'a', 1, '"a" expected'); | |
287 expectSuccess(parser, 'aa', ['a', 'a']); | |
288 expectSuccess(parser, 'aaa', ['a', 'a'], 2); | |
289 }); | |
290 test('repeat()', () { | |
291 var parser = char('a').repeat(2, 3); | |
292 expectFailure(parser, '', 0, '"a" expected'); | |
293 expectFailure(parser, 'a', 1, '"a" expected'); | |
294 expectSuccess(parser, 'aa', ['a', 'a']); | |
295 expectSuccess(parser, 'aaa', ['a', 'a', 'a']); | |
296 expectSuccess(parser, 'aaaa', ['a', 'a', 'a'], 3); | |
297 }); | |
298 test('repeat() unbounded', () { | |
299 var input = new List.filled(100000, 'a'); | |
300 var parser = char('a').repeat(2, unbounded); | |
301 expectSuccess(parser, input.join(), input); | |
302 }); | |
303 test('repeatGreedy()', () { | |
304 var parser = word().repeatGreedy(digit(), 2, 4); | |
305 expectFailure(parser, '', 0, 'letter or digit expected'); | |
306 expectFailure(parser, 'a', 1, 'letter or digit expected'); | |
307 expectFailure(parser, 'ab', 2, 'digit expected'); | |
308 expectFailure(parser, 'abc', 2, 'digit expected'); | |
309 expectFailure(parser, 'abcd', 2, 'digit expected'); | |
310 expectFailure(parser, 'abcde', 2, 'digit expected'); | |
311 expectFailure(parser, '1', 1, 'letter or digit expected'); | |
312 expectFailure(parser, 'a1', 2, 'digit expected'); | |
313 expectSuccess(parser, 'ab1', ['a', 'b'], 2); | |
314 expectSuccess(parser, 'abc1', ['a', 'b', 'c'], 3); | |
315 expectSuccess(parser, 'abcd1', ['a', 'b', 'c', 'd'], 4); | |
316 expectFailure(parser, 'abcde1', 2, 'digit expected'); | |
317 expectFailure(parser, '12', 2, 'digit expected'); | |
318 expectSuccess(parser, 'a12', ['a', '1'], 2); | |
319 expectSuccess(parser, 'ab12', ['a', 'b', '1'], 3); | |
320 expectSuccess(parser, 'abc12', ['a', 'b', 'c', '1'], 4); | |
321 expectSuccess(parser, 'abcd12', ['a', 'b', 'c', 'd'], 4); | |
322 expectFailure(parser, 'abcde12', 2, 'digit expected'); | |
323 expectSuccess(parser, '123', ['1', '2'], 2); | |
324 expectSuccess(parser, 'a123', ['a', '1', '2'], 3); | |
325 expectSuccess(parser, 'ab123', ['a', 'b', '1', '2'], 4); | |
326 expectSuccess(parser, 'abc123', ['a', 'b', 'c', '1'], 4); | |
327 expectSuccess(parser, 'abcd123', ['a', 'b', 'c', 'd'], 4); | |
328 expectFailure(parser, 'abcde123', 2, 'digit expected'); | |
329 }); | |
330 test('repeatGreedy() unbounded', () { | |
331 var inputLetter = new List.filled(100000, 'a'); | |
332 var inputDigit = new List.filled(100000, '1'); | |
333 var parser = word().repeatGreedy(digit(), 2, unbounded); | |
334 expectSuccess(parser, inputLetter.join() + '1', inputLetter, inputLetter.l
ength); | |
335 expectSuccess(parser, inputDigit.join() + '1', inputDigit, inputDigit.leng
th); | |
336 }); | |
337 test('repeatLazy()', () { | |
338 var parser = word().repeatLazy(digit(), 2, 4); | |
339 expectFailure(parser, '', 0, 'letter or digit expected'); | |
340 expectFailure(parser, 'a', 1, 'letter or digit expected'); | |
341 expectFailure(parser, 'ab', 2, 'digit expected'); | |
342 expectFailure(parser, 'abc', 3, 'digit expected'); | |
343 expectFailure(parser, 'abcd', 4, 'digit expected'); | |
344 expectFailure(parser, 'abcde', 4, 'digit expected'); | |
345 expectFailure(parser, '1', 1, 'letter or digit expected'); | |
346 expectFailure(parser, 'a1', 2, 'digit expected'); | |
347 expectSuccess(parser, 'ab1', ['a', 'b'], 2); | |
348 expectSuccess(parser, 'abc1', ['a', 'b', 'c'], 3); | |
349 expectSuccess(parser, 'abcd1', ['a', 'b', 'c', 'd'], 4); | |
350 expectFailure(parser, 'abcde1', 4, 'digit expected'); | |
351 expectFailure(parser, '12', 2, 'digit expected'); | |
352 expectSuccess(parser, 'a12', ['a', '1'], 2); | |
353 expectSuccess(parser, 'ab12', ['a', 'b'], 2); | |
354 expectSuccess(parser, 'abc12', ['a', 'b', 'c'], 3); | |
355 expectSuccess(parser, 'abcd12', ['a', 'b', 'c', 'd'], 4); | |
356 expectFailure(parser, 'abcde12', 4, 'digit expected'); | |
357 expectSuccess(parser, '123', ['1', '2'], 2); | |
358 expectSuccess(parser, 'a123', ['a', '1'], 2); | |
359 expectSuccess(parser, 'ab123', ['a', 'b'], 2); | |
360 expectSuccess(parser, 'abc123', ['a', 'b', 'c'], 3); | |
361 expectSuccess(parser, 'abcd123', ['a', 'b', 'c', 'd'], 4); | |
362 expectFailure(parser, 'abcde123', 4, 'digit expected'); | |
363 }); | |
364 test('repeatLazy() unbounded', () { | |
365 var input = new List.filled(100000, 'a'); | |
366 var parser = word().repeatLazy(digit(), 2, unbounded); | |
367 expectSuccess(parser, input.join() + '1111', input, input.length); | |
368 }); | |
369 test('separatedBy()', () { | |
370 var parser = char('a').separatedBy(char('b')); | |
371 expectFailure(parser, '', 0, '"a" expected'); | |
372 expectSuccess(parser, 'a', ['a']); | |
373 expectSuccess(parser, 'ab', ['a'], 1); | |
374 expectSuccess(parser, 'aba', ['a', 'b', 'a']); | |
375 expectSuccess(parser, 'abab', ['a', 'b', 'a'], 3); | |
376 expectSuccess(parser, 'ababa', ['a', 'b', 'a', 'b', 'a']); | |
377 expectSuccess(parser, 'ababab', ['a', 'b', 'a', 'b', 'a'], 5); | |
378 }); | |
379 test('separatedBy() without separators', () { | |
380 var parser = char('a').separatedBy(char('b'), includeSeparators: false); | |
381 expectFailure(parser, '', 0, '"a" expected'); | |
382 expectSuccess(parser, 'a', ['a']); | |
383 expectSuccess(parser, 'ab', ['a'], 1); | |
384 expectSuccess(parser, 'aba', ['a', 'a']); | |
385 expectSuccess(parser, 'abab', ['a', 'a'], 3); | |
386 expectSuccess(parser, 'ababa', ['a', 'a', 'a']); | |
387 expectSuccess(parser, 'ababab', ['a', 'a', 'a'], 5); | |
388 }); | |
389 test('separatedBy() separator at end', () { | |
390 var parser = | |
391 char('a').separatedBy(char('b'), optionalSeparatorAtEnd: true); | |
392 expectFailure(parser, '', 0, '"a" expected'); | |
393 expectSuccess(parser, 'a', ['a']); | |
394 expectSuccess(parser, 'ab', ['a', 'b']); | |
395 expectSuccess(parser, 'aba', ['a', 'b', 'a']); | |
396 expectSuccess(parser, 'abab', ['a', 'b', 'a', 'b']); | |
397 expectSuccess(parser, 'ababa', ['a', 'b', 'a', 'b', 'a']); | |
398 expectSuccess(parser, 'ababab', ['a', 'b', 'a', 'b', 'a', 'b']); | |
399 }); | |
400 test('separatedBy() without separators & separator at end', () { | |
401 var parser = char('a').separatedBy(char('b'), | |
402 includeSeparators: false, optionalSeparatorAtEnd: true); | |
403 expectFailure(parser, '', 0, '"a" expected'); | |
404 expectSuccess(parser, 'a', ['a']); | |
405 expectSuccess(parser, 'ab', ['a']); | |
406 expectSuccess(parser, 'aba', ['a', 'a']); | |
407 expectSuccess(parser, 'abab', ['a', 'a']); | |
408 expectSuccess(parser, 'ababa', ['a', 'a', 'a']); | |
409 expectSuccess(parser, 'ababab', ['a', 'a', 'a']); | |
410 }); | |
411 test('seq() operator', () { | |
412 var parser = char('a') & char('b'); | |
413 expectSuccess(parser, 'ab', ['a', 'b']); | |
414 expectFailure(parser, ''); | |
415 expectFailure(parser, 'x'); | |
416 expectFailure(parser, 'a', 1); | |
417 expectFailure(parser, 'ax', 1); | |
418 }); | |
419 test('seq() of two', () { | |
420 var parser = char('a').seq(char('b')); | |
421 expectSuccess(parser, 'ab', ['a', 'b']); | |
422 expectFailure(parser, ''); | |
423 expectFailure(parser, 'x'); | |
424 expectFailure(parser, 'a', 1); | |
425 expectFailure(parser, 'ax', 1); | |
426 }); | |
427 test('seq() of three', () { | |
428 var parser = char('a').seq(char('b')).seq(char('c')); | |
429 expectSuccess(parser, 'abc', ['a', 'b', 'c']); | |
430 expectFailure(parser, ''); | |
431 expectFailure(parser, 'x'); | |
432 expectFailure(parser, 'a', 1); | |
433 expectFailure(parser, 'ax', 1); | |
434 expectFailure(parser, 'ab', 2); | |
435 expectFailure(parser, 'abx', 2); | |
436 }); | |
437 test('star()', () { | |
438 var parser = char('a').star(); | |
439 expectSuccess(parser, '', []); | |
440 expectSuccess(parser, 'a', ['a']); | |
441 expectSuccess(parser, 'aa', ['a', 'a']); | |
442 expectSuccess(parser, 'aaa', ['a', 'a', 'a']); | |
443 }); | |
444 test('starGreedy()', () { | |
445 var parser = word().starGreedy(digit()); | |
446 expectFailure(parser, '', 0, 'digit expected'); | |
447 expectFailure(parser, 'a', 0, 'digit expected'); | |
448 expectFailure(parser, 'ab', 0, 'digit expected'); | |
449 expectSuccess(parser, '1', [], 0); | |
450 expectSuccess(parser, 'a1', ['a'], 1); | |
451 expectSuccess(parser, 'ab1', ['a', 'b'], 2); | |
452 expectSuccess(parser, 'abc1', ['a', 'b', 'c'], 3); | |
453 expectSuccess(parser, '12', ['1'], 1); | |
454 expectSuccess(parser, 'a12', ['a', '1'], 2); | |
455 expectSuccess(parser, 'ab12', ['a', 'b', '1'], 3); | |
456 expectSuccess(parser, 'abc12', ['a', 'b', 'c', '1'], 4); | |
457 expectSuccess(parser, '123', ['1', '2'], 2); | |
458 expectSuccess(parser, 'a123', ['a', '1', '2'], 3); | |
459 expectSuccess(parser, 'ab123', ['a', 'b', '1', '2'], 4); | |
460 expectSuccess(parser, 'abc123', ['a', 'b', 'c', '1', '2'], 5); | |
461 }); | |
462 test('starLazy()', () { | |
463 var parser = word().starLazy(digit()); | |
464 expectFailure(parser, ''); | |
465 expectFailure(parser, 'a', 1, 'digit expected'); | |
466 expectFailure(parser, 'ab', 2, 'digit expected'); | |
467 expectSuccess(parser, '1', [], 0); | |
468 expectSuccess(parser, 'a1', ['a'], 1); | |
469 expectSuccess(parser, 'ab1', ['a', 'b'], 2); | |
470 expectSuccess(parser, 'abc1', ['a', 'b', 'c'], 3); | |
471 expectSuccess(parser, '12', [], 0); | |
472 expectSuccess(parser, 'a12', ['a'], 1); | |
473 expectSuccess(parser, 'ab12', ['a', 'b'], 2); | |
474 expectSuccess(parser, 'abc12', ['a', 'b', 'c'], 3); | |
475 expectSuccess(parser, '123', [], 0); | |
476 expectSuccess(parser, 'a123', ['a'], 1); | |
477 expectSuccess(parser, 'ab123', ['a', 'b'], 2); | |
478 expectSuccess(parser, 'abc123', ['a', 'b', 'c'], 3); | |
479 }); | |
480 test('trim()', () { | |
481 var parser = char('a').trim(); | |
482 expectSuccess(parser, 'a', 'a'); | |
483 expectSuccess(parser, ' a', 'a'); | |
484 expectSuccess(parser, 'a ', 'a'); | |
485 expectSuccess(parser, ' a ', 'a'); | |
486 expectSuccess(parser, ' a', 'a'); | |
487 expectSuccess(parser, 'a ', 'a'); | |
488 expectSuccess(parser, ' a ', 'a'); | |
489 expectFailure(parser, '', 0, '"a" expected'); | |
490 expectFailure(parser, 'b', 0, '"a" expected'); | |
491 expectFailure(parser, ' b', 1, '"a" expected'); | |
492 expectFailure(parser, ' b', 2, '"a" expected'); | |
493 }); | |
494 test('trim() both', () { | |
495 var parser = char('a').trim(char('*')); | |
496 expectSuccess(parser, 'a', 'a'); | |
497 expectSuccess(parser, '*a', 'a'); | |
498 expectSuccess(parser, 'a*', 'a'); | |
499 expectSuccess(parser, '*a*', 'a'); | |
500 expectSuccess(parser, '**a', 'a'); | |
501 expectSuccess(parser, 'a**', 'a'); | |
502 expectSuccess(parser, '**a**', 'a'); | |
503 expectFailure(parser, '', 0, '"a" expected'); | |
504 expectFailure(parser, 'b', 0, '"a" expected'); | |
505 expectFailure(parser, '*b', 1, '"a" expected'); | |
506 expectFailure(parser, '**b', 2, '"a" expected'); | |
507 }); | |
508 test('trim() left/right', () { | |
509 var parser = char('a').trim(char('*'), char('#')); | |
510 expectSuccess(parser, 'a', 'a'); | |
511 expectSuccess(parser, '*a', 'a'); | |
512 expectSuccess(parser, 'a#', 'a'); | |
513 expectSuccess(parser, '*a#', 'a'); | |
514 expectSuccess(parser, '**a', 'a'); | |
515 expectSuccess(parser, 'a##', 'a'); | |
516 expectSuccess(parser, '**a##', 'a'); | |
517 expectFailure(parser, '', 0, '"a" expected'); | |
518 expectFailure(parser, 'b', 0, '"a" expected'); | |
519 expectFailure(parser, '*b', 1, '"a" expected'); | |
520 expectFailure(parser, '**b', 2, '"a" expected'); | |
521 expectFailure(parser, '#a', 0, '"a" expected'); | |
522 expectSuccess(parser, 'a*', 'a', 1); | |
523 }); | |
524 test('undefined()', () { | |
525 var parser = undefined(); | |
526 expectFailure(parser, '', 0, 'undefined parser'); | |
527 expectFailure(parser, 'a', 0, 'undefined parser'); | |
528 parser.set(char('a')); | |
529 expectSuccess(parser, 'a', 'a'); | |
530 }); | |
531 test('setable()', () { | |
532 var parser = char('a').settable(); | |
533 expectSuccess(parser, 'a', 'a'); | |
534 expectFailure(parser, 'b', 0, '"a" expected'); | |
535 expectFailure(parser, ''); | |
536 }); | |
537 }); | |
538 group('characters', () { | |
539 test('anyOf()', () { | |
540 var parser = anyOf('uncopyrightable'); | |
541 expectSuccess(parser, 'c', 'c'); | |
542 expectSuccess(parser, 'g', 'g'); | |
543 expectSuccess(parser, 'h', 'h'); | |
544 expectSuccess(parser, 'i', 'i'); | |
545 expectSuccess(parser, 'o', 'o'); | |
546 expectSuccess(parser, 'p', 'p'); | |
547 expectSuccess(parser, 'r', 'r'); | |
548 expectSuccess(parser, 't', 't'); | |
549 expectSuccess(parser, 'y', 'y'); | |
550 expectFailure(parser, 'x', 0, 'any of "uncopyrightable" expected'); | |
551 }); | |
552 test('noneOf()', () { | |
553 var parser = noneOf('uncopyrightable'); | |
554 expectSuccess(parser, 'x', 'x'); | |
555 expectFailure(parser, 'c', 0, 'none of "uncopyrightable" expected'); | |
556 expectFailure(parser, 'g', 0, 'none of "uncopyrightable" expected'); | |
557 expectFailure(parser, 'h', 0, 'none of "uncopyrightable" expected'); | |
558 expectFailure(parser, 'i', 0, 'none of "uncopyrightable" expected'); | |
559 expectFailure(parser, 'o', 0, 'none of "uncopyrightable" expected'); | |
560 expectFailure(parser, 'p', 0, 'none of "uncopyrightable" expected'); | |
561 expectFailure(parser, 'r', 0, 'none of "uncopyrightable" expected'); | |
562 expectFailure(parser, 't', 0, 'none of "uncopyrightable" expected'); | |
563 expectFailure(parser, 'y', 0, 'none of "uncopyrightable" expected'); | |
564 }); | |
565 test('char() with number', () { | |
566 var parser = char(97, 'lowercase a'); | |
567 expectSuccess(parser, 'a', 'a'); | |
568 expectFailure(parser, 'b', 0, 'lowercase a'); | |
569 expectFailure(parser, ''); | |
570 }); | |
571 test('char() invalid', () { | |
572 expect(() => char('ab'), throwsArgumentError); | |
573 }); | |
574 test('digit()', () { | |
575 var parser = digit(); | |
576 expectSuccess(parser, '1', '1'); | |
577 expectSuccess(parser, '9', '9'); | |
578 expectFailure(parser, 'a', 0, 'digit expected'); | |
579 expectFailure(parser, ''); | |
580 }); | |
581 test('letter()', () { | |
582 var parser = letter(); | |
583 expectSuccess(parser, 'a', 'a'); | |
584 expectSuccess(parser, 'X', 'X'); | |
585 expectFailure(parser, '0', 0, 'letter expected'); | |
586 expectFailure(parser, ''); | |
587 }); | |
588 test('lowercase()', () { | |
589 var parser = lowercase(); | |
590 expectSuccess(parser, 'a', 'a'); | |
591 expectSuccess(parser, 'z', 'z'); | |
592 expectFailure(parser, 'A', 0, 'lowercase letter expected'); | |
593 expectFailure(parser, '0', 0, 'lowercase letter expected'); | |
594 expectFailure(parser, ''); | |
595 }); | |
596 test('pattern() with single', () { | |
597 var parser = pattern('abc'); | |
598 expectSuccess(parser, 'a', 'a'); | |
599 expectSuccess(parser, 'b', 'b'); | |
600 expectSuccess(parser, 'c', 'c'); | |
601 expectFailure(parser, 'd', 0, '[abc] expected'); | |
602 expectFailure(parser, ''); | |
603 }); | |
604 test('pattern() with range', () { | |
605 var parser = pattern('a-c'); | |
606 expectSuccess(parser, 'a', 'a'); | |
607 expectSuccess(parser, 'b', 'b'); | |
608 expectSuccess(parser, 'c', 'c'); | |
609 expectFailure(parser, 'd', 0, '[a-c] expected'); | |
610 expectFailure(parser, ''); | |
611 }); | |
612 test('pattern() with overlapping range', () { | |
613 var parser = pattern('b-da-c'); | |
614 expectSuccess(parser, 'a', 'a'); | |
615 expectSuccess(parser, 'b', 'b'); | |
616 expectSuccess(parser, 'c', 'c'); | |
617 expectSuccess(parser, 'd', 'd'); | |
618 expectFailure(parser, 'e', 0, '[b-da-c] expected'); | |
619 expectFailure(parser, '', 0, '[b-da-c] expected'); | |
620 }); | |
621 test('pattern() with adjacent range', () { | |
622 var parser = pattern('c-ea-c'); | |
623 expectSuccess(parser, 'a', 'a'); | |
624 expectSuccess(parser, 'b', 'b'); | |
625 expectSuccess(parser, 'c', 'c'); | |
626 expectSuccess(parser, 'd', 'd'); | |
627 expectSuccess(parser, 'e', 'e'); | |
628 expectFailure(parser, 'f', 0, '[c-ea-c] expected'); | |
629 expectFailure(parser, '', 0, '[c-ea-c] expected'); | |
630 }); | |
631 test('pattern() with prefix range', () { | |
632 var parser = pattern('a-ea-c'); | |
633 expectSuccess(parser, 'a', 'a'); | |
634 expectSuccess(parser, 'b', 'b'); | |
635 expectSuccess(parser, 'c', 'c'); | |
636 expectSuccess(parser, 'd', 'd'); | |
637 expectSuccess(parser, 'e', 'e'); | |
638 expectFailure(parser, 'f', 0, '[a-ea-c] expected'); | |
639 expectFailure(parser, '', 0, '[a-ea-c] expected'); | |
640 }); | |
641 test('pattern() with postfix range', () { | |
642 var parser = pattern('a-ec-e'); | |
643 expectSuccess(parser, 'a', 'a'); | |
644 expectSuccess(parser, 'b', 'b'); | |
645 expectSuccess(parser, 'c', 'c'); | |
646 expectSuccess(parser, 'd', 'd'); | |
647 expectSuccess(parser, 'e', 'e'); | |
648 expectFailure(parser, 'f', 0, '[a-ec-e] expected'); | |
649 expectFailure(parser, '', 0, '[a-ec-e] expected'); | |
650 }); | |
651 test('pattern() with repeated range', () { | |
652 var parser = pattern('a-ea-e'); | |
653 expectSuccess(parser, 'a', 'a'); | |
654 expectSuccess(parser, 'b', 'b'); | |
655 expectSuccess(parser, 'c', 'c'); | |
656 expectSuccess(parser, 'd', 'd'); | |
657 expectSuccess(parser, 'e', 'e'); | |
658 expectFailure(parser, 'f', 0, '[a-ea-e] expected'); | |
659 expectFailure(parser, '', 0, '[a-ea-e] expected'); | |
660 }); | |
661 test('pattern() with composed range', () { | |
662 var parser = pattern('ac-df-'); | |
663 expectSuccess(parser, 'a', 'a'); | |
664 expectSuccess(parser, 'c', 'c'); | |
665 expectSuccess(parser, 'd', 'd'); | |
666 expectSuccess(parser, 'f', 'f'); | |
667 expectSuccess(parser, '-', '-'); | |
668 expectFailure(parser, 'b', 0, '[ac-df-] expected'); | |
669 expectFailure(parser, 'e', 0, '[ac-df-] expected'); | |
670 expectFailure(parser, 'g', 0, '[ac-df-] expected'); | |
671 expectFailure(parser, ''); | |
672 }); | |
673 test('pattern() with negated single', () { | |
674 var parser = pattern('^a'); | |
675 expectSuccess(parser, 'b', 'b'); | |
676 expectFailure(parser, 'a', 0, '[^a] expected'); | |
677 expectFailure(parser, ''); | |
678 }); | |
679 test('pattern() with negated range', () { | |
680 var parser = pattern('^a-c'); | |
681 expectSuccess(parser, 'd', 'd'); | |
682 expectFailure(parser, 'a', 0, '[^a-c] expected'); | |
683 expectFailure(parser, 'b', 0, '[^a-c] expected'); | |
684 expectFailure(parser, 'c', 0, '[^a-c] expected'); | |
685 expectFailure(parser, ''); | |
686 }); | |
687 test('range()', () { | |
688 var parser = range('e', 'o'); | |
689 expectSuccess(parser, 'e', 'e'); | |
690 expectSuccess(parser, 'i', 'i'); | |
691 expectSuccess(parser, 'o', 'o'); | |
692 expectFailure(parser, 'p', 0, 'e..o expected'); | |
693 expectFailure(parser, 'd', 0, 'e..o expected'); | |
694 expectFailure(parser, ''); | |
695 }); | |
696 test('uppercase()', () { | |
697 var parser = uppercase(); | |
698 expectSuccess(parser, 'A', 'A'); | |
699 expectSuccess(parser, 'Z', 'Z'); | |
700 expectFailure(parser, 'a', 0, 'uppercase letter expected'); | |
701 expectFailure(parser, '0', 0, 'uppercase letter expected'); | |
702 expectFailure(parser, ''); | |
703 }); | |
704 test('whitespace()', () { | |
705 var parser = whitespace(); | |
706 expectSuccess(parser, ' ', ' '); | |
707 expectSuccess(parser, '\t', '\t'); | |
708 expectSuccess(parser, '\r', '\r'); | |
709 expectSuccess(parser, '\f', '\f'); | |
710 expectFailure(parser, 'z', 0, 'whitespace expected'); | |
711 expectFailure(parser, ''); | |
712 }); | |
713 test('whitespace() unicode', () { | |
714 var string = new String.fromCharCodes([ | |
715 0x09, | |
716 0x0A, | |
717 0x0B, | |
718 0x0C, | |
719 0x0D, | |
720 0x20, | |
721 0x85, | |
722 0xA0, | |
723 0x1680, | |
724 0x180E, | |
725 0x2000, | |
726 0x2001, | |
727 0x2002, | |
728 0x2003, | |
729 0x2004, | |
730 0x2005, | |
731 0x2006, | |
732 0x2007, | |
733 0x2008, | |
734 0x2009, | |
735 0x200A, | |
736 0x2028, | |
737 0x2029, | |
738 0x202F, | |
739 0x205F, | |
740 0x3000, | |
741 0xFEFF | |
742 ]); | |
743 var parser = whitespace().star().flatten().end(); | |
744 expectSuccess(parser, string, string); | |
745 }); | |
746 test('word()', () { | |
747 var parser = word(); | |
748 expectSuccess(parser, 'a', 'a'); | |
749 expectSuccess(parser, 'z', 'z'); | |
750 expectSuccess(parser, 'A', 'A'); | |
751 expectSuccess(parser, 'Z', 'Z'); | |
752 expectSuccess(parser, '0', '0'); | |
753 expectSuccess(parser, '9', '9'); | |
754 expectSuccess(parser, '_', '_'); | |
755 expectFailure(parser, '-', 0, 'letter or digit expected'); | |
756 expectFailure(parser, ''); | |
757 }); | |
758 }); | |
759 group('predicates', () { | |
760 test('any()', () { | |
761 var parser = any(); | |
762 expectSuccess(parser, 'a', 'a'); | |
763 expectSuccess(parser, 'b', 'b'); | |
764 expectFailure(parser, '', 0, 'input expected'); | |
765 }); | |
766 test('anyIn()', () { | |
767 var parser = anyIn(['a', 'b']); | |
768 expectSuccess(parser, 'a', 'a'); | |
769 expectSuccess(parser, 'b', 'b'); | |
770 expectFailure(parser, 'c'); | |
771 expectFailure(parser, ''); | |
772 }); | |
773 test('string()', () { | |
774 var parser = string('foo'); | |
775 expectSuccess(parser, 'foo', 'foo'); | |
776 expectFailure(parser, ''); | |
777 expectFailure(parser, 'f'); | |
778 expectFailure(parser, 'fo'); | |
779 expectFailure(parser, 'Foo'); | |
780 }); | |
781 test('stringIgnoreCase()', () { | |
782 var parser = stringIgnoreCase('foo'); | |
783 expectSuccess(parser, 'foo', 'foo'); | |
784 expectSuccess(parser, 'FOO', 'FOO'); | |
785 expectSuccess(parser, 'fOo', 'fOo'); | |
786 expectFailure(parser, ''); | |
787 expectFailure(parser, 'f'); | |
788 expectFailure(parser, 'Fo'); | |
789 }); | |
790 }); | |
791 group('token', () { | |
792 var parser = any().map((value) => value.codeUnitAt(0)).token().star(); | |
793 var buffer = '1\r12\r\n123\n1234'; | |
794 var result = parser.parse(buffer).value; | |
795 test('value', () { | |
796 var expected = [49, 13, 49, 50, 13, 10, 49, 50, 51, 10, 49, 50, 51, 52]; | |
797 expect(result.map((token) => token.value), expected); | |
798 }); | |
799 test('buffer', () { | |
800 var expected = new List.filled(buffer.length, buffer); | |
801 expect(result.map((token) => token.buffer), expected); | |
802 }); | |
803 test('start', () { | |
804 var expected = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; | |
805 expect(result.map((token) => token.start), expected); | |
806 }); | |
807 test('stop', () { | |
808 var expected = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]; | |
809 expect(result.map((token) => token.stop), expected); | |
810 }); | |
811 test('length', () { | |
812 var expected = new List.filled(buffer.length, 1); | |
813 expect(result.map((token) => token.length), expected); | |
814 }); | |
815 test('line', () { | |
816 var expected = [1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4]; | |
817 expect(result.map((token) => token.line), expected); | |
818 }); | |
819 test('column', () { | |
820 var expected = [1, 2, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]; | |
821 expect(result.map((token) => token.column), expected); | |
822 }); | |
823 test('input', () { | |
824 var expected = ['1', '\r', '1', '2', '\r', '\n', '1', '2', '3', '\n', '1',
'2', '3', '4']; | |
825 expect(result.map((token) => token.input), expected); | |
826 }); | |
827 test('unique', () { | |
828 expect(new Set.from(result).length, result.length); | |
829 }); | |
830 test('equals', () { | |
831 for (var i = 0; i < result.length; i++) { | |
832 for (var j = 0; j < result.length; j++) { | |
833 var condition = i == j ? isTrue : isFalse; | |
834 expect(result[i] == result[j], condition); | |
835 expect(result[i].hashCode == result[j].hashCode, condition); | |
836 } | |
837 } | |
838 }); | |
839 }); | |
840 group('context', () { | |
841 var buffer = 'a\nc'; | |
842 var context = new Context(buffer, 0); | |
843 test('context', () { | |
844 expect(context.buffer, buffer); | |
845 expect(context.position, 0); | |
846 expect(context.toString(), 'Context[1:1]'); | |
847 }); | |
848 test('success', () { | |
849 var success = context.success('result'); | |
850 expect(success.buffer, buffer); | |
851 expect(success.position, 0); | |
852 expect(success.value, 'result'); | |
853 expect(success.message, isNull); | |
854 expect(success.isSuccess, isTrue); | |
855 expect(success.isFailure, isFalse); | |
856 expect(success.toString(), 'Success[1:1]: result'); | |
857 }); | |
858 test('success with position', () { | |
859 var success = context.success('result', 2); | |
860 expect(success.buffer, buffer); | |
861 expect(success.position, 2); | |
862 expect(success.value, 'result'); | |
863 expect(success.message, isNull); | |
864 expect(success.isSuccess, isTrue); | |
865 expect(success.isFailure, isFalse); | |
866 expect(success.toString(), 'Success[2:1]: result'); | |
867 }); | |
868 test('failure', () { | |
869 var failure = context.failure('error'); | |
870 expect(failure.buffer, buffer); | |
871 expect(failure.position, 0); | |
872 try { | |
873 failure.value; | |
874 fail('Expected ParserError to be thrown'); | |
875 } on ParserError catch (error) { | |
876 expect(error.failure, same(failure)); | |
877 expect(error.toString(), 'error at 1:1'); | |
878 } | |
879 expect(failure.message, 'error'); | |
880 expect(failure.isSuccess, isFalse); | |
881 expect(failure.isFailure, isTrue); | |
882 expect(failure.toString(), 'Failure[1:1]: error'); | |
883 }); | |
884 test('failure with position', () { | |
885 var failure = context.failure('error', 2); | |
886 expect(failure.buffer, buffer); | |
887 expect(failure.position, 2); | |
888 try { | |
889 failure.value; | |
890 fail('Expected ParserError to be thrown'); | |
891 } on ParserError catch (error) { | |
892 expect(error.failure, same(failure)); | |
893 expect(error.toString(), 'error at 2:1'); | |
894 } | |
895 expect(failure.message, 'error'); | |
896 expect(failure.isSuccess, isFalse); | |
897 expect(failure.isFailure, isTrue); | |
898 expect(failure.toString(), 'Failure[2:1]: error'); | |
899 }); | |
900 }); | |
901 group('parsing', () { | |
902 test('parse()', () { | |
903 var parser = char('a'); | |
904 expect(parser.parse('a').isSuccess, isTrue); | |
905 expect(parser.parse('b').isSuccess, isFalse); | |
906 }); | |
907 test('accept()', () { | |
908 var parser = char('a'); | |
909 expect(parser.accept('a'), isTrue); | |
910 expect(parser.accept('b'), isFalse); | |
911 }); | |
912 test('matches()', () { | |
913 var parser = digit().seq(digit()).flatten(); | |
914 expect(parser.matches('a123b45'), ['12', '23', '45']); | |
915 }); | |
916 test('matchesSkipping()', () { | |
917 var parser = digit().seq(digit()).flatten(); | |
918 expect(parser.matchesSkipping('a123b45'), ['12', '45']); | |
919 }); | |
920 }); | |
921 group('examples', () { | |
922 final identifier = letter().seq(word().star()).flatten(); | |
923 final number = char('-') | |
924 .optional() | |
925 .seq(digit().plus()) | |
926 .seq(char('.').seq(digit().plus()).optional()) | |
927 .flatten(); | |
928 final quoted = char('"') | |
929 .seq(char('"').neg().star()) | |
930 .seq(char('"')) | |
931 .flatten(); | |
932 final keyword = string('return') | |
933 .seq(whitespace().plus().flatten()) | |
934 .seq(identifier.or(number).or(quoted)) | |
935 .map((list) => list.last); | |
936 final javadoc = string('/**') | |
937 .seq(string('*/').neg().star()) | |
938 .seq(string('*/')) | |
939 .flatten(); | |
940 test('valid identifier', () { | |
941 expectSuccess(identifier, 'a', 'a'); | |
942 expectSuccess(identifier, 'a1', 'a1'); | |
943 expectSuccess(identifier, 'a12', 'a12'); | |
944 expectSuccess(identifier, 'ab', 'ab'); | |
945 expectSuccess(identifier, 'a1b', 'a1b'); | |
946 }); | |
947 test('incomplete identifier', () { | |
948 expectSuccess(identifier, 'a=', 'a', 1); | |
949 expectSuccess(identifier, 'a1-', 'a1', 2); | |
950 expectSuccess(identifier, 'a12+', 'a12', 3); | |
951 expectSuccess(identifier, 'ab ', 'ab', 2); | |
952 }); | |
953 test('invalid identifier', () { | |
954 expectFailure(identifier, '', 0, 'letter expected'); | |
955 expectFailure(identifier, '1', 0, 'letter expected'); | |
956 expectFailure(identifier, '1a', 0, 'letter expected'); | |
957 }); | |
958 test('positive number', () { | |
959 expectSuccess(number, '1', '1'); | |
960 expectSuccess(number, '12', '12'); | |
961 expectSuccess(number, '12.3', '12.3'); | |
962 expectSuccess(number, '12.34', '12.34'); | |
963 }); | |
964 test('negative number', () { | |
965 expectSuccess(number, '-1', '-1'); | |
966 expectSuccess(number, '-12', '-12'); | |
967 expectSuccess(number, '-12.3', '-12.3'); | |
968 expectSuccess(number, '-12.34', '-12.34'); | |
969 }); | |
970 test('incomplete number', () { | |
971 expectSuccess(number, '1..', '1', 1); | |
972 expectSuccess(number, '12-', '12', 2); | |
973 expectSuccess(number, '12.3.', '12.3', 4); | |
974 expectSuccess(number, '12.34.', '12.34', 5); | |
975 }); | |
976 test('invalid number', () { | |
977 expectFailure(number, '', 0, 'digit expected'); | |
978 expectFailure(number, '-', 1, 'digit expected'); | |
979 expectFailure(number, '-x', 1, 'digit expected'); | |
980 expectFailure(number, '.', 0, 'digit expected'); | |
981 expectFailure(number, '.1', 0, 'digit expected'); | |
982 }); | |
983 test('valid string', () { | |
984 expectSuccess(quoted, '""', '""'); | |
985 expectSuccess(quoted, '"a"', '"a"'); | |
986 expectSuccess(quoted, '"ab"', '"ab"'); | |
987 expectSuccess(quoted, '"abc"', '"abc"'); | |
988 }); | |
989 test('incomplete string', () { | |
990 expectSuccess(quoted, '""x', '""', 2); | |
991 expectSuccess(quoted, '"a"x', '"a"', 3); | |
992 expectSuccess(quoted, '"ab"x', '"ab"', 4); | |
993 expectSuccess(quoted, '"abc"x', '"abc"', 5); | |
994 }); | |
995 test('invalid string', () { | |
996 expectFailure(quoted, '"', 1, '""" expected'); | |
997 expectFailure(quoted, '"a', 2, '""" expected'); | |
998 expectFailure(quoted, '"ab', 3, '""" expected'); | |
999 expectFailure(quoted, 'a"', 0, '""" expected'); | |
1000 expectFailure(quoted, 'ab"', 0, '""" expected'); | |
1001 }); | |
1002 test('return statement', () { | |
1003 expectSuccess(keyword, 'return f', 'f'); | |
1004 expectSuccess(keyword, 'return f', 'f'); | |
1005 expectSuccess(keyword, 'return foo', 'foo'); | |
1006 expectSuccess(keyword, 'return foo', 'foo'); | |
1007 expectSuccess(keyword, 'return 1', '1'); | |
1008 expectSuccess(keyword, 'return 1', '1'); | |
1009 expectSuccess(keyword, 'return -2.3', '-2.3'); | |
1010 expectSuccess(keyword, 'return -2.3', '-2.3'); | |
1011 expectSuccess(keyword, 'return "a"', '"a"'); | |
1012 expectSuccess(keyword, 'return "a"', '"a"'); | |
1013 }); | |
1014 test('invalid statement', () { | |
1015 expectFailure(keyword, 'retur f', 0, 'return expected'); | |
1016 expectFailure(keyword, 'return1', 6, 'whitespace expected'); | |
1017 expectFailure(keyword, 'return _', 8, '""" expected'); | |
1018 }); | |
1019 test('javadoc', () { | |
1020 expectSuccess(javadoc, '/** foo */', '/** foo */'); | |
1021 expectSuccess(javadoc, '/** * * */', '/** * * */'); | |
1022 }); | |
1023 }); | |
1024 group('copying, matching, replacing', () { | |
1025 void verify(Parser parser) { | |
1026 var copy = parser.copy(); | |
1027 // check copying | |
1028 expect(copy, isNot(same(parser))); | |
1029 expect(copy.toString(), parser.toString()); | |
1030 expect(copy.runtimeType, parser.runtimeType); | |
1031 expect(copy.children, | |
1032 pairwiseCompare(parser.children, identical, 'same children')); | |
1033 // check equality | |
1034 expect(copy.isEqualTo(copy), isTrue); | |
1035 expect(parser.isEqualTo(parser), isTrue); | |
1036 expect(copy.isEqualTo(parser), isTrue); | |
1037 expect(parser.isEqualTo(copy), isTrue); | |
1038 // check replacing | |
1039 var replaced = new List(); | |
1040 for (var i = 0; i < copy.children.length; i++) { | |
1041 var source = copy.children[i], | |
1042 target = any(); | |
1043 copy.replace(source, target); | |
1044 expect(copy.children[i], same(target)); | |
1045 replaced.add(target); | |
1046 } | |
1047 expect(copy.children, | |
1048 pairwiseCompare(replaced, identical, 'replaced children')); | |
1049 } | |
1050 test('any()', () => verify(any())); | |
1051 test('and()', () => verify(digit().and())); | |
1052 test('char()', () => verify(char('a'))); | |
1053 test('digit()', () => verify(digit())); | |
1054 test('delegate()', () => verify(new DelegateParser(any()))); | |
1055 test('end()', () => verify(digit().end())); | |
1056 test('epsilon()', () => verify(epsilon())); | |
1057 test('failure()', () => verify(failure())); | |
1058 test('flatten()', () => verify(digit().flatten())); | |
1059 test('map()', () => verify(digit().map((a) => a))); | |
1060 test('not()', () => verify(digit().not())); | |
1061 test('optional()', () => verify(digit().optional())); | |
1062 test('or()', () => verify(digit().or(word()))); | |
1063 test('plus()', () => verify(digit().plus())); | |
1064 test('plusGreedy()', () => verify(digit().plusGreedy(word()))); | |
1065 test('plusLazy()', () => verify(digit().plusLazy(word()))); | |
1066 test('repeat()', () => verify(digit().repeat(2, 3))); | |
1067 test('repeatGreedy()', () => verify(digit().repeatGreedy(word(), 2, 3))); | |
1068 test('repeatLazy()', () => verify(digit().repeatLazy(word(), 2, 3))); | |
1069 test('seq()', () => verify(digit().seq(word()))); | |
1070 test('setable()', () => verify(digit().settable())); | |
1071 test('star()', () => verify(digit().star())); | |
1072 test('starGreedy()', () => verify(digit().starGreedy(word()))); | |
1073 test('starLazy()', () => verify(digit().starLazy(word()))); | |
1074 test('string()', () => verify(string('ab'))); | |
1075 test('times()', () => verify(digit().times(2))); | |
1076 test('token()', () => verify(digit().token())); | |
1077 test('trim()', () => verify(digit().trim(char('a'), char('b')))); | |
1078 test('undefined()', () => verify(undefined())); | |
1079 }); | |
1080 group('regressions', () { | |
1081 test('flatten().trim()', () { | |
1082 var parser = word().plus().flatten().trim(); | |
1083 expectSuccess(parser, 'ab1', 'ab1'); | |
1084 expectSuccess(parser, ' ab1 ', 'ab1'); | |
1085 expectSuccess(parser, ' ab1 ', 'ab1'); | |
1086 }); | |
1087 test('trim().flatten()', () { | |
1088 var parser = word().plus().trim().flatten(); | |
1089 expectSuccess(parser, 'ab1', 'ab1'); | |
1090 expectSuccess(parser, ' ab1 ', ' ab1 '); | |
1091 expectSuccess(parser, ' ab1 ', ' ab1 '); | |
1092 }); | |
1093 }); | |
1094 group('definition', () { | |
1095 var grammarDefinition = new ListGrammarDefinition(); | |
1096 var parserDefinition = new ListParserDefinition(); | |
1097 var tokenDefinition = new TokenizedListGrammarDefinition(); | |
1098 var buggedDefinition = new BuggedGrammarDefinition(); | |
1099 | |
1100 test('reference without parameters', () { | |
1101 var firstReference = grammarDefinition.ref(grammarDefinition.start); | |
1102 var secondReference = grammarDefinition.ref(grammarDefinition.start); | |
1103 expect(firstReference, isNot(same(secondReference))); | |
1104 expect(firstReference == secondReference, isTrue); | |
1105 }); | |
1106 test('reference with different production', () { | |
1107 var firstReference = grammarDefinition.ref(grammarDefinition.start); | |
1108 var secondReference = grammarDefinition.ref(grammarDefinition.element); | |
1109 expect(firstReference, isNot(same(secondReference))); | |
1110 expect(firstReference == secondReference, isFalse); | |
1111 }); | |
1112 test('reference with same parameters', () { | |
1113 var firstReference = grammarDefinition.ref(grammarDefinition.start, 'a'); | |
1114 var secondReference = grammarDefinition.ref(grammarDefinition.start, 'a'); | |
1115 expect(firstReference, isNot(same(secondReference))); | |
1116 expect(firstReference == secondReference, isTrue); | |
1117 }); | |
1118 test('reference with different parameters', () { | |
1119 var firstReference = grammarDefinition.ref(grammarDefinition.start, 'a'); | |
1120 var secondReference = grammarDefinition.ref(grammarDefinition.start, 'b'); | |
1121 expect(firstReference, isNot(same(secondReference))); | |
1122 expect(firstReference == secondReference, isFalse); | |
1123 }); | |
1124 test('grammar', () { | |
1125 var parser = grammarDefinition.build(); | |
1126 expectSuccess(parser, '1,2', ['1', ',', '2']); | |
1127 expectSuccess(parser, '1,2,3', ['1', ',', ['2', ',', '3']]); | |
1128 }); | |
1129 test('parser', () { | |
1130 var parser = parserDefinition.build(); | |
1131 expectSuccess(parser, '1,2', [1, ',', 2]); | |
1132 expectSuccess(parser, '1,2,3', [1, ',', [2, ',', 3]]); | |
1133 }); | |
1134 test('token', () { | |
1135 var parser = tokenDefinition.build(); | |
1136 expectSuccess(parser, '1, 2', ['1', ',', '2']); | |
1137 expectSuccess(parser, '1, 2, 3', ['1', ',', ['2', ',', '3']]); | |
1138 }); | |
1139 test('direct recursion', () { | |
1140 expect(() => | |
1141 buggedDefinition.build(start: buggedDefinition.directRecursion1), | |
1142 throwsStateError); | |
1143 }); | |
1144 test('indirect recursion', () { | |
1145 expect(() => buggedDefinition.build( | |
1146 start: buggedDefinition.indirectRecursion1), throwsStateError); | |
1147 expect(() => buggedDefinition.build( | |
1148 start: buggedDefinition.indirectRecursion2), throwsStateError); | |
1149 expect(() => buggedDefinition.build( | |
1150 start: buggedDefinition.indirectRecursion3), throwsStateError); | |
1151 }); | |
1152 test('delegation', () { | |
1153 expect(buggedDefinition.build( | |
1154 start: buggedDefinition.delegation1) is EpsilonParser, isTrue); | |
1155 expect(buggedDefinition.build( | |
1156 start: buggedDefinition.delegation2) is EpsilonParser, isTrue); | |
1157 expect(buggedDefinition.build( | |
1158 start: buggedDefinition.delegation3) is EpsilonParser, isTrue); | |
1159 }); | |
1160 test('lambda example', () { | |
1161 var definition = new LambdaGrammarDefinition(); | |
1162 var parser = definition.build(); | |
1163 expect(parser.accept('x'), isTrue); | |
1164 expect(parser.accept('xy'), isTrue); | |
1165 expect(parser.accept('x12'), isTrue); | |
1166 expect(parser.accept('\\x.y'), isTrue); | |
1167 expect(parser.accept('\\x.\\y.z'), isTrue); | |
1168 expect(parser.accept('(x x)'), isTrue); | |
1169 expect(parser.accept('(x y)'), isTrue); | |
1170 expect(parser.accept('(x (y z))'), isTrue); | |
1171 expect(parser.accept('((x y) z)'), isTrue); | |
1172 }); | |
1173 test('expression example', () { | |
1174 var definition = new ExpressionGrammarDefinition(); | |
1175 var parser = definition.build(); | |
1176 expect(parser.accept('1'), isTrue); | |
1177 expect(parser.accept('12'), isTrue); | |
1178 expect(parser.accept('1.23'), isTrue); | |
1179 expect(parser.accept('-12.3'), isTrue); | |
1180 expect(parser.accept('1 + 2'), isTrue); | |
1181 expect(parser.accept('1 + 2 + 3'), isTrue); | |
1182 expect(parser.accept('1 - 2'), isTrue); | |
1183 expect(parser.accept('1 - 2 - 3'), isTrue); | |
1184 expect(parser.accept('1 * 2'), isTrue); | |
1185 expect(parser.accept('1 * 2 * 3'), isTrue); | |
1186 expect(parser.accept('1 / 2'), isTrue); | |
1187 expect(parser.accept('1 / 2 / 3'), isTrue); | |
1188 expect(parser.accept('1 ^ 2'), isTrue); | |
1189 expect(parser.accept('1 ^ 2 ^ 3'), isTrue); | |
1190 expect(parser.accept('1 + (2 * 3)'), isTrue); | |
1191 expect(parser.accept('(1 + 2) * 3'), isTrue); | |
1192 }); | |
1193 }); | |
1194 group('expression', () { | |
1195 Parser build({attachAction: true}) { | |
1196 var action = attachAction ? (func) => func : (func) => null; | |
1197 var root = failure().settable(); | |
1198 var builder = new ExpressionBuilder(); | |
1199 builder.group() | |
1200 ..primitive(char('(').trim().seq(root).seq(char(')').trim()).pick(1)) | |
1201 ..primitive(digit().plus().seq(char('.').seq(digit().plus()).optional()) | |
1202 .flatten().trim().map((a) => double.parse(a))); | |
1203 builder.group() | |
1204 ..prefix(char('-').trim(), action((op, a) => -a)); | |
1205 builder.group() | |
1206 ..postfix(string('++').trim(), action((a, op) => ++a)) | |
1207 ..postfix(string('--').trim(), action((a, op) => --a)); | |
1208 builder.group() | |
1209 ..right(char('^').trim(), action((a, op, b) => math.pow(a, b))); | |
1210 builder.group() | |
1211 ..left(char('*').trim(), action((a, op, b) => a * b)) | |
1212 ..left(char('/').trim(), action((a, op, b) => a / b)); | |
1213 builder.group() | |
1214 ..left(char('+').trim(), action((a, op, b) => a + b)) | |
1215 ..left(char('-').trim(), action((a, op, b) => a - b)); | |
1216 root.set(builder.build()); | |
1217 return root.end(); | |
1218 } | |
1219 var epsilon = 1e-5; | |
1220 var evaluator = build(attachAction: true); | |
1221 var parser = build(attachAction: false); | |
1222 test('number', () { | |
1223 expect(evaluator.parse('0').value, closeTo(0, epsilon)); | |
1224 expect(evaluator.parse('0.0').value, closeTo(0, epsilon)); | |
1225 expect(evaluator.parse('1').value, closeTo(1, epsilon)); | |
1226 expect(evaluator.parse('1.2').value, closeTo(1.2, epsilon)); | |
1227 expect(evaluator.parse('34').value, closeTo(34, epsilon)); | |
1228 expect(evaluator.parse('34.7').value, closeTo(34.7, epsilon)); | |
1229 expect(evaluator.parse('56.78').value, closeTo(56.78, epsilon)); | |
1230 }); | |
1231 test('number negative', () { | |
1232 expect(evaluator.parse('-1').value, closeTo(-1, epsilon)); | |
1233 expect(evaluator.parse('-1.2').value, closeTo(-1.2, epsilon)); | |
1234 }); | |
1235 test('number parse', () { | |
1236 expect(parser.parse('0').value, 0); | |
1237 expect(parser.parse('-1').value, ['-', 1]); | |
1238 }); | |
1239 test('add', () { | |
1240 expect(evaluator.parse('1 + 2').value, closeTo(3, epsilon)); | |
1241 expect(evaluator.parse('2 + 1').value, closeTo(3, epsilon)); | |
1242 expect(evaluator.parse('1 + 2.3').value, closeTo(3.3, epsilon)); | |
1243 expect(evaluator.parse('2.3 + 1').value, closeTo(3.3, epsilon)); | |
1244 expect(evaluator.parse('1 + -2').value, closeTo(-1, epsilon)); | |
1245 expect(evaluator.parse('-2 + 1').value, closeTo(-1, epsilon)); | |
1246 }); | |
1247 test('add many', () { | |
1248 expect(evaluator.parse('1').value, closeTo(1, epsilon)); | |
1249 expect(evaluator.parse('1 + 2').value, closeTo(3, epsilon)); | |
1250 expect(evaluator.parse('1 + 2 + 3').value, closeTo(6, epsilon)); | |
1251 expect(evaluator.parse('1 + 2 + 3 + 4').value, closeTo(10, epsilon)); | |
1252 expect(evaluator.parse('1 + 2 + 3 + 4 + 5').value, closeTo(15, epsilon)); | |
1253 }); | |
1254 test('add parse', () { | |
1255 expect(parser.parse('1 + 2 + 3').value, [[1, '+', 2], '+', 3]); | |
1256 }); | |
1257 test('sub', () { | |
1258 expect(evaluator.parse('1 - 2').value, closeTo(-1, epsilon)); | |
1259 expect(evaluator.parse('1.2 - 1.2').value, closeTo(0, epsilon)); | |
1260 expect(evaluator.parse('1 - -2').value, closeTo(3, epsilon)); | |
1261 expect(evaluator.parse('-1 - -2').value, closeTo(1, epsilon)); | |
1262 }); | |
1263 test('sub many', () { | |
1264 expect(evaluator.parse('1').value, closeTo(1, epsilon)); | |
1265 expect(evaluator.parse('1 - 2').value, closeTo(-1, epsilon)); | |
1266 expect(evaluator.parse('1 - 2 - 3').value, closeTo(-4, epsilon)); | |
1267 expect(evaluator.parse('1 - 2 - 3 - 4').value, closeTo(-8, epsilon)); | |
1268 expect(evaluator.parse('1 - 2 - 3 - 4 - 5').value, closeTo(-13, epsilon)); | |
1269 }); | |
1270 test('sub parse', () { | |
1271 expect(parser.parse('1 - 2 - 3').value, [[1, '-', 2], '-', 3]); | |
1272 }); | |
1273 test('mul', () { | |
1274 expect(evaluator.parse('2 * 3').value, closeTo(6, epsilon)); | |
1275 expect(evaluator.parse('2 * -4').value, closeTo(-8, epsilon)); | |
1276 }); | |
1277 test('mul many', () { | |
1278 expect(evaluator.parse('1 * 2').value, closeTo(2, epsilon)); | |
1279 expect(evaluator.parse('1 * 2 * 3').value, closeTo(6, epsilon)); | |
1280 expect(evaluator.parse('1 * 2 * 3 * 4').value, closeTo(24, epsilon)); | |
1281 expect(evaluator.parse('1 * 2 * 3 * 4 * 5').value, closeTo(120, epsilon)); | |
1282 }); | |
1283 test('mul parse', () { | |
1284 expect(parser.parse('1 * 2 * 3').value, [[1, '*', 2], '*', 3]); | |
1285 }); | |
1286 test('div', () { | |
1287 expect(evaluator.parse('12 / 3').value, closeTo(4, epsilon)); | |
1288 expect(evaluator.parse('-16 / -4').value, closeTo(4, epsilon)); | |
1289 }); | |
1290 test('div many', () { | |
1291 expect(evaluator.parse('100 / 2').value, closeTo(50, epsilon)); | |
1292 expect(evaluator.parse('100 / 2 / 2').value, closeTo(25, epsilon)); | |
1293 expect(evaluator.parse('100 / 2 / 2 / 5').value, closeTo(5, epsilon)); | |
1294 expect(evaluator.parse('100 / 2 / 2 / 5 / 5').value, closeTo(1, epsilon)); | |
1295 }); | |
1296 test('mul parse', () { | |
1297 expect(parser.parse('1 / 2 / 3').value, [[1, '/', 2], '/', 3]); | |
1298 }); | |
1299 test('pow', () { | |
1300 expect(evaluator.parse('2 ^ 3').value, closeTo(8, epsilon)); | |
1301 expect(evaluator.parse('-2 ^ 3').value, closeTo(-8, epsilon)); | |
1302 expect(evaluator.parse('-2 ^ -3').value, closeTo(-0.125, epsilon)); | |
1303 }); | |
1304 test('pow many', () { | |
1305 expect(evaluator.parse('4 ^ 3').value, closeTo(64, epsilon)); | |
1306 expect(evaluator.parse('4 ^ 3 ^ 2').value, closeTo(262144, epsilon)); | |
1307 expect(evaluator.parse('4 ^ 3 ^ 2 ^ 1').value, closeTo(262144, epsilon)); | |
1308 expect(evaluator.parse('4 ^ 3 ^ 2 ^ 1 ^ 0').value, closeTo(262144, epsilon
)); | |
1309 }); | |
1310 test('pow parse', () { | |
1311 expect(parser.parse('1 ^ 2 ^ 3').value, [1, '^', [2, '^', 3]]); | |
1312 }); | |
1313 test('parens', () { | |
1314 expect(evaluator.parse('(1)').value, closeTo(1, epsilon)); | |
1315 expect(evaluator.parse('(1 + 2)').value, closeTo(3, epsilon)); | |
1316 expect(evaluator.parse('((1))').value, closeTo(1, epsilon)); | |
1317 expect(evaluator.parse('((1 + 2))').value, closeTo(3, epsilon)); | |
1318 expect(evaluator.parse('2 * (3 + 4)').value, closeTo(14, epsilon)); | |
1319 expect(evaluator.parse('(2 + 3) * 4').value, closeTo(20, epsilon)); | |
1320 expect(evaluator.parse('6 / (2 + 4)').value, closeTo(1, epsilon)); | |
1321 expect(evaluator.parse('(2 + 6) / 2').value, closeTo(4, epsilon)); | |
1322 }); | |
1323 test('priority', () { | |
1324 expect(evaluator.parse('2 * 3 + 4').value, closeTo(10, epsilon)); | |
1325 expect(evaluator.parse('2 + 3 * 4').value, closeTo(14, epsilon)); | |
1326 expect(evaluator.parse('6 / 3 + 4').value, closeTo(6, epsilon)); | |
1327 expect(evaluator.parse('2 + 6 / 2').value, closeTo(5, epsilon)); | |
1328 }); | |
1329 test('priority parse', () { | |
1330 expect(parser.parse('2 * 3 + 4').value, [[2.0, '*', 3.0], '+', 4.0]); | |
1331 expect(parser.parse('2 + 3 * 4').value, [2.0, '+', [3.0, '*', 4.0]]); | |
1332 }); | |
1333 test('postfix add', () { | |
1334 expect(evaluator.parse('0++').value, closeTo(1, epsilon)); | |
1335 expect(evaluator.parse('0++++').value, closeTo(2, epsilon)); | |
1336 expect(evaluator.parse('0++++++').value, closeTo(3, epsilon)); | |
1337 expect(evaluator.parse('0+++1').value, closeTo(2, epsilon)); | |
1338 expect(evaluator.parse('0+++++1').value, closeTo(3, epsilon)); | |
1339 expect(evaluator.parse('0+++++++1').value, closeTo(4, epsilon)); | |
1340 }); | |
1341 test('postfix sub', () { | |
1342 expect(evaluator.parse('1--').value, closeTo(0, epsilon)); | |
1343 expect(evaluator.parse('2----').value, closeTo(0, epsilon)); | |
1344 expect(evaluator.parse('3------').value, closeTo(0, epsilon)); | |
1345 expect(evaluator.parse('2---1').value, closeTo(0, epsilon)); | |
1346 expect(evaluator.parse('3-----1').value, closeTo(0, epsilon)); | |
1347 expect(evaluator.parse('4-------1').value, closeTo(0, epsilon)); | |
1348 }); | |
1349 test('prefix negate', () { | |
1350 expect(evaluator.parse('1').value, closeTo(1, epsilon)); | |
1351 expect(evaluator.parse('-1').value, closeTo(-1, epsilon)); | |
1352 expect(evaluator.parse('--1').value, closeTo(1, epsilon)); | |
1353 expect(evaluator.parse('---1').value, closeTo(-1, epsilon)); | |
1354 }); | |
1355 }); | |
1356 group('tutorial', () { | |
1357 test('simple grammar', () { | |
1358 var id = letter().seq(letter().or(digit()).star()); | |
1359 var id1 = id.parse('yeah'); | |
1360 var id2 = id.parse('f12'); | |
1361 expect(id1.value, ['y', ['e', 'a', 'h']]); | |
1362 expect(id2.value, ['f', ['1', '2']]); | |
1363 var id3 = id.parse('123'); | |
1364 expect(id3.message, 'letter expected'); | |
1365 expect(id3.position, 0); | |
1366 expect(id.accept('foo'), isTrue); | |
1367 expect(id.accept('123'), isFalse); | |
1368 }); | |
1369 test('different parsers', () { | |
1370 var id = letter().seq(word().star()).flatten(); | |
1371 var matches = id.matchesSkipping('foo 123 bar4'); | |
1372 expect(matches, ['foo', 'bar4']); | |
1373 }); | |
1374 test('complicated grammar', () { | |
1375 var number = digit().plus().flatten().trim().map(int.parse); | |
1376 var term = undefined(); | |
1377 var prod = undefined(); | |
1378 var prim = undefined(); | |
1379 term.set(prod.seq(char('+').trim()).seq(term).map((values) { | |
1380 return values[0] + values[2]; | |
1381 }).or(prod)); | |
1382 prod.set(prim.seq(char('*').trim()).seq(prod).map((values) { | |
1383 return values[0] * values[2]; | |
1384 }).or(prim)); | |
1385 prim.set(char('(').trim().seq(term).seq(char(')'.trim())).map((values) { | |
1386 return values[1]; | |
1387 }).or(number)); | |
1388 var start = term.end(); | |
1389 expect(7, start.parse('1 + 2 * 3').value); | |
1390 expect(9, start.parse('(1 + 2) * 3').value); | |
1391 }); | |
1392 }); | |
1393 group('composite (deprecated)', () { | |
1394 test('start', () { | |
1395 var parser = new PluggableCompositeParser((self) { | |
1396 self.def('start', char('a')); | |
1397 }); | |
1398 expectSuccess(parser, 'a', 'a', 1); | |
1399 expectFailure(parser, 'b', 0, '"a" expected'); | |
1400 expectFailure(parser, ''); | |
1401 }); | |
1402 test('circular', () { | |
1403 var parser = new PluggableCompositeParser((self) { | |
1404 self.def('start', self.ref('loop').or(char('b'))); | |
1405 self.def('loop', char('a').seq(self.ref('start'))); | |
1406 }); | |
1407 expect(parser.accept('b'), isTrue); | |
1408 expect(parser.accept('ab'), isTrue); | |
1409 expect(parser.accept('aab'), isTrue); | |
1410 expect(parser.accept('aaab'), isTrue); | |
1411 }); | |
1412 test('redefine parser', () { | |
1413 var parser = new PluggableCompositeParser((self) { | |
1414 self.def('start', char('b')); | |
1415 self.redef('start', char('a')); | |
1416 }); | |
1417 expectSuccess(parser, 'a', 'a', 1); | |
1418 expectFailure(parser, 'b', 0, '"a" expected'); | |
1419 expectFailure(parser, ''); | |
1420 }); | |
1421 test('redefine function', () { | |
1422 var parser = new PluggableCompositeParser((self) { | |
1423 var b = char('b'); | |
1424 self.def('start', b); | |
1425 self.redef('start', (old) { | |
1426 expect(b, old); | |
1427 return char('a'); | |
1428 }); | |
1429 }); | |
1430 expectSuccess(parser, 'a', 'a', 1); | |
1431 expectFailure(parser, 'b', 0, '"a" expected'); | |
1432 expectFailure(parser, ''); | |
1433 }); | |
1434 test('define completed', () { | |
1435 var parser = new PluggableCompositeParser((self) { | |
1436 self.def('start', char('a')); | |
1437 }); | |
1438 expect(() => parser.def('other', char('b')), throws); | |
1439 expect(() => parser.redef('start', char('b')), throws); | |
1440 expect(() => parser.action('start', (each) => each), throws); | |
1441 }); | |
1442 test('reference completed', () { | |
1443 var parsers = { | |
1444 'start': char('a'), | |
1445 'for_b': char('b'), | |
1446 'for_c': char('c') | |
1447 }; | |
1448 var parser = new PluggableCompositeParser((self) { | |
1449 for (var key in parsers.keys) { | |
1450 self.def(key, parsers[key]); | |
1451 } | |
1452 }); | |
1453 for (var key in parsers.keys) { | |
1454 expect(parsers[key], parser[key]); | |
1455 expect(parsers[key], parser.ref(key)); | |
1456 } | |
1457 }); | |
1458 test('reference unknown', () { | |
1459 var parser = new PluggableCompositeParser((self) { | |
1460 self.def('start', char('a')); | |
1461 }); | |
1462 try { | |
1463 parser.ref('star1'); | |
1464 fail('Expected UndefinedProductionError to be thrown'); | |
1465 } on UndefinedProductionError catch (error) { | |
1466 expect(error.toString(), 'Undefined production: star1'); | |
1467 } | |
1468 }); | |
1469 test('duplicated start', () { | |
1470 new PluggableCompositeParser((self) { | |
1471 self.def('start', char('a')); | |
1472 try { | |
1473 self.def('start', char('b')); | |
1474 fail('Expected UndefinedProductionError to be thrown'); | |
1475 } on RedefinedProductionError catch (error) { | |
1476 expect(error.toString(), 'Redefined production: start'); | |
1477 } | |
1478 }); | |
1479 }); | |
1480 test('undefined start', () { | |
1481 expect(() => new PluggableCompositeParser((self) {}), throws); | |
1482 }); | |
1483 test('undefined redef', () { | |
1484 new PluggableCompositeParser((self) { | |
1485 self.def('start', char('a')); | |
1486 expect(() => self.redef('star1', char('b')), throws); | |
1487 }); | |
1488 }); | |
1489 test('example (lambda)', () { | |
1490 var parser = new PluggableCompositeParser((self) { | |
1491 self.def('start', self.ref('expression').end()); | |
1492 self.def('variable', letter().seq(word().star()).flatten().trim()); | |
1493 self.def('expression', self | |
1494 .ref('variable') | |
1495 .or(self.ref('abstraction')) | |
1496 .or(self.ref('application'))); | |
1497 self.def('abstraction', char('\\') | |
1498 .trim() | |
1499 .seq(self.ref('variable')) | |
1500 .seq(char('.').trim()) | |
1501 .seq(self.ref('expression'))); | |
1502 self.def('application', char('(') | |
1503 .trim() | |
1504 .seq(self.ref('expression')) | |
1505 .seq(self.ref('expression')) | |
1506 .seq(char(')').trim())); | |
1507 }); | |
1508 expect(parser.accept('x'), isTrue); | |
1509 expect(parser.accept('xy'), isTrue); | |
1510 expect(parser.accept('x12'), isTrue); | |
1511 expect(parser.accept('\\x.y'), isTrue); | |
1512 expect(parser.accept('\\x.\\y.z'), isTrue); | |
1513 expect(parser.accept('(x x)'), isTrue); | |
1514 expect(parser.accept('(x y)'), isTrue); | |
1515 expect(parser.accept('(x (y z))'), isTrue); | |
1516 expect(parser.accept('((x y) z)'), isTrue); | |
1517 }); | |
1518 test('example (expression)', () { | |
1519 var parser = new PluggableCompositeParser((self) { | |
1520 self.def('start', self.ref('terms').end()); | |
1521 self.def('terms', self.ref('addition').or(self.ref('factors'))); | |
1522 self.def('addition', | |
1523 self.ref('factors').separatedBy(char('+').or(char('-')).trim())); | |
1524 self.def('factors', self.ref('multiplication').or(self.ref('power'))); | |
1525 self.def('multiplication', | |
1526 self.ref('power').separatedBy(char('*').or(char('/')).trim())); | |
1527 self.def('power', self.ref('primary').separatedBy(char('^').trim())); | |
1528 self.def('primary', self.ref('number').or(self.ref('parentheses'))); | |
1529 self.def('number', char('-') | |
1530 .optional() | |
1531 .seq(digit().plus()) | |
1532 .seq(char('.').seq(digit().plus()).optional()) | |
1533 .flatten() | |
1534 .trim()); | |
1535 self.def('parentheses', | |
1536 char('(').trim().seq(self.ref('terms')).seq(char(')').trim())); | |
1537 }); | |
1538 expect(parser.accept('1'), isTrue); | |
1539 expect(parser.accept('12'), isTrue); | |
1540 expect(parser.accept('1.23'), isTrue); | |
1541 expect(parser.accept('-12.3'), isTrue); | |
1542 expect(parser.accept('1 + 2'), isTrue); | |
1543 expect(parser.accept('1 + 2 + 3'), isTrue); | |
1544 expect(parser.accept('1 - 2'), isTrue); | |
1545 expect(parser.accept('1 - 2 - 3'), isTrue); | |
1546 expect(parser.accept('1 * 2'), isTrue); | |
1547 expect(parser.accept('1 * 2 * 3'), isTrue); | |
1548 expect(parser.accept('1 / 2'), isTrue); | |
1549 expect(parser.accept('1 / 2 / 3'), isTrue); | |
1550 expect(parser.accept('1 ^ 2'), isTrue); | |
1551 expect(parser.accept('1 ^ 2 ^ 3'), isTrue); | |
1552 expect(parser.accept('1 + (2 * 3)'), isTrue); | |
1553 expect(parser.accept('(1 + 2) * 3'), isTrue); | |
1554 }); | |
1555 }); | |
1556 } | |
OLD | NEW |