Skip to content
This repository was archived by the owner on Apr 25, 2024. It is now read-only.

Commit 778cd00

Browse files
committed
Merge branch 'noah/anti-unify-sort-fix' of https://github.com/runtimeverification/pyk into noah/anti-unify-sort-fix
2 parents b2ad557 + a8222ac commit 778cd00

30 files changed

+2523
-302
lines changed

deps/k_release

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
6.0.44
1+
6.0.50

package/version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.1.405
1+
0.1.412

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
44

55
[tool.poetry]
66
name = "pyk"
7-
version = "0.1.405"
7+
version = "0.1.412"
88
description = ""
99
authors = [
1010
"Runtime Verification, Inc. <[email protected]>",

src/pyk/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@
66
from typing import Final
77

88

9-
K_VERSION: Final = '6.0.44'
9+
K_VERSION: Final = '6.0.50'

src/pyk/kast/markdown.py

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
from __future__ import annotations
2+
3+
import re
4+
from abc import ABC, abstractmethod
5+
from dataclasses import dataclass
6+
from typing import TYPE_CHECKING, NamedTuple, final
7+
8+
if TYPE_CHECKING:
9+
from collections.abc import Container, Iterable, Iterator
10+
from typing import Final
11+
12+
13+
def select_code_blocks(text: str, selector: str | None = None) -> str:
14+
_selector = SelectorParser(selector).parse() if selector else None
15+
16+
def selected(code_block: CodeBlock) -> bool:
17+
if _selector is None:
18+
return True
19+
20+
tags = parse_tags(code_block.info)
21+
return _selector.eval(tags)
22+
23+
return '\n'.join(code_block.code for code_block in code_blocks(text) if selected(code_block))
24+
25+
26+
class CodeBlock(NamedTuple):
27+
info: str
28+
code: str
29+
30+
31+
_CODE_BLOCK_PATTERN: Final = re.compile(
32+
r'(^|(?<=\n)) {0,3}(?P<fence>```+)(?!`)(?P<info>.*)\n(?P<code>(.*\n)*?) {0,3}(?P=fence)`*'
33+
)
34+
35+
36+
def code_blocks(text: str) -> Iterator[CodeBlock]:
37+
return (CodeBlock(match['info'], match['code'].rstrip()) for match in _CODE_BLOCK_PATTERN.finditer(text))
38+
39+
40+
def parse_tags(text: str) -> set[str]:
41+
def check_tag(tag: str) -> None:
42+
if not (tag and all(c.isalnum() or c == '_' for c in tag)):
43+
raise ValueError(f'Invalid tag: {tag!r}')
44+
45+
if not text:
46+
return set()
47+
48+
if text[0] != '{':
49+
check_tag(text)
50+
return {text}
51+
52+
if text[-1] != '}':
53+
raise ValueError("Expected '}', found: {text[-1]!r}")
54+
55+
res: set[str] = set()
56+
tags = text[1:-1].split()
57+
for tag in tags:
58+
if tag[0] != '.':
59+
raise ValueError("Expected '.', found: {tag[0]!r}")
60+
check_tag(tag[1:])
61+
res.add(tag[1:])
62+
63+
return res
64+
65+
66+
# ----------------------
67+
# Selector mini-language
68+
# ----------------------
69+
70+
71+
class Selector(ABC):
72+
@abstractmethod
73+
def eval(self, atoms: Container[str]) -> bool:
74+
...
75+
76+
77+
@final
78+
@dataclass(frozen=True)
79+
class Atom(Selector):
80+
name: str
81+
82+
def eval(self, atoms: Container[str]) -> bool:
83+
return self.name in atoms
84+
85+
86+
@final
87+
@dataclass(frozen=True)
88+
class Not(Selector):
89+
op: Selector
90+
91+
def eval(self, atoms: Container[str]) -> bool:
92+
return not self.op.eval(atoms)
93+
94+
95+
@final
96+
@dataclass(frozen=True)
97+
class And(Selector):
98+
ops: tuple[Selector, ...]
99+
100+
def eval(self, atoms: Container[str]) -> bool:
101+
return all(op.eval(atoms) for op in self.ops)
102+
103+
104+
@final
105+
@dataclass(frozen=True)
106+
class Or(Selector):
107+
ops: tuple[Selector, ...]
108+
109+
def eval(self, atoms: Container[str]) -> bool:
110+
return any(op.eval(atoms) for op in self.ops)
111+
112+
113+
_SPECIAL = tuple('!&|()')
114+
115+
116+
def selector_lexer(it: Iterable[str]) -> Iterator[str]:
117+
it = iter(it)
118+
la = next(it, '')
119+
while True:
120+
while la.isspace():
121+
la = next(it, '')
122+
123+
if not la:
124+
yield ''
125+
return
126+
127+
if la in _SPECIAL:
128+
yield la
129+
la = next(it, '')
130+
continue
131+
132+
buf: list[str] = []
133+
while la.isalnum() or la == '_':
134+
buf.append(la)
135+
la = next(it, '')
136+
137+
if not buf:
138+
raise ValueError(f'Unexpected character: {la!r}')
139+
140+
yield ''.join(buf)
141+
142+
143+
class SelectorParser:
144+
_la: str
145+
_it: Iterator[str]
146+
147+
def __init__(self, selector: str):
148+
self._it = selector_lexer(selector)
149+
self._consume()
150+
151+
def _consume(self) -> None:
152+
self._la = next(self._it, '')
153+
154+
def _match(self, token: str) -> None:
155+
if self._la != token:
156+
raise ValueError('Unexpected token: {token}')
157+
self._consume()
158+
159+
def parse(self) -> Selector:
160+
res = self._or()
161+
if self._la:
162+
raise ValueError(f'Expected EOF, found: {self._la}')
163+
return res
164+
165+
def _or(self) -> Selector:
166+
ops = [self._and()]
167+
while self._la == '|':
168+
self._consume()
169+
ops.append(self._and())
170+
if len(ops) > 1:
171+
return Or(tuple(ops))
172+
return ops[0]
173+
174+
def _and(self) -> Selector:
175+
ops = [self._lit()]
176+
while self._la == '&':
177+
self._consume()
178+
ops.append(self._lit())
179+
if len(ops) > 1:
180+
return And(tuple(ops))
181+
return ops[0]
182+
183+
def _lit(self) -> Selector:
184+
if not self._la:
185+
raise ValueError('Unexpected EOF')
186+
187+
if self._la == '(':
188+
self._consume()
189+
expr = self._or()
190+
self._match(')')
191+
return expr
192+
193+
if self._la == '!':
194+
self._consume()
195+
lit = self._lit()
196+
return Not(lit)
197+
198+
if len(self._la) > 1 or self._la.isalnum() or self._la == '-':
199+
atom = self._la
200+
self._consume()
201+
return Atom(atom)
202+
203+
raise ValueError(f'Unexpected token: {self._la}')

src/pyk/kore/rpc.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,8 @@ def request(self, method: str, **params: Any) -> dict[str, Any]:
232232

233233
_LOGGER.debug(f'Sending request to {server_addr}: {req}')
234234
resp = self._transport.request(req)
235+
if not resp:
236+
raise RuntimeError('Empty response received')
235237
_LOGGER.debug(f'Received response from {server_addr}: {resp}')
236238

237239
if self._bug_report:

src/pyk/ktool/kprint.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from ..kore.kompiled import KompiledKore
2020
from ..kore.parser import KoreParser
2121
from ..kore.syntax import App, SortApp
22+
from ..kore.tools import PrintOutput, kore_print
2223
from ..utils import run_process
2324
from .kompile import DefinitionInfo
2425

@@ -258,13 +259,8 @@ def kore_to_kast(self, kore: Pattern) -> KInner:
258259
except ValueError as err:
259260
_LOGGER.warning(err)
260261

261-
_LOGGER.warning(f'Falling back to using `kast` for Kore -> Kast: {kore.text}')
262-
proc_res = self._expression_kast(
263-
kore.text,
264-
input=KAstInput.KORE,
265-
output=KAstOutput.JSON,
266-
)
267-
return kast_term(json.loads(proc_res.stdout), KInner) # type: ignore # https://github.com/python/mypy/issues/4717
262+
_LOGGER.warning(f'Falling back to using `kore-print` for Kore -> Kast: {kore.text}')
263+
return kast_term(json.loads(kore_print(kore, self.definition_dir, PrintOutput.JSON)), KInner) # type: ignore # https://github.com/python/mypy/issues/4717
268264

269265
def kast_to_kore(self, kast: KInner, sort: KSort | None = None) -> Pattern:
270266
try:

0 commit comments

Comments
 (0)