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 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 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#[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 RootType::parser,
131 preceded(space0, params()),
133 opt(preceded(space0, alt(("internal", "external", "private", "public")))),
135 opt(preceded(space0, alt(("pure", "view", "payable")))),
137 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 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}