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-2024 Aleo Systems 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: self.program_name }), 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()
        }
    }
}