tinylivecode/
lib.rs

1#![no_std]
2
3use micromath::F32Ext;
4
5const MAX_STACK_SIZE: usize = 64;
6
7pub enum ParseError {
8    TooManyValuesInStack,
9    NoValuesInStack,
10    NumberNotParsed,
11}
12
13struct Stack<T> {
14    stack: [Option<T>; MAX_STACK_SIZE],
15    stack_top: usize,
16}
17impl<T: Copy> Stack<T> {
18    fn new() -> Stack<T> {
19        Stack {
20            stack: [Option::None; MAX_STACK_SIZE],
21            stack_top: 0,
22        }
23    }
24
25    fn push(&mut self, val: T) -> Result<(), ParseError> {
26        if self.stack_top >= MAX_STACK_SIZE {
27            return Err(ParseError::TooManyValuesInStack);
28        }
29
30        self.stack[self.stack_top] = Some(val);
31        self.stack_top += 1;
32        Ok(())
33    }
34
35    fn pop(&mut self) -> Option<T> {
36        if self.stack_top == 0 {
37            return None;
38        }
39
40        // this will never be empty, i promise
41        self.stack_top -= 1;
42        let result = self.stack[self.stack_top].take();
43
44        result
45    }
46
47    fn iter<'a>(&'a self) -> StackIter<'a, T> {
48        StackIter {
49            curr_loc: 0,
50            end_loc: self.stack_top,
51            data: self,
52        }
53    }
54}
55
56struct StackIter<'a, T> {
57    curr_loc: usize,
58    end_loc: usize,
59    data: &'a Stack<T>,
60}
61
62impl<'a, T> StackIter<'a, T> {
63    fn is_empty(&self) -> bool {
64        self.curr_loc == self.end_loc
65    }
66}
67
68impl<'a, T: Copy> Iterator for StackIter<'a, T> {
69    type Item = T;
70
71    fn next(&mut self) -> Option<Self::Item> {
72        if self.is_empty() {
73            None
74        } else {
75            let result = self.data.stack[self.curr_loc]; // there is a case that _shouldn't_ happen where this is None
76            self.curr_loc += 1;
77            result
78        }
79    }
80}
81
82pub struct TinyExpr {
83    stack: Stack<ExprNode>,
84}
85
86impl TinyExpr {
87    pub fn from_str(s: &str) -> ParseResult<TinyExpr> {
88        let tokens = s.split_whitespace();
89
90        let mut stack = Stack::new();
91
92        for token in tokens {
93            let val = ExprNode::parse_token(token)?;
94            stack.push(val)?;
95        }
96
97        Ok(Self { stack })
98    }
99
100    pub fn eval(&self, x: f32, y: f32) -> ParseResult<f32> {
101        let mut eval_stack = Stack::new();
102
103        let mut stack_iterator = self.stack.iter();
104
105        while let Some(curr_val) = stack_iterator.next() {
106            // println!("curr_val {:?}", curr_val);
107            // println!("eval_stack {:?}", eval_stack.stack);
108            match curr_val {
109                ExprNode::Lerp => {
110                    let z: f32 = eval_stack
111                        .pop()
112                        .ok_or_else(|| ParseError::NoValuesInStack)?;
113                    let y: f32 = eval_stack
114                        .pop()
115                        .ok_or_else(|| ParseError::NoValuesInStack)?;
116                    let x = eval_stack
117                        .pop()
118                        .ok_or_else(|| ParseError::NoValuesInStack)?;
119                    eval_stack.push((1.0 - z) * x + z * y)?;
120                }
121                // binary
122                ExprNode::Add => {
123                    let y: f32 = eval_stack
124                        .pop()
125                        .ok_or_else(|| ParseError::NoValuesInStack)?;
126                    let x = eval_stack
127                        .pop()
128                        .ok_or_else(|| ParseError::NoValuesInStack)?;
129
130                    eval_stack.push(x + y)?;
131                }
132                ExprNode::Mul => {
133                    let y: f32 = eval_stack
134                        .pop()
135                        .ok_or_else(|| ParseError::NoValuesInStack)?;
136                    let x = eval_stack
137                        .pop()
138                        .ok_or_else(|| ParseError::NoValuesInStack)?;
139                    eval_stack.push(x * y)?;
140                }
141                ExprNode::Div => {
142                    let y: f32 = eval_stack
143                        .pop()
144                        .ok_or_else(|| ParseError::NoValuesInStack)?;
145                    let x = eval_stack
146                        .pop()
147                        .ok_or_else(|| ParseError::NoValuesInStack)?;
148                    eval_stack.push(x / y)?;
149                }
150                ExprNode::Sub => {
151                    let y: f32 = eval_stack
152                        .pop()
153                        .ok_or_else(|| ParseError::NoValuesInStack)?;
154                    let x = eval_stack
155                        .pop()
156                        .ok_or_else(|| ParseError::NoValuesInStack)?;
157                    eval_stack.push(x - y)?;
158                }
159                ExprNode::Pow => {
160                    let y: f32 = eval_stack
161                        .pop()
162                        .ok_or_else(|| ParseError::NoValuesInStack)?;
163                    let x = eval_stack
164                        .pop()
165                        .ok_or_else(|| ParseError::NoValuesInStack)?;
166                    eval_stack.push(x.powf(y))?;
167                }
168                ExprNode::Atan2 => {
169                    let y: f32 = eval_stack
170                        .pop()
171                        .ok_or_else(|| ParseError::NoValuesInStack)?;
172                    let x: f32 = eval_stack
173                        .pop()
174                        .ok_or_else(|| ParseError::NoValuesInStack)?;
175                    eval_stack.push(x.atan2(y))?;
176                }
177
178                // unary
179                ExprNode::Sin => {
180                    let x = eval_stack
181                        .pop()
182                        .ok_or_else(|| ParseError::NoValuesInStack)?;
183                    eval_stack.push(x.sin())?;
184                }
185                ExprNode::Cos => {
186                    let x = eval_stack
187                        .pop()
188                        .ok_or_else(|| ParseError::NoValuesInStack)?;
189                    eval_stack.push(x.cos())?;
190                }
191                ExprNode::Fract => {
192                    let x = eval_stack
193                        .pop()
194                        .ok_or_else(|| ParseError::NoValuesInStack)?;
195                    eval_stack.push(x.fract())?;
196                }
197                ExprNode::Sqrt => {
198                    let x = eval_stack
199                        .pop()
200                        .ok_or_else(|| ParseError::NoValuesInStack)?;
201                    eval_stack.push(x.fract())?;
202                }
203                ExprNode::Floor => {
204                    let x = eval_stack
205                        .pop()
206                        .ok_or_else(|| ParseError::NoValuesInStack)?;
207                    eval_stack.push(x.floor())?;
208                }
209                ExprNode::Abs => {
210                    let x = eval_stack
211                        .pop()
212                        .ok_or_else(|| ParseError::NoValuesInStack)?;
213                    eval_stack.push(x.abs())?;
214                }
215                // no variable
216                ExprNode::Number(r) => eval_stack.push(r)?,
217                ExprNode::X => eval_stack.push(x)?,
218                ExprNode::Y => eval_stack.push(y)?,
219            };
220        }
221
222        if let Some(x) = eval_stack.pop() {
223            Ok(x)
224        } else {
225            Err(ParseError::NoValuesInStack)
226        }
227    }
228}
229
230pub type ParseResult<T> = Result<T, ParseError>;
231
232#[derive(Copy, Clone, Debug)]
233enum ExprNode {
234    Number(f32),
235    X,
236    Y,
237    // unary
238    Sin,
239    Cos,
240    Fract,
241    Sqrt,
242    Floor,
243    Abs,
244    // binary
245    Atan2,
246    Pow,
247    Add,
248    Mul,
249    Div,
250    Sub,
251    // ternary
252    Lerp,
253}
254
255impl ExprNode {
256    fn parse_token(token: &str) -> ParseResult<ExprNode> {
257        match token {
258            "x" => Ok(ExprNode::X),
259            "y" => Ok(ExprNode::Y),
260            "sin" => Ok(ExprNode::Sin),
261            "cos" => Ok(ExprNode::Cos),
262            "atan2" => Ok(ExprNode::Atan2),
263            "fract" => Ok(ExprNode::Fract),
264            "pow" => Ok(ExprNode::Pow),
265            "sqrt" => Ok(ExprNode::Sqrt),
266            "floor" => Ok(ExprNode::Floor),
267            "abs" => Ok(ExprNode::Abs),
268            "add" => Ok(ExprNode::Add),
269            "mul" => Ok(ExprNode::Mul),
270            "div" => Ok(ExprNode::Div),
271            "sub" => Ok(ExprNode::Sub),
272            "lerp" => Ok(ExprNode::Lerp),
273            _ => {
274                let float = token
275                    .parse::<f32>()
276                    .map_err(|_| ParseError::NumberNotParsed)?;
277                Ok(ExprNode::Number(float))
278            }
279        }
280    }
281}
282
283#[cfg(test)]
284mod tests {
285    use super::*;
286
287    enum TestEvalResult {
288        Ok(f32),
289        ErrFromStr(ParseError),
290        ErrEval(ParseError),
291    }
292
293    impl TestEvalResult {
294        /// Returns `true` if the test eval result is [`Ok`].
295        ///
296        /// [`Ok`]: TestEvalResult::Ok
297        #[must_use]
298        fn is_ok(&self) -> bool {
299            matches!(self, Self::Ok(..))
300        }
301
302        /// Returns `true` if the test eval result is [`ErrFromStr`].
303        ///
304        /// [`ErrFromStr`]: TestEvalResult::ErrFromStr
305        #[must_use]
306        fn is_err_from_str(&self) -> bool {
307            matches!(self, Self::ErrFromStr(..))
308        }
309
310        /// Returns `true` if the test eval result is [`ErrEval`].
311        ///
312        /// [`ErrEval`]: TestEvalResult::ErrEval
313        #[must_use]
314        fn is_err_eval(&self) -> bool {
315            matches!(self, Self::ErrEval(..))
316        }
317    }
318
319    fn evaluate(s: &str, x: f32, y: f32) -> TestEvalResult {
320        let e = match TinyExpr::from_str(s) {
321            Ok(r) => match r.eval(x, y) {
322                Ok(a) => TestEvalResult::Ok(a),
323                Err(err) => TestEvalResult::ErrEval(err),
324            },
325            Err(err) => TestEvalResult::ErrFromStr(err),
326        };
327
328        // can be useful to debug if you introduce std again
329        // match &e {
330        //     TestEvalResult::Ok(e) => println!("Ok, {}", e),
331        //     TestEvalResult::ErrFromStr(parse_error) => println!("{}", "err from str"),
332        //     TestEvalResult::ErrEval(parse_error) => println!("{}", "err from eval"),
333        // };
334
335        e
336    }
337
338    #[test]
339    fn it_works() {
340        match evaluate("2.3", 0.0, 0.0) {
341            TestEvalResult::Ok(r) => assert_eq!(r, 2.3),
342            _ => assert!(false),
343        }
344
345        match evaluate("2", 0.0, 0.0) {
346            TestEvalResult::Ok(r) => assert_eq!(r, 2.0),
347            _ => assert!(false),
348        }
349
350        match evaluate("x", 5.0, 0.0) {
351            TestEvalResult::Ok(r) => assert_eq!(r, 5.0),
352            _ => assert!(false),
353        }
354
355        match evaluate("y", 0.0, 6.0) {
356            TestEvalResult::Ok(r) => assert_eq!(r, 6.0),
357            _ => assert!(false),
358        }
359
360        match evaluate("x y add", 2.0, 5.0) {
361            TestEvalResult::Ok(r) => {
362                assert_eq!(r, 7.0)
363            }
364            _ => {
365                assert!(false)
366            }
367        }
368
369        match evaluate("x y 0.0 lerp", 2.0, 5.0) {
370            TestEvalResult::Ok(r) => {
371                assert_eq!(r, 2.0)
372            }
373            _ => {
374                assert!(false)
375            }
376        }
377
378        match evaluate("x y 1.0 lerp", 2.0, 5.0) {
379            TestEvalResult::Ok(r) => {
380                assert_eq!(r, 5.0)
381            }
382            _ => {
383                assert!(false)
384            }
385        }
386
387        match evaluate("x y mul 4.0 add", 2.0, 5.0) {
388            TestEvalResult::Ok(r) => {
389                assert_eq!(r, 14.0)
390            }
391            _ => {
392                assert!(false)
393            }
394        }
395    }
396
397    #[test]
398    fn it_does_not_work_empty() {
399        let empty_result = evaluate("", 0.0, 0.0);
400        assert!(empty_result.is_err_eval());
401    }
402
403    #[test]
404    fn it_does_not_work_gobbligook() {
405        let empty_result = evaluate("gobbligook", 0.0, 0.0);
406        assert!(empty_result.is_err_from_str());
407    }
408}