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

136 lines, 82 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 self.VisitType(dfn)
37 self.EmitFooter()
38
39 def VisitType(self, typ, depth=0):
40 if isinstance(typ.value, ast.SimpleSum):
41 self.VisitSimpleSum(typ.value, typ.name, depth)
42
43 elif isinstance(typ.value, ast.Sum):
44 self.VisitCompoundSum(typ.value, typ.name, depth)
45
46 elif isinstance(typ.value, ast.Product):
47 self.VisitProduct(typ.value, typ.name, depth)
48
49 else:
50 raise AssertionError(typ)
51
52 # Optionally overridden.
53 def VisitSimpleSum(self, value, name, depth):
54 pass
55
56 def VisitCompoundSum(self, value, name, depth):
57 pass
58
59 def VisitProduct(self, value, name, depth):
60 pass
61
62 def EmitFooter(self):
63 pass
64
65
66TABSIZE = 2
67MAX_COL = 80
68
69# Copied from asdl_c.py
70
71
72def _ReflowLines(s, depth):
73 """Reflow the line s indented depth tabs.
74
75 Return a sequence of lines where no line extends beyond MAX_COL when
76 properly indented. The first line is properly indented based
77 exclusively on depth * TABSIZE. All following lines -- these are
78 the reflowed lines generated by this function -- start at the same
79 column as the first character beyond the opening { in the first
80 line.
81 """
82 size = MAX_COL - depth * TABSIZE
83 if len(s) < size:
84 return [s]
85
86 lines = []
87 cur = s
88 padding = ""
89 while len(cur) > size:
90 i = cur.rfind(' ', 0, size)
91 if i == -1:
92 if 0:
93 print(
94 "Warning: No space to reflow line (size=%d, depth=%d, cur=%r): %r"
95 % (size, depth, cur, s),
96 file=sys.stderr)
97 lines.append(padding + cur)
98 break
99
100 lines.append(padding + cur[:i])
101 if len(lines) == 1:
102 # find new size based on brace
103 j = cur.find('{', 0, i)
104 if j >= 0:
105 j += 2 # account for the brace and the space after it
106 size -= j
107 padding = " " * j
108 else:
109 j = cur.find('(', 0, i)
110 if j >= 0:
111 j += 1 # account for the paren (no space after it)
112 size -= j
113 padding = " " * j
114 cur = cur[i + 1:]
115 else:
116 lines.append(padding + cur)
117 return lines
118
119
120def FormatLines(s, depth, reflow=True):
121 """Make the generated code readable.
122
123 Args:
124 depth: controls indentation
125 reflow: line wrapping.
126 """
127 if reflow:
128 lines = _ReflowLines(s, depth)
129 else:
130 lines = [s]
131
132 result = []
133 for line in lines:
134 line = (" " * TABSIZE * depth) + line + "\n"
135 result.append(line)
136 return result