#############################################################################
# Copyright (C) DSTC Pty Ltd (ACN 052 372 577) 1993, 1994, 1995.
# Unpublished work.  All Rights Reserved.
#
# The software contained on this media is the property of the
# DSTC Pty Ltd.  Use of this software is strictly in accordance
# with the license agreement in the accompanying LICENSE.DOC file.
# If your distribution of this software does not contain a
# LICENSE.DOC file then you have no rights to use this software
# in any manner and should contact DSTC at the address below
# to determine an appropriate licensing arrangement.
# 
#      DSTC Pty Ltd
#      Level 7, Gehrmann Labs
#      University of Queensland
#      St Lucia, 4072
#      Australia
#      Tel: +61 7 3365 4310
#      Fax: +61 7 3365 4311
#      Email: enquiries@dstc.edu.au
# 
# This software is being provided "AS IS" without warranty of
# any kind.  In no event shall DSTC Pty Ltd be liable for
# damage of any kind arising out of or in connection with
# the use or performance of this software.
#
# Project:  Python Tools
#
# File:     ASTWalker.py
#
# Description:
#           Classes to walk Python ASTs at a higher level than tuple
#           representation.
#
# History:
#           19-Dec-1995  Martin Chilvers (martin) : created
#
# "@(#)$RCSfile: ASTWalker.py,v $ $Revision: 1 $"
#
#$Log: /Gendoc/ASTWalker.py $
# 
# 1     98-04-01 13:15 Daniel
# Revision 1.2  1996/07/08  05:10:45  omfadmin
# Incorporated changes Fred Drake made to docco (support for Python 1.4).
#
# Revision 1.1  1996/06/13  17:57:56  omfadmin
# Initial revision
#
# Revision 1.3  1995/12/19 01:43:04  martin
# Added DSTC header.
#
#
#############################################################################

""" Classes to walk Python ASTs at a higher level than tuple representation.

The 'ASTWalker' class allows Python ASTs to be walked at a 'higher' level by
allowing a builder object to translate AST nodes in tuple form into some other
representation. The translated nodes are then passed to the method that
handles nodes of the appropriate type.

"""

# Built-in/standard modules.
import string

# Local modules.
import ast
import ast_doc

import symbol
import token

class ASTWalker:
    """ Walk python ASTs translating nodes and passing them to methods."""

    def __init__(self, node_builder = None):
	""" Constructor.

	'node_builder'  is the object that is responsible for translating
	                AST nodes from tuple form into a nicer(TM!) form.

	"""
	
	# Allow the builder to be specified to create different node
	# representations.
	if node_builder != None:
	    self.node_builder = node_builder
	else:
	    self.node_builder = ASTNodeBuilder()

	# Map methods to the nodes that we are interested in (ie. the ones
	# that we have got around to defining make functions for!).
	self.d_node_map = node_map = {}
	for num, name in symbol.sym_name.items() + [
	    (token.DEDENT, 'dedent'), (token.INDENT, 'indent')]:
	    try:
		node_map[num] = getattr(self, name)
	    except AttributeError:
		pass

	return


    def walk(self, the_ast):
	""" Walk the AST calling the appropriate method at each node. """

	ast.map_fn(the_ast, self.doit)
	return


    def doit(self, node):
	""" Translate the node and pass it to the appropriate method. """

	node_object = self.node_builder.make_node(node)
	try:
	    # Call the appropriate method with the translated node object as
	    # its only parameter.
	    map_fn = self.d_node_map[ast.node_type(node)]
	    apply(map_fn, (node_object,))

	except KeyError:
	    pass

	return

    # Override the following functions in a derived-class to do what you want
    # with the various nodes!

    def file_input(self, ref_module):
	""" Override this to handle module nodes. """
	pass

    def import_stmt(self, ref_import):
	""" Override this to handle imported module nodes. """
	pass

    def classdef(self, ref_classdef):
	""" Override this to handle class definition nodes. """
	pass

    def funcdef(self, ref_funcdef):
	""" Override this to handle function definition nodes. """
	pass

    def indent(self, ref_indent):
	""" Override this to handle indent nodes. """
	pass

    def dedent(self, ref_dedent):
	""" Override this to handle dedent nodes. """
	pass


class ASTNodeBuilder:
    """ Translates AST nodes from tuples into object instances. """

    def __init__(self):
	self.d_make_map = {symbol.file_input: ModuleNode,
			   symbol.classdef:   ClassDefNode,
			   symbol.funcdef:    FunctionDefNode,
			   symbol.import_stmt:ImportNode,
			   token.INDENT:      IndentNode,
			   token.DEDENT:      DedentNode}
	return


    def make_node(self, node):
	""" Make an object instance to represent the AST node.

	'node'  is an AST node in tuple form.

	"""

	try:
	    make_fn = self.d_make_map[ast.node_type(node)]
	    new_node = apply(make_fn, (node,))

	except KeyError:
	    new_node = None

	return new_node


class ASTNode:
    """ Abstract class representation of an AST node. """

    def __init__(self, ast):
	""" Constructor.

	'ast'  is a Python AST in tuple form.

	"""
	self.ast = ast
	return


class ModuleNode(ASTNode):
    """ Class representation of a module node.

    'self.doc'  is the module documentation.

    """

    def __init__(self, node):
	""" Constructor.

	'node'  is a module node in tuple form.

	"""
	ASTNode.__init__(self, node)

	# Get the module documentation.
	self.doc = ast_doc.module_doc(node)

	return


class ImportNode(ASTNode):
    """Class representation of an import statement node."""

    def __init__(self, node):

	ASTNode.__init__(self, node)

	self.name = ast_doc.import_name(node)


class ClassDefNode(ASTNode):
    """ Class representation of a class definition node.

    'self.name'        is the classname.
    'self.inheritance  is a list of the base classes.
    'self.doc'         is the class documentation.

    """

    def __init__(self, node):
	""" Constructor.

	'node'  is a class definition node in tuple form.

	"""
	ASTNode.__init__(self, node)

	# Get the classname, base classes and documentation. 
	(self.name, self.inheritance, self.doc) = ast_doc.class_doc(node)

	return

    
    def __str__(self):
	""" Return a string representation of the class header. """

	str = 'class %s' % self.name
	if self.inheritance != []:
	    str = str + '('
	    str = str + string.joinfields(self.inheritance, ', ')
	    str = str + ')'
	str = str + ':'

	return str


class FunctionDefNode(ASTNode):
    """ Class representation of a function definition node.

    'self.name'       is the function name.
    'self.parameters  is a list of the parameters [(Parameter, DefaultValue)].
    'self.doc'        is the function documentation.

    """

    def __init__(self, node):
	""" Constructor.

	'node'  is a function definition node in tuple form.

	"""
	ASTNode.__init__(self, node)

	# Get the function name, parameters, and documentation.
	(self.name, self.parameters, self.doc) = ast_doc.function_doc(node)

	return


    def __str__(self):
	""" Return a string representation of the function header. """

	str = 'def %s(' % self.name
	if self.parameters != []:
	    lst_parameters = map(self.format_parameter, self.parameters)
	    str = str + string.joinfields(lst_parameters, ', ')
	str = str + '):'

	return str


    def format_parameter(self, (name, default)):
	""" Return a string representation of a formal parameter. """

	str = name
	if default != None:
	    str = str + '=' + default
	return str


class IndentNode(ASTNode):
    """ Class representation of an indent node. """

    def __init__(self, node):
	""" Constructor.

	'node'  is an indent node in tuple form.

	"""
	ASTNode.__init__(self, node)
	return


class DedentNode(ASTNode):
    """ Class representation of a dedent node. """
    
    def __init__(self, node):
	""" Constructor.

	'node'  is a dedent node in tuple form.

	"""
	ASTNode.__init__(self, node)
	return
