antlr-基础-解析规则

解析规则

Parser Rules
Parsers包含一系列语法分析规则
java通过使用ANTLR生成的rule函数启动解析
最简单的规则是一个规则名,后跟单一的可选项,最后用分号

/** Javadoc comment can precede rule */
retstat : 'return' expr ';' ;

可以使用可选择的分隔符|

stat: retstat
    | 'break' ';'
    | 'continue' ';'
    ;

可选的规则可以是一系列的规则元素,或者是空

superClass
    : 'extends' ID
    | // empty means other alternative(s) are optional
    ;

Alternative Labels
通过设置最外层的,alternatives #符号,可以获得更精确的parse-tree监听器,所有的选择都是labeled

grammar T;
stat: 'return' e ';' # Return
    | 'break' ';' # Break
    ;
e   : e '*' e # Mult
    | e '+' e # Add
    | INT # Int
    ;

生成的监听器如下

public interface AListener extends ParseTreeListener {
    void enterReturn(AParser.ReturnContext ctx);
    void exitReturn(AParser.ReturnContext ctx);
    void enterBreak(AParser.BreakContext ctx);
    void exitBreak(AParser.BreakContext ctx);
    void enterMult(AParser.MultContext ctx);
    void exitMult(AParser.MultContext ctx);
    void enterAdd(AParser.AddContext ctx);
    void exitAdd(AParser.AddContext ctx);
    void enterInt(AParser.IntContext ctx);
    void exitInt(AParser.IntContext ctx);
}

每一个labeled alternative有一个enter和一个exit
在多个选择中,可以服用同一个label,这样那些任务会触发同一个事件

 e : e '*' e 		# BinaryOp
    | e '+' e 		# BinaryOp
    | INT		    # Int
    ;

生成如下监听器

void enterBinaryOp(AParser.BinaryOpContext ctx);
void exitBinaryOp(AParser.BinaryOpContext ctx);
void enterInt(AParser.IntContext ctx);
void exitInt(AParser.IntContext ctx);

alternative的名字和rule的名字相同会起冲突

  e : e '*' e # e
    | e '+' e # Stat
    | INT # Int
    ;

context对象

Rule Context Objects
就是解析树的节点
ANTLR会生成方法,访问和规则关联的规则上下文对象

 inc : e '++' ;

ANTLR 生成下面的context类

public static class IncContext extends ParserRuleContext {
    public EContext e() { ... } // return context object associated with e
    ...
}
field : e '.' e ;

生成多余一个引用

public static class FieldContext extends ParserRuleContext {
    public EContext e(int i) { ... } // get ith e context
    public List<EContext> e() { ... } // return ALL e contexts
    ...
}

如果有另一个规则s,一个嵌入的action,可以访问规则list

s : field
    {
	    List<EContext> x = $field.ctx.e();
	    ...
    }
;

listener和visitor也可以做这个,指向FieldContext对象

Rule Element Labels
可以使用=操作符,加载fields到rule context objects中

stat: 'return' value=e ';' # Return
    | 'break'          ';' # Break
    ;

value是返回值e的一个标签,e在其他地方定义,Labels在特定的parse node类中,在这里value成为了ReturnContext 的一部分,因为后面的Return标签

public static class ReturnContext extends StatContext {
    public EContext value;
    ...
}

很方变去追踪tokens,可以使用+= list标签操作符,比如下面的规则创建一个token对象的列表,符合一个array结构

array : '{' el+=INT (',' el+=INT)* '}' ;

在rule context class中生成List域
array结构

public static class ArrayContext extends ParserRuleContext {
	public List<Token> el = new ArrayList<Token>();
	...
}

这些list标签同样适合于规则引用

elist : exprs+=e (',' exprs+=e)* ;

ANTLR 生成一个成员,保存context objects列表

public static class ElistContext extends ParserRuleContext {
	public List<EContext> exprs = new ArrayList<EContext>();
	...
}

Rule Elements
规则元素指定了parser应该在给定的时刻做什么事,元素可以是:rule、token、expression、ID、return
下面是完整的规则元素表,后面会详细描述actions和predicates;

Syntax Description
T 当前输入位置匹配token T,token一般是大写字母开头
'literal' 在当前的位置匹配字符串literal,固定字符串的token
r 当前位置匹配规则r,以为着类似函数调用
r [«args»]
$x、和$x.y
{«p»}?
. 通配符,匹配任何内容

是否定符号,比如INT是匹配非INT的内容
~(INT|ID)匹配除了INT或者ID的内容

子规则
子规则扩上圆括号,没有名字,可以有更多的选项,不能定义attributes,有四种子规则
(x|y|z)只要有一种满足就可
returnType : (type | 'void') ;

(x|y|z)?一个都不男足,或者任意
classDeclaration : 'class' ID (typeParameters)? ('extends' type)? ('implements' typeList)? classBody ;

(x|y|z)* 满足0或更多个条件
annotationName : ID ('.' ID)* ;

(x|y|z)+满足一次或更多次
annotations : (annotation)+ ;

?, *, +
??, *?, +? 在后面加上?是非贪婪模式

也可以省略括号作为缩写形式,比如:ID+、annotation+

捕获异常
语法出错之后,ANTLR会捕获异常

void r() throws RecognitionException {
    try {
        rule-body
    }
    catch (RecognitionException re) {
        _errHandler.reportError(this, re);
        _errHandler.recover(this, re);
    }
    finally {
        exitRule();
    }
}

Section 9.5讲解如何利用策略对象处理错误,修改错误异常处理,在定义之后指定异常
单个规则指定异常

r : ...
  ;
  catch[RecognitionException e] { throw e; }

也可以指定其他的异常

r : ...
  ;
  catch[FailedPredicateException fpe] { ... }
  catch[RecognitionException e] { ... }

也可以指定finally

r : ...
  ;
  // catch blocks go first
  finally { System.out.println("exit rule r"); }

异常列表

RecognitionException		
NoViableAltException
LexerNoViableAltException
InputMismatchException
FailedPredicateException

Rule Attribute Definitions
rule可以输入参数、返回值、本地变量像函数;
Antlr收集定义的变量,放到rule context对象中,这些变量被称为attributes,下面的语法演示了可能定义属性的位置

rulename[args] returns [retvals] locals [localvars] : ... ;

比如

// Return the argument plus the integer value of the INT token
add[int x] returns [int result] : '+=' INT {$result = $x + $INT.int;} ;

args、locals、return都用目标语言编写,但是有些限制[...]是逗号分隔的列表,不同语言的有些区别..

例子:

/** Derived from rule "row : field (',' field)* '\r'? '\n' ;" */
row[String[] columns]
   returns [Map<String,String> values]
   locals [int col=0]
    @init {
    $values = new HashMap<String,String>();
    }
    @after {
    if ($values!=null && $values.size()>0) {
    System.out.println("values = "+$values);
    }
    }
    : ...
    ;

规则行获取columns、返回值values、定义的本地变量col;
方括号中的actions直接拷贝到生成的代码中

public class CSVParser extends Parser {
    ...
    public static class RowContext extends ParserRuleContext {
        public String [] columns;
        public Map<String,String> values;
        public int col=0;
        ...
    }
    ...
}

生成的规则函数也指定规则参数,作为函数参数,但是也很快的拷贝到本地的RowContext对象中;

public class CSVParser extends Parser {
    ...
    public final RowContext row(String [] columns) throws RecognitionException {
        RowContext _localctx = new RowContext(_ctx, 4, columns);
        enterRule(_localctx, RULE_row);
        ...
    }
    ...
}

多属性的情况

a[Map<String,String> x, int y] : ... ;

结果

public final AContext a(Map<String,String> x, int y)
    throws RecognitionException {
    AContext _localctx = new AContext(_ctx, 0, x, y);
    enterRule(_localctx, RULE_a);
    ...
}

Start Rules and EOF
start rule是第一个执行的rule,任何的rule都可以作为start rule;
start rule不必消费全部的输入,如下

s : ID
  | ID '+'
  | ID '+' INT
  ;

a+3匹配第三条
a+b匹配第二条,忽略b字母
如果不加EOF,会尽量匹配,如果没有匹配的则返回
如果加上EOF,出错则不会立即返回

config : element*; // can "match" even with invalid input.
file : element* EOF; // don't stop early. must match all input
posted @ 2016-11-29 13:35  zhangshihai1232  阅读(1190)  评论(0)    收藏  举报