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::*;
18use snarkvm::prelude::Itertools;
19
20use leo_errors::{ParserError, Result, TypeCheckerError};
21use leo_span::{Symbol, sym};
22
23impl ParserContext<'_> {
24    /// Returns a [`Program`] AST if all tokens can be consumed and represent a valid Leo program.
25    pub fn parse_program(&mut self) -> Result<Program> {
26        let mut imports = IndexMap::new();
27        let mut program_scopes = IndexMap::new();
28
29        // TODO: Remove restrictions on multiple program scopes
30        let mut parsed_program_scope = false;
31
32        while self.has_next() {
33            match &self.token.token {
34                Token::Import => {
35                    let (id, import) = self.parse_import()?;
36                    imports.insert(id, import);
37                }
38                Token::Program => {
39                    match parsed_program_scope {
40                        // Only one program scope is allowed per file.
41                        true => {
42                            return Err(ParserError::only_one_program_scope_is_allowed(self.token.span).into());
43                        }
44                        false => {
45                            parsed_program_scope = true;
46                            let program_scope = self.parse_program_scope()?;
47                            program_scopes.insert(program_scope.program_id.name.name, program_scope);
48                        }
49                    }
50                }
51                _ => return Err(Self::unexpected_item(&self.token, &[Token::Import, Token::Program]).into()),
52            }
53        }
54
55        // Requires that at least one program scope is present.
56        if !parsed_program_scope {
57            return Err(ParserError::missing_program_scope(self.token.span).into());
58        }
59
60        Ok(Program { modules: IndexMap::new(), imports, stubs: IndexMap::new(), program_scopes })
61    }
62
63    fn unexpected_item(token: &SpannedToken, expected: &[Token]) -> ParserError {
64        ParserError::unexpected(
65            &token.token,
66            expected.iter().map(|x| format!("'{x}'")).collect::<Vec<_>>().join(", "),
67            token.span,
68        )
69    }
70
71    // TODO: remove import resolution from parser.
72    /// Parses an import statement `import foo.leo;`.
73    pub(super) fn parse_import(&mut self) -> Result<(Symbol, (Program, Span))> {
74        // Parse `import`.
75        let start = self.expect(&Token::Import)?;
76
77        // Parse `foo`.
78        let import_name = self.expect_identifier()?;
79
80        // Parse `.`.
81        self.expect(&Token::Dot)?;
82
83        // Parse network, which currently must be `aleo`.
84        if !self.eat(&Token::Aleo) {
85            // Throw error for non-aleo networks.
86            return Err(ParserError::invalid_network(self.token.span).into());
87        }
88
89        let end = self.expect(&Token::Semicolon)?;
90
91        // Return the import name and the span.
92        Ok((import_name.name, (Program::default(), start + end)))
93    }
94
95    /// Parses a program scope `program foo.aleo { ... }`.
96    fn parse_program_scope(&mut self) -> Result<ProgramScope> {
97        // Parse `program` keyword.
98        let start = self.expect(&Token::Program)?;
99
100        // Parse the program name.
101        let name = self.expect_identifier()?;
102
103        // Set the program name in the context.
104        self.program_name = Some(name.name);
105
106        // Parse the `.`.
107        self.expect(&Token::Dot)?;
108
109        // Parse the program network, which must be `aleo`, otherwise throw parser error.
110        let network_span = self.expect(&Token::Aleo).map_err(|_| ParserError::invalid_network(self.token.span))?;
111
112        // Construct the program id.
113        let program_id = ProgramId {
114            name,
115            network: Identifier { name: sym::aleo, id: self.node_builder.next_id(), span: network_span },
116        };
117
118        // Parse `{`.
119        self.expect(&Token::LeftCurly)?;
120
121        // Check that there are no annotations.
122        if !self.annotations.is_empty() {
123            self.emit_err(ParserError::custom("Unexpected dangling annotation", self.annotations[0].span))
124        }
125
126        // Parse the body of the program scope.
127        let mut consts: Vec<(Symbol, ConstDeclaration)> = Vec::new();
128        let (mut transitions, mut functions) = (Vec::new(), Vec::new());
129        let mut structs: Vec<(Symbol, Composite)> = Vec::new();
130        let mut mappings: Vec<(Symbol, Mapping)> = Vec::new();
131        let mut constructor = None;
132
133        while self.has_next() {
134            match &self.token.token {
135                Token::Const => {
136                    let declaration = self.parse_const_declaration_statement()?;
137                    consts.push((Symbol::intern(&declaration.place.to_string()), declaration));
138                }
139                Token::Struct | Token::Record => {
140                    let (id, struct_) = self.parse_struct()?;
141                    structs.push((id, struct_));
142                }
143                Token::Mapping => {
144                    let (id, mapping) = self.parse_mapping()?;
145                    mappings.push((id, mapping));
146                }
147                Token::At => {
148                    let annotation = self.parse_annotation()?;
149                    self.annotations.push(annotation);
150                }
151                Token::Async if self.look_ahead(1, |t| &t.token) == &Token::Constructor => {
152                    // If a constructor already exists, return an error.
153                    if constructor.is_some() {
154                        return Err(TypeCheckerError::custom(
155                            "A program can only have one constructor.",
156                            self.token.span,
157                        )
158                        .into());
159                    }
160                    let constructor_ = self.parse_constructor()?;
161                    constructor = Some(constructor_);
162                }
163                Token::Async | Token::Function | Token::Transition | Token::Inline | Token::Script => {
164                    let (id, function) = self.parse_function()?;
165
166                    // Partition into transitions and functions so that we don't have to sort later.
167                    if function.variant.is_transition() {
168                        transitions.push((id, function));
169                    } else {
170                        functions.push((id, function));
171                    }
172                }
173                Token::RightCurly => break,
174                _ => {
175                    return Err(Self::unexpected_item(&self.token, &[
176                        Token::Const,
177                        Token::Struct,
178                        Token::Record,
179                        Token::Mapping,
180                        Token::At,
181                        Token::Async,
182                        Token::Function,
183                        Token::Transition,
184                        Token::Inline,
185                        Token::Script,
186                        Token::RightCurly,
187                    ])
188                    .into());
189                }
190            }
191        }
192
193        // Check that there are no dangling annotations.
194        if !self.annotations.is_empty() {
195            self.emit_err(ParserError::custom("Unexpected dangling annotation", self.annotations[0].span))
196        }
197
198        // Parse `}`.
199        let end = self.expect(&Token::RightCurly)?;
200
201        Ok(ProgramScope {
202            program_id,
203            consts,
204            structs,
205            mappings,
206            constructor,
207            functions: [transitions, functions].concat(),
208            span: start + end,
209        })
210    }
211
212    /// Parses a module.
213    pub fn parse_module(&mut self, path: &[Symbol]) -> Result<Module> {
214        // Parse the body of the program scope.
215        let mut consts: Vec<(Symbol, ConstDeclaration)> = Vec::new();
216        let mut functions = Vec::new();
217        let mut structs: Vec<(Symbol, Composite)> = Vec::new();
218
219        while self.has_next() {
220            // Only allow consts, structs, and inlines in modules. This restriction may be relaxed in the future.
221            match &self.token.token {
222                Token::Const => {
223                    let declaration = self.parse_const_declaration_statement()?;
224                    consts.push((Symbol::intern(&declaration.place.to_string()), declaration));
225                }
226                Token::Struct => {
227                    let (id, struct_) = self.parse_struct()?;
228                    structs.push((id, struct_));
229                }
230                Token::Inline => {
231                    let (id, function) = self.parse_function()?;
232
233                    functions.push((id, function));
234                }
235                Token::RightCurly => break,
236                _ => {
237                    return Err(Self::unexpected_item(&self.token, &[
238                        Token::Const,
239                        Token::Struct,
240                        Token::Inline,
241                        Token::RightCurly,
242                    ])
243                    .into());
244                }
245            }
246        }
247
248        Ok(Module {
249            program_name: self.program_name.unwrap_or_default(),
250            path: path.to_owned(),
251            consts,
252            functions,
253            structs,
254        })
255    }
256
257    /// Returns a [`Vec<Member>`] AST node if the next tokens represent a struct member.
258    fn parse_struct_members(&mut self) -> Result<(Vec<Member>, Span)> {
259        let mut members = Vec::new();
260
261        while !self.check(&Token::RightCurly) {
262            let variable = self.parse_member_variable_declaration()?;
263
264            if !self.check(&Token::RightCurly) && !self.eat(&Token::Comma) {
265                self.emit_err(ParserError::comma_expected_after_member(self.token.span));
266            }
267
268            members.push(variable);
269        }
270        let span = self.expect(&Token::RightCurly)?;
271
272        Ok((members, span))
273    }
274
275    /// Parses `IDENT: TYPE`.
276    pub(super) fn parse_typed_ident(&mut self) -> Result<(Identifier, Type, Span)> {
277        let name = self.expect_identifier()?;
278        self.expect(&Token::Colon)?;
279        let (type_, span) = self.parse_type()?;
280
281        Ok((name, type_, name.span + span))
282    }
283
284    /// Returns a [`Member`] AST node if the next tokens represent a struct member variable.
285    fn parse_member_variable_declaration(&mut self) -> Result<Member> {
286        let mode = self.parse_mode()?;
287
288        let (identifier, type_, span) = self.parse_typed_ident()?;
289
290        Ok(Member { mode, identifier, type_, span, id: self.node_builder.next_id() })
291    }
292
293    /// Parses a struct or record definition, e.g., `struct Foo { ... }` or `record Foo { ... }`.
294    pub(super) fn parse_struct(&mut self) -> Result<(Symbol, Composite)> {
295        let is_record = matches!(&self.token.token, Token::Record);
296        let start = self.expect_any(&[Token::Struct, Token::Record])?;
297
298        // Check if using external type
299        let file_type = self.look_ahead(1, |t| &t.token);
300        if self.token.token == Token::Dot && (file_type == &Token::Aleo) {
301            return Err(ParserError::cannot_declare_external_struct(self.token.span).into());
302        }
303
304        let struct_name = self.expect_identifier()?;
305
306        let const_parameters = if self.eat(&Token::DoubleColon) {
307            self.parse_bracket_comma_list(|p| p.parse_const_parameter().map(Some))?.0
308        } else {
309            vec![]
310        };
311
312        self.expect(&Token::LeftCurly)?;
313        let (members, end) = self.parse_struct_members()?;
314
315        // Only provide a program name for records.
316        let external = if is_record { self.program_name } else { None };
317
318        Ok((struct_name.name, Composite {
319            identifier: struct_name,
320            const_parameters,
321            members,
322            external,
323            is_record,
324            span: start + end,
325            id: self.node_builder.next_id(),
326        }))
327    }
328
329    /// Parses a mapping declaration, e.g. `mapping balances: address => u128`.
330    pub(super) fn parse_mapping(&mut self) -> Result<(Symbol, Mapping)> {
331        let start = self.expect(&Token::Mapping)?;
332        let identifier = self.expect_identifier()?;
333        self.expect(&Token::Colon)?;
334        let (key_type, _) = self.parse_type()?;
335        self.expect(&Token::BigArrow)?;
336        let (value_type, _) = self.parse_type()?;
337        let end = self.expect(&Token::Semicolon)?;
338        Ok((identifier.name, Mapping {
339            identifier,
340            key_type,
341            value_type,
342            span: start + end,
343            id: self.node_builder.next_id(),
344        }))
345    }
346
347    // TODO: Return a span associated with the mode.
348    /// Returns a [`ParamMode`] AST node if the next tokens represent a function parameter mode.
349    pub(super) fn parse_mode(&mut self) -> Result<Mode> {
350        let private = self.eat(&Token::Private).then_some(self.prev_token.span);
351        let public = self.eat(&Token::Public).then_some(self.prev_token.span);
352        let constant = self.eat(&Token::Constant).then_some(self.prev_token.span);
353
354        match (private, public, constant) {
355            (None, None, None) => Ok(Mode::None),
356            (Some(_), None, None) => Ok(Mode::Private),
357            (None, Some(_), None) => Ok(Mode::Public),
358            (None, None, Some(_)) => Ok(Mode::Constant),
359            _ => {
360                let mut spans = [private, public, constant].into_iter().flatten();
361
362                // There must exist at least one mode, since the none case is handled above.
363                let starting_span = spans.next().unwrap();
364                // Sum the spans.
365                let summed_span = spans.fold(starting_span, |span, next| span + next);
366                // Emit an error.
367                Err(ParserError::inputs_multiple_variable_modes_specified(summed_span).into())
368            }
369        }
370    }
371
372    /// Returns an [`Input`] AST node if the next tokens represent a function input.
373    fn parse_input(&mut self) -> Result<Input> {
374        let mode = self.parse_mode()?;
375        let name = self.expect_identifier()?;
376        self.expect(&Token::Colon)?;
377
378        let (type_, type_span) = self.parse_type()?;
379        let span = name.span() + type_span;
380
381        Ok(Input { identifier: name, mode, type_, span, id: self.node_builder.next_id() })
382    }
383
384    /// Returns an [`Output`] AST node if the next tokens represent a function output.
385    fn parse_output(&mut self) -> Result<Output> {
386        let mode = self.parse_mode()?;
387        let (type_, span) = self.parse_type()?;
388        Ok(Output { mode, type_, span, id: self.node_builder.next_id() })
389    }
390
391    /// Returns an [`Annotation`] AST node if the next tokens represent an annotation.
392    fn parse_annotation(&mut self) -> Result<Annotation> {
393        // Parse the `@` symbol and identifier.
394        let start = self.expect(&Token::At)?;
395        let identifier = match self.token.token {
396            Token::Program => {
397                Identifier { name: sym::program, span: self.expect(&Token::Program)?, id: self.node_builder.next_id() }
398            }
399            _ => self.expect_identifier()?,
400        };
401        let mut span = start + identifier.span;
402
403        // TODO: Verify that this check is sound.
404        // Check that there is no whitespace or comments in between the `@` symbol and identifier.
405        if identifier.span.hi - start.lo > 1 + identifier.name.to_string().len() as u32 {
406            return Err(ParserError::space_in_annotation(span).into());
407        }
408
409        let mut map = IndexMap::new();
410
411        if self.eat(&Token::LeftParen) {
412            loop {
413                let key = match self.token.token {
414                    Token::Mapping => Identifier {
415                        name: sym::mapping,
416                        span: self.expect(&Token::Mapping)?,
417                        id: self.node_builder.next_id(),
418                    },
419                    Token::Address => Identifier {
420                        name: sym::address,
421                        span: self.expect(&Token::Address)?,
422                        id: self.node_builder.next_id(),
423                    },
424                    _ => self.expect_identifier()?,
425                };
426                self.expect(&Token::Assign)?;
427                if let Token::StaticString(s) = &self.token.token {
428                    map.insert(key.name, s.clone());
429                    self.bump();
430                } else {
431                    return Err(ParserError::unexpected(&self.token.token, "a string", self.token.span).into());
432                }
433
434                match &self.token.token {
435                    Token::Comma => {
436                        self.bump();
437                        if let Token::RightParen = &self.token.token {
438                            span = span + self.token.span;
439                            self.bump();
440                            break;
441                        }
442                    }
443                    Token::RightParen => {
444                        span = span + self.token.span;
445                        self.bump();
446                        break;
447                    }
448                    _ => return Err(ParserError::unexpected(&self.token.token, ", or )", self.token.span).into()),
449                }
450            }
451        }
452
453        Ok(Annotation { identifier, span, id: self.node_builder.next_id(), map })
454    }
455
456    /// Returns an [`(Identifier, Function)`] AST node if the next tokens represent a function name
457    /// and function definition.
458    fn parse_function(&mut self) -> Result<(Symbol, Function)> {
459        // Get any accumulated annotations.
460        let annotations = self.annotations.drain(..).collect();
461        // Parse a potential async signifier.
462        let (is_async, start_async) =
463            if self.token.token == Token::Async { (true, self.expect(&Token::Async)?) } else { (false, Span::dummy()) };
464        // Parse `<variant> IDENT`, where `<variant>` is `function`, `transition`, or `inline`.
465        let (variant, start) = match self.token.token.clone() {
466            Token::Inline => (Variant::Inline, self.expect(&Token::Inline)?),
467            Token::Script => (Variant::Script, self.expect(&Token::Script)?),
468            Token::Function => {
469                (if is_async { Variant::AsyncFunction } else { Variant::Function }, self.expect(&Token::Function)?)
470            }
471            Token::Transition => (
472                if is_async { Variant::AsyncTransition } else { Variant::Transition },
473                self.expect(&Token::Transition)?,
474            ),
475            _ => self.unexpected("'function', 'transition', or 'inline'")?,
476        };
477        let name = self.expect_identifier()?;
478
479        let const_parameters = if self.eat(&Token::DoubleColon) {
480            self.parse_bracket_comma_list(|p| p.parse_const_parameter().map(Some))?.0
481        } else {
482            vec![]
483        };
484
485        // Parse parameters.
486        let (inputs, ..) = self.parse_paren_comma_list(|p| p.parse_input().map(Some))?;
487
488        // Parse return type.
489        let output = match self.eat(&Token::Arrow) {
490            false => vec![],
491            true => {
492                self.disallow_struct_construction = true;
493                let output = match self.peek_is_left_par() {
494                    true => self.parse_paren_comma_list(|p| p.parse_output().map(Some))?.0,
495                    false => vec![self.parse_output()?],
496                };
497                self.disallow_struct_construction = false;
498                output
499            }
500        };
501
502        // Parse the function body, which must be a block,
503        // but we also allow no body and a semicolon instead (e.g. `fn foo(a:u8);`).
504        let (_has_empty_block, block) = match &self.token.token {
505            Token::LeftCurly => (false, self.parse_block()?),
506            Token::Semicolon => {
507                let semicolon = self.expect(&Token::Semicolon)?;
508                // TODO: make a distinction between empty block and no block (i.e. semicolon)
509                (true, Block { statements: Vec::new(), span: semicolon, id: self.node_builder.next_id() })
510            }
511            _ => self.unexpected("block or semicolon")?,
512        };
513
514        let span = if start_async == Span::dummy() { start + block.span } else { start_async + block.span };
515
516        Ok((
517            name.name,
518            Function::new(
519                annotations,
520                variant,
521                name,
522                const_parameters,
523                inputs,
524                output,
525                block,
526                span,
527                self.node_builder.next_id(),
528            ),
529        ))
530    }
531
532    /// Returns a `Constructor` AST node if the next tokens represent a constructor.
533    pub(crate) fn parse_constructor(&mut self) -> Result<Constructor> {
534        // Get any accumulated annotations.
535        let annotations = self.annotations.drain(..).collect_vec();
536        // Parse the `async` keyword.
537        let start = self.expect(&Token::Async)?;
538        // Parse the `constructor` keyword.
539        let _ = self.expect(&Token::Constructor)?;
540        // Parse the opening parenthesis.
541        let _ = self.expect(&Token::LeftParen)?;
542        // Parse the closing parenthesis.
543        let _ = self.expect(&Token::RightParen)?;
544        // Parse the constructor body, which must be a block.
545        let block = self.parse_block()?;
546
547        // Correctly handle the starting span.
548        let start = match annotations.is_empty() {
549            true => start,
550            false => annotations[0].span,
551        };
552
553        Ok(Constructor { span: start + block.span, annotations, block, id: self.node_builder.next_id() })
554    }
555}