Fallback parser: scan for cell boundaries, parse each cell individually. Called when ast.parse() on the full file fails due to syntax errors. Returns a tuple of (nodes, scanner_generated_lines) where scanner_generated_lines contains the 1-indexed start line numbers of unparsable cel
(
source: str, filepath: str
)
| 590 | |
| 591 | |
| 592 | def scan_parse_fallback( |
| 593 | source: str, filepath: str |
| 594 | ) -> tuple[list[ast.stmt], frozenset[int]]: |
| 595 | """Fallback parser: scan for cell boundaries, parse each cell individually. |
| 596 | |
| 597 | Called when ast.parse() on the full file fails due to syntax errors. |
| 598 | Returns a tuple of (nodes, scanner_generated_lines) where |
| 599 | scanner_generated_lines contains the 1-indexed start line numbers of |
| 600 | unparsable cells created by the scanner (vs. pre-existing |
| 601 | app._unparsable_cell() calls in the source). |
| 602 | Returns ([], frozenset()) if no cell boundaries are found. |
| 603 | """ |
| 604 | from marimo._ast.parse import ast_parse |
| 605 | |
| 606 | if not _has_cell_boundaries(source): |
| 607 | return [], frozenset() |
| 608 | |
| 609 | scan = scan_notebook(source) |
| 610 | |
| 611 | nodes: list[ast.stmt] = [] |
| 612 | scanner_lines: set[int] = set() |
| 613 | |
| 614 | # Preamble |
| 615 | if scan.preamble.strip(): |
| 616 | try: |
| 617 | tree = ast_parse(scan.preamble, filename=filepath) |
| 618 | nodes.extend(tree.body) |
| 619 | except SyntaxError: |
| 620 | raise # Preamble errors are fatal |
| 621 | |
| 622 | # Cells |
| 623 | for cell in scan.cells: |
| 624 | try: |
| 625 | cell_tree = ast_parse(cell.source, filename=filepath) |
| 626 | ast.increment_lineno(cell_tree, cell.start_line - 1) |
| 627 | nodes.extend(cell_tree.body) |
| 628 | except SyntaxError: |
| 629 | inner_code = _extract_body_code(cell.source, cell.kind) |
| 630 | node = _build_unparsable_node( |
| 631 | inner_code, cell.name, cell.start_line |
| 632 | ) |
| 633 | scanner_lines.add(node.lineno) |
| 634 | nodes.append(node) |
| 635 | |
| 636 | # Run guard |
| 637 | if scan.run_guard_line is not None: |
| 638 | nodes.append(_build_run_guard_node(scan.run_guard_line)) |
| 639 | |
| 640 | return nodes, frozenset(scanner_lines) |
searching dependent graphs…