【问题标题】:how to get rid of escape character in a token with spirit::lex?如何用 Spirit::lex 去除令牌中的转义字符?
【发布时间】:2013-05-13 06:32:01
【问题描述】:

我想标记我自己的 SQL 语法扩展。这涉及识别双引号字符串中的转义双引号。例如。在 MySQL 中,这两个字符串标记是等价的:""""(第二个双引号充当转义字符)和'"'。我尝试了不同的方法,但我不知道如何替换令牌的值。

#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 = ".." 结合起来呢?

【问题讨论】:

    标签: c++ boost-spirit boost-spirit-lex


    【解决方案1】:

    已编辑回应评论,见下文“更新”


    我建议不要尝试在词法分析器中解决这个问题。让词法分析器产生原始字符串:

    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 可以是任何满足 Spirit 语义动作标准的 Phoenix 演员。一个脑残的示例实现是:

    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 函数,但将其包装在满足the criteria for a Lexer Semantic Action 的延迟可调用对象(或“多态函子”)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;
    }
    

    【讨论】:

    • 我喜欢如何用一个正则表达式匹配这样一个带有转义的字符串的想法——实际上我认为这是不可能的。仍然我的目标是/希望有一个更简单的解决方案,即不涉及语法。这真的需要吗?也许只是为了让测试/调试更容易?
    • @coproc 当然可以:/ 这不是我建议的。我添加了一个包装器undouble_quote_lex 函子,它向您展示了如何在纯 lex 中执行此操作(请参阅 UPDATE)。 similarly adapted sample program 仍按预期打印bla"blo
    • 我仍在研究令牌类型。 mpl::vector 中的char 类型对于AttributeTypes 的用途是什么? std::string 类型不够吗?实际上我不明白为什么在一个令牌类型定义中可以有多个属性类型以及如何使用它们。
    • @coproc 哦,显然你可以删除字符。我在那里有它,因为我最初尝试使用您的原始令牌定义。令牌值是列出的类型的variant。这一切都记录在这里:boost.org/doc/libs/1_53_0/libs/spirit/doc/html/spirit/lex/…
    【解决方案2】:

    我建议在词法分析器中解决这个和类似的任务,而不是让词法分析器返回一些中间值,然后用额外的代码解析它。双引号可能不是字符串中唯一的复杂情况,可能还有其他转义,最好在一个地方清楚地描述字符串解析过程并让词法分析器完成所有工作。

    这是仅使用词法分析器的主题中问题的解决方案:

    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;
    };
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多