From 38ae626999dea5869b07732d107020d2c4c809fb Mon Sep 17 00:00:00 2001 From: zeozeozeo <108888572+zeozeozeo@users.noreply.github.com> Date: Sun, 19 Nov 2023 15:37:46 +0500 Subject: [PATCH 1/6] Add `sqrt` builtin --- src/compiler.rs | 16 +++++++++++++--- src/evaler.rs | 19 ++++++++++++++----- src/parser.rs | 13 ++++++++++++- tests/compile.rs | 13 +++++++++++-- 4 files changed, 50 insertions(+), 11 deletions(-) diff --git a/src/compiler.rs b/src/compiler.rs index dcf0072..7b7c326 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -34,7 +34,7 @@ use crate::parser::{ 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, @@ -167,6 +167,7 @@ pub enum Instruction { IFuncASinH(InstructionI), IFuncACosH(InstructionI), IFuncATanH(InstructionI), + IFuncSqrt(InstructionI), IPrintFunc(PrintFunc), // Not optimized (it would be pointless because of i/o bottleneck). } @@ -176,8 +177,8 @@ 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, IPrintFunc, IVar, IAND, IEQ, IGT, IGTE, ILT, ILTE, INE, IOR, }; impl Default for Instruction { @@ -827,6 +828,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 { @@ -1123,6 +1125,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..6d49a9c 100644 --- a/src/evaler.rs +++ b/src/evaler.rs @@ -13,8 +13,9 @@ 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, IPrintFunc, IVar, IAND, IEQ, IGT, IGTE, ILT, ILTE, + INE, IOR, }, IC, }; @@ -33,7 +34,7 @@ use crate::parser::{ 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}, @@ -429,7 +430,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 +458,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 +467,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 +496,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, @@ -621,7 +627,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) } @@ -683,6 +689,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 +698,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 +724,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, diff --git a/src/parser.rs b/src/parser.rs index aca027d..a4c80b0 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -148,13 +148,14 @@ 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. @@ -1062,6 +1063,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")] diff --git a/tests/compile.rs b/tests/compile.rs index 105e09c..1bc0c5d 100644 --- a/tests/compile.rs +++ b/tests/compile.rs @@ -3,8 +3,8 @@ 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, IPrintFunc, IVar, IAND, IEQ, IGT, IGTE, ILT, ILTE, INE, IOR, }; use fasteval2::compiler::IC; #[cfg(feature = "eval-builtin")] @@ -1342,6 +1342,15 @@ 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 comp_chk( r#"print("test",1.23)"#, From b6c85d36d4b83cd308aecfaa0cd44a587c6c37c7 Mon Sep 17 00:00:00 2001 From: zeozeozeo <108888572+zeozeozeo@users.noreply.github.com> Date: Sun, 19 Nov 2023 15:44:06 +0500 Subject: [PATCH 2/6] Clippy fixes --- examples/advanced-vars.rs | 2 +- examples/repl.rs | 2 +- src/compiler.rs | 78 ++++++++++++++++----------------------- src/evaler.rs | 25 ++++++------- src/evalns.rs | 23 +++++------- src/parser.rs | 40 +++++++++----------- tests/eval.rs | 6 +-- tests/evalns.rs | 2 +- tests/from_go.rs | 6 +-- 9 files changed, 77 insertions(+), 107 deletions(-) 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 7b7c326..2f5cb06 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -220,15 +220,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) } } } @@ -238,16 +235,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) } } } @@ -322,13 +316,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) { @@ -347,13 +339,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) { @@ -520,17 +510,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 @@ -748,7 +736,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) } } @@ -955,13 +943,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 { @@ -1005,13 +991,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 { diff --git a/src/evaler.rs b/src/evaler.rs index 6d49a9c..0950723 100644 --- a/src/evaler.rs +++ b/src/evaler.rs @@ -237,10 +237,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); } @@ -255,14 +254,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; } } } @@ -277,14 +275,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; } } } 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 a4c80b0..dc19e05 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -47,6 +47,7 @@ pub struct ValueI(pub usize); /// /// It can be `compile()`d or `eval()`d. #[derive(Debug, PartialEq)] +#[derive(Default)] pub struct Expression { pub(crate) first: Value, pub(crate) pairs: Vec, // cap=8 @@ -285,10 +286,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 { @@ -353,7 +354,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( @@ -399,18 +400,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') @@ -423,7 +424,7 @@ impl Parser { { saw_val = true; suffix_ok = false; - toklen = toklen + 3; + toklen += 3; } break; } else { @@ -461,7 +462,7 @@ impl Parser { slab.char_buf.push_str(&exp.to_string()); tok = &slab.char_buf; - toklen = toklen + suffixlen; + toklen += suffixlen; } } } @@ -738,7 +739,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 { @@ -1162,7 +1163,7 @@ impl Parser { Some(b'"') => false, Some(_) => true, } { - toklen = toklen + 1; + toklen += 1; } let out = from_utf8(&bs[..toklen]) @@ -1182,14 +1183,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) @@ -1226,7 +1220,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}; @@ -1266,8 +1260,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/eval.rs b/tests/eval.rs index bace256..9b43088 100644 --- a/tests/eval.rs +++ b/tests/eval.rs @@ -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() From d48449b841d56fd54543848677b62c6cdbfafc26 Mon Sep 17 00:00:00 2001 From: zeozeozeo <108888572+zeozeozeo@users.noreply.github.com> Date: Sun, 19 Nov 2023 15:45:37 +0500 Subject: [PATCH 3/6] Bump version (2.0.4) --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b793d76..b5eda18 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" From f52f3bc0d326d285429938a6afb4e87ef1cc8739 Mon Sep 17 00:00:00 2001 From: zeozeozeo <108888572+zeozeozeo@users.noreply.github.com> Date: Sun, 19 Nov 2023 16:04:59 +0500 Subject: [PATCH 4/6] Add `print-func` feature to toggle `print` builtin --- Cargo.toml | 2 +- src/compiler.rs | 12 ++++++++++-- src/evaler.rs | 24 +++++++++++++++++++----- src/parser.rs | 22 +++++++++++++++++----- tests/compile.rs | 25 ++++++++++++++++++------- tests/parse.rs | 2 ++ 6 files changed, 67 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b5eda18..0378aef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,4 +22,4 @@ default = ["alpha-keywords"] 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/src/compiler.rs b/src/compiler.rs index 2f5cb06..a8850e5 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -30,7 +30,7 @@ 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, @@ -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 { @@ -169,6 +172,7 @@ pub enum Instruction { 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}; @@ -178,9 +182,12 @@ 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, IFuncSqrt, IFuncTan, IFuncTanH, IInv, IMod, IMul, - INeg, INot, IPrintFunc, IVar, IAND, IEQ, IGT, IGTE, ILT, ILTE, INE, IOR, + 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) @@ -752,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()), } } diff --git a/src/evaler.rs b/src/evaler.rs index 0950723..e9bc878 100644 --- a/src/evaler.rs +++ b/src/evaler.rs @@ -14,11 +14,14 @@ use crate::compiler::{ self, IAdd, IConst, IExp, IFunc, IFuncACos, IFuncACosH, IFuncASin, IFuncASinH, IFuncATan, IFuncATanH, IFuncAbs, IFuncCeil, IFuncCos, IFuncCosH, IFuncFloor, IFuncInt, IFuncLog, IFuncMax, IFuncMin, IFuncRound, IFuncSign, IFuncSin, IFuncSinH, IFuncSqrt, IFuncTan, - IFuncTanH, IInv, IMod, IMul, INeg, INot, IPrintFunc, IVar, IAND, IEQ, IGT, IGTE, ILT, ILTE, - INE, IOR, + 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")] @@ -29,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, 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; @@ -318,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), }; } @@ -326,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), } } @@ -550,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 { @@ -663,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), } } @@ -807,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/parser.rs b/src/parser.rs index dc19e05..cf44cff 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -46,8 +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(Default)] +#[derive(Debug, PartialEq, Default)] pub struct Expression { pub(crate) first: Value, pub(crate) pairs: Vec, // cap=8 @@ -62,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)] @@ -161,6 +164,7 @@ use StdFunc::{ /// 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`. @@ -169,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()); @@ -713,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, @@ -1088,6 +1097,7 @@ impl Parser { } } + #[cfg(feature = "print-func")] fn read_printfunc( &self, slab: &mut ParseSlab, @@ -1128,6 +1138,7 @@ impl Parser { Ok(PrintFunc(args)) } + #[cfg(feature = "print-func")] fn read_expressionorstring( &self, slab: &mut ParseSlab, @@ -1142,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); @@ -1220,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}; diff --git a/tests/compile.rs b/tests/compile.rs index 1bc0c5d..aef6e97 100644 --- a/tests/compile.rs +++ b/tests/compile.rs @@ -4,15 +4,22 @@ 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, IFuncSqrt, IFuncTan, IFuncTanH, IInv, IMod, IMul, - INeg, INot, IPrintFunc, IVar, IAND, IEQ, IGT, IGTE, ILT, ILTE, INE, IOR, + 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, @@ -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())); @@ -1352,6 +1362,7 @@ fn all_instrs() { ); // IPrintFunc + #[cfg(feature = "print-func")] comp_chk( r#"print("test",1.23)"#, IPrintFunc(PrintFunc(vec![ 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(); From 6ebdd3a9ff6dd9d6accc398ff0ec08d1d62fccdc Mon Sep 17 00:00:00 2001 From: zeozeozeo <108888572+zeozeozeo@users.noreply.github.com> Date: Sun, 19 Nov 2023 16:12:21 +0500 Subject: [PATCH 5/6] Remove magic numbers (clippy suggestion) --- tests/compile.rs | 20 ++++++++++---------- tests/eval.rs | 10 +++++----- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/compile.rs b/tests/compile.rs index aef6e97..48314d9 100644 --- a/tests/compile.rs +++ b/tests/compile.rs @@ -481,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", @@ -498,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", @@ -1038,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)", @@ -1278,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 diff --git a/tests/eval.rs b/tests/eval.rs index 9b43088..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() From 1e65aecea6074d32547dae333a9b6b9c8d2f7b40 Mon Sep 17 00:00:00 2001 From: zeozeozeo <108888572+zeozeozeo@users.noreply.github.com> Date: Sun, 19 Nov 2023 16:14:46 +0500 Subject: [PATCH 6/6] Enable `print-func` feature by default --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 0378aef..702b4af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ 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.