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