Skip to content

Commit aed956e

Browse files
committed
add OptionalOrElse Parser
1 parent 5d1772c commit aed956e

File tree

5 files changed

+137
-5
lines changed

5 files changed

+137
-5
lines changed

README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ where
116116
| `map` | Map the output of the parser | `(T,)` |
117117
| `repeat` | Repeat the parser multiple times | `(Vec<Output of Self>,)` |
118118
| `optional` | Success whether the pattern is matched or not | `( Option<Output of Self>, )` |
119-
| `optional_or` | Success whether the pattern is matched or not | `Output` of `Self` |
119+
| `optional_or`, `or_else` | Success whether the pattern is matched or not | `Output` of `Self` |
120120
| `not` | Match for Pattern1 to success and Pattern2 to fail | `Output` of `Self` |
121121
| `reduce_left`, `reduce_right` | Reduce the output of the parser | `Output` of `Self` |
122122
| `reduce_with`, `reduce_right_with` | Reduce the output of the parser with initial value | `Init` |
@@ -327,7 +327,7 @@ assert_eq!(res.it.collect::<String>(), "bcd");
327327

328328

329329

330-
### `optional`, `optional_or`: success whether the pattern is matched or not
330+
### `optional`, `optional_or`, `or_else`: success whether the pattern is matched or not
331331
```rust
332332
let a_optional_parser = 'a'.optional(); // (Option<char>,)
333333

@@ -342,17 +342,21 @@ let a_optional_or = 'a'.optional_or(('x',)); // (char,)
342342

343343
let res = rp::parse(&a_optional_or, "bcd".chars());
344344
assert_eq!(res.output.unwrap(), ('x',));
345+
346+
// if 'a' failed, evaluate the closure
347+
let a_or_else = 'a'.or_else( || 'x' ); // (char,)
348+
assert_eq!(res.output.unwrap(), ('x',));
345349
```
346350

347351
`Output` for `optional`:
348352
- if `Output` of the origin parser is `(T0,)`, `(Option<T0>,)`
349353
- otherwise, `( Option<Output of Self>, )`
350354

351-
`Output` for `optional_or`:
355+
`Output` for `optional_or`, `or_else`:
352356
- <`Output` of `Self`>.
353357

354358
#### Note
355-
- The passed value's type to `optional_or` must match with the `Output` of `Self`
359+
- The passed value's type to `optional_or` and `or_else` must match with the `Output` of `Self`
356360
- For single-value-output ( which's output is `(T,)` ), passing either `T` or `(T,)` is permitted.
357361

358362

rusty_parser/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rusty_parser"
3-
version = "1.0.2"
3+
version = "1.1.0"
44
edition = "2021"
55
authors = ["ehwan <ehwank98@gmail.com>"]
66
description = "A Generic compile-time Parser generator and pattern matching library written in Rust"

rusty_parser/src/core/into_parser.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,38 @@ pub trait IntoParser {
237237
crate::wrapper::option::OptionalOrParser::new(self.into_parser(), output)
238238
}
239239

240+
/// This parser always success whether the input is matched or not.
241+
/// If it failed, the given closure will be evaluated and returned.
242+
///
243+
/// `Output`:
244+
/// <`Output` of Self>.
245+
///
246+
/// The value given to `or_else` must match with the `Output` of the origin parser.
247+
///
248+
/// For single-value-output ( which's output is `(T,)` ),
249+
/// returning either `T` or `(T,)` is permitted.
250+
///
251+
/// # Example
252+
/// ```rust
253+
/// use rusty_parser as rp;
254+
/// use rp::IntoParser;
255+
///
256+
/// // if 'a' failed, return 'x'
257+
/// let a_or_else = 'a'.or_else(|| 'x'); // (char,)
258+
///
259+
/// let res = rp::parse(&a_or_else, "bcd".chars());
260+
/// assert_eq!(res.output.unwrap(), ('x',));
261+
/// ```
262+
fn or_else<Closure>(
263+
self,
264+
closure: Closure,
265+
) -> crate::wrapper::or_else::OptionalOrElseParser<Self::Into, Closure>
266+
where
267+
Self: Sized,
268+
{
269+
crate::wrapper::or_else::OptionalOrElseParser::new(self.into_parser(), closure)
270+
}
271+
240272
/// Match for parser1 but not parser2.
241273
///
242274
/// `Output`: `Output` of `Self`

rusty_parser/src/wrapper/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ pub mod not;
55
pub mod notconsume;
66
pub mod option;
77
pub mod or;
8+
pub mod or_else;
89
pub mod output;
910
pub mod rced;
1011
pub mod reduce;
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
use crate::core::into_parser::IntoParser;
2+
use crate::core::iterator_bound::InputIteratorTrait;
3+
use crate::core::parser::Parser;
4+
use crate::core::result::ParseResult;
5+
use crate::core::tuple::Tuple;
6+
use crate::wrapper::tupleutils::singlevalue::SingleValueAutoTuple;
7+
8+
#[derive(Debug, Clone, Copy)]
9+
pub struct OptionalOrElseParser<Parser, F> {
10+
parser: Parser,
11+
closure: F,
12+
}
13+
14+
impl<Parser, F> OptionalOrElseParser<Parser, F> {
15+
pub fn new(parser: Parser, closure: F) -> Self {
16+
Self { parser, closure }
17+
}
18+
}
19+
20+
impl<ParserA, F, It, FnOutput> Parser<It> for OptionalOrElseParser<ParserA, F>
21+
where
22+
It: InputIteratorTrait,
23+
ParserA: Parser<It>,
24+
F: Fn() -> FnOutput,
25+
FnOutput: SingleValueAutoTuple<ParserA::Output, Output = ParserA::Output>,
26+
FnOutput::Output: Tuple,
27+
{
28+
type Output = <ParserA as Parser<It>>::Output;
29+
30+
fn parse(&self, it: It) -> ParseResult<Self::Output, It> {
31+
let res = self.parser.parse(it);
32+
if let Some(val) = res.output {
33+
return ParseResult {
34+
output: Some(val),
35+
it: res.it,
36+
};
37+
}
38+
ParseResult {
39+
output: Some((self.closure)().wrap()),
40+
it: res.it,
41+
}
42+
}
43+
44+
fn match_pattern(&self, it: It) -> ParseResult<(), It> {
45+
let res = self.parser.match_pattern(it);
46+
if res.output.is_some() {
47+
ParseResult {
48+
output: Some(()),
49+
it: res.it,
50+
}
51+
} else {
52+
ParseResult {
53+
output: Some(()),
54+
it: res.it,
55+
}
56+
}
57+
}
58+
}
59+
60+
impl<Parser, F> IntoParser for OptionalOrElseParser<Parser, F> {
61+
type Into = OptionalOrElseParser<Parser, F>;
62+
63+
fn into_parser(self) -> Self::Into {
64+
self
65+
}
66+
}
67+
68+
#[cfg(test)]
69+
mod test {
70+
use super::*;
71+
use crate::leaf::singlerange::SingleRangeParser;
72+
#[test]
73+
fn success1() {
74+
let digit_parser = SingleRangeParser::from('0'..='9');
75+
let digit_or_parser = OptionalOrElseParser::new(digit_parser, || 'a');
76+
77+
let str = "1a2b3c";
78+
79+
let res = digit_or_parser.parse(str.chars());
80+
assert_eq!(res.output, Some(('1',)));
81+
assert_eq!(res.it.as_str(), "a2b3c");
82+
}
83+
84+
#[test]
85+
fn success2() {
86+
let digit_parser = SingleRangeParser::from('0'..='9');
87+
let digit_or_parser = OptionalOrElseParser::new(digit_parser, || 'a');
88+
89+
let str = "ba2b3c";
90+
91+
let res = digit_or_parser.parse(str.chars());
92+
assert_eq!(res.output, Some(('a',)));
93+
assert_eq!(res.it.as_str(), "ba2b3c");
94+
}
95+
}

0 commit comments

Comments
 (0)