【转】Vczh Library++3.0之可配置语法分析器(分析器内核)

从网上无意间看到这个系列的文章,作者非常有想法,转下来慢慢研究,好好学习。   祝大家学习愉快,做自己的爱好 ^_^ !

上一篇文章中,我们已经实现了在C++中直接写文法并转换成文法树的工作,现在要开始实现三个组合文法了。

    文法树的定义是递归的,所以我们的文法树在分析输入的时候也是递归的。每个文法树都由更小的文法树组成,因此每一个文法树在分析输入的时候都需要调用更小的文法树的分析过程。因为连接、分支和循环都很像,因此下面我的描述性文字会少一些。

    下面是连接的代码:

 1          template < typename I, typename O1, typename O2 >
 2           class  _Seq :  public  Combinator < I, ParsingPair < O1, O2 >>
 3          {
 4           protected :
 5              typename Combinator < I, O1 > ::Ref        first;
 6              typename Combinator < I, O2 > ::Ref        second;
 7           public :
 8              _Seq( const  typename Combinator < I, O1 > ::Ref &  _first,  const  typename Combinator < I, O2 > ::Ref &  _second)
 9                  :first(_first)
10                  ,second(_second)
11              {
12              }
13 
14              ParsingResult < OutputType >  Parse(InputType &  input, GlobalInfoType &  globalInfo) const
15              {
16                   if (ParsingResult < O1 >  firstResult = first -> Parse(input, globalInfo))
17                  {
18                       if (ParsingResult < O2 >  secondResult = second -> Parse(input, globalInfo))
19                      {
20                           return  ParsingResult < OutputType > (ParsingPair < O1, O2 > (firstResult.Value(), secondResult.Value()));
21                      }
22                  }
23                   return  ParsingResult < OutputType > ();
24              }
25          };


    让我们看Parse函数。首先用左子树(first)分析输入,如果成功则接着使用右子树进行分析并返回结构,否则返回空。这非常符合文法树的定义,两个输入的连接必须是两个输入以指定的顺序先后出现。因此对于A的合法输入i,B的合法输入j,A+B的合法输入就是ij。至于这里的globalInfo参数是用来存放错误信息的。

    接下来看分支:

 1          template < typename I, typename O >
 2           class  _Alt :  public  Combinator < I, O >
 3          {
 4           protected :
 5              typename Combinator < I, O > ::Ref        first;
 6              typename Combinator < I, O > ::Ref        second;
 7           public :
 8              _Alt( const  typename Combinator < I, O > ::Ref &  _first,  const  typename Combinator < I, O > ::Ref &  _second)
 9                  :first(_first)
10                  ,second(_second)
11              {
12              }
13 
14              ParsingResult < OutputType >  Parse(InputType &  input, GlobalInfoType &  globalInfo) const
15              {
16                  GlobalInfoType firstInfo;
17                  InputType firstInput = input;
18                   if (ParsingResult < OutputType >  firstResult = first -> Parse(firstInput, firstInfo))
19                  {
20                      input = firstInput;
21                      globalInfo.Append(firstInfo);
22                       return  firstResult;
23                  }
24 
25                  GlobalInfoType secondInfo;
26                  InputType secondInput = input;
27                   if (ParsingResult < OutputType >  secondResult = second -> Parse(secondInput, secondInfo))
28                  {
29                      input = secondInput;
30                      globalInfo.Append(secondInfo);
31                       return  secondResult;
32                  }
33 
34                   if (firstInput >= secondInput)
35                  {
36                      input = firstInput;
37                      globalInfo.Append(firstInfo);
38                  }
39                   else
40                  {
41                      input = secondInput;
42                      globalInfo.Append(secondInfo);
43                  }
44                   return  ParsingResult < OutputType > ();
45              }
46          };


    分支比较复杂。因为左右只要有一个成立了就代表输入成立,因此在输入的时候我们需要先将输入的状态保存下来,然后才开始左子树的分析。成功了就直接返回,失败了就使用右子树进行分析。如果都失败了,那我们就要看看究竟谁出错的地方比较晚,就返回谁的错误。每个函数在Parse的时候,都会在输入发生错误的时候停下来,这个时候不会进行输入的恢复,因为这个位置代表了第一个出错的位置。

    最后是循环:

 1          template < typename I, typename O >
 2           class  _Loop :  public  Combinator < I, ParsingList < O >>
 3          {
 4           protected :
 5              typename Combinator < I, O > ::Ref        element;
 6               int                                     min;
 7               int                                     max;
 8           public :
 9              _Loop( const  typename Combinator < I, O > ::Ref _element,  int  _min,  int  _max =- 1 )
10                  :element(_element)
11                  ,min(_min)
12                  ,max(_max)
13              {
14              }
15 
16              ParsingResult < OutputType >  Parse(InputType &  input, GlobalInfoType &  globalInfo) const
17              {
18                  ParsingList < O >  result;
19                  InputType elementInput = input;
20                  GlobalInfoType elementInfo;
21 
22                   while (max ==- 1   ||  result.Count() < max)
23                  {
24                      ParsingResult < O >  elementResult = element -> Parse(elementInput, elementInfo);
25                       if (elementResult)
26                      {
27                          result.Append(elementResult.Value());
28                          input = elementInput;
29                          globalInfo.Append(elementInfo);
30                          elementInfo.errors.Clear();
31                      }
32                       else
33                      {
34                           break ;
35                      }
36                  }
37 
38                   if (result.Count() >= min)
39                  {
40                      globalInfo.CandidateAppend(elementInfo);
41                       return  ParsingResult < OutputType > (result);
42                  }
43                   else
44                  {
45                      input = elementInput;
46                      globalInfo.Append(elementInfo);
47                       return  ParsingResult < OutputType > ();
48                  }
49              }
50          };


    代码比较长主要是考虑到分析可以指定下界和上界,也就是最少循环次数和最大循环次数。如果上限是-1则表示没有上限。于是在这里要将最后一次输入的错误保存到另一个地方,如果是在循环不下去了,就要看循环的次数是否在允许的范围内。不允许就要记录错误了,

    其实文法有这三种元素是远远不够的,因为这三种元素都是组合元素,最根本的是我们要判断输入的一个记号(或者字符)是否满足某个条件,满足则吃掉并往下走。这种操作叫Unit:

 1          template < typename I >
 2           class  _Unit :  public  Combinator < I, typename I::ElementType >
 3          {
 4              typedef typename I::ElementType        ElementType;
 5           protected :
 6              ElementType            baseline;
 7              WString                errorMessage;
 8           public :
 9              _Unit( const  ElementType &  _baseline,  const  WString _errorMessage = WString::Empty)
10                  :baseline(_baseline)
11                  ,errorMessage(_errorMessage)
12              {
13              }
14 
15              ParsingResult < OutputType >  Parse(InputType &  input, GlobalInfoType &  globalInfo) const
16              {
17                   if (input.Available()  &&  input.Current() == baseline)
18                  {
19                      ParsingResult < OutputType >  result(input.Current());
20                      input.Next();
21                       return  result;
22                  }
23                   else
24                  {
25                      globalInfo.errors.Add( new  ErrorType(errorMessage, input));
26                       return  ParsingResult < OutputType > ();
27                  }
28              }
29          };


    这里的行为还是相对简单的,而且Unit也是现在展示的4个操作里面,唯一产生了一个具体的错误信息的文法元素。于是相似的我们可以有当一个函数返回true的时候才往下走,或者当分析出错的时候让一个函数来生成具体的错误信息或者假装成功往下走,或者当分析成功的时候再用一个函数来讲结果转换成一个目标语法树的节点。这种琐碎的操作就比较多了。这里已经把文法分析其介绍的差不多了,下一篇文章我们将以文法分析其作为例子,讲一下FpMacro的实现并放出可以编译的Demo。

转载于:https://www.cnblogs.com/xuangong/archive/2011/07/31/2122665.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值