1use itertools::{Itertools, PeekingNext};
35use libreda_stream_parser::*;
36use std::ops::*;
37
38#[derive(Clone, Debug, Eq, PartialEq, Hash)]
40pub enum BooleanExpr<Var> {
41 False,
43 True,
45 Var(Var),
47 Neg(Box<Self>),
49 And(Box<Self>, Box<Self>),
51 Or(Box<Self>, Box<Self>),
53 Xor(Box<Self>, Box<Self>),
55}
56
57impl<V> From<bool> for BooleanExpr<V> {
58 fn from(value: bool) -> Self {
59 match value {
60 false => BooleanExpr::False,
61 true => BooleanExpr::True,
62 }
63 }
64}
65
66impl<V> BooleanExpr<V> {
67 pub fn eval<B>(&self, value_fn: &impl Fn(&V) -> B) -> B
89 where
90 B: Not<Output = B> + BitAnd<Output = B> + BitOr<Output = B> + BitXor<Output = B> + Default,
91 {
92 match self {
93 BooleanExpr::False => B::default(),
94 BooleanExpr::True => !B::default(),
95 BooleanExpr::Var(v) => value_fn(v),
96 BooleanExpr::Neg(expr) => !expr.eval(value_fn),
97 BooleanExpr::And(a, b) => a.eval(value_fn) & b.eval(value_fn),
98 BooleanExpr::Or(a, b) => a.eval(value_fn) | b.eval(value_fn),
99 BooleanExpr::Xor(a, b) => a.eval(value_fn) ^ b.eval(value_fn),
100 }
101 }
102
103 pub fn substitute_variable_names<V2>(
126 &self,
127 substitution: &impl Fn(&V) -> V2,
128 ) -> BooleanExpr<V2> {
129 self.substitute_variables(&|v| BooleanExpr::Var(substitution(v)))
130 }
131
132 pub fn substitute_variables<V2>(
156 &self,
157 substitution: &impl Fn(&V) -> BooleanExpr<V2>,
158 ) -> BooleanExpr<V2> {
159 match self {
160 BooleanExpr::False => BooleanExpr::False,
161 BooleanExpr::True => BooleanExpr::True,
162 BooleanExpr::Var(v) => substitution(v),
163 BooleanExpr::Neg(expr) => {
164 BooleanExpr::Neg(Box::new(expr.substitute_variables(substitution)))
165 }
166 BooleanExpr::And(a, b) => BooleanExpr::And(
167 Box::new(a.substitute_variables(substitution)),
168 Box::new(b.substitute_variables(substitution)),
169 ),
170 BooleanExpr::Or(a, b) => BooleanExpr::Or(
171 Box::new(a.substitute_variables(substitution)),
172 Box::new(b.substitute_variables(substitution)),
173 ),
174 BooleanExpr::Xor(a, b) => BooleanExpr::Xor(
175 Box::new(a.substitute_variables(substitution)),
176 Box::new(b.substitute_variables(substitution)),
177 ),
178 }
179 }
180}
181
182impl<V> std::fmt::Display for BooleanExpr<V>
183where
184 V: std::fmt::Display,
185{
186 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
187 match self {
188 BooleanExpr::False => write!(f, "false"),
189 BooleanExpr::True => write!(f, "true"),
190 BooleanExpr::Var(v) => write!(f, "{}", v),
191 BooleanExpr::Neg(expr) => match expr.as_ref() {
192 v @ BooleanExpr::False | v @ BooleanExpr::True => write!(f, "!{}", v),
193 BooleanExpr::Var(v) => write!(f, "!{}", v), _ => write!(f, "!({})", expr),
195 },
196 BooleanExpr::And(expr1, expr2) => write!(f, "({} & {})", expr1, expr2),
197 BooleanExpr::Or(expr1, expr2) => write!(f, "({} | {})", expr1, expr2),
198 BooleanExpr::Xor(expr1, expr2) => write!(f, "({} ^ {})", expr1, expr2),
199 }
200 }
201}
202
203#[test]
204fn test_eval_boolean_expr() {
205 let a = BooleanExpr::Var(0);
206 let b = BooleanExpr::Var(1);
207
208 let expr = a & b;
209
210 let values = vec![false, true];
211 assert!(!expr.eval(&|v| values[*v]));
212
213 let values = vec![true, true];
214 assert!(expr.eval(&|v| values[*v]));
215
216 let values = vec![0b1010, 0b1100];
218 assert_eq!(expr.eval(&|v| values[*v]), 0b1010 & 0b1100);
219}
220
221#[test]
222fn test_format_then_parse() {
223 let [a, b, c, d] = std::array::from_fn(|i| {
224 BooleanExpr::Var(i + 2 )
225 });
226
227 let expr = !a & (b | c) ^ d;
228
229 let formatted = format!("{}", &expr);
230
231 let (parsed_expr, var_names) = parse_boolean_function(formatted.chars().peekable()).unwrap();
232 let parsed_expr = parsed_expr.substitute_variable_names(&|v| v + 2);
233
234 assert_eq!(expr, parsed_expr);
235}
236
237impl<V> Not for BooleanExpr<V> {
238 type Output = Self;
239
240 fn not(self) -> Self::Output {
241 BooleanExpr::Neg(Box::new(self))
242 }
243}
244
245impl<V> BitAnd for BooleanExpr<V> {
246 type Output = Self;
247
248 fn bitand(self, rhs: Self) -> Self::Output {
249 Self::And(Box::new(self), Box::new(rhs))
250 }
251}
252
253impl<V> BitOr for BooleanExpr<V> {
254 type Output = Self;
255
256 fn bitor(self, rhs: Self) -> Self::Output {
257 Self::Or(Box::new(self), Box::new(rhs))
258 }
259}
260
261impl<V> BitXor for BooleanExpr<V> {
262 type Output = Self;
263
264 fn bitxor(self, rhs: Self) -> Self::Output {
265 Self::Xor(Box::new(self), Box::new(rhs))
266 }
267}
268
269impl<V> Add for BooleanExpr<V> {
270 type Output = Self;
271
272 fn add(self, rhs: Self) -> Self::Output {
273 self.bitor(rhs)
274 }
275}
276
277impl<V> Mul for BooleanExpr<V> {
278 type Output = Self;
279
280 fn mul(self, rhs: Self) -> Self::Output {
281 self.bitand(rhs)
282 }
283}
284
285fn is_terminal_char(c: char) -> bool {
286 c.is_whitespace()
287 || matches!(
288 c,
289 '(' | ')' | '!' | '~' | '+' | '&' | '|' | '*' | '^' | '\''
290 )
291}
292
293struct LibertyBooleanExpLexer {}
294
295impl Lexer for LibertyBooleanExpLexer {
296 type Char = char;
297
298 fn consume_next_token(
299 &mut self,
300 input: &mut (impl Iterator<Item = Self::Char> + itertools::PeekingNext),
301 mut output: impl FnMut(Self::Char),
302 ) -> Result<(), ParserError<Self::Char>> {
303 loop {
304 let _n = input.peeking_take_while(|c| c.is_whitespace()).count();
306
307 if let Some(c) = input.next() {
309 match c {
310 '#' => {
311 input
313 .peeking_take_while(|&c| c != '\n' && c != '\r')
314 .count();
315 }
316 '/' => {
317 if input.peeking_next(|&c| c == '*').is_some() {
318 loop {
321 input.peeking_take_while(|&c| c != '*').count();
323 if input.next() != Some('*') {
324 break; }
326
327 if input.peeking_next(|&c| c == '/').is_some() {
328 break;
330 }
331 }
332 } else if input.peeking_next(|&c| c == '/').is_some() {
333 input.peeking_take_while(|&c| c != '\n').count();
337 } else {
338 output(c);
339 }
340 }
341 '\\' => {
342 if input.peeking_next(|&c| c == '\\').is_some() {
343 output('\\');
345 } else if input.peeking_next(|&c| c == '\n').is_some() {
346 input.next();
348 } else if input.peeking_next(|&c| c == '\r').is_some() {
349 input.next();
351 if input.peeking_next(|&c| c == '\n').is_some() {
352 input.next();
353 }
354 } else {
355 output('\\');
356 }
357 }
358 _ => {
359 let mut prev = Some(c);
361 output(c);
362
363 if is_terminal_char(c) {
364 return Ok(());
365 }
366
367 while let Some(c) = input.peeking_next(|&c| !is_terminal_char(c)) {
368 if prev != Some('\\') && (c.is_whitespace() || is_terminal_char(c)) {
369 break;
371 }
372
373 output(c);
374 prev = Some(c);
375 }
376 return Ok(());
377 }
378 }
379 } else {
380 return Ok(());
381 }
382 }
383 }
384}
385
386#[test]
387fn test_tokenize_expression() {
388 let expr = r#"
389 (AA & B)|C1+D1*E1+!f+~g
390 "#;
391
392 let mut tk = tokenize(expr.chars(), LibertyBooleanExpLexer {});
393
394 let token_stream = tk.map(|token| {
396 let s: String = token.iter().collect();
397 s
398 });
399
400 let tokens: Vec<_> = token_stream.collect();
401
402 assert_eq!(
403 tokens,
404 vec![
405 "(", "AA", "&", "B", ")", "|", "C1", "+", "D1", "*", "E1", "+", "!", "f", "+", "~", "g"
406 ]
407 );
408}
409
410pub fn parse_boolean_function(
413 chars: impl Iterator<Item = char>,
414) -> Result<(BooleanExpr<usize>, Vec<String>), ParserError<char>> {
415 let mut tk = tokenize(chars.peekable(), LibertyBooleanExpLexer {});
416
417 let mut var_names: Vec<String> = vec![];
418
419 tk.advance(); let expr = parse_or_expr(&mut tk, &mut var_names)?;
422
423 Ok((expr, var_names))
424}
425
426fn parse_or_expr<I: Iterator<Item = char> + PeekingNext>(
427 tk: &mut Tokenized<I, LibertyBooleanExpLexer>,
428 names: &mut Vec<String>,
429) -> Result<BooleanExpr<usize>, ParserError<char>> {
430 let mut or_expr = parse_and_expr(tk, names)?;
431 while tk.current_token_ref().is_some() {
432 if tk.test_str("+")? || tk.test_str("|")? {
433 let rhs = parse_and_expr(tk, names)?;
434 or_expr = BooleanExpr::Or(Box::new(or_expr), Box::new(rhs));
435 } else {
436 break;
437 }
438 }
439
440 Ok(or_expr)
441}
442
443fn parse_and_expr<I: Iterator<Item = char> + PeekingNext>(
444 tk: &mut Tokenized<I, LibertyBooleanExpLexer>,
445 names: &mut Vec<String>,
446) -> Result<BooleanExpr<usize>, ParserError<char>> {
447 let mut and_expr = parse_xor_expr(tk, names)?;
448
449 while let Some(t) = tk.current_token_ref() {
450 let t0 = t[0];
451 if tk.test_str("&")? || tk.test_str("*")? || !"^|+)".contains(t0) {
453 let rhs = parse_xor_expr(tk, names)?;
454 and_expr = BooleanExpr::And(Box::new(and_expr), Box::new(rhs));
455 } else {
456 break;
457 }
458 }
459 Ok(and_expr)
460}
461
462fn parse_xor_expr<I: Iterator<Item = char> + PeekingNext>(
463 tk: &mut Tokenized<I, LibertyBooleanExpLexer>,
464 names: &mut Vec<String>,
465) -> Result<BooleanExpr<usize>, ParserError<char>> {
466 let mut xor_expr = parse_atom(tk, names)?;
467 while tk.current_token_ref().is_some() {
468 if tk.test_str("^")? {
469 let rhs = parse_atom(tk, names)?;
470 xor_expr = BooleanExpr::Xor(Box::new(xor_expr), Box::new(rhs));
471 } else {
472 break;
473 }
474 }
475 Ok(xor_expr)
476}
477
478fn parse_atom<I: Iterator<Item = char> + PeekingNext>(
479 tk: &mut Tokenized<I, LibertyBooleanExpLexer>,
480 names: &mut Vec<String>,
481) -> Result<BooleanExpr<usize>, ParserError<char>> {
482 let mut atom = if tk.test_str("0")? {
483 BooleanExpr::False
484 } else if tk.test_str("1")? {
485 BooleanExpr::True
486 } else if tk.test_str("!")? || tk.test_str("~")? {
487 let atom = parse_atom(tk, names)?;
488 BooleanExpr::Neg(Box::new(atom))
489 } else if tk.test_str("(")? {
490 let or_expr = parse_or_expr(tk, names)?;
491 tk.expect_str(")")?;
492 or_expr
493 } else {
494 let var_name = tk.take_str()?;
496
497 let var_id = {
498 let var_id = names
501 .iter()
502 .enumerate()
503 .find(|(idx, n)| n == &&var_name)
504 .map(|(idx, _)| idx);
505
506 var_id.unwrap_or_else(|| {
508 names.push(var_name);
509 names.len() - 1
510 })
511 };
512
513 BooleanExpr::Var(var_id)
514 };
515
516 while tk.current_token_ref().is_some() && tk.test_str("'")? {
517 atom = BooleanExpr::Neg(Box::new(atom))
518 }
519 Ok(atom)
520}
521
522#[test]
523fn test_parse_boolean_expression() {
524 fn v(var_id: usize) -> BooleanExpr<usize> {
525 BooleanExpr::Var(var_id)
526 }
527
528 let testcases = [
529 ("0", BooleanExpr::False),
531 ("1", BooleanExpr::True),
532 ("A", v(0)),
533 ("(A)", v(0)),
534 ("\t (\nA ) ", v(0)),
535 ("!A", !v(0)),
536 ("!!A", !!v(0)),
537 ("~A", !v(0)),
538 ("A'", !v(0)),
539 ("A''", !!v(0)),
540 ("A B", v(0) & v(1)),
541 ("x x", v(0) & v(0)),
542 ("(A)(B)", v(0) & v(1)),
543 ("A(B)", v(0) & v(1)),
544 ("(A)B", v(0) & v(1)),
545 ("A&B", v(0) & v(1)),
546 ("A*B", v(0) & v(1)),
547 ("A+B", v(0) | v(1)),
548 ("A|B", v(0) | v(1)),
549 ("(A)|(B)", v(0) | v(1)),
550 ("A B C", v(0) & v(1) & v(2)),
551 ("A (B C)", v(0) & (v(1) & v(2))),
552 ("A | B C", v(0) | v(1) & v(2)),
553 ("A B | C", v(0) & v(1) | v(2)),
554 ("!A B' | ~C", !v(0) & !v(1) | !v(2)),
555 (
556 "(A' & ~B | !C & D & (E|A)')",
557 !v(0) & !v(1) | !v(2) & v(3) & !(v(4) | v(0)),
558 ),
559 ];
560
561 for (expr, expected) in testcases {
562 let result = parse_boolean_function(expr.chars().peekable());
563
564 let (parsed, name_map) = result.unwrap();
565
566 assert_eq!(parsed, expected)
567 }
568}
569
570#[test]
571fn test_substitute_variables() {
572 let a = BooleanExpr::Var(0);
574 let b = BooleanExpr::Var(1);
575 let c = BooleanExpr::Var(2);
576 let expr = a & (b | c);
577
578 let substitution = |var: &usize| -> String {
580 match var {
581 0 => "a".into(),
582 1 => "b".into(),
583 2 => "c".into(),
584 _ => panic!("no such variable"),
585 }
586 };
587
588 let substituted = expr.substitute_variable_names(&substitution);
590
591 let a = BooleanExpr::Var("a".into());
593 let b = BooleanExpr::Var("b".into());
594 let c = BooleanExpr::Var("c".into());
595 let reference_expr = a & (b | c);
596
597 assert_eq!(reference_expr, substituted);
599}