Showing
5 changed files
with
301 additions
and
0 deletions
.gitignore
0 → 100644
codanim/__init__.py
0 → 100644
1 | +class ExecEnv (dict) : | ||
2 | + def __init__ (self, *l, **k) : | ||
3 | + super().__init__(*l, **k) | ||
4 | + def __setitem__ (self, key, val) : | ||
5 | + old = self.get(key, None) | ||
6 | + try : | ||
7 | + old.set(val) | ||
8 | + except AttributeError : | ||
9 | + super().__setitem__(key, val) | ||
10 | + def __getitem__ (self, key) : | ||
11 | + old = self.get(key, None) | ||
12 | + try : | ||
13 | + return old.get() | ||
14 | + except AttributeError : | ||
15 | + return super().__getitem__(key) | ||
16 | + def exec (self, code) : | ||
17 | + exec(code, self) | ||
18 | + def eval (self, code) : | ||
19 | + return eval(code, self) | ||
20 | + | ||
21 | +class CAni (object) : | ||
22 | + _env = ExecEnv() | ||
23 | + @property | ||
24 | + def IP (self) : | ||
25 | + return self._env.get("IP", 1) | ||
26 | + @IP.setter | ||
27 | + def IP (self, val) : | ||
28 | + self._env["IP"] = val | ||
29 | + @property | ||
30 | + def RET (self) : | ||
31 | + return self._env.get("RET", None) | ||
32 | + @RET.setter | ||
33 | + def RET (self, val) : | ||
34 | + self._env["RET"] = val | ||
35 | + def exec (self, code) : | ||
36 | + self._env.exec(code) | ||
37 | + def eval (self, code) : | ||
38 | + RET = self.RET = self._env.eval(code) | ||
39 | + return RET |
codanim/data.py
0 → 100644
This diff is collapsed. Click to expand it.
codanim/flow.py
0 → 100644
1 | +from inspect import Signature, Parameter | ||
2 | + | ||
3 | +from . import CAni | ||
4 | +from .highlight import pygmentize | ||
5 | + | ||
6 | +class _CODE (CAni) : | ||
7 | + _fields = [] | ||
8 | + _options = [] | ||
9 | + def __init__ (self, *l, **k) : | ||
10 | + params = [] | ||
11 | + for i, name in enumerate(self._fields) : | ||
12 | + if name[0] == "*" : | ||
13 | + self._fields = self._fields[:] | ||
14 | + self._fields[i] = name[1:] | ||
15 | + params.append(Parameter(name[1:], Parameter.VAR_POSITIONAL)) | ||
16 | + else : | ||
17 | + params.append(Parameter(name, Parameter.POSITIONAL_OR_KEYWORD)) | ||
18 | + for name in self._options : | ||
19 | + params.append(Parameter(name, Parameter.POSITIONAL_OR_KEYWORD, default=None)) | ||
20 | + params.append(Parameter("src", Parameter.KEYWORD_ONLY, default=None)) | ||
21 | + sig = Signature(params) | ||
22 | + args = sig.bind(*l, **k) | ||
23 | + args.apply_defaults() | ||
24 | + for key, val in args.arguments.items() : | ||
25 | + setattr(self, key, val) | ||
26 | + self._at = set() | ||
27 | + def __str__ (self) : | ||
28 | + content = [] | ||
29 | + for key, val in self.items() : | ||
30 | + if isinstance(val, _CODE) : | ||
31 | + content.append((key, str(val))) | ||
32 | + else : | ||
33 | + content.append((key, repr(val))) | ||
34 | + return "%s(%s)" % (self.__class__.__name__, | ||
35 | + ", ".join("%s=%r" % item for item in content)) | ||
36 | + def items (self) : | ||
37 | + for field in self._fields : | ||
38 | + yield field, getattr(self, field) | ||
39 | + for field in self._options : | ||
40 | + member = getattr(self, field, None) | ||
41 | + if member is not None : | ||
42 | + yield field, member | ||
43 | + def source (self) : | ||
44 | + sub = {} | ||
45 | + for key, val in self.items() : | ||
46 | + if isinstance(val, _CODE) : | ||
47 | + sub[key] = val.source() | ||
48 | + else : | ||
49 | + sub[key] = val | ||
50 | + return self.src.format(**sub) | ||
51 | + def tex (self) : | ||
52 | + sub = self.src.format(**{key : "$" for key, val in self.items()}).split("$") | ||
53 | + parts = [pygmentize(sub[0])] | ||
54 | + for (key, val), txt in zip(self.items(), sub[1:]) : | ||
55 | + if isinstance(val, _CODE) : | ||
56 | + parts.append(val.tex()) | ||
57 | + else : | ||
58 | + parts.append(pygmentize(str(val))) | ||
59 | + parts.append(pygmentize(txt)) | ||
60 | + tex = "".join(parts) | ||
61 | + if self._at : | ||
62 | + return r"\onlyhl{%s}{" % ",".join(str(i) for i in self._at) + tex + "}" | ||
63 | + else : | ||
64 | + return tex | ||
65 | + | ||
66 | +class BLOCK (_CODE) : | ||
67 | + _fields = ["*body"] | ||
68 | + def __call__ (self) : | ||
69 | + self._at.add(self.IP) | ||
70 | + for code in self.body : | ||
71 | + code() | ||
72 | + def source (self) : | ||
73 | + return "".join(b.source() for b in self.body) | ||
74 | + def tex (self) : | ||
75 | + return "".join(b.tex() for b in self.body) | ||
76 | + | ||
77 | +class STMT (_CODE) : | ||
78 | + _fields = ["*steps"] | ||
79 | + def __call__ (self) : | ||
80 | + for s in self.steps : | ||
81 | + self._at.add(self.IP) | ||
82 | + self.exec(s) | ||
83 | + self.IP += 1 | ||
84 | + | ||
85 | +class EXPR (_CODE) : | ||
86 | + _fields = ["expr"] | ||
87 | + def __init__ (self, *l, **k) : | ||
88 | + super().__init__(*l, **k) | ||
89 | + if self.src is None : | ||
90 | + self.src = self.expr | ||
91 | + def __call__ (self) : | ||
92 | + self._at.add(self.IP) | ||
93 | + self.eval(self.expr) | ||
94 | + self.IP += 1 | ||
95 | + | ||
96 | +class PY (_CODE) : | ||
97 | + _fields = ["py"] | ||
98 | + def __call__ (self) : | ||
99 | + self.exec(self.py) | ||
100 | + def tex (self) : | ||
101 | + return "" | ||
102 | + def source (self) : | ||
103 | + return "" | ||
104 | + | ||
105 | +class ENV (_CODE) : | ||
106 | + _fields = ["name", "value"] | ||
107 | + def __call__ (self) : | ||
108 | + self._env[self.name] = self.value | ||
109 | + def tex (self) : | ||
110 | + return "" | ||
111 | + def source (self) : | ||
112 | + return "" | ||
113 | + | ||
114 | +class WS (_CODE) : | ||
115 | + _fields = [] | ||
116 | + def __init__ (self, src) : | ||
117 | + super().__init__(src=src) | ||
118 | + def __call__ (self) : | ||
119 | + pass | ||
120 | + def tex (self) : | ||
121 | + return self.src | ||
122 | + | ||
123 | +class XDECL (_CODE) : | ||
124 | + _fields = ["*names"] | ||
125 | + def __call__ (self) : | ||
126 | + for name in self.names : | ||
127 | + self._env[name] = None | ||
128 | + self._at.add(self.IP) | ||
129 | + self.IP += 1 | ||
130 | + | ||
131 | +class DECL (_CODE) : | ||
132 | + _fields = ["name"] | ||
133 | + _options = ["init"] | ||
134 | + def __call__ (self) : | ||
135 | + if self.init is not None : | ||
136 | + self.init() | ||
137 | + self._env[self.name] = self.RET | ||
138 | + else : | ||
139 | + self._env[self.name] = None | ||
140 | + self._at.add(self.IP) | ||
141 | + self.IP += 1 | ||
142 | + def tex (self) : | ||
143 | + src = super().tex() | ||
144 | + if self.animate is None : | ||
145 | + return src | ||
146 | + else : | ||
147 | + return src + " " + "".join(self._tex()) | ||
148 | + def _tex (self) : | ||
149 | + tail = r"\PY{{c+c1}}{{/* {value} */}}" | ||
150 | + for value, start, stop in self._cell.hist : | ||
151 | + if value is not None : | ||
152 | + yield (r"\onlyshow{{{start}-{stop}}}{{{value}}}" | ||
153 | + r"").format(start=start or 1, | ||
154 | + stop=stop or "", | ||
155 | + value=tail.format(value=value)) | ||
156 | + | ||
157 | +class BreakLoop (Exception) : | ||
158 | + def __init__ (self) : | ||
159 | + super().__init__() | ||
160 | + | ||
161 | +class BREAK (_CODE) : | ||
162 | + def __call__ (self) : | ||
163 | + self._at.add(self.IP) | ||
164 | + self.IP += 1 | ||
165 | + raise BreakLoop() | ||
166 | + | ||
167 | +class FunctionReturn (Exception) : | ||
168 | + def __init__ (self, RET) : | ||
169 | + super().__init__() | ||
170 | + self.RET = RET | ||
171 | + | ||
172 | +class RETURN (_CODE) : | ||
173 | + _fields = ["value"] | ||
174 | + def __call__ (self) : | ||
175 | + self.value() | ||
176 | + self._at.add(self.IP) | ||
177 | + self.IP += 1 | ||
178 | + raise FunctionReturn(self.RET) | ||
179 | + | ||
180 | +class IF (_CODE) : | ||
181 | + _fields = ["cond", "then"] | ||
182 | + _options = ["otherwise"] | ||
183 | + def __call__ (self) : | ||
184 | + self.cond() | ||
185 | + if self.RET : | ||
186 | + self.then() | ||
187 | + elif self.otherwise is not None : | ||
188 | + self.otherwise() | ||
189 | + | ||
190 | +class WHILE (_CODE) : | ||
191 | + _fields = ["cond", "body"] | ||
192 | + def __call__ (self) : | ||
193 | + try : | ||
194 | + while True : | ||
195 | + self.cond() | ||
196 | + if not self.RET : | ||
197 | + break | ||
198 | + self.body() | ||
199 | + except BreakLoop : | ||
200 | + return | ||
201 | + | ||
202 | +class DO (_CODE) : | ||
203 | + _fields = ["body", "cond"] | ||
204 | + def __call__ (self) : | ||
205 | + try : | ||
206 | + while True : | ||
207 | + self.body() | ||
208 | + self.cond() | ||
209 | + if not self.RET : | ||
210 | + break | ||
211 | + except BreakLoop : | ||
212 | + pass | ||
213 | + | ||
214 | +class FOR (_CODE) : | ||
215 | + _fields = ["init", "cond", "step", "body"] | ||
216 | + def __call__ (self) : | ||
217 | + self.init() | ||
218 | + try : | ||
219 | + while True : | ||
220 | + self.cond() | ||
221 | + if not self.RET : | ||
222 | + break | ||
223 | + self.body() | ||
224 | + self.step() | ||
225 | + except BreakLoop : | ||
226 | + pass | ||
227 | + | ||
228 | +class FUNC (_CODE) : | ||
229 | + _fields = ["body"] | ||
230 | + def __call__ (self) : | ||
231 | + try : | ||
232 | + self.body() | ||
233 | + except FunctionReturn as exc : | ||
234 | + self._env["RET"] = exc.RET | ||
235 | + |
codanim/highlight.py
0 → 100644
1 | +from pygments import highlight as _pygmentize | ||
2 | +import pygments.lexers, pygments.formatters | ||
3 | + | ||
4 | +## | ||
5 | +## code pretty-printing | ||
6 | +## | ||
7 | + | ||
8 | +_lexer = pygments.lexers.get_lexer_by_name("C") | ||
9 | +_formatter = pygments.formatters.get_formatter_by_name("latex") | ||
10 | + | ||
11 | +def pygmentize (src) : | ||
12 | + return "\n".join("\n".join(_pygmentize(line, _lexer, _formatter).splitlines()[1:-1]) | ||
13 | + for line in src.split("\n")) |
-
Please register or login to post a comment