fe_parser/grammar/
contracts.rs

1use super::functions::parse_fn_def;
2use super::types::{parse_field, parse_opt_qualifier};
3
4use crate::ast::{Contract, ContractStmt};
5use crate::node::{Node, Span};
6use crate::{ParseFailed, ParseResult, Parser, TokenKind};
7
8// Rule: all "statement" level parse functions consume their trailing
9// newline(s), either directly or via a function they call.
10// This is required to parse an `if` block, because we need to peek past the
11// trailing newlines to check whether it's followed by an `else` block, and is
12// done for all statements for consistency.
13
14/// Parse a contract definition.
15/// # Panics
16/// Panics if the next token isn't `contract`.
17pub fn parse_contract_def(
18    par: &mut Parser,
19    contract_pub_qual: Option<Span>,
20) -> ParseResult<Node<Contract>> {
21    let contract_tok = par.assert(TokenKind::Contract);
22    let contract_name = par.expect_with_notes(
23        TokenKind::Name,
24        "failed to parse contract definition",
25        |_| vec!["Note: `contract` must be followed by a name, which must start with a letter and contain only letters, numbers, or underscores".into()],
26    )?;
27
28    let mut span = contract_tok.span + contract_name.span;
29    par.enter_block(span, "contract definition")?;
30
31    let mut fields = vec![];
32    let mut defs = vec![];
33
34    loop {
35        par.eat_newlines();
36        let mut pub_qual = parse_opt_qualifier(par, TokenKind::Pub);
37        let const_qual = parse_opt_qualifier(par, TokenKind::Const);
38        if pub_qual.is_none() && const_qual.is_some() && par.peek() == Some(TokenKind::Pub) {
39            pub_qual = parse_opt_qualifier(par, TokenKind::Pub);
40            par.error(
41                pub_qual.unwrap() + const_qual,
42                "`const pub` should be written `pub const`",
43            );
44        }
45
46        match par.peek_or_err()? {
47            TokenKind::Name => {
48                let field = parse_field(par, vec![], pub_qual, const_qual)?;
49                if !defs.is_empty() {
50                    par.error(
51                        field.span,
52                        "contract field definitions must come before any function definitions",
53                    );
54                }
55                fields.push(field);
56            }
57            TokenKind::Fn | TokenKind::Unsafe => {
58                if let Some(span) = const_qual {
59                    par.error(
60                        span,
61                        "`const` qualifier can't be used with function definitions",
62                    );
63                }
64                defs.push(ContractStmt::Function(parse_fn_def(par, pub_qual)?));
65            }
66            TokenKind::BraceClose => {
67                span += par.next()?.span;
68                break;
69            }
70            _ => {
71                let tok = par.next()?;
72                par.unexpected_token_error(
73                    &tok,
74                    "failed to parse contract definition body",
75                    vec![],
76                );
77                return Err(ParseFailed);
78            }
79        };
80    }
81
82    Ok(Node::new(
83        Contract {
84            name: Node::new(contract_name.text.into(), contract_name.span),
85            fields,
86            body: defs,
87            pub_qual: contract_pub_qual,
88        },
89        span,
90    ))
91}