/* * The SIP parser. * * Copyright (c) 2010 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. * * This copy of SIP may also used under the terms of the GNU General Public * License v2 or v3 as published by the Free Software Foundation which can be * found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package. * * 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 || currentModule->container != NULL) 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 int currentIsSignal; /* Set if the current is Q_SIGNAL. */ static int currentIsSlot; /* Set if the current is Q_SLOT. */ static int currentIsTemplate; /* Set if the current is a template. */ static char *previousFile; /* The file just parsed. */ static parserContext currentContext; /* The current 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 classList *currentSupers; /* The current super-class list. */ static int defaultKwdArgs; /* Support keyword arguments by default. */ static int makeProtPublic; /* Treat protected items as public. */ static const char *getPythonName(optFlags *optflgs, const char *cname); static classDef *findClass(sipSpec *pt, ifaceFileType iftype, apiVersionRangeDef *api_range, scopedNameDef *fqname); static classDef *findClassWithInterface(sipSpec *pt, ifaceFileDef *iff); static classDef *newClass(sipSpec *pt, ifaceFileType iftype, apiVersionRangeDef *api_range, scopedNameDef *snd); static void finishClass(sipSpec *, moduleDef *, classDef *, optFlags *); static exceptionDef *findException(sipSpec *pt, scopedNameDef *fqname, int new); static mappedTypeDef *newMappedType(sipSpec *,argDef *, optFlags *); static enumDef *newEnum(sipSpec *pt, moduleDef *mod, mappedTypeDef *mt_scope, char *name, optFlags *of, int flags); static void instantiateClassTemplate(sipSpec *pt, moduleDef *mod, classDef *scope, scopedNameDef *fqname, classTmplDef *tcd, templateDef *td); static void newTypedef(sipSpec *, moduleDef *, char *, argDef *, optFlags *); static void newVar(sipSpec *, moduleDef *, char *, int, argDef *, optFlags *, codeBlock *, codeBlock *, codeBlock *); static void newCtor(char *, int, signatureDef *, optFlags *, codeBlock *, throwArgs *, signatureDef *, int, codeBlock *); static void newFunction(sipSpec *, moduleDef *, classDef *, mappedTypeDef *, int, int, int, int, int, char *, signatureDef *, int, int, optFlags *, codeBlock *, codeBlock *, throwArgs *, signatureDef *, codeBlock *); static optFlag *findOptFlag(optFlags *,char *,flagType); static memberDef *findFunction(sipSpec *, moduleDef *, classDef *, mappedTypeDef *, const char *, int, int, int); static void checkAttributes(sipSpec *, moduleDef *, classDef *, mappedTypeDef *, const char *, int); static void newModule(FILE *fp, char *filename); static moduleDef *allocModule(); static void parseFile(FILE *fp, char *name, moduleDef *prevmod, int optional); static void handleEOF(void); static void handleEOM(void); static qualDef *findQualifier(const char *name); static scopedNameDef *text2scopedName(ifaceFileDef *scope, char *text); static scopedNameDef *scopeScopedName(ifaceFileDef *scope, 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 *filename); 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 getTransfer(optFlags *optflgs); static int getReleaseGIL(optFlags *optflgs); static int getHoldGIL(optFlags *optflgs); static int getDeprecated(optFlags *optflgs); static int getAllowNone(optFlags *optflgs); static const char *getDocType(optFlags *optflgs); static const char *getDocValue(optFlags *optflgs); 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 *type2string(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 stringFind(stringList *sl, const char *s); static void setModuleName(sipSpec *pt, moduleDef *mod, const char *fullname); static int foundInScope(scopedNameDef *fq_name, scopedNameDef *rel_name); static void defineClass(scopedNameDef *snd, classList *supers, optFlags *of); static classDef *completeClass(scopedNameDef *snd, optFlags *of, int has_def); static memberDef *instantiateTemplateMethods(memberDef *tmd, moduleDef *mod); static void instantiateTemplateEnums(sipSpec *pt, classTmplDef *tcd, templateDef *td, classDef *cd, ifaceFileList **used, scopedNameDef *type_names, scopedNameDef *type_values); static void instantiateTemplateVars(sipSpec *pt, classTmplDef *tcd, templateDef *td, classDef *cd, ifaceFileList **used, scopedNameDef *type_names, scopedNameDef *type_values); static overDef *instantiateTemplateOverloads(sipSpec *pt, overDef *tod, memberDef *tmethods, memberDef *methods, classTmplDef *tcd, templateDef *td, classDef *cd, ifaceFileList **used, scopedNameDef *type_names, scopedNameDef *type_values); static void resolveAnyTypedef(sipSpec *pt, argDef *ad); static void addVariable(sipSpec *pt, varDef *vd); static void applyTypeFlags(moduleDef *mod, argDef *ad, optFlags *flags); static argType convertEncoding(const char *encoding); static apiVersionRangeDef *getAPIRange(optFlags *optflgs); static apiVersionRangeDef *convertAPIRange(moduleDef *mod, nameDef *name, int from, int to); static char *convertFeaturedString(char *fs); static scopedNameDef *text2scopePart(char *text); static int usesKeywordArgs(optFlags *optflgs, signatureDef *sd); static char *strip(char *s); static int isEnabledFeature(const char *name); %} %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_API %token TK_DEFENCODING %token TK_PLUGIN %token TK_DOCSTRING %token TK_DOC %token TK_EXPORTEDDOC %token TK_MAKEFILE %token TK_ACCESSCODE %token TK_GETCODE %token TK_SETCODE %token TK_PREINITCODE %token TK_INITCODE %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_GETBUFFERCODE %token TK_RELEASEBUFFERCODE %token TK_READBUFFERCODE %token TK_WRITEBUFFERCODE %token TK_SEGCOUNTCODE %token TK_CHARBUFFERCODE %token TK_PICKLECODE %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_CONSMODULE %token TK_COMPOMODULE %token TK_CLASS %token TK_STRUCT %token TK_PUBLIC %token TK_PROTECTED %token TK_PRIVATE %token TK_SIGNALS %token TK_SIGNAL_METHOD %token TK_SLOTS %token TK_SLOT_METHOD %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 %token TK_DEFMETATYPE %token TK_DEFSUPERTYPE %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 getbufcode %type releasebufcode %type readbufcode %type writebufcode %type segcountcode %type charbufcode %type picklecode %type modcode %type typecode %type codeblock %type codelines %type virtualcatchercode %type methodcode %type raisecode %type docstring %type optdocstring %type operatorname %type optfilename %type optname %type dottedname %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 (currentContext.prevmod != NULL) handleEOM(); free(previousFile); previousFile = NULL; } } modstatement ; modstatement: module | consmodule | compmodule | plugin | copying | include | optinclude | import | api | timeline | platforms | feature | license | defencoding | defmetatype | defsupertype | exphdrcode { if (notSkipping()) appendCodeBlock(¤tSpec->exphdrcode, $1); } | modhdrcode { if (notSkipping()) appendCodeBlock(¤tModule->hdrcode, $1); } | modcode { if (notSkipping()) appendCodeBlock(¤tModule->cppcode, $1); } | preinitcode | initcode | 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->iff->hdrcode, $1); } } ; defencoding: TK_DEFENCODING TK_STRING { if (notSkipping()) { if ((currentModule->encoding = convertEncoding($2)) == no_type) yyerror("The value of %DefaultEncoding must be one of \"ASCII\", \"Latin-1\", \"UTF-8\" or \"None\""); } } ; plugin: TK_PLUGIN TK_NAME { appendString(¤tSpec->plugins, $2); } ; api: TK_API TK_NAME TK_NUMBER { if (notSkipping()) { apiVersionRangeDef *avd; if (findAPI(currentSpec, $2) != NULL) yyerror("The API name in the %API directive has already been defined"); if ($3 < 1) yyerror("The version number in the %API directive must be greater than or equal to 1"); avd = sipMalloc(sizeof (apiVersionRangeDef)); avd->api_name = cacheName(currentSpec, $2); avd->from = $3; avd->to = -1; avd->next = currentModule->api_versions; currentModule->api_versions = avd; if (inMainModule()) setIsUsedName(avd->api_name); } } ; exception: TK_EXCEPTION scopedname baseexception optflags '{' opttypehdrcode raisecode '}' ';' { if (notSkipping()) { exceptionDef *xd; const char *pyname; if (currentSpec->genc) yyerror("%Exception not allowed in a C module"); pyname = getPythonName(&$4, scopedNameTail($2)); checkAttributes(currentSpec, currentModule, NULL, 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->iff->hdrcode = $6; xd->pyname = pyname; xd->bibase = $3.bibase; xd->base = $3.base; xd->raisecode = $7; if (findOptFlag(&$4, "Default", bool_flag) != NULL) currentModule->defexception = xd; if (xd->bibase != NULL || xd->base != NULL) xd->exceptionnr = currentModule->nrexceptions++; } } ; 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 (compareScopedNames(xd->iff->fqcname, $2) == 0) { $$.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 optflags { if (notSkipping()) currentMappedType = newMappedType(currentSpec, &$2, &$3); } mtdefinition ; mappedtypetmpl: template TK_MAPPEDTYPE basetype optflags { int a; if (currentSpec->genc) yyerror("%MappedType templates not allowed in a C module"); /* Check the template arguments are basic types or simple names. */ for (a = 0; a < $1.nrArgs; ++a) { argDef *ad = &$1.args[a]; if (ad->atype == defined_type && ad->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; ifaceFileDef *iff; /* Check a template hasn't already been provided. */ for (mtt = currentSpec->mappedtypetemplates; mtt != NULL; mtt = mtt->next) if (compareScopedNames(mtt->mt->type.u.td->fqname, $3.u.td->fqname) == 0 && 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(currentSpec, &$3); mtt->mt->doctype = getDocType(&$4); mtt->next = currentSpec->mappedtypetemplates; currentSpec->mappedtypetemplates = mtt; currentMappedType = mtt->mt; /* Create a dummy interface file. */ iff = sipMalloc(sizeof (ifaceFileDef)); iff->hdrcode = NULL; mtt->mt->iff = iff; } } 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->iff->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; } } | enum | mtfunction ; mtfunction: TK_STATIC cpptype TK_NAME '(' arglist ')' optconst optexceptions optflags optsig ';' optdocstring methodcode { if (notSkipping()) { applyTypeFlags(currentModule, &$2, &$9); $5.result = $2; newFunction(currentSpec, currentModule, NULL, currentMappedType, 0, TRUE, FALSE, FALSE, FALSE, $3, &$5, $7, FALSE, &$9, $13, NULL, $8, $10, $12); } } ; namespace: TK_NAMESPACE TK_NAME { if (currentSpec -> genc) yyerror("namespace definition not allowed in a C module"); if (notSkipping()) { classDef *ns, *c_scope; ifaceFileDef *scope; if ((c_scope = currentScope()) != NULL) scope = c_scope->iff; else scope = NULL; ns = newClass(currentSpec, namespace_iface, NULL, text2scopedName(scope, $2)); pushScope(ns); sectionFlags = 0; } } '{' nsbody '}' ';' { if (notSkipping()) { if (inMainModule()) { classDef *ns = currentScope(); setIsUsedName(ns->iff->name); setIsUsedName(ns->pyname); } 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; } ; defmetatype:TK_DEFMETATYPE dottedname { if (notSkipping()) { if (currentModule->defmetatype != NULL) yyerror("%DefaultMetatype has already been defined for this module"); currentModule->defmetatype = cacheName(currentSpec, $2); } } ; defsupertype: TK_DEFSUPERTYPE dottedname { if (notSkipping()) { if (currentModule->defsupertype != NULL) yyerror("%DefaultSupertype has already been defined for this module"); currentModule->defsupertype = cacheName(currentSpec, $2); } } ; consmodule: TK_CONSMODULE dottedname { /* Make sure this is the first mention of a module. */ if (currentSpec->module != currentModule) yyerror("A %ConsolidatedModule cannot be %Imported"); if (currentModule->fullname != NULL) yyerror("%ConsolidatedModule must appear before any %Module or %CModule directive"); setModuleName(currentSpec, currentModule, $2); setIsConsolidated(currentModule); } ; compmodule: TK_COMPOMODULE dottedname { /* Make sure this is the first mention of a module. */ if (currentSpec->module != currentModule) yyerror("A %CompositeModule cannot be %Imported"); if (currentModule->fullname != NULL) yyerror("%CompositeModule must appear before any %Module or %CModule directive"); setModuleName(currentSpec, currentModule, $2); setIsComposite(currentModule); } ; module: modlang dottedname 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->text, $2) == 0) yyerror("Module is already defined"); /* * If we are in a container module then create a component module * and make it current. */ if (isContainer(currentModule) || currentModule->container != NULL) { mod = allocModule(); mod->file = currentContext.filename; mod->container = (isContainer(currentModule) ? currentModule : currentModule->container); currentModule = mod; } setModuleName(currentSpec, currentModule, $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; } ; dottedname: TK_NAME | TK_PATHNAME { /* * The grammar design is a bit broken and this is the easiest way * to allow periods in names. */ char *cp; for (cp = $1; *cp != '\0'; ++cp) if (*cp != '.' && *cp != '_' && !isalnum(*cp)) yyerror("Invalid character in 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 { appendCodeBlock(¤tModule->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; } ; getbufcode: TK_GETBUFFERCODE codeblock { $$ = $2; } ; releasebufcode: TK_RELEASEBUFFERCODE codeblock { $$ = $2; } ; readbufcode: TK_READBUFFERCODE codeblock { $$ = $2; } ; writebufcode: TK_WRITEBUFFERCODE codeblock { $$ = $2; } ; segcountcode: TK_SEGCOUNTCODE codeblock { $$ = $2; } ; charbufcode: TK_CHARBUFFERCODE codeblock { $$ = $2; } ; picklecode: TK_PICKLECODE codeblock { $$ = $2; } ; modcode: TK_MODCODE codeblock { $$ = $2; } ; typecode: TK_TYPECODE codeblock { $$ = $2; } ; preinitcode: TK_PREINITCODE codeblock { if (notSkipping()) appendCodeBlock(¤tModule->preinitcode, $2); } ; initcode: TK_INITCODE codeblock { if (notSkipping()) appendCodeBlock(¤tModule->initcode, $2); } ; postinitcode: TK_POSTINITCODE codeblock { if (notSkipping()) appendCodeBlock(¤tModule->postinitcode, $2); } ; unitcode: TK_UNITCODE codeblock { if (notSkipping()) appendCodeBlock(¤tModule->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((char *)$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, currentMappedType, $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()) { enumMemberDef *emd, **tail; /* Note that we don't use the assigned value. */ emd = sipMalloc(sizeof (enumMemberDef)); emd -> pyname = cacheName(currentSpec, getPythonName(&$3, $1)); emd -> cname = $1; emd -> ed = currentEnum; emd -> next = NULL; checkAttributes(currentSpec, currentModule, emd->ed->ecd, emd->ed->emtd, 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 optflags ';' { if (notSkipping()) { applyTypeFlags(currentModule, &$2, &$4); newTypedef(currentSpec, currentModule, $3, &$2, &$4); } } | TK_TYPEDEF cpptype '(' deref TK_NAME ')' '(' cpptypelist ')' optflags ';' { if (notSkipping()) { signatureDef *sig; argDef ftype; applyTypeFlags(currentModule, &$2, &$10); memset(&ftype, 0, sizeof (argDef)); /* 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.nrderefs = $4; ftype.u.sa = sig; newTypedef(currentSpec, currentModule, $5, &ftype, &$10); } } ; struct: TK_STRUCT scopedname { if (currentSpec -> genc && $2->next != NULL) yyerror("Namespaces not allowed in a C module"); if (notSkipping()) currentSupers = NULL; } superclasses optflags { if (notSkipping()) { if (currentSpec->genc && currentSupers != NULL) yyerror("Super-classes not allowed in a C module struct"); defineClass($2, currentSupers, &$5); sectionFlags = SECT_IS_PUBLIC; } } optclassbody ';' { if (notSkipping()) completeClass($2, &$5, $7); } ; classtmpl: template {currentIsTemplate = TRUE;} 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 = $3; tcd->next = currentSpec->classtemplates; currentSpec->classtemplates = tcd; } currentIsTemplate = FALSE; } ; template: TK_TEMPLATE '<' cpptypelist '>' { $$ = $3; } ; class: TK_CLASS scopedname { if (currentSpec->genc) yyerror("Class definition not allowed in a C module"); if (notSkipping()) currentSupers = NULL; } superclasses optflags { if (notSkipping()) { defineClass($2, currentSupers, &$5); sectionFlags = SECT_IS_PRIVATE; } } optclassbody ';' { if (notSkipping()) $$ = completeClass($2, &$5, $7); } ; superclasses: | ':' superlist ; superlist: superclass | superlist ',' superclass ; superclass: scopedname { if (notSkipping()) { argDef ad; classDef *super; scopedNameDef *snd = $1; /* * This is a hack to allow typedef'ed classes to be used before * we have resolved the typedef definitions. Unlike elsewhere, * we require that the typedef is defined before being used. */ for (;;) { ad.atype = no_type; ad.argflags = 0; ad.nrderefs = 0; ad.original_type = NULL; searchTypedefs(currentSpec, snd, &ad); if (ad.atype != defined_type) break; if (ad.nrderefs != 0 || isConstArg(&ad) || isReference(&ad)) break; snd = ad.u.snd; } if (ad.atype != no_type) yyerror("Super-class list contains an invalid type"); super = findClass(currentSpec, class_iface, NULL, snd); appendToClassList(¤tSupers, super); } } ; optclassbody: { $$ = FALSE; } | '{' classbody '}' { $$ = TRUE; } ; classbody: classline | classbody classline ; classline: ifstart | ifend | namespace | struct | class | exception | typedef | enum | docstring { if (notSkipping()) { classDef *scope = currentScope(); /* Make sure this is before any ctor docstrings. */ $1->next = scope->docstring; scope->docstring = $1; } } | typecode { if (notSkipping()) appendCodeBlock(¤tScope()->cppcode, $1); } | typehdrcode { if (notSkipping()) appendCodeBlock(¤tScope()->iff->hdrcode, $1); } | travcode { if (notSkipping()) { classDef *scope = currentScope(); if (scope->travcode != NULL) yyerror("%GCTraverseCode already given for class"); scope->travcode = $1; } } | clearcode { if (notSkipping()) { classDef *scope = currentScope(); if (scope->clearcode != NULL) yyerror("%GCClearCode already given for class"); scope->clearcode = $1; } } | getbufcode { if (notSkipping()) { classDef *scope = currentScope(); if (scope->getbufcode != NULL) yyerror("%BIGetBufferCode already given for class"); scope->getbufcode = $1; } } | releasebufcode { if (notSkipping()) { classDef *scope = currentScope(); if (scope->releasebufcode != NULL) yyerror("%BIReleaseBufferCode already given for class"); scope->releasebufcode = $1; } } | readbufcode { if (notSkipping()) { classDef *scope = currentScope(); if (scope->readbufcode != NULL) yyerror("%BIGetReadBufferCode already given for class"); scope->readbufcode = $1; } } | writebufcode { if (notSkipping()) { classDef *scope = currentScope(); if (scope->writebufcode != NULL) yyerror("%BIGetWriteBufferCode already given for class"); scope->writebufcode = $1; } } | segcountcode { if (notSkipping()) { classDef *scope = currentScope(); if (scope->segcountcode != NULL) yyerror("%BIGetSegCountCode already given for class"); scope->segcountcode = $1; } } | charbufcode { if (notSkipping()) { classDef *scope = currentScope(); if (scope->charbufcode != NULL) yyerror("%BIGetCharBufferCode already given for class"); scope->charbufcode = $1; } } | picklecode { if (notSkipping()) { classDef *scope = currentScope(); if (scope->picklecode != NULL) yyerror("%PickleCode already given for class"); scope->picklecode = $1; } } | ctor | dtor | varmember | TK_TOSUBCLASS codeblock { if (notSkipping()) { classDef *scope = currentScope(); if (scope->convtosubcode != NULL) yyerror("Class has more than one %ConvertToSubClassCode directive"); scope->convtosubcode = $2; } } | TK_TOTYPE codeblock { if (notSkipping()) { classDef *scope = currentScope(); if (scope->convtocode != NULL) yyerror("Class has more than one %ConvertToTypeCode directive"); scope->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; /* * Note that we don't apply the protected/public hack to dtors * as it (I think) may change the behaviour of the wrapped API. */ 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 ';' optdocstring methodcode { /* Note that we allow ctors in C modules. */ if (notSkipping()) { if (currentSpec -> genc) { if ($10 == 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, $10, $5, $7, currentCtorIsExplicit, $9); } 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 ';' optdocstring methodcode virtualcatchercode { if (notSkipping()) { applyTypeFlags(currentModule, &$1, &$9); $4.result = $1; newFunction(currentSpec, currentModule, currentScope(), NULL, sectionFlags, currentIsStatic, currentIsSignal, currentIsSlot, currentOverIsVirt, $2, &$4, $6, $8, &$9, $13, $14, $7, $10, $12); } currentIsStatic = FALSE; currentIsSignal = FALSE; currentIsSlot = FALSE; currentOverIsVirt = FALSE; } | cpptype TK_OPERATOR '=' '(' cpptype ')' ';' { /* * It looks like an assignment operator (though we don't bother to * check the types) so make sure it is private. */ if (notSkipping()) { classDef *cd = currentScope(); if (cd == NULL || !(sectionFlags & SECT_IS_PRIVATE)) yyerror("Assignment operators may only be defined as private"); setCannotAssign(cd); } currentIsStatic = FALSE; currentIsSignal = FALSE; currentIsSlot = FALSE; currentOverIsVirt = FALSE; } | cpptype TK_OPERATOR operatorname '(' arglist ')' optconst optexceptions optabstract optflags optsig ';' methodcode virtualcatchercode { if (notSkipping()) { classDef *cd = currentScope(); applyTypeFlags(currentModule, &$1, &$10); /* 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, cd, NULL, sectionFlags, currentIsStatic, currentIsSignal, currentIsSlot, currentOverIsVirt, $3, &$5, $7, $9, &$10, $13, $14, $8, $11, NULL); } currentIsStatic = FALSE; currentIsSignal = FALSE; currentIsSlot = FALSE; currentOverIsVirt = FALSE; } | TK_OPERATOR cpptype '(' arglist ')' optconst optexceptions optabstract optflags optsig ';' methodcode virtualcatchercode { if (notSkipping()) { char *sname; classDef *scope = currentScope(); if (scope == NULL || $4.nrArgs != 0) yyerror("Operator casts must be specified in a class and have no arguments"); applyTypeFlags(currentModule, &$2, &$9); 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, scope, NULL, sectionFlags, currentIsStatic, currentIsSignal, currentIsSlot, currentOverIsVirt, sname, &$4, $6, $8, &$9, $12, $13, $7, $10, NULL); } else { argList *al; /* Check it doesn't already exist. */ for (al = scope->casts; al != NULL; al = al->next) if (compareScopedNames($2.u.snd, al->arg.u.snd) == 0) 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; currentIsSignal = FALSE; currentIsSlot = 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: dottedname { $$.ftype = (strchr($1, '.') != NULL) ? dotted_name_flag : name_flag; $$.fvalue.sval = $1; } | TK_NAME ':' optnumber '-' optnumber { apiVersionRangeDef *avd; int from, to; $$.ftype = api_range_flag; /* Check that the API is known. */ if ((avd = findAPI(currentSpec, $1)) == NULL) yyerror("unknown API name in API annotation"); if (inMainModule()) setIsUsedName(avd->api_name); /* Unbounded values are represented by 0. */ if ((from = $3) < 0) from = 0; if ((to = $5) < 0) to = 0; $$.fvalue.aval = convertAPIRange(currentModule, avd->api_name, from, to); } | TK_STRING { $$.ftype = string_flag; $$.fvalue.sval = convertFeaturedString($1); } | TK_NUMBER { $$.ftype = integer_flag; $$.fvalue.ival = $1; } ; docstring: TK_DOCSTRING codeblock { $$ = $2; } ; optdocstring: { $$ = NULL; } | docstring ; 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 optflags optassign { $$.atype = signal_type; $$.argflags = ARG_IS_CONST; $$.nrderefs = 0; $$.name = cacheName(currentSpec, $2); $$.defval = $4; currentSpec -> sigslots = TRUE; } | TK_SIPSLOT optname optflags optassign { $$.atype = slot_type; $$.argflags = ARG_IS_CONST; $$.nrderefs = 0; $$.name = cacheName(currentSpec, $2); $$.defval = $4; currentSpec -> sigslots = TRUE; } | TK_SIPANYSLOT optname optflags optassign { $$.atype = anyslot_type; $$.argflags = ARG_IS_CONST; $$.nrderefs = 0; $$.name = cacheName(currentSpec, $2); $$.defval = $4; currentSpec -> sigslots = TRUE; } | TK_SIPRXCON optname optflags { $$.atype = rxcon_type; $$.argflags = 0; $$.nrderefs = 0; $$.name = cacheName(currentSpec, $2); if (findOptFlag(&$3, "SingleShot", bool_flag) != NULL) $$.argflags |= ARG_SINGLE_SHOT; currentSpec -> sigslots = TRUE; } | TK_SIPRXDIS optname optflags { $$.atype = rxdis_type; $$.argflags = 0; $$.nrderefs = 0; $$.name = cacheName(currentSpec, $2); currentSpec -> sigslots = TRUE; } | TK_SIPSLOTCON '(' arglist ')' optname optflags { $$.atype = slotcon_type; $$.argflags = ARG_IS_CONST; $$.nrderefs = 0; $$.name = cacheName(currentSpec, $5); memset(&$3.result, 0, sizeof (argDef)); $3.result.atype = void_type; $$.u.sa = sipMalloc(sizeof (signatureDef)); *$$.u.sa = $3; currentSpec -> sigslots = TRUE; } | TK_SIPSLOTDIS '(' arglist ')' optname optflags { $$.atype = slotdis_type; $$.argflags = ARG_IS_CONST; $$.nrderefs = 0; $$.name = cacheName(currentSpec, $5); memset(&$3.result, 0, sizeof (argDef)); $3.result.atype = void_type; $$.u.sa = sipMalloc(sizeof (signatureDef)); *$$.u.sa = $3; currentSpec -> sigslots = TRUE; } | TK_QOBJECT optname optflags { $$.atype = qobject_type; $$.argflags = 0; $$.nrderefs = 0; $$.name = cacheName(currentSpec, $2); } | argtype optassign { $$ = $1; $$.defval = $2; } ; varmember: TK_SIGNAL_METHOD {currentIsSignal = TRUE;} simple_varmem | TK_SLOT_METHOD {currentIsSlot = TRUE;} simple_varmem | simple_varmem ; simple_varmem: 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"); applyTypeFlags(currentModule, &$1, &$3); 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; } | basetype deref optref { $$ = $1; $$.nrderefs += $2; $$.argflags |= $3; } ; argtype: cpptype optname optflags { $$ = $1; $$.name = cacheName(currentSpec, $2); if (getAllowNone(&$3)) $$.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 (getTransfer(&$3)) $$.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, "KeepReference", bool_flag) != NULL) { $$.argflags |= ARG_KEEP_REF; $$.key = currentModule->next_key++; } if (findOptFlag(&$3,"In",bool_flag) != NULL) $$.argflags |= ARG_IN; if (findOptFlag(&$3,"Out",bool_flag) != NULL) $$.argflags |= ARG_OUT; if (findOptFlag(&$3, "ResultSize", bool_flag) != NULL) $$.argflags |= ARG_RESULT_SIZE; if (findOptFlag(&$3, "NoCopy", bool_flag) != NULL) $$.argflags |= ARG_NO_COPY; 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; } } applyTypeFlags(currentModule, &$$, &$3); $$.docval = getDocValue(&$3); } ; optref: { $$ = 0; } | '&' { if (currentSpec -> genc) yyerror("References not allowed in a C module"); $$ = ARG_IS_REF; } ; deref: { $$ = 0; } | deref '*' { $$ = $1 + 1; } ; basetype: scopedname { memset(&$$, 0, sizeof (argDef)); $$.atype = defined_type; $$.u.snd = $1; /* Try and resolve typedefs as early as possible. */ resolveAnyTypedef(currentSpec, &$$); } | scopedname '<' cpptypelist '>' { templateDef *td; td = sipMalloc(sizeof(templateDef)); td->fqname = $1; td->types = $3; memset(&$$, 0, sizeof (argDef)); $$.atype = template_type; $$.u.td = td; } | TK_STRUCT scopedname { memset(&$$, 0, sizeof (argDef)); /* 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 { memset(&$$, 0, sizeof (argDef)); $$.atype = ushort_type; } | TK_SHORT { memset(&$$, 0, sizeof (argDef)); $$.atype = short_type; } | TK_UNSIGNED { memset(&$$, 0, sizeof (argDef)); $$.atype = uint_type; } | TK_UNSIGNED TK_INT { memset(&$$, 0, sizeof (argDef)); $$.atype = uint_type; } | TK_INT { memset(&$$, 0, sizeof (argDef)); $$.atype = int_type; } | TK_LONG { memset(&$$, 0, sizeof (argDef)); $$.atype = long_type; } | TK_UNSIGNED TK_LONG { memset(&$$, 0, sizeof (argDef)); $$.atype = ulong_type; } | TK_LONG TK_LONG { memset(&$$, 0, sizeof (argDef)); $$.atype = longlong_type; } | TK_UNSIGNED TK_LONG TK_LONG { memset(&$$, 0, sizeof (argDef)); $$.atype = ulonglong_type; } | TK_FLOAT { memset(&$$, 0, sizeof (argDef)); $$.atype = float_type; } | TK_DOUBLE { memset(&$$, 0, sizeof (argDef)); $$.atype = double_type; } | TK_BOOL { memset(&$$, 0, sizeof (argDef)); $$.atype = bool_type; } | TK_SIGNED TK_CHAR { memset(&$$, 0, sizeof (argDef)); $$.atype = sstring_type; } | TK_UNSIGNED TK_CHAR { memset(&$$, 0, sizeof (argDef)); $$.atype = ustring_type; } | TK_CHAR { memset(&$$, 0, sizeof (argDef)); $$.atype = string_type; } | TK_WCHAR_T { memset(&$$, 0, sizeof (argDef)); $$.atype = wstring_type; } | TK_VOID { memset(&$$, 0, sizeof (argDef)); $$.atype = void_type; } | TK_PYOBJECT { memset(&$$, 0, sizeof (argDef)); $$.atype = pyobject_type; } | TK_PYTUPLE { memset(&$$, 0, sizeof (argDef)); $$.atype = pytuple_type; } | TK_PYLIST { memset(&$$, 0, sizeof (argDef)); $$.atype = pylist_type; } | TK_PYDICT { memset(&$$, 0, sizeof (argDef)); $$.atype = pydict_type; } | TK_PYCALLABLE { memset(&$$, 0, sizeof (argDef)); $$.atype = pycallable_type; } | TK_PYSLICE { memset(&$$, 0, sizeof (argDef)); $$.atype = pyslice_type; } | TK_PYTYPE { memset(&$$, 0, sizeof (argDef)); $$.atype = pytype_type; } | TK_ELLIPSIS { memset(&$$, 0, sizeof (argDef)); $$.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"); $$ = $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, int kwdArgs, int protHack) { classTmplDef *tcd; /* Initialise the spec. */ spec->modules = NULL; spec->namecache = NULL; spec->ifacefiles = NULL; spec->classes = NULL; spec->classtemplates = NULL; spec->exceptions = NULL; spec->mappedtypes = NULL; spec->mappedtypetemplates = NULL; spec->enums = NULL; spec->vars = NULL; spec->typedefs = NULL; spec->exphdrcode = NULL; spec->docs = NULL; spec->sigslots = FALSE; spec->genc = -1; spec->plugins = NULL; currentSpec = spec; neededQualifiers = tsl; excludedQualifiers = xfl; currentModule = NULL; currentMappedType = NULL; currentOverIsVirt = FALSE; currentCtorIsExplicit = FALSE; currentIsStatic = FALSE; currentIsSignal = FALSE; currentIsSlot = FALSE; currentIsTemplate = FALSE; previousFile = NULL; skipStackPtr = 0; currentScopeIdx = 0; sectionFlags = 0; defaultKwdArgs = kwdArgs; makeProtPublic = protHack; 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); currentContext = *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 *mod; parseFile(fp, filename, currentModule, FALSE); mod = allocModule(); mod->file = filename; if (currentModule != NULL) mod->defexception = currentModule->defexception; currentModule = mod; } /* * Allocate and initialise the memory for a new module. */ static moduleDef *allocModule() { moduleDef *newmod, **tailp; newmod = sipMalloc(sizeof (moduleDef)); newmod->version = -1; newmod->encoding = no_type; newmod->qobjclass = -1; newmod->nrvirthandlers = -1; newmod->next_key = 1; /* * The consolidated module support needs these to be in order that they * appeared. */ for (tailp = ¤tSpec->modules; *tailp != NULL; tailp = &(*tailp)->next) ; *tailp = newmod; return newmod; } /* * Switch to parsing a new file. */ static void parseFile(FILE *fp, char *name, moduleDef *prevmod, int optional) { parserContext pc; pc.filename = name; pc.ifdepth = skipStackPtr; pc.prevmod = prevmod; if (setInputFile(fp, &pc, optional)) currentContext = pc; } /* * Find an interface file, or create a new one. */ ifaceFileDef *findIfaceFile(sipSpec *pt, moduleDef *mod, scopedNameDef *fqname, ifaceFileType iftype, apiVersionRangeDef *api_range, argDef *ad) { ifaceFileDef *iff, *first_alt = NULL; /* See if the name is already used. */ for (iff = pt->ifacefiles; iff != NULL; iff = iff->next) { if (compareScopedNames(iff->fqcname, fqname) != 0) continue; /* * If they are both versioned then assume the user knows what they are * doing. */ if (iff->api_range != NULL && api_range != NULL && iff->module == mod) { /* Remember the first of the alternate APIs. */ if ((first_alt = iff->first_alt) == NULL) first_alt = iff; break; } /* * They must be the same type except that we allow a class 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; /* * This is a bit of a cheat. With consolidated modules it's * possible to have two implementations of a mapped type in * different branches of the module hierarchy. We assume that, if * there really are multiple implementations in the same branch, * then it will be picked up in a non-consolidated build. */ if (isConsolidated(pt->module)) continue; 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, scopedNameToString(fqname)); iff->api_range = api_range; if (first_alt != NULL) { iff->first_alt = first_alt; iff->next_alt = first_alt->next_alt; first_alt->next_alt = iff; } else { /* This is the first alternate so point to itself. */ iff->first_alt = iff; } iff->type = iftype; iff->ifacenr = -1; iff->fqcname = fqname; iff->module = NULL; iff->hdrcode = 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, apiVersionRangeDef *api_range, scopedNameDef *fqname) { return findClassWithInterface(pt, findIfaceFile(pt, currentModule, fqname, iftype, api_range, 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 = cacheName(pt, classBaseName(cd)); cd->next = pt->classes; pt->classes = cd; return cd; } /* * Add an interface file to an interface file list if it isn't already there. */ void addToUsedList(ifaceFileList **ifflp, ifaceFileDef *iff) { /* Make sure we don't try to add an interface file to its own list. */ if (&iff->used != ifflp) { ifaceFileList *iffl; while ((iffl = *ifflp) != NULL) { /* Don't bother if it is already there. */ if (iffl->iff == iff) return; ifflp = &iffl -> next; } iffl = sipMalloc(sizeof (ifaceFileList)); iffl->iff = iff; iffl->next = NULL; *ifflp = 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, 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->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, apiVersionRangeDef *api_range, 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 protected sections"); flags = 0; if ((scope = currentScope()) != NULL) { if (sectionFlags & SECT_IS_PROT && !makeProtPublic) { flags = CLASS_IS_PROTECTED; if (scope->iff->type == class_iface) setHasShadow(scope); } /* Header code from outer scopes is also included. */ hdrcode = scope->iff->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, api_range, 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; if (currentIsTemplate) setIsTemplateClass(cd); appendCodeBlock(&cd->iff->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 (compareScopedNames(ns->iff->fqcname, fqname) != 0) 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) { const 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, mod, cd->ecd, NULL, pyname, FALSE); cd->pyname = cacheName(pt, pyname); if ((flg = findOptFlag(of, "Metatype", dotted_name_flag)) != NULL) cd->metatype = cacheName(pt, flg->fvalue.sval); if ((flg = findOptFlag(of, "Supertype", dotted_name_flag)) != NULL) cd->supertype = cacheName(pt, flg->fvalue.sval); if ((flg = findOptFlag(of, "PyQt4Flags", integer_flag)) != NULL) cd->pyqt4_flags = flg->fvalue.ival; if (findOptFlag(of, "PyQt4NoQMetaObject", bool_flag) != NULL) setPyTQt4NoTQMetaObject(cd); 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.result.atype = void_type; cd->ctors->cppsig = &cd->ctors->pysig; 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 (getDeprecated(of)) setIsDeprecatedClass(cd); if (cd->convtocode != NULL && getAllowNone(of)) setClassHandlesNone(cd); 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 floordiv_slot: case ifloordiv_slot: case truediv_slot: case itruediv_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); setIsUsedName(cd->pyname); } } /* * Return the encoded name of a template (ie. including its argument types) as * a scoped name. */ scopedNameDef *encodedTemplateName(templateDef *td) { int a; scopedNameDef *snd; snd = copyScopedName(td->fqname); for (a = 0; a < td->types.nrArgs; ++a) { char buf[50]; int flgs; scopedNameDef *arg_snd; argDef *ad = &td->types.args[a]; flgs = 0; if (isConstArg(ad)) flgs += 1; if (isReference(ad)) flgs += 2; /* We use numbers so they don't conflict with names. */ sprintf(buf, "%02d%d%d", ad->atype, flgs, ad->nrderefs); switch (ad->atype) { case defined_type: arg_snd = copyScopedName(ad->u.snd); break; case template_type: arg_snd = encodedTemplateName(ad->u.td); break; case struct_type: arg_snd = copyScopedName(ad->u.sname); break; default: arg_snd = NULL; } /* * Replace the first element of the argument name with a copy with the * encoding prepended. */ if (arg_snd != NULL) arg_snd->name = concat(buf, arg_snd->name, NULL); else arg_snd = text2scopePart(sipStrdup(buf)); appendScopedName(&snd, arg_snd); } return snd; } /* * Create a new mapped type. */ static mappedTypeDef *newMappedType(sipSpec *pt, argDef *ad, optFlags *of) { mappedTypeDef *mtd; scopedNameDef *snd; ifaceFileDef *iff; const char *cname; /* Check that the type is one we want to map. */ switch (ad->atype) { case defined_type: snd = ad->u.snd; cname = scopedNameTail(snd); break; case template_type: snd = encodedTemplateName(ad->u.td); cname = NULL; break; case struct_type: snd = ad->u.sname; cname = scopedNameTail(snd); break; default: yyerror("Invalid type for %MappedType"); } iff = findIfaceFile(pt, currentModule, snd, mappedtype_iface, getAPIRange(of), ad); /* 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(pt, ad); if (cname != NULL) mtd->pyname = cacheName(pt, getPythonName(of, cname)); if (findOptFlag(of, "NoRelease", bool_flag) != NULL) setNoRelease(mtd); if (getAllowNone(of)) setHandlesNone(mtd); mtd->doctype = getDocType(of); mtd->iff = iff; mtd->next = pt->mappedtypes; pt->mappedtypes = mtd; if (inMainModule()) { setIsUsedName(mtd->cname); if (mtd->pyname) setIsUsedName(mtd->pyname); } return mtd; } /* * Allocate, intialise and return a mapped type structure. */ mappedTypeDef *allocMappedType(sipSpec *pt, argDef *type) { mappedTypeDef *mtd; mtd = sipMalloc(sizeof (mappedTypeDef)); mtd->type = *type; mtd->type.argflags = 0; mtd->type.nrderefs = 0; mtd->cname = cacheName(pt, type2string(&mtd->type)); return mtd; } /* * Create a new enum. */ static enumDef *newEnum(sipSpec *pt, moduleDef *mod, mappedTypeDef *mt_scope, char *name, optFlags *of, int flags) { enumDef *ed, *first_alt, *next_alt; classDef *c_scope; ifaceFileDef *scope; if (mt_scope != NULL) { scope = mt_scope->iff; c_scope = NULL; } else { if ((c_scope = currentScope()) != NULL) scope = c_scope->iff; else scope = NULL; } ed = sipMalloc(sizeof (enumDef)); /* Assume the enum isn't versioned. */ first_alt = ed; next_alt = NULL; if (name != NULL) { ed->pyname = cacheName(pt, getPythonName(of, name)); checkAttributes(pt, mod, c_scope, mt_scope, ed->pyname->text, FALSE); ed->fqcname = text2scopedName(scope, name); ed->cname = cacheName(pt, scopedNameToString(ed->fqcname)); if (inMainModule()) { setIsUsedName(ed->pyname); setIsUsedName(ed->cname); } /* If the scope is versioned then look for any alternate. */ if (scope != NULL && scope->api_range != NULL) { enumDef *alt; for (alt = pt->enums; alt != NULL; alt = alt->next) { if (alt->module != mod || alt->fqcname == NULL) continue; if (compareScopedNames(alt->fqcname, ed->fqcname) == 0) { first_alt = alt->first_alt; next_alt = first_alt->next_alt; first_alt->next_alt = ed; break; } } } } else { ed->pyname = NULL; ed->fqcname = NULL; ed->cname = NULL; } if (flags & SECT_IS_PROT && makeProtPublic) { flags &= ~SECT_IS_PROT; flags |= SECT_IS_PUBLIC; } ed->enumflags = flags; ed->enumnr = -1; ed->ecd = c_scope; ed->emtd = mt_scope; ed->first_alt = first_alt; ed->next_alt = next_alt; ed->module = mod; ed->members = NULL; ed->slots = NULL; ed->overs = NULL; ed->next = pt -> enums; pt->enums = ed; 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, *val; argDef *sad; /* * 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) { /* Skip base types. */ if (known->args[k].atype != defined_type) continue; 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. For defined types we don't want * any indirection or references. */ sad = &src->args[a]; if (sad->atype == defined_type) val = scopedNameToString(sad->u.snd); else val = type2string(sad); appendScopedName(values, text2scopePart(val)); } 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 on the heap. The string will use the minimum * whitespace while still remaining valid C++. */ static char *type2string(argDef *ad) { int i, on_heap = FALSE; int nr_derefs = ad->nrderefs; int is_reference = isReference(ad); char *s; /* Use the original type if possible. */ if (ad->original_type != NULL && !noTypeName(ad->original_type)) { s = scopedNameToString(ad->original_type->fqname); on_heap = TRUE; nr_derefs -= ad->original_type->type.nrderefs; if (isReference(&ad->original_type->type)) is_reference = FALSE; } else switch (ad->atype) { case template_type: { templateDef *td = ad->u.td; s = scopedNameToString(td->fqname); append(&s, "<"); for (i = 0; i < td->types.nrArgs; ++i) { char *sub_type = type2string(&td->types.args[i]); if (i > 0) append(&s, ","); append(&s, sub_type); free(sub_type); } if (s[strlen(s) - 1] == '>') append(&s, " >"); else append(&s, ">"); on_heap = TRUE; break; } case struct_type: s = scopedNameToString(ad->u.sname); on_heap = TRUE; break; case defined_type: s = scopedNameToString(ad->u.snd); on_heap = TRUE; break; case ustring_type: s = "unsigned char"; break; case ascii_string_type: case latin1_string_type: case utf8_string_type: case string_type: s = "char"; break; case sstring_type: s = "signed char"; break; case wstring_type: s = "wchar_t"; break; case ushort_type: s = "unsigned short"; break; case short_type: s = "short"; break; case uint_type: s = "unsigned int"; break; case int_type: case cint_type: s = "int"; break; case ulong_type: s = "unsigned long"; break; case long_type: s = "long"; break; case ulonglong_type: s = "unsigned long long"; break; case longlong_type: s = "long long"; break; case float_type: case cfloat_type: s = "float"; break; case double_type: case cdouble_type: s = "double"; break; case bool_type: case cbool_type: s = "bool"; break; default: fatal("Unsupported type argument to type2string(): %d\n", ad->atype); } /* Make sure the string is on the heap. */ if (!on_heap) s = sipStrdup(s); while (nr_derefs-- > 0) append(&s, "*"); if (is_reference) append(&s, "&"); return s; } /* * 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) { /* Ignore the encoded part of template names. */ if (isdigit(snd->next->name[0])) break; 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) { /* Ignore the encoded part of template names. */ if (isdigit(snd->next->name[0])) break; 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; argDef *ad; ifaceFileList *iffl, **used; type_names = type_values = NULL; appendTypeStrings(classFTQCName(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++]; memset(ad, 0, sizeof (argDef)); ad->atype = defined_type; ad->u.snd = classFTQCName(tcd->cd); appendScopedName(&type_names, text2scopePart(scopedNameTail(classFTQCName(tcd->cd)))); appendScopedName(&type_values, text2scopePart(scopedNameToString(fqname))); /* Create the new class. */ cd = sipMalloc(sizeof (classDef)); /* Start with a shallow copy. */ *cd = *tcd->cd; resetIsTemplateClass(cd); cd->pyname = cacheName(pt, scopedNameTail(fqname)); cd->td = td; /* Handle the interface file. */ cd->iff = findIfaceFile(pt, mod, fqname, class_iface, (scope != NULL ? scope->iff->api_range : NULL), 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); /* Include any scope header code. */ if (scope != NULL) appendCodeBlock(&cd->iff->hdrcode, scope->iff->hdrcode); if (inMainModule()) { setIsUsedName(cd->iff->name); setIsUsedName(cd->pyname); } cd->ecd = currentScope(); /* Handle the enums. */ instantiateTemplateEnums(pt, tcd, td, cd, used, type_names, type_values); /* Handle the variables. */ instantiateTemplateVars(pt, tcd, td, cd, used, type_names, type_values); /* 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 methods. */ cd->members = instantiateTemplateMethods(tcd->cd->members, mod); cd->overs = instantiateTemplateOverloads(pt, tcd->cd->overs, tcd->cd->members, cd->members, tcd, td, cd, used, type_names, type_values); cd->cppcode = templateCode(pt, used, cd->cppcode, type_names, type_values); cd->iff->hdrcode = templateCode(pt, used, cd->iff->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->getbufcode = templateCode(pt, used, cd->getbufcode, type_names, type_values); cd->releasebufcode = templateCode(pt, used, cd->releasebufcode, 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->picklecode = templateCode(pt, used, cd->picklecode, type_names, type_values); cd->next = pt->classes; pt->classes = cd; tcd->sig.nrArgs--; freeScopedName(type_names); freeScopedName(type_values); } /* * Instantiate the methods of a template class. */ static memberDef *instantiateTemplateMethods(memberDef *tmd, moduleDef *mod) { memberDef *md, *methods, **mdtail; methods = NULL; mdtail = &methods; for (md = tmd; md != NULL; md = md->next) { memberDef *nmd = sipMalloc(sizeof (memberDef)); /* Start with a shallow copy. */ *nmd = *md; nmd->module = mod; if (inMainModule()) setIsUsedName(nmd->pyname); nmd->next = NULL; *mdtail = nmd; mdtail = &nmd->next; } return methods; } /* * Instantiate the overloads of a template class. */ static overDef *instantiateTemplateOverloads(sipSpec *pt, overDef *tod, memberDef *tmethods, memberDef *methods, classTmplDef *tcd, templateDef *td, classDef *cd, ifaceFileList **used, scopedNameDef *type_names, scopedNameDef *type_values) { overDef *od, *overloads, **odtail; overloads = NULL; odtail = &overloads; for (od = tod; od != NULL; od = od->next) { overDef *nod = sipMalloc(sizeof (overDef)); memberDef *nmd, *omd; /* Start with a shallow copy. */ *nod = *od; for (nmd = methods, omd = tmethods; omd != NULL; omd = omd->next, nmd = nmd->next) if (omd == od->common) { nod->common = nmd; break; } templateSignature(&nod->pysig, TRUE, tcd, td, cd); if (od->cppsig == &od->pysig) nod->cppsig = &nod->pysig; else { nod->cppsig = sipMalloc(sizeof (signatureDef)); *nod->cppsig = *od->cppsig; templateSignature(nod->cppsig, TRUE, tcd, td, cd); } nod->methodcode = templateCode(pt, used, nod->methodcode, type_names, type_values); /* Handle any virtual handler. */ if (od->virthandler != NULL) { moduleDef *mod = cd->iff->module; nod->virthandler = sipMalloc(sizeof (virtHandlerDef)); /* Start with a shallow copy. */ *nod->virthandler = *od->virthandler; if (od->virthandler->cppsig == &od->pysig) nod->virthandler->cppsig = &nod->pysig; else { nod->virthandler->cppsig = sipMalloc(sizeof (signatureDef)); *nod->virthandler->cppsig = *od->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; } return overloads; } /* * Instantiate the enums of a template class. */ static void instantiateTemplateEnums(sipSpec *pt, classTmplDef *tcd, templateDef *td, classDef *cd, ifaceFileList **used, scopedNameDef *type_names, scopedNameDef *type_values) { enumDef *ted; moduleDef *mod = cd->iff->module; for (ted = pt->enums; ted != NULL; ted = ted->next) if (ted->ecd == tcd->cd) { enumDef *ed; enumMemberDef *temd; ed = sipMalloc(sizeof (enumDef)); /* Start with a shallow copy. */ *ed = *ted; if (ed->fqcname != NULL) { ed->fqcname = text2scopedName(cd->iff, scopedNameTail(ed->fqcname)); ed->cname = cacheName(pt, scopedNameToString(ed->fqcname)); } if (inMainModule()) { if (ed->pyname != NULL) setIsUsedName(ed->pyname); if (ed->cname != NULL) setIsUsedName(ed->cname); } ed->ecd = cd; ed->first_alt = ed; ed->module = mod; ed->members = NULL; for (temd = ted->members; temd != NULL; temd = temd->next) { enumMemberDef *emd; emd = sipMalloc(sizeof (enumMemberDef)); /* Start with a shallow copy. */ *emd = *temd; emd->ed = ed; emd->next = ed->members; ed->members = emd; } ed->slots = instantiateTemplateMethods(ted->slots, mod); ed->overs = instantiateTemplateOverloads(pt, ted->overs, ted->slots, ed->slots, tcd, td, cd, used, type_names, type_values); ed->next = pt->enums; pt->enums = ed; } } /* * Instantiate the variables of a template class. */ static void instantiateTemplateVars(sipSpec *pt, classTmplDef *tcd, templateDef *td, classDef *cd, ifaceFileList **used, scopedNameDef *type_names, scopedNameDef *type_values) { varDef *tvd; for (tvd = pt->vars; tvd != NULL; tvd = tvd->next) if (tvd->ecd == tcd->cd) { varDef *vd; vd = sipMalloc(sizeof (varDef)); /* Start with a shallow copy. */ *vd = *tvd; if (inMainModule()) setIsUsedName(vd->pyname); vd->fqcname = text2scopedName(cd->iff, scopedNameTail(vd->fqcname)); vd->ecd = cd; vd->module = cd->iff->module; templateType(&vd->type, tcd, td, cd); vd->accessfunc = templateCode(pt, used, vd->accessfunc, type_names, type_values); vd->getcode = templateCode(pt, used, vd->getcode, type_names, type_values); vd->setcode = templateCode(pt, used, vd->setcode, type_names, type_values); addVariable(pt, vd); } } /* * 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; /* Descend into any sub-templates. */ if (ad->atype == template_type) { templateDef *new_td = sipMalloc(sizeof (templateDef)); /* Make a deep copy of the template definition. */ *new_td = *ad->u.td; ad->u.td = new_td; templateSignature(&ad->u.td->types, FALSE, tcd, td, ncd); return; } /* 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) { argDef *tad = &td->types.args[a]; ad->atype = tad->atype; /* We take the constrained flag from the real type. */ resetIsConstrained(ad); if (isConstrained(tad)) setIsConstrained(ad); ad->u = tad->u; return; } /* Handle the class name itself. */ if (strcmp(name, scopedNameTail(classFTQCName(tcd->cd))) == 0) { ad->atype = class_type; ad->u.cd = ncd; ad->original_type = NULL; } } /* * 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[] = { "sipType_", "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'); } /* * Compare a (possibly) relative scoped name with a fully qualified scoped name * while taking the current scope into account. */ static int foundInScope(scopedNameDef *fq_name, scopedNameDef *rel_name) { classDef *scope; for (scope = currentScope(); scope != NULL; scope = scope->ecd) { scopedNameDef *snd; int found; snd = copyScopedName(classFTQCName(scope)); appendScopedName(&snd, copyScopedName(rel_name)); found = (compareScopedNames(fq_name, snd) == 0); freeScopedName(snd); if (found) return TRUE; } return compareScopedNames(fq_name, rel_name) == 0; } /* * Create a new typedef. */ static void newTypedef(sipSpec *pt, moduleDef *mod, char *name, argDef *type, optFlags *optflgs) { typedefDef *td, **tdp; scopedNameDef *fqname; classDef *scope; scope = currentScope(); fqname = text2scopedName((scope != NULL ? scope->iff : NULL), name); /* 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 (foundInScope(tcd->cd->iff->fqcname, td->fqname) && sameTemplateSignature(&tcd->sig, &td->types, FALSE)) { instantiateClassTemplate(pt, mod, scope, fqname, tcd, td); /* All done. */ return; } } /* * Check it doesn't already exist and find the position in the sorted list * where it should be put. */ for (tdp = &pt->typedefs; *tdp != NULL; tdp = &(*tdp)->next) { int res = compareScopedNames((*tdp)->fqname, fqname); if (res == 0) { fatalScopedName(fqname); fatal(" already defined\n"); } if (res > 0) break; } td = sipMalloc(sizeof (typedefDef)); td->tdflags = 0; td->fqname = fqname; td->ecd = scope; td->module = mod; td->type = *type; td->next = *tdp; *tdp = td; if (findOptFlag(optflgs, "NoTypeName", bool_flag) != NULL) setNoTypeName(td); mod->nrtypedefs++; } /* * Speculatively try and resolve any typedefs. In some cases (eg. when * comparing template signatures) it helps to use the real type if it is known. * Note that this wouldn't be necessary if we required that all types be known * before they are used. */ static void resolveAnyTypedef(sipSpec *pt, argDef *ad) { argDef orig = *ad; while (ad->atype == defined_type) { ad->atype = no_type; searchTypedefs(pt, ad->u.snd, ad); /* * Don't resolve to a template type as it may be superceded later on * by a more specific mapped type. */ if (ad->atype == no_type || ad->atype == template_type) { *ad = orig; break; } } } /* * 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 *tmpl_sd, signatureDef *args_sd, int deep) { int a; if (tmpl_sd->nrArgs != args_sd->nrArgs) return FALSE; for (a = 0; a < tmpl_sd->nrArgs; ++a) { argDef *tmpl_ad = &tmpl_sd->args[a]; argDef *args_ad = &args_sd->args[a]; /* * If we are doing a shallow comparision (ie. for class templates) then * a type name in the template signature matches anything in the * argument signature. */ if (tmpl_ad->atype == defined_type && !deep) continue; /* * For type names only compare the references and pointers, and do the * same for any nested templates. */ if (tmpl_ad->atype == defined_type && args_ad->atype == defined_type) { if (isReference(tmpl_ad) != isReference(args_ad) || tmpl_ad->nrderefs != args_ad->nrderefs) return FALSE; } else if (tmpl_ad->atype == template_type && args_ad->atype == template_type) { if (!sameTemplateSignature(&tmpl_ad->u.td->types, &args_ad->u.td->types, deep)) return FALSE; } else if (!sameBaseType(tmpl_ad, args_ad)) 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, mod, escope, NULL, nd->text, FALSE); var = sipMalloc(sizeof (varDef)); var->pyname = nd; var->fqcname = text2scopedName((escope != NULL ? escope->iff : NULL), name); var->ecd = escope; var->module = mod; var->varflags = 0; var->type = *type; var->accessfunc = acode; var->getcode = gcode; var->setcode = scode; if (isstatic || (escope != NULL && escope->iff->type == namespace_iface)) setIsStaticVar(var); addVariable(pt, var); } /* * Create a new ctor. */ static void newCtor(char *name, int sectFlags, signatureDef *args, optFlags *optflgs, codeBlock *methodcode, throwArgs *exceptions, signatureDef *cppsig, int explicit, codeBlock *docstring) { 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"); if (docstring != NULL) appendCodeBlock(&cd->docstring, docstring); /* Add to the list of constructors. */ ct = sipMalloc(sizeof (ctorDef)); if (sectFlags & SECT_IS_PROT && makeProtPublic) { sectFlags &= ~SECT_IS_PROT; sectFlags |= SECT_IS_PUBLIC; } /* Allow the signature to be used like an function signature. */ memset(&args->result, 0, sizeof (argDef)); args->result.atype = void_type; ct->ctorflags = sectFlags; ct->api_range = getAPIRange(optflgs); ct->pysig = *args; ct->cppsig = (cppsig != NULL ? cppsig : &ct->pysig); ct->exceptions = exceptions; ct->methodcode = methodcode; 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 (getTransfer(optflgs)) setIsResultTransferredCtor(ct); if (getDeprecated(optflgs)) setIsDeprecatedCtor(ct); if (!isPrivateCtor(ct) && usesKeywordArgs(optflgs, &ct->pysig)) setUseKeywordArgsCtor(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, classDef *c_scope, mappedTypeDef *mt_scope, int sflags, int isstatic, int issignal, int isslot, int isvirt, char *name, signatureDef *sig, int isconst, int isabstract, optFlags *optflgs, codeBlock *methodcode, codeBlock *vcode, throwArgs *exceptions, signatureDef *cppsig, codeBlock *docstring) { int factory, xferback, no_arg_parser; overDef *od, **odp, **headp; optFlag *of; virtHandlerDef *vhd; /* Extra checks for a C module. */ if (pt->genc) { if (c_scope != 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"); } if (mt_scope != NULL) headp = &mt_scope->overs; else if (c_scope != NULL) headp = &c_scope->overs; else headp = &mod->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 (c_scope == 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"); /* Create a new overload definition. */ od = sipMalloc(sizeof (overDef)); /* Set the overload flags. */ if ((sflags & SECT_IS_PROT) && makeProtPublic) { sflags &= ~SECT_IS_PROT; sflags |= SECT_IS_PUBLIC | OVER_REALLY_PROT; } od->overflags = sflags; if (issignal) { resetIsSlot(od); setIsSignal(od); } else if (isslot) { resetIsSignal(od); setIsSlot(od); } if (factory) setIsFactory(od); if (xferback) setIsResultTransferredBack(od); if (getTransfer(optflgs)) setIsResultTransferred(od); if (findOptFlag(optflgs, "TransferThis", bool_flag) != NULL) setIsThisTransferredMeth(od); if (isProtected(od)) setHasShadow(c_scope); if ((isSlot(od) || isSignal(od)) && !isPrivate(od)) { if (isSignal(od)) setHasShadow(c_scope); 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) { if (of->fvalue.sval == NULL || isEnabledFeature(of->fvalue.sval)) setIsAutoGen(od); } if (isvirt) { if (isSignal(od) && pluginPyTQt3(pt)) yyerror("Virtual signals aren't supported"); setIsVirtual(od); setHasShadow(c_scope); vhd = sipMalloc(sizeof (virtHandlerDef)); vhd->virthandlernr = -1; vhd->vhflags = 0; vhd->pysig = &od->pysig; vhd->cppsig = (cppsig != NULL ? cppsig : &od->pysig); vhd->virtcode = vcode; if (factory || xferback) setIsTransferVH(vhd); /* * Only add it to the module's virtual handlers if we are not in a * class template. */ if (!currentIsTemplate) { vhd->module = mod; vhd->next = mod->virthandlers; mod->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; no_arg_parser = (findOptFlag(optflgs, "NoArgParser", bool_flag) != NULL); if (no_arg_parser) { if (methodcode == NULL) yyerror("%MethodCode must be supplied if /NoArgParser/ is specified"); } if (findOptFlag(optflgs, "NoCopy", bool_flag) != NULL) setNoCopy(&od->pysig.result); od->common = findFunction(pt, mod, c_scope, mt_scope, getPythonName(optflgs, name), (methodcode != NULL), sig->nrArgs, no_arg_parser); if (docstring != NULL) appendCodeBlock(&od->common->docstring, docstring); od->api_range = getAPIRange(optflgs); if (od->api_range == NULL) setNotVersioned(od->common); 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); if (getDeprecated(optflgs)) setIsDeprecated(od); if (!isPrivate(od) && !isSignal(od) && od->common->slot == no_slot && usesKeywordArgs(optflgs, &od->pysig)) { setUseKeywordArgs(od); setUseKeywordArgsFunction(od->common); } /* See if we want to auto-generate a __len__() method. */ if (findOptFlag(optflgs, "__len__", bool_flag) != NULL) { overDef *len; len = sipMalloc(sizeof (overDef)); len->cppname = "__len__"; len->overflags = SECT_IS_PUBLIC; len->pysig.result.atype = ssize_type; len->pysig.nrArgs = 0; len->cppsig = &len->pysig; len->common = findFunction(pt, mod, c_scope, mt_scope, len->cppname, TRUE, 0, FALSE); if ((len->methodcode = od->methodcode) == NULL) { char *buf = sipStrdup(" sipRes = (SIP_SSIZE_T)sipCpp->"); codeBlock *code; append(&buf, od->cppname); append(&buf, "();\n"); code = sipMalloc(sizeof (codeBlock)); code->frag = buf; code->filename = "Auto-generated"; code->linenr = 0; code->next = NULL; len->methodcode = code; } len->next = NULL; od->next = len; } else { 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 const char *getPythonName(optFlags *optflgs, const char *cname) { const 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. Entries in the cache are stored in order of * decreasing length. */ nameDef *cacheName(sipSpec *pt, const char *name) { nameDef *nd, **ndp; size_t len; /* Allow callers to be lazy about checking if there is really a name. */ if (name == NULL) return NULL; /* Skip entries that are too large. */ ndp = &pt->namecache; len = strlen(name); while (*ndp != NULL && (*ndp)->len > len) ndp = &(*ndp)->next; /* Check entries that are the right length. */ for (nd = *ndp; nd != NULL && nd->len == len; nd = nd->next) if (memcmp(nd->text, name, len) == 0) return nd; /* Create a new one. */ nd = sipMalloc(sizeof (nameDef)); nd->nameflags = 0; nd->text = name; nd->len = len; nd->next = *ndp; *ndp = nd; return nd; } /* * Find (or create) an overloaded function name. */ static memberDef *findFunction(sipSpec *pt, moduleDef *mod, classDef *c_scope, mappedTypeDef *mt_scope, const char *pname, int hwcode, int nrargs, int no_arg_parser) { static struct slot_map { const char *name; /* The slot name. */ slotType type; /* The corresponding type. */ int needs_hwcode; /* Set 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}, {"__floordiv__", floordiv_slot, TRUE, 1}, {"__truediv__", truediv_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}, {"__ifloordiv__", ifloordiv_slot, TRUE, 1}, {"__itruediv__", itruediv_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, 2}, {"__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}, {"__bool__", bool_slot, TRUE, 0}, {"__nonzero__", bool_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}, {"__index__", index_slot, TRUE, 0}, {"__iter__", iter_slot, TRUE, 0}, {"__next__", next_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) == 0) { if (sm->needs_hwcode && !hwcode) yyerror("This Python slot requires %MethodCode"); if (sm->nrargs >= 0) { if (mt_scope == NULL && c_scope == 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; } /* Check there is no name clash. */ checkAttributes(pt, mod, c_scope, mt_scope, pname, TRUE); /* See if it already exists. */ if (mt_scope != NULL) flist = &mt_scope->members; else if (c_scope != NULL) flist = &c_scope->members; else flist = &mod->othfuncs; for (md = *flist; md != NULL; md = md->next) if (strcmp(md->pyname->text, pname) == 0 && md->module == mod) break; if (md == NULL) { /* Create a new one. */ md = sipMalloc(sizeof (memberDef)); md->pyname = cacheName(pt, pname); md->memberflags = 0; md->slot = st; md->module = mod; md->next = *flist; *flist = md; if (inMainModule()) setIsUsedName(md->pyname); if (no_arg_parser) setNoArgParser(md); } else if (noArgParser(md)) yyerror("Another overload has already been defined that is annotated as /NoArgParser/"); /* Global operators are a subset. */ if (mt_scope == NULL && c_scope == 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, moduleDef *mod, classDef *py_c_scope, mappedTypeDef *py_mt_scope, const 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->pyname == NULL) continue; if (py_c_scope != NULL) { if (ed->ecd != py_c_scope) continue; } else if (py_mt_scope != NULL) { if (ed->emtd != py_mt_scope) continue; } else if (ed->ecd != NULL || ed->emtd != 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"); } /* * 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; overDef *overs; if (py_mt_scope != NULL) { membs = py_mt_scope->members; overs = py_mt_scope->overs; } else if (py_c_scope != NULL) { membs = py_c_scope->members; overs = py_c_scope->overs; } else { membs = mod->othfuncs; overs = mod->overs; } for (md = membs; md != NULL; md = md->next) { overDef *od; if (strcmp(md->pyname->text, attr) != 0) continue; /* Check for a conflict with all overloads. */ 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"); } } } /* If the scope was a mapped type then that's all we have to check. */ if (py_mt_scope != NULL) return; /* Check the variables. */ for (vd = pt->vars; vd != NULL; vd = vd->next) { if (vd->ecd != py_c_scope) continue; if (strcmp(vd->pyname->text,attr) == 0) yyerror("There is already a variable in scope with the same Python name"); } /* Check the classes. */ for (cd = pt->classes; cd != NULL; cd = cd->next) { if (cd->ecd != py_c_scope || cd->pyname == NULL) continue; if (strcmp(cd->pyname->text, attr) == 0 && !isExternal(cd)) yyerror("There is already a class or namespace in scope with the same Python name"); } /* Check the exceptions. */ if (py_c_scope == 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. */ 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 > currentContext.ifdepth) fatal("Too many %%If statements in %s\n", previousFile); if (skipStackPtr < currentContext.ifdepth) fatal("Too many %%End statements in %s\n", previousFile); } /* * Handle the end of a fully parsed a module. */ static void handleEOM() { moduleDef *from; /* Check it has been named. */ if (currentModule->name == NULL) fatal("No %%Module has been specified for module defined in %s\n", previousFile); from = currentContext.prevmod; if (from != NULL && from->encoding == no_type) from->encoding = currentModule->encoding; /* The previous module is now current. */ currentModule = from; } /* * Find an existing qualifier. */ static qualDef *findQualifier(const 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; } /* * Find an existing API. */ apiVersionRangeDef *findAPI(sipSpec *pt, const char *name) { moduleDef *mod; for (mod = pt->modules; mod != NULL; mod = mod->next) { apiVersionRangeDef *avd; for (avd = mod->api_versions; avd != NULL; avd = avd->next) if (strcmp(avd->api_name->text, name) == 0) return avd; } 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. */ static 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(ifaceFileDef *scope, char *text) { return scopeScopedName(scope, text2scopePart(text)); } /* * Prepend any current scope to a scoped name. */ static scopedNameDef *scopeScopedName(ifaceFileDef *scope, scopedNameDef *name) { scopedNameDef *snd; snd = (scope != NULL ? copyScopedName(scope->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 *filename) { moduleDef *from, *mod; moduleListDef *mld; /* Create a new module if it has not already been defined. */ for (mod = currentSpec->modules; mod != NULL; mod = mod->next) if (strcmp(mod->file, filename) == 0) break; from = currentModule; if (mod == NULL) { newModule(NULL, filename); mod = currentModule; } else if (from->encoding == no_type) { /* Import any defaults from the already parsed module. */ from->encoding = mod->encoding; } /* 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 /Transfer/ option flag. */ static int getTransfer(optFlags *optflgs) { return (findOptFlag(optflgs, "Transfer", bool_flag) != 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); } /* * Get the /Deprecated/ option flag. */ static int getDeprecated(optFlags *optflgs) { return (findOptFlag(optflgs, "Deprecated", bool_flag) != NULL); } /* * Get the /AllowNone/ option flag. */ static int getAllowNone(optFlags *optflgs) { return (findOptFlag(optflgs, "AllowNone", bool_flag) != NULL); } /* * Get the /DocType/ option flag. */ static const char *getDocType(optFlags *optflgs) { optFlag *of = findOptFlag(optflgs, "DocType", string_flag); if (of == NULL) return NULL; return of->fvalue.sval; } /* * Get the /DocValue/ option flag. */ static const char *getDocValue(optFlags *optflgs) { optFlag *of = findOptFlag(optflgs, "DocValue", string_flag); if (of == NULL) return NULL; return of->fvalue.sval; } /* * Return TRUE if the PyQt3 plugin was specified. */ int pluginPyTQt3(sipSpec *pt) { return stringFind(pt->plugins, "PyQt3"); } /* * Return TRUE if the PyQt4 plugin was specified. */ int pluginPyTQt4(sipSpec *pt) { return stringFind(pt->plugins, "PyQt4"); } /* * Return TRUE if a list of strings contains a given entry. */ static int stringFind(stringList *sl, const char *s) { while (sl != NULL) { if (strcmp(sl->s, s) == 0) return TRUE; sl = sl->next; } return FALSE; } /* * Set the name of a module. */ static void setModuleName(sipSpec *pt, moduleDef *mod, const char *fullname) { mod->fullname = cacheName(pt, fullname); if (inMainModule()) setIsUsedName(mod->fullname); if ((mod->name = strrchr(fullname, '.')) != NULL) mod->name++; else mod->name = fullname; } /* * Define a new class and set its name. */ static void defineClass(scopedNameDef *snd, classList *supers, optFlags *of) { classDef *cd, *c_scope = currentScope(); cd = newClass(currentSpec, class_iface, getAPIRange(of), scopeScopedName((c_scope != NULL ? c_scope->iff : NULL), snd)); cd->supers = supers; pushScope(cd); } /* * Complete the definition of a class. */ static classDef *completeClass(scopedNameDef *snd, optFlags *of, int has_def) { classDef *cd = currentScope(); /* See if the class was defined or just declared. */ if (has_def) { if (snd->next != NULL) yyerror("A scoped name cannot be given in a class/struct definition"); } else if (cd->supers != NULL) yyerror("Class/struct has super-classes but no definition"); else setIsOpaque(cd); finishClass(currentSpec, currentModule, cd, of); popScope(); /* * Check that external classes have only been declared at the global scope. */ if (isExternal(cd) && currentScope() != NULL) yyerror("External classes/structs can only be declared in the global scope"); return cd; } /* * Add a variable to the list so that the list remains sorted. */ static void addVariable(sipSpec *pt, varDef *vd) { varDef **at = &pt->vars; while (*at != NULL) { if (strcmp(vd->pyname->text, (*at)->pyname->text) < 0) break; at = &(*at)->next; } vd->next = *at; *at = vd; } /* * Update a type according to optional flags. */ static void applyTypeFlags(moduleDef *mod, argDef *ad, optFlags *flags) { ad->doctype = getDocType(flags); if (ad->atype == string_type && !isArray(ad) && !isReference(ad)) { optFlag *of; if ((of = findOptFlag(flags, "Encoding", string_flag)) == NULL) { if (mod->encoding != no_type) ad->atype = mod->encoding; else ad->atype = string_type; } else if ((ad->atype = convertEncoding(of->fvalue.sval)) == no_type) yyerror("The value of the /Encoding/ annotation must be one of \"ASCII\", \"Latin-1\", \"UTF-8\" or \"None\""); } } /* * Return the argument type for a string with the given encoding or no_type if * the encoding was invalid. */ static argType convertEncoding(const char *encoding) { if (strcmp(encoding, "ASCII") == 0) return ascii_string_type; if (strcmp(encoding, "Latin-1") == 0) return latin1_string_type; if (strcmp(encoding, "UTF-8") == 0) return utf8_string_type; if (strcmp(encoding, "None") == 0) return string_type; return no_type; } /* * Get the /API/ option flag. */ static apiVersionRangeDef *getAPIRange(optFlags *optflgs) { optFlag *of; if ((of = findOptFlag(optflgs, "API", api_range_flag)) == NULL) return NULL; return of->fvalue.aval; } /* * Return the API range structure and version number corresponding to the * given API range. */ static apiVersionRangeDef *convertAPIRange(moduleDef *mod, nameDef *name, int from, int to) { int index; apiVersionRangeDef *avd, **avdp; /* Handle the trivial case. */ if (from == 0 && to == 0) return NULL; for (index = 0, avdp = &mod->api_ranges; (*avdp) != NULL; avdp = &(*avdp)->next, ++index) { avd = *avdp; if (avd->api_name == name && avd->from == from && avd->to == to) return avd; } /* The new one must be appended so that version numbers remain valid. */ avd = sipMalloc(sizeof (apiVersionRangeDef)); avd->api_name = name; avd->from = from; avd->to = to; avd->index = index; avd->next = NULL; *avdp = avd; return avd; } /* * Return TRUE if a signature with annotations uses keyword arguments. */ static int usesKeywordArgs(optFlags *optflgs, signatureDef *sd) { int kwd_args_anno, no_kwd_args_anno; kwd_args_anno = (findOptFlag(optflgs, "KeywordArgs", bool_flag) != NULL); no_kwd_args_anno = (findOptFlag(optflgs, "NoKeywordArgs", bool_flag) != NULL); /* * An ellipsis cannot be used with keyword arguments. Only complain if it * has been explicitly requested. */ if (kwd_args_anno && sd->nrArgs > 0 && sd->args[sd->nrArgs - 1].atype == ellipsis_type) yyerror("/KeywordArgs/ cannot be specified for calls with a variable number of arguments"); if ((defaultKwdArgs || kwd_args_anno) && !no_kwd_args_anno) { int a, is_name = FALSE; /* * Mark argument names as being used and check there is at least one. */ for (a = 0; a < sd->nrArgs; ++a) { nameDef *nd = sd->args[a].name; if (sd->args[a].name != NULL) { setIsUsedName(nd); is_name = TRUE; } } return is_name; } return FALSE; } /* * Extract the version of a string value optionally associated with a * particular feature. */ static char *convertFeaturedString(char *fs) { while (fs != NULL) { char *next, *value; /* Individual values are ';' separated. */ if ((next = strchr(fs, ';')) != NULL) *next++ = '\0'; /* Features and values are ':' separated. */ if ((value = strchr(fs, ':')) == NULL) { /* This is an unconditional value so just return it. */ return strip(fs); } *value++ = '\0'; if (isEnabledFeature(strip(fs))) return strip(value); fs = next; } /* No value was enabled. */ return NULL; } /* * Return the stripped version of a string. */ static char *strip(char *s) { while (*s == ' ') ++s; if (*s != '\0') { char *cp = &s[strlen(s) - 1]; while (*cp == ' ') *cp-- = '\0'; } return s; } /* * Return TRUE if the given feature is enabled. */ static int isEnabledFeature(const char *name) { qualDef *qd; if ((qd = findQualifier(name)) == NULL || qd->qtype != feature_qualifier) yyerror("No such feature"); return !excludedFeature(excludedQualifiers, qd); }