移:强悍的ANTLR Lexer

强悍的ANTLR Lexer
ANTLR的Lexer不是基于DFA的,所以更加灵活
比如,我要解析一段这样的代码,class的大括号{}需要解析,method的大括号{}内的内容不需要解析,只要作为一个整体就可以
class clazz{
 test1(){
  abc;
 }
 
 test2(){
  {
   efg;
  }
 }
}
 
code.g:
header{
 import java.util.*;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
}
class JParser extends Parser;
options {
 k = 2;                            // two token lookahead
 buildAST = true;
}
tokens {
 BLOCK;
}
startParse
 : classDefinition
 ;
classDefinition
 : "class" IDENT LCURLY (methodDefinition)* RCURLY
 ;
 
methodDefinition
 : IDENT LPAREN RPAREN CODEBLOCK
 ;
 
class JLexer extends Lexer;
options {
 k=2; 
}
{
 private static final Log log = LogFactory.getLog(JLexer.class);
 int deep = 0;
}

LPAREN   : '('  ;
RPAREN   : ')'  ;
SEMI   : ';'  ;

LCURLY   : '{'  {deep++;}
    ;

RCURLY   : '}'  {deep--;}
    ;
//in class, a code block don't need parse
CODEBLOCK  : {deep > 0}?
    '{' (~'}')* '}'
    {
     log.info($getText);
    }
    ;
// whitespace
WS : ( ' '
  | '/t'
  | '/r' '/n' { newline(); }
  | '/n' { newline(); }
  )
  {$setType(Token.SKIP);} //ignore this token
  ;
// an indentify, such as kent1_name
IDENT
 options {testLiterals=true;}
 : ('a'..'z'|'A'..'Z'|'_'|'$') ('a'..'z'|'A'..'Z'|'_'|'0'..'9'|'$')*
 ;
 
看看能打印出什么?
11:18:14,843  INFO JLexer[mCODEBLOCK]:956 - {
  abc;
 }
11:18:14,843  INFO JLexer[mCODEBLOCK]:956 - {
  {
   efg;
  }
哈,方法内的内容全部打印出来了!注意k必须>1
同样的功能使用JavaCC要麻烦一些.ANTLR的确强大!
Lexer只是ANTLR的一小部分功能,还有其他更吸引人的地方,比如正则表达式方式书写产生式(不需要写成递归的),AST语法,比如#^!等等,还没搞清楚,等全部搞清了再写下一篇吧


强悍的ANTLR Lexer-续
上文中只能支持单层的{},如果{}嵌套则会出现问题,得出结果类似: {{}
比如下文中的例子:
class clazz{
 test1(){
  abc;
 }
 
 test2(){
  {
   efg;
   {
    hij;
    {
     klm;
    }
    opq;
   }
   rst;
  }
 }
}
多重嵌套如何处理呢?
 
对于ANTLR很简单,因为它支持递归的正则表达式,只需要把code.g修改如下就可以:
CODEBLOCK  : {deep > 0}?
    '{' {deep++;}
    ( CODEBLOCK | ~'}')*
    '}' {deep--;}
    {
     if(deep == 1){
      log.info("START" + $getText);
     }
    }
    ;
看看我们能打印出什么结果?
14:12:30,953  INFO JLexer[mCODEBLOCK]:961 - START{
  abc;
 }
14:12:30,953  INFO JLexer[mCODEBLOCK]:961 - START{
  {
   efg;
   {
    hij;
    {
     klm;
    }
    opq;
   }
   rst;
  }
 }
大功告成!



强悍的ANTLR LEXER-续2

接上文,我需要增加新的功能:除了把java method完全识别,还要识别java field.
就是说:类似以下语句:
abc = 123;
bde = 34{56{};ere{};sfa};
我必须把abc作为一个词, = 123作为一个词
bde作为一个词, = 34{56{};ere{};sfa};作为一个词
为什么要这样定义词法呢?因为对于java 类变量,一般有以下定义:
int i= 0; //第一种:直接赋值
int j= B.getIntValue(); //第二种:从其他类获得,包括 = (new B()).getValue();
B b = new B(); //第三种:new 一个
E e = (E) J.getValue(); //第四种,造型
D d = new D(){//第五种:使用内部类创建
public Object get(int i){
return null;
}
};
总结以上五种方法,有如下特征:
1.以=开始,以;结束
2.除了定义一个内部类会有{}和;嵌套,其他都不包含{}和;
code.g增加如下定义:
EXPR
 : '=' (~(';'|'{'))*  EXPRBLOCK
 
  {
   log.info("expr: " + $getText);
  }
 ;
protected
EXPRBLOCK
 :
     (
      {deep > 0}?
         '{' {deep++;}
      ( EXPRBLOCK | ~'}')*
      '}' {deep--;}
      {
       if(deep == 1){
        //log.info("START" + $getText);
       }
         }
      ';'
     )
     |';'
 ;
看上去非常复杂,测试结果:
14:05:05,828  INFO JLexer[mEXPR]:266 - expr: = 123;
14:05:05,828  INFO JLexer[mEXPR]:266 - expr: = 34{56{};ere{};sfa};
成功了!
不知道这是否最佳写法,但是看上去似乎也解决了问题!



强悍的ANTLR LEXER-续3

接上文,在测试过程中,上文的正则表达式出现了问题.
解决方法就是:采用非贪婪设置
在ANTLR中,可以使用greedy设置是否贪婪.
贪婪本来是词法分析中的一个原则.比如,有两个表达式,分别是a和ab,现在有个串:abcd,按照贪婪原则,词法分析器应该匹配ab而不是 a.在ANTLR中可以关闭这个选项:greedy=false.同时,为了不报错误,还必须设置: generateAmbigWarnings= false;
按照非贪婪的写法,现在简单了:
必须说明的是,为了简单化,多个变量的定义用,分隔的,如:
a=1,b=1;这里识别为a和=1,b=1;
因为不能用,表明两个变量,而需要更加精确的分析表达式.
比如: a=new A(1,2), b=2;
这样的表达式必须分析 A(1,2)这样的表达式,很麻烦
 
EXPRBLOCK
 : '='
 (
  options {
   generateAmbigWarnings=false;
   greedy = false;
  }   
  : . | CODEBLOCK
 
 )*
      ';'
 
 {
  log.info("expr: " + $getText);
 }
 ;

//in class, a code block don't need parse
CODEBLOCK 
 : {deep > 0}?
     '{' {deep++;}
     ( CODEBLOCK | ~'}')*
     '}' {deep--;}
     {
      if(deep == 1){
       log.info("START" + $getText);
      }
     }
     ;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值