解析除关键字之外的标识符
parsing identifiers except keywords
我正在努力编写一个标识符解析器,它解析一个不是关键字的字母数字字符串。
关键字都在 table:
struct keywords_t : x3::symbols<x3::unused_type> {
keywords_t() {
add("for", x3::unused)
("in", x3::unused)
("while", x3::unused);
}
} const keywords;
标识符的解析器应该是这样的:
auto const identifier_def =
x3::lexeme[
(x3::alpha | '_') >> *(x3::alnum | '_')
];
现在我尝试将它们结合起来,以便标识符解析器在解析关键字时失败。
我这样试过:
auto const identifier_def =
x3::lexeme[
(x3::alpha | '_') >> *(x3::alnum | '_')
]-keywords;
还有这个:
auto const identifier_def =
x3::lexeme[
(x3::alpha | '_') >> *(x3::alnum | '_') - keywords
];
它适用于大多数输入,但如果字符串以 int, whilefoo, forbar
之类的关键字开头,则解析器无法解析此字符串。
我怎样才能让这个解析器正确?
问题是,它在没有词法分析器的情况下运行,也就是说,如果你写
keyword >> *char_
并输入 whilefoo
它会将 while
解析为 keyword
并将 foo
解析为 *char_
.
您可以通过两种方式防止这种情况发生:要么要求在关键字后添加 space,即
auto keyword_rule = (keyword >> x3::space);
//or if you use phrase_parse
auto keyword_rule = x3::lexeme[keyword >> x3::space];
您描述的另一种方法也是可行的,即明确地从字符串中删除关键字(我会那样做):
auto string = x3::lexeme[!keyword >> (x3::alpha | '_') >> *(x3::alnum | '_')];
您的定义存在问题,它将第一组字符解释为关键字,因此选择根本不解析它。 'x-y' 运算符意味着解析 x,但不解析 y。但是,如果您传递 'whilefoo',它将把 'while' 解释为关键字,因此根本不会解析。
你的问题是由Spirit中差分运算符的语义引起的。当您拥有 a - b
精神时,它会执行以下操作:
- 检查
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 | '_') ];
完整样本(Running on 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;
}
我正在努力编写一个标识符解析器,它解析一个不是关键字的字母数字字符串。 关键字都在 table:
struct keywords_t : x3::symbols<x3::unused_type> {
keywords_t() {
add("for", x3::unused)
("in", x3::unused)
("while", x3::unused);
}
} const keywords;
标识符的解析器应该是这样的:
auto const identifier_def =
x3::lexeme[
(x3::alpha | '_') >> *(x3::alnum | '_')
];
现在我尝试将它们结合起来,以便标识符解析器在解析关键字时失败。 我这样试过:
auto const identifier_def =
x3::lexeme[
(x3::alpha | '_') >> *(x3::alnum | '_')
]-keywords;
还有这个:
auto const identifier_def =
x3::lexeme[
(x3::alpha | '_') >> *(x3::alnum | '_') - keywords
];
它适用于大多数输入,但如果字符串以 int, whilefoo, forbar
之类的关键字开头,则解析器无法解析此字符串。
我怎样才能让这个解析器正确?
问题是,它在没有词法分析器的情况下运行,也就是说,如果你写
keyword >> *char_
并输入 whilefoo
它会将 while
解析为 keyword
并将 foo
解析为 *char_
.
您可以通过两种方式防止这种情况发生:要么要求在关键字后添加 space,即
auto keyword_rule = (keyword >> x3::space);
//or if you use phrase_parse
auto keyword_rule = x3::lexeme[keyword >> x3::space];
您描述的另一种方法也是可行的,即明确地从字符串中删除关键字(我会那样做):
auto string = x3::lexeme[!keyword >> (x3::alpha | '_') >> *(x3::alnum | '_')];
您的定义存在问题,它将第一组字符解释为关键字,因此选择根本不解析它。 'x-y' 运算符意味着解析 x,但不解析 y。但是,如果您传递 'whilefoo',它将把 'while' 解释为关键字,因此根本不会解析。
你的问题是由Spirit中差分运算符的语义引起的。当您拥有 a - b
精神时,它会执行以下操作:
- 检查
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 | '_') ];
完整样本(Running on 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;
}