text: #!/usr/bin/env python
  
  from operator import attrgetter
  from optparse import OptionParser
  import re
  import sys
  
  
  def simplerepr(obj):
      keys = sorted(key for key in obj.__dict__.keys() if not key.startswith('_'))
      attrs = ''.join(' %s=%r' % (key, obj.__dict__[key]) for key in keys)
      return '<%s%s>' % (obj.__class__.__name__, attrs)
  
  
  class Exportable(object):
  
      def __init__(self, name):
          self.name = name
  
      __repr__ = simplerepr
  
      def export(self):
          return ''
  
      def extern(self):
          return ''
  
      def typedef(self):
          return ''
  
  
  class ObjectLiteral(Exportable):
  
      def __init__(self, name, objects):
          Exportable.__init__(self, name)
          self.prop_types = {}
          self.objects = objects
  
      __repr__ = simplerepr
  
      def extern(self):
          lines = []
          lines.append('\n\n\n')
          lines.append('/**\n')
          lines.append(' * @interface\n')
          lines.append(' */\n')
          lines.append('%s = function() {};\n' % (self.extern_name(),))
          for prop in sorted(self.prop_types.keys()):
              lines.append('\n\n')
              lines.append('/**\n')
              prop_types = self.prop_types[prop].split('|')
              for i, t in enumerate(prop_types):
                  if t in self.objects and isinstance(self.objects[t], ObjectLiteral):
                      prop_types[i] = self.objects[t].extern_name()
              prop_types = '|'.join(prop_types)
              lines.append(' * @type {%s}\n' % (prop_types,))
              lines.append(' */\n')
              lines.append('%s.prototype.%s;\n' % (self.extern_name(), prop))
          return ''.join(lines)
  
      def extern_name(self):
          return re.sub(r'(olx\.\S+)', r'\1Extern', self.name)
  
      def extern_namespace(self):
          return '.'.join(self.extern_name().split('.')[:-1]) or None
  
      def provide(self):
          return 'goog.provide(\'%s\');\n' % (self.name,)
  
      def typedef(self):
          lines = []
          lines.append('\n\n')
          lines.append('/**\n')
          for i, prop in enumerate(sorted(self.prop_types.keys())):
              prefix =  ' * @typedef {{' if i == 0 else ' *            '
              suffix = '}}' if i == len(self.prop_types) - 1 else ','
              type = self.prop_types[prop]
              if '|' in type:
                  type = '(%s)' % (type,)
              lines.append('%s%s: %s%s\n' % (prefix, prop, type, suffix))
          lines.append(' */\n')
          lines.append('%s;\n' % (self.name,))
          return ''.join(lines)
  
  
  class Symbol(Exportable):
  
      def __init__(self, name, export_symbol):
          Exportable.__init__(self, name)
          self.export_symbol = export_symbol
          self.props = set()
  
      __repr__ = simplerepr
  
      def export(self):
          lines = []
          if self.export_symbol:
              lines.append('\n\ngoog.exportSymbol(\n    \'%s\',\n    %s);\n' % (self.name, self.name))
          lines.extend('goog.exportProperty(\n    %s,\n    \'%s\',\n    %s.%s);\n' % (self.name, prop, self.name, prop) for prop in sorted(self.props))
          return ''.join(lines)
  
  
  def main(argv):
  
      option_parser = OptionParser()
      option_parser.add_option('--exports', action='store_true')
      option_parser.add_option('--externs', action='store_true')
      option_parser.add_option('--typedef', action='store_true')
      options, args = option_parser.parse_args(argv[1:])
  
      objects = {}
      requires = set()
      for arg in args:
          in_comment = False
          object_literal = None
          for line in open(arg, 'rU'):
              line = line.strip()
              if not line:
                  continue
              if line == '/**':
                  assert not in_comment
                  in_comment = True
                  continue
              if line == '*/':
                  assert in_comment
                  in_comment = False
                  object_literal = None
                  continue
              if in_comment:
                  if not line.startswith('*'):
                      raise RuntimeError(line)  # malformed comment
                  m = re.match(r'\*\s*@typedef\s*\{Object\}\s*(?P<name>\S+)', line)
                  if m:
                      assert object_literal is None
                      name = m.group('name')
                      if name in objects:
                          raise RuntimeError(line)  # Name already defined
                      object_literal = ObjectLiteral(name, objects)
                      objects[name] = object_literal
                      continue
                  m = re.match(r'\*\s*@property\s*{(?P<type>.*?)}\s*(?P<prop>\S+)', line)
                  if m:
                      assert object_literal is not None
                      prop = m.group('prop')
                      if prop in object_literal.prop_types:
                          raise RuntimeError(line)  # Duplicate property
                      type = m.group('type')
                      object_literal.prop_types[prop] = type
                      continue
                  continue
              m = re.match(r'@exportProperty\s+(?P<prop>\S+)\Z', line)
              if m:
                  components = m.group('prop').split('.')
                  if components[-2] == 'prototype':
                      requires.add('.'.join(components[:-2]))
                  else:
                      requires.add('.'.join(components[:-1]))
                  name = '.'.join(components[:-1]) 
                  prop = components[-1]
                  if name in objects:
                      symbol = objects[name]
                  else:
                      symbol = Symbol(name, False)
                      objects[name] = symbol
                  symbol.props.add(prop)
                  continue
              m = re.match(r'@exportSymbol\s+(?P<name>\S+)\Z', line)
              if m:
                  name = m.group('name')
                  if name in objects:
                      raise RuntimeError(line)  # Name already defined
                  symbol = Symbol(name, True)
                  objects[name] = symbol
                  components = m.group('name').split('.')
                  if re.match(r'[A-Z]', components[-1]):
                      requires.add(name)
                  else:
                      requires.add('.'.join(components[:-1]))
                  continue
              raise RuntimeError(line)
  
      objects = sorted(objects.values(), key=attrgetter('name'))
  
      if options.exports:
          if requires:
              for require in sorted(requires):
                  sys.stdout.write('goog.require(\'%s\');\n' % (require,))
          for obj in objects:
              sys.stdout.write(obj.export())
  
      if options.externs:
          object_literals = [obj for obj in objects if isinstance(obj, ObjectLiteral)]
          sys.stdout.write('/**\n')
          sys.stdout.write(' * @externs\n')
          sys.stdout.write(' */\n')
          namespaces = sorted(set(filter(None, (object_literal.extern_namespace() for object_literal in object_literals))))
          for namespace in namespaces:
              sys.stdout.write('\n\n')
              sys.stdout.write('/**\n')
              sys.stdout.write(' * @type {Object}\n')
              sys.stdout.write(' */\n')
              if '.' in namespace:
                  sys.stdout.write('%s = {};\n' % (namespace,))
              else:
                  sys.stdout.write('var %s;\n' % (namespace,))
          for object_literal in object_literals:
              sys.stdout.write(object_literal.typedef())
              sys.stdout.write(object_literal.extern())
  
      if options.typedef:
          object_literals = [obj for obj in objects if isinstance(obj, ObjectLiteral)]
          for object_literal in object_literals:
              sys.stdout.write(object_literal.provide())
          for object_literal in object_literals:
              sys.stdout.write(object_literal.typedef())
  
  
  if __name__ == '__main__':
      sys.exit(main(sys.argv))
  