(self)
| 426 | ) |
| 427 | |
| 428 | def test_left_recursive(self) -> None: |
| 429 | grammar_source = """ |
| 430 | start: expr NEWLINE |
| 431 | expr: ('-' term | expr '+' term | term) |
| 432 | term: NUMBER |
| 433 | foo: NAME+ |
| 434 | bar: NAME* |
| 435 | baz: NAME? |
| 436 | """ |
| 437 | grammar: Grammar = parse_string(grammar_source, GrammarParser) |
| 438 | parser_class = generate_parser(grammar) |
| 439 | rules = grammar.rules |
| 440 | self.assertFalse(rules["start"].left_recursive) |
| 441 | self.assertTrue(rules["expr"].left_recursive) |
| 442 | self.assertFalse(rules["term"].left_recursive) |
| 443 | self.assertFalse(rules["foo"].left_recursive) |
| 444 | self.assertFalse(rules["bar"].left_recursive) |
| 445 | self.assertFalse(rules["baz"].left_recursive) |
| 446 | node = parse_string("1 + 2 + 3\n", parser_class) |
| 447 | self.assertEqual( |
| 448 | node, |
| 449 | [ |
| 450 | [ |
| 451 | [ |
| 452 | TokenInfo( |
| 453 | NUMBER, |
| 454 | string="1", |
| 455 | start=(1, 0), |
| 456 | end=(1, 1), |
| 457 | line="1 + 2 + 3\n", |
| 458 | ), |
| 459 | TokenInfo( |
| 460 | OP, string="+", start=(1, 2), end=(1, 3), line="1 + 2 + 3\n" |
| 461 | ), |
| 462 | TokenInfo( |
| 463 | NUMBER, |
| 464 | string="2", |
| 465 | start=(1, 4), |
| 466 | end=(1, 5), |
| 467 | line="1 + 2 + 3\n", |
| 468 | ), |
| 469 | ], |
| 470 | TokenInfo( |
| 471 | OP, string="+", start=(1, 6), end=(1, 7), line="1 + 2 + 3\n" |
| 472 | ), |
| 473 | TokenInfo( |
| 474 | NUMBER, string="3", start=(1, 8), end=(1, 9), line="1 + 2 + 3\n" |
| 475 | ), |
| 476 | ], |
| 477 | TokenInfo( |
| 478 | NEWLINE, string="\n", start=(1, 9), end=(1, 10), line="1 + 2 + 3\n" |
| 479 | ), |
| 480 | ], |
| 481 | ) |
| 482 | |
| 483 | def test_python_expr(self) -> None: |
| 484 | grammar = """ |
nothing calls this directly
no test coverage detected