blob: e4ada4b416c0f8b8e9515c2f8b383c722cec8493 [file] [log] [blame]
Michael Layzell5bde96f2017-01-24 17:59:21 -05001use IResult;
David Tolnaydef66372016-10-24 21:51:32 -07002use space::{skip_whitespace, word_break};
David Tolnayb79ee962016-09-04 09:39:20 -07003
Michael Layzell24645a32017-02-04 13:19:26 -05004/// Parse a piece of punctuation, skipping whitespace before it.
5///
6/// - **Syntax:** `punct!("...")`
7/// - **Output:** `&str`
8///
9/// ```rust
10/// extern crate syn;
11/// #[macro_use] extern crate synom;
12///
13/// named!(bang -> &str, punct!("!"));
14///
15/// fn main() {
16/// let input = " !";
17/// bang(input).expect("bang");
18/// }
19/// ```
Michael Layzell5bde96f2017-01-24 17:59:21 -050020#[macro_export]
David Tolnayb79ee962016-09-04 09:39:20 -070021macro_rules! punct {
22 ($i:expr, $punct:expr) => {
David Tolnay5fe14fc2017-01-27 16:22:08 -080023 $crate::helper::punct($i, $punct)
David Tolnayb79ee962016-09-04 09:39:20 -070024 };
25}
26
David Tolnay5fe14fc2017-01-27 16:22:08 -080027// Not public API.
28#[doc(hidden)]
David Tolnay13e5da42016-09-04 16:18:34 -070029pub fn punct<'a>(input: &'a str, token: &'static str) -> IResult<&'a str, &'a str> {
David Tolnaydef66372016-10-24 21:51:32 -070030 let input = skip_whitespace(input);
David Tolnay14cbdeb2016-10-01 12:13:59 -070031 if input.starts_with(token) {
32 IResult::Done(&input[token.len()..], token)
33 } else {
34 IResult::Error
David Tolnay13e5da42016-09-04 16:18:34 -070035 }
David Tolnay13e5da42016-09-04 16:18:34 -070036}
37
Michael Layzell24645a32017-02-04 13:19:26 -050038/// Parse a keyword. The word must make up a complete identifier.
39///
40/// - **Syntax:** `keyword!("...")`
41/// - **Output:** `&str`
42///
43/// ```rust
44/// extern crate syn;
45/// #[macro_use] extern crate synom;
46///
47/// named!(apple -> &str, keyword!("apple"));
48///
49/// fn main() {
50/// let input = " apple";
51/// apple(input).expect("apple");
52/// let input = "apples";
53/// assert_eq!(apple(input), synom::IResult::Error);
54/// }
55/// ```
Michael Layzell5bde96f2017-01-24 17:59:21 -050056#[macro_export]
David Tolnay10413f02016-09-30 09:12:02 -070057macro_rules! keyword {
58 ($i:expr, $keyword:expr) => {
David Tolnay5fe14fc2017-01-27 16:22:08 -080059 $crate::helper::keyword($i, $keyword)
David Tolnay10413f02016-09-30 09:12:02 -070060 };
61}
62
David Tolnay5fe14fc2017-01-27 16:22:08 -080063// Not public API.
64#[doc(hidden)]
David Tolnay10413f02016-09-30 09:12:02 -070065pub fn keyword<'a>(input: &'a str, token: &'static str) -> IResult<&'a str, &'a str> {
66 match punct(input, token) {
67 IResult::Done(rest, _) => {
68 match word_break(rest) {
69 IResult::Done(_, _) => IResult::Done(rest, token),
70 IResult::Error => IResult::Error,
71 }
72 }
73 IResult::Error => IResult::Error,
74 }
75}
76
Michael Layzell24645a32017-02-04 13:19:26 -050077/// Try to run the parser and wrap it in an `Option`, if it fails, succeed but
78/// produce a `None`.
79///
80/// - **Syntax:** `option!(THING)`
81/// - **Output:** `THING`
82///
83/// ```rust
84/// extern crate syn;
85/// #[macro_use] extern crate synom;
86///
87/// named!(maybe_bang -> Option<&str>, option!(punct!("!")));
88///
89/// fn main() {
90/// let input = " !";
91/// assert_eq!(maybe_bang(input).expect("maybe bang"), Some("!"));
92/// let input = "";
93/// assert_eq!(maybe_bang(input).expect("maybe bang"), None);
94/// }
95/// ```
Michael Layzell5bde96f2017-01-24 17:59:21 -050096#[macro_export]
David Tolnayb5a7b142016-09-13 22:46:39 -070097macro_rules! option {
98 ($i:expr, $submac:ident!( $($args:tt)* )) => {
David Tolnayf6ccb832016-09-04 15:00:56 -070099 match $submac!($i, $($args)*) {
Michael Layzell5bde96f2017-01-24 17:59:21 -0500100 $crate::IResult::Done(i, o) => $crate::IResult::Done(i, Some(o)),
101 $crate::IResult::Error => $crate::IResult::Done($i, None),
David Tolnayf6ccb832016-09-04 15:00:56 -0700102 }
David Tolnayb5a7b142016-09-13 22:46:39 -0700103 };
David Tolnayf6ccb832016-09-04 15:00:56 -0700104
David Tolnayb5a7b142016-09-13 22:46:39 -0700105 ($i:expr, $f:expr) => {
106 option!($i, call!($f));
107 };
108}
109
Michael Layzell24645a32017-02-04 13:19:26 -0500110/// Try to run the parser, if it fails, succeed and produce an empty Vec.
111///
112/// The argument parser must be a Vec.
113///
114/// - **Syntax:** `opt_vec!(THING)`
115/// - **Output:** `THING`
116///
117/// ```rust
118/// extern crate syn;
119/// #[macro_use] extern crate synom;
120///
121/// use syn::Expr;
122/// use syn::parse::expr;
123///
124/// named!(opt_expr_list -> Vec<Expr>, opt_vec!(
125/// separated_list!(punct!(","), expr)));
126///
127/// fn main() {
128/// let input = "a, 1 + 1, Object { construct: ion }";
129/// let result = opt_expr_list(input).expect("opt expr list");
130/// assert_eq!(result.len(), 3);
131///
132/// let input = "";
133/// let result = opt_expr_list(input).expect("opt expr list");
134/// assert_eq!(result.len(), 0);
135/// }
136/// ```
Michael Layzell5bde96f2017-01-24 17:59:21 -0500137#[macro_export]
David Tolnayb5a7b142016-09-13 22:46:39 -0700138macro_rules! opt_vec {
139 ($i:expr, $submac:ident!( $($args:tt)* )) => {
David Tolnayb79ee962016-09-04 09:39:20 -0700140 match $submac!($i, $($args)*) {
Michael Layzell5bde96f2017-01-24 17:59:21 -0500141 $crate::IResult::Done(i, o) => $crate::IResult::Done(i, o),
142 $crate::IResult::Error => $crate::IResult::Done($i, Vec::new()),
David Tolnayb79ee962016-09-04 09:39:20 -0700143 }
David Tolnayb5a7b142016-09-13 22:46:39 -0700144 };
145}
David Tolnayb79ee962016-09-04 09:39:20 -0700146
Michael Layzell24645a32017-02-04 13:19:26 -0500147/// Parses nothing and always succeeds.
148///
149/// - **Syntax:** `epsilon!()`
150/// - **Output:** `()`
151///
152/// ```rust
153/// #[macro_use] extern crate synom;
154///
155/// named!(epsi -> (), epsilon!());
156///
157/// fn main() {
158/// let input = "";
159/// assert_eq!(epsi(input).expect("maybe bang"), ());
160/// }
161/// ```
Michael Layzell5bde96f2017-01-24 17:59:21 -0500162#[macro_export]
David Tolnayb79ee962016-09-04 09:39:20 -0700163macro_rules! epsilon {
164 ($i:expr,) => {
Michael Layzell5bde96f2017-01-24 17:59:21 -0500165 $crate::IResult::Done($i, ())
David Tolnayfa0edf22016-09-23 22:58:24 -0700166 };
167}
168
Michael Layzell24645a32017-02-04 13:19:26 -0500169/// Run a parser, binding the result to a name, and then evaluating an
170/// expression.
171///
172/// Discards the result of the expression and parser.
173///
174/// - **Syntax:** `tap!(NAME : THING => EXPR)`
175/// - **Output:** `()`
176///
177/// ```rust
178/// extern crate syn;
179/// #[macro_use] extern crate synom;
180///
181/// use syn::{Expr, ExprKind};
182/// use syn::parse::expr;
183///
184/// named!(pub expr_with_arrow_call -> Expr, do_parse!(
185/// mut e: expr >>
186/// many0!(tap!(arg: tuple!(punct!("=>"), expr) => {
187/// e = Expr {
188/// node: ExprKind::Call(Box::new(e), vec![arg.1]),
189/// attrs: Vec::new(),
190/// };
191/// })) >>
192/// (e)
193/// ));
194///
195/// fn main() {
196/// let input = "something => argument1 => argument2";
197///
198/// let result = expr_with_arrow_call(input).expect("expr with arrow call");
199///
200/// println!("result = {:?}", result);
201/// }
202/// ```
Michael Layzell5bde96f2017-01-24 17:59:21 -0500203#[macro_export]
David Tolnayfa0edf22016-09-23 22:58:24 -0700204macro_rules! tap {
205 ($i:expr, $name:ident : $submac:ident!( $($args:tt)* ) => $e:expr) => {
206 match $submac!($i, $($args)*) {
Michael Layzell5bde96f2017-01-24 17:59:21 -0500207 $crate::IResult::Done(i, o) => {
David Tolnayfa0edf22016-09-23 22:58:24 -0700208 let $name = o;
209 $e;
Michael Layzell5bde96f2017-01-24 17:59:21 -0500210 $crate::IResult::Done(i, ())
David Tolnayfa0edf22016-09-23 22:58:24 -0700211 }
Michael Layzell5bde96f2017-01-24 17:59:21 -0500212 $crate::IResult::Error => $crate::IResult::Error,
David Tolnayfa0edf22016-09-23 22:58:24 -0700213 }
214 };
215
216 ($i:expr, $name:ident : $f:expr => $e:expr) => {
217 tap!($i, $name: call!($f) => $e);
David Tolnayb79ee962016-09-04 09:39:20 -0700218 };
219}
David Tolnay674258d2016-10-08 13:30:45 -0700220
Michael Layzell24645a32017-02-04 13:19:26 -0500221/// Parses a series of things, separated by the given punctuation. Does not
222/// allow for a trailing seperator.
223///
224/// The implementation requires that the first parameter is a `punct!` macro,
225/// and the second is a named parser.
226///
227/// - **Syntax:** `separated_list!(punct!("..."), THING)`
228/// - **Output:** `Vec<THING>`
229///
230/// ```rust
231/// extern crate syn;
232/// #[macro_use] extern crate synom;
233///
234/// use syn::Expr;
235/// use syn::parse::expr;
236///
237/// named!(pub expr_list -> Vec<Expr>,
238/// separated_list!(punct!(","), expr));
239///
240/// fn main() {
241/// let input = "1 + 1, things, Construct { this: thing }";
242///
243/// let result = expr_list(input).expect("expr list");
244/// assert_eq!(result.len(), 3);
245/// }
246/// ```
Michael Layzell5bde96f2017-01-24 17:59:21 -0500247#[macro_export]
David Tolnay674258d2016-10-08 13:30:45 -0700248macro_rules! separated_list {
249 ($i:expr, punct!($sep:expr), $f:expr) => {
David Tolnay5fe14fc2017-01-27 16:22:08 -0800250 $crate::helper::separated_list($i, $sep, $f, false)
David Tolnay674258d2016-10-08 13:30:45 -0700251 };
252}
253
Michael Layzell24645a32017-02-04 13:19:26 -0500254/// Parses a series of things, separated by the given punctuation. Allows for
255/// a trailing seperator.
256///
257/// The implementation requires that the first parameter is a `punct!` macro,
258/// and the second is a named parser.
259///
260/// - **Syntax:** `terminated_list!(punct!("..."), THING)`
261/// - **Output:** `Vec<THING>`
262///
263/// ```rust
264/// extern crate syn;
265/// #[macro_use] extern crate synom;
266///
267/// use syn::Expr;
268/// use syn::parse::expr;
269///
270/// named!(pub expr_list -> Vec<Expr>,
271/// terminated_list!(punct!(","), expr));
272///
273/// fn main() {
274/// let input = "1 + 1, things, Construct { this: thing },";
275///
276/// let result = expr_list(input).expect("expr list");
277/// assert_eq!(result.len(), 3);
278/// }
279/// ```
Michael Layzell5bde96f2017-01-24 17:59:21 -0500280#[macro_export]
David Tolnayff46fd22016-10-08 13:53:28 -0700281macro_rules! terminated_list {
282 ($i:expr, punct!($sep:expr), $f:expr) => {
David Tolnay5fe14fc2017-01-27 16:22:08 -0800283 $crate::helper::separated_list($i, $sep, $f, true)
David Tolnayff46fd22016-10-08 13:53:28 -0700284 };
285}
286
David Tolnay5fe14fc2017-01-27 16:22:08 -0800287// Not public API.
288#[doc(hidden)]
David Tolnayc7f646a2016-10-16 10:54:39 -0700289pub fn separated_list<'a, T>(mut input: &'a str,
290 sep: &'static str,
291 f: fn(&'a str) -> IResult<&'a str, T>,
292 terminated: bool)
293 -> IResult<&'a str, Vec<T>> {
David Tolnay674258d2016-10-08 13:30:45 -0700294 let mut res = Vec::new();
295
296 // get the first element
297 match f(input) {
298 IResult::Error => IResult::Done(input, Vec::new()),
299 IResult::Done(i, o) => {
300 if i.len() == input.len() {
301 IResult::Error
302 } else {
303 res.push(o);
304 input = i;
305
306 // get the separator first
307 while let IResult::Done(i2, _) = punct(input, sep) {
308 if i2.len() == input.len() {
309 break;
310 }
311
312 // get the element next
313 if let IResult::Done(i3, o3) = f(i2) {
314 if i3.len() == i2.len() {
315 break;
316 }
317 res.push(o3);
318 input = i3;
319 } else {
320 break;
321 }
322 }
David Tolnayff46fd22016-10-08 13:53:28 -0700323 if terminated {
324 if let IResult::Done(after, _) = punct(input, sep) {
325 input = after;
326 }
327 }
David Tolnay674258d2016-10-08 13:30:45 -0700328 IResult::Done(input, res)
329 }
330 }
331 }
332}