Таким образом, представлен атрибут верхнего уровня expression
, который, честно говоря, вообще не представляет выражения.
Скорее, он представляет собой искусственную единицу синтаксиса ввода выражения, которая может быть названным "operation_chain".
Это также затруднит использование вашего AST для семантически правильных преобразований (таких как, например, оценка выражений), потому что важная информация, такая как приоритет операций, не закодирована в нем.
На самом деле, если мы не будем осторожны, вполне возможно, что эта информация - если она присутствует во входных данных - будет потеряна. Я думаю на практике возможно go из вашего AST и реконструировать дерево операций с зависимыми операциями в порядке их приоритета. Но я обычно ошибаюсь в безопасности при явном моделировании дерева выражений для отражения зависимостей операций.
Тем не менее, conditional_op
не является двоичной операцией цепочки, поэтому она не соответствует форма. Я бы посоветовал сделать так, чтобы правила «верхнего уровня» выставляли ast::operand
вместо этого (чтобы он мог соответствовать conditional_op
или expression
и тому и другому).
Однако из-за «ленивого» способа мы Чтобы определить условия, для этого требуются некоторые действия semanti c, чтобы на самом деле создать надлежащие атрибуты:
auto const conditional_def =
logical [([](auto& ctx) { _val(ctx) = _attr(ctx); })]
>> -('?' > expression > ':' > expression) [make_conditional_op]
;
Первое действие semanti c прямое, второе стало достаточно большим, чтобы определить его. -не-линии:
auto make_conditional_op = [](auto& ctx) {
using boost::fusion::at_c;
x3::_val(ctx) = ast::conditional_op {
x3::_val(ctx),
at_c<0>(x3::_attr(ctx)),
at_c<1>(x3::_attr(ctx)) };
};
Все еще прямой, но неуклюжий. Обратите внимание, что причина в том, что мы выставляем разные типы в зависимости от наличия дополнительной ветки.
Вот все это вместе взятые:
Live On Coliru
//#define BOOST_SPIRIT_X3_DEBUG
//#define DEBUG_SYMBOLS
#include <iostream>
#include <functional>
#include <iomanip>
#include <list>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/math/constants/constants.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
namespace x3 = boost::spirit::x3;
namespace ast {
struct nil {};
struct unary_op;
struct binary_op;
struct conditional_op;
struct expression;
using UnFunc = std::function<double(double)>;
using BinFunc = std::function<double(double, double)>;
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 {
UnFunc op;
operand rhs;
};
struct binary_op {
BinFunc op;
operand lhs;
operand rhs;
};
struct conditional_op {
operand lhs;
operand rhs_true;
operand rhs_false;
};
struct operation {
BinFunc op;
operand rhs;
};
struct expression {
operand lhs;
std::list<operation> rhs;
};
} // namespace ast
BOOST_FUSION_ADAPT_STRUCT(ast::expression, lhs, rhs)
BOOST_FUSION_ADAPT_STRUCT(ast::operation, op, rhs)
BOOST_FUSION_ADAPT_STRUCT(ast::conditional_op, lhs, rhs_true, rhs_false)
BOOST_FUSION_ADAPT_STRUCT(ast::binary_op, op, lhs, rhs)
BOOST_FUSION_ADAPT_STRUCT(ast::unary_op, op, rhs)
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::operand >{"expression"};
auto const conditional = x3::rule<conditional_class , ast::operand >{"conditional"};
auto const primary = x3::rule<primary_class , ast::operand >{"primary"};
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 unary = x3::rule<unary_class , ast::unary_op >{"unary"};
auto const binary = x3::rule<binary_class , ast::binary_op >{"binary"};
auto const variable = x3::rule<variable_class , std::string >{"variable"};
struct constant_ : x3::symbols<double> {
constant_() {
this->add
("e" , boost::math::constants::e<double>())
("pi" , boost::math::constants::pi<double>())
;
}
} constant;
struct ufunc_ : x3::symbols<ast::UnFunc> {
ufunc_() {
this->add
("abs" , &std::abs<double>)
;
}
} ufunc;
struct bfunc_ : x3::symbols<ast::BinFunc> {
bfunc_() {
this->add
("max" , [](double a,double b){ return std::fmax(a,b); })
("min" , [](double a,double b){ return std::fmin(a,b); })
("pow" , [](double a,double b){ return std::pow(a,b); })
;
}
} bfunc;
struct unary_op_ : x3::symbols<ast::UnFunc> {
unary_op_() {
this->add
("+", [](double v) { return +v; })
("-", std::negate{})
("!", [](double v) { return !v; })
;
}
} unary_op;
struct additive_op_ : x3::symbols<ast::BinFunc> {
additive_op_() {
this->add
("+", std::plus{})
("-", std::minus{})
;
}
} additive_op;
struct multiplicative_op_ : x3::symbols<ast::BinFunc> {
multiplicative_op_() {
this->add
("*", std::multiplies<>{})
("/", std::divides<>{})
("%", [](double a, double b) { return std::fmod(a, b); })
;
}
} multiplicative_op;
struct logical_op_ : x3::symbols<ast::BinFunc> {
logical_op_() {
this->add
("&&", std::logical_and{})
("||", std::logical_or{})
;
}
} logical_op;
struct relational_op_ : x3::symbols<ast::BinFunc> {
relational_op_() {
this->add
("<" , std::less{})
("<=", std::less_equal{})
(">" , std::greater{})
(">=", std::greater_equal{})
;
}
} relational_op;
struct equality_op_ : x3::symbols<ast::BinFunc> {
equality_op_() {
this->add
("==", std::equal_to{})
("!=", std::not_equal_to{})
;
}
} equality_op;
struct power_ : x3::symbols<ast::BinFunc> {
power_() {
this->add
("**", [](double v, double exp) { return std::pow(v, exp); })
;
}
} power;
auto const variable_def = x3::lexeme[x3::alpha >> *x3::alnum];
// Rule defintions
auto const expression_def =
conditional
;
auto make_conditional_op = [](auto& ctx) {
using boost::fusion::at_c;
x3::_val(ctx) = ast::conditional_op {
x3::_val(ctx),
at_c<0>(x3::_attr(ctx)),
at_c<1>(x3::_attr(ctx)) };
};
auto const conditional_def =
logical [([](auto& ctx) { _val(ctx) = _attr(ctx); })]
>> -('?' > expression > ':' > expression) [make_conditional_op]
;
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
= (unary_op > primary)
| (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)
BOOST_SPIRIT_DEFINE(logical)
BOOST_SPIRIT_DEFINE(equality)
BOOST_SPIRIT_DEFINE(relational)
BOOST_SPIRIT_DEFINE(additive)
BOOST_SPIRIT_DEFINE(multiplicative)
BOOST_SPIRIT_DEFINE(factor)
BOOST_SPIRIT_DEFINE(primary)
BOOST_SPIRIT_DEFINE(unary)
BOOST_SPIRIT_DEFINE(binary)
BOOST_SPIRIT_DEFINE(conditional)
BOOST_SPIRIT_DEFINE(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::operand out;
if (phrase_parse(f, l, P::expression, x3::space, out)) {
std::cout << "Success\n";
} else {
std::cout << "Failed\n";
}
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)"
Мне кажется, можно быть
но, к сожалению, мне не хватило времени на работу над ним, поэтому это на данный момент:)