fe_parser/grammar/
module.rs

1use super::expressions::parse_expr;
2use super::functions::parse_fn_def;
3use super::types::{
4    parse_impl_def, parse_path_tail, parse_struct_def, parse_trait_def, parse_type_alias,
5    parse_type_desc,
6};
7use super::{contracts::parse_contract_def, types::parse_enum_def};
8use crate::ast::{ConstantDecl, Module, ModuleStmt, Pragma, Use, UseTree};
9use crate::node::{Node, Span};
10use crate::{Label, ParseFailed, ParseResult, Parser, TokenKind};
11
12use semver::VersionReq;
13
14/// Parse a [`Module`].
15pub fn parse_module(par: &mut Parser) -> Node<Module> {
16    let mut body = vec![];
17    loop {
18        match par.peek() {
19            Some(TokenKind::Newline) => {
20                par.next().unwrap();
21            }
22            None => break,
23            Some(_) => {
24                match parse_module_stmt(par) {
25                    Ok(stmt) => body.push(stmt),
26                    Err(_) => {
27                        // TODO: capture a real span here
28                        body.push(ModuleStmt::ParseError(Span::zero(par.file_id)));
29                        break;
30                    }
31                };
32            }
33        }
34    }
35    let span = Span::zero(par.file_id) + body.first() + body.last();
36    Node::new(Module { body }, span)
37}
38
39/// Parse a [`ModuleStmt`].
40pub fn parse_module_stmt(par: &mut Parser) -> ParseResult<ModuleStmt> {
41    let stmt = match par.peek_or_err()? {
42        TokenKind::Pragma => ModuleStmt::Pragma(parse_pragma(par)?),
43        TokenKind::Use => ModuleStmt::Use(parse_use(par)?),
44        TokenKind::Contract => ModuleStmt::Contract(parse_contract_def(par, None)?),
45        TokenKind::Struct => ModuleStmt::Struct(parse_struct_def(par, None)?),
46        TokenKind::Enum => ModuleStmt::Enum(parse_enum_def(par, None)?),
47        TokenKind::Trait => ModuleStmt::Trait(parse_trait_def(par, None)?),
48        TokenKind::Impl => ModuleStmt::Impl(parse_impl_def(par)?),
49        TokenKind::Type => ModuleStmt::TypeAlias(parse_type_alias(par, None)?),
50        TokenKind::Const => ModuleStmt::Constant(parse_constant(par, None)?),
51        TokenKind::Pub => {
52            let pub_span = par.next()?.span;
53            match par.peek_or_err()? {
54                TokenKind::Fn | TokenKind::Unsafe => {
55                    ModuleStmt::Function(parse_fn_def(par, Some(pub_span))?)
56                }
57                TokenKind::Struct => ModuleStmt::Struct(parse_struct_def(par, Some(pub_span))?),
58                TokenKind::Enum => ModuleStmt::Enum(parse_enum_def(par, Some(pub_span))?),
59                TokenKind::Trait => ModuleStmt::Trait(parse_trait_def(par, Some(pub_span))?),
60                TokenKind::Type => ModuleStmt::TypeAlias(parse_type_alias(par, Some(pub_span))?),
61                TokenKind::Const => ModuleStmt::Constant(parse_constant(par, Some(pub_span))?),
62                TokenKind::Contract => {
63                    ModuleStmt::Contract(parse_contract_def(par, Some(pub_span))?)
64                }
65                _ => {
66                    let tok = par.next()?;
67                    par.unexpected_token_error(
68                        &tok,
69                        "failed to parse module",
70                        vec!["Note: expected `fn`".into()],
71                    );
72                    return Err(ParseFailed);
73                }
74            }
75        }
76        TokenKind::Fn | TokenKind::Unsafe => ModuleStmt::Function(parse_fn_def(par, None)?),
77        TokenKind::Hash => {
78            let attr = par.expect(TokenKind::Hash, "expected `#`")?;
79            let attr_name = par.expect_with_notes(TokenKind::Name, "failed to parse attribute definition", |_|
80                vec!["Note: an attribute name must start with a letter or underscore, and contain letters, numbers, or underscores".into()])?;
81            ModuleStmt::Attribute(Node::new(attr_name.text.into(), attr.span + attr_name.span))
82        }
83        _ => {
84            let tok = par.next()?;
85            par.unexpected_token_error(
86                &tok,
87                "failed to parse module",
88                vec!["Note: expected import, contract, struct, type or const".into()],
89            );
90            return Err(ParseFailed);
91        }
92    };
93    Ok(stmt)
94}
95
96/// Parse a constant, e.g. `const MAGIC_NUMBER: u256 = 4711`.
97/// # Panics
98/// Panics if the next token isn't `const`.
99pub fn parse_constant(par: &mut Parser, pub_qual: Option<Span>) -> ParseResult<Node<ConstantDecl>> {
100    let const_tok = par.assert(TokenKind::Const);
101    let name = par.expect(TokenKind::Name, "failed to parse constant declaration")?;
102    par.expect_with_notes(
103        TokenKind::Colon,
104        "failed to parse constant declaration",
105        |_| {
106            vec![
107                "Note: constant name must be followed by a colon and a type description".into(),
108                format!("Example: let `{}: u256 = 1000`", name.text),
109            ]
110        },
111    )?;
112    let typ = parse_type_desc(par)?;
113    par.expect_with_notes(
114        TokenKind::Eq,
115        "failed to parse constant declaration",
116        |_| {
117            vec![
118            "Note: the type of a constant must be followed by an equals sign and a value assignment"
119                .into(),
120                format!(
121                    "Example: let `{}: u256 = 1000`",
122                    name.text
123                ),
124        ]
125        },
126    )?;
127
128    let exp = parse_expr(par)?;
129
130    let span = const_tok.span + exp.span;
131    Ok(Node::new(
132        ConstantDecl {
133            name: name.into(),
134            typ,
135            value: exp,
136            pub_qual,
137        },
138        span,
139    ))
140}
141
142/// Parse a `use` statement.
143/// # Panics
144/// Panics if the next token isn't `use`.
145pub fn parse_use(par: &mut Parser) -> ParseResult<Node<Use>> {
146    let use_tok = par.assert(TokenKind::Use);
147
148    let tree = parse_use_tree(par)?;
149    let tree_span = tree.span;
150
151    Ok(Node::new(Use { tree }, use_tok.span + tree_span))
152}
153
154/// Parse a `use` tree.
155pub fn parse_use_tree(par: &mut Parser) -> ParseResult<Node<UseTree>> {
156    let (path, path_span, trailing_delim) = {
157        let path_head =
158            par.expect_with_notes(TokenKind::Name, "failed to parse `use` statement", |_| {
159                vec![
160                    "Note: `use` paths must start with a name".into(),
161                    "Example: `use foo::bar`".into(),
162                ]
163            })?;
164        parse_path_tail(par, path_head.into())
165    };
166
167    if trailing_delim.is_some() {
168        match par.peek() {
169            Some(TokenKind::BraceOpen) => {
170                par.next()?;
171
172                let mut children = vec![];
173                let close_brace_span;
174
175                loop {
176                    children.push(parse_use_tree(par)?);
177                    let tok = par.next()?;
178                    match tok.kind {
179                        TokenKind::Comma => {
180                            continue;
181                        }
182                        TokenKind::BraceClose => {
183                            close_brace_span = tok.span;
184                            break;
185                        }
186                        _ => {
187                            par.unexpected_token_error(
188                                &tok,
189                                "failed to parse `use` tree",
190                                vec!["Note: expected a `,` or `}` token".to_string()],
191                            );
192                            return Err(ParseFailed);
193                        }
194                    }
195                }
196
197                Ok(Node::new(
198                    UseTree::Nested {
199                        prefix: path,
200                        children,
201                    },
202                    close_brace_span,
203                ))
204            }
205            Some(TokenKind::Star) => {
206                par.next()?;
207                Ok(Node::new(UseTree::Glob { prefix: path }, path_span))
208            }
209            _ => {
210                let tok = par.next()?;
211                par.unexpected_token_error(
212                    &tok,
213                    "failed to parse `use` tree",
214                    vec!["Note: expected a `*`, `{` or name token".to_string()],
215                );
216                Err(ParseFailed)
217            }
218        }
219    } else if par.peek() == Some(TokenKind::As) {
220        par.next()?;
221
222        let rename_tok = par.expect(TokenKind::Name, "failed to parse `use` tree")?;
223        let span = path_span + rename_tok.span;
224        let rename = Some(rename_tok.into());
225
226        Ok(Node::new(UseTree::Simple { path, rename }, span))
227    } else {
228        Ok(Node::new(UseTree::Simple { path, rename: None }, path_span))
229    }
230}
231
232/// Parse a `pragma <version-requirement>` statement.
233pub fn parse_pragma(par: &mut Parser) -> ParseResult<Node<Pragma>> {
234    let tok = par.assert(TokenKind::Pragma);
235    assert_eq!(tok.text, "pragma");
236
237    let mut version_string = String::new();
238    let mut tokens = vec![];
239    loop {
240        match par.peek() {
241            Some(TokenKind::Newline) => break,
242            None => break,
243            _ => {
244                let tok = par.next()?;
245                version_string.push_str(tok.text);
246                tokens.push(tok);
247            }
248        }
249    }
250
251    let version_requirement_span = match (tokens.first(), tokens.last()) {
252        (Some(first), Some(last)) => first.span + last.span,
253        _ => {
254            par.error(
255                tok.span,
256                "failed to parse pragma statement: missing version requirement",
257            );
258            return Err(ParseFailed);
259        }
260    };
261
262    match VersionReq::parse(&version_string) {
263        Ok(_) => Ok(Node::new(
264            Pragma {
265                version_requirement: Node::new(version_string.into(), version_requirement_span),
266            },
267            tok.span + version_requirement_span,
268        )),
269        Err(err) => {
270            par.fancy_error(
271                format!("failed to parse pragma statement: {err}"),
272                vec![Label::primary(
273                    version_requirement_span,
274                    "Invalid version requirement",
275                )],
276                vec!["Example: `^0.5.0`".into()],
277            );
278            Err(ParseFailed)
279        }
280    }
281}