yap/lib.rs
1/*!
2This small, zero-dependency crate helps you to parse input strings and slices by building on the [`Iterator`]
3interface.
4
5The aim of this crate is to provide the sorts of functions you'd come to expect from a parser
6combinator library, but without immersing you into a world of parser combinators and forcing you
7to use a novel return type, library-provided errors or parser-combinator based control flow. we
8sacrifice some conciseness in exchange for simplicity.
9
10**Some specific features/goals:**
11- Great documentation, with examples for almost every function provided.
12- Prioritise simplicity at the cost of verbosity.
13- Be iterator-centric. Where applicable, combinators return things which implement [`Tokens`]/[`Iterator`].
14- Allow user defined errors to be returned anywhere that it might make sense. Some functions have `_err`
15 variants incase you need error information when they don't otherwise hand back errors for simplicity.
16- Location information should always be available, so that you can tell users where something went wrong.
17 see [`Tokens::offset`] and [`Tokens::location()`].
18- Backtracking by default. Coming from Haskell's Parsec, this feels like the sensible default. It means that
19 if one of the provided parsing functions fails to parse something, it won't consume any input trying.
20- Expose all of the "low level" functions. You can save and rewind to locations as needed (see [`Tokens::location`]),
21 and implement any of the provided functions using these primitives.
22- Aims to be "fairly quick". Avoids allocations (and allows you to do the same via the iterator-centric interface)
23 almost everywhere. If you need "as fast as you can get", there amay be quicker alternatives.
24
25Have a look at the [`Tokens`] trait for all of the parsing methods made available, and examples for each.
26
27Have a look in the `examples` folder for more in depth examples.
28
29# Example
30
31```rust
32use yap::{
33 // This trait has all of the parsing methods on it:
34 Tokens,
35 // Allows you to use `.into_tokens()` on strings and slices,
36 // to get an instance of the above:
37 IntoTokens
38};
39
40// Step 1: convert our input into something implementing `Tokens`
41// ================================================================
42
43let mut tokens = "10 + 2 x 12-4,foobar".into_tokens();
44
45// Step 2: Parse some things from our tokens
46// =========================================
47
48#[derive(PartialEq,Debug)]
49enum Op { Plus, Minus, Multiply }
50#[derive(PartialEq,Debug)]
51enum OpOrDigit { Op(Op), Digit(u32) }
52
53// The `Tokens` trait builds on `Iterator`, so we get a `next` method.
54fn parse_op(t: &mut impl Tokens<Item=char>) -> Option<Op> {
55 match t.next()? {
56 '-' => Some(Op::Minus),
57 '+' => Some(Op::Plus),
58 'x' => Some(Op::Multiply),
59 _ => None
60 }
61}
62
63// We also get other useful functions..
64fn parse_digits(t: &mut impl Tokens<Item=char>) -> Option<u32> {
65 t.take_while(|c| c.is_digit(10))
66 .parse::<u32, String>()
67 .ok()
68}
69
70// As well as combinator functions like `sep_by_all` and `surrounded_by`..
71let op_or_digit = tokens.sep_by_all(
72 |t| t.surrounded_by(
73 |t| parse_digits(t).map(OpOrDigit::Digit),
74 |t| { t.skip_while(|c| c.is_ascii_whitespace()); }
75 ),
76 |t| parse_op(t).map(OpOrDigit::Op)
77);
78
79// Now we've parsed our input into OpOrDigits, let's calculate the result..
80let mut current_op = Op::Plus;
81let mut current_digit = 0;
82for d in op_or_digit.into_iter() {
83 match d {
84 OpOrDigit::Op(op) => {
85 current_op = op
86 },
87 OpOrDigit::Digit(n) => {
88 match current_op {
89 Op::Plus => { current_digit += n },
90 Op::Minus => { current_digit -= n },
91 Op::Multiply => { current_digit *= n },
92 }
93 },
94 }
95}
96assert_eq!(current_digit, 140);
97
98// Step 3: do whatever you like with the rest of the input!
99// ========================================================
100
101// This is available on the concrete type that strings
102// are converted into (rather than on the `Tokens` trait):
103let remaining = tokens.remaining();
104
105assert_eq!(remaining, ",foobar");
106```
107*/
108#![deny(missing_docs)]
109#![cfg_attr(not(feature = "std"), no_std)]
110
111mod tokens;
112
113#[doc(hidden)]
114pub mod one_of;
115
116pub mod chars;
117pub mod types;
118pub use tokens::{IntoTokens, TokenLocation, Tokens};