天天看点

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中,还没找到这个体系。有知道的同仁,请告知。

继续阅读