/*
 * The SIP parser.
 *
 * Copyright (c) 2007
 * 	Riverbank Computing Limited <info@riverbankcomputing.co.uk>
 * 
 * This file is part of SIP.
 * 
 * This copy of SIP is licensed for use under the terms of the SIP License
 * Agreement.  See the file LICENSE for more details.
 * 
 * SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */

%{
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "sip.h"


#define MAX_NESTED_IF       10
#define MAX_NESTED_SCOPE    10

#define inMainModule()      (currentSpec -> module == currentModule)


static sipSpec *currentSpec;            /* The current spec being parsed. */
static stringList *neededQualifiers;    /* The list of required qualifiers. */
static stringList *excludedQualifiers;  /* The list of excluded qualifiers. */
static moduleDef *currentModule;        /* The current module being parsed. */
static mappedTypeDef *currentMappedType;    /* The current mapped type. */
static enumDef *currentEnum;            /* The current enum being parsed. */
static int sectionFlags;                /* The current section flags. */
static int currentOverIsVirt;           /* Set if the overload is virtual. */
static int currentCtorIsExplicit;       /* Set if the ctor is explicit. */
static int currentIsStatic;             /* Set if the current is static. */
static char *previousFile;              /* The file just parsed. */
static parserContext newContext;        /* The new pending context. */
static int skipStackPtr;                /* The skip stack pointer. */
static int skipStack[MAX_NESTED_IF];    /* Stack of skip flags. */
static classDef *scopeStack[MAX_NESTED_SCOPE];  /* The scope stack. */
static int sectFlagsStack[MAX_NESTED_SCOPE];    /* The section flags stack. */
static int currentScopeIdx;             /* The scope stack index. */
static int currentTimelineOrder;        /* The current timeline order. */


static char *getPythonName(optFlags *optflgs, char *cname);
static nameDef *cacheName(sipSpec *,char *);
static classDef *findClass(sipSpec *,ifaceFileType,scopedNameDef *);
static classDef *findClassWithInterface(sipSpec *pt, ifaceFileDef *iff);
static classDef *newClass(sipSpec *,ifaceFileType,scopedNameDef *);
static void finishClass(sipSpec *,moduleDef *,classDef *,optFlags *);
static exceptionDef *findException(sipSpec *pt, scopedNameDef *fqname, int new);
static mappedTypeDef *newMappedType(sipSpec *,argDef *);
static enumDef *newEnum(sipSpec *,moduleDef *,char *,optFlags *,int);
static void instantiateClassTemplate(sipSpec *pt, moduleDef *mod, classDef *scope, scopedNameDef *fqname, classTmplDef *tcd, templateDef *td);
static void newTypedef(sipSpec *,moduleDef *,char *,argDef *);
static void newVar(sipSpec *,moduleDef *,char *,int,argDef *,optFlags *,
           codeBlock *,codeBlock *,codeBlock *);
static void newCtor(char *,int,signatureDef *,optFlags *,codeBlock *,
            throwArgs *,signatureDef *,int);
static void newFunction(sipSpec *,moduleDef *,int,int,int,char *,
            signatureDef *,int,int,optFlags *,codeBlock *,
            codeBlock *,throwArgs *,signatureDef *);
static optFlag *findOptFlag(optFlags *,char *,flagType);
static memberDef *findFunction(sipSpec *,moduleDef *,classDef *,nameDef *,int,
                   int);
static void checkAttributes(sipSpec *,classDef *,char *,int);
static void newModule(FILE *,char *);
static void appendCodeBlock(codeBlock **,codeBlock *);
static void parseFile(FILE *,char *,moduleDef *,int);
static void handleEOF(void);
static void handleEOM(void);
static qualDef *findQualifier(char *);
static scopedNameDef *text2scopedName(char *);
static scopedNameDef *scopeScopedName(scopedNameDef *name);
static void pushScope(classDef *);
static void popScope(void);
static classDef *currentScope(void);
static void newQualifier(moduleDef *,int,int,char *,qualType);
static void newImport(char *);
static void usedInMainModule(sipSpec *,ifaceFileDef *);
static int timePeriod(char *,char *);
static int platOrFeature(char *,int);
static int isNeeded(qualDef *);
static int notSkipping(void);
static void getHooks(optFlags *,char **,char **);
static int getReleaseGIL(optFlags *);
static int getHoldGIL(optFlags *);
static void templateSignature(signatureDef *sd, int result, classTmplDef *tcd, templateDef *td, classDef *ncd);
static void templateType(argDef *ad, classTmplDef *tcd, templateDef *td, classDef *ncd);
static int search_back(const char *end, const char *start, const char *target);
static char *getType(scopedNameDef *ename, argDef *ad);
static char *scopedNameToString(scopedNameDef *name);
static void addUsedFromCode(sipSpec *pt, ifaceFileList **used, const char *sname);
static int sameName(scopedNameDef *snd, const char *sname);
static int optFind(sipSpec *pt, const char *opt);
%}

%union {
    char            qchar;
    char            *text;
    long            number;
    double          real;
    argDef          memArg;
    signatureDef    signature;
    signatureDef    *optsignature;
    throwArgs       *throwlist;
    codeBlock       *codeb;
    valueDef        value;
    valueDef        *valp;
    optFlags        optflags;
    optFlag         flag;
    scopedNameDef   *scpvalp;
    fcallDef        fcall;
    int             boolean;
    exceptionDef    exceptionbase;
    classDef        *klass;
}

%token          TK_OPTIONS
%token          TK_NOEMITTERS
%token          TK_DOC
%token          TK_EXPORTEDDOC
%token          TK_MAKEFILE
%token          TK_ACCESSCODE
%token          TK_GETCODE
%token          TK_SETCODE
%token          TK_PREINITCODE
%token          TK_POSTINITCODE
%token          TK_UNITCODE
%token          TK_MODCODE
%token          TK_TYPECODE
%token          TK_PREPYCODE
%token          TK_COPYING
%token          TK_MAPPEDTYPE
%token <codeb>  TK_CODELINE
%token          TK_IF
%token          TK_END
%token <text>   TK_NAME
%token <text>   TK_PATHNAME
%token <text>   TK_STRING
%token          TK_VIRTUALCATCHERCODE
%token          TK_TRAVERSECODE
%token          TK_CLEARCODE
%token          TK_READBUFFERCODE
%token          TK_WRITEBUFFERCODE
%token          TK_SEGCOUNTCODE
%token          TK_CHARBUFFERCODE
%token          TK_METHODCODE
%token          TK_FROMTYPE
%token          TK_TOTYPE
%token          TK_TOSUBCLASS
%token          TK_INCLUDE
%token          TK_OPTINCLUDE
%token          TK_IMPORT
%token          TK_EXPHEADERCODE
%token          TK_MODHEADERCODE
%token          TK_TYPEHEADERCODE
%token          TK_MODULE
%token          TK_CMODULE
%token          TK_CLASS
%token          TK_STRUCT
%token          TK_PUBLIC
%token          TK_PROTECTED
%token          TK_PRIVATE
%token          TK_SIGNALS
%token          TK_SLOTS
%token          TK_BOOL
%token          TK_SHORT
%token          TK_INT
%token          TK_LONG
%token          TK_FLOAT
%token          TK_DOUBLE
%token          TK_CHAR
%token          TK_WCHAR_T
%token          TK_VOID
%token          TK_PYOBJECT
%token          TK_PYTUPLE
%token          TK_PYLIST
%token          TK_PYDICT
%token          TK_PYCALLABLE
%token          TK_PYSLICE
%token          TK_PYTYPE
%token          TK_VIRTUAL
%token          TK_ENUM
%token          TK_SIGNED
%token          TK_UNSIGNED
%token          TK_SCOPE
%token          TK_LOGICAL_OR
%token          TK_CONST
%token          TK_STATIC
%token          TK_SIPSIGNAL
%token          TK_SIPSLOT
%token          TK_SIPANYSLOT
%token          TK_SIPRXCON
%token          TK_SIPRXDIS
%token          TK_SIPSLOTCON
%token          TK_SIPSLOTDIS
%token <number> TK_NUMBER
%token <real>   TK_REAL
%token          TK_TYPEDEF
%token          TK_NAMESPACE
%token          TK_TIMELINE
%token          TK_PLATFORMS
%token          TK_FEATURE
%token          TK_LICENSE
%token <qchar>  TK_QCHAR
%token          TK_TRUE
%token          TK_FALSE
%token          TK_NULL
%token          TK_OPERATOR
%token          TK_THROW
%token          TK_QOBJECT
%token          TK_EXCEPTION
%token          TK_RAISECODE
%token          TK_EXPLICIT
%token          TK_TEMPLATE
%token          TK_ELLIPSIS

%type <memArg>          argvalue
%type <memArg>          argtype
%type <memArg>          cpptype
%type <memArg>          basetype
%type <signature>       template
%type <signature>       arglist
%type <signature>       rawarglist
%type <signature>       cpptypelist
%type <optsignature>    optsig
%type <optsignature>    optctorsig
%type <throwlist>       optexceptions
%type <throwlist>       exceptionlist
%type <number>          optslot
%type <number>          optref
%type <number>          optconst
%type <number>          optvirtual
%type <number>          optabstract
%type <number>          deref
%type <number>          optnumber
%type <value>           simplevalue
%type <valp>            value
%type <valp>            expr
%type <valp>            optassign
%type <codeb>           optaccesscode
%type <codeb>           optgetcode
%type <codeb>           optsetcode
%type <codeb>           exphdrcode
%type <codeb>           modhdrcode
%type <codeb>           typehdrcode
%type <codeb>           opttypehdrcode
%type <codeb>           travcode
%type <codeb>           clearcode
%type <codeb>           readbufcode
%type <codeb>           writebufcode
%type <codeb>           segcountcode
%type <codeb>           charbufcode
%type <codeb>           modcode
%type <codeb>           typecode
%type <codeb>           codeblock
%type <codeb>           codelines
%type <codeb>           virtualcatchercode
%type <codeb>           methodcode
%type <codeb>           raisecode
%type <text>            operatorname
%type <text>            optfilename
%type <text>            optname
%type <text>            modname
%type <optflags>        optflags
%type <optflags>        flaglist
%type <flag>            flag
%type <flag>            flagvalue
%type <qchar>           optunop
%type <qchar>           binop
%type <scpvalp>         scopepart
%type <scpvalp>         scopedname
%type <fcall>           exprlist
%type <boolean>         qualifiers
%type <boolean>         oredqualifiers
%type <boolean>         modlang
%type <boolean>         optclassbody
%type <exceptionbase>   baseexception
%type <klass>           class

%%

specification:  statement
    |   specification statement
    ;

statement:  {
            /*
             * We don't do these in parserEOF() because the parser
             * is reading ahead and that would be too early.
             */

            if (previousFile != NULL)
            {
                handleEOF();

                if (newContext.prevmod != NULL)
                    handleEOM();

                free(previousFile);
                previousFile = NULL;
            }
    } modstatement
    ;

modstatement:   module
    |   options
    |   noemitters
    |   copying
    |   include
    |   optinclude
    |   import
    |   timeline
    |   platforms
    |   feature
    |   license
    |   exphdrcode {
            if (notSkipping())
                appendCodeBlock(&currentSpec->exphdrcode, $1);
        }
    |   modhdrcode {
            if (notSkipping() && inMainModule())
                appendCodeBlock(&currentSpec -> hdrcode,$1);
        }
    |   modcode {
            if (notSkipping() && inMainModule())
                appendCodeBlock(&currentSpec -> cppcode,$1);
        }
    |   preinitcode
    |   postinitcode
    |   unitcode
    |   prepycode
    |   doc
    |   exporteddoc
    |   makefile
    |   mappedtype
    |   mappedtypetmpl
    |   nsstatement
    ;

nsstatement:    ifstart
    |   ifend
    |   namespace
    |   struct
    |   class
    |   classtmpl
    |   exception
    |   typedef
    |   enum
    |   function
    |   variable
    |   typehdrcode {
            if (notSkipping())
            {
                classDef *scope = currentScope();

                if (scope == NULL)
                    yyerror("%TypeHeaderCode can only be used in a namespace, class or mapped type");

                appendCodeBlock(&scope->hdrcode, $1);
            }
        }
    ;

options:    TK_OPTIONS '(' optionlist ')'
    ;

optionlist: TK_NAME {
            appendString(&currentSpec->options, $1);
        }
    |   optionlist ',' TK_NAME {
            appendString(&currentSpec->options, $3);
        }
    ;

noemitters: TK_NOEMITTERS {
            if (notSkipping())
            {
                yywarning("%SIPNoEmitters is deprecated, please use %SIPOptions instead");
                appendString(&currentSpec->options, "QtNoEmitters");
            }
        }
    ;

exception:  TK_EXCEPTION scopedname baseexception optflags '{' opttypehdrcode raisecode '}' ';' {
            if (notSkipping())
            {
                exceptionDef *xd;
                char *pyname;

                if (currentSpec->genc)
                    yyerror("%Exception not allowed in a C module");

                pyname = getPythonName(&$4, scopedNameTail($2));

                checkAttributes(currentSpec, NULL, pyname, FALSE);

                xd = findException(currentSpec, $2, TRUE);

                if (xd->cd != NULL)
                    yyerror("%Exception name has already been seen as a class name - it must be defined before being used");

                if (xd->iff->module != NULL)
                    yyerror("The %Exception has already been defined");

                /* Complete the definition. */

                xd->iff->module = currentModule;
                xd->pyname = pyname;
                xd->bibase = $3.bibase;
                xd->base = $3.base;
                xd->hdrcode = $6;
                xd->raisecode = $7;

                if (xd->bibase != NULL || xd->base != NULL)
                    xd->exceptionnr = currentModule->nrexceptions++;

                if (inMainModule() && xd->base != NULL && xd->base->iff->module != currentModule)
                    addToUsedList(&currentSpec->used, xd->base->iff);
            }
        }
    ;

baseexception:  {
            $$.bibase = NULL;
            $$.base = NULL;
        }
    |   '(' scopedname ')' {
            exceptionDef *xd;

            $$.bibase = NULL;
            $$.base = NULL;

            /* See if it is a defined exception. */
            for (xd = currentSpec->exceptions; xd != NULL; xd = xd->next)
                if (sameScopedName(xd->iff->fqcname, $2))
                {
                    $$.base = xd;
                    break;
                }

            if (xd == NULL && $2->next == NULL && strncmp($2->name, "SIP_", 4) == 0)
            {
                /* See if it is a builtin exception. */

                static char *builtins[] = {
                    "Exception",
                    "StopIteration",
                    "StandardError",
                    "ArithmeticError",
                    "LookupError",
                    "AssertionError",
                    "AttributeError",
                    "EOFError",
                    "FloatingPointError",
                    "EnvironmentError",
                    "IOError",
                    "OSError",
                    "ImportError",
                    "IndexError",
                    "KeyError",
                    "KeyboardInterrupt",
                    "MemoryError",
                    "NameError",
                    "OverflowError",
                    "RuntimeError",
                    "NotImplementedError",
                    "SyntaxError",
                    "IndentationError",
                    "TabError",
                    "ReferenceError",
                    "SystemError",
                    "SystemExit",
                    "TypeError",
                    "UnboundLocalError",
                    "UnicodeError",
                    "UnicodeEncodeError",
                    "UnicodeDecodeError",
                    "UnicodeTranslateError",
                    "ValueError",
                    "ZeroDivisionError",
                    "WindowsError",
                    "VMSError",
                    NULL
                };

                char **cp;

                for (cp = builtins; *cp != NULL; ++cp)
                    if (strcmp($2->name + 4, *cp) == 0)
                    {
                        $$.bibase = *cp;
                        break;
                    }
            }

            if ($$.bibase == NULL && $$.base == NULL)
                yyerror("Unknown exception base type");
        }
    ;

raisecode:  TK_RAISECODE codeblock {
            $$ = $2;
        }
    ;

mappedtype: TK_MAPPEDTYPE basetype {
            if (notSkipping())
                currentMappedType = newMappedType(currentSpec,&$2);
        } mtdefinition
    ;

mappedtypetmpl: template TK_MAPPEDTYPE basetype {
            int a;

            if (currentSpec->genc)
                yyerror("%MappedType templates not allowed in a C module");

            /* Check the template arguments are all just simple names. */
            for (a = 0; a < $1.nrArgs; ++a)
                if ($1.args[a].atype != defined_type || $1.args[a].u.snd->next != NULL)
                    yyerror("%MappedType template arguments must be simple names");

            if ($3.atype != template_type)
                yyerror("%MappedType template must map a template type");

            if (notSkipping())
            {
                mappedTypeTmplDef *mtt;

                /* Check a template hasn't already been provided. */
                for (mtt = currentSpec->mappedtypetemplates; mtt != NULL; mtt = mtt->next)
                    if (sameScopedName(mtt->mt->type.u.td->fqname, $3.u.td->fqname) && sameTemplateSignature(&mtt->mt->type.u.td->types, &$3.u.td->types, TRUE))
                        yyerror("%MappedType template for this type has already been defined");

                $3.nrderefs = 0;
                $3.argflags = 0;

                mtt = sipMalloc(sizeof (mappedTypeTmplDef));

                mtt->sig = $1;
                mtt->mt = allocMappedType(&$3);
                mtt->next = currentSpec->mappedtypetemplates;

                currentSpec->mappedtypetemplates = mtt;

                currentMappedType = mtt->mt;
            }
        } mtdefinition
    ;

mtdefinition:   '{' mtbody '}' ';' {
            if (notSkipping())
            {
                if (currentMappedType->convfromcode == NULL)
                    yyerror("%MappedType must have a %ConvertFromTypeCode directive");

                if (currentMappedType->convtocode == NULL)
                    yyerror("%MappedType must have a %ConvertToTypeCode directive");

                currentMappedType = NULL;
            }
        }
    ;

mtbody:     mtline
    |   mtbody mtline
    ;

mtline:     typehdrcode {
            if (notSkipping())
                appendCodeBlock(&currentMappedType -> hdrcode,$1);
        }
    |   TK_FROMTYPE codeblock {
            if (notSkipping())
            {
                if (currentMappedType -> convfromcode != NULL)
                    yyerror("%MappedType has more than one %ConvertFromTypeCode directive");

                currentMappedType -> convfromcode = $2;
            }
        }
    |   TK_TOTYPE codeblock {
            if (notSkipping())
            {
                if (currentMappedType -> convtocode != NULL)
                    yyerror("%MappedType has more than one %ConvertToTypeCode directive");

                currentMappedType -> convtocode = $2;
            }
        }
    ;

namespace:  TK_NAMESPACE TK_NAME {
            if (currentSpec -> genc)
                yyerror("namespace definition not allowed in a C module");

            if (notSkipping())
            {
                classDef *ns;

                ns = newClass(currentSpec,namespace_iface,text2scopedName($2));

                pushScope(ns);

                sectionFlags = 0;
            }
        } '{' nsbody '}' ';' {
            if (inMainModule())
            {
                classDef *ns = currentScope();

                if (!isUsedName(ns->iff->name))
                {
                    varDef *vd;

                    for (vd = currentSpec->vars; vd != NULL; vd = vd->next)
                        if (vd->ecd == ns)
                        {
                            setIsUsedName(ns->iff->name);
                            break;
                        }
                }
            }

            if (notSkipping())
                popScope();
        }
    ;

nsbody:     nsstatement
    |   nsbody nsstatement
    ;

platforms:  TK_PLATFORMS {
            qualDef *qd;

            for (qd = currentModule -> qualifiers; qd != NULL; qd = qd -> next)
                if (qd -> qtype == platform_qualifier)
                    yyerror("%Platforms has already been defined for this module");
        }
        '{' platformlist '}' {
            qualDef *qd;
            int nrneeded;

            /*
             * Check that exactly one platform in the set was
             * requested.
             */

            nrneeded = 0;

            for (qd = currentModule -> qualifiers; qd != NULL; qd = qd -> next)
                if (qd -> qtype == platform_qualifier && isNeeded(qd))
                    ++nrneeded;

            if (nrneeded > 1)
                yyerror("No more than one of these %Platforms must be specified with the -t flag");
        }
    ;

platformlist:   platform
    |   platformlist platform
    ;

platform:   TK_NAME {
            newQualifier(currentModule,-1,-1,$1,platform_qualifier);
        }
    ;

feature:    TK_FEATURE TK_NAME {
            newQualifier(currentModule,-1,-1,$2,feature_qualifier);
        }
    ;

timeline:   TK_TIMELINE {
            currentTimelineOrder = 0;
        }
        '{' qualifierlist '}' {
            qualDef *qd;
            int nrneeded;

            /*
             * Check that exactly one time slot in the set was
             * requested.
             */

            nrneeded = 0;

            for (qd = currentModule -> qualifiers; qd != NULL; qd = qd -> next)
                if (qd -> qtype == time_qualifier && isNeeded(qd))
                    ++nrneeded;

            if (nrneeded > 1)
                yyerror("At most one of this %Timeline must be specified with the -t flag");

            currentModule -> nrtimelines++;
        }
    ;

qualifierlist:  qualifiername
    |   qualifierlist qualifiername
    ;

qualifiername:  TK_NAME {
            newQualifier(currentModule,currentModule -> nrtimelines,currentTimelineOrder++,$1,time_qualifier);
        }
    ;

ifstart:    TK_IF '(' qualifiers ')' {
            if (skipStackPtr >= MAX_NESTED_IF)
                yyerror("Internal error: increase the value of MAX_NESTED_IF");

            /* Nested %Ifs are implicit logical ands. */

            if (skipStackPtr > 0)
                $3 = ($3 && skipStack[skipStackPtr - 1]);

            skipStack[skipStackPtr++] = $3;
        }
    ;

oredqualifiers: TK_NAME {
            $$ = platOrFeature($1,FALSE);
        }
    |   '!' TK_NAME {
            $$ = platOrFeature($2,TRUE);
        }
    |   oredqualifiers TK_LOGICAL_OR TK_NAME {
            $$ = (platOrFeature($3,FALSE) || $1);
        }
    |   oredqualifiers TK_LOGICAL_OR '!' TK_NAME {
            $$ = (platOrFeature($4,TRUE) || $1);
        }
    ;

qualifiers: oredqualifiers
    |   optname '-' optname {
            $$ = timePeriod($1,$3);
        }
    ;

ifend:      TK_END {
            if (skipStackPtr-- <= 0)
                yyerror("Too many %End directives");
        }
    ;

license:    TK_LICENSE optflags {
            optFlag *of;

            if ($2.nrFlags == 0)
                yyerror("%License details not specified");

            if ((of = findOptFlag(&$2,"Type",string_flag)) == NULL)
                yyerror("%License type not specified");

            currentModule -> license = sipMalloc(sizeof (licenseDef));

            currentModule -> license -> type = of -> fvalue.sval;

            currentModule -> license -> licensee = 
                ((of = findOptFlag(&$2,"Licensee",string_flag)) != NULL)
                    ? of -> fvalue.sval : NULL;

            currentModule -> license -> timestamp = 
                ((of = findOptFlag(&$2,"Timestamp",string_flag)) != NULL)
                    ? of -> fvalue.sval : NULL;

            currentModule -> license -> sig = 
                ((of = findOptFlag(&$2,"Signature",string_flag)) != NULL)
                    ? of -> fvalue.sval : NULL;
        }
    ;

module:     modlang modname optnumber {
            /* Check the module hasn't already been defined. */

            moduleDef *mod;

            for (mod = currentSpec -> modules; mod != NULL; mod = mod -> next)
                if (mod->fullname != NULL && strcmp(mod->fullname, $2) == 0)
                    yyerror("Module is already defined");

            currentModule->fullname = $2;

            if ((currentModule->name = strrchr($2, '.')) != NULL)
                currentModule->name++;
            else
                currentModule->name = $2;

            currentModule -> version = $3;

            if (currentSpec -> genc < 0)
                currentSpec -> genc = $1;
            else if (currentSpec -> genc != $1)
                yyerror("Cannot mix C and C++ modules");
        }
    ;

modlang:    TK_MODULE {
            $$ = FALSE;
        }
    |   TK_CMODULE {
            $$ = TRUE;
        }
    ;

modname:    TK_NAME
    |   TK_PATHNAME {
            /*
             * The grammar design is a bit broken and this is the
             * easiest way to allow periods in module names.
             */

            char *cp;

            for (cp = $1; *cp != '\0'; ++cp)
                if (*cp != '.' && *cp != '_' && !isalnum(*cp))
                    yyerror("Invalid character in module name");

            $$ = $1;
        }
    ;

optnumber:  {
            $$ = -1;
        }
    |   TK_NUMBER
    ;

include:    TK_INCLUDE TK_PATHNAME {
            parseFile(NULL,$2,NULL,FALSE);
        }
    ;

optinclude: TK_OPTINCLUDE TK_PATHNAME {
            parseFile(NULL,$2,NULL,TRUE);
        }
    ;

import:     TK_IMPORT TK_PATHNAME {
            newImport($2);
        }
    ;

optaccesscode:  {
            $$ = NULL;
        }
    |   TK_ACCESSCODE codeblock {
            $$ = $2;
        }
    ;

optgetcode: {
            $$ = NULL;
        }
    |   TK_GETCODE codeblock {
            $$ = $2;
        }
    ;

optsetcode: {
            $$ = NULL;
        }
    |   TK_SETCODE codeblock {
            $$ = $2;
        }
    ;

copying:    TK_COPYING codeblock {
            if (inMainModule())
                appendCodeBlock(&currentSpec -> copying,$2);
        }
    ;

exphdrcode: TK_EXPHEADERCODE codeblock {
            $$ = $2;
        }
    ;

modhdrcode: TK_MODHEADERCODE codeblock {
            $$ = $2;
        }
    ;

typehdrcode:    TK_TYPEHEADERCODE codeblock {
            $$ = $2;
        }
    ;

opttypehdrcode: {
            $$ = NULL;
        }
    |   typehdrcode
    ;

travcode:   TK_TRAVERSECODE codeblock {
            $$ = $2;
        }
    ;

clearcode:  TK_CLEARCODE codeblock {
            $$ = $2;
        }
    ;

readbufcode:    TK_READBUFFERCODE codeblock {
            $$ = $2;
        }
    ;

writebufcode:   TK_WRITEBUFFERCODE codeblock {
            $$ = $2;
        }
    ;

segcountcode:   TK_SEGCOUNTCODE codeblock {
            $$ = $2;
        }
    ;

charbufcode:    TK_CHARBUFFERCODE codeblock {
            $$ = $2;
        }
    ;

modcode:    TK_MODCODE codeblock {
            $$ = $2;
        }
    ;

typecode:   TK_TYPECODE codeblock {
            $$ = $2;
        }
    ;

preinitcode:    TK_PREINITCODE codeblock {
            if (notSkipping() && inMainModule())
                appendCodeBlock(&currentSpec -> preinitcode,$2);
        }
    ;

postinitcode:   TK_POSTINITCODE codeblock {
            if (notSkipping() && inMainModule())
                appendCodeBlock(&currentSpec -> postinitcode,$2);
        }
    ;

unitcode:   TK_UNITCODE codeblock {
            if (notSkipping() && inMainModule())
                appendCodeBlock(&currentSpec->unitcode, $2);
        }
    ;

prepycode:  TK_PREPYCODE codeblock {
            /*
             * This is a no-op and is retained for compatibility
             * until the last use of it (by SIP v3) can be removed
             * from PyQt.
             */
        }
    ;

doc:        TK_DOC codeblock {
            if (inMainModule())
                appendCodeBlock(&currentSpec -> docs,$2);
        }
    ;

exporteddoc:    TK_EXPORTEDDOC codeblock {
            appendCodeBlock(&currentSpec -> docs,$2);
        }
    ;

makefile:   TK_MAKEFILE TK_PATHNAME optfilename codeblock {
            if (inMainModule())
                yywarning("%Makefile is ignored, please use the -b flag instead");
        }
    ;

codeblock:  codelines TK_END
    ;

codelines:  TK_CODELINE
    |   codelines TK_CODELINE {
            $$ = $1;

            append(&$$->frag, $2->frag);

            free($2->frag);
            free($2->filename);
            free($2);
        }
    ;

enum:       TK_ENUM optname optflags {
            if (notSkipping())
            {
                if (sectionFlags != 0 && (sectionFlags & ~(SECT_IS_PUBLIC | SECT_IS_PROT)) != 0)
                    yyerror("Class enums must be in the public or protected sections");

                currentEnum = newEnum(currentSpec,currentModule,$2,&$3,sectionFlags);
            }
        } '{' optenumbody '}' ';'
    ;

optfilename:    {
            $$ = NULL;
        }
    |   TK_PATHNAME {
            $$ = $1;
        }
    ;

optname:    {
            $$ = NULL;
        }
    |   TK_NAME {
            $$ = $1;
        }
    ;

optenumbody:
    |   enumbody
    ;

enumbody:   enumline
    |   enumbody enumline
    ;

enumline:   ifstart
    |   ifend
    |   TK_NAME optenumassign optflags optcomma {
            if (notSkipping())
            {
                /*
                 * Note that we don't use the assigned value.
                 * This is a hangover from when enums where
                 * generated in Python.  We can remove it when
                 * we have got around to updating all the .sip
                 * files.
                 */
                enumMemberDef *emd, **tail;

                emd = sipMalloc(sizeof (enumMemberDef));

                emd -> pyname = cacheName(currentSpec, getPythonName(&$3, $1));
                emd -> cname = $1;
                emd -> ed = currentEnum;
                emd -> next = NULL;

                checkAttributes(currentSpec,emd -> ed -> ecd,emd -> pyname -> text,FALSE);

                /* Append to preserve the order. */
                for (tail = &currentEnum->members; *tail != NULL; tail = &(*tail)->next)
                    ;

                *tail = emd;

                if (inMainModule())
                    setIsUsedName(emd -> pyname);
            }
        }
    ;

optcomma:
    |   ','
    ;

optenumassign:
    |   '=' value
    ;

optassign:  {
            $$ = NULL;
        }
    |   '=' expr {
            $$ = $2;
        }
    ;

expr:       value
    |   expr binop value {
            valueDef *vd;
 
            if ($1 -> vtype == string_value || $3 -> vtype == string_value)
                yyerror("Invalid binary operator for string");
 
            /* Find the last value in the existing expression. */
 
            for (vd = $1; vd -> next != NULL; vd = vd -> next)
                ;
 
            vd -> vbinop = $2;
            vd -> next = $3;

            $$ = $1;
        }
    ;

binop:      '-' {
            $$ = '-';
        }
    |   '+' {
            $$ = '+';
        }
    |   '*' {
            $$ = '*';
        }
    |   '/' {
            $$ = '/';
        }
    |   '&' {
            $$ = '&';
        }
    |   '|' {
            $$ = '|';
        }
    ;

optunop:    {
            $$ = '\0';
        }
    |   '!' {
            $$ = '!';
        }
    |   '~' {
            $$ = '~';
        }
    |   '-' {
            $$ = '-';
        }
    |   '+' {
            $$ = '+';
        }
    ;

value:      optunop simplevalue {
            if ($1 != '\0' && $2.vtype == string_value)
                yyerror("Invalid unary operator for string");
 
            /*
             * Convert the value to a simple expression on the
             * heap.
             */
 
            $$ = sipMalloc(sizeof (valueDef));
 
            *$$ = $2;
            $$ -> vunop = $1;
            $$ -> vbinop = '\0';
            $$ -> next = NULL;
        }
    ;

scopedname: scopepart
    |   scopedname TK_SCOPE scopepart {
            if (currentSpec -> genc)
                yyerror("Scoped names are not allowed in a C module");

            appendScopedName(&$1,$3);
        }
    ;

scopepart:  TK_NAME {
            $$ = text2scopePart($1);
        }
    ;

simplevalue:    scopedname {
            /*
             * We let the C++ compiler decide if the value is a
             * valid one - no point in building a full C++ parser
             * here.
             */

            $$.vtype = scoped_value;
            $$.u.vscp = $1;
        }
    |   basetype '(' exprlist ')' {
            fcallDef *fcd;

            fcd = sipMalloc(sizeof (fcallDef));
            *fcd = $3;
            fcd -> type = $1;

            $$.vtype = fcall_value;
            $$.u.fcd = fcd;
        }
    |   TK_REAL {
            $$.vtype = real_value;
            $$.u.vreal = $1;
        }
    |   TK_NUMBER {
            $$.vtype = numeric_value;
            $$.u.vnum = $1;
        }
    |   TK_TRUE {
            $$.vtype = numeric_value;
            $$.u.vnum = 1;
        }
    |   TK_FALSE {
            $$.vtype = numeric_value;
            $$.u.vnum = 0;
        }
    |   TK_NULL {
            $$.vtype = numeric_value;
            $$.u.vnum = 0;
        }
    |   TK_STRING {
            $$.vtype = string_value;
            $$.u.vstr = $1;
        }
    |   TK_QCHAR {
            $$.vtype = qchar_value;
            $$.u.vqchar = $1;
        }
    ;

exprlist:   {
            /* No values. */

            $$.nrArgs = 0;
        }
    |   expr {
            /* The single or first expression. */

            $$.args[0] = $1;
            $$.nrArgs = 1;
        }
    |   exprlist ',' expr {
            /* Check that it wasn't ...(,expression...). */

            if ($$.nrArgs == 0)
                yyerror("First argument to function call is missing");

            /* Check there is room. */

            if ($1.nrArgs == MAX_NR_ARGS)
                yyerror("Too many arguments to function call");

            $$ = $1;

            $$.args[$$.nrArgs] = $3;
            $$.nrArgs++;
        }
    ;

typedef:    TK_TYPEDEF cpptype TK_NAME ';' {
            if (notSkipping())
                newTypedef(currentSpec,currentModule,$3,&$2);
        }
    |   TK_TYPEDEF cpptype '(' deref TK_NAME ')' '(' cpptypelist ')' ';' {
            if (notSkipping())
            {
                argDef ftype;
                signatureDef *sig;

                /* Create the full signature on the heap. */
                sig = sipMalloc(sizeof (signatureDef));
                *sig = $8;
                sig -> result = $2;

                /* Create the full type. */
                ftype.atype = function_type;
                ftype.argflags = 0;
                ftype.nrderefs = $4;
                ftype.defval = NULL;
                ftype.u.sa = sig;

                newTypedef(currentSpec,currentModule,$5,&ftype);
            }
        }
    ;

struct:     TK_STRUCT TK_NAME {
            if (notSkipping())
            {
                classDef *cd;

                cd = newClass(currentSpec,class_iface,text2scopedName($2));

                pushScope(cd);

                sectionFlags = SECT_IS_PUBLIC;
            }
        } optflags '{' classbody '}' ';' {
            if (notSkipping())
            {
                finishClass(currentSpec, currentModule, currentScope(), &$4);
                popScope();
            }
        }
    ;

classtmpl:  template class {
            if (currentSpec->genc)
                yyerror("Class templates not allowed in a C module");

            if (notSkipping())
            {
                classTmplDef *tcd;

                /*
                 * Make sure there is room for the extra class
                 * name argument.
                 */
                if ($1.nrArgs == MAX_NR_ARGS)
                    yyerror("Internal error - increase the value of MAX_NR_ARGS");

                tcd = sipMalloc(sizeof (classTmplDef));
                tcd->sig = $1;
                tcd->cd = $2;
                tcd->next = currentSpec->classtemplates;

                currentSpec->classtemplates = tcd;
            }
        }
    ;

template:   TK_TEMPLATE '<' cpptypelist '>' {
            $$ = $3;
        }
    ;

class:      TK_CLASS scopedname {
            if (currentSpec -> genc)
                yyerror("Class definition not allowed in a C module");

            if (notSkipping())
            {
                classDef *cd;

                cd = newClass(currentSpec, class_iface, scopeScopedName($2));

                pushScope(cd);

                sectionFlags = SECT_IS_PRIVATE;
            }
        } superclasses optflags optclassbody ';' {
            if (notSkipping())
            {
                classDef *cd = currentScope();

                /*
                 * See if the class was defined or just
                 * declared.
                 */
                if ($6)
                {
                    if ($2->next != NULL)
                        yyerror("A scoped name cannot be given in a class definition");

                }
                else if (cd->supers != NULL)
                    yyerror("Class has super-classes but no definition");
                else
                    setIsOpaque(cd);

                finishClass(currentSpec, currentModule, cd, &$5);
                popScope();

                /*
                 * Check that external classes have only been
                 * declared at the global scope.
                 */
                if (isExternal(cd) && currentScope() != NULL)
                    yyerror("External classes can only be declared in the global scope");

                $$ = cd;
            }
        }
    ;

superclasses:
    |   ':' superlist
    ;

superlist:  superclass
    |   superlist ',' superclass 
    ;

superclass: scopedname {
            if (notSkipping())
            {
                classDef *cd, *super;

                cd = currentScope();

                super = findClass(currentSpec,class_iface,$1);

                appendToClassList(&cd -> supers,super);
                addToUsedList(&cd->iff->used, super->iff);
            }
        }
    ;

optclassbody:   {
            $$ = FALSE;
        }
    |   '{' classbody '}' {
            $$ = TRUE;
        }
    ;

classbody:  classline
    |   classbody classline
    ;

classline:  ifstart
    |   ifend
    |   namespace
    |   struct
    |   class
    |   exception
    |   typedef
    |   enum
    |   typecode {
            if (notSkipping())
                appendCodeBlock(&currentScope() -> cppcode,$1);
        }
    |   typehdrcode {
            if (notSkipping())
                appendCodeBlock(&currentScope() -> hdrcode,$1);
        }
    |   travcode {
            if (currentScope()->travcode != NULL)
                yyerror("%GCTraverseCode already given for class");

            if (notSkipping())
                currentScope()->travcode = $1;
        }
    |   clearcode {
            if (currentScope()->clearcode != NULL)
                yyerror("%GCClearCode already given for class");

            if (notSkipping())
                currentScope()->clearcode = $1;
        }
    |   readbufcode {
            if (currentScope()->readbufcode != NULL)
                yyerror("%BIGetReadBufferCode already given for class");

            if (notSkipping())
                currentScope()->readbufcode = $1;
        }
    |   writebufcode {
            if (currentScope()->writebufcode != NULL)
                yyerror("%BIGetWriteBufferCode already given for class");

            if (notSkipping())
                currentScope()->writebufcode = $1;
        }
    |   segcountcode {
            if (currentScope()->segcountcode != NULL)
                yyerror("%BIGetSegCountCode already given for class");

            if (notSkipping())
                currentScope()->segcountcode = $1;
        }
    |   charbufcode {
            if (currentScope()->charbufcode != NULL)
                yyerror("%BIGetCharBufferCode already given for class");

            if (notSkipping())
                currentScope()->charbufcode = $1;
        }
    |   ctor
    |   dtor
    |   varmember
    |   TK_TOSUBCLASS codeblock {
            if (notSkipping())
            {
                classDef *cd = currentScope();

                if (cd -> convtosubcode != NULL)
                    yyerror("Class has more than one %ConvertToSubClassCode directive");

                cd -> convtosubcode = $2;
            }
        }
    |   TK_TOTYPE codeblock {
            if (notSkipping())
            {
                classDef *cd = currentScope();

                if (cd -> convtocode != NULL)
                    yyerror("Class has more than one %ConvertToTypeCode directive");

                cd -> convtocode = $2;
            }
        }
    |   TK_PUBLIC optslot ':' {
            if (currentSpec -> genc)
                yyerror("public section not allowed in a C module");

            if (notSkipping())
                sectionFlags = SECT_IS_PUBLIC | $2;
        }
    |   TK_PROTECTED optslot ':' {
            if (currentSpec -> genc)
                yyerror("protected section not allowed in a C module");

            if (notSkipping())
                sectionFlags = SECT_IS_PROT | $2;
        }
    |   TK_PRIVATE optslot ':' {
            if (currentSpec -> genc)
                yyerror("private section not allowed in a C module");

            if (notSkipping())
                sectionFlags = SECT_IS_PRIVATE | $2;
        }
    |   TK_SIGNALS ':' {
            if (currentSpec -> genc)
                yyerror("signals section not allowed in a C module");

            if (notSkipping())
                sectionFlags = SECT_IS_SIGNAL;
        }
    ;

optslot:    {
            $$ = 0;
        }
    |   TK_SLOTS {
            $$ = SECT_IS_SLOT;
        }
    ;

dtor:       optvirtual '~' TK_NAME '(' ')' optexceptions optabstract optflags ';' methodcode virtualcatchercode {
            /* Note that we allow non-virtual dtors in C modules. */

            if (notSkipping())
            {
                classDef *cd = currentScope();

                if (strcmp(classBaseName(cd),$3) != 0)
                    yyerror("Destructor doesn't have the same name as its class");

                if (isDtor(cd))
                    yyerror("Destructor has already been defined");

                if (currentSpec -> genc && $10 == NULL)
                    yyerror("Destructor in C modules must include %MethodCode");

                cd -> dealloccode = $10;
                cd -> dtorcode = $11;
                cd -> dtorexceptions = $6;
                cd -> classflags |= sectionFlags;

                if ($7)
                {
                    if (!$1)
                        yyerror("Abstract destructor must be virtual");

                    setIsAbstractClass(cd);
                }

                /*
                 * The class has a shadow if we have a virtual dtor or some
                 * dtor code.
                 */
                if ($1 || $11 != NULL)
                {
                    if (currentSpec -> genc)
                        yyerror("Virtual destructor or %VirtualCatcherCode not allowed in a C module");

                    setHasShadow(cd);
                }

                if (getReleaseGIL(&$8))
                    setIsReleaseGILDtor(cd);
                else if (getHoldGIL(&$8))
                    setIsHoldGILDtor(cd);
            }
        }
    ;

ctor:       TK_EXPLICIT {currentCtorIsExplicit = TRUE;} simplector
    |   simplector
    ;

simplector: TK_NAME '(' arglist ')' optexceptions optflags optctorsig ';' methodcode {
            /* Note that we allow ctors in C modules. */

            if (notSkipping())
            {
                if (currentSpec -> genc)
                {
                    if ($9 == NULL && $3.nrArgs != 0)
                        yyerror("Constructors with arguments in C modules must include %MethodCode");

                    if (currentCtorIsExplicit)
                        yyerror("Explicit constructors not allowed in a C module");
                }

                if ((sectionFlags & (SECT_IS_PUBLIC | SECT_IS_PROT | SECT_IS_PRIVATE)) == 0)
                    yyerror("Constructor must be in the public, private or protected sections");

                newCtor($1,sectionFlags,&$3,&$6,$9,$5,$7,currentCtorIsExplicit);
            }

            free($1);

            currentCtorIsExplicit = FALSE;
        }
    ;

optctorsig: {
            $$ = NULL;
        }
    |   '[' '(' arglist ')' ']' {
            $$ = sipMalloc(sizeof (signatureDef));

            *$$ = $3;
        }
    ;

optsig: {
            $$ = NULL;
        }
    |   '[' cpptype '(' arglist ')' ']' {
            $$ = sipMalloc(sizeof (signatureDef));

            *$$ = $4;
            $$ -> result = $2;
        }
    ;

optvirtual: {
            $$ = FALSE;
        }
    |   TK_VIRTUAL {
            $$ = TRUE;
        }
    ;

function:   cpptype TK_NAME '(' arglist ')' optconst optexceptions optabstract optflags optsig ';' methodcode virtualcatchercode {
            if (notSkipping())
            {
                if (sectionFlags != 0 && (sectionFlags & (SECT_IS_PUBLIC | SECT_IS_PROT | SECT_IS_PRIVATE | SECT_IS_SLOT | SECT_IS_SIGNAL)) == 0)
                    yyerror("Class function must be in the public, private, protected, slot or signal sections");

                $4.result = $1;

                newFunction(currentSpec,currentModule,
                        sectionFlags,currentIsStatic,
                        currentOverIsVirt,
                        $2,&$4,$6,$8,&$9,$12,$13,$7,$10);
            }

            currentIsStatic = FALSE;
            currentOverIsVirt = FALSE;
        }
    |   cpptype TK_OPERATOR operatorname '(' arglist ')' optconst optexceptions optabstract optflags optsig ';' methodcode virtualcatchercode {
            if (notSkipping())
            {
                classDef *cd = currentScope();

                /* Handle the unary '+' and '-' operators. */
                if ((cd != NULL && $5.nrArgs == 0) || (cd == NULL && $5.nrArgs == 1))
                {
                    if (strcmp($3, "__add__") == 0)
                        $3 = "__pos__";
                    else if (strcmp($3, "__sub__") == 0)
                        $3 = "__neg__";
                }

                $5.result = $1;

                newFunction(currentSpec,currentModule,
                        sectionFlags,currentIsStatic,
                        currentOverIsVirt,
                        $3,&$5,$7,$9,&$10,$13,$14,$8,$11);
            }

            currentIsStatic = FALSE;
            currentOverIsVirt = FALSE;
        }
    |   TK_OPERATOR cpptype '(' arglist ')' optconst optexceptions optabstract optflags optsig ';' methodcode virtualcatchercode {
            classDef *scope = currentScope();

            if (scope == NULL || $4.nrArgs != 0)
                yyerror("Operator casts must be specified in a class and have no arguments");


            if (notSkipping())
            {
                char *sname;

                switch ($2.atype)
                {
                case defined_type:
                    sname = NULL;
                    break;

                case bool_type:
                case cbool_type:
                case short_type:
                case ushort_type:
                case int_type:
                case cint_type:
                case uint_type:
                    sname = "__int__";
                    break;

                case long_type:
                case ulong_type:
                case longlong_type:
                case ulonglong_type:
                    sname = "__long__";
                    break;

                case float_type:
                case cfloat_type:
                case double_type:
                case cdouble_type:
                    sname = "__float__";
                    break;

                default:
                    yyerror("Unsupported operator cast");
                }

                if (sname != NULL)
                {
                    $4.result = $2;

                    newFunction(currentSpec, currentModule,
                            sectionFlags,
                            currentIsStatic,
                            currentOverIsVirt, sname,
                            &$4, $6, $8, &$9, $12, $13,
                            $7, $10);
                }
                else
                {
                    argList *al;

                    /* Check it doesn't already exist. */
                    for (al = scope->casts; al != NULL; al = al->next)
                        if (sameScopedName($2.u.snd, al->arg.u.snd))
                            yyerror("This operator cast has already been specified in this class");

                    al = sipMalloc(sizeof (argList));
                    al->arg = $2;
                    al->next = scope->casts;

                    scope->casts = al;
                }
            }

            currentIsStatic = FALSE;
            currentOverIsVirt = FALSE;
        }
    ;

operatorname:   '+'     {$$ = "__add__";}
    |   '-'     {$$ = "__sub__";}
    |   '*'     {$$ = "__mul__";}
    |   '/'     {$$ = "__div__";}
    |   '%'     {$$ = "__mod__";}
    |   '&'     {$$ = "__and__";}
    |   '|'     {$$ = "__or__";}
    |   '^'     {$$ = "__xor__";}
    |   '<' '<'     {$$ = "__lshift__";}
    |   '>' '>'     {$$ = "__rshift__";}
    |   '+' '='     {$$ = "__iadd__";}
    |   '-' '='     {$$ = "__isub__";}
    |   '*' '='     {$$ = "__imul__";}
    |   '/' '='     {$$ = "__idiv__";}
    |   '%' '='     {$$ = "__imod__";}
    |   '&' '='     {$$ = "__iand__";}
    |   '|' '='     {$$ = "__ior__";}
    |   '^' '='     {$$ = "__ixor__";}
    |   '<' '<' '=' {$$ = "__ilshift__";}
    |   '>' '>' '=' {$$ = "__irshift__";}
    |   '~'     {$$ = "__invert__";}
    |   '(' ')'     {$$ = "__call__";}
    |   '[' ']'     {$$ = "__getitem__";}
    |   '<'     {$$ = "__lt__";}
    |   '<' '='     {$$ = "__le__";}
    |   '=' '='     {$$ = "__eq__";}
    |   '!' '='     {$$ = "__ne__";}
    |   '>'     {$$ = "__gt__";}
    |   '>' '='     {$$ = "__ge__";}
    ;

optconst:   {
            $$ = FALSE;
        }
    |   TK_CONST {
            $$ = TRUE;
        }
    ;

optabstract:    {
            $$ = 0;
        }
    |   '=' TK_NUMBER {
            if ($2 != 0)
                yyerror("Abstract virtual function '= 0' expected");

            $$ = TRUE;
        }
    ;

optflags:   {
            $$.nrFlags = 0;
        }
    |   '/' flaglist '/' {
            $$ = $2;
        }
    ;


flaglist:   flag {
            $$.flags[0] = $1;
            $$.nrFlags = 1;
        }
    |   flaglist ',' flag {
            /* Check there is room. */

            if ($1.nrFlags == MAX_NR_FLAGS)
                yyerror("Too many optional flags");

            $$ = $1;

            $$.flags[$$.nrFlags++] = $3;
        }
    ;

flag:       TK_NAME {
            $$.ftype = bool_flag;
            $$.fname = $1;
        }
    |   TK_NAME '=' flagvalue {
            $$ = $3;
            $$.fname = $1;
        }
    ;

flagvalue:  TK_NAME {
            $$.ftype = name_flag;
            $$.fvalue.sval = $1;
        }
    |   TK_STRING {
            $$.ftype = string_flag;
            $$.fvalue.sval = $1;
        }
    |   TK_NUMBER {
            $$.ftype = integer_flag;
            $$.fvalue.ival = $1;
        }
    ;

methodcode: {
            $$ = NULL;
        }
    |   TK_METHODCODE codeblock {
            $$ = $2;
        }
    ;

virtualcatchercode: {
            $$ = NULL;
        }
    |   TK_VIRTUALCATCHERCODE codeblock {
            $$ = $2;
        }
    ;

arglist:    rawarglist {
            int a, nrrxcon, nrrxdis, nrslotcon, nrslotdis, nrarray, nrarraysize;

            nrrxcon = nrrxdis = nrslotcon = nrslotdis = nrarray = nrarraysize = 0;

            for (a = 0; a < $1.nrArgs; ++a)
            {
                argDef *ad = &$1.args[a];

                switch (ad -> atype)
                {
                case rxcon_type:
                    ++nrrxcon;
                    break;

                case rxdis_type:
                    ++nrrxdis;
                    break;

                case slotcon_type:
                    ++nrslotcon;
                    break;

                case slotdis_type:
                    ++nrslotdis;
                    break;
                }

                if (isArray(ad))
                    ++nrarray;

                if (isArraySize(ad))
                    ++nrarraysize;
            }

            if (nrrxcon != nrslotcon || nrrxcon > 1)
                yyerror("SIP_RXOBJ_CON and SIP_SLOT_CON must both be given and at most once");

            if (nrrxdis != nrslotdis || nrrxdis > 1)
                yyerror("SIP_RXOBJ_DIS and SIP_SLOT_DIS must both be given and at most once");

            if (nrarray != nrarraysize || nrarray > 1)
                yyerror("/Array/ and /ArraySize/ must both be given and at most once");

            $$ = $1;
        }
    ;

rawarglist: {
            /* No arguments. */

            $$.nrArgs = 0;
        }
    |   argvalue {
            /* The single or first argument. */

            $$.args[0] = $1;
            $$.nrArgs = 1;
        }
    |   rawarglist ',' argvalue {
            /* Check that it wasn't ...(,arg...). */
            if ($1.nrArgs == 0)
                yyerror("First argument of the list is missing");

            /* Check there is nothing after an ellipsis. */
            if ($1.args[$1.nrArgs - 1].atype == ellipsis_type)
                yyerror("An ellipsis must be at the end of the argument list");

            /*
             * If this argument has no default value, then the
             * previous one mustn't either.
             */
            if ($3.defval == NULL && $1.args[$1.nrArgs - 1].defval != NULL)
                yyerror("Compulsory argument given after optional argument");

            /* Check there is room. */
            if ($1.nrArgs == MAX_NR_ARGS)
                yyerror("Internal error - increase the value of MAX_NR_ARGS");

            $$ = $1;

            $$.args[$$.nrArgs] = $3;
            $$.nrArgs++;
        }
    ;

argvalue:   TK_SIPSIGNAL optname optassign {
            $$.atype = signal_type;
            $$.argflags = ARG_IS_CONST;
            $$.nrderefs = 0;
            $$.name = $2;
            $$.defval = $3;

            currentSpec -> sigslots = TRUE;
        }
    |   TK_SIPSLOT optname optassign {
            $$.atype = slot_type;
            $$.argflags = ARG_IS_CONST;
            $$.nrderefs = 0;
            $$.name = $2;
            $$.defval = $3;

            currentSpec -> sigslots = TRUE;
        }
    |   TK_SIPANYSLOT optname optassign {
            $$.atype = anyslot_type;
            $$.argflags = ARG_IS_CONST;
            $$.nrderefs = 0;
            $$.name = $2;
            $$.defval = $3;

            currentSpec -> sigslots = TRUE;
        }
    |   TK_SIPRXCON optname {
            $$.atype = rxcon_type;
            $$.argflags = 0;
            $$.nrderefs = 0;
            $$.name = $2;

            currentSpec -> sigslots = TRUE;
        }
    |   TK_SIPRXDIS optname {
            $$.atype = rxdis_type;
            $$.argflags = 0;
            $$.nrderefs = 0;
            $$.name = $2;

            currentSpec -> sigslots = TRUE;
        }
    |   TK_SIPSLOTCON '(' arglist ')' optname {
            $$.atype = slotcon_type;
            $$.argflags = ARG_IS_CONST;
            $$.nrderefs = 0;
            $$.name = $5;

            $3.result.atype = void_type;
            $3.result.argflags = 0;
            $3.result.nrderefs = 0;

            $$.u.sa = sipMalloc(sizeof (signatureDef));
            *$$.u.sa = $3;

            currentSpec -> sigslots = TRUE;
        }
    |   TK_SIPSLOTDIS '(' arglist ')' optname {
            $$.atype = slotdis_type;
            $$.argflags = ARG_IS_CONST;
            $$.nrderefs = 0;
            $$.name = $5;

            $3.result.atype = void_type;
            $3.result.argflags = 0;
            $3.result.nrderefs = 0;

            $$.u.sa = sipMalloc(sizeof (signatureDef));
            *$$.u.sa = $3;

            currentSpec -> sigslots = TRUE;
        }
    |   TK_QOBJECT optname {
            $$.atype = qobject_type;
            $$.argflags = 0;
            $$.nrderefs = 0;
            $$.name = $2;
        }
    |   argtype optassign {
            $$ = $1;
            $$.defval = $2;
        }
    ;

varmember:  TK_STATIC {currentIsStatic = TRUE;} varmem
    |   varmem
    ;

varmem:     member
    |   variable
    ;

member:     TK_VIRTUAL {currentOverIsVirt = TRUE;} function
    |   function
    ;

variable:   cpptype TK_NAME optflags ';' optaccesscode optgetcode optsetcode {
            if (notSkipping())
            {
                /* Check the section. */

                if (sectionFlags != 0)
                {
                    if ((sectionFlags & SECT_IS_PUBLIC) == 0)
                        yyerror("Class variables must be in the public section");

                    if (!currentIsStatic && $5 != NULL)
                        yyerror("%AccessCode cannot be specified for non-static class variables");
                }

                if (currentIsStatic && currentSpec -> genc)
                    yyerror("Cannot have static members in a C structure");

                if ($6 != NULL || $7 != NULL)
                {
                    if ($5 != NULL)
                        yyerror("Cannot mix %AccessCode and %GetCode or %SetCode");

                    if (currentScope() == NULL)
                        yyerror("Cannot specify %GetCode or %SetCode for global variables");
                }

                newVar(currentSpec,currentModule,$2,currentIsStatic,&$1,&$3,$5,$6,$7);
            }

            currentIsStatic = FALSE;
        }
    ;

cpptype:    TK_CONST basetype deref optref {
            $$ = $2;
            $$.nrderefs = $3;
            $$.argflags = ARG_IS_CONST | $4;
            $$.name = NULL;
        }
    |   basetype deref optref {
            $$ = $1;
            $$.nrderefs = $2;
            $$.argflags = $3;
            $$.name = NULL;
        }
    ;

argtype:    cpptype optname optflags {
            $$ = $1;
            $$.name = $2;

            if (findOptFlag(&$3,"AllowNone",bool_flag) != NULL)
                $$.argflags |= ARG_ALLOW_NONE;

            if (findOptFlag(&$3,"GetWrapper",bool_flag) != NULL)
                $$.argflags |= ARG_GET_WRAPPER;

            if (findOptFlag(&$3,"Array",bool_flag) != NULL)
                $$.argflags |= ARG_ARRAY;

            if (findOptFlag(&$3,"ArraySize",bool_flag) != NULL)
                $$.argflags |= ARG_ARRAY_SIZE;

            if (findOptFlag(&$3,"Transfer",bool_flag) != NULL)
                $$.argflags |= ARG_XFERRED;

            if (findOptFlag(&$3,"TransferThis",bool_flag) != NULL)
                $$.argflags |= ARG_THIS_XFERRED;

            if (findOptFlag(&$3,"TransferBack",bool_flag) != NULL)
                $$.argflags |= ARG_XFERRED_BACK;

            if (findOptFlag(&$3,"In",bool_flag) != NULL)
                $$.argflags |= ARG_IN;

            if (findOptFlag(&$3,"Out",bool_flag) != NULL)
                $$.argflags |= ARG_OUT;

            if (findOptFlag(&$3,"Constrained",bool_flag) != NULL)
            {
                $$.argflags |= ARG_CONSTRAINED;

                switch ($$.atype)
                {
                case bool_type:
                    $$.atype = cbool_type;
                    break;

                case int_type:
                    $$.atype = cint_type;
                    break;

                case float_type:
                    $$.atype = cfloat_type;
                    break;

                case double_type:
                    $$.atype = cdouble_type;
                    break;
                }
            }
        }
    ;

optref:     {
            $$ = 0;
        }
    |   '&' {
            if (currentSpec -> genc)
                yyerror("References not allowed in a C module");

            $$ = ARG_IS_REF;
        }
    ;

deref:      {
            $$ = 0;
        }
    |   deref '*' {
            $$ = $1 + 1;
        }
    ;

basetype:   scopedname {
            $$.atype = defined_type;
            $$.u.snd = $1;
        }
    |   scopedname '<' cpptypelist '>' {
            templateDef *td;

            td = sipMalloc(sizeof(templateDef));
            td -> fqname = $1;
            td -> types = $3;

            $$.atype = template_type;
            $$.u.td = td;
        }
    |   TK_STRUCT scopedname {
            /* In a C module all structures must be defined. */
            if (currentSpec -> genc)
            {
                $$.atype = defined_type;
                $$.u.snd = $2;
            }
            else
            {
                $$.atype = struct_type;
                $$.u.sname = $2;
            }
        }
    |   TK_UNSIGNED TK_SHORT {
            $$.atype = ushort_type;
        }
    |   TK_SHORT {
            $$.atype = short_type;
        }
    |   TK_UNSIGNED {
            $$.atype = uint_type;
        }
    |   TK_UNSIGNED TK_INT {
            $$.atype = uint_type;
        }
    |   TK_INT {
            $$.atype = int_type;
        }
    |   TK_LONG {
            $$.atype = long_type;
        }
    |   TK_UNSIGNED TK_LONG {
            $$.atype = ulong_type;
        }
    |   TK_LONG TK_LONG {
            $$.atype = longlong_type;
        }
    |   TK_UNSIGNED TK_LONG TK_LONG {
            $$.atype = ulonglong_type;
        }
    |   TK_FLOAT {
            $$.atype = float_type;
        }
    |   TK_DOUBLE {
            $$.atype = double_type;
        }
    |   TK_BOOL {
            $$.atype = bool_type;
        }
    |   TK_SIGNED TK_CHAR {
            $$.atype = sstring_type;
        }
    |   TK_UNSIGNED TK_CHAR {
            $$.atype = ustring_type;
        }
    |   TK_CHAR {
            $$.atype = string_type;
        }
    |   TK_WCHAR_T {
            $$.atype = wstring_type;
        }
    |   TK_VOID {
            $$.atype = void_type;
        }
    |   TK_PYOBJECT {
            $$.atype = pyobject_type;
        }
    |   TK_PYTUPLE {
            $$.atype = pytuple_type;
        }
    |   TK_PYLIST {
            $$.atype = pylist_type;
        }
    |   TK_PYDICT {
            $$.atype = pydict_type;
        }
    |   TK_PYCALLABLE {
            $$.atype = pycallable_type;
        }
    |   TK_PYSLICE {
            $$.atype = pyslice_type;
        }
    |   TK_PYTYPE {
            $$.atype = pytype_type;
        }
    |   TK_ELLIPSIS {
            $$.atype = ellipsis_type;
        }
    ;

cpptypelist:    cpptype {
            /* The single or first type. */

            $$.args[0] = $1;
            $$.nrArgs = 1;
        }
    |   cpptypelist ',' cpptype {
            /* Check there is nothing after an ellipsis. */
            if ($1.args[$1.nrArgs - 1].atype == ellipsis_type)
                yyerror("An ellipsis must be at the end of the argument list");

            /* Check there is room. */
            if ($1.nrArgs == MAX_NR_ARGS)
                yyerror("Internal error - increase the value of MAX_NR_ARGS");

            $$ = $1;

            $$.args[$$.nrArgs] = $3;
            $$.nrArgs++;
        }
    ;

optexceptions:  {
            $$ = NULL;
        }
    |   TK_THROW '(' exceptionlist ')' {
            if (currentSpec->genc)
                yyerror("Exceptions not allowed in a C module");

            if (notSkipping() && inMainModule())
            {
                int e;
                ifaceFileList **ifl;

                /*
                 * Make sure the exceptions' header files are
                 * included.  We unconditionally mark them to
                 * be included in the current scope's header
                 * file to save us the effort of checking if
                 * they are being used with a protected method,
                 * a virtual or a signal.
                 */
                ifl = (currentScope() != NULL) ? &currentScope()->iff->used : &currentSpec->used;

                for (e = 0; e < $3->nrArgs; ++e)
                    addToUsedList(ifl, $3->args[e]->iff);
            }

            $$ = $3;
        }
    ;

exceptionlist:  {
            /* Empty list so use a blank. */

            $$ = sipMalloc(sizeof (throwArgs));
            $$ -> nrArgs = 0;
        }
    |   scopedname {
            /* The only or first exception. */

            $$ = sipMalloc(sizeof (throwArgs));
            $$ -> nrArgs = 1;
            $$ -> args[0] = findException(currentSpec, $1, FALSE);
        }
    |   exceptionlist ',' scopedname {
            /* Check that it wasn't ...(,arg...). */

            if ($1 -> nrArgs == 0)
                yyerror("First exception of throw specifier is missing");

            /* Check there is room. */

            if ($1 -> nrArgs == MAX_NR_ARGS)
                yyerror("Internal error - increase the value of MAX_NR_ARGS");

            $$ = $1;
            $$ -> args[$$ -> nrArgs++] = findException(currentSpec, $3, FALSE);
        }
    ;

%%


/*
 * Parse the specification.
 */
void parse(sipSpec *spec,FILE *fp,char *filename,stringList *tsl,
       stringList *xfl)
{
    classTmplDef *tcd;

        /* Initialise the spec. */
 
    spec -> modules = NULL;
    spec -> namecache = NULL;
    spec -> ifacefiles = NULL;
    spec -> classes = NULL;
    spec -> classtemplates = NULL;
    spec -> proxies = NULL;
    spec -> exceptions = NULL;
    spec -> mappedtypes = NULL;
    spec -> mappedtypetemplates = NULL;
    spec -> qobjclass = -1;
    spec -> enums = NULL;
    spec -> vars = NULL;
    spec -> othfuncs = NULL;
    spec -> overs = NULL;
    spec -> typedefs = NULL;
    spec -> copying = NULL;
    spec -> exphdrcode = NULL;
    spec -> hdrcode = NULL;
    spec -> cppcode = NULL;
    spec -> docs = NULL;
    spec -> preinitcode = NULL;
    spec -> postinitcode = NULL;
    spec -> unitcode = NULL;
    spec -> used = NULL;
    spec -> sigslots = FALSE;
    spec -> genc = -1;
    spec -> options = NULL;

    currentSpec = spec;
    neededQualifiers = tsl;
    excludedQualifiers = xfl;
    currentModule = NULL;
    currentMappedType = NULL;
    currentOverIsVirt = FALSE;
    currentCtorIsExplicit = FALSE;
    currentIsStatic = FALSE;
    previousFile = NULL;
    skipStackPtr = 0;
    currentScopeIdx = 0;
    sectionFlags = 0;

    newModule(fp,filename);
    spec -> module = currentModule;

    yyparse();

    handleEOF();
    handleEOM();

    /*
     * Go through each template class and remove it from the list of
     * classes.
     */
    for (tcd = spec->classtemplates; tcd != NULL; tcd = tcd->next)
    {
        classDef **cdp;

        for (cdp = &spec->classes; *cdp != NULL; cdp = &(*cdp)->next)
            if (*cdp == tcd->cd)
            {
                ifaceFileDef **ifdp;

                /* Remove the interface file as well. */
                for (ifdp = &spec->ifacefiles; *ifdp != NULL; ifdp = &(*ifdp)->next)
                    if (*ifdp == tcd->cd->iff)
                    {
                        *ifdp = (*ifdp)->next;
                        break;
                    }

                *cdp = (*cdp)->next;
                break;
            }
    }
}


/*
 * Tell the parser that a complete file has now been read.
 */
void parserEOF(char *name,parserContext *pc)
{
    previousFile = sipStrdup(name);
    newContext = *pc;
}


/*
 * Append a class definition to a class list if it doesn't already appear.
 * Append is needed specifically for the list of super-classes because the
 * order is important to Python.
 */
void appendToClassList(classList **clp,classDef *cd)
{
    classList *new;

    /* Find the end of the list. */

    while (*clp != NULL)
    {
        if ((*clp) -> cd == cd)
            return;

        clp = &(*clp) -> next;
    }

    new = sipMalloc(sizeof (classList));

    new -> cd = cd;
    new -> next = NULL;

    *clp = new;
}


/*
 * Create a new module for the current specification and make it current.
 */
static void newModule(FILE *fp,char *filename)
{
    moduleDef *newmod;

    parseFile(fp,filename,currentModule,FALSE);

    newmod = sipMalloc(sizeof (moduleDef));
    newmod -> fullname = NULL;
    newmod -> name = NULL;
    newmod -> version = -1;
    newmod -> modflags = 0;
    newmod -> modulenr = -1;
    newmod -> file = filename;
    newmod -> qualifiers = NULL;
    newmod -> root.cd = NULL;
    newmod -> root.child = NULL;
    newmod -> nrtimelines = 0;
    newmod -> nrclasses = 0;
    newmod -> nrexceptions = 0;
    newmod -> nrmappedtypes = 0;
    newmod -> nrenums = 0;
    newmod -> nrtypedefs = 0;
    newmod -> nrvirthandlers = 0;
    newmod -> virthandlers = NULL;
    newmod -> license = NULL;
    newmod -> allimports = NULL;
    newmod -> imports = NULL;
    newmod -> next = currentSpec -> modules;

    currentModule = currentSpec->modules = newmod;
}


/*
 * Switch to parsing a new file.
 */
static void parseFile(FILE *fp,char *name,moduleDef *prevmod,int optional)
{
    parserContext pc;

    pc.ifdepth = skipStackPtr;
    pc.prevmod = prevmod;

    setInputFile(fp,name,&pc,optional);
}


/*
 * Find an interface file, or create a new one.
 */
ifaceFileDef *findIfaceFile(sipSpec *pt, moduleDef *mod, scopedNameDef *fqname,
                ifaceFileType iftype, argDef *ad)
{
    ifaceFileDef *iff;

    /* See if the name is already used. */

    for (iff = pt -> ifacefiles; iff != NULL; iff = iff -> next)
    {
        if (!sameScopedName(iff -> fqcname,fqname))
            continue;

        /*
         * They must be the same type except that we allow a class if
         * if we want an exception.  This is because we allow classes
         * to be used before they are defined.
         */
        if (iff -> type != iftype)
            if (iftype != exception_iface || iff -> type != class_iface)
                yyerror("A class, exception, namespace or mapped type has already been defined with the same name");

        /* Ignore an external class declared in another module. */
        if (iftype == class_iface && iff->module != mod)
        {
            classDef *cd;

            for (cd = pt->classes; cd != NULL; cd = cd->next)
                if (cd->iff == iff)
                    break;

            if (cd != NULL && iff->module != NULL && isExternal(cd))
                continue;
        }

        /*
         * If this is a mapped type with the same name defined in a
         * different module, then check that this type isn't the same
         * as any of the mapped types defined in that module.
         */
        if (iftype == mappedtype_iface && iff -> module != mod)
        {
            mappedTypeDef *mtd;

            for (mtd = pt -> mappedtypes; mtd != NULL; mtd = mtd -> next)
            {
                if (mtd -> iff != iff)
                    continue;

                if (ad -> atype != template_type ||
                    mtd -> type.atype != template_type ||
                    sameBaseType(ad,&mtd -> type))
                    yyerror("Mapped type has already been defined in another module");
            }

            /*
             * If we got here then we have a mapped type based on
             * an existing template, but with unique parameters.
             * We don't want to use interface files from other
             * modules, so skip this one.
             */

            continue;
        }

        /* Ignore a namespace defined in another module. */
        if (iftype == namespace_iface && iff->module != mod)
            continue;

        return iff;
    }

    iff = sipMalloc(sizeof (ifaceFileDef));

    iff -> name = cacheName(pt,scopedNameTail(fqname));
    iff -> type = iftype;
    iff -> fqcname = fqname;
    iff -> module = NULL;
    iff -> used = NULL;
    iff -> next = pt -> ifacefiles;

    pt -> ifacefiles = iff;

    return iff;
}


/*
 * Find a class definition in a parse tree.
 */
static classDef *findClass(sipSpec *pt,ifaceFileType iftype,
               scopedNameDef *fqname)
{
    return findClassWithInterface(pt, findIfaceFile(pt, currentModule, fqname, iftype, NULL));
}


/*
 * Find a class definition given an existing interface file.
 */
static classDef *findClassWithInterface(sipSpec *pt, ifaceFileDef *iff)
{
    classDef *cd;

    for (cd = pt -> classes; cd != NULL; cd = cd -> next)
        if (cd -> iff == iff)
            return cd;

    /* Create a new one. */
    cd = sipMalloc(sizeof (classDef));

    cd -> iff = iff;
    cd -> pyname = classBaseName(cd);
    cd -> classnr = -1;
    cd -> classflags = 0;
    cd -> userflags = 0;
    cd -> ecd = NULL;
    cd -> dtorexceptions = NULL;
    cd -> real = NULL;
    cd -> node = NULL;
    cd -> supers = NULL;
    cd -> mro = NULL;
    cd -> td = NULL;
    cd -> ctors = NULL;
    cd -> defctor = NULL;
    cd -> dealloccode = NULL;
    cd -> dtorcode = NULL;
    cd -> members = NULL;
    cd -> overs = NULL;
    cd -> casts = NULL;
    cd -> vmembers = NULL;
    cd -> visible = NULL;
    cd -> cppcode = NULL;
    cd -> hdrcode = NULL;
    cd -> convtosubcode = NULL;
    cd -> subbase = NULL;
    cd -> convtocode = NULL;
    cd -> travcode = NULL;
    cd -> clearcode = NULL;
    cd -> readbufcode = NULL;
    cd -> writebufcode = NULL;
    cd -> segcountcode = NULL;
    cd -> charbufcode = NULL;
    cd -> next = pt -> classes;

    pt -> classes = cd;

    return cd;
}


/*
 * Add an interface file to an interface file list if it isn't already there.
 */
ifaceFileList *addToUsedList(ifaceFileList **ifflp, ifaceFileDef *iff)
{
    ifaceFileList *iffl;

    while ((iffl = *ifflp) != NULL)
    {
        /* Don't bother if it is already there. */
        if (iffl -> iff == iff)
            return iffl;

        ifflp = &iffl -> next;
    }

    iffl = sipMalloc(sizeof (ifaceFileList));

    iffl->iff = iff;
    iffl->header = FALSE;
    iffl->next = NULL;

    *ifflp = iffl;

    return iffl;
}


/*
 * Find an undefined (or create a new) exception definition in a parse tree.
 */
static exceptionDef *findException(sipSpec *pt, scopedNameDef *fqname, int new)
{
    exceptionDef *xd, **tail;
    ifaceFileDef *iff;
    classDef *cd;

    iff = findIfaceFile(pt, currentModule, fqname, exception_iface, NULL);

    /* See if it is an existing one. */
    for (xd = pt->exceptions; xd != NULL; xd = xd->next)
        if (xd->iff == iff)
            return xd;

    /*
     * If it is an exception interface file then we have never seen this
     * name before.  We require that exceptions are defined before being
     * used, but don't make the same requirement of classes (for reasons of
     * backwards compatibility).  Therefore the name must be reinterpreted
     * as a (as yet undefined) class.
     */
    if (new)
        if (iff->type == exception_iface)
            cd = NULL;
        else
            yyerror("There is already a class with the same name or the exception has been used before being defined");
    else
    {
        if (iff->type == exception_iface)
            iff->type = class_iface;

        cd = findClassWithInterface(pt, iff);
    }

    /* Create a new one. */
    xd = sipMalloc(sizeof (exceptionDef));

    xd->exceptionnr = -1;
    xd->iff = iff;
    xd->pyname = NULL;
    xd->cd = cd;
    xd->bibase = NULL;
    xd->base = NULL;
    xd->hdrcode = NULL;
    xd->raisecode = NULL;
    xd->next = NULL;

    /* Append it to the list. */
    for (tail = &pt->exceptions; *tail != NULL; tail = &(*tail)->next)
        ;

    *tail = xd;

    return xd;
}


/*
 * Find an undefined (or create a new) class definition in a parse tree.
 */
static classDef *newClass(sipSpec *pt,ifaceFileType iftype,
              scopedNameDef *fqname)
{
    int flags;
    classDef *cd, *scope;
    codeBlock *hdrcode;

    if (sectionFlags & SECT_IS_PRIVATE)
        yyerror("Classes, structs and namespaces must be in the public or or protected sections");

    flags = 0;

    if ((scope = currentScope()) != NULL)
    {
        if (sectionFlags & SECT_IS_PROT)
            flags = CLASS_IS_PROTECTED;

        hdrcode = scope -> hdrcode;
    }
    else
        hdrcode = NULL;

    if (pt -> genc)
    {
        /* C structs are always global types. */
        while (fqname -> next != NULL)
            fqname = fqname -> next;

        scope = NULL;
    }

    cd = findClass(pt,iftype,fqname);

    /* Check it hasn't already been defined. */
    if (iftype != namespace_iface && cd->iff->module != NULL)
        yyerror("The struct/class has already been defined");

    /* Complete the initialisation. */
    cd->classflags |= flags;
    cd->ecd = scope;
    cd->iff->module = currentModule;

    appendCodeBlock(&cd->hdrcode, hdrcode);

    /* See if it is a namespace extender. */
    if (iftype == namespace_iface)
    {
        classDef *ns;

        for (ns = pt->classes; ns != NULL; ns = ns->next)
        {
            if (ns == cd)
                continue;

            if (ns->iff->type != namespace_iface)
                continue;

            if (!sameScopedName(ns->iff->fqcname, fqname))
                continue;

            cd->real = ns;
            break;
        }
    }

    return cd;
}


/*
 * Tidy up after finishing a class definition.
 */
static void finishClass(sipSpec *pt, moduleDef *mod, classDef *cd, optFlags *of)
{
    char *pyname;
    optFlag *flg;

    /* Get the Python name and see if it is different to the C++ name. */
    pyname = getPythonName(of, classBaseName(cd));

    cd -> pyname = NULL;
    checkAttributes(pt, cd->ecd, pyname, FALSE);
    cd->pyname = pyname;

    if (cd->pyname != classBaseName(cd))
        setIsRenamedClass(cd);

    if ((flg = findOptFlag(of, "TypeFlags", integer_flag)) != NULL)
        cd->userflags = flg->fvalue.ival;

    if (isOpaque(cd))
    {
        if (findOptFlag(of, "External", bool_flag) != NULL)
            setIsExternal(cd);
    }
    else
    {
        int seq_might, seq_not;
        memberDef *md;

        if (findOptFlag(of, "NoDefaultCtors", bool_flag) != NULL)
            setNoDefaultCtors(cd);

        if (cd -> ctors == NULL)
        {
            if (!noDefaultCtors(cd))
            {
                /* Provide a default ctor. */

                cd->ctors = sipMalloc(sizeof (ctorDef));
 
                cd->ctors->ctorflags = SECT_IS_PUBLIC;
                cd->ctors->pysig.nrArgs = 0;
                cd->ctors->cppsig = &cd -> ctors -> pysig;
                cd->ctors->exceptions = NULL;
                cd->ctors->methodcode = NULL;
                cd->ctors->prehook = NULL;
                cd->ctors->posthook = NULL;
                cd->ctors->next = NULL;

                cd->defctor = cd->ctors;

                setCanCreate(cd);
            }
        }
        else if (cd -> defctor == NULL)
        {
            ctorDef *ct, *last = NULL;

            for (ct = cd -> ctors; ct != NULL; ct = ct -> next)
            {
                if (!isPublicCtor(ct))
                    continue;

                if (ct -> pysig.nrArgs == 0 || ct -> pysig.args[0].defval != NULL)
                {
                    cd -> defctor = ct;
                    break;
                }

                if (last == NULL)
                    last = ct;
            }

            /* The last resort is the first public ctor. */
            if (cd->defctor == NULL)
                cd->defctor = last;
        }

        if (findOptFlag(of,"Abstract",bool_flag) != NULL)
        {
            setIsAbstractClass(cd);
            setIsIncomplete(cd);
            resetCanCreate(cd);
        }

        /* We assume a public dtor if nothing specific was provided. */
        if (!isDtor(cd))
            setIsPublicDtor(cd);

        if (findOptFlag(of, "DelayDtor", bool_flag) != NULL)
        {
            setIsDelayedDtor(cd);
            setHasDelayedDtors(mod);
        }

        /*
         * There are subtle differences between the add and concat methods and
         * the multiply and repeat methods.  The number versions can have their
         * operands swapped and may return NotImplemented.  If the user has
         * used the /Numeric/ annotation or there are other numeric operators
         * then we use add/multiply.  Otherwise, if there are indexing
         * operators then we use concat/repeat.
         */
        seq_might = seq_not = FALSE;

        for (md = cd -> members; md != NULL; md = md -> next)
            switch (md -> slot)
            {
            case getitem_slot:
            case setitem_slot:
            case delitem_slot:
                /* This might be a sequence. */
                seq_might = TRUE;
                break;

            case sub_slot:
            case isub_slot:
            case div_slot:
            case idiv_slot:
            case mod_slot:
            case imod_slot:
            case pos_slot:
            case neg_slot:
                /* This is definately not a sequence. */
                seq_not = TRUE;
                break;
            }

        if (!seq_not && seq_might)
            for (md = cd -> members; md != NULL; md = md -> next)
            {
                /* Ignore if the user has been explicit. */
                if (isNumeric(md))
                    continue;

                switch (md -> slot)
                {
                case add_slot:
                    md -> slot = concat_slot;
                    break;

                case iadd_slot:
                    md -> slot = iconcat_slot;
                    break;

                case mul_slot:
                    md -> slot = repeat_slot;
                    break;

                case imul_slot:
                    md -> slot = irepeat_slot;
                    break;
                }
            }
    }

    if (inMainModule())
    {
        setIsUsedName(cd->iff->name);
        setIsClassName(cd->iff->name);
    }
}


/*
 * Create a new mapped type.
 */
static mappedTypeDef *newMappedType(sipSpec *pt,argDef *ad)
{
    mappedTypeDef *mtd;
    scopedNameDef *snd;
    ifaceFileDef *iff;

    /* Check that the type is one we want to map. */
    switch (ad -> atype)
    {
    case defined_type:
        snd = ad -> u.snd;
        break;

    case template_type:
        snd = ad -> u.td -> fqname;
        break;

    case struct_type:
        snd = ad -> u.sname;
        break;

    default:
        yyerror("Invalid type for %MappedType");
    }

    iff = findIfaceFile(pt, currentModule, snd, mappedtype_iface, ad);

    if (inMainModule())
        setIsUsedName(iff -> name);

    /* Check it hasn't already been defined. */
    for (mtd = pt -> mappedtypes; mtd != NULL; mtd = mtd -> next)
        if (mtd -> iff == iff)
        {
            /*
             * We allow types based on the same template but with
             * different arguments.
             */

            if (ad -> atype != template_type ||
                sameBaseType(ad,&mtd -> type))
                yyerror("Mapped type has already been defined in this module");
        }

    /* The module may not have been set yet. */
    iff -> module = currentModule;

    /* Create a new mapped type. */
    mtd = allocMappedType(ad);

    mtd -> iff = iff;
    mtd -> next = pt -> mappedtypes;

    pt -> mappedtypes = mtd;

    return mtd;
}


/*
 * Allocate, intialise and return a mapped type structure.
 */
mappedTypeDef *allocMappedType(argDef *type)
{
    mappedTypeDef *mtd;

    mtd = sipMalloc(sizeof (mappedTypeDef));

    mtd->type = *type;
    mtd->type.argflags = 0;
    mtd->type.nrderefs = 0;

    mtd->mappednr = -1;
    mtd->iff = NULL;
    mtd->hdrcode = NULL;
    mtd->convfromcode = NULL;
    mtd->convtocode = NULL;
    mtd->next = NULL;

    return mtd;
}


/*
 * Create a new enum.
 */
static enumDef *newEnum(sipSpec *pt,moduleDef *mod,char *name,optFlags *of,
            int flags)
{
    enumDef *ed;
    classDef *escope = currentScope();

    ed = sipMalloc(sizeof (enumDef));

    if (name != NULL)
    {
        ed -> fqcname = text2scopedName(name);
        ed -> pyname = cacheName(pt, getPythonName(of, name));

        checkAttributes(pt, escope, ed->pyname->text, FALSE);
    }
    else
    {
        ed -> fqcname = NULL;
        ed -> pyname = NULL;
    }

    ed -> enumflags = flags;
    ed -> enumnr = -1;
    ed -> ecd = escope;
    ed -> pcd = (flags & SECT_IS_PROT) ? escope : NULL;
    ed -> module = mod;
    ed -> members = NULL;
    ed -> slots = NULL;
    ed -> overs = NULL;
    ed -> next = pt -> enums;

    if (name != NULL && strcmp(ed->pyname->text, name) != 0)
        setIsRenamedEnum(ed);

    pt -> enums = ed;

    if (escope != NULL)
        setHasEnums(escope);

    return ed;
}


/*
 * Get the type values and (optionally) the type names for substitution in
 * handwritten code.
 */
void appendTypeStrings(scopedNameDef *ename, signatureDef *patt, signatureDef *src, signatureDef *known, scopedNameDef **names, scopedNameDef **values)
{
    int a;

    for (a = 0; a < patt->nrArgs; ++a)
    {
        argDef *pad = &patt->args[a];

        if (pad->atype == defined_type)
        {
            char *nam = NULL;

            /*
             * If the type names are already known then check that
             * this is one of them.
             */
            if (known == NULL)
                nam = scopedNameTail(pad->u.snd);
            else if (pad->u.snd->next == NULL)
            {
                int k;

                for (k = 0; k < known->nrArgs; ++k)
                    if (strcmp(pad->u.snd->name, known->args[k].u.snd->name) == 0)
                    {
                        nam = pad->u.snd->name;
                        break;
                    }
            }

            if (nam == NULL)
                continue;

            /* Add the name. */
            appendScopedName(names, text2scopePart(nam));

            /* Add the corresponding value. */
            appendScopedName(values, text2scopePart(getType(ename, &src->args[a])));
        }
        else if (pad->atype == template_type)
        {
            argDef *sad = &src->args[a];

            /* These checks shouldn't be necessary, but... */
            if (sad->atype == template_type && pad->u.td->types.nrArgs == sad->u.td->types.nrArgs)
                appendTypeStrings(ename, &pad->u.td->types, &sad->u.td->types, known, names, values);
        }
    }
}


/*
 * Convert a type to a string.  We impose some limitations because I'm too lazy
 * to handle everything that might be needed one day.
 */
static char *getType(scopedNameDef *ename, argDef *ad)
{
    if (ad->atype == defined_type)
        return scopedNameToString(ad->u.snd);

    fatalScopedName(ename);
    fatal(": unsupported type argument to template class instantiation\n");

    return NULL;
}


/*
 * Convert a scoped name to a string on the heap.
 */
static char *scopedNameToString(scopedNameDef *name)
{
    static const char scope_string[] = "::";
    size_t len;
    scopedNameDef *snd;
    char *s, *dp;

    /* Work out the length of buffer needed. */
    len = 0;

    for (snd = name; snd != NULL; snd = snd->next)
    {
        len += strlen(snd->name);

        if (snd->next != NULL)
            len += strlen(scope_string);
    }

    /* Allocate and populate the buffer. */
    dp = s = sipMalloc(len + 1);

    for (snd = name; snd != NULL; snd = snd->next)
    {
        strcpy(dp, snd->name);
        dp += strlen(snd->name);

        if (snd->next != NULL)
        {
            strcpy(dp, scope_string);
            dp += strlen(scope_string);
        }
    }

    return s;
}


/*
 * Instantiate a class template.
 */
static void instantiateClassTemplate(sipSpec *pt, moduleDef *mod, classDef *scope, scopedNameDef *fqname, classTmplDef *tcd, templateDef *td)
{
    scopedNameDef *type_names, *type_values;
    classDef *cd;
    ctorDef *oct, **cttail;
    memberDef *omd, **mdtail;
    overDef *ood, **odtail;
    argDef *ad;
    ifaceFileList *iffl, **used;

    type_names = type_values = NULL;
    appendTypeStrings(classFQCName(tcd->cd), &tcd->sig, &td->types, NULL, &type_names, &type_values);

    /*
     * Add a mapping from the template name to the instantiated name.  If
     * we have got this far we know there is room for it.
     */
    ad = &tcd->sig.args[tcd->sig.nrArgs++];
    ad->atype = defined_type;
    ad->name = NULL;
    ad->argflags = 0;
    ad->nrderefs = 0;
    ad->defval = NULL;
    ad->u.snd = classFQCName(tcd->cd);

    appendScopedName(&type_names, text2scopePart(scopedNameTail(classFQCName(tcd->cd))));
    appendScopedName(&type_values, text2scopePart(scopedNameToString(fqname)));

    /* Create the new class. */
    cd = sipMalloc(sizeof (classDef));

    /* Start with a shallow copy. */
    *cd = *tcd->cd;

    cd->pyname = scopedNameTail(fqname);
    cd->td = td;

    /* Handle the interface file. */
    cd->iff = findIfaceFile(pt, mod, fqname, class_iface, NULL);
    cd->iff->module = mod;

    /* Make a copy of the used list and add the enclosing scope. */
    used = &cd->iff->used;

    for (iffl = tcd->cd->iff->used; iffl != NULL; iffl = iffl->next)
        addToUsedList(used, iffl->iff);

    if (scope != NULL)
        addToUsedList(&cd->iff->used, scope->iff);

    if (inMainModule())
    {
        setIsUsedName(cd->iff->name);
        setIsClassName(cd->iff->name);
    }

    cd->ecd = currentScope();

    /* Handle the ctors. */
    cd->ctors = NULL;
    cttail = &cd->ctors;

    for (oct = tcd->cd->ctors; oct != NULL; oct = oct->next)
    {
        ctorDef *nct = sipMalloc(sizeof (ctorDef));

        /* Start with a shallow copy. */
        *nct = *oct;

        templateSignature(&nct->pysig, FALSE, tcd, td, cd);

        if (oct->cppsig == NULL)
            nct->cppsig = NULL;
        else if (oct->cppsig == &oct->pysig)
            nct->cppsig = &nct->pysig;
        else
        {
            nct->cppsig = sipMalloc(sizeof (signatureDef));

            *nct->cppsig = *oct->cppsig;

            templateSignature(nct->cppsig, FALSE, tcd, td, cd);
        }

        nct->methodcode = templateCode(pt, used, nct->methodcode, type_names, type_values);

        nct->next = NULL;
        *cttail = nct;
        cttail = &nct->next;

        /* Handle the default ctor. */
        if (tcd->cd->defctor == oct)
            cd->defctor = nct;
    }

    cd->dealloccode = templateCode(pt, used, cd->dealloccode, type_names, type_values);
    cd->dtorcode = templateCode(pt, used, cd->dtorcode, type_names, type_values);

    /* Handle the members, ie. the common parts of overloads. */
    cd->members = NULL;
    mdtail = &cd->members;

    for (omd = tcd->cd->members; omd != NULL; omd = omd->next)
    {
        memberDef *nmd = sipMalloc(sizeof (memberDef));

        /* Start with a shallow copy. */
        *nmd = *omd;

        nmd->module = mod;

        nmd->next = NULL;
        *mdtail = nmd;
        mdtail = &nmd->next;
    }

    /* Handle the overloads. */
    cd->overs = NULL;
    odtail = &cd->overs;

    for (ood = tcd->cd->overs; ood != NULL; ood = ood->next)
    {
        overDef *nod = sipMalloc(sizeof (overDef));
        memberDef *nmd;

        /* Start with a shallow copy. */
        *nod = *ood;

        for (nmd = cd->members, omd = tcd->cd->members; omd != NULL; omd = omd->next, nmd = nmd->next)
            if (omd == ood->common)
            {
                nod->common = nmd;
                break;
            }

        templateSignature(&nod->pysig, TRUE, tcd, td, cd);

        if (ood->cppsig == &ood->pysig)
            nod->cppsig = &nod->pysig;
        else
        {
            nod->cppsig = sipMalloc(sizeof (signatureDef));

            *nod->cppsig = *ood->cppsig;

            templateSignature(nod->cppsig, TRUE, tcd, td, cd);
        }

        nod->methodcode = templateCode(pt, used, nod->methodcode, type_names, type_values);

        /* Handle any virtual handler. */
        if (ood->virthandler != NULL)
        {
            nod->virthandler = sipMalloc(sizeof (virtHandlerDef));

            /* Start with a shallow copy. */
            *nod->virthandler = *ood->virthandler;

            if (ood->virthandler->cppsig == &ood->pysig)
                nod->virthandler->cppsig = &nod->pysig;
            else
            {
                nod->virthandler->cppsig = sipMalloc(sizeof (signatureDef));

                *nod->virthandler->cppsig = *ood->virthandler->cppsig;

                templateSignature(nod->virthandler->cppsig, TRUE, tcd, td, cd);
            }

            nod->virthandler->module = mod;
            nod->virthandler->virtcode = templateCode(pt, used, nod->virthandler->virtcode, type_names, type_values);
            nod->virthandler->next = mod->virthandlers;

            mod->virthandlers = nod->virthandler;
        }

        nod->next = NULL;
        *odtail = nod;
        odtail = &nod->next;
    }

    cd->cppcode = templateCode(pt, used, cd->cppcode, type_names, type_values);
    cd->hdrcode = templateCode(pt, used, cd->hdrcode, type_names, type_values);
    cd->convtosubcode = templateCode(pt, used, cd->convtosubcode, type_names, type_values);
    cd->convtocode = templateCode(pt, used, cd->convtocode, type_names, type_values);
    cd->travcode = templateCode(pt, used, cd->travcode, type_names, type_values);
    cd->clearcode = templateCode(pt, used, cd->clearcode, type_names, type_values);
    cd->readbufcode = templateCode(pt, used, cd->readbufcode, type_names, type_values);
    cd->writebufcode = templateCode(pt, used, cd->writebufcode, type_names, type_values);
    cd->segcountcode = templateCode(pt, used, cd->segcountcode, type_names, type_values);
    cd->charbufcode = templateCode(pt, used, cd->charbufcode, type_names, type_values);
    cd->next = pt->classes;

    pt->classes = cd;

    tcd->sig.nrArgs--;

    freeScopedName(type_names);
    freeScopedName(type_values);
}


/*
 * Replace any template arguments in a signature.
 */
static void templateSignature(signatureDef *sd, int result, classTmplDef *tcd, templateDef *td, classDef *ncd)
{
    int a;

    if (result)
        templateType(&sd->result, tcd, td, ncd);

    for (a = 0; a < sd->nrArgs; ++a)
        templateType(&sd->args[a], tcd, td, ncd);
}


/*
 * Replace any template arguments in a type.
 */
static void templateType(argDef *ad, classTmplDef *tcd, templateDef *td, classDef *ncd)
{
    int a;
    char *name;

    /* Ignore if it isn't an unscoped name. */
    if (ad->atype != defined_type || ad->u.snd->next != NULL)
        return;

    name = ad->u.snd->name;

    for (a = 0; a < tcd->sig.nrArgs - 1; ++a)
        if (strcmp(name, scopedNameTail(tcd->sig.args[a].u.snd)) == 0)
        {
            ad->atype = td->types.args[a].atype;

            /* We take the constrained flag from the real type. */
            resetIsConstrained(ad);

            if (isConstrained(&td->types.args[a]))
                setIsConstrained(ad);

            ad->u = td->types.args[a].u;

            return;
        }

    /* Handle the class name itself. */
    if (strcmp(name, scopedNameTail(classFQCName(tcd->cd))) == 0)
    {
        ad->atype = class_type;
        ad->u.cd = ncd;
    }
}


/*
 * Replace any template arguments in a literal code block.
 */
codeBlock *templateCode(sipSpec *pt, ifaceFileList **used, codeBlock *ocb, scopedNameDef *names, scopedNameDef *values)
{
    codeBlock *ncb = NULL, **tail = &ncb;

    while (ocb != NULL)
    {
        char *at = ocb->frag;

        do
        {
            char *first = NULL;
            codeBlock *cb;
            scopedNameDef *nam, *val, *nam_first, *val_first;

            /*
             * Go through the rest of this fragment looking for
             * each of the types and the name of the class itself.
             */
            nam = names;
            val = values;

            while (nam != NULL && val != NULL)
            {
                char *cp;

                if ((cp = strstr(at, nam->name)) != NULL)
                    if (first == NULL || first > cp)
                    {
                        nam_first = nam;
                        val_first = val;
                        first = cp;
                    }

                nam = nam->next;
                val = val->next;
            }

            /* Create the new fragment. */
            cb = sipMalloc(sizeof (codeBlock));

            if (at == ocb->frag)
            {
                cb->filename = ocb->filename;
                cb->linenr = ocb->linenr;
            }
            else
                cb->filename = NULL;

            cb->next = NULL;
            *tail = cb;
            tail = &cb->next;

            /* See if anything was found. */
            if (first == NULL)
            {
                /* We can just point to this. */
                cb->frag = at;

                /* All done with this one. */
                at = NULL;
            }
            else
            {
                static char *gen_names[] = {
                    "sipForceConvertToTransfer_",
                    "sipForceConvertTo_",
                    "sipConvertFromTransfer_",
                    "sipConvertFrom_",
                    "sipClass_",
                    "sipEnum_",
                    "sipException_",
                    NULL
                };

                char *dp, *sp, **gn;
                int genname = FALSE;

                /*
                 * If the context in which the text is used is
                 * in the name of a SIP generated object then
                 * translate any "::" scoping to "_".
                 */
                for (gn = gen_names; *gn != NULL; ++gn)
                    if (search_back(first, at, *gn))
                    {
                        addUsedFromCode(pt, used, val_first->name);
                        genname = TRUE;
                        break;
                    }

                /* Fragment the fragment. */
                cb->frag = sipMalloc(first - at + strlen(val_first->name) + 1);

                strncpy(cb->frag, at, first - at);

                dp = &cb->frag[first - at];
                sp = val_first->name;

                if (genname)
                {
                    char gch;

                    while ((gch = *sp++) != '\0')
                        if (gch == ':' && *sp == ':')
                        {
                            *dp++ = '_';
                            ++sp;
                        }
                        else
                            *dp++ = gch;

                    *dp = '\0';
                }
                else
                    strcpy(dp, sp);

                /* Move past the replaced text. */
                at = first + strlen(nam_first->name);
            }
        }
        while (at != NULL && *at != '\0');

        ocb = ocb->next;
    }

    return ncb;
}


/*
 * Return TRUE if the text at the end of a string matches the target string.
 */
static int search_back(const char *end, const char *start, const char *target)
{
    size_t tlen = strlen(target);

    if (start + tlen >= end)
        return FALSE;

    return (strncmp(end - tlen, target, tlen) == 0);
}


/*
 * Add any needed interface files based on handwritten code.
 */
static void addUsedFromCode(sipSpec *pt, ifaceFileList **used, const char *sname)
{
    ifaceFileDef *iff;
    enumDef *ed;

    for (iff = pt->ifacefiles; iff != NULL; iff = iff->next)
    {
        if (iff->type != class_iface && iff->type != exception_iface)
            continue;

        if (sameName(iff->fqcname, sname))
        {
            addToUsedList(used, iff);

            return;
        }
    }

    for (ed = pt->enums; ed != NULL; ed = ed->next)
    {
        if (ed->ecd == NULL)
            continue;

        if (sameName(ed->fqcname, sname))
        {
            addToUsedList(used, ed->ecd->iff);

            return;
        }
    }
}


/*
 * Compare a scoped name with its string equivalent.
 */
static int sameName(scopedNameDef *snd, const char *sname)
{
    while (snd != NULL && *sname != '\0')
    {
        const char *sp = snd->name;

        while (*sp != '\0' && *sname != ':' && *sname != '\0')
            if (*sp++ != *sname++)
                return FALSE;

        if (*sp != '\0' || (*sname != ':' && *sname != '\0'))
            return FALSE;

        snd = snd->next;

        if (*sname == ':')
            sname += 2;
    }

    return (snd == NULL && *sname == '\0');
}


/*
 * Create a new typedef.
 */
static void newTypedef(sipSpec *pt,moduleDef *mod,char *name,argDef *type)
{
    typedefDef *td;
    scopedNameDef *fqname = text2scopedName(name);
    classDef *scope = currentScope();

    /* See if we are instantiating a template class. */
    if (type->atype == template_type)
    {
        classTmplDef *tcd;
        templateDef *td = type->u.td;

        for (tcd = pt->classtemplates; tcd != NULL; tcd = tcd->next)
            if (sameScopedName(tcd->cd->iff->fqcname, td->fqname))
            {
                if (!sameTemplateSignature(&tcd->sig, &td->types, FALSE))
                    continue;

                instantiateClassTemplate(pt, mod, scope, fqname, tcd, td);

                /* All done. */
                return;
            }
    }

    /* Check it doesn't already exist. */
    for (td = pt -> typedefs; td != NULL; td = td -> next)
        if (sameScopedName(td -> fqname,fqname))
        {
            fatalScopedName(fqname);
            fatal(" already defined\n");
        }

    td = sipMalloc(sizeof (typedefDef));

    td -> fqname = fqname;
    td -> ecd = scope;
    td -> module = mod;
    td -> type = *type;
    td -> next = pt -> typedefs;

    mod -> nrtypedefs++;

    pt -> typedefs = td;
}


/*
 * Return TRUE if the template signatures are the same.  A deep comparison is
 * used for mapped type templates where we want to recurse into any nested
 * templates.
 */
int sameTemplateSignature(signatureDef *sd1, signatureDef *sd2, int deep)
{
    int a;

    if (sd1->nrArgs != sd2->nrArgs)
        return FALSE;

    for (a = 0; a < sd1->nrArgs; ++a)
    {
        argDef *ad1 = &sd1->args[a];
        argDef *ad2 = &sd2->args[a];

        /*
         * If we are doing a shallow comparision (ie. for class
         * templates) then a type name on the left hand side matches
         * anything on the right hand side.
         */
        if (ad1->atype == defined_type && !deep)
            continue;

        /*
         * For type names only compare the references and pointers, and
         * do the same for any nested templates.
         */
        if (ad1->atype == defined_type && ad2->atype == defined_type)
        {
            if (isReference(ad1) != isReference(ad2) || ad1->nrderefs != ad2->nrderefs)
                return FALSE;
        }
        else if (ad1->atype == template_type && ad2->atype == template_type)
        {
            if (!sameTemplateSignature(&ad1->u.td->types, &ad2->u.td->types, deep))
                return FALSE;
        }
        else if (!sameBaseType(ad1, ad2))
            return FALSE;
    }

    return TRUE;
}


/*
 * Create a new variable.
 */
static void newVar(sipSpec *pt,moduleDef *mod,char *name,int isstatic,
           argDef *type,optFlags *of,codeBlock *acode,codeBlock *gcode,
           codeBlock *scode)
{
    varDef *var;
    classDef *escope = currentScope();
    nameDef *nd = cacheName(pt,getPythonName(of,name));

    if (inMainModule())
        setIsUsedName(nd);

    checkAttributes(pt,escope,nd -> text,FALSE);

    var = sipMalloc(sizeof (varDef));

    var -> pyname = nd;
    var -> fqcname = text2scopedName(name);
    var -> ecd = escope;
    var -> module = mod;
    var -> varflags = 0;
    var -> type = *type;
    var -> accessfunc = acode;
    var -> getcode = gcode;
    var -> setcode = scode;
    var -> next = pt -> vars;

    if (isstatic || (escope != NULL && escope->iff->type == namespace_iface))
        setIsStaticVar(var);

    pt -> vars = var;
}


/*
 * Create a new ctor.
 */
static void newCtor(char *name,int sectFlags,signatureDef *args,
            optFlags *optflgs,codeBlock *methodcode,
            throwArgs *exceptions,signatureDef *cppsig,int explicit)
{
    ctorDef *ct, **ctp;
    classDef *cd = currentScope();

    /* Check the name of the constructor. */
    if (strcmp(classBaseName(cd),name) != 0)
        yyerror("Constructor doesn't have the same name as its class");

    /* Add to the list of constructors. */
    ct = sipMalloc(sizeof (ctorDef));

    ct -> ctorflags = sectFlags;
    ct -> pysig = *args;
    ct -> cppsig = (cppsig != NULL ? cppsig : &ct -> pysig);
    ct -> exceptions = exceptions;
    ct -> methodcode = methodcode;
    ct -> next = NULL;

    if (!isPrivateCtor(ct))
        setCanCreate(cd);

    if (isProtectedCtor(ct))
        setHasShadow(cd);

    if (explicit)
        setIsExplicitCtor(ct);

    getHooks(optflgs,&ct -> prehook,&ct -> posthook);

    if (getReleaseGIL(optflgs))
        setIsReleaseGILCtor(ct);
    else if (getHoldGIL(optflgs))
        setIsHoldGILCtor(ct);

    if (findOptFlag(optflgs,"NoDerived",bool_flag) != NULL)
    {
        if (cppsig != NULL)
            yyerror("The /NoDerived/ annotation cannot be used with a C++ signature");

        if (methodcode == NULL)
            yyerror("The /NoDerived/ annotation must be used with %MethodCode");

        ct->cppsig = NULL;
    }

    if (findOptFlag(optflgs,"Default",bool_flag) != NULL)
    {
        if (cd -> defctor != NULL)
            yyerror("A constructor with the /Default/ annotation has already been defined");

        cd -> defctor = ct;
    }

    /* Append to the list. */
    for (ctp = &cd->ctors; *ctp != NULL; ctp = &(*ctp)->next)
        ;

    *ctp = ct;
}


/*
 * Create a new function.
 */
static void newFunction(sipSpec *pt,moduleDef *mod,int sflags,int isstatic,
            int isvirt,char *name,signatureDef *sig,int isconst,
            int isabstract,optFlags *optflgs,codeBlock *methodcode,
            codeBlock *vcode,throwArgs *exceptions,
            signatureDef *cppsig)
{
    classDef *cd = currentScope();
    nameDef *pname;
    int factory, xferback;
    overDef *od, **odp, **headp;
    optFlag *of;
    virtHandlerDef *vhd;

    /* Extra checks for a C module. */
    if (pt -> genc)
    {
        if (cd != NULL)
            yyerror("Function declaration not allowed in a struct in a C module");

        if (isstatic)
            yyerror("Static functions not allowed in a C module");

        if (exceptions != NULL)
            yyerror("Exceptions not allowed in a C module");
    }

    headp = (cd != NULL ?  &cd -> overs : &pt -> overs);

    /* See if it is a factory method. */
    if (findOptFlag(optflgs,"Factory",bool_flag) != NULL)
        factory = TRUE;
    else
    {
        int a;

        factory = FALSE;

        /* Check /TransferThis/ wasn't specified. */
        if (cd == NULL || isstatic)
            for (a = 0; a < sig -> nrArgs; ++a)
                if (isThisTransferred(&sig -> args[a]))
                    yyerror("/TransferThis/ may only be specified in constructors and class methods");
    }

    /* See if the result is to be returned to Python ownership. */
    xferback = (findOptFlag(optflgs,"TransferBack",bool_flag) != NULL);

    if (factory && xferback)
        yyerror("/TransferBack/ and /Factory/ cannot both be specified");

    /* Use the C++ name if a Python name wasn't given. */
    pname = cacheName(pt, getPythonName(optflgs, name));

    /* Create a new overload definition. */

    od = sipMalloc(sizeof (overDef));

    /* Set the overload flags. */

    od -> overflags = sflags;

    if (factory)
        setIsFactory(od);

    if (xferback)
        setIsResultTransferredBack(od);

    if (isProtected(od))
        setHasShadow(cd);

    if ((isSlot(od) || isSignal(od)) && !isPrivate(od))
    {
        if (isSignal(od))
            setHasShadow(cd);

        pt -> sigslots = TRUE;
    }

    if (isSignal(od) && (methodcode != NULL || vcode != NULL))
        yyerror("Cannot provide code for signals");

    if (isstatic)
    {
        if (isSignal(od))
            yyerror("Static functions cannot be signals");

        if (isvirt)
            yyerror("Static functions cannot be virtual");

        setIsStatic(od);
    }

    if (isconst)
        setIsConst(od);

    if (isabstract)
    {
        if (sflags == 0)
            yyerror("Non-class function specified as abstract");

        setIsAbstract(od);
    }

    if ((of = findOptFlag(optflgs,"AutoGen",opt_name_flag)) != NULL)
    {
        setIsAutoGen(od);

        if (of -> fvalue.sval != NULL)
        {
            qualDef *qd;

            if ((qd = findQualifier(of -> fvalue.sval)) == NULL || qd -> qtype != feature_qualifier)
                yyerror("No such feature");

            if (excludedFeature(excludedQualifiers,qd))
                resetIsAutoGen(od);
        }
    }

    if (isvirt)
    {
        if (isSignal(od) && !optNoEmitters(pt))
            yyerror("Virtual signals aren't supported");

        setIsVirtual(od);
        setHasShadow(cd);

        vhd = sipMalloc(sizeof (virtHandlerDef));

        vhd -> virthandlernr = -1;
        vhd -> vhflags = 0;
        vhd -> pysig = &od -> pysig;
        vhd -> cppsig = (cppsig != NULL ? cppsig : &od -> pysig);
        vhd -> module = currentModule;
        vhd -> virtcode = vcode;
        vhd -> next = currentModule -> virthandlers;

        if (factory || xferback)
            setIsTransferVH(vhd);

        currentModule -> virthandlers = vhd;
    }
    else
    {
        if (vcode != NULL)
            yyerror("%VirtualCatcherCode provided for non-virtual function");

        vhd = NULL;
    }

    od -> cppname = name;
    od -> pysig = *sig;
    od -> cppsig = (cppsig != NULL ? cppsig : &od -> pysig);
    od -> exceptions = exceptions;
    od -> methodcode = methodcode;
    od -> virthandler = vhd;
    od -> common = findFunction(pt,mod,cd,pname,(methodcode != NULL),sig -> nrArgs);

    if (findOptFlag(optflgs,"Numeric",bool_flag) != NULL)
        setIsNumeric(od -> common);

    /* Methods that run in new threads must be virtual. */
    if (findOptFlag(optflgs,"NewThread",bool_flag) != NULL)
    {
        argDef *res;

        if (!isvirt)
            yyerror("/NewThread/ may only be specified for virtual functions");

        /*
         * This is an arbitary limitation to make the code generator
         * slightly easier - laziness on my part.
         */
        res = &od -> cppsig -> result;

        if (res -> atype != void_type || res -> nrderefs != 0)
            yyerror("/NewThread/ may only be specified for void functions");

        setIsNewThread(od);
    }

    getHooks(optflgs,&od -> prehook,&od -> posthook);

    if (getReleaseGIL(optflgs))
        setIsReleaseGIL(od);
    else if (getHoldGIL(optflgs))
        setIsHoldGIL(od);

    od -> next = NULL;

    /* Append to the list. */
    for (odp = headp; *odp != NULL; odp = &(*odp)->next)
        ;

    *odp = od;
}


/*
 * Return the Python name based on the C/C++ name and any /PyName/ annotation.
 */
static char *getPythonName(optFlags *optflgs, char *cname)
{
    char *pname;
    optFlag *of;

    if ((of = findOptFlag(optflgs, "PyName", name_flag)) != NULL)
        pname = of -> fvalue.sval;
    else
        pname = cname;

    return pname;
}


/*
 * Cache a name in a module.
 */
static nameDef *cacheName(sipSpec *pt,char *name)
{
    nameDef *nd;

    /* See if it already exists. */
    for (nd = pt -> namecache; nd != NULL; nd = nd -> next)
        if (strcmp(nd -> text,name) == 0)
            return nd;

    /* Create a new one. */
    nd = sipMalloc(sizeof (nameDef));

    nd -> nameflags = 0;
    nd -> module = currentSpec -> module;
    nd -> text = name;
    nd -> next = pt -> namecache;

    pt -> namecache = nd;

    return nd;
}


/*
 * Find (or create) an overloaded function name.
 */
static memberDef *findFunction(sipSpec *pt,moduleDef *mod,classDef *cd,
                   nameDef *pname,int hwcode,int nrargs)
{
    static struct slot_map {
        char *name;     /* The slot name. */
        slotType type;      /* The corresponding type. */
        int needs_hwcode;   /* If handwritten code is required. */
        int nrargs;     /* Nr. of arguments. */
    } slot_table[] = {
        {"__str__", str_slot, TRUE, 0},
        {"__unicode__", unicode_slot, TRUE, 0},
        {"__int__", int_slot, FALSE, 0},
        {"__long__", long_slot, FALSE, 0},
        {"__float__", float_slot, FALSE, 0},
        {"__len__", len_slot, TRUE, 0},
        {"__contains__", contains_slot, TRUE, 1},
        {"__add__", add_slot, FALSE, 1},
        {"__sub__", sub_slot, FALSE, 1},
        {"__mul__", mul_slot, FALSE, 1},
        {"__div__", div_slot, FALSE, 1},
        {"__mod__", mod_slot, FALSE, 1},
        {"__and__", and_slot, FALSE, 1},
        {"__or__", or_slot, FALSE, 1},
        {"__xor__", xor_slot, FALSE, 1},
        {"__lshift__", lshift_slot, FALSE, 1},
        {"__rshift__", rshift_slot, FALSE, 1},
        {"__iadd__", iadd_slot, FALSE, 1},
        {"__isub__", isub_slot, FALSE, 1},
        {"__imul__", imul_slot, FALSE, 1},
        {"__idiv__", idiv_slot, FALSE, 1},
        {"__imod__", imod_slot, FALSE, 1},
        {"__iand__", iand_slot, FALSE, 1},
        {"__ior__", ior_slot, FALSE, 1},
        {"__ixor__", ixor_slot, FALSE, 1},
        {"__ilshift__", ilshift_slot, FALSE, 1},
        {"__irshift__", irshift_slot, FALSE, 1},
        {"__invert__", invert_slot, FALSE, 0},
        {"__call__", call_slot, FALSE, -1},
        {"__getitem__", getitem_slot, FALSE, -1},
        {"__setitem__", setitem_slot, TRUE, -1},
        {"__delitem__", delitem_slot, TRUE, -1},
        {"__lt__", lt_slot, FALSE, 1},
        {"__le__", le_slot, FALSE, 1},
        {"__eq__", eq_slot, FALSE, 1},
        {"__ne__", ne_slot, FALSE, 1},
        {"__gt__", gt_slot, FALSE, 1},
        {"__ge__", ge_slot, FALSE, 1},
        {"__cmp__", cmp_slot, FALSE, 1},
        {"__nonzero__", nonzero_slot, TRUE, 0},
        {"__neg__", neg_slot, FALSE, 0},
        {"__pos__", pos_slot, FALSE, 0},
        {"__abs__", abs_slot, TRUE, 0},
        {"__repr__", repr_slot, TRUE, 0},
        {"__hash__", hash_slot, TRUE, 0},
        {NULL}
    };

    memberDef *md, **flist;
    struct slot_map *sm;
    slotType st;

    /* Get the slot type. */
    st = no_slot;

    for (sm = slot_table; sm -> name != NULL; ++sm)
        if (strcmp(sm -> name,pname -> text) == 0)
        {
            if (sm -> needs_hwcode && !hwcode)
                yyerror("This Python slot requires %MethodCode");

            if (sm -> nrargs < 0)
            {
                int min_nr;

                /* These require a minimum number. */
                switch (sm -> type)
                {
                case getitem_slot:
                case delitem_slot:
                    min_nr = 1;
                    break;

                case setitem_slot:
                    min_nr = 2;
                    break;

                default:
                    min_nr = 0;
                }

                if (nrargs < min_nr)
                    yyerror("Insufficient number of arguments to Python slot");
            }
            else if (cd == NULL)
            {
                /* Global operators need one extra argument. */
                if (sm -> nrargs + 1 != nrargs)
                    yyerror("Incorrect number of arguments to global operator");
            }
            else if (sm -> nrargs != nrargs)
                yyerror("Incorrect number of arguments to Python slot");

            st = sm -> type;

            break;
        }

    if (inMainModule())
        setIsUsedName(pname);

    /* Check there is no name clash. */
    checkAttributes(pt,cd,pname -> text,TRUE);

    /* See if it already exists. */
    flist = (cd != NULL ? &cd -> members : &pt -> othfuncs);

    for (md = *flist; md != NULL; md = md -> next)
        if (md -> pyname == pname && md -> module == mod)
            return md;

    /* Create a new one. */
    md = sipMalloc(sizeof (memberDef));

    md -> pyname = pname;
    md -> memberflags = 0;
    md -> slot = st;
    md -> module = mod;
    md -> next = *flist;

    *flist = md;

    /* Global operators are a subset. */
    if (cd == NULL && st != no_slot && st != neg_slot && st != pos_slot && !isNumberSlot(md) && !isRichCompareSlot(md))
        yyerror("Global operators must be either numeric or comparison operators");

    return md;
}


/*
 * Search a set of flags for a particular one and check its type.
 */
static optFlag *findOptFlag(optFlags *flgs,char *name,flagType ft)
{
    int f;

    for (f = 0; f < flgs -> nrFlags; ++f)
    {
        optFlag *of = &flgs -> flags[f];

        if (strcmp(of -> fname,name) == 0)
        {
            /*
             * An optional name can look like a boolean or a name.
             */

            if (ft == opt_name_flag)
            {
                if (of -> ftype == bool_flag)
                {
                    of -> ftype = opt_name_flag;
                    of -> fvalue.sval = NULL;
                }
                else if (of -> ftype == name_flag)
                    of -> ftype = opt_name_flag;
            }

            if (ft != of -> ftype)
                yyerror("Optional flag has a value of the wrong type");

            return of;
        }
    }

    return NULL;
}


/*
 * A name is going to be used as a Python attribute name within a Python scope
 * (ie. a Python dictionary), so check against what we already know is going in
 * the same scope in case there is a clash.
 */
static void checkAttributes(sipSpec *pt,classDef *pyscope,char *attr,int isfunc)
{
    enumDef *ed;
    varDef *vd;
    classDef *cd;

    /* Check the enums. */

    for (ed = pt -> enums; ed != NULL; ed = ed -> next)
    {
        enumMemberDef *emd;

        if (ed -> ecd != pyscope || ed -> pyname == NULL)
            continue;

        if (strcmp(ed->pyname->text, attr) == 0)
            yyerror("There is already an enum in scope with the same Python name");

        for (emd = ed -> members; emd != NULL; emd = emd -> next)
            if (strcmp(emd -> pyname -> text, attr) == 0)
                yyerror("There is already an enum member in scope with the same Python name");
    }

    /* Check the variables. */

    for (vd = pt -> vars; vd != NULL; vd = vd -> next)
    {
        if (vd -> ecd != pyscope)
            continue;

        if (strcmp(vd -> pyname -> text, attr) == 0)
            yyerror("There is already a variable in scope with the same Python name");
    }

    /*
     * Only check the members if this attribute isn't a member because we
     * can handle members with the same name in the same scope.
     */
    if (!isfunc)
    {
        memberDef *md, *membs;

        membs = (pyscope != NULL ? pyscope -> members : pt -> othfuncs);

        for (md = membs; md != NULL; md = md -> next)
        {
            overDef *od, *overs;

            if (strcmp(md -> pyname -> text, attr) != 0)
                continue;

            /* Check for a conflict with all overloads. */

            overs = (pyscope != NULL ? pyscope -> overs : pt -> overs);

            for (od = overs; od != NULL; od = od -> next)
            {
                if (od -> common != md)
                    continue;

                yyerror("There is already a function in scope with the same Python name");
            }
        }
    }

    /* Check the classes. */

    for (cd = pt -> classes; cd != NULL; cd = cd -> next)
    {
        if (cd -> ecd != pyscope || cd -> pyname == NULL)
            continue;

        if (strcmp(cd->pyname, attr) == 0 && !isExternal(cd))
            yyerror("There is already a class or namespace in scope with the same Python name");
    }

    /* Check the exceptions. */

    if (pyscope == NULL)
    {
        exceptionDef *xd;

        for (xd = pt->exceptions; xd != NULL; xd = xd->next)
            if (xd->pyname != NULL && strcmp(xd->pyname, attr) == 0)
                yyerror("There is already an exception with the same Python name");
    }
}


/*
 * Append a code block to a list of them.  Append is needed to give the
 * specifier easy control over the order of the documentation.
 */
static void appendCodeBlock(codeBlock **headp,codeBlock *new)
{
    while (*headp != NULL)
        headp = &(*headp) -> next;

    *headp = new;
}


/*
 * Handle the end of a fully parsed a file.
 */
static void handleEOF()
{
    /*
     * Check that the number of nested if's is the same as when we started
     * the file.
     */

    if (skipStackPtr > newContext.ifdepth)
        fatal("Too many %%If statements in %s\n",previousFile);

    if (skipStackPtr < newContext.ifdepth)
        fatal("Too many %%End statements in %s\n",previousFile);
}


/*
 * Handle the end of a fully parsed a module.
 */
static void handleEOM()
{
    /* Check it has been named. */

    if (currentModule -> name == NULL)
        fatal("No %%Module has been specified for module defined in %s\n",previousFile);

    /* The previous module is now current. */

    currentModule = newContext.prevmod;
}


/*
 * Find an existing qualifier.
 */
static qualDef *findQualifier(char *name)
{
    moduleDef *mod;

    for (mod = currentSpec -> modules; mod != NULL; mod = mod -> next)
    {
        qualDef *qd;

        for (qd = mod -> qualifiers; qd != NULL; qd = qd -> next)
            if (strcmp(qd -> name,name) == 0)
                return qd;
    }

    return NULL;
}


/*
 * Return a copy of a scoped name.
 */
scopedNameDef *copyScopedName(scopedNameDef *snd)
{
    scopedNameDef *head;

    head = NULL;

    while (snd != NULL)
    {
        appendScopedName(&head,text2scopePart(snd -> name));
        snd = snd -> next;
    }

    return head;
}


/*
 * Append a name to a list of scopes.
 */
void appendScopedName(scopedNameDef **headp,scopedNameDef *newsnd)
{
    while (*headp != NULL)
        headp = &(*headp) -> next;

    *headp = newsnd;
}


/*
 * Free a scoped name - but not the text itself.
 */
void freeScopedName(scopedNameDef *snd)
{
    while (snd != NULL)
    {
        scopedNameDef *next = snd -> next;

        free(snd);

        snd = next;
    }
}


/*
 * Convert a text string to a scope part structure.
 */
scopedNameDef *text2scopePart(char *text)
{
    scopedNameDef *snd;

    snd = sipMalloc(sizeof (scopedNameDef));

    snd -> name = text;
    snd -> next = NULL;

    return snd;
}


/*
 * Convert a text string to a fully scoped name.
 */
static scopedNameDef *text2scopedName(char *text)
{
    return scopeScopedName(text2scopePart(text));
}


/*
 * Prepend any current scope to a scoped name.
 */
static scopedNameDef *scopeScopedName(scopedNameDef *name)
{
    classDef *cd = currentScope();
    scopedNameDef *snd;

    snd = (cd != NULL ? copyScopedName(cd->iff->fqcname) : NULL);

    appendScopedName(&snd, name);

    return snd;
}


/*
 * Return a pointer to the tail part of a scoped name.
 */
char *scopedNameTail(scopedNameDef *snd)
{
    if (snd == NULL)
        return NULL;

    while (snd -> next != NULL)
        snd = snd -> next;

    return snd -> name;
}


/*
 * Push the given scope onto the scope stack.
 */
static void pushScope(classDef *scope)
{
    if (currentScopeIdx >= MAX_NESTED_SCOPE)
        fatal("Internal error: increase the value of MAX_NESTED_SCOPE\n");

    scopeStack[currentScopeIdx] = scope;
    sectFlagsStack[currentScopeIdx] = sectionFlags;

    ++currentScopeIdx;
}


/*
 * Pop the scope stack.
 */
static void popScope(void)
{
    if (currentScopeIdx > 0)
        sectionFlags = sectFlagsStack[--currentScopeIdx];
}


/*
 * Return non-zero if the current input should be parsed rather than be
 * skipped.
 */
static int notSkipping()
{
    return (skipStackPtr == 0 ? TRUE : skipStack[skipStackPtr - 1]);
}


/*
 * Return the value of an expression involving a time period.
 */
static int timePeriod(char *lname,char *uname)
{
    int this, line;
    qualDef *qd, *lower, *upper;
    moduleDef *mod;

    if (lname == NULL)
        lower = NULL;
    else if ((lower = findQualifier(lname)) == NULL || lower -> qtype != time_qualifier)
        yyerror("Lower bound is not a time version");

    if (uname == NULL)
        upper = NULL;
    else if ((upper = findQualifier(uname)) == NULL || upper -> qtype != time_qualifier)
        yyerror("Upper bound is not a time version");

    /* Sanity checks on the bounds. */

    if (lower == NULL && upper == NULL)
        yyerror("Lower and upper bounds cannot both be omitted");

    if (lower != NULL && upper != NULL)
    {
        if (lower -> module != upper -> module || lower -> line != upper -> line)
            yyerror("Lower and upper bounds are from different timelines");

        if (lower == upper)
            yyerror("Lower and upper bounds must be different");

        if (lower -> order > upper -> order)
            yyerror("Later version specified as lower bound");
    }

    /* Go through each slot in the relevant timeline. */

    if (lower != NULL)
    {
        mod = lower -> module;
        line = lower -> line;
    }
    else
    {
        mod = upper -> module;
        line = upper -> line;
    }

    this = FALSE;

    for (qd = mod -> qualifiers; qd != NULL; qd = qd -> next)
    {
        if (qd -> qtype != time_qualifier || qd -> line != line)
            continue;

        if (lower != NULL && qd -> order < lower -> order)
            continue;

        if (upper != NULL && qd -> order >= upper -> order)
            continue;

        /*
         * This is within the required range so if it is also needed
         * then the expression is true.
         */

        if (isNeeded(qd))
        {
            this = TRUE;
            break;
        }
    }

    return this;
}


/*
 * Return the value of an expression involving a single platform or feature.
 */
static int platOrFeature(char *name,int optnot)
{
    int this;
    qualDef *qd;

    if ((qd = findQualifier(name)) == NULL || qd -> qtype == time_qualifier)
        yyerror("No such platform or feature");

    /* Assume this sub-expression is false. */

    this = FALSE;

    if (qd -> qtype == feature_qualifier)
    {
        if (!excludedFeature(excludedQualifiers,qd))
            this = TRUE;
    }
    else if (isNeeded(qd))
        this = TRUE;

    if (optnot)
        this = !this;

    return this;
}


/*
 * Return TRUE if the given qualifier is excluded.
 */
int excludedFeature(stringList *xsl,qualDef *qd)
{
    while (xsl != NULL)
    {
        if (strcmp(qd -> name,xsl -> s) == 0)
            return TRUE;

        xsl = xsl -> next;
    }

    return FALSE;
}


/*
 * Return TRUE if the given qualifier is needed.
 */
static int isNeeded(qualDef *qd)
{
    stringList *sl;

    for (sl = neededQualifiers; sl != NULL; sl = sl -> next)
        if (strcmp(qd -> name,sl -> s) == 0)
            return TRUE;

    return FALSE;
}


/*
 * Return the current scope.  currentScope() is only valid if notSkipping()
 * returns non-zero.
 */
static classDef *currentScope(void)
{
    return (currentScopeIdx > 0 ? scopeStack[currentScopeIdx - 1] : NULL);
}


/*
 * Create a new qualifier.
 */
static void newQualifier(moduleDef *mod,int line,int order,char *name,qualType qt)
{
    qualDef *qd;

    /* Check it doesn't already exist. */

    if (findQualifier(name) != NULL)
        yyerror("Version is already defined");

    qd = sipMalloc(sizeof (qualDef));
    qd -> name = name;
    qd -> qtype = qt;
    qd -> module = mod;
    qd -> line = line;
    qd -> order = order;
    qd -> next = mod -> qualifiers;
    mod -> qualifiers = qd;
}


/*
 * Create a new imported module.
 */
static void newImport(char *name)
{
    moduleDef *from, *mod;
    moduleListDef *mld;

    /* Create a new module if it has already been imported. */
    for (mod = currentSpec -> modules; mod != NULL; mod = mod -> next)
        if (strcmp(mod -> file,name) == 0)
            break;

    from = currentModule;

    if (mod == NULL)
    {
        newModule(NULL,name);
        mod = currentModule;
    }

    /* Add the new import unless it has already been imported. */
    for (mld = from->imports; mld != NULL; mld = mld->next)
        if (mld->module == mod)
            return;

    mld = sipMalloc(sizeof (moduleListDef));
    mld -> module = mod;
    mld -> next = from->imports;

    from->imports = mld;
}


/*
 * Set up pointers to hook names.
 */
static void getHooks(optFlags *optflgs,char **pre,char **post)
{
    optFlag *of;

    if ((of = findOptFlag(optflgs,"PreHook",name_flag)) != NULL)
        *pre = of -> fvalue.sval;
    else
        *pre = NULL;

    if ((of = findOptFlag(optflgs,"PostHook",name_flag)) != NULL)
        *post = of -> fvalue.sval;
    else
        *post = NULL;
}


/*
 * Get the /ReleaseGIL/ option flag.
 */
static int getReleaseGIL(optFlags *optflgs)
{
    return (findOptFlag(optflgs, "ReleaseGIL", bool_flag) != NULL);
}


/*
 * Get the /HoldGIL/ option flag.
 */
static int getHoldGIL(optFlags *optflgs)
{
    return (findOptFlag(optflgs, "HoldGIL", bool_flag) != NULL);
}


/*
 * Return TRUE if the QtNoEmitters option was specified.
 */
int optNoEmitters(sipSpec *pt)
{
    return optFind(pt, "QtNoEmitters");
}


/*
 * Return TRUE if the QtRegisterTypes option was specified.
 */
int optRegisterTypes(sipSpec *pt)
{
    return optFind(pt, "QtRegisterTypes");
}


/*
 * Return TRUE if the Qt4Q_OBJECT option was specified.
 */
int optQ_OBJECT4(sipSpec *pt)
{
    return optFind(pt, "Qt4Q_OBJECT");
}


/*
 * Return TRUE if a particular option was specified with %SIPOptions.
 */
static int optFind(sipSpec *pt, const char *opt)
{
    stringList *sl;

    for (sl = pt->options; sl != NULL; sl = sl->next)
        if (strcmp(sl->s, opt) == 0)
            return TRUE;

    return FALSE;
}
