forked from browserengineering/book
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathoutlines.py
executable file
·172 lines (143 loc) · 4.91 KB
/
outlines.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#!/usr/bin/env python3
import argparse
import ast
from dataclasses import dataclass
import sys
from typing import List
groups = [ "Node", "Layout", "Draw" ]
class Item: pass
@dataclass
class Function(Item):
name: str
args: List[str]
def str(self):
if len(self.args) > 0 and self.args[0] == "self":
args = self.args[1:]
else:
args = self.args
return "def {}({})".format(self.name, ", ".join(args))
def html(self):
return self.str().replace("def", "<span class=kw>def</span>")
def sub(self):
return None
@dataclass
class Class:
name: str
fns: List[Item]
def str(self):
return "class {}:".format(self.name)
def html(self):
return self.str().replace("class", "<span class=kw>class</span>")
def sub(self):
return self.fns
@dataclass
class Const(Item):
names: List[str]
def str(self):
return "{}".format(", ".join(self.names))
def html(self):
return self.str()
def sub(self):
return None
@dataclass
class IfMain:
pass
def str(self):
return "if __name__ == \"__main__\""
def html(self):
return self.str().replace("if", "<span class=cf>if</span>") \
.replace("==", "<span class=op>==</span>") \
.replace("\"__main__\"", "<span class=st>\"__main__\"</span>")
def sub(self):
return None
def write_str(objs, indent=0):
for obj in objs:
print(" " * indent, obj.str(), sep="")
subs = obj.sub()
if subs is None:
pass
elif len(subs) == 0:
print(" " * (indent + 4), "pass", sep="")
else:
write_str(subs, indent=indent+4)
def write_html(objs, indent=0):
for obj in objs:
print("<code class=line>", " " * indent, obj.html(), sep="")
subs = obj.sub()
if subs is None:
pass
elif len(subs) == 0:
print("<code class=line>", " " * (indent + 4), "pass", "</code>", sep="")
else:
write_html(subs, indent=indent+4)
print("</code>")
def to_item(cmd):
if isinstance(cmd, ast.ClassDef):
return Class(cmd.name, [to_item(scmd) for scmd in cmd.body])
elif isinstance(cmd, ast.FunctionDef):
return Function(cmd.name, [arg.arg for arg in cmd.args.args])
elif isinstance(cmd, ast.Assign) and len(cmd.targets) == 1:
if isinstance(cmd.targets[0], ast.Name):
names = [cmd.targets[0].id]
elif isinstance(cmd.targets[0], ast.Tuple):
names = [elt.id for elt in cmd.targets[0].elts]
else:
raise Exception(ast.dump(cmd))
return Const(names)
elif isinstance(cmd, ast.Expr) and \
isinstance(cmd.value, ast.Constant) and isinstance(cmd.value.value, str):
return
elif isinstance(cmd, ast.Import):
return
elif isinstance(cmd, ast.ImportFrom):
return
elif isinstance(cmd, ast.If) and isinstance(cmd.test, ast.Compare) and \
isinstance(cmd.test.left, ast.Name) and cmd.test.left.id == "__name__" and \
len(cmd.test.comparators) == 1 and isinstance(cmd.test.comparators[0], ast.Constant) and \
cmd.test.comparators[0].value == "__main__" and len(cmd.test.ops) == 1 and \
isinstance(cmd.test.ops[0], ast.Eq):
return IfMain()
else:
raise Exception(ast.dump(cmd))
def outline(tree):
objs = []
assert isinstance(tree, ast.Module)
for cmd in tree.body:
item = to_item(cmd)
if item: objs.append(item)
return objs
def get_imports(tree):
objs = []
assert isinstance(tree, ast.Module)
for cmd in tree.body:
if isinstance(cmd, ast.ImportFrom):
assert cmd.level == 0
assert cmd.module
assert all(name.asname is None for name in cmd.names)
names = [name.name for name in cmd.names]
filename = "src/{}.py".format(cmd.module)
with open(filename) as file:
suboutline = outline(ast.parse(file.read(), filename))
for item in suboutline:
if isinstance(item, IfMain):
pass
elif isinstance(item, Const):
if set(names) & set(item.names):
assert set(item.names).issubset(names)
objs.append(item)
else:
if item.name in names:
objs.append(item)
return objs
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Generates outlines for each chapter's code")
parser.add_argument("file", type=argparse.FileType())
parser.add_argument("--html", action="store_true", default=False)
args = parser.parse_args()
tree = ast.parse(args.file.read(), args.file.name)
ol = get_imports(tree)
ol.extend(outline(tree))
if args.html:
write_html(ol)
else:
write_str(ol)