как избавиться от escape-символа в токене с помощью Spirit :: lex?

Я хочу маркировать свое собственное расширение синтаксиса SQL. Это включает в себя распознавание экранированной двойной кавычки внутри строки в двойных кавычках. Например. в MySQL эти два строковых токена эквивалентны: """" (вторая двойная кавычка действует как escape-символ) и '"', Я пробовал разные вещи, но я застрял в том, как заменить значение токена.

#include <boost/spirit/include/lex_lexertl.hpp>
namespace lex = boost::spirit::lex;

template <typename Lexer>
struct sql_tokens : lex::lexer<Lexer>
{
sql_tokens()
{
string_quote_double = "\\\"";    // '"'

this->self("INITIAL")
= string_quote_double [ lex::_state = "STRING_DOUBLE" ] // how to also ignore + ctx.more()?
| ...
;

this->self("STRING_DOUBLE")
= lex::token_def<>("[^\\\"]*") // action: ignore + ctx.more()
| lex::token_def<>("\\\"\\\"") // how to set token value to '"' ?
| lex::token_def<>("\\\"") [ lex::_state = "INITIAL" ]
;
}

lex::token_def<> string_quote_double, ...;
};

Итак, как установить значение токена в " когда "" был найден?

Помимо этого у меня есть также следующий вопрос: я могу написать функтор для семантического действия, чтобы вызвать ctx.more () и одновременно игнорировать токен (таким образом объединяя токены «низкого уровня» в строковый токен «высокого уровня»). ). Но как элегантно сочетать это с lex :: _ state = «..»?

3

Решение

РЕДАКТИРОВАНИЕ в ответ на комментарий смотрите ниже «ОБНОВЛЕНИЕ»


Я предлагаю не пытаться решить это в лексере. Пусть лексер выдает необработанные строки:

template <typename Lexer>
struct mylexer_t : lex::lexer<Lexer>
{
mylexer_t()
{
string_quote_double = "\\\"([^\"]|\\\"\\\")*\\\"";

this->self("INITIAL")
= string_quote_double
| lex::token_def<>("[ \t\r\n]") [ lex::_pass = lex::pass_flags::pass_ignore ]
;
}

lex::token_def<std::string> string_quote_double;
};

НОТА Для предоставления такого атрибута токена требуется модифицированный токен typedef:

typedef lex::lexertl::token<char const*, boost::mpl::vector<char, std::string> > token_type;
typedef lex::lexertl::actor_lexer<token_type> lexer_type;

Постпроцесс в парсере:

template <typename Iterator> struct mygrammar_t
: public qi::grammar<Iterator, std::vector<std::string>()>
{
typedef mygrammar_t<Iterator> This;

template <typename TokenDef>
mygrammar_t(TokenDef const& tok) : mygrammar_t::base_type(start)
{
using namespace qi;

string_quote_double %= tok.string_quote_double [ undoublequote ];
start = *string_quote_double;

BOOST_SPIRIT_DEBUG_NODES((start)(string_quote_double));
}

private:
qi::rule<Iterator, std::vector<std::string>()> start;
qi::rule<Iterator, std::string()> string_quote_double;
};

Как вы видете, undoubleqoute может быть любым актером Феникса, который удовлетворяет критериям семантического действия Духа. Мозговой пример реализации будет:

static bool undoublequote(std::string& val)
{
auto outidx = 0;
for(auto in = val.begin(); in!=val.end(); ++in) {
switch(*in) {
case '"':
if (++in == val.end()) { // eat the escape
// end of input reached
val.resize(outidx); // resize to effective chars
return true;
}
// fall through
default:
val[outidx++] = *in; // append the character
}
}

return false; // not ended with double quote as expected
}

Но я предлагаю вам написать «правильный» де-эскапер (так как я уверен, что MySql позволит \t, \r, \u001e или даже более архаичные вещи).

У меня есть еще несколько полных примеров в старых ответах:


На самом деле, как вы указали, довольно просто интегрировать нормализацию значения атрибута в сам лексер:

template <typename Lexer>
struct mylexer_t : lex::lexer<Lexer>
{
struct undoublequote_lex_type {
template <typename, typename, typename, typename> struct result { typedef void type; };

template <typename It, typename IdType, typename pass_flag, typename Ctx>
void operator()(It& f, It& l, pass_flag& pass, IdType& id, Ctx& ctx) const {
std::string raw(f,l);
if (undoublequote(raw))
ctx.set_value(raw);
else
pass = lex::pass_flags::pass_fail;
}
} undoublequote_lex;

mylexer_t()
{
string_quote_double = "\\\"([^\"]|\\\"\\\")*\\\"";

const static undoublequote_lex_type undoublequote_lex;
this->self("INITIAL")
= string_quote_double [ undoublequote_lex ]
| lex::token_def<>("[ \t\r\n]") [ lex::_pass = lex::pass_flags::pass_ignore ]
;
}

lex::token_def<std::string> string_quote_double;
};

Это использует то же самое undoublequote функция показана выше, но оборачивает ее в отложенный вызываемый объект (или «полиморфный функтор») undoublequote_lex_type это удовлетворяет критерии для лексера семантического действия.


Вот полностью рабочее доказательство концепции:

//#include <boost/config/warning_disable.hpp>
//#define BOOST_SPIRIT_DEBUG_PRINT_SOME 80
//#define BOOST_SPIRIT_DEBUG // before including Spirit
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/qi.hpp>
#include <fstream>
#ifdef MEMORY_MAPPED
#   include <boost/iostreams/device/mapped_file.hpp>
#endif
//#include <boost/spirit/include/lex_generate_static_lexertl.hpp>

namespace /*anon*/
{
namespace phx=boost::phoenix;
namespace qi =boost::spirit::qi;
namespace lex=boost::spirit::lex;

template <typename Lexer>
struct mylexer_t : lex::lexer<Lexer>
{
mylexer_t()
{
string_quote_double = "\\\"([^\"]|\\\"\\\")*\\\"";

this->self("INITIAL")
= string_quote_double
| lex::token_def<>("[ \t\r\n]") [ lex::_pass = lex::pass_flags::pass_ignore ]
;
}

lex::token_def<std::string> string_quote_double;
};

static bool undoublequote(std::string& val)
{
auto outidx = 0;
for(auto in = val.begin(); in!=val.end(); ++in) {
switch(*in) {
case '"':
if (++in == val.end()) { // eat the escape
// end of input reached
val.resize(outidx); // resize to effective chars
return true;
}
// fall through
default:
val[outidx++] = *in; // append the character
}
}

return false; // not ended with double quote as expected
}

template <typename Iterator> struct mygrammar_t
: public qi::grammar<Iterator, std::vector<std::string>()>
{
typedef mygrammar_t<Iterator> This;

template <typename TokenDef>
mygrammar_t(TokenDef const& tok) : mygrammar_t::base_type(start)
{
using namespace qi;

string_quote_double %= tok.string_quote_double [ undoublequote ];
start = *string_quote_double;

BOOST_SPIRIT_DEBUG_NODES((start)(string_quote_double));
}

private:
qi::rule<Iterator, std::vector<std::string>()> start;
qi::rule<Iterator, std::string()> string_quote_double;
};
}

std::vector<std::string> do_test_parse(const std::string& v)
{
char const *first = &v[0];
char const *last = first+v.size();

typedef lex::lexertl::token<char const*, boost::mpl::vector<char, std::string> > token_type;
typedef lex::lexertl::actor_lexer<token_type> lexer_type;

typedef mylexer_t<lexer_type>::iterator_type iterator_type;
const static mylexer_t<lexer_type> mylexer;
const static mygrammar_t<iterator_type> parser(mylexer);

auto iter = mylexer.begin(first, last);
auto end = mylexer.end();

std::vector<std::string> data;
bool r = qi::parse(iter, end, parser, data);

r = r && (iter == end);

if (!r)
std::cerr << "parsing (" << iter->state() << ") failed at: '" << std::string(first, last) << "'\n";

return data;
}

int main(int argc, const char *argv[])
{
for (auto&& s : do_test_parse( "\"bla\"\"blo\""))
std::cout << s << std::endl;
}
5

Другие решения

Я предлагаю решить эту и аналогичные задачи в лексере, вместо того, чтобы заставить лексер возвращать что-то промежуточное, а затем анализировать его с помощью дополнительного кода. Двойные кавычки могут быть не единственным осложнением внутри строк, могут быть другие побеги, и лучше иметь четкое описание процесса разбора строк в одном месте и заставить лексер выполнять ВСЕ эту работу.

Вот решение вопроса в теме с использованием только лексера:

using namespace boost::spirit;
namespace px = boost::phoenix;

template <typename Lexer>
struct sql_tokens : public lex::lexer<Lexer>
{
sql_tokens()
{
string = '"';

this->self +=
lex::token_def<>('"')
[
lex::_state = "STRING",
lex::_pass = lex::pass_flags::pass_ignore,
px::ref(curString) = std::string()
];

std::string& (std::string::*append)(std::string::iterator,
std::string::iterator)
{ &std::string::append<std::string::iterator> };

this->self("STRING") =
lex::token_def<>("[^\"]*")
[
lex::_pass = lex::pass_flags::pass_ignore,
px::bind(append, curString, lex::_start, lex::_end)
] |
lex::token_def<>("\\\"\\\"")
[
lex::_pass = lex::pass_flags::pass_ignore,
px::ref(curString) += px::val("\"")
] |
string
[
lex::_val = px::ref(curString),
lex::_state = "INITIAL"];

this->self("WS") = lex::token_def<>("[ \\t\\n]+");
}

std::string curString;
lex::token_def<std::string> string;
};
2