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打开后,


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



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值