leap_lang/parser/
commentsparser.rs

1use std::{
2    iter::{Enumerate, Peekable},
3    str::Chars,
4};
5
6use crate::{
7    leaptypes::{Comment, CommentType},
8    parser::position::Position,
9};
10
11// comments parser is separte from types parser for simplicity
12pub fn parse(data: &str) -> Vec<Comment> {
13    let mut comments = vec![];
14    let mut chars = data.chars().enumerate().peekable();
15    // let v: String = chars;
16    while let Some((i, v)) = chars.next() {
17        if v == '/' {
18            // ::Line comment
19            let (comment, length) = parse_comment(&mut chars);
20            comments.push(Comment {
21                comment,
22                comment_type: CommentType::Line,
23                position: Position::new(i, length),
24            });
25        } else if v.is_whitespace() {
26            let mut v = v;
27            while let Some((_, next_v)) = chars.peek() {
28                if v == '\n' {
29                    break;
30                };
31                if !next_v.is_whitespace() {
32                    // not an empty line
33                    break;
34                }
35                v = if let Some((_, new_v)) = chars.next() {
36                    new_v
37                } else {
38                    break;
39                };
40            }
41            if v == '\n' {
42                // found empty line
43                // ::Separator
44                comments.push(Comment {
45                    comment: String::new(),
46                    comment_type: CommentType::Separator,
47                    position: Position::new(i, 0),
48                });
49            }
50        } else if !v.is_whitespace() {
51            // ::Trail comment
52            // skip up to `/` or new line
53            for (i, v) in &mut chars {
54                match v {
55                    '/' => {
56                        let (comment, length) = parse_comment(&mut chars);
57                        comments.push(Comment {
58                            comment,
59                            comment_type: CommentType::Trail,
60                            position: Position::new(i, length),
61                        });
62                        break;
63                    }
64                    '\n' => break,
65                    _ => {}
66                };
67            }
68        }
69    }
70    comments
71}
72
73fn parse_comment(chars: &mut Peekable<Enumerate<Chars>>) -> (String, usize) {
74    let mut comment_mark_len = 1;
75    while chars.peek().map(|(_, c)| *c == '-').unwrap_or(false) {
76        chars.next();
77        comment_mark_len += 1;
78        if comment_mark_len == 3 {
79            // prefix is `/--`
80            break;
81        }
82    }
83    let comment = consume_up_to_line_end(chars);
84    (comment.trim().to_owned(), comment_mark_len + comment.len())
85}
86
87fn consume_up_to_line_end(chars: &mut impl Iterator<Item = (usize, char)>) -> String {
88    let mut s = vec![];
89    for (_, v) in chars {
90        if v == '\n' {
91            break;
92        }
93        s.push(v);
94    }
95    s.into_iter().collect()
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101
102    #[test]
103    fn test_parse() {
104        assert_eq!(parse("aaa\nbbb\nccc").len(), 0);
105        let comments = parse("    /--   aaaa  ");
106        assert_eq!(comments[0].position.start, 4);
107        assert_eq!(comments[0].position.length, 12);
108        assert_eq!(comments[0].comment, "aaaa");
109        assert_eq!(comments[0].comment_type, CommentType::Line);
110        let comments = parse("    /-   aaaa  ");
111        assert_eq!(comments[0].position.start, 4);
112        assert_eq!(comments[0].position.length, 11);
113        assert_eq!(comments[0].comment, "aaaa");
114        assert_eq!(comments[0].comment_type, CommentType::Line);
115        let comments = parse("    /   aaaa  ");
116        assert_eq!(comments[0].position.start, 4);
117        assert_eq!(comments[0].position.length, 10);
118        assert_eq!(comments[0].comment, "aaaa");
119        assert_eq!(comments[0].comment_type, CommentType::Line);
120        let comments = parse("    /---   aaaa  ");
121        assert_eq!(comments[0].position.start, 4);
122        assert_eq!(comments[0].position.length, 13);
123        assert_eq!(comments[0].comment, "-   aaaa");
124        assert_eq!(comments[0].comment_type, CommentType::Line);
125        let comments = parse("    /--   aaaa  \n   \nbbb");
126        assert_eq!(comments[0].position.start, 4);
127        assert_eq!(comments[0].position.length, 12);
128        assert_eq!(comments[0].comment, "aaaa");
129        assert_eq!(comments[0].comment_type, CommentType::Line);
130        assert_eq!(comments[1].position.start, 17);
131        assert_eq!(comments[1].position.length, 0);
132        assert_eq!(comments[1].comment_type, CommentType::Separator);
133        let comments = parse("  text /--  aaaa   ");
134        assert_eq!(comments[0].position.start, 7);
135        assert_eq!(comments[0].position.length, 12);
136        assert_eq!(comments[0].comment, "aaaa");
137        assert_eq!(comments[0].comment_type, CommentType::Trail);
138        let comments = parse(".struct some-my-struct /-- text4\n\n/text5\n\nv: int");
139        assert_eq!(comments[1].comment_type, CommentType::Separator);
140        assert_eq!(comments[0].comment_type, CommentType::Trail);
141        assert_eq!(comments[1].comment_type, CommentType::Separator);
142        let comments = parse(".struct some-my-struct\n\n\n\n\n");
143        assert_eq!(comments.len(), 4);
144        let comments = parse("\n\n\n\n\n.struct some-my-struct");
145        assert_eq!(comments.len(), 5);
146        let comments = parse(".enum my-enum\n\n\n\n\n.struct some-my-struct");
147        assert_eq!(comments.len(), 4);
148        let comments = parse("/-- text");
149        assert_eq!(comments[0].comment, "text");
150    }
151}