這幾天在調研各種.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打開後,
了解lex&yacc的人,一下就看明白了。
但現在還不明白,為什麼在lex檔案中,無法打斷點。
我們在yacc中下斷。
然後,F5啟動調試。
這裡,有可能會報錯。
因為這裡:
如果裝在d:盤,你要更正一下。
啟動後:
是一個空白的vs,我就不附圖了。
然後,我們打開示例:
D:\Program Files (x86)\Microsoft Visual Studio 2008 SDK\VisualStudioIntegration\Samples\IDE\CSharp\Example.ManagedMyC\testfiles
然後,我們第一次等到斷點:
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 中基實也一樣。
結果,你看現,
程式一上電,程度就先掃了一次yacc.
那麼意味着,我相跟哪個分支,要在這次預找之後,才能下斷。
我搞不清楚,微軟這些家夥,腦子裡是不是進水了。這本是可以通過預編譯來解決的。
我是很讨厭理論一講一大圖錄,做起事來,就TM圖錄反賬。
算了,反正也不打算用了,接着寫這個記錄吧。
Ctrl+shift+F去了所有斷點,跳出來
回頭去下斷,NND。
停下後,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)); 看到了移進的初始化
}
%}
然後到這裡,終于看到點東西了
時刻看local窗:
這裡我們看到了前面我強調的目前掃到哪一行了。做得還挺怪,有個startline,還有endline,這個其實讓人費解。
說明,這兩個變量,可能是微軟自定義的,專用于yacc的,不是lex的初始line.
關鍵yytext 和yyval哪裡去了?
以後再說吧。
解析器難調的地方在于,每次調試,好象都不一樣。
先看看我們的成果:
感興趣的同仁,接着往下找吧。
我得開工了。不能一直在這寫總結。
另外,vs 2010和vs2013中,還沒找到這個體系。有知道的同仁,請告知。