I'm trying to write a parser using rust and nom which can parse separated lists of tokens with the separator usually surrounded by spaces, but not if the thing between the tokens is in parenthesis.
For example, this is a valid expression: a and b
as would be (a)and(b)
or (a) and b
, however aandb
is not valid.
For cases other than (a)and(b)
, the following code works fine. But removing the spaces from tag(" and ")
makes (a)andb
valid. How can I support both cases?
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::character::complete::{alpha1, char};
use nom::combinator::{all_consuming, complete, verify};
use nom::error::Error;
use nom::multi::separated_list1;
use nom::sequence::delimited;
use nom::{Finish, IResult};
fn parse_token(i: &str) -> IResult<&str, &str> {
alpha1(i)
}
fn parse_parens(i: &str) -> IResult<&str, &str> {
delimited(char('('), parse_token, char(')'))(i)
}
fn parse_and(i: &str) -> IResult<&str, Vec<&str>> {
separated_list1(tag(" and "), alt((parse_parens, parse_token)))(i)
}
fn parse(i: &str) -> Result<Vec<&str>, Error<&str>> {
let result = all_consuming(complete(parse_and))(i);
result.finish().map(|(_, o)| o)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn no_parens() {
assert!(parse("a and b").is_ok())
}
#[test]
fn parens() {
assert!(parse("(a) and (b)").is_ok())
}
#[test]
fn mixed() {
assert!(parse("(a) and b").is_ok())
}
#[test]
fn parens_no_space() {
assert!(parse("(a)and b").is_ok())
}
#[test]
fn no_parens_no_space() {
assert!(parse("(a)andb").is_err())
}
}