打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
又一个语言识别工具(ANTLR)

刚刚看到《动态计算字串表达式值的类》,好像有许多人表示更喜欢解析器形式的求值类。其实我个人倒觉得用反射实现没什么不好,恰恰相反,我觉得这种实现方法很聪明!另外,装配中的脑袋的代码可以稍微修改一下来提高效率,那样再用的话就不会因为反复编译而影响效率了。

言规正传,不知道大家有没有注意我最近在Blog中添加了一个叫ANTLR的连接,它就是一个著名的“编译器的编译器”,不过它实际上是ANother Tool for Language Recognition(ANTLR),它的描述语言可以生成词法分析器、语法分析器与语义分析器,也就是说,我们可以用它来识别加工不同的语言(编译器的编译器)。它同时支持3大类语言的输出:C++, Java, C#(按照生日排序),也就是说,我们可以利用它来用C#生成编译器。ANTLR是免费开源的,而且历史也很久远了,目前的版本是2.7.5,有兴趣的朋友可以在我的Blog的连接中找到网址。

那么,除了用反射轻松的实现计算值之外我们也可以用ANTLR来描述这些数学表达式,下面我附上一个我在看到《动态计算字串表达式值的类》后写的一个语法文件,感兴趣的朋友可以去下载ANTLR来试一试下面的语法哦!

文件Calc.g

header
{
using CharBuffer = antlr.CharBuffer;
}
options
{
language = "CSharp";
namespace = "Cavingdeep.Test.Calc";
}
/******************************\
Calc Parser
\******************************/
class CalcParser extends Parser;
options
{
exportVocab = Calc;
buildAST = true;
}
expr
: sumExpr EOF
;
sumExpr
: mulExpr ( ( SUM^ | NEG^ ) mulExpr )*
;
mulExpr
: powExpr ( ( MUL^ | DIV^ ) powExpr )*
;
powExpr
: negExpr ( POW^ negExpr )*
;
negExpr
: ( NEG^ )* a:atom!
{
if (#negExpr != null) {
AST lastNegNode = #negExpr;
while (lastNegNode.getNumberOfChildren() > 0) {
lastNegNode = lastNegNode.getFirstChild();
}
lastNegNode.setFirstChild(#a);
} else {
## = #a;
}
}
;
atom
: NUMBER
| LCURLY! sumExpr RCURLY!
;

/***************************\
Calc Lexer
\***************************/
class CalcLexer extends Lexer;
options
{
caseSensitive = false;
exportVocab = Calc;
}
WS
: ( ‘ ‘
| ‘\t‘
| ‘\n‘
| ‘\r‘ )+ { $setType(Token.SKIP); }
;
NUMBER
: ( ( ‘0‘..‘9‘ )+ ‘.‘ ( ‘0‘..‘9‘ )+ ) => ( ‘0‘..‘9‘ )+ ‘.‘ ( ‘0‘..‘9‘ )+
| ‘0‘..‘9‘
;
SUM : ‘+‘ ;
NEG : ‘-‘ ;
MUL : ‘*‘ ;
DIV : ‘/‘ ;
POW : ‘^‘ ;
LCURLY : ‘(‘ ;
RCURLY : ‘)‘ ;

/***************************\
Calc Tree Parser
\***************************/
class CalcTreeParser extends TreeParser;
options
{
exportVocab = Calc;
}
{
public static void Main(string[] args) {
CalcLexer lexer = new CalcLexer(new CharBuffer(Console.In));
CalcParser parser = new CalcParser(lexer);
parser.expr();
AST ast = parser.getAST();
CalcTreeParser treeParser = new CalcTreeParser();

double r = treeParser.eval(ast);
Console.WriteLine("结果是:" + r);
}
}
eval returns [double v = 0]
: v=expr
;
protected
expr returns [double v = 0]
{
double izq = 0;
double der = 0;
}
: i:NUMBER { v = Convert.ToDouble(i.getText()); }
| #( SUM izq=expr der=expr ) { v = izq + der; }
| #( DIV izq=expr der=expr ) { v = izq / der; }
| ( #( NEG expr expr ) ) => #( NEG izq=expr der=expr ) { v = izq - der; }
| #( MUL izq=expr der=expr ) { v = izq * der; }
| #( POW izq=expr der=expr ) { v = Math.Pow(izq, der); }
| #( NEG izq=expr ) { v = -(izq);}
;
  2005年4月5日 15:24 - (阅读:6828;评论:18)

 

反馈

# re: 又一个语言识别工具(ANTLR) 2005-4-5 16:37 ayong
能不能实现这个:

(a+b)(a+c)+b(c+d) = a*a +a*c+a*b+b*c +b*c + b*d
= a + ac+ab+bc+bd
= a(1+b+c)+bc+bd
= a+bc+bd


a,b,c,d都是布尔值,


# re: 又一个语言识别工具(ANTLR) 2005-4-5 16:57 progame
我还是喜欢用coco/r 毕竟用熟了

# re: 又一个语言识别工具(ANTLR) 2005-4-5 17:19 Cavingdeep
ANTLR与其他LL(k) parser的不同在于它支持断言(Predicates)、动作(Actions)与动作的参数(可以return的),所以我虽然不是特别了解CocoR(也是一个LL(k) parser),不过我觉得论能力来讲ANTLR要比CocoR强,因为一个有断言、动作的parser的能力要比一个LR(k)的parser还要强。^_^

# re: 又一个语言识别工具(ANTLR) 2005-4-5 17:29 progame
ANTLR强的一点是直接可以生成AST,我本来想用它 后来看到它的例子 有点晕乎 还是coco/r 上手简单

你是否可具体解释一下它的断言(Predicates)、动作(Actions)与动作的参数 ?

# re: 又一个语言识别工具(ANTLR) 2005-4-5 17:47 progame
对了 我不用它的另一个原因是必须要继承一个基类 还有一个C#CUP也是要这样,而coco/r 不需要 

# re: 又一个语言识别工具(ANTLR) 2005-4-5 19:21 Cavingdeep
to progame,

我在上面给出的例子中就用到了断言、动作、参数等,概念上说简单也简单,说复杂也复杂,我就简单点说吧:)

断言:分为三种类型,语法断言、语义断言(区分断言、验证断言)。看下面例子:

INT : ( DIGIT )+ ;
DIGIT : ‘0‘..‘9‘ ;
REAL : INT ‘.‘ INT ;

当k=1的时候,我们就不能确定到底是INT,还是REAL,那么再往下走应该算哪个Token的呢?这时候通常的解决方法就是将k设为2,这样就能识别了,一般会出现情况要将k设的很大,但是k很大的话就会很影响效率,这时就可以用语法断言(Syntactic Predicate)来临时增大一下k,直到k满足为止,如下:

REAL
: ( INT ‘.‘ ) => INT ‘.‘ INT
| INT
;

在有的情况下也只能通过语法断言来检查语法情况,如:
A
: ( ( CHAR )* ‘;‘ ) => ( CHAR )* ‘;‘
| ( CHAR )*
;

区分断言,利用输出语言做区分,如用C#语法区分,下面用自定义的isType方法接受第一个Token并检测是否是一个定义的语言中的类型:
a
: { isType(LT(1)) }? ID ID ";" // 声明
| ID "=" expr ";" // 赋值
;

验证断言类似,不过写在规则(Rule)之后,如果验证断言失败那么抛出SemanticException。

动作:就是你在上面看到的{}中的内容,这部分是直接签入在输出源代码中的代码,直接用输出语言写的,比如上面的isType方法就可以在动作中写出。

参数,我上面说错了,不是动作的参数,是规则(Rule)的参数,你看我Blog上的TreeParser那部分计算结果时就用到了动作与返回参数,通过参数,我可以让规则可以有返回值或可以接受参数(规则在输出源代码中是一个方法)。

# re: 又一个语言识别工具(ANTLR) 2005-4-5 19:24 Cavingdeep
to progame,

关于继承的问题,我想你可以用另一个类来将生成好的Parser等封装起来,比如可以考虑使用代理模式,这样应该就可以解决你需要继承的问题了。^_^

# re: 又一个语言识别工具(ANTLR) 2005-4-5 21:26 progame
thanks, 关于断言 应该是类似于处理LL冲突吧?
coco/r 提供的方法是可以自行取下面N个token来进行判断,因为是递归下降的分析方法 所以它的生成代码可读性和可调试性都非常好(不过词法分析没办法,而且我还给他们指出了一个错误,现在已经修正了)

而接受参数和返回值,我觉得coco/r做得也很好:
ConstantList<out Constant node> = Constant<out node>
["," ConstantList<out node.nextconstant>]

这里node就是返回的参数了,当然参数也可以多个,byval byref out都可以 因为它因为是LL分析的,所以很多时候只能把上面的写成这种方式,或者:
ConstantList = Constant {"," Constant }

其实最好的答案是:
ConstantList = ConstaintList {"," Constant}

也就是它只支持EBNF 不支持BNF的写法,而我觉得golden parser对于LL冲突解决的最好 而且可以可以验证BNF语法,可以做为初期语法设计使用,不足的就是它只能生成Yacc的语法文件。

# re: 又一个语言识别工具(ANTLR) 2005-4-6 8:49 Cavingdeep
to progame,

首先值得注意的一点是语法断言可以支持non-LL(k)表达式,也就是

A
: ( (CHAR)* B ) => (CHAR)* B
| (CHAR)* C
;

关于这一点我不知道Coco/R是怎么处理的?

另外一点比较关键的是其实ANTLR在LL(k)的基础上将算法做了优化,将原本的m^k变成了m*k,大大的提高了分析效率。不过这样做会导致ANTLR不是纯正的LL(k),而是pred-Strong-LL(k)或pred-SLL(k)。

其实我觉得ANTLR最大的好处还是在于它用统一的语法结合了三大类分析器:词法分析器、语法分析器与语义分析器(AST),这样使得它在使用上非常的方便,另外由于它是一个pred-LL(k) parser(有断言的LL(k) parser),它的能力是所有其他LL(k) parser所不及的。我想也正是以上这两种原因使我选择了ANTLR吧,不过另外一件激励我选择ANTLR的原因是:当JetBrains公司决定做IntelliJ IDEA与ReSharper的时候,他们,也选择了ANTLR。^_^



# re: 又一个语言识别工具(ANTLR) 2005-4-6 9:58 progame
著名的hibernate3.0中使用的也是ANTLR 我不怀疑它的强大,只是我是先用coco/r的 它还能够支撑我的使用,这可能也就是用出了感情吧 上面的例子coco/r 无法简单地处理,但我想一般的语法设计应该避免这种情况的出现吧,这样必须预读N个token才能判断出它的意思了 像这种语法 可能LR分析要来得好一些

gold parser就能够很好地处理LL(K)的问题,但正因为此,会导致语法设计的不严谨,可能是自己设计的一些小错误,结果会被它自动处理掉,但这种错误会导致语法歧义 当然一些语法本身是允许歧义的,这就又另当别论了

(怎么博客堂老说我校验码不正确?是时间太长session失效还是我没认清1I 0o,这种字体实在不好辨认)

# re: 又一个语言识别工具(ANTLR) 2005-4-11 14:51 csharphack
云里雾里

# re: 又一个语言识别工具(ANTLR) 2005-4-12 16:45 Laser.NET
语法有点看不懂,呵呵,有没有语法介绍啊?
看来cavingdeep的编译原理的基本功很扎实嘛!!赞一下:)

# re: 又一个语言识别工具(ANTLR) 2005-4-12 17:42 Cavingdeep
这个语法看起来的确很复杂,其实从根本来说,ANTLR就是一个生成器,只不过它只生成识别语言的源代码,道理与我的DCG类似,我的采用ASP的写法就足够了,不过ANTLR要自己有一个写法才可以(也就是它的语法)。

好的入门资料有倒是有,有一个西班牙语的比较好的PDF,我就是看它慢慢学的!;)英文的就不知道哪个好了,我看了几个入门都不怎么好:(


# re: 又一个语言识别工具(ANTLR) 2005-4-13 14:24 Laser.NET
呵呵,还是懂多国语言的好啊~~牛!!
西班牙语我就别指望了:)呵呵,还是以后要用的时候再问你吧:)

# re: 又一个语言识别工具(ANTLR) 2005-4-15 18:23 csharphack
等我学会了西班牙语后,我都火化了!...

# re: 又一个语言识别工具(ANTLR) 2005-4-22 20:10 S.F.
今后不用再想写编译器了我……
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
Coco/R快速编译器生成
ANTLR笔记1
Coco Custom Tool for Visual Studio.Net
[转载] ANTLR——语法分析 - 6DAN - 博客园
《编译原理简明教程》PPT 第13章
Tony Bai
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服