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

189 lines, 89 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 for dfn in mod.dfns:
45 if isinstance(dfn, ast.SubTypeDecl):
46 self.VisitSubType(dfn)
47
48 elif isinstance(dfn, ast.TypeDecl):
49 typ = dfn
50 if isinstance(typ.value, ast.SimpleSum):
51 self.VisitSimpleSum(typ.value, typ.name, depth)
52
53 elif isinstance(typ.value, ast.Sum):
54 self.VisitCompoundSum(typ.value, typ.name, depth)
55
56 elif isinstance(typ.value, ast.Product):
57 self.VisitProduct(typ.value, typ.name, depth)
58
59 else:
60 raise AssertionError(typ)
61
62 else:
63 raise AssertionError(dfn)
64
65 self.EmitFooter()
66
67 def VisitSubType(self, subtype):
68 # type: (ast.SubTypeDecl) -> None
69 pass
70
71 # Optionally overridden.
72 def VisitSimpleSum(self, value, name, depth):
73 # type: (ast.SimpleSum, str, int) -> None
74 pass
75
76 def VisitCompoundSum(self, value, name, depth):
77 # type: (ast.Sum, str, int) -> None
78 pass
79
80 def VisitProduct(self, value, name, depth):
81 # type: (ast.Product, str, int) -> None
82 pass
83
84 def EmitFooter(self):
85 # type: () -> None
86 pass
87
88
89TABSIZE = 2
90MAX_COL = 80
91
92# Copied from asdl_c.py
93
94
95def _ReflowLines(s, depth):
96 # type: (str, int) -> List[str]
97 """Reflow the line s indented depth tabs.
98
99 Return a sequence of lines where no line extends beyond MAX_COL when
100 properly indented. The first line is properly indented based
101 exclusively on depth * TABSIZE. All following lines -- these are
102 the reflowed lines generated by this function -- start at the same
103 column as the first character beyond the opening { in the first
104 line.
105 """
106 size = MAX_COL - depth * TABSIZE
107 if len(s) < size:
108 return [s]
109
110 lines = []
111 cur = s
112 padding = ""
113 while len(cur) > size:
114 i = cur.rfind(' ', 0, size)
115 if i == -1:
116 if 0:
117 print(
118 "Warning: No space to reflow line (size=%d, depth=%d, cur=%r): %r"
119 % (size, depth, cur, s),
120 file=sys.stderr)
121 lines.append(padding + cur)
122 break
123
124 lines.append(padding + cur[:i])
125 if len(lines) == 1:
126 # find new size based on brace
127 j = cur.find('{', 0, i)
128 if j >= 0:
129 j += 2 # account for the brace and the space after it
130 size -= j
131 padding = " " * j
132 else:
133 j = cur.find('(', 0, i)
134 if j >= 0:
135 j += 1 # account for the paren (no space after it)
136 size -= j
137 padding = " " * j
138 cur = cur[i + 1:]
139 else:
140 lines.append(padding + cur)
141 return lines
142
143
144def FormatLines(s, depth, reflow=True):
145 # type: (str, int, bool) -> List[str]
146 """Make the generated code readable.
147
148 Args:
149 depth: controls indentation
150 reflow: line wrapping.
151 """
152 if reflow:
153 lines = _ReflowLines(s, depth)
154 else:
155 lines = [s]
156
157 result = []
158 for line in lines:
159 line = (" " * TABSIZE * depth) + line + "\n"
160 result.append(line)
161 return result
162
163
164"""
165For */*_gen.py, and asdl/gen_*.py
166
167from asdl.util import Write
168
169def f():
170 # constant string with leading stuff stripped off
171 Write(f, '''
172 hello
173 there
174 |''')
175
176 # - option to reflow
177 # - option to add MORE depth, in addition to stripping whitespace
178 # - I wonder if YSH template strings could use something like that
179 Write(f, '''
180 a = {a}
181 b = {b}
182 |''', locals(), reflow=True, depth=2)
183
184 with Printer(locals()) as p:
185 p.Write('''
186 a = {a}
187 b = {b}
188 |''')
189"""