使用Eclipse/Antlr解析简单文本

http://www.cppblog.com/morya/archive/2009/12/07/102681.html?opt=admin

这篇文章antlr的中文翻译,作为参考文档非常好

 

ANTLR 有三种主要的使用方法。

第一种是实现“验证器”。 它检验输入文本是否符合文法规定的规则。第二种是实现 “处理器”。 它检验并处理输入文本。 可以进行计算,更新数据库,读取配置文件到内存中,等。 我们后面的Math示例就是一个处理器的例子。第三种就是“翻译器”。 它验证输入,并将输入翻译成另一种格式,比如编程语言或字节码。晚点我们会讨论 “动作” 和 “重写规则”。 当然,明确这三种在何种情况使用会利于理解。 验证器不使用 “动作”和 “重写规则”。 处理器使用“动作”,但不使用“重写规则”。 翻译器使用“动作”,可能使用“重写规则”。 (包含 printlns)

 

ANTLR学习心得

1、请在Eclipse中建立一个新的项目,名叫Simple2,在classpath中,要加入antlr.jar

2、再下载一个文件:simple2.g,放在这个项目的路径下

3、在DOS窗口下,输入antlr simple2.g

4、回到eclispe,按F5刷新,会看到多出不少文件来。

5、再新建一个类Main,输入以下代码:

import java.io.*;
public class Main {
    public static void main(String args[]) {
        FileInputStream f = null;
        try {
            f = new FileInputStream("......//test.txt");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        DataInputStream input = new DataInputStream(f);
        SimpleLexer lexer = new SimpleLexer(input);
        SimpleParser parser = new SimpleParser(lexer);
        try {
            parser.entry();
        } catch(Exception e) {}
    }
}

6、新建一个文本文件test.txt,输入内容:

06/06/82 Peter 20;
03/04/83 Rosie 19;
04/05/81 Mikey 21;

7、运行Mainm,就能看到输出结果:

Name: Peter, Age: 20, DOB: 06/06/82
Name: Rosie, Age: 19, DOB: 03/04/83
Name: Mikey, Age: 21, DOB: 04/05/81

 

下面来解释一下,这里面的原理。先来看simple2.g的内容:

class SimpleParser extends Parser;

entry : (d:DOB n:NAME a:AGE(SEMI)
      { 
        System.out.println(
          "Name: "    + 
          n.getText() +
          ", Age: "   +
          a.getText() + 
          ", DOB: "   +
          d.getText()
        );
      })*
      ;

class SimpleLexer extends Lexer;

NAME : ('a'..'z'|'A'..'Z')+;

DOB  : ('0'..'9' '0'..'9' '/')=> 
       (('0'..'9')('0'..'9')'/')(('0'..'9')('0'..'9')'/')('0'..'9')('0'..'9') //{ $setType(DOB); }
     | ('0'..'9')+  { $setType(AGE); } ;

WS     :
    (' ' 
    | '/t' 
    | '/r' '/n' { newline(); } 
    | '/n'      { newline(); }
    ) 
    { $setType(Token.SKIP); } 
  ;

SEMI : ';' ;

 

要理解这个文件,我们得从下往上读,才能渐入佳境图片点击可在新窗口打开查看

 

最简单的是: 

SEMI : ';';

SEMI是一种符号,对于词法分析器来说,只要它读到一个字符是“;”,它就认为自己是读到了一个SEMI

 

第二简单的是:

NAME : ('a'..'z'|'A'..'Z')+;

当词法分析器读取一个一个的字符的时候,当他读到一个以上的字母(无论大小写),它都把它们连在一起,作为一个NAME。()+,就是出现一次以上的意思。()*,就是出现0次以上的意思。

 

第三简单的是:

WS  :
    (' ' 
    | '/t' 
    | '/r' '/n' { newline(); } 
    | '/n'      { newline(); }
    ) 
    { $setType(Token.SKIP); } 
  ;

任何一个词法分析中定义的词,都有这样的格式:

[名称] : [定义] ;

定义的格式,又可以是一个或多个定义,';'就是一个定义,' '|'/t'就是两个定义。

每个定义之后,都可以跟一段代码,在

其中处理程序部分是可以省略的。

对于WS的定义,就是(' '|'/t'|'/r''/n'|'/n') 。但是,我们在这个定义之中,发现了三个嵌有代码的地方:'/r''/n'和'/n'后面,都有一句话,newline(),这是告诉词法分析器,行号计数器加一。这样在出现词法、语法错误时,就能报告一个准确的行号了。

在整个定义完成之后,还有一行代码$setType(Token.SKIP);,这是代码调用一个antlr的内置函数,告诉词法分析器,以上遇到的这四种字符情况,都请一律跳过。

 

最难的一种:

DOB  : ('0'..'9' '0'..'9' '/')=> 
       (('0'..'9')('0'..'9')'/')(('0'..'9')('0'..'9')'/')('0'..'9')('0'..'9') //{ $setType(DOB); }
     | ('0'..'9')+  { $setType(AGE); } ;

为什么说这个是最难的一种,因为这篇blog我本来按照每天一篇的进度,是该在昨天发出来的,结果我想了一天,才终于想通这个语法的意义。

DOB,是一种单词,但是这个单词不是一次分析出来的,而是有一个试错的过程。

DOB的格式是00/00/00,而AGE的格式是00。这样要区分两个单词,就相当困难。

而现在的这个定义,则分为三个部分 (...)=>(...)|(...)。这相当于一般语言中的三元表达式:

(1)?(2):(3)。如果式1为真,则返回式2的值,否则返回式3的值。

DOB与AGE的区别在于第三个字符,如果('0'..'9' '0'..'9' '/')的尝试判断无误,则进一步分析这剩下的字符,是否符合DOB的格式。那//注释后面的代码,之所以被注释起来,就是因为他其实不用执行,也是DOB类型了。而如果('0'..'9' '0'..'9' '/')的尝试判断出错,则只能按照 ('0'..'9')+的判断来分析试一试,如果成立,就手工赋予一个类型AGE。

 

 

 

 

class ExprLexer extends Lexer;

options {
k=2; // needed for newline junk
charVocabulary='/u0000'..'/u007F'; // allow ascii
}

LPAREN: '(' ;
RPAREN: ')' ;
PLUS : '+' ;
MINUS : '-' ;
STAR : '*' ;
INT : ('0'..'9')+ ;
WS : ( ' '
| '/r' '/n'
| '/n'
| '/t'
)
{$setType(Token.SKIP);}
;
class ExprParser extends Parser;

expr: mexpr ((PLUS|MINUS) mexpr)*
;

mexpr
: atom (STAR atom)*
;

atom: INT
| LPAREN expr RPAREN
;
class ExprParser extends Parser;

expr returns [int value=0]
{int x;}
: value=mexpr
( PLUS x=mexpr {value += x;}
| MINUS x=mexpr {value -= x;}
)*
;

mexpr returns [int value=0]
{int x;}
: value=atom ( STAR x=atom {value *= x;} )*
;

atom returns [int value=0]
: i:INT {value=Integer.parseInt(i.getText());}
| LPAREN value=expr RPAREN
;
options {buildAST=true;}
class ExprTreeParser extends TreeParser;

options {
importVocab=ExprParser;
}

expr returns [int r=0]
{ int a,b; }
: #(PLUS a=expr b=expr) {r = a+b;}
| #(MINUS a=expr b=expr) {r = a-b;}
| #(STAR a=expr b=expr) {r = a*b;}
| i:INT {r = (int)Integer.parseInt(i.getText());}
;
        ExprLexer lexer = new ExprLexer(System.in);
ExprParser parser = new ExprParser(lexer);
parser.expr();
AST t = parser.getAST();
System.out.println(t.toStringTree());
ExprTreeParser treeParser = new ExprTreeParser();
int x = treeParser.expr(t);

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值