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