一般情况下我们最好不要这么做,这会使应用代码与语法代码混淆。但是在某些情况下也有她的好处:
- 简易的:有时候仅仅是简答的几个动作,避免创建监听器或访问器。
- 有效的:在特定的资源应用中,我们不想浪费时间和内存去构建一个解析树。
- 判断解析:在很少的情况下,我么需要依靠之前的输入流来判断解析属性。一些语法需要构建符号表来意识到将来的不同情况的输入,依赖与标识符是否是类型或者方法。
演示
grammar Rows;
@parser::members { // add members to generated RowsParser
int col;
public RowsParser(TokenStream input, int col) { // custom constructor
this(input);
this.col = col;
}
}
file: (row NL)+ ;
row
locals [int i=0] //locals 本地变量表
: ( STUFF
{ //行为 ,通过$来取出本地变量 ,col是全局变量
$i++;
if ( $i == col ) System.out.println($STUFF.text);
}
)+
;
TAB : '\t' -> skip ; // match but don't pass to the parser
NL : '\r'? '\n' ; // match and pass to the parser
STUFF: ~[\t\r\n]+ ; // match any chars except tab, newline
利用语法规则的外部行为
<header>
public class <grammarName> Parser extends Parser {
<members>
...
}
package/import statements is header and
class members like fields and methods is members
如果要指定header行为,我们用@header,如果要注入字段和方法,我们用@memers。
在一个合并的parser/lexer语法中,他们命名的行为应用在parser和lexer中。如果要限制生成的行为在parser或lexer中,我们可以用 @parser::name 或者 @lexer::name.
通常情况下,$x.y表示y属性的x元素,x可以是token的引用或者rule引用。
expr例子
/** Grammar from tour chapter augmented with actions */
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
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>() <!--0-->;
}
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 ;
csvexample
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' ;" */
//参数 columns ,返回值values
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