Modules | Files | Inheritance Tree | Inheritance Graph | Name Index | Config
File: Synopsis/Parser/Python/python.py
    1| # Python Parser
    2| # Copyright(c) 2000 by Stephen Davies
    3| #
    4| # This file is under the GPL.
    5| """Main module for the Python parser.
    6| Parsing python is achieved by using the code in the Python distribution that
    7| is an example for parsing python by using the built-in parser. This parser
    8| returns a parse tree which we can traverse and translate into Synopsis' AST.
    9| The exparse module contains the enhanced example code (it has many more
   10| features than the simple example did), and this module translates the
   11| resulting intermediate AST objects into Synopsis.Core.AST objects.
   12| """
   13| 
   14| import parser, sys, os, string, getopt
   15| from Synopsis.Core import AST, Type
   16| import exparse
   17| 
   18| verbose = 0
   19| 
   20| def addDeclaration(decl):
   21|     """Adds the given declaration to the current top scope and to the
   22|     SourceFile for this file."""
   23|     global scopes, sourcefile
   24|     scopes[-1].declarations().append(decl)
   25|     sourcefile.declarations().append(decl)
   26| 
   27| def push(scope):
   28|     """Pushes the given scope onto the top of the stack"""
   29|     scopes.append(scope)
   30| 
   31| def pop():
   32|     """Pops the scope stack by one level"""
   33|     del scopes[-1]
   34| 
   35| def scopeName(name):
   36|     """Scopes the given name. If the given name is a list then it is returned
   37|     verbatim, else it is concatenated with the (scoped) name of the current
   38|     scope"""
   39|     if type(name) == type([]): return tuple(name)
   40|     return tuple(list(scopes[-1].name()) + [name])
   41| 
   42| def process_ModuleInfo(mi):
   43|     """Processes a ModuleInfo object. The comments are extracted, and any
   44|     functions and comments recursively processed."""
   45|     comm = AST.Comment(mi.get_docstring(), sourcefile, -1)
   46|     scopes[-1].comments().append(comm)
   47|     for name in mi.get_function_names():
   48|         process_FunctionInfo(mi.get_function_info(name))
   49|     for name in mi.get_class_names():
   50|         process_ClassInfo(mi.get_class_info(name))
   51| 
   52| def add_params(func, fi):
   53|     """Adds the parameters of 'fi' to the AST.Function 'func'."""
   54|     for name, value in map(None, fi.get_params(), fi.get_param_defaults()):
   55|         func.parameters().append(AST.Parameter('', returnType,'',name,value))
   56| 
   57| def process_FunctionInfo(fi):
   58|     """Process a FunctionInfo object. An AST.Function object is created and
   59|     inserted into the current scope."""
   60|     name = scopeName(fi.get_name())
   61|     func = AST.Function(sourcefile, -1, 'Python', 'function', '', returnType, name, name[-1])
   62|     comm = AST.Comment(fi.get_docstring(), '', -1)
   63|     func.comments().append(comm)
   64|     add_params(func, fi)
   65|     addDeclaration(func)
   66| 
   67| def process_MethodInfo(fi):
   68|     """Process a MethodInfo object. An AST.Operation object is created and
   69|     inserted into the current scope."""
   70|     name = scopeName(fi.get_name())
   71|     func = AST.Operation(sourcefile,-1, 'Python', 'operation', '', returnType, name, name[-1])
   72|     comm = AST.Comment(fi.get_docstring(), '', -1)
   73|     func.comments().append(comm)
   74|     add_params(func, fi)
   75|     addDeclaration(func)
   76| 
   77| 
   78| def process_ClassInfo(ci):
   79|     """Process a ClassInfo object. An AST.Class object is created and
   80|     inserted into the current scope. The inheritance of the class is also
   81|     parsed, and nested classes and methods recursively processed."""
   82|     name = scopeName(ci.get_name())
   83|     clas = AST.Class(sourcefile,-1, 'Python', 'class', name)
   84|     comm = AST.Comment(ci.get_docstring(), '', -1)
   85|     clas.comments().append(comm)
   86|     # Figure out bases:
   87|     for base in ci.get_base_names():
   88|         try:
   89|             t = types[scopeName(base)]
   90|         except KeyError:
   91|             #print "Adding Unknown type for",scopeName(base)
   92|             t = Type.Unknown("Python", scopeName(base))
   93|         clas.parents().append(AST.Inheritance('', t, ''))
   94|     # Add the new class
   95|     addDeclaration(clas)
   96|     types[clas.name()] = Type.Declared("Python", clas.name(), clas)
   97|     push(clas)
   98|     for name in ci.get_class_names():
   99|         process_ClassInfo(ci.get_class_info(name))
  100|     for name in ci.get_method_names():
  101|         process_MethodInfo(ci.get_method_info(name))
  102|     pop()
  103|     
  104| def usage():
  105|     """Prints a usage message"""
  106|     print \
  107| """
  108|   -b <basename>                        Specify basename"""
  109| 
  110| def __parseArgs(args, config_obj):
  111|     """Parses the command line arguments and the config object"""
  112|     global basename, verbose
  113|     # Set defaults
  114|     basename = ''
  115| 
  116|     # Parse config object
  117|     if hasattr(config_obj, 'verbose') and config_obj.verbose:
  118|         verbose = 1
  119|     if hasattr(config_obj, 'basename') and config_obj.basename:
  120|         basename = config_obj.basename
  121| 
  122|     # Parse command line arguments
  123|     try:
  124|         opts,remainder = getopt.getopt(args, "b:v")
  125|     except getopt.error, e:
  126|         sys.stderr.write("Error in arguments: " + str(e) + "\n")
  127|         sys.exit(1)
  128| 
  129|     for opt in opts:
  130|         o,a = opt
  131|         if o == "-b": basename = a
  132|         elif o == "-v": verbose = 1
  133| 
  134| def get_synopsis(file):
  135|     """Returns the docstring from the top of an open file"""
  136|     mi = exparse.get_docs(file)
  137|     return mi.get_docstring()
  138| 
  139| def parse(file, extra_files, parser_args, config_obj):
  140|     """Entry point for the Python parser"""
  141|     __parseArgs(parser_args, config_obj)
  142|     global scopes, filename, types, basename, returnType, sourcefile
  143| 
  144|     ast = AST.AST()
  145|     filename = file
  146|     types = ast.types()
  147| 
  148|     # Split the filename into a scoped tuple name
  149|     name = file
  150|     if file[:len(basename)] == basename:
  151|         name = filename = file[len(basename):]
  152|     name = string.split(os.path.splitext(name)[0], os.sep)
  153|     exparse.packagepath = string.join(string.split(file, os.sep)[:-1], os.sep)
  154|     exparse.packagename = name[:-1]
  155|     #print "package path, name:",exparse.packagepath, exparse.packagename
  156|     sourcefile = AST.SourceFile(filename, file, 'Python')
  157|     sourcefile.set_is_main(1)
  158| 
  159|     # Create the enclosing module scopes
  160|     scopes = [AST.Scope(sourcefile, -1, 'Python', 'file scope', name)]
  161|     for depth in range(len(name) - 1):
  162|         module = AST.Module(sourcefile, -1, 'Python', 'package', name[:depth+1])
  163|         addDeclaration(module)
  164|         types[module.name()] = Type.Declared("Python", module.name(), module)
  165|         push(module)
  166|     # Add final name as Module -- unless module is __init__
  167|     if name[-1] != '__init__':
  168|         module = AST.Module(sourcefile, -1, 'Python', 'module', name)
  169|         addDeclaration(module)
  170|         types[module.name()] = Type.Declared("Python", module.name(), module)
  171|         push(module)
  172|     
  173|     # Parse the file
  174|     mi = exparse.get_docs(file)
  175| 
  176|     # Create return type for Python functions:
  177|     returnType = Type.Base('Python',('',))
  178| 
  179|     # Process the parsed file
  180|     process_ModuleInfo(mi)
  181| 
  182|     # Store the declarations
  183|     ast.files()[filename] = sourcefile
  184|     ast.declarations().extend(scopes[0].declarations())
  185| 
  186|     # Return new AST object
  187|     return ast
  188|