1 /* -----------------------------------------------------------------------------
2  * This file is part of SWIG, which is licensed as a whole under version 3
3  * (or any later version) of the GNU General Public License. Some additional
4  * terms also apply to certain portions of SWIG. The full details of the SWIG
5  * license and copyrights can be found in the LICENSE and COPYRIGHT files
6  * included with the SWIG source code as distributed by the SWIG developers
7  * and at http://www.swig.org/legal.html.
8  *
9  * scanner.c
10  *
11  * SWIG tokenizer.  This file is a wrapper around the generic C scanner
12  * found in Swig/scanner.c.   Extra logic is added both to accommodate the
13  * bison-based grammar and certain peculiarities of C++ parsing (e.g.,
14  * operator overloading, typedef resolution, etc.).  This code also splits
15  * C identifiers up into keywords and SWIG directives.
16  * ----------------------------------------------------------------------------- */
17 
18 #include "cparse.h"
19 #include "parser.h"
20 #include <string.h>
21 #include <ctype.h>
22 
23 /* Scanner object */
24 static Scanner *scan = 0;
25 
26 /* Global string containing C code. Used by the parser to grab code blocks */
27 String *scanner_ccode = 0;
28 
29 /* The main file being parsed */
30 static String *main_input_file = 0;
31 
32 /* Error reporting/location information */
33 int     cparse_line = 1;
34 String *cparse_file = 0;
35 int     cparse_start_line = 0;
36 
37 /* C++ mode */
38 int cparse_cplusplus = 0;
39 
40 /* Generate C++ compatible code when wrapping C code */
41 int cparse_cplusplusout = 0;
42 
43 /* To allow better error reporting */
44 String *cparse_unknown_directive = 0;
45 
46 /* Private vars */
47 static int scan_init = 0;
48 static int num_brace = 0;
49 static int last_brace = 0;
50 static int last_id = 0;
51 static int rename_active = 0;
52 
53 /* Doxygen comments scanning */
54 int scan_doxygen_comments = 0;
55 
isStructuralDoxygen(String * s)56 int isStructuralDoxygen(String *s) {
57   static const char* const structuralTags[] = {
58     "addtogroup",
59     "callgraph",
60     "callergraph",
61     "category",
62     "def",
63     "defgroup",
64     "dir",
65     "example",
66     "file",
67     "headerfile",
68     "internal",
69     "mainpage",
70     "name",
71     "nosubgrouping",
72     "overload",
73     "package",
74     "page",
75     "protocol",
76     "relates",
77     "relatesalso",
78     "showinitializer",
79     "weakgroup",
80   };
81 
82   unsigned n;
83   char *slashPointer = Strchr(s, '\\');
84   char *atPointer = Strchr(s,'@');
85   if (slashPointer == NULL && atPointer == NULL)
86     return 0;
87   else if(slashPointer == NULL)
88     slashPointer = atPointer;
89 
90   slashPointer++; /* skip backslash or at sign */
91 
92   for (n = 0; n < sizeof(structuralTags)/sizeof(structuralTags[0]); n++) {
93     const size_t len = strlen(structuralTags[n]);
94     if (strncmp(slashPointer, structuralTags[n], len) == 0) {
95       /* Take care to avoid false positives with prefixes of other tags. */
96       if (slashPointer[len] == '\0' || isspace(slashPointer[len]))
97 	return 1;
98     }
99   }
100 
101   return 0;
102 }
103 
104 /* -----------------------------------------------------------------------------
105  * Swig_cparse_cplusplus()
106  * ----------------------------------------------------------------------------- */
107 
Swig_cparse_cplusplus(int v)108 void Swig_cparse_cplusplus(int v) {
109   cparse_cplusplus = v;
110 }
111 
112 /* -----------------------------------------------------------------------------
113  * Swig_cparse_cplusplusout()
114  * ----------------------------------------------------------------------------- */
115 
Swig_cparse_cplusplusout(int v)116 void Swig_cparse_cplusplusout(int v) {
117   cparse_cplusplusout = v;
118 }
119 
120 /* ----------------------------------------------------------------------------
121  * scanner_init()
122  *
123  * Initialize buffers
124  * ------------------------------------------------------------------------- */
125 
scanner_init()126 void scanner_init() {
127   scan = NewScanner();
128   Scanner_idstart(scan,"%");
129   scan_init = 1;
130   scanner_ccode = NewStringEmpty();
131 }
132 
133 /* ----------------------------------------------------------------------------
134  * scanner_file(DOHFile *f)
135  *
136  * Start reading from new file
137  * ------------------------------------------------------------------------- */
scanner_file(DOHFile * f)138 void scanner_file(DOHFile * f) {
139   if (!scan_init) scanner_init();
140   Scanner_clear(scan);
141   Scanner_push(scan,f);
142 }
143 
144 /* ----------------------------------------------------------------------------
145  * start_inline(char *text, int line)
146  *
147  * Take a chunk of text and recursively feed it back into the scanner.  Used
148  * by the %inline directive.
149  * ------------------------------------------------------------------------- */
150 
start_inline(char * text,int line)151 void start_inline(char *text, int line) {
152   String *stext = NewString(text);
153 
154   Seek(stext,0,SEEK_SET);
155   Setfile(stext,cparse_file);
156   Setline(stext,line);
157   Scanner_push(scan,stext);
158   Delete(stext);
159 }
160 
161 /* -----------------------------------------------------------------------------
162  * skip_balanced()
163  *
164  * Skips a piece of code enclosed in begin/end symbols such as '{...}' or
165  * (...).  Ignores symbols inside comments or strings.
166  * ----------------------------------------------------------------------------- */
167 
skip_balanced(int startchar,int endchar)168 void skip_balanced(int startchar, int endchar) {
169   int start_line = Scanner_line(scan);
170   Clear(scanner_ccode);
171 
172   if (Scanner_skip_balanced(scan,startchar,endchar) < 0) {
173     Swig_error(cparse_file, start_line, "Missing '%c'. Reached end of input.\n", endchar);
174     return;
175   }
176 
177   cparse_line = Scanner_line(scan);
178   cparse_file = Scanner_file(scan);
179 
180   Append(scanner_ccode, Scanner_text(scan));
181   if (endchar == '}')
182     num_brace--;
183   return;
184 }
185 
186 /* -----------------------------------------------------------------------------
187  * get_raw_text_balanced()
188  *
189  * Returns raw text between 2 braces
190  * ----------------------------------------------------------------------------- */
191 
get_raw_text_balanced(int startchar,int endchar)192 String *get_raw_text_balanced(int startchar, int endchar) {
193   return Scanner_get_raw_text_balanced(scan, startchar, endchar);
194 }
195 
196 /* ----------------------------------------------------------------------------
197  * void skip_decl(void)
198  *
199  * This tries to skip over an entire declaration.   For example
200  *
201  *  friend ostream& operator<<(ostream&, const char *s);
202  *
203  * or
204  *  friend ostream& operator<<(ostream&, const char *s) { }
205  *
206  * ------------------------------------------------------------------------- */
207 
skip_decl(void)208 void skip_decl(void) {
209   int tok;
210   int done = 0;
211   int start_line = Scanner_line(scan);
212 
213   while (!done) {
214     tok = Scanner_token(scan);
215     if (tok == 0) {
216       if (!Swig_error_count()) {
217 	Swig_error(cparse_file, start_line, "Missing semicolon. Reached end of input.\n");
218       }
219       return;
220     }
221     if (tok == SWIG_TOKEN_LBRACE) {
222       if (Scanner_skip_balanced(scan,'{','}') < 0) {
223 	Swig_error(cparse_file, start_line, "Missing '}'. Reached end of input.\n");
224       }
225       break;
226     }
227     if (tok == SWIG_TOKEN_SEMI) {
228       done = 1;
229     }
230   }
231   cparse_file = Scanner_file(scan);
232   cparse_line = Scanner_line(scan);
233 }
234 
235 /* ----------------------------------------------------------------------------
236  * int yylook()
237  *
238  * Lexical scanner.
239  * ------------------------------------------------------------------------- */
240 
yylook(void)241 static int yylook(void) {
242 
243   int tok = 0;
244 
245   while (1) {
246     if ((tok = Scanner_token(scan)) == 0)
247       return 0;
248     if (tok == SWIG_TOKEN_ERROR)
249       return 0;
250     cparse_start_line = Scanner_start_line(scan);
251     cparse_line = Scanner_line(scan);
252     cparse_file = Scanner_file(scan);
253 
254     switch(tok) {
255     case SWIG_TOKEN_ID:
256       return ID;
257     case SWIG_TOKEN_LPAREN:
258       return LPAREN;
259     case SWIG_TOKEN_RPAREN:
260       return RPAREN;
261     case SWIG_TOKEN_SEMI:
262       return SEMI;
263     case SWIG_TOKEN_COMMA:
264       return COMMA;
265     case SWIG_TOKEN_STAR:
266       return STAR;
267     case SWIG_TOKEN_RBRACE:
268       num_brace--;
269       if (num_brace < 0) {
270 	Swig_error(cparse_file, cparse_line, "Syntax error. Extraneous '}'\n");
271 	num_brace = 0;
272       } else {
273 	return RBRACE;
274       }
275       break;
276     case SWIG_TOKEN_LBRACE:
277       last_brace = num_brace;
278       num_brace++;
279       return LBRACE;
280     case SWIG_TOKEN_EQUAL:
281       return EQUAL;
282     case SWIG_TOKEN_EQUALTO:
283       return EQUALTO;
284     case SWIG_TOKEN_PLUS:
285       return PLUS;
286     case SWIG_TOKEN_MINUS:
287       return MINUS;
288     case SWIG_TOKEN_SLASH:
289       return SLASH;
290     case SWIG_TOKEN_AND:
291       return AND;
292     case SWIG_TOKEN_LAND:
293       return LAND;
294     case SWIG_TOKEN_OR:
295       return OR;
296     case SWIG_TOKEN_LOR:
297       return LOR;
298     case SWIG_TOKEN_XOR:
299       return XOR;
300     case SWIG_TOKEN_NOT:
301       return NOT;
302     case SWIG_TOKEN_LNOT:
303       return LNOT;
304     case SWIG_TOKEN_NOTEQUAL:
305       return NOTEQUALTO;
306     case SWIG_TOKEN_LBRACKET:
307       return LBRACKET;
308     case SWIG_TOKEN_RBRACKET:
309       return RBRACKET;
310     case SWIG_TOKEN_QUESTION:
311       return QUESTIONMARK;
312     case SWIG_TOKEN_LESSTHAN:
313       return LESSTHAN;
314     case SWIG_TOKEN_LTEQUAL:
315       return LESSTHANOREQUALTO;
316     case SWIG_TOKEN_LSHIFT:
317       return LSHIFT;
318     case SWIG_TOKEN_GREATERTHAN:
319       return GREATERTHAN;
320     case SWIG_TOKEN_GTEQUAL:
321       return GREATERTHANOREQUALTO;
322     case SWIG_TOKEN_RSHIFT:
323       return RSHIFT;
324     case SWIG_TOKEN_ARROW:
325       return ARROW;
326     case SWIG_TOKEN_PERIOD:
327       return PERIOD;
328     case SWIG_TOKEN_MODULO:
329       return MODULO;
330     case SWIG_TOKEN_COLON:
331       return COLON;
332     case SWIG_TOKEN_DCOLONSTAR:
333       return DSTAR;
334 
335     case SWIG_TOKEN_DCOLON:
336       {
337 	int nexttok = Scanner_token(scan);
338 	if (nexttok == SWIG_TOKEN_STAR) {
339 	  return DSTAR;
340 	} else if (nexttok == SWIG_TOKEN_NOT) {
341 	  return DCNOT;
342 	} else {
343 	  Scanner_pushtoken(scan,nexttok,Scanner_text(scan));
344 	  if (!last_id) {
345 	    scanner_next_token(DCOLON);
346 	    return NONID;
347 	  } else {
348 	    return DCOLON;
349 	  }
350 	}
351       }
352       break;
353 
354       /* Look for multi-character sequences */
355 
356     case SWIG_TOKEN_RSTRING:
357       yylval.type = NewString(Scanner_text(scan));
358       return TYPE_RAW;
359 
360     case SWIG_TOKEN_STRING:
361       yylval.id = Swig_copy_string(Char(Scanner_text(scan)));
362       return STRING;
363 
364     case SWIG_TOKEN_WSTRING:
365       yylval.id = Swig_copy_string(Char(Scanner_text(scan)));
366       return WSTRING;
367 
368     case SWIG_TOKEN_CHAR:
369       yylval.str = NewString(Scanner_text(scan));
370       if (Len(yylval.str) == 0) {
371 	Swig_error(cparse_file, cparse_line, "Empty character constant\n");
372       }
373       return CHARCONST;
374 
375     case SWIG_TOKEN_WCHAR:
376       yylval.str = NewString(Scanner_text(scan));
377       if (Len(yylval.str) == 0) {
378 	Swig_error(cparse_file, cparse_line, "Empty character constant\n");
379       }
380       return WCHARCONST;
381 
382       /* Numbers */
383 
384     case SWIG_TOKEN_INT:
385       return NUM_INT;
386 
387     case SWIG_TOKEN_UINT:
388       return NUM_UNSIGNED;
389 
390     case SWIG_TOKEN_LONG:
391       return NUM_LONG;
392 
393     case SWIG_TOKEN_ULONG:
394       return NUM_ULONG;
395 
396     case SWIG_TOKEN_LONGLONG:
397       return NUM_LONGLONG;
398 
399     case SWIG_TOKEN_ULONGLONG:
400       return NUM_ULONGLONG;
401 
402     case SWIG_TOKEN_DOUBLE:
403     case SWIG_TOKEN_FLOAT:
404       return NUM_FLOAT;
405 
406     case SWIG_TOKEN_BOOL:
407       return NUM_BOOL;
408 
409     case SWIG_TOKEN_POUND:
410       Scanner_skip_line(scan);
411       yylval.id = Swig_copy_string(Char(Scanner_text(scan)));
412       return POUND;
413       break;
414 
415     case SWIG_TOKEN_CODEBLOCK:
416       yylval.str = NewString(Scanner_text(scan));
417       return HBLOCK;
418 
419     case SWIG_TOKEN_COMMENT:
420       {
421 	typedef enum {
422 	  DOX_COMMENT_PRE = -1,
423 	  DOX_COMMENT_NONE,
424 	  DOX_COMMENT_POST
425 	} comment_kind_t;
426 	comment_kind_t existing_comment = DOX_COMMENT_NONE;
427 
428 	/* Concatenate or skip all consecutive comments at once. */
429 	do {
430 	  String *cmt = Scanner_text(scan);
431 	  String *cmt_modified = 0;
432 	  char *loc = Char(cmt);
433 	  if ((strncmp(loc, "/*@SWIG", 7) == 0) && (loc[Len(cmt)-3] == '@')) {
434 	    Scanner_locator(scan, cmt);
435 	  }
436 	  if (scan_doxygen_comments) { /* else just skip this node, to avoid crashes in parser module*/
437 
438 	    int slashStyle = 0; /* Flag for "///" style doxygen comments */
439 	    if (strncmp(loc, "///", 3) == 0) {
440 	      slashStyle = 1;
441 	      if (Len(cmt) == 3) {
442 		/* Modify to make length=4 to ensure that the empty comment does
443 		   get processed to preserve the newlines in the original comments. */
444 		cmt_modified = NewStringf("%s ", cmt);
445 		cmt = cmt_modified;
446 		loc = Char(cmt);
447 	      }
448 	    }
449 
450 	    /* Check for all possible Doxygen comment start markers while ignoring
451 	       comments starting with a row of asterisks or slashes just as
452 	       Doxygen itself does.  Also skip empty comment (slash-star-star-slash),
453 	       which causes a crash due to begin > end. */
454 	    if (Len(cmt) > 3 && loc[0] == '/' &&
455 		((loc[1] == '/' && ((loc[2] == '/' && loc[3] != '/') || loc[2] == '!')) ||
456 		 (loc[1] == '*' && ((loc[2] == '*' && loc[3] != '*' && loc[3] != '/') || loc[2] == '!')))) {
457 	      comment_kind_t this_comment = loc[3] == '<' ? DOX_COMMENT_POST : DOX_COMMENT_PRE;
458 	      if (existing_comment != DOX_COMMENT_NONE && this_comment != existing_comment) {
459 		/* We can't concatenate together Doxygen pre- and post-comments. */
460 		break;
461 	      }
462 
463 	      if (this_comment == DOX_COMMENT_POST || !isStructuralDoxygen(loc)) {
464 		String *str;
465 
466 		int begin = this_comment == DOX_COMMENT_POST ? 4 : 3;
467 		int end = Len(cmt);
468 		if (loc[end - 1] == '/' && loc[end - 2] == '*') {
469 		  end -= 2;
470 		}
471 
472 		str = NewStringWithSize(loc + begin, end - begin);
473 
474 		if (existing_comment == DOX_COMMENT_NONE) {
475 		  yylval.str = str;
476 		  Setline(yylval.str, Scanner_start_line(scan));
477 		  Setfile(yylval.str, Scanner_file(scan));
478 		} else {
479 		  if (slashStyle) {
480 		    /* Add a newline to the end of each doxygen "///" comment,
481 		       since they are processed individually, unlike the
482 		       slash-star style, which gets processed as a block with
483 		       newlines included. */
484 		    Append(yylval.str, "\n");
485 		  }
486 		  Append(yylval.str, str);
487 		}
488 
489 		existing_comment = this_comment;
490 	      }
491 	    }
492 	  }
493 	  do {
494 	    tok = Scanner_token(scan);
495 	  } while (tok == SWIG_TOKEN_ENDLINE);
496 	  Delete(cmt_modified);
497 	} while (tok == SWIG_TOKEN_COMMENT);
498 
499 	Scanner_pushtoken(scan, tok, Scanner_text(scan));
500 
501 	switch (existing_comment) {
502 	  case DOX_COMMENT_PRE:
503 	    return DOXYGENSTRING;
504 	  case DOX_COMMENT_NONE:
505 	    break;
506 	  case DOX_COMMENT_POST:
507 	    return DOXYGENPOSTSTRING;
508 	}
509       }
510       break;
511     case SWIG_TOKEN_ENDLINE:
512       break;
513     case SWIG_TOKEN_BACKSLASH:
514       break;
515     default:
516       Swig_error(cparse_file, cparse_line, "Illegal token '%s'.\n", Scanner_text(scan));
517       return (ILLEGAL);
518     }
519   }
520 }
521 
522 static int check_typedef = 0;
523 
scanner_set_location(String * file,int line)524 void scanner_set_location(String *file, int line) {
525   Scanner_set_location(scan,file,line-1);
526 }
527 
scanner_check_typedef()528 void scanner_check_typedef() {
529   check_typedef = 1;
530 }
531 
scanner_ignore_typedef()532 void scanner_ignore_typedef() {
533   check_typedef = 0;
534 }
535 
scanner_last_id(int x)536 void scanner_last_id(int x) {
537   last_id = x;
538 }
539 
scanner_clear_rename()540 void scanner_clear_rename() {
541   rename_active = 0;
542 }
543 
544 /* Used to push a fictitious token into the scanner */
545 static int next_token = 0;
scanner_next_token(int tok)546 void scanner_next_token(int tok) {
547   next_token = tok;
548 }
549 
scanner_set_main_input_file(String * file)550 void scanner_set_main_input_file(String *file) {
551   main_input_file = file;
552 }
553 
scanner_get_main_input_file()554 String *scanner_get_main_input_file() {
555   return main_input_file;
556 }
557 
558 /* ----------------------------------------------------------------------------
559  * int yylex()
560  *
561  * Gets the lexene and returns tokens.
562  * ------------------------------------------------------------------------- */
563 
yylex(void)564 int yylex(void) {
565 
566   int l;
567   char *yytext;
568 
569   if (!scan_init) {
570     scanner_init();
571   }
572 
573   if (next_token) {
574     l = next_token;
575     next_token = 0;
576     return l;
577   }
578 
579   l = yylook();
580 
581   /*   Swig_diagnostic(cparse_file, cparse_line, ":::%d: '%s'\n", l, Scanner_text(scan)); */
582 
583   if (l == NONID) {
584     last_id = 1;
585   } else {
586     last_id = 0;
587   }
588 
589   /* We got some sort of non-white space object.  We set the start_line
590      variable unless it has already been set */
591 
592   if (!cparse_start_line) {
593     cparse_start_line = cparse_line;
594   }
595 
596   /* Copy the lexene */
597 
598   switch (l) {
599 
600   case NUM_INT:
601   case NUM_FLOAT:
602   case NUM_ULONG:
603   case NUM_LONG:
604   case NUM_UNSIGNED:
605   case NUM_LONGLONG:
606   case NUM_ULONGLONG:
607   case NUM_BOOL:
608     if (l == NUM_INT)
609       yylval.dtype.type = T_INT;
610     if (l == NUM_FLOAT)
611       yylval.dtype.type = T_DOUBLE;
612     if (l == NUM_ULONG)
613       yylval.dtype.type = T_ULONG;
614     if (l == NUM_LONG)
615       yylval.dtype.type = T_LONG;
616     if (l == NUM_UNSIGNED)
617       yylval.dtype.type = T_UINT;
618     if (l == NUM_LONGLONG)
619       yylval.dtype.type = T_LONGLONG;
620     if (l == NUM_ULONGLONG)
621       yylval.dtype.type = T_ULONGLONG;
622     if (l == NUM_BOOL)
623       yylval.dtype.type = T_BOOL;
624     yylval.dtype.val = NewString(Scanner_text(scan));
625     yylval.dtype.bitfield = 0;
626     yylval.dtype.throws = 0;
627     return (l);
628 
629   case ID:
630     yytext = Char(Scanner_text(scan));
631     if (yytext[0] != '%') {
632       /* Look for keywords now */
633 
634       if (strcmp(yytext, "int") == 0) {
635 	yylval.type = NewSwigType(T_INT);
636 	return (TYPE_INT);
637       }
638       if (strcmp(yytext, "double") == 0) {
639 	yylval.type = NewSwigType(T_DOUBLE);
640 	return (TYPE_DOUBLE);
641       }
642       if (strcmp(yytext, "void") == 0) {
643 	yylval.type = NewSwigType(T_VOID);
644 	return (TYPE_VOID);
645       }
646       if (strcmp(yytext, "char") == 0) {
647 	yylval.type = NewSwigType(T_CHAR);
648 	return (TYPE_CHAR);
649       }
650       if (strcmp(yytext, "wchar_t") == 0) {
651 	yylval.type = NewSwigType(T_WCHAR);
652 	return (TYPE_WCHAR);
653       }
654       if (strcmp(yytext, "short") == 0) {
655 	yylval.type = NewSwigType(T_SHORT);
656 	return (TYPE_SHORT);
657       }
658       if (strcmp(yytext, "long") == 0) {
659 	yylval.type = NewSwigType(T_LONG);
660 	return (TYPE_LONG);
661       }
662       if (strcmp(yytext, "float") == 0) {
663 	yylval.type = NewSwigType(T_FLOAT);
664 	return (TYPE_FLOAT);
665       }
666       if (strcmp(yytext, "signed") == 0) {
667 	yylval.type = NewSwigType(T_INT);
668 	return (TYPE_SIGNED);
669       }
670       if (strcmp(yytext, "unsigned") == 0) {
671 	yylval.type = NewSwigType(T_UINT);
672 	return (TYPE_UNSIGNED);
673       }
674       if (strcmp(yytext, "bool") == 0) {
675 	yylval.type = NewSwigType(T_BOOL);
676 	return (TYPE_BOOL);
677       }
678 
679       /* Non ISO (Windows) C extensions */
680       if (strcmp(yytext, "__int8") == 0) {
681 	yylval.type = NewString(yytext);
682 	return (TYPE_NON_ISO_INT8);
683       }
684       if (strcmp(yytext, "__int16") == 0) {
685 	yylval.type = NewString(yytext);
686 	return (TYPE_NON_ISO_INT16);
687       }
688       if (strcmp(yytext, "__int32") == 0) {
689 	yylval.type = NewString(yytext);
690 	return (TYPE_NON_ISO_INT32);
691       }
692       if (strcmp(yytext, "__int64") == 0) {
693 	yylval.type = NewString(yytext);
694 	return (TYPE_NON_ISO_INT64);
695       }
696 
697       /* C++ keywords */
698       if (cparse_cplusplus) {
699 	if (strcmp(yytext, "and") == 0)
700 	  return (LAND);
701 	if (strcmp(yytext, "or") == 0)
702 	  return (LOR);
703 	if (strcmp(yytext, "not") == 0)
704 	  return (LNOT);
705 	if (strcmp(yytext, "class") == 0)
706 	  return (CLASS);
707 	if (strcmp(yytext, "private") == 0)
708 	  return (PRIVATE);
709 	if (strcmp(yytext, "public") == 0)
710 	  return (PUBLIC);
711 	if (strcmp(yytext, "protected") == 0)
712 	  return (PROTECTED);
713 	if (strcmp(yytext, "friend") == 0)
714 	  return (FRIEND);
715 	if (strcmp(yytext, "constexpr") == 0)
716 	  return (CONSTEXPR);
717 	if (strcmp(yytext, "thread_local") == 0)
718 	  return (THREAD_LOCAL);
719 	if (strcmp(yytext, "decltype") == 0)
720 	  return (DECLTYPE);
721 	if (strcmp(yytext, "virtual") == 0)
722 	  return (VIRTUAL);
723 	if (strcmp(yytext, "static_assert") == 0)
724 	  return (STATIC_ASSERT);
725 	if (strcmp(yytext, "operator") == 0) {
726 	  int nexttok;
727 	  String *s = NewString("operator ");
728 
729 	  /* If we have an operator, we have to collect the operator symbol and attach it to
730              the operator identifier.   To do this, we need to scan ahead by several tokens.
731              Cases include:
732 
733              (1) If the next token is an operator as determined by Scanner_isoperator(),
734                  it means that the operator applies to one of the standard C++ mathematical,
735                  assignment, or logical operator symbols (e.g., '+','<=','==','&', etc.)
736                  In this case, we merely append the symbol text to the operator string above.
737 
738              (2) If the next token is (, we look for ).  This is operator ().
739              (3) If the next token is [, we look for ].  This is operator [].
740 	     (4) If the next token is an identifier.  The operator is possibly a conversion operator.
741                       (a) Must check for special case new[] and delete[]
742 
743              Error handling is somewhat tricky here.  We'll try to back out gracefully if we can.
744 
745 	  */
746 
747 	  do {
748 	    nexttok = Scanner_token(scan);
749 	  } while (nexttok == SWIG_TOKEN_ENDLINE || nexttok == SWIG_TOKEN_COMMENT);
750 
751 	  if (Scanner_isoperator(nexttok)) {
752 	    /* One of the standard C/C++ symbolic operators */
753 	    Append(s,Scanner_text(scan));
754 	    yylval.str = s;
755 	    return OPERATOR;
756 	  } else if (nexttok == SWIG_TOKEN_LPAREN) {
757 	    /* Function call operator.  The next token MUST be a RPAREN */
758 	    nexttok = Scanner_token(scan);
759 	    if (nexttok != SWIG_TOKEN_RPAREN) {
760 	      Swig_error(Scanner_file(scan),Scanner_line(scan),"Syntax error. Bad operator name.\n");
761 	    } else {
762 	      Append(s,"()");
763 	      yylval.str = s;
764 	      return OPERATOR;
765 	    }
766 	  } else if (nexttok == SWIG_TOKEN_LBRACKET) {
767 	    /* Array access operator.  The next token MUST be a RBRACKET */
768 	    nexttok = Scanner_token(scan);
769 	    if (nexttok != SWIG_TOKEN_RBRACKET) {
770 	      Swig_error(Scanner_file(scan),Scanner_line(scan),"Syntax error. Bad operator name.\n");
771 	    } else {
772 	      Append(s,"[]");
773 	      yylval.str = s;
774 	      return OPERATOR;
775 	    }
776 	  } else if (nexttok == SWIG_TOKEN_STRING) {
777 	    /* Operator "" or user-defined string literal ""_suffix */
778 	    Append(s,"\"\"");
779 	    yylval.str = s;
780 	    return OPERATOR;
781 	  } else if (nexttok == SWIG_TOKEN_ID) {
782 	    /* We have an identifier.  This could be any number of things. It could be a named version of
783                an operator (e.g., 'and_eq') or it could be a conversion operator.   To deal with this, we're
784                going to read tokens until we encounter a ( or ;.  Some care is needed for formatting. */
785 	    int needspace = 1;
786 	    int termtoken = 0;
787 	    const char *termvalue = 0;
788 
789 	    Append(s,Scanner_text(scan));
790 	    while (1) {
791 
792 	      nexttok = Scanner_token(scan);
793 	      if (nexttok <= 0) {
794 		Swig_error(Scanner_file(scan),Scanner_line(scan),"Syntax error. Bad operator name.\n");
795 	      }
796 	      if (nexttok == SWIG_TOKEN_LPAREN) {
797 		termtoken = SWIG_TOKEN_LPAREN;
798 		termvalue = "(";
799 		break;
800               } else if (nexttok == SWIG_TOKEN_CODEBLOCK) {
801                 termtoken = SWIG_TOKEN_CODEBLOCK;
802                 termvalue = Char(Scanner_text(scan));
803                 break;
804               } else if (nexttok == SWIG_TOKEN_LBRACE) {
805                 termtoken = SWIG_TOKEN_LBRACE;
806                 termvalue = "{";
807                 break;
808               } else if (nexttok == SWIG_TOKEN_SEMI) {
809 		termtoken = SWIG_TOKEN_SEMI;
810 		termvalue = ";";
811 		break;
812               } else if (nexttok == SWIG_TOKEN_STRING) {
813 		termtoken = SWIG_TOKEN_STRING;
814                 termvalue = Swig_copy_string(Char(Scanner_text(scan)));
815 		break;
816 	      } else if (nexttok == SWIG_TOKEN_ID) {
817 		if (needspace) {
818 		  Append(s," ");
819 		}
820 		Append(s,Scanner_text(scan));
821 	      } else if (nexttok == SWIG_TOKEN_ENDLINE) {
822 	      } else if (nexttok == SWIG_TOKEN_COMMENT) {
823 	      } else {
824 		Append(s,Scanner_text(scan));
825 		needspace = 0;
826 	      }
827 	    }
828 	    yylval.str = s;
829 	    if (!rename_active) {
830 	      String *cs;
831 	      char *t = Char(s) + 9;
832 	      if (!((strcmp(t, "new") == 0)
833 		    || (strcmp(t, "delete") == 0)
834 		    || (strcmp(t, "new[]") == 0)
835 		    || (strcmp(t, "delete[]") == 0)
836 		    || (strcmp(t, "and") == 0)
837 		    || (strcmp(t, "and_eq") == 0)
838 		    || (strcmp(t, "bitand") == 0)
839 		    || (strcmp(t, "bitor") == 0)
840 		    || (strcmp(t, "compl") == 0)
841 		    || (strcmp(t, "not") == 0)
842 		    || (strcmp(t, "not_eq") == 0)
843 		    || (strcmp(t, "or") == 0)
844 		    || (strcmp(t, "or_eq") == 0)
845 		    || (strcmp(t, "xor") == 0)
846 		    || (strcmp(t, "xor_eq") == 0)
847 		    )) {
848 		/*              retract(strlen(t)); */
849 
850 		/* The operator is a conversion operator.   In order to deal with this, we need to feed the
851                    type information back into the parser.  For now this is a hack.  Needs to be cleaned up later. */
852 		cs = NewString(t);
853 		if (termtoken) Append(cs,termvalue);
854 		Seek(cs,0,SEEK_SET);
855 		Setline(cs,cparse_line);
856 		Setfile(cs,cparse_file);
857 		Scanner_push(scan,cs);
858 		Delete(cs);
859 		return CONVERSIONOPERATOR;
860 	      }
861 	    }
862 	    if (termtoken)
863               Scanner_pushtoken(scan, termtoken, termvalue);
864 	    return (OPERATOR);
865 	  }
866 	}
867 	if (strcmp(yytext, "throw") == 0)
868 	  return (THROW);
869 	if (strcmp(yytext, "noexcept") == 0)
870 	  return (NOEXCEPT);
871 	if (strcmp(yytext, "try") == 0)
872 	  return (yylex());
873 	if (strcmp(yytext, "catch") == 0)
874 	  return (CATCH);
875 	if (strcmp(yytext, "inline") == 0)
876 	  return (yylex());
877 	if (strcmp(yytext, "mutable") == 0)
878 	  return (yylex());
879 	if (strcmp(yytext, "explicit") == 0)
880 	  return (EXPLICIT);
881 	if (strcmp(yytext, "auto") == 0)
882 	  return (AUTO);
883 	if (strcmp(yytext, "export") == 0)
884 	  return (yylex());
885 	if (strcmp(yytext, "typename") == 0)
886 	  return (TYPENAME);
887 	if (strcmp(yytext, "template") == 0) {
888 	  yylval.intvalue = cparse_line;
889 	  return (TEMPLATE);
890 	}
891 	if (strcmp(yytext, "delete") == 0)
892 	  return (DELETE_KW);
893 	if (strcmp(yytext, "default") == 0)
894 	  return (DEFAULT);
895 	if (strcmp(yytext, "using") == 0)
896 	  return (USING);
897 	if (strcmp(yytext, "namespace") == 0)
898 	  return (NAMESPACE);
899 	if (strcmp(yytext, "override") == 0) {
900 	  last_id = 1;
901 	  return (OVERRIDE);
902 	}
903 	if (strcmp(yytext, "final") == 0) {
904 	  last_id = 1;
905 	  return (FINAL);
906 	}
907       } else {
908 	if (strcmp(yytext, "class") == 0) {
909 	  Swig_warning(WARN_PARSE_CLASS_KEYWORD, cparse_file, cparse_line, "class keyword used, but not in C++ mode.\n");
910 	}
911 	if (strcmp(yytext, "complex") == 0) {
912 	  yylval.type = NewSwigType(T_COMPLEX);
913 	  return (TYPE_COMPLEX);
914 	}
915 	if (strcmp(yytext, "restrict") == 0)
916 	  return (yylex());
917       }
918 
919       /* Misc keywords */
920 
921       if (strcmp(yytext, "extern") == 0)
922 	return (EXTERN);
923       if (strcmp(yytext, "const") == 0)
924 	return (CONST_QUAL);
925       if (strcmp(yytext, "static") == 0)
926 	return (STATIC);
927       if (strcmp(yytext, "struct") == 0)
928 	return (STRUCT);
929       if (strcmp(yytext, "union") == 0)
930 	return (UNION);
931       if (strcmp(yytext, "enum") == 0)
932 	return (ENUM);
933       if (strcmp(yytext, "sizeof") == 0)
934 	return (SIZEOF);
935 
936       if (strcmp(yytext, "typedef") == 0) {
937 	yylval.intvalue = 0;
938 	return (TYPEDEF);
939       }
940 
941       /* Ignored keywords */
942 
943       if (strcmp(yytext, "volatile") == 0)
944 	return (VOLATILE);
945       if (strcmp(yytext, "register") == 0)
946 	return (REGISTER);
947       if (strcmp(yytext, "inline") == 0)
948 	return (yylex());
949 
950     } else {
951       Delete(cparse_unknown_directive);
952       cparse_unknown_directive = NULL;
953 
954       /* SWIG directives */
955       if (strcmp(yytext, "%module") == 0)
956 	return (MODULE);
957       if (strcmp(yytext, "%insert") == 0)
958 	return (INSERT);
959       if (strcmp(yytext, "%name") == 0)
960 	return (NAME);
961       if (strcmp(yytext, "%rename") == 0) {
962 	rename_active = 1;
963 	return (RENAME);
964       }
965       if (strcmp(yytext, "%namewarn") == 0) {
966 	rename_active = 1;
967 	return (NAMEWARN);
968       }
969       if (strcmp(yytext, "%includefile") == 0)
970 	return (INCLUDE);
971       if (strcmp(yytext, "%beginfile") == 0)
972 	return (BEGINFILE);
973       if (strcmp(yytext, "%endoffile") == 0)
974 	return (ENDOFFILE);
975       if (strcmp(yytext, "%val") == 0) {
976 	Swig_warning(WARN_DEPRECATED_VAL, cparse_file, cparse_line, "%%val directive deprecated (ignored).\n");
977 	return (yylex());
978       }
979       if (strcmp(yytext, "%out") == 0) {
980 	Swig_warning(WARN_DEPRECATED_OUT, cparse_file, cparse_line, "%%out directive deprecated (ignored).\n");
981 	return (yylex());
982       }
983       if (strcmp(yytext, "%constant") == 0)
984 	return (CONSTANT);
985       if (strcmp(yytext, "%typedef") == 0) {
986 	yylval.intvalue = 1;
987 	return (TYPEDEF);
988       }
989       if (strcmp(yytext, "%native") == 0)
990 	return (NATIVE);
991       if (strcmp(yytext, "%pragma") == 0)
992 	return (PRAGMA);
993       if (strcmp(yytext, "%extend") == 0)
994 	return (EXTEND);
995       if (strcmp(yytext, "%fragment") == 0)
996 	return (FRAGMENT);
997       if (strcmp(yytext, "%inline") == 0)
998 	return (INLINE);
999       if (strcmp(yytext, "%typemap") == 0)
1000 	return (TYPEMAP);
1001       if (strcmp(yytext, "%feature") == 0) {
1002         /* The rename_active indicates we don't need the information of the
1003          * following function's return type. This applied for %rename, so do
1004          * %feature.
1005          */
1006         rename_active = 1;
1007 	return (FEATURE);
1008       }
1009       if (strcmp(yytext, "%except") == 0)
1010 	return (EXCEPT);
1011       if (strcmp(yytext, "%importfile") == 0)
1012 	return (IMPORT);
1013       if (strcmp(yytext, "%echo") == 0)
1014 	return (ECHO);
1015       if (strcmp(yytext, "%apply") == 0)
1016 	return (APPLY);
1017       if (strcmp(yytext, "%clear") == 0)
1018 	return (CLEAR);
1019       if (strcmp(yytext, "%types") == 0)
1020 	return (TYPES);
1021       if (strcmp(yytext, "%parms") == 0)
1022 	return (PARMS);
1023       if (strcmp(yytext, "%varargs") == 0)
1024 	return (VARARGS);
1025       if (strcmp(yytext, "%template") == 0) {
1026 	return (SWIGTEMPLATE);
1027       }
1028       if (strcmp(yytext, "%warn") == 0)
1029 	return (WARN);
1030 
1031       /* Note down the apparently unknown directive for error reporting. */
1032       cparse_unknown_directive = NewString(yytext);
1033     }
1034     /* Have an unknown identifier, as a last step, we'll do a typedef lookup on it. */
1035 
1036     /* Need to fix this */
1037     if (check_typedef) {
1038       if (SwigType_istypedef(yytext)) {
1039 	yylval.type = NewString(yytext);
1040 	return (TYPE_TYPEDEF);
1041       }
1042     }
1043     yylval.id = Swig_copy_string(yytext);
1044     last_id = 1;
1045     return (ID);
1046   case POUND:
1047     return yylex();
1048   case SWIG_TOKEN_COMMENT:
1049     return yylex();
1050   default:
1051     return (l);
1052   }
1053 }
1054