• <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  評(píng)論-2670  文章-0  trackbacks-0
             

            決定把Vczh Library++3.0的項(xiàng)目的主要工程升級(jí)到VC++ 2010。新的VC++智能提示變得無敵順暢,無論我怎么模板怎么宏亂嵌套,結(jié)果都是正確的。娃哈哈。不過反正語法是兼容的,使用Vczh Library++3.0的也無法直接使用那個(gè)單元測(cè)試用的工程文件,所以我想影響應(yīng)該不大。

            posted @ 2010-05-19 19:27 陳梓瀚(vczh) 閱讀(3414) | 評(píng)論 (18)編輯 收藏

            跟老外聊天果然是不流利啊,就算英文可以看文章寫wiki,說起話來還是不夠爽。特別是非美帝且非天朝的人說起英語,口音跟天朝有一拼,就互相聽不懂了。

            四個(gè)星期。第一個(gè)周末去了批發(fā)市場(chǎng)買名牌,當(dāng)然我只是看,同事在買。第二個(gè)周末去了看郁金香展,拍了些圖。第三個(gè)星期去了Orcas島(這個(gè)M$產(chǎn)品的code name就是喜歡用這些湖啊島啊山啊……)等到山頂,那個(gè)比上海北京都好得多的空氣讓我們可以直接用眼睛看到加拿大的山。第四個(gè)星期同事出去玩,我就在寫代碼了。還是寫代碼爽啊,游山玩水什么的都是浮云。當(dāng)然戰(zhàn)利品是有的,帶了一盒明信片,還有人體工學(xué)鍵盤鼠標(biāo)。M$賣給自己人還要打5折,壓榨了我們的剩余價(jià)值應(yīng)該送個(gè)才對(duì),后來打聽到了,美帝的人要鍵盤是不用錢的,天朝的人就得自己花錢了,雖然打折……

            Vczh Library++的C囧(NativeX,拿C改了點(diǎn)語法,當(dāng)中間語言用,功能基本沒有減少)編譯器一直在重構(gòu)。昨天晚上終于做了一個(gè)功能,可以把二進(jìn)制格式的assembly轉(zhuǎn)成文本文件供查閱了。雖然做了點(diǎn)代碼生成的優(yōu)化,不過其實(shí)還是不夠好。接下來就是慢慢看文本文件里面記錄的指令集,慢慢想辦法優(yōu)化輸出的指令了。給C囧語言加上點(diǎn)泛型的想法開始成型了,雖然這主要是為了高級(jí)語言編譯到低級(jí)的C囧語言所做的準(zhǔn)備,不過還是有點(diǎn)麻煩。Vczh Library++的最終目的就是提供本地強(qiáng)類型語言、垃圾收集的面向?qū)ο髲?qiáng)類型語言和動(dòng)態(tài)語言三種語言的編譯器和一些輔助類,讓這三種語言可以編譯到相同的assembly,互相調(diào)用,并且提供大量工具可以讓用戶們基于這三種語言的編譯器輕松開發(fā)出自己的語言。最后只要把最低級(jí)的C囧語言的JIT給做了,就等于所有語言同時(shí)享受JIT帶來的好處。革命尚未成功,同志仍需努力。

            明天上飛機(jī),美帝的人民群眾對(duì)食物果然還是沒有追求啊,還是天朝好……
            posted @ 2010-05-06 22:56 陳梓瀚(vczh) 閱讀(3209) | 評(píng)論 (9)編輯 收藏
                 摘要: Vczh Library++ 語法分析器開發(fā)指南 陳梓瀚   前言 在日常的開發(fā)工作中我們總是時(shí)不時(shí)需要寫一些語法分析器。語法分析器不一定指的是一門語言的編譯器前端,也有可能僅僅是一個(gè)自己設(shè)計(jì)格式的配置文件的讀寫程序,或者是一門用來簡(jiǎn)化我們開發(fā)的DSL(領(lǐng)域?qū)S谜Z言)。我們可以選擇使用XML,不過因?yàn)閄ML的噪音實(shí)在是太多,所以自己寫語法分析器在有些情況下是必要的,特別是那種經(jīng)常...  閱讀全文
            posted @ 2010-04-27 20:05 陳梓瀚(vczh) 閱讀(11337) | 評(píng)論 (12)編輯 收藏
                這幾天使用Performance Wizard搞了一下我的那個(gè)東西,結(jié)果發(fā)現(xiàn)瓶頸在一個(gè)很不可思議的地方,當(dāng)然總的原因是因?yàn)樘摵瘮?shù)調(diào)太多了,所以得修改修改。不過還好,還是有不破壞設(shè)計(jì)的辦法的。

                Performance Wizard是Visual Studio自帶的一個(gè)工具。使用的時(shí)候很簡(jiǎn)單,首先找Analyze -> Launch Performance Wizard...,出來一個(gè)向?qū)?。第一個(gè)頁面選你要做檢查的工程,第二個(gè)頁面在Sampling和Instrumentation之間作選擇(我一般選擇后面的),完成了之后就會(huì)出來一個(gè)叫Performance Explorer的窗口,里面有建立好的性能檢測(cè)文件,當(dāng)然是不保存的,一般來說也不用保存。Performance Explorer的根節(jié)點(diǎn)跟你的解決方案同名,右鍵選擇Properties,在Sampling的Sample Event里面選擇Performance Counter,好了大功告成。

                想啟動(dòng)很簡(jiǎn)單,首先將你的編譯指向Release,然后選擇Performance Explorer根節(jié)點(diǎn)下面Targets目錄下面的一個(gè)跟工程同名的節(jié)點(diǎn),點(diǎn)擊Performance Explorer工具欄上有綠色箭頭的按鈕,然后就開始了。上面的配置將性能分析指到了最精確的一種方法,因此運(yùn)行的時(shí)候慢,而且產(chǎn)生的文件特別大,建議在超級(jí)好的機(jī)器上使用。不然的話就只好使用Sampling而不是Instrumentation了。這個(gè)時(shí)候Performance Wizard會(huì)編譯你的工程然后執(zhí)行一直到結(jié)束,執(zhí)行的過程中記錄下很多信息。注意在Vista或以上操作系統(tǒng),Visual Studio要用管理員權(quán)限打開才能正常使用Performance Wizard。

                之后就一直等,完了就會(huì)有一張超級(jí)詳細(xì)的報(bào)表出來,這個(gè)時(shí)候切換到“Call Tree”報(bào)表,上面一個(gè)火苗樣子的工具欄按鈕按下去就自定尋找性能瓶頸并定位。這個(gè)Call Tree就是Call Stack在整個(gè)程序執(zhí)行過程中的變化情況,附加信息非常詳細(xì),非常好用。

                慢慢享受吧,啊哈哈。
            posted @ 2010-04-16 20:42 陳梓瀚(vczh) 閱讀(3132) | 評(píng)論 (10)編輯 收藏
                今晚在Vczh Library++3.0里面實(shí)現(xiàn)了C++調(diào)用NativeX腳本函數(shù)的單元測(cè)試代碼,這個(gè)Demo其實(shí)是從單元測(cè)試?yán)锩娉槌鰜淼摹?br>
                因?yàn)榇a可以在這里下載到,所以這里只列出當(dāng)前版本C++調(diào)用NativeX腳本函數(shù)的例子。首先我們假設(shè)有下面的字符串,然后存放在const WString& code;的變量里面:
            1 unit simple_function;
            2 function int Add(int a, int b){
            3     result=a+b;
            4     exit;
            5 }
            6 function int Sub(int a, int b){
            7     result=a-b;
            8     exit;
            9 }

                因?yàn)镹ativeX基本上是用來被更加高級(jí)的語言做編譯媒介的中間語言,或者拿來寫一點(diǎn)腳本引擎的庫用的,因此語法并沒有設(shè)計(jì)得十分的簡(jiǎn)練。大部分還是從便于分析,并且接近C語言的角度出發(fā)。現(xiàn)在的NativeX支持?jǐn)?shù)組、指針、自定義函數(shù)和結(jié)構(gòu)體,不過這個(gè)Demo為了顯露出本質(zhì),就簡(jiǎn)單化了,只實(shí)現(xiàn)了一個(gè)加法函數(shù)和減法函數(shù)。

                那么假設(shè)這段代碼已經(jīng)保存在變量code里面,那么可以通過下面的方法來調(diào)用Add和Sub函數(shù):
             1 List<Ptr<LanguageAssembly>> references;
             2 List<WString> codes;
             3 List<Ptr<LanguageException>> errors;
             4 codes.Add(code);
             5 
             6 Ptr<ILanguageProvider> provider=GetNativeXProvider().provider;
             7 Ptr<LanguageAssembly> assembly=provider->Compile(references.Wrap(), codes.Wrap(), errors.Wrap());
             8 BasicLanguageMetadata* metadata=assembly->GetBasicLanguageMetadata();
             9 BasicDeclarationInfo add=metadata->GetDeclaration(0);
            10 BasicDeclarationInfo sub=metadata->GetDeclaration(1);
            11 
            12 LanguageHost host(65536);
            13 host.LoadAssembly(assembly);
            14 Ptr<LanguageState> state=host.CreateState();
            15 BasicFunctionExecutor<int(int,int)> addFunc(add, state);
            16 BasicFunctionExecutor<int(int,int)> subFunc(sub, state);
            17 
            18 TEST_ASSERT(addFunc(12)==3);
            19 TEST_ASSERT(subFunc(12)==-1);

                不通過BasicFunctionExecutor而想自己設(shè)置返回值指針和自己挨個(gè)把參數(shù)Push進(jìn)堆棧也行,其實(shí)BasicFunctionExecutor的實(shí)現(xiàn)正是調(diào)用了這些原始接口來做到的。

                例子就分享到這里了,完整的代碼請(qǐng)去這里下載。
            posted @ 2010-04-10 23:19 陳梓瀚(vczh) 閱讀(2750) | 評(píng)論 (4)編輯 收藏
                上海什么的完全不能跟西雅圖比啊,西雅圖能見度無敵高,下雨了車開了一個(gè)小時(shí)玻璃還是干凈的,在上海下雨天開一個(gè)小時(shí)車那玻璃上全是泥。西雅圖綠化非常好,就像是把城市建在了樹林里面。而且高速公路設(shè)計(jì)合理,拐彎的時(shí)候有斜坡不會(huì)一不小心甩出去。而且有些地方還有Stop標(biāo)記,無論你路上東西多不多,車一定要先完全停止了才能再啟動(dòng)。

                比較窘的地方就是第一天竟然遇到冰雹了,說來也搞笑西雅圖的云只需要一小塊就呼風(fēng)喚雨,這一塊云下完了雨也就沒了,加上空氣干凈緯度較高變成了冰雹,而且猛下冰雹的時(shí)候旁邊還有太陽,正所謂冰火兩重天也。

                于是今天就跑去Microsoft里面的Company Store參觀了一下XBox,里面展覽Windows 7的幾臺(tái)電腦儼然被同志們處理成了網(wǎng)吧。星期一就要去上班了,這幾天寫寫代碼,倒倒時(shí)差,吃吃神奇的東西,睡睡覺,兩天就過去了。
            posted @ 2010-04-09 21:31 陳梓瀚(vczh) 閱讀(4957) | 評(píng)論 (14)編輯 收藏
                因?yàn)轫?xiàng)目原因去美帝出差,codeplex的速度估計(jì)就下降了……之前剛剛把NativeX寫完,但是還剩下最后一個(gè)接口沒在Language Provider上實(shí)現(xiàn),因此還有一些Test Case沒寫完。現(xiàn)在把一個(gè)NativeX編譯完之后,可以從LanguageAssembly上面反射出NativeX所有的接口。于是在這個(gè)基礎(chǔ)之上就可以做ABI了。

                整個(gè)項(xiàng)目的大方向是將本地語言、托管強(qiáng)類型語言和托管動(dòng)態(tài)語言有機(jī)的結(jié)合在一起,因此采取的路線是動(dòng)態(tài)語言編譯成托管語言,然后再編譯成本地語言,在之后編譯成指令集,就可以用虛擬機(jī)執(zhí)行了。指令集還可以做JIT,最終讓CPU直接執(zhí)行x86的代碼。

                在美帝一兩天安頓好之后,將會(huì)做完第一個(gè)Language Provider對(duì)NativeX的支持,然后優(yōu)化parser combinator和regular expression lexer,再補(bǔ)充好文檔,然后發(fā)布第一個(gè)alpha preview binary。當(dāng)然這個(gè)alpha preview binary距離項(xiàng)目的目標(biāo)是相當(dāng)遠(yuǎn)的,只是做一下將這一整套東西變成dll的試驗(yàn)。
            posted @ 2010-04-08 08:16 陳梓瀚(vczh) 閱讀(2547) | 評(píng)論 (6)編輯 收藏
                啊,人生就是不斷地重寫同一個(gè)程序。大家看我的博客估計(jì)會(huì)發(fā)現(xiàn)我總是不斷的徘徊在編譯器和界面庫上。這估計(jì)是由于一直以來我總是不能把這兩個(gè)東西做到我滿意的地步,然后就不斷地重寫,不斷地重構(gòu),重構(gòu)到不行了再重寫。這篇文章講了我之前做編譯器的道路,而這里、這里(這篇文章有一小部分是同學(xué)寫我的)和這里可以看成是我做界面庫的道路。

                我學(xué)習(xí)編程是從VB拖控件開始的,后來又經(jīng)歷了Delphi,現(xiàn)在工作又用C#,所以平時(shí)自己寫什么想堅(jiān)持C++就變成了一件很糾結(jié)的事情。我覺得語言是一種信仰,C++用的爽當(dāng)然希望他無所不能(其實(shí)本來也是。嗯……這句話應(yīng)該也是信仰)。結(jié)果MFC又封裝的太難用,其他的界面庫基本上都沒編輯器,現(xiàn)在C#還有WPF、Silverlight,Adobe還有Flash或者Flex,那C++有啥?啥都沒有。但是我還是想用C++寫漂亮的demo啊,怎么辦呢,沒有只好自己操刀。

                話說回來,我從來就不指望我寫的界面庫能夠充當(dāng)WPF和Flex這種分量的,只是我自己需要了,就寫一個(gè),反正閑著也是閑著,做了編譯器總要有一些看得見摸得著的demo的。

                第一次做界面庫倒并不是因?yàn)镃++的緣故,本來是給Delphi做游戲的。大一的時(shí)候想移植到C++上面,結(jié)果就用OpenGL做了一次。后來為了更方便我封裝了一下WinAPI(其實(shí)封裝比自己寫難TM多了)。只是Windows的界面標(biāo)準(zhǔn)過于高級(jí),API又過于靈活。我一直以為在菜單上加個(gè)圖標(biāo)是很容易的,到最后才明白VB也好Delphi也好.NET也好都偷偷替你OwnerDraw了。我一直以為按鈕可以放在一個(gè)GroupBox上的,結(jié)果竟然XP這個(gè)bug沒修,.NET偷偷在Button下面放了一個(gè)容器來繞過這個(gè)問題。我一直以為API提供了WS_TABSTOP的話就幫我們解決了Tab跳轉(zhuǎn)焦點(diǎn)的問題,結(jié)果WS_TABSTOP竟然是為了我們方便自己實(shí)現(xiàn)跳轉(zhuǎn)焦點(diǎn)而存在的。我一直以為模態(tài)窗口是一件很自然的事情,結(jié)果不僅沒支持(MessageBox那玩意兒倒支持了),實(shí)現(xiàn)起來還不容易。

                而且加上我自己也很喜歡RIA,于是就想著干脆總結(jié)之前的經(jīng)驗(yàn),自己做已做好了。搞定了之后還能給編譯器寫個(gè)demo還是寫個(gè)破IDE什么的,湊合著估計(jì)還有點(diǎn)用。

                目前還沒有一個(gè)非常詳細(xì)的計(jì)劃,只是在項(xiàng)目開了一個(gè)子文件夾做做實(shí)驗(yàn)。由于觀察到界面庫的Logic Tree和Visual Tree一般來說有明顯結(jié)構(gòu)上的區(qū)別,繪圖API可以置換,Hit Test跟Control Style應(yīng)該捆綁等現(xiàn)象,打算基于這種想法來實(shí)踐實(shí)踐:

                1:使用Windows API創(chuàng)建窗口和GDI對(duì)象,再給出一個(gè)跟平臺(tái)無關(guān)的接口來屏蔽這個(gè)實(shí)現(xiàn)。其實(shí)我不想跨平臺(tái)的,只是這樣工程管理起來比較有效罷了。

                2:將控件切割為狀態(tài)、顯示位置、風(fēng)格和排版四個(gè)部分。其中控件自己有自己的結(jié)構(gòu),排版則將控件結(jié)構(gòu)(Logic Tree)映射到一個(gè)顯示結(jié)構(gòu)(Visual Tree)并自動(dòng)維護(hù),顯示位置是用于提供顯示結(jié)構(gòu)功能的一個(gè)框架,風(fēng)格則負(fù)責(zé)繪制以及做Hit Test從而將用戶輸入的系統(tǒng)消息轉(zhuǎn)換成邏輯消息(譬如說點(diǎn)擊某個(gè)位置 => 點(diǎn)擊滾動(dòng)條的向上按鈕)。

                3:提供一套Control Combinator。Windows那套嚴(yán)格來說不是Combinator,憑什么ListView的條目不能是我自己寫的控件呢?顯然這是他不是Combinator的一個(gè)明顯的證據(jù)。Combinator就好比我們寫代碼,各種不同的語句組合在一起還是語句,不同的表達(dá)式組合在一起還是表達(dá)式,而且還有把表達(dá)式和語句互相轉(zhuǎn)換的唯一方法,這就是Combinator了。換個(gè)角度來說,這跟WPF的那么多種ItemTemplate還是什么破Template應(yīng)該是一回事。

                4:為每一種Control提供一個(gè)默認(rèn)的風(fēng)格。其實(shí)這個(gè)是必須做的,不然連用都沒法用。

                5:讓控件渲染用的技術(shù)、控件風(fēng)格和控件排版功能可以獨(dú)立變化。這是所有RIA的共同特征。

                其實(shí)也就這么多了。如果試驗(yàn)成功的話這個(gè)庫將會(huì)被合并進(jìn)Vczh Library++3.0
            posted @ 2010-03-28 08:39 陳梓瀚(vczh) 閱讀(4150) | 評(píng)論 (9)編輯 收藏
                其實(shí)Vczh Library++3.0提供的parser combinator并不能大量減少語法分析器的代碼量,其實(shí)真正降低的是語法分析器的復(fù)雜程度。當(dāng)你想比較快速的完成一個(gè)功能的時(shí)候,有兩種代碼量差不多的設(shè)計(jì),一種實(shí)現(xiàn)起來比較難并且調(diào)試起來很慘,一種實(shí)現(xiàn)起來比較簡(jiǎn)單而且基本不用怎么調(diào)試,那相對(duì)來說肯定會(huì)選擇后一種方法了。除非你純粹是想獲得鍛煉。

                使用parser combinator開發(fā)語法分析器的時(shí)候,你可以直接往C++里面寫EBNF語法,當(dāng)然語法的具體形式因?yàn)槭艿紺++語言本身的限制我做了一點(diǎn)點(diǎn)修改,譬如說A*和A+只好寫成*A和+A,A B只好寫成A + B、A>>B或者A<<B了。空明流產(chǎn)跟我抱怨說boost::spirit編譯速度奇慢(據(jù)說要一個(gè)多小時(shí),不知道是不是他機(jī)器太爛……)而且容易出現(xiàn)C1060 compiler is out of heap space的錯(cuò)誤,相比之下我在用我自己開發(fā)的parser combinator的時(shí)候,我一個(gè)充滿語法的cpp文件只需要一秒多一點(diǎn)(Thinkpad R61i, Vista Home Basic, 3G內(nèi)存),而且不會(huì)出現(xiàn)C1060這種離譜的錯(cuò)誤。至少?gòu)倪@個(gè)程度上來說,開發(fā)boost::spirit的人應(yīng)該是有很大的C++潔癖癥,才會(huì)把好好地一個(gè)parser combinator折騰成那個(gè)樣子。

                我是用的文法模型是帶類型修飾的文法,從文法的類型只能看出文法最終給你什么數(shù)據(jù),而不是文法他本身是怎么寫的。Vczh Library++2.0的parser combinator采用了后面一中的做法,據(jù)說boost::spirit也是這么做的,不過奇怪的是舊的parser combinator也沒出現(xiàn)那兩種錯(cuò)誤,取而代之是VC++經(jīng)常抱怨我一個(gè)表達(dá)式的類型簽名超過了4000個(gè)字符(囧)。于是Vczh Library++3.0的parser combinator做了一點(diǎn)修改。

                假設(shè)你一條文法A的結(jié)果是node<input, type>,第二條文法B的結(jié)果是node<input, string>,那么A+B的結(jié)果就是node<input, pair<type, string>>。這是什么意義呢?我們看表達(dá)文法type name semicolon的意思,大概可以理解為他可以接受“int a;”的這種語句。首先由于C++的限制我們替換成type + name + semicolon,其次由于那個(gè)semicolon,也就是分號(hào),其實(shí)僅僅是語法的要求而不是語法樹的一個(gè)必須成分,因此改成type + name << semicolon。這樣的話,這個(gè)文法依舊會(huì)要求輸入的字符串分別是一個(gè)類型、一個(gè)名字和一個(gè)分號(hào),但是返回的結(jié)果就自動(dòng)把分號(hào)給忽略掉了。那么我們?nèi)绾伪硎疽粋€(gè)同時(shí)包含type和name的類型呢?因?yàn)槲姆ú豢赡芴婺銊?chuàng)建一個(gè)struct,所以就定義了一個(gè)泛型的pair來表達(dá)。于是type + name << semicolon的結(jié)果類型就是node<input, pair<type, string>>了。這里input代表輸入的記號(hào)列表的類型。

                上面是新的parser combinator的做法,舊的parser combinator(據(jù)說也是boost::spirit的做法)的類型表示方法比較BT:當(dāng)你有文法type : node<input, type>,string : node<input, string>和semicolon : node<input, token>的話,那么type + name << semicolon的類型會(huì)變成:
            1 discard_right<input, sequence<input, node<input, type>, node<input, string>>, node<input, token>>
                寫成這樣大概就可以理解什么是“文法他本身是怎么寫的”了吧。

                舊的parser combinator的好處是C++為每一個(gè)文法生成了一個(gè)類型,雖然代碼會(huì)膨脹一點(diǎn)但是執(zhí)行過程會(huì)很快,只不過缺點(diǎn)比較多。第一個(gè)當(dāng)然是類型太多VC++編譯器會(huì)崩潰(C1060 compiler is out of heap space),第二個(gè)是編譯時(shí)間過長(zhǎng),第三個(gè)是當(dāng)你的文法比較長(zhǎng)的時(shí)候,類型簽名可能會(huì)超過VC++給你的限制,然后就會(huì)出現(xiàn)奇怪的問題。所以我在Vczh Library++3.0的parser combinator就是用了一個(gè)新的做法,也就是僅保留文法的結(jié)果類型,所以也就不得不引入虛函數(shù)了。因?yàn)橐粋€(gè)文法node<input, type>有非常多種組合可能,在結(jié)構(gòu)上沒辦法表現(xiàn)出來,所以必須使用虛函數(shù)。

                在聽了空明流產(chǎn)的抱怨之后,我去搜了一下使用boost::spirit的人的反應(yīng),好像都是遇到了那兩個(gè)嚴(yán)重的問題。幸好我喜歡造車輪,不然的話也許也會(huì)深陷地獄了。不過boost::spirit還是提供了解決辦法的,就是把你的長(zhǎng)的文法拆開成短的。寫過編譯器的人都會(huì)知道,這么做的嚴(yán)重后果就是你的分析器變成一團(tuán)亂麻,根本不知道自己在寫什么,不僅不可能有我上一篇文章描寫的優(yōu)美結(jié)果,更不可能把NativeX的分析器寫成下面這個(gè)樣子了:
             1                     primitive        = TRUE[ToTrue] | FALSE[ToFalse]
             2                                     | ACHAR[ToAChar] | WCHAR[ToWChar]
             3                                     | ASTRING[ToAString] | WSTRING[ToWString]
             4                                     | FLOAT[ToFloat] | DOUBLE[ToDouble]
             5                                     | NULL_VALUE[ToNull]
             6                                     | INTEGER[ToInteger]
             7                                     ;
             8                     reference        = ID[ToReference];
             9 
            10                     exp0            = primitive
            11                                     | reference
            12                                     | RESULT[ToResult]
            13                                     | (CAST + (LT >> type << GT) + (OPEN_BRACE >> exp << CLOSE_BRACE))[ToCastExpression]
            14                                     ;
            15                     exp1            = lrec(exp0 +  *(
            16                                                     (OPEN_ARRAY + exp0 << CLOSE_ARRAY)
            17                                                     | (OPEN_BRACE + list(opt(exp + *(COMMA >> exp)))[UpgradeArguments] << CLOSE_BRACE)
            18                                                     | ((DOT | POINTER) + reference)
            19                                                     | (INCREASE | DECREASE)[UpgradePostfix]
            20                                                     ), ToPostUnary);
            21                     exp2            = exp1 | ((INCREASE | DECREASE | BIT_AND | MUL | SUB | BIT_NOT | NOT) + exp1)[ToPreUnary];
            22                     exp3            = lrec(exp2 + *((MUL | DIV | MOD) + exp2), ToBinary);
            23                     exp4            = lrec(exp3 + *((ADD | SUB) + exp3), ToBinary);
            24                     exp5            = lrec(exp4 + *((SHL | SHR) + exp4), ToBinary);
            25                     exp6            = lrec(exp5 + *((LT | GT | LE | GE) + exp5), ToBinary);
            26                     exp7            = lrec(exp6 + *((EQ | NE) + exp6), ToBinary);
            27                     exp8            = lrec(exp7 + *(BIT_AND + exp7), ToBinary);
            28                     exp9            = lrec(exp8 + *(XOR + exp8), ToBinary);
            29                     exp10            = lrec(exp9 + *(BIT_OR + exp9), ToBinary);
            30                     exp11            = lrec(exp10 + *(AND + exp10), ToBinary);
            31                     exp12            = lrec(exp11 + *(OR + exp11), ToBinary);
            32                     exp                = lrec(exp12 + *((OP_ASSIGN | ASSIGN) + exp12), ToBinary);
            33 
            34                     primType        = (FUNCTION + type + (OPEN_BRACE >> list(opt(type + *(COMMA >> type))) << CLOSE_BRACE))[ToFunctionType]
            35                                     | (PRIM_TYPE | ID)[ToNamedType]
            36                                     ;
            37                     type            = lrec(primType + *(MUL | (OPEN_ARRAY >> INTEGER << CLOSE_ARRAY)), ToDecoratedType);
            38 
            39                     statement        = SEMICOLON[ToEmptyStat]
            40                                     | (exp + SEMICOLON)[ToExprStat]
            41                                     | (VARIABLE + type + ID + opt(ASSIGN >> exp) << SEMICOLON)[ToVarStat]
            42                                     | (IF + (OPEN_BRACE >> exp << CLOSE_BRACE) + statement + opt(ELSE >> statement))[ToIfStat]
            43                                     | (BREAK << SEMICOLON)[ToBreakStat]
            44                                     | (CONTINUE << SEMICOLON)[ToContinueStat]
            45                                     | (EXIT << SEMICOLON)[ToReturnStat]
            46                                     | (OPEN_STAT + list(*statement) << CLOSE_STAT)[ToCompositeStat]
            47                                     | (DO + statement + (WHILE >> OPEN_BRACE >> exp << CLOSE_BRACE << SEMICOLON))[ToDoWhileStat]
            48                                     | (LOOP + statement)[ToLoopStat]
            49                                     | (WHILE + (OPEN_BRACE >> exp << CLOSE_BRACE) + statement + opt(WHEN >> OPEN_BRACE >> exp << CLOSE_BRACE << SEMICOLON))[ToWhileStat]
            50                                     | (FOR + list(*statement) + (WHEN >> OPEN_BRACE >> exp << CLOSE_BRACE) + (WITH >> list(*statement)) + (DO >> statement))[ToForStat]
            51                                     ;
            52 
            53                     declaration        = (VARIABLE + type + ID + opt(ASSIGN >> exp) << SEMICOLON)[ToVarDecl]
            54                                     | (TYPE + ID + (ASSIGN >> type) << SEMICOLON)[ToTypedefDecl]
            55                                     | (STRUCTURE + ID << SEMICOLON)[ToStructPreDecl]
            56                                     | (STRUCTURE + ID + (OPEN_STAT >> *(type + ID << SEMICOLON) << CLOSE_STAT))[ToStructDecl]
            57                                     | (FUNCTION + type + ID + (OPEN_BRACE >> plist(opt((type + ID) + *(COMMA >> (type + ID)))) << CLOSE_BRACE) + statement)[ToFuncDecl]
            58                                     ;
            59 
            60                     unit            = ((UNIT >> ID << SEMICOLON) + list(opt(USES >> (ID + *(COMMA >> ID)) << SEMICOLON)) + list(*declaration))[ToUnit];

                啊,簡(jiǎn)直就跟EBNF沒什么區(qū)別啊。

                當(dāng)前的進(jìn)度可以在Vczh Library++3.0的頁面上看到。
            posted @ 2010-03-20 23:49 陳梓瀚(vczh) 閱讀(4565) | 評(píng)論 (2)編輯 收藏
                昨天剛剛完備了Vczh Library++3.0對(duì)于基本的分析器的支持。分析器主要有兩部分組成,第一部分是語法分析器。這個(gè)分析器通過程序員直接寫在C++里面的語法來分析一個(gè)輸入。第二部分是詞法分析器,這是通過實(shí)現(xiàn)一個(gè)正則表達(dá)式引擎來完成的。為了讓分析其更加靈活,分析器使用模板來寫,輸入只要滿足一定的接口就可以了。Vczh Library++3.0內(nèi)置字符串輸入、迭代器輸入和列表輸入。于是在單元測(cè)試?yán)锩婢蛯懥藘啥未a來分析一個(gè)四則運(yùn)算式子,第一個(gè)沒有詞法分析器,第二個(gè)用了詞法分析器。

                我們先來分析一下四則運(yùn)算式子的一般做法??偟膩碚f我們會(huì)先寫一個(gè)不包含左遞歸的文法:
            1 FACTOR = NUM | ( EXP )
            2 TERM = FACTOR (MUL FACTOR)*
            3 EXP = TERM (ADD TERM)*

                從EXP開始,我們就可以將一個(gè)四則運(yùn)算式子的結(jié)構(gòu)表達(dá)出來了。我們先用Vczh Library++3.0提供的庫,通過直接分析輸入的字符串來拆解一個(gè)四則運(yùn)算式子并將其結(jié)果計(jì)算出來:

                首先我們需要一個(gè)函數(shù),這個(gè)函數(shù)輸入兩個(gè)整數(shù)和一個(gè)字符(代表操作符)來計(jì)算出結(jié)果。當(dāng)然因?yàn)榉治銎浔旧淼囊?,操作符和右操作?shù)是一個(gè)pair:
             1 int cal(const int& first, const ParsingPair<wchar_t, int>& second)
             2 {
             3     int result=first;
             4     switch(second.First())
             5     {
             6         case L'+':
             7             result+=second.Second();
             8             break;
             9         case L'-':
            10             result-=second.Second();
            11             break;
            12         case L'*':
            13             result*=second.Second();
            14             break;
            15         case L'/':
            16             result/=second.Second();
            17             break;
            18     }
            19     return result;
            20 }

                接下來,我們要在C++里面寫一個(gè)文法:
            1     typedef Rule<StringInput<wchar_t>int> _Rule;
            2     typedef Node<StringInput<wchar_t>int> _Node;
            3 
            4     _Rule FACTOR, TERM, EXP;
            5     FACTOR = rgx(L"/d+")[wtoi] | (ch(L'(')>>EXP<<ch(L')'));
            6     TERM = lrec(FACTOR + *(chs(L"*/"+ FACTOR), cal);
            7     EXP = lrec(TERM + *(chs(L"+-"+ TERM), cal);

                是不是比較直觀捏?,F(xiàn)在來解釋一下里面的內(nèi)容。

                首先對(duì)于FACTOR = rgx(L"/d+")[wtoi] | (ch(L'(') >> EXP << ch(L')'));,我們知道這其實(shí)就是FACTOR = NUM | ( EXP )。這里rgx是一個(gè)正則表達(dá)式的輸入檢查,如果輸入的字符串是整數(shù)那么就走左邊的,如果輸入的第一個(gè)字符是括號(hào)就走右邊的。a >> b << c的意思是輸入必須是先a后b后c,然后拋棄a和c的分析結(jié)果只留下b。這個(gè)很好理解,分析完我們只需要那個(gè)EXP不需要兩個(gè)括號(hào)的。a[b]的意思是把a(bǔ)的分析結(jié)果用b函數(shù)轉(zhuǎn)換一下。rgx的結(jié)果自然是一個(gè)字符串,會(huì)告訴你輸入的整數(shù)究竟是什么,然后通過函數(shù)wtoi將字符串轉(zhuǎn)換成整數(shù)。

                剩下兩條都比較像。我們知道左遞歸的寫法是:TERM = FACTOR | TERM MUL FACTOR,因?yàn)槲业姆治銎鞑荒苤苯又С肿筮f歸,所以需要一個(gè)變換:TERM = FACTOR (MUL FACTOR)*,但是這樣計(jì)算函數(shù)寫起來會(huì)很麻煩,所以我提供了一個(gè)lrec組合子將非左遞歸的模式在計(jì)算完成之后,重新轉(zhuǎn)成左遞歸,然后調(diào)用那個(gè)cal函數(shù)。因此cal函數(shù)才需要三個(gè)參數(shù),如果不用lrec的話,cal將是一個(gè)整數(shù),還有一個(gè)操作符和整數(shù)的列表,寫起來比較麻煩。

                最后就剩下分析了:
             1     {
             2         Types<StringInput<wchar_t>>::GlobalInfo info;
             3         StringInput<wchar_t> input=L"(1+2)*(3+4)";
             4         ParsingResult<int> result=EXP.Parse(input, info);
             5         TEST_ASSERT(result);
             6         TEST_ASSERT(result.Value()==21);
             7         TEST_ASSERT(info.errors.Count()==0);
             8     }
             9     {
            10         TEST_ASSERT(EXP.Parse(L"(10+20)*(30+40)"false)==2100);
            11     }

                Vczh Library++3.0提供了兩種分析方法,分別對(duì)于需要知道詳細(xì)的錯(cuò)誤信息和不需要知道詳細(xì)的錯(cuò)誤信息來做。如果程序員可以假定輸入正確,或者說不需要報(bào)告那么詳細(xì)的輸入錯(cuò)誤信息的話,使用第二種方法就行了,一行代碼搞定。

                那么接下來看第二種。第二種我們走傳統(tǒng)道路,先詞法分析后語法分析。詞法分析把輸入分成了5種記號(hào),分別是整數(shù)、左括號(hào)、右括號(hào)、加法和乘法,用正則表達(dá)式來描述:
             1     typedef Rule<TokenInput<RegexToken>int> _Rule;
             2     typedef Node<TokenInput<RegexToken>, RegexToken> _Node;
             3 
             4     List<WString> tokens;
             5     tokens.Add(L"/d+");
             6     tokens.Add(L"/(");
             7     tokens.Add(L"/)");
             8     tokens.Add(L"/+|-");
             9     tokens.Add(L"/*|//");
            10 
            11     RegexLexer lexer(tokens.Wrap());
            12 
            13     _Node NUM=tk(0);
            14     _Node OPEN=tk(1);
            15     _Node CLOSE=tk(2);
            16     _Node ADD=tk(3);
            17     _Node MUL=tk(4);

                因此我們的cal函數(shù)就要變一變了,同時(shí)還要提供一個(gè)tval函數(shù)將RegexLexer分析出的一個(gè)記號(hào)RegexToken轉(zhuǎn)成整數(shù):
             1 int tcal(const int& first, const ParsingPair<RegexToken, int>& second)
             2 {
             3     int result=first;
             4     int value=second.Second();
             5     switch(*second.First().reading)
             6     {
             7         case L'+':
             8             result+=value;
             9             break;
            10         case L'-':
            11             result-=value;
            12             break;
            13         case L'*':
            14             result*=value;
            15             break;
            16         case L'/':
            17             result/=value;
            18             break;
            19     }
            20     return result;
            21 }
            22 
            23 int tval(const RegexToken& input)
            24 {
            25     return wtoi(WString(input.reading, input.length));
            26 }

                至此剩下的都差不多了。我相信看懂了第一種做法的人可以直接看懂第二種做法,因?yàn)橹皇菗Q了一個(gè)輸入類型而已,剩下的內(nèi)容都是一樣的:
             1     _Rule FACTOR, TERM, EXP;
             2     FACTOR = NUM[tval] | (OPEN >> EXP << CLOSE);
             3     TERM = lrec(FACTOR + *(MUL + FACTOR), tcal);
             4     EXP = lrec(TERM + *(ADD + FACTOR), tcal);
             5 
             6     {
             7         WString code=L"(1+2)*(3+4)";
             8         List<RegexToken> tokens;
             9         CopyFrom(tokens.Wrap(), lexer.Parse(code));
            10         Types<TokenInput<RegexToken>>::GlobalInfo info;
            11         TokenInput<RegexToken> input(&tokens[0], tokens.Count());
            12         ParsingResult<int> result=EXP.Parse(input, info);
            13         TEST_ASSERT(result);
            14         TEST_ASSERT(result.Value()==21);
            15         TEST_ASSERT(info.errors.Count()==0);
            16     }
            17     {
            18         WString code=L"(10+20)*(30+40)";
            19         List<RegexToken> tokens;
            20         CopyFrom(tokens.Wrap(), lexer.Parse(code));
            21         TokenInput<RegexToken> input(&tokens[0], tokens.Count());
            22         TEST_ASSERT(EXP.Parse(input, false)==2100);
            23     }

                這里需要注意的是由于lexer.Parse返回的記號(hào)里面只保存了wchar_t*,所以變量code有必要存活下來,不然那個(gè)指針就會(huì)被釋放掉。此法的過程還是比較麻煩的,要多寫四行。這里并沒有過濾空格,如果需要過濾空格的話,用一下linq:

            1 bool DiscardBlank(const RegexToken& token)
            2 {
            3   //如果token有定義返回true,空格沒有定義會(huì)返回false。
            4   return token.token!=-1;
            5 }

                然后把CopyFrom那行改成:CopyFrom(tokens.Wrap(), lexer.Parser(code)>>Where(DiscardBlank));就完了。linq萬歲!

                至此兩種方法就介紹完了,上面的測(cè)試代碼和分析器的源代碼都可以在Vczh Library++3.0找到。
            posted @ 2010-03-06 21:02 陳梓瀚(vczh) 閱讀(2633) | 評(píng)論 (2)編輯 收藏
            僅列出標(biāo)題
            共35頁: First 11 12 13 14 15 16 17 18 19 Last 
            亚洲综合伊人久久大杳蕉| 久久精品一区二区国产| 亚洲人成无码网站久久99热国产| 久久精品三级视频| 中文字幕无码精品亚洲资源网久久| 久久人妻少妇嫩草AV无码专区| 中文字幕一区二区三区久久网站| 热RE99久久精品国产66热| 亚洲av伊人久久综合密臀性色 | 国内精品久久久久影院优| 久久亚洲国产精品一区二区| 欧美日韩中文字幕久久久不卡| 久久人人爽人人爽人人片AV麻烦| 97热久久免费频精品99| 久久人人爽人人爽人人片AV麻豆| 久久久久99精品成人片试看| 久久国产香蕉视频| 狠狠色婷婷久久一区二区三区| 香蕉久久AⅤ一区二区三区| 狠狠色丁香婷综合久久| 久久婷婷国产剧情内射白浆| 久久久久国产精品三级网| 国产婷婷成人久久Av免费高清 | 东方aⅴ免费观看久久av| 久久综合丁香激情久久| 久久人人爽人人爽人人爽| 久久精品国产99久久久香蕉| 狠狠久久亚洲欧美专区| 久久香蕉超碰97国产精品| 久久精品国产日本波多野结衣 | 丁香五月网久久综合| 香蕉久久夜色精品升级完成| 伊人久久五月天| 少妇久久久久久被弄到高潮| 韩国三级中文字幕hd久久精品 | 久久99国产精品久久99| 久久亚洲国产成人精品性色| 天堂久久天堂AV色综合| 久久丫精品国产亚洲av不卡| 久久天天躁狠狠躁夜夜网站| 精产国品久久一二三产区区别 |