diff --git a/Cargo.toml b/Cargo.toml index b793d76..702b4af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fasteval2" -version = "2.0.3" +version = "2.0.4" authors = ["Pasha Podolsky ", "Christopher Sebastian "] license = "MIT" readme = "README.md" @@ -18,8 +18,8 @@ debug = true lto = true [features] -default = ["alpha-keywords"] +default = ["alpha-keywords", "print-func"] alpha-keywords = [] # Enable 'NaN', 'inf', 'and', 'or' unsafe-vars = [] # tinyexpr-style pointer-based variables. nightly = [] # Enable features that depend on Rust nightly. - +print-func = [] # Enables the builtin print function diff --git a/examples/advanced-vars.rs b/examples/advanced-vars.rs index 7eef671..d4ca7fa 100644 --- a/examples/advanced-vars.rs +++ b/examples/advanced-vars.rs @@ -15,7 +15,7 @@ fn main() -> Result<(), fasteval2::Error> { // The `args.get...` code is the same as: // mydata[args[0] as usize] // ...but it won't panic if either index is out-of-bounds. - "data" => args.get(0).and_then(|f| mydata.get(*f as usize).copied()), + "data" => args.first().and_then(|f| mydata.get(*f as usize).copied()), // A wildcard to handle all undefined names: _ => None, diff --git a/examples/repl.rs b/examples/repl.rs index e557569..6e9a581 100644 --- a/examples/repl.rs +++ b/examples/repl.rs @@ -68,7 +68,7 @@ fn repl() { None => break, }; let mut line = line.trim().to_string(); - if line == "" { + if line.is_empty() { continue; } diff --git a/src/compiler.rs b/src/compiler.rs index dcf0072..a8850e5 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -30,11 +30,11 @@ use crate::parser::{ BinaryOp::{ self, EAdd, EDiv, EExp, EMod, EMul, ESub, EAND, EEQ, EGT, EGTE, ELT, ELTE, ENE, EOR, }, - ExprPair, Expression, PrintFunc, + ExprPair, Expression, 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, + EFuncPi, EFuncRound, EFuncSign, EFuncSin, EFuncSinH, EFuncSqrt, EFuncTan, EFuncTanH, EVar, }, UnaryOp::{self, ENeg, ENot, EParentheses, EPos}, Value, @@ -42,6 +42,9 @@ use crate::parser::{ use crate::slab::{CompileSlab, ParseSlab}; use crate::Error; +#[cfg(feature = "print-func")] +use crate::parser::PrintFunc; + /// `true` --> `1.0`, `false` --> `0.0` #[macro_export] macro_rules! bool_to_f64 { @@ -167,7 +170,9 @@ pub enum Instruction { IFuncASinH(InstructionI), IFuncACosH(InstructionI), IFuncATanH(InstructionI), + IFuncSqrt(InstructionI), + #[cfg(feature = "print-func")] IPrintFunc(PrintFunc), // Not optimized (it would be pointless because of i/o bottleneck). } use crate::{eval_var, EvalNamespace}; @@ -176,10 +181,13 @@ 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, + IFuncRound, IFuncSign, IFuncSin, IFuncSinH, IFuncSqrt, IFuncTan, IFuncTanH, IInv, IMod, IMul, + INeg, INot, IVar, IAND, IEQ, IGT, IGTE, ILT, ILTE, INE, IOR, }; +#[cfg(feature = "print-func")] +use Instruction::IPrintFunc; + impl Default for Instruction { fn default() -> Self { IConst(std::f64::NAN) @@ -219,15 +227,12 @@ impl<'s> ExprSlice<'s> { sl } fn split(&self, bop: BinaryOp, dst: &mut Vec>) { - dst.push(ExprSlice::new(&self.first)); + dst.push(ExprSlice::new(self.first)); for exprpair in self.pairs.iter() { if exprpair.0 == bop { dst.push(ExprSlice::new(&exprpair.1)); - } else { - match dst.last_mut() { - Some(cur) => cur.pairs.push(exprpair), - None => (), // unreachable - } + } else if let Some(cur) = dst.last_mut() { + cur.pairs.push(exprpair) } } } @@ -237,16 +242,13 @@ impl<'s> ExprSlice<'s> { xsdst: &mut Vec>, opdst: &mut Vec<&'s BinaryOp>, ) { - xsdst.push(ExprSlice::new(&self.first)); + xsdst.push(ExprSlice::new(self.first)); for exprpair in self.pairs.iter() { if search.contains(&exprpair.0) { xsdst.push(ExprSlice::new(&exprpair.1)); opdst.push(&exprpair.0); - } else { - match xsdst.last_mut() { - Some(cur) => cur.pairs.push(exprpair), - None => (), // unreachable - } + } else if let Some(cur) = xsdst.last_mut() { + cur.pairs.push(exprpair) } } } @@ -321,13 +323,11 @@ fn compile_mul(instrs: Vec, cslab: &mut CompileSlab) -> Instruction for instr in instrs { if let IConst(c) = instr { const_prod *= c; // Floats don't overflow. + } else if out_set { + out = IMul(cslab.push_instr(out), IC::I(cslab.push_instr(instr))); } else { - if out_set { - out = IMul(cslab.push_instr(out), IC::I(cslab.push_instr(instr))); - } else { - out = instr; - out_set = true; - } + out = instr; + out_set = true; } } if f64_ne!(const_prod, 1.0) { @@ -346,13 +346,11 @@ fn compile_add(instrs: Vec, cslab: &mut CompileSlab) -> Instruction for instr in instrs { if let IConst(c) = instr { const_sum += c; // Floats don't overflow. + } else if out_set { + out = IAdd(cslab.push_instr(out), IC::I(cslab.push_instr(instr))); } else { - if out_set { - out = IAdd(cslab.push_instr(out), IC::I(cslab.push_instr(instr))); - } else { - out = instr; - out_set = true; - } + out = instr; + out_set = true; } } if f64_ne!(const_sum, 0.0) { @@ -519,17 +517,15 @@ impl Compiler for ExprSlice<'_> { let instr = xs.compile(pslab, cslab, ns); if out_set { out = IOR(cslab.push_instr(out), instr_to_ic!(cslab, instr)); - } else { - if let IConst(c) = instr { - if f64_ne!(c, 0.0) { - return instr; - } - // 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). - // out_set = true; - } else { - out = instr; - out_set = true; + } else if let IConst(c) = instr { + if f64_ne!(c, 0.0) { + return instr; } + // 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). + // out_set = true; + } else { + out = instr; + out_set = true; } } out @@ -747,7 +743,7 @@ impl Compiler for Expression { cslab: &mut CompileSlab, ns: &mut impl EvalNamespace, ) -> Instruction { - let top = ExprSlice::from_expr(&self); + let top = ExprSlice::from_expr(self); top.compile(pslab, cslab, ns) } } @@ -763,6 +759,7 @@ impl Compiler for Value { Value::EConstant(c) => IConst(*c), Value::EUnaryOp(u) => u.compile(pslab, cslab, ns), Value::EStdFunc(f) => f.compile(pslab, cslab, ns), + #[cfg(feature = "print-func")] Value::EPrintFunc(pf) => IPrintFunc(pf.clone()), } } @@ -827,6 +824,7 @@ impl Compiler for StdFunc { } if is_all_const { let computed_value = eval_var!(ns, name, f64_args, unsafe { + #[allow(invalid_reference_casting)] &mut *(&pslab.char_buf as *const _ as *mut _) }); if let Ok(value) = computed_value { @@ -953,13 +951,11 @@ impl Compiler for StdFunc { const_min = f; const_min_set = true; } + } else if out_set { + out = IFuncMin(cslab.push_instr(out), IC::I(cslab.push_instr(instr))); } else { - if out_set { - out = IFuncMin(cslab.push_instr(out), IC::I(cslab.push_instr(instr))); - } else { - out = instr; - out_set = true; - } + out = instr; + out_set = true; } } if const_min_set { @@ -1003,13 +999,11 @@ impl Compiler for StdFunc { const_max = f; const_max_set = true; } + } else if out_set { + out = IFuncMax(cslab.push_instr(out), IC::I(cslab.push_instr(instr))); } else { - if out_set { - out = IFuncMax(cslab.push_instr(out), IC::I(cslab.push_instr(instr))); - } else { - out = instr; - out_set = true; - } + out = instr; + out_set = true; } } if const_max_set { @@ -1123,6 +1117,14 @@ impl Compiler for StdFunc { IFuncATanH(cslab.push_instr(instr)) } } + EFuncSqrt(i) => { + let instr = get_expr!(pslab, i).compile(pslab, cslab, ns); + if let IConst(c) = instr { + IConst(c.sqrt()) + } else { + IFuncSqrt(cslab.push_instr(instr)) + } + } } } } diff --git a/src/evaler.rs b/src/evaler.rs index ff06eed..e9bc878 100644 --- a/src/evaler.rs +++ b/src/evaler.rs @@ -13,11 +13,15 @@ use crate::compiler::{ Instruction::{ self, 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, + IFuncMax, IFuncMin, IFuncRound, IFuncSign, IFuncSin, IFuncSinH, IFuncSqrt, IFuncTan, + IFuncTanH, IInv, IMod, IMul, INeg, INot, IVar, IAND, IEQ, IGT, IGTE, ILT, ILTE, INE, IOR, }, IC, }; + +#[cfg(feature = "print-func")] +use crate::compiler::Instruction::IPrintFunc; + use crate::error::Error; use crate::evalns::EvalNamespace; #[cfg(feature = "unsafe-vars")] @@ -28,16 +32,22 @@ use crate::parser::{ self, EAdd, EDiv, EExp, EMod, EMul, ESub, EAND, EEQ, EGT, EGTE, ELT, ELTE, ENE, EOR, }, Expression, - ExpressionOrString::{EExpr, EStr}, - 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, + EFuncPi, EFuncRound, EFuncSign, EFuncSin, EFuncSinH, EFuncSqrt, EFuncTan, EFuncTanH, EVar, }, UnaryOp::{self, ENeg, ENot, EParentheses, EPos}, - Value::{self, EConstant, EPrintFunc, EStdFunc, EUnaryOp}, + Value::{self, EConstant, EStdFunc, EUnaryOp}, +}; + +#[cfg(feature = "print-func")] +use crate::parser::{ + ExpressionOrString::{EExpr, EStr}, + PrintFunc, + Value::EPrintFunc, }; + use crate::slab::Slab; use std::collections::BTreeSet; @@ -236,10 +246,9 @@ impl Evaler for Expression { }; if op == search { let res = op.binaryop_eval(vals.get(i), vals.get(i + 1)); - match vals.get_mut(i) { - Some(val_ref) => *val_ref = res, - None => (), // unreachable - }; + if let Some(val_ref) = vals.get_mut(i) { + *val_ref = res + } remove_no_panic(vals, i + 1); remove_no_panic(ops, i); } @@ -254,14 +263,13 @@ impl Evaler for Expression { Some(op) => { if *op == search { let res = op.binaryop_eval(vals.get(i), vals.get(i + 1)); - match vals.get_mut(i) { - Some(val_ref) => *val_ref = res, - None => (), // unreachable - }; + if let Some(val_ref) = vals.get_mut(i) { + *val_ref = res + } remove_no_panic(vals, i + 1); remove_no_panic(ops, i); } else { - i = i + 1; + i += 1; } } } @@ -276,14 +284,13 @@ impl Evaler for Expression { Some(op) => { if search.contains(op) { let res = op.binaryop_eval(vals.get(i), vals.get(i + 1)); - match vals.get_mut(i) { - Some(val_ref) => *val_ref = res, - None => (), // unreachable - }; + if let Some(val_ref) = vals.get_mut(i) { + *val_ref = res + } remove_no_panic(vals, i + 1); remove_no_panic(ops, i); } else { - i = i + 1; + i += 1; } } } @@ -320,6 +327,7 @@ impl Evaler for Value { EConstant(_) => (), EUnaryOp(u) => u._var_names(slab, dst), EStdFunc(f) => f._var_names(slab, dst), + #[cfg(feature = "print-func")] EPrintFunc(f) => f._var_names(slab, dst), }; } @@ -328,6 +336,7 @@ impl Evaler for Value { EConstant(c) => Ok(*c), EUnaryOp(u) => u.eval(slab, ns), EStdFunc(f) => f.eval(slab, ns), + #[cfg(feature = "print-func")] EPrintFunc(f) => f.eval(slab, ns), } } @@ -429,7 +438,9 @@ impl Evaler for StdFunc { EFuncInt(xi) | EFuncCeil(xi) | EFuncFloor(xi) | EFuncAbs(xi) | EFuncSign(xi) | EFuncSin(xi) | EFuncCos(xi) | EFuncTan(xi) | EFuncASin(xi) | EFuncACos(xi) | EFuncATan(xi) | EFuncSinH(xi) | EFuncCosH(xi) | EFuncTanH(xi) | EFuncASinH(xi) - | EFuncACosH(xi) | EFuncATanH(xi) => get_expr!(slab.ps, xi)._var_names(slab, dst), + | EFuncACosH(xi) | EFuncATanH(xi) | EFuncSqrt(xi) => { + get_expr!(slab.ps, xi)._var_names(slab, dst) + } EFuncE | EFuncPi => (), EFuncLog { base: opt, expr } | EFuncRound { modulus: opt, expr } => { @@ -455,6 +466,7 @@ impl Evaler for StdFunc { EUnsafeVar { ptr, .. } => unsafe { Ok(**ptr) }, EVar(name) => eval_var!(ns, name, Vec::new(), unsafe { + #[allow(invalid_reference_casting)] &mut *(&slab.ps.char_buf as *const _ as *mut _) }), EFunc { name, args: xis } => { @@ -463,6 +475,7 @@ impl Evaler for StdFunc { args.push(get_expr!(slab.ps, xi).eval(slab, ns)?) } eval_var!(ns, name, args, unsafe { + #[allow(invalid_reference_casting)] &mut *(&slab.ps.char_buf as *const _ as *mut _) }) } @@ -491,6 +504,7 @@ impl Evaler for StdFunc { EFuncASinH(expr_i) => Ok(get_expr!(slab.ps, expr_i).eval(slab, ns)?.asinh()), EFuncACosH(expr_i) => Ok(get_expr!(slab.ps, expr_i).eval(slab, ns)?.acosh()), EFuncATanH(expr_i) => Ok(get_expr!(slab.ps, expr_i).eval(slab, ns)?.atanh()), + EFuncSqrt(expr_i) => Ok(get_expr!(slab.ps, expr_i).eval(slab, ns)?.sqrt()), EFuncRound { modulus: modulus_opt, @@ -547,6 +561,7 @@ impl Evaler for StdFunc { } } +#[cfg(feature = "print-func")] impl Evaler for PrintFunc { fn _var_names(&self, slab: &Slab, dst: &mut BTreeSet) { for x_or_s in &self.0 { @@ -621,7 +636,7 @@ impl Evaler for Instruction { INeg(ii) | INot(ii) | IInv(ii) | IFuncInt(ii) | IFuncCeil(ii) | IFuncFloor(ii) | IFuncAbs(ii) | IFuncSign(ii) | IFuncSin(ii) | IFuncCos(ii) | IFuncTan(ii) | IFuncASin(ii) | IFuncACos(ii) | IFuncATan(ii) | IFuncSinH(ii) | IFuncCosH(ii) - | IFuncTanH(ii) | IFuncASinH(ii) | IFuncACosH(ii) | IFuncATanH(ii) => { + | IFuncTanH(ii) | IFuncASinH(ii) | IFuncACosH(ii) | IFuncATanH(ii) | IFuncSqrt(ii) => { get_instr!(slab.cs, ii)._var_names(slab, dst) } @@ -660,6 +675,7 @@ impl Evaler for Instruction { ic_to_instr!(slab.cs, iconst, ric)._var_names(slab, dst); } + #[cfg(feature = "print-func")] IPrintFunc(pf) => pf._var_names(slab, dst), } } @@ -683,6 +699,7 @@ impl Evaler for Instruction { IInv(i) => Ok(1.0 / eval_compiled_ref!(get_instr!(slab.cs, i), slab, ns)), IVar(name) => eval_var!(ns, name, Vec::new(), unsafe { + #[allow(invalid_reference_casting)] &mut *(&slab.ps.char_buf as *const _ as *mut _) }), IFunc { name, args: ics } => { @@ -691,6 +708,7 @@ impl Evaler for Instruction { args.push(eval_ic_ref!(ic, slab, ns)); } eval_var!(ns, name, args, unsafe { + #[allow(invalid_reference_casting)] &mut *(&slab.ps.char_buf as *const _ as *mut _) }) } @@ -716,6 +734,7 @@ impl Evaler for Instruction { IFuncASinH(i) => Ok(eval_compiled_ref!(get_instr!(slab.cs, i), slab, ns).asinh()), IFuncACosH(i) => Ok(eval_compiled_ref!(get_instr!(slab.cs, i), slab, ns).acosh()), IFuncATanH(i) => Ok(eval_compiled_ref!(get_instr!(slab.cs, i), slab, ns).atanh()), + IFuncSqrt(i) => Ok(eval_compiled_ref!(get_instr!(slab.cs, i), slab, ns).sqrt()), IFuncRound { modulus: modic, @@ -801,6 +820,7 @@ impl Evaler for Instruction { } } + #[cfg(feature = "print-func")] IPrintFunc(pf) => pf.eval(slab, ns), // Put these last because you should be using the eval_compiled*!() macros to eliminate function calls. diff --git a/src/evalns.rs b/src/evalns.rs index 35ad521..99275dd 100644 --- a/src/evalns.rs +++ b/src/evalns.rs @@ -216,7 +216,7 @@ pub trait Cached { fn cache_clear(&mut self); } -//// I don't want to put this into the public API until it is needed. +/// I don't want to put this into the public API until it is needed. // pub trait Layered { // fn push(&mut self); // fn pop(&mut self); @@ -230,6 +230,9 @@ pub trait Cached { /// pub struct EmptyNamespace; +// I think a reference would be more efficient than a Box, but then I would need to use a funky 'let cb=|n|{}; Namespace::new(&cb)' syntax. The Box results in a super convenient pass-the-cb-by-value API interface. +pub type NamespaceCallback<'a> = Box) -> Option + 'a>; + /// `CachedCallbackNamespace` is useful when your variable/function lookups are expensive. /// /// Each variable+args combo will only be looked up once, and then it will be @@ -239,11 +242,11 @@ pub struct EmptyNamespace; /// pub struct CachedCallbackNamespace<'a> { cache: BTreeMap, - cb: Box) -> Option + 'a>, // I think a reference would be more efficient than a Box, but then I would need to use a funky 'let cb=|n|{}; Namespace::new(&cb)' syntax. The Box results in a super convenient pass-the-cb-by-value API interface. + cb: NamespaceCallback<'a>, } -//// I am commenting these out until I need them in real-life. -//// (I don't want to add things to the public API until necessary.) +/// I am commenting these out until I need them in real-life. +/// (I don't want to add things to the public API until necessary.) // pub struct CachedLayeredNamespace<'a> { // caches:Vec>, // cb :Box)->Option + 'a>, @@ -300,11 +303,7 @@ pub type StringToCallbackNamespace<'a> = BTreeMap impl EvalNamespace for StringToCallbackNamespace<'_> { #[inline] fn lookup(&mut self, name: &str, args: Vec, _keybuf: &mut String) -> Option { - if let Some(f) = self.get_mut(name) { - Some(f(args)) - } else { - None - } + self.get_mut(name).map(|f| f(args)) } } @@ -317,11 +316,7 @@ pub type StrToCallbackNamespace<'a> = BTreeMap<&'static str, Box { #[inline] fn lookup(&mut self, name: &str, args: Vec, _keybuf: &mut String) -> Option { - if let Some(f) = self.get_mut(name) { - Some(f(args)) - } else { - None - } + self.get_mut(name).map(|f| f(args)) } } diff --git a/src/parser.rs b/src/parser.rs index aca027d..cf44cff 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -46,7 +46,7 @@ pub struct ValueI(pub usize); /// An `Expression` is the top node of a parsed AST. /// /// It can be `compile()`d or `eval()`d. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Default)] pub struct Expression { pub(crate) first: Value, pub(crate) pairs: Vec, // cap=8 @@ -61,9 +61,13 @@ pub enum Value { EConstant(f64), EUnaryOp(UnaryOp), EStdFunc(StdFunc), + #[cfg(feature = "print-func")] EPrintFunc(PrintFunc), } -use Value::{EConstant, EPrintFunc, EStdFunc, EUnaryOp}; +use Value::{EConstant, EStdFunc, EUnaryOp}; + +#[cfg(feature = "print-func")] +use Value::EPrintFunc; /// Unary Operators #[derive(Debug, PartialEq)] @@ -148,17 +152,19 @@ pub enum StdFunc { EFuncASinH(ExpressionI), EFuncACosH(ExpressionI), EFuncATanH(ExpressionI), + EFuncSqrt(ExpressionI), } #[cfg(feature = "unsafe-vars")] use StdFunc::EUnsafeVar; use StdFunc::{ 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, + EFuncPi, EFuncRound, EFuncSign, EFuncSin, EFuncSinH, EFuncSqrt, EFuncTan, EFuncTanH, EVar, }; /// Represents a `print()` function call in the `fasteval` expression AST. #[derive(Debug, PartialEq)] +#[cfg(feature = "print-func")] pub struct PrintFunc(pub Vec); // cap=8 /// Used by the `print()` function. Can hold an `Expression` or a `String`. @@ -167,8 +173,11 @@ pub enum ExpressionOrString { EExpr(ExpressionI), EStr(String), // cap=64 } + +#[cfg(feature = "print-func")] use ExpressionOrString::{EExpr, EStr}; +#[cfg(feature = "print-func")] impl Clone for PrintFunc { fn clone(&self) -> Self { let mut vec = Vec::::with_capacity(self.0.len()); @@ -284,10 +293,10 @@ impl Parser { } fn is_varname_byte(b: u8, i: usize) -> bool { - (b'A' <= b && b <= b'Z') - || (b'a' <= b && b <= b'z') + b.is_ascii_uppercase() + || b.is_ascii_lowercase() || b == b'_' - || (i > 0 && (b'0' <= b && b <= b'9')) + || (i > 0 && b.is_ascii_digit()) } fn is_varname_byte_opt(bo: Option, i: usize) -> bool { match bo { @@ -352,7 +361,7 @@ impl Parser { }; return Err(Error::UnparsedTokensRemaining(bs_str.to_string())); } - Ok(slab.push_expr(Expression { first, pairs })?) + slab.push_expr(Expression { first, pairs }) } fn read_value( @@ -398,18 +407,18 @@ impl Parser { match peek_n!(bs, toklen) { None => break, Some(b) => { - if b'0' <= b && b <= b'9' || b == b'.' { + if b.is_ascii_digit() || b == b'.' { saw_val = true; sign_ok = false; specials_ok = false; - toklen = toklen + 1; + toklen += 1; } else if sign_ok && (b == b'-' || b == b'+') { sign_ok = false; - toklen = toklen + 1; + toklen += 1; } else if saw_val && (b == b'e' || b == b'E') { suffix_ok = false; sign_ok = true; - toklen = toklen + 1; + toklen += 1; } else if specials_ok && (b == b'N' && peek_is!(bs, toklen + 1, b'a') @@ -422,7 +431,7 @@ impl Parser { { saw_val = true; suffix_ok = false; - toklen = toklen + 3; + toklen += 3; } break; } else { @@ -460,7 +469,7 @@ impl Parser { slab.char_buf.push_str(&exp.to_string()); tok = &slab.char_buf; - toklen = toklen + suffixlen; + toklen += suffixlen; } } } @@ -711,13 +720,15 @@ impl Parser { } Bite(open_parenth) => { // VarNames with Parenthesis are first matched against builtins, then custom. - match varname.as_ref() { + match varname.as_str() { + #[cfg(feature = "print-func")] "print" => Ok(Bite(EPrintFunc(self.read_printfunc( slab, bs, depth, open_parenth, )?))), + _ => Ok(Bite(EStdFunc(self.read_func( varname, slab, @@ -737,7 +748,7 @@ impl Parser { let mut toklen = 0; while Self::is_varname_byte_opt(peek_n!(bs, toklen), toklen) { - toklen = toklen + 1; + toklen += 1; } if toklen == 0 { @@ -1062,6 +1073,16 @@ impl Parser { Err(Error::WrongArgs("atanh: expected one arg".to_string())) } } + "sqrt" => { + if args.len() == 1 { + Ok(EFuncSqrt(match args.pop() { + Some(xi) => xi, + None => return Err(Error::Unreachable), + })) + } else { + Err(Error::WrongArgs("sqrt: expected one arg".to_string())) + } + } _ => { #[cfg(feature = "unsafe-vars")] @@ -1076,6 +1097,7 @@ impl Parser { } } + #[cfg(feature = "print-func")] fn read_printfunc( &self, slab: &mut ParseSlab, @@ -1116,6 +1138,7 @@ impl Parser { Ok(PrintFunc(args)) } + #[cfg(feature = "print-func")] fn read_expressionorstring( &self, slab: &mut ParseSlab, @@ -1130,6 +1153,7 @@ impl Parser { } // TODO: Improve this logic, especially to handle embedded quotes: + #[cfg(feature = "print-func")] fn read_string(bs: &mut &[u8]) -> Result, Error> { spaces!(bs); @@ -1151,7 +1175,7 @@ impl Parser { Some(b'"') => false, Some(_) => true, } { - toklen = toklen + 1; + toklen += 1; } let out = from_utf8(&bs[..toklen]) @@ -1171,14 +1195,7 @@ impl Default for Parser { Self::new() } } -impl Default for Expression { - fn default() -> Self { - Expression { - first: Default::default(), - pairs: Vec::new(), - } - } -} + impl Default for Value { fn default() -> Self { EConstant(std::f64::NAN) @@ -1215,7 +1232,7 @@ mod internal_tests { use super::*; use crate::slab::Slab; - //// Commented so I can compile with stable Rust. + /// Commented so I can compile with stable Rust. // extern crate test; // use test::{Bencher, black_box}; @@ -1255,8 +1272,8 @@ mod internal_tests { } } - assert!((&[0u8; 0]).is_empty()); - assert!(!(&[1]).is_empty()); + assert!([0u8; 0].is_empty()); + assert!(![1].is_empty()); assert!((b"").is_empty()); assert!(!(b"x").is_empty()); diff --git a/tests/compile.rs b/tests/compile.rs index 105e09c..48314d9 100644 --- a/tests/compile.rs +++ b/tests/compile.rs @@ -3,16 +3,23 @@ use fasteval2::compiler::Instruction::IEvalFunc; use fasteval2::compiler::Instruction::{ self, IAdd, IConst, IExp, 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, + IFuncRound, IFuncSign, IFuncSin, IFuncSinH, IFuncSqrt, IFuncTan, IFuncTanH, IInv, IMod, IMul, + INeg, INot, IVar, IAND, IEQ, IGT, IGTE, ILT, ILTE, INE, IOR, }; + +#[cfg(feature = "print-func")] +use fasteval2::{ + compiler::Instruction::IPrintFunc, + parser::{ + ExpressionOrString::{EExpr, EStr}, + PrintFunc, + }, +}; + use fasteval2::compiler::IC; #[cfg(feature = "eval-builtin")] use fasteval2::parser::{EvalFunc, KWArg}; -use fasteval2::parser::{ - ExpressionOrString::{EExpr, EStr}, - PrintFunc, -}; + use fasteval2::{ eval_compiled, eval_compiled_ref, CachedCallbackNamespace, Compiler, EmptyNamespace, Error, Evaler, ExpressionI, InstructionI, Parser, Slab, @@ -474,9 +481,9 @@ fn all_instrs() { comp_chk("4 ^ 0.5", IConst(2.0), "CompileSlab{ instrs:{} }", 2.0); comp_chk( "2 ^ 0.5", - IConst(1.4142135623730951), + IConst(std::f64::consts::SQRT_2), "CompileSlab{ instrs:{} }", - 1.4142135623730951, + std::f64::consts::SQRT_2, ); comp_chk_str( "-4 ^ 0.5", @@ -491,7 +498,7 @@ fn all_instrs() { power: IC::C(0.5), }, "CompileSlab{ instrs:{ 0:IVar(\"y\") } }", - 1.4142135623730951, + std::f64::consts::SQRT_2, ); comp_chk( "2 ^ 3 ^ 2", @@ -913,8 +920,11 @@ fn all_instrs() { let (_s, i) = comp("int"); assert_eq!(i, IVar("int".to_string())); - let (_s, i) = comp("print"); - assert_eq!(i, IVar("print".to_string())); + #[cfg(feature = "print-func")] + { + let (_s, i) = comp("print"); + assert_eq!(i, IVar("print".to_string())); + } let (_s, i) = comp("eval"); assert_eq!(i, IVar("eval".to_string())); @@ -1028,15 +1038,15 @@ fn all_instrs() { comp_chk("log(10)", IConst(1.0), "CompileSlab{ instrs:{} }", 1.0); comp_chk( "log(2, 10)", - IConst(3.321928094887362), + IConst(std::f64::consts::LOG2_10), "CompileSlab{ instrs:{} }", - 3.321928094887362, + std::f64::consts::LOG2_10, ); comp_chk( "log(e(), 10)", - IConst(2.302585092994046), + IConst(std::f64::consts::LN_10), "CompileSlab{ instrs:{} }", - 2.302585092994046, + std::f64::consts::LN_10, ); comp_chk( "log(x)", @@ -1268,15 +1278,15 @@ fn all_instrs() { // IFuncACos comp_chk( "acos(0)", - IConst(1.5707963267948966), + IConst(std::f64::consts::FRAC_PI_2), "CompileSlab{ instrs:{} }", - 1.5707963267948966, + std::f64::consts::FRAC_PI_2, ); comp_chk( "acos(w)", IFuncACos(InstructionI(0)), "CompileSlab{ instrs:{ 0:IVar(\"w\") } }", - 1.5707963267948966, + std::f64::consts::FRAC_PI_2, ); // IFuncATan @@ -1342,7 +1352,17 @@ fn all_instrs() { 0.0, ); + // IFuncSqrt + comp_chk("sqrt(0)", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); + comp_chk( + "sqrt(w)", + IFuncSqrt(InstructionI(0)), + "CompileSlab{ instrs:{ 0:IVar(\"w\") } }", + 0.0, + ); + // IPrintFunc + #[cfg(feature = "print-func")] comp_chk( r#"print("test",1.23)"#, IPrintFunc(PrintFunc(vec![ diff --git a/tests/eval.rs b/tests/eval.rs index bace256..1be13d0 100644 --- a/tests/eval.rs +++ b/tests/eval.rs @@ -401,7 +401,7 @@ fn aaa_basics() { .unwrap() .from(&slab.ps) .eval(&slab, &mut ns), - Ok(2.718281828459045) + Ok(std::f64::consts::E) ); assert_eq!( Parser::new() @@ -409,7 +409,7 @@ fn aaa_basics() { .unwrap() .from(&slab.ps) .eval(&slab, &mut ns), - Ok(3.141592653589793) + Ok(std::f64::consts::PI) ); assert_eq!( @@ -442,7 +442,7 @@ fn aaa_basics() { .unwrap() .from(&slab.ps) .eval(&slab, &mut ns), - Ok(1.5707963267948966) + Ok(std::f64::consts::FRAC_PI_2) ); assert_eq!( Parser::new() @@ -450,7 +450,7 @@ fn aaa_basics() { .unwrap() .from(&slab.ps) .eval(&slab, &mut ns), - Ok(1.5707963267948966) + Ok(std::f64::consts::FRAC_PI_2) ); assert_eq!( Parser::new() @@ -458,7 +458,7 @@ fn aaa_basics() { .unwrap() .from(&slab.ps) .eval(&slab, &mut ns), - Ok(0.7853981633974483) + Ok(std::f64::consts::FRAC_PI_4) ); assert_eq!( Parser::new() @@ -486,7 +486,7 @@ fn aaa_basics() { ); } -//// Commented out until we bring CachedLayeredNamespace back. +/// Commented out until we bring CachedLayeredNamespace back. // #[derive(Debug)] // struct TestEvaler; // impl Evaler for TestEvaler { @@ -566,9 +566,9 @@ fn custom_func() { "x" => Some(1.0), "y" => Some(2.0), "z" => Some(3.0), - "foo" => Some(args.get(0).unwrap_or(&std::f64::NAN) * 10.0), + "foo" => Some(args.first().unwrap_or(&std::f64::NAN) * 10.0), "bar" => { - Some(args.get(0).unwrap_or(&std::f64::NAN) + args.get(1).unwrap_or(&std::f64::NAN)) + Some(args.first().unwrap_or(&std::f64::NAN) + args.get(1).unwrap_or(&std::f64::NAN)) } _ => None, } diff --git a/tests/evalns.rs b/tests/evalns.rs index ad9b23a..f27f18c 100644 --- a/tests/evalns.rs +++ b/tests/evalns.rs @@ -128,7 +128,7 @@ fn custom_vector_funcs() { ns.insert( "vec_sum", Box::new(|args| { - if let Some(index) = args.get(0) { + if let Some(index) = args.first() { if let Some(v) = vecs_cell.borrow().get(*index as usize) { return v.iter().sum(); } diff --git a/tests/from_go.rs b/tests/from_go.rs index f126f56..7d76063 100644 --- a/tests/from_go.rs +++ b/tests/from_go.rs @@ -8,10 +8,10 @@ use fasteval2::{ use std::collections::BTreeMap; use std::collections::BTreeSet; -fn parse_raw<'a>(s: &str, slab: &'a mut Slab) -> Result { +fn parse_raw(s: &str, slab: &mut Slab) -> Result { Parser::new().parse(s, &mut slab.ps) } -fn ok_parse<'a>(s: &str, slab: &'a mut Slab) -> ExpressionI { +fn ok_parse(s: &str, slab: &mut Slab) -> ExpressionI { parse_raw(s, slab).unwrap() } @@ -24,7 +24,7 @@ fn do_eval(s: &str) -> f64 { .unwrap() } -//// TODO: +/// TODO: // fn capture_stderr(f:&dyn Fn()) -> String { // f(); // "".to_string() diff --git a/tests/parse.rs b/tests/parse.rs index 6031c7f..7afc3c8 100644 --- a/tests/parse.rs +++ b/tests/parse.rs @@ -21,12 +21,14 @@ fn basics() { assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(-43.0), pairs: [ExprPair(ESub, EConstant(0.21))] }, 1:Expression { first: EConstant(12.34), pairs: [ExprPair(EAdd, EStdFunc(EFuncAbs(ExpressionI(0)))), ExprPair(EAdd, EConstant(11.11))] } }, vals:{}, instrs:{} }"); + #[cfg(feature = "print-func")] Parser::new() .parse("12.34 + print ( 43.21 ) + 11.11", &mut slab.ps) .unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(43.21), pairs: [] }, 1:Expression { first: EConstant(12.34), pairs: [ExprPair(EAdd, EPrintFunc(PrintFunc([EExpr(ExpressionI(0))]))), ExprPair(EAdd, EConstant(11.11))] } }, vals:{}, instrs:{} }"); + #[cfg(feature = "print-func")] Parser::new() .parse("12.34 + print [ 43.21 ] + 11.11", &mut slab.ps) .unwrap();