@@ -5,8 +5,10 @@ use super::pr::{Annotation, Stmt, StmtKind};
5
5
use crate :: lexer:: lr:: TokenKind ;
6
6
use crate :: span:: Span ;
7
7
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 ! {
10
12
TokenKind :: Ident ( ident) => ident,
11
13
TokenKind :: Keyword ( ident) if & ident == "module" => ident,
12
14
}
@@ -16,18 +18,27 @@ pub fn ident_part() -> impl Parser<TokenKind, String, Error = PError> + Clone {
16
18
[ Some ( TokenKind :: Ident ( "" . to_string ( ) ) ) ] ,
17
19
e. found ( ) . cloned ( ) ,
18
20
)
19
- } ) ;
21
+ } )
20
22
}
21
23
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 {
23
25
just ( TokenKind :: Keyword ( kw. to_string ( ) ) ) . ignored ( )
24
26
}
25
27
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.
26
32
pub 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" )
28
39
}
29
40
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 {
31
42
just ( TokenKind :: Control ( char) ) . ignored ( )
32
43
}
33
44
@@ -36,5 +47,77 @@ pub fn into_stmt((annotations, kind): (Vec<Annotation>, StmtKind), span: Span) -
36
47
kind,
37
48
span : Some ( span) ,
38
49
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""### ) ;
39
122
}
40
123
}
0 commit comments