alloy_sol_type_parser/
utils.rs

1#![allow(missing_docs)]
2
3use crate::{
4    ident::identifier_parser, input::check_recursion, new_input, Error, Input, ParameterSpecifier,
5    Result, RootType, StateMutability,
6};
7use alloc::{string::String, vec::Vec};
8use core::{slice, str};
9use winnow::{
10    ascii::space0,
11    combinator::{alt, cut_err, opt, preceded, separated, terminated, trace},
12    error::{ContextError, ParserError, StrContext, StrContextValue},
13    stream::{Accumulate, AsChar, Stream},
14    ModalParser, ModalResult, Parser,
15};
16
17pub use crate::ident::identifier;
18
19#[inline]
20pub fn spanned<'a, O, E: ParserError<Input<'a>>>(
21    mut f: impl ModalParser<Input<'a>, O, E>,
22) -> impl ModalParser<Input<'a>, (&'a str, O), E> {
23    trace("spanned", move |input: &mut Input<'a>| {
24        let start = input.as_ptr();
25
26        let mut len = input.len();
27        let r = f.parse_next(input)?;
28        len -= input.len();
29
30        // SAFETY: str invariant
31        unsafe {
32            let span = slice::from_raw_parts(start, len);
33            debug_assert!(str::from_utf8(span).is_ok());
34            Ok((str::from_utf8_unchecked(span), r))
35        }
36    })
37}
38
39#[inline]
40pub fn char_parser<'a>(c: char) -> impl ModalParser<Input<'a>, char, ContextError> {
41    #[cfg(feature = "debug")]
42    let name = format!("char={c:?}");
43    #[cfg(not(feature = "debug"))]
44    let name = "char";
45    trace(name, c.context(StrContext::Expected(StrContextValue::CharLiteral(c))))
46}
47
48#[inline]
49pub fn str_parser<'a>(s: &'static str) -> impl ModalParser<Input<'a>, &'a str, ContextError> {
50    #[cfg(feature = "debug")]
51    let name = format!("str={s:?}");
52    #[cfg(not(feature = "debug"))]
53    let name = "str";
54    trace(name, s.context(StrContext::Expected(StrContextValue::StringLiteral(s))))
55}
56
57pub fn tuple_parser<'a, O1, O2: Accumulate<O1>>(
58    f: impl ModalParser<Input<'a>, O1, ContextError>,
59) -> impl ModalParser<Input<'a>, O2, ContextError> {
60    list_parser('(', ',', ')', f)
61}
62
63pub fn array_parser<'a, O1, O2: Accumulate<O1>>(
64    f: impl ModalParser<Input<'a>, O1, ContextError>,
65) -> impl ModalParser<Input<'a>, O2, ContextError> {
66    list_parser('[', ',', ']', f)
67}
68
69#[inline]
70fn list_parser<'i, O1, O2>(
71    open: char,
72    delim: char,
73    close: char,
74    f: impl ModalParser<Input<'i>, O1, ContextError>,
75) -> impl ModalParser<Input<'i>, O2, ContextError>
76where
77    O2: Accumulate<O1>,
78{
79    #[cfg(feature = "debug")]
80    let name = format!("list({open:?}, {delim:?}, {close:?})");
81    #[cfg(not(feature = "debug"))]
82    let name = "list";
83
84    // These have to be outside of the closure for some reason.
85    let f = check_recursion(f);
86    let elems_1 = separated(1.., f, (char_parser(delim), space0));
87    let mut elems_and_end = terminated(elems_1, (opt(delim), space0, cut_err(char_parser(close))));
88    trace(name, move |input: &mut Input<'i>| {
89        let _ = char_parser(open).parse_next(input)?;
90        let _ = space0(input)?;
91        if input.starts_with(close) {
92            input.next_slice(close.len());
93            return Ok(O2::initial(Some(0)));
94        }
95        elems_and_end.parse_next(input)
96    })
97}
98
99pub fn opt_ws_ident<'a>(input: &mut Input<'a>) -> ModalResult<Option<&'a str>> {
100    preceded(space0, opt(identifier_parser)).parse_next(input)
101}
102
103// Not public API.
104#[doc(hidden)]
105#[inline]
106pub fn parse_item_keyword<'a>(s: &mut &'a str) -> Result<&'a str> {
107    trace("item", terminated(identifier, space0)).parse_next(s).map_err(Error::parser)
108}
109
110#[doc(hidden)]
111#[derive(Clone, Debug, PartialEq, Eq)]
112pub struct ParsedSignature<Param> {
113    pub name: String,
114    pub inputs: Vec<Param>,
115    pub outputs: Vec<Param>,
116    pub anonymous: bool,
117    pub state_mutability: Option<StateMutability>,
118}
119
120#[doc(hidden)]
121pub fn parse_signature<'a, const OUT: bool, F: Fn(ParameterSpecifier<'a>) -> T, T>(
122    s: &'a str,
123    f: F,
124) -> Result<ParsedSignature<T>> {
125    let params = || tuple_parser(ParameterSpecifier::parser.map(&f));
126    trace(
127        "signature",
128        (
129            // name
130            RootType::parser,
131            // inputs
132            preceded(space0, params()),
133            // visibility
134            opt(preceded(space0, alt(("internal", "external", "private", "public")))),
135            // state mutability
136            opt(preceded(space0, alt(("pure", "view", "payable")))),
137            // outputs
138            move |i: &mut _| {
139                if OUT {
140                    preceded((space0, opt(":"), opt("returns"), space0), opt(params()))
141                        .parse_next(i)
142                        .map(Option::unwrap_or_default)
143                } else {
144                    Ok(vec![])
145                }
146            },
147            // anonymous
148            preceded(space0, opt("anonymous").map(|x| x.is_some())),
149        ),
150    )
151    .map(|(name, inputs, _visibility, mutability, outputs, anonymous)| ParsedSignature {
152        name: name.span().into(),
153        inputs,
154        outputs,
155        anonymous,
156        state_mutability: mutability.map(|s| s.parse().unwrap()),
157    })
158    .parse(new_input(s))
159    .map_err(Error::parser)
160}