leo_parser/parser/
file.rs

1// Copyright (C) 2019-2025 Provable Inc.
2// This file is part of the Leo library.
3
4// The Leo library is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// The Leo library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
16
17use super::*;
18
19use leo_errors::{ParserError, Result};
20use leo_span::{Symbol, sym};
21
22impl<N: Network> ParserContext<'_, N> {
23    /// Returns a [`Program`] AST if all tokens can be consumed and represent a valid Leo program.
24    pub fn parse_program(&mut self) -> Result<Program> {
25        let mut imports = IndexMap::new();
26        let mut program_scopes = IndexMap::new();
27
28        // TODO: Remove restrictions on multiple program scopes
29        let mut parsed_program_scope = false;
30
31        while self.has_next() {
32            match &self.token.token {
33                Token::Import => {
34                    let (id, import) = self.parse_import()?;
35                    imports.insert(id, import);
36                }
37                Token::Program => {
38                    match parsed_program_scope {
39                        // Only one program scope is allowed per file.
40                        true => {
41                            return Err(ParserError::only_one_program_scope_is_allowed(self.token.span).into());
42                        }
43                        false => {
44                            parsed_program_scope = true;
45                            let program_scope = self.parse_program_scope()?;
46                            program_scopes.insert(program_scope.program_id.name.name, program_scope);
47                        }
48                    }
49                }
50                _ => return Err(Self::unexpected_item(&self.token, &[Token::Import, Token::Program]).into()),
51            }
52        }
53
54        // Requires that at least one program scope is present.
55        if !parsed_program_scope {
56            return Err(ParserError::missing_program_scope(self.token.span).into());
57        }
58
59        Ok(Program { imports, stubs: IndexMap::new(), program_scopes })
60    }
61
62    fn unexpected_item(token: &SpannedToken, expected: &[Token]) -> ParserError {
63        ParserError::unexpected(
64            &token.token,
65            expected.iter().map(|x| format!("'{x}'")).collect::<Vec<_>>().join(", "),
66            token.span,
67        )
68    }
69
70    // TODO: remove import resolution from parser.
71    /// Parses an import statement `import foo.leo;`.
72    pub(super) fn parse_import(&mut self) -> Result<(Symbol, (Program, Span))> {
73        // Parse `import`.
74        let start = self.expect(&Token::Import)?;
75
76        // Parse `foo`.
77        let import_name = self.expect_identifier()?;
78
79        // Parse `.`.
80        self.expect(&Token::Dot)?;
81
82        // Parse network, which currently must be `aleo`.
83        if !self.eat(&Token::Aleo) {
84            // Throw error for non-aleo networks.
85            return Err(ParserError::invalid_network(self.token.span).into());
86        }
87
88        let end = self.expect(&Token::Semicolon)?;
89
90        // Return the import name and the span.
91        Ok((import_name.name, (Program::default(), start + end)))
92    }
93
94    /// Parses a program scope `program foo.aleo { ... }`.
95    fn parse_program_scope(&mut self) -> Result<ProgramScope> {
96        // Parse `program` keyword.
97        let start = self.expect(&Token::Program)?;
98
99        // Parse the program name.
100        let name = self.expect_identifier()?;
101
102        // Set the program name in the context.
103        self.program_name = Some(name.name);
104
105        // Parse the `.`.
106        self.expect(&Token::Dot)?;
107
108        // Parse the program network, which must be `aleo`, otherwise throw parser error.
109        let network_span = self.expect(&Token::Aleo).map_err(|_| ParserError::invalid_network(self.token.span))?;
110
111        // Construct the program id.
112        let program_id = ProgramId {
113            name,
114            network: Identifier { name: sym::aleo, id: self.node_builder.next_id(), span: network_span },
115        };
116
117        // Parse `{`.
118        self.expect(&Token::LeftCurly)?;
119
120        // Parse the body of the program scope.
121        let mut consts: Vec<(Symbol, ConstDeclaration)> = Vec::new();
122        let (mut transitions, mut functions) = (Vec::new(), Vec::new());
123        let mut structs: Vec<(Symbol, Composite)> = Vec::new();
124        let mut mappings: Vec<(Symbol, Mapping)> = Vec::new();
125
126        while self.has_next() {
127            match &self.token.token {
128                Token::Const => {
129                    let declaration = self.parse_const_declaration_statement()?;
130                    consts.push((Symbol::intern(&declaration.place.to_string()), declaration));
131                }
132                Token::Struct | Token::Record => {
133                    let (id, struct_) = self.parse_struct()?;
134                    structs.push((id, struct_));
135                }
136                Token::Mapping => {
137                    let (id, mapping) = self.parse_mapping()?;
138                    mappings.push((id, mapping));
139                }
140                Token::At | Token::Async | Token::Function | Token::Transition | Token::Inline | Token::Script => {
141                    let (id, function) = self.parse_function()?;
142
143                    // Partition into transitions and functions so that we don't have to sort later.
144                    if function.variant.is_transition() {
145                        transitions.push((id, function));
146                    } else {
147                        functions.push((id, function));
148                    }
149                }
150                Token::RightCurly => break,
151                _ => {
152                    return Err(Self::unexpected_item(&self.token, &[
153                        Token::Const,
154                        Token::Struct,
155                        Token::Record,
156                        Token::Mapping,
157                        Token::At,
158                        Token::Async,
159                        Token::Function,
160                        Token::Transition,
161                        Token::Inline,
162                        Token::Script,
163                        Token::RightCurly,
164                    ])
165                    .into());
166                }
167            }
168        }
169
170        // Parse `}`.
171        let end = self.expect(&Token::RightCurly)?;
172
173        Ok(ProgramScope {
174            program_id,
175            consts,
176            functions: [transitions, functions].concat(),
177            structs,
178            mappings,
179            span: start + end,
180        })
181    }
182
183    /// Returns a [`Vec<Member>`] AST node if the next tokens represent a struct member.
184    fn parse_struct_members(&mut self) -> Result<(Vec<Member>, Span)> {
185        let mut members = Vec::new();
186
187        while !self.check(&Token::RightCurly) {
188            let variable = self.parse_member_variable_declaration()?;
189
190            if !self.check(&Token::RightCurly) && !self.eat(&Token::Comma) {
191                self.emit_err(ParserError::comma_expected_after_member(self.token.span));
192            }
193
194            members.push(variable);
195        }
196        let span = self.expect(&Token::RightCurly)?;
197
198        Ok((members, span))
199    }
200
201    /// Parses `IDENT: TYPE`.
202    pub(super) fn parse_typed_ident(&mut self) -> Result<(Identifier, Type, Span)> {
203        let name = self.expect_identifier()?;
204        self.expect(&Token::Colon)?;
205        let (type_, span) = self.parse_type()?;
206
207        Ok((name, type_, name.span + span))
208    }
209
210    /// Returns a [`Member`] AST node if the next tokens represent a struct member variable.
211    fn parse_member_variable_declaration(&mut self) -> Result<Member> {
212        let mode = self.parse_mode()?;
213
214        let (identifier, type_, span) = self.parse_typed_ident()?;
215
216        Ok(Member { mode, identifier, type_, span, id: self.node_builder.next_id() })
217    }
218
219    /// Parses a struct or record definition, e.g., `struct Foo { ... }` or `record Foo { ... }`.
220    pub(super) fn parse_struct(&mut self) -> Result<(Symbol, Composite)> {
221        let is_record = matches!(&self.token.token, Token::Record);
222        let start = self.expect_any(&[Token::Struct, Token::Record])?;
223
224        // Check if using external type
225        let file_type = self.look_ahead(1, |t| &t.token);
226        if self.token.token == Token::Dot && (file_type == &Token::Aleo) {
227            return Err(ParserError::cannot_declare_external_struct(self.token.span).into());
228        }
229
230        let struct_name = self.expect_identifier()?;
231
232        let const_parameters = if self.eat(&Token::DoubleColon) {
233            self.parse_bracket_comma_list(|p| p.parse_const_parameter().map(Some))?.0
234        } else {
235            vec![]
236        };
237
238        self.expect(&Token::LeftCurly)?;
239        let (members, end) = self.parse_struct_members()?;
240
241        // Only provide a program name for records.
242        let external = if is_record { self.program_name } else { None };
243
244        Ok((struct_name.name, Composite {
245            identifier: struct_name,
246            const_parameters,
247            members,
248            external,
249            is_record,
250            span: start + end,
251            id: self.node_builder.next_id(),
252        }))
253    }
254
255    /// Parses a mapping declaration, e.g. `mapping balances: address => u128`.
256    pub(super) fn parse_mapping(&mut self) -> Result<(Symbol, Mapping)> {
257        let start = self.expect(&Token::Mapping)?;
258        let identifier = self.expect_identifier()?;
259        self.expect(&Token::Colon)?;
260        let (key_type, _) = self.parse_type()?;
261        self.expect(&Token::BigArrow)?;
262        let (value_type, _) = self.parse_type()?;
263        let end = self.expect(&Token::Semicolon)?;
264        Ok((identifier.name, Mapping {
265            identifier,
266            key_type,
267            value_type,
268            span: start + end,
269            id: self.node_builder.next_id(),
270        }))
271    }
272
273    // TODO: Return a span associated with the mode.
274    /// Returns a [`ParamMode`] AST node if the next tokens represent a function parameter mode.
275    pub(super) fn parse_mode(&mut self) -> Result<Mode> {
276        let private = self.eat(&Token::Private).then_some(self.prev_token.span);
277        let public = self.eat(&Token::Public).then_some(self.prev_token.span);
278        let constant = self.eat(&Token::Constant).then_some(self.prev_token.span);
279
280        match (private, public, constant) {
281            (None, None, None) => Ok(Mode::None),
282            (Some(_), None, None) => Ok(Mode::Private),
283            (None, Some(_), None) => Ok(Mode::Public),
284            (None, None, Some(_)) => Ok(Mode::Constant),
285            _ => {
286                let mut spans = [private, public, constant].into_iter().flatten();
287
288                // There must exist at least one mode, since the none case is handled above.
289                let starting_span = spans.next().unwrap();
290                // Sum the spans.
291                let summed_span = spans.fold(starting_span, |span, next| span + next);
292                // Emit an error.
293                Err(ParserError::inputs_multiple_variable_modes_specified(summed_span).into())
294            }
295        }
296    }
297
298    /// Returns an [`Input`] AST node if the next tokens represent a function input.
299    fn parse_input(&mut self) -> Result<Input> {
300        let mode = self.parse_mode()?;
301        let name = self.expect_identifier()?;
302        self.expect(&Token::Colon)?;
303
304        let (type_, type_span) = self.parse_type()?;
305        let span = name.span() + type_span;
306
307        Ok(Input { identifier: name, mode, type_, span, id: self.node_builder.next_id() })
308    }
309
310    /// Returns an [`Output`] AST node if the next tokens represent a function output.
311    fn parse_output(&mut self) -> Result<Output> {
312        let mode = self.parse_mode()?;
313        let (type_, span) = self.parse_type()?;
314        Ok(Output { mode, type_, span, id: self.node_builder.next_id() })
315    }
316
317    /// Returns an [`Annotation`] AST node if the next tokens represent an annotation.
318    fn parse_annotation(&mut self) -> Result<Annotation> {
319        // Parse the `@` symbol and identifier.
320        let start = self.expect(&Token::At)?;
321        let identifier = match self.token.token {
322            Token::Program => {
323                Identifier { name: sym::program, span: self.expect(&Token::Program)?, id: self.node_builder.next_id() }
324            }
325            _ => self.expect_identifier()?,
326        };
327        let mut span = start + identifier.span;
328
329        // TODO: Verify that this check is sound.
330        // Check that there is no whitespace or comments in between the `@` symbol and identifier.
331        if identifier.span.hi - start.lo > 1 + identifier.name.to_string().len() as u32 {
332            return Err(ParserError::space_in_annotation(span).into());
333        }
334
335        let mut map = IndexMap::new();
336
337        if self.eat(&Token::LeftParen) {
338            loop {
339                let key = self.expect_identifier()?;
340                self.expect(&Token::Assign)?;
341                if let Token::StaticString(s) = &self.token.token {
342                    map.insert(key.name, s.clone());
343                    self.bump();
344                } else {
345                    return Err(ParserError::unexpected(&self.token.token, "a string", self.token.span).into());
346                }
347
348                match &self.token.token {
349                    Token::Comma => {
350                        self.bump();
351                        if let Token::RightParen = &self.token.token {
352                            span = span + self.token.span;
353                            self.bump();
354                            break;
355                        }
356                    }
357                    Token::RightParen => {
358                        span = span + self.token.span;
359                        self.bump();
360                        break;
361                    }
362                    _ => return Err(ParserError::unexpected(&self.token.token, ", or )", self.token.span).into()),
363                }
364            }
365        }
366
367        Ok(Annotation { identifier, span, id: self.node_builder.next_id(), map })
368    }
369
370    /// Returns an [`(Identifier, Function)`] AST node if the next tokens represent a function name
371    /// and function definition.
372    fn parse_function(&mut self) -> Result<(Symbol, Function)> {
373        // TODO: Handle dangling annotations.
374        // Parse annotations, if they exist.
375        let mut annotations = Vec::new();
376        while self.look_ahead(0, |t| &t.token) == &Token::At {
377            annotations.push(self.parse_annotation()?)
378        }
379        // Parse a potential async signifier.
380        let (is_async, start_async) =
381            if self.token.token == Token::Async { (true, self.expect(&Token::Async)?) } else { (false, Span::dummy()) };
382        // Parse `<variant> IDENT`, where `<variant>` is `function`, `transition`, or `inline`.
383        let (variant, start) = match self.token.token.clone() {
384            Token::Inline => (Variant::Inline, self.expect(&Token::Inline)?),
385            Token::Script => (Variant::Script, self.expect(&Token::Script)?),
386            Token::Function => {
387                (if is_async { Variant::AsyncFunction } else { Variant::Function }, self.expect(&Token::Function)?)
388            }
389            Token::Transition => (
390                if is_async { Variant::AsyncTransition } else { Variant::Transition },
391                self.expect(&Token::Transition)?,
392            ),
393            _ => self.unexpected("'function', 'transition', or 'inline'")?,
394        };
395        let name = self.expect_identifier()?;
396
397        let const_parameters = if self.eat(&Token::DoubleColon) {
398            self.parse_bracket_comma_list(|p| p.parse_const_parameter().map(Some))?.0
399        } else {
400            vec![]
401        };
402
403        // Parse parameters.
404        let (inputs, ..) = self.parse_paren_comma_list(|p| p.parse_input().map(Some))?;
405
406        // Parse return type.
407        let output = match self.eat(&Token::Arrow) {
408            false => vec![],
409            true => {
410                self.disallow_struct_construction = true;
411                let output = match self.peek_is_left_par() {
412                    true => self.parse_paren_comma_list(|p| p.parse_output().map(Some))?.0,
413                    false => vec![self.parse_output()?],
414                };
415                self.disallow_struct_construction = false;
416                output
417            }
418        };
419
420        // Parse the function body, which must be a block,
421        // but we also allow no body and a semicolon instead (e.g. `fn foo(a:u8);`).
422        let (_has_empty_block, block) = match &self.token.token {
423            Token::LeftCurly => (false, self.parse_block()?),
424            Token::Semicolon => {
425                let semicolon = self.expect(&Token::Semicolon)?;
426                // TODO: make a distinction between empty block and no block (i.e. semicolon)
427                (true, Block { statements: Vec::new(), span: semicolon, id: self.node_builder.next_id() })
428            }
429            _ => self.unexpected("block or semicolon")?,
430        };
431
432        let span = if start_async == Span::dummy() { start + block.span } else { start_async + block.span };
433
434        Ok((
435            name.name,
436            Function::new(
437                annotations,
438                variant,
439                name,
440                const_parameters,
441                inputs,
442                output,
443                block,
444                span,
445                self.node_builder.next_id(),
446            ),
447        ))
448    }
449}