|
|
@@ -28,7 +28,7 @@ namespace grammar |
|
|
|
|
|
|
|
using namespace pegtl; |
|
|
|
|
|
|
|
enum class op : uint8_t |
|
|
|
enum class id : uint8_t |
|
|
|
{ |
|
|
|
/* Variables and constants */ |
|
|
|
x, |
|
|
@@ -46,45 +46,8 @@ enum class op : uint8_t |
|
|
|
min, max, |
|
|
|
}; |
|
|
|
|
|
|
|
// Map operation enums to pegl::string<> rules |
|
|
|
template<op OP> struct r_call_string {}; |
|
|
|
|
|
|
|
template<> struct r_call_string<op::abs> : string<'a','b','s'> {}; |
|
|
|
template<> struct r_call_string<op::sqrt> : string<'s','q','r','t'> {}; |
|
|
|
template<> struct r_call_string<op::cbrt> : string<'c','b','r','t'> {}; |
|
|
|
template<> struct r_call_string<op::exp> : string<'e','x','p'> {}; |
|
|
|
template<> struct r_call_string<op::exp2> : string<'e','x','p','2'> {}; |
|
|
|
template<> struct r_call_string<op::log> : string<'l','o','g'> {}; |
|
|
|
template<> struct r_call_string<op::log2> : string<'l','o','g','2'> {}; |
|
|
|
template<> struct r_call_string<op::log10> : string<'l','o','g','1','0'> {}; |
|
|
|
template<> struct r_call_string<op::sin> : string<'s','i','n'> {}; |
|
|
|
template<> struct r_call_string<op::cos> : string<'c','o','s'> {}; |
|
|
|
template<> struct r_call_string<op::tan> : string<'t','a','n'> {}; |
|
|
|
template<> struct r_call_string<op::asin> : string<'a','s','i','n'> {}; |
|
|
|
template<> struct r_call_string<op::acos> : string<'a','c','o','s'> {}; |
|
|
|
template<> struct r_call_string<op::atan> : string<'a','t','a','n'> {}; |
|
|
|
template<> struct r_call_string<op::sinh> : string<'s','i','n','h'> {}; |
|
|
|
template<> struct r_call_string<op::cosh> : string<'c','o','s','h'> {}; |
|
|
|
template<> struct r_call_string<op::tanh> : string<'t','a','n','h'> {}; |
|
|
|
|
|
|
|
template<> struct r_call_string<op::atan2> : string<'a','t','a','n','2'> {}; |
|
|
|
template<> struct r_call_string<op::pow> : string<'p','o','w'> {}; |
|
|
|
template<> struct r_call_string<op::min> : string<'m','i','n'> {}; |
|
|
|
template<> struct r_call_string<op::max> : 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<r_stmt>(str, this); |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
* Evaluate expression at x |
|
|
|
*/ |
|
|
@@ -96,12 +59,12 @@ struct expression |
|
|
|
for (int i = 0; i < m_ops.count(); ++i) |
|
|
|
{ |
|
|
|
/* Rules that do not consume stack elements */ |
|
|
|
if (m_ops[i].m1 == op::x) |
|
|
|
if (m_ops[i].m1 == id::x) |
|
|
|
{ |
|
|
|
stack.push(x); |
|
|
|
continue; |
|
|
|
} |
|
|
|
else if (m_ops[i].m1 == op::constant) |
|
|
|
else if (m_ops[i].m1 == id::constant) |
|
|
|
{ |
|
|
|
stack.push(m_constants[m_ops[i].m2]); |
|
|
|
continue; |
|
|
@@ -112,39 +75,39 @@ struct expression |
|
|
|
|
|
|
|
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: |
|
|
|
case id::plus: stack.push(head); break; |
|
|
|
case id::minus: stack.push(-head); break; |
|
|
|
|
|
|
|
case id::abs: stack.push(fabs(head)); break; |
|
|
|
case id::sqrt: stack.push(sqrt(head)); break; |
|
|
|
case id::cbrt: stack.push(cbrt(head)); break; |
|
|
|
case id::exp: stack.push(exp(head)); break; |
|
|
|
case id::exp2: stack.push(exp2(head)); break; |
|
|
|
case id::log: stack.push(log(head)); break; |
|
|
|
case id::log2: stack.push(log2(head)); break; |
|
|
|
case id::log10: stack.push(log10(head)); break; |
|
|
|
case id::sin: stack.push(sin(head)); break; |
|
|
|
case id::cos: stack.push(cos(head)); break; |
|
|
|
case id::tan: stack.push(tan(head)); break; |
|
|
|
case id::asin: stack.push(asin(head)); break; |
|
|
|
case id::acos: stack.push(acos(head)); break; |
|
|
|
case id::atan: stack.push(atan(head)); break; |
|
|
|
case id::sinh: stack.push(sinh(head)); break; |
|
|
|
case id::cosh: stack.push(cosh(head)); break; |
|
|
|
case id::tanh: stack.push(tanh(head)); break; |
|
|
|
|
|
|
|
case id::add: stack.push(stack.pop() + head); break; |
|
|
|
case id::sub: stack.push(stack.pop() - head); break; |
|
|
|
case id::mul: stack.push(stack.pop() * head); break; |
|
|
|
case id::div: stack.push(stack.pop() / head); break; |
|
|
|
|
|
|
|
case id::atan2: stack.push(atan2(stack.pop(), head)); break; |
|
|
|
case id::pow: stack.push(pow(stack.pop(), head)); break; |
|
|
|
case id::min: stack.push(min(stack.pop(), head)); break; |
|
|
|
case id::max: stack.push(max(stack.pop(), head)); break; |
|
|
|
|
|
|
|
case id::x: |
|
|
|
case id::constant: |
|
|
|
/* Already handled above */ |
|
|
|
break; |
|
|
|
} |
|
|
@@ -155,101 +118,70 @@ struct expression |
|
|
|
} |
|
|
|
|
|
|
|
private: |
|
|
|
lol::array<op, int> m_ops; |
|
|
|
lol::array<id, int> m_ops; |
|
|
|
lol::array<lol::real> m_constants; |
|
|
|
|
|
|
|
private: |
|
|
|
struct do_constant : action_base<do_constant> |
|
|
|
{ |
|
|
|
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<op OP> |
|
|
|
struct do_op : action_base<do_op<OP>> |
|
|
|
{ |
|
|
|
static void apply(std::string const &ctx, expression *that) |
|
|
|
{ |
|
|
|
UNUSED(ctx); |
|
|
|
that->m_ops.push(OP, -1); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
struct r_expr; |
|
|
|
|
|
|
|
// r_ <- <blank> * |
|
|
|
struct _ : star<space> {}; |
|
|
|
|
|
|
|
// r_call_unary <- <unary_op> "(" r_expr ")" |
|
|
|
template<op OP> |
|
|
|
struct r_call_unary : ifapply<seq<r_call_string<OP>, |
|
|
|
_, |
|
|
|
one<'('>, |
|
|
|
_, |
|
|
|
r_expr, |
|
|
|
_, |
|
|
|
one<')'>>, |
|
|
|
do_op<OP>> {}; |
|
|
|
|
|
|
|
// r_call_binary <- <binary_op> "(" r_expr "," r_expr ")" |
|
|
|
template<op OP> |
|
|
|
struct r_call_binary : ifapply<seq<r_call_string<OP>, |
|
|
|
_, |
|
|
|
one<'('>, |
|
|
|
_, |
|
|
|
r_expr, |
|
|
|
_, |
|
|
|
one<','>, |
|
|
|
_, |
|
|
|
r_expr, |
|
|
|
_, |
|
|
|
one<')'>>, |
|
|
|
do_op<OP>> {}; |
|
|
|
|
|
|
|
// r_constant <- <digit> + ( "." <digit> * ) ? ( [eE] [+-] ? <digit> + ) ? |
|
|
|
struct r_constant : ifapply<seq<plus<digit>, |
|
|
|
opt<seq<one<'.'>, |
|
|
|
star<digit>>>, |
|
|
|
opt<seq<one<'e', 'E'>, |
|
|
|
opt<one<'+', '-'>>, |
|
|
|
plus<digit>>>>, |
|
|
|
do_constant> {}; |
|
|
|
struct r_constant : seq<plus<digit>, |
|
|
|
opt<seq<one<'.'>, |
|
|
|
star<digit>>>, |
|
|
|
opt<seq<one<'e', 'E'>, |
|
|
|
opt<one<'+', '-'>>, |
|
|
|
plus<digit>>>> {}; |
|
|
|
|
|
|
|
// r_var <- "x" |
|
|
|
struct r_var : ifapply<one<'x'>, do_op<op::x>> {}; |
|
|
|
|
|
|
|
// r_call <- r_call_unary / r_call_binary |
|
|
|
struct r_call : sor<r_call_unary<op::abs>, |
|
|
|
r_call_unary<op::sqrt>, |
|
|
|
r_call_unary<op::cbrt>, |
|
|
|
r_call_unary<op::exp>, |
|
|
|
r_call_unary<op::exp2>, |
|
|
|
r_call_unary<op::log>, |
|
|
|
r_call_unary<op::log2>, |
|
|
|
r_call_unary<op::log10>, |
|
|
|
r_call_unary<op::sin>, |
|
|
|
r_call_unary<op::cos>, |
|
|
|
r_call_unary<op::tan>, |
|
|
|
r_call_unary<op::asin>, |
|
|
|
r_call_unary<op::acos>, |
|
|
|
r_call_unary<op::atan>, |
|
|
|
r_call_unary<op::sinh>, |
|
|
|
r_call_unary<op::cosh>, |
|
|
|
r_call_unary<op::tanh>, |
|
|
|
r_call_binary<op::atan2>, |
|
|
|
r_call_binary<op::pow>, |
|
|
|
r_call_binary<op::min>, |
|
|
|
r_call_binary<op::max>> {}; |
|
|
|
struct r_var : pegtl_string_t("x") {}; |
|
|
|
|
|
|
|
// r_binary_call <- <r_binary_fun> "(" r_expr "," r_expr ")" |
|
|
|
struct r_binary_fun : sor<pegtl_string_t("atan2"), |
|
|
|
pegtl_string_t("pow"), |
|
|
|
pegtl_string_t("min"), |
|
|
|
pegtl_string_t("max")> {}; |
|
|
|
|
|
|
|
struct r_binary_call : seq<r_binary_fun, |
|
|
|
_, one<'('>, |
|
|
|
_, r_expr, |
|
|
|
_, one<','>, |
|
|
|
_, r_expr, |
|
|
|
_, one<')'>> {}; |
|
|
|
|
|
|
|
// r_unary_call <- <r_unary_fun> "(" r_expr ")" |
|
|
|
struct r_unary_fun : sor<pegtl_string_t("abs"), |
|
|
|
pegtl_string_t("sqrt"), |
|
|
|
pegtl_string_t("cbrt"), |
|
|
|
pegtl_string_t("exp"), |
|
|
|
pegtl_string_t("exp2"), |
|
|
|
pegtl_string_t("log"), |
|
|
|
pegtl_string_t("log2"), |
|
|
|
pegtl_string_t("log10"), |
|
|
|
pegtl_string_t("sin"), |
|
|
|
pegtl_string_t("cos"), |
|
|
|
pegtl_string_t("tan"), |
|
|
|
pegtl_string_t("asin"), |
|
|
|
pegtl_string_t("acos"), |
|
|
|
pegtl_string_t("atan"), |
|
|
|
pegtl_string_t("sinh"), |
|
|
|
pegtl_string_t("cosh"), |
|
|
|
pegtl_string_t("tanh")> {}; |
|
|
|
|
|
|
|
struct r_unary_call : seq<r_unary_fun, |
|
|
|
_, one<'('>, |
|
|
|
_, r_expr, |
|
|
|
_, one<')'>> {}; |
|
|
|
|
|
|
|
// r_call <- r_binary_call / r_unary_call |
|
|
|
struct r_call : sor<r_binary_call, |
|
|
|
r_unary_call> {}; |
|
|
|
|
|
|
|
// r_parentheses <- "(" r_expr ")" |
|
|
|
struct r_parentheses : seq<one<'('>, |
|
|
|
_, |
|
|
|
r_expr, |
|
|
|
_, |
|
|
|
pad<r_expr, space>, |
|
|
|
one<')'>> {}; |
|
|
|
|
|
|
|
// r_terminal <- r_call / r_var / r_constant / r_parentheses |
|
|
@@ -259,16 +191,16 @@ private: |
|
|
|
r_parentheses> {}; |
|
|
|
|
|
|
|
// r_signed <- "-" r_signed / "+" r_signed / r_terminal |
|
|
|
struct r_signed : sor<ifapply<seq<one<'-'>, _, r_signed>, |
|
|
|
do_op<op::minus>>, |
|
|
|
struct r_signed; |
|
|
|
struct r_minus : seq<one<'-'>, _, r_signed> {}; |
|
|
|
struct r_signed : sor<r_minus, |
|
|
|
seq<one<'+'>, _, r_signed>, |
|
|
|
r_terminal> {}; |
|
|
|
|
|
|
|
// r_exponent <- ( "^" / "**" ) r_signed |
|
|
|
struct r_exponent : ifapply<seq<_, |
|
|
|
sor<one<'^'>, string<'*', '*'>>, |
|
|
|
_, |
|
|
|
r_signed>, do_op<op::pow>> {}; |
|
|
|
struct r_exponent : seq<pad<sor<one<'^'>, |
|
|
|
string<'*', '*'>>, space>, |
|
|
|
r_signed> {}; |
|
|
|
|
|
|
|
// r_factor <- r_signed ( r_exponent ) * |
|
|
|
struct r_factor : seq<r_signed, |
|
|
@@ -277,21 +209,138 @@ private: |
|
|
|
// r_mul <- "*" r_factor |
|
|
|
// r_div <- "/" r_factor |
|
|
|
// r_term <- r_factor ( r_mul / r_div ) * |
|
|
|
struct r_mul : ifapply<seq<_, one<'*'>, _, r_factor>, do_op<op::mul>> {}; |
|
|
|
struct r_div : ifapply<seq<_, one<'/'>, _, r_factor>, do_op<op::div>> {}; |
|
|
|
struct r_mul : seq<_, one<'*'>, _, r_factor> {}; |
|
|
|
struct r_div : seq<_, one<'/'>, _, r_factor> {}; |
|
|
|
struct r_term : seq<r_factor, |
|
|
|
star<sor<r_mul, r_div>>> {}; |
|
|
|
|
|
|
|
// r_add <- "+" r_term |
|
|
|
// r_sub <- "-" r_term |
|
|
|
// r_expr <- r_term ( r_add / r_sub ) * |
|
|
|
struct r_add : ifapply<seq<_, one<'+'>, _, r_term>, do_op<op::add>> {}; |
|
|
|
struct r_sub : ifapply<seq<_, one<'-'>, _, r_term>, do_op<op::sub>> {}; |
|
|
|
struct r_add : seq<_, one<'+'>, _, r_term> {}; |
|
|
|
struct r_sub : seq<_, one<'-'>, _, r_term> {}; |
|
|
|
struct r_expr : seq<r_term, |
|
|
|
star<sor<r_add, r_sub>>> {}; |
|
|
|
|
|
|
|
// r_stmt <- r_expr <end> |
|
|
|
struct r_stmt : seq<_, r_expr, _, pegtl::eof> {}; |
|
|
|
struct r_stmt : must<pad<r_expr, space>, pegtl::eof> {}; |
|
|
|
|
|
|
|
// |
|
|
|
// Default actions |
|
|
|
// |
|
|
|
|
|
|
|
template<typename R> |
|
|
|
struct action : nothing<R> {}; |
|
|
|
|
|
|
|
template<id OP> |
|
|
|
struct generic_action |
|
|
|
{ |
|
|
|
static void apply(input const &in, expression *that) |
|
|
|
{ |
|
|
|
UNUSED(in); |
|
|
|
that->m_ops.push(OP, -1); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
public: |
|
|
|
/* |
|
|
|
* Parse arithmetic expression in x, e.g. 2*x+3 |
|
|
|
*/ |
|
|
|
void parse(std::string const &str) |
|
|
|
{ |
|
|
|
m_ops.empty(); |
|
|
|
m_constants.empty(); |
|
|
|
|
|
|
|
pegtl::parse<r_stmt, action>(str, "expression", this); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
// |
|
|
|
// Rule specialisations for simple operators |
|
|
|
// |
|
|
|
|
|
|
|
template<> struct expression::action<expression::r_var> : generic_action<id::x> {}; |
|
|
|
template<> struct expression::action<expression::r_exponent> : generic_action<id::pow> {}; |
|
|
|
template<> struct expression::action<expression::r_mul> : generic_action<id::mul> {}; |
|
|
|
template<> struct expression::action<expression::r_div> : generic_action<id::div> {}; |
|
|
|
template<> struct expression::action<expression::r_add> : generic_action<id::add> {}; |
|
|
|
template<> struct expression::action<expression::r_sub> : generic_action<id::sub> {}; |
|
|
|
template<> struct expression::action<expression::r_minus> : generic_action<id::minus> {}; |
|
|
|
|
|
|
|
// |
|
|
|
// Rule specialisations for unary and binary function calls |
|
|
|
// |
|
|
|
|
|
|
|
template<> |
|
|
|
struct expression::action<expression::r_binary_call> |
|
|
|
{ |
|
|
|
static void apply(input const &in, expression *that) |
|
|
|
{ |
|
|
|
struct { id id; char const *name; } lut[] = |
|
|
|
{ |
|
|
|
{ id::atan2, "atan2" }, |
|
|
|
{ id::pow, "pow" }, |
|
|
|
{ id::min, "min" }, |
|
|
|
{ id::max, "max" }, |
|
|
|
}; |
|
|
|
|
|
|
|
for (auto pair : lut) |
|
|
|
{ |
|
|
|
if (strncmp(in.string().c_str(), pair.name, strlen(pair.name)) != 0) |
|
|
|
continue; |
|
|
|
|
|
|
|
that->m_ops.push(pair.id, -1); |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
template<> |
|
|
|
struct expression::action<expression::r_unary_call> |
|
|
|
{ |
|
|
|
static void apply(input const &in, expression *that) |
|
|
|
{ |
|
|
|
struct { id id; char const *name; } lut[] = |
|
|
|
{ |
|
|
|
{ id::abs, "abs" }, |
|
|
|
{ id::sqrt, "sqrt" }, |
|
|
|
{ id::cbrt, "cbrt" }, |
|
|
|
{ id::exp2, "exp2" }, |
|
|
|
{ id::exp, "exp" }, |
|
|
|
{ id::log10, "log10" }, |
|
|
|
{ id::log2, "log2" }, |
|
|
|
{ id::log, "log" }, |
|
|
|
{ id::sinh, "sinh" }, |
|
|
|
{ id::cosh, "cosh" }, |
|
|
|
{ id::tanh, "tanh" }, |
|
|
|
{ id::sin, "sin" }, |
|
|
|
{ id::cos, "cos" }, |
|
|
|
{ id::tan, "tan" }, |
|
|
|
{ id::asin, "asin" }, |
|
|
|
{ id::acos, "acos" }, |
|
|
|
{ id::atan, "atan" }, |
|
|
|
}; |
|
|
|
|
|
|
|
for (auto pair : lut) |
|
|
|
{ |
|
|
|
if (strncmp(in.string().c_str(), pair.name, strlen(pair.name)) != 0) |
|
|
|
continue; |
|
|
|
|
|
|
|
that->m_ops.push(pair.id, -1); |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
template<> |
|
|
|
struct expression::action<expression::r_constant> |
|
|
|
{ |
|
|
|
static void apply(input const &in, expression *that) |
|
|
|
{ |
|
|
|
/* FIXME: check if the constant is already in the list */ |
|
|
|
that->m_ops.push(id::constant, that->m_constants.count()); |
|
|
|
that->m_constants.push(lol::real(in.string().c_str())); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
} /* namespace grammar */ |
|
|
|