问题
Inspired by this video, I thought a little parser combinator library would be a good way to learn about strings, borrowing and typing in Rust—and it was so far.
I managed to get a char parser and a digit parser to work:
pub enum Parsed<'a, T> {
Some(T, &'a str),
None(&'a str),
}
impl<T> Parsed<'_, T> {
// I was neither sure with the third & before the T...
pub fn unwrap(&self) -> (&T, &str) {
match self {
// ... nor with the first one here.
Parsed::Some(head, tail) => (&head, &tail),
_ => panic!("Called unwrap on nothing."),
}
// But this was the only way that I came up with that compiled.
}
pub fn is_none(&self) -> bool {
match self {
Parsed::None(_) => true,
_ => false,
}
}
}
pub fn parse<T>(what: fn(&str) -> Parsed<T>, input: &str) -> Parsed<T> {
what(input)
}
pub fn char(input: &str) -> Parsed<char> {
match input.chars().next() {
Some(c) => Parsed::Some(c, &input[1..]),
None => Parsed::None(input),
}
}
pub fn digit(input: &str) -> Parsed<u8> {
match input.chars().next() {
Some(d @ '0'..='9') => Parsed::Some(d as u8 - ('0' as u8), &input[1..]),
_ => Parsed::None(input),
}
}
Then I wanted to turn to combinators, here some
to get an arbitrary number of matches for a given parser. That one hit me hard. This is the version I had in the beginning that was able to fulfill some unit tests:
pub fn some<T>(input: &str, parser: fn(&str) -> Parsed<T>) -> Parsed<Vec<T>> {
let mut re = Vec::new();
let mut pos = input;
loop {
match parser(pos) {
Parsed::Some(head, tail) => {
re.push(head);
pos = tail;
}
Parsed::None(_) => break,
}
}
Parsed::Some(re, pos)
}
But to be able to use it with parse::parse
it has to take only a parser function and return one. I tried so many variants:
fn(&str) -> Parsed<T>
as return typeimpl Fn(&str) -> Parsed<T>
as return typeimpl FnOnce(&str) -> Parsed<T>
as return type- several
for<'r> something
which the compiler spat out and I don't even understand - packing the code into a closure and returning that, with and without
move
There was always at least one line that Rust wasn't happy with. Now I don't know what to try anymore. The testing code looks like this:
#[test]
fn test() {
assert_eq!(char("foo").unwrap(), (&'f', "oo"));
assert!(parse(digit, "foo").is_none());
assert_eq!(parse(digit, "9foo").unwrap(), (&9, "foo"));
assert_eq!(
parse(some(digit), "12space").unwrap(),
(&vec![1, 2], "space")
);
}
Here's a link to a playground.
回答1:
Return an anonymous type that implements one of the Fn*
traits by returning a closure:
fn some<T>(parser: impl Fn(&str) -> Parsed<T>) -> impl FnOnce(&str) -> Parsed<Vec<T>> {
move |input| {
let mut re = Vec::new();
let mut pos = input;
loop {
match parser(pos) {
Parsed::Some(head, tail) => {
re.push(head);
pos = tail;
}
Parsed::None(_) => break,
}
}
Parsed::Some(re, pos)
}
}
Playground
Note that I've switched from function pointers to generic types for the arguments:
fn some<T>(parser: fn(&str) -> Parsed<T>) // before
fn some<T>(parser: impl Fn(&str) -> Parsed<T>) // after
I advocate doing this for all of your functions so that you have a consistent and connectable API. This is the pattern taken by many parsing libraries, including my own peresil.
See also:
- How do you pass a Rust function as a parameter?
- Why does passing a closure to function which accepts a function pointer not work?
来源:https://stackoverflow.com/questions/60173959/how-do-i-write-combinators-for-my-own-parsers-in-rust