Attributes与Actions
采用Listener或者Visitor的方式,回调都是在解析完成之后才被执行的,有些时候,我们需要在解析的过程中执行一些动作,这个动作称为Action。使用Action的理由有三个
- 减少Listener里面的代码量,让Listener更加简洁
- 高效,一边解析一边执行,不会浪费资源
- 有些语法依赖于上下文,可以在Action里面处理,会更方便一些
这种方式耦合性太高了,少用
使用Action完成四则运算
grammar Expr;
@header {
package tools;
import java.util.*;
}
@parser::members {
/** "memory" for our calculator; variable/value pairs go here */
Map<String, Integer> memory = new HashMap<String, Integer>();
int eval(int left, int op, int right) {
switch ( op ) {
case MUL : return left * right;
case DIV : return left / right;
case ADD : return left + right;
case SUB : return left - right;
}
return 0;
}
}
stat: e NEWLINE {System.out.println($e.v);}
| ID '=' e NEWLINE {memory.put($ID.text, $e.v);}
| NEWLINE
;
e returns [int v]
: a=e op=('*'|'/') b=e {$v = eval($a.v, $op.type, $b.v);}
| a=e op=('+'|'-') b=e {$v = eval($a.v, $op.type, $b.v);}
| INT {$v = $INT.int;}
| ID
{
String id = $ID.text;
$v = memory.containsKey(id) ? memory.get(id) : 0;
}
| '(' e ')' {$v = $e.v;}
;
MUL : '*' ;
DIV : '/' ;
ADD : '+' ;
SUB : '-' ;
ID : [a-zA-Z]+ ; // match identifiers
INT : [0-9]+ ; // match integers
NEWLINE:'\r'? '\n' ; // return newlines to parser (is end-statement signal)
WS : [ \t]+ -> skip ; // toss out whitespace
$e.v这种就是访问这个对象里面的属性了,这个v的来源是底下e这个语法所返回的内容
使用Acton完成CSV语法
grammar CSV;
@header {
import java.util.*;
}
/** Derived from rule "file : hdr row+ ;" */
file
locals [int i=0]
: hdr ( rows+=row[$hdr.text.split(",")] {$i++;} )+
{
System.out.println($i+" rows");
for (RowContext r : $rows) {
System.out.println("row token interval: "+r.getSourceInterval());
}
}
;
hdr : row[null] {System.out.println("header: '"+$text.trim()+"'");} ;
/** 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);
}
}
// rule row cont'd...
: field
{
if ($columns!=null) {
$values.put($columns[$col++].trim(), $field.text.trim());
}
}
( ',' field
{
if ($columns!=null) {
$values.put($columns[$col++].trim(), $field.text.trim());
}
}
)* '\r'? '\n'
;
field
: TEXT
| STRING
|
;
TEXT : ~[,\n\r"]+ ;
STRING : '"' ('""'|~'"')* '"' ; // quote-quote is an escaped quote
Keyword语法
grammar Keywords;
@lexer::header { // place this header action only in lexer, not the parser
import java.util.*;
}
// explicitly define keyword token types to avoid implicit def warnings
tokens { BEGIN, END, IF, THEN, WHILE }
@lexer::members { // place this class member only in lexer
Map<String,Integer> keywords = new HashMap<String,Integer>() {{
put("begin", KeywordsParser.BEGIN);
put("end", KeywordsParser.END);
put("if", KeywordsParser.IF);
put("then", KeywordsParser.THEN);
put("while", KeywordsParser.WHILE);
}};
}
stat: BEGIN stat* END
| IF expr THEN stat
| WHILE expr stat
| ID '=' expr ';'
;
expr: INT | CHAR ;
ID : [a-zA-Z]+
{
if ( keywords.containsKey(getText()) ) {
setType(keywords.get(getText())); // reset token type
}
}
;
/** Convert 3-char 'x' input sequence to string x */
CHAR: '\'' . '\'' {setText( String.valueOf(getText().charAt(1)) );} ;
INT : [0-9]+ ;
WS : [ \t\n\r]+ -> skip ;