expressions/
expressions.rs

1use orx_imp_vec::*;
2use std::{
3    fmt::Display,
4    ops::{Add, Sub},
5};
6
7/// A scope for expressions.
8#[derive(Default)]
9struct Scope<'a> {
10    expressions: ImpVec<Expr<'a>>,
11}
12
13impl<'a> Scope<'a> {
14    /// Bottom of the expressions recursion, the symbol primitive
15    fn symbol(&'a self, name: &'static str) -> ExprInScope<'a> {
16        let expr = Expr::Symbol(name);
17        self.expressions.imp_push(expr);
18        ExprInScope {
19            scope: self,
20            expr: &self.expressions[self.expressions.len() - 1],
21        }
22    }
23}
24
25/// A recursive expression with three demo variants
26enum Expr<'a> {
27    Symbol(&'static str),
28    Addition(&'a Expr<'a>, &'a Expr<'a>),
29    Subtraction(&'a Expr<'a>, &'a Expr<'a>),
30}
31
32impl<'a> Display for Expr<'a> {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        match self {
35            Expr::Symbol(x) => write!(f, "{}", x),
36            Expr::Addition(x, y) => write!(f, "{} + {}", x, y),
37            Expr::Subtraction(x, y) => write!(f, "{} - {}", x, y),
38        }
39    }
40}
41
42/// Expression in a scope:
43/// * it knows what it is
44/// * it knows which scope it belongs to
45///
46/// It can implement Copy which turns out to be extremely important!
47#[derive(Clone, Copy)]
48struct ExprInScope<'a> {
49    scope: &'a Scope<'a>,
50    expr: &'a Expr<'a>,
51}
52
53impl<'a> ExprInScope<'a> {
54    /// Recall, it knows the scope it belongs to,
55    /// and can check it in O(1)
56    fn belongs_to_same_scope(&self, other: Self) -> bool {
57        let self_scope = self.scope as *const Scope;
58        let other_scope = other.scope as *const Scope;
59        self_scope == other_scope
60    }
61}
62impl<'a> Display for ExprInScope<'a> {
63    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64        write!(f, "{}", self.expr)
65    }
66}
67
68impl<'a> Add for ExprInScope<'a> {
69    type Output = ExprInScope<'a>;
70
71    /// We can create an expression by adding two expressions
72    ///
73    /// Where do we store the new expression?
74    ///
75    /// Of course, in the scope that both expressions belong to.
76    /// And we can do so by `imp_push`.
77    ///
78    /// # Panics
79    ///
80    /// Panics if the lhs & rhs do not belong to the same scope.
81    fn add(self, rhs: Self) -> Self::Output {
82        assert!(self.belongs_to_same_scope(rhs));
83        let expressions = &self.scope.expressions;
84        let expr = Expr::Addition(self.expr, rhs.expr);
85        expressions.imp_push(expr);
86        ExprInScope {
87            scope: self.scope,
88            expr: &expressions[expressions.len() - 1],
89        }
90    }
91}
92
93impl<'a> Sub for ExprInScope<'a> {
94    type Output = ExprInScope<'a>;
95
96    /// Similarly, we can create an expression by subtracting two expressions
97    ///
98    /// Where do we store the new expression?
99    ///
100    /// Of course, in the scope that both expressions belong to.
101    /// And we can do so by `imp_push`.
102    ///
103    /// # Panics
104    ///
105    /// Panics if the lhs & rhs do not belong to the same scope.
106    fn sub(self, rhs: Self) -> Self::Output {
107        assert!(self.belongs_to_same_scope(rhs));
108        let expressions = &self.scope.expressions;
109        let expr = Expr::Subtraction(self.expr, rhs.expr);
110        expressions.imp_push(expr);
111        ExprInScope {
112            scope: self.scope,
113            expr: &expressions[expressions.len() - 1],
114        }
115    }
116}
117
118fn main() {
119    let scope = Scope::default();
120
121    // instantiate some symbols
122    let x = scope.symbol("x");
123    let y = scope.symbol("y");
124    assert_eq!(&x.to_string(), "x");
125    assert_eq!(&y.to_string(), "y");
126
127    // apply binary operations to create new symbols
128    let p = x + y;
129    assert_eq!(&p.to_string(), "x + y");
130
131    let q = x - y;
132    assert_eq!(&q.to_string(), "x - y");
133
134    // and further binary operations
135    let t = p + q;
136    assert_eq!(&t.to_string(), "x + y + x - y");
137
138    // we only use 'scope' to create symbols
139    // but in the background, all expressions are collected in our scope
140    let all_expressions: Vec<_> = scope.expressions.iter().map(|x| x.to_string()).collect();
141    assert_eq!(
142        all_expressions,
143        ["x", "y", "x + y", "x - y", "x + y + x - y"]
144    );
145}