天天看點

vs2008中的lex&yacc

這幾天在調研各種.net下的lex&yacc方案。

現在看來,都不成熟。還是轉向最有保障的基于C語言的lex&yacc.

但這裡我記錄一下,部分調研的過程。

經過調研,個人的看法,在c#中,最好的解決方案就是,vs 2008SDK中自帶的。vs2010和vs2013的sdk裝好後,沒有找到,有了解的同仁請知會一下。

vs2008的SDK中,有兩個EXE,一個是

MPLex.exe

MPPG.exe

D:\Program Files (x86)\Microsoft Visual Studio 2008 SDK\VisualStudioIntegration\Tools\Bin

我是裝在d盤。

例子在這裡:

D:\Program Files (x86)\Microsoft Visual Studio 2008 SDK\VisualStudioIntegration\Samples\IDE\CSharp\Example.ManagedMyC

首先裝2008sdk,我的2008是sp1的,是以下載下傳的時候,要下SDK SP1

http://download.microsoft.com/download/c/2/0/c20073e0-c842-44a8-a4e9-7dd5d289eafe/VsSDK_sfx.exe

裝好後,就能找到上述的檔案。

VS2008打開後,

vs2008中的lex&yacc

了解lex&yacc的人,一下就看明白了。

但現在還不明白,為什麼在lex檔案中,無法打斷點。

我們在yacc中下斷。

然後,F5啟動調試。

這裡,有可能會報錯。

因為這裡:

vs2008中的lex&yacc

如果裝在d:盤,你要更正一下。

啟動後:

是一個空白的vs,我就不附圖了。

然後,我們打開示例:

D:\Program Files (x86)\Microsoft Visual Studio 2008 SDK\VisualStudioIntegration\Samples\IDE\CSharp\Example.ManagedMyC\testfiles

vs2008中的lex&yacc

然後,我們第一次等到斷點:

vs2008中的lex&yacc

shift + f11 跳到外面,看看lex和yacc啟動的過程:

using System;

using System.Collections.Generic;

using Microsoft.VisualStudio;

using Microsoft.VisualStudio.TextManager.Interop;

using Microsoft.VisualStudio.Package;

namespace Babel

{

    public abstract class BabelLanguageService : Microsoft.VisualStudio.Package.LanguageService

    {

      。。。

        public override Microsoft.VisualStudio.Package.AuthoringScope ParseSource(ParseRequest req)

        {

            Source source = (Source) this.GetSource(req.FileName);

            bool yyparseResult = false;

            // req.DirtySpan seems to be set even though no changes have occurred

            // source.IsDirty also behaves strangely

            // might be possible to use source.ChangeCount to sync instead

            if (req.DirtySpan.iStartIndex != req.DirtySpan.iEndIndex

                || req.DirtySpan.iStartLine != req.DirtySpan.iEndLine)

            {

                Babel.Parser.ErrorHandler handler = new Babel.Parser.ErrorHandler();

                Babel.Lexer.Scanner scanner = new Babel.Lexer.Scanner(); // string interface

                Parser.Parser parser = new Parser.Parser();  // use noarg constructor

                parser.scanner = scanner;

                scanner.Handler = handler;

                parser.SetHandler(handler);

                scanner.SetSource(req.Text, 0);

                parser.MBWInit(req);

                yyparseResult = parser.Parse();

好的,我們找到這些最關鍵的資訊。

然後,在最後那句話那裡F11, step in ,這就不多了,你靜下心來,一步步分析。

而且,你要真的了解lex&yacc.

調試,lex&yacc,需要一些技巧,我記得很久以前,我寫過一個文章,但我現在也找不到了。

就是重點放在yacc上,時刻關注目前你走到第幾行,以及,時刻關注目前的 yyval的值。

但這微軟提供的這個什麼Bible示例,太過于簡單,yyval隻有一種類型,第一張圖我标出來了。

對于真正的高手來說(我可不是啊),他還會關注文法解析樹,目前的狀态,跟蹤每一個移進(Shift)過程。

但,這方面,我一直沒搞通。因為的确工作中,還沒碰到那種極其複雜的情況。

制作編譯器是很難的。而lex&yacc隻是編譯器的前端,但這已經很難了。

比方說吧,C語言本身,直到今天在設計語言和編譯器時的錯誤,我們一直沿用着。但你不能責難他們,我們去實作,隻可能錯比那更多。做編譯器的過程,是自己把坐在椅子上的自己擡起來的過程。

事實上,有些bug,是因為不得不那麼做,因為那時的編譯器本身還過于簡單。淩亂了,不要說下去了。總之,寫個簡單的,和實作一個複雜的完全不同。

回頭再看,微軟有些地方,還是有可取之處,說老實話,我真的不願意用難用的C語言去跟。

微軟的體系中,最重要的參數是這個:

ParseRequest req

我列出一部分内容:

+		Callback	{Method = {Void HandleParseResponse(Microsoft.VisualStudio.Package.ParseRequest)}}	Microsoft.VisualStudio.Package.ParseResultHandler
+		callback	{Method = {Void HandleParseResponse(Microsoft.VisualStudio.Package.ParseRequest)}}	Microsoft.VisualStudio.Package.ParseResultHandler
		col	0	int
		Col	0	int
+		dirtySpan	{Microsoft.VisualStudio.TextManager.Interop.TextSpan}	Microsoft.VisualStudio.TextManager.Interop.TextSpan
+		DirtySpan	{Microsoft.VisualStudio.TextManager.Interop.TextSpan}	Microsoft.VisualStudio.TextManager.Interop.TextSpan
		FileName	"E:\\work\\Parser\\MyCLanguageService\\MyCLanguageService\\Test Files\\short.myc"	string
		fileName	"E:\\work\\Parser\\MyCLanguageService\\MyCLanguageService\\Test Files\\short.myc"	string
           

另外,從這段來看,這個lex 和yacc,是可以與vs intergation環境分離的,這也是一個好消息:

Babel.Parser.ErrorHandler handler = new Babel.Parser.ErrorHandler();
                Babel.Lexer.Scanner scanner = new Babel.Lexer.Scanner(); // string interface
                Parser.Parser parser = new Parser.Parser();  // use noarg constructor

                parser.scanner = scanner;
                scanner.Handler = handler;
                parser.SetHandler(handler);
                scanner.SetSource(req.Text, 0);

                parser.MBWInit(req);
                yyparseResult = parser.Parse();
           

不過,再往下,問題來了,

我也不明白為什麼,C#的這個示例,把yacc解析了兩次。

這是一個本質性的錯誤!

因為,lex&yacc體系,對于開發者來說,最重要莫過于排錯,開發一門新語言,耗時費力。是以,調試環境,至關重要。

一般來說,調試者,都是一個文法,一個文法的調。是以,會一個巴氏範式,一個巴氏範式地往前跟。

是以,yacc中要下斷。lex 中基實也一樣。

結果,你看現,

vs2008中的lex&yacc

程式一上電,程度就先掃了一次yacc.

那麼意味着,我相跟哪個分支,要在這次預找之後,才能下斷。

我搞不清楚,微軟這些家夥,腦子裡是不是進水了。這本是可以通過預編譯來解決的。

我是很讨厭理論一講一大圖錄,做起事來,就TM圖錄反賬。

算了,反正也不打算用了,接着寫這個記錄吧。

Ctrl+shift+F去了所有斷點,跳出來

vs2008中的lex&yacc

回頭去下斷,NND。

vs2008中的lex&yacc

停下後,F11

很無聊。現在的确很無聊。

%{

    ErrorHandler handler = null;

    public void SetHandler(ErrorHandler hdlr) { handler = hdlr; }

    internal void CallHdlr(string msg, LexLocation val)

    {

        handler.AddError(msg, val.sLin, val.sCol, val.eCol - val.sCol);

    }

    internal TextSpan MkTSpan(LexLocation s) { return TextSpan(s.sLin, s.sCol, s.eLin, s.eCol); }

    internal void Match(LexLocation lh, LexLocation rh)

    {

        DefineMatch(MkTSpan(lh), MkTSpan(rh));   看到了移進的初始化

    }

%}

然後到這裡,終于看到點東西了

vs2008中的lex&yacc

時刻看local窗:

這裡我們看到了前面我強調的目前掃到哪一行了。做得還挺怪,有個startline,還有endline,這個其實讓人費解。

說明,這兩個變量,可能是微軟自定義的,專用于yacc的,不是lex的初始line.

關鍵yytext 和yyval哪裡去了?

以後再說吧。

解析器難調的地方在于,每次調試,好象都不一樣。

先看看我們的成果:

vs2008中的lex&yacc

感興趣的同仁,接着往下找吧。

我得開工了。不能一直在這寫總結。

另外,vs 2010和vs2013中,還沒找到這個體系。有知道的同仁,請告知。

繼續閱讀