kirin-toolchain 0.13.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (225) hide show
  1. kirin/__init__.py +7 -0
  2. kirin/analysis/__init__.py +24 -0
  3. kirin/analysis/callgraph.py +61 -0
  4. kirin/analysis/cfg.py +112 -0
  5. kirin/analysis/const/__init__.py +20 -0
  6. kirin/analysis/const/_visitor.py +2 -0
  7. kirin/analysis/const/_visitor.pyi +8 -0
  8. kirin/analysis/const/lattice.py +219 -0
  9. kirin/analysis/const/prop.py +116 -0
  10. kirin/analysis/forward.py +100 -0
  11. kirin/analysis/typeinfer/__init__.py +5 -0
  12. kirin/analysis/typeinfer/analysis.py +90 -0
  13. kirin/analysis/typeinfer/solve.py +141 -0
  14. kirin/decl/__init__.py +108 -0
  15. kirin/decl/base.py +65 -0
  16. kirin/decl/camel2snake.py +2 -0
  17. kirin/decl/emit/__init__.py +0 -0
  18. kirin/decl/emit/_create_fn.py +29 -0
  19. kirin/decl/emit/_set_new_attribute.py +22 -0
  20. kirin/decl/emit/dialect.py +8 -0
  21. kirin/decl/emit/init.py +277 -0
  22. kirin/decl/emit/name.py +10 -0
  23. kirin/decl/emit/property.py +182 -0
  24. kirin/decl/emit/repr.py +31 -0
  25. kirin/decl/emit/traits.py +13 -0
  26. kirin/decl/emit/typecheck.py +77 -0
  27. kirin/decl/emit/verify.py +51 -0
  28. kirin/decl/info.py +346 -0
  29. kirin/decl/scan_fields.py +157 -0
  30. kirin/decl/verify.py +69 -0
  31. kirin/dialects/__init__.py +14 -0
  32. kirin/dialects/_pprint_helper.py +53 -0
  33. kirin/dialects/cf/__init__.py +20 -0
  34. kirin/dialects/cf/constprop.py +51 -0
  35. kirin/dialects/cf/dialect.py +3 -0
  36. kirin/dialects/cf/emit.py +58 -0
  37. kirin/dialects/cf/interp.py +24 -0
  38. kirin/dialects/cf/stmts.py +68 -0
  39. kirin/dialects/cf/typeinfer.py +27 -0
  40. kirin/dialects/eltype.py +23 -0
  41. kirin/dialects/func/__init__.py +20 -0
  42. kirin/dialects/func/attrs.py +39 -0
  43. kirin/dialects/func/constprop.py +138 -0
  44. kirin/dialects/func/dialect.py +3 -0
  45. kirin/dialects/func/emit.py +80 -0
  46. kirin/dialects/func/interp.py +68 -0
  47. kirin/dialects/func/stmts.py +233 -0
  48. kirin/dialects/func/typeinfer.py +124 -0
  49. kirin/dialects/ilist/__init__.py +33 -0
  50. kirin/dialects/ilist/_dialect.py +3 -0
  51. kirin/dialects/ilist/_wrapper.py +51 -0
  52. kirin/dialects/ilist/interp.py +85 -0
  53. kirin/dialects/ilist/lowering.py +25 -0
  54. kirin/dialects/ilist/passes.py +32 -0
  55. kirin/dialects/ilist/rewrite/__init__.py +3 -0
  56. kirin/dialects/ilist/rewrite/const.py +45 -0
  57. kirin/dialects/ilist/rewrite/list.py +38 -0
  58. kirin/dialects/ilist/rewrite/unroll.py +131 -0
  59. kirin/dialects/ilist/runtime.py +63 -0
  60. kirin/dialects/ilist/stmts.py +102 -0
  61. kirin/dialects/ilist/typeinfer.py +120 -0
  62. kirin/dialects/lowering/__init__.py +7 -0
  63. kirin/dialects/lowering/call.py +48 -0
  64. kirin/dialects/lowering/cf.py +206 -0
  65. kirin/dialects/lowering/func.py +134 -0
  66. kirin/dialects/math/__init__.py +41 -0
  67. kirin/dialects/math/_gen.py +176 -0
  68. kirin/dialects/math/dialect.py +3 -0
  69. kirin/dialects/math/interp.py +190 -0
  70. kirin/dialects/math/stmts.py +369 -0
  71. kirin/dialects/module.py +139 -0
  72. kirin/dialects/py/__init__.py +40 -0
  73. kirin/dialects/py/assertion.py +91 -0
  74. kirin/dialects/py/assign.py +103 -0
  75. kirin/dialects/py/attr.py +59 -0
  76. kirin/dialects/py/base.py +34 -0
  77. kirin/dialects/py/binop/__init__.py +23 -0
  78. kirin/dialects/py/binop/_dialect.py +3 -0
  79. kirin/dialects/py/binop/interp.py +60 -0
  80. kirin/dialects/py/binop/julia.py +33 -0
  81. kirin/dialects/py/binop/lowering.py +22 -0
  82. kirin/dialects/py/binop/stmts.py +79 -0
  83. kirin/dialects/py/binop/typeinfer.py +108 -0
  84. kirin/dialects/py/boolop.py +84 -0
  85. kirin/dialects/py/builtin.py +78 -0
  86. kirin/dialects/py/cmp/__init__.py +16 -0
  87. kirin/dialects/py/cmp/_dialect.py +3 -0
  88. kirin/dialects/py/cmp/interp.py +48 -0
  89. kirin/dialects/py/cmp/julia.py +33 -0
  90. kirin/dialects/py/cmp/lowering.py +45 -0
  91. kirin/dialects/py/cmp/stmts.py +62 -0
  92. kirin/dialects/py/constant.py +79 -0
  93. kirin/dialects/py/indexing.py +251 -0
  94. kirin/dialects/py/iterable.py +90 -0
  95. kirin/dialects/py/len.py +57 -0
  96. kirin/dialects/py/list/__init__.py +15 -0
  97. kirin/dialects/py/list/_dialect.py +3 -0
  98. kirin/dialects/py/list/interp.py +21 -0
  99. kirin/dialects/py/list/lowering.py +25 -0
  100. kirin/dialects/py/list/stmts.py +22 -0
  101. kirin/dialects/py/list/typeinfer.py +54 -0
  102. kirin/dialects/py/range.py +76 -0
  103. kirin/dialects/py/slice.py +120 -0
  104. kirin/dialects/py/tuple.py +109 -0
  105. kirin/dialects/py/unary/__init__.py +24 -0
  106. kirin/dialects/py/unary/_dialect.py +3 -0
  107. kirin/dialects/py/unary/constprop.py +20 -0
  108. kirin/dialects/py/unary/interp.py +24 -0
  109. kirin/dialects/py/unary/julia.py +21 -0
  110. kirin/dialects/py/unary/lowering.py +22 -0
  111. kirin/dialects/py/unary/stmts.py +33 -0
  112. kirin/dialects/py/unary/typeinfer.py +23 -0
  113. kirin/dialects/py/unpack.py +90 -0
  114. kirin/dialects/scf/__init__.py +23 -0
  115. kirin/dialects/scf/_dialect.py +3 -0
  116. kirin/dialects/scf/absint.py +64 -0
  117. kirin/dialects/scf/constprop.py +140 -0
  118. kirin/dialects/scf/interp.py +35 -0
  119. kirin/dialects/scf/lowering.py +123 -0
  120. kirin/dialects/scf/stmts.py +250 -0
  121. kirin/dialects/scf/trim.py +36 -0
  122. kirin/dialects/scf/typeinfer.py +58 -0
  123. kirin/dialects/scf/unroll.py +92 -0
  124. kirin/emit/__init__.py +3 -0
  125. kirin/emit/abc.py +89 -0
  126. kirin/emit/abc.pyi +38 -0
  127. kirin/emit/exceptions.py +5 -0
  128. kirin/emit/julia.py +63 -0
  129. kirin/emit/str.py +51 -0
  130. kirin/exceptions.py +59 -0
  131. kirin/graph.py +34 -0
  132. kirin/idtable.py +57 -0
  133. kirin/interp/__init__.py +39 -0
  134. kirin/interp/abstract.py +253 -0
  135. kirin/interp/base.py +438 -0
  136. kirin/interp/concrete.py +62 -0
  137. kirin/interp/exceptions.py +26 -0
  138. kirin/interp/frame.py +151 -0
  139. kirin/interp/impl.py +197 -0
  140. kirin/interp/result.py +93 -0
  141. kirin/interp/state.py +71 -0
  142. kirin/interp/table.py +40 -0
  143. kirin/interp/value.py +73 -0
  144. kirin/ir/__init__.py +46 -0
  145. kirin/ir/attrs/__init__.py +20 -0
  146. kirin/ir/attrs/_types.py +8 -0
  147. kirin/ir/attrs/_types.pyi +13 -0
  148. kirin/ir/attrs/abc.py +46 -0
  149. kirin/ir/attrs/py.py +45 -0
  150. kirin/ir/attrs/types.py +522 -0
  151. kirin/ir/dialect.py +125 -0
  152. kirin/ir/group.py +249 -0
  153. kirin/ir/method.py +118 -0
  154. kirin/ir/nodes/__init__.py +7 -0
  155. kirin/ir/nodes/base.py +149 -0
  156. kirin/ir/nodes/block.py +458 -0
  157. kirin/ir/nodes/region.py +337 -0
  158. kirin/ir/nodes/stmt.py +713 -0
  159. kirin/ir/nodes/view.py +142 -0
  160. kirin/ir/ssa.py +204 -0
  161. kirin/ir/traits/__init__.py +36 -0
  162. kirin/ir/traits/abc.py +42 -0
  163. kirin/ir/traits/basic.py +78 -0
  164. kirin/ir/traits/callable.py +51 -0
  165. kirin/ir/traits/lowering/__init__.py +2 -0
  166. kirin/ir/traits/lowering/call.py +37 -0
  167. kirin/ir/traits/lowering/context.py +120 -0
  168. kirin/ir/traits/region/__init__.py +2 -0
  169. kirin/ir/traits/region/ssacfg.py +22 -0
  170. kirin/ir/traits/symbol.py +57 -0
  171. kirin/ir/use.py +17 -0
  172. kirin/lattice/__init__.py +13 -0
  173. kirin/lattice/abc.py +128 -0
  174. kirin/lattice/empty.py +25 -0
  175. kirin/lattice/mixin.py +51 -0
  176. kirin/lowering/__init__.py +7 -0
  177. kirin/lowering/binding.py +65 -0
  178. kirin/lowering/core.py +72 -0
  179. kirin/lowering/dialect.py +35 -0
  180. kirin/lowering/dialect.pyi +183 -0
  181. kirin/lowering/frame.py +171 -0
  182. kirin/lowering/result.py +68 -0
  183. kirin/lowering/state.py +441 -0
  184. kirin/lowering/stream.py +53 -0
  185. kirin/passes/__init__.py +3 -0
  186. kirin/passes/abc.py +44 -0
  187. kirin/passes/aggressive/__init__.py +1 -0
  188. kirin/passes/aggressive/fold.py +43 -0
  189. kirin/passes/fold.py +45 -0
  190. kirin/passes/inline.py +25 -0
  191. kirin/passes/typeinfer.py +25 -0
  192. kirin/prelude.py +197 -0
  193. kirin/print/__init__.py +15 -0
  194. kirin/print/printable.py +141 -0
  195. kirin/print/printer.py +415 -0
  196. kirin/py.typed +0 -0
  197. kirin/registry.py +105 -0
  198. kirin/registry.pyi +52 -0
  199. kirin/rewrite/__init__.py +14 -0
  200. kirin/rewrite/abc.py +43 -0
  201. kirin/rewrite/aggressive/__init__.py +1 -0
  202. kirin/rewrite/aggressive/fold.py +43 -0
  203. kirin/rewrite/alias.py +16 -0
  204. kirin/rewrite/apply_type.py +47 -0
  205. kirin/rewrite/call2invoke.py +34 -0
  206. kirin/rewrite/chain.py +39 -0
  207. kirin/rewrite/compactify.py +288 -0
  208. kirin/rewrite/cse.py +48 -0
  209. kirin/rewrite/dce.py +19 -0
  210. kirin/rewrite/fixpoint.py +34 -0
  211. kirin/rewrite/fold.py +57 -0
  212. kirin/rewrite/getfield.py +21 -0
  213. kirin/rewrite/getitem.py +37 -0
  214. kirin/rewrite/inline.py +143 -0
  215. kirin/rewrite/result.py +15 -0
  216. kirin/rewrite/walk.py +83 -0
  217. kirin/rewrite/wrap_const.py +55 -0
  218. kirin/source.py +21 -0
  219. kirin/symbol_table.py +27 -0
  220. kirin/types.py +34 -0
  221. kirin/worklist.py +30 -0
  222. kirin_toolchain-0.13.0.dist-info/METADATA +42 -0
  223. kirin_toolchain-0.13.0.dist-info/RECORD +225 -0
  224. kirin_toolchain-0.13.0.dist-info/WHEEL +4 -0
  225. kirin_toolchain-0.13.0.dist-info/licenses/LICENSE +234 -0
kirin/prelude.py ADDED
@@ -0,0 +1,197 @@
1
+ """This module contains some common eDSLs that can be used to build
2
+ more complex eDSLs. The eDSLs in this module are built on top of the
3
+ basic dialects provided by the `kirin.dialects` module.
4
+ """
5
+
6
+ from typing_extensions import Doc, Annotated
7
+
8
+ from kirin.ir import Method, dialect_group
9
+ from kirin.passes import aggressive
10
+ from kirin.dialects import cf, scf, func, math, ilist, lowering
11
+ from kirin.dialects.py import (
12
+ cmp,
13
+ len,
14
+ attr,
15
+ base,
16
+ list,
17
+ binop,
18
+ range,
19
+ slice,
20
+ tuple,
21
+ unary,
22
+ assign,
23
+ boolop,
24
+ builtin,
25
+ constant,
26
+ indexing,
27
+ iterable,
28
+ assertion,
29
+ )
30
+ from kirin.passes.fold import Fold
31
+ from kirin.passes.typeinfer import TypeInfer
32
+
33
+
34
+ @dialect_group(
35
+ [
36
+ base,
37
+ binop,
38
+ cmp,
39
+ unary,
40
+ assign,
41
+ attr,
42
+ boolop,
43
+ builtin,
44
+ constant,
45
+ indexing,
46
+ len,
47
+ tuple,
48
+ assertion,
49
+ iterable,
50
+ ]
51
+ )
52
+ def python_basic(self):
53
+ """The basic Python dialect without list, range, and slice."""
54
+
55
+ def run_pass(mt: Method) -> None:
56
+ pass
57
+
58
+ return run_pass
59
+
60
+
61
+ @dialect_group(
62
+ python_basic.union(
63
+ [list, range, slice, cf, func, lowering.cf, lowering.func, lowering.call, math]
64
+ )
65
+ )
66
+ def python_no_opt(self):
67
+ """The Python dialect without optimization passes."""
68
+
69
+ def run_pass(mt: Method) -> None:
70
+ pass
71
+
72
+ return run_pass
73
+
74
+
75
+ @dialect_group(
76
+ python_basic.union(
77
+ [ilist, range, slice, cf, func, math, lowering.cf, lowering.func, lowering.call]
78
+ )
79
+ )
80
+ def basic_no_opt(self):
81
+ """The basic kernel without optimization passes. This is a builtin
82
+ eDSL that includes the basic dialects that are commonly used in
83
+ Python-like eDSLs.
84
+
85
+ This eDSL includes the basic dialects without any optimization passes.
86
+ Other eDSL can usually be built on top of this eDSL by utilizing the
87
+ `basic_no_opt.add` method to add more dialects and optimization passes.
88
+
89
+ Note that unlike Python, list in this eDSL is immutable, and the
90
+ `append` method is not available. Use `+` operator to concatenate lists
91
+ instead. Immutable list is easier to optimize and reason about.
92
+
93
+ See also [`basic`][kirin.prelude.basic] for the basic kernel with optimization passes.
94
+ See also [`ilist`][kirin.dialects.ilist] for the immutable list dialect.
95
+ """
96
+ ilist_desugar = ilist.IListDesugar(self)
97
+
98
+ def run_pass(mt: Method) -> None:
99
+ ilist_desugar.fixpoint(mt)
100
+
101
+ return run_pass
102
+
103
+
104
+ @dialect_group(basic_no_opt)
105
+ def basic(self):
106
+ """The basic kernel.
107
+
108
+ This eDSL includes the basic dialects and the basic optimization passes.
109
+ Other eDSL can usually be built on top of this eDSL by utilizing the
110
+ `basic.add` method to add more dialects and optimization passes.
111
+
112
+ See also [`basic_no_opt`][kirin.prelude.basic_no_opt] for the basic kernel without optimization passes.
113
+
114
+ ## Example
115
+
116
+ ```python
117
+ from kirin.prelude import basic
118
+
119
+ @basic(typeinfer=True)
120
+ def main(x: int) -> int:
121
+ return x + 1 + 1
122
+
123
+ main.print() # main is a Method!
124
+ ```
125
+ """
126
+ fold_pass = Fold(self)
127
+ ilist_desugar = ilist.IListDesugar(self)
128
+ aggressive_fold_pass = aggressive.Fold(self)
129
+ typeinfer_pass = TypeInfer(self)
130
+
131
+ def run_pass(
132
+ mt: Annotated[Method, Doc("The method to run pass on.")],
133
+ *,
134
+ verify: Annotated[
135
+ bool, Doc("run `verify` before running passes, default is `True`")
136
+ ] = True,
137
+ typeinfer: Annotated[
138
+ bool,
139
+ Doc(
140
+ "run type inference and apply the inferred type to IR, default `False`"
141
+ ),
142
+ ] = False,
143
+ fold: Annotated[bool, Doc("run folding passes")] = True,
144
+ aggressive: Annotated[
145
+ bool, Doc("run aggressive folding passes if `fold=True`")
146
+ ] = False,
147
+ ) -> None:
148
+ if verify:
149
+ mt.verify()
150
+
151
+ ilist_desugar.fixpoint(mt)
152
+
153
+ if fold:
154
+ if aggressive:
155
+ aggressive_fold_pass.fixpoint(mt)
156
+ else:
157
+ fold_pass(mt)
158
+
159
+ if typeinfer:
160
+ typeinfer_pass(mt)
161
+
162
+ return run_pass
163
+
164
+
165
+ @dialect_group(
166
+ python_basic.union(
167
+ [ilist, range, slice, scf, cf, func, math, lowering.func, lowering.call]
168
+ )
169
+ )
170
+ def structural_no_opt(self):
171
+ """Structural kernel without optimization passes."""
172
+
173
+ def run_pass(method: Method) -> None:
174
+ pass
175
+
176
+ return run_pass
177
+
178
+
179
+ @dialect_group(
180
+ python_basic.union(
181
+ [ilist, range, slice, scf, cf, func, math, lowering.func, lowering.call]
182
+ )
183
+ )
184
+ def structural(self):
185
+ """Structural kernel without optimization passes."""
186
+ typeinfer_pass = TypeInfer(self)
187
+
188
+ def run_pass(
189
+ method: Method, *, verify: bool = True, typeinfer: bool = True
190
+ ) -> None:
191
+ if verify:
192
+ method.verify()
193
+
194
+ if typeinfer:
195
+ typeinfer_pass(method)
196
+
197
+ return run_pass
@@ -0,0 +1,15 @@
1
+ """Pretty printing utilities.
2
+
3
+ This module provides a pretty printing utility for the IR nodes and other
4
+ objects in the compiler.
5
+
6
+ The pretty printing utility is implemented using the visitor pattern. The
7
+ [`Printable`][kirin.print.Printable] class is the base class for all objects that can be pretty printed.
8
+
9
+ The [`Printer`][kirin.print.Printer] class is the visitor that traverses the object and prints the
10
+ object to a string. The [`Printer`][kirin.print.Printer] class provides methods for printing different
11
+ types of objects.
12
+ """
13
+
14
+ from kirin.print.printer import Printer as Printer
15
+ from kirin.print.printable import Printable as Printable
@@ -0,0 +1,141 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import abstractmethod
4
+ from typing import TYPE_CHECKING
5
+
6
+ # NOTE: we don't want to actually load rich here
7
+ if TYPE_CHECKING:
8
+ from typing import Any, Literal, TypedDict
9
+
10
+ from rich.theme import Theme
11
+ from rich.console import Console
12
+ from typing_extensions import Unpack
13
+
14
+ from kirin import ir
15
+ from kirin.print import Printer
16
+
17
+ class _PrintOptions(TypedDict, total=False):
18
+ console: Console
19
+ hint: str
20
+ analysis: dict["ir.SSAValue", Any]
21
+ show_indent_mark: bool
22
+ theme: Theme | dict | Literal["dark", "light"]
23
+
24
+
25
+ class Printable:
26
+ """Base class for all objects that can be pretty printed.
27
+
28
+ This class provides an interface for pretty printing objects. The
29
+ `print` method is the entry point for the printing process. The
30
+ `print_impl` method is the implementation of the printing process
31
+ and should be implemented by the derived classes.
32
+ """
33
+
34
+ @staticmethod
35
+ def __get_printer(
36
+ printer: Printer | None = None,
37
+ **options: Unpack["_PrintOptions"],
38
+ ) -> Printer:
39
+ if printer is None:
40
+ from kirin.print import Printer
41
+
42
+ return Printer(**options)
43
+ return printer
44
+
45
+ def pager(
46
+ self,
47
+ printer: Printer | None = None,
48
+ **options: Unpack["_PrintOptions"],
49
+ ) -> None:
50
+ """Pretty print the object with a pager.
51
+
52
+ Args:
53
+ printer (Printer):
54
+ `Printer` object to use for printing.
55
+ If None, a new `Printer` object will be created.
56
+
57
+ Keyword Args:
58
+ console (rich.Console):
59
+ The console to use for printing. If not provided, a new console
60
+ will be created.
61
+ analysis (dict[ir.SSAValue, Printable]):
62
+ Analysis results to use for printing. If `None`, no analysis results
63
+ show_indent_mark (bool):
64
+ Whether to show the indentation mark.
65
+ theme (Theme | dict | str):
66
+ The theme to use for printing, defaults to "dark".
67
+
68
+ !!! note
69
+ This function also accepts all other `rich.console.Console` options.
70
+ """
71
+ printer = self.__get_printer(printer, **options)
72
+ with printer.console.pager(styles=True, links=True):
73
+ self.print(printer)
74
+
75
+ def print(
76
+ self,
77
+ printer: Printer | None = None,
78
+ end: str = "\n",
79
+ **options: Unpack["_PrintOptions"],
80
+ ) -> None:
81
+ """
82
+ Entry point of the printing process.
83
+
84
+ Args:
85
+ printer (Printer):
86
+ `Printer` object to use for printing.
87
+ If None, a new `Printer` object will be created.
88
+
89
+ Keyword Args:
90
+ console (rich.Console):
91
+ The console to use for printing. If not provided, a new console
92
+ will be created.
93
+ analysis (dict[ir.SSAValue, Printable]):
94
+ Analysis results to use for printing. If `None`, no analysis results
95
+ show_indent_mark (bool):
96
+ Whether to show the indentation mark.
97
+ theme (Theme | dict | str):
98
+ The theme to use for printing, defaults to "dark".
99
+
100
+ !!! note
101
+ This function also accepts all other `rich.console.Console` options.
102
+ """
103
+ printer = self.__get_printer(printer, **options)
104
+ self.print_impl(printer)
105
+ printer.plain_print(end)
106
+
107
+ def print_str(
108
+ self,
109
+ printer: Printer | None = None,
110
+ end: str = "\n",
111
+ **options: Unpack["_PrintOptions"],
112
+ ) -> str:
113
+ """Print the object to a string.
114
+
115
+ Args:
116
+ printer (Printer):
117
+ `Printer` object to use for printing.
118
+ If None, a new `Printer` object will be created.
119
+
120
+ Keyword Args:
121
+ console (rich.Console):
122
+ The console to use for printing. If not provided, a new console
123
+ will be created.
124
+ analysis (dict[ir.SSAValue, Printable]):
125
+ Analysis results to use for printing. If `None`, no analysis results
126
+ show_indent_mark (bool):
127
+ Whether to show the indentation mark.
128
+ theme (Theme | dict | str):
129
+ The theme to use for printing, defaults to "dark".
130
+
131
+ !!! note
132
+ This function also accepts all other `rich.console.Console` options.
133
+ """
134
+ printer = self.__get_printer(printer, **options)
135
+ with printer.string_io() as stream:
136
+ self.print(printer, end=end, **options)
137
+ return stream.getvalue()
138
+
139
+ @abstractmethod
140
+ def print_impl(self, printer: Printer) -> None:
141
+ raise NotImplementedError(f"print is not implemented for {type(self)}")