#![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();
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";
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)
}
#[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",
(
RootType::parser,
preceded(space0, params()),
opt(preceded(space0, alt(("internal", "external", "private", "public")))),
opt(preceded(space0, alt(("pure", "view", "payable")))),
move |i: &mut _| {
if OUT {
preceded((space0, opt(":"), opt("returns"), space0), opt(params()))
.parse_next(i)
.map(Option::unwrap_or_default)
} else {
Ok(vec![])
}
},
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)
}