| 1 | #!/usr/bin/env python
 | 
| 2 | """
 | 
| 3 | gen_python.py
 | 
| 4 | 
 | 
| 5 | Generate Python code from and ASDL schema.
 | 
| 6 | 
 | 
| 7 | TODO:
 | 
| 8 | - What about Id?  app_types?
 | 
| 9 | """
 | 
| 10 | 
 | 
| 11 | import sys
 | 
| 12 | 
 | 
| 13 | from asdl import asdl_ as asdl
 | 
| 14 | from asdl import visitor
 | 
| 15 | 
 | 
| 16 | 
 | 
| 17 | class GenClassesVisitor(visitor.AsdlVisitor):
 | 
| 18 | 
 | 
| 19 |   def VisitSimpleSum(self, sum, name, depth):
 | 
| 20 |     self.Emit('class %s_e(py_meta.SimpleObj):' % name, depth)
 | 
| 21 |     self.Emit('  ASDL_TYPE = TYPE_LOOKUP.ByTypeName(%r)' % name, depth)
 | 
| 22 |     self.Emit('', depth)
 | 
| 23 | 
 | 
| 24 |     # Just use #define, since enums aren't namespaced.
 | 
| 25 |     for i, variant in enumerate(sum.types):
 | 
| 26 |       attr = '%s_e.%s = %s_e(%d, %r)' % (
 | 
| 27 |           name, variant.name, name, i + 1, variant.name)
 | 
| 28 |       self.Emit(attr, depth)
 | 
| 29 |     self.Emit('', depth)
 | 
| 30 | 
 | 
| 31 |   def _GenClass(self, desc, name, super_name, depth, tag_num=None):
 | 
| 32 |     self.Emit('class %s(%s):' % (name, super_name), depth)
 | 
| 33 | 
 | 
| 34 |     if tag_num is not None:
 | 
| 35 |       self.Emit('  tag = %d' % tag_num, depth)
 | 
| 36 | 
 | 
| 37 |     field_names = [f.name for f in desc.fields]
 | 
| 38 | 
 | 
| 39 |     quoted_fields = repr(tuple(field_names))
 | 
| 40 |     # NOTE: FIELDS is a duplicate of __slots__, used for pretty printing and
 | 
| 41 |     # oheap serialization.  TODO: measure the effect of __slots__, and then get
 | 
| 42 |     # rid of FIELDS?  Or you can just make it an alias.
 | 
| 43 |     # FIELDS = self.__slots__.
 | 
| 44 |     self.Emit('  ASDL_TYPE = TYPE_LOOKUP.ByTypeName(%r)' % name, depth)
 | 
| 45 |     self.Emit('  __slots__ = %s' % quoted_fields, depth)
 | 
| 46 | 
 | 
| 47 |     self.Emit('', depth)
 | 
| 48 | 
 | 
| 49 |     # TODO: leave out spids?  Mark it as an attribute?
 | 
| 50 |     args = ', '.join('%s=None' % f.name for f in desc.fields)
 | 
| 51 |     self.Emit('  def __init__(self, %s):' % args, depth)
 | 
| 52 | 
 | 
| 53 |     for f in desc.fields:
 | 
| 54 |       # This logic is like _MakeFieldDescriptors
 | 
| 55 |       default = None
 | 
| 56 |       if f.opt:  # Maybe
 | 
| 57 |         if f.type == 'int':
 | 
| 58 |           default = 'const.NO_INTEGER'
 | 
| 59 |         elif f.type == 'string':
 | 
| 60 |           default = "''"
 | 
| 61 |         else:
 | 
| 62 |           default = 'None'
 | 
| 63 | 
 | 
| 64 |       elif f.seq:  # Array
 | 
| 65 |         default = '[]'
 | 
| 66 | 
 | 
| 67 |       # PROBLEM: Optional ints can't be zero!
 | 
| 68 |       # self.span_id = span_id or const.NO_INTEGER
 | 
| 69 |       # I don't want to add if statements checking against None?
 | 
| 70 |       # For now don't use optional ints.  We don't need it.
 | 
| 71 | 
 | 
| 72 |       default_str = (' or %s' % default) if default else ''
 | 
| 73 |       self.Emit('    self.%s = %s%s' % (f.name, f.name, default_str), depth)
 | 
| 74 | 
 | 
| 75 |     self.Emit('', depth)
 | 
| 76 | 
 | 
| 77 |   def VisitConstructor(self, cons, def_name, tag_num, depth):
 | 
| 78 |     if cons.fields:
 | 
| 79 |       self._GenClass(cons, cons.name, def_name, depth, tag_num=tag_num)
 | 
| 80 |     else:
 | 
| 81 |       self.Emit("class %s(%s):" % (cons.name, def_name), depth)
 | 
| 82 |       self.Emit('  ASDL_TYPE = TYPE_LOOKUP.ByTypeName(%r)' % cons.name, depth)
 | 
| 83 |       self.Emit('  tag = %d'  % tag_num, depth)
 | 
| 84 |       self.Emit('', depth)
 | 
| 85 | 
 | 
| 86 |   def VisitCompoundSum(self, sum, name, depth):
 | 
| 87 |     # define command_e
 | 
| 88 |     self.Emit('class %s_e(object):' % name, depth)
 | 
| 89 |     for i, variant in enumerate(sum.types):
 | 
| 90 |       self.Emit('  %s = %d' % (variant.name, i + 1), depth)
 | 
| 91 |     self.Emit('', depth)
 | 
| 92 | 
 | 
| 93 |     self.Emit('class %s(py_meta.CompoundObj):' % name, depth)
 | 
| 94 |     self.Emit('  ASDL_TYPE = TYPE_LOOKUP.ByTypeName(%r)' % name, depth)
 | 
| 95 |     self.Emit('', depth)
 | 
| 96 | 
 | 
| 97 |     # define command_t, and then make subclasses
 | 
| 98 |     super_name = '%s' % name
 | 
| 99 |     for i, t in enumerate(sum.types):
 | 
| 100 |       tag_num = i + 1
 | 
| 101 |       self.VisitConstructor(t, super_name, tag_num, depth)
 | 
| 102 | 
 | 
| 103 |   def VisitProduct(self, product, name, depth):
 | 
| 104 |     self._GenClass(product, name, 'py_meta.CompoundObj', depth)
 | 
| 105 | 
 | 
| 106 |   def EmitFooter(self):
 | 
| 107 |     pass
 | 
| 108 | 
 | 
| 109 | 
 | 
| 110 | def main(argv):
 | 
| 111 | 
 | 
| 112 |   schema_path = argv[1]
 | 
| 113 |   type_lookup_import = argv[2]
 | 
| 114 |   with open(schema_path) as input_f:
 | 
| 115 |     module = asdl.parse(input_f)
 | 
| 116 | 
 | 
| 117 |   f = sys.stdout
 | 
| 118 | 
 | 
| 119 |   f.write("""\
 | 
| 120 | from asdl import const  # For const.NO_INTEGER
 | 
| 121 | from asdl import py_meta
 | 
| 122 | %s
 | 
| 123 | 
 | 
| 124 | """ % type_lookup_import)
 | 
| 125 | 
 | 
| 126 |   v = GenClassesVisitor(f)
 | 
| 127 |   v.VisitModule(module)
 | 
| 128 | 
 | 
| 129 | 
 | 
| 130 | if __name__ == '__main__':
 | 
| 131 |   try:
 | 
| 132 |     main(sys.argv)
 | 
| 133 |   except RuntimeError as e:
 | 
| 134 |     print >>sys.stderr, 'FATAL: %s' % e
 | 
| 135 |     sys.exit(1)
 |