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
160
#![allow(missing_docs)]

use crate::{
    ident::identifier_parser, input::check_recursion, new_input, Error, Input, ParameterSpecifier,
    Result, RootType, StateMutability,
};
use alloc::{string::String, vec::Vec};
use core::{slice, str};
use winnow::{
    ascii::space0,
    combinator::{alt, cut_err, opt, preceded, separated, terminated, trace},
    error::{ContextError, StrContext, StrContextValue},
    stream::{Accumulate, AsChar, Stream},
    PResult, Parser,
};

pub use crate::ident::identifier;

#[inline]
pub fn spanned<'a, O, E>(
    mut f: impl Parser<Input<'a>, O, E>,
) -> impl Parser<Input<'a>, (&'a str, O), E> {
    trace("spanned", move |input: &mut Input<'a>| {
        let start = input.as_ptr();

        let mut len = input.len();
        let r = f.parse_next(input)?;
        len -= input.len();

        // SAFETY: str invariant
        unsafe {
            let span = slice::from_raw_parts(start, len);
            debug_assert!(str::from_utf8(span).is_ok());
            Ok((str::from_utf8_unchecked(span), r))
        }
    })
}

#[inline]
pub fn char_parser<'a>(c: char) -> impl Parser<Input<'a>, char, ContextError> {
    #[cfg(feature = "debug")]
    let name = format!("char={c:?}");
    #[cfg(not(feature = "debug"))]
    let name = "char";
    trace(name, c.context(StrContext::Expected(StrContextValue::CharLiteral(c))))
}

#[inline]
pub fn str_parser<'a>(s: &'static str) -> impl Parser<Input<'a>, &'a str, ContextError> {
    #[cfg(feature = "debug")]
    let name = format!("str={s:?}");
    #[cfg(not(feature = "debug"))]
    let name = "str";
    trace(name, s.context(StrContext::Expected(StrContextValue::StringLiteral(s))))
}

pub fn tuple_parser<'a, O1, O2: Accumulate<O1>>(
    f: impl Parser<Input<'a>, O1, ContextError>,
) -> impl Parser<Input<'a>, O2, ContextError> {
    list_parser('(', ',', ')', f)
}

pub fn array_parser<'a, O1, O2: Accumulate<O1>>(
    f: impl Parser<Input<'a>, O1, ContextError>,
) -> impl Parser<Input<'a>, O2, ContextError> {
    list_parser('[', ',', ']', f)
}

#[inline]
fn list_parser<'i, O1, O2>(
    open: char,
    delim: char,
    close: char,
    f: impl Parser<Input<'i>, O1, ContextError>,
) -> impl Parser<Input<'i>, O2, ContextError>
where
    O2: Accumulate<O1>,
{
    #[cfg(feature = "debug")]
    let name = format!("list({open:?}, {delim:?}, {close:?})");
    #[cfg(not(feature = "debug"))]
    let name = "list";

    // These have to be outside of the closure for some reason.
    let f = check_recursion(f);
    let elems_1 = separated(1.., f, (char_parser(delim), space0));
    let mut elems_and_end = terminated(elems_1, (opt(delim), space0, cut_err(char_parser(close))));
    trace(name, move |input: &mut Input<'i>| {
        let _ = char_parser(open).parse_next(input)?;
        let _ = space0(input)?;
        if input.starts_with(close) {
            input.next_slice(close.len());
            return Ok(O2::initial(Some(0)));
        }
        elems_and_end.parse_next(input)
    })
}

pub fn opt_ws_ident<'a>(input: &mut Input<'a>) -> PResult<Option<&'a str>> {
    preceded(space0, opt(identifier_parser)).parse_next(input)
}

// Not public API.
#[doc(hidden)]
#[inline]
pub fn parse_item_keyword<'a>(s: &mut &'a str) -> Result<&'a str> {
    trace("item", terminated(identifier, space0)).parse_next(s).map_err(Error::parser)
}

#[doc(hidden)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ParsedSignature<Param> {
    pub name: String,
    pub inputs: Vec<Param>,
    pub outputs: Vec<Param>,
    pub anonymous: bool,
    pub state_mutability: Option<StateMutability>,
}

#[doc(hidden)]
pub fn parse_signature<'a, const OUT: bool, F: Fn(ParameterSpecifier<'a>) -> T, T>(
    s: &'a str,
    f: F,
) -> Result<ParsedSignature<T>> {
    let params = || tuple_parser(ParameterSpecifier::parser.map(&f));
    trace(
        "signature",
        (
            // name
            RootType::parser,
            // inputs
            preceded(space0, params()),
            // visibility
            opt(preceded(space0, alt(("internal", "external", "private", "public")))),
            // state mutability
            opt(preceded(space0, alt(("pure", "view", "payable")))),
            // outputs
            move |i: &mut _| {
                if OUT {
                    preceded((space0, opt(":"), opt("returns"), space0), opt(params()))
                        .parse_next(i)
                        .map(Option::unwrap_or_default)
                } else {
                    Ok(vec![])
                }
            },
            // anonymous
            preceded(space0, opt("anonymous").map(|x| x.is_some())),
        ),
    )
    .map(|(name, inputs, _visibility, mutability, outputs, anonymous)| ParsedSignature {
        name: name.span().into(),
        inputs,
        outputs,
        anonymous,
        state_mutability: mutability.map(|s| s.parse().unwrap()),
    })
    .parse(new_input(s))
    .map_err(Error::parser)
}