@@ -9,8 +9,8 @@ use crate::ast::{Name, SeparatedList};
99use crate :: data:: DiagnosticResult ;
1010use crate :: syntax:: common:: ParseResult ;
1111use 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 } ;
1414use crate :: Diagnostic ;
1515use vhdl_lang:: syntax:: parser:: ParsingContext ;
1616
@@ -30,29 +30,20 @@ fn skip_extraneous_tokens(ctx: &mut ParsingContext<'_>, separator: Kind) {
3030 }
3131}
3232
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`
4934/// However, when supplied with a `recover_token` will skip until either the separator
5035/// 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.
5141pub fn parse_list_with_separator_or_recover < F , T > (
5242 ctx : & mut ParsingContext < ' _ > ,
5343 separator : Kind ,
5444 parse_fn : F ,
5545 recover_token : Option < Kind > ,
46+ start_token : Option < Kind > ,
5647) -> DiagnosticResult < SeparatedList < T > >
5748where
5849 F : Fn ( & mut ParsingContext < ' _ > ) -> ParseResult < T > ,
7566 if let Some ( separator_tok) = ctx. stream . pop_if_kind ( separator) {
7667 skip_extraneous_tokens ( ctx, separator) ;
7768 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+ }
7878 } else {
7979 break ;
8080 }
@@ -83,17 +83,17 @@ where
8383}
8484
8585pub 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 )
8787}
8888
8989#[ cfg( test) ]
9090mod test {
9191 use crate :: ast:: SeparatedList ;
92- use crate :: syntax:: names:: parse_association_element;
92+ use crate :: syntax:: names:: { parse_association_element, parse_name } ;
9393 use crate :: syntax:: separated_list:: { parse_list_with_separator_or_recover, parse_name_list} ;
9494 use crate :: syntax:: test:: Code ;
9595 use crate :: syntax:: Kind ;
96- use crate :: syntax:: Kind :: RightPar ;
96+ use crate :: syntax:: Kind :: { Identifier , RightPar } ;
9797 use crate :: Diagnostic ;
9898
9999 #[ test]
@@ -176,6 +176,7 @@ mod test {
176176 Kind :: Comma ,
177177 parse_association_element,
178178 Some ( RightPar ) ,
179+ Some ( Identifier ) ,
179180 ) ;
180181 ctx. stream . skip ( ) ;
181182 res
@@ -214,4 +215,47 @@ mod test {
214215 )
215216 ) ;
216217 }
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+ }
217261}
0 commit comments