Разбор идентификаторов, кроме ключевых слов
Я изо всех сил пишу анализатор идентификатора, который анализирует строку алфавита, который не является ключевым словом. все ключевые слова находятся в таблице:
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
парсер не может разобрать эти строки. как я могу получить этот парсер правильно?
2 ответа
Ваша проблема вызвана семантикой разностного оператора в 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 | '_') ];
Полный образец ( работает на 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;
}
Проблема в том, что это работает без лексера, то есть, если вы пишете
keyword >> *char_
И положить в whilefoo
это будет разбирать while
как keyword
а также foo
как *char_
,
Вы можете предотвратить это двумя способами: либо указать пробел после ключевого слова, т.е.
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 | '_')];
Проблема с вашим определением состоит в том, что он будет интерпретировать первый набор символов как ключевое слово, тем самым решив вообще его не анализировать. Оператор "xy" означает, анализирует x, но не y. Но если вы передадите 'whilefoo', он будет интерпретировать 'while' в качестве ключевого слова и поэтому вообще не будет анализироваться.