• <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>

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

            新建網(wǎng)頁(yè) 1
               
                   

            摘要

                   

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

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

               
                   
                       
                           

            1. 分析

                       
               

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

               

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

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

            2. Lex文件

                       
               

             

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

            3. Yacc文件

                       
               

             

               
            %{
            #include <iostream>
            #include "main.hpp"
            #define YYDEBUG 0 // 將這個(gè)變量設(shè)置為1則表示啟動(dòng)Yacc的調(diào)試功能
            extern "C"{
            void yyerror(const char *s);
            extern int yylex(void);
            }
            std::vector<Include> g_Includes;// 用來(lái)記錄所有的包含聲明
            Include             *g_pInclude;// 用來(lái)保存新增的包含聲明信息的指針
            %}
            %token INCLUDE
            %token HEADERFILE
            %%
            program:/* empty */
                            | program include_preprocess // 用這種遞歸的方式從有限的標(biāo)記表達(dá)出無(wú)限的內(nèi)容
                            ;
            include_preprocess:
                      INCLUDE '<'  HEADERFILE '>'
                    {
                            // 注意這里的$3,實(shí)際上就是上面的標(biāo)記的第三個(gè)的意思
                          // 因?yàn)閥ylval被聲明為Include結(jié)構(gòu),參見(jiàn)main.hpp文件
                          // 因而每個(gè)標(biāo)記都是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;// 是尖括號(hào)
                          g_pInclude->is_quotation = false;// 不是雙引號(hào)
                    }
                            | INCLUDE '\"' HEADERFILE '\"'
                    {
                            // 值得說(shuō)明的是:上面的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;// 不是尖括號(hào)
                          g_pInclude->is_quotation = true;// 是雙引號(hào)
                    }
                            ;
            %%
            void yyerror(const char *s)
            {
                    std::cerr<< s << std::endl;
            }
            int main()
            {
            #if YYDEBUG
                    yydebug = 1;
            #endif//YYDEBUG
                    yyparse();// 進(jìn)行語(yǔ)法分析,這個(gè)函數(shù)是Yacc自動(dòng)生成的
                    // 下面的這行代碼僅僅使用了STL的輸出算法到標(biāo)準(zhǔn)輸出
                    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>
            // 對(duì)于每一個(gè)項(xiàng)目最好都用一個(gè)獨(dú)立的數(shù)據(jù)結(jié)構(gòu)來(lái)保存相應(yīng)的信息
            struct Include
            {
                    void clear();// 設(shè)置Include的初始值
                    std::string headerfile;// 記錄頭文件全名(包括路徑)
                    bool is_quotation;// 是否是雙引號(hào)""括起來(lái)的頭文件
                    bool is_angle;// 是否是尖括號(hào)<>括起來(lái)的頭文件
                    // 下面的這個(gè)函數(shù)僅僅是用來(lái)輸出到C++流而準(zhǔn)備的
                    friend std::ostream&operator<<(std::ostream&s,const Include&I);
            };
            std::ostream&operator<<(std::ostream&s,const Include&I);
            // 下面的這個(gè)宏定義用來(lái)取消Lex和Yacc默認(rèn)的YYSTYPE定義,因?yàn)槟J(rèn)的YYSTYPE定義
            // 僅僅只能夠記錄整數(shù)信息,因此要保存額外的信息必須這樣定義宏,可以參見(jiàn)Yacc
            // 自動(dòng)生成的標(biāo)記頭文件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;
            }
            // 為了能夠方便輸出,在這里直接準(zhǔn)備好了一個(gè)流輸出函數(shù)
            std::ostream&operator<<(std::ostream&s,const Include&I)
            {
                    if(I.is_angle)
                            s << "采用尖括號(hào)的" ;
                    if(I.is_quotation)
                            s << "采用雙引號(hào)的" ;
                    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. 運(yùn)行結(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
                      采用尖括號(hào)的頭文件:[iostream]
            采用尖括號(hào)的頭文件:[string]
            采用尖括號(hào)的頭文件:[ffmpeg/avformat.h]
            采用尖括號(hào)的頭文件:[ffmpeg/avcodec.h]
            采用尖括號(hào)的頭文件:[ffmpeg/avutils.h]
            采用尖括號(hào)的頭文件:[stdio.h]
            采用尖括號(hào)的頭文件:[stdlib.h]
            采用雙引號(hào)的頭文件:[hello.h]
            采用雙引號(hào)的頭文件:[../hello.h]
            采用雙引號(hào)的頭文件:[space.h]

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

            9. 總結(jié)

                       
               

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

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

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

                       

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

                       

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

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

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

                       
            YYSTYPE, yylval, $$,$1,$2,...$n
                       
            因?yàn)榫帉懺~法分析程序和語(yǔ)法分析程序的目的就是為了操作分析出來(lái)的數(shù)據(jù) ,所以就需要有一種比較方便的形式來(lái)表達(dá)這些分析出來(lái)的數(shù)據(jù)。一種是詞 法分析程序使用的方式,叫做yylval;一種是語(yǔ)法分析程序使用的,叫做$n ,從上面的詞法分析程序和語(yǔ)法分析程序中已經(jīng)可以看到它們?cè)谙鄳?yīng)的文件 中的使用了。            

            至于YYSTYPE那就更簡(jiǎn)單了,因?yàn)橐磉_(dá)詞法分析程序和語(yǔ)法分析程序中的數(shù)據(jù) ,既然是數(shù)據(jù),在C/C++中就有數(shù)據(jù)類型的概念,這里的YYSTYPE就是yylval和 $n的數(shù)據(jù)類型。            

                       

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

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

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

                       

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

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

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

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


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


            <2025年6月>
            25262728293031
            1234567
            891011121314
            15161718192021
            22232425262728
            293012345

            導(dǎo)航

            常用鏈接

            留言簿

            隨筆分類(11)

            隨筆檔案(79)

            文章檔案(1)

            收藏夾(38)

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

            一般網(wǎng)站

            最新隨筆

            搜索

            積分與排名

            最新評(píng)論

            閱讀排行榜

            99久久精品免费观看国产| 成人综合伊人五月婷久久| 久久亚洲av无码精品浪潮| 亚洲欧美日韩久久精品| 国产99久久久国产精品小说| 久久国产精品无码HDAV| 久久久久国产| 东京热TOKYO综合久久精品 | …久久精品99久久香蕉国产| 久久九九亚洲精品| 一本久久知道综合久久| 伊人久久精品线影院| 亚洲色欲久久久综合网东京热| 精品久久人人爽天天玩人人妻| 久久精品国产2020| 久久露脸国产精品| 久久国产成人精品麻豆| 久久夜色精品国产噜噜噜亚洲AV| 亚洲成av人片不卡无码久久| 久久这里只精品国产99热| 久久狠狠高潮亚洲精品| 无码超乳爆乳中文字幕久久| 欧美久久亚洲精品| 久久久久九九精品影院| 99久久精品国产一区二区三区| 久久久久亚洲av无码专区| 久久国产欧美日韩精品| 一本久久a久久精品综合香蕉| 亚洲精品高清久久| 国产精品免费久久久久电影网| 久久久91精品国产一区二区三区| 国产精品久久午夜夜伦鲁鲁| 人妻无码久久一区二区三区免费| 伊人久久大香线蕉av一区| 一本色道久久99一综合| 超级97碰碰碰碰久久久久最新| 伊人久久一区二区三区无码| 久久精品国产99国产精品导航| 色婷婷综合久久久久中文一区二区| 热re99久久6国产精品免费| 久久国产免费观看精品3|