Skip to content

Commit 660b841

Browse files
authored
Generalize KAST-to-KORE conversion for KSequence (#4874)
* Allow items of sort `K` to appear at any position within a `KSequence`, not just at the end. During `_kast_to_kore`, an application of `append` is generated in such cases. * Conversely, during `_kore_to_kast`, generate a `KSequence` for each `append`. * Fix `KVariable` sort inference so that it does not override declared sorts of `KSequence` items. * Update `test_kast_to_kore_frontend` to compare the Frontend’s result against expected outputs, rather than `pyk`'s results. This makes it easier to add new tests based on the Frontend’s output.
1 parent a38132c commit 660b841

File tree

5 files changed

+94
-27
lines changed

5 files changed

+94
-27
lines changed

pyk/src/pyk/kast/outer.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
top_down,
3131
)
3232
from .kast import kast_term
33+
from .prelude.k import K_ITEM, K
3334
from .rewrite import indexed_rewrite
3435

3536
if TYPE_CHECKING:
@@ -1460,13 +1461,15 @@ def transform(
14601461
for t, a in zip(prod.argument_sorts, term.args, strict=True):
14611462
if type(a) is KVariable:
14621463
occurrences[a.name].append(a.let_sort(t))
1463-
elif isinstance(term, KSequence) and term.arity > 0:
1464-
for a in term.items[0:-1]:
1465-
if type(a) is KVariable:
1466-
occurrences[a.name].append(a.let_sort(KSort('KItem')))
1467-
last_a = term.items[-1]
1468-
if type(last_a) is KVariable:
1469-
occurrences[last_a.name].append(last_a.let_sort(KSort('K')))
1464+
elif isinstance(term, KSequence):
1465+
for i, item in enumerate(term.items):
1466+
if isinstance(item, KVariable):
1467+
if item.sort is not None:
1468+
occurrences[item.name].append(item)
1469+
elif i == term.arity - 1:
1470+
occurrences[item.name].append(item.let_sort(K))
1471+
else:
1472+
occurrences[item.name].append(item.let_sort(K_ITEM))
14701473
return (term, occurrences)
14711474

14721475
(new_term, var_occurrences) = bottom_up_with_summary(transform, kast)

pyk/src/pyk/konvert/_kast_to_kore.py

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -82,41 +82,43 @@ def kast_to_kore(definition: KDefinition, kast: KInner, sort: KSort | None = Non
8282
kast = definition.sort_vars(kast, sort)
8383
kast = definition.add_cell_map_items(kast)
8484
kast = definition.add_sort_params(kast)
85-
kast = _replace_ksequence_by_kapply(kast)
85+
kast = _replace_ksequence_by_kapply(definition, kast)
8686
kast = _add_sort_injections(definition, kast, sort)
8787
kore = _kast_to_kore(kast)
8888
return kore
8989

9090

91-
def _replace_ksequence_by_kapply(term: KInner) -> KInner:
91+
def _replace_ksequence_by_kapply(definition: KDefinition, term: KInner) -> KInner:
9292
dotk = KApply('dotk')
9393
kseq = KLabel('kseq')
94+
append = KLabel('append')
9495

95-
def transform(term: KInner) -> KInner:
96-
match term:
97-
case KSequence(items):
98-
return transform_items(items)
99-
case _:
100-
return term
96+
def kapply(x: KInner, y: KInner) -> KApply:
97+
return append(x, y) if definition.sort_strict(x) == K else kseq(x, y)
98+
99+
def ksequence_to_kapply(term: KInner) -> KInner:
100+
if not isinstance(term, KSequence):
101+
return term
102+
103+
items = term.items
101104

102-
def transform_items(items: tuple[KInner, ...]) -> KInner:
103105
if not items:
104106
return dotk
105107

106108
unit: KInner
107109
args: tuple[KInner, ...]
108110

109111
last = items[-1]
110-
if isinstance(last, KVariable) and last.sort == K:
112+
if definition.sort_strict(last) == K:
111113
unit = last
112114
args = items[:-1]
113115
else:
114116
unit = dotk
115117
args = items
116118

117-
return reduce(lambda x, y: kseq(y, x), reversed(args), unit)
119+
return reduce(lambda x, y: kapply(y, x), reversed(args), unit)
118120

119-
return top_down(transform, term)
121+
return top_down(ksequence_to_kapply, term)
120122

121123

122124
def _add_sort_injections(definition: KDefinition, term: KInner, sort: KSort) -> KInner:
@@ -179,6 +181,8 @@ def _argument_sorts(definition: KDefinition, term: KInner) -> tuple[KSort, ...]:
179181
return (K_ITEM, K)
180182
case 'dotk':
181183
return ()
184+
case 'append':
185+
return (K, K)
182186
case '#Forall' | '#Exists':
183187
_, argument_sorts = definition.resolve_sorts(term.label)
184188
_, argument_sort = argument_sorts
@@ -202,7 +206,7 @@ def _let_arguments(term: KInner, args: list[KInner]) -> KInner:
202206
def _inject(definition: KDefinition, term: KInner, sort: KSort) -> KInner:
203207
actual_sort: KSort
204208
match term:
205-
case KApply(KLabel('kseq' | 'dotk')): # Special case: pseudo-labels
209+
case KApply(KLabel('kseq' | 'dotk' | 'append')): # Special case: pseudo-labels
206210
actual_sort = K
207211
case _:
208212
actual_sort = definition.sort_strict(term)
@@ -437,7 +441,7 @@ def _kapply_to_pattern(kapply: KApply, patterns: list[Pattern]) -> Pattern:
437441

438442

439443
def _label_to_kore(label: str) -> str:
440-
if label in ['inj', 'kseq', 'dotk']: # pseudo-labels introduced during KAST-to-KORE tranformation
444+
if label in ['inj', 'kseq', 'dotk', 'append']: # pseudo-labels introduced during KAST-to-KORE tranformation
441445
return label
442446

443447
if label in ML_PATTERN_LABELS:

pyk/src/pyk/konvert/_kore_to_kast.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ def _pattern_to_kast(pattern: Pattern, terms: list[KInner]) -> KInner:
9393
_, _ = terms
9494
return KSequence(terms)
9595

96+
elif symbol == 'append':
97+
_, _ = terms
98+
return KSequence(terms)
99+
96100
else:
97101
klabel = KLabel(unmunge(symbol[3:]))
98102
return KApply(klabel, terms)

pyk/src/tests/integration/konvert/test_simple_proofs.py

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,59 @@
211211
""",
212212
KSequence([KApply('foo_SIMPLE-PROOFS_KItem'), KApply('foo-bar_SIMPLE-PROOFS_Baz')]),
213213
),
214+
(
215+
'kseq-with-k-head',
216+
KSort('K'),
217+
r'append{}(VarX : SortK{}, kseq{}(inj{SortInt{}, SortKItem{}}(\dv{SortInt{}}("1")), dotk{}()))',
218+
KSequence(KVariable('X', 'K'), intToken(1)),
219+
),
220+
(
221+
'kseq-with-k-middle',
222+
KSort('K'),
223+
r"""
224+
kseq{}(
225+
inj{SortInt{}, SortKItem{}}(\dv{SortInt{}}("1")),
226+
append{}(
227+
VarX : SortK{},
228+
kseq{}(
229+
inj{SortInt{}, SortKItem{}}(\dv{SortInt{}}("2")),
230+
dotk{}()
231+
)
232+
)
233+
)
234+
""",
235+
KSequence(intToken(1), KVariable('X', 'K'), intToken(2)),
236+
),
237+
(
238+
'kseq-with-multiple-ks',
239+
KSort('K'),
240+
r"""
241+
kseq{}(
242+
inj{SortInt{}, SortKItem{}}(\dv{SortInt{}}("1")),
243+
append{}(
244+
VarX1 : SortK{},
245+
append{}(
246+
VarX2 : SortK{},
247+
append{}(
248+
VarX3 : SortK{},
249+
kseq{}(
250+
inj{SortInt{}, SortKItem{}}(\dv{SortInt{}}("2")),
251+
VarX4 : SortK{}
252+
)
253+
)
254+
)
255+
)
256+
)
257+
""",
258+
KSequence(
259+
intToken(1),
260+
KVariable('X1', 'K'),
261+
KVariable('X2', 'K'),
262+
KVariable('X3', 'K'),
263+
intToken(2),
264+
KVariable('X4', 'K'),
265+
),
266+
),
214267
(
215268
'if-then-else',
216269
KSort('K'),
@@ -699,20 +752,20 @@ def test_kast_to_kore(
699752
kast: KInner,
700753
) -> None:
701754
# Given
702-
kore = KoreParser(kore_text).pattern()
755+
expected_kore = KoreParser(kore_text).pattern()
703756

704757
# When
705758
actual_kore = kast_to_kore(definition, kast, sort=sort)
706759

707760
# Then
708-
assert actual_kore == kore
761+
assert actual_kore == expected_kore
709762

710763
@pytest.mark.parametrize(
711764
'test_id,sort,kore_text,kast',
712765
KAST_TO_KORE_TEST_DATA,
713766
ids=[test_id for test_id, *_ in KAST_TO_KORE_TEST_DATA],
714767
)
715-
def test_kast_to_kore_frontend_comp(
768+
def test_kast_to_kore_frontend(
716769
self,
717770
definition: KDefinition,
718771
test_id: str,
@@ -725,13 +778,13 @@ def test_kast_to_kore_frontend_comp(
725778
pytest.skip()
726779

727780
# Given
728-
frontend_kore = kprint.kast_to_kore(kast=kast, sort=sort, force_kast=True)
781+
expected_kore = KoreParser(kore_text).pattern()
729782

730783
# When
731-
actual_kore = kast_to_kore(definition, kast, sort=sort)
784+
actual_kore = kprint.kast_to_kore(kast=kast, sort=sort, force_kast=True)
732785

733786
# Then
734-
assert actual_kore == frontend_kore
787+
assert actual_kore == expected_kore
735788

736789
@pytest.mark.parametrize(
737790
'test_id,_sort,kore_text,kast',

pyk/src/tests/integration/test-data/frontend-comp-skip

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ if-then-else-no-sort-param-k
44
kseq-empty
55
kseq-singleton
66
kseq-two-element
7+
kseq-with-k-head
8+
kseq-with-k-middle
9+
kseq-with-multiple-ks
710
ksequence-duo-var-1
811
ksequence-duo-var-2
912
ksequence-empty

0 commit comments

Comments
 (0)