Clang之词法分析Lex

Clang是LLVM编译器框架的前端(Frontend)编译器,可编译链接C、C++、Objective-C和Objective-C++四种语言的项目代码。Clang 的开发目标是提供一个可以替代 GCC 的前端编译器,与GCC相比,节省时间和内存空间;拥有更人性化的代码诊断输出;基于库的框架,使编译和链接过程模块化;方便集成进IDE等等(具体参见calng源码目录clang/www/comparison.html, clang都指源码目录,以下都同此)。从开发角度,GCC或G++代码庞大,代码耦合度高,“是一个纯粹的编译系统”,非常不利于二次开发。LLVM+Clang则不同,可谓开发者的编译器。

1、何为前端(Frontend)

早期,我们将编译器描述为一个简单的盒子,能够将源程序转化为目标程序即可…单盒模型指出,编译器必须理解源程序并将功能映射到目标机。这两项任务截然不同的性质暗示着一种可能的任务划分,并最终导致了一种将编译分解为两个主要部分的设计:前端和后端。(摘自《编译器设计》,Keith D.Cooper  Linda Torczon著)。LLVM就是如上所说之后端,Clang就是前端。以下论述中llvm的版本为3.6.0。

2、三步演绎

编译过程一般分为词法分析、语法分析和语义分析,clang也不例外:

clang通过Preprocessor词法分析出一个一个 Token;

语法分析可得AST(Abstract Syntax Tree,抽象语法树),clang提供了访问者Visistor和各种回调函数供用户使用;

Clang基本支持C系列的标准,在语义分析之上——静态代码分析层面,通过注册机制,只需实现ExplodedGraph提供Checker 和 CheckerVisitor接口就可实现高级语义检查。

3、  词法分析(Lex)

Clang词法分析的核心数据结构是预编译器Preprocessor,循环调用Preprocessor.Lex(Token)可解析出一个一个Token。使用Proprocessor需要一大堆的初始化函数,首先从构造函数着手。

3.1  构造函数说明

Preprocessor(IntrusiveRefCntPtr<PreprocessorOptions> PPOpts,

                           DiagnosticsEngine &diags, LangOptions &opts,

                           SourceManager &SM, HeaderSearch &Headers,

                           ModuleLoader &TheModuleLoader,

                           IdentifierInfoLookup *IILookup, bool OwnsHeaders,

                           TranslationUnitKind TUKind)

(1)PreprocessorOptions

为Preprocess初始化做准备,主要设置预处理选项,如提供宏参数(-Dbug)、重映射文件说明(Remappedfile)等;

(2)DiagnosticsEngine

Diagnostics存放于源代码clang/lib/Basic中,足说明它是一个很基础的类,贯穿整个clang。DiagnosticsEngine是供前端报告错误、警告、提示等消息的具体类,它需要一个翻译单元和位置管理器(DiagnosticsEngine is tied to one translation unit and one SourceManager,因为一般编译打印的诊断信息主要就是错误的位置、错误类型)。

它有一个成员函数Report成员函数,可以触发各种Diagnositc信息,还有一个回调的类DiagnosticConsumer处理触发的各种Diagnositc。

inline DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID);

  inline DiagnosticBuilder Report(unsigned DiagID);

其构造函数如下,下面一一说明。

DiagnosticsEngine(const IntrusiveRefCntPtr<DiagnosticIDs> &Diags,

                      DiagnosticOptions *DiagOpts,

                      DiagnosticConsumer *client = nullptr,

                      bool ShouldOwnClient = true);

(2-1)DiagnosticIDs

所有代码的诊断(Diagnostic)信息的种类和输出方式等都在这里实现。Clang/include/clang/Basic下 DiagnosticCommonKinds.inc, DiagnosticDriverKinds.inc, DiagnosticFrontendKinds.inc, DiagnosticSerializationKinds.inc, DiagnosticLexKinds.inc, DiagnosticParseKinds.inc, DiagnosticASTKinds.inc, DiagnosticCommentKinds.inc, DiagnosticSemaKinds.inc, DiagnosticAnalysisKinds.inc列举了各种诊断信息。

(2-2)DiagnosticOptions

Diagnostic的选项设置。

(2-3)DiagnosticConsumer

该类是一个回调类,对已经得到的诊断(Diagnostic)信息,做再次处理,主要作用是以定制的方式呈现给外界。Clang中实现了DiagnosticConsumer的继承类,如IgnoringDiagConsumer, LogDiagnosticPrinter,TextDiagnosticPrinter,ForwardingDiagnosticConsumer等,如下简单打印是第几个warnings:

class clientConsumer:public DiagnosticConsumer{

void HandleDiagnostic(DiagnosticsEngine::Level Level,

                                            const Diagnostic &Info) {

DiagnosticConsumer::HandleDiagnostic(Level, Info);

        std::cout<<getNumWarnings()<<"th warnings occurs!"<<std::endl;//当warning出现时,打印输出。}};

Clang中的诊断Diagnostic等级有六类,如下枚举:

enum Level {

Ignored = DiagnosticIDs::Ignored,

Note = DiagnosticIDs::Note,

Remark = DiagnosticIDs::Remark,

Warning = DiagnosticIDs::Warning,

Error = DiagnosticIDs::Error,

Fatal = DiagnosticIDs::Fatal

};

(2-4)ShouldOwnClient

    当设置为True时,可以转移DiagnosticsEngine 的控制权(具体可以参考DiagnosticsEngine的takeClient()成员函数)。

(3)LangOptions

    主要是关于C/C++/Objective-c/objective-c++语言的一些选项。

(4)SourceManager

    SourceManager也位于clang/lib/Basic中,是资源管理的工具箱(Track and cache source files)。除cache、buffer管理之外,还管理SourceLocation。

(4-1)SourceLocation

    在编译器中需要用到的三个“重要”Location:行号,列号,声明和调用文件路径,都与SourceManager有关,其中行号和列号用SourceLocation表示。SourceLocation是一个偏移,整个type大小为四个字节(4==sizeof(SourceLocation))。SourceRange是两个SourceLocation组成的区间。

    SourceLocation:Encodes a location in the source. The SourceManager can decode this

     to get at the full include stack, line and column information.

(4-2)Location种类

Location有三种类型:spelling Location,expansion Location,presumed location。

当遇到宏展开的时候,expansion和presumed解析方式一样,行的结果可能不能一样(因为presumed遇到#line会重新计算行号),列结果一样都是call的列;而spelling是其原始定义(#define)的行和列。

当遇到#line指定行号之后的代码,spelling和expansion结果一样(非宏定义的地方),而presumed会重新计算行号。如下简单说明,第六行的数字2的位置spelling是第一行,而expansion则是其展开的位置第六行,presumed因为前面有#line 4则表示第四行。

1:#define min(x,y) (x)>(y)?(y):(x)

2:

3:Void main(int argc,char **argv){

4:

5:#line 4

6:int a=min(2,4);

//解析数字2的位置:|spelling |expansion|presumed|

//                   |  1行    |  6行    |   4行  |

9:}   

调用SourceManager的getSpellingLineNumber(SourceLocation)获得行号,getSpellingColumNumber(SourceLocation)获得列号。其他两种Location类似。

(4-3)Token所在的文件

    Preprocessor不仅cpp文件中的Token,而且还处理#include的Token。

    如果此Token属于#include文件,可以使用sourceManager的getBufferName成员函数。如下示例:

if(!SourceMgr_->isInMainFile(tok.getLocation()))

std::cout<<SourceMgr_->getBufferName(tok.getLocation())<<std::endl;

如果是在MainFile中(即translation unit中的CPP文件,可以通过SourceManager的getPresumedLoc(SourceLocation)获取PresumedLoc,该类中有相关filename。

FileID fd=SourceMgr_->getMainFileID();

    if (!fd.isInvalid()){

         std::cout<<"main file:";

         const FileEntry * FE=SourceMgr_->getFileEntryForID(fd);

            if (FE && FE->isValid())

          std::cout<<FE->getName()<<std::endl;

}

(5)HeaderSearch

提供头文件的搜寻位置,其AddSearchPath成员函数可以为头文件搜索提供新的路径,当AddSearchPath第二个参数设置为True,则会覆盖原有路径;如果为false,则为添加。

const DirectoryEntry *DE = FileMgr.getDirectory(SearchPath);

if(DE){

DirectoryLookup DL(DE, SrcMgr::C_User, false);

HeaderInfo->AddSearchPath(DL, false);

}

(6)ModuleLoader

主要预处理Objective-C语言代码中@import指令。在clang/doc/Modules.rst中,大篇幅谈及了import加载模块的好处,将向C++委员会建议加入此功能。

(7)IdentifierInfoLookup

词法解析(Lex)每个Token,都有与之对应TokenKinds,Identifier是TokenKinds的其中一种(include/clang/Basic/TokenKinds.def有说明),主要是指函数或者变量名等。IdentifierInfoLookup 是个抽象接口【virtual IdentifierInfo* get(StringRef Name) 必须实现】,在Preprocessor构造函数中,如果该项不为NULL,预处理器在查询IdentiferInfo表(hash表)时,将优先调用IdentifierInfoLookup的get成员函数,获取IdentifierInfo,这样就可以达到修改Token的IdentifierInfo属性的目的。

(8)OwnsHeaders

    如果前面的HeaderSearch是动态分配的,该项设置为true,则Preprocessor会回收该空间。无需用户调用delete。

(9)TranslationUnitKind

每一个Cpp及include文件组成一个翻译单元(Translation unit),在Preprocessor中默认为TU_Complete,表示是一个完整的翻译单元,也没有使用该参数。

enum TranslationUnitKind {

  /// \brief The translation unit is a complete translation unit.

  TU_Complete,

  /// \brief The translation unit is a prefix to a translation unit, and is

  /// not complete.

  TU_Prefix,

  /// \brief The translation unit is a module.

  TU_Module

};

至此,Preprocessor的构造函数说明,在使用之前只需要做些繁琐的初始化工作。

3.2  Preprocessor钩子

    在预处理translation unit完每一部分(如#include、#if)的时候,还可以往Preprocessor中添加“钩子”( 继承PPCallbacks,实现某些接口函数,然后addPPCallbacks),就可以将用户的“意图”加入到Preprocessor的处理过程中。这些可以接口函数参考clang/Lex/PPCallbacks.h文件。如下示例实现了InclusionDirective接口,打印#include文件的搜索路径。

/*该回调函数打印#include文件的搜索路径*/

class InclusionDirectiveCallbacks : public PPCallbacks {

public:

  void InclusionDirective(SourceLocation HashLoc,

    const Token &IncludeTok,

    StringRef FileName,

    bool IsAngled,

    CharSourceRange FilenameRange,

    const FileEntry *File,

    StringRef SearchPath,

    StringRef RelativePath,

    const Module *Imported) {

          std::cout<< FileName.str()<<std::endl;

     std::cout<<SearchPath.str()<<std::endl;  }};

在clang中源代码中有PPConditionalDirectiveRecord和PreprocessingRecord——两个Preprocess的Hooks,以PPConditionalDirectiveRecord为例,监听Preprocess处理#If,#Ifdef,#Ifndef,#Elif,#Else,#Endif,

PPConditionalDirectiveRecord *callbacks2=new PPConditionalDirectiveRecord(*SourceMgr_);

    PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(callbacks2));

.

//测试一个代码位置区间是否与#If,#Ifdef,#Ifndef,#Elif,#Else,#Endif等模块有交集

bool ret=callbacks2->rangeIntersectsConditionalDirective(SourceRange(innerdefiner,outdefiner));

3.3整个代码

(1)makefile

LLVM基础库众多,而且找对应库非常麻烦。借助llvm-config工具,虽然降低了编译速度,但使用简单。以下用clang++编译LexerTest.cpp。

CXX := clang++

LLVMCOMPONENTS := cppbackend

RTTIFLAG := -fno-rtti

LLVMCONFIG := llvm-config

 

CXXFLAGS := -I. -I/usr/local/include -I/usr/include -I$(shell $(LLVMCONFIG) --src-root)/tools/clang/include -I$(shell $(LLVMCONFIG) --obj-root)/tools/clang/include -g  $(shell $(LLVMCONFIG) --cxxflags) $(RTTIFLAG)

LLVMLDFLAGS := $(shell $(LLVMCONFIG) --ldflags --libs $(LLVMCOMPONENTS))

 

SOURCES =    LexerTest.cpp

 

OBJECTS = $(SOURCES:.cpp=.o)

EXES = $(OBJECTS:.o=)

CLANGLIBS = \

               -lclangTooling\

               -lclangFrontendTool\

               -lclangFrontend\

               -lclangDriver\

               -lclangSerialization\

               -lclangCodeGen\

               -lclangParse\

               -lclangSema\

               -lclangStaticAnalyzerFrontend\

               -lclangStaticAnalyzerCheckers\

               -lclangStaticAnalyzerCore\

               -lclangAnalysis\

               -lclangARCMigrate\

               -lclangRewriteFrontend\

               -lclangRewrite\

               -lclangEdit\

               -lclangAST\

               -lclangLex\

               -lclangBasic\

               $(shell $(LLVMCONFIG) --libs)\

               $(shell $(LLVMCONFIG) --system-libs)

 

#all: $(OBJECTS)

#$(EXES)

%.o:%.cpp

$(CXX) $(CXXFLAGS) -c -o [email protected] $< 

 

#%: %.o

LexerTest: LexerTest.o

$(CXX)  -o [email protected] *.o  $(CLANGLIBS) $(LLVMLDFLAGS)

#FrontendAction:FrontendAction.o

clean:

-rm -f $(EXES) $(OBJECTS)

(2)源代码

#include "clang/Lex/Lexer.h"

#include "clang/Basic/Diagnostic.h"

#include "clang/Basic/DiagnosticOptions.h"

#include "clang/Basic/FileManager.h"

#include "clang/Basic/LangOptions.h"

#include "clang/Basic/SourceManager.h"

#include "clang/Basic/TargetInfo.h"

#include "clang/Basic/TargetOptions.h"

#include "clang/Lex/HeaderSearch.h"

#include "clang/Lex/HeaderSearchOptions.h"

#include "clang/Lex/ModuleLoader.h"

#include "clang/Lex/Preprocessor.h"

#include "clang/Lex/PreprocessorOptions.h"

#include "clang/Frontend/TextDiagnosticPrinter.h"

#include "clang/Lex/PPConditionalDirectiveRecord.h"

#include "llvm/Support/Path.h"

#include<iostream>

using namespace llvm;

using namespace clang;

 

class clientConsumer:public DiagnosticConsumer{

void HandleDiagnostic(DiagnosticsEngine::Level Level,

                                            const Diagnostic &Info) {

DiagnosticConsumer::HandleDiagnostic(Level, Info);

        std::cout<<getNumWarnings()<<"th warnings occurs!"<<std::endl;

}

};

std::string getSourceText_(Token Begin, Token End,    SourceManager * SourceMgr_,

    LangOptions & LangOpts);

class InclusionDirectiveCallbacks : public PPCallbacks {

public:

  void InclusionDirective(SourceLocation HashLoc,

    const Token &IncludeTok,

    StringRef FileName,

    bool IsAngled,

    CharSourceRange FilenameRange,

    const FileEntry *File,

    StringRef SearchPath,

    StringRef RelativePath,

    const Module *Imported) {

    

      std::cout<< include file:<<FileName.str()<<::;

     std::cout<<SearchPath.str()<<std::endl;

  }

};

 

class IDLookup :public IdentifierInfoLookup{

IdentifierInfo* get(StringRef Name){

return NULL;

}

 

};

 

class moduleImportCallback:public PPCallbacks{

public:

void moduleImport(SourceLocation ImportLoc,

                            ModuleIdPath Path,

                            const Module *Imported) {

if(Imported)

std::cout<<"import:"<<Imported->Name<<std::endl;

  }

 

 

 

};

 

class VoidModuleLoader : public ModuleLoader {

  ModuleLoadResult loadModule(SourceLocation ImportLoc,

                              ModuleIdPath Path,

                              Module::NameVisibilityKind Visibility,

                              bool IsInclusionDirective) override {

     std::cout<<"load Module:"<<std::endl;

    return ModuleLoadResult();

  }

 

  void makeModuleVisible(Module *Mod,

                         Module::NameVisibilityKind Visibility,

                         SourceLocation ImportLoc,

                         bool Complain) override { std::cout<<Mod->Name<<std::endl;}

 

  GlobalModuleIndex *loadGlobalModuleIndex(SourceLocation TriggerLoc) override

    {

    std::cout<<"loadGlobalModuleIndex"<<std::endl;

   return nullptr; }

  bool lookupMissingImports(StringRef Name, SourceLocation TriggerLoc) override

    {

     std::cout<<"lookupMissingImports"<<std::endl;

       return 0;}

  };

 

 bool  CheckLex(StringRef Source) {

    DiagnosticOptions diagnosticOptions;

    TextDiagnosticPrinter *pTextDiagnosticPrinter =

        new TextDiagnosticPrinter(

            llvm::errs(),

            &diagnosticOptions,

            true);

    IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());

    clientConsumer cC;

    DiagnosticsEngine *Diags =new DiagnosticsEngine(DiagID,

            &diagnosticOptions,

            //pTextDiagnosticPrinter);

          //new IgnoringDiagConsumer());

            &cC,false);

    FileSystemOptions FileMgrOpts;

    FileManager FileMgr(FileMgrOpts);

 

    SourceManager * SourceMgr_=  new SourceManager(*Diags,FileMgr);

    LangOptions LangOpts;

    clang::TargetOptions targetOptions;

   targetOptions.Triple = llvm::sys::getDefaultTargetTriple();

 

   IntrusiveRefCntPtr<TargetInfo>  Target = TargetInfo::CreateTargetInfo(*Diags,

            std::make_shared<clang::TargetOptions>(targetOptions));

  

    std::unique_ptr<MemoryBuffer> Buf = MemoryBuffer::getMemBuffer(Source);

    const FileEntry *File = FileMgr.getFile("./input.cpp");

    if(File){

     SourceMgr_->setMainFileID(SourceMgr_->createFileID(File,SourceLocation(),SrcMgr::C_User));

    }

    else

    SourceMgr_->setMainFileID(SourceMgr_->createFileID(std::move(Buf)));

 

    VoidModuleLoader ModLoader;

    HeaderSearch *HeaderInfo=new HeaderSearch(new HeaderSearchOptions, *SourceMgr_, *Diags, LangOpts,

                            Target.get());

       StringRef SearchPath = llvm::sys::path::parent_path("/home/usr/Desktop/Lex");

     const DirectoryEntry *DE = FileMgr.getDirectory(SearchPath);

      const DirectoryEntry *DE1 = FileMgr.getDirectory("/home/usr/Desktop/Lex");

     if(DE1&&DE){

      DirectoryLookup DL(DE, SrcMgr::C_User, false);

      DirectoryLookup DL1(DE1, SrcMgr::C_User, false);

     HeaderInfo->AddSearchPath(DL, false);

     HeaderInfo->AddSearchPath(DL1, true);}

     IDLookup *idlookup=new IDLookup;

    Preprocessor PP(new PreprocessorOptions(), *Diags, LangOpts, *SourceMgr_,

                    *HeaderInfo, ModLoader, /*IILookup =*/NULL,

                    /*OwnsHeaderSearch =*/true,TU_Prefix);

    PP.Initialize(*Target);

    moduleImportCallback *Callbacks1= new moduleImportCallback;

    PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks1));

InclusionDirectiveCallbacks *Callbacks=new InclusionDirectiveCallbacks;

    PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks));

    PPConditionalDirectiveRecord *callbacks2=new PPConditionalDirectiveRecord(*SourceMgr_);

    PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(callbacks2));

    PP.EnterMainSourceFile();

    bool Invalid;

    bool islong=false;

    SourceLocation innerdefiner;

    SourceLocation outdefiner;

   

    while (1) {

      Token tok;

      PP.Lex(tok);

      if (tok.is(tok::eof))

        break;

      std::string str =getSourceText_(tok,tok,

                             SourceMgr_, LangOpts);

      if(str=="printf")

           Diags->Report(diag::warn_mt_message) << str;

      if(str=="innerDefiner")

           innerdefiner=tok.getLocation();

      if(str=="outDefiner")

           outdefiner=tok.getLocation();

      //std::cout<<str<<":"<<tok.getName()<<std::endl;

     //std::cout<<str<<std::endl;

   

  //std::cout<<Str.data()<<std::endl;

   /* std::cout<<str<<"::" \

             <<SourceMgr_->getExpansionLineNumber(tok.getLocation())<<":"  \

            <<SourceMgr_->getExpansionColumnNumber(tok.getLocation())<<std::endl;*/

std::cout<<str<<"----";

if(!SourceMgr_->isInMainFile(tok.getLocation()))

std::cout<<SourceMgr_->getBufferName(tok.getLocation())<<std::endl;

 

    }

    FileID fd=SourceMgr_->getMainFileID();

    if (!fd.isInvalid()){

         std::cout<<"main file:";

         const FileEntry * FE=SourceMgr_->getFileEntryForID(fd);

            if (FE && FE->isValid())

          std::cout<<FE->getName()<<std::endl;

    }

         bool ret=callbacks2->rangeIntersectsConditionalDirective(SourceRange(innerdefiner,outdefiner));

   if(ret)

   std::cout<<"Total Memory:"<<callbacks2->getTotalMemory()<<std::endl;

 

return true;

}

//}//end namespace clangT

 

std::string getSourceText_(Token Begin, Token End,    SourceManager * SourceMgr_,

    LangOptions & LangOpts) {

    bool Invalid;

    StringRef Str =

        Lexer::getSourceText(CharSourceRange::getTokenRange(SourceRange( \

                                    Begin.getLocation(), End.getLocation())),

                             *SourceMgr_, LangOpts, &Invalid);

    if (Invalid)

      return "<INVALID>";

   // std::cout<<Str.str()<<std::endl;

    return Str;

  }

 

 

 

int main(int argc,char **argv)

{

FileSystemOptions fileSystemOptions;

FileManager file(fileSystemOptions);

auto FileBuffer=file.getBufferForFile("./input.cpp");

std::string code= (*FileBuffer)->getBuffer();

 CheckLex(code);

}

时间: 2024-10-12 03:14:13

Clang之词法分析Lex的相关文章

Lua2.4 词法分析 lex.c

先看一下在打开文件里用到的一个函数 lua_setinput void lua_setinput (Input fn) {   current = ' ';   input = fn;   if (yytext == NULL)   {     textsize = MINBUFF;     yytext = newvector(textsize, char);   } } 设置当前是的输入字符为空格.设置函数指针,函数指针的定义为:typedef int  (*Input) (void);即:

Yacc 与 Lex 快速入门(词法分析和语法分析)

我们知道,高级语言,一般的如c,Java等是不能直接运行的,它们需要经过编译成机器认识的语言.即编译器的工作. 编译器工作流程:词法分析.语法分析.语义分析.IR(中间代码,intermediate Representation)产生.IR优化.代码产生.最终优化: 我们这里主要介绍的是语法分析: Lex 代表 Lexical Analyzar.Yacc 代表 Yet Another Compiler Compiler. 让我们从 Lex 开始吧. Lex Lex 是一种生成扫描器的工具.扫描器

词法分析程序 LEX和VC6整合使用的一个简单例子

词法分析的理论知识不少,包括了正规式.正规文法.它们之间的转换以及确定的有穷自动机和不确定的有穷自动机等等... 要自己写一个词法分析器也不会很难,只要给出了最简的有穷自动机,就能很方便实现了,用if.switch-case来写一通所谓的状态转换就可以,我近期会写一个简单的词法分析程序来作为例子... 现在已经有人发明了一个叫LEX的工具让你去应用,那我们就省了不少力气,毕竟没到万不得已的时候,我们都没必要重新发明轮子,从另一个角度来说,使用工具是我们人类知识继承的一种方法,也是我们比其他动物优

yacc语法分析与lex词法分析相结合

语法 对于某些应用,我们所完成的简单的词类识别也许足够用了;而另一些应用需要识别特殊的标记序列并执行适当的动作.传统上,对这样的一套动作描述成为语法. 使用右箭头"->"意味着可以用一个新的符号取代一套特殊的标记. 例如: subject ->noun\pronoun 指示一个新的符号subject是名词或代词. 词法分析程序和语法分析程序的通信 当一起使用lex扫描程序和yacc语法分析程序的时候,语法分析程序是比较高级别的例程.当他需要来自输入的标记时,就调用词法分析程

软件构造—— 实验二 lex词法分析

实验题目: 拷贝一个C文件,将其中的关键字int替换成float. 代码: 1 %{ 2 %} 3 %% 4 //表示如果是在双引号(")中(即为字符串),则照常打印,编译时请删除此注 5 \".*\" {printf("%s",yytext);} 6 [^ \t\n]+ {printf("%s",yytext);} 7 //表示如果遇到float,且附加模式是后面跟有空白符,则将float替换为double,编译时请删除此注释 8 i

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

作者:史宁宁(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

Clang之语法抽象语法树AST

语法分析器的任务是确定某个单词流是否能够与源语言的语法适配,即设定一个称之为上下文无关语言(context-free language)的语言集合,语法分析器建立一颗与(词法分析出的)输入单词流对应的正确语法树.语法分析树的建立过程主要有两种方法:自顶向下语法分析法和自底向上分析法.AST作为语法分析树(parse tree)的一种简写方式,它独立于具体编程语言(C++.Java.C等),而且与语法分析树的建立过程无关(自顶向下和自底向上逻辑等价),是联系编译器前端.后端的重要接口.Clang的

GCC,LLVM,Clang编译器对比

http://www.cnblogs.com/qoakzmxncb/archive/2013/04/18/3029105.html 在XCode中,我们经常会看到这些编译选项(如下图),有些人可能会有些茫然,本文将对GCC4.2.LLVM GCC 4.2.LLVM compliler 2.0三个编译选项进行一个详细的介绍. GCC GCC(GNU Compiler Collection,GNU编译器套装),是一套由 GNU 开发的编程语言编译器.它是一套以 GPL 及 LGPL 许可证所发行的自

windows平台下基于VisualStudio的Clang安装和配置

LLVM 是一个开源的编译器架构,它已经被成功应用到多个应用领域.Clang是 LLVM 的一个编译器前端,它目前支持 C, C++, Objective-C 以及 Objective-C++ 等编程语言.Clang 对源程序进行词法分析和语义分析,并将分析结果转换为 AST ( 抽象语法树 ) ,最后使用 LLVM 作为后端代码的生成器. Clang 的开发目标是提供一个可以替代 GCC 的前端编译器.与 GCC 相比,Clang 是一个重新设计的编译器前端,具有一系列优点,例如模块化,代码简