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 | 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 |
|
66 | TABSIZE = 2
|
67 | MAX_COL = 80
|
68 |
|
69 | # Copied from asdl_c.py
|
70 |
|
71 |
|
72 | def _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 |
|
120 | def 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
|