1- """Parse KORE axioms into rewrite rules.
2-
3- Based on the [LLVM Backend's implementation](https://github.com/runtimeverification/llvm-backend/blob/d5eab4b0f0e610bc60843ebb482f79c043b92702/lib/ast/pattern_matching.cpp).
4- """
5-
61from __future__ import annotations
72
3+ import logging
84from abc import ABC
95from dataclasses import dataclass
10- from typing import TYPE_CHECKING , final
6+ from typing import TYPE_CHECKING , Generic , TypeVar , final
117
128from .prelude import inj
13- from .syntax import And , App , Axiom , Ceil , Equals , EVar , Implies , In , Not , Rewrites , SortVar , String , Top
9+ from .syntax import (
10+ DV ,
11+ And ,
12+ App ,
13+ Axiom ,
14+ Ceil ,
15+ Equals ,
16+ EVar ,
17+ Implies ,
18+ In ,
19+ Not ,
20+ Pattern ,
21+ Rewrites ,
22+ SortApp ,
23+ SortVar ,
24+ String ,
25+ Top ,
26+ )
1427
1528if TYPE_CHECKING :
1629 from typing import Final
1730
18- from .syntax import Definition , Pattern
31+ from .syntax import Definition
1932
2033 Attrs = dict [str , tuple [Pattern , ...]]
2134
2235
36+ P = TypeVar ('P' , bound = Pattern )
37+
38+
39+ _LOGGER : Final = logging .getLogger (__name__ )
40+
41+
2342# There's a simplification rule with irregular form in the prelude module INJ.
2443# This rule is skipped in Rule.extract_all.
2544_S1 , _S2 , _S3 , _R = (SortVar (name ) for name in ['S1' , 'S2' , 'S3' , 'R' ])
@@ -56,33 +75,35 @@ def from_axiom(axiom: Axiom) -> Rule:
5675 if isinstance (axiom .pattern , Rewrites ):
5776 return RewriteRule .from_axiom (axiom )
5877
59- if 'simplification' in axiom .attrs_by_key :
60- return SimpliRule .from_axiom (axiom )
78+ if 'simplification' not in axiom .attrs_by_key :
79+ return FunctionRule .from_axiom (axiom )
6180
62- return FunctionRule .from_axiom (axiom )
81+ match axiom .pattern :
82+ case Implies (right = Equals (left = App ())):
83+ return AppRule .from_axiom (axiom )
84+ case Implies (right = Equals (left = Ceil ())):
85+ return CeilRule .from_axiom (axiom )
86+ case Implies (right = Equals (left = Equals ())):
87+ return EqualsRule .from_axiom (axiom )
88+ case _:
89+ raise ValueError (f'Cannot parse simplification rule: { axiom .text } ' )
6390
6491 @staticmethod
6592 def extract_all (defn : Definition ) -> list [Rule ]:
66- return [Rule .from_axiom (axiom ) for axiom in defn .axioms if Rule ._is_rule (axiom )]
67-
68- @staticmethod
69- def _is_rule (axiom : Axiom ) -> bool :
70- if axiom == _INJ_AXIOM :
71- return False
72-
73- if any (attr in axiom .attrs_by_key for attr in _SKIPPED_ATTRS ):
74- return False
93+ def is_rule (axiom : Axiom ) -> bool :
94+ if axiom == _INJ_AXIOM :
95+ return False
7596
76- match axiom .pattern :
77- case Implies (right = Equals (left = Ceil ())):
78- # Ceil rule
97+ if any (attr in axiom .attrs_by_key for attr in _SKIPPED_ATTRS ):
7998 return False
8099
81- return True
100+ return True
101+
102+ return [Rule .from_axiom (axiom ) for axiom in defn .axioms if is_rule (axiom )]
82103
83104
84105@final
85- @dataclass
106+ @dataclass ( frozen = True )
86107class RewriteRule (Rule ):
87108 lhs : App
88109 rhs : App
@@ -95,8 +116,7 @@ class RewriteRule(Rule):
95116
96117 @staticmethod
97118 def from_axiom (axiom : Axiom ) -> RewriteRule :
98- lhs , req , ctx = RewriteRule ._extract_lhs (axiom )
99- rhs , ens = RewriteRule ._extract_rhs (axiom )
119+ lhs , rhs , req , ens , ctx = RewriteRule ._extract (axiom )
100120 priority = _extract_priority (axiom )
101121 uid = _extract_uid (axiom )
102122 label = _extract_label (axiom )
@@ -112,51 +132,35 @@ def from_axiom(axiom: Axiom) -> RewriteRule:
112132 )
113133
114134 @staticmethod
115- def _extract_lhs (axiom : Axiom ) -> tuple [App , Pattern | None , EVar | None ]:
116- req : Pattern | None = None
117- # Cases 0-5 of get_left_hand_side
118- # Cases 5-10 of get_requires
135+ def _extract (axiom : Axiom ) -> tuple [App , App , Pattern | None , Pattern | None , EVar | None ]:
119136 match axiom .pattern :
120- case Rewrites (left = And (ops = (Top (), lhs ))):
121- pass
122- case Rewrites (left = And (ops = (Equals (left = req ), lhs ))):
123- pass
124- case Rewrites (left = And (ops = (lhs , Top ()))):
125- pass
126- case Rewrites (left = And (ops = (lhs , Equals (left = req )))):
127- pass
128- case Rewrites (left = And (ops = (Not (), And (ops = (Top (), lhs ))))):
129- pass
130- case Rewrites (left = And (ops = (Not (), And (ops = (Equals (left = req ), lhs ))))):
137+ case Rewrites (left = And (ops = (_lhs , _req )), right = _rhs ):
131138 pass
132139 case _:
133- raise ValueError (f'Cannot extract LHS from axiom: { axiom .text } ' )
140+ raise ValueError (f'Cannot extract rewrite rule from axiom: { axiom .text } ' )
134141
135142 ctx : EVar | None = None
136- match lhs :
137- case App ("Lbl'-LT-'generatedTop'-GT-'" ) as app :
143+ match _lhs :
144+ case App ("Lbl'-LT-'generatedTop'-GT-'" ) as lhs :
138145 pass
139- case And (_, (App ("Lbl'-LT-'generatedTop'-GT-'" ) as app , EVar ("Var'Hash'Configuration" ) as ctx )):
146+ case And (_, (App ("Lbl'-LT-'generatedTop'-GT-'" ) as lhs , EVar ("Var'Hash'Configuration" ) as ctx )):
140147 pass
148+ case _:
149+ raise ValueError (f'Cannot extract LHS configuration from axiom: { axiom .text } ' )
141150
142- return app , req , ctx
143-
144- @staticmethod
145- def _extract_rhs (axiom : Axiom ) -> tuple [App , Pattern | None ]:
146- # Case 2 of get_right_hand_side:
147- # 2: rhs(\rewrites(_, \and(X, Y))) = get_builtin(\and(X, Y))
148- # Below is a special case without get_builtin
149- match axiom .pattern :
150- case Rewrites (right = And (ops = (App ("Lbl'-LT-'generatedTop'-GT-'" ) as rhs , Top () | Equals () as _ens ))):
151+ req = _extract_condition (_req )
152+ rhs , ens = _extract_rhs (_rhs )
153+ match rhs :
154+ case App ("Lbl'-LT-'generatedTop'-GT-'" ):
151155 pass
152156 case _:
153- raise ValueError (f'Cannot extract RHS from axiom: { axiom .text } ' )
154- ens = _extract_ensures ( _ens )
155- return rhs , ens
157+ raise ValueError (f'Cannot extract RHS configuration from axiom: { axiom .text } ' )
158+
159+ return lhs , rhs , req , ens , ctx
156160
157161
158162@final
159- @dataclass
163+ @dataclass ( frozen = True )
160164class FunctionRule (Rule ):
161165 lhs : App
162166 rhs : Pattern
@@ -166,9 +170,7 @@ class FunctionRule(Rule):
166170
167171 @staticmethod
168172 def from_axiom (axiom : Axiom ) -> FunctionRule :
169- args , req = FunctionRule ._extract_args (axiom )
170- app , rhs , ens = FunctionRule ._extract_rhs (axiom )
171- lhs = app .let (args = args )
173+ lhs , rhs , req , ens = FunctionRule ._extract (axiom )
172174 priority = _extract_priority (axiom )
173175 return FunctionRule (
174176 lhs = lhs ,
@@ -179,93 +181,133 @@ def from_axiom(axiom: Axiom) -> FunctionRule:
179181 )
180182
181183 @staticmethod
182- def _extract_args (axiom : Axiom ) -> tuple [tuple [Pattern , ...], Pattern | None ]:
183- req : Pattern | None = None
184- # Cases 7-10 of get_left_hand_side
185- # Cases 0-3 of get_requires
184+ def _extract (axiom : Axiom ) -> tuple [App , Pattern , Pattern | None , Pattern | None ]:
186185 match axiom .pattern :
187- case Implies (left = And (ops = (Top (), pat ))):
188- return FunctionRule ._get_patterns (pat ), req
189- case Implies (left = And (ops = (Equals (left = req ), pat ))):
190- return FunctionRule ._get_patterns (pat ), req
191- case Implies (left = And (ops = (Not (), And (ops = (Top (), pat ))))):
192- return FunctionRule ._get_patterns (pat ), req
193- case Implies (left = And (ops = (Not (), And (ops = (Equals (left = req ), pat ))))):
194- return FunctionRule ._get_patterns (pat ), req
186+ case Implies (
187+ left = And (
188+ ops = (Not (), And (ops = (_req , _args ))) | (_req , _args ),
189+ ),
190+ right = Equals (left = App () as app , right = _rhs ),
191+ ):
192+ args = FunctionRule ._extract_args (_args )
193+ lhs = app .let (args = args )
194+ req = _extract_condition (_req )
195+ rhs , ens = _extract_rhs (_rhs )
196+ return lhs , rhs , req , ens
195197 case _:
196- raise ValueError (f'Cannot extract LHS from axiom: { axiom .text } ' )
198+ raise ValueError (f'Cannot extract function rule from axiom: { axiom .text } ' )
197199
198200 @staticmethod
199- def _get_patterns (pattern : Pattern ) -> tuple [Pattern , ...]:
200- # get_patterns(\top()) = []
201- # get_patterns(\and(\in(_, X), Y)) = X : get_patterns(Y)
201+ def _extract_args (pattern : Pattern ) -> tuple [Pattern , ...]:
202202 match pattern :
203203 case Top ():
204204 return ()
205- case And (ops = (In (right = x ), y )):
206- return (x ,) + FunctionRule ._get_patterns ( y )
205+ case And (ops = (In (left = EVar (), right = arg ), rest )):
206+ return (arg ,) + FunctionRule ._extract_args ( rest )
207207 case _:
208- raise AssertionError ()
208+ raise ValueError (f'Cannot extract argument list from pattern: { pattern .text } ' )
209+
210+
211+ class SimpliRule (Rule , Generic [P ], ABC ):
212+ lhs : P
209213
210214 @staticmethod
211- def _extract_rhs (axiom : Axiom ) -> tuple [App , Pattern , Pattern | None ]:
212- # Case 0 of get_right_hand_side
215+ def _extract (axiom : Axiom , lhs_type : type [P ]) -> tuple [P , Pattern , Pattern | None , Pattern | None ]:
213216 match axiom .pattern :
214- case Implies (right = Equals (left = App () as app , right = And (ops = (rhs , Top () | Equals () as _ens )))):
215- pass
217+ case Implies (left = _req , right = Equals (left = lhs , right = _rhs )):
218+ req = _extract_condition (_req )
219+ rhs , ens = _extract_rhs (_rhs )
220+ if not isinstance (lhs , lhs_type ):
221+ raise ValueError (f'Invalid LHS type from simplification axiom: { axiom .text } ' )
222+ return lhs , rhs , req , ens
216223 case _:
217- raise ValueError (f'Cannot extract RHS from axiom: { axiom .text } ' )
218- ens = _extract_ensures (_ens )
219- return app , rhs , ens
224+ raise ValueError (f'Cannot extract simplification rule from axiom: { axiom .text } ' )
220225
221226
222227@final
223- @dataclass
224- class SimpliRule (Rule ):
225- lhs : Pattern
228+ @dataclass (frozen = True )
229+ class AppRule (SimpliRule [App ]):
230+ lhs : App
231+ rhs : Pattern
232+ req : Pattern | None
233+ ens : Pattern | None
234+ priority : int
235+
236+ @staticmethod
237+ def from_axiom (axiom : Axiom ) -> AppRule :
238+ lhs , rhs , req , ens = SimpliRule ._extract (axiom , App )
239+ priority = _extract_priority (axiom )
240+ return AppRule (
241+ lhs = lhs ,
242+ rhs = rhs ,
243+ req = req ,
244+ ens = ens ,
245+ priority = priority ,
246+ )
247+
248+
249+ @final
250+ @dataclass (frozen = True )
251+ class CeilRule (SimpliRule ):
252+ lhs : Ceil
226253 rhs : Pattern
227254 req : Pattern | None
228255 ens : Pattern | None
229256 priority : int
230257
231258 @staticmethod
232- def from_axiom (axiom : Axiom ) -> SimpliRule :
233- lhs , rhs , req , ens = SimpliRule ._extract (axiom )
259+ def from_axiom (axiom : Axiom ) -> CeilRule :
260+ lhs , rhs , req , ens = SimpliRule ._extract (axiom , Ceil )
234261 priority = _extract_priority (axiom )
235- return SimpliRule (
262+ return CeilRule (
236263 lhs = lhs ,
237264 rhs = rhs ,
238265 req = req ,
239266 ens = ens ,
240267 priority = priority ,
241268 )
242269
270+
271+ @final
272+ @dataclass (frozen = True )
273+ class EqualsRule (SimpliRule ):
274+ lhs : Equals
275+ rhs : Pattern
276+ req : Pattern | None
277+ ens : Pattern | None
278+ priority : int
279+
243280 @staticmethod
244- def _extract (axiom : Axiom ) -> tuple [Pattern , Pattern , Pattern | None , Pattern | None ]:
245- req : Pattern | None = None
246- # Cases 11-12 of get_left_hand_side
247- # Case 0 of get_right_hand_side
248- match axiom .pattern :
249- case Implies (left = Top (), right = Equals (left = lhs , right = And (ops = (rhs , Top () | Equals () as _ens )))):
250- pass
251- case Implies (left = Equals (left = req ), right = Equals (left = lhs , right = And (ops = (rhs , Top () | Equals () as _ens )))):
252- pass
253- case Implies (right = Equals (left = Ceil ())):
254- raise ValueError (f'Axiom is a ceil rule: { axiom .text } ' )
255- case _:
256- raise ValueError (f'Cannot extract simplification rule from axiom: { axiom .text } ' )
257- ens = _extract_ensures (_ens )
258- return lhs , rhs , req , ens
281+ def from_axiom (axiom : Axiom ) -> EqualsRule :
282+ lhs , rhs , req , ens = SimpliRule ._extract (axiom , Equals )
283+ if not isinstance (lhs , Equals ):
284+ raise ValueError (f'Cannot extract LHS as Equals from axiom: { axiom .text } ' )
285+ priority = _extract_priority (axiom )
286+ return EqualsRule (
287+ lhs = lhs ,
288+ rhs = rhs ,
289+ req = req ,
290+ ens = ens ,
291+ priority = priority ,
292+ )
293+
294+
295+ def _extract_rhs (pattern : Pattern ) -> tuple [Pattern , Pattern | None ]:
296+ match pattern :
297+ case And (ops = (rhs , _ens )):
298+ return rhs , _extract_condition (_ens )
299+ case _:
300+ raise ValueError (f'Cannot extract RHS from pattern: { pattern .text } ' )
259301
260302
261- def _extract_ensures ( ens : Top | Equals | None ) -> Pattern | None :
262- match ens :
303+ def _extract_condition ( pattern : Pattern ) -> Pattern | None :
304+ match pattern :
263305 case Top ():
264306 return None
265- case Equals (left = res ):
266- return res
307+ case Equals (left = cond , right = DV ( SortApp ( 'SortBool' ), String ( 'true' )) ):
308+ return cond
267309 case _:
268- raise AssertionError ( )
310+ raise ValueError ( f'Cannot extract condition from pattern: { pattern . text } ' )
269311
270312
271313def _extract_uid (axiom : Axiom ) -> str :
0 commit comments