Modules
This commit is contained in:
parent
8f00b77c2c
commit
cbb30d3e9f
21
src/choice.rs
Normal file
21
src/choice.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
use crate::Parser;
|
||||||
|
|
||||||
|
pub fn choice<'a, I, O, E>(parsers: &'a [&'a dyn Parser<I, O, E>]) -> impl Parser<I, O, E> + 'a
|
||||||
|
where
|
||||||
|
I: Clone,
|
||||||
|
{
|
||||||
|
move |input: I| {
|
||||||
|
//TODO need a more principled way to return an error when no choices work
|
||||||
|
let mut err = None;
|
||||||
|
|
||||||
|
for parser in parsers.iter() {
|
||||||
|
match parser.parse(input.clone()) {
|
||||||
|
Ok(res) => return Ok(res),
|
||||||
|
Err(e) => {
|
||||||
|
err = Some(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err.unwrap())
|
||||||
|
}
|
||||||
|
}
|
61
src/lib.rs
61
src/lib.rs
@ -1,8 +1,15 @@
|
|||||||
#![allow(dead_code)] //TODO eventually turn this off
|
#![allow(dead_code)] //TODO eventually turn this off
|
||||||
|
mod choice;
|
||||||
|
mod primitives;
|
||||||
|
mod sequence;
|
||||||
|
|
||||||
|
pub use choice::*;
|
||||||
|
pub use primitives::*;
|
||||||
|
pub use sequence::*;
|
||||||
|
|
||||||
type ParseResult<I, O, E> = Result<(O, I), E>;
|
type ParseResult<I, O, E> = Result<(O, I), E>;
|
||||||
|
|
||||||
trait Parser<I, O, E> {
|
pub trait Parser<I, O, E> {
|
||||||
fn parse(&self, input: I) -> ParseResult<I, O, E>;
|
fn parse(&self, input: I) -> ParseResult<I, O, E>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -15,46 +22,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn literal(expected: &'static str) -> impl Fn(&str) -> ParseResult<&str, &str, &str> {
|
|
||||||
move |input| match input.get(0..expected.len()) {
|
|
||||||
Some(next) if next == expected => Ok((next, &input[expected.len()..])),
|
|
||||||
_ => Err(input),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sequence<I, O1, O2, E>(
|
|
||||||
first: impl Parser<I, O1, E>,
|
|
||||||
second: impl Parser<I, O2, E>,
|
|
||||||
) -> impl Parser<I, (O1, O2), E> {
|
|
||||||
move |input| -> ParseResult<I, (O1, O2), E> {
|
|
||||||
first.parse(input).and_then(|(result1, rest)| {
|
|
||||||
second
|
|
||||||
.parse(rest)
|
|
||||||
.map(|(result2, rest2)| ((result1, result2), rest2))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn choice<'a, I, O, E>(parsers: &'a [&'a dyn Parser<I, O, E>]) -> impl Parser<I, O, E> + 'a
|
|
||||||
where
|
|
||||||
I: Clone,
|
|
||||||
{
|
|
||||||
move |input: I| {
|
|
||||||
//TODO need a more principled way to return an error when no choices work
|
|
||||||
let mut err = None;
|
|
||||||
|
|
||||||
for parser in parsers.iter() {
|
|
||||||
match parser.parse(input.clone()) {
|
|
||||||
Ok(res) => return Ok(res),
|
|
||||||
Err(e) => {
|
|
||||||
err = Some(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err.unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -74,4 +41,16 @@ mod tests {
|
|||||||
assert_eq!(output.0 .1, (" ", "jonzzz"));
|
assert_eq!(output.0 .1, (" ", "jonzzz"));
|
||||||
assert_eq!(output.1, "");
|
assert_eq!(output.1, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_choice() {
|
||||||
|
let a = literal("bongo");
|
||||||
|
let b = literal("sucy");
|
||||||
|
let c = literal("ara");
|
||||||
|
let inputs = [&a as &dyn Parser<&str, &str, &str>, &b, &c];
|
||||||
|
let parser = choice(&inputs);
|
||||||
|
|
||||||
|
let output = parser.parse("ara hajimete").unwrap();
|
||||||
|
assert_eq!(("ara", " hajimete"), output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
26
src/primitives.rs
Normal file
26
src/primitives.rs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
use crate::{ParseResult, Parser};
|
||||||
|
|
||||||
|
pub fn literal(expected: &'static str) -> impl Fn(&str) -> ParseResult<&str, &str, &str> {
|
||||||
|
move |input| match input.get(0..expected.len()) {
|
||||||
|
Some(next) if next == expected => Ok((next, &input[expected.len()..])),
|
||||||
|
_ => Err(input),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn literal_char<'a>(expected: char) -> impl Parser<&'a str, char, &'a str> {
|
||||||
|
move |input: &'a str| match input.chars().next() {
|
||||||
|
Some(ch) if ch == expected => Ok((expected, &input[ch.len_utf8()..])),
|
||||||
|
_ => Err(input),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn literals() {
|
||||||
|
let parser = literal_char('f');
|
||||||
|
assert_eq!(Ok(('f', "unky")), parser.parse("funky"));
|
||||||
|
}
|
||||||
|
}
|
14
src/sequence.rs
Normal file
14
src/sequence.rs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
use crate::{ParseResult, Parser};
|
||||||
|
|
||||||
|
pub fn sequence<I, O1, O2, E>(
|
||||||
|
first: impl Parser<I, O1, E>,
|
||||||
|
second: impl Parser<I, O2, E>,
|
||||||
|
) -> impl Parser<I, (O1, O2), E> {
|
||||||
|
move |input| -> ParseResult<I, (O1, O2), E> {
|
||||||
|
first.parse(input).and_then(|(result1, rest)| {
|
||||||
|
second
|
||||||
|
.parse(rest)
|
||||||
|
.map(|(result2, rest2)| ((result1, result2), rest2))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user