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