Skip to main content

ternary_grammar/
lib.rs

1//! # ternary-grammar
2//!
3//! Context-free grammar for generating and parsing valid ternary strategy expressions.
4//!
5//! Provides:
6//! - `Grammar` — CFG definition and production rules
7//! - `TernaryExpr` — AST for ternary strategy expressions
8//! - `Parser` — Recursive descent parser for ternary expressions
9//! - `Generator` — Generate valid ternary expressions from grammar
10//! - `Evaluator` — Evaluate ternary expressions
11
12use std::collections::HashMap;
13use std::fmt;
14
15/// Ternary value
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17pub enum Trit {
18    Neg,
19    Zero,
20    Pos,
21}
22
23impl Trit {
24    pub fn to_i8(self) -> i8 {
25        match self {
26            Trit::Neg => -1,
27            Trit::Zero => 0,
28            Trit::Pos => 1,
29        }
30    }
31
32    pub fn from_i8(v: i8) -> Option<Self> {
33        match v {
34            -1 => Some(Trit::Neg),
35            0 => Some(Trit::Zero),
36            1 => Some(Trit::Pos),
37            _ => None,
38        }
39    }
40}
41
42/// Strategy operator for combining ternary values
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub enum Op {
45    Add,       // a + b (clamped)
46    Multiply,  // a * b
47    Max,       // max(a, b)
48    Min,       // min(a, b)
49    Negate,    // -a
50    Compose,   // sequential composition
51}
52
53impl fmt::Display for Op {
54    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
55        match self {
56            Op::Add => write!(f, "+"),
57            Op::Multiply => write!(f, "*"),
58            Op::Max => write!(f, "max"),
59            Op::Min => write!(f, "min"),
60            Op::Negate => write!(f, "neg"),
61            Op::Compose => write!(f, ">>"),
62        }
63    }
64}
65
66/// AST node for ternary strategy expressions
67#[derive(Debug, Clone, PartialEq)]
68pub enum TernaryExpr {
69    /// Literal ternary value
70    Literal(Trit),
71    /// Variable reference (e.g., "x", "y")
72    Var(String),
73    /// Unary operation
74    Unary(Op, Box<TernaryExpr>),
75    /// Binary operation
76    Binary(Op, Box<TernaryExpr>, Box<TernaryExpr>),
77    /// Conditional: if guard then_expr else_expr
78    If(Box<TernaryExpr>, Box<TernaryExpr>, Box<TernaryExpr>),
79    /// Sequence of expressions
80    Seq(Vec<TernaryExpr>),
81}
82
83impl fmt::Display for TernaryExpr {
84    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
85        match self {
86            TernaryExpr::Literal(t) => write!(f, "{}", t.to_i8()),
87            TernaryExpr::Var(name) => write!(f, "{}", name),
88            TernaryExpr::Unary(op, expr) => write!(f, "({} {})", op, expr),
89            TernaryExpr::Binary(op, l, r) => write!(f, "({} {} {})", l, op, r),
90            TernaryExpr::If(guard, then_e, else_e) => write!(f, "(if {} {} {})", guard, then_e, else_e),
91            TernaryExpr::Seq(exprs) => {
92                write!(f, "(seq")?;
93                for e in exprs {
94                    write!(f, " {}", e)?;
95                }
96                write!(f, ")")
97            }
98        }
99    }
100}
101
102/// A grammar production rule
103#[derive(Debug, Clone)]
104pub struct Production {
105    pub name: String,
106    pub alternatives: Vec<String>,
107}
108
109/// Context-free grammar for ternary expressions
110#[derive(Debug, Clone)]
111pub struct Grammar {
112    productions: Vec<Production>,
113    terminals: Vec<String>,
114}
115
116impl Grammar {
117    pub fn new() -> Self {
118        let productions = vec![
119            Production {
120                name: "expr".to_string(),
121                alternatives: vec![
122                    "literal".to_string(),
123                    "variable".to_string(),
124                    "unary".to_string(),
125                    "binary".to_string(),
126                    "conditional".to_string(),
127                    "sequence".to_string(),
128                ],
129            },
130            Production {
131                name: "literal".to_string(),
132                alternatives: vec!["NEG".to_string(), "ZERO".to_string(), "POS".to_string()],
133            },
134            Production {
135                name: "variable".to_string(),
136                alternatives: vec!["x".to_string(), "y".to_string(), "z".to_string()],
137            },
138            Production {
139                name: "unary".to_string(),
140                alternatives: vec!["NEGATE expr".to_string()],
141            },
142            Production {
143                name: "binary".to_string(),
144                alternatives: vec![
145                    "expr ADD expr".to_string(),
146                    "expr MUL expr".to_string(),
147                    "expr MAX expr".to_string(),
148                    "expr MIN expr".to_string(),
149                    "expr COMP expr".to_string(),
150                ],
151            },
152            Production {
153                name: "conditional".to_string(),
154                alternatives: vec!["IF expr expr expr".to_string()],
155            },
156            Production {
157                name: "sequence".to_string(),
158                alternatives: vec!["SEQ expr_list".to_string()],
159            },
160            Production {
161                name: "expr_list".to_string(),
162                alternatives: vec!["expr".to_string(), "expr expr_list".to_string()],
163            },
164        ];
165
166        let terminals = vec![
167            "NEG".to_string(), "ZERO".to_string(), "POS".to_string(),
168            "ADD".to_string(), "MUL".to_string(), "MAX".to_string(), "MIN".to_string(),
169            "NEGATE".to_string(), "COMP".to_string(),
170            "IF".to_string(), "SEQ".to_string(),
171            "x".to_string(), "y".to_string(), "z".to_string(),
172        ];
173
174        Grammar { productions, terminals }
175    }
176
177    pub fn productions(&self) -> &[Production] {
178        &self.productions
179    }
180
181    pub fn terminals(&self) -> &[String] {
182        &self.terminals
183    }
184
185    pub fn is_terminal(&self, symbol: &str) -> bool {
186        self.terminals.iter().any(|t| t == symbol)
187    }
188
189    pub fn get_production(&self, name: &str) -> Option<&Production> {
190        self.productions.iter().find(|p| p.name == name)
191    }
192}
193
194/// Token for parsing
195#[derive(Debug, Clone, PartialEq)]
196pub enum Token {
197    Literal(Trit),
198    Var(String),
199    Op(Op),
200    If,
201    Seq,
202    LParen,
203    RParen,
204}
205
206/// Tokenizer for ternary expressions
207pub struct Tokenizer;
208
209impl Tokenizer {
210    pub fn tokenize(input: &str) -> Result<Vec<Token>, String> {
211        let mut tokens = Vec::new();
212        let mut chars = input.chars().peekable();
213
214        while let Some(&ch) = chars.peek() {
215            match ch {
216                ' ' | '\t' | '\n' | '\r' => { chars.next(); }
217                '(' => { tokens.push(Token::LParen); chars.next(); }
218                ')' => { tokens.push(Token::RParen); chars.next(); }
219                '-' => {
220                    chars.next();
221                    if chars.peek() == Some(&'1') {
222                        chars.next();
223                        tokens.push(Token::Literal(Trit::Neg));
224                    } else {
225                        tokens.push(Token::Op(Op::Negate));
226                    }
227                }
228                '0' => { chars.next(); tokens.push(Token::Literal(Trit::Zero)); }
229                '1' => { chars.next(); tokens.push(Token::Literal(Trit::Pos)); }
230                '+' => { chars.next(); tokens.push(Token::Op(Op::Add)); }
231                '*' => { chars.next(); tokens.push(Token::Op(Op::Multiply)); }
232                '>' => {
233                    chars.next();
234                    if chars.peek() == Some(&'>') {
235                        chars.next();
236                        tokens.push(Token::Op(Op::Compose));
237                    }
238                }
239                _ if ch.is_alphabetic() => {
240                    let mut word = String::new();
241                    while let Some(&c) = chars.peek() {
242                        if c.is_alphanumeric() || c == '_' {
243                            word.push(c);
244                            chars.next();
245                        } else {
246                            break;
247                        }
248                    }
249                    match word.as_str() {
250                        "neg" | "NEG" => tokens.push(Token::Op(Op::Negate)),
251                        "max" | "MAX" => tokens.push(Token::Op(Op::Max)),
252                        "min" | "MIN" => tokens.push(Token::Op(Op::Min)),
253                        "if" | "IF" => tokens.push(Token::If),
254                        "seq" | "SEQ" => tokens.push(Token::Seq),
255                        _ => tokens.push(Token::Var(word)),
256                    }
257                }
258                _ => return Err(format!("Unexpected character: {}", ch)),
259            }
260        }
261        Ok(tokens)
262    }
263}
264
265/// Recursive descent parser
266pub struct Parser {
267    pos: usize,
268    tokens: Vec<Token>,
269}
270
271impl Parser {
272    pub fn new(tokens: Vec<Token>) -> Self {
273        Parser { pos: 0, tokens }
274    }
275
276    pub fn parse(input: &str) -> Result<TernaryExpr, String> {
277        let tokens = Tokenizer::tokenize(input)?;
278        let mut parser = Parser::new(tokens);
279        let expr = parser.parse_expr()?;
280        if parser.pos < parser.tokens.len() {
281            return Err("Unexpected tokens after expression".to_string());
282        }
283        Ok(expr)
284    }
285
286    fn peek(&self) -> Option<&Token> {
287        self.tokens.get(self.pos)
288    }
289
290    fn advance(&mut self) -> Option<Token> {
291        if self.pos < self.tokens.len() {
292            let tok = self.tokens[self.pos].clone();
293            self.pos += 1;
294            Some(tok)
295        } else {
296            None
297        }
298    }
299
300    fn parse_expr(&mut self) -> Result<TernaryExpr, String> {
301        // Check for parenthesized expression
302        if self.peek() == Some(&Token::LParen) {
303            self.advance(); // consume '('
304            let expr = self.parse_inner()?;
305            if self.peek() == Some(&Token::RParen) {
306                self.advance(); // consume ')'
307            }
308            return Ok(expr);
309        }
310
311        // Simple atom
312        self.parse_atom()
313    }
314
315    fn parse_inner(&mut self) -> Result<TernaryExpr, String> {
316        // Check for 'if' keyword
317        if self.peek() == Some(&Token::If) {
318            self.advance();
319            let guard = self.parse_expr()?;
320            let then_expr = self.parse_expr()?;
321            let else_expr = self.parse_expr()?;
322            return Ok(TernaryExpr::If(
323                Box::new(guard),
324                Box::new(then_expr),
325                Box::new(else_expr),
326            ));
327        }
328
329        // Check for 'seq'
330        if self.peek() == Some(&Token::Seq) {
331            self.advance();
332            let mut exprs = Vec::new();
333            while self.peek().is_some() && self.peek() != Some(&Token::RParen) {
334                exprs.push(self.parse_expr()?);
335            }
336            return Ok(TernaryExpr::Seq(exprs));
337        }
338
339        // Parse first operand
340        let left = self.parse_expr()?;
341
342        // Check for binary operator
343        if let Some(Token::Op(op)) = self.peek() {
344            if matches!(op, Op::Add | Op::Multiply | Op::Max | Op::Min | Op::Compose) {
345                let op = *op;
346                self.advance();
347                let right = self.parse_expr()?;
348                return Ok(TernaryExpr::Binary(op, Box::new(left), Box::new(right)));
349            }
350        }
351
352        Ok(left)
353    }
354
355    fn parse_atom(&mut self) -> Result<TernaryExpr, String> {
356        match self.advance() {
357            Some(Token::Literal(t)) => Ok(TernaryExpr::Literal(t)),
358            Some(Token::Var(name)) => Ok(TernaryExpr::Var(name)),
359            Some(Token::Op(Op::Negate)) => {
360                let expr = self.parse_expr()?;
361                Ok(TernaryExpr::Unary(Op::Negate, Box::new(expr)))
362            }
363            Some(Token::LParen) => {
364                let expr = self.parse_inner()?;
365                if self.peek() == Some(&Token::RParen) {
366                    self.advance();
367                }
368                Ok(expr)
369            }
370            other => Err(format!("Unexpected token: {:?}", other)),
371        }
372    }
373}
374
375/// Expression generator
376pub struct Generator {
377    max_depth: usize,
378    counter: usize,
379}
380
381impl Generator {
382    pub fn new(max_depth: usize) -> Self {
383        Generator { max_depth, counter: 0 }
384    }
385
386    /// Generate a random-ish expression
387    pub fn generate(&mut self) -> TernaryExpr {
388        self.gen_expr(self.max_depth)
389    }
390
391    fn gen_expr(&mut self, depth: usize) -> TernaryExpr {
392        if depth == 0 {
393            return self.gen_literal();
394        }
395
396        self.counter = self.counter.wrapping_add(1);
397        let choice = self.counter % 4;
398
399        match choice {
400            0 => self.gen_literal(),
401            1 => self.gen_var(),
402            2 => self.gen_binary(depth - 1),
403            3 => self.gen_unary(depth - 1),
404            _ => self.gen_literal(),
405        }
406    }
407
408    fn gen_literal(&mut self) -> TernaryExpr {
409        self.counter = self.counter.wrapping_add(1);
410        let trit = match self.counter % 3 {
411            0 => Trit::Neg,
412            1 => Trit::Zero,
413            _ => Trit::Pos,
414        };
415        TernaryExpr::Literal(trit)
416    }
417
418    fn gen_var(&mut self) -> TernaryExpr {
419        self.counter = self.counter.wrapping_add(1);
420        let name = match self.counter % 3 {
421            0 => "x".to_string(),
422            1 => "y".to_string(),
423            _ => "z".to_string(),
424        };
425        TernaryExpr::Var(name)
426    }
427
428    fn gen_binary(&mut self, depth: usize) -> TernaryExpr {
429        self.counter = self.counter.wrapping_add(1);
430        let op = match self.counter % 5 {
431            0 => Op::Add,
432            1 => Op::Multiply,
433            2 => Op::Max,
434            3 => Op::Min,
435            _ => Op::Compose,
436        };
437        let left = self.gen_expr(depth);
438        let right = self.gen_expr(depth);
439        TernaryExpr::Binary(op, Box::new(left), Box::new(right))
440    }
441
442    fn gen_unary(&mut self, depth: usize) -> TernaryExpr {
443        let expr = self.gen_expr(depth);
444        TernaryExpr::Unary(Op::Negate, Box::new(expr))
445    }
446
447    /// Generate an if expression
448    pub fn generate_if(&mut self) -> TernaryExpr {
449        let guard = self.gen_expr(self.max_depth.saturating_sub(1));
450        let then_expr = self.gen_literal();
451        let else_expr = self.gen_literal();
452        TernaryExpr::If(Box::new(guard), Box::new(then_expr), Box::new(else_expr))
453    }
454
455    /// Generate a sequence
456    pub fn generate_seq(&mut self, len: usize) -> TernaryExpr {
457        let exprs: Vec<TernaryExpr> = (0..len).map(|_| self.gen_expr(self.max_depth.saturating_sub(1))).collect();
458        TernaryExpr::Seq(exprs)
459    }
460}
461
462/// Evaluate ternary expressions
463pub struct Evaluator;
464
465impl Evaluator {
466    /// Evaluate an expression with given variable bindings
467    pub fn eval(expr: &TernaryExpr, vars: &HashMap<String, Trit>) -> Result<Trit, String> {
468        match expr {
469            TernaryExpr::Literal(t) => Ok(*t),
470            TernaryExpr::Var(name) => {
471                vars.get(name).copied().ok_or_else(|| format!("Undefined variable: {}", name))
472            }
473            TernaryExpr::Unary(Op::Negate, inner) => {
474                let v = Self::eval(inner, vars)?.to_i8();
475                Trit::from_i8(-v).ok_or("Negation failed".to_string())
476            }
477            TernaryExpr::Unary(_, inner) => Self::eval(inner, vars),
478            TernaryExpr::Binary(op, left, right) => {
479                let lv = Self::eval(left, vars)?.to_i8();
480                let rv = Self::eval(right, vars)?.to_i8();
481                let result = match op {
482                    Op::Add => (lv + rv).clamp(-1, 1),
483                    Op::Multiply => (lv * rv).clamp(-1, 1),
484                    Op::Max => lv.max(rv),
485                    Op::Min => lv.min(rv),
486                    Op::Compose => rv, // Right-biased composition
487                    _ => 0,
488                };
489                Ok(Trit::from_i8(result).unwrap_or(Trit::Zero))
490            }
491            TernaryExpr::If(guard, then_expr, else_expr) => {
492                let g = Self::eval(guard, vars)?;
493                if g == Trit::Pos {
494                    Self::eval(then_expr, vars)
495                } else {
496                    Self::eval(else_expr, vars)
497                }
498            }
499            TernaryExpr::Seq(exprs) => {
500                let mut last = Trit::Zero;
501                for e in exprs {
502                    last = Self::eval(e, vars)?;
503                }
504                Ok(last)
505            }
506        }
507    }
508
509    /// Simplify an expression by evaluating constant sub-expressions
510    pub fn simplify(expr: &TernaryExpr) -> TernaryExpr {
511        match expr {
512            TernaryExpr::Literal(t) => TernaryExpr::Literal(*t),
513            TernaryExpr::Var(name) => TernaryExpr::Var(name.clone()),
514            TernaryExpr::Unary(op, inner) => {
515                let simplified = Self::simplify(inner);
516                if let TernaryExpr::Literal(t) = simplified {
517                    // Can evaluate
518                    let v = t.to_i8();
519                    if *op == Op::Negate {
520                        TernaryExpr::Literal(Trit::from_i8(-v).unwrap_or(Trit::Zero))
521                    } else {
522                        TernaryExpr::Literal(t)
523                    }
524                } else {
525                    TernaryExpr::Unary(*op, Box::new(simplified))
526                }
527            }
528            TernaryExpr::Binary(op, left, right) => {
529                let sl = Self::simplify(left);
530                let sr = Self::simplify(right);
531                if let (TernaryExpr::Literal(l), TernaryExpr::Literal(r)) = (&sl, &sr) {
532                    let lv = l.to_i8();
533                    let rv = r.to_i8();
534                    let result = match op {
535                        Op::Add => (lv + rv).clamp(-1, 1),
536                        Op::Multiply => (lv * rv).clamp(-1, 1),
537                        Op::Max => lv.max(rv),
538                        Op::Min => lv.min(rv),
539                        Op::Compose => rv,
540                        _ => 0,
541                    };
542                    TernaryExpr::Literal(Trit::from_i8(result).unwrap_or(Trit::Zero))
543                } else {
544                    TernaryExpr::Binary(*op, Box::new(sl), Box::new(sr))
545                }
546            }
547            TernaryExpr::If(guard, then_expr, else_expr) => {
548                let sg = Self::simplify(guard);
549                if let TernaryExpr::Literal(t) = sg {
550                    if t == Trit::Pos {
551                        Self::simplify(then_expr)
552                    } else {
553                        Self::simplify(else_expr)
554                    }
555                } else {
556                    TernaryExpr::If(
557                        Box::new(sg),
558                        Box::new(Self::simplify(then_expr)),
559                        Box::new(Self::simplify(else_expr)),
560                    )
561                }
562            }
563            TernaryExpr::Seq(exprs) => {
564                let simplified: Vec<TernaryExpr> = exprs.iter().map(Self::simplify).collect();
565                TernaryExpr::Seq(simplified)
566            }
567        }
568    }
569}
570
571#[cfg(test)]
572mod tests {
573    use super::*;
574
575    #[test]
576    fn test_trit_values() {
577        assert_eq!(Trit::Neg.to_i8(), -1);
578        assert_eq!(Trit::Zero.to_i8(), 0);
579        assert_eq!(Trit::Pos.to_i8(), 1);
580    }
581
582    #[test]
583    fn test_expr_display_literal() {
584        let expr = TernaryExpr::Literal(Trit::Pos);
585        assert_eq!(format!("{}", expr), "1");
586    }
587
588    #[test]
589    fn test_expr_display_binary() {
590        let expr = TernaryExpr::Binary(
591            Op::Add,
592            Box::new(TernaryExpr::Literal(Trit::Pos)),
593            Box::new(TernaryExpr::Literal(Trit::Neg)),
594        );
595        assert_eq!(format!("{}", expr), "(1 + -1)");
596    }
597
598    #[test]
599    fn test_expr_display_unary() {
600        let expr = TernaryExpr::Unary(Op::Negate, Box::new(TernaryExpr::Literal(Trit::Pos)));
601        assert_eq!(format!("{}", expr), "(neg 1)");
602    }
603
604    #[test]
605    fn test_expr_display_var() {
606        let expr = TernaryExpr::Var("x".to_string());
607        assert_eq!(format!("{}", expr), "x");
608    }
609
610    #[test]
611    fn test_grammar_new() {
612        let g = Grammar::new();
613        assert!(!g.productions().is_empty());
614        assert!(!g.terminals().is_empty());
615    }
616
617    #[test]
618    fn test_grammar_is_terminal() {
619        let g = Grammar::new();
620        assert!(g.is_terminal("NEG"));
621        assert!(g.is_terminal("ADD"));
622        assert!(!g.is_terminal("expr"));
623    }
624
625    #[test]
626    fn test_grammar_get_production() {
627        let g = Grammar::new();
628        let prod = g.get_production("expr").unwrap();
629        assert_eq!(prod.name, "expr");
630        assert!(prod.alternatives.len() > 1);
631    }
632
633    #[test]
634    fn test_tokenizer_literals() {
635        let tokens = Tokenizer::tokenize("-1 0 1").unwrap();
636        assert_eq!(tokens.len(), 3);
637        assert_eq!(tokens[0], Token::Literal(Trit::Neg));
638        assert_eq!(tokens[1], Token::Literal(Trit::Zero));
639        assert_eq!(tokens[2], Token::Literal(Trit::Pos));
640    }
641
642    #[test]
643    fn test_tokenizer_operators() {
644        let tokens = Tokenizer::tokenize("+ * >>").unwrap();
645        assert_eq!(tokens.len(), 3);
646        assert_eq!(tokens[0], Token::Op(Op::Add));
647        assert_eq!(tokens[1], Token::Op(Op::Multiply));
648        assert_eq!(tokens[2], Token::Op(Op::Compose));
649    }
650
651    #[test]
652    fn test_tokenizer_keywords() {
653        let tokens = Tokenizer::tokenize("neg max min if seq").unwrap();
654        assert_eq!(tokens.len(), 5);
655        assert_eq!(tokens[0], Token::Op(Op::Negate));
656        assert_eq!(tokens[1], Token::Op(Op::Max));
657        assert_eq!(tokens[3], Token::If);
658        assert_eq!(tokens[4], Token::Seq);
659    }
660
661    #[test]
662    fn test_tokenizer_vars() {
663        let tokens = Tokenizer::tokenize("x y z").unwrap();
664        assert_eq!(tokens.len(), 3);
665        assert_eq!(tokens[0], Token::Var("x".to_string()));
666    }
667
668    #[test]
669    fn test_parser_literal() {
670        let expr = Parser::parse("1").unwrap();
671        assert_eq!(expr, TernaryExpr::Literal(Trit::Pos));
672    }
673
674    #[test]
675    fn test_parser_binary() {
676        let expr = Parser::parse("(1 + -1)").unwrap();
677        assert_eq!(expr, TernaryExpr::Binary(
678            Op::Add,
679            Box::new(TernaryExpr::Literal(Trit::Pos)),
680            Box::new(TernaryExpr::Literal(Trit::Neg)),
681        ));
682    }
683
684    #[test]
685    fn test_parser_unary() {
686        let expr = Parser::parse("neg 1").unwrap();
687        assert_eq!(expr, TernaryExpr::Unary(Op::Negate, Box::new(TernaryExpr::Literal(Trit::Pos))));
688    }
689
690    #[test]
691    fn test_parser_nested() {
692        let expr = Parser::parse("((1 + -1) * 0)").unwrap();
693        assert!(matches!(expr, TernaryExpr::Binary(Op::Multiply, _, _)));
694    }
695
696    #[test]
697    fn test_evaluator_literal() {
698        let expr = TernaryExpr::Literal(Trit::Pos);
699        let result = Evaluator::eval(&expr, &HashMap::new()).unwrap();
700        assert_eq!(result, Trit::Pos);
701    }
702
703    #[test]
704    fn test_evaluator_var() {
705        let expr = TernaryExpr::Var("x".to_string());
706        let mut vars = HashMap::new();
707        vars.insert("x".to_string(), Trit::Neg);
708        let result = Evaluator::eval(&expr, &vars).unwrap();
709        assert_eq!(result, Trit::Neg);
710    }
711
712    #[test]
713    fn test_evaluator_add() {
714        let expr = TernaryExpr::Binary(Op::Add, Box::new(TernaryExpr::Literal(Trit::Pos)), Box::new(TernaryExpr::Literal(Trit::Neg)));
715        let result = Evaluator::eval(&expr, &HashMap::new()).unwrap();
716        assert_eq!(result, Trit::Zero);
717    }
718
719    #[test]
720    fn test_evaluator_multiply() {
721        let expr = TernaryExpr::Binary(Op::Multiply, Box::new(TernaryExpr::Literal(Trit::Pos)), Box::new(TernaryExpr::Literal(Trit::Pos)));
722        let result = Evaluator::eval(&expr, &HashMap::new()).unwrap();
723        assert_eq!(result, Trit::Pos);
724    }
725
726    #[test]
727    fn test_evaluator_negate() {
728        let expr = TernaryExpr::Unary(Op::Negate, Box::new(TernaryExpr::Literal(Trit::Pos)));
729        let result = Evaluator::eval(&expr, &HashMap::new()).unwrap();
730        assert_eq!(result, Trit::Neg);
731    }
732
733    #[test]
734    fn test_evaluator_if() {
735        let expr = TernaryExpr::If(
736            Box::new(TernaryExpr::Literal(Trit::Pos)),
737            Box::new(TernaryExpr::Literal(Trit::Pos)),
738            Box::new(TernaryExpr::Literal(Trit::Neg)),
739        );
740        let result = Evaluator::eval(&expr, &HashMap::new()).unwrap();
741        assert_eq!(result, Trit::Pos);
742    }
743
744    #[test]
745    fn test_simplify_constants() {
746        let expr = TernaryExpr::Binary(Op::Add, Box::new(TernaryExpr::Literal(Trit::Pos)), Box::new(TernaryExpr::Literal(Trit::Neg)));
747        let simplified = Evaluator::simplify(&expr);
748        assert_eq!(simplified, TernaryExpr::Literal(Trit::Zero));
749    }
750}