@@ -9,8 +9,8 @@ use crate::ast::{Name, SeparatedList};
9
9
use crate :: data:: DiagnosticResult ;
10
10
use crate :: syntax:: common:: ParseResult ;
11
11
use crate :: syntax:: names:: parse_name;
12
- use crate :: syntax:: Kind :: Comma ;
13
- use crate :: syntax:: { kind_str, Kind , TokenAccess } ;
12
+ use crate :: syntax:: Kind :: { Comma , Identifier } ;
13
+ use crate :: syntax:: { kind_str, kinds_error , Kind , TokenAccess } ;
14
14
use crate :: Diagnostic ;
15
15
use vhdl_lang:: syntax:: parser:: ParsingContext ;
16
16
@@ -30,29 +30,20 @@ fn skip_extraneous_tokens(ctx: &mut ParsingContext<'_>, separator: Kind) {
30
30
}
31
31
}
32
32
33
- /// Parses a list of the form
34
- /// `element { separator element }`
35
- /// where `element` is an AST element and `separator` is a token of some `ast::Kind`.
36
- /// The returned list retains information of the whereabouts of the separator tokens.
37
- pub fn parse_list_with_separator < F , T > (
38
- ctx : & mut ParsingContext < ' _ > ,
39
- separator : Kind ,
40
- parse_fn : F ,
41
- ) -> DiagnosticResult < SeparatedList < T > >
42
- where
43
- F : Fn ( & mut ParsingContext < ' _ > ) -> ParseResult < T > ,
44
- {
45
- parse_list_with_separator_or_recover ( ctx, separator, parse_fn, None )
46
- }
47
-
48
- /// Same as `parse_list_with_separator`.
33
+ /// Parses a list that is separated by a single token, denoted by the `separator`
49
34
/// However, when supplied with a `recover_token` will skip until either the separator
50
35
/// or the recover token is found.
36
+ ///
37
+ /// * recover_token: When supplied with a `recover_token`, the `parse_fn` fails and will forward
38
+ /// the stream until this token is seen
39
+ /// * start_token: The token-kind that `parse_fn` starts with.
40
+ /// If not `None`, this is used to detect missing separators and continue parsing.
51
41
pub fn parse_list_with_separator_or_recover < F , T > (
52
42
ctx : & mut ParsingContext < ' _ > ,
53
43
separator : Kind ,
54
44
parse_fn : F ,
55
45
recover_token : Option < Kind > ,
46
+ start_token : Option < Kind > ,
56
47
) -> DiagnosticResult < SeparatedList < T > >
57
48
where
58
49
F : Fn ( & mut ParsingContext < ' _ > ) -> ParseResult < T > ,
75
66
if let Some ( separator_tok) = ctx. stream . pop_if_kind ( separator) {
76
67
skip_extraneous_tokens ( ctx, separator) ;
77
68
tokens. push ( separator_tok) ;
69
+ } else if let Some ( kind) = start_token {
70
+ if ctx. stream . next_kind_is ( kind) {
71
+ ctx. diagnostics . push ( kinds_error (
72
+ ctx. stream . pos_before ( ctx. stream . peek ( ) . unwrap ( ) ) ,
73
+ & [ separator] ,
74
+ ) ) ;
75
+ } else {
76
+ break ;
77
+ }
78
78
} else {
79
79
break ;
80
80
}
@@ -83,17 +83,17 @@ where
83
83
}
84
84
85
85
pub fn parse_name_list ( ctx : & mut ParsingContext < ' _ > ) -> DiagnosticResult < Vec < WithTokenSpan < Name > > > {
86
- Ok ( parse_list_with_separator ( ctx, Comma , parse_name) ?. items )
86
+ Ok ( parse_list_with_separator_or_recover ( ctx, Comma , parse_name, None , Some ( Identifier ) ) ?. items )
87
87
}
88
88
89
89
#[ cfg( test) ]
90
90
mod test {
91
91
use crate :: ast:: SeparatedList ;
92
- use crate :: syntax:: names:: parse_association_element;
92
+ use crate :: syntax:: names:: { parse_association_element, parse_name } ;
93
93
use crate :: syntax:: separated_list:: { parse_list_with_separator_or_recover, parse_name_list} ;
94
94
use crate :: syntax:: test:: Code ;
95
95
use crate :: syntax:: Kind ;
96
- use crate :: syntax:: Kind :: RightPar ;
96
+ use crate :: syntax:: Kind :: { Identifier , RightPar } ;
97
97
use crate :: Diagnostic ;
98
98
99
99
#[ test]
@@ -176,6 +176,7 @@ mod test {
176
176
Kind :: Comma ,
177
177
parse_association_element,
178
178
Some ( RightPar ) ,
179
+ Some ( Identifier ) ,
179
180
) ;
180
181
ctx. stream . skip ( ) ;
181
182
res
@@ -214,4 +215,47 @@ mod test {
214
215
)
215
216
) ;
216
217
}
218
+
219
+ #[ test]
220
+ fn parse_list_with_missing_separator ( ) {
221
+ let code = Code :: new ( "a ,b, c d, e)" ) ;
222
+ let ( res, diag) = code. with_stream_diagnostics ( |ctx| {
223
+ let res = parse_list_with_separator_or_recover (
224
+ ctx,
225
+ Kind :: Comma ,
226
+ parse_name,
227
+ Some ( RightPar ) ,
228
+ Some ( Identifier ) ,
229
+ ) ;
230
+ ctx. stream . skip ( ) ;
231
+ res
232
+ } ) ;
233
+ assert_eq ! (
234
+ res,
235
+ SeparatedList {
236
+ items: vec![
237
+ code. s1( "a" ) . name( ) ,
238
+ code. s1( "b" ) . name( ) ,
239
+ code. s1( "c" ) . name( ) ,
240
+ code. s1( "d" ) . name( ) ,
241
+ code. s1( "e" ) . name( )
242
+ ] ,
243
+ tokens: vec![
244
+ code. s( "," , 1 ) . token( ) ,
245
+ code. s( "," , 2 ) . token( ) ,
246
+ code. s( "," , 3 ) . token( )
247
+ ] ,
248
+ }
249
+ ) ;
250
+
251
+ let mut c_pos = code. s1 ( "c" ) . pos ( ) . end_pos ( ) ;
252
+ // single char position right after the 'c' character
253
+ c_pos. range . start . character += 1 ;
254
+ c_pos. range . end . character += 2 ;
255
+
256
+ assert_eq ! (
257
+ diag,
258
+ vec![ Diagnostic :: syntax_error( c_pos, "Expected ','" ) ]
259
+ ) ;
260
+ }
217
261
}
0 commit comments