Сравнение строк с учетом регистра символов Boost::Spirit Token в семантическом действии
У меня есть токенизатор и парсер. синтаксический анализатор имеет специальный тип токена, KEYWORD, для ключевых слов (их ~50). В моем парсере я хочу убедиться, что токены соответствуют моим ожиданиям, поэтому у меня есть правила для каждого из них. Вот так:
KW_A = tok.KEYWORDS[_pass = (_1 == "A")];
KW_B = tok.KEYWORDS[_pass = (_1 == "B")];
KW_C = tok.KEYWORDS[_pass = (_1 == "C")];
Это работает достаточно хорошо, но не учитывает регистр (и грамматика, которую я пытаюсь обработать, такова!). Я хотел бы использовать boost::iequals, но попытки конвертировать _1 в std:: string приводят к следующей ошибке:
error: no viable conversion from 'const _1_type' (aka 'const actor<argument<0> >') to 'std::string' (aka 'basic_string<char>')
Как я могу рассматривать эти ключевые слова как строки и гарантировать, что они являются ожидаемым текстом независимо от регистра?
1 ответ
Небольшое обучение прошло долгий путь. Я добавил следующее в свой лексер:
struct normalise_keyword_impl
{
template <typename Value>
struct result
{
typedef void type;
};
template <typename Value>
void operator()(Value const& val) const
{
// This modifies the original input string.
typedef boost::iterator_range<std::string::iterator> iterpair_type;
iterpair_type const& ip = boost::get<iterpair_type>(val);
std::for_each(ip.begin(), ip.end(),
[](char& in)
{
in = std::toupper(in);
});
}
};
boost::phoenix::function<normalise_keyword_impl> normalise_keyword;
// The rest...
};
А затем использовал phoenix для привязки действия к ключевому токену в моем конструкторе, например так:
this->self =
KEYWORD [normalise_keyword(_val)]
// The rest...
;
Несмотря на то, что это завершает то, что было после, оно изменяет исходную последовательность ввода. Могу ли я внести некоторые изменения, чтобы я мог использовать const_iterator вместо итератора и избежать изменения моей входной последовательности?
Я попытался вернуть std:: string, скопированный из ip.begin() в ip.end() и в верхнем регистре, используя boost::toupper(...), присвоив его _val. Несмотря на то, что он компилировался и работал, были явно некоторые проблемы с тем, что он производил:
Enter a sequence to be tokenised: select a from b
Input is 'select a from b'.
result is SELECT
Token: 0: KEYWORD ('KEYWOR')
Token: 1: REGULAR_IDENTIFIER ('a')
result is FROM
Token: 0: KEYWORD ('KEYW')
Token: 1: REGULAR_IDENTIFIER ('b')
Похоже, у меня есть кое-что еще.
Окончательное решение
Хорошо, я закончил с использованием этой функции:
struct normalise_keyword_impl
{
template <typename Value>
struct result
{
typedef std::string type;
};
template <typename Value>
std::string operator()(Value const& val) const
{
// Copy the token and update the attribute value.
typedef boost::iterator_range<std::string::const_iterator> iterpair_type;
iterpair_type const& ip = boost::get<iterpair_type>(val);
auto result = std::string(ip.begin(), ip.end());
result = boost::to_upper_copy(result);
return result;
}
};
И это семантическое действие:
KEYWORD [_val = normalise_keyword(_val)]
С (и это разобрались) модифицированным token_type:
typedef std::string::const_iterator base_iterator;
typedef boost::spirit::lex::lexertl::token<base_iterator, boost::mpl::vector<std::string> > token_type;
typedef boost::spirit::lex::lexertl::actor_lexer<token_type> lexer_type;
typedef type_system::Tokens<lexer_type> tokens_type;
typedef tokens_type::iterator_type iterator_type;
typedef type_system::Grammar<iterator_type> grammar_type;
// Establish our lexer and our parser.
tokens_type lexer;
grammar_type parser(lexer);
// ...
Важным дополнением является boost::mpl::vector<std::string> >
, Результат:
Enter a sequence to be tokenised: select a from b
Input is 'select a from b'.
Token: 0: KEYWORD ('SELECT')
Token: 1: REGULAR_IDENTIFIER ('a')
Token: 0: KEYWORD ('FROM')
Token: 1: REGULAR_IDENTIFIER ('b')
Я понятия не имею, почему это решило проблему, поэтому, если кто-то может поделиться со своим опытом, я готовый студент.