3

SQL 構文の独自の拡張機能をトークン化したいと考えています。これには、二重引用符で囲まれた文字列内のエスケープされた二重引用符の認識が含まれます。たとえば、MySQL では、これら 2 つの文字列トークンは同等です: """"(2 番目の二重引用符はエスケープ文字として機能します) と'"'. さまざまなことを試しましたが、トークンの値を置き換える方法に行き詰まっています。

#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 = ".." とエレガントに組み合わせる方法は?

4

2 に答える 2

2

これと同様のタスクをレクサーで解決することをお勧めします。レクサーが中間的なものを返してから追加のコードで解析するのではありません。二重引用符は、文字列内の唯一の複雑さではありません。他のエスケープが存在する可能性があります。文字列解析プロセスの明確な説明を 1 か所にまとめて、lexer にすべての仕事を行わせることをお勧めします。

レクサーのみを使用したトピックの質問の解決策は次のとおりです。

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;
};
于 2014-01-09T23:28:49.440 に答える