Boost Spirit x3 条件(三元)运算符解析器

Boost Spirit x3 conditional (ternary) operator parser

本文关键字:运算符 三元 Spirit x3 条件 Boost      更新时间:2023-10-16

我需要从数学表达式构建一个抽象语法树,稍后我需要将其作为计算树链接在一起。我发现表达式解析器库 https://github.com/hmenke/boost_matheval 是一个很好的起点。

在我的用例中,我需要支持三元条件表达式condition ? true_exp : false_exp。截至目前,解析器能够解析表达式,例如1 + (2 + abs(x))min(x,1+y)。但是我需要这样的语法:(x > y ? 1 : 0) * (y - z).

我尝试定义规则

auto const conditional_def =
expression > '?' > expression > ':'> expression
;

并通过条件扩展启动规则

auto const primary_def =
x3::double_
| ('(' > expression > ')')
| (unary_op > primary)
| binary
| unary
| conditional
| constant
| variable
;

但是,这不能正确解析。开始规则消耗condition并递归尝试解析剩下的内容:? true_exp : false_exp.这与任何东西都不匹配。如果我将条件定义为这样

auto const conditional_def =
(x3::lit("if") > '(' > expression > ',' > expression > ','> expression > ')')
;

解析适用于if(x > y, x , y)等表达式 - 但这不是标准的三元条件?:

以下是 ast 属性和声明的符号定义的外观


namespace x3 = boost::spirit::x3;
namespace ast {
struct nil {};
struct unary_op;
struct binary_op;
struct conditional_op;
struct expression;
struct operand : x3::variant<
nil
, double
, std::string
, x3::forward_ast<unary_op>
, x3::forward_ast<binary_op>
, x3::forward_ast<conditional_op>
, x3::forward_ast<expression>
> {
using base_type::base_type;
using base_type::operator=;
};
struct unary_op {
double (*op)(double);
operand rhs;
};
struct binary_op {
double (*op)(double, double);
operand lhs;
operand rhs;
};
struct conditional_op {
operand lhs;
operand rhs_true;
operand rhs_false;
};
struct operation {
double (*op)(double, double);
operand rhs;
};
struct expression {
operand lhs;
std::list<operation> rhs;
};
} // namespace ast
struct constant_ : x3::symbols<double> {
constant_() {
add
("e"      , boost::math::constants::e<double>())
("pi"     , boost::math::constants::pi<double>())
;
}
} constant;
struct ufunc_ : x3::symbols<double (*)(double)> {
ufunc_() {
add
("abs"   , static_cast<double (*)(double)>(&std::abs))
;
}
} ufunc;
struct bfunc_ : x3::symbols<double (*)(double, double)> {
bfunc_() {
add
("max"  , static_cast<double (*)(double, double)>(&std::fmax))
;
}
} bfunc;
struct unary_op_ : x3::symbols<double (*)(double)> {
unary_op_() {
add
("+", static_cast<double (*)(double)>(&math::plus))
("-", static_cast<double (*)(double)>(&math::minus))
("!", static_cast<double (*)(double)>(&math::unary_not))
;
}
} unary_op;
struct additive_op_ : x3::symbols<double (*)(double, double)> {
additive_op_() {
add
("+", static_cast<double (*)(double, double)>(&math::plus))
("-", static_cast<double (*)(double, double)>(&math::minus))
;
}
} additive_op;
struct multiplicative_op_ : x3::symbols<double (*)(double, double)> {
multiplicative_op_() {
add
("*", static_cast<double (*)(double, double)>(&math::multiplies))
("/", static_cast<double (*)(double, double)>(&math::divides))
("%", static_cast<double (*)(double, double)>(&std::fmod))
;
}
} multiplicative_op;
struct logical_op_ : x3::symbols<double (*)(double, double)> {
logical_op_() {
add
("&&", static_cast<double (*)(double, double)>(&math::logical_and))
("||", static_cast<double (*)(double, double)>(&math::logical_or))
;
}
} logical_op;
struct relational_op_ : x3::symbols<double (*)(double, double)> {
relational_op_() {
add
("<" , static_cast<double (*)(double, double)>(&math::less))
("<=", static_cast<double (*)(double, double)>(&math::less_equals))
(">" , static_cast<double (*)(double, double)>(&math::greater))
(">=", static_cast<double (*)(double, double)>(&math::greater_equals))
;
}
} relational_op;
struct equality_op_ : x3::symbols<double (*)(double, double)> {
equality_op_() {
add
("==", static_cast<double (*)(double, double)>(&math::equals))
("!=", static_cast<double (*)(double, double)>(&math::not_equals))
;
}
} equality_op;
struct power_ : x3::symbols<double (*)(double, double)> {
power_() {
add
("**", static_cast<double (*)(double, double)>(&std::pow))
;
}
} power;

更完整的语法和ast属性的定义如下。如果有人对Boots精神更有经验的人可以指导我如何正确定义三元条件,我们将不胜感激。


struct expression_class;
struct logical_class;
struct equality_class;
struct relational_class;
struct additive_class;
struct multiplicative_class;
struct factor_class;
struct primary_class;
struct unary_class;
struct binary_class;
struct conditional_class;
struct variable_class;
// Rule declarations
auto const expression     = x3::rule<expression_class    , ast::expression    >{"expression"};
auto const logical        = x3::rule<logical_class       , ast::expression    >{"logical"};
auto const equality       = x3::rule<equality_class      , ast::expression    >{"equality"};
auto const relational     = x3::rule<relational_class    , ast::expression    >{"relational"};
auto const additive       = x3::rule<additive_class      , ast::expression    >{"additive"};
auto const multiplicative = x3::rule<multiplicative_class, ast::expression    >{"multiplicative"};
auto const factor         = x3::rule<factor_class        , ast::expression    >{"factor"};
auto const primary        = x3::rule<primary_class       , ast::operand       >{"primary"};
auto const unary          = x3::rule<unary_class         , ast::unary_op      >{"unary"};
auto const binary         = x3::rule<binary_class        , ast::binary_op     >{"binary"};
auto const conditional    = x3::rule<conditional_class   , ast::conditional_op>{"conditional"};
auto const variable       = x3::rule<variable_class      , std::string        >{"variable"};
// Rule defintions
auto const expression_def =
logical
;
auto const logical_def =
equality >> *(logical_op > equality)
;
auto const equality_def =
relational >> *(equality_op > relational)
;
auto const relational_def =
additive >> *(relational_op > additive)
;
auto const additive_def =
multiplicative >> *(additive_op > multiplicative)
;
auto const multiplicative_def =
factor >> *(multiplicative_op > factor)
;
auto const factor_def =
primary >> *( power > factor )
;
auto const unary_def =
ufunc > '(' > expression > ')'
;
auto const binary_def =
bfunc > '(' > expression > ',' > expression > ')'
;
auto const conditional_def =
expression  > '?' > expression > ':'> expression
;
auto const primary_def =
x3::double_
| ('(' > expression > ')')
| (unary_op > primary)
| binary
| unary
| conditional
| constant
| variable
;
BOOST_SPIRIT_DEFINE(
expression,
logical,
equality,
relational,
additive,
multiplicative,
factor,
primary,
unary,
binary,
conditional,
variable
)

以下是如何使用提升静态访问者遍历 AST 以使用变量符号表评估表达式

namespace ast {
// Evaluator
struct Evaluator {
using result_type = double;
explicit Evaluator(std::map<std::string, double> sym);
double operator()(nil) const;
double operator()(double n) const;
double operator()(std::string const &c) const;
double operator()(operation const &x, double lhs) const;
double operator()(unary_op const &x) const;
double operator()(binary_op const &x) const;
double operator()(conditional_op const &x) const;
double operator()(expression const &x) const;
private:
std::map<std::string, double> st;
};
Evaluator::Evaluator(std::map<std::string, double> sym) 
: st(std::move(sym)) {}
double Evaluator::operator()(nil) const {
BOOST_ASSERT(0);
return 0;
}
double Evaluator::operator()(double n) const { return n; }
double Evaluator::operator()(std::string const &c) const {
auto it = st.find(c);
if (it == st.end()) {
throw std::invalid_argument("Unknown variable " + c);
}
return it->second;
}
double Evaluator::operator()(operation const &x, double lhs) const {
double rhs = boost::apply_visitor(*this, x.rhs);
return x.op(lhs, rhs);
}
double Evaluator::operator()(unary_op const &x) const {
double rhs = boost::apply_visitor(*this, x.rhs);
return x.op(rhs);
}
double Evaluator::operator()(binary_op const &x) const {
double lhs = boost::apply_visitor(*this, x.lhs);
double rhs = boost::apply_visitor(*this, x.rhs);
return x.op(lhs, rhs);
}
double Evaluator::operator()(conditional_op const &x) const {
return static_cast<bool>(boost::apply_visitor(*this, x.lhs)) 
? boost::apply_visitor(*this, x.rhs_true) 
: boost::apply_visitor(*this, x.rhs_false);
}
double Evaluator::operator()(expression const &x) const {
double state = boost::apply_visitor(*this, x.lhs);
for (operation const &oper : x.rhs) {
state = (*this)(oper, state);
}
return state;
}
} // namespace ast

条件不是主要表达式。

事实上,它是一个中缀标记的三元表达式。

这会导致primary规则表现出左递归(根据定义,它下降到expression(。

您可能应该做的是像对待其他中缀运算符一样对待三元运算符,这具有明确相对运算符优先级的额外好处。

浏览编程语言中通常的优先级,我会建议如下:

auto const expression_def =
conditional
;
auto const conditional_def =
logical >> -('?' > expression > ':'> expression)
;
auto const logical_def =
equality >> *(logical_op > equality)
;

(当然还有从primary_def中删除它(。

现场演示

住在科里鲁

//#define BOOST_SPIRIT_X3_DEBUG
//#define DEBUG_SYMBOLS
#include <iostream>
#include <iomanip>
#include <boost/spirit/home/x3.hpp>
namespace x3 = boost::spirit::x3;
namespace ast {
using expression     = x3::unused_type;
using unary_op       = x3::unused_type;
using binary_op      = x3::unused_type;
using conditional_op = x3::unused_type;
using operand        = x3::unused_type;
}
namespace P {
struct ehbase {
template <typename It, typename Ctx>
x3::error_handler_result on_error(It f, It l, x3::expectation_failure<It> const& e, Ctx const& /*ctx*/) const {
std::cout << std::string(f,l) << "n"
<< std::setw(1+std::distance(f, e.where())) << "^"
<< "-- expected: " << e.which() << "n";
return x3::error_handler_result::fail;
}
};
struct expression_class     : ehbase {};
struct logical_class        : ehbase {};
struct equality_class       : ehbase {};
struct relational_class     : ehbase {};
struct additive_class       : ehbase {};
struct multiplicative_class : ehbase {};
struct factor_class         : ehbase {};
struct primary_class        : ehbase {};
struct unary_class          : ehbase {};
struct binary_class         : ehbase {};
struct conditional_class    : ehbase {};
struct variable_class       : ehbase {};
// Rule declarations
auto const expression     = x3::rule<expression_class    , ast::expression    >{"expression"};
auto const logical        = x3::rule<logical_class       , ast::expression    >{"logical"};
auto const equality       = x3::rule<equality_class      , ast::expression    >{"equality"};
auto const relational     = x3::rule<relational_class    , ast::expression    >{"relational"};
auto const additive       = x3::rule<additive_class      , ast::expression    >{"additive"};
auto const multiplicative = x3::rule<multiplicative_class, ast::expression    >{"multiplicative"};
auto const factor         = x3::rule<factor_class        , ast::expression    >{"factor"};
auto const primary        = x3::rule<primary_class       , ast::operand       >{"primary"};
auto const unary          = x3::rule<unary_class         , ast::unary_op      >{"unary"};
auto const binary         = x3::rule<binary_class        , ast::binary_op     >{"binary"};
auto const conditional    = x3::rule<conditional_class   , ast::conditional_op>{"conditional"};
auto const variable       = x3::rule<variable_class      , std::string        >{"variable"};
template <typename Tag>
static auto make_sym(std::initializer_list<char const*> elements) {
#ifdef DEBUG_SYMBOLS
static x3::symbols<x3::unused_type> instance;
static auto name = boost::core::demangle(typeid(Tag*).name());
for (auto el : elements)
instance += el;
return x3::rule<Tag> {name.c_str()} = instance;
#else
x3::symbols<x3::unused_type> instance;
for (auto el : elements)
instance += el;
return instance;
#endif
}
static const auto logical_op        = make_sym<struct logical_op_>({"and","or","xor"});
static const auto equality_op       = make_sym<struct equality_op_>({"=","!="});
static const auto relational_op     = make_sym<struct relational_op_>({"<","<=",">",">="});
static const auto additive_op       = make_sym<struct additive_op_>({"+","-"});
static const auto multiplicative_op = make_sym<struct multiplicative_op_>({"*","/"});
static const auto unary_op          = make_sym<struct unary_op_>({"!","-","~"}); // TODO FIXME interference with binop?
static const auto ufunc             = make_sym<struct ufunc_>({"gamma","factorial","abs"});
static const auto bfunc             = make_sym<struct bfunc_>({"pow","min","max"});
static const auto constant          = make_sym<struct constant_>({"pi","e","tau"});
static const auto variable_def      = make_sym<struct variable_def_>({"a","b","c","d","x","y","z"});
// Rule defintions
auto const expression_def =
conditional
;
auto const conditional_def =
logical >> -('?' > expression > ':' > expression)
;
auto const logical_def =
equality >> *(logical_op > equality)
;
auto const equality_def =
relational >> *(equality_op > relational)
;
auto const relational_def =
additive >> *(relational_op > additive)
;
auto const additive_def =
multiplicative >> *(additive_op > multiplicative)
;
auto const multiplicative_def =
factor >> *(multiplicative_op > factor)
;
auto const factor_def =
primary >> *( '^' > factor )
;
auto const unary_def =
ufunc > '(' > expression > ')'
;
auto const binary_def =
bfunc > '(' > expression > ',' > expression > ')'
;
auto const primary_def =
x3::double_
| ('(' > expression > ')')
| (unary_op > primary)
| binary
| unary
| constant
| variable
;
BOOST_SPIRIT_DEFINE(
expression,
logical,
equality,
relational,
additive,
multiplicative,
factor,
primary,
unary,
binary,
conditional,
variable
)
}
int main() {
for (std::string const input : {
"x+(3^pow(2,8))",
"1 + (2 + abs(x))",
"min(x,1+y)",
"(x > y ? 1 : 0) * (y - z)",
"min(3^4,7))",
"3^^4",
"(3,4)",
})
{
std::cout << " ===== " << std::quoted(input) << " =====n";
auto f = begin(input), l = end(input);
ast::expression out;
if (phrase_parse(f, l, P::expression, x3::space, out)) {
std::cout << "Successn";
} else {
std::cout << "Failedn";
}
if (f!=l) {
std::cout << "Unparsed: " << std::quoted(std::string(f,l)) << "n";
}
}
}

指纹

===== "x+(3^pow(2,8))" =====
Success
===== "1 + (2 + abs(x))" =====
Success
===== "min(x,1+y)" =====
Success
===== "(x > y ? 1 : 0) * (y - z)" =====
Success
===== "min(3^4,7))" =====
Success
Unparsed: ")"
===== "3^^4" =====
3^^4
^-- expected: factor
Failed
Unparsed: "3^^4"
===== "(3,4)" =====
(3,4)
^-- expected: ')'
Failed
Unparsed: "(3,4)"

笔记

我认为你的语法有相对多的期望点。这可能没问题,只是一个观察。

此外,您的语法有几个名称域。您可能应该采取预防措施来匹配部分名称(例如,如果存在aa,则不希望过早a变量(。