tagua_parser/
macros.rs

1// Tagua VM
2//
3//
4// New BSD License
5//
6// Copyright © 2016-2016, Ivan Enderlin.
7// All rights reserved.
8//
9// Redistribution and use in source and binary forms, with or without
10// modification, are permitted provided that the following conditions are met:
11//     * Redistributions of source code must retain the above copyright
12//       notice, this list of conditions and the following disclaimer.
13//     * Redistributions in binary form must reproduce the above copyright
14//       notice, this list of conditions and the following disclaimer in the
15//       documentation and/or other materials provided with the distribution.
16//     * Neither the name of the Hoa nor the names of its contributors may be
17//       used to endorse or promote products derived from this software without
18//       specific prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
24// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30// POSSIBILITY OF SUCH DAMAGE.
31
32//! Extra macros helping to write parsers.
33
34/// Custom values for `ErrorKind::Custom`.
35pub enum ErrorKindCustom {
36    /// Represent errors from the `exclude` macro.
37    Exclude,
38    /// Represent errors from the `itag` macro.
39    ITag
40}
41
42/// `exclude!(I -> Result<I, O>, I -> Result<I, P>) => I -> Result<I, 0>`
43/// returns the result of the first parser if the second fails. Both parsers
44/// run on the same input.
45///
46/// This is handy when the first parser accepts general values and the second
47/// parser denies a particular subset of values.
48///
49/// # Examples
50///
51/// ```
52/// # #[macro_use]
53/// # extern crate nom;
54/// # #[macro_use]
55/// # extern crate tagua_parser;
56/// use tagua_parser::{
57///     Error,
58///     ErrorKind,
59///     Result
60/// };
61/// use tagua_parser::macros::ErrorKindCustom;
62///
63/// # fn main() {
64/// named!(
65///     test,
66///     exclude!(
67///         is_a!("abcdef"),
68///         alt!(
69///             tag!("abc")
70///           | tag!("ace")
71///         )
72///     )
73/// );
74///
75/// assert_eq!(test(&b"fedabc"[..]), Result::Done(&b""[..], &b"fedabc"[..]));
76/// assert_eq!(test(&b"abcabc"[..]), Result::Error(Error::Position(ErrorKind::Custom(ErrorKindCustom::Exclude as u32), &b"abcabc"[..])));
77/// # }
78/// ```
79#[macro_export]
80macro_rules! exclude(
81    ($input:expr, $submacro1:ident!($($arguments1:tt)*), $submacro2:ident!($($arguments2:tt)*)) => (
82        {
83            match $submacro1!($input, $($arguments1)*) {
84                $crate::Result::Done(i, o) =>
85                    match $submacro2!(o, $($arguments2)*) {
86                        $crate::Result::Done(_, _) =>
87                            $crate::Result::Error($crate::Error::Position($crate::ErrorKind::Custom($crate::macros::ErrorKindCustom::Exclude as u32), $input)),
88
89                        $crate::Result::Incomplete(_) =>
90                            $crate::Result::Done(i, o),
91
92                        $crate::Result::Error(_) =>
93                            $crate::Result::Done(i, o),
94                    },
95
96                $crate::Result::Incomplete(e) =>
97                    $crate::Result::Incomplete(e),
98
99                $crate::Result::Error(e) =>
100                    $crate::Result::Error(e)
101            }
102        }
103    );
104
105    ($input:expr, $submacro1:ident!($($arguments1:tt)*), $g:expr) => (
106        exclude!($input, $submacro1!($($arguments1)*), call!($g));
107    );
108
109    ($input:expr, $f:expr, $submacro2:ident!($($arguments2:tt)*)) => (
110        exclude!($input, call!($f), $submacro2!($($arguments2)*));
111    );
112
113    ($input:expr, $f:expr, $g:expr) => (
114        exclude!($input, call!($f), call!($g));
115    );
116);
117
118/// `first!(I -> Result<I, O>) => I -> Result<I, O>`
119/// is applying the `skip` rule before the first argument; it allows to skip
120/// tokens.
121///
122/// # Examples
123///
124/// ```
125/// # #[macro_use]
126/// # extern crate nom;
127/// # #[macro_use]
128/// # extern crate tagua_parser;
129/// use tagua_parser::Result;
130///
131/// # fn main() {
132/// named!(
133///     test,
134///     first!(tag!("bar"))
135/// );
136///
137/// assert_eq!(test(&b"/* foo */bar"[..]), Result::Done(&b""[..], &b"bar"[..]));
138/// # }
139/// ```
140#[macro_export]
141macro_rules! first(
142    ($input:expr, $submacro:ident!($($arguments:tt)*)) => (
143        {
144            preceded!(
145                $input,
146                call!($crate::rules::skip::skip),
147                $submacro!($($arguments)*)
148            )
149        }
150    );
151
152    ($input:expr, $f:expr) => (
153        first!($input, call!($f));
154    );
155);
156
157/// `itag!(&[T]: nom::AsBytes) => &[T] -> Result<&[T], &[T]>`
158/// declares a case-insensitive ASCII array as a suite to recognize.
159///
160/// It is pretty similar to the nom `tag!` macro except it is case-insensitive
161/// and only accepts ASCII characters so far.
162///
163/// It does not return the consumed data but the expected data (the first
164/// argument).
165///
166/// # Examples
167///
168/// ```
169/// # #[macro_use]
170/// # extern crate nom;
171/// # #[macro_use]
172/// # extern crate tagua_parser;
173/// use tagua_parser::Result;
174///
175/// # fn main() {
176/// named!(
177///     test<&str>,
178///     itag!("foobar")
179/// );
180///
181/// let output = Result::Done(&b""[..], "foobar");
182///
183/// assert_eq!(test(&b"foobar"[..]), output);
184/// assert_eq!(test(&b"FoObAr"[..]), output);
185/// # }
186/// ```
187#[macro_export]
188macro_rules! itag(
189    ($input:expr, $string:expr) => (
190        {
191            use std::ascii::AsciiExt;
192
193            #[inline(always)]
194            fn as_bytes<T: ::nom::AsBytes>(datum: &T) -> &[u8] {
195                datum.as_bytes()
196            }
197
198            let expected      = $string;
199            let bytes         = as_bytes(&expected);
200            let input_length  = $input.len();
201            let bytes_length  = bytes.len();
202            let length        = ::std::cmp::min(input_length, bytes_length);
203            let reduced_input = &$input[..length];
204            let reduced_bytes = &bytes[..length];
205
206            let output: $crate::Result<_, _> =
207                if !reduced_input.eq_ignore_ascii_case(reduced_bytes) {
208                    $crate::Result::Error($crate::Error::Position($crate::ErrorKind::Custom($crate::macros::ErrorKindCustom::ITag as u32), $input))
209                } else if length < bytes_length {
210                    $crate::Result::Incomplete($crate::Needed::Size(bytes_length))
211                } else {
212                    $crate::Result::Done(&$input[bytes_length..], $string)
213                };
214
215            output
216        }
217    );
218);
219
220/// `keyword!(&[T]: nom::AsBytes) => &[T] -> Result<&[T], &[T]>`
221/// is an alias to the `itag` macro.
222///
223/// The goal of this alias is twofold:
224///
225///   1. It avoids confusion and errors (a PHP keyword is always
226///      case-insensitive),
227///   2. It ensures a better readability of parsers.
228///
229/// # Examples
230///
231/// ```
232/// # #[macro_use]
233/// # extern crate nom;
234/// # #[macro_use]
235/// # extern crate tagua_parser;
236/// use tagua_parser::{
237///     Result,
238///     tokens
239/// };
240///
241/// # fn main() {
242/// named!(
243///     test<&[u8]>,
244///     keyword!(tokens::CLASS)
245/// );
246///
247/// let output = Result::Done(&b""[..], tokens::CLASS);
248///
249/// assert_eq!(test(&b"class"[..]), output);
250/// assert_eq!(test(&b"ClAsS"[..]), output);
251/// # }
252/// ```
253#[macro_export]
254macro_rules! keyword(
255    ($input:expr, $keyword:expr) => (
256        {
257            itag!($input, $keyword)
258        }
259    );
260);
261
262
263#[cfg(test)]
264mod tests {
265    use super::ErrorKindCustom;
266    use super::super::internal::{
267        Error,
268        ErrorKind,
269        Needed,
270        Result
271    };
272
273    #[test]
274    fn case_exclude_empty_set() {
275        named!(
276            test,
277            exclude!(
278                is_a!("abcdef"),
279                alt!(
280                    tag!("abc")
281                  | tag!("ace")
282                )
283            )
284        );
285
286        assert_eq!(test(&b"fedabc"[..]), Result::Done(&b""[..], &b"fedabc"[..]));
287    }
288
289    #[test]
290    fn case_exclude_one_branch() {
291        named!(
292            test,
293            exclude!(
294                is_a!("abcdef"),
295                alt!(
296                    tag!("abc")
297                  | tag!("ace")
298                )
299            )
300        );
301
302        assert_eq!(test(&b"abcabc"[..]), Result::Error(Error::Position(ErrorKind::Custom(ErrorKindCustom::Exclude as u32), &b"abcabc"[..])));
303    }
304
305    #[test]
306    fn case_exclude_another_branch() {
307        named!(
308            test,
309            exclude!(
310                is_a!("abcdef"),
311                alt!(
312                    tag!("abc")
313                  | tag!("ace")
314                )
315            )
316        );
317
318        assert_eq!(test(&b"acebdf"[..]), Result::Error(Error::Position(ErrorKind::Custom(ErrorKindCustom::Exclude as u32), &b"acebdf"[..])));
319    }
320
321    #[test]
322    fn case_exclude_incomplete() {
323        named!(
324            test,
325            exclude!(
326                take!(3),
327                alt!(
328                    tag!("abc")
329                  | tag!("ace")
330                )
331            )
332        );
333
334        assert_eq!(test(&b"a"[..]), Result::Incomplete(Needed::Size(3)));
335    }
336
337    #[test]
338    fn case_exclude_incomplete_submacro() {
339        named!(
340            test,
341            exclude!(
342                take!(3),
343                take!(5)
344            )
345        );
346
347        assert_eq!(test(&b"abcdef"[..]), Result::Done(&b"def"[..], &b"abc"[..]));
348    }
349
350    #[test]
351    fn case_first_with_whitespace() {
352        named!(hello, tag!("hello"));
353        named!(test1, first!(tag!("hello")));
354        named!(test2, first!(hello));
355
356        let input  = &b"  \nhello\t\r"[..];
357        let output = Result::Done(&b"\t\r"[..], &b"hello"[..]);
358
359        assert_eq!(test1(input), output);
360        assert_eq!(test2(input), output);
361    }
362
363    #[test]
364    fn case_first_with_comment() {
365        named!(hello, tag!("hello"));
366        named!(test1, first!(tag!("hello")));
367        named!(test2, first!(hello));
368
369        let input  = &b"/* foo */hello/* bar */"[..];
370        let output = Result::Done(&b"/* bar */"[..], &b"hello"[..]);
371
372        assert_eq!(test1(input), output);
373        assert_eq!(test2(input), output);
374    }
375
376    #[test]
377    fn case_first_with_whitespace_and_comment() {
378        named!(hello, tag!("hello"));
379        named!(test1, first!(tag!("hello")));
380        named!(test2, first!(hello));
381
382        let input  = &b"/* foo */  \nhello/* bar */\t"[..];
383        let output = Result::Done(&b"/* bar */\t"[..], &b"hello"[..]);
384
385        assert_eq!(test1(input), output);
386        assert_eq!(test2(input), output);
387    }
388
389    #[test]
390    fn case_itag() {
391        named!(test1<&str>, itag!("foobar"));
392        named!(test2<&str>, itag!("fOoBaR"));
393
394        let input = &b"FoObArBaZQuX"[..];
395
396        assert_eq!(test1(input), Result::Done(&b"BaZQuX"[..], "foobar"));
397        assert_eq!(test2(input), Result::Done(&b"BaZQuX"[..], "fOoBaR"));
398    }
399
400    #[test]
401    fn case_itag_incomplete() {
402        named!(test1<&str>, itag!("foobar"));
403        named!(test2<&str>, itag!("FoObAR"));
404
405        let input  = &b"FoOb"[..];
406        let output = Result::Incomplete(Needed::Size(6));
407
408        assert_eq!(test1(input), output);
409        assert_eq!(test2(input), output);
410    }
411
412    #[test]
413    fn case_itag_error() {
414        named!(test<&str>, itag!("foobar"));
415
416        assert_eq!(test(&b"BaZQuX"[..]), Result::Error(Error::Position(ErrorKind::Custom(ErrorKindCustom::ITag as u32), &b"BaZQuX"[..])));
417    }
418
419    #[test]
420    fn case_keyword() {
421        named!(test1<&str>, keyword!("foobar"));
422        named!(test2<&str>, keyword!("fOoBaR"));
423
424        let input = &b"FoObArBaZQuX"[..];
425
426        assert_eq!(test1(input), Result::Done(&b"BaZQuX"[..], "foobar"));
427        assert_eq!(test2(input), Result::Done(&b"BaZQuX"[..], "fOoBaR"));
428    }
429
430    #[test]
431    fn case_keyword_incomplete() {
432        named!(test1<&str>, keyword!("foobar"));
433        named!(test2<&str>, keyword!("FoObAR"));
434
435        let input  = &b"FoOb"[..];
436        let output = Result::Incomplete(Needed::Size(6));
437
438        assert_eq!(test1(input), output);
439        assert_eq!(test2(input), output);
440    }
441
442    #[test]
443    fn case_keyword_error() {
444        named!(test<&str>, keyword!("foobar"));
445
446        assert_eq!(test(&b"BaZQuX"[..]), Result::Error(Error::Position(ErrorKind::Custom(ErrorKindCustom::ITag as u32), &b"BaZQuX"[..])));
447    }
448}