`

antlr使用教程

阅读更多
我从以下几个问题入手介绍ANTLR的相关知识。
1 ANTLR是什么?
ANTLR, ANother Tool for Language Recognition, 是一个可以接受含有语法描述的语言描述符并且生成程序能够识别这些语言所产生的句子。作为一个翻译程序的 一部分,你可以给你的语法附上简单的操作符和行为并且告诉ANTLR如何构造AST并且如何输出它们。ANTLR知道如何使用Java,C++,C#或者Python来生成它们。
ANTLR知道如何构造识别程序并且将语法结构应用到三种不同的输入上:(i) 字符流, (ii) 标记(token)流,(iii) 二维树结构。本质上这些对应了词法分析器(Lexer),解析器(Parser)和Tree Walker。用于制定这些语法(Grammar)的句法(Syntax),被称 为meta-language,在所有情况下是独立的。
一旦你适应了ANTLR或者相应的工具,你将会以另一种眼光来看待编程。很多任务期待一种不同于传统编程语言流派的语言解决方案。举个例子,这些课程的笔记就是用TML编写 的,Terence's Markup Language。我讨厌输入HTML所以我用ANTLR编写了一个简单的翻译程序来转换文本成为HTML或者PDF或者其他我讨厌直接编写的东西。
(以上摘自http://www.cs.usfca.edu/~parrt/course/652/lectures/antlr.html)
2 ANTLR能做些什么?
ANTLR仅仅是一个工具!它帮你通过自动生成单调乏味的组件来构造程序,但并不试图让你创造一个完整的编译器,举个例子,单行的描述。
3 ANTLR怎么搭建环境?
我安装的是插件形式的ANTLR,它的版本为2.7.6.
当其他插件安装一样,比较简单。
或者:
1 、从 http://www.antlr.org/ 上下载 antlr-x.x.x.jar
2 、配置环境变量: classpath=.;x:\jdk\lib\tools.jar;x:\antlr-x.x.x.jar
3 、在命令提示符环境里在 t.g 所在目录下执行:
java antlr.Tool t.g

4 ANTLR语法文件怎么写?
4.1 Antlr 的主要类:
Antlr 中有主要类有两种(其实还有一种 TreeLexer )
Lexer: 文法分析器类。主要用于把读入的字节流根据规则分段。既把长面条根据你要的尺寸切成一段一段:)并不对其作任何修改。
Parser: 解析器类。主要用于处理经过 Lexer 处理后的各段。一些具体的操作都在这里。
4.2 Antlr 文法文件形式:
Antlr 文件是 *.g 形式,即以 g 为后缀名。
例如: t.g
class P extends Parser;
startRule
:   n:NAME
{System.out.println("Hi there, "+n.getText());}
class L extends Lexer;
// one-or-more letters followed by a newline
NAME:   ( 'a'……'z'|'A'……'Z' )+ NEWLINE
NEWLINE
:   '\r' '\n'   // DOS
|   '\n'        // UNIX 网管u家u.bitsCN.com

4.3 具体成分分析:
1 、总体结构
Class P extends Parser
Class L extends Lexer
两行同 JAVA 继承一样, P 继承 Parser 类; L 继承 Lexer 类。每个 .g 文件只能各有一个。
2 、 Lexer 类分析
一般按照:
类型名:匹配的具体规则的形式构成。是分隔字节流的依据。同时可以看到里面可以互相引用。如本例中的类型名 NEWLINE 出现在 NEW 的匹配规则中。
4.4 Parser 类分析
一般按照
起始规则名:
规则实例名:类型名或规则名
{Java 语句……; };
……
的形式构成。
起始规则名:任意。
 规则实例名:就象 Java 中“ String s ;”的 s 一样。规则实例名用于在之后的 JAVA 语句中调用。
 类型名或规则名:可以是在 Lexer 中定义的类型名,也可以是 Parser 中定义的规则名。感觉就像是 int 与 Integer 的区别。
Java 语句:指当满足当前规则时所执行的语句。 Antlr 会自动嵌入生成的 java 类中。
三、生成 Java 类
会在当前目录下生成如下文件:
L.java : Lexer 文法分析器 java 类。
P.java : Parser 解析器 java 类。
PTokenTypes.java : Lexer 中定义的类型具体化,供 Parser 解析器调用。
PTokenTypes.txt :当外部的(如 t2.g )要调用当前的类型或规则时要用到本文件。
4.5 执行
1 、编写 Main 类
import java.io.*;
class Main {
public static void main(String[] args) {
try {
L lexer = new L(new DataInputStream(System.in));
P parser = new P(lexer);
parser.startRule();
} catch(Exception e) {
System.err.println("exception: "+e);
}
5 再从编写一个表达式解析器文件介绍一下ANTLR语法:
表达式是几乎所有高级编程语言中,都会出现的重要组成部分。因此,如何准确的理解一个表达式,可以说是各种不同的语言所共同面临的问题。表达式千变万化,真正要想正确解释C/C++那样的复杂表达式,是非常困难的,我们这里只从最简单的表达式做起。
假设一个表达式中,只有常量,没有变量。所有的运算只有:“+”、“-”、“*”、“/”、“^”、“%”六种,而且没有括号,只有整数,没有小数。就这么简单,要解释这样的表达式,我们如果要想自己写个程序来解释,只怕也是非常麻烦的吧,而且要写一个功能全面且BUG很少的解释器势必要大费周折。
现在有了ANTLR,我们只需要将定义写清楚,程序就会自动帮我们生成了。可以先下载一个人家现成的文件来看看:expression.g,然后再antlr expression.g生成一堆java文件。
接下来的步骤和前面的也差不多,建一个Main.java,
import java.io.*;
import antlr.CommonAST;
import antlr.collections.AST;
import antlr.debug.misc.ASTFrame;
public class Main {
  public static void main(String args[]) {
    try {
      DataInputStream input = new DataInputStream(System.in);
ExpressionLexer lexer = new ExpressionLexer(input);
    ExpressionParser parser = new ExpressionParser(lexer);

      parser.expr();
      CommonAST parseTree = (CommonAST)parser.getAST();
      System.out.println(parseTree.toStringList());
      ASTFrame frame = new ASTFrame("The tree", parseTree);
      frame.setVisible(true);

      ExpressionTreeWalker walker = new ExpressionTreeWalker();
      double r = walker.expr(parseTree);
      System.out.println("Value: "+r);
    } catch(Exception e) { System.err.println("Exception: "+e); }
  }
}
执行这个Main.class,输入个表达式给它试一试,比如: 1+2-3*4/5^6;系统应该就能给出正确的答案了:
( - ( + 1 2 ) ( / ( * 3 4 ) ( ^ 5 6 ) ) ) ;
Value: 2.999232

咱们来一句一句的解释一下这个expression.g文件。还是从最简单的几行开始:

class ExpressionLexer extends Lexer; //这是用来声明一个词法分析器,名字叫
                                                   //ExpressionLexer

PLUS  : '+' ;                                   //加号
MINUS : '-' ;                                   //减号
MUL   : '*' ;                                   //乘号
DIV   : '/' ;                                    //除号
MOD   : '%' ;                                 //求余
POW   : '^' ;                                 //开方
SEMI  : ';' ;                                    //结束号
                                                   //上面这些都太简单了,简直就不需要说明

protected DIGIT : '0'..'9' ;               //数字,这是一个受保护的单词,只能被
                                                   //词法分析器内部使用
INT   : (DIGIT)+ ;                           //出现了一次以上的数字的词,就是整数,
                                                   //它通过受保护的单词:“数字”来定义自己。
//如果DIGIT不是被保护的单词,则词法分析器就会
                                                   //无法分辨究竟是数字还是整数了

接下来看语法分析器的代码:

class ExpressionParser extends Parser;   //定义一个语法分析器,名字叫ExpressionParser
options { buildAST=true; }                  //告诉ANTLR,要帮我生成一棵抽象语法树,
                                                       //留着以后有用

//接下来的部分就非常复杂了,主要是多出来了两个特殊的符号“!”、“^”
//这两个符号,不是EBNF原有的,而是ANTLR为了生成AST而增加的符号
//“!”,是告诉AST生成程序,不要把自己算进去
//“^”,是告诉AST生成程序,把这个符号,放在一颗树的根部,或者一颗子树的根部
//另外,“*”表示出现0次以上,“+”表示出现一次以上,“?”表示出现0或1次

/*以下为定义一个语法的规则,一般采用递归的形式来规定。
即就是它描述了一个表达式的表达形式。解析和计算表达式的方式有很多种。在使用递归下降的解析器时,表达式被视为递归的数据结构—— 表达式由其本身来定义。假定表达式只能使用+、-、*、/和圆括号,那么所有的表达式可以用下面的规则来定义:
表达式 项 [+项] [–项]
项因数[*因数] [/因数]
因数变量、数字或者(表达式)
方括号里面表示可选元素,而箭头表示箭头前面的元素由箭头后面的元素定义产生。实际上,该规则通常被称为表达式的生成规则。因此,对于项的定义可以这样表述:“项由因数乘以因数或者因数除以因数产生。”需要注意的是,运算符的优先级已经隐含在表达式的定义中。 */
expr     : sumExpr SEMI!;                                  //“;”作为结束符,不放入AST
sumExpr  : prodExpr ((PLUS^|MINUS^) prodExpr)* ;     //“+”“-”作为计算符号
                                                                              //放在树的顶部
prodExpr : powExpr ((MUL^|DIV^|MOD^) powExpr)* ; //剩下的就不解释了,都能明白的
powExpr  : atom (POW^ atom)? ;
atom     : INT ;

再来看AST计算器的代码。这“AST计算器”是我起的名字,也就是通过对一个生成的抽象语法树,递归求值,得到最后的结果。

{import java.lang.Math;}                                     //ExpressionTreeWalker要用到的
class ExpressionTreeWalker extends TreeParser;    //声明一个树计算器
expr returns [double r]                                      //有一个方法叫expr
                                                                      //它的返回值是double类型
  {double a,b; r=0; }                                         //嵌入的代码,后面要用到

  : #(PLUS a=expr b=expr)  { r=a+b; }               //以下就是计算各种算符,不用多说了
  | #(MINUS a=expr b=expr) { r=a-b; }
  | #(MUL  a=expr b=expr)  { r=a*b; }
  | #(DIV  a=expr b=expr)  { r=a/b; }
  | #(MOD  a=expr b=expr)  { r=a%b; }
  | #(POW  a=expr b=expr)  { r=Math.pow(a,b); }
  | i:INT { r=(double)Integer.parseInt(i.getText()); }
  ;
5.1 完整的expression.g文件:
class ExpressionParser extends Parser;
options { buildAST=true; }

expr     : sumExpr SEMI;
sumExpr  : prodExpr ((PLUS^|MINUS^) prodExpr)*;
prodExpr : powExpr ((MUL^|DIV^|MOD^) powExpr)* ;
powExpr  : atom (POW^ atom)? ;
atom     : INT ;

class ExpressionLexer extends Lexer;

PLUS  : '+' ;
MINUS : '-' ;
MUL   : '*' ;
DIV   : '/' ;
MOD   : '%' ;
POW   : '^' ;
SEMI  : ';' ;
protected DIGIT : '0'..'9' ;
INT   : (DIGIT)+ ;

{import java.lang.Math;}
class ExpressionTreeWalker extends TreeParser;

expr returns [double r]
  { double a,b; r=0; }

  : #(PLUS a=expr b=expr)  { r=a+b; }
  | #(MINUS a=expr b=expr) { r=a-b; }
  | #(MUL  a=expr b=expr)  { r=a*b; }
  | #(DIV  a=expr b=expr)  { r=a/b; }
  | #(MOD  a=expr b=expr)  { r=a%b; }
  | #(POW  a=expr b=expr)  { r=Math.pow(a,b); }
  | i:INT { r=(double)Integer.parseInt(i.getText()); }
6 ANTLR使用中的一些使用心得:
1):在此法分析中,比如要描述一个“>=”与”>”时,如果用
BEQUAL:('>''=');
BIGER:”>”;
当语法文件进行此法分析的时,当扫描到一个”>”形式时,不知道是将其当BEQUAL还是当BIGER符号处理,即出现了冲突,那么可以采用以下这种形式定义:
BEQUAL:('>''=')=>('>''=')|'>'{ $setType(BIGER); };//它的形式为: (...)=>(...)|(...)。这相当于一般语言中的三元表达式:(1)?(2):(3)。如果式1为真,则返回式2的值,否则返回式3的值。
2):在ANTLR中一个规则相当与JAVA语言中的一个函数,因此它可以有传参和返回值,例如:
expr[HashMap hm] returns [String s]//即相当于JAVA中的:
public String expr(HashMap hm){…}
3):ANTLR中可以内嵌生成的目标语言 如:{import java.lang.Math;}
3
1
分享到:
评论

相关推荐

    ANTLR 4简明教程.epub

    ANTLR 4简明教程.epub

    编译器的编译器antlr的教程

    教程定位为帮助零基础的初学者入门到掌握编译前端,为熟悉antlr的人,提供一些问题备忘录,以及共享一些对antlr本身设计的看法。希望能对您有所帮助。产品的开发需要,对词法语法解析工具选型,选了几个工具,javacc...

    antlr经典教程,The definitive antlr reference(英文)

    The definitive antlr reference是一本经典的antlr教程,对antlr的Grammar,构造AST和以及编译技术,LL分析法都有深入的剖析,在antlr教程匮乏的时代,此书无疑是一个最好的补充。

    antlr-v4jar包和使用教程

    antlr-v4的jar包,及其简单的官方使用教程,可用于生成词法分析和语法分析的Java代码。语法分析的方式是LL(1)。

    ANTLR入门详细教程定稿.pdf

    ANTLR入门详细教程定稿.pdf

    Antlr教程文法分析器

    文法分析器类主要用于把读入的字节流根据规则分段

    收集的一些antlr资源

    Antlr入门详细教程.doc 利用ANTLR生成C 描述的分析程序.htm 使用 Antlr 处理文本.htm 使用 Antlr 开发领域语言.htm 强悍的ANTLR Lexer.htm a little madness » Blog Archive » ANTLR By Example Part 1 The ...

    Antlr入门详细教程

    Antlr 中有主要类有两种(其实还有一种 TreeLexer )    Lexer: 文法分析器类。主要用于把读入的字节流根据规则分段。既把长面条根据你要的尺寸切成一段一段:)并不对其作任何修改。    Parser: 解析器类。...

    solidity-parser:JavaScript中的Solidity解析器

    :warning: 停产通知 :warning: 该库不再维护。 对于最新的Solidity解析器,请查看 。 实体解析器 Javascript中的Solidity解析器。 因此,我们可以评估和更改Solidity代码,而无需求助于粗糙的预处理。...

    Antlr4指南

    Antlr4指南 权威介绍 学习教程

    Pragmatic.The Definitive ANTLR 4 Reference.2013.pdf

    英文原版antlr4语法分析教程,可以对自定义语法进行解析。

    js-edi:antlr4ts编译的Antlr4语法中的下一代EDI解析器

    JS-EDI 正在进行的工作,不适合生产使用。 利用Antlr4语法的EDIX12和EDIFACT解析器。 如果我可以称其为J-EDI,我会的。 愿原力与你同在。特征支持EDI X12和EDIFACT 在JS中本地解析为文档对象模型(DOM) 从构建的dom...

    关于antlr的详细教材

    ANTLR是ANother Tool for Language Recognition的缩写“又一个语言识别工具”。从名字上可以看出在ANTLR出现之前已经存在其它语言识别工具了(如LEX [1],YACC [2])。ANTLR的官方定义为:根据一种可以嵌入如Java, ...

    ANTLR3_handouts.pdf(En)

    关于ANTLR3的简单教程,有过有基础的话,可以很快上手。但是不能当做手册用。

    hand in hand with antlr安装

    关于anltr的零基础教程与demo,本篇只是安装部分,后续教程在继续中。

    example-antlr:Antlr示例

    Antlr 4示例/简短教程这是我的《 博客文章的示例代码库。

    Language Implementation Patterns.pdf

    英文原版编程语言实现模式,来自于Antlr相关,在antlr权威教程之后又一力作

    OGNL教程,简单,看完懂OGNL语言

    Luke 参与进来后,用 ANTLR 来实现了该语言,并给它取了这个新名字,他后来又使用 JavaCC 重新实现了该语言。目前 OGNL 由 Drew 来负责维护。目前很多项目中都用到了 OGNL,其中不乏为大家所熟知的,例如几个流行的...

    parsing-sql:解析SQL的同伴存储库,有关解析SQL的工具和库以及如何构建SQL解析器的文章

    如果您不知道如何使用,可以阅读有关的教程。 要生成解析器,可以使用此命令。 antlr4 SQL.g4 -Dlanguage=CSharp -o generated\ -encoding UTF-8这将在generated文件夹内generated解析器。 数据文件夹sqlite-sakila...

    hive词法语法分析草稿0.3

    antlr的应用实例教程 hive源码分析 从词法语法语义解析到执行计划的生成优化以及执行。

Global site tag (gtag.js) - Google Analytics