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.
12 #include "general.h" /* must always come first */
27 #define MAX_STRING_LENGTH 256
49 static kindOption rustKinds
[] = {
50 {TRUE
, 'n', "namespace", "module"},
51 {TRUE
, 's', "struct", "structural type"},
52 {TRUE
, 'i', "interface", "trait interface"},
53 {TRUE
, 'c', "class", "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"},
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
)
117 vStringCat(out_str
, lexer
->token_str
);
120 vStringCat(out_str
, lexer
->token_str
);
122 case TOKEN_WHITESPACE
:
123 vStringPut(out_str
, ' ');
126 vStringCatS(out_str
, "<<");
129 vStringCatS(out_str
, ">>");
132 vStringCatS(out_str
, "->");
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
)
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
);
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
))
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
)
198 if (lexer
->next_c
== '/')
200 advanceNChar(lexer
, 2);
201 while (lexer
->cur_c
!= EOF
&& lexer
->cur_c
!= '\n')
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
== '[')
211 while (lexer
->cur_c
!= EOF
&& lexer
->cur_c
!= '\n')
215 else if (lexer
->next_c
== '*')
218 advanceNChar(lexer
, 2);
219 while (lexer
->cur_c
!= EOF
&& level
> 0)
221 if (lexer
->cur_c
== '*' && lexer
->next_c
== '/')
224 advanceNChar(lexer
, 2);
226 else if (lexer
->cur_c
== '/' && lexer
->next_c
== '*')
229 advanceNChar(lexer
, 2);
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
!= '"')
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
)
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
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 */
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
== '*'))
360 have_whitespace
= TRUE
;
364 if (have_whitespace
&& !skip_whitspace
)
365 return lexer
->cur_token
= TOKEN_WHITESPACE
;
369 lexer
->line
= getSourceLineNumber();
370 lexer
->pos
= getInputFilePosition();
371 while (lexer
->cur_c
!= EOF
)
373 if (lexer
->cur_c
== '"')
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
;
412 int c
= lexer
->cur_c
;
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
== '!')
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
)
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
.arglist
= 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
;
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
)
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)
474 for(ii
= 0; ii
< num_goal_tokens
; ii
++)
476 if (lexer
->cur_token
== goal_tokens
[ii
])
481 if (ii
< num_goal_tokens
)
484 switch (lexer
->cur_token
)
511 if (angle_level
>= 2)
514 /* TOKEN_LSHIFT is never interpreted as two <'s in valid Rust code */
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)
522 advanceToken(lexer
, TRUE
);
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
;
536 boolean found_paren
= FALSE
;
537 boolean valid_signature
= TRUE
;
539 advanceToken(lexer
, TRUE
);
540 if (lexer
->cur_token
!= TOKEN_IDENT
)
543 name
= vStringNewCopy(lexer
->token_str
);
544 arg_list
= vStringNew();
549 advanceToken(lexer
, TRUE
);
551 /* HACK: This is a bit coarse as far as what tag entry means by
553 while (lexer
->cur_token
!= '{' && lexer
->cur_token
!= ';')
555 if (lexer
->cur_token
== '}')
557 valid_signature
= FALSE
;
560 else if (lexer
->cur_token
== '(')
565 else if (lexer
->cur_token
== ')')
570 valid_signature
= FALSE
;
574 else if (lexer
->cur_token
== TOKEN_EOF
)
576 valid_signature
= FALSE
;
579 writeCurTokenToStr(lexer
, arg_list
);
580 advanceToken(lexer
, FALSE
);
582 if (!found_paren
|| paren_level
!= 0)
583 valid_signature
= FALSE
;
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
);
594 vStringDelete(arg_list
);
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
)
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
);
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
)
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)
656 vStringCat(name
, lexer
->token_str
);
658 else if (lexer
->cur_token
== '<' || lexer
->cur_token
== '{')
662 advanceToken(lexer
, TRUE
);
664 skipTypeBlock(lexer
);
668 * "impl" [<type_bounds>] <qualified_ident>[<type_bounds>] ["for" <qualified_ident>[<type_bounds>]] "{" [<body>] "}"
670 static void parseImpl (lexerState
*lexer
, vString
*scope
, int parent_kind
)
676 advanceToken(lexer
, TRUE
);
681 skipTypeBlock(lexer
);
685 parseQualifiedType(lexer
, name
);
687 if (lexer
->cur_token
== TOKEN_IDENT
&& strcmp(lexer
->token_str
->buffer
, "for") == 0)
689 advanceToken(lexer
, TRUE
);
690 parseQualifiedType(lexer
, name
);
693 addTag(name
, NULL
, NULL
, K_IMPL
, line
, pos
, scope
, parent_kind
);
694 addToScope(scope
, name
);
696 parseBlock(lexer
, TRUE
, K_IMPL
, scope
);
702 * "static" ["mut"] <ident>
704 static void parseStatic (lexerState
*lexer
, vString
*scope
, int parent_kind
)
706 advanceToken(lexer
, TRUE
);
707 if (lexer
->cur_token
!= TOKEN_IDENT
)
709 if (strcmp(lexer
->token_str
->buffer
, "mut") == 0)
711 advanceToken(lexer
, TRUE
);
713 if (lexer
->cur_token
!= TOKEN_IDENT
)
716 addTag(lexer
->token_str
, NULL
, NULL
, K_STATIC
, lexer
->line
, lexer
->pos
, scope
, parent_kind
);
722 static void parseType (lexerState
*lexer
, vString
*scope
, int parent_kind
)
724 advanceToken(lexer
, TRUE
);
725 if (lexer
->cur_token
!= TOKEN_IDENT
)
728 addTag(lexer
->token_str
, NULL
, NULL
, K_TYPE
, lexer
->line
, lexer
->pos
, scope
, parent_kind
);
731 /* Structs and enums are very similar syntax-wise.
732 * It is possible to parse variants a bit more cleverly (e.g. make tuple variants functions and
733 * struct variants structs) but it'd be too clever and the signature wouldn't make too much sense without
734 * the enum's definition (e.g. for the type bounds)
736 * Struct/Enum format:
737 * "struct/enum" <ident>[<type_bounds>] "{" [<ident>,]+ "}"
738 * "struct/enum" <ident>[<type_bounds>] ";"
740 static void parseStructOrEnum (lexerState
*lexer
, vString
*scope
, int parent_kind
, boolean is_struct
)
742 int kind
= is_struct
? K_STRUCT
: K_ENUM
;
743 int field_kind
= is_struct
? K_FIELD
: K_VARIANT
;
744 int goal_tokens1
[] = {';', '{'};
746 advanceToken(lexer
, TRUE
);
747 if (lexer
->cur_token
!= TOKEN_IDENT
)
750 addTag(lexer
->token_str
, NULL
, NULL
, kind
, lexer
->line
, lexer
->pos
, scope
, parent_kind
);
751 addToScope(scope
, lexer
->token_str
);
753 skipUntil(lexer
, goal_tokens1
, 2);
755 if (lexer
->cur_token
== '{')
757 vString
*field_name
= vStringNew();
758 while (lexer
->cur_token
!= TOKEN_EOF
)
760 int goal_tokens2
[] = {'}', ','};
761 /* Skip attributes. Format:
764 if (lexer
->cur_token
== '#')
766 advanceToken(lexer
, TRUE
);
767 if (lexer
->cur_token
== '!')
768 advanceToken(lexer
, TRUE
);
769 if (lexer
->cur_token
== '[')
771 /* It's an attribute, skip it. */
772 skipUntil(lexer
, NULL
, 0);
776 /* Something's up with this field, skip to the next one */
777 skipUntil(lexer
, goal_tokens2
, 2);
781 if (lexer
->cur_token
== TOKEN_IDENT
)
783 if (strcmp(lexer
->token_str
->buffer
, "priv") == 0
784 || strcmp(lexer
->token_str
->buffer
, "pub") == 0)
786 advanceToken(lexer
, TRUE
);
787 if (lexer
->cur_token
!= TOKEN_IDENT
)
789 /* Something's up with this field, skip to the next one */
790 skipUntil(lexer
, goal_tokens2
, 2);
795 vStringClear(field_name
);
796 vStringCat(field_name
, lexer
->token_str
);
797 addTag(field_name
, NULL
, NULL
, field_kind
, lexer
->line
, lexer
->pos
, scope
, kind
);
798 skipUntil(lexer
, goal_tokens2
, 2);
800 if (lexer
->cur_token
== '}')
802 advanceToken(lexer
, TRUE
);
805 advanceToken(lexer
, TRUE
);
807 vStringDelete(field_name
);
811 /* Skip the body of the macro. Can't use skipUntil here as
812 * the body of the macro may have arbitrary code which confuses it (e.g.
813 * bitshift operators/function return arrows) */
814 static void skipMacro (lexerState
*lexer
)
820 advanceToken(lexer
, TRUE
);
821 switch (lexer
->cur_token
)
839 while (lexer
->cur_token
!= TOKEN_EOF
)
841 if (lexer
->cur_token
== plus_token
)
843 else if (lexer
->cur_token
== minus_token
)
847 advanceToken(lexer
, TRUE
);
849 advanceToken(lexer
, TRUE
);
853 * Macro rules format:
854 * "macro_rules" "!" <ident> <macro_body>
856 static void parseMacroRules (lexerState
*lexer
, vString
*scope
, int parent_kind
)
858 advanceToken(lexer
, TRUE
);
860 if (lexer
->cur_token
!= '!')
863 advanceToken(lexer
, TRUE
);
865 if (lexer
->cur_token
!= TOKEN_IDENT
)
868 addTag(lexer
->token_str
, NULL
, NULL
, K_MACRO
, lexer
->line
, lexer
->pos
, scope
, parent_kind
);
874 * Rust is very liberal with nesting, so this function is used pretty much for any block
876 static void parseBlock (lexerState
*lexer
, boolean delim
, int kind
, vString
*scope
)
881 if (lexer
->cur_token
!= '{')
883 advanceToken(lexer
, TRUE
);
885 while (lexer
->cur_token
!= TOKEN_EOF
)
887 if (lexer
->cur_token
== TOKEN_IDENT
)
889 size_t old_scope_len
= vStringLength(scope
);
890 if (strcmp(lexer
->token_str
->buffer
, "fn") == 0)
892 parseFn(lexer
, scope
, kind
);
894 else if(strcmp(lexer
->token_str
->buffer
, "mod") == 0)
896 parseMod(lexer
, scope
, kind
);
898 else if(strcmp(lexer
->token_str
->buffer
, "static") == 0)
900 parseStatic(lexer
, scope
, kind
);
902 else if(strcmp(lexer
->token_str
->buffer
, "trait") == 0)
904 parseTrait(lexer
, scope
, kind
);
906 else if(strcmp(lexer
->token_str
->buffer
, "type") == 0)
908 parseType(lexer
, scope
, kind
);
910 else if(strcmp(lexer
->token_str
->buffer
, "impl") == 0)
912 parseImpl(lexer
, scope
, kind
);
914 else if(strcmp(lexer
->token_str
->buffer
, "struct") == 0)
916 parseStructOrEnum(lexer
, scope
, kind
, TRUE
);
918 else if(strcmp(lexer
->token_str
->buffer
, "enum") == 0)
920 parseStructOrEnum(lexer
, scope
, kind
, FALSE
);
922 else if(strcmp(lexer
->token_str
->buffer
, "macro_rules") == 0)
924 parseMacroRules(lexer
, scope
, kind
);
928 advanceToken(lexer
, TRUE
);
929 if (lexer
->cur_token
== '!')
934 resetScope(scope
, old_scope_len
);
936 else if (lexer
->cur_token
== '{')
939 advanceToken(lexer
, TRUE
);
941 else if (lexer
->cur_token
== '}')
944 advanceToken(lexer
, TRUE
);
946 else if (lexer
->cur_token
== '\'')
948 /* Skip over the 'static lifetime, as it confuses the static parser above */
949 advanceToken(lexer
, TRUE
);
950 if (lexer
->cur_token
== TOKEN_IDENT
&& strcmp(lexer
->token_str
->buffer
, "static") == 0)
951 advanceToken(lexer
, TRUE
);
955 advanceToken(lexer
, TRUE
);
957 if (delim
&& level
<= 0)
962 static void findRustTags (void)
965 vString
* scope
= vStringNew();
968 parseBlock(&lexer
, FALSE
, K_NONE
, scope
);
969 vStringDelete(scope
);
974 extern parserDefinition
*RustParser (void)
976 static const char *const extensions
[] = { "rs", NULL
};
977 parserDefinition
*def
= parserNew ("Rust");
978 def
->kinds
= rustKinds
;
979 def
->kindCount
= KIND_COUNT (rustKinds
);
980 def
->extensions
= extensions
;
981 def
->parser
= findRustTags
;