sway_parse/
parse.rs

1use crate::keywords::RESERVED_KEYWORDS;
2use crate::{ParseResult, Parser, ParserConsumed, Peeker};
3
4use sway_ast::keywords::{Keyword, PanicToken};
5use sway_ast::Intrinsic;
6use sway_error::parser_error::ParseErrorKind;
7use sway_types::{ast::Delimiter, Ident, Spanned};
8
9pub trait Parse {
10    const FALLBACK_ERROR: ParseErrorKind = ParseErrorKind::InvalidItem;
11
12    fn parse(parser: &mut Parser) -> ParseResult<Self>
13    where
14        Self: Sized;
15
16    fn error(
17        #[allow(clippy::boxed_local)] _spans: Box<[sway_types::Span]>,
18        _error: sway_error::handler::ErrorEmitted,
19    ) -> Option<Self>
20    where
21        Self: Sized,
22    {
23        None
24    }
25}
26
27pub trait Peek {
28    fn peek(peeker: Peeker<'_>) -> Option<Self>
29    where
30        Self: Sized;
31}
32
33pub trait ParseToEnd {
34    fn parse_to_end<'a>(parser: Parser<'a, '_>) -> ParseResult<(Self, ParserConsumed<'a>)>
35    where
36        Self: Sized;
37}
38
39impl<T> Parse for Box<T>
40where
41    T: Parse,
42{
43    fn parse(parser: &mut Parser) -> ParseResult<Box<T>> {
44        let value = parser.parse()?;
45        Ok(Box::new(value))
46    }
47}
48
49macro_rules! impl_tuple (
50    ($($name:ident,)*) => {
51        impl<$($name,)*> Parse for ($($name,)*)
52        where
53            $($name: Parse,)*
54        {
55            #[allow(unused)]
56            fn parse(parser: &mut Parser) -> ParseResult<($($name,)*)> {
57                $(
58                    #[allow(non_snake_case)]
59                    let $name = parser.parse()?;
60                )*
61                Ok(($($name,)*))
62            }
63        }
64
65        impl<$($name,)*> Peek for ($($name,)*)
66        where
67            $($name: Peek,)*
68        {
69            fn peek(peeker: Peeker<'_>) -> Option<Self> {
70                #![allow(unused_assignments, unused, non_snake_case)]
71
72                let mut tokens = peeker.token_trees;
73                $(
74                    let ($name, fewer_tokens) = Peeker::with::<$name>(tokens)?;
75                    tokens = fewer_tokens;
76
77                )*
78                Some(($($name,)*))
79            }
80        }
81    };
82);
83
84impl_tuple!();
85impl_tuple!(T0,);
86impl_tuple!(T0, T1,);
87impl_tuple!(T0, T1, T2,);
88impl_tuple!(T0, T1, T2, T3,);
89impl_tuple!(T0, T1, T2, T3, T4,);
90impl_tuple!(T0, T1, T2, T3, T4, T5,);
91impl_tuple!(T0, T1, T2, T3, T4, T5, T6,);
92impl_tuple!(T0, T1, T2, T3, T4, T5, T6, T7,);
93impl_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8,);
94impl_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9,);
95impl_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10,);
96impl_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,);
97
98impl<T> ParseToEnd for Vec<T>
99where
100    T: Parse,
101{
102    fn parse_to_end<'a, 'e>(
103        mut parser: Parser<'a, '_>,
104    ) -> ParseResult<(Vec<T>, ParserConsumed<'a>)> {
105        let mut ret = Vec::new();
106        loop {
107            if let Some(consumed) = parser.check_empty() {
108                return Ok((ret, consumed));
109            }
110
111            match parser.parse_with_recovery() {
112                Ok(value) => ret.push(value),
113                Err(r) => {
114                    let (spans, error) =
115                        r.recover_at_next_line_with_fallback_error(T::FALLBACK_ERROR);
116                    if let Some(error) = T::error(spans, error) {
117                        ret.push(error);
118                    } else {
119                        Err(error)?
120                    }
121                }
122            }
123        }
124    }
125}
126
127impl Peek for Ident {
128    fn peek(peeker: Peeker<'_>) -> Option<Ident> {
129        peeker.peek_ident().ok().cloned()
130    }
131}
132
133impl Parse for Ident {
134    fn parse(parser: &mut Parser) -> ParseResult<Ident> {
135        match parser.take::<Ident>() {
136            Some(ident) => {
137                let ident_str = ident.as_str();
138
139                if parser.check_double_underscore
140                    && (ident_str.starts_with("__") && Intrinsic::try_from_str(ident_str).is_none())
141                {
142                    return Err(parser.emit_error_with_span(
143                        ParseErrorKind::InvalidDoubleUnderscore,
144                        ident.span(),
145                    ));
146                }
147
148                if !ident.is_raw_ident()
149                    && (RESERVED_KEYWORDS.contains(ident_str)
150                        || (parser.experimental.error_type && ident_str == PanicToken::AS_STR))
151                {
152                    return Err(parser.emit_error_with_span(
153                        ParseErrorKind::ReservedKeywordIdentifier,
154                        ident.span(),
155                    ));
156                }
157
158                Ok(ident)
159            }
160            None => Err(parser.emit_error(ParseErrorKind::ExpectedIdent)),
161        }
162    }
163}
164
165impl Peek for Delimiter {
166    fn peek(peeker: Peeker<'_>) -> Option<Delimiter> {
167        peeker.peek_delimiter().ok()
168    }
169}