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.
tdebindings/python/sip/sipgen/parser.y

4946 lines
128 KiB

/*
* The SIP parser.
*
* Copyright (c) 2007
* Riverbank Computing Limited <info@riverbankcomputing.co.uk>
*
* This file is part of SIP.
*
* This copy of SIP is licensed for use under the terms of the SIP License
* Agreement. See the file LICENSE for more details.
*
* SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
%{
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "sip.h"
#define MAX_NESTED_IF 10
#define MAX_NESTED_SCOPE 10
#define inMainModule() (currentSpec -> module == currentModule)
static sipSpec *currentSpec; /* The current spec being parsed. */
static stringList *neededQualifiers; /* The list of required qualifiers. */
static stringList *excludedQualifiers; /* The list of excluded qualifiers. */
static moduleDef *currentModule; /* The current module being parsed. */
static mappedTypeDef *currentMappedType; /* The current mapped type. */
static enumDef *currentEnum; /* The current enum being parsed. */
static int sectionFlags; /* The current section flags. */
static int currentOverIsVirt; /* Set if the overload is virtual. */
static int currentCtorIsExplicit; /* Set if the ctor is explicit. */
static int currentIsStatic; /* Set if the current is static. */
static char *previousFile; /* The file just parsed. */
static parserContext newContext; /* The new pending context. */
static int skipStackPtr; /* The skip stack pointer. */
static int skipStack[MAX_NESTED_IF]; /* Stack of skip flags. */
static classDef *scopeStack[MAX_NESTED_SCOPE]; /* The scope stack. */
static int sectFlagsStack[MAX_NESTED_SCOPE]; /* The section flags stack. */
static int currentScopeIdx; /* The scope stack index. */
static int currentTimelineOrder; /* The current timeline order. */
static char *getPythonName(optFlags *optflgs, char *cname);
static nameDef *cacheName(sipSpec *,char *);
static classDef *findClass(sipSpec *,ifaceFileType,scopedNameDef *);
static classDef *findClassWithInterface(sipSpec *pt, ifaceFileDef *iff);
static classDef *newClass(sipSpec *,ifaceFileType,scopedNameDef *);
static void finishClass(sipSpec *,moduleDef *,classDef *,optFlags *);
static exceptionDef *findException(sipSpec *pt, scopedNameDef *fqname, int new);
static mappedTypeDef *newMappedType(sipSpec *,argDef *);
static enumDef *newEnum(sipSpec *,moduleDef *,char *,optFlags *,int);
static void instantiateClassTemplate(sipSpec *pt, moduleDef *mod, classDef *scope, scopedNameDef *fqname, classTmplDef *tcd, templateDef *td);
static void newTypedef(sipSpec *,moduleDef *,char *,argDef *);
static void newVar(sipSpec *,moduleDef *,char *,int,argDef *,optFlags *,
codeBlock *,codeBlock *,codeBlock *);
static void newCtor(char *,int,signatureDef *,optFlags *,codeBlock *,
throwArgs *,signatureDef *,int);
static void newFunction(sipSpec *,moduleDef *,int,int,int,char *,
signatureDef *,int,int,optFlags *,codeBlock *,
codeBlock *,throwArgs *,signatureDef *);
static optFlag *findOptFlag(optFlags *,char *,flagType);
static memberDef *findFunction(sipSpec *,moduleDef *,classDef *,nameDef *,int,
int);
static void checkAttributes(sipSpec *,classDef *,char *,int);
static void newModule(FILE *,char *);
static void appendCodeBlock(codeBlock **,codeBlock *);
static void parseFile(FILE *,char *,moduleDef *,int);
static void handleEOF(void);
static void handleEOM(void);
static qualDef *findQualifier(char *);
static scopedNameDef *text2scopedName(char *);
static scopedNameDef *scopeScopedName(scopedNameDef *name);
static void pushScope(classDef *);
static void popScope(void);
static classDef *currentScope(void);
static void newQualifier(moduleDef *,int,int,char *,qualType);
static void newImport(char *);
static void usedInMainModule(sipSpec *,ifaceFileDef *);
static int timePeriod(char *,char *);
static int platOrFeature(char *,int);
static int isNeeded(qualDef *);
static int notSkipping(void);
static void getHooks(optFlags *,char **,char **);
static int getReleaseGIL(optFlags *);
static int getHoldGIL(optFlags *);
static void templateSignature(signatureDef *sd, int result, classTmplDef *tcd, templateDef *td, classDef *ncd);
static void templateType(argDef *ad, classTmplDef *tcd, templateDef *td, classDef *ncd);
static int search_back(const char *end, const char *start, const char *target);
static char *getType(scopedNameDef *ename, argDef *ad);
static char *scopedNameToString(scopedNameDef *name);
static void addUsedFromCode(sipSpec *pt, ifaceFileList **used, const char *sname);
static int sameName(scopedNameDef *snd, const char *sname);
static int optFind(sipSpec *pt, const char *opt);
%}
%union {
char qchar;
char *text;
long number;
double real;
argDef memArg;
signatureDef signature;
signatureDef *optsignature;
throwArgs *throwlist;
codeBlock *codeb;
valueDef value;
valueDef *valp;
optFlags optflags;
optFlag flag;
scopedNameDef *scpvalp;
fcallDef fcall;
int boolean;
exceptionDef exceptionbase;
classDef *klass;
}
%token TK_OPTIONS
%token TK_NOEMITTERS
%token TK_DOC
%token TK_EXPORTEDDOC
%token TK_MAKEFILE
%token TK_ACCESSCODE
%token TK_GETCODE
%token TK_SETCODE
%token TK_PREINITCODE
%token TK_POSTINITCODE
%token TK_UNITCODE
%token TK_MODCODE
%token TK_TYPECODE
%token TK_PREPYCODE
%token TK_COPYING
%token TK_MAPPEDTYPE
%token <codeb> TK_CODELINE
%token TK_IF
%token TK_END
%token <text> TK_NAME
%token <text> TK_PATHNAME
%token <text> TK_STRING
%token TK_VIRTUALCATCHERCODE
%token TK_TRAVERSECODE
%token TK_CLEARCODE
%token TK_READBUFFERCODE
%token TK_WRITEBUFFERCODE
%token TK_SEGCOUNTCODE
%token TK_CHARBUFFERCODE
%token TK_METHODCODE
%token TK_FROMTYPE
%token TK_TOTYPE
%token TK_TOSUBCLASS
%token TK_INCLUDE
%token TK_OPTINCLUDE
%token TK_IMPORT
%token TK_EXPHEADERCODE
%token TK_MODHEADERCODE
%token TK_TYPEHEADERCODE
%token TK_MODULE
%token TK_CMODULE
%token TK_CLASS
%token TK_STRUCT
%token TK_PUBLIC
%token TK_PROTECTED
%token TK_PRIVATE
%token TK_SIGNALS
%token TK_SLOTS
%token TK_BOOL
%token TK_SHORT
%token TK_INT
%token TK_LONG
%token TK_FLOAT
%token TK_DOUBLE
%token TK_CHAR
%token TK_WCHAR_T
%token TK_VOID
%token TK_PYOBJECT
%token TK_PYTUPLE
%token TK_PYLIST
%token TK_PYDICT
%token TK_PYCALLABLE
%token TK_PYSLICE
%token TK_PYTYPE
%token TK_VIRTUAL
%token TK_ENUM
%token TK_SIGNED
%token TK_UNSIGNED
%token TK_SCOPE
%token TK_LOGICAL_OR
%token TK_CONST
%token TK_STATIC
%token TK_SIPSIGNAL
%token TK_SIPSLOT
%token TK_SIPANYSLOT
%token TK_SIPRXCON
%token TK_SIPRXDIS
%token TK_SIPSLOTCON
%token TK_SIPSLOTDIS
%token <number> TK_NUMBER
%token <real> TK_REAL
%token TK_TYPEDEF
%token TK_NAMESPACE
%token TK_TIMELINE
%token TK_PLATFORMS
%token TK_FEATURE
%token TK_LICENSE
%token <qchar> TK_QCHAR
%token TK_TRUE
%token TK_FALSE
%token TK_NULL
%token TK_OPERATOR
%token TK_THROW
%token TK_QOBJECT
%token TK_EXCEPTION
%token TK_RAISECODE
%token TK_EXPLICIT
%token TK_TEMPLATE
%token TK_ELLIPSIS
%type <memArg> argvalue
%type <memArg> argtype
%type <memArg> cpptype
%type <memArg> basetype
%type <signature> template
%type <signature> arglist
%type <signature> rawarglist
%type <signature> cpptypelist
%type <optsignature> optsig
%type <optsignature> optctorsig
%type <throwlist> optexceptions
%type <throwlist> exceptionlist
%type <number> optslot
%type <number> optref
%type <number> optconst
%type <number> optvirtual
%type <number> optabstract
%type <number> deref
%type <number> optnumber
%type <value> simplevalue
%type <valp> value
%type <valp> expr
%type <valp> optassign
%type <codeb> optaccesscode
%type <codeb> optgetcode
%type <codeb> optsetcode
%type <codeb> exphdrcode
%type <codeb> modhdrcode
%type <codeb> typehdrcode
%type <codeb> opttypehdrcode
%type <codeb> travcode
%type <codeb> clearcode
%type <codeb> readbufcode
%type <codeb> writebufcode
%type <codeb> segcountcode
%type <codeb> charbufcode
%type <codeb> modcode
%type <codeb> typecode
%type <codeb> codeblock
%type <codeb> codelines
%type <codeb> virtualcatchercode
%type <codeb> methodcode
%type <codeb> raisecode
%type <text> operatorname
%type <text> optfilename
%type <text> optname
%type <text> modname
%type <optflags> optflags
%type <optflags> flaglist
%type <flag> flag
%type <flag> flagvalue
%type <qchar> optunop
%type <qchar> binop
%type <scpvalp> scopepart
%type <scpvalp> scopedname
%type <fcall> exprlist
%type <boolean> qualifiers
%type <boolean> oredqualifiers
%type <boolean> modlang
%type <boolean> optclassbody
%type <exceptionbase> baseexception
%type <klass> class
%%
specification: statement
| specification statement
;
statement: {
/*
* We don't do these in parserEOF() because the parser
* is reading ahead and that would be too early.
*/
if (previousFile != NULL)
{
handleEOF();
if (newContext.prevmod != NULL)
handleEOM();
free(previousFile);
previousFile = NULL;
}
} modstatement
;
modstatement: module
| options
| noemitters
| copying
| include
| optinclude
| import
| timeline
| platforms
| feature
| license
| exphdrcode {
if (notSkipping())
appendCodeBlock(&currentSpec->exphdrcode, $1);
}
| modhdrcode {
if (notSkipping() && inMainModule())
appendCodeBlock(&currentSpec -> hdrcode,$1);
}
| modcode {
if (notSkipping() && inMainModule())
appendCodeBlock(&currentSpec -> cppcode,$1);
}
| preinitcode
| postinitcode
| unitcode
| prepycode
| doc
| exporteddoc
| makefile
| mappedtype
| mappedtypetmpl
| nsstatement
;
nsstatement: ifstart
| ifend
| namespace
| struct
| class
| classtmpl
| exception
| typedef
| enum
| function
| variable
| typehdrcode {
if (notSkipping())
{
classDef *scope = currentScope();
if (scope == NULL)
yyerror("%TypeHeaderCode can only be used in a namespace, class or mapped type");
appendCodeBlock(&scope->hdrcode, $1);
}
}
;
options: TK_OPTIONS '(' optionlist ')'
;
optionlist: TK_NAME {
appendString(&currentSpec->options, $1);
}
| optionlist ',' TK_NAME {
appendString(&currentSpec->options, $3);
}
;
noemitters: TK_NOEMITTERS {
if (notSkipping())
{
yywarning("%SIPNoEmitters is deprecated, please use %SIPOptions instead");
appendString(&currentSpec->options, "QtNoEmitters");
}
}
;
exception: TK_EXCEPTION scopedname baseexception optflags '{' opttypehdrcode raisecode '}' ';' {
if (notSkipping())
{
exceptionDef *xd;
char *pyname;
if (currentSpec->genc)
yyerror("%Exception not allowed in a C module");
pyname = getPythonName(&$4, scopedNameTail($2));
checkAttributes(currentSpec, NULL, pyname, FALSE);
xd = findException(currentSpec, $2, TRUE);
if (xd->cd != NULL)
yyerror("%Exception name has already been seen as a class name - it must be defined before being used");
if (xd->iff->module != NULL)
yyerror("The %Exception has already been defined");
/* Complete the definition. */
xd->iff->module = currentModule;
xd->pyname = pyname;
xd->bibase = $3.bibase;
xd->base = $3.base;
xd->hdrcode = $6;
xd->raisecode = $7;
if (xd->bibase != NULL || xd->base != NULL)
xd->exceptionnr = currentModule->nrexceptions++;
if (inMainModule() && xd->base != NULL && xd->base->iff->module != currentModule)
addToUsedList(&currentSpec->used, xd->base->iff);
}
}
;
baseexception: {
$$.bibase = NULL;
$$.base = NULL;
}
| '(' scopedname ')' {
exceptionDef *xd;
$$.bibase = NULL;
$$.base = NULL;
/* See if it is a defined exception. */
for (xd = currentSpec->exceptions; xd != NULL; xd = xd->next)
if (sameScopedName(xd->iff->fqcname, $2))
{
$$.base = xd;
break;
}
if (xd == NULL && $2->next == NULL && strncmp($2->name, "SIP_", 4) == 0)
{
/* See if it is a builtin exception. */
static char *builtins[] = {
"Exception",
"StopIteration",
"StandardError",
"ArithmeticError",
"LookupError",
"AssertionError",
"AttributeError",
"EOFError",
"FloatingPointError",
"EnvironmentError",
"IOError",
"OSError",
"ImportError",
"IndexError",
"KeyError",
"KeyboardInterrupt",
"MemoryError",
"NameError",
"OverflowError",
"RuntimeError",
"NotImplementedError",
"SyntaxError",
"IndentationError",
"TabError",
"ReferenceError",
"SystemError",
"SystemExit",
"TypeError",
"UnboundLocalError",
"UnicodeError",
"UnicodeEncodeError",
"UnicodeDecodeError",
"UnicodeTranslateError",
"ValueError",
"ZeroDivisionError",
"WindowsError",
"VMSError",
NULL
};
char **cp;
for (cp = builtins; *cp != NULL; ++cp)
if (strcmp($2->name + 4, *cp) == 0)
{
$$.bibase = *cp;
break;
}
}
if ($$.bibase == NULL && $$.base == NULL)
yyerror("Unknown exception base type");
}
;
raisecode: TK_RAISECODE codeblock {
$$ = $2;
}
;
mappedtype: TK_MAPPEDTYPE basetype {
if (notSkipping())
currentMappedType = newMappedType(currentSpec,&$2);
} mtdefinition
;
mappedtypetmpl: template TK_MAPPEDTYPE basetype {
int a;
if (currentSpec->genc)
yyerror("%MappedType templates not allowed in a C module");
/* Check the template arguments are all just simple names. */
for (a = 0; a < $1.nrArgs; ++a)
if ($1.args[a].atype != defined_type || $1.args[a].u.snd->next != NULL)
yyerror("%MappedType template arguments must be simple names");
if ($3.atype != template_type)
yyerror("%MappedType template must map a template type");
if (notSkipping())
{
mappedTypeTmplDef *mtt;
/* Check a template hasn't already been provided. */
for (mtt = currentSpec->mappedtypetemplates; mtt != NULL; mtt = mtt->next)
if (sameScopedName(mtt->mt->type.u.td->fqname, $3.u.td->fqname) && sameTemplateSignature(&mtt->mt->type.u.td->types, &$3.u.td->types, TRUE))
yyerror("%MappedType template for this type has already been defined");
$3.nrderefs = 0;
$3.argflags = 0;
mtt = sipMalloc(sizeof (mappedTypeTmplDef));
mtt->sig = $1;
mtt->mt = allocMappedType(&$3);
mtt->next = currentSpec->mappedtypetemplates;
currentSpec->mappedtypetemplates = mtt;
currentMappedType = mtt->mt;
}
} mtdefinition
;
mtdefinition: '{' mtbody '}' ';' {
if (notSkipping())
{
if (currentMappedType->convfromcode == NULL)
yyerror("%MappedType must have a %ConvertFromTypeCode directive");
if (currentMappedType->convtocode == NULL)
yyerror("%MappedType must have a %ConvertToTypeCode directive");
currentMappedType = NULL;
}
}
;
mtbody: mtline
| mtbody mtline
;
mtline: typehdrcode {
if (notSkipping())
appendCodeBlock(&currentMappedType -> hdrcode,$1);
}
| TK_FROMTYPE codeblock {
if (notSkipping())
{
if (currentMappedType -> convfromcode != NULL)
yyerror("%MappedType has more than one %ConvertFromTypeCode directive");
currentMappedType -> convfromcode = $2;
}
}
| TK_TOTYPE codeblock {
if (notSkipping())
{
if (currentMappedType -> convtocode != NULL)
yyerror("%MappedType has more than one %ConvertToTypeCode directive");
currentMappedType -> convtocode = $2;
}
}
;
namespace: TK_NAMESPACE TK_NAME {
if (currentSpec -> genc)
yyerror("namespace definition not allowed in a C module");
if (notSkipping())
{
classDef *ns;
ns = newClass(currentSpec,namespace_iface,text2scopedName($2));
pushScope(ns);
sectionFlags = 0;
}
} '{' nsbody '}' ';' {
if (inMainModule())
{
classDef *ns = currentScope();
if (!isUsedName(ns->iff->name))
{
varDef *vd;
for (vd = currentSpec->vars; vd != NULL; vd = vd->next)
if (vd->ecd == ns)
{
setIsUsedName(ns->iff->name);
break;
}
}
}
if (notSkipping())
popScope();
}
;
nsbody: nsstatement
| nsbody nsstatement
;
platforms: TK_PLATFORMS {
qualDef *qd;
for (qd = currentModule -> qualifiers; qd != NULL; qd = qd -> next)
if (qd -> qtype == platform_qualifier)
yyerror("%Platforms has already been defined for this module");
}
'{' platformlist '}' {
qualDef *qd;
int nrneeded;
/*
* Check that exactly one platform in the set was
* requested.
*/
nrneeded = 0;
for (qd = currentModule -> qualifiers; qd != NULL; qd = qd -> next)
if (qd -> qtype == platform_qualifier && isNeeded(qd))
++nrneeded;
if (nrneeded > 1)
yyerror("No more than one of these %Platforms must be specified with the -t flag");
}
;
platformlist: platform
| platformlist platform
;
platform: TK_NAME {
newQualifier(currentModule,-1,-1,$1,platform_qualifier);
}
;
feature: TK_FEATURE TK_NAME {
newQualifier(currentModule,-1,-1,$2,feature_qualifier);
}
;
timeline: TK_TIMELINE {
currentTimelineOrder = 0;
}
'{' qualifierlist '}' {
qualDef *qd;
int nrneeded;
/*
* Check that exactly one time slot in the set was
* requested.
*/
nrneeded = 0;
for (qd = currentModule -> qualifiers; qd != NULL; qd = qd -> next)
if (qd -> qtype == time_qualifier && isNeeded(qd))
++nrneeded;
if (nrneeded > 1)
yyerror("At most one of this %Timeline must be specified with the -t flag");
currentModule -> nrtimelines++;
}
;
qualifierlist: qualifiername
| qualifierlist qualifiername
;
qualifiername: TK_NAME {
newQualifier(currentModule,currentModule -> nrtimelines,currentTimelineOrder++,$1,time_qualifier);
}
;
ifstart: TK_IF '(' qualifiers ')' {
if (skipStackPtr >= MAX_NESTED_IF)
yyerror("Internal error: increase the value of MAX_NESTED_IF");
/* Nested %Ifs are implicit logical ands. */
if (skipStackPtr > 0)
$3 = ($3 && skipStack[skipStackPtr - 1]);
skipStack[skipStackPtr++] = $3;
}
;
oredqualifiers: TK_NAME {
$$ = platOrFeature($1,FALSE);
}
| '!' TK_NAME {
$$ = platOrFeature($2,TRUE);
}
| oredqualifiers TK_LOGICAL_OR TK_NAME {
$$ = (platOrFeature($3,FALSE) || $1);
}
| oredqualifiers TK_LOGICAL_OR '!' TK_NAME {
$$ = (platOrFeature($4,TRUE) || $1);
}
;
qualifiers: oredqualifiers
| optname '-' optname {
$$ = timePeriod($1,$3);
}
;
ifend: TK_END {
if (skipStackPtr-- <= 0)
yyerror("Too many %End directives");
}
;
license: TK_LICENSE optflags {
optFlag *of;
if ($2.nrFlags == 0)
yyerror("%License details not specified");
if ((of = findOptFlag(&$2,"Type",string_flag)) == NULL)
yyerror("%License type not specified");
currentModule -> license = sipMalloc(sizeof (licenseDef));
currentModule -> license -> type = of -> fvalue.sval;
currentModule -> license -> licensee =
((of = findOptFlag(&$2,"Licensee",string_flag)) != NULL)
? of -> fvalue.sval : NULL;
currentModule -> license -> timestamp =
((of = findOptFlag(&$2,"Timestamp",string_flag)) != NULL)
? of -> fvalue.sval : NULL;
currentModule -> license -> sig =
((of = findOptFlag(&$2,"Signature",string_flag)) != NULL)
? of -> fvalue.sval : NULL;
}
;
module: modlang modname optnumber {
/* Check the module hasn't already been defined. */
moduleDef *mod;
for (mod = currentSpec -> modules; mod != NULL; mod = mod -> next)
if (mod->fullname != NULL && strcmp(mod->fullname, $2) == 0)
yyerror("Module is already defined");
currentModule->fullname = $2;
if ((currentModule->name = strrchr($2, '.')) != NULL)
currentModule->name++;
else
currentModule->name = $2;
currentModule -> version = $3;
if (currentSpec -> genc < 0)
currentSpec -> genc = $1;
else if (currentSpec -> genc != $1)
yyerror("Cannot mix C and C++ modules");
}
;
modlang: TK_MODULE {
$$ = FALSE;
}
| TK_CMODULE {
$$ = TRUE;
}
;
modname: TK_NAME
| TK_PATHNAME {
/*
* The grammar design is a bit broken and this is the
* easiest way to allow periods in module names.
*/
char *cp;
for (cp = $1; *cp != '\0'; ++cp)
if (*cp != '.' && *cp != '_' && !isalnum(*cp))
yyerror("Invalid character in module name");
$$ = $1;
}
;
optnumber: {
$$ = -1;
}
| TK_NUMBER
;
include: TK_INCLUDE TK_PATHNAME {
parseFile(NULL,$2,NULL,FALSE);
}
;
optinclude: TK_OPTINCLUDE TK_PATHNAME {
parseFile(NULL,$2,NULL,TRUE);
}
;
import: TK_IMPORT TK_PATHNAME {
newImport($2);
}
;
optaccesscode: {
$$ = NULL;
}
| TK_ACCESSCODE codeblock {
$$ = $2;
}
;
optgetcode: {
$$ = NULL;
}
| TK_GETCODE codeblock {
$$ = $2;
}
;
optsetcode: {
$$ = NULL;
}
| TK_SETCODE codeblock {
$$ = $2;
}
;
copying: TK_COPYING codeblock {
if (inMainModule())
appendCodeBlock(&currentSpec -> copying,$2);
}
;
exphdrcode: TK_EXPHEADERCODE codeblock {
$$ = $2;
}
;
modhdrcode: TK_MODHEADERCODE codeblock {
$$ = $2;
}
;
typehdrcode: TK_TYPEHEADERCODE codeblock {
$$ = $2;
}
;
opttypehdrcode: {
$$ = NULL;
}
| typehdrcode
;
travcode: TK_TRAVERSECODE codeblock {
$$ = $2;
}
;
clearcode: TK_CLEARCODE codeblock {
$$ = $2;
}
;
readbufcode: TK_READBUFFERCODE codeblock {
$$ = $2;
}
;
writebufcode: TK_WRITEBUFFERCODE codeblock {
$$ = $2;
}
;
segcountcode: TK_SEGCOUNTCODE codeblock {
$$ = $2;
}
;
charbufcode: TK_CHARBUFFERCODE codeblock {
$$ = $2;
}
;
modcode: TK_MODCODE codeblock {
$$ = $2;
}
;
typecode: TK_TYPECODE codeblock {
$$ = $2;
}
;
preinitcode: TK_PREINITCODE codeblock {
if (notSkipping() && inMainModule())
appendCodeBlock(&currentSpec -> preinitcode,$2);
}
;
postinitcode: TK_POSTINITCODE codeblock {
if (notSkipping() && inMainModule())
appendCodeBlock(&currentSpec -> postinitcode,$2);
}
;
unitcode: TK_UNITCODE codeblock {
if (notSkipping() && inMainModule())
appendCodeBlock(&currentSpec->unitcode, $2);
}
;
prepycode: TK_PREPYCODE codeblock {
/*
* This is a no-op and is retained for compatibility
* until the last use of it (by SIP v3) can be removed
* from PyQt.
*/
}
;
doc: TK_DOC codeblock {
if (inMainModule())
appendCodeBlock(&currentSpec -> docs,$2);
}
;
exporteddoc: TK_EXPORTEDDOC codeblock {
appendCodeBlock(&currentSpec -> docs,$2);
}
;
makefile: TK_MAKEFILE TK_PATHNAME optfilename codeblock {
if (inMainModule())
yywarning("%Makefile is ignored, please use the -b flag instead");
}
;
codeblock: codelines TK_END
;
codelines: TK_CODELINE
| codelines TK_CODELINE {
$$ = $1;
append(&$$->frag, $2->frag);
free($2->frag);
free($2->filename);
free($2);
}
;
enum: TK_ENUM optname optflags {
if (notSkipping())
{
if (sectionFlags != 0 && (sectionFlags & ~(SECT_IS_PUBLIC | SECT_IS_PROT)) != 0)
yyerror("Class enums must be in the public or protected sections");
currentEnum = newEnum(currentSpec,currentModule,$2,&$3,sectionFlags);
}
} '{' optenumbody '}' ';'
;
optfilename: {
$$ = NULL;
}
| TK_PATHNAME {
$$ = $1;
}
;
optname: {
$$ = NULL;
}
| TK_NAME {
$$ = $1;
}
;
optenumbody:
| enumbody
;
enumbody: enumline
| enumbody enumline
;
enumline: ifstart
| ifend
| TK_NAME optenumassign optflags optcomma {
if (notSkipping())
{
/*
* Note that we don't use the assigned value.
* This is a hangover from when enums where
* generated in Python. We can remove it when
* we have got around to updating all the .sip
* files.
*/
enumMemberDef *emd, **tail;
emd = sipMalloc(sizeof (enumMemberDef));
emd -> pyname = cacheName(currentSpec, getPythonName(&$3, $1));
emd -> cname = $1;
emd -> ed = currentEnum;
emd -> next = NULL;
checkAttributes(currentSpec,emd -> ed -> ecd,emd -> pyname -> text,FALSE);
/* Append to preserve the order. */
for (tail = &currentEnum->members; *tail != NULL; tail = &(*tail)->next)
;
*tail = emd;
if (inMainModule())
setIsUsedName(emd -> pyname);
}
}
;
optcomma:
| ','
;
optenumassign:
| '=' value
;
optassign: {
$$ = NULL;
}
| '=' expr {
$$ = $2;
}
;
expr: value
| expr binop value {
valueDef *vd;
if ($1 -> vtype == string_value || $3 -> vtype == string_value)
yyerror("Invalid binary operator for string");
/* Find the last value in the existing expression. */
for (vd = $1; vd -> next != NULL; vd = vd -> next)
;
vd -> vbinop = $2;
vd -> next = $3;
$$ = $1;
}
;
binop: '-' {
$$ = '-';
}
| '+' {
$$ = '+';
}
| '*' {
$$ = '*';
}
| '/' {
$$ = '/';
}
| '&' {
$$ = '&';
}
| '|' {
$$ = '|';
}
;
optunop: {
$$ = '\0';
}
| '!' {
$$ = '!';
}
| '~' {
$$ = '~';
}
| '-' {
$$ = '-';
}
| '+' {
$$ = '+';
}
;
value: optunop simplevalue {
if ($1 != '\0' && $2.vtype == string_value)
yyerror("Invalid unary operator for string");
/*
* Convert the value to a simple expression on the
* heap.
*/
$$ = sipMalloc(sizeof (valueDef));
*$$ = $2;
$$ -> vunop = $1;
$$ -> vbinop = '\0';
$$ -> next = NULL;
}
;
scopedname: scopepart
| scopedname TK_SCOPE scopepart {
if (currentSpec -> genc)
yyerror("Scoped names are not allowed in a C module");
appendScopedName(&$1,$3);
}
;
scopepart: TK_NAME {
$$ = text2scopePart($1);
}
;
simplevalue: scopedname {
/*
* We let the C++ compiler decide if the value is a
* valid one - no point in building a full C++ parser
* here.
*/
$$.vtype = scoped_value;
$$.u.vscp = $1;
}
| basetype '(' exprlist ')' {
fcallDef *fcd;
fcd = sipMalloc(sizeof (fcallDef));
*fcd = $3;
fcd -> type = $1;
$$.vtype = fcall_value;
$$.u.fcd = fcd;
}
| TK_REAL {
$$.vtype = real_value;
$$.u.vreal = $1;
}
| TK_NUMBER {
$$.vtype = numeric_value;
$$.u.vnum = $1;
}
| TK_TRUE {
$$.vtype = numeric_value;
$$.u.vnum = 1;
}
| TK_FALSE {
$$.vtype = numeric_value;
$$.u.vnum = 0;
}
| TK_NULL {
$$.vtype = numeric_value;
$$.u.vnum = 0;
}
| TK_STRING {
$$.vtype = string_value;
$$.u.vstr = $1;
}
| TK_QCHAR {
$$.vtype = qchar_value;
$$.u.vqchar = $1;
}
;
exprlist: {
/* No values. */
$$.nrArgs = 0;
}
| expr {
/* The single or first expression. */
$$.args[0] = $1;
$$.nrArgs = 1;
}
| exprlist ',' expr {
/* Check that it wasn't ...(,expression...). */
if ($$.nrArgs == 0)
yyerror("First argument to function call is missing");
/* Check there is room. */
if ($1.nrArgs == MAX_NR_ARGS)
yyerror("Too many arguments to function call");
$$ = $1;
$$.args[$$.nrArgs] = $3;
$$.nrArgs++;
}
;
typedef: TK_TYPEDEF cpptype TK_NAME ';' {
if (notSkipping())
newTypedef(currentSpec,currentModule,$3,&$2);
}
| TK_TYPEDEF cpptype '(' deref TK_NAME ')' '(' cpptypelist ')' ';' {
if (notSkipping())
{
argDef ftype;
signatureDef *sig;
/* Create the full signature on the heap. */
sig = sipMalloc(sizeof (signatureDef));
*sig = $8;
sig -> result = $2;
/* Create the full type. */
ftype.atype = function_type;
ftype.argflags = 0;
ftype.nrderefs = $4;
ftype.defval = NULL;
ftype.u.sa = sig;
newTypedef(currentSpec,currentModule,$5,&ftype);
}
}
;
struct: TK_STRUCT TK_NAME {
if (notSkipping())
{
classDef *cd;
cd = newClass(currentSpec,class_iface,text2scopedName($2));
pushScope(cd);
sectionFlags = SECT_IS_PUBLIC;
}
} optflags '{' classbody '}' ';' {
if (notSkipping())
{
finishClass(currentSpec, currentModule, currentScope(), &$4);
popScope();
}
}
;
classtmpl: template class {
if (currentSpec->genc)
yyerror("Class templates not allowed in a C module");
if (notSkipping())
{
classTmplDef *tcd;
/*
* Make sure there is room for the extra class
* name argument.
*/
if ($1.nrArgs == MAX_NR_ARGS)
yyerror("Internal error - increase the value of MAX_NR_ARGS");
tcd = sipMalloc(sizeof (classTmplDef));
tcd->sig = $1;
tcd->cd = $2;
tcd->next = currentSpec->classtemplates;
currentSpec->classtemplates = tcd;
}
}
;
template: TK_TEMPLATE '<' cpptypelist '>' {
$$ = $3;
}
;
class: TK_CLASS scopedname {
if (currentSpec -> genc)
yyerror("Class definition not allowed in a C module");
if (notSkipping())
{
classDef *cd;
cd = newClass(currentSpec, class_iface, scopeScopedName($2));
pushScope(cd);
sectionFlags = SECT_IS_PRIVATE;
}
} superclasses optflags optclassbody ';' {
if (notSkipping())
{
classDef *cd = currentScope();
/*
* See if the class was defined or just
* declared.
*/
if ($6)
{
if ($2->next != NULL)
yyerror("A scoped name cannot be given in a class definition");
}
else if (cd->supers != NULL)
yyerror("Class has super-classes but no definition");
else
setIsOpaque(cd);
finishClass(currentSpec, currentModule, cd, &$5);
popScope();
/*
* Check that external classes have only been
* declared at the global scope.
*/
if (isExternal(cd) && currentScope() != NULL)
yyerror("External classes can only be declared in the global scope");
$$ = cd;
}
}
;
superclasses:
| ':' superlist
;
superlist: superclass
| superlist ',' superclass
;
superclass: scopedname {
if (notSkipping())
{
classDef *cd, *super;
cd = currentScope();
super = findClass(currentSpec,class_iface,$1);
appendToClassList(&cd -> supers,super);
addToUsedList(&cd->iff->used, super->iff);
}
}
;
optclassbody: {
$$ = FALSE;
}
| '{' classbody '}' {
$$ = TRUE;
}
;
classbody: classline
| classbody classline
;
classline: ifstart
| ifend
| namespace
| struct
| class
| exception
| typedef
| enum
| typecode {
if (notSkipping())
appendCodeBlock(&currentScope() -> cppcode,$1);
}
| typehdrcode {
if (notSkipping())
appendCodeBlock(&currentScope() -> hdrcode,$1);
}
| travcode {
if (currentScope()->travcode != NULL)
yyerror("%GCTraverseCode already given for class");
if (notSkipping())
currentScope()->travcode = $1;
}
| clearcode {
if (currentScope()->clearcode != NULL)
yyerror("%GCClearCode already given for class");
if (notSkipping())
currentScope()->clearcode = $1;
}
| readbufcode {
if (currentScope()->readbufcode != NULL)
yyerror("%BIGetReadBufferCode already given for class");
if (notSkipping())
currentScope()->readbufcode = $1;
}
| writebufcode {
if (currentScope()->writebufcode != NULL)
yyerror("%BIGetWriteBufferCode already given for class");
if (notSkipping())
currentScope()->writebufcode = $1;
}
| segcountcode {
if (currentScope()->segcountcode != NULL)
yyerror("%BIGetSegCountCode already given for class");
if (notSkipping())
currentScope()->segcountcode = $1;
}
| charbufcode {
if (currentScope()->charbufcode != NULL)
yyerror("%BIGetCharBufferCode already given for class");
if (notSkipping())
currentScope()->charbufcode = $1;
}
| ctor
| dtor
| varmember
| TK_TOSUBCLASS codeblock {
if (notSkipping())
{
classDef *cd = currentScope();
if (cd -> convtosubcode != NULL)
yyerror("Class has more than one %ConvertToSubClassCode directive");
cd -> convtosubcode = $2;
}
}
| TK_TOTYPE codeblock {
if (notSkipping())
{
classDef *cd = currentScope();
if (cd -> convtocode != NULL)
yyerror("Class has more than one %ConvertToTypeCode directive");
cd -> convtocode = $2;
}
}
| TK_PUBLIC optslot ':' {
if (currentSpec -> genc)
yyerror("public section not allowed in a C module");
if (notSkipping())
sectionFlags = SECT_IS_PUBLIC | $2;
}
| TK_PROTECTED optslot ':' {
if (currentSpec -> genc)
yyerror("protected section not allowed in a C module");
if (notSkipping())
sectionFlags = SECT_IS_PROT | $2;
}
| TK_PRIVATE optslot ':' {
if (currentSpec -> genc)
yyerror("private section not allowed in a C module");
if (notSkipping())
sectionFlags = SECT_IS_PRIVATE | $2;
}
| TK_SIGNALS ':' {
if (currentSpec -> genc)
yyerror("signals section not allowed in a C module");
if (notSkipping())
sectionFlags = SECT_IS_SIGNAL;
}
;
optslot: {
$$ = 0;
}
| TK_SLOTS {
$$ = SECT_IS_SLOT;
}
;
dtor: optvirtual '~' TK_NAME '(' ')' optexceptions optabstract optflags ';' methodcode virtualcatchercode {
/* Note that we allow non-virtual dtors in C modules. */
if (notSkipping())
{
classDef *cd = currentScope();
if (strcmp(classBaseName(cd),$3) != 0)
yyerror("Destructor doesn't have the same name as its class");
if (isDtor(cd))
yyerror("Destructor has already been defined");
if (currentSpec -> genc && $10 == NULL)
yyerror("Destructor in C modules must include %MethodCode");
cd -> dealloccode = $10;
cd -> dtorcode = $11;
cd -> dtorexceptions = $6;
cd -> classflags |= sectionFlags;
if ($7)
{
if (!$1)
yyerror("Abstract destructor must be virtual");
setIsAbstractClass(cd);
}
/*
* The class has a shadow if we have a virtual dtor or some
* dtor code.
*/
if ($1 || $11 != NULL)
{
if (currentSpec -> genc)
yyerror("Virtual destructor or %VirtualCatcherCode not allowed in a C module");
setHasShadow(cd);
}
if (getReleaseGIL(&$8))
setIsReleaseGILDtor(cd);
else if (getHoldGIL(&$8))
setIsHoldGILDtor(cd);
}
}
;
ctor: TK_EXPLICIT {currentCtorIsExplicit = TRUE;} simplector
| simplector
;
simplector: TK_NAME '(' arglist ')' optexceptions optflags optctorsig ';' methodcode {
/* Note that we allow ctors in C modules. */
if (notSkipping())
{
if (currentSpec -> genc)
{
if ($9 == NULL && $3.nrArgs != 0)
yyerror("Constructors with arguments in C modules must include %MethodCode");
if (currentCtorIsExplicit)
yyerror("Explicit constructors not allowed in a C module");
}
if ((sectionFlags & (SECT_IS_PUBLIC | SECT_IS_PROT | SECT_IS_PRIVATE)) == 0)
yyerror("Constructor must be in the public, private or protected sections");
newCtor($1,sectionFlags,&$3,&$6,$9,$5,$7,currentCtorIsExplicit);
}
free($1);
currentCtorIsExplicit = FALSE;
}
;
optctorsig: {
$$ = NULL;
}
| '[' '(' arglist ')' ']' {
$$ = sipMalloc(sizeof (signatureDef));
*$$ = $3;
}
;
optsig: {
$$ = NULL;
}
| '[' cpptype '(' arglist ')' ']' {
$$ = sipMalloc(sizeof (signatureDef));
*$$ = $4;
$$ -> result = $2;
}
;
optvirtual: {
$$ = FALSE;
}
| TK_VIRTUAL {
$$ = TRUE;
}
;
function: cpptype TK_NAME '(' arglist ')' optconst optexceptions optabstract optflags optsig ';' methodcode virtualcatchercode {
if (notSkipping())
{
if (sectionFlags != 0 && (sectionFlags & (SECT_IS_PUBLIC | SECT_IS_PROT | SECT_IS_PRIVATE | SECT_IS_SLOT | SECT_IS_SIGNAL)) == 0)
yyerror("Class function must be in the public, private, protected, slot or signal sections");
$4.result = $1;
newFunction(currentSpec,currentModule,
sectionFlags,currentIsStatic,
currentOverIsVirt,
$2,&$4,$6,$8,&$9,$12,$13,$7,$10);
}
currentIsStatic = FALSE;
currentOverIsVirt = FALSE;
}
| cpptype TK_OPERATOR operatorname '(' arglist ')' optconst optexceptions optabstract optflags optsig ';' methodcode virtualcatchercode {
if (notSkipping())
{
classDef *cd = currentScope();
/* Handle the unary '+' and '-' operators. */
if ((cd != NULL && $5.nrArgs == 0) || (cd == NULL && $5.nrArgs == 1))
{
if (strcmp($3, "__add__") == 0)
$3 = "__pos__";
else if (strcmp($3, "__sub__") == 0)
$3 = "__neg__";
}
$5.result = $1;
newFunction(currentSpec,currentModule,
sectionFlags,currentIsStatic,
currentOverIsVirt,
$3,&$5,$7,$9,&$10,$13,$14,$8,$11);
}
currentIsStatic = FALSE;
currentOverIsVirt = FALSE;
}
| TK_OPERATOR cpptype '(' arglist ')' optconst optexceptions optabstract optflags optsig ';' methodcode virtualcatchercode {
classDef *scope = currentScope();
if (scope == NULL || $4.nrArgs != 0)
yyerror("Operator casts must be specified in a class and have no arguments");
if (notSkipping())
{
char *sname;
switch ($2.atype)
{
case defined_type:
sname = NULL;
break;
case bool_type:
case cbool_type:
case short_type:
case ushort_type:
case int_type:
case cint_type:
case uint_type:
sname = "__int__";
break;
case long_type:
case ulong_type:
case longlong_type:
case ulonglong_type:
sname = "__long__";
break;
case float_type:
case cfloat_type:
case double_type:
case cdouble_type:
sname = "__float__";
break;
default:
yyerror("Unsupported operator cast");
}
if (sname != NULL)
{
$4.result = $2;
newFunction(currentSpec, currentModule,
sectionFlags,
currentIsStatic,
currentOverIsVirt, sname,
&$4, $6, $8, &$9, $12, $13,
$7, $10);
}
else
{
argList *al;
/* Check it doesn't already exist. */
for (al = scope->casts; al != NULL; al = al->next)
if (sameScopedName($2.u.snd, al->arg.u.snd))
yyerror("This operator cast has already been specified in this class");
al = sipMalloc(sizeof (argList));
al->arg = $2;
al->next = scope->casts;
scope->casts = al;
}
}
currentIsStatic = FALSE;
currentOverIsVirt = FALSE;
}
;
operatorname: '+' {$$ = "__add__";}
| '-' {$$ = "__sub__";}
| '*' {$$ = "__mul__";}
| '/' {$$ = "__div__";}
| '%' {$$ = "__mod__";}
| '&' {$$ = "__and__";}
| '|' {$$ = "__or__";}
| '^' {$$ = "__xor__";}
| '<' '<' {$$ = "__lshift__";}
| '>' '>' {$$ = "__rshift__";}
| '+' '=' {$$ = "__iadd__";}
| '-' '=' {$$ = "__isub__";}
| '*' '=' {$$ = "__imul__";}
| '/' '=' {$$ = "__idiv__";}
| '%' '=' {$$ = "__imod__";}
| '&' '=' {$$ = "__iand__";}
| '|' '=' {$$ = "__ior__";}
| '^' '=' {$$ = "__ixor__";}
| '<' '<' '=' {$$ = "__ilshift__";}
| '>' '>' '=' {$$ = "__irshift__";}
| '~' {$$ = "__invert__";}
| '(' ')' {$$ = "__call__";}
| '[' ']' {$$ = "__getitem__";}
| '<' {$$ = "__lt__";}
| '<' '=' {$$ = "__le__";}
| '=' '=' {$$ = "__eq__";}
| '!' '=' {$$ = "__ne__";}
| '>' {$$ = "__gt__";}
| '>' '=' {$$ = "__ge__";}
;
optconst: {
$$ = FALSE;
}
| TK_CONST {
$$ = TRUE;
}
;
optabstract: {
$$ = 0;
}
| '=' TK_NUMBER {
if ($2 != 0)
yyerror("Abstract virtual function '= 0' expected");
$$ = TRUE;
}
;
optflags: {
$$.nrFlags = 0;
}
| '/' flaglist '/' {
$$ = $2;
}
;
flaglist: flag {
$$.flags[0] = $1;
$$.nrFlags = 1;
}
| flaglist ',' flag {
/* Check there is room. */
if ($1.nrFlags == MAX_NR_FLAGS)
yyerror("Too many optional flags");
$$ = $1;
$$.flags[$$.nrFlags++] = $3;
}
;
flag: TK_NAME {
$$.ftype = bool_flag;
$$.fname = $1;
}
| TK_NAME '=' flagvalue {
$$ = $3;
$$.fname = $1;
}
;
flagvalue: TK_NAME {
$$.ftype = name_flag;
$$.fvalue.sval = $1;
}
| TK_STRING {
$$.ftype = string_flag;
$$.fvalue.sval = $1;
}
| TK_NUMBER {
$$.ftype = integer_flag;
$$.fvalue.ival = $1;
}
;
methodcode: {
$$ = NULL;
}
| TK_METHODCODE codeblock {
$$ = $2;
}
;
virtualcatchercode: {
$$ = NULL;
}
| TK_VIRTUALCATCHERCODE codeblock {
$$ = $2;
}
;
arglist: rawarglist {
int a, nrrxcon, nrrxdis, nrslotcon, nrslotdis, nrarray, nrarraysize;
nrrxcon = nrrxdis = nrslotcon = nrslotdis = nrarray = nrarraysize = 0;
for (a = 0; a < $1.nrArgs; ++a)
{
argDef *ad = &$1.args[a];
switch (ad -> atype)
{
case rxcon_type:
++nrrxcon;
break;
case rxdis_type:
++nrrxdis;
break;
case slotcon_type:
++nrslotcon;
break;
case slotdis_type:
++nrslotdis;
break;
}
if (isArray(ad))
++nrarray;
if (isArraySize(ad))
++nrarraysize;
}
if (nrrxcon != nrslotcon || nrrxcon > 1)
yyerror("SIP_RXOBJ_CON and SIP_SLOT_CON must both be given and at most once");
if (nrrxdis != nrslotdis || nrrxdis > 1)
yyerror("SIP_RXOBJ_DIS and SIP_SLOT_DIS must both be given and at most once");
if (nrarray != nrarraysize || nrarray > 1)
yyerror("/Array/ and /ArraySize/ must both be given and at most once");
$$ = $1;
}
;
rawarglist: {
/* No arguments. */
$$.nrArgs = 0;
}
| argvalue {
/* The single or first argument. */
$$.args[0] = $1;
$$.nrArgs = 1;
}
| rawarglist ',' argvalue {
/* Check that it wasn't ...(,arg...). */
if ($1.nrArgs == 0)
yyerror("First argument of the list is missing");
/* Check there is nothing after an ellipsis. */
if ($1.args[$1.nrArgs - 1].atype == ellipsis_type)
yyerror("An ellipsis must be at the end of the argument list");
/*
* If this argument has no default value, then the
* previous one mustn't either.
*/
if ($3.defval == NULL && $1.args[$1.nrArgs - 1].defval != NULL)
yyerror("Compulsory argument given after optional argument");
/* Check there is room. */
if ($1.nrArgs == MAX_NR_ARGS)
yyerror("Internal error - increase the value of MAX_NR_ARGS");
$$ = $1;
$$.args[$$.nrArgs] = $3;
$$.nrArgs++;
}
;
argvalue: TK_SIPSIGNAL optname optassign {
$$.atype = signal_type;
$$.argflags = ARG_IS_CONST;
$$.nrderefs = 0;
$$.name = $2;
$$.defval = $3;
currentSpec -> sigslots = TRUE;
}
| TK_SIPSLOT optname optassign {
$$.atype = slot_type;
$$.argflags = ARG_IS_CONST;
$$.nrderefs = 0;
$$.name = $2;
$$.defval = $3;
currentSpec -> sigslots = TRUE;
}
| TK_SIPANYSLOT optname optassign {
$$.atype = anyslot_type;
$$.argflags = ARG_IS_CONST;
$$.nrderefs = 0;
$$.name = $2;
$$.defval = $3;
currentSpec -> sigslots = TRUE;
}
| TK_SIPRXCON optname {
$$.atype = rxcon_type;
$$.argflags = 0;
$$.nrderefs = 0;
$$.name = $2;
currentSpec -> sigslots = TRUE;
}
| TK_SIPRXDIS optname {
$$.atype = rxdis_type;
$$.argflags = 0;
$$.nrderefs = 0;
$$.name = $2;
currentSpec -> sigslots = TRUE;
}
| TK_SIPSLOTCON '(' arglist ')' optname {
$$.atype = slotcon_type;
$$.argflags = ARG_IS_CONST;
$$.nrderefs = 0;
$$.name = $5;
$3.result.atype = void_type;
$3.result.argflags = 0;
$3.result.nrderefs = 0;
$$.u.sa = sipMalloc(sizeof (signatureDef));
*$$.u.sa = $3;
currentSpec -> sigslots = TRUE;
}
| TK_SIPSLOTDIS '(' arglist ')' optname {
$$.atype = slotdis_type;
$$.argflags = ARG_IS_CONST;
$$.nrderefs = 0;
$$.name = $5;
$3.result.atype = void_type;
$3.result.argflags = 0;
$3.result.nrderefs = 0;
$$.u.sa = sipMalloc(sizeof (signatureDef));
*$$.u.sa = $3;
currentSpec -> sigslots = TRUE;
}
| TK_QOBJECT optname {
$$.atype = qobject_type;
$$.argflags = 0;
$$.nrderefs = 0;
$$.name = $2;
}
| argtype optassign {
$$ = $1;
$$.defval = $2;
}
;
varmember: TK_STATIC {currentIsStatic = TRUE;} varmem
| varmem
;
varmem: member
| variable
;
member: TK_VIRTUAL {currentOverIsVirt = TRUE;} function
| function
;
variable: cpptype TK_NAME optflags ';' optaccesscode optgetcode optsetcode {
if (notSkipping())
{
/* Check the section. */
if (sectionFlags != 0)
{
if ((sectionFlags & SECT_IS_PUBLIC) == 0)
yyerror("Class variables must be in the public section");
if (!currentIsStatic && $5 != NULL)
yyerror("%AccessCode cannot be specified for non-static class variables");
}
if (currentIsStatic && currentSpec -> genc)
yyerror("Cannot have static members in a C structure");
if ($6 != NULL || $7 != NULL)
{
if ($5 != NULL)
yyerror("Cannot mix %AccessCode and %GetCode or %SetCode");
if (currentScope() == NULL)
yyerror("Cannot specify %GetCode or %SetCode for global variables");
}
newVar(currentSpec,currentModule,$2,currentIsStatic,&$1,&$3,$5,$6,$7);
}
currentIsStatic = FALSE;
}
;
cpptype: TK_CONST basetype deref optref {
$$ = $2;
$$.nrderefs = $3;
$$.argflags = ARG_IS_CONST | $4;
$$.name = NULL;
}
| basetype deref optref {
$$ = $1;
$$.nrderefs = $2;
$$.argflags = $3;
$$.name = NULL;
}
;
argtype: cpptype optname optflags {
$$ = $1;
$$.name = $2;
if (findOptFlag(&$3,"AllowNone",bool_flag) != NULL)
$$.argflags |= ARG_ALLOW_NONE;
if (findOptFlag(&$3,"GetWrapper",bool_flag) != NULL)
$$.argflags |= ARG_GET_WRAPPER;
if (findOptFlag(&$3,"Array",bool_flag) != NULL)
$$.argflags |= ARG_ARRAY;
if (findOptFlag(&$3,"ArraySize",bool_flag) != NULL)
$$.argflags |= ARG_ARRAY_SIZE;
if (findOptFlag(&$3,"Transfer",bool_flag) != NULL)
$$.argflags |= ARG_XFERRED;
if (findOptFlag(&$3,"TransferThis",bool_flag) != NULL)
$$.argflags |= ARG_THIS_XFERRED;
if (findOptFlag(&$3,"TransferBack",bool_flag) != NULL)
$$.argflags |= ARG_XFERRED_BACK;
if (findOptFlag(&$3,"In",bool_flag) != NULL)
$$.argflags |= ARG_IN;
if (findOptFlag(&$3,"Out",bool_flag) != NULL)
$$.argflags |= ARG_OUT;
if (findOptFlag(&$3,"Constrained",bool_flag) != NULL)
{
$$.argflags |= ARG_CONSTRAINED;
switch ($$.atype)
{
case bool_type:
$$.atype = cbool_type;
break;
case int_type:
$$.atype = cint_type;
break;
case float_type:
$$.atype = cfloat_type;
break;
case double_type:
$$.atype = cdouble_type;
break;
}
}
}
;
optref: {
$$ = 0;
}
| '&' {
if (currentSpec -> genc)
yyerror("References not allowed in a C module");
$$ = ARG_IS_REF;
}
;
deref: {
$$ = 0;
}
| deref '*' {
$$ = $1 + 1;
}
;
basetype: scopedname {
$$.atype = defined_type;
$$.u.snd = $1;
}
| scopedname '<' cpptypelist '>' {
templateDef *td;
td = sipMalloc(sizeof(templateDef));
td -> fqname = $1;
td -> types = $3;
$$.atype = template_type;
$$.u.td = td;
}
| TK_STRUCT scopedname {
/* In a C module all structures must be defined. */
if (currentSpec -> genc)
{
$$.atype = defined_type;
$$.u.snd = $2;
}
else
{
$$.atype = struct_type;
$$.u.sname = $2;
}
}
| TK_UNSIGNED TK_SHORT {
$$.atype = ushort_type;
}
| TK_SHORT {
$$.atype = short_type;
}
| TK_UNSIGNED {
$$.atype = uint_type;
}
| TK_UNSIGNED TK_INT {
$$.atype = uint_type;
}
| TK_INT {
$$.atype = int_type;
}
| TK_LONG {
$$.atype = long_type;
}
| TK_UNSIGNED TK_LONG {
$$.atype = ulong_type;
}
| TK_LONG TK_LONG {
$$.atype = longlong_type;
}
| TK_UNSIGNED TK_LONG TK_LONG {
$$.atype = ulonglong_type;
}
| TK_FLOAT {
$$.atype = float_type;
}
| TK_DOUBLE {
$$.atype = double_type;
}
| TK_BOOL {
$$.atype = bool_type;
}
| TK_SIGNED TK_CHAR {
$$.atype = sstring_type;
}
| TK_UNSIGNED TK_CHAR {
$$.atype = ustring_type;
}
| TK_CHAR {
$$.atype = string_type;
}
| TK_WCHAR_T {
$$.atype = wstring_type;
}
| TK_VOID {
$$.atype = void_type;
}
| TK_PYOBJECT {
$$.atype = pyobject_type;
}
| TK_PYTUPLE {
$$.atype = pytuple_type;
}
| TK_PYLIST {
$$.atype = pylist_type;
}
| TK_PYDICT {
$$.atype = pydict_type;
}
| TK_PYCALLABLE {
$$.atype = pycallable_type;
}
| TK_PYSLICE {
$$.atype = pyslice_type;
}
| TK_PYTYPE {
$$.atype = pytype_type;
}
| TK_ELLIPSIS {
$$.atype = ellipsis_type;
}
;
cpptypelist: cpptype {
/* The single or first type. */
$$.args[0] = $1;
$$.nrArgs = 1;
}
| cpptypelist ',' cpptype {
/* Check there is nothing after an ellipsis. */
if ($1.args[$1.nrArgs - 1].atype == ellipsis_type)
yyerror("An ellipsis must be at the end of the argument list");
/* Check there is room. */
if ($1.nrArgs == MAX_NR_ARGS)
yyerror("Internal error - increase the value of MAX_NR_ARGS");
$$ = $1;
$$.args[$$.nrArgs] = $3;
$$.nrArgs++;
}
;
optexceptions: {
$$ = NULL;
}
| TK_THROW '(' exceptionlist ')' {
if (currentSpec->genc)
yyerror("Exceptions not allowed in a C module");
if (notSkipping() && inMainModule())
{
int e;
ifaceFileList **ifl;
/*
* Make sure the exceptions' header files are
* included. We unconditionally mark them to
* be included in the current scope's header
* file to save us the effort of checking if
* they are being used with a protected method,
* a virtual or a signal.
*/
ifl = (currentScope() != NULL) ? &currentScope()->iff->used : &currentSpec->used;
for (e = 0; e < $3->nrArgs; ++e)
addToUsedList(ifl, $3->args[e]->iff);
}
$$ = $3;
}
;
exceptionlist: {
/* Empty list so use a blank. */
$$ = sipMalloc(sizeof (throwArgs));
$$ -> nrArgs = 0;
}
| scopedname {
/* The only or first exception. */
$$ = sipMalloc(sizeof (throwArgs));
$$ -> nrArgs = 1;
$$ -> args[0] = findException(currentSpec, $1, FALSE);
}
| exceptionlist ',' scopedname {
/* Check that it wasn't ...(,arg...). */
if ($1 -> nrArgs == 0)
yyerror("First exception of throw specifier is missing");
/* Check there is room. */
if ($1 -> nrArgs == MAX_NR_ARGS)
yyerror("Internal error - increase the value of MAX_NR_ARGS");
$$ = $1;
$$ -> args[$$ -> nrArgs++] = findException(currentSpec, $3, FALSE);
}
;
%%
/*
* Parse the specification.
*/
void parse(sipSpec *spec,FILE *fp,char *filename,stringList *tsl,
stringList *xfl)
{
classTmplDef *tcd;
/* Initialise the spec. */
spec -> modules = NULL;
spec -> namecache = NULL;
spec -> ifacefiles = NULL;
spec -> classes = NULL;
spec -> classtemplates = NULL;
spec -> proxies = NULL;
spec -> exceptions = NULL;
spec -> mappedtypes = NULL;
spec -> mappedtypetemplates = NULL;
spec -> qobjclass = -1;
spec -> enums = NULL;
spec -> vars = NULL;
spec -> othfuncs = NULL;
spec -> overs = NULL;
spec -> typedefs = NULL;
spec -> copying = NULL;
spec -> exphdrcode = NULL;
spec -> hdrcode = NULL;
spec -> cppcode = NULL;
spec -> docs = NULL;
spec -> preinitcode = NULL;
spec -> postinitcode = NULL;
spec -> unitcode = NULL;
spec -> used = NULL;
spec -> sigslots = FALSE;
spec -> genc = -1;
spec -> options = NULL;
currentSpec = spec;
neededQualifiers = tsl;
excludedQualifiers = xfl;
currentModule = NULL;
currentMappedType = NULL;
currentOverIsVirt = FALSE;
currentCtorIsExplicit = FALSE;
currentIsStatic = FALSE;
previousFile = NULL;
skipStackPtr = 0;
currentScopeIdx = 0;
sectionFlags = 0;
newModule(fp,filename);
spec -> module = currentModule;
yyparse();
handleEOF();
handleEOM();
/*
* Go through each template class and remove it from the list of
* classes.
*/
for (tcd = spec->classtemplates; tcd != NULL; tcd = tcd->next)
{
classDef **cdp;
for (cdp = &spec->classes; *cdp != NULL; cdp = &(*cdp)->next)
if (*cdp == tcd->cd)
{
ifaceFileDef **ifdp;
/* Remove the interface file as well. */
for (ifdp = &spec->ifacefiles; *ifdp != NULL; ifdp = &(*ifdp)->next)
if (*ifdp == tcd->cd->iff)
{
*ifdp = (*ifdp)->next;
break;
}
*cdp = (*cdp)->next;
break;
}
}
}
/*
* Tell the parser that a complete file has now been read.
*/
void parserEOF(char *name,parserContext *pc)
{
previousFile = sipStrdup(name);
newContext = *pc;
}
/*
* Append a class definition to a class list if it doesn't already appear.
* Append is needed specifically for the list of super-classes because the
* order is important to Python.
*/
void appendToClassList(classList **clp,classDef *cd)
{
classList *new;
/* Find the end of the list. */
while (*clp != NULL)
{
if ((*clp) -> cd == cd)
return;
clp = &(*clp) -> next;
}
new = sipMalloc(sizeof (classList));
new -> cd = cd;
new -> next = NULL;
*clp = new;
}
/*
* Create a new module for the current specification and make it current.
*/
static void newModule(FILE *fp,char *filename)
{
moduleDef *newmod;
parseFile(fp,filename,currentModule,FALSE);
newmod = sipMalloc(sizeof (moduleDef));
newmod -> fullname = NULL;
newmod -> name = NULL;
newmod -> version = -1;
newmod -> modflags = 0;
newmod -> modulenr = -1;
newmod -> file = filename;
newmod -> qualifiers = NULL;
newmod -> root.cd = NULL;
newmod -> root.child = NULL;
newmod -> nrtimelines = 0;
newmod -> nrclasses = 0;
newmod -> nrexceptions = 0;
newmod -> nrmappedtypes = 0;
newmod -> nrenums = 0;
newmod -> nrtypedefs = 0;
newmod -> nrvirthandlers = 0;
newmod -> virthandlers = NULL;
newmod -> license = NULL;
newmod -> allimports = NULL;
newmod -> imports = NULL;
newmod -> next = currentSpec -> modules;
currentModule = currentSpec->modules = newmod;
}
/*
* Switch to parsing a new file.
*/
static void parseFile(FILE *fp,char *name,moduleDef *prevmod,int optional)
{
parserContext pc;
pc.ifdepth = skipStackPtr;
pc.prevmod = prevmod;
setInputFile(fp,name,&pc,optional);
}
/*
* Find an interface file, or create a new one.
*/
ifaceFileDef *findIfaceFile(sipSpec *pt, moduleDef *mod, scopedNameDef *fqname,
ifaceFileType iftype, argDef *ad)
{
ifaceFileDef *iff;
/* See if the name is already used. */
for (iff = pt -> ifacefiles; iff != NULL; iff = iff -> next)
{
if (!sameScopedName(iff -> fqcname,fqname))
continue;
/*
* They must be the same type except that we allow a class if
* if we want an exception. This is because we allow classes
* to be used before they are defined.
*/
if (iff -> type != iftype)
if (iftype != exception_iface || iff -> type != class_iface)
yyerror("A class, exception, namespace or mapped type has already been defined with the same name");
/* Ignore an external class declared in another module. */
if (iftype == class_iface && iff->module != mod)
{
classDef *cd;
for (cd = pt->classes; cd != NULL; cd = cd->next)
if (cd->iff == iff)
break;
if (cd != NULL && iff->module != NULL && isExternal(cd))
continue;
}
/*
* If this is a mapped type with the same name defined in a
* different module, then check that this type isn't the same
* as any of the mapped types defined in that module.
*/
if (iftype == mappedtype_iface && iff -> module != mod)
{
mappedTypeDef *mtd;
for (mtd = pt -> mappedtypes; mtd != NULL; mtd = mtd -> next)
{
if (mtd -> iff != iff)
continue;
if (ad -> atype != template_type ||
mtd -> type.atype != template_type ||
sameBaseType(ad,&mtd -> type))
yyerror("Mapped type has already been defined in another module");
}
/*
* If we got here then we have a mapped type based on
* an existing template, but with unique parameters.
* We don't want to use interface files from other
* modules, so skip this one.
*/
continue;
}
/* Ignore a namespace defined in another module. */
if (iftype == namespace_iface && iff->module != mod)
continue;
return iff;
}
iff = sipMalloc(sizeof (ifaceFileDef));
iff -> name = cacheName(pt,scopedNameTail(fqname));
iff -> type = iftype;
iff -> fqcname = fqname;
iff -> module = NULL;
iff -> used = NULL;
iff -> next = pt -> ifacefiles;
pt -> ifacefiles = iff;
return iff;
}
/*
* Find a class definition in a parse tree.
*/
static classDef *findClass(sipSpec *pt,ifaceFileType iftype,
scopedNameDef *fqname)
{
return findClassWithInterface(pt, findIfaceFile(pt, currentModule, fqname, iftype, NULL));
}
/*
* Find a class definition given an existing interface file.
*/
static classDef *findClassWithInterface(sipSpec *pt, ifaceFileDef *iff)
{
classDef *cd;
for (cd = pt -> classes; cd != NULL; cd = cd -> next)
if (cd -> iff == iff)
return cd;
/* Create a new one. */
cd = sipMalloc(sizeof (classDef));
cd -> iff = iff;
cd -> pyname = classBaseName(cd);
cd -> classnr = -1;
cd -> classflags = 0;
cd -> userflags = 0;
cd -> ecd = NULL;
cd -> dtorexceptions = NULL;
cd -> real = NULL;
cd -> node = NULL;
cd -> supers = NULL;
cd -> mro = NULL;
cd -> td = NULL;
cd -> ctors = NULL;
cd -> defctor = NULL;
cd -> dealloccode = NULL;
cd -> dtorcode = NULL;
cd -> members = NULL;
cd -> overs = NULL;
cd -> casts = NULL;
cd -> vmembers = NULL;
cd -> visible = NULL;
cd -> cppcode = NULL;
cd -> hdrcode = NULL;
cd -> convtosubcode = NULL;
cd -> subbase = NULL;
cd -> convtocode = NULL;
cd -> travcode = NULL;
cd -> clearcode = NULL;
cd -> readbufcode = NULL;
cd -> writebufcode = NULL;
cd -> segcountcode = NULL;
cd -> charbufcode = NULL;
cd -> next = pt -> classes;
pt -> classes = cd;
return cd;
}
/*
* Add an interface file to an interface file list if it isn't already there.
*/
ifaceFileList *addToUsedList(ifaceFileList **ifflp, ifaceFileDef *iff)
{
ifaceFileList *iffl;
while ((iffl = *ifflp) != NULL)
{
/* Don't bother if it is already there. */
if (iffl -> iff == iff)
return iffl;
ifflp = &iffl -> next;
}
iffl = sipMalloc(sizeof (ifaceFileList));
iffl->iff = iff;
iffl->header = FALSE;
iffl->next = NULL;
*ifflp = iffl;
return iffl;
}
/*
* Find an undefined (or create a new) exception definition in a parse tree.
*/
static exceptionDef *findException(sipSpec *pt, scopedNameDef *fqname, int new)
{
exceptionDef *xd, **tail;
ifaceFileDef *iff;
classDef *cd;
iff = findIfaceFile(pt, currentModule, fqname, exception_iface, NULL);
/* See if it is an existing one. */
for (xd = pt->exceptions; xd != NULL; xd = xd->next)
if (xd->iff == iff)
return xd;
/*
* If it is an exception interface file then we have never seen this
* name before. We require that exceptions are defined before being
* used, but don't make the same requirement of classes (for reasons of
* backwards compatibility). Therefore the name must be reinterpreted
* as a (as yet undefined) class.
*/
if (new)
if (iff->type == exception_iface)
cd = NULL;
else
yyerror("There is already a class with the same name or the exception has been used before being defined");
else
{
if (iff->type == exception_iface)
iff->type = class_iface;
cd = findClassWithInterface(pt, iff);
}
/* Create a new one. */
xd = sipMalloc(sizeof (exceptionDef));
xd->exceptionnr = -1;
xd->iff = iff;
xd->pyname = NULL;
xd->cd = cd;
xd->bibase = NULL;
xd->base = NULL;
xd->hdrcode = NULL;
xd->raisecode = NULL;
xd->next = NULL;
/* Append it to the list. */
for (tail = &pt->exceptions; *tail != NULL; tail = &(*tail)->next)
;
*tail = xd;
return xd;
}
/*
* Find an undefined (or create a new) class definition in a parse tree.
*/
static classDef *newClass(sipSpec *pt,ifaceFileType iftype,
scopedNameDef *fqname)
{
int flags;
classDef *cd, *scope;
codeBlock *hdrcode;
if (sectionFlags & SECT_IS_PRIVATE)
yyerror("Classes, structs and namespaces must be in the public or or protected sections");
flags = 0;
if ((scope = currentScope()) != NULL)
{
if (sectionFlags & SECT_IS_PROT)
flags = CLASS_IS_PROTECTED;
hdrcode = scope -> hdrcode;
}
else
hdrcode = NULL;
if (pt -> genc)
{
/* C structs are always global types. */
while (fqname -> next != NULL)
fqname = fqname -> next;
scope = NULL;
}
cd = findClass(pt,iftype,fqname);
/* Check it hasn't already been defined. */
if (iftype != namespace_iface && cd->iff->module != NULL)
yyerror("The struct/class has already been defined");
/* Complete the initialisation. */
cd->classflags |= flags;
cd->ecd = scope;
cd->iff->module = currentModule;
appendCodeBlock(&cd->hdrcode, hdrcode);
/* See if it is a namespace extender. */
if (iftype == namespace_iface)
{
classDef *ns;
for (ns = pt->classes; ns != NULL; ns = ns->next)
{
if (ns == cd)
continue;
if (ns->iff->type != namespace_iface)
continue;
if (!sameScopedName(ns->iff->fqcname, fqname))
continue;
cd->real = ns;
break;
}
}
return cd;
}
/*
* Tidy up after finishing a class definition.
*/
static void finishClass(sipSpec *pt, moduleDef *mod, classDef *cd, optFlags *of)
{
char *pyname;
optFlag *flg;
/* Get the Python name and see if it is different to the C++ name. */
pyname = getPythonName(of, classBaseName(cd));
cd -> pyname = NULL;
checkAttributes(pt, cd->ecd, pyname, FALSE);
cd->pyname = pyname;
if (cd->pyname != classBaseName(cd))
setIsRenamedClass(cd);
if ((flg = findOptFlag(of, "TypeFlags", integer_flag)) != NULL)
cd->userflags = flg->fvalue.ival;
if (isOpaque(cd))
{
if (findOptFlag(of, "External", bool_flag) != NULL)
setIsExternal(cd);
}
else
{
int seq_might, seq_not;
memberDef *md;
if (findOptFlag(of, "NoDefaultCtors", bool_flag) != NULL)
setNoDefaultCtors(cd);
if (cd -> ctors == NULL)
{
if (!noDefaultCtors(cd))
{
/* Provide a default ctor. */
cd->ctors = sipMalloc(sizeof (ctorDef));
cd->ctors->ctorflags = SECT_IS_PUBLIC;
cd->ctors->pysig.nrArgs = 0;
cd->ctors->cppsig = &cd -> ctors -> pysig;
cd->ctors->exceptions = NULL;
cd->ctors->methodcode = NULL;
cd->ctors->prehook = NULL;
cd->ctors->posthook = NULL;
cd->ctors->next = NULL;
cd->defctor = cd->ctors;
setCanCreate(cd);
}
}
else if (cd -> defctor == NULL)
{
ctorDef *ct, *last = NULL;
for (ct = cd -> ctors; ct != NULL; ct = ct -> next)
{
if (!isPublicCtor(ct))
continue;
if (ct -> pysig.nrArgs == 0 || ct -> pysig.args[0].defval != NULL)
{
cd -> defctor = ct;
break;
}
if (last == NULL)
last = ct;
}
/* The last resort is the first public ctor. */
if (cd->defctor == NULL)
cd->defctor = last;
}
if (findOptFlag(of,"Abstract",bool_flag) != NULL)
{
setIsAbstractClass(cd);
setIsIncomplete(cd);
resetCanCreate(cd);
}
/* We assume a public dtor if nothing specific was provided. */
if (!isDtor(cd))
setIsPublicDtor(cd);
if (findOptFlag(of, "DelayDtor", bool_flag) != NULL)
{
setIsDelayedDtor(cd);
setHasDelayedDtors(mod);
}
/*
* There are subtle differences between the add and concat methods and
* the multiply and repeat methods. The number versions can have their
* operands swapped and may return NotImplemented. If the user has
* used the /Numeric/ annotation or there are other numeric operators
* then we use add/multiply. Otherwise, if there are indexing
* operators then we use concat/repeat.
*/
seq_might = seq_not = FALSE;
for (md = cd -> members; md != NULL; md = md -> next)
switch (md -> slot)
{
case getitem_slot:
case setitem_slot:
case delitem_slot:
/* This might be a sequence. */
seq_might = TRUE;
break;
case sub_slot:
case isub_slot:
case div_slot:
case idiv_slot:
case mod_slot:
case imod_slot:
case pos_slot:
case neg_slot:
/* This is definately not a sequence. */
seq_not = TRUE;
break;
}
if (!seq_not && seq_might)
for (md = cd -> members; md != NULL; md = md -> next)
{
/* Ignore if the user has been explicit. */
if (isNumeric(md))
continue;
switch (md -> slot)
{
case add_slot:
md -> slot = concat_slot;
break;
case iadd_slot:
md -> slot = iconcat_slot;
break;
case mul_slot:
md -> slot = repeat_slot;
break;
case imul_slot:
md -> slot = irepeat_slot;
break;
}
}
}
if (inMainModule())
{
setIsUsedName(cd->iff->name);
setIsClassName(cd->iff->name);
}
}
/*
* Create a new mapped type.
*/
static mappedTypeDef *newMappedType(sipSpec *pt,argDef *ad)
{
mappedTypeDef *mtd;
scopedNameDef *snd;
ifaceFileDef *iff;
/* Check that the type is one we want to map. */
switch (ad -> atype)
{
case defined_type:
snd = ad -> u.snd;
break;
case template_type:
snd = ad -> u.td -> fqname;
break;
case struct_type:
snd = ad -> u.sname;
break;
default:
yyerror("Invalid type for %MappedType");
}
iff = findIfaceFile(pt, currentModule, snd, mappedtype_iface, ad);
if (inMainModule())
setIsUsedName(iff -> name);
/* Check it hasn't already been defined. */
for (mtd = pt -> mappedtypes; mtd != NULL; mtd = mtd -> next)
if (mtd -> iff == iff)
{
/*
* We allow types based on the same template but with
* different arguments.
*/
if (ad -> atype != template_type ||
sameBaseType(ad,&mtd -> type))
yyerror("Mapped type has already been defined in this module");
}
/* The module may not have been set yet. */
iff -> module = currentModule;
/* Create a new mapped type. */
mtd = allocMappedType(ad);
mtd -> iff = iff;
mtd -> next = pt -> mappedtypes;
pt -> mappedtypes = mtd;
return mtd;
}
/*
* Allocate, intialise and return a mapped type structure.
*/
mappedTypeDef *allocMappedType(argDef *type)
{
mappedTypeDef *mtd;
mtd = sipMalloc(sizeof (mappedTypeDef));
mtd->type = *type;
mtd->type.argflags = 0;
mtd->type.nrderefs = 0;
mtd->mappednr = -1;
mtd->iff = NULL;
mtd->hdrcode = NULL;
mtd->convfromcode = NULL;
mtd->convtocode = NULL;
mtd->next = NULL;
return mtd;
}
/*
* Create a new enum.
*/
static enumDef *newEnum(sipSpec *pt,moduleDef *mod,char *name,optFlags *of,
int flags)
{
enumDef *ed;
classDef *escope = currentScope();
ed = sipMalloc(sizeof (enumDef));
if (name != NULL)
{
ed -> fqcname = text2scopedName(name);
ed -> pyname = cacheName(pt, getPythonName(of, name));
checkAttributes(pt, escope, ed->pyname->text, FALSE);
}
else
{
ed -> fqcname = NULL;
ed -> pyname = NULL;
}
ed -> enumflags = flags;
ed -> enumnr = -1;
ed -> ecd = escope;
ed -> pcd = (flags & SECT_IS_PROT) ? escope : NULL;
ed -> module = mod;
ed -> members = NULL;
ed -> slots = NULL;
ed -> overs = NULL;
ed -> next = pt -> enums;
if (name != NULL && strcmp(ed->pyname->text, name) != 0)
setIsRenamedEnum(ed);
pt -> enums = ed;
if (escope != NULL)
setHasEnums(escope);
return ed;
}
/*
* Get the type values and (optionally) the type names for substitution in
* handwritten code.
*/
void appendTypeStrings(scopedNameDef *ename, signatureDef *patt, signatureDef *src, signatureDef *known, scopedNameDef **names, scopedNameDef **values)
{
int a;
for (a = 0; a < patt->nrArgs; ++a)
{
argDef *pad = &patt->args[a];
if (pad->atype == defined_type)
{
char *nam = NULL;
/*
* If the type names are already known then check that
* this is one of them.
*/
if (known == NULL)
nam = scopedNameTail(pad->u.snd);
else if (pad->u.snd->next == NULL)
{
int k;
for (k = 0; k < known->nrArgs; ++k)
if (strcmp(pad->u.snd->name, known->args[k].u.snd->name) == 0)
{
nam = pad->u.snd->name;
break;
}
}
if (nam == NULL)
continue;
/* Add the name. */
appendScopedName(names, text2scopePart(nam));
/* Add the corresponding value. */
appendScopedName(values, text2scopePart(getType(ename, &src->args[a])));
}
else if (pad->atype == template_type)
{
argDef *sad = &src->args[a];
/* These checks shouldn't be necessary, but... */
if (sad->atype == template_type && pad->u.td->types.nrArgs == sad->u.td->types.nrArgs)
appendTypeStrings(ename, &pad->u.td->types, &sad->u.td->types, known, names, values);
}
}
}
/*
* Convert a type to a string. We impose some limitations because I'm too lazy
* to handle everything that might be needed one day.
*/
static char *getType(scopedNameDef *ename, argDef *ad)
{
if (ad->atype == defined_type)
return scopedNameToString(ad->u.snd);
fatalScopedName(ename);
fatal(": unsupported type argument to template class instantiation\n");
return NULL;
}
/*
* Convert a scoped name to a string on the heap.
*/
static char *scopedNameToString(scopedNameDef *name)
{
static const char scope_string[] = "::";
size_t len;
scopedNameDef *snd;
char *s, *dp;
/* Work out the length of buffer needed. */
len = 0;
for (snd = name; snd != NULL; snd = snd->next)
{
len += strlen(snd->name);
if (snd->next != NULL)
len += strlen(scope_string);
}
/* Allocate and populate the buffer. */
dp = s = sipMalloc(len + 1);
for (snd = name; snd != NULL; snd = snd->next)
{
strcpy(dp, snd->name);
dp += strlen(snd->name);
if (snd->next != NULL)
{
strcpy(dp, scope_string);
dp += strlen(scope_string);
}
}
return s;
}
/*
* Instantiate a class template.
*/
static void instantiateClassTemplate(sipSpec *pt, moduleDef *mod, classDef *scope, scopedNameDef *fqname, classTmplDef *tcd, templateDef *td)
{
scopedNameDef *type_names, *type_values;
classDef *cd;
ctorDef *oct, **cttail;
memberDef *omd, **mdtail;
overDef *ood, **odtail;
argDef *ad;
ifaceFileList *iffl, **used;
type_names = type_values = NULL;
appendTypeStrings(classFQCName(tcd->cd), &tcd->sig, &td->types, NULL, &type_names, &type_values);
/*
* Add a mapping from the template name to the instantiated name. If
* we have got this far we know there is room for it.
*/
ad = &tcd->sig.args[tcd->sig.nrArgs++];
ad->atype = defined_type;
ad->name = NULL;
ad->argflags = 0;
ad->nrderefs = 0;
ad->defval = NULL;
ad->u.snd = classFQCName(tcd->cd);
appendScopedName(&type_names, text2scopePart(scopedNameTail(classFQCName(tcd->cd))));
appendScopedName(&type_values, text2scopePart(scopedNameToString(fqname)));
/* Create the new class. */
cd = sipMalloc(sizeof (classDef));
/* Start with a shallow copy. */
*cd = *tcd->cd;
cd->pyname = scopedNameTail(fqname);
cd->td = td;
/* Handle the interface file. */
cd->iff = findIfaceFile(pt, mod, fqname, class_iface, NULL);
cd->iff->module = mod;
/* Make a copy of the used list and add the enclosing scope. */
used = &cd->iff->used;
for (iffl = tcd->cd->iff->used; iffl != NULL; iffl = iffl->next)
addToUsedList(used, iffl->iff);
if (scope != NULL)
addToUsedList(&cd->iff->used, scope->iff);
if (inMainModule())
{
setIsUsedName(cd->iff->name);
setIsClassName(cd->iff->name);
}
cd->ecd = currentScope();
/* Handle the ctors. */
cd->ctors = NULL;
cttail = &cd->ctors;
for (oct = tcd->cd->ctors; oct != NULL; oct = oct->next)
{
ctorDef *nct = sipMalloc(sizeof (ctorDef));
/* Start with a shallow copy. */
*nct = *oct;
templateSignature(&nct->pysig, FALSE, tcd, td, cd);
if (oct->cppsig == NULL)
nct->cppsig = NULL;
else if (oct->cppsig == &oct->pysig)
nct->cppsig = &nct->pysig;
else
{
nct->cppsig = sipMalloc(sizeof (signatureDef));
*nct->cppsig = *oct->cppsig;
templateSignature(nct->cppsig, FALSE, tcd, td, cd);
}
nct->methodcode = templateCode(pt, used, nct->methodcode, type_names, type_values);
nct->next = NULL;
*cttail = nct;
cttail = &nct->next;
/* Handle the default ctor. */
if (tcd->cd->defctor == oct)
cd->defctor = nct;
}
cd->dealloccode = templateCode(pt, used, cd->dealloccode, type_names, type_values);
cd->dtorcode = templateCode(pt, used, cd->dtorcode, type_names, type_values);
/* Handle the members, ie. the common parts of overloads. */
cd->members = NULL;
mdtail = &cd->members;
for (omd = tcd->cd->members; omd != NULL; omd = omd->next)
{
memberDef *nmd = sipMalloc(sizeof (memberDef));
/* Start with a shallow copy. */
*nmd = *omd;
nmd->module = mod;
nmd->next = NULL;
*mdtail = nmd;
mdtail = &nmd->next;
}
/* Handle the overloads. */
cd->overs = NULL;
odtail = &cd->overs;
for (ood = tcd->cd->overs; ood != NULL; ood = ood->next)
{
overDef *nod = sipMalloc(sizeof (overDef));
memberDef *nmd;
/* Start with a shallow copy. */
*nod = *ood;
for (nmd = cd->members, omd = tcd->cd->members; omd != NULL; omd = omd->next, nmd = nmd->next)
if (omd == ood->common)
{
nod->common = nmd;
break;
}
templateSignature(&nod->pysig, TRUE, tcd, td, cd);
if (ood->cppsig == &ood->pysig)
nod->cppsig = &nod->pysig;
else
{
nod->cppsig = sipMalloc(sizeof (signatureDef));
*nod->cppsig = *ood->cppsig;
templateSignature(nod->cppsig, TRUE, tcd, td, cd);
}
nod->methodcode = templateCode(pt, used, nod->methodcode, type_names, type_values);
/* Handle any virtual handler. */
if (ood->virthandler != NULL)
{
nod->virthandler = sipMalloc(sizeof (virtHandlerDef));
/* Start with a shallow copy. */
*nod->virthandler = *ood->virthandler;
if (ood->virthandler->cppsig == &ood->pysig)
nod->virthandler->cppsig = &nod->pysig;
else
{
nod->virthandler->cppsig = sipMalloc(sizeof (signatureDef));
*nod->virthandler->cppsig = *ood->virthandler->cppsig;
templateSignature(nod->virthandler->cppsig, TRUE, tcd, td, cd);
}
nod->virthandler->module = mod;
nod->virthandler->virtcode = templateCode(pt, used, nod->virthandler->virtcode, type_names, type_values);
nod->virthandler->next = mod->virthandlers;
mod->virthandlers = nod->virthandler;
}
nod->next = NULL;
*odtail = nod;
odtail = &nod->next;
}
cd->cppcode = templateCode(pt, used, cd->cppcode, type_names, type_values);
cd->hdrcode = templateCode(pt, used, cd->hdrcode, type_names, type_values);
cd->convtosubcode = templateCode(pt, used, cd->convtosubcode, type_names, type_values);
cd->convtocode = templateCode(pt, used, cd->convtocode, type_names, type_values);
cd->travcode = templateCode(pt, used, cd->travcode, type_names, type_values);
cd->clearcode = templateCode(pt, used, cd->clearcode, type_names, type_values);
cd->readbufcode = templateCode(pt, used, cd->readbufcode, type_names, type_values);
cd->writebufcode = templateCode(pt, used, cd->writebufcode, type_names, type_values);
cd->segcountcode = templateCode(pt, used, cd->segcountcode, type_names, type_values);
cd->charbufcode = templateCode(pt, used, cd->charbufcode, type_names, type_values);
cd->next = pt->classes;
pt->classes = cd;
tcd->sig.nrArgs--;
freeScopedName(type_names);
freeScopedName(type_values);
}
/*
* Replace any template arguments in a signature.
*/
static void templateSignature(signatureDef *sd, int result, classTmplDef *tcd, templateDef *td, classDef *ncd)
{
int a;
if (result)
templateType(&sd->result, tcd, td, ncd);
for (a = 0; a < sd->nrArgs; ++a)
templateType(&sd->args[a], tcd, td, ncd);
}
/*
* Replace any template arguments in a type.
*/
static void templateType(argDef *ad, classTmplDef *tcd, templateDef *td, classDef *ncd)
{
int a;
char *name;
/* Ignore if it isn't an unscoped name. */
if (ad->atype != defined_type || ad->u.snd->next != NULL)
return;
name = ad->u.snd->name;
for (a = 0; a < tcd->sig.nrArgs - 1; ++a)
if (strcmp(name, scopedNameTail(tcd->sig.args[a].u.snd)) == 0)
{
ad->atype = td->types.args[a].atype;
/* We take the constrained flag from the real type. */
resetIsConstrained(ad);
if (isConstrained(&td->types.args[a]))
setIsConstrained(ad);
ad->u = td->types.args[a].u;
return;
}
/* Handle the class name itself. */
if (strcmp(name, scopedNameTail(classFQCName(tcd->cd))) == 0)
{
ad->atype = class_type;
ad->u.cd = ncd;
}
}
/*
* Replace any template arguments in a literal code block.
*/
codeBlock *templateCode(sipSpec *pt, ifaceFileList **used, codeBlock *ocb, scopedNameDef *names, scopedNameDef *values)
{
codeBlock *ncb = NULL, **tail = &ncb;
while (ocb != NULL)
{
char *at = ocb->frag;
do
{
char *first = NULL;
codeBlock *cb;
scopedNameDef *nam, *val, *nam_first, *val_first;
/*
* Go through the rest of this fragment looking for
* each of the types and the name of the class itself.
*/
nam = names;
val = values;
while (nam != NULL && val != NULL)
{
char *cp;
if ((cp = strstr(at, nam->name)) != NULL)
if (first == NULL || first > cp)
{
nam_first = nam;
val_first = val;
first = cp;
}
nam = nam->next;
val = val->next;
}
/* Create the new fragment. */
cb = sipMalloc(sizeof (codeBlock));
if (at == ocb->frag)
{
cb->filename = ocb->filename;
cb->linenr = ocb->linenr;
}
else
cb->filename = NULL;
cb->next = NULL;
*tail = cb;
tail = &cb->next;
/* See if anything was found. */
if (first == NULL)
{
/* We can just point to this. */
cb->frag = at;
/* All done with this one. */
at = NULL;
}
else
{
static char *gen_names[] = {
"sipForceConvertToTransfer_",
"sipForceConvertTo_",
"sipConvertFromTransfer_",
"sipConvertFrom_",
"sipClass_",
"sipEnum_",
"sipException_",
NULL
};
char *dp, *sp, **gn;
int genname = FALSE;
/*
* If the context in which the text is used is
* in the name of a SIP generated object then
* translate any "::" scoping to "_".
*/
for (gn = gen_names; *gn != NULL; ++gn)
if (search_back(first, at, *gn))
{
addUsedFromCode(pt, used, val_first->name);
genname = TRUE;
break;
}
/* Fragment the fragment. */
cb->frag = sipMalloc(first - at + strlen(val_first->name) + 1);
strncpy(cb->frag, at, first - at);
dp = &cb->frag[first - at];
sp = val_first->name;
if (genname)
{
char gch;
while ((gch = *sp++) != '\0')
if (gch == ':' && *sp == ':')
{
*dp++ = '_';
++sp;
}
else
*dp++ = gch;
*dp = '\0';
}
else
strcpy(dp, sp);
/* Move past the replaced text. */
at = first + strlen(nam_first->name);
}
}
while (at != NULL && *at != '\0');
ocb = ocb->next;
}
return ncb;
}
/*
* Return TRUE if the text at the end of a string matches the target string.
*/
static int search_back(const char *end, const char *start, const char *target)
{
size_t tlen = strlen(target);
if (start + tlen >= end)
return FALSE;
return (strncmp(end - tlen, target, tlen) == 0);
}
/*
* Add any needed interface files based on handwritten code.
*/
static void addUsedFromCode(sipSpec *pt, ifaceFileList **used, const char *sname)
{
ifaceFileDef *iff;
enumDef *ed;
for (iff = pt->ifacefiles; iff != NULL; iff = iff->next)
{
if (iff->type != class_iface && iff->type != exception_iface)
continue;
if (sameName(iff->fqcname, sname))
{
addToUsedList(used, iff);
return;
}
}
for (ed = pt->enums; ed != NULL; ed = ed->next)
{
if (ed->ecd == NULL)
continue;
if (sameName(ed->fqcname, sname))
{
addToUsedList(used, ed->ecd->iff);
return;
}
}
}
/*
* Compare a scoped name with its string equivalent.
*/
static int sameName(scopedNameDef *snd, const char *sname)
{
while (snd != NULL && *sname != '\0')
{
const char *sp = snd->name;
while (*sp != '\0' && *sname != ':' && *sname != '\0')
if (*sp++ != *sname++)
return FALSE;
if (*sp != '\0' || (*sname != ':' && *sname != '\0'))
return FALSE;
snd = snd->next;
if (*sname == ':')
sname += 2;
}
return (snd == NULL && *sname == '\0');
}
/*
* Create a new typedef.
*/
static void newTypedef(sipSpec *pt,moduleDef *mod,char *name,argDef *type)
{
typedefDef *td;
scopedNameDef *fqname = text2scopedName(name);
classDef *scope = currentScope();
/* See if we are instantiating a template class. */
if (type->atype == template_type)
{
classTmplDef *tcd;
templateDef *td = type->u.td;
for (tcd = pt->classtemplates; tcd != NULL; tcd = tcd->next)
if (sameScopedName(tcd->cd->iff->fqcname, td->fqname))
{
if (!sameTemplateSignature(&tcd->sig, &td->types, FALSE))
continue;
instantiateClassTemplate(pt, mod, scope, fqname, tcd, td);
/* All done. */
return;
}
}
/* Check it doesn't already exist. */
for (td = pt -> typedefs; td != NULL; td = td -> next)
if (sameScopedName(td -> fqname,fqname))
{
fatalScopedName(fqname);
fatal(" already defined\n");
}
td = sipMalloc(sizeof (typedefDef));
td -> fqname = fqname;
td -> ecd = scope;
td -> module = mod;
td -> type = *type;
td -> next = pt -> typedefs;
mod -> nrtypedefs++;
pt -> typedefs = td;
}
/*
* Return TRUE if the template signatures are the same. A deep comparison is
* used for mapped type templates where we want to recurse into any nested
* templates.
*/
int sameTemplateSignature(signatureDef *sd1, signatureDef *sd2, int deep)
{
int a;
if (sd1->nrArgs != sd2->nrArgs)
return FALSE;
for (a = 0; a < sd1->nrArgs; ++a)
{
argDef *ad1 = &sd1->args[a];
argDef *ad2 = &sd2->args[a];
/*
* If we are doing a shallow comparision (ie. for class
* templates) then a type name on the left hand side matches
* anything on the right hand side.
*/
if (ad1->atype == defined_type && !deep)
continue;
/*
* For type names only compare the references and pointers, and
* do the same for any nested templates.
*/
if (ad1->atype == defined_type && ad2->atype == defined_type)
{
if (isReference(ad1) != isReference(ad2) || ad1->nrderefs != ad2->nrderefs)
return FALSE;
}
else if (ad1->atype == template_type && ad2->atype == template_type)
{
if (!sameTemplateSignature(&ad1->u.td->types, &ad2->u.td->types, deep))
return FALSE;
}
else if (!sameBaseType(ad1, ad2))
return FALSE;
}
return TRUE;
}
/*
* Create a new variable.
*/
static void newVar(sipSpec *pt,moduleDef *mod,char *name,int isstatic,
argDef *type,optFlags *of,codeBlock *acode,codeBlock *gcode,
codeBlock *scode)
{
varDef *var;
classDef *escope = currentScope();
nameDef *nd = cacheName(pt,getPythonName(of,name));
if (inMainModule())
setIsUsedName(nd);
checkAttributes(pt,escope,nd -> text,FALSE);
var = sipMalloc(sizeof (varDef));
var -> pyname = nd;
var -> fqcname = text2scopedName(name);
var -> ecd = escope;
var -> module = mod;
var -> varflags = 0;
var -> type = *type;
var -> accessfunc = acode;
var -> getcode = gcode;
var -> setcode = scode;
var -> next = pt -> vars;
if (isstatic || (escope != NULL && escope->iff->type == namespace_iface))
setIsStaticVar(var);
pt -> vars = var;
}
/*
* Create a new ctor.
*/
static void newCtor(char *name,int sectFlags,signatureDef *args,
optFlags *optflgs,codeBlock *methodcode,
throwArgs *exceptions,signatureDef *cppsig,int explicit)
{
ctorDef *ct, **ctp;
classDef *cd = currentScope();
/* Check the name of the constructor. */
if (strcmp(classBaseName(cd),name) != 0)
yyerror("Constructor doesn't have the same name as its class");
/* Add to the list of constructors. */
ct = sipMalloc(sizeof (ctorDef));
ct -> ctorflags = sectFlags;
ct -> pysig = *args;
ct -> cppsig = (cppsig != NULL ? cppsig : &ct -> pysig);
ct -> exceptions = exceptions;
ct -> methodcode = methodcode;
ct -> next = NULL;
if (!isPrivateCtor(ct))
setCanCreate(cd);
if (isProtectedCtor(ct))
setHasShadow(cd);
if (explicit)
setIsExplicitCtor(ct);
getHooks(optflgs,&ct -> prehook,&ct -> posthook);
if (getReleaseGIL(optflgs))
setIsReleaseGILCtor(ct);
else if (getHoldGIL(optflgs))
setIsHoldGILCtor(ct);
if (findOptFlag(optflgs,"NoDerived",bool_flag) != NULL)
{
if (cppsig != NULL)
yyerror("The /NoDerived/ annotation cannot be used with a C++ signature");
if (methodcode == NULL)
yyerror("The /NoDerived/ annotation must be used with %MethodCode");
ct->cppsig = NULL;
}
if (findOptFlag(optflgs,"Default",bool_flag) != NULL)
{
if (cd -> defctor != NULL)
yyerror("A constructor with the /Default/ annotation has already been defined");
cd -> defctor = ct;
}
/* Append to the list. */
for (ctp = &cd->ctors; *ctp != NULL; ctp = &(*ctp)->next)
;
*ctp = ct;
}
/*
* Create a new function.
*/
static void newFunction(sipSpec *pt,moduleDef *mod,int sflags,int isstatic,
int isvirt,char *name,signatureDef *sig,int isconst,
int isabstract,optFlags *optflgs,codeBlock *methodcode,
codeBlock *vcode,throwArgs *exceptions,
signatureDef *cppsig)
{
classDef *cd = currentScope();
nameDef *pname;
int factory, xferback;
overDef *od, **odp, **headp;
optFlag *of;
virtHandlerDef *vhd;
/* Extra checks for a C module. */
if (pt -> genc)
{
if (cd != NULL)
yyerror("Function declaration not allowed in a struct in a C module");
if (isstatic)
yyerror("Static functions not allowed in a C module");
if (exceptions != NULL)
yyerror("Exceptions not allowed in a C module");
}
headp = (cd != NULL ? &cd -> overs : &pt -> overs);
/* See if it is a factory method. */
if (findOptFlag(optflgs,"Factory",bool_flag) != NULL)
factory = TRUE;
else
{
int a;
factory = FALSE;
/* Check /TransferThis/ wasn't specified. */
if (cd == NULL || isstatic)
for (a = 0; a < sig -> nrArgs; ++a)
if (isThisTransferred(&sig -> args[a]))
yyerror("/TransferThis/ may only be specified in constructors and class methods");
}
/* See if the result is to be returned to Python ownership. */
xferback = (findOptFlag(optflgs,"TransferBack",bool_flag) != NULL);
if (factory && xferback)
yyerror("/TransferBack/ and /Factory/ cannot both be specified");
/* Use the C++ name if a Python name wasn't given. */
pname = cacheName(pt, getPythonName(optflgs, name));
/* Create a new overload definition. */
od = sipMalloc(sizeof (overDef));
/* Set the overload flags. */
od -> overflags = sflags;
if (factory)
setIsFactory(od);
if (xferback)
setIsResultTransferredBack(od);
if (isProtected(od))
setHasShadow(cd);
if ((isSlot(od) || isSignal(od)) && !isPrivate(od))
{
if (isSignal(od))
setHasShadow(cd);
pt -> sigslots = TRUE;
}
if (isSignal(od) && (methodcode != NULL || vcode != NULL))
yyerror("Cannot provide code for signals");
if (isstatic)
{
if (isSignal(od))
yyerror("Static functions cannot be signals");
if (isvirt)
yyerror("Static functions cannot be virtual");
setIsStatic(od);
}
if (isconst)
setIsConst(od);
if (isabstract)
{
if (sflags == 0)
yyerror("Non-class function specified as abstract");
setIsAbstract(od);
}
if ((of = findOptFlag(optflgs,"AutoGen",opt_name_flag)) != NULL)
{
setIsAutoGen(od);
if (of -> fvalue.sval != NULL)
{
qualDef *qd;
if ((qd = findQualifier(of -> fvalue.sval)) == NULL || qd -> qtype != feature_qualifier)
yyerror("No such feature");
if (excludedFeature(excludedQualifiers,qd))
resetIsAutoGen(od);
}
}
if (isvirt)
{
if (isSignal(od) && !optNoEmitters(pt))
yyerror("Virtual signals aren't supported");
setIsVirtual(od);
setHasShadow(cd);
vhd = sipMalloc(sizeof (virtHandlerDef));
vhd -> virthandlernr = -1;
vhd -> vhflags = 0;
vhd -> pysig = &od -> pysig;
vhd -> cppsig = (cppsig != NULL ? cppsig : &od -> pysig);
vhd -> module = currentModule;
vhd -> virtcode = vcode;
vhd -> next = currentModule -> virthandlers;
if (factory || xferback)
setIsTransferVH(vhd);
currentModule -> virthandlers = vhd;
}
else
{
if (vcode != NULL)
yyerror("%VirtualCatcherCode provided for non-virtual function");
vhd = NULL;
}
od -> cppname = name;
od -> pysig = *sig;
od -> cppsig = (cppsig != NULL ? cppsig : &od -> pysig);
od -> exceptions = exceptions;
od -> methodcode = methodcode;
od -> virthandler = vhd;
od -> common = findFunction(pt,mod,cd,pname,(methodcode != NULL),sig -> nrArgs);
if (findOptFlag(optflgs,"Numeric",bool_flag) != NULL)
setIsNumeric(od -> common);
/* Methods that run in new threads must be virtual. */
if (findOptFlag(optflgs,"NewThread",bool_flag) != NULL)
{
argDef *res;
if (!isvirt)
yyerror("/NewThread/ may only be specified for virtual functions");
/*
* This is an arbitary limitation to make the code generator
* slightly easier - laziness on my part.
*/
res = &od -> cppsig -> result;
if (res -> atype != void_type || res -> nrderefs != 0)
yyerror("/NewThread/ may only be specified for void functions");
setIsNewThread(od);
}
getHooks(optflgs,&od -> prehook,&od -> posthook);
if (getReleaseGIL(optflgs))
setIsReleaseGIL(od);
else if (getHoldGIL(optflgs))
setIsHoldGIL(od);
od -> next = NULL;
/* Append to the list. */
for (odp = headp; *odp != NULL; odp = &(*odp)->next)
;
*odp = od;
}
/*
* Return the Python name based on the C/C++ name and any /PyName/ annotation.
*/
static char *getPythonName(optFlags *optflgs, char *cname)
{
char *pname;
optFlag *of;
if ((of = findOptFlag(optflgs, "PyName", name_flag)) != NULL)
pname = of -> fvalue.sval;
else
pname = cname;
return pname;
}
/*
* Cache a name in a module.
*/
static nameDef *cacheName(sipSpec *pt,char *name)
{
nameDef *nd;
/* See if it already exists. */
for (nd = pt -> namecache; nd != NULL; nd = nd -> next)
if (strcmp(nd -> text,name) == 0)
return nd;
/* Create a new one. */
nd = sipMalloc(sizeof (nameDef));
nd -> nameflags = 0;
nd -> module = currentSpec -> module;
nd -> text = name;
nd -> next = pt -> namecache;
pt -> namecache = nd;
return nd;
}
/*
* Find (or create) an overloaded function name.
*/
static memberDef *findFunction(sipSpec *pt,moduleDef *mod,classDef *cd,
nameDef *pname,int hwcode,int nrargs)
{
static struct slot_map {
char *name; /* The slot name. */
slotType type; /* The corresponding type. */
int needs_hwcode; /* If handwritten code is required. */
int nrargs; /* Nr. of arguments. */
} slot_table[] = {
{"__str__", str_slot, TRUE, 0},
{"__unicode__", unicode_slot, TRUE, 0},
{"__int__", int_slot, FALSE, 0},
{"__long__", long_slot, FALSE, 0},
{"__float__", float_slot, FALSE, 0},
{"__len__", len_slot, TRUE, 0},
{"__contains__", contains_slot, TRUE, 1},
{"__add__", add_slot, FALSE, 1},
{"__sub__", sub_slot, FALSE, 1},
{"__mul__", mul_slot, FALSE, 1},
{"__div__", div_slot, FALSE, 1},
{"__mod__", mod_slot, FALSE, 1},
{"__and__", and_slot, FALSE, 1},
{"__or__", or_slot, FALSE, 1},
{"__xor__", xor_slot, FALSE, 1},
{"__lshift__", lshift_slot, FALSE, 1},
{"__rshift__", rshift_slot, FALSE, 1},
{"__iadd__", iadd_slot, FALSE, 1},
{"__isub__", isub_slot, FALSE, 1},
{"__imul__", imul_slot, FALSE, 1},
{"__idiv__", idiv_slot, FALSE, 1},
{"__imod__", imod_slot, FALSE, 1},
{"__iand__", iand_slot, FALSE, 1},
{"__ior__", ior_slot, FALSE, 1},
{"__ixor__", ixor_slot, FALSE, 1},
{"__ilshift__", ilshift_slot, FALSE, 1},
{"__irshift__", irshift_slot, FALSE, 1},
{"__invert__", invert_slot, FALSE, 0},
{"__call__", call_slot, FALSE, -1},
{"__getitem__", getitem_slot, FALSE, -1},
{"__setitem__", setitem_slot, TRUE, -1},
{"__delitem__", delitem_slot, TRUE, -1},
{"__lt__", lt_slot, FALSE, 1},
{"__le__", le_slot, FALSE, 1},
{"__eq__", eq_slot, FALSE, 1},
{"__ne__", ne_slot, FALSE, 1},
{"__gt__", gt_slot, FALSE, 1},
{"__ge__", ge_slot, FALSE, 1},
{"__cmp__", cmp_slot, FALSE, 1},
{"__nonzero__", nonzero_slot, TRUE, 0},
{"__neg__", neg_slot, FALSE, 0},
{"__pos__", pos_slot, FALSE, 0},
{"__abs__", abs_slot, TRUE, 0},
{"__repr__", repr_slot, TRUE, 0},
{"__hash__", hash_slot, TRUE, 0},
{NULL}
};
memberDef *md, **flist;
struct slot_map *sm;
slotType st;
/* Get the slot type. */
st = no_slot;
for (sm = slot_table; sm -> name != NULL; ++sm)
if (strcmp(sm -> name,pname -> text) == 0)
{
if (sm -> needs_hwcode && !hwcode)
yyerror("This Python slot requires %MethodCode");
if (sm -> nrargs < 0)
{
int min_nr;
/* These require a minimum number. */
switch (sm -> type)
{
case getitem_slot:
case delitem_slot:
min_nr = 1;
break;
case setitem_slot:
min_nr = 2;
break;
default:
min_nr = 0;
}
if (nrargs < min_nr)
yyerror("Insufficient number of arguments to Python slot");
}
else if (cd == NULL)
{
/* Global operators need one extra argument. */
if (sm -> nrargs + 1 != nrargs)
yyerror("Incorrect number of arguments to global operator");
}
else if (sm -> nrargs != nrargs)
yyerror("Incorrect number of arguments to Python slot");
st = sm -> type;
break;
}
if (inMainModule())
setIsUsedName(pname);
/* Check there is no name clash. */
checkAttributes(pt,cd,pname -> text,TRUE);
/* See if it already exists. */
flist = (cd != NULL ? &cd -> members : &pt -> othfuncs);
for (md = *flist; md != NULL; md = md -> next)
if (md -> pyname == pname && md -> module == mod)
return md;
/* Create a new one. */
md = sipMalloc(sizeof (memberDef));
md -> pyname = pname;
md -> memberflags = 0;
md -> slot = st;
md -> module = mod;
md -> next = *flist;
*flist = md;
/* Global operators are a subset. */
if (cd == NULL && st != no_slot && st != neg_slot && st != pos_slot && !isNumberSlot(md) && !isRichCompareSlot(md))
yyerror("Global operators must be either numeric or comparison operators");
return md;
}
/*
* Search a set of flags for a particular one and check its type.
*/
static optFlag *findOptFlag(optFlags *flgs,char *name,flagType ft)
{
int f;
for (f = 0; f < flgs -> nrFlags; ++f)
{
optFlag *of = &flgs -> flags[f];
if (strcmp(of -> fname,name) == 0)
{
/*
* An optional name can look like a boolean or a name.
*/
if (ft == opt_name_flag)
{
if (of -> ftype == bool_flag)
{
of -> ftype = opt_name_flag;
of -> fvalue.sval = NULL;
}
else if (of -> ftype == name_flag)
of -> ftype = opt_name_flag;
}
if (ft != of -> ftype)
yyerror("Optional flag has a value of the wrong type");
return of;
}
}
return NULL;
}
/*
* A name is going to be used as a Python attribute name within a Python scope
* (ie. a Python dictionary), so check against what we already know is going in
* the same scope in case there is a clash.
*/
static void checkAttributes(sipSpec *pt,classDef *pyscope,char *attr,int isfunc)
{
enumDef *ed;
varDef *vd;
classDef *cd;
/* Check the enums. */
for (ed = pt -> enums; ed != NULL; ed = ed -> next)
{
enumMemberDef *emd;
if (ed -> ecd != pyscope || ed -> pyname == NULL)
continue;
if (strcmp(ed->pyname->text, attr) == 0)
yyerror("There is already an enum in scope with the same Python name");
for (emd = ed -> members; emd != NULL; emd = emd -> next)
if (strcmp(emd -> pyname -> text, attr) == 0)
yyerror("There is already an enum member in scope with the same Python name");
}
/* Check the variables. */
for (vd = pt -> vars; vd != NULL; vd = vd -> next)
{
if (vd -> ecd != pyscope)
continue;
if (strcmp(vd -> pyname -> text, attr) == 0)
yyerror("There is already a variable in scope with the same Python name");
}
/*
* Only check the members if this attribute isn't a member because we
* can handle members with the same name in the same scope.
*/
if (!isfunc)
{
memberDef *md, *membs;
membs = (pyscope != NULL ? pyscope -> members : pt -> othfuncs);
for (md = membs; md != NULL; md = md -> next)
{
overDef *od, *overs;
if (strcmp(md -> pyname -> text, attr) != 0)
continue;
/* Check for a conflict with all overloads. */
overs = (pyscope != NULL ? pyscope -> overs : pt -> overs);
for (od = overs; od != NULL; od = od -> next)
{
if (od -> common != md)
continue;
yyerror("There is already a function in scope with the same Python name");
}
}
}
/* Check the classes. */
for (cd = pt -> classes; cd != NULL; cd = cd -> next)
{
if (cd -> ecd != pyscope || cd -> pyname == NULL)
continue;
if (strcmp(cd->pyname, attr) == 0 && !isExternal(cd))
yyerror("There is already a class or namespace in scope with the same Python name");
}
/* Check the exceptions. */
if (pyscope == NULL)
{
exceptionDef *xd;
for (xd = pt->exceptions; xd != NULL; xd = xd->next)
if (xd->pyname != NULL && strcmp(xd->pyname, attr) == 0)
yyerror("There is already an exception with the same Python name");
}
}
/*
* Append a code block to a list of them. Append is needed to give the
* specifier easy control over the order of the documentation.
*/
static void appendCodeBlock(codeBlock **headp,codeBlock *new)
{
while (*headp != NULL)
headp = &(*headp) -> next;
*headp = new;
}
/*
* Handle the end of a fully parsed a file.
*/
static void handleEOF()
{
/*
* Check that the number of nested if's is the same as when we started
* the file.
*/
if (skipStackPtr > newContext.ifdepth)
fatal("Too many %%If statements in %s\n",previousFile);
if (skipStackPtr < newContext.ifdepth)
fatal("Too many %%End statements in %s\n",previousFile);
}
/*
* Handle the end of a fully parsed a module.
*/
static void handleEOM()
{
/* Check it has been named. */
if (currentModule -> name == NULL)
fatal("No %%Module has been specified for module defined in %s\n",previousFile);
/* The previous module is now current. */
currentModule = newContext.prevmod;
}
/*
* Find an existing qualifier.
*/
static qualDef *findQualifier(char *name)
{
moduleDef *mod;
for (mod = currentSpec -> modules; mod != NULL; mod = mod -> next)
{
qualDef *qd;
for (qd = mod -> qualifiers; qd != NULL; qd = qd -> next)
if (strcmp(qd -> name,name) == 0)
return qd;
}
return NULL;
}
/*
* Return a copy of a scoped name.
*/
scopedNameDef *copyScopedName(scopedNameDef *snd)
{
scopedNameDef *head;
head = NULL;
while (snd != NULL)
{
appendScopedName(&head,text2scopePart(snd -> name));
snd = snd -> next;
}
return head;
}
/*
* Append a name to a list of scopes.
*/
void appendScopedName(scopedNameDef **headp,scopedNameDef *newsnd)
{
while (*headp != NULL)
headp = &(*headp) -> next;
*headp = newsnd;
}
/*
* Free a scoped name - but not the text itself.
*/
void freeScopedName(scopedNameDef *snd)
{
while (snd != NULL)
{
scopedNameDef *next = snd -> next;
free(snd);
snd = next;
}
}
/*
* Convert a text string to a scope part structure.
*/
scopedNameDef *text2scopePart(char *text)
{
scopedNameDef *snd;
snd = sipMalloc(sizeof (scopedNameDef));
snd -> name = text;
snd -> next = NULL;
return snd;
}
/*
* Convert a text string to a fully scoped name.
*/
static scopedNameDef *text2scopedName(char *text)
{
return scopeScopedName(text2scopePart(text));
}
/*
* Prepend any current scope to a scoped name.
*/
static scopedNameDef *scopeScopedName(scopedNameDef *name)
{
classDef *cd = currentScope();
scopedNameDef *snd;
snd = (cd != NULL ? copyScopedName(cd->iff->fqcname) : NULL);
appendScopedName(&snd, name);
return snd;
}
/*
* Return a pointer to the tail part of a scoped name.
*/
char *scopedNameTail(scopedNameDef *snd)
{
if (snd == NULL)
return NULL;
while (snd -> next != NULL)
snd = snd -> next;
return snd -> name;
}
/*
* Push the given scope onto the scope stack.
*/
static void pushScope(classDef *scope)
{
if (currentScopeIdx >= MAX_NESTED_SCOPE)
fatal("Internal error: increase the value of MAX_NESTED_SCOPE\n");
scopeStack[currentScopeIdx] = scope;
sectFlagsStack[currentScopeIdx] = sectionFlags;
++currentScopeIdx;
}
/*
* Pop the scope stack.
*/
static void popScope(void)
{
if (currentScopeIdx > 0)
sectionFlags = sectFlagsStack[--currentScopeIdx];
}
/*
* Return non-zero if the current input should be parsed rather than be
* skipped.
*/
static int notSkipping()
{
return (skipStackPtr == 0 ? TRUE : skipStack[skipStackPtr - 1]);
}
/*
* Return the value of an expression involving a time period.
*/
static int timePeriod(char *lname,char *uname)
{
int this, line;
qualDef *qd, *lower, *upper;
moduleDef *mod;
if (lname == NULL)
lower = NULL;
else if ((lower = findQualifier(lname)) == NULL || lower -> qtype != time_qualifier)
yyerror("Lower bound is not a time version");
if (uname == NULL)
upper = NULL;
else if ((upper = findQualifier(uname)) == NULL || upper -> qtype != time_qualifier)
yyerror("Upper bound is not a time version");
/* Sanity checks on the bounds. */
if (lower == NULL && upper == NULL)
yyerror("Lower and upper bounds cannot both be omitted");
if (lower != NULL && upper != NULL)
{
if (lower -> module != upper -> module || lower -> line != upper -> line)
yyerror("Lower and upper bounds are from different timelines");
if (lower == upper)
yyerror("Lower and upper bounds must be different");
if (lower -> order > upper -> order)
yyerror("Later version specified as lower bound");
}
/* Go through each slot in the relevant timeline. */
if (lower != NULL)
{
mod = lower -> module;
line = lower -> line;
}
else
{
mod = upper -> module;
line = upper -> line;
}
this = FALSE;
for (qd = mod -> qualifiers; qd != NULL; qd = qd -> next)
{
if (qd -> qtype != time_qualifier || qd -> line != line)
continue;
if (lower != NULL && qd -> order < lower -> order)
continue;
if (upper != NULL && qd -> order >= upper -> order)
continue;
/*
* This is within the required range so if it is also needed
* then the expression is true.
*/
if (isNeeded(qd))
{
this = TRUE;
break;
}
}
return this;
}
/*
* Return the value of an expression involving a single platform or feature.
*/
static int platOrFeature(char *name,int optnot)
{
int this;
qualDef *qd;
if ((qd = findQualifier(name)) == NULL || qd -> qtype == time_qualifier)
yyerror("No such platform or feature");
/* Assume this sub-expression is false. */
this = FALSE;
if (qd -> qtype == feature_qualifier)
{
if (!excludedFeature(excludedQualifiers,qd))
this = TRUE;
}
else if (isNeeded(qd))
this = TRUE;
if (optnot)
this = !this;
return this;
}
/*
* Return TRUE if the given qualifier is excluded.
*/
int excludedFeature(stringList *xsl,qualDef *qd)
{
while (xsl != NULL)
{
if (strcmp(qd -> name,xsl -> s) == 0)
return TRUE;
xsl = xsl -> next;
}
return FALSE;
}
/*
* Return TRUE if the given qualifier is needed.
*/
static int isNeeded(qualDef *qd)
{
stringList *sl;
for (sl = neededQualifiers; sl != NULL; sl = sl -> next)
if (strcmp(qd -> name,sl -> s) == 0)
return TRUE;
return FALSE;
}
/*
* Return the current scope. currentScope() is only valid if notSkipping()
* returns non-zero.
*/
static classDef *currentScope(void)
{
return (currentScopeIdx > 0 ? scopeStack[currentScopeIdx - 1] : NULL);
}
/*
* Create a new qualifier.
*/
static void newQualifier(moduleDef *mod,int line,int order,char *name,qualType qt)
{
qualDef *qd;
/* Check it doesn't already exist. */
if (findQualifier(name) != NULL)
yyerror("Version is already defined");
qd = sipMalloc(sizeof (qualDef));
qd -> name = name;
qd -> qtype = qt;
qd -> module = mod;
qd -> line = line;
qd -> order = order;
qd -> next = mod -> qualifiers;
mod -> qualifiers = qd;
}
/*
* Create a new imported module.
*/
static void newImport(char *name)
{
moduleDef *from, *mod;
moduleListDef *mld;
/* Create a new module if it has already been imported. */
for (mod = currentSpec -> modules; mod != NULL; mod = mod -> next)
if (strcmp(mod -> file,name) == 0)
break;
from = currentModule;
if (mod == NULL)
{
newModule(NULL,name);
mod = currentModule;
}
/* Add the new import unless it has already been imported. */
for (mld = from->imports; mld != NULL; mld = mld->next)
if (mld->module == mod)
return;
mld = sipMalloc(sizeof (moduleListDef));
mld -> module = mod;
mld -> next = from->imports;
from->imports = mld;
}
/*
* Set up pointers to hook names.
*/
static void getHooks(optFlags *optflgs,char **pre,char **post)
{
optFlag *of;
if ((of = findOptFlag(optflgs,"PreHook",name_flag)) != NULL)
*pre = of -> fvalue.sval;
else
*pre = NULL;
if ((of = findOptFlag(optflgs,"PostHook",name_flag)) != NULL)
*post = of -> fvalue.sval;
else
*post = NULL;
}
/*
* Get the /ReleaseGIL/ option flag.
*/
static int getReleaseGIL(optFlags *optflgs)
{
return (findOptFlag(optflgs, "ReleaseGIL", bool_flag) != NULL);
}
/*
* Get the /HoldGIL/ option flag.
*/
static int getHoldGIL(optFlags *optflgs)
{
return (findOptFlag(optflgs, "HoldGIL", bool_flag) != NULL);
}
/*
* Return TRUE if the QtNoEmitters option was specified.
*/
int optNoEmitters(sipSpec *pt)
{
return optFind(pt, "QtNoEmitters");
}
/*
* Return TRUE if the QtRegisterTypes option was specified.
*/
int optRegisterTypes(sipSpec *pt)
{
return optFind(pt, "QtRegisterTypes");
}
/*
* Return TRUE if the Qt4Q_OBJECT option was specified.
*/
int optQ_OBJECT4(sipSpec *pt)
{
return optFind(pt, "Qt4Q_OBJECT");
}
/*
* Return TRUE if a particular option was specified with %SIPOptions.
*/
static int optFind(sipSpec *pt, const char *opt)
{
stringList *sl;
for (sl = pt->options; sl != NULL; sl = sl->next)
if (strcmp(sl->s, opt) == 0)
return TRUE;
return FALSE;
}