如何使用Boost Regex标记C++
How to tokenize C++ using Boost Regex
我目前正在为一个使用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;
所以,基本上,我使用空格和符号作为分隔符,但我仍然输出这两者(因为我仍然希望符号是标记)。就像我说的,我对助推不是很熟悉,所以我不确定我是否采取了正确的方法。
我的最终目标是分割一个有简单c++代码块的文件并将其标记化,下面是我使用的示例文件:
#define MAX 5
int main(int argc)
{
for(int i = 0; i < MAX; i ++)
{
cout << "i is equal to " << i << endl;
}
return 0;
}
我遇到了麻烦,因为它将下一行和空格作为标记进行计数,我真的需要把它们扔掉。此外,我很难使用"++"标记,我似乎找不出正确的表达式来计算"++"。
如有任何帮助,我们将不胜感激!
谢谢!Tim
首先,
- Boost有Boost Wave,它有(我认为有几个)现成的C++源代码标记器
- Boost有Spirit-Lex,这是一个可以基于正则表达式模式和一些状态支持进行标记的lexer。它允许动态lexer表和静态生成的lexer表
如果你有兴趣使用Lex,我运行了一个快速&我自己的脏手指练习:它象征着自己在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][<][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"][;][}]
Tokenization succeeded; remaining input: ''
-
实际上,您应该想要一个不同的lexer状态来解析预处理器指令(行尾变得有意义,并且其他几个表达式/关键字是有效的)。在现实生活中,通常有一个单独的预处理器步骤在这里进行自己的词法分析。(当lexing include文件规范时,可以看到这种情况的后果,例如
- lexer中标记的排序对结果至关重要
- 在这个示例中,您总是将
&
令牌匹配为binop_
。you had 的常用口语形式可能想要匹配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";
}
- 将依赖名称显式标记为类型名和模板的奇怪之处
- 为什么output_editor Concept不需要output_e迭代器标记
- 标记 '","' 之前的预期主表达式
- 为什么g++在未执行的代码处标记强制转换错误
- muQueue.front() 给出了 const 实例,即使我没有将其标记为 const
- 使用 boost 进行标记化会给出相同的输出
- 为什么 -mmacosx-version-min=10.10 不阻止使用标记为从 10.11 开始的函数?
- 错误 C2760:语法错误:映射迭代器上意外的标记"标识符",预期的";"
- C++标头错误 C2238 意外标记";"
- 语句错误:"","标记之前有"预期的')'
- 编译 llvm 3.1 时,为什么会出现错误:在">"标记之前预期主表达式
- 生成错误 - "." 标记之前的预期主表达式
- 错误:应在"{"标记之前使用"",""或";&qu
- '{'标记之前的预期类名,然后在预声明时无效使用不完整的类型'class class_name'
- 将字符串拆分为标记,并将标记拆分为两个单独的数组
- 无法使用迭代器标记调度实例化模板
- 如何分隔字符串并将标记传递给方法
- SFML:错误:")"标记之前的预期主表达式
- "]"标记之前的预期主要表达式(平分搜索)
- CGAL:在浏览平面地图时使用类型"标记"