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    // The list of valid segment sequences we will accept is always the same.
53    const VALID_SEGMENT_SEQUENCES: [&[TokenTy]; 4] = [
54        &[
55            TokenTy::Whitespace,
56            TokenTy::ColonColon,
57            TokenTy::Whitespace,
58            TokenTy::Identifier,
59        ],
60        &[
61            TokenTy::Whitespace,
62            TokenTy::ColonColon,
63            TokenTy::Identifier,
64        ],
65        &[
66            TokenTy::ColonColon,
67            TokenTy::Whitespace,
68            TokenTy::Identifier,
69        ],
70        &[TokenTy::ColonColon, TokenTy::Identifier],
71    ];
72
73    for sep_token_sequence in VALID_SEGMENT_SEQUENCES {
74        if parser.matches(sep_token_sequence) {
75            parser.advance(sep_token_sequence.len() - 1);
76            // SAFETY: We just checked/matched that this parser ends with an identifier.
77            return Some(unsafe { Identifier::parse(parser).unwrap_unchecked() });
78        }
79    }
80
81    // If none of the valid segment sequences match, return None.
82    None
83}
84
85#[cfg(test)]
86mod test_path {
87    use crate::{
88        ast::path::Path,
89        lexer::Lexer,
90        parser::Parser,
91        source_tracking::{SourceMap, filename::FileName, source::Source},
92    };
93
94    /// Test the simple case path.
95    #[test]
96    fn test_ok_paths() {
97        let map = SourceMap::new();
98        let sources = &["test::path", "test :: path", "test ::path", "test:: path"];
99
100        for source in sources {
101            dbg!(source);
102            let source_ref = map.add(Source::new_from_static_str(FileName::None, *source));
103            let lexer = Lexer::new(source_ref);
104            let mut parser = Parser::new(lexer);
105            let path = Path::parse(&mut parser).unwrap();
106            assert_eq!(path.head.fragment.as_str(), "test");
107            assert_eq!(path.tail[0].fragment.as_str(), "path");
108            assert_eq!(path.full_path.len(), source.len());
109            assert_eq!(parser.lexer.bytes_remaining(), 0);
110        }
111    }
112
113    #[test]
114    fn test_not_paths() {
115        let sources = &["", "0", "_"];
116
117        for source in sources {
118            let mut parser = Parser::new(Lexer::new_test(source));
119            assert_eq!(Path::parse(&mut parser).unwrap_err().location.as_str(), *source);
120        }
121    }
122}