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

隨筆-341  評論-2670  文章-0  trackbacks-0

就像之前的博客文章所說的,(主要還是)因為GacUI的原因,我決定開發(fā)一個更好的可配置輕量級語法分析器來代替之前的落后的版本。在說這個文章之前,我還是想在此向大家推薦一本《編程語言實現(xiàn)模式》,這的確是一本好書,讓我相見恨晚。

其實說到開發(fā)語法分析器,我從2007年就已經(jīng)開始在思考類似的問題了。當時C++還處于用的不太熟練的時候,難免會做出一些傻逼的事情,不過總的來說當年的idea還是能用的。從那時候開始,我為了鍛煉自己,一直在實現(xiàn)各種不同的語言。所以給自己開發(fā)一個可配置語法分析器也是在所難免的事情了。于是就有:
第一版:http://hi.baidu.com/geniusvczh/archive/tag/syngram%E6%97%A5%E5%BF%97
第二版:http://www.shnenglu.com/vczh/archive/2009/04/06/79122.html
第三版:http://www.shnenglu.com/vczh/archive/2009/12/13/103101.html
還有第三版的教程:http://www.shnenglu.com/vczh/archive/2010/04/28/113836.html

上面的所有分析器都致力于在C++里面可以通過直接描述文法和一些語義行為來讓系統(tǒng)可以迅速構造出一個針對特定目的的用起來方便的語法分析器,而“第三版”就是到目前為止還在用的一個版本。至于為什么我要做一個新的——也就是第四版——之前的文章已經(jīng)說了。

而今天,第四版的開發(fā)已經(jīng)開始了有好幾天。如果大家關心進度的話,可以去GacUI的Codeplex頁面下載代碼,然后閱讀Common\Source\Parsing下面的源文件。對應的單元測試可以在Common\UnitTest\UnitTest\TestParsing.cpp里找到。

于是今天就說說關于構造語法樹的事情。

用C++寫過parser的人都知道,構造語法樹以及語義分析用的符號表是一件極其繁瑣,而且一不小心就容易寫出翔的事情。但是根據(jù)我寫過無窮多棵語法樹以及構造過無窮多個符號表以及附帶的副作用,翔,啊不,經(jīng)驗,做這個事情還是有一些方法的。

在介紹這個方法之前,首先要說一句,人肉做完下面的所有事情是肯定要瘋掉的,所以這一次的可配置語法分析器我已經(jīng)決定了一定要TMD寫出一個生成語法樹的C++代碼的工具。

一顆語法樹,其實就是一大堆互相繼承的類。一切成熟的語法樹結構所具有的共同特征,不是他的成員怎么安排,而是他一定會附帶一個visitor模式的機制。至于什么是visitor模式,大家請自行參考設計模式,我就不多說廢話了。這一次的可配置語法分析器是帶有一個描述性語法的。也就是說,跟Antlr或者Yacc一樣,首先在一個文本文件里面準備好語法樹結構和文法規(guī)則,然后我的工具會幫你生成一個內(nèi)存中的語法分析器,以及用C++描述的語法樹的聲明和實現(xiàn)文件。這個描述性語法就類似下面的這個大家熟悉到不能再熟悉的帶函數(shù)的四則運算表達式結構:

class Expression
{
}

class NumberExpression : Expression
{
    token value;
}

class BinaryExpression : Expression
{
    enum BinaryOperator
    {
        Add,
        Sub,
        Mul,
        Div,
    }

    Expression firstOperand;
    Expression secondOperand;
    BinaryOperator binaryOperator;
}

class FunctionExpression : Expression
{
    token functionName;
    Expression[] arguments;
}

token NAME = "[a-zA-Z_]/w*";
token NUMBER = "/d+(./d+)";
token ADD = "/+";
token SUB = "-";
token MUL = "/*";
token DIV = "http://";
token LEFT = "/(";
token RIGHT = "/)";
token COMMA = ",";

rule NumberExpression Number
        = NUMBER : value;

rule FunctionExpression Call
        = NAME : functionName "(" [ Exp : arguments { "," Exp : arguments } ] ")";

rule Expression Factor
        = !Number | !Call;

rule Expression Term
        = !Factor;
        = Term : firstOperand "*" Factory : secondOperand as BinaryExpression with { binaryOperator = "Mul" };
        = Term : firstOperand "/" Factory : secondOperand as BinaryExpression with { binaryOperator = "Div" };

rule Expression Exp
        = !Term;
        = Exp : firstOperand "+" Term : secondOperand as BinaryExpression with { binaryOperator = "Add" };
        = Exp : firstOperand "-" Term : secondOperand as BinaryExpression with { binaryOperator = "Sub" };

上面的語法樹聲明借用的C#語法,描述起來特別簡單。但是要在C++里面達到可以使用的程度,肯定要有一個自帶的visitor模式。所以出來之后的代碼大概就類似于下面這個樣子:

class Expression;
class NumberExpression;
class BinaryExpression;
class FunctionExpression;

class Expression : public ParsingTreeCustomBase
{
public:
    class IVisitor : public Interface
    {
    public:
        virtual void Visit(NumberExpression* node)=0;
        virtual void Visit(BinaryExpression* node)=0;
        virtual void Visit(FunctionExpression* node)=0;
    };

    virtual void Accept(IVisitor* visitor)=0;
};

class NumberExpression : public Expression
{
public:
    TokenValue value;

    void Accept(IVisitor* visitor){visitor->Visit(this);}
};

class BinaryExpression : public Expression
{
public:
    enum BinaryOperator
    {
        Add, Sub, Mul, Div,
    };
    Ptr<Expression> firstOperator;
    Ptr<Expression> secondOperator;
    BinaryOperator binaryOperator;

    void Accept(IVisitor* visitor){visitor->Visit(this);}
};

class FunctionExpression : public Expression
{
public:
    TokenValue functionName;
    List<Ptr<Expression>> arguments;

    void Accept(IVisitor* visitor){visitor->Visit(this);}
};

為什么要這樣做呢?學習過面向對象開發(fā)方法的都知道,把一個明顯是繼承結構的東西寫成一堆union/struct和一個enum來判斷他們,是不對的。第一個不好的地方就是,如果其中的成員需要構造函數(shù)和析構函數(shù),那union就用不了了,struct就一定會造成大量的內(nèi)存浪費。因為一顆語法樹是可以很大的。其次,當語法樹的結構(主要是添加刪除了新的語法樹類型)之后,我們根本不可能保證我們所有的swtich(node->enumType)語句都接受到了正確的更新。

那要如何解決這兩個問題呢?答案之一就是使用visitor模式。盡管剛開始寫起來的時候可能會有點別扭,但是我們只要把原本是swtich結構的代碼做一下Continuation Passing Style變換,就可以寫出使用visitor的版本了。在這里我做一個小小的演示,如何把一個“把上面的語法樹還原成四則運算式子的函數(shù)”給用Expression::IVisitor的框架下實現(xiàn)出來:

class FunctionExpression : public Expression
{
public:
    TokenValue functionName;
    List<Ptr<Expression>> arguments;

    void Accept(IVisitor* visitor){visitor->Visit(this);}
};

class ExpressionPrinter : public Expression::IVisitor
{
public:
    WString result;

    void Visit(NumberExpression* node)
    {
        result+=node->value.stringValue;
    }

    void Visit(BinaryExpression* node)
    {
        result+=L"(";
        node->firstOperand->Accept(this);
        switch(binaryOperator)
        {
        case Add: result+=L" + "; break;
        case Sub: result+=L" - "; break;
        case Mul: result+=L" * "; break;
        case Div: result+=L" / "; break;
        }
        node->secondOperand->Accept(this);
        result+=L")";
    }

    void Visit(FunctionExpression* node)
    {
        result+=node->functionName.stringValue+L"(";
        for(int i=0;i<arguments.Count();i++)
        {
            if(i>0) result+=L", ";
            arguments[i]->Accept(this);
        }
        result+=L")";
    }
};

WString PrintExpression(Ptr<Expression> expression)
{
    ExpressionPrinter printer;
    expression->Accept(&printer);
    return printer.result;
}

其實大家可以看到,使用了visitor模式,代碼量其實也沒有多大變化,本來是遞歸的地方還是遞歸,本來該計算什么還計算什么,唯一不同的就是原本這個“函數(shù)”的參數(shù)和返回值都跑到了一個visitor類的成員變量里面去了。當然,為了便于使用,一般來說我們會把原本的函數(shù)的原型寫出來,并且在里面調(diào)用visitor模式,就像上面的PrintExpression函數(shù)一樣。如果我們高興的話,完全可以在ExpressionPrinter這個visitor類里面使用PrintExpression,無非就是在里面構造新的ExpressionPrinter然后獲取結構罷了。一般來說,visitor類都是非常的輕量級的,在現(xiàn)今的CPU性能下面,構造多幾個完全不會帶來多大影響。

可配置語法分析器既然擁有一個描述性語法,那么我肯定也針對這個描述性語法寫了一顆語法樹的。這顆語法樹的代碼在Common\Source\Parsing\ParsingDefinition.h里面,而ParsingLogging.cpp則是跟上面說的一樣,用visitor的方法寫了一個龐大的把語法樹轉回描述性語法的函數(shù)。這個函數(shù)非常有用,不僅可以用來打log,還可以用來保存程序生成的一個語法規(guī)則(反正可以parse回來,所以保存成文本是一件特別方便的事情),甚至是生成錯誤消息的片段等等。

今天就先講到這里了。現(xiàn)在的可配置語法分析器的開發(fā)進度是正在寫語義分析的部分。等到語義分析寫完了,我會再寫一篇紀事來說明開發(fā)語義分析程序和構造符號表的一般做法。

posted on 2012-11-21 06:42 陳梓瀚(vczh) 閱讀(11661) 評論(6)  編輯 收藏 引用 所屬分類: C++

評論:
# re: 可配置語法分析器開發(fā)紀事(一)&mdash;&mdash;構造語法樹 2012-11-21 07:34 | Ooseven
哈,早就該這樣做了,我比較幸運,沒有走那么多彎路,一開始就是奔著類似yacc的思路來設計語法生成器的。不過雖然是這樣,設計完了之后還是有點遺憾,腦袋里只有c++了,所以沒有考慮到別的語言,其實完全可以再大膽一點,用c++設計一個可以方便的生成任何語言的語法分析器框架,java,c#,pascal等等,非常重要的一點是生成對任何語言的支持應該非常的方便,不需要修改已經(jīng)寫好的代碼,只需要增加類似插件的機制才是完美的設計。

如果您的第四版沒有考慮到這一點,那我估計你還需要來個第五版。希望我的提醒對你有用。

  回復  更多評論
  
# re: 可配置語法分析器開發(fā)紀事(一)&mdash;&mdash;構造語法樹 2012-11-21 07:50 | 陳梓瀚(vczh)
@Ooseven
其實我的目標只有生成語法樹的代碼,至于分析過程,還是在類庫里面自己搞定的,所以我不考慮其它語言了。  回復  更多評論
  
# re: 可配置語法分析器開發(fā)紀事(一)&mdash;&mdash;構造語法樹 2012-11-21 08:08 | ooseven
作為語法分析生成器而言,其實是分析用戶編寫的bnf腳本,而后產(chǎn)生語法棧并讓用戶在腳本里定義的相應的動作正確的入棧與出棧,至于用戶寫bnf腳本里的動作定義是要生成語法樹,還是要直接進行其他操作都可以,為啥要定死一定要生成語法樹呢。
而且語法樹是啥結構根本跟語法生成器沒有半毛錢關系,建議解耦。您的目標是生成語法樹的代碼,那么只要語法生成器些好了,就只在bnf腳本里折騰就行了
  回復  更多評論
  
# re: 可配置語法分析器開發(fā)紀事(一)&mdash;&mdash;構造語法樹[未登錄] 2012-11-21 20:11 | 陳梓瀚(vczh)
@ooseven
這,因為我需要的就是語法樹啊。如果你的原始目的是直接計算表達是結果的話,你打可以自己實現(xiàn)Expression::IVisitor,里面就已經(jīng)隱含了入棧出棧的含義了。而且這還更maintainable,因為突然有一天你發(fā)現(xiàn)你還要干點其他事情,譬如說求導數(shù),這個時候才來做語法樹然后寫兩個visitor分別求值和求導,何苦呢。所以不如一開始就讓你自己設計好語法樹,visitor接口替你生成了,然后你要做的事情,就是實現(xiàn)一個visitor。這樣的話,你并沒有比“直接求值”多寫什么代碼,而且還給你的未來帶來了方便,多好啊。

而且對于我個人來說,我的確在大部分情況下都是需要語法樹的。  回復  更多評論
  
# re: 可配置語法分析器開發(fā)紀事(一)&mdash;&mdash;構造語法樹[未登錄] 2012-11-21 20:13 | 陳梓瀚(vczh)
@ooseven
不過話說回來,語法樹是什么結構如果不寫進文法的話,會帶來更大的噪音。參考yacc(如果你要產(chǎn)生語法樹還要在{里面自己new多麻煩啊}),和Antlr(同理)。所以在大部分情況下都需要語法樹的時候,你給出必須生成語法樹的實現(xiàn),帶來的結構就是,這個描述起來特別他媽的簡潔。而且在你不打算生成語法樹的時候其實也不會造成什么麻煩。反過來就不一樣了。  回復  更多評論
  
# re: 可配置語法分析器開發(fā)紀事(一)&mdash;&mdash;構造語法樹 2013-08-24 06:08 | RexfieldVon
《編程語言實現(xiàn)模式》是本好書,每當我思路卡殼的時候都會翻出來查。  回復  更多評論
  
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            另类图片综合电影| 性做久久久久久免费观看欧美| 狂野欧美性猛交xxxx巴西| 精品盗摄一区二区三区| 美女网站久久| 欧美成人视屏| 亚洲欧美日韩中文视频| 欧美一区三区三区高中清蜜桃| 在线播放豆国产99亚洲| 亚洲国产精品一区制服丝袜| 欧美人成网站| 久久久久久有精品国产| 欧美国产91| 欧美一区二区啪啪| 美女999久久久精品视频| 在线亚洲欧美| 久久精品91久久久久久再现| 亚洲精品日韩精品| 亚洲欧美国产毛片在线| 亚洲国产欧美不卡在线观看| 久久久久亚洲综合| 午夜日韩在线观看| 另类尿喷潮videofree| 亚洲视频视频在线| 久久久综合网站| 亚洲在线观看视频| 久久一区二区三区av| 亚洲欧美另类在线| 免费视频一区| 久久精品72免费观看| 欧美激情亚洲另类| 久久亚洲图片| 国产精品久久久免费| 欧美mv日韩mv亚洲| 国产欧美69| 9人人澡人人爽人人精品| 韩国v欧美v日本v亚洲v| 亚洲午夜一区二区| 一本一本久久a久久精品牛牛影视| 羞羞漫画18久久大片| 亚洲一区免费网站| 欧美精品一区在线发布| 另类av导航| 国模套图日韩精品一区二区| 一区二区欧美激情| 99国产精品私拍| 久久一区国产| 巨乳诱惑日韩免费av| 国产午夜精品久久| 亚洲免费视频中文字幕| 亚洲男女自偷自拍图片另类| 欧美精品一区二区三| 欧美大片在线看免费观看| 韩国女主播一区| 午夜一区在线| 欧美在线观看天堂一区二区三区| 欧美视频在线观看视频极品| 亚洲茄子视频| 99re6这里只有精品视频在线观看| 美国十次了思思久久精品导航| 久热精品视频在线观看一区| 樱桃国产成人精品视频| 久久久久久亚洲综合影院红桃| 久久精品国产亚洲精品| 韩国精品一区二区三区| 国产视频欧美| 午夜精品一区二区三区在线视| 欧美午夜精品久久久| 日韩一区二区精品| 亚洲淫性视频| 国产精品丝袜91| 午夜视频久久久久久| 久久精品国产亚洲一区二区| 精品av久久707| 蜜臀av性久久久久蜜臀aⅴ| 亚洲国产精品久久久久秋霞不卡| 亚洲激情成人| 欧美日韩精品一区二区| 国产视频精品免费播放| 一区二区不卡在线视频 午夜欧美不卡在 | 亚洲视频在线视频| 国产精品视频精品| 久久激情网站| 亚洲第一网站免费视频| 夜夜狂射影院欧美极品| 国产精品久久久久影院色老大| 亚洲日本va午夜在线电影| 亚洲午夜电影| 久热精品视频| 中文欧美日韩| 国模叶桐国产精品一区| 亚洲高清在线精品| 亚洲午夜激情网页| 久久综合九色| 国产夜色精品一区二区av| 亚洲人成77777在线观看网| 美女久久网站| 亚洲一区二区三区在线看| 亚洲欧美日韩精品| 欧美激情国产日韩精品一区18| 一区二区高清| 一区二区三区在线免费播放| 欧美日韩免费观看一区二区三区| 午夜在线视频观看日韩17c| 亚洲第一视频| 欧美在线观看视频在线 | 欧美精品一区二区精品网 | 亚洲激情视频网| 久久精品卡一| 亚洲午夜免费视频| 亚洲第一级黄色片| 国产伪娘ts一区| 欧美色欧美亚洲另类二区| 麻豆精品视频在线观看| 亚洲欧美视频在线观看| 99在线|亚洲一区二区| 欧美大片在线观看一区二区| 久久成人综合视频| 亚洲欧美日韩一区| 在线性视频日韩欧美| 亚洲国产精品福利| 狠狠色伊人亚洲综合网站色| 国产乱码精品一区二区三| 欧美日韩精品欧美日韩精品| 久久伊人亚洲| 久久精品夜色噜噜亚洲aⅴ| 亚洲亚洲精品三区日韩精品在线视频 | 亚洲精品日本| 亚洲国产精品久久91精品| 国产亚洲欧洲997久久综合| 国产精品九色蝌蚪自拍| 欧美日韩免费在线观看| 欧美精品国产| 欧美日韩国产免费| 欧美日韩精品在线视频| 欧美精品www| 欧美日韩国产综合网 | 亚洲自拍高清| 亚洲欧美日韩综合aⅴ视频| 亚洲一区日韩在线| 亚洲自拍偷拍麻豆| 亚洲欧美日本视频在线观看| 亚洲主播在线观看| 午夜一区二区三区不卡视频| 亚洲欧美激情四射在线日| 亚洲欧美激情诱惑| 欧美一区二视频在线免费观看| 性欧美18~19sex高清播放| 欧美一区二区视频在线观看| 欧美在线视频观看免费网站| 久久精品人人做人人综合| 久久久久久夜| 欧美激情综合| 国产精品久久久久久妇女6080| 国产精品久久999| 国产区欧美区日韩区| 欧美成人在线网站| 中国亚洲黄色| 欧美一区二区高清在线观看| 久久全球大尺度高清视频| 欧美激情91| 国产精品免费一区二区三区在线观看 | 亚洲午夜在线| 久久精品成人一区二区三区| 欧美**字幕| 亚洲美女黄色| 欧美伊久线香蕉线新在线| 狂野欧美性猛交xxxx巴西| 欧美视频一区二区在线观看 | 久久免费视频这里只有精品| 欧美日韩p片| 国产日韩在线不卡| 日韩视频欧美视频| 欧美在线国产| 亚洲国产婷婷香蕉久久久久久99 | 久久久国产精品一区二区三区| 欧美激情一二区| 亚洲欧美日韩综合| 欧美精品情趣视频| 国内精品免费在线观看| 艳妇臀荡乳欲伦亚洲一区| 久久精品一区四区| 亚洲国产人成综合网站| 亚洲嫩草精品久久| 欧美麻豆久久久久久中文| 国产亚洲女人久久久久毛片| 9色porny自拍视频一区二区| 欧美在线观看一区| 99精品国产99久久久久久福利| 欧美在线综合| 国产精品热久久久久夜色精品三区| 亚洲国产婷婷综合在线精品| 欧美伊人影院| 在线视频精品| 欧美精品在线免费观看| 在线高清一区| 久久久久综合| 性一交一乱一区二区洋洋av| 国产精品ⅴa在线观看h| 日韩视频在线免费|