OILS / asdl / visitor.py View on Github | oils.pub

195 lines, 92 significant
1"""visitor.py."""
2from __future__ import print_function
3
4import sys
5from asdl import ast
6from typing import List, IO
7
8
9class AsdlVisitor:
10 """Tree walker base class.
11
12 This is NOT a visitor, since it doesn't have accept(self) and double
13 dispatch.
14
15 VisitModule uses the "template method pattern"
16 https://en.wikipedia.org/wiki/Template_method_pattern
17 """
18
19 def __init__(self, f):
20 # type: (IO[bytes]) -> None
21 self.f = f
22 self.current_depth = 0 # the current number of indent levels
23
24 def Indent(self):
25 # type: () -> None
26 self.current_depth += 1
27
28 def Dedent(self):
29 # type: () -> None
30 self.current_depth -= 1
31
32 def Emit(self, s, depth=-1, reflow=True):
33 # type: (str, int, bool) -> None
34 if depth == -1:
35 depth = self.current_depth
36 for line in FormatLines(s, depth, reflow=reflow):
37 self.f.write(line)
38
39 def VisitModule(self, mod, depth=0):
40 # type: (ast.Module, int) -> None
41 """
42 Template method
43 """
44 self.EmitHeader(mod)
45
46 for dfn in mod.dfns:
47 if isinstance(dfn, ast.SubTypeDecl):
48 self.VisitSubType(dfn)
49
50 elif isinstance(dfn, ast.TypeDecl):
51 typ = dfn
52 if isinstance(typ.value, ast.SimpleSum):
53 self.VisitSimpleSum(typ.value, typ.name, depth)
54
55 elif isinstance(typ.value, ast.Sum):
56 self.VisitCompoundSum(typ.value, typ.name, depth)
57
58 elif isinstance(typ.value, ast.Product):
59 self.VisitProduct(typ.value, typ.name, depth)
60
61 else:
62 raise AssertionError(typ)
63
64 else:
65 raise AssertionError(dfn)
66
67 self.EmitFooter()
68
69 def VisitSubType(self, subtype):
70 # type: (ast.SubTypeDecl) -> None
71 pass
72
73 # Optionally overridden.
74 def VisitSimpleSum(self, value, name, depth):
75 # type: (ast.SimpleSum, str, int) -> None
76 pass
77
78 def VisitCompoundSum(self, value, name, depth):
79 # type: (ast.Sum, str, int) -> None
80 pass
81
82 def VisitProduct(self, value, name, depth):
83 # type: (ast.Product, str, int) -> None
84 pass
85
86 def EmitHeader(self, schema_ast):
87 # type: (ast.Module) -> None
88 pass
89
90 def EmitFooter(self):
91 # type: () -> None
92 pass
93
94
95TABSIZE = 2
96MAX_COL = 80
97
98# Copied from asdl_c.py
99
100
101def _ReflowLines(s, depth):
102 # type: (str, int) -> List[str]
103 """Reflow the line s indented depth tabs.
104
105 Return a sequence of lines where no line extends beyond MAX_COL when
106 properly indented. The first line is properly indented based
107 exclusively on depth * TABSIZE. All following lines -- these are
108 the reflowed lines generated by this function -- start at the same
109 column as the first character beyond the opening { in the first
110 line.
111 """
112 size = MAX_COL - depth * TABSIZE
113 if len(s) < size:
114 return [s]
115
116 lines = []
117 cur = s
118 padding = ""
119 while len(cur) > size:
120 i = cur.rfind(' ', 0, size)
121 if i == -1:
122 if 0:
123 print(
124 "Warning: No space to reflow line (size=%d, depth=%d, cur=%r): %r"
125 % (size, depth, cur, s),
126 file=sys.stderr)
127 lines.append(padding + cur)
128 break
129
130 lines.append(padding + cur[:i])
131 if len(lines) == 1:
132 # find new size based on brace
133 j = cur.find('{', 0, i)
134 if j >= 0:
135 j += 2 # account for the brace and the space after it
136 size -= j
137 padding = " " * j
138 else:
139 j = cur.find('(', 0, i)
140 if j >= 0:
141 j += 1 # account for the paren (no space after it)
142 size -= j
143 padding = " " * j
144 cur = cur[i + 1:]
145 else:
146 lines.append(padding + cur)
147 return lines
148
149
150def FormatLines(s, depth, reflow=True):
151 # type: (str, int, bool) -> List[str]
152 """Make the generated code readable.
153
154 Args:
155 depth: controls indentation
156 reflow: line wrapping.
157 """
158 if reflow:
159 lines = _ReflowLines(s, depth)
160 else:
161 lines = [s]
162
163 result = []
164 for line in lines:
165 line = (" " * TABSIZE * depth) + line + "\n"
166 result.append(line)
167 return result
168
169
170"""
171For */*_gen.py, and asdl/gen_*.py
172
173from asdl.util import Write
174
175def f():
176 # constant string with leading stuff stripped off
177 Write(f, '''
178 hello
179 there
180 |''')
181
182 # - option to reflow
183 # - option to add MORE depth, in addition to stripping whitespace
184 # - I wonder if YSH template strings could use something like that
185 Write(f, '''
186 a = {a}
187 b = {b}
188 |''', locals(), reflow=True, depth=2)
189
190 with Printer(locals()) as p:
191 p.Write('''
192 a = {a}
193 b = {b}
194 |''')
195"""