|
26 | 26 | from hyperbase.hyperedge import Atom, Hyperedge |
27 | 27 | from hyperbase.parsers import Parser, get_parser, list_parsers |
28 | 28 | from hyperbase.parsers.badness import badness_check |
| 29 | +from hyperbase.parsers.parse_result import ParseResult |
29 | 30 | from hyperbase.parsers.repl_api import ( |
30 | 31 | CommandHandler, |
31 | 32 | PostResultHook, |
|
70 | 71 | "default": 20, |
71 | 72 | "description": "Number of results shown per page in paginated commands.", |
72 | 73 | }, |
| 74 | + "save_parses_to": { |
| 75 | + "type": str, |
| 76 | + "default": None, |
| 77 | + "description": "Path to a .jsonl file where /save-parse appends parse results.", |
| 78 | + "is_path": True, |
| 79 | + }, |
73 | 80 | } |
74 | 81 |
|
75 | 82 |
|
@@ -328,6 +335,9 @@ def __init__(self, parser_name: str | None, settings: dict[str, Any]) -> None: |
328 | 335 | self.edges: list[Hyperedge] = [] |
329 | 336 | self.edges_source: Path | None = None |
330 | 337 |
|
| 338 | + # Last result of parse_text(), exposed for /save-parse. |
| 339 | + self.last_parse_result: list[ParseResult] | None = None |
| 340 | + |
331 | 341 | # Per-parser registrations -- everything in these collections is |
332 | 342 | # cleared and re-installed whenever the active parser changes. |
333 | 343 | self._extra_settings: dict[str, dict[str, Any]] = {} |
@@ -383,6 +393,10 @@ def __init__(self, parser_name: str | None, settings: dict[str, Any]) -> None: |
383 | 393 | "help": "Save in-memory edges to a .jsonl file", |
384 | 394 | "handler": self.cmd_save, |
385 | 395 | }, |
| 396 | + "save-parse": { |
| 397 | + "help": "Append the last parse results to the file in save_parses_to", |
| 398 | + "handler": self.cmd_save_parse, |
| 399 | + }, |
386 | 400 | "edges": { |
387 | 401 | "help": "Show in-memory edges (count and source file)", |
388 | 402 | "handler": self.cmd_edges, |
@@ -597,6 +611,9 @@ def _all_commands(self) -> dict[str, dict[str, Any]]: |
597 | 611 |
|
598 | 612 | def _path_settings(self) -> frozenset[str]: |
599 | 613 | names: set[str] = set() |
| 614 | + for name, info in BUILTIN_REPL_SETTINGS.items(): |
| 615 | + if info.get("is_path"): |
| 616 | + names.add(name) |
600 | 617 | if self.parser is not None: |
601 | 618 | for name, info in type(self.parser).accepted_params().items(): |
602 | 619 | if info.get("is_path"): |
@@ -909,6 +926,37 @@ def cmd_save(self, args: list) -> bool: |
909 | 926 | ) |
910 | 927 | return False |
911 | 928 |
|
| 929 | + def cmd_save_parse(self, args: list) -> bool: |
| 930 | + path_str = self.settings.get("save_parses_to") |
| 931 | + if not path_str: |
| 932 | + self.console.print( |
| 933 | + "[red]Error:[/red] no destination file set. " |
| 934 | + "Use [cyan]/set save_parses_to <path>[/cyan] first." |
| 935 | + ) |
| 936 | + return False |
| 937 | + if not self.last_parse_result: |
| 938 | + self.console.print( |
| 939 | + "[yellow]No parse to save.[/yellow] " |
| 940 | + "[dim]Parse some text first, then run /save-parse.[/dim]" |
| 941 | + ) |
| 942 | + return False |
| 943 | + |
| 944 | + path = Path(path_str).expanduser() |
| 945 | + try: |
| 946 | + with open(path, "a") as f: |
| 947 | + for result in self.last_parse_result: |
| 948 | + f.write(result.to_json() + "\n") |
| 949 | + except OSError as e: |
| 950 | + self.console.print(f"[red]Error:[/red] failed to write {path}: {e}") |
| 951 | + return False |
| 952 | + |
| 953 | + n = len(self.last_parse_result) |
| 954 | + self.console.print( |
| 955 | + f"[green]✓[/green] Appended [cyan]{n}[/cyan] parse result(s) " |
| 956 | + f"to [cyan]{path}[/cyan]" |
| 957 | + ) |
| 958 | + return False |
| 959 | + |
912 | 960 | def _build_tok_pos_tree(self, edge: Hyperedge) -> Hyperedge: |
913 | 961 | """Build a tok_pos mirror tree where each atom is replaced by its |
914 | 962 | ``tok_pos`` (or ``-1`` for synthetic atoms with no position).""" |
@@ -1595,8 +1643,6 @@ def _render_count_row(self, n: int, key: object, count: int) -> None: |
1595 | 1643 | self.console.print() |
1596 | 1644 |
|
1597 | 1645 | def _load_edges_from_jsonl(self, path: Path) -> tuple[list[Hyperedge], int]: |
1598 | | - from hyperbase.parsers.parse_result import ParseResult |
1599 | | - |
1600 | 1646 | edges: list[Hyperedge] = [] |
1601 | 1647 | skipped = 0 |
1602 | 1648 | with open(path) as f: |
@@ -1681,6 +1727,7 @@ def parse_text(self, text: str) -> None: |
1681 | 1727 | start_time = time.perf_counter() |
1682 | 1728 | parse_result = self.parser.parse(text) |
1683 | 1729 | elapsed_time = time.perf_counter() - start_time |
| 1730 | + self.last_parse_result = parse_result |
1684 | 1731 |
|
1685 | 1732 | if not parse_result: |
1686 | 1733 | self.console.print() |
|
0 commit comments