交叉递归规则声明
Declaration of cross-recursive rules
我将语法规则声明为静态常量。在我尝试使用交叉递归规则之前效果很好(rule1 是使用 rule2 定义的,它是使用 rule1).仍然可以构建源代码,但是在包含这种交叉递归情况的解析源上会出现段错误。
这是语法的简化代码:
template < typename Iterator >
class Skipper : public qi::grammar<Iterator> {
public:
Skipper ( ) : Skipper::base_type(_skip_rule) { }
private:
static qi::rule<Iterator> const
_comment,
_skip_rule;
};
template < typename Iterator >
typename qi::rule<Iterator> const
Skipper<Iterator>::_comment(
boost::spirit::repository::confix("/*", "*/")[*(qi::char_ - "*/")] // Multi-line
| boost::spirit::repository::confix("//", qi::eol)[*(qi::char_ - qi::eol)] // Single-line
);
template < typename Iterator >
typename qi::rule<Iterator> const
Skipper<Iterator>::_skip_rule(qi::ascii::space | _comment);
template < typename Iterator, typename Skipper >
class Grammar : public qi::grammar<Iterator, Skipper > {
public:
Grammar ( ) : Grammar::base_type(expression) { }
private:
static qi::rule<Iterator, Skipper> const
// Tokens
scalar_literal,
identifier,
// Rules
operand,
expression;
};
template < typename Iterator, typename Skipper >
typename qi::rule<Iterator, Skipper> const
Grammar<Iterator, Skipper>::scalar_literal(qi::uint_ | qi::int_);
template < typename Iterator, typename Skipper >
typename qi::rule<Iterator, Skipper> const
Grammar<Iterator, Skipper>::identifier(qi::lexeme[(qi::alpha | '_') >> *(qi::alnum | '_')]);
template < typename Iterator, typename Skipper >
typename qi::rule<Iterator, Skipper> const
Grammar<Iterator, Skipper>::operand((scalar_literal | identifier | ('(' >> expression >> ')')));
template < typename Iterator, typename Skipper >
typename qi::rule<Iterator, Skipper> const
Grammar<Iterator, Skipper>::expression(operand);
(expression规则与operand相同,使代码更容易理解;当然它应该更复杂但基于在 操作数 上)。 operand 声明使用 expression 之一,反之亦然。例如,尝试 parse_phrase 时会出现段错误 (123)。我想这是因为 "forward" 使用了 expression;如果我将 expression 声明放在 operand 之前,也会发生同样的情况。那么应该以什么方式声明这些规则,以避免运行时错误?
首先,静态与它无关:
Live On Coliru 失败同样严重:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/repository/include/qi.hpp>
namespace qi = boost::spirit::qi;
template <typename Iterator>
struct Skipper : qi::grammar<Iterator> {
Skipper() : Skipper::base_type(_skip_rule) { }
private:
qi::rule<Iterator> const
_comment {
boost::spirit::repository::confix("/*", "*/") [*(qi::char_ - "*/")] // Multi-line
| boost::spirit::repository::confix("//", qi::eol) [*(qi::char_ - qi::eol)] // Single-line
},
_skip_rule {
qi::ascii::space | _comment
};
};
template <typename Iterator, typename Skipper>
struct Grammar : qi::grammar<Iterator, Skipper> {
Grammar() : Grammar::base_type(expression) { }
private:
qi::rule<Iterator, Skipper> const
// Tokens
scalar_literal { qi::uint_ | qi::int_ },
identifier { qi::lexeme[(qi::alpha | '_') >> *(qi::alnum | '_')] },
// Rules
operand { (scalar_literal | identifier | ('(' >> expression >> ')')) },
expression { operand };
};
int main() {
using It = std::string::const_iterator;
Skipper<It> s;
Grammar<It, Skipper<It> > p;
std::string const input = "(123)";
It f = input.begin(), l = input.end();
bool ok = qi::phrase_parse(f,l,p,s);
if (ok) std::cout << "Parse success\n";
else std::cout << "Parse failed\n";
if (f!=l) std::cout << "Remaining input: '" << std::string(f,l) << "'\n";
}
其次,船长与事情无关:
Live On Coliru 失败同样严重:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/repository/include/qi.hpp>
namespace qi = boost::spirit::qi;
template <typename Iterator, typename Skipper = qi::ascii::space_type>
struct Grammar : qi::grammar<Iterator, Skipper> {
Grammar() : Grammar::base_type(expression) { }
private:
qi::rule<Iterator, Skipper> const
// Tokens
scalar_literal { qi::uint_ | qi::int_ },
identifier { qi::lexeme[(qi::alpha | '_') >> *(qi::alnum | '_')] },
// Rules
operand { (scalar_literal | identifier | ('(' >> expression >> ')')) },
expression { operand };
};
int main() {
using It = std::string::const_iterator;
Grammar<It> p;
std::string const input = "(123)";
It f = input.begin(), l = input.end();
bool ok = qi::phrase_parse(f,l,p,qi::ascii::space);
if (ok) std::cout << "Parse success\n";
else std::cout << "Parse failed\n";
if (f!=l) std::cout << "Remaining input: '" << std::string(f,l) << "'\n";
}
第三,初始化的时机也有关系:
Live On Coliru 成功:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/repository/include/qi.hpp>
namespace qi = boost::spirit::qi;
template <typename Iterator, typename Skipper = qi::ascii::space_type>
struct Grammar : qi::grammar<Iterator, Skipper> {
Grammar() : Grammar::base_type(expression) {
scalar_literal = qi::uint_ | qi::int_;
identifier = (qi::alpha | '_') >> *(qi::alnum | '_');
// Rules
operand = (scalar_literal | identifier | ('(' >> expression >> ')'));
expression = operand;
}
private:
qi::rule<Iterator> scalar_literal, identifier; // Tokens
qi::rule<Iterator, Skipper> operand, expression; // Rules
};
int main() {
using It = std::string::const_iterator;
Grammar<It> p;
std::string const input = "(123)";
It f = input.begin(), l = input.end();
bool ok = qi::phrase_parse(f,l,p,qi::ascii::space);
if (ok) std::cout << "Parse success\n";
else std::cout << "Parse failed\n";
if (f!=l) std::cout << "Remaining input: '" << std::string(f,l) << "'\n";
}
版画
Parse success
终于可以蛋糕吃完了:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/repository/include/qi.hpp>
namespace qi = boost::spirit::qi;
namespace parsing {
namespace detail {
template <typename Iterator>
struct Skipper : qi::grammar<Iterator> {
Skipper() : Skipper::base_type(_skip_rule) {
_comment = boost::spirit::repository::confix("/*", "*/") [*(qi::char_ - "*/")] // Multi-line
| boost::spirit::repository::confix("//", qi::eol) [*(qi::char_ - qi::eol)] // Single-line
;
_skip_rule = qi::ascii::space | _comment;
}
private:
qi::rule<Iterator> _comment, _skip_rule;
};
template <typename Iterator, typename Skipper = Skipper<Iterator> >
struct Grammar : qi::grammar<Iterator, Skipper> {
Grammar() : Grammar::base_type(expression) {
scalar_literal = qi::uint_ | qi::int_;
identifier = (qi::alpha | '_') >> *(qi::alnum | '_');
// Rules
operand = (scalar_literal | identifier | ('(' >> expression >> ')'));
expression = operand;
}
private:
qi::rule<Iterator> scalar_literal, identifier; // Tokens
qi::rule<Iterator, Skipper> operand, expression; // Rules
};
}
template <typename Iterator, typename Skipper = detail::Skipper<Iterator> >
struct facade {
template <typename Range> static bool parse(Range const& input) {
Iterator f = boost::begin(input), l = boost::end(input);
bool ok = qi::phrase_parse(f, l, _parser, _skipper);
if (f!=l)
std::cout << "Remaining input: '" << std::string(f,l) << "'\n";
return ok;
}
private:
static const detail::Skipper<Iterator> _skipper;
static const detail::Grammar<Iterator, Skipper> _parser;
};
template <class I, class S> const detail::Skipper<I> facade<I,S>::_skipper = {};
template <class I, class S> const detail::Grammar<I, S> facade<I,S>::_parser = {};
}
int main() {
using It = std::string::const_iterator;
std::string const input = "(123)";
bool ok = parsing::facade<It>::parse(input);
if (ok) std::cout << "Parse success\n";
else std::cout << "Parse failed\n";
}
注意结果是一样的,parser/skipper 和原始代码一样都是静态和常量,代码更容易维护(并且在同时)。
这基本上就是 Singletons-are-bad 主题与 inner-const-is-problematic 主题相遇的地方。您不需要将字段设为常量。您不需要将实例设为静态。
只是,如果您愿意,只创建一个实例。此外,解析器现在可复制也不是问题(您不必复制它;但现在您可以)。
我将语法规则声明为静态常量。在我尝试使用交叉递归规则之前效果很好(rule1 是使用 rule2 定义的,它是使用 rule1).仍然可以构建源代码,但是在包含这种交叉递归情况的解析源上会出现段错误。 这是语法的简化代码:
template < typename Iterator >
class Skipper : public qi::grammar<Iterator> {
public:
Skipper ( ) : Skipper::base_type(_skip_rule) { }
private:
static qi::rule<Iterator> const
_comment,
_skip_rule;
};
template < typename Iterator >
typename qi::rule<Iterator> const
Skipper<Iterator>::_comment(
boost::spirit::repository::confix("/*", "*/")[*(qi::char_ - "*/")] // Multi-line
| boost::spirit::repository::confix("//", qi::eol)[*(qi::char_ - qi::eol)] // Single-line
);
template < typename Iterator >
typename qi::rule<Iterator> const
Skipper<Iterator>::_skip_rule(qi::ascii::space | _comment);
template < typename Iterator, typename Skipper >
class Grammar : public qi::grammar<Iterator, Skipper > {
public:
Grammar ( ) : Grammar::base_type(expression) { }
private:
static qi::rule<Iterator, Skipper> const
// Tokens
scalar_literal,
identifier,
// Rules
operand,
expression;
};
template < typename Iterator, typename Skipper >
typename qi::rule<Iterator, Skipper> const
Grammar<Iterator, Skipper>::scalar_literal(qi::uint_ | qi::int_);
template < typename Iterator, typename Skipper >
typename qi::rule<Iterator, Skipper> const
Grammar<Iterator, Skipper>::identifier(qi::lexeme[(qi::alpha | '_') >> *(qi::alnum | '_')]);
template < typename Iterator, typename Skipper >
typename qi::rule<Iterator, Skipper> const
Grammar<Iterator, Skipper>::operand((scalar_literal | identifier | ('(' >> expression >> ')')));
template < typename Iterator, typename Skipper >
typename qi::rule<Iterator, Skipper> const
Grammar<Iterator, Skipper>::expression(operand);
(expression规则与operand相同,使代码更容易理解;当然它应该更复杂但基于在 操作数 上)。 operand 声明使用 expression 之一,反之亦然。例如,尝试 parse_phrase 时会出现段错误 (123)。我想这是因为 "forward" 使用了 expression;如果我将 expression 声明放在 operand 之前,也会发生同样的情况。那么应该以什么方式声明这些规则,以避免运行时错误?
首先,静态与它无关:
Live On Coliru 失败同样严重:
#include <boost/spirit/include/qi.hpp> #include <boost/spirit/repository/include/qi.hpp> namespace qi = boost::spirit::qi; template <typename Iterator> struct Skipper : qi::grammar<Iterator> { Skipper() : Skipper::base_type(_skip_rule) { } private: qi::rule<Iterator> const _comment { boost::spirit::repository::confix("/*", "*/") [*(qi::char_ - "*/")] // Multi-line | boost::spirit::repository::confix("//", qi::eol) [*(qi::char_ - qi::eol)] // Single-line }, _skip_rule { qi::ascii::space | _comment }; }; template <typename Iterator, typename Skipper> struct Grammar : qi::grammar<Iterator, Skipper> { Grammar() : Grammar::base_type(expression) { } private: qi::rule<Iterator, Skipper> const // Tokens scalar_literal { qi::uint_ | qi::int_ }, identifier { qi::lexeme[(qi::alpha | '_') >> *(qi::alnum | '_')] }, // Rules operand { (scalar_literal | identifier | ('(' >> expression >> ')')) }, expression { operand }; }; int main() { using It = std::string::const_iterator; Skipper<It> s; Grammar<It, Skipper<It> > p; std::string const input = "(123)"; It f = input.begin(), l = input.end(); bool ok = qi::phrase_parse(f,l,p,s); if (ok) std::cout << "Parse success\n"; else std::cout << "Parse failed\n"; if (f!=l) std::cout << "Remaining input: '" << std::string(f,l) << "'\n"; }
其次,船长与事情无关:
Live On Coliru 失败同样严重:
#include <boost/spirit/include/qi.hpp> #include <boost/spirit/repository/include/qi.hpp> namespace qi = boost::spirit::qi; template <typename Iterator, typename Skipper = qi::ascii::space_type> struct Grammar : qi::grammar<Iterator, Skipper> { Grammar() : Grammar::base_type(expression) { } private: qi::rule<Iterator, Skipper> const // Tokens scalar_literal { qi::uint_ | qi::int_ }, identifier { qi::lexeme[(qi::alpha | '_') >> *(qi::alnum | '_')] }, // Rules operand { (scalar_literal | identifier | ('(' >> expression >> ')')) }, expression { operand }; }; int main() { using It = std::string::const_iterator; Grammar<It> p; std::string const input = "(123)"; It f = input.begin(), l = input.end(); bool ok = qi::phrase_parse(f,l,p,qi::ascii::space); if (ok) std::cout << "Parse success\n"; else std::cout << "Parse failed\n"; if (f!=l) std::cout << "Remaining input: '" << std::string(f,l) << "'\n"; }
第三,初始化的时机也有关系:
Live On Coliru 成功:
#include <boost/spirit/include/qi.hpp> #include <boost/spirit/repository/include/qi.hpp> namespace qi = boost::spirit::qi; template <typename Iterator, typename Skipper = qi::ascii::space_type> struct Grammar : qi::grammar<Iterator, Skipper> { Grammar() : Grammar::base_type(expression) { scalar_literal = qi::uint_ | qi::int_; identifier = (qi::alpha | '_') >> *(qi::alnum | '_'); // Rules operand = (scalar_literal | identifier | ('(' >> expression >> ')')); expression = operand; } private: qi::rule<Iterator> scalar_literal, identifier; // Tokens qi::rule<Iterator, Skipper> operand, expression; // Rules }; int main() { using It = std::string::const_iterator; Grammar<It> p; std::string const input = "(123)"; It f = input.begin(), l = input.end(); bool ok = qi::phrase_parse(f,l,p,qi::ascii::space); if (ok) std::cout << "Parse success\n"; else std::cout << "Parse failed\n"; if (f!=l) std::cout << "Remaining input: '" << std::string(f,l) << "'\n"; }
版画
Parse success
终于可以蛋糕吃完了:
#include <boost/spirit/include/qi.hpp> #include <boost/spirit/repository/include/qi.hpp> namespace qi = boost::spirit::qi; namespace parsing { namespace detail { template <typename Iterator> struct Skipper : qi::grammar<Iterator> { Skipper() : Skipper::base_type(_skip_rule) { _comment = boost::spirit::repository::confix("/*", "*/") [*(qi::char_ - "*/")] // Multi-line | boost::spirit::repository::confix("//", qi::eol) [*(qi::char_ - qi::eol)] // Single-line ; _skip_rule = qi::ascii::space | _comment; } private: qi::rule<Iterator> _comment, _skip_rule; }; template <typename Iterator, typename Skipper = Skipper<Iterator> > struct Grammar : qi::grammar<Iterator, Skipper> { Grammar() : Grammar::base_type(expression) { scalar_literal = qi::uint_ | qi::int_; identifier = (qi::alpha | '_') >> *(qi::alnum | '_'); // Rules operand = (scalar_literal | identifier | ('(' >> expression >> ')')); expression = operand; } private: qi::rule<Iterator> scalar_literal, identifier; // Tokens qi::rule<Iterator, Skipper> operand, expression; // Rules }; } template <typename Iterator, typename Skipper = detail::Skipper<Iterator> > struct facade { template <typename Range> static bool parse(Range const& input) { Iterator f = boost::begin(input), l = boost::end(input); bool ok = qi::phrase_parse(f, l, _parser, _skipper); if (f!=l) std::cout << "Remaining input: '" << std::string(f,l) << "'\n"; return ok; } private: static const detail::Skipper<Iterator> _skipper; static const detail::Grammar<Iterator, Skipper> _parser; }; template <class I, class S> const detail::Skipper<I> facade<I,S>::_skipper = {}; template <class I, class S> const detail::Grammar<I, S> facade<I,S>::_parser = {}; } int main() { using It = std::string::const_iterator; std::string const input = "(123)"; bool ok = parsing::facade<It>::parse(input); if (ok) std::cout << "Parse success\n"; else std::cout << "Parse failed\n"; }
注意结果是一样的,parser/skipper 和原始代码一样都是静态和常量,代码更容易维护(并且在同时)。
这基本上就是 Singletons-are-bad 主题与 inner-const-is-problematic 主题相遇的地方。您不需要将字段设为常量。您不需要将实例设为静态。
只是,如果您愿意,只创建一个实例。此外,解析器现在可复制也不是问题(您不必复制它;但现在您可以)。