use nom::{
IResult, Parser,
branch::alt,
bytes::complete::tag,
character::complete::{char, digit1, space1},
combinator::{map, recognize, value},
multi::many_m_n,
sequence::{pair, preceded, terminated},
};
use D4Token::*;
#[derive(Debug, Clone, PartialEq)]
pub enum D4Token {
And,
Or,
True,
False,
Edge {
from: i32,
to: i32,
features: Vec<i32>,
},
}
#[inline]
pub fn lex_line_d4(line: &str) -> IResult<&str, D4Token> {
alt((lex_edge, lex_or, lex_and, lex_true, lex_false)).parse(line)
}
fn lex_edge(line: &str) -> IResult<&str, D4Token> {
map(
terminated(
recognize(many_m_n(2, usize::MAX, parse_signed_alt_space1_number1)),
tag("0"),
),
|out: &str| {
let ws_numbers: Vec<i32> = out
.split_whitespace()
.map(|num: &str| {
num.parse::<i32>().unwrap_or_else(|_| {
panic!("Was not able to parse i32 for edge. String was {out}")
})
})
.collect::<Vec<i32>>();
Edge {
from: ws_numbers[0],
to: ws_numbers[1],
features: ws_numbers[2..ws_numbers.len()].to_vec(),
}
},
)
.parse(line)
}
pub(super) fn parse_signed_alt_space1_number1(line: &str) -> IResult<&str, (&str, &str)> {
alt((pair(digit1, space1), pair(neg_digit1, space1))).parse(line)
}
fn neg_digit1(line: &str) -> IResult<&str, &str> {
recognize(pair(char('-'), digit1)).parse(line)
}
fn lex_and(line: &str) -> IResult<&str, D4Token> {
value(And, preceded(tag("a "), digit1)).parse(line)
}
fn lex_or(line: &str) -> IResult<&str, D4Token> {
value(Or, preceded(tag("o "), digit1)).parse(line)
}
fn lex_true(line: &str) -> IResult<&str, D4Token> {
value(True, preceded(tag("t "), digit1)).parse(line)
}
fn lex_false(line: &str) -> IResult<&str, D4Token> {
value(False, preceded(tag("f "), digit1)).parse(line)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn individual_lexer_d4() {
let and_str = "a 1 0";
let or_str = "o 2 0";
let true_str = "t 3 0";
let false_str = "f 4 0";
let edge_str = "2 3 4 -5 0";
let failed_edge = "2 3 -5 THREE 0";
assert_eq!(lex_and(and_str).unwrap().1, And);
assert_eq!(lex_line_d4(and_str).unwrap().1, And,);
assert_eq!(lex_or(or_str).unwrap().1, Or);
assert_eq!(lex_line_d4(or_str).unwrap().1, Or);
assert_eq!(lex_true(true_str).unwrap().1, True,);
assert_eq!(lex_line_d4(true_str).unwrap().1, True,);
assert_eq!(lex_false(false_str).unwrap().1, False,);
assert_eq!(lex_line_d4(false_str).unwrap().1, False,);
assert_eq!(
lex_edge(edge_str).unwrap().1,
Edge {
from: 2,
to: 3,
features: vec![4, -5]
},
);
assert_eq!(
lex_line_d4(edge_str).unwrap().1,
Edge {
from: 2,
to: 3,
features: vec![4, -5]
},
);
let result = std::panic::catch_unwind(|| lex_edge(failed_edge)).unwrap();
assert!(result.is_err());
let result = std::panic::catch_unwind(|| lex_line_d4(failed_edge)).unwrap();
assert!(result.is_err());
}
}