1use 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 pub fn parse(parser: &mut Parser) -> Result<Self, ParserError> {
17 let head: Identifier = parse_head(parser)?;
18 let mut tail = Vec::new();
19
20 while let Some(ident) = parse_segment(parser) {
22 tail.push(ident);
23 }
24
25 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 full_path: Fragment {
32 source: Arc::clone(&head.fragment.source),
33 range: matched_source_range,
34 },
35 head,
36 tail,
37 })
38 }
39}
40
41fn 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
49fn parse_segment(parser: &mut Parser) -> Option<Identifier> {
52 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 return Some(unsafe { Identifier::parse(parser).unwrap_unchecked() });
78 }
79 }
80
81 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]
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}