带有 Boost Spirit X3 的最小计算器
Minimal calculator with Boost Spirit X3
我正在尝试使用 Boost spirit X3 编写一个简约的计算器示例。我发现官方示例确实令人困惑,因为它们似乎使用了很多不必要的样板代码来定义和评估 AST,并且在 X3 的各个版本之间存在细微的语法变化。
这里有一些伪代码概述了我想做的事情:
using namespace boost::spirit::x3;
std::string Test("1 + (3 - 2)");
auto first = Test.begin();
auto last = Test.end();
rule<class dash_expr> const dash_expr("dash_expr");
rule<class factor_expr> const factor_expr("factor_expr");
auto const dash_expr_def = factor_expr
>> *( (char_('+') >> dash_expr)
| (char_('-') >> dash_expr)
);
auto const factor_expr_def = uint_
| '(' >> dash_expr >> ')'
;
BOOST_SPIRIT_DEFINE(dash_expr, factor_expr);
ascii::space_type space;
bool r = phrase_parse(
first,
last,
dash_expr,
space
);
首先,这不能用当前版本的 boost 编译,因为 BOOST_SPIRIT_DEFINE
会导致“预期表达式”错误。此外,如何在不定义过多的附加类型和操作的情况下评估生成的 AST?较早的 Boost Spirit Qi 示例表明可以简单地使用像 (char_('+') >> dash_expr) [_val += _1]
这样的“内联”表达式。此功能是否已弃用?如何将我的伪代码变成一个工作示例,使用最少的额外代码行输出正确的结果2
?
Q. First of all, this does not compile with a current version of boost because BOOST_SPIRIT_DEFINE causes a "expected expression" error.
那是因为注册规则定义特化了函数模板。它需要在命名空间范围内。
固定:https://godbolt.org/z/e3Essd8e8
Q. Additionally, how do I evaluate the resulting AST without defining a plethora of additional types and operations? Older Boost Spirit Qi examples suggest that it is possible to simply use "inline" expressions like (char_('+') >> dash_expr) [_val += _1]. Is this feature deprecated?
没有。它只是没有在 X3 中实现。语义动作已被简化多次,现在是常规的 lambda。因此,基于 Proto 的语义操作 DSL 的编译开销似乎是多余的。
Q. How do I turn my pseudo code into a working example that outputs the correct result 2 using the least amount of additional lines of code?
基本上,通过添加一些代码。我看到了更多可以简化的事情。我可以向您展示一些变体。
您可能感兴趣的是一个简单的头文件(类似于新的 Boost Lambda2 提案),它实现了 X3 的 Boost Phoenix 的核心。
先做简单的蛋糕
直接的方法是“只做工作”。为了让它不那么麻烦,我将使用一个宏。我还将实现更多的二元运算符:
#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <iomanip>
namespace x3 = boost::spirit::x3;
using V = int32_t;
namespace Parser {
x3::rule<struct expr, V> const expr{"expr"};
x3::rule<struct simple, V> const simple{"simple"};
x3::rule<struct factor, V> const factor{"factor"};
auto assign = [](auto& ctx) { _val(ctx) = _attr(ctx); };
#define BINOP(op, rhs) (x3::lit(#op) >> x3::as_parser(rhs) \
[([](auto& ctx) { _val(ctx) = _val(ctx) op _attr(ctx); })])
auto simple_def = x3::double_ | '(' >> expr >> ')';
auto factor_def = simple[assign] >>
*(BINOP(*, factor)
| BINOP(/, factor)
| BINOP(%, factor));
auto expr_def = factor[assign] >>
*(BINOP(+, expr)
| BINOP(-, expr));
BOOST_SPIRIT_DEFINE(expr, factor, simple)
} // namespace Parser
V evaluate(std::string_view text) {
V value{};
if (!phrase_parse(text.begin(), text.end(), Parser::expr >> x3::eoi,
x3::space, value))
throw std::runtime_error("error in expression");
return value;
}
int main() {
for (auto expr : {
"1 + (3 - 2)",
"1 * 3 - 2",
"17 % 5 - 2",
"17 % (5 - 2)",
"1 - 5 * 7",
"(1 - 5) * 7",
"((1 - 5) * 7)",
})
{
std::cout << std::quoted(expr) << "\t-> " << evaluate(expr) << "\n";
}
}
版画
"1 + (3 - 2)" -> 2
"1 * 3 - 2" -> 1
"17 % 5 - 2" -> 0
"17 % (5 - 2)" -> 2
"1 - 5 * 7" -> -34
"(1 - 5) * 7" -> -28
"((1 - 5) * 7)" -> -28
Phoeni_X3.hpp
没有任何形式的保证,以下是灵感来源:https://github.com/sehe/expression-parsers/tree/x3-c++17
Wandbox 现场演示:
----------------------------------------------
OUTPUT: 0 ← "1 + ( 2 - 3 )"
----------------------------------------------
OUTPUT: 6.25 ← "( ( 1 + ( 2 - 3 ) + 5 ) / 2 ) ^ 2"
----------------------------------------------
OUTPUT: 14 ← "5 + ( ( 1 + 2 ) * 4 ) - 3"
----------------------------------------------
OUTPUT: 7 ← "3+4"
----------------------------------------------
OUTPUT: 11 ← "3+4*2"
----------------------------------------------
OUTPUT: 11 ← "3+(4*2)"
----------------------------------------------
OUTPUT: 14 ← "(3+4)*2"
----------------------------------------------
OUTPUT: 128 ← "2*2* 2* 2 * 2\t*2\n*2"
----------------------------------------------
Expecting ')' in "(2"
(2
^--- here
----------------------------------------------
Expecting term in "(2+/"
(2+/
^--- here
----------------------------------------------
Expecting term in "42*()"
42*()
^--- here
----------------------------------------------
Expecting ')' in "(2*2* 2* 2 * 2\t*2\n*2"
*2
^--- here
文件test.cpp
//#define BOOST_SPIRIT_X3_DEBUG
#include <iostream>
#include <iomanip>
#include <string_view>
#include "quote_esc.hpp"
#include "phoeni_x3.hpp"
namespace x3 = boost::spirit::x3;
namespace parsing {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wparentheses"
using namespace PhoeniX3::placeholders;
using Value = double;
using x3::expect;
x3::rule<struct _e, Value> expr { "expr" };
x3::rule<struct _x, Value> expo { "exponentation" };
x3::rule<struct _t, Value> term { "term" };
x3::rule<struct _f, Value> factor { "factor" };
auto simple = x3::rule<struct _s, Value> {"simple"}
= x3::double_ [_val = _attr]
| '(' >> expect[term][_val = _attr] > ')'
;
auto expo_def = simple [_val = _attr] >> *(
'^' >> expect[expo] [ _val ^= _attr ]
)
;
auto factor_def = expo [_val = _attr] >> *(
'*' >> expect[factor] [_val *= _attr]
| '/' >> expect[factor] [_val /= _attr])
;
auto term_def = factor [_val = _attr] >> *(
'+' >> expect[term] [_val += _attr]
| '-' >> expect[term] [_val -= _attr])
;
auto expr_def = x3::skip(x3::space)[ x3::eps > term > x3::eoi ];
BOOST_SPIRIT_DEFINE(expr, term, factor, expo)
using expectation_failure = x3::expectation_failure<std::string_view::const_iterator>;
#pragma GCC diagnostic pop
}
int main() {
for (std::string_view text : {
"1 + ( 2 - 3 )", //OUTPUT: 0.0
"( ( 1 + ( 2 - 3 ) + 5 ) / 2 ) ^ 2", //OUTPUT: 6.25
"5 + ( ( 1 + 2 ) * 4 ) - 3", //OUTPUT: 14.0
"3+4",
"3+4*2",
"3+(4*2)",
"(3+4)*2",
"2*2* 2* 2 * 2\t*2\n*2",
"(2",
"(2+/",
"42*()",
"(2*2* 2* 2 * 2\t*2\n*2",
})
try {
std::cout << "----------------------------------------------\n";
double result;
if (parse(text.begin(), text.end(), parsing::expr, result)) {
std::cout << "OUTPUT: " << result << " ← " << quote_esc(text) << "\n";
} else {
std::cout << "Unparsed expression " << quote_esc(text) << "\n";
}
} catch(parsing::expectation_failure const& ef) {
auto pos = ef.where() - text.begin();
// isolate a line
auto sol = text.find_last_of("\r\n", pos) + 1;
auto eol = text.find_first_of("\r\n", pos);
std::cout
<< "Expecting " << ef.which() << " in " << quote_esc(text)
<< "\n " << text.substr(sol, eol)
<< "\n " << std::string(pos - sol, ' ') << "^--- here\n";
}
}
文件phoeni_x3.hpp
#pragma once
#include <boost/spirit/home/x3.hpp>
namespace PhoeniX3 {
namespace x3 = boost::spirit::x3;
// base facilities
void eval(void); // ADL enable
// for instance-only operator overloads
template <typename T>
struct _base_type {
using self = T;
using base = _base_type;
template <typename U> auto operator=(U&&) const;
template <typename Ctx> decltype(auto) operator()(Ctx& ctx)/*&&*/ {
return eval(ctx, self{});
}
};
// placeholders
struct _val_type : _base_type<_val_type> {using base::operator(); using base::operator=;};
struct _attr_type : _base_type<_attr_type> {using base::operator(); using base::operator=;};
template <int N>
struct _atc_type : _base_type<_atc_type<N>> {
using base = _base_type<_atc_type>;
using base::operator();
using base::operator=;
};
namespace placeholders {
_val_type static const _val {};
_attr_type static const _attr {};
_atc_type<0> static const _1 {};
_atc_type<1> static const _2 {};
_atc_type<2> static const _3 {};
_atc_type<3> static const _4 {};
_atc_type<4> static const _5 {};
_atc_type<5> static const _6 {};
_atc_type<6> static const _7 {};
_atc_type<7> static const _8 {};
_atc_type<8> static const _9 {};
}
// expression types
template <typename L, typename R, typename Op> struct BinExpr : _base_type<BinExpr<L,R,Op> > {
using BinExpr::base::operator();
using BinExpr::base::operator=;
};
namespace tag {
struct _add; struct _add_assign;
struct _sub; struct _sub_assign;
struct _mul; struct _mul_assign;
struct _div; struct _div_assign;
struct _exp; struct _exp_assign;
struct _assign;
}
template <typename L, typename R> BinExpr<L, R, tag::_exp_assign> operator^=(L&&, R&&) { return {}; }
template <typename L, typename R> BinExpr<L, R, tag::_add_assign> operator+=(L&&, R&&) { return {}; }
template <typename L, typename R> BinExpr<L, R, tag::_sub_assign> operator-=(L&&, R&&) { return {}; }
template <typename L, typename R> BinExpr<L, R, tag::_mul_assign> operator*=(L&&, R&&) { return {}; }
template <typename L, typename R> BinExpr<L, R, tag::_div_assign> operator/=(L&&, R&&) { return {}; }
template <typename L, typename R> BinExpr<L, R, tag::_exp> operator&(L&&, R&&) { return {}; }
template <typename L, typename R> BinExpr<L, R, tag::_add> operator+(L&&, R&&) { return {}; }
template <typename L, typename R> BinExpr<L, R, tag::_sub> operator-(L&&, R&&) { return {}; }
template <typename L, typename R> BinExpr<L, R, tag::_mul> operator*(L&&, R&&) { return {}; }
template <typename L, typename R> BinExpr<L, R, tag::_div> operator/(L&&, R&&) { return {}; }
template <typename L> template <typename R>
auto _base_type<L>::operator=(R&&) const { return BinExpr<L, R, tag::_assign>{}; }
template <typename Ctx> auto&& eval(Ctx& ctx, _val_type) { return x3::_val(ctx); }
template <typename Ctx> auto&& eval(Ctx& ctx, _attr_type) { return x3::_attr(ctx); }
template <typename Ctx, int N> auto&& eval(Ctx& ctx, _atc_type<N>) { return boost::fusion::at_c<N>(x3::_attr(ctx)); }
template <typename L, typename R, typename Ctx> auto eval(Ctx& ctx, BinExpr<L, R, tag::_add>) { return eval(ctx, L{}) + eval(ctx, R{}); }
template <typename L, typename R, typename Ctx> auto eval(Ctx& ctx, BinExpr<L, R, tag::_sub>) { return eval(ctx, L{}) - eval(ctx, R{}); }
template <typename L, typename R, typename Ctx> auto eval(Ctx& ctx, BinExpr<L, R, tag::_mul>) { return eval(ctx, L{}) * eval(ctx, R{}); }
template <typename L, typename R, typename Ctx> auto eval(Ctx& ctx, BinExpr<L, R, tag::_div>) { return eval(ctx, L{}) / eval(ctx, R{}); }
template <typename L, typename R, typename Ctx> auto eval(Ctx& ctx, BinExpr<L, R, tag::_exp>) { return pow(eval(ctx, L{}), eval(ctx, R{})); }
template <typename L, typename R, typename Ctx> auto eval(Ctx& ctx, BinExpr<L, R, tag::_assign>) { return eval(ctx, L{}) = eval(ctx, R{}); }
template <typename L, typename R, typename Ctx> auto&& eval(Ctx& ctx, BinExpr<L, R, tag::_add_assign>) { return eval(ctx, L{}) += eval(ctx, R{}); }
template <typename L, typename R, typename Ctx> auto&& eval(Ctx& ctx, BinExpr<L, R, tag::_sub_assign>) { return eval(ctx, L{}) -= eval(ctx, R{}); }
template <typename L, typename R, typename Ctx> auto&& eval(Ctx& ctx, BinExpr<L, R, tag::_mul_assign>) { return eval(ctx, L{}) *= eval(ctx, R{}); }
template <typename L, typename R, typename Ctx> auto&& eval(Ctx& ctx, BinExpr<L, R, tag::_div_assign>) { return eval(ctx, L{}) /= eval(ctx, R{}); }
template <typename L, typename R, typename Ctx> auto&& eval(Ctx& ctx, BinExpr<L, R, tag::_exp_assign>) { return eval(ctx, L{}) = pow(eval(ctx, L{}), eval(ctx, R{})); }
}
文件quote_esc.hpp
#pragma once
#include <ostream>
#include <iomanip>
template <typename T>
struct quote_esc {
T sv;
quote_esc(T sv):sv(std::move(sv)) {}
friend std::ostream& operator<<(std::ostream& os, quote_esc const& esc) {
os << '"';
for(uint8_t ch : std::string_view(esc.sv))
switch(ch) {
case '"' : os << "\\""; break;
case '\r': os << "\r"; break;
case '\n': os << "\n"; break;
case '\b': os << "\b"; break;
case '[=15=]': os << "\0"; break;
case '\t': os << "\t"; break;
case '\f': os << "\f"; break;
default: os << ch;
}
return os << '"';
}
};
我正在尝试使用 Boost spirit X3 编写一个简约的计算器示例。我发现官方示例确实令人困惑,因为它们似乎使用了很多不必要的样板代码来定义和评估 AST,并且在 X3 的各个版本之间存在细微的语法变化。
这里有一些伪代码概述了我想做的事情:
using namespace boost::spirit::x3;
std::string Test("1 + (3 - 2)");
auto first = Test.begin();
auto last = Test.end();
rule<class dash_expr> const dash_expr("dash_expr");
rule<class factor_expr> const factor_expr("factor_expr");
auto const dash_expr_def = factor_expr
>> *( (char_('+') >> dash_expr)
| (char_('-') >> dash_expr)
);
auto const factor_expr_def = uint_
| '(' >> dash_expr >> ')'
;
BOOST_SPIRIT_DEFINE(dash_expr, factor_expr);
ascii::space_type space;
bool r = phrase_parse(
first,
last,
dash_expr,
space
);
首先,这不能用当前版本的 boost 编译,因为 BOOST_SPIRIT_DEFINE
会导致“预期表达式”错误。此外,如何在不定义过多的附加类型和操作的情况下评估生成的 AST?较早的 Boost Spirit Qi 示例表明可以简单地使用像 (char_('+') >> dash_expr) [_val += _1]
这样的“内联”表达式。此功能是否已弃用?如何将我的伪代码变成一个工作示例,使用最少的额外代码行输出正确的结果2
?
Q. First of all, this does not compile with a current version of boost because BOOST_SPIRIT_DEFINE causes a "expected expression" error.
那是因为注册规则定义特化了函数模板。它需要在命名空间范围内。
固定:https://godbolt.org/z/e3Essd8e8
Q. Additionally, how do I evaluate the resulting AST without defining a plethora of additional types and operations? Older Boost Spirit Qi examples suggest that it is possible to simply use "inline" expressions like (char_('+') >> dash_expr) [_val += _1]. Is this feature deprecated?
没有。它只是没有在 X3 中实现。语义动作已被简化多次,现在是常规的 lambda。因此,基于 Proto 的语义操作 DSL 的编译开销似乎是多余的。
Q. How do I turn my pseudo code into a working example that outputs the correct result 2 using the least amount of additional lines of code?
基本上,通过添加一些代码。我看到了更多可以简化的事情。我可以向您展示一些变体。
您可能感兴趣的是一个简单的头文件(类似于新的 Boost Lambda2 提案),它实现了 X3 的 Boost Phoenix 的核心。
先做简单的蛋糕
直接的方法是“只做工作”。为了让它不那么麻烦,我将使用一个宏。我还将实现更多的二元运算符:
#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <iomanip>
namespace x3 = boost::spirit::x3;
using V = int32_t;
namespace Parser {
x3::rule<struct expr, V> const expr{"expr"};
x3::rule<struct simple, V> const simple{"simple"};
x3::rule<struct factor, V> const factor{"factor"};
auto assign = [](auto& ctx) { _val(ctx) = _attr(ctx); };
#define BINOP(op, rhs) (x3::lit(#op) >> x3::as_parser(rhs) \
[([](auto& ctx) { _val(ctx) = _val(ctx) op _attr(ctx); })])
auto simple_def = x3::double_ | '(' >> expr >> ')';
auto factor_def = simple[assign] >>
*(BINOP(*, factor)
| BINOP(/, factor)
| BINOP(%, factor));
auto expr_def = factor[assign] >>
*(BINOP(+, expr)
| BINOP(-, expr));
BOOST_SPIRIT_DEFINE(expr, factor, simple)
} // namespace Parser
V evaluate(std::string_view text) {
V value{};
if (!phrase_parse(text.begin(), text.end(), Parser::expr >> x3::eoi,
x3::space, value))
throw std::runtime_error("error in expression");
return value;
}
int main() {
for (auto expr : {
"1 + (3 - 2)",
"1 * 3 - 2",
"17 % 5 - 2",
"17 % (5 - 2)",
"1 - 5 * 7",
"(1 - 5) * 7",
"((1 - 5) * 7)",
})
{
std::cout << std::quoted(expr) << "\t-> " << evaluate(expr) << "\n";
}
}
版画
"1 + (3 - 2)" -> 2
"1 * 3 - 2" -> 1
"17 % 5 - 2" -> 0
"17 % (5 - 2)" -> 2
"1 - 5 * 7" -> -34
"(1 - 5) * 7" -> -28
"((1 - 5) * 7)" -> -28
Phoeni_X3.hpp
没有任何形式的保证,以下是灵感来源:https://github.com/sehe/expression-parsers/tree/x3-c++17
Wandbox 现场演示:
----------------------------------------------
OUTPUT: 0 ← "1 + ( 2 - 3 )"
----------------------------------------------
OUTPUT: 6.25 ← "( ( 1 + ( 2 - 3 ) + 5 ) / 2 ) ^ 2"
----------------------------------------------
OUTPUT: 14 ← "5 + ( ( 1 + 2 ) * 4 ) - 3"
----------------------------------------------
OUTPUT: 7 ← "3+4"
----------------------------------------------
OUTPUT: 11 ← "3+4*2"
----------------------------------------------
OUTPUT: 11 ← "3+(4*2)"
----------------------------------------------
OUTPUT: 14 ← "(3+4)*2"
----------------------------------------------
OUTPUT: 128 ← "2*2* 2* 2 * 2\t*2\n*2"
----------------------------------------------
Expecting ')' in "(2"
(2
^--- here
----------------------------------------------
Expecting term in "(2+/"
(2+/
^--- here
----------------------------------------------
Expecting term in "42*()"
42*()
^--- here
----------------------------------------------
Expecting ')' in "(2*2* 2* 2 * 2\t*2\n*2"
*2
^--- here
文件
test.cpp
//#define BOOST_SPIRIT_X3_DEBUG #include <iostream> #include <iomanip> #include <string_view> #include "quote_esc.hpp" #include "phoeni_x3.hpp" namespace x3 = boost::spirit::x3; namespace parsing { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wparentheses" using namespace PhoeniX3::placeholders; using Value = double; using x3::expect; x3::rule<struct _e, Value> expr { "expr" }; x3::rule<struct _x, Value> expo { "exponentation" }; x3::rule<struct _t, Value> term { "term" }; x3::rule<struct _f, Value> factor { "factor" }; auto simple = x3::rule<struct _s, Value> {"simple"} = x3::double_ [_val = _attr] | '(' >> expect[term][_val = _attr] > ')' ; auto expo_def = simple [_val = _attr] >> *( '^' >> expect[expo] [ _val ^= _attr ] ) ; auto factor_def = expo [_val = _attr] >> *( '*' >> expect[factor] [_val *= _attr] | '/' >> expect[factor] [_val /= _attr]) ; auto term_def = factor [_val = _attr] >> *( '+' >> expect[term] [_val += _attr] | '-' >> expect[term] [_val -= _attr]) ; auto expr_def = x3::skip(x3::space)[ x3::eps > term > x3::eoi ]; BOOST_SPIRIT_DEFINE(expr, term, factor, expo) using expectation_failure = x3::expectation_failure<std::string_view::const_iterator>; #pragma GCC diagnostic pop } int main() { for (std::string_view text : { "1 + ( 2 - 3 )", //OUTPUT: 0.0 "( ( 1 + ( 2 - 3 ) + 5 ) / 2 ) ^ 2", //OUTPUT: 6.25 "5 + ( ( 1 + 2 ) * 4 ) - 3", //OUTPUT: 14.0 "3+4", "3+4*2", "3+(4*2)", "(3+4)*2", "2*2* 2* 2 * 2\t*2\n*2", "(2", "(2+/", "42*()", "(2*2* 2* 2 * 2\t*2\n*2", }) try { std::cout << "----------------------------------------------\n"; double result; if (parse(text.begin(), text.end(), parsing::expr, result)) { std::cout << "OUTPUT: " << result << " ← " << quote_esc(text) << "\n"; } else { std::cout << "Unparsed expression " << quote_esc(text) << "\n"; } } catch(parsing::expectation_failure const& ef) { auto pos = ef.where() - text.begin(); // isolate a line auto sol = text.find_last_of("\r\n", pos) + 1; auto eol = text.find_first_of("\r\n", pos); std::cout << "Expecting " << ef.which() << " in " << quote_esc(text) << "\n " << text.substr(sol, eol) << "\n " << std::string(pos - sol, ' ') << "^--- here\n"; } }
文件
phoeni_x3.hpp
#pragma once #include <boost/spirit/home/x3.hpp> namespace PhoeniX3 { namespace x3 = boost::spirit::x3; // base facilities void eval(void); // ADL enable // for instance-only operator overloads template <typename T> struct _base_type { using self = T; using base = _base_type; template <typename U> auto operator=(U&&) const; template <typename Ctx> decltype(auto) operator()(Ctx& ctx)/*&&*/ { return eval(ctx, self{}); } }; // placeholders struct _val_type : _base_type<_val_type> {using base::operator(); using base::operator=;}; struct _attr_type : _base_type<_attr_type> {using base::operator(); using base::operator=;}; template <int N> struct _atc_type : _base_type<_atc_type<N>> { using base = _base_type<_atc_type>; using base::operator(); using base::operator=; }; namespace placeholders { _val_type static const _val {}; _attr_type static const _attr {}; _atc_type<0> static const _1 {}; _atc_type<1> static const _2 {}; _atc_type<2> static const _3 {}; _atc_type<3> static const _4 {}; _atc_type<4> static const _5 {}; _atc_type<5> static const _6 {}; _atc_type<6> static const _7 {}; _atc_type<7> static const _8 {}; _atc_type<8> static const _9 {}; } // expression types template <typename L, typename R, typename Op> struct BinExpr : _base_type<BinExpr<L,R,Op> > { using BinExpr::base::operator(); using BinExpr::base::operator=; }; namespace tag { struct _add; struct _add_assign; struct _sub; struct _sub_assign; struct _mul; struct _mul_assign; struct _div; struct _div_assign; struct _exp; struct _exp_assign; struct _assign; } template <typename L, typename R> BinExpr<L, R, tag::_exp_assign> operator^=(L&&, R&&) { return {}; } template <typename L, typename R> BinExpr<L, R, tag::_add_assign> operator+=(L&&, R&&) { return {}; } template <typename L, typename R> BinExpr<L, R, tag::_sub_assign> operator-=(L&&, R&&) { return {}; } template <typename L, typename R> BinExpr<L, R, tag::_mul_assign> operator*=(L&&, R&&) { return {}; } template <typename L, typename R> BinExpr<L, R, tag::_div_assign> operator/=(L&&, R&&) { return {}; } template <typename L, typename R> BinExpr<L, R, tag::_exp> operator&(L&&, R&&) { return {}; } template <typename L, typename R> BinExpr<L, R, tag::_add> operator+(L&&, R&&) { return {}; } template <typename L, typename R> BinExpr<L, R, tag::_sub> operator-(L&&, R&&) { return {}; } template <typename L, typename R> BinExpr<L, R, tag::_mul> operator*(L&&, R&&) { return {}; } template <typename L, typename R> BinExpr<L, R, tag::_div> operator/(L&&, R&&) { return {}; } template <typename L> template <typename R> auto _base_type<L>::operator=(R&&) const { return BinExpr<L, R, tag::_assign>{}; } template <typename Ctx> auto&& eval(Ctx& ctx, _val_type) { return x3::_val(ctx); } template <typename Ctx> auto&& eval(Ctx& ctx, _attr_type) { return x3::_attr(ctx); } template <typename Ctx, int N> auto&& eval(Ctx& ctx, _atc_type<N>) { return boost::fusion::at_c<N>(x3::_attr(ctx)); } template <typename L, typename R, typename Ctx> auto eval(Ctx& ctx, BinExpr<L, R, tag::_add>) { return eval(ctx, L{}) + eval(ctx, R{}); } template <typename L, typename R, typename Ctx> auto eval(Ctx& ctx, BinExpr<L, R, tag::_sub>) { return eval(ctx, L{}) - eval(ctx, R{}); } template <typename L, typename R, typename Ctx> auto eval(Ctx& ctx, BinExpr<L, R, tag::_mul>) { return eval(ctx, L{}) * eval(ctx, R{}); } template <typename L, typename R, typename Ctx> auto eval(Ctx& ctx, BinExpr<L, R, tag::_div>) { return eval(ctx, L{}) / eval(ctx, R{}); } template <typename L, typename R, typename Ctx> auto eval(Ctx& ctx, BinExpr<L, R, tag::_exp>) { return pow(eval(ctx, L{}), eval(ctx, R{})); } template <typename L, typename R, typename Ctx> auto eval(Ctx& ctx, BinExpr<L, R, tag::_assign>) { return eval(ctx, L{}) = eval(ctx, R{}); } template <typename L, typename R, typename Ctx> auto&& eval(Ctx& ctx, BinExpr<L, R, tag::_add_assign>) { return eval(ctx, L{}) += eval(ctx, R{}); } template <typename L, typename R, typename Ctx> auto&& eval(Ctx& ctx, BinExpr<L, R, tag::_sub_assign>) { return eval(ctx, L{}) -= eval(ctx, R{}); } template <typename L, typename R, typename Ctx> auto&& eval(Ctx& ctx, BinExpr<L, R, tag::_mul_assign>) { return eval(ctx, L{}) *= eval(ctx, R{}); } template <typename L, typename R, typename Ctx> auto&& eval(Ctx& ctx, BinExpr<L, R, tag::_div_assign>) { return eval(ctx, L{}) /= eval(ctx, R{}); } template <typename L, typename R, typename Ctx> auto&& eval(Ctx& ctx, BinExpr<L, R, tag::_exp_assign>) { return eval(ctx, L{}) = pow(eval(ctx, L{}), eval(ctx, R{})); } }
文件
quote_esc.hpp
#pragma once #include <ostream> #include <iomanip> template <typename T> struct quote_esc { T sv; quote_esc(T sv):sv(std::move(sv)) {} friend std::ostream& operator<<(std::ostream& os, quote_esc const& esc) { os << '"'; for(uint8_t ch : std::string_view(esc.sv)) switch(ch) { case '"' : os << "\\""; break; case '\r': os << "\r"; break; case '\n': os << "\n"; break; case '\b': os << "\b"; break; case '[=15=]': os << "\0"; break; case '\t': os << "\t"; break; case '\f': os << "\f"; break; default: os << ch; } return os << '"'; } };