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

41 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-15 10:34 +0000

1# Copyright (C) 2024-2026 TNO 

2# 

3# Licensed under the terms specified in the LICENSE file in the root directory 

4# of this repository, also available at: 

5# https://github.com/TNO/Renaissance.Py/blob/main/LICENSE 

6 

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

8 

9import io 

10from collections.abc import Sequence 

11from io import StringIO 

12from pathlib import Path 

13from typing import Protocol, Self, runtime_checkable 

14 

15from termcolor import colored 

16 

17from renaissance.utils.ast_utils import display_context 

18 

19 

20@runtime_checkable 

21class Displayable(Protocol): 

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

23 

24 parser_kind: str 

25 semantic_kind: object 

26 children: list[Self] 

27 is_implicit: bool 

28 show_props: bool 

29 

30 

31class ASTShower: 

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

33 

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

35 

36 @staticmethod 

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

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

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

40 

41 @staticmethod 

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

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

44 for ast_node in ast_nodes: 

45 ASTShower.show_node(ast_node, include_properties, display_parser_kind) 

46 

47 @staticmethod 

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

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

50 if isinstance(ast_node, Displayable): 

51 buffer = io.StringIO() 

52 with display_context(display_parser_kind): 

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

54 return buffer.getvalue() 

55 return "" 

56 

57 @staticmethod 

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

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

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

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

62 

63 @staticmethod 

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

65 if node.is_implicit: 

66 node.indent = indent 

67 node.show_props = include_properties 

68 raw = str(node) 

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

70 output.write(raw) 

71 if node.children: 

72 for child in node.children: 

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