You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sip4-tqt/sipgen/parser.y

6386 lines
167 KiB

/*
* The SIP parser.
*
* Copyright (c) 2010 Riverbank Computing Limited <info@riverbankcomputing.com>
*
* 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 <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "sip.h"
#define MAX_NESTED_IF 10
#define MAX_NESTED_SCOPE 10
#define inMainModule() (currentSpec->module == currentModule || 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 <codeb> TK_CODELINE
%token TK_IF
%token TK_END
%token <text> TK_NAME
%token <text> TK_PATHNAME
%token <text> TK_STRING
%token TK_VIRTUALCATCHERCODE
%token TK_TRAVERSECODE
%token TK_CLEARCODE
%token TK_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 <number> TK_NUMBER
%token <real> TK_REAL
%token TK_TYPEDEF
%token TK_NAMESPACE
%token TK_TIMELINE
%token TK_PLATFORMS
%token TK_FEATURE
%token TK_LICENSE
%token <qchar> TK_QCHAR
%token TK_TRUE
%token TK_FALSE
%token TK_NULL
%token TK_OPERATOR
%token TK_THROW
%token TK_QOBJECT
%token TK_EXCEPTION
%token TK_RAISECODE
%token TK_EXPLICIT
%token TK_TEMPLATE
%token TK_ELLIPSIS
%token TK_DEFMETATYPE
%token TK_DEFSUPERTYPE
%type <memArg> argvalue
%type <memArg> argtype
%type <memArg> cpptype
%type <memArg> basetype
%type <signature> template
%type <signature> arglist
%type <signature> rawarglist
%type <signature> cpptypelist
%type <optsignature> optsig
%type <optsignature> optctorsig
%type <throwlist> optexceptions
%type <throwlist> exceptionlist
%type <number> optslot
%type <number> optref
%type <number> optconst
%type <number> optvirtual
%type <number> optabstract
%type <number> deref
%type <number> optnumber
%type <value> simplevalue
%type <valp> value
%type <valp> expr
%type <valp> optassign
%type <codeb> optaccesscode
%type <codeb> optgetcode
%type <codeb> optsetcode
%type <codeb> exphdrcode
%type <codeb> modhdrcode
%type <codeb> typehdrcode
%type <codeb> opttypehdrcode
%type <codeb> travcode
%type <codeb> clearcode
%type <codeb> getbufcode
%type <codeb> releasebufcode
%type <codeb> readbufcode
%type <codeb> writebufcode
%type <codeb> segcountcode
%type <codeb> charbufcode
%type <codeb> picklecode
%type <codeb> modcode
%type <codeb> typecode
%type <codeb> codeblock
%type <codeb> codelines
%type <codeb> virtualcatchercode
%type <codeb> methodcode
%type <codeb> raisecode
%type <codeb> docstring
%type <codeb> optdocstring
%type <text> operatorname
%type <text> optfilename
%type <text> optname
%type <text> dottedname
%type <optflags> optflags
%type <optflags> flaglist
%type <flag> flag
%type <flag> flagvalue
%type <qchar> optunop
%type <qchar> binop
%type <scpvalp> scopepart
%type <scpvalp> scopedname
%type <fcall> exprlist
%type <boolean> qualifiers
%type <boolean> oredqualifiers
%type <boolean> modlang
%type <boolean> optclassbody
%type <exceptionbase> baseexception
%type <klass> class
%%
specification: statement
| specification statement
;
statement: {
/*
* We don't do these in parserEOF() because the parser is reading
* ahead and that would be too early.
*/
if (previousFile != NULL)
{
handleEOF();
if (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(&currentSpec->exphdrcode, $1);
}
| modhdrcode {
if (notSkipping())
appendCodeBlock(&currentModule->hdrcode, $1);
}
| modcode {
if (notSkipping())
appendCodeBlock(&currentModule->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(&currentSpec->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(&currentMappedType->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(&currentModule->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(&currentModule->preinitcode, $2);
}
;
initcode: TK_INITCODE codeblock {
if (notSkipping())
appendCodeBlock(&currentModule->initcode, $2);
}
;
postinitcode: TK_POSTINITCODE codeblock {
if (notSkipping())
appendCodeBlock(&currentModule->postinitcode, $2);
}
;
unitcode: TK_UNITCODE codeblock {
if (notSkipping())
appendCodeBlock(&currentModule->unitcode, $2);
}
;
prepycode: TK_PREPYCODE codeblock {
/*
* This is a no-op and is retained for compatibility
* until the last use of it (by SIP v3) can be removed
* from PyQt.
*/
}
;
doc: TK_DOC codeblock {
if (inMainModule())
appendCodeBlock(&currentSpec -> docs,$2);
}
;
exporteddoc: TK_EXPORTEDDOC codeblock {
appendCodeBlock(&currentSpec -> docs,$2);
}
;
makefile: TK_MAKEFILE TK_PATHNAME optfilename codeblock {
if (inMainModule())
yywarning("%Makefile is ignored, please use the -b flag instead");
}
;
codeblock: codelines TK_END
;
codelines: TK_CODELINE
| codelines TK_CODELINE {
$$ = $1;
append(&$$->frag, $2->frag);
free($2->frag);
free((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 = &currentEnum->members; *tail != NULL; tail = &(*tail)->next)
;
*tail = emd;
if (inMainModule())
setIsUsedName(emd -> pyname);
}
}
;
optcomma:
| ','
;
optenumassign:
| '=' value
;
optassign: {
$$ = NULL;
}
| '=' expr {
$$ = $2;
}
;
expr: value
| expr binop value {
valueDef *vd;
if ($1 -> vtype == string_value || $3 -> vtype == string_value)
yyerror("Invalid binary operator for string");
/* Find the last value in the existing expression. */
for (vd = $1; vd -> next != NULL; vd = vd -> next)
;
vd -> vbinop = $2;
vd -> next = $3;
$$ = $1;
}
;
binop: '-' {
$$ = '-';
}
| '+' {
$$ = '+';
}
| '*' {
$$ = '*';
}
| '/' {
$$ = '/';
}
| '&' {
$$ = '&';
}
| '|' {
$$ = '|';
}
;
optunop: {
$$ = '\0';
}
| '!' {
$$ = '!';
}
| '~' {
$$ = '~';
}
| '-' {
$$ = '-';
}
| '+' {
$$ = '+';
}
;
value: optunop simplevalue {
if ($1 != '\0' && $2.vtype == string_value)
yyerror("Invalid unary operator for string");
/*
* Convert the value to a simple expression on the
* heap.
*/
$$ = sipMalloc(sizeof (valueDef));
*$$ = $2;
$$ -> vunop = $1;
$$ -> vbinop = '\0';
$$ -> next = NULL;
}
;
scopedname: scopepart
| scopedname TK_SCOPE scopepart {
if (currentSpec -> genc)
yyerror("Scoped names are not allowed in a C module");
appendScopedName(&$1,$3);
}
;
scopepart: TK_NAME {
$$ = text2scopePart($1);
}
;
simplevalue: scopedname {
/*
* We let the C++ compiler decide if the value is a valid one - no
* point in building a full C++ parser here.
*/
$$.vtype = scoped_value;
$$.u.vscp = $1;
}
| basetype '(' exprlist ')' {
fcallDef *fcd;
fcd = sipMalloc(sizeof (fcallDef));
*fcd = $3;
fcd -> type = $1;
$$.vtype = fcall_value;
$$.u.fcd = fcd;
}
| TK_REAL {
$$.vtype = real_value;
$$.u.vreal = $1;
}
| TK_NUMBER {
$$.vtype = numeric_value;
$$.u.vnum = $1;
}
| TK_TRUE {
$$.vtype = numeric_value;
$$.u.vnum = 1;
}
| TK_FALSE {
$$.vtype = numeric_value;
$$.u.vnum = 0;
}
| TK_NULL {
$$.vtype = numeric_value;
$$.u.vnum = 0;
}
| TK_STRING {
$$.vtype = string_value;
$$.u.vstr = $1;
}
| TK_QCHAR {
$$.vtype = qchar_value;
$$.u.vqchar = $1;
}
;
exprlist: {
/* No values. */
$$.nrArgs = 0;
}
| expr {
/* The single or first expression. */
$$.args[0] = $1;
$$.nrArgs = 1;
}
| exprlist ',' expr {
/* Check that it wasn't ...(,expression...). */
if ($$.nrArgs == 0)
yyerror("First argument to function call is missing");
/* Check there is room. */
if ($1.nrArgs == MAX_NR_ARGS)
yyerror("Too many arguments to function call");
$$ = $1;
$$.args[$$.nrArgs] = $3;
$$.nrArgs++;
}
;
typedef: TK_TYPEDEF cpptype TK_NAME 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(&currentSupers, 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(&currentScope()->cppcode, $1);
}
| typehdrcode {
if (notSkipping())
appendCodeBlock(&currentScope()->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 = &currentSpec->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;
</