/* * The SIP parser. * * Copyright (c) 2007 * Riverbank Computing Limited * * 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 #include #include #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 TK_CODELINE %token TK_IF %token TK_END %token TK_NAME %token TK_PATHNAME %token 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 TK_NUMBER %token TK_REAL %token TK_TYPEDEF %token TK_NAMESPACE %token TK_TIMELINE %token TK_PLATFORMS %token TK_FEATURE %token TK_LICENSE %token 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 argvalue %type argtype %type cpptype %type basetype %type template %type arglist %type rawarglist %type cpptypelist %type optsig %type optctorsig %type optexceptions %type exceptionlist %type optslot %type optref %type optconst %type optvirtual %type optabstract %type deref %type optnumber %type simplevalue %type value %type expr %type optassign %type optaccesscode %type optgetcode %type optsetcode %type exphdrcode %type modhdrcode %type typehdrcode %type opttypehdrcode %type travcode %type clearcode %type readbufcode %type writebufcode %type segcountcode %type charbufcode %type modcode %type typecode %type codeblock %type codelines %type virtualcatchercode %type methodcode %type raisecode %type operatorname %type optfilename %type optname %type modname %type optflags %type flaglist %type flag %type flagvalue %type optunop %type binop %type scopepart %type scopedname %type exprlist %type qualifiers %type oredqualifiers %type modlang %type optclassbody %type baseexception %type 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(¤tSpec->exphdrcode, $1); } | modhdrcode { if (notSkipping() && inMainModule()) appendCodeBlock(¤tSpec -> hdrcode,$1); } | modcode { if (notSkipping() && inMainModule()) appendCodeBlock(¤tSpec -> 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(¤tSpec->options, $1); } | optionlist ',' TK_NAME { appendString(¤tSpec->options, $3); } ; noemitters: TK_NOEMITTERS { if (notSkipping()) { yywarning("%SIPNoEmitters is deprecated, please use %SIPOptions instead"); appendString(¤tSpec->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(¤tSpec->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(¤tMappedType -> 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(¤tSpec -> 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(¤tSpec -> preinitcode,$2); } ; postinitcode: TK_POSTINITCODE codeblock { if (notSkipping() && inMainModule()) appendCodeBlock(¤tSpec -> postinitcode,$2); } ; unitcode: TK_UNITCODE codeblock { if (notSkipping() && inMainModule()) appendCodeBlock(¤tSpec->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(¤tSpec -> docs,$2); } ; exporteddoc: TK_EXPORTEDDOC codeblock { appendCodeBlock(¤tSpec -> 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 = ¤tEnum->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(¤tScope() -> cppcode,$1); } | typehdrcode { if (notSkipping()) appendCodeBlock(¤tScope() -> 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) ? ¤tScope()->iff->used : ¤tSpec->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; }