| 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
|