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}