зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1486815 - Remove nom 1 from dependencies. r=emilio
This commit is contained in:
Родитель
f78d708b01
Коммит
3946c67f0c
|
@ -172,7 +172,7 @@ name = "bindgen"
|
|||
version = "0.37.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cexpr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cexpr 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clang-sys 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -280,10 +280,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "cexpr"
|
||||
version = "0.2.0"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"nom 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nom 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1489,11 +1489,6 @@ name = "nodrop"
|
|||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "1.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "3.2.1"
|
||||
|
@ -2860,7 +2855,7 @@ dependencies = [
|
|||
"checksum bzip2 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3eafc42c44e0d827de6b1c131175098fe7fb53b8ce8a47e65cb3ea94688be24"
|
||||
"checksum bzip2-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2c5162604199bbb17690ede847eaa6120a3f33d5ab4dcc8e7c25b16d849ae79b"
|
||||
"checksum cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "2119ea4867bd2b8ed3aecab467709720b2d55b1bcfe09f772fd68066eaf15275"
|
||||
"checksum cexpr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "393a5f0088efbe41f9d1fcd062f24e83c278608420e62109feb2c8abee07de7d"
|
||||
"checksum cexpr 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42aac45e9567d97474a834efdee3081b3c942b2205be932092f53354ce503d6c"
|
||||
"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
|
||||
"checksum chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "9213f7cd7c27e95c2b57c49f0e69b1ea65b27138da84a170133fd21b07659c00"
|
||||
"checksum clang-sys 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d7f7c04e52c35222fffcc3a115b5daf5f7e2bfb71c13c4e2321afe1fc71859c2"
|
||||
|
@ -2969,7 +2964,6 @@ dependencies = [
|
|||
"checksum new-ordered-float 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8ccbebba6fb53a6d2bdcfaf79cb339bc136dee3bfff54dc337a334bafe36476a"
|
||||
"checksum new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0cdc457076c78ab54d5e0d6fa7c47981757f1e34dc39ff92787f217dede586c4"
|
||||
"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2"
|
||||
"checksum nom 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a5b8c256fd9471521bcb84c3cdba98921497f1a331cbc15b8030fc63b82050ce"
|
||||
"checksum nom 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05aec50c70fd288702bcd93284a8444607f3292dbdf2a30de5ea5dcdbe72287b"
|
||||
"checksum num 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "a311b77ebdc5dd4cf6449d81e4135d9f0e3b153839ac90e648a8ef538f923525"
|
||||
"checksum num-derive 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d2c31b75c36a993d30c7a13d70513cb93f02acafdd5b7ba250f9b0e18615de7"
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"fb51f35c4be7c1a6768ab026c11092cbf8b81aebaaa970e19f0b0033f7a26b88","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"d9771b8c6cf4426d3846de54c1febe20907f1eeadf7adfb5ade89a83bd9ea77f","src/expr.rs":"dc3e299b02b33e1441d6e82482409272554cfae8f271e3b5664a216ecf1fecdc","src/lib.rs":"a37cca356d761557bd27e08ce46044b758bd21d523d039105e71597f64da9ba1","src/literal.rs":"deddad795a6788a78533f49b6afa784702e2dbfd9848d6fad60ec5cecabfb840","src/token.rs":"85d63f778a438ba9f39dac643473d457103bad2f2d4b53dd62e65ded65b1a83d","tests/clang.rs":"125058defb8166679019475d72a76804ac2dbfc09f50c3fad5b908970d652f02","tests/input/chars.h":"69c8141870872b795b5174bad125b748732c2b01d0e98ffcfc37b19f3f791f69","tests/input/fail.h":"b0b6cffd2dd17410b5eb02ee79ab75754820480b960db8a9866cc9983bd36b65","tests/input/floats.h":"28ec664e793c494e1a31f3bc5b790014e9921fc741bf475a86319b9a9eee5915","tests/input/int_signed.h":"934199eded85dd7820ca08c0beb1381ee6d9339970d2720a69c23025571707ce","tests/input/int_unsigned.h":"3cdf9782a6e74796833ea0dfcb35b6ab6ca76a89080e82ed39bac0d55064d88c","tests/input/strings.h":"27de768b506a1aedfdb750ac53b7a395cc20395958ff3263af1ab5851e2c412c"},"package":"393a5f0088efbe41f9d1fcd062f24e83c278608420e62109feb2c8abee07de7d"}
|
||||
{"files":{"Cargo.toml":"22914a43154e0b38bbe265a67024c1f98af9087ca561448ac0f13ed57c9311ae","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"d9771b8c6cf4426d3846de54c1febe20907f1eeadf7adfb5ade89a83bd9ea77f","src/expr.rs":"b888963ab9eb344c93c0654286231b4204809a971682155fade5c69a4493636f","src/lib.rs":"78bbde89e803cf931216e38bdd992e13894cd898868478a258eac5155bdf4de9","src/literal.rs":"fb80a7b43d96bedfd47bc2d39eaf588c5cef6a2374132fbdfe5146dd56a1894c","src/token.rs":"52d42deb2a2575bb8631e2e821593d8288fed16e21bab3ceeacb6a7b06c40087","tests/clang.rs":"a650cde69ab2e801f994f15e4cb32c538e53abefcdd862865ce277ce9a055242","tests/input/chars.h":"69c8141870872b795b5174bad125b748732c2b01d0e98ffcfc37b19f3f791f69","tests/input/fail.h":"b0b6cffd2dd17410b5eb02ee79ab75754820480b960db8a9866cc9983bd36b65","tests/input/floats.h":"28ec664e793c494e1a31f3bc5b790014e9921fc741bf475a86319b9a9eee5915","tests/input/int_signed.h":"934199eded85dd7820ca08c0beb1381ee6d9339970d2720a69c23025571707ce","tests/input/int_unsigned.h":"f47c1ccb6c69856162639277d7552090055420155df55f65581e57217cccce76","tests/input/strings.h":"75c60527068172b97983d2b8361938e856ea394002d5bef05de1adc6a0f5fc01","tests/input/test_llvm_bug_9069.h":"8d9ae1d1eadc8f6d5c14296f984547fe894d0f2ce5cd6d7aa8caad40a56bc5e1"},"package":"42aac45e9567d97474a834efdee3081b3c942b2205be932092f53354ce503d6c"}
|
|
@ -1,15 +1,26 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g. crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
name = "cexpr"
|
||||
version = "0.2.0"
|
||||
version = "0.2.3"
|
||||
authors = ["Jethro Beekman <jethro@jbeekman.nl>"]
|
||||
license = "Apache-2.0/MIT"
|
||||
description = "A C expression parser and evaluator"
|
||||
documentation = "https://jethrogb.github.io/rust-cexpr/cexpr"
|
||||
documentation = "https://docs.rs/cexpr/"
|
||||
keywords = ["C", "expression", "parser"]
|
||||
license = "Apache-2.0/MIT"
|
||||
repository = "https://github.com/jethrogb/rust-cexpr"
|
||||
keywords = ["C","expression","parser"]
|
||||
|
||||
[dependencies]
|
||||
nom = "^1"
|
||||
|
||||
[dev-dependencies]
|
||||
clang-sys = "0.11.0"
|
||||
[dependencies.nom]
|
||||
version = "^3"
|
||||
features = ["verbose-errors"]
|
||||
[dev-dependencies.clang-sys]
|
||||
version = "0.11.0"
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
//!
|
||||
//! The `sizeof` operator is not supported.
|
||||
//!
|
||||
//! String concatenation is supported, but width prefixes are ignored all
|
||||
//! String concatenation is supported, but width prefixes are ignored; all
|
||||
//! strings are treated as narrow strings.
|
||||
//!
|
||||
//! Use the `IdentifierParser` to substitute identifiers found in expressions.
|
||||
|
@ -85,9 +85,9 @@ impl From<Vec<u8>> for EvalResult {
|
|||
// ===========================================
|
||||
|
||||
macro_rules! exact_token (
|
||||
($i:expr, $k: ident, $c: expr) => ({
|
||||
($i:expr, $k:ident, $c:expr) => ({
|
||||
if $i.is_empty() {
|
||||
let res: CResult<&[u8]> = IResult::Incomplete(Needed::Size(1));
|
||||
let res: CResult<&[u8]> = IResult::Incomplete(Needed::Size($c.len()));
|
||||
res
|
||||
} else {
|
||||
if $i[0].kind==TokenKind::$k && &$i[0].raw[..]==$c {
|
||||
|
@ -100,7 +100,7 @@ macro_rules! exact_token (
|
|||
);
|
||||
|
||||
macro_rules! typed_token (
|
||||
($i:expr, $k: ident) => ({
|
||||
($i:expr, $k:ident) => ({
|
||||
if $i.is_empty() {
|
||||
let res: CResult<&[u8]> = IResult::Incomplete(Needed::Size(1));
|
||||
res
|
||||
|
@ -114,6 +114,7 @@ macro_rules! typed_token (
|
|||
});
|
||||
);
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! any_token (
|
||||
($i:expr,) => ({
|
||||
if $i.is_empty() {
|
||||
|
@ -126,7 +127,24 @@ macro_rules! any_token (
|
|||
);
|
||||
|
||||
macro_rules! p (
|
||||
($i:expr, $c: expr) => (exact_token!($i,Punctuation,$c.as_bytes()))
|
||||
($i:expr, $c:expr) => (exact_token!($i,Punctuation,$c.as_bytes()))
|
||||
);
|
||||
|
||||
macro_rules! one_of_punctuation (
|
||||
($i:expr, $c:expr) => ({
|
||||
if $i.is_empty() {
|
||||
let min = $c.iter().map(|opt|opt.len()).min().expect("at least one option");
|
||||
let res: CResult<&[u8]> = IResult::Incomplete(Needed::Size(min));
|
||||
res
|
||||
} else {
|
||||
if $i[0].kind==TokenKind::Punctuation && $c.iter().any(|opt|opt.as_bytes()==&$i[0].raw[..]) {
|
||||
IResult::Done(&$i[1..], &$i[0].raw[..])
|
||||
} else {
|
||||
const VAILD_VALUES: &'static [&'static str] = &$c;
|
||||
IResult::Error(Err::Position(ErrorKind::Custom(::Error::ExactTokens(TokenKind::Punctuation,VAILD_VALUES)), $i))
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
// ==================================================
|
||||
|
@ -264,71 +282,104 @@ impl<'a> PRef<'a> {
|
|||
delimited!(p!("("),call_m!(self.numeric_expr),p!(")")) |
|
||||
numeric!(call_m!(self.literal)) |
|
||||
numeric!(call_m!(self.identifier)) |
|
||||
map_opt!(pair!(alt!( p!("+") | p!("-") | p!("~") ),call_m!(self.unary)),unary_op)
|
||||
map_opt!(pair!(one_of_punctuation!(["+", "-", "~"]),call_m!(self.unary)),unary_op)
|
||||
)
|
||||
);
|
||||
|
||||
method!(mul_div_rem<PRef<'a>,&[Token],EvalResult,::Error>, mut self,
|
||||
chain!(
|
||||
mut acc: call_m!(self.unary) ~
|
||||
many0!(alt!(
|
||||
tap!(mul: preceded!(p!("*"), call_m!(self.unary)) => acc *= &mul) |
|
||||
tap!(div: preceded!(p!("/"), call_m!(self.unary)) => acc /= &div) |
|
||||
tap!(rem: preceded!(p!("%"), call_m!(self.unary)) => acc %= &rem)
|
||||
)),
|
||||
|| { return acc }
|
||||
do_parse!(
|
||||
acc: call_m!(self.unary) >>
|
||||
res: fold_many0!(
|
||||
pair!(one_of_punctuation!(["*", "/", "%"]), call_m!(self.unary)),
|
||||
acc,
|
||||
|mut acc, (op, val): (&[u8], EvalResult)| {
|
||||
match op[0] as char {
|
||||
'*' => acc *= &val,
|
||||
'/' => acc /= &val,
|
||||
'%' => acc %= &val,
|
||||
_ => unreachable!()
|
||||
};
|
||||
acc
|
||||
}
|
||||
) >> (res)
|
||||
)
|
||||
);
|
||||
|
||||
method!(add_sub<PRef<'a>,&[Token],EvalResult,::Error>, mut self,
|
||||
chain!(
|
||||
mut acc: call_m!(self.mul_div_rem) ~
|
||||
many0!(alt!(
|
||||
tap!(add: preceded!(p!("+"), call_m!(self.mul_div_rem)) => acc += &add) |
|
||||
tap!(sub: preceded!(p!("-"), call_m!(self.mul_div_rem)) => acc -= &sub)
|
||||
)),
|
||||
|| { return acc }
|
||||
do_parse!(
|
||||
acc: call_m!(self.mul_div_rem) >>
|
||||
res: fold_many0!(
|
||||
pair!(one_of_punctuation!(["+", "-"]), call_m!(self.mul_div_rem)),
|
||||
acc,
|
||||
|mut acc, (op, val): (&[u8], EvalResult)| {
|
||||
match op[0] as char {
|
||||
'+' => acc += &val,
|
||||
'-' => acc -= &val,
|
||||
_ => unreachable!()
|
||||
};
|
||||
acc
|
||||
}
|
||||
) >> (res)
|
||||
)
|
||||
);
|
||||
|
||||
method!(shl_shr<PRef<'a>,&[Token],EvalResult,::Error>, mut self,
|
||||
numeric!(chain!(
|
||||
mut acc: call_m!(self.add_sub) ~
|
||||
many0!(alt!(
|
||||
tap!(shl: preceded!(p!("<<"), call_m!(self.add_sub)) => acc <<= &shl) |
|
||||
tap!(shr: preceded!(p!(">>"), call_m!(self.add_sub)) => acc >>= &shr)
|
||||
)),
|
||||
|| { return acc }
|
||||
numeric!(do_parse!(
|
||||
acc: call_m!(self.add_sub) >>
|
||||
res: fold_many0!(
|
||||
pair!(one_of_punctuation!(["<<", ">>"]), call_m!(self.add_sub)),
|
||||
acc,
|
||||
|mut acc, (op, val): (&[u8], EvalResult)| {
|
||||
match op {
|
||||
b"<<" => acc <<= &val,
|
||||
b">>" => acc >>= &val,
|
||||
_ => unreachable!()
|
||||
};
|
||||
acc
|
||||
}
|
||||
) >> (res)
|
||||
))
|
||||
);
|
||||
|
||||
method!(and<PRef<'a>,&[Token],EvalResult,::Error>, mut self,
|
||||
numeric!(chain!(
|
||||
mut acc: call_m!(self.shl_shr) ~
|
||||
many0!(
|
||||
tap!(and: preceded!(p!("&"), call_m!(self.shl_shr)) => acc &= &and)
|
||||
),
|
||||
|| { return acc }
|
||||
numeric!(do_parse!(
|
||||
acc: call_m!(self.shl_shr) >>
|
||||
res: fold_many0!(
|
||||
preceded!(p!("&"), call_m!(self.shl_shr)),
|
||||
acc,
|
||||
|mut acc, val: EvalResult| {
|
||||
acc &= &val;
|
||||
acc
|
||||
}
|
||||
) >> (res)
|
||||
))
|
||||
);
|
||||
|
||||
method!(xor<PRef<'a>,&[Token],EvalResult,::Error>, mut self,
|
||||
numeric!(chain!(
|
||||
mut acc: call_m!(self.and) ~
|
||||
many0!(
|
||||
tap!(xor: preceded!(p!("^"), call_m!(self.and)) => acc ^= &xor)
|
||||
),
|
||||
|| { return acc }
|
||||
numeric!(do_parse!(
|
||||
acc: call_m!(self.and) >>
|
||||
res: fold_many0!(
|
||||
preceded!(p!("^"), call_m!(self.and)),
|
||||
acc,
|
||||
|mut acc, val: EvalResult| {
|
||||
acc ^= &val;
|
||||
acc
|
||||
}
|
||||
) >> (res)
|
||||
))
|
||||
);
|
||||
|
||||
method!(or<PRef<'a>,&[Token],EvalResult,::Error>, mut self,
|
||||
numeric!(chain!(
|
||||
mut acc: call_m!(self.xor) ~
|
||||
many0!(
|
||||
tap!(or: preceded!(p!("|"), call_m!(self.xor)) => acc |= &or)
|
||||
),
|
||||
|| { return acc }
|
||||
numeric!(do_parse!(
|
||||
acc: call_m!(self.xor) >>
|
||||
res: fold_many0!(
|
||||
preceded!(p!("|"), call_m!(self.xor)),
|
||||
acc,
|
||||
|mut acc, val: EvalResult| {
|
||||
acc |= &val;
|
||||
acc
|
||||
}
|
||||
) >> (res)
|
||||
))
|
||||
);
|
||||
|
||||
|
@ -390,8 +441,8 @@ impl<'a> PRef<'a> {
|
|||
|
||||
method!(expr<PRef<'a>,&[Token],EvalResult,::Error>, mut self,
|
||||
alt!(
|
||||
delimited!(p!("("),call_m!(self.expr),p!(")")) |
|
||||
call_m!(self.numeric_expr) |
|
||||
delimited!(p!("("),call_m!(self.expr),p!(")")) |
|
||||
call_m!(self.concat_str) |
|
||||
call_m!(self.literal) |
|
||||
call_m!(self.identifier)
|
||||
|
@ -474,3 +525,54 @@ pub fn expr<'a>(input: &'a [Token]) -> CResult<'a,EvalResult> {
|
|||
pub fn macro_definition<'a>(input: &'a [Token]) -> CResult<'a,(&'a [u8],EvalResult)> {
|
||||
IdentifierParser::new(&HashMap::new()).macro_definition(input)
|
||||
}
|
||||
|
||||
named_attr!(
|
||||
/// Parse a functional macro declaration from a list of tokens.
|
||||
///
|
||||
/// Returns the identifier for the macro and the argument list (in order). The
|
||||
/// input should not include `#define`. The actual definition is not parsed and
|
||||
/// may be obtained from the unparsed data returned.
|
||||
///
|
||||
/// Returns an error if the input is not a functional macro or if the token
|
||||
/// stream contains comments.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use cexpr::expr::{IdentifierParser, EvalResult, fn_macro_declaration};
|
||||
/// use cexpr::assert_full_parse;
|
||||
/// use cexpr::token::Kind::*;
|
||||
/// use cexpr::token::Token;
|
||||
///
|
||||
/// // #define SUFFIX(arg) arg "suffix"
|
||||
/// let tokens = vec![
|
||||
/// (Identifier, &b"SUFFIX"[..]).into(),
|
||||
/// (Punctuation, &b"("[..]).into(),
|
||||
/// (Identifier, &b"arg"[..]).into(),
|
||||
/// (Punctuation, &b")"[..]).into(),
|
||||
/// (Identifier, &b"arg"[..]).into(),
|
||||
/// (Literal, &br#""suffix""#[..]).into(),
|
||||
/// ];
|
||||
///
|
||||
/// // Try to parse the functional part
|
||||
/// let (expr, (ident, args)) = fn_macro_declaration(&tokens).unwrap();
|
||||
/// assert_eq!(ident, b"SUFFIX");
|
||||
///
|
||||
/// // Create dummy arguments
|
||||
/// let idents = args.into_iter().map(|arg|
|
||||
/// (arg.to_owned(), EvalResult::Str(b"test".to_vec()))
|
||||
/// ).collect();
|
||||
///
|
||||
/// // Evaluate the macro
|
||||
/// let (_, evaluated) = assert_full_parse(IdentifierParser::new(&idents).expr(expr)).unwrap();
|
||||
/// assert_eq!(evaluated, EvalResult::Str(b"testsuffix".to_vec()));
|
||||
/// ```
|
||||
,pub fn_macro_declaration<&[Token],(&[u8],Vec<&[u8]>),::Error>,
|
||||
pair!(
|
||||
typed_token!(Identifier),
|
||||
delimited!(
|
||||
p!("("),
|
||||
separated_list!(p!(","), typed_token!(Identifier)),
|
||||
p!(")")
|
||||
)
|
||||
)
|
||||
);
|
||||
|
|
|
@ -24,6 +24,8 @@ use nom::*;
|
|||
pub enum Error {
|
||||
/// Expected the specified token
|
||||
ExactToken(token::Kind,&'static [u8]),
|
||||
/// Expected one of the specified tokens
|
||||
ExactTokens(token::Kind,&'static [&'static str]),
|
||||
/// Expected a token of the specified kind
|
||||
TypedToken(token::Kind),
|
||||
/// An unknown identifier was encountered
|
||||
|
|
|
@ -83,34 +83,6 @@ impl Into<Vec<u8>> for CChar {
|
|||
// ======== macros that shouldn't be necessary ========
|
||||
// ====================================================
|
||||
|
||||
fn split_off_prefix<'a,T>(full: &'a [T], suffix: &'a [T]) -> &'a [T] {
|
||||
let n=::std::mem::size_of::<T>();
|
||||
let start=full.as_ptr() as usize;
|
||||
let end=start+(full.len()*n);
|
||||
let cur=suffix.as_ptr() as usize;
|
||||
assert!(start<=cur && cur<=end);
|
||||
&full[..(cur-start)/n]
|
||||
}
|
||||
|
||||
// There is a HORRIBLE BUG in nom's recognize!
|
||||
// https://github.com/Geal/nom/issues/278
|
||||
#[macro_export]
|
||||
macro_rules! my_recognize (
|
||||
($i:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
{
|
||||
match $submac!($i, $($args)*) {
|
||||
IResult::Done(i,_) => IResult::Done(i, split_off_prefix($i,i)),
|
||||
IResult::Error(e) => IResult::Error(e),
|
||||
IResult::Incomplete(i) => IResult::Incomplete(i)
|
||||
}
|
||||
}
|
||||
);
|
||||
($i:expr, $f:expr) => (
|
||||
my_recognize!($i, call!($f))
|
||||
);
|
||||
);
|
||||
|
||||
|
||||
macro_rules! force_type (
|
||||
($input:expr,IResult<$i:ty,$o:ty,$e:ty>) => (IResult::Error::<$i,$o,$e>(Err::Position(ErrorKind::Fix,$input)))
|
||||
);
|
||||
|
@ -171,8 +143,8 @@ fn c_unicode_escape(n: Vec<u8>) -> Option<CChar> {
|
|||
|
||||
named!(escaped_char<CChar>,
|
||||
preceded!(char!('\\'),alt!(
|
||||
map!(one_of!(br#"'"?\"#),CChar::Char) |
|
||||
map!(one_of!(b"abfnrtv"),escape2char) |
|
||||
map!(one_of!(r#"'"?\"#),CChar::Char) |
|
||||
map!(one_of!("abfnrtv"),escape2char) |
|
||||
map_opt!(many_m_n!(1,3,octal),|v|c_raw_escape(v,8)) |
|
||||
map_opt!(preceded!(char!('x'),many1!(hexadecimal)),|v|c_raw_escape(v,16)) |
|
||||
map_opt!(preceded!(char!('u'),many_m_n!(4,4,hexadecimal)),c_unicode_escape) |
|
||||
|
@ -200,13 +172,10 @@ named!(c_char<CChar>,
|
|||
named!(c_string<Vec<u8> >,
|
||||
delimited!(
|
||||
alt!( preceded!(c_width_prefix,char!('"')) | char!('"') ),
|
||||
chain!(
|
||||
mut vec: value!(vec![]) ~
|
||||
many0!(alt!(
|
||||
map!(tap!(c: escaped_char => { let v: Vec<u8>=c.into(); vec.extend_from_slice(&v) } ),|_|()) |
|
||||
map!(tap!(s: is_not!(b"\"") => vec.extend_from_slice(s) ),|_|())
|
||||
)),
|
||||
||{return vec}
|
||||
fold_many0!(
|
||||
alt!(map!(escaped_char, |c:CChar| c.into()) | map!(is_not!("\""), |c: &[u8]| c.into())),
|
||||
Vec::new(),
|
||||
|mut v: Vec<u8>, res:Vec<u8>| { v.extend_from_slice(&res); v }
|
||||
),
|
||||
char!('"')
|
||||
)
|
||||
|
@ -240,11 +209,11 @@ named!(float_exp<(Option<u8>,Vec<u8>)>,preceded!(byte!(b'e'|b'E'),pair!(opt!(byt
|
|||
|
||||
named!(c_float<f64>,
|
||||
map_opt!(alt!(
|
||||
terminated!(my_recognize!(tuple!(many1!(decimal),byte!(b'.'),many0!(decimal))),opt!(float_width)) |
|
||||
terminated!(my_recognize!(tuple!(many0!(decimal),byte!(b'.'),many1!(decimal))),opt!(float_width)) |
|
||||
terminated!(my_recognize!(tuple!(many0!(decimal),opt!(byte!(b'.')),many1!(decimal),float_exp)),opt!(float_width)) |
|
||||
terminated!(my_recognize!(tuple!(many1!(decimal),opt!(byte!(b'.')),many0!(decimal),float_exp)),opt!(float_width)) |
|
||||
terminated!(my_recognize!(many1!(decimal)),float_width)
|
||||
terminated!(recognize!(tuple!(many1!(decimal),byte!(b'.'),many0!(decimal))),opt!(float_width)) |
|
||||
terminated!(recognize!(tuple!(many0!(decimal),byte!(b'.'),many1!(decimal))),opt!(float_width)) |
|
||||
terminated!(recognize!(tuple!(many0!(decimal),opt!(byte!(b'.')),many1!(decimal),float_exp)),opt!(float_width)) |
|
||||
terminated!(recognize!(tuple!(many1!(decimal),opt!(byte!(b'.')),many0!(decimal),float_exp)),opt!(float_width)) |
|
||||
terminated!(recognize!(many1!(decimal)),float_width)
|
||||
),|v|str::from_utf8(v).ok().and_then(|i|f64::from_str(i).ok()))
|
||||
);
|
||||
|
||||
|
|
|
@ -24,6 +24,15 @@ pub struct Token {
|
|||
pub raw: Box<[u8]>,
|
||||
}
|
||||
|
||||
impl<'a> From<(Kind, &'a [u8])> for Token {
|
||||
fn from((kind, value): (Kind, &'a [u8])) -> Token {
|
||||
Token {
|
||||
kind: kind,
|
||||
raw: value.to_owned().into_boxed_slice()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove all comment tokens from a vector of tokens
|
||||
pub fn remove_comments(v: &mut Vec<Token>) -> &mut Vec<Token> {
|
||||
v.retain(|t|t.kind!=Kind::Comment);
|
||||
|
|
|
@ -14,25 +14,45 @@ use std::collections::HashMap;
|
|||
|
||||
use clang_sys::*;
|
||||
use cexpr::token::Token;
|
||||
use cexpr::expr::{IdentifierParser,EvalResult};
|
||||
use cexpr::expr::{IdentifierParser,EvalResult,fn_macro_declaration};
|
||||
use cexpr::literal::CChar;
|
||||
use cexpr::assert_full_parse;
|
||||
|
||||
// main testing routine
|
||||
fn test_definition(ident: Vec<u8>, tokens: &[Token], idents: &mut HashMap<Vec<u8>,EvalResult>) -> bool {
|
||||
fn bytes_to_int(value: &[u8]) -> Option<EvalResult> {
|
||||
str::from_utf8(value).ok()
|
||||
.map(|s|s.replace("n","-"))
|
||||
.map(|s|s.replace("_",""))
|
||||
.and_then(|v|i64::from_str(&v).ok())
|
||||
.map(::std::num::Wrapping)
|
||||
.map(Int)
|
||||
}
|
||||
|
||||
use cexpr::expr::EvalResult::*;
|
||||
|
||||
let display_name=String::from_utf8_lossy(&ident).into_owned();
|
||||
|
||||
let functional;
|
||||
let test={
|
||||
// Split name such as Str_test_string into (Str,test_string)
|
||||
let pos=ident.iter().position(|c|*c==b'_').expect(&format!("Invalid definition in testcase: {}",display_name));
|
||||
let expected=&ident[..pos];
|
||||
let value=&ident[(pos+1)..];
|
||||
let mut expected=&ident[..pos];
|
||||
let mut value=&ident[(pos+1)..];
|
||||
|
||||
functional=expected==b"Fn";
|
||||
|
||||
if functional {
|
||||
let ident=value;
|
||||
let pos=ident.iter().position(|c|*c==b'_').expect(&format!("Invalid definition in testcase: {}",display_name));
|
||||
expected=&ident[..pos];
|
||||
value=&ident[(pos+1)..];
|
||||
}
|
||||
|
||||
if expected==b"Str" {
|
||||
Some(Str(value.to_owned()))
|
||||
} else if expected==b"Int" {
|
||||
str::from_utf8(value).ok().map(|s|s.replace("n","-")).and_then(|v|i64::from_str(&v).ok()).map(::std::num::Wrapping).map(Int)
|
||||
bytes_to_int(value)
|
||||
} else if expected==b"Float" {
|
||||
str::from_utf8(value).ok().map(|s|s.replace("n","-").replace("p",".")).and_then(|v|f64::from_str(&v).ok()).map(Float)
|
||||
} else if expected==b"CharRaw" {
|
||||
|
@ -44,8 +64,34 @@ fn test_definition(ident: Vec<u8>, tokens: &[Token], idents: &mut HashMap<Vec<u8
|
|||
}.expect(&format!("Invalid definition in testcase: {}",display_name))
|
||||
};
|
||||
|
||||
match IdentifierParser::new(idents).macro_definition(&tokens) {
|
||||
cexpr::nom::IResult::Done(_,(_,val)) => {
|
||||
let result = if functional {
|
||||
let mut fnidents;
|
||||
let expr_tokens;
|
||||
match fn_macro_declaration(&tokens) {
|
||||
cexpr::nom::IResult::Done(rest,(_,args)) => {
|
||||
fnidents=idents.clone();
|
||||
expr_tokens=rest;
|
||||
for arg in args {
|
||||
let val = match test {
|
||||
Int(_) => bytes_to_int(&arg),
|
||||
Str(_) => Some(Str(arg.to_owned())),
|
||||
_ => unimplemented!()
|
||||
}.expect(&format!("Invalid argument in functional macro testcase: {}",display_name));
|
||||
fnidents.insert(arg.to_owned(), val);
|
||||
}
|
||||
},
|
||||
e => {
|
||||
println!("Failed test for {}, unable to parse functional macro declaration: {:?}",display_name,e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
assert_full_parse(IdentifierParser::new(&fnidents).expr(&expr_tokens))
|
||||
} else {
|
||||
IdentifierParser::new(idents).macro_definition(&tokens).map(|(_,val)|val)
|
||||
};
|
||||
|
||||
match result {
|
||||
cexpr::nom::IResult::Done(_,val) => {
|
||||
if val==test {
|
||||
if let Some(_)=idents.insert(ident,val) {
|
||||
panic!("Duplicate definition for testcase: {}",display_name);
|
||||
|
@ -56,7 +102,7 @@ fn test_definition(ident: Vec<u8>, tokens: &[Token], idents: &mut HashMap<Vec<u8
|
|||
false
|
||||
}
|
||||
},
|
||||
e @ _ => {
|
||||
e => {
|
||||
if test==Invalid {
|
||||
true
|
||||
} else {
|
||||
|
@ -110,9 +156,9 @@ unsafe fn location_in_scope(r: CXSourceRange) -> bool {
|
|||
&& file.0!=ptr::null_mut()
|
||||
}
|
||||
|
||||
fn test_file(file: &str) -> bool {
|
||||
let mut idents=HashMap::new();
|
||||
let mut all_succeeded=true;
|
||||
/// tokenize_range_adjust can be used to work around LLVM bug 9069
|
||||
/// https://bugs.llvm.org//show_bug.cgi?id=9069
|
||||
fn file_visit_macros<F: FnMut(Vec<u8>, Vec<Token>)>(file: &str, tokenize_range_adjust: bool, mut visitor: F) {
|
||||
unsafe {
|
||||
let tu={
|
||||
let index=clang_createIndex(true as _, false as _);
|
||||
|
@ -132,7 +178,7 @@ fn test_file(file: &str) -> bool {
|
|||
if cur.kind==CXCursor_MacroDefinition {
|
||||
let mut range=clang_getCursorExtent(cur);
|
||||
if !location_in_scope(range) { return CXChildVisit_Continue }
|
||||
range.end_int_data-=1; // clang bug for macros only
|
||||
range.end_int_data-=if tokenize_range_adjust { 1 } else { 0 };
|
||||
let mut token_ptr=ptr::null_mut();
|
||||
let mut num=0;
|
||||
clang_tokenize(tu,range,&mut token_ptr,&mut num);
|
||||
|
@ -146,16 +192,44 @@ fn test_file(file: &str) -> bool {
|
|||
}
|
||||
).collect();
|
||||
clang_disposeTokens(tu,token_ptr,num);
|
||||
all_succeeded&=test_definition(clang_str_to_vec(clang_getCursorSpelling(cur)),&tokens,&mut idents);
|
||||
visitor(clang_str_to_vec(clang_getCursorSpelling(cur)),tokens)
|
||||
}
|
||||
}
|
||||
CXChildVisit_Continue
|
||||
});
|
||||
clang_disposeTranslationUnit(tu);
|
||||
};
|
||||
}
|
||||
|
||||
fn test_file(file: &str) -> bool {
|
||||
let mut idents=HashMap::new();
|
||||
let mut all_succeeded=true;
|
||||
file_visit_macros(file, fix_bug_9069(), |ident, tokens| all_succeeded&=test_definition(ident, &tokens, &mut idents));
|
||||
all_succeeded
|
||||
}
|
||||
|
||||
fn fix_bug_9069() -> bool {
|
||||
fn check_bug_9069() -> bool {
|
||||
let mut token_sets = vec![];
|
||||
file_visit_macros("tests/input/test_llvm_bug_9069.h", false, |ident, tokens| {
|
||||
assert_eq!(&ident, b"A");
|
||||
token_sets.push(tokens);
|
||||
});
|
||||
assert_eq!(token_sets.len(), 2);
|
||||
token_sets[0] != token_sets[1]
|
||||
}
|
||||
|
||||
use std::sync::{Once, ONCE_INIT};
|
||||
use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
|
||||
|
||||
static CHECK_FIX: Once = ONCE_INIT;
|
||||
static FIX: AtomicBool = ATOMIC_BOOL_INIT;
|
||||
|
||||
CHECK_FIX.call_once(|| FIX.store(check_bug_9069(), Ordering::SeqCst));
|
||||
|
||||
FIX.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
macro_rules! test_file {
|
||||
($f:ident) => {
|
||||
#[test] fn $f() {
|
||||
|
|
|
@ -10,5 +10,17 @@
|
|||
#define Int_16 (((1)<<4ULL))/*comment*/
|
||||
#define Int_13 1|8^6&2<<1
|
||||
|
||||
#define Int_47 32|15
|
||||
#define Int_38 (32|15)^9
|
||||
#define Int_6 ((32|15)^9)&7
|
||||
#define Int_12 (((32|15)^9)&7)<<1
|
||||
#define Int_17 ((((32|15)^9)&7)<<1)+5
|
||||
#define Int_15 (((((32|15)^9)&7)<<1)+5)-2
|
||||
#define Int_60 ((((((32|15)^9)&7)<<1)+5)-2)*4
|
||||
#define Int_30 (((((((32|15)^9)&7)<<1)+5)-2)*4)/2
|
||||
#define Int_39 32|15^9&7<<1+5-2*4/2
|
||||
|
||||
#define Int_n1 18446744073709551615 /*2^64-1*/
|
||||
#define Int_n9223372036854775808 9223372036854775808
|
||||
|
||||
#define Fn_Int_9(_3) _3*3
|
||||
|
|
|
@ -5,3 +5,8 @@
|
|||
#define Str_concat u"con" L"cat"
|
||||
#define Str_concat_parens ("concat" U"_parens")
|
||||
#define Str_concat_identifier (Str_concat L"_identifier")
|
||||
#define Fn_Str_no_args() "no_args"
|
||||
#define Fn_Str_no_args_concat() "no_args_" Str_concat
|
||||
#define Fn_Str_prepend_arg(arg) "prepend_" arg
|
||||
#define Fn_Str_two_args(two, args) two "_" args
|
||||
#define Fn_Str_three_args(three, _, args) three _ args
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
// The following two definitions should yield the same list of tokens.
|
||||
// If https://bugs.llvm.org//show_bug.cgi?id=9069 is not fixed, they don't.
|
||||
#define A 1
|
||||
#define A 1
|
|
@ -1 +0,0 @@
|
|||
{"files":{".travis.yml":"6d4e81838b10c5e330749857c72c2f2b1a2e575e71abcd11c094f3b612347b2a","CHANGELOG.md":"d4722e028b2a5b88c466b0d759e463b90bdcfa1b79181a1c76cd313b0a27c615","Cargo.toml":"aebcb999933c3425db85012bea19f9ce78da8e7834dbab54d4a2966e8bc62149","LICENSE":"de730187d5563a81342a3c011d968f78dff37c934fac9b3701e8c762b6118a55","src/bits.rs":"97c9148f63e175489bb6199d039c594ddc56bdf0b7491b9f38b8d74e898bca80","src/bytes.rs":"8f29b976a5e8e6500eb618a9dead7f212688ba9eb06c7066a4016e2db99fed00","src/character.rs":"9ee081f56b508212231ff70d7455b1b85ae44722a39aa60223e8cd95c6570859","src/internal.rs":"ada499b9c178be2a7f9b56319ffb10a778f25fafcda39c78d26b364d89debd72","src/lib.rs":"34efb051214acfde2053e93a7ba718a4fd41b6e0d9edd65a1737605d99b994ab","src/macros.rs":"d39ce3a2cd2b1cb9dd57ce90c06a1ca84720a2dc75e6332cffebba6086cb75d3","src/methods.rs":"24bdbcb0e3570c8bf3fa270dd8d79dd6dfcb982276c82180a89a1e73c5e38019","src/nom.rs":"b0a9c7ce0d09388179bce8f8e23bf57df76b504d925815583c249ec3fc04baab","src/regexp.rs":"8fdae52b761dbad90179e6be87e0e66357fefa34d76af541fb0fcf550fd6ec08","src/str.rs":"198fa15d45c3636289d92c0a592002a07e5a04a431e8cfdf724266e44d484be2","src/stream.rs":"c1bd5b8e7a2061ff66eb2c954033146001f1d65a26d12efa06af8cf93ffa53e4","src/util.rs":"da40ebac865d3176567d3a37b01170234398a03e938553720ce30aa1f6005b6d","tests/arithmetic.rs":"b98936b7fa0228835ca022f6db5342b72a9c01cc3f16a4e05263bbe6424ba3e9","tests/arithmetic_ast.rs":"b18b9a46ba573ae13c40a31217425f6e8cf8fade09a75cdbbfa7146ec668f0b2","tests/cross_function_backtracking.rs":"b071d13031c1f12195473186e3775943991496b10f4590db3f36d511e9f98a1c","tests/ini.rs":"776f681542028564899e55f71533b3bcda5ed1bbb971f24b5b1b9578111ba0cb","tests/ini_str.rs":"315046d9b6dc38d6d306d3562d7ac6518c9ecce9aabcc58fb80c07577ad99789","tests/issues.rs":"2193c219397b7a417cc009b72c13adc42471e7a4917a2a4009aa0fca23c6ea8c","tests/mp4.rs":"b4bf0514fd645160851cc4da9ad6bf81d571cd14865bf134837c19578caaf6e6","tests/omnom.rs":"409d2349fa24f3503bd02e0079c1554a58ce3d40dd7eb0e5d4bb63b588afdae4","tests/test1.rs":"3e0c187bad91d822ebc113eb5cf30fc6585e53a961728304ac24e05ab2123d10"},"package":"a5b8c256fd9471521bcb84c3cdba98921497f1a331cbc15b8030fc63b82050ce"}
|
|
@ -1,46 +0,0 @@
|
|||
language: rust
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libcurl4-openssl-dev
|
||||
- libelf-dev
|
||||
- libdw-dev
|
||||
|
||||
rust:
|
||||
- nightly
|
||||
- beta
|
||||
- stable
|
||||
- 1.2.0
|
||||
|
||||
before_script:
|
||||
- pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH
|
||||
|
||||
script:
|
||||
- |
|
||||
travis-cargo --only 1.2 test -- --features regexp &&
|
||||
travis-cargo --only stable test -- --features "regexp regexp_macros" &&
|
||||
travis-cargo --only beta test -- --features "regexp regexp_macros" &&
|
||||
travis-cargo --only nightly build -- --features "nightly core regexp" &&
|
||||
travis-cargo --only nightly test -- --features "regexp" &&
|
||||
travis-cargo bench &&
|
||||
travis-cargo --only stable doc -- --features "regexp"
|
||||
|
||||
after_success:
|
||||
- travis-cargo coveralls --no-sudo
|
||||
|
||||
notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://webhooks.gitter.im/e/9c035a194ac4fd4cc061
|
||||
on_success: change
|
||||
on_failure: always
|
||||
on_start: false
|
||||
|
||||
|
||||
env:
|
||||
global:
|
||||
# override the default `--features unstable` used for the nightly branch (optional)
|
||||
- TRAVIS_CARGO_NIGHTLY_FEATURE=nightly
|
||||
|
||||
sudo: false
|
|
@ -1,555 +0,0 @@
|
|||
# Change Log
|
||||
|
||||
## [Unreleased][unreleased]
|
||||
|
||||
### Changed
|
||||
|
||||
## 1.2.4 - 2016-07-20
|
||||
|
||||
### Thanks
|
||||
- @Phlosioneer for documentation fixes
|
||||
- @sourrust for fixing offsets in `take_bits!`
|
||||
- @ChrisMacNaughton for the XFS crate
|
||||
- @pwoolcoc for `rest_s`
|
||||
- @fitzgen for more `IResult` methods
|
||||
- @gtors for the negative lookahead feature
|
||||
- @frk1 and @jeandudey for little endian float parsing
|
||||
- @jethrogb for fixing input usage in `many1`
|
||||
- @acatton for beating me at nom golf :D
|
||||
|
||||
### Added
|
||||
- the `rest_s` method on `IResult` returns the remaining `&str` input
|
||||
- `unwrap_err` and `unwrap_inc` methods on `IResult`
|
||||
- `not!` will peek at the input and return `Done` if the underlying parser returned `Error` or `Incomplete`, without consuming the input
|
||||
- `le_f32` and `le_f64` parse little endian floating point numbers (IEEE 754)
|
||||
-
|
||||
|
||||
### Fixed
|
||||
- documentation fixes
|
||||
- `take_bits!` is now more precise
|
||||
- `many1` inccorectly used the `len` function instead of `input_len`
|
||||
- the INI parser is simpler
|
||||
- `recognize!` had an early `return` taht is removed now
|
||||
|
||||
## 1.2.3 - 2016-05-10
|
||||
|
||||
### Thanks
|
||||
- @lu-zero for the contribution guidelines
|
||||
- @GuillaumeGomez for fixes on `length_bytes` and some documentation
|
||||
- @Hywan for ducomentation and test fixes
|
||||
- @Xirdus for correct trait import issues
|
||||
- @mspiegel for the new AST example
|
||||
- @cholcombe973 for adding the `cond_with_error!` combinator
|
||||
- @tstorch for refactoring `many0!`
|
||||
- @panicbit for the folding combinators
|
||||
- @evestera for `separated_list!` fixes
|
||||
- @DanielKeep for correcting some enum imports
|
||||
|
||||
### Added
|
||||
- Regular expression combinators starting with `re_bytes_` work on byte slices
|
||||
- example parsing arithmetic expressions to an AST
|
||||
- `cond_with_error!` works like `cond!` but will return `None` if the condition is false, and `Some(value)` if the underlying parser succeeded
|
||||
- `fold_many0!`, `fold_many1!` and `fold_many_m_n!` will take a parser, an initial value and a combining function, and fold over the successful applications of the parser
|
||||
|
||||
### Fixed
|
||||
- `length_bytes!` converts the result of its child parser to usize
|
||||
- `take_till!` now imports `InputLength` instead of assuming it's in scope
|
||||
- `separated_list!` and `separated_nonempty_list!` will not consume the separator if there's no following successfully parsed value
|
||||
- no more warnings on build
|
||||
|
||||
### Changed
|
||||
- simpler implementation of `many0!`
|
||||
|
||||
## 1.2.2 - 2016-03-09
|
||||
|
||||
### Thanks
|
||||
- @conradev for fixing take_until_s!`
|
||||
- @GuillaumeGomez for some documentation fixes
|
||||
- @frewsxcv for some documentation fixes
|
||||
- @tstorch for some test refactorings
|
||||
|
||||
### Added
|
||||
- `nom::Err` now implements `std::error::Error`
|
||||
|
||||
### Fixed
|
||||
- `hex_u32` does not parses more than 8 chars now
|
||||
- `take_while!` and `take_while1!` will not perturb the behaviour of `recognize!` anymore
|
||||
|
||||
## 1.2.1 - 2016-02-23
|
||||
|
||||
### Thanks
|
||||
- @sourrust for adding methods to `IResult`
|
||||
- @tstorch for the test refactoring, and for adding methods to `IResult` and `Needed`
|
||||
- @joelself for fixing the method system
|
||||
|
||||
### Added
|
||||
|
||||
- mapping methods over `IResult` and `Needed`
|
||||
|
||||
### Changed
|
||||
|
||||
- `apply_rf` is renamed to `apply_m`. This will not warrant a major version, since it is part missing from the methods feture added in the 1.2.0 release
|
||||
- the `regexp_macros` feature that used `regex!` to precompile regular expressions has been replaced by the normal regex engine combined with `lazy_static`
|
||||
|
||||
### Fixed
|
||||
|
||||
- when a parser or combinator was returning an empty buffer as remaining part, it was generating one from a static empty string. This was messing with buffer offset calculation. Now, that empty slice is taken like this: `&input[input.len()..]`.
|
||||
- The `regexp_macros` and `no_std` feature build again and are now tested with Travis CI
|
||||
|
||||
## 1.2.0 - 2016-02-08
|
||||
|
||||
### Thanks
|
||||
- @zentner-kyle for type inference fixes
|
||||
- @joelself for his work on `&str` parsing and method parsers
|
||||
- @GuillaumeGomez for implementing methods on `IResult`
|
||||
- @dirk for the `alt_complete!` combinator
|
||||
- @tstorch for a lot of refactoring work and unit tests additions
|
||||
- @jansegre for the hex digit parsers
|
||||
- @belgum for some documentation fixes
|
||||
- @lwandrebeck for some documentation fixes and code fixes in `hex_digit`
|
||||
|
||||
### Added
|
||||
- `take_until_and_consume_s!` for consumption of string data until a tag
|
||||
- more function patterns in `named!`. The error type can now be specified
|
||||
- `alt_complete!` works like the `alt!` combinator, but tries the next branch if the current one returned `Incomplete`, instead of returning directly
|
||||
- more unit tests for a lot of combinators
|
||||
- hexadecimal digit parsers
|
||||
- the `tuple!` combinator takes a list of parsers as argument, and applies them serially on the input. If all of them are successful, it willr eturn a tuple accumulating all the values. This combinator will (hopefully) replace most uses of `chain!`
|
||||
- parsers can now be implemented as a method for a struct thanks to the `method!`, `call_m!` and `apply_rf!` combinators
|
||||
|
||||
### Fixed
|
||||
- there were type inference issues in a few combinators. They will now be easier to compile
|
||||
- `peek!` compilation with bare functions
|
||||
- `&str` parsers were splitting data at the byte level, not at the char level, which can result in inconsistencies in parsing UTF-8 characters. They now use character indexes
|
||||
- some method implementations were missing on `ÌResult<I,O,E>` (with specified error type instead of implicit)
|
||||
|
||||
## 1.1.0 - 2016-01-01
|
||||
|
||||
This release adds a lot of features related to `&str` parsing. The previous versions
|
||||
were focused on `&[u8]` and bit streams parsing, but there's a need for more text
|
||||
parsing with nom. The parsing functions like `alpha`, `digit` and others will now
|
||||
accept either a `&[u8]` or a `&str`, so there is no breaking change on that part.
|
||||
|
||||
There are also a few performance improvements and documentation fixes.
|
||||
|
||||
### Thanks
|
||||
- @Binero for pushing the work on `&str` parsing
|
||||
- @meh for fixing `Option` and `Vec` imports
|
||||
- @hoodie for a documentation fix
|
||||
- @joelself for some documentation fixes
|
||||
- @vberger for his traits magic making `nom functions more generic
|
||||
|
||||
### Added
|
||||
|
||||
- string related parsers: `tag_s!`, `take_s!`, `is_a_s!`, `is_not_s!`, `take_while_s!`, `take_while1_s!`, `take_till_s!
|
||||
- `value!` is a combinator that always returns the same value. If a child parser is passed as second argument, that value is returned when the child parser succeeds
|
||||
|
||||
### Changed
|
||||
|
||||
- `tag!` will now compare even on partial input. If it expects "abcd" but receives "ef", it will now return an `Error` instead of `Incomplete`
|
||||
- `many0!` and others will preallocate a larger vector to avoid some copies and reallocations
|
||||
- `alpha`, `digit`, `alphanumeric`, `space` and `multispace` now accept as input a `&[u8]` or a `&str`. Additionally, they return an error if they receive an empty input
|
||||
- `take_while!`, `take_while1!`, `take_while_s!`, `take_while1_s!` wilreturn an error on empty input
|
||||
|
||||
### Fixed
|
||||
|
||||
- if the child parser of `many0!` or `many1!` returns `Incomplete`, it will return `Incomplete` too, possibly updating the needed size
|
||||
- `Option,` `Some`, `None` and `Vec` are now used with full path imports
|
||||
|
||||
## 1.0.1 - 2015-11-22
|
||||
|
||||
This releases makes the 1.0 version compatible with Rust 1.2 and 1.3
|
||||
|
||||
### Thanks
|
||||
- @steveklabnik for fixing lifetime issues in Producers and Consumers
|
||||
|
||||
## 1.0.0 - 2015-11-16
|
||||
|
||||
Stable release for nom. A lot of new features, a few breaking changes
|
||||
|
||||
### Thanks
|
||||
- @ahenry for macro fixes
|
||||
- @bluss for fixing documentation
|
||||
- @sourrust for cleaning code and debugging the new streaming utilities
|
||||
- @meh for inline optimizations
|
||||
- @ccmtaylor for fixing function imports
|
||||
- @soro for improvements to the streaming utilities
|
||||
- @breard-r for catching my typos
|
||||
- @nelsonjchen for catching my typos too
|
||||
- @divarvel for hex string parsers
|
||||
- @mrordinaire for the `length_bytes!` combinator
|
||||
|
||||
### Breaking changes
|
||||
- `IResult::Error` can now use custom error types, and is generic over the input type
|
||||
- Producers and consumers have been replaced. The new implementation uses less memory and integrates more with parsers
|
||||
- `nom::ErrorCode` is now `nom::ErrorKind`
|
||||
- `filter!` has been renamed to `take_while!`
|
||||
- `chain!` will count how much data is consumed and use that number to calculate how much data is needed if a parser returned `Incomplete`
|
||||
- `alt!` returns `Incomplete` if a child parser returned `Incomplete`, instead of skipping to the next parser
|
||||
- `IResult` does not require a lifetime tag anymore, yay!
|
||||
|
||||
### Added
|
||||
|
||||
- `complete!` will return an error if the child parser returned `Incomplete`
|
||||
- `add_error!` will wrap an error, but allow backtracking
|
||||
- `hex_u32` parser
|
||||
|
||||
### Fixed
|
||||
- the behaviour around `Incomplete` is better for most parsers now
|
||||
|
||||
## 0.5.0 - 2015-10-16
|
||||
|
||||
This release fixes a few issues and stabilizes the code.
|
||||
|
||||
### Thanks
|
||||
- @nox for documentation fixes
|
||||
- @daboross for linting fixes
|
||||
- @ahenry for fixing `tap!` and extending `dbg!` and `dbg_dmp!`
|
||||
- @bluss for tracking down and fixing issues with unsafe code
|
||||
- @meh for inlining parser functions
|
||||
- @ccmtaylor for fixing import of `str::from_utf8`
|
||||
|
||||
### Fixed
|
||||
- `tap!`, `dbg!` and `dbg_dmp!` now accept function parameters
|
||||
|
||||
### Changed
|
||||
- the type used in `count_fixed!` must be `Copy`
|
||||
- `chain!` calculates how much data is needed if one of the parsers returns `Incomplete
|
||||
- optional parsers in `chain!` can return `Incomplete`
|
||||
|
||||
## 0.4.0 - 2015-09-08
|
||||
|
||||
Considering the number of changes since the last release, this version can contain breaking changes, so the version number becomes 0.4.0. A lot of new features and performance improvements!
|
||||
|
||||
### Thanks
|
||||
- @frewsxcv for documentation fixes
|
||||
- @ngrewe for his work on producers and consumers
|
||||
- @meh for fixes on `chain!` and for the `rest` parser
|
||||
- @daboross for refactoring `many0!` and `many1!`
|
||||
- @aleksander for the `switch!` combinator idea
|
||||
- @TechnoMancer for his help with bit level parsing
|
||||
- @sxeraverx for pointing out a bug in `is_a!`
|
||||
|
||||
### Fixed
|
||||
- `count_fixed!` must take an explicit type as argument to generate the fixed-size array
|
||||
- optional parsing behaviour in `chain!`
|
||||
- `count!` can take 0 elements
|
||||
- `is_a!` and `is_not!` can now consume the whole input
|
||||
|
||||
### Added
|
||||
- it is now possible to seek to the end of a `MemProducer`
|
||||
- `opt!` returns `Done(input, None)` if `the child parser returned `Incomplete`
|
||||
- `rest` will return the remaining input
|
||||
- consumers can now seek to and from the end of input
|
||||
- `switch!` applies a first parser then matches on its result to choose the next parser
|
||||
- bit-level parsers
|
||||
- character-level parsers
|
||||
- regular expression parsers
|
||||
- implementation of `take_till!`, `take_while!` and `take_while1!`
|
||||
|
||||
### Changed
|
||||
- `alt!` can return `Incomplete`
|
||||
- the error analysis functions will now take references to functions instead of moving them
|
||||
- performance improvements on producers
|
||||
- performance improvement for `filter!`
|
||||
- performance improvement for `count!`: a `Vec` of the right size is directly allocated
|
||||
|
||||
## 0.3.11 - 2015-08-04
|
||||
|
||||
### Thanks
|
||||
- @bluss for remarking that the crate included random junk lying non commited in my local repository
|
||||
|
||||
### Fixed
|
||||
- cleanup of my local repository will ship less files in the crates, resulting in a smaller download
|
||||
|
||||
## 0.3.10 - 2015-08-03
|
||||
|
||||
### Added
|
||||
|
||||
- `bits!` for bit level parsing. It indicates that all child parsers will take a `(&[u8], usize)`as input, with the second parameter indicating the bit offset in the first byte. This allows viewing a byte slice as a bit stream. Most combinators can be used directly under `bits!`
|
||||
- `take_bits!` takes an integer type and a number of bits, consumes that number of bits and updates the offset, possibly by crossing byte boundaries
|
||||
- bit level parsers are all written in `src/bits.rs`
|
||||
|
||||
### Changed
|
||||
|
||||
- Parsers that specifically handle bytes have been moved to src/bytes.rs`. This applies to `tag!`, `is_not!`, `is_a!`, `filter!`, `take!`, `take_str!`, `take_until_and_consume!`, `take_until!`, `take_until_either_and_consume!`, `take_until_either!`
|
||||
|
||||
## 0.3.9 - 2015-07-20
|
||||
|
||||
### Thanks
|
||||
- @badboy for fixing `filter!`
|
||||
- @idmit for some documentation fixes
|
||||
|
||||
### Added
|
||||
- `opt_res!` applies a parser and transform its result in a Result. This parser never fails
|
||||
- `cond_reduce!` takes an expression as parameter, applies the parser if the expression is true, and returns an error if the expression is false
|
||||
- `tap!` pass the result of a parser to a block to manipulate it, but do not affect the parser's result
|
||||
- `AccReader` is a Read+BufRead that supports data accumulation and partial consumption. The `consume` method must be called afterwardsto indicate how much was consumed
|
||||
- Arithmetic expression evaluation and parsing example
|
||||
- `u16!`, `u32!`, `u64!`, `i16!`, `i32!`, `i64!` take an expression as parameter, if the expression is true, apply the big endian integer parser, if false, the little endian version
|
||||
- type information for combinators. This will make the documentation a bit easier to navigate
|
||||
|
||||
### Fixed
|
||||
- `map_opt!` and `map_res!` had issues with argument order due to bad macros
|
||||
- `delimited!` did not compile for certain combinations of arguments
|
||||
- `filter!` did not return a byte slice but a fixed array
|
||||
|
||||
## 0.3.8 - 2015-07-03
|
||||
|
||||
### Added
|
||||
- code coverage is now calculated automatically on Travis CI
|
||||
- `Stepper`: wrap a `Producer`, and call the method `step` with a parser. This method will buffer data if there is not enough, apply the parser if there is, and keep the rest of the input in memory for the next call
|
||||
- `ReadProducer`: takes something implementing `Read`, and makes a `Producer` out of it
|
||||
|
||||
### Fixed
|
||||
- the combinators `separated_pair!` and `delimited!` did not work because an implementation macro was not exported
|
||||
- if a `MemProducer` reached its end, it should always return `Eof`
|
||||
- `map!` had issues with argument matching
|
||||
|
||||
## 0.3.7 - 2015-06-24
|
||||
|
||||
### Added
|
||||
- `expr_res!` and `expr_opt!` evaluate an expression returning a Result or Opt and convert it to IResult
|
||||
- `AsBytes` is implemented for fixed size arrays. This allows `tag!([41u8, 42u8])`
|
||||
|
||||
### Fixed
|
||||
- `count_fixed!` argument parsing works again
|
||||
|
||||
## 0.3.6 - 2015-06-15
|
||||
|
||||
### Added
|
||||
- documentation for a few functions
|
||||
- the consumer trait now requires the `failed(&self, error_code)` method in case of parsing error
|
||||
- `named!` now handles thge alternative `named!(pub fun_name<OutputType>, ...)`
|
||||
|
||||
### Fixed
|
||||
- `filter!` now returns the whole input if the filter function never returned false
|
||||
- `take!` casts its argument as usize, so it can accepts any integer type now
|
||||
|
||||
## 0.3.5 - 2015-06-10
|
||||
|
||||
### Thanks
|
||||
- @cmr for some documentation fixes
|
||||
|
||||
### Added
|
||||
- `count_fixed!` returns a fixed array
|
||||
|
||||
### Fixed
|
||||
- `count!` is back to the previous behaviour, returning a `Vec` for sizes known at runtime
|
||||
|
||||
### Changed
|
||||
- functions and traits exported from `nom::util` are now directly in `nom::`
|
||||
|
||||
## 0.3.4 - 2015-06-09
|
||||
|
||||
### Thanks
|
||||
- @andrew-d for fixes on `cond!`
|
||||
- @keruspe for features in `chain!`
|
||||
|
||||
### Added
|
||||
- `chain!` can now have mutable fields
|
||||
|
||||
### Fixed
|
||||
- `cond!` had an infinite macro recursion
|
||||
|
||||
### Changed
|
||||
- `chain!` generates less code now. No apprent compilation time improvement
|
||||
|
||||
## 0.3.3 - 2015-06-09
|
||||
|
||||
### Thanks
|
||||
- @andrew-d for the little endian signed integer parsers
|
||||
- @keruspe for fixes on `count!`
|
||||
|
||||
### Added
|
||||
- `le_i8`, `le_i16`, `le_i32`, `le_i64`: little endian signed integer parsers
|
||||
|
||||
### Changed
|
||||
- the `alt!` parser compiles much faster, even with more than 8 branches
|
||||
- `count!` can now return a fixed size array instead of a growable vector
|
||||
|
||||
## 0.3.2 - 2015-05-31
|
||||
|
||||
### Thanks
|
||||
- @keruspe for the `take_str` parser and the function application combinator
|
||||
|
||||
### Added
|
||||
- `take_str!`: takes the specified number of bytes and return a UTF-8 string
|
||||
- `apply!`: do partial application on the parameters of a function
|
||||
|
||||
### Changed
|
||||
- `Needed::Size` now contains a `usize` instead of a `u32`
|
||||
|
||||
## 0.3.1 - 2015-05-21
|
||||
|
||||
### Thanks
|
||||
- @divarvel for the big endian signed integer parsers
|
||||
|
||||
### Added
|
||||
- `be_i8`, `be_i16`, `be_i32`, `be_i64`: big endian signed integer parsers
|
||||
- the `core` feature can be passed to cargo to build with `no_std`
|
||||
- colored hexdump can be generated from error chains
|
||||
|
||||
## 0.3.0 - 2015-05-07
|
||||
|
||||
### Thanks
|
||||
- @filipegoncalves for some documentation and the new eof parser
|
||||
- @CrimsonVoid for putting fully qualified types in the macros
|
||||
- @lu_zero for some documentation fixes
|
||||
|
||||
### Added
|
||||
- new error types that can contain an error code, an input slice, and a list of following errors
|
||||
- `error!` will cut backtracking and return directly from the parser, with a specified error code
|
||||
- `eof` parser, successful if there is no more input
|
||||
- specific error codes for the parsers provided by nom
|
||||
|
||||
### Changed
|
||||
- fully qualified types in macros. A lot of imports are not needed anymore
|
||||
|
||||
### Removed
|
||||
- `FlatMap`, `FlatpMapOpt` and `Functor` traits (replaced by `map!`, `map_opt!` and `map_res!`)
|
||||
|
||||
## 0.2.2 - 2015-04-12
|
||||
|
||||
### Thanks
|
||||
- @filipegoncalves and @thehydroimpulse for debugging an infinite loop in many0 and many1
|
||||
- @thehydroimpulse for suggesting public named parsers
|
||||
- @skade for removing the dependency on the collections gate
|
||||
|
||||
### Added
|
||||
- `named!` can now declare public functions like this: `named!(pub tst, tag!("abcd"));`
|
||||
- `pair!(X,Y)` returns a tuple `(x, y)`
|
||||
- `separated_pair!(X, sep, Y)` returns a tuple `(x, y)`
|
||||
- `preceded!(opening, X)` returns `x`
|
||||
- `terminated!(X, closing)` returns `x`
|
||||
- `delimited(opening, X, closing)` returns `x`
|
||||
- `separated_list(sep, X)` returns a `Vec<X>`
|
||||
- `separated_nonempty_list(sep, X)` returns a `Vec<X>` of at list one element
|
||||
|
||||
### Changed
|
||||
- `many0!` and `many1!` forbid parsers that do not consume input
|
||||
- `is_a!`, `is_not!`, `alpha`, `digit`, `space`, `multispace` will now return an error if they do not consume at least one byte
|
||||
|
||||
## 0.2.1 - 2015-04-04
|
||||
|
||||
### Thanks
|
||||
- @mtsr for catching the remaining debug println!
|
||||
- @jag426 who killed a lot of warnings
|
||||
- @skade for removing the dependency on the core feature gate
|
||||
|
||||
|
||||
### Added
|
||||
- little endian unsigned int parsers le_u8, le_u16, le_u32, le_u64
|
||||
- `count!` to apply a parser a specified number of times
|
||||
- `cond!` applies a parser if the condition is met
|
||||
- more parser development tools in `util::*`
|
||||
|
||||
### Fixed
|
||||
- in one case, `opt!` would not compile
|
||||
|
||||
### Removed
|
||||
- most of the feature gates are now removed. The only one still needed is `collections`
|
||||
|
||||
## 0.2.0 - 2015-03-24
|
||||
*works with `rustc 1.0.0-dev (81e2396c7 2015-03-19) (built 2015-03-19)`*
|
||||
|
||||
### Thanks
|
||||
- Ryman for the AsBytes implementation
|
||||
- jag426 and jaredly for documentation fixes
|
||||
- eternaleye on #rust IRC for his help on the new macro syntax
|
||||
|
||||
### Changed
|
||||
- the AsBytes trait improves readability, no more b"...", but "..." instead
|
||||
- Incomplete will now hold either Needed;;Unknown, or Needed::Size(u32). Matching on Incomplete without caring for the value is done with `Incomplete(_)`, but if more granularity is mandatory, `Needed` can be matched too
|
||||
- `alt!` can pass the result of the parser to a closure
|
||||
- the `take_*` macros changed behaviour, the default case is now not to consume the separator. The macros have been renamed as follows: `take_until!` -> `take_until_and_consume!`, `take_until_and_leave!` -> `take_until!`, `take_until_either_and_leave!` -> `take_until_either!`, `take_until_either!` -> `take_until_either_and_consume!`
|
||||
|
||||
### Added
|
||||
- `peek!` macro: matches the future input but does not consume it
|
||||
- `length_value!` macro: the first argument is a parser returning a `n` that can cast to usize, then applies the second parser `n` times. The macro has a variant with a third argument indicating the expected input size for the second parser
|
||||
- benchmarks are available at https://github.com/Geal/nom_benchmarks
|
||||
- more documentation
|
||||
- **Unnamed parser syntax**: warning, this is a breaking change. With this new syntax, the macro combinators do not generate functions anymore, they create blocks. That way, they can be nested, for better readability. The `named!` macro is provided to create functions from parsers. Please be aware that nesting parsers comes with a small cost of compilation time, negligible in most cases, but can quickly get to the minutes scale if not careful. If this happens, separate your parsers in multiple subfunctions.
|
||||
- `named!`, `closure!` and `call!` macros used to support the unnamed syntax
|
||||
- `map!`, `map_opt!` and `map_res!` to combine a parser with a normal function, transforming the input directly, or returning an `Option` or `Result`
|
||||
|
||||
### Fixed
|
||||
- `is_a!` is now working properly
|
||||
|
||||
### Removed
|
||||
- the `o!` macro does less than `chain!`, so it has been removed
|
||||
- the `fold0!` and `fold1!` macros were too complex and awkward to use, the `many*` combinators will be useful for most uses for now
|
||||
|
||||
## 0.1.6 - 2015-02-24
|
||||
### Changed
|
||||
- consumers must have an end method that will be called after parsing
|
||||
|
||||
### Added
|
||||
- big endian unsigned int and float parsers: be_u8, be_u16, be_u32, be_u64, be_f32, be_f64
|
||||
- producers can seek
|
||||
- function and macros documentation
|
||||
- README documentation
|
||||
### Fixed
|
||||
- lifetime declarations
|
||||
- tag! can return Incomplete
|
||||
|
||||
## 0.1.5 - 2015-02-17
|
||||
### Changed
|
||||
- traits were renamed: FlatMapper -> FlatMap, Mapper -> FlatMapOpt, Mapper2 -> Functor
|
||||
|
||||
### Fixed
|
||||
- woeks with rustc f1bb6c2f4
|
||||
|
||||
## 0.1.4 - 2015-02-17
|
||||
### Changed
|
||||
- the chaining macro can take optional arguments with '?'
|
||||
|
||||
## 0.1.3 - 2015-02-16
|
||||
### Changed
|
||||
- the chaining macro now takes the closure at the end of the argument list
|
||||
|
||||
## 0.1.2 - 2015-02-16
|
||||
### Added
|
||||
- flat_map implementation for <&[u8], &[u8]>
|
||||
- chaining macro
|
||||
- partial MP4 parser example
|
||||
|
||||
|
||||
## 0.1.1 - 2015-02-06
|
||||
### Fixed
|
||||
- closure syntax change
|
||||
|
||||
## Compare code
|
||||
|
||||
* [unreleased]: https://github.com/Geal/nom/compare/1.2.4...HEAD
|
||||
* [1.2.3]: https://github.com/Geal/nom/compare/1.2.3...1.2.4
|
||||
* [1.2.3]: https://github.com/Geal/nom/compare/1.2.2...1.2.3
|
||||
* [1.2.2]: https://github.com/Geal/nom/compare/1.2.1...1.2.2
|
||||
* [1.2.1]: https://github.com/Geal/nom/compare/1.2.0...1.2.1
|
||||
* [1.2.0]: https://github.com/Geal/nom/compare/1.1.0...1.2.0
|
||||
* [1.1.0]: https://github.com/Geal/nom/compare/1.0.1...1.1.0
|
||||
* [1.0.1]: https://github.com/Geal/nom/compare/1.0.0...1.0.1
|
||||
* [1.0.0]: https://github.com/Geal/nom/compare/0.5.0...1.0.0
|
||||
* [0.5.0]: https://github.com/geal/nom/compare/0.4.0...0.5.0
|
||||
* [0.4.0]: https://github.com/geal/nom/compare/0.3.11...0.4.0
|
||||
* [0.3.11]: https://github.com/geal/nom/compare/0.3.10...0.3.11
|
||||
* [0.3.10]: https://github.com/geal/nom/compare/0.3.9...0.3.10
|
||||
* [0.3.9]: https://github.com/geal/nom/compare/0.3.8...0.3.9
|
||||
* [0.3.8]: https://github.com/Geal/nom/compare/0.3.7...0.3.8
|
||||
* [0.3.7]: https://github.com/Geal/nom/compare/0.3.6...0.3.7
|
||||
* [0.3.6]: https://github.com/Geal/nom/compare/0.3.5...0.3.6
|
||||
* [0.3.5]: https://github.com/Geal/nom/compare/0.3.4...0.3.5
|
||||
* [0.3.4]: https://github.com/Geal/nom/compare/0.3.3...0.3.4
|
||||
* [0.3.3]: https://github.com/Geal/nom/compare/0.3.2...0.3.3
|
||||
* [0.3.2]: https://github.com/Geal/nom/compare/0.3.1...0.3.2
|
||||
* [0.3.1]: https://github.com/Geal/nom/compare/0.3.0...0.3.1
|
||||
* [0.3.0]: https://github.com/Geal/nom/compare/0.2.2...0.3.0
|
||||
* [0.2.2]: https://github.com/Geal/nom/compare/0.2.1...0.2.2
|
||||
* [0.2.1]: https://github.com/Geal/nom/compare/0.2.0...0.2.1
|
||||
* [0.2.0]: https://github.com/Geal/nom/compare/0.1.6...0.2.0
|
||||
* [0.1.6]: https://github.com/Geal/nom/compare/0.1.5...0.1.6
|
||||
* [0.1.5]: https://github.com/Geal/nom/compare/0.1.4...0.1.5
|
||||
* [0.1.4]: https://github.com/Geal/nom/compare/0.1.3...0.1.4
|
||||
* [0.1.3]: https://github.com/Geal/nom/compare/0.1.2...0.1.3
|
||||
* [0.1.2]: https://github.com/Geal/nom/compare/0.1.1...0.1.2
|
||||
* [0.1.1]: https://github.com/Geal/nom/compare/0.1.0...0.1.1
|
|
@ -1,38 +0,0 @@
|
|||
[package]
|
||||
|
||||
name = "nom"
|
||||
version = "1.2.4"
|
||||
authors = [ "contact@geoffroycouprie.com" ]
|
||||
description = "A byte-oriented, zero-copy, parser combinators library"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/Geal/nom"
|
||||
readme = "README.md"
|
||||
documentation = "http://rust.unhandledexpression.com/nom/"
|
||||
keywords = ["parser", "parser-combinators", "parsing", "streaming", "bit"]
|
||||
|
||||
include = [
|
||||
"CHANGELOG.md",
|
||||
"LICENSE",
|
||||
".gitignore",
|
||||
".travis.yml",
|
||||
"Cargo.toml",
|
||||
"src/*.rs",
|
||||
"tests/*.rs"
|
||||
]
|
||||
|
||||
[features]
|
||||
core = []
|
||||
nightly = []
|
||||
default = ["stream"]
|
||||
regexp = ["regex"]
|
||||
regexp_macros = ["regexp", "lazy_static"]
|
||||
stream = []
|
||||
|
||||
[dependencies.regex]
|
||||
version = "^0.1.56"
|
||||
optional = true
|
||||
|
||||
[dependencies.lazy_static]
|
||||
version = "^0.2.1"
|
||||
optional = true
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
Copyright (c) 2015 Geoffroy Couprie
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -1 +0,0 @@
|
|||
{"files":{".travis.yml":"6d4e81838b10c5e330749857c72c2f2b1a2e575e71abcd11c094f3b612347b2a","CHANGELOG.md":"d4722e028b2a5b88c466b0d759e463b90bdcfa1b79181a1c76cd313b0a27c615","Cargo.toml":"aebcb999933c3425db85012bea19f9ce78da8e7834dbab54d4a2966e8bc62149","LICENSE":"de730187d5563a81342a3c011d968f78dff37c934fac9b3701e8c762b6118a55","src/bits.rs":"97c9148f63e175489bb6199d039c594ddc56bdf0b7491b9f38b8d74e898bca80","src/bytes.rs":"8f29b976a5e8e6500eb618a9dead7f212688ba9eb06c7066a4016e2db99fed00","src/character.rs":"9ee081f56b508212231ff70d7455b1b85ae44722a39aa60223e8cd95c6570859","src/internal.rs":"ada499b9c178be2a7f9b56319ffb10a778f25fafcda39c78d26b364d89debd72","src/lib.rs":"34efb051214acfde2053e93a7ba718a4fd41b6e0d9edd65a1737605d99b994ab","src/macros.rs":"d39ce3a2cd2b1cb9dd57ce90c06a1ca84720a2dc75e6332cffebba6086cb75d3","src/methods.rs":"24bdbcb0e3570c8bf3fa270dd8d79dd6dfcb982276c82180a89a1e73c5e38019","src/nom.rs":"b0a9c7ce0d09388179bce8f8e23bf57df76b504d925815583c249ec3fc04baab","src/regexp.rs":"8fdae52b761dbad90179e6be87e0e66357fefa34d76af541fb0fcf550fd6ec08","src/str.rs":"198fa15d45c3636289d92c0a592002a07e5a04a431e8cfdf724266e44d484be2","src/stream.rs":"c1bd5b8e7a2061ff66eb2c954033146001f1d65a26d12efa06af8cf93ffa53e4","src/util.rs":"da40ebac865d3176567d3a37b01170234398a03e938553720ce30aa1f6005b6d","tests/arithmetic.rs":"b98936b7fa0228835ca022f6db5342b72a9c01cc3f16a4e05263bbe6424ba3e9","tests/arithmetic_ast.rs":"b18b9a46ba573ae13c40a31217425f6e8cf8fade09a75cdbbfa7146ec668f0b2","tests/cross_function_backtracking.rs":"b071d13031c1f12195473186e3775943991496b10f4590db3f36d511e9f98a1c","tests/ini.rs":"776f681542028564899e55f71533b3bcda5ed1bbb971f24b5b1b9578111ba0cb","tests/ini_str.rs":"315046d9b6dc38d6d306d3562d7ac6518c9ecce9aabcc58fb80c07577ad99789","tests/issues.rs":"2193c219397b7a417cc009b72c13adc42471e7a4917a2a4009aa0fca23c6ea8c","tests/mp4.rs":"b4bf0514fd645160851cc4da9ad6bf81d571cd14865bf134837c19578caaf6e6","tests/omnom.rs":"409d2349fa24f3503bd02e0079c1554a58ce3d40dd7eb0e5d4bb63b588afdae4","tests/test1.rs":"3e0c187bad91d822ebc113eb5cf30fc6585e53a961728304ac24e05ab2123d10"},"package":"a5b8c256fd9471521bcb84c3cdba98921497f1a331cbc15b8030fc63b82050ce"}
|
|
@ -1,46 +0,0 @@
|
|||
language: rust
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libcurl4-openssl-dev
|
||||
- libelf-dev
|
||||
- libdw-dev
|
||||
|
||||
rust:
|
||||
- nightly
|
||||
- beta
|
||||
- stable
|
||||
- 1.2.0
|
||||
|
||||
before_script:
|
||||
- pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH
|
||||
|
||||
script:
|
||||
- |
|
||||
travis-cargo --only 1.2 test -- --features regexp &&
|
||||
travis-cargo --only stable test -- --features "regexp regexp_macros" &&
|
||||
travis-cargo --only beta test -- --features "regexp regexp_macros" &&
|
||||
travis-cargo --only nightly build -- --features "nightly core regexp" &&
|
||||
travis-cargo --only nightly test -- --features "regexp" &&
|
||||
travis-cargo bench &&
|
||||
travis-cargo --only stable doc -- --features "regexp"
|
||||
|
||||
after_success:
|
||||
- travis-cargo coveralls --no-sudo
|
||||
|
||||
notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://webhooks.gitter.im/e/9c035a194ac4fd4cc061
|
||||
on_success: change
|
||||
on_failure: always
|
||||
on_start: false
|
||||
|
||||
|
||||
env:
|
||||
global:
|
||||
# override the default `--features unstable` used for the nightly branch (optional)
|
||||
- TRAVIS_CARGO_NIGHTLY_FEATURE=nightly
|
||||
|
||||
sudo: false
|
|
@ -1,555 +0,0 @@
|
|||
# Change Log
|
||||
|
||||
## [Unreleased][unreleased]
|
||||
|
||||
### Changed
|
||||
|
||||
## 1.2.4 - 2016-07-20
|
||||
|
||||
### Thanks
|
||||
- @Phlosioneer for documentation fixes
|
||||
- @sourrust for fixing offsets in `take_bits!`
|
||||
- @ChrisMacNaughton for the XFS crate
|
||||
- @pwoolcoc for `rest_s`
|
||||
- @fitzgen for more `IResult` methods
|
||||
- @gtors for the negative lookahead feature
|
||||
- @frk1 and @jeandudey for little endian float parsing
|
||||
- @jethrogb for fixing input usage in `many1`
|
||||
- @acatton for beating me at nom golf :D
|
||||
|
||||
### Added
|
||||
- the `rest_s` method on `IResult` returns the remaining `&str` input
|
||||
- `unwrap_err` and `unwrap_inc` methods on `IResult`
|
||||
- `not!` will peek at the input and return `Done` if the underlying parser returned `Error` or `Incomplete`, without consuming the input
|
||||
- `le_f32` and `le_f64` parse little endian floating point numbers (IEEE 754)
|
||||
-
|
||||
|
||||
### Fixed
|
||||
- documentation fixes
|
||||
- `take_bits!` is now more precise
|
||||
- `many1` inccorectly used the `len` function instead of `input_len`
|
||||
- the INI parser is simpler
|
||||
- `recognize!` had an early `return` taht is removed now
|
||||
|
||||
## 1.2.3 - 2016-05-10
|
||||
|
||||
### Thanks
|
||||
- @lu-zero for the contribution guidelines
|
||||
- @GuillaumeGomez for fixes on `length_bytes` and some documentation
|
||||
- @Hywan for ducomentation and test fixes
|
||||
- @Xirdus for correct trait import issues
|
||||
- @mspiegel for the new AST example
|
||||
- @cholcombe973 for adding the `cond_with_error!` combinator
|
||||
- @tstorch for refactoring `many0!`
|
||||
- @panicbit for the folding combinators
|
||||
- @evestera for `separated_list!` fixes
|
||||
- @DanielKeep for correcting some enum imports
|
||||
|
||||
### Added
|
||||
- Regular expression combinators starting with `re_bytes_` work on byte slices
|
||||
- example parsing arithmetic expressions to an AST
|
||||
- `cond_with_error!` works like `cond!` but will return `None` if the condition is false, and `Some(value)` if the underlying parser succeeded
|
||||
- `fold_many0!`, `fold_many1!` and `fold_many_m_n!` will take a parser, an initial value and a combining function, and fold over the successful applications of the parser
|
||||
|
||||
### Fixed
|
||||
- `length_bytes!` converts the result of its child parser to usize
|
||||
- `take_till!` now imports `InputLength` instead of assuming it's in scope
|
||||
- `separated_list!` and `separated_nonempty_list!` will not consume the separator if there's no following successfully parsed value
|
||||
- no more warnings on build
|
||||
|
||||
### Changed
|
||||
- simpler implementation of `many0!`
|
||||
|
||||
## 1.2.2 - 2016-03-09
|
||||
|
||||
### Thanks
|
||||
- @conradev for fixing take_until_s!`
|
||||
- @GuillaumeGomez for some documentation fixes
|
||||
- @frewsxcv for some documentation fixes
|
||||
- @tstorch for some test refactorings
|
||||
|
||||
### Added
|
||||
- `nom::Err` now implements `std::error::Error`
|
||||
|
||||
### Fixed
|
||||
- `hex_u32` does not parses more than 8 chars now
|
||||
- `take_while!` and `take_while1!` will not perturb the behaviour of `recognize!` anymore
|
||||
|
||||
## 1.2.1 - 2016-02-23
|
||||
|
||||
### Thanks
|
||||
- @sourrust for adding methods to `IResult`
|
||||
- @tstorch for the test refactoring, and for adding methods to `IResult` and `Needed`
|
||||
- @joelself for fixing the method system
|
||||
|
||||
### Added
|
||||
|
||||
- mapping methods over `IResult` and `Needed`
|
||||
|
||||
### Changed
|
||||
|
||||
- `apply_rf` is renamed to `apply_m`. This will not warrant a major version, since it is part missing from the methods feture added in the 1.2.0 release
|
||||
- the `regexp_macros` feature that used `regex!` to precompile regular expressions has been replaced by the normal regex engine combined with `lazy_static`
|
||||
|
||||
### Fixed
|
||||
|
||||
- when a parser or combinator was returning an empty buffer as remaining part, it was generating one from a static empty string. This was messing with buffer offset calculation. Now, that empty slice is taken like this: `&input[input.len()..]`.
|
||||
- The `regexp_macros` and `no_std` feature build again and are now tested with Travis CI
|
||||
|
||||
## 1.2.0 - 2016-02-08
|
||||
|
||||
### Thanks
|
||||
- @zentner-kyle for type inference fixes
|
||||
- @joelself for his work on `&str` parsing and method parsers
|
||||
- @GuillaumeGomez for implementing methods on `IResult`
|
||||
- @dirk for the `alt_complete!` combinator
|
||||
- @tstorch for a lot of refactoring work and unit tests additions
|
||||
- @jansegre for the hex digit parsers
|
||||
- @belgum for some documentation fixes
|
||||
- @lwandrebeck for some documentation fixes and code fixes in `hex_digit`
|
||||
|
||||
### Added
|
||||
- `take_until_and_consume_s!` for consumption of string data until a tag
|
||||
- more function patterns in `named!`. The error type can now be specified
|
||||
- `alt_complete!` works like the `alt!` combinator, but tries the next branch if the current one returned `Incomplete`, instead of returning directly
|
||||
- more unit tests for a lot of combinators
|
||||
- hexadecimal digit parsers
|
||||
- the `tuple!` combinator takes a list of parsers as argument, and applies them serially on the input. If all of them are successful, it willr eturn a tuple accumulating all the values. This combinator will (hopefully) replace most uses of `chain!`
|
||||
- parsers can now be implemented as a method for a struct thanks to the `method!`, `call_m!` and `apply_rf!` combinators
|
||||
|
||||
### Fixed
|
||||
- there were type inference issues in a few combinators. They will now be easier to compile
|
||||
- `peek!` compilation with bare functions
|
||||
- `&str` parsers were splitting data at the byte level, not at the char level, which can result in inconsistencies in parsing UTF-8 characters. They now use character indexes
|
||||
- some method implementations were missing on `ÌResult<I,O,E>` (with specified error type instead of implicit)
|
||||
|
||||
## 1.1.0 - 2016-01-01
|
||||
|
||||
This release adds a lot of features related to `&str` parsing. The previous versions
|
||||
were focused on `&[u8]` and bit streams parsing, but there's a need for more text
|
||||
parsing with nom. The parsing functions like `alpha`, `digit` and others will now
|
||||
accept either a `&[u8]` or a `&str`, so there is no breaking change on that part.
|
||||
|
||||
There are also a few performance improvements and documentation fixes.
|
||||
|
||||
### Thanks
|
||||
- @Binero for pushing the work on `&str` parsing
|
||||
- @meh for fixing `Option` and `Vec` imports
|
||||
- @hoodie for a documentation fix
|
||||
- @joelself for some documentation fixes
|
||||
- @vberger for his traits magic making `nom functions more generic
|
||||
|
||||
### Added
|
||||
|
||||
- string related parsers: `tag_s!`, `take_s!`, `is_a_s!`, `is_not_s!`, `take_while_s!`, `take_while1_s!`, `take_till_s!
|
||||
- `value!` is a combinator that always returns the same value. If a child parser is passed as second argument, that value is returned when the child parser succeeds
|
||||
|
||||
### Changed
|
||||
|
||||
- `tag!` will now compare even on partial input. If it expects "abcd" but receives "ef", it will now return an `Error` instead of `Incomplete`
|
||||
- `many0!` and others will preallocate a larger vector to avoid some copies and reallocations
|
||||
- `alpha`, `digit`, `alphanumeric`, `space` and `multispace` now accept as input a `&[u8]` or a `&str`. Additionally, they return an error if they receive an empty input
|
||||
- `take_while!`, `take_while1!`, `take_while_s!`, `take_while1_s!` wilreturn an error on empty input
|
||||
|
||||
### Fixed
|
||||
|
||||
- if the child parser of `many0!` or `many1!` returns `Incomplete`, it will return `Incomplete` too, possibly updating the needed size
|
||||
- `Option,` `Some`, `None` and `Vec` are now used with full path imports
|
||||
|
||||
## 1.0.1 - 2015-11-22
|
||||
|
||||
This releases makes the 1.0 version compatible with Rust 1.2 and 1.3
|
||||
|
||||
### Thanks
|
||||
- @steveklabnik for fixing lifetime issues in Producers and Consumers
|
||||
|
||||
## 1.0.0 - 2015-11-16
|
||||
|
||||
Stable release for nom. A lot of new features, a few breaking changes
|
||||
|
||||
### Thanks
|
||||
- @ahenry for macro fixes
|
||||
- @bluss for fixing documentation
|
||||
- @sourrust for cleaning code and debugging the new streaming utilities
|
||||
- @meh for inline optimizations
|
||||
- @ccmtaylor for fixing function imports
|
||||
- @soro for improvements to the streaming utilities
|
||||
- @breard-r for catching my typos
|
||||
- @nelsonjchen for catching my typos too
|
||||
- @divarvel for hex string parsers
|
||||
- @mrordinaire for the `length_bytes!` combinator
|
||||
|
||||
### Breaking changes
|
||||
- `IResult::Error` can now use custom error types, and is generic over the input type
|
||||
- Producers and consumers have been replaced. The new implementation uses less memory and integrates more with parsers
|
||||
- `nom::ErrorCode` is now `nom::ErrorKind`
|
||||
- `filter!` has been renamed to `take_while!`
|
||||
- `chain!` will count how much data is consumed and use that number to calculate how much data is needed if a parser returned `Incomplete`
|
||||
- `alt!` returns `Incomplete` if a child parser returned `Incomplete`, instead of skipping to the next parser
|
||||
- `IResult` does not require a lifetime tag anymore, yay!
|
||||
|
||||
### Added
|
||||
|
||||
- `complete!` will return an error if the child parser returned `Incomplete`
|
||||
- `add_error!` will wrap an error, but allow backtracking
|
||||
- `hex_u32` parser
|
||||
|
||||
### Fixed
|
||||
- the behaviour around `Incomplete` is better for most parsers now
|
||||
|
||||
## 0.5.0 - 2015-10-16
|
||||
|
||||
This release fixes a few issues and stabilizes the code.
|
||||
|
||||
### Thanks
|
||||
- @nox for documentation fixes
|
||||
- @daboross for linting fixes
|
||||
- @ahenry for fixing `tap!` and extending `dbg!` and `dbg_dmp!`
|
||||
- @bluss for tracking down and fixing issues with unsafe code
|
||||
- @meh for inlining parser functions
|
||||
- @ccmtaylor for fixing import of `str::from_utf8`
|
||||
|
||||
### Fixed
|
||||
- `tap!`, `dbg!` and `dbg_dmp!` now accept function parameters
|
||||
|
||||
### Changed
|
||||
- the type used in `count_fixed!` must be `Copy`
|
||||
- `chain!` calculates how much data is needed if one of the parsers returns `Incomplete
|
||||
- optional parsers in `chain!` can return `Incomplete`
|
||||
|
||||
## 0.4.0 - 2015-09-08
|
||||
|
||||
Considering the number of changes since the last release, this version can contain breaking changes, so the version number becomes 0.4.0. A lot of new features and performance improvements!
|
||||
|
||||
### Thanks
|
||||
- @frewsxcv for documentation fixes
|
||||
- @ngrewe for his work on producers and consumers
|
||||
- @meh for fixes on `chain!` and for the `rest` parser
|
||||
- @daboross for refactoring `many0!` and `many1!`
|
||||
- @aleksander for the `switch!` combinator idea
|
||||
- @TechnoMancer for his help with bit level parsing
|
||||
- @sxeraverx for pointing out a bug in `is_a!`
|
||||
|
||||
### Fixed
|
||||
- `count_fixed!` must take an explicit type as argument to generate the fixed-size array
|
||||
- optional parsing behaviour in `chain!`
|
||||
- `count!` can take 0 elements
|
||||
- `is_a!` and `is_not!` can now consume the whole input
|
||||
|
||||
### Added
|
||||
- it is now possible to seek to the end of a `MemProducer`
|
||||
- `opt!` returns `Done(input, None)` if `the child parser returned `Incomplete`
|
||||
- `rest` will return the remaining input
|
||||
- consumers can now seek to and from the end of input
|
||||
- `switch!` applies a first parser then matches on its result to choose the next parser
|
||||
- bit-level parsers
|
||||
- character-level parsers
|
||||
- regular expression parsers
|
||||
- implementation of `take_till!`, `take_while!` and `take_while1!`
|
||||
|
||||
### Changed
|
||||
- `alt!` can return `Incomplete`
|
||||
- the error analysis functions will now take references to functions instead of moving them
|
||||
- performance improvements on producers
|
||||
- performance improvement for `filter!`
|
||||
- performance improvement for `count!`: a `Vec` of the right size is directly allocated
|
||||
|
||||
## 0.3.11 - 2015-08-04
|
||||
|
||||
### Thanks
|
||||
- @bluss for remarking that the crate included random junk lying non commited in my local repository
|
||||
|
||||
### Fixed
|
||||
- cleanup of my local repository will ship less files in the crates, resulting in a smaller download
|
||||
|
||||
## 0.3.10 - 2015-08-03
|
||||
|
||||
### Added
|
||||
|
||||
- `bits!` for bit level parsing. It indicates that all child parsers will take a `(&[u8], usize)`as input, with the second parameter indicating the bit offset in the first byte. This allows viewing a byte slice as a bit stream. Most combinators can be used directly under `bits!`
|
||||
- `take_bits!` takes an integer type and a number of bits, consumes that number of bits and updates the offset, possibly by crossing byte boundaries
|
||||
- bit level parsers are all written in `src/bits.rs`
|
||||
|
||||
### Changed
|
||||
|
||||
- Parsers that specifically handle bytes have been moved to src/bytes.rs`. This applies to `tag!`, `is_not!`, `is_a!`, `filter!`, `take!`, `take_str!`, `take_until_and_consume!`, `take_until!`, `take_until_either_and_consume!`, `take_until_either!`
|
||||
|
||||
## 0.3.9 - 2015-07-20
|
||||
|
||||
### Thanks
|
||||
- @badboy for fixing `filter!`
|
||||
- @idmit for some documentation fixes
|
||||
|
||||
### Added
|
||||
- `opt_res!` applies a parser and transform its result in a Result. This parser never fails
|
||||
- `cond_reduce!` takes an expression as parameter, applies the parser if the expression is true, and returns an error if the expression is false
|
||||
- `tap!` pass the result of a parser to a block to manipulate it, but do not affect the parser's result
|
||||
- `AccReader` is a Read+BufRead that supports data accumulation and partial consumption. The `consume` method must be called afterwardsto indicate how much was consumed
|
||||
- Arithmetic expression evaluation and parsing example
|
||||
- `u16!`, `u32!`, `u64!`, `i16!`, `i32!`, `i64!` take an expression as parameter, if the expression is true, apply the big endian integer parser, if false, the little endian version
|
||||
- type information for combinators. This will make the documentation a bit easier to navigate
|
||||
|
||||
### Fixed
|
||||
- `map_opt!` and `map_res!` had issues with argument order due to bad macros
|
||||
- `delimited!` did not compile for certain combinations of arguments
|
||||
- `filter!` did not return a byte slice but a fixed array
|
||||
|
||||
## 0.3.8 - 2015-07-03
|
||||
|
||||
### Added
|
||||
- code coverage is now calculated automatically on Travis CI
|
||||
- `Stepper`: wrap a `Producer`, and call the method `step` with a parser. This method will buffer data if there is not enough, apply the parser if there is, and keep the rest of the input in memory for the next call
|
||||
- `ReadProducer`: takes something implementing `Read`, and makes a `Producer` out of it
|
||||
|
||||
### Fixed
|
||||
- the combinators `separated_pair!` and `delimited!` did not work because an implementation macro was not exported
|
||||
- if a `MemProducer` reached its end, it should always return `Eof`
|
||||
- `map!` had issues with argument matching
|
||||
|
||||
## 0.3.7 - 2015-06-24
|
||||
|
||||
### Added
|
||||
- `expr_res!` and `expr_opt!` evaluate an expression returning a Result or Opt and convert it to IResult
|
||||
- `AsBytes` is implemented for fixed size arrays. This allows `tag!([41u8, 42u8])`
|
||||
|
||||
### Fixed
|
||||
- `count_fixed!` argument parsing works again
|
||||
|
||||
## 0.3.6 - 2015-06-15
|
||||
|
||||
### Added
|
||||
- documentation for a few functions
|
||||
- the consumer trait now requires the `failed(&self, error_code)` method in case of parsing error
|
||||
- `named!` now handles thge alternative `named!(pub fun_name<OutputType>, ...)`
|
||||
|
||||
### Fixed
|
||||
- `filter!` now returns the whole input if the filter function never returned false
|
||||
- `take!` casts its argument as usize, so it can accepts any integer type now
|
||||
|
||||
## 0.3.5 - 2015-06-10
|
||||
|
||||
### Thanks
|
||||
- @cmr for some documentation fixes
|
||||
|
||||
### Added
|
||||
- `count_fixed!` returns a fixed array
|
||||
|
||||
### Fixed
|
||||
- `count!` is back to the previous behaviour, returning a `Vec` for sizes known at runtime
|
||||
|
||||
### Changed
|
||||
- functions and traits exported from `nom::util` are now directly in `nom::`
|
||||
|
||||
## 0.3.4 - 2015-06-09
|
||||
|
||||
### Thanks
|
||||
- @andrew-d for fixes on `cond!`
|
||||
- @keruspe for features in `chain!`
|
||||
|
||||
### Added
|
||||
- `chain!` can now have mutable fields
|
||||
|
||||
### Fixed
|
||||
- `cond!` had an infinite macro recursion
|
||||
|
||||
### Changed
|
||||
- `chain!` generates less code now. No apprent compilation time improvement
|
||||
|
||||
## 0.3.3 - 2015-06-09
|
||||
|
||||
### Thanks
|
||||
- @andrew-d for the little endian signed integer parsers
|
||||
- @keruspe for fixes on `count!`
|
||||
|
||||
### Added
|
||||
- `le_i8`, `le_i16`, `le_i32`, `le_i64`: little endian signed integer parsers
|
||||
|
||||
### Changed
|
||||
- the `alt!` parser compiles much faster, even with more than 8 branches
|
||||
- `count!` can now return a fixed size array instead of a growable vector
|
||||
|
||||
## 0.3.2 - 2015-05-31
|
||||
|
||||
### Thanks
|
||||
- @keruspe for the `take_str` parser and the function application combinator
|
||||
|
||||
### Added
|
||||
- `take_str!`: takes the specified number of bytes and return a UTF-8 string
|
||||
- `apply!`: do partial application on the parameters of a function
|
||||
|
||||
### Changed
|
||||
- `Needed::Size` now contains a `usize` instead of a `u32`
|
||||
|
||||
## 0.3.1 - 2015-05-21
|
||||
|
||||
### Thanks
|
||||
- @divarvel for the big endian signed integer parsers
|
||||
|
||||
### Added
|
||||
- `be_i8`, `be_i16`, `be_i32`, `be_i64`: big endian signed integer parsers
|
||||
- the `core` feature can be passed to cargo to build with `no_std`
|
||||
- colored hexdump can be generated from error chains
|
||||
|
||||
## 0.3.0 - 2015-05-07
|
||||
|
||||
### Thanks
|
||||
- @filipegoncalves for some documentation and the new eof parser
|
||||
- @CrimsonVoid for putting fully qualified types in the macros
|
||||
- @lu_zero for some documentation fixes
|
||||
|
||||
### Added
|
||||
- new error types that can contain an error code, an input slice, and a list of following errors
|
||||
- `error!` will cut backtracking and return directly from the parser, with a specified error code
|
||||
- `eof` parser, successful if there is no more input
|
||||
- specific error codes for the parsers provided by nom
|
||||
|
||||
### Changed
|
||||
- fully qualified types in macros. A lot of imports are not needed anymore
|
||||
|
||||
### Removed
|
||||
- `FlatMap`, `FlatpMapOpt` and `Functor` traits (replaced by `map!`, `map_opt!` and `map_res!`)
|
||||
|
||||
## 0.2.2 - 2015-04-12
|
||||
|
||||
### Thanks
|
||||
- @filipegoncalves and @thehydroimpulse for debugging an infinite loop in many0 and many1
|
||||
- @thehydroimpulse for suggesting public named parsers
|
||||
- @skade for removing the dependency on the collections gate
|
||||
|
||||
### Added
|
||||
- `named!` can now declare public functions like this: `named!(pub tst, tag!("abcd"));`
|
||||
- `pair!(X,Y)` returns a tuple `(x, y)`
|
||||
- `separated_pair!(X, sep, Y)` returns a tuple `(x, y)`
|
||||
- `preceded!(opening, X)` returns `x`
|
||||
- `terminated!(X, closing)` returns `x`
|
||||
- `delimited(opening, X, closing)` returns `x`
|
||||
- `separated_list(sep, X)` returns a `Vec<X>`
|
||||
- `separated_nonempty_list(sep, X)` returns a `Vec<X>` of at list one element
|
||||
|
||||
### Changed
|
||||
- `many0!` and `many1!` forbid parsers that do not consume input
|
||||
- `is_a!`, `is_not!`, `alpha`, `digit`, `space`, `multispace` will now return an error if they do not consume at least one byte
|
||||
|
||||
## 0.2.1 - 2015-04-04
|
||||
|
||||
### Thanks
|
||||
- @mtsr for catching the remaining debug println!
|
||||
- @jag426 who killed a lot of warnings
|
||||
- @skade for removing the dependency on the core feature gate
|
||||
|
||||
|
||||
### Added
|
||||
- little endian unsigned int parsers le_u8, le_u16, le_u32, le_u64
|
||||
- `count!` to apply a parser a specified number of times
|
||||
- `cond!` applies a parser if the condition is met
|
||||
- more parser development tools in `util::*`
|
||||
|
||||
### Fixed
|
||||
- in one case, `opt!` would not compile
|
||||
|
||||
### Removed
|
||||
- most of the feature gates are now removed. The only one still needed is `collections`
|
||||
|
||||
## 0.2.0 - 2015-03-24
|
||||
*works with `rustc 1.0.0-dev (81e2396c7 2015-03-19) (built 2015-03-19)`*
|
||||
|
||||
### Thanks
|
||||
- Ryman for the AsBytes implementation
|
||||
- jag426 and jaredly for documentation fixes
|
||||
- eternaleye on #rust IRC for his help on the new macro syntax
|
||||
|
||||
### Changed
|
||||
- the AsBytes trait improves readability, no more b"...", but "..." instead
|
||||
- Incomplete will now hold either Needed;;Unknown, or Needed::Size(u32). Matching on Incomplete without caring for the value is done with `Incomplete(_)`, but if more granularity is mandatory, `Needed` can be matched too
|
||||
- `alt!` can pass the result of the parser to a closure
|
||||
- the `take_*` macros changed behaviour, the default case is now not to consume the separator. The macros have been renamed as follows: `take_until!` -> `take_until_and_consume!`, `take_until_and_leave!` -> `take_until!`, `take_until_either_and_leave!` -> `take_until_either!`, `take_until_either!` -> `take_until_either_and_consume!`
|
||||
|
||||
### Added
|
||||
- `peek!` macro: matches the future input but does not consume it
|
||||
- `length_value!` macro: the first argument is a parser returning a `n` that can cast to usize, then applies the second parser `n` times. The macro has a variant with a third argument indicating the expected input size for the second parser
|
||||
- benchmarks are available at https://github.com/Geal/nom_benchmarks
|
||||
- more documentation
|
||||
- **Unnamed parser syntax**: warning, this is a breaking change. With this new syntax, the macro combinators do not generate functions anymore, they create blocks. That way, they can be nested, for better readability. The `named!` macro is provided to create functions from parsers. Please be aware that nesting parsers comes with a small cost of compilation time, negligible in most cases, but can quickly get to the minutes scale if not careful. If this happens, separate your parsers in multiple subfunctions.
|
||||
- `named!`, `closure!` and `call!` macros used to support the unnamed syntax
|
||||
- `map!`, `map_opt!` and `map_res!` to combine a parser with a normal function, transforming the input directly, or returning an `Option` or `Result`
|
||||
|
||||
### Fixed
|
||||
- `is_a!` is now working properly
|
||||
|
||||
### Removed
|
||||
- the `o!` macro does less than `chain!`, so it has been removed
|
||||
- the `fold0!` and `fold1!` macros were too complex and awkward to use, the `many*` combinators will be useful for most uses for now
|
||||
|
||||
## 0.1.6 - 2015-02-24
|
||||
### Changed
|
||||
- consumers must have an end method that will be called after parsing
|
||||
|
||||
### Added
|
||||
- big endian unsigned int and float parsers: be_u8, be_u16, be_u32, be_u64, be_f32, be_f64
|
||||
- producers can seek
|
||||
- function and macros documentation
|
||||
- README documentation
|
||||
### Fixed
|
||||
- lifetime declarations
|
||||
- tag! can return Incomplete
|
||||
|
||||
## 0.1.5 - 2015-02-17
|
||||
### Changed
|
||||
- traits were renamed: FlatMapper -> FlatMap, Mapper -> FlatMapOpt, Mapper2 -> Functor
|
||||
|
||||
### Fixed
|
||||
- woeks with rustc f1bb6c2f4
|
||||
|
||||
## 0.1.4 - 2015-02-17
|
||||
### Changed
|
||||
- the chaining macro can take optional arguments with '?'
|
||||
|
||||
## 0.1.3 - 2015-02-16
|
||||
### Changed
|
||||
- the chaining macro now takes the closure at the end of the argument list
|
||||
|
||||
## 0.1.2 - 2015-02-16
|
||||
### Added
|
||||
- flat_map implementation for <&[u8], &[u8]>
|
||||
- chaining macro
|
||||
- partial MP4 parser example
|
||||
|
||||
|
||||
## 0.1.1 - 2015-02-06
|
||||
### Fixed
|
||||
- closure syntax change
|
||||
|
||||
## Compare code
|
||||
|
||||
* [unreleased]: https://github.com/Geal/nom/compare/1.2.4...HEAD
|
||||
* [1.2.3]: https://github.com/Geal/nom/compare/1.2.3...1.2.4
|
||||
* [1.2.3]: https://github.com/Geal/nom/compare/1.2.2...1.2.3
|
||||
* [1.2.2]: https://github.com/Geal/nom/compare/1.2.1...1.2.2
|
||||
* [1.2.1]: https://github.com/Geal/nom/compare/1.2.0...1.2.1
|
||||
* [1.2.0]: https://github.com/Geal/nom/compare/1.1.0...1.2.0
|
||||
* [1.1.0]: https://github.com/Geal/nom/compare/1.0.1...1.1.0
|
||||
* [1.0.1]: https://github.com/Geal/nom/compare/1.0.0...1.0.1
|
||||
* [1.0.0]: https://github.com/Geal/nom/compare/0.5.0...1.0.0
|
||||
* [0.5.0]: https://github.com/geal/nom/compare/0.4.0...0.5.0
|
||||
* [0.4.0]: https://github.com/geal/nom/compare/0.3.11...0.4.0
|
||||
* [0.3.11]: https://github.com/geal/nom/compare/0.3.10...0.3.11
|
||||
* [0.3.10]: https://github.com/geal/nom/compare/0.3.9...0.3.10
|
||||
* [0.3.9]: https://github.com/geal/nom/compare/0.3.8...0.3.9
|
||||
* [0.3.8]: https://github.com/Geal/nom/compare/0.3.7...0.3.8
|
||||
* [0.3.7]: https://github.com/Geal/nom/compare/0.3.6...0.3.7
|
||||
* [0.3.6]: https://github.com/Geal/nom/compare/0.3.5...0.3.6
|
||||
* [0.3.5]: https://github.com/Geal/nom/compare/0.3.4...0.3.5
|
||||
* [0.3.4]: https://github.com/Geal/nom/compare/0.3.3...0.3.4
|
||||
* [0.3.3]: https://github.com/Geal/nom/compare/0.3.2...0.3.3
|
||||
* [0.3.2]: https://github.com/Geal/nom/compare/0.3.1...0.3.2
|
||||
* [0.3.1]: https://github.com/Geal/nom/compare/0.3.0...0.3.1
|
||||
* [0.3.0]: https://github.com/Geal/nom/compare/0.2.2...0.3.0
|
||||
* [0.2.2]: https://github.com/Geal/nom/compare/0.2.1...0.2.2
|
||||
* [0.2.1]: https://github.com/Geal/nom/compare/0.2.0...0.2.1
|
||||
* [0.2.0]: https://github.com/Geal/nom/compare/0.1.6...0.2.0
|
||||
* [0.1.6]: https://github.com/Geal/nom/compare/0.1.5...0.1.6
|
||||
* [0.1.5]: https://github.com/Geal/nom/compare/0.1.4...0.1.5
|
||||
* [0.1.4]: https://github.com/Geal/nom/compare/0.1.3...0.1.4
|
||||
* [0.1.3]: https://github.com/Geal/nom/compare/0.1.2...0.1.3
|
||||
* [0.1.2]: https://github.com/Geal/nom/compare/0.1.1...0.1.2
|
||||
* [0.1.1]: https://github.com/Geal/nom/compare/0.1.0...0.1.1
|
|
@ -1,38 +0,0 @@
|
|||
[package]
|
||||
|
||||
name = "nom"
|
||||
version = "1.2.4"
|
||||
authors = [ "contact@geoffroycouprie.com" ]
|
||||
description = "A byte-oriented, zero-copy, parser combinators library"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/Geal/nom"
|
||||
readme = "README.md"
|
||||
documentation = "http://rust.unhandledexpression.com/nom/"
|
||||
keywords = ["parser", "parser-combinators", "parsing", "streaming", "bit"]
|
||||
|
||||
include = [
|
||||
"CHANGELOG.md",
|
||||
"LICENSE",
|
||||
".gitignore",
|
||||
".travis.yml",
|
||||
"Cargo.toml",
|
||||
"src/*.rs",
|
||||
"tests/*.rs"
|
||||
]
|
||||
|
||||
[features]
|
||||
core = []
|
||||
nightly = []
|
||||
default = ["stream"]
|
||||
regexp = ["regex"]
|
||||
regexp_macros = ["regexp", "lazy_static"]
|
||||
stream = []
|
||||
|
||||
[dependencies.regex]
|
||||
version = "^0.1.56"
|
||||
optional = true
|
||||
|
||||
[dependencies.lazy_static]
|
||||
version = "^0.2.1"
|
||||
optional = true
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
Copyright (c) 2015 Geoffroy Couprie
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -1,220 +0,0 @@
|
|||
//! Bit level parsers and combinators
|
||||
//!
|
||||
//! Bit parsing is handled by tweaking the input in most macros.
|
||||
//! In byte level parsing, the input is generally a `&[u8]` passed from combinator
|
||||
//! to combinator until the slices are manipulated.
|
||||
//!
|
||||
//! Bit parsers take a `(&[u8], usize)` as input. The first part of the tuple is an byte slice,
|
||||
//! the second part is a bit offset in the first byte of the slice.
|
||||
//!
|
||||
//! By passing a pair like this, we can leverage most of the combinators, and avoid
|
||||
//! transforming the whole slice to a vector of booleans. This should make it easy
|
||||
//! to see a byte slice as a bit stream, and parse code points of arbitrary bit length.
|
||||
|
||||
|
||||
/// `bits!( parser ) => ( &[u8], (&[u8], usize) -> IResult<(&[u8], usize), T> ) -> IResult<&[u8], T>`
|
||||
/// transforms its byte slice input into a bit stream for the underlying parsers
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::Done;
|
||||
/// # fn main() {
|
||||
/// named!( take_3_bits<u8>, bits!( take_bits!( u8, 3 ) ) );
|
||||
///
|
||||
/// let input = vec![0b10101010, 0b11110000, 0b00110011];
|
||||
/// let sl = &input[..];
|
||||
///
|
||||
/// assert_eq!(take_3_bits( sl ), Done(&sl[1..], 5) );
|
||||
/// # }
|
||||
#[macro_export]
|
||||
macro_rules! bits (
|
||||
($i:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
bits_impl!($i, $submac!($($args)*));
|
||||
);
|
||||
($i:expr, $f:expr) => (
|
||||
bits_impl!($i, call!($f));
|
||||
);
|
||||
);
|
||||
|
||||
/// Internal parser, do not use directly
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! bits_impl (
|
||||
($i:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
{
|
||||
let input = ($i, 0usize);
|
||||
match $submac!(input, $($args)*) {
|
||||
$crate::IResult::Error(e) => {
|
||||
let err = match e {
|
||||
$crate::Err::Code(k) | $crate::Err::Node(k, _) => $crate::Err::Code(k),
|
||||
$crate::Err::Position(k, (i,b)) | $crate::Err::NodePosition(k, (i,b), _) => {
|
||||
$crate::Err::Position(k, &i[b/8..])
|
||||
}
|
||||
};
|
||||
$crate::IResult::Error(err)
|
||||
}
|
||||
$crate::IResult::Incomplete($crate::Needed::Unknown) => $crate::IResult::Incomplete($crate::Needed::Unknown),
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(i)) => {
|
||||
//println!("bits parser returned Needed::Size({})", i);
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(i / 8 + 1))
|
||||
},
|
||||
$crate::IResult::Done((i, bit_index), o) => {
|
||||
let byte_index = bit_index / 8 + if bit_index % 8 == 0 { 0 } else { 1 } ;
|
||||
//println!("bit index=={} => byte index=={}", bit_index, byte_index);
|
||||
$crate::IResult::Done(&i[byte_index..], o)
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// `take_bits!(type, nb) => ( (&[T], usize), U, usize) -> IResult<(&[T], usize), U>`
|
||||
/// generates a parser consuming the specified number of bits.
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::Done;
|
||||
/// # fn main() {
|
||||
/// named!( take_pair<(u8, u8)>, bits!( pair!( take_bits!( u8, 3 ), take_bits!(u8, 5) ) ) );
|
||||
///
|
||||
/// let input = vec![0b10101010, 0b11110000, 0b00110011];
|
||||
/// let sl = &input[..];
|
||||
///
|
||||
/// assert_eq!(take_pair( sl ), Done(&sl[1..], (5, 10)) );
|
||||
/// assert_eq!(take_pair( &sl[1..] ), Done(&sl[2..], (7, 16)) );
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! take_bits (
|
||||
($i:expr, $t:ty, $count:expr) => (
|
||||
{
|
||||
use std::ops::Div;
|
||||
//println!("taking {} bits from {:?}", $count, $i);
|
||||
let (input, bit_offset) = $i;
|
||||
let res : $crate::IResult<(&[u8],usize), $t> = if $count == 0 {
|
||||
$crate::IResult::Done( (input, bit_offset), 0)
|
||||
} else {
|
||||
let cnt = ($count as usize + bit_offset).div(8);
|
||||
if input.len() * 8 < $count as usize + bit_offset {
|
||||
//println!("returning incomplete: {}", $count as usize + bit_offset);
|
||||
$crate::IResult::Incomplete($crate::Needed::Size($count as usize))
|
||||
} else {
|
||||
let mut acc:$t = 0;
|
||||
let mut offset: usize = bit_offset;
|
||||
let mut remaining: usize = $count;
|
||||
let mut end_offset: usize = 0;
|
||||
|
||||
for byte in input.iter().take(cnt + 1) {
|
||||
if remaining == 0 {
|
||||
break;
|
||||
}
|
||||
let val: $t = if offset == 0 {
|
||||
*byte as $t
|
||||
} else {
|
||||
((*byte << offset) as u8 >> offset) as $t
|
||||
};
|
||||
|
||||
if remaining < 8 - offset {
|
||||
acc += val >> (8 - offset - remaining);
|
||||
end_offset = remaining + offset;
|
||||
break;
|
||||
} else {
|
||||
acc += val << (remaining - (8 - offset));
|
||||
remaining -= 8 - offset;
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
$crate::IResult::Done( (&input[cnt..], end_offset) , acc)
|
||||
}
|
||||
};
|
||||
res
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// matches an integer pattern to a bitstream. The number of bits of the input to compare must be specified
|
||||
#[macro_export]
|
||||
macro_rules! tag_bits (
|
||||
($i:expr, $t:ty, $count:expr, $p: pat) => (
|
||||
{
|
||||
match take_bits!($i, $t, $count) {
|
||||
$crate::IResult::Incomplete(i) => $crate::IResult::Incomplete(i),
|
||||
$crate::IResult::Done(i, o) => {
|
||||
if let $p = o {
|
||||
let res: $crate::IResult<(&[u8],usize),$t> = $crate::IResult::Done(i, o);
|
||||
res
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Position($crate::ErrorKind::TagBits, $i))
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
$crate::IResult::Error($crate::Err::Position($crate::ErrorKind::TagBits, $i))
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use internal::{IResult,Needed,Err};
|
||||
use ErrorKind;
|
||||
|
||||
#[test]
|
||||
fn take_bits() {
|
||||
let input = vec![0b10101010, 0b11110000, 0b00110011];
|
||||
let sl = &input[..];
|
||||
|
||||
assert_eq!(take_bits!( (sl, 0), u8, 0 ), IResult::Done((sl, 0), 0));
|
||||
assert_eq!(take_bits!( (sl, 0), u8, 8 ), IResult::Done((&sl[1..], 0), 170));
|
||||
assert_eq!(take_bits!( (sl, 0), u8, 3 ), IResult::Done((&sl[0..], 3), 5));
|
||||
assert_eq!(take_bits!( (sl, 0), u8, 6 ), IResult::Done((&sl[0..], 6), 42));
|
||||
assert_eq!(take_bits!( (sl, 1), u8, 1 ), IResult::Done((&sl[0..], 2), 0));
|
||||
assert_eq!(take_bits!( (sl, 1), u8, 2 ), IResult::Done((&sl[0..], 3), 1));
|
||||
assert_eq!(take_bits!( (sl, 1), u8, 3 ), IResult::Done((&sl[0..], 4), 2));
|
||||
assert_eq!(take_bits!( (sl, 6), u8, 3 ), IResult::Done((&sl[1..], 1), 5));
|
||||
assert_eq!(take_bits!( (sl, 0), u16, 10 ), IResult::Done((&sl[1..], 2), 683));
|
||||
assert_eq!(take_bits!( (sl, 0), u16, 8 ), IResult::Done((&sl[1..], 0), 170));
|
||||
assert_eq!(take_bits!( (sl, 6), u16, 10 ), IResult::Done((&sl[2..], 0), 752));
|
||||
assert_eq!(take_bits!( (sl, 6), u16, 11 ), IResult::Done((&sl[2..], 1), 1504));
|
||||
assert_eq!(take_bits!( (sl, 0), u32, 20 ), IResult::Done((&sl[2..], 4), 700163));
|
||||
assert_eq!(take_bits!( (sl, 4), u32, 20 ), IResult::Done((&sl[3..], 0), 716851));
|
||||
assert_eq!(take_bits!( (sl, 4), u32, 22 ), IResult::Incomplete(Needed::Size(22)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tag_bits() {
|
||||
let input = vec![0b10101010, 0b11110000, 0b00110011];
|
||||
let sl = &input[..];
|
||||
|
||||
assert_eq!(tag_bits!( (sl, 0), u8, 3, 0b101), IResult::Done((&sl[0..], 3), 5));
|
||||
assert_eq!(tag_bits!( (sl, 0), u8, 4, 0b1010), IResult::Done((&sl[0..], 4), 10));
|
||||
}
|
||||
|
||||
named!(ch<(&[u8],usize),(u8,u8)>,
|
||||
chain!(
|
||||
tag_bits!(u8, 3, 0b101) ~
|
||||
x: take_bits!(u8, 4) ~
|
||||
y: take_bits!(u8, 5) ,
|
||||
|| { (x,y) }
|
||||
)
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn chain_bits() {
|
||||
let input = vec![0b10101010, 0b11110000, 0b00110011];
|
||||
let sl = &input[..];
|
||||
assert_eq!(ch((&input[..],0)), IResult::Done((&sl[1..], 4), (5,15)));
|
||||
assert_eq!(ch((&input[..],4)), IResult::Done((&sl[2..], 0), (7,16)));
|
||||
assert_eq!(ch((&input[..1],0)), IResult::Incomplete(Needed::Size(12)));
|
||||
}
|
||||
|
||||
named!(ch_bytes<(u8,u8)>, bits!(ch));
|
||||
#[test]
|
||||
fn bits_to_bytes() {
|
||||
let input = vec![0b10101010, 0b11110000, 0b00110011];
|
||||
assert_eq!(ch_bytes(&input[..]), IResult::Done(&input[2..], (5,15)));
|
||||
assert_eq!(ch_bytes(&input[..1]), IResult::Incomplete(Needed::Size(2)));
|
||||
assert_eq!(ch_bytes(&input[1..]), IResult::Error(Err::Position(ErrorKind::TagBits, &input[1..])));
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,184 +0,0 @@
|
|||
/// Character level parsers
|
||||
|
||||
use internal::{IResult,Needed,Err};
|
||||
use util::ErrorKind;
|
||||
|
||||
/// matches one of the provided characters
|
||||
#[macro_export]
|
||||
macro_rules! one_of (
|
||||
($i:expr, $inp: expr) => (
|
||||
{
|
||||
if $i.is_empty() {
|
||||
$crate::IResult::Incomplete::<_, _>($crate::Needed::Size(1))
|
||||
} else {
|
||||
#[inline(always)]
|
||||
fn as_bytes<T: $crate::AsBytes>(b: &T) -> &[u8] {
|
||||
b.as_bytes()
|
||||
}
|
||||
|
||||
let expected = $inp;
|
||||
let bytes = as_bytes(&expected);
|
||||
one_of_bytes!($i, bytes)
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! one_of_bytes (
|
||||
($i:expr, $bytes: expr) => (
|
||||
{
|
||||
if $i.is_empty() {
|
||||
$crate::IResult::Incomplete::<_, _>($crate::Needed::Size(1))
|
||||
} else {
|
||||
let mut found = false;
|
||||
|
||||
for &i in $bytes {
|
||||
if i == $i[0] {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
$crate::IResult::Done(&$i[1..], $i[0] as char)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Position($crate::ErrorKind::OneOf, $i))
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// matches anything but the provided characters
|
||||
#[macro_export]
|
||||
macro_rules! none_of (
|
||||
($i:expr, $inp: expr) => (
|
||||
{
|
||||
if $i.is_empty() {
|
||||
$crate::IResult::Incomplete::<_, _>($crate::Needed::Size(1))
|
||||
} else {
|
||||
#[inline(always)]
|
||||
fn as_bytes<T: $crate::AsBytes>(b: &T) -> &[u8] {
|
||||
b.as_bytes()
|
||||
}
|
||||
|
||||
let expected = $inp;
|
||||
let bytes = as_bytes(&expected);
|
||||
none_of_bytes!($i, bytes)
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! none_of_bytes (
|
||||
($i:expr, $bytes: expr) => (
|
||||
{
|
||||
if $i.is_empty() {
|
||||
$crate::IResult::Incomplete::<_, _>($crate::Needed::Size(1))
|
||||
} else {
|
||||
let mut found = false;
|
||||
|
||||
for &i in $bytes {
|
||||
if i == $i[0] {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
$crate::IResult::Done(&$i[1..], $i[0] as char)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Position($crate::ErrorKind::NoneOf, $i))
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// matches one character: `char!(char) => &[u8] -> IResult<&[u8], char>
|
||||
#[macro_export]
|
||||
macro_rules! char (
|
||||
($i:expr, $c: expr) => (
|
||||
{
|
||||
if $i.is_empty() {
|
||||
let res: $crate::IResult<&[u8], char> = $crate::IResult::Incomplete($crate::Needed::Size(1));
|
||||
res
|
||||
} else {
|
||||
if $i[0] == $c as u8 {
|
||||
$crate::IResult::Done(&$i[1..], $i[0] as char)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Position($crate::ErrorKind::Char, $i))
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
named!(pub newline<char>, char!('\n'));
|
||||
|
||||
pub fn crlf(input:&[u8]) -> IResult<&[u8], char> {
|
||||
if input.len() < 2 {
|
||||
IResult::Incomplete(Needed::Size(2))
|
||||
} else {
|
||||
if &input[0..2] == &b"\r\n"[..] {
|
||||
IResult::Done(&input[2..], '\n')
|
||||
} else {
|
||||
IResult::Error(Err::Position(ErrorKind::CrLf, input))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
named!(pub eol<char>, alt!(crlf | newline));
|
||||
named!(pub tab<char>, char!('\t'));
|
||||
|
||||
pub fn anychar(input:&[u8]) -> IResult<&[u8], char> {
|
||||
if input.is_empty() {
|
||||
IResult::Incomplete(Needed::Size(1))
|
||||
} else {
|
||||
IResult::Done(&input[1..], input[0] as char)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use internal::IResult::*;
|
||||
use internal::Err::*;
|
||||
use util::ErrorKind;
|
||||
|
||||
#[test]
|
||||
fn one_of() {
|
||||
named!(f<char>, one_of!("ab"));
|
||||
|
||||
let a = &b"abcd"[..];
|
||||
assert_eq!(f(a), Done(&b"bcd"[..], 'a'));
|
||||
|
||||
let b = &b"cde"[..];
|
||||
assert_eq!(f(b), Error(Position(ErrorKind::OneOf, b)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn none_of() {
|
||||
named!(f<char>, none_of!("ab"));
|
||||
|
||||
let a = &b"abcd"[..];
|
||||
assert_eq!(f(a), Error(Position(ErrorKind::NoneOf, a)));
|
||||
|
||||
let b = &b"cde"[..];
|
||||
assert_eq!(f(b), Done(&b"de"[..], 'c'));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn char() {
|
||||
named!(f<char>, char!('c'));
|
||||
|
||||
let a = &b"abcd"[..];
|
||||
assert_eq!(f(a), Error(Position(ErrorKind::Char, a)));
|
||||
|
||||
let b = &b"cde"[..];
|
||||
assert_eq!(f(b), Done(&b"de"[..], 'c'));
|
||||
}
|
||||
}
|
|
@ -1,347 +0,0 @@
|
|||
//! Basic types to build the parsers
|
||||
|
||||
use self::IResult::*;
|
||||
use self::Needed::*;
|
||||
use util::ErrorKind;
|
||||
|
||||
#[cfg(feature = "core")]
|
||||
use std::prelude::v1::*;
|
||||
use std::boxed::Box;
|
||||
|
||||
/// Contains the error that a parser can return
|
||||
///
|
||||
/// It can represent a linked list of errors, indicating the path taken in the parsing tree, with corresponding position in the input data.
|
||||
/// It depends on P, the input position (for a &[u8] parser, it would be a &[u8]), and E, the custom error type (by default, u32)
|
||||
#[derive(Debug,PartialEq,Eq,Clone)]
|
||||
pub enum Err<P,E=u32>{
|
||||
/// An error code, represented by an ErrorKind, which can contain a custom error code represented by E
|
||||
Code(ErrorKind<E>),
|
||||
/// An error code, and the next error
|
||||
Node(ErrorKind<E>, Box<Err<P,E>>),
|
||||
/// An error code, and the input position
|
||||
Position(ErrorKind<E>, P),
|
||||
/// An error code, the input position and the next error
|
||||
NodePosition(ErrorKind<E>, P, Box<Err<P,E>>)
|
||||
}
|
||||
|
||||
/// Contains information on needed data if a parser returned `Incomplete`
|
||||
#[derive(Debug,PartialEq,Eq,Clone,Copy)]
|
||||
pub enum Needed {
|
||||
/// needs more data, but we do not know how much
|
||||
Unknown,
|
||||
/// contains the required data size
|
||||
Size(usize)
|
||||
}
|
||||
|
||||
impl Needed {
|
||||
pub fn is_known(&self) -> bool {
|
||||
*self != Unknown
|
||||
}
|
||||
|
||||
/// Maps a `Needed` to `Needed` by appling a function to a contained `Size` value.
|
||||
#[inline]
|
||||
pub fn map<F: FnOnce(usize) -> usize>(self, f: F) -> Needed {
|
||||
match self {
|
||||
Unknown => Unknown,
|
||||
Size(n) => Size(f(n)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Holds the result of parsing functions
|
||||
///
|
||||
/// It depends on I, the input type, O, the output type, and E, the error type (by default u32)
|
||||
///
|
||||
#[derive(Debug,PartialEq,Eq,Clone)]
|
||||
pub enum IResult<I,O,E=u32> {
|
||||
/// indicates a correct parsing, the first field containing the rest of the unparsed data, the second field contains the parsed data
|
||||
Done(I,O),
|
||||
/// contains a Err, an enum that can indicate an error code, a position in the input, and a pointer to another error, making a list of errors in the parsing tree
|
||||
Error(Err<I,E>),
|
||||
/// Incomplete contains a Needed, an enum than can represent a known quantity of input data, or unknown
|
||||
Incomplete(Needed)
|
||||
}
|
||||
|
||||
impl<I,O,E> IResult<I,O,E> {
|
||||
pub fn is_done(&self) -> bool {
|
||||
match *self {
|
||||
Done(_,_) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_err(&self) -> bool {
|
||||
match *self {
|
||||
Error(_) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_incomplete(&self) -> bool {
|
||||
match *self {
|
||||
Incomplete(_) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps a `IResult<I, O, E>` to `IResult<I, N, E>` by appling a function
|
||||
/// to a contained `Done` value, leaving `Error` and `Incomplete` value
|
||||
/// untouched.
|
||||
#[inline]
|
||||
pub fn map<N, F: FnOnce(O) -> N>(self, f: F) -> IResult<I, N, E> {
|
||||
match self {
|
||||
Done(i, o) => Done(i, f(o)),
|
||||
Error(e) => Error(e),
|
||||
Incomplete(n) => Incomplete(n),
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps a `IResult<I, O, E>` to `IResult<I, O, E>` by appling a function
|
||||
/// to a contained `Incomplete` value, leaving `Done` and `Error` value
|
||||
/// untouched.
|
||||
#[inline]
|
||||
pub fn map_inc<F>(self, f: F) -> IResult<I, O, E>
|
||||
where F: FnOnce(Needed) -> Needed {
|
||||
match self {
|
||||
Error(e) => Error(e),
|
||||
Incomplete(n) => Incomplete(f(n)),
|
||||
Done(i, o) => Done(i, o),
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps a `IResult<I, O, E>` to `IResult<I, O, N>` by appling a function
|
||||
/// to a contained `Error` value, leaving `Done` and `Incomplete` value
|
||||
/// untouched.
|
||||
#[inline]
|
||||
pub fn map_err<N, F>(self, f: F) -> IResult<I, O, N>
|
||||
where F: FnOnce(Err<I, E>) -> Err<I, N> {
|
||||
match self {
|
||||
Error(e) => Error(f(e)),
|
||||
Incomplete(n) => Incomplete(n),
|
||||
Done(i, o) => Done(i, o),
|
||||
}
|
||||
}
|
||||
|
||||
/// Unwrap the contained `Done(I, O)` value, or panic if the `IResult` is not
|
||||
/// `Done`.
|
||||
pub fn unwrap(self) -> (I, O) {
|
||||
match self {
|
||||
Done(i, o) => (i, o),
|
||||
Incomplete(_) => panic!("unwrap() called on an IResult that is Incomplete"),
|
||||
Error(_) => panic!("unwrap() called on an IResult that is Error")
|
||||
}
|
||||
}
|
||||
|
||||
/// Unwrap the contained `Done(I, O)` value, or panic if the `IResult` is not
|
||||
/// `Done`.
|
||||
pub fn unwrap_inc(self) -> Needed {
|
||||
match self {
|
||||
Incomplete(n) => n,
|
||||
Done(_, _) => panic!("unwrap_inc() called on an IResult that is Done"),
|
||||
Error(_) => panic!("unwrap_inc() called on an IResult that is Error")
|
||||
}
|
||||
}
|
||||
|
||||
/// Unwrap the contained `Done(I, O)` value, or panic if the `IResult` is not
|
||||
/// `Done`.
|
||||
pub fn unwrap_err(self) -> Err<I, E> {
|
||||
match self {
|
||||
Error(e) => e,
|
||||
Done(_, _) => panic!("unwrap_err() called on an IResult that is Done"),
|
||||
Incomplete(_) => panic!("unwrap_err() called on an IResult that is Incomplete"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait GetInput<I> {
|
||||
fn remaining_input(&self) -> Option<I>;
|
||||
}
|
||||
|
||||
pub trait GetOutput<O> {
|
||||
fn output(&self) -> Option<O>;
|
||||
}
|
||||
|
||||
impl<'a,I,O,E> GetInput<&'a[I]> for IResult<&'a[I],O,E> {
|
||||
fn remaining_input(&self) -> Option<&'a[I]> {
|
||||
match *self {
|
||||
Done(ref i,_) => Some(*i),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<O,E> GetInput<()> for IResult<(),O,E> {
|
||||
fn remaining_input(&self) -> Option<()> {
|
||||
match *self {
|
||||
Done((),_) => Some(()),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a,O,E> GetInput<&'a str> for IResult<&'a str,O,E> {
|
||||
fn remaining_input(&self) -> Option<&'a str> {
|
||||
match *self {
|
||||
Done(ref i,_) => Some(*i),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a,I,O,E> GetOutput<&'a[O]> for IResult<I,&'a[O],E> {
|
||||
fn output(&self) -> Option<&'a[O]> {
|
||||
match *self {
|
||||
Done(_, ref o) => Some(*o),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I,E> GetOutput<()> for IResult<I,(),E> {
|
||||
fn output(&self) -> Option<()> {
|
||||
match *self {
|
||||
Done(_,()) => Some(()),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a,I,E> GetOutput<&'a str> for IResult<I,&'a str,E> {
|
||||
fn output(&self) -> Option<&'a str> {
|
||||
match *self {
|
||||
Done(_,ref o) => Some(*o),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
use std::any::Any;
|
||||
#[cfg(not(feature = "core"))]
|
||||
use std::{error,fmt};
|
||||
#[cfg(not(feature = "core"))]
|
||||
use std::fmt::Debug;
|
||||
#[cfg(not(feature = "core"))]
|
||||
impl<P:Debug+Any,E:Debug+Any> error::Error for Err<P,E> {
|
||||
fn description(&self) -> &str {
|
||||
let kind = match *self {
|
||||
Err::Code(ref e) | Err::Node(ref e, _) | Err::Position(ref e, _) | Err::NodePosition(ref e, _, _) => e
|
||||
};
|
||||
kind.description()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
impl<P:fmt::Debug,E:fmt::Debug> fmt::Display for Err<P,E> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Err::Code(ref e) | Err::Node(ref e, _) => {
|
||||
write!(f, "{:?}", e)
|
||||
},
|
||||
Err::Position(ref e, ref p) | Err::NodePosition(ref e, ref p, _) => {
|
||||
write!(f, "{:?}:{:?}", p, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use util::ErrorKind;
|
||||
|
||||
const REST: [u8; 0] = [];
|
||||
const DONE: IResult<&'static [u8], u32> = IResult::Done(&REST, 5);
|
||||
const ERROR: IResult<&'static [u8], u32> = IResult::Error(Err::Code(ErrorKind::Tag));
|
||||
const INCOMPLETE: IResult<&'static [u8], u32> = IResult::Incomplete(Needed::Unknown);
|
||||
|
||||
#[test]
|
||||
fn needed_map() {
|
||||
let unknown = Needed::Unknown;
|
||||
let size = Needed::Size(5);
|
||||
|
||||
assert_eq!(size.map(|x| x * 2), Needed::Size(10));
|
||||
assert_eq!(unknown.map(|x| x * 2), Needed::Unknown);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iresult_map() {
|
||||
assert_eq!(DONE.map(|x| x * 2), IResult::Done(&b""[..], 10));
|
||||
assert_eq!(ERROR.map(|x| x * 2), IResult::Error(Err::Code(ErrorKind::Tag)));
|
||||
assert_eq!(INCOMPLETE.map(|x| x * 2), IResult::Incomplete(Needed::Unknown));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iresult_map_inc() {
|
||||
let inc_unknown: IResult<&[u8], u32> = IResult::Incomplete(Needed::Unknown);
|
||||
let inc_size: IResult<&[u8], u32> = IResult::Incomplete(Needed::Size(5));
|
||||
|
||||
assert_eq!(DONE.map_inc(|n| if let Needed::Size(i) = n {Needed::Size(i+1)} else {n}), IResult::Done(&b""[..], 5));
|
||||
assert_eq!(ERROR.map_inc(|n| if let Needed::Size(i) = n {Needed::Size(i+1)} else {n}), IResult::Error(Err::Code(ErrorKind::Tag)));
|
||||
assert_eq!(inc_unknown.map_inc(|n| if let Needed::Size(i) = n {Needed::Size(i+1)} else {n}), IResult::Incomplete(Needed::Unknown));
|
||||
assert_eq!(inc_size.map_inc(|n| if let Needed::Size(i) = n {Needed::Size(i+1)} else {n}), IResult::Incomplete(Needed::Size(6)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iresult_map_err() {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
struct Error(u32);
|
||||
|
||||
let error_kind = Err::Code(ErrorKind::Custom(Error(5)));
|
||||
|
||||
assert_eq!(DONE.map_err(|_| error_kind.clone()), IResult::Done(&b""[..], 5));
|
||||
assert_eq!(ERROR.map_err(|x| {println!("err: {:?}", x); error_kind.clone()}), IResult::Error(error_kind.clone()));
|
||||
assert_eq!(INCOMPLETE.map_err(|x| {println!("err: {:?}", x); error_kind.clone()}), IResult::Incomplete(Needed::Unknown));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iresult_unwrap_on_done() {
|
||||
assert_eq!(DONE.unwrap(), (&b""[..], 5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn iresult_unwrap_on_err() {
|
||||
ERROR.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn iresult_unwrap_on_inc() {
|
||||
INCOMPLETE.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn iresult_unwrap_err_on_done() {
|
||||
DONE.unwrap_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iresult_unwrap_err_on_err() {
|
||||
assert_eq!(ERROR.unwrap_err(), Err::Code(ErrorKind::Tag));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn iresult_unwrap_err_on_inc() {
|
||||
INCOMPLETE.unwrap_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn iresult_unwrap_inc_on_done() {
|
||||
DONE.unwrap_inc();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn iresult_unwrap_inc_on_err() {
|
||||
ERROR.unwrap_inc();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iresult_unwrap_inc_on_inc() {
|
||||
assert_eq!(INCOMPLETE.unwrap_inc(), Needed::Unknown);
|
||||
}
|
||||
}
|
|
@ -1,151 +0,0 @@
|
|||
//! nom, eating data byte by byte
|
||||
//!
|
||||
//! nom is a parser combinator library with a focus on safe parsing,
|
||||
//! streaming patterns, and as much as possible zero copy.
|
||||
//!
|
||||
//! The code is available on [Github](https://github.com/Geal/nom)
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```
|
||||
//! #[macro_use]
|
||||
//! extern crate nom;
|
||||
//!
|
||||
//! use nom::{IResult,digit};
|
||||
//! use nom::IResult::*;
|
||||
//!
|
||||
//! // Parser definition
|
||||
//!
|
||||
//! use std::str;
|
||||
//! use std::str::FromStr;
|
||||
//!
|
||||
//! named!(parens<i64>, delimited!(
|
||||
//! char!('('),
|
||||
//! expr,
|
||||
//! char!(')')
|
||||
//! )
|
||||
//! );
|
||||
//!
|
||||
//! named!(i64_digit<i64>,
|
||||
//! map_res!(
|
||||
//! map_res!(
|
||||
//! digit,
|
||||
//! str::from_utf8
|
||||
//! ),
|
||||
//! FromStr::from_str
|
||||
//! )
|
||||
//! );
|
||||
//!
|
||||
//! // We transform an integer string into a i64
|
||||
//! // we look for a digit suite, and try to convert it.
|
||||
//! // if either str::from_utf8 or FromStr::from_str fail,
|
||||
//! // the parser will fail
|
||||
//! named!(factor<i64>,
|
||||
//! alt!(
|
||||
//! i64_digit
|
||||
//! | parens
|
||||
//! )
|
||||
//! );
|
||||
//!
|
||||
//! // we define acc as mutable to update its value whenever a new term is found
|
||||
//! named!(term <i64>,
|
||||
//! chain!(
|
||||
//! mut acc: factor ~
|
||||
//! many0!(
|
||||
//! alt!(
|
||||
//! tap!(mul: preceded!(tag!("*"), factor) => acc = acc * mul) |
|
||||
//! tap!(div: preceded!(tag!("/"), factor) => acc = acc / div)
|
||||
//! )
|
||||
//! ),
|
||||
//! || { return acc }
|
||||
//! )
|
||||
//! );
|
||||
//!
|
||||
//! named!(expr <i64>,
|
||||
//! chain!(
|
||||
//! mut acc: term ~
|
||||
//! many0!(
|
||||
//! alt!(
|
||||
//! tap!(add: preceded!(tag!("+"), term) => acc = acc + add) |
|
||||
//! tap!(sub: preceded!(tag!("-"), term) => acc = acc - sub)
|
||||
//! )
|
||||
//! ),
|
||||
//! || { return acc }
|
||||
//! )
|
||||
//! );
|
||||
//!
|
||||
//! fn main() {
|
||||
//! assert_eq!(expr(b"1+2"), IResult::Done(&b""[..], 3));
|
||||
//! assert_eq!(expr(b"12+6-4+3"), IResult::Done(&b""[..], 17));
|
||||
//! assert_eq!(expr(b"1+2*3+4"), IResult::Done(&b""[..], 11));
|
||||
//!
|
||||
//! assert_eq!(expr(b"(2)"), IResult::Done(&b""[..], 2));
|
||||
//! assert_eq!(expr(b"2*(3+4)"), IResult::Done(&b""[..], 14));
|
||||
//! assert_eq!(expr(b"2*2/(5-1)+3"), IResult::Done(&b""[..], 4));
|
||||
//! }
|
||||
//! ```
|
||||
#![cfg_attr(feature = "core", feature(no_std))]
|
||||
#![cfg_attr(feature = "core", feature(collections))]
|
||||
#![cfg_attr(feature = "core", no_std)]
|
||||
#![cfg_attr(feature = "nightly", feature(test))]
|
||||
#![cfg_attr(feature = "nightly", feature(const_fn))]
|
||||
|
||||
#[cfg(feature = "core")]
|
||||
extern crate collections;
|
||||
#[cfg(feature = "regexp")]
|
||||
extern crate regex;
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[macro_use] extern crate lazy_static;
|
||||
#[cfg(feature = "nightly")]
|
||||
extern crate test;
|
||||
|
||||
#[cfg(feature = "core")]
|
||||
mod std {
|
||||
#[macro_use]
|
||||
pub use core::{fmt, iter, option, ops, slice, mem};
|
||||
pub use collections::{boxed, vec, string};
|
||||
pub mod prelude {
|
||||
pub use core::prelude as v1;
|
||||
}
|
||||
}
|
||||
|
||||
pub use self::util::*;
|
||||
pub use self::internal::*;
|
||||
pub use self::macros::*;
|
||||
pub use self::methods::*;
|
||||
pub use self::bytes::*;
|
||||
pub use self::bits::*;
|
||||
|
||||
pub use self::nom::*;
|
||||
pub use self::character::*;
|
||||
|
||||
#[cfg(feature = "regexp")]
|
||||
pub use self::regexp::*;
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
#[cfg(feature = "stream")]
|
||||
pub use self::stream::*;
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
pub use self::str::*;
|
||||
|
||||
#[macro_use] mod util;
|
||||
mod internal;
|
||||
#[macro_use] mod macros;
|
||||
#[macro_use] mod methods;
|
||||
#[macro_use] mod bytes;
|
||||
#[macro_use] mod bits;
|
||||
|
||||
#[macro_use] mod nom;
|
||||
#[macro_use] mod character;
|
||||
|
||||
#[cfg(feature = "regexp")]
|
||||
#[macro_use] mod regexp;
|
||||
|
||||
#[macro_use]
|
||||
#[cfg(not(feature = "core"))]
|
||||
#[cfg(feature = "stream")]
|
||||
mod stream;
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
mod str;
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,480 +0,0 @@
|
|||
//! Method macro combinators
|
||||
//!
|
||||
//! These macros make parsers as methods of structs
|
||||
//! and that can take methods of structs to call
|
||||
//! as parsers.
|
||||
//!
|
||||
//! There is a trick to make them easier to assemble,
|
||||
//! combinators are defined like this:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! macro_rules! tag (
|
||||
//! ($i:expr, $inp: expr) => (
|
||||
//! {
|
||||
//! ...
|
||||
//! }
|
||||
//! );
|
||||
//! );
|
||||
//! ```
|
||||
//!
|
||||
//! But when used as methods in other combinators, are used
|
||||
//! like this:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! method!(my_function<Parser<'a> >, self, tag!("abcd"));
|
||||
//! ```
|
||||
//!
|
||||
//! Internally, other combinators will rewrite
|
||||
//! that call to pass the input as second argument:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! macro_rules! method (
|
||||
//! ($name:ident<$a:ty>, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
//! fn $name( $self_: $a, i: &[u8] ) -> $crate::IResult<&[u8], &[u8]> {
|
||||
//! $submac!(i, $($args)*)
|
||||
//! }
|
||||
//! );
|
||||
//! );
|
||||
//! ```
|
||||
//!
|
||||
//! The `method!` macro is similar to the `named!` macro in the macros module.
|
||||
//! While `named!` will create a parser function, `method!` will create a parser
|
||||
//! method on the struct it is defined in.
|
||||
//!
|
||||
//! Compared to the `named!` macro there are a few differences in how they are
|
||||
//! invoked. A `method!` invocation always has to have the type of `self`
|
||||
//! declared and it can't be a reference due to Rust's borrow lifetime
|
||||
//! restrictions:
|
||||
//! ```ignore
|
||||
//! // -`self`'s type-
|
||||
//! method!(method_name< Parser<'a> >, ...);
|
||||
//! ```
|
||||
//! `self`'s type always comes first.
|
||||
//! The next difference is you have to input the self struct. Due to Rust's
|
||||
//! macro hygiene the macro can't declare it on it's own.
|
||||
//! ```ignore
|
||||
//! // -self-
|
||||
//! method!(method_name<Parser<'a>, &'a str, &'a str>, self, ...);
|
||||
//! ```
|
||||
//! When making a parsing struct with parsing methods, due to the static borrow
|
||||
//! checker,calling any parsing methods on self (or any other parsing struct)
|
||||
//! will cause self to be moved for the rest of the method.To get around this
|
||||
//! restriction all self is moved into the called method and then the called
|
||||
//! method will return self to the caller.
|
||||
//!
|
||||
//! To call a method on self you need to use the `call_m!` macro. For example:
|
||||
//! ```ignore
|
||||
//! struct<'a> Parser<'a> {
|
||||
//! parsed: &'a str,
|
||||
//! }
|
||||
//! impl<'a> Parser<'a> {
|
||||
//! // Constructor omitted for brevity
|
||||
//! method!(take4<Parser<'a>, &'a str, &'a str>, self, take!(4));
|
||||
//! method!(caller<Parser<'a>, &'a str, &'a str>, self, call_m!(self.take4));
|
||||
//! }
|
||||
//! ```
|
||||
//! More complicated combinations still mostly look the same as their `named!`
|
||||
//! counterparts:
|
||||
//! ```ignore
|
||||
//! method!(pub simple_chain<&mut Parser<'a>, &'a str, &'a str>, self,
|
||||
//! chain!(
|
||||
//! call_m!(self.tag_abc) ~
|
||||
//! call_m!(self.tag_def) ~
|
||||
//! call_m!(self.tag_ghi) ~
|
||||
//! last: call_m!(self.simple_peek) ,
|
||||
//! ||{sb.parsed = last; last}
|
||||
//! )
|
||||
//! );
|
||||
//! ```
|
||||
//! The three additions to method definitions to remember are:
|
||||
//! 1. Specify `self`'s type
|
||||
//! 2. Pass `self` to the macro
|
||||
//! 4. Call parser methods using the `call_m!` macro.
|
||||
|
||||
/// Makes a method from a parser combination
|
||||
///
|
||||
/// The must be set up because the compiler needs
|
||||
/// the information
|
||||
///
|
||||
/// ```ignore
|
||||
/// method!(my_function<Parser<'a> >( &[u8] ) -> &[u8], tag!("abcd"));
|
||||
/// // first type parameter is `self`'s type, second is input, third is output
|
||||
/// method!(my_function<Parser<'a>, &[u8], &[u8]>, tag!("abcd"));
|
||||
/// //prefix them with 'pub' to make the methods public
|
||||
/// method!(pub my_function<Parser<'a>,&[u8], &[u8]>, tag!("abcd"));
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! method (
|
||||
// Non-public immutable self
|
||||
($name:ident<$a:ty>( $i:ty ) -> $o:ty, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( $self_: $a, i: $i ) -> ($a, $crate::IResult<$i,$o,u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
($name:ident<$a:ty,$i:ty,$o:ty,$e:ty>, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( $self_: $a, i: $i ) -> ($a, $crate::IResult<$i, $o, $e>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
($name:ident<$a:ty,$i:ty,$o:ty>, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( $self_: $a, i: $i ) -> ($a, $crate::IResult<$i,$o,u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
($name:ident<$a:ty,$o:ty>, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name<'a>( $self_: $a, i: &'a[u8] ) -> ($a, $crate::IResult<&'a [u8], $o, u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
($name:ident<$a:ty>, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( $self_: $a, i: &[u8] ) -> ($a, $crate::IResult<&[u8], &[u8], u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
// Public immutable self
|
||||
(pub $name:ident<$a:ty>( $i:ty ) -> $o:ty, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name( $self_: $a, i: $i ) -> ($a, $crate::IResult<$i,$o,u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$a:ty,$i:ty,$o:ty,$e:ty>, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( $self_: $a, i: $i ) -> ($a, $crate::IResult<$i, $o, $e>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$a:ty,$i:ty,$o:ty>, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name( $self_: $a,i: $i ) -> ($a, $crate::IResult<$i,$o,u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$a:ty,$o:ty>, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name<'a>( $self_: $a, i: &'a[u8] ) -> ($a, $crate::IResult<&'a [u8], $o, u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$a:ty>, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name( $self_: $a, i: &[u8] ) -> ($a, $crate::IResult<&[u8], &[u8], u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
// Non-public mutable self
|
||||
($name:ident<$a:ty>( $i:ty ) -> $o:ty, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( mut $self_: $a, i: $i ) -> ($a, $crate::IResult<$i,$o,u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
($name:ident<$a:ty,$i:ty,$o:ty,$e:ty>, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( mut $self_: $a, i: $i ) -> ($a, $crate::IResult<$i, $o, $e>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
($name:ident<$a:ty,$i:ty,$o:ty>, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( mut $self_: $a, i: $i ) -> ($a, $crate::IResult<$i,$o,u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
($name:ident<$a:ty,$o:ty>, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name<'a>( mut $self_: $a, i: &'a[u8] ) -> ($a, $crate::IResult<&'a [u8], $o, u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
($name:ident<$a:ty>, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( mut $self_: $a, i: &[u8] ) -> ($a, $crate::IResult<&[u8], &[u8], u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
// Public mutable self
|
||||
(pub $name:ident<$a:ty>( $i:ty ) -> $o:ty, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name( mut $self_: $a, i: $i ) -> ($a, $crate::IResult<$i,$o,u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$a:ty,$i:ty,$o:ty,$e:ty>, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( mut $self_: $a, i: $i ) -> ($a, $crate::IResult<$i, $o, $e>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$a:ty,$i:ty,$o:ty>, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name( mut $self_: $a,i: $i ) -> ($a, $crate::IResult<$i,$o,u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$a:ty,$o:ty>, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name<'a>( mut $self_: $a, i: &'a[u8] ) -> ($a, $crate::IResult<&'a [u8], $o, u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$a:ty>, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name( mut $self_: $a, i: &[u8] ) -> ($a, $crate::IResult<&[u8], &[u8], u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// Used to called methods then move self back into self
|
||||
#[macro_export]
|
||||
macro_rules! call_m (
|
||||
($i:expr, $self_:ident.$method:ident) => (
|
||||
{
|
||||
let (tmp, res) = $self_.$method($i);
|
||||
$self_ = tmp;
|
||||
res
|
||||
}
|
||||
);
|
||||
($i:expr, $self_:ident.$method:ident, $($args:expr),* ) => (
|
||||
{
|
||||
let (tmp, res) = $self_.$method($i, $($args),*);
|
||||
$self_ = tmp;
|
||||
res
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
|
||||
/// emulate function currying for method calls on structs
|
||||
/// `apply!(self.my_function, arg1, arg2, ...)` becomes `self.my_function(input, arg1, arg2, ...)`
|
||||
///
|
||||
/// Supports up to 6 arguments
|
||||
#[macro_export]
|
||||
macro_rules! apply_m (
|
||||
($i:expr, $self_:ident.$method:ident, $($args:expr),* ) => ( { let (tmp, res) = $self_.$method( $i, $($args),* ); $self_ = tmp; res } );
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use internal::IResult::*;
|
||||
|
||||
// reproduce the tag_s and take_s macros, because of module import order
|
||||
macro_rules! tag_s (
|
||||
($i:expr, $tag: expr) => (
|
||||
{
|
||||
let res: $crate::IResult<_,_> = if $tag.len() > $i.len() {
|
||||
$crate::IResult::Incomplete($crate::Needed::Size($tag.len()))
|
||||
//} else if &$i[0..$tag.len()] == $tag {
|
||||
} else if ($i).starts_with($tag) {
|
||||
$crate::IResult::Done(&$i[$tag.len()..], &$i[0..$tag.len()])
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Position($crate::ErrorKind::TagStr, $i))
|
||||
};
|
||||
res
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
macro_rules! take_s (
|
||||
($i:expr, $count:expr) => (
|
||||
{
|
||||
let cnt = $count as usize;
|
||||
let res: $crate::IResult<_,_> = if $i.chars().count() < cnt {
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(cnt))
|
||||
} else {
|
||||
let mut offset = $i.len();
|
||||
let mut count = 0;
|
||||
for (o, _) in $i.char_indices() {
|
||||
if count == cnt {
|
||||
offset = o;
|
||||
break;
|
||||
}
|
||||
count += 1;
|
||||
}
|
||||
$crate::IResult::Done(&$i[offset..], &$i[..offset])
|
||||
};
|
||||
res
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
struct Parser<'a> {
|
||||
bcd: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
pub fn new() -> Parser<'a> {
|
||||
Parser{bcd: ""}
|
||||
}
|
||||
|
||||
method!(tag_abc<Parser<'a>, &'a str, &'a str>, self, tag_s!("áβç"));
|
||||
method!(tag_bcd<Parser<'a> >(&'a str) -> &'a str, self, tag_s!("βçδ"));
|
||||
method!(pub tag_hij<Parser<'a> >(&'a str) -> &'a str, self, tag_s!("λïJ"));
|
||||
method!(pub tag_ijk<Parser<'a>, &'a str, &'a str>, self, tag_s!("ïJƙ"));
|
||||
method!(take3<Parser<'a>, &'a str, &'a str>, self, take_s!(3));
|
||||
method!(pub simple_call<Parser<'a>, &'a str, &'a str>, mut self,
|
||||
call_m!(self.tag_abc)
|
||||
);
|
||||
method!(pub simple_peek<Parser<'a>, &'a str, &'a str>, mut self,
|
||||
peek!(call_m!(self.take3))
|
||||
);
|
||||
method!(pub simple_chain<Parser<'a>, &'a str, &'a str>, mut self,
|
||||
chain!(
|
||||
bcd: call_m!(self.tag_bcd) ~
|
||||
last: call_m!(self.simple_peek) ,
|
||||
||{self.bcd = bcd; last}
|
||||
)
|
||||
);
|
||||
fn tag_stuff(mut self: Parser<'a>, input: &'a str, something: &'a str) -> (Parser<'a>, ::IResult<&'a str, &'a str>) {
|
||||
self.bcd = something;
|
||||
let(tmp, res) = self.tag_abc(input);
|
||||
self = tmp;
|
||||
(self, res)
|
||||
}
|
||||
method!(use_apply<Parser<'a>, &'a str, &'a str>, mut self, apply_m!(self.tag_stuff, "βçδ"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_method_call_abc() {
|
||||
let p = Parser::new();
|
||||
let input: &str = "áβçδèƒϱλïJƙ";
|
||||
let consumed: &str = "áβç";
|
||||
let leftover: &str = "δèƒϱλïJƙ";
|
||||
let(_, res) = p.tag_abc(input);
|
||||
match res {
|
||||
Done(extra, output) => { assert!(extra == leftover, "`Parser.tag_abc` consumed leftover input. leftover: {}", extra);
|
||||
assert!(output == consumed, "`Parser.tag_abc` doesnt return the string it consumed \
|
||||
on success. Expected `{}`, got `{}`.", consumed, output);
|
||||
},
|
||||
other => panic!("`Parser.tag_abc` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_method_call_bcd() {
|
||||
let p = Parser::new();
|
||||
let input: &str = "βçδèƒϱλïJƙ";
|
||||
let consumed: &str = "βçδ";
|
||||
let leftover: &str = "èƒϱλïJƙ";
|
||||
let(_, res) = p.tag_bcd(input);
|
||||
match res {
|
||||
Done(extra, output) => { assert!(extra == leftover, "`Parser.tag_bcd` consumed leftover input. leftover: {}", extra);
|
||||
assert!(output == consumed, "`Parser.tag_bcd` doesn't return the string it consumed \
|
||||
on success. Expected `{}`, got `{}`.", consumed, output);
|
||||
},
|
||||
other => panic!("`Parser.tag_bcd` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_method_call_hij() {
|
||||
let p = Parser::new();
|
||||
let input: &str = "λïJƙℓ₥ñôƥ9řƨ";
|
||||
let consumed: &str = "λïJ";
|
||||
let leftover: &str = "ƙℓ₥ñôƥ9řƨ";
|
||||
let(_, res) = p.tag_hij(input);
|
||||
match res {
|
||||
Done(extra, output) => { assert!(extra == leftover, "`Parser.tag_hij` consumed leftover input. leftover: {}", extra);
|
||||
assert!(output == consumed, "`Parser.tag_hij` doesn't return the string it consumed \
|
||||
on success. Expected `{}`, got `{}`.", consumed, output);
|
||||
},
|
||||
other => panic!("`Parser.tag_hij` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_method_call_ijk() {
|
||||
let p = Parser::new();
|
||||
let input: &str = "ïJƙℓ₥ñôƥ9řƨ";
|
||||
let consumed: &str = "ïJƙ";
|
||||
let leftover: &str = "ℓ₥ñôƥ9řƨ";
|
||||
let(_, res) = p.tag_ijk(input);
|
||||
match res {
|
||||
Done(extra, output) => { assert!(extra == leftover, "`Parser.tag_ijk` consumed leftover input. leftover: {}", extra);
|
||||
assert!(output == consumed, "`Parser.tag_ijk` doesn't return the string it consumed \
|
||||
on success. Expected `{}`, got `{}`.", consumed, output);
|
||||
},
|
||||
other => panic!("`Parser.tag_ijk` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn test_method_simple_call() {
|
||||
let p = Parser::new();
|
||||
let input: &str = "áβçδèƒϱλïJƙ";
|
||||
let consumed: &str = "áβç";
|
||||
let leftover: &str = "δèƒϱλïJƙ";
|
||||
let(_, res) = p.simple_call(input);
|
||||
match res {
|
||||
Done(extra, output) => { assert!(extra == leftover, "`Parser.simple_call` consumed leftover input. leftover: {}", extra);
|
||||
assert!(output == consumed, "`Parser.simple_call` doesn't return the string it consumed \
|
||||
on success. Expected `{}`, got `{}`.", consumed, output);
|
||||
},
|
||||
other => panic!("`Parser.simple_call` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_apply_m() {
|
||||
let mut p = Parser::new();
|
||||
let input: &str = "áβçδèƒϱλïJƙ";
|
||||
let consumed: &str = "áβç";
|
||||
let leftover: &str = "δèƒϱλïJƙ";
|
||||
let(tmp, res) = p.use_apply(input);
|
||||
p = tmp;
|
||||
match res {
|
||||
Done(extra, output) => { assert!(extra == leftover, "`Parser.use_apply` consumed leftover input. leftover: {}", extra);
|
||||
assert!(output == consumed, "`Parser.use_apply` doesn't return the string it was supposed to \
|
||||
on success. Expected `{}`, got `{}`.", leftover, output);
|
||||
assert!(p.bcd == "βçδ", "Parser.use_apply didn't modify the parser field correctly: {}", p.bcd);
|
||||
},
|
||||
other => panic!("`Parser.use_apply` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_method_call_peek() {
|
||||
let p = Parser::new();
|
||||
let input: &str = "ж¥ƺáβçδèƒϱλïJƙ";
|
||||
let consumed: &str = "ж¥ƺ";
|
||||
let(_, res) = p.simple_peek(input);
|
||||
match res {
|
||||
Done(extra, output) => { assert!(extra == input, "`Parser.simple_peek` consumed leftover input. leftover: {}", extra);
|
||||
assert!(output == consumed, "`Parser.simple_peek` doesn't return the string it consumed \
|
||||
on success. Expected `{}`, got `{}`.", consumed, output);
|
||||
},
|
||||
other => panic!("`Parser.simple_peek` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_method_call_chain() {
|
||||
let mut p = Parser::new();
|
||||
let input : &str = "βçδδèƒϱλïJƙℓ";
|
||||
let leftover : &str = "δèƒϱλïJƙℓ";
|
||||
let output : &str = "늟";
|
||||
let(tmp, res) = p.simple_chain(input);
|
||||
p = tmp;
|
||||
match res {
|
||||
Done(extra, out) => { assert!(extra == leftover, "`Parser.simple_chain` consumed leftover input. leftover: {}", extra);
|
||||
assert!(out == output, "`Parser.simple_chain` doesn't return the string it was supposed to \
|
||||
on success. Expected `{}`, got `{}`.", output, out);
|
||||
assert!(p.bcd == "βçδ", "Parser.simple_chain didn't modify the parser field correctly: {}", p.bcd);
|
||||
},
|
||||
other => panic!("`Parser.simple_chain` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,950 +0,0 @@
|
|||
//! Useful parser combinators
|
||||
//!
|
||||
//! A number of useful parser combinators have already been implemented.
|
||||
//! Some of them use macros, other are implemented through functions.
|
||||
//! Hopefully, the syntax will converge to onely one way in the future,
|
||||
//! but the macros system makes no promises.
|
||||
//!
|
||||
|
||||
#[cfg(feature = "core")]
|
||||
use std::prelude::v1::*;
|
||||
use std::boxed::Box;
|
||||
|
||||
use std::fmt::Debug;
|
||||
use internal::*;
|
||||
use internal::IResult::*;
|
||||
use internal::Err::*;
|
||||
use util::{ErrorKind,IterIndices,AsChar,InputLength};
|
||||
use std::mem::transmute;
|
||||
|
||||
#[inline]
|
||||
pub fn tag_cl<'a,'b>(rec:&'a[u8]) -> Box<Fn(&'b[u8]) -> IResult<&'b[u8], &'b[u8]> + 'a> {
|
||||
Box::new(move |i: &'b[u8]| -> IResult<&'b[u8], &'b[u8]> {
|
||||
if i.len() >= rec.len() && &i[0..rec.len()] == rec {
|
||||
Done(&i[rec.len()..], &i[0..rec.len()])
|
||||
} else {
|
||||
Error(Position(ErrorKind::TagClosure, i))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
#[inline]
|
||||
pub fn print<T: Debug>(input: T) -> IResult<T, ()> {
|
||||
println!("{:?}", input);
|
||||
Done(input, ())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn begin(input: &[u8]) -> IResult<(), &[u8]> {
|
||||
Done((), input)
|
||||
}
|
||||
|
||||
// FIXME: when rust-lang/rust#17436 is fixed, macros will be able to export
|
||||
// public methods
|
||||
//pub is_not!(line_ending b"\r\n")
|
||||
pub fn not_line_ending(input:&[u8]) -> IResult<&[u8], &[u8]> {
|
||||
for (idx, item) in input.iter().enumerate() {
|
||||
for &i in b"\r\n".iter() {
|
||||
if *item == i {
|
||||
return Done(&input[idx..], &input[0..idx])
|
||||
}
|
||||
}
|
||||
}
|
||||
Done(&input[input.len()..], input)
|
||||
}
|
||||
|
||||
named!(tag_ln, tag!("\n"));
|
||||
|
||||
/// Recognizes a line feed
|
||||
#[inline]
|
||||
pub fn line_ending(input:&[u8]) -> IResult<&[u8], &[u8]> {
|
||||
tag_ln(input)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_alphabetic(chr:u8) -> bool {
|
||||
(chr >= 0x41 && chr <= 0x5A) || (chr >= 0x61 && chr <= 0x7A)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_digit(chr: u8) -> bool {
|
||||
chr >= 0x30 && chr <= 0x39
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_hex_digit(chr: u8) -> bool {
|
||||
(chr >= 0x30 && chr <= 0x39) ||
|
||||
(chr >= 0x41 && chr <= 0x46) ||
|
||||
(chr >= 0x61 && chr <= 0x66)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_oct_digit(chr: u8) -> bool {
|
||||
chr >= 0x30 && chr <= 0x37
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_alphanumeric(chr: u8) -> bool {
|
||||
is_alphabetic(chr) || is_digit(chr)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_space(chr:u8) -> bool {
|
||||
chr == ' ' as u8 || chr == '\t' as u8
|
||||
}
|
||||
|
||||
// FIXME: when rust-lang/rust#17436 is fixed, macros will be able to export
|
||||
//pub filter!(alpha is_alphabetic)
|
||||
//pub filter!(digit is_digit)
|
||||
//pub filter!(hex_digit is_hex_digit)
|
||||
//pub filter!(oct_digit is_oct_digit)
|
||||
//pub filter!(alphanumeric is_alphanumeric)
|
||||
|
||||
use std::ops::{Index,Range,RangeFrom};
|
||||
/// Recognizes lowercase and uppercase alphabetic characters: a-zA-Z
|
||||
pub fn alpha<'a, T: ?Sized>(input:&'a T) -> IResult<&'a T, &'a T> where
|
||||
T:Index<Range<usize>, Output=T>+Index<RangeFrom<usize>, Output=T>,
|
||||
&'a T: IterIndices+InputLength {
|
||||
let input_length = input.input_len();
|
||||
if input_length == 0 {
|
||||
return Error(Position(ErrorKind::Alpha, input))
|
||||
}
|
||||
|
||||
for (idx, item) in input.iter_indices() {
|
||||
if ! item.is_alpha() {
|
||||
if idx == 0 {
|
||||
return Error(Position(ErrorKind::Alpha, input))
|
||||
} else {
|
||||
return Done(&input[idx..], &input[0..idx])
|
||||
}
|
||||
}
|
||||
}
|
||||
Done(&input[input_length..], input)
|
||||
}
|
||||
|
||||
/// Recognizes numerical characters: 0-9
|
||||
pub fn digit<'a, T: ?Sized>(input:&'a T) -> IResult<&'a T, &'a T> where
|
||||
T:Index<Range<usize>, Output=T>+Index<RangeFrom<usize>, Output=T>,
|
||||
&'a T: IterIndices+InputLength {
|
||||
let input_length = input.input_len();
|
||||
if input_length == 0 {
|
||||
return Error(Position(ErrorKind::Digit, input))
|
||||
}
|
||||
|
||||
for (idx, item) in input.iter_indices() {
|
||||
if ! item.is_0_to_9() {
|
||||
if idx == 0 {
|
||||
return Error(Position(ErrorKind::Digit, input))
|
||||
} else {
|
||||
return Done(&input[idx..], &input[0..idx])
|
||||
}
|
||||
}
|
||||
}
|
||||
Done(&input[input_length..], input)
|
||||
}
|
||||
|
||||
/// Recognizes hexadecimal numerical characters: 0-9, A-F, a-f
|
||||
pub fn hex_digit<'a, T: ?Sized>(input:&'a T) -> IResult<&'a T, &'a T> where
|
||||
T:Index<Range<usize>, Output=T>+Index<RangeFrom<usize>, Output=T>,
|
||||
&'a T: IterIndices+InputLength {
|
||||
let input_length = input.input_len();
|
||||
if input_length == 0 {
|
||||
return Error(Position(ErrorKind::HexDigit, input))
|
||||
}
|
||||
|
||||
for (idx, item) in input.iter_indices() {
|
||||
if ! item.is_hex_digit() {
|
||||
if idx == 0 {
|
||||
return Error(Position(ErrorKind::HexDigit, input))
|
||||
} else {
|
||||
return Done(&input[idx..], &input[0..idx])
|
||||
}
|
||||
}
|
||||
}
|
||||
Done(&input[input_length..], input)
|
||||
}
|
||||
|
||||
/// Recognizes octal characters: 0-7
|
||||
pub fn oct_digit<'a, T: ?Sized>(input:&'a T) -> IResult<&'a T, &'a T> where
|
||||
T:Index<Range<usize>, Output=T>+Index<RangeFrom<usize>, Output=T>,
|
||||
&'a T: IterIndices+InputLength {
|
||||
let input_length = input.input_len();
|
||||
if input_length == 0 {
|
||||
return Error(Position(ErrorKind::OctDigit, input))
|
||||
}
|
||||
|
||||
for (idx, item) in input.iter_indices() {
|
||||
if ! item.is_oct_digit() {
|
||||
if idx == 0 {
|
||||
return Error(Position(ErrorKind::OctDigit, input))
|
||||
} else {
|
||||
return Done(&input[idx..], &input[0..idx])
|
||||
}
|
||||
}
|
||||
}
|
||||
Done(&input[input_length..], input)
|
||||
}
|
||||
|
||||
/// Recognizes numerical and alphabetic characters: 0-9a-zA-Z
|
||||
pub fn alphanumeric<'a, T: ?Sized>(input:&'a T) -> IResult<&'a T, &'a T> where
|
||||
T:Index<Range<usize>, Output=T>+Index<RangeFrom<usize>, Output=T>,
|
||||
&'a T: IterIndices+InputLength {
|
||||
let input_length = input.input_len();
|
||||
if input_length == 0 {
|
||||
return Error(Position(ErrorKind::AlphaNumeric, input));
|
||||
}
|
||||
|
||||
for (idx, item) in input.iter_indices() {
|
||||
if ! item.is_alphanum() {
|
||||
if idx == 0 {
|
||||
return Error(Position(ErrorKind::AlphaNumeric, input))
|
||||
} else {
|
||||
return Done(&input[idx..], &input[0..idx])
|
||||
}
|
||||
}
|
||||
}
|
||||
Done(&input[input_length..], input)
|
||||
}
|
||||
|
||||
/// Recognizes spaces and tabs
|
||||
pub fn space<'a, T: ?Sized>(input:&'a T) -> IResult<&'a T, &'a T> where
|
||||
T:Index<Range<usize>, Output=T>+Index<RangeFrom<usize>, Output=T>,
|
||||
&'a T: IterIndices+InputLength {
|
||||
let input_length = input.input_len();
|
||||
if input_length == 0 {
|
||||
return Error(Position(ErrorKind::Space, input));
|
||||
}
|
||||
|
||||
for (idx, item) in input.iter_indices() {
|
||||
let chr = item.as_char();
|
||||
if ! (chr == ' ' || chr == '\t') {
|
||||
if idx == 0 {
|
||||
return Error(Position(ErrorKind::Space, input))
|
||||
} else {
|
||||
return Done(&input[idx..], &input[0..idx])
|
||||
}
|
||||
}
|
||||
}
|
||||
Done(&input[input_length..], input)
|
||||
}
|
||||
|
||||
/// Recognizes spaces, tabs, carriage returns and line feeds
|
||||
pub fn multispace<'a, T: ?Sized>(input:&'a T) -> IResult<&'a T, &'a T> where
|
||||
T:Index<Range<usize>, Output=T>+Index<RangeFrom<usize>, Output=T>,
|
||||
&'a T: IterIndices+InputLength {
|
||||
let input_length = input.input_len();
|
||||
if input_length == 0 {
|
||||
return Error(Position(ErrorKind::MultiSpace, input));
|
||||
}
|
||||
|
||||
for (idx, item) in input.iter_indices() {
|
||||
let chr = item.as_char();
|
||||
if ! (chr == ' ' || chr == '\t' || chr == '\r' || chr == '\n') {
|
||||
if idx == 0 {
|
||||
return Error(Position(ErrorKind::MultiSpace, input))
|
||||
} else {
|
||||
return Done(&input[idx..], &input[0..idx])
|
||||
}
|
||||
}
|
||||
}
|
||||
Done(&input[input_length..], input)
|
||||
}
|
||||
|
||||
pub fn sized_buffer(input:&[u8]) -> IResult<&[u8], &[u8]> {
|
||||
if input.is_empty() {
|
||||
return Incomplete(Needed::Unknown)
|
||||
}
|
||||
|
||||
let len = input[0] as usize;
|
||||
|
||||
if input.len() >= len + 1 {
|
||||
Done(&input[len+1..], &input[1..len+1])
|
||||
} else {
|
||||
Incomplete(Needed::Size(1 + len))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn length_value(input:&[u8]) -> IResult<&[u8], &[u8]> {
|
||||
let input_len = input.len();
|
||||
if input_len == 0 {
|
||||
return Error(Position(ErrorKind::LengthValueFn, input))
|
||||
}
|
||||
|
||||
let len = input[0] as usize;
|
||||
if input_len - 1 >= len {
|
||||
IResult::Done(&input[len+1..], &input[1..len+1])
|
||||
} else {
|
||||
IResult::Incomplete(Needed::Size(1+len))
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes an unsigned 1 byte integer (equivalent to take!(1)
|
||||
#[inline]
|
||||
pub fn be_u8(i: &[u8]) -> IResult<&[u8], u8> {
|
||||
if i.len() < 1 {
|
||||
Incomplete(Needed::Size(1))
|
||||
} else {
|
||||
Done(&i[1..], i[0])
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes big endian unsigned 2 bytes integer
|
||||
#[inline]
|
||||
pub fn be_u16(i: &[u8]) -> IResult<&[u8], u16> {
|
||||
if i.len() < 2 {
|
||||
Incomplete(Needed::Size(2))
|
||||
} else {
|
||||
let res = ((i[0] as u16) << 8) + i[1] as u16;
|
||||
Done(&i[2..], res)
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes big endian unsigned 4 bytes integer
|
||||
#[inline]
|
||||
pub fn be_u32(i: &[u8]) -> IResult<&[u8], u32> {
|
||||
if i.len() < 4 {
|
||||
Incomplete(Needed::Size(4))
|
||||
} else {
|
||||
let res = ((i[0] as u32) << 24) + ((i[1] as u32) << 16) + ((i[2] as u32) << 8) + i[3] as u32;
|
||||
Done(&i[4..], res)
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes big endian unsigned 8 bytes integer
|
||||
#[inline]
|
||||
pub fn be_u64(i: &[u8]) -> IResult<&[u8], u64> {
|
||||
if i.len() < 8 {
|
||||
Incomplete(Needed::Size(8))
|
||||
} else {
|
||||
let res = ((i[0] as u64) << 56) + ((i[1] as u64) << 48) + ((i[2] as u64) << 40) + ((i[3] as u64) << 32) +
|
||||
((i[4] as u64) << 24) + ((i[5] as u64) << 16) + ((i[6] as u64) << 8) + i[7] as u64;
|
||||
Done(&i[8..], res)
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes a signed 1 byte integer (equivalent to take!(1)
|
||||
#[inline]
|
||||
pub fn be_i8(i:&[u8]) -> IResult<&[u8], i8> {
|
||||
map!(i, be_u8, | x | { x as i8 })
|
||||
}
|
||||
|
||||
/// Recognizes big endian signed 2 bytes integer
|
||||
#[inline]
|
||||
pub fn be_i16(i:&[u8]) -> IResult<&[u8], i16> {
|
||||
map!(i, be_u16, | x | { x as i16 })
|
||||
}
|
||||
|
||||
/// Recognizes big endian signed 4 bytes integer
|
||||
#[inline]
|
||||
pub fn be_i32(i:&[u8]) -> IResult<&[u8], i32> {
|
||||
map!(i, be_u32, | x | { x as i32 })
|
||||
}
|
||||
|
||||
/// Recognizes big endian signed 8 bytes integer
|
||||
#[inline]
|
||||
pub fn be_i64(i:&[u8]) -> IResult<&[u8], i64> {
|
||||
map!(i, be_u64, | x | { x as i64 })
|
||||
}
|
||||
|
||||
/// Recognizes an unsigned 1 byte integer (equivalent to take!(1)
|
||||
#[inline]
|
||||
pub fn le_u8(i: &[u8]) -> IResult<&[u8], u8> {
|
||||
if i.len() < 1 {
|
||||
Incomplete(Needed::Size(1))
|
||||
} else {
|
||||
Done(&i[1..], i[0])
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes little endian unsigned 2 bytes integer
|
||||
#[inline]
|
||||
pub fn le_u16(i: &[u8]) -> IResult<&[u8], u16> {
|
||||
if i.len() < 2 {
|
||||
Incomplete(Needed::Size(2))
|
||||
} else {
|
||||
let res = ((i[1] as u16) << 8) + i[0] as u16;
|
||||
Done(&i[2..], res)
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes little endian unsigned 4 bytes integer
|
||||
#[inline]
|
||||
pub fn le_u32(i: &[u8]) -> IResult<&[u8], u32> {
|
||||
if i.len() < 4 {
|
||||
Incomplete(Needed::Size(4))
|
||||
} else {
|
||||
let res = ((i[3] as u32) << 24) + ((i[2] as u32) << 16) + ((i[1] as u32) << 8) + i[0] as u32;
|
||||
Done(&i[4..], res)
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes little endian unsigned 8 bytes integer
|
||||
#[inline]
|
||||
pub fn le_u64(i: &[u8]) -> IResult<&[u8], u64> {
|
||||
if i.len() < 8 {
|
||||
Incomplete(Needed::Size(8))
|
||||
} else {
|
||||
let res = ((i[7] as u64) << 56) + ((i[6] as u64) << 48) + ((i[5] as u64) << 40) + ((i[4] as u64) << 32) +
|
||||
((i[3] as u64) << 24) + ((i[2] as u64) << 16) + ((i[1] as u64) << 8) + i[0] as u64;
|
||||
Done(&i[8..], res)
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes a signed 1 byte integer (equivalent to take!(1)
|
||||
#[inline]
|
||||
pub fn le_i8(i:&[u8]) -> IResult<&[u8], i8> {
|
||||
map!(i, le_u8, | x | { x as i8 })
|
||||
}
|
||||
|
||||
/// Recognizes little endian signed 2 bytes integer
|
||||
#[inline]
|
||||
pub fn le_i16(i:&[u8]) -> IResult<&[u8], i16> {
|
||||
map!(i, le_u16, | x | { x as i16 })
|
||||
}
|
||||
|
||||
/// Recognizes little endian signed 4 bytes integer
|
||||
#[inline]
|
||||
pub fn le_i32(i:&[u8]) -> IResult<&[u8], i32> {
|
||||
map!(i, le_u32, | x | { x as i32 })
|
||||
}
|
||||
|
||||
/// Recognizes little endian signed 8 bytes integer
|
||||
#[inline]
|
||||
pub fn le_i64(i:&[u8]) -> IResult<&[u8], i64> {
|
||||
map!(i, le_u64, | x | { x as i64 })
|
||||
}
|
||||
|
||||
/// if parameter is true, parse a big endian u16 integer,
|
||||
/// otherwise a little endian u16 integer
|
||||
#[macro_export]
|
||||
macro_rules! u16 ( ($i:expr, $e:expr) => ( {if $e { $crate::be_u16($i) } else { $crate::le_u16($i) } } ););
|
||||
/// if parameter is true, parse a big endian u32 integer,
|
||||
/// otherwise a little endian u32 integer
|
||||
#[macro_export]
|
||||
macro_rules! u32 ( ($i:expr, $e:expr) => ( {if $e { $crate::be_u32($i) } else { $crate::le_u32($i) } } ););
|
||||
/// if parameter is true, parse a big endian u64 integer,
|
||||
/// otherwise a little endian u64 integer
|
||||
#[macro_export]
|
||||
macro_rules! u64 ( ($i:expr, $e:expr) => ( {if $e { $crate::be_u64($i) } else { $crate::le_u64($i) } } ););
|
||||
|
||||
/// if parameter is true, parse a big endian i16 integer,
|
||||
/// otherwise a little endian i16 integer
|
||||
#[macro_export]
|
||||
macro_rules! i16 ( ($i:expr, $e:expr) => ( {if $e { $crate::be_i16($i) } else { $crate::le_i16($i) } } ););
|
||||
/// if parameter is true, parse a big endian i32 integer,
|
||||
/// otherwise a little endian i32 integer
|
||||
#[macro_export]
|
||||
macro_rules! i32 ( ($i:expr, $e:expr) => ( {if $e { $crate::be_i32($i) } else { $crate::le_i32($i) } } ););
|
||||
/// if parameter is true, parse a big endian i64 integer,
|
||||
/// otherwise a little endian i64 integer
|
||||
#[macro_export]
|
||||
macro_rules! i64 ( ($i:expr, $e:expr) => ( {if $e { $crate::be_i64($i) } else { $crate::le_i64($i) } } ););
|
||||
|
||||
/// Recognizes big endian 4 bytes floating point number
|
||||
#[inline]
|
||||
pub fn be_f32(input: &[u8]) -> IResult<&[u8], f32> {
|
||||
match be_u32(input) {
|
||||
Error(e) => Error(e),
|
||||
Incomplete(e) => Incomplete(e),
|
||||
Done(i,o) => {
|
||||
unsafe {
|
||||
Done(i, transmute::<u32, f32>(o))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes big endian 8 bytes floating point number
|
||||
#[inline]
|
||||
pub fn be_f64(input: &[u8]) -> IResult<&[u8], f64> {
|
||||
match be_u64(input) {
|
||||
Error(e) => Error(e),
|
||||
Incomplete(e) => Incomplete(e),
|
||||
Done(i,o) => {
|
||||
unsafe {
|
||||
Done(i, transmute::<u64, f64>(o))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes little endian 4 bytes floating point number
|
||||
#[inline]
|
||||
pub fn le_f32(input: &[u8]) -> IResult<&[u8], f32> {
|
||||
match le_u32(input) {
|
||||
Error(e) => Error(e),
|
||||
Incomplete(e) => Incomplete(e),
|
||||
Done(i,o) => {
|
||||
unsafe {
|
||||
Done(i, transmute::<u32, f32>(o))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes little endian 8 bytes floating point number
|
||||
#[inline]
|
||||
pub fn le_f64(input: &[u8]) -> IResult<&[u8], f64> {
|
||||
match le_u64(input) {
|
||||
Error(e) => Error(e),
|
||||
Incomplete(e) => Incomplete(e),
|
||||
Done(i,o) => {
|
||||
unsafe {
|
||||
Done(i, transmute::<u64, f64>(o))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes a hex-encoded integer
|
||||
#[inline]
|
||||
pub fn hex_u32(input: &[u8]) -> IResult<&[u8], u32> {
|
||||
match is_a!(input, &b"0123456789abcdef"[..]) {
|
||||
Error(e) => Error(e),
|
||||
Incomplete(e) => Incomplete(e),
|
||||
Done(i,o) => {
|
||||
let mut res = 0u32;
|
||||
|
||||
// Do not parse more than 8 characters for a u32
|
||||
let mut remaining = i;
|
||||
let mut parsed = o;
|
||||
if o.len() > 8 {
|
||||
remaining = &input[8..];
|
||||
parsed = &input[..8];
|
||||
}
|
||||
|
||||
for &e in parsed {
|
||||
let digit = e as char;
|
||||
let value = digit.to_digit(16).unwrap_or(0);
|
||||
res = value + (res << 4);
|
||||
}
|
||||
Done(remaining, res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes empty input buffers
|
||||
///
|
||||
/// useful to verify that the previous parsers used all of the input
|
||||
#[inline]
|
||||
//pub fn eof(input:&[u8]) -> IResult<&[u8], &[u8]> {
|
||||
pub fn eof<'a, T:?Sized>(input: &'a T) -> IResult<&'a T,&'a T> where
|
||||
T:Index<Range<usize>, Output=T>+Index<RangeFrom<usize>, Output=T>,
|
||||
&'a T: InputLength {
|
||||
if input.input_len() == 0 {
|
||||
Done(input, input)
|
||||
} else {
|
||||
Error(Position(ErrorKind::Eof, input))
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes non empty buffers
|
||||
#[inline]
|
||||
pub fn non_empty<'a, T:?Sized>(input: &'a T) -> IResult<&'a T,&'a T> where
|
||||
T:Index<Range<usize>, Output=T>+Index<RangeFrom<usize>, Output=T>,
|
||||
&'a T: InputLength {
|
||||
if input.input_len() == 0 {
|
||||
Error(Position(ErrorKind::NonEmpty, input))
|
||||
} else {
|
||||
Done(&input[input.input_len()..], input)
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the remaining input.
|
||||
#[inline]
|
||||
pub fn rest(input: &[u8]) -> IResult<&[u8], &[u8]> {
|
||||
IResult::Done(&input[input.len()..], input)
|
||||
}
|
||||
|
||||
/// Return the remaining input, for strings.
|
||||
#[inline]
|
||||
pub fn rest_s(input: &str) -> IResult<&str, &str> {
|
||||
IResult::Done(&input[input.len()..], input)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use internal::{Needed,IResult};
|
||||
use internal::IResult::*;
|
||||
use internal::Err::*;
|
||||
use util::ErrorKind;
|
||||
|
||||
#[test]
|
||||
fn tag_closure() {
|
||||
let x = tag_cl(&b"abcd"[..]);
|
||||
let r = x(&b"abcdabcdefgh"[..]);
|
||||
assert_eq!(r, Done(&b"abcdefgh"[..], &b"abcd"[..]));
|
||||
|
||||
let r2 = x(&b"abcefgh"[..]);
|
||||
assert_eq!(r2, Error(Position(ErrorKind::TagClosure, &b"abcefgh"[..])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn character() {
|
||||
let empty: &[u8] = b"";
|
||||
let a: &[u8] = b"abcd";
|
||||
let b: &[u8] = b"1234";
|
||||
let c: &[u8] = b"a123";
|
||||
let d: &[u8] = "azé12".as_bytes();
|
||||
let e: &[u8] = b" ";
|
||||
assert_eq!(alpha(a), Done(empty, a));
|
||||
assert_eq!(alpha(b), Error(Position(ErrorKind::Alpha,b)));
|
||||
assert_eq!(alpha(c), Done(&c[1..], &b"a"[..]));
|
||||
assert_eq!(alpha(d), Done("é12".as_bytes(), &b"az"[..]));
|
||||
assert_eq!(digit(a), Error(Position(ErrorKind::Digit,a)));
|
||||
assert_eq!(digit(b), Done(empty, b));
|
||||
assert_eq!(digit(c), Error(Position(ErrorKind::Digit,c)));
|
||||
assert_eq!(digit(d), Error(Position(ErrorKind::Digit,d)));
|
||||
assert_eq!(hex_digit(a), Done(empty, a));
|
||||
assert_eq!(hex_digit(b), Done(empty, b));
|
||||
assert_eq!(hex_digit(c), Done(empty, c));
|
||||
assert_eq!(hex_digit(d), Done("zé12".as_bytes(), &b"a"[..]));
|
||||
assert_eq!(hex_digit(e), Error(Position(ErrorKind::HexDigit,e)));
|
||||
assert_eq!(oct_digit(a), Error(Position(ErrorKind::OctDigit,a)));
|
||||
assert_eq!(oct_digit(b), Done(empty, b));
|
||||
assert_eq!(oct_digit(c), Error(Position(ErrorKind::OctDigit,c)));
|
||||
assert_eq!(oct_digit(d), Error(Position(ErrorKind::OctDigit,d)));
|
||||
assert_eq!(alphanumeric(a), Done(empty, a));
|
||||
assert_eq!(fix_error!(b,(), alphanumeric), Done(empty, b));
|
||||
assert_eq!(alphanumeric(c), Done(empty, c));
|
||||
assert_eq!(alphanumeric(d), Done("é12".as_bytes(), &b"az"[..]));
|
||||
assert_eq!(space(e), Done(&b""[..], &b" "[..]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn character_s() {
|
||||
let empty = "";
|
||||
let a = "abcd";
|
||||
let b = "1234";
|
||||
let c = "a123";
|
||||
let d = "azé12";
|
||||
let e = " ";
|
||||
assert_eq!(alpha(a), Done(empty, a));
|
||||
assert_eq!(alpha(b), Error(Position(ErrorKind::Alpha,b)));
|
||||
assert_eq!(alpha(c), Done(&c[1..], &"a"[..]));
|
||||
assert_eq!(alpha(d), Done("12", &"azé"[..]));
|
||||
assert_eq!(digit(a), Error(Position(ErrorKind::Digit,a)));
|
||||
assert_eq!(digit(b), Done(empty, b));
|
||||
assert_eq!(digit(c), Error(Position(ErrorKind::Digit,c)));
|
||||
assert_eq!(digit(d), Error(Position(ErrorKind::Digit,d)));
|
||||
assert_eq!(hex_digit(a), Done(empty, a));
|
||||
assert_eq!(hex_digit(b), Done(empty, b));
|
||||
assert_eq!(hex_digit(c), Done(empty, c));
|
||||
assert_eq!(hex_digit(d), Done("zé12", &"a"[..]));
|
||||
assert_eq!(hex_digit(e), Error(Position(ErrorKind::HexDigit,e)));
|
||||
assert_eq!(oct_digit(a), Error(Position(ErrorKind::OctDigit,a)));
|
||||
assert_eq!(oct_digit(b), Done(empty, b));
|
||||
assert_eq!(oct_digit(c), Error(Position(ErrorKind::OctDigit,c)));
|
||||
assert_eq!(oct_digit(d), Error(Position(ErrorKind::OctDigit,d)));
|
||||
assert_eq!(alphanumeric(a), Done(empty, a));
|
||||
assert_eq!(fix_error!(b,(), alphanumeric), Done(empty, b));
|
||||
assert_eq!(alphanumeric(c), Done(empty, c));
|
||||
assert_eq!(alphanumeric(d), Done("", &"azé12"[..]));
|
||||
assert_eq!(space(e), Done(&""[..], &" "[..]));
|
||||
}
|
||||
|
||||
use util::HexDisplay;
|
||||
#[test]
|
||||
fn offset() {
|
||||
let a = &b"abcd"[..];
|
||||
let b = &b"1234"[..];
|
||||
let c = &b"a123"[..];
|
||||
let d = &b" \t"[..];
|
||||
let e = &b" \t\r\n"[..];
|
||||
let f = &b"123abcDEF"[..];
|
||||
|
||||
match alpha(a) {
|
||||
Done(i, _) => { assert_eq!(a.offset(i) + i.len(), a.len()); }
|
||||
_ => { panic!("wrong return type in offset test for alpha") }
|
||||
}
|
||||
match digit(b) {
|
||||
Done(i, _) => { assert_eq!(b.offset(i) + i.len(), b.len()); }
|
||||
_ => { panic!("wrong return type in offset test for digit") }
|
||||
}
|
||||
match alphanumeric(c) {
|
||||
Done(i, _) => { assert_eq!(c.offset(i) + i.len(), c.len()); }
|
||||
_ => { panic!("wrong return type in offset test for alphanumeric") }
|
||||
}
|
||||
match space(d) {
|
||||
Done(i, _) => { assert_eq!(d.offset(i) + i.len(), d.len()); }
|
||||
_ => { panic!("wrong return type in offset test for space") }
|
||||
}
|
||||
match multispace(e) {
|
||||
Done(i, _) => { assert_eq!(e.offset(i) + i.len(), e.len()); }
|
||||
_ => { panic!("wrong return type in offset test for multispace") }
|
||||
}
|
||||
match hex_digit(f) {
|
||||
Done(i, _) => { assert_eq!(f.offset(i) + i.len(), f.len()); }
|
||||
_ => { panic!("wrong return type in offset test for hex_digit") }
|
||||
}
|
||||
match oct_digit(f) {
|
||||
Done(i, _) => { assert_eq!(f.offset(i) + i.len(), f.len()); }
|
||||
_ => { panic!("wrong return type in offset test for oct_digit") }
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_not() {
|
||||
let a: &[u8] = b"ab12cd\nefgh";
|
||||
assert_eq!(not_line_ending(a), Done(&b"\nefgh"[..], &b"ab12cd"[..]));
|
||||
|
||||
let b: &[u8] = b"ab12cd\nefgh\nijkl";
|
||||
assert_eq!(not_line_ending(b), Done(&b"\nefgh\nijkl"[..], &b"ab12cd"[..]));
|
||||
|
||||
let c: &[u8] = b"ab12cd";
|
||||
assert_eq!(not_line_ending(c), Done(&b""[..], c));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn buffer_with_size() {
|
||||
let i:Vec<u8> = vec![7,8];
|
||||
let o:Vec<u8> = vec![4,5,6];
|
||||
//let arr:[u8; 6usize] = [3, 4, 5, 6, 7, 8];
|
||||
let arr:[u8; 6usize] = [3, 4, 5, 6, 7, 8];
|
||||
let res = sized_buffer(&arr[..]);
|
||||
assert_eq!(res, Done(&i[..], &o[..]))
|
||||
}
|
||||
|
||||
/*#[test]
|
||||
fn t1() {
|
||||
let v1:Vec<u8> = vec![1,2,3];
|
||||
let v2:Vec<u8> = vec![4,5,6];
|
||||
let d = Done(&v1[..], &v2[..]);
|
||||
let res = d.flat_map(print);
|
||||
assert_eq!(res, Done(&v2[..], ()));
|
||||
}*/
|
||||
|
||||
#[test]
|
||||
fn length_value_test() {
|
||||
let i1 = vec![7,8];
|
||||
let o1 = vec![4, 5, 6];
|
||||
let arr1:[u8; 6usize] = [3, 4, 5, 6, 7, 8];
|
||||
let res1 = length_value(&arr1);
|
||||
assert_eq!(Done(&i1[..], &o1[..]), res1);
|
||||
|
||||
let i2:Vec<u8> = vec![4,5,6,7,8];
|
||||
let o2: &[u8] = b"";
|
||||
let arr2:[u8; 6usize] = [0, 4, 5, 6, 7, 8];
|
||||
let res2 = length_value(&arr2);
|
||||
assert_eq!(Done(&i2[..], o2), res2);
|
||||
|
||||
let arr3:[u8; 7usize] = [8, 4, 5, 6, 7, 8, 9];
|
||||
let res3 = length_value(&arr3);
|
||||
//FIXME: should be incomplete
|
||||
assert_eq!(Incomplete(Needed::Size(9)), res3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i8_tests() {
|
||||
assert_eq!(be_i8(&[0x00]), Done(&b""[..], 0));
|
||||
assert_eq!(be_i8(&[0x7f]), Done(&b""[..], 127));
|
||||
assert_eq!(be_i8(&[0xff]), Done(&b""[..], -1));
|
||||
assert_eq!(be_i8(&[0x80]), Done(&b""[..], -128));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i16_tests() {
|
||||
assert_eq!(be_i16(&[0x00, 0x00]), Done(&b""[..], 0));
|
||||
assert_eq!(be_i16(&[0x7f, 0xff]), Done(&b""[..], 32767_i16));
|
||||
assert_eq!(be_i16(&[0xff, 0xff]), Done(&b""[..], -1));
|
||||
assert_eq!(be_i16(&[0x80, 0x00]), Done(&b""[..], -32768_i16));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i32_tests() {
|
||||
assert_eq!(be_i32(&[0x00, 0x00, 0x00, 0x00]), Done(&b""[..], 0));
|
||||
assert_eq!(be_i32(&[0x7f, 0xff, 0xff, 0xff]), Done(&b""[..], 2147483647_i32));
|
||||
assert_eq!(be_i32(&[0xff, 0xff, 0xff, 0xff]), Done(&b""[..], -1));
|
||||
assert_eq!(be_i32(&[0x80, 0x00, 0x00, 0x00]), Done(&b""[..], -2147483648_i32));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i64_tests() {
|
||||
assert_eq!(be_i64(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), Done(&b""[..], 0));
|
||||
assert_eq!(be_i64(&[0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), Done(&b""[..], 9223372036854775807_i64));
|
||||
assert_eq!(be_i64(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), Done(&b""[..], -1));
|
||||
assert_eq!(be_i64(&[0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), Done(&b""[..], -9223372036854775808_i64));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn le_i8_tests() {
|
||||
assert_eq!(le_i8(&[0x00]), Done(&b""[..], 0));
|
||||
assert_eq!(le_i8(&[0x7f]), Done(&b""[..], 127));
|
||||
assert_eq!(le_i8(&[0xff]), Done(&b""[..], -1));
|
||||
assert_eq!(le_i8(&[0x80]), Done(&b""[..], -128));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn le_i16_tests() {
|
||||
assert_eq!(le_i16(&[0x00, 0x00]), Done(&b""[..], 0));
|
||||
assert_eq!(le_i16(&[0xff, 0x7f]), Done(&b""[..], 32767_i16));
|
||||
assert_eq!(le_i16(&[0xff, 0xff]), Done(&b""[..], -1));
|
||||
assert_eq!(le_i16(&[0x00, 0x80]), Done(&b""[..], -32768_i16));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn le_i32_tests() {
|
||||
assert_eq!(le_i32(&[0x00, 0x00, 0x00, 0x00]), Done(&b""[..], 0));
|
||||
assert_eq!(le_i32(&[0xff, 0xff, 0xff, 0x7f]), Done(&b""[..], 2147483647_i32));
|
||||
assert_eq!(le_i32(&[0xff, 0xff, 0xff, 0xff]), Done(&b""[..], -1));
|
||||
assert_eq!(le_i32(&[0x00, 0x00, 0x00, 0x80]), Done(&b""[..], -2147483648_i32));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn le_i64_tests() {
|
||||
assert_eq!(le_i64(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), Done(&b""[..], 0));
|
||||
assert_eq!(le_i64(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]), Done(&b""[..], 9223372036854775807_i64));
|
||||
assert_eq!(le_i64(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), Done(&b""[..], -1));
|
||||
assert_eq!(le_i64(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]), Done(&b""[..], -9223372036854775808_i64));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn be_f32_tests() {
|
||||
assert_eq!(be_f32(&[0x00, 0x00, 0x00, 0x00]), Done(&b""[..], 0_f32));
|
||||
assert_eq!(be_f32(&[0x4d, 0x31, 0x1f, 0xd8]), Done(&b""[..], 185728392_f32));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn be_f64_tests() {
|
||||
assert_eq!(be_f64(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), Done(&b""[..], 0_f64));
|
||||
assert_eq!(be_f64(&[0x41, 0xa6, 0x23, 0xfb, 0x10, 0x00, 0x00, 0x00]), Done(&b""[..], 185728392_f64));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn le_f32_tests() {
|
||||
assert_eq!(le_f32(&[0x00, 0x00, 0x00, 0x00]), Done(&b""[..], 0_f32));
|
||||
assert_eq!(le_f32(&[0xd8, 0x1f, 0x31, 0x4d]), Done(&b""[..], 185728392_f32));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn le_f64_tests() {
|
||||
assert_eq!(le_f64(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), Done(&b""[..], 0_f64));
|
||||
assert_eq!(le_f64(&[0x00, 0x00, 0x00, 0x10, 0xfb, 0x23, 0xa6, 0x41]), Done(&b""[..], 185728392_f64));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hex_u32_tests() {
|
||||
assert_eq!(hex_u32(&b""[..]), Done(&b""[..], 0));
|
||||
assert_eq!(hex_u32(&b"ff"[..]), Done(&b""[..], 255));
|
||||
assert_eq!(hex_u32(&b"1be2"[..]), Done(&b""[..], 7138));
|
||||
assert_eq!(hex_u32(&b"c5a31be2"[..]), Done(&b""[..], 3315801058));
|
||||
assert_eq!(hex_u32(&b"00c5a31be2"[..]), Done(&b"e2"[..], 12952347));
|
||||
assert_eq!(hex_u32(&b"c5a31be201"[..]), Done(&b"01"[..], 3315801058));
|
||||
assert_eq!(hex_u32(&b"ffffffff"[..]), Done(&b""[..], 4294967295));
|
||||
assert_eq!(hex_u32(&b"0x1be2"[..]), Done(&b"x1be2"[..], 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn end_of_input() {
|
||||
let not_over = &b"Hello, world!"[..];
|
||||
let is_over = &b""[..];
|
||||
|
||||
let res_not_over = eof(not_over);
|
||||
assert_eq!(res_not_over, Error(Position(ErrorKind::Eof, not_over)));
|
||||
|
||||
let res_over = eof(is_over);
|
||||
assert_eq!(res_over, Done(is_over, is_over));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn configurable_endianness() {
|
||||
named!(be_tst16<u16>, u16!(true));
|
||||
named!(le_tst16<u16>, u16!(false));
|
||||
assert_eq!(be_tst16(&[0x80, 0x00]), Done(&b""[..], 32768_u16));
|
||||
assert_eq!(le_tst16(&[0x80, 0x00]), Done(&b""[..], 128_u16));
|
||||
|
||||
named!(be_tst32<u32>, u32!(true));
|
||||
named!(le_tst32<u32>, u32!(false));
|
||||
assert_eq!(be_tst32(&[0x12, 0x00, 0x60, 0x00]), Done(&b""[..], 302014464_u32));
|
||||
assert_eq!(le_tst32(&[0x12, 0x00, 0x60, 0x00]), Done(&b""[..], 6291474_u32));
|
||||
|
||||
named!(be_tst64<u64>, u64!(true));
|
||||
named!(le_tst64<u64>, u64!(false));
|
||||
assert_eq!(be_tst64(&[0x12, 0x00, 0x60, 0x00, 0x12, 0x00, 0x80, 0x00]), Done(&b""[..], 1297142246100992000_u64));
|
||||
assert_eq!(le_tst64(&[0x12, 0x00, 0x60, 0x00, 0x12, 0x00, 0x80, 0x00]), Done(&b""[..], 36028874334666770_u64));
|
||||
|
||||
named!(be_tsti16<i16>, i16!(true));
|
||||
named!(le_tsti16<i16>, i16!(false));
|
||||
assert_eq!(be_tsti16(&[0x00, 0x80]), Done(&b""[..], 128_i16));
|
||||
assert_eq!(le_tsti16(&[0x00, 0x80]), Done(&b""[..], -32768_i16));
|
||||
|
||||
named!(be_tsti32<i32>, i32!(true));
|
||||
named!(le_tsti32<i32>, i32!(false));
|
||||
assert_eq!(be_tsti32(&[0x00, 0x12, 0x60, 0x00]), Done(&b""[..], 1204224_i32));
|
||||
assert_eq!(le_tsti32(&[0x00, 0x12, 0x60, 0x00]), Done(&b""[..], 6296064_i32));
|
||||
|
||||
named!(be_tsti64<i64>, i64!(true));
|
||||
named!(le_tsti64<i64>, i64!(false));
|
||||
assert_eq!(be_tsti64(&[0x00, 0xFF, 0x60, 0x00, 0x12, 0x00, 0x80, 0x00]), Done(&b""[..], 71881672479506432_i64));
|
||||
assert_eq!(le_tsti64(&[0x00, 0xFF, 0x60, 0x00, 0x12, 0x00, 0x80, 0x00]), Done(&b""[..], 36028874334732032_i64));
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manual_configurable_endianness_test() {
|
||||
let x = 1;
|
||||
let int_parse: Box<Fn(&[u8]) -> IResult<&[u8], u16> > = if x == 2 {
|
||||
Box::new(be_u16)
|
||||
} else {
|
||||
Box::new(le_u16)
|
||||
};
|
||||
println!("{:?}", int_parse(&b"3"[..]));
|
||||
assert_eq!(int_parse(&[0x80, 0x00]), Done(&b""[..], 128_u16));
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn custom_error(input: &[u8]) -> IResult<&[u8], &[u8], ()> {
|
||||
fix_error!(input, (), alphanumeric)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hex_digit_test() {
|
||||
let empty = &b""[..];
|
||||
|
||||
let i = &b"0123456789abcdefABCDEF"[..];
|
||||
assert_eq!(hex_digit(i), Done(empty, i));
|
||||
|
||||
let i = &b"g"[..];
|
||||
assert_eq!(hex_digit(i), Error(Position(ErrorKind::HexDigit,i)));
|
||||
|
||||
let i = &b"G"[..];
|
||||
assert_eq!(hex_digit(i), Error(Position(ErrorKind::HexDigit,i)));
|
||||
|
||||
assert!(is_hex_digit(b'0'));
|
||||
assert!(is_hex_digit(b'9'));
|
||||
assert!(is_hex_digit(b'a'));
|
||||
assert!(is_hex_digit(b'f'));
|
||||
assert!(is_hex_digit(b'A'));
|
||||
assert!(is_hex_digit(b'F'));
|
||||
assert!(!is_hex_digit(b'g'));
|
||||
assert!(!is_hex_digit(b'G'));
|
||||
assert!(!is_hex_digit(b'/'));
|
||||
assert!(!is_hex_digit(b':'));
|
||||
assert!(!is_hex_digit(b'@'));
|
||||
assert!(!is_hex_digit(b'\x60'));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn oct_digit_test() {
|
||||
let empty = &b""[..];
|
||||
|
||||
let i = &b"01234567"[..];
|
||||
assert_eq!(oct_digit(i), Done(empty, i));
|
||||
|
||||
let i = &b"8"[..];
|
||||
assert_eq!(oct_digit(i), Error(Position(ErrorKind::OctDigit,i)));
|
||||
|
||||
assert!(is_oct_digit(b'0'));
|
||||
assert!(is_oct_digit(b'7'));
|
||||
assert!(!is_oct_digit(b'8'));
|
||||
assert!(!is_oct_digit(b'9'));
|
||||
assert!(!is_oct_digit(b'a'));
|
||||
assert!(!is_oct_digit(b'A'));
|
||||
assert!(!is_oct_digit(b'/'));
|
||||
assert!(!is_oct_digit(b':'));
|
||||
assert!(!is_oct_digit(b'@'));
|
||||
assert!(!is_oct_digit(b'\x60'));
|
||||
}
|
||||
}
|
|
@ -1,644 +0,0 @@
|
|||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! regex (
|
||||
($re: ident, $s:expr) => (
|
||||
lazy_static! {
|
||||
static ref $re: ::regex::Regex = ::regex::Regex::new($s).unwrap();
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! regex_bytes (
|
||||
($re: ident, $s:expr) => (
|
||||
lazy_static! {
|
||||
static ref $re: ::regex::bytes::Regex = ::regex::bytes::Regex::new($s).unwrap();
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
|
||||
/// `re_match!(regexp) => &[T] -> IResult<&[T], &[T]>`
|
||||
/// Returns the whole input if a match is found
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_match (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
use $crate::InputLength;
|
||||
let re = ::regex::Regex::new($re).unwrap();
|
||||
if re.is_match($i) {
|
||||
$crate::IResult::Done(&$i[$i.input_len()..], $i)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpMatch))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_match_static!(regexp) => &[T] -> IResult<&[T], &[T]>`
|
||||
/// Returns the whole input if a match is found. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_match_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
use $crate::InputLength;
|
||||
regex!(RE, $re);
|
||||
if RE.is_match($i) {
|
||||
$crate::IResult::Done(&$i[$i.input_len()..], $i)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpMatch))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
/// `re_bytes_match!(regexp) => &[T] -> IResult<&[T], &[T]>`
|
||||
/// Returns the whole input if a match is found
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_match (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
use $crate::InputLength;
|
||||
let re = ::regex::bytes::Regex::new($re).unwrap();
|
||||
if re.is_match($i) {
|
||||
$crate::IResult::Done(&$i[$i.input_len()..], $i)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpMatch))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_bytes_match_static!(regexp) => &[T] -> IResult<&[T], &[T]>`
|
||||
/// Returns the whole input if a match is found. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_match_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
use $crate::InputLength;
|
||||
regex_bytes!(RE, $re);
|
||||
if RE.is_match($i) {
|
||||
$crate::IResult::Done(&$i[$i.input_len()..], $i)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpMatch))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
/// `re_find!(regexp) => &[T] -> IResult<&[T], &[T]>`
|
||||
/// Returns the first match
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_find (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
let re = ::regex::Regex::new($re).unwrap();
|
||||
if let Some((begin, end)) = re.find($i) {
|
||||
$crate::IResult::Done(&$i[end..], &$i[begin..end])
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpFind))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_find_static!(regexp) => &[T] -> IResult<&[T], &[T]>`
|
||||
/// Returns the first match. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_find_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
regex!(RE, $re);
|
||||
if let Some((begin, end)) = RE.find($i) {
|
||||
$crate::IResult::Done(&$i[end..], &$i[begin..end])
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpFind))
|
||||
}
|
||||
}
|
||||
|
||||
)
|
||||
);
|
||||
|
||||
/// `re_bytes_find!(regexp) => &[T] -> IResult<&[T], &[T]>`
|
||||
/// Returns the first match
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_find (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
let re = ::regex::bytes::Regex::new($re).unwrap();
|
||||
if let Some((begin, end)) = re.find($i) {
|
||||
$crate::IResult::Done(&$i[end..], &$i[begin..end])
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpFind))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_bytes_find!(regexp) => &[T] -> IResult<&[T], &[T]>`
|
||||
/// Returns the first match. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_find_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
regex_bytes!(RE, $re);
|
||||
if let Some((begin, end)) = RE.find($i) {
|
||||
$crate::IResult::Done(&$i[end..], &$i[begin..end])
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpFind))
|
||||
}
|
||||
}
|
||||
|
||||
)
|
||||
);
|
||||
|
||||
/// `re_matches!(regexp) => &[T] -> IResult<&[T], Vec<&[T]>>`
|
||||
/// Returns all the matched parts
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_matches (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
let re = ::regex::Regex::new($re).unwrap();
|
||||
let v: Vec<&str> = re.find_iter($i).map(|(begin,end)| &$i[begin..end]).collect();
|
||||
if v.len() != 0 {
|
||||
let offset = {
|
||||
let end = v.last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done(&$i[offset..], v)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpMatches))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_matches_static!(regexp) => &[T] -> IResult<&[T], Vec<&[T]>>`
|
||||
/// Returns all the matched parts. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_matches_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
regex!(RE, $re);
|
||||
let v: Vec<&str> = RE.find_iter($i).map(|(begin,end)| &$i[begin..end]).collect();
|
||||
if v.len() != 0 {
|
||||
let offset = {
|
||||
let end = v.last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done(&$i[offset..], v)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpMatches))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
/// `re_bytes_matches!(regexp) => &[T] -> IResult<&[T], Vec<&[T]>>`
|
||||
/// Returns all the matched parts
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_matches (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
let re = ::regex::bytes::Regex::new($re).unwrap();
|
||||
let v: Vec<&[u8]> = re.find_iter($i).map(|(begin,end)| &$i[begin..end]).collect();
|
||||
if v.len() != 0 {
|
||||
let offset = {
|
||||
let end = v.last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done(&$i[offset..], v)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpMatches))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_bytes_matches_static!(regexp) => &[T] -> IResult<&[T], Vec<&[T]>>`
|
||||
/// Returns all the matched parts. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_matches_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
regex_bytes!(RE, $re);
|
||||
let v: Vec<&[u8]> = RE.find_iter($i).map(|(begin,end)| &$i[begin..end]).collect();
|
||||
if v.len() != 0 {
|
||||
let offset = {
|
||||
let end = v.last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done(&$i[offset..], v)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpMatches))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
/// `re_capture!(regexp) => &[T] -> IResult<&[T], Vec<&[T]>>`
|
||||
/// Returns the first capture group
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_capture (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
let re = ::regex::Regex::new($re).unwrap();
|
||||
if let Some(c) = re.captures($i) {
|
||||
let v:Vec<&str> = c.iter_pos().filter(|el| el.is_some()).map(|el| el.unwrap()).map(|(begin,end)| &$i[begin..end]).collect();
|
||||
let offset = {
|
||||
let end = v.last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done(&$i[offset..], v)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpCapture))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_capture_static!(regexp) => &[T] -> IResult<&[T], Vec<&[T]>>`
|
||||
/// Returns the first capture group. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_capture_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
regex!(RE, $re);
|
||||
if let Some(c) = RE.captures($i) {
|
||||
let v:Vec<&str> = c.iter_pos().filter(|el| el.is_some()).map(|el| el.unwrap()).map(|(begin,end)| &$i[begin..end]).collect();
|
||||
let offset = {
|
||||
let end = v.last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done(&$i[offset..], v)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpCapture))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
/// `re_bytes_capture!(regexp) => &[T] -> IResult<&[T], Vec<&[T]>>`
|
||||
/// Returns the first capture group
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_capture (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
let re = ::regex::bytes::Regex::new($re).unwrap();
|
||||
if let Some(c) = re.captures($i) {
|
||||
let v:Vec<&[u8]> = c.iter_pos().filter(|el| el.is_some()).map(|el| el.unwrap()).map(|(begin,end)| &$i[begin..end]).collect();
|
||||
let offset = {
|
||||
let end = v.last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done(&$i[offset..], v)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpCapture))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_bytes_capture_static!(regexp) => &[T] -> IResult<&[T], Vec<&[T]>>`
|
||||
/// Returns the first capture group. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_capture_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
regex_bytes!(RE, $re);
|
||||
if let Some(c) = RE.captures($i) {
|
||||
let v:Vec<&[u8]> = c.iter_pos().filter(|el| el.is_some()).map(|el| el.unwrap()).map(|(begin,end)| &$i[begin..end]).collect();
|
||||
let offset = {
|
||||
let end = v.last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done(&$i[offset..], v)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpCapture))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
/// `re_captures!(regexp) => &[T] -> IResult<&[T], Vec<Vec<&[T]>>>`
|
||||
/// Returns all the capture groups
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_captures (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
let re = ::regex::Regex::new($re).unwrap();
|
||||
let v:Vec<Vec<&str>> = re.captures_iter($i).map(|c| c.iter_pos().filter(|el| el.is_some()).map(|el| el.unwrap()).map(|(begin,end)| &$i[begin..end]).collect()).collect();
|
||||
if v.len() != 0 {
|
||||
let offset = {
|
||||
let end = v.last().unwrap().last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done(&$i[offset..], v)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpCapture))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_captures_static!(regexp) => &[T] -> IResult<&[T], Vec<Vec<&[T]>>>`
|
||||
/// Returns all the capture groups. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_captures_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
regex!(RE, $re);
|
||||
let v:Vec<Vec<&str>> = RE.captures_iter($i).map(|c| c.iter_pos().filter(|el| el.is_some()).map(|el| el.unwrap()).map(|(begin,end)| &$i[begin..end]).collect()).collect();
|
||||
if v.len() != 0 {
|
||||
let offset = {
|
||||
let end = v.last().unwrap().last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done(&$i[offset..], v)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpCapture))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
/// `re_bytes_captures!(regexp) => &[T] -> IResult<&[T], Vec<Vec<&[T]>>>`
|
||||
/// Returns all the capture groups
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_captures (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
let re = ::regex::bytes::Regex::new($re).unwrap();
|
||||
let v:Vec<Vec<&[u8]>> = re.captures_iter($i).map(|c| c.iter_pos().filter(|el| el.is_some()).map(|el| el.unwrap()).map(|(begin,end)| &$i[begin..end]).collect()).collect();
|
||||
if v.len() != 0 {
|
||||
let offset = {
|
||||
let end = v.last().unwrap().last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done(&$i[offset..], v)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpCapture))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_bytes_captures_static!(regexp) => &[T] -> IResult<&[T], Vec<Vec<&[T]>>>`
|
||||
/// Returns all the capture groups. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_captures_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
regex_bytes!(RE, $re);
|
||||
let v:Vec<Vec<&[u8]>> = RE.captures_iter($i).map(|c| c.iter_pos().filter(|el| el.is_some()).map(|el| el.unwrap()).map(|(begin,end)| &$i[begin..end]).collect()).collect();
|
||||
if v.len() != 0 {
|
||||
let offset = {
|
||||
let end = v.last().unwrap().last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done(&$i[offset..], v)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpCapture))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use internal::IResult::*;
|
||||
use internal::Err::*;
|
||||
use util::ErrorKind;
|
||||
|
||||
#[test]
|
||||
fn re_match() {
|
||||
named!(rm<&str,&str>, re_match!(r"^\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm("2015-09-07"), Done("", "2015-09-07"));
|
||||
assert_eq!(rm("blah"), Error(Code(ErrorKind::RegexpMatch)));
|
||||
assert_eq!(rm("2015-09-07blah"), Done("", "2015-09-07blah"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_match_static() {
|
||||
named!(rm<&str,&str>, re_match_static!(r"^\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm("2015-09-07"), Done("", "2015-09-07"));
|
||||
assert_eq!(rm("blah"), Error(Code(ErrorKind::RegexpMatch)));
|
||||
assert_eq!(rm("2015-09-07blah"), Done("", "2015-09-07blah"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn re_find() {
|
||||
named!(rm<&str,&str>, re_find!(r"^\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm("2015-09-07"), Done("", "2015-09-07"));
|
||||
assert_eq!(rm("blah"), Error(Code(ErrorKind::RegexpFind)));
|
||||
assert_eq!(rm("2015-09-07blah"), Done("blah", "2015-09-07"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_find_static() {
|
||||
named!(rm<&str,&str>, re_find_static!(r"^\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm("2015-09-07"), Done("", "2015-09-07"));
|
||||
assert_eq!(rm("blah"), Error(Code(ErrorKind::RegexpFind)));
|
||||
assert_eq!(rm("2015-09-07blah"), Done("blah", "2015-09-07"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn re_matches() {
|
||||
named!(rm< &str,Vec<&str> >, re_matches!(r"\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm("2015-09-07"), Done("", vec!["2015-09-07"]));
|
||||
assert_eq!(rm("blah"), Error(Code(ErrorKind::RegexpMatches)));
|
||||
assert_eq!(rm("aaa2015-09-07blah2015-09-09pouet"), Done("pouet", vec!["2015-09-07", "2015-09-09"]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_matches_static() {
|
||||
named!(rm< &str,Vec<&str> >, re_matches_static!(r"\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm("2015-09-07"), Done("", vec!["2015-09-07"]));
|
||||
assert_eq!(rm("blah"), Error(Code(ErrorKind::RegexpMatches)));
|
||||
assert_eq!(rm("aaa2015-09-07blah2015-09-09pouet"), Done("pouet", vec!["2015-09-07", "2015-09-09"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn re_capture() {
|
||||
named!(rm< &str,Vec<&str> >, re_capture!(r"([:alpha:]+)\s+((\d+).(\d+).(\d+))"));
|
||||
assert_eq!(rm("blah nom 0.3.11pouet"), Done("pouet", vec!["nom 0.3.11", "nom", "0.3.11", "0", "3", "11"]));
|
||||
assert_eq!(rm("blah"), Error(Code(ErrorKind::RegexpCapture)));
|
||||
assert_eq!(rm("hello nom 0.3.11 world regex 0.1.41"), Done(" world regex 0.1.41", vec!["nom 0.3.11", "nom", "0.3.11", "0", "3", "11"]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_capture_static() {
|
||||
named!(rm< &str,Vec<&str> >, re_capture_static!(r"([:alpha:]+)\s+((\d+).(\d+).(\d+))"));
|
||||
assert_eq!(rm("blah nom 0.3.11pouet"), Done("pouet", vec!["nom 0.3.11", "nom", "0.3.11", "0", "3", "11"]));
|
||||
assert_eq!(rm("blah"), Error(Code(ErrorKind::RegexpCapture)));
|
||||
assert_eq!(rm("hello nom 0.3.11 world regex 0.1.41"), Done(" world regex 0.1.41", vec!["nom 0.3.11", "nom", "0.3.11", "0", "3", "11"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn re_captures() {
|
||||
named!(rm< &str,Vec<Vec<&str>> >, re_captures!(r"([:alpha:]+)\s+((\d+).(\d+).(\d+))"));
|
||||
assert_eq!(rm("blah nom 0.3.11pouet"), Done("pouet", vec![vec!["nom 0.3.11", "nom", "0.3.11", "0", "3", "11"]]));
|
||||
assert_eq!(rm("blah"), Error(Code(ErrorKind::RegexpCapture)));
|
||||
assert_eq!(rm("hello nom 0.3.11 world regex 0.1.41 aaa"), Done(" aaa", vec![
|
||||
vec!["nom 0.3.11", "nom", "0.3.11", "0", "3", "11"],
|
||||
vec!["regex 0.1.41", "regex", "0.1.41", "0", "1", "41"],
|
||||
]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_captures_static() {
|
||||
named!(rm< &str,Vec<Vec<&str>> >, re_captures_static!(r"([:alpha:]+)\s+((\d+).(\d+).(\d+))"));
|
||||
assert_eq!(rm("blah nom 0.3.11pouet"), Done("pouet", vec![vec!["nom 0.3.11", "nom", "0.3.11", "0", "3", "11"]]));
|
||||
assert_eq!(rm("blah"), Error(Code(ErrorKind::RegexpCapture)));
|
||||
assert_eq!(rm("hello nom 0.3.11 world regex 0.1.41 aaa"), Done(" aaa", vec![
|
||||
vec!["nom 0.3.11", "nom", "0.3.11", "0", "3", "11"],
|
||||
vec!["regex 0.1.41", "regex", "0.1.41", "0", "1", "41"],
|
||||
]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn re_bytes_match() {
|
||||
named!(rm, re_bytes_match!(r"^\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm(&b"2015-09-07"[..]), Done(&b""[..], &b"2015-09-07"[..]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(Code(ErrorKind::RegexpMatch)));
|
||||
assert_eq!(rm(&b"2015-09-07blah"[..]), Done(&b""[..], &b"2015-09-07blah"[..]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_bytes_match_static() {
|
||||
named!(rm, re_bytes_match_static!(r"^\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm(&b"2015-09-07"[..]), Done(&b""[..], &b"2015-09-07"[..]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(Code(ErrorKind::RegexpMatch)));
|
||||
assert_eq!(rm(&b"2015-09-07blah"[..]), Done(&b""[..], &b"2015-09-07blah"[..]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn re_bytes_find() {
|
||||
named!(rm, re_bytes_find!(r"^\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm(&b"2015-09-07"[..]), Done(&b""[..], &b"2015-09-07"[..]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(Code(ErrorKind::RegexpFind)));
|
||||
assert_eq!(rm(&b"2015-09-07blah"[..]), Done(&b"blah"[..], &b"2015-09-07"[..]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_bytes_find_static() {
|
||||
named!(rm, re_bytes_find_static!(r"^\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm(&b"2015-09-07"[..]), Done(&b""[..], &b"2015-09-07"[..]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(Code(ErrorKind::RegexpFind)));
|
||||
assert_eq!(rm(&b"2015-09-07blah"[..]), Done(&b"blah"[..], &b"2015-09-07"[..]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn re_bytes_matches() {
|
||||
named!(rm<Vec<&[u8]> >, re_bytes_matches!(r"\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm(&b"2015-09-07"[..]), Done(&b""[..], vec![&b"2015-09-07"[..]]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(Code(ErrorKind::RegexpMatches)));
|
||||
assert_eq!(rm(&b"aaa2015-09-07blah2015-09-09pouet"[..]), Done(&b"pouet"[..], vec![&b"2015-09-07"[..], &b"2015-09-09"[..]]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_bytes_matches_static() {
|
||||
named!(rm<Vec<&[u8]> >, re_bytes_matches_static!(r"\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm(&b"2015-09-07"[..]), Done(&b""[..], vec![&b"2015-09-07"[..]]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(Code(ErrorKind::RegexpMatches)));
|
||||
assert_eq!(rm(&b"aaa2015-09-07blah2015-09-09pouet"[..]), Done(&b"pouet"[..], vec![&b"2015-09-07"[..], &b"2015-09-09"[..]]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn re_bytes_capture() {
|
||||
named!(rm<Vec<&[u8]> >, re_bytes_capture!(r"([:alpha:]+)\s+((\d+).(\d+).(\d+))"));
|
||||
assert_eq!(rm(&b"blah nom 0.3.11pouet"[..]), Done(&b"pouet"[..], vec![&b"nom 0.3.11"[..], &b"nom"[..], &b"0.3.11"[..], &b"0"[..], &b"3"[..], &b"11"[..]]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(Code(ErrorKind::RegexpCapture)));
|
||||
assert_eq!(rm(&b"hello nom 0.3.11 world regex 0.1.41"[..]), Done(&b" world regex 0.1.41"[..], vec![&b"nom 0.3.11"[..], &b"nom"[..], &b"0.3.11"[..], &b"0"[..], &b"3"[..], &b"11"[..]]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_bytes_capture_static() {
|
||||
named!(rm< Vec<&[u8]> >, re_bytes_capture_static!(r"([:alpha:]+)\s+((\d+).(\d+).(\d+))"));
|
||||
assert_eq!(rm(&b"blah nom 0.3.11pouet"[..]), Done(&b"pouet"[..], vec![&b"nom 0.3.11"[..], &b"nom"[..], &b"0.3.11"[..], &b"0"[..], &b"3"[..], &b"11"[..]]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(Code(ErrorKind::RegexpCapture)));
|
||||
assert_eq!(rm(&b"hello nom 0.3.11 world regex 0.1.41"[..]), Done(&b" world regex 0.1.41"[..], vec![&b"nom 0.3.11"[..], &b"nom"[..], &b"0.3.11"[..], &b"0"[..], &b"3"[..], &b"11"[..]]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn re_bytes_captures() {
|
||||
named!(rm< Vec<Vec<&[u8]>> >, re_bytes_captures!(r"([:alpha:]+)\s+((\d+).(\d+).(\d+))"));
|
||||
assert_eq!(rm(&b"blah nom 0.3.11pouet"[..]), Done(&b"pouet"[..], vec![vec![&b"nom 0.3.11"[..], &b"nom"[..], &b"0.3.11"[..], &b"0"[..], &b"3"[..], &b"11"[..]]]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(Code(ErrorKind::RegexpCapture)));
|
||||
assert_eq!(rm(&b"hello nom 0.3.11 world regex 0.1.41 aaa"[..]), Done(&b" aaa"[..], vec![
|
||||
vec![&b"nom 0.3.11"[..], &b"nom"[..], &b"0.3.11"[..], &b"0"[..], &b"3"[..], &b"11"[..]],
|
||||
vec![&b"regex 0.1.41"[..], &b"regex"[..], &b"0.1.41"[..], &b"0"[..], &b"1"[..], &b"41"[..]],
|
||||
]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_bytes_captures_static() {
|
||||
named!(rm< Vec<Vec<&[u8]>> >, re_bytes_captures_static!(r"([:alpha:]+)\s+((\d+).(\d+).(\d+))"));
|
||||
assert_eq!(rm(&b"blah nom 0.3.11pouet"[..]), Done(&b"pouet"[..], vec![vec![&b"nom 0.3.11"[..], &b"nom"[..], &b"0.3.11"[..], &b"0"[..], &b"3"[..], &b"11"[..]]]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(Code(ErrorKind::RegexpCapture)));
|
||||
assert_eq!(rm(&b"hello nom 0.3.11 world regex 0.1.41 aaa"[..]), Done(&b" aaa"[..], vec![
|
||||
vec![&b"nom 0.3.11"[..], &b"nom"[..], &b"0.3.11"[..], &b"0"[..], &b"3"[..], &b"11"[..]],
|
||||
vec![&b"regex 0.1.41"[..], &b"regex"[..], &b"0.1.41"[..], &b"0"[..], &b"1"[..], &b"41"[..]],
|
||||
]));
|
||||
}
|
||||
}
|
|
@ -1,734 +0,0 @@
|
|||
//! Parsers and helper functions operating on strings, especially useful when writing parsers for
|
||||
//! text-based formats.
|
||||
|
||||
/// `tag_s!(&str) => &str -> IResult<&str, &str>`
|
||||
/// declares a string as a suite to recognize
|
||||
///
|
||||
/// consumes the recognized characters
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::{self,Done};
|
||||
/// # fn main() {
|
||||
/// fn test(input: &str) -> IResult<&str, &str> {
|
||||
/// tag_s!(input, "abcd")
|
||||
/// }
|
||||
/// let r = test("abcdefgh");
|
||||
/// assert_eq!(r, Done("efgh", "abcd"));
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! tag_s (
|
||||
($i:expr, $tag: expr) => (
|
||||
{
|
||||
let res: $crate::IResult<_,_> = if $tag.len() > $i.len() {
|
||||
$crate::IResult::Incomplete($crate::Needed::Size($tag.len()))
|
||||
//} else if &$i[0..$tag.len()] == $tag {
|
||||
} else if ($i).starts_with($tag) {
|
||||
$crate::IResult::Done(&$i[$tag.len()..], &$i[0..$tag.len()])
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Position($crate::ErrorKind::TagStr, $i))
|
||||
};
|
||||
res
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// `take_s!(nb) => &str -> IResult<&str, &str>`
|
||||
/// generates a parser consuming the specified number of characters
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::Done;
|
||||
/// # fn main() {
|
||||
/// // Desmond parser
|
||||
/// named!(take5<&str,&str>, take_s!( 5 ) );
|
||||
///
|
||||
/// let a = "abcdefgh";
|
||||
///
|
||||
/// assert_eq!(take5(a), Done("fgh", "abcde"));
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! take_s (
|
||||
($i:expr, $count:expr) => (
|
||||
{
|
||||
let cnt = $count as usize;
|
||||
let res: $crate::IResult<_,_> = if $i.chars().count() < cnt {
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(cnt))
|
||||
} else {
|
||||
let mut offset = $i.len();
|
||||
let mut count = 0;
|
||||
for (o, _) in $i.char_indices() {
|
||||
if count == cnt {
|
||||
offset = o;
|
||||
break;
|
||||
}
|
||||
count += 1;
|
||||
}
|
||||
$crate::IResult::Done(&$i[offset..], &$i[..offset])
|
||||
};
|
||||
res
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
|
||||
/// `is_not_s!(&str) => &str -> IResult<&str, &str>`
|
||||
/// returns the longest list of characters that do not appear in the provided array
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::Done;
|
||||
/// # fn main() {
|
||||
/// named!( not_space<&str,&str>, is_not_s!( " \t\r\n" ) );
|
||||
///
|
||||
/// let r = not_space("abcdefgh\nijkl");
|
||||
/// assert_eq!(r, Done("\nijkl", "abcdefgh"));
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! is_not_s (
|
||||
($input:expr, $arr:expr) => (
|
||||
{
|
||||
use std::collections::HashSet;
|
||||
let set: HashSet<char> = $arr.chars().collect();
|
||||
let mut offset = $input.len();
|
||||
for (o, c) in $input.char_indices() {
|
||||
if set.contains(&c) {
|
||||
offset = o;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if offset == 0 {
|
||||
$crate::IResult::Error($crate::Err::Position($crate::ErrorKind::IsAStr,$input))
|
||||
} else if offset < $input.len() {
|
||||
$crate::IResult::Done(&$input[offset..], &$input[..offset])
|
||||
} else {
|
||||
$crate::IResult::Done("", $input)
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// `is_a_s!(&str) => &str -> IResult<&str, &str>`
|
||||
/// returns the longest list of characters that appear in the provided array
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::Done;
|
||||
/// # fn main() {
|
||||
/// named!(abcd<&str, &str>, is_a_s!( "abcd" ));
|
||||
///
|
||||
/// let r1 = abcd("aaaaefgh");
|
||||
/// assert_eq!(r1, Done("efgh", "aaaa"));
|
||||
///
|
||||
/// let r2 = abcd("dcbaefgh");
|
||||
/// assert_eq!(r2, Done("efgh", "dcba"));
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! is_a_s (
|
||||
($input:expr, $arr:expr) => (
|
||||
{
|
||||
use std::collections::HashSet;
|
||||
let set: HashSet<char> = $arr.chars().collect();
|
||||
let mut offset = $input.len();
|
||||
for (o, c) in $input.char_indices() {
|
||||
if !set.contains(&c) {
|
||||
offset = o;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if offset == 0 {
|
||||
$crate::IResult::Error($crate::Err::Position($crate::ErrorKind::IsAStr,$input))
|
||||
} else if offset < $input.len() {
|
||||
$crate::IResult::Done(&$input[offset..], &$input[..offset])
|
||||
} else {
|
||||
$crate::IResult::Done("", $input)
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
|
||||
/// `take_while_s!(char -> bool) => &str -> IResult<&str, &str>`
|
||||
/// returns the longest list of characters until the provided function fails.
|
||||
///
|
||||
/// The argument is either a function `char -> bool` or a macro returning a `bool
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::Done;
|
||||
/// # use nom::is_alphanumeric;
|
||||
/// # fn main() {
|
||||
/// fn alphabetic(chr: char) -> bool { (chr >= 0x41 as char && chr <= 0x5A as char) || (chr >= 0x61 as char && chr <= 0x7A as char) }
|
||||
/// named!( alpha<&str,&str>, take_while_s!( alphabetic ) );
|
||||
///
|
||||
/// let r = alpha("abcd\nefgh");
|
||||
/// assert_eq!(r, Done("\nefgh", "abcd"));
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! take_while_s (
|
||||
($input:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
{
|
||||
let mut offset = $input.len();
|
||||
for (o, c) in $input.char_indices() {
|
||||
if !$submac!(c, $($args)*) {
|
||||
offset = o;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if offset < $input.len() {
|
||||
$crate::IResult::Done(&$input[offset..], &$input[..offset])
|
||||
} else {
|
||||
$crate::IResult::Done("", $input)
|
||||
}
|
||||
}
|
||||
);
|
||||
($input:expr, $f:expr) => (
|
||||
take_while_s!($input, call!($f));
|
||||
);
|
||||
);
|
||||
|
||||
/// `take_while1_s!(char -> bool) => &str -> IResult<&str, &str>`
|
||||
/// returns the longest (non empty) list of characters until the provided function fails.
|
||||
///
|
||||
/// The argument is either a function `char -> bool` or a macro returning a `bool`
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::Done;
|
||||
/// # use nom::is_alphanumeric;
|
||||
/// # fn main() {
|
||||
/// fn alphabetic(chr: char) -> bool { (chr >= 0x41 as char && chr <= 0x5A as char) || (chr >= 0x61 as char && chr <= 0x7A as char) }
|
||||
/// named!( alpha<&str,&str>, take_while1_s!( alphabetic ) );
|
||||
///
|
||||
/// let r = alpha("abcd\nefgh");
|
||||
/// assert_eq!(r, Done("\nefgh", "abcd"));
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! take_while1_s (
|
||||
($input:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
{
|
||||
let mut offset = $input.len();
|
||||
for (o, c) in $input.char_indices() {
|
||||
if !$submac!(c, $($args)*) {
|
||||
offset = o;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if offset == 0 {
|
||||
$crate::IResult::Error($crate::Err::Position($crate::ErrorKind::TakeWhile1Str,$input))
|
||||
} else if offset < $input.len() {
|
||||
$crate::IResult::Done(&$input[offset..], &$input[..offset])
|
||||
} else {
|
||||
$crate::IResult::Done("", $input)
|
||||
}
|
||||
}
|
||||
);
|
||||
($input:expr, $f:expr) => (
|
||||
take_while1_s!($input, call!($f));
|
||||
);
|
||||
);
|
||||
|
||||
|
||||
/// `take_till_s!(&str -> bool) => &str -> IResult<&str, &str>`
|
||||
/// returns the longest list of characters until the provided function succeeds
|
||||
///
|
||||
/// The argument is either a function `char -> bool` or a macro returning a `bool
|
||||
#[macro_export]
|
||||
macro_rules! take_till_s (
|
||||
($input:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
|
||||
{
|
||||
let mut offset = $input.len();
|
||||
for (o, c) in $input.char_indices() {
|
||||
if $submac!(c, $($args)*) {
|
||||
offset = o;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if offset < $input.len() {
|
||||
$crate::IResult::Done(&$input[offset..], &$input[..offset])
|
||||
} else {
|
||||
$crate::IResult::Done("", $input)
|
||||
}
|
||||
}
|
||||
);
|
||||
($input:expr, $f:expr) => (
|
||||
take_till_s!($input, call!($f));
|
||||
);
|
||||
);
|
||||
|
||||
/// `take_until_and_consume_s!(&str) => &str -> IResult<&str, &str>`
|
||||
/// generates a parser consuming all chars until the specified string is found and consumes it
|
||||
#[macro_export]
|
||||
macro_rules! take_until_and_consume_s (
|
||||
($input:expr, $substr:expr) => (
|
||||
{
|
||||
#[inline(always)]
|
||||
fn shift_window_and_cmp(window: & mut ::std::vec::Vec<char>, c: char, substr_vec: & ::std::vec::Vec<char>) -> bool {
|
||||
window.push(c);
|
||||
if window.len() > substr_vec.len() {
|
||||
window.remove(0);
|
||||
}
|
||||
window == substr_vec
|
||||
}
|
||||
let res: $crate::IResult<_, _> = if $substr.len() > $input.len() {
|
||||
$crate::IResult::Incomplete($crate::Needed::Size($substr.len()))
|
||||
} else {
|
||||
let substr_vec: ::std::vec::Vec<char> = $substr.chars().collect();
|
||||
let mut window: ::std::vec::Vec<char> = vec![];
|
||||
let mut offset = $input.len();
|
||||
let mut parsed = false;
|
||||
for (o, c) in $input.char_indices() {
|
||||
if parsed {
|
||||
// The easiest way to get the byte offset of the char after the found string
|
||||
offset = o;
|
||||
break;
|
||||
}
|
||||
if shift_window_and_cmp(& mut window, c, &substr_vec) {
|
||||
parsed = true;
|
||||
}
|
||||
}
|
||||
if parsed {
|
||||
if offset < $input.len() {
|
||||
$crate::IResult::Done(&$input[offset..], &$input[..offset])
|
||||
} else {
|
||||
$crate::IResult::Done("", $input)
|
||||
}
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Position($crate::ErrorKind::TakeUntilAndConsumeStr,$input))
|
||||
}
|
||||
};
|
||||
res
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// `take_until_s!(&str) => &str -> IResult<&str, &str>`
|
||||
/// generates a parser consuming all chars until the specified string is found and leaves it in the remaining input
|
||||
#[macro_export]
|
||||
macro_rules! take_until_s (
|
||||
($input:expr, $substr:expr) => (
|
||||
{
|
||||
#[inline(always)]
|
||||
fn shift_window_and_cmp(window: & mut Vec<char>, c: char, substr_vec: &Vec<char>) -> bool {
|
||||
window.push(c);
|
||||
if window.len() > substr_vec.len() {
|
||||
window.remove(0);
|
||||
}
|
||||
window == substr_vec
|
||||
}
|
||||
let res: $crate::IResult<&str, &str> = if $substr.len() > $input.len() {
|
||||
$crate::IResult::Incomplete($crate::Needed::Size($substr.len()))
|
||||
} else {
|
||||
let substr_vec: Vec<char> = $substr.chars().collect();
|
||||
let mut window: Vec<char> = vec![];
|
||||
let mut offset = $input.len();
|
||||
let mut parsed = false;
|
||||
for (o, c) in $input.char_indices() {
|
||||
if shift_window_and_cmp(& mut window, c, &substr_vec) {
|
||||
parsed = true;
|
||||
window.pop();
|
||||
let window_len: usize = window.iter()
|
||||
.map(|x| x.len_utf8())
|
||||
.fold(0, |x, y| x + y);
|
||||
offset = o - window_len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if parsed {
|
||||
$crate::IResult::Done(&$input[offset..], &$input[..offset])
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Position($crate::ErrorKind::TakeUntilStr,$input))
|
||||
}
|
||||
};
|
||||
res
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use ::IResult;
|
||||
|
||||
#[test]
|
||||
fn tag_str_succeed() {
|
||||
const INPUT: &'static str = "Hello World!";
|
||||
const TAG: &'static str = "Hello";
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
tag_s!(input, TAG)
|
||||
}
|
||||
|
||||
match test(INPUT) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == " World!", "Parser `tag_s` consumed leftover input.");
|
||||
assert!(output == TAG,
|
||||
"Parser `tag_s` doesn't return the tag it matched on success. \
|
||||
Expected `{}`, got `{}`.", TAG, output);
|
||||
},
|
||||
other => panic!("Parser `tag_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tag_str_incomplete() {
|
||||
const INPUT: &'static str = "Hello";
|
||||
const TAG: &'static str = "Hello World!";
|
||||
|
||||
match tag_s!(INPUT, TAG) {
|
||||
IResult::Incomplete(_) => (),
|
||||
other => {
|
||||
panic!("Parser `tag_s` didn't require more input when it should have. \
|
||||
Got `{:?}`.", other);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tag_str_error() {
|
||||
const INPUT: &'static str = "Hello World!";
|
||||
const TAG: &'static str = "Random"; // TAG must be closer than INPUT.
|
||||
|
||||
match tag_s!(INPUT, TAG) {
|
||||
IResult::Error(_) => (),
|
||||
other => {
|
||||
panic!("Parser `tag_s` didn't fail when it should have. Got `{:?}`.`", other);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_s_succeed() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const CONSUMED: &'static str = "βèƒôřèÂßÇ";
|
||||
const LEFTOVER: &'static str = "áƒƭèř";
|
||||
|
||||
match take_s!(INPUT, 9) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == LEFTOVER, "Parser `take_s` consumed leftover input. Leftover `{}`.", extra);
|
||||
assert!(output == CONSUMED,
|
||||
"Parser `take_s` doens't return the string it consumed on success. Expected `{}`, got `{}`.",
|
||||
CONSUMED, output);
|
||||
},
|
||||
other => panic!("Parser `take_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_until_s_succeed() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇ∂áƒƭèř";
|
||||
const FIND: &'static str = "ÂßÇ∂";
|
||||
const CONSUMED: &'static str = "βèƒôřè";
|
||||
const LEFTOVER: &'static str = "ÂßÇ∂áƒƭèř";
|
||||
|
||||
match take_until_s!(INPUT, FIND) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == LEFTOVER, "Parser `take_until_s`\
|
||||
consumed leftover input. Leftover `{}`.", extra);
|
||||
assert!(output == CONSUMED, "Parser `take_until_s`\
|
||||
doens't return the string it consumed on success. Expected `{}`, got `{}`.",
|
||||
CONSUMED, output);
|
||||
}
|
||||
other => panic!("Parser `take_until_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_s_incomplete() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇá";
|
||||
|
||||
match take_s!(INPUT, 13) {
|
||||
IResult::Incomplete(_) => (),
|
||||
other => panic!("Parser `take_s` didn't require more input when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
}
|
||||
}
|
||||
|
||||
use internal::IResult::{Done, Error};
|
||||
use internal::Err::Position;
|
||||
use util::ErrorKind;
|
||||
|
||||
pub fn is_alphabetic(c:char) -> bool {
|
||||
(c as u8 >= 0x41 && c as u8 <= 0x5A) || (c as u8 >= 0x61 && c as u8 <= 0x7A)
|
||||
}
|
||||
#[test]
|
||||
fn take_while_s() {
|
||||
named!(f<&str,&str>, take_while_s!(is_alphabetic));
|
||||
let a = "";
|
||||
let b = "abcd";
|
||||
let c = "abcd123";
|
||||
let d = "123";
|
||||
|
||||
assert_eq!(f(&a[..]), Done(&a[..], &a[..]));
|
||||
assert_eq!(f(&b[..]), Done(&a[..], &b[..]));
|
||||
assert_eq!(f(&c[..]), Done(&d[..], &b[..]));
|
||||
assert_eq!(f(&d[..]), Done(&d[..], &a[..]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_while1_s() {
|
||||
named!(f<&str,&str>, take_while1_s!(is_alphabetic));
|
||||
let a = "";
|
||||
let b = "abcd";
|
||||
let c = "abcd123";
|
||||
let d = "123";
|
||||
|
||||
assert_eq!(f(&a[..]), Error(Position(ErrorKind::TakeWhile1Str, &""[..])));
|
||||
assert_eq!(f(&b[..]), Done(&a[..], &b[..]));
|
||||
assert_eq!(f(&c[..]), Done(&"123"[..], &b[..]));
|
||||
assert_eq!(f(&d[..]), Error(Position(ErrorKind::TakeWhile1Str, &d[..])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_till_s_succeed() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const CONSUMED: &'static str = "βèƒôřèÂßÇ";
|
||||
const LEFTOVER: &'static str = "áƒƭèř";
|
||||
fn till_s(c: char) -> bool {
|
||||
c == 'á'
|
||||
}
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
take_till_s!(input, till_s)
|
||||
}
|
||||
match test(INPUT) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == LEFTOVER, "Parser `take_till_s` consumed leftover input.");
|
||||
assert!(output == CONSUMED,
|
||||
"Parser `take_till_s` doesn't return the string it consumed on success. \
|
||||
Expected `{}`, got `{}`.", CONSUMED, output);
|
||||
},
|
||||
other => panic!("Parser `take_till_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_while_s_succeed_none() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const CONSUMED: &'static str = "";
|
||||
const LEFTOVER: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
fn while_s(c: char) -> bool {
|
||||
c == '9'
|
||||
}
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
take_while_s!(input, while_s)
|
||||
}
|
||||
match test(INPUT) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == LEFTOVER, "Parser `take_while_s` consumed leftover input.");
|
||||
assert!(output == CONSUMED,
|
||||
"Parser `take_while_s` doesn't return the string it consumed on success. \
|
||||
Expected `{}`, got `{}`.", CONSUMED, output);
|
||||
},
|
||||
other => panic!("Parser `take_while_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_not_s_succeed() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const AVOID: &'static str = "£úçƙ¥á";
|
||||
const CONSUMED: &'static str = "βèƒôřèÂßÇ";
|
||||
const LEFTOVER: &'static str = "áƒƭèř";
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
is_not_s!(input, AVOID)
|
||||
}
|
||||
match test(INPUT) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == LEFTOVER, "Parser `is_not_s` consumed leftover input. Leftover `{}`.", extra);
|
||||
assert!(output == CONSUMED,
|
||||
"Parser `is_not_s` doens't return the string it consumed on success. Expected `{}`, got `{}`.",
|
||||
CONSUMED, output);
|
||||
},
|
||||
other => panic!("Parser `is_not_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_until_and_consume_s_succeed() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const FIND: &'static str = "姂";
|
||||
const CONSUMED: &'static str = "βèƒôřèÂßÇ";
|
||||
const LEFTOVER: &'static str = "áƒƭèř";
|
||||
|
||||
match take_until_and_consume_s!(INPUT, FIND) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == LEFTOVER, "Parser `take_until_and_consume_s`\
|
||||
consumed leftover input. Leftover `{}`.", extra);
|
||||
assert!(output == CONSUMED, "Parser `take_until_and_consume_s`\
|
||||
doens't return the string it consumed on success. Expected `{}`, got `{}`.",
|
||||
CONSUMED, output);
|
||||
}
|
||||
other => panic!("Parser `take_until_and_consume_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_while_s_succeed_some() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const CONSUMED: &'static str = "βèƒôřèÂßÇ";
|
||||
const LEFTOVER: &'static str = "áƒƭèř";
|
||||
fn while_s(c: char) -> bool {
|
||||
c == 'β' || c == 'è' || c == 'ƒ' || c == 'ô' || c == 'ř' ||
|
||||
c == 'è' || c == 'Â' || c == 'ß' || c == 'Ç'
|
||||
}
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
take_while_s!(input, while_s)
|
||||
}
|
||||
match test(INPUT) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == LEFTOVER, "Parser `take_while_s` consumed leftover input.");
|
||||
assert!(output == CONSUMED,
|
||||
"Parser `take_while_s` doesn't return the string it consumed on success. \
|
||||
Expected `{}`, got `{}`.", CONSUMED, output);
|
||||
},
|
||||
other => panic!("Parser `take_while_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_not_s_fail() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const AVOID: &'static str = "βúçƙ¥";
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
is_not_s!(input, AVOID)
|
||||
}
|
||||
match test(INPUT) {
|
||||
IResult::Error(_) => (),
|
||||
other => panic!("Parser `is_not_s` didn't fail when it should have. Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_while1_s_succeed() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const CONSUMED: &'static str = "βèƒôřèÂßÇ";
|
||||
const LEFTOVER: &'static str = "áƒƭèř";
|
||||
fn while1_s(c: char) -> bool {
|
||||
c == 'β' || c == 'è' || c == 'ƒ' || c == 'ô' || c == 'ř' ||
|
||||
c == 'è' || c == 'Â' || c == 'ß' || c == 'Ç'
|
||||
}
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
take_while1_s!(input, while1_s)
|
||||
}
|
||||
match test(INPUT) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == LEFTOVER, "Parser `take_while1_s` consumed leftover input.");
|
||||
assert!(output == CONSUMED,
|
||||
"Parser `take_while1_s` doesn't return the string it consumed on success. \
|
||||
Expected `{}`, got `{}`.", CONSUMED, output);
|
||||
},
|
||||
other => panic!("Parser `take_while1_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_until_and_consume_s_incomplete() {
|
||||
const INPUT: &'static str = "βèƒôřè";
|
||||
const FIND: &'static str = "βèƒôřèÂßÇ";
|
||||
|
||||
match take_until_and_consume_s!(INPUT, FIND) {
|
||||
IResult::Incomplete(_) => (),
|
||||
other => panic!("Parser `take_until_and_consume_s` didn't require more input when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_until_s_incomplete() {
|
||||
const INPUT: &'static str = "βèƒôřè";
|
||||
const FIND: &'static str = "βèƒôřèÂßÇ";
|
||||
|
||||
match take_until_s!(INPUT, FIND) {
|
||||
IResult::Incomplete(_) => (),
|
||||
other => panic!("Parser `take_until_s` didn't require more input when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_a_s_succeed() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const MATCH: &'static str = "βèƒôřèÂßÇ";
|
||||
const CONSUMED: &'static str = "βèƒôřèÂßÇ";
|
||||
const LEFTOVER: &'static str = "áƒƭèř";
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
is_a_s!(input, MATCH)
|
||||
}
|
||||
match test(INPUT) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == LEFTOVER, "Parser `is_a_s` consumed leftover input. Leftover `{}`.", extra);
|
||||
assert!(output == CONSUMED,
|
||||
"Parser `is_a_s` doens't return the string it consumed on success. Expected `{}`, got `{}`.",
|
||||
CONSUMED, output);
|
||||
},
|
||||
other => panic!("Parser `is_a_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_while1_s_fail() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
fn while1_s(c: char) -> bool {
|
||||
c == '9'
|
||||
}
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
take_while1_s!(input, while1_s)
|
||||
}
|
||||
match test(INPUT) {
|
||||
IResult::Error(_) => (),
|
||||
other => panic!("Parser `take_while1_s` didn't fail when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_a_s_fail() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const MATCH: &'static str = "Ûñℓúçƙ¥";
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
is_a_s!(input, MATCH)
|
||||
}
|
||||
match test(INPUT) {
|
||||
IResult::Error(_) => (),
|
||||
other => panic!("Parser `is_a_s` didn't fail when it should have. Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_until_and_consume_s_error() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const FIND: &'static str = "Ráñδô₥";
|
||||
|
||||
match take_until_and_consume_s!(INPUT, FIND) {
|
||||
IResult::Error(_) => (),
|
||||
other => panic!("Parser `take_until_and_consume_s` didn't fail when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_until_s_error() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const FIND: &'static str = "Ráñδô₥";
|
||||
|
||||
match take_until_s!(INPUT, FIND) {
|
||||
IResult::Error(_) => (),
|
||||
other => panic!("Parser `take_until_and_consume_s` didn't fail when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,769 +0,0 @@
|
|||
use internal::{IResult,Err};
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[cfg(feature = "core")]
|
||||
use std::prelude::v1::*;
|
||||
use std::vec::Vec;
|
||||
use std::string::ToString;
|
||||
|
||||
/// useful functions to calculate the offset between slices and show a hexdump of a slice
|
||||
#[cfg(not(feature = "core"))]
|
||||
pub trait HexDisplay {
|
||||
/// offset between the first byte of self and the first byte of the argument
|
||||
fn offset(&self, second:&[u8]) -> usize;// OFFSET SHOULD GO TO ITS OWN TRAIT
|
||||
|
||||
/// Converts the value of `self` to a hex dump, returning the owned
|
||||
/// string.
|
||||
fn to_hex(&self, chunk_size: usize) -> String;
|
||||
|
||||
/// Converts the value of `self` to a hex dump beginning at `from` address, returning the owned
|
||||
/// string.
|
||||
fn to_hex_from(&self, chunk_size: usize, from: usize) -> String;
|
||||
}
|
||||
|
||||
pub trait InputLength {
|
||||
#[inline]
|
||||
fn input_len(&self) -> usize;
|
||||
}
|
||||
|
||||
impl<'a, T> InputLength for &'a[T] {
|
||||
#[inline]
|
||||
fn input_len(&self) -> usize {
|
||||
self.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> InputLength for &'a str {
|
||||
#[inline]
|
||||
fn input_len(&self) -> usize {
|
||||
self.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> InputLength for (&'a [u8], usize) {
|
||||
#[inline]
|
||||
fn input_len(&self) -> usize {
|
||||
//println!("bit input length for ({:?}, {}):", self.0, self.1);
|
||||
let res = self.0.len() * 8 - self.1;
|
||||
//println!("-> {}", res);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
use std::iter::Enumerate;
|
||||
#[cfg(not(feature = "core"))]
|
||||
use std::str::CharIndices;
|
||||
|
||||
pub trait AsChar {
|
||||
#[inline]
|
||||
fn as_char(self) -> char;
|
||||
#[inline]
|
||||
fn is_alpha(self) -> bool;
|
||||
#[inline]
|
||||
fn is_alphanum(self) -> bool;
|
||||
#[inline]
|
||||
fn is_0_to_9(self) -> bool;
|
||||
#[inline]
|
||||
fn is_hex_digit(self) -> bool;
|
||||
#[inline]
|
||||
fn is_oct_digit(self) -> bool;
|
||||
}
|
||||
|
||||
impl<'a> AsChar for &'a u8 {
|
||||
#[inline]
|
||||
fn as_char(self) -> char { *self as char }
|
||||
#[inline]
|
||||
fn is_alpha(self) -> bool {
|
||||
(*self >= 0x41 && *self <= 0x5A) || (*self >= 0x61 && *self <= 0x7A)
|
||||
}
|
||||
#[inline]
|
||||
fn is_alphanum(self) -> bool { self.is_alpha() || self.is_0_to_9() }
|
||||
#[inline]
|
||||
fn is_0_to_9(self) -> bool {
|
||||
*self >= 0x30 && *self <= 0x39
|
||||
}
|
||||
#[inline]
|
||||
fn is_hex_digit(self) -> bool {
|
||||
(*self >= 0x30 && *self <= 0x39) ||
|
||||
(*self >= 0x41 && *self <= 0x46) ||
|
||||
(*self >= 0x61 && *self <= 0x66)
|
||||
}
|
||||
#[inline]
|
||||
fn is_oct_digit(self) -> bool {
|
||||
*self >= 0x30 && *self <= 0x37
|
||||
}
|
||||
}
|
||||
|
||||
impl AsChar for char {
|
||||
#[inline]
|
||||
fn as_char(self) -> char { self }
|
||||
#[inline]
|
||||
fn is_alpha(self) -> bool { self.is_alphabetic() }
|
||||
#[inline]
|
||||
fn is_alphanum(self) -> bool { self.is_alpha() || self.is_0_to_9() }
|
||||
#[inline]
|
||||
fn is_0_to_9(self) -> bool { self.is_digit(10) }
|
||||
#[inline]
|
||||
fn is_hex_digit(self) -> bool { self.is_digit(16) }
|
||||
#[inline]
|
||||
fn is_oct_digit(self) -> bool { self.is_digit(8) }
|
||||
}
|
||||
|
||||
pub trait IterIndices {
|
||||
type Item: AsChar;
|
||||
type Iter : Iterator<Item=(usize, Self::Item)>;
|
||||
fn iter_indices(self) -> Self::Iter;
|
||||
}
|
||||
|
||||
impl<'a> IterIndices for &'a [u8] {
|
||||
type Item = &'a u8;
|
||||
type Iter = Enumerate<::std::slice::Iter<'a, u8>>;
|
||||
#[inline]
|
||||
fn iter_indices(self) -> Enumerate<::std::slice::Iter<'a, u8>> {
|
||||
self.iter().enumerate()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
impl<'a> IterIndices for &'a str {
|
||||
type Item = char;
|
||||
type Iter = CharIndices<'a>;
|
||||
#[inline]
|
||||
fn iter_indices(self) -> CharIndices<'a> {
|
||||
self.char_indices()
|
||||
}
|
||||
}
|
||||
|
||||
static CHARS: &'static[u8] = b"0123456789abcdef";
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
impl HexDisplay for [u8] {
|
||||
fn offset(&self, second:&[u8]) -> usize {
|
||||
let fst = self.as_ptr();
|
||||
let snd = second.as_ptr();
|
||||
|
||||
snd as usize - fst as usize
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn to_hex(&self, chunk_size: usize) -> String {
|
||||
self.to_hex_from(chunk_size, 0)
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn to_hex_from(&self, chunk_size: usize, from: usize) -> String {
|
||||
let mut v = Vec::with_capacity(self.len() * 3);
|
||||
let mut i = from;
|
||||
for chunk in self.chunks(chunk_size) {
|
||||
let s = format!("{:08x}", i);
|
||||
for &ch in s.as_bytes().iter() {
|
||||
v.push(ch);
|
||||
}
|
||||
v.push('\t' as u8);
|
||||
|
||||
i = i + chunk_size;
|
||||
|
||||
for &byte in chunk {
|
||||
v.push(CHARS[(byte >> 4) as usize]);
|
||||
v.push(CHARS[(byte & 0xf) as usize]);
|
||||
v.push(' ' as u8);
|
||||
}
|
||||
if chunk_size > chunk.len() {
|
||||
for j in 0..(chunk_size - chunk.len()) {
|
||||
v.push(' ' as u8);
|
||||
v.push(' ' as u8);
|
||||
v.push(' ' as u8);
|
||||
}
|
||||
}
|
||||
v.push('\t' as u8);
|
||||
|
||||
for &byte in chunk {
|
||||
if (byte >=32 && byte <= 126) || byte >= 128 {
|
||||
v.push(byte);
|
||||
} else {
|
||||
v.push('.' as u8);
|
||||
}
|
||||
}
|
||||
v.push('\n' as u8);
|
||||
}
|
||||
|
||||
String::from_utf8_lossy(&v[..]).into_owned()
|
||||
}
|
||||
}
|
||||
|
||||
/// Prints a message if the parser fails
|
||||
///
|
||||
/// The message prints the `Error` or `Incomplete`
|
||||
/// and the parser's calling code
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # fn main() {
|
||||
/// named!(f, dbg!( tag!( "abcd" ) ) );
|
||||
///
|
||||
/// let a = &b"efgh"[..];
|
||||
///
|
||||
/// // Will print the following message:
|
||||
/// // Error(Position(0, [101, 102, 103, 104])) at l.5 by ' tag ! ( "abcd" ) '
|
||||
/// f(a);
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! dbg (
|
||||
($i: expr, $submac:ident!( $($args:tt)* )) => (
|
||||
{
|
||||
let l = line!();
|
||||
match $submac!($i, $($args)*) {
|
||||
$crate::IResult::Error(a) => {
|
||||
println!("Error({:?}) at l.{} by ' {} '", a, l, stringify!($submac!($($args)*)));
|
||||
$crate::IResult::Error(a)
|
||||
},
|
||||
$crate::IResult::Incomplete(a) => {
|
||||
println!("Incomplete({:?}) at {} by ' {} '", a, l, stringify!($submac!($($args)*)));
|
||||
$crate::IResult::Incomplete(a)
|
||||
},
|
||||
a => a
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
($i:expr, $f:ident) => (
|
||||
dbg!($i, call!($f));
|
||||
);
|
||||
);
|
||||
|
||||
/// Prints a message and the input if the parser fails
|
||||
///
|
||||
/// The message prints the `Error` or `Incomplete`
|
||||
/// and the parser's calling code.
|
||||
///
|
||||
/// It also displays the input in hexdump format
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # fn main() {
|
||||
/// named!(f, dbg_dmp!( tag!( "abcd" ) ) );
|
||||
///
|
||||
/// let a = &b"efghijkl"[..];
|
||||
///
|
||||
/// // Will print the following message:
|
||||
/// // Error(Position(0, [101, 102, 103, 104, 105, 106, 107, 108])) at l.5 by ' tag ! ( "abcd" ) '
|
||||
/// // 00000000 65 66 67 68 69 6a 6b 6c efghijkl
|
||||
/// f(a);
|
||||
/// # }
|
||||
#[macro_export]
|
||||
macro_rules! dbg_dmp (
|
||||
($i: expr, $submac:ident!( $($args:tt)* )) => (
|
||||
{
|
||||
use $crate::HexDisplay;
|
||||
let l = line!();
|
||||
match $submac!($i, $($args)*) {
|
||||
$crate::IResult::Error(a) => {
|
||||
println!("Error({:?}) at l.{} by ' {} '\n{}", a, l, stringify!($submac!($($args)*)), $i.to_hex(8));
|
||||
$crate::IResult::Error(a)
|
||||
},
|
||||
$crate::IResult::Incomplete(a) => {
|
||||
println!("Incomplete({:?}) at {} by ' {} '\n{}", a, l, stringify!($submac!($($args)*)), $i.to_hex(8));
|
||||
$crate::IResult::Incomplete(a)
|
||||
},
|
||||
a => a
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
($i:expr, $f:ident) => (
|
||||
dbg_dmp!($i, call!($f));
|
||||
);
|
||||
);
|
||||
|
||||
pub fn error_to_list<P,E:Clone>(e:&Err<P,E>) -> Vec<ErrorKind<E>> {
|
||||
let mut v:Vec<ErrorKind<E>> = Vec::new();
|
||||
let mut err = e;
|
||||
loop {
|
||||
match *err {
|
||||
Err::Code(ref i) | Err::Position(ref i,_) => {
|
||||
v.push(i.clone());
|
||||
return v;
|
||||
},
|
||||
Err::Node(ref i, ref next) | Err::NodePosition(ref i, _, ref next) => {
|
||||
v.push(i.clone());
|
||||
err = &*next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compare_error_paths<P,E:Clone+PartialEq>(e1:&Err<P,E>, e2:&Err<P,E>) -> bool {
|
||||
error_to_list(e1) == error_to_list(e2)
|
||||
}
|
||||
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
use std::hash::Hash;
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
pub fn add_error_pattern<'a,I,O,E: Clone+Hash+Eq>(h: &mut HashMap<Vec<ErrorKind<E>>, &'a str>, res: IResult<I,O,E>, message: &'a str) -> bool {
|
||||
if let IResult::Error(e) = res {
|
||||
h.insert(error_to_list(&e), message);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn slice_to_offsets(input: &[u8], s: &[u8]) -> (usize, usize) {
|
||||
let start = input.as_ptr();
|
||||
let off1 = s.as_ptr() as usize - start as usize;
|
||||
let off2 = off1 + s.len();
|
||||
(off1, off2)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
pub fn prepare_errors<O,E: Clone>(input: &[u8], res: IResult<&[u8],O,E>) -> Option<Vec<(ErrorKind<E>, usize, usize)> > {
|
||||
if let IResult::Error(e) = res {
|
||||
let mut v:Vec<(ErrorKind<E>, usize, usize)> = Vec::new();
|
||||
let mut err = e.clone();
|
||||
loop {
|
||||
match err {
|
||||
Err::Position(i,s) => {
|
||||
let (o1, o2) = slice_to_offsets(input, s);
|
||||
v.push((i, o1, o2));
|
||||
//println!("v is: {:?}", v);
|
||||
break;
|
||||
},
|
||||
Err::NodePosition(i, s, next) => {
|
||||
let (o1, o2) = slice_to_offsets(input, s);
|
||||
v.push((i, o1, o2));
|
||||
err = *next;
|
||||
},
|
||||
Err::Node(_, next) => {
|
||||
err = *next;
|
||||
},
|
||||
Err::Code(_) => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
v.sort_by(|a, b| a.1.cmp(&b.1));
|
||||
Some(v)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
pub fn print_error<O,E:Clone>(input: &[u8], res: IResult<&[u8],O,E>) {
|
||||
if let Some(v) = prepare_errors(input, res) {
|
||||
let colors = generate_colors(&v);
|
||||
println!("parser codes: {}", print_codes(colors, HashMap::new()));
|
||||
println!("{}", print_offsets(input, 0, &v));
|
||||
|
||||
} else {
|
||||
println!("not an error");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
pub fn generate_colors<E>(v: &[(ErrorKind<E>, usize, usize)]) -> HashMap<u32, u8> {
|
||||
let mut h: HashMap<u32, u8> = HashMap::new();
|
||||
let mut color = 0;
|
||||
|
||||
for &(ref c,_,_) in v.iter() {
|
||||
h.insert(error_to_u32(c), color + 31);
|
||||
color = color + 1 % 7;
|
||||
}
|
||||
|
||||
h
|
||||
}
|
||||
|
||||
pub fn code_from_offset<E>(v: &[(ErrorKind<E>, usize, usize)], offset: usize) -> Option<u32> {
|
||||
let mut acc: Option<(u32, usize, usize)> = None;
|
||||
for &(ref ek, s, e) in v.iter() {
|
||||
let c = error_to_u32(ek);
|
||||
if s <= offset && offset <=e {
|
||||
if let Some((_, start, end)) = acc {
|
||||
if start <= s && e <= end {
|
||||
acc = Some((c, s, e));
|
||||
}
|
||||
} else {
|
||||
acc = Some((c, s, e));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some((code, _, _)) = acc {
|
||||
return Some(code);
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset_color(v: &mut Vec<u8>) {
|
||||
v.push(0x1B);
|
||||
v.push('[' as u8);
|
||||
v.push(0);
|
||||
v.push('m' as u8);
|
||||
}
|
||||
|
||||
pub fn write_color(v: &mut Vec<u8>, color: u8) {
|
||||
v.push(0x1B);
|
||||
v.push('[' as u8);
|
||||
v.push(1);
|
||||
v.push(';' as u8);
|
||||
let s = color.to_string();
|
||||
let bytes = s.as_bytes();
|
||||
v.extend(bytes.iter().cloned());
|
||||
v.push('m' as u8);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
pub fn print_codes(colors: HashMap<u32, u8>, names: HashMap<u32, &str>) -> String {
|
||||
let mut v = Vec::new();
|
||||
for (code, &color) in &colors {
|
||||
if let Some(&s) = names.get(&code) {
|
||||
let bytes = s.as_bytes();
|
||||
write_color(&mut v, color);
|
||||
v.extend(bytes.iter().cloned());
|
||||
} else {
|
||||
let s = code.to_string();
|
||||
let bytes = s.as_bytes();
|
||||
write_color(&mut v, color);
|
||||
v.extend(bytes.iter().cloned());
|
||||
}
|
||||
reset_color(&mut v);
|
||||
v.push(' ' as u8);
|
||||
}
|
||||
reset_color(&mut v);
|
||||
|
||||
String::from_utf8_lossy(&v[..]).into_owned()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
pub fn print_offsets<E>(input: &[u8], from: usize, offsets: &[(ErrorKind<E>, usize, usize)]) -> String {
|
||||
let mut v = Vec::with_capacity(input.len() * 3);
|
||||
let mut i = from;
|
||||
let chunk_size = 8;
|
||||
let mut current_code: Option<u32> = None;
|
||||
let mut current_code2: Option<u32> = None;
|
||||
|
||||
let colors = generate_colors(&offsets);
|
||||
|
||||
for chunk in input.chunks(chunk_size) {
|
||||
let s = format!("{:08x}", i);
|
||||
for &ch in s.as_bytes().iter() {
|
||||
v.push(ch);
|
||||
}
|
||||
v.push('\t' as u8);
|
||||
|
||||
let mut k = i;
|
||||
let mut l = i;
|
||||
for &byte in chunk {
|
||||
if let Some(code) = code_from_offset(&offsets, k) {
|
||||
if let Some(current) = current_code {
|
||||
if current != code {
|
||||
reset_color(&mut v);
|
||||
current_code = Some(code);
|
||||
if let Some(&color) = colors.get(&code) {
|
||||
write_color(&mut v, color);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
current_code = Some(code);
|
||||
if let Some(&color) = colors.get(&code) {
|
||||
write_color(&mut v, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
v.push(CHARS[(byte >> 4) as usize]);
|
||||
v.push(CHARS[(byte & 0xf) as usize]);
|
||||
v.push(' ' as u8);
|
||||
k = k + 1;
|
||||
}
|
||||
|
||||
reset_color(&mut v);
|
||||
|
||||
if chunk_size > chunk.len() {
|
||||
for _ in 0..(chunk_size - chunk.len()) {
|
||||
v.push(' ' as u8);
|
||||
v.push(' ' as u8);
|
||||
v.push(' ' as u8);
|
||||
}
|
||||
}
|
||||
v.push('\t' as u8);
|
||||
|
||||
for &byte in chunk {
|
||||
if let Some(code) = code_from_offset(&offsets, l) {
|
||||
if let Some(current) = current_code2 {
|
||||
if current != code {
|
||||
reset_color(&mut v);
|
||||
current_code2 = Some(code);
|
||||
if let Some(&color) = colors.get(&code) {
|
||||
write_color(&mut v, color);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
current_code2 = Some(code);
|
||||
if let Some(&color) = colors.get(&code) {
|
||||
write_color(&mut v, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (byte >=32 && byte <= 126) || byte >= 128 {
|
||||
v.push(byte);
|
||||
} else {
|
||||
v.push('.' as u8);
|
||||
}
|
||||
l = l + 1;
|
||||
}
|
||||
reset_color(&mut v);
|
||||
|
||||
v.push('\n' as u8);
|
||||
i = i + chunk_size;
|
||||
}
|
||||
|
||||
String::from_utf8_lossy(&v[..]).into_owned()
|
||||
}
|
||||
|
||||
pub trait AsBytes {
|
||||
fn as_bytes(&self) -> &[u8];
|
||||
}
|
||||
|
||||
impl<'a> AsBytes for &'a str {
|
||||
#[inline(always)]
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
str::as_bytes(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsBytes for str {
|
||||
#[inline(always)]
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
str::as_bytes(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsBytes for &'a [u8] {
|
||||
#[inline(always)]
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl AsBytes for [u8] {
|
||||
#[inline(always)]
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! array_impls {
|
||||
($($N:expr)+) => {
|
||||
$(
|
||||
impl<'a> AsBytes for &'a [u8; $N] {
|
||||
#[inline(always)]
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl AsBytes for [u8; $N] {
|
||||
#[inline(always)]
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
self
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
array_impls! {
|
||||
0 1 2 3 4 5 6 7 8 9
|
||||
10 11 12 13 14 15 16 17 18 19
|
||||
20 21 22 23 24 25 26 27 28 29
|
||||
30 31 32
|
||||
}
|
||||
|
||||
/// indicates which parser returned an error
|
||||
#[derive(Debug,PartialEq,Eq,Hash,Clone)]
|
||||
pub enum ErrorKind<E=u32> {
|
||||
Custom(E),
|
||||
Tag,
|
||||
MapRes,
|
||||
MapOpt,
|
||||
Alt,
|
||||
IsNot,
|
||||
IsA,
|
||||
SeparatedList,
|
||||
SeparatedNonEmptyList,
|
||||
Many0,
|
||||
Many1,
|
||||
Count,
|
||||
TakeUntilAndConsume,
|
||||
TakeUntil,
|
||||
TakeUntilEitherAndConsume,
|
||||
TakeUntilEither,
|
||||
LengthValue,
|
||||
TagClosure,
|
||||
Alpha,
|
||||
Digit,
|
||||
HexDigit,
|
||||
OctDigit,
|
||||
AlphaNumeric,
|
||||
Space,
|
||||
MultiSpace,
|
||||
LengthValueFn,
|
||||
Eof,
|
||||
ExprOpt,
|
||||
ExprRes,
|
||||
CondReduce,
|
||||
Switch,
|
||||
TagBits,
|
||||
OneOf,
|
||||
NoneOf,
|
||||
Char,
|
||||
CrLf,
|
||||
RegexpMatch,
|
||||
RegexpMatches,
|
||||
RegexpFind,
|
||||
RegexpCapture,
|
||||
RegexpCaptures,
|
||||
TakeWhile1,
|
||||
Complete,
|
||||
Fix,
|
||||
Escaped,
|
||||
EscapedTransform,
|
||||
TagStr,
|
||||
IsNotStr,
|
||||
IsAStr,
|
||||
TakeWhile1Str,
|
||||
NonEmpty,
|
||||
ManyMN,
|
||||
TakeUntilAndConsumeStr,
|
||||
TakeUntilStr,
|
||||
Not
|
||||
}
|
||||
|
||||
pub fn error_to_u32<E>(e: &ErrorKind<E>) -> u32 {
|
||||
match *e {
|
||||
ErrorKind::Custom(_) => 0,
|
||||
ErrorKind::Tag => 1,
|
||||
ErrorKind::MapRes => 2,
|
||||
ErrorKind::MapOpt => 3,
|
||||
ErrorKind::Alt => 4,
|
||||
ErrorKind::IsNot => 5,
|
||||
ErrorKind::IsA => 6,
|
||||
ErrorKind::SeparatedList => 7,
|
||||
ErrorKind::SeparatedNonEmptyList => 8,
|
||||
ErrorKind::Many1 => 9,
|
||||
ErrorKind::Count => 10,
|
||||
ErrorKind::TakeUntilAndConsume => 11,
|
||||
ErrorKind::TakeUntil => 12,
|
||||
ErrorKind::TakeUntilEitherAndConsume => 13,
|
||||
ErrorKind::TakeUntilEither => 14,
|
||||
ErrorKind::LengthValue => 15,
|
||||
ErrorKind::TagClosure => 16,
|
||||
ErrorKind::Alpha => 17,
|
||||
ErrorKind::Digit => 18,
|
||||
ErrorKind::AlphaNumeric => 19,
|
||||
ErrorKind::Space => 20,
|
||||
ErrorKind::MultiSpace => 21,
|
||||
ErrorKind::LengthValueFn => 22,
|
||||
ErrorKind::Eof => 23,
|
||||
ErrorKind::ExprOpt => 24,
|
||||
ErrorKind::ExprRes => 25,
|
||||
ErrorKind::CondReduce => 26,
|
||||
ErrorKind::Switch => 27,
|
||||
ErrorKind::TagBits => 28,
|
||||
ErrorKind::OneOf => 29,
|
||||
ErrorKind::NoneOf => 30,
|
||||
ErrorKind::Char => 40,
|
||||
ErrorKind::CrLf => 41,
|
||||
ErrorKind::RegexpMatch => 42,
|
||||
ErrorKind::RegexpMatches => 43,
|
||||
ErrorKind::RegexpFind => 44,
|
||||
ErrorKind::RegexpCapture => 45,
|
||||
ErrorKind::RegexpCaptures => 46,
|
||||
ErrorKind::TakeWhile1 => 47,
|
||||
ErrorKind::Complete => 48,
|
||||
ErrorKind::Fix => 49,
|
||||
ErrorKind::Escaped => 50,
|
||||
ErrorKind::EscapedTransform => 51,
|
||||
ErrorKind::TagStr => 52,
|
||||
ErrorKind::IsNotStr => 53,
|
||||
ErrorKind::IsAStr => 54,
|
||||
ErrorKind::TakeWhile1Str => 55,
|
||||
ErrorKind::NonEmpty => 56,
|
||||
ErrorKind::ManyMN => 57,
|
||||
ErrorKind::TakeUntilAndConsumeStr => 58,
|
||||
ErrorKind::HexDigit => 59,
|
||||
ErrorKind::TakeUntilStr => 60,
|
||||
ErrorKind::OctDigit => 61,
|
||||
ErrorKind::Many0 => 62,
|
||||
ErrorKind::Not => 63,
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> ErrorKind<E> {
|
||||
pub fn description(&self) -> &str {
|
||||
match *self {
|
||||
ErrorKind::Custom(_) => "Custom error",
|
||||
ErrorKind::Tag => "Tag",
|
||||
ErrorKind::MapRes => "Map on Result",
|
||||
ErrorKind::MapOpt => "Map on Option",
|
||||
ErrorKind::Alt => "Alternative",
|
||||
ErrorKind::IsNot => "IsNot",
|
||||
ErrorKind::IsA => "IsA",
|
||||
ErrorKind::SeparatedList => "Separated list",
|
||||
ErrorKind::SeparatedNonEmptyList => "Separated non empty list",
|
||||
ErrorKind::Many0 => "Many0",
|
||||
ErrorKind::Many1 => "Many1",
|
||||
ErrorKind::Count => "Count",
|
||||
ErrorKind::TakeUntilAndConsume => "Take until and consume",
|
||||
ErrorKind::TakeUntil => "Take until",
|
||||
ErrorKind::TakeUntilEitherAndConsume => "Take until either and consume",
|
||||
ErrorKind::TakeUntilEither => "Take until either",
|
||||
ErrorKind::LengthValue => "Length followed by value",
|
||||
ErrorKind::TagClosure => "Tag closure",
|
||||
ErrorKind::Alpha => "Alphabetic",
|
||||
ErrorKind::Digit => "Digit",
|
||||
ErrorKind::AlphaNumeric => "AlphaNumeric",
|
||||
ErrorKind::Space => "Space",
|
||||
ErrorKind::MultiSpace => "Multiple spaces",
|
||||
ErrorKind::LengthValueFn => "LengthValueFn",
|
||||
ErrorKind::Eof => "End of file",
|
||||
ErrorKind::ExprOpt => "Evaluate Option",
|
||||
ErrorKind::ExprRes => "Evaluate Result",
|
||||
ErrorKind::CondReduce => "Condition reduce",
|
||||
ErrorKind::Switch => "Switch",
|
||||
ErrorKind::TagBits => "Tag on bitstream",
|
||||
ErrorKind::OneOf => "OneOf",
|
||||
ErrorKind::NoneOf => "NoneOf",
|
||||
ErrorKind::Char => "Char",
|
||||
ErrorKind::CrLf => "CrLf",
|
||||
ErrorKind::RegexpMatch => "RegexpMatch",
|
||||
ErrorKind::RegexpMatches => "RegexpMatches",
|
||||
ErrorKind::RegexpFind => "RegexpFind",
|
||||
ErrorKind::RegexpCapture => "RegexpCapture",
|
||||
ErrorKind::RegexpCaptures => "RegexpCaptures",
|
||||
ErrorKind::TakeWhile1 => "TakeWhile1",
|
||||
ErrorKind::Complete => "Complete",
|
||||
ErrorKind::Fix => "Fix",
|
||||
ErrorKind::Escaped => "Escaped",
|
||||
ErrorKind::EscapedTransform => "EscapedTransform",
|
||||
ErrorKind::TagStr => "Tag on strings",
|
||||
ErrorKind::IsNotStr => "IsNot on strings",
|
||||
ErrorKind::IsAStr => "IsA on strings",
|
||||
ErrorKind::TakeWhile1Str => "TakeWhile1 on strings",
|
||||
ErrorKind::NonEmpty => "NonEmpty",
|
||||
ErrorKind::ManyMN => "Many(m, n)",
|
||||
ErrorKind::TakeUntilAndConsumeStr => "Take until and consume on strings",
|
||||
ErrorKind::HexDigit => "Hexadecimal Digit",
|
||||
ErrorKind::TakeUntilStr => "Take until on strings",
|
||||
ErrorKind::OctDigit => "Octal digit",
|
||||
ErrorKind::Not => "Negation",
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use nom::{IResult,digit, multispace};
|
||||
|
||||
use std::str;
|
||||
use std::str::FromStr;
|
||||
|
||||
named!(parens<i64>, delimited!(
|
||||
delimited!(opt!(multispace), tag!("("), opt!(multispace)),
|
||||
expr,
|
||||
delimited!(opt!(multispace), tag!(")"), opt!(multispace))
|
||||
)
|
||||
);
|
||||
|
||||
named!(factor<i64>, alt!(
|
||||
map_res!(
|
||||
map_res!(
|
||||
delimited!(opt!(multispace), digit, opt!(multispace)),
|
||||
str::from_utf8
|
||||
),
|
||||
FromStr::from_str
|
||||
)
|
||||
| parens
|
||||
)
|
||||
);
|
||||
|
||||
named!(term <i64>, chain!(
|
||||
mut acc: factor ~
|
||||
many0!(
|
||||
alt!(
|
||||
tap!(mul: preceded!(tag!("*"), factor) => acc = acc * mul) |
|
||||
tap!(div: preceded!(tag!("/"), factor) => acc = acc / div)
|
||||
)
|
||||
),
|
||||
|| { return acc }
|
||||
)
|
||||
);
|
||||
|
||||
named!(expr <i64>, chain!(
|
||||
mut acc: term ~
|
||||
many0!(
|
||||
alt!(
|
||||
tap!(add: preceded!(tag!("+"), term) => acc = acc + add) |
|
||||
tap!(sub: preceded!(tag!("-"), term) => acc = acc - sub)
|
||||
)
|
||||
),
|
||||
|| { return acc }
|
||||
)
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn factor_test() {
|
||||
assert_eq!(factor(&b"3"[..]), IResult::Done(&b""[..], 3));
|
||||
assert_eq!(factor(&b" 12"[..]), IResult::Done(&b""[..], 12));
|
||||
assert_eq!(factor(&b"537 "[..]), IResult::Done(&b""[..], 537));
|
||||
assert_eq!(factor(&b" 24 "[..]), IResult::Done(&b""[..], 24));
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn term_test() {
|
||||
assert_eq!(term(&b" 12 *2 / 3"[..]), IResult::Done(&b""[..], 8));
|
||||
assert_eq!(term(&b" 2* 3 *2 *2 / 3"[..]), IResult::Done(&b""[..], 8));
|
||||
assert_eq!(term(&b" 48 / 3/2"[..]), IResult::Done(&b""[..], 8));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expr_test() {
|
||||
assert_eq!(expr(&b" 1 + 2 "[..]), IResult::Done(&b""[..], 3));
|
||||
assert_eq!(expr(&b" 12 + 6 - 4+ 3"[..]), IResult::Done(&b""[..], 17));
|
||||
assert_eq!(expr(&b" 1 + 2*3 + 4"[..]), IResult::Done(&b""[..], 11));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parens_test() {
|
||||
assert_eq!(expr(&b" ( 2 )"[..]), IResult::Done(&b""[..], 2));
|
||||
assert_eq!(expr(&b" 2* ( 3 + 4 ) "[..]), IResult::Done(&b""[..], 14));
|
||||
assert_eq!(expr(&b" 2*2 / ( 5 - 1) + 3"[..]), IResult::Done(&b""[..], 4));
|
||||
}
|
|
@ -1,137 +0,0 @@
|
|||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use std::fmt;
|
||||
use std::fmt::{Display, Debug, Formatter};
|
||||
|
||||
use std::str;
|
||||
use std::str::FromStr;
|
||||
|
||||
use nom::{IResult, digit, multispace};
|
||||
|
||||
pub enum Expr {
|
||||
Value(i64),
|
||||
Add(Box<Expr>, Box<Expr>),
|
||||
Sub(Box<Expr>, Box<Expr>),
|
||||
Mul(Box<Expr>, Box<Expr>),
|
||||
Div(Box<Expr>, Box<Expr>),
|
||||
Paren(Box<Expr>),
|
||||
}
|
||||
|
||||
pub enum Oper {
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
}
|
||||
|
||||
impl Display for Expr {
|
||||
fn fmt(&self, format: &mut Formatter) -> fmt::Result {
|
||||
use self::Expr::*;
|
||||
match *self {
|
||||
Value(val) => write!(format, "{}", val),
|
||||
Add(ref left, ref right) => write!(format, "{} + {}", left, right),
|
||||
Sub(ref left, ref right) => write!(format, "{} - {}", left, right),
|
||||
Mul(ref left, ref right) => write!(format, "{} * {}", left, right),
|
||||
Div(ref left, ref right) => write!(format, "{} / {}", left, right),
|
||||
Paren(ref expr) => write!(format, "({})", expr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Expr {
|
||||
fn fmt(&self, format: &mut Formatter) -> fmt::Result {
|
||||
use self::Expr::*;
|
||||
match *self {
|
||||
Value(val) => write!(format, "{}", val),
|
||||
Add(ref left, ref right) => write!(format, "({:?} + {:?})", left, right),
|
||||
Sub(ref left, ref right) => write!(format, "({:?} - {:?})", left, right),
|
||||
Mul(ref left, ref right) => write!(format, "({:?} * {:?})", left, right),
|
||||
Div(ref left, ref right) => write!(format, "({:?} / {:?})", left, right),
|
||||
Paren(ref expr) => write!(format, "[{:?}]", expr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
named!(parens< Expr >, delimited!(
|
||||
delimited!(opt!(multispace), tag!("("), opt!(multispace)),
|
||||
map!(map!(expr, Box::new), Expr::Paren),
|
||||
delimited!(opt!(multispace), tag!(")"), opt!(multispace))
|
||||
)
|
||||
);
|
||||
|
||||
named!(factor< Expr >, alt_complete!(
|
||||
map!(
|
||||
map_res!(
|
||||
map_res!(
|
||||
delimited!(opt!(multispace), digit, opt!(multispace)),
|
||||
str::from_utf8
|
||||
),
|
||||
FromStr::from_str
|
||||
),
|
||||
Expr::Value)
|
||||
| parens
|
||||
)
|
||||
);
|
||||
|
||||
fn fold_exprs(initial: Expr, remainder: Vec<(Oper, Expr)>) -> Expr {
|
||||
remainder.into_iter().fold(initial, |acc, pair| {
|
||||
let (oper, expr) = pair;
|
||||
match oper {
|
||||
Oper::Add => Expr::Add(Box::new(acc), Box::new(expr)),
|
||||
Oper::Sub => Expr::Sub(Box::new(acc), Box::new(expr)),
|
||||
Oper::Mul => Expr::Mul(Box::new(acc), Box::new(expr)),
|
||||
Oper::Div => Expr::Div(Box::new(acc), Box::new(expr)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
named!(term< Expr >, chain!(
|
||||
initial: factor ~
|
||||
remainder: many0!(
|
||||
alt!(
|
||||
chain!(tag!("*") ~ mul: factor, || { (Oper::Mul, mul) }) |
|
||||
chain!(tag!("/") ~ div: factor, || { (Oper::Div, div) })
|
||||
)
|
||||
),
|
||||
|| fold_exprs(initial, remainder))
|
||||
);
|
||||
|
||||
named!(expr< Expr >, chain!(
|
||||
initial: term ~
|
||||
remainder: many0!(
|
||||
alt!(
|
||||
chain!(tag!("+") ~ add: term, || { (Oper::Add, add) }) |
|
||||
chain!(tag!("-") ~ sub: term, || { (Oper::Sub, sub) })
|
||||
)
|
||||
),
|
||||
|| fold_exprs(initial, remainder))
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn factor_test() {
|
||||
assert_eq!(factor(&b" 3 "[..]).map(|x| format!("{:?}", x)),
|
||||
IResult::Done(&b""[..], String::from("3")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn term_test() {
|
||||
assert_eq!(term(&b" 3 * 5 "[..]).map(|x| format!("{:?}", x)),
|
||||
IResult::Done(&b""[..], String::from("(3 * 5)")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expr_test() {
|
||||
assert_eq!(expr(&b" 1 + 2 * 3 "[..]).map(|x| format!("{:?}", x)),
|
||||
IResult::Done(&b""[..], String::from("(1 + (2 * 3))")));
|
||||
assert_eq!(expr(&b" 1 + 2 * 3 / 4 - 5 "[..]).map(|x| format!("{:?}", x)),
|
||||
IResult::Done(&b""[..], String::from("((1 + ((2 * 3) / 4)) - 5)")));
|
||||
assert_eq!(expr(&b" 72 / 2 / 3 "[..]).map(|x| format!("{:?}", x)),
|
||||
IResult::Done(&b""[..], String::from("((72 / 2) / 3)")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parens_test() {
|
||||
assert_eq!(expr(&b" ( 1 + 2 ) * 3 "[..]).map(|x| format!("{:?}", x)),
|
||||
IResult::Done(&b""[..], String::from("([(1 + 2)] * 3)")));
|
||||
}
|
|
@ -1,140 +0,0 @@
|
|||
/// this file tests a different backtracking behaviour. With the current
|
||||
/// `error!` macro, an early return is done in the current function, but
|
||||
/// backtracking continues normally outside of that function.
|
||||
///
|
||||
/// The solution here wraps `IResult` in a `Result`: a `Ok` indicates usual
|
||||
/// backtracking, `Err` indicates that we must "cut".
|
||||
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
macro_rules! n (
|
||||
($name:ident( $i:ty ) -> $o:ty, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( i: $i ) -> std::result::Result<nom::IResult<$i,$o,u32>, nom::Err<$i, u32>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
($name:ident<$i:ty,$o:ty,$e:ty>, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( i: $i ) -> std::result::Result<nom::IResult<$i, $o, $e>, nom::Err<$i, $e>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
($name:ident<$i:ty,$o:ty>, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( i: $i ) -> std::result::Result<nom::IResult<$i, $o, u32>, nom::Err<$i, u32>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
($name:ident<$o:ty>, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name<'a>( i: &'a[u8] ) -> std::result::Result<nom::IResult<&'a [u8], $o, u32>, nom::Err<&'a [u8], u32>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
($name:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( i: &[u8] ) -> std::result::Result<nom::IResult<&[u8], &[u8], u32>, nom::Err<&[u8], u32>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
(pub $name:ident( $i:ty ) -> $o:ty, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name( i: $i ) -> std::result::Result<nom::IResult<$i,$o, u32>, nom::Err<$i, u32>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$i:ty,$o:ty,$e:ty>, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name( i: $i ) -> std::result::Result<nom::IResult<$i, $o, $e>, nom::Err<$i, $e>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$i:ty,$o:ty>, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name( i: $i ) -> std::result::Result<nom::IResult<$i, $o, u32>, nom::Err<$i, u32>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$o:ty>, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name( i: &[u8] ) -> std::result::Result<nom::IResult<&[u8], $o, u32>, nom::Err<&[u8], u32>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
(pub $name:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name<'a>( i: &'a [u8] ) -> std::result::Result<nom::IResult<&[u8], &[u8], u32>, nom::Err<&[u8], u32>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
macro_rules! cut (
|
||||
($i:expr, $code:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
{
|
||||
let cl = || {
|
||||
Ok($submac!($i, $($args)*))
|
||||
};
|
||||
|
||||
match cl() {
|
||||
std::result::Result::Ok(nom::IResult::Incomplete(x)) => nom::IResult::Incomplete(x),
|
||||
std::result::Result::Ok(nom::IResult::Done(i, o)) => nom::IResult::Done(i, o),
|
||||
std::result::Result::Ok(nom::IResult::Error(e)) | std::result::Result::Err(e) => {
|
||||
return std::result::Result::Err(nom::Err::NodePosition($code, $i, Box::new(e)))
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
($i:expr, $code:expr, $f:expr) => (
|
||||
cut!($i, $code, call!($f));
|
||||
);
|
||||
);
|
||||
|
||||
macro_rules! c (
|
||||
($i:expr, $f:expr) => (
|
||||
{
|
||||
match $f($i) {
|
||||
std::result::Result::Ok(nom::IResult::Incomplete(x)) => nom::IResult::Incomplete(x),
|
||||
std::result::Result::Ok(nom::IResult::Done(i, o)) => nom::IResult::Done(i, o),
|
||||
std::result::Result::Ok(nom::IResult::Error(e)) => nom::IResult::Error(e),
|
||||
std::result::Result::Err(e) => {
|
||||
return std::result::Result::Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
n!(pub foo< bool >,
|
||||
chain!(
|
||||
tag!("a") ~
|
||||
cut!(nom::ErrorKind::Custom(42),dbg_dmp!(tag!("b"))) ,
|
||||
|| { true }
|
||||
)
|
||||
);
|
||||
|
||||
n!(pub foos< Vec<bool> >,
|
||||
delimited!(
|
||||
tag!("("),
|
||||
many0!(c!(foo)),
|
||||
tag!(")")
|
||||
)
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn test_ok() {
|
||||
let r = foos(b"(abab)");
|
||||
println!("result: {:?}", r);
|
||||
match r {
|
||||
Ok(nom::IResult::Done(_,result)) => assert_eq!(result,vec![true,true]),
|
||||
res => panic!("Oops {:?}.",res)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_err() {
|
||||
let input = b"(ac)";
|
||||
let r = foos(&input[..]);
|
||||
println!("result: {:?}", r);
|
||||
match r {
|
||||
//Ok(nom::IResult::Error(nom::Err::Position(kind,_))) => assert_eq!(kind,nom::ErrorKind::Custom(42)),
|
||||
Err(nom::Err::NodePosition(kind, position, _)) => {
|
||||
assert_eq!(kind, nom::ErrorKind::Custom(42));
|
||||
assert_eq!(position, &input[2..]);
|
||||
}
|
||||
res => panic!("Oops, {:?}",res)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,234 +0,0 @@
|
|||
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use nom::{IResult,not_line_ending, space, alphanumeric, multispace};
|
||||
|
||||
use std::str;
|
||||
use std::collections::HashMap;
|
||||
|
||||
named!(category<&str>, map_res!(
|
||||
terminated!(
|
||||
delimited!(tag!("["), take_until!("]"), tag!("]")),
|
||||
opt!(multispace)
|
||||
),
|
||||
str::from_utf8
|
||||
));
|
||||
|
||||
named!(key_value <&[u8],(&str,&str)>,
|
||||
chain!(
|
||||
key: map_res!(alphanumeric, std::str::from_utf8) ~
|
||||
space? ~
|
||||
tag!("=") ~
|
||||
space? ~
|
||||
val: map_res!(
|
||||
take_until_either!("\n;"),
|
||||
str::from_utf8
|
||||
) ~
|
||||
space? ~
|
||||
chain!(
|
||||
tag!(";") ~
|
||||
not_line_ending ,
|
||||
||{}
|
||||
) ? ~
|
||||
multispace? ,
|
||||
||{(key, val)}
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
named!(keys_and_values_aggregator<&[u8], Vec<(&str,&str)> >, many0!(key_value));
|
||||
|
||||
fn keys_and_values(input:&[u8]) -> IResult<&[u8], HashMap<&str, &str> > {
|
||||
let mut h: HashMap<&str, &str> = HashMap::new();
|
||||
|
||||
match keys_and_values_aggregator(input) {
|
||||
IResult::Done(i,tuple_vec) => {
|
||||
for &(k,v) in &tuple_vec {
|
||||
h.insert(k, v);
|
||||
}
|
||||
IResult::Done(i, h)
|
||||
},
|
||||
IResult::Incomplete(a) => IResult::Incomplete(a),
|
||||
IResult::Error(a) => IResult::Error(a)
|
||||
}
|
||||
}
|
||||
|
||||
named!(category_and_keys<&[u8],(&str,HashMap<&str,&str>)>,
|
||||
chain!(
|
||||
category: category ~
|
||||
keys: keys_and_values ,
|
||||
move ||{(category, keys)}
|
||||
)
|
||||
);
|
||||
|
||||
named!(categories_aggregator<&[u8], Vec<(&str, HashMap<&str,&str>)> >, many0!(category_and_keys));
|
||||
|
||||
fn categories(input: &[u8]) -> IResult<&[u8], HashMap<&str, HashMap<&str, &str> > > {
|
||||
let mut h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
|
||||
|
||||
match categories_aggregator(input) {
|
||||
IResult::Done(i,tuple_vec) => {
|
||||
for &(k,ref v) in &tuple_vec {
|
||||
h.insert(k, v.clone());
|
||||
}
|
||||
IResult::Done(i, h)
|
||||
},
|
||||
IResult::Incomplete(a) => IResult::Incomplete(a),
|
||||
IResult::Error(a) => IResult::Error(a)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_category_test() {
|
||||
let ini_file = &b"[category]
|
||||
|
||||
parameter=value
|
||||
key = value2"[..];
|
||||
|
||||
let ini_without_category = &b"parameter=value
|
||||
key = value2"[..];
|
||||
|
||||
let res = category(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
assert_eq!(res, IResult::Done(ini_without_category, "category"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_key_value_test() {
|
||||
let ini_file = &b"parameter=value
|
||||
key = value2"[..];
|
||||
|
||||
let ini_without_key_value = &b"key = value2"[..];
|
||||
|
||||
let res = key_value(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, (o1, o2)) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn parse_key_value_with_space_test() {
|
||||
let ini_file = &b"parameter = value
|
||||
key = value2"[..];
|
||||
|
||||
let ini_without_key_value = &b"key = value2"[..];
|
||||
|
||||
let res = key_value(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, (o1, o2)) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_key_value_with_comment_test() {
|
||||
let ini_file = &b"parameter=value;abc
|
||||
key = value2"[..];
|
||||
|
||||
let ini_without_key_value = &b"key = value2"[..];
|
||||
|
||||
let res = key_value(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, (o1, o2)) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_multiple_keys_and_values_test() {
|
||||
let ini_file = &b"parameter=value;abc
|
||||
|
||||
key = value2
|
||||
|
||||
[category]"[..];
|
||||
|
||||
let ini_without_key_value = &b"[category]"[..];
|
||||
|
||||
let res = keys_and_values(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, ref o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
let mut expected: HashMap<&str, &str> = HashMap::new();
|
||||
expected.insert("parameter", "value");
|
||||
expected.insert("key", "value2");
|
||||
assert_eq!(res, IResult::Done(ini_without_key_value, expected));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_category_then_multiple_keys_and_values_test() {
|
||||
//FIXME: there can be an empty line or a comment line after a category
|
||||
let ini_file = &b"[abcd]
|
||||
parameter=value;abc
|
||||
|
||||
key = value2
|
||||
|
||||
[category]"[..];
|
||||
|
||||
let ini_after_parser = &b"[category]"[..];
|
||||
|
||||
let res = category_and_keys(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, ref o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
let mut expected_h: HashMap<&str, &str> = HashMap::new();
|
||||
expected_h.insert("parameter", "value");
|
||||
expected_h.insert("key", "value2");
|
||||
assert_eq!(res, IResult::Done(ini_after_parser, ("abcd", expected_h)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_multiple_categories_test() {
|
||||
let ini_file = &b"[abcd]
|
||||
|
||||
parameter=value;abc
|
||||
|
||||
key = value2
|
||||
|
||||
[category]
|
||||
parameter3=value3
|
||||
key4 = value4
|
||||
"[..];
|
||||
|
||||
let ini_after_parser = &b""[..];
|
||||
|
||||
let res = categories(ini_file);
|
||||
//println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, ref o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
let mut expected_1: HashMap<&str, &str> = HashMap::new();
|
||||
expected_1.insert("parameter", "value");
|
||||
expected_1.insert("key", "value2");
|
||||
let mut expected_2: HashMap<&str, &str> = HashMap::new();
|
||||
expected_2.insert("parameter3", "value3");
|
||||
expected_2.insert("key4", "value4");
|
||||
let mut expected_h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
|
||||
expected_h.insert("abcd", expected_1);
|
||||
expected_h.insert("category", expected_2);
|
||||
assert_eq!(res, IResult::Done(ini_after_parser, expected_h));
|
||||
}
|
|
@ -1,251 +0,0 @@
|
|||
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use nom::IResult;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
fn is_alphabetic(chr:char) -> bool {
|
||||
(chr as u8 >= 0x41 && chr as u8 <= 0x5A) || (chr as u8 >= 0x61 && chr as u8 <= 0x7A)
|
||||
}
|
||||
|
||||
fn is_digit(chr: char) -> bool {
|
||||
chr as u8 >= 0x30 && chr as u8 <= 0x39
|
||||
}
|
||||
|
||||
fn is_alphanumeric(chr: char) -> bool {
|
||||
is_alphabetic(chr) || is_digit(chr)
|
||||
}
|
||||
|
||||
fn is_space(chr:char) -> bool {
|
||||
chr == ' ' || chr == '\t'
|
||||
}
|
||||
|
||||
fn is_line_ending_or_comment(chr:char) -> bool {
|
||||
chr == ';' || chr == '\n'
|
||||
}
|
||||
|
||||
named!(alphanumeric<&str,&str>, take_while_s!(is_alphanumeric));
|
||||
named!(not_line_ending<&str,&str>, is_not_s!("\r\n"));
|
||||
named!(space<&str,&str>, take_while_s!(is_space));
|
||||
named!(space_or_line_ending<&str,&str>, is_a_s!(" \r\n"));
|
||||
|
||||
fn right_bracket(c:char) -> bool {
|
||||
c == ']'
|
||||
}
|
||||
|
||||
named!(category <&str, &str>,
|
||||
chain!(
|
||||
tag_s!("[") ~
|
||||
name: take_till_s!(right_bracket) ~
|
||||
tag_s!("]") ~
|
||||
space_or_line_ending? ,
|
||||
||{ name }
|
||||
)
|
||||
);
|
||||
|
||||
named!(key_value <&str,(&str,&str)>,
|
||||
chain!(
|
||||
key: alphanumeric ~
|
||||
space? ~
|
||||
tag_s!("=") ~
|
||||
space? ~
|
||||
val: take_till_s!(is_line_ending_or_comment) ~
|
||||
space? ~
|
||||
pair!(tag_s!(";"), not_line_ending)? ~
|
||||
space_or_line_ending? ,
|
||||
||{(key, val)}
|
||||
)
|
||||
);
|
||||
|
||||
named!(keys_and_values_aggregator<&str, Vec<(&str,&str)> >, many0!(key_value));
|
||||
|
||||
fn keys_and_values(input:&str) -> IResult<&str, HashMap<&str, &str> > {
|
||||
let mut h: HashMap<&str, &str> = HashMap::new();
|
||||
|
||||
match keys_and_values_aggregator(input) {
|
||||
IResult::Done(i,tuple_vec) => {
|
||||
for &(k,v) in &tuple_vec {
|
||||
h.insert(k, v);
|
||||
}
|
||||
IResult::Done(i, h)
|
||||
},
|
||||
IResult::Incomplete(a) => IResult::Incomplete(a),
|
||||
IResult::Error(a) => IResult::Error(a)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
named!(category_and_keys<&str,(&str,HashMap<&str,&str>)>,
|
||||
pair!(category, keys_and_values)
|
||||
);
|
||||
|
||||
named!(categories_aggregator<&str, Vec<(&str, HashMap<&str,&str>)> >, many0!(category_and_keys));
|
||||
|
||||
fn categories(input: &str) -> IResult<&str, HashMap<&str, HashMap<&str, &str> > > {
|
||||
let mut h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
|
||||
|
||||
match categories_aggregator(input) {
|
||||
IResult::Done(i,tuple_vec) => {
|
||||
for &(k,ref v) in &tuple_vec {
|
||||
h.insert(k, v.clone());
|
||||
}
|
||||
IResult::Done(i, h)
|
||||
},
|
||||
IResult::Incomplete(a) => IResult::Incomplete(a),
|
||||
IResult::Error(a) => IResult::Error(a)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn parse_category_test() {
|
||||
let ini_file = "[category]
|
||||
|
||||
parameter=value
|
||||
key = value2";
|
||||
|
||||
let ini_without_category = "parameter=value
|
||||
key = value2";
|
||||
|
||||
let res = category(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, o) => println!("i: {} | o: {:?}", i, o),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
assert_eq!(res, IResult::Done(ini_without_category, "category"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_key_value_test() {
|
||||
let ini_file = "parameter=value
|
||||
key = value2";
|
||||
|
||||
let ini_without_key_value = "key = value2";
|
||||
|
||||
let res = key_value(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, (o1, o2)) => println!("i: {} | o: ({:?},{:?})", i, o1, o2),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_key_value_with_space_test() {
|
||||
let ini_file = "parameter = value
|
||||
key = value2";
|
||||
|
||||
let ini_without_key_value = "key = value2";
|
||||
|
||||
let res = key_value(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, (o1, o2)) => println!("i: {} | o: ({:?},{:?})", i, o1, o2),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_key_value_with_comment_test() {
|
||||
let ini_file = "parameter=value;abc
|
||||
key = value2";
|
||||
|
||||
let ini_without_key_value = "key = value2";
|
||||
|
||||
let res = key_value(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, (o1, o2)) => println!("i: {} | o: ({:?},{:?})", i, o1, o2),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_multiple_keys_and_values_test() {
|
||||
let ini_file = "parameter=value;abc
|
||||
|
||||
key = value2
|
||||
|
||||
[category]";
|
||||
|
||||
let ini_without_key_value = "[category]";
|
||||
|
||||
let res = keys_and_values(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, ref o) => println!("i: {} | o: {:?}", i, o),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
let mut expected: HashMap<&str, &str> = HashMap::new();
|
||||
expected.insert("parameter", "value");
|
||||
expected.insert("key", "value2");
|
||||
assert_eq!(res, IResult::Done(ini_without_key_value, expected));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_category_then_multiple_keys_and_values_test() {
|
||||
//FIXME: there can be an empty line or a comment line after a category
|
||||
let ini_file = "[abcd]
|
||||
parameter=value;abc
|
||||
|
||||
key = value2
|
||||
|
||||
[category]";
|
||||
|
||||
let ini_after_parser = "[category]";
|
||||
|
||||
let res = category_and_keys(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, ref o) => println!("i: {} | o: {:?}", i, o),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
let mut expected_h: HashMap<&str, &str> = HashMap::new();
|
||||
expected_h.insert("parameter", "value");
|
||||
expected_h.insert("key", "value2");
|
||||
assert_eq!(res, IResult::Done(ini_after_parser, ("abcd", expected_h)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_multiple_categories_test() {
|
||||
let ini_file = "[abcd]
|
||||
|
||||
parameter=value;abc
|
||||
|
||||
key = value2
|
||||
|
||||
[category]
|
||||
parameter3=value3
|
||||
key4 = value4
|
||||
";
|
||||
|
||||
let res = categories(ini_file);
|
||||
//println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, ref o) => println!("i: {} | o: {:?}", i, o),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
let mut expected_1: HashMap<&str, &str> = HashMap::new();
|
||||
expected_1.insert("parameter", "value");
|
||||
expected_1.insert("key", "value2");
|
||||
let mut expected_2: HashMap<&str, &str> = HashMap::new();
|
||||
expected_2.insert("parameter3", "value3");
|
||||
expected_2.insert("key4", "value4");
|
||||
let mut expected_h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
|
||||
expected_h.insert("abcd", expected_1);
|
||||
expected_h.insert("category", expected_2);
|
||||
assert_eq!(res, IResult::Done("", expected_h));
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
//#![feature(trace_macros)]
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use nom::{IResult,Needed,HexDisplay,space,digit,be_u16};
|
||||
use std::str;
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Range {
|
||||
start: char,
|
||||
end: char
|
||||
}
|
||||
|
||||
pub fn take_char(input: &[u8]) -> IResult<&[u8], char> {
|
||||
if input.len() > 0 {
|
||||
IResult::Done(&input[1..], input[0] as char)
|
||||
} else {
|
||||
IResult::Incomplete(Needed::Size(1))
|
||||
}
|
||||
}
|
||||
|
||||
//trace_macros!(true);
|
||||
|
||||
#[allow(dead_code)]
|
||||
named!(range<&[u8], Range>,
|
||||
alt!(
|
||||
chain!(
|
||||
start: take_char ~
|
||||
tag!("-") ~
|
||||
end: take_char,
|
||||
|| {
|
||||
Range {
|
||||
start: start,
|
||||
end: end,
|
||||
}
|
||||
}
|
||||
) |
|
||||
map!(
|
||||
take_char,
|
||||
|c| {
|
||||
Range {
|
||||
start: c,
|
||||
end: c,
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
#[allow(dead_code)]
|
||||
named!(literal<&[u8], Vec<char> >,
|
||||
map!(
|
||||
many1!(take_char),
|
||||
|cs| {
|
||||
cs
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn issue_58() {
|
||||
range(&b"abcd"[..]);
|
||||
literal(&b"abcd"[..]);
|
||||
}
|
||||
|
||||
//trace_macros!(false);
|
||||
|
||||
named!(parse_ints< Vec<i32> >, many0!(spaces_or_int));
|
||||
|
||||
fn spaces_or_int(input: &[u8]) -> IResult<&[u8], i32>{
|
||||
println!("{}", input.to_hex(8));
|
||||
chain!(input,
|
||||
opt!(space) ~
|
||||
x: digit,
|
||||
|| {
|
||||
println!("x: {:?}", x);
|
||||
let result = str::from_utf8(x).unwrap();
|
||||
println!("Result: {}", result);
|
||||
println!("int is empty?: {}", x.is_empty());
|
||||
match result.parse(){
|
||||
Ok(i) => i,
|
||||
Err(_) => panic!("UH OH! NOT A DIGIT!")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_142(){
|
||||
let subject = parse_ints(&b"12 34 5689"[..]);
|
||||
let expected = IResult::Done(&b""[..], vec![12, 34, 5689]);
|
||||
assert_eq!(subject, expected);
|
||||
|
||||
let subject = parse_ints(&b"12 34 5689 "[..]);
|
||||
let expected = IResult::Done(&b" "[..], vec![12, 34, 5689]);
|
||||
assert_eq!(subject, expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn usize_length_bytes_issue(){
|
||||
length_bytes!(b"012346", be_u16);
|
||||
}
|
||||
|
||||
/*
|
||||
DOES NOT COMPILE
|
||||
#[test]
|
||||
fn issue_152() {
|
||||
named!(take4, take!(4));
|
||||
named!(xyz, tag!("XYZ"));
|
||||
named!(abc, tag!("abc"));
|
||||
|
||||
|
||||
named!(sw,
|
||||
switch!(take4,
|
||||
b"abcd" => xyz |
|
||||
b"efgh" => abc
|
||||
)
|
||||
);
|
||||
}
|
||||
*/
|
||||
|
||||
#[test]
|
||||
fn take_till_issue() {
|
||||
named!(nothing,
|
||||
take_till!(call!(|_| true))
|
||||
);
|
||||
|
||||
assert_eq!(nothing(b""), IResult::Done(&b""[..], &b""[..]));
|
||||
assert_eq!(nothing(b"abc"), IResult::Done(&b"abc"[..], &b""[..]));
|
||||
}
|
|
@ -1,531 +0,0 @@
|
|||
#![cfg(feature = "stream")]
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use nom::{HexDisplay,Needed,IResult,be_u16,be_u32,be_u64,be_f32,ErrorKind};
|
||||
use nom::{Consumer,ConsumerState,Move,Input,Producer,FileProducer,FileProducerState};
|
||||
use nom::IResult::*;
|
||||
use nom::Err::*;
|
||||
|
||||
use std::str;
|
||||
use std::io::SeekFrom;
|
||||
|
||||
fn mp4_box(input:&[u8]) -> IResult<&[u8], &[u8]> {
|
||||
match be_u32(input) {
|
||||
Done(i, offset) => {
|
||||
let sz: usize = offset as usize;
|
||||
if i.len() >= sz - 4 {
|
||||
Done(&i[(sz-4)..], &i[0..(sz-4)])
|
||||
} else {
|
||||
Incomplete(Needed::Size(offset as usize + 4))
|
||||
}
|
||||
}
|
||||
Error(e) => Error(e),
|
||||
Incomplete(e) => Incomplete(e)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq,Eq,Debug)]
|
||||
struct FileType<'a> {
|
||||
major_brand: &'a str,
|
||||
major_brand_version: &'a [u8],
|
||||
compatible_brands: Vec<&'a str>
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct Mvhd32 {
|
||||
version_flags: u32, // actually:
|
||||
// version: u8,
|
||||
// flags: u24 // 3 bytes
|
||||
created_date: u32,
|
||||
modified_date: u32,
|
||||
scale: u32,
|
||||
duration: u32,
|
||||
speed: f32,
|
||||
volume: u16, // actually a 2 bytes decimal
|
||||
/* 10 bytes reserved */
|
||||
scaleA: f32,
|
||||
rotateB: f32,
|
||||
angleU: f32,
|
||||
rotateC: f32,
|
||||
scaleD: f32,
|
||||
angleV: f32,
|
||||
positionX: f32,
|
||||
positionY: f32,
|
||||
scaleW: f32,
|
||||
preview: u64,
|
||||
poster: u32,
|
||||
selection: u64,
|
||||
current_time: u32,
|
||||
track_id: u32
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct Mvhd64 {
|
||||
version_flags: u32, // actually:
|
||||
// version: u8,
|
||||
// flags: u24 // 3 bytes
|
||||
created_date: u64,
|
||||
modified_date: u64,
|
||||
scale: u32,
|
||||
duration: u64,
|
||||
speed: f32,
|
||||
volume: u16, // actually a 2 bytes decimal
|
||||
/* 10 bytes reserved */
|
||||
scaleA: f32,
|
||||
rotateB: f32,
|
||||
angleU: f32,
|
||||
rotateC: f32,
|
||||
scaleD: f32,
|
||||
angleV: f32,
|
||||
positionX: f32,
|
||||
positionY: f32,
|
||||
scaleW: f32,
|
||||
preview: u64,
|
||||
poster: u32,
|
||||
selection: u64,
|
||||
current_time: u32,
|
||||
track_id: u32
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
named!(mvhd32 <&[u8], MvhdBox>,
|
||||
chain!(
|
||||
version_flags: be_u32 ~
|
||||
created_date: be_u32 ~
|
||||
modified_date: be_u32 ~
|
||||
scale: be_u32 ~
|
||||
duration: be_u32 ~
|
||||
speed: be_f32 ~
|
||||
volume: be_u16 ~ // actually a 2 bytes decimal
|
||||
take!(10) ~
|
||||
scale_a: be_f32 ~
|
||||
rotate_b: be_f32 ~
|
||||
angle_u: be_f32 ~
|
||||
rotate_c: be_f32 ~
|
||||
scale_d: be_f32 ~
|
||||
angle_v: be_f32 ~
|
||||
position_x: be_f32 ~
|
||||
position_y: be_f32 ~
|
||||
scale_w: be_f32 ~
|
||||
preview: be_u64 ~
|
||||
poster: be_u32 ~
|
||||
selection: be_u64 ~
|
||||
current_time: be_u32 ~
|
||||
track_id: be_u32,
|
||||
||{
|
||||
MvhdBox::M32(Mvhd32 {
|
||||
version_flags: version_flags,
|
||||
created_date: created_date,
|
||||
modified_date: modified_date,
|
||||
scale: scale,
|
||||
duration: duration,
|
||||
speed: speed,
|
||||
volume: volume,
|
||||
scaleA: scale_a,
|
||||
rotateB: rotate_b,
|
||||
angleU: angle_u,
|
||||
rotateC: rotate_c,
|
||||
scaleD: scale_d,
|
||||
angleV: angle_v,
|
||||
positionX: position_x,
|
||||
positionY: position_y,
|
||||
scaleW: scale_w,
|
||||
preview: preview,
|
||||
poster: poster,
|
||||
selection: selection,
|
||||
current_time: current_time,
|
||||
track_id: track_id
|
||||
})
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
named!(mvhd64 <&[u8], MvhdBox>,
|
||||
chain!(
|
||||
version_flags: be_u32 ~
|
||||
created_date: be_u64 ~
|
||||
modified_date: be_u64 ~
|
||||
scale: be_u32 ~
|
||||
duration: be_u64 ~
|
||||
speed: be_f32 ~
|
||||
volume: be_u16 ~ // actually a 2 bytes decimal
|
||||
take!(10) ~
|
||||
scale_a: be_f32 ~
|
||||
rotate_b: be_f32 ~
|
||||
angle_u: be_f32 ~
|
||||
rotate_c: be_f32 ~
|
||||
scale_d: be_f32 ~
|
||||
angle_v: be_f32 ~
|
||||
position_x: be_f32 ~
|
||||
position_y: be_f32 ~
|
||||
scale_w: be_f32 ~
|
||||
preview: be_u64 ~
|
||||
poster: be_u32 ~
|
||||
selection: be_u64 ~
|
||||
current_time: be_u32 ~
|
||||
track_id: be_u32,
|
||||
||{
|
||||
MvhdBox::M64(Mvhd64 {
|
||||
version_flags: version_flags,
|
||||
created_date: created_date,
|
||||
modified_date: modified_date,
|
||||
scale: scale,
|
||||
duration: duration,
|
||||
speed: speed,
|
||||
volume: volume,
|
||||
scaleA: scale_a,
|
||||
rotateB: rotate_b,
|
||||
angleU: angle_u,
|
||||
rotateC: rotate_c,
|
||||
scaleD: scale_d,
|
||||
angleV: angle_v,
|
||||
positionX: position_x,
|
||||
positionY: position_y,
|
||||
scaleW: scale_w,
|
||||
preview: preview,
|
||||
poster: poster,
|
||||
selection: selection,
|
||||
current_time: current_time,
|
||||
track_id: track_id
|
||||
})
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[derive(Debug,Clone)]
|
||||
pub enum MvhdBox {
|
||||
M32(Mvhd32),
|
||||
M64(Mvhd64)
|
||||
}
|
||||
|
||||
#[derive(Debug,Clone)]
|
||||
pub enum MoovBox {
|
||||
Mdra,
|
||||
Dref,
|
||||
Cmov,
|
||||
Rmra,
|
||||
Iods,
|
||||
Mvhd(MvhdBox),
|
||||
Clip,
|
||||
Trak,
|
||||
Udta
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum MP4BoxType {
|
||||
Ftyp,
|
||||
Moov,
|
||||
Mdat,
|
||||
Free,
|
||||
Skip,
|
||||
Wide,
|
||||
Mdra,
|
||||
Dref,
|
||||
Cmov,
|
||||
Rmra,
|
||||
Iods,
|
||||
Mvhd,
|
||||
Clip,
|
||||
Trak,
|
||||
Udta,
|
||||
Unknown
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MP4BoxHeader {
|
||||
length: u32,
|
||||
tag: MP4BoxType
|
||||
}
|
||||
|
||||
named!(brand_name<&[u8],&str>, map_res!(take!(4), str::from_utf8));
|
||||
|
||||
named!(filetype_parser<&[u8], FileType>,
|
||||
chain!(
|
||||
m: brand_name ~
|
||||
v: take!(4) ~
|
||||
c: many0!(brand_name) ,
|
||||
||{ FileType{ major_brand: m, major_brand_version:v, compatible_brands: c } }
|
||||
)
|
||||
);
|
||||
|
||||
fn mvhd_box(input:&[u8]) -> IResult<&[u8],MvhdBox> {
|
||||
let res = if input.len() < 100 {
|
||||
Incomplete(Needed::Size(100))
|
||||
} else if input.len() == 100 {
|
||||
mvhd32(input)
|
||||
} else if input.len() == 112 {
|
||||
mvhd64(input)
|
||||
} else {
|
||||
Error(Position(ErrorKind::Custom(32),input))
|
||||
};
|
||||
println!("res: {:?}", res);
|
||||
res
|
||||
}
|
||||
|
||||
fn unknown_box_type(input:&[u8]) -> IResult<&[u8], MP4BoxType> {
|
||||
Done(input, MP4BoxType::Unknown)
|
||||
}
|
||||
|
||||
//named!(box_type<&[u8], MP4BoxType>,
|
||||
fn box_type(input: &[u8]) -> IResult<&[u8], MP4BoxType, u32> {
|
||||
alt!(input,
|
||||
tag!("ftyp") => { |_| MP4BoxType::Ftyp } |
|
||||
tag!("moov") => { |_| MP4BoxType::Moov } |
|
||||
tag!("mdat") => { |_| MP4BoxType::Mdat } |
|
||||
tag!("free") => { |_| MP4BoxType::Free } |
|
||||
tag!("skip") => { |_| MP4BoxType::Skip } |
|
||||
tag!("wide") => { |_| MP4BoxType::Wide } |
|
||||
unknown_box_type
|
||||
)
|
||||
}
|
||||
|
||||
// warning, an alt combinator with 9 branches containing a tag combinator
|
||||
// can make the compilation very slow. Use functions as sub parsers,
|
||||
// or split into multiple alt! parsers if it gets slow
|
||||
named!(moov_type<&[u8], MP4BoxType>,
|
||||
alt!(
|
||||
tag!("mdra") => { |_| MP4BoxType::Mdra } |
|
||||
tag!("dref") => { |_| MP4BoxType::Dref } |
|
||||
tag!("cmov") => { |_| MP4BoxType::Cmov } |
|
||||
tag!("rmra") => { |_| MP4BoxType::Rmra } |
|
||||
tag!("iods") => { |_| MP4BoxType::Iods } |
|
||||
tag!("mvhd") => { |_| MP4BoxType::Mvhd } |
|
||||
tag!("clip") => { |_| MP4BoxType::Clip } |
|
||||
tag!("trak") => { |_| MP4BoxType::Trak } |
|
||||
tag!("udta") => { |_| MP4BoxType::Udta }
|
||||
)
|
||||
);
|
||||
|
||||
named!(box_header<&[u8],MP4BoxHeader>,
|
||||
chain!(
|
||||
length: be_u32 ~
|
||||
tag: box_type ,
|
||||
|| { MP4BoxHeader{ length: length, tag: tag} }
|
||||
)
|
||||
);
|
||||
|
||||
named!(moov_header<&[u8],MP4BoxHeader>,
|
||||
chain!(
|
||||
length: be_u32 ~
|
||||
tag: moov_type ,
|
||||
|| { MP4BoxHeader{ length: length, tag: tag} }
|
||||
)
|
||||
);
|
||||
|
||||
#[derive(Debug,PartialEq,Eq)]
|
||||
enum MP4State {
|
||||
Main,
|
||||
Moov,
|
||||
Mvhd(usize)
|
||||
}
|
||||
|
||||
pub struct MP4Consumer {
|
||||
state: MP4State,
|
||||
moov_bytes: usize,
|
||||
c_state: ConsumerState<(), (), Move>
|
||||
}
|
||||
|
||||
impl MP4Consumer {
|
||||
fn new() -> MP4Consumer {
|
||||
MP4Consumer { state: MP4State::Main, moov_bytes: 0, c_state: ConsumerState::Continue(Move::Consume(0)) }
|
||||
}
|
||||
|
||||
fn consume_main(&mut self, input: Input<&[u8]>) -> ConsumerState<(), (), Move> {
|
||||
//println!("\nparsing box header:\n{}", input.to_hex(8));
|
||||
match input {
|
||||
Input::Eof(None) => ConsumerState::Done(Move::Consume(0), ()),
|
||||
Input::Empty => ConsumerState::Continue(Move::Consume(0)),
|
||||
Input::Element(sl) | Input::Eof(Some(sl)) => {
|
||||
match box_header(sl) {
|
||||
Done(i, header) => {
|
||||
match header.tag {
|
||||
MP4BoxType::Ftyp => {
|
||||
println!("-> FTYP");
|
||||
match filetype_parser(&i[0..(header.length as usize - 8)]) {
|
||||
Done(rest, filetype_header) => {
|
||||
println!("filetype header: {:?}", filetype_header);
|
||||
//return ConsumerState::Await(header.length as usize, header.length as usize - 8);
|
||||
return ConsumerState::Continue(Move::Consume(sl.offset(rest)));
|
||||
}
|
||||
Error(a) => {
|
||||
println!("ftyp parsing error: {:?}", a);
|
||||
assert!(false);
|
||||
return ConsumerState::Error(());
|
||||
},
|
||||
Incomplete(n) => {
|
||||
println!("ftyp incomplete -> await: {}", sl.len());
|
||||
return ConsumerState::Continue(Move::Await(n));
|
||||
//return ConsumerState::Await(0, input.len() + 100);
|
||||
}
|
||||
}
|
||||
},
|
||||
MP4BoxType::Moov => {
|
||||
println!("-> MOOV");
|
||||
self.state = MP4State::Moov;
|
||||
self.moov_bytes = header.length as usize - 8;
|
||||
return ConsumerState::Continue(Move::Consume(sl.offset(i)));
|
||||
},
|
||||
MP4BoxType::Mdat => println!("-> MDAT"),
|
||||
MP4BoxType::Free => println!("-> FREE"),
|
||||
MP4BoxType::Skip => println!("-> SKIP"),
|
||||
MP4BoxType::Wide => println!("-> WIDE"),
|
||||
MP4BoxType::Unknown => {
|
||||
println!("-> UNKNOWN");
|
||||
println!("bytes:\n{}", (sl).to_hex(8));
|
||||
//return ConsumerState::Continue(Move::Consume(sl.offset(i)));
|
||||
},
|
||||
_ => { println!("invalid"); return ConsumerState::Error(())}
|
||||
}
|
||||
return ConsumerState::Continue(Move::Seek(SeekFrom::Current((header.length) as i64)))
|
||||
},
|
||||
Error(a) => {
|
||||
println!("mp4 parsing error: {:?}", a);
|
||||
assert!(false);
|
||||
return ConsumerState::Error(());
|
||||
},
|
||||
Incomplete(i) => {
|
||||
// FIXME: incomplete should send the required size
|
||||
println!("mp4 incomplete -> await: {}", sl.len());
|
||||
return ConsumerState::Continue(Move::Await(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn consume_moov(&mut self, input: Input<&[u8]>) -> ConsumerState<(), (), Move> {
|
||||
//println!("\nparsing moov box(remaining {} bytes):\n{}", self.moov_bytes, input.to_hex(8));
|
||||
match input {
|
||||
Input::Eof(None) => return ConsumerState::Error(()),
|
||||
Input::Empty => return ConsumerState::Continue(Move::Consume(0)),
|
||||
Input::Element(sl) | Input::Eof(Some(sl)) => {
|
||||
if self.moov_bytes == 0 {
|
||||
//println!("finished parsing moov atom, continuing with main parser");
|
||||
self.state = MP4State::Main;
|
||||
return ConsumerState::Continue(Move::Consume(0));
|
||||
}
|
||||
match moov_header(sl) {
|
||||
Done(i, header) => {
|
||||
match header.tag {
|
||||
MP4BoxType::Mvhd => {
|
||||
println!("-> MVHD");
|
||||
self.state = MP4State::Mvhd(header.length as usize - 8);
|
||||
// TODO: check for overflow here
|
||||
self.moov_bytes = self.moov_bytes - (sl.len() - i.len());
|
||||
println!("remaining moov_bytes: {}", self.moov_bytes);
|
||||
return ConsumerState::Continue(Move::Consume(sl.offset(i)));
|
||||
},
|
||||
MP4BoxType::Wide => println!("-> WIDE"),
|
||||
MP4BoxType::Mdra => println!("-> MDRA"),
|
||||
MP4BoxType::Dref => println!("-> DREF"),
|
||||
MP4BoxType::Cmov => println!("-> CMOV"),
|
||||
MP4BoxType::Rmra => println!("-> RMRA"),
|
||||
MP4BoxType::Iods => println!("-> IODS"),
|
||||
MP4BoxType::Clip => println!("-> CLIP"),
|
||||
MP4BoxType::Trak => println!("-> TRAK"),
|
||||
MP4BoxType::Udta => println!("-> UDTA"),
|
||||
MP4BoxType::Unknown => println!("-> MOOV UNKNOWN"),
|
||||
_ => { println!("invalid header here: {:?}", header.tag); return ConsumerState::Error(());}
|
||||
};
|
||||
// TODO: check for overflow here
|
||||
self.moov_bytes = self.moov_bytes - header.length as usize;
|
||||
println!("remaining moov_bytes: {}", self.moov_bytes);
|
||||
return ConsumerState::Continue(Move::Seek(SeekFrom::Current((header.length) as i64)))
|
||||
},
|
||||
Error(a) => {
|
||||
println!("moov parsing error: {:?}", a);
|
||||
println!("data:\n{}", sl.to_hex(8));
|
||||
assert!(false);
|
||||
return ConsumerState::Error(());
|
||||
},
|
||||
Incomplete(i) => {
|
||||
println!("moov incomplete -> await: {}", sl.len());
|
||||
return ConsumerState::Continue(Move::Await(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
consumer_from_parser!(MvhdConsumer<MvhdBox>, mvhd_box);
|
||||
|
||||
impl<'a> Consumer<&'a[u8], (), (), Move> for MP4Consumer {
|
||||
fn handle(&mut self, input: Input<&[u8]>) -> &ConsumerState<(), (), Move> {
|
||||
match self.state {
|
||||
MP4State::Main => {
|
||||
self.c_state = self.consume_main(input);
|
||||
},
|
||||
MP4State::Moov => {
|
||||
self.c_state = self.consume_moov(input);
|
||||
},
|
||||
MP4State::Mvhd(sz) => {
|
||||
match input {
|
||||
Input::Eof(None) => self.c_state = ConsumerState::Error(()),
|
||||
Input::Empty => self.c_state = ConsumerState::Continue(Move::Consume(0)),
|
||||
Input::Element(sl) | Input::Eof(Some(sl)) => {
|
||||
let mut c = MvhdConsumer{ state:ConsumerState::Continue(Move::Consume(0)) };
|
||||
self.c_state = c.handle(Input::Element(&sl[..sz])).flat_map(|m, _| {
|
||||
self.state = MP4State::Moov;
|
||||
ConsumerState::Continue(m)
|
||||
});
|
||||
println!("found mvhd?: {:?}", c.state());
|
||||
match self.c_state {
|
||||
ConsumerState::Continue(Move::Consume(sz)) => self.moov_bytes = self.moov_bytes - sz,
|
||||
ConsumerState::Continue(Move::Seek(SeekFrom::Current(sz))) => self.moov_bytes = self.moov_bytes - (sz as usize),
|
||||
_ => ()
|
||||
};
|
||||
println!("remaining moov_bytes: {}", self.moov_bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
&self.c_state
|
||||
}
|
||||
|
||||
fn state(&self) -> &ConsumerState<(), (), Move> {
|
||||
&self.c_state
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_must_use)]
|
||||
fn explore_mp4_file(filename: &str) {
|
||||
let mut p = FileProducer::new(filename, 400).unwrap();
|
||||
let mut c = MP4Consumer{state: MP4State::Main, moov_bytes: 0, c_state: ConsumerState::Continue(Move::Consume(0))};
|
||||
//c.run(&mut p);
|
||||
while let &ConsumerState::Continue(mv) = p.apply(&mut c) {
|
||||
println!("move: {:?}", mv);
|
||||
}
|
||||
println!("last consumer state: {:?} | last state: {:?}", c.c_state, c.state);
|
||||
|
||||
if let ConsumerState::Done(Move::Consume(0), ()) = c.c_state {
|
||||
println!("consumer state ok");
|
||||
} else {
|
||||
assert!(false, "consumer should have reached Done state");
|
||||
}
|
||||
assert_eq!(c.state, MP4State::Main);
|
||||
assert_eq!(p.state(), FileProducerState::Eof);
|
||||
//assert!(false);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn small_test() {
|
||||
explore_mp4_file("assets/small.mp4");
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn big_bunny_test() {
|
||||
explore_mp4_file("assets/bigbuckbunny.mp4");
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,160 +0,0 @@
|
|||
#![cfg(feature = "stream")]
|
||||
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use nom::{Producer,Consumer,ConsumerState,Input,Move,MemProducer,IResult,HexDisplay};
|
||||
|
||||
#[derive(PartialEq,Eq,Debug)]
|
||||
enum State {
|
||||
Beginning,
|
||||
Middle,
|
||||
End,
|
||||
Done,
|
||||
Error
|
||||
}
|
||||
|
||||
struct TestConsumer {
|
||||
state: State,
|
||||
c_state: ConsumerState<usize,(),Move>,
|
||||
counter: usize,
|
||||
}
|
||||
|
||||
named!(om_parser, tag!("om"));
|
||||
named!(nomnom_parser<&[u8],Vec<&[u8]> >, many1!(tag!("nom")));
|
||||
named!(end_parser, tag!("kthxbye"));
|
||||
|
||||
impl<'a> Consumer<&'a[u8], usize, (), Move> for TestConsumer {
|
||||
fn state(&self) -> &ConsumerState<usize,(),Move> {
|
||||
&self.c_state
|
||||
}
|
||||
|
||||
fn handle(&mut self, input: Input<&'a [u8]>) -> &ConsumerState<usize,(),Move> {
|
||||
match self.state {
|
||||
State::Beginning => {
|
||||
match input {
|
||||
Input::Empty | Input::Eof(None) => {
|
||||
self.state = State::Error;
|
||||
self.c_state = ConsumerState::Error(());
|
||||
},
|
||||
Input::Element(sl) | Input::Eof(Some(sl)) => {
|
||||
match om_parser(sl) {
|
||||
IResult::Error(_) => {
|
||||
self.state = State::Error;
|
||||
self.c_state = ConsumerState::Error(());
|
||||
},
|
||||
IResult::Incomplete(n) => {
|
||||
self.c_state = ConsumerState::Continue(Move::Await(n));
|
||||
},
|
||||
IResult::Done(i,_) => {
|
||||
self.state = State::Middle;
|
||||
self.c_state = ConsumerState::Continue(Move::Consume(sl.offset(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
State::Middle => {
|
||||
match input {
|
||||
Input::Empty | Input::Eof(None) => {
|
||||
self.state = State::Error;
|
||||
self.c_state = ConsumerState::Error(());
|
||||
},
|
||||
Input::Element(sl) | Input::Eof(Some(sl)) => {
|
||||
match nomnom_parser(sl) {
|
||||
IResult::Error(_) => {
|
||||
self.state = State::End;
|
||||
self.c_state = ConsumerState::Continue(Move::Consume(0));
|
||||
},
|
||||
IResult::Incomplete(n) => {
|
||||
println!("Middle got Incomplete({:?})", n);
|
||||
self.c_state = ConsumerState::Continue(Move::Await(n));
|
||||
},
|
||||
IResult::Done(i,noms_vec) => {
|
||||
self.counter = self.counter + noms_vec.len();
|
||||
self.state = State::Middle;
|
||||
self.c_state = ConsumerState::Continue(Move::Consume(sl.offset(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
State::End => {
|
||||
match input {
|
||||
Input::Empty | Input::Eof(None) => {
|
||||
self.state = State::Error;
|
||||
self.c_state = ConsumerState::Error(());
|
||||
},
|
||||
Input::Element(sl) | Input::Eof(Some(sl)) => {
|
||||
match end_parser(sl) {
|
||||
IResult::Error(_) => {
|
||||
self.state = State::Error;
|
||||
self.c_state = ConsumerState::Error(());
|
||||
},
|
||||
IResult::Incomplete(n) => {
|
||||
self.c_state = ConsumerState::Continue(Move::Await(n));
|
||||
},
|
||||
IResult::Done(i,_) => {
|
||||
self.state = State::Done;
|
||||
self.c_state = ConsumerState::Done(Move::Consume(sl.offset(i)), self.counter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
State::Done | State::Error => {
|
||||
// this should not be called
|
||||
self.state = State::Error;
|
||||
self.c_state = ConsumerState::Error(())
|
||||
}
|
||||
};
|
||||
&self.c_state
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nom1() {
|
||||
let mut p = MemProducer::new(&b"omnomkthxbye"[..], 8);
|
||||
let mut c = TestConsumer{state: State::Beginning, counter: 0, c_state: ConsumerState::Continue(Move::Consume(0))};
|
||||
while let &ConsumerState::Continue(Move::Consume(_)) = p.apply(&mut c) {
|
||||
}
|
||||
|
||||
assert_eq!(c.counter, 1);
|
||||
assert_eq!(c.state, State::Done);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nomnomnom() {
|
||||
let mut p = MemProducer::new(&b"omnomnomnomkthxbye"[..], 9);
|
||||
let mut c = TestConsumer{state: State::Beginning, counter: 0, c_state: ConsumerState::Continue(Move::Consume(0))};
|
||||
while let &ConsumerState::Continue(_) = p.apply(&mut c) {
|
||||
}
|
||||
|
||||
assert_eq!(c.counter, 3);
|
||||
assert_eq!(c.state, State::Done);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_nomnom() {
|
||||
let mut p = MemProducer::new(&b"omkthxbye"[..], 8);
|
||||
let mut c = TestConsumer{state: State::Beginning, counter: 0, c_state: ConsumerState::Continue(Move::Consume(0))};
|
||||
while let &ConsumerState::Continue(_) = p.apply(&mut c) {
|
||||
}
|
||||
|
||||
assert_eq!(c.counter, 0);
|
||||
assert_eq!(c.state, State::Done);
|
||||
}
|
||||
|
||||
/*
|
||||
#[test]
|
||||
fn impolite() {
|
||||
let mut p = MemProducer::new(&b"omnomnomnom"[..], 11);
|
||||
let mut c = TestConsumer{state: State::Beginning, counter: 0, c_state: ConsumerState::Continue(Move::Consume(0))};
|
||||
while let &ConsumerState::Continue(cont) = p.apply(&mut c) {
|
||||
println!("continue {:?}", cont);
|
||||
}
|
||||
|
||||
assert_eq!(c.counter, 3);
|
||||
assert_eq!(c.state, State::End);
|
||||
}
|
||||
*/
|
|
@ -1,44 +0,0 @@
|
|||
#![cfg(feature = "stream")]
|
||||
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use nom::{IResult,Producer,FileProducer,not_line_ending};
|
||||
|
||||
use std::str;
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[test]
|
||||
#[allow(unused_must_use)]
|
||||
fn tag() {
|
||||
FileProducer::new("assets/links.txt", 20).map(|producer: FileProducer| {
|
||||
let mut p = producer;
|
||||
p.refill();
|
||||
|
||||
consumer_from_parser!(PrintConsumer<()>, flat_map!(map_res!(tag!("https!"), str::from_utf8), print));
|
||||
let mut cs = PrintConsumer::new();
|
||||
for _ in 1..4 {
|
||||
p.apply(&mut cs);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn print<T: Debug>(input: T) -> IResult<T,()> {
|
||||
println!("{:?}", input);
|
||||
IResult::Done(input, ())
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn is_not() {
|
||||
//is_not!(foo b"\r\n");
|
||||
named!(foo<&[u8],&[u8]>, is_not!(&b"\r\n"[..]));
|
||||
let a = &b"ab12cd\nefgh"[..];
|
||||
assert_eq!(foo(a), IResult::Done(&b"\nefgh"[..], &b"ab12cd"[..]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exported_public_method_defined_by_macro() {
|
||||
let a = &b"ab12cd\nefgh"[..];
|
||||
assert_eq!(not_line_ending(a), IResult::Done(&b"\nefgh"[..], &b"ab12cd"[..]));
|
||||
}
|
|
@ -1,220 +0,0 @@
|
|||
//! Bit level parsers and combinators
|
||||
//!
|
||||
//! Bit parsing is handled by tweaking the input in most macros.
|
||||
//! In byte level parsing, the input is generally a `&[u8]` passed from combinator
|
||||
//! to combinator until the slices are manipulated.
|
||||
//!
|
||||
//! Bit parsers take a `(&[u8], usize)` as input. The first part of the tuple is an byte slice,
|
||||
//! the second part is a bit offset in the first byte of the slice.
|
||||
//!
|
||||
//! By passing a pair like this, we can leverage most of the combinators, and avoid
|
||||
//! transforming the whole slice to a vector of booleans. This should make it easy
|
||||
//! to see a byte slice as a bit stream, and parse code points of arbitrary bit length.
|
||||
|
||||
|
||||
/// `bits!( parser ) => ( &[u8], (&[u8], usize) -> IResult<(&[u8], usize), T> ) -> IResult<&[u8], T>`
|
||||
/// transforms its byte slice input into a bit stream for the underlying parsers
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::Done;
|
||||
/// # fn main() {
|
||||
/// named!( take_3_bits<u8>, bits!( take_bits!( u8, 3 ) ) );
|
||||
///
|
||||
/// let input = vec![0b10101010, 0b11110000, 0b00110011];
|
||||
/// let sl = &input[..];
|
||||
///
|
||||
/// assert_eq!(take_3_bits( sl ), Done(&sl[1..], 5) );
|
||||
/// # }
|
||||
#[macro_export]
|
||||
macro_rules! bits (
|
||||
($i:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
bits_impl!($i, $submac!($($args)*));
|
||||
);
|
||||
($i:expr, $f:expr) => (
|
||||
bits_impl!($i, call!($f));
|
||||
);
|
||||
);
|
||||
|
||||
/// Internal parser, do not use directly
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! bits_impl (
|
||||
($i:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
{
|
||||
let input = ($i, 0usize);
|
||||
match $submac!(input, $($args)*) {
|
||||
$crate::IResult::Error(e) => {
|
||||
let err = match e {
|
||||
$crate::Err::Code(k) | $crate::Err::Node(k, _) => $crate::Err::Code(k),
|
||||
$crate::Err::Position(k, (i,b)) | $crate::Err::NodePosition(k, (i,b), _) => {
|
||||
$crate::Err::Position(k, &i[b/8..])
|
||||
}
|
||||
};
|
||||
$crate::IResult::Error(err)
|
||||
}
|
||||
$crate::IResult::Incomplete($crate::Needed::Unknown) => $crate::IResult::Incomplete($crate::Needed::Unknown),
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(i)) => {
|
||||
//println!("bits parser returned Needed::Size({})", i);
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(i / 8 + 1))
|
||||
},
|
||||
$crate::IResult::Done((i, bit_index), o) => {
|
||||
let byte_index = bit_index / 8 + if bit_index % 8 == 0 { 0 } else { 1 } ;
|
||||
//println!("bit index=={} => byte index=={}", bit_index, byte_index);
|
||||
$crate::IResult::Done(&i[byte_index..], o)
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// `take_bits!(type, nb) => ( (&[T], usize), U, usize) -> IResult<(&[T], usize), U>`
|
||||
/// generates a parser consuming the specified number of bits.
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::Done;
|
||||
/// # fn main() {
|
||||
/// named!( take_pair<(u8, u8)>, bits!( pair!( take_bits!( u8, 3 ), take_bits!(u8, 5) ) ) );
|
||||
///
|
||||
/// let input = vec![0b10101010, 0b11110000, 0b00110011];
|
||||
/// let sl = &input[..];
|
||||
///
|
||||
/// assert_eq!(take_pair( sl ), Done(&sl[1..], (5, 10)) );
|
||||
/// assert_eq!(take_pair( &sl[1..] ), Done(&sl[2..], (7, 16)) );
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! take_bits (
|
||||
($i:expr, $t:ty, $count:expr) => (
|
||||
{
|
||||
use std::ops::Div;
|
||||
//println!("taking {} bits from {:?}", $count, $i);
|
||||
let (input, bit_offset) = $i;
|
||||
let res : $crate::IResult<(&[u8],usize), $t> = if $count == 0 {
|
||||
$crate::IResult::Done( (input, bit_offset), 0)
|
||||
} else {
|
||||
let cnt = ($count as usize + bit_offset).div(8);
|
||||
if input.len() * 8 < $count as usize + bit_offset {
|
||||
//println!("returning incomplete: {}", $count as usize + bit_offset);
|
||||
$crate::IResult::Incomplete($crate::Needed::Size($count as usize))
|
||||
} else {
|
||||
let mut acc:$t = 0;
|
||||
let mut offset: usize = bit_offset;
|
||||
let mut remaining: usize = $count;
|
||||
let mut end_offset: usize = 0;
|
||||
|
||||
for byte in input.iter().take(cnt + 1) {
|
||||
if remaining == 0 {
|
||||
break;
|
||||
}
|
||||
let val: $t = if offset == 0 {
|
||||
*byte as $t
|
||||
} else {
|
||||
((*byte << offset) as u8 >> offset) as $t
|
||||
};
|
||||
|
||||
if remaining < 8 - offset {
|
||||
acc += val >> (8 - offset - remaining);
|
||||
end_offset = remaining + offset;
|
||||
break;
|
||||
} else {
|
||||
acc += val << (remaining - (8 - offset));
|
||||
remaining -= 8 - offset;
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
$crate::IResult::Done( (&input[cnt..], end_offset) , acc)
|
||||
}
|
||||
};
|
||||
res
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// matches an integer pattern to a bitstream. The number of bits of the input to compare must be specified
|
||||
#[macro_export]
|
||||
macro_rules! tag_bits (
|
||||
($i:expr, $t:ty, $count:expr, $p: pat) => (
|
||||
{
|
||||
match take_bits!($i, $t, $count) {
|
||||
$crate::IResult::Incomplete(i) => $crate::IResult::Incomplete(i),
|
||||
$crate::IResult::Done(i, o) => {
|
||||
if let $p = o {
|
||||
let res: $crate::IResult<(&[u8],usize),$t> = $crate::IResult::Done(i, o);
|
||||
res
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Position($crate::ErrorKind::TagBits, $i))
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
$crate::IResult::Error($crate::Err::Position($crate::ErrorKind::TagBits, $i))
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use internal::{IResult,Needed,Err};
|
||||
use ErrorKind;
|
||||
|
||||
#[test]
|
||||
fn take_bits() {
|
||||
let input = vec![0b10101010, 0b11110000, 0b00110011];
|
||||
let sl = &input[..];
|
||||
|
||||
assert_eq!(take_bits!( (sl, 0), u8, 0 ), IResult::Done((sl, 0), 0));
|
||||
assert_eq!(take_bits!( (sl, 0), u8, 8 ), IResult::Done((&sl[1..], 0), 170));
|
||||
assert_eq!(take_bits!( (sl, 0), u8, 3 ), IResult::Done((&sl[0..], 3), 5));
|
||||
assert_eq!(take_bits!( (sl, 0), u8, 6 ), IResult::Done((&sl[0..], 6), 42));
|
||||
assert_eq!(take_bits!( (sl, 1), u8, 1 ), IResult::Done((&sl[0..], 2), 0));
|
||||
assert_eq!(take_bits!( (sl, 1), u8, 2 ), IResult::Done((&sl[0..], 3), 1));
|
||||
assert_eq!(take_bits!( (sl, 1), u8, 3 ), IResult::Done((&sl[0..], 4), 2));
|
||||
assert_eq!(take_bits!( (sl, 6), u8, 3 ), IResult::Done((&sl[1..], 1), 5));
|
||||
assert_eq!(take_bits!( (sl, 0), u16, 10 ), IResult::Done((&sl[1..], 2), 683));
|
||||
assert_eq!(take_bits!( (sl, 0), u16, 8 ), IResult::Done((&sl[1..], 0), 170));
|
||||
assert_eq!(take_bits!( (sl, 6), u16, 10 ), IResult::Done((&sl[2..], 0), 752));
|
||||
assert_eq!(take_bits!( (sl, 6), u16, 11 ), IResult::Done((&sl[2..], 1), 1504));
|
||||
assert_eq!(take_bits!( (sl, 0), u32, 20 ), IResult::Done((&sl[2..], 4), 700163));
|
||||
assert_eq!(take_bits!( (sl, 4), u32, 20 ), IResult::Done((&sl[3..], 0), 716851));
|
||||
assert_eq!(take_bits!( (sl, 4), u32, 22 ), IResult::Incomplete(Needed::Size(22)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tag_bits() {
|
||||
let input = vec![0b10101010, 0b11110000, 0b00110011];
|
||||
let sl = &input[..];
|
||||
|
||||
assert_eq!(tag_bits!( (sl, 0), u8, 3, 0b101), IResult::Done((&sl[0..], 3), 5));
|
||||
assert_eq!(tag_bits!( (sl, 0), u8, 4, 0b1010), IResult::Done((&sl[0..], 4), 10));
|
||||
}
|
||||
|
||||
named!(ch<(&[u8],usize),(u8,u8)>,
|
||||
chain!(
|
||||
tag_bits!(u8, 3, 0b101) ~
|
||||
x: take_bits!(u8, 4) ~
|
||||
y: take_bits!(u8, 5) ,
|
||||
|| { (x,y) }
|
||||
)
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn chain_bits() {
|
||||
let input = vec![0b10101010, 0b11110000, 0b00110011];
|
||||
let sl = &input[..];
|
||||
assert_eq!(ch((&input[..],0)), IResult::Done((&sl[1..], 4), (5,15)));
|
||||
assert_eq!(ch((&input[..],4)), IResult::Done((&sl[2..], 0), (7,16)));
|
||||
assert_eq!(ch((&input[..1],0)), IResult::Incomplete(Needed::Size(12)));
|
||||
}
|
||||
|
||||
named!(ch_bytes<(u8,u8)>, bits!(ch));
|
||||
#[test]
|
||||
fn bits_to_bytes() {
|
||||
let input = vec![0b10101010, 0b11110000, 0b00110011];
|
||||
assert_eq!(ch_bytes(&input[..]), IResult::Done(&input[2..], (5,15)));
|
||||
assert_eq!(ch_bytes(&input[..1]), IResult::Incomplete(Needed::Size(2)));
|
||||
assert_eq!(ch_bytes(&input[1..]), IResult::Error(Err::Position(ErrorKind::TagBits, &input[1..])));
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,184 +0,0 @@
|
|||
/// Character level parsers
|
||||
|
||||
use internal::{IResult,Needed,Err};
|
||||
use util::ErrorKind;
|
||||
|
||||
/// matches one of the provided characters
|
||||
#[macro_export]
|
||||
macro_rules! one_of (
|
||||
($i:expr, $inp: expr) => (
|
||||
{
|
||||
if $i.is_empty() {
|
||||
$crate::IResult::Incomplete::<_, _>($crate::Needed::Size(1))
|
||||
} else {
|
||||
#[inline(always)]
|
||||
fn as_bytes<T: $crate::AsBytes>(b: &T) -> &[u8] {
|
||||
b.as_bytes()
|
||||
}
|
||||
|
||||
let expected = $inp;
|
||||
let bytes = as_bytes(&expected);
|
||||
one_of_bytes!($i, bytes)
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! one_of_bytes (
|
||||
($i:expr, $bytes: expr) => (
|
||||
{
|
||||
if $i.is_empty() {
|
||||
$crate::IResult::Incomplete::<_, _>($crate::Needed::Size(1))
|
||||
} else {
|
||||
let mut found = false;
|
||||
|
||||
for &i in $bytes {
|
||||
if i == $i[0] {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
$crate::IResult::Done(&$i[1..], $i[0] as char)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Position($crate::ErrorKind::OneOf, $i))
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// matches anything but the provided characters
|
||||
#[macro_export]
|
||||
macro_rules! none_of (
|
||||
($i:expr, $inp: expr) => (
|
||||
{
|
||||
if $i.is_empty() {
|
||||
$crate::IResult::Incomplete::<_, _>($crate::Needed::Size(1))
|
||||
} else {
|
||||
#[inline(always)]
|
||||
fn as_bytes<T: $crate::AsBytes>(b: &T) -> &[u8] {
|
||||
b.as_bytes()
|
||||
}
|
||||
|
||||
let expected = $inp;
|
||||
let bytes = as_bytes(&expected);
|
||||
none_of_bytes!($i, bytes)
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! none_of_bytes (
|
||||
($i:expr, $bytes: expr) => (
|
||||
{
|
||||
if $i.is_empty() {
|
||||
$crate::IResult::Incomplete::<_, _>($crate::Needed::Size(1))
|
||||
} else {
|
||||
let mut found = false;
|
||||
|
||||
for &i in $bytes {
|
||||
if i == $i[0] {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
$crate::IResult::Done(&$i[1..], $i[0] as char)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Position($crate::ErrorKind::NoneOf, $i))
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// matches one character: `char!(char) => &[u8] -> IResult<&[u8], char>
|
||||
#[macro_export]
|
||||
macro_rules! char (
|
||||
($i:expr, $c: expr) => (
|
||||
{
|
||||
if $i.is_empty() {
|
||||
let res: $crate::IResult<&[u8], char> = $crate::IResult::Incomplete($crate::Needed::Size(1));
|
||||
res
|
||||
} else {
|
||||
if $i[0] == $c as u8 {
|
||||
$crate::IResult::Done(&$i[1..], $i[0] as char)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Position($crate::ErrorKind::Char, $i))
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
named!(pub newline<char>, char!('\n'));
|
||||
|
||||
pub fn crlf(input:&[u8]) -> IResult<&[u8], char> {
|
||||
if input.len() < 2 {
|
||||
IResult::Incomplete(Needed::Size(2))
|
||||
} else {
|
||||
if &input[0..2] == &b"\r\n"[..] {
|
||||
IResult::Done(&input[2..], '\n')
|
||||
} else {
|
||||
IResult::Error(Err::Position(ErrorKind::CrLf, input))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
named!(pub eol<char>, alt!(crlf | newline));
|
||||
named!(pub tab<char>, char!('\t'));
|
||||
|
||||
pub fn anychar(input:&[u8]) -> IResult<&[u8], char> {
|
||||
if input.is_empty() {
|
||||
IResult::Incomplete(Needed::Size(1))
|
||||
} else {
|
||||
IResult::Done(&input[1..], input[0] as char)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use internal::IResult::*;
|
||||
use internal::Err::*;
|
||||
use util::ErrorKind;
|
||||
|
||||
#[test]
|
||||
fn one_of() {
|
||||
named!(f<char>, one_of!("ab"));
|
||||
|
||||
let a = &b"abcd"[..];
|
||||
assert_eq!(f(a), Done(&b"bcd"[..], 'a'));
|
||||
|
||||
let b = &b"cde"[..];
|
||||
assert_eq!(f(b), Error(Position(ErrorKind::OneOf, b)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn none_of() {
|
||||
named!(f<char>, none_of!("ab"));
|
||||
|
||||
let a = &b"abcd"[..];
|
||||
assert_eq!(f(a), Error(Position(ErrorKind::NoneOf, a)));
|
||||
|
||||
let b = &b"cde"[..];
|
||||
assert_eq!(f(b), Done(&b"de"[..], 'c'));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn char() {
|
||||
named!(f<char>, char!('c'));
|
||||
|
||||
let a = &b"abcd"[..];
|
||||
assert_eq!(f(a), Error(Position(ErrorKind::Char, a)));
|
||||
|
||||
let b = &b"cde"[..];
|
||||
assert_eq!(f(b), Done(&b"de"[..], 'c'));
|
||||
}
|
||||
}
|
|
@ -1,347 +0,0 @@
|
|||
//! Basic types to build the parsers
|
||||
|
||||
use self::IResult::*;
|
||||
use self::Needed::*;
|
||||
use util::ErrorKind;
|
||||
|
||||
#[cfg(feature = "core")]
|
||||
use std::prelude::v1::*;
|
||||
use std::boxed::Box;
|
||||
|
||||
/// Contains the error that a parser can return
|
||||
///
|
||||
/// It can represent a linked list of errors, indicating the path taken in the parsing tree, with corresponding position in the input data.
|
||||
/// It depends on P, the input position (for a &[u8] parser, it would be a &[u8]), and E, the custom error type (by default, u32)
|
||||
#[derive(Debug,PartialEq,Eq,Clone)]
|
||||
pub enum Err<P,E=u32>{
|
||||
/// An error code, represented by an ErrorKind, which can contain a custom error code represented by E
|
||||
Code(ErrorKind<E>),
|
||||
/// An error code, and the next error
|
||||
Node(ErrorKind<E>, Box<Err<P,E>>),
|
||||
/// An error code, and the input position
|
||||
Position(ErrorKind<E>, P),
|
||||
/// An error code, the input position and the next error
|
||||
NodePosition(ErrorKind<E>, P, Box<Err<P,E>>)
|
||||
}
|
||||
|
||||
/// Contains information on needed data if a parser returned `Incomplete`
|
||||
#[derive(Debug,PartialEq,Eq,Clone,Copy)]
|
||||
pub enum Needed {
|
||||
/// needs more data, but we do not know how much
|
||||
Unknown,
|
||||
/// contains the required data size
|
||||
Size(usize)
|
||||
}
|
||||
|
||||
impl Needed {
|
||||
pub fn is_known(&self) -> bool {
|
||||
*self != Unknown
|
||||
}
|
||||
|
||||
/// Maps a `Needed` to `Needed` by appling a function to a contained `Size` value.
|
||||
#[inline]
|
||||
pub fn map<F: FnOnce(usize) -> usize>(self, f: F) -> Needed {
|
||||
match self {
|
||||
Unknown => Unknown,
|
||||
Size(n) => Size(f(n)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Holds the result of parsing functions
|
||||
///
|
||||
/// It depends on I, the input type, O, the output type, and E, the error type (by default u32)
|
||||
///
|
||||
#[derive(Debug,PartialEq,Eq,Clone)]
|
||||
pub enum IResult<I,O,E=u32> {
|
||||
/// indicates a correct parsing, the first field containing the rest of the unparsed data, the second field contains the parsed data
|
||||
Done(I,O),
|
||||
/// contains a Err, an enum that can indicate an error code, a position in the input, and a pointer to another error, making a list of errors in the parsing tree
|
||||
Error(Err<I,E>),
|
||||
/// Incomplete contains a Needed, an enum than can represent a known quantity of input data, or unknown
|
||||
Incomplete(Needed)
|
||||
}
|
||||
|
||||
impl<I,O,E> IResult<I,O,E> {
|
||||
pub fn is_done(&self) -> bool {
|
||||
match *self {
|
||||
Done(_,_) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_err(&self) -> bool {
|
||||
match *self {
|
||||
Error(_) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_incomplete(&self) -> bool {
|
||||
match *self {
|
||||
Incomplete(_) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps a `IResult<I, O, E>` to `IResult<I, N, E>` by appling a function
|
||||
/// to a contained `Done` value, leaving `Error` and `Incomplete` value
|
||||
/// untouched.
|
||||
#[inline]
|
||||
pub fn map<N, F: FnOnce(O) -> N>(self, f: F) -> IResult<I, N, E> {
|
||||
match self {
|
||||
Done(i, o) => Done(i, f(o)),
|
||||
Error(e) => Error(e),
|
||||
Incomplete(n) => Incomplete(n),
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps a `IResult<I, O, E>` to `IResult<I, O, E>` by appling a function
|
||||
/// to a contained `Incomplete` value, leaving `Done` and `Error` value
|
||||
/// untouched.
|
||||
#[inline]
|
||||
pub fn map_inc<F>(self, f: F) -> IResult<I, O, E>
|
||||
where F: FnOnce(Needed) -> Needed {
|
||||
match self {
|
||||
Error(e) => Error(e),
|
||||
Incomplete(n) => Incomplete(f(n)),
|
||||
Done(i, o) => Done(i, o),
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps a `IResult<I, O, E>` to `IResult<I, O, N>` by appling a function
|
||||
/// to a contained `Error` value, leaving `Done` and `Incomplete` value
|
||||
/// untouched.
|
||||
#[inline]
|
||||
pub fn map_err<N, F>(self, f: F) -> IResult<I, O, N>
|
||||
where F: FnOnce(Err<I, E>) -> Err<I, N> {
|
||||
match self {
|
||||
Error(e) => Error(f(e)),
|
||||
Incomplete(n) => Incomplete(n),
|
||||
Done(i, o) => Done(i, o),
|
||||
}
|
||||
}
|
||||
|
||||
/// Unwrap the contained `Done(I, O)` value, or panic if the `IResult` is not
|
||||
/// `Done`.
|
||||
pub fn unwrap(self) -> (I, O) {
|
||||
match self {
|
||||
Done(i, o) => (i, o),
|
||||
Incomplete(_) => panic!("unwrap() called on an IResult that is Incomplete"),
|
||||
Error(_) => panic!("unwrap() called on an IResult that is Error")
|
||||
}
|
||||
}
|
||||
|
||||
/// Unwrap the contained `Done(I, O)` value, or panic if the `IResult` is not
|
||||
/// `Done`.
|
||||
pub fn unwrap_inc(self) -> Needed {
|
||||
match self {
|
||||
Incomplete(n) => n,
|
||||
Done(_, _) => panic!("unwrap_inc() called on an IResult that is Done"),
|
||||
Error(_) => panic!("unwrap_inc() called on an IResult that is Error")
|
||||
}
|
||||
}
|
||||
|
||||
/// Unwrap the contained `Done(I, O)` value, or panic if the `IResult` is not
|
||||
/// `Done`.
|
||||
pub fn unwrap_err(self) -> Err<I, E> {
|
||||
match self {
|
||||
Error(e) => e,
|
||||
Done(_, _) => panic!("unwrap_err() called on an IResult that is Done"),
|
||||
Incomplete(_) => panic!("unwrap_err() called on an IResult that is Incomplete"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait GetInput<I> {
|
||||
fn remaining_input(&self) -> Option<I>;
|
||||
}
|
||||
|
||||
pub trait GetOutput<O> {
|
||||
fn output(&self) -> Option<O>;
|
||||
}
|
||||
|
||||
impl<'a,I,O,E> GetInput<&'a[I]> for IResult<&'a[I],O,E> {
|
||||
fn remaining_input(&self) -> Option<&'a[I]> {
|
||||
match *self {
|
||||
Done(ref i,_) => Some(*i),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<O,E> GetInput<()> for IResult<(),O,E> {
|
||||
fn remaining_input(&self) -> Option<()> {
|
||||
match *self {
|
||||
Done((),_) => Some(()),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a,O,E> GetInput<&'a str> for IResult<&'a str,O,E> {
|
||||
fn remaining_input(&self) -> Option<&'a str> {
|
||||
match *self {
|
||||
Done(ref i,_) => Some(*i),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a,I,O,E> GetOutput<&'a[O]> for IResult<I,&'a[O],E> {
|
||||
fn output(&self) -> Option<&'a[O]> {
|
||||
match *self {
|
||||
Done(_, ref o) => Some(*o),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I,E> GetOutput<()> for IResult<I,(),E> {
|
||||
fn output(&self) -> Option<()> {
|
||||
match *self {
|
||||
Done(_,()) => Some(()),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a,I,E> GetOutput<&'a str> for IResult<I,&'a str,E> {
|
||||
fn output(&self) -> Option<&'a str> {
|
||||
match *self {
|
||||
Done(_,ref o) => Some(*o),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
use std::any::Any;
|
||||
#[cfg(not(feature = "core"))]
|
||||
use std::{error,fmt};
|
||||
#[cfg(not(feature = "core"))]
|
||||
use std::fmt::Debug;
|
||||
#[cfg(not(feature = "core"))]
|
||||
impl<P:Debug+Any,E:Debug+Any> error::Error for Err<P,E> {
|
||||
fn description(&self) -> &str {
|
||||
let kind = match *self {
|
||||
Err::Code(ref e) | Err::Node(ref e, _) | Err::Position(ref e, _) | Err::NodePosition(ref e, _, _) => e
|
||||
};
|
||||
kind.description()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
impl<P:fmt::Debug,E:fmt::Debug> fmt::Display for Err<P,E> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Err::Code(ref e) | Err::Node(ref e, _) => {
|
||||
write!(f, "{:?}", e)
|
||||
},
|
||||
Err::Position(ref e, ref p) | Err::NodePosition(ref e, ref p, _) => {
|
||||
write!(f, "{:?}:{:?}", p, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use util::ErrorKind;
|
||||
|
||||
const REST: [u8; 0] = [];
|
||||
const DONE: IResult<&'static [u8], u32> = IResult::Done(&REST, 5);
|
||||
const ERROR: IResult<&'static [u8], u32> = IResult::Error(Err::Code(ErrorKind::Tag));
|
||||
const INCOMPLETE: IResult<&'static [u8], u32> = IResult::Incomplete(Needed::Unknown);
|
||||
|
||||
#[test]
|
||||
fn needed_map() {
|
||||
let unknown = Needed::Unknown;
|
||||
let size = Needed::Size(5);
|
||||
|
||||
assert_eq!(size.map(|x| x * 2), Needed::Size(10));
|
||||
assert_eq!(unknown.map(|x| x * 2), Needed::Unknown);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iresult_map() {
|
||||
assert_eq!(DONE.map(|x| x * 2), IResult::Done(&b""[..], 10));
|
||||
assert_eq!(ERROR.map(|x| x * 2), IResult::Error(Err::Code(ErrorKind::Tag)));
|
||||
assert_eq!(INCOMPLETE.map(|x| x * 2), IResult::Incomplete(Needed::Unknown));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iresult_map_inc() {
|
||||
let inc_unknown: IResult<&[u8], u32> = IResult::Incomplete(Needed::Unknown);
|
||||
let inc_size: IResult<&[u8], u32> = IResult::Incomplete(Needed::Size(5));
|
||||
|
||||
assert_eq!(DONE.map_inc(|n| if let Needed::Size(i) = n {Needed::Size(i+1)} else {n}), IResult::Done(&b""[..], 5));
|
||||
assert_eq!(ERROR.map_inc(|n| if let Needed::Size(i) = n {Needed::Size(i+1)} else {n}), IResult::Error(Err::Code(ErrorKind::Tag)));
|
||||
assert_eq!(inc_unknown.map_inc(|n| if let Needed::Size(i) = n {Needed::Size(i+1)} else {n}), IResult::Incomplete(Needed::Unknown));
|
||||
assert_eq!(inc_size.map_inc(|n| if let Needed::Size(i) = n {Needed::Size(i+1)} else {n}), IResult::Incomplete(Needed::Size(6)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iresult_map_err() {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
struct Error(u32);
|
||||
|
||||
let error_kind = Err::Code(ErrorKind::Custom(Error(5)));
|
||||
|
||||
assert_eq!(DONE.map_err(|_| error_kind.clone()), IResult::Done(&b""[..], 5));
|
||||
assert_eq!(ERROR.map_err(|x| {println!("err: {:?}", x); error_kind.clone()}), IResult::Error(error_kind.clone()));
|
||||
assert_eq!(INCOMPLETE.map_err(|x| {println!("err: {:?}", x); error_kind.clone()}), IResult::Incomplete(Needed::Unknown));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iresult_unwrap_on_done() {
|
||||
assert_eq!(DONE.unwrap(), (&b""[..], 5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn iresult_unwrap_on_err() {
|
||||
ERROR.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn iresult_unwrap_on_inc() {
|
||||
INCOMPLETE.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn iresult_unwrap_err_on_done() {
|
||||
DONE.unwrap_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iresult_unwrap_err_on_err() {
|
||||
assert_eq!(ERROR.unwrap_err(), Err::Code(ErrorKind::Tag));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn iresult_unwrap_err_on_inc() {
|
||||
INCOMPLETE.unwrap_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn iresult_unwrap_inc_on_done() {
|
||||
DONE.unwrap_inc();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn iresult_unwrap_inc_on_err() {
|
||||
ERROR.unwrap_inc();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iresult_unwrap_inc_on_inc() {
|
||||
assert_eq!(INCOMPLETE.unwrap_inc(), Needed::Unknown);
|
||||
}
|
||||
}
|
|
@ -1,151 +0,0 @@
|
|||
//! nom, eating data byte by byte
|
||||
//!
|
||||
//! nom is a parser combinator library with a focus on safe parsing,
|
||||
//! streaming patterns, and as much as possible zero copy.
|
||||
//!
|
||||
//! The code is available on [Github](https://github.com/Geal/nom)
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```
|
||||
//! #[macro_use]
|
||||
//! extern crate nom;
|
||||
//!
|
||||
//! use nom::{IResult,digit};
|
||||
//! use nom::IResult::*;
|
||||
//!
|
||||
//! // Parser definition
|
||||
//!
|
||||
//! use std::str;
|
||||
//! use std::str::FromStr;
|
||||
//!
|
||||
//! named!(parens<i64>, delimited!(
|
||||
//! char!('('),
|
||||
//! expr,
|
||||
//! char!(')')
|
||||
//! )
|
||||
//! );
|
||||
//!
|
||||
//! named!(i64_digit<i64>,
|
||||
//! map_res!(
|
||||
//! map_res!(
|
||||
//! digit,
|
||||
//! str::from_utf8
|
||||
//! ),
|
||||
//! FromStr::from_str
|
||||
//! )
|
||||
//! );
|
||||
//!
|
||||
//! // We transform an integer string into a i64
|
||||
//! // we look for a digit suite, and try to convert it.
|
||||
//! // if either str::from_utf8 or FromStr::from_str fail,
|
||||
//! // the parser will fail
|
||||
//! named!(factor<i64>,
|
||||
//! alt!(
|
||||
//! i64_digit
|
||||
//! | parens
|
||||
//! )
|
||||
//! );
|
||||
//!
|
||||
//! // we define acc as mutable to update its value whenever a new term is found
|
||||
//! named!(term <i64>,
|
||||
//! chain!(
|
||||
//! mut acc: factor ~
|
||||
//! many0!(
|
||||
//! alt!(
|
||||
//! tap!(mul: preceded!(tag!("*"), factor) => acc = acc * mul) |
|
||||
//! tap!(div: preceded!(tag!("/"), factor) => acc = acc / div)
|
||||
//! )
|
||||
//! ),
|
||||
//! || { return acc }
|
||||
//! )
|
||||
//! );
|
||||
//!
|
||||
//! named!(expr <i64>,
|
||||
//! chain!(
|
||||
//! mut acc: term ~
|
||||
//! many0!(
|
||||
//! alt!(
|
||||
//! tap!(add: preceded!(tag!("+"), term) => acc = acc + add) |
|
||||
//! tap!(sub: preceded!(tag!("-"), term) => acc = acc - sub)
|
||||
//! )
|
||||
//! ),
|
||||
//! || { return acc }
|
||||
//! )
|
||||
//! );
|
||||
//!
|
||||
//! fn main() {
|
||||
//! assert_eq!(expr(b"1+2"), IResult::Done(&b""[..], 3));
|
||||
//! assert_eq!(expr(b"12+6-4+3"), IResult::Done(&b""[..], 17));
|
||||
//! assert_eq!(expr(b"1+2*3+4"), IResult::Done(&b""[..], 11));
|
||||
//!
|
||||
//! assert_eq!(expr(b"(2)"), IResult::Done(&b""[..], 2));
|
||||
//! assert_eq!(expr(b"2*(3+4)"), IResult::Done(&b""[..], 14));
|
||||
//! assert_eq!(expr(b"2*2/(5-1)+3"), IResult::Done(&b""[..], 4));
|
||||
//! }
|
||||
//! ```
|
||||
#![cfg_attr(feature = "core", feature(no_std))]
|
||||
#![cfg_attr(feature = "core", feature(collections))]
|
||||
#![cfg_attr(feature = "core", no_std)]
|
||||
#![cfg_attr(feature = "nightly", feature(test))]
|
||||
#![cfg_attr(feature = "nightly", feature(const_fn))]
|
||||
|
||||
#[cfg(feature = "core")]
|
||||
extern crate collections;
|
||||
#[cfg(feature = "regexp")]
|
||||
extern crate regex;
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[macro_use] extern crate lazy_static;
|
||||
#[cfg(feature = "nightly")]
|
||||
extern crate test;
|
||||
|
||||
#[cfg(feature = "core")]
|
||||
mod std {
|
||||
#[macro_use]
|
||||
pub use core::{fmt, iter, option, ops, slice, mem};
|
||||
pub use collections::{boxed, vec, string};
|
||||
pub mod prelude {
|
||||
pub use core::prelude as v1;
|
||||
}
|
||||
}
|
||||
|
||||
pub use self::util::*;
|
||||
pub use self::internal::*;
|
||||
pub use self::macros::*;
|
||||
pub use self::methods::*;
|
||||
pub use self::bytes::*;
|
||||
pub use self::bits::*;
|
||||
|
||||
pub use self::nom::*;
|
||||
pub use self::character::*;
|
||||
|
||||
#[cfg(feature = "regexp")]
|
||||
pub use self::regexp::*;
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
#[cfg(feature = "stream")]
|
||||
pub use self::stream::*;
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
pub use self::str::*;
|
||||
|
||||
#[macro_use] mod util;
|
||||
mod internal;
|
||||
#[macro_use] mod macros;
|
||||
#[macro_use] mod methods;
|
||||
#[macro_use] mod bytes;
|
||||
#[macro_use] mod bits;
|
||||
|
||||
#[macro_use] mod nom;
|
||||
#[macro_use] mod character;
|
||||
|
||||
#[cfg(feature = "regexp")]
|
||||
#[macro_use] mod regexp;
|
||||
|
||||
#[macro_use]
|
||||
#[cfg(not(feature = "core"))]
|
||||
#[cfg(feature = "stream")]
|
||||
mod stream;
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
mod str;
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,480 +0,0 @@
|
|||
//! Method macro combinators
|
||||
//!
|
||||
//! These macros make parsers as methods of structs
|
||||
//! and that can take methods of structs to call
|
||||
//! as parsers.
|
||||
//!
|
||||
//! There is a trick to make them easier to assemble,
|
||||
//! combinators are defined like this:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! macro_rules! tag (
|
||||
//! ($i:expr, $inp: expr) => (
|
||||
//! {
|
||||
//! ...
|
||||
//! }
|
||||
//! );
|
||||
//! );
|
||||
//! ```
|
||||
//!
|
||||
//! But when used as methods in other combinators, are used
|
||||
//! like this:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! method!(my_function<Parser<'a> >, self, tag!("abcd"));
|
||||
//! ```
|
||||
//!
|
||||
//! Internally, other combinators will rewrite
|
||||
//! that call to pass the input as second argument:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! macro_rules! method (
|
||||
//! ($name:ident<$a:ty>, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
//! fn $name( $self_: $a, i: &[u8] ) -> $crate::IResult<&[u8], &[u8]> {
|
||||
//! $submac!(i, $($args)*)
|
||||
//! }
|
||||
//! );
|
||||
//! );
|
||||
//! ```
|
||||
//!
|
||||
//! The `method!` macro is similar to the `named!` macro in the macros module.
|
||||
//! While `named!` will create a parser function, `method!` will create a parser
|
||||
//! method on the struct it is defined in.
|
||||
//!
|
||||
//! Compared to the `named!` macro there are a few differences in how they are
|
||||
//! invoked. A `method!` invocation always has to have the type of `self`
|
||||
//! declared and it can't be a reference due to Rust's borrow lifetime
|
||||
//! restrictions:
|
||||
//! ```ignore
|
||||
//! // -`self`'s type-
|
||||
//! method!(method_name< Parser<'a> >, ...);
|
||||
//! ```
|
||||
//! `self`'s type always comes first.
|
||||
//! The next difference is you have to input the self struct. Due to Rust's
|
||||
//! macro hygiene the macro can't declare it on it's own.
|
||||
//! ```ignore
|
||||
//! // -self-
|
||||
//! method!(method_name<Parser<'a>, &'a str, &'a str>, self, ...);
|
||||
//! ```
|
||||
//! When making a parsing struct with parsing methods, due to the static borrow
|
||||
//! checker,calling any parsing methods on self (or any other parsing struct)
|
||||
//! will cause self to be moved for the rest of the method.To get around this
|
||||
//! restriction all self is moved into the called method and then the called
|
||||
//! method will return self to the caller.
|
||||
//!
|
||||
//! To call a method on self you need to use the `call_m!` macro. For example:
|
||||
//! ```ignore
|
||||
//! struct<'a> Parser<'a> {
|
||||
//! parsed: &'a str,
|
||||
//! }
|
||||
//! impl<'a> Parser<'a> {
|
||||
//! // Constructor omitted for brevity
|
||||
//! method!(take4<Parser<'a>, &'a str, &'a str>, self, take!(4));
|
||||
//! method!(caller<Parser<'a>, &'a str, &'a str>, self, call_m!(self.take4));
|
||||
//! }
|
||||
//! ```
|
||||
//! More complicated combinations still mostly look the same as their `named!`
|
||||
//! counterparts:
|
||||
//! ```ignore
|
||||
//! method!(pub simple_chain<&mut Parser<'a>, &'a str, &'a str>, self,
|
||||
//! chain!(
|
||||
//! call_m!(self.tag_abc) ~
|
||||
//! call_m!(self.tag_def) ~
|
||||
//! call_m!(self.tag_ghi) ~
|
||||
//! last: call_m!(self.simple_peek) ,
|
||||
//! ||{sb.parsed = last; last}
|
||||
//! )
|
||||
//! );
|
||||
//! ```
|
||||
//! The three additions to method definitions to remember are:
|
||||
//! 1. Specify `self`'s type
|
||||
//! 2. Pass `self` to the macro
|
||||
//! 4. Call parser methods using the `call_m!` macro.
|
||||
|
||||
/// Makes a method from a parser combination
|
||||
///
|
||||
/// The must be set up because the compiler needs
|
||||
/// the information
|
||||
///
|
||||
/// ```ignore
|
||||
/// method!(my_function<Parser<'a> >( &[u8] ) -> &[u8], tag!("abcd"));
|
||||
/// // first type parameter is `self`'s type, second is input, third is output
|
||||
/// method!(my_function<Parser<'a>, &[u8], &[u8]>, tag!("abcd"));
|
||||
/// //prefix them with 'pub' to make the methods public
|
||||
/// method!(pub my_function<Parser<'a>,&[u8], &[u8]>, tag!("abcd"));
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! method (
|
||||
// Non-public immutable self
|
||||
($name:ident<$a:ty>( $i:ty ) -> $o:ty, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( $self_: $a, i: $i ) -> ($a, $crate::IResult<$i,$o,u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
($name:ident<$a:ty,$i:ty,$o:ty,$e:ty>, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( $self_: $a, i: $i ) -> ($a, $crate::IResult<$i, $o, $e>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
($name:ident<$a:ty,$i:ty,$o:ty>, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( $self_: $a, i: $i ) -> ($a, $crate::IResult<$i,$o,u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
($name:ident<$a:ty,$o:ty>, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name<'a>( $self_: $a, i: &'a[u8] ) -> ($a, $crate::IResult<&'a [u8], $o, u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
($name:ident<$a:ty>, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( $self_: $a, i: &[u8] ) -> ($a, $crate::IResult<&[u8], &[u8], u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
// Public immutable self
|
||||
(pub $name:ident<$a:ty>( $i:ty ) -> $o:ty, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name( $self_: $a, i: $i ) -> ($a, $crate::IResult<$i,$o,u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$a:ty,$i:ty,$o:ty,$e:ty>, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( $self_: $a, i: $i ) -> ($a, $crate::IResult<$i, $o, $e>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$a:ty,$i:ty,$o:ty>, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name( $self_: $a,i: $i ) -> ($a, $crate::IResult<$i,$o,u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$a:ty,$o:ty>, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name<'a>( $self_: $a, i: &'a[u8] ) -> ($a, $crate::IResult<&'a [u8], $o, u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$a:ty>, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name( $self_: $a, i: &[u8] ) -> ($a, $crate::IResult<&[u8], &[u8], u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
// Non-public mutable self
|
||||
($name:ident<$a:ty>( $i:ty ) -> $o:ty, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( mut $self_: $a, i: $i ) -> ($a, $crate::IResult<$i,$o,u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
($name:ident<$a:ty,$i:ty,$o:ty,$e:ty>, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( mut $self_: $a, i: $i ) -> ($a, $crate::IResult<$i, $o, $e>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
($name:ident<$a:ty,$i:ty,$o:ty>, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( mut $self_: $a, i: $i ) -> ($a, $crate::IResult<$i,$o,u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
($name:ident<$a:ty,$o:ty>, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name<'a>( mut $self_: $a, i: &'a[u8] ) -> ($a, $crate::IResult<&'a [u8], $o, u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
($name:ident<$a:ty>, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( mut $self_: $a, i: &[u8] ) -> ($a, $crate::IResult<&[u8], &[u8], u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
// Public mutable self
|
||||
(pub $name:ident<$a:ty>( $i:ty ) -> $o:ty, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name( mut $self_: $a, i: $i ) -> ($a, $crate::IResult<$i,$o,u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$a:ty,$i:ty,$o:ty,$e:ty>, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( mut $self_: $a, i: $i ) -> ($a, $crate::IResult<$i, $o, $e>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$a:ty,$i:ty,$o:ty>, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name( mut $self_: $a,i: $i ) -> ($a, $crate::IResult<$i,$o,u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$a:ty,$o:ty>, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name<'a>( mut $self_: $a, i: &'a[u8] ) -> ($a, $crate::IResult<&'a [u8], $o, u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$a:ty>, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name( mut $self_: $a, i: &[u8] ) -> ($a, $crate::IResult<&[u8], &[u8], u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// Used to called methods then move self back into self
|
||||
#[macro_export]
|
||||
macro_rules! call_m (
|
||||
($i:expr, $self_:ident.$method:ident) => (
|
||||
{
|
||||
let (tmp, res) = $self_.$method($i);
|
||||
$self_ = tmp;
|
||||
res
|
||||
}
|
||||
);
|
||||
($i:expr, $self_:ident.$method:ident, $($args:expr),* ) => (
|
||||
{
|
||||
let (tmp, res) = $self_.$method($i, $($args),*);
|
||||
$self_ = tmp;
|
||||
res
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
|
||||
/// emulate function currying for method calls on structs
|
||||
/// `apply!(self.my_function, arg1, arg2, ...)` becomes `self.my_function(input, arg1, arg2, ...)`
|
||||
///
|
||||
/// Supports up to 6 arguments
|
||||
#[macro_export]
|
||||
macro_rules! apply_m (
|
||||
($i:expr, $self_:ident.$method:ident, $($args:expr),* ) => ( { let (tmp, res) = $self_.$method( $i, $($args),* ); $self_ = tmp; res } );
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use internal::IResult::*;
|
||||
|
||||
// reproduce the tag_s and take_s macros, because of module import order
|
||||
macro_rules! tag_s (
|
||||
($i:expr, $tag: expr) => (
|
||||
{
|
||||
let res: $crate::IResult<_,_> = if $tag.len() > $i.len() {
|
||||
$crate::IResult::Incomplete($crate::Needed::Size($tag.len()))
|
||||
//} else if &$i[0..$tag.len()] == $tag {
|
||||
} else if ($i).starts_with($tag) {
|
||||
$crate::IResult::Done(&$i[$tag.len()..], &$i[0..$tag.len()])
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Position($crate::ErrorKind::TagStr, $i))
|
||||
};
|
||||
res
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
macro_rules! take_s (
|
||||
($i:expr, $count:expr) => (
|
||||
{
|
||||
let cnt = $count as usize;
|
||||
let res: $crate::IResult<_,_> = if $i.chars().count() < cnt {
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(cnt))
|
||||
} else {
|
||||
let mut offset = $i.len();
|
||||
let mut count = 0;
|
||||
for (o, _) in $i.char_indices() {
|
||||
if count == cnt {
|
||||
offset = o;
|
||||
break;
|
||||
}
|
||||
count += 1;
|
||||
}
|
||||
$crate::IResult::Done(&$i[offset..], &$i[..offset])
|
||||
};
|
||||
res
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
struct Parser<'a> {
|
||||
bcd: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
pub fn new() -> Parser<'a> {
|
||||
Parser{bcd: ""}
|
||||
}
|
||||
|
||||
method!(tag_abc<Parser<'a>, &'a str, &'a str>, self, tag_s!("áβç"));
|
||||
method!(tag_bcd<Parser<'a> >(&'a str) -> &'a str, self, tag_s!("βçδ"));
|
||||
method!(pub tag_hij<Parser<'a> >(&'a str) -> &'a str, self, tag_s!("λïJ"));
|
||||
method!(pub tag_ijk<Parser<'a>, &'a str, &'a str>, self, tag_s!("ïJƙ"));
|
||||
method!(take3<Parser<'a>, &'a str, &'a str>, self, take_s!(3));
|
||||
method!(pub simple_call<Parser<'a>, &'a str, &'a str>, mut self,
|
||||
call_m!(self.tag_abc)
|
||||
);
|
||||
method!(pub simple_peek<Parser<'a>, &'a str, &'a str>, mut self,
|
||||
peek!(call_m!(self.take3))
|
||||
);
|
||||
method!(pub simple_chain<Parser<'a>, &'a str, &'a str>, mut self,
|
||||
chain!(
|
||||
bcd: call_m!(self.tag_bcd) ~
|
||||
last: call_m!(self.simple_peek) ,
|
||||
||{self.bcd = bcd; last}
|
||||
)
|
||||
);
|
||||
fn tag_stuff(mut self: Parser<'a>, input: &'a str, something: &'a str) -> (Parser<'a>, ::IResult<&'a str, &'a str>) {
|
||||
self.bcd = something;
|
||||
let(tmp, res) = self.tag_abc(input);
|
||||
self = tmp;
|
||||
(self, res)
|
||||
}
|
||||
method!(use_apply<Parser<'a>, &'a str, &'a str>, mut self, apply_m!(self.tag_stuff, "βçδ"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_method_call_abc() {
|
||||
let p = Parser::new();
|
||||
let input: &str = "áβçδèƒϱλïJƙ";
|
||||
let consumed: &str = "áβç";
|
||||
let leftover: &str = "δèƒϱλïJƙ";
|
||||
let(_, res) = p.tag_abc(input);
|
||||
match res {
|
||||
Done(extra, output) => { assert!(extra == leftover, "`Parser.tag_abc` consumed leftover input. leftover: {}", extra);
|
||||
assert!(output == consumed, "`Parser.tag_abc` doesnt return the string it consumed \
|
||||
on success. Expected `{}`, got `{}`.", consumed, output);
|
||||
},
|
||||
other => panic!("`Parser.tag_abc` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_method_call_bcd() {
|
||||
let p = Parser::new();
|
||||
let input: &str = "βçδèƒϱλïJƙ";
|
||||
let consumed: &str = "βçδ";
|
||||
let leftover: &str = "èƒϱλïJƙ";
|
||||
let(_, res) = p.tag_bcd(input);
|
||||
match res {
|
||||
Done(extra, output) => { assert!(extra == leftover, "`Parser.tag_bcd` consumed leftover input. leftover: {}", extra);
|
||||
assert!(output == consumed, "`Parser.tag_bcd` doesn't return the string it consumed \
|
||||
on success. Expected `{}`, got `{}`.", consumed, output);
|
||||
},
|
||||
other => panic!("`Parser.tag_bcd` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_method_call_hij() {
|
||||
let p = Parser::new();
|
||||
let input: &str = "λïJƙℓ₥ñôƥ9řƨ";
|
||||
let consumed: &str = "λïJ";
|
||||
let leftover: &str = "ƙℓ₥ñôƥ9řƨ";
|
||||
let(_, res) = p.tag_hij(input);
|
||||
match res {
|
||||
Done(extra, output) => { assert!(extra == leftover, "`Parser.tag_hij` consumed leftover input. leftover: {}", extra);
|
||||
assert!(output == consumed, "`Parser.tag_hij` doesn't return the string it consumed \
|
||||
on success. Expected `{}`, got `{}`.", consumed, output);
|
||||
},
|
||||
other => panic!("`Parser.tag_hij` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_method_call_ijk() {
|
||||
let p = Parser::new();
|
||||
let input: &str = "ïJƙℓ₥ñôƥ9řƨ";
|
||||
let consumed: &str = "ïJƙ";
|
||||
let leftover: &str = "ℓ₥ñôƥ9řƨ";
|
||||
let(_, res) = p.tag_ijk(input);
|
||||
match res {
|
||||
Done(extra, output) => { assert!(extra == leftover, "`Parser.tag_ijk` consumed leftover input. leftover: {}", extra);
|
||||
assert!(output == consumed, "`Parser.tag_ijk` doesn't return the string it consumed \
|
||||
on success. Expected `{}`, got `{}`.", consumed, output);
|
||||
},
|
||||
other => panic!("`Parser.tag_ijk` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn test_method_simple_call() {
|
||||
let p = Parser::new();
|
||||
let input: &str = "áβçδèƒϱλïJƙ";
|
||||
let consumed: &str = "áβç";
|
||||
let leftover: &str = "δèƒϱλïJƙ";
|
||||
let(_, res) = p.simple_call(input);
|
||||
match res {
|
||||
Done(extra, output) => { assert!(extra == leftover, "`Parser.simple_call` consumed leftover input. leftover: {}", extra);
|
||||
assert!(output == consumed, "`Parser.simple_call` doesn't return the string it consumed \
|
||||
on success. Expected `{}`, got `{}`.", consumed, output);
|
||||
},
|
||||
other => panic!("`Parser.simple_call` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_apply_m() {
|
||||
let mut p = Parser::new();
|
||||
let input: &str = "áβçδèƒϱλïJƙ";
|
||||
let consumed: &str = "áβç";
|
||||
let leftover: &str = "δèƒϱλïJƙ";
|
||||
let(tmp, res) = p.use_apply(input);
|
||||
p = tmp;
|
||||
match res {
|
||||
Done(extra, output) => { assert!(extra == leftover, "`Parser.use_apply` consumed leftover input. leftover: {}", extra);
|
||||
assert!(output == consumed, "`Parser.use_apply` doesn't return the string it was supposed to \
|
||||
on success. Expected `{}`, got `{}`.", leftover, output);
|
||||
assert!(p.bcd == "βçδ", "Parser.use_apply didn't modify the parser field correctly: {}", p.bcd);
|
||||
},
|
||||
other => panic!("`Parser.use_apply` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_method_call_peek() {
|
||||
let p = Parser::new();
|
||||
let input: &str = "ж¥ƺáβçδèƒϱλïJƙ";
|
||||
let consumed: &str = "ж¥ƺ";
|
||||
let(_, res) = p.simple_peek(input);
|
||||
match res {
|
||||
Done(extra, output) => { assert!(extra == input, "`Parser.simple_peek` consumed leftover input. leftover: {}", extra);
|
||||
assert!(output == consumed, "`Parser.simple_peek` doesn't return the string it consumed \
|
||||
on success. Expected `{}`, got `{}`.", consumed, output);
|
||||
},
|
||||
other => panic!("`Parser.simple_peek` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_method_call_chain() {
|
||||
let mut p = Parser::new();
|
||||
let input : &str = "βçδδèƒϱλïJƙℓ";
|
||||
let leftover : &str = "δèƒϱλïJƙℓ";
|
||||
let output : &str = "늟";
|
||||
let(tmp, res) = p.simple_chain(input);
|
||||
p = tmp;
|
||||
match res {
|
||||
Done(extra, out) => { assert!(extra == leftover, "`Parser.simple_chain` consumed leftover input. leftover: {}", extra);
|
||||
assert!(out == output, "`Parser.simple_chain` doesn't return the string it was supposed to \
|
||||
on success. Expected `{}`, got `{}`.", output, out);
|
||||
assert!(p.bcd == "βçδ", "Parser.simple_chain didn't modify the parser field correctly: {}", p.bcd);
|
||||
},
|
||||
other => panic!("`Parser.simple_chain` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,950 +0,0 @@
|
|||
//! Useful parser combinators
|
||||
//!
|
||||
//! A number of useful parser combinators have already been implemented.
|
||||
//! Some of them use macros, other are implemented through functions.
|
||||
//! Hopefully, the syntax will converge to onely one way in the future,
|
||||
//! but the macros system makes no promises.
|
||||
//!
|
||||
|
||||
#[cfg(feature = "core")]
|
||||
use std::prelude::v1::*;
|
||||
use std::boxed::Box;
|
||||
|
||||
use std::fmt::Debug;
|
||||
use internal::*;
|
||||
use internal::IResult::*;
|
||||
use internal::Err::*;
|
||||
use util::{ErrorKind,IterIndices,AsChar,InputLength};
|
||||
use std::mem::transmute;
|
||||
|
||||
#[inline]
|
||||
pub fn tag_cl<'a,'b>(rec:&'a[u8]) -> Box<Fn(&'b[u8]) -> IResult<&'b[u8], &'b[u8]> + 'a> {
|
||||
Box::new(move |i: &'b[u8]| -> IResult<&'b[u8], &'b[u8]> {
|
||||
if i.len() >= rec.len() && &i[0..rec.len()] == rec {
|
||||
Done(&i[rec.len()..], &i[0..rec.len()])
|
||||
} else {
|
||||
Error(Position(ErrorKind::TagClosure, i))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
#[inline]
|
||||
pub fn print<T: Debug>(input: T) -> IResult<T, ()> {
|
||||
println!("{:?}", input);
|
||||
Done(input, ())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn begin(input: &[u8]) -> IResult<(), &[u8]> {
|
||||
Done((), input)
|
||||
}
|
||||
|
||||
// FIXME: when rust-lang/rust#17436 is fixed, macros will be able to export
|
||||
// public methods
|
||||
//pub is_not!(line_ending b"\r\n")
|
||||
pub fn not_line_ending(input:&[u8]) -> IResult<&[u8], &[u8]> {
|
||||
for (idx, item) in input.iter().enumerate() {
|
||||
for &i in b"\r\n".iter() {
|
||||
if *item == i {
|
||||
return Done(&input[idx..], &input[0..idx])
|
||||
}
|
||||
}
|
||||
}
|
||||
Done(&input[input.len()..], input)
|
||||
}
|
||||
|
||||
named!(tag_ln, tag!("\n"));
|
||||
|
||||
/// Recognizes a line feed
|
||||
#[inline]
|
||||
pub fn line_ending(input:&[u8]) -> IResult<&[u8], &[u8]> {
|
||||
tag_ln(input)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_alphabetic(chr:u8) -> bool {
|
||||
(chr >= 0x41 && chr <= 0x5A) || (chr >= 0x61 && chr <= 0x7A)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_digit(chr: u8) -> bool {
|
||||
chr >= 0x30 && chr <= 0x39
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_hex_digit(chr: u8) -> bool {
|
||||
(chr >= 0x30 && chr <= 0x39) ||
|
||||
(chr >= 0x41 && chr <= 0x46) ||
|
||||
(chr >= 0x61 && chr <= 0x66)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_oct_digit(chr: u8) -> bool {
|
||||
chr >= 0x30 && chr <= 0x37
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_alphanumeric(chr: u8) -> bool {
|
||||
is_alphabetic(chr) || is_digit(chr)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_space(chr:u8) -> bool {
|
||||
chr == ' ' as u8 || chr == '\t' as u8
|
||||
}
|
||||
|
||||
// FIXME: when rust-lang/rust#17436 is fixed, macros will be able to export
|
||||
//pub filter!(alpha is_alphabetic)
|
||||
//pub filter!(digit is_digit)
|
||||
//pub filter!(hex_digit is_hex_digit)
|
||||
//pub filter!(oct_digit is_oct_digit)
|
||||
//pub filter!(alphanumeric is_alphanumeric)
|
||||
|
||||
use std::ops::{Index,Range,RangeFrom};
|
||||
/// Recognizes lowercase and uppercase alphabetic characters: a-zA-Z
|
||||
pub fn alpha<'a, T: ?Sized>(input:&'a T) -> IResult<&'a T, &'a T> where
|
||||
T:Index<Range<usize>, Output=T>+Index<RangeFrom<usize>, Output=T>,
|
||||
&'a T: IterIndices+InputLength {
|
||||
let input_length = input.input_len();
|
||||
if input_length == 0 {
|
||||
return Error(Position(ErrorKind::Alpha, input))
|
||||
}
|
||||
|
||||
for (idx, item) in input.iter_indices() {
|
||||
if ! item.is_alpha() {
|
||||
if idx == 0 {
|
||||
return Error(Position(ErrorKind::Alpha, input))
|
||||
} else {
|
||||
return Done(&input[idx..], &input[0..idx])
|
||||
}
|
||||
}
|
||||
}
|
||||
Done(&input[input_length..], input)
|
||||
}
|
||||
|
||||
/// Recognizes numerical characters: 0-9
|
||||
pub fn digit<'a, T: ?Sized>(input:&'a T) -> IResult<&'a T, &'a T> where
|
||||
T:Index<Range<usize>, Output=T>+Index<RangeFrom<usize>, Output=T>,
|
||||
&'a T: IterIndices+InputLength {
|
||||
let input_length = input.input_len();
|
||||
if input_length == 0 {
|
||||
return Error(Position(ErrorKind::Digit, input))
|
||||
}
|
||||
|
||||
for (idx, item) in input.iter_indices() {
|
||||
if ! item.is_0_to_9() {
|
||||
if idx == 0 {
|
||||
return Error(Position(ErrorKind::Digit, input))
|
||||
} else {
|
||||
return Done(&input[idx..], &input[0..idx])
|
||||
}
|
||||
}
|
||||
}
|
||||
Done(&input[input_length..], input)
|
||||
}
|
||||
|
||||
/// Recognizes hexadecimal numerical characters: 0-9, A-F, a-f
|
||||
pub fn hex_digit<'a, T: ?Sized>(input:&'a T) -> IResult<&'a T, &'a T> where
|
||||
T:Index<Range<usize>, Output=T>+Index<RangeFrom<usize>, Output=T>,
|
||||
&'a T: IterIndices+InputLength {
|
||||
let input_length = input.input_len();
|
||||
if input_length == 0 {
|
||||
return Error(Position(ErrorKind::HexDigit, input))
|
||||
}
|
||||
|
||||
for (idx, item) in input.iter_indices() {
|
||||
if ! item.is_hex_digit() {
|
||||
if idx == 0 {
|
||||
return Error(Position(ErrorKind::HexDigit, input))
|
||||
} else {
|
||||
return Done(&input[idx..], &input[0..idx])
|
||||
}
|
||||
}
|
||||
}
|
||||
Done(&input[input_length..], input)
|
||||
}
|
||||
|
||||
/// Recognizes octal characters: 0-7
|
||||
pub fn oct_digit<'a, T: ?Sized>(input:&'a T) -> IResult<&'a T, &'a T> where
|
||||
T:Index<Range<usize>, Output=T>+Index<RangeFrom<usize>, Output=T>,
|
||||
&'a T: IterIndices+InputLength {
|
||||
let input_length = input.input_len();
|
||||
if input_length == 0 {
|
||||
return Error(Position(ErrorKind::OctDigit, input))
|
||||
}
|
||||
|
||||
for (idx, item) in input.iter_indices() {
|
||||
if ! item.is_oct_digit() {
|
||||
if idx == 0 {
|
||||
return Error(Position(ErrorKind::OctDigit, input))
|
||||
} else {
|
||||
return Done(&input[idx..], &input[0..idx])
|
||||
}
|
||||
}
|
||||
}
|
||||
Done(&input[input_length..], input)
|
||||
}
|
||||
|
||||
/// Recognizes numerical and alphabetic characters: 0-9a-zA-Z
|
||||
pub fn alphanumeric<'a, T: ?Sized>(input:&'a T) -> IResult<&'a T, &'a T> where
|
||||
T:Index<Range<usize>, Output=T>+Index<RangeFrom<usize>, Output=T>,
|
||||
&'a T: IterIndices+InputLength {
|
||||
let input_length = input.input_len();
|
||||
if input_length == 0 {
|
||||
return Error(Position(ErrorKind::AlphaNumeric, input));
|
||||
}
|
||||
|
||||
for (idx, item) in input.iter_indices() {
|
||||
if ! item.is_alphanum() {
|
||||
if idx == 0 {
|
||||
return Error(Position(ErrorKind::AlphaNumeric, input))
|
||||
} else {
|
||||
return Done(&input[idx..], &input[0..idx])
|
||||
}
|
||||
}
|
||||
}
|
||||
Done(&input[input_length..], input)
|
||||
}
|
||||
|
||||
/// Recognizes spaces and tabs
|
||||
pub fn space<'a, T: ?Sized>(input:&'a T) -> IResult<&'a T, &'a T> where
|
||||
T:Index<Range<usize>, Output=T>+Index<RangeFrom<usize>, Output=T>,
|
||||
&'a T: IterIndices+InputLength {
|
||||
let input_length = input.input_len();
|
||||
if input_length == 0 {
|
||||
return Error(Position(ErrorKind::Space, input));
|
||||
}
|
||||
|
||||
for (idx, item) in input.iter_indices() {
|
||||
let chr = item.as_char();
|
||||
if ! (chr == ' ' || chr == '\t') {
|
||||
if idx == 0 {
|
||||
return Error(Position(ErrorKind::Space, input))
|
||||
} else {
|
||||
return Done(&input[idx..], &input[0..idx])
|
||||
}
|
||||
}
|
||||
}
|
||||
Done(&input[input_length..], input)
|
||||
}
|
||||
|
||||
/// Recognizes spaces, tabs, carriage returns and line feeds
|
||||
pub fn multispace<'a, T: ?Sized>(input:&'a T) -> IResult<&'a T, &'a T> where
|
||||
T:Index<Range<usize>, Output=T>+Index<RangeFrom<usize>, Output=T>,
|
||||
&'a T: IterIndices+InputLength {
|
||||
let input_length = input.input_len();
|
||||
if input_length == 0 {
|
||||
return Error(Position(ErrorKind::MultiSpace, input));
|
||||
}
|
||||
|
||||
for (idx, item) in input.iter_indices() {
|
||||
let chr = item.as_char();
|
||||
if ! (chr == ' ' || chr == '\t' || chr == '\r' || chr == '\n') {
|
||||
if idx == 0 {
|
||||
return Error(Position(ErrorKind::MultiSpace, input))
|
||||
} else {
|
||||
return Done(&input[idx..], &input[0..idx])
|
||||
}
|
||||
}
|
||||
}
|
||||
Done(&input[input_length..], input)
|
||||
}
|
||||
|
||||
pub fn sized_buffer(input:&[u8]) -> IResult<&[u8], &[u8]> {
|
||||
if input.is_empty() {
|
||||
return Incomplete(Needed::Unknown)
|
||||
}
|
||||
|
||||
let len = input[0] as usize;
|
||||
|
||||
if input.len() >= len + 1 {
|
||||
Done(&input[len+1..], &input[1..len+1])
|
||||
} else {
|
||||
Incomplete(Needed::Size(1 + len))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn length_value(input:&[u8]) -> IResult<&[u8], &[u8]> {
|
||||
let input_len = input.len();
|
||||
if input_len == 0 {
|
||||
return Error(Position(ErrorKind::LengthValueFn, input))
|
||||
}
|
||||
|
||||
let len = input[0] as usize;
|
||||
if input_len - 1 >= len {
|
||||
IResult::Done(&input[len+1..], &input[1..len+1])
|
||||
} else {
|
||||
IResult::Incomplete(Needed::Size(1+len))
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes an unsigned 1 byte integer (equivalent to take!(1)
|
||||
#[inline]
|
||||
pub fn be_u8(i: &[u8]) -> IResult<&[u8], u8> {
|
||||
if i.len() < 1 {
|
||||
Incomplete(Needed::Size(1))
|
||||
} else {
|
||||
Done(&i[1..], i[0])
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes big endian unsigned 2 bytes integer
|
||||
#[inline]
|
||||
pub fn be_u16(i: &[u8]) -> IResult<&[u8], u16> {
|
||||
if i.len() < 2 {
|
||||
Incomplete(Needed::Size(2))
|
||||
} else {
|
||||
let res = ((i[0] as u16) << 8) + i[1] as u16;
|
||||
Done(&i[2..], res)
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes big endian unsigned 4 bytes integer
|
||||
#[inline]
|
||||
pub fn be_u32(i: &[u8]) -> IResult<&[u8], u32> {
|
||||
if i.len() < 4 {
|
||||
Incomplete(Needed::Size(4))
|
||||
} else {
|
||||
let res = ((i[0] as u32) << 24) + ((i[1] as u32) << 16) + ((i[2] as u32) << 8) + i[3] as u32;
|
||||
Done(&i[4..], res)
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes big endian unsigned 8 bytes integer
|
||||
#[inline]
|
||||
pub fn be_u64(i: &[u8]) -> IResult<&[u8], u64> {
|
||||
if i.len() < 8 {
|
||||
Incomplete(Needed::Size(8))
|
||||
} else {
|
||||
let res = ((i[0] as u64) << 56) + ((i[1] as u64) << 48) + ((i[2] as u64) << 40) + ((i[3] as u64) << 32) +
|
||||
((i[4] as u64) << 24) + ((i[5] as u64) << 16) + ((i[6] as u64) << 8) + i[7] as u64;
|
||||
Done(&i[8..], res)
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes a signed 1 byte integer (equivalent to take!(1)
|
||||
#[inline]
|
||||
pub fn be_i8(i:&[u8]) -> IResult<&[u8], i8> {
|
||||
map!(i, be_u8, | x | { x as i8 })
|
||||
}
|
||||
|
||||
/// Recognizes big endian signed 2 bytes integer
|
||||
#[inline]
|
||||
pub fn be_i16(i:&[u8]) -> IResult<&[u8], i16> {
|
||||
map!(i, be_u16, | x | { x as i16 })
|
||||
}
|
||||
|
||||
/// Recognizes big endian signed 4 bytes integer
|
||||
#[inline]
|
||||
pub fn be_i32(i:&[u8]) -> IResult<&[u8], i32> {
|
||||
map!(i, be_u32, | x | { x as i32 })
|
||||
}
|
||||
|
||||
/// Recognizes big endian signed 8 bytes integer
|
||||
#[inline]
|
||||
pub fn be_i64(i:&[u8]) -> IResult<&[u8], i64> {
|
||||
map!(i, be_u64, | x | { x as i64 })
|
||||
}
|
||||
|
||||
/// Recognizes an unsigned 1 byte integer (equivalent to take!(1)
|
||||
#[inline]
|
||||
pub fn le_u8(i: &[u8]) -> IResult<&[u8], u8> {
|
||||
if i.len() < 1 {
|
||||
Incomplete(Needed::Size(1))
|
||||
} else {
|
||||
Done(&i[1..], i[0])
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes little endian unsigned 2 bytes integer
|
||||
#[inline]
|
||||
pub fn le_u16(i: &[u8]) -> IResult<&[u8], u16> {
|
||||
if i.len() < 2 {
|
||||
Incomplete(Needed::Size(2))
|
||||
} else {
|
||||
let res = ((i[1] as u16) << 8) + i[0] as u16;
|
||||
Done(&i[2..], res)
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes little endian unsigned 4 bytes integer
|
||||
#[inline]
|
||||
pub fn le_u32(i: &[u8]) -> IResult<&[u8], u32> {
|
||||
if i.len() < 4 {
|
||||
Incomplete(Needed::Size(4))
|
||||
} else {
|
||||
let res = ((i[3] as u32) << 24) + ((i[2] as u32) << 16) + ((i[1] as u32) << 8) + i[0] as u32;
|
||||
Done(&i[4..], res)
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes little endian unsigned 8 bytes integer
|
||||
#[inline]
|
||||
pub fn le_u64(i: &[u8]) -> IResult<&[u8], u64> {
|
||||
if i.len() < 8 {
|
||||
Incomplete(Needed::Size(8))
|
||||
} else {
|
||||
let res = ((i[7] as u64) << 56) + ((i[6] as u64) << 48) + ((i[5] as u64) << 40) + ((i[4] as u64) << 32) +
|
||||
((i[3] as u64) << 24) + ((i[2] as u64) << 16) + ((i[1] as u64) << 8) + i[0] as u64;
|
||||
Done(&i[8..], res)
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes a signed 1 byte integer (equivalent to take!(1)
|
||||
#[inline]
|
||||
pub fn le_i8(i:&[u8]) -> IResult<&[u8], i8> {
|
||||
map!(i, le_u8, | x | { x as i8 })
|
||||
}
|
||||
|
||||
/// Recognizes little endian signed 2 bytes integer
|
||||
#[inline]
|
||||
pub fn le_i16(i:&[u8]) -> IResult<&[u8], i16> {
|
||||
map!(i, le_u16, | x | { x as i16 })
|
||||
}
|
||||
|
||||
/// Recognizes little endian signed 4 bytes integer
|
||||
#[inline]
|
||||
pub fn le_i32(i:&[u8]) -> IResult<&[u8], i32> {
|
||||
map!(i, le_u32, | x | { x as i32 })
|
||||
}
|
||||
|
||||
/// Recognizes little endian signed 8 bytes integer
|
||||
#[inline]
|
||||
pub fn le_i64(i:&[u8]) -> IResult<&[u8], i64> {
|
||||
map!(i, le_u64, | x | { x as i64 })
|
||||
}
|
||||
|
||||
/// if parameter is true, parse a big endian u16 integer,
|
||||
/// otherwise a little endian u16 integer
|
||||
#[macro_export]
|
||||
macro_rules! u16 ( ($i:expr, $e:expr) => ( {if $e { $crate::be_u16($i) } else { $crate::le_u16($i) } } ););
|
||||
/// if parameter is true, parse a big endian u32 integer,
|
||||
/// otherwise a little endian u32 integer
|
||||
#[macro_export]
|
||||
macro_rules! u32 ( ($i:expr, $e:expr) => ( {if $e { $crate::be_u32($i) } else { $crate::le_u32($i) } } ););
|
||||
/// if parameter is true, parse a big endian u64 integer,
|
||||
/// otherwise a little endian u64 integer
|
||||
#[macro_export]
|
||||
macro_rules! u64 ( ($i:expr, $e:expr) => ( {if $e { $crate::be_u64($i) } else { $crate::le_u64($i) } } ););
|
||||
|
||||
/// if parameter is true, parse a big endian i16 integer,
|
||||
/// otherwise a little endian i16 integer
|
||||
#[macro_export]
|
||||
macro_rules! i16 ( ($i:expr, $e:expr) => ( {if $e { $crate::be_i16($i) } else { $crate::le_i16($i) } } ););
|
||||
/// if parameter is true, parse a big endian i32 integer,
|
||||
/// otherwise a little endian i32 integer
|
||||
#[macro_export]
|
||||
macro_rules! i32 ( ($i:expr, $e:expr) => ( {if $e { $crate::be_i32($i) } else { $crate::le_i32($i) } } ););
|
||||
/// if parameter is true, parse a big endian i64 integer,
|
||||
/// otherwise a little endian i64 integer
|
||||
#[macro_export]
|
||||
macro_rules! i64 ( ($i:expr, $e:expr) => ( {if $e { $crate::be_i64($i) } else { $crate::le_i64($i) } } ););
|
||||
|
||||
/// Recognizes big endian 4 bytes floating point number
|
||||
#[inline]
|
||||
pub fn be_f32(input: &[u8]) -> IResult<&[u8], f32> {
|
||||
match be_u32(input) {
|
||||
Error(e) => Error(e),
|
||||
Incomplete(e) => Incomplete(e),
|
||||
Done(i,o) => {
|
||||
unsafe {
|
||||
Done(i, transmute::<u32, f32>(o))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes big endian 8 bytes floating point number
|
||||
#[inline]
|
||||
pub fn be_f64(input: &[u8]) -> IResult<&[u8], f64> {
|
||||
match be_u64(input) {
|
||||
Error(e) => Error(e),
|
||||
Incomplete(e) => Incomplete(e),
|
||||
Done(i,o) => {
|
||||
unsafe {
|
||||
Done(i, transmute::<u64, f64>(o))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes little endian 4 bytes floating point number
|
||||
#[inline]
|
||||
pub fn le_f32(input: &[u8]) -> IResult<&[u8], f32> {
|
||||
match le_u32(input) {
|
||||
Error(e) => Error(e),
|
||||
Incomplete(e) => Incomplete(e),
|
||||
Done(i,o) => {
|
||||
unsafe {
|
||||
Done(i, transmute::<u32, f32>(o))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes little endian 8 bytes floating point number
|
||||
#[inline]
|
||||
pub fn le_f64(input: &[u8]) -> IResult<&[u8], f64> {
|
||||
match le_u64(input) {
|
||||
Error(e) => Error(e),
|
||||
Incomplete(e) => Incomplete(e),
|
||||
Done(i,o) => {
|
||||
unsafe {
|
||||
Done(i, transmute::<u64, f64>(o))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes a hex-encoded integer
|
||||
#[inline]
|
||||
pub fn hex_u32(input: &[u8]) -> IResult<&[u8], u32> {
|
||||
match is_a!(input, &b"0123456789abcdef"[..]) {
|
||||
Error(e) => Error(e),
|
||||
Incomplete(e) => Incomplete(e),
|
||||
Done(i,o) => {
|
||||
let mut res = 0u32;
|
||||
|
||||
// Do not parse more than 8 characters for a u32
|
||||
let mut remaining = i;
|
||||
let mut parsed = o;
|
||||
if o.len() > 8 {
|
||||
remaining = &input[8..];
|
||||
parsed = &input[..8];
|
||||
}
|
||||
|
||||
for &e in parsed {
|
||||
let digit = e as char;
|
||||
let value = digit.to_digit(16).unwrap_or(0);
|
||||
res = value + (res << 4);
|
||||
}
|
||||
Done(remaining, res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes empty input buffers
|
||||
///
|
||||
/// useful to verify that the previous parsers used all of the input
|
||||
#[inline]
|
||||
//pub fn eof(input:&[u8]) -> IResult<&[u8], &[u8]> {
|
||||
pub fn eof<'a, T:?Sized>(input: &'a T) -> IResult<&'a T,&'a T> where
|
||||
T:Index<Range<usize>, Output=T>+Index<RangeFrom<usize>, Output=T>,
|
||||
&'a T: InputLength {
|
||||
if input.input_len() == 0 {
|
||||
Done(input, input)
|
||||
} else {
|
||||
Error(Position(ErrorKind::Eof, input))
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes non empty buffers
|
||||
#[inline]
|
||||
pub fn non_empty<'a, T:?Sized>(input: &'a T) -> IResult<&'a T,&'a T> where
|
||||
T:Index<Range<usize>, Output=T>+Index<RangeFrom<usize>, Output=T>,
|
||||
&'a T: InputLength {
|
||||
if input.input_len() == 0 {
|
||||
Error(Position(ErrorKind::NonEmpty, input))
|
||||
} else {
|
||||
Done(&input[input.input_len()..], input)
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the remaining input.
|
||||
#[inline]
|
||||
pub fn rest(input: &[u8]) -> IResult<&[u8], &[u8]> {
|
||||
IResult::Done(&input[input.len()..], input)
|
||||
}
|
||||
|
||||
/// Return the remaining input, for strings.
|
||||
#[inline]
|
||||
pub fn rest_s(input: &str) -> IResult<&str, &str> {
|
||||
IResult::Done(&input[input.len()..], input)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use internal::{Needed,IResult};
|
||||
use internal::IResult::*;
|
||||
use internal::Err::*;
|
||||
use util::ErrorKind;
|
||||
|
||||
#[test]
|
||||
fn tag_closure() {
|
||||
let x = tag_cl(&b"abcd"[..]);
|
||||
let r = x(&b"abcdabcdefgh"[..]);
|
||||
assert_eq!(r, Done(&b"abcdefgh"[..], &b"abcd"[..]));
|
||||
|
||||
let r2 = x(&b"abcefgh"[..]);
|
||||
assert_eq!(r2, Error(Position(ErrorKind::TagClosure, &b"abcefgh"[..])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn character() {
|
||||
let empty: &[u8] = b"";
|
||||
let a: &[u8] = b"abcd";
|
||||
let b: &[u8] = b"1234";
|
||||
let c: &[u8] = b"a123";
|
||||
let d: &[u8] = "azé12".as_bytes();
|
||||
let e: &[u8] = b" ";
|
||||
assert_eq!(alpha(a), Done(empty, a));
|
||||
assert_eq!(alpha(b), Error(Position(ErrorKind::Alpha,b)));
|
||||
assert_eq!(alpha(c), Done(&c[1..], &b"a"[..]));
|
||||
assert_eq!(alpha(d), Done("é12".as_bytes(), &b"az"[..]));
|
||||
assert_eq!(digit(a), Error(Position(ErrorKind::Digit,a)));
|
||||
assert_eq!(digit(b), Done(empty, b));
|
||||
assert_eq!(digit(c), Error(Position(ErrorKind::Digit,c)));
|
||||
assert_eq!(digit(d), Error(Position(ErrorKind::Digit,d)));
|
||||
assert_eq!(hex_digit(a), Done(empty, a));
|
||||
assert_eq!(hex_digit(b), Done(empty, b));
|
||||
assert_eq!(hex_digit(c), Done(empty, c));
|
||||
assert_eq!(hex_digit(d), Done("zé12".as_bytes(), &b"a"[..]));
|
||||
assert_eq!(hex_digit(e), Error(Position(ErrorKind::HexDigit,e)));
|
||||
assert_eq!(oct_digit(a), Error(Position(ErrorKind::OctDigit,a)));
|
||||
assert_eq!(oct_digit(b), Done(empty, b));
|
||||
assert_eq!(oct_digit(c), Error(Position(ErrorKind::OctDigit,c)));
|
||||
assert_eq!(oct_digit(d), Error(Position(ErrorKind::OctDigit,d)));
|
||||
assert_eq!(alphanumeric(a), Done(empty, a));
|
||||
assert_eq!(fix_error!(b,(), alphanumeric), Done(empty, b));
|
||||
assert_eq!(alphanumeric(c), Done(empty, c));
|
||||
assert_eq!(alphanumeric(d), Done("é12".as_bytes(), &b"az"[..]));
|
||||
assert_eq!(space(e), Done(&b""[..], &b" "[..]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn character_s() {
|
||||
let empty = "";
|
||||
let a = "abcd";
|
||||
let b = "1234";
|
||||
let c = "a123";
|
||||
let d = "azé12";
|
||||
let e = " ";
|
||||
assert_eq!(alpha(a), Done(empty, a));
|
||||
assert_eq!(alpha(b), Error(Position(ErrorKind::Alpha,b)));
|
||||
assert_eq!(alpha(c), Done(&c[1..], &"a"[..]));
|
||||
assert_eq!(alpha(d), Done("12", &"azé"[..]));
|
||||
assert_eq!(digit(a), Error(Position(ErrorKind::Digit,a)));
|
||||
assert_eq!(digit(b), Done(empty, b));
|
||||
assert_eq!(digit(c), Error(Position(ErrorKind::Digit,c)));
|
||||
assert_eq!(digit(d), Error(Position(ErrorKind::Digit,d)));
|
||||
assert_eq!(hex_digit(a), Done(empty, a));
|
||||
assert_eq!(hex_digit(b), Done(empty, b));
|
||||
assert_eq!(hex_digit(c), Done(empty, c));
|
||||
assert_eq!(hex_digit(d), Done("zé12", &"a"[..]));
|
||||
assert_eq!(hex_digit(e), Error(Position(ErrorKind::HexDigit,e)));
|
||||
assert_eq!(oct_digit(a), Error(Position(ErrorKind::OctDigit,a)));
|
||||
assert_eq!(oct_digit(b), Done(empty, b));
|
||||
assert_eq!(oct_digit(c), Error(Position(ErrorKind::OctDigit,c)));
|
||||
assert_eq!(oct_digit(d), Error(Position(ErrorKind::OctDigit,d)));
|
||||
assert_eq!(alphanumeric(a), Done(empty, a));
|
||||
assert_eq!(fix_error!(b,(), alphanumeric), Done(empty, b));
|
||||
assert_eq!(alphanumeric(c), Done(empty, c));
|
||||
assert_eq!(alphanumeric(d), Done("", &"azé12"[..]));
|
||||
assert_eq!(space(e), Done(&""[..], &" "[..]));
|
||||
}
|
||||
|
||||
use util::HexDisplay;
|
||||
#[test]
|
||||
fn offset() {
|
||||
let a = &b"abcd"[..];
|
||||
let b = &b"1234"[..];
|
||||
let c = &b"a123"[..];
|
||||
let d = &b" \t"[..];
|
||||
let e = &b" \t\r\n"[..];
|
||||
let f = &b"123abcDEF"[..];
|
||||
|
||||
match alpha(a) {
|
||||
Done(i, _) => { assert_eq!(a.offset(i) + i.len(), a.len()); }
|
||||
_ => { panic!("wrong return type in offset test for alpha") }
|
||||
}
|
||||
match digit(b) {
|
||||
Done(i, _) => { assert_eq!(b.offset(i) + i.len(), b.len()); }
|
||||
_ => { panic!("wrong return type in offset test for digit") }
|
||||
}
|
||||
match alphanumeric(c) {
|
||||
Done(i, _) => { assert_eq!(c.offset(i) + i.len(), c.len()); }
|
||||
_ => { panic!("wrong return type in offset test for alphanumeric") }
|
||||
}
|
||||
match space(d) {
|
||||
Done(i, _) => { assert_eq!(d.offset(i) + i.len(), d.len()); }
|
||||
_ => { panic!("wrong return type in offset test for space") }
|
||||
}
|
||||
match multispace(e) {
|
||||
Done(i, _) => { assert_eq!(e.offset(i) + i.len(), e.len()); }
|
||||
_ => { panic!("wrong return type in offset test for multispace") }
|
||||
}
|
||||
match hex_digit(f) {
|
||||
Done(i, _) => { assert_eq!(f.offset(i) + i.len(), f.len()); }
|
||||
_ => { panic!("wrong return type in offset test for hex_digit") }
|
||||
}
|
||||
match oct_digit(f) {
|
||||
Done(i, _) => { assert_eq!(f.offset(i) + i.len(), f.len()); }
|
||||
_ => { panic!("wrong return type in offset test for oct_digit") }
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_not() {
|
||||
let a: &[u8] = b"ab12cd\nefgh";
|
||||
assert_eq!(not_line_ending(a), Done(&b"\nefgh"[..], &b"ab12cd"[..]));
|
||||
|
||||
let b: &[u8] = b"ab12cd\nefgh\nijkl";
|
||||
assert_eq!(not_line_ending(b), Done(&b"\nefgh\nijkl"[..], &b"ab12cd"[..]));
|
||||
|
||||
let c: &[u8] = b"ab12cd";
|
||||
assert_eq!(not_line_ending(c), Done(&b""[..], c));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn buffer_with_size() {
|
||||
let i:Vec<u8> = vec![7,8];
|
||||
let o:Vec<u8> = vec![4,5,6];
|
||||
//let arr:[u8; 6usize] = [3, 4, 5, 6, 7, 8];
|
||||
let arr:[u8; 6usize] = [3, 4, 5, 6, 7, 8];
|
||||
let res = sized_buffer(&arr[..]);
|
||||
assert_eq!(res, Done(&i[..], &o[..]))
|
||||
}
|
||||
|
||||
/*#[test]
|
||||
fn t1() {
|
||||
let v1:Vec<u8> = vec![1,2,3];
|
||||
let v2:Vec<u8> = vec![4,5,6];
|
||||
let d = Done(&v1[..], &v2[..]);
|
||||
let res = d.flat_map(print);
|
||||
assert_eq!(res, Done(&v2[..], ()));
|
||||
}*/
|
||||
|
||||
#[test]
|
||||
fn length_value_test() {
|
||||
let i1 = vec![7,8];
|
||||
let o1 = vec![4, 5, 6];
|
||||
let arr1:[u8; 6usize] = [3, 4, 5, 6, 7, 8];
|
||||
let res1 = length_value(&arr1);
|
||||
assert_eq!(Done(&i1[..], &o1[..]), res1);
|
||||
|
||||
let i2:Vec<u8> = vec![4,5,6,7,8];
|
||||
let o2: &[u8] = b"";
|
||||
let arr2:[u8; 6usize] = [0, 4, 5, 6, 7, 8];
|
||||
let res2 = length_value(&arr2);
|
||||
assert_eq!(Done(&i2[..], o2), res2);
|
||||
|
||||
let arr3:[u8; 7usize] = [8, 4, 5, 6, 7, 8, 9];
|
||||
let res3 = length_value(&arr3);
|
||||
//FIXME: should be incomplete
|
||||
assert_eq!(Incomplete(Needed::Size(9)), res3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i8_tests() {
|
||||
assert_eq!(be_i8(&[0x00]), Done(&b""[..], 0));
|
||||
assert_eq!(be_i8(&[0x7f]), Done(&b""[..], 127));
|
||||
assert_eq!(be_i8(&[0xff]), Done(&b""[..], -1));
|
||||
assert_eq!(be_i8(&[0x80]), Done(&b""[..], -128));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i16_tests() {
|
||||
assert_eq!(be_i16(&[0x00, 0x00]), Done(&b""[..], 0));
|
||||
assert_eq!(be_i16(&[0x7f, 0xff]), Done(&b""[..], 32767_i16));
|
||||
assert_eq!(be_i16(&[0xff, 0xff]), Done(&b""[..], -1));
|
||||
assert_eq!(be_i16(&[0x80, 0x00]), Done(&b""[..], -32768_i16));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i32_tests() {
|
||||
assert_eq!(be_i32(&[0x00, 0x00, 0x00, 0x00]), Done(&b""[..], 0));
|
||||
assert_eq!(be_i32(&[0x7f, 0xff, 0xff, 0xff]), Done(&b""[..], 2147483647_i32));
|
||||
assert_eq!(be_i32(&[0xff, 0xff, 0xff, 0xff]), Done(&b""[..], -1));
|
||||
assert_eq!(be_i32(&[0x80, 0x00, 0x00, 0x00]), Done(&b""[..], -2147483648_i32));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i64_tests() {
|
||||
assert_eq!(be_i64(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), Done(&b""[..], 0));
|
||||
assert_eq!(be_i64(&[0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), Done(&b""[..], 9223372036854775807_i64));
|
||||
assert_eq!(be_i64(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), Done(&b""[..], -1));
|
||||
assert_eq!(be_i64(&[0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), Done(&b""[..], -9223372036854775808_i64));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn le_i8_tests() {
|
||||
assert_eq!(le_i8(&[0x00]), Done(&b""[..], 0));
|
||||
assert_eq!(le_i8(&[0x7f]), Done(&b""[..], 127));
|
||||
assert_eq!(le_i8(&[0xff]), Done(&b""[..], -1));
|
||||
assert_eq!(le_i8(&[0x80]), Done(&b""[..], -128));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn le_i16_tests() {
|
||||
assert_eq!(le_i16(&[0x00, 0x00]), Done(&b""[..], 0));
|
||||
assert_eq!(le_i16(&[0xff, 0x7f]), Done(&b""[..], 32767_i16));
|
||||
assert_eq!(le_i16(&[0xff, 0xff]), Done(&b""[..], -1));
|
||||
assert_eq!(le_i16(&[0x00, 0x80]), Done(&b""[..], -32768_i16));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn le_i32_tests() {
|
||||
assert_eq!(le_i32(&[0x00, 0x00, 0x00, 0x00]), Done(&b""[..], 0));
|
||||
assert_eq!(le_i32(&[0xff, 0xff, 0xff, 0x7f]), Done(&b""[..], 2147483647_i32));
|
||||
assert_eq!(le_i32(&[0xff, 0xff, 0xff, 0xff]), Done(&b""[..], -1));
|
||||
assert_eq!(le_i32(&[0x00, 0x00, 0x00, 0x80]), Done(&b""[..], -2147483648_i32));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn le_i64_tests() {
|
||||
assert_eq!(le_i64(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), Done(&b""[..], 0));
|
||||
assert_eq!(le_i64(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]), Done(&b""[..], 9223372036854775807_i64));
|
||||
assert_eq!(le_i64(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), Done(&b""[..], -1));
|
||||
assert_eq!(le_i64(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]), Done(&b""[..], -9223372036854775808_i64));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn be_f32_tests() {
|
||||
assert_eq!(be_f32(&[0x00, 0x00, 0x00, 0x00]), Done(&b""[..], 0_f32));
|
||||
assert_eq!(be_f32(&[0x4d, 0x31, 0x1f, 0xd8]), Done(&b""[..], 185728392_f32));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn be_f64_tests() {
|
||||
assert_eq!(be_f64(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), Done(&b""[..], 0_f64));
|
||||
assert_eq!(be_f64(&[0x41, 0xa6, 0x23, 0xfb, 0x10, 0x00, 0x00, 0x00]), Done(&b""[..], 185728392_f64));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn le_f32_tests() {
|
||||
assert_eq!(le_f32(&[0x00, 0x00, 0x00, 0x00]), Done(&b""[..], 0_f32));
|
||||
assert_eq!(le_f32(&[0xd8, 0x1f, 0x31, 0x4d]), Done(&b""[..], 185728392_f32));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn le_f64_tests() {
|
||||
assert_eq!(le_f64(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), Done(&b""[..], 0_f64));
|
||||
assert_eq!(le_f64(&[0x00, 0x00, 0x00, 0x10, 0xfb, 0x23, 0xa6, 0x41]), Done(&b""[..], 185728392_f64));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hex_u32_tests() {
|
||||
assert_eq!(hex_u32(&b""[..]), Done(&b""[..], 0));
|
||||
assert_eq!(hex_u32(&b"ff"[..]), Done(&b""[..], 255));
|
||||
assert_eq!(hex_u32(&b"1be2"[..]), Done(&b""[..], 7138));
|
||||
assert_eq!(hex_u32(&b"c5a31be2"[..]), Done(&b""[..], 3315801058));
|
||||
assert_eq!(hex_u32(&b"00c5a31be2"[..]), Done(&b"e2"[..], 12952347));
|
||||
assert_eq!(hex_u32(&b"c5a31be201"[..]), Done(&b"01"[..], 3315801058));
|
||||
assert_eq!(hex_u32(&b"ffffffff"[..]), Done(&b""[..], 4294967295));
|
||||
assert_eq!(hex_u32(&b"0x1be2"[..]), Done(&b"x1be2"[..], 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn end_of_input() {
|
||||
let not_over = &b"Hello, world!"[..];
|
||||
let is_over = &b""[..];
|
||||
|
||||
let res_not_over = eof(not_over);
|
||||
assert_eq!(res_not_over, Error(Position(ErrorKind::Eof, not_over)));
|
||||
|
||||
let res_over = eof(is_over);
|
||||
assert_eq!(res_over, Done(is_over, is_over));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn configurable_endianness() {
|
||||
named!(be_tst16<u16>, u16!(true));
|
||||
named!(le_tst16<u16>, u16!(false));
|
||||
assert_eq!(be_tst16(&[0x80, 0x00]), Done(&b""[..], 32768_u16));
|
||||
assert_eq!(le_tst16(&[0x80, 0x00]), Done(&b""[..], 128_u16));
|
||||
|
||||
named!(be_tst32<u32>, u32!(true));
|
||||
named!(le_tst32<u32>, u32!(false));
|
||||
assert_eq!(be_tst32(&[0x12, 0x00, 0x60, 0x00]), Done(&b""[..], 302014464_u32));
|
||||
assert_eq!(le_tst32(&[0x12, 0x00, 0x60, 0x00]), Done(&b""[..], 6291474_u32));
|
||||
|
||||
named!(be_tst64<u64>, u64!(true));
|
||||
named!(le_tst64<u64>, u64!(false));
|
||||
assert_eq!(be_tst64(&[0x12, 0x00, 0x60, 0x00, 0x12, 0x00, 0x80, 0x00]), Done(&b""[..], 1297142246100992000_u64));
|
||||
assert_eq!(le_tst64(&[0x12, 0x00, 0x60, 0x00, 0x12, 0x00, 0x80, 0x00]), Done(&b""[..], 36028874334666770_u64));
|
||||
|
||||
named!(be_tsti16<i16>, i16!(true));
|
||||
named!(le_tsti16<i16>, i16!(false));
|
||||
assert_eq!(be_tsti16(&[0x00, 0x80]), Done(&b""[..], 128_i16));
|
||||
assert_eq!(le_tsti16(&[0x00, 0x80]), Done(&b""[..], -32768_i16));
|
||||
|
||||
named!(be_tsti32<i32>, i32!(true));
|
||||
named!(le_tsti32<i32>, i32!(false));
|
||||
assert_eq!(be_tsti32(&[0x00, 0x12, 0x60, 0x00]), Done(&b""[..], 1204224_i32));
|
||||
assert_eq!(le_tsti32(&[0x00, 0x12, 0x60, 0x00]), Done(&b""[..], 6296064_i32));
|
||||
|
||||
named!(be_tsti64<i64>, i64!(true));
|
||||
named!(le_tsti64<i64>, i64!(false));
|
||||
assert_eq!(be_tsti64(&[0x00, 0xFF, 0x60, 0x00, 0x12, 0x00, 0x80, 0x00]), Done(&b""[..], 71881672479506432_i64));
|
||||
assert_eq!(le_tsti64(&[0x00, 0xFF, 0x60, 0x00, 0x12, 0x00, 0x80, 0x00]), Done(&b""[..], 36028874334732032_i64));
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manual_configurable_endianness_test() {
|
||||
let x = 1;
|
||||
let int_parse: Box<Fn(&[u8]) -> IResult<&[u8], u16> > = if x == 2 {
|
||||
Box::new(be_u16)
|
||||
} else {
|
||||
Box::new(le_u16)
|
||||
};
|
||||
println!("{:?}", int_parse(&b"3"[..]));
|
||||
assert_eq!(int_parse(&[0x80, 0x00]), Done(&b""[..], 128_u16));
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn custom_error(input: &[u8]) -> IResult<&[u8], &[u8], ()> {
|
||||
fix_error!(input, (), alphanumeric)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hex_digit_test() {
|
||||
let empty = &b""[..];
|
||||
|
||||
let i = &b"0123456789abcdefABCDEF"[..];
|
||||
assert_eq!(hex_digit(i), Done(empty, i));
|
||||
|
||||
let i = &b"g"[..];
|
||||
assert_eq!(hex_digit(i), Error(Position(ErrorKind::HexDigit,i)));
|
||||
|
||||
let i = &b"G"[..];
|
||||
assert_eq!(hex_digit(i), Error(Position(ErrorKind::HexDigit,i)));
|
||||
|
||||
assert!(is_hex_digit(b'0'));
|
||||
assert!(is_hex_digit(b'9'));
|
||||
assert!(is_hex_digit(b'a'));
|
||||
assert!(is_hex_digit(b'f'));
|
||||
assert!(is_hex_digit(b'A'));
|
||||
assert!(is_hex_digit(b'F'));
|
||||
assert!(!is_hex_digit(b'g'));
|
||||
assert!(!is_hex_digit(b'G'));
|
||||
assert!(!is_hex_digit(b'/'));
|
||||
assert!(!is_hex_digit(b':'));
|
||||
assert!(!is_hex_digit(b'@'));
|
||||
assert!(!is_hex_digit(b'\x60'));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn oct_digit_test() {
|
||||
let empty = &b""[..];
|
||||
|
||||
let i = &b"01234567"[..];
|
||||
assert_eq!(oct_digit(i), Done(empty, i));
|
||||
|
||||
let i = &b"8"[..];
|
||||
assert_eq!(oct_digit(i), Error(Position(ErrorKind::OctDigit,i)));
|
||||
|
||||
assert!(is_oct_digit(b'0'));
|
||||
assert!(is_oct_digit(b'7'));
|
||||
assert!(!is_oct_digit(b'8'));
|
||||
assert!(!is_oct_digit(b'9'));
|
||||
assert!(!is_oct_digit(b'a'));
|
||||
assert!(!is_oct_digit(b'A'));
|
||||
assert!(!is_oct_digit(b'/'));
|
||||
assert!(!is_oct_digit(b':'));
|
||||
assert!(!is_oct_digit(b'@'));
|
||||
assert!(!is_oct_digit(b'\x60'));
|
||||
}
|
||||
}
|
|
@ -1,644 +0,0 @@
|
|||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! regex (
|
||||
($re: ident, $s:expr) => (
|
||||
lazy_static! {
|
||||
static ref $re: ::regex::Regex = ::regex::Regex::new($s).unwrap();
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! regex_bytes (
|
||||
($re: ident, $s:expr) => (
|
||||
lazy_static! {
|
||||
static ref $re: ::regex::bytes::Regex = ::regex::bytes::Regex::new($s).unwrap();
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
|
||||
/// `re_match!(regexp) => &[T] -> IResult<&[T], &[T]>`
|
||||
/// Returns the whole input if a match is found
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_match (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
use $crate::InputLength;
|
||||
let re = ::regex::Regex::new($re).unwrap();
|
||||
if re.is_match($i) {
|
||||
$crate::IResult::Done(&$i[$i.input_len()..], $i)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpMatch))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_match_static!(regexp) => &[T] -> IResult<&[T], &[T]>`
|
||||
/// Returns the whole input if a match is found. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_match_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
use $crate::InputLength;
|
||||
regex!(RE, $re);
|
||||
if RE.is_match($i) {
|
||||
$crate::IResult::Done(&$i[$i.input_len()..], $i)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpMatch))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
/// `re_bytes_match!(regexp) => &[T] -> IResult<&[T], &[T]>`
|
||||
/// Returns the whole input if a match is found
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_match (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
use $crate::InputLength;
|
||||
let re = ::regex::bytes::Regex::new($re).unwrap();
|
||||
if re.is_match($i) {
|
||||
$crate::IResult::Done(&$i[$i.input_len()..], $i)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpMatch))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_bytes_match_static!(regexp) => &[T] -> IResult<&[T], &[T]>`
|
||||
/// Returns the whole input if a match is found. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_match_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
use $crate::InputLength;
|
||||
regex_bytes!(RE, $re);
|
||||
if RE.is_match($i) {
|
||||
$crate::IResult::Done(&$i[$i.input_len()..], $i)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpMatch))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
/// `re_find!(regexp) => &[T] -> IResult<&[T], &[T]>`
|
||||
/// Returns the first match
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_find (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
let re = ::regex::Regex::new($re).unwrap();
|
||||
if let Some((begin, end)) = re.find($i) {
|
||||
$crate::IResult::Done(&$i[end..], &$i[begin..end])
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpFind))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_find_static!(regexp) => &[T] -> IResult<&[T], &[T]>`
|
||||
/// Returns the first match. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_find_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
regex!(RE, $re);
|
||||
if let Some((begin, end)) = RE.find($i) {
|
||||
$crate::IResult::Done(&$i[end..], &$i[begin..end])
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpFind))
|
||||
}
|
||||
}
|
||||
|
||||
)
|
||||
);
|
||||
|
||||
/// `re_bytes_find!(regexp) => &[T] -> IResult<&[T], &[T]>`
|
||||
/// Returns the first match
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_find (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
let re = ::regex::bytes::Regex::new($re).unwrap();
|
||||
if let Some((begin, end)) = re.find($i) {
|
||||
$crate::IResult::Done(&$i[end..], &$i[begin..end])
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpFind))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_bytes_find!(regexp) => &[T] -> IResult<&[T], &[T]>`
|
||||
/// Returns the first match. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_find_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
regex_bytes!(RE, $re);
|
||||
if let Some((begin, end)) = RE.find($i) {
|
||||
$crate::IResult::Done(&$i[end..], &$i[begin..end])
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpFind))
|
||||
}
|
||||
}
|
||||
|
||||
)
|
||||
);
|
||||
|
||||
/// `re_matches!(regexp) => &[T] -> IResult<&[T], Vec<&[T]>>`
|
||||
/// Returns all the matched parts
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_matches (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
let re = ::regex::Regex::new($re).unwrap();
|
||||
let v: Vec<&str> = re.find_iter($i).map(|(begin,end)| &$i[begin..end]).collect();
|
||||
if v.len() != 0 {
|
||||
let offset = {
|
||||
let end = v.last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done(&$i[offset..], v)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpMatches))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_matches_static!(regexp) => &[T] -> IResult<&[T], Vec<&[T]>>`
|
||||
/// Returns all the matched parts. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_matches_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
regex!(RE, $re);
|
||||
let v: Vec<&str> = RE.find_iter($i).map(|(begin,end)| &$i[begin..end]).collect();
|
||||
if v.len() != 0 {
|
||||
let offset = {
|
||||
let end = v.last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done(&$i[offset..], v)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpMatches))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
/// `re_bytes_matches!(regexp) => &[T] -> IResult<&[T], Vec<&[T]>>`
|
||||
/// Returns all the matched parts
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_matches (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
let re = ::regex::bytes::Regex::new($re).unwrap();
|
||||
let v: Vec<&[u8]> = re.find_iter($i).map(|(begin,end)| &$i[begin..end]).collect();
|
||||
if v.len() != 0 {
|
||||
let offset = {
|
||||
let end = v.last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done(&$i[offset..], v)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpMatches))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_bytes_matches_static!(regexp) => &[T] -> IResult<&[T], Vec<&[T]>>`
|
||||
/// Returns all the matched parts. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_matches_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
regex_bytes!(RE, $re);
|
||||
let v: Vec<&[u8]> = RE.find_iter($i).map(|(begin,end)| &$i[begin..end]).collect();
|
||||
if v.len() != 0 {
|
||||
let offset = {
|
||||
let end = v.last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done(&$i[offset..], v)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpMatches))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
/// `re_capture!(regexp) => &[T] -> IResult<&[T], Vec<&[T]>>`
|
||||
/// Returns the first capture group
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_capture (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
let re = ::regex::Regex::new($re).unwrap();
|
||||
if let Some(c) = re.captures($i) {
|
||||
let v:Vec<&str> = c.iter_pos().filter(|el| el.is_some()).map(|el| el.unwrap()).map(|(begin,end)| &$i[begin..end]).collect();
|
||||
let offset = {
|
||||
let end = v.last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done(&$i[offset..], v)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpCapture))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_capture_static!(regexp) => &[T] -> IResult<&[T], Vec<&[T]>>`
|
||||
/// Returns the first capture group. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_capture_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
regex!(RE, $re);
|
||||
if let Some(c) = RE.captures($i) {
|
||||
let v:Vec<&str> = c.iter_pos().filter(|el| el.is_some()).map(|el| el.unwrap()).map(|(begin,end)| &$i[begin..end]).collect();
|
||||
let offset = {
|
||||
let end = v.last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done(&$i[offset..], v)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpCapture))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
/// `re_bytes_capture!(regexp) => &[T] -> IResult<&[T], Vec<&[T]>>`
|
||||
/// Returns the first capture group
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_capture (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
let re = ::regex::bytes::Regex::new($re).unwrap();
|
||||
if let Some(c) = re.captures($i) {
|
||||
let v:Vec<&[u8]> = c.iter_pos().filter(|el| el.is_some()).map(|el| el.unwrap()).map(|(begin,end)| &$i[begin..end]).collect();
|
||||
let offset = {
|
||||
let end = v.last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done(&$i[offset..], v)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpCapture))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_bytes_capture_static!(regexp) => &[T] -> IResult<&[T], Vec<&[T]>>`
|
||||
/// Returns the first capture group. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_capture_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
regex_bytes!(RE, $re);
|
||||
if let Some(c) = RE.captures($i) {
|
||||
let v:Vec<&[u8]> = c.iter_pos().filter(|el| el.is_some()).map(|el| el.unwrap()).map(|(begin,end)| &$i[begin..end]).collect();
|
||||
let offset = {
|
||||
let end = v.last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done(&$i[offset..], v)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpCapture))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
/// `re_captures!(regexp) => &[T] -> IResult<&[T], Vec<Vec<&[T]>>>`
|
||||
/// Returns all the capture groups
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_captures (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
let re = ::regex::Regex::new($re).unwrap();
|
||||
let v:Vec<Vec<&str>> = re.captures_iter($i).map(|c| c.iter_pos().filter(|el| el.is_some()).map(|el| el.unwrap()).map(|(begin,end)| &$i[begin..end]).collect()).collect();
|
||||
if v.len() != 0 {
|
||||
let offset = {
|
||||
let end = v.last().unwrap().last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done(&$i[offset..], v)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpCapture))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_captures_static!(regexp) => &[T] -> IResult<&[T], Vec<Vec<&[T]>>>`
|
||||
/// Returns all the capture groups. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_captures_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
regex!(RE, $re);
|
||||
let v:Vec<Vec<&str>> = RE.captures_iter($i).map(|c| c.iter_pos().filter(|el| el.is_some()).map(|el| el.unwrap()).map(|(begin,end)| &$i[begin..end]).collect()).collect();
|
||||
if v.len() != 0 {
|
||||
let offset = {
|
||||
let end = v.last().unwrap().last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done(&$i[offset..], v)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpCapture))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
/// `re_bytes_captures!(regexp) => &[T] -> IResult<&[T], Vec<Vec<&[T]>>>`
|
||||
/// Returns all the capture groups
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_captures (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
let re = ::regex::bytes::Regex::new($re).unwrap();
|
||||
let v:Vec<Vec<&[u8]>> = re.captures_iter($i).map(|c| c.iter_pos().filter(|el| el.is_some()).map(|el| el.unwrap()).map(|(begin,end)| &$i[begin..end]).collect()).collect();
|
||||
if v.len() != 0 {
|
||||
let offset = {
|
||||
let end = v.last().unwrap().last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done(&$i[offset..], v)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpCapture))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_bytes_captures_static!(regexp) => &[T] -> IResult<&[T], Vec<Vec<&[T]>>>`
|
||||
/// Returns all the capture groups. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_captures_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
regex_bytes!(RE, $re);
|
||||
let v:Vec<Vec<&[u8]>> = RE.captures_iter($i).map(|c| c.iter_pos().filter(|el| el.is_some()).map(|el| el.unwrap()).map(|(begin,end)| &$i[begin..end]).collect()).collect();
|
||||
if v.len() != 0 {
|
||||
let offset = {
|
||||
let end = v.last().unwrap().last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done(&$i[offset..], v)
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Code($crate::ErrorKind::RegexpCapture))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use internal::IResult::*;
|
||||
use internal::Err::*;
|
||||
use util::ErrorKind;
|
||||
|
||||
#[test]
|
||||
fn re_match() {
|
||||
named!(rm<&str,&str>, re_match!(r"^\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm("2015-09-07"), Done("", "2015-09-07"));
|
||||
assert_eq!(rm("blah"), Error(Code(ErrorKind::RegexpMatch)));
|
||||
assert_eq!(rm("2015-09-07blah"), Done("", "2015-09-07blah"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_match_static() {
|
||||
named!(rm<&str,&str>, re_match_static!(r"^\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm("2015-09-07"), Done("", "2015-09-07"));
|
||||
assert_eq!(rm("blah"), Error(Code(ErrorKind::RegexpMatch)));
|
||||
assert_eq!(rm("2015-09-07blah"), Done("", "2015-09-07blah"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn re_find() {
|
||||
named!(rm<&str,&str>, re_find!(r"^\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm("2015-09-07"), Done("", "2015-09-07"));
|
||||
assert_eq!(rm("blah"), Error(Code(ErrorKind::RegexpFind)));
|
||||
assert_eq!(rm("2015-09-07blah"), Done("blah", "2015-09-07"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_find_static() {
|
||||
named!(rm<&str,&str>, re_find_static!(r"^\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm("2015-09-07"), Done("", "2015-09-07"));
|
||||
assert_eq!(rm("blah"), Error(Code(ErrorKind::RegexpFind)));
|
||||
assert_eq!(rm("2015-09-07blah"), Done("blah", "2015-09-07"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn re_matches() {
|
||||
named!(rm< &str,Vec<&str> >, re_matches!(r"\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm("2015-09-07"), Done("", vec!["2015-09-07"]));
|
||||
assert_eq!(rm("blah"), Error(Code(ErrorKind::RegexpMatches)));
|
||||
assert_eq!(rm("aaa2015-09-07blah2015-09-09pouet"), Done("pouet", vec!["2015-09-07", "2015-09-09"]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_matches_static() {
|
||||
named!(rm< &str,Vec<&str> >, re_matches_static!(r"\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm("2015-09-07"), Done("", vec!["2015-09-07"]));
|
||||
assert_eq!(rm("blah"), Error(Code(ErrorKind::RegexpMatches)));
|
||||
assert_eq!(rm("aaa2015-09-07blah2015-09-09pouet"), Done("pouet", vec!["2015-09-07", "2015-09-09"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn re_capture() {
|
||||
named!(rm< &str,Vec<&str> >, re_capture!(r"([:alpha:]+)\s+((\d+).(\d+).(\d+))"));
|
||||
assert_eq!(rm("blah nom 0.3.11pouet"), Done("pouet", vec!["nom 0.3.11", "nom", "0.3.11", "0", "3", "11"]));
|
||||
assert_eq!(rm("blah"), Error(Code(ErrorKind::RegexpCapture)));
|
||||
assert_eq!(rm("hello nom 0.3.11 world regex 0.1.41"), Done(" world regex 0.1.41", vec!["nom 0.3.11", "nom", "0.3.11", "0", "3", "11"]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_capture_static() {
|
||||
named!(rm< &str,Vec<&str> >, re_capture_static!(r"([:alpha:]+)\s+((\d+).(\d+).(\d+))"));
|
||||
assert_eq!(rm("blah nom 0.3.11pouet"), Done("pouet", vec!["nom 0.3.11", "nom", "0.3.11", "0", "3", "11"]));
|
||||
assert_eq!(rm("blah"), Error(Code(ErrorKind::RegexpCapture)));
|
||||
assert_eq!(rm("hello nom 0.3.11 world regex 0.1.41"), Done(" world regex 0.1.41", vec!["nom 0.3.11", "nom", "0.3.11", "0", "3", "11"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn re_captures() {
|
||||
named!(rm< &str,Vec<Vec<&str>> >, re_captures!(r"([:alpha:]+)\s+((\d+).(\d+).(\d+))"));
|
||||
assert_eq!(rm("blah nom 0.3.11pouet"), Done("pouet", vec![vec!["nom 0.3.11", "nom", "0.3.11", "0", "3", "11"]]));
|
||||
assert_eq!(rm("blah"), Error(Code(ErrorKind::RegexpCapture)));
|
||||
assert_eq!(rm("hello nom 0.3.11 world regex 0.1.41 aaa"), Done(" aaa", vec![
|
||||
vec!["nom 0.3.11", "nom", "0.3.11", "0", "3", "11"],
|
||||
vec!["regex 0.1.41", "regex", "0.1.41", "0", "1", "41"],
|
||||
]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_captures_static() {
|
||||
named!(rm< &str,Vec<Vec<&str>> >, re_captures_static!(r"([:alpha:]+)\s+((\d+).(\d+).(\d+))"));
|
||||
assert_eq!(rm("blah nom 0.3.11pouet"), Done("pouet", vec![vec!["nom 0.3.11", "nom", "0.3.11", "0", "3", "11"]]));
|
||||
assert_eq!(rm("blah"), Error(Code(ErrorKind::RegexpCapture)));
|
||||
assert_eq!(rm("hello nom 0.3.11 world regex 0.1.41 aaa"), Done(" aaa", vec![
|
||||
vec!["nom 0.3.11", "nom", "0.3.11", "0", "3", "11"],
|
||||
vec!["regex 0.1.41", "regex", "0.1.41", "0", "1", "41"],
|
||||
]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn re_bytes_match() {
|
||||
named!(rm, re_bytes_match!(r"^\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm(&b"2015-09-07"[..]), Done(&b""[..], &b"2015-09-07"[..]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(Code(ErrorKind::RegexpMatch)));
|
||||
assert_eq!(rm(&b"2015-09-07blah"[..]), Done(&b""[..], &b"2015-09-07blah"[..]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_bytes_match_static() {
|
||||
named!(rm, re_bytes_match_static!(r"^\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm(&b"2015-09-07"[..]), Done(&b""[..], &b"2015-09-07"[..]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(Code(ErrorKind::RegexpMatch)));
|
||||
assert_eq!(rm(&b"2015-09-07blah"[..]), Done(&b""[..], &b"2015-09-07blah"[..]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn re_bytes_find() {
|
||||
named!(rm, re_bytes_find!(r"^\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm(&b"2015-09-07"[..]), Done(&b""[..], &b"2015-09-07"[..]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(Code(ErrorKind::RegexpFind)));
|
||||
assert_eq!(rm(&b"2015-09-07blah"[..]), Done(&b"blah"[..], &b"2015-09-07"[..]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_bytes_find_static() {
|
||||
named!(rm, re_bytes_find_static!(r"^\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm(&b"2015-09-07"[..]), Done(&b""[..], &b"2015-09-07"[..]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(Code(ErrorKind::RegexpFind)));
|
||||
assert_eq!(rm(&b"2015-09-07blah"[..]), Done(&b"blah"[..], &b"2015-09-07"[..]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn re_bytes_matches() {
|
||||
named!(rm<Vec<&[u8]> >, re_bytes_matches!(r"\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm(&b"2015-09-07"[..]), Done(&b""[..], vec![&b"2015-09-07"[..]]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(Code(ErrorKind::RegexpMatches)));
|
||||
assert_eq!(rm(&b"aaa2015-09-07blah2015-09-09pouet"[..]), Done(&b"pouet"[..], vec![&b"2015-09-07"[..], &b"2015-09-09"[..]]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_bytes_matches_static() {
|
||||
named!(rm<Vec<&[u8]> >, re_bytes_matches_static!(r"\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm(&b"2015-09-07"[..]), Done(&b""[..], vec![&b"2015-09-07"[..]]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(Code(ErrorKind::RegexpMatches)));
|
||||
assert_eq!(rm(&b"aaa2015-09-07blah2015-09-09pouet"[..]), Done(&b"pouet"[..], vec![&b"2015-09-07"[..], &b"2015-09-09"[..]]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn re_bytes_capture() {
|
||||
named!(rm<Vec<&[u8]> >, re_bytes_capture!(r"([:alpha:]+)\s+((\d+).(\d+).(\d+))"));
|
||||
assert_eq!(rm(&b"blah nom 0.3.11pouet"[..]), Done(&b"pouet"[..], vec![&b"nom 0.3.11"[..], &b"nom"[..], &b"0.3.11"[..], &b"0"[..], &b"3"[..], &b"11"[..]]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(Code(ErrorKind::RegexpCapture)));
|
||||
assert_eq!(rm(&b"hello nom 0.3.11 world regex 0.1.41"[..]), Done(&b" world regex 0.1.41"[..], vec![&b"nom 0.3.11"[..], &b"nom"[..], &b"0.3.11"[..], &b"0"[..], &b"3"[..], &b"11"[..]]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_bytes_capture_static() {
|
||||
named!(rm< Vec<&[u8]> >, re_bytes_capture_static!(r"([:alpha:]+)\s+((\d+).(\d+).(\d+))"));
|
||||
assert_eq!(rm(&b"blah nom 0.3.11pouet"[..]), Done(&b"pouet"[..], vec![&b"nom 0.3.11"[..], &b"nom"[..], &b"0.3.11"[..], &b"0"[..], &b"3"[..], &b"11"[..]]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(Code(ErrorKind::RegexpCapture)));
|
||||
assert_eq!(rm(&b"hello nom 0.3.11 world regex 0.1.41"[..]), Done(&b" world regex 0.1.41"[..], vec![&b"nom 0.3.11"[..], &b"nom"[..], &b"0.3.11"[..], &b"0"[..], &b"3"[..], &b"11"[..]]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn re_bytes_captures() {
|
||||
named!(rm< Vec<Vec<&[u8]>> >, re_bytes_captures!(r"([:alpha:]+)\s+((\d+).(\d+).(\d+))"));
|
||||
assert_eq!(rm(&b"blah nom 0.3.11pouet"[..]), Done(&b"pouet"[..], vec![vec![&b"nom 0.3.11"[..], &b"nom"[..], &b"0.3.11"[..], &b"0"[..], &b"3"[..], &b"11"[..]]]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(Code(ErrorKind::RegexpCapture)));
|
||||
assert_eq!(rm(&b"hello nom 0.3.11 world regex 0.1.41 aaa"[..]), Done(&b" aaa"[..], vec![
|
||||
vec![&b"nom 0.3.11"[..], &b"nom"[..], &b"0.3.11"[..], &b"0"[..], &b"3"[..], &b"11"[..]],
|
||||
vec![&b"regex 0.1.41"[..], &b"regex"[..], &b"0.1.41"[..], &b"0"[..], &b"1"[..], &b"41"[..]],
|
||||
]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_bytes_captures_static() {
|
||||
named!(rm< Vec<Vec<&[u8]>> >, re_bytes_captures_static!(r"([:alpha:]+)\s+((\d+).(\d+).(\d+))"));
|
||||
assert_eq!(rm(&b"blah nom 0.3.11pouet"[..]), Done(&b"pouet"[..], vec![vec![&b"nom 0.3.11"[..], &b"nom"[..], &b"0.3.11"[..], &b"0"[..], &b"3"[..], &b"11"[..]]]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(Code(ErrorKind::RegexpCapture)));
|
||||
assert_eq!(rm(&b"hello nom 0.3.11 world regex 0.1.41 aaa"[..]), Done(&b" aaa"[..], vec![
|
||||
vec![&b"nom 0.3.11"[..], &b"nom"[..], &b"0.3.11"[..], &b"0"[..], &b"3"[..], &b"11"[..]],
|
||||
vec![&b"regex 0.1.41"[..], &b"regex"[..], &b"0.1.41"[..], &b"0"[..], &b"1"[..], &b"41"[..]],
|
||||
]));
|
||||
}
|
||||
}
|
|
@ -1,734 +0,0 @@
|
|||
//! Parsers and helper functions operating on strings, especially useful when writing parsers for
|
||||
//! text-based formats.
|
||||
|
||||
/// `tag_s!(&str) => &str -> IResult<&str, &str>`
|
||||
/// declares a string as a suite to recognize
|
||||
///
|
||||
/// consumes the recognized characters
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::{self,Done};
|
||||
/// # fn main() {
|
||||
/// fn test(input: &str) -> IResult<&str, &str> {
|
||||
/// tag_s!(input, "abcd")
|
||||
/// }
|
||||
/// let r = test("abcdefgh");
|
||||
/// assert_eq!(r, Done("efgh", "abcd"));
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! tag_s (
|
||||
($i:expr, $tag: expr) => (
|
||||
{
|
||||
let res: $crate::IResult<_,_> = if $tag.len() > $i.len() {
|
||||
$crate::IResult::Incomplete($crate::Needed::Size($tag.len()))
|
||||
//} else if &$i[0..$tag.len()] == $tag {
|
||||
} else if ($i).starts_with($tag) {
|
||||
$crate::IResult::Done(&$i[$tag.len()..], &$i[0..$tag.len()])
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Position($crate::ErrorKind::TagStr, $i))
|
||||
};
|
||||
res
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// `take_s!(nb) => &str -> IResult<&str, &str>`
|
||||
/// generates a parser consuming the specified number of characters
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::Done;
|
||||
/// # fn main() {
|
||||
/// // Desmond parser
|
||||
/// named!(take5<&str,&str>, take_s!( 5 ) );
|
||||
///
|
||||
/// let a = "abcdefgh";
|
||||
///
|
||||
/// assert_eq!(take5(a), Done("fgh", "abcde"));
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! take_s (
|
||||
($i:expr, $count:expr) => (
|
||||
{
|
||||
let cnt = $count as usize;
|
||||
let res: $crate::IResult<_,_> = if $i.chars().count() < cnt {
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(cnt))
|
||||
} else {
|
||||
let mut offset = $i.len();
|
||||
let mut count = 0;
|
||||
for (o, _) in $i.char_indices() {
|
||||
if count == cnt {
|
||||
offset = o;
|
||||
break;
|
||||
}
|
||||
count += 1;
|
||||
}
|
||||
$crate::IResult::Done(&$i[offset..], &$i[..offset])
|
||||
};
|
||||
res
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
|
||||
/// `is_not_s!(&str) => &str -> IResult<&str, &str>`
|
||||
/// returns the longest list of characters that do not appear in the provided array
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::Done;
|
||||
/// # fn main() {
|
||||
/// named!( not_space<&str,&str>, is_not_s!( " \t\r\n" ) );
|
||||
///
|
||||
/// let r = not_space("abcdefgh\nijkl");
|
||||
/// assert_eq!(r, Done("\nijkl", "abcdefgh"));
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! is_not_s (
|
||||
($input:expr, $arr:expr) => (
|
||||
{
|
||||
use std::collections::HashSet;
|
||||
let set: HashSet<char> = $arr.chars().collect();
|
||||
let mut offset = $input.len();
|
||||
for (o, c) in $input.char_indices() {
|
||||
if set.contains(&c) {
|
||||
offset = o;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if offset == 0 {
|
||||
$crate::IResult::Error($crate::Err::Position($crate::ErrorKind::IsAStr,$input))
|
||||
} else if offset < $input.len() {
|
||||
$crate::IResult::Done(&$input[offset..], &$input[..offset])
|
||||
} else {
|
||||
$crate::IResult::Done("", $input)
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// `is_a_s!(&str) => &str -> IResult<&str, &str>`
|
||||
/// returns the longest list of characters that appear in the provided array
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::Done;
|
||||
/// # fn main() {
|
||||
/// named!(abcd<&str, &str>, is_a_s!( "abcd" ));
|
||||
///
|
||||
/// let r1 = abcd("aaaaefgh");
|
||||
/// assert_eq!(r1, Done("efgh", "aaaa"));
|
||||
///
|
||||
/// let r2 = abcd("dcbaefgh");
|
||||
/// assert_eq!(r2, Done("efgh", "dcba"));
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! is_a_s (
|
||||
($input:expr, $arr:expr) => (
|
||||
{
|
||||
use std::collections::HashSet;
|
||||
let set: HashSet<char> = $arr.chars().collect();
|
||||
let mut offset = $input.len();
|
||||
for (o, c) in $input.char_indices() {
|
||||
if !set.contains(&c) {
|
||||
offset = o;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if offset == 0 {
|
||||
$crate::IResult::Error($crate::Err::Position($crate::ErrorKind::IsAStr,$input))
|
||||
} else if offset < $input.len() {
|
||||
$crate::IResult::Done(&$input[offset..], &$input[..offset])
|
||||
} else {
|
||||
$crate::IResult::Done("", $input)
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
|
||||
/// `take_while_s!(char -> bool) => &str -> IResult<&str, &str>`
|
||||
/// returns the longest list of characters until the provided function fails.
|
||||
///
|
||||
/// The argument is either a function `char -> bool` or a macro returning a `bool
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::Done;
|
||||
/// # use nom::is_alphanumeric;
|
||||
/// # fn main() {
|
||||
/// fn alphabetic(chr: char) -> bool { (chr >= 0x41 as char && chr <= 0x5A as char) || (chr >= 0x61 as char && chr <= 0x7A as char) }
|
||||
/// named!( alpha<&str,&str>, take_while_s!( alphabetic ) );
|
||||
///
|
||||
/// let r = alpha("abcd\nefgh");
|
||||
/// assert_eq!(r, Done("\nefgh", "abcd"));
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! take_while_s (
|
||||
($input:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
{
|
||||
let mut offset = $input.len();
|
||||
for (o, c) in $input.char_indices() {
|
||||
if !$submac!(c, $($args)*) {
|
||||
offset = o;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if offset < $input.len() {
|
||||
$crate::IResult::Done(&$input[offset..], &$input[..offset])
|
||||
} else {
|
||||
$crate::IResult::Done("", $input)
|
||||
}
|
||||
}
|
||||
);
|
||||
($input:expr, $f:expr) => (
|
||||
take_while_s!($input, call!($f));
|
||||
);
|
||||
);
|
||||
|
||||
/// `take_while1_s!(char -> bool) => &str -> IResult<&str, &str>`
|
||||
/// returns the longest (non empty) list of characters until the provided function fails.
|
||||
///
|
||||
/// The argument is either a function `char -> bool` or a macro returning a `bool`
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::Done;
|
||||
/// # use nom::is_alphanumeric;
|
||||
/// # fn main() {
|
||||
/// fn alphabetic(chr: char) -> bool { (chr >= 0x41 as char && chr <= 0x5A as char) || (chr >= 0x61 as char && chr <= 0x7A as char) }
|
||||
/// named!( alpha<&str,&str>, take_while1_s!( alphabetic ) );
|
||||
///
|
||||
/// let r = alpha("abcd\nefgh");
|
||||
/// assert_eq!(r, Done("\nefgh", "abcd"));
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! take_while1_s (
|
||||
($input:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
{
|
||||
let mut offset = $input.len();
|
||||
for (o, c) in $input.char_indices() {
|
||||
if !$submac!(c, $($args)*) {
|
||||
offset = o;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if offset == 0 {
|
||||
$crate::IResult::Error($crate::Err::Position($crate::ErrorKind::TakeWhile1Str,$input))
|
||||
} else if offset < $input.len() {
|
||||
$crate::IResult::Done(&$input[offset..], &$input[..offset])
|
||||
} else {
|
||||
$crate::IResult::Done("", $input)
|
||||
}
|
||||
}
|
||||
);
|
||||
($input:expr, $f:expr) => (
|
||||
take_while1_s!($input, call!($f));
|
||||
);
|
||||
);
|
||||
|
||||
|
||||
/// `take_till_s!(&str -> bool) => &str -> IResult<&str, &str>`
|
||||
/// returns the longest list of characters until the provided function succeeds
|
||||
///
|
||||
/// The argument is either a function `char -> bool` or a macro returning a `bool
|
||||
#[macro_export]
|
||||
macro_rules! take_till_s (
|
||||
($input:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
|
||||
{
|
||||
let mut offset = $input.len();
|
||||
for (o, c) in $input.char_indices() {
|
||||
if $submac!(c, $($args)*) {
|
||||
offset = o;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if offset < $input.len() {
|
||||
$crate::IResult::Done(&$input[offset..], &$input[..offset])
|
||||
} else {
|
||||
$crate::IResult::Done("", $input)
|
||||
}
|
||||
}
|
||||
);
|
||||
($input:expr, $f:expr) => (
|
||||
take_till_s!($input, call!($f));
|
||||
);
|
||||
);
|
||||
|
||||
/// `take_until_and_consume_s!(&str) => &str -> IResult<&str, &str>`
|
||||
/// generates a parser consuming all chars until the specified string is found and consumes it
|
||||
#[macro_export]
|
||||
macro_rules! take_until_and_consume_s (
|
||||
($input:expr, $substr:expr) => (
|
||||
{
|
||||
#[inline(always)]
|
||||
fn shift_window_and_cmp(window: & mut ::std::vec::Vec<char>, c: char, substr_vec: & ::std::vec::Vec<char>) -> bool {
|
||||
window.push(c);
|
||||
if window.len() > substr_vec.len() {
|
||||
window.remove(0);
|
||||
}
|
||||
window == substr_vec
|
||||
}
|
||||
let res: $crate::IResult<_, _> = if $substr.len() > $input.len() {
|
||||
$crate::IResult::Incomplete($crate::Needed::Size($substr.len()))
|
||||
} else {
|
||||
let substr_vec: ::std::vec::Vec<char> = $substr.chars().collect();
|
||||
let mut window: ::std::vec::Vec<char> = vec![];
|
||||
let mut offset = $input.len();
|
||||
let mut parsed = false;
|
||||
for (o, c) in $input.char_indices() {
|
||||
if parsed {
|
||||
// The easiest way to get the byte offset of the char after the found string
|
||||
offset = o;
|
||||
break;
|
||||
}
|
||||
if shift_window_and_cmp(& mut window, c, &substr_vec) {
|
||||
parsed = true;
|
||||
}
|
||||
}
|
||||
if parsed {
|
||||
if offset < $input.len() {
|
||||
$crate::IResult::Done(&$input[offset..], &$input[..offset])
|
||||
} else {
|
||||
$crate::IResult::Done("", $input)
|
||||
}
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Position($crate::ErrorKind::TakeUntilAndConsumeStr,$input))
|
||||
}
|
||||
};
|
||||
res
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// `take_until_s!(&str) => &str -> IResult<&str, &str>`
|
||||
/// generates a parser consuming all chars until the specified string is found and leaves it in the remaining input
|
||||
#[macro_export]
|
||||
macro_rules! take_until_s (
|
||||
($input:expr, $substr:expr) => (
|
||||
{
|
||||
#[inline(always)]
|
||||
fn shift_window_and_cmp(window: & mut Vec<char>, c: char, substr_vec: &Vec<char>) -> bool {
|
||||
window.push(c);
|
||||
if window.len() > substr_vec.len() {
|
||||
window.remove(0);
|
||||
}
|
||||
window == substr_vec
|
||||
}
|
||||
let res: $crate::IResult<&str, &str> = if $substr.len() > $input.len() {
|
||||
$crate::IResult::Incomplete($crate::Needed::Size($substr.len()))
|
||||
} else {
|
||||
let substr_vec: Vec<char> = $substr.chars().collect();
|
||||
let mut window: Vec<char> = vec![];
|
||||
let mut offset = $input.len();
|
||||
let mut parsed = false;
|
||||
for (o, c) in $input.char_indices() {
|
||||
if shift_window_and_cmp(& mut window, c, &substr_vec) {
|
||||
parsed = true;
|
||||
window.pop();
|
||||
let window_len: usize = window.iter()
|
||||
.map(|x| x.len_utf8())
|
||||
.fold(0, |x, y| x + y);
|
||||
offset = o - window_len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if parsed {
|
||||
$crate::IResult::Done(&$input[offset..], &$input[..offset])
|
||||
} else {
|
||||
$crate::IResult::Error($crate::Err::Position($crate::ErrorKind::TakeUntilStr,$input))
|
||||
}
|
||||
};
|
||||
res
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use ::IResult;
|
||||
|
||||
#[test]
|
||||
fn tag_str_succeed() {
|
||||
const INPUT: &'static str = "Hello World!";
|
||||
const TAG: &'static str = "Hello";
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
tag_s!(input, TAG)
|
||||
}
|
||||
|
||||
match test(INPUT) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == " World!", "Parser `tag_s` consumed leftover input.");
|
||||
assert!(output == TAG,
|
||||
"Parser `tag_s` doesn't return the tag it matched on success. \
|
||||
Expected `{}`, got `{}`.", TAG, output);
|
||||
},
|
||||
other => panic!("Parser `tag_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tag_str_incomplete() {
|
||||
const INPUT: &'static str = "Hello";
|
||||
const TAG: &'static str = "Hello World!";
|
||||
|
||||
match tag_s!(INPUT, TAG) {
|
||||
IResult::Incomplete(_) => (),
|
||||
other => {
|
||||
panic!("Parser `tag_s` didn't require more input when it should have. \
|
||||
Got `{:?}`.", other);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tag_str_error() {
|
||||
const INPUT: &'static str = "Hello World!";
|
||||
const TAG: &'static str = "Random"; // TAG must be closer than INPUT.
|
||||
|
||||
match tag_s!(INPUT, TAG) {
|
||||
IResult::Error(_) => (),
|
||||
other => {
|
||||
panic!("Parser `tag_s` didn't fail when it should have. Got `{:?}`.`", other);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_s_succeed() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const CONSUMED: &'static str = "βèƒôřèÂßÇ";
|
||||
const LEFTOVER: &'static str = "áƒƭèř";
|
||||
|
||||
match take_s!(INPUT, 9) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == LEFTOVER, "Parser `take_s` consumed leftover input. Leftover `{}`.", extra);
|
||||
assert!(output == CONSUMED,
|
||||
"Parser `take_s` doens't return the string it consumed on success. Expected `{}`, got `{}`.",
|
||||
CONSUMED, output);
|
||||
},
|
||||
other => panic!("Parser `take_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_until_s_succeed() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇ∂áƒƭèř";
|
||||
const FIND: &'static str = "ÂßÇ∂";
|
||||
const CONSUMED: &'static str = "βèƒôřè";
|
||||
const LEFTOVER: &'static str = "ÂßÇ∂áƒƭèř";
|
||||
|
||||
match take_until_s!(INPUT, FIND) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == LEFTOVER, "Parser `take_until_s`\
|
||||
consumed leftover input. Leftover `{}`.", extra);
|
||||
assert!(output == CONSUMED, "Parser `take_until_s`\
|
||||
doens't return the string it consumed on success. Expected `{}`, got `{}`.",
|
||||
CONSUMED, output);
|
||||
}
|
||||
other => panic!("Parser `take_until_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_s_incomplete() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇá";
|
||||
|
||||
match take_s!(INPUT, 13) {
|
||||
IResult::Incomplete(_) => (),
|
||||
other => panic!("Parser `take_s` didn't require more input when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
}
|
||||
}
|
||||
|
||||
use internal::IResult::{Done, Error};
|
||||
use internal::Err::Position;
|
||||
use util::ErrorKind;
|
||||
|
||||
pub fn is_alphabetic(c:char) -> bool {
|
||||
(c as u8 >= 0x41 && c as u8 <= 0x5A) || (c as u8 >= 0x61 && c as u8 <= 0x7A)
|
||||
}
|
||||
#[test]
|
||||
fn take_while_s() {
|
||||
named!(f<&str,&str>, take_while_s!(is_alphabetic));
|
||||
let a = "";
|
||||
let b = "abcd";
|
||||
let c = "abcd123";
|
||||
let d = "123";
|
||||
|
||||
assert_eq!(f(&a[..]), Done(&a[..], &a[..]));
|
||||
assert_eq!(f(&b[..]), Done(&a[..], &b[..]));
|
||||
assert_eq!(f(&c[..]), Done(&d[..], &b[..]));
|
||||
assert_eq!(f(&d[..]), Done(&d[..], &a[..]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_while1_s() {
|
||||
named!(f<&str,&str>, take_while1_s!(is_alphabetic));
|
||||
let a = "";
|
||||
let b = "abcd";
|
||||
let c = "abcd123";
|
||||
let d = "123";
|
||||
|
||||
assert_eq!(f(&a[..]), Error(Position(ErrorKind::TakeWhile1Str, &""[..])));
|
||||
assert_eq!(f(&b[..]), Done(&a[..], &b[..]));
|
||||
assert_eq!(f(&c[..]), Done(&"123"[..], &b[..]));
|
||||
assert_eq!(f(&d[..]), Error(Position(ErrorKind::TakeWhile1Str, &d[..])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_till_s_succeed() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const CONSUMED: &'static str = "βèƒôřèÂßÇ";
|
||||
const LEFTOVER: &'static str = "áƒƭèř";
|
||||
fn till_s(c: char) -> bool {
|
||||
c == 'á'
|
||||
}
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
take_till_s!(input, till_s)
|
||||
}
|
||||
match test(INPUT) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == LEFTOVER, "Parser `take_till_s` consumed leftover input.");
|
||||
assert!(output == CONSUMED,
|
||||
"Parser `take_till_s` doesn't return the string it consumed on success. \
|
||||
Expected `{}`, got `{}`.", CONSUMED, output);
|
||||
},
|
||||
other => panic!("Parser `take_till_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_while_s_succeed_none() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const CONSUMED: &'static str = "";
|
||||
const LEFTOVER: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
fn while_s(c: char) -> bool {
|
||||
c == '9'
|
||||
}
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
take_while_s!(input, while_s)
|
||||
}
|
||||
match test(INPUT) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == LEFTOVER, "Parser `take_while_s` consumed leftover input.");
|
||||
assert!(output == CONSUMED,
|
||||
"Parser `take_while_s` doesn't return the string it consumed on success. \
|
||||
Expected `{}`, got `{}`.", CONSUMED, output);
|
||||
},
|
||||
other => panic!("Parser `take_while_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_not_s_succeed() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const AVOID: &'static str = "£úçƙ¥á";
|
||||
const CONSUMED: &'static str = "βèƒôřèÂßÇ";
|
||||
const LEFTOVER: &'static str = "áƒƭèř";
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
is_not_s!(input, AVOID)
|
||||
}
|
||||
match test(INPUT) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == LEFTOVER, "Parser `is_not_s` consumed leftover input. Leftover `{}`.", extra);
|
||||
assert!(output == CONSUMED,
|
||||
"Parser `is_not_s` doens't return the string it consumed on success. Expected `{}`, got `{}`.",
|
||||
CONSUMED, output);
|
||||
},
|
||||
other => panic!("Parser `is_not_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_until_and_consume_s_succeed() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const FIND: &'static str = "姂";
|
||||
const CONSUMED: &'static str = "βèƒôřèÂßÇ";
|
||||
const LEFTOVER: &'static str = "áƒƭèř";
|
||||
|
||||
match take_until_and_consume_s!(INPUT, FIND) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == LEFTOVER, "Parser `take_until_and_consume_s`\
|
||||
consumed leftover input. Leftover `{}`.", extra);
|
||||
assert!(output == CONSUMED, "Parser `take_until_and_consume_s`\
|
||||
doens't return the string it consumed on success. Expected `{}`, got `{}`.",
|
||||
CONSUMED, output);
|
||||
}
|
||||
other => panic!("Parser `take_until_and_consume_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_while_s_succeed_some() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const CONSUMED: &'static str = "βèƒôřèÂßÇ";
|
||||
const LEFTOVER: &'static str = "áƒƭèř";
|
||||
fn while_s(c: char) -> bool {
|
||||
c == 'β' || c == 'è' || c == 'ƒ' || c == 'ô' || c == 'ř' ||
|
||||
c == 'è' || c == 'Â' || c == 'ß' || c == 'Ç'
|
||||
}
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
take_while_s!(input, while_s)
|
||||
}
|
||||
match test(INPUT) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == LEFTOVER, "Parser `take_while_s` consumed leftover input.");
|
||||
assert!(output == CONSUMED,
|
||||
"Parser `take_while_s` doesn't return the string it consumed on success. \
|
||||
Expected `{}`, got `{}`.", CONSUMED, output);
|
||||
},
|
||||
other => panic!("Parser `take_while_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_not_s_fail() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const AVOID: &'static str = "βúçƙ¥";
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
is_not_s!(input, AVOID)
|
||||
}
|
||||
match test(INPUT) {
|
||||
IResult::Error(_) => (),
|
||||
other => panic!("Parser `is_not_s` didn't fail when it should have. Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_while1_s_succeed() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const CONSUMED: &'static str = "βèƒôřèÂßÇ";
|
||||
const LEFTOVER: &'static str = "áƒƭèř";
|
||||
fn while1_s(c: char) -> bool {
|
||||
c == 'β' || c == 'è' || c == 'ƒ' || c == 'ô' || c == 'ř' ||
|
||||
c == 'è' || c == 'Â' || c == 'ß' || c == 'Ç'
|
||||
}
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
take_while1_s!(input, while1_s)
|
||||
}
|
||||
match test(INPUT) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == LEFTOVER, "Parser `take_while1_s` consumed leftover input.");
|
||||
assert!(output == CONSUMED,
|
||||
"Parser `take_while1_s` doesn't return the string it consumed on success. \
|
||||
Expected `{}`, got `{}`.", CONSUMED, output);
|
||||
},
|
||||
other => panic!("Parser `take_while1_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_until_and_consume_s_incomplete() {
|
||||
const INPUT: &'static str = "βèƒôřè";
|
||||
const FIND: &'static str = "βèƒôřèÂßÇ";
|
||||
|
||||
match take_until_and_consume_s!(INPUT, FIND) {
|
||||
IResult::Incomplete(_) => (),
|
||||
other => panic!("Parser `take_until_and_consume_s` didn't require more input when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_until_s_incomplete() {
|
||||
const INPUT: &'static str = "βèƒôřè";
|
||||
const FIND: &'static str = "βèƒôřèÂßÇ";
|
||||
|
||||
match take_until_s!(INPUT, FIND) {
|
||||
IResult::Incomplete(_) => (),
|
||||
other => panic!("Parser `take_until_s` didn't require more input when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_a_s_succeed() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const MATCH: &'static str = "βèƒôřèÂßÇ";
|
||||
const CONSUMED: &'static str = "βèƒôřèÂßÇ";
|
||||
const LEFTOVER: &'static str = "áƒƭèř";
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
is_a_s!(input, MATCH)
|
||||
}
|
||||
match test(INPUT) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == LEFTOVER, "Parser `is_a_s` consumed leftover input. Leftover `{}`.", extra);
|
||||
assert!(output == CONSUMED,
|
||||
"Parser `is_a_s` doens't return the string it consumed on success. Expected `{}`, got `{}`.",
|
||||
CONSUMED, output);
|
||||
},
|
||||
other => panic!("Parser `is_a_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_while1_s_fail() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
fn while1_s(c: char) -> bool {
|
||||
c == '9'
|
||||
}
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
take_while1_s!(input, while1_s)
|
||||
}
|
||||
match test(INPUT) {
|
||||
IResult::Error(_) => (),
|
||||
other => panic!("Parser `take_while1_s` didn't fail when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_a_s_fail() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const MATCH: &'static str = "Ûñℓúçƙ¥";
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
is_a_s!(input, MATCH)
|
||||
}
|
||||
match test(INPUT) {
|
||||
IResult::Error(_) => (),
|
||||
other => panic!("Parser `is_a_s` didn't fail when it should have. Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_until_and_consume_s_error() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const FIND: &'static str = "Ráñδô₥";
|
||||
|
||||
match take_until_and_consume_s!(INPUT, FIND) {
|
||||
IResult::Error(_) => (),
|
||||
other => panic!("Parser `take_until_and_consume_s` didn't fail when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_until_s_error() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const FIND: &'static str = "Ráñδô₥";
|
||||
|
||||
match take_until_s!(INPUT, FIND) {
|
||||
IResult::Error(_) => (),
|
||||
other => panic!("Parser `take_until_and_consume_s` didn't fail when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,769 +0,0 @@
|
|||
use internal::{IResult,Err};
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[cfg(feature = "core")]
|
||||
use std::prelude::v1::*;
|
||||
use std::vec::Vec;
|
||||
use std::string::ToString;
|
||||
|
||||
/// useful functions to calculate the offset between slices and show a hexdump of a slice
|
||||
#[cfg(not(feature = "core"))]
|
||||
pub trait HexDisplay {
|
||||
/// offset between the first byte of self and the first byte of the argument
|
||||
fn offset(&self, second:&[u8]) -> usize;// OFFSET SHOULD GO TO ITS OWN TRAIT
|
||||
|
||||
/// Converts the value of `self` to a hex dump, returning the owned
|
||||
/// string.
|
||||
fn to_hex(&self, chunk_size: usize) -> String;
|
||||
|
||||
/// Converts the value of `self` to a hex dump beginning at `from` address, returning the owned
|
||||
/// string.
|
||||
fn to_hex_from(&self, chunk_size: usize, from: usize) -> String;
|
||||
}
|
||||
|
||||
pub trait InputLength {
|
||||
#[inline]
|
||||
fn input_len(&self) -> usize;
|
||||
}
|
||||
|
||||
impl<'a, T> InputLength for &'a[T] {
|
||||
#[inline]
|
||||
fn input_len(&self) -> usize {
|
||||
self.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> InputLength for &'a str {
|
||||
#[inline]
|
||||
fn input_len(&self) -> usize {
|
||||
self.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> InputLength for (&'a [u8], usize) {
|
||||
#[inline]
|
||||
fn input_len(&self) -> usize {
|
||||
//println!("bit input length for ({:?}, {}):", self.0, self.1);
|
||||
let res = self.0.len() * 8 - self.1;
|
||||
//println!("-> {}", res);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
use std::iter::Enumerate;
|
||||
#[cfg(not(feature = "core"))]
|
||||
use std::str::CharIndices;
|
||||
|
||||
pub trait AsChar {
|
||||
#[inline]
|
||||
fn as_char(self) -> char;
|
||||
#[inline]
|
||||
fn is_alpha(self) -> bool;
|
||||
#[inline]
|
||||
fn is_alphanum(self) -> bool;
|
||||
#[inline]
|
||||
fn is_0_to_9(self) -> bool;
|
||||
#[inline]
|
||||
fn is_hex_digit(self) -> bool;
|
||||
#[inline]
|
||||
fn is_oct_digit(self) -> bool;
|
||||
}
|
||||
|
||||
impl<'a> AsChar for &'a u8 {
|
||||
#[inline]
|
||||
fn as_char(self) -> char { *self as char }
|
||||
#[inline]
|
||||
fn is_alpha(self) -> bool {
|
||||
(*self >= 0x41 && *self <= 0x5A) || (*self >= 0x61 && *self <= 0x7A)
|
||||
}
|
||||
#[inline]
|
||||
fn is_alphanum(self) -> bool { self.is_alpha() || self.is_0_to_9() }
|
||||
#[inline]
|
||||
fn is_0_to_9(self) -> bool {
|
||||
*self >= 0x30 && *self <= 0x39
|
||||
}
|
||||
#[inline]
|
||||
fn is_hex_digit(self) -> bool {
|
||||
(*self >= 0x30 && *self <= 0x39) ||
|
||||
(*self >= 0x41 && *self <= 0x46) ||
|
||||
(*self >= 0x61 && *self <= 0x66)
|
||||
}
|
||||
#[inline]
|
||||
fn is_oct_digit(self) -> bool {
|
||||
*self >= 0x30 && *self <= 0x37
|
||||
}
|
||||
}
|
||||
|
||||
impl AsChar for char {
|
||||
#[inline]
|
||||
fn as_char(self) -> char { self }
|
||||
#[inline]
|
||||
fn is_alpha(self) -> bool { self.is_alphabetic() }
|
||||
#[inline]
|
||||
fn is_alphanum(self) -> bool { self.is_alpha() || self.is_0_to_9() }
|
||||
#[inline]
|
||||
fn is_0_to_9(self) -> bool { self.is_digit(10) }
|
||||
#[inline]
|
||||
fn is_hex_digit(self) -> bool { self.is_digit(16) }
|
||||
#[inline]
|
||||
fn is_oct_digit(self) -> bool { self.is_digit(8) }
|
||||
}
|
||||
|
||||
pub trait IterIndices {
|
||||
type Item: AsChar;
|
||||
type Iter : Iterator<Item=(usize, Self::Item)>;
|
||||
fn iter_indices(self) -> Self::Iter;
|
||||
}
|
||||
|
||||
impl<'a> IterIndices for &'a [u8] {
|
||||
type Item = &'a u8;
|
||||
type Iter = Enumerate<::std::slice::Iter<'a, u8>>;
|
||||
#[inline]
|
||||
fn iter_indices(self) -> Enumerate<::std::slice::Iter<'a, u8>> {
|
||||
self.iter().enumerate()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
impl<'a> IterIndices for &'a str {
|
||||
type Item = char;
|
||||
type Iter = CharIndices<'a>;
|
||||
#[inline]
|
||||
fn iter_indices(self) -> CharIndices<'a> {
|
||||
self.char_indices()
|
||||
}
|
||||
}
|
||||
|
||||
static CHARS: &'static[u8] = b"0123456789abcdef";
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
impl HexDisplay for [u8] {
|
||||
fn offset(&self, second:&[u8]) -> usize {
|
||||
let fst = self.as_ptr();
|
||||
let snd = second.as_ptr();
|
||||
|
||||
snd as usize - fst as usize
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn to_hex(&self, chunk_size: usize) -> String {
|
||||
self.to_hex_from(chunk_size, 0)
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn to_hex_from(&self, chunk_size: usize, from: usize) -> String {
|
||||
let mut v = Vec::with_capacity(self.len() * 3);
|
||||
let mut i = from;
|
||||
for chunk in self.chunks(chunk_size) {
|
||||
let s = format!("{:08x}", i);
|
||||
for &ch in s.as_bytes().iter() {
|
||||
v.push(ch);
|
||||
}
|
||||
v.push('\t' as u8);
|
||||
|
||||
i = i + chunk_size;
|
||||
|
||||
for &byte in chunk {
|
||||
v.push(CHARS[(byte >> 4) as usize]);
|
||||
v.push(CHARS[(byte & 0xf) as usize]);
|
||||
v.push(' ' as u8);
|
||||
}
|
||||
if chunk_size > chunk.len() {
|
||||
for j in 0..(chunk_size - chunk.len()) {
|
||||
v.push(' ' as u8);
|
||||
v.push(' ' as u8);
|
||||
v.push(' ' as u8);
|
||||
}
|
||||
}
|
||||
v.push('\t' as u8);
|
||||
|
||||
for &byte in chunk {
|
||||
if (byte >=32 && byte <= 126) || byte >= 128 {
|
||||
v.push(byte);
|
||||
} else {
|
||||
v.push('.' as u8);
|
||||
}
|
||||
}
|
||||
v.push('\n' as u8);
|
||||
}
|
||||
|
||||
String::from_utf8_lossy(&v[..]).into_owned()
|
||||
}
|
||||
}
|
||||
|
||||
/// Prints a message if the parser fails
|
||||
///
|
||||
/// The message prints the `Error` or `Incomplete`
|
||||
/// and the parser's calling code
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # fn main() {
|
||||
/// named!(f, dbg!( tag!( "abcd" ) ) );
|
||||
///
|
||||
/// let a = &b"efgh"[..];
|
||||
///
|
||||
/// // Will print the following message:
|
||||
/// // Error(Position(0, [101, 102, 103, 104])) at l.5 by ' tag ! ( "abcd" ) '
|
||||
/// f(a);
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! dbg (
|
||||
($i: expr, $submac:ident!( $($args:tt)* )) => (
|
||||
{
|
||||
let l = line!();
|
||||
match $submac!($i, $($args)*) {
|
||||
$crate::IResult::Error(a) => {
|
||||
println!("Error({:?}) at l.{} by ' {} '", a, l, stringify!($submac!($($args)*)));
|
||||
$crate::IResult::Error(a)
|
||||
},
|
||||
$crate::IResult::Incomplete(a) => {
|
||||
println!("Incomplete({:?}) at {} by ' {} '", a, l, stringify!($submac!($($args)*)));
|
||||
$crate::IResult::Incomplete(a)
|
||||
},
|
||||
a => a
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
($i:expr, $f:ident) => (
|
||||
dbg!($i, call!($f));
|
||||
);
|
||||
);
|
||||
|
||||
/// Prints a message and the input if the parser fails
|
||||
///
|
||||
/// The message prints the `Error` or `Incomplete`
|
||||
/// and the parser's calling code.
|
||||
///
|
||||
/// It also displays the input in hexdump format
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # fn main() {
|
||||
/// named!(f, dbg_dmp!( tag!( "abcd" ) ) );
|
||||
///
|
||||
/// let a = &b"efghijkl"[..];
|
||||
///
|
||||
/// // Will print the following message:
|
||||
/// // Error(Position(0, [101, 102, 103, 104, 105, 106, 107, 108])) at l.5 by ' tag ! ( "abcd" ) '
|
||||
/// // 00000000 65 66 67 68 69 6a 6b 6c efghijkl
|
||||
/// f(a);
|
||||
/// # }
|
||||
#[macro_export]
|
||||
macro_rules! dbg_dmp (
|
||||
($i: expr, $submac:ident!( $($args:tt)* )) => (
|
||||
{
|
||||
use $crate::HexDisplay;
|
||||
let l = line!();
|
||||
match $submac!($i, $($args)*) {
|
||||
$crate::IResult::Error(a) => {
|
||||
println!("Error({:?}) at l.{} by ' {} '\n{}", a, l, stringify!($submac!($($args)*)), $i.to_hex(8));
|
||||
$crate::IResult::Error(a)
|
||||
},
|
||||
$crate::IResult::Incomplete(a) => {
|
||||
println!("Incomplete({:?}) at {} by ' {} '\n{}", a, l, stringify!($submac!($($args)*)), $i.to_hex(8));
|
||||
$crate::IResult::Incomplete(a)
|
||||
},
|
||||
a => a
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
($i:expr, $f:ident) => (
|
||||
dbg_dmp!($i, call!($f));
|
||||
);
|
||||
);
|
||||
|
||||
pub fn error_to_list<P,E:Clone>(e:&Err<P,E>) -> Vec<ErrorKind<E>> {
|
||||
let mut v:Vec<ErrorKind<E>> = Vec::new();
|
||||
let mut err = e;
|
||||
loop {
|
||||
match *err {
|
||||
Err::Code(ref i) | Err::Position(ref i,_) => {
|
||||
v.push(i.clone());
|
||||
return v;
|
||||
},
|
||||
Err::Node(ref i, ref next) | Err::NodePosition(ref i, _, ref next) => {
|
||||
v.push(i.clone());
|
||||
err = &*next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compare_error_paths<P,E:Clone+PartialEq>(e1:&Err<P,E>, e2:&Err<P,E>) -> bool {
|
||||
error_to_list(e1) == error_to_list(e2)
|
||||
}
|
||||
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
use std::hash::Hash;
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
pub fn add_error_pattern<'a,I,O,E: Clone+Hash+Eq>(h: &mut HashMap<Vec<ErrorKind<E>>, &'a str>, res: IResult<I,O,E>, message: &'a str) -> bool {
|
||||
if let IResult::Error(e) = res {
|
||||
h.insert(error_to_list(&e), message);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn slice_to_offsets(input: &[u8], s: &[u8]) -> (usize, usize) {
|
||||
let start = input.as_ptr();
|
||||
let off1 = s.as_ptr() as usize - start as usize;
|
||||
let off2 = off1 + s.len();
|
||||
(off1, off2)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
pub fn prepare_errors<O,E: Clone>(input: &[u8], res: IResult<&[u8],O,E>) -> Option<Vec<(ErrorKind<E>, usize, usize)> > {
|
||||
if let IResult::Error(e) = res {
|
||||
let mut v:Vec<(ErrorKind<E>, usize, usize)> = Vec::new();
|
||||
let mut err = e.clone();
|
||||
loop {
|
||||
match err {
|
||||
Err::Position(i,s) => {
|
||||
let (o1, o2) = slice_to_offsets(input, s);
|
||||
v.push((i, o1, o2));
|
||||
//println!("v is: {:?}", v);
|
||||
break;
|
||||
},
|
||||
Err::NodePosition(i, s, next) => {
|
||||
let (o1, o2) = slice_to_offsets(input, s);
|
||||
v.push((i, o1, o2));
|
||||
err = *next;
|
||||
},
|
||||
Err::Node(_, next) => {
|
||||
err = *next;
|
||||
},
|
||||
Err::Code(_) => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
v.sort_by(|a, b| a.1.cmp(&b.1));
|
||||
Some(v)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
pub fn print_error<O,E:Clone>(input: &[u8], res: IResult<&[u8],O,E>) {
|
||||
if let Some(v) = prepare_errors(input, res) {
|
||||
let colors = generate_colors(&v);
|
||||
println!("parser codes: {}", print_codes(colors, HashMap::new()));
|
||||
println!("{}", print_offsets(input, 0, &v));
|
||||
|
||||
} else {
|
||||
println!("not an error");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
pub fn generate_colors<E>(v: &[(ErrorKind<E>, usize, usize)]) -> HashMap<u32, u8> {
|
||||
let mut h: HashMap<u32, u8> = HashMap::new();
|
||||
let mut color = 0;
|
||||
|
||||
for &(ref c,_,_) in v.iter() {
|
||||
h.insert(error_to_u32(c), color + 31);
|
||||
color = color + 1 % 7;
|
||||
}
|
||||
|
||||
h
|
||||
}
|
||||
|
||||
pub fn code_from_offset<E>(v: &[(ErrorKind<E>, usize, usize)], offset: usize) -> Option<u32> {
|
||||
let mut acc: Option<(u32, usize, usize)> = None;
|
||||
for &(ref ek, s, e) in v.iter() {
|
||||
let c = error_to_u32(ek);
|
||||
if s <= offset && offset <=e {
|
||||
if let Some((_, start, end)) = acc {
|
||||
if start <= s && e <= end {
|
||||
acc = Some((c, s, e));
|
||||
}
|
||||
} else {
|
||||
acc = Some((c, s, e));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some((code, _, _)) = acc {
|
||||
return Some(code);
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset_color(v: &mut Vec<u8>) {
|
||||
v.push(0x1B);
|
||||
v.push('[' as u8);
|
||||
v.push(0);
|
||||
v.push('m' as u8);
|
||||
}
|
||||
|
||||
pub fn write_color(v: &mut Vec<u8>, color: u8) {
|
||||
v.push(0x1B);
|
||||
v.push('[' as u8);
|
||||
v.push(1);
|
||||
v.push(';' as u8);
|
||||
let s = color.to_string();
|
||||
let bytes = s.as_bytes();
|
||||
v.extend(bytes.iter().cloned());
|
||||
v.push('m' as u8);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
pub fn print_codes(colors: HashMap<u32, u8>, names: HashMap<u32, &str>) -> String {
|
||||
let mut v = Vec::new();
|
||||
for (code, &color) in &colors {
|
||||
if let Some(&s) = names.get(&code) {
|
||||
let bytes = s.as_bytes();
|
||||
write_color(&mut v, color);
|
||||
v.extend(bytes.iter().cloned());
|
||||
} else {
|
||||
let s = code.to_string();
|
||||
let bytes = s.as_bytes();
|
||||
write_color(&mut v, color);
|
||||
v.extend(bytes.iter().cloned());
|
||||
}
|
||||
reset_color(&mut v);
|
||||
v.push(' ' as u8);
|
||||
}
|
||||
reset_color(&mut v);
|
||||
|
||||
String::from_utf8_lossy(&v[..]).into_owned()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "core"))]
|
||||
pub fn print_offsets<E>(input: &[u8], from: usize, offsets: &[(ErrorKind<E>, usize, usize)]) -> String {
|
||||
let mut v = Vec::with_capacity(input.len() * 3);
|
||||
let mut i = from;
|
||||
let chunk_size = 8;
|
||||
let mut current_code: Option<u32> = None;
|
||||
let mut current_code2: Option<u32> = None;
|
||||
|
||||
let colors = generate_colors(&offsets);
|
||||
|
||||
for chunk in input.chunks(chunk_size) {
|
||||
let s = format!("{:08x}", i);
|
||||
for &ch in s.as_bytes().iter() {
|
||||
v.push(ch);
|
||||
}
|
||||
v.push('\t' as u8);
|
||||
|
||||
let mut k = i;
|
||||
let mut l = i;
|
||||
for &byte in chunk {
|
||||
if let Some(code) = code_from_offset(&offsets, k) {
|
||||
if let Some(current) = current_code {
|
||||
if current != code {
|
||||
reset_color(&mut v);
|
||||
current_code = Some(code);
|
||||
if let Some(&color) = colors.get(&code) {
|
||||
write_color(&mut v, color);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
current_code = Some(code);
|
||||
if let Some(&color) = colors.get(&code) {
|
||||
write_color(&mut v, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
v.push(CHARS[(byte >> 4) as usize]);
|
||||
v.push(CHARS[(byte & 0xf) as usize]);
|
||||
v.push(' ' as u8);
|
||||
k = k + 1;
|
||||
}
|
||||
|
||||
reset_color(&mut v);
|
||||
|
||||
if chunk_size > chunk.len() {
|
||||
for _ in 0..(chunk_size - chunk.len()) {
|
||||
v.push(' ' as u8);
|
||||
v.push(' ' as u8);
|
||||
v.push(' ' as u8);
|
||||
}
|
||||
}
|
||||
v.push('\t' as u8);
|
||||
|
||||
for &byte in chunk {
|
||||
if let Some(code) = code_from_offset(&offsets, l) {
|
||||
if let Some(current) = current_code2 {
|
||||
if current != code {
|
||||
reset_color(&mut v);
|
||||
current_code2 = Some(code);
|
||||
if let Some(&color) = colors.get(&code) {
|
||||
write_color(&mut v, color);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
current_code2 = Some(code);
|
||||
if let Some(&color) = colors.get(&code) {
|
||||
write_color(&mut v, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (byte >=32 && byte <= 126) || byte >= 128 {
|
||||
v.push(byte);
|
||||
} else {
|
||||
v.push('.' as u8);
|
||||
}
|
||||
l = l + 1;
|
||||
}
|
||||
reset_color(&mut v);
|
||||
|
||||
v.push('\n' as u8);
|
||||
i = i + chunk_size;
|
||||
}
|
||||
|
||||
String::from_utf8_lossy(&v[..]).into_owned()
|
||||
}
|
||||
|
||||
pub trait AsBytes {
|
||||
fn as_bytes(&self) -> &[u8];
|
||||
}
|
||||
|
||||
impl<'a> AsBytes for &'a str {
|
||||
#[inline(always)]
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
str::as_bytes(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsBytes for str {
|
||||
#[inline(always)]
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
str::as_bytes(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsBytes for &'a [u8] {
|
||||
#[inline(always)]
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl AsBytes for [u8] {
|
||||
#[inline(always)]
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! array_impls {
|
||||
($($N:expr)+) => {
|
||||
$(
|
||||
impl<'a> AsBytes for &'a [u8; $N] {
|
||||
#[inline(always)]
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl AsBytes for [u8; $N] {
|
||||
#[inline(always)]
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
self
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
array_impls! {
|
||||
0 1 2 3 4 5 6 7 8 9
|
||||
10 11 12 13 14 15 16 17 18 19
|
||||
20 21 22 23 24 25 26 27 28 29
|
||||
30 31 32
|
||||
}
|
||||
|
||||
/// indicates which parser returned an error
|
||||
#[derive(Debug,PartialEq,Eq,Hash,Clone)]
|
||||
pub enum ErrorKind<E=u32> {
|
||||
Custom(E),
|
||||
Tag,
|
||||
MapRes,
|
||||
MapOpt,
|
||||
Alt,
|
||||
IsNot,
|
||||
IsA,
|
||||
SeparatedList,
|
||||
SeparatedNonEmptyList,
|
||||
Many0,
|
||||
Many1,
|
||||
Count,
|
||||
TakeUntilAndConsume,
|
||||
TakeUntil,
|
||||
TakeUntilEitherAndConsume,
|
||||
TakeUntilEither,
|
||||
LengthValue,
|
||||
TagClosure,
|
||||
Alpha,
|
||||
Digit,
|
||||
HexDigit,
|
||||
OctDigit,
|
||||
AlphaNumeric,
|
||||
Space,
|
||||
MultiSpace,
|
||||
LengthValueFn,
|
||||
Eof,
|
||||
ExprOpt,
|
||||
ExprRes,
|
||||
CondReduce,
|
||||
Switch,
|
||||
TagBits,
|
||||
OneOf,
|
||||
NoneOf,
|
||||
Char,
|
||||
CrLf,
|
||||
RegexpMatch,
|
||||
RegexpMatches,
|
||||
RegexpFind,
|
||||
RegexpCapture,
|
||||
RegexpCaptures,
|
||||
TakeWhile1,
|
||||
Complete,
|
||||
Fix,
|
||||
Escaped,
|
||||
EscapedTransform,
|
||||
TagStr,
|
||||
IsNotStr,
|
||||
IsAStr,
|
||||
TakeWhile1Str,
|
||||
NonEmpty,
|
||||
ManyMN,
|
||||
TakeUntilAndConsumeStr,
|
||||
TakeUntilStr,
|
||||
Not
|
||||
}
|
||||
|
||||
pub fn error_to_u32<E>(e: &ErrorKind<E>) -> u32 {
|
||||
match *e {
|
||||
ErrorKind::Custom(_) => 0,
|
||||
ErrorKind::Tag => 1,
|
||||
ErrorKind::MapRes => 2,
|
||||
ErrorKind::MapOpt => 3,
|
||||
ErrorKind::Alt => 4,
|
||||
ErrorKind::IsNot => 5,
|
||||
ErrorKind::IsA => 6,
|
||||
ErrorKind::SeparatedList => 7,
|
||||
ErrorKind::SeparatedNonEmptyList => 8,
|
||||
ErrorKind::Many1 => 9,
|
||||
ErrorKind::Count => 10,
|
||||
ErrorKind::TakeUntilAndConsume => 11,
|
||||
ErrorKind::TakeUntil => 12,
|
||||
ErrorKind::TakeUntilEitherAndConsume => 13,
|
||||
ErrorKind::TakeUntilEither => 14,
|
||||
ErrorKind::LengthValue => 15,
|
||||
ErrorKind::TagClosure => 16,
|
||||
ErrorKind::Alpha => 17,
|
||||
ErrorKind::Digit => 18,
|
||||
ErrorKind::AlphaNumeric => 19,
|
||||
ErrorKind::Space => 20,
|
||||
ErrorKind::MultiSpace => 21,
|
||||
ErrorKind::LengthValueFn => 22,
|
||||
ErrorKind::Eof => 23,
|
||||
ErrorKind::ExprOpt => 24,
|
||||
ErrorKind::ExprRes => 25,
|
||||
ErrorKind::CondReduce => 26,
|
||||
ErrorKind::Switch => 27,
|
||||
ErrorKind::TagBits => 28,
|
||||
ErrorKind::OneOf => 29,
|
||||
ErrorKind::NoneOf => 30,
|
||||
ErrorKind::Char => 40,
|
||||
ErrorKind::CrLf => 41,
|
||||
ErrorKind::RegexpMatch => 42,
|
||||
ErrorKind::RegexpMatches => 43,
|
||||
ErrorKind::RegexpFind => 44,
|
||||
ErrorKind::RegexpCapture => 45,
|
||||
ErrorKind::RegexpCaptures => 46,
|
||||
ErrorKind::TakeWhile1 => 47,
|
||||
ErrorKind::Complete => 48,
|
||||
ErrorKind::Fix => 49,
|
||||
ErrorKind::Escaped => 50,
|
||||
ErrorKind::EscapedTransform => 51,
|
||||
ErrorKind::TagStr => 52,
|
||||
ErrorKind::IsNotStr => 53,
|
||||
ErrorKind::IsAStr => 54,
|
||||
ErrorKind::TakeWhile1Str => 55,
|
||||
ErrorKind::NonEmpty => 56,
|
||||
ErrorKind::ManyMN => 57,
|
||||
ErrorKind::TakeUntilAndConsumeStr => 58,
|
||||
ErrorKind::HexDigit => 59,
|
||||
ErrorKind::TakeUntilStr => 60,
|
||||
ErrorKind::OctDigit => 61,
|
||||
ErrorKind::Many0 => 62,
|
||||
ErrorKind::Not => 63,
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> ErrorKind<E> {
|
||||
pub fn description(&self) -> &str {
|
||||
match *self {
|
||||
ErrorKind::Custom(_) => "Custom error",
|
||||
ErrorKind::Tag => "Tag",
|
||||
ErrorKind::MapRes => "Map on Result",
|
||||
ErrorKind::MapOpt => "Map on Option",
|
||||
ErrorKind::Alt => "Alternative",
|
||||
ErrorKind::IsNot => "IsNot",
|
||||
ErrorKind::IsA => "IsA",
|
||||
ErrorKind::SeparatedList => "Separated list",
|
||||
ErrorKind::SeparatedNonEmptyList => "Separated non empty list",
|
||||
ErrorKind::Many0 => "Many0",
|
||||
ErrorKind::Many1 => "Many1",
|
||||
ErrorKind::Count => "Count",
|
||||
ErrorKind::TakeUntilAndConsume => "Take until and consume",
|
||||
ErrorKind::TakeUntil => "Take until",
|
||||
ErrorKind::TakeUntilEitherAndConsume => "Take until either and consume",
|
||||
ErrorKind::TakeUntilEither => "Take until either",
|
||||
ErrorKind::LengthValue => "Length followed by value",
|
||||
ErrorKind::TagClosure => "Tag closure",
|
||||
ErrorKind::Alpha => "Alphabetic",
|
||||
ErrorKind::Digit => "Digit",
|
||||
ErrorKind::AlphaNumeric => "AlphaNumeric",
|
||||
ErrorKind::Space => "Space",
|
||||
ErrorKind::MultiSpace => "Multiple spaces",
|
||||
ErrorKind::LengthValueFn => "LengthValueFn",
|
||||
ErrorKind::Eof => "End of file",
|
||||
ErrorKind::ExprOpt => "Evaluate Option",
|
||||
ErrorKind::ExprRes => "Evaluate Result",
|
||||
ErrorKind::CondReduce => "Condition reduce",
|
||||
ErrorKind::Switch => "Switch",
|
||||
ErrorKind::TagBits => "Tag on bitstream",
|
||||
ErrorKind::OneOf => "OneOf",
|
||||
ErrorKind::NoneOf => "NoneOf",
|
||||
ErrorKind::Char => "Char",
|
||||
ErrorKind::CrLf => "CrLf",
|
||||
ErrorKind::RegexpMatch => "RegexpMatch",
|
||||
ErrorKind::RegexpMatches => "RegexpMatches",
|
||||
ErrorKind::RegexpFind => "RegexpFind",
|
||||
ErrorKind::RegexpCapture => "RegexpCapture",
|
||||
ErrorKind::RegexpCaptures => "RegexpCaptures",
|
||||
ErrorKind::TakeWhile1 => "TakeWhile1",
|
||||
ErrorKind::Complete => "Complete",
|
||||
ErrorKind::Fix => "Fix",
|
||||
ErrorKind::Escaped => "Escaped",
|
||||
ErrorKind::EscapedTransform => "EscapedTransform",
|
||||
ErrorKind::TagStr => "Tag on strings",
|
||||
ErrorKind::IsNotStr => "IsNot on strings",
|
||||
ErrorKind::IsAStr => "IsA on strings",
|
||||
ErrorKind::TakeWhile1Str => "TakeWhile1 on strings",
|
||||
ErrorKind::NonEmpty => "NonEmpty",
|
||||
ErrorKind::ManyMN => "Many(m, n)",
|
||||
ErrorKind::TakeUntilAndConsumeStr => "Take until and consume on strings",
|
||||
ErrorKind::HexDigit => "Hexadecimal Digit",
|
||||
ErrorKind::TakeUntilStr => "Take until on strings",
|
||||
ErrorKind::OctDigit => "Octal digit",
|
||||
ErrorKind::Not => "Negation",
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use nom::{IResult,digit, multispace};
|
||||
|
||||
use std::str;
|
||||
use std::str::FromStr;
|
||||
|
||||
named!(parens<i64>, delimited!(
|
||||
delimited!(opt!(multispace), tag!("("), opt!(multispace)),
|
||||
expr,
|
||||
delimited!(opt!(multispace), tag!(")"), opt!(multispace))
|
||||
)
|
||||
);
|
||||
|
||||
named!(factor<i64>, alt!(
|
||||
map_res!(
|
||||
map_res!(
|
||||
delimited!(opt!(multispace), digit, opt!(multispace)),
|
||||
str::from_utf8
|
||||
),
|
||||
FromStr::from_str
|
||||
)
|
||||
| parens
|
||||
)
|
||||
);
|
||||
|
||||
named!(term <i64>, chain!(
|
||||
mut acc: factor ~
|
||||
many0!(
|
||||
alt!(
|
||||
tap!(mul: preceded!(tag!("*"), factor) => acc = acc * mul) |
|
||||
tap!(div: preceded!(tag!("/"), factor) => acc = acc / div)
|
||||
)
|
||||
),
|
||||
|| { return acc }
|
||||
)
|
||||
);
|
||||
|
||||
named!(expr <i64>, chain!(
|
||||
mut acc: term ~
|
||||
many0!(
|
||||
alt!(
|
||||
tap!(add: preceded!(tag!("+"), term) => acc = acc + add) |
|
||||
tap!(sub: preceded!(tag!("-"), term) => acc = acc - sub)
|
||||
)
|
||||
),
|
||||
|| { return acc }
|
||||
)
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn factor_test() {
|
||||
assert_eq!(factor(&b"3"[..]), IResult::Done(&b""[..], 3));
|
||||
assert_eq!(factor(&b" 12"[..]), IResult::Done(&b""[..], 12));
|
||||
assert_eq!(factor(&b"537 "[..]), IResult::Done(&b""[..], 537));
|
||||
assert_eq!(factor(&b" 24 "[..]), IResult::Done(&b""[..], 24));
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn term_test() {
|
||||
assert_eq!(term(&b" 12 *2 / 3"[..]), IResult::Done(&b""[..], 8));
|
||||
assert_eq!(term(&b" 2* 3 *2 *2 / 3"[..]), IResult::Done(&b""[..], 8));
|
||||
assert_eq!(term(&b" 48 / 3/2"[..]), IResult::Done(&b""[..], 8));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expr_test() {
|
||||
assert_eq!(expr(&b" 1 + 2 "[..]), IResult::Done(&b""[..], 3));
|
||||
assert_eq!(expr(&b" 12 + 6 - 4+ 3"[..]), IResult::Done(&b""[..], 17));
|
||||
assert_eq!(expr(&b" 1 + 2*3 + 4"[..]), IResult::Done(&b""[..], 11));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parens_test() {
|
||||
assert_eq!(expr(&b" ( 2 )"[..]), IResult::Done(&b""[..], 2));
|
||||
assert_eq!(expr(&b" 2* ( 3 + 4 ) "[..]), IResult::Done(&b""[..], 14));
|
||||
assert_eq!(expr(&b" 2*2 / ( 5 - 1) + 3"[..]), IResult::Done(&b""[..], 4));
|
||||
}
|
|
@ -1,137 +0,0 @@
|
|||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use std::fmt;
|
||||
use std::fmt::{Display, Debug, Formatter};
|
||||
|
||||
use std::str;
|
||||
use std::str::FromStr;
|
||||
|
||||
use nom::{IResult, digit, multispace};
|
||||
|
||||
pub enum Expr {
|
||||
Value(i64),
|
||||
Add(Box<Expr>, Box<Expr>),
|
||||
Sub(Box<Expr>, Box<Expr>),
|
||||
Mul(Box<Expr>, Box<Expr>),
|
||||
Div(Box<Expr>, Box<Expr>),
|
||||
Paren(Box<Expr>),
|
||||
}
|
||||
|
||||
pub enum Oper {
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
}
|
||||
|
||||
impl Display for Expr {
|
||||
fn fmt(&self, format: &mut Formatter) -> fmt::Result {
|
||||
use self::Expr::*;
|
||||
match *self {
|
||||
Value(val) => write!(format, "{}", val),
|
||||
Add(ref left, ref right) => write!(format, "{} + {}", left, right),
|
||||
Sub(ref left, ref right) => write!(format, "{} - {}", left, right),
|
||||
Mul(ref left, ref right) => write!(format, "{} * {}", left, right),
|
||||
Div(ref left, ref right) => write!(format, "{} / {}", left, right),
|
||||
Paren(ref expr) => write!(format, "({})", expr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Expr {
|
||||
fn fmt(&self, format: &mut Formatter) -> fmt::Result {
|
||||
use self::Expr::*;
|
||||
match *self {
|
||||
Value(val) => write!(format, "{}", val),
|
||||
Add(ref left, ref right) => write!(format, "({:?} + {:?})", left, right),
|
||||
Sub(ref left, ref right) => write!(format, "({:?} - {:?})", left, right),
|
||||
Mul(ref left, ref right) => write!(format, "({:?} * {:?})", left, right),
|
||||
Div(ref left, ref right) => write!(format, "({:?} / {:?})", left, right),
|
||||
Paren(ref expr) => write!(format, "[{:?}]", expr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
named!(parens< Expr >, delimited!(
|
||||
delimited!(opt!(multispace), tag!("("), opt!(multispace)),
|
||||
map!(map!(expr, Box::new), Expr::Paren),
|
||||
delimited!(opt!(multispace), tag!(")"), opt!(multispace))
|
||||
)
|
||||
);
|
||||
|
||||
named!(factor< Expr >, alt_complete!(
|
||||
map!(
|
||||
map_res!(
|
||||
map_res!(
|
||||
delimited!(opt!(multispace), digit, opt!(multispace)),
|
||||
str::from_utf8
|
||||
),
|
||||
FromStr::from_str
|
||||
),
|
||||
Expr::Value)
|
||||
| parens
|
||||
)
|
||||
);
|
||||
|
||||
fn fold_exprs(initial: Expr, remainder: Vec<(Oper, Expr)>) -> Expr {
|
||||
remainder.into_iter().fold(initial, |acc, pair| {
|
||||
let (oper, expr) = pair;
|
||||
match oper {
|
||||
Oper::Add => Expr::Add(Box::new(acc), Box::new(expr)),
|
||||
Oper::Sub => Expr::Sub(Box::new(acc), Box::new(expr)),
|
||||
Oper::Mul => Expr::Mul(Box::new(acc), Box::new(expr)),
|
||||
Oper::Div => Expr::Div(Box::new(acc), Box::new(expr)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
named!(term< Expr >, chain!(
|
||||
initial: factor ~
|
||||
remainder: many0!(
|
||||
alt!(
|
||||
chain!(tag!("*") ~ mul: factor, || { (Oper::Mul, mul) }) |
|
||||
chain!(tag!("/") ~ div: factor, || { (Oper::Div, div) })
|
||||
)
|
||||
),
|
||||
|| fold_exprs(initial, remainder))
|
||||
);
|
||||
|
||||
named!(expr< Expr >, chain!(
|
||||
initial: term ~
|
||||
remainder: many0!(
|
||||
alt!(
|
||||
chain!(tag!("+") ~ add: term, || { (Oper::Add, add) }) |
|
||||
chain!(tag!("-") ~ sub: term, || { (Oper::Sub, sub) })
|
||||
)
|
||||
),
|
||||
|| fold_exprs(initial, remainder))
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn factor_test() {
|
||||
assert_eq!(factor(&b" 3 "[..]).map(|x| format!("{:?}", x)),
|
||||
IResult::Done(&b""[..], String::from("3")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn term_test() {
|
||||
assert_eq!(term(&b" 3 * 5 "[..]).map(|x| format!("{:?}", x)),
|
||||
IResult::Done(&b""[..], String::from("(3 * 5)")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expr_test() {
|
||||
assert_eq!(expr(&b" 1 + 2 * 3 "[..]).map(|x| format!("{:?}", x)),
|
||||
IResult::Done(&b""[..], String::from("(1 + (2 * 3))")));
|
||||
assert_eq!(expr(&b" 1 + 2 * 3 / 4 - 5 "[..]).map(|x| format!("{:?}", x)),
|
||||
IResult::Done(&b""[..], String::from("((1 + ((2 * 3) / 4)) - 5)")));
|
||||
assert_eq!(expr(&b" 72 / 2 / 3 "[..]).map(|x| format!("{:?}", x)),
|
||||
IResult::Done(&b""[..], String::from("((72 / 2) / 3)")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parens_test() {
|
||||
assert_eq!(expr(&b" ( 1 + 2 ) * 3 "[..]).map(|x| format!("{:?}", x)),
|
||||
IResult::Done(&b""[..], String::from("([(1 + 2)] * 3)")));
|
||||
}
|
|
@ -1,140 +0,0 @@
|
|||
/// this file tests a different backtracking behaviour. With the current
|
||||
/// `error!` macro, an early return is done in the current function, but
|
||||
/// backtracking continues normally outside of that function.
|
||||
///
|
||||
/// The solution here wraps `IResult` in a `Result`: a `Ok` indicates usual
|
||||
/// backtracking, `Err` indicates that we must "cut".
|
||||
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
macro_rules! n (
|
||||
($name:ident( $i:ty ) -> $o:ty, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( i: $i ) -> std::result::Result<nom::IResult<$i,$o,u32>, nom::Err<$i, u32>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
($name:ident<$i:ty,$o:ty,$e:ty>, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( i: $i ) -> std::result::Result<nom::IResult<$i, $o, $e>, nom::Err<$i, $e>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
($name:ident<$i:ty,$o:ty>, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( i: $i ) -> std::result::Result<nom::IResult<$i, $o, u32>, nom::Err<$i, u32>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
($name:ident<$o:ty>, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name<'a>( i: &'a[u8] ) -> std::result::Result<nom::IResult<&'a [u8], $o, u32>, nom::Err<&'a [u8], u32>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
($name:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( i: &[u8] ) -> std::result::Result<nom::IResult<&[u8], &[u8], u32>, nom::Err<&[u8], u32>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
(pub $name:ident( $i:ty ) -> $o:ty, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name( i: $i ) -> std::result::Result<nom::IResult<$i,$o, u32>, nom::Err<$i, u32>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$i:ty,$o:ty,$e:ty>, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name( i: $i ) -> std::result::Result<nom::IResult<$i, $o, $e>, nom::Err<$i, $e>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$i:ty,$o:ty>, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name( i: $i ) -> std::result::Result<nom::IResult<$i, $o, u32>, nom::Err<$i, u32>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$o:ty>, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name( i: &[u8] ) -> std::result::Result<nom::IResult<&[u8], $o, u32>, nom::Err<&[u8], u32>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
(pub $name:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name<'a>( i: &'a [u8] ) -> std::result::Result<nom::IResult<&[u8], &[u8], u32>, nom::Err<&[u8], u32>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
macro_rules! cut (
|
||||
($i:expr, $code:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
{
|
||||
let cl = || {
|
||||
Ok($submac!($i, $($args)*))
|
||||
};
|
||||
|
||||
match cl() {
|
||||
std::result::Result::Ok(nom::IResult::Incomplete(x)) => nom::IResult::Incomplete(x),
|
||||
std::result::Result::Ok(nom::IResult::Done(i, o)) => nom::IResult::Done(i, o),
|
||||
std::result::Result::Ok(nom::IResult::Error(e)) | std::result::Result::Err(e) => {
|
||||
return std::result::Result::Err(nom::Err::NodePosition($code, $i, Box::new(e)))
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
($i:expr, $code:expr, $f:expr) => (
|
||||
cut!($i, $code, call!($f));
|
||||
);
|
||||
);
|
||||
|
||||
macro_rules! c (
|
||||
($i:expr, $f:expr) => (
|
||||
{
|
||||
match $f($i) {
|
||||
std::result::Result::Ok(nom::IResult::Incomplete(x)) => nom::IResult::Incomplete(x),
|
||||
std::result::Result::Ok(nom::IResult::Done(i, o)) => nom::IResult::Done(i, o),
|
||||
std::result::Result::Ok(nom::IResult::Error(e)) => nom::IResult::Error(e),
|
||||
std::result::Result::Err(e) => {
|
||||
return std::result::Result::Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
n!(pub foo< bool >,
|
||||
chain!(
|
||||
tag!("a") ~
|
||||
cut!(nom::ErrorKind::Custom(42),dbg_dmp!(tag!("b"))) ,
|
||||
|| { true }
|
||||
)
|
||||
);
|
||||
|
||||
n!(pub foos< Vec<bool> >,
|
||||
delimited!(
|
||||
tag!("("),
|
||||
many0!(c!(foo)),
|
||||
tag!(")")
|
||||
)
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn test_ok() {
|
||||
let r = foos(b"(abab)");
|
||||
println!("result: {:?}", r);
|
||||
match r {
|
||||
Ok(nom::IResult::Done(_,result)) => assert_eq!(result,vec![true,true]),
|
||||
res => panic!("Oops {:?}.",res)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_err() {
|
||||
let input = b"(ac)";
|
||||
let r = foos(&input[..]);
|
||||
println!("result: {:?}", r);
|
||||
match r {
|
||||
//Ok(nom::IResult::Error(nom::Err::Position(kind,_))) => assert_eq!(kind,nom::ErrorKind::Custom(42)),
|
||||
Err(nom::Err::NodePosition(kind, position, _)) => {
|
||||
assert_eq!(kind, nom::ErrorKind::Custom(42));
|
||||
assert_eq!(position, &input[2..]);
|
||||
}
|
||||
res => panic!("Oops, {:?}",res)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,234 +0,0 @@
|
|||
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use nom::{IResult,not_line_ending, space, alphanumeric, multispace};
|
||||
|
||||
use std::str;
|
||||
use std::collections::HashMap;
|
||||
|
||||
named!(category<&str>, map_res!(
|
||||
terminated!(
|
||||
delimited!(tag!("["), take_until!("]"), tag!("]")),
|
||||
opt!(multispace)
|
||||
),
|
||||
str::from_utf8
|
||||
));
|
||||
|
||||
named!(key_value <&[u8],(&str,&str)>,
|
||||
chain!(
|
||||
key: map_res!(alphanumeric, std::str::from_utf8) ~
|
||||
space? ~
|
||||
tag!("=") ~
|
||||
space? ~
|
||||
val: map_res!(
|
||||
take_until_either!("\n;"),
|
||||
str::from_utf8
|
||||
) ~
|
||||
space? ~
|
||||
chain!(
|
||||
tag!(";") ~
|
||||
not_line_ending ,
|
||||
||{}
|
||||
) ? ~
|
||||
multispace? ,
|
||||
||{(key, val)}
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
named!(keys_and_values_aggregator<&[u8], Vec<(&str,&str)> >, many0!(key_value));
|
||||
|
||||
fn keys_and_values(input:&[u8]) -> IResult<&[u8], HashMap<&str, &str> > {
|
||||
let mut h: HashMap<&str, &str> = HashMap::new();
|
||||
|
||||
match keys_and_values_aggregator(input) {
|
||||
IResult::Done(i,tuple_vec) => {
|
||||
for &(k,v) in &tuple_vec {
|
||||
h.insert(k, v);
|
||||
}
|
||||
IResult::Done(i, h)
|
||||
},
|
||||
IResult::Incomplete(a) => IResult::Incomplete(a),
|
||||
IResult::Error(a) => IResult::Error(a)
|
||||
}
|
||||
}
|
||||
|
||||
named!(category_and_keys<&[u8],(&str,HashMap<&str,&str>)>,
|
||||
chain!(
|
||||
category: category ~
|
||||
keys: keys_and_values ,
|
||||
move ||{(category, keys)}
|
||||
)
|
||||
);
|
||||
|
||||
named!(categories_aggregator<&[u8], Vec<(&str, HashMap<&str,&str>)> >, many0!(category_and_keys));
|
||||
|
||||
fn categories(input: &[u8]) -> IResult<&[u8], HashMap<&str, HashMap<&str, &str> > > {
|
||||
let mut h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
|
||||
|
||||
match categories_aggregator(input) {
|
||||
IResult::Done(i,tuple_vec) => {
|
||||
for &(k,ref v) in &tuple_vec {
|
||||
h.insert(k, v.clone());
|
||||
}
|
||||
IResult::Done(i, h)
|
||||
},
|
||||
IResult::Incomplete(a) => IResult::Incomplete(a),
|
||||
IResult::Error(a) => IResult::Error(a)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_category_test() {
|
||||
let ini_file = &b"[category]
|
||||
|
||||
parameter=value
|
||||
key = value2"[..];
|
||||
|
||||
let ini_without_category = &b"parameter=value
|
||||
key = value2"[..];
|
||||
|
||||
let res = category(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
assert_eq!(res, IResult::Done(ini_without_category, "category"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_key_value_test() {
|
||||
let ini_file = &b"parameter=value
|
||||
key = value2"[..];
|
||||
|
||||
let ini_without_key_value = &b"key = value2"[..];
|
||||
|
||||
let res = key_value(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, (o1, o2)) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn parse_key_value_with_space_test() {
|
||||
let ini_file = &b"parameter = value
|
||||
key = value2"[..];
|
||||
|
||||
let ini_without_key_value = &b"key = value2"[..];
|
||||
|
||||
let res = key_value(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, (o1, o2)) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_key_value_with_comment_test() {
|
||||
let ini_file = &b"parameter=value;abc
|
||||
key = value2"[..];
|
||||
|
||||
let ini_without_key_value = &b"key = value2"[..];
|
||||
|
||||
let res = key_value(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, (o1, o2)) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_multiple_keys_and_values_test() {
|
||||
let ini_file = &b"parameter=value;abc
|
||||
|
||||
key = value2
|
||||
|
||||
[category]"[..];
|
||||
|
||||
let ini_without_key_value = &b"[category]"[..];
|
||||
|
||||
let res = keys_and_values(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, ref o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
let mut expected: HashMap<&str, &str> = HashMap::new();
|
||||
expected.insert("parameter", "value");
|
||||
expected.insert("key", "value2");
|
||||
assert_eq!(res, IResult::Done(ini_without_key_value, expected));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_category_then_multiple_keys_and_values_test() {
|
||||
//FIXME: there can be an empty line or a comment line after a category
|
||||
let ini_file = &b"[abcd]
|
||||
parameter=value;abc
|
||||
|
||||
key = value2
|
||||
|
||||
[category]"[..];
|
||||
|
||||
let ini_after_parser = &b"[category]"[..];
|
||||
|
||||
let res = category_and_keys(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, ref o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
let mut expected_h: HashMap<&str, &str> = HashMap::new();
|
||||
expected_h.insert("parameter", "value");
|
||||
expected_h.insert("key", "value2");
|
||||
assert_eq!(res, IResult::Done(ini_after_parser, ("abcd", expected_h)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_multiple_categories_test() {
|
||||
let ini_file = &b"[abcd]
|
||||
|
||||
parameter=value;abc
|
||||
|
||||
key = value2
|
||||
|
||||
[category]
|
||||
parameter3=value3
|
||||
key4 = value4
|
||||
"[..];
|
||||
|
||||
let ini_after_parser = &b""[..];
|
||||
|
||||
let res = categories(ini_file);
|
||||
//println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, ref o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
let mut expected_1: HashMap<&str, &str> = HashMap::new();
|
||||
expected_1.insert("parameter", "value");
|
||||
expected_1.insert("key", "value2");
|
||||
let mut expected_2: HashMap<&str, &str> = HashMap::new();
|
||||
expected_2.insert("parameter3", "value3");
|
||||
expected_2.insert("key4", "value4");
|
||||
let mut expected_h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
|
||||
expected_h.insert("abcd", expected_1);
|
||||
expected_h.insert("category", expected_2);
|
||||
assert_eq!(res, IResult::Done(ini_after_parser, expected_h));
|
||||
}
|
|
@ -1,251 +0,0 @@
|
|||
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use nom::IResult;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
fn is_alphabetic(chr:char) -> bool {
|
||||
(chr as u8 >= 0x41 && chr as u8 <= 0x5A) || (chr as u8 >= 0x61 && chr as u8 <= 0x7A)
|
||||
}
|
||||
|
||||
fn is_digit(chr: char) -> bool {
|
||||
chr as u8 >= 0x30 && chr as u8 <= 0x39
|
||||
}
|
||||
|
||||
fn is_alphanumeric(chr: char) -> bool {
|
||||
is_alphabetic(chr) || is_digit(chr)
|
||||
}
|
||||
|
||||
fn is_space(chr:char) -> bool {
|
||||
chr == ' ' || chr == '\t'
|
||||
}
|
||||
|
||||
fn is_line_ending_or_comment(chr:char) -> bool {
|
||||
chr == ';' || chr == '\n'
|
||||
}
|
||||
|
||||
named!(alphanumeric<&str,&str>, take_while_s!(is_alphanumeric));
|
||||
named!(not_line_ending<&str,&str>, is_not_s!("\r\n"));
|
||||
named!(space<&str,&str>, take_while_s!(is_space));
|
||||
named!(space_or_line_ending<&str,&str>, is_a_s!(" \r\n"));
|
||||
|
||||
fn right_bracket(c:char) -> bool {
|
||||
c == ']'
|
||||
}
|
||||
|
||||
named!(category <&str, &str>,
|
||||
chain!(
|
||||
tag_s!("[") ~
|
||||
name: take_till_s!(right_bracket) ~
|
||||
tag_s!("]") ~
|
||||
space_or_line_ending? ,
|
||||
||{ name }
|
||||
)
|
||||
);
|
||||
|
||||
named!(key_value <&str,(&str,&str)>,
|
||||
chain!(
|
||||
key: alphanumeric ~
|
||||
space? ~
|
||||
tag_s!("=") ~
|
||||
space? ~
|
||||
val: take_till_s!(is_line_ending_or_comment) ~
|
||||
space? ~
|
||||
pair!(tag_s!(";"), not_line_ending)? ~
|
||||
space_or_line_ending? ,
|
||||
||{(key, val)}
|
||||
)
|
||||
);
|
||||
|
||||
named!(keys_and_values_aggregator<&str, Vec<(&str,&str)> >, many0!(key_value));
|
||||
|
||||
fn keys_and_values(input:&str) -> IResult<&str, HashMap<&str, &str> > {
|
||||
let mut h: HashMap<&str, &str> = HashMap::new();
|
||||
|
||||
match keys_and_values_aggregator(input) {
|
||||
IResult::Done(i,tuple_vec) => {
|
||||
for &(k,v) in &tuple_vec {
|
||||
h.insert(k, v);
|
||||
}
|
||||
IResult::Done(i, h)
|
||||
},
|
||||
IResult::Incomplete(a) => IResult::Incomplete(a),
|
||||
IResult::Error(a) => IResult::Error(a)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
named!(category_and_keys<&str,(&str,HashMap<&str,&str>)>,
|
||||
pair!(category, keys_and_values)
|
||||
);
|
||||
|
||||
named!(categories_aggregator<&str, Vec<(&str, HashMap<&str,&str>)> >, many0!(category_and_keys));
|
||||
|
||||
fn categories(input: &str) -> IResult<&str, HashMap<&str, HashMap<&str, &str> > > {
|
||||
let mut h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
|
||||
|
||||
match categories_aggregator(input) {
|
||||
IResult::Done(i,tuple_vec) => {
|
||||
for &(k,ref v) in &tuple_vec {
|
||||
h.insert(k, v.clone());
|
||||
}
|
||||
IResult::Done(i, h)
|
||||
},
|
||||
IResult::Incomplete(a) => IResult::Incomplete(a),
|
||||
IResult::Error(a) => IResult::Error(a)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn parse_category_test() {
|
||||
let ini_file = "[category]
|
||||
|
||||
parameter=value
|
||||
key = value2";
|
||||
|
||||
let ini_without_category = "parameter=value
|
||||
key = value2";
|
||||
|
||||
let res = category(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, o) => println!("i: {} | o: {:?}", i, o),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
assert_eq!(res, IResult::Done(ini_without_category, "category"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_key_value_test() {
|
||||
let ini_file = "parameter=value
|
||||
key = value2";
|
||||
|
||||
let ini_without_key_value = "key = value2";
|
||||
|
||||
let res = key_value(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, (o1, o2)) => println!("i: {} | o: ({:?},{:?})", i, o1, o2),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_key_value_with_space_test() {
|
||||
let ini_file = "parameter = value
|
||||
key = value2";
|
||||
|
||||
let ini_without_key_value = "key = value2";
|
||||
|
||||
let res = key_value(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, (o1, o2)) => println!("i: {} | o: ({:?},{:?})", i, o1, o2),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_key_value_with_comment_test() {
|
||||
let ini_file = "parameter=value;abc
|
||||
key = value2";
|
||||
|
||||
let ini_without_key_value = "key = value2";
|
||||
|
||||
let res = key_value(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, (o1, o2)) => println!("i: {} | o: ({:?},{:?})", i, o1, o2),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_multiple_keys_and_values_test() {
|
||||
let ini_file = "parameter=value;abc
|
||||
|
||||
key = value2
|
||||
|
||||
[category]";
|
||||
|
||||
let ini_without_key_value = "[category]";
|
||||
|
||||
let res = keys_and_values(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, ref o) => println!("i: {} | o: {:?}", i, o),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
let mut expected: HashMap<&str, &str> = HashMap::new();
|
||||
expected.insert("parameter", "value");
|
||||
expected.insert("key", "value2");
|
||||
assert_eq!(res, IResult::Done(ini_without_key_value, expected));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_category_then_multiple_keys_and_values_test() {
|
||||
//FIXME: there can be an empty line or a comment line after a category
|
||||
let ini_file = "[abcd]
|
||||
parameter=value;abc
|
||||
|
||||
key = value2
|
||||
|
||||
[category]";
|
||||
|
||||
let ini_after_parser = "[category]";
|
||||
|
||||
let res = category_and_keys(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, ref o) => println!("i: {} | o: {:?}", i, o),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
let mut expected_h: HashMap<&str, &str> = HashMap::new();
|
||||
expected_h.insert("parameter", "value");
|
||||
expected_h.insert("key", "value2");
|
||||
assert_eq!(res, IResult::Done(ini_after_parser, ("abcd", expected_h)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_multiple_categories_test() {
|
||||
let ini_file = "[abcd]
|
||||
|
||||
parameter=value;abc
|
||||
|
||||
key = value2
|
||||
|
||||
[category]
|
||||
parameter3=value3
|
||||
key4 = value4
|
||||
";
|
||||
|
||||
let res = categories(ini_file);
|
||||
//println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, ref o) => println!("i: {} | o: {:?}", i, o),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
let mut expected_1: HashMap<&str, &str> = HashMap::new();
|
||||
expected_1.insert("parameter", "value");
|
||||
expected_1.insert("key", "value2");
|
||||
let mut expected_2: HashMap<&str, &str> = HashMap::new();
|
||||
expected_2.insert("parameter3", "value3");
|
||||
expected_2.insert("key4", "value4");
|
||||
let mut expected_h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
|
||||
expected_h.insert("abcd", expected_1);
|
||||
expected_h.insert("category", expected_2);
|
||||
assert_eq!(res, IResult::Done("", expected_h));
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
//#![feature(trace_macros)]
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use nom::{IResult,Needed,HexDisplay,space,digit,be_u16};
|
||||
use std::str;
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Range {
|
||||
start: char,
|
||||
end: char
|
||||
}
|
||||
|
||||
pub fn take_char(input: &[u8]) -> IResult<&[u8], char> {
|
||||
if input.len() > 0 {
|
||||
IResult::Done(&input[1..], input[0] as char)
|
||||
} else {
|
||||
IResult::Incomplete(Needed::Size(1))
|
||||
}
|
||||
}
|
||||
|
||||
//trace_macros!(true);
|
||||
|
||||
#[allow(dead_code)]
|
||||
named!(range<&[u8], Range>,
|
||||
alt!(
|
||||
chain!(
|
||||
start: take_char ~
|
||||
tag!("-") ~
|
||||
end: take_char,
|
||||
|| {
|
||||
Range {
|
||||
start: start,
|
||||
end: end,
|
||||
}
|
||||
}
|
||||
) |
|
||||
map!(
|
||||
take_char,
|
||||
|c| {
|
||||
Range {
|
||||
start: c,
|
||||
end: c,
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
#[allow(dead_code)]
|
||||
named!(literal<&[u8], Vec<char> >,
|
||||
map!(
|
||||
many1!(take_char),
|
||||
|cs| {
|
||||
cs
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn issue_58() {
|
||||
range(&b"abcd"[..]);
|
||||
literal(&b"abcd"[..]);
|
||||
}
|
||||
|
||||
//trace_macros!(false);
|
||||
|
||||
named!(parse_ints< Vec<i32> >, many0!(spaces_or_int));
|
||||
|
||||
fn spaces_or_int(input: &[u8]) -> IResult<&[u8], i32>{
|
||||
println!("{}", input.to_hex(8));
|
||||
chain!(input,
|
||||
opt!(space) ~
|
||||
x: digit,
|
||||
|| {
|
||||
println!("x: {:?}", x);
|
||||
let result = str::from_utf8(x).unwrap();
|
||||
println!("Result: {}", result);
|
||||
println!("int is empty?: {}", x.is_empty());
|
||||
match result.parse(){
|
||||
Ok(i) => i,
|
||||
Err(_) => panic!("UH OH! NOT A DIGIT!")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_142(){
|
||||
let subject = parse_ints(&b"12 34 5689"[..]);
|
||||
let expected = IResult::Done(&b""[..], vec![12, 34, 5689]);
|
||||
assert_eq!(subject, expected);
|
||||
|
||||
let subject = parse_ints(&b"12 34 5689 "[..]);
|
||||
let expected = IResult::Done(&b" "[..], vec![12, 34, 5689]);
|
||||
assert_eq!(subject, expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn usize_length_bytes_issue(){
|
||||
length_bytes!(b"012346", be_u16);
|
||||
}
|
||||
|
||||
/*
|
||||
DOES NOT COMPILE
|
||||
#[test]
|
||||
fn issue_152() {
|
||||
named!(take4, take!(4));
|
||||
named!(xyz, tag!("XYZ"));
|
||||
named!(abc, tag!("abc"));
|
||||
|
||||
|
||||
named!(sw,
|
||||
switch!(take4,
|
||||
b"abcd" => xyz |
|
||||
b"efgh" => abc
|
||||
)
|
||||
);
|
||||
}
|
||||
*/
|
||||
|
||||
#[test]
|
||||
fn take_till_issue() {
|
||||
named!(nothing,
|
||||
take_till!(call!(|_| true))
|
||||
);
|
||||
|
||||
assert_eq!(nothing(b""), IResult::Done(&b""[..], &b""[..]));
|
||||
assert_eq!(nothing(b"abc"), IResult::Done(&b"abc"[..], &b""[..]));
|
||||
}
|
|
@ -1,531 +0,0 @@
|
|||
#![cfg(feature = "stream")]
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use nom::{HexDisplay,Needed,IResult,be_u16,be_u32,be_u64,be_f32,ErrorKind};
|
||||
use nom::{Consumer,ConsumerState,Move,Input,Producer,FileProducer,FileProducerState};
|
||||
use nom::IResult::*;
|
||||
use nom::Err::*;
|
||||
|
||||
use std::str;
|
||||
use std::io::SeekFrom;
|
||||
|
||||
fn mp4_box(input:&[u8]) -> IResult<&[u8], &[u8]> {
|
||||
match be_u32(input) {
|
||||
Done(i, offset) => {
|
||||
let sz: usize = offset as usize;
|
||||
if i.len() >= sz - 4 {
|
||||
Done(&i[(sz-4)..], &i[0..(sz-4)])
|
||||
} else {
|
||||
Incomplete(Needed::Size(offset as usize + 4))
|
||||
}
|
||||
}
|
||||
Error(e) => Error(e),
|
||||
Incomplete(e) => Incomplete(e)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq,Eq,Debug)]
|
||||
struct FileType<'a> {
|
||||
major_brand: &'a str,
|
||||
major_brand_version: &'a [u8],
|
||||
compatible_brands: Vec<&'a str>
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct Mvhd32 {
|
||||
version_flags: u32, // actually:
|
||||
// version: u8,
|
||||
// flags: u24 // 3 bytes
|
||||
created_date: u32,
|
||||
modified_date: u32,
|
||||
scale: u32,
|
||||
duration: u32,
|
||||
speed: f32,
|
||||
volume: u16, // actually a 2 bytes decimal
|
||||
/* 10 bytes reserved */
|
||||
scaleA: f32,
|
||||
rotateB: f32,
|
||||
angleU: f32,
|
||||
rotateC: f32,
|
||||
scaleD: f32,
|
||||
angleV: f32,
|
||||
positionX: f32,
|
||||
positionY: f32,
|
||||
scaleW: f32,
|
||||
preview: u64,
|
||||
poster: u32,
|
||||
selection: u64,
|
||||
current_time: u32,
|
||||
track_id: u32
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct Mvhd64 {
|
||||
version_flags: u32, // actually:
|
||||
// version: u8,
|
||||
// flags: u24 // 3 bytes
|
||||
created_date: u64,
|
||||
modified_date: u64,
|
||||
scale: u32,
|
||||
duration: u64,
|
||||
speed: f32,
|
||||
volume: u16, // actually a 2 bytes decimal
|
||||
/* 10 bytes reserved */
|
||||
scaleA: f32,
|
||||
rotateB: f32,
|
||||
angleU: f32,
|
||||
rotateC: f32,
|
||||
scaleD: f32,
|
||||
angleV: f32,
|
||||
positionX: f32,
|
||||
positionY: f32,
|
||||
scaleW: f32,
|
||||
preview: u64,
|
||||
poster: u32,
|
||||
selection: u64,
|
||||
current_time: u32,
|
||||
track_id: u32
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
named!(mvhd32 <&[u8], MvhdBox>,
|
||||
chain!(
|
||||
version_flags: be_u32 ~
|
||||
created_date: be_u32 ~
|
||||
modified_date: be_u32 ~
|
||||
scale: be_u32 ~
|
||||
duration: be_u32 ~
|
||||
speed: be_f32 ~
|
||||
volume: be_u16 ~ // actually a 2 bytes decimal
|
||||
take!(10) ~
|
||||
scale_a: be_f32 ~
|
||||
rotate_b: be_f32 ~
|
||||
angle_u: be_f32 ~
|
||||
rotate_c: be_f32 ~
|
||||
scale_d: be_f32 ~
|
||||
angle_v: be_f32 ~
|
||||
position_x: be_f32 ~
|
||||
position_y: be_f32 ~
|
||||
scale_w: be_f32 ~
|
||||
preview: be_u64 ~
|
||||
poster: be_u32 ~
|
||||
selection: be_u64 ~
|
||||
current_time: be_u32 ~
|
||||
track_id: be_u32,
|
||||
||{
|
||||
MvhdBox::M32(Mvhd32 {
|
||||
version_flags: version_flags,
|
||||
created_date: created_date,
|
||||
modified_date: modified_date,
|
||||
scale: scale,
|
||||
duration: duration,
|
||||
speed: speed,
|
||||
volume: volume,
|
||||
scaleA: scale_a,
|
||||
rotateB: rotate_b,
|
||||
angleU: angle_u,
|
||||
rotateC: rotate_c,
|
||||
scaleD: scale_d,
|
||||
angleV: angle_v,
|
||||
positionX: position_x,
|
||||
positionY: position_y,
|
||||
scaleW: scale_w,
|
||||
preview: preview,
|
||||
poster: poster,
|
||||
selection: selection,
|
||||
current_time: current_time,
|
||||
track_id: track_id
|
||||
})
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
named!(mvhd64 <&[u8], MvhdBox>,
|
||||
chain!(
|
||||
version_flags: be_u32 ~
|
||||
created_date: be_u64 ~
|
||||
modified_date: be_u64 ~
|
||||
scale: be_u32 ~
|
||||
duration: be_u64 ~
|
||||
speed: be_f32 ~
|
||||
volume: be_u16 ~ // actually a 2 bytes decimal
|
||||
take!(10) ~
|
||||
scale_a: be_f32 ~
|
||||
rotate_b: be_f32 ~
|
||||
angle_u: be_f32 ~
|
||||
rotate_c: be_f32 ~
|
||||
scale_d: be_f32 ~
|
||||
angle_v: be_f32 ~
|
||||
position_x: be_f32 ~
|
||||
position_y: be_f32 ~
|
||||
scale_w: be_f32 ~
|
||||
preview: be_u64 ~
|
||||
poster: be_u32 ~
|
||||
selection: be_u64 ~
|
||||
current_time: be_u32 ~
|
||||
track_id: be_u32,
|
||||
||{
|
||||
MvhdBox::M64(Mvhd64 {
|
||||
version_flags: version_flags,
|
||||
created_date: created_date,
|
||||
modified_date: modified_date,
|
||||
scale: scale,
|
||||
duration: duration,
|
||||
speed: speed,
|
||||
volume: volume,
|
||||
scaleA: scale_a,
|
||||
rotateB: rotate_b,
|
||||
angleU: angle_u,
|
||||
rotateC: rotate_c,
|
||||
scaleD: scale_d,
|
||||
angleV: angle_v,
|
||||
positionX: position_x,
|
||||
positionY: position_y,
|
||||
scaleW: scale_w,
|
||||
preview: preview,
|
||||
poster: poster,
|
||||
selection: selection,
|
||||
current_time: current_time,
|
||||
track_id: track_id
|
||||
})
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[derive(Debug,Clone)]
|
||||
pub enum MvhdBox {
|
||||
M32(Mvhd32),
|
||||
M64(Mvhd64)
|
||||
}
|
||||
|
||||
#[derive(Debug,Clone)]
|
||||
pub enum MoovBox {
|
||||
Mdra,
|
||||
Dref,
|
||||
Cmov,
|
||||
Rmra,
|
||||
Iods,
|
||||
Mvhd(MvhdBox),
|
||||
Clip,
|
||||
Trak,
|
||||
Udta
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum MP4BoxType {
|
||||
Ftyp,
|
||||
Moov,
|
||||
Mdat,
|
||||
Free,
|
||||
Skip,
|
||||
Wide,
|
||||
Mdra,
|
||||
Dref,
|
||||
Cmov,
|
||||
Rmra,
|
||||
Iods,
|
||||
Mvhd,
|
||||
Clip,
|
||||
Trak,
|
||||
Udta,
|
||||
Unknown
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MP4BoxHeader {
|
||||
length: u32,
|
||||
tag: MP4BoxType
|
||||
}
|
||||
|
||||
named!(brand_name<&[u8],&str>, map_res!(take!(4), str::from_utf8));
|
||||
|
||||
named!(filetype_parser<&[u8], FileType>,
|
||||
chain!(
|
||||
m: brand_name ~
|
||||
v: take!(4) ~
|
||||
c: many0!(brand_name) ,
|
||||
||{ FileType{ major_brand: m, major_brand_version:v, compatible_brands: c } }
|
||||
)
|
||||
);
|
||||
|
||||
fn mvhd_box(input:&[u8]) -> IResult<&[u8],MvhdBox> {
|
||||
let res = if input.len() < 100 {
|
||||
Incomplete(Needed::Size(100))
|
||||
} else if input.len() == 100 {
|
||||
mvhd32(input)
|
||||
} else if input.len() == 112 {
|
||||
mvhd64(input)
|
||||
} else {
|
||||
Error(Position(ErrorKind::Custom(32),input))
|
||||
};
|
||||
println!("res: {:?}", res);
|
||||
res
|
||||
}
|
||||
|
||||
fn unknown_box_type(input:&[u8]) -> IResult<&[u8], MP4BoxType> {
|
||||
Done(input, MP4BoxType::Unknown)
|
||||
}
|
||||
|
||||
//named!(box_type<&[u8], MP4BoxType>,
|
||||
fn box_type(input: &[u8]) -> IResult<&[u8], MP4BoxType, u32> {
|
||||
alt!(input,
|
||||
tag!("ftyp") => { |_| MP4BoxType::Ftyp } |
|
||||
tag!("moov") => { |_| MP4BoxType::Moov } |
|
||||
tag!("mdat") => { |_| MP4BoxType::Mdat } |
|
||||
tag!("free") => { |_| MP4BoxType::Free } |
|
||||
tag!("skip") => { |_| MP4BoxType::Skip } |
|
||||
tag!("wide") => { |_| MP4BoxType::Wide } |
|
||||
unknown_box_type
|
||||
)
|
||||
}
|
||||
|
||||
// warning, an alt combinator with 9 branches containing a tag combinator
|
||||
// can make the compilation very slow. Use functions as sub parsers,
|
||||
// or split into multiple alt! parsers if it gets slow
|
||||
named!(moov_type<&[u8], MP4BoxType>,
|
||||
alt!(
|
||||
tag!("mdra") => { |_| MP4BoxType::Mdra } |
|
||||
tag!("dref") => { |_| MP4BoxType::Dref } |
|
||||
tag!("cmov") => { |_| MP4BoxType::Cmov } |
|
||||
tag!("rmra") => { |_| MP4BoxType::Rmra } |
|
||||
tag!("iods") => { |_| MP4BoxType::Iods } |
|
||||
tag!("mvhd") => { |_| MP4BoxType::Mvhd } |
|
||||
tag!("clip") => { |_| MP4BoxType::Clip } |
|
||||
tag!("trak") => { |_| MP4BoxType::Trak } |
|
||||
tag!("udta") => { |_| MP4BoxType::Udta }
|
||||
)
|
||||
);
|
||||
|
||||
named!(box_header<&[u8],MP4BoxHeader>,
|
||||
chain!(
|
||||
length: be_u32 ~
|
||||
tag: box_type ,
|
||||
|| { MP4BoxHeader{ length: length, tag: tag} }
|
||||
)
|
||||
);
|
||||
|
||||
named!(moov_header<&[u8],MP4BoxHeader>,
|
||||
chain!(
|
||||
length: be_u32 ~
|
||||
tag: moov_type ,
|
||||
|| { MP4BoxHeader{ length: length, tag: tag} }
|
||||
)
|
||||
);
|
||||
|
||||
#[derive(Debug,PartialEq,Eq)]
|
||||
enum MP4State {
|
||||
Main,
|
||||
Moov,
|
||||
Mvhd(usize)
|
||||
}
|
||||
|
||||
pub struct MP4Consumer {
|
||||
state: MP4State,
|
||||
moov_bytes: usize,
|
||||
c_state: ConsumerState<(), (), Move>
|
||||
}
|
||||
|
||||
impl MP4Consumer {
|
||||
fn new() -> MP4Consumer {
|
||||
MP4Consumer { state: MP4State::Main, moov_bytes: 0, c_state: ConsumerState::Continue(Move::Consume(0)) }
|
||||
}
|
||||
|
||||
fn consume_main(&mut self, input: Input<&[u8]>) -> ConsumerState<(), (), Move> {
|
||||
//println!("\nparsing box header:\n{}", input.to_hex(8));
|
||||
match input {
|
||||
Input::Eof(None) => ConsumerState::Done(Move::Consume(0), ()),
|
||||
Input::Empty => ConsumerState::Continue(Move::Consume(0)),
|
||||
Input::Element(sl) | Input::Eof(Some(sl)) => {
|
||||
match box_header(sl) {
|
||||
Done(i, header) => {
|
||||
match header.tag {
|
||||
MP4BoxType::Ftyp => {
|
||||
println!("-> FTYP");
|
||||
match filetype_parser(&i[0..(header.length as usize - 8)]) {
|
||||
Done(rest, filetype_header) => {
|
||||
println!("filetype header: {:?}", filetype_header);
|
||||
//return ConsumerState::Await(header.length as usize, header.length as usize - 8);
|
||||
return ConsumerState::Continue(Move::Consume(sl.offset(rest)));
|
||||
}
|
||||
Error(a) => {
|
||||
println!("ftyp parsing error: {:?}", a);
|
||||
assert!(false);
|
||||
return ConsumerState::Error(());
|
||||
},
|
||||
Incomplete(n) => {
|
||||
println!("ftyp incomplete -> await: {}", sl.len());
|
||||
return ConsumerState::Continue(Move::Await(n));
|
||||
//return ConsumerState::Await(0, input.len() + 100);
|
||||
}
|
||||
}
|
||||
},
|
||||
MP4BoxType::Moov => {
|
||||
println!("-> MOOV");
|
||||
self.state = MP4State::Moov;
|
||||
self.moov_bytes = header.length as usize - 8;
|
||||
return ConsumerState::Continue(Move::Consume(sl.offset(i)));
|
||||
},
|
||||
MP4BoxType::Mdat => println!("-> MDAT"),
|
||||
MP4BoxType::Free => println!("-> FREE"),
|
||||
MP4BoxType::Skip => println!("-> SKIP"),
|
||||
MP4BoxType::Wide => println!("-> WIDE"),
|
||||
MP4BoxType::Unknown => {
|
||||
println!("-> UNKNOWN");
|
||||
println!("bytes:\n{}", (sl).to_hex(8));
|
||||
//return ConsumerState::Continue(Move::Consume(sl.offset(i)));
|
||||
},
|
||||
_ => { println!("invalid"); return ConsumerState::Error(())}
|
||||
}
|
||||
return ConsumerState::Continue(Move::Seek(SeekFrom::Current((header.length) as i64)))
|
||||
},
|
||||
Error(a) => {
|
||||
println!("mp4 parsing error: {:?}", a);
|
||||
assert!(false);
|
||||
return ConsumerState::Error(());
|
||||
},
|
||||
Incomplete(i) => {
|
||||
// FIXME: incomplete should send the required size
|
||||
println!("mp4 incomplete -> await: {}", sl.len());
|
||||
return ConsumerState::Continue(Move::Await(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn consume_moov(&mut self, input: Input<&[u8]>) -> ConsumerState<(), (), Move> {
|
||||
//println!("\nparsing moov box(remaining {} bytes):\n{}", self.moov_bytes, input.to_hex(8));
|
||||
match input {
|
||||
Input::Eof(None) => return ConsumerState::Error(()),
|
||||
Input::Empty => return ConsumerState::Continue(Move::Consume(0)),
|
||||
Input::Element(sl) | Input::Eof(Some(sl)) => {
|
||||
if self.moov_bytes == 0 {
|
||||
//println!("finished parsing moov atom, continuing with main parser");
|
||||
self.state = MP4State::Main;
|
||||
return ConsumerState::Continue(Move::Consume(0));
|
||||
}
|
||||
match moov_header(sl) {
|
||||
Done(i, header) => {
|
||||
match header.tag {
|
||||
MP4BoxType::Mvhd => {
|
||||
println!("-> MVHD");
|
||||
self.state = MP4State::Mvhd(header.length as usize - 8);
|
||||
// TODO: check for overflow here
|
||||
self.moov_bytes = self.moov_bytes - (sl.len() - i.len());
|
||||
println!("remaining moov_bytes: {}", self.moov_bytes);
|
||||
return ConsumerState::Continue(Move::Consume(sl.offset(i)));
|
||||
},
|
||||
MP4BoxType::Wide => println!("-> WIDE"),
|
||||
MP4BoxType::Mdra => println!("-> MDRA"),
|
||||
MP4BoxType::Dref => println!("-> DREF"),
|
||||
MP4BoxType::Cmov => println!("-> CMOV"),
|
||||
MP4BoxType::Rmra => println!("-> RMRA"),
|
||||
MP4BoxType::Iods => println!("-> IODS"),
|
||||
MP4BoxType::Clip => println!("-> CLIP"),
|
||||
MP4BoxType::Trak => println!("-> TRAK"),
|
||||
MP4BoxType::Udta => println!("-> UDTA"),
|
||||
MP4BoxType::Unknown => println!("-> MOOV UNKNOWN"),
|
||||
_ => { println!("invalid header here: {:?}", header.tag); return ConsumerState::Error(());}
|
||||
};
|
||||
// TODO: check for overflow here
|
||||
self.moov_bytes = self.moov_bytes - header.length as usize;
|
||||
println!("remaining moov_bytes: {}", self.moov_bytes);
|
||||
return ConsumerState::Continue(Move::Seek(SeekFrom::Current((header.length) as i64)))
|
||||
},
|
||||
Error(a) => {
|
||||
println!("moov parsing error: {:?}", a);
|
||||
println!("data:\n{}", sl.to_hex(8));
|
||||
assert!(false);
|
||||
return ConsumerState::Error(());
|
||||
},
|
||||
Incomplete(i) => {
|
||||
println!("moov incomplete -> await: {}", sl.len());
|
||||
return ConsumerState::Continue(Move::Await(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
consumer_from_parser!(MvhdConsumer<MvhdBox>, mvhd_box);
|
||||
|
||||
impl<'a> Consumer<&'a[u8], (), (), Move> for MP4Consumer {
|
||||
fn handle(&mut self, input: Input<&[u8]>) -> &ConsumerState<(), (), Move> {
|
||||
match self.state {
|
||||
MP4State::Main => {
|
||||
self.c_state = self.consume_main(input);
|
||||
},
|
||||
MP4State::Moov => {
|
||||
self.c_state = self.consume_moov(input);
|
||||
},
|
||||
MP4State::Mvhd(sz) => {
|
||||
match input {
|
||||
Input::Eof(None) => self.c_state = ConsumerState::Error(()),
|
||||
Input::Empty => self.c_state = ConsumerState::Continue(Move::Consume(0)),
|
||||
Input::Element(sl) | Input::Eof(Some(sl)) => {
|
||||
let mut c = MvhdConsumer{ state:ConsumerState::Continue(Move::Consume(0)) };
|
||||
self.c_state = c.handle(Input::Element(&sl[..sz])).flat_map(|m, _| {
|
||||
self.state = MP4State::Moov;
|
||||
ConsumerState::Continue(m)
|
||||
});
|
||||
println!("found mvhd?: {:?}", c.state());
|
||||
match self.c_state {
|
||||
ConsumerState::Continue(Move::Consume(sz)) => self.moov_bytes = self.moov_bytes - sz,
|
||||
ConsumerState::Continue(Move::Seek(SeekFrom::Current(sz))) => self.moov_bytes = self.moov_bytes - (sz as usize),
|
||||
_ => ()
|
||||
};
|
||||
println!("remaining moov_bytes: {}", self.moov_bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
&self.c_state
|
||||
}
|
||||
|
||||
fn state(&self) -> &ConsumerState<(), (), Move> {
|
||||
&self.c_state
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_must_use)]
|
||||
fn explore_mp4_file(filename: &str) {
|
||||
let mut p = FileProducer::new(filename, 400).unwrap();
|
||||
let mut c = MP4Consumer{state: MP4State::Main, moov_bytes: 0, c_state: ConsumerState::Continue(Move::Consume(0))};
|
||||
//c.run(&mut p);
|
||||
while let &ConsumerState::Continue(mv) = p.apply(&mut c) {
|
||||
println!("move: {:?}", mv);
|
||||
}
|
||||
println!("last consumer state: {:?} | last state: {:?}", c.c_state, c.state);
|
||||
|
||||
if let ConsumerState::Done(Move::Consume(0), ()) = c.c_state {
|
||||
println!("consumer state ok");
|
||||
} else {
|
||||
assert!(false, "consumer should have reached Done state");
|
||||
}
|
||||
assert_eq!(c.state, MP4State::Main);
|
||||
assert_eq!(p.state(), FileProducerState::Eof);
|
||||
//assert!(false);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn small_test() {
|
||||
explore_mp4_file("assets/small.mp4");
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn big_bunny_test() {
|
||||
explore_mp4_file("assets/bigbuckbunny.mp4");
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,160 +0,0 @@
|
|||
#![cfg(feature = "stream")]
|
||||
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use nom::{Producer,Consumer,ConsumerState,Input,Move,MemProducer,IResult,HexDisplay};
|
||||
|
||||
#[derive(PartialEq,Eq,Debug)]
|
||||
enum State {
|
||||
Beginning,
|
||||
Middle,
|
||||
End,
|
||||
Done,
|
||||
Error
|
||||
}
|
||||
|
||||
struct TestConsumer {
|
||||
state: State,
|
||||
c_state: ConsumerState<usize,(),Move>,
|
||||
counter: usize,
|
||||
}
|
||||
|
||||
named!(om_parser, tag!("om"));
|
||||
named!(nomnom_parser<&[u8],Vec<&[u8]> >, many1!(tag!("nom")));
|
||||
named!(end_parser, tag!("kthxbye"));
|
||||
|
||||
impl<'a> Consumer<&'a[u8], usize, (), Move> for TestConsumer {
|
||||
fn state(&self) -> &ConsumerState<usize,(),Move> {
|
||||
&self.c_state
|
||||
}
|
||||
|
||||
fn handle(&mut self, input: Input<&'a [u8]>) -> &ConsumerState<usize,(),Move> {
|
||||
match self.state {
|
||||
State::Beginning => {
|
||||
match input {
|
||||
Input::Empty | Input::Eof(None) => {
|
||||
self.state = State::Error;
|
||||
self.c_state = ConsumerState::Error(());
|
||||
},
|
||||
Input::Element(sl) | Input::Eof(Some(sl)) => {
|
||||
match om_parser(sl) {
|
||||
IResult::Error(_) => {
|
||||
self.state = State::Error;
|
||||
self.c_state = ConsumerState::Error(());
|
||||
},
|
||||
IResult::Incomplete(n) => {
|
||||
self.c_state = ConsumerState::Continue(Move::Await(n));
|
||||
},
|
||||
IResult::Done(i,_) => {
|
||||
self.state = State::Middle;
|
||||
self.c_state = ConsumerState::Continue(Move::Consume(sl.offset(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
State::Middle => {
|
||||
match input {
|
||||
Input::Empty | Input::Eof(None) => {
|
||||
self.state = State::Error;
|
||||
self.c_state = ConsumerState::Error(());
|
||||
},
|
||||
Input::Element(sl) | Input::Eof(Some(sl)) => {
|
||||
match nomnom_parser(sl) {
|
||||
IResult::Error(_) => {
|
||||
self.state = State::End;
|
||||
self.c_state = ConsumerState::Continue(Move::Consume(0));
|
||||
},
|
||||
IResult::Incomplete(n) => {
|
||||
println!("Middle got Incomplete({:?})", n);
|
||||
self.c_state = ConsumerState::Continue(Move::Await(n));
|
||||
},
|
||||
IResult::Done(i,noms_vec) => {
|
||||
self.counter = self.counter + noms_vec.len();
|
||||
self.state = State::Middle;
|
||||
self.c_state = ConsumerState::Continue(Move::Consume(sl.offset(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
State::End => {
|
||||
match input {
|
||||
Input::Empty | Input::Eof(None) => {
|
||||
self.state = State::Error;
|
||||
self.c_state = ConsumerState::Error(());
|
||||
},
|
||||
Input::Element(sl) | Input::Eof(Some(sl)) => {
|
||||
match end_parser(sl) {
|
||||
IResult::Error(_) => {
|
||||
self.state = State::Error;
|
||||
self.c_state = ConsumerState::Error(());
|
||||
},
|
||||
IResult::Incomplete(n) => {
|
||||
self.c_state = ConsumerState::Continue(Move::Await(n));
|
||||
},
|
||||
IResult::Done(i,_) => {
|
||||
self.state = State::Done;
|
||||
self.c_state = ConsumerState::Done(Move::Consume(sl.offset(i)), self.counter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
State::Done | State::Error => {
|
||||
// this should not be called
|
||||
self.state = State::Error;
|
||||
self.c_state = ConsumerState::Error(())
|
||||
}
|
||||
};
|
||||
&self.c_state
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nom1() {
|
||||
let mut p = MemProducer::new(&b"omnomkthxbye"[..], 8);
|
||||
let mut c = TestConsumer{state: State::Beginning, counter: 0, c_state: ConsumerState::Continue(Move::Consume(0))};
|
||||
while let &ConsumerState::Continue(Move::Consume(_)) = p.apply(&mut c) {
|
||||
}
|
||||
|
||||
assert_eq!(c.counter, 1);
|
||||
assert_eq!(c.state, State::Done);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nomnomnom() {
|
||||
let mut p = MemProducer::new(&b"omnomnomnomkthxbye"[..], 9);
|
||||
let mut c = TestConsumer{state: State::Beginning, counter: 0, c_state: ConsumerState::Continue(Move::Consume(0))};
|
||||
while let &ConsumerState::Continue(_) = p.apply(&mut c) {
|
||||
}
|
||||
|
||||
assert_eq!(c.counter, 3);
|
||||
assert_eq!(c.state, State::Done);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_nomnom() {
|
||||
let mut p = MemProducer::new(&b"omkthxbye"[..], 8);
|
||||
let mut c = TestConsumer{state: State::Beginning, counter: 0, c_state: ConsumerState::Continue(Move::Consume(0))};
|
||||
while let &ConsumerState::Continue(_) = p.apply(&mut c) {
|
||||
}
|
||||
|
||||
assert_eq!(c.counter, 0);
|
||||
assert_eq!(c.state, State::Done);
|
||||
}
|
||||
|
||||
/*
|
||||
#[test]
|
||||
fn impolite() {
|
||||
let mut p = MemProducer::new(&b"omnomnomnom"[..], 11);
|
||||
let mut c = TestConsumer{state: State::Beginning, counter: 0, c_state: ConsumerState::Continue(Move::Consume(0))};
|
||||
while let &ConsumerState::Continue(cont) = p.apply(&mut c) {
|
||||
println!("continue {:?}", cont);
|
||||
}
|
||||
|
||||
assert_eq!(c.counter, 3);
|
||||
assert_eq!(c.state, State::End);
|
||||
}
|
||||
*/
|
|
@ -1,44 +0,0 @@
|
|||
#![cfg(feature = "stream")]
|
||||
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use nom::{IResult,Producer,FileProducer,not_line_ending};
|
||||
|
||||
use std::str;
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[test]
|
||||
#[allow(unused_must_use)]
|
||||
fn tag() {
|
||||
FileProducer::new("assets/links.txt", 20).map(|producer: FileProducer| {
|
||||
let mut p = producer;
|
||||
p.refill();
|
||||
|
||||
consumer_from_parser!(PrintConsumer<()>, flat_map!(map_res!(tag!("https!"), str::from_utf8), print));
|
||||
let mut cs = PrintConsumer::new();
|
||||
for _ in 1..4 {
|
||||
p.apply(&mut cs);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn print<T: Debug>(input: T) -> IResult<T,()> {
|
||||
println!("{:?}", input);
|
||||
IResult::Done(input, ())
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn is_not() {
|
||||
//is_not!(foo b"\r\n");
|
||||
named!(foo<&[u8],&[u8]>, is_not!(&b"\r\n"[..]));
|
||||
let a = &b"ab12cd\nefgh"[..];
|
||||
assert_eq!(foo(a), IResult::Done(&b"\nefgh"[..], &b"ab12cd"[..]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exported_public_method_defined_by_macro() {
|
||||
let a = &b"ab12cd\nefgh"[..];
|
||||
assert_eq!(not_line_ending(a), IResult::Done(&b"\nefgh"[..], &b"ab12cd"[..]));
|
||||
}
|
Загрузка…
Ссылка в новой задаче