深入研究Clang(五) Clang Lexer代码阅读笔记之Lexer

简介: 作者:史宁宁(snsn1984)Clang的Lexer(词法分析器)的源码的主要位置如下:clang/lib/Lex    这里是主要的Lexer的代码;clang/include/clang/Lex   这里是Lexer的头文件代码的位置;同时,Lexer还使用了clangBasic库,所以要分析Lexer的代码,clangBasic(clang/lib/Basic)的一些代码也会用到。

作者:史宁宁(snsn1984)


Clang的Lexer(词法分析器)的源码的主要位置如下:

clang/lib/Lex    这里是主要的Lexer的代码;

clang/include/clang/Lex   这里是Lexer的头文件代码的位置;

同时,Lexer还使用了clangBasic库,所以要分析Lexer的代码,clangBasic(clang/lib/Basic)的一些代码也会用到。


首先从Lexer入手。


clang/include/clang/Lex/Lexer.h

clang::Lexer:

00057   //===--------------------------------------------------------------------===//
00058   // Context-specific lexing flags set by the preprocessor.
00059   //
00060
00061   /// ExtendedTokenMode - The lexer can optionally keep comments and whitespace
00062   /// and return them as tokens.  This is used for -C and -CC modes, and
00063   /// whitespace preservation can be useful for some clients that want to lex
00064   /// the file in raw mode and get every character from the file.
00065   ///
00066   /// When this is set to 2 it returns comments and whitespace.  When set to 1
00067   /// it returns comments, when it is set to 0 it returns normal tokens only.
00068   unsigned char ExtendedTokenMode;
00069
00070   //===--------------------------------------------------------------------===//
这个成员变量保存词法分析的一个状态,根据它的值的不同:0、1、2,分别对应只返回正常的token,返回comments
和正常的token,返回空格、comments和正常的token。
下面是几个操作这个成员变量的函数,基本上都是获取值、设置值和重设值。代码不复杂,
00162   /// isKeepWhitespaceMode - Return true if the lexer should return tokens for
00163   /// every character in the file, including whitespace and comments.  This
00164   /// should only be used in raw mode, as the preprocessor is not prepared to
00165   /// deal with the excess tokens.
00166   bool isKeepWhitespaceMode() const {
00167     return ExtendedTokenMode > 1;
00168   }
00169
00170   /// SetKeepWhitespaceMode - This method lets clients enable or disable
00171   /// whitespace retention mode.
00172   void SetKeepWhitespaceMode(bool Val) {
00173     assert((!Val || LexingRawMode || LangOpts.TraditionalCPP) &&
00174            "Can only retain whitespace in raw mode or -traditional-cpp");
00175     ExtendedTokenMode = Val ? 2 : 0;
00176   }
00177
00178   /// inKeepCommentMode - Return true if the lexer should return comments as
00179   /// tokens.
00180   bool inKeepCommentMode() const {
00181     return ExtendedTokenMode > 0;
00182   }
00183
00184   /// SetCommentRetentionMode - Change the comment retention mode of the lexer
00185   /// to the specified mode.  This is really only useful when lexing in raw
00186   /// mode, because otherwise the lexer needs to manage this.
00187   void SetCommentRetentionState(bool Mode) {
00188     assert(!isKeepWhitespaceMode() &&
00189            "Can't play with comment retention state when retaining whitespace");
00190     ExtendedTokenMode = Mode ? 1 : 0;
00191   }
00192
00193   /// Sets the extended token mode back to its initial value, according to the
00194   /// language options and preprocessor. This controls whether the lexer
00195   /// produces comment and whitespace tokens.
00196   ///
00197   /// This requires the lexer to have an associated preprocessor. A standalone
00198   /// lexer has nothing to reset to.
00199   void resetExtendedTokenMode();
关于raw mode:
raw mode的时候,ExtendedTokenMode = 2,Lexer会输出包含空格、comments和正常tokens在内的所有
字符。在Lexer的父类:clang::PreprocessorLexer类中(),有一个成员变量:
00049   /// \brief True if in raw mode.
00050   ///
00051   /// Raw mode disables interpretation of tokens and is a far faster mode to
00052   /// lex in than non-raw-mode.  This flag:
00053   ///  1. If EOF of the current lexer is found, the include stack isn't popped.
00054   ///  2. Identifier information is not looked up for identifier tokens.  As an
00055   ///     effect of this, implicit macro expansion is naturally disabled.
00056   ///  3. "#" tokens at the start of a line are treated as normal tokens, not
00057   ///     implicitly transformed by the lexer.
00058   ///  4. All diagnostic messages are disabled.
00059   ///  5. No callbacks are made into the preprocessor.
00060   ///
00061   /// Note that in raw mode that the PP pointer may be null.
00062   bool LexingRawMode;
它可以表明Lexer是否在raw mode下。同时,这里的注释也说明了raw model的作用。

从clang::Lexer的定义可以看出,它是clang::PreprocessorLexer的子类,上面raw model的部分也引用了clang::PreprocessorLexer类的代码,下面看下clang::PreprocessorLexer的代码。

clang/include/clang/Lex/PreprocessorLexer.h

00022 namespace clang {
00023 
00024 class FileEntry;
00025 class Preprocessor;
从这里可以看出clang::PreprocessorLexer使用了上面两个类,而在头文件中的具体位置就是:

00027 class PreprocessorLexer {
00028   virtual void anchor();
00029 protected:
00030   Preprocessor *PP;              // Preprocessor object controlling lexing.
以及

00164   /// getFileEntry - Return the FileEntry corresponding to this FileID.  Like
00165   /// getFileID(), this only works for lexers with attached preprocessors.
00166   const FileEntry *getFileEntry() const;

从代码中可以看出,这两个类,一个是作为成员变量,一个是作为了一个成员函数的返回类型来使用的。我们跟踪代码去看下这两个类的具体实现。这两个类的具体实现,FileEntry较为简单,很容易看出到底内容;而Preprocessor类较为复杂,牵涉内容较多,在这里暂且不作分析。后续继续分析。






目录
相关文章
|
前端开发 C语言 iOS开发
基于LLVM的编译原理简明教程 (1) - 写编译器越来越容易了
跟学院派的厚书给大家的印象不同,其实用LLVM写个简单的编译器是件容易的事情,因为大部分事情LLVM都替我们做了。
8331 0
|
前端开发 IDE 编译器
LLVM编译器前端 Clang 简介
昨天晚上安装rails的开发环境,被ruby的编译搞的有点崩溃。下载的ruby的源码不能用系统自带的gcc -4.21编译,也不能用系统自带的clang进行编译,必须下载并使用gcc -4.2进行编译才能通过。今天稍微看看编译器的一些背景。
557 0
LLVM编译器前端 Clang 简介
|
编译器 iOS开发 Windows
带你读《LLVM编译器实战教程》之一:构建和安装LLVM
本书的前半部分将向您介绍怎么样去配置、构建、和安装LLVM的不同软件库、工具和外部项目。接下来,本书的后半部分将向您介绍LLVM的各种设计细节,并逐步地讲解LLVM的各个编译步骤:前段、中间表示(IR)、后端、即时编译(JIT)引擎、跨平台编译和插件接口。本书包含有大量翔实的示例和代码片段,以帮助读者平稳顺利的掌握LLVM的编译器开发环境。
20171 0
|
Android开发 C语言 C++
《深入理解LLVM》第一章 LLVM简介
第一章 LLVM简介 作者:史宁宁 1.1 LLVM是什么 LLVM是什么?这是一个虽然基础,但是也曾经让很多新入门的人迷惑的一个问题。从字面上来讲,LLVM(Low Level Virtual Machine)是一个底层虚拟机,LLVM曾经有一部分功能对虚拟机有所帮助。
2529 0
|
前端开发 编译器 C++
深入研究Clang(一)Clang和LLVM的关系及整体架构
作者:史宁宁(snsn1984) Clang和LLVM的关系        Clang和LLVM到底是什么关系,这是在研究Clang的过程中所不可避免的一个问题。
1748 0
|
Unix
深入研究Clang(九) Clang代码阅读之打log读流程2
继续上一篇,同样的hello.c,同样的执行过程,只不过继续添加了一些log信息,而且对代码进行了更近一步的挖掘。先看输入和输出的log信息(前半部分): shining@shining-VirtualBox:~/llvm-3.
1450 0
深入研究Clang(八) Clang代码阅读之打log读流程1
这个过程简单的分为几步。 第一步:写一个简单的小程序,hello.c。内容如下: #includeint main() {  printf("Hello world!\n");} 第二步:找出如何在LLVM里面输出信息,最后选择采用llvm::errs(),等于采用了LLVM的错误机制。
1235 0