@@ -5,8 +5,10 @@ use super::pr::{Annotation, Stmt, StmtKind};
55use crate :: lexer:: lr:: TokenKind ;
66use crate :: span:: Span ;
77
8- pub fn ident_part ( ) -> impl Parser < TokenKind , String , Error = PError > + Clone {
9- return select ! {
8+ use super :: SupportsDocComment ;
9+
10+ pub ( crate ) fn ident_part ( ) -> impl Parser < TokenKind , String , Error = PError > + Clone {
11+ select ! {
1012 TokenKind :: Ident ( ident) => ident,
1113 TokenKind :: Keyword ( ident) if & ident == "module" => ident,
1214 }
@@ -16,18 +18,27 @@ pub fn ident_part() -> impl Parser<TokenKind, String, Error = PError> + Clone {
1618 [ Some ( TokenKind :: Ident ( "" . to_string ( ) ) ) ] ,
1719 e. found ( ) . cloned ( ) ,
1820 )
19- } ) ;
21+ } )
2022}
2123
22- pub fn keyword ( kw : & ' static str ) -> impl Parser < TokenKind , ( ) , Error = PError > + Clone {
24+ pub ( crate ) fn keyword ( kw : & ' static str ) -> impl Parser < TokenKind , ( ) , Error = PError > + Clone {
2325 just ( TokenKind :: Keyword ( kw. to_string ( ) ) ) . ignored ( )
2426}
2527
28+ /// Our approach to new lines is each item consumes new lines _before_ itself,
29+ /// but not newlines after itself. This allows us to enforce new lines between
30+ /// some items. The only place we handle new lines after an item is in the root
31+ /// parser.
2632pub fn new_line ( ) -> impl Parser < TokenKind , ( ) , Error = PError > + Clone {
27- just ( TokenKind :: NewLine ) . ignored ( )
33+ just ( TokenKind :: NewLine )
34+ // Start is considered a new line, so we can enforce things start on a new
35+ // line while allowing them to be at the beginning of a file
36+ . or ( just ( TokenKind :: Start ) )
37+ . ignored ( )
38+ . labelled ( "new line" )
2839}
2940
30- pub fn ctrl ( char : char ) -> impl Parser < TokenKind , ( ) , Error = PError > + Clone {
41+ pub ( crate ) fn ctrl ( char : char ) -> impl Parser < TokenKind , ( ) , Error = PError > + Clone {
3142 just ( TokenKind :: Control ( char) ) . ignored ( )
3243}
3344
@@ -36,5 +47,77 @@ pub fn into_stmt((annotations, kind): (Vec<Annotation>, StmtKind), span: Span) -
3647 kind,
3748 span : Some ( span) ,
3849 annotations,
50+ doc_comment : None ,
51+ }
52+ }
53+
54+ pub ( crate ) fn doc_comment ( ) -> impl Parser < TokenKind , String , Error = PError > + Clone {
55+ // doc comments must start on a new line, so we enforce a new line (which
56+ // can also be a file start) before the doc comment
57+ //
58+ // TODO: we currently lose any empty newlines between doc comments;
59+ // eventually we want to retain them
60+ ( new_line ( ) . repeated ( ) . at_least ( 1 ) . ignore_then ( select ! {
61+ TokenKind :: DocComment ( dc) => dc,
62+ } ) )
63+ . repeated ( )
64+ . at_least ( 1 )
65+ . collect ( )
66+ . map ( |lines : Vec < String > | lines. join ( "\n " ) )
67+ . labelled ( "doc comment" )
68+ }
69+
70+ pub ( crate ) fn with_doc_comment < ' a , P , O > (
71+ parser : P ,
72+ ) -> impl Parser < TokenKind , O , Error = PError > + Clone + ' a
73+ where
74+ P : Parser < TokenKind , O , Error = PError > + Clone + ' a ,
75+ O : SupportsDocComment + ' a ,
76+ {
77+ doc_comment ( )
78+ . or_not ( )
79+ . then ( parser)
80+ . map ( |( doc_comment, inner) | inner. with_doc_comment ( doc_comment) )
81+ }
82+
83+ #[ cfg( test) ]
84+ mod tests {
85+ use insta:: assert_debug_snapshot;
86+
87+ use super :: * ;
88+ use crate :: test:: parse_with_parser;
89+
90+ #[ test]
91+ fn test_doc_comment ( ) {
92+ assert_debug_snapshot ! ( parse_with_parser( r#"
93+ #! doc comment
94+ #! another line
95+
96+ "# , doc_comment( ) ) , @r###"
97+ Ok(
98+ " doc comment\n another line",
99+ )
100+ "### ) ;
101+ }
102+
103+ #[ test]
104+ fn test_doc_comment_or_not ( ) {
105+ assert_debug_snapshot ! ( parse_with_parser( r#"hello"# , doc_comment( ) . or_not( ) ) . unwrap( ) , @"None" ) ;
106+ assert_debug_snapshot ! ( parse_with_parser( r#"hello"# , doc_comment( ) . or_not( ) . then_ignore( new_line( ) . repeated( ) ) . then( ident_part( ) ) ) . unwrap( ) , @r###"
107+ (
108+ None,
109+ "hello",
110+ )
111+ "### ) ;
112+ }
113+
114+ #[ test]
115+ fn test_no_doc_comment_in_with_doc_comment ( ) {
116+ impl SupportsDocComment for String {
117+ fn with_doc_comment ( self , _doc_comment : Option < String > ) -> Self {
118+ self
119+ }
120+ }
121+ assert_debug_snapshot ! ( parse_with_parser( r#"hello"# , with_doc_comment( new_line( ) . ignore_then( ident_part( ) ) ) ) . unwrap( ) , @r###""hello""### ) ;
39122 }
40123}
0 commit comments