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.
6386 lines
167 KiB
6386 lines
167 KiB
/*
|
|
* The SIP parser.
|
|
*
|
|
* Copyright (c) 2010 Riverbank Computing Limited <info@riverbankcomputing.com>
|
|
*
|
|
* This file is part of SIP.
|
|
*
|
|
* This copy of SIP is licensed for use under the terms of the SIP License
|
|
* Agreement. See the file LICENSE for more details.
|
|
*
|
|
* This copy of SIP may also used under the terms of the GNU General Public
|
|
* License v2 or v3 as published by the Free Software Foundation which can be
|
|
* found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
|
|
*
|
|
* SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
*/
|
|
|
|
%{
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#include "sip.h"
|
|
|
|
|
|
#define MAX_NESTED_IF 10
|
|
#define MAX_NESTED_SCOPE 10
|
|
|
|
#define inMainModule() (currentSpec->module == currentModule || currentModule->container != NULL)
|
|
|
|
|
|
static sipSpec *currentSpec; /* The current spec being parsed. */
|
|
static stringList *neededQualifiers; /* The list of required qualifiers. */
|
|
static stringList *excludedQualifiers; /* The list of excluded qualifiers. */
|
|
static moduleDef *currentModule; /* The current module being parsed. */
|
|
static mappedTypeDef *currentMappedType; /* The current mapped type. */
|
|
static enumDef *currentEnum; /* The current enum being parsed. */
|
|
static int sectionFlags; /* The current section flags. */
|
|
static int currentOverIsVirt; /* Set if the overload is virtual. */
|
|
static int currentCtorIsExplicit; /* Set if the ctor is explicit. */
|
|
static int currentIsStatic; /* Set if the current is static. */
|
|
static int currentIsSignal; /* Set if the current is Q_SIGNAL. */
|
|
static int currentIsSlot; /* Set if the current is Q_SLOT. */
|
|
static int currentIsTemplate; /* Set if the current is a template. */
|
|
static char *previousFile; /* The file just parsed. */
|
|
static parserContext currentContext; /* The current context. */
|
|
static int skipStackPtr; /* The skip stack pointer. */
|
|
static int skipStack[MAX_NESTED_IF]; /* Stack of skip flags. */
|
|
static classDef *scopeStack[MAX_NESTED_SCOPE]; /* The scope stack. */
|
|
static int sectFlagsStack[MAX_NESTED_SCOPE]; /* The section flags stack. */
|
|
static int currentScopeIdx; /* The scope stack index. */
|
|
static int currentTimelineOrder; /* The current timeline order. */
|
|
static classList *currentSupers; /* The current super-class list. */
|
|
static int defaultKwdArgs; /* Support keyword arguments by default. */
|
|
static int makeProtPublic; /* Treat protected items as public. */
|
|
|
|
|
|
static const char *getPythonName(optFlags *optflgs, const char *cname);
|
|
static classDef *findClass(sipSpec *pt, ifaceFileType iftype,
|
|
apiVersionRangeDef *api_range, scopedNameDef *fqname);
|
|
static classDef *findClassWithInterface(sipSpec *pt, ifaceFileDef *iff);
|
|
static classDef *newClass(sipSpec *pt, ifaceFileType iftype,
|
|
apiVersionRangeDef *api_range, scopedNameDef *snd);
|
|
static void finishClass(sipSpec *, moduleDef *, classDef *, optFlags *);
|
|
static exceptionDef *findException(sipSpec *pt, scopedNameDef *fqname, int new);
|
|
static mappedTypeDef *newMappedType(sipSpec *,argDef *, optFlags *);
|
|
static enumDef *newEnum(sipSpec *pt, moduleDef *mod, mappedTypeDef *mt_scope,
|
|
char *name, optFlags *of, int flags);
|
|
static void instantiateClassTemplate(sipSpec *pt, moduleDef *mod, classDef *scope, scopedNameDef *fqname, classTmplDef *tcd, templateDef *td);
|
|
static void newTypedef(sipSpec *, moduleDef *, char *, argDef *, optFlags *);
|
|
static void newVar(sipSpec *, moduleDef *, char *, int, argDef *, optFlags *,
|
|
codeBlock *, codeBlock *, codeBlock *);
|
|
static void newCtor(char *, int, signatureDef *, optFlags *, codeBlock *,
|
|
throwArgs *, signatureDef *, int, codeBlock *);
|
|
static void newFunction(sipSpec *, moduleDef *, classDef *, mappedTypeDef *,
|
|
int, int, int, int, int, char *, signatureDef *, int, int, optFlags *,
|
|
codeBlock *, codeBlock *, throwArgs *, signatureDef *, codeBlock *);
|
|
static optFlag *findOptFlag(optFlags *,char *,flagType);
|
|
static memberDef *findFunction(sipSpec *, moduleDef *, classDef *,
|
|
mappedTypeDef *, const char *, int, int, int);
|
|
static void checkAttributes(sipSpec *, moduleDef *, classDef *,
|
|
mappedTypeDef *, const char *, int);
|
|
static void newModule(FILE *fp, char *filename);
|
|
static moduleDef *allocModule();
|
|
static void parseFile(FILE *fp, char *name, moduleDef *prevmod, int optional);
|
|
static void handleEOF(void);
|
|
static void handleEOM(void);
|
|
static qualDef *findQualifier(const char *name);
|
|
static scopedNameDef *text2scopedName(ifaceFileDef *scope, char *text);
|
|
static scopedNameDef *scopeScopedName(ifaceFileDef *scope,
|
|
scopedNameDef *name);
|
|
static void pushScope(classDef *);
|
|
static void popScope(void);
|
|
static classDef *currentScope(void);
|
|
static void newQualifier(moduleDef *,int,int,char *,qualType);
|
|
static void newImport(char *filename);
|
|
static int timePeriod(char *,char *);
|
|
static int platOrFeature(char *,int);
|
|
static int isNeeded(qualDef *);
|
|
static int notSkipping(void);
|
|
static void getHooks(optFlags *,char **,char **);
|
|
static int getTransfer(optFlags *optflgs);
|
|
static int getReleaseGIL(optFlags *optflgs);
|
|
static int getHoldGIL(optFlags *optflgs);
|
|
static int getDeprecated(optFlags *optflgs);
|
|
static int getAllowNone(optFlags *optflgs);
|
|
static const char *getDocType(optFlags *optflgs);
|
|
static const char *getDocValue(optFlags *optflgs);
|
|
static void templateSignature(signatureDef *sd, int result, classTmplDef *tcd, templateDef *td, classDef *ncd);
|
|
static void templateType(argDef *ad, classTmplDef *tcd, templateDef *td, classDef *ncd);
|
|
static int search_back(const char *end, const char *start, const char *target);
|
|
static char *type2string(argDef *ad);
|
|
static char *scopedNameToString(scopedNameDef *name);
|
|
static void addUsedFromCode(sipSpec *pt, ifaceFileList **used, const char *sname);
|
|
static int sameName(scopedNameDef *snd, const char *sname);
|
|
static int stringFind(stringList *sl, const char *s);
|
|
static void setModuleName(sipSpec *pt, moduleDef *mod, const char *fullname);
|
|
static int foundInScope(scopedNameDef *fq_name, scopedNameDef *rel_name);
|
|
static void defineClass(scopedNameDef *snd, classList *supers, optFlags *of);
|
|
static classDef *completeClass(scopedNameDef *snd, optFlags *of, int has_def);
|
|
static memberDef *instantiateTemplateMethods(memberDef *tmd, moduleDef *mod);
|
|
static void instantiateTemplateEnums(sipSpec *pt, classTmplDef *tcd,
|
|
templateDef *td, classDef *cd, ifaceFileList **used,
|
|
scopedNameDef *type_names, scopedNameDef *type_values);
|
|
static void instantiateTemplateVars(sipSpec *pt, classTmplDef *tcd,
|
|
templateDef *td, classDef *cd, ifaceFileList **used,
|
|
scopedNameDef *type_names, scopedNameDef *type_values);
|
|
static overDef *instantiateTemplateOverloads(sipSpec *pt, overDef *tod,
|
|
memberDef *tmethods, memberDef *methods, classTmplDef *tcd,
|
|
templateDef *td, classDef *cd, ifaceFileList **used,
|
|
scopedNameDef *type_names, scopedNameDef *type_values);
|
|
static void resolveAnyTypedef(sipSpec *pt, argDef *ad);
|
|
static void addVariable(sipSpec *pt, varDef *vd);
|
|
static void applyTypeFlags(moduleDef *mod, argDef *ad, optFlags *flags);
|
|
static argType convertEncoding(const char *encoding);
|
|
static apiVersionRangeDef *getAPIRange(optFlags *optflgs);
|
|
static apiVersionRangeDef *convertAPIRange(moduleDef *mod, nameDef *name,
|
|
int from, int to);
|
|
static char *convertFeaturedString(char *fs);
|
|
static scopedNameDef *text2scopePart(char *text);
|
|
static int usesKeywordArgs(optFlags *optflgs, signatureDef *sd);
|
|
static char *strip(char *s);
|
|
static int isEnabledFeature(const char *name);
|
|
%}
|
|
|
|
%union {
|
|
char qchar;
|
|
char *text;
|
|
long number;
|
|
double real;
|
|
argDef memArg;
|
|
signatureDef signature;
|
|
signatureDef *optsignature;
|
|
throwArgs *throwlist;
|
|
codeBlock *codeb;
|
|
valueDef value;
|
|
valueDef *valp;
|
|
optFlags optflags;
|
|
optFlag flag;
|
|
scopedNameDef *scpvalp;
|
|
fcallDef fcall;
|
|
int boolean;
|
|
exceptionDef exceptionbase;
|
|
classDef *klass;
|
|
}
|
|
|
|
%token TK_API
|
|
%token TK_DEFENCODING
|
|
%token TK_PLUGIN
|
|
%token TK_DOCSTRING
|
|
%token TK_DOC
|
|
%token TK_EXPORTEDDOC
|
|
%token TK_MAKEFILE
|
|
%token TK_ACCESSCODE
|
|
%token TK_GETCODE
|
|
%token TK_SETCODE
|
|
%token TK_PREINITCODE
|
|
%token TK_INITCODE
|
|
%token TK_POSTINITCODE
|
|
%token TK_UNITCODE
|
|
%token TK_MODCODE
|
|
%token TK_TYPECODE
|
|
%token TK_PREPYCODE
|
|
%token TK_COPYING
|
|
%token TK_MAPPEDTYPE
|
|
%token <codeb> TK_CODELINE
|
|
%token TK_IF
|
|
%token TK_END
|
|
%token <text> TK_NAME
|
|
%token <text> TK_PATHNAME
|
|
%token <text> TK_STRING
|
|
%token TK_VIRTUALCATCHERCODE
|
|
%token TK_TRAVERSECODE
|
|
%token TK_CLEARCODE
|
|
%token TK_GETBUFFERCODE
|
|
%token TK_RELEASEBUFFERCODE
|
|
%token TK_READBUFFERCODE
|
|
%token TK_WRITEBUFFERCODE
|
|
%token TK_SEGCOUNTCODE
|
|
%token TK_CHARBUFFERCODE
|
|
%token TK_PICKLECODE
|
|
%token TK_METHODCODE
|
|
%token TK_FROMTYPE
|
|
%token TK_TOTYPE
|
|
%token TK_TOSUBCLASS
|
|
%token TK_INCLUDE
|
|
%token TK_OPTINCLUDE
|
|
%token TK_IMPORT
|
|
%token TK_EXPHEADERCODE
|
|
%token TK_MODHEADERCODE
|
|
%token TK_TYPEHEADERCODE
|
|
%token TK_MODULE
|
|
%token TK_CMODULE
|
|
%token TK_CONSMODULE
|
|
%token TK_COMPOMODULE
|
|
%token TK_CLASS
|
|
%token TK_STRUCT
|
|
%token TK_PUBLIC
|
|
%token TK_PROTECTED
|
|
%token TK_PRIVATE
|
|
%token TK_SIGNALS
|
|
%token TK_SIGNAL_METHOD
|
|
%token TK_SLOTS
|
|
%token TK_SLOT_METHOD
|
|
%token TK_BOOL
|
|
%token TK_SHORT
|
|
%token TK_INT
|
|
%token TK_LONG
|
|
%token TK_FLOAT
|
|
%token TK_DOUBLE
|
|
%token TK_CHAR
|
|
%token TK_WCHAR_T
|
|
%token TK_VOID
|
|
%token TK_PYOBJECT
|
|
%token TK_PYTUPLE
|
|
%token TK_PYLIST
|
|
%token TK_PYDICT
|
|
%token TK_PYCALLABLE
|
|
%token TK_PYSLICE
|
|
%token TK_PYTYPE
|
|
%token TK_VIRTUAL
|
|
%token TK_ENUM
|
|
%token TK_SIGNED
|
|
%token TK_UNSIGNED
|
|
%token TK_SCOPE
|
|
%token TK_LOGICAL_OR
|
|
%token TK_CONST
|
|
%token TK_STATIC
|
|
%token TK_SIPSIGNAL
|
|
%token TK_SIPSLOT
|
|
%token TK_SIPANYSLOT
|
|
%token TK_SIPRXCON
|
|
%token TK_SIPRXDIS
|
|
%token TK_SIPSLOTCON
|
|
%token TK_SIPSLOTDIS
|
|
%token <number> TK_NUMBER
|
|
%token <real> TK_REAL
|
|
%token TK_TYPEDEF
|
|
%token TK_NAMESPACE
|
|
%token TK_TIMELINE
|
|
%token TK_PLATFORMS
|
|
%token TK_FEATURE
|
|
%token TK_LICENSE
|
|
%token <qchar> TK_TQCHAR
|
|
%token TK_TRUE
|
|
%token TK_FALSE
|
|
%token TK_NULL
|
|
%token TK_OPERATOR
|
|
%token TK_THROW
|
|
%token TK_TQOBJECT
|
|
%token TK_EXCEPTION
|
|
%token TK_RAISECODE
|
|
%token TK_EXPLICIT
|
|
%token TK_TEMPLATE
|
|
%token TK_ELLIPSIS
|
|
%token TK_DEFMETATYPE
|
|
%token TK_DEFSUPERTYPE
|
|
|
|
%type <memArg> argvalue
|
|
%type <memArg> argtype
|
|
%type <memArg> cpptype
|
|
%type <memArg> basetype
|
|
%type <signature> template
|
|
%type <signature> arglist
|
|
%type <signature> rawarglist
|
|
%type <signature> cpptypelist
|
|
%type <optsignature> optsig
|
|
%type <optsignature> optctorsig
|
|
%type <throwlist> optexceptions
|
|
%type <throwlist> exceptionlist
|
|
%type <number> optslot
|
|
%type <number> optref
|
|
%type <number> optconst
|
|
%type <number> optvirtual
|
|
%type <number> optabstract
|
|
%type <number> deref
|
|
%type <number> optnumber
|
|
%type <value> simplevalue
|
|
%type <valp> value
|
|
%type <valp> expr
|
|
%type <valp> optassign
|
|
%type <codeb> optaccesscode
|
|
%type <codeb> optgetcode
|
|
%type <codeb> optsetcode
|
|
%type <codeb> exphdrcode
|
|
%type <codeb> modhdrcode
|
|
%type <codeb> typehdrcode
|
|
%type <codeb> opttypehdrcode
|
|
%type <codeb> travcode
|
|
%type <codeb> clearcode
|
|
%type <codeb> getbufcode
|
|
%type <codeb> releasebufcode
|
|
%type <codeb> readbufcode
|
|
%type <codeb> writebufcode
|
|
%type <codeb> segcountcode
|
|
%type <codeb> charbufcode
|
|
%type <codeb> picklecode
|
|
%type <codeb> modcode
|
|
%type <codeb> typecode
|
|
%type <codeb> codeblock
|
|
%type <codeb> codelines
|
|
%type <codeb> virtualcatchercode
|
|
%type <codeb> methodcode
|
|
%type <codeb> raisecode
|
|
%type <codeb> docstring
|
|
%type <codeb> optdocstring
|
|
%type <text> operatorname
|
|
%type <text> optfilename
|
|
%type <text> optname
|
|
%type <text> dottedname
|
|
%type <optflags> optflags
|
|
%type <optflags> flaglist
|
|
%type <flag> flag
|
|
%type <flag> flagvalue
|
|
%type <qchar> optunop
|
|
%type <qchar> binop
|
|
%type <scpvalp> scopepart
|
|
%type <scpvalp> scopedname
|
|
%type <fcall> exprlist
|
|
%type <boolean> qualifiers
|
|
%type <boolean> oredqualifiers
|
|
%type <boolean> modlang
|
|
%type <boolean> optclassbody
|
|
%type <exceptionbase> baseexception
|
|
%type <klass> class
|
|
|
|
%%
|
|
|
|
specification: statement
|
|
| specification statement
|
|
;
|
|
|
|
statement: {
|
|
/*
|
|
* We don't do these in parserEOF() because the parser is reading
|
|
* ahead and that would be too early.
|
|
*/
|
|
|
|
if (previousFile != NULL)
|
|
{
|
|
handleEOF();
|
|
|
|
if (currentContext.prevmod != NULL)
|
|
handleEOM();
|
|
|
|
free(previousFile);
|
|
previousFile = NULL;
|
|
}
|
|
} modstatement
|
|
;
|
|
|
|
modstatement: module
|
|
| consmodule
|
|
| compmodule
|
|
| plugin
|
|
| copying
|
|
| include
|
|
| optinclude
|
|
| import
|
|
| api
|
|
| timeline
|
|
| platforms
|
|
| feature
|
|
| license
|
|
| defencoding
|
|
| defmetatype
|
|
| defsupertype
|
|
| exphdrcode {
|
|
if (notSkipping())
|
|
appendCodeBlock(¤tSpec->exphdrcode, $1);
|
|
}
|
|
| modhdrcode {
|
|
if (notSkipping())
|
|
appendCodeBlock(¤tModule->hdrcode, $1);
|
|
}
|
|
| modcode {
|
|
if (notSkipping())
|
|
appendCodeBlock(¤tModule->cppcode, $1);
|
|
}
|
|
| preinitcode
|
|
| initcode
|
|
| postinitcode
|
|
| unitcode
|
|
| prepycode
|
|
| doc
|
|
| exporteddoc
|
|
| makefile
|
|
| mappedtype
|
|
| mappedtypetmpl
|
|
| nsstatement
|
|
;
|
|
|
|
nsstatement: ifstart
|
|
| ifend
|
|
| namespace
|
|
| struct
|
|
| class
|
|
| classtmpl
|
|
| exception
|
|
| typedef
|
|
| enum
|
|
| function
|
|
| variable
|
|
| typehdrcode {
|
|
if (notSkipping())
|
|
{
|
|
classDef *scope = currentScope();
|
|
|
|
if (scope == NULL)
|
|
yyerror("%TypeHeaderCode can only be used in a namespace, class or mapped type");
|
|
|
|
appendCodeBlock(&scope->iff->hdrcode, $1);
|
|
}
|
|
}
|
|
;
|
|
|
|
defencoding: TK_DEFENCODING TK_STRING {
|
|
if (notSkipping())
|
|
{
|
|
if ((currentModule->encoding = convertEncoding($2)) == no_type)
|
|
yyerror("The value of %DefaultEncoding must be one of \"ASCII\", \"Latin-1\", \"UTF-8\" or \"None\"");
|
|
}
|
|
}
|
|
;
|
|
|
|
plugin: TK_PLUGIN TK_NAME {
|
|
appendString(¤tSpec->plugins, $2);
|
|
}
|
|
;
|
|
|
|
api: TK_API TK_NAME TK_NUMBER {
|
|
if (notSkipping())
|
|
{
|
|
apiVersionRangeDef *avd;
|
|
|
|
if (findAPI(currentSpec, $2) != NULL)
|
|
yyerror("The API name in the %API directive has already been defined");
|
|
|
|
if ($3 < 1)
|
|
yyerror("The version number in the %API directive must be greater than or equal to 1");
|
|
|
|
avd = sipMalloc(sizeof (apiVersionRangeDef));
|
|
|
|
avd->api_name = cacheName(currentSpec, $2);
|
|
avd->from = $3;
|
|
avd->to = -1;
|
|
|
|
avd->next = currentModule->api_versions;
|
|
currentModule->api_versions = avd;
|
|
|
|
if (inMainModule())
|
|
setIsUsedName(avd->api_name);
|
|
}
|
|
}
|
|
;
|
|
|
|
exception: TK_EXCEPTION scopedname baseexception optflags '{' opttypehdrcode raisecode '}' ';' {
|
|
if (notSkipping())
|
|
{
|
|
exceptionDef *xd;
|
|
const char *pyname;
|
|
|
|
if (currentSpec->genc)
|
|
yyerror("%Exception not allowed in a C module");
|
|
|
|
pyname = getPythonName(&$4, scopedNameTail($2));
|
|
|
|
checkAttributes(currentSpec, currentModule, NULL, NULL,
|
|
pyname, FALSE);
|
|
|
|
xd = findException(currentSpec, $2, TRUE);
|
|
|
|
if (xd->cd != NULL)
|
|
yyerror("%Exception name has already been seen as a class name - it must be defined before being used");
|
|
|
|
if (xd->iff->module != NULL)
|
|
yyerror("The %Exception has already been defined");
|
|
|
|
/* Complete the definition. */
|
|
xd->iff->module = currentModule;
|
|
xd->iff->hdrcode = $6;
|
|
xd->pyname = pyname;
|
|
xd->bibase = $3.bibase;
|
|
xd->base = $3.base;
|
|
xd->raisecode = $7;
|
|
|
|
if (findOptFlag(&$4, "Default", bool_flag) != NULL)
|
|
currentModule->defexception = xd;
|
|
|
|
if (xd->bibase != NULL || xd->base != NULL)
|
|
xd->exceptionnr = currentModule->nrexceptions++;
|
|
}
|
|
}
|
|
;
|
|
|
|
baseexception: {
|
|
$$.bibase = NULL;
|
|
$$.base = NULL;
|
|
}
|
|
| '(' scopedname ')' {
|
|
exceptionDef *xd;
|
|
|
|
$$.bibase = NULL;
|
|
$$.base = NULL;
|
|
|
|
/* See if it is a defined exception. */
|
|
for (xd = currentSpec->exceptions; xd != NULL; xd = xd->next)
|
|
if (compareScopedNames(xd->iff->fqcname, $2) == 0)
|
|
{
|
|
$$.base = xd;
|
|
break;
|
|
}
|
|
|
|
if (xd == NULL && $2->next == NULL && strncmp($2->name, "SIP_", 4) == 0)
|
|
{
|
|
/* See if it is a builtin exception. */
|
|
|
|
static char *builtins[] = {
|
|
"Exception",
|
|
"StopIteration",
|
|
"StandardError",
|
|
"ArithmeticError",
|
|
"LookupError",
|
|
"AssertionError",
|
|
"AttributeError",
|
|
"EOFError",
|
|
"FloatingPointError",
|
|
"EnvironmentError",
|
|
"IOError",
|
|
"OSError",
|
|
"ImportError",
|
|
"IndexError",
|
|
"KeyError",
|
|
"KeyboardInterrupt",
|
|
"MemoryError",
|
|
"NameError",
|
|
"OverflowError",
|
|
"RuntimeError",
|
|
"NotImplementedError",
|
|
"SyntaxError",
|
|
"IndentationError",
|
|
"TabError",
|
|
"ReferenceError",
|
|
"SystemError",
|
|
"SystemExit",
|
|
"TypeError",
|
|
"UnboundLocalError",
|
|
"UnicodeError",
|
|
"UnicodeEncodeError",
|
|
"UnicodeDecodeError",
|
|
"UnicodeTranslateError",
|
|
"ValueError",
|
|
"ZeroDivisionError",
|
|
"WindowsError",
|
|
"VMSError",
|
|
NULL
|
|
};
|
|
|
|
char **cp;
|
|
|
|
for (cp = builtins; *cp != NULL; ++cp)
|
|
if (strcmp($2->name + 4, *cp) == 0)
|
|
{
|
|
$$.bibase = *cp;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($$.bibase == NULL && $$.base == NULL)
|
|
yyerror("Unknown exception base type");
|
|
}
|
|
;
|
|
|
|
raisecode: TK_RAISECODE codeblock {
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
mappedtype: TK_MAPPEDTYPE basetype optflags {
|
|
if (notSkipping())
|
|
currentMappedType = newMappedType(currentSpec, &$2, &$3);
|
|
} mtdefinition
|
|
;
|
|
|
|
mappedtypetmpl: template TK_MAPPEDTYPE basetype optflags {
|
|
int a;
|
|
|
|
if (currentSpec->genc)
|
|
yyerror("%MappedType templates not allowed in a C module");
|
|
|
|
/* Check the template arguments are basic types or simple names. */
|
|
for (a = 0; a < $1.nrArgs; ++a)
|
|
{
|
|
argDef *ad = &$1.args[a];
|
|
|
|
if (ad->atype == defined_type && ad->u.snd->next != NULL)
|
|
yyerror("%MappedType template arguments must be simple names");
|
|
}
|
|
|
|
if ($3.atype != template_type)
|
|
yyerror("%MappedType template must map a template type");
|
|
|
|
if (notSkipping())
|
|
{
|
|
mappedTypeTmplDef *mtt;
|
|
ifaceFileDef *iff;
|
|
|
|
/* Check a template hasn't already been provided. */
|
|
for (mtt = currentSpec->mappedtypetemplates; mtt != NULL; mtt = mtt->next)
|
|
if (compareScopedNames(mtt->mt->type.u.td->fqname, $3.u.td->fqname) == 0 && sameTemplateSignature(&mtt->mt->type.u.td->types, &$3.u.td->types, TRUE))
|
|
yyerror("%MappedType template for this type has already been defined");
|
|
|
|
$3.nrderefs = 0;
|
|
$3.argflags = 0;
|
|
|
|
mtt = sipMalloc(sizeof (mappedTypeTmplDef));
|
|
|
|
mtt->sig = $1;
|
|
mtt->mt = allocMappedType(currentSpec, &$3);
|
|
mtt->mt->doctype = getDocType(&$4);
|
|
mtt->next = currentSpec->mappedtypetemplates;
|
|
|
|
currentSpec->mappedtypetemplates = mtt;
|
|
|
|
currentMappedType = mtt->mt;
|
|
|
|
/* Create a dummy interface file. */
|
|
iff = sipMalloc(sizeof (ifaceFileDef));
|
|
iff->hdrcode = NULL;
|
|
mtt->mt->iff = iff;
|
|
}
|
|
} mtdefinition
|
|
;
|
|
|
|
mtdefinition: '{' mtbody '}' ';' {
|
|
if (notSkipping())
|
|
{
|
|
if (currentMappedType->convfromcode == NULL)
|
|
yyerror("%MappedType must have a %ConvertFromTypeCode directive");
|
|
|
|
if (currentMappedType->convtocode == NULL)
|
|
yyerror("%MappedType must have a %ConvertToTypeCode directive");
|
|
|
|
currentMappedType = NULL;
|
|
}
|
|
}
|
|
;
|
|
|
|
mtbody: mtline
|
|
| mtbody mtline
|
|
;
|
|
|
|
mtline: typehdrcode {
|
|
if (notSkipping())
|
|
appendCodeBlock(¤tMappedType->iff->hdrcode, $1);
|
|
}
|
|
| TK_FROMTYPE codeblock {
|
|
if (notSkipping())
|
|
{
|
|
if (currentMappedType -> convfromcode != NULL)
|
|
yyerror("%MappedType has more than one %ConvertFromTypeCode directive");
|
|
|
|
currentMappedType -> convfromcode = $2;
|
|
}
|
|
}
|
|
| TK_TOTYPE codeblock {
|
|
if (notSkipping())
|
|
{
|
|
if (currentMappedType -> convtocode != NULL)
|
|
yyerror("%MappedType has more than one %ConvertToTypeCode directive");
|
|
|
|
currentMappedType -> convtocode = $2;
|
|
}
|
|
}
|
|
| enum
|
|
| mtfunction
|
|
;
|
|
|
|
mtfunction: TK_STATIC cpptype TK_NAME '(' arglist ')' optconst optexceptions optflags optsig ';' optdocstring methodcode {
|
|
if (notSkipping())
|
|
{
|
|
applyTypeFlags(currentModule, &$2, &$9);
|
|
|
|
$5.result = $2;
|
|
|
|
newFunction(currentSpec, currentModule, NULL,
|
|
currentMappedType, 0, TRUE, FALSE, FALSE, FALSE, $3,
|
|
&$5, $7, FALSE, &$9, $13, NULL, $8, $10, $12);
|
|
}
|
|
}
|
|
;
|
|
|
|
namespace: TK_NAMESPACE TK_NAME {
|
|
if (currentSpec -> genc)
|
|
yyerror("namespace definition not allowed in a C module");
|
|
|
|
if (notSkipping())
|
|
{
|
|
classDef *ns, *c_scope;
|
|
ifaceFileDef *scope;
|
|
|
|
if ((c_scope = currentScope()) != NULL)
|
|
scope = c_scope->iff;
|
|
else
|
|
scope = NULL;
|
|
|
|
ns = newClass(currentSpec, namespace_iface, NULL,
|
|
text2scopedName(scope, $2));
|
|
|
|
pushScope(ns);
|
|
|
|
sectionFlags = 0;
|
|
}
|
|
} '{' nsbody '}' ';' {
|
|
if (notSkipping())
|
|
{
|
|
if (inMainModule())
|
|
{
|
|
classDef *ns = currentScope();
|
|
|
|
setIsUsedName(ns->iff->name);
|
|
setIsUsedName(ns->pyname);
|
|
}
|
|
|
|
popScope();
|
|
}
|
|
}
|
|
;
|
|
|
|
nsbody: nsstatement
|
|
| nsbody nsstatement
|
|
;
|
|
|
|
platforms: TK_PLATFORMS {
|
|
qualDef *qd;
|
|
|
|
for (qd = currentModule -> qualifiers; qd != NULL; qd = qd -> next)
|
|
if (qd -> qtype == platform_qualifier)
|
|
yyerror("%Platforms has already been defined for this module");
|
|
}
|
|
'{' platformlist '}' {
|
|
qualDef *qd;
|
|
int nrneeded;
|
|
|
|
/*
|
|
* Check that exactly one platform in the set was
|
|
* requested.
|
|
*/
|
|
|
|
nrneeded = 0;
|
|
|
|
for (qd = currentModule -> qualifiers; qd != NULL; qd = qd -> next)
|
|
if (qd -> qtype == platform_qualifier && isNeeded(qd))
|
|
++nrneeded;
|
|
|
|
if (nrneeded > 1)
|
|
yyerror("No more than one of these %Platforms must be specified with the -t flag");
|
|
}
|
|
;
|
|
|
|
platformlist: platform
|
|
| platformlist platform
|
|
;
|
|
|
|
platform: TK_NAME {
|
|
newQualifier(currentModule,-1,-1,$1,platform_qualifier);
|
|
}
|
|
;
|
|
|
|
feature: TK_FEATURE TK_NAME {
|
|
newQualifier(currentModule,-1,-1,$2,feature_qualifier);
|
|
}
|
|
;
|
|
|
|
timeline: TK_TIMELINE {
|
|
currentTimelineOrder = 0;
|
|
}
|
|
'{' qualifierlist '}' {
|
|
qualDef *qd;
|
|
int nrneeded;
|
|
|
|
/*
|
|
* Check that exactly one time slot in the set was
|
|
* requested.
|
|
*/
|
|
|
|
nrneeded = 0;
|
|
|
|
for (qd = currentModule -> qualifiers; qd != NULL; qd = qd -> next)
|
|
if (qd -> qtype == time_qualifier && isNeeded(qd))
|
|
++nrneeded;
|
|
|
|
if (nrneeded > 1)
|
|
yyerror("At most one of this %Timeline must be specified with the -t flag");
|
|
|
|
currentModule -> nrtimelines++;
|
|
}
|
|
;
|
|
|
|
qualifierlist: qualifiername
|
|
| qualifierlist qualifiername
|
|
;
|
|
|
|
qualifiername: TK_NAME {
|
|
newQualifier(currentModule,currentModule -> nrtimelines,currentTimelineOrder++,$1,time_qualifier);
|
|
}
|
|
;
|
|
|
|
ifstart: TK_IF '(' qualifiers ')' {
|
|
if (skipStackPtr >= MAX_NESTED_IF)
|
|
yyerror("Internal error: increase the value of MAX_NESTED_IF");
|
|
|
|
/* Nested %Ifs are implicit logical ands. */
|
|
|
|
if (skipStackPtr > 0)
|
|
$3 = ($3 && skipStack[skipStackPtr - 1]);
|
|
|
|
skipStack[skipStackPtr++] = $3;
|
|
}
|
|
;
|
|
|
|
oredqualifiers: TK_NAME {
|
|
$$ = platOrFeature($1,FALSE);
|
|
}
|
|
| '!' TK_NAME {
|
|
$$ = platOrFeature($2,TRUE);
|
|
}
|
|
| oredqualifiers TK_LOGICAL_OR TK_NAME {
|
|
$$ = (platOrFeature($3,FALSE) || $1);
|
|
}
|
|
| oredqualifiers TK_LOGICAL_OR '!' TK_NAME {
|
|
$$ = (platOrFeature($4,TRUE) || $1);
|
|
}
|
|
;
|
|
|
|
qualifiers: oredqualifiers
|
|
| optname '-' optname {
|
|
$$ = timePeriod($1,$3);
|
|
}
|
|
;
|
|
|
|
ifend: TK_END {
|
|
if (skipStackPtr-- <= 0)
|
|
yyerror("Too many %End directives");
|
|
}
|
|
;
|
|
|
|
license: TK_LICENSE optflags {
|
|
optFlag *of;
|
|
|
|
if ($2.nrFlags == 0)
|
|
yyerror("%License details not specified");
|
|
|
|
if ((of = findOptFlag(&$2,"Type",string_flag)) == NULL)
|
|
yyerror("%License type not specified");
|
|
|
|
currentModule -> license = sipMalloc(sizeof (licenseDef));
|
|
|
|
currentModule -> license -> type = of -> fvalue.sval;
|
|
|
|
currentModule -> license -> licensee =
|
|
((of = findOptFlag(&$2,"Licensee",string_flag)) != NULL)
|
|
? of -> fvalue.sval : NULL;
|
|
|
|
currentModule -> license -> timestamp =
|
|
((of = findOptFlag(&$2,"Timestamp",string_flag)) != NULL)
|
|
? of -> fvalue.sval : NULL;
|
|
|
|
currentModule -> license -> sig =
|
|
((of = findOptFlag(&$2,"Signature",string_flag)) != NULL)
|
|
? of -> fvalue.sval : NULL;
|
|
}
|
|
;
|
|
|
|
defmetatype:TK_DEFMETATYPE dottedname {
|
|
if (notSkipping())
|
|
{
|
|
if (currentModule->defmetatype != NULL)
|
|
yyerror("%DefaultMetatype has already been defined for this module");
|
|
|
|
currentModule->defmetatype = cacheName(currentSpec, $2);
|
|
}
|
|
}
|
|
;
|
|
|
|
defsupertype: TK_DEFSUPERTYPE dottedname {
|
|
if (notSkipping())
|
|
{
|
|
if (currentModule->defsupertype != NULL)
|
|
yyerror("%DefaultSupertype has already been defined for this module");
|
|
|
|
currentModule->defsupertype = cacheName(currentSpec, $2);
|
|
}
|
|
}
|
|
;
|
|
|
|
consmodule: TK_CONSMODULE dottedname {
|
|
/* Make sure this is the first mention of a module. */
|
|
if (currentSpec->module != currentModule)
|
|
yyerror("A %ConsolidatedModule cannot be %Imported");
|
|
|
|
if (currentModule->fullname != NULL)
|
|
yyerror("%ConsolidatedModule must appear before any %Module or %CModule directive");
|
|
|
|
setModuleName(currentSpec, currentModule, $2);
|
|
setIsConsolidated(currentModule);
|
|
}
|
|
;
|
|
|
|
compmodule: TK_COMPOMODULE dottedname {
|
|
/* Make sure this is the first mention of a module. */
|
|
if (currentSpec->module != currentModule)
|
|
yyerror("A %CompositeModule cannot be %Imported");
|
|
|
|
if (currentModule->fullname != NULL)
|
|
yyerror("%CompositeModule must appear before any %Module or %CModule directive");
|
|
|
|
setModuleName(currentSpec, currentModule, $2);
|
|
setIsComposite(currentModule);
|
|
}
|
|
;
|
|
|
|
module: modlang dottedname optnumber {
|
|
/* Check the module hasn't already been defined. */
|
|
|
|
moduleDef *mod;
|
|
|
|
for (mod = currentSpec->modules; mod != NULL; mod = mod->next)
|
|
if (mod->fullname != NULL && strcmp(mod->fullname->text, $2) == 0)
|
|
yyerror("Module is already defined");
|
|
|
|
/*
|
|
* If we are in a container module then create a component module
|
|
* and make it current.
|
|
*/
|
|
if (isContainer(currentModule) || currentModule->container != NULL)
|
|
{
|
|
mod = allocModule();
|
|
|
|
mod->file = currentContext.filename;
|
|
mod->container = (isContainer(currentModule) ? currentModule : currentModule->container);
|
|
|
|
currentModule = mod;
|
|
}
|
|
|
|
setModuleName(currentSpec, currentModule, $2);
|
|
currentModule->version = $3;
|
|
|
|
if (currentSpec->genc < 0)
|
|
currentSpec->genc = $1;
|
|
else if (currentSpec->genc != $1)
|
|
yyerror("Cannot mix C and C++ modules");
|
|
}
|
|
;
|
|
|
|
modlang: TK_MODULE {
|
|
$$ = FALSE;
|
|
}
|
|
| TK_CMODULE {
|
|
$$ = TRUE;
|
|
}
|
|
;
|
|
|
|
dottedname: TK_NAME
|
|
| TK_PATHNAME {
|
|
/*
|
|
* The grammar design is a bit broken and this is the easiest way
|
|
* to allow periods in names.
|
|
*/
|
|
|
|
char *cp;
|
|
|
|
for (cp = $1; *cp != '\0'; ++cp)
|
|
if (*cp != '.' && *cp != '_' && !isalnum(*cp))
|
|
yyerror("Invalid character in name");
|
|
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
optnumber: {
|
|
$$ = -1;
|
|
}
|
|
| TK_NUMBER
|
|
;
|
|
|
|
include: TK_INCLUDE TK_PATHNAME {
|
|
parseFile(NULL, $2, NULL, FALSE);
|
|
}
|
|
;
|
|
|
|
optinclude: TK_OPTINCLUDE TK_PATHNAME {
|
|
parseFile(NULL, $2, NULL, TRUE);
|
|
}
|
|
;
|
|
|
|
import: TK_IMPORT TK_PATHNAME {
|
|
newImport($2);
|
|
}
|
|
;
|
|
|
|
optaccesscode: {
|
|
$$ = NULL;
|
|
}
|
|
| TK_ACCESSCODE codeblock {
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
optgetcode: {
|
|
$$ = NULL;
|
|
}
|
|
| TK_GETCODE codeblock {
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
optsetcode: {
|
|
$$ = NULL;
|
|
}
|
|
| TK_SETCODE codeblock {
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
copying: TK_COPYING codeblock {
|
|
appendCodeBlock(¤tModule->copying, $2);
|
|
}
|
|
;
|
|
|
|
exphdrcode: TK_EXPHEADERCODE codeblock {
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
modhdrcode: TK_MODHEADERCODE codeblock {
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
typehdrcode: TK_TYPEHEADERCODE codeblock {
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
opttypehdrcode: {
|
|
$$ = NULL;
|
|
}
|
|
| typehdrcode
|
|
;
|
|
|
|
travcode: TK_TRAVERSECODE codeblock {
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
clearcode: TK_CLEARCODE codeblock {
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
getbufcode: TK_GETBUFFERCODE codeblock {
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
releasebufcode: TK_RELEASEBUFFERCODE codeblock {
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
readbufcode: TK_READBUFFERCODE codeblock {
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
writebufcode: TK_WRITEBUFFERCODE codeblock {
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
segcountcode: TK_SEGCOUNTCODE codeblock {
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
charbufcode: TK_CHARBUFFERCODE codeblock {
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
picklecode: TK_PICKLECODE codeblock {
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
modcode: TK_MODCODE codeblock {
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
typecode: TK_TYPECODE codeblock {
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
preinitcode: TK_PREINITCODE codeblock {
|
|
if (notSkipping())
|
|
appendCodeBlock(¤tModule->preinitcode, $2);
|
|
}
|
|
;
|
|
|
|
initcode: TK_INITCODE codeblock {
|
|
if (notSkipping())
|
|
appendCodeBlock(¤tModule->initcode, $2);
|
|
}
|
|
;
|
|
|
|
postinitcode: TK_POSTINITCODE codeblock {
|
|
if (notSkipping())
|
|
appendCodeBlock(¤tModule->postinitcode, $2);
|
|
}
|
|
;
|
|
|
|
unitcode: TK_UNITCODE codeblock {
|
|
if (notSkipping())
|
|
appendCodeBlock(¤tModule->unitcode, $2);
|
|
}
|
|
;
|
|
|
|
prepycode: TK_PREPYCODE codeblock {
|
|
/*
|
|
* This is a no-op and is retained for compatibility
|
|
* until the last use of it (by SIP v3) can be removed
|
|
* from PyQt.
|
|
*/
|
|
}
|
|
;
|
|
|
|
doc: TK_DOC codeblock {
|
|
if (inMainModule())
|
|
appendCodeBlock(¤tSpec -> docs,$2);
|
|
}
|
|
;
|
|
|
|
exporteddoc: TK_EXPORTEDDOC codeblock {
|
|
appendCodeBlock(¤tSpec -> docs,$2);
|
|
}
|
|
;
|
|
|
|
makefile: TK_MAKEFILE TK_PATHNAME optfilename codeblock {
|
|
if (inMainModule())
|
|
yywarning("%Makefile is ignored, please use the -b flag instead");
|
|
}
|
|
;
|
|
|
|
codeblock: codelines TK_END
|
|
;
|
|
|
|
codelines: TK_CODELINE
|
|
| codelines TK_CODELINE {
|
|
$$ = $1;
|
|
|
|
append(&$$->frag, $2->frag);
|
|
|
|
free($2->frag);
|
|
free((char *)$2->filename);
|
|
free($2);
|
|
}
|
|
;
|
|
|
|
enum: TK_ENUM optname optflags {
|
|
if (notSkipping())
|
|
{
|
|
if (sectionFlags != 0 && (sectionFlags & ~(SECT_IS_PUBLIC | SECT_IS_PROT)) != 0)
|
|
yyerror("Class enums must be in the public or protected sections");
|
|
|
|
currentEnum = newEnum(currentSpec, currentModule,
|
|
currentMappedType, $2, &$3, sectionFlags);
|
|
}
|
|
} '{' optenumbody '}' ';'
|
|
;
|
|
|
|
optfilename: {
|
|
$$ = NULL;
|
|
}
|
|
| TK_PATHNAME {
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
optname: {
|
|
$$ = NULL;
|
|
}
|
|
| TK_NAME {
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
optenumbody:
|
|
| enumbody
|
|
;
|
|
|
|
enumbody: enumline
|
|
| enumbody enumline
|
|
;
|
|
|
|
enumline: ifstart
|
|
| ifend
|
|
| TK_NAME optenumassign optflags optcomma {
|
|
if (notSkipping())
|
|
{
|
|
enumMemberDef *emd, **tail;
|
|
|
|
/* Note that we don't use the assigned value. */
|
|
emd = sipMalloc(sizeof (enumMemberDef));
|
|
|
|
emd -> pyname = cacheName(currentSpec, getPythonName(&$3, $1));
|
|
emd -> cname = $1;
|
|
emd -> ed = currentEnum;
|
|
emd -> next = NULL;
|
|
|
|
checkAttributes(currentSpec, currentModule, emd->ed->ecd,
|
|
emd->ed->emtd, emd->pyname->text, FALSE);
|
|
|
|
/* Append to preserve the order. */
|
|
for (tail = ¤tEnum->members; *tail != NULL; tail = &(*tail)->next)
|
|
;
|
|
|
|
*tail = emd;
|
|
|
|
if (inMainModule())
|
|
setIsUsedName(emd -> pyname);
|
|
}
|
|
}
|
|
;
|
|
|
|
optcomma:
|
|
| ','
|
|
;
|
|
|
|
optenumassign:
|
|
| '=' value
|
|
;
|
|
|
|
optassign: {
|
|
$$ = NULL;
|
|
}
|
|
| '=' expr {
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
expr: value
|
|
| expr binop value {
|
|
valueDef *vd;
|
|
|
|
if ($1 -> vtype == string_value || $3 -> vtype == string_value)
|
|
yyerror("Invalid binary operator for string");
|
|
|
|
/* Find the last value in the existing expression. */
|
|
|
|
for (vd = $1; vd -> next != NULL; vd = vd -> next)
|
|
;
|
|
|
|
vd -> vbinop = $2;
|
|
vd -> next = $3;
|
|
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
binop: '-' {
|
|
$$ = '-';
|
|
}
|
|
| '+' {
|
|
$$ = '+';
|
|
}
|
|
| '*' {
|
|
$$ = '*';
|
|
}
|
|
| '/' {
|
|
$$ = '/';
|
|
}
|
|
| '&' {
|
|
$$ = '&';
|
|
}
|
|
| '|' {
|
|
$$ = '|';
|
|
}
|
|
;
|
|
|
|
optunop: {
|
|
$$ = '\0';
|
|
}
|
|
| '!' {
|
|
$$ = '!';
|
|
}
|
|
| '~' {
|
|
$$ = '~';
|
|
}
|
|
| '-' {
|
|
$$ = '-';
|
|
}
|
|
| '+' {
|
|
$$ = '+';
|
|
}
|
|
;
|
|
|
|
value: optunop simplevalue {
|
|
if ($1 != '\0' && $2.vtype == string_value)
|
|
yyerror("Invalid unary operator for string");
|
|
|
|
/*
|
|
* Convert the value to a simple expression on the
|
|
* heap.
|
|
*/
|
|
|
|
$$ = sipMalloc(sizeof (valueDef));
|
|
|
|
*$$ = $2;
|
|
$$ -> vunop = $1;
|
|
$$ -> vbinop = '\0';
|
|
$$ -> next = NULL;
|
|
}
|
|
;
|
|
|
|
scopedname: scopepart
|
|
| scopedname TK_SCOPE scopepart {
|
|
if (currentSpec -> genc)
|
|
yyerror("Scoped names are not allowed in a C module");
|
|
|
|
appendScopedName(&$1,$3);
|
|
}
|
|
;
|
|
|
|
scopepart: TK_NAME {
|
|
$$ = text2scopePart($1);
|
|
}
|
|
;
|
|
|
|
simplevalue: scopedname {
|
|
/*
|
|
* We let the C++ compiler decide if the value is a valid one - no
|
|
* point in building a full C++ parser here.
|
|
*/
|
|
|
|
$$.vtype = scoped_value;
|
|
$$.u.vscp = $1;
|
|
}
|
|
| basetype '(' exprlist ')' {
|
|
fcallDef *fcd;
|
|
|
|
fcd = sipMalloc(sizeof (fcallDef));
|
|
*fcd = $3;
|
|
fcd -> type = $1;
|
|
|
|
$$.vtype = fcall_value;
|
|
$$.u.fcd = fcd;
|
|
}
|
|
| TK_REAL {
|
|
$$.vtype = real_value;
|
|
$$.u.vreal = $1;
|
|
}
|
|
| TK_NUMBER {
|
|
$$.vtype = numeric_value;
|
|
$$.u.vnum = $1;
|
|
}
|
|
| TK_TRUE {
|
|
$$.vtype = numeric_value;
|
|
$$.u.vnum = 1;
|
|
}
|
|
| TK_FALSE {
|
|
$$.vtype = numeric_value;
|
|
$$.u.vnum = 0;
|
|
}
|
|
| TK_NULL {
|
|
$$.vtype = numeric_value;
|
|
$$.u.vnum = 0;
|
|
}
|
|
| TK_STRING {
|
|
$$.vtype = string_value;
|
|
$$.u.vstr = $1;
|
|
}
|
|
| TK_TQCHAR {
|
|
$$.vtype = qchar_value;
|
|
$$.u.vqchar = $1;
|
|
}
|
|
;
|
|
|
|
exprlist: {
|
|
/* No values. */
|
|
|
|
$$.nrArgs = 0;
|
|
}
|
|
| expr {
|
|
/* The single or first expression. */
|
|
|
|
$$.args[0] = $1;
|
|
$$.nrArgs = 1;
|
|
}
|
|
| exprlist ',' expr {
|
|
/* Check that it wasn't ...(,expression...). */
|
|
|
|
if ($$.nrArgs == 0)
|
|
yyerror("First argument to function call is missing");
|
|
|
|
/* Check there is room. */
|
|
|
|
if ($1.nrArgs == MAX_NR_ARGS)
|
|
yyerror("Too many arguments to function call");
|
|
|
|
$$ = $1;
|
|
|
|
$$.args[$$.nrArgs] = $3;
|
|
$$.nrArgs++;
|
|
}
|
|
;
|
|
|
|
typedef: TK_TYPEDEF cpptype TK_NAME optflags ';' {
|
|
if (notSkipping())
|
|
{
|
|
applyTypeFlags(currentModule, &$2, &$4);
|
|
newTypedef(currentSpec, currentModule, $3, &$2, &$4);
|
|
}
|
|
}
|
|
| TK_TYPEDEF cpptype '(' deref TK_NAME ')' '(' cpptypelist ')' optflags ';' {
|
|
if (notSkipping())
|
|
{
|
|
signatureDef *sig;
|
|
argDef ftype;
|
|
|
|
applyTypeFlags(currentModule, &$2, &$10);
|
|
|
|
memset(&ftype, 0, sizeof (argDef));
|
|
|
|
/* Create the full signature on the heap. */
|
|
sig = sipMalloc(sizeof (signatureDef));
|
|
*sig = $8;
|
|
sig->result = $2;
|
|
|
|
/* Create the full type. */
|
|
ftype.atype = function_type;
|
|
ftype.nrderefs = $4;
|
|
ftype.u.sa = sig;
|
|
|
|
newTypedef(currentSpec, currentModule, $5, &ftype, &$10);
|
|
}
|
|
}
|
|
;
|
|
|
|
struct: TK_STRUCT scopedname {
|
|
if (currentSpec -> genc && $2->next != NULL)
|
|
yyerror("Namespaces not allowed in a C module");
|
|
|
|
if (notSkipping())
|
|
currentSupers = NULL;
|
|
} superclasses optflags {
|
|
if (notSkipping())
|
|
{
|
|
if (currentSpec->genc && currentSupers != NULL)
|
|
yyerror("Super-classes not allowed in a C module struct");
|
|
|
|
defineClass($2, currentSupers, &$5);
|
|
sectionFlags = SECT_IS_PUBLIC;
|
|
}
|
|
} optclassbody ';' {
|
|
if (notSkipping())
|
|
completeClass($2, &$5, $7);
|
|
}
|
|
;
|
|
|
|
classtmpl: template {currentIsTemplate = TRUE;} class {
|
|
if (currentSpec->genc)
|
|
yyerror("Class templates not allowed in a C module");
|
|
|
|
if (notSkipping())
|
|
{
|
|
classTmplDef *tcd;
|
|
|
|
/*
|
|
* Make sure there is room for the extra class name argument.
|
|
*/
|
|
if ($1.nrArgs == MAX_NR_ARGS)
|
|
yyerror("Internal error - increase the value of MAX_NR_ARGS");
|
|
|
|
tcd = sipMalloc(sizeof (classTmplDef));
|
|
tcd->sig = $1;
|
|
tcd->cd = $3;
|
|
tcd->next = currentSpec->classtemplates;
|
|
|
|
currentSpec->classtemplates = tcd;
|
|
}
|
|
|
|
currentIsTemplate = FALSE;
|
|
}
|
|
;
|
|
|
|
template: TK_TEMPLATE '<' cpptypelist '>' {
|
|
$$ = $3;
|
|
}
|
|
;
|
|
|
|
class: TK_CLASS scopedname {
|
|
if (currentSpec->genc)
|
|
yyerror("Class definition not allowed in a C module");
|
|
|
|
if (notSkipping())
|
|
currentSupers = NULL;
|
|
} superclasses optflags {
|
|
if (notSkipping())
|
|
{
|
|
defineClass($2, currentSupers, &$5);
|
|
sectionFlags = SECT_IS_PRIVATE;
|
|
}
|
|
} optclassbody ';' {
|
|
if (notSkipping())
|
|
$$ = completeClass($2, &$5, $7);
|
|
}
|
|
;
|
|
|
|
superclasses:
|
|
| ':' superlist
|
|
;
|
|
|
|
superlist: superclass
|
|
| superlist ',' superclass
|
|
;
|
|
|
|
superclass: scopedname {
|
|
if (notSkipping())
|
|
{
|
|
argDef ad;
|
|
classDef *super;
|
|
scopedNameDef *snd = $1;
|
|
|
|
/*
|
|
* This is a hack to allow typedef'ed classes to be used before
|
|
* we have resolved the typedef definitions. Unlike elsewhere,
|
|
* we require that the typedef is defined before being used.
|
|
*/
|
|
for (;;)
|
|
{
|
|
ad.atype = no_type;
|
|
ad.argflags = 0;
|
|
ad.nrderefs = 0;
|
|
ad.original_type = NULL;
|
|
|
|
searchTypedefs(currentSpec, snd, &ad);
|
|
|
|
if (ad.atype != defined_type)
|
|
break;
|
|
|
|
if (ad.nrderefs != 0 || isConstArg(&ad) || isReference(&ad))
|
|
break;
|
|
|
|
snd = ad.u.snd;
|
|
}
|
|
|
|
if (ad.atype != no_type)
|
|
yyerror("Super-class list contains an invalid type");
|
|
|
|
super = findClass(currentSpec, class_iface, NULL, snd);
|
|
appendToClassList(¤tSupers, super);
|
|
}
|
|
}
|
|
;
|
|
|
|
optclassbody: {
|
|
$$ = FALSE;
|
|
}
|
|
| '{' classbody '}' {
|
|
$$ = TRUE;
|
|
}
|
|
;
|
|
|
|
classbody: classline
|
|
| classbody classline
|
|
;
|
|
|
|
classline: ifstart
|
|
| ifend
|
|
| namespace
|
|
| struct
|
|
| class
|
|
| exception
|
|
| typedef
|
|
| enum
|
|
| docstring {
|
|
if (notSkipping())
|
|
{
|
|
classDef *scope = currentScope();
|
|
|
|
/* Make sure this is before any ctor docstrings. */
|
|
$1->next = scope->docstring;
|
|
scope->docstring = $1;
|
|
}
|
|
}
|
|
| typecode {
|
|
if (notSkipping())
|
|
appendCodeBlock(¤tScope()->cppcode, $1);
|
|
}
|
|
| typehdrcode {
|
|
if (notSkipping())
|
|
appendCodeBlock(¤tScope()->iff->hdrcode, $1);
|
|
}
|
|
| travcode {
|
|
if (notSkipping())
|
|
{
|
|
classDef *scope = currentScope();
|
|
|
|
if (scope->travcode != NULL)
|
|
yyerror("%GCTraverseCode already given for class");
|
|
|
|
scope->travcode = $1;
|
|
}
|
|
}
|
|
| clearcode {
|
|
if (notSkipping())
|
|
{
|
|
classDef *scope = currentScope();
|
|
|
|
if (scope->clearcode != NULL)
|
|
yyerror("%GCClearCode already given for class");
|
|
|
|
scope->clearcode = $1;
|
|
}
|
|
}
|
|
| getbufcode {
|
|
if (notSkipping())
|
|
{
|
|
classDef *scope = currentScope();
|
|
|
|
if (scope->getbufcode != NULL)
|
|
yyerror("%BIGetBufferCode already given for class");
|
|
|
|
scope->getbufcode = $1;
|
|
}
|
|
}
|
|
| releasebufcode {
|
|
if (notSkipping())
|
|
{
|
|
classDef *scope = currentScope();
|
|
|
|
if (scope->releasebufcode != NULL)
|
|
yyerror("%BIReleaseBufferCode already given for class");
|
|
|
|
scope->releasebufcode = $1;
|
|
}
|
|
}
|
|
| readbufcode {
|
|
if (notSkipping())
|
|
{
|
|
classDef *scope = currentScope();
|
|
|
|
if (scope->readbufcode != NULL)
|
|
yyerror("%BIGetReadBufferCode already given for class");
|
|
|
|
scope->readbufcode = $1;
|
|
}
|
|
}
|
|
| writebufcode {
|
|
if (notSkipping())
|
|
{
|
|
classDef *scope = currentScope();
|
|
|
|
if (scope->writebufcode != NULL)
|
|
yyerror("%BIGetWriteBufferCode already given for class");
|
|
|
|
scope->writebufcode = $1;
|
|
}
|
|
}
|
|
| segcountcode {
|
|
if (notSkipping())
|
|
{
|
|
classDef *scope = currentScope();
|
|
|
|
if (scope->segcountcode != NULL)
|
|
yyerror("%BIGetSegCountCode already given for class");
|
|
|
|
scope->segcountcode = $1;
|
|
}
|
|
}
|
|
| charbufcode {
|
|
if (notSkipping())
|
|
{
|
|
classDef *scope = currentScope();
|
|
|
|
if (scope->charbufcode != NULL)
|
|
yyerror("%BIGetCharBufferCode already given for class");
|
|
|
|
scope->charbufcode = $1;
|
|
}
|
|
}
|
|
| picklecode {
|
|
if (notSkipping())
|
|
{
|
|
classDef *scope = currentScope();
|
|
|
|
if (scope->picklecode != NULL)
|
|
yyerror("%PickleCode already given for class");
|
|
|
|
scope->picklecode = $1;
|
|
}
|
|
}
|
|
| ctor
|
|
| dtor
|
|
| varmember
|
|
| TK_TOSUBCLASS codeblock {
|
|
if (notSkipping())
|
|
{
|
|
classDef *scope = currentScope();
|
|
|
|
if (scope->convtosubcode != NULL)
|
|
yyerror("Class has more than one %ConvertToSubClassCode directive");
|
|
|
|
scope->convtosubcode = $2;
|
|
}
|
|
}
|
|
| TK_TOTYPE codeblock {
|
|
if (notSkipping())
|
|
{
|
|
classDef *scope = currentScope();
|
|
|
|
if (scope->convtocode != NULL)
|
|
yyerror("Class has more than one %ConvertToTypeCode directive");
|
|
|
|
scope->convtocode = $2;
|
|
}
|
|
}
|
|
| TK_PUBLIC optslot ':' {
|
|
if (currentSpec -> genc)
|
|
yyerror("public section not allowed in a C module");
|
|
|
|
if (notSkipping())
|
|
sectionFlags = SECT_IS_PUBLIC | $2;
|
|
}
|
|
| TK_PROTECTED optslot ':' {
|
|
if (currentSpec -> genc)
|
|
yyerror("protected section not allowed in a C module");
|
|
|
|
if (notSkipping())
|
|
sectionFlags = SECT_IS_PROT | $2;
|
|
}
|
|
| TK_PRIVATE optslot ':' {
|
|
if (currentSpec -> genc)
|
|
yyerror("private section not allowed in a C module");
|
|
|
|
if (notSkipping())
|
|
sectionFlags = SECT_IS_PRIVATE | $2;
|
|
}
|
|
| TK_SIGNALS ':' {
|
|
if (currentSpec -> genc)
|
|
yyerror("signals section not allowed in a C module");
|
|
|
|
if (notSkipping())
|
|
sectionFlags = SECT_IS_SIGNAL;
|
|
}
|
|
;
|
|
|
|
optslot: {
|
|
$$ = 0;
|
|
}
|
|
| TK_SLOTS {
|
|
$$ = SECT_IS_SLOT;
|
|
}
|
|
;
|
|
|
|
dtor: optvirtual '~' TK_NAME '(' ')' optexceptions optabstract optflags ';' methodcode virtualcatchercode {
|
|
/* Note that we allow non-virtual dtors in C modules. */
|
|
|
|
if (notSkipping())
|
|
{
|
|
classDef *cd = currentScope();
|
|
|
|
if (strcmp(classBaseName(cd),$3) != 0)
|
|
yyerror("Destructor doesn't have the same name as its class");
|
|
|
|
if (isDtor(cd))
|
|
yyerror("Destructor has already been defined");
|
|
|
|
if (currentSpec -> genc && $10 == NULL)
|
|
yyerror("Destructor in C modules must include %MethodCode");
|
|
|
|
cd -> dealloccode = $10;
|
|
cd -> dtorcode = $11;
|
|
cd -> dtorexceptions = $6;
|
|
|
|
/*
|
|
* Note that we don't apply the protected/public hack to dtors
|
|
* as it (I think) may change the behaviour of the wrapped API.
|
|
*/
|
|
cd->classflags |= sectionFlags;
|
|
|
|
if ($7)
|
|
{
|
|
if (!$1)
|
|
yyerror("Abstract destructor must be virtual");
|
|
|
|
setIsAbstractClass(cd);
|
|
}
|
|
|
|
/*
|
|
* The class has a shadow if we have a virtual dtor or some
|
|
* dtor code.
|
|
*/
|
|
if ($1 || $11 != NULL)
|
|
{
|
|
if (currentSpec -> genc)
|
|
yyerror("Virtual destructor or %VirtualCatcherCode not allowed in a C module");
|
|
|
|
setHasShadow(cd);
|
|
}
|
|
|
|
if (getReleaseGIL(&$8))
|
|
setIsReleaseGILDtor(cd);
|
|
else if (getHoldGIL(&$8))
|
|
setIsHoldGILDtor(cd);
|
|
}
|
|
}
|
|
;
|
|
|
|
ctor: TK_EXPLICIT {currentCtorIsExplicit = TRUE;} simplector
|
|
| simplector
|
|
;
|
|
|
|
simplector: TK_NAME '(' arglist ')' optexceptions optflags optctorsig ';' optdocstring methodcode {
|
|
/* Note that we allow ctors in C modules. */
|
|
|
|
if (notSkipping())
|
|
{
|
|
if (currentSpec -> genc)
|
|
{
|
|
if ($10 == NULL && $3.nrArgs != 0)
|
|
yyerror("Constructors with arguments in C modules must include %MethodCode");
|
|
|
|
if (currentCtorIsExplicit)
|
|
yyerror("Explicit constructors not allowed in a C module");
|
|
}
|
|
|
|
if ((sectionFlags & (SECT_IS_PUBLIC | SECT_IS_PROT | SECT_IS_PRIVATE)) == 0)
|
|
yyerror("Constructor must be in the public, private or protected sections");
|
|
|
|
newCtor($1, sectionFlags, &$3, &$6, $10, $5, $7,
|
|
currentCtorIsExplicit, $9);
|
|
}
|
|
|
|
free($1);
|
|
|
|
currentCtorIsExplicit = FALSE;
|
|
}
|
|
;
|
|
|
|
optctorsig: {
|
|
$$ = NULL;
|
|
}
|
|
| '[' '(' arglist ')' ']' {
|
|
$$ = sipMalloc(sizeof (signatureDef));
|
|
|
|
*$$ = $3;
|
|
}
|
|
;
|
|
|
|
optsig: {
|
|
$$ = NULL;
|
|
}
|
|
| '[' cpptype '(' arglist ')' ']' {
|
|
$$ = sipMalloc(sizeof (signatureDef));
|
|
|
|
*$$ = $4;
|
|
$$ -> result = $2;
|
|
}
|
|
;
|
|
|
|
optvirtual: {
|
|
$$ = FALSE;
|
|
}
|
|
| TK_VIRTUAL {
|
|
$$ = TRUE;
|
|
}
|
|
;
|
|
|
|
function: cpptype TK_NAME '(' arglist ')' optconst optexceptions optabstract optflags optsig ';' optdocstring methodcode virtualcatchercode {
|
|
if (notSkipping())
|
|
{
|
|
applyTypeFlags(currentModule, &$1, &$9);
|
|
|
|
$4.result = $1;
|
|
|
|
newFunction(currentSpec, currentModule, currentScope(), NULL,
|
|
sectionFlags, currentIsStatic, currentIsSignal,
|
|
currentIsSlot, currentOverIsVirt, $2, &$4, $6, $8, &$9,
|
|
$13, $14, $7, $10, $12);
|
|
}
|
|
|
|
currentIsStatic = FALSE;
|
|
currentIsSignal = FALSE;
|
|
currentIsSlot = FALSE;
|
|
currentOverIsVirt = FALSE;
|
|
}
|
|
| cpptype TK_OPERATOR '=' '(' cpptype ')' ';' {
|
|
/*
|
|
* It looks like an assignment operator (though we don't bother to
|
|
* check the types) so make sure it is private.
|
|
*/
|
|
if (notSkipping())
|
|
{
|
|
classDef *cd = currentScope();
|
|
|
|
if (cd == NULL || !(sectionFlags & SECT_IS_PRIVATE))
|
|
yyerror("Assignment operators may only be defined as private");
|
|
|
|
setCannotAssign(cd);
|
|
}
|
|
|
|
currentIsStatic = FALSE;
|
|
currentIsSignal = FALSE;
|
|
currentIsSlot = FALSE;
|
|
currentOverIsVirt = FALSE;
|
|
}
|
|
| cpptype TK_OPERATOR operatorname '(' arglist ')' optconst optexceptions optabstract optflags optsig ';' methodcode virtualcatchercode {
|
|
if (notSkipping())
|
|
{
|
|
classDef *cd = currentScope();
|
|
|
|
applyTypeFlags(currentModule, &$1, &$10);
|
|
|
|
/* Handle the unary '+' and '-' operators. */
|
|
if ((cd != NULL && $5.nrArgs == 0) || (cd == NULL && $5.nrArgs == 1))
|
|
{
|
|
if (strcmp($3, "__add__") == 0)
|
|
$3 = "__pos__";
|
|
else if (strcmp($3, "__sub__") == 0)
|
|
$3 = "__neg__";
|
|
}
|
|
|
|
$5.result = $1;
|
|
|
|
newFunction(currentSpec, currentModule, cd, NULL,
|
|
sectionFlags, currentIsStatic, currentIsSignal,
|
|
currentIsSlot, currentOverIsVirt, $3, &$5, $7, $9,
|
|
&$10, $13, $14, $8, $11, NULL);
|
|
}
|
|
|
|
currentIsStatic = FALSE;
|
|
currentIsSignal = FALSE;
|
|
currentIsSlot = FALSE;
|
|
currentOverIsVirt = FALSE;
|
|
}
|
|
| TK_OPERATOR cpptype '(' arglist ')' optconst optexceptions optabstract optflags optsig ';' methodcode virtualcatchercode {
|
|
if (notSkipping())
|
|
{
|
|
char *sname;
|
|
classDef *scope = currentScope();
|
|
|
|
if (scope == NULL || $4.nrArgs != 0)
|
|
yyerror("Operator casts must be specified in a class and have no arguments");
|
|
|
|
applyTypeFlags(currentModule, &$2, &$9);
|
|
|
|
switch ($2.atype)
|
|
{
|
|
case defined_type:
|
|
sname = NULL;
|
|
break;
|
|
|
|
case bool_type:
|
|
case cbool_type:
|
|
case short_type:
|
|
case ushort_type:
|
|
case int_type:
|
|
case cint_type:
|
|
case uint_type:
|
|
sname = "__int__";
|
|
break;
|
|
|
|
case long_type:
|
|
case ulong_type:
|
|
case longlong_type:
|
|
case ulonglong_type:
|
|
sname = "__long__";
|
|
break;
|
|
|
|
case float_type:
|
|
case cfloat_type:
|
|
case double_type:
|
|
case cdouble_type:
|
|
sname = "__float__";
|
|
break;
|
|
|
|
default:
|
|
yyerror("Unsupported operator cast");
|
|
}
|
|
|
|
if (sname != NULL)
|
|
{
|
|
$4.result = $2;
|
|
|
|
newFunction(currentSpec, currentModule, scope, NULL,
|
|
sectionFlags, currentIsStatic, currentIsSignal,
|
|
currentIsSlot, currentOverIsVirt, sname, &$4, $6,
|
|
$8, &$9, $12, $13, $7, $10, NULL);
|
|
}
|
|
else
|
|
{
|
|
argList *al;
|
|
|
|
/* Check it doesn't already exist. */
|
|
for (al = scope->casts; al != NULL; al = al->next)
|
|
if (compareScopedNames($2.u.snd, al->arg.u.snd) == 0)
|
|
yyerror("This operator cast has already been specified in this class");
|
|
|
|
al = sipMalloc(sizeof (argList));
|
|
al->arg = $2;
|
|
al->next = scope->casts;
|
|
|
|
scope->casts = al;
|
|
}
|
|
}
|
|
|
|
currentIsStatic = FALSE;
|
|
currentIsSignal = FALSE;
|
|
currentIsSlot = FALSE;
|
|
currentOverIsVirt = FALSE;
|
|
}
|
|
;
|
|
|
|
operatorname: '+' {$$ = "__add__";}
|
|
| '-' {$$ = "__sub__";}
|
|
| '*' {$$ = "__mul__";}
|
|
| '/' {$$ = "__div__";}
|
|
| '%' {$$ = "__mod__";}
|
|
| '&' {$$ = "__and__";}
|
|
| '|' {$$ = "__or__";}
|
|
| '^' {$$ = "__xor__";}
|
|
| '<' '<' {$$ = "__lshift__";}
|
|
| '>' '>' {$$ = "__rshift__";}
|
|
| '+' '=' {$$ = "__iadd__";}
|
|
| '-' '=' {$$ = "__isub__";}
|
|
| '*' '=' {$$ = "__imul__";}
|
|
| '/' '=' {$$ = "__idiv__";}
|
|
| '%' '=' {$$ = "__imod__";}
|
|
| '&' '=' {$$ = "__iand__";}
|
|
| '|' '=' {$$ = "__ior__";}
|
|
| '^' '=' {$$ = "__ixor__";}
|
|
| '<' '<' '=' {$$ = "__ilshift__";}
|
|
| '>' '>' '=' {$$ = "__irshift__";}
|
|
| '~' {$$ = "__invert__";}
|
|
| '(' ')' {$$ = "__call__";}
|
|
| '[' ']' {$$ = "__getitem__";}
|
|
| '<' {$$ = "__lt__";}
|
|
| '<' '=' {$$ = "__le__";}
|
|
| '=' '=' {$$ = "__eq__";}
|
|
| '!' '=' {$$ = "__ne__";}
|
|
| '>' {$$ = "__gt__";}
|
|
| '>' '=' {$$ = "__ge__";}
|
|
;
|
|
|
|
optconst: {
|
|
$$ = FALSE;
|
|
}
|
|
| TK_CONST {
|
|
$$ = TRUE;
|
|
}
|
|
;
|
|
|
|
optabstract: {
|
|
$$ = 0;
|
|
}
|
|
| '=' TK_NUMBER {
|
|
if ($2 != 0)
|
|
yyerror("Abstract virtual function '= 0' expected");
|
|
|
|
$$ = TRUE;
|
|
}
|
|
;
|
|
|
|
optflags: {
|
|
$$.nrFlags = 0;
|
|
}
|
|
| '/' flaglist '/' {
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
|
|
flaglist: flag {
|
|
$$.flags[0] = $1;
|
|
$$.nrFlags = 1;
|
|
}
|
|
| flaglist ',' flag {
|
|
/* Check there is room. */
|
|
|
|
if ($1.nrFlags == MAX_NR_FLAGS)
|
|
yyerror("Too many optional flags");
|
|
|
|
$$ = $1;
|
|
|
|
$$.flags[$$.nrFlags++] = $3;
|
|
}
|
|
;
|
|
|
|
flag: TK_NAME {
|
|
$$.ftype = bool_flag;
|
|
$$.fname = $1;
|
|
}
|
|
| TK_NAME '=' flagvalue {
|
|
$$ = $3;
|
|
$$.fname = $1;
|
|
}
|
|
;
|
|
|
|
flagvalue: dottedname {
|
|
$$.ftype = (strchr($1, '.') != NULL) ? dotted_name_flag : name_flag;
|
|
$$.fvalue.sval = $1;
|
|
}
|
|
| TK_NAME ':' optnumber '-' optnumber {
|
|
apiVersionRangeDef *avd;
|
|
int from, to;
|
|
|
|
$$.ftype = api_range_flag;
|
|
|
|
/* Check that the API is known. */
|
|
if ((avd = findAPI(currentSpec, $1)) == NULL)
|
|
yyerror("unknown API name in API annotation");
|
|
|
|
if (inMainModule())
|
|
setIsUsedName(avd->api_name);
|
|
|
|
/* Unbounded values are represented by 0. */
|
|
if ((from = $3) < 0)
|
|
from = 0;
|
|
|
|
if ((to = $5) < 0)
|
|
to = 0;
|
|
|
|
$$.fvalue.aval = convertAPIRange(currentModule, avd->api_name,
|
|
from, to);
|
|
}
|
|
| TK_STRING {
|
|
$$.ftype = string_flag;
|
|
$$.fvalue.sval = convertFeaturedString($1);
|
|
}
|
|
| TK_NUMBER {
|
|
$$.ftype = integer_flag;
|
|
$$.fvalue.ival = $1;
|
|
}
|
|
;
|
|
|
|
docstring: TK_DOCSTRING codeblock {
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
optdocstring: {
|
|
$$ = NULL;
|
|
}
|
|
| docstring
|
|
;
|
|
|
|
methodcode: {
|
|
$$ = NULL;
|
|
}
|
|
| TK_METHODCODE codeblock {
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
virtualcatchercode: {
|
|
$$ = NULL;
|
|
}
|
|
| TK_VIRTUALCATCHERCODE codeblock {
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
arglist: rawarglist {
|
|
int a, nrrxcon, nrrxdis, nrslotcon, nrslotdis, nrarray, nrarraysize;
|
|
|
|
nrrxcon = nrrxdis = nrslotcon = nrslotdis = nrarray = nrarraysize = 0;
|
|
|
|
for (a = 0; a < $1.nrArgs; ++a)
|
|
{
|
|
argDef *ad = &$1.args[a];
|
|
|
|
switch (ad -> atype)
|
|
{
|
|
case rxcon_type:
|
|
++nrrxcon;
|
|
break;
|
|
|
|
case rxdis_type:
|
|
++nrrxdis;
|
|
break;
|
|
|
|
case slotcon_type:
|
|
++nrslotcon;
|
|
break;
|
|
|
|
case slotdis_type:
|
|
++nrslotdis;
|
|
break;
|
|
}
|
|
|
|
if (isArray(ad))
|
|
++nrarray;
|
|
|
|
if (isArraySize(ad))
|
|
++nrarraysize;
|
|
}
|
|
|
|
if (nrrxcon != nrslotcon || nrrxcon > 1)
|
|
yyerror("SIP_RXOBJ_CON and SIP_SLOT_CON must both be given and at most once");
|
|
|
|
if (nrrxdis != nrslotdis || nrrxdis > 1)
|
|
yyerror("SIP_RXOBJ_DIS and SIP_SLOT_DIS must both be given and at most once");
|
|
|
|
if (nrarray != nrarraysize || nrarray > 1)
|
|
yyerror("/Array/ and /ArraySize/ must both be given and at most once");
|
|
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
rawarglist: {
|
|
/* No arguments. */
|
|
|
|
$$.nrArgs = 0;
|
|
}
|
|
| argvalue {
|
|
/* The single or first argument. */
|
|
|
|
$$.args[0] = $1;
|
|
$$.nrArgs = 1;
|
|
}
|
|
| rawarglist ',' argvalue {
|
|
/* Check that it wasn't ...(,arg...). */
|
|
if ($1.nrArgs == 0)
|
|
yyerror("First argument of the list is missing");
|
|
|
|
/* Check there is nothing after an ellipsis. */
|
|
if ($1.args[$1.nrArgs - 1].atype == ellipsis_type)
|
|
yyerror("An ellipsis must be at the end of the argument list");
|
|
|
|
/*
|
|
* If this argument has no default value, then the
|
|
* previous one mustn't either.
|
|
*/
|
|
if ($3.defval == NULL && $1.args[$1.nrArgs - 1].defval != NULL)
|
|
yyerror("Compulsory argument given after optional argument");
|
|
|
|
/* Check there is room. */
|
|
if ($1.nrArgs == MAX_NR_ARGS)
|
|
yyerror("Internal error - increase the value of MAX_NR_ARGS");
|
|
|
|
$$ = $1;
|
|
|
|
$$.args[$$.nrArgs] = $3;
|
|
$$.nrArgs++;
|
|
}
|
|
;
|
|
|
|
argvalue: TK_SIPSIGNAL optname optflags optassign {
|
|
$$.atype = signal_type;
|
|
$$.argflags = ARG_IS_CONST;
|
|
$$.nrderefs = 0;
|
|
$$.name = cacheName(currentSpec, $2);
|
|
$$.defval = $4;
|
|
|
|
currentSpec -> sigslots = TRUE;
|
|
}
|
|
| TK_SIPSLOT optname optflags optassign {
|
|
$$.atype = slot_type;
|
|
$$.argflags = ARG_IS_CONST;
|
|
$$.nrderefs = 0;
|
|
$$.name = cacheName(currentSpec, $2);
|
|
$$.defval = $4;
|
|
|
|
currentSpec -> sigslots = TRUE;
|
|
}
|
|
| TK_SIPANYSLOT optname optflags optassign {
|
|
$$.atype = anyslot_type;
|
|
$$.argflags = ARG_IS_CONST;
|
|
$$.nrderefs = 0;
|
|
$$.name = cacheName(currentSpec, $2);
|
|
$$.defval = $4;
|
|
|
|
currentSpec -> sigslots = TRUE;
|
|
}
|
|
| TK_SIPRXCON optname optflags {
|
|
$$.atype = rxcon_type;
|
|
$$.argflags = 0;
|
|
$$.nrderefs = 0;
|
|
$$.name = cacheName(currentSpec, $2);
|
|
|
|
if (findOptFlag(&$3, "SingleShot", bool_flag) != NULL)
|
|
$$.argflags |= ARG_SINGLE_SHOT;
|
|
|
|
currentSpec -> sigslots = TRUE;
|
|
}
|
|
| TK_SIPRXDIS optname optflags {
|
|
$$.atype = rxdis_type;
|
|
$$.argflags = 0;
|
|
$$.nrderefs = 0;
|
|
$$.name = cacheName(currentSpec, $2);
|
|
|
|
currentSpec -> sigslots = TRUE;
|
|
}
|
|
| TK_SIPSLOTCON '(' arglist ')' optname optflags {
|
|
$$.atype = slotcon_type;
|
|
$$.argflags = ARG_IS_CONST;
|
|
$$.nrderefs = 0;
|
|
$$.name = cacheName(currentSpec, $5);
|
|
|
|
memset(&$3.result, 0, sizeof (argDef));
|
|
$3.result.atype = void_type;
|
|
|
|
$$.u.sa = sipMalloc(sizeof (signatureDef));
|
|
*$$.u.sa = $3;
|
|
|
|
currentSpec -> sigslots = TRUE;
|
|
}
|
|
| TK_SIPSLOTDIS '(' arglist ')' optname optflags {
|
|
$$.atype = slotdis_type;
|
|
$$.argflags = ARG_IS_CONST;
|
|
$$.nrderefs = 0;
|
|
$$.name = cacheName(currentSpec, $5);
|
|
|
|
memset(&$3.result, 0, sizeof (argDef));
|
|
$3.result.atype = void_type;
|
|
|
|
$$.u.sa = sipMalloc(sizeof (signatureDef));
|
|
*$$.u.sa = $3;
|
|
|
|
currentSpec -> sigslots = TRUE;
|
|
}
|
|
| TK_TQOBJECT optname optflags {
|
|
$$.atype = qobject_type;
|
|
$$.argflags = 0;
|
|
$$.nrderefs = 0;
|
|
$$.name = cacheName(currentSpec, $2);
|
|
}
|
|
| argtype optassign {
|
|
$$ = $1;
|
|
$$.defval = $2;
|
|
}
|
|
;
|
|
|
|
varmember:
|
|
TK_SIGNAL_METHOD {currentIsSignal = TRUE;} simple_varmem
|
|
| TK_SLOT_METHOD {currentIsSlot = TRUE;} simple_varmem
|
|
| simple_varmem
|
|
;
|
|
|
|
simple_varmem:
|
|
TK_STATIC {currentIsStatic = TRUE;} varmem
|
|
| varmem
|
|
;
|
|
|
|
varmem:
|
|
member
|
|
| variable
|
|
;
|
|
|
|
member:
|
|
TK_VIRTUAL {currentOverIsVirt = TRUE;} function
|
|
| function
|
|
;
|
|
|
|
variable: cpptype TK_NAME optflags ';' optaccesscode optgetcode optsetcode {
|
|
if (notSkipping())
|
|
{
|
|
/* Check the section. */
|
|
|
|
if (sectionFlags != 0)
|
|
{
|
|
if ((sectionFlags & SECT_IS_PUBLIC) == 0)
|
|
yyerror("Class variables must be in the public section");
|
|
|
|
if (!currentIsStatic && $5 != NULL)
|
|
yyerror("%AccessCode cannot be specified for non-static class variables");
|
|
}
|
|
|
|
if (currentIsStatic && currentSpec -> genc)
|
|
yyerror("Cannot have static members in a C structure");
|
|
|
|
applyTypeFlags(currentModule, &$1, &$3);
|
|
|
|
if ($6 != NULL || $7 != NULL)
|
|
{
|
|
if ($5 != NULL)
|
|
yyerror("Cannot mix %AccessCode and %GetCode or %SetCode");
|
|
|
|
if (currentScope() == NULL)
|
|
yyerror("Cannot specify %GetCode or %SetCode for global variables");
|
|
}
|
|
|
|
newVar(currentSpec,currentModule,$2,currentIsStatic,&$1,&$3,$5,$6,$7);
|
|
}
|
|
|
|
currentIsStatic = FALSE;
|
|
}
|
|
;
|
|
|
|
cpptype: TK_CONST basetype deref optref {
|
|
$$ = $2;
|
|
$$.nrderefs += $3;
|
|
$$.argflags |= ARG_IS_CONST | $4;
|
|
}
|
|
| basetype deref optref {
|
|
$$ = $1;
|
|
$$.nrderefs += $2;
|
|
$$.argflags |= $3;
|
|
}
|
|
;
|
|
|
|
argtype: cpptype optname optflags {
|
|
$$ = $1;
|
|
$$.name = cacheName(currentSpec, $2);
|
|
|
|
if (getAllowNone(&$3))
|
|
$$.argflags |= ARG_ALLOW_NONE;
|
|
|
|
if (findOptFlag(&$3,"GetWrapper",bool_flag) != NULL)
|
|
$$.argflags |= ARG_GET_WRAPPER;
|
|
|
|
if (findOptFlag(&$3,"Array",bool_flag) != NULL)
|
|
$$.argflags |= ARG_ARRAY;
|
|
|
|
if (findOptFlag(&$3,"ArraySize",bool_flag) != NULL)
|
|
$$.argflags |= ARG_ARRAY_SIZE;
|
|
|
|
if (getTransfer(&$3))
|
|
$$.argflags |= ARG_XFERRED;
|
|
|
|
if (findOptFlag(&$3,"TransferThis",bool_flag) != NULL)
|
|
$$.argflags |= ARG_THIS_XFERRED;
|
|
|
|
if (findOptFlag(&$3,"TransferBack",bool_flag) != NULL)
|
|
$$.argflags |= ARG_XFERRED_BACK;
|
|
|
|
if (findOptFlag(&$3, "KeepReference", bool_flag) != NULL)
|
|
{
|
|
$$.argflags |= ARG_KEEP_REF;
|
|
$$.key = currentModule->next_key++;
|
|
}
|
|
|
|
if (findOptFlag(&$3,"In",bool_flag) != NULL)
|
|
$$.argflags |= ARG_IN;
|
|
|
|
if (findOptFlag(&$3,"Out",bool_flag) != NULL)
|
|
$$.argflags |= ARG_OUT;
|
|
|
|
if (findOptFlag(&$3, "ResultSize", bool_flag) != NULL)
|
|
$$.argflags |= ARG_RESULT_SIZE;
|
|
|
|
if (findOptFlag(&$3, "NoCopy", bool_flag) != NULL)
|
|
$$.argflags |= ARG_NO_COPY;
|
|
|
|
if (findOptFlag(&$3,"Constrained",bool_flag) != NULL)
|
|
{
|
|
$$.argflags |= ARG_CONSTRAINED;
|
|
|
|
switch ($$.atype)
|
|
{
|
|
case bool_type:
|
|
$$.atype = cbool_type;
|
|
break;
|
|
|
|
case int_type:
|
|
$$.atype = cint_type;
|
|
break;
|
|
|
|
case float_type:
|
|
$$.atype = cfloat_type;
|
|
break;
|
|
|
|
case double_type:
|
|
$$.atype = cdouble_type;
|
|
break;
|
|
}
|
|
}
|
|
|
|
applyTypeFlags(currentModule, &$$, &$3);
|
|
$$.docval = getDocValue(&$3);
|
|
}
|
|
;
|
|
|
|
optref: {
|
|
$$ = 0;
|
|
}
|
|
| '&' {
|
|
if (currentSpec -> genc)
|
|
yyerror("References not allowed in a C module");
|
|
|
|
$$ = ARG_IS_REF;
|
|
}
|
|
;
|
|
|
|
deref: {
|
|
$$ = 0;
|
|
}
|
|
| deref '*' {
|
|
$$ = $1 + 1;
|
|
}
|
|
;
|
|
|
|
basetype: scopedname {
|
|
memset(&$$, 0, sizeof (argDef));
|
|
$$.atype = defined_type;
|
|
$$.u.snd = $1;
|
|
|
|
/* Try and resolve typedefs as early as possible. */
|
|
resolveAnyTypedef(currentSpec, &$$);
|
|
}
|
|
| scopedname '<' cpptypelist '>' {
|
|
templateDef *td;
|
|
|
|
td = sipMalloc(sizeof(templateDef));
|
|
td->fqname = $1;
|
|
td->types = $3;
|
|
|
|
memset(&$$, 0, sizeof (argDef));
|
|
$$.atype = template_type;
|
|
$$.u.td = td;
|
|
}
|
|
| TK_STRUCT scopedname {
|
|
memset(&$$, 0, sizeof (argDef));
|
|
|
|
/* In a C module all structures must be defined. */
|
|
if (currentSpec -> genc)
|
|
{
|
|
$$.atype = defined_type;
|
|
$$.u.snd = $2;
|
|
}
|
|
else
|
|
{
|
|
$$.atype = struct_type;
|
|
$$.u.sname = $2;
|
|
}
|
|
}
|
|
| TK_UNSIGNED TK_SHORT {
|
|
memset(&$$, 0, sizeof (argDef));
|
|
$$.atype = ushort_type;
|
|
}
|
|
| TK_SHORT {
|
|
memset(&$$, 0, sizeof (argDef));
|
|
$$.atype = short_type;
|
|
}
|
|
| TK_UNSIGNED {
|
|
memset(&$$, 0, sizeof (argDef));
|
|
$$.atype = uint_type;
|
|
}
|
|
| TK_UNSIGNED TK_INT {
|
|
memset(&$$, 0, sizeof (argDef));
|
|
$$.atype = uint_type;
|
|
}
|
|
| TK_INT {
|
|
memset(&$$, 0, sizeof (argDef));
|
|
$$.atype = int_type;
|
|
}
|
|
| TK_LONG {
|
|
memset(&$$, 0, sizeof (argDef));
|
|
$$.atype = long_type;
|
|
}
|
|
| TK_UNSIGNED TK_LONG {
|
|
memset(&$$, 0, sizeof (argDef));
|
|
$$.atype = ulong_type;
|
|
}
|
|
| TK_LONG TK_LONG {
|
|
memset(&$$, 0, sizeof (argDef));
|
|
$$.atype = longlong_type;
|
|
}
|
|
| TK_UNSIGNED TK_LONG TK_LONG {
|
|
memset(&$$, 0, sizeof (argDef));
|
|
$$.atype = ulonglong_type;
|
|
}
|
|
| TK_FLOAT {
|
|
memset(&$$, 0, sizeof (argDef));
|
|
$$.atype = float_type;
|
|
}
|
|
| TK_DOUBLE {
|
|
memset(&$$, 0, sizeof (argDef));
|
|
$$.atype = double_type;
|
|
}
|
|
| TK_BOOL {
|
|
memset(&$$, 0, sizeof (argDef));
|
|
$$.atype = bool_type;
|
|
}
|
|
| TK_SIGNED TK_CHAR {
|
|
memset(&$$, 0, sizeof (argDef));
|
|
$$.atype = sstring_type;
|
|
}
|
|
| TK_UNSIGNED TK_CHAR {
|
|
memset(&$$, 0, sizeof (argDef));
|
|
$$.atype = ustring_type;
|
|
}
|
|
| TK_CHAR {
|
|
memset(&$$, 0, sizeof (argDef));
|
|
$$.atype = string_type;
|
|
}
|
|
| TK_WCHAR_T {
|
|
memset(&$$, 0, sizeof (argDef));
|
|
$$.atype = wstring_type;
|
|
}
|
|
| TK_VOID {
|
|
memset(&$$, 0, sizeof (argDef));
|
|
$$.atype = void_type;
|
|
}
|
|
| TK_PYOBJECT {
|
|
memset(&$$, 0, sizeof (argDef));
|
|
$$.atype = pyobject_type;
|
|
}
|
|
| TK_PYTUPLE {
|
|
memset(&$$, 0, sizeof (argDef));
|
|
$$.atype = pytuple_type;
|
|
}
|
|
| TK_PYLIST {
|
|
memset(&$$, 0, sizeof (argDef));
|
|
$$.atype = pylist_type;
|
|
}
|
|
| TK_PYDICT {
|
|
memset(&$$, 0, sizeof (argDef));
|
|
$$.atype = pydict_type;
|
|
}
|
|
| TK_PYCALLABLE {
|
|
memset(&$$, 0, sizeof (argDef));
|
|
$$.atype = pycallable_type;
|
|
}
|
|
| TK_PYSLICE {
|
|
memset(&$$, 0, sizeof (argDef));
|
|
$$.atype = pyslice_type;
|
|
}
|
|
| TK_PYTYPE {
|
|
memset(&$$, 0, sizeof (argDef));
|
|
$$.atype = pytype_type;
|
|
}
|
|
| TK_ELLIPSIS {
|
|
memset(&$$, 0, sizeof (argDef));
|
|
$$.atype = ellipsis_type;
|
|
}
|
|
;
|
|
|
|
cpptypelist: cpptype {
|
|
/* The single or first type. */
|
|
|
|
$$.args[0] = $1;
|
|
$$.nrArgs = 1;
|
|
}
|
|
| cpptypelist ',' cpptype {
|
|
/* Check there is nothing after an ellipsis. */
|
|
if ($1.args[$1.nrArgs - 1].atype == ellipsis_type)
|
|
yyerror("An ellipsis must be at the end of the argument list");
|
|
|
|
/* Check there is room. */
|
|
if ($1.nrArgs == MAX_NR_ARGS)
|
|
yyerror("Internal error - increase the value of MAX_NR_ARGS");
|
|
|
|
$$ = $1;
|
|
|
|
$$.args[$$.nrArgs] = $3;
|
|
$$.nrArgs++;
|
|
}
|
|
;
|
|
|
|
optexceptions: {
|
|
$$ = NULL;
|
|
}
|
|
| TK_THROW '(' exceptionlist ')' {
|
|
if (currentSpec->genc)
|
|
yyerror("Exceptions not allowed in a C module");
|
|
|
|
$$ = $3;
|
|
}
|
|
;
|
|
|
|
exceptionlist: {
|
|
/* Empty list so use a blank. */
|
|
|
|
$$ = sipMalloc(sizeof (throwArgs));
|
|
$$ -> nrArgs = 0;
|
|
}
|
|
| scopedname {
|
|
/* The only or first exception. */
|
|
|
|
$$ = sipMalloc(sizeof (throwArgs));
|
|
$$ -> nrArgs = 1;
|
|
$$ -> args[0] = findException(currentSpec, $1, FALSE);
|
|
}
|
|
| exceptionlist ',' scopedname {
|
|
/* Check that it wasn't ...(,arg...). */
|
|
|
|
if ($1 -> nrArgs == 0)
|
|
yyerror("First exception of throw specifier is missing");
|
|
|
|
/* Check there is room. */
|
|
|
|
if ($1 -> nrArgs == MAX_NR_ARGS)
|
|
yyerror("Internal error - increase the value of MAX_NR_ARGS");
|
|
|
|
$$ = $1;
|
|
$$ -> args[$$ -> nrArgs++] = findException(currentSpec, $3, FALSE);
|
|
}
|
|
;
|
|
|
|
%%
|
|
|
|
|
|
/*
|
|
* Parse the specification.
|
|
*/
|
|
void parse(sipSpec *spec, FILE *fp, char *filename, stringList *tsl,
|
|
stringList *xfl, int kwdArgs, int protHack)
|
|
{
|
|
classTmplDef *tcd;
|
|
|
|
/* Initialise the spec. */
|
|
|
|
spec->modules = NULL;
|
|
spec->namecache = NULL;
|
|
spec->ifacefiles = NULL;
|
|
spec->classes = NULL;
|
|
spec->classtemplates = NULL;
|
|
spec->exceptions = NULL;
|
|
spec->mappedtypes = NULL;
|
|
spec->mappedtypetemplates = NULL;
|
|
spec->enums = NULL;
|
|
spec->vars = NULL;
|
|
spec->typedefs = NULL;
|
|
spec->exphdrcode = NULL;
|
|
spec->docs = NULL;
|
|
spec->sigslots = FALSE;
|
|
spec->genc = -1;
|
|
spec->plugins = NULL;
|
|
|
|
currentSpec = spec;
|
|
neededQualifiers = tsl;
|
|
excludedQualifiers = xfl;
|
|
currentModule = NULL;
|
|
currentMappedType = NULL;
|
|
currentOverIsVirt = FALSE;
|
|
currentCtorIsExplicit = FALSE;
|
|
currentIsStatic = FALSE;
|
|
currentIsSignal = FALSE;
|
|
currentIsSlot = FALSE;
|
|
currentIsTemplate = FALSE;
|
|
previousFile = NULL;
|
|
skipStackPtr = 0;
|
|
currentScopeIdx = 0;
|
|
sectionFlags = 0;
|
|
defaultKwdArgs = kwdArgs;
|
|
makeProtPublic = protHack;
|
|
|
|
newModule(fp, filename);
|
|
spec->module = currentModule;
|
|
|
|
yyparse();
|
|
|
|
handleEOF();
|
|
handleEOM();
|
|
|
|
/*
|
|
* Go through each template class and remove it from the list of classes.
|
|
*/
|
|
for (tcd = spec->classtemplates; tcd != NULL; tcd = tcd->next)
|
|
{
|
|
classDef **cdp;
|
|
|
|
for (cdp = &spec->classes; *cdp != NULL; cdp = &(*cdp)->next)
|
|
if (*cdp == tcd->cd)
|
|
{
|
|
ifaceFileDef **ifdp;
|
|
|
|
/* Remove the interface file as well. */
|
|
for (ifdp = &spec->ifacefiles; *ifdp != NULL; ifdp = &(*ifdp)->next)
|
|
if (*ifdp == tcd->cd->iff)
|
|
{
|
|
*ifdp = (*ifdp)->next;
|
|
break;
|
|
}
|
|
|
|
*cdp = (*cdp)->next;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Tell the parser that a complete file has now been read.
|
|
*/
|
|
void parserEOF(char *name, parserContext *pc)
|
|
{
|
|
previousFile = sipStrdup(name);
|
|
currentContext = *pc;
|
|
}
|
|
|
|
|
|
/*
|
|
* Append a class definition to a class list if it doesn't already appear.
|
|
* Append is needed specifically for the list of super-classes because the
|
|
* order is important to Python.
|
|
*/
|
|
void appendToClassList(classList **clp,classDef *cd)
|
|
{
|
|
classList *new;
|
|
|
|
/* Find the end of the list. */
|
|
|
|
while (*clp != NULL)
|
|
{
|
|
if ((*clp) -> cd == cd)
|
|
return;
|
|
|
|
clp = &(*clp) -> next;
|
|
}
|
|
|
|
new = sipMalloc(sizeof (classList));
|
|
|
|
new -> cd = cd;
|
|
new -> next = NULL;
|
|
|
|
*clp = new;
|
|
}
|
|
|
|
|
|
/*
|
|
* Create a new module for the current specification and make it current.
|
|
*/
|
|
static void newModule(FILE *fp, char *filename)
|
|
{
|
|
moduleDef *mod;
|
|
|
|
parseFile(fp, filename, currentModule, FALSE);
|
|
|
|
mod = allocModule();
|
|
mod->file = filename;
|
|
|
|
if (currentModule != NULL)
|
|
mod->defexception = currentModule->defexception;
|
|
|
|
currentModule = mod;
|
|
}
|
|
|
|
|
|
/*
|
|
* Allocate and initialise the memory for a new module.
|
|
*/
|
|
static moduleDef *allocModule()
|
|
{
|
|
moduleDef *newmod, **tailp;
|
|
|
|
newmod = sipMalloc(sizeof (moduleDef));
|
|
|
|
newmod->version = -1;
|
|
newmod->encoding = no_type;
|
|
newmod->qobjclass = -1;
|
|
newmod->nrvirthandlers = -1;
|
|
newmod->next_key = 1;
|
|
|
|
/*
|
|
* The consolidated module support needs these to be in order that they
|
|
* appeared.
|
|
*/
|
|
for (tailp = ¤tSpec->modules; *tailp != NULL; tailp = &(*tailp)->next)
|
|
;
|
|
|
|
*tailp = newmod;
|
|
|
|
return newmod;
|
|
}
|
|
|
|
|
|
/*
|
|
* Switch to parsing a new file.
|
|
*/
|
|
static void parseFile(FILE *fp, char *name, moduleDef *prevmod, int optional)
|
|
{
|
|
parserContext pc;
|
|
|
|
pc.filename = name;
|
|
pc.ifdepth = skipStackPtr;
|
|
pc.prevmod = prevmod;
|
|
|
|
if (setInputFile(fp, &pc, optional))
|
|
currentContext = pc;
|
|
}
|
|
|
|
|
|
/*
|
|
* Find an interface file, or create a new one.
|
|
*/
|
|
ifaceFileDef *findIfaceFile(sipSpec *pt, moduleDef *mod, scopedNameDef *fqname,
|
|
ifaceFileType iftype, apiVersionRangeDef *api_range, argDef *ad)
|
|
{
|
|
ifaceFileDef *iff, *first_alt = NULL;
|
|
|
|
/* See if the name is already used. */
|
|
|
|
for (iff = pt->ifacefiles; iff != NULL; iff = iff->next)
|
|
{
|
|
if (compareScopedNames(iff->fqcname, fqname) != 0)
|
|
continue;
|
|
|
|
/*
|
|
* If they are both versioned then assume the user knows what they are
|
|
* doing.
|
|
*/
|
|
if (iff->api_range != NULL && api_range != NULL && iff->module == mod)
|
|
{
|
|
/* Remember the first of the alternate APIs. */
|
|
if ((first_alt = iff->first_alt) == NULL)
|
|
first_alt = iff;
|
|
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* They must be the same type except that we allow a class if we want
|
|
* an exception. This is because we allow classes to be used before
|
|
* they are defined.
|
|
*/
|
|
if (iff->type != iftype)
|
|
if (iftype != exception_iface || iff->type != class_iface)
|
|
yyerror("A class, exception, namespace or mapped type has already been defined with the same name");
|
|
|
|
/* Ignore an external class declared in another module. */
|
|
if (iftype == class_iface && iff->module != mod)
|
|
{
|
|
classDef *cd;
|
|
|
|
for (cd = pt->classes; cd != NULL; cd = cd->next)
|
|
if (cd->iff == iff)
|
|
break;
|
|
|
|
if (cd != NULL && iff->module != NULL && isExternal(cd))
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* If this is a mapped type with the same name defined in a different
|
|
* module, then check that this type isn't the same as any of the
|
|
* mapped types defined in that module.
|
|
*/
|
|
if (iftype == mappedtype_iface && iff->module != mod)
|
|
{
|
|
mappedTypeDef *mtd;
|
|
|
|
/*
|
|
* This is a bit of a cheat. With consolidated modules it's
|
|
* possible to have two implementations of a mapped type in
|
|
* different branches of the module hierarchy. We assume that, if
|
|
* there really are multiple implementations in the same branch,
|
|
* then it will be picked up in a non-consolidated build.
|
|
*/
|
|
if (isConsolidated(pt->module))
|
|
continue;
|
|
|
|
for (mtd = pt->mappedtypes; mtd != NULL; mtd = mtd->next)
|
|
{
|
|
if (mtd->iff != iff)
|
|
continue;
|
|
|
|
if (ad->atype != template_type ||
|
|
mtd->type.atype != template_type ||
|
|
sameBaseType(ad, &mtd->type))
|
|
yyerror("Mapped type has already been defined in another module");
|
|
}
|
|
|
|
/*
|
|
* If we got here then we have a mapped type based on an existing
|
|
* template, but with unique parameters. We don't want to use
|
|
* interface files from other modules, so skip this one.
|
|
*/
|
|
|
|
continue;
|
|
}
|
|
|
|
/* Ignore a namespace defined in another module. */
|
|
if (iftype == namespace_iface && iff->module != mod)
|
|
continue;
|
|
|
|
return iff;
|
|
}
|
|
|
|
iff = sipMalloc(sizeof (ifaceFileDef));
|
|
|
|
iff->name = cacheName(pt, scopedNameToString(fqname));
|
|
iff->api_range = api_range;
|
|
|
|
if (first_alt != NULL)
|
|
{
|
|
iff->first_alt = first_alt;
|
|
iff->next_alt = first_alt->next_alt;
|
|
|
|
first_alt->next_alt = iff;
|
|
}
|
|
else
|
|
{
|
|
/* This is the first alternate so point to itself. */
|
|
iff->first_alt = iff;
|
|
}
|
|
|
|
iff->type = iftype;
|
|
iff->ifacenr = -1;
|
|
iff->fqcname = fqname;
|
|
iff->module = NULL;
|
|
iff->hdrcode = NULL;
|
|
iff->used = NULL;
|
|
iff->next = pt->ifacefiles;
|
|
|
|
pt->ifacefiles = iff;
|
|
|
|
return iff;
|
|
}
|
|
|
|
|
|
/*
|
|
* Find a class definition in a parse tree.
|
|
*/
|
|
static classDef *findClass(sipSpec *pt, ifaceFileType iftype,
|
|
apiVersionRangeDef *api_range, scopedNameDef *fqname)
|
|
{
|
|
return findClassWithInterface(pt, findIfaceFile(pt, currentModule, fqname, iftype, api_range, NULL));
|
|
}
|
|
|
|
|
|
/*
|
|
* Find a class definition given an existing interface file.
|
|
*/
|
|
static classDef *findClassWithInterface(sipSpec *pt, ifaceFileDef *iff)
|
|
{
|
|
classDef *cd;
|
|
|
|
for (cd = pt -> classes; cd != NULL; cd = cd -> next)
|
|
if (cd -> iff == iff)
|
|
return cd;
|
|
|
|
/* Create a new one. */
|
|
cd = sipMalloc(sizeof (classDef));
|
|
|
|
cd->iff = iff;
|
|
cd->pyname = cacheName(pt, classBaseName(cd));
|
|
cd->next = pt->classes;
|
|
|
|
pt->classes = cd;
|
|
|
|
return cd;
|
|
}
|
|
|
|
|
|
/*
|
|
* Add an interface file to an interface file list if it isn't already there.
|
|
*/
|
|
void addToUsedList(ifaceFileList **ifflp, ifaceFileDef *iff)
|
|
{
|
|
/* Make sure we don't try to add an interface file to its own list. */
|
|
if (&iff->used != ifflp)
|
|
{
|
|
ifaceFileList *iffl;
|
|
|
|
while ((iffl = *ifflp) != NULL)
|
|
{
|
|
/* Don't bother if it is already there. */
|
|
if (iffl->iff == iff)
|
|
return;
|
|
|
|
ifflp = &iffl -> next;
|
|
}
|
|
|
|
iffl = sipMalloc(sizeof (ifaceFileList));
|
|
|
|
iffl->iff = iff;
|
|
iffl->next = NULL;
|
|
|
|
*ifflp = iffl;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Find an undefined (or create a new) exception definition in a parse tree.
|
|
*/
|
|
static exceptionDef *findException(sipSpec *pt, scopedNameDef *fqname, int new)
|
|
{
|
|
exceptionDef *xd, **tail;
|
|
ifaceFileDef *iff;
|
|
classDef *cd;
|
|
|
|
iff = findIfaceFile(pt, currentModule, fqname, exception_iface, NULL, NULL);
|
|
|
|
/* See if it is an existing one. */
|
|
for (xd = pt->exceptions; xd != NULL; xd = xd->next)
|
|
if (xd->iff == iff)
|
|
return xd;
|
|
|
|
/*
|
|
* If it is an exception interface file then we have never seen this
|
|
* name before. We require that exceptions are defined before being
|
|
* used, but don't make the same requirement of classes (for reasons of
|
|
* backwards compatibility). Therefore the name must be reinterpreted
|
|
* as a (as yet undefined) class.
|
|
*/
|
|
if (new)
|
|
{
|
|
if (iff->type == exception_iface)
|
|
cd = NULL;
|
|
else
|
|
yyerror("There is already a class with the same name or the exception has been used before being defined");
|
|
}
|
|
else
|
|
{
|
|
if (iff->type == exception_iface)
|
|
iff->type = class_iface;
|
|
|
|
cd = findClassWithInterface(pt, iff);
|
|
}
|
|
|
|
/* Create a new one. */
|
|
xd = sipMalloc(sizeof (exceptionDef));
|
|
|
|
xd->exceptionnr = -1;
|
|
xd->iff = iff;
|
|
xd->pyname = NULL;
|
|
xd->cd = cd;
|
|
xd->bibase = NULL;
|
|
xd->base = NULL;
|
|
xd->raisecode = NULL;
|
|
xd->next = NULL;
|
|
|
|
/* Append it to the list. */
|
|
for (tail = &pt->exceptions; *tail != NULL; tail = &(*tail)->next)
|
|
;
|
|
|
|
*tail = xd;
|
|
|
|
return xd;
|
|
}
|
|
|
|
|
|
/*
|
|
* Find an undefined (or create a new) class definition in a parse tree.
|
|
*/
|
|
static classDef *newClass(sipSpec *pt, ifaceFileType iftype,
|
|
apiVersionRangeDef *api_range, scopedNameDef *fqname)
|
|
{
|
|
int flags;
|
|
classDef *cd, *scope;
|
|
codeBlock *hdrcode;
|
|
|
|
if (sectionFlags & SECT_IS_PRIVATE)
|
|
yyerror("Classes, structs and namespaces must be in the public or protected sections");
|
|
|
|
flags = 0;
|
|
|
|
if ((scope = currentScope()) != NULL)
|
|
{
|
|
if (sectionFlags & SECT_IS_PROT && !makeProtPublic)
|
|
{
|
|
flags = CLASS_IS_PROTECTED;
|
|
|
|
if (scope->iff->type == class_iface)
|
|
setHasShadow(scope);
|
|
}
|
|
|
|
/* Header code from outer scopes is also included. */
|
|
hdrcode = scope->iff->hdrcode;
|
|
}
|
|
else
|
|
hdrcode = NULL;
|
|
|
|
if (pt -> genc)
|
|
{
|
|
/* C structs are always global types. */
|
|
while (fqname -> next != NULL)
|
|
fqname = fqname -> next;
|
|
|
|
scope = NULL;
|
|
}
|
|
|
|
cd = findClass(pt, iftype, api_range, fqname);
|
|
|
|
/* Check it hasn't already been defined. */
|
|
if (iftype != namespace_iface && cd->iff->module != NULL)
|
|
yyerror("The struct/class has already been defined");
|
|
|
|
/* Complete the initialisation. */
|
|
cd->classflags |= flags;
|
|
cd->ecd = scope;
|
|
cd->iff->module = currentModule;
|
|
|
|
if (currentIsTemplate)
|
|
setIsTemplateClass(cd);
|
|
|
|
appendCodeBlock(&cd->iff->hdrcode, hdrcode);
|
|
|
|
/* See if it is a namespace extender. */
|
|
if (iftype == namespace_iface)
|
|
{
|
|
classDef *ns;
|
|
|
|
for (ns = pt->classes; ns != NULL; ns = ns->next)
|
|
{
|
|
if (ns == cd)
|
|
continue;
|
|
|
|
if (ns->iff->type != namespace_iface)
|
|
continue;
|
|
|
|
if (compareScopedNames(ns->iff->fqcname, fqname) != 0)
|
|
continue;
|
|
|
|
cd->real = ns;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return cd;
|
|
}
|
|
|
|
|
|
/*
|
|
* Tidy up after finishing a class definition.
|
|
*/
|
|
static void finishClass(sipSpec *pt, moduleDef *mod, classDef *cd,
|
|
optFlags *of)
|
|
{
|
|
const char *pyname;
|
|
optFlag *flg;
|
|
|
|
/* Get the Python name and see if it is different to the C++ name. */
|
|
pyname = getPythonName(of, classBaseName(cd));
|
|
|
|
cd->pyname = NULL;
|
|
checkAttributes(pt, mod, cd->ecd, NULL, pyname, FALSE);
|
|
cd->pyname = cacheName(pt, pyname);
|
|
|
|
if ((flg = findOptFlag(of, "Metatype", dotted_name_flag)) != NULL)
|
|
cd->metatype = cacheName(pt, flg->fvalue.sval);
|
|
|
|
if ((flg = findOptFlag(of, "Supertype", dotted_name_flag)) != NULL)
|
|
cd->supertype = cacheName(pt, flg->fvalue.sval);
|
|
|
|
if ((flg = findOptFlag(of, "PyQt4Flags", integer_flag)) != NULL)
|
|
cd->pyqt4_flags = flg->fvalue.ival;
|
|
|
|
if (findOptFlag(of, "PyQt4NoQMetaObject", bool_flag) != NULL)
|
|
setPyQt4NoTQMetaObject(cd);
|
|
|
|
if (isOpaque(cd))
|
|
{
|
|
if (findOptFlag(of, "External", bool_flag) != NULL)
|
|
setIsExternal(cd);
|
|
}
|
|
else
|
|
{
|
|
int seq_might, seq_not;
|
|
memberDef *md;
|
|
|
|
if (findOptFlag(of, "NoDefaultCtors", bool_flag) != NULL)
|
|
setNoDefaultCtors(cd);
|
|
|
|
if (cd -> ctors == NULL)
|
|
{
|
|
if (!noDefaultCtors(cd))
|
|
{
|
|
/* Provide a default ctor. */
|
|
|
|
cd->ctors = sipMalloc(sizeof (ctorDef));
|
|
|
|
cd->ctors->ctorflags = SECT_IS_PUBLIC;
|
|
cd->ctors->pysig.result.atype = void_type;
|
|
cd->ctors->cppsig = &cd->ctors->pysig;
|
|
|
|
cd->defctor = cd->ctors;
|
|
|
|
setCanCreate(cd);
|
|
}
|
|
}
|
|
else if (cd -> defctor == NULL)
|
|
{
|
|
ctorDef *ct, *last = NULL;
|
|
|
|
for (ct = cd -> ctors; ct != NULL; ct = ct -> next)
|
|
{
|
|
if (!isPublicCtor(ct))
|
|
continue;
|
|
|
|
if (ct -> pysig.nrArgs == 0 || ct -> pysig.args[0].defval != NULL)
|
|
{
|
|
cd -> defctor = ct;
|
|
break;
|
|
}
|
|
|
|
if (last == NULL)
|
|
last = ct;
|
|
}
|
|
|
|
/* The last resort is the first public ctor. */
|
|
if (cd->defctor == NULL)
|
|
cd->defctor = last;
|
|
}
|
|
|
|
if (getDeprecated(of))
|
|
setIsDeprecatedClass(cd);
|
|
|
|
if (cd->convtocode != NULL && getAllowNone(of))
|
|
setClassHandlesNone(cd);
|
|
|
|
if (findOptFlag(of,"Abstract",bool_flag) != NULL)
|
|
{
|
|
setIsAbstractClass(cd);
|
|
setIsIncomplete(cd);
|
|
resetCanCreate(cd);
|
|
}
|
|
|
|
/* We assume a public dtor if nothing specific was provided. */
|
|
if (!isDtor(cd))
|
|
setIsPublicDtor(cd);
|
|
|
|
if (findOptFlag(of, "DelayDtor", bool_flag) != NULL)
|
|
{
|
|
setIsDelayedDtor(cd);
|
|
setHasDelayedDtors(mod);
|
|
}
|
|
|
|
/*
|
|
* There are subtle differences between the add and concat methods and
|
|
* the multiply and repeat methods. The number versions can have their
|
|
* operands swapped and may return NotImplemented. If the user has
|
|
* used the /Numeric/ annotation or there are other numeric operators
|
|
* then we use add/multiply. Otherwise, if there are indexing
|
|
* operators then we use concat/repeat.
|
|
*/
|
|
seq_might = seq_not = FALSE;
|
|
|
|
for (md = cd -> members; md != NULL; md = md -> next)
|
|
switch (md -> slot)
|
|
{
|
|
case getitem_slot:
|
|
case setitem_slot:
|
|
case delitem_slot:
|
|
/* This might be a sequence. */
|
|
seq_might = TRUE;
|
|
break;
|
|
|
|
case sub_slot:
|
|
case isub_slot:
|
|
case div_slot:
|
|
case idiv_slot:
|
|
case mod_slot:
|
|
case imod_slot:
|
|
case floordiv_slot:
|
|
case ifloordiv_slot:
|
|
case truediv_slot:
|
|
case itruediv_slot:
|
|
case pos_slot:
|
|
case neg_slot:
|
|
/* This is definately not a sequence. */
|
|
seq_not = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (!seq_not && seq_might)
|
|
for (md = cd -> members; md != NULL; md = md -> next)
|
|
{
|
|
/* Ignore if the user has been explicit. */
|
|
if (isNumeric(md))
|
|
continue;
|
|
|
|
switch (md -> slot)
|
|
{
|
|
case add_slot:
|
|
md -> slot = concat_slot;
|
|
break;
|
|
|
|
case iadd_slot:
|
|
md -> slot = iconcat_slot;
|
|
break;
|
|
|
|
case mul_slot:
|
|
md -> slot = repeat_slot;
|
|
break;
|
|
|
|
case imul_slot:
|
|
md -> slot = irepeat_slot;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (inMainModule())
|
|
{
|
|
setIsUsedName(cd->iff->name);
|
|
setIsUsedName(cd->pyname);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Return the encoded name of a template (ie. including its argument types) as
|
|
* a scoped name.
|
|
*/
|
|
scopedNameDef *encodedTemplateName(templateDef *td)
|
|
{
|
|
int a;
|
|
scopedNameDef *snd;
|
|
|
|
snd = copyScopedName(td->fqname);
|
|
|
|
for (a = 0; a < td->types.nrArgs; ++a)
|
|
{
|
|
char buf[50];
|
|
int flgs;
|
|
scopedNameDef *arg_snd;
|
|
argDef *ad = &td->types.args[a];
|
|
|
|
flgs = 0;
|
|
|
|
if (isConstArg(ad))
|
|
flgs += 1;
|
|
|
|
if (isReference(ad))
|
|
flgs += 2;
|
|
|
|
/* We use numbers so they don't conflict with names. */
|
|
sprintf(buf, "%02d%d%d", ad->atype, flgs, ad->nrderefs);
|
|
|
|
switch (ad->atype)
|
|
{
|
|
case defined_type:
|
|
arg_snd = copyScopedName(ad->u.snd);
|
|
break;
|
|
|
|
case template_type:
|
|
arg_snd = encodedTemplateName(ad->u.td);
|
|
break;
|
|
|
|
case struct_type:
|
|
arg_snd = copyScopedName(ad->u.sname);
|
|
break;
|
|
|
|
default:
|
|
arg_snd = NULL;
|
|
}
|
|
|
|
/*
|
|
* Replace the first element of the argument name with a copy with the
|
|
* encoding prepended.
|
|
*/
|
|
if (arg_snd != NULL)
|
|
arg_snd->name = concat(buf, arg_snd->name, NULL);
|
|
else
|
|
arg_snd = text2scopePart(sipStrdup(buf));
|
|
|
|
appendScopedName(&snd, arg_snd);
|
|
}
|
|
|
|
return snd;
|
|
}
|
|
|
|
|
|
/*
|
|
* Create a new mapped type.
|
|
*/
|
|
static mappedTypeDef *newMappedType(sipSpec *pt, argDef *ad, optFlags *of)
|
|
{
|
|
mappedTypeDef *mtd;
|
|
scopedNameDef *snd;
|
|
ifaceFileDef *iff;
|
|
const char *cname;
|
|
|
|
/* Check that the type is one we want to map. */
|
|
switch (ad->atype)
|
|
{
|
|
case defined_type:
|
|
snd = ad->u.snd;
|
|
cname = scopedNameTail(snd);
|
|
break;
|
|
|
|
case template_type:
|
|
snd = encodedTemplateName(ad->u.td);
|
|
cname = NULL;
|
|
break;
|
|
|
|
case struct_type:
|
|
snd = ad->u.sname;
|
|
cname = scopedNameTail(snd);
|
|
break;
|
|
|
|
default:
|
|
yyerror("Invalid type for %MappedType");
|
|
}
|
|
|
|
iff = findIfaceFile(pt, currentModule, snd, mappedtype_iface,
|
|
getAPIRange(of), ad);
|
|
|
|
/* Check it hasn't already been defined. */
|
|
for (mtd = pt->mappedtypes; mtd != NULL; mtd = mtd->next)
|
|
if (mtd->iff == iff)
|
|
{
|
|
/*
|
|
* We allow types based on the same template but with different
|
|
* arguments.
|
|
*/
|
|
if (ad->atype != template_type || sameBaseType(ad, &mtd->type))
|
|
yyerror("Mapped type has already been defined in this module");
|
|
}
|
|
|
|
/* The module may not have been set yet. */
|
|
iff->module = currentModule;
|
|
|
|
/* Create a new mapped type. */
|
|
mtd = allocMappedType(pt, ad);
|
|
|
|
if (cname != NULL)
|
|
mtd->pyname = cacheName(pt, getPythonName(of, cname));
|
|
|
|
if (findOptFlag(of, "NoRelease", bool_flag) != NULL)
|
|
setNoRelease(mtd);
|
|
|
|
if (getAllowNone(of))
|
|
setHandlesNone(mtd);
|
|
|
|
mtd->doctype = getDocType(of);
|
|
|
|
mtd->iff = iff;
|
|
mtd->next = pt->mappedtypes;
|
|
|
|
pt->mappedtypes = mtd;
|
|
|
|
if (inMainModule())
|
|
{
|
|
setIsUsedName(mtd->cname);
|
|
|
|
if (mtd->pyname)
|
|
setIsUsedName(mtd->pyname);
|
|
}
|
|
|
|
return mtd;
|
|
}
|
|
|
|
|
|
/*
|
|
* Allocate, intialise and return a mapped type structure.
|
|
*/
|
|
mappedTypeDef *allocMappedType(sipSpec *pt, argDef *type)
|
|
{
|
|
mappedTypeDef *mtd;
|
|
|
|
mtd = sipMalloc(sizeof (mappedTypeDef));
|
|
|
|
mtd->type = *type;
|
|
mtd->type.argflags = 0;
|
|
mtd->type.nrderefs = 0;
|
|
|
|
mtd->cname = cacheName(pt, type2string(&mtd->type));
|
|
|
|
return mtd;
|
|
}
|
|
|
|
|
|
/*
|
|
* Create a new enum.
|
|
*/
|
|
static enumDef *newEnum(sipSpec *pt, moduleDef *mod, mappedTypeDef *mt_scope,
|
|
char *name, optFlags *of, int flags)
|
|
{
|
|
enumDef *ed, *first_alt, *next_alt;
|
|
classDef *c_scope;
|
|
ifaceFileDef *scope;
|
|
|
|
if (mt_scope != NULL)
|
|
{
|
|
scope = mt_scope->iff;
|
|
c_scope = NULL;
|
|
}
|
|
else
|
|
{
|
|
if ((c_scope = currentScope()) != NULL)
|
|
scope = c_scope->iff;
|
|
else
|
|
scope = NULL;
|
|
}
|
|
|
|
ed = sipMalloc(sizeof (enumDef));
|
|
|
|
/* Assume the enum isn't versioned. */
|
|
first_alt = ed;
|
|
next_alt = NULL;
|
|
|
|
if (name != NULL)
|
|
{
|
|
ed->pyname = cacheName(pt, getPythonName(of, name));
|
|
checkAttributes(pt, mod, c_scope, mt_scope, ed->pyname->text, FALSE);
|
|
|
|
ed->fqcname = text2scopedName(scope, name);
|
|
ed->cname = cacheName(pt, scopedNameToString(ed->fqcname));
|
|
|
|
if (inMainModule())
|
|
{
|
|
setIsUsedName(ed->pyname);
|
|
setIsUsedName(ed->cname);
|
|
}
|
|
|
|
/* If the scope is versioned then look for any alternate. */
|
|
if (scope != NULL && scope->api_range != NULL)
|
|
{
|
|
enumDef *alt;
|
|
|
|
for (alt = pt->enums; alt != NULL; alt = alt->next)
|
|
{
|
|
if (alt->module != mod || alt->fqcname == NULL)
|
|
continue;
|
|
|
|
if (compareScopedNames(alt->fqcname, ed->fqcname) == 0)
|
|
{
|
|
first_alt = alt->first_alt;
|
|
next_alt = first_alt->next_alt;
|
|
first_alt->next_alt = ed;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ed->pyname = NULL;
|
|
ed->fqcname = NULL;
|
|
ed->cname = NULL;
|
|
}
|
|
|
|
if (flags & SECT_IS_PROT && makeProtPublic)
|
|
{
|
|
flags &= ~SECT_IS_PROT;
|
|
flags |= SECT_IS_PUBLIC;
|
|
}
|
|
|
|
ed->enumflags = flags;
|
|
ed->enumnr = -1;
|
|
ed->ecd = c_scope;
|
|
ed->emtd = mt_scope;
|
|
ed->first_alt = first_alt;
|
|
ed->next_alt = next_alt;
|
|
ed->module = mod;
|
|
ed->members = NULL;
|
|
ed->slots = NULL;
|
|
ed->overs = NULL;
|
|
ed->next = pt -> enums;
|
|
|
|
pt->enums = ed;
|
|
|
|
return ed;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get the type values and (optionally) the type names for substitution in
|
|
* handwritten code.
|
|
*/
|
|
void appendTypeStrings(scopedNameDef *ename, signatureDef *patt, signatureDef *src, signatureDef *known, scopedNameDef **names, scopedNameDef **values)
|
|
{
|
|
int a;
|
|
|
|
for (a = 0; a < patt->nrArgs; ++a)
|
|
{
|
|
argDef *pad = &patt->args[a];
|
|
|
|
if (pad->atype == defined_type)
|
|
{
|
|
char *nam = NULL, *val;
|
|
argDef *sad;
|
|
|
|
/*
|
|
* If the type names are already known then check that this is one
|
|
* of them.
|
|
*/
|
|
if (known == NULL)
|
|
nam = scopedNameTail(pad->u.snd);
|
|
else if (pad->u.snd->next == NULL)
|
|
{
|
|
int k;
|
|
|
|
for (k = 0; k < known->nrArgs; ++k)
|
|
{
|
|
/* Skip base types. */
|
|
if (known->args[k].atype != defined_type)
|
|
continue;
|
|
|
|
if (strcmp(pad->u.snd->name, known->args[k].u.snd->name) == 0)
|
|
{
|
|
nam = pad->u.snd->name;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (nam == NULL)
|
|
continue;
|
|
|
|
/* Add the name. */
|
|
appendScopedName(names, text2scopePart(nam));
|
|
|
|
/*
|
|
* Add the corresponding value. For defined types we don't want
|
|
* any indirection or references.
|
|
*/
|
|
sad = &src->args[a];
|
|
|
|
if (sad->atype == defined_type)
|
|
val = scopedNameToString(sad->u.snd);
|
|
else
|
|
val = type2string(sad);
|
|
|
|
appendScopedName(values, text2scopePart(val));
|
|
}
|
|
else if (pad->atype == template_type)
|
|
{
|
|
argDef *sad = &src->args[a];
|
|
|
|
/* These checks shouldn't be necessary, but... */
|
|
if (sad->atype == template_type && pad->u.td->types.nrArgs == sad->u.td->types.nrArgs)
|
|
appendTypeStrings(ename, &pad->u.td->types, &sad->u.td->types, known, names, values);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Convert a type to a string on the heap. The string will use the minimum
|
|
* whitespace while still remaining valid C++.
|
|
*/
|
|
static char *type2string(argDef *ad)
|
|
{
|
|
int i, on_heap = FALSE;
|
|
int nr_derefs = ad->nrderefs;
|
|
int is_reference = isReference(ad);
|
|
char *s;
|
|
|
|
/* Use the original type if possible. */
|
|
if (ad->original_type != NULL && !noTypeName(ad->original_type))
|
|
{
|
|
s = scopedNameToString(ad->original_type->fqname);
|
|
on_heap = TRUE;
|
|
|
|
nr_derefs -= ad->original_type->type.nrderefs;
|
|
|
|
if (isReference(&ad->original_type->type))
|
|
is_reference = FALSE;
|
|
}
|
|
else
|
|
switch (ad->atype)
|
|
{
|
|
case template_type:
|
|
{
|
|
templateDef *td = ad->u.td;
|
|
|
|
s = scopedNameToString(td->fqname);
|
|
append(&s, "<");
|
|
|
|
for (i = 0; i < td->types.nrArgs; ++i)
|
|
{
|
|
char *sub_type = type2string(&td->types.args[i]);
|
|
|
|
if (i > 0)
|
|
append(&s, ",");
|
|
|
|
append(&s, sub_type);
|
|
free(sub_type);
|
|
}
|
|
|
|
if (s[strlen(s) - 1] == '>')
|
|
append(&s, " >");
|
|
else
|
|
append(&s, ">");
|
|
|
|
on_heap = TRUE;
|
|
break;
|
|
}
|
|
|
|
case struct_type:
|
|
s = scopedNameToString(ad->u.sname);
|
|
on_heap = TRUE;
|
|
break;
|
|
|
|
case defined_type:
|
|
s = scopedNameToString(ad->u.snd);
|
|
on_heap = TRUE;
|
|
break;
|
|
|
|
case ustring_type:
|
|
s = "unsigned char";
|
|
break;
|
|
|
|
case ascii_string_type:
|
|
case latin1_string_type:
|
|
case utf8_string_type:
|
|
case string_type:
|
|
s = "char";
|
|
break;
|
|
|
|
case sstring_type:
|
|
s = "signed char";
|
|
break;
|
|
|
|
case wstring_type:
|
|
s = "wchar_t";
|
|
break;
|
|
|
|
case ushort_type:
|
|
s = "unsigned short";
|
|
break;
|
|
|
|
case short_type:
|
|
s = "short";
|
|
break;
|
|
|
|
case uint_type:
|
|
s = "unsigned int";
|
|
break;
|
|
|
|
case int_type:
|
|
case cint_type:
|
|
s = "int";
|
|
break;
|
|
|
|
case ulong_type:
|
|
s = "unsigned long";
|
|
break;
|
|
|
|
case long_type:
|
|
s = "long";
|
|
break;
|
|
|
|
case ulonglong_type:
|
|
s = "unsigned long long";
|
|
break;
|
|
|
|
case longlong_type:
|
|
s = "long long";
|
|
break;
|
|
|
|
case float_type:
|
|
case cfloat_type:
|
|
s = "float";
|
|
break;
|
|
|
|
case double_type:
|
|
case cdouble_type:
|
|
s = "double";
|
|
break;
|
|
|
|
case bool_type:
|
|
case cbool_type:
|
|
s = "bool";
|
|
break;
|
|
|
|
default:
|
|
fatal("Unsupported type argument to type2string(): %d\n", ad->atype);
|
|
}
|
|
|
|
/* Make sure the string is on the heap. */
|
|
if (!on_heap)
|
|
s = sipStrdup(s);
|
|
|
|
while (nr_derefs-- > 0)
|
|
append(&s, "*");
|
|
|
|
if (is_reference)
|
|
append(&s, "&");
|
|
|
|
return s;
|
|
}
|
|
|
|
|
|
/*
|
|
* Convert a scoped name to a string on the heap.
|
|
*/
|
|
static char *scopedNameToString(scopedNameDef *name)
|
|
{
|
|
static const char scope_string[] = "::";
|
|
size_t len;
|
|
scopedNameDef *snd;
|
|
char *s, *dp;
|
|
|
|
/* Work out the length of buffer needed. */
|
|
len = 0;
|
|
|
|
for (snd = name; snd != NULL; snd = snd->next)
|
|
{
|
|
len += strlen(snd->name);
|
|
|
|
if (snd->next != NULL)
|
|
{
|
|
/* Ignore the encoded part of template names. */
|
|
if (isdigit(snd->next->name[0]))
|
|
break;
|
|
|
|
len += strlen(scope_string);
|
|
}
|
|
}
|
|
|
|
/* Allocate and populate the buffer. */
|
|
dp = s = sipMalloc(len + 1);
|
|
|
|
for (snd = name; snd != NULL; snd = snd->next)
|
|
{
|
|
strcpy(dp, snd->name);
|
|
dp += strlen(snd->name);
|
|
|
|
if (snd->next != NULL)
|
|
{
|
|
/* Ignore the encoded part of template names. */
|
|
if (isdigit(snd->next->name[0]))
|
|
break;
|
|
|
|
strcpy(dp, scope_string);
|
|
dp += strlen(scope_string);
|
|
}
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
|
|
/*
|
|
* Instantiate a class template.
|
|
*/
|
|
static void instantiateClassTemplate(sipSpec *pt, moduleDef *mod,
|
|
classDef *scope, scopedNameDef *fqname, classTmplDef *tcd,
|
|
templateDef *td)
|
|
{
|
|
scopedNameDef *type_names, *type_values;
|
|
classDef *cd;
|
|
ctorDef *oct, **cttail;
|
|
argDef *ad;
|
|
ifaceFileList *iffl, **used;
|
|
|
|
type_names = type_values = NULL;
|
|
appendTypeStrings(classFTQCName(tcd->cd), &tcd->sig, &td->types, NULL, &type_names, &type_values);
|
|
|
|
/*
|
|
* Add a mapping from the template name to the instantiated name. If we
|
|
* have got this far we know there is room for it.
|
|
*/
|
|
ad = &tcd->sig.args[tcd->sig.nrArgs++];
|
|
memset(ad, 0, sizeof (argDef));
|
|
ad->atype = defined_type;
|
|
ad->u.snd = classFTQCName(tcd->cd);
|
|
|
|
appendScopedName(&type_names, text2scopePart(scopedNameTail(classFTQCName(tcd->cd))));
|
|
appendScopedName(&type_values, text2scopePart(scopedNameToString(fqname)));
|
|
|
|
/* Create the new class. */
|
|
cd = sipMalloc(sizeof (classDef));
|
|
|
|
/* Start with a shallow copy. */
|
|
*cd = *tcd->cd;
|
|
|
|
resetIsTemplateClass(cd);
|
|
cd->pyname = cacheName(pt, scopedNameTail(fqname));
|
|
cd->td = td;
|
|
|
|
/* Handle the interface file. */
|
|
cd->iff = findIfaceFile(pt, mod, fqname, class_iface,
|
|
(scope != NULL ? scope->iff->api_range : NULL), NULL);
|
|
cd->iff->module = mod;
|
|
|
|
/* Make a copy of the used list and add the enclosing scope. */
|
|
used = &cd->iff->used;
|
|
|
|
for (iffl = tcd->cd->iff->used; iffl != NULL; iffl = iffl->next)
|
|
addToUsedList(used, iffl->iff);
|
|
|
|
/* Include any scope header code. */
|
|
if (scope != NULL)
|
|
appendCodeBlock(&cd->iff->hdrcode, scope->iff->hdrcode);
|
|
|
|
if (inMainModule())
|
|
{
|
|
setIsUsedName(cd->iff->name);
|
|
setIsUsedName(cd->pyname);
|
|
}
|
|
|
|
cd->ecd = currentScope();
|
|
|
|
/* Handle the enums. */
|
|
instantiateTemplateEnums(pt, tcd, td, cd, used, type_names, type_values);
|
|
|
|
/* Handle the variables. */
|
|
instantiateTemplateVars(pt, tcd, td, cd, used, type_names, type_values);
|
|
|
|
/* Handle the ctors. */
|
|
cd->ctors = NULL;
|
|
cttail = &cd->ctors;
|
|
|
|
for (oct = tcd->cd->ctors; oct != NULL; oct = oct->next)
|
|
{
|
|
ctorDef *nct = sipMalloc(sizeof (ctorDef));
|
|
|
|
/* Start with a shallow copy. */
|
|
*nct = *oct;
|
|
|
|
templateSignature(&nct->pysig, FALSE, tcd, td, cd);
|
|
|
|
if (oct->cppsig == NULL)
|
|
nct->cppsig = NULL;
|
|
else if (oct->cppsig == &oct->pysig)
|
|
nct->cppsig = &nct->pysig;
|
|
else
|
|
{
|
|
nct->cppsig = sipMalloc(sizeof (signatureDef));
|
|
|
|
*nct->cppsig = *oct->cppsig;
|
|
|
|
templateSignature(nct->cppsig, FALSE, tcd, td, cd);
|
|
}
|
|
|
|
nct->methodcode = templateCode(pt, used, nct->methodcode, type_names, type_values);
|
|
|
|
nct->next = NULL;
|
|
*cttail = nct;
|
|
cttail = &nct->next;
|
|
|
|
/* Handle the default ctor. */
|
|
if (tcd->cd->defctor == oct)
|
|
cd->defctor = nct;
|
|
}
|
|
|
|
cd->dealloccode = templateCode(pt, used, cd->dealloccode, type_names, type_values);
|
|
cd->dtorcode = templateCode(pt, used, cd->dtorcode, type_names, type_values);
|
|
|
|
/* Handle the methods. */
|
|
cd->members = instantiateTemplateMethods(tcd->cd->members, mod);
|
|
cd->overs = instantiateTemplateOverloads(pt, tcd->cd->overs,
|
|
tcd->cd->members, cd->members, tcd, td, cd, used, type_names,
|
|
type_values);
|
|
|
|
cd->cppcode = templateCode(pt, used, cd->cppcode, type_names, type_values);
|
|
cd->iff->hdrcode = templateCode(pt, used, cd->iff->hdrcode, type_names, type_values);
|
|
cd->convtosubcode = templateCode(pt, used, cd->convtosubcode, type_names, type_values);
|
|
cd->convtocode = templateCode(pt, used, cd->convtocode, type_names, type_values);
|
|
cd->travcode = templateCode(pt, used, cd->travcode, type_names, type_values);
|
|
cd->clearcode = templateCode(pt, used, cd->clearcode, type_names, type_values);
|
|
cd->getbufcode = templateCode(pt, used, cd->getbufcode, type_names, type_values);
|
|
cd->releasebufcode = templateCode(pt, used, cd->releasebufcode, type_names, type_values);
|
|
cd->readbufcode = templateCode(pt, used, cd->readbufcode, type_names, type_values);
|
|
cd->writebufcode = templateCode(pt, used, cd->writebufcode, type_names, type_values);
|
|
cd->segcountcode = templateCode(pt, used, cd->segcountcode, type_names, type_values);
|
|
cd->charbufcode = templateCode(pt, used, cd->charbufcode, type_names, type_values);
|
|
cd->picklecode = templateCode(pt, used, cd->picklecode, type_names, type_values);
|
|
cd->next = pt->classes;
|
|
|
|
pt->classes = cd;
|
|
|
|
tcd->sig.nrArgs--;
|
|
|
|
freeScopedName(type_names);
|
|
freeScopedName(type_values);
|
|
}
|
|
|
|
|
|
/*
|
|
* Instantiate the methods of a template class.
|
|
*/
|
|
static memberDef *instantiateTemplateMethods(memberDef *tmd, moduleDef *mod)
|
|
{
|
|
memberDef *md, *methods, **mdtail;
|
|
|
|
methods = NULL;
|
|
mdtail = &methods;
|
|
|
|
for (md = tmd; md != NULL; md = md->next)
|
|
{
|
|
memberDef *nmd = sipMalloc(sizeof (memberDef));
|
|
|
|
/* Start with a shallow copy. */
|
|
*nmd = *md;
|
|
|
|
nmd->module = mod;
|
|
|
|
if (inMainModule())
|
|
setIsUsedName(nmd->pyname);
|
|
|
|
nmd->next = NULL;
|
|
*mdtail = nmd;
|
|
mdtail = &nmd->next;
|
|
}
|
|
|
|
return methods;
|
|
}
|
|
|
|
|
|
/*
|
|
* Instantiate the overloads of a template class.
|
|
*/
|
|
static overDef *instantiateTemplateOverloads(sipSpec *pt, overDef *tod,
|
|
memberDef *tmethods, memberDef *methods, classTmplDef *tcd,
|
|
templateDef *td, classDef *cd, ifaceFileList **used,
|
|
scopedNameDef *type_names, scopedNameDef *type_values)
|
|
{
|
|
overDef *od, *overloads, **odtail;
|
|
|
|
overloads = NULL;
|
|
odtail = &overloads;
|
|
|
|
for (od = tod; od != NULL; od = od->next)
|
|
{
|
|
overDef *nod = sipMalloc(sizeof (overDef));
|
|
memberDef *nmd, *omd;
|
|
|
|
/* Start with a shallow copy. */
|
|
*nod = *od;
|
|
|
|
for (nmd = methods, omd = tmethods; omd != NULL; omd = omd->next, nmd = nmd->next)
|
|
if (omd == od->common)
|
|
{
|
|
nod->common = nmd;
|
|
break;
|
|
}
|
|
|
|
templateSignature(&nod->pysig, TRUE, tcd, td, cd);
|
|
|
|
if (od->cppsig == &od->pysig)
|
|
nod->cppsig = &nod->pysig;
|
|
else
|
|
{
|
|
nod->cppsig = sipMalloc(sizeof (signatureDef));
|
|
|
|
*nod->cppsig = *od->cppsig;
|
|
|
|
templateSignature(nod->cppsig, TRUE, tcd, td, cd);
|
|
}
|
|
|
|
nod->methodcode = templateCode(pt, used, nod->methodcode, type_names, type_values);
|
|
|
|
/* Handle any virtual handler. */
|
|
if (od->virthandler != NULL)
|
|
{
|
|
moduleDef *mod = cd->iff->module;
|
|
|
|
nod->virthandler = sipMalloc(sizeof (virtHandlerDef));
|
|
|
|
/* Start with a shallow copy. */
|
|
*nod->virthandler = *od->virthandler;
|
|
|
|
if (od->virthandler->cppsig == &od->pysig)
|
|
nod->virthandler->cppsig = &nod->pysig;
|
|
else
|
|
{
|
|
nod->virthandler->cppsig = sipMalloc(sizeof (signatureDef));
|
|
|
|
*nod->virthandler->cppsig = *od->virthandler->cppsig;
|
|
|
|
templateSignature(nod->virthandler->cppsig, TRUE, tcd, td, cd);
|
|
}
|
|
|
|
nod->virthandler->module = mod;
|
|
nod->virthandler->virtcode = templateCode(pt, used, nod->virthandler->virtcode, type_names, type_values);
|
|
nod->virthandler->next = mod->virthandlers;
|
|
|
|
mod->virthandlers = nod->virthandler;
|
|
}
|
|
|
|
nod->next = NULL;
|
|
*odtail = nod;
|
|
odtail = &nod->next;
|
|
}
|
|
|
|
return overloads;
|
|
}
|
|
|
|
|
|
/*
|
|
* Instantiate the enums of a template class.
|
|
*/
|
|
static void instantiateTemplateEnums(sipSpec *pt, classTmplDef *tcd,
|
|
templateDef *td, classDef *cd, ifaceFileList **used,
|
|
scopedNameDef *type_names, scopedNameDef *type_values)
|
|
{
|
|
enumDef *ted;
|
|
moduleDef *mod = cd->iff->module;
|
|
|
|
for (ted = pt->enums; ted != NULL; ted = ted->next)
|
|
if (ted->ecd == tcd->cd)
|
|
{
|
|
enumDef *ed;
|
|
enumMemberDef *temd;
|
|
|
|
ed = sipMalloc(sizeof (enumDef));
|
|
|
|
/* Start with a shallow copy. */
|
|
*ed = *ted;
|
|
|
|
if (ed->fqcname != NULL)
|
|
{
|
|
ed->fqcname = text2scopedName(cd->iff,
|
|
scopedNameTail(ed->fqcname));
|
|
ed->cname = cacheName(pt, scopedNameToString(ed->fqcname));
|
|
}
|
|
|
|
if (inMainModule())
|
|
{
|
|
if (ed->pyname != NULL)
|
|
setIsUsedName(ed->pyname);
|
|
|
|
if (ed->cname != NULL)
|
|
setIsUsedName(ed->cname);
|
|
}
|
|
|
|
ed->ecd = cd;
|
|
ed->first_alt = ed;
|
|
ed->module = mod;
|
|
ed->members = NULL;
|
|
|
|
for (temd = ted->members; temd != NULL; temd = temd->next)
|
|
{
|
|
enumMemberDef *emd;
|
|
|
|
emd = sipMalloc(sizeof (enumMemberDef));
|
|
|
|
/* Start with a shallow copy. */
|
|
*emd = *temd;
|
|
emd->ed = ed;
|
|
|
|
emd->next = ed->members;
|
|
ed->members = emd;
|
|
}
|
|
|
|
ed->slots = instantiateTemplateMethods(ted->slots, mod);
|
|
ed->overs = instantiateTemplateOverloads(pt, ted->overs,
|
|
ted->slots, ed->slots, tcd, td, cd, used, type_names,
|
|
type_values);
|
|
|
|
ed->next = pt->enums;
|
|
pt->enums = ed;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Instantiate the variables of a template class.
|
|
*/
|
|
static void instantiateTemplateVars(sipSpec *pt, classTmplDef *tcd,
|
|
templateDef *td, classDef *cd, ifaceFileList **used,
|
|
scopedNameDef *type_names, scopedNameDef *type_values)
|
|
{
|
|
varDef *tvd;
|
|
|
|
for (tvd = pt->vars; tvd != NULL; tvd = tvd->next)
|
|
if (tvd->ecd == tcd->cd)
|
|
{
|
|
varDef *vd;
|
|
|
|
vd = sipMalloc(sizeof (varDef));
|
|
|
|
/* Start with a shallow copy. */
|
|
*vd = *tvd;
|
|
|
|
if (inMainModule())
|
|
setIsUsedName(vd->pyname);
|
|
|
|
vd->fqcname = text2scopedName(cd->iff,
|
|
scopedNameTail(vd->fqcname));
|
|
vd->ecd = cd;
|
|
vd->module = cd->iff->module;
|
|
|
|
templateType(&vd->type, tcd, td, cd);
|
|
|
|
vd->accessfunc = templateCode(pt, used, vd->accessfunc, type_names, type_values);
|
|
vd->getcode = templateCode(pt, used, vd->getcode, type_names, type_values);
|
|
vd->setcode = templateCode(pt, used, vd->setcode, type_names, type_values);
|
|
|
|
addVariable(pt, vd);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Replace any template arguments in a signature.
|
|
*/
|
|
static void templateSignature(signatureDef *sd, int result, classTmplDef *tcd, templateDef *td, classDef *ncd)
|
|
{
|
|
int a;
|
|
|
|
if (result)
|
|
templateType(&sd->result, tcd, td, ncd);
|
|
|
|
for (a = 0; a < sd->nrArgs; ++a)
|
|
templateType(&sd->args[a], tcd, td, ncd);
|
|
}
|
|
|
|
|
|
/*
|
|
* Replace any template arguments in a type.
|
|
*/
|
|
static void templateType(argDef *ad, classTmplDef *tcd, templateDef *td, classDef *ncd)
|
|
{
|
|
int a;
|
|
char *name;
|
|
|
|
/* Descend into any sub-templates. */
|
|
if (ad->atype == template_type)
|
|
{
|
|
templateDef *new_td = sipMalloc(sizeof (templateDef));
|
|
|
|
/* Make a deep copy of the template definition. */
|
|
*new_td = *ad->u.td;
|
|
ad->u.td = new_td;
|
|
|
|
templateSignature(&ad->u.td->types, FALSE, tcd, td, ncd);
|
|
|
|
return;
|
|
}
|
|
|
|
/* Ignore if it isn't an unscoped name. */
|
|
if (ad->atype != defined_type || ad->u.snd->next != NULL)
|
|
return;
|
|
|
|
name = ad->u.snd->name;
|
|
|
|
for (a = 0; a < tcd->sig.nrArgs - 1; ++a)
|
|
if (strcmp(name, scopedNameTail(tcd->sig.args[a].u.snd)) == 0)
|
|
{
|
|
argDef *tad = &td->types.args[a];
|
|
|
|
ad->atype = tad->atype;
|
|
|
|
/* We take the constrained flag from the real type. */
|
|
resetIsConstrained(ad);
|
|
|
|
if (isConstrained(tad))
|
|
setIsConstrained(ad);
|
|
|
|
ad->u = tad->u;
|
|
|
|
return;
|
|
}
|
|
|
|
/* Handle the class name itself. */
|
|
if (strcmp(name, scopedNameTail(classFTQCName(tcd->cd))) == 0)
|
|
{
|
|
ad->atype = class_type;
|
|
ad->u.cd = ncd;
|
|
ad->original_type = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Replace any template arguments in a literal code block.
|
|
*/
|
|
codeBlock *templateCode(sipSpec *pt, ifaceFileList **used, codeBlock *ocb,
|
|
scopedNameDef *names, scopedNameDef *values)
|
|
{
|
|
codeBlock *ncb = NULL, **tail = &ncb;
|
|
|
|
while (ocb != NULL)
|
|
{
|
|
char *at = ocb->frag;
|
|
|
|
do
|
|
{
|
|
char *first = NULL;
|
|
codeBlock *cb;
|
|
scopedNameDef *nam, *val, *nam_first, *val_first;
|
|
|
|
/*
|
|
* Go through the rest of this fragment looking for each of the
|
|
* types and the name of the class itself.
|
|
*/
|
|
nam = names;
|
|
val = values;
|
|
|
|
while (nam != NULL && val != NULL)
|
|
{
|
|
char *cp;
|
|
|
|
if ((cp = strstr(at, nam->name)) != NULL)
|
|
if (first == NULL || first > cp)
|
|
{
|
|
nam_first = nam;
|
|
val_first = val;
|
|
first = cp;
|
|
}
|
|
|
|
nam = nam->next;
|
|
val = val->next;
|
|
}
|
|
|
|
/* Create the new fragment. */
|
|
cb = sipMalloc(sizeof (codeBlock));
|
|
|
|
if (at == ocb->frag)
|
|
{
|
|
cb->filename = ocb->filename;
|
|
cb->linenr = ocb->linenr;
|
|
}
|
|
else
|
|
cb->filename = NULL;
|
|
|
|
cb->next = NULL;
|
|
*tail = cb;
|
|
tail = &cb->next;
|
|
|
|
/* See if anything was found. */
|
|
if (first == NULL)
|
|
{
|
|
/* We can just point to this. */
|
|
cb->frag = at;
|
|
|
|
/* All done with this one. */
|
|
at = NULL;
|
|
}
|
|
else
|
|
{
|
|
static char *gen_names[] = {
|
|
"sipType_",
|
|
"sipClass_",
|
|
"sipEnum_",
|
|
"sipException_",
|
|
NULL
|
|
};
|
|
|
|
char *dp, *sp, **gn;
|
|
int genname = FALSE;
|
|
|
|
/*
|
|
* If the context in which the text is used is in the name of a
|
|
* SIP generated object then translate any "::" scoping to "_".
|
|
*/
|
|
for (gn = gen_names; *gn != NULL; ++gn)
|
|
if (search_back(first, at, *gn))
|
|
{
|
|
addUsedFromCode(pt, used, val_first->name);
|
|
genname = TRUE;
|
|
break;
|
|
}
|
|
|
|
/* Fragment the fragment. */
|
|
cb->frag = sipMalloc(first - at + strlen(val_first->name) + 1);
|
|
|
|
strncpy(cb->frag, at, first - at);
|
|
|
|
dp = &cb->frag[first - at];
|
|
sp = val_first->name;
|
|
|
|
if (genname)
|
|
{
|
|
char gch;
|
|
|
|
while ((gch = *sp++) != '\0')
|
|
if (gch == ':' && *sp == ':')
|
|
{
|
|
*dp++ = '_';
|
|
++sp;
|
|
}
|
|
else
|
|
*dp++ = gch;
|
|
|
|
*dp = '\0';
|
|
}
|
|
else
|
|
strcpy(dp, sp);
|
|
|
|
/* Move past the replaced text. */
|
|
at = first + strlen(nam_first->name);
|
|
}
|
|
}
|
|
while (at != NULL && *at != '\0');
|
|
|
|
ocb = ocb->next;
|
|
}
|
|
|
|
return ncb;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return TRUE if the text at the end of a string matches the target string.
|
|
*/
|
|
static int search_back(const char *end, const char *start, const char *target)
|
|
{
|
|
size_t tlen = strlen(target);
|
|
|
|
if (start + tlen >= end)
|
|
return FALSE;
|
|
|
|
return (strncmp(end - tlen, target, tlen) == 0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Add any needed interface files based on handwritten code.
|
|
*/
|
|
static void addUsedFromCode(sipSpec *pt, ifaceFileList **used, const char *sname)
|
|
{
|
|
ifaceFileDef *iff;
|
|
enumDef *ed;
|
|
|
|
for (iff = pt->ifacefiles; iff != NULL; iff = iff->next)
|
|
{
|
|
if (iff->type != class_iface && iff->type != exception_iface)
|
|
continue;
|
|
|
|
if (sameName(iff->fqcname, sname))
|
|
{
|
|
addToUsedList(used, iff);
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (ed = pt->enums; ed != NULL; ed = ed->next)
|
|
{
|
|
if (ed->ecd == NULL)
|
|
continue;
|
|
|
|
if (sameName(ed->fqcname, sname))
|
|
{
|
|
addToUsedList(used, ed->ecd->iff);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Compare a scoped name with its string equivalent.
|
|
*/
|
|
static int sameName(scopedNameDef *snd, const char *sname)
|
|
{
|
|
while (snd != NULL && *sname != '\0')
|
|
{
|
|
const char *sp = snd->name;
|
|
|
|
while (*sp != '\0' && *sname != ':' && *sname != '\0')
|
|
if (*sp++ != *sname++)
|
|
return FALSE;
|
|
|
|
if (*sp != '\0' || (*sname != ':' && *sname != '\0'))
|
|
return FALSE;
|
|
|
|
snd = snd->next;
|
|
|
|
if (*sname == ':')
|
|
sname += 2;
|
|
}
|
|
|
|
return (snd == NULL && *sname == '\0');
|
|
}
|
|
|
|
|
|
/*
|
|
* Compare a (possibly) relative scoped name with a fully qualified scoped name
|
|
* while taking the current scope into account.
|
|
*/
|
|
static int foundInScope(scopedNameDef *fq_name, scopedNameDef *rel_name)
|
|
{
|
|
classDef *scope;
|
|
|
|
for (scope = currentScope(); scope != NULL; scope = scope->ecd)
|
|
{
|
|
scopedNameDef *snd;
|
|
int found;
|
|
|
|
snd = copyScopedName(classFTQCName(scope));
|
|
appendScopedName(&snd, copyScopedName(rel_name));
|
|
|
|
found = (compareScopedNames(fq_name, snd) == 0);
|
|
|
|
freeScopedName(snd);
|
|
|
|
if (found)
|
|
return TRUE;
|
|
}
|
|
|
|
return compareScopedNames(fq_name, rel_name) == 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Create a new typedef.
|
|
*/
|
|
static void newTypedef(sipSpec *pt, moduleDef *mod, char *name, argDef *type,
|
|
optFlags *optflgs)
|
|
{
|
|
typedefDef *td, **tdp;
|
|
scopedNameDef *fqname;
|
|
classDef *scope;
|
|
|
|
scope = currentScope();
|
|
fqname = text2scopedName((scope != NULL ? scope->iff : NULL), name);
|
|
|
|
/* See if we are instantiating a template class. */
|
|
if (type->atype == template_type)
|
|
{
|
|
classTmplDef *tcd;
|
|
templateDef *td = type->u.td;
|
|
|
|
for (tcd = pt->classtemplates; tcd != NULL; tcd = tcd->next)
|
|
if (foundInScope(tcd->cd->iff->fqcname, td->fqname) &&
|
|
sameTemplateSignature(&tcd->sig, &td->types, FALSE))
|
|
{
|
|
instantiateClassTemplate(pt, mod, scope, fqname, tcd, td);
|
|
|
|
/* All done. */
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check it doesn't already exist and find the position in the sorted list
|
|
* where it should be put.
|
|
*/
|
|
for (tdp = &pt->typedefs; *tdp != NULL; tdp = &(*tdp)->next)
|
|
{
|
|
int res = compareScopedNames((*tdp)->fqname, fqname);
|
|
|
|
if (res == 0)
|
|
{
|
|
fatalScopedName(fqname);
|
|
fatal(" already defined\n");
|
|
}
|
|
|
|
if (res > 0)
|
|
break;
|
|
}
|
|
|
|
td = sipMalloc(sizeof (typedefDef));
|
|
|
|
td->tdflags = 0;
|
|
td->fqname = fqname;
|
|
td->ecd = scope;
|
|
td->module = mod;
|
|
td->type = *type;
|
|
|
|
td->next = *tdp;
|
|
*tdp = td;
|
|
|
|
if (findOptFlag(optflgs, "NoTypeName", bool_flag) != NULL)
|
|
setNoTypeName(td);
|
|
|
|
mod->nrtypedefs++;
|
|
}
|
|
|
|
|
|
/*
|
|
* Speculatively try and resolve any typedefs. In some cases (eg. when
|
|
* comparing template signatures) it helps to use the real type if it is known.
|
|
* Note that this wouldn't be necessary if we required that all types be known
|
|
* before they are used.
|
|
*/
|
|
static void resolveAnyTypedef(sipSpec *pt, argDef *ad)
|
|
{
|
|
argDef orig = *ad;
|
|
|
|
while (ad->atype == defined_type)
|
|
{
|
|
ad->atype = no_type;
|
|
searchTypedefs(pt, ad->u.snd, ad);
|
|
|
|
/*
|
|
* Don't resolve to a template type as it may be superceded later on
|
|
* by a more specific mapped type.
|
|
*/
|
|
if (ad->atype == no_type || ad->atype == template_type)
|
|
{
|
|
*ad = orig;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Return TRUE if the template signatures are the same. A deep comparison is
|
|
* used for mapped type templates where we want to recurse into any nested
|
|
* templates.
|
|
*/
|
|
int sameTemplateSignature(signatureDef *tmpl_sd, signatureDef *args_sd,
|
|
int deep)
|
|
{
|
|
int a;
|
|
|
|
if (tmpl_sd->nrArgs != args_sd->nrArgs)
|
|
return FALSE;
|
|
|
|
for (a = 0; a < tmpl_sd->nrArgs; ++a)
|
|
{
|
|
argDef *tmpl_ad = &tmpl_sd->args[a];
|
|
argDef *args_ad = &args_sd->args[a];
|
|
|
|
/*
|
|
* If we are doing a shallow comparision (ie. for class templates) then
|
|
* a type name in the template signature matches anything in the
|
|
* argument signature.
|
|
*/
|
|
if (tmpl_ad->atype == defined_type && !deep)
|
|
continue;
|
|
|
|
/*
|
|
* For type names only compare the references and pointers, and do the
|
|
* same for any nested templates.
|
|
*/
|
|
if (tmpl_ad->atype == defined_type && args_ad->atype == defined_type)
|
|
{
|
|
if (isReference(tmpl_ad) != isReference(args_ad) || tmpl_ad->nrderefs != args_ad->nrderefs)
|
|
return FALSE;
|
|
}
|
|
else if (tmpl_ad->atype == template_type && args_ad->atype == template_type)
|
|
{
|
|
if (!sameTemplateSignature(&tmpl_ad->u.td->types, &args_ad->u.td->types, deep))
|
|
return FALSE;
|
|
}
|
|
else if (!sameBaseType(tmpl_ad, args_ad))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Create a new variable.
|
|
*/
|
|
static void newVar(sipSpec *pt,moduleDef *mod,char *name,int isstatic,
|
|
argDef *type,optFlags *of,codeBlock *acode,codeBlock *gcode,
|
|
codeBlock *scode)
|
|
{
|
|
varDef *var;
|
|
classDef *escope = currentScope();
|
|
nameDef *nd = cacheName(pt,getPythonName(of,name));
|
|
|
|
if (inMainModule())
|
|
setIsUsedName(nd);
|
|
|
|
checkAttributes(pt, mod, escope, NULL, nd->text, FALSE);
|
|
|
|
var = sipMalloc(sizeof (varDef));
|
|
|
|
var->pyname = nd;
|
|
var->fqcname = text2scopedName((escope != NULL ? escope->iff : NULL),
|
|
name);
|
|
var->ecd = escope;
|
|
var->module = mod;
|
|
var->varflags = 0;
|
|
var->type = *type;
|
|
var->accessfunc = acode;
|
|
var->getcode = gcode;
|
|
var->setcode = scode;
|
|
|
|
if (isstatic || (escope != NULL && escope->iff->type == namespace_iface))
|
|
setIsStaticVar(var);
|
|
|
|
addVariable(pt, var);
|
|
}
|
|
|
|
|
|
/*
|
|
* Create a new ctor.
|
|
*/
|
|
static void newCtor(char *name, int sectFlags, signatureDef *args,
|
|
optFlags *optflgs, codeBlock *methodcode, throwArgs *exceptions,
|
|
signatureDef *cppsig, int explicit, codeBlock *docstring)
|
|
{
|
|
ctorDef *ct, **ctp;
|
|
classDef *cd = currentScope();
|
|
|
|
/* Check the name of the constructor. */
|
|
if (strcmp(classBaseName(cd), name) != 0)
|
|
yyerror("Constructor doesn't have the same name as its class");
|
|
|
|
if (docstring != NULL)
|
|
appendCodeBlock(&cd->docstring, docstring);
|
|
|
|
/* Add to the list of constructors. */
|
|
ct = sipMalloc(sizeof (ctorDef));
|
|
|
|
if (sectFlags & SECT_IS_PROT && makeProtPublic)
|
|
{
|
|
sectFlags &= ~SECT_IS_PROT;
|
|
sectFlags |= SECT_IS_PUBLIC;
|
|
}
|
|
|
|
/* Allow the signature to be used like an function signature. */
|
|
memset(&args->result, 0, sizeof (argDef));
|
|
args->result.atype = void_type;
|
|
|
|
ct->ctorflags = sectFlags;
|
|
ct->api_range = getAPIRange(optflgs);
|
|
ct->pysig = *args;
|
|
ct->cppsig = (cppsig != NULL ? cppsig : &ct->pysig);
|
|
ct->exceptions = exceptions;
|
|
ct->methodcode = methodcode;
|
|
|
|
if (!isPrivateCtor(ct))
|
|
setCanCreate(cd);
|
|
|
|
if (isProtectedCtor(ct))
|
|
setHasShadow(cd);
|
|
|
|
if (explicit)
|
|
setIsExplicitCtor(ct);
|
|
|
|
getHooks(optflgs, &ct->prehook, &ct->posthook);
|
|
|
|
if (getReleaseGIL(optflgs))
|
|
setIsReleaseGILCtor(ct);
|
|
else if (getHoldGIL(optflgs))
|
|
setIsHoldGILCtor(ct);
|
|
|
|
if (getTransfer(optflgs))
|
|
setIsResultTransferredCtor(ct);
|
|
|
|
if (getDeprecated(optflgs))
|
|
setIsDeprecatedCtor(ct);
|
|
|
|
if (!isPrivateCtor(ct) && usesKeywordArgs(optflgs, &ct->pysig))
|
|
setUseKeywordArgsCtor(ct);
|
|
|
|
if (findOptFlag(optflgs, "NoDerived", bool_flag) != NULL)
|
|
{
|
|
if (cppsig != NULL)
|
|
yyerror("The /NoDerived/ annotation cannot be used with a C++ signature");
|
|
|
|
if (methodcode == NULL)
|
|
yyerror("The /NoDerived/ annotation must be used with %MethodCode");
|
|
|
|
ct->cppsig = NULL;
|
|
}
|
|
|
|
if (findOptFlag(optflgs, "Default", bool_flag) != NULL)
|
|
{
|
|
if (cd->defctor != NULL)
|
|
yyerror("A constructor with the /Default/ annotation has already been defined");
|
|
|
|
cd->defctor = ct;
|
|
}
|
|
|
|
/* Append to the list. */
|
|
for (ctp = &cd->ctors; *ctp != NULL; ctp = &(*ctp)->next)
|
|
;
|
|
|
|
*ctp = ct;
|
|
}
|
|
|
|
|
|
/*
|
|
* Create a new function.
|
|
*/
|
|
static void newFunction(sipSpec *pt, moduleDef *mod, classDef *c_scope,
|
|
mappedTypeDef *mt_scope, int sflags, int isstatic, int issignal,
|
|
int isslot, int isvirt, char *name, signatureDef *sig, int isconst,
|
|
int isabstract, optFlags *optflgs, codeBlock *methodcode,
|
|
codeBlock *vcode, throwArgs *exceptions, signatureDef *cppsig,
|
|
codeBlock *docstring)
|
|
{
|
|
int factory, xferback, no_arg_parser;
|
|
overDef *od, **odp, **headp;
|
|
optFlag *of;
|
|
virtHandlerDef *vhd;
|
|
|
|
/* Extra checks for a C module. */
|
|
if (pt->genc)
|
|
{
|
|
if (c_scope != NULL)
|
|
yyerror("Function declaration not allowed in a struct in a C module");
|
|
|
|
if (isstatic)
|
|
yyerror("Static functions not allowed in a C module");
|
|
|
|
if (exceptions != NULL)
|
|
yyerror("Exceptions not allowed in a C module");
|
|
}
|
|
|
|
if (mt_scope != NULL)
|
|
headp = &mt_scope->overs;
|
|
else if (c_scope != NULL)
|
|
headp = &c_scope->overs;
|
|
else
|
|
headp = &mod->overs;
|
|
|
|
/* See if it is a factory method. */
|
|
if (findOptFlag(optflgs, "Factory", bool_flag) != NULL)
|
|
factory = TRUE;
|
|
else
|
|
{
|
|
int a;
|
|
|
|
factory = FALSE;
|
|
|
|
/* Check /TransferThis/ wasn't specified. */
|
|
if (c_scope == NULL || isstatic)
|
|
for (a = 0; a < sig->nrArgs; ++a)
|
|
if (isThisTransferred(&sig->args[a]))
|
|
yyerror("/TransferThis/ may only be specified in constructors and class methods");
|
|
}
|
|
|
|
/* See if the result is to be returned to Python ownership. */
|
|
xferback = (findOptFlag(optflgs, "TransferBack", bool_flag) != NULL);
|
|
|
|
if (factory && xferback)
|
|
yyerror("/TransferBack/ and /Factory/ cannot both be specified");
|
|
|
|
/* Create a new overload definition. */
|
|
|
|
od = sipMalloc(sizeof (overDef));
|
|
|
|
/* Set the overload flags. */
|
|
|
|
if ((sflags & SECT_IS_PROT) && makeProtPublic)
|
|
{
|
|
sflags &= ~SECT_IS_PROT;
|
|
sflags |= SECT_IS_PUBLIC | OVER_REALLY_PROT;
|
|
}
|
|
|
|
od->overflags = sflags;
|
|
|
|
if (issignal)
|
|
{
|
|
resetIsSlot(od);
|
|
setIsSignal(od);
|
|
}
|
|
else if (isslot)
|
|
{
|
|
resetIsSignal(od);
|
|
setIsSlot(od);
|
|
}
|
|
|
|
if (factory)
|
|
setIsFactory(od);
|
|
|
|
if (xferback)
|
|
setIsResultTransferredBack(od);
|
|
|
|
if (getTransfer(optflgs))
|
|
setIsResultTransferred(od);
|
|
|
|
if (findOptFlag(optflgs, "TransferThis", bool_flag) != NULL)
|
|
setIsThisTransferredMeth(od);
|
|
|
|
if (isProtected(od))
|
|
setHasShadow(c_scope);
|
|
|
|
if ((isSlot(od) || isSignal(od)) && !isPrivate(od))
|
|
{
|
|
if (isSignal(od))
|
|
setHasShadow(c_scope);
|
|
|
|
pt->sigslots = TRUE;
|
|
}
|
|
|
|
if (isSignal(od) && (methodcode != NULL || vcode != NULL))
|
|
yyerror("Cannot provide code for signals");
|
|
|
|
if (isstatic)
|
|
{
|
|
if (isSignal(od))
|
|
yyerror("Static functions cannot be signals");
|
|
|
|
if (isvirt)
|
|
yyerror("Static functions cannot be virtual");
|
|
|
|
setIsStatic(od);
|
|
}
|
|
|
|
if (isconst)
|
|
setIsConst(od);
|
|
|
|
if (isabstract)
|
|
{
|
|
if (sflags == 0)
|
|
yyerror("Non-class function specified as abstract");
|
|
|
|
setIsAbstract(od);
|
|
}
|
|
|
|
if ((of = findOptFlag(optflgs, "AutoGen", opt_name_flag)) != NULL)
|
|
{
|
|
if (of->fvalue.sval == NULL || isEnabledFeature(of->fvalue.sval))
|
|
setIsAutoGen(od);
|
|
}
|
|
|
|
if (isvirt)
|
|
{
|
|
if (isSignal(od) && pluginPyTQt3(pt))
|
|
yyerror("Virtual signals aren't supported");
|
|
|
|
setIsVirtual(od);
|
|
setHasShadow(c_scope);
|
|
|
|
vhd = sipMalloc(sizeof (virtHandlerDef));
|
|
|
|
vhd->virthandlernr = -1;
|
|
vhd->vhflags = 0;
|
|
vhd->pysig = &od->pysig;
|
|
vhd->cppsig = (cppsig != NULL ? cppsig : &od->pysig);
|
|
vhd->virtcode = vcode;
|
|
|
|
if (factory || xferback)
|
|
setIsTransferVH(vhd);
|
|
|
|
/*
|
|
* Only add it to the module's virtual handlers if we are not in a
|
|
* class template.
|
|
*/
|
|
if (!currentIsTemplate)
|
|
{
|
|
vhd->module = mod;
|
|
|
|
vhd->next = mod->virthandlers;
|
|
mod->virthandlers = vhd;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (vcode != NULL)
|
|
yyerror("%VirtualCatcherCode provided for non-virtual function");
|
|
|
|
vhd = NULL;
|
|
}
|
|
|
|
od->cppname = name;
|
|
od->pysig = *sig;
|
|
od->cppsig = (cppsig != NULL ? cppsig : &od->pysig);
|
|
od->exceptions = exceptions;
|
|
od->methodcode = methodcode;
|
|
od->virthandler = vhd;
|
|
|
|
no_arg_parser = (findOptFlag(optflgs, "NoArgParser", bool_flag) != NULL);
|
|
|
|
if (no_arg_parser)
|
|
{
|
|
if (methodcode == NULL)
|
|
yyerror("%MethodCode must be supplied if /NoArgParser/ is specified");
|
|
}
|
|
|
|
if (findOptFlag(optflgs, "NoCopy", bool_flag) != NULL)
|
|
setNoCopy(&od->pysig.result);
|
|
|
|
od->common = findFunction(pt, mod, c_scope, mt_scope,
|
|
getPythonName(optflgs, name), (methodcode != NULL), sig->nrArgs,
|
|
no_arg_parser);
|
|
|
|
if (docstring != NULL)
|
|
appendCodeBlock(&od->common->docstring, docstring);
|
|
|
|
od->api_range = getAPIRange(optflgs);
|
|
|
|
if (od->api_range == NULL)
|
|
setNotVersioned(od->common);
|
|
|
|
if (findOptFlag(optflgs, "Numeric", bool_flag) != NULL)
|
|
setIsNumeric(od->common);
|
|
|
|
/* Methods that run in new threads must be virtual. */
|
|
if (findOptFlag(optflgs, "NewThread", bool_flag) != NULL)
|
|
{
|
|
argDef *res;
|
|
|
|
if (!isvirt)
|
|
yyerror("/NewThread/ may only be specified for virtual functions");
|
|
|
|
/*
|
|
* This is an arbitary limitation to make the code generator slightly
|
|
* easier - laziness on my part.
|
|
*/
|
|
res = &od->cppsig->result;
|
|
|
|
if (res->atype != void_type || res->nrderefs != 0)
|
|
yyerror("/NewThread/ may only be specified for void functions");
|
|
|
|
setIsNewThread(od);
|
|
}
|
|
|
|
getHooks(optflgs, &od->prehook, &od->posthook);
|
|
|
|
if (getReleaseGIL(optflgs))
|
|
setIsReleaseGIL(od);
|
|
else if (getHoldGIL(optflgs))
|
|
setIsHoldGIL(od);
|
|
|
|
if (getDeprecated(optflgs))
|
|
setIsDeprecated(od);
|
|
|
|
if (!isPrivate(od) && !isSignal(od) && od->common->slot == no_slot && usesKeywordArgs(optflgs, &od->pysig))
|
|
{
|
|
setUseKeywordArgs(od);
|
|
setUseKeywordArgsFunction(od->common);
|
|
}
|
|
|
|
/* See if we want to auto-generate a __len__() method. */
|
|
if (findOptFlag(optflgs, "__len__", bool_flag) != NULL)
|
|
{
|
|
overDef *len;
|
|
|
|
len = sipMalloc(sizeof (overDef));
|
|
|
|
len->cppname = "__len__";
|
|
len->overflags = SECT_IS_PUBLIC;
|
|
len->pysig.result.atype = ssize_type;
|
|
len->pysig.nrArgs = 0;
|
|
len->cppsig = &len->pysig;
|
|
|
|
len->common = findFunction(pt, mod, c_scope, mt_scope, len->cppname,
|
|
TRUE, 0, FALSE);
|
|
|
|
if ((len->methodcode = od->methodcode) == NULL)
|
|
{
|
|
char *buf = sipStrdup(" sipRes = (SIP_SSIZE_T)sipCpp->");
|
|
codeBlock *code;
|
|
|
|
append(&buf, od->cppname);
|
|
append(&buf, "();\n");
|
|
|
|
code = sipMalloc(sizeof (codeBlock));
|
|
|
|
code->frag = buf;
|
|
code->filename = "Auto-generated";
|
|
code->linenr = 0;
|
|
code->next = NULL;
|
|
|
|
len->methodcode = code;
|
|
}
|
|
|
|
len->next = NULL;
|
|
|
|
od->next = len;
|
|
}
|
|
else
|
|
{
|
|
od->next = NULL;
|
|
}
|
|
|
|
/* Append to the list. */
|
|
for (odp = headp; *odp != NULL; odp = &(*odp)->next)
|
|
;
|
|
|
|
*odp = od;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return the Python name based on the C/C++ name and any /PyName/ annotation.
|
|
*/
|
|
static const char *getPythonName(optFlags *optflgs, const char *cname)
|
|
{
|
|
const char *pname;
|
|
optFlag *of;
|
|
|
|
if ((of = findOptFlag(optflgs, "PyName", name_flag)) != NULL)
|
|
pname = of->fvalue.sval;
|
|
else
|
|
pname = cname;
|
|
|
|
return pname;
|
|
}
|
|
|
|
|
|
/*
|
|
* Cache a name in a module. Entries in the cache are stored in order of
|
|
* decreasing length.
|
|
*/
|
|
nameDef *cacheName(sipSpec *pt, const char *name)
|
|
{
|
|
nameDef *nd, **ndp;
|
|
size_t len;
|
|
|
|
/* Allow callers to be lazy about checking if there is really a name. */
|
|
if (name == NULL)
|
|
return NULL;
|
|
|
|
/* Skip entries that are too large. */
|
|
ndp = &pt->namecache;
|
|
len = strlen(name);
|
|
|
|
while (*ndp != NULL && (*ndp)->len > len)
|
|
ndp = &(*ndp)->next;
|
|
|
|
/* Check entries that are the right length. */
|
|
for (nd = *ndp; nd != NULL && nd->len == len; nd = nd->next)
|
|
if (memcmp(nd->text, name, len) == 0)
|
|
return nd;
|
|
|
|
/* Create a new one. */
|
|
nd = sipMalloc(sizeof (nameDef));
|
|
|
|
nd->nameflags = 0;
|
|
nd->text = name;
|
|
nd->len = len;
|
|
nd->next = *ndp;
|
|
|
|
*ndp = nd;
|
|
|
|
return nd;
|
|
}
|
|
|
|
|
|
/*
|
|
* Find (or create) an overloaded function name.
|
|
*/
|
|
static memberDef *findFunction(sipSpec *pt, moduleDef *mod, classDef *c_scope,
|
|
mappedTypeDef *mt_scope, const char *pname, int hwcode, int nrargs,
|
|
int no_arg_parser)
|
|
{
|
|
static struct slot_map {
|
|
const char *name; /* The slot name. */
|
|
slotType type; /* The corresponding type. */
|
|
int needs_hwcode; /* Set if handwritten code is required. */
|
|
int nrargs; /* Nr. of arguments. */
|
|
} slot_table[] = {
|
|
{"__str__", str_slot, TRUE, 0},
|
|
{"__unicode__", unicode_slot, TRUE, 0},
|
|
{"__int__", int_slot, FALSE, 0},
|
|
{"__long__", long_slot, FALSE, 0},
|
|
{"__float__", float_slot, FALSE, 0},
|
|
{"__len__", len_slot, TRUE, 0},
|
|
{"__contains__", contains_slot, TRUE, 1},
|
|
{"__add__", add_slot, FALSE, 1},
|
|
{"__sub__", sub_slot, FALSE, 1},
|
|
{"__mul__", mul_slot, FALSE, 1},
|
|
{"__div__", div_slot, FALSE, 1},
|
|
{"__mod__", mod_slot, FALSE, 1},
|
|
{"__floordiv__", floordiv_slot, TRUE, 1},
|
|
{"__truediv__", truediv_slot, FALSE, 1},
|
|
{"__and__", and_slot, FALSE, 1},
|
|
{"__or__", or_slot, FALSE, 1},
|
|
{"__xor__", xor_slot, FALSE, 1},
|
|
{"__lshift__", lshift_slot, FALSE, 1},
|
|
{"__rshift__", rshift_slot, FALSE, 1},
|
|
{"__iadd__", iadd_slot, FALSE, 1},
|
|
{"__isub__", isub_slot, FALSE, 1},
|
|
{"__imul__", imul_slot, FALSE, 1},
|
|
{"__idiv__", idiv_slot, FALSE, 1},
|
|
{"__imod__", imod_slot, FALSE, 1},
|
|
{"__ifloordiv__", ifloordiv_slot, TRUE, 1},
|
|
{"__itruediv__", itruediv_slot, FALSE, 1},
|
|
{"__iand__", iand_slot, FALSE, 1},
|
|
{"__ior__", ior_slot, FALSE, 1},
|
|
{"__ixor__", ixor_slot, FALSE, 1},
|
|
{"__ilshift__", ilshift_slot, FALSE, 1},
|
|
{"__irshift__", irshift_slot, FALSE, 1},
|
|
{"__invert__", invert_slot, FALSE, 0},
|
|
{"__call__", call_slot, FALSE, -1},
|
|
{"__getitem__", getitem_slot, FALSE, 1},
|
|
{"__setitem__", setitem_slot, TRUE, 2},
|
|
{"__delitem__", delitem_slot, TRUE, 1},
|
|
{"__lt__", lt_slot, FALSE, 1},
|
|
{"__le__", le_slot, FALSE, 1},
|
|
{"__eq__", eq_slot, FALSE, 1},
|
|
{"__ne__", ne_slot, FALSE, 1},
|
|
{"__gt__", gt_slot, FALSE, 1},
|
|
{"__ge__", ge_slot, FALSE, 1},
|
|
{"__cmp__", cmp_slot, FALSE, 1},
|
|
{"__bool__", bool_slot, TRUE, 0},
|
|
{"__nonzero__", bool_slot, TRUE, 0},
|
|
{"__neg__", neg_slot, FALSE, 0},
|
|
{"__pos__", pos_slot, FALSE, 0},
|
|
{"__abs__", abs_slot, TRUE, 0},
|
|
{"__repr__", repr_slot, TRUE, 0},
|
|
{"__hash__", hash_slot, TRUE, 0},
|
|
{"__index__", index_slot, TRUE, 0},
|
|
{"__iter__", iter_slot, TRUE, 0},
|
|
{"__next__", next_slot, TRUE, 0},
|
|
{NULL}
|
|
};
|
|
|
|
memberDef *md, **flist;
|
|
struct slot_map *sm;
|
|
slotType st;
|
|
|
|
/* Get the slot type. */
|
|
st = no_slot;
|
|
|
|
for (sm = slot_table; sm->name != NULL; ++sm)
|
|
if (strcmp(sm->name, pname) == 0)
|
|
{
|
|
if (sm->needs_hwcode && !hwcode)
|
|
yyerror("This Python slot requires %MethodCode");
|
|
|
|
if (sm->nrargs >= 0)
|
|
{
|
|
if (mt_scope == NULL && c_scope == NULL)
|
|
{
|
|
/* Global operators need one extra argument. */
|
|
if (sm -> nrargs + 1 != nrargs)
|
|
yyerror("Incorrect number of arguments to global operator");
|
|
}
|
|
else if (sm->nrargs != nrargs)
|
|
yyerror("Incorrect number of arguments to Python slot");
|
|
}
|
|
|
|
st = sm->type;
|
|
|
|
break;
|
|
}
|
|
|
|
/* Check there is no name clash. */
|
|
checkAttributes(pt, mod, c_scope, mt_scope, pname, TRUE);
|
|
|
|
/* See if it already exists. */
|
|
if (mt_scope != NULL)
|
|
flist = &mt_scope->members;
|
|
else if (c_scope != NULL)
|
|
flist = &c_scope->members;
|
|
else
|
|
flist = &mod->othfuncs;
|
|
|
|
for (md = *flist; md != NULL; md = md->next)
|
|
if (strcmp(md->pyname->text, pname) == 0 && md->module == mod)
|
|
break;
|
|
|
|
if (md == NULL)
|
|
{
|
|
/* Create a new one. */
|
|
md = sipMalloc(sizeof (memberDef));
|
|
|
|
md->pyname = cacheName(pt, pname);
|
|
md->memberflags = 0;
|
|
md->slot = st;
|
|
md->module = mod;
|
|
md->next = *flist;
|
|
|
|
*flist = md;
|
|
|
|
if (inMainModule())
|
|
setIsUsedName(md->pyname);
|
|
|
|
if (no_arg_parser)
|
|
setNoArgParser(md);
|
|
}
|
|
else if (noArgParser(md))
|
|
yyerror("Another overload has already been defined that is annotated as /NoArgParser/");
|
|
|
|
/* Global operators are a subset. */
|
|
if (mt_scope == NULL && c_scope == NULL && st != no_slot && st != neg_slot && st != pos_slot && !isNumberSlot(md) && !isRichCompareSlot(md))
|
|
yyerror("Global operators must be either numeric or comparison operators");
|
|
|
|
return md;
|
|
}
|
|
|
|
|
|
/*
|
|
* Search a set of flags for a particular one and check its type.
|
|
*/
|
|
static optFlag *findOptFlag(optFlags *flgs,char *name,flagType ft)
|
|
{
|
|
int f;
|
|
|
|
for (f = 0; f < flgs -> nrFlags; ++f)
|
|
{
|
|
optFlag *of = &flgs -> flags[f];
|
|
|
|
if (strcmp(of -> fname,name) == 0)
|
|
{
|
|
/*
|
|
* An optional name can look like a boolean or a name.
|
|
*/
|
|
|
|
if (ft == opt_name_flag)
|
|
{
|
|
if (of -> ftype == bool_flag)
|
|
{
|
|
of -> ftype = opt_name_flag;
|
|
of -> fvalue.sval = NULL;
|
|
}
|
|
else if (of -> ftype == name_flag)
|
|
of -> ftype = opt_name_flag;
|
|
}
|
|
|
|
if (ft != of -> ftype)
|
|
yyerror("Optional flag has a value of the wrong type");
|
|
|
|
return of;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* A name is going to be used as a Python attribute name within a Python scope
|
|
* (ie. a Python dictionary), so check against what we already know is going in
|
|
* the same scope in case there is a clash.
|
|
*/
|
|
static void checkAttributes(sipSpec *pt, moduleDef *mod, classDef *py_c_scope,
|
|
mappedTypeDef *py_mt_scope, const char *attr, int isfunc)
|
|
{
|
|
enumDef *ed;
|
|
varDef *vd;
|
|
classDef *cd;
|
|
|
|
/* Check the enums. */
|
|
|
|
for (ed = pt->enums; ed != NULL; ed = ed->next)
|
|
{
|
|
enumMemberDef *emd;
|
|
|
|
if (ed->pyname == NULL)
|
|
continue;
|
|
|
|
if (py_c_scope != NULL)
|
|
{
|
|
if (ed->ecd != py_c_scope)
|
|
continue;
|
|
}
|
|
else if (py_mt_scope != NULL)
|
|
{
|
|
if (ed->emtd != py_mt_scope)
|
|
continue;
|
|
}
|
|
else if (ed->ecd != NULL || ed->emtd != NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (strcmp(ed->pyname->text, attr) == 0)
|
|
yyerror("There is already an enum in scope with the same Python name");
|
|
|
|
for (emd = ed->members; emd != NULL; emd = emd->next)
|
|
if (strcmp(emd->pyname->text, attr) == 0)
|
|
yyerror("There is already an enum member in scope with the same Python name");
|
|
}
|
|
|
|
/*
|
|
* Only check the members if this attribute isn't a member because we
|
|
* can handle members with the same name in the same scope.
|
|
*/
|
|
if (!isfunc)
|
|
{
|
|
memberDef *md, *membs;
|
|
overDef *overs;
|
|
|
|
if (py_mt_scope != NULL)
|
|
{
|
|
membs = py_mt_scope->members;
|
|
overs = py_mt_scope->overs;
|
|
}
|
|
else if (py_c_scope != NULL)
|
|
{
|
|
membs = py_c_scope->members;
|
|
overs = py_c_scope->overs;
|
|
}
|
|
else
|
|
{
|
|
membs = mod->othfuncs;
|
|
overs = mod->overs;
|
|
}
|
|
|
|
for (md = membs; md != NULL; md = md->next)
|
|
{
|
|
overDef *od;
|
|
|
|
if (strcmp(md->pyname->text, attr) != 0)
|
|
continue;
|
|
|
|
/* Check for a conflict with all overloads. */
|
|
for (od = overs; od != NULL; od = od->next)
|
|
{
|
|
if (od->common != md)
|
|
continue;
|
|
|
|
yyerror("There is already a function in scope with the same Python name");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If the scope was a mapped type then that's all we have to check. */
|
|
if (py_mt_scope != NULL)
|
|
return;
|
|
|
|
/* Check the variables. */
|
|
for (vd = pt->vars; vd != NULL; vd = vd->next)
|
|
{
|
|
if (vd->ecd != py_c_scope)
|
|
continue;
|
|
|
|
if (strcmp(vd->pyname->text,attr) == 0)
|
|
yyerror("There is already a variable in scope with the same Python name");
|
|
}
|
|
|
|
/* Check the classes. */
|
|
for (cd = pt->classes; cd != NULL; cd = cd->next)
|
|
{
|
|
if (cd->ecd != py_c_scope || cd->pyname == NULL)
|
|
continue;
|
|
|
|
if (strcmp(cd->pyname->text, attr) == 0 && !isExternal(cd))
|
|
yyerror("There is already a class or namespace in scope with the same Python name");
|
|
}
|
|
|
|
/* Check the exceptions. */
|
|
if (py_c_scope == NULL)
|
|
{
|
|
exceptionDef *xd;
|
|
|
|
for (xd = pt->exceptions; xd != NULL; xd = xd->next)
|
|
if (xd->pyname != NULL && strcmp(xd->pyname, attr) == 0)
|
|
yyerror("There is already an exception with the same Python name");
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Append a code block to a list of them. Append is needed to give the
|
|
* specifier easy control over the order of the documentation.
|
|
*/
|
|
void appendCodeBlock(codeBlock **headp, codeBlock *new)
|
|
{
|
|
while (*headp != NULL)
|
|
headp = &(*headp)->next;
|
|
|
|
*headp = new;
|
|
}
|
|
|
|
|
|
/*
|
|
* Handle the end of a fully parsed a file.
|
|
*/
|
|
static void handleEOF()
|
|
{
|
|
/*
|
|
* Check that the number of nested if's is the same as when we started
|
|
* the file.
|
|
*/
|
|
|
|
if (skipStackPtr > currentContext.ifdepth)
|
|
fatal("Too many %%If statements in %s\n", previousFile);
|
|
|
|
if (skipStackPtr < currentContext.ifdepth)
|
|
fatal("Too many %%End statements in %s\n", previousFile);
|
|
}
|
|
|
|
|
|
/*
|
|
* Handle the end of a fully parsed a module.
|
|
*/
|
|
static void handleEOM()
|
|
{
|
|
moduleDef *from;
|
|
|
|
/* Check it has been named. */
|
|
if (currentModule->name == NULL)
|
|
fatal("No %%Module has been specified for module defined in %s\n",
|
|
previousFile);
|
|
|
|
from = currentContext.prevmod;
|
|
|
|
if (from != NULL && from->encoding == no_type)
|
|
from->encoding = currentModule->encoding;
|
|
|
|
/* The previous module is now current. */
|
|
currentModule = from;
|
|
}
|
|
|
|
|
|
/*
|
|
* Find an existing qualifier.
|
|
*/
|
|
static qualDef *findQualifier(const char *name)
|
|
{
|
|
moduleDef *mod;
|
|
|
|
for (mod = currentSpec->modules; mod != NULL; mod = mod->next)
|
|
{
|
|
qualDef *qd;
|
|
|
|
for (qd = mod->qualifiers; qd != NULL; qd = qd->next)
|
|
if (strcmp(qd->name, name) == 0)
|
|
return qd;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* Find an existing API.
|
|
*/
|
|
apiVersionRangeDef *findAPI(sipSpec *pt, const char *name)
|
|
{
|
|
moduleDef *mod;
|
|
|
|
for (mod = pt->modules; mod != NULL; mod = mod->next)
|
|
{
|
|
apiVersionRangeDef *avd;
|
|
|
|
for (avd = mod->api_versions; avd != NULL; avd = avd->next)
|
|
if (strcmp(avd->api_name->text, name) == 0)
|
|
return avd;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return a copy of a scoped name.
|
|
*/
|
|
scopedNameDef *copyScopedName(scopedNameDef *snd)
|
|
{
|
|
scopedNameDef *head;
|
|
|
|
head = NULL;
|
|
|
|
while (snd != NULL)
|
|
{
|
|
appendScopedName(&head,text2scopePart(snd -> name));
|
|
snd = snd -> next;
|
|
}
|
|
|
|
return head;
|
|
}
|
|
|
|
|
|
/*
|
|
* Append a name to a list of scopes.
|
|
*/
|
|
void appendScopedName(scopedNameDef **headp,scopedNameDef *newsnd)
|
|
{
|
|
while (*headp != NULL)
|
|
headp = &(*headp) -> next;
|
|
|
|
*headp = newsnd;
|
|
}
|
|
|
|
|
|
/*
|
|
* Free a scoped name - but not the text itself.
|
|
*/
|
|
void freeScopedName(scopedNameDef *snd)
|
|
{
|
|
while (snd != NULL)
|
|
{
|
|
scopedNameDef *next = snd -> next;
|
|
|
|
free(snd);
|
|
|
|
snd = next;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Convert a text string to a scope part structure.
|
|
*/
|
|
static scopedNameDef *text2scopePart(char *text)
|
|
{
|
|
scopedNameDef *snd;
|
|
|
|
snd = sipMalloc(sizeof (scopedNameDef));
|
|
|
|
snd->name = text;
|
|
snd->next = NULL;
|
|
|
|
return snd;
|
|
}
|
|
|
|
|
|
/*
|
|
* Convert a text string to a fully scoped name.
|
|
*/
|
|
static scopedNameDef *text2scopedName(ifaceFileDef *scope, char *text)
|
|
{
|
|
return scopeScopedName(scope, text2scopePart(text));
|
|
}
|
|
|
|
|
|
/*
|
|
* Prepend any current scope to a scoped name.
|
|
*/
|
|
static scopedNameDef *scopeScopedName(ifaceFileDef *scope, scopedNameDef *name)
|
|
{
|
|
scopedNameDef *snd;
|
|
|
|
snd = (scope != NULL ? copyScopedName(scope->fqcname) : NULL);
|
|
|
|
appendScopedName(&snd, name);
|
|
|
|
return snd;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return a pointer to the tail part of a scoped name.
|
|
*/
|
|
char *scopedNameTail(scopedNameDef *snd)
|
|
{
|
|
if (snd == NULL)
|
|
return NULL;
|
|
|
|
while (snd -> next != NULL)
|
|
snd = snd -> next;
|
|
|
|
return snd -> name;
|
|
}
|
|
|
|
|
|
/*
|
|
* Push the given scope onto the scope stack.
|
|
*/
|
|
static void pushScope(classDef *scope)
|
|
{
|
|
if (currentScopeIdx >= MAX_NESTED_SCOPE)
|
|
fatal("Internal error: increase the value of MAX_NESTED_SCOPE\n");
|
|
|
|
scopeStack[currentScopeIdx] = scope;
|
|
sectFlagsStack[currentScopeIdx] = sectionFlags;
|
|
|
|
++currentScopeIdx;
|
|
}
|
|
|
|
|
|
/*
|
|
* Pop the scope stack.
|
|
*/
|
|
static void popScope(void)
|
|
{
|
|
if (currentScopeIdx > 0)
|
|
sectionFlags = sectFlagsStack[--currentScopeIdx];
|
|
}
|
|
|
|
|
|
/*
|
|
* Return non-zero if the current input should be parsed rather than be
|
|
* skipped.
|
|
*/
|
|
static int notSkipping()
|
|
{
|
|
return (skipStackPtr == 0 ? TRUE : skipStack[skipStackPtr - 1]);
|
|
}
|
|
|
|
|
|
/*
|
|
* Return the value of an expression involving a time period.
|
|
*/
|
|
static int timePeriod(char *lname,char *uname)
|
|
{
|
|
int this, line;
|
|
qualDef *qd, *lower, *upper;
|
|
moduleDef *mod;
|
|
|
|
if (lname == NULL)
|
|
lower = NULL;
|
|
else if ((lower = findQualifier(lname)) == NULL || lower -> qtype != time_qualifier)
|
|
yyerror("Lower bound is not a time version");
|
|
|
|
if (uname == NULL)
|
|
upper = NULL;
|
|
else if ((upper = findQualifier(uname)) == NULL || upper -> qtype != time_qualifier)
|
|
yyerror("Upper bound is not a time version");
|
|
|
|
/* Sanity checks on the bounds. */
|
|
|
|
if (lower == NULL && upper == NULL)
|
|
yyerror("Lower and upper bounds cannot both be omitted");
|
|
|
|
if (lower != NULL && upper != NULL)
|
|
{
|
|
if (lower -> module != upper -> module || lower -> line != upper -> line)
|
|
yyerror("Lower and upper bounds are from different timelines");
|
|
|
|
if (lower == upper)
|
|
yyerror("Lower and upper bounds must be different");
|
|
|
|
if (lower -> order > upper -> order)
|
|
yyerror("Later version specified as lower bound");
|
|
}
|
|
|
|
/* Go through each slot in the relevant timeline. */
|
|
|
|
if (lower != NULL)
|
|
{
|
|
mod = lower -> module;
|
|
line = lower -> line;
|
|
}
|
|
else
|
|
{
|
|
mod = upper -> module;
|
|
line = upper -> line;
|
|
}
|
|
|
|
this = FALSE;
|
|
|
|
for (qd = mod -> qualifiers; qd != NULL; qd = qd -> next)
|
|
{
|
|
if (qd -> qtype != time_qualifier || qd -> line != line)
|
|
continue;
|
|
|
|
if (lower != NULL && qd -> order < lower -> order)
|
|
continue;
|
|
|
|
if (upper != NULL && qd -> order >= upper -> order)
|
|
continue;
|
|
|
|
/*
|
|
* This is within the required range so if it is also needed
|
|
* then the expression is true.
|
|
*/
|
|
|
|
if (isNeeded(qd))
|
|
{
|
|
this = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return the value of an expression involving a single platform or feature.
|
|
*/
|
|
static int platOrFeature(char *name,int optnot)
|
|
{
|
|
int this;
|
|
qualDef *qd;
|
|
|
|
if ((qd = findQualifier(name)) == NULL || qd -> qtype == time_qualifier)
|
|
yyerror("No such platform or feature");
|
|
|
|
/* Assume this sub-expression is false. */
|
|
|
|
this = FALSE;
|
|
|
|
if (qd -> qtype == feature_qualifier)
|
|
{
|
|
if (!excludedFeature(excludedQualifiers,qd))
|
|
this = TRUE;
|
|
}
|
|
else if (isNeeded(qd))
|
|
this = TRUE;
|
|
|
|
if (optnot)
|
|
this = !this;
|
|
|
|
return this;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return TRUE if the given qualifier is excluded.
|
|
*/
|
|
int excludedFeature(stringList *xsl,qualDef *qd)
|
|
{
|
|
while (xsl != NULL)
|
|
{
|
|
if (strcmp(qd -> name,xsl -> s) == 0)
|
|
return TRUE;
|
|
|
|
xsl = xsl -> next;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return TRUE if the given qualifier is needed.
|
|
*/
|
|
static int isNeeded(qualDef *qd)
|
|
{
|
|
stringList *sl;
|
|
|
|
for (sl = neededQualifiers; sl != NULL; sl = sl -> next)
|
|
if (strcmp(qd -> name,sl -> s) == 0)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return the current scope. currentScope() is only valid if notSkipping()
|
|
* returns non-zero.
|
|
*/
|
|
static classDef *currentScope(void)
|
|
{
|
|
return (currentScopeIdx > 0 ? scopeStack[currentScopeIdx - 1] : NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* Create a new qualifier.
|
|
*/
|
|
static void newQualifier(moduleDef *mod, int line, int order, char *name,
|
|
qualType qt)
|
|
{
|
|
qualDef *qd;
|
|
|
|
/* Check it doesn't already exist. */
|
|
|
|
if (findQualifier(name) != NULL)
|
|
yyerror("Version is already defined");
|
|
|
|
qd = sipMalloc(sizeof (qualDef));
|
|
qd->name = name;
|
|
qd->qtype = qt;
|
|
qd->module = mod;
|
|
qd->line = line;
|
|
qd->order = order;
|
|
qd->next = mod -> qualifiers;
|
|
mod->qualifiers = qd;
|
|
}
|
|
|
|
|
|
/*
|
|
* Create a new imported module.
|
|
*/
|
|
static void newImport(char *filename)
|
|
{
|
|
moduleDef *from, *mod;
|
|
moduleListDef *mld;
|
|
|
|
/* Create a new module if it has not already been defined. */
|
|
for (mod = currentSpec->modules; mod != NULL; mod = mod->next)
|
|
if (strcmp(mod->file, filename) == 0)
|
|
break;
|
|
|
|
from = currentModule;
|
|
|
|
if (mod == NULL)
|
|
{
|
|
newModule(NULL, filename);
|
|
mod = currentModule;
|
|
}
|
|
else if (from->encoding == no_type)
|
|
{
|
|
/* Import any defaults from the already parsed module. */
|
|
from->encoding = mod->encoding;
|
|
}
|
|
|
|
/* Add the new import unless it has already been imported. */
|
|
for (mld = from->imports; mld != NULL; mld = mld->next)
|
|
if (mld->module == mod)
|
|
return;
|
|
|
|
mld = sipMalloc(sizeof (moduleListDef));
|
|
mld->module = mod;
|
|
mld->next = from->imports;
|
|
|
|
from->imports = mld;
|
|
}
|
|
|
|
|
|
/*
|
|
* Set up pointers to hook names.
|
|
*/
|
|
static void getHooks(optFlags *optflgs,char **pre,char **post)
|
|
{
|
|
optFlag *of;
|
|
|
|
if ((of = findOptFlag(optflgs,"PreHook",name_flag)) != NULL)
|
|
*pre = of -> fvalue.sval;
|
|
else
|
|
*pre = NULL;
|
|
|
|
if ((of = findOptFlag(optflgs,"PostHook",name_flag)) != NULL)
|
|
*post = of -> fvalue.sval;
|
|
else
|
|
*post = NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get the /Transfer/ option flag.
|
|
*/
|
|
static int getTransfer(optFlags *optflgs)
|
|
{
|
|
return (findOptFlag(optflgs, "Transfer", bool_flag) != NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* Get the /ReleaseGIL/ option flag.
|
|
*/
|
|
static int getReleaseGIL(optFlags *optflgs)
|
|
{
|
|
return (findOptFlag(optflgs, "ReleaseGIL", bool_flag) != NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* Get the /HoldGIL/ option flag.
|
|
*/
|
|
static int getHoldGIL(optFlags *optflgs)
|
|
{
|
|
return (findOptFlag(optflgs, "HoldGIL", bool_flag) != NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* Get the /Deprecated/ option flag.
|
|
*/
|
|
static int getDeprecated(optFlags *optflgs)
|
|
{
|
|
return (findOptFlag(optflgs, "Deprecated", bool_flag) != NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* Get the /AllowNone/ option flag.
|
|
*/
|
|
static int getAllowNone(optFlags *optflgs)
|
|
{
|
|
return (findOptFlag(optflgs, "AllowNone", bool_flag) != NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* Get the /DocType/ option flag.
|
|
*/
|
|
static const char *getDocType(optFlags *optflgs)
|
|
{
|
|
optFlag *of = findOptFlag(optflgs, "DocType", string_flag);
|
|
|
|
if (of == NULL)
|
|
return NULL;
|
|
|
|
return of->fvalue.sval;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get the /DocValue/ option flag.
|
|
*/
|
|
static const char *getDocValue(optFlags *optflgs)
|
|
{
|
|
optFlag *of = findOptFlag(optflgs, "DocValue", string_flag);
|
|
|
|
if (of == NULL)
|
|
return NULL;
|
|
|
|
return of->fvalue.sval;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return TRUE if the PyQt3 plugin was specified.
|
|
*/
|
|
int pluginPyTQt3(sipSpec *pt)
|
|
{
|
|
return stringFind(pt->plugins, "PyQt3");
|
|
}
|
|
|
|
|
|
/*
|
|
* Return TRUE if the PyQt4 plugin was specified.
|
|
*/
|
|
int pluginPyQt4(sipSpec *pt)
|
|
{
|
|
return stringFind(pt->plugins, "PyQt4");
|
|
}
|
|
|
|
|
|
/*
|
|
* Return TRUE if a list of strings contains a given entry.
|
|
*/
|
|
static int stringFind(stringList *sl, const char *s)
|
|
{
|
|
while (sl != NULL)
|
|
{
|
|
if (strcmp(sl->s, s) == 0)
|
|
return TRUE;
|
|
|
|
sl = sl->next;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Set the name of a module.
|
|
*/
|
|
static void setModuleName(sipSpec *pt, moduleDef *mod, const char *fullname)
|
|
{
|
|
mod->fullname = cacheName(pt, fullname);
|
|
|
|
if (inMainModule())
|
|
setIsUsedName(mod->fullname);
|
|
|
|
if ((mod->name = strrchr(fullname, '.')) != NULL)
|
|
mod->name++;
|
|
else
|
|
mod->name = fullname;
|
|
}
|
|
|
|
|
|
/*
|
|
* Define a new class and set its name.
|
|
*/
|
|
static void defineClass(scopedNameDef *snd, classList *supers, optFlags *of)
|
|
{
|
|
classDef *cd, *c_scope = currentScope();
|
|
|
|
cd = newClass(currentSpec, class_iface, getAPIRange(of),
|
|
scopeScopedName((c_scope != NULL ? c_scope->iff : NULL), snd));
|
|
cd->supers = supers;
|
|
|
|
pushScope(cd);
|
|
}
|
|
|
|
|
|
/*
|
|
* Complete the definition of a class.
|
|
*/
|
|
static classDef *completeClass(scopedNameDef *snd, optFlags *of, int has_def)
|
|
{
|
|
classDef *cd = currentScope();
|
|
|
|
/* See if the class was defined or just declared. */
|
|
if (has_def)
|
|
{
|
|
if (snd->next != NULL)
|
|
yyerror("A scoped name cannot be given in a class/struct definition");
|
|
|
|
}
|
|
else if (cd->supers != NULL)
|
|
yyerror("Class/struct has super-classes but no definition");
|
|
else
|
|
setIsOpaque(cd);
|
|
|
|
finishClass(currentSpec, currentModule, cd, of);
|
|
popScope();
|
|
|
|
/*
|
|
* Check that external classes have only been declared at the global scope.
|
|
*/
|
|
if (isExternal(cd) && currentScope() != NULL)
|
|
yyerror("External classes/structs can only be declared in the global scope");
|
|
|
|
return cd;
|
|
}
|
|
|
|
|
|
/*
|
|
* Add a variable to the list so that the list remains sorted.
|
|
*/
|
|
static void addVariable(sipSpec *pt, varDef *vd)
|
|
{
|
|
varDef **at = &pt->vars;
|
|
|
|
while (*at != NULL)
|
|
{
|
|
if (strcmp(vd->pyname->text, (*at)->pyname->text) < 0)
|
|
break;
|
|
|
|
at = &(*at)->next;
|
|
}
|
|
|
|
vd->next = *at;
|
|
*at = vd;
|
|
}
|
|
|
|
|
|
/*
|
|
* Update a type according to optional flags.
|
|
*/
|
|
static void applyTypeFlags(moduleDef *mod, argDef *ad, optFlags *flags)
|
|
{
|
|
ad->doctype = getDocType(flags);
|
|
|
|
if (ad->atype == string_type && !isArray(ad) && !isReference(ad))
|
|
{
|
|
optFlag *of;
|
|
|
|
if ((of = findOptFlag(flags, "Encoding", string_flag)) == NULL)
|
|
{
|
|
if (mod->encoding != no_type)
|
|
ad->atype = mod->encoding;
|
|
else
|
|
ad->atype = string_type;
|
|
}
|
|
else if ((ad->atype = convertEncoding(of->fvalue.sval)) == no_type)
|
|
yyerror("The value of the /Encoding/ annotation must be one of \"ASCII\", \"Latin-1\", \"UTF-8\" or \"None\"");
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Return the argument type for a string with the given encoding or no_type if
|
|
* the encoding was invalid.
|
|
*/
|
|
static argType convertEncoding(const char *encoding)
|
|
{
|
|
if (strcmp(encoding, "ASCII") == 0)
|
|
return ascii_string_type;
|
|
|
|
if (strcmp(encoding, "Latin-1") == 0)
|
|
return latin1_string_type;
|
|
|
|
if (strcmp(encoding, "UTF-8") == 0)
|
|
return utf8_string_type;
|
|
|
|
if (strcmp(encoding, "None") == 0)
|
|
return string_type;
|
|
|
|
return no_type;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get the /API/ option flag.
|
|
*/
|
|
static apiVersionRangeDef *getAPIRange(optFlags *optflgs)
|
|
{
|
|
optFlag *of;
|
|
|
|
if ((of = findOptFlag(optflgs, "API", api_range_flag)) == NULL)
|
|
return NULL;
|
|
|
|
return of->fvalue.aval;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return the API range structure and version number corresponding to the
|
|
* given API range.
|
|
*/
|
|
static apiVersionRangeDef *convertAPIRange(moduleDef *mod, nameDef *name,
|
|
int from, int to)
|
|
{
|
|
int index;
|
|
apiVersionRangeDef *avd, **avdp;
|
|
|
|
/* Handle the trivial case. */
|
|
if (from == 0 && to == 0)
|
|
return NULL;
|
|
|
|
for (index = 0, avdp = &mod->api_ranges; (*avdp) != NULL; avdp = &(*avdp)->next, ++index)
|
|
{
|
|
avd = *avdp;
|
|
|
|
if (avd->api_name == name && avd->from == from && avd->to == to)
|
|
return avd;
|
|
}
|
|
|
|
/* The new one must be appended so that version numbers remain valid. */
|
|
avd = sipMalloc(sizeof (apiVersionRangeDef));
|
|
|
|
avd->api_name = name;
|
|
avd->from = from;
|
|
avd->to = to;
|
|
avd->index = index;
|
|
|
|
avd->next = NULL;
|
|
*avdp = avd;
|
|
|
|
return avd;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return TRUE if a signature with annotations uses keyword arguments.
|
|
*/
|
|
static int usesKeywordArgs(optFlags *optflgs, signatureDef *sd)
|
|
{
|
|
int kwd_args_anno, no_kwd_args_anno;
|
|
|
|
kwd_args_anno = (findOptFlag(optflgs, "KeywordArgs", bool_flag) != NULL);
|
|
no_kwd_args_anno = (findOptFlag(optflgs, "NoKeywordArgs", bool_flag) != NULL);
|
|
|
|
/*
|
|
* An ellipsis cannot be used with keyword arguments. Only complain if it
|
|
* has been explicitly requested.
|
|
*/
|
|
if (kwd_args_anno && sd->nrArgs > 0 && sd->args[sd->nrArgs - 1].atype == ellipsis_type)
|
|
yyerror("/KeywordArgs/ cannot be specified for calls with a variable number of arguments");
|
|
|
|
if ((defaultKwdArgs || kwd_args_anno) && !no_kwd_args_anno)
|
|
{
|
|
int a, is_name = FALSE;
|
|
|
|
/*
|
|
* Mark argument names as being used and check there is at least one.
|
|
*/
|
|
for (a = 0; a < sd->nrArgs; ++a)
|
|
{
|
|
nameDef *nd = sd->args[a].name;
|
|
|
|
if (sd->args[a].name != NULL)
|
|
{
|
|
setIsUsedName(nd);
|
|
is_name = TRUE;
|
|
}
|
|
}
|
|
|
|
return is_name;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Extract the version of a string value optionally associated with a
|
|
* particular feature.
|
|
*/
|
|
static char *convertFeaturedString(char *fs)
|
|
{
|
|
while (fs != NULL)
|
|
{
|
|
char *next, *value;
|
|
|
|
/* Individual values are ';' separated. */
|
|
if ((next = strchr(fs, ';')) != NULL)
|
|
*next++ = '\0';
|
|
|
|
/* Features and values are ':' separated. */
|
|
if ((value = strchr(fs, ':')) == NULL)
|
|
{
|
|
/* This is an unconditional value so just return it. */
|
|
return strip(fs);
|
|
}
|
|
|
|
*value++ = '\0';
|
|
|
|
if (isEnabledFeature(strip(fs)))
|
|
return strip(value);
|
|
|
|
fs = next;
|
|
}
|
|
|
|
/* No value was enabled. */
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return the stripped version of a string.
|
|
*/
|
|
static char *strip(char *s)
|
|
{
|
|
while (*s == ' ')
|
|
++s;
|
|
|
|
if (*s != '\0')
|
|
{
|
|
char *cp = &s[strlen(s) - 1];
|
|
|
|
while (*cp == ' ')
|
|
*cp-- = '\0';
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return TRUE if the given feature is enabled.
|
|
*/
|
|
static int isEnabledFeature(const char *name)
|
|
{
|
|
qualDef *qd;
|
|
|
|
if ((qd = findQualifier(name)) == NULL || qd->qtype != feature_qualifier)
|
|
yyerror("No such feature");
|
|
|
|
return !excludedFeature(excludedQualifiers, qd);
|
|
}
|