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