青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

Lex和Yacc從入門到精通(6)-解析C/C++包含文件

新建網(wǎng)頁 1
   
       

摘要

       

在這一章里面將要涉及到處理C/C++的包含宏的解析。也就是說要從一大串C/C++ 包含文件的聲明中提取出文件名,以及相互依賴關(guān)系等等。實際上在這一章里面 使用的Lex和Yacc技術(shù)也是非常重要的,這些都會在本章中進行詳細講解。        

我們知道對于C/C++包含文件聲明是為程序提供了一些庫存的功能,因此存在一種依賴 關(guān)系,如果把這種依賴關(guān)系表達成為Makefile的形式,那么就可以自動生成Makefile 。在這一章里面并不會實現(xiàn)自動生成Makefile的功能,而是僅僅解析出所有的包含文 件名,并記錄下來。

   
       
           
               

1. 分析

           
   

我們知道C/C++中存在兩種形式的包含文件,一種是用“<>”包含的頭文件, 一種是“""”包含的頭文件,這兩種不同的形式表達了頭文件的不同的搜索方式。 另外還需要注意的是:這兩種方式包含的都是磁盤上存在的文件名。也就是說,只 要是磁盤上存在的文件名都可以包含的,都是合法的,因而C/C++里面存在的有擴展 名的頭文件和沒有擴展名的頭文件都是合法的。并且還需要注意的是C/C++包含的頭 文件是可以續(xù)行的。     

   

因而總結(jié)起來需要做到如下的幾件事情:

   
       
               
  1. 處理“<>”和“""”兩種包含方式
  2.            
  3. 處理文件名
  4.            
  5. 處理續(xù)行
  6.        
   
       
           
               

2. Lex文件

           
   

 

   
%{
#include "main.hpp"// 在其中保存了記錄頭文件所需要的所有數(shù)據(jù)結(jié)構(gòu)
#include "frame.tab.h"// 由Yacc自動生成的所有標記聲明,實際上都是C宏
extern "C"{
int yywrap(void);
int yylex(void);
}
%}
%x _INCLUDE_
%x _INCLUDE_FILE_
%%
"#"[ \t]*"include"      {
                                BEGIN _INCLUDE_;// 進入_INCLUDE_狀態(tài)
                                yylval.clear();// 需要將所有的Include值初始化
                                return INCLUDE;// 返回INCLUDE標記
                        }
<_INCLUDE_>[\"|<]       {
                                BEGIN _INCLUDE_FILE_;// 進入_INCLUDE_FILE_狀態(tài)
                                return *yytext; // 返回引號或者尖括號
                        }
<_INCLUDE_FILE_>[^\">]* {
                                yylval.headerfile+=yytext;// 記錄頭文件字符串
                                return HEADERFILE;// 返回頭文件標記
                        }
<_INCLUDE_FILE_>[\"|>]  {
                                BEGIN INITIAL;// 恢復(fù)到初始狀態(tài),默認狀態(tài)
                                return *yytext;// 返回引號或者尖括號
                        }
[ \t\n]                 ;// 對于額外的空白都不處理直接扔掉
%%
int yywrap(void)
{
        return 1;// 只處理一個輸入文件
}
   
       
           
               

3. Yacc文件

           
   

 

   
%{
#include <iostream>
#include "main.hpp"
#define YYDEBUG 0 // 將這個變量設(shè)置為1則表示啟動Yacc的調(diào)試功能
extern "C"{
void yyerror(const char *s);
extern int yylex(void);
}
std::vector<Include> g_Includes;// 用來記錄所有的包含聲明
Include             *g_pInclude;// 用來保存新增的包含聲明信息的指針
%}
%token INCLUDE
%token HEADERFILE
%%
program:/* empty */
                | program include_preprocess // 用這種遞歸的方式從有限的標記表達出無限的內(nèi)容
                ;
include_preprocess:
          INCLUDE '<'  HEADERFILE '>'
        {
                // 注意這里的$3,實際上就是上面的標記的第三個的意思
              // 因為yylval被聲明為Include結(jié)構(gòu),參見main.hpp文件
              // 因而每個標記都是Include結(jié)構(gòu)類型。
              g_Includes.push_back(Include());
              g_pInclude = &g_Includes.back();
              g_pInclude->clear();// 初始化
              g_pInclude->headerfile   = $3.headerfile;// 可以證明$3的類型就是Include類型
              g_pInclude->is_angle     = true;// 是尖括號
              g_pInclude->is_quotation = false;// 不是雙引號
        }
                | INCLUDE '\"' HEADERFILE '\"'
        {
                // 值得說明的是:上面的include_preprocess用$表示,
              // 而不是用$0表示。從左向右依次為:
              // include_preprocess        $
              // INCLUDE                   $1
              // '\"'                      $2
              // HEADERFILE                $3
              // '\"'                      $4
              g_Includes.push_back(Include());
              g_pInclude = &g_Includes.back();
              g_pInclude->clear();// 初始化
              g_pInclude->headerfile   = $3.headerfile;
              g_pInclude->is_angle     = false;// 不是尖括號
              g_pInclude->is_quotation = true;// 是雙引號
        }
                ;
%%
void yyerror(const char *s)
{
        std::cerr<< s << std::endl;
}
int main()
{
#if YYDEBUG
        yydebug = 1;
#endif//YYDEBUG
        yyparse();// 進行語法分析,這個函數(shù)是Yacc自動生成的
        // 下面的這行代碼僅僅使用了STL的輸出算法到標準輸出
        std::copy(g_Includes.begin(),g_Includes.end(),std::ostream_iterator<Include>(std::cout,"\n"));
        return 0;
}
   
       
           
               

4. main.hpp文件

           
   

 

   
#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
// 對于每一個項目最好都用一個獨立的數(shù)據(jù)結(jié)構(gòu)來保存相應(yīng)的信息
struct Include
{
        void clear();// 設(shè)置Include的初始值
        std::string headerfile;// 記錄頭文件全名(包括路徑)
        bool is_quotation;// 是否是雙引號""括起來的頭文件
        bool is_angle;// 是否是尖括號<>括起來的頭文件
        // 下面的這個函數(shù)僅僅是用來輸出到C++流而準備的
        friend std::ostream&operator<<(std::ostream&s,const Include&I);
};
std::ostream&operator<<(std::ostream&s,const Include&I);
// 下面的這個宏定義用來取消Lex和Yacc默認的YYSTYPE定義,因為默認的YYSTYPE定義
// 僅僅只能夠記錄整數(shù)信息,因此要保存額外的信息必須這樣定義宏,可以參見Yacc
// 自動生成的標記頭文件frame.tab.h。
#define YYSTYPE Include
   
       
           
               

5. main.cpp文件

           
   

 

   
#include "main.hpp"
// 初始化所有的Include信息,避免前后關(guān)聯(lián)
void Include::clear()
{
        headerfile.clear();
        is_quotation = false;
        is_angle = false;
}
// 為了能夠方便輸出,在這里直接準備好了一個流輸出函數(shù)
std::ostream&operator<<(std::ostream&s,const Include&I)
{
        if(I.is_angle)
                s << "采用尖括號的" ;
        if(I.is_quotation)
                s << "采用雙引號的" ;
        s << "頭文件:[" << I.headerfile << "]" ;
        return s;
}
   
       
           
               

6. Makefile文件

           
   

 

   
LEX=flex
YACC=bison
CC=g++
a.exe:lex.yy.o frame.tab.o main.o
                $(CC) lex.yy.o frame.tab.o main.o -o a.exe
lex.yy.o:lex.yy.c frame.tab.h main.hpp
                $(CC) -c lex.yy.c
frame.tab.o:frame.tab.c main.hpp
                $(CC) -c frame.tab.c
main.o:main.hpp main.cpp
                $(CC) -c main.cpp
frame.tab.c frame.tab.h:frame.y
                $(YACC) -d frame.y
lex.yy.c:frame.l
                $(LEX) frame.l
clean:
                rm -f *.o *.c *.h
   
       
           
               

7. sample.cpp文件

           
   

 

   
#include <iostream>
#include <string>
#include <ffmpeg/avformat.h>
#include <ffmpeg/avcodec.h>
#include <ffmpeg/avutils.h>
#include <stdio.h>
#include <stdlib.h>
#include "hello.h"
#include "../hello.h"
#        include "space.h"
   
       
           
               

8. 運行結(jié)果

           
   

 

   
pandaxcl@PANDAXCL-F78E7D /d/work/lex_yacc/chapter06
$ make
flex frame.l
bison -d frame.y
g++ -c lex.yy.c
g++ -c frame.tab.c
g++ -c main.cpp
g++ lex.yy.o frame.tab.o main.o -o a.exe

pandaxcl@PANDAXCL-F78E7D /d/work/lex_yacc/chapter06
$ ./a.exe < sample.cpp
          采用尖括號的頭文件:[iostream]
采用尖括號的頭文件:[string]
采用尖括號的頭文件:[ffmpeg/avformat.h]
采用尖括號的頭文件:[ffmpeg/avcodec.h]
采用尖括號的頭文件:[ffmpeg/avutils.h]
采用尖括號的頭文件:[stdio.h]
采用尖括號的頭文件:[stdlib.h]
采用雙引號的頭文件:[hello.h]
采用雙引號的頭文件:[../hello.h]
采用雙引號的頭文件:[space.h]

pandaxcl@PANDAXCL-F78E7D /d/work/lex_yacc/chapter06
$
   
       
           
               

9. 總結(jié)

           
   

總的來說,上面的解析C/C++包含預(yù)處理信息的時候需要了解如下的概念:

   
       
           
詞法狀態(tài)
           
所謂的詞法狀態(tài)就是指對文本進行詞法分析的時候,詞法分析器當前所處 的狀態(tài),默認情況下,詞法分析器都處于INITIAL狀態(tài),這個INITIAL狀態(tài) 是Lex內(nèi)置的狀態(tài)。用戶可以通過%x來重新定義各種各樣的狀態(tài)。            

至于為什么要使用狀態(tài),我們來看一個實際的例子:上面分析頭文件的時候 采用了兩個自定義的狀態(tài):_INCLUDE_狀態(tài)和_INCLUDE_FILE_狀態(tài), _INCLUDE_狀態(tài)是當遇到了#include開始的,因為這個狀態(tài)之后是尖括號或者 是雙引號括起來的頭文件名,在后面分析模板(使用尖括號)和分析字符串 (使用雙引號)的時候也會遇到尖括號和雙引號,因而需要區(qū)分這兩種情況 ,所以才需要使用_INCLUDE_狀態(tài),以此來區(qū)分是包含文件還是模板或者是字 符串了。這一點非常重要!            

           

同樣,狀態(tài)_INCLUDE_FILE_存在也是為了區(qū)分雙引號包含的頭文件名稱的, 因為雙引號不同于尖括號,雙引號在頭文件名的開始和結(jié)束都是相同的,因 此為了區(qū)分頭部和尾部的雙引號,必須再增加一個狀態(tài)。實際上這可以用來 簡化詞法分析器的編寫,當您遇到這種類似的問題的時候可以考慮再增加一 種新的狀態(tài),通常來說就可以解決問題啦:)            

           

不過還有一點特別需要強調(diào)的是當您感覺所添加的狀態(tài)太多了,出現(xiàn)了混亂 現(xiàn)象,就說明用Lex狀態(tài)已經(jīng)不大適合處理這種問題了,就應(yīng)該考慮采用Yacc 的一條獨立的語法規(guī)則來進行處理了:)這也是Yacc語法文件存在的原因,要 不然全部都可以采用詞法分析文件來解決啦,還要語法分析文件干什么!           

           
遞歸表達
           
這里需要特別注意的是:frame.y文件中program的構(gòu)成采用了左遞歸的形 式。從代碼中可以看出:program可以是空(什么也沒有)也可以是由現(xiàn)有 的program內(nèi)容再追加一條include_preprocess類構(gòu)成。當program內(nèi)容為 空的時候增加一條include_preprocess類就表示program只有一條 include_preprocess內(nèi)容,當program已經(jīng)有了一條include_preprocess內(nèi) 容之后再增加一條include_preprocess內(nèi)容就可以表示兩條 include_preprocess內(nèi)容了,依次類推,可以表達無數(shù)的包含信息,從而 表達了無限的內(nèi)容了。特別需要注意的是,這里的program表示的僅僅是現(xiàn) 有的內(nèi)容,包括但不限于include_preprocess內(nèi)容,還可以有其他的內(nèi)容 ,這一點可以在增加其他內(nèi)容的時候體現(xiàn)出來,因為C/C++源代碼不僅僅是 由包含信息構(gòu)成的嘛:)            

特別需要注意的是,這里要特表強調(diào)一下使用左遞歸,不是說右遞歸不行,而 是出于程序運行效率考慮最好使用左遞歸。具體原因在后續(xù)的文檔中會有詳細 的說明的:)            

           
YYSTYPE, yylval, $$,$1,$2,...$n
           
因為編寫詞法分析程序和語法分析程序的目的就是為了操作分析出來的數(shù)據(jù) ,所以就需要有一種比較方便的形式來表達這些分析出來的數(shù)據(jù)。一種是詞 法分析程序使用的方式,叫做yylval;一種是語法分析程序使用的,叫做$n ,從上面的詞法分析程序和語法分析程序中已經(jīng)可以看到它們在相應(yīng)的文件 中的使用了。            

至于YYSTYPE那就更簡單了,因為要表達詞法分析程序和語法分析程序中的數(shù)據(jù) ,既然是數(shù)據(jù),在C/C++中就有數(shù)據(jù)類型的概念,這里的YYSTYPE就是yylval和 $n的數(shù)據(jù)類型。            

           

特別需要注意的是,語法分析程序中每一個語法規(guī)則冒號左邊的類的值用$$表 示,而冒號右邊的第一項用$1表示,第二項用$2表示,依次類推。             

           
標記和值
           
標記指的是由%token定義的INCLUDE和HEADERFILE,他們都對應(yīng)著一個具體 值,而且具體值類型還有可能完全不一樣。這里需要特別強調(diào)的一點是: 每一個標記都對應(yīng)著一個值,你可以不使用,但是他就是確確實實存在著 ,而且從始至終都保持著這種對應(yīng)。例如上面的INCLUDE標記的值就沒有使 用,但是HEADERFILE標記的值就被使用了。在Lex和Yacc中標記都是用一個 C宏定義的一個整數(shù),而標記的值都是由YYSTYPE定義著的一個變量,這個 變量的名字就是yylval,其中保存著相關(guān)的信息,這個信息就是在詞法分 析文件中進行設(shè)置的,而在語法分析文件中就直接采用了。            

實際上%token還可以更進一步的簡化Yacc語法程序的編寫,從而避免一些不 必要的錯誤。從上面的語法分析來看,對于不同的$n,還需要記住$n的精確 類型和變量名,這一點其實是不必要的,可以通過%token <headerfile> HEADERFILE來聲明標記,那么在Yacc程序的語法規(guī)則 中就可以直接使用$3來表示yylval.headerfile了,從而也就不需要記住那 些具體變量名啦:)           

           

值得注意的是,盡管標記是可以用%token來定義,但是并不僅僅限于這種方 式,Yacc中還可以用%type來定義,采用%type來定義的目的就是為那些不是 標記的類也準備一個對應(yīng)的值的,例如:完全可以為include_preprocess定 義一個值,用來保存一些額外的信息,不過本文中并不需要,后續(xù)的文檔中 就會需要這個功能了,在此先簡單說明一下:)           

           
詞法動作
           
對于詞法分析程序中的每一個正則表達式對應(yīng)的規(guī)則,都有相應(yīng)的C/C++ 語句來做一些額外的處理,這個額外的處理就是詞法動作。            
           
語法動作
           
對于語法分析程序中的每一個語法規(guī)則,都有相應(yīng)的C/C++語句來做一些額 外的處理,這個額外的處理就是語法動作。不過語法動作和詞法動作的不同 之處在于,語法動作允許嵌入式的語法動作,而詞法動作不行。至于什么是 嵌入式的語法動作,在后續(xù)的文檔中會有詳細的說明的!            
       
   

好了,本章中還殘留有一些問題故意沒有解決,例如:包含文件的續(xù)行問題!留個 讀者自己思考,可以在本文所討論的基礎(chǔ)上稍微改動一下就可以了。后續(xù)的文檔正 在努力寫出,敬請關(guān)注;)    

posted on 2008-02-04 09:06 FongLuo 閱讀(782) 評論(0)  編輯 收藏 引用


只有注冊用戶登錄后才能發(fā)表評論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


<2008年7月>
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

導(dǎo)航

常用鏈接

留言簿

隨筆分類(11)

隨筆檔案(79)

文章檔案(1)

收藏夾(38)

學(xué)習網(wǎng)站

一般網(wǎng)站

最新隨筆

搜索

積分與排名

最新評論

閱讀排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            伊人伊人伊人久久| 国产精品免费区二区三区观看| 欧美一区二区黄色| 亚洲一区二区不卡免费| 亚洲午夜国产成人av电影男同| 一本色道久久综合亚洲精品不| 夜夜爽www精品| 亚洲免费伊人电影在线观看av| 欧美亚洲一区二区在线观看| 久久精品国产欧美亚洲人人爽| 久久精品视频在线播放| 欧美va天堂| 亚洲精选国产| 午夜精品短视频| 老司机免费视频久久| 欧美日韩国产a| 国产亚洲欧美激情| 最新国产成人av网站网址麻豆| 99国产精品久久久久老师 | 亚洲欧美日韩直播| 久久青草久久| 国产精品久久久久久av福利软件| 国语精品一区| 国产精品99久久久久久久久久久久 | 久久九九热re6这里有精品| 嫩草影视亚洲| 国产精品一级久久久| 精久久久久久| 亚洲欧美激情一区| 欧美黄色视屏| 欧美在线视频观看| 欧美日韩一区二区在线观看视频| 国产一区99| 一片黄亚洲嫩模| 免费成人在线观看视频| 亚洲欧美日韩一区二区在线| 欧美黄色网络| 91久久久久久| 老司机午夜精品视频在线观看| 亚洲最快最全在线视频| 久久嫩草精品久久久久| 国产欧美va欧美不卡在线| 一区二区欧美视频| 亚洲国产成人精品视频| 久久精品网址| 国产在线观看91精品一区| 国产在线精品成人一区二区三区| 亚洲美女91| 欧美成人伊人久久综合网| 午夜一区二区三区在线观看| 欧美日韩一级片在线观看| 亚洲国产高清自拍| 裸体一区二区三区| 久久精品72免费观看| 国产精品爽黄69| 亚洲欧美日本在线| 亚洲视频免费| 国产精品美女| 性感少妇一区| 亚洲欧美自拍偷拍| 国产精品视频观看| 欧美在线免费视屏| 午夜久久久久久| 国产香蕉97碰碰久久人人| 香蕉免费一区二区三区在线观看| av成人免费| 国产精品美女久久久久久2018 | 日韩亚洲精品在线| 欧美日韩在线一区二区| 中文在线不卡| 亚洲欧美日韩精品| 黄色一区二区在线观看| 蜜臀av性久久久久蜜臀aⅴ| 久久精品色图| 亚洲激情在线激情| 亚洲免费观看高清完整版在线观看| 欧美日韩国产系列| 亚洲男人的天堂在线| 亚洲午夜在线视频| 国产综合激情| 最新成人av网站| 国产精品久久久亚洲一区| 欧美专区在线观看一区| 久久久久一区二区| 亚洲靠逼com| 中文精品在线| 狠狠久久婷婷| 亚洲精品123区| 国产农村妇女精品一区二区| 久久久久久久综合| 欧美精品国产一区| 欧美在线free| 欧美理论电影网| 欧美主播一区二区三区美女 久久精品人| 久久国产精品色婷婷| 亚洲美女啪啪| 欧美一区二视频| 亚洲久久一区二区| 欧美中文在线观看国产| 9久草视频在线视频精品| 欧美一级在线播放| 野花国产精品入口| 久久久91精品国产一区二区三区| 日韩网站在线看片你懂的| 欧美成人高清视频| 欧美激情综合色| 久久精品视频99| 欧美日韩精品二区第二页| 欧美在线关看| 欧美日韩在线视频一区| 久久久青草青青国产亚洲免观| 欧美成人精品福利| 久久久久久久波多野高潮日日| 欧美另类videos死尸| 久久亚洲国产成人| 国产伦精品一区二区三区四区免费| 亚洲国产精品免费| 在线观看欧美| 欧美一区日韩一区| 午夜亚洲伦理| 欧美亚韩一区| 99热精品在线| 日韩一级片网址| 欧美激情视频一区二区三区在线播放 | 久久久午夜视频| 国产精品一二一区| 亚洲视频每日更新| 亚洲视频福利| 欧美日韩免费一区| 亚洲日韩欧美一区二区在线| 亚洲黄色免费| 欧美成人在线免费视频| 欧美成人免费在线| 国内精品嫩模av私拍在线观看| 亚洲性夜色噜噜噜7777| 亚洲一区二区免费视频| 欧美三级黄美女| 亚洲视频免费| 久久国产精品久久w女人spa| 国产精品外国| 香蕉久久夜色精品| 久久久久国产一区二区| 国产在线播放一区二区三区| 午夜久久tv| 美女啪啪无遮挡免费久久网站| 在线播放中文一区| 农夫在线精品视频免费观看| 欧美成人一品| 夜夜精品视频一区二区| 国产精品mm| 欧美一二区视频| 免费亚洲一区二区| 日韩小视频在线观看专区| 欧美视频中文字幕在线| 亚洲欧美国内爽妇网| 久久躁日日躁aaaaxxxx| 亚洲黄色影片| 国产精品久久久久久久久久免费 | 国产精品xxxxx| 午夜精品999| 欧美国产日本| 亚洲一区二区三区影院| 国产欧美va欧美不卡在线| 久久久久久久久久久久久9999| 欧美高清你懂得| 性娇小13――14欧美| 欧美成人四级电影| 欧美gay视频激情| 亚洲老司机av| 欧美一区日韩一区| 激情av一区| 欧美亚一区二区| 久久久久久久久综合| 亚洲激情小视频| 欧美在线不卡| 一本久道久久综合中文字幕| 国产欧美日韩| 欧美日韩成人| 久久国产一区| 中文在线资源观看网站视频免费不卡| 久久国产精品一区二区三区| 亚洲欧洲日产国产网站| 国产精品日日摸夜夜添夜夜av| 久久久久国色av免费看影院| 亚洲精品久久久久中文字幕欢迎你| 性娇小13――14欧美| 亚洲精品视频二区| 狠狠v欧美v日韩v亚洲ⅴ| 欧美午夜视频| 欧美精品综合| 久久久久久一区二区| 亚洲在线播放| aa成人免费视频| 亚洲国产精品成人一区二区 | 亚洲国产精品专区久久| 性伦欧美刺激片在线观看| 亚洲精品乱码久久久久| 韩国成人精品a∨在线观看| 国产精品色婷婷| 国产精品久久久久9999高清|