Skip to content
This repository was archived by the owner on Apr 25, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package/version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.1.342
0.1.343
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "pyk"
version = "0.1.342"
version = "0.1.343"
description = ""
authors = [
"Runtime Verification, Inc. <[email protected]>",
Expand Down
10 changes: 9 additions & 1 deletion src/pyk/kast/kast.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def _freeze(m: Any) -> Any:
if isinstance(m, (int, str, tuple, FrozenDict, frozenset)):
return m
elif isinstance(m, list):
return tuple(v for v in m)
return tuple(_freeze(v) for v in m)
elif isinstance(m, dict):
return FrozenDict((k, _freeze(v)) for (k, v) in m.items())
raise ValueError(f"Don't know how to freeze attribute value {m} of type {type(m)}.")
Expand Down Expand Up @@ -133,6 +133,14 @@ def update(self, atts: Mapping[str, Any]) -> KAtt:
def remove(self, atts: Iterable[str]) -> KAtt:
return KAtt({k: v for k, v in self.atts.items() if k not in atts})

def drop_source(self) -> KAtt:
new_atts = {
key: value
for key, value in self.atts.items()
if key != self.SOURCE and key != self.LOCATION
}
return KAtt(atts=new_atts)

@property
def pretty(self) -> str:
if len(self) == 0:
Expand Down
10 changes: 1 addition & 9 deletions src/pyk/kast/manip.py
Original file line number Diff line number Diff line change
Expand Up @@ -493,15 +493,7 @@ def minimize_rule(rule: RL, keep_vars: Iterable[str] = ()) -> RL:


def remove_source_map(definition: KDefinition) -> KDefinition:
def _remove_source_map(att: KAtt) -> KAtt:
atts = att.atts
new_atts = {}
for att_key in atts:
if att_key != 'org.kframework.attributes.Source' and att_key != 'org.kframework.attributes.Location':
new_atts[att_key] = atts[att_key]
return KAtt(atts=new_atts)

return on_attributes(definition, _remove_source_map)
return on_attributes(definition, lambda att: att.drop_source())


def remove_attrs(term: KInner) -> KInner:
Expand Down
10 changes: 9 additions & 1 deletion src/pyk/kast/outer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import json
import logging
from abc import abstractmethod
from collections import Counter
from collections.abc import Iterable
from dataclasses import dataclass
from enum import Enum
Expand Down Expand Up @@ -1029,10 +1030,17 @@ def production_for_klabel(self, klabel: KLabel) -> KProduction:
prods = [prod for prod in self.productions if prod.klabel and prod.klabel.name == klabel.name]
_prods = [prod for prod in prods if 'unparseAvoid' not in prod.att]
if len(_prods) < len(prods):
prods = _prods
_LOGGER.warning(
f'Discarding {len(prods) - len(_prods)} productions with `unparseAvoid` attribute for label: {klabel}'
)
prods = _prods
# Automatically defined symbols like isInt may get multiple
# definitions in different modules.
counter = Counter(prod.let_att(prod.att.drop_source()) for prod in prods)
_prods = list(counter)
if len(_prods) < len(prods):
_LOGGER.warning(f'Discarding {len(prods) - len(_prods)} equivalent productions')
Comment on lines +1039 to +1040
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

len(_prods) is the number of keys in counter. counter is a Mapping[KProduction, int] where the key is the representative of the equivalence class (i.e. the production without the source attribute), and the value is the number of productions that fall into the equivalence class (i.e. those that equal the representative when dropping the source attribute).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think that's correct. I'm not sure why you explained this, you probably wanted to point out something that I'm still missing.

All I'm saying here is that if I have 'n' production, which I group in 'm' equivalence classes, and I keep one production form each class, this means that I dropped all the other productions, i.e. I dropped n-m productions.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dropped n-m productions

I see, thanks for the clarification.

For me, the following order seems more logical though:

  • If there's more than one equivalence class, fail.
  • Otherwise, emit the log message about the number of discarded equivalent productions from the single class.

By the way, using this n-m trick, the Counter is unnecessary for both orderings: you can just use a set instead (converting to list is not necessary).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed it to a set.

To me, this code looks like a series of filters (remove 'unparseAvoid', remove equivalent productions), and other filters may be added in the future. To me, it makes sense to log each filter's activity, then move to the next step (and, for the last filter, the next step is to check how many productions are left and return)

prods = _prods
try:
self._production_for_klabel[klabel] = single(prods)
except ValueError as err:
Expand Down
20 changes: 20 additions & 0 deletions src/tests/integration/k-files/multiple-definitions.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module MULTIPLE-DEFINITIONS
imports A
imports B
imports BOOL

syntax KItem ::= a(KItem) [symbol, label(a)]
| "b" [symbol, label(b)]
| "c" [symbol, label(c)]
rule a(X) => b requires isInt(X)
rule a(X) => b requires notBool isInt(X)
rule b => c
endmodule

module A
syntax Int
endmodule

module B
syntax Int
endmodule
70 changes: 70 additions & 0 deletions src/tests/integration/kcfg/test_multiple_definitions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from __future__ import annotations

from typing import TYPE_CHECKING

import pytest

from pyk.cterm import CTerm
from pyk.kast.inner import KApply, KSequence, KVariable
from pyk.prelude.ml import mlTop
from pyk.testing import KCFGExploreTest

from ..utils import K_FILES

if TYPE_CHECKING:
from collections.abc import Iterable

from pyk.kcfg import KCFGExplore


EXECUTE_TEST_DATA: Iterable[tuple[str]] = (('branch',),)


class TestMultipleDefinitionsProof(KCFGExploreTest):
KOMPILE_MAIN_FILE = K_FILES / 'multiple-definitions.k'

@staticmethod
def config() -> CTerm:
return CTerm(
KApply(
'<generatedTop>',
KApply('<k>', KSequence(KApply('a', [KVariable('X')]))),
KVariable('GENERATED_COUNTER_CELL'),
),
(mlTop(),),
)

@pytest.mark.parametrize(
'test_id',
EXECUTE_TEST_DATA,
ids=[test_id for test_id, *_ in EXECUTE_TEST_DATA],
)
def test_execute(
self,
kcfg_explore: KCFGExplore,
test_id: str,
) -> None:
split_depth, split_post_term, split_next_terms, _logs = kcfg_explore.cterm_execute(self.config(), depth=1)

split_k = kcfg_explore.kprint.pretty_print(split_post_term.cell('K_CELL'))
split_next_k = [kcfg_explore.kprint.pretty_print(split_post_term.cell('K_CELL')) for term in split_next_terms]

assert split_depth == 0
assert len(split_next_terms) == 2
assert 'a ( X:KItem )' == split_k
assert [
'a ( X:KItem )',
'a ( X:KItem )',
] == split_next_k

step_1_depth, step_1_post_term, step_1_next_terms, _logs = kcfg_explore.cterm_execute(
split_next_terms[0], depth=1
)
step_1_k = kcfg_explore.kprint.pretty_print(step_1_post_term.cell('K_CELL'))
assert 'c' == step_1_k

step_2_depth, step_2_post_term, step_2_next_terms, _logs = kcfg_explore.cterm_execute(
split_next_terms[1], depth=1
)
step_2_k = kcfg_explore.kprint.pretty_print(step_1_post_term.cell('K_CELL'))
assert 'c' == step_2_k