use std::cell::RefCell;
#[cfg(feature = "unsafe-vars")]
use crate::parser::StdFunc::EUnsafeVar;
use crate::{parser::{
BinaryOp::{
self, EAdd, EDiv, EExp, EMod, EMul, ESub, EAND, EEQ, EGT, EGTE, ELT, ELTE, ENE, EOR,
},
ExprPair, Expression, PrintFunc,
StdFunc::{
self, EFunc, EFuncACos, EFuncACosH, EFuncASin, EFuncASinH, EFuncATan, EFuncATanH, EFuncAbs,
EFuncCeil, EFuncCos, EFuncCosH, EFuncE, EFuncFloor, EFuncInt, EFuncLog, EFuncMax, EFuncMin,
EFuncPi, EFuncRound, EFuncSign, EFuncSin, EFuncSinH, EFuncTan, EFuncTanH, EVar,
},
UnaryOp::{self, ENeg, ENot, EParentheses, EPos},
Value,
}, ExpressionI};
use crate::slab::{CompileSlab, ParseSlab};
use crate::Error;
#[macro_export]
macro_rules! bool_to_f64 {
($b:expr) => {
if $b {
1.0_f64
} else {
0.0_f64
}
};
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct InstructionI(pub usize);
#[derive(Debug, PartialEq)]
pub enum IC {
I(InstructionI),
C(f64),
}
macro_rules! instr_to_ic {
($cslab:ident, $instr:ident) => {
match $instr {
IConst(c) => IC::C(c),
_ => IC::I($cslab.push_instr($instr)),
}
};
}
macro_rules! ic_to_instr {
($cslab:expr, $dst:ident, $ic:ident) => {
match $ic {
IC::C(c) => {
$dst = IConst(*c);
&$dst
}
IC::I(i) => get_instr!($cslab, i),
}
};
}
#[derive(Debug, PartialEq)]
pub enum Instruction {
IConst(f64),
INeg(InstructionI),
INot(InstructionI),
IInv(InstructionI),
IAdd(InstructionI, IC),
IMul(InstructionI, IC),
IMod {
dividend: IC,
divisor: IC,
},
IExp {
base: IC,
power: IC,
},
ILT(IC, IC),
ILTE(IC, IC),
IEQ(IC, IC),
INE(IC, IC),
IGTE(IC, IC),
IGT(IC, IC),
IOR(InstructionI, IC),
IAND(InstructionI, IC),
IVar(String),
#[cfg(feature = "unsafe-vars")]
IUnsafeVar {
name: String,
ptr: *const f64,
},
IFunc {
name: String,
args: Vec<IC>,
},
IFuncInt(InstructionI),
IFuncCeil(InstructionI),
IFuncFloor(InstructionI),
IFuncAbs(InstructionI),
IFuncSign(InstructionI),
IFuncLog {
base: IC,
of: IC,
},
IFuncRound {
modulus: IC,
of: IC,
},
IFuncMin(InstructionI, IC),
IFuncMax(InstructionI, IC),
IFuncSin(InstructionI),
IFuncCos(InstructionI),
IFuncTan(InstructionI),
IFuncASin(InstructionI),
IFuncACos(InstructionI),
IFuncATan(InstructionI),
IFuncSinH(InstructionI),
IFuncCosH(InstructionI),
IFuncTanH(InstructionI),
IFuncASinH(InstructionI),
IFuncACosH(InstructionI),
IFuncATanH(InstructionI),
IPrintFunc(PrintFunc), }
use crate::{eval_var, EvalNamespace};
#[cfg(feature = "unsafe-vars")]
use Instruction::IUnsafeVar;
use Instruction::{
IAdd, IConst, IExp, IFunc, IFuncACos, IFuncACosH, IFuncASin, IFuncASinH, IFuncATan, IFuncATanH,
IFuncAbs, IFuncCeil, IFuncCos, IFuncCosH, IFuncFloor, IFuncInt, IFuncLog, IFuncMax, IFuncMin,
IFuncRound, IFuncSign, IFuncSin, IFuncSinH, IFuncTan, IFuncTanH, IInv, IMod, IMul, INeg, INot,
IPrintFunc, IVar, IAND, IEQ, IGT, IGTE, ILT, ILTE, INE, IOR,
};
impl Default for Instruction {
fn default() -> Self {
IConst(std::f64::NAN)
}
}
pub trait Compiler {
fn compile(
&self,
pslab: &ParseSlab,
cslab: &mut CompileSlab,
ns: &mut impl EvalNamespace,
) -> Instruction;
}
#[derive(Debug)]
struct ExprSlice<'s> {
first: &'s Value,
pairs: Vec<&'s ExprPair>,
}
impl<'s> ExprSlice<'s> {
fn new(first: &Value) -> ExprSlice<'_> {
ExprSlice {
first,
pairs: Vec::with_capacity(8),
}
}
fn from_expr(expr: &Expression) -> ExprSlice<'_> {
let mut sl = ExprSlice::new(&expr.first);
for exprpairref in &expr.pairs {
sl.pairs.push(exprpairref);
}
sl
}
fn split(&self, bop: BinaryOp, dst: &mut Vec<ExprSlice<'s>>) {
dst.push(ExprSlice::new(self.first));
for exprpair in &self.pairs {
if exprpair.0 == bop {
dst.push(ExprSlice::new(&exprpair.1));
} else if let Some(cur) = dst.last_mut() {
cur.pairs.push(exprpair);
}
}
}
fn split_multi(
&self,
search: &[BinaryOp],
xsdst: &mut Vec<ExprSlice<'s>>,
opdst: &mut Vec<&'s BinaryOp>,
) {
xsdst.push(ExprSlice::new(self.first));
for exprpair in &self.pairs {
if search.contains(&exprpair.0) {
xsdst.push(ExprSlice::new(&exprpair.1));
opdst.push(&exprpair.0);
} else if let Some(cur) = xsdst.last_mut() {
cur.pairs.push(exprpair);
}
}
}
#[inline]
fn process_comparisons(
&self,
parsed_slab: &ParseSlab,
compiled_slab: &mut CompileSlab,
namespace: &mut impl EvalNamespace
) -> Instruction {
let mut ops = Vec::<&BinaryOp>::with_capacity(4);
let mut xss = Vec::<ExprSlice>::with_capacity(ops.len() + 1);
self.split_multi(&[EEQ, ENE, ELT, EGT, ELTE, EGTE], &mut xss, &mut ops);
let mut out: Instruction = xss.first().map_or(IConst(std::f64::NAN), |xs| {
xs.compile(parsed_slab, compiled_slab, namespace)
});
for (i, op) in ops.into_iter().enumerate() {
let instruction: Instruction = xss.get(i + 1).map_or(IConst(f64::NAN), |xs| {
xs.compile(parsed_slab, compiled_slab, namespace)
});
if let IConst(l) = out {
if let IConst(r) = instruction {
out = match op {
EEQ => IConst(bool_to_f64!(crate::f64_eq!(l, r))),
ENE => IConst(bool_to_f64!(crate::f64_ne!(l, r))),
ELT => IConst(bool_to_f64!(l < r)),
EGT => IConst(bool_to_f64!(l > r)),
ELTE => IConst(bool_to_f64!(l <= r)),
EGTE => IConst(bool_to_f64!(l >= r)),
_ => IConst(std::f64::NAN), };
continue;
}
}
out = match op {
EEQ => IEQ(instr_to_ic!(compiled_slab, out), instr_to_ic!(compiled_slab, instruction)),
ENE => INE(instr_to_ic!(compiled_slab, out), instr_to_ic!(compiled_slab, instruction)),
ELT => ILT(instr_to_ic!(compiled_slab, out), instr_to_ic!(compiled_slab, instruction)),
EGT => IGT(instr_to_ic!(compiled_slab, out), instr_to_ic!(compiled_slab, instruction)),
ELTE => ILTE(instr_to_ic!(compiled_slab, out), instr_to_ic!(compiled_slab, instruction)),
EGTE => IGTE(instr_to_ic!(compiled_slab, out), instr_to_ic!(compiled_slab, instruction)),
_ => IConst(std::f64::NAN), };
}
out
}
#[inline]
fn process_or(
&self,
parsed_slab: &ParseSlab,
compiled_slab: &mut CompileSlab,
namespace: &mut impl EvalNamespace
) -> Instruction {
let mut xss = Vec::<ExprSlice>::with_capacity(4);
self.split(EOR, &mut xss);
let mut out = IConst(0.0);
let mut out_set = false;
for xs in &xss {
let instr = xs.compile(parsed_slab, compiled_slab, namespace);
if out_set {
out = IOR(compiled_slab.push_instr(out), instr_to_ic!(compiled_slab, instr));
} else if let IConst(c) = instr {
if crate::f64_ne!(c, 0.0) {
return instr;
}
} else {
out = instr;
out_set = true;
}
}
out
}
#[inline]
fn process_and(
&self,
parsed_slab: &ParseSlab,
compiled_slab: &mut CompileSlab,
namespace: &mut impl EvalNamespace
) -> Instruction {
let mut xss = Vec::<ExprSlice>::with_capacity(4);
self.split(EAND, &mut xss);
let mut out = IConst(1.0);
let mut out_set = false;
for xs in &xss {
let instr = xs.compile(parsed_slab, compiled_slab, namespace);
if let IConst(c) = instr {
if crate::f64_eq!(c, 0.0) {
return instr;
}
}
if out_set {
if let IConst(_) = out {
out = instr;
} else {
out = IAND(compiled_slab.push_instr(out), instr_to_ic!(compiled_slab, instr));
}
} else {
out = instr;
out_set = true;
}
}
out
}
#[inline]
fn process_addition(
&self,
parsed_slab: &ParseSlab,
compiled_slab: &mut CompileSlab,
namespace: &mut impl EvalNamespace
) -> Instruction {
let mut xss = Vec::<ExprSlice>::with_capacity(4);
self.split(EAdd, &mut xss);
let mut instrs = Vec::<Instruction>::with_capacity(xss.len());
for xs in xss {
let instr = xs.compile(parsed_slab, compiled_slab, namespace);
if let IAdd(li, ric) = instr {
push_add_leaves(&mut instrs, compiled_slab, li, &ric); } else {
instrs.push(instr);
}
}
compile_add(instrs, compiled_slab)
}
#[inline]
fn process_subtraction(&self, parsed_slab: &ParseSlab, compiled_slab: &mut CompileSlab, namespace: &mut impl EvalNamespace) -> Instruction {
let mut xss = Vec::<ExprSlice>::with_capacity(4);
self.split(ESub, &mut xss);
let mut instrs = Vec::<Instruction>::with_capacity(xss.len());
for (i, xs) in xss.into_iter().enumerate() {
let instr = xs.compile(parsed_slab, compiled_slab, namespace);
if i == 0 {
instrs.push(instr);
} else {
instrs.push(neg_wrap(instr, compiled_slab));
}
}
compile_add(instrs, compiled_slab)
}
#[inline]
fn process_multiplication(
&self,
parsed_slab: &ParseSlab,
compiled_slab: &mut CompileSlab,
namespace: &mut impl EvalNamespace
) -> Instruction {
let mut xss = Vec::<ExprSlice>::with_capacity(4);
self.split(EMul, &mut xss);
let mut instrs = Vec::<Instruction>::with_capacity(xss.len());
for xs in xss {
let instr = xs.compile(parsed_slab, compiled_slab, namespace);
if let IMul(li, ric) = instr {
push_mul_leaves(&mut instrs, compiled_slab, li, &ric); } else {
instrs.push(instr);
}
}
compile_mul(instrs, compiled_slab)
}
}
macro_rules! process_fn {
($name:ident, $operation:ident, $fallback:ident) => {
#[inline]
fn $name(
parsed_slab: &ParseSlab,
compiled_slab: &mut CompileSlab,
namespace: &mut impl EvalNamespace,
expr: ExpressionI
) -> Instruction {
let instruction = get_expr!(parsed_slab, expr).compile(parsed_slab, compiled_slab, namespace);
if let IConst(target) = instruction {
IConst(target.$operation())
} else {
$fallback(compiled_slab.push_instr(instruction))
}
}
};
}
#[macro_export]
macro_rules! f64_eq {
($l:ident, $r:literal) => {
($l - $r).abs() <= 8.0 * std::f64::EPSILON
};
($l:ident, $r:ident) => {
($l - $r).abs() <= 8.0 * std::f64::EPSILON
};
($l:expr, $r:literal) => {
($l - $r).abs() <= 8.0 * std::f64::EPSILON
};
($l:expr, $r:expr) => {
(($l) - ($r)).abs() <= 8.0 * std::f64::EPSILON
};
}
#[macro_export]
macro_rules! f64_ne {
($l:ident, $r:literal) => {
($l - $r).abs() > 8.0 * std::f64::EPSILON
};
($l:ident, $r:ident) => {
($l - $r).abs() > 8.0 * std::f64::EPSILON
};
($l:expr, $r:literal) => {
($l - $r).abs() > 8.0 * std::f64::EPSILON
};
($l:expr, $r:expr) => {
(($l) - ($r)).abs() > 8.0 * std::f64::EPSILON
};
}
fn neg_wrap(instr: Instruction, cslab: &mut CompileSlab) -> Instruction {
if let IConst(c) = instr {
IConst(-c)
} else if let INeg(i) = instr {
cslab.take_instr(i)
} else {
INeg(cslab.push_instr(instr))
}
}
fn not_wrap(instr: Instruction, cslab: &mut CompileSlab) -> Instruction {
if let IConst(c) = instr {
IConst(bool_to_f64!(f64_eq!(c, 0.0)))
} else if let INot(i) = instr {
cslab.take_instr(i)
} else {
INot(cslab.push_instr(instr))
}
}
fn inv_wrap(instr: Instruction, cslab: &mut CompileSlab) -> Instruction {
if let IConst(c) = instr {
IConst(1.0 / c)
} else if let IInv(i) = instr {
cslab.take_instr(i)
} else {
IInv(cslab.push_instr(instr))
}
}
fn compile_mul(instrs: Vec<Instruction>, cslab: &mut CompileSlab) -> Instruction {
let mut out = IConst(1.0);
let mut out_set = false;
let mut const_prod = 1.0;
for instr in instrs {
if let IConst(c) = instr {
const_prod *= c; } else if out_set {
out = IMul(cslab.push_instr(out), IC::I(cslab.push_instr(instr)));
} else {
out = instr;
out_set = true;
}
}
if f64_ne!(const_prod, 1.0) {
if out_set {
out = IMul(cslab.push_instr(out), IC::C(const_prod));
} else {
out = IConst(const_prod);
}
}
out
}
fn compile_add(instrs: Vec<Instruction>, cslab: &mut CompileSlab) -> Instruction {
let mut out = IConst(0.0);
let mut out_set = false;
let mut const_sum = 0.0;
for instr in instrs {
if let IConst(c) = instr {
const_sum += c; } else if out_set {
out = IAdd(cslab.push_instr(out), IC::I(cslab.push_instr(instr)));
} else {
out = instr;
out_set = true;
}
}
if f64_ne!(const_sum, 0.0) {
if out_set {
out = IAdd(cslab.push_instr(out), IC::C(const_sum));
} else {
out = IConst(const_sum);
}
}
out
}
pub(crate) fn log(base: f64, n: f64) -> f64 {
if f64_eq!(base, 2.0) {
return n.log2();
}
if f64_eq!(base, 10.0) {
return n.log10();
}
n.log(base)
}
fn push_mul_leaves(
instrs: &mut Vec<Instruction>,
cslab: &mut CompileSlab,
li: InstructionI,
ric: &IC,
) {
match *ric {
IC::I(ri) => {
let instr = cslab.take_instr(ri);
if let IMul(rli, rric) = instr {
push_mul_leaves(instrs, cslab, rli, &rric);
} else {
instrs.push(instr);
}
}
IC::C(c) => instrs.push(IConst(c)),
};
let instr = cslab.take_instr(li);
if let IMul(lli, lric) = instr {
push_mul_leaves(instrs, cslab, lli, &lric);
} else {
instrs.push(instr);
}
}
fn push_add_leaves(
instrs: &mut Vec<Instruction>,
cslab: &mut CompileSlab,
li: InstructionI,
ric: &IC,
) {
match *ric {
IC::I(ri) => {
let instr = cslab.take_instr(ri);
if let IAdd(rli, rric) = instr {
push_add_leaves(instrs, cslab, rli, &rric);
} else {
instrs.push(instr);
}
}
IC::C(c) => instrs.push(IConst(c)),
};
let instr = cslab.take_instr(li);
if let IAdd(lli, lric) = instr {
push_add_leaves(instrs, cslab, lli, &lric);
} else {
instrs.push(instr);
}
}
impl Compiler for ExprSlice<'_> {
fn compile(
&self,
parsed_slab: &ParseSlab,
compiled_slab: &mut CompileSlab,
namespace: &mut impl EvalNamespace,
) -> Instruction {
let mut lowest_op = match self.pairs.first() {
Some(p0) => p0.0,
None => return self.first.compile(parsed_slab, compiled_slab, namespace),
};
for exprpair in &self.pairs {
if exprpair.0 < lowest_op {
lowest_op = exprpair.0;
}
}
if lowest_op == EEQ
|| lowest_op == ENE
|| lowest_op == ELT
|| lowest_op == EGT
|| lowest_op == ELTE
|| lowest_op == EGTE
{
return self.process_comparisons(parsed_slab, compiled_slab, namespace)
}
match lowest_op {
EOR => self.process_or(parsed_slab, compiled_slab, namespace),
EAND => self.process_and(parsed_slab, compiled_slab, namespace),
EAdd => self.process_addition(parsed_slab, compiled_slab, namespace),
ESub => self.process_subtraction(parsed_slab, compiled_slab, namespace),
EMul => self.process_multiplication(parsed_slab, compiled_slab, namespace),
EDiv => {
let mut xss = Vec::<ExprSlice>::with_capacity(4);
self.split(EDiv, &mut xss);
let mut instrs = Vec::<Instruction>::with_capacity(xss.len());
for (i, xs) in xss.into_iter().enumerate() {
let instr = xs.compile(parsed_slab, compiled_slab, namespace);
if i == 0 {
instrs.push(instr);
} else {
instrs.push(inv_wrap(instr, compiled_slab));
}
}
compile_mul(instrs, compiled_slab)
}
EMod => {
let mut xss = Vec::<ExprSlice>::with_capacity(2);
self.split(EMod, &mut xss);
let mut out = IConst(0.0);
let mut out_set = false;
for xs in &xss {
let instr = xs.compile(parsed_slab, compiled_slab, namespace);
if out_set {
if let IConst(dividend) = out {
if let IConst(divisor) = instr {
out = IConst(dividend % divisor);
continue;
}
}
out = IMod {
dividend: instr_to_ic!(compiled_slab, out),
divisor: instr_to_ic!(compiled_slab, instr),
};
} else {
out = instr;
out_set = true;
}
}
out
}
EExp => {
let mut xss = Vec::<ExprSlice>::with_capacity(2);
self.split(EExp, &mut xss);
let mut out = IConst(0.0);
let mut out_set = false;
for xs in xss.into_iter().rev() {
let instr = xs.compile(parsed_slab, compiled_slab, namespace);
if out_set {
if let IConst(power) = out {
if let IConst(base) = instr {
out = IConst(base.powf(power));
continue;
}
}
out = IExp {
base: instr_to_ic!(compiled_slab, instr),
power: instr_to_ic!(compiled_slab, out),
};
} else {
out = instr;
out_set = true;
}
}
out
}
ENE | EEQ | EGTE | ELTE | EGT | ELT => IConst(std::f64::NAN), }
}
}
impl Compiler for Expression {
fn compile(
&self,
parsed_slab: &ParseSlab,
compiled_slab: &mut CompileSlab,
ns: &mut impl EvalNamespace,
) -> Instruction {
let top = ExprSlice::from_expr(self);
top.compile(parsed_slab, compiled_slab, ns)
}
}
impl Compiler for Value {
fn compile(
&self,
parsed_slab: &ParseSlab,
compiled_slab: &mut CompileSlab,
ns: &mut impl EvalNamespace,
) -> Instruction {
match self {
Self::EConstant(c) => IConst(*c),
Self::EUnaryOp(u) => u.compile(parsed_slab, compiled_slab, ns),
Self::EStdFunc(f) => f.compile(parsed_slab, compiled_slab, ns),
Self::EPrintFunc(pf) => IPrintFunc(pf.clone()),
}
}
}
impl Compiler for UnaryOp {
fn compile(
&self,
parsed_slab: &ParseSlab,
compiled_slab: &mut CompileSlab,
ns: &mut impl EvalNamespace,
) -> Instruction {
match self {
EPos(i) => get_val!(parsed_slab, i).compile(parsed_slab, compiled_slab, ns),
ENeg(i) => {
let instr = get_val!(parsed_slab, i).compile(parsed_slab, compiled_slab, ns);
if let IConst(c) = instr {
IConst(-c)
} else {
neg_wrap(instr, compiled_slab)
}
}
ENot(i) => {
let instr = get_val!(parsed_slab, i).compile(parsed_slab, compiled_slab, ns);
if let IConst(c) = instr {
IConst(bool_to_f64!(f64_eq!(c, 0.0)))
} else {
not_wrap(instr, compiled_slab)
}
}
EParentheses(i) => get_expr!(parsed_slab, i).compile(parsed_slab, compiled_slab, ns),
}
}
}
impl StdFunc {
#[inline]
fn process_custom_fn(
parsed_slab: &ParseSlab,
compiled_slab: &mut CompileSlab,
namespace: &mut impl EvalNamespace,
name: &String,
expressions: &Vec<ExpressionI>,
celled_parsed_slab: &RefCell<String>
) -> Instruction {
let mut args = Vec::<IC>::with_capacity(expressions.len());
let mut f64_args = Vec::<f64>::with_capacity(expressions.len());
let mut is_all_const = true;
for expr in expressions {
let instr = get_expr!(parsed_slab, expr).compile(parsed_slab, compiled_slab, namespace);
if let IConst(c) = instr {
f64_args.push(c);
} else {
is_all_const = false;
}
args.push(instr_to_ic!(compiled_slab, instr));
}
if is_all_const {
let computed_value = eval_var!(namespace, name, f64_args, &mut celled_parsed_slab.borrow_mut());
computed_value.map_or_else(|_| IFunc { name: name.clone(), args }, IConst)
} else {
IFunc {
name: name.clone(),
args,
}
}
}
#[inline]
fn process_int_fn(
parsed_slab: &ParseSlab,
compiled_slab: &mut CompileSlab,
namespace: &mut impl EvalNamespace,
expression: ExpressionI
) -> Instruction {
let instr = get_expr!(parsed_slab, expression).compile(parsed_slab, compiled_slab, namespace);
if let IConst(c) = instr {
IConst(c.trunc())
} else {
IFuncInt(compiled_slab.push_instr(instr))
}
}
#[inline]
fn process_ceil_fn(
parsed_slab: &ParseSlab,
compiled_slab: &mut CompileSlab,
namespace: &mut impl EvalNamespace,
expr: ExpressionI
) -> Instruction {
let instr = get_expr!(parsed_slab, expr).compile(parsed_slab, compiled_slab, namespace);
if let IConst(c) = instr {
IConst(c.ceil())
} else {
IFuncCeil(compiled_slab.push_instr(instr))
}
}
#[inline]
fn process_floor_fn(
parsed_slab: &ParseSlab,
compiled_slab: &mut CompileSlab,
namespace: &mut impl EvalNamespace,
expr: ExpressionI
) -> Instruction {
let instr = get_expr!(parsed_slab, expr).compile(parsed_slab, compiled_slab, namespace);
if let IConst(c) = instr {
IConst(c.floor())
} else {
IFuncFloor(compiled_slab.push_instr(instr))
}
}
#[inline]
fn process_abs_fn(
parsed_slab: &ParseSlab,
compiled_slab: &mut CompileSlab,
namespace: &mut impl EvalNamespace,
expr: ExpressionI
) -> Instruction {
let instr = get_expr!(parsed_slab, expr).compile(parsed_slab, compiled_slab, namespace);
if let IConst(c) = instr {
IConst(c.abs())
} else {
IFuncAbs(compiled_slab.push_instr(instr))
}
}
#[inline]
fn process_signum(
parsed_slab: &ParseSlab,
compiled_slab: &mut CompileSlab,
namespace: &mut impl EvalNamespace,
expr: ExpressionI
) -> Instruction {
let instr = get_expr!(parsed_slab, expr).compile(parsed_slab, compiled_slab, namespace);
if let IConst(c) = instr {
IConst(c.signum())
} else {
IFuncSign(compiled_slab.push_instr(instr))
}
}
#[inline]
fn process_log(
parsed_slab: &ParseSlab,
compiled_slab: &mut CompileSlab,
namespace: &mut impl EvalNamespace,
base_options: &Option<ExpressionI>,
expr: ExpressionI
) -> Instruction {
let base: Instruction = base_options.as_ref().map_or(IConst(10.0), |bi| get_expr!(parsed_slab, bi).compile(parsed_slab, compiled_slab, namespace));
let instr = get_expr!(parsed_slab, expr).compile(parsed_slab, compiled_slab, namespace);
if let IConst(b) = base {
if let IConst(n) = instr {
return IConst(log(b, n));
}
}
IFuncLog {
base: instr_to_ic!(compiled_slab, base),
of: instr_to_ic!(compiled_slab, instr),
}
}
#[inline]
fn process_round(
parsed_slab: &ParseSlab,
compiled_slab: &mut CompileSlab,
namespace: &mut impl EvalNamespace,
mod_option: &Option<ExpressionI>,
expr: ExpressionI
) -> Instruction {
let modulus: Instruction = mod_option.as_ref().map_or(IConst(1.0), |mi| get_expr!(parsed_slab, mi).compile(parsed_slab, compiled_slab, namespace));
let instr = get_expr!(parsed_slab, expr).compile(parsed_slab, compiled_slab, namespace);
if let IConst(m) = modulus {
if let IConst(n) = instr {
return IConst((n / m).round() * m); }
}
IFuncRound {
modulus: instr_to_ic!(compiled_slab, modulus),
of: instr_to_ic!(compiled_slab, instr),
}
}
#[inline]
fn process_min(
parsed_slab: &ParseSlab,
compiled_slab: &mut CompileSlab,
namespace: &mut impl EvalNamespace,
fi: ExpressionI,
is: &Vec<ExpressionI>
) -> Instruction {
let first = get_expr!(parsed_slab, fi).compile(parsed_slab, compiled_slab, namespace);
let mut rest = Vec::<Instruction>::with_capacity(is.len());
for i in is {
rest.push(get_expr!(parsed_slab, i).compile(parsed_slab, compiled_slab, namespace));
}
let mut out = IConst(0.0);
let mut out_set = false;
let mut const_min = 0.0;
let mut const_min_set = false;
if let IConst(f) = first {
const_min = f;
const_min_set = true;
} else {
out = first;
out_set = true;
}
for instr in rest {
if let IConst(f) = instr {
if const_min_set {
if f < const_min {
const_min = f;
}
} else {
const_min = f;
const_min_set = true;
}
} else if out_set {
out = IFuncMin(compiled_slab.push_instr(out), IC::I(compiled_slab.push_instr(instr)));
} else {
out = instr;
out_set = true;
}
}
if const_min_set {
if out_set {
out = IFuncMin(compiled_slab.push_instr(out), IC::C(const_min));
} else {
out = IConst(const_min);
}
}
out
}
#[inline]
fn process_max(
parsed_slab: &ParseSlab,
compiled_slab: &mut CompileSlab,
namespace: &mut impl EvalNamespace,
fi: ExpressionI,
is: &Vec<ExpressionI>
) -> Instruction {
let first = get_expr!(parsed_slab, fi).compile(parsed_slab, compiled_slab, namespace);
let mut rest = Vec::<Instruction>::with_capacity(is.len());
for i in is {
rest.push(get_expr!(parsed_slab, i).compile(parsed_slab, compiled_slab, namespace));
}
let mut out = IConst(0.0);
let mut out_set = false;
let mut const_max = 0.0;
let mut const_max_set = false;
if let IConst(f) = first {
const_max = f;
const_max_set = true;
} else {
out = first;
out_set = true;
}
for instr in rest {
if let IConst(f) = instr {
if const_max_set {
if f > const_max {
const_max = f;
}
} else {
const_max = f;
const_max_set = true;
}
} else if out_set {
out = IFuncMax(compiled_slab.push_instr(out), IC::I(compiled_slab.push_instr(instr)));
} else {
out = instr;
out_set = true;
}
}
if const_max_set {
if out_set {
out = IFuncMax(compiled_slab.push_instr(out), IC::C(const_max));
} else {
out = IConst(const_max);
}
}
out
}
process_fn!(process_sin, sin, IFuncSin);
process_fn!(process_cos, cos, IFuncCos);
process_fn!(process_tan, tan, IFuncTan);
process_fn!(process_asin, asin, IFuncASin);
process_fn!(process_acos, acos, IFuncACos);
process_fn!(process_atan, atan, IFuncATan);
}
impl Compiler for StdFunc {
fn compile(
&self,
parsed_slab: &ParseSlab,
compiled_slab: &mut CompileSlab,
namespace: &mut impl EvalNamespace,
) -> Instruction {
let celled_parsed_slab = RefCell::from(parsed_slab.char_buf.clone());
match self {
EVar(name) => IVar(name.clone()),
#[cfg(feature = "unsafe-vars")]
EUnsafeVar { name, ptr } => IUnsafeVar {
name: name.clone(),
ptr: *ptr,
},
EFunc { name, args } => Self::process_custom_fn(parsed_slab, compiled_slab, namespace, name, args, &celled_parsed_slab),
EFuncInt(expr) => Self::process_int_fn(parsed_slab, compiled_slab, namespace, *expr),
EFuncCeil(expr) => Self::process_ceil_fn(parsed_slab, compiled_slab, namespace, *expr),
EFuncFloor(expr) => Self::process_floor_fn(parsed_slab, compiled_slab, namespace, *expr),
EFuncAbs(expr) => Self::process_abs_fn(parsed_slab, compiled_slab, namespace, *expr),
EFuncSign(expr) => Self::process_signum(parsed_slab, compiled_slab, namespace, *expr),
EFuncLog {
base: base_option,
expr,
} => {
Self::process_log(parsed_slab, compiled_slab, namespace, base_option, *expr)
}
EFuncRound {
modulus: mod_option,
expr,
} => {
Self::process_round(parsed_slab, compiled_slab, namespace, mod_option, *expr)
}
EFuncMin {
first: fi,
rest: is,
} => {
Self::process_min(parsed_slab, compiled_slab, namespace, *fi, is)
}
EFuncMax {
first: fi,
rest: is,
} => {
Self::process_max(parsed_slab, compiled_slab, namespace, *fi, is)
}
EFuncE => IConst(std::f64::consts::E),
EFuncPi => IConst(std::f64::consts::PI),
EFuncSin(expr) => Self::process_sin(parsed_slab, compiled_slab, namespace, *expr),
EFuncCos(expr) => Self::process_cos(parsed_slab, compiled_slab, namespace, *expr),
EFuncTan(expr) => Self::process_tan(parsed_slab, compiled_slab, namespace, *expr),
EFuncASin(expr) => Self::process_asin(parsed_slab, compiled_slab, namespace, *expr),
EFuncACos(expr) => Self::process_acos(parsed_slab, compiled_slab, namespace, *expr),
EFuncATan(expr) => Self::process_atan(parsed_slab, compiled_slab, namespace, *expr),
EFuncSinH(i) => {
let instr = get_expr!(parsed_slab, i).compile(parsed_slab, compiled_slab, namespace);
if let IConst(c) = instr {
IConst(c.sinh())
} else {
IFuncSinH(compiled_slab.push_instr(instr))
}
}
EFuncCosH(i) => {
let instr = get_expr!(parsed_slab, i).compile(parsed_slab, compiled_slab, namespace);
if let IConst(c) = instr {
IConst(c.cosh())
} else {
IFuncCosH(compiled_slab.push_instr(instr))
}
}
EFuncTanH(i) => {
let instr = get_expr!(parsed_slab, i).compile(parsed_slab, compiled_slab, namespace);
if let IConst(c) = instr {
IConst(c.tanh())
} else {
IFuncTanH(compiled_slab.push_instr(instr))
}
}
EFuncASinH(i) => {
let instr = get_expr!(parsed_slab, i).compile(parsed_slab, compiled_slab, namespace);
if let IConst(c) = instr {
IConst(c.asinh())
} else {
IFuncASinH(compiled_slab.push_instr(instr))
}
}
EFuncACosH(i) => {
let instr = get_expr!(parsed_slab, i).compile(parsed_slab, compiled_slab, namespace);
if let IConst(c) = instr {
IConst(c.acosh())
} else {
IFuncACosH(compiled_slab.push_instr(instr))
}
}
EFuncATanH(i) => {
let instr = get_expr!(parsed_slab, i).compile(parsed_slab, compiled_slab, namespace);
if let IConst(c) = instr {
IConst(c.atanh())
} else {
IFuncATanH(compiled_slab.push_instr(instr))
}
}
}
}
}