curryparty 0.2.0__py3-none-any.whl → 0.2.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- curryparty/__init__.py +66 -21
- curryparty/display.py +4 -0
- curryparty/utils.py +26 -3
- {curryparty-0.2.0.dist-info → curryparty-0.2.3.dist-info}/METADATA +1 -1
- curryparty-0.2.3.dist-info/RECORD +8 -0
- {curryparty-0.2.0.dist-info → curryparty-0.2.3.dist-info}/WHEEL +2 -2
- curryparty-0.2.0.dist-info/RECORD +0 -8
curryparty/__init__.py
CHANGED
|
@@ -6,10 +6,13 @@ except ImportError:
|
|
|
6
6
|
raise ImportError(
|
|
7
7
|
"curryparty needs the `polars` library. \n Please install it, typically with `pip install polars`"
|
|
8
8
|
)
|
|
9
|
-
|
|
9
|
+
import uuid
|
|
10
|
+
|
|
11
|
+
from svg import SVG, Rect
|
|
10
12
|
|
|
11
13
|
from .core import SCHEMA, beta_reduce, compose, find_redexes, find_variables, subtree
|
|
12
14
|
from .display import (
|
|
15
|
+
compute_height,
|
|
13
16
|
compute_svg_frame_final,
|
|
14
17
|
compute_svg_frame_init,
|
|
15
18
|
compute_svg_frame_phase_a,
|
|
@@ -55,23 +58,29 @@ class Term:
|
|
|
55
58
|
if term is None:
|
|
56
59
|
break
|
|
57
60
|
|
|
58
|
-
def show_beta(self):
|
|
61
|
+
def show_beta(self, x0=-10, width=30):
|
|
62
|
+
"""
|
|
63
|
+
Generates an HTML representation that toggles visibility between
|
|
64
|
+
a static state and a SMIL animation on hover using pure CSS.
|
|
65
|
+
"""
|
|
59
66
|
candidates = find_redexes(self.nodes)
|
|
60
67
|
if len(candidates) == 0:
|
|
61
|
-
return
|
|
68
|
+
return self._repr_html_()
|
|
69
|
+
|
|
62
70
|
_redex, lamb, b = candidates.row(0)
|
|
63
71
|
new_nodes = beta_reduce(self.nodes, lamb, b)
|
|
64
72
|
vars = find_variables(self.nodes, lamb)["id"]
|
|
65
73
|
b_subtree = subtree(self.nodes, b)
|
|
74
|
+
height = compute_height(self.nodes) + 3
|
|
66
75
|
shapes: dict[int, ShapeAnim] = {}
|
|
67
|
-
N_STEPS =
|
|
76
|
+
N_STEPS = 6
|
|
68
77
|
|
|
69
78
|
for t in range(N_STEPS):
|
|
70
|
-
if t
|
|
79
|
+
if t == 0:
|
|
71
80
|
items = compute_svg_frame_init(self.nodes)
|
|
72
|
-
elif t ==
|
|
81
|
+
elif t == 1 or t == 2:
|
|
73
82
|
items = compute_svg_frame_phase_a(self.nodes, lamb, b_subtree, vars)
|
|
74
|
-
elif t ==
|
|
83
|
+
elif t == 3 or t == 4:
|
|
75
84
|
items = compute_svg_frame_phase_b(
|
|
76
85
|
self.nodes, lamb, b_subtree, new_nodes
|
|
77
86
|
)
|
|
@@ -82,33 +91,58 @@ class Term:
|
|
|
82
91
|
shapes[k] = ShapeAnim(e)
|
|
83
92
|
shapes[k].append_frame(t, attributes.items())
|
|
84
93
|
|
|
85
|
-
|
|
86
|
-
|
|
94
|
+
figure_id = uuid.uuid4()
|
|
95
|
+
box_id = f"lambda_box_{figure_id}".replace("-", "")
|
|
96
|
+
anim_elements = [
|
|
97
|
+
x.to_element(N_STEPS, begin=f"{box_id}.click", reset=f"{box_id}.mouseover")
|
|
98
|
+
for x in shapes.values()
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
anim_elements.append(
|
|
102
|
+
Rect(
|
|
103
|
+
id=box_id,
|
|
104
|
+
x=str(x0),
|
|
105
|
+
y="0",
|
|
106
|
+
width="100%",
|
|
107
|
+
height="100%",
|
|
108
|
+
fill="transparent",
|
|
109
|
+
)
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
anim_svg = SVG(
|
|
113
|
+
xmlns="http://www.w3.org/2000/svg",
|
|
114
|
+
viewBox=f"{x0} 0 {width} {height}",
|
|
115
|
+
height=f"{35 * height}px",
|
|
116
|
+
elements=anim_elements,
|
|
117
|
+
).as_str()
|
|
118
|
+
|
|
87
119
|
return Html(
|
|
88
|
-
|
|
89
|
-
xmlns="http://www.w3.org/2000/svg",
|
|
90
|
-
viewBox=f"-10 0 30 {height}", # type: ignore
|
|
91
|
-
height=f"{35 * height}px", # type: ignore
|
|
92
|
-
elements=elements,
|
|
93
|
-
).as_str()
|
|
120
|
+
f'<div><div style="margin:5px">click to animate, move away and back to reset</div>{anim_svg}</div>'
|
|
94
121
|
)
|
|
95
122
|
|
|
96
|
-
def _repr_html_(self):
|
|
123
|
+
def _repr_html_(self, x0=-10, width=30):
|
|
97
124
|
frame = compute_svg_frame_init(self.nodes)
|
|
98
125
|
elements = []
|
|
99
|
-
|
|
126
|
+
|
|
127
|
+
height = compute_height(self.nodes) + 1
|
|
100
128
|
for _, e, attributes in frame:
|
|
101
129
|
for name, v in attributes.items():
|
|
102
130
|
e.__setattr__(name, v)
|
|
103
131
|
elements.append(e)
|
|
104
132
|
return SVG(
|
|
105
133
|
xmlns="http://www.w3.org/2000/svg",
|
|
106
|
-
viewBox=f"
|
|
134
|
+
viewBox=f"{x0} 0 {width} {height}", # type: ignore
|
|
107
135
|
height=f"{35 * height}px", # type: ignore
|
|
108
136
|
elements=elements,
|
|
109
137
|
).as_str()
|
|
110
138
|
|
|
111
139
|
|
|
140
|
+
def offset_var(x: Union[int, str], offset: int) -> Union[int, str]:
|
|
141
|
+
if isinstance(x, int):
|
|
142
|
+
return x + offset
|
|
143
|
+
return x
|
|
144
|
+
|
|
145
|
+
|
|
112
146
|
class L:
|
|
113
147
|
def __init__(self, *lambda_names):
|
|
114
148
|
self.n = len(lambda_names)
|
|
@@ -126,7 +160,7 @@ class L:
|
|
|
126
160
|
if isinstance(t, L):
|
|
127
161
|
offset = self.n
|
|
128
162
|
for i, x in t.refs:
|
|
129
|
-
self.refs.append((offset + i, t.lambdas.get(x, x)))
|
|
163
|
+
self.refs.append((offset + i, offset_var(t.lambdas.get(x, x), offset)))
|
|
130
164
|
|
|
131
165
|
for i, x in t.args:
|
|
132
166
|
self.args.append((offset + i, offset + x))
|
|
@@ -143,7 +177,10 @@ class L:
|
|
|
143
177
|
|
|
144
178
|
def call(self, arg: Union[str, "L"]) -> "L":
|
|
145
179
|
assert self.last_ is not None
|
|
146
|
-
self.refs = [
|
|
180
|
+
self.refs = [
|
|
181
|
+
(i + 1, offset_var(x, 1)) if i >= self.last_ else (i, x)
|
|
182
|
+
for (i, x) in self.refs
|
|
183
|
+
]
|
|
147
184
|
self.args = [
|
|
148
185
|
(i + 1, x + 1) if i >= self.last_ else (i, x) for (i, x) in self.args
|
|
149
186
|
]
|
|
@@ -155,7 +192,15 @@ class L:
|
|
|
155
192
|
return self
|
|
156
193
|
|
|
157
194
|
def build(self) -> "Term":
|
|
158
|
-
|
|
195
|
+
def bind_var(x: Union[str, int]) -> int:
|
|
196
|
+
# check that all remaining unbound variables are bound to this lambda
|
|
197
|
+
if isinstance(x, int):
|
|
198
|
+
return x
|
|
199
|
+
if x not in self.lambdas:
|
|
200
|
+
raise ValueError(f"variable {x} is not bound to any lambda")
|
|
201
|
+
return self.lambdas[x]
|
|
202
|
+
|
|
203
|
+
self.refs = [(i, bind_var(x)) for i, x in self.refs]
|
|
159
204
|
ref = pl.from_records(
|
|
160
205
|
self.refs, orient="row", schema={"id": pl.UInt32, "ref": pl.UInt32}
|
|
161
206
|
)
|
curryparty/display.py
CHANGED
|
@@ -38,6 +38,10 @@ def compute_layout(
|
|
|
38
38
|
y[node] = y[child] | y[node]
|
|
39
39
|
return x, y
|
|
40
40
|
|
|
41
|
+
def compute_height(nodes: pl.DataFrame):
|
|
42
|
+
_, y = compute_layout(nodes)
|
|
43
|
+
return max(interval[1] for interval in y.values() if interval)
|
|
44
|
+
|
|
41
45
|
|
|
42
46
|
def draw(
|
|
43
47
|
x: dict[Union[int, tuple[int, int]], Interval],
|
curryparty/utils.py
CHANGED
|
@@ -56,7 +56,7 @@ class ShapeAnim:
|
|
|
56
56
|
self.attributes.add(name)
|
|
57
57
|
self.values[i, name] = v
|
|
58
58
|
|
|
59
|
-
def to_element(self, n: int):
|
|
59
|
+
def to_element(self, n: int, begin: str, reset: str):
|
|
60
60
|
elements = []
|
|
61
61
|
|
|
62
62
|
visible = [
|
|
@@ -74,12 +74,24 @@ class ShapeAnim:
|
|
|
74
74
|
for i in range(n):
|
|
75
75
|
if (i, name) not in self.values:
|
|
76
76
|
self.values[i, name] = non_nulls[0]
|
|
77
|
+
self.shape.__setattr__(name, non_nulls[0])
|
|
78
|
+
|
|
77
79
|
elements.append(
|
|
78
80
|
Animate(
|
|
79
81
|
attributeName=name,
|
|
80
82
|
values=";".join(str(self.values[i, name]) for i in range(n)),
|
|
81
83
|
dur=timedelta(seconds=self.duration),
|
|
82
|
-
|
|
84
|
+
begin=begin,
|
|
85
|
+
fill="freeze",
|
|
86
|
+
repeatCount="1",
|
|
87
|
+
)
|
|
88
|
+
)
|
|
89
|
+
elements.append(
|
|
90
|
+
Animate(
|
|
91
|
+
attributeName=name,
|
|
92
|
+
values=f"{non_nulls[0]}",
|
|
93
|
+
dur=0,
|
|
94
|
+
begin=reset,
|
|
83
95
|
)
|
|
84
96
|
)
|
|
85
97
|
elements.append(
|
|
@@ -87,10 +99,21 @@ class ShapeAnim:
|
|
|
87
99
|
attributeName="opacity",
|
|
88
100
|
values=";".join("1" if v else "0" for v in visible),
|
|
89
101
|
dur=timedelta(seconds=self.duration),
|
|
90
|
-
|
|
102
|
+
begin=begin,
|
|
103
|
+
fill="freeze",
|
|
104
|
+
repeatCount="1",
|
|
105
|
+
)
|
|
106
|
+
)
|
|
107
|
+
elements.append(
|
|
108
|
+
Animate(
|
|
109
|
+
attributeName="opacity",
|
|
110
|
+
values="1" if visible[0] else "0",
|
|
111
|
+
dur=0,
|
|
112
|
+
begin=reset,
|
|
91
113
|
)
|
|
92
114
|
)
|
|
93
115
|
|
|
94
116
|
assert not self.shape.elements
|
|
117
|
+
self.shape
|
|
95
118
|
self.shape.elements = elements
|
|
96
119
|
return self.shape
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
curryparty/__init__.py,sha256=kizUbEeGQbTtKEnv9k2PDfCedyXeQoBTWhlLvgCbG1k,7057
|
|
2
|
+
curryparty/core.py,sha256=9vQJlUeYXJ71SkRV6tUiw1RSu7gUJu4f_e6w2Zt-tLQ,3665
|
|
3
|
+
curryparty/display.py,sha256=3u80YvTYBe3mfexng2fiIiNwNAb_S7eYE5afY08f49M,6925
|
|
4
|
+
curryparty/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
curryparty/utils.py,sha256=XvOGyYiIuMcmi-bnBNLdgSQQ69vT0pJ3qJXgwmfhK28,3494
|
|
6
|
+
curryparty-0.2.3.dist-info/WHEEL,sha256=e_m4S054HL0hyR3CpOk-b7Q7fDX6BuFkgL5OjAExXas,80
|
|
7
|
+
curryparty-0.2.3.dist-info/METADATA,sha256=0h8jSXKNY3_ECpRJJJEpcqru-7zfNKi56Yx5l38Iro8,2139
|
|
8
|
+
curryparty-0.2.3.dist-info/RECORD,,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
curryparty/__init__.py,sha256=VvTj-jDrxDqR9g-tg0IHPx81JAc6xBrRstXzyUpdtu4,5744
|
|
2
|
-
curryparty/core.py,sha256=9vQJlUeYXJ71SkRV6tUiw1RSu7gUJu4f_e6w2Zt-tLQ,3665
|
|
3
|
-
curryparty/display.py,sha256=8PRRP-9ys-q0OEvIhePxM9di_2n0WRkwA13qK5sq4JU,6783
|
|
4
|
-
curryparty/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
-
curryparty/utils.py,sha256=Q5vYGQ1PvxDPntjF39D-6geAwDDdXz7pVFiAyUGE4WM,2839
|
|
6
|
-
curryparty-0.2.0.dist-info/WHEEL,sha256=KSLUh82mDPEPk0Bx0ScXlWL64bc8KmzIPNcpQZFV-6E,79
|
|
7
|
-
curryparty-0.2.0.dist-info/METADATA,sha256=GfshNERaRDxxfFSXbpKcFf7wEmgRLY6N-yQ-0bhw0sI,2139
|
|
8
|
-
curryparty-0.2.0.dist-info/RECORD,,
|