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/decl/info.py ADDED
@@ -0,0 +1,346 @@
1
+ from abc import abstractmethod
2
+ from types import GenericAlias
3
+ from typing import Any, Callable, Optional
4
+ from functools import cached_property
5
+ from dataclasses import MISSING, field, dataclass
6
+
7
+ from kirin import types
8
+ from kirin.ir import Block, Region, Attribute
9
+
10
+
11
+ @dataclass
12
+ class Field:
13
+ name: Optional[str] = field(default=None, init=False)
14
+ annotation: Any = field(default=None, init=False)
15
+ kw_only: bool
16
+ alias: Optional[str]
17
+
18
+ __class_getitem__ = classmethod(GenericAlias)
19
+
20
+ @abstractmethod
21
+ def has_no_default(self) -> bool: ...
22
+
23
+
24
+ @dataclass
25
+ class AttributeField(Field):
26
+ default: Any
27
+ init: bool
28
+ repr: bool
29
+ default_factory: Optional[Callable[[], Attribute]]
30
+ type: types.TypeAttribute
31
+ pytype: bool = False
32
+ "if `True`, annotation is a python type hint instead of `TypeAttribute`"
33
+
34
+ def has_no_default(self):
35
+ return self.default is MISSING and self.default_factory is None
36
+
37
+
38
+ def attribute(
39
+ type: types.TypeAttribute = types.Any,
40
+ *,
41
+ init: bool = True,
42
+ repr: bool = True,
43
+ default: Any = MISSING,
44
+ default_factory: Optional[Callable[[], Any]] = None,
45
+ kw_only: bool = True,
46
+ alias: Optional[str] = None,
47
+ ) -> Any:
48
+ if kw_only is False:
49
+ raise TypeError("attribute fields must be keyword-only")
50
+
51
+ return AttributeField(
52
+ type=type,
53
+ init=init,
54
+ repr=repr,
55
+ default=default,
56
+ default_factory=default_factory,
57
+ kw_only=kw_only,
58
+ alias=alias,
59
+ )
60
+
61
+
62
+ @dataclass
63
+ class ArgumentField(Field):
64
+ type: types.TypeAttribute
65
+ """type of the argument, will be used in validation.
66
+ """
67
+ print: bool = True
68
+ """if `True`, this argument name is printed in the signature.
69
+ """
70
+ group: bool = False # NOTE: this cannot be set by user
71
+ """if `True`, this argument is annotated with Tuple[SSAValue, ...]
72
+ """
73
+
74
+ def has_no_default(self):
75
+ return True
76
+
77
+
78
+ # NOTE: argument must appear in init and repr
79
+ def argument(
80
+ type: types.TypeAttribute = types.Any,
81
+ *,
82
+ print: bool = True,
83
+ kw_only: bool = False,
84
+ alias: Optional[str] = None,
85
+ ) -> Any:
86
+ """Field specifier for arguments.
87
+
88
+ Args:
89
+ type(TypeAttribute): type of the argument, will be used in validation.
90
+ print(bool): if `True`, this argument name is printed in the signature.
91
+ kw_only(bool): if `True`, this argument is keyword-only.
92
+ alias(Optional[str]): an alias for the argument name in the `__init__` method.
93
+ """
94
+ return ArgumentField(
95
+ type=type,
96
+ print=print,
97
+ kw_only=kw_only,
98
+ alias=alias,
99
+ )
100
+
101
+
102
+ @dataclass
103
+ class ResultField(Field):
104
+ init: bool
105
+ repr: bool
106
+ type: types.TypeAttribute = field(default_factory=types.AnyType)
107
+
108
+ def has_no_default(self):
109
+ return True
110
+
111
+
112
+ def result(
113
+ type: types.TypeAttribute = types.Any,
114
+ *,
115
+ # NOTE: init is false, use other hooks to set custom results
116
+ # or just mutate the statement after creation
117
+ init: bool = False,
118
+ repr: bool = True,
119
+ kw_only: bool = True,
120
+ alias: Optional[str] = None,
121
+ ) -> Any:
122
+ """Field specifier for results.
123
+
124
+ Args:
125
+ type(TypeAttribute): type of the result.
126
+ init(bool): if `True`, this result field is included in the `__init__` method.
127
+ repr(bool): if `True`, this result field is included in the `__repr__` and pretty printing.
128
+ kw_only(bool): if `True`, this result field is keyword-only.
129
+ alias(Optional[str]): an alias for the result field name in the `__init__` method.
130
+ """
131
+ if kw_only is False: # for linting
132
+ raise TypeError("result fields must be keyword-only")
133
+
134
+ if init is True:
135
+ raise TypeError("result fields cannot appear in __init__")
136
+
137
+ return ResultField(
138
+ type=type,
139
+ init=init,
140
+ repr=repr,
141
+ kw_only=kw_only,
142
+ alias=alias,
143
+ )
144
+
145
+
146
+ @dataclass
147
+ class RegionField(Field):
148
+ init: bool
149
+ repr: bool
150
+ multi: bool
151
+ default_factory: Callable[[], Region]
152
+
153
+ def has_no_default(self):
154
+ return False
155
+
156
+
157
+ def region(
158
+ *,
159
+ init: bool = True, # so we can use the default_factory
160
+ repr: bool = True,
161
+ kw_only: bool = True,
162
+ alias: Optional[str] = None,
163
+ multi: bool = False,
164
+ default_factory: Callable[[], Region] = Region,
165
+ ) -> Any:
166
+ """Field specifier for regions.
167
+
168
+ Args:
169
+ init(bool): if `True`, this region field is included in the `__init__` method.
170
+ repr(bool): if `True`, this region field is included in the `__repr__` and pretty printing.
171
+ kw_only(bool): if `True`, this region field is keyword-only.
172
+ alias(Optional[str]): an alias for the region field name in the `__init__` method.
173
+ multi(bool): if `True`, this region can contain multiple blocks.
174
+ default_factory(Callable[[], Region]): a factory function to create a default region.
175
+ """
176
+ if kw_only is False:
177
+ raise TypeError("region fields must be keyword-only")
178
+
179
+ return RegionField(
180
+ init=init,
181
+ repr=repr,
182
+ kw_only=kw_only,
183
+ alias=alias,
184
+ multi=multi,
185
+ default_factory=default_factory,
186
+ )
187
+
188
+
189
+ @dataclass
190
+ class BlockField(Field):
191
+ init: bool
192
+ repr: bool
193
+ default_factory: Callable[[], Block]
194
+
195
+ def has_no_default(self):
196
+ return False
197
+
198
+
199
+ def block(
200
+ *,
201
+ init: bool = True,
202
+ repr: bool = True,
203
+ kw_only: bool = True,
204
+ alias: Optional[str] = None,
205
+ default_factory: Callable[[], Block] = Block,
206
+ ) -> Any:
207
+ """Field specifier for blocks.
208
+
209
+ Args:
210
+ init(bool): if `True`, this block field is included in the `__init__` method.
211
+ repr(bool): if `True`, this block field is included in the `__repr__` and pretty printing.
212
+ kw_only(bool): if `True`, this block field is keyword-only.
213
+ alias(Optional[str]): an alias for the block field name in the `__init__` method.
214
+ default_factory(Callable[[], Block]): a factory function to create a default block.
215
+ """
216
+ if kw_only is False:
217
+ raise TypeError("block fields must be keyword-only")
218
+
219
+ return BlockField(
220
+ init=init,
221
+ repr=repr,
222
+ kw_only=kw_only,
223
+ alias=alias,
224
+ default_factory=default_factory,
225
+ )
226
+
227
+
228
+ @dataclass
229
+ class StatementFields:
230
+ std_args: dict[str, ArgumentField] = field(default_factory=dict)
231
+ """standard arguments of the statement."""
232
+ kw_args: dict[str, ArgumentField] = field(default_factory=dict)
233
+ """keyword-only arguments of the statement."""
234
+ results: dict[str, ResultField] = field(default_factory=dict)
235
+ """results of the statement."""
236
+ regions: dict[str, RegionField] = field(default_factory=dict)
237
+ """regions of the statement."""
238
+ blocks: dict[str, BlockField] = field(default_factory=dict)
239
+ """blocks of the statement."""
240
+ attributes: dict[str, AttributeField] = field(default_factory=dict)
241
+ """attributes of the statement."""
242
+
243
+ class Args:
244
+ def __init__(self, fields: "StatementFields"):
245
+ self.fields = fields
246
+
247
+ def __len__(self):
248
+ return len(self.fields.std_args) + len(self.fields.kw_args)
249
+
250
+ def __getitem__(self, name):
251
+ if (value := self.fields.std_args.get(name)) is not None:
252
+ return value
253
+ elif (value := self.fields.kw_args.get(name)) is not None:
254
+ return value
255
+ raise KeyError(name)
256
+
257
+ def __setitem__(self, name: str, value: ArgumentField):
258
+ if value.kw_only:
259
+ self.fields.kw_args[name] = value
260
+ else:
261
+ self.fields.std_args[name] = value
262
+
263
+ def __contains__(self, name):
264
+ return name in self.fields.std_args or name in self.fields.kw_args
265
+
266
+ def values(self):
267
+ yield from self.fields.std_args.values()
268
+ yield from self.fields.kw_args.values()
269
+
270
+ def items(self):
271
+ yield from self.fields.std_args.items()
272
+ yield from self.fields.kw_args.items()
273
+
274
+ def keys(self):
275
+ yield from self.fields.std_args.keys()
276
+ yield from self.fields.kw_args.keys()
277
+
278
+ @property
279
+ def args(self):
280
+ """iterable of all argument fields."""
281
+ return self.Args(self)
282
+
283
+ @classmethod
284
+ def from_fields(cls, fields: dict[str, Field]):
285
+ ret = cls()
286
+ for name, f in fields.items():
287
+ ret[name] = f
288
+ return ret
289
+
290
+ def __contains__(self, name):
291
+ return (
292
+ name in self.args
293
+ or name in self.results
294
+ or name in self.regions
295
+ or name in self.blocks
296
+ or name in self.attributes
297
+ )
298
+
299
+ def __setitem__(self, name, value):
300
+ if isinstance(value, ArgumentField):
301
+ self.args[name] = value
302
+ elif isinstance(value, ResultField):
303
+ self.results[name] = value
304
+ elif isinstance(value, RegionField):
305
+ self.regions[name] = value
306
+ elif isinstance(value, BlockField):
307
+ self.blocks[name] = value
308
+ elif isinstance(value, AttributeField):
309
+ self.attributes[name] = value
310
+ else:
311
+ raise TypeError(f"unknown field type {value}")
312
+
313
+ def __iter__(self):
314
+ yield from self.args.values()
315
+ yield from self.kw_args.values()
316
+ yield from self.results.values()
317
+ yield from self.regions.values()
318
+ yield from self.blocks.values()
319
+ yield from self.attributes.values()
320
+
321
+ def __len__(self):
322
+ return (
323
+ len(self.args)
324
+ + len(self.results)
325
+ + len(self.regions)
326
+ + len(self.blocks)
327
+ + len(self.attributes)
328
+ )
329
+
330
+ @cached_property
331
+ def attr_or_props(self):
332
+ return set(self.attributes.keys())
333
+
334
+ @cached_property
335
+ def required_names(self):
336
+ """set of all fields that do not have a default value."""
337
+ return set(
338
+ list(self.args.keys())
339
+ + [name for name, f in self.attributes.items() if f.has_no_default()]
340
+ + [name for name, f in self.blocks.items() if f.has_no_default()]
341
+ + [name for name, f in self.regions.items() if f.has_no_default()]
342
+ )
343
+
344
+ @cached_property
345
+ def group_arg_names(self):
346
+ return set([name for name, f in self.args.items() if f.group])
@@ -0,0 +1,157 @@
1
+ import re
2
+ import sys
3
+ import inspect
4
+ import dataclasses
5
+ from types import ModuleType
6
+ from typing import Callable, get_args
7
+ from dataclasses import KW_ONLY
8
+
9
+ from beartype.door import is_subhint
10
+
11
+ from kirin import ir, types
12
+
13
+ from .base import BaseModifier
14
+ from .info import Field, ArgumentField, AttributeField, StatementFields, argument
15
+
16
+
17
+ class ScanFields(BaseModifier):
18
+ _FIELDS = "__kirin_stmt_fields"
19
+ # String regex that string annotations for ClassVar or InitVar must match.
20
+ # Allows "identifier.identifier[" or "identifier[".
21
+ # https://bugs.python.org/issue33453 for details.
22
+ _MODULE_IDENTIFIER_RE = re.compile(r"^(?:\s*(\w+)\s*\.)?\s*(\w+)")
23
+
24
+ def scan_fields(self):
25
+ self._scan_base_fields()
26
+ self._scan_annotations()
27
+ setattr(self.cls, self._FIELDS, self.fields)
28
+ return
29
+
30
+ def _scan_base_fields(self):
31
+ # let's just assume dict preserve insertion order in python 3.7+
32
+ for b in self.cls.__mro__[-1:0:-1]: # taken from dataclass impl
33
+ base_fields: StatementFields | None = getattr(b, self._FIELDS, None)
34
+ if base_fields is not None:
35
+ self.has_statement_bases = True
36
+ for f in base_fields:
37
+ assert f.name is not None, "field name must be set"
38
+ self.fields[f.name] = f
39
+
40
+ def _scan_annotations(self):
41
+ cls_fields: list[Field] = []
42
+ cls_annotations = inspect.get_annotations(self.cls, eval_str=True)
43
+ for name, typ in cls_annotations.items():
44
+ # See if this is a marker to change the value of kw_only.
45
+ if self._is_kw_only(typ) or (
46
+ isinstance(typ, str)
47
+ and self._is_type(typ, dataclasses, KW_ONLY, self._is_kw_only)
48
+ ):
49
+ if self.KW_ONLY_seen:
50
+ raise TypeError(
51
+ f"{name!r} is KW_ONLY, but KW_ONLY "
52
+ "has already been specified"
53
+ )
54
+ self.KW_ONLY_seen = True
55
+ self.kw_only = True
56
+ else:
57
+ cls_fields.append(self._get_field(name, typ))
58
+
59
+ for f in cls_fields:
60
+ name: str = f.name # type: ignore # name has been set
61
+ self.fields[name] = f
62
+ if hasattr(self.cls, name):
63
+ # remove the field from the class
64
+ # unlike dataclass, we don't actually
65
+ # store any values in the class, they
66
+ # are stored inside the IR node.
67
+ delattr(self.cls, name)
68
+
69
+ def _get_field(self, name: str, typ):
70
+ """Return a Field object for this field name and type."""
71
+ guess = self._guess_type(typ)
72
+ default = getattr(self.cls, name, dataclasses.MISSING)
73
+ if isinstance(default, Field):
74
+ f = default
75
+ # no default descriptor, create a new one
76
+ elif is_subhint(typ, ir.SSAValue) or is_subhint(typ, tuple[ir.SSAValue, ...]):
77
+ f: Field = (
78
+ argument()
79
+ ) # only argument can have default, others must use field specifiers
80
+ elif typ is ir.ResultValue:
81
+ raise ValueError(
82
+ f"expect field specifiers for `ir.ResultValue` fields: {name}"
83
+ )
84
+ elif typ is ir.Region:
85
+ raise ValueError(f"expect field specifiers for `ir.Region` fields: {name}")
86
+ elif typ is ir.Block:
87
+ raise ValueError(f"expect field specifiers for `ir.Block` fields: {name}")
88
+ else:
89
+ # could be a wrong SSAValue field, e.g list[SSAValue], try check args
90
+ if typ_args := get_args(typ):
91
+ if any(is_subhint(arg, ir.SSAValue) for arg in typ_args):
92
+ raise ValueError(
93
+ f"unsupported SSAValue field: {name},"
94
+ f" expect `SSAValue` or `tuple[SSAValue, ...]`, got {typ}"
95
+ )
96
+ raise ValueError(f"expect field specifiers for attribute fields: {name}")
97
+
98
+ f.name = name
99
+ f.annotation = typ
100
+ self._post_init_field(f, guess)
101
+ return f
102
+
103
+ def _post_init_field(self, f: Field, guess: type | None):
104
+ if isinstance(f, ArgumentField) and is_subhint(guess, tuple[ir.SSAValue, ...]):
105
+ f.group = True
106
+ # try to narrow the type based on the guess
107
+ elif isinstance(f, AttributeField):
108
+ if guess and not is_subhint(
109
+ guess, ir.Attribute
110
+ ): # not specified, and using python type
111
+ if f.type is types.Any: # not set or too generic
112
+ f.type = types.hint2type(guess)
113
+ f.pytype = True
114
+
115
+ @staticmethod
116
+ def _is_kw_only(a_type):
117
+ return a_type is KW_ONLY
118
+
119
+ def _is_type(
120
+ self,
121
+ annotation: type | str,
122
+ obj_module: ModuleType,
123
+ obj_type: type,
124
+ is_type_predicate: Callable[[type], bool],
125
+ ):
126
+ """Given a type annotation string, does it refer to `obj_type` in
127
+ `obj_module`? For example, when checking that annotation denotes a
128
+ `ClassVar`, then `obj_module` is typing, and a_type is
129
+ `typing.ClassVar`.
130
+
131
+ Taken from dataclasses.py/is_type.
132
+ """
133
+ guess = self._guess_type(annotation)
134
+ if guess is not None:
135
+ return guess.__module__ is obj_module and is_type_predicate(guess)
136
+ return False
137
+
138
+ def _guess_type(self, annotation: type | str) -> type | None:
139
+ """Guess the type/hint object from a string annotation."""
140
+ if not isinstance(annotation, str):
141
+ return annotation
142
+
143
+ match = self._MODULE_IDENTIFIER_RE.match(annotation)
144
+ ns = None
145
+ if match:
146
+ module_name = match.group(1)
147
+ if not module_name:
148
+ # No module name, assume the class's module did
149
+ # "from dataclasses import InitVar".
150
+ ns = sys.modules.get(self.cls.__module__).__dict__
151
+ else:
152
+ # Look up module_name in the class's module.
153
+ if self.cls_module:
154
+ ns = self.cls_module.__dict__.get(module_name).__dict__
155
+ if ns:
156
+ return ns.get(match.group(2))
157
+ return None
kirin/decl/verify.py ADDED
@@ -0,0 +1,69 @@
1
+ import inspect
2
+ import textwrap
3
+
4
+ from beartype.door import is_subhint
5
+
6
+ from kirin import ir
7
+
8
+ from .base import BaseModifier
9
+ from .info import Field, ResultField, ArgumentField
10
+
11
+
12
+ class Verify(BaseModifier):
13
+ RESERVED_NAMES = {
14
+ "name",
15
+ "traits",
16
+ "dialect",
17
+ "args",
18
+ "parent",
19
+ "parent_node",
20
+ "parent_region",
21
+ "parent_block",
22
+ "next_stmt",
23
+ "prev_stmt",
24
+ "results",
25
+ "regions",
26
+ "successors",
27
+ "attributes",
28
+ }
29
+
30
+ def verify_mro(self):
31
+ if not issubclass(self.cls, ir.Statement):
32
+ raise TypeError(
33
+ f"expected {self.cls.__name__} to be a subclass of ir.Statement"
34
+ )
35
+
36
+ def verify_fields(self):
37
+ # Do we have any Field members that don't also have annotations?
38
+ cls_annotations = inspect.get_annotations(self.cls)
39
+ for name, value in self.cls.__dict__.items():
40
+ if isinstance(value, Field) and name not in cls_annotations:
41
+ raise ValueError(f"{name!r} is a field but has no type annotation")
42
+
43
+ for f in self.fields:
44
+ if f.name in self.RESERVED_NAMES:
45
+ raise ValueError(f"{f.name!r} is a reserved name")
46
+
47
+ if isinstance(f, ArgumentField):
48
+ if not (
49
+ is_subhint(f.annotation, ir.SSAValue)
50
+ or is_subhint(f.annotation, tuple[ir.SSAValue, ...])
51
+ ):
52
+ raise ValueError(
53
+ f"{f.name!r} is an argument field but has an invalid type annotation"
54
+ )
55
+
56
+ if is_subhint(f.annotation, ir.ResultValue):
57
+ raise ValueError(
58
+ textwrap.dedent(
59
+ f"{f.name!r} is an argument field but has an invalid type annotation {f.annotation}"
60
+ " (did you mean to use `info.result` instead?)"
61
+ )
62
+ )
63
+
64
+ if isinstance(f, ResultField) and not is_subhint(
65
+ f.annotation, ir.ResultValue
66
+ ):
67
+ raise ValueError(
68
+ f"{f.name!r} is a result field but has an invalid type annotation"
69
+ )
@@ -0,0 +1,14 @@
1
+ """Built-in dialects for Kirin.
2
+
3
+ This module contains the built-in dialects for Kirin. Each dialect is an
4
+ instance of the `Dialect` class. Each submodule contains a `dialect` variable
5
+ that is an instance of the corresponding `Dialect` class.
6
+
7
+ The modules can be directly used as dialects. For example, you can write
8
+
9
+ ```python
10
+ from kirin.dialects import py, func
11
+ ```
12
+
13
+ to import the Python and function dialects.
14
+ """
@@ -0,0 +1,53 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ from kirin import ir
4
+
5
+ if TYPE_CHECKING:
6
+ from kirin.ir import Statement
7
+ from kirin.print.printer import Printer
8
+
9
+
10
+ def pprint_calllike(
11
+ invoke_or_call: "Statement", callee: str, printer: "Printer"
12
+ ) -> None:
13
+ with printer.rich(style="red"):
14
+ printer.print_name(invoke_or_call)
15
+ printer.plain_print(" ")
16
+
17
+ n_total = len(invoke_or_call.args)
18
+ printer.plain_print(callee)
19
+ if (inputs := getattr(invoke_or_call, "inputs", None)) is None:
20
+ raise ValueError(f"{invoke_or_call} does not have inputs")
21
+
22
+ if not isinstance(inputs, tuple):
23
+ raise ValueError(f"inputs of {invoke_or_call} is not a tuple")
24
+
25
+ if (kwargs := getattr(invoke_or_call, "kwargs", None)) is None:
26
+ raise ValueError(f"{invoke_or_call} does not have kwargs")
27
+
28
+ if not isinstance(kwargs, tuple):
29
+ raise ValueError(f"kwargs of {invoke_or_call} is not a tuple")
30
+
31
+ positional = inputs[: n_total - len(kwargs)]
32
+ kwargs = dict(
33
+ zip(
34
+ kwargs,
35
+ inputs[n_total - len(kwargs) :],
36
+ )
37
+ )
38
+
39
+ printer.plain_print("(")
40
+ printer.print_seq(positional)
41
+ if kwargs and positional:
42
+ printer.plain_print(", ")
43
+ printer.print_mapping(kwargs, delim=", ")
44
+ printer.plain_print(")")
45
+
46
+ with printer.rich(style="comment"):
47
+ printer.plain_print(" : ")
48
+ printer.print_seq(
49
+ [result.type for result in invoke_or_call._results],
50
+ delim=", ",
51
+ )
52
+ if trait := invoke_or_call.get_trait(ir.MaybePure):
53
+ printer.plain_print(f" maybe_pure={trait.is_pure(invoke_or_call)}")
@@ -0,0 +1,20 @@
1
+ """Control flow dialect.
2
+
3
+ This dialect provides a low-level control flow representation.
4
+
5
+ This dialect does not provide any lowering strategies, to lowering
6
+ a Python AST to this dialect, use the `kirin.dialects.lowering.cf` dialect
7
+ with this dialect.
8
+ """
9
+
10
+ from kirin.dialects.cf import (
11
+ emit as emit,
12
+ constprop as constprop,
13
+ typeinfer as typeinfer,
14
+ )
15
+ from kirin.dialects.cf.stmts import (
16
+ Branch as Branch,
17
+ ConditionalBranch as ConditionalBranch,
18
+ )
19
+ from kirin.dialects.cf.interp import CfInterpreter as CfInterpreter
20
+ from kirin.dialects.cf.dialect import dialect as dialect