Mercurial > lbo > hg > rcombinators
changeset 20:728109a7df09
Implement Ignore combinator.
author | Lewin Bormann <lewin@lewin-bormann.info> |
---|---|
date | Tue, 04 Jun 2019 18:49:36 +0200 |
parents | 8920f09345d5 |
children | d90066bf03d0 |
files | src/combinators.rs src/lib.rs src/primitives.rs |
diffstat | 3 files changed, 62 insertions(+), 17 deletions(-) [+] |
line wrap: on
line diff
--- a/src/combinators.rs Tue Jun 04 00:09:14 2019 +0200 +++ b/src/combinators.rs Tue Jun 04 18:49:36 2019 +0200 @@ -328,6 +328,32 @@ } } +/// Ignore ignores the result of an inner parser, effectively hiding the result. Useful if consumed +/// input should not be processed further, and simplifies types in combined parsers. +pub struct Ignore<Inner: Parser> { + inner: Inner, +} + +impl<Inner: Parser> Ignore<Inner> { + pub fn new(p: Inner) -> Ignore<Inner> { + Ignore { inner: p } + } +} + +impl<R, P: Parser<Result = R>> Parser for Ignore<P> { + type Result = (); + fn parse( + &mut self, + st: &mut ParseState<impl Iterator<Item = char>>, + ) -> ParseResult<Self::Result> { + match self.inner.parse(st) { + Ok(_) => Ok(()), + Err(e) => Err(e), + } + } +} + + #[cfg(test)] mod tests { use super::*;
--- a/src/lib.rs Tue Jun 04 00:09:14 2019 +0200 +++ b/src/lib.rs Tue Jun 04 18:49:36 2019 +0200 @@ -1,5 +1,8 @@ #![allow(dead_code)] +//! rcombinators is a parser combinator library without special magic. It aims to be both easy to +//! use and reasonably fast, without using too much special syntax or macros. + #[allow(unused_imports)] #[macro_use] extern crate time_test; @@ -8,3 +11,8 @@ mod parser; mod primitives; mod state; + +pub use combinators::*; +pub use parser::*; +pub use primitives::*; +pub use state::*;
--- a/src/primitives.rs Tue Jun 04 00:09:14 2019 +0200 +++ b/src/primitives.rs Tue Jun 04 18:49:36 2019 +0200 @@ -1,4 +1,4 @@ -use crate::combinators::{Maybe, Repeat, RepeatSpec, Sequence}; +use crate::combinators::{Ignore, Maybe, Repeat, RepeatSpec, Sequence}; use crate::parser::{execerr, ParseError, ParseResult, Parser}; use crate::state::ParseState; @@ -7,6 +7,7 @@ use std::iter::FromIterator; use std::str::{self, FromStr}; +/// StringParser consumes a fixed string. pub struct StringParser(String); impl StringParser { @@ -46,6 +47,10 @@ } } +/// Int parses an integer resulting in `IType`. It is recommended to use the specializations such +/// as `Int64`, `Uint32`, etc. +/// +/// This is an optimized parser, not using combinators. pub struct Int<IType: Default + str::FromStr>(IType); pub type Int128 = Int<i128>; @@ -79,8 +84,8 @@ let mut widebuf: Option<Vec<char>> = None; let mut i = 0; + // Only check for negative sign if type is a signed integer. if IType::try_from(-1 as i8).is_ok() { - // Check for negative sign, only if integer can be signed. match st.peek() { None => return Err(ParseError::EOF), Some('-') => { @@ -146,10 +151,11 @@ } fn assemble_float( - s: Option<String>, + s: Option<()>, big: String, dot: Option<String>, mut little: Option<String>, + exp: Option<((), i32)>, ) -> ParseResult<f64> { if dot.is_some() && little.is_none() { little = Some("0".to_string()); @@ -167,20 +173,25 @@ Err(e) => return Err(execerr(e.description())), } } - let minus = if s.is_some() { -1. } else { 1. }; - return Ok(minus * (bigf + littlef)); + let mut multiplier: f64 = if s.is_some() { -1. } else { 1. }; + if let Some((_, e)) = exp { + multiplier = (10. as f64).powi(e); + } + return Ok(multiplier * (bigf + littlef)); } -/// float parses floats in the format of `[-]dd[.[dd]]`. Currently, `e` notation is not supported. +/// float parses floats in the format of `[-]dd[.[dd]][e[-]ddd]`. /// /// TODO: Compare with "native" parser, i.e. without combinators, and keep this as example. pub fn float() -> impl Parser<Result = f64> { - let minus = Maybe::new(StringParser::new("-")); - let digits = string_of("0123456789", RepeatSpec::Min(1)); + let digits_set = "0123456789"; + let minus = Maybe::new(Ignore::new(StringParser::new("-"))); + let digits = string_of(digits_set, RepeatSpec::Min(1)); let point = Maybe::new(StringParser::new(".")); - let smalldigits = Maybe::new(string_of("0123456789", RepeatSpec::Min(1))); - let parser = Sequence::new((minus, digits, point, smalldigits)) - .apply(|(m, d, p, sd)| assemble_float(m, d, p, sd)); + let smalldigits = Maybe::new(string_of(digits_set, RepeatSpec::Min(1))); + let exp = Maybe::new(Sequence::new((Ignore::new(StringParser::new("e")), Int32::new()))); + let parser = Sequence::new((minus, digits, point, smalldigits, exp)) + .apply(|(m, d, p, sd, exp)| assemble_float(m, d, p, sd, exp)); parser } @@ -197,7 +208,7 @@ } } -/// OneOf matches any character that is in its specification. +/// OneOf matches any character that is (or is not) in its set. pub struct OneOf(HashSet<char>, bool); impl OneOf { @@ -205,7 +216,7 @@ OneOf(chars.as_ref().chars().collect(), false) } /// Create a OneOf parser that parses all characters *not* in the given set. - pub fn new_inverse<S: AsRef<str>>(chars: S) -> OneOf { + pub fn new_none_of<S: AsRef<str>>(chars: S) -> OneOf { OneOf(chars.as_ref().chars().collect(), true) } } @@ -220,7 +231,7 @@ Some(c) => { let present = self.0.contains(&c); if self.1 { - // Inverse mode + // Inverse mode: Match all chars that are not in set. if present { return Err(ParseError::Fail("char (inverse) not matched", st.index())); } @@ -249,7 +260,7 @@ /// A parser that parses a string consisting of any characters not in the set. fn string_none_of<S: AsRef<str>>(chars: S, rp: RepeatSpec) -> impl Parser<Result = String> { - let oo = OneOf::new_inverse(chars); + let oo = OneOf::new_none_of(chars); let rp = Repeat::new(oo, rp); let make_string = |charvec: Vec<char>| Ok(String::from_iter(charvec.into_iter())); rp.apply(make_string) @@ -295,9 +306,9 @@ #[test] fn test_parse_floats() { - let mut ps = ParseState::new("1 1. 1.5 -1.5 -1.75"); + let mut ps = ParseState::new("1 1. 1.5 -1.5 -1.75 2.5e-4"); let mut p = float(); - let want = vec![1., 1., 1.5, -1.5, -1.75]; + let want = vec![1., 1., 1.5, -1.5, -1.75, 2.5e-4]; for &f in want.iter() { assert_eq!(Ok(f), p.parse(&mut ps)); let _ = StringParser::new(" ").parse(&mut ps);