wright/parser/
path.rs

1//! Parsing implementation for [Path].
2
3use std::sync::Arc;
4
5use super::Parser;
6use super::error::ParserError;
7use super::error::ParserErrorKind;
8use crate::ast::identifier::Identifier;
9use crate::ast::path::Path;
10use crate::lexer::token::TokenTy;
11use crate::source_tracking::fragment::Fragment;
12
13impl Path {
14    /// Parse a [Path] from the given [Parser]. This is greedy (as much path as possible will be parsed).
15    /// [Path]s of size 1 (just a single identifier) are accepted.
16    pub fn parse(parser: &mut Parser) -> Result<Self, ParserError> {
17        let head: Identifier = parse_head(parser)?;
18        let mut tail = Vec::new();
19
20        // Parse the tail.
21        while let Some(ident) = parse_segment(parser) {
22            tail.push(ident);
23        }
24
25        // Calculate the fragment containing the whole path.
26        let last = tail.last().unwrap_or(&head);
27        let matched_source_range = head.fragment.range.start..last.fragment.range.end;
28
29        Ok(Path {
30            // Head and tail should all have the same source ref since they came from the same parser.
31            full_path: Fragment {
32                source: Arc::clone(&head.fragment.source),
33                range: matched_source_range,
34            },
35            head,
36            tail,
37        })
38    }
39}
40
41/// Parse the first (and possibly only) [Identifier] in the [Path].
42fn parse_head(parser: &mut Parser) -> Result<Identifier, ParserError> {
43    Identifier::parse(parser).map_err(|mut err| {
44        err.kind = ParserErrorKind::ExpectedPath;
45        err
46    })
47}
48
49/// Attempt to parse a segment of this path indivisbly (never just parse a seperator without another [Identifier]
50/// at the end of it).
51fn parse_segment(parser: &mut Parser) -> Option<Identifier> {
52    if parser.matches_ignore_whitespace(&[TokenTy::ColonColon, TokenTy::Identifier]) {
53        // Consume any whitespace
54        parser.consume_optional_whitespace();
55
56        // Skip the double colon
57        parser.advance(1);
58
59        // Consume any more whitespace
60        parser.consume_optional_whitespace();
61
62        // Consume the identifier we know is at the end of the sequence.
63        // SAFETY: We just checked/matched that this parser ends with an identifier.
64        return Some(unsafe { Identifier::parse(parser).unwrap_unchecked() });
65    }
66
67    // If the sequence doesn't match, there's no segment/tail.
68    None
69}
70
71#[cfg(test)]
72mod test_path {
73    use crate::{
74        ast::path::Path,
75        lexer::Lexer,
76        parser::Parser,
77        source_tracking::{SourceMap, filename::FileName, source::Source},
78    };
79
80    /// Test the simple case path.
81    #[test]
82    fn test_ok_paths() {
83        let map = SourceMap::new();
84        let sources = &["test::path", "test :: path", "test ::path", "test:: path"];
85
86        for source in sources {
87            dbg!(source);
88            let source_ref = map.add(Source::new_from_static_str(FileName::None, *source));
89            let lexer = Lexer::new(source_ref);
90            let mut parser = Parser::new(lexer);
91            let path = Path::parse(&mut parser).unwrap();
92            assert_eq!(path.head.fragment.as_str(), "test");
93            assert_eq!(path.tail[0].fragment.as_str(), "path");
94            assert_eq!(path.full_path.len(), source.len());
95            assert_eq!(parser.lexer.bytes_remaining(), 0);
96        }
97    }
98
99    #[test]
100    fn test_not_paths() {
101        let sources = &["", "0", "_"];
102
103        for source in sources {
104            let mut parser = Parser::new(Lexer::new_test(source));
105            assert_eq!(Path::parse(&mut parser).unwrap_err().location.as_str(), *source);
106        }
107    }
108}