ctags: Rename Geany-specific tagEntryInfo::arglist to upstream's ::signature
[geany-mirror.git] / ctags / parsers / rust.c
blob0158773a2a7f7f2d379e1821b150ebb695c9c0a6
1 /*
3 * This source code is released for free distribution under the terms of the
4 * GNU General Public License.
6 * This module contains functions for generating tags for Rust files.
7 */
9 /*
10 * INCLUDE FILES
12 #include "general.h" /* must always come first */
13 #include "main.h"
15 #include <string.h>
17 #include "keyword.h"
18 #include "parse.h"
19 #include "entry.h"
20 #include "options.h"
21 #include "read.h"
22 #include "vstring.h"
25 * MACROS
27 #define MAX_STRING_LENGTH 256
30 * DATA DECLARATIONS
33 typedef enum {
34 K_MOD,
35 K_STRUCT,
36 K_TRAIT,
37 K_IMPL,
38 K_FN,
39 K_ENUM,
40 K_TYPE,
41 K_STATIC,
42 K_MACRO,
43 K_FIELD,
44 K_VARIANT,
45 K_METHOD,
46 K_NONE
47 } RustKind;
49 static kindOption rustKinds[] = {
50 {TRUE, 'n', "module", "module"},
51 {TRUE, 's', "struct", "structural type"},
52 {TRUE, 'i', "interface", "trait interface"},
53 {TRUE, 'c', "implementation", "implementation"},
54 {TRUE, 'f', "function", "Function"},
55 {TRUE, 'g', "enum", "Enum"},
56 {TRUE, 't', "typedef", "Type Alias"},
57 {TRUE, 'v', "variable", "Global variable"},
58 {TRUE, 'M', "macro", "Macro Definition"},
59 {TRUE, 'm', "field", "A struct field"},
60 {TRUE, 'e', "enumerator", "An enum variant"},
61 {TRUE, 'F', "method", "A method"},
64 typedef enum {
65 TOKEN_WHITESPACE,
66 TOKEN_STRING,
67 TOKEN_IDENT,
68 TOKEN_LSHIFT,
69 TOKEN_RSHIFT,
70 TOKEN_RARROW,
71 TOKEN_EOF
72 } tokenType;
74 typedef struct {
75 /* Characters */
76 int cur_c;
77 int next_c;
79 /* Tokens */
80 int cur_token;
81 vString* token_str;
82 unsigned long line;
83 MIOPos pos;
84 } lexerState;
87 * FUNCTION PROTOTYPES
90 static void parseBlock (lexerState *lexer, boolean delim, int kind, vString *scope);
93 * FUNCTION DEFINITIONS
96 /* Resets the scope string to the old length */
97 static void resetScope (vString *scope, size_t old_len)
99 scope->length = old_len;
100 scope->buffer[old_len] = '\0';
103 /* Adds a name to the end of the scope string */
104 static void addToScope (vString *scope, vString *name)
106 if (vStringLength(scope) > 0)
107 vStringCatS(scope, "::");
108 vStringCat(scope, name);
111 /* Write the lexer's current token to string, taking care of special tokens */
112 static void writeCurTokenToStr (lexerState *lexer, vString *out_str)
114 switch (lexer->cur_token)
116 case TOKEN_IDENT:
117 vStringCat(out_str, lexer->token_str);
118 break;
119 case TOKEN_STRING:
120 vStringCat(out_str, lexer->token_str);
121 break;
122 case TOKEN_WHITESPACE:
123 vStringPut(out_str, ' ');
124 break;
125 case TOKEN_LSHIFT:
126 vStringCatS(out_str, "<<");
127 break;
128 case TOKEN_RSHIFT:
129 vStringCatS(out_str, ">>");
130 break;
131 case TOKEN_RARROW:
132 vStringCatS(out_str, "->");
133 break;
134 default:
135 vStringPut(out_str, (char) lexer->cur_token);
139 /* Reads a character from the file */
140 static void advanceChar (lexerState *lexer)
142 lexer->cur_c = lexer->next_c;
143 lexer->next_c = fileGetc();
146 /* Reads N characters from the file */
147 static void advanceNChar (lexerState *lexer, int n)
149 while (n--)
150 advanceChar(lexer);
153 /* Store the current character in lexerState::token_str if there is space
154 * (set by MAX_STRING_LENGTH), and then read the next character from the file */
155 static void advanceAndStoreChar (lexerState *lexer)
157 if (vStringLength(lexer->token_str) < MAX_STRING_LENGTH)
158 vStringPut(lexer->token_str, (char) lexer->cur_c);
159 advanceChar(lexer);
162 static boolean isWhitespace (int c)
164 return c == ' ' || c == '\t' || c == '\r' || c == '\n';
167 static boolean isAscii (int c)
169 return (c >= 0) && (c < 0x80);
172 /* This isn't quite right for Unicode identifiers */
173 static boolean isIdentifierStart (int c)
175 return (isAscii(c) && (isalpha(c) || c == '_')) || !isAscii(c);
178 /* This isn't quite right for Unicode identifiers */
179 static boolean isIdentifierContinue (int c)
181 return (isAscii(c) && (isalnum(c) || c == '_')) || !isAscii(c);
184 static void scanWhitespace (lexerState *lexer)
186 while (isWhitespace(lexer->cur_c))
187 advanceChar(lexer);
190 /* Normal line comments start with two /'s and continue until the next \n
191 * (potentially after a \r). Additionally, a shebang in the beginning of the
192 * file also counts as a line comment as long as it is not this sequence: #![ .
193 * Block comments start with / followed by a * and end with a * followed by a /.
194 * Unlike in C/C++ they nest. */
195 static void scanComments (lexerState *lexer)
197 /* // */
198 if (lexer->next_c == '/')
200 advanceNChar(lexer, 2);
201 while (lexer->cur_c != EOF && lexer->cur_c != '\n')
202 advanceChar(lexer);
204 /* #! */
205 else if (lexer->next_c == '!')
207 advanceNChar(lexer, 2);
208 /* If it is exactly #![ then it is not a comment, but an attribute */
209 if (lexer->cur_c == '[')
210 return;
211 while (lexer->cur_c != EOF && lexer->cur_c != '\n')
212 advanceChar(lexer);
214 /* block comment */
215 else if (lexer->next_c == '*')
217 int level = 1;
218 advanceNChar(lexer, 2);
219 while (lexer->cur_c != EOF && level > 0)
221 if (lexer->cur_c == '*' && lexer->next_c == '/')
223 level--;
224 advanceNChar(lexer, 2);
226 else if (lexer->cur_c == '/' && lexer->next_c == '*')
228 level++;
229 advanceNChar(lexer, 2);
231 else
233 advanceChar(lexer);
239 static void scanIdentifier (lexerState *lexer)
241 vStringClear(lexer->token_str);
244 advanceAndStoreChar(lexer);
245 } while(lexer->cur_c != EOF && isIdentifierContinue(lexer->cur_c));
248 /* Double-quoted strings, we only care about the \" escape. These
249 * last past the end of the line, so be careful not too store too much
250 * of them (see MAX_STRING_LENGTH). The only place we look at their
251 * contents is in the function definitions, and there the valid strings are
252 * things like "C" and "Rust" */
253 static void scanString (lexerState *lexer)
255 vStringClear(lexer->token_str);
256 advanceAndStoreChar(lexer);
257 while (lexer->cur_c != EOF && lexer->cur_c != '"')
259 if (lexer->cur_c == '\\' && lexer->next_c == '"')
260 advanceAndStoreChar(lexer);
261 advanceAndStoreChar(lexer);
263 advanceAndStoreChar(lexer);
266 /* Raw strings look like this: r"" or r##""## where the number of
267 * hashes must match */
268 static void scanRawString (lexerState *lexer)
270 size_t num_initial_hashes = 0;
271 vStringClear(lexer->token_str);
272 advanceAndStoreChar(lexer);
273 /* Count how many leading hashes there are */
274 while (lexer->cur_c == '#')
276 num_initial_hashes++;
277 advanceAndStoreChar(lexer);
279 if (lexer->cur_c != '"')
280 return;
281 advanceAndStoreChar(lexer);
282 while (lexer->cur_c != EOF)
284 /* Count how many trailing hashes there are. If the number is equal or more
285 * than the number of leading hashes, break. */
286 if (lexer->cur_c == '"')
288 size_t num_trailing_hashes = 0;
289 advanceAndStoreChar(lexer);
290 while (lexer->cur_c == '#' && num_trailing_hashes < num_initial_hashes)
292 num_trailing_hashes++;
294 advanceAndStoreChar(lexer);
296 if (num_trailing_hashes == num_initial_hashes)
297 break;
299 else
301 advanceAndStoreChar(lexer);
306 /* This deals with character literals: 'n', '\n', '\uFFFF'; and lifetimes:
307 * 'lifetime. We'll use this approximate regexp for the literals:
308 * \' \\ [^']+ \' or \' [^'] \' or \' \\ \' \'. Either way, we'll treat this
309 * token as a string, so it gets preserved as is for function signatures with
310 * lifetimes. */
311 static void scanCharacterOrLifetime (lexerState *lexer)
313 vStringClear(lexer->token_str);
314 advanceAndStoreChar(lexer);
316 if (lexer->cur_c == '\\')
318 advanceAndStoreChar(lexer);
319 /* The \' \\ \' \' (literally '\'') case */
320 if (lexer->cur_c == '\'' && lexer->next_c == '\'')
322 advanceAndStoreChar(lexer);
323 advanceAndStoreChar(lexer);
325 /* The \' \\ [^']+ \' case */
326 else
328 while (lexer->cur_c != EOF && lexer->cur_c != '\'')
329 advanceAndStoreChar(lexer);
332 /* The \' [^'] \' case */
333 else if (lexer->cur_c != '\'' && lexer->next_c == '\'')
335 advanceAndStoreChar(lexer);
336 advanceAndStoreChar(lexer);
338 /* Otherwise it is malformed, or a lifetime */
341 /* Advances the parser one token, optionally skipping whitespace
342 * (otherwise it is concatenated and returned as a single whitespace token).
343 * Whitespace is needed to properly render function signatures. Unrecognized
344 * token starts are stored literally, e.g. token may equal to a character '#'. */
345 static int advanceToken (lexerState *lexer, boolean skip_whitspace)
347 boolean have_whitespace = FALSE;
348 lexer->line = getSourceLineNumber();
349 lexer->pos = getInputFilePosition();
350 while (lexer->cur_c != EOF)
352 if (isWhitespace(lexer->cur_c))
354 scanWhitespace(lexer);
355 have_whitespace = TRUE;
357 else if (lexer->cur_c == '/' && (lexer->next_c == '/' || lexer->next_c == '*'))
359 scanComments(lexer);
360 have_whitespace = TRUE;
362 else
364 if (have_whitespace && !skip_whitspace)
365 return lexer->cur_token = TOKEN_WHITESPACE;
366 break;
369 lexer->line = getSourceLineNumber();
370 lexer->pos = getInputFilePosition();
371 while (lexer->cur_c != EOF)
373 if (lexer->cur_c == '"')
375 scanString(lexer);
376 return lexer->cur_token = TOKEN_STRING;
378 else if (lexer->cur_c == 'r' && (lexer->next_c == '#' || lexer->next_c == '"'))
380 scanRawString(lexer);
381 return lexer->cur_token = TOKEN_STRING;
383 else if (lexer->cur_c == '\'')
385 scanCharacterOrLifetime(lexer);
386 return lexer->cur_token = TOKEN_STRING;
388 else if (isIdentifierStart(lexer->cur_c))
390 scanIdentifier(lexer);
391 return lexer->cur_token = TOKEN_IDENT;
393 /* These shift tokens aren't too important for tag-generation per se,
394 * but they confuse the skipUntil code which tracks the <> pairs. */
395 else if (lexer->cur_c == '>' && lexer->next_c == '>')
397 advanceNChar(lexer, 2);
398 return lexer->cur_token = TOKEN_RSHIFT;
400 else if (lexer->cur_c == '<' && lexer->next_c == '<')
402 advanceNChar(lexer, 2);
403 return lexer->cur_token = TOKEN_LSHIFT;
405 else if (lexer->cur_c == '-' && lexer->next_c == '>')
407 advanceNChar(lexer, 2);
408 return lexer->cur_token = TOKEN_RARROW;
410 else
412 int c = lexer->cur_c;
413 advanceChar(lexer);
414 return lexer->cur_token = c;
417 return lexer->cur_token = TOKEN_EOF;
420 static void initLexer (lexerState *lexer)
422 advanceNChar(lexer, 2);
423 lexer->token_str = vStringNew();
425 if (lexer->cur_c == '#' && lexer->next_c == '!')
426 scanComments(lexer);
427 advanceToken(lexer, TRUE);
430 static void deInitLexer (lexerState *lexer)
432 vStringDelete(lexer->token_str);
433 lexer->token_str = NULL;
436 static void addTag (vString* ident, const char* type, const char* arg_list, int kind, unsigned long line, MIOPos pos, vString *scope, int parent_kind)
438 if (kind == K_NONE)
439 return;
440 tagEntryInfo tag;
441 initTagEntry(&tag, ident->buffer);
443 tag.lineNumber = line;
444 tag.filePosition = pos;
445 tag.sourceFileName = getSourceFileName();
447 tag.kindName = rustKinds[kind].name;
448 tag.kind = rustKinds[kind].letter;
450 tag.extensionFields.signature = arg_list;
451 tag.extensionFields.varType = type;
452 if (parent_kind != K_NONE)
454 tag.extensionFields.scope[0] = rustKinds[parent_kind].name;
455 tag.extensionFields.scope[1] = scope->buffer;
457 makeTagEntry(&tag);
460 /* Skip tokens until one of the goal tokens is hit. Escapes when level = 0 if there are no goal tokens.
461 * Keeps track of balanced <>'s, ()'s, []'s, and {}'s and ignores the goal tokens within those pairings */
462 static void skipUntil (lexerState *lexer, int goal_tokens[], int num_goal_tokens)
464 int angle_level = 0;
465 int paren_level = 0;
466 int brace_level = 0;
467 int bracket_level = 0;
468 while (lexer->cur_token != TOKEN_EOF)
470 if (angle_level == 0 && paren_level == 0 && brace_level == 0
471 && bracket_level == 0)
473 int ii = 0;
474 for(ii = 0; ii < num_goal_tokens; ii++)
476 if (lexer->cur_token == goal_tokens[ii])
478 break;
481 if (ii < num_goal_tokens)
482 break;
484 switch (lexer->cur_token)
486 case '<':
487 angle_level++;
488 break;
489 case '(':
490 paren_level++;
491 break;
492 case '{':
493 brace_level++;
494 break;
495 case '[':
496 bracket_level++;
497 break;
498 case '>':
499 angle_level--;
500 break;
501 case ')':
502 paren_level--;
503 break;
504 case '}':
505 brace_level--;
506 break;
507 case ']':
508 bracket_level--;
509 break;
510 case TOKEN_RSHIFT:
511 if (angle_level >= 2)
512 angle_level -= 2;
513 break;
514 /* TOKEN_LSHIFT is never interpreted as two <'s in valid Rust code */
515 default:
516 break;
518 /* Has to be after the token switch to catch the case when we start with the initial level token */
519 if (num_goal_tokens == 0 && angle_level == 0 && paren_level == 0 && brace_level == 0
520 && bracket_level == 0)
521 break;
522 advanceToken(lexer, TRUE);
526 /* Function format:
527 * "fn" <ident>[<type_bounds>] "(" [<args>] ")" ["->" <ret_type>] "{" [<body>] "}"*/
528 static void parseFn (lexerState *lexer, vString *scope, int parent_kind)
530 int kind = (parent_kind == K_TRAIT || parent_kind == K_IMPL) ? K_METHOD : K_FN;
531 vString *name;
532 vString *arg_list;
533 unsigned long line;
534 MIOPos pos;
535 int paren_level = 0;
536 boolean found_paren = FALSE;
537 boolean valid_signature = TRUE;
539 advanceToken(lexer, TRUE);
540 if (lexer->cur_token != TOKEN_IDENT)
541 return;
543 name = vStringNewCopy(lexer->token_str);
544 arg_list = vStringNew();
546 line = lexer->line;
547 pos = lexer->pos;
549 advanceToken(lexer, TRUE);
551 /* HACK: This is a bit coarse as far as what tag entry means by
552 * 'arglist'... */
553 while (lexer->cur_token != '{' && lexer->cur_token != ';')
555 if (lexer->cur_token == '}')
557 valid_signature = FALSE;
558 break;
560 else if (lexer->cur_token == '(')
562 found_paren = TRUE;
563 paren_level++;
565 else if (lexer->cur_token == ')')
567 paren_level--;
568 if (paren_level < 0)
570 valid_signature = FALSE;
571 break;
574 else if (lexer->cur_token == TOKEN_EOF)
576 valid_signature = FALSE;
577 break;
579 writeCurTokenToStr(lexer, arg_list);
580 advanceToken(lexer, FALSE);
582 if (!found_paren || paren_level != 0)
583 valid_signature = FALSE;
585 if (valid_signature)
587 vStringStripTrailing(arg_list);
588 addTag(name, NULL, arg_list->buffer, kind, line, pos, scope, parent_kind);
589 addToScope(scope, name);
590 parseBlock(lexer, TRUE, kind, scope);
593 vStringDelete(name);
594 vStringDelete(arg_list);
597 /* Mod format:
598 * "mod" <ident> "{" [<body>] "}"
599 * "mod" <ident> ";"*/
600 static void parseMod (lexerState *lexer, vString *scope, int parent_kind)
602 advanceToken(lexer, TRUE);
603 if (lexer->cur_token != TOKEN_IDENT)
604 return;
606 addTag(lexer->token_str, NULL, NULL, K_MOD, lexer->line, lexer->pos, scope, parent_kind);
607 addToScope(scope, lexer->token_str);
609 advanceToken(lexer, TRUE);
611 parseBlock(lexer, TRUE, K_MOD, scope);
614 /* Trait format:
615 * "trait" <ident> [<type_bounds>] "{" [<body>] "}"
617 static void parseTrait (lexerState *lexer, vString *scope, int parent_kind)
619 int goal_tokens[] = {'{'};
621 advanceToken(lexer, TRUE);
622 if (lexer->cur_token != TOKEN_IDENT)
623 return;
625 addTag(lexer->token_str, NULL, NULL, K_TRAIT, lexer->line, lexer->pos, scope, parent_kind);
626 addToScope(scope, lexer->token_str);
628 advanceToken(lexer, TRUE);
630 skipUntil(lexer, goal_tokens, 1);
632 parseBlock(lexer, TRUE, K_TRAIT, scope);
635 /* Skips type blocks of the form <T:T<T>, ...> */
636 static void skipTypeBlock (lexerState *lexer)
638 if (lexer->cur_token == '<')
640 skipUntil(lexer, NULL, 0);
641 advanceToken(lexer, TRUE);
645 /* Essentially grabs the last ident before 'for', '<' and '{', which
646 * tends to correspond to what we want as the impl tag entry name */
647 static void parseQualifiedType (lexerState *lexer, vString* name)
649 while (lexer->cur_token != TOKEN_EOF)
651 if (lexer->cur_token == TOKEN_IDENT)
653 if (strcmp(lexer->token_str->buffer, "for") == 0
654 || strcmp(lexer->token_str->buffer, "where") == 0)
655 break;
656 vStringClear(name);
657 vStringCat(name, lexer->token_str);
659 else if (lexer->cur_token == '<' || lexer->cur_token == '{')
661 break;
663 advanceToken(lexer, TRUE);
665 skipTypeBlock(lexer);
668 /* Impl format:
669 * "impl" [<type_bounds>] <qualified_ident>[<type_bounds>] ["for" <qualified_ident>[<type_bounds>]] "{" [<body>] "}"
671 static void parseImpl (lexerState *lexer, vString *scope, int parent_kind)
673 unsigned long line;
674 MIOPos pos;
675 vString *name;
677 advanceToken(lexer, TRUE);
679 line = lexer->line;
680 pos = lexer->pos;
682 skipTypeBlock(lexer);
684 name = vStringNew();
686 parseQualifiedType(lexer, name);
688 if (lexer->cur_token == TOKEN_IDENT && strcmp(lexer->token_str->buffer, "for") == 0)
690 advanceToken(lexer, TRUE);
691 parseQualifiedType(lexer, name);
694 addTag(name, NULL, NULL, K_IMPL, line, pos, scope, parent_kind);
695 addToScope(scope, name);
697 parseBlock(lexer, TRUE, K_IMPL, scope);
699 vStringDelete(name);
702 /* Static format:
703 * "static" ["mut"] <ident>
705 static void parseStatic (lexerState *lexer, vString *scope, int parent_kind)
707 advanceToken(lexer, TRUE);
708 if (lexer->cur_token != TOKEN_IDENT)
709 return;
710 if (strcmp(lexer->token_str->buffer, "mut") == 0)
712 advanceToken(lexer, TRUE);
714 if (lexer->cur_token != TOKEN_IDENT)
715 return;
717 addTag(lexer->token_str, NULL, NULL, K_STATIC, lexer->line, lexer->pos, scope, parent_kind);
720 /* Type format:
721 * "type" <ident>
723 static void parseType (lexerState *lexer, vString *scope, int parent_kind)
725 advanceToken(lexer, TRUE);
726 if (lexer->cur_token != TOKEN_IDENT)
727 return;
729 addTag(lexer->token_str, NULL, NULL, K_TYPE, lexer->line, lexer->pos, scope, parent_kind);
732 /* Structs and enums are very similar syntax-wise.
733 * It is possible to parse variants a bit more cleverly (e.g. make tuple variants functions and
734 * struct variants structs) but it'd be too clever and the signature wouldn't make too much sense without
735 * the enum's definition (e.g. for the type bounds)
737 * Struct/Enum format:
738 * "struct/enum" <ident>[<type_bounds>] "{" [<ident>,]+ "}"
739 * "struct/enum" <ident>[<type_bounds>] ";"
740 * */
741 static void parseStructOrEnum (lexerState *lexer, vString *scope, int parent_kind, boolean is_struct)
743 int kind = is_struct ? K_STRUCT : K_ENUM;
744 int field_kind = is_struct ? K_FIELD : K_VARIANT;
745 int goal_tokens1[] = {';', '{'};
747 advanceToken(lexer, TRUE);
748 if (lexer->cur_token != TOKEN_IDENT)
749 return;
751 addTag(lexer->token_str, NULL, NULL, kind, lexer->line, lexer->pos, scope, parent_kind);
752 addToScope(scope, lexer->token_str);
754 skipUntil(lexer, goal_tokens1, 2);
756 if (lexer->cur_token == '{')
758 vString *field_name = vStringNew();
759 while (lexer->cur_token != TOKEN_EOF)
761 int goal_tokens2[] = {'}', ','};
762 /* Skip attributes. Format:
763 * #[..] or #![..]
764 * */
765 if (lexer->cur_token == '#')
767 advanceToken(lexer, TRUE);
768 if (lexer->cur_token == '!')
769 advanceToken(lexer, TRUE);
770 if (lexer->cur_token == '[')
772 /* It's an attribute, skip it. */
773 skipUntil(lexer, NULL, 0);
775 else
777 /* Something's up with this field, skip to the next one */
778 skipUntil(lexer, goal_tokens2, 2);
779 continue;
782 if (lexer->cur_token == TOKEN_IDENT)
784 if (strcmp(lexer->token_str->buffer, "priv") == 0
785 || strcmp(lexer->token_str->buffer, "pub") == 0)
787 advanceToken(lexer, TRUE);
788 if (lexer->cur_token != TOKEN_IDENT)
790 /* Something's up with this field, skip to the next one */
791 skipUntil(lexer, goal_tokens2, 2);
792 continue;
796 vStringClear(field_name);
797 vStringCat(field_name, lexer->token_str);
798 addTag(field_name, NULL, NULL, field_kind, lexer->line, lexer->pos, scope, kind);
799 skipUntil(lexer, goal_tokens2, 2);
801 if (lexer->cur_token == '}')
803 advanceToken(lexer, TRUE);
804 break;
806 advanceToken(lexer, TRUE);
808 vStringDelete(field_name);
812 /* Skip the body of the macro. Can't use skipUntil here as
813 * the body of the macro may have arbitrary code which confuses it (e.g.
814 * bitshift operators/function return arrows) */
815 static void skipMacro (lexerState *lexer)
817 int level = 0;
818 int plus_token = 0;
819 int minus_token = 0;
821 advanceToken(lexer, TRUE);
822 switch (lexer->cur_token)
824 case '(':
825 plus_token = '(';
826 minus_token = ')';
827 break;
828 case '{':
829 plus_token = '{';
830 minus_token = '}';
831 break;
832 case '[':
833 plus_token = '[';
834 minus_token = ']';
835 break;
836 default:
837 return;
840 while (lexer->cur_token != TOKEN_EOF)
842 if (lexer->cur_token == plus_token)
843 level++;
844 else if (lexer->cur_token == minus_token)
845 level--;
846 if (level == 0)
847 break;
848 advanceToken(lexer, TRUE);
850 advanceToken(lexer, TRUE);
854 * Macro rules format:
855 * "macro_rules" "!" <ident> <macro_body>
857 static void parseMacroRules (lexerState *lexer, vString *scope, int parent_kind)
859 advanceToken(lexer, TRUE);
861 if (lexer->cur_token != '!')
862 return;
864 advanceToken(lexer, TRUE);
866 if (lexer->cur_token != TOKEN_IDENT)
867 return;
869 addTag(lexer->token_str, NULL, NULL, K_MACRO, lexer->line, lexer->pos, scope, parent_kind);
871 skipMacro(lexer);
875 * Rust is very liberal with nesting, so this function is used pretty much for any block
877 static void parseBlock (lexerState *lexer, boolean delim, int kind, vString *scope)
879 int level = 1;
880 if (delim)
882 if (lexer->cur_token != '{')
883 return;
884 advanceToken(lexer, TRUE);
886 while (lexer->cur_token != TOKEN_EOF)
888 if (lexer->cur_token == TOKEN_IDENT)
890 size_t old_scope_len = vStringLength(scope);
891 if (strcmp(lexer->token_str->buffer, "fn") == 0)
893 parseFn(lexer, scope, kind);
895 else if(strcmp(lexer->token_str->buffer, "mod") == 0)
897 parseMod(lexer, scope, kind);
899 else if(strcmp(lexer->token_str->buffer, "static") == 0)
901 parseStatic(lexer, scope, kind);
903 else if(strcmp(lexer->token_str->buffer, "trait") == 0)
905 parseTrait(lexer, scope, kind);
907 else if(strcmp(lexer->token_str->buffer, "type") == 0)
909 parseType(lexer, scope, kind);
911 else if(strcmp(lexer->token_str->buffer, "impl") == 0)
913 parseImpl(lexer, scope, kind);
915 else if(strcmp(lexer->token_str->buffer, "struct") == 0)
917 parseStructOrEnum(lexer, scope, kind, TRUE);
919 else if(strcmp(lexer->token_str->buffer, "enum") == 0)
921 parseStructOrEnum(lexer, scope, kind, FALSE);
923 else if(strcmp(lexer->token_str->buffer, "macro_rules") == 0)
925 parseMacroRules(lexer, scope, kind);
927 else
929 advanceToken(lexer, TRUE);
930 if (lexer->cur_token == '!')
932 skipMacro(lexer);
935 resetScope(scope, old_scope_len);
937 else if (lexer->cur_token == '{')
939 level++;
940 advanceToken(lexer, TRUE);
942 else if (lexer->cur_token == '}')
944 level--;
945 advanceToken(lexer, TRUE);
947 else if (lexer->cur_token == '\'')
949 /* Skip over the 'static lifetime, as it confuses the static parser above */
950 advanceToken(lexer, TRUE);
951 if (lexer->cur_token == TOKEN_IDENT && strcmp(lexer->token_str->buffer, "static") == 0)
952 advanceToken(lexer, TRUE);
954 else
956 advanceToken(lexer, TRUE);
958 if (delim && level <= 0)
959 break;
963 static void findRustTags (void)
965 lexerState lexer;
966 vString* scope = vStringNew();
967 initLexer(&lexer);
969 parseBlock(&lexer, FALSE, K_NONE, scope);
970 vStringDelete(scope);
972 deInitLexer(&lexer);
975 extern parserDefinition *RustParser (void)
977 static const char *const extensions[] = { "rs", NULL };
978 parserDefinition *def = parserNew ("Rust");
979 def->kinds = rustKinds;
980 def->kindCount = KIND_COUNT (rustKinds);
981 def->extensions = extensions;
982 def->parser = findRustTags;
984 return def;