sv_parser/
lib.rs

1#![recursion_limit = "256"]
2
3use nom_greedyerror::error_position;
4use std::fmt;
5use std::hash::BuildHasher;
6use std::path::{Path, PathBuf};
7pub use sv_parser_error::Error;
8use sv_parser_parser::{
9    lib_parser, lib_parser_incomplete, sv_parser, sv_parser_incomplete, Span, SpanInfo,
10};
11pub use sv_parser_pp::preprocess::{
12    preprocess, preprocess_str, Define, DefineText, Defines, PreprocessedText,
13};
14pub use sv_parser_syntaxtree::*;
15
16pub struct SyntaxTree {
17    node: AnyNode,
18    text: PreprocessedText,
19}
20
21impl SyntaxTree {
22    /// Get `&str` from the specified node
23    pub fn get_str<'a, T: Into<RefNodes<'a>>>(&self, nodes: T) -> Option<&str> {
24        let mut beg = None;
25        let mut end = 0;
26        for n in Iter::new(nodes.into()) {
27            if let RefNode::Locate(x) = n {
28                if beg.is_none() {
29                    beg = Some(x.offset);
30                }
31                end = x.offset + x.len;
32            }
33        }
34        if let Some(beg) = beg {
35            let ret = unsafe { self.text.text().get_unchecked(beg..end) };
36            Some(ret)
37        } else {
38            None
39        }
40    }
41
42    /// Get `&str` without trailing `WhiteSpace` from the specified node
43    pub fn get_str_trim<'a, T: Into<RefNodes<'a>>>(&self, nodes: T) -> Option<&str> {
44        let mut beg = None;
45        let mut end = 0;
46        let mut skip = false;
47        for n in Iter::new(nodes.into()).event() {
48            match n {
49                NodeEvent::Enter(RefNode::WhiteSpace(_)) => {
50                    skip = true;
51                }
52                NodeEvent::Leave(RefNode::WhiteSpace(_)) => {
53                    skip = false;
54                }
55                NodeEvent::Enter(RefNode::Locate(x)) if !skip => {
56                    if beg.is_none() {
57                        beg = Some(x.offset);
58                    }
59                    end = x.offset + x.len;
60                }
61                _ => (),
62            }
63        }
64        if let Some(beg) = beg {
65            let ret = unsafe { self.text.text().get_unchecked(beg..end) };
66            Some(ret)
67        } else {
68            None
69        }
70    }
71
72    /// Get source code location of the specified `Locate`
73    pub fn get_origin(&self, locate: &Locate) -> Option<(&PathBuf, usize)> {
74        self.text.origin(locate.offset)
75    }
76}
77
78impl fmt::Display for SyntaxTree {
79    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80        let mut ret = String::from("");
81        let mut skip = false;
82        let mut depth = 0;
83        for node in self.into_iter().event() {
84            match node {
85                NodeEvent::Enter(RefNode::Locate(locate)) => {
86                    if !skip {
87                        ret.push_str(&format!(
88                            "{}Token: '{}' @ line:{}\n",
89                            " ".repeat(depth),
90                            self.get_str(locate).unwrap(),
91                            locate.line,
92                        ));
93                    }
94                    depth += 1;
95                }
96                NodeEvent::Enter(RefNode::WhiteSpace(_)) => {
97                    skip = true;
98                }
99                NodeEvent::Leave(RefNode::WhiteSpace(_)) => {
100                    skip = false;
101                }
102                NodeEvent::Enter(x) => {
103                    if !skip {
104                        ret.push_str(&format!("{}{}\n", " ".repeat(depth), x));
105                    }
106                    depth += 1;
107                }
108                NodeEvent::Leave(_) => {
109                    depth -= 1;
110                }
111            }
112        }
113        write!(f, "{}", ret)
114    }
115}
116
117impl fmt::Debug for SyntaxTree {
118    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119        enum WS {
120            NotWhitespace,
121            Newline,
122            Space,
123            Comment,
124            CompilerDirective,
125        }
126
127        let mut ws: WS = WS::NotWhitespace;
128        let mut depth = 0;
129        let mut ret = String::from("");
130        for node in self.into_iter().event() {
131            match node {
132                NodeEvent::Enter(RefNode::Locate(locate)) => {
133                    let (pre, t, post) = match ws {
134                        WS::Newline => ("<<<<<", "NewlineToken", ">>>>>"),
135                        WS::Space => ("<<<<<", "SpaceToken", ">>>>>"),
136                        WS::Comment => ("<<<<<", "CommentToken", ">>>>>"),
137                        _ => ("", "Token", ""),
138                    };
139                    ret.push_str(&format!(
140                        "{}{} @line:{}: {}{}{}\n",
141                        " ".repeat(depth),
142                        t,
143                        locate.line,
144                        pre,
145                        self.get_str(locate).unwrap(),
146                        post,
147                    ));
148                    depth += 1;
149                }
150                NodeEvent::Enter(x) => {
151                    match x {
152                        RefNode::WhiteSpace(WhiteSpace::Newline(_)) => { ws = WS::Newline; }
153                        RefNode::WhiteSpace(WhiteSpace::Space(_)) => { ws = WS::Space; }
154                        RefNode::WhiteSpace(WhiteSpace::Comment(_)) => { ws = WS::Comment; }
155                        RefNode::WhiteSpace(WhiteSpace::CompilerDirective(_)) => { ws = WS::CompilerDirective; }
156                        _ => {}
157                    }
158                    ret.push_str(&format!("{}{}\n", " ".repeat(depth), x));
159                    depth += 1;
160                }
161                NodeEvent::Leave(x) => {
162                    match x {
163                        RefNode::WhiteSpace(_) => {}
164                        _ => { ws = WS::NotWhitespace; }
165                    }
166                    depth -= 1;
167                }
168            }
169        }
170        write!(f, "{}", ret)
171    }
172}
173
174impl<'a> IntoIterator for &'a SyntaxTree {
175    type Item = RefNode<'a>;
176    type IntoIter = Iter<'a>;
177
178    fn into_iter(self) -> Self::IntoIter {
179        let ref_node: RefNode = (&self.node).into();
180        ref_node.into_iter()
181    }
182}
183
184pub fn parse_sv<T: AsRef<Path>, U: AsRef<Path>, V: BuildHasher>(
185    path: T,
186    pre_defines: &Defines<V>,
187    include_paths: &[U],
188    ignore_include: bool,
189    allow_incomplete: bool,
190) -> Result<(SyntaxTree, Defines), Error> {
191    let (text, defines) = preprocess(
192        path,
193        pre_defines,
194        include_paths,
195        false, // strip_comments
196        ignore_include,
197    )?;
198    parse_sv_pp(text, defines, allow_incomplete)
199}
200
201pub fn parse_sv_pp(
202    text: PreprocessedText,
203    defines: Defines,
204    allow_incomplete: bool,
205) -> Result<(SyntaxTree, Defines), Error> {
206    let span = Span::new_extra(text.text(), SpanInfo::default());
207    let result = if allow_incomplete {
208        sv_parser_incomplete(span)
209    } else {
210        sv_parser(span)
211    };
212    match result {
213        Ok((_, x)) => Ok((
214            SyntaxTree {
215                node: x.into(),
216                text,
217            },
218            defines,
219        )),
220        Err(x) => {
221            let pos = match x {
222                nom::Err::Incomplete(_) => None,
223                nom::Err::Error(e) => error_position(&e),
224                nom::Err::Failure(e) => error_position(&e),
225            };
226            let origin = if let Some(pos) = pos {
227                if let Some(origin) = text.origin(pos) {
228                    Some((origin.0.clone(), origin.1))
229                } else {
230                    None
231                }
232            } else {
233                None
234            };
235            Err(Error::Parse(origin))
236        }
237    }
238}
239
240pub fn parse_sv_str<T: AsRef<Path>, U: AsRef<Path>, V: BuildHasher>(
241    s: &str,
242    path: T,
243    pre_defines: &Defines<V>,
244    include_paths: &[U],
245    ignore_include: bool,
246    allow_incomplete: bool,
247) -> Result<(SyntaxTree, Defines), Error> {
248    let (text, defines) = preprocess_str(
249        s,
250        path,
251        pre_defines,
252        include_paths,
253        ignore_include,
254        false, // strip_comments
255        0, // resolve_depth
256        0, // include_depth
257    )?;
258    parse_sv_pp(text, defines, allow_incomplete)
259}
260
261pub fn parse_lib<T: AsRef<Path>, U: AsRef<Path>, V: BuildHasher>(
262    path: T,
263    pre_defines: &Defines<V>,
264    include_paths: &[U],
265    ignore_include: bool,
266    allow_incomplete: bool,
267) -> Result<(SyntaxTree, Defines), Error> {
268    let (text, defines) = preprocess(
269        path,
270        pre_defines,
271        include_paths,
272        false, // strip_comments
273        ignore_include,
274    )?;
275    parse_lib_pp(text, defines, allow_incomplete)
276}
277
278pub fn parse_lib_str<T: AsRef<Path>, U: AsRef<Path>, V: BuildHasher>(
279    s: &str,
280    path: T,
281    pre_defines: &Defines<V>,
282    include_paths: &[U],
283    ignore_include: bool,
284    allow_incomplete: bool,
285) -> Result<(SyntaxTree, Defines), Error> {
286    let (text, defines) = preprocess_str(
287        s,
288        path,
289        pre_defines,
290        include_paths,
291        ignore_include,
292        false, // strip_comments
293        0, // resolve_depth
294        0, // include_depth
295    )?;
296    parse_lib_pp(text, defines, allow_incomplete)
297}
298
299pub fn parse_lib_pp(
300    text: PreprocessedText,
301    defines: Defines,
302    allow_incomplete: bool,
303) -> Result<(SyntaxTree, Defines), Error> {
304    let span = Span::new_extra(text.text(), SpanInfo::default());
305    let result = if allow_incomplete {
306        lib_parser_incomplete(span)
307    } else {
308        lib_parser(span)
309    };
310    match result {
311        Ok((_, x)) => Ok((
312            SyntaxTree {
313                node: x.into(),
314                text,
315            },
316            defines,
317        )),
318        Err(x) => {
319            let pos = match x {
320                nom::Err::Incomplete(_) => None,
321                nom::Err::Error(e) => error_position(&e),
322                nom::Err::Failure(e) => error_position(&e),
323            };
324            let origin = if let Some(pos) = pos {
325                if let Some(origin) = text.origin(pos) {
326                    Some((origin.0.clone(), origin.1))
327                } else {
328                    None
329                }
330            } else {
331                None
332            };
333            Err(Error::Parse(origin))
334        }
335    }
336}
337
338#[macro_export]
339macro_rules! unwrap_node {
340    ($n:expr, $( $ty:tt ),+) => {{
341        let unwrap = || {
342            for x in $n {
343                match x {
344                    $($crate::RefNode::$ty(x) => return Some($crate::RefNode::$ty(x)),)*
345                    _ => (),
346                }
347            }
348            None
349        };
350        unwrap()
351    }};
352}
353
354#[macro_export]
355macro_rules! unwrap_locate {
356    ($n:expr) => {{
357        let unwrap = || {
358            for x in $n {
359                match x {
360                    $crate::RefNode::Locate(x) => return Some(x),
361                    _ => (),
362                }
363            }
364            None
365        };
366        unwrap()
367    }};
368}
369
370#[cfg(test)]
371mod test {
372    use super::*;
373    use std::collections::HashMap;
374
375    #[test]
376    fn test() {
377        let src = "/* comment */";
378        let (syntax_tree, _) =
379            parse_sv_str(src, PathBuf::from(""), &HashMap::new(), &[""], false, false).unwrap();
380        let comment = unwrap_node!(&syntax_tree, Comment);
381        assert!(comment.is_some());
382    }
383
384    #[test]
385    fn test_continuous() {
386        let src = r##"`ifdef A
387`endif
388
389module FetchStage();
390    always_comb begin
391        for (int j = i + 1; j < FETCH_WIDTH; j++) begin
392        end
393        break;
394    end
395
396    AddrPath fetchAddrOut;
397endmodule"##;
398
399        let src_broken = r##"`ifdef A
400endif
401
402module FetchStage();
403    always_comb begin
404        for (int j = i + 1; j < FETCH_WIDTH; j++) begin
405        end
406        break;
407    end
408
409    AddrPath fetchAddrOut;
410endmodule"##;
411
412        let path = PathBuf::from("");
413        let defines = HashMap::new();
414        let ret = parse_sv_str(src, &path, &defines, &[""], false, false);
415        assert!(ret.is_ok());
416        let ret = parse_sv_str(src_broken, &path, &defines, &[""], false, false);
417        assert!(ret.is_err());
418        let ret = parse_sv_str(src, &path, &defines, &[""], false, false);
419        assert!(ret.is_ok());
420        let ret = parse_sv_str(src_broken, &path, &defines, &[""], false, false);
421        assert!(ret.is_err());
422        let ret = parse_sv_str(src, &path, &defines, &[""], false, false);
423        assert!(ret.is_ok());
424    }
425}