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

            歲月流轉,往昔空明

            C++博客 首頁 新隨筆 聯系 聚合 管理
              118 Posts :: 3 Stories :: 413 Comments :: 0 Trackbacks

            Exodus:語法分析(一)

            在一個魔法世界里,你是一個會魔法的法師。我的意思是,作為一個法師,你什么都會了,也什么都有了,施法材料,法袍,魔杖,法術書。甚至你連成功后的慶祝動作都想好了。你以為你會“魔法”了。只可惜,這里還缺少了一樣東西,那就是,魔法的口訣。

            而在這里,我們什么都有了。用來分析的Token,語法樹到OP CODE的翻譯,虛擬機,什么都有了。但是我們還是缺一樣口訣,那就是,如何從Token到語法樹的口訣。

            在我們進行詞法分析的時候,遵從的是Spirit這本頗有難度的《圣經》。不過,我們只瀏覽了如《使徒行傳》般流暢而松散的Spirit.Lex。在這里,我們依然沿用Spirit,這是我們編譯器前端的原旨。不過現在,我們要講解的是環環相扣、蕩氣回腸的《Exodus》——Spirit.Qi。

            嘛,這段神叨叨的引子,只是為了強調語法分析的地位而已。在繼續閱讀本章之前,需要你看的明白BNF。有關于BNF方面的信息,你可以在任何一本講述編譯原理的書籍上找到。

            仍然是以一個簡單的A+B為例。由于我們已經有了詞法“literal_int”和“literal_add”,因此A+B這樣一個表達式,用BNF來表示,就是:

            Expr ::= literal_int literal_add literal_int

            在Spirit.Qi里,語法的表達也類似于BNF的形式。只要你設計出語言的BNF,就很容易的翻譯成Spirit.Qi支持的語法定義。我們這里,就可以寫成:

            template <typename IteratorT>

            struct binary_expression: qi::grammar<IteratorT>{

                template <typename TokenDefT> binary_expression(const TokenDefT& tok): binary_expression::base_type(start)

                {

                    start = (  literal_int >> literal_op >> literal_int );

                    literal_int = tok.littok_int;

                    literal_op = tok.optok_add;

                }

                qi::rule<IteratorT> literal_op, literal_int, start;

            };

            在Spirit.Qi中,一個Rule就等于EBNF的一個非終結符。一個Grammar相當于一組Rule的集合,并且可以擁有一個或者多個的起始Rule作為入口。本質上我們可以把Grammar看成一個Rule(準確的說,是Parser,若要了解相關概念,請參閱Spirit的自定義Parser部分)。等號用于連接非終結符(Rule)及其推導式;使用“>>”(輸入流/右位移運算符)連接語法要素之間的連接符號。更多的符號請參閱Spirit.Qi文檔。

            至于為什么不將Rule合并到一起,而提供一個Grammar的中間層,主要有兩方面的考慮,一個是提供了一個抽象層,例如我們可以把Statement和Expression分開來寫,使得層次上更加清晰;還有一個方面在于節省編譯時間。因為Spirit使用了大量的源編程技術,如果把所有的Rule合并到一起編譯,會占用大量的編譯時間。在使用了Grammar之后,可以運用C++編譯器在一個編譯過程里對相同的模板特化只進行一次的Tricks,大大節省了編譯時間。

            在上一章里,咱們最后還留了一個問題,就是空白符號的處理方法。這里,我們將于空白符號一起,來走一下Spirit的語法和詞法分析的流程。

            首先,我們建立好詞法,將源代碼字符流組織成更加容易被語法分析識別的Token流。

            template <typename BaseLexerT>

            struct sasl_tokens : public boost::spirit::lex::lexer< BaseLexerT > {

                sasl_tokens(){

                    this->self.add_pattern("SPACE", "[ \\t\\v\\f]+");

             

                    littok_int = "[0-9]+";

                    optok_add = "[\\+]");

                    whitetok_space = "{SPACE}";

                   

                    this->self = littok_int | optok_add;

                    this->self("SKIPPED") = whitetok_space;

                }

                boost::spirit::lex::token_def<>

                    littok_int, optok_add, whitetok_space;

            };

            這里,我們將詞法分為兩組,對語法分析有效的Tokens組和無效的空白組,空白組用”Skipped”作為狀態以示區別。這里我們需要說明一下,Spirit.LEX的詞法分析的“狀態”與詞法分析工具“Lex/Flex”中的狀態概念是相同的。

            在Lex類的詞法分析工具里,有一個專門的狀態。一般而言,這些狀態都用字符串表示。Lex的默認是“INITIAL”,Spirit.Lex的默認狀態是空(如果我沒記錯的話)。在指定詞法的時候,可以告訴詞法分析器,此文法在什么狀態下,這條詞法才發揮作用。詞法分析器的狀態可以由外部程序自由指定。

            我們將表示空白的詞法都放在Skipped狀態下后,我們就可以對每個單詞,用Skipped狀態去匹配。如果發現是在Skipped狀態下匹配成功的單詞,在進入語法分析前就可以先丟棄,進而實現過濾空白符的目的。

            考慮表達式“55_+38”(‘_’代表空格),在分析成Token流之后,會變成以下的形式:

            State

            INITIAL

            SKIPPED

            INITIAL

            INITIAL

            Token

            Literal_int

            Literal_ws

            Literal_op

            Literal_int

            Literal

            55

            _

            +

            38

            然后撰寫我們的Grammar。由于我們需要指定Skipper來跳過我們不需要的Token,因此我們的Grammar在模板參數里,也要加入這個Skipper的類型參數。

            template <typename IteratorT, typename LexerT>

            struct binary_expression:

            qi::grammar<IteratorT, qi::in_state_skipper<LexerT> >

            {

                template <typename TokenDefT>

            binary_expression(const TokenDefT& tok):

                binary_expression::base_type(start)

                {

                    start = (  literal_int >> literal_op >> literal_int );

                    literal_int = tok.littok_int;

                    literal_op = tok.optok_add;

                }

            boost::spirit::qi::in_state_skipper<LexerT> skipper_type;

                qi::rule<IteratorT, skipper_type> literal_op, literal_int, start;

            };

            并在咱們的驅動代碼里面,這樣寫:

            typedef sasl_tokenizer::iterator_type sasl_token_iterator;

            typedef sasl_tokenizer::lexer_def sasl_skipper;

             

            sasl_tokenizer sasl_tok;

            binary_expression<sasl_token_iterator, sasl_skipper> g( sasl_tok );

             

            lex::tokenize_and_phrase_parse(

            first,

            last,

            sasl_tok,

            g, qi::in_state("SKIPPED")[sasl_tok.self]

            );

            喏,看到了指定skipper的代碼了不?這就提示parser,遇到了skipped狀態解析出來的token,就自己吃了吧,不要拿去匹配了。這樣就達到了過濾掉空白符的目的。

            不過呢,盡管我們parse通過了,但是仍然沒有提取出我們想要的信息來。到目前為止,我們還沒能讓parser構造出咱們之前手工構建并傳遞給Code Generator的語法樹來。這仍然是橫亙在出埃及的我們面前的紅海。

            下一次,我們將仍然相信Spirit這本Bible,相信它給我們的一章叫 “Semantic Action”的啟示錄。它將告訴我們,如何把Parser分析出的結果轉化為我們要的語法樹,以引領我們走向流OP CODE之地。

            God bless programmers and p2p sites except gfw’s developers and Cisco. Amen

            posted on 2009-12-15 23:35 空明流轉 閱讀(2371) 評論(1)  編輯 收藏 引用

            評論

            # re: 實用編譯器構建指南(四) 2010-01-07 11:09 正心
            It looks like you've read so many God's books! :)~~~  回復  更多評論
              

            久久国产免费观看精品| 久久香蕉国产线看观看精品yw | 亚洲人成精品久久久久| 狠狠色狠狠色综合久久| 色偷偷久久一区二区三区| 九九久久99综合一区二区| 久久青青草原亚洲av无码| 日产精品久久久久久久| 国内精品久久久久久中文字幕| 久久久精品波多野结衣| 久久精品99久久香蕉国产色戒| 国产精品日韩深夜福利久久| 无码国内精品久久人妻蜜桃| 亚洲天堂久久精品| 亚洲AV无码一区东京热久久| 成人午夜精品久久久久久久小说| 精品久久久久久中文字幕大豆网| 久久久久久免费一区二区三区| 久久99九九国产免费看小说| 久久精品嫩草影院| 五月丁香综合激情六月久久| 无码人妻久久一区二区三区蜜桃 | 7国产欧美日韩综合天堂中文久久久久| 久久久久一级精品亚洲国产成人综合AV区| 亚洲中文字幕久久精品无码APP | 亚洲国产成人久久笫一页| 久久国产精品久久国产精品| 久久久久久人妻无码| 久久精品国产乱子伦| 国产精品久久久久久久app | 久久精品国产免费| 国内精品久久久久久99| 亚洲va久久久噜噜噜久久天堂| 亚洲国产成人精品91久久久 | 久久夜色精品国产噜噜亚洲a| 亚洲国产精品久久66| 国产精品久久永久免费| 久久99精品国产麻豆宅宅| 久久久国产精品亚洲一区| 久久九九精品99国产精品| 久久精品国产亚洲AV麻豆网站 |