leo_parser/parser/
type_.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};
20
21pub(super) const TYPE_TOKENS: &[Token] = &[
22    Token::Address,
23    Token::Bool,
24    Token::Field,
25    Token::Future,
26    Token::Group,
27    Token::Scalar,
28    Token::Signature,
29    Token::String,
30    Token::I8,
31    Token::I16,
32    Token::I32,
33    Token::I64,
34    Token::I128,
35    Token::U8,
36    Token::U16,
37    Token::U32,
38    Token::U64,
39    Token::U128,
40];
41
42impl ParserContext<'_> {
43    /// Returns a [`IntegerType`] AST node if the given token is a supported integer type, or [`None`].
44    pub(super) fn token_to_int_type(token: &Token) -> Option<IntegerType> {
45        Some(match token {
46            Token::I8 => IntegerType::I8,
47            Token::I16 => IntegerType::I16,
48            Token::I32 => IntegerType::I32,
49            Token::I64 => IntegerType::I64,
50            Token::I128 => IntegerType::I128,
51            Token::U8 => IntegerType::U8,
52            Token::U16 => IntegerType::U16,
53            Token::U32 => IntegerType::U32,
54            Token::U64 => IntegerType::U64,
55            Token::U128 => IntegerType::U128,
56            _ => return None,
57        })
58    }
59
60    /// Returns a [`(Type, Span)`] tuple of AST nodes if the next token represents a primitive type.
61    /// Also returns the span of the parsed token.
62    ///
63    /// These correspond to what the ABNF grammar calls 'named primitive types';
64    /// the 'primitive types' according to the ABNF grammar include also the unit type.
65    pub fn parse_primitive_type(&mut self) -> Result<(Type, Span)> {
66        let span = self.expect_any(TYPE_TOKENS)?;
67        Ok((
68            match &self.prev_token.token {
69                Token::Address => Type::Address,
70                Token::Bool => Type::Boolean,
71                Token::Field => Type::Field,
72                Token::Group => Type::Group,
73                Token::Scalar => Type::Scalar,
74                Token::Signature => Type::Signature,
75                Token::String => Type::String,
76                x => Type::Integer(Self::token_to_int_type(x).expect("invalid int type")),
77            },
78            span,
79        ))
80    }
81
82    /// Returns a [`(Type, Span)`] tuple of AST nodes if the next token represents a type.
83    /// Also returns the span of the parsed token.
84    pub fn parse_type(&mut self) -> Result<(Type, Span)> {
85        if let Some(ident) = self.eat_identifier() {
86            // Check if using external type
87            let file_type = self.look_ahead(1, |t| &t.token);
88            if self.token.token == Token::Dot && (file_type == &Token::Aleo) {
89                // Only allow `.aleo` as the network identifier
90                if file_type == &Token::Leo {
91                    return Err(ParserError::invalid_network(self.token.span).into());
92                }
93
94                // Parse `.aleo/`
95                self.expect(&Token::Dot)?;
96                self.expect(&Token::Aleo)?;
97                self.expect(&Token::Div)?;
98
99                // Parse the record name
100                if let Some(record_name) = self.eat_identifier() {
101                    // Return the external type
102                    return Ok((
103                        Type::Composite(CompositeType {
104                            path: record_name.into(),
105                            const_arguments: Vec::new(), // For now, external composite types can't have const generics
106                            program: Some(ident.name),
107                        }),
108                        ident.span + record_name.span,
109                    ));
110                } else {
111                    return Err(ParserError::invalid_external_type(self.token.span).into());
112                }
113            }
114
115            // Begin collecting the rest of the path, starting from `ident`
116            let mut path_span = ident.span;
117            let mut segments = vec![ident];
118
119            // Parse `::`-separated path segments
120            while self.check(&Token::DoubleColon) {
121                // Look ahead without consuming `::` to detect const generics
122                if self.look_ahead(1, |next| matches!(next.token, Token::LeftSquare)) {
123                    break;
124                }
125
126                self.bump(); // consume `::`
127
128                let next_ident = self.expect_identifier()?;
129                path_span = path_span + next_ident.span;
130                segments.push(next_ident);
131            }
132
133            // Parse optional const generic arguments `::[...]`
134            let const_arguments = if self.eat(&Token::DoubleColon) {
135                self.parse_bracket_comma_list(|p| p.parse_expression().map(Some))?.0
136            } else {
137                Vec::new()
138            };
139
140            let (identifier, qualifier) = segments.split_last().expect("guaranateed to have at least one segment");
141            Ok((
142                Type::Composite(CompositeType {
143                    path: Path::new(qualifier.to_vec(), *identifier, None, path_span, self.node_builder.next_id()),
144                    const_arguments,
145                    program: None,
146                }),
147                path_span,
148            ))
149        } else if self.token.token == Token::LeftSquare {
150            // Parse the left bracket.
151            self.expect(&Token::LeftSquare)?;
152            // Parse the element type.
153            let (element_type, _) = self.parse_type()?;
154            // Parse the semi-colon.
155            self.expect(&Token::Semicolon)?;
156            // Parse the length as an expression.
157            let length = self.parse_expression()?;
158            // Parse the right bracket.
159            self.expect(&Token::RightSquare)?;
160            // Return the array type.
161            Ok((Type::Array(ArrayType::new(element_type, length)), self.prev_token.span))
162        } else if self.token.token == Token::LeftParen {
163            let (types, _, span) = self.parse_paren_comma_list(|p| p.parse_type().map(Some))?;
164            match types.len() {
165                // If the parenthetical block is empty, e.g. `()` or `( )`, it should be parsed into `Unit` types.
166                0 => Ok((Type::Unit, span)),
167                // If the parenthetical block contains a single type, e.g. `(u8)`, emit an error, since tuples must have at least two elements.
168                1 => Err(ParserError::tuple_must_have_at_least_two_elements("type", span).into()),
169                // Otherwise, parse it into a `Tuple` type.
170                // Note: This is the only place where `Tuple` type is constructed in the parser.
171                _ => Ok((Type::Tuple(TupleType::new(types.into_iter().map(|t| t.0).collect())), span)),
172            }
173        } else if self.token.token == Token::Future {
174            // Parse the `Future` token.
175            let span = self.expect(&Token::Future)?;
176            // Parse the explicit future type, e.g. `Future<Fn(u32, u32)>`, `Future<Fn(u32, Future<Fn(u32, u32, u64)>)>` etc.
177            if self.token.token == Token::Lt {
178                // Expect the sequence `<`, `Fn`.
179                self.expect(&Token::Lt)?;
180                self.expect(&Token::Fn)?;
181                // Parse the parenthesized list of function arguments.
182                let (types, _, full_span) = self.parse_paren_comma_list(|p| p.parse_type().map(Some))?;
183                // Expect the closing `>`.
184                self.expect(&Token::Gt)?;
185                Ok((
186                    Type::Future(FutureType::new(types.into_iter().map(|t| t.0).collect(), None, true)),
187                    span + full_span,
188                ))
189            } else {
190                Ok((Type::Future(Default::default()), span))
191            }
192        } else {
193            self.parse_primitive_type()
194        }
195    }
196}