1 /******************************************************************************
5 * Copyright (C) 1997-2015 by Dimitri van Heesch.
7 * Permission to use, copy, modify, and distribute this software and its
8 * documentation under the terms of the GNU General Public License is hereby
9 * granted. No representations are made about the suitability of this software
10 * for any purpose. It is provided "as is" without express or implied warranty.
11 * See the GNU General Public License for more details.
13 * Documents produced by Doxygen are derivative works derived from the
14 * input used in their production; they are not affected by this license.
19 * This scanner is used to convert a string into a list of function or
20 * template arguments. Each parsed argument results in a Argument struct,
21 * that is put into an ArgumentList in declaration order.
22 * Comment blocks for arguments can also be included in the string.
23 * The argument string does not contain new-lines (except inside any
25 * An Argument consists of the string fields:
26 * type,name,default value, and documentation
27 * The Argument list as a whole can be pure, constant or volatile.
29 * Examples of input strings are:
31 * "(int a,int b) const"
32 * "(const char *s="hello world",int=5) = 0"
34 * "(char c,const char)"
37 * Note: It is not always possible to distinguish between the name and
38 * type of an argument. In case of doubt the name is added to the
39 * type, and the matchArgumentList in util.cpp is be used to
40 * further determine the correct separation.
42 %option never-interactive
43 %option prefix="defargsYY"
51 //#include <iostream.h>
55 #include <qcstringlist.h>
60 #include "arguments.h"
64 #define YY_NO_UNISTD_H 1
66 /* -----------------------------------------------------------------
69 static const char *g_inputString;
70 static int g_inputPosition;
71 static ArgumentList *g_argList;
72 static QCString *g_copyArgValue;
73 static QCString g_curArgTypeName;
74 static QCString g_curArgDefValue;
75 static QCString g_curArgName;
76 static QCString g_curArgDocs;
77 static QCString g_curArgAttrib;
78 static QCString g_curArgArray;
79 static QCString g_curTypeConstraint;
80 static QCString g_extraTypeChars;
81 static int g_argRoundCount;
82 static int g_argSharpCount;
83 static int g_argCurlyCount;
84 static int g_readArgContext;
85 static int g_lastDocContext;
86 static int g_lastDocChar;
87 static int g_lastExtendsContext;
88 static QCString g_delimiter;
90 /* -----------------------------------------------------------------
93 #define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size);
95 static int yyread(char *buf,int max_size)
98 while( c < max_size && g_inputString[g_inputPosition] )
100 *buf = g_inputString[g_inputPosition++] ;
107 static bool nameIsActuallyPartOfType(QCString &name)
109 static bool first=TRUE;
110 static QDict<void> keywords(17);
111 if (first) // fill keyword dict first time
113 #define DUMMY_ADDR (void*)0x8
114 keywords.insert("unsigned", DUMMY_ADDR); // foo(... unsigned)
115 keywords.insert("signed", DUMMY_ADDR); // foo(... signed)
116 keywords.insert("bool", DUMMY_ADDR); // foo(... bool)
117 keywords.insert("char", DUMMY_ADDR); // foo(... char)
118 keywords.insert("char8_t", DUMMY_ADDR); // foo(... char8_t)
119 keywords.insert("char16_t", DUMMY_ADDR); // foo(... char16_t)
120 keywords.insert("char32_t", DUMMY_ADDR); // foo(... char32_t)
121 keywords.insert("int", DUMMY_ADDR); // foo(... int)
122 keywords.insert("short", DUMMY_ADDR); // foo(... short)
123 keywords.insert("long", DUMMY_ADDR); // foo(... long)
124 keywords.insert("float", DUMMY_ADDR); // foo(... float)
125 keywords.insert("double", DUMMY_ADDR); // foo(... double)
126 keywords.insert("int8_t", DUMMY_ADDR); // foo(... int8_t)
127 keywords.insert("uint8_t", DUMMY_ADDR); // foo(... uint8_t)
128 keywords.insert("int16_t", DUMMY_ADDR); // foo(... int16_t)
129 keywords.insert("uint16_t", DUMMY_ADDR); // foo(... uint16_t)
130 keywords.insert("int32_t", DUMMY_ADDR); // foo(... int32_t)
131 keywords.insert("uint32_t", DUMMY_ADDR); // foo(... uint32_t)
132 keywords.insert("const", DUMMY_ADDR); // foo(... const)
133 keywords.insert("volatile", DUMMY_ADDR); // foo(... volatile)
136 return name.length()>0 && keywords.find(name)!=0;
141 ID [a-z_A-Z\x80-\xFF][a-z_A-Z0-9\x80-\xFF]*
142 RAWBEGIN (u|U|L|u8)?R\"[^ \t\(\)\\]{0,16}"("
143 RAWEND ")"[^ \t\(\)\\]{0,16}\"
160 %x ReadTypeConstraint
166 <Start>[<(] { BEGIN(ReadFuncArgType); }
168 <ReadFuncArgType>{B}* {
169 g_curArgTypeName+=" ";
171 <ReadFuncArgType>"["[^\]]*"]" {
172 if (g_curArgTypeName.stripWhiteSpace().isEmpty())
174 g_curArgAttrib=yytext; // for M$-IDL
178 g_curArgArray+=yytext;
181 <ReadFuncArgDef>"'"\\[0-7]{1,3}"'" { g_curArgDefValue+=yytext; }
182 <ReadFuncArgDef>"'"\\."'" { g_curArgDefValue+=yytext; }
183 <ReadFuncArgDef>"'"."'" { g_curArgDefValue+=yytext; }
184 <ReadFuncArgDef>{RAWBEGIN} { g_curArgDefValue+=yytext;
185 QCString text=yytext;
186 int i=text.find('"');
187 g_delimiter = yytext+i+1;
188 g_delimiter=g_delimiter.left(g_delimiter.length()-1);
189 BEGIN( CopyRawString );
192 g_curArgDefValue+=*yytext;
193 BEGIN( CopyArgString );
195 <ReadFuncArgType>"("([^:)]+{B}*"::")*{B}*[&*\^]+{B}*/{ID} {
196 // function pointer as argument
197 g_curArgTypeName+=yytext;
198 //g_curArgTypeName=g_curArgTypeName.simplifyWhiteSpace();
199 BEGIN( ReadFuncArgPtr );
201 <ReadFuncArgPtr>{ID} {
204 <ReadFuncArgPtr>")"{B}*"(" { // function pointer
205 g_curArgTypeName+=yytext;
206 //g_curArgTypeName=g_curArgTypeName.simplifyWhiteSpace();
207 g_readArgContext = ReadFuncArgType;
208 g_copyArgValue=&g_curArgTypeName;
210 BEGIN( CopyArgRound2 );
212 <ReadFuncArgPtr>")"/{B}*"[" { // pointer to fixed size array
213 g_curArgTypeName+=yytext;
214 g_curArgTypeName+=g_curArgName;
215 //g_curArgTypeName=g_curArgTypeName.simplifyWhiteSpace();
216 BEGIN( ReadFuncArgType );
218 <ReadFuncArgPtr>")" { // redundant braces detected / remove them
219 int i=g_curArgTypeName.findRev('('),l=g_curArgTypeName.length();
221 g_curArgTypeName=g_curArgTypeName.left(i)+
222 g_curArgTypeName.right(l-i-1);
223 g_curArgTypeName+=g_curArgName;
224 BEGIN( ReadFuncArgType );
226 <ReadFuncArgType>"<="|">="|"->"|">>"|"<<" { // handle operators in defargs
227 g_curArgTypeName+=yytext;
229 <ReadFuncArgType,ReadFuncArgDef>[({<] {
230 if (YY_START==ReadFuncArgType)
232 g_curArgTypeName+=*yytext;
233 g_copyArgValue=&g_curArgTypeName;
235 else // YY_START==ReadFuncArgDef
237 g_curArgDefValue+=*yytext;
238 g_copyArgValue=&g_curArgDefValue;
240 g_readArgContext = YY_START;
244 BEGIN( CopyArgRound );
246 else if (*yytext=='{')
249 BEGIN( CopyArgCurly );
255 BEGIN( CopyArgSharp );
258 <CopyArgRound,CopyArgRound2>"(" {
260 *g_copyArgValue += *yytext;
262 <CopyArgRound,CopyArgRound2>")"({B}*{ID})* {
263 *g_copyArgValue += yytext;
264 if (g_argRoundCount>0)
270 if (YY_START==CopyArgRound2)
272 *g_copyArgValue+=" "+g_curArgName;
274 BEGIN( g_readArgContext );
277 <CopyArgRound>")"/{B}* {
278 *g_copyArgValue += *yytext;
279 if (g_argRoundCount>0) g_argRoundCount--;
280 else BEGIN( g_readArgContext );
283 if (g_argRoundCount>0)
285 *g_copyArgValue += yytext;
292 <CopyArgSharp>">>)" { // combined token (see bug 790320)
293 *g_copyArgValue += yytext;
294 if (g_argSharpCount>0) g_argSharpCount--;
295 else BEGIN( g_readArgContext );
296 if (g_argSharpCount>0) g_argSharpCount--;
297 else BEGIN( g_readArgContext );
301 if (g_argRoundCount>0)
303 *g_copyArgValue += yytext;
312 *g_copyArgValue += *yytext;
315 *g_copyArgValue += *yytext;
316 if (g_argSharpCount>0) g_argSharpCount--;
317 else BEGIN( g_readArgContext );
321 *g_copyArgValue += *yytext;
325 *g_copyArgValue += *yytext;
329 *g_copyArgValue += *yytext;
332 *g_copyArgValue += *yytext;
333 if (g_argCurlyCount>0) g_argCurlyCount--;
334 else BEGIN( g_readArgContext );
337 g_curArgDefValue+=yytext;
339 <CopyRawString>{RAWEND} {
340 g_curArgDefValue+=yytext;
341 QCString delimiter = yytext+1;
342 delimiter=delimiter.left(delimiter.length()-1);
343 if (delimiter==g_delimiter)
345 BEGIN( ReadFuncArgDef );
349 g_curArgDefValue+=*yytext;
350 BEGIN( ReadFuncArgDef );
352 <ReadFuncArgType>"=" {
353 BEGIN( ReadFuncArgDef );
355 <ReadFuncArgType,ReadFuncArgDef>[,)>]{B}*("/*"[*!]|"//"[/!])"<" {
356 g_lastDocContext=YY_START;
357 g_lastDocChar=*yytext;
358 QCString text=yytext;
359 if (text.find("//")!=-1)
360 BEGIN( ReadDocLine );
362 BEGIN( ReadDocBlock );
364 <ReadFuncArgType,ReadFuncArgDef>[,)>] {
365 if (*yytext==')' && g_curArgTypeName.stripWhiteSpace().isEmpty())
367 g_curArgTypeName+=*yytext;
372 g_curArgTypeName=removeRedundantWhiteSpace(g_curArgTypeName);
373 g_curArgDefValue=g_curArgDefValue.stripWhiteSpace();
374 //printf("curArgType=`%s' curArgDefVal=`%s'\n",g_curArgTypeName.data(),g_curArgDefValue.data());
375 int l=g_curArgTypeName.length();
379 while (i>=0 && (isspace((uchar)g_curArgTypeName.at(i)) || g_curArgTypeName.at(i)=='.')) i--;
380 while (i>=0 && (isId(g_curArgTypeName.at(i)) || g_curArgTypeName.at(i)=='$')) i--;
381 Argument *a = new Argument;
382 a->attrib = g_curArgAttrib.copy();
383 a->typeConstraint = g_curTypeConstraint.stripWhiteSpace();
384 //printf("a->type=%s a->name=%s i=%d l=%d\n",
385 // a->type.data(),a->name.data(),i,l);
387 if (i==l-1 && g_curArgTypeName.at(i)==')') // function argument
389 int bi=g_curArgTypeName.find('(');
391 //printf("func arg fi=%d\n",fi);
392 while (fi>=0 && (isId(g_curArgTypeName.at(fi)) || g_curArgTypeName.at(fi)==':')) fi--;
395 a->type = g_curArgTypeName.left(fi+1);
396 a->name = g_curArgTypeName.mid(fi+1,bi-fi-1).stripWhiteSpace();
397 a->array = g_curArgTypeName.right(l-bi);
401 a->type = g_curArgTypeName;
404 else if (i>=0 && g_curArgTypeName.at(i)!=':')
405 { // type contains a name
406 a->type = removeRedundantWhiteSpace(g_curArgTypeName.left(i+1)).stripWhiteSpace();
407 a->name = g_curArgTypeName.right(l-i-1).stripWhiteSpace();
409 // if the type becomes a type specifier only then we make a mistake
410 // and need to correct it to avoid seeing a nameless parameter
411 // "struct A" as a parameter with type "struct" and name "A".
413 if (a->type.left(6)=="const ") sv=6;
414 else if (a->type.left(9)=="volatile ") sv=9;
416 if (a->type.mid(sv)=="struct" ||
417 a->type.mid(sv)=="union" ||
418 a->type.mid(sv)=="class" ||
419 a->type.mid(sv)=="typename" ||
420 nameIsActuallyPartOfType(a->name)
423 a->type = a->type + " " + a->name;
426 //printf(" --> a->type='%s' a->name='%s'\n",a->type.data(),a->name.data());
428 else // assume only the type was specified, try to determine name later
430 a->type = removeRedundantWhiteSpace(g_curArgTypeName);
432 if (!a->type.isEmpty() && a->type.at(0)=='$') // typeless PHP name?
437 a->array += removeRedundantWhiteSpace(g_curArgArray);
438 //printf("array=%s\n",a->array.data());
439 int alen = a->array.length();
440 if (alen>2 && a->array.at(0)=='(' &&
441 a->array.at(alen-1)==')') // fix-up for int *(a[10])
443 int i=a->array.find('[')-1;
444 a->array = a->array.mid(1,alen-2);
445 if (i>0 && a->name.isEmpty())
447 a->name = a->array.left(i).stripWhiteSpace();
448 a->array = a->array.mid(i);
451 a->defval = g_curArgDefValue.copy();
452 //printf("a->type=%s a->name=%s a->defval=\"%s\"\n",a->type.data(),a->name.data(),a->defval.data());
453 a->docs = g_curArgDocs.stripWhiteSpace();
454 //printf("Argument `%s' `%s' adding docs=`%s'\n",a->type.data(),a->name.data(),a->docs.data());
455 g_argList->append(a);
457 g_curArgAttrib.resize(0);
458 g_curArgTypeName.resize(0);
459 g_curArgDefValue.resize(0);
460 g_curArgArray.resize(0);
461 g_curArgDocs.resize(0);
462 g_curTypeConstraint.resize(0);
466 //printf(">>> end of argument list\n");
470 BEGIN( ReadFuncArgType );
474 <ReadFuncArgType,ReadFuncArgPtr>"extends" {
475 g_curTypeConstraint.resize(0);
476 g_lastExtendsContext=YY_START;
477 BEGIN(ReadTypeConstraint);
479 <ReadFuncArgType,ReadFuncArgPtr>"$"?{ID} {
480 QCString name=yytext; //resolveDefines(yytext);
481 if (YY_START==ReadFuncArgType && g_curArgArray=="[]") // Java style array
483 g_curArgTypeName+=" []";
484 g_curArgArray.resize(0);
486 //printf("resolveName `%s'->`%s'\n",yytext,name.data());
487 g_curArgTypeName+=name;
489 <ReadFuncArgType,ReadFuncArgPtr>. {
490 g_curArgTypeName+=*yytext;
493 <ReadFuncArgDef,CopyArgString>"<="|"->"|">="|">>"|"<<" {
494 g_curArgDefValue+=yytext;
496 <ReadFuncArgDef,CopyArgString,CopyRawString>. {
497 g_curArgDefValue+=*yytext;
499 <CopyArgRound,CopyArgRound2,CopyArgSharp,CopyArgCurly>{ID} {
500 QCString name=yytext; //resolveDefines(yytext);
501 *g_copyArgValue+=name;
503 <CopyArgRound,CopyArgRound2,CopyArgSharp,CopyArgCurly>. {
504 *g_copyArgValue += *yytext;
506 <ReadTypeConstraint>[,)>] {
508 BEGIN(g_lastExtendsContext);
510 <ReadTypeConstraint>. {
511 g_curTypeConstraint+=yytext;
513 <ReadTypeConstraint>\n {
514 g_curTypeConstraint+=' ';
517 g_argList->constSpecifier=TRUE;
519 <FuncQual>"volatile" {
520 g_argList->volatileSpecifier=TRUE;
523 g_argList->refQualifier=RefQualifierLValue;
526 g_argList->refQualifier=RefQualifierRValue;
528 <FuncQual,TrailingReturn>"="{B}*"0" {
529 g_argList->pureSpecifier=TRUE;
532 <FuncQual>"->" { // C++11 trailing return type
533 g_argList->trailingReturnType=" -> ";
534 BEGIN(TrailingReturn);
536 <TrailingReturn>{B}/("final"|"override"){B}* {
541 g_argList->trailingReturnType+=yytext;
544 g_argList->trailingReturnType+=yytext;
546 <FuncQual>")"{B}*"["[^]]*"]" { // for functions returning a pointer to an array,
547 // i.e. ")[]" in "int (*f(int))[4]" with argsString="(int))[4]"
548 g_extraTypeChars=yytext;
550 <ReadDocBlock>[^\*\n]+ {
551 g_curArgDocs+=yytext;
553 <ReadDocLine>[^\n]+ {
554 g_curArgDocs+=yytext;
557 if (g_lastDocChar!=0)
558 unput(g_lastDocChar);
559 BEGIN(g_lastDocContext);
562 if (g_lastDocChar!=0)
563 unput(g_lastDocChar);
564 BEGIN(g_lastDocContext);
567 g_curArgDocs+=*yytext;
570 g_curArgDocs+=*yytext;
572 <*>("/*"[*!]|"//"[/!])("<"?) {
573 g_lastDocContext=YY_START;
576 BEGIN( ReadDocLine );
578 BEGIN( ReadDocBlock );
585 /* ----------------------------------------------------------------------------
588 /*! Converts an argument string into an ArgumentList.
589 * \param[in] argsString the list of Arguments.
590 * \param[out] al a reference to resulting argument list pointer.
591 * \param[out] extraTypeChars point to string to which trailing characters
592 * for complex types are written to
595 void stringToArgumentList(const char *argsString,ArgumentList* al,QCString *extraTypeChars)
598 if (argsString==0) return;
599 printlex(yy_flex_debug, TRUE, __FILE__, NULL);
602 g_curArgDocs.resize(0);
603 g_curArgAttrib.resize(0);
604 g_curArgArray.resize(0);
605 g_curTypeConstraint.resize(0);
606 g_extraTypeChars.resize(0);
612 g_inputString = argsString;
614 g_curArgTypeName.resize(0);
615 g_curArgDefValue.resize(0);
616 g_curArgName.resize(0);
618 defargsYYrestart( defargsYYin );
621 if (extraTypeChars) *extraTypeChars=g_extraTypeChars;
622 //printf("stringToArgumentList(%s) result=%s\n",argsString,argListToString(al).data());
623 printlex(yy_flex_debug, FALSE, __FILE__, NULL);
626 #if !defined(YY_FLEX_SUBMINOR_VERSION)
627 extern "C" { // some bogus code to keep the compiler happy
628 void defargsYYdummy() { yy_flex_realloc(0,0); }