Showing
3 changed files
with
195 additions
and
0 deletions
README.md
0 → 100644
1 | +codanim: Animated code & data structures with LaTeX/beamer | ||
2 | + | ||
3 | + (C) 2020 Franck Pommereau <franck.pommereau@univ-evry.fr> | ||
4 | + This is free software, see file LICENCE | ||
5 | + | ||
6 | +codanim allows to generate LaTeX/TikZ code to animate source code and | ||
7 | +data structures for beamer presentations. So far, it is oriented to | ||
8 | +simulate C code, but there is no conceptual limitation for the | ||
9 | +simulation of other languages. | ||
10 | + | ||
11 | +codanim is currently highly experimental and neither well documented | ||
12 | +nor well tested. You've been warned. | ||
13 | + | ||
14 | +## INSTALL | ||
15 | + | ||
16 | +There is so far no installation procedure. Just copy directory | ||
17 | +`codanim` somewhere in your `PYTHONPATH`. | ||
18 | + | ||
19 | +File `examples/pygments.sty` will be needed to compile the final LaTeX | ||
20 | +files. File `examples/tpl.tex` shows how the generated LaTeX code may | ||
21 | +be included in a beamer presentation. | ||
22 | + | ||
23 | +## CONCEPTS | ||
24 | + | ||
25 | +codanim defines Python classes to simulate the data and control-flow | ||
26 | +structures of an imperative language. Data structures are defined in | ||
27 | +`codanim.data` and include: | ||
28 | + | ||
29 | + * `Pointer(data)` a pointer to another data `data` | ||
30 | + * `Value(value)` an arbitrary value, initialised to `value`. If | ||
31 | + `value` is `None` (the default), the value is considered | ||
32 | + uninitialised | ||
33 | + * `Array(init, index=[])` an array of values, that is zero-indexed, | ||
34 | + and initialised as follows: | ||
35 | + * if `init` is an `int`, then it is the length of the array whose | ||
36 | + values are all uninitialised | ||
37 | + * if `init` is a `list` then is holds the initial values of the | ||
38 | + array | ||
39 | + `index` is a list of variables identifiers that may be used to | ||
40 | + access the array cells (see below) | ||
41 | + * `Struct(init)` a structure whose fields and initial values are | ||
42 | + defined by `init` that must be a `dict` | ||
43 | + * `Heap()` a container for dynamically allocated data that provides | ||
44 | + methods `new` and `free` to do so | ||
45 | + | ||
46 | +Control-flow structures are used to simulate within Python code that | ||
47 | +may potentially be written in any imperative language. Doing so, all | ||
48 | +the changes that are made to the data structures defined above are | ||
49 | +recorded so that they may be latter animated, consistently with the | ||
50 | +animation of the code itself. The idea is that source code in the | ||
51 | +simulated language is split into corresponding control-flow | ||
52 | +structures, and actual computation is done using equivalent Python | ||
53 | +code instead of executing the code in the source language. | ||
54 | +Control-flow structures are defined in `codanim.flow` and include: | ||
55 | + | ||
56 | + * `RAW(src='...')` raw code from the simulated language, its | ||
57 | + simulation is no-op, but it is rendered highlighted in the final | ||
58 | + animation | ||
59 | + * `WS(src='...')` just like `RAW` but should be only white spaces (so | ||
60 | + that it won't be highlighted) | ||
61 | + * `BLOCK(*body)` a group of other structures that are simulated (and | ||
62 | + rendered) sequentially | ||
63 | + * `STMT(*steps, src='...')` an arbitrary statement that is simulated | ||
64 | + by running its`steps` sequentially, each of which being an | ||
65 | + arbitrary Python statement that is `exec`ed | ||
66 | + * `EXPR(expr, src='...')` an arbitrary Python expression `expr` that | ||
67 | + simulate an expression in the simulated language | ||
68 | + * `PY(code)` an arbitrary Python statement that need to be executed | ||
69 | + but is not rendered in the final animation, just like every | ||
70 | + structure that expects no `src='...'` argument | ||
71 | + * `ENV(name, value)` a data structure that need to be defined in | ||
72 | + order to do the simulation (typically: global or external | ||
73 | + variables), and which is stored into variable `name` that can be | ||
74 | + used from the Python code of statements and expression | ||
75 | + * `DECL(name, init=None, animate=False, src'...')` an actual variable | ||
76 | + declaration in the simulated language, that will be executed and | ||
77 | + rendered. The execution consists of evaluating `init` that should | ||
78 | + be an `EXPR` (or `None`) and assigning its value to `name`. If | ||
79 | + `animate` is `True` then the value of the variable will displayed | ||
80 | + within a comment just next to the declaration in the final | ||
81 | + animation | ||
82 | + * `XDECL(*names, src='...')` several uninitialised declarations | ||
83 | + * `BREAK` a `break` instruction for loops and switches | ||
84 | + * `RETURN(value, src='...')` a return instruction from a function | ||
85 | + * `IF(cond, then, otherwise=None, src='...')` simulates an `if` block | ||
86 | + with an optional `else` part (called `otherwise` since `else` is a | ||
87 | + Python keyword. Argument `cond` should be an `EXPR` instance | ||
88 | + * `WHILE(cond, body, src='...')` simulates a `while` loop | ||
89 | + * `DO(body, cond, src='...')` simulates a `do/while` loop | ||
90 | + * `FOR(init, cont, step, body)` simulates a C-like `for` loop | ||
91 | + * `FUNC(body, src='...')` simulates a function | ||
92 | + **TODO:** functions calls is not implemented yet, so basically, a | ||
93 | + function is so far only a `BLOCK` with source code | ||
94 | + * `SWITCH(cond, *cases, src='...')` simulates a C-like switch, | ||
95 | + `cases` may be: | ||
96 | + * `CASE(value, body)` to simulate a `case` statement | ||
97 | + * `DEFAULT()` to simulate a `default` statement | ||
98 | + | ||
99 | +So, all what you need is to defined some data structures, some | ||
100 | +control-flow structures with appropriate `src` arguments (so that code | ||
101 | +is rendered in the simulated language), and with appropriate Python | ||
102 | +code to simulate the original statements and expressions. | ||
103 | + | ||
104 | +Writing the control-flow structures may be tedious, so codanim may be | ||
105 | +called from the command line to parse actual code to be simulated and | ||
106 | +generate the appropriate control-flow structures (in which the Python | ||
107 | +code remains to be written). Run `python -m codanim` for help. | ||
108 | + | ||
109 | +## EXAMPLES | ||
110 | + | ||
111 | +File `examples/Makefile` can be used to build the PDF for all the | ||
112 | +examples. For instance use `make heap.pdf` to build the first example | ||
113 | +described below. | ||
114 | + | ||
115 | +### `examples/heap.py` | ||
116 | + | ||
117 | +It defines a `Heap` instance and add chained `Struct`s to it. The | ||
118 | +final picture is rendered as TikZ code. There is no animation, this is | ||
119 | +just the picture of a chained list. | ||
120 | + | ||
121 | +### `examples/stack-push.py` | ||
122 | + | ||
123 | +It uses the same kind of linked lists as above, seen as stacks, to | ||
124 | +animate a push onto a stack. First the data is defined, then the | ||
125 | +control-flow structure. The latter is then simulated and finally, both | ||
126 | +code and data structures are rendered for LaTeX/beamer inclusion. | ||
127 | + | ||
128 | +Note the use of class `Box` that can be used to layout several data | ||
129 | +structures. | ||
130 | + | ||
131 | +### `examples/qs-partition.py` and `examples/qs-main.py` | ||
132 | + | ||
133 | +The partitioning and main algorithm of a quick-sort. This shows how to | ||
134 | +use `Array`, in particular the `index` argument. `qs-main` also shows | ||
135 | +how to define custom layout of arrays, as well as auxiliary Python | ||
136 | +functions to simulate full C-functions calls in one step. |
codanim/__main__.py
0 → 100644
1 | +import argparse, pathlib, importlib, sys | ||
2 | +from . import lang as _lang | ||
3 | + | ||
4 | +languages = {} | ||
5 | + | ||
6 | +for path in pathlib.Path(_lang.__file__).parent.glob("*.py") : | ||
7 | + module_name = path.with_suffix("").name | ||
8 | + if module_name == "__init__" : | ||
9 | + continue | ||
10 | + module = importlib.import_module(f".lang.{module_name}", package="codanim") | ||
11 | + languages[module_name.lower()] = module | ||
12 | + | ||
13 | +class ListLang (argparse.Action) : | ||
14 | + def __call__ (self, parser, *l, **k) : | ||
15 | + print("Supported languages:") | ||
16 | + width = max(len(l) for l in languages) + 4 | ||
17 | + for lang, module in sorted(languages.items()) : | ||
18 | + print((" - {lang:<%s} {help}" % width).format(lang=lang, | ||
19 | + help=module.translate.__doc__)) | ||
20 | + parser.exit(0) | ||
21 | + | ||
22 | +parser = argparse.ArgumentParser( | ||
23 | + prog="codanim", | ||
24 | + description="parse code and generate Python stub suitable for codanim") | ||
25 | + | ||
26 | +parser.add_argument("-l", "--lang", | ||
27 | + default=None, | ||
28 | + help="language to be parsed (default: guess from file name)") | ||
29 | +parser.add_argument("-L", "--list-lang", nargs=0, | ||
30 | + action=ListLang, | ||
31 | + help="list supported languages") | ||
32 | +parser.add_argument("-o", "--output", type=argparse.FileType("w"), | ||
33 | + default=sys.stdout, | ||
34 | + help="output file") | ||
35 | +parser.add_argument("SOURCE", type=argparse.FileType("r"), | ||
36 | + help="source file") | ||
37 | +args = parser.parse_args() | ||
38 | + | ||
39 | +if args.lang is None : | ||
40 | + args.lang = args.SOURCE.name.rsplit(".")[-1] | ||
41 | + | ||
42 | +args.lang = args.lang.lower() | ||
43 | + | ||
44 | +if args.lang not in languages : | ||
45 | + parser.error("unsupported language: %r" % args.lang) | ||
46 | + | ||
47 | +translate = languages[args.lang].translate | ||
48 | + | ||
49 | +args.output.write(repr(translate(args.SOURCE.read())) + "\n") |
... | @@ -385,3 +385,13 @@ class Translator (object) : | ... | @@ -385,3 +385,13 @@ class Translator (object) : |
385 | return default | 385 | return default |
386 | def do_Break (self, node) : | 386 | def do_Break (self, node) : |
387 | return flow.BREAK() | 387 | return flow.BREAK() |
388 | + | ||
389 | +## | ||
390 | +## CLI interface | ||
391 | +## | ||
392 | + | ||
393 | +def translate (code) : | ||
394 | + "the C language" | ||
395 | + a = parse(code) | ||
396 | + t = Translator() | ||
397 | + return t(a) | ... | ... |
-
Please register or login to post a comment