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 |