1 | """visitor.py."""
|
2 | from __future__ import print_function
|
3 |
|
4 | import sys
|
5 | from asdl import ast
|
6 |
|
7 |
|
8 | class 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 |
|
72 | TABSIZE = 2
|
73 | MAX_COL = 80
|
74 |
|
75 | # Copied from asdl_c.py
|
76 |
|
77 |
|
78 | def _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 |
|
126 | def 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
|