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

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