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())
}
}
The solution was to check for a space, closing paren or eof after
parse_token, this solved the problem for me: