这几天在调研各种.net下的lex&yacc方案。
现在看来,都不成熟。还是转向最有保障的基于C语言的lex&yacc.
但这里我记录一下,部分调研的过程。
经过调研,个人的看法,在c#中,最好的解决方案就是,vs 2008SDK中自带的。vs2010和vs2013的sdk装好后,没有找到,有了解的同仁请知会一下。
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中,还没找到这个体系。有知道的同仁,请告知。