1 | #!/usr/bin/python
|
2 | """
|
3 | visitor.py
|
4 | """
|
5 |
|
6 | from asdl import asdl_ as asdl
|
7 |
|
8 |
|
9 | class AsdlVisitor:
|
10 | """Base class for visitors.
|
11 |
|
12 | TODO:
|
13 | - It might be useful to separate this into VisitChildren() / generic_visit()
|
14 | like Python's ast.NodeVisitor does.
|
15 | - Also remove self.f and self.Emit. Those can go in self.output?
|
16 | - Move to common location, since gen_python uses it as well.
|
17 | """
|
18 | def __init__(self, f):
|
19 | self.f = f
|
20 |
|
21 | def Emit(self, s, depth, reflow=True):
|
22 | for line in FormatLines(s, depth):
|
23 | self.f.write(line)
|
24 |
|
25 | def VisitModule(self, mod):
|
26 | for dfn in mod.dfns:
|
27 | self.VisitType(dfn)
|
28 | self.EmitFooter()
|
29 |
|
30 | def VisitType(self, typ, depth=0):
|
31 | if isinstance(typ.value, asdl.Sum):
|
32 | self.VisitSum(typ.value, typ.name, depth)
|
33 | elif isinstance(typ.value, asdl.Product):
|
34 | self.VisitProduct(typ.value, typ.name, depth)
|
35 | else:
|
36 | raise AssertionError(typ)
|
37 |
|
38 | def VisitSum(self, sum, name, depth):
|
39 | if asdl.is_simple(sum):
|
40 | self.VisitSimpleSum(sum, name, depth)
|
41 | else:
|
42 | self.VisitCompoundSum(sum, name, depth)
|
43 |
|
44 | # Optionally overridden.
|
45 | def VisitProduct(self, value, name, depth):
|
46 | pass
|
47 | def VisitSimpleSum(self, value, name, depth):
|
48 | pass
|
49 | def VisitCompoundSum(self, value, name, depth):
|
50 | pass
|
51 | def EmitFooter(self):
|
52 | pass
|
53 |
|
54 |
|
55 | TABSIZE = 2
|
56 | MAX_COL = 80
|
57 |
|
58 | # Copied from asdl_c.py
|
59 |
|
60 | def _ReflowLines(s, depth):
|
61 | """Reflow the line s indented depth tabs.
|
62 |
|
63 | Return a sequence of lines where no line extends beyond MAX_COL when properly
|
64 | indented. The first line is properly indented based exclusively on depth *
|
65 | TABSIZE. All following lines -- these are the reflowed lines generated by
|
66 | this function -- start at the same column as the first character beyond the
|
67 | opening { in the first line.
|
68 | """
|
69 | size = MAX_COL - depth * TABSIZE
|
70 | if len(s) < size:
|
71 | return [s]
|
72 |
|
73 | lines = []
|
74 | cur = s
|
75 | padding = ""
|
76 | while len(cur) > size:
|
77 | i = cur.rfind(' ', 0, size)
|
78 | # XXX this should be fixed for real
|
79 | if i == -1 and 'GeneratorExp' in cur:
|
80 | i = size + 3
|
81 | assert i != -1, "Impossible line %d to reflow: %r" % (size, s)
|
82 | lines.append(padding + cur[:i])
|
83 | if len(lines) == 1:
|
84 | # find new size based on brace
|
85 | j = cur.find('{', 0, i)
|
86 | if j >= 0:
|
87 | j += 2 # account for the brace and the space after it
|
88 | size -= j
|
89 | padding = " " * j
|
90 | else:
|
91 | j = cur.find('(', 0, i)
|
92 | if j >= 0:
|
93 | j += 1 # account for the paren (no space after it)
|
94 | size -= j
|
95 | padding = " " * j
|
96 | cur = cur[i + 1:]
|
97 | else:
|
98 | lines.append(padding + cur)
|
99 | return lines
|
100 |
|
101 |
|
102 | def FormatLines(s, depth, reflow=True):
|
103 | """Make the generated code readable.
|
104 |
|
105 | Args:
|
106 | depth: controls indentation
|
107 | reflow: line wrapping.
|
108 | """
|
109 | if reflow:
|
110 | lines = _ReflowLines(s, depth)
|
111 | else:
|
112 | lines = [s]
|
113 |
|
114 | result = []
|
115 | for line in lines:
|
116 | line = (" " * TABSIZE * depth) + line + "\n"
|
117 | result.append(line)
|
118 | return result
|