fasteval3/
compiler.rs

1//! This module compiles parsed `Expression`s into an optimized AST node called an `Instruction`.
2//! The compiled form is much faster, especially for constants.
3//!
4//! # Compile-time Optimizations
5//!
6//! ## Constant Folding
7//! Operations with constants can be calculated at compile time so
8//! they don't need to be calculated during `eval()`.
9//!
10//! For example, `1 + x + 1` will be compiled into `x + 2`, saving some time during `eval()`.
11//!
12//! If the entire expression only consists of constants (no variables),
13//! then the expression can be reduced to a final result at compile time,
14//! resulting in near-native speed during `eval()`.
15//!
16//! ## Algebraic Simplification
17//! * Subtraction is converted to Addition.
18//! * Division is converted to Multiplication.
19//! * Built-in functions with constant arguments are evaluated.
20//! * Constant terms are combined.
21//! * Logical operator short-circuits are applied and no-op branches are discarded.
22//!
23//! ## Optimized Memory Layout and Execution
24//! * Variable-length `Expression`/`Value` AST nodes are converted into constant-sized `Instruction` nodes.
25//! * The `IC` enumeration helps to eliminate expensive function calls.
26
27use std::cell::RefCell;
28
29#[cfg(feature = "unsafe-vars")]
30use crate::parser::StdFunc::EUnsafeVar;
31use crate::{parser::{
32    BinaryOp::{
33        self, EAdd, EDiv, EExp, EMod, EMul, ESub, EAND, EEQ, EGT, EGTE, ELT, ELTE, ENE, EOR,
34    },
35    ExprPair, Expression, PrintFunc,
36    StdFunc::{
37        self, EFunc, EFuncACos, EFuncACosH, EFuncASin, EFuncASinH, EFuncATan, EFuncATanH, EFuncAbs,
38        EFuncCeil, EFuncCos, EFuncCosH, EFuncE, EFuncFloor, EFuncInt, EFuncLog, EFuncMax, EFuncMin,
39        EFuncPi, EFuncRound, EFuncSign, EFuncSin, EFuncSinH, EFuncTan, EFuncTanH, EVar,
40    },
41    UnaryOp::{self, ENeg, ENot, EParentheses, EPos},
42    Value,
43}, ExpressionI};
44use crate::slab::{CompileSlab, ParseSlab};
45use crate::Error;
46
47/// `true` --> `1.0`,  `false` --> `0.0`
48#[macro_export]
49macro_rules! bool_to_f64 {
50    ($b:expr) => {
51        if $b {
52            1.0_f64
53        } else {
54            0.0_f64
55        }
56    };
57}
58
59/// An `InstructionI` represents an index into `Slab.cs.instrs`.
60///
61/// It behaves much like a pointer or reference, but it is 'safe' (unlike a raw
62/// pointer) and is not managed by the Rust borrow checker (unlike a reference).
63#[derive(Debug, PartialEq, Eq, Copy, Clone)]
64pub struct InstructionI(pub usize);
65
66/// This enumeration boosts performance because it eliminates expensive function calls for constant values.
67#[derive(Debug, PartialEq)]
68pub enum IC {
69    I(InstructionI),
70    C(f64),
71}
72
73macro_rules! instr_to_ic {
74    ($cslab:ident, $instr:ident) => {
75        match $instr {
76            IConst(c) => IC::C(c),
77            _ => IC::I($cslab.push_instr($instr)),
78        }
79    };
80}
81macro_rules! ic_to_instr {
82    ($cslab:expr, $dst:ident, $ic:ident) => {
83        match $ic {
84            IC::C(c) => {
85                $dst = IConst(*c);
86                &$dst
87            }
88            IC::I(i) => get_instr!($cslab, i),
89        }
90    };
91}
92
93/// An `Instruction` is an optimized AST node resulting from compilation.
94#[derive(Debug, PartialEq)]
95pub enum Instruction {
96    //---- Primitive Value Types:
97    IConst(f64),
98
99    //---- Unary Ops:
100    // Parentheses is a noop
101    // Pos is a noop
102    INeg(InstructionI),
103    INot(InstructionI),
104    IInv(InstructionI),
105
106    //---- Binary Math Ops:
107    IAdd(InstructionI, IC),
108    // A Sub(x) is converted to an Add(Neg(x)).
109    IMul(InstructionI, IC),
110    // A Div(n,d) is converted to a Mul(n,Inv(d)).
111    IMod {
112        dividend: IC,
113        divisor: IC,
114    },
115    IExp {
116        base: IC,
117        power: IC,
118    },
119
120    //---- Binary Comparison Ops:
121    ILT(IC, IC),
122    ILTE(IC, IC),
123    IEQ(IC, IC),
124    INE(IC, IC),
125    IGTE(IC, IC),
126    IGT(IC, IC),
127
128    //---- Binary Logic Ops:
129    IOR(InstructionI, IC),
130    IAND(InstructionI, IC),
131
132    //---- Callables:
133    IVar(String),
134    #[cfg(feature = "unsafe-vars")]
135    IUnsafeVar {
136        name: String,
137        ptr: *const f64,
138    },
139    IFunc {
140        name: String,
141        args: Vec<IC>,
142    },
143
144    IFuncInt(InstructionI),
145    IFuncCeil(InstructionI),
146    IFuncFloor(InstructionI),
147    IFuncAbs(InstructionI),
148    IFuncSign(InstructionI),
149    IFuncLog {
150        base: IC,
151        of: IC,
152    },
153    IFuncRound {
154        modulus: IC,
155        of: IC,
156    },
157    IFuncMin(InstructionI, IC),
158    IFuncMax(InstructionI, IC),
159
160    IFuncSin(InstructionI),
161    IFuncCos(InstructionI),
162    IFuncTan(InstructionI),
163    IFuncASin(InstructionI),
164    IFuncACos(InstructionI),
165    IFuncATan(InstructionI),
166    IFuncSinH(InstructionI),
167    IFuncCosH(InstructionI),
168    IFuncTanH(InstructionI),
169    IFuncASinH(InstructionI),
170    IFuncACosH(InstructionI),
171    IFuncATanH(InstructionI),
172
173    IPrintFunc(PrintFunc), // Not optimized (it would be pointless because of i/o bottleneck).
174}
175use crate::{eval_var, EvalNamespace};
176#[cfg(feature = "unsafe-vars")]
177use Instruction::IUnsafeVar;
178use Instruction::{
179    IAdd, IConst, IExp, IFunc, IFuncACos, IFuncACosH, IFuncASin, IFuncASinH, IFuncATan, IFuncATanH,
180    IFuncAbs, IFuncCeil, IFuncCos, IFuncCosH, IFuncFloor, IFuncInt, IFuncLog, IFuncMax, IFuncMin,
181    IFuncRound, IFuncSign, IFuncSin, IFuncSinH, IFuncTan, IFuncTanH, IInv, IMod, IMul, INeg, INot,
182    IPrintFunc, IVar, IAND, IEQ, IGT, IGTE, ILT, ILTE, INE, IOR,
183};
184
185impl Default for Instruction {
186    fn default() -> Self {
187        IConst(std::f64::NAN)
188    }
189}
190
191/// You must `use` the `Compiler` trait before you can call `.compile()` on parsed `Expression`s.
192pub trait Compiler {
193    /// Turns a parsed `Expression` into a compiled `Instruction`.
194    ///
195    /// Cannot fail, unless you run out of memory.
196    fn compile(
197        &self,
198        pslab: &ParseSlab,
199        cslab: &mut CompileSlab,
200        ns: &mut impl EvalNamespace,
201    ) -> Instruction;
202}
203
204#[derive(Debug)]
205struct ExprSlice<'s> {
206    first: &'s Value,
207    pairs: Vec<&'s ExprPair>,
208}
209
210impl<'s> ExprSlice<'s> {
211    fn new(first: &Value) -> ExprSlice<'_> {
212        ExprSlice {
213            first,
214            pairs: Vec::with_capacity(8),
215        }
216    }
217
218    fn from_expr(expr: &Expression) -> ExprSlice<'_> {
219        let mut sl = ExprSlice::new(&expr.first);
220        for exprpairref in &expr.pairs {
221            sl.pairs.push(exprpairref);
222        }
223        sl
224    }
225
226    fn split(&self, bop: BinaryOp, dst: &mut Vec<ExprSlice<'s>>) {
227        dst.push(ExprSlice::new(self.first));
228        for exprpair in &self.pairs {
229            if exprpair.0 == bop {
230                dst.push(ExprSlice::new(&exprpair.1));
231            } else if let Some(cur) = dst.last_mut() {
232                cur.pairs.push(exprpair);
233            }
234        }
235    }
236
237    fn split_multi(
238        &self,
239        search: &[BinaryOp],
240        xsdst: &mut Vec<ExprSlice<'s>>,
241        opdst: &mut Vec<&'s BinaryOp>,
242    ) {
243        xsdst.push(ExprSlice::new(self.first));
244        for exprpair in &self.pairs {
245            if search.contains(&exprpair.0) {
246                xsdst.push(ExprSlice::new(&exprpair.1));
247                opdst.push(&exprpair.0);
248            } else if let Some(cur) = xsdst.last_mut() {
249                cur.pairs.push(exprpair);
250            }
251        }
252    }
253
254    /// Comparison processing step during compilation
255    #[inline]
256    fn process_comparisons(
257        &self,
258        parsed_slab: &ParseSlab,
259        compiled_slab: &mut CompileSlab,
260        namespace: &mut impl EvalNamespace
261    ) -> Instruction {
262        let mut ops = Vec::<&BinaryOp>::with_capacity(4);
263        let mut xss = Vec::<ExprSlice>::with_capacity(ops.len() + 1);
264        self.split_multi(&[EEQ, ENE, ELT, EGT, ELTE, EGTE], &mut xss, &mut ops);
265        let mut out: Instruction = xss.first().map_or(IConst(std::f64::NAN), |xs| {
266            xs.compile(parsed_slab, compiled_slab, namespace)
267        });
268
269        for (i, op) in ops.into_iter().enumerate() {
270            let instruction: Instruction = xss.get(i + 1).map_or(IConst(f64::NAN), |xs| {
271                xs.compile(parsed_slab, compiled_slab, namespace)
272            });
273
274            if let IConst(l) = out {
275                if let IConst(r) = instruction {
276                    out = match op {
277                        EEQ => IConst(bool_to_f64!(crate::f64_eq!(l, r))),
278                        ENE => IConst(bool_to_f64!(crate::f64_ne!(l, r))),
279                        ELT => IConst(bool_to_f64!(l < r)),
280                        EGT => IConst(bool_to_f64!(l > r)),
281                        ELTE => IConst(bool_to_f64!(l <= r)),
282                        EGTE => IConst(bool_to_f64!(l >= r)),
283                        _ => IConst(std::f64::NAN), // unreachable
284                    };
285                    continue;
286                }
287            }
288            out = match op {
289                EEQ => IEQ(instr_to_ic!(compiled_slab, out), instr_to_ic!(compiled_slab, instruction)),
290                ENE => INE(instr_to_ic!(compiled_slab, out), instr_to_ic!(compiled_slab, instruction)),
291                ELT => ILT(instr_to_ic!(compiled_slab, out), instr_to_ic!(compiled_slab, instruction)),
292                EGT => IGT(instr_to_ic!(compiled_slab, out), instr_to_ic!(compiled_slab, instruction)),
293                ELTE => ILTE(instr_to_ic!(compiled_slab, out), instr_to_ic!(compiled_slab, instruction)),
294                EGTE => IGTE(instr_to_ic!(compiled_slab, out), instr_to_ic!(compiled_slab, instruction)),
295                _ => IConst(std::f64::NAN), // unreachable
296            };
297        }
298        out
299    }
300
301    /// OR processing step during compilation
302    #[inline]
303    fn process_or(
304        &self,
305        parsed_slab: &ParseSlab,
306        compiled_slab: &mut CompileSlab,
307        namespace: &mut impl EvalNamespace
308    ) -> Instruction {
309        let mut xss = Vec::<ExprSlice>::with_capacity(4);
310        self.split(EOR, &mut xss);
311        let mut out = IConst(0.0);
312        let mut out_set = false;
313        for xs in &xss {
314            let instr = xs.compile(parsed_slab, compiled_slab, namespace);
315            if out_set {
316                out = IOR(compiled_slab.push_instr(out), instr_to_ic!(compiled_slab, instr));
317            } else if let IConst(c) = instr {
318                if crate::f64_ne!(c, 0.0) {
319                    return instr;
320                }
321            } else {
322                out = instr;
323                out_set = true;
324            }
325        }
326        out
327    }
328
329    /// AND processing step during compilation
330    #[inline]
331    fn process_and(
332        &self, 
333        parsed_slab: &ParseSlab, 
334        compiled_slab: &mut CompileSlab, 
335        namespace: &mut impl EvalNamespace
336    ) -> Instruction {
337        let mut xss = Vec::<ExprSlice>::with_capacity(4);
338        self.split(EAND, &mut xss);
339        let mut out = IConst(1.0);
340        let mut out_set = false;
341        for xs in &xss {
342            let instr = xs.compile(parsed_slab, compiled_slab, namespace);
343            if let IConst(c) = instr {
344                if crate::f64_eq!(c, 0.0) {
345                    return instr;
346                }
347            }
348            if out_set {
349                if let IConst(_) = out {
350                    // If we get here, we know that the const is non-zero.
351                    out = instr;
352                } else {
353                    out = IAND(compiled_slab.push_instr(out), instr_to_ic!(compiled_slab, instr));
354                }
355            } else {
356                out = instr;
357                out_set = true;
358            }
359        }
360        out
361    }
362
363    /// Addition processing step during compilation
364    #[inline]
365    fn process_addition(
366        &self,
367        parsed_slab: &ParseSlab,
368        compiled_slab: &mut CompileSlab,
369        namespace: &mut impl EvalNamespace
370    ) -> Instruction {
371        let mut xss = Vec::<ExprSlice>::with_capacity(4);
372        self.split(EAdd, &mut xss);
373        let mut instrs = Vec::<Instruction>::with_capacity(xss.len());
374        for xs in xss {
375            let instr = xs.compile(parsed_slab, compiled_slab, namespace);
376            if let IAdd(li, ric) = instr {
377                push_add_leaves(&mut instrs, compiled_slab, li, &ric); // Flatten nested structures like "x - 1 + 2 - 3".
378            } else {
379                instrs.push(instr);
380            }
381        }
382        compile_add(instrs, compiled_slab)
383    }
384
385    /// Subtraction processing step during compilation
386    #[inline]
387    fn process_subtraction(&self, parsed_slab: &ParseSlab, compiled_slab: &mut CompileSlab, namespace: &mut impl EvalNamespace) -> Instruction {
388        // Note: We don't need to push_add_leaves from here because Sub has a higher precedence than Add.
389
390        let mut xss = Vec::<ExprSlice>::with_capacity(4);
391        self.split(ESub, &mut xss);
392        let mut instrs = Vec::<Instruction>::with_capacity(xss.len());
393        for (i, xs) in xss.into_iter().enumerate() {
394            let instr = xs.compile(parsed_slab, compiled_slab, namespace);
395            if i == 0 {
396                instrs.push(instr);
397            } else {
398                instrs.push(neg_wrap(instr, compiled_slab));
399            }
400        }
401        compile_add(instrs, compiled_slab)
402    }
403
404    /// Multiplication processing step during compilation.
405    #[inline]
406    fn process_multiplication(
407        &self,
408        parsed_slab: &ParseSlab,
409        compiled_slab: &mut CompileSlab,
410        namespace: &mut impl EvalNamespace
411    ) -> Instruction {
412        let mut xss = Vec::<ExprSlice>::with_capacity(4);
413        self.split(EMul, &mut xss);
414        let mut instrs = Vec::<Instruction>::with_capacity(xss.len());
415        for xs in xss {
416            let instr = xs.compile(parsed_slab, compiled_slab, namespace);
417            if let IMul(li, ric) = instr {
418                push_mul_leaves(&mut instrs, compiled_slab, li, &ric); // Flatten nested structures like "deg/360 * 2*pi()".
419            } else {
420                instrs.push(instr);
421            }
422        }
423        compile_mul(instrs, compiled_slab)
424    }
425}
426
427/// Creates process functions dynamically.
428/// Each match arm provides different variations of similar function styles.
429/// The first is used to define Trigonometric functions.
430macro_rules! process_fn {
431    ($name:ident, $operation:ident, $fallback:ident) => {
432        #[inline]
433        fn $name(
434            parsed_slab: &ParseSlab,
435            compiled_slab: &mut CompileSlab,
436            namespace: &mut impl EvalNamespace,
437            expr: ExpressionI
438        ) -> Instruction {
439            let instruction = get_expr!(parsed_slab, expr).compile(parsed_slab, compiled_slab, namespace);
440            if let IConst(target) = instruction {
441                IConst(target.$operation())
442            } else {
443                $fallback(compiled_slab.push_instr(instruction))
444            }
445        }
446    };
447}
448
449/// Uses [`EPSILON`](https://doc.rust-lang.org/core/f64/constant.EPSILON.html) to determine equality of two `f64`s.
450#[macro_export]
451macro_rules! f64_eq {
452    ($l:ident, $r:literal) => {
453        ($l - $r).abs() <= 8.0 * std::f64::EPSILON
454    };
455    ($l:ident, $r:ident) => {
456        ($l - $r).abs() <= 8.0 * std::f64::EPSILON
457    };
458    ($l:expr, $r:literal) => {
459        ($l - $r).abs() <= 8.0 * std::f64::EPSILON
460    };
461    ($l:expr, $r:expr) => {
462        (($l) - ($r)).abs() <= 8.0 * std::f64::EPSILON
463    };
464}
465
466/// Uses [`EPSILON`](https://doc.rust-lang.org/core/f64/constant.EPSILON.html) to determine inequality of two `f64`s.
467///
468/// This is exactly the same as saying `!f64_eq(x,y)` but it is slightly more efficient.
469#[macro_export]
470macro_rules! f64_ne {
471    ($l:ident, $r:literal) => {
472        ($l - $r).abs() > 8.0 * std::f64::EPSILON
473    };
474    ($l:ident, $r:ident) => {
475        ($l - $r).abs() > 8.0 * std::f64::EPSILON
476    };
477    ($l:expr, $r:literal) => {
478        ($l - $r).abs() > 8.0 * std::f64::EPSILON
479    };
480    ($l:expr, $r:expr) => {
481        (($l) - ($r)).abs() > 8.0 * std::f64::EPSILON
482    };
483}
484fn neg_wrap(instr: Instruction, cslab: &mut CompileSlab) -> Instruction {
485    if let IConst(c) = instr {
486        IConst(-c)
487    } else if let INeg(i) = instr {
488        cslab.take_instr(i)
489    } else {
490        INeg(cslab.push_instr(instr))
491    }
492}
493fn not_wrap(instr: Instruction, cslab: &mut CompileSlab) -> Instruction {
494    if let IConst(c) = instr {
495        IConst(bool_to_f64!(f64_eq!(c, 0.0)))
496    } else if let INot(i) = instr {
497        cslab.take_instr(i)
498    } else {
499        INot(cslab.push_instr(instr))
500    }
501}
502fn inv_wrap(instr: Instruction, cslab: &mut CompileSlab) -> Instruction {
503    if let IConst(c) = instr {
504        IConst(1.0 / c)
505    } else if let IInv(i) = instr {
506        cslab.take_instr(i)
507    } else {
508        IInv(cslab.push_instr(instr))
509    }
510}
511fn compile_mul(instrs: Vec<Instruction>, cslab: &mut CompileSlab) -> Instruction {
512    let mut out = IConst(1.0);
513    let mut out_set = false;
514    let mut const_prod = 1.0;
515    for instr in instrs {
516        if let IConst(c) = instr {
517            const_prod *= c; // Floats don't overflow.
518        } else if out_set {
519            out = IMul(cslab.push_instr(out), IC::I(cslab.push_instr(instr)));
520        } else {
521            out = instr;
522                out_set = true;
523        }
524    }
525    if f64_ne!(const_prod, 1.0) {
526        if out_set {
527            out = IMul(cslab.push_instr(out), IC::C(const_prod));
528        } else {
529            out = IConst(const_prod);
530        }
531    }
532    out
533}
534fn compile_add(instrs: Vec<Instruction>, cslab: &mut CompileSlab) -> Instruction {
535    let mut out = IConst(0.0);
536    let mut out_set = false;
537    let mut const_sum = 0.0;
538    for instr in instrs {
539        if let IConst(c) = instr {
540            const_sum += c; // Floats don't overflow.
541        } else if out_set {
542            out = IAdd(cslab.push_instr(out), IC::I(cslab.push_instr(instr)));
543        } else {
544            out = instr;
545            out_set = true;
546        }
547    }
548    if f64_ne!(const_sum, 0.0) {
549        if out_set {
550            out = IAdd(cslab.push_instr(out), IC::C(const_sum));
551        } else {
552            out = IConst(const_sum);
553        }
554    }
555    out
556}
557pub(crate) fn log(base: f64, n: f64) -> f64 {
558    // Can't use floating point in 'match' patterns.  :(
559    if f64_eq!(base, 2.0) {
560        return n.log2();
561    }
562    if f64_eq!(base, 10.0) {
563        return n.log10();
564    }
565    n.log(base)
566}
567
568// Can't inline recursive functions:
569fn push_mul_leaves(
570    instrs: &mut Vec<Instruction>,
571    cslab: &mut CompileSlab,
572    li: InstructionI,
573    ric: &IC,
574) {
575    // Take 'r' before 'l' for a chance for more efficient memory usage:
576    match *ric {
577        IC::I(ri) => {
578            let instr = cslab.take_instr(ri);
579            if let IMul(rli, rric) = instr {
580                push_mul_leaves(instrs, cslab, rli, &rric);
581            } else {
582                instrs.push(instr);
583            }
584        }
585        IC::C(c) => instrs.push(IConst(c)),
586    };
587
588    let instr = cslab.take_instr(li);
589    if let IMul(lli, lric) = instr {
590        push_mul_leaves(instrs, cslab, lli, &lric);
591    } else {
592        instrs.push(instr);
593    }
594}
595fn push_add_leaves(
596    instrs: &mut Vec<Instruction>,
597    cslab: &mut CompileSlab,
598    li: InstructionI,
599    ric: &IC,
600) {
601    // Take 'r' before 'l' for a chance for more efficient memory usage:
602    match *ric {
603        IC::I(ri) => {
604            let instr = cslab.take_instr(ri);
605            if let IAdd(rli, rric) = instr {
606                push_add_leaves(instrs, cslab, rli, &rric);
607            } else {
608                instrs.push(instr);
609            }
610        }
611        IC::C(c) => instrs.push(IConst(c)),
612    };
613
614    let instr = cslab.take_instr(li);
615    if let IAdd(lli, lric) = instr {
616        push_add_leaves(instrs, cslab, lli, &lric);
617    } else {
618        instrs.push(instr);
619    }
620}
621
622impl Compiler for ExprSlice<'_> {
623    fn compile(
624        &self,
625        parsed_slab: &ParseSlab,
626        compiled_slab: &mut CompileSlab,
627        namespace: &mut impl EvalNamespace,
628    ) -> Instruction {
629        // Associative:  (2+3)+4 = 2+(3+4)
630        // Commutative:  1+2 = 2+1
631        //
632        //          Only         Only
633        // Neither  Associative  Commutative  Both
634        // -------  -----------  -----------  ----
635        // GTE      (none)       (none)       OR
636        // LTE                                AND
637        // GT                                 NE
638        // LT                                 EQ
639        // Minus (opt with neg & add)         Plus
640        // Div (opt with inv & mul)           Mul
641        // Mod
642        // Exp
643
644        // Find the lowest-priority BinaryOp:
645        let mut lowest_op = match self.pairs.first() {
646            Some(p0) => p0.0,
647            None => return self.first.compile(parsed_slab, compiled_slab, namespace),
648        };
649        for exprpair in &self.pairs {
650            if exprpair.0 < lowest_op {
651                lowest_op = exprpair.0;
652            }
653        }
654
655        // All comparisons have equal precedence:
656        if lowest_op == EEQ
657            || lowest_op == ENE
658            || lowest_op == ELT
659            || lowest_op == EGT
660            || lowest_op == ELTE
661            || lowest_op == EGTE
662        {
663            return self.process_comparisons(parsed_slab, compiled_slab, namespace)
664        }
665
666        match lowest_op {
667            EOR => self.process_or(parsed_slab, compiled_slab, namespace),
668            EAND => self.process_and(parsed_slab, compiled_slab, namespace),
669            EAdd => self.process_addition(parsed_slab, compiled_slab, namespace),
670            ESub => self.process_subtraction(parsed_slab, compiled_slab, namespace),
671            EMul => self.process_multiplication(parsed_slab, compiled_slab, namespace),
672            EDiv => {
673                // Note: We don't need to push_mul_leaves from here because Div has a higher precedence than Mul.
674
675                let mut xss = Vec::<ExprSlice>::with_capacity(4);
676                self.split(EDiv, &mut xss);
677                let mut instrs = Vec::<Instruction>::with_capacity(xss.len());
678                for (i, xs) in xss.into_iter().enumerate() {
679                    let instr = xs.compile(parsed_slab, compiled_slab, namespace);
680                    if i == 0 {
681                        instrs.push(instr);
682                    } else {
683                        instrs.push(inv_wrap(instr, compiled_slab));
684                    }
685                }
686                compile_mul(instrs, compiled_slab)
687            }
688            //          EDiv => {
689            //              let mut xss = Vec::<ExprSlice>::with_capacity(4);
690            //              self.split(EDiv, &mut xss);
691            //              let mut out = IConst(1.0); let mut out_set = false;
692            //              let mut const_prod = 1.0;
693            //              let mut is_first = true;
694            //              for xs in xss.iter() {
695            //                  let instr = xs.compile(pslab,cslab,ns);
696            //                  if let IConst(c) = instr {
697            //                      if is_first {
698            //                          const_prod *= c;  // Floats don't overflow.
699            //                      } else {
700            //                          const_prod /= c;
701            //                      }
702            //                  } else {
703            //                      if is_first {
704            //                          if out_set {
705            //                              out = IMul(cslab.push_instr(out), cslab.push_instr(instr));
706            //                          } else {
707            //                              out = instr;
708            //                              out_set = true;
709            //                          }
710            //                      } else {
711            //                          let instr = inv_wrap(instr,cslab);
712            //                          if out_set {
713            //                              out = IMul(cslab.push_instr(out), cslab.push_instr(instr));
714            //                          } else {
715            //                              out = instr;
716            //                              out_set = true;
717            //                          }
718            //                      }
719            //                  }
720            //                  is_first = false;
721            //              }
722            //              if f64_ne!(const_prod,1.0) {
723            //                  if out_set {
724            //                      out = IMul(cslab.push_instr(out), cslab.push_instr(IConst(const_prod)));
725            //                  } else {
726            //                      out = IConst(const_prod);
727            //                  }
728            //              }
729            //              out
730            //          }
731            EMod => {
732                let mut xss = Vec::<ExprSlice>::with_capacity(2);
733                self.split(EMod, &mut xss);
734                let mut out = IConst(0.0);
735                let mut out_set = false;
736                for xs in &xss {
737                    let instr = xs.compile(parsed_slab, compiled_slab, namespace);
738                    if out_set {
739                        if let IConst(dividend) = out {
740                            if let IConst(divisor) = instr {
741                                out = IConst(dividend % divisor);
742                                continue;
743                            }
744                        }
745                        out = IMod {
746                            dividend: instr_to_ic!(compiled_slab, out),
747                            divisor: instr_to_ic!(compiled_slab, instr),
748                        };
749                    } else {
750                        out = instr;
751                        out_set = true;
752                    }
753                }
754                out
755            }
756            EExp => {
757                // Right-to-Left Associativity
758                let mut xss = Vec::<ExprSlice>::with_capacity(2);
759                self.split(EExp, &mut xss);
760                let mut out = IConst(0.0);
761                let mut out_set = false;
762                for xs in xss.into_iter().rev() {
763                    let instr = xs.compile(parsed_slab, compiled_slab, namespace);
764                    if out_set {
765                        if let IConst(power) = out {
766                            if let IConst(base) = instr {
767                                out = IConst(base.powf(power));
768                                continue;
769                            }
770                        }
771                        out = IExp {
772                            base: instr_to_ic!(compiled_slab, instr),
773                            power: instr_to_ic!(compiled_slab, out),
774                        };
775                    } else {
776                        out = instr;
777                        out_set = true;
778                    }
779                }
780                out
781            }
782            //          EExp => {  // Left-to-Right Associativity
783            //              let mut xss = Vec::<ExprSlice>::with_capacity(2);
784            //              self.split(EExp, &mut xss);
785            //              let mut pow_instrs = Vec::<Instruction>::with_capacity(xss.len()-1);
786            //              let mut base = IConst(0.0);
787            //              for (i,xs) in xss.into_iter().enumerate() {
788            //                  let instr = xs.compile(pslab,cslab,ns);
789            //                  if i==0 {
790            //                      base = instr;
791            //                  } else {
792            //                      pow_instrs.push(instr);
793            //                  }
794            //              }
795            //              let power = compile_mul(pow_instrs,cslab);
796            //              if let IConst(b) = base {
797            //                  if let IConst(p) = power {
798            //                      return IConst(b.powf(p));
799            //                  }
800            //              }
801            //              IExp{base:cslab.push_instr(base), power:cslab.push_instr(power)}
802            //          }
803            ENE | EEQ | EGTE | ELTE | EGT | ELT => IConst(std::f64::NAN), // unreachable
804        }
805    }
806}
807
808impl Compiler for Expression {
809    fn compile(
810        &self,
811        parsed_slab: &ParseSlab,
812        compiled_slab: &mut CompileSlab,
813        ns: &mut impl EvalNamespace,
814    ) -> Instruction {
815        let top = ExprSlice::from_expr(self);
816        top.compile(parsed_slab, compiled_slab, ns)
817    }
818}
819
820impl Compiler for Value {
821    fn compile(
822        &self,
823        parsed_slab: &ParseSlab,
824        compiled_slab: &mut CompileSlab,
825        ns: &mut impl EvalNamespace,
826    ) -> Instruction {
827        match self {
828            Self::EConstant(c) => IConst(*c),
829            Self::EUnaryOp(u) => u.compile(parsed_slab, compiled_slab, ns),
830            Self::EStdFunc(f) => f.compile(parsed_slab, compiled_slab, ns),
831            Self::EPrintFunc(pf) => IPrintFunc(pf.clone()),
832        }
833    }
834}
835
836impl Compiler for UnaryOp {
837    fn compile(
838        &self,
839        parsed_slab: &ParseSlab,
840        compiled_slab: &mut CompileSlab,
841        ns: &mut impl EvalNamespace,
842    ) -> Instruction {
843        match self {
844            EPos(i) => get_val!(parsed_slab, i).compile(parsed_slab, compiled_slab, ns),
845            ENeg(i) => {
846                let instr = get_val!(parsed_slab, i).compile(parsed_slab, compiled_slab, ns);
847                if let IConst(c) = instr {
848                    IConst(-c)
849                } else {
850                    neg_wrap(instr, compiled_slab)
851                }
852            }
853            ENot(i) => {
854                let instr = get_val!(parsed_slab, i).compile(parsed_slab, compiled_slab, ns);
855                if let IConst(c) = instr {
856                    IConst(bool_to_f64!(f64_eq!(c, 0.0)))
857                } else {
858                    not_wrap(instr, compiled_slab)
859                }
860            }
861            EParentheses(i) => get_expr!(parsed_slab, i).compile(parsed_slab, compiled_slab, ns),
862        }
863    }
864}
865
866impl StdFunc {
867    /// Custom Function processing step during compilation.
868    #[inline]
869    fn process_custom_fn(
870        parsed_slab: &ParseSlab,
871        compiled_slab: &mut CompileSlab,
872        namespace: &mut impl EvalNamespace,
873        name: &String,
874        expressions: &Vec<ExpressionI>,
875        celled_parsed_slab: &RefCell<String>
876    ) -> Instruction {
877        let mut args = Vec::<IC>::with_capacity(expressions.len());
878        let mut f64_args = Vec::<f64>::with_capacity(expressions.len());
879        let mut is_all_const = true;
880        for expr in expressions {
881            let instr = get_expr!(parsed_slab, expr).compile(parsed_slab, compiled_slab, namespace);
882            if let IConst(c) = instr {
883                f64_args.push(c);
884            } else {
885                is_all_const = false;
886            }
887            args.push(instr_to_ic!(compiled_slab, instr));
888        }
889        if is_all_const {
890            let computed_value = eval_var!(namespace, name, f64_args, &mut celled_parsed_slab.borrow_mut());
891            computed_value.map_or_else(|_| IFunc { name: name.clone(), args }, IConst)
892        } else {
893            IFunc {
894                name: name.clone(),
895                args,
896            }
897        }
898    }
899
900    /// Integer Function processing step during compilation.
901    #[inline]
902    fn process_int_fn(
903        parsed_slab: &ParseSlab,
904        compiled_slab: &mut CompileSlab,
905        namespace: &mut impl EvalNamespace,
906        expression: ExpressionI
907    ) -> Instruction {
908        let instr = get_expr!(parsed_slab, expression).compile(parsed_slab, compiled_slab, namespace);
909        if let IConst(c) = instr {
910            IConst(c.trunc())
911        } else {
912            IFuncInt(compiled_slab.push_instr(instr))
913        }
914    }
915
916    /// Ceiling processing step during compilation.
917    #[inline]
918    fn process_ceil_fn(
919        parsed_slab: &ParseSlab,
920        compiled_slab: &mut CompileSlab,
921        namespace: &mut impl EvalNamespace,
922        expr: ExpressionI
923    ) -> Instruction {
924        let instr = get_expr!(parsed_slab, expr).compile(parsed_slab, compiled_slab, namespace);
925        if let IConst(c) = instr {
926            IConst(c.ceil())
927        } else {
928            IFuncCeil(compiled_slab.push_instr(instr))
929        }
930    }
931
932    /// Flooring processing step during compilation.
933    #[inline]
934    fn process_floor_fn(
935        parsed_slab: &ParseSlab,
936        compiled_slab: &mut CompileSlab,
937        namespace: &mut impl EvalNamespace,
938        expr: ExpressionI
939    ) -> Instruction {
940        let instr = get_expr!(parsed_slab, expr).compile(parsed_slab, compiled_slab, namespace);
941        if let IConst(c) = instr {
942            IConst(c.floor())
943        } else {
944            IFuncFloor(compiled_slab.push_instr(instr))
945        }
946    }
947
948    /// Absolute Value processing step during compilation.
949    #[inline]
950    fn process_abs_fn(
951        parsed_slab: &ParseSlab,
952        compiled_slab: &mut CompileSlab,
953        namespace: &mut impl EvalNamespace,
954        expr: ExpressionI
955    ) -> Instruction {
956        let instr = get_expr!(parsed_slab, expr).compile(parsed_slab, compiled_slab, namespace);
957        if let IConst(c) = instr {
958            IConst(c.abs())
959        } else {
960            IFuncAbs(compiled_slab.push_instr(instr))
961        }
962    }
963
964    /// Sign processing step during compilation
965    #[inline]
966    fn process_signum(
967        parsed_slab: &ParseSlab,
968        compiled_slab: &mut CompileSlab,
969        namespace: &mut impl EvalNamespace,
970        expr: ExpressionI
971    ) -> Instruction {
972        let instr = get_expr!(parsed_slab, expr).compile(parsed_slab, compiled_slab, namespace);
973        if let IConst(c) = instr {
974            IConst(c.signum())
975        } else {
976            IFuncSign(compiled_slab.push_instr(instr))
977        }
978    }
979
980    /// Logarithm processing step during compilation.
981    #[inline]
982    fn process_log(
983        parsed_slab: &ParseSlab,
984        compiled_slab: &mut CompileSlab,
985        namespace: &mut impl EvalNamespace,
986        base_options: &Option<ExpressionI>,
987        expr: ExpressionI
988    ) -> Instruction {
989        let base: Instruction = base_options.as_ref().map_or(IConst(10.0), |bi| get_expr!(parsed_slab, bi).compile(parsed_slab, compiled_slab, namespace));
990        let instr = get_expr!(parsed_slab, expr).compile(parsed_slab, compiled_slab, namespace);
991        if let IConst(b) = base {
992            if let IConst(n) = instr {
993                return IConst(log(b, n));
994            }
995        }
996        IFuncLog {
997            base: instr_to_ic!(compiled_slab, base),
998            of: instr_to_ic!(compiled_slab, instr),
999        }
1000    }
1001
1002    /// Rounding processing step during compilation.
1003    #[inline]
1004    fn process_round(
1005        parsed_slab: &ParseSlab,
1006        compiled_slab: &mut CompileSlab,
1007        namespace: &mut impl EvalNamespace,
1008        mod_option: &Option<ExpressionI>,
1009        expr: ExpressionI
1010    ) -> Instruction {
1011        let modulus: Instruction = mod_option.as_ref().map_or(IConst(1.0), |mi| get_expr!(parsed_slab, mi).compile(parsed_slab, compiled_slab, namespace));
1012        let instr = get_expr!(parsed_slab, expr).compile(parsed_slab, compiled_slab, namespace);
1013        if let IConst(m) = modulus {
1014            if let IConst(n) = instr {
1015                return IConst((n / m).round() * m); // Floats don't overflow.
1016            }
1017        }
1018        IFuncRound {
1019            modulus: instr_to_ic!(compiled_slab, modulus),
1020            of: instr_to_ic!(compiled_slab, instr),
1021        }
1022    }
1023
1024    /// Min processing step during compilation.
1025    #[inline]
1026    fn process_min(
1027        parsed_slab: &ParseSlab,
1028        compiled_slab: &mut CompileSlab,
1029        namespace: &mut impl EvalNamespace,
1030        fi: ExpressionI,
1031        is: &Vec<ExpressionI>
1032    ) -> Instruction {
1033        let first = get_expr!(parsed_slab, fi).compile(parsed_slab, compiled_slab, namespace);
1034        let mut rest = Vec::<Instruction>::with_capacity(is.len());
1035        for i in is {
1036            rest.push(get_expr!(parsed_slab, i).compile(parsed_slab, compiled_slab, namespace));
1037        }
1038        let mut out = IConst(0.0);
1039        let mut out_set = false;
1040        let mut const_min = 0.0;
1041        let mut const_min_set = false;
1042        if let IConst(f) = first {
1043            const_min = f;
1044            const_min_set = true;
1045        } else {
1046            out = first;
1047            out_set = true;
1048        }
1049        for instr in rest {
1050            if let IConst(f) = instr {
1051                if const_min_set {
1052                    if f < const_min {
1053                        const_min = f;
1054                    }
1055                } else {
1056                    const_min = f;
1057                    const_min_set = true;
1058                }
1059            } else if out_set {
1060                out = IFuncMin(compiled_slab.push_instr(out), IC::I(compiled_slab.push_instr(instr)));
1061            } else {
1062                out = instr;
1063                out_set = true;
1064            }
1065        }
1066        if const_min_set {
1067            if out_set {
1068                out = IFuncMin(compiled_slab.push_instr(out), IC::C(const_min));
1069            } else {
1070                out = IConst(const_min);
1071                // out_set = true;  // Comment out so the compiler doesn't complain about unused assignments.
1072            }
1073        }
1074        //assert!(out_set);
1075        out
1076    }
1077
1078    /// Max processing step during compilation.
1079    #[inline]
1080    fn process_max(
1081        parsed_slab: &ParseSlab,
1082        compiled_slab: &mut CompileSlab,
1083        namespace: &mut impl EvalNamespace,
1084        fi: ExpressionI,
1085        is: &Vec<ExpressionI>
1086    ) -> Instruction {
1087        let first = get_expr!(parsed_slab, fi).compile(parsed_slab, compiled_slab, namespace);
1088        let mut rest = Vec::<Instruction>::with_capacity(is.len());
1089        for i in is {
1090            rest.push(get_expr!(parsed_slab, i).compile(parsed_slab, compiled_slab, namespace));
1091        }
1092        let mut out = IConst(0.0);
1093        let mut out_set = false;
1094        let mut const_max = 0.0;
1095        let mut const_max_set = false;
1096        if let IConst(f) = first {
1097            const_max = f;
1098            const_max_set = true;
1099        } else {
1100            out = first;
1101            out_set = true;
1102        }
1103        for instr in rest {
1104            if let IConst(f) = instr {
1105                if const_max_set {
1106                    if f > const_max {
1107                        const_max = f;
1108                    }
1109                } else {
1110                    const_max = f;
1111                    const_max_set = true;
1112                }
1113            } else if out_set {
1114                out = IFuncMax(compiled_slab.push_instr(out), IC::I(compiled_slab.push_instr(instr)));
1115            } else {
1116                out = instr;
1117                out_set = true;
1118            }
1119        }
1120        if const_max_set {
1121            if out_set {
1122                out = IFuncMax(compiled_slab.push_instr(out), IC::C(const_max));
1123            } else {
1124                out = IConst(const_max);
1125                // out_set = true;  // Comment out so the compiler doesn't complain about unused assignments.
1126            }
1127        }
1128        //assert!(out_set);
1129        out
1130    }
1131
1132    process_fn!(process_sin, sin, IFuncSin);
1133    process_fn!(process_cos, cos, IFuncCos);
1134    process_fn!(process_tan, tan, IFuncTan);
1135    process_fn!(process_asin, asin, IFuncASin);
1136    process_fn!(process_acos, acos, IFuncACos);
1137    process_fn!(process_atan, atan, IFuncATan);
1138}
1139
1140impl Compiler for StdFunc {
1141    fn compile(
1142        &self,
1143        parsed_slab: &ParseSlab,
1144        compiled_slab: &mut CompileSlab,
1145        namespace: &mut impl EvalNamespace,
1146    ) -> Instruction {
1147        let celled_parsed_slab = RefCell::from(parsed_slab.char_buf.clone());
1148        match self {
1149            EVar(name) => IVar(name.clone()),
1150            #[cfg(feature = "unsafe-vars")]
1151            EUnsafeVar { name, ptr } => IUnsafeVar {
1152                name: name.clone(),
1153                ptr: *ptr,
1154            },
1155            EFunc { name, args } => Self::process_custom_fn(parsed_slab, compiled_slab, namespace, name, args, &celled_parsed_slab),
1156
1157            EFuncInt(expr) => Self::process_int_fn(parsed_slab, compiled_slab, namespace, *expr),
1158            EFuncCeil(expr) => Self::process_ceil_fn(parsed_slab, compiled_slab, namespace, *expr),
1159            EFuncFloor(expr) => Self::process_floor_fn(parsed_slab, compiled_slab, namespace, *expr),
1160            EFuncAbs(expr) => Self::process_abs_fn(parsed_slab, compiled_slab, namespace, *expr),
1161            EFuncSign(expr) => Self::process_signum(parsed_slab, compiled_slab, namespace, *expr),
1162            EFuncLog {
1163                base: base_option,
1164                expr,
1165            } => {
1166                Self::process_log(parsed_slab, compiled_slab, namespace, base_option, *expr)
1167            }
1168            EFuncRound {
1169                modulus: mod_option,
1170                expr,
1171            } => {
1172                Self::process_round(parsed_slab, compiled_slab, namespace, mod_option, *expr)
1173            }
1174            EFuncMin {
1175                first: fi,
1176                rest: is,
1177            } => {
1178                Self::process_min(parsed_slab, compiled_slab, namespace, *fi, is)
1179            }
1180            EFuncMax {
1181                first: fi,
1182                rest: is,
1183            } => {
1184                Self::process_max(parsed_slab, compiled_slab, namespace, *fi, is)
1185            }
1186
1187            EFuncE => IConst(std::f64::consts::E),
1188            EFuncPi => IConst(std::f64::consts::PI),
1189
1190            EFuncSin(expr) => Self::process_sin(parsed_slab, compiled_slab, namespace, *expr),
1191            EFuncCos(expr) => Self::process_cos(parsed_slab, compiled_slab, namespace, *expr),
1192            EFuncTan(expr) => Self::process_tan(parsed_slab, compiled_slab, namespace, *expr),
1193            EFuncASin(expr) => Self::process_asin(parsed_slab, compiled_slab, namespace, *expr),
1194            EFuncACos(expr) => Self::process_acos(parsed_slab, compiled_slab, namespace, *expr),
1195            EFuncATan(expr) => Self::process_atan(parsed_slab, compiled_slab, namespace, *expr),
1196            EFuncSinH(i) => {
1197                let instr = get_expr!(parsed_slab, i).compile(parsed_slab, compiled_slab, namespace);
1198                if let IConst(c) = instr {
1199                    IConst(c.sinh())
1200                } else {
1201                    IFuncSinH(compiled_slab.push_instr(instr))
1202                }
1203            }
1204            EFuncCosH(i) => {
1205                let instr = get_expr!(parsed_slab, i).compile(parsed_slab, compiled_slab, namespace);
1206                if let IConst(c) = instr {
1207                    IConst(c.cosh())
1208                } else {
1209                    IFuncCosH(compiled_slab.push_instr(instr))
1210                }
1211            }
1212            EFuncTanH(i) => {
1213                let instr = get_expr!(parsed_slab, i).compile(parsed_slab, compiled_slab, namespace);
1214                if let IConst(c) = instr {
1215                    IConst(c.tanh())
1216                } else {
1217                    IFuncTanH(compiled_slab.push_instr(instr))
1218                }
1219            }
1220            EFuncASinH(i) => {
1221                let instr = get_expr!(parsed_slab, i).compile(parsed_slab, compiled_slab, namespace);
1222                if let IConst(c) = instr {
1223                    IConst(c.asinh())
1224                } else {
1225                    IFuncASinH(compiled_slab.push_instr(instr))
1226                }
1227            }
1228            EFuncACosH(i) => {
1229                let instr = get_expr!(parsed_slab, i).compile(parsed_slab, compiled_slab, namespace);
1230                if let IConst(c) = instr {
1231                    IConst(c.acosh())
1232                } else {
1233                    IFuncACosH(compiled_slab.push_instr(instr))
1234                }
1235            }
1236            EFuncATanH(i) => {
1237                let instr = get_expr!(parsed_slab, i).compile(parsed_slab, compiled_slab, namespace);
1238                if let IConst(c) = instr {
1239                    IConst(c.atanh())
1240                } else {
1241                    IFuncATanH(compiled_slab.push_instr(instr))
1242                }
1243            }
1244        }
1245    }
1246}