// // LolRemez - Remez algorithm implementation // // Copyright © 2005—2015 Sam Hocevar // // This program is free software. It comes without any warranty, to // the extent permitted by applicable law. You can redistribute it // and/or modify it under the terms of the Do What the Fuck You Want // to Public License, Version 2, as published by the WTFPL Task Force. // See http://www.wtfpl.net/ for more details. // #pragma once // // Powerful arithmetic expression parser/evaluator // // Usage: // expression e; // e.parse(" 2*x^3 + 3 * sin(x - atan(x))"); // auto y = e.eval("1.5"); // #include "pegtl.hh" namespace grammar { using namespace pegtl; enum class op : uint8_t { /* Variables and constants */ x, constant, /* Unary functions/operators */ plus, minus, abs, sqrt, cbrt, exp, exp2, log, log2, log10, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, /* Binary functions/operators */ add, sub, mul, div, atan2, pow, min, max, }; // Map operation enums to pegl::string<> rules template struct r_call_string {}; template<> struct r_call_string : string<'a','b','s'> {}; template<> struct r_call_string : string<'s','q','r','t'> {}; template<> struct r_call_string : string<'c','b','r','t'> {}; template<> struct r_call_string : string<'e','x','p'> {}; template<> struct r_call_string : string<'e','x','p','2'> {}; template<> struct r_call_string : string<'l','o','g'> {}; template<> struct r_call_string : string<'l','o','g','2'> {}; template<> struct r_call_string : string<'l','o','g','1','0'> {}; template<> struct r_call_string : string<'s','i','n'> {}; template<> struct r_call_string : string<'c','o','s'> {}; template<> struct r_call_string : string<'t','a','n'> {}; template<> struct r_call_string : string<'a','s','i','n'> {}; template<> struct r_call_string : string<'a','c','o','s'> {}; template<> struct r_call_string : string<'a','t','a','n'> {}; template<> struct r_call_string : string<'s','i','n','h'> {}; template<> struct r_call_string : string<'c','o','s','h'> {}; template<> struct r_call_string : string<'t','a','n','h'> {}; template<> struct r_call_string : string<'a','t','a','n','2'> {}; template<> struct r_call_string : string<'p','o','w'> {}; template<> struct r_call_string : string<'m','i','n'> {}; template<> struct r_call_string : string<'m','a','x'> {}; struct expression { /* * Parse arithmetic expression in x, e.g. 2*x+3 */ void parse(std::string const &str) { m_ops.Empty(); m_constants.Empty(); basic_parse_string(str, this); } /* * Evaluate expression at x */ lol::real eval(lol::real const &x) const { /* Use a stack */ lol::array stack; for (int i = 0; i < m_ops.Count(); ++i) { /* Rules that do not consume stack elements */ if (m_ops[i].m1 == op::x) { stack.push(x); continue; } else if (m_ops[i].m1 == op::constant) { stack.push(m_constants[m_ops[i].m2]); continue; } /* All other rules consume at least the head of the stack */ lol::real head = stack.pop(); switch (m_ops[i].m1) { case op::plus: stack.push(head); break; case op::minus: stack.push(-head); break; case op::abs: stack.push(fabs(head)); break; case op::sqrt: stack.push(sqrt(head)); break; case op::cbrt: stack.push(cbrt(head)); break; case op::exp: stack.push(exp(head)); break; case op::exp2: stack.push(exp2(head)); break; case op::log: stack.push(log(head)); break; case op::log2: stack.push(log2(head)); break; case op::log10: stack.push(log10(head)); break; case op::sin: stack.push(sin(head)); break; case op::cos: stack.push(cos(head)); break; case op::tan: stack.push(tan(head)); break; case op::asin: stack.push(asin(head)); break; case op::acos: stack.push(acos(head)); break; case op::atan: stack.push(atan(head)); break; case op::sinh: stack.push(sinh(head)); break; case op::cosh: stack.push(cosh(head)); break; case op::tanh: stack.push(tanh(head)); break; case op::add: stack.push(stack.pop() + head); break; case op::sub: stack.push(stack.pop() - head); break; case op::mul: stack.push(stack.pop() * head); break; case op::div: stack.push(stack.pop() / head); break; case op::atan2: stack.push(atan2(stack.pop(), head)); break; case op::pow: stack.push(pow(stack.pop(), head)); break; case op::min: stack.push(min(stack.pop(), head)); break; case op::max: stack.push(max(stack.pop(), head)); break; case op::x: case op::constant: /* Already handled above */ break; } } ASSERT(stack.count() == 1); return stack.pop(); } private: lol::array m_ops; lol::array m_constants; private: struct do_constant : action_base { static void apply(std::string const &ctx, expression *that) { /* FIXME: check if the constant is already in the list */ that->m_ops.push(op::constant, that->m_constants.Count()); that->m_constants.push(lol::real(ctx.c_str())); } }; template struct do_op : action_base> { static void apply(std::string const &ctx, expression *that) { UNUSED(ctx); that->m_ops.push(OP, -1); } }; struct r_expr; // r_ <- * struct _ : star {}; // r_call_unary <- "(" r_expr ")" template struct r_call_unary : ifapply, _, one<'('>, _, r_expr, _, one<')'>>, do_op> {}; // r_call_binary <- "(" r_expr "," r_expr ")" template struct r_call_binary : ifapply, _, one<'('>, _, r_expr, _, one<','>, _, r_expr, _, one<')'>>, do_op> {}; // r_constant <- + ( "." * ) ? ( [eE] [+-] ? + ) ? struct r_constant : ifapply, opt, star>>, opt, opt>, plus>>>, do_constant> {}; // r_var <- "x" struct r_var : ifapply, do_op> {}; // r_call <- r_call_unary / r_call_binary struct r_call : sor, r_call_unary, r_call_unary, r_call_unary, r_call_unary, r_call_unary, r_call_unary, r_call_unary, r_call_unary, r_call_unary, r_call_unary, r_call_unary, r_call_unary, r_call_unary, r_call_unary, r_call_unary, r_call_unary, r_call_binary, r_call_binary, r_call_binary, r_call_binary> {}; // r_parentheses <- "(" r_expr ")" struct r_parentheses : seq, _, r_expr, _, one<')'>> {}; // r_terminal <- r_call / r_var / r_constant / r_parentheses struct r_terminal : sor {}; // r_signed <- "-" r_signed / "+" r_signed / r_terminal struct r_signed : sor, _, r_signed>, do_op>, seq, _, r_signed>, r_terminal> {}; // r_exponent <- ( "^" / "**" ) r_signed struct r_exponent : ifapply, string<'*', '*'>>, _, r_signed>, do_op> {}; // r_factor <- r_signed ( r_exponent ) * struct r_factor : seq> {}; // r_mul <- "*" r_factor // r_div <- "/" r_factor // r_term <- r_factor ( r_mul / r_div ) * struct r_mul : ifapply, _, r_factor>, do_op> {}; struct r_div : ifapply, _, r_factor>, do_op> {}; struct r_term : seq>> {}; // r_add <- "+" r_term // r_sub <- "-" r_term // r_expr <- r_term ( r_add / r_sub ) * struct r_add : ifapply, _, r_term>, do_op> {}; struct r_sub : ifapply, _, r_term>, do_op> {}; struct r_expr : seq>> {}; // r_stmt <- r_expr struct r_stmt : seq<_, r_expr, _, eof> {}; }; } /* namespace grammar */ using grammar::expression;