Skip to content

Base printer

base_printer

BasePrinter dataclass

Source code in xdsl/utils/base_printer.py
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
@dataclass(eq=False, repr=False)
class BasePrinter:
    stream: IO[str] | None = field(default=None)
    indent_num_spaces: int = field(default=2, kw_only=True)
    _indent: int = field(default=0, init=False)
    _current_line: int = field(default=0, init=False)
    _current_column: int = field(default=0, init=False)

    _next_line_callback: list[Callable[[], None]] = field(
        default_factory=list[Callable[[], None]], init=False
    )

    def print_string(self, text: str, *, indent: int | None = None) -> None:
        """
        Prints a string to the printer's output.

        This function takes into account indentation level when
        printing new lines.
        If the indentation level is specified as 0, the string is printed as-is, if `None`
        then the `Printer` instance's indentation level is used.
        """

        num_newlines = text.count("\n")

        if not num_newlines:
            self._current_column += len(text)
            print(text, end="", file=self.stream)
            return

        indent = self._indent if indent is None else indent
        lines = text.split("\n")

        if indent == 0 and not self._next_line_callback:
            # No indent and no callback to print after the next newline, the text
            # can be printed directly.
            self._current_line += num_newlines
            self._current_column = len(lines[-1])
            print(text, end="", file=self.stream)
            return

        # Line and column information is not computed ahead of time
        # as indent-aware newline printing may use it as part of
        # callbacks.
        print(lines[0], end="", file=self.stream)
        self._current_column += len(lines[0])
        for line in lines[1:]:
            self._print_new_line(indent=indent)
            print(line, end="", file=self.stream)
            self._current_column += len(line)

    T = TypeVar("T")

    def print_list(
        self, elems: Iterable[T], print_fn: Callable[[T], Any], delimiter: str = ", "
    ) -> None:
        for i, elem in enumerate(elems):
            if i:
                self.print_string(delimiter)
            print_fn(elem)

    @contextmanager
    def delimited(self, start: str, end: str):
        self.print_string(start)
        yield
        self.print_string(end)

    def in_angle_brackets(self):
        return self.delimited("<", ">")

    def in_braces(self):
        return self.delimited("{", "}")

    def in_parens(self):
        return self.delimited("(", ")")

    def in_square_brackets(self):
        return self.delimited("[", "]")

    def _print_new_line(
        self, indent: int | None = None, print_message: bool = True
    ) -> None:
        indent = self._indent if indent is None else indent
        # Prints a newline, bypassing the `print_string` method
        print(file=self.stream)
        self._current_line += 1
        if print_message:
            for callback in self._next_line_callback:
                callback()
            self._next_line_callback = []
        num_spaces = indent * self.indent_num_spaces
        # Prints indentation, bypassing the `print_string` method
        print(" " * num_spaces, end="", file=self.stream)
        self._current_column = num_spaces

    @contextmanager
    def indented(self, amount: int = 1):
        """
        Increases the indentation level by the provided amount
        for the duration of the context.

        Only affects new lines printed within the context.
        """

        self._indent += amount
        try:
            yield
        finally:
            self._indent -= amount

    def _add_message_on_next_line(self, message: str, begin_pos: int, end_pos: int):
        """Add a message that will be displayed on the next line."""

        def callback(indent: int = self._indent):
            self._print_message(message, begin_pos, end_pos, indent)

        self._next_line_callback.append(callback)

    def _print_message(
        self, message: str, begin_pos: int, end_pos: int, indent: int | None = None
    ):
        """
        Print a message.
        This is expected to be called at the beginning of a new line and to create a new
        line at the end.
        The span of the message to be underlined is represented as [begin_pos, end_pos).
        """
        indent = self._indent if indent is None else indent
        indent_size = indent * self.indent_num_spaces
        self.print_string(" " * indent_size)
        message_end_pos = max(map(len, message.split("\n"))) + indent_size + 2
        first_line = (
            (begin_pos - indent_size) * "-"
            + (end_pos - begin_pos) * "^"
            + (max(message_end_pos, end_pos) - end_pos) * "-"
        )
        self.print_string(first_line)
        self._print_new_line(indent=indent, print_message=False)
        for message_line in message.split("\n"):
            self.print_string("| ")
            self.print_string(message_line)
            self._print_new_line(indent=indent, print_message=False)
        self.print_string("-" * (max(message_end_pos, end_pos) - indent_size))
        self._print_new_line(indent=0, print_message=False)

stream: IO[str] | None = field(default=None) class-attribute instance-attribute

indent_num_spaces: int = field(default=2, kw_only=True) class-attribute instance-attribute

T = TypeVar('T') class-attribute instance-attribute

__init__(stream: IO[str] | None = None, *, indent_num_spaces: int = 2) -> None

print_string(text: str, *, indent: int | None = None) -> None

Prints a string to the printer's output.

This function takes into account indentation level when printing new lines. If the indentation level is specified as 0, the string is printed as-is, if None then the Printer instance's indentation level is used.

Source code in xdsl/utils/base_printer.py
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
def print_string(self, text: str, *, indent: int | None = None) -> None:
    """
    Prints a string to the printer's output.

    This function takes into account indentation level when
    printing new lines.
    If the indentation level is specified as 0, the string is printed as-is, if `None`
    then the `Printer` instance's indentation level is used.
    """

    num_newlines = text.count("\n")

    if not num_newlines:
        self._current_column += len(text)
        print(text, end="", file=self.stream)
        return

    indent = self._indent if indent is None else indent
    lines = text.split("\n")

    if indent == 0 and not self._next_line_callback:
        # No indent and no callback to print after the next newline, the text
        # can be printed directly.
        self._current_line += num_newlines
        self._current_column = len(lines[-1])
        print(text, end="", file=self.stream)
        return

    # Line and column information is not computed ahead of time
    # as indent-aware newline printing may use it as part of
    # callbacks.
    print(lines[0], end="", file=self.stream)
    self._current_column += len(lines[0])
    for line in lines[1:]:
        self._print_new_line(indent=indent)
        print(line, end="", file=self.stream)
        self._current_column += len(line)

print_list(elems: Iterable[T], print_fn: Callable[[T], Any], delimiter: str = ', ') -> None

Source code in xdsl/utils/base_printer.py
63
64
65
66
67
68
69
def print_list(
    self, elems: Iterable[T], print_fn: Callable[[T], Any], delimiter: str = ", "
) -> None:
    for i, elem in enumerate(elems):
        if i:
            self.print_string(delimiter)
        print_fn(elem)

delimited(start: str, end: str)

Source code in xdsl/utils/base_printer.py
71
72
73
74
75
@contextmanager
def delimited(self, start: str, end: str):
    self.print_string(start)
    yield
    self.print_string(end)

in_angle_brackets()

Source code in xdsl/utils/base_printer.py
77
78
def in_angle_brackets(self):
    return self.delimited("<", ">")

in_braces()

Source code in xdsl/utils/base_printer.py
80
81
def in_braces(self):
    return self.delimited("{", "}")

in_parens()

Source code in xdsl/utils/base_printer.py
83
84
def in_parens(self):
    return self.delimited("(", ")")

in_square_brackets()

Source code in xdsl/utils/base_printer.py
86
87
def in_square_brackets(self):
    return self.delimited("[", "]")

indented(amount: int = 1)

Increases the indentation level by the provided amount for the duration of the context.

Only affects new lines printed within the context.

Source code in xdsl/utils/base_printer.py
105
106
107
108
109
110
111
112
113
114
115
116
117
118
@contextmanager
def indented(self, amount: int = 1):
    """
    Increases the indentation level by the provided amount
    for the duration of the context.

    Only affects new lines printed within the context.
    """

    self._indent += amount
    try:
        yield
    finally:
        self._indent -= amount