/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

/*
 * parser.yy - BISON grammar for IDLC 1.0
 */

%{
#include <string.h>

#ifndef _IDLC_IDLC_HXX_
#include <idlc/idlc.hxx>
#endif
#ifndef _IDLC_ERRORHANDLER_HXX_
#include <idlc/errorhandler.hxx>
#endif
#ifndef _IDLC_FEHELPER_HXX_
#include <idlc/fehelper.hxx>
#endif
#ifndef _IDLC_EXPRESSION_HXX_
#include <idlc/astexpression.hxx>
#endif
#ifndef _IDLC_ASTCONSTANTS_HXX_
#include <idlc/astconstants.hxx>
#endif
#ifndef _IDLC_ASTCONSTANT_HXX_
#include <idlc/astconstant.hxx>
#endif
#ifndef _IDLC_ASTARRAY_HXX_
#include <idlc/astarray.hxx>
#endif
#ifndef _IDLC_ASTBASETYPE_HXX_
#include <idlc/astbasetype.hxx>
#endif
#ifndef _IDLC_ASTTYPEDEF_HXX_
#include <idlc/asttypedef.hxx>
#endif
#ifndef _IDLC_ASTEXCEPTION_HXX_
#include <idlc/astexception.hxx>
#endif
#ifndef _IDLC_ASTMEMBER_HXX_
#include <idlc/astmember.hxx>
#endif
#ifndef _IDLC_ASTENUM_HXX_
#include <idlc/astenum.hxx>
#endif
#ifndef _IDLC_ASTSEQUENCE_HXX_
#include <idlc/astsequence.hxx>
#endif
#ifndef _IDLC_ASTATTRIBUTE_HXX_
#include <idlc/astattribute.hxx>
#endif
#ifndef _IDLC_ASTOPERATION_HXX_
#include <idlc/astoperation.hxx>
#endif
#ifndef _IDLC_ASTPARAMETER_HXX_
#include <idlc/astparameter.hxx>
#endif
#ifndef _IDLC_ASTINTERFACEMEMBER_HXX_
#include <idlc/astinterfacemember.hxx>
#endif
#ifndef _IDLC_ASTSERVICEMEMBER_HXX_
#include <idlc/astservicemember.hxx>
#endif
#ifndef _IDLC_ASTOBSERVES_HXX_
#include <idlc/astobserves.hxx>
#endif
#ifndef _IDLC_ASTNEEDS_HXX_
#include <idlc/astneeds.hxx>
#endif
#ifndef _IDLC_ASTUNION_HXX_
#include <idlc/astunion.hxx>
#endif
#include "idlc/aststructinstance.hxx"

#include "attributeexceptions.hxx"

#include "rtl/strbuf.hxx"

#include <algorithm>
#include <vector>
    
using namespace ::rtl;

#define YYDEBUG 1
#define YYERROR_VERBOSE 1

extern int yylex(void);
void yyerror(char const *);

void checkIdentifier(::rtl::OString* id)
{
    static short check = 0;
    if (check == 0) {
        if (idlc()->getOptions()->isValid("-cid"))
            check = 1;
        else
            check = 2;
    }

    if ( id->indexOf('_') >= 0 )
        if ( (id->pData->buffer[0] >= 97 && id->pData->buffer[0] <= 122)
             || id->pData->buffer[0] == '_') {
            if (check == 1) {
                ::rtl::OStringBuffer msg(25 + id->getLength());
                msg.append("mismatched identifier '");
                msg.append(*id);
                msg.append("'");
                idlc()->error()->syntaxError(idlc()->getParseState(),
                                         idlc()->getLineNumber(),
                                         msg.getStr());
            }
            else
                idlc()->error()->warning0(WIDL_WRONG_NAMING_CONV, id->getStr());
        }
}

void reportDoubleMemberDeclarations(
    AstInterface::DoubleMemberDeclarations const & doubleMembers)
{
    for (AstInterface::DoubleMemberDeclarations::const_iterator i(
             doubleMembers.begin());
         i != doubleMembers.end(); ++i)
    {
        idlc()->error()->error2(EIDL_DOUBLE_MEMBER, i->first, i->second);
    }
}

void addInheritedInterface(
    AstInterface * ifc, rtl::OString const & name, bool optional,
    rtl::OUString const & documentation)
{
    AstDeclaration * decl = ifc->lookupByName(name);
    AstDeclaration const * resolved = resolveTypedefs(decl);
    if (resolved != 0 && resolved->getNodeType() == NT_interface) {
        if (idlc()->error()->checkPublished(decl)) {
            if (!static_cast< AstInterface const * >(resolved)->isDefined()) {
                idlc()->error()->inheritanceError(
                    NT_interface, &ifc->getScopedName(), decl);
            } else {
                AstInterface::DoubleDeclarations doubleDecls(
                    ifc->checkInheritedInterfaceClashes(
                        static_cast< AstInterface const * >(resolved),
                        optional));
                if (doubleDecls.interfaces.empty()
                    && doubleDecls.members.empty())
                {
                    ifc->addInheritedInterface(
                        static_cast< AstType * >(decl), optional,
                        documentation);
                } else {
                    for (AstInterface::DoubleInterfaceDeclarations::iterator i(
                             doubleDecls.interfaces.begin());
                         i != doubleDecls.interfaces.end(); ++i)
                    {
                        idlc()->error()->error1(
                            EIDL_DOUBLE_INHERITANCE, *i);
                    }
                    reportDoubleMemberDeclarations(doubleDecls.members);
                }
            }
        }
    } else {
        idlc()->error()->lookupError(
            EIDL_INTERFACEMEMBER_LOOKUP, name, scopeAsDecl(ifc));
    }
}

AstDeclaration const * createNamedType(
    rtl::OString const * scopedName, DeclList const * typeArgs)
{
    AstDeclaration * decl = idlc()->scopes()->topNonNull()->lookupByName(
        *scopedName);
    AstDeclaration const * resolved = resolveTypedefs(decl);
    if (decl == 0) {
        idlc()->error()->lookupError(*scopedName);
    } else if (!idlc()->error()->checkPublished(decl)) {
        decl = 0;
    } else if (resolved->getNodeType() == NT_struct) {
        if (static_cast< AstStruct const * >(resolved)->getTypeParameterCount()
            != (typeArgs == 0 ? 0 : typeArgs->size()))
        {
            idlc()->error()->error0(EIDL_WRONG_NUMBER_OF_TYPE_ARGUMENTS);
            decl = 0;
        } else if (typeArgs != 0) {
            AstScope * global = idlc()->scopes()->bottom();
            AstDeclaration * inst = new AstStructInstance(
                static_cast< AstType * >(decl), typeArgs, global);
            decl = global->addDeclaration(inst);
            if (decl != inst) {
                delete inst;
            }
        }
    } else if (decl->isType()) {
        if (typeArgs != 0) {
            idlc()->error()->error0(EIDL_WRONG_NUMBER_OF_TYPE_ARGUMENTS);
            decl = 0;
        }
    } else {
        idlc()->error()->noTypeError(decl);
        decl = 0;
    }
    delete scopedName;
    delete typeArgs;
    return decl;
}

bool includes(AstDeclaration const * type1, AstDeclaration const * type2) {
    OSL_ASSERT(type2 != 0);
    if (type1 != 0) {
        if (type1->getNodeType() == NT_instantiated_struct) {
            AstStructInstance const * inst
                = static_cast< AstStructInstance const * >(type1);
            if (inst->getTypeTemplate() == type2) {
                return true;
            }
            for (DeclList::const_iterator i(inst->getTypeArgumentsBegin());
                 i != inst->getTypeArgumentsEnd(); ++i)
            {
                if (includes(*i, type2)) {
                    return true;
                }
            }
        } else if (type1 == type2) {
            return true;
        }
    }
    return false;
}

// Suppress any warnings from generated code:
#if defined __GNUC__
#pragma GCC system_header
#elif defined __SUNPRO_CC
#pragma disable_warn
#elif defined _MSC_VER
#pragma warning(push, 1)
#pragma warning(disable: 4273 4701 4706)
#endif
%}
/*
 * Declare the type of values in the grammar
 */
%union {
	ExprType				etval;     /* Expression type */
	AstDeclaration*		dclval;    /* Declaration */
    AstDeclaration const * cdclval;
    DeclList * dclsval;
	AstExpression*		exval;		/* expression value */
	ExprList*				exlval;	/* expression list value */
	FeDeclarator*			fdval;		/* declarator value */
	FeDeclList*			dlval;		/* declarator list value */
	FeInheritanceHeader*	ihval;		/* inheritance header value */
	::rtl::OString*		sval;		/* OString value */
    std::vector< rtl::OString > * svals;
	sal_Char* 			strval;	/* sal_Char* value */
	sal_Bool				bval;		/* sal_Boolean* value */
	sal_Int64				ival;		/* sal_Int64 value */
    sal_uInt64 uval; /* sal_uInt64 value */
	sal_uInt32			ulval;		/* sal_uInt32 value */
	double					dval;		/* double value */
	float					fval;		/* float value */
	StringList*			slval;		/* StringList value	*/
	LabelList*			llval;		/* LabelList value	*/
	AstUnionLabel*		lbval;		/* union label value */
	AstMember*			mval;		/* member value */
    AttributeExceptions::Part attexcpval;
    AttributeExceptions attexcval;
}

/*
 * Token types: These are returned by the lexer
 */

%token <sval>		IDL_IDENTIFIER
%token 			IDL_ATTRIBUTE
%token				IDL_BOUND
%token 			IDL_CASE
%token 			IDL_CONST
%token 			IDL_CONSTANTS
%token				IDL_CONSTRAINED
%token 			IDL_DEFAULT
%token 			IDL_ENUM
%token 			IDL_EXCEPTION
%token 			IDL_INTERFACE
%token 			IDL_MAYBEAMBIGUOUS
%token 			IDL_MAYBEDEFAULT
%token 			IDL_MAYBEVOID
%token 			IDL_MODULE
%token 			IDL_NEEDS
%token 			IDL_OBSERVES
%token 			IDL_OPTIONAL
%token 			IDL_PROPERTY
%token 			IDL_RAISES
%token 			IDL_READONLY
%token 			IDL_REMOVEABLE
%token 			IDL_SERVICE
%token 			IDL_SEQUENCE
%token 			IDL_SINGLETON
%token 			IDL_STRUCT
%token 			IDL_SWITCH
%token 			IDL_TYPEDEF
%token				IDL_TRANSIENT
%token 			IDL_UNION

%token 			IDL_ANY
%token 			IDL_CHAR
%token 			IDL_BOOLEAN
%token 			IDL_BYTE
%token 			IDL_DOUBLE
%token 			IDL_FLOAT
%token 			IDL_HYPER
%token 			IDL_LONG
%token 			IDL_SHORT
%token 			IDL_VOID
%token 			IDL_STRING
%token 			IDL_TYPE
%token 			IDL_UNSIGNED

%token 			IDL_TRUE
%token 			IDL_FALSE

%token 			IDL_IN
%token 			IDL_OUT
%token 			IDL_INOUT
%token 			IDL_ONEWAY

%token IDL_GET
%token IDL_SET

%token IDL_PUBLISHED

%token IDL_ELLIPSIS

%token <strval>	IDL_LEFTSHIFT
%token <strval>	IDL_RIGHTSHIFT
%token <strval> 	IDL_SCOPESEPARATOR

%token <ival>		IDL_INTEGER_LITERAL
%token <uval> IDL_INTEGER_ULITERAL
%token <dval>		IDL_FLOATING_PT_LITERAL

/*
 * These are production names:
 */
%type <dclval>	type_dcl const_dcl
%type <dclval>	array_declarator
%type <dclval>  exception_name
%type <cdclval> array_type constructed_type_spec enum_type op_type_spec
%type <cdclval> sequence_type_spec simple_type_spec struct_type switch_type_spec
%type <cdclval> template_type_spec type_spec union_type
%type <cdclval> fundamental_type type_arg type_or_parameter
%type <dclsval> opt_raises raises exception_list
%type <attexcpval> opt_attribute_get_raises attribute_get_raises
%type <attexcpval> opt_attribute_set_raises attribute_set_raises
%type <dclsval> opt_type_args type_args

%type <sval>    identifier
%type <sval>	interface_decl 
%type <sval>	scoped_name inheritance_spec
%type <slval>  	scoped_names at_least_one_scoped_name

%type <etval>	const_type integer_type char_type boolean_type
%type <etval>	floating_pt_type any_type signed_int string_type
%type <etval>	unsigned_int base_type_spec byte_type type_type 

%type <exval>	expression const_expr or_expr xor_expr and_expr
%type <exval>	add_expr mult_expr unary_expr primary_expr shift_expr
%type <exval>	literal positive_int_expr array_dim

%type <exlval> 	at_least_one_array_dim array_dims

%type <fdval> 	declarator simple_declarator complex_declarator
%type <dlval> 	declarators at_least_one_declarator

%type <ihval>	exception_header structure_header interfaceheader

%type <ulval> 	flag_header opt_attrflags opt_attrflag operation_head
%type <ulval>	direction service_interface_header service_service_header

%type <llval>	case_labels at_least_one_case_label
%type <lbval>	case_label
%type <mval>	element_spec

%type <bval>    optional_inherited_interface opt_rest opt_service_body

%type <attexcval> opt_attribute_block attribute_block_rest opt_attribute_raises

%type <svals> opt_type_params type_params

%%
/*
 * Grammar start here
 */
start : definitions;

definitions : 
	definition definitions
	| /* EMPTY */
	;

definition : 
    opt_published publishable_definition
	| module_dcl
	{
		idlc()->setParseState(PS_ModuleDeclSeen);
	}
	';'
	{
		idlc()->setParseState(PS_NoState);
	}
	| error ';' 
	{
		yyerror("definitions");
		yyerrok;
	}
	;

opt_published:
    IDL_PUBLISHED { idlc()->setPublished(true); }
    | /* empty */ { idlc()->setPublished(false); }
    ;

publishable_definition:
	type_dcl 
	{
		idlc()->setParseState(PS_TypeDeclSeen);
	}
	';'
	{
		idlc()->setParseState(PS_NoState);
	}
	| const_dcl
	{
		idlc()->setParseState(PS_ConstantDeclSeen);
	}
	';'
	{
		idlc()->setParseState(PS_NoState);
	}
	| exception_dcl
	{
		idlc()->setParseState(PS_ExceptionDeclSeen);
	}
	';'
	{
		idlc()->setParseState(PS_NoState);
	}
	| interface
	{
		idlc()->setParseState(PS_InterfaceDeclSeen);
	}
	';'
	{
		idlc()->setParseState(PS_NoState);
	}
	| service_dcl
	{
		idlc()->setParseState(PS_ServiceDeclSeen);
	}
	';'
	{
		idlc()->setParseState(PS_NoState);
	}
	| singleton_dcl
	{
		idlc()->setParseState(PS_SingletonDeclSeen);
	}
	';'
	{
		idlc()->setParseState(PS_NoState);
	}
	| constants_dcl
	{
		idlc()->setParseState(PS_ConstantsDeclSeen);
	}
	';'
	{
		idlc()->setParseState(PS_NoState);
	}
    ;

module_dcl :
	IDL_MODULE
	{
		idlc()->setParseState(PS_ModuleSeen);
        idlc()->setPublished(false);
	}
	identifier
	{
        idlc()->setParseState(PS_ModuleIDSeen);
        checkIdentifier($3);

        AstScope* 		pScope = idlc()->scopes()->topNonNull();
        AstModule* 		pModule = NULL;
        AstDeclaration*	pExists = NULL;

        if ( pScope )
        {
        	pModule = new AstModule(*$3, pScope);
			if( (pExists = pScope->lookupForAdd(pModule)) )
			{
				pExists->setInMainfile(idlc()->isInMainFile());
				pExists->setFileName(pModule->getFileName());
                if (pExists->isPredefined())
                {
                    pExists->setPredefined(false);
                    if (pExists->getDocumentation().getLength() == 0 &&
                        pModule->getDocumentation().getLength() > 0)
                    {
                        pExists->setDocumentation(pModule->getDocumentation());
                    }
                }
				delete(pModule);
				pModule = (AstModule*)pExists;
			} else
			{
				pScope->addDeclaration(pModule);
			}
			idlc()->scopes()->push(pModule);
        }
        delete $3;
    }
    '{'
    {
        idlc()->setParseState(PS_ModuleSqSeen);
    }
	definitions
	{
		idlc()->setParseState(PS_ModuleBodySeen);
	}
	'}'
	{
		idlc()->setParseState(PS_ModuleQsSeen);
		/*
		 * Finished with this module - pop it from the scope stack
		 */
		idlc()->scopes()->pop();
	}
	;

interface :
	interface_dcl
	| forward_dcl
	; 

interface_decl :
	IDL_INTERFACE
	{
		idlc()->setParseState(PS_InterfaceSeen);
	}
	identifier
	{
		idlc()->setParseState(PS_InterfaceIDSeen);
       checkIdentifier($3);
		$$ = $3;
	}
	;

forward_dcl :
	interface_decl
	{
		idlc()->setParseState(PS_ForwardDeclSeen);

		AstScope* 		pScope = idlc()->scopes()->topNonNull();
		AstInterface*	pForward = NULL;
		AstDeclaration*	pDecl = NULL;

        /*
		 * Make a new forward interface node and add it to its enclosing scope
		 */
		if ( pScope && $1 ) 
		{
			pForward = new AstInterface(*$1, NULL, pScope);
			
			if ( pDecl = pScope->lookupByName(pForward->getScopedName()) ) 
			{
				if ( (pDecl != pForward) && 
					 (pDecl->getNodeType() == NT_interface) )
				{
					delete pForward;
				} else
				{
					idlc()->error()->error2(EIDL_REDEF_SCOPE, scopeAsDecl(pScope), pDecl);
				}
			} else
			{
				/*
				 * Add the interface to its definition scope
				 */
				pScope->addDeclaration(pForward);
			}
		}
		delete $1;
	}
	;

interface_dcl :
	interfaceheader
	{
		idlc()->setParseState(PS_InterfaceHeadSeen);

		AstScope* 		pScope = idlc()->scopes()->topNonNull();
		AstInterface*	pInterface = NULL;
		AstInterface*	pForward = NULL;
		AstDeclaration*	pDecl = NULL;

        /*
		 * Make a new interface node and add it to its enclosing scope
		 */
		if ( pScope && $1 ) 
		{
			pInterface = new AstInterface(
                *$1->getName(),
                static_cast< AstInterface * >($1->getInherits()), pScope);
			if ( pInterface &&
				(pDecl = pScope->lookupByName(pInterface->getScopedName())) ) 
			{
				/*
				 * See if we're defining a forward declared interface.
				 */
				if (pDecl->getNodeType() == NT_interface) 
				{
					pForward = (AstInterface*)pDecl;
					if ( !pForward->isDefined() ) 
					{
						/*
						 * Check if redefining in same scope
						 */
						if ( pForward->getScope() != pScope ) 
						{
							if ( pForward->getScopedName() != pInterface->getScopedName() )
							{
								idlc()->error()->error3(EIDL_SCOPE_CONFLICT,
										 pInterface, pForward, scopeAsDecl(pScope));
							}
						}
                        else if ( !pInterface->isPublished()
                                  && pForward->isPublished() )
                        {
                            idlc()->error()->error0(EIDL_PUBLISHED_FORWARD);
                        }
						/*
						 * All OK, set full definition
						 */
						else 
						{
                            pForward->forwardDefined(*pInterface);
							delete pInterface;
							pInterface = pForward;
						}
					} else {
                        // special handling for XInterface because it is predefined
                        if ( pForward->isPredefined() &&
                             pForward->getScopedName() == "com::sun::star::uno::XInterface")
                        {
                            /* replace the predefined XInterface */
                            *pForward = *pInterface;
                            delete pInterface;
                            pInterface = pForward;
                        }
                        
                    }
				}
			} else
			{
				/*
				 * Add the interface to its definition scope
				 */
				pScope->addDeclaration(pInterface);
			}
		}
		/*
		 * Push it on the scope stack
		 */
		idlc()->scopes()->push(pInterface);
		delete($1);
	}
	'{'
	{
		idlc()->setParseState(PS_InterfaceSqSeen);
	}
	exports
	{
        AstInterface * ifc = static_cast< AstInterface * >(
            idlc()->scopes()->topNonNull());
        if (!ifc->hasMandatoryInheritedInterfaces()
            && ifc->getScopedName() != "com::sun::star::uno::XInterface")
        {
            addInheritedInterface(
                ifc, rtl::OString("::com::sun::star::uno::XInterface"), false,
                rtl::OUString());
        }
        ifc->setDefined();
		idlc()->setParseState(PS_InterfaceBodySeen);
	}
	'}'
	{
		idlc()->setParseState(PS_InterfaceQsSeen);
		/*
		 * Done with this interface - pop it off the scopes stack
		 */
		idlc()->scopes()->pop();
	}
	| error '}' 
	{
		yyerror("interface definition");
		yyerrok;
	}
	;

interfaceheader :
	interface_decl inheritance_spec
	{
		idlc()->setParseState(PS_InheritSpecSeen);

		$$ = new FeInheritanceHeader(NT_interface, $1, $2, 0);
		delete $2;
	}
	;   

inheritance_spec :
	':'
	{
		idlc()->setParseState(PS_InheritColonSeen);
	}
	scoped_name
	{
        $$ = $3;
	}
	| /* EMPTY */
	{
		$$ = NULL;
	}
	;

exports :
	exports export
	| /* EMPTY */
	;

export :
    attribute
	{
		idlc()->setParseState(PS_AttributeDeclSeen);
	}
	';'
	{
		idlc()->setParseState(PS_NoState);
	}
	| operation
	{
		idlc()->setParseState(PS_OperationDeclSeen);
	}
	';'
	{
		idlc()->setParseState(PS_NoState);
	}
    | interface_inheritance_decl
    {
		idlc()->setParseState(PS_InterfaceInheritanceDeclSeen);
    }
    ';'
	{
		idlc()->setParseState(PS_NoState);
	}
    ;

attribute :
	flag_header
	simple_type_spec
	{
		idlc()->setParseState(PS_AttrTypeSeen);
	}
    simple_declarator
	{
		idlc()->setParseState(PS_AttrCompleted);
        if (($1 & ~(AF_BOUND | AF_READONLY)) != AF_ATTRIBUTE) {
            idlc()->error()->flagError(EIDL_BAD_ATTRIBUTE_FLAGS, $1);
        }
        AstInterface * scope = static_cast< AstInterface * >(
            idlc()->scopes()->top());
        AstAttribute * attr = new AstAttribute(
            $1, $4->compose($2), $4->getName(), scope);
        delete $4;
        AstInterface::DoubleMemberDeclarations doubleMembers(
            scope->checkMemberClashes(attr));
        if (doubleMembers.empty()) {
            scope->addMember(attr);
        } else {
            reportDoubleMemberDeclarations(doubleMembers);
        }
        idlc()->scopes()->push(attr);
    }
    opt_attribute_block
    {
        static_cast< AstAttribute * >(idlc()->scopes()->top())->setExceptions(
            $6.get.documentation, $6.get.exceptions, $6.set.documentation,
            $6.set.exceptions);
        delete $6.get.documentation;
        delete $6.get.exceptions;
        delete $6.set.documentation;
        delete $6.set.exceptions;
        idlc()->scopes()->pop();
    }
	;

flag_header :
	'[' opt_attrflags ']'
	{
		idlc()->setParseState(PS_FlagHeaderSeen);
		$$ = $2;
	}
	;

opt_attrflags :
	opt_attrflags ',' opt_attrflag 
	{
		if ( ($1 & $3) == $3 )
 			idlc()->error()->flagError(EIDL_DEFINED_ATTRIBUTEFLAG, $3);
 
 		$$ = $1 | $3;
	}
	| opt_attrflag
	{
		$$ = $1;
	}
	;

opt_attrflag :
	IDL_ATTRIBUTE
	{
		idlc()->setParseState(PS_AttrSeen);
		$$ = AF_ATTRIBUTE;
	}
	| IDL_PROPERTY
	{
		idlc()->setParseState(PS_PropertySeen);
		$$ = AF_PROPERTY;
	}
	| IDL_READONLY
	{
		idlc()->setParseState(PS_ReadOnlySeen);
		$$ = AF_READONLY;
	}
	| IDL_OPTIONAL
	{
		idlc()->setParseState(PS_OptionalSeen);
		$$ = AF_OPTIONAL;
	}
	| IDL_MAYBEVOID
	{
		idlc()->setParseState(PS_MayBeVoidSeen);
		$$ = AF_MAYBEVOID;
	}
	| IDL_BOUND
	{
		idlc()->setParseState(PS_BoundSeen);
		$$ = AF_BOUND;
	}
	| IDL_CONSTRAINED
	{
		idlc()->setParseState(PS_ConstrainedSeen);
		$$ = AF_CONSTRAINED;
	}
	| IDL_TRANSIENT
	{
		idlc()->setParseState(PS_TransientSeen);
		$$ = AF_TRANSIENT;
	}
	| IDL_MAYBEAMBIGUOUS
	{
		idlc()->setParseState(PS_MayBeAmbigiousSeen);
		$$ = AF_MAYBEAMBIGUOUS;
	}
	| IDL_MAYBEDEFAULT
	{
		idlc()->setParseState(PS_MayBeDefaultSeen);
		$$ = AF_MAYBEDEFAULT;
	}
	| IDL_REMOVEABLE
	{
		idlc()->setParseState(PS_RemoveableSeen);
		$$ = AF_REMOVEABLE;
	}
	| error ']' 
	{
       yyerror("unknown property|attribute flag");
		yyerrok;
	}
	;

opt_attribute_block:
    '{' attribute_block_rest { $$ = $2; }
    | /* empty */
    {
        $$.get.documentation = 0;
        $$.get.exceptions = 0;
        $$.set.documentation = 0;
        $$.set.exceptions = 0;
    }
    ;

attribute_block_rest:
    opt_attribute_raises '}'
    | error '}'
    {
        yyerror("bad attribute raises block");
        yyerrok;
        $$.get.documentation = 0;
        $$.get.exceptions = 0;
        $$.set.documentation = 0;
        $$.set.exceptions = 0;
    }
    ;

opt_attribute_raises:
    attribute_get_raises
    opt_attribute_set_raises
    {
        $$.get = $1;
        $$.set = $2;
    }
    | attribute_set_raises
    opt_attribute_get_raises
    {
        $$.get = $2;
        $$.set = $1;
    }
    | /* empty */
    {
        $$.get.documentation = 0;
        $$.get.exceptions = 0;
        $$.set.documentation = 0;
        $$.set.exceptions = 0;
    }
    ;

opt_attribute_get_raises:
    attribute_get_raises
    | /* empty */ { $$.documentation = 0; $$.exceptions = 0; }
    ;

attribute_get_raises:
    IDL_GET raises ';'
    {
        $$.documentation = new rtl::OUString(
            rtl::OStringToOUString(
                idlc()->getDocumentation(), RTL_TEXTENCODING_UTF8));
        $$.exceptions = $2;
    }
    ;

opt_attribute_set_raises:
    attribute_set_raises
    | /* empty */ { $$.documentation = 0; $$.exceptions = 0; }
    ;

attribute_set_raises:
    IDL_SET
    {
        if (static_cast< AstAttribute * >(idlc()->scopes()->top())->
            isReadonly())
        {
            idlc()->error()->error0(EIDL_READONLY_ATTRIBUTE_SET_EXCEPTIONS);
        }
    }
    raises ';'
    {
        $$.documentation = new rtl::OUString(
            rtl::OStringToOUString(
                idlc()->getDocumentation(), RTL_TEXTENCODING_UTF8));
        $$.exceptions = $3;
    }
    ;

operation :
	operation_head
	op_type_spec
	{
		idlc()->setParseState(PS_OpTypeSeen);
	}
	identifier
	{
		idlc()->setParseState(PS_OpIDSeen);
       checkIdentifier($4);

		AstInterface * pScope = static_cast< AstInterface * >(
            idlc()->scopes()->top());
		AstOperation* 	pOp = NULL;

		/*
		 * Create a node representing an operation on an interface
		 * and add it to its enclosing scope
		 */
		if ( pScope && $2 )
		{
			AstType *pType = (AstType*)$2;
			if ( !pType || (pType->getNodeType() == NT_exception) )
			{
				// type ERROR 
			} else 
			{
				pOp = new AstOperation($1, pType, *$4, pScope);

                AstInterface::DoubleMemberDeclarations doubleMembers(
                    pScope->checkMemberClashes(pOp));
                if (doubleMembers.empty()) {
                    pScope->addMember(pOp);
                } else {
                    reportDoubleMemberDeclarations(doubleMembers);
                }
			}
		}
		delete $4;
		/*
		 * Push the operation scope onto the scopes stack
		 */
		idlc()->scopes()->push(pOp);
	}
	'('
	{
		idlc()->setParseState(PS_OpSqSeen);
	}
	parameters
	{
		idlc()->setParseState(PS_OpParsCompleted);
	}
	')'
	{
		idlc()->setParseState(PS_OpQsSeen);
	}
	opt_raises
	{
		AstScope*		pScope = idlc()->scopes()->topNonNull();
		AstOperation* 	pOp = NULL;
		/*
		 * Add exceptions and context to the operation
		 */
		if ( pScope && pScope->getScopeNodeType() == NT_operation)
		{
			pOp = (AstOperation*)pScope;

			if ( pOp )
				pOp->setExceptions($12);
		}
        delete $12;
		/*
		 * Done with this operation. Pop its scope from the scopes stack
		 */
		idlc()->scopes()->pop();
	}
	;

operation_head :
	'['
	IDL_ONEWAY
	{
		idlc()->setParseState(PS_OpOnewaySeen);
	}
	']'
	{
		idlc()->setParseState(PS_OpHeadSeen);
		$$ = OP_ONEWAY;
	}
	| /* EMPTY */
	{
		$$ = OP_NONE;
	}
	;

op_type_spec :
	simple_type_spec
	| IDL_VOID
	{
		$$ = idlc()->scopes()->bottom()->lookupPrimitiveType(ET_void);
	}
	;

parameters :
	parameter
	| parameters
	','
	{
		idlc()->setParseState(PS_OpParCommaSeen);
	}
	parameter
	| /* EMPTY */
	| error ',' 
	{
		yyerror("parameter definition");
		yyerrok;
	}
	;

parameter :
	'['
	direction
	']'
	{
		idlc()->setParseState(PS_OpParDirSeen);
	}
	simple_type_spec
	{
		idlc()->setParseState(PS_OpParTypeSeen);
	}
    opt_rest
	declarator
	{
		idlc()->setParseState(PS_OpParDeclSeen);

        AstOperation * pScope = static_cast< AstOperation * >(
            idlc()->scopes()->top());
		AstParameter* 	pParam = NULL;

		/*
		 * Create a node representing an argument to an operation
		 * Add it to the enclosing scope (the operation scope)
		 */
		if ( pScope && $5 && $8 )
		{
            AstType const * pType = $8->compose($5);
			if ( pType )
			{
                if (pScope->isConstructor() && $2 != DIR_IN) {
                    idlc()->error()->error0(EIDL_CONSTRUCTOR_PARAMETER_NOT_IN);
                }
                if (pScope->isVariadic()) {
                    idlc()->error()->error0(EIDL_REST_PARAMETER_NOT_LAST);
                }
                if ($7) {
                    AstDeclaration const * type = resolveTypedefs(pType);
                    if (type->getNodeType() != NT_predefined
                        || (static_cast< AstBaseType const * >(type)->
                            getExprType() != ET_any))
                    {
                        idlc()->error()->error0(EIDL_REST_PARAMETER_NOT_ANY);
                    }
                    if (pScope->isConstructor()) {
                        if (pScope->getIteratorBegin()
                            != pScope->getIteratorEnd())
                        {
                            idlc()->error()->error0(
                                EIDL_CONSTRUCTOR_REST_PARAMETER_NOT_FIRST);
                        }
                    } else {
                        idlc()->error()->error0(EIDL_METHOD_HAS_REST_PARAMETER);
                    }
                }

				pParam = new AstParameter(
                    static_cast< Direction >($2), $7, pType, $8->getName(),
                    pScope);
								
				if ( !$8->checkType($5) )
				{
					// WARNING	
				}

				pScope->addDeclaration(pParam);
			}
		}
	}
	| error
	simple_type_spec
	{
		idlc()->setParseState(PS_NoState);
		yyerrok;
	}
	;

direction :
	IDL_IN
	{
		$$ = DIR_IN;
	}
	| IDL_OUT
	{
		$$ = DIR_OUT;
	}
	| IDL_INOUT
	{
		$$ = DIR_INOUT;
	}
	;

opt_rest:
    IDL_ELLIPSIS
    {
        $$ = true;
    }
    | /* empty */
    {
        $$ = false;
    }
    ;

opt_raises:
    raises
    | /* empty */
    {
        $$ = 0;
    }
    ;

raises:
    IDL_RAISES
    {
        idlc()->setParseState(PS_RaiseSeen);
    }
    '('
    {
        idlc()->setParseState(PS_RaiseSqSeen);
    }
    exception_list
    ')'
    {
        idlc()->setParseState(PS_RaiseQsSeen);
        $$ = $5;
    }
    ;

exception_list:
    exception_name
    {
        $$ = new DeclList;
        $$->push_back($1);
    }
    | exception_list ',' exception_name
    {
        $1->push_back($3);
        $$ = $1;
    }
    ;

exception_name:
    scoped_name
    {
        // The topmost scope is either an AstOperation (for interface methods
        // and service constructors) or an AstAttribute (for interface
        // attributes), so look up exception names in the next-to-topmost scope:
        AstDeclaration * decl = idlc()->scopes()->nextToTop()->lookupByName(
            *$1);
        if (decl == 0) {
            idlc()->error()->lookupError(*$1);
        } else if (!idlc()->error()->checkPublished(decl)) {
            decl = 0;
        } else if (decl->getNodeType() != NT_exception) {
            idlc()->error()->error1(EIDL_ILLEGAL_RAISES, decl);
            decl = 0;
        }
        delete $1;
        $$ = decl;
    }
    ;

interface_inheritance_decl:
    optional_inherited_interface
    IDL_INTERFACE
    {
        idlc()->setParseState(PS_ServiceIFHeadSeen);
    }
    scoped_name
    {
        AstInterface * ifc = static_cast< AstInterface * >(
            idlc()->scopes()->top());
        if (ifc->usesSingleInheritance()) {
            idlc()->error()->error0(EIDL_MIXED_INHERITANCE);
        } else {
            addInheritedInterface(
                ifc, *$4, $1,
                rtl::OStringToOUString(
                    idlc()->getDocumentation(), RTL_TEXTENCODING_UTF8));
        }
        delete $4;
    }
    ;

optional_inherited_interface:
    '[' IDL_OPTIONAL ']' { $$ = true; }
    | /* EMPTY */ { $$ = false; }
    ;

constants_exports :
	constants_export constants_exports
	| /* EMPTY */
	;

constants_export :
	const_dcl
	{
		idlc()->setParseState(PS_ConstantDeclSeen);
	}
	';' {};

const_dcl : 
	IDL_CONST
	{
		idlc()->setParseState(PS_ConstSeen);
	}
	const_type
	{
		idlc()->setParseState(PS_ConstTypeSeen);
	}
	identifier
	{
        idlc()->setParseState(PS_ConstIDSeen);
        checkIdentifier($5);
	}
	'='
	{
		idlc()->setParseState(PS_ConstAssignSeen);
	}
	expression
	{
		idlc()->setParseState(PS_ConstExprSeen);

		AstScope* 		pScope = idlc()->scopes()->topNonNull();
		AstConstant*	pConstant = NULL;

		if ( $9 && pScope )
		{
			if ( !$9->coerce($3) )
			{
				idlc()->error()->coercionError($9, $3);
			} else
			{
				pConstant = new AstConstant($3, $9, *$5, pScope);
				pScope->addDeclaration(pConstant);
			}
		}
		delete $5;
	}
	;

constants_dcl : 
	IDL_CONSTANTS
	{
		idlc()->setParseState(PS_ConstantsSeen);
	}
	identifier
	{
        idlc()->setParseState(PS_ConstantsIDSeen);
        checkIdentifier($3);
	}
	'{'
	{
		idlc()->setParseState(PS_ConstantsSqSeen);

		AstScope* 		pScope = idlc()->scopes()->topNonNull();
		AstConstants*	pConstants = NULL;
		AstDeclaration*	pExists = NULL;

		if ( pScope )
		{
			pConstants = new AstConstants(*$3, pScope);
			if( (pExists = pScope->lookupForAdd(pConstants)) )
			{
				pExists->setInMainfile(idlc()->isInMainFile());
				delete(pConstants);
				pConstants = (AstConstants*)pExists;
			} else
			{
				pScope->addDeclaration(pConstants);
			}
			idlc()->scopes()->push(pConstants);
		}
		delete $3;
	}
	constants_exports
	{
		idlc()->setParseState(PS_ConstantsBodySeen);
	}
	'}'
	{
		idlc()->setParseState(PS_ConstantsQsSeen);
		/*
		 * Finished with this constants - pop it from the scope stack
		 */
		idlc()->scopes()->pop();
	}
	;

expression : const_expr ;

const_expr : or_expr ;

or_expr : 
	xor_expr
	| or_expr '|' xor_expr
	{
		$$ = new AstExpression(EC_or, $1, $3);
	}
	;

xor_expr :
	and_expr
	| xor_expr '^' and_expr
	{
		$$ = new AstExpression(EC_xor, $1, $3);
	}
	;

and_expr :
	shift_expr
	| and_expr '&' shift_expr
	{
		$$ = new AstExpression(EC_and, $1, $3);
	}
	;

shift_expr : 
	add_expr
	| shift_expr IDL_LEFTSHIFT add_expr
	{
		$$ = new AstExpression(EC_left, $1, $3);
	}
	| shift_expr IDL_RIGHTSHIFT add_expr
	{
		$$ = new AstExpression(EC_right, $1, $3);
	}
	;

add_expr : 
	mult_expr
	| add_expr '+' mult_expr
	{
		$$ = new AstExpression(EC_add, $1, $3);
	}
	| add_expr '-' mult_expr
	{
		$$ = new AstExpression(EC_minus, $1, $3);
	}
	;

mult_expr : 
	unary_expr
	| mult_expr '*' unary_expr
	{
		$$ = new AstExpression(EC_mul, $1, $3);
	}
	| mult_expr '/' unary_expr
	{
		$$ = new AstExpression(EC_div, $1, $3);
	}
	| mult_expr '%' unary_expr
	{
		$$ = new AstExpression(EC_mod, $1, $3);
	}
	;

unary_expr : 
	primary_expr
	| '+' primary_expr
	{
		$$ = new AstExpression(EC_u_plus, $2, NULL);
	}
	| '-' primary_expr
	{
		$$ = new AstExpression(EC_u_minus, $2, NULL);
	}
	| '~' primary_expr
	{
	}
	;

primary_expr : 
	scoped_name
	{
		/*
		 * An expression which is a scoped name is not resolved now,
		 * but only when it is evaluated (such as when it is assigned
		 * as a constant value)
		 */
		$$ = new AstExpression($1);
	}
	| literal
	| '(' const_expr ')'
	{
		$$ = $2;
	}
	;

literal : 
	IDL_INTEGER_LITERAL
	{
		$$ = new AstExpression($1);
	}
    | IDL_INTEGER_ULITERAL
    {
        $$ = new AstExpression($1);
    }
	| IDL_FLOATING_PT_LITERAL
	{
		$$ = new AstExpression($1);
	}
	| IDL_TRUE
	{
		$$ = new AstExpression((sal_Int32)1, ET_boolean);
	}
	| IDL_FALSE
	{
		$$ = new AstExpression((sal_Int32)0, ET_boolean);
	}
	;

positive_int_expr :
	const_expr
	{
		$1->evaluate(EK_const);
		if ( !$1->coerce(ET_ulong) )
		{
			idlc()->error()->coercionError($1, ET_ulong);
			delete $1;
			$$ = NULL;
		}
	}
	;

const_type :
	integer_type
	| char_type
	| byte_type
	| boolean_type
	| floating_pt_type
	| scoped_name
	{
		AstScope* 		pScope = idlc()->scopes()->topNonNull();
        AstDeclaration const * type = 0;
		
		/*
		 * If the constant's type is a scoped name, it must resolve
		 * to a scalar constant type
		 */
		if ( pScope && (type = pScope->lookupByName(*$1)) ) {
            if (!idlc()->error()->checkPublished(type))
            {
                type = 0;
            }
            else
            {
                type = resolveTypedefs(type);
                if (type->getNodeType() == NT_predefined) 
                {
                    $$ = static_cast< AstBaseType const * >(type)->
                        getExprType();
                } else
                    $$ = ET_any;
            }
		} else
			$$ = ET_any;
	}
	;

exception_header :
	IDL_EXCEPTION
	{
		idlc()->setParseState(PS_ExceptSeen);
	}
	identifier
 	{
        idlc()->setParseState(PS_ExceptIDSeen);
        checkIdentifier($3);
	}
	inheritance_spec
	{
		idlc()->setParseState(PS_InheritSpecSeen);

		$$ = new FeInheritanceHeader(NT_exception, $3, $5, 0);
		delete $5;
	}
	;

exception_dcl : 
	exception_header
	{
		idlc()->setParseState(PS_ExceptHeaderSeen);

		AstScope* 		pScope = idlc()->scopes()->topNonNull();
		AstException*	pExcept = NULL;

		if ( pScope )
		{
			AstException* pBase = static_cast< AstException* >(
                $1->getInherits());
			pExcept = new AstException(*$1->getName(), pBase, pScope);	
			pScope->addDeclaration(pExcept);
		}
		/*
		 * Push the scope of the exception on the scopes stack
		 */
		idlc()->scopes()->push(pExcept);
		delete $1;
	}
	'{'
	{
		idlc()->setParseState(PS_ExceptSqSeen);
	}
	members
	{
		idlc()->setParseState(PS_ExceptBodySeen);
	}
	'}'
	{
		idlc()->setParseState(PS_ExceptQsSeen);
		/* this exception is finished, pop its scope from the stack */ 
		idlc()->scopes()->pop();
	}
	;

property :
	flag_header
	simple_type_spec
	{
		idlc()->setParseState(PS_PropertyTypeSeen);
	}
	at_least_one_declarator
	{
		idlc()->setParseState(PS_PropertyCompleted);

		AstScope* 		pScope = idlc()->scopes()->topNonNull();
		AstAttribute* 	pAttr = NULL;
		FeDeclList*		pList = $4;
		FeDeclarator*	pDecl = NULL;
        AstType const * pType = NULL;

		if ( pScope->getScopeNodeType() == NT_singleton )
		{
			idlc()->error()->error0(EIDL_ILLEGAL_ADD);			
		} else
		{
			if ( ($1 & AF_ATTRIBUTE) == AF_ATTRIBUTE )
				idlc()->error()->flagError(EIDL_WRONGATTRIBUTEKEYWORD, AF_ATTRIBUTE);

			if ( ($1 & AF_PROPERTY) != AF_PROPERTY )
				idlc()->error()->flagError(EIDL_MISSINGATTRIBUTEKEYWORD, AF_PROPERTY);

			/*
			 * Create nodes representing attributes and add them to the
			 * enclosing scope
			 */
			if ( pScope && $2 && pList )
			{
				FeDeclList::iterator iter = pList->begin();
				FeDeclList::iterator end = pList->end();

				while (iter != end)
				{
					pDecl = (*iter);
					if ( !pDecl )
					{
						iter++;
						continue;
					}

					pType = pDecl->compose($2);				

					if ( !pType )
					{
						iter++;
						continue;
					}

					pAttr = new AstAttribute(NT_property, $1, pType, pDecl->getName(), pScope);

					pScope->addDeclaration(pAttr);
					iter++;
					delete pDecl;
				}
			}
		}			

		if ( pList )
			delete pList;
	}
	| error ';' 
	{
		yyerror("property");
		yyerrok;
	}
	;

service_exports :
	service_exports service_export
	| /* EMPTY */
	;

service_export :
	service_interface_header
	at_least_one_scoped_name
	';'
	{
		idlc()->setParseState(PS_ServiceMemberSeen);
		
		AstScope* 			pScope = idlc()->scopes()->topNonNull();
		AstDeclaration* 	pDecl = NULL;
		AstInterfaceMember* pIMember = NULL;

		if ( pScope->getScopeNodeType() == NT_singleton )
		{
			idlc()->error()->error0(EIDL_ILLEGAL_ADD);			
		} else
		{
			/*
			 * Create a node representing a class member.
			 * Store it in the enclosing scope
	         */
			if ( pScope && $2 )
			{
				StringList::iterator iter = $2->begin();
				StringList::iterator end = $2->end();						

				while ( iter != end )
				{
					pDecl = pScope->lookupByName(*iter);
					if ( pDecl && (pDecl->getNodeType() == NT_interface) )
					{
                        /* we relax the strict published check and allow to add new
                         * interfaces if they are optional
                         */
                        bool bOptional = (($1 & AF_OPTIONAL) == AF_OPTIONAL);
                        if ( idlc()->error()->checkPublished(pDecl, bOptional) )
                        {
                            pIMember = new AstInterfaceMember(
                                $1, (AstInterface*)pDecl, *iter, pScope);
                            pScope->addDeclaration(pIMember);
                        }
					} else
					{
						idlc()->error()->
							lookupError(EIDL_INTERFACEMEMBER_LOOKUP, *iter, scopeAsDecl(pScope));
					}
					iter++;
				}
			}				
		}
		delete $2;
	}
	| service_service_header
	at_least_one_scoped_name
	';'
	{
		idlc()->setParseState(PS_ServiceMemberSeen);

		AstScope* 		  pScope = idlc()->scopes()->topNonNull();
		AstDeclaration*   pDecl = NULL;
		AstServiceMember* pSMember = NULL;

		/*
		 * Create a node representing a class member.
		 * Store it in the enclosing scope
         */
		if ( pScope && $2 )
		{
			StringList::iterator iter = $2->begin();
			StringList::iterator end = $2->end();						

			while ( iter != end )
			{
				pDecl = pScope->lookupByName(*iter);
				if ( pDecl && (pDecl->getNodeType() == NT_service) )
				{
					if ( pScope->getScopeNodeType() == NT_singleton && pScope->nMembers() > 0 )
						idlc()->error()->error0(EIDL_ILLEGAL_ADD);										
                    else if ( idlc()->error()->checkPublished(pDecl) )
                    {
                        pSMember = new AstServiceMember(
                            $1, (AstService*)pDecl, *iter, pScope);
                        pScope->addDeclaration(pSMember);
                    }
				} else
				{
					idlc()->error()->
						lookupError(EIDL_SERVICEMEMBER_LOOKUP, *iter, scopeAsDecl(pScope));
				}
				iter++;
			}
		}				
		delete $2;
	}
	| IDL_OBSERVES
	at_least_one_scoped_name
	';'
	{
		idlc()->setParseState(PS_ServiceMemberSeen);

		AstScope* 		pScope = idlc()->scopes()->topNonNull();
		AstDeclaration* pDecl = NULL;
		AstObserves* 	pObserves = NULL;

		if ( pScope->getScopeNodeType() == NT_singleton )
		{
			idlc()->error()->error0(EIDL_ILLEGAL_ADD);			
		} else
		{
			/*
			 * Create a node representing a class member.
			 * Store it in the enclosing scope
	         */
			if ( pScope && $2 )
			{
				StringList::iterator iter = $2->begin();
				StringList::iterator end = $2->end();						

				while ( iter != end )
				{
					pDecl = pScope->lookupByName(*iter);
					if ( pDecl && (pDecl->getNodeType() == NT_interface) )
					{
						pObserves = new AstObserves((AstInterface*)pDecl, *iter, pScope);
						pScope->addDeclaration(pObserves);
					} else
					{
						idlc()->error()->
							lookupError(EIDL_INTERFACEMEMBER_LOOKUP, *iter, scopeAsDecl(pScope));
					}
					iter++;
				}
			}				
		}
		delete $2;
	}
	| IDL_NEEDS
	at_least_one_scoped_name
	';'
	{
		idlc()->setParseState(PS_ServiceMemberSeen);

		AstScope* 		pScope = idlc()->scopes()->topNonNull();
		AstDeclaration*	pDecl = NULL;
		AstNeeds*	   	pNeeds = NULL;

		if ( pScope->getScopeNodeType() == NT_singleton )
		{
			idlc()->error()->error0(EIDL_ILLEGAL_ADD);			
		} else
		{
			/*
			 * Create a node representing a class member.
			 * Store it in the enclosing scope
	         */
			if ( pScope && $2 )
			{
				StringList::iterator iter = $2->begin();
				StringList::iterator end = $2->end();						

				while ( iter != end )
				{
					pDecl = pScope->lookupByName(*iter);
					if ( pDecl && (pDecl->getNodeType() == NT_service) )
					{
						pNeeds = new AstNeeds((AstService*)pDecl, *iter, pScope);
						pScope->addDeclaration(pNeeds);
					} else
					{
						idlc()->error()->
							lookupError(EIDL_SERVICEMEMBER_LOOKUP, *iter, scopeAsDecl(pScope));
					}
					iter++;
				}
			}				
		}
		delete $2;
	}
	| property
	';'
	{
		idlc()->setParseState(PS_PropertyDeclSeen);
	}
	;

service_interface_header :
	IDL_INTERFACE
	{
		idlc()->setParseState(PS_ServiceIFHeadSeen);
		$$ = AF_INVALID;
	}
	| flag_header
	IDL_INTERFACE
	{
		idlc()->setParseState(PS_ServiceIFHeadSeen);
		if ( (AF_OPTIONAL != $1) && ( AF_INVALID != $1) )
			idlc()->error()->flagError(EIDL_OPTIONALEXPECTED, $1);
		$$ = $1;
	}
	;

service_service_header :
	IDL_SERVICE
	{
		idlc()->setParseState(PS_ServiceSHeadSeen);
		$$ = AF_INVALID;
	}
	| flag_header
	IDL_SERVICE
	{
		idlc()->setParseState(PS_ServiceSHeadSeen);
		if ( (AF_OPTIONAL != $1) && ( AF_INVALID != $1) )
			idlc()->error()->flagError(EIDL_OPTIONALEXPECTED, $1);
		$$ = $1;
	}
	;

service_dcl : 
	IDL_SERVICE
	{
		idlc()->setParseState(PS_ServiceSeen);
	}
	identifier
 	{
        idlc()->setParseState(PS_ServiceIDSeen);
        checkIdentifier($3);

        AstScope* 	pScope = idlc()->scopes()->topNonNull();
        AstService*	pService = NULL;

        /*
         * Make a new service and add it to the enclosing scope
         */
        if (pScope != NULL)
        {
            pService = new AstService(*$3, pScope);
            pScope->addDeclaration(pService);
        }
        delete $3;
        /*
         * Push it on the stack
         */
        idlc()->scopes()->push(pService);
	}
    service_dfn
	{
		/* this service is finished, pop its scope from the stack */ 
		idlc()->scopes()->pop();
	}
	;

service_dfn:
    service_interface_dfn
    | service_obsolete_dfn
    ;

service_interface_dfn:
    ':' scoped_name
    {
        AstScope * scope = idlc()->scopes()->nextToTop();
            // skip the scope pushed by service_dcl
        AstDeclaration * decl = scope->lookupByName(*$2);
        if (decl != 0 && resolveTypedefs(decl)->getNodeType() == NT_interface) {
            if (idlc()->error()->checkPublished(decl)) {
                idlc()->scopes()->top()->addDeclaration(decl);
            }
        } else {
            idlc()->error()->lookupError(
                EIDL_INTERFACEMEMBER_LOOKUP, *$2, scopeAsDecl(scope));
        }
        delete $2;
    }
    opt_service_body
    {
        AstService * s = static_cast< AstService * >(idlc()->scopes()->top());
        if (s != 0) {
            s->setDefaultConstructor(!$4);
        }
    }
    ;

opt_service_body:
    service_body { $$ = true; }
    | /* empty */ { $$ = false; }
    ;

service_body:
    '{'
    constructors
    '}'
    ;

constructors:
    constructors constructor
    | /* empty */
    ;

constructor:
    identifier
    {
        checkIdentifier($1);
        AstScope * scope = idlc()->scopes()->top();
        AstOperation * ctor = new AstOperation(OP_NONE, 0, *$1, scope);
        delete $1;
        scope->addDeclaration(ctor);
		idlc()->scopes()->push(ctor);
    }
    '('
    parameters
    ')'
    opt_raises
    {
        static_cast< AstOperation * >(idlc()->scopes()->top())->setExceptions(
            $6);
        delete $6;
        idlc()->scopes()->pop();
        if (static_cast< AstService * >(idlc()->scopes()->top())->
            checkLastConstructor())
        {
            idlc()->error()->error0(EIDL_SIMILAR_CONSTRUCTORS);
        }
    }
    ';'
    ;

singleton_dcl : 
	IDL_SINGLETON
	{
		idlc()->setParseState(PS_SingletonSeen);
	}
	identifier
 	{
        idlc()->setParseState(PS_SingletonIDSeen);
        checkIdentifier($3);

		AstScope* 	pScope = idlc()->scopes()->topNonNull();
		AstService*	pService = NULL;
		
		/*
		 * Make a new service and add it to the enclosing scope
		 */
		if (pScope != NULL) 
		{
			pService = new AstService(NT_singleton, *$3, pScope);
			pScope->addDeclaration(pService);
		}
		delete $3;
		/*
		 * Push it on the stack
		 */
		idlc()->scopes()->push(pService);
	}
    singleton_dfn
	{
		/* this singelton is finished, pop its scope from the stack */ 
		idlc()->scopes()->pop();
	}
	;

singleton_dfn:
    singleton_interface_dfn
    | service_obsolete_dfn
    ;

singleton_interface_dfn:
    ':' scoped_name
    {
        AstScope * scope = idlc()->scopes()->nextToTop();
            // skip the scope (needlessly) pushed by singleton_dcl
        AstDeclaration * decl = scope->lookupByName(*$2);
        if (decl != 0 && resolveTypedefs(decl)->getNodeType() == NT_interface) {
            if (idlc()->error()->checkPublished(decl)) {
                idlc()->scopes()->top()->addDeclaration(decl);
            }
        } else {
            idlc()->error()->lookupError(
                EIDL_INTERFACEMEMBER_LOOKUP, *$2, scopeAsDecl(scope));
        }
        delete $2;
    }
    ;

service_obsolete_dfn:
    '{'
    {
        idlc()->setParseState(
            idlc()->scopes()->top()->getScopeNodeType() == NT_service
            ? PS_ServiceSqSeen : PS_SingletonSqSeen);
    }
    service_exports
    {
        idlc()->setParseState(
            idlc()->scopes()->top()->getScopeNodeType() == NT_service
            ? PS_ServiceBodySeen : PS_SingletonBodySeen);
    }
    '}'
    {
        idlc()->setParseState(
            idlc()->scopes()->top()->getScopeNodeType() == NT_service
            ? PS_ServiceQsSeen : PS_SingletonQsSeen);
    }
    ;

type_dcl :
	IDL_TYPEDEF
	{
		idlc()->setParseState(PS_TypedefSeen);
	}
	type_declarator {}
	| struct_type {}
	| union_type {}
	| enum_type {}
	;

type_declarator :
	type_spec
	{
		idlc()->setParseState(PS_TypeSpecSeen);
        if ($1 != 0 && $1->getNodeType() == NT_instantiated_struct) {
            idlc()->error()->error0(EIDL_INSTANTIATED_STRUCT_TYPE_TYPEDEF);
        }
	}
	at_least_one_declarator
	{
		idlc()->setParseState(PS_DeclaratorsSeen);

		AstScope* 		pScope = idlc()->scopes()->topNonNull();
		AstTypeDef* 	pTypeDef = NULL;
		FeDeclList*		pList = $3;
		FeDeclarator*	pDecl = NULL;
        AstType const * pType = NULL;

		/*
		 * Create nodes representing typedefs and add them to the
		 * enclosing scope
		 */
		if ( pScope && $1 && pList )
		{
			FeDeclList::iterator iter = pList->begin();
			FeDeclList::iterator end = pList->end();

			while (iter != end)
			{
				pDecl = (*iter);
				if ( !pDecl )
				{
					iter++;
					continue;
				}

				pType = pDecl->compose($1);

				if ( !pType )
				{
					iter++;
					continue;
				}

				pTypeDef = new AstTypeDef(pType, pDecl->getName(), pScope);

				pScope->addDeclaration(pTypeDef);
				iter++;
				delete pDecl;
			}
			delete pList;
		}
	}
	;

at_least_one_declarator :
	declarator declarators
	{
		if ( $2 )
		{
			$2->push_back($1);
			$$ = $2;
		} else
		{
			FeDeclList* pList = new FeDeclList();
			pList->push_back($1);
			$$ = pList;
		}
	}
	;

declarators : 
	declarators
	','
	{
		idlc()->setParseState(PS_DeclsCommaSeen);
	} 
	declarator
	{
		idlc()->setParseState(PS_DeclsDeclSeen);
		if ( $1 )
		{
			$1->push_back($4);
			$$ = $1;
		} else
		{
			FeDeclList* pList = new FeDeclList();
			pList->push_back($4);
			$$ = pList;
		}
	}
	| /* EMPTY */
	{
		$$ = NULL;
	}
	;

declarator : 
	simple_declarator
	| complex_declarator
	;

simple_declarator :
	identifier
	{
        // For historic reasons, the struct com.sun.star.uno.Uik contains
        // members with illegal names (of the form "m_DataN"); avoid useless
        // warnings about them:
        AstScope * scope = idlc()->scopes()->top();
        if (scope == 0 || scope->getScopeNodeType() != NT_struct
            || (scopeAsDecl(scope)->getScopedName()
                != "com::sun::star::uno::Uik"))
        {
            checkIdentifier($1);
        }

        $$ = new FeDeclarator(*$1, FeDeclarator::FD_simple, NULL);
        delete $1;
	}
	;

complex_declarator :
	array_declarator
	{
		$$ = new FeDeclarator($1->getLocalName(), FeDeclarator::FD_complex, $1);
	}
	;

array_declarator :
	identifier
	{
        idlc()->setParseState(PS_ArrayIDSeen);
        checkIdentifier($1);
	}
	at_least_one_array_dim
	{
		idlc()->setParseState(PS_ArrayCompleted);
		$$ = new AstArray(*$1, NULL, *$3, idlc()->scopes()->bottom());
		delete $1;
	}
	;

at_least_one_array_dim :
	array_dim array_dims
	{
		if( $2 )
		{
			$2->push_front($1);
			$$ = $2;
		} else
		{
			ExprList* pList = new ExprList();
			pList->push_back($1);
			$$ = pList;
		}
	}
	;

array_dims : 
	array_dims array_dim
	{
		if( $1 )
		{
			$1->push_back($2);
			$$ = $1;
		} else
		{
			ExprList* pList = new ExprList();
			pList->push_back($2);
			$$ = pList;
		}
	}
	| /* EMPTY */
	{
		$$ = NULL;
	}
    ;

array_dim :
	'['
	{
		idlc()->setParseState(PS_DimSqSeen);
	}
	positive_int_expr
	{
		idlc()->setParseState(PS_DimExprSeen);
	}
	']'
	{
		idlc()->setParseState(PS_DimQsSeen);
		/*
		 * Array dimensions are expressions which must be coerced to
		 * positive integers
		 */
		if ( !$3 || !$3->coerce(ET_uhyper) ) 
		{
			idlc()->error()->coercionError($3, ET_uhyper);
			$$ = NULL;
		} else
			$$ = $3;
	}
	;

at_least_one_scoped_name :
	scoped_name scoped_names
	{
		if ($2)
		{
			$2->push_front(*$1);
		 	$$ = $2;
		} else
		{
			StringList* pNames = new StringList();
			pNames->push_back(*$1);
			$$ = pNames;
		}
		delete($1);
	}
	;

scoped_names :
	scoped_names
	','
	{
		idlc()->setParseState(PS_SNListCommaSeen);
	}
	scoped_name
	{
		idlc()->setParseState(PS_ScopedNameSeen);
		if ($1)
		{
			$1->push_back(*$4);
		 	$$ = $1;
		} else
		{
			StringList* pNames = new StringList();
			pNames->push_back(*$4);
			$$ = pNames;
		}
		delete($4);
	}
	| /* EMPTY */
	{
		$$ = NULL;
	}
	;

scoped_name :
	identifier
	{
        idlc()->setParseState(PS_SN_IDSeen);
        checkIdentifier($1);
        $$ = $1;
	}
	| IDL_SCOPESEPARATOR
	{
		idlc()->setParseState(PS_ScopeDelimSeen);
	}
	identifier
	{
        checkIdentifier($3);
        OString* pName = new OString("::");
        *pName += *$3;
        delete $3;
        $$ = pName;
	}
	| scoped_name
	IDL_SCOPESEPARATOR
	{
	}
	identifier
    {
        checkIdentifier($4);
        *$1 += ::rtl::OString("::");
        *$1 += *$4;
        delete $4;
        $$ = $1;
	}
	;

type_spec :
	simple_type_spec
	| constructed_type_spec
	;

simple_type_spec : 
    fundamental_type
	| scoped_name opt_type_args
	{
        $$ = createNamedType($1, $2);
	}
	;

fundamental_type:
	base_type_spec
	{
		$$ = idlc()->scopes()->bottom()->lookupPrimitiveType($1);
	}
	| template_type_spec
    ;

opt_type_args:
    '<' type_args '>' { $$ = $2; }
    | /* empty */ { $$ = 0; }
    ;

type_args:
    type_arg
    {
        $$ = new DeclList;
        $$->push_back(const_cast< AstDeclaration * >($1)); //TODO: const_cast
    }
    | type_args ',' type_arg
    {
        $1->push_back(const_cast< AstDeclaration * >($3)); //TODO: const_cast
        $$ = $1;
    }
    ;

type_arg:
    simple_type_spec
    {
        if ($1 != 0 && static_cast< AstType const * >($1)->isUnsigned()) {
            idlc()->error()->error0(EIDL_UNSIGNED_TYPE_ARGUMENT);
        }
        $$ = $1;
    }
    ;

base_type_spec : 
	integer_type
	| floating_pt_type
	| char_type
	| boolean_type
	| byte_type
	| any_type
	| type_type
	| string_type
	;

integer_type : 
	signed_int
	| unsigned_int
	;

signed_int : 
	IDL_LONG
	{
		$$ = ET_long;
	}
	| IDL_HYPER
	{
		$$ = ET_hyper;
	}
	| IDL_SHORT
	{
		$$ = ET_short;
	}
	;

unsigned_int : 
	IDL_UNSIGNED IDL_LONG
	{
		$$ = ET_ulong;
	}
	| IDL_UNSIGNED IDL_HYPER
	{
		$$ = ET_uhyper;
	}
	| IDL_UNSIGNED IDL_SHORT
	{
		$$ = ET_ushort;
	}
	;

floating_pt_type : 
	IDL_DOUBLE
	{
		$$ = ET_double;
	}
	| IDL_FLOAT
	{
		$$ = ET_float;
	}
	;

char_type : 
	IDL_CHAR
	{
		$$ = ET_char;
	}
	;

byte_type : 
	IDL_BYTE
	{
		$$ = ET_byte;
	}
	;

boolean_type : 
	IDL_BOOLEAN
	{
		$$ = ET_boolean;
	}
	;

any_type : 
	IDL_ANY
	{
		$$ = ET_any;
	}
	;

type_type :
	IDL_TYPE
	{
		$$ = ET_type;
	}
	;

string_type :
	IDL_STRING
	{
		$$ = ET_string;
	}
	;

template_type_spec : 
	sequence_type_spec
	| array_type
	;

constructed_type_spec : 
	struct_type
	| union_type
	| enum_type
	;

array_type :
	simple_type_spec
	{
		idlc()->setParseState(PS_ArrayTypeSeen);
	}
	at_least_one_array_dim
	{
		idlc()->setParseState(PS_ArrayCompleted);

		AstScope* pScope = idlc()->scopes()->bottom();
		AstDeclaration* pDecl = NULL;
		AstDeclaration* pArray = NULL;

		if ( $1 )
		{
			pArray = new AstArray((AstType*)$1, *$3, idlc()->scopes()->bottom());
			if ( pScope )
			{
				pDecl = pScope->addDeclaration(pArray);				
				if ( pArray != pDecl )
				{
					// if array type already defined then use it
					delete pArray;
					pArray = pDecl;					
				}
			}
		}
		$$ = pArray; 
	}
	;

sequence_type_spec :
	IDL_SEQUENCE
	{
		idlc()->setParseState(PS_SequenceSeen);
		/*
		 * Push a sequence marker on scopes stack
		 */
		idlc()->scopes()->push(NULL);
	}
	'<'
	{
		idlc()->setParseState(PS_SequenceSqSeen);
	}
	simple_type_spec
	{
		idlc()->setParseState(PS_SequenceTypeSeen);
	}
	'>'
	{
		idlc()->setParseState(PS_SequenceQsSeen);
		/*
		 * Remove sequence marker from scopes stack
		 */
		if (idlc()->scopes()->top() == NULL)
			idlc()->scopes()->pop();
		/*
		 * Create a node representing a sequence
		 */
		AstScope* pScope = idlc()->scopes()->bottom();
		AstDeclaration* pDecl = NULL;
		AstDeclaration* pSeq = NULL;
		
		if ( $5 )
		{
			AstType *pType = (AstType*)$5;
			if ( pType )
			{
				pSeq = new AstSequence(pType, pScope);
				/*
				 * Add this AstSequence to the types defined in the global scope
				 */
				pDecl = pScope->addDeclaration(pSeq);
				if ( pSeq != pDecl )
				{
					// if sequence type already defined then use it
					delete pSeq;
					pSeq = pDecl;
				}
			}
        }
		$$ = pSeq;
	}
	| error '>' 
	{
		yyerror("sequence declaration");
		yyerrok;
        $$ = 0;
	}
	;

struct_type :
	structure_header
	{
		idlc()->setParseState(PS_StructHeaderSeen);

		AstScope* 	pScope = idlc()->scopes()->topNonNull();
		AstStruct*	pStruct = NULL;

		if ( pScope )
		{
			AstStruct* pBase= static_cast< AstStruct* >($1->getInherits());
            pStruct = new AstStruct(
                *$1->getName(), $1->getTypeParameters(), pBase, pScope);
			pScope->addDeclaration(pStruct);
		}
		/*
		 * Push the scope of the struct on the scopes stack
		 */
		idlc()->scopes()->push(pStruct);
		delete $1;
	}
	'{'
	{
		idlc()->setParseState(PS_StructSqSeen);
	}
	at_least_one_member
	{
		idlc()->setParseState(PS_StructBodySeen);
	}
	'}'
	{
		idlc()->setParseState(PS_StructQsSeen);
		/* this exception is finished, pop its scope from the stack */ 
		idlc()->scopes()->pop();
	}
	;

structure_header :
	IDL_STRUCT
	{
        idlc()->setParseState(PS_StructSeen);
	}
	identifier
 	{
        idlc()->setParseState(PS_StructIDSeen);
        checkIdentifier($3);
	}
    opt_type_params
	inheritance_spec
	{
        idlc()->setParseState(PS_InheritSpecSeen);

        // Polymorphic struct type templates with base types would cause various
        // problems in language bindings, so forbid them here.  For example,
        // GCC prior to version 3.4 fails with code like
        //
        //  struct Base { ... };
        //  template< typename typeparam_T > struct Derived: public Base {
        //      int member1 CPPU_GCC3_ALIGN(Base);
        //      ... };
        //
        // (Note that plain struct types with instantiated polymorphic struct
        // type bases, which might also cause problems in language bindings, are
        // already rejected on a syntactic level.)
        if ($5 != 0 && $6 != 0) {
            idlc()->error()->error0(EIDL_STRUCT_TYPE_TEMPLATE_WITH_BASE);
        }

        $$ = new FeInheritanceHeader(NT_struct, $3, $6, $5);
        delete $5;
        delete $6;
	}
	;

opt_type_params:
    '<' type_params '>' { $$ = $2; }
    | /* empty */ { $$ = 0; }
    ;

type_params:
    identifier
    {
        $$ = new std::vector< rtl::OString >;
        $$->push_back(*$1);
        delete $1;
    }
    | type_params ',' identifier
    {
        if (std::find($1->begin(), $1->end(), *$3) != $1->end()) {
            idlc()->error()->error0(EIDL_IDENTICAL_TYPE_PARAMETERS);
        }
        $1->push_back(*$3);
        delete $3;
        $$ = $1;
    }
    ;

at_least_one_member : member members ;

members : 
	members member
	| /* EMPTY */
	;

member :
	type_or_parameter
	{
		idlc()->setParseState(PS_MemberTypeSeen);
	}
	at_least_one_declarator
	{
		idlc()->setParseState(PS_MemberDeclsSeen);
	}
	';'
	{
		idlc()->setParseState(PS_MemberDeclsCompleted);

		AstScope* 		pScope = idlc()->scopes()->topNonNull();
		AstMember*		pMember = NULL;
		FeDeclList*		pList = $3;
		FeDeclarator*	pDecl = NULL;
        AstType const * pType = NULL;

		// !!! check recursive type

		if ( pScope && pList && $1 )
		{
			FeDeclList::iterator iter = pList->begin();
			FeDeclList::iterator end = pList->end();
			while (iter != end)
			{
				pDecl = (*iter);
				if ( !pDecl )
				{
					iter++;
					continue;
				}

				pType = pDecl->compose($1);				

				if ( !pType )
				{
					iter++;
					continue;
				}

				pMember = new AstMember(pType, pDecl->getName(), pScope);

				if ( !pDecl->checkType($1) )
				{
					// WARNING	
				}

				pScope->addDeclaration(pMember);
				iter++;
				delete pDecl;
			}
			delete pList;
		}
	}
	| error ';' 
	{
		yyerror("member definition");
		yyerrok;
	}
	;

type_or_parameter:
    fundamental_type
	| scoped_name opt_type_args
	{
        AstDeclaration const * decl = 0;
        AstStruct * scope = static_cast< AstStruct * >(idlc()->scopes()->top());
        if (scope != 0 && $2 == 0) {
            decl = scope->findTypeParameter(*$1);
        }
        if (decl != 0) {
            delete $1;
            delete $2;
        } else {
            decl = createNamedType($1, $2);
            if (scope != 0 && includes(decl, scopeAsDecl(scope))) {
                idlc()->error()->error1(
                    EIDL_RECURSIVE_TYPE, scopeAsDecl(scope));
                decl = 0;
            }
        }
        $$ = decl;
	}
    ;

enum_type :
	IDL_ENUM
	{
		idlc()->setParseState(PS_EnumSeen);
	}
	identifier
	{
        idlc()->setParseState(PS_EnumIDSeen);
        checkIdentifier($3);

		AstScope* 		pScope = idlc()->scopes()->topNonNull();
		AstEnum*		pEnum = NULL;

		/*
		 * Create a node representing an enum and add it to its
		 * enclosing scope
		 */
		if (pScope != NULL) 
		{
			pEnum = new AstEnum(*$3, pScope);
			/*
			 * Add it to its defining scope
			 */
			pScope->addDeclaration(pEnum);
		}
		delete $3;
		/*
		 * Push the enum scope on the scopes stack
		 */
		idlc()->scopes()->push(pEnum);
		
	}
	'{'
	{
		idlc()->setParseState(PS_EnumSqSeen);
	}
	at_least_one_enumerator
	{
		idlc()->setParseState(PS_EnumBodySeen);
	}
	'}'
	{
		idlc()->setParseState(PS_EnumQsSeen);
		/*
		 * Done with this enum. Pop its scope from the scopes stack
		 */
		if (idlc()->scopes()->top() == NULL)
			$$ = NULL;
		else 
		{
			$$ = (AstEnum*)idlc()->scopes()->topNonNull();
			idlc()->scopes()->pop();
		}
	}
	;

at_least_one_enumerator : enumerator enumerators ;

enumerators : 
	enumerators
	','
	{
		idlc()->setParseState(PS_EnumCommaSeen);
	}
	enumerator
	| /* EMPTY */
	| error ',' 
	{
		yyerror("enumerator definition");
		yyerrok;
	}
	;

enumerator :
	identifier
	{
        checkIdentifier($1);

		AstScope* 		pScope = idlc()->scopes()->topNonNull();
		AstEnum*		pEnum = NULL;
		AstConstant* 	pEnumVal = NULL;

		if ( pScope && pScope->getScopeNodeType() == NT_enum) 
		{
			pEnum = (AstEnum*)pScope;
			if (pEnum && $1)
			{
				AstExpression* pExpr = new AstExpression(pEnum->getEnumValueCount());
				pEnumVal = new AstConstant(ET_long , NT_enum_val, 
										   pExpr, *$1, pScope);
			}
			if ( pEnum->checkValue(pEnumVal->getConstValue()) )
				idlc()->error()->error1(EIDL_EVAL_ERROR, pEnum);				

			pScope->addDeclaration(pEnumVal);
		}
		delete $1;
	}
	| identifier
	'='
	const_expr
	{
        checkIdentifier($1);

		AstScope* 		pScope = idlc()->scopes()->topNonNull();
		AstEnum*		pEnum = NULL;
		AstConstant* 	pEnumVal = NULL;

		if ( $3 && pScope && pScope->getScopeNodeType() == NT_enum) 
		{
			$3->evaluate(EK_const);
			if ( $3->coerce(ET_long) )
			{
				pEnum = (AstEnum*)pScope;
				if (pEnum)
				{
					pEnumVal = new AstConstant(ET_long , NT_enum_val, 
											   $3, *$1, pScope);
				}
				if ( pEnum->checkValue(pEnumVal->getConstValue()) )
					idlc()->error()->error1(EIDL_EVAL_ERROR, pEnum);				

				pScope->addDeclaration(pEnumVal);
			} else
			{
				idlc()->error()->coercionError($3, ET_long);
				delete $3;
			}
		}
		delete $1;
	}
	;

union_type :
	IDL_UNION
	{
		idlc()->setParseState(PS_UnionSeen);
	}
	identifier
	{
        idlc()->setParseState(PS_UnionIDSeen);
        checkIdentifier($3);
	}
	IDL_SWITCH
	{
		idlc()->setParseState(PS_SwitchSeen);
	}
	'('
	{
		idlc()->setParseState(PS_SwitchOpenParSeen);
	}
	switch_type_spec
	{
		idlc()->setParseState(PS_SwitchTypeSeen);
	}
	')'
	{
		idlc()->setParseState(PS_SwitchCloseParSeen);

		AstScope* 		pScope = idlc()->scopes()->topNonNull();
		AstUnion*		pUnion = NULL;

		/*
		 * Create a node representing a union. Add it to its enclosing
		 * scope
		 */
		if ( $9 && pScope ) 
		{
			AstType* pType = (AstType*)$9;
			if ( !pType) 
			{
				idlc()->error()->noTypeError($9);
			} else 
			{
				pUnion = new AstUnion(*$3, pType, pScope);
				pScope->addDeclaration(pUnion);
			}
		}
		delete $3;
		/*
		 * Push the scope of the union on the scopes stack
		 */
		idlc()->scopes()->push(pUnion);
	}
	'{'
	{
		idlc()->setParseState(PS_UnionSqSeen);
	}
	at_least_one_case_branch
	{
		idlc()->setParseState(PS_UnionBodySeen);
	}
	'}'
	{
		idlc()->setParseState(PS_UnionQsSeen);
		/* this union is finished, pop its scope from the stack */ 
		idlc()->scopes()->pop();
	}
	;

switch_type_spec :
	integer_type
	{	
		$$ = idlc()->scopes()->bottom()->lookupPrimitiveType($1);
	}
	| char_type
	{
		$$ = idlc()->scopes()->bottom()->lookupPrimitiveType($1);
	}
	| boolean_type
	{
		$$ = idlc()->scopes()->bottom()->lookupPrimitiveType($1);
	}
	| enum_type
	| scoped_name
	{
		AstScope* 		pScope = idlc()->scopes()->topNonNull();
		AstBaseType*	pBaseType = NULL;
        AstDeclaration const * pDecl = NULL;
		AstTypeDef*		pTypeDef = NULL;
		sal_Bool		bFound = sal_False;
		/*
		 * If the constant's type is a scoped name, it must resolve
		 * to a scalar constant type
		 */
		if ( pScope && (pDecl = pScope->lookupByName(*$1)) ) 
		{
			/*
			 * Look through typedefs
			 */
			while ( !bFound ) 
			{
				switch (pDecl->getNodeType()) 
				{
					case NT_enum:
						$$ = pDecl;
						bFound = sal_True;
						break;
					case NT_predefined:
						pBaseType = (AstBaseType*)pDecl;
						if ( pBaseType ) 
						{
							switch (pBaseType->getExprType()) 
							{
								case ET_short:
								case ET_ushort:
								case ET_long:
								case ET_ulong:
								case ET_hyper:
								case ET_uhyper:
								case ET_char:
								case ET_byte:
								case ET_boolean:
									$$ = pBaseType;
									bFound = sal_True;
									break;
								default:
									$$ = NULL;
									bFound = sal_True;
									break;
							}
						}
						break;
					case NT_typedef:
						pTypeDef = (AstTypeDef*)pDecl;
						if ( pTypeDef )
							pDecl = pTypeDef->getBaseType();
						break;
					default:
						$$ = NULL;
						bFound = sal_True;
		               break;		
				}
			}
		} else
			$$ = NULL;

		if ($$ == NULL)
			idlc()->error()->lookupError(*$1);
	}
	;

at_least_one_case_branch : case_branch case_branches ;

case_branches : 
	case_branches case_branch
	| /* EMPTY */
	;

case_branch :
	at_least_one_case_label
	{
		idlc()->setParseState(PS_UnionLabelSeen);
	}
	element_spec
	{
		idlc()->setParseState(PS_UnionElemSeen);

		AstScope* 		pScope = idlc()->scopes()->topNonNull();
		AstUnionLabel*	pLabel = NULL;
		AstUnionBranch* pBranch = NULL;
		AstMember*		pMember = $3;

		/*
		 * Create several nodes representing branches of a union.
		 * Add them to the enclosing scope (the union scope)
		 */
		if ( pScope && $1 && $3 ) 
		{
			LabelList::iterator iter = $1->begin();
			LabelList::iterator end = $1->end();
			for (;iter != end; iter++) 
			{
				pLabel = *iter;
				if ( !pLabel )
				{
					iter++;
					continue;
				}
				pBranch = new AstUnionBranch(pLabel, pMember->getType(),
                                        	 pMember->getLocalName(), pScope);
				pScope->addDeclaration(pBranch);
			}
		}
		if ( $1 ) delete($1);
	}
	;

at_least_one_case_label :
	case_label case_labels
	{
		if ( $2 )
		{
			$2->push_front($1);
		 	$$ = $2;
		} else
		{
			LabelList* pLabels = new LabelList();
			pLabels->push_back($1);
			$$ = pLabels;
		}
	}
	;

case_labels : 
	case_labels case_label
	{
		if ( $1 )
		{
			$1->push_back($2);
		 	$$ = $1;
		} else
		{
			LabelList* pLabels = new LabelList();
			pLabels->push_back($2);
			$$ = pLabels;
		}
	}
	| /* EMPTY */
	{
		$$ = NULL;
	}
	;

case_label : 
	IDL_DEFAULT
	{
		idlc()->setParseState(PS_DefaultSeen);
	}
	':'
	{
		idlc()->setParseState(PS_LabelColonSeen);
		$$ = new AstUnionLabel(UL_default, NULL);
	}
	| IDL_CASE
	{
		idlc()->setParseState(PS_CaseSeen);
	}
	const_expr
	{
		idlc()->setParseState(PS_LabelExprSeen);
	}
	':'
	{
		idlc()->setParseState(PS_LabelColonSeen);
		$$ = new AstUnionLabel(UL_label, $3);
	}
	;

element_spec :
	type_spec
	{
		idlc()->setParseState(PS_UnionElemTypeSeen);
	}
	declarator
	{
		idlc()->setParseState(PS_UnionElemDeclSeen);
	}
	';'
	{
		idlc()->setParseState(PS_UnionElemCompleted);

		AstScope* pScope = idlc()->scopes()->topNonNull();
		/*
		 * Check for illegal recursive use of type
		 */
//		if ( $1 && AST_illegal_recursive_type($1))
//			idlc()->error()->error1(EIDL_RECURSIVE_TYPE, $1);
		/*
		 * Create a field in a union branch
		 */
		if ( $1 && $3 )
		{
            AstType const * pType = $3->compose($1);
			if ( !pType )
				$$ = NULL;
			else
				$$ = new AstMember(pType, $3->getName(), pScope);
		} else
			$$ = NULL;
		
		if ( $3 ) delete $3;
	}
	| error
	';'
	{
		$$ = NULL;
	}
	;

identifier:
    IDL_IDENTIFIER
    | IDL_GET { $$ = new OString("get"); }
    | IDL_SET { $$ = new OString("set"); }
    | IDL_PUBLISHED { $$ = new OString("published"); }
    ;

%%

/*
 * Report an error situation discovered in a production
 */
void yyerror(char const *errmsg)
{
	idlc()->error()->syntaxError(idlc()->getParseState(), idlc()->getLineNumber(), errmsg);
	idlc()->setParseState(PS_NoState);
}
