fasteval/
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
27
28
29use crate::slab::{ParseSlab, CompileSlab};
30use crate::parser::{Expression, ExprPair, Value, UnaryOp::{self, EPos, ENeg, ENot, EParentheses}, BinaryOp::{self, EOR, EAND, ENE, EEQ, EGTE, ELTE, EGT, ELT, EAdd, ESub, EMul, EDiv, EMod, EExp}, StdFunc::{self, EVar, EFunc, EFuncInt, EFuncCeil, EFuncFloor, EFuncAbs, EFuncSign, EFuncLog, EFuncRound, EFuncMin, EFuncMax, EFuncE, EFuncPi, EFuncSin, EFuncCos, EFuncTan, EFuncASin, EFuncACos, EFuncATan, EFuncSinH, EFuncCosH, EFuncTanH, EFuncASinH, EFuncACosH, EFuncATanH}, PrintFunc};
31#[cfg(feature="unsafe-vars")]
32use crate::parser::StdFunc::EUnsafeVar;
33
34
35/// `true` --> `1.0`,  `false` --> `0.0`
36#[macro_export]
37macro_rules! bool_to_f64 {
38    ($b:expr) => {
39        if $b { 1.0 }
40        else { 0.0 }
41    };
42}
43
44
45/// An `InstructionI` represents an index into `Slab.cs.instrs`.
46///
47/// It behaves much like a pointer or reference, but it is 'safe' (unlike a raw
48/// pointer) and is not managed by the Rust borrow checker (unlike a reference).
49#[derive(Debug, PartialEq, Copy, Clone)]
50pub struct InstructionI(pub usize);
51
52/// This enumeration boosts performance because it eliminates expensive function calls for constant values.
53#[derive(Debug, PartialEq)]
54pub enum IC {
55    I(InstructionI),
56    C(f64),
57}
58
59macro_rules! instr_to_ic {
60    ($cslab:ident, $instr:ident) => {
61        match $instr {
62            IConst(c) => IC::C(c),
63            _ => IC::I($cslab.push_instr($instr)),
64        }
65    }
66}
67macro_rules! ic_to_instr {
68    ($cslab:expr, $dst:ident, $ic:ident) => {
69        match $ic {
70            IC::C(c) => {
71                $dst = IConst(*c);
72                &$dst
73            }
74            IC::I(i) => get_instr!($cslab,i),
75        }
76    }
77}
78
79/// An `Instruction` is an optimized AST node resulting from compilation.
80#[derive(Debug, PartialEq)]
81pub enum Instruction {
82    //---- Primitive Value Types:
83    IConst(f64),
84
85    //---- Unary Ops:
86    // Parentheses is a noop
87    // Pos is a noop
88    INeg(InstructionI),
89    INot(InstructionI),
90    IInv(InstructionI),
91
92    //---- Binary Math Ops:
93    IAdd(InstructionI, IC),
94    // A Sub(x) is converted to an Add(Neg(x)).
95    IMul(InstructionI, IC),
96    // A Div(n,d) is converted to a Mul(n,Inv(d)).
97    IMod{dividend:IC, divisor:IC},
98    IExp{base:IC, power:IC},
99
100    //---- Binary Comparison Ops:
101    ILT(IC, IC),
102    ILTE(IC, IC),
103    IEQ(IC, IC),
104    INE(IC, IC),
105    IGTE(IC, IC),
106    IGT(IC, IC),
107
108    //---- Binary Logic Ops:
109    IOR(InstructionI, IC),
110    IAND(InstructionI, IC),
111
112    //---- Callables:
113    IVar(String),
114    #[cfg(feature="unsafe-vars")]
115    IUnsafeVar{name:String, ptr:*const f64},
116    IFunc{name:String, args:Vec<IC>},
117
118    IFuncInt(InstructionI),
119    IFuncCeil(InstructionI),
120    IFuncFloor(InstructionI),
121    IFuncAbs(InstructionI),
122    IFuncSign(InstructionI),
123    IFuncLog{base:IC, of:IC},
124    IFuncRound{modulus:IC, of:IC},
125    IFuncMin(InstructionI, IC),
126    IFuncMax(InstructionI, IC),
127
128    IFuncSin(InstructionI),
129    IFuncCos(InstructionI),
130    IFuncTan(InstructionI),
131    IFuncASin(InstructionI),
132    IFuncACos(InstructionI),
133    IFuncATan(InstructionI),
134    IFuncSinH(InstructionI),
135    IFuncCosH(InstructionI),
136    IFuncTanH(InstructionI),
137    IFuncASinH(InstructionI),
138    IFuncACosH(InstructionI),
139    IFuncATanH(InstructionI),
140
141    IPrintFunc(PrintFunc),  // Not optimized (it would be pointless because of i/o bottleneck).
142}
143use Instruction::{IConst, INeg, INot, IInv, IAdd, IMul, IMod, IExp, ILT, ILTE, IEQ, INE, IGTE, IGT, IOR, IAND, IVar, IFunc, IFuncInt, IFuncCeil, IFuncFloor, IFuncAbs, IFuncSign, IFuncLog, IFuncRound, IFuncMin, IFuncMax, IFuncSin, IFuncCos, IFuncTan, IFuncASin, IFuncACos, IFuncATan, IFuncSinH, IFuncCosH, IFuncTanH, IFuncASinH, IFuncACosH, IFuncATanH, IPrintFunc};
144#[cfg(feature="unsafe-vars")]
145use Instruction::IUnsafeVar;
146
147impl Default for Instruction {
148    fn default() -> Self { IConst(std::f64::NAN) }
149}
150
151
152/// You must `use` the `Compiler` trait before you can call `.compile()` on parsed `Expression`s.
153pub trait Compiler {
154    /// Turns a parsed `Expression` into a compiled `Instruction`.
155    ///
156    /// Cannot fail, unless you run out of memory.
157    fn compile(&self, pslab:&ParseSlab, cslab:&mut CompileSlab) -> Instruction;
158}
159
160
161#[derive(Debug)]
162struct ExprSlice<'s> {
163    first: &'s Value,
164    pairs: Vec<&'s ExprPair>,
165}
166impl<'s> ExprSlice<'s> {
167    fn new(first:&Value) -> ExprSlice<'_> {
168        ExprSlice{
169            first,
170            pairs:Vec::with_capacity(8),
171        }
172    }
173    fn from_expr(expr:&Expression) -> ExprSlice<'_> {
174        let mut sl = ExprSlice::new(&expr.first);
175        for exprpairref in expr.pairs.iter() { sl.pairs.push(exprpairref) }
176        sl
177    }
178    fn split(&self, bop:BinaryOp, dst:&mut Vec<ExprSlice<'s>>) {
179        dst.push(ExprSlice::new(&self.first));
180        for exprpair in self.pairs.iter() {
181            if exprpair.0==bop {
182                dst.push(ExprSlice::new(&exprpair.1));
183            } else {
184                match dst.last_mut() {
185                    Some(cur) => cur.pairs.push(exprpair),
186                    None => (),  // unreachable
187                }
188            }
189        }
190    }
191    fn split_multi(&self, search:&[BinaryOp], xsdst:&mut Vec<ExprSlice<'s>>, opdst:&mut Vec<&'s BinaryOp>) {
192        xsdst.push(ExprSlice::new(&self.first));
193        for exprpair in self.pairs.iter() {
194            if search.contains(&exprpair.0) {
195                xsdst.push(ExprSlice::new(&exprpair.1));
196                opdst.push(&exprpair.0);
197            } else {
198                match xsdst.last_mut() {
199                    Some(cur) => cur.pairs.push(exprpair),
200                    None => (),  // unreachable
201                }
202            }
203        }
204    }
205}
206
207/// Uses [`EPSILON`](https://doc.rust-lang.org/core/f64/constant.EPSILON.html) to determine equality of two `f64`s.
208#[macro_export]
209macro_rules! f64_eq {
210    ($l:ident, $r:literal) => {
211        ($l-$r).abs() <= 8.0*std::f64::EPSILON
212    };
213    ($l:ident, $r:ident) => {
214        ($l-$r).abs() <= 8.0*std::f64::EPSILON
215    };
216    ($l:expr, $r:literal) => {
217        ($l-$r).abs() <= 8.0*std::f64::EPSILON
218    };
219    ($l:expr, $r:expr) => {
220        (($l)-($r)).abs() <= 8.0*std::f64::EPSILON
221    };
222}
223
224/// Uses [`EPSILON`](https://doc.rust-lang.org/core/f64/constant.EPSILON.html) to determine inequality of two `f64`s.
225///
226/// This is exactly the same as saying `!f64_eq(x,y)` but it is slightly more efficient.
227#[macro_export]
228macro_rules! f64_ne {
229    ($l:ident, $r:literal) => {
230        ($l-$r).abs() > 8.0*std::f64::EPSILON
231    };
232    ($l:ident, $r:ident) => {
233        ($l-$r).abs() > 8.0*std::f64::EPSILON
234    };
235    ($l:expr, $r:literal) => {
236        ($l-$r).abs() > 8.0*std::f64::EPSILON
237    };
238    ($l:expr, $r:expr) => {
239        (($l)-($r)).abs() > 8.0*std::f64::EPSILON
240    };
241}
242fn neg_wrap(instr:Instruction, cslab:&mut CompileSlab) -> Instruction {
243    if let IConst(c) = instr {
244        IConst(-c)
245    } else if let INeg(i) = instr {
246        cslab.take_instr(i)
247    } else {
248        INeg(cslab.push_instr(instr))
249    }
250}
251fn not_wrap(instr:Instruction, cslab:&mut CompileSlab) -> Instruction {
252    if let IConst(c) = instr {
253        IConst(bool_to_f64!(f64_eq!(c,0.0)))
254    } else if let INot(i) = instr {
255        cslab.take_instr(i)
256    } else {
257        INot(cslab.push_instr(instr))
258    }
259}
260fn inv_wrap(instr:Instruction, cslab:&mut CompileSlab) -> Instruction {
261    if let IConst(c) = instr {
262        IConst(1.0/c)
263    } else if let IInv(i) = instr {
264        cslab.take_instr(i)
265    } else {
266        IInv(cslab.push_instr(instr))
267    }
268}
269fn compile_mul(instrs:Vec<Instruction>, cslab:&mut CompileSlab) -> Instruction {
270    let mut out = IConst(1.0); let mut out_set = false;
271    let mut const_prod = 1.0;
272    for instr in instrs {
273        if let IConst(c) = instr {
274            const_prod *= c;  // Floats don't overflow.
275        } else {
276            if out_set {
277                out = IMul(cslab.push_instr(out), IC::I(cslab.push_instr(instr)));
278            } else {
279                out = instr;
280                out_set = true;
281            }
282        }
283    }
284    if f64_ne!(const_prod,1.0) {
285        if out_set {
286            out = IMul(cslab.push_instr(out), IC::C(const_prod));
287        } else {
288            out = IConst(const_prod);
289        }
290    }
291    out
292}
293fn compile_add(instrs:Vec<Instruction>, cslab:&mut CompileSlab) -> Instruction {
294    let mut out = IConst(0.0); let mut out_set = false;
295    let mut const_sum = 0.0;
296    for instr in instrs {
297        if let IConst(c) = instr {
298            const_sum += c;  // Floats don't overflow.
299        } else {
300            if out_set {
301                out = IAdd(cslab.push_instr(out), IC::I(cslab.push_instr(instr)));
302            } else {
303                out = instr;
304                out_set = true;
305            }
306        }
307    }
308    if f64_ne!(const_sum,0.0) {
309        if out_set {
310            out = IAdd(cslab.push_instr(out), IC::C(const_sum));
311        } else {
312            out = IConst(const_sum);
313        }
314    }
315    out
316}
317pub(crate) fn log(base:f64, n:f64) -> f64 {
318    // Can't use floating point in 'match' patterns.  :(
319    if f64_eq!(base,2.0) { return n.log2(); }
320    if f64_eq!(base,10.0) { return n.log10(); }
321    n.log(base)
322}
323
324// Can't inline recursive functions:
325fn push_mul_leaves(instrs:&mut Vec<Instruction>, cslab:&mut CompileSlab, li:InstructionI, ric:IC) {
326    // Take 'r' before 'l' for a chance for more efficient memory usage:
327    match ric {
328        IC::I(ri) => {
329            let instr = cslab.take_instr(ri);
330            if let IMul(rli,rric) = instr {
331                push_mul_leaves(instrs,cslab,rli,rric);
332            } else {
333                instrs.push(instr);
334            }
335        }
336        IC::C(c) => instrs.push(IConst(c)),
337    };
338
339    let instr = cslab.take_instr(li);
340    if let IMul(lli,lric) = instr {
341        push_mul_leaves(instrs,cslab,lli,lric);
342    } else {
343        instrs.push(instr);
344    }
345}
346fn push_add_leaves(instrs:&mut Vec<Instruction>, cslab:&mut CompileSlab, li:InstructionI, ric:IC) {
347    // Take 'r' before 'l' for a chance for more efficient memory usage:
348    match ric {
349        IC::I(ri) => {
350            let instr = cslab.take_instr(ri);
351            if let IAdd(rli,rric) = instr {
352                push_add_leaves(instrs,cslab,rli,rric);
353            } else {
354                instrs.push(instr);
355            }
356        }
357        IC::C(c) => instrs.push(IConst(c)),
358    };
359
360    let instr = cslab.take_instr(li);
361    if let IAdd(lli,lric) = instr {
362        push_add_leaves(instrs,cslab,lli,lric);
363    } else {
364        instrs.push(instr);
365    }
366}
367
368impl Compiler for ExprSlice<'_> {
369    fn compile(&self, pslab:&ParseSlab, cslab:&mut CompileSlab) -> Instruction {
370        // Associative:  (2+3)+4 = 2+(3+4)
371        // Commutative:  1+2 = 2+1
372        //
373        //          Only         Only
374        // Neither  Associative  Commutative  Both
375        // -------  -----------  -----------  ----
376        // GTE      (none)       (none)       OR
377        // LTE                                AND
378        // GT                                 NE
379        // LT                                 EQ
380        // Minus (opt with neg & add)         Plus
381        // Div (opt with inv & mul)           Mul
382        // Mod
383        // Exp
384
385        // Find the lowest-priority BinaryOp:
386        let mut lowest_op = match self.pairs.first() {
387            Some(p0) => p0.0,
388            None => return self.first.compile(pslab,cslab),
389        };
390        for exprpair in self.pairs.iter() {
391            if exprpair.0<lowest_op { lowest_op=exprpair.0 }
392        }
393
394        // All comparisons have equal precedence:
395        if lowest_op==EEQ || lowest_op==ENE || lowest_op==ELT || lowest_op==EGT || lowest_op==ELTE || lowest_op==EGTE {
396            let mut ops = Vec::<&BinaryOp>::with_capacity(4);
397            let mut xss = Vec::<ExprSlice>::with_capacity(ops.len()+1);
398            self.split_multi(&[EEQ, ENE, ELT, EGT, ELTE, EGTE], &mut xss, &mut ops);
399            let mut out = match xss.first() {
400                Some(xs) => xs.compile(pslab,cslab),
401                None => IConst(std::f64::NAN),  // unreachable
402            };
403            for (i,op) in ops.into_iter().enumerate() {
404                let instr = match xss.get(i+1) {
405                    Some(xs) => xs.compile(pslab,cslab),
406                    None => IConst(std::f64::NAN),  // unreachable
407                };
408                if let IConst(l) = out {
409                    if let IConst(r) = instr {
410                        out = match op {
411                            EEQ => IConst(bool_to_f64!(f64_eq!(l,r))),
412                            ENE => IConst(bool_to_f64!(f64_ne!(l,r))),
413                            ELT => IConst(bool_to_f64!(l<r)),
414                            EGT => IConst(bool_to_f64!(l>r)),
415                            ELTE => IConst(bool_to_f64!(l<=r)),
416                            EGTE => IConst(bool_to_f64!(l>=r)),
417                            _ => IConst(std::f64::NAN),  // unreachable
418                        };
419                        continue;
420                    }
421                }
422                out = match op {
423                    EEQ => IEQ(instr_to_ic!(cslab,out), instr_to_ic!(cslab,instr)),
424                    ENE => INE(instr_to_ic!(cslab,out), instr_to_ic!(cslab,instr)),
425                    ELT => ILT(instr_to_ic!(cslab,out), instr_to_ic!(cslab,instr)),
426                    EGT => IGT(instr_to_ic!(cslab,out), instr_to_ic!(cslab,instr)),
427                    ELTE => ILTE(instr_to_ic!(cslab,out), instr_to_ic!(cslab,instr)),
428                    EGTE => IGTE(instr_to_ic!(cslab,out), instr_to_ic!(cslab,instr)),
429                    _ => IConst(std::f64::NAN),  // unreachable
430                };
431            }
432            return out;
433        }
434
435        match lowest_op {
436            EOR => {
437                let mut xss = Vec::<ExprSlice>::with_capacity(4);
438                self.split(EOR, &mut xss);
439                let mut out = IConst(0.0); let mut out_set = false;
440                for xs in xss.iter() {
441                    let instr = xs.compile(pslab,cslab);
442                    if out_set {
443                        out = IOR(cslab.push_instr(out), instr_to_ic!(cslab,instr));
444                    } else {
445                        if let IConst(c) = instr {
446                            if f64_ne!(c,0.0) { return instr; }
447                            // out = instr;     // Skip this 0 value (mostly so I don't complicate my logic in 'if out_set' since I can assume that any set value is non-const).
448                            // out_set = true;
449                        } else {
450                            out = instr;
451                            out_set = true;
452                        }
453                    }
454                }
455                out
456            }
457            EAND => {
458                let mut xss = Vec::<ExprSlice>::with_capacity(4);
459                self.split(EAND, &mut xss);
460                let mut out = IConst(1.0); let mut out_set = false;
461                for xs in xss.iter() {
462                    let instr = xs.compile(pslab,cslab);
463                    if let IConst(c) = instr {
464                        if f64_eq!(c,0.0) { return instr; }
465                    }
466                    if out_set {
467                        if let IConst(_) = out {
468                            // If we get here, we know that the const is non-zero.
469                            out = instr;
470                        } else {
471                            out = IAND(cslab.push_instr(out), instr_to_ic!(cslab,instr));
472                        }
473                    } else {
474                        out = instr;
475                        out_set = true;
476                    }
477                }
478                out
479            }
480            EAdd => {
481                let mut xss = Vec::<ExprSlice>::with_capacity(4);
482                self.split(EAdd, &mut xss);
483                let mut instrs = Vec::<Instruction>::with_capacity(xss.len());
484                for xs in xss {
485                    let instr = xs.compile(pslab,cslab);
486                    if let IAdd(li,ric) = instr {
487                        push_add_leaves(&mut instrs,cslab,li,ric);  // Flatten nested structures like "x - 1 + 2 - 3".
488                    } else {
489                        instrs.push(instr);
490                    }
491                }
492                compile_add(instrs,cslab)
493            }
494            ESub => {
495                // Note: We don't need to push_add_leaves from here because Sub has a higher precedence than Add.
496
497                let mut xss = Vec::<ExprSlice>::with_capacity(4);
498                self.split(ESub, &mut xss);
499                let mut instrs = Vec::<Instruction>::with_capacity(xss.len());
500                for (i,xs) in xss.into_iter().enumerate() {
501                    let instr = xs.compile(pslab,cslab);
502                    if i==0 {
503                        instrs.push(instr);
504                    } else {
505                        instrs.push(neg_wrap(instr,cslab));
506                    }
507                }
508                compile_add(instrs,cslab)
509            }
510            EMul => {
511                let mut xss = Vec::<ExprSlice>::with_capacity(4);
512                self.split(EMul, &mut xss);
513                let mut instrs = Vec::<Instruction>::with_capacity(xss.len());
514                for xs in xss {
515                    let instr = xs.compile(pslab,cslab);
516                    if let IMul(li,ric) = instr {
517                        push_mul_leaves(&mut instrs,cslab,li,ric);  // Flatten nested structures like "deg/360 * 2*pi()".
518                    } else {
519                        instrs.push(instr);
520                    }
521                }
522                compile_mul(instrs,cslab)
523            }
524            EDiv => {
525                // Note: We don't need to push_mul_leaves from here because Div has a higher precedence than Mul.
526
527                let mut xss = Vec::<ExprSlice>::with_capacity(4);
528                self.split(EDiv, &mut xss);
529                let mut instrs = Vec::<Instruction>::with_capacity(xss.len());
530                for (i,xs) in xss.into_iter().enumerate() {
531                    let instr = xs.compile(pslab,cslab);
532                    if i==0 {
533                        instrs.push(instr);
534                    } else {
535                        instrs.push(inv_wrap(instr,cslab));
536                    }
537                }
538                compile_mul(instrs,cslab)
539            }
540//          EDiv => {
541//              let mut xss = Vec::<ExprSlice>::with_capacity(4);
542//              self.split(EDiv, &mut xss);
543//              let mut out = IConst(1.0); let mut out_set = false;
544//              let mut const_prod = 1.0;
545//              let mut is_first = true;
546//              for xs in xss.iter() {
547//                  let instr = xs.compile(pslab,cslab);
548//                  if let IConst(c) = instr {
549//                      if is_first {
550//                          const_prod *= c;  // Floats don't overflow.
551//                      } else {
552//                          const_prod /= c;
553//                      }
554//                  } else {
555//                      if is_first {
556//                          if out_set {
557//                              out = IMul(cslab.push_instr(out), cslab.push_instr(instr));
558//                          } else {
559//                              out = instr;
560//                              out_set = true;
561//                          }
562//                      } else {
563//                          let instr = inv_wrap(instr,cslab);
564//                          if out_set {
565//                              out = IMul(cslab.push_instr(out), cslab.push_instr(instr));
566//                          } else {
567//                              out = instr;
568//                              out_set = true;
569//                          }
570//                      }
571//                  }
572//                  is_first = false;
573//              }
574//              if f64_ne!(const_prod,1.0) {
575//                  if out_set {
576//                      out = IMul(cslab.push_instr(out), cslab.push_instr(IConst(const_prod)));
577//                  } else {
578//                      out = IConst(const_prod);
579//                  }
580//              }
581//              out
582//          }
583            EMod => {
584                let mut xss = Vec::<ExprSlice>::with_capacity(2);
585                self.split(EMod, &mut xss);
586                let mut out = IConst(0.0); let mut out_set = false;
587                for xs in xss.iter() {
588                    let instr = xs.compile(pslab,cslab);
589                    if out_set {
590                        if let IConst(dividend) = out {
591                            if let IConst(divisor) = instr {
592                                out = IConst(dividend%divisor);
593                                continue;
594                            }
595                        }
596                        out = IMod{dividend:instr_to_ic!(cslab,out), divisor:instr_to_ic!(cslab,instr)};
597                    } else {
598                        out = instr;
599                        out_set = true;
600                    }
601                }
602                out
603            }
604            EExp => {  // Right-to-Left Associativity
605                let mut xss = Vec::<ExprSlice>::with_capacity(2);
606                self.split(EExp, &mut xss);
607                let mut out = IConst(0.0); let mut out_set = false;
608                for xs in xss.into_iter().rev() {
609                    let instr = xs.compile(pslab,cslab);
610                    if out_set {
611                        if let IConst(power) = out {
612                            if let IConst(base) = instr {
613                                out = IConst(base.powf(power));
614                                continue;
615                            }
616                        }
617                        out = IExp{base:instr_to_ic!(cslab,instr), power:instr_to_ic!(cslab,out)};
618                    } else {
619                        out = instr;
620                        out_set = true;
621                    }
622                }
623                out
624            }
625//          EExp => {  // Left-to-Right Associativity
626//              let mut xss = Vec::<ExprSlice>::with_capacity(2);
627//              self.split(EExp, &mut xss);
628//              let mut pow_instrs = Vec::<Instruction>::with_capacity(xss.len()-1);
629//              let mut base = IConst(0.0);
630//              for (i,xs) in xss.into_iter().enumerate() {
631//                  let instr = xs.compile(pslab,cslab);
632//                  if i==0 {
633//                      base = instr;
634//                  } else {
635//                      pow_instrs.push(instr);
636//                  }
637//              }
638//              let power = compile_mul(pow_instrs,cslab);
639//              if let IConst(b) = base {
640//                  if let IConst(p) = power {
641//                      return IConst(b.powf(p));
642//                  }
643//              }
644//              IExp{base:cslab.push_instr(base), power:cslab.push_instr(power)}
645//          }
646            ENE | EEQ | EGTE | ELTE | EGT | ELT => IConst(std::f64::NAN),  // unreachable
647        }
648    }
649}
650
651impl Compiler for Expression {
652    fn compile(&self, pslab:&ParseSlab, cslab:&mut CompileSlab) -> Instruction {
653        let top = ExprSlice::from_expr(&self);
654        top.compile(pslab,cslab)
655    }
656}
657
658impl Compiler for Value {
659    fn compile(&self, pslab:&ParseSlab, cslab:&mut CompileSlab) -> Instruction {
660        match self {
661            Value::EConstant(c) => IConst(*c),
662            Value::EUnaryOp(u) => u.compile(pslab,cslab),
663            Value::EStdFunc(f) => f.compile(pslab,cslab),
664            Value::EPrintFunc(pf) => IPrintFunc(pf.clone()),
665        }
666    }
667}
668
669impl Compiler for UnaryOp {
670    fn compile(&self, pslab:&ParseSlab, cslab:&mut CompileSlab) -> Instruction {
671        match self {
672            EPos(i) => get_val!(pslab,i).compile(pslab,cslab),
673            ENeg(i) => {
674                let instr = get_val!(pslab,i).compile(pslab,cslab);
675                if let IConst(c) = instr {
676                    IConst(-c)
677                } else {
678                    neg_wrap(instr,cslab)
679                }
680            }
681            ENot(i) => {
682                let instr = get_val!(pslab,i).compile(pslab,cslab);
683                if let IConst(c) = instr {
684                    IConst(bool_to_f64!(f64_eq!(c,0.0)))
685                } else {
686                    not_wrap(instr,cslab)
687                }
688            }
689            EParentheses(i) => get_expr!(pslab,i).compile(pslab,cslab),
690        }
691    }
692}
693
694impl Compiler for StdFunc {
695    fn compile(&self, pslab:&ParseSlab, cslab:&mut CompileSlab) -> Instruction {
696        match self {
697            EVar(name) => IVar(name.clone()),
698            #[cfg(feature="unsafe-vars")]
699            EUnsafeVar{name,ptr} => IUnsafeVar{name:name.clone(), ptr:*ptr},
700            EFunc{name, args:xis} => {
701                let mut args = Vec::<IC>::with_capacity(xis.len());
702                for xi in xis {
703                    let instr = get_expr!(pslab,xi).compile(pslab,cslab);
704                    args.push(instr_to_ic!(cslab,instr));
705                }
706                IFunc{name:name.clone(), args}
707            }
708
709            EFuncInt(i) => {
710                let instr = get_expr!(pslab,i).compile(pslab,cslab);
711                if let IConst(c) = instr {
712                    IConst(c.trunc())
713                } else {
714                    IFuncInt(cslab.push_instr(instr))
715                }
716            }
717            EFuncCeil(i) => {
718                let instr = get_expr!(pslab,i).compile(pslab,cslab);
719                if let IConst(c) = instr {
720                    IConst(c.ceil())
721                } else {
722                    IFuncCeil(cslab.push_instr(instr))
723                }
724            }
725            EFuncFloor(i) => {
726                let instr = get_expr!(pslab,i).compile(pslab,cslab);
727                if let IConst(c) = instr {
728                    IConst(c.floor())
729                } else {
730                    IFuncFloor(cslab.push_instr(instr))
731                }
732            }
733            EFuncAbs(i) => {
734                let instr = get_expr!(pslab,i).compile(pslab,cslab);
735                if let IConst(c) = instr {
736                    IConst(c.abs())
737                } else {
738                    IFuncAbs(cslab.push_instr(instr))
739                }
740            }
741            EFuncSign(i) => {
742                let instr = get_expr!(pslab,i).compile(pslab,cslab);
743                if let IConst(c) = instr {
744                    IConst(c.signum())
745                } else {
746                    IFuncSign(cslab.push_instr(instr))
747                }
748            }
749            EFuncLog{base:baseopt, expr:i} => {
750                let base = match baseopt {
751                    Some(bi) => get_expr!(pslab,bi).compile(pslab,cslab),
752                    None => IConst(10.0),
753                };
754                let instr = get_expr!(pslab,i).compile(pslab,cslab);
755                if let IConst(b) = base {
756                    if let IConst(n) = instr {
757                        return IConst(log(b,n));
758                    }
759                }
760                IFuncLog{base:instr_to_ic!(cslab,base), of:instr_to_ic!(cslab,instr)}
761            }
762            EFuncRound{modulus:modopt, expr:i} => {
763                let modulus = match modopt {
764                    Some(mi) => get_expr!(pslab,mi).compile(pslab,cslab),
765                    None => IConst(1.0),
766                };
767                let instr = get_expr!(pslab,i).compile(pslab,cslab);
768                if let IConst(m) = modulus {
769                    if let IConst(n) = instr {
770                        return IConst( (n/m).round() * m );  // Floats don't overflow.
771                    }
772                }
773                IFuncRound{modulus:instr_to_ic!(cslab,modulus), of:instr_to_ic!(cslab,instr)}
774            }
775            EFuncMin{first:fi, rest:is} => {
776                let first = get_expr!(pslab,fi).compile(pslab,cslab);
777                let mut rest = Vec::<Instruction>::with_capacity(is.len());
778                for i in is { rest.push(get_expr!(pslab,i).compile(pslab,cslab)); }
779                let mut out = IConst(0.0); let mut out_set = false;
780                let mut const_min = 0.0; let mut const_min_set = false;
781                if let IConst(f) = first {
782                    const_min = f;
783                    const_min_set = true;
784                } else {
785                    out = first;
786                    out_set = true;
787                }
788                for instr in rest {
789                    if let IConst(f) = instr {
790                        if const_min_set {
791                            if f<const_min { const_min=f; }
792                        } else {
793                            const_min = f;
794                            const_min_set = true;
795                        }
796                    } else {
797                        if out_set {
798                            out = IFuncMin(cslab.push_instr(out), IC::I(cslab.push_instr(instr)));
799                        } else {
800                            out = instr;
801                            out_set = true;
802                        }
803                    }
804                }
805                if const_min_set {
806                    if out_set {
807                        out = IFuncMin(cslab.push_instr(out), IC::C(const_min));
808                    } else {
809                        out = IConst(const_min);
810                        // out_set = true;  // Comment out so the compiler doesn't complain about unused assignments.
811                    }
812                }
813                //assert!(out_set);
814                out
815            }
816            EFuncMax{first:fi, rest:is} => {
817                let first = get_expr!(pslab,fi).compile(pslab,cslab);
818                let mut rest = Vec::<Instruction>::with_capacity(is.len());
819                for i in is { rest.push(get_expr!(pslab,i).compile(pslab,cslab)); }
820                let mut out = IConst(0.0); let mut out_set = false;
821                let mut const_max = 0.0; let mut const_max_set = false;
822                if let IConst(f) = first {
823                    const_max = f;
824                    const_max_set = true;
825                } else {
826                    out = first;
827                    out_set = true;
828                }
829                for instr in rest {
830                    if let IConst(f) = instr {
831                        if const_max_set {
832                            if f>const_max { const_max=f; }
833                        } else {
834                            const_max = f;
835                            const_max_set = true;
836                        }
837                    } else {
838                        if out_set {
839                            out = IFuncMax(cslab.push_instr(out), IC::I(cslab.push_instr(instr)));
840                        } else {
841                            out = instr;
842                            out_set = true;
843                        }
844                    }
845                }
846                if const_max_set {
847                    if out_set {
848                        out = IFuncMax(cslab.push_instr(out), IC::C(const_max));
849                    } else {
850                        out = IConst(const_max);
851                        // out_set = true;  // Comment out so the compiler doesn't complain about unused assignments.
852                    }
853                }
854                //assert!(out_set);
855                out
856            }
857
858            EFuncE => IConst(std::f64::consts::E),
859            EFuncPi => IConst(std::f64::consts::PI),
860
861            EFuncSin(i) => {
862                let instr = get_expr!(pslab,i).compile(pslab,cslab);
863                if let IConst(c) = instr {
864                    IConst(c.sin())
865                } else {
866                    IFuncSin(cslab.push_instr(instr))
867                }
868            }
869            EFuncCos(i) => {
870                let instr = get_expr!(pslab,i).compile(pslab,cslab);
871                if let IConst(c) = instr {
872                    IConst(c.cos())
873                } else {
874                    IFuncCos(cslab.push_instr(instr))
875                }
876            }
877            EFuncTan(i) => {
878                let instr = get_expr!(pslab,i).compile(pslab,cslab);
879                if let IConst(c) = instr {
880                    IConst(c.tan())
881                } else {
882                    IFuncTan(cslab.push_instr(instr))
883                }
884            }
885            EFuncASin(i) => {
886                let instr = get_expr!(pslab,i).compile(pslab,cslab);
887                if let IConst(c) = instr {
888                    IConst(c.asin())
889                } else {
890                    IFuncASin(cslab.push_instr(instr))
891                }
892            }
893            EFuncACos(i) => {
894                let instr = get_expr!(pslab,i).compile(pslab,cslab);
895                if let IConst(c) = instr {
896                    IConst(c.acos())
897                } else {
898                    IFuncACos(cslab.push_instr(instr))
899                }
900            }
901            EFuncATan(i) => {
902                let instr = get_expr!(pslab,i).compile(pslab,cslab);
903                if let IConst(c) = instr {
904                    IConst(c.atan())
905                } else {
906                    IFuncATan(cslab.push_instr(instr))
907                }
908            }
909            EFuncSinH(i) => {
910                let instr = get_expr!(pslab,i).compile(pslab,cslab);
911                if let IConst(c) = instr {
912                    IConst(c.sinh())
913                } else {
914                    IFuncSinH(cslab.push_instr(instr))
915                }
916            }
917            EFuncCosH(i) => {
918                let instr = get_expr!(pslab,i).compile(pslab,cslab);
919                if let IConst(c) = instr {
920                    IConst(c.cosh())
921                } else {
922                    IFuncCosH(cslab.push_instr(instr))
923                }
924            }
925            EFuncTanH(i) => {
926                let instr = get_expr!(pslab,i).compile(pslab,cslab);
927                if let IConst(c) = instr {
928                    IConst(c.tanh())
929                } else {
930                    IFuncTanH(cslab.push_instr(instr))
931                }
932            }
933            EFuncASinH(i) => {
934                let instr = get_expr!(pslab,i).compile(pslab,cslab);
935                if let IConst(c) = instr {
936                    IConst(c.asinh())
937                } else {
938                    IFuncASinH(cslab.push_instr(instr))
939                }
940            }
941            EFuncACosH(i) => {
942                let instr = get_expr!(pslab,i).compile(pslab,cslab);
943                if let IConst(c) = instr {
944                    IConst(c.acosh())
945                } else {
946                    IFuncACosH(cslab.push_instr(instr))
947                }
948            }
949            EFuncATanH(i) => {
950                let instr = get_expr!(pslab,i).compile(pslab,cslab);
951                if let IConst(c) = instr {
952                    IConst(c.atanh())
953                } else {
954                    IFuncATanH(cslab.push_instr(instr))
955                }
956            }
957        }
958    }
959}
960