#!/usr/bin/env python
#############################################################################
# Copyright (C) DSTC Pty Ltd (ACN 052 372 577) 1997, 1998, 1999
# 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.HTML file.  If your
# distribution of this software does not contain a LICENSE.HTML 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, GP South
#      Staff House Road
#      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:      Fnorb
# File:         $Source: /cvsroot/fnorb/fnorb/parser/IDLParser.py,v $
# Version:      @(#)$RCSfile: IDLParser.py,v $ $Revision: 1.25 $
#
#############################################################################
""" Parser for OMG IDL. """


# Standard/built-in modules.
import new, string, sys, types

# Fnorb modules.
from Fnorb.orb import CORBA, Fixed, Limits, Util

# Fnorb extension modules.
use_bison = 1
try:
    import bison
except ImportError:
    use_bison = 0

# YAPPS parser
import idl

# Parser modules.
import Declarator, Prefix, Stack


class IDLParser:
    """ Parser for OMG IDL.

    This class implements the semantic actions for the IDL parser. The actual
    parsing engine is implemented in 'C' using 'flex' and 'bison'. 

    """

    #########################################################################
    # Parser interface.
    #########################################################################

    def parse(self, repository, filename, yyin):
	""" Parse some IDL!

	'repository' is the interface repository (IFR) to be populated.
	'filename'   is the filename of the top-level IDL file.
	'yyin'       is an open file object to read the IDL from.

	"""
        if use_bison:
            return self.parse_bison(repository, filename, yyin)
        else:
            return self.parse_yapps(repository, filename, yyin)

    def parse_bison(self, repository, filename, yyin):
	self.__repository = repository
	self.__filename = filename

	# Initialise the various data structures used during parsing.
	self.__initialise()

	# Do the parsing!
	result = bison.yyparse(self, yyin)

	# Clean up!
	self.__cleanup()

	return (result, self.__top_level_definitions)

    def parse_yapps(self, repository, filename, yyin):
	""" Parse some IDL!

	'repository' is the interface repository (IFR) to be populated.
	'filename'   is the filename of the top-level IDL file.
	'yyin'       is an open file object to read the IDL from.

	"""
        # Tokenize from a file-like object
        text = yyin.read()+'$'
        tokens = idl.IDLScanner(text)
        tokens.p = self
        return self._parse_yapps(repository, filename, tokens, text = text)

    def parse_tokenized(self, repository, filename, preprocessor):
        # Tokenize from a parser.cpp.Preprocessor
        import cpp

        try:
            tokens = idl.TokenizedScanner(preprocessor)
        except cpp.SyntaxError, e:
            print "SyntaxError:" + e.args[0]
            return (1, None)
        tokens.p = self
        return self._parse_yapps(repository, filename, tokens)

    def _parse_yapps(self, repository, filename, tokens, text = None):
	self.__repository = repository
	self.__filename = filename

	# Initialise the various data structures used during parsing.
	self.__initialise()

	# Do the parsing!
        parser = idl.IDLParser(tokens)
        parser.p = self
        global bison
        bison = tokens # to support bison 
        try:
            parser.start()
            result = 0
        except idl.SyntaxError,e:
            line = tokens.yyfileandlineno()
            if type(line) is types.TupleType:
                file, line = line
            else:
                file = self.__current_file
            print "%s:%s:%s" % (file, line, e.msg)
            # Can't rely on e.pos: it is sometimes character position,
            # sometimes token number
            # p = e.pos
            try:
                p = tokens.tokens[parser._pos][0]
            except IndexError:
                p = -1
            if p != -1 and type(p) is not types.TupleType and text:
                left = string.rfind(text, '\n', max(p-80,0),p)
                right = string.find(text, '\n', p, p+80)
                line = text[left+1:right] # this takes into account -1 results
                p = p - left - 1
                l1 = line[:p]
                l2 = line[p:]
                l1 = string.expandtabs(l1)
                p = len(l1)
                line = l1 + l2
                while p > 60:
                    # Cut off 10 chars
                    line = "..."+line[10:]
                    p = p - 7
                print "> "+line
                print "> "+" "*p+"^"    
            print "List of nearby tokens",tokens
            
            result = 1

	# Clean up!
	self.__cleanup()

	return (result, self.__top_level_definitions)


    def yyerror(self, message):
	""" Error function called from the 'bison' parser. """

	sys.stderr.write("Error: %s on line %d of %s\n" % \
			 (message, bison.yylineno(), self.__current_file))
	return

    def yywarning(self, message):
	""" Warning function. """

	sys.stderr.write("Warning: %s on line %d of %s\n" % \
			 (message, bison.yylineno(), self.__current_file))
	return

    #########################################################################
    # Semantic actions (called from 'grammar.y').
    #########################################################################

    def specification_start(self):
	""" specification: """

	# The container stack initially contains just the repository.
	self.__container_stack = Stack.Stack()
	self.__container_stack.push(self.__repository)

	return

    def specification_end(self):
	""" specification: definition_PLUS """

	# Report an error for any interfaces that were 'forward' declared but
	# not defined!
	self.__check_forward_interfaces()

	# Pop the repository from the container stack, and make sure that the
	# stack really is empty!
	self.__container_stack.pop()
	assert(len(self.__container_stack) == 0)

	return

    def module_header(self, name):
	""" MODULE IDENTIFIER """

	# Get the container and prefix from the stacks.
	container = self.__container_stack.get()
	prefix = self.__prefix_stack.get()

	# Does the container already contain a definition with this name?
	module = container.lookup(name)

	# If there is *no* definition with this name then we can safely create
	# a new module.
	if module is None:
	    # Currently the version is always "1.0"!
	    version = "1.0"

	    # Generate an interface repository id.
	    ifr_id = self.__create_ifr_id(prefix, name, version)

	    # Create a new module definition in the container.
	    module = container.create_module(ifr_id, name, version)

	    # If the module is defined at the global scope of the top-level IDL
	    # file then add it to our list of top-level definitions.
	    if self.__is_a_top_level_definition(module):
		self.__top_level_definitions.append(module)

	# Otherwise, a definition with this name already exists.
	else:
	    # If it is a module definition then we just "re-open" it to allow
	    # more definitions to be added.  If it is not a module definition
	    # then it is illegal!
	    if module._get_def_kind() != CORBA.dk_Module:
		self.yyerror("Redefinition of name '%s'" % name)
		raise bison.YYERROR

	    # If the module is defined at the global scope of the top-level IDL
	    # file then add it to our list of top-level definitions.
	    #
	    # We have to do this here as the first occurence of the module may
	    # have been in a file that was '#included'.
	    if self.__is_a_top_level_definition(module):
		self.__top_level_definitions.append(module)

	    # fixme: Check for ID compatability in the case of '#pragma ID'?
	    pass

	# Push the module onto the container stack.
	self.__container_stack.push(module)

	# Push the new prefix onto the prefix stack.
	self.__prefix_stack.push(self.__create_prefix(prefix, name))    

	return

    def module_body(self):
	""" '{' definition_STAR '}' """
	
	# Pop the container and prefix from the stacks.
	self.__container_stack.pop()
	self.__prefix_stack.pop()
	
	return

    def interface_dcl_header(self, (abstract, name), base_interfaces):
	""" [ABSTRACT|LOCAL] INTERFACE IDENTIFIER interface_inheritance_spec_OPT """

        assert abstract is None

	# Get the container and prefix from the stacks.
	container = self.__container_stack.get()
	prefix = self.__prefix_stack.get()

	# Does the container already contain a definition with this name?
	interface = container.lookup(name)

	# If there is *no* definition with this name then we can safely create
	# a new interface.
	if interface is None:
	    # Currently the version is always "1.0"!
	    version = "1.0"

	    # Generate an interface repository id.
	    ifr_id = self.__create_ifr_id(prefix, name, version)

	    # Create a new interface definition in the container.
	    interface = container.create_interface(ifr_id, name, version, [])

	    # If the interface is defined at the global scope of the top-level
	    # IDL file then add it to our list of top-level definitions.
	    if self.__is_a_top_level_definition(interface):
		self.__top_level_definitions.append(interface)

	# Otherwise, a definition with this name already exists.
	else:
	    # If it is *not* an interface definition then it is definitely
	    # illegal!
	    if interface._get_def_kind() != CORBA.dk_Interface:
		self.yyerror("Redefinition of name '%s'" % name)
		raise bison.YYERROR

	    # If it *is* an interface definition then it must be a forward
	    # declaration. If not then it is also illegal!
	    if interface._get_version() != 'forward':
		self.yyerror("Redefinition of name '%s'" % name)
		raise bison.YYERROR

	    # fixme: Check for ID compatability in the case of '#pragma ID'?
	    pass

	    # 'Move' the interface definition to the same container (this has
	    # the side-effect of placing it last in the containers list of
	    # contents ;^)
	    interface.move(container, name, "1.0")

	    # Delete the interface from the dictionary containing forward
	    # declared but undefined interfaces,
	    del self.__forward_interfaces[interface._get_id()]

	# Check the base interfaces.
	for base in base_interfaces:
	    # Make sure that the base interface is not a forward delcaration.
	    if base._get_version() == 'forward':
		self.yyerror("Interface '%s' not defined" % base._get_name())
		raise bison.YYERROR

	    # Make sure that some poor shmuck is not attempting to make the 
	    # interface inherit from itself!
	    #
	    # fixme: Will this work in the presence of '#pragma ID'?
	    if base._get_id() == interface._get_id():
		self.yyerror("Interface '%s' inherits from itself!" % name)
		raise bison.YYERROR

	# Update the list of base interfaces.
	interface._set_base_interfaces(base_interfaces)
	    
	# Push the interface onto the container stack.
	self.__container_stack.push(interface)

	# Push a new prefix onto the prefix stack.
	self.__prefix_stack.push(self.__create_prefix(prefix, name))    

	return interface

    def interface_dcl_body(self, interface):
	""" '{' export_STAR '}' """

	# Get the interface description.
	description = interface.describe_interface()

	# Get the names of all of the interface's operations and attributes.
	names = map(lambda o: o.name, description.operations)
	names = names + map(lambda a: a.name, description.attributes)

	# Add the interface to the interface dictionary.
	self.__interfaces[interface._get_id()] = names

	# Get the names of all inherited operations and attributes.
	inherited = self.__get_inherited_operations(interface, [])

	# Make sure there are no clashes!
	if len(inherited) > 0:
	    for name in names:
		if name in inherited:
		    self.yyerror("Overloaded operation/attribute '%s'" % name)
		    raise bison.YYERROR

	# Pop the container and prefix from the stacks.
	self.__container_stack.pop()
	self.__prefix_stack.pop()
	
	return

    def foward_dcl(self, (abstract, name)):
	""" forward_dcl: [ABSTRACT|LOCAL] INTERFACE IDENTIFIER """

        assert abstract is None

	# Get the container and prefix from the stacks.
	container = self.__container_stack.get()
	prefix = self.__prefix_stack.get()

	# Does the container already contain a definition with this name?
	interface = container.lookup(name)

	# If there is *no* definition with this name then we can safely create
	# a new interface.
	if interface is None:
	    # Currently the version is always "1.0"!
	    version = "1.0"

	    # Generate a repository id.
	    ifr_id = self.__create_ifr_id(prefix, name, version)

	    # Create a new interface definition in the container and tag it
	    # with a version of "forward".
	    interface = container.create_interface(ifr_id, name, "forward", [])

	    # If the interface is defined at the global scope of the top-level
	    # IDL file then add it to our list of top-level definitions.
	    if self.__is_a_top_level_definition(interface):
		self.__top_level_definitions.append(interface)

	    # Add the interface to the dictionary of forward declared but
	    # undefined interfaces.
	    self.__forward_interfaces[ifr_id] = (interface, bison.yylineno())

	# Otherwise, a definition with this name already exists.
	else:
	    # If it is *not* an interface definition then it is illegal!
	    if interface._get_def_kind() != CORBA.dk_Interface:
		self.yyerror("Redefinition of name '%s'" % name)
		raise bison.YYERROR

	    # fixme: Check for ID compatability in the case of '#pragma ID'?
	    pass

	return

    def interface_inheritance_spec_empty(self):
	""" interface_inheritance_spec: /* empty */ """

	return [] # [InterfaceDef]

    def interface_inheritance_spec_full(self, scoped_names):
	""" interface_inheritance_spec: ':' scoped_name_CSV_PLUS """

	# Get the container from the stack.
	container = self.__container_stack.get()

	# Find the interface definition for each scoped name.
	base_interfaces = []
	for scoped_name in scoped_names:
	    # Lookup the definition.
	    interface = self.__find_scoped_name(container, scoped_name)
	    if interface is None:
		raise bison.YYERROR

	    # If it is *not* an interface definition then it is illegal!
	    if interface._get_def_kind() != CORBA.dk_Interface:
		self.yyerror("'%s' is not an interface" % scoped_name)
		raise bison.YYERROR

	    base_interfaces.append(interface)

	return base_interfaces

    def value_dcl_header(self, (abstract, name),
                         (base_values, supported_interface_names)):
	""" [ABSTRACT|CUSTOM] INTERFACE IDENTIFIER value_inheritance_spec """

        # could be "abstract", "custom", or None
        is_abstract = abstract == "abstract"
        is_custom = abstract == "custom"

        # Don't know the initializers yet
        initializers = []

	# Get the container and prefix from the stacks.
	container = self.__container_stack.get()
	prefix = self.__prefix_stack.get()

        # could be None, or (truncatable, base_values)
        if base_values is None:
            is_truncatable = CORBA.FALSE
            base_value = None
            base_values = []
            abstract_base_values = []
        else:
            is_truncatable, bases = base_values
            base_values = []
            for scoped_name in bases:
                # Lookup the definition.
                value = self.__find_scoped_name(container, scoped_name)
                if value is None:
                    raise bison.YYERROR
                
                # If it is not a value then it is illegal!
                if value._get_def_kind() != CORBA.dk_Value:
                    self.yyerror("'%s' is not a valuetype" % scoped_name)
                    raise bison.YYERROR

                base_values.append(value)

            if not base_values[0]._get_is_abstract():
                # the first base might be concrete
                base_value = base_values[0]
                abstract_base_values = base_values[1:]
            else:
                base_value = None
                abstract_base_values = base_values
                if is_truncatable:
                    self.yyerror("'%s' has an abstract truncatable base"
                                 % scoped_name)
                    raise bison.YYERROR

            # make sure bases are abstract
            for value in abstract_base_values:
                if not value._get_is_abstract():
                    self.yyerror("'%s' is not abstract" % value._get_name())

        # could be None or list of scoped names
        if supported_interface_names is None:
            supported_interface_names = []

        supported_interfaces = []
        for scoped_name in supported_interface_names:
            # Lookup the definition.
            interface = self.__find_scoped_name(container, scoped_name)
            if value is None:
                raise bison.YYERROR
                
            # If it is not an interface then it is illegal!
            if value._get_def_kind() != CORBA.dk_Interface:
                self.yyerror("'%s' is not a valuetype" % scoped_name)
                raise bison.YYERROR

            supported_interfaces.append(interface)
            

	# Does the container already contain a definition with this name?
	value = container.lookup(name)

	# If there is *no* definition with this name then we can safely create
	# a new valuetype.
	if value is None:
	    # Currently the version is always "1.0"!
	    version = "1.0"

	    # Generate an interface repository id.
	    ifr_id = self.__create_ifr_id(prefix, name, version)

	    # Create a new value definition in the container.
	    value = container.create_value(ifr_id, name, version,
                                           is_custom, is_abstract,
                                           base_value, is_truncatable,
                                           abstract_base_values,
                                           supported_interfaces,
                                           initializers)

	    # If the value is defined at the global scope of the top-level
	    # IDL file then add it to our list of top-level definitions.
	    if self.__is_a_top_level_definition(value):
		self.__top_level_definitions.append(value)

	# Otherwise, a definition with this name already exists.
	else:
	    # If it is *not* an value definition then it is definitely
	    # illegal!
	    if value._get_def_kind() != CORBA.dk_Value:
		self.yyerror("Redefinition of name '%s'" % name)
		raise bison.YYERROR

	    # If it *is* an value definition then it must be a forward
	    # declaration. If not then it is also illegal!
	    if value._get_version() != 'forward':
		self.yyerror("Redefinition of name '%s'" % name)
		raise bison.YYERROR

	    # fixme: Check for ID compatability in the case of '#pragma ID'?
	    pass

	    # 'Move' the value definition to the same container (this has
	    # the side-effect of placing it last in the containers list of
	    # contents ;^)
	    value.move(container, name, "1.0")

	    # Delete the value from the dictionary containing forward
	    # declared but undefined value,
	    del self.__forward_interfaces[value._get_id()]

	# Check the base values.
	for base in base_values:
	    # Make sure that the base value is not a forward delcaration.
	    if base._get_version() == 'forward':
		self.yyerror("Value '%s' not defined" % base._get_name())
		raise bison.YYERROR

	    # Make sure that some poor shmuck is not attempting to make the 
	    # value inherit from itself!
	    #
	    # fixme: Will this work in the presence of '#pragma ID'?
	    if base._get_id() == value._get_id():
		self.yyerror("Value '%s' inherits from itself!" % name)
		raise bison.YYERROR

	# Update the list of base interfaces.
        # XXX already done during creation
	# interface._set_base_interfaces(base_interfaces)
	    
	# Push the interface onto the container stack.
	self.__container_stack.push(value)

	# Push a new prefix onto the prefix stack.
	self.__prefix_stack.push(self.__create_prefix(prefix, name))    

        # Return a the value, and a list to collect initializers in
	return value, []

    def value_dcl_body(self, (value, initializers)):
	""" '{' export_STAR '}' """

	# Get the value description.
	description = value.describe_value()

	# Get the names of all of the value's operations and attributes.
	names = map(lambda o: o.name, description.operations)
	names = names + map(lambda a: a.name, description.attributes)

        # XXX check supported interfaces. What constraints?

	# Add the interface to the interface dictionary.
	self.__interfaces[value._get_id()] = names

	# Get the names of all inherited operations and attributes.
	inherited = self.__get_inherited_operations(value, [])

	# Make sure there are no clashes!
	if len(inherited) > 0:
	    for name in names:
		if name in inherited:
		    self.yyerror("Overloaded operation/attribute '%s'" % name)
		    raise bison.YYERROR

	# Pop the container and prefix from the stacks.
	self.__container_stack.pop()
	self.__prefix_stack.pop()
	
	return

    def value_box_dcl(self, (abstract, name), type_def):
        """ value_box_dcl: VALUETYPE IDENTIFIER type_spec"""

        # Verify we are not trying to build an abstract box.
        if abstract is not None:
            self.yyerror("'%s' not allowed for value boxes" % abstract)
            raise bison.YYERROR

	# Get the container and prefix from the stacks.
	container = self.__container_stack.get()
	prefix = self.__prefix_stack.get()

	# Does the container already contain a definition with this name?
	box = container.lookup(name)

	# If there is *no* definition with this name then we can safely create
	# a new valuebox.
	if box is None:
	    # Currently the version is always "1.0"!
	    version = "1.0"

	    # Generate an interface repository id.
	    ifr_id = self.__create_ifr_id(prefix, name, version)

	    # Create a new valuebox definition in the container.
	    box = container.create_value_box(ifr_id, name, version,
                                             type_def)

	    # If the valuebox is defined at the global scope of the top-level
	    # IDL file then add it to our list of top-level definitions.
	    if self.__is_a_top_level_definition(box):
		self.__top_level_definitions.append(box)

	# Otherwise, a definition with this name already exists.
	else:
	    self.yyerror("Redefinition of name '%s'" % name)
	    raise bison.YYERROR

	return box

    def state_member(self, public, type_def, declarators):
        """ state_member: [PUBLIC|PRIVATE] type_spec declarators ; """

	# Get the container and prefix from the stacks.
	container = self.__container_stack.get()
	prefix = self.__prefix_stack.get()

        if public == "public":
            visibility = CORBA.PUBLIC_MEMBER
        else:
            visibility = CORBA.PRIVATE_MEMBER

	members = []
	for declarator in declarators:
	    # If this is an array declarator.
	    if isinstance(declarator, Declarator.ArrayDeclarator):
		# Get the array dimensions.
		dimensions = declarator.dimensions()

		# Create the array type.
		actual_type_def = self.__create_array(type_def, dimensions)

	    # Otherwise it is a simple declarator.
	    else:
		actual_type_def = type_def

            name = declarator.name()

	    # Currently the version is always "1.0"!
	    version = "1.0"

	    # Generate an interface repository id.
	    ifr_id = self.__create_ifr_id(prefix, name, version)

	    # Create a new value member in the container.
	    member = container.create_value_member(ifr_id, name, version,
                                               actual_type_def, visibility)

            members.append(member)

        return members

    def init_dcl(self, name, params, (value, initializers)):
        """ init_dcl: FACTORY name ( [init_param_dcls] ) ; """

        members = []
        for p in params:
            if p.mode != CORBA.PARAM_IN:
                self.yyerror("invalid parameter mode in factory")
                raise bison.YYERROR

            members.append(CORBA.StructMember(p.name,
                                              p.type,
                                              p.type_def))

        initializers.append(CORBA.Initializer(members, name))

    def scoped_name_absolute(self, identifier):
	""" scoped_name: SCOPE_DELIMITER IDENTIFIER """

	return "::" + identifier

    def scoped_name_relative(self, scoped_name, identifier):
	""" scoped_name: scoped_name SCOPE_DELIMITER IDENTIFIER """

	return scoped_name + "::" + identifier

    def const_dcl(self, type_def, name, any):
	""" const_dcl: CONST const_type IDENTIFIER '=' const_expr """

	# Get the container and prefix from the stacks.
	container = self.__container_stack.get()
	prefix = self.__prefix_stack.get()

	# Does the container already contain a definition with this name?
	constant = container.lookup(name)

	# If there is *no* definition with this name then we can safely create
	# a new constant.
	if constant is None:
	    # Make sure that the value is of the appropriate type.
	    if not self.__check_constant_type(type_def, any):
		self.yyerror("Constant '%s' invalid value type" % name)
		raise bison.YYERROR

	    # Make sure that the value is within the limits of the type, and
	    # coerce it if necessary.
	    (result, value) = self.__check_limits(type_def, any)
	    if not result:
		self.yyerror("Constant '%s' out of range" % name)
		raise bison.YYERROR

	    # Wrap the coerced value back in an 'Any' of the appropriate type.
	    any = CORBA.Any(type_def._get_type(), value)

	    # Currently the version is always "1.0"!
	    version = "1.0"

	    # Generate an interface repository id.
	    ifr_id = self.__create_ifr_id(prefix, name, version)

	    # Create a new constant definition in the container.
	    constant = container.create_constant(ifr_id, name, version,
						 type_def, any)

	    # If the constant is defined at the global scope of the top-level
	    # IDL file then add it to our list of top-level definitions.
	    if self.__is_a_top_level_definition(constant):
		self.__top_level_definitions.append(constant)

	# Otherwise, a definition with this name already exists.
	else:
	    self.yyerror("Redefinition of name '%s'" % name)
	    raise bison.YYERROR

	return constant

    # Valid types for constant expressions.
    __CONST_TYPES = [CORBA.pk_short,
		     CORBA.pk_long,
		     CORBA.pk_ushort,
		     CORBA.pk_ulong,
		     CORBA.pk_char,
		     CORBA.pk_boolean,
		     CORBA.pk_float,
		     CORBA.pk_double,
		     CORBA.pk_string,
		     CORBA.pk_longlong,
		     CORBA.pk_ulonglong,
		     CORBA.pk_longdouble,
		     CORBA.pk_wchar,
		     CORBA.pk_wstring]

    def const_type_scoped_name(self, scoped_name):
	""" const_type: scoped_name """

	# Get the container from the stack.
	container = self.__container_stack.get()

	# Lookup the definition referenced by the scoped name.
	definition = self.__find_scoped_name(container, scoped_name)

	# Unwind any 'typedef' chain.
	definition = self.__unwind_typedef_chain(definition)

	# Make sure that the definition is either:-
	#
	# 1) One of the following primitive types:-
	#
	#    integer, char, wchar, boolean, floating point, string, wstring
	#
	# 2) Of type 'fixed'.
	if definition._get_def_kind() == CORBA.dk_Primitive \
	   and definition._get_kind() in IDLParser.__CONST_TYPES:
	    pass

	elif definition._get_def_kind() == CORBA.dk_Fixed:
	    pass

	else:
	    self.yyerror("Invalid constant type '%s'" % scoped_name)
 	    raise bison.YYERROR

	return definition

    # The arguments to constant expression operators are 'Any's, and the
    # operations are implemented in the 'Any' module.
    def or_expr(self, x, y):
	""" or_expr: or_expr '|' xor_expr """

	return x | y

    def xor_expr(self, x, y):
	""" xor_expr: xor_expr '^' and_expr """

	return x ^ y

    def and_expr(self, x, y):
	""" and_expr: and_expr '&' shift_expr """

	return x & y

    def shift_expr_right(self, x, y):
	""" shift_expr: shift_expr RIGHT_SHIFT add_expr """

	return x >> y

    def shift_expr_left(self, x, y):
	""" shift_expr: shift_expr LEFT_SHIFT add_expr """

	return x << y

    def add_expr_add(self, x, y):
	""" add_expr: add_expr '+' mult_expr """

	return x + y

    def add_expr_subtract(self, x, y):
	""" add_expr: add_expr '-' mult_expr """

	return x - y

    def mult_expr_multiply(self, x, y):
	""" mult_expr: mult_expr '*' unary_expr """

	return x * y

    def mult_expr_divide(self, x, y):
	""" mult_expr: mult_expr '/' unary_expr """

	return x / y

    def mult_expr_mod(self, x, y):
	""" mult_expr: mult_expr '%' unary_expr """

	return x % y

    def unary_expr_neg(self, x):
	""" unary_expr: '-' primary_expr """

	return -x

    def unary_expr_pos(self, x):
	""" unary_expr: '+' primary_expr """

	return +x

    def unary_expr_invert(self, x):
	""" unary_expr: '~' primary_expr """

	return ~x

    def primary_expr_scoped_name(self, scoped_name):
	""" primary_expr: scoped_name """

	# Get the container from the stack.
	container = self.__container_stack.get()

	# Lookup the definition referenced by the scoped name.
	definition = self.__find_scoped_name(container, scoped_name)
	if definition is None:
	    raise bison.YYERROR

	# If it is not a constant definition then it is illegal!
	if definition._get_def_kind() != CORBA.dk_Constant:
	    self.yyerror("'%s' is not a constant expression" % scoped_name)
	    raise bison.YYERROR

	# Return the constant's value (an 'Any').
	return definition._get_value()

    def literal_integer_literal(self, value):
	""" literal: INTEGER_LITERAL """

	# We get integer literals as the raw string token from the lexer.
	#
	# fixme: Should we do this for floating point literals as well?

	# All integer literals are converted to Python *long* integers and then
	# coerced back as appropriate.
	return CORBA.Any(CORBA.TC_longlong, eval(value + "L"))

    def literal_string_literal(self, value):
	""" literal: STRING_LITERAL """

	# Evaluate the string containing the string!
	return CORBA.Any(CORBA.TC_string, eval(value))

    def literal_character_literal(self, value):
	""" literal: CHARACTER_LITERAL """

	# Evaluate the string containing the character!
	return CORBA.Any(CORBA.TC_char, eval(value))

    def literal_fixed_pt_literal(self, value):
	""" literal: FIXED_PT_LITERAL """

	# Fixed point values are instances of the 'Fixed' class.
	fixed = new.instance(Fixed.Fixed, {})

	# Initialise the instance from the literal.
	fixed._fnorb_from_literal(value)

	return CORBA.Any(fixed._fnorb_typecode(), fixed)

    def literal_floating_pt_literal(self, value):
	""" literal: FLOATING_PT_LITERAL """

	return CORBA.Any(CORBA.TC_double, value)

    def boolean_literal_true(self):
	""" boolean_literal: TRUE """
	
	return CORBA.Any(CORBA.TC_boolean, 1)

    def boolean_literal_false(self):
	""" boolean_literal: FALSE """
	
	return CORBA.Any(CORBA.TC_boolean, 0)
	
    def positive_int_const(self, any):
	""" positive_int_const: const_expr """

	# Get the typecode's 'kind'.
	kind = any.typecode().kind()

	# Make sure that it really is a positive, integer ;^)
	if kind not in IDLParser.__TK_INT or any.value() < 0:
	    self.yyerror("Positive integer value required")
	    raise bison.YYERROR

	# Extract the value from the 'Any'.
	return any.value()

    def type_declarator(self, type_def, declarators):
	""" type_declarator: type_spec declarators """

	# Get the container and prefix from the stacks.
	container = self.__container_stack.get()
	prefix = self.__prefix_stack.get()

	# Create a 'typedef' definition for each declarator.
	for declarator in declarators:
	    # Get the name of the declarator.
	    name = declarator.name()

	    # Does the container already contain a definition with this name?
	    definition = container.lookup(name)

	    # If there is *no* definition with this name then we can safely
	    # create a new 'typedef' definition.
	    if definition is None:
		# Currently the version is always "1.0"!
		version = "1.0"

		# Generate an interface repository id.
		ifr_id = self.__create_ifr_id(prefix, name, version)

		# If this is an array declarator.
		if isinstance(declarator, Declarator.ArrayDeclarator):
		    # Get the array dimensions.
		    dimensions = declarator.dimensions()

		    # Create the array type.
		    actual_type_def = self.__create_array(type_def, dimensions)

		# Otherwise it is a simple declarator.
		else:
		    actual_type_def = type_def

		# Create a new 'typedef' definition in the container.
		definition = container.create_alias(ifr_id, name, version,
						    actual_type_def)

		# If the 'typedef' is defined at the global scope of the
		# top-level IDL file then add it to our list of top-level
		# definitions.
		if self.__is_a_top_level_definition(definition):
		    self.__top_level_definitions.append(definition)

	    # Otherwise, a definition with this name already exists.
	    else:
		self.yyerror("Redefinition of name '%s'" % name)
		raise bison.YYERROR
		
	return

    def simple_declarator(self, name):
	""" simple_declarator: IDENTIFIER """

	return Declarator.SimpleDeclarator(name)

    def float_type(self):
	""" floating_pt_type: FLOAT """

	return self.__repository.get_primitive(CORBA.pk_float)

    def double_type(self):
	""" floating_pt_type: DOUBLE """

	return self.__repository.get_primitive(CORBA.pk_double)

    def longdouble_type(self):
	""" floating_pt_type: LONG DOUBLE """

	return self.__repository.get_primitive(CORBA.pk_longdouble)

    def signed_short_int(self):
	""" signed_short_int: SHORT """

	return self.__repository.get_primitive(CORBA.pk_short)

    def signed_long_int(self):
	""" signed_long_int: LONG """

	return self.__repository.get_primitive(CORBA.pk_long)

    def signed_longlong_int(self):
	""" signed_longlong_int: LONG LONG"""

	return self.__repository.get_primitive(CORBA.pk_longlong)

    def unsigned_short_int(self):
	""" unsigned_short_int: UNSIGNED SHORT """

	return self.__repository.get_primitive(CORBA.pk_ushort)

    def unsigned_long_int(self):
	""" unsigned_long_int: UNSIGNED LONG """

	return self.__repository.get_primitive(CORBA.pk_ulong)

    def unsigned_longlong_int(self):
	""" unsigned_longlong_int: UNSIGNED LONG LONG"""

	return self.__repository.get_primitive(CORBA.pk_ulonglong)

    def char_type(self):
	""" char_type: CHAR """

	return self.__repository.get_primitive(CORBA.pk_char)

    def wide_char_type(self):
	""" wide_char_type: WCHAR """

	return self.__repository.get_primitive(CORBA.pk_wchar)

    def boolean_type(self):
	""" boolean_type: BOOLEAN """

	return self.__repository.get_primitive(CORBA.pk_boolean)

    def octet_type(self):
	""" octet_type: OCTET """

	return self.__repository.get_primitive(CORBA.pk_octet)

    def any_type(self):
	""" any_type: IDL_ANY """

	return self.__repository.get_primitive(CORBA.pk_any)

    def object_type(self):
	""" object_type: OBJECT """

	return self.__repository.get_primitive(CORBA.pk_objref)

    def struct_type_header(self, name):
	""" struct_type: STRUCT IDENTIFIER """

	# Get the container and prefix from the stacks.
	container = self.__container_stack.get()
	prefix = self.__prefix_stack.get()

	# Does the container already contain a definition with this name?
	struct = container.lookup(name)

	# If there is *no* definition with this name then we can safely create
	# a new structure definition.
	if struct is None:
	    # Set the version to "wip" (for Work In Progress) to allow the
	    # detection of recursive types.
	    version = "wip"
	    
	    # Generate an interface repository id.
	    ifr_id = self.__create_ifr_id(prefix, name, version)

	    # Create a new structure definition in the container.
	    struct = container.create_struct(ifr_id, name, version, [])

	    # If the structure is defined at the global scope of the top-level
	    # IDL file then add it to our list of top-level definitions.
	    if self.__is_a_top_level_definition(struct):
		self.__top_level_definitions.append(struct)

	# Otherwise, a definition with this name already exists.
	else:
	    self.yyerror("Redefinition of name '%s'" % name)
	    raise bison.YYERROR

	# Push the structure onto the container stack.
	self.__container_stack.push(struct)

	# Push a new prefix onto the prefix stack.
	self.__prefix_stack.push(self.__create_prefix(prefix, name))    

	return struct

    def struct_type_body(self, struct, members):
	""" struct_type: '{'  member_PLUS '}' """

	# Make sure that all member names are unique.
	dict = {}
	for member in members:
	    if dict.has_key(member.name):
		self.yyerror("Duplicate member name '%s'" % name)
		raise bison.YYERROR

	    dict[member.name] = None

 	    # Check for recursive members.
 	    if self.__is_recursive_member(member.type_def):
 		# Get the repository id of the recursive structure.
 		ifr_id = member.type_def._get_element_type_def()._get_id()

 		# Find out how deep the recursion is.
 		offset = self.__get_recursion_depth(struct, ifr_id)

 		# Get the bound of the sequence type.
 		length = member.type.length()

 		# Replace the sequence typecode with a recursive sequence
 		# typecode.
 		member.type = CORBA.RecursiveSequenceTypeCode(length, offset)

	# Fixup the version.
	struct._set_version("1.0")

	# Update the structure definition.
	struct._set_members(members)

	# Pop the container and prefix from the stacks.
	self.__container_stack.pop()
	self.__prefix_stack.pop()
	
	return struct

    def member(self, type_def, declarators):
	""" member: type_spec declarators """

	members = []
	for declarator in declarators:
	    # If this is an array declarator.
	    if isinstance(declarator, Declarator.ArrayDeclarator):
		# Get the array dimensions.
		dimensions = declarator.dimensions()

		# Create the array type.
		actual_type_def = self.__create_array(type_def, dimensions)

	    # Otherwise it is a simple declarator.
	    else:
		actual_type_def = type_def

	    # Create a 'StructMember' instance.
	    members.append(CORBA.StructMember(declarator.name(),
					      actual_type_def._get_type(),
					      actual_type_def))
	return members

    def union_type_header(self, name, type_def):
	""" union_type: UNION IDENTIFIER SWITCH '(' switch_type_spec ')' """

	# Get the container and prefix from the stacks.
	container = self.__container_stack.get()
	prefix = self.__prefix_stack.get()

	# Does the container already contain a definition with this name?
	union = container.lookup(name)

	# If there is *no* definition with this name then we can safely create
	# a new union.
	if union is None:
	    # Set the version to "wip" (for Work In Progress) to allow the
	    # detection of recursive types.
	    version = "wip"

	    # Generate an interface repository id.
	    ifr_id = self.__create_ifr_id(prefix, name, version)

	    # Create a new union definition in the container.
	    union = container.create_union(ifr_id, name, version, type_def, [])

	    # If the union is defined at the global scope of the top-level IDL
	    # file then add it to our list of top-level definitions.
	    if self.__is_a_top_level_definition(union):
		self.__top_level_definitions.append(union)

	# Otherwise, a definition with this name already exists.
	else:
	    self.yyerror("Redefinition of name '%s'" % name)
	    raise bison.YYERROR

	# Push the union onto the container stack.
	self.__container_stack.push(union)

	# Push a new prefix onto the prefix stack.
	self.__prefix_stack.push(self.__create_prefix(prefix, name))    

	return union

    def union_type_body(self, union, elements):
	""" union_type: '{' switch_body '}' """

        # First we break out each pair of (labels, element) into
        # (label, element) pairs ie flattening the label lists.
        # This is also the right place to check for duplicates, as
        # after the "flattening" we can't know which are duplicates
        # and which just happened to have multiple labels

        members = []

        names_seen = [] # All the element names we've processed
        
	for element in elements:

            labels, type_def, declarator = element
            
            typecode = type_def._get_type()
            name = declarator.name()

            if name in names_seen:
                self.yyerror("Duplicate member name '%s'" % name)
                raise bison.YYERROR

            names_seen.append(name)

            for label in labels:
                members.append(CORBA.UnionMember(name, label,
                                                 typecode, type_def))

	# Get the union's discrimintator (aka switch) typecode and type
	# definition.
	typecode = union._get_discriminator_type()
	type_def = union._get_discriminator_type_def()

	# Make sure that all member names and label values are unique. This
	# could take some time if some idiot has used, say an unsigned long as
	# the discriminator type and a couple of billion cases ;^)
	name_dict = {}
	label_dict = {}
	default_used = 0

	for member in members:
	    # Ignore the default case.
	    if member.label.typecode().kind() == CORBA.tk_octet:
		default_used = 1

	    else:
		# Get the label value.
		value = member.label.value()

		# Make sure the label is the same type as the discriminant
                # (or coercable to it).
		if not self.__check_label_type(type_def, member.label):
		    self.yyerror("Invalid label value %s" % str(value))
		    raise bison.YYERROR

		# Make sure that the value is within the limits of the type,
		# and coerce it if necessary.
		(result, value) = self.__check_limits(type_def, member.label)
		if not result:
		    self.yyerror("Label value '%s' out of range"
                                 % str(value))
		    raise bison.YYERROR

		# Wrap the coerced value back in an 'Any' of the appropriate
		# type.
		member.label = CORBA.Any(typecode, value)

		name_dict[member.name] = None

		# Label check.
		if label_dict.has_key(value):
		    self.yyerror("Duplicate case label %s" % str(value))
		    raise bison.YYERROR

		label_dict[value] = None

		# Check for recursive members.
		if self.__is_recursive_member(member.type_def):
		    # Get the repository id of the recursive structure.
		    ifr_id = member.type_def._get_element_type_def()._get_id()

		    # Find out how deep the recursion is.
		    offset = self.__get_recursion_depth(union, ifr_id)

		    # Get the bound of the sequence type.
		    length = member.type.length()

		    # Replace the sequence typecode with a recursive sequence
		    # typecode.
		    member.type = CORBA.RecursiveSequenceTypeCode(length,
								  offset)

	# If a default case has been specified, then make sure that the
	# case labels for the specified type haven't been exhausted.
	if default_used:
	    # Get the typecode kind.
	    kind = typecode.kind()

	    if kind == CORBA.tk_enum:
		limit = typecode.member_count()
		
	    elif kind == CORBA.tk_short \
		 or typecode.kind() == CORBA.tk_ushort:
		limit = 65536 # 2 ^ 16

	    elif kind == CORBA.tk_long \
	         or typecode.kind() == CORBA.tk_ulong:
		limit = 4294967296L # 2 ^ 32

	    elif kind == CORBA.tk_longlong \
	         or typecode.kind() == CORBA.tk_ulonglong:
		limit = 18446744073709551616L # 2 ^ 64

	    elif kind == CORBA.tk_char:
		limit = 256 # 2 ^ 8

	    elif kind == CORBA.tk_boolean:
		limit = 2 # 2 ;^)

	    if len(members) - 1 >= limit:
		self.yyerror("All case labels exhausted")
		raise bison.YYERROR

	# Fixup the version.
	union._set_version("1.0")

	# Update the union definition.
	union._set_members(members)

	# Pop the container and prefix from the stacks.
	self.__container_stack.pop()
	self.__prefix_stack.pop()
	
	return union

    # Valid types for union switch specifications.
    __SWITCH_TYPES = [CORBA.pk_short,
		      CORBA.pk_long,
		      CORBA.pk_longlong,
		      CORBA.pk_ushort,
		      CORBA.pk_ulong,
		      CORBA.pk_ulonglong,
		      CORBA.pk_char,
		      CORBA.pk_boolean]

    def switch_type_spec_scoped_name(self, scoped_name):
	""" switch_type_spec: scoped_name """

	# Get the container from the stack.
	container = self.__container_stack.get()

	# Lookup the definition referenced by the scoped name.
	definition = self.__find_scoped_name(container, scoped_name)

	# Unwind any 'typedef' chain.
	definition = self.__unwind_typedef_chain(definition)

	# Make sure the definition type is either integer, char, boolean, or
	# an enumeration.
	if definition._get_def_kind() == CORBA.dk_Primitive:
	    if definition._get_kind() not in IDLParser.__SWITCH_TYPES:
		self.yyerror("Invalid switch type '%s'" % scoped_name)
		raise bison.YYERROR

	elif definition._get_def_kind() != CORBA.dk_Enum:
	    self.yyerror("Invalid switch type '%s'" % scoped_name)
	    raise bison.YYERROR
       
	return definition

    def case(self, labels, (type_def, declarator)):
	""" case: case_label_PLUS element_spec ';' """

	typecode = type_def._get_type()
	name = declarator.name()

	members = [ (labels, type_def, declarator) ]
	
	return members

    def case_label_default(self):
	""" case_label: DEFAULT ':' """

	return CORBA.Any(CORBA.TC_octet, 0)

    def element_spec(self, type_def, declarator):
	""" element_spec: type_def declarator """

	return (type_def, declarator)

    def enum_type_header(self, name):
	""" enum_type: ENUM IDENTIFIER """

	# Get the container and prefix from the stacks.
	container = self.__container_stack.get()
	prefix = self.__prefix_stack.get()

	# Does the container already contain a definition with this name?
	enum = container.lookup(name)

	# If there is *no* definition with this name then we can safely create
	# a new enumeration.
	if enum is None:
	    # Currently the version is always "1.0"!
	    version = "1.0"

	    # Generate an interface repository id.
	    ifr_id = self.__create_ifr_id(prefix, name, version)

	    # Create a new enumeration definition in the container.
	    enum = container.create_enum(ifr_id, name, version, [])

	    # If the enumeration is defined at the global scope of the
	    # top-level IDL file then add it to our list of top-level
	    # definitions.
	    if self.__is_a_top_level_definition(enum):
		self.__top_level_definitions.append(enum)

	# Otherwise, a definition with this name already exists.
	else:
	    self.yyerror("Redefinition of name '%s'" % name)
	    raise bison.YYERROR

	return enum

    def enum_type_body(self, enum, members):
	""" enum_type: '{' enumerators '}' """

	# Get the container and prefix from the stacks.
	container = self.__container_stack.get()
	prefix = self.__prefix_stack.get()

	# Create temporary constant definitions for each member.
	for i in range(len(members)):
	    # Get the name of the member.
	    name = members[i]

	    # Does the container already contain a definition with this name?
	    constant = container.lookup(name)

	    # If there is *no* definition with this name then we can safely
	    # create a new constant.
	    if constant is None:
		# Tag the enum member with a distinctive version ;^)
		version = "Delete Me!"

		# Generate an interface repository id.
		ifr_id = self.__create_ifr_id(prefix, name, version)

		# Create an Enum member instance.
		any = CORBA.Any(enum._get_type(), Util.EnumMember(name, i))

		# Create a new constant definition in the container.
		constant = container.create_constant(ifr_id, name, version,
						     enum, any)

	        # Add the definition to our list of enumeration members to
		# be cleaned up at the end.
		self.__enum_members.append(constant)

	    # Otherwise, a definition with this name already exists.
	    else:
		self.yyerror("Redefinition of name '%s'" % name)
		raise bison.YYERROR

	# Update the enumeration definition.
	enum._set_members(members)

	return enum

    def native_type_dcl(self, identifier):
	""" type_dcl: NATIVE simple_declarator """
	
	self.yywarning('No native types specified in the language mapping')
	return

    def sequence_type(self, element_type_def, bound=0):
	""" sequence_type: SEQUENCE
                          '<' simple_type_spec ',' positive_int_const '>'
                          |
                          SEQUENCE '<' simple_type_spec '>'
        """
        return self.__repository.create_sequence(bound, element_type_def)

    def string_type(self, bound=0):
	""" string_type: STRING '<' positive_int_const '>'
                         |
                         STRING
        """
	if bound == 0:
	    definition = self.__repository.get_primitive(CORBA.pk_string)

	else:
	    definition = self.__repository.create_string(bound)

	return definition

    def wide_string_type(self, bound=0):
	""" wide_string_type: WSTRING '<' positive_int_const '>'
                              |
                              WSTRING
        """
	if bound == 0:
	    definition = self.__repository.get_primitive(CORBA.pk_wstring)

	else:
	    definition = self.__repository.create_wstring(bound)

	return definition

    def array_declarator(self, name, dimensions):
	""" array_declarator: IDENTIFIER fixed_array_size_PLUS """

	return Declarator.ArrayDeclarator(name, dimensions)

    def fixed_array_size(self, dimension):
	""" fixed_array_size: '[' positive_int_const ']' """

	return dimension

    def attr_dcl(self, mode, type_def, declarators):
	""" attr_dcl: readonly_OPT ATTRIBUTE param_type_spec
                      simple_declarators

	"""
	# Get the container and prefix from the stacks.
	container = self.__container_stack.get()
	prefix = self.__prefix_stack.get()

	for declarator in declarators:
	    # Get the name of the declarator.
	    name = declarator.name()

	    # Does the container already contain a definition with this name?
	    attribute = container.lookup(name)

	    # If there is *no* definition with this name then we can safely
	    # create a new attribute.
	    if attribute is None:
		# Currently the version is always "1.0"!
		version = "1.0"

		# Generate an interface repository id.
		ifr_id = self.__create_ifr_id(prefix, name, version)

		# Create a new attribute definition in the container.
		attribute = container.create_attribute(ifr_id, name, version,
						       type_def, mode)

	    # Otherwise, a definition with this name already exists.
	    else:
		self.yyerror("Redefinition of name '%s'" % name)
		raise bison.YYERROR

	return

    def readonly_OPT_normal(self):
	""" readonly_OPT: /* empty */ """

	return CORBA.ATTR_NORMAL

    def readonly_OPT_readonly(self):
	""" readonly_OPT: READONLY """

	return CORBA.ATTR_READONLY

    def except_dcl_header(self, name):
	""" EXCEPTION IDENTIFIER """
	
	# Get the container and prefix from the stacks.
	container = self.__container_stack.get()
	prefix = self.__prefix_stack.get()

	# Does the container already contain a definition with this name?
	exception = container.lookup(name)

	# If there is *no* definition with this name then we can safely create
	# a new exception.
	if exception is None:
	    # Currently the version is always "1.0"!
	    version = "1.0"

	    # Generate an interface repository id.
	    ifr_id = self.__create_ifr_id(prefix, name, version)

	    # Create a new exception definition in the container.
	    exception = container.create_exception(ifr_id, name, version, [])

	    # If the exception is defined at the global scope of the top-level
	    # IDL file then add it to our list of top-level definitions.
	    if self.__is_a_top_level_definition(exception):
		self.__top_level_definitions.append(exception)

	# Otherwise, a definition with this name already exists.
	else:
	    self.yyerror("Redefinition of name '%s'" % name)
	    raise bison.YYERROR

	# Push the exception onto the container stack.
	self.__container_stack.push(exception)

	# Push a new prefix onto the prefix stack.
	self.__prefix_stack.push(self.__create_prefix(prefix, name))    

	return exception

    def except_dcl_body(self, exception, members):
	""" except_dcl: '{' member_STAR '}' """
	
	# The same as for structure definitions!
	self.struct_type_body(exception, members)
	return

    def op_dcl_header(self, mode, result_type_def, name):
	""" op_dcl: op_attribute_OPT op_type_spec IDENTIFER """

	# Get the container and prefix from the stacks.
	container = self.__container_stack.get()
	prefix = self.__prefix_stack.get()

	# Does the container already contain a definition with this name?
	operation = container.lookup(name)

	# If there is *no* definition with this name then we can safely create
	# a new operation.
	if operation is None:
	    # Currently the version is always "1.0"!
	    version = "1.0"

	    # Generate an interface repository id.
	    ifr_id = self.__create_ifr_id(prefix, name, version)

	    # Create a new operation definition in the container.
	    operation = container.create_operation(ifr_id, name, version,
						   result_type_def,
						   mode,
						   [], # Parameters.
						   [], # Exceptions.
						   []) # Contexts.

	# Otherwise, a definition with this name already exists.
	else:
	    self.yyerror("Redefinition of name '%s'" % name)
	    raise bison.YYERROR

	return operation

    def op_dcl_body(self, operation, params, exceptions, contexts):
	""" op_dcl: op_attribute_OPT op_type_spec IDENTIFER """

	# Make sure that the parameter names are unique.
	dict = {}
	for param in params:
	    if dict.has_key(param.name):
		self.yyerror("Duplicate parameter name '%s'" % param.name)
		raise bison.YYERROR

	    dict[param.name] = None

	# Update the operation definition.
	operation._set_params(params)
	operation._set_exceptions(exceptions)
	operation._set_contexts(contexts)

	return

    def op_attribute_OPT_empty(self):
	""" op_attribute_OPT: /* empty */ """

	return CORBA.OP_NORMAL

    def op_attribute_OPT_oneway(self):
	""" op_attribute_OPT: ONEWAY """

	return CORBA.OP_ONEWAY

    def op_type_spec_void(self):
	""" op_type_spec: VOID """

	return self.__repository.get_primitive(CORBA.pk_void)

    def parameter_dcls_empty(self):
	""" parameter_dcls: '(' ')' """

	return []

    def param_dcl(self, mode, type_def, declarator):
	""" param_dcl: param_attribute param_type_spec simple_declarator """

	return CORBA.ParameterDescription(declarator.name(),
					  type_def._get_type(),
					  type_def,
					  mode)
    def param_attribute_in(self):
	""" param_attribute: IN """

	return CORBA.PARAM_IN

    def param_attribute_out(self):
	""" param_attribute: OUT """

	return CORBA.PARAM_OUT

    def param_attribute_inout(self):
	""" param_attribute: INOUT """

	return CORBA.PARAM_INOUT

    def raises_expr_OPT_empty(self):
	""" raises_expr_OPT: /* empty */ """

	return []

    def raises_expr(self, scoped_names):
	""" raises_expr: RAISES '(' scoped_name_CSV_PLUS ')' """

	# Get the container and prefix from the stacks.
	container = self.__container_stack.get()

	# Make sure that all of the names refer to exceptions!
	exceptions = []
	for scoped_name in scoped_names:
	    # Lookup the definition referenced by the scoped name.
	    definition = self.__find_scoped_name(container, scoped_name)
	    if definition is None:
		raise bison.YYERROR

	    # Make sure that it is an exception definition.
	    if definition._get_def_kind() != CORBA.dk_Exception:
		self.yyerror("'%s' is not an exception" % scoped_name)
		raise bison.YYERROR

	    exceptions.append(definition)

	return exceptions

    def context_expr_OPT_empty(self):
	""" context_expr_OPT: /* empty */ """

	return []

    def context_expr(self, string_literals):
	""" context_expr: CONTEXT '(' string_literal_CSV_PLUS ')' """

	# fixme: Fnorb does not support contexts!
	for string_literal in string_literals:
	    self.yywarning("%s context ignored" % string_literal)

	return []

    def fixed_pt_type(self, digits, scale):
	""" fixed_pt_type: FIXED '<' positive_int_const ',' integer_literal '>'

        The fixed point type is not yet implemented.

        """
	self.yywarning("'fixed' not supported by Fnorb runtime")

	# 'digits' is a positive integer constant and is returned as a Python
	# long, and ;scale' is an integer literal which is returned as an
	# 'Any'.
	return self.__repository.create_fixed(digits, scale.value())

    def fixed_pt_const_type(self):
	""" fixed_pt_const_type: FIXED """

	self.yywarning("'fixed' constants not supported by Fnorb runtime")
	return self.__repository.create_fixed(0, 0)
    #
    # '#pragma' directives.
    #
    def pragma(self, yytext):
	""" PRAGMA """

	# Split the text of the directive on spaces.
	components = string.split(yytext)

	# Ignore any malformed directives.
	if len(components) == 3:
	    if components[1] == 'prefix':
		try:
		    # The prefix must be a quoted string.
		    pragma_prefix = eval(components[2])
		    if type(pragma_prefix) != types.StringType:
			raise TypeError

		    # Get the current prefix from the stack.
		    prefix = self.__prefix_stack.get()

		    # Update the 'pragma' portion of the prefix.
		    prefix.set_pragma(pragma_prefix)
			
		# Ignore any errors.
	        except:
		    self.yywarning("Malformed #pragma prefix")

	elif len(components) == 4:
	    if components[1] == 'ID':
		try:
		    # Get the name of the definition.
		    name = components[2]

		    # The id must be a quoted string.
		    ifr_id = eval(components[3])
		    if type(ifr_id) != types.StringType:
			raise TypeError

		    # Get the container from the stacks.
		    container = self.__container_stack.get()

		    # Lookup the definition referenced by the scoped name.
		    definition = self.__find_scoped_name(container, name)
		    if definition is None:
			raise bison.YYERROR

		    # Update the definition's interface repository id.
		    definition._set_id(ifr_id)
			
		# Ignore any errors.
	        except:
		    self.yywarning("Malformed #pragma ID")

	    elif components[1] == 'version':
		try:
		    # Get the name of the definition.
		    name = components[2]

		    # The version is used as a string, but must be in the
		    # format <major>.<minor>
		    version = components[3]
		    if type(eval(version)) != types.FloatType:
			raise TypeError
		    
		    # Get the container from the stacks.
		    container = self.__container_stack.get()

		    # Lookup the definition referenced by the scoped name.
		    definition = self.__find_scoped_name(container, name)
		    if definition is None:
			raise bison.YYERROR

		    # Update the definition's version.
		    definition._set_version(version)
			
		# Ignore any errors.
	        except:
		    self.yywarning("Malformed #pragma version")

	return

    def line_directive(self, yytext):
	""" Parse line/file pre-processor directives.
	
	This method is called from the lexer (see the file 'lexer.l').

	"""
	line = None
	file = None

	# Split the text on spaces.
	components = string.split(yytext)

	# If this a '# line' directive.
	if components[1] == 'line':
	    # e.g. '# line 1'
	    line = eval(components[2])
		
	    # e.g. '# line 1 "filename.idl"
	    if len(components) >= 4:
		file = eval(components[3])

	# Otherwise this is a pre-processor inserted line.
        else:
	    # e.g. '# 1'
	    line = eval(components[1])
		    
	    # e.g. '# 1 "filename.idl"', but not '# 1 "<directive>"'
            if len(components) >= 3 and components[2][:2] != '"<':
		file = eval(components[2])

	# Update the line number.
	if line is not None:
	    bison.yylineno(line)
		
	# Update the file.
	if file is not None:
	    # Get the prefix stack for this file.
	    try:
		self.__prefix_stack = self.__prefix_stacks[file]

	    # If there is no prefix stack for this file then create one!
	    except KeyError:
		# The prefix stack initially contains just an empty prefix.
		self.__prefix_stack = Stack.Stack()
		self.__prefix_stack.push(Prefix.Prefix())
		self.__prefix_stacks[file] = self.__prefix_stack

	    # Update the current filename.
	    self.__current_file = file
		
	return
    #
    # Generic actions.
    #
    def list_empty(self):
	""" Generic empty list. """

	return []

    def list_insert(self, item, list):
	""" Generic list insert. """

	if type(item) == types.ListType:
	    list = item + list

	else:
	    list.insert(0, item)

	return list

    def idl_type_scoped_name(self, scoped_name):
	""" Check that the scoped name references an IDL type definition. """

	# Get the container from the stack.
	container = self.__container_stack.get()

	# Lookup the definition referenced by the scoped name.
	definition = self.__find_scoped_name(container, scoped_name)
	if definition is None:
	    raise bison.YYERROR

	# If it is not an 'IDLType' definition then it is illegal.
	if not definition._is_a("IDL:omg.org/CORBA/IDLType:1.0"):
	    self.yyerror("'%s' is not a type name" % scoped_name)
	    raise bison.YYERROR

	return definition

    #########################################################################
    # Private interface.
    #########################################################################

    def __initialise(self):
	""" Initialise the various data structures used during parsing. """

	# The name of the IDL file currently being parsed.
	self.__current_file = self.__filename

	# A dictionary of prefix stacks - keyed on filename.
	self.__prefix_stacks = {} # {Filename: Stack<Prefix>}

	# The prefix stack initially contains just an empty prefix.
	self.__prefix_stack = Stack.Stack()
	self.__prefix_stack.push(Prefix.Prefix())
	self.__prefix_stacks[self.__filename] = self.__prefix_stack

	# A list of all definitions at the global scope of the top-level IDL
	# file.
	self.__top_level_definitions = []

	# A dictionary of all interfaces along with the names of their
	# operations and attributes.
	self.__interfaces = {} # {RepositoryId: [NamesofOpsAndAttributes]}

	# A dictionary of forward declared but undefined interfaces.
	self.__forward_interfaces = {} # {RepositoryId: (InterfaceDef, LineNo)}

	# A list of the temporary constant definitions created for members of 
	# enumerations.
	self.__enum_members = []

	return

    def __cleanup(self):
	""" Cleanup after parsing. """

	# Get rid of the temporary contant definitions created for members of
	# enumerations.
	for enum_member in self.__enum_members:
	    enum_member.destroy()

	return

    def __create_prefix(self, prefix, name):
	""" Create a new prefix. """

	new_prefix = "%s%s/" % (prefix.get_prefix(), name)
	return Prefix.Prefix(prefix.get_pragma(), new_prefix)

    def __create_ifr_id(self, prefix, name, version):
	""" Create an interface repository id. """

	return "IDL:%s%s:%s" % (str(prefix), name, version)

    def __find_scoped_name(self, container, scoped_name):
	""" Find the definition for the scoped name. """

	if scoped_name == "TypeCode":
	    definition = self.__repository.get_primitive(CORBA.pk_TypeCode)

	elif scoped_name == "Principal":
	    definition = self.__repository.get_primitive(CORBA.pk_Principal)

	else:
	    # If the container is an interface then we look first in
	    # the interface itself, and then recursively through its base
	    # interfaces.
	    if container._get_def_kind() == CORBA.dk_Interface:
		definition = self.__find_inherited(container, scoped_name)

	    # Otherwise, we start by searching the current container, and then
	    # its enclosing scopes.
	    else:
		definition = container.lookup(scoped_name)

	    # While the definition is not found.
	    while definition is None:
		# Is the container also 'contained'?
		if container._is_a("IDL:omg.org/CORBA/Contained:1.0"):
		    # Get the container's container ;^)
		    container = container._get_defined_in()

		    # Look in there!
		    definition = container.lookup(scoped_name)

		else:
		    self.yyerror("Undeclared scoped name '%s'" % scoped_name)
		    break

	return definition

    def __find_inherited(self, container, scoped_name):
	""" Find the definition for the scoped name. """

	# Lookup the name in the interface first.
	definition = container.lookup(scoped_name)
	if definition is None:
	    # Look through all base interfaces.
	    for base in container._get_base_interfaces():
		# Lookup the name in the base's base interfaces!
		definition = self.__find_inherited(base, scoped_name)
		if definition is not None:
		    break

	return definition

    def __create_array(self, type_def, dims):
	""" Create an array type definition. """

	# Single dimension
	if len(dims) == 1:
	    array_type_def = self.__repository.create_array(dims[0], type_def)

	# Multi-dimension
	else:
	    # Create the 'slice' type.
	    slice_type_def = self.__repository.create_array(dims[-1], type_def)

	    # Recursively create the array type.
	    array_type_def = self.__create_array(slice_type_def, dims[:-1])

	return array_type_def

    def __is_a_top_level_definition(self, definition):
	""" Is the defn at the global scope of the top-level IDL file? """

	if self.__current_file == self.__filename \
	   and definition._get_defined_in() == self.__repository:
	    result = 1
	
	else:
	    # Include any global scope definitions that are not modules!
	    if definition._get_defined_in() == self.__repository \
	       and definition._get_def_kind() != CORBA.dk_Module:
		result = 1

	    else:
		result = 0

	return result

    # Integer typecode kinds.
    __TK_INT = [CORBA.tk_short,
		CORBA.tk_long,
		CORBA.tk_longlong,
		CORBA.tk_ushort,
		CORBA.tk_ulong,
		CORBA.tk_ulonglong]

    # Floating point typecode kinds.
    __TK_FP = [CORBA.tk_float,
	       CORBA.tk_double,
	       CORBA.tk_longdouble]

    def __check_constant_type(self, type_def, any):
	""" Make sure that the value is of the specified type. """

	# Get the typecode 'kind's.
	type_def_kind = type_def._get_type().kind()
	any_kind = any.typecode().kind()

	if type_def_kind in IDLParser.__TK_INT \
	   and any_kind in IDLParser.__TK_INT:
	    result = 1

	elif type_def_kind in IDLParser.__TK_FP \
	     and any_kind in IDLParser.__TK_FP + IDLParser.__TK_INT:
	    result = 1

	elif type_def_kind == any_kind:
	    result = 1

	else:
	    result = 0

	return result

    def __check_limits(self, type_def, any):
	""" Make sure that the value is within the limits of the type. """

	# Get the typecode 'kind'.
	type_def_kind = type_def._get_type().kind()

	# Get the value out of the any.
	value = any.value()

	if type_def_kind == CORBA.tk_short:
	    if value < Limits.MIN_SHORT or value > Limits.MAX_SHORT:
		result = 0
		
	    else:
		result = 1
		value = int(value)

	elif type_def_kind == CORBA.tk_ushort:
	    if value < Limits.MIN_USHORT or value > Limits.MAX_USHORT:
		result = 0

	    else:
		result = 1
		value = int(value)

	elif type_def_kind == CORBA.tk_long:
	    if value < Limits.MIN_LONG or value > Limits.MAX_LONG:
		result = 0

	    else:
		result = 1
		value = int(value)

	elif type_def_kind == CORBA.tk_ulong:
	    if value < Limits.MIN_ULONG or value > Limits.MAX_ULONG:
		result = 0

	    else:
		result = 1

	elif type_def_kind == CORBA.tk_longlong:
	    if value < Limits.MIN_LONGLONG or value > Limits.MAX_LONGLONG:
		result = 0

	    else:
		result = 1

	elif type_def_kind == CORBA.tk_ulonglong:
	    if value < Limits.MIN_ULONGLONG or value > Limits.MAX_ULONGLONG:
		result = 0

	    else:
		result = 1

        elif type_def_kind in IDLParser.__TK_FP:
            # Coerce integer constants to floating point values
            # FIXME: IDL expressions are meant to have the type
            # of the constant they are assigned to right from the
            # beginning
            value = float(value)
            result = 1
	    
	# fixme: This should not be here!!!
	elif type_def_kind == CORBA.tk_fixed:
	    any_typecode = any.typecode()
	    type_def._set_digits(any_typecode.fixed_digits())
	    type_def._set_scale(any_typecode.fixed_scale())

	    result = 1

	else:
	    result = 1

	return (result, value)

    def __check_label_type(self, type_def, any):
	""" Make sure that the value is of the specified type. """

	# Get the typecode 'kind's.
	type_def_kind = type_def._get_type().kind()
	any_kind = any.typecode().kind()

	if type_def_kind in IDLParser.__TK_INT \
	   and any_kind in IDLParser.__TK_INT:
	    result = 1

	elif type_def_kind == any_kind:
	    # If the union is switched on an enumeration then make sure that
	    # the value is a valid element of the same enumeration!
	    if type_def_kind == CORBA.tk_enum:
		if type_def._get_type().id() == any.typecode().id():
		    result = 1

		else:
		    result = 0
	    else:
		result = 1

	else:
	    result = 0

	return result

    def __check_forward_interfaces(self):
	""" Make sure that all forward declared interfaces were defined. """

	if len(self.__forward_interfaces) > 0:
	    for (interface, yylineno) in self.__forward_interfaces.values():
		# Set the line number for error reporting.
		bison.yylineno(yylineno)

		# Report the error.
		self.yyerror("Interface '%s' declared but not defined" % \
			     interface._get_name())
		
	    raise bison.YYERROR

	return

    def __get_bases(self, interface):
        if interface._get_def_kind() == CORBA.dk_Interface:
            return interface._get_base_interfaces()
        elif interface._get_def_kind() == CORBA.dk_Value:
            base = interface._get_base_value()
            abases = interface._get_abstract_base_values()
            if base:
                return [base] + abases
            else:
                return abases

        raise NotImplementedError

    def __get_inherited_operations(self, interface, visited):
	""" Get all operations and attributes inherited by an interface. """

	names = []
	for base in self.__get_bases(interface):
	    # Get the interface repository id of the interface.
	    ifr_id = base._get_id()
	    
	    # Only include the operations and attributes for the interface
	    # once!
	    if ifr_id not in visited:
		# Add the interface's operations and attributes to the list.
		names = names + self.__interfaces[ifr_id]

		# Add the interface repository id of the interface to the list
		# of those already visited.
		visited.append(ifr_id)

		# Do it recursively!
		names = names + self.__get_inherited_operations(base, visited)

	# Check for duplicates!
	tmp = {}
	for name in names:
	    if tmp.has_key(name):
		self.yyerror("Overloaded operation/attribute '%s'" % name)
		raise bison.YYERROR

	    else:
		tmp[name] = None

	return names

    def __unwind_typedef_chain(self, definition):
	""" Unwind any 'typedef' chain! """

	# fixme: Which will it be? dk_Typedef or dk_Alias?!?!?!?!?! My bet is
	# on dk_Alias!
	while definition._get_def_kind() == CORBA.dk_Typedef \
	      or definition._get_def_kind() == CORBA.dk_Alias:
	    definition = definition._get_original_type_def()

	return definition

    def __is_recursive_member(self, type_def):
	""" Determine whether or not this is a recursive member. """
 
	if type_def._get_def_kind() == CORBA.dk_Sequence:
	    element_type_def = type_def._get_element_type_def()
	    if element_type_def._get_def_kind() == CORBA.dk_Struct or \
	       element_type_def._get_def_kind() == CORBA.dk_Union:
		if element_type_def._get_version() == "wip":
		    return 1
		    
	return 0

    def __get_recursion_depth(self, type_def, ifr_id, depth=1):
	""" Find the depth (or 'offset') of a recursive type. """

	if type_def._get_id() == ifr_id:
	    return depth

	else:
	    return self.__get_recursion_depth(type_def._get_defined_in(),
					      ifr_id,
					      depth + 1)
	return

#############################################################################
