rust-parser-combinator/src/primitives.rs

79 lines
2.2 KiB
Rust

use crate::{
representation::{Representation, EBNF},
ParseResult, Parser, ParserExtension,
};
pub fn literal<'a>(expected: &'static str) -> impl Parser<&'a str, &'a str, ()> {
let p = move |input: &'a str| match input.get(0..expected.len()) {
Some(next) if next == expected => Ok((next, &input[expected.len()..])),
_ => Err(((), input)),
};
let production = EBNF::StringTerminal(expected.into());
p.to_anno()
.with_repr(Representation::new().with_production(production))
}
pub fn literal_char<'a>(expected: char) -> impl Parser<&'a str, char, ()> {
(move |input: &'a str| match input.chars().next() {
Some(ch) if ch == expected => Ok((expected, &input[ch.len_utf8()..])),
_ => Err(((), input)),
})
.to_anno()
.with_repr(Representation::new().with_production(EBNF::CharTerminal(expected)))
}
pub fn one_of<'a>(items: &'static str) -> impl Parser<&'a str, char, ()> {
move |input: &'a str| {
if let Some(ch) = input.chars().next() {
if items.contains(ch) {
let (_first, rest) = input.split_at(1);
return Ok((ch, rest));
}
}
Err(((), input))
}
}
/// Parses a standard identifier in a programming language
pub fn identifier(input: &str) -> ParseResult<&str, String, ()> {
let mut chars = input.chars();
let mut buf = String::new();
match chars.next() {
Some(ch) if ch.is_alphabetic() => buf.push(ch),
_ => return Err(((), input)),
}
for next in chars {
if next.is_alphanumeric() {
buf.push(next);
} else {
break;
}
}
let next_index = buf.len();
Ok((buf, &input[next_index..]))
}
pub fn whitespace(input: &str) -> ParseResult<&str, char, ()> {
match input.chars().next() {
Some(ch) if ch.is_whitespace() => Ok((ch, &input[1..])),
_ => Err(((), input)),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn literals() {
let parser = literal_char('f');
assert_eq!(Ok(('f', "unky")), parser.parse("funky"));
let repr = parser.representation();
assert!(matches!(repr.production(), EBNF::CharTerminal('f')));
}
}