Exa*_*gon 3 c++ parsing boost-spirit c++14 boost-spirit-x3
我正在努力编写一个标识符解析器,它解析一个不是关键字的字母字符串。关键字都在一个表中:
struct keywords_t : x3::symbols<x3::unused_type> {
keywords_t() {
add("for", x3::unused)
("in", x3::unused)
("while", x3::unused);
}
} const keywords;
Run Code Online (Sandbox Code Playgroud)
标识符的解析器应该是这样的:
auto const identifier_def =
x3::lexeme[
(x3::alpha | '_') >> *(x3::alnum | '_')
];
Run Code Online (Sandbox Code Playgroud)
现在我尝试将这些组合起来,以便标识符解析器在解析关键字时失败。我是这样试的:
auto const identifier_def =
x3::lexeme[
(x3::alpha | '_') >> *(x3::alnum | '_')
]-keywords;
Run Code Online (Sandbox Code Playgroud)
和这个:
auto const identifier_def =
x3::lexeme[
(x3::alpha | '_') >> *(x3::alnum | '_') - keywords
];
Run Code Online (Sandbox Code Playgroud)
它适用于大多数输入,但如果字符串以关键字开头,例如int, whilefoo, forbar解析器无法解析此字符串。我怎样才能让这个解析器正确?
您的问题是由 Spirit 中差异运算符的语义引起的。当您拥有a - bSpirit 时,请执行以下操作:
b匹配:
a - b失败并且不会解析任何内容。b失败,则检查是否a匹配:
a失败,则a - b失败并且不会解析任何内容。a成功,a - b成功并解析任何a解析。在您的情况下 ( unchecked_identifier - keyword) 只要标识符以关键字开头,keyword就会匹配并且您的解析器将失败。因此,您需要在keyword传递不同关键字时与匹配的内容进行交换,但在关键字后跟其他内容时失败。在not predicate(!)可以帮助这一点。
auto const distinct_keyword = x3::lexeme[ keyword >> !(x3::alnum | '_') ];
Run Code Online (Sandbox Code Playgroud)
完整示例(在 Coliru 上运行):
//#define BOOST_SPIRIT_X3_DEBUG
#include <iostream>
#include <boost/spirit/home/x3.hpp>
namespace parser {
namespace x3 = boost::spirit::x3;
struct keywords_t : x3::symbols<x3::unused_type> {
keywords_t() {
add("for", x3::unused)
("in", x3::unused)
("while", x3::unused);
}
} const keywords;
x3::rule<struct identifier_tag,std::string> const identifier ("identifier");
auto const distinct_keyword = x3::lexeme[ keywords >> !(x3::alnum | '_') ];
auto const unchecked_identifier = x3::lexeme[(x3::alpha | x3::char_('_')) >> *(x3::alnum | x3::char_('_'))];
auto const identifier_def = unchecked_identifier - distinct_keyword;
//This should also work:
//auto const identifier_def = !distinct_keyword >> unchecked_identifier
BOOST_SPIRIT_DEFINE(identifier);
bool is_identifier(const std::string& input)
{
auto iter = std::begin(input), end= std::end(input);
bool result = x3::phrase_parse(iter,end,identifier,x3::space);
return result && iter==end;
}
}
int main() {
std::cout << parser::is_identifier("fortran") << std::endl;
std::cout << parser::is_identifier("for") << std::endl;
std::cout << parser::is_identifier("integer") << std::endl;
std::cout << parser::is_identifier("in") << std::endl;
std::cout << parser::is_identifier("whileechoyote") << std::endl;
std::cout << parser::is_identifier("while") << std::endl;
}
Run Code Online (Sandbox Code Playgroud)