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 */
27 #define MAX_STRING_LENGTH 256
49 static kindDefinition 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, 'P', "method", "A method"},
90 static void parseBlock (lexerState
*lexer
, bool 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 vStringTruncate (scope
, old_len
);
102 /* Adds a name to the end of the scope string */
103 static void addToScope (vString
*scope
, vString
*name
)
105 if (vStringLength(scope
) > 0)
106 vStringCatS(scope
, "::");
107 vStringCat(scope
, name
);
110 /* Write the lexer's current token to string, taking care of special tokens */
111 static void writeCurTokenToStr (lexerState
*lexer
, vString
*out_str
)
113 switch (lexer
->cur_token
)
116 vStringCat(out_str
, lexer
->token_str
);
119 vStringCat(out_str
, lexer
->token_str
);
121 case TOKEN_WHITESPACE
:
122 vStringPut(out_str
, ' ');
125 vStringCatS(out_str
, "<<");
128 vStringCatS(out_str
, ">>");
131 vStringCatS(out_str
, "->");
134 vStringPut(out_str
, (char) lexer
->cur_token
);
138 /* Reads a character from the file */
139 static void advanceChar (lexerState
*lexer
)
141 lexer
->cur_c
= lexer
->next_c
;
142 lexer
->next_c
= getcFromInputFile();
145 /* Reads N characters from the file */
146 static void advanceNChar (lexerState
*lexer
, int n
)
152 /* Store the current character in lexerState::token_str if there is space
153 * (set by MAX_STRING_LENGTH), and then read the next character from the file */
154 static void advanceAndStoreChar (lexerState
*lexer
)
156 if (vStringLength(lexer
->token_str
) < MAX_STRING_LENGTH
)
157 vStringPut(lexer
->token_str
, (char) lexer
->cur_c
);
161 static bool isWhitespace (int c
)
163 return c
== ' ' || c
== '\t' || c
== '\r' || c
== '\n';
166 static bool isAscii (int c
)
168 return (c
>= 0) && (c
< 0x80);
171 /* This isn't quite right for Unicode identifiers */
172 static bool isIdentifierStart (int c
)
174 return (isAscii(c
) && (isalpha(c
) || c
== '_')) || !isAscii(c
);
177 /* This isn't quite right for Unicode identifiers */
178 static bool isIdentifierContinue (int c
)
180 return (isAscii(c
) && (isalnum(c
) || c
== '_')) || !isAscii(c
);
183 static void scanWhitespace (lexerState
*lexer
)
185 while (isWhitespace(lexer
->cur_c
))
189 /* Normal line comments start with two /'s and continue until the next \n
190 * (potentially after a \r). Additionally, a shebang in the beginning of the
191 * file also counts as a line comment as long as it is not this sequence: #![ .
192 * Block comments start with / followed by a * and end with a * followed by a /.
193 * Unlike in C/C++ they nest. */
194 static void scanComments (lexerState
*lexer
)
197 if (lexer
->next_c
== '/')
199 advanceNChar(lexer
, 2);
200 while (lexer
->cur_c
!= EOF
&& lexer
->cur_c
!= '\n')
204 else if (lexer
->next_c
== '!')
206 advanceNChar(lexer
, 2);
207 /* If it is exactly #![ then it is not a comment, but an attribute */
208 if (lexer
->cur_c
== '[')
210 while (lexer
->cur_c
!= EOF
&& lexer
->cur_c
!= '\n')
214 else if (lexer
->next_c
== '*')
217 advanceNChar(lexer
, 2);
218 while (lexer
->cur_c
!= EOF
&& level
> 0)
220 if (lexer
->cur_c
== '*' && lexer
->next_c
== '/')
223 advanceNChar(lexer
, 2);
225 else if (lexer
->cur_c
== '/' && lexer
->next_c
== '*')
228 advanceNChar(lexer
, 2);
238 static void scanIdentifier (lexerState
*lexer
)
240 vStringClear(lexer
->token_str
);
243 advanceAndStoreChar(lexer
);
244 } while(lexer
->cur_c
!= EOF
&& isIdentifierContinue(lexer
->cur_c
));
247 /* Double-quoted strings, we only care about the \" escape. These
248 * last past the end of the line, so be careful not too store too much
249 * of them (see MAX_STRING_LENGTH). The only place we look at their
250 * contents is in the function definitions, and there the valid strings are
251 * things like "C" and "Rust" */
252 static void scanString (lexerState
*lexer
)
254 vStringClear(lexer
->token_str
);
255 advanceAndStoreChar(lexer
);
256 while (lexer
->cur_c
!= EOF
&& lexer
->cur_c
!= '"')
258 if (lexer
->cur_c
== '\\' && lexer
->next_c
== '"')
259 advanceAndStoreChar(lexer
);
260 advanceAndStoreChar(lexer
);
262 advanceAndStoreChar(lexer
);
265 /* Raw strings look like this: r"" or r##""## where the number of
266 * hashes must match */
267 static void scanRawString (lexerState
*lexer
)
269 size_t num_initial_hashes
= 0;
270 vStringClear(lexer
->token_str
);
271 advanceAndStoreChar(lexer
);
272 /* Count how many leading hashes there are */
273 while (lexer
->cur_c
== '#')
275 num_initial_hashes
++;
276 advanceAndStoreChar(lexer
);
278 if (lexer
->cur_c
!= '"')
280 advanceAndStoreChar(lexer
);
281 while (lexer
->cur_c
!= EOF
)
283 /* Count how many trailing hashes there are. If the number is equal or more
284 * than the number of leading hashes, break. */
285 if (lexer
->cur_c
== '"')
287 size_t num_trailing_hashes
= 0;
288 advanceAndStoreChar(lexer
);
289 while (lexer
->cur_c
== '#' && num_trailing_hashes
< num_initial_hashes
)
291 num_trailing_hashes
++;
293 advanceAndStoreChar(lexer
);
295 if (num_trailing_hashes
== num_initial_hashes
)
300 advanceAndStoreChar(lexer
);
305 /* This deals with character literals: 'n', '\n', '\uFFFF'; and lifetimes:
306 * 'lifetime. We'll use this approximate regexp for the literals:
307 * \' \\ [^']+ \' or \' [^'] \' or \' \\ \' \'. Either way, we'll treat this
308 * token as a string, so it gets preserved as is for function signatures with
310 static void scanCharacterOrLifetime (lexerState
*lexer
)
312 vStringClear(lexer
->token_str
);
313 advanceAndStoreChar(lexer
);
315 if (lexer
->cur_c
== '\\')
317 advanceAndStoreChar(lexer
);
318 /* The \' \\ \' \' (literally '\'') case */
319 if (lexer
->cur_c
== '\'' && lexer
->next_c
== '\'')
321 advanceAndStoreChar(lexer
);
322 advanceAndStoreChar(lexer
);
324 /* The \' \\ [^']+ \' case */
327 while (lexer
->cur_c
!= EOF
&& lexer
->cur_c
!= '\'')
328 advanceAndStoreChar(lexer
);
331 /* The \' [^'] \' case */
332 else if (lexer
->cur_c
!= '\'' && lexer
->next_c
== '\'')
334 advanceAndStoreChar(lexer
);
335 advanceAndStoreChar(lexer
);
337 /* Otherwise it is malformed, or a lifetime */
340 /* Advances the parser one token, optionally skipping whitespace
341 * (otherwise it is concatenated and returned as a single whitespace token).
342 * Whitespace is needed to properly render function signatures. Unrecognized
343 * token starts are stored literally, e.g. token may equal to a character '#'. */
344 static int advanceToken (lexerState
*lexer
, bool skip_whitspace
)
346 bool have_whitespace
= false;
347 lexer
->line
= getInputLineNumber();
348 lexer
->pos
= getInputFilePosition();
349 while (lexer
->cur_c
!= EOF
)
351 if (isWhitespace(lexer
->cur_c
))
353 scanWhitespace(lexer
);
354 have_whitespace
= true;
356 else if (lexer
->cur_c
== '/' && (lexer
->next_c
== '/' || lexer
->next_c
== '*'))
359 have_whitespace
= true;
363 if (have_whitespace
&& !skip_whitspace
)
364 return lexer
->cur_token
= TOKEN_WHITESPACE
;
368 lexer
->line
= getInputLineNumber();
369 lexer
->pos
= getInputFilePosition();
370 while (lexer
->cur_c
!= EOF
)
372 if (lexer
->cur_c
== '"')
375 return lexer
->cur_token
= TOKEN_STRING
;
377 else if (lexer
->cur_c
== 'r' && (lexer
->next_c
== '#' || lexer
->next_c
== '"'))
379 scanRawString(lexer
);
380 return lexer
->cur_token
= TOKEN_STRING
;
382 else if (lexer
->cur_c
== '\'')
384 scanCharacterOrLifetime(lexer
);
385 return lexer
->cur_token
= TOKEN_STRING
;
387 else if (isIdentifierStart(lexer
->cur_c
))
389 scanIdentifier(lexer
);
390 return lexer
->cur_token
= TOKEN_IDENT
;
392 /* These shift tokens aren't too important for tag-generation per se,
393 * but they confuse the skipUntil code which tracks the <> pairs. */
394 else if (lexer
->cur_c
== '>' && lexer
->next_c
== '>')
396 advanceNChar(lexer
, 2);
397 return lexer
->cur_token
= TOKEN_RSHIFT
;
399 else if (lexer
->cur_c
== '<' && lexer
->next_c
== '<')
401 advanceNChar(lexer
, 2);
402 return lexer
->cur_token
= TOKEN_LSHIFT
;
404 else if (lexer
->cur_c
== '-' && lexer
->next_c
== '>')
406 advanceNChar(lexer
, 2);
407 return lexer
->cur_token
= TOKEN_RARROW
;
411 int c
= lexer
->cur_c
;
413 return lexer
->cur_token
= c
;
416 return lexer
->cur_token
= TOKEN_EOF
;
419 static void initLexer (lexerState
*lexer
)
421 advanceNChar(lexer
, 2);
422 lexer
->token_str
= vStringNew();
424 if (lexer
->cur_c
== '#' && lexer
->next_c
== '!')
426 advanceToken(lexer
, true);
429 static void deInitLexer (lexerState
*lexer
)
431 vStringDelete(lexer
->token_str
);
432 lexer
->token_str
= NULL
;
435 static void addTag (vString
* ident
, const char* arg_list
, int kind
, unsigned long line
, MIOPos pos
, vString
*scope
, int parent_kind
)
437 if (kind
== K_NONE
|| ! rustKinds
[kind
].enabled
)
440 initTagEntry(&tag
, vStringValue(ident
), kind
);
442 tag
.lineNumber
= line
;
443 tag
.filePosition
= pos
;
445 tag
.extensionFields
.signature
= arg_list
;
446 /*tag.extensionFields.varType = type;*/ /* FIXME: map to typeRef[1]? */
447 if (parent_kind
!= K_NONE
)
449 tag
.extensionFields
.scopeKindIndex
= parent_kind
;
450 tag
.extensionFields
.scopeName
= vStringValue(scope
);
455 /* Skip tokens until one of the goal tokens is hit. Escapes when level = 0 if there are no goal tokens.
456 * Keeps track of balanced <>'s, ()'s, []'s, and {}'s and ignores the goal tokens within those pairings */
457 static void skipUntil (lexerState
*lexer
, int goal_tokens
[], int num_goal_tokens
)
462 int bracket_level
= 0;
463 while (lexer
->cur_token
!= TOKEN_EOF
)
465 if (angle_level
== 0 && paren_level
== 0 && brace_level
== 0
466 && bracket_level
== 0)
469 for(ii
= 0; ii
< num_goal_tokens
; ii
++)
471 if (lexer
->cur_token
== goal_tokens
[ii
])
476 if (ii
< num_goal_tokens
)
479 switch (lexer
->cur_token
)
506 if (angle_level
>= 2)
509 /* TOKEN_LSHIFT is never interpreted as two <'s in valid Rust code */
513 /* Has to be after the token switch to catch the case when we start with the initial level token */
514 if (num_goal_tokens
== 0 && angle_level
== 0 && paren_level
== 0 && brace_level
== 0
515 && bracket_level
== 0)
517 advanceToken(lexer
, true);
522 * "fn" <ident>[<type_bounds>] "(" [<args>] ")" ["->" <ret_type>] "{" [<body>] "}"*/
523 static void parseFn (lexerState
*lexer
, vString
*scope
, int parent_kind
)
525 int kind
= (parent_kind
== K_TRAIT
|| parent_kind
== K_IMPL
) ? K_METHOD
: K_FN
;
531 int bracket_level
= 0;
532 bool found_paren
= false;
533 bool valid_signature
= true;
535 advanceToken(lexer
, true);
536 if (lexer
->cur_token
!= TOKEN_IDENT
)
539 name
= vStringNewCopy(lexer
->token_str
);
540 arg_list
= vStringNew();
545 advanceToken(lexer
, true);
547 /* HACK: This is a bit coarse as far as what tag entry means by
549 while (lexer
->cur_token
!= '{')
551 if (lexer
->cur_token
== ';' && bracket_level
== 0)
555 else 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
== '[')
578 else if (lexer
->cur_token
== ']')
582 else if (lexer
->cur_token
== TOKEN_EOF
)
584 valid_signature
= false;
587 writeCurTokenToStr(lexer
, arg_list
);
588 advanceToken(lexer
, false);
590 if (!found_paren
|| paren_level
!= 0 || bracket_level
!= 0)
591 valid_signature
= false;
595 vStringStripTrailing(arg_list
);
596 addTag(name
, vStringValue(arg_list
), kind
, line
, pos
, scope
, parent_kind
);
597 addToScope(scope
, name
);
598 parseBlock(lexer
, true, kind
, scope
);
602 vStringDelete(arg_list
);
606 * "mod" <ident> "{" [<body>] "}"
607 * "mod" <ident> ";"*/
608 static void parseMod (lexerState
*lexer
, vString
*scope
, int parent_kind
)
610 advanceToken(lexer
, true);
611 if (lexer
->cur_token
!= TOKEN_IDENT
)
614 addTag(lexer
->token_str
, NULL
, K_MOD
, lexer
->line
, lexer
->pos
, scope
, parent_kind
);
615 addToScope(scope
, lexer
->token_str
);
617 advanceToken(lexer
, true);
619 parseBlock(lexer
, true, K_MOD
, scope
);
623 * "trait" <ident> [<type_bounds>] "{" [<body>] "}"
625 static void parseTrait (lexerState
*lexer
, vString
*scope
, int parent_kind
)
627 int goal_tokens
[] = {'{'};
629 advanceToken(lexer
, true);
630 if (lexer
->cur_token
!= TOKEN_IDENT
)
633 addTag(lexer
->token_str
, NULL
, K_TRAIT
, lexer
->line
, lexer
->pos
, scope
, parent_kind
);
634 addToScope(scope
, lexer
->token_str
);
636 advanceToken(lexer
, true);
638 skipUntil(lexer
, goal_tokens
, 1);
640 parseBlock(lexer
, true, K_TRAIT
, scope
);
643 /* Skips type blocks of the form <T:T<T>, ...> */
644 static void skipTypeBlock (lexerState
*lexer
)
646 if (lexer
->cur_token
== '<')
648 skipUntil(lexer
, NULL
, 0);
649 advanceToken(lexer
, true);
653 /* Essentially grabs the last ident before 'for', '<' and '{', which
654 * tends to correspond to what we want as the impl tag entry name */
655 static void parseQualifiedType (lexerState
*lexer
, vString
* name
)
657 while (lexer
->cur_token
!= TOKEN_EOF
)
659 if (lexer
->cur_token
== TOKEN_IDENT
)
661 if (strcmp(vStringValue(lexer
->token_str
), "for") == 0
662 || strcmp(vStringValue(lexer
->token_str
), "where") == 0)
665 vStringCat(name
, lexer
->token_str
);
667 else if (lexer
->cur_token
== '<' || lexer
->cur_token
== '{')
671 advanceToken(lexer
, true);
673 skipTypeBlock(lexer
);
677 * "impl" [<type_bounds>] <qualified_ident>[<type_bounds>] ["for" <qualified_ident>[<type_bounds>]] "{" [<body>] "}"
679 static void parseImpl (lexerState
*lexer
, vString
*scope
, int parent_kind
)
685 advanceToken(lexer
, true);
690 skipTypeBlock(lexer
);
694 parseQualifiedType(lexer
, name
);
696 if (lexer
->cur_token
== TOKEN_IDENT
&& strcmp(vStringValue(lexer
->token_str
), "for") == 0)
698 advanceToken(lexer
, true);
699 parseQualifiedType(lexer
, name
);
702 addTag(name
, NULL
, K_IMPL
, line
, pos
, scope
, parent_kind
);
703 addToScope(scope
, name
);
705 parseBlock(lexer
, true, K_IMPL
, scope
);
711 * "static" ["mut"] <ident>
713 static void parseStatic (lexerState
*lexer
, vString
*scope
, int parent_kind
)
715 advanceToken(lexer
, true);
716 if (lexer
->cur_token
!= TOKEN_IDENT
)
718 if (strcmp(vStringValue(lexer
->token_str
), "mut") == 0)
720 advanceToken(lexer
, true);
722 if (lexer
->cur_token
!= TOKEN_IDENT
)
725 addTag(lexer
->token_str
, NULL
, K_STATIC
, lexer
->line
, lexer
->pos
, scope
, parent_kind
);
731 static void parseType (lexerState
*lexer
, vString
*scope
, int parent_kind
)
733 advanceToken(lexer
, true);
734 if (lexer
->cur_token
!= TOKEN_IDENT
)
737 addTag(lexer
->token_str
, NULL
, K_TYPE
, lexer
->line
, lexer
->pos
, scope
, parent_kind
);
740 /* Structs and enums are very similar syntax-wise.
741 * It is possible to parse variants a bit more cleverly (e.g. make tuple variants functions and
742 * struct variants structs) but it'd be too clever and the signature wouldn't make too much sense without
743 * the enum's definition (e.g. for the type bounds)
745 * Struct/Enum format:
746 * "struct/enum" <ident>[<type_bounds>] "{" [<ident>,]+ "}"
747 * "struct/enum" <ident>[<type_bounds>] ";"
749 static void parseStructOrEnum (lexerState
*lexer
, vString
*scope
, int parent_kind
, bool is_struct
)
751 int kind
= is_struct
? K_STRUCT
: K_ENUM
;
752 int field_kind
= is_struct
? K_FIELD
: K_VARIANT
;
753 int goal_tokens1
[] = {';', '{'};
755 advanceToken(lexer
, true);
756 if (lexer
->cur_token
!= TOKEN_IDENT
)
759 addTag(lexer
->token_str
, NULL
, kind
, lexer
->line
, lexer
->pos
, scope
, parent_kind
);
760 addToScope(scope
, lexer
->token_str
);
762 skipUntil(lexer
, goal_tokens1
, 2);
764 if (lexer
->cur_token
== '{')
766 vString
*field_name
= vStringNew();
767 while (lexer
->cur_token
!= TOKEN_EOF
)
769 int goal_tokens2
[] = {'}', ','};
770 /* Skip attributes. Format:
773 if (lexer
->cur_token
== '#')
775 advanceToken(lexer
, true);
776 if (lexer
->cur_token
== '!')
777 advanceToken(lexer
, true);
778 if (lexer
->cur_token
== '[')
780 /* It's an attribute, skip it. */
781 skipUntil(lexer
, NULL
, 0);
785 /* Something's up with this field, skip to the next one */
786 skipUntil(lexer
, goal_tokens2
, 2);
790 if (lexer
->cur_token
== TOKEN_IDENT
)
792 if (strcmp(vStringValue(lexer
->token_str
), "priv") == 0
793 || strcmp(vStringValue(lexer
->token_str
), "pub") == 0)
795 advanceToken(lexer
, true);
797 /* Skip thevisibility specificaions.
798 * https://doc.rust-lang.org/reference/visibility-and-privacy.html */
799 if (lexer
->cur_token
== '(')
801 advanceToken(lexer
, true);
802 skipUntil (lexer
, (int []){')'}, 1);
803 advanceToken(lexer
, true);
806 if (lexer
->cur_token
!= TOKEN_IDENT
)
808 /* Something's up with this field, skip to the next one */
809 skipUntil(lexer
, goal_tokens2
, 2);
814 vStringClear(field_name
);
815 vStringCat(field_name
, lexer
->token_str
);
816 addTag(field_name
, NULL
, field_kind
, lexer
->line
, lexer
->pos
, scope
, kind
);
817 skipUntil(lexer
, goal_tokens2
, 2);
819 if (lexer
->cur_token
== '}')
821 advanceToken(lexer
, true);
824 advanceToken(lexer
, true);
826 vStringDelete(field_name
);
830 /* Skip the body of the macro. Can't use skipUntil here as
831 * the body of the macro may have arbitrary code which confuses it (e.g.
832 * bitshift operators/function return arrows) */
833 static void skipMacro (lexerState
*lexer
)
839 advanceToken(lexer
, true);
840 switch (lexer
->cur_token
)
858 while (lexer
->cur_token
!= TOKEN_EOF
)
860 if (lexer
->cur_token
== plus_token
)
862 else if (lexer
->cur_token
== minus_token
)
866 advanceToken(lexer
, true);
868 advanceToken(lexer
, true);
872 * Macro rules format:
873 * "macro_rules" "!" <ident> <macro_body>
875 static void parseMacroRules (lexerState
*lexer
, vString
*scope
, int parent_kind
)
877 advanceToken(lexer
, true);
879 if (lexer
->cur_token
!= '!')
882 advanceToken(lexer
, true);
884 if (lexer
->cur_token
!= TOKEN_IDENT
)
887 addTag(lexer
->token_str
, NULL
, K_MACRO
, lexer
->line
, lexer
->pos
, scope
, parent_kind
);
893 * Rust is very liberal with nesting, so this function is used pretty much for any block
895 static void parseBlock (lexerState
*lexer
, bool delim
, int kind
, vString
*scope
)
900 if (lexer
->cur_token
!= '{')
902 advanceToken(lexer
, true);
904 while (lexer
->cur_token
!= TOKEN_EOF
)
906 if (lexer
->cur_token
== TOKEN_IDENT
)
908 size_t old_scope_len
= vStringLength(scope
);
909 if (strcmp(vStringValue(lexer
->token_str
), "fn") == 0)
911 parseFn(lexer
, scope
, kind
);
913 else if(strcmp(vStringValue(lexer
->token_str
), "mod") == 0)
915 parseMod(lexer
, scope
, kind
);
917 else if(strcmp(vStringValue(lexer
->token_str
), "static") == 0)
919 parseStatic(lexer
, scope
, kind
);
921 else if(strcmp(vStringValue(lexer
->token_str
), "trait") == 0)
923 parseTrait(lexer
, scope
, kind
);
925 else if(strcmp(vStringValue(lexer
->token_str
), "type") == 0)
927 parseType(lexer
, scope
, kind
);
929 else if(strcmp(vStringValue(lexer
->token_str
), "impl") == 0)
931 parseImpl(lexer
, scope
, kind
);
933 else if(strcmp(vStringValue(lexer
->token_str
), "struct") == 0)
935 parseStructOrEnum(lexer
, scope
, kind
, true);
937 else if(strcmp(vStringValue(lexer
->token_str
), "enum") == 0)
939 parseStructOrEnum(lexer
, scope
, kind
, false);
941 else if(strcmp(vStringValue(lexer
->token_str
), "macro_rules") == 0)
943 parseMacroRules(lexer
, scope
, kind
);
947 advanceToken(lexer
, true);
948 if (lexer
->cur_token
== '!')
953 resetScope(scope
, old_scope_len
);
955 else if (lexer
->cur_token
== '{')
958 advanceToken(lexer
, true);
960 else if (lexer
->cur_token
== '}')
963 advanceToken(lexer
, true);
965 else if (lexer
->cur_token
== '\'')
967 /* Skip over the 'static lifetime, as it confuses the static parser above */
968 advanceToken(lexer
, true);
969 if (lexer
->cur_token
== TOKEN_IDENT
&& strcmp(vStringValue(lexer
->token_str
), "static") == 0)
970 advanceToken(lexer
, true);
974 advanceToken(lexer
, true);
976 if (delim
&& level
<= 0)
981 static void findRustTags (void)
983 lexerState lexer
= {0};
984 vString
* scope
= vStringNew();
987 parseBlock(&lexer
, false, K_NONE
, scope
);
988 vStringDelete(scope
);
993 extern parserDefinition
*RustParser (void)
995 static const char *const extensions
[] = { "rs", NULL
};
996 parserDefinition
*def
= parserNew ("Rust");
997 def
->kindTable
= rustKinds
;
998 def
->kindCount
= ARRAY_SIZE (rustKinds
);
999 def
->extensions
= extensions
;
1000 def
->parser
= findRustTags
;