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