3 * This source code is released for free distribution under the terms of the
4 * GNU General Public License version 2 or (at your option) any later version.
6 * This module contains functions for generating tags for Rust files.
12 #include "general.h" /* must always come first */
28 #define MAX_STRING_LENGTH 256
50 static kindOption rustKinds
[] = {
51 {TRUE
, 'n', "module", "module"},
52 {TRUE
, 's', "struct", "structural type"},
53 {TRUE
, 'i', "interface", "trait interface"},
54 {TRUE
, 'c', "implementation", "implementation"},
55 {TRUE
, 'f', "function", "Function"},
56 {TRUE
, 'g', "enum", "Enum"},
57 {TRUE
, 't', "typedef", "Type Alias"},
58 {TRUE
, 'v', "variable", "Global variable"},
59 {TRUE
, 'M', "macro", "Macro Definition"},
60 {TRUE
, 'm', "field", "A struct field"},
61 {TRUE
, 'e', "enumerator", "An enum variant"},
62 {TRUE
, 'F', "method", "A method"},
91 static void parseBlock (lexerState
*lexer
, boolean delim
, int kind
, vString
*scope
);
94 * FUNCTION DEFINITIONS
97 /* Resets the scope string to the old length */
98 static void resetScope (vString
*scope
, size_t old_len
)
100 scope
->length
= old_len
;
101 scope
->buffer
[old_len
] = '\0';
104 /* Adds a name to the end of the scope string */
105 static void addToScope (vString
*scope
, vString
*name
)
107 if (vStringLength(scope
) > 0)
108 vStringCatS(scope
, "::");
109 vStringCat(scope
, name
);
112 /* Write the lexer's current token to string, taking care of special tokens */
113 static void writeCurTokenToStr (lexerState
*lexer
, vString
*out_str
)
115 switch (lexer
->cur_token
)
118 vStringCat(out_str
, lexer
->token_str
);
121 vStringCat(out_str
, lexer
->token_str
);
123 case TOKEN_WHITESPACE
:
124 vStringPut(out_str
, ' ');
127 vStringCatS(out_str
, "<<");
130 vStringCatS(out_str
, ">>");
133 vStringCatS(out_str
, "->");
136 vStringPut(out_str
, (char) lexer
->cur_token
);
140 /* Reads a character from the file */
141 static void advanceChar (lexerState
*lexer
)
143 lexer
->cur_c
= lexer
->next_c
;
144 lexer
->next_c
= fileGetc();
147 /* Reads N characters from the file */
148 static void advanceNChar (lexerState
*lexer
, int n
)
154 /* Store the current character in lexerState::token_str if there is space
155 * (set by MAX_STRING_LENGTH), and then read the next character from the file */
156 static void advanceAndStoreChar (lexerState
*lexer
)
158 if (vStringLength(lexer
->token_str
) < MAX_STRING_LENGTH
)
159 vStringPut(lexer
->token_str
, (char) lexer
->cur_c
);
163 static boolean
isWhitespace (int c
)
165 return c
== ' ' || c
== '\t' || c
== '\r' || c
== '\n';
168 static boolean
isAscii (int c
)
170 return (c
>= 0) && (c
< 0x80);
173 /* This isn't quite right for Unicode identifiers */
174 static boolean
isIdentifierStart (int c
)
176 return (isAscii(c
) && (isalpha(c
) || c
== '_')) || !isAscii(c
);
179 /* This isn't quite right for Unicode identifiers */
180 static boolean
isIdentifierContinue (int c
)
182 return (isAscii(c
) && (isalnum(c
) || c
== '_')) || !isAscii(c
);
185 static void scanWhitespace (lexerState
*lexer
)
187 while (isWhitespace(lexer
->cur_c
))
191 /* Normal line comments start with two /'s and continue until the next \n
192 * (potentially after a \r). Additionally, a shebang in the beginning of the
193 * file also counts as a line comment as long as it is not this sequence: #![ .
194 * Block comments start with / followed by a * and end with a * followed by a /.
195 * Unlike in C/C++ they nest. */
196 static void scanComments (lexerState
*lexer
)
199 if (lexer
->next_c
== '/')
201 advanceNChar(lexer
, 2);
202 while (lexer
->cur_c
!= EOF
&& lexer
->cur_c
!= '\n')
206 else if (lexer
->next_c
== '!')
208 advanceNChar(lexer
, 2);
209 /* If it is exactly #![ then it is not a comment, but an attribute */
210 if (lexer
->cur_c
== '[')
212 while (lexer
->cur_c
!= EOF
&& lexer
->cur_c
!= '\n')
216 else if (lexer
->next_c
== '*')
219 advanceNChar(lexer
, 2);
220 while (lexer
->cur_c
!= EOF
&& level
> 0)
222 if (lexer
->cur_c
== '*' && lexer
->next_c
== '/')
225 advanceNChar(lexer
, 2);
227 else if (lexer
->cur_c
== '/' && lexer
->next_c
== '*')
230 advanceNChar(lexer
, 2);
240 static void scanIdentifier (lexerState
*lexer
)
242 vStringClear(lexer
->token_str
);
245 advanceAndStoreChar(lexer
);
246 } while(lexer
->cur_c
!= EOF
&& isIdentifierContinue(lexer
->cur_c
));
249 /* Double-quoted strings, we only care about the \" escape. These
250 * last past the end of the line, so be careful not too store too much
251 * of them (see MAX_STRING_LENGTH). The only place we look at their
252 * contents is in the function definitions, and there the valid strings are
253 * things like "C" and "Rust" */
254 static void scanString (lexerState
*lexer
)
256 vStringClear(lexer
->token_str
);
257 advanceAndStoreChar(lexer
);
258 while (lexer
->cur_c
!= EOF
&& lexer
->cur_c
!= '"')
260 if (lexer
->cur_c
== '\\' && lexer
->next_c
== '"')
261 advanceAndStoreChar(lexer
);
262 advanceAndStoreChar(lexer
);
264 advanceAndStoreChar(lexer
);
267 /* Raw strings look like this: r"" or r##""## where the number of
268 * hashes must match */
269 static void scanRawString (lexerState
*lexer
)
271 size_t num_initial_hashes
= 0;
272 vStringClear(lexer
->token_str
);
273 advanceAndStoreChar(lexer
);
274 /* Count how many leading hashes there are */
275 while (lexer
->cur_c
== '#')
277 num_initial_hashes
++;
278 advanceAndStoreChar(lexer
);
280 if (lexer
->cur_c
!= '"')
282 advanceAndStoreChar(lexer
);
283 while (lexer
->cur_c
!= EOF
)
285 /* Count how many trailing hashes there are. If the number is equal or more
286 * than the number of leading hashes, break. */
287 if (lexer
->cur_c
== '"')
289 size_t num_trailing_hashes
= 0;
290 advanceAndStoreChar(lexer
);
291 while (lexer
->cur_c
== '#' && num_trailing_hashes
< num_initial_hashes
)
293 num_trailing_hashes
++;
295 advanceAndStoreChar(lexer
);
297 if (num_trailing_hashes
== num_initial_hashes
)
302 advanceAndStoreChar(lexer
);
307 /* This deals with character literals: 'n', '\n', '\uFFFF'; and lifetimes:
308 * 'lifetime. We'll use this approximate regexp for the literals:
309 * \' \\ [^']+ \' or \' [^'] \' or \' \\ \' \'. Either way, we'll treat this
310 * token as a string, so it gets preserved as is for function signatures with
312 static void scanCharacterOrLifetime (lexerState
*lexer
)
314 vStringClear(lexer
->token_str
);
315 advanceAndStoreChar(lexer
);
317 if (lexer
->cur_c
== '\\')
319 advanceAndStoreChar(lexer
);
320 /* The \' \\ \' \' (literally '\'') case */
321 if (lexer
->cur_c
== '\'' && lexer
->next_c
== '\'')
323 advanceAndStoreChar(lexer
);
324 advanceAndStoreChar(lexer
);
326 /* The \' \\ [^']+ \' case */
329 while (lexer
->cur_c
!= EOF
&& lexer
->cur_c
!= '\'')
330 advanceAndStoreChar(lexer
);
333 /* The \' [^'] \' case */
334 else if (lexer
->cur_c
!= '\'' && lexer
->next_c
== '\'')
336 advanceAndStoreChar(lexer
);
337 advanceAndStoreChar(lexer
);
339 /* Otherwise it is malformed, or a lifetime */
342 /* Advances the parser one token, optionally skipping whitespace
343 * (otherwise it is concatenated and returned as a single whitespace token).
344 * Whitespace is needed to properly render function signatures. Unrecognized
345 * token starts are stored literally, e.g. token may equal to a character '#'. */
346 static int advanceToken (lexerState
*lexer
, boolean skip_whitspace
)
348 boolean have_whitespace
= FALSE
;
349 lexer
->line
= getSourceLineNumber();
350 lexer
->pos
= getInputFilePosition();
351 while (lexer
->cur_c
!= EOF
)
353 if (isWhitespace(lexer
->cur_c
))
355 scanWhitespace(lexer
);
356 have_whitespace
= TRUE
;
358 else if (lexer
->cur_c
== '/' && (lexer
->next_c
== '/' || lexer
->next_c
== '*'))
361 have_whitespace
= TRUE
;
365 if (have_whitespace
&& !skip_whitspace
)
366 return lexer
->cur_token
= TOKEN_WHITESPACE
;
370 lexer
->line
= getSourceLineNumber();
371 lexer
->pos
= getInputFilePosition();
372 while (lexer
->cur_c
!= EOF
)
374 if (lexer
->cur_c
== '"')
377 return lexer
->cur_token
= TOKEN_STRING
;
379 else if (lexer
->cur_c
== 'r' && (lexer
->next_c
== '#' || lexer
->next_c
== '"'))
381 scanRawString(lexer
);
382 return lexer
->cur_token
= TOKEN_STRING
;
384 else if (lexer
->cur_c
== '\'')
386 scanCharacterOrLifetime(lexer
);
387 return lexer
->cur_token
= TOKEN_STRING
;
389 else if (isIdentifierStart(lexer
->cur_c
))
391 scanIdentifier(lexer
);
392 return lexer
->cur_token
= TOKEN_IDENT
;
394 /* These shift tokens aren't too important for tag-generation per se,
395 * but they confuse the skipUntil code which tracks the <> pairs. */
396 else if (lexer
->cur_c
== '>' && lexer
->next_c
== '>')
398 advanceNChar(lexer
, 2);
399 return lexer
->cur_token
= TOKEN_RSHIFT
;
401 else if (lexer
->cur_c
== '<' && lexer
->next_c
== '<')
403 advanceNChar(lexer
, 2);
404 return lexer
->cur_token
= TOKEN_LSHIFT
;
406 else if (lexer
->cur_c
== '-' && lexer
->next_c
== '>')
408 advanceNChar(lexer
, 2);
409 return lexer
->cur_token
= TOKEN_RARROW
;
413 int c
= lexer
->cur_c
;
415 return lexer
->cur_token
= c
;
418 return lexer
->cur_token
= TOKEN_EOF
;
421 static void initLexer (lexerState
*lexer
)
423 advanceNChar(lexer
, 2);
424 lexer
->token_str
= vStringNew();
426 if (lexer
->cur_c
== '#' && lexer
->next_c
== '!')
428 advanceToken(lexer
, TRUE
);
431 static void deInitLexer (lexerState
*lexer
)
433 vStringDelete(lexer
->token_str
);
434 lexer
->token_str
= NULL
;
437 static void addTag (vString
* ident
, const char* type
, const char* arg_list
, int kind
, unsigned long line
, MIOPos pos
, vString
*scope
, int parent_kind
)
442 initTagEntry(&tag
, ident
->buffer
);
444 tag
.lineNumber
= line
;
445 tag
.filePosition
= pos
;
446 tag
.sourceFileName
= getSourceFileName();
448 tag
.kindName
= rustKinds
[kind
].name
;
449 tag
.kind
= rustKinds
[kind
].letter
;
451 tag
.extensionFields
.signature
= arg_list
;
452 tag
.extensionFields
.varType
= type
;
453 if (parent_kind
!= K_NONE
)
455 tag
.extensionFields
.scope
[0] = rustKinds
[parent_kind
].name
;
456 tag
.extensionFields
.scope
[1] = scope
->buffer
;
461 /* Skip tokens until one of the goal tokens is hit. Escapes when level = 0 if there are no goal tokens.
462 * Keeps track of balanced <>'s, ()'s, []'s, and {}'s and ignores the goal tokens within those pairings */
463 static void skipUntil (lexerState
*lexer
, int goal_tokens
[], int num_goal_tokens
)
468 int bracket_level
= 0;
469 while (lexer
->cur_token
!= TOKEN_EOF
)
471 if (angle_level
== 0 && paren_level
== 0 && brace_level
== 0
472 && bracket_level
== 0)
475 for(ii
= 0; ii
< num_goal_tokens
; ii
++)
477 if (lexer
->cur_token
== goal_tokens
[ii
])
482 if (ii
< num_goal_tokens
)
485 switch (lexer
->cur_token
)
512 if (angle_level
>= 2)
515 /* TOKEN_LSHIFT is never interpreted as two <'s in valid Rust code */
519 /* Has to be after the token switch to catch the case when we start with the initial level token */
520 if (num_goal_tokens
== 0 && angle_level
== 0 && paren_level
== 0 && brace_level
== 0
521 && bracket_level
== 0)
523 advanceToken(lexer
, TRUE
);
528 * "fn" <ident>[<type_bounds>] "(" [<args>] ")" ["->" <ret_type>] "{" [<body>] "}"*/
529 static void parseFn (lexerState
*lexer
, vString
*scope
, int parent_kind
)
531 int kind
= (parent_kind
== K_TRAIT
|| parent_kind
== K_IMPL
) ? K_METHOD
: K_FN
;
537 boolean found_paren
= FALSE
;
538 boolean valid_signature
= TRUE
;
540 advanceToken(lexer
, TRUE
);
541 if (lexer
->cur_token
!= TOKEN_IDENT
)
544 name
= vStringNewCopy(lexer
->token_str
);
545 arg_list
= vStringNew();
550 advanceToken(lexer
, TRUE
);
552 /* HACK: This is a bit coarse as far as what tag entry means by
554 while (lexer
->cur_token
!= '{' && lexer
->cur_token
!= ';')
556 if (lexer
->cur_token
== '}')
558 valid_signature
= FALSE
;
561 else if (lexer
->cur_token
== '(')
566 else if (lexer
->cur_token
== ')')
571 valid_signature
= FALSE
;
575 else if (lexer
->cur_token
== TOKEN_EOF
)
577 valid_signature
= FALSE
;
580 writeCurTokenToStr(lexer
, arg_list
);
581 advanceToken(lexer
, FALSE
);
583 if (!found_paren
|| paren_level
!= 0)
584 valid_signature
= FALSE
;
588 vStringStripTrailing(arg_list
);
589 addTag(name
, NULL
, arg_list
->buffer
, kind
, line
, pos
, scope
, parent_kind
);
590 addToScope(scope
, name
);
591 parseBlock(lexer
, TRUE
, kind
, scope
);
595 vStringDelete(arg_list
);
599 * "mod" <ident> "{" [<body>] "}"
600 * "mod" <ident> ";"*/
601 static void parseMod (lexerState
*lexer
, vString
*scope
, int parent_kind
)
603 advanceToken(lexer
, TRUE
);
604 if (lexer
->cur_token
!= TOKEN_IDENT
)
607 addTag(lexer
->token_str
, NULL
, NULL
, K_MOD
, lexer
->line
, lexer
->pos
, scope
, parent_kind
);
608 addToScope(scope
, lexer
->token_str
);
610 advanceToken(lexer
, TRUE
);
612 parseBlock(lexer
, TRUE
, K_MOD
, scope
);
616 * "trait" <ident> [<type_bounds>] "{" [<body>] "}"
618 static void parseTrait (lexerState
*lexer
, vString
*scope
, int parent_kind
)
620 int goal_tokens
[] = {'{'};
622 advanceToken(lexer
, TRUE
);
623 if (lexer
->cur_token
!= TOKEN_IDENT
)
626 addTag(lexer
->token_str
, NULL
, NULL
, K_TRAIT
, lexer
->line
, lexer
->pos
, scope
, parent_kind
);
627 addToScope(scope
, lexer
->token_str
);
629 advanceToken(lexer
, TRUE
);
631 skipUntil(lexer
, goal_tokens
, 1);
633 parseBlock(lexer
, TRUE
, K_TRAIT
, scope
);
636 /* Skips type blocks of the form <T:T<T>, ...> */
637 static void skipTypeBlock (lexerState
*lexer
)
639 if (lexer
->cur_token
== '<')
641 skipUntil(lexer
, NULL
, 0);
642 advanceToken(lexer
, TRUE
);
646 /* Essentially grabs the last ident before 'for', '<' and '{', which
647 * tends to correspond to what we want as the impl tag entry name */
648 static void parseQualifiedType (lexerState
*lexer
, vString
* name
)
650 while (lexer
->cur_token
!= TOKEN_EOF
)
652 if (lexer
->cur_token
== TOKEN_IDENT
)
654 if (strcmp(lexer
->token_str
->buffer
, "for") == 0
655 || strcmp(lexer
->token_str
->buffer
, "where") == 0)
658 vStringCat(name
, lexer
->token_str
);
660 else if (lexer
->cur_token
== '<' || lexer
->cur_token
== '{')
664 advanceToken(lexer
, TRUE
);
666 skipTypeBlock(lexer
);
670 * "impl" [<type_bounds>] <qualified_ident>[<type_bounds>] ["for" <qualified_ident>[<type_bounds>]] "{" [<body>] "}"
672 static void parseImpl (lexerState
*lexer
, vString
*scope
, int parent_kind
)
678 advanceToken(lexer
, TRUE
);
683 skipTypeBlock(lexer
);
687 parseQualifiedType(lexer
, name
);
689 if (lexer
->cur_token
== TOKEN_IDENT
&& strcmp(lexer
->token_str
->buffer
, "for") == 0)
691 advanceToken(lexer
, TRUE
);
692 parseQualifiedType(lexer
, name
);
695 addTag(name
, NULL
, NULL
, K_IMPL
, line
, pos
, scope
, parent_kind
);
696 addToScope(scope
, name
);
698 parseBlock(lexer
, TRUE
, K_IMPL
, scope
);
704 * "static" ["mut"] <ident>
706 static void parseStatic (lexerState
*lexer
, vString
*scope
, int parent_kind
)
708 advanceToken(lexer
, TRUE
);
709 if (lexer
->cur_token
!= TOKEN_IDENT
)
711 if (strcmp(lexer
->token_str
->buffer
, "mut") == 0)
713 advanceToken(lexer
, TRUE
);
715 if (lexer
->cur_token
!= TOKEN_IDENT
)
718 addTag(lexer
->token_str
, NULL
, NULL
, K_STATIC
, lexer
->line
, lexer
->pos
, scope
, parent_kind
);
724 static void parseType (lexerState
*lexer
, vString
*scope
, int parent_kind
)
726 advanceToken(lexer
, TRUE
);
727 if (lexer
->cur_token
!= TOKEN_IDENT
)
730 addTag(lexer
->token_str
, NULL
, NULL
, K_TYPE
, lexer
->line
, lexer
->pos
, scope
, parent_kind
);
733 /* Structs and enums are very similar syntax-wise.
734 * It is possible to parse variants a bit more cleverly (e.g. make tuple variants functions and
735 * struct variants structs) but it'd be too clever and the signature wouldn't make too much sense without
736 * the enum's definition (e.g. for the type bounds)
738 * Struct/Enum format:
739 * "struct/enum" <ident>[<type_bounds>] "{" [<ident>,]+ "}"
740 * "struct/enum" <ident>[<type_bounds>] ";"
742 static void parseStructOrEnum (lexerState
*lexer
, vString
*scope
, int parent_kind
, boolean is_struct
)
744 int kind
= is_struct
? K_STRUCT
: K_ENUM
;
745 int field_kind
= is_struct
? K_FIELD
: K_VARIANT
;
746 int goal_tokens1
[] = {';', '{'};
748 advanceToken(lexer
, TRUE
);
749 if (lexer
->cur_token
!= TOKEN_IDENT
)
752 addTag(lexer
->token_str
, NULL
, NULL
, kind
, lexer
->line
, lexer
->pos
, scope
, parent_kind
);
753 addToScope(scope
, lexer
->token_str
);
755 skipUntil(lexer
, goal_tokens1
, 2);
757 if (lexer
->cur_token
== '{')
759 vString
*field_name
= vStringNew();
760 while (lexer
->cur_token
!= TOKEN_EOF
)
762 int goal_tokens2
[] = {'}', ','};
763 /* Skip attributes. Format:
766 if (lexer
->cur_token
== '#')
768 advanceToken(lexer
, TRUE
);
769 if (lexer
->cur_token
== '!')
770 advanceToken(lexer
, TRUE
);
771 if (lexer
->cur_token
== '[')
773 /* It's an attribute, skip it. */
774 skipUntil(lexer
, NULL
, 0);
778 /* Something's up with this field, skip to the next one */
779 skipUntil(lexer
, goal_tokens2
, 2);
783 if (lexer
->cur_token
== TOKEN_IDENT
)
785 if (strcmp(lexer
->token_str
->buffer
, "priv") == 0
786 || strcmp(lexer
->token_str
->buffer
, "pub") == 0)
788 advanceToken(lexer
, TRUE
);
789 if (lexer
->cur_token
!= TOKEN_IDENT
)
791 /* Something's up with this field, skip to the next one */
792 skipUntil(lexer
, goal_tokens2
, 2);
797 vStringClear(field_name
);
798 vStringCat(field_name
, lexer
->token_str
);
799 addTag(field_name
, NULL
, NULL
, field_kind
, lexer
->line
, lexer
->pos
, scope
, kind
);
800 skipUntil(lexer
, goal_tokens2
, 2);
802 if (lexer
->cur_token
== '}')
804 advanceToken(lexer
, TRUE
);
807 advanceToken(lexer
, TRUE
);
809 vStringDelete(field_name
);
813 /* Skip the body of the macro. Can't use skipUntil here as
814 * the body of the macro may have arbitrary code which confuses it (e.g.
815 * bitshift operators/function return arrows) */
816 static void skipMacro (lexerState
*lexer
)
822 advanceToken(lexer
, TRUE
);
823 switch (lexer
->cur_token
)
841 while (lexer
->cur_token
!= TOKEN_EOF
)
843 if (lexer
->cur_token
== plus_token
)
845 else if (lexer
->cur_token
== minus_token
)
849 advanceToken(lexer
, TRUE
);
851 advanceToken(lexer
, TRUE
);
855 * Macro rules format:
856 * "macro_rules" "!" <ident> <macro_body>
858 static void parseMacroRules (lexerState
*lexer
, vString
*scope
, int parent_kind
)
860 advanceToken(lexer
, TRUE
);
862 if (lexer
->cur_token
!= '!')
865 advanceToken(lexer
, TRUE
);
867 if (lexer
->cur_token
!= TOKEN_IDENT
)
870 addTag(lexer
->token_str
, NULL
, NULL
, K_MACRO
, lexer
->line
, lexer
->pos
, scope
, parent_kind
);
876 * Rust is very liberal with nesting, so this function is used pretty much for any block
878 static void parseBlock (lexerState
*lexer
, boolean delim
, int kind
, vString
*scope
)
883 if (lexer
->cur_token
!= '{')
885 advanceToken(lexer
, TRUE
);
887 while (lexer
->cur_token
!= TOKEN_EOF
)
889 if (lexer
->cur_token
== TOKEN_IDENT
)
891 size_t old_scope_len
= vStringLength(scope
);
892 if (strcmp(lexer
->token_str
->buffer
, "fn") == 0)
894 parseFn(lexer
, scope
, kind
);
896 else if(strcmp(lexer
->token_str
->buffer
, "mod") == 0)
898 parseMod(lexer
, scope
, kind
);
900 else if(strcmp(lexer
->token_str
->buffer
, "static") == 0)
902 parseStatic(lexer
, scope
, kind
);
904 else if(strcmp(lexer
->token_str
->buffer
, "trait") == 0)
906 parseTrait(lexer
, scope
, kind
);
908 else if(strcmp(lexer
->token_str
->buffer
, "type") == 0)
910 parseType(lexer
, scope
, kind
);
912 else if(strcmp(lexer
->token_str
->buffer
, "impl") == 0)
914 parseImpl(lexer
, scope
, kind
);
916 else if(strcmp(lexer
->token_str
->buffer
, "struct") == 0)
918 parseStructOrEnum(lexer
, scope
, kind
, TRUE
);
920 else if(strcmp(lexer
->token_str
->buffer
, "enum") == 0)
922 parseStructOrEnum(lexer
, scope
, kind
, FALSE
);
924 else if(strcmp(lexer
->token_str
->buffer
, "macro_rules") == 0)
926 parseMacroRules(lexer
, scope
, kind
);
930 advanceToken(lexer
, TRUE
);
931 if (lexer
->cur_token
== '!')
936 resetScope(scope
, old_scope_len
);
938 else if (lexer
->cur_token
== '{')
941 advanceToken(lexer
, TRUE
);
943 else if (lexer
->cur_token
== '}')
946 advanceToken(lexer
, TRUE
);
948 else if (lexer
->cur_token
== '\'')
950 /* Skip over the 'static lifetime, as it confuses the static parser above */
951 advanceToken(lexer
, TRUE
);
952 if (lexer
->cur_token
== TOKEN_IDENT
&& strcmp(lexer
->token_str
->buffer
, "static") == 0)
953 advanceToken(lexer
, TRUE
);
957 advanceToken(lexer
, TRUE
);
959 if (delim
&& level
<= 0)
964 static void findRustTags (void)
967 vString
* scope
= vStringNew();
970 parseBlock(&lexer
, FALSE
, K_NONE
, scope
);
971 vStringDelete(scope
);
976 extern parserDefinition
*RustParser (void)
978 static const char *const extensions
[] = { "rs", NULL
};
979 parserDefinition
*def
= parserNew ("Rust");
980 def
->kinds
= rustKinds
;
981 def
->kindCount
= KIND_COUNT (rustKinds
);
982 def
->extensions
= extensions
;
983 def
->parser
= findRustTags
;