wright/parser/ty/
named.rs

1use crate::{
2    ast::{
3        path::Path,
4        ty::{NamedTy, Type},
5    },
6    lexer::token::TokenTy,
7    parser::{
8        Parser,
9        error::{ParserError, ParserErrorKind},
10    },
11    source_tracking::fragment::Fragment,
12};
13
14impl NamedTy {
15    /// Parse a named type from source code.
16    pub fn parse(parser: &mut Parser) -> Result<NamedTy, ParserError> {
17        // If we don't start with a path, there's no type here.
18        let path = Path::parse(parser)?;
19
20        let mut generic_tys = Vec::new();
21
22        // If the next non-whitespace is an angle bracket, consume it and then read through generics until it's closed.
23        if parser.matches_ignore_whitespace(&[TokenTy::Lt]) {
24            // Chew through any whitespace.
25            parser.consume_optional_whitespace();
26
27            // Skip the `<`
28            parser.advance(1);
29
30            loop {
31                // Parse a generic type
32                let t = Type::parse(parser)?;
33
34                // Push the type to the list of generics.
35                generic_tys.push(t);
36
37                // Check if it was the last one.
38                const ENDING_SEQUENCES: &[&[TokenTy]] =
39                    &[&[TokenTy::Comma, TokenTy::Gt], &[TokenTy::Gt]];
40
41                for seq in ENDING_SEQUENCES {
42                    if parser.matches_ignore_whitespace(seq) {
43                        // Chew through remaining tokens
44                        for _ in 0..(seq.len() - 1) {
45                            parser.consume_optional_whitespace();
46                            parser.advance(1);
47                        }
48
49                        parser.consume_optional_whitespace();
50
51                        // SAFETY: We have confirmed that there's a token here and it's not an unknown.
52                        let last_token =
53                            unsafe { parser.next_token().unwrap_unchecked().unwrap_unchecked() };
54
55                        // Compute the fragment to cover the whole thing.
56                        let matching_source =
57                            Fragment::cover(&path.full_path, &last_token.fragment);
58
59                        return Ok(NamedTy {
60                            matching_source,
61                            name: path,
62                            generic_tys,
63                            // generic_consts: (),
64                        });
65                    }
66                }
67
68                // If it wasn't the last one (we get here):
69                // Try to parse a comma and then another generic type or fail.
70                if !parser.matches_ignore_whitespace(&[TokenTy::Comma]) {
71                    let fragment = parser.peek_fragment_or_rest_cloned();
72                    return Err(ParserErrorKind::UnterminatedGenericTypeSignature.at(fragment));
73                }
74
75                // Chew through whitespace and comment
76                parser.consume_optional_whitespace();
77                parser.advance(1);
78                parser.consume_optional_whitespace();
79
80                // Loop back and consume next type.
81            }
82        }
83
84        // If parser does not match the angle bracket for generics, just return a named
85        // type without them
86        Ok(NamedTy {
87            matching_source: path.full_path.clone(),
88            name: path,
89            generic_tys,
90            // generic_consts: (),
91        })
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use crate::{
98        ast::ty::{AtomicTyVariant, NamedTy},
99        lexer::Lexer,
100        parser::Parser,
101    };
102
103    #[test]
104    fn test_basic_named_type() {
105        let mut parser = Parser::new(Lexer::new_test("MyType"));
106        let named_ty = NamedTy::parse(&mut parser).expect("Failed to parse named type");
107        assert_eq!(named_ty.name.full_path.as_str(), "MyType");
108        assert_eq!(named_ty.name.full_path, named_ty.matching_source);
109        assert!(named_ty.generic_tys.is_empty());
110    }
111
112    #[test]
113    fn test_named_type_with_generics() {
114        let mut parser = Parser::new(Lexer::new_test("MyType<i8, @@@i8, OtherType<ThirdType>, >"));
115        let named_ty = NamedTy::parse(&mut parser).unwrap();
116        assert_eq!(named_ty.name.full_path.as_str(), "MyType");
117        assert_eq!(
118            named_ty.generic_tys[0]
119                .downcast_primitive()
120                .unwrap()
121                .variant,
122            AtomicTyVariant::I8
123        );
124        assert_eq!(named_ty.generic_tys[1].matching_source().as_str(), "@@@i8");
125        assert_eq!(
126            named_ty.generic_tys[2]
127                .downcast_named()
128                .unwrap()
129                .name
130                .full_path
131                .as_str(),
132            "OtherType"
133        );
134        assert_eq!(
135            named_ty.generic_tys[2]
136                .downcast_named()
137                .unwrap()
138                .matching_source
139                .as_str(),
140            "OtherType<ThirdType>"
141        );
142    }
143}