xyfree
发表于 2010-03-13 11:00
回复 18# prolj
非常好的例子,不过首先要考虑几点:
1.你举的例子是C++的吧?那么对于C++来说,像a+++1这样的表达式是不是存在可以分割的点呢?
我可没说过任意代码在语义分析上都可以切割。不过呢,你给的例子,在词法分析上确实是可以切割的:a、 1,切割成这两个东西。
其实怎么切割是要根据语法规则,例如ANTLR的g文件,YACC的y文件来计算分割集,甚至就是语法规则里面指定哪些是可以分割的。
2. 语义问题。在C++里面,看来可能的理解是: (a++)+1。
但是,对于一个足够OO的语言来说,a + (++1) 这样的理解似乎也不是不合理的,如果把常量1也看做一个对象,并且拥有++自增操作,自增之后产生临时对象2。
还是归纳成上面那一点,要看语法定义允许怎样的切割。
我之所以提出从两端开始分析,并不是用来解决算术表达式上容易引起误解部分
而是试着解决,一个地方的代码语法出错了,如何才能不影响其他正确的代码的分析。
class ContainerBase <#typename Iter, typename T#> : {
typename Iter<#typename InnerType#> {## '{' 前漏了个冒号 ##
public func next(void):>ref:T;
};
private lockable_var size_:int;
## 正确的写法应该是 var iter:Iter<#InnerType=>T#> ##
private Iter<#InnerType=>T#> iter;
public func size(void):>var:int {
return
};
};
像上面这样的代码(我自己的编译器中遇到的)明明是两个错误,
但是基于LL的分析就很难正确指出这两个错误。
如果有办法应用切割的话,
在第4行的分号之前就有可能分析Iter概念的定义语法,这样就不会出现两个错误纠缠不清的问题了。
xyfree
发表于 2010-03-13 11:07
回复 20# prolj
嗯,这个是态度问题了。
既然LL(0) 太弱,LR(n) 太难承受
你可以选择两个做法
1. 回避问题,不理它。只用LL(k)或者LALR(1)。
2. 试着做一些小的适合自己的改进。
我可没想过像solidus那样搞了个通用SLR和LALR分析器,所以你跟我说我那个想法有多不行,那只能是对于通用的来说的吧?
但我只想解决我自己遇到的问题。
prolj
发表于 2010-03-13 11:57
本帖最后由 prolj 于 2010-03-13 12:32 编辑
呵呵,lz小心啊。MS的上一个图灵奖得主在太平洋失踪了,现在又出来一个,不知道这个会怎么样。
专用的parser我写多了,实在无聊。。。
cjaizss
发表于 2010-03-13 12:27
基于两端分析,我来试想一下lz的意思吧.
就是先找到两端,然后两个叠代器,一个从前向后规约,一个从后向前规约,是这个意思吧?
这和那些经典的分析手段也没什么大不同,除了更加复杂一点而已.
xyfree
发表于 2010-03-13 14:05
回复 24# cjaizss
对的,差不多就那个意思。
但不是自右向左输入。
我主要的想法是,尽可能分析出一大段可能含有出错代码中,没有出错的部分。
如果光是从前往后的输入,遇到一个错误之后,后面的识别可能就会受到影响。
但是如果能够将词法符号输入流切割的话,那么在某个切割段之中的出错不会影响下一个切割段的识别。
prolj
发表于 2010-03-13 14:36
前端出错处理机制是这样的:读到一个错误的tok之后,能进行规约就规约,不能的话继续移进。gcc的cparser应该是这样的。
clang的好一些,遍历ast的时候发现类型不匹配了,跳过这个ast,继续下一个。