Coverage for src/renaissance/syntax_tree/ast_shower.py: 95%

41 statements  

« prev     ^ index     » next       coverage.py v7.16.1, created at 2026-10-02 13:12 +0000

1"""AI: Render an AST node tree to the console for debugging and inspection.""" 

2 

3import io 

4from collections.abc import Sequence 

5from io import StringIO 

6from pathlib import Path 

7from typing import Protocol, Self, runtime_checkable 

8 

9from termcolor import colored 

10 

11from renaissance.utils.ast_utils import display_context 

12 

13 

14@runtime_checkable 

15class Displayable(Protocol): 

16 """AI: Structural protocol describing the shape required for rendering a node via ASTShower.""" 

17 

18 parser_kind: str 

19 semantic_kind: object 

20 children: list[Self] 

21 is_implicit: bool 

22 show_props: bool 

23 

24 

25class ASTShower: 

26 """AI: Render an AST node tree to the console for debugging and inspection.""" 

27 

28 focus: str = "NO-FOCUS-DEFINED" 

29 

30 @staticmethod 

31 def show_node(node, include_properties: bool = False, display_parser_kind: bool = False) -> None: 

32 """AI: Print node's rendered tree to the console.""" 

33 print("\n" + ASTShower.get_node(node, include_properties, display_parser_kind)) 

34 

35 @staticmethod 

36 def show_nodes(ast_nodes: Sequence, include_properties: bool = False, display_parser_kind: bool = False) -> None: 

37 """AI: Print each node's rendered tree to the console.""" 

38 for ast_node in ast_nodes: 

39 ASTShower.show_node(ast_node, include_properties, display_parser_kind) 

40 

41 @staticmethod 

42 def get_node(ast_node: Displayable, include_properties: bool = False, display_parser_kind: bool = False) -> str: 

43 """AI: Return the rendered tree text for ast_node, or an empty string if it is not Displayable.""" 

44 if isinstance(ast_node, Displayable): 

45 buffer = io.StringIO() 

46 with display_context(display_parser_kind): 

47 ASTShower._process_node(buffer, "", ast_node, include_properties) 

48 return buffer.getvalue() 

49 return "" 

50 

51 @staticmethod 

52 def store_node(filename: str, ast_node: Displayable, include_properties: bool = False, display_parser_kind: bool = False) -> None: 

53 """AI: Write ast_node's rendered tree text to the file named filename.""" 

54 with Path(filename).open("w") as f: 

55 f.write(ASTShower.get_node(ast_node, include_properties, display_parser_kind)) 

56 

57 @staticmethod 

58 def _process_node(output: StringIO, indent: str, node: Displayable, include_properties: bool) -> None: 

59 if node.is_implicit: 

60 node.indent = indent 

61 node.show_props = include_properties 

62 raw = str(node) 

63 raw = raw.replace(ASTShower.focus, colored(ASTShower.focus, "red", attrs=["bold"])) 

64 output.write(raw) 

65 if node.children: 

66 for child in node.children: 

67 ASTShower._process_node(output, indent + " ", child, include_properties)