wright/parser/decl/
import.rs

1//! Parser implementation for `use path::to::thing;` declaration.
2
3use crate::{
4    ast::{decl::import::ImportDecl, identifier::Identifier, path::Path},
5    lexer::token::{Token, TokenTy},
6    parser::{
7        Parser,
8        error::{ParserError, ParserErrorKind},
9    },
10    source_tracking::fragment::Fragment,
11};
12
13impl ImportDecl {
14    /// Parse an import declaration.
15    ///
16    /// This will advance the parser if `use` is seen -- if a valid formed import does not follow,
17    /// the parser may be left in the middle of a malformed declaration.
18    pub fn parse(parser: &mut Parser) -> Result<Self, ParserError> {
19        let use_kw: Token = parser.next_if_is(TokenTy::KwUse).ok_or(
20            ParserErrorKind::ExpectedImportDeclaration.at(parser.peek_fragment_or_rest_cloned()),
21        )?;
22
23        // Require a whitespace after the keyword.
24        parser.consume_at_least_one_whitespace()?;
25        // Parse the path.
26        let path: Path = Path::parse(parser)?;
27
28        // Whitespace and then "as ...;" or optional whitespace and semi ";".
29
30        // The "as ...;" requires a whitespace.
31        let imported_as = match parser.next_if_is(TokenTy::Whitespace) {
32            // If there's no whitespace after the path, we expect it to be followed by a semicolon (no renaming).
33            None => None,
34
35            // If there is a whitespace, then it could be followed by `as ...;` or just `;`.
36            Some(_) => {
37                // Either way, consume any additional whitespace/comments.
38                parser.consume_optional_whitespace();
39
40                // Check if we have an `as` and if so read the renaming clause.
41                // Otherwise pass on to reading the semicolon.
42                match parser.next_if_is(TokenTy::KwAs) {
43                    // No `as` -- do nothing (return no renaming clause).
44                    None => None,
45
46                    // `as ...;` -- consume the ` ...` part.
47                    Some(_) => {
48                        parser.consume_at_least_one_whitespace().map_err(|e| {
49                            e.with_help("whitespace needed between \"as\" and binding.")
50                        })?;
51
52                        let imported_as = Identifier::parse(parser).map_err(|e| {
53                            e.with_help("expected binding in \"use ... as\" declaration.")
54                        })?;
55
56                        Some(imported_as)
57                    }
58                }
59            }
60        };
61
62        parser.consume_optional_whitespace();
63
64        if let Some(semi) = parser.next_if_is(TokenTy::Semi) {
65            Ok(ImportDecl {
66                matching_source: Fragment::cover(&use_kw.fragment, &semi.fragment),
67                imported_item: path,
68                imported_as,
69            })
70        } else {
71            Err(ParserErrorKind::ImportMustEndWithSemicolon
72                .at(parser.peek_fragment_or_rest_cloned()))
73        }
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use crate::{ast::decl::import::ImportDecl, lexer::Lexer, parser::Parser};
80
81    #[test]
82    fn test_import() {
83        let mut parser = Parser::new(Lexer::new_test("use wright::util;"));
84        let import_decl = ImportDecl::parse(&mut parser).unwrap();
85        assert!(parser.lexer.remaining.is_empty());
86        assert_eq!(import_decl.imported_item.head.fragment.as_str(), "wright");
87        assert_eq!(import_decl.imported_item.tail[0].fragment.as_str(), "util");
88    }
89
90    #[test]
91    fn test_import_with_whitespace() {
92        let mut parser = Parser::new(Lexer::new_test("use wright :: util ;"));
93        let import_decl = ImportDecl::parse(&mut parser).unwrap();
94        assert!(parser.lexer.remaining.is_empty());
95        assert_eq!(import_decl.imported_item.head.fragment.as_str(), "wright");
96        assert_eq!(import_decl.imported_item.tail[0].fragment.as_str(), "util");
97    }
98
99    #[test]
100    fn test_import_as() {
101        let mut parser = Parser::new(Lexer::new_test("use wright::util as u;"));
102        let import_decl = ImportDecl::parse(&mut parser).unwrap();
103        assert!(parser.lexer.remaining.is_empty());
104        assert_eq!(import_decl.imported_item.head.fragment.as_str(), "wright");
105        assert_eq!(import_decl.imported_item.tail[0].fragment.as_str(), "util");
106        assert_eq!(import_decl.imported_as.unwrap().fragment.as_str(), "u");
107    }
108
109    #[test]
110    fn test_import_as_with_comment() {
111        let mut parser = Parser::new(Lexer::new_test("use wright::util as /* old_name */ u;"));
112        let import_decl = ImportDecl::parse(&mut parser).unwrap();
113        assert!(parser.lexer.remaining.is_empty());
114        assert_eq!(import_decl.imported_item.head.fragment.as_str(), "wright");
115        assert_eq!(import_decl.imported_item.tail[0].fragment.as_str(), "util");
116        assert_eq!(import_decl.imported_as.unwrap().fragment.as_str(), "u");
117    }
118
119    #[test]
120    fn test_import_as_with_preceding_comment() {
121        let mut parser = Parser::new(Lexer::new_test("use wright::util /* as old_name */ as u;"));
122        let import_decl = ImportDecl::parse(&mut parser).unwrap();
123        assert!(parser.lexer.remaining.is_empty());
124        assert_eq!(import_decl.imported_item.head.fragment.as_str(), "wright");
125        assert_eq!(import_decl.imported_item.tail[0].fragment.as_str(), "util");
126        assert_eq!(import_decl.imported_as.unwrap().fragment.as_str(), "u");
127    }
128}