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

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