【问题标题】:How to tokenize C++ using Boost Regex如何使用 Boost Regex 标记 C++
【发布时间】:2014-04-27 05:42:49
【问题描述】:

我目前正在使用 boost regex 为一个类开发一个标记器。我对 boost 不太熟悉,所以我可能与我目前所拥有的有所不同,但无论如何,这就是我正在使用的:

regex re("[\\s*,()=;<>\+-]{1,2}");
sregex_token_iterator i(text.begin(), text.end(), re, -1);
sregex_token_iterator j;

sregex_token_iterator begin(text.begin(), text.end(), re), end;

unsigned count = 0;
while(i != j)
{
    if(*i != ' ' && *i != '\n')
    {
        count++;
        cout << "From i - " << count << "   " << *i << endl;
    }
    i++;

    if(*begin != ' ' && *begin != '\n')
    {
        count++;
        cout << "Form j - " << count << "   " << *begin << endl;
    }

    begin++;
}

cout << "There were " << count << " tokens found." << endl;

所以,基本上,我使用空格和符号作为分隔符,但我仍然输出两者(因为我仍然希望符号成为标记)。就像我说的,我对 boost 不是很熟悉,所以如果我采取了正确的方法,我并不肯定。

我的最终目标是拆分一个包含简单 c++ 代码块的文件并对其进行标记,这是我正在使用的示例文件:

#define MAX 5


int main(int argc)
{
    for(int i = 0; i < MAX; i ++)
    {
        cout << "i is equal to " << i << endl; 
    }

    return 0;
}

我遇到了麻烦,因为它将下一行和空格计算为标记,我真的需要将它们扔掉。此外,我很难使用“++”标记,我似乎无法找出正确的表达式来计算“++”。

任何帮助将不胜感激!

谢谢! 蒂姆

【问题讨论】:

    标签: c++ regex boost tokenize


    【解决方案1】:

    首先,

    • Boost 有 Boost Wave,它有(我认为有几个)现成的用于 C++ 源代码的标记器
    • Boost 有 Spirit Lex,它是一个词法分析器,可以基于正则表达式模式和一些状态支持进行标记。它允许动态词法分析器表和静态生成的词法分析器表

    如果您对使用 Lex 感兴趣,我为自己做了一个快速而肮脏的手指练习:it tokenizes itself Live On Coliru

    注意事项:

    • Lex 标记器与 Boost Spirit Qi 很好地配合进行解析(但老实说,我更喜欢直接在源迭代器上执行 Spirit 语法)。
    • 它公开了一个迭代器接口,尽管我的示例利用回调接口来显示标记:

      int main()
      {
          typedef boost::spirit::istream_iterator It;
          typedef lex::lexertl::token<It, boost::mpl::vector<int, double>, boost::mpl::true_ > token_type;
          tokens<lex::lexertl::actor_lexer<token_type> > lexer;
      
          std::ifstream ifs("main.cpp");
          ifs >> std::noskipws;
          It first(ifs), last;
          bool ok = lex::tokenize(first, last, lexer, process_token());
      
          std::cout << "\nTokenization " << (ok?"succeeded":"failed") << "; remaining input: '" << std::string(first,last) << "'\n";
      }
      

      在输出中被标记为(修剪前面的输出):

      [int][main][(][)][{][typedef][boost][::][spirit][::][istream_iterator][It][;][typedef][lex][::][lexertl][::][token][&lt;][It][,][boost][::][mpl][::][vector][&lt;][int][,][double][&gt;][,][boost][::][mpl][::][true_][&gt;][token_type][;][tokens][&lt;][lex][::][lexertl][::][actor_lexer][&lt;][token_type][&gt;][&gt;][lexer][;][std][::][ifstream][ifs][(]["main.cpp"][)][;][ifs][&gt;&gt;][std][::][noskipws][;][It][first][(][ifs][)][,][last][;][bool][ok][=][lex][::][tokenize][(][first][,][last][,][lexer][,][process_token][(][)][)][;][std][::][cout][&lt;&lt;]["\nTokenization "][&lt;&lt;][(][ok][?]["succeeded"][:]["failed"][)][&lt;&lt;]["; remaining input: '"][&lt;&lt;][std][::][string][(][first][,][last][)][&lt;&lt;]["'\n"][;][}]
      Tokenization succeeded; remaining input: ''

    • 您实际上应该想要一个不同的词法分析器状态来解析预处理器指令(行尾变得有意义,并且其他几个表达式/关键字是有效的)。在现实生活中,通常有一个单独的预处理器步骤在此处进行自己的词法分析。 (例如,在对包含文件规范进行词法分析时可以看到这种情况的后果)

    • 词法分析器中标记的顺序对结果至关重要
    • 在此示例中,您始终将&amp; 标记匹配为binop_。你 可能想要匹配 ampersand_ 令牌并在解析时决定 无论是二元运算符(按位与)、一元运算符(地址)、引用类型限定符等。C++ 的解析真的很有趣:|
    • 支持评论!
    • 不支持二合字母/三合字母 :)
    • 不支持编译指示、行/文件指令等

    总而言之,如果你想制作一个简单的语法高亮器或格式化程序,这应该非常有用。除此之外的任何事情都需要更多的解析/语义分析。

    完整列表:

    #include <boost/spirit/include/support_istream_iterator.hpp>
    #include <boost/spirit/include/lex_lexertl.hpp>
    #include <fstream>
    #include <sstream>    
    #include <boost/lexical_cast.hpp>
    
    namespace lex = boost::spirit::lex;
    
    template <typename Lexer>
    struct tokens : lex::lexer<Lexer>
    {
        tokens() 
        {
            pound_   = "#";
            define_  = "define";
            if_      = "if";
            else_    = "else";
            endif_   = "endif";
            ifdef_   = "ifdef";
            ifndef_  = "ifndef";
            defined_ = "defined";
            keyword_ = "for|break|continue|while|do|switch|case|default|if|else|return|goto|throw|catch"
                       "static|volatile|auto|void|int|char|signed|unsigned|long|double|float|"
                       "delete|new|virtual|override|final|"
                       "typename|template|using|namespace|extern|\"C\"|"
                       "friend|public|private|protected|"
                       "class|struct|enum|"
                       "register|thread_local|noexcept|constexpr";
            scope_   = "::";
            dot_     = '.';
            arrow_   = "->";
            star_    = '*';
            popen_   = '(';
            pclose_  = ')';
            bopen_   = '{';
            bclose_  = '}';
            iopen_   = '[';
            iclose_  = ']';
            colon_   = ':';
            semic_   = ';';
            comma_   = ',';
            tern_q_  = '?';
            relop_   = "==|!=|<=|>=|<|>";
            assign_  = '=';
            incr_    = "\\+\\+";
            decr_    = "--";
            binop_   = "[-+/%&|^]|>>|<<";
            unop_    = "[-+~!]";
    
            real_    = "[-+]?[0-9]+(e[-+]?[0-9]+)?f?";
            int_     = "[-+]?[0-9]+";
            identifier_ = "[a-zA-Z_][a-zA-Z0-9_]*";
    
            ws_            = "[ \\t\\r\\n]";
            line_comment_  = "\\/\\/.*?[\\r\\n]";
            block_comment_ = "\\/\\*.*?\\*\\/";
    
            this->self.add_pattern
                ("SCHAR", "\\\\(x[0-9a-fA-F][0-9a-fA-F]|[\\\\\"'0tbrn])|[^\"\\\\'\\r\\n]")
                ;
            string_lit = "\\\"('|{SCHAR})*?\\\"";
            char_lit   = "'(\\\"|{SCHAR})'";
    
            this->self += 
                  pound_ | define_ | if_ | else_ | endif_ | ifdef_ | ifndef_ | defined_
                | keyword_ | scope_ | dot_ | arrow_ | star_ | popen_ | pclose_ | bopen_ | bclose_ | iopen_ | iclose_ | colon_ | semic_ | comma_ | tern_q_
                | relop_ | assign_ | incr_ | decr_ | binop_ | unop_
                | int_ | real_ | identifier_ | string_lit | char_lit
                // ignore whitespace and comments
                | ws_           [ lex::_pass = lex::pass_flags::pass_ignore ]
                | line_comment_ [ lex::_pass = lex::pass_flags::pass_ignore ]
                | block_comment_[ lex::_pass = lex::pass_flags::pass_ignore ] 
                ;
        }
    
      private:
        lex::token_def<> pound_, define_, if_, else_, endif_, ifdef_, ifndef_, defined_;
        lex::token_def<> keyword_, scope_, dot_, arrow_, star_, popen_, pclose_, bopen_, bclose_, iopen_, iclose_, colon_, semic_, comma_, tern_q_;
        lex::token_def<> relop_, assign_, incr_, decr_, binop_, unop_;
        lex::token_def<int> int_;
        lex::token_def<double> real_;
        lex::token_def<> identifier_, string_lit, char_lit;
        lex::token_def<lex::omit> ws_, line_comment_, block_comment_;
    };
    struct token_value : boost::static_visitor<std::string>
    {
        template <typename... T> // the token value can be a variant over any of the exposed attribute types
        std::string operator()(boost::variant<T...> const& v) const {
            return boost::apply_visitor(*this, v);
        }
    
        template <typename T> // the default value is a pair of iterators into the source sequence
        std::string operator()(boost::iterator_range<T> const& v) const {
            return { v.begin(), v.end() };
        }
    
        template <typename T>
        std::string operator()(T const& v) const { 
            // not taken unless used in Spirit Qi rules, I guess
            return std::string("attr<") + typeid(v).name() + ">(" + boost::lexical_cast<std::string>(v) + ")";
        }
    };
    
    struct process_token
    {
        template <typename T>
        bool operator()(T const& token) const {
            std::cout << '[' /*<< token.id() << ":" */<< print(token.value()) << "]";
            return true;
        }
    
        token_value print;
    };
    
    #if 0
    std::string read(std::string fname)
    {
        std::ifstream ifs(fname);
        std::ostringstream oss;
        oss << ifs.rdbuf();
        return oss.str();
    }
    #endif
    
    int main()
    {
        typedef boost::spirit::istream_iterator It;
        typedef lex::lexertl::token<It, boost::mpl::vector<int, double>, boost::mpl::true_ > token_type;
        tokens<lex::lexertl::actor_lexer<token_type> > lexer;
    
        std::ifstream ifs("main.cpp");
        ifs >> std::noskipws;
        It first(ifs), last;
        bool ok = lex::tokenize(first, last, lexer, process_token());
    
        std::cout << "\nTokenization " << (ok?"succeeded":"failed") << "; remaining input: '" << std::string(first,last) << "'\n";
    }
    

    【讨论】:

    • 哦,我明白了。我会试一试这样的事情。我会回来告诉你它是如何为我工作的。非常感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-01
    • 2021-09-12
    • 2011-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多