ANTLR:有一个简单的例子吗?
我想开始使用ANTLR,但花了几个小时在antlr.org网站上查看示例之后,我仍然无法清楚地理解Java过程的语法。
有没有一个简单的例子,像ANTLR通过parsing器定义和Java源代码一起实现的一个四操作计算器?
你首先创build一个语法。 下面是一个小语法,可以用来评估使用4个基本的math运算符(+, – ,*和/)构build的expression式。 您也可以使用圆括号对expression式进行分组。
请注意,这个语法只是一个非常基本的语法:它不处理一元运算符(减号:-1 + 9)或小数点(例如.99)(没有前导数字),这只是两个缺点。 这只是一个例子,你可以自己工作。
以下是语法文件Exp.g的内容:
grammar Exp; /* This will be the entry point of our parser. */ eval : additionExp ; /* Addition and subtraction have the lowest precedence. */ additionExp : multiplyExp ( '+' multiplyExp | '-' multiplyExp )* ; /* Multiplication and division have a higher precedence. */ multiplyExp : atomExp ( '*' atomExp | '/' atomExp )* ; /* An expression atom is the smallest part of an expression: a number. Or when we encounter parenthesis, we're making a recursive call back to the rule 'additionExp'. As you can see, an 'atomExp' has the highest precedence. */ atomExp : Number | '(' additionExp ')' ; /* A number: can be an integer value, or a decimal value */ Number : ('0'..'9')+ ('.' ('0'..'9')+)? ; /* We're going to ignore all white space characters */ WS : (' ' | '\t' | '\r'| '\n') {$channel=HIDDEN;} ;
(parsing器规则以小写字母开头,词法规则以大写字母开头)
创build语法之后,您需要从中生成parsing器和词法分析器。 下载ANTLR jar并将其存储在与语法文件相同的目录中。
在您的shell /命令提示符处执行以下命令:
java -cp antlr-3.2.jar org.antlr.Tool Exp.g
它不应该产生任何错误信息,现在应该生成文件ExpLexer.java , ExpParser.java和Exp.tokens 。
要看看它是否正常工作,创build这个testing类:
import org.antlr.runtime.*; public class ANTLRDemo { public static void main(String[] args) throws Exception { ANTLRStringStream in = new ANTLRStringStream("12*(5-6)"); ExpLexer lexer = new ExpLexer(in); CommonTokenStream tokens = new CommonTokenStream(lexer); ExpParser parser = new ExpParser(tokens); parser.eval(); } }
并编译它:
// *nix/MacOS javac -cp .:antlr-3.2.jar ANTLRDemo.java // Windows javac -cp .;antlr-3.2.jar ANTLRDemo.java
然后运行它:
// *nix/MacOS java -cp .:antlr-3.2.jar ANTLRDemo // Windows java -cp .;antlr-3.2.jar ANTLRDemo
如果一切顺利,没有任何东西正在打印到控制台上。 这意味着parsing器没有发现任何错误。 当您将"12*(5-6)"
更改为"12*(5-6)"
,然后重新编译并运行时,应该打印下列内容:
line 0:-1 mismatched input '<EOF>' expecting ')'
好的,现在我们要在语法中添加一些Java代码,这样parsing器实际上会做一些有用的事情。 添加代码可以通过将{
和}
放置在语法中,并在其中包含一些简单的Java代码。
但首先:语法文件中的所有parsing器规则应该返回一个原始的double值。 你可以通过在每个规则之后添加returns [double value]
:
grammar Exp; eval returns [double value] : additionExp ; additionExp returns [double value] : multiplyExp ( '+' multiplyExp | '-' multiplyExp )* ; // ...
这需要很less的解释:每个规则预期会返回一个双重值。 现在,要从代码块中与返回值double value
(不在普通Java代码块{...}
)进行“交互”,您需要在value
前面添加一个美元符号:
grammar Exp; /* This will be the entry point of our parser. */ eval returns [double value] : additionExp { /* plain code block! */ System.out.println("value equals: "+$value); } ; // ...
这里是语法,但现在添加了Java代码:
grammar Exp; eval returns [double value] : exp=additionExp {$value = $exp.value;} ; additionExp returns [double value] : m1=multiplyExp {$value = $m1.value;} ( '+' m2=multiplyExp {$value += $m2.value;} | '-' m2=multiplyExp {$value -= $m2.value;} )* ; multiplyExp returns [double value] : a1=atomExp {$value = $a1.value;} ( '*' a2=atomExp {$value *= $a2.value;} | '/' a2=atomExp {$value /= $a2.value;} )* ; atomExp returns [double value] : n=Number {$value = Double.parseDouble($n.text);} | '(' exp=additionExp ')' {$value = $exp.value;} ; Number : ('0'..'9')+ ('.' ('0'..'9')+)? ; WS : (' ' | '\t' | '\r'| '\n') {$channel=HIDDEN;} ;
由于我们的eval
规则现在返回一个double,所以把你的ANTLRDemo.java改成:
import org.antlr.runtime.*; public class ANTLRDemo { public static void main(String[] args) throws Exception { ANTLRStringStream in = new ANTLRStringStream("12*(5-6)"); ExpLexer lexer = new ExpLexer(in); CommonTokenStream tokens = new CommonTokenStream(lexer); ExpParser parser = new ExpParser(tokens); System.out.println(parser.eval()); // print the value } }
再次(重新)从你的语法生成一个新的词法分析器和parsing器(1),编译所有类(2)并运行ANTLRDemo(3):
// *nix/MacOS java -cp antlr-3.2.jar org.antlr.Tool Exp.g // 1 javac -cp .:antlr-3.2.jar ANTLRDemo.java // 2 java -cp .:antlr-3.2.jar ANTLRDemo // 3 // Windows java -cp antlr-3.2.jar org.antlr.Tool Exp.g // 1 javac -cp .;antlr-3.2.jar ANTLRDemo.java // 2 java -cp .;antlr-3.2.jar ANTLRDemo // 3
现在你会看到expression式12*(5-6)
打印到你的控制台的结果!
再次:这是一个非常简短的解释。 我鼓励你浏览ANTLR wiki ,阅读一些教程和/或播放我刚发布的内容。
祝你好运!
编辑:
本文展示了如何扩展上面的例子,以便提供一个Map<String, Double>
来保存提供的expression式中的variables。
这个问答演示了如何创build一个简单的expression式parsing器,并使用ANTLR4进行评估。
为了使这个代码与当前版本的Antlr一起工作(2014年6月),我需要做一些改变。 ANTLRStringStream
需要成为ANTLRInputStream
,从parser.eval()
到parser.eval().value
value所需的返回值,我需要在最后删除WS
子句,因为像$channel
这样的属性值不再被允许出现在词法分析的行动。
对于Antlr 4,Java代码生成过程如下:
java -cp antlr-4.5.3-complete.jar org.antlr.v4.Tool Exp.g
相应地在classpath中更新jar名称。
在https://github.com/BITPlan/com.bitplan.antlr你会发现有一些有用的帮助类和一些完整的例子ANTLR Java库。 它准备与maven一起使用,如果你喜欢eclipse和maven。
https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/main/antlr4/com/bitplan/exp/Exp.g4
是一个简单的expression式语言,可以做乘法和增加操作。 https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestExpParser.java有相应的unit testing。
- parsing器语法
- 词法分析器
- 导入LexBasic语法
就我个人而言,我发现这是最棘手的部分得到正确的。 见http://wiki.bitplan.com/index.php/ANTLR_maven_plugin
https://github.com/BITPlan/com.bitplan.antlr/tree/master/src/main/antlr4/com/bitplan/expr
包含另外三个在早期版本中为ANTLR4的性能问题创build的示例。 与此同时,这个问题已经被解决,如testing用例https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestIssue994.java所示。;
对我来说,这个教程是非常有用的: https : //tomassetti.me/antlr-mega-tutorial
它有语法示例,不同语言(Java,JavaScript,C#和Python)的访问者的例子以及许多其他的东西。 强烈推荐。