use crate::propform::PropformToken::*;
use crate::temex_error::TemexError;
use core::iter::Peekable;
use core::str::Chars;
use itertools::Itertools;
use std::iter::Iterator;
use std::str::FromStr;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum PropformToken {
AlphaNum(String),
True,
False,
LParen,
RParen,
And,
Or,
Xor,
Nand,
Nor,
Not,
Eof,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum PropformErrorToken {
AlphaNum,
LParen,
RParen,
And,
Or,
Xor,
Nand,
Nor,
Not,
Eof,
}
pub fn get_boolean_vars(raw_input: &str) -> Result<Vec<String>, TemexError> {
Ok(lex_propform(raw_input)?
.into_iter()
.filter_map(|x| match x {
AlphaNum(s) => Some(s),
_ => None,
})
.collect::<Vec<String>>())
}
fn consume_whitespace(raw_chars: &mut Peekable<Chars>) {
for _ in raw_chars.peeking_take_while(|x| (*x).is_ascii_whitespace()) {}
}
pub fn lex_propform(raw_input: &str) -> Result<Vec<PropformToken>, TemexError> {
let raw_chars = &mut raw_input.chars().peekable();
let mut tokens: Vec<PropformToken> = vec![];
loop {
consume_whitespace(raw_chars);
let ch = raw_chars.next();
match ch {
None => break,
Some('(') => tokens.push(PropformToken::LParen),
Some(')') => tokens.push(PropformToken::RParen),
Some(c) if c.is_ascii_alphabetic() => {
let mut alpha_num_chars = vec![c];
loop {
match raw_chars.peek() {
Some(chr) if (*chr).is_ascii_alphanumeric() || *chr == '_' => {
alpha_num_chars.push(*chr);
let _ = raw_chars.next();
}
_ => break,
}
}
let alpha_num: String = alpha_num_chars.into_iter().collect();
match alpha_num.as_str() {
"and" => tokens.push(PropformToken::And),
"or" => tokens.push(PropformToken::Or),
"not" => tokens.push(PropformToken::Not),
"nand" => tokens.push(PropformToken::Nand),
"xor" => tokens.push(PropformToken::Xor),
"nor" => tokens.push(PropformToken::Nor),
"true" => tokens.push(PropformToken::True),
"false" => tokens.push(PropformToken::False),
_ => tokens.push(PropformToken::AlphaNum(alpha_num)),
}
}
Some(e) => return Err(TemexError::UnexpectedCharacter(e)),
}
}
Ok(tokens)
}
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum Propform {
True,
False,
BooleanVar(String),
And(Box<Propform>, Box<Propform>),
Nand(Box<Propform>, Box<Propform>),
Nor(Box<Propform>, Box<Propform>),
Xor(Box<Propform>, Box<Propform>),
Or(Box<Propform>, Box<Propform>),
Not(Box<Propform>),
}
impl FromStr for Propform {
type Err = TemexError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
fn pf0(tokens: &[PropformToken]) -> Result<(Propform, &[PropformToken]), TemexError> {
let (lhs, rest) = pf1(tokens)?;
match rest.split_first() {
Some((And, ys)) => {
let (rhs, rest2) = pf0(ys)?;
Ok((Propform::And(Box::new(lhs), Box::new(rhs)), rest2))
}
Some((Or, ys)) => {
let (rhs, rest2) = pf0(ys)?;
Ok((Propform::Or(Box::new(lhs), Box::new(rhs)), rest2))
}
Some((Xor, ys)) => {
let (rhs, rest2) = pf0(ys)?;
Ok((Propform::Xor(Box::new(lhs), Box::new(rhs)), rest2))
}
Some((Nor, ys)) => {
let (rhs, rest2) = pf0(ys)?;
Ok((Propform::Nor(Box::new(lhs), Box::new(rhs)), rest2))
}
Some((Nand, ys)) => {
let (rhs, rest2) = pf0(ys)?;
Ok((Propform::Nand(Box::new(lhs), Box::new(rhs)), rest2))
}
Some((RParen, _)) | None => Ok((lhs, rest)),
Some((e, _)) => Err(TemexError::UnexpectedToken(
vec![
PropformErrorToken::And,
PropformErrorToken::Or,
PropformErrorToken::Xor,
PropformErrorToken::Nor,
PropformErrorToken::Nand,
PropformErrorToken::RParen,
],
e.clone(),
)),
}
}
fn pf1(tokens: &[PropformToken]) -> Result<(Propform, &[PropformToken]), TemexError> {
match tokens.split_first() {
Some((Not, xs)) => {
let (prop, rest) = pf1(xs)?;
Ok((Propform::Not(Box::new(prop)), rest))
}
_ => pf2(tokens),
}
}
fn pf2(tokens: &[PropformToken]) -> Result<(Propform, &[PropformToken]), TemexError> {
match tokens.split_first() {
Some((LParen, xs)) => {
let (prop, rest) = pf0(xs)?;
match rest.split_first() {
Some((RParen, ys)) => Ok((prop, ys)),
Some((e, _)) => Err(TemexError::UnexpectedToken(
vec![PropformErrorToken::RParen],
e.clone(),
)),
None => Err(TemexError::UnexpectedToken(
vec![PropformErrorToken::RParen],
Eof,
)),
}
}
Some((AlphaNum(s), xs)) => Ok((Propform::BooleanVar(s.to_owned()), xs)),
Some((True, xs)) => Ok((Propform::True, xs)),
Some((False, xs)) => Ok((Propform::False, xs)),
Some((e, _)) => Err(TemexError::UnexpectedToken(
vec![PropformErrorToken::RParen, PropformErrorToken::AlphaNum],
e.clone(),
)),
None => Err(TemexError::UnexpectedToken(
vec![PropformErrorToken::RParen, PropformErrorToken::AlphaNum],
Eof,
)),
}
}
let tokens = lex_propform(s)?;
match pf0(&tokens)? {
(propform, []) => Ok(propform),
(_, [e, ..]) => Err(TemexError::UnexpectedToken(
vec![PropformErrorToken::Eof],
e.clone(),
)),
}
}
}
#[cfg(test)]
mod parser_tests {
use super::Propform;
use super::Propform::*;
const PROP1: &str = "p1";
const PROP2: &str = "p1 and p2";
const PROP3: &str = "p1 or p2";
const PROP4: &str = "true";
const PROP5: &str = "false";
const PROP6: &str = "not p1";
#[test]
fn test1() {
let pf1 = PROP1.to_string().parse::<Propform>().unwrap();
let pf2 = PROP2.to_string().parse::<Propform>().unwrap();
let pf3 = PROP3.to_string().parse::<Propform>().unwrap();
let pf4 = PROP4.to_string().parse::<Propform>().unwrap();
let pf5 = PROP5.to_string().parse::<Propform>().unwrap();
let pf6 = PROP6.to_string().parse::<Propform>().unwrap();
let p1 = Box::new(pf1.clone());
let p2 = Box::new(BooleanVar("p2".to_string()));
assert_eq!(pf1, BooleanVar("p1".to_string()));
assert_eq!(pf2, And(p1.clone(), p2.clone()));
assert_eq!(pf3, Or(p1.clone(), p2.clone()));
assert_eq!(pf4, True);
assert_eq!(pf5, False);
assert_eq!(pf6, Not(p1.clone()));
}
}