├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── Cargo.toml ├── LICENSE.txt ├── README.md ├── rustfmt.toml ├── src ├── combinators.rs └── lib.rs └── tests ├── calculator.rs └── ingredient_list.rs /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | name: Continuous integration 4 | 5 | jobs: 6 | primary: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | rust: 11 | - stable 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - uses: actions-rs/toolchain@v1 17 | with: 18 | profile: minimal 19 | toolchain: ${{ matrix.rust }} 20 | override: true 21 | components: rustfmt, clippy 22 | 23 | - uses: actions-rs/cargo@v1 24 | with: 25 | command: build 26 | 27 | - uses: actions-rs/cargo@v1 28 | with: 29 | command: test 30 | 31 | - uses: actions-rs/cargo@v1 32 | with: 33 | command: fmt 34 | args: --all -- --check 35 | 36 | - uses: actions-rs/cargo@v1 37 | with: 38 | command: clippy 39 | args: -- -D warnings 40 | 41 | secondary: 42 | runs-on: ubuntu-latest 43 | strategy: 44 | matrix: 45 | rust: 46 | - beta 47 | - nightly 48 | - 1.31.0 49 | 50 | steps: 51 | - uses: actions/checkout@v2 52 | 53 | - uses: actions-rs/toolchain@v1 54 | with: 55 | profile: minimal 56 | toolchain: ${{ matrix.rust }} 57 | override: true 58 | 59 | - uses: actions-rs/cargo@v1 60 | with: 61 | command: build 62 | 63 | - uses: actions-rs/cargo@v1 64 | with: 65 | command: test 66 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "peresil" 4 | version = "0.4.0-alpha.0" 5 | authors = ["Jake Goulding "] 6 | 7 | description = "A simple and simplistic string parsing library" 8 | readme = "README.md" 9 | keywords = ["parser"] 10 | 11 | homepage = "https://github.com/shepmaster/peresil" 12 | repository = "https://github.com/shepmaster/peresil" 13 | documentation = "https://shepmaster.github.io/peresil/" 14 | 15 | license = "MIT" 16 | 17 | [features] 18 | default = ["combinators"] 19 | combinators = [] 20 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jake Goulding 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Peresil 2 | 3 | [![crates.io][Crate Logo]][Crate] 4 | [![Documentation][Doc Logo]][Doc] 5 | [![Build Status][CI Logo]][CI] 6 | 7 | A simple and simplistic string parsing library in Rust. 8 | 9 | Check [the docs][Doc] for a quick introduction. 10 | 11 | ## Contributing 12 | 13 | 1. Fork it ( https://github.com/shepmaster/peresil/fork ) 14 | 2. Create your feature branch (`git checkout -b my-new-feature`) 15 | 3. Add a failing test. 16 | 4. Add code to pass the test. 17 | 5. Commit your changes (`git commit -am 'Add some feature'`) 18 | 6. Ensure tests pass. 19 | 7. Push to the branch (`git push origin my-new-feature`) 20 | 8. Create a new Pull Request 21 | 22 | [Crate]: https://crates.io/crates/peresil 23 | [Crate Logo]: https://img.shields.io/crates/v/peresil.svg 24 | 25 | [Doc]: https://docs.rs/peresil 26 | [Doc Logo]: https://docs.rs/peresil/badge.svg 27 | 28 | [CI]: https://github.com/shepmaster/peresil/actions?query=branch%3Amaster 29 | [CI Logo]: https://github.com/shepmaster/peresil/workflows/Continuous%20integration/badge.svg?branch=master 30 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | use_field_init_shorthand = true 2 | -------------------------------------------------------------------------------- /src/combinators.rs: -------------------------------------------------------------------------------- 1 | use super::{ParseMaster, Point, Progress, Recoverable, Status}; 2 | 3 | #[macro_export] 4 | macro_rules! sequence { 5 | ($pm:expr, $pt:expr, {let $x:pat = $parser:expr; $($rest:tt)*}, $creator:expr) => {{ 6 | let (pt, $x) = try_parse!($parser($pm, $pt)); 7 | sequence!($pm, pt, {$($rest)*}, $creator) 8 | }}; 9 | ($pm:expr, $pt:expr, {$x:pat = $parser:expr; $($rest:tt)*}, $creator:expr) => {{ 10 | let (pt, $x) = try_parse!($parser($pm, $pt)); 11 | sequence!($pm, pt, {$($rest)*}, $creator) 12 | }}; 13 | ($pm:expr, $pt:expr, {$parser:expr; $($rest:tt)*}, $creator:expr) => {{ 14 | let (pt, _) = try_parse!($parser($pm, $pt)); 15 | sequence!($pm, pt, {$($rest)*}, $creator) 16 | }}; 17 | ($pm:expr, $pt:expr, {}, $creator:expr) => { 18 | Progress::success($pt, $creator($pm, $pt)) 19 | }; 20 | } 21 | 22 | pub trait IntoAppend { 23 | fn into(self) -> Vec; 24 | } 25 | 26 | impl IntoAppend for Vec { 27 | fn into(self) -> Vec { 28 | self 29 | } 30 | } 31 | 32 | impl IntoAppend for Option { 33 | fn into(self) -> Vec { 34 | self.map(|v| vec![v]).unwrap_or_else(Vec::new) 35 | } 36 | } 37 | 38 | pub fn optional( 39 | parser: F, 40 | ) -> impl FnOnce(&mut ParseMaster, P) -> Progress, E> 41 | where 42 | F: FnOnce(&mut ParseMaster, P) -> Progress, 43 | P: Point, 44 | E: Recoverable, 45 | { 46 | move |pm, pt| pm.optional(pt, parser) 47 | } 48 | 49 | pub fn optional_append( 50 | append_to: A, 51 | parser: F, 52 | ) -> impl FnOnce(&mut ParseMaster, P) -> Progress, E> 53 | where 54 | F: FnOnce(&mut ParseMaster, P) -> Progress, 55 | A: IntoAppend, 56 | P: Point, 57 | //E: Recoverable, // TODO: use this 58 | { 59 | move |pm, pt| { 60 | let mut append_to = append_to.into(); 61 | match parser(pm, pt) { 62 | Progress { 63 | point, 64 | status: Status::Success(v), 65 | } => { 66 | append_to.push(v); 67 | Progress::success(point, append_to) 68 | } 69 | Progress { 70 | point, 71 | status: Status::Failure(..), 72 | } => Progress::success(point, append_to), 73 | } 74 | } 75 | } 76 | 77 | pub fn zero_or_more( 78 | parser: F, 79 | ) -> impl Fn(&mut ParseMaster, P) -> Progress, E> 80 | where 81 | F: Fn(&mut ParseMaster, P) -> Progress, 82 | P: Point, 83 | E: Recoverable, 84 | { 85 | move |pm, pt| pm.zero_or_more(pt, &parser) // what why ref? 86 | } 87 | 88 | pub fn zero_or_more_append( 89 | append_to: A, 90 | parser: F, 91 | ) -> impl FnOnce(&mut ParseMaster, P) -> Progress, E> 92 | where 93 | F: Fn(&mut ParseMaster, P) -> Progress, 94 | A: IntoAppend, 95 | P: Point, 96 | E: Recoverable, 97 | { 98 | move |pm, mut pt| { 99 | let mut append_to = append_to.into(); 100 | loop { 101 | match parser(pm, pt) { 102 | Progress { 103 | point, 104 | status: Status::Success(v), 105 | } => { 106 | append_to.push(v); 107 | pt = point; 108 | } 109 | Progress { .. } => return Progress::success(pt, append_to), 110 | } 111 | } 112 | } 113 | } 114 | 115 | pub fn one_or_more( 116 | parser: F, 117 | ) -> impl FnOnce(&mut ParseMaster, P) -> Progress, E> 118 | where 119 | F: Fn(&mut ParseMaster, P) -> Progress, 120 | P: Point, 121 | E: Recoverable, 122 | { 123 | move |pm, pt| { 124 | let (pt, head) = try_parse!(parser(pm, pt)); 125 | let append_to = vec![head]; 126 | let (pt, tail) = try_parse!(zero_or_more_append(append_to, parser)(pm, pt)); 127 | 128 | Progress::success(pt, tail) 129 | } 130 | } 131 | 132 | pub fn one_or_more_append( 133 | append_to: A, 134 | parser: F, 135 | ) -> impl FnOnce(&mut ParseMaster, P) -> Progress, E> 136 | where 137 | F: Fn(&mut ParseMaster, P) -> Progress, 138 | A: IntoAppend, 139 | P: Point, 140 | E: Recoverable, 141 | { 142 | move |pm, pt| { 143 | let mut append_to = append_to.into(); 144 | let (pt, head) = try_parse!(parser(pm, pt)); 145 | append_to.push(head); 146 | let (pt, tail) = try_parse!(zero_or_more_append(append_to, parser)(pm, pt)); 147 | 148 | Progress::success(pt, tail) 149 | } 150 | } 151 | 152 | pub fn map( 153 | parser: F, 154 | convert: C, 155 | ) -> impl FnOnce(&mut ParseMaster, P) -> Progress 156 | where 157 | F: FnOnce(&mut ParseMaster, P) -> Progress, 158 | C: FnOnce(T) -> U, 159 | P: Point, 160 | E: Recoverable, 161 | { 162 | move |pm, pt| parser(pm, pt).map(convert) 163 | } 164 | 165 | pub fn point(_: &mut ParseMaster, pt: P) -> Progress 166 | where 167 | P: Clone, 168 | { 169 | Progress::success(pt.clone(), pt) 170 | } 171 | 172 | pub fn inspect(f: F) -> impl Fn(&mut ParseMaster, P) -> Progress 173 | where 174 | F: Fn(&P), 175 | { 176 | move |_, pt| { 177 | f(&pt); 178 | Progress::success(pt, ()) 179 | } 180 | } 181 | 182 | pub fn state(f: F) -> impl Fn(&mut ParseMaster, P) -> Progress 183 | where 184 | F: Fn(&mut S), 185 | { 186 | move |pm, pt| { 187 | f(&mut pm.state); 188 | Progress::success(pt, ()) 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A simple and simplistic parsing library 2 | //! 3 | //! ### Example 4 | //! 5 | //! ``` 6 | //! #[macro_use] 7 | //! extern crate peresil; 8 | //! 9 | //! use peresil::{ParseMaster,Progress,Recoverable,Status,StringPoint}; 10 | //! 11 | //! type DemoMaster<'a> = ParseMaster, DemoError>; 12 | //! type DemoProgress<'a, T> = Progress, T, DemoError>; 13 | //! enum DemoError { 14 | //! ExpectedGreeting, 15 | //! ExpectedWhitespace, 16 | //! ExpectedObject, 17 | //! } 18 | //! 19 | //! impl Recoverable for DemoError { 20 | //! fn recoverable(&self) -> bool { true } 21 | //! } 22 | //! 23 | //! fn parse_basic<'a>(pm: &mut DemoMaster<'a>, pt: StringPoint<'a>) 24 | //! -> DemoProgress<'a, (&'a str, &'a str)> 25 | //! { 26 | //! let tmp = pm.alternate(pt) 27 | //! .one(|_, pt| pt.consume_literal("goodbye").map_err(|_| DemoError::ExpectedGreeting)) 28 | //! .one(|_, pt| pt.consume_literal("hello").map_err(|_| DemoError::ExpectedGreeting)) 29 | //! .finish(); 30 | //! let (pt, greeting) = try_parse!(tmp); 31 | //! 32 | //! let (pt, _) = try_parse!(pt.consume_literal(" ").map_err(|_| DemoError::ExpectedWhitespace)); 33 | //! 34 | //! let tmp = pm.alternate(pt) 35 | //! .one(|_, pt| pt.consume_literal("world").map_err(|_| DemoError::ExpectedObject)) 36 | //! .one(|_, pt| pt.consume_literal("moon").map_err(|_| DemoError::ExpectedObject)) 37 | //! .finish(); 38 | //! let (pt, object) = try_parse!(tmp); 39 | //! 40 | //! Progress::success(pt, (greeting, object)) 41 | //! } 42 | //! 43 | //! fn main() { 44 | //! let mut pm = ParseMaster::new(); 45 | //! let pt = StringPoint::new("hello world"); 46 | //! 47 | //! let result = parse_basic(&mut pm, pt); 48 | //! let (greeting, object) = match pm.finish(result) { 49 | //! Progress { status: Status::Success(v), .. } => v, 50 | //! Progress { status: Status::Failure(..), .. } => panic!("Did not parse"), 51 | //! }; 52 | //! 53 | //! println!("Parsed [{}], [{}]", greeting, object); 54 | //! } 55 | //! 56 | 57 | /// An analog to `try!`, but for `Progress` 58 | #[macro_export] 59 | macro_rules! try_parse( 60 | ($e:expr) => ({ 61 | match $e { 62 | $crate::Progress { status: $crate::Status::Success(val), point } => (point, val), 63 | $crate::Progress { status: $crate::Status::Failure(val), point } => { 64 | return $crate::Progress { point: point, status: $crate::Status::Failure(val.into()) } 65 | } 66 | } 67 | }); 68 | ); 69 | 70 | #[cfg(feature = "combinators")] 71 | pub mod combinators; 72 | 73 | /// A location in the parsed data 74 | pub trait Point: Ord + Copy { 75 | /// The initial point 76 | fn zero() -> Self; 77 | } 78 | 79 | impl Point for usize { 80 | fn zero() -> usize { 81 | 0 82 | } 83 | } 84 | impl Point for i32 { 85 | fn zero() -> i32 { 86 | 0 87 | } 88 | } 89 | 90 | /// Indicate if an error should terminate all parsing. 91 | /// 92 | /// Non-recoverable errors will not allow for alternatives to be 93 | /// tried, basically unwinding the parsing stack all the way back to 94 | /// the beginning. Unrecoverable errors are useful for errors that 95 | /// indicate that the content was well-formed but not semantically 96 | /// correct. 97 | pub trait Recoverable { 98 | fn recoverable(&self) -> bool; 99 | } 100 | 101 | #[derive(Debug, PartialEq)] 102 | struct Failures { 103 | point: P, 104 | kinds: Vec, 105 | } 106 | 107 | use std::cmp::Ordering; 108 | 109 | impl Failures 110 | where 111 | P: Point, 112 | { 113 | fn new() -> Failures { 114 | Failures { 115 | point: P::zero(), 116 | kinds: Vec::new(), 117 | } 118 | } 119 | 120 | fn add(&mut self, point: P, failure: E) { 121 | match point.cmp(&self.point) { 122 | Ordering::Less => { 123 | // Do nothing, our existing failures are better 124 | } 125 | Ordering::Greater => { 126 | // The new failure is better, toss existing failures 127 | self.replace(point, failure); 128 | } 129 | Ordering::Equal => { 130 | // Multiple failures at the same point, tell the user all 131 | // the ways they could do better. 132 | self.kinds.push(failure); 133 | } 134 | } 135 | } 136 | 137 | fn replace(&mut self, point: P, failure: E) { 138 | self.point = point; 139 | self.kinds.clear(); 140 | self.kinds.push(failure); 141 | } 142 | 143 | fn into_progress(self) -> Progress> { 144 | Progress { 145 | point: self.point, 146 | status: Status::Failure(self.kinds), 147 | } 148 | } 149 | } 150 | 151 | /// An analog to `Result`, specialized for parsing. 152 | #[derive(Debug, Copy, Clone, PartialEq)] 153 | pub enum Status { 154 | Success(T), 155 | Failure(E), 156 | } 157 | 158 | impl Status { 159 | fn map(self, f: F) -> Status 160 | where 161 | F: FnOnce(T) -> T2, 162 | { 163 | match self { 164 | Status::Success(x) => Status::Success(f(x)), 165 | Status::Failure(x) => Status::Failure(x), 166 | } 167 | } 168 | 169 | fn and_then(self, f: F) -> Status 170 | where 171 | F: FnOnce(T) -> Result, 172 | { 173 | match self { 174 | Status::Success(x) => match f(x) { 175 | Ok(v) => Status::Success(v), 176 | Err(e) => Status::Failure(e), 177 | }, 178 | Status::Failure(x) => Status::Failure(x), 179 | } 180 | } 181 | 182 | fn map_err(self, f: F) -> Status 183 | where 184 | F: FnOnce(E) -> E2, 185 | { 186 | match self { 187 | Status::Success(x) => Status::Success(x), 188 | Status::Failure(x) => Status::Failure(f(x)), 189 | } 190 | } 191 | } 192 | 193 | /// Tracks where the parser currently is and if it is successful. 194 | /// 195 | /// On success, some value has been parsed. On failure, nothing has 196 | /// been parsed and the value indicates the reason for the failure. 197 | /// The returned point indicates where to next start parsing, often 198 | /// unchanged on failure. 199 | #[must_use] 200 | #[derive(Debug, Copy, Clone, PartialEq)] 201 | pub struct Progress { 202 | /// The current location. 203 | pub point: P, 204 | /// If the point indicates the location of a successful or failed parse. 205 | pub status: Status, 206 | } 207 | 208 | impl Progress { 209 | pub fn success(point: P, val: T) -> Progress { 210 | Progress { 211 | point, 212 | status: Status::Success(val), 213 | } 214 | } 215 | 216 | pub fn failure(point: P, val: E) -> Progress { 217 | Progress { 218 | point, 219 | status: Status::Failure(val), 220 | } 221 | } 222 | 223 | /// Convert the success value, if there is one. 224 | pub fn map(self, f: F) -> Progress 225 | where 226 | F: FnOnce(T) -> T2, 227 | { 228 | Progress { 229 | point: self.point, 230 | status: self.status.map(f), 231 | } 232 | } 233 | 234 | /// Convert the success value, if there is one, potentially 235 | /// converting into a failure. 236 | pub fn and_then(self, restore_to: P, f: F) -> Progress 237 | where 238 | F: FnOnce(T) -> Result, 239 | { 240 | match self.status.and_then(f) { 241 | s @ Status::Success(..) => Progress { 242 | point: self.point, 243 | status: s, 244 | }, 245 | s @ Status::Failure(..) => Progress { 246 | point: restore_to, 247 | status: s, 248 | }, 249 | } 250 | } 251 | 252 | /// Convert the failure value, if there is one. 253 | pub fn map_err(self, f: F) -> Progress 254 | where 255 | F: FnOnce(E) -> E2, 256 | { 257 | Progress { 258 | point: self.point, 259 | status: self.status.map_err(f), 260 | } 261 | } 262 | 263 | /// Returns the value on success, or rewinds the point and returns 264 | /// `None` on failure. 265 | pub fn optional(self, reset_to: P) -> (P, Option) { 266 | // If we fail N optionals and then a required, it'd be nice to 267 | // report all the optional things. Might be difficult to do 268 | // that and return the optional value. 269 | match self { 270 | Progress { 271 | status: Status::Success(val), 272 | point, 273 | } => (point, Some(val)), 274 | Progress { 275 | status: Status::Failure(..), 276 | .. 277 | } => (reset_to, None), 278 | } 279 | } 280 | } 281 | 282 | /// The main entrypoint to parsing. 283 | /// 284 | /// This tracks the collection of outstanding errors and provides 285 | /// helper methods for parsing alternative paths and sequences of 286 | /// other parsers. 287 | #[derive(Debug, PartialEq)] 288 | pub struct ParseMaster { 289 | failures: Failures, 290 | pub state: S, 291 | } 292 | 293 | impl<'a, P, E> Default for ParseMaster 294 | where 295 | P: Point, 296 | E: Recoverable, 297 | { 298 | fn default() -> Self { 299 | Self::new() 300 | } 301 | } 302 | 303 | impl<'a, P, E> ParseMaster 304 | where 305 | P: Point, 306 | E: Recoverable, 307 | { 308 | pub fn new() -> ParseMaster { 309 | ParseMaster::with_state(()) 310 | } 311 | } 312 | 313 | impl<'a, P, E, S> ParseMaster 314 | where 315 | P: Point, 316 | E: Recoverable, 317 | { 318 | pub fn with_state(state: S) -> ParseMaster { 319 | ParseMaster { 320 | failures: Failures::new(), 321 | state, 322 | } 323 | } 324 | 325 | fn consume(&mut self, progress: Progress) -> Progress { 326 | match progress { 327 | Progress { 328 | status: Status::Success(..), 329 | .. 330 | } => progress.map_err(|_| ()), 331 | Progress { 332 | status: Status::Failure(f), 333 | point, 334 | } => { 335 | if f.recoverable() { 336 | self.failures.add(point, f); 337 | } else { 338 | self.failures.replace(point, f); 339 | } 340 | Progress { 341 | status: Status::Failure(()), 342 | point, 343 | } 344 | } 345 | } 346 | } 347 | 348 | /// Returns the value on success, or rewinds the point and returns 349 | /// `None` on a recoverable failure. Non-recoverable failures are 350 | /// propagated. 351 | pub fn optional(&mut self, point: P, parser: F) -> Progress, E> 352 | where 353 | F: FnOnce(&mut ParseMaster, P) -> Progress, 354 | { 355 | let orig_point = point; 356 | // If we fail N optionals and then a required, it'd be nice to 357 | // report all the optional things. Might be difficult to do 358 | // that and return the optional value. 359 | match parser(self, point) { 360 | Progress { 361 | status: Status::Success(val), 362 | point, 363 | } => Progress::success(point, Some(val)), 364 | Progress { 365 | status: Status::Failure(f), 366 | point, 367 | } => { 368 | if f.recoverable() { 369 | Progress::success(orig_point, None) 370 | } else { 371 | Progress::failure(point, f) 372 | } 373 | } 374 | } 375 | } 376 | 377 | /// Run sub-parsers in order until one succeeds. 378 | pub fn alternate(&mut self, pt: P) -> Alternate<'_, P, T, E, S> { 379 | Alternate { 380 | master: self, 381 | current: None, 382 | point: pt, 383 | } 384 | } 385 | 386 | /// Runs the parser until it fails. 387 | /// 388 | /// If the parser fails due to a recoverable error, a collection 389 | /// of values will be returned and the point will be at the end of 390 | /// the last successful parse. If the error is not recoverable, 391 | /// the error will be passed through directly. 392 | pub fn zero_or_more(&mut self, point: P, mut parser: F) -> Progress, E> 393 | where 394 | F: FnMut(&mut ParseMaster, P) -> Progress, 395 | { 396 | let mut current_point = point; 397 | let mut values = Vec::new(); 398 | 399 | loop { 400 | let progress = parser(self, current_point); 401 | match progress { 402 | Progress { 403 | status: Status::Success(v), 404 | point, 405 | } => { 406 | values.push(v); 407 | current_point = point; 408 | } 409 | Progress { 410 | status: Status::Failure(f), 411 | point, 412 | } => { 413 | if f.recoverable() { 414 | self.failures.add(point, f); 415 | break; 416 | } else { 417 | return Progress { 418 | status: Status::Failure(f), 419 | point, 420 | }; 421 | } 422 | } 423 | } 424 | } 425 | 426 | Progress { 427 | status: Status::Success(values), 428 | point: current_point, 429 | } 430 | } 431 | 432 | /// When parsing is complete, provide the final result and gain 433 | /// access to all failures. Will be recycled and may be used for 434 | /// further parsing. 435 | pub fn finish(&mut self, progress: Progress) -> Progress> { 436 | let progress = self.consume(progress); 437 | 438 | match progress { 439 | Progress { 440 | status: Status::Success(..), 441 | .. 442 | } => progress.map_err(|_| Vec::new()), 443 | Progress { 444 | status: Status::Failure(..), 445 | .. 446 | } => { 447 | use std::mem; 448 | let f = mem::replace(&mut self.failures, Failures::new()); 449 | f.into_progress() 450 | } 451 | } 452 | } 453 | } 454 | 455 | /// Follows the first successful parsing path. 456 | #[must_use] 457 | pub struct Alternate<'pm, P: 'pm, T, E: 'pm, S: 'pm> { 458 | master: &'pm mut ParseMaster, 459 | current: Option>, 460 | point: P, 461 | } 462 | 463 | impl<'pm, P, T, E, S> Alternate<'pm, P, T, E, S> 464 | where 465 | P: Point, 466 | E: Recoverable, 467 | { 468 | fn run_one(&mut self, parser: F) 469 | where 470 | F: FnOnce(&mut ParseMaster, P) -> Progress, 471 | { 472 | let r = parser(self.master, self.point); 473 | if let Some(prev) = self.current.take() { 474 | // We don't care about the previous error, once we've consumed it 475 | let _ = self.master.consume(prev); 476 | } 477 | self.current = Some(r); 478 | } 479 | 480 | /// Run one alternative parser. 481 | pub fn one(mut self, parser: F) -> Alternate<'pm, P, T, E, S> 482 | where 483 | F: FnOnce(&mut ParseMaster, P) -> Progress, 484 | { 485 | let recoverable = if let Some(Progress { 486 | status: Status::Failure(ref f), 487 | .. 488 | }) = self.current 489 | { 490 | f.recoverable() 491 | } else { 492 | false 493 | }; 494 | 495 | match self.current { 496 | None => self.run_one(parser), 497 | Some(Progress { 498 | status: Status::Success(..), 499 | .. 500 | }) => {} 501 | Some(Progress { 502 | status: Status::Failure(..), 503 | .. 504 | }) if recoverable => self.run_one(parser), 505 | Some(Progress { 506 | status: Status::Failure(..), 507 | .. 508 | }) => {} 509 | } 510 | 511 | self 512 | } 513 | 514 | /// Complete the alternatives, returning the first successful branch. 515 | pub fn finish(self) -> Progress { 516 | self.current.unwrap() 517 | } 518 | } 519 | 520 | /// Matches a literal string to a specific type, usually an enum. 521 | pub type Identifier<'a, T> = (&'a str, T); 522 | 523 | /// Tracks the location of parsing in a string, the most common case. 524 | /// 525 | /// Helper methods are provided to do basic parsing tasks, such as 526 | /// finding literal strings. 527 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 528 | pub struct StringPoint<'a> { 529 | /// The portion of the input string to start parsing next 530 | pub s: &'a str, 531 | /// How far into the original string we are 532 | pub offset: usize, 533 | } 534 | 535 | impl<'a> PartialOrd for StringPoint<'a> { 536 | #[inline] 537 | fn partial_cmp(&self, other: &StringPoint<'a>) -> Option { 538 | Some(self.cmp(&other)) 539 | } 540 | } 541 | 542 | impl<'a> Ord for StringPoint<'a> { 543 | #[inline] 544 | fn cmp(&self, other: &StringPoint<'a>) -> Ordering { 545 | self.offset.cmp(&other.offset) 546 | } 547 | } 548 | 549 | impl<'a> Point for StringPoint<'a> { 550 | fn zero() -> StringPoint<'a> { 551 | StringPoint { s: "", offset: 0 } 552 | } 553 | } 554 | 555 | impl<'a> StringPoint<'a> { 556 | #[inline] 557 | pub fn new(s: &'a str) -> StringPoint<'a> { 558 | StringPoint { s, offset: 0 } 559 | } 560 | 561 | #[inline] 562 | pub fn is_empty(self) -> bool { 563 | self.s.is_empty() 564 | } 565 | 566 | /// Slices the string. 567 | #[inline] 568 | pub fn to(self, other: StringPoint<'a>) -> &'a str { 569 | let len = other.offset - self.offset; 570 | &self.s[..len] 571 | } 572 | 573 | #[inline] 574 | pub fn success(self, len: usize) -> Progress, &'a str, E> { 575 | let matched = &self.s[..len]; 576 | let rest = &self.s[len..]; 577 | 578 | Progress { 579 | point: StringPoint { 580 | s: rest, 581 | offset: self.offset + len, 582 | }, 583 | status: Status::Success(matched), 584 | } 585 | } 586 | 587 | #[inline] 588 | fn fail(self) -> Progress, T, ()> { 589 | Progress { 590 | point: self, 591 | status: Status::Failure(()), 592 | } 593 | } 594 | 595 | /// Advances the point by the number of bytes. If the value is 596 | /// `None`, then no value was able to be consumed, and the result 597 | /// is a failure. 598 | #[inline] 599 | pub fn consume_to(&self, l: Option) -> Progress, &'a str, ()> { 600 | match l { 601 | None => self.fail(), 602 | Some(position) => self.success(position), 603 | } 604 | } 605 | 606 | /// Advances the point if it starts with the literal. 607 | #[inline] 608 | pub fn consume_literal(self, val: &str) -> Progress, &'a str, ()> { 609 | if self.s.starts_with(val) { 610 | self.success(val.len()) 611 | } else { 612 | self.fail() 613 | } 614 | } 615 | 616 | /// Iterates through the identifiers and advances the point on the 617 | /// first matching identifier. 618 | #[inline] 619 | pub fn consume_identifier( 620 | self, 621 | identifiers: &[Identifier], 622 | ) -> Progress, T, ()> 623 | where 624 | T: Clone, 625 | { 626 | for &(identifier, ref item) in identifiers { 627 | if self.s.starts_with(identifier) { 628 | return self 629 | .consume_to(Some(identifier.len())) 630 | .map(|_| item.clone()) 631 | .map_err(|_| unreachable!()); 632 | } 633 | } 634 | 635 | self.fail() 636 | } 637 | } 638 | 639 | #[derive(Debug)] 640 | pub struct SlicePoint<'s, T: 's> { 641 | pub offset: usize, 642 | pub s: &'s [T], 643 | } 644 | 645 | impl<'s, T: 's> SlicePoint<'s, T> { 646 | pub fn new(slice: &'s [T]) -> Self { 647 | SlicePoint { 648 | offset: 0, 649 | s: slice, 650 | } 651 | } 652 | 653 | pub fn advance_by(&self, offset: usize) -> Self { 654 | SlicePoint { 655 | s: &self.s[offset..], 656 | offset: self.offset + offset, 657 | } 658 | } 659 | } 660 | 661 | impl<'s, T> Point for SlicePoint<'s, T> { 662 | fn zero() -> Self { 663 | SlicePoint { offset: 0, s: &[] } 664 | } 665 | } 666 | 667 | impl<'s, T> Copy for SlicePoint<'s, T> {} 668 | impl<'s, T> Clone for SlicePoint<'s, T> { 669 | fn clone(&self) -> Self { 670 | *self 671 | } 672 | } 673 | 674 | impl<'s, T> PartialOrd for SlicePoint<'s, T> { 675 | fn partial_cmp(&self, other: &Self) -> Option { 676 | Some(self.cmp(other)) 677 | } 678 | } 679 | 680 | impl<'s, T> Ord for SlicePoint<'s, T> { 681 | fn cmp(&self, other: &Self) -> std::cmp::Ordering { 682 | self.offset.cmp(&other.offset) 683 | } 684 | } 685 | 686 | impl<'s, T> PartialEq for SlicePoint<'s, T> { 687 | fn eq(&self, other: &Self) -> bool { 688 | self.offset.eq(&other.offset) 689 | } 690 | } 691 | 692 | impl<'s, T> Eq for SlicePoint<'s, T> {} 693 | 694 | #[cfg(test)] 695 | mod test { 696 | use super::{ParseMaster, Progress, Recoverable, Status, StringPoint}; 697 | 698 | #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] 699 | struct AnError(u8); 700 | 701 | impl Recoverable for AnError { 702 | fn recoverable(&self) -> bool { 703 | self.0 < 0x80 704 | } 705 | } 706 | 707 | type SimpleMaster = ParseMaster; 708 | type SimpleProgress = Progress; 709 | 710 | #[test] 711 | fn one_error() { 712 | let mut d = ParseMaster::new(); 713 | 714 | let r = d.finish::<()>(Progress { 715 | point: 0, 716 | status: Status::Failure(AnError(1)), 717 | }); 718 | 719 | assert_eq!( 720 | r, 721 | Progress { 722 | point: 0, 723 | status: Status::Failure(vec![AnError(1)]) 724 | } 725 | ); 726 | } 727 | 728 | #[test] 729 | fn two_error_at_same_point() { 730 | let mut d = ParseMaster::new(); 731 | 732 | let r = d 733 | .alternate::<()>(0) 734 | .one(|_, _| Progress { 735 | point: 0, 736 | status: Status::Failure(AnError(1)), 737 | }) 738 | .one(|_, _| Progress { 739 | point: 0, 740 | status: Status::Failure(AnError(2)), 741 | }) 742 | .finish(); 743 | 744 | let r = d.finish(r); 745 | 746 | assert_eq!( 747 | r, 748 | Progress { 749 | point: 0, 750 | status: Status::Failure(vec![AnError(1), AnError(2)]) 751 | } 752 | ); 753 | } 754 | 755 | #[test] 756 | fn first_error_is_better() { 757 | let mut d = ParseMaster::new(); 758 | 759 | let r = d 760 | .alternate::<()>(0) 761 | .one(|_, _| Progress { 762 | point: 1, 763 | status: Status::Failure(AnError(1)), 764 | }) 765 | .one(|_, _| Progress { 766 | point: 0, 767 | status: Status::Failure(AnError(2)), 768 | }) 769 | .finish(); 770 | 771 | let r = d.finish(r); 772 | 773 | assert_eq!( 774 | r, 775 | Progress { 776 | point: 1, 777 | status: Status::Failure(vec![AnError(1)]) 778 | } 779 | ); 780 | } 781 | 782 | #[test] 783 | fn second_error_is_better() { 784 | let mut d = ParseMaster::new(); 785 | 786 | let r = d 787 | .alternate::<()>(0) 788 | .one(|_, _| Progress { 789 | point: 0, 790 | status: Status::Failure(AnError(1)), 791 | }) 792 | .one(|_, _| Progress { 793 | point: 1, 794 | status: Status::Failure(AnError(2)), 795 | }) 796 | .finish(); 797 | 798 | let r = d.finish(r); 799 | 800 | assert_eq!( 801 | r, 802 | Progress { 803 | point: 1, 804 | status: Status::Failure(vec![AnError(2)]) 805 | } 806 | ); 807 | } 808 | 809 | #[test] 810 | fn one_success() { 811 | let mut d = ParseMaster::<_, AnError>::new(); 812 | 813 | let r = d.finish(Progress { 814 | point: 0, 815 | status: Status::Success(42), 816 | }); 817 | 818 | assert_eq!( 819 | r, 820 | Progress { 821 | point: 0, 822 | status: Status::Success(42) 823 | } 824 | ); 825 | } 826 | 827 | #[test] 828 | fn success_after_failure() { 829 | let mut d = ParseMaster::new(); 830 | 831 | let r = d 832 | .alternate(0) 833 | .one(|_, _| Progress { 834 | point: 0, 835 | status: Status::Failure(AnError(1)), 836 | }) 837 | .one(|_, _| Progress { 838 | point: 0, 839 | status: Status::Success(42), 840 | }) 841 | .finish(); 842 | 843 | let r = d.finish(r); 844 | assert_eq!( 845 | r, 846 | Progress { 847 | point: 0, 848 | status: Status::Success(42) 849 | } 850 | ); 851 | } 852 | 853 | #[test] 854 | fn success_before_failure() { 855 | let mut d = ParseMaster::<_, AnError>::new(); 856 | 857 | let r = d 858 | .alternate(0) 859 | .one(|_, _| Progress { 860 | point: 0, 861 | status: Status::Success(42), 862 | }) 863 | .one(|_, _| panic!("Should not even be called")) 864 | .finish(); 865 | 866 | let r = d.finish(r); 867 | assert_eq!( 868 | r, 869 | Progress { 870 | point: 0, 871 | status: Status::Success(42) 872 | } 873 | ); 874 | } 875 | 876 | #[test] 877 | fn sequential_success() { 878 | fn first(_: &mut SimpleMaster, pt: usize) -> SimpleProgress { 879 | Progress { 880 | point: pt + 1, 881 | status: Status::Success(1), 882 | } 883 | } 884 | 885 | fn second(_: &mut SimpleMaster, pt: usize) -> SimpleProgress { 886 | Progress { 887 | point: pt + 1, 888 | status: Status::Success(2), 889 | } 890 | } 891 | 892 | fn both(d: &mut SimpleMaster, pt: usize) -> SimpleProgress<(u8, u8)> { 893 | let (pt, val1) = try_parse!(first(d, pt)); 894 | let (pt, val2) = try_parse!(second(d, pt)); 895 | Progress { 896 | point: pt, 897 | status: Status::Success((val1, val2)), 898 | } 899 | } 900 | 901 | let mut d = ParseMaster::new(); 902 | let r = both(&mut d, 0); 903 | let r = d.finish(r); 904 | 905 | assert_eq!( 906 | r, 907 | Progress { 908 | point: 2, 909 | status: Status::Success((1, 2)) 910 | } 911 | ); 912 | } 913 | 914 | #[test] 915 | fn child_parse_succeeds() { 916 | fn parent(d: &mut SimpleMaster, pt: usize) -> SimpleProgress<(u8, u8)> { 917 | let (pt, val1) = try_parse!(child(d, pt)); 918 | Progress { 919 | point: pt + 1, 920 | status: Status::Success((val1, 2)), 921 | } 922 | } 923 | 924 | fn child(_: &mut SimpleMaster, pt: usize) -> SimpleProgress { 925 | Progress { 926 | point: pt + 1, 927 | status: Status::Success(1), 928 | } 929 | } 930 | 931 | let mut d = ParseMaster::new(); 932 | let r = parent(&mut d, 0); 933 | let r = d.finish(r); 934 | 935 | assert_eq!( 936 | r, 937 | Progress { 938 | point: 2, 939 | status: Status::Success((1, 2)) 940 | } 941 | ); 942 | } 943 | 944 | #[test] 945 | fn child_parse_fails_child_step() { 946 | fn parent(d: &mut SimpleMaster, pt: usize) -> SimpleProgress<(u8, u8)> { 947 | let (pt, val1) = try_parse!(child(d, pt)); 948 | Progress { 949 | point: pt + 1, 950 | status: Status::Success((val1, 2)), 951 | } 952 | } 953 | 954 | fn child(_: &mut SimpleMaster, pt: usize) -> SimpleProgress { 955 | Progress { 956 | point: pt + 1, 957 | status: Status::Failure(AnError(1)), 958 | } 959 | } 960 | 961 | let mut d = ParseMaster::new(); 962 | let r = parent(&mut d, 0); 963 | let r = d.finish(r); 964 | 965 | assert_eq!( 966 | r, 967 | Progress { 968 | point: 1, 969 | status: Status::Failure(vec![AnError(1)]) 970 | } 971 | ); 972 | } 973 | 974 | #[test] 975 | fn child_parse_fails_parent_step() { 976 | fn parent(d: &mut SimpleMaster, pt: usize) -> SimpleProgress<(u8, u8)> { 977 | let (pt, _) = try_parse!(child(d, pt)); 978 | Progress { 979 | point: pt + 1, 980 | status: Status::Failure(AnError(2)), 981 | } 982 | } 983 | 984 | fn child(_: &mut SimpleMaster, pt: usize) -> SimpleProgress { 985 | Progress { 986 | point: pt + 1, 987 | status: Status::Success(1), 988 | } 989 | } 990 | 991 | let mut d = ParseMaster::new(); 992 | let r = parent(&mut d, 0); 993 | let r = d.finish(r); 994 | 995 | assert_eq!( 996 | r, 997 | Progress { 998 | point: 2, 999 | status: Status::Failure(vec![AnError(2)]) 1000 | } 1001 | ); 1002 | } 1003 | 1004 | #[test] 1005 | fn alternate_with_children_parses() { 1006 | fn first(_: &mut SimpleMaster, pt: usize) -> SimpleProgress { 1007 | Progress { 1008 | point: pt + 1, 1009 | status: Status::Failure(AnError(1)), 1010 | } 1011 | } 1012 | 1013 | fn second(_: &mut SimpleMaster, pt: usize) -> SimpleProgress { 1014 | Progress { 1015 | point: pt + 1, 1016 | status: Status::Success(1), 1017 | } 1018 | } 1019 | 1020 | fn both(d: &mut SimpleMaster, pt: usize) -> Progress { 1021 | d.alternate(pt).one(first).one(second).finish() 1022 | } 1023 | 1024 | let mut d = ParseMaster::new(); 1025 | let r = both(&mut d, 0); 1026 | let r = d.finish(r); 1027 | 1028 | assert_eq!( 1029 | r, 1030 | Progress { 1031 | point: 1, 1032 | status: Status::Success(1) 1033 | } 1034 | ); 1035 | } 1036 | 1037 | #[test] 1038 | fn alternate_stops_parsing_after_unrecoverable_failure() { 1039 | let mut d = ParseMaster::new(); 1040 | let r = d 1041 | .alternate(0) 1042 | .one(|_, _| Progress { 1043 | point: 0, 1044 | status: Status::Failure(AnError(255)), 1045 | }) 1046 | .one(|_, _| Progress { 1047 | point: 0, 1048 | status: Status::Success(()), 1049 | }) 1050 | .finish(); 1051 | let r = d.finish(r); 1052 | 1053 | assert_eq!( 1054 | r, 1055 | Progress { 1056 | point: 0, 1057 | status: Status::Failure(vec![AnError(255)]) 1058 | } 1059 | ); 1060 | } 1061 | 1062 | #[test] 1063 | fn optional_present() { 1064 | fn optional(_: &mut SimpleMaster, pt: usize) -> SimpleProgress { 1065 | Progress { 1066 | point: pt + 1, 1067 | status: Status::Success(1), 1068 | } 1069 | } 1070 | 1071 | let mut d = ParseMaster::new(); 1072 | let (pt, val) = optional(&mut d, 0).optional(0); 1073 | 1074 | assert_eq!(pt, 1); 1075 | assert_eq!(val, Some(1)); 1076 | } 1077 | 1078 | #[test] 1079 | fn optional_missing() { 1080 | fn optional(_: &mut SimpleMaster, pt: usize) -> SimpleProgress { 1081 | Progress { 1082 | point: pt + 1, 1083 | status: Status::Failure(AnError(1)), 1084 | } 1085 | } 1086 | 1087 | let mut d = ParseMaster::new(); 1088 | let (pt, val) = optional(&mut d, 0).optional(0); 1089 | 1090 | assert_eq!(pt, 0); 1091 | assert_eq!(val, None); 1092 | } 1093 | 1094 | #[test] 1095 | fn optional_with_recoverable() { 1096 | fn optional(_: &mut SimpleMaster, pt: usize) -> SimpleProgress { 1097 | Progress { 1098 | point: pt + 1, 1099 | status: Status::Failure(AnError(1)), 1100 | } 1101 | } 1102 | 1103 | let mut d = ParseMaster::new(); 1104 | let r = d.optional(0, |pm, pt| optional(pm, pt)); 1105 | let r = d.finish(r); 1106 | 1107 | assert_eq!( 1108 | r, 1109 | Progress { 1110 | point: 0, 1111 | status: Status::Success(None) 1112 | } 1113 | ); 1114 | } 1115 | 1116 | #[test] 1117 | fn optional_with_unrecoverable() { 1118 | fn optional(_: &mut SimpleMaster, pt: usize) -> SimpleProgress { 1119 | Progress { 1120 | point: pt + 1, 1121 | status: Status::Failure(AnError(255)), 1122 | } 1123 | } 1124 | 1125 | let mut d = ParseMaster::new(); 1126 | let r = d.optional(0, |pm, pt| optional(pm, pt)); 1127 | let r = d.finish(r); 1128 | 1129 | assert_eq!( 1130 | r, 1131 | Progress { 1132 | point: 1, 1133 | status: Status::Failure(vec![AnError(255)]) 1134 | } 1135 | ); 1136 | } 1137 | 1138 | #[test] 1139 | fn zero_or_more() { 1140 | let mut remaining: u8 = 2; 1141 | 1142 | let mut body = |_: &mut SimpleMaster, pt: usize| -> SimpleProgress { 1143 | if remaining > 0 { 1144 | remaining -= 1; 1145 | Progress { 1146 | point: pt + 1, 1147 | status: Status::Success(remaining), 1148 | } 1149 | } else { 1150 | Progress { 1151 | point: pt + 1, 1152 | status: Status::Failure(AnError(1)), 1153 | } 1154 | } 1155 | }; 1156 | 1157 | let mut d = ParseMaster::new(); 1158 | let r = d.zero_or_more(0, |d, pt| body(d, pt)); 1159 | let r = d.finish(r); 1160 | 1161 | assert_eq!( 1162 | r, 1163 | Progress { 1164 | point: 2, 1165 | status: Status::Success(vec![1, 0]) 1166 | } 1167 | ); 1168 | } 1169 | 1170 | #[test] 1171 | fn zero_or_more_failure_returns_to_beginning_of_line() { 1172 | fn body(_: &mut SimpleMaster, pt: usize) -> SimpleProgress { 1173 | Progress { 1174 | point: pt + 1, 1175 | status: Status::Failure(AnError(1)), 1176 | } 1177 | } 1178 | 1179 | let mut d = ParseMaster::new(); 1180 | let r = d.zero_or_more(0, |d, pt| body(d, pt)); 1181 | let r = d.finish(r); 1182 | 1183 | assert_eq!( 1184 | r, 1185 | Progress { 1186 | point: 0, 1187 | status: Status::Success(vec![]) 1188 | } 1189 | ); 1190 | } 1191 | 1192 | #[test] 1193 | fn zero_or_more_fails_on_unrecoverable_failure() { 1194 | fn body(_: &mut SimpleMaster, pt: usize) -> SimpleProgress { 1195 | Progress { 1196 | point: pt, 1197 | status: Status::Failure(AnError(255)), 1198 | } 1199 | } 1200 | 1201 | let mut d = ParseMaster::new(); 1202 | let r = d.zero_or_more(0, |d, pt| body(d, pt)); 1203 | let r = d.finish(r); 1204 | 1205 | assert_eq!( 1206 | r, 1207 | Progress { 1208 | point: 0, 1209 | status: Status::Failure(vec![AnError(255)]) 1210 | } 1211 | ); 1212 | } 1213 | 1214 | type StringMaster<'a> = ParseMaster, AnError>; 1215 | type StringProgress<'a, T> = Progress, T, AnError>; 1216 | 1217 | #[test] 1218 | fn string_sequential() { 1219 | fn all<'a>(pt: StringPoint<'a>) -> StringProgress<'a, (&'a str, &'a str, &'a str)> { 1220 | let (pt, a) = try_parse!(pt.consume_literal("a").map_err(|_| AnError(1))); 1221 | let (pt, b) = try_parse!(pt.consume_literal("b").map_err(|_| AnError(2))); 1222 | let (pt, c) = try_parse!(pt.consume_literal("c").map_err(|_| AnError(3))); 1223 | 1224 | Progress { 1225 | point: pt, 1226 | status: Status::Success((a, b, c)), 1227 | } 1228 | } 1229 | 1230 | let mut d = ParseMaster::new(); 1231 | let pt = StringPoint::new("abc"); 1232 | 1233 | let r = all(pt); 1234 | let r = d.finish(r); 1235 | 1236 | assert_eq!( 1237 | r, 1238 | Progress { 1239 | point: StringPoint { s: "", offset: 3 }, 1240 | status: Status::Success(("a", "b", "c")) 1241 | } 1242 | ); 1243 | } 1244 | 1245 | #[test] 1246 | fn string_alternate() { 1247 | fn any<'a>(d: &mut StringMaster<'a>, pt: StringPoint<'a>) -> StringProgress<'a, &'a str> { 1248 | d.alternate(pt) 1249 | .one(|_, pt| pt.consume_literal("a").map_err(|_| AnError(1))) 1250 | .one(|_, pt| pt.consume_literal("b").map_err(|_| AnError(2))) 1251 | .one(|_, pt| pt.consume_literal("c").map_err(|_| AnError(3))) 1252 | .finish() 1253 | } 1254 | 1255 | let mut d = ParseMaster::new(); 1256 | let pt = StringPoint::new("c"); 1257 | 1258 | let r = any(&mut d, pt); 1259 | let r = d.finish(r); 1260 | 1261 | assert_eq!( 1262 | r, 1263 | Progress { 1264 | point: StringPoint { s: "", offset: 1 }, 1265 | status: Status::Success("c") 1266 | } 1267 | ); 1268 | } 1269 | 1270 | #[test] 1271 | fn string_zero_or_more() { 1272 | fn any<'a>( 1273 | d: &mut StringMaster<'a>, 1274 | pt: StringPoint<'a>, 1275 | ) -> StringProgress<'a, Vec<&'a str>> { 1276 | d.zero_or_more(pt, |_, pt| pt.consume_literal("a").map_err(|_| AnError(1))) 1277 | } 1278 | 1279 | let mut d = ParseMaster::new(); 1280 | let pt = StringPoint::new("aaa"); 1281 | 1282 | let r = any(&mut d, pt); 1283 | let r = d.finish(r); 1284 | 1285 | assert_eq!( 1286 | r, 1287 | Progress { 1288 | point: StringPoint { s: "", offset: 3 }, 1289 | status: Status::Success(vec!["a", "a", "a"]) 1290 | } 1291 | ); 1292 | } 1293 | 1294 | #[test] 1295 | fn string_to() { 1296 | let pt1 = StringPoint::new("hello world"); 1297 | let pt2 = StringPoint { 1298 | offset: pt1.offset + 5, 1299 | s: &pt1.s[5..], 1300 | }; 1301 | assert_eq!("hello", pt1.to(pt2)); 1302 | } 1303 | 1304 | #[test] 1305 | fn string_consume_literal() { 1306 | let pt = StringPoint::new("hello world"); 1307 | 1308 | let r = pt.consume_literal("hello"); 1309 | assert_eq!( 1310 | r, 1311 | Progress { 1312 | point: StringPoint { 1313 | s: " world", 1314 | offset: 5 1315 | }, 1316 | status: Status::Success("hello") 1317 | } 1318 | ); 1319 | 1320 | let r = pt.consume_literal("goodbye"); 1321 | assert_eq!( 1322 | r, 1323 | Progress { 1324 | point: StringPoint { 1325 | s: "hello world", 1326 | offset: 0 1327 | }, 1328 | status: Status::Failure(()) 1329 | } 1330 | ); 1331 | } 1332 | 1333 | #[test] 1334 | fn string_consume_identifier() { 1335 | let pt = StringPoint::new("hello world"); 1336 | 1337 | let r = pt.consume_identifier(&[("goodbye", 1), ("hello", 2)]); 1338 | assert_eq!( 1339 | r, 1340 | Progress { 1341 | point: StringPoint { 1342 | s: " world", 1343 | offset: 5 1344 | }, 1345 | status: Status::Success(2) 1346 | } 1347 | ); 1348 | 1349 | let r = pt.consume_identifier(&[("red", 3), ("blue", 4)]); 1350 | assert_eq!( 1351 | r, 1352 | Progress { 1353 | point: StringPoint { 1354 | s: "hello world", 1355 | offset: 0 1356 | }, 1357 | status: Status::Failure(()) 1358 | } 1359 | ); 1360 | } 1361 | } 1362 | -------------------------------------------------------------------------------- /tests/calculator.rs: -------------------------------------------------------------------------------- 1 | //! # Simple 4-function calculator 2 | //! 3 | //! This demonstrates usage of Peresil, and also shows how you can 4 | //! write a recursive-descent parser that handles left-associative 5 | //! operators. 6 | //! 7 | //! For an extra wrinkle, input numbers must be integers in the range 8 | //! [0, 31]. This allows an opportunity to show how errors outside the 9 | //! grammar can be generated at parsing time. 10 | //! 11 | //! ## Grammar 12 | //! 13 | //! Expr := Add 14 | //! Add := Add '+' Mul 15 | //! := Add '-' Mul 16 | //! := Mul 17 | //! Mul := Mul '*' Num 18 | //! := Mul '/' Num 19 | //! := Num 20 | //! Num := [0-9]+ 21 | 22 | #[macro_use] 23 | extern crate peresil; 24 | 25 | use peresil::{ParseMaster, Recoverable, StringPoint}; 26 | 27 | // It's recommended to make type aliases to clean up signatures 28 | type CalcMaster<'a> = ParseMaster, Error>; 29 | type CalcProgress<'a, T> = peresil::Progress, T, Error>; 30 | 31 | #[derive(Debug, Clone, PartialEq)] 32 | enum Expression { 33 | Add(Box, Box), 34 | Sub(Box, Box), 35 | Mul(Box, Box), 36 | Div(Box, Box), 37 | Num(u8), 38 | } 39 | 40 | impl Expression { 41 | fn evaluate(&self) -> i32 { 42 | use Expression::*; 43 | 44 | match *self { 45 | Add(ref l, ref r) => l.evaluate() + r.evaluate(), 46 | Sub(ref l, ref r) => l.evaluate() - r.evaluate(), 47 | Mul(ref l, ref r) => l.evaluate() * r.evaluate(), 48 | Div(ref l, ref r) => l.evaluate() / r.evaluate(), 49 | Num(v) => v as i32, 50 | } 51 | } 52 | } 53 | 54 | #[derive(Debug, Copy, Clone, PartialEq)] 55 | enum Error { 56 | ExpectedNumber, 57 | InvalidNumber(u8), 58 | } 59 | 60 | impl Recoverable for Error { 61 | fn recoverable(&self) -> bool { 62 | use Error::*; 63 | 64 | match *self { 65 | ExpectedNumber => true, 66 | InvalidNumber(..) => false, 67 | } 68 | } 69 | } 70 | 71 | /// Maps an operator to a function that builds the corresponding Expression 72 | type LeftAssociativeRule<'a> = ( 73 | &'static str, 74 | &'a dyn Fn(Expression, Expression) -> Expression, 75 | ); 76 | 77 | /// Iteratively parses left-associative operators, avoiding infinite 78 | /// recursion. Provide a `child_parser` that corresponds to each side 79 | /// of the operator, as well as a rule for each operator. 80 | fn parse_left_associative_operator<'a, P>( 81 | pm: &mut CalcMaster<'a>, 82 | pt: StringPoint<'a>, 83 | child_parser: P, 84 | rules: &[LeftAssociativeRule], 85 | ) -> CalcProgress<'a, Expression> 86 | where 87 | P: for<'b> Fn(&mut CalcMaster<'b>, StringPoint<'b>) -> CalcProgress<'b, Expression>, 88 | { 89 | let (pt, mut a) = try_parse!(child_parser(pm, pt)); 90 | let mut start = pt; 91 | 92 | loop { 93 | let mut matched = false; 94 | 95 | for &(ref operator, ref builder) in rules { 96 | let (pt, op) = start.consume_literal(operator).optional(start); 97 | if op.is_none() { 98 | continue; 99 | } 100 | 101 | let (pt, b) = try_parse!(child_parser(pm, pt)); 102 | 103 | a = builder(a, b); 104 | start = pt; 105 | 106 | matched = true; 107 | break; 108 | } 109 | 110 | if !matched { 111 | break; 112 | } 113 | } 114 | 115 | peresil::Progress::success(pt, a) 116 | } 117 | 118 | /// Parse a sequence of one-or-more ASCII digits 119 | fn parse_num<'a>(_: &mut CalcMaster<'a>, pt: StringPoint<'a>) -> CalcProgress<'a, Expression> { 120 | let original_pt = pt; 121 | 122 | // We can cheat and know that ASCII 0-9 only takes one byte each 123 | let digits = pt.s.chars().take_while(|&c| c >= '0' && c <= '9').count(); 124 | let r = if digits == 0 { 125 | pt.consume_to(None) 126 | } else { 127 | pt.consume_to(Some(digits)) 128 | }; 129 | 130 | let (pt, v) = try_parse!(r.map_err(|_| Error::ExpectedNumber)); 131 | 132 | let num = v.parse().unwrap(); 133 | 134 | // Here's where we can raise our own parsing errors. Note that we 135 | // kept the point where the number started, in order to give an 136 | // accurate error position. 137 | if num > 31 { 138 | peresil::Progress::failure(original_pt, Error::InvalidNumber(num)) 139 | } else { 140 | peresil::Progress::success(pt, Expression::Num(num)) 141 | } 142 | } 143 | 144 | fn parse_muldiv<'a>(pm: &mut CalcMaster<'a>, pt: StringPoint<'a>) -> CalcProgress<'a, Expression> { 145 | parse_left_associative_operator( 146 | pm, 147 | pt, 148 | parse_num, 149 | &[ 150 | ("*", &|a, b| Expression::Mul(Box::new(a), Box::new(b))), 151 | ("/", &|a, b| Expression::Div(Box::new(a), Box::new(b))), 152 | ], 153 | ) 154 | } 155 | 156 | fn parse_addsub<'a>(pm: &mut CalcMaster<'a>, pt: StringPoint<'a>) -> CalcProgress<'a, Expression> { 157 | parse_left_associative_operator( 158 | pm, 159 | pt, 160 | parse_muldiv, 161 | &[ 162 | ("+", &|a, b| Expression::Add(Box::new(a), Box::new(b))), 163 | ("-", &|a, b| Expression::Sub(Box::new(a), Box::new(b))), 164 | ], 165 | ) 166 | } 167 | 168 | fn parse(s: &str) -> Result)> { 169 | let mut pm = ParseMaster::new(); 170 | let pt = StringPoint::new(s); 171 | 172 | let result = parse_addsub(&mut pm, pt); 173 | match pm.finish(result) { 174 | peresil::Progress { 175 | status: peresil::Status::Success(v), 176 | .. 177 | } => Ok(v), 178 | peresil::Progress { 179 | status: peresil::Status::Failure(f), 180 | point, 181 | } => Err((point.offset, f)), 182 | } 183 | } 184 | 185 | fn n(n: u8) -> Box { 186 | Box::new(Expression::Num(n)) 187 | } 188 | 189 | #[test] 190 | fn single_number() { 191 | use Expression::*; 192 | assert_eq!(parse("1"), Ok(Num(1))); 193 | } 194 | 195 | #[test] 196 | fn add_two_numbers() { 197 | use Expression::*; 198 | assert_eq!(parse("1+2"), Ok(Add(n(1), n(2)))); 199 | } 200 | 201 | #[test] 202 | fn add_three_numbers() { 203 | use Expression::*; 204 | assert_eq!(parse("3+4+5"), Ok(Add(Box::new(Add(n(3), n(4))), n(5)))); 205 | } 206 | 207 | #[test] 208 | fn subtract_two_numbers() { 209 | use Expression::*; 210 | assert_eq!(parse("9-8"), Ok(Sub(n(9), n(8)))); 211 | } 212 | 213 | #[test] 214 | fn multiply_two_numbers() { 215 | use Expression::*; 216 | assert_eq!(parse("5*6"), Ok(Mul(n(5), n(6)))); 217 | } 218 | 219 | #[test] 220 | fn multiply_three_numbers() { 221 | use Expression::*; 222 | assert_eq!(parse("3*6*9"), Ok(Mul(Box::new(Mul(n(3), n(6))), n(9)))); 223 | } 224 | 225 | #[test] 226 | fn divide_two_numbers() { 227 | use Expression::*; 228 | assert_eq!(parse("9/3"), Ok(Div(n(9), n(3)))); 229 | } 230 | 231 | #[test] 232 | fn addition_adds() { 233 | assert_eq!(parse("1+2+3").unwrap().evaluate(), 6); 234 | } 235 | 236 | #[test] 237 | fn subtraction_subtracts() { 238 | assert_eq!(parse("1-2-3").unwrap().evaluate(), -4); 239 | } 240 | 241 | #[test] 242 | fn multiplication_multiplies() { 243 | assert_eq!(parse("2*3*4").unwrap().evaluate(), 24); 244 | } 245 | 246 | #[test] 247 | fn division_divides() { 248 | assert_eq!(parse("9/3/3").unwrap().evaluate(), 1); 249 | } 250 | 251 | #[test] 252 | fn all_operators_together() { 253 | assert_eq!(parse("3+2-2*9/3").unwrap().evaluate(), -1); 254 | } 255 | 256 | #[test] 257 | fn failure_not_a_number() { 258 | assert_eq!(parse("cow"), Err((0, vec![Error::ExpectedNumber]))); 259 | } 260 | 261 | #[test] 262 | fn failure_invalid_number() { 263 | assert_eq!(parse("32"), Err((0, vec![Error::InvalidNumber(32)]))); 264 | } 265 | 266 | #[test] 267 | fn failure_invalid_number_in_other_position() { 268 | assert_eq!(parse("1+99"), Err((2, vec![Error::InvalidNumber(99)]))); 269 | } 270 | -------------------------------------------------------------------------------- /tests/ingredient_list.rs: -------------------------------------------------------------------------------- 1 | //! # Ingredient list parser 2 | //! 3 | //! ## Example 4 | //! 5 | //! 2 cups fresh snow peas 6 | //! 1 oz water 7 | //! 6 tbsp baking soda 8 | //! 9 | //! ## Grammar 10 | //! 11 | //! Ingredients := Ingredient* 12 | //! Ingredient := Amount Name "\n" 13 | //! Amount := Number Unit 14 | //! Number := [0-9]+ 15 | //! Unit := cups | cup | c 16 | //! := ounces | ounce | oz 17 | //! := tablespoons | tablespoon | tbsp 18 | //! Name := [^\n]* 19 | 20 | #[macro_use] 21 | extern crate peresil; 22 | 23 | use peresil::{ParseMaster, Recoverable, StringPoint}; 24 | use std::borrow::ToOwned; 25 | 26 | #[derive(Debug, Copy, Clone, PartialEq)] 27 | enum Unit { 28 | Cup, 29 | Ounce, 30 | Tablespoon, 31 | } 32 | 33 | #[derive(Debug, Copy, Clone, PartialEq)] 34 | struct Amount { 35 | unit: Unit, 36 | size: u8, 37 | } 38 | 39 | #[derive(Debug, Clone, PartialEq)] 40 | struct Ingredient { 41 | amount: Amount, 42 | name: String, 43 | } 44 | 45 | #[derive(Debug, Copy, Clone, PartialEq)] 46 | enum Error { 47 | ExpectedWhitespace, 48 | ExpectedNumber, 49 | UnknownUnit, 50 | ExpectedName, 51 | InputRemaining, 52 | } 53 | 54 | impl Recoverable for Error { 55 | fn recoverable(&self) -> bool { 56 | use Error::*; 57 | 58 | match *self { 59 | ExpectedWhitespace | ExpectedNumber | ExpectedName => true, 60 | InputRemaining => true, 61 | UnknownUnit => false, 62 | } 63 | } 64 | } 65 | 66 | type IngredientMaster<'a> = peresil::ParseMaster, Error>; 67 | type IngredientProgress<'a, T> = peresil::Progress, T, Error>; 68 | 69 | /// Parse a sequence of one-or-more ASCII space characters 70 | fn parse_whitespace<'a>( 71 | _: &mut IngredientMaster<'a>, 72 | pt: StringPoint<'a>, 73 | ) -> IngredientProgress<'a, &'a str> { 74 | let digits = pt.s.chars().take_while(|&c| c == ' ').count(); 75 | let r = if digits == 0 { 76 | pt.consume_to(None) 77 | } else { 78 | pt.consume_to(Some(digits)) 79 | }; 80 | 81 | r.map_err(|_| Error::ExpectedWhitespace) 82 | } 83 | 84 | /// Parse a sequence of one-or-more ASCII digits 85 | fn parse_number<'a>( 86 | _: &mut IngredientMaster<'a>, 87 | pt: StringPoint<'a>, 88 | ) -> IngredientProgress<'a, u8> { 89 | // We can cheat and know that ASCII 0-9 only takes one byte each 90 | let digits = pt.s.chars().take_while(|&c| c >= '0' && c <= '9').count(); 91 | let r = if digits == 0 { 92 | pt.consume_to(None) 93 | } else { 94 | pt.consume_to(Some(digits)) 95 | }; 96 | 97 | let (pt, v) = try_parse!(r.map_err(|_| Error::ExpectedNumber)); 98 | 99 | let num = v.parse().unwrap(); 100 | peresil::Progress::success(pt, num) 101 | } 102 | 103 | fn parse_unit<'a>( 104 | _: &mut IngredientMaster<'a>, 105 | pt: StringPoint<'a>, 106 | ) -> IngredientProgress<'a, Unit> { 107 | let identifiers = &[ 108 | ("cups", Unit::Cup), 109 | ("cup", Unit::Cup), 110 | ("c", Unit::Cup), 111 | ("ounces", Unit::Ounce), 112 | ("ounce", Unit::Ounce), 113 | ("oz", Unit::Ounce), 114 | ("tablespoons", Unit::Tablespoon), 115 | ("tablespoon", Unit::Tablespoon), 116 | ("tbsp", Unit::Tablespoon), 117 | ]; 118 | 119 | pt.consume_identifier(identifiers) 120 | .map_err(|_| Error::UnknownUnit) 121 | } 122 | 123 | /// Parse a sequence of 1-or-more characters that aren't newlines 124 | fn parse_name<'a>( 125 | _: &mut IngredientMaster<'a>, 126 | pt: StringPoint<'a>, 127 | ) -> IngredientProgress<'a, &'a str> { 128 | let len = pt.s.len(); 129 | let end_of_name = pt.s.find('\n').or(if len > 0 { Some(len) } else { None }); 130 | 131 | pt.consume_to(end_of_name).map_err(|_| Error::ExpectedName) 132 | } 133 | 134 | fn parse_ingredient<'a>( 135 | pm: &mut IngredientMaster<'a>, 136 | pt: StringPoint<'a>, 137 | ) -> IngredientProgress<'a, Ingredient> { 138 | let (pt, size) = try_parse!(parse_number(pm, pt)); 139 | let (pt, _) = try_parse!(parse_whitespace(pm, pt)); 140 | let (pt, unit) = try_parse!(parse_unit(pm, pt)); 141 | let (pt, _) = try_parse!(parse_whitespace(pm, pt)); 142 | let (pt, name) = try_parse!(parse_name(pm, pt)); 143 | 144 | let i = Ingredient { 145 | amount: Amount { size, unit }, 146 | name: name.to_owned(), 147 | }; 148 | peresil::Progress::success(pt, i) 149 | } 150 | 151 | fn parse(s: &str) -> Result, (usize, Vec)> { 152 | let mut pm = ParseMaster::new(); 153 | let pt = StringPoint::new(s); 154 | 155 | let r = pm.zero_or_more(pt, |pm, pt| parse_ingredient(pm, pt)); 156 | 157 | // Check if there's input left 158 | let r = match r { 159 | peresil::Progress { 160 | status: peresil::Status::Success(..), 161 | point: StringPoint { s: "", .. }, 162 | } => r, 163 | peresil::Progress { 164 | status: peresil::Status::Success(..), 165 | point, 166 | } => peresil::Progress::failure(point, Error::InputRemaining), 167 | _ => r, 168 | }; 169 | 170 | match pm.finish(r) { 171 | peresil::Progress { 172 | status: peresil::Status::Success(v), 173 | .. 174 | } => Ok(v), 175 | peresil::Progress { 176 | status: peresil::Status::Failure(e), 177 | point, 178 | } => Err((point.offset, e)), 179 | } 180 | } 181 | 182 | #[test] 183 | fn cups() { 184 | assert_eq!( 185 | parse("2 cups fresh snow peas"), 186 | Ok(vec![Ingredient { 187 | name: "fresh snow peas".to_owned(), 188 | amount: Amount { 189 | size: 2, 190 | unit: Unit::Cup 191 | } 192 | }]) 193 | ); 194 | } 195 | 196 | #[test] 197 | fn ounces() { 198 | assert_eq!( 199 | parse("1 oz water"), 200 | Ok(vec![Ingredient { 201 | name: "water".to_owned(), 202 | amount: Amount { 203 | size: 1, 204 | unit: Unit::Ounce 205 | } 206 | }]) 207 | ); 208 | } 209 | 210 | #[test] 211 | fn tablespoons() { 212 | assert_eq!( 213 | parse("6 tbsp baking soda"), 214 | Ok(vec![Ingredient { 215 | name: "baking soda".to_owned(), 216 | amount: Amount { 217 | size: 6, 218 | unit: Unit::Tablespoon 219 | } 220 | }]) 221 | ); 222 | } 223 | 224 | #[test] 225 | fn failure_invalid_size() { 226 | assert_eq!( 227 | parse("many tbsp salt"), 228 | Err((0, vec![Error::ExpectedNumber, Error::InputRemaining])) 229 | ); 230 | } 231 | 232 | #[test] 233 | fn failure_expected_whitespace_after_size() { 234 | assert_eq!(parse("5cup"), Err((1, vec![Error::ExpectedWhitespace]))); 235 | } 236 | 237 | #[test] 238 | fn failure_unknown_unit() { 239 | assert_eq!(parse("100 grains rice"), Err((4, vec![Error::UnknownUnit]))); 240 | } 241 | 242 | #[test] 243 | fn failure_expected_whitespace_after_unit() { 244 | assert_eq!(parse("10 cups"), Err((7, vec![Error::ExpectedWhitespace]))); 245 | } 246 | 247 | #[test] 248 | fn failure_expected_name() { 249 | assert_eq!(parse("10 cups "), Err((8, vec![Error::ExpectedName]))); 250 | } 251 | --------------------------------------------------------------------------------