Вдохновленный этим видео , я подумал, что небольшая библиотека комбинатора синтаксического анализатора будет хорошим способом узнать о строках, заимствованиях и вводе в Rust - и это было до сих пор.
I удалось заставить работать синтаксический анализатор и di git:
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),
}
}
Затем я хотел обратиться к комбинаторам, здесь some
, чтобы получить произвольное количество совпадений для данного парсера. Этот сильно ударил меня. Это версия, которая была у меня в начале, которая могла выполнять некоторые модульные тесты:
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)
}
Но чтобы иметь возможность использовать ее с parse::parse
, она должна принимать только функцию синтаксического анализа и возвращать ее. Я пробовал так много вариантов:
fn(&str) -> Parsed<T>
в качестве типа возврата impl Fn(&str) -> Parsed<T>
в качестве типа возврата impl FnOnce(&str) -> Parsed<T>
в качестве типа возврата - несколько
for<'r> something
, которые выплюнул компилятор, и я даже не понимаю - , упаковывающих код в замыкание и возвращающих это, с
move
* 1031 и без него всегда хотя бы одну строчку, которой Руст не был доволен. Теперь я не знаю, что попробовать больше. Код тестирования выглядит следующим образом:
#[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")
);
}
Вот ссылка на игровую площадку .