• <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>
            隨筆-341  評論-2670  文章-0  trackbacks-0
                上一篇文章中我們看到了可配置語法分析器使用起來的樣子,在這篇文章中我將告訴大家如何通過重載操作符的方法構(gòu)造文法表達(dá)式樹,從而使用遞歸向下法進(jìn)行語法分析的工作。

                在這之前我們將研究一下什么是文法表達(dá)式。我們將文法表達(dá)式看成分析器,于是復(fù)雜的文法表達(dá)式就是由簡單的分析器通過各種方法組合起來的復(fù)雜分析器。一個分析器有以下幾個屬性:

                1:輸入類型。輸入類型通常是一個字符串的指針還是迭代器什么的,具體類型不重要,重要的是輸入狀態(tài)必須能被復(fù)制,能跳到下一個元素。當(dāng)然wchar_t*也滿足這種要求,但是我們?yōu)榱送ㄓ眯裕ㄆ┤缈梢詾槟阕约旱娜萜鲾U(kuò)展出一個輸入迭代器)我們采用類似STL的迭代器的方法,也就是concept(這并沒有包含其技巧,只是概念)來實(shí)現(xiàn)。然后庫將為一些基本的東西提供默認(rèn)的迭代器,譬如IEnumerable<T>(嗯嗯,這不是C#,已經(jīng)被Vczh Library++實(shí)現(xiàn)了。容器采用了一種泛型+接口的方法,但是在不必要的情況下允許不支付虛函數(shù)的代價(jià),不過這根本章內(nèi)容無關(guān),以后再談),或者WString和AString。

                2:輸出類型。輸出類型一般包含兩個方面。第一個是成功后的結(jié)果,第二個是失敗后的錯誤信息。怎么讓可配置語法分析器在恰當(dāng)?shù)牡胤捷敵鲱愋鸵彩且粋€很復(fù)雜的問題,不過這根本章內(nèi)容無關(guān),下一篇文章接著講這個細(xì)節(jié)。在這里我們先忽略錯誤信息,就如同正則表達(dá)式拒絕匹配一個字符串也不會告訴你為什么一樣。

                我們可以通過這兩種屬性來構(gòu)造出一個文法表達(dá)式的基類。表達(dá)式樹通常用基類+若干子類的方法來實(shí)現(xiàn),有了基類等于定下了子類的基調(diào)。
            1 template<typename I, typename O>
            2 class Expression
            3 {
            4 public:
            5   virtual Maybe<O> Parse(I& input)=0;
            6 };

                Maybe指的是里面可以有類型O的值,或者什么都沒有。這額外的信息可以添加一個bool來表達(dá),這里就不贅敘了。到了這里我們明白一個表達(dá)式樹的重點(diǎn)不是其內(nèi)容,而是分析輸入的算法。因此我們可以組合出連接、分支和循環(huán):
             1 template<typename I, typename O1, typename O2>
             2 class Sequence : public Expression<I, ParsingPair<O1, O2>>;
             3 {
             4 public:
             5   Ptr<Expression<I, O1>> left;
             6   Ptr<Expression<I, O2>> right;
             7 
             8   Maybe<ParsingPair<O1, O2>> Parse(I& input);
             9 };
            10 
            11 template<typename I, typename O>
            12 class Alternate : public Expression<I, O>;
            13 {
            14 public:
            15   Ptr<Expression<I, O>> left;
            16   Ptr<Expression<I, O>> right;
            17 
            18   Maybe<O> Parse(I& input);
            19 };
            20 
            21 template<typename I, typename O>
            22 class Loop : public Expression<I, ParsingList<O>>;
            23 {
            24 public:
            25   Ptr<Expression<I, O>> element;
            26   int min;
            27   int max;
            28 
            29   Maybe<ParsingList<O>> Parse(I& input);
            30 };

                這就是連接、分支和循環(huán)的聲明了。現(xiàn)在我們可以很清楚的了解什么是帶類型的文法了。類型主要指的是輸出類型,而輸入類型肯定是不能變化的。ParsingPair<A, B>就是一個帶兩個數(shù)據(jù)的結(jié)構(gòu),而ParsingList<T>是一個鏈表。做成這樣主要是為了在傳遞他們的時(shí)候不要做太多浪費(fèi)的復(fù)制工作,在這里我們只需要了解其概念就好了。

                每一種組合都對子文法的類型有著一些要求,譬如說分支要求左右文法表達(dá)式的類型是一樣的。而且輸出類型是通過子文法的類型計(jì)算而得到的。Ptr<T>是智能指針,在這里使用主要是為了避免復(fù)制的時(shí)候出現(xiàn)問題。智能指針在這種數(shù)據(jù)結(jié)構(gòu)下還是十分好用的,反正構(gòu)造和析構(gòu)一條文法的效率都是無關(guān)緊要的,不要太慢就可以了。

                但是我們?nèi)绾沃剌d操作符來組合文法表達(dá)式呢?其實(shí)文法表達(dá)式最終產(chǎn)生的結(jié)果都是Ptr<Expression<I, O>>,Ptr的操作符重載是不能修改的,所以我們還要一個代理類:
             1 template<typename I, typename O>
             2 class Node
             3 {
             4 public:
             5   Ptr<Expression<I, O>> expression;
             6 };
             7 
             8 template<typename I, typename O>
             9 Node<I, O> operator|(const Node<I, O>& left, const Node<I, O>& right);
            10 
            11 template<typename I, typename O1, typename O2>
            12 Node<I, ParsingPair<O1, O2>> operator+(const Node<I, O1>& left, const Node<I, O2>& right);
            13 
            14 //除了+以外,還可以繼承*啊,或者干脆寫個loop(node, min, max)什么的
            15 template<typename I, typename O>
            16 Node<I, ParsingList<O>> operator+(const Node<I, O>& element);

                我們就可以在每一個操作符重載里面將各自Node的expression成員變量拿出來,然后通過構(gòu)造上面提供的Sequence、Alternate和Loop來構(gòu)造更加復(fù)雜的文法表達(dá)式,最后重新裝進(jìn)一個Node<I, O>里面就行了。

                這就是我們可以將文法寫進(jìn)C++的小技巧。下一篇文章我們將會了解到表達(dá)式的每一個Parse函數(shù)內(nèi)部都做了些什么。
            posted on 2009-12-04 23:43 陳梓瀚(vczh) 閱讀(3204) 評論(1)  編輯 收藏 引用 所屬分類: VL++3.0開發(fā)紀(jì)事

            評論:
            # re: Vczh Library++ 3.0之可配置語法分析器(設(shè)計(jì)文法表達(dá)式) 2009-12-05 06:59 | radar
            精辟啊!  回復(fù)  更多評論
              
            日日狠狠久久偷偷色综合0| 一本色综合网久久| 国内精品伊人久久久久影院对白 | 久久人妻少妇嫩草AV蜜桃| 久久久91精品国产一区二区三区| 中文字幕亚洲综合久久| 久久亚洲AV无码精品色午夜| 久久97精品久久久久久久不卡 | 精品久久一区二区| 青青草国产精品久久| 亚洲精品乱码久久久久久蜜桃 | 伊人久久精品无码av一区| 久久精品www| 亚洲欧美久久久久9999| 日本精品久久久中文字幕| 久久精品青青草原伊人| 狠狠精品久久久无码中文字幕 | 国产精品欧美久久久久无广告| 久久精品国产精品亚洲下载| 天天爽天天狠久久久综合麻豆| 国产无套内射久久久国产| 久久精品国产亚洲av水果派| 亚洲精品国产综合久久一线| 99久久成人18免费网站| 久久久久亚洲av无码专区| 免费精品久久久久久中文字幕| 久久亚洲国产中v天仙www| 人妻精品久久无码区| 囯产极品美女高潮无套久久久| 久久久久久噜噜精品免费直播| 久久综合狠狠色综合伊人| 国产精品一久久香蕉国产线看观看| 麻豆精品久久久久久久99蜜桃 | 四虎影视久久久免费| 91亚洲国产成人久久精品网址| 久久国产精品一国产精品金尊| 久久这里只有精品首页| 久久无码中文字幕东京热| 中文字幕无码久久精品青草| 亚洲欧美国产精品专区久久| 中文字幕无码久久精品青草|