OILS / asdl / visitor.py View on Github | oilshell.org

142 lines, 87 significant
1"""visitor.py."""
2from __future__ import print_function
3
4import sys
5from asdl import ast
6
7
8class AsdlVisitor:
9 """Base class for visitors.
10
11 TODO:
12 - It might be useful to separate this into VisitChildren() / generic_visit()
13 like Python's ast.NodeVisitor does.
14 - Also remove self.f and self.Emit. Those can go in self.output?
15 - Move to common location, since gen_python uses it as well.
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):
35 for dfn in mod.dfns:
36 if isinstance(dfn, ast.SubTypeDecl):
37 self.VisitSubType(dfn)
38 else:
39 self.VisitType(dfn)
40 self.EmitFooter()
41
42 def VisitSubType(self, subtype):
43 pass
44
45 def VisitType(self, typ, depth=0):
46 if isinstance(typ.value, ast.SimpleSum):
47 self.VisitSimpleSum(typ.value, typ.name, depth)
48
49 elif isinstance(typ.value, ast.Sum):
50 self.VisitCompoundSum(typ.value, typ.name, depth)
51
52 elif isinstance(typ.value, ast.Product):
53 self.VisitProduct(typ.value, typ.name, depth)
54
55 else:
56 raise AssertionError(typ)
57
58 # Optionally overridden.
59 def VisitSimpleSum(self, value, name, depth):
60 pass
61
62 def VisitCompoundSum(self, value, name, depth):
63 pass
64
65 def VisitProduct(self, value, name, depth):
66 pass
67
68 def EmitFooter(self):
69 pass
70
71
72TABSIZE = 2
73MAX_COL = 80
74
75# Copied from asdl_c.py
76
77
78def _ReflowLines(s, depth):
79 """Reflow the line s indented depth tabs.
80
81 Return a sequence of lines where no line extends beyond MAX_COL when
82 properly indented. The first line is properly indented based
83 exclusively on depth * TABSIZE. All following lines -- these are
84 the reflowed lines generated by this function -- start at the same
85 column as the first character beyond the opening { in the first
86 line.
87 """
88 size = MAX_COL - depth * TABSIZE
89 if len(s) < size:
90 return [s]
91
92 lines = []
93 cur = s
94 padding = ""
95 while len(cur) > size:
96 i = cur.rfind(' ', 0, size)
97 if i == -1:
98 if 0:
99 print(
100 "Warning: No space to reflow line (size=%d, depth=%d, cur=%r): %r"
101 % (size, depth, cur, s),
102 file=sys.stderr)
103 lines.append(padding + cur)
104 break
105
106 lines.append(padding + cur[:i])
107 if len(lines) == 1:
108 # find new size based on brace
109 j = cur.find('{', 0, i)
110 if j >= 0:
111 j += 2 # account for the brace and the space after it
112 size -= j
113 padding = " " * j
114 else:
115 j = cur.find('(', 0, i)
116 if j >= 0:
117 j += 1 # account for the paren (no space after it)
118 size -= j
119 padding = " " * j
120 cur = cur[i + 1:]
121 else:
122 lines.append(padding + cur)
123 return lines
124
125
126def FormatLines(s, depth, reflow=True):
127 """Make the generated code readable.
128
129 Args:
130 depth: controls indentation
131 reflow: line wrapping.
132 """
133 if reflow:
134 lines = _ReflowLines(s, depth)
135 else:
136 lines = [s]
137
138 result = []
139 for line in lines:
140 line = (" " * TABSIZE * depth) + line + "\n"
141 result.append(line)
142 return result