leo_parser/parser/type_.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
// Copyright (C) 2019-2025 Provable Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use super::*;
use leo_errors::{ParserError, Result};
pub(super) const TYPE_TOKENS: &[Token] = &[
Token::Address,
Token::Bool,
Token::Field,
Token::Future,
Token::Group,
Token::Scalar,
Token::Signature,
Token::String,
Token::I8,
Token::I16,
Token::I32,
Token::I64,
Token::I128,
Token::U8,
Token::U16,
Token::U32,
Token::U64,
Token::U128,
];
impl<N: Network> ParserContext<'_, N> {
/// Returns a [`IntegerType`] AST node if the given token is a supported integer type, or [`None`].
pub(super) fn token_to_int_type(token: &Token) -> Option<IntegerType> {
Some(match token {
Token::I8 => IntegerType::I8,
Token::I16 => IntegerType::I16,
Token::I32 => IntegerType::I32,
Token::I64 => IntegerType::I64,
Token::I128 => IntegerType::I128,
Token::U8 => IntegerType::U8,
Token::U16 => IntegerType::U16,
Token::U32 => IntegerType::U32,
Token::U64 => IntegerType::U64,
Token::U128 => IntegerType::U128,
_ => return None,
})
}
/// Returns a [`(Type, Span)`] tuple of AST nodes if the next token represents a primitive type.
/// Also returns the span of the parsed token.
///
/// These correspond to what the ABNF grammar calls 'named primitive types';
/// the 'primitive types' according to the ABNF grammar include also the unit type.
pub fn parse_primitive_type(&mut self) -> Result<(Type, Span)> {
let span = self.expect_any(TYPE_TOKENS)?;
Ok((
match &self.prev_token.token {
Token::Address => Type::Address,
Token::Bool => Type::Boolean,
Token::Field => Type::Field,
Token::Group => Type::Group,
Token::Scalar => Type::Scalar,
Token::Signature => Type::Signature,
Token::String => Type::String,
x => Type::Integer(Self::token_to_int_type(x).expect("invalid int type")),
},
span,
))
}
/// Returns a [`(Type, Span)`] tuple of AST nodes if the next token represents a type.
/// Also returns the span of the parsed token.
pub fn parse_type(&mut self) -> Result<(Type, Span)> {
if let Some(ident) = self.eat_identifier() {
// Check if using external type
let file_type = self.look_ahead(1, |t| &t.token);
if self.token.token == Token::Dot && (file_type == &Token::Aleo) {
// Only allow `.aleo` as the network identifier
if file_type == &Token::Leo {
return Err(ParserError::invalid_network(self.token.span).into());
}
// Parse `.aleo/`
self.expect(&Token::Dot)?;
self.expect(&Token::Aleo)?;
self.expect(&Token::Div)?;
// Parse the record name
if let Some(record_name) = self.eat_identifier() {
// Return the external type
return Ok((
Type::Composite(CompositeType { id: record_name, program: Some(ident.name) }),
ident.span + record_name.span,
));
} else {
return Err(ParserError::invalid_external_type(self.token.span).into());
}
}
Ok((Type::Composite(CompositeType { id: ident, program: None }), ident.span))
} else if self.token.token == Token::LeftSquare {
// Parse the left bracket.
self.expect(&Token::LeftSquare)?;
// Parse the element type.
let (element_type, _) = self.parse_type()?;
// Parse the semi-colon.
self.expect(&Token::Semicolon)?;
// Parse the length.
let (length, _) = self.eat_whole_number()?;
// Parse the right bracket.
self.expect(&Token::RightSquare)?;
// Return the array type.
Ok((Type::Array(ArrayType::new(element_type, length)), self.prev_token.span))
} else if self.token.token == Token::LeftParen {
let (types, _, span) = self.parse_paren_comma_list(|p| p.parse_type().map(Some))?;
match types.len() {
// If the parenthetical block is empty, e.g. `()` or `( )`, it should be parsed into `Unit` types.
0 => Ok((Type::Unit, span)),
// If the parenthetical block contains a single type, e.g. `(u8)`, emit an error, since tuples must have at least two elements.
1 => Err(ParserError::tuple_must_have_at_least_two_elements("type", span).into()),
// Otherwise, parse it into a `Tuple` type.
// Note: This is the only place where `Tuple` type is constructed in the parser.
_ => Ok((Type::Tuple(TupleType::new(types.into_iter().map(|t| t.0).collect())), span)),
}
} else if self.token.token == Token::Future {
// Parse the `Future` token.
let span = self.expect(&Token::Future)?;
// Parse the explicit future type, e.g. `Future<Fn(u32, u32)>`, `Future<Fn(u32, Future<Fn(u32, u32, u64)>)>` etc.
if self.token.token == Token::Lt {
// Expect the sequence `<`, `Fn`.
self.expect(&Token::Lt)?;
self.expect(&Token::Fn)?;
// Parse the parenthesized list of function arguments.
let (types, _, full_span) = self.parse_paren_comma_list(|p| p.parse_type().map(Some))?;
// Expect the closing `>`.
self.expect(&Token::Gt)?;
Ok((
Type::Future(FutureType::new(types.into_iter().map(|t| t.0).collect(), None, true)),
span + full_span,
))
} else {
Ok((Type::Future(Default::default()), span))
}
} else {
self.parse_primitive_type()
}
}
}