前言
仅生成给出true/false的识别器是没有多大用处的,自然的就有在识别过程中遇到某一结构时执行一段代码、存储该结构中信息的想法。
ANTLR提供了在文法中嵌入属性和动作超级混合“文法”,可以生成内部表示AST或模板;当然如果直接输出部分结构识别结果的话动作也可以应付。
 
内容
基本与原文第6章一致
属性和动作
1 文法动作
2 Token属性
3 预定义规则属性
4 属性作用域
5 在动作中引用属性
 
属性和动作
用几个实例予以说明,动作中语言均是Java。
[ex1]动作可以在规则还没结束的位置
decl : type ID {System.out.println($ID.text);}';';
type : 'int' | 'float';
[ex2]动作放置在规则结束位置
decl : type ID ';' {System.out.println("var " + $ID.text+":"+$type.text+";");};
type : 'int' | 'float';
[ex3]属性标签,区分同名规则
decl : type ID ';' {System.out.println("var " + $ID.text+":"+$type.text+";");}
        | t=ID id=ID ';' {System.out.println("var " + $id.text+":"+$t.text+";");};
[ex4]规则中出现* +
decl : type ids+=ID (',' ids+=ID)* ';'; //ids是ID token的List
[ex5]规则参数和返回值
decl : type declarator[$type.text] ';' ;//使用规则参数
declarator[String typeText]
    : '*' ID {"var " + $ID.text+":^"+$typeText+";")}
       | ID {System.out.println("var " + $ID.text+":"+$typeText+";")};

field : d=decl ';'
{System.out.println("type=)+$d.type+", vars="+$d.vars);};//使用规则返回值
decl returns[String type, List vars]
: t=type ids+=ID (',' ids+=ID)* {$type=$t.text;$vars=ids;}
[ex6]规则间通信:共享变量
@members{String methodName;}//文法内全局作用域变量
method : type ID {methodName=$ID.text;} body ;
body : '{' statement+ '}' ;
statement : decl {...methodName...} ';'//引用变量
| ... ;

method scope {String name;}//规则作用域变量
: type ID {$method::name=$ID.text;} body ;
body : '{' statement+ '}' ;
statement : decl {...$method::name...} ';'//引用变量
| ... ;
1 文法动作
动作是用目标语言编写的嵌入在文法中的代码片段。
 
命名全局动作
 名称  说明
 header  生成的代码中类定义之前的代码,常是包定义和包导入语句
 memebers  定义实例变量和方法
 rulecatch  动作中语法错误的默认catch语句
 synpredgate  句法谓词开关
全局动作的作用域(scope)是lexer,parser或treeparser。
 
文法规则中嵌入的命名动作 
 名称  说明
 init  放置解析规则的代码执行前的代码
 after  放置解析规则的代码执行后的代码
 catch  放置解析规则的代码出现异常的处理代码
 finally  放置解析规则的代码出现异常的最终处理代码
 
sample:
parser grammar T;
@header{package p;}
@members{
int i;
public TParser(TokenStream input, int foo){
this(input);
i = foo;
} 

}
a[int x] returns [int y]
@init {int z=0;}
@after {System.out.println("after matching rule, before finally");}
: {<<action1>>} A {<<action2>>}
;
catch[RecognitionException re] {
System.err.println("error");
}
finally {<<do-this-no-matter-what-happened>>}
2 Token属性
Token作为lexer提交给parser的最小单元,在动作中可以引用的Token属性有
 属性  类型  说明
 text  String  token对应的文本,调用Token#getText()
 type  int  token的类型(正整数),调用Token#getType()
 line  int  token所在行(从1计数),调用Token#getLine()
 pos  int  该行中token首字符的位置(从0计数),调用Token#getCharPositionInLine()
 index  int  token流中该token的索引(从0计数),调用Toekn#getTokenIndex()
 channel  int  token的channel号码,两个值Token.DEFAULT_CHANNEL, Token.HIDDEN_CHANNEL
 tree  Object  构建AST时,该属性指向依据token创建的树节点
引用方式$label.attribute,label是token的标签;$label引用token本身。
 
例外:在lexer规则中,有些label不是token,而是字符。
sample:
lexer grammar T;
R : a='c' b='hin' c=. {$a, $b.text, $c};// a,c都不是token,b是token
 
3 预定义规则属性
预定义属性通常是只读的,唯一的例外是生成AST时在after动作中可以设置tree和st属性。
用$attribute或$enclosingRuleName.attribute引用。
 
预定义的parser规则属性
 属性 类型  说明 
 text  String  匹配规则开始直至$text表示式求值时的文本,包含了hidden channel中token的文本
 start  Token  非hidden channel中匹配该规则的第一个token
 stop  Token  非hidden channel中匹配该规则的最后一个token
 tree  Object  规则计算出的AST,通常是重写规则的结果。引用当前规则时,仅在after动作中可用
 st  StringTemplate  规则计算出的模板,通常是重写规则的结果。引用当前规则时,仅在after动作中可用
 
预定的lexer规则属性
 属性 类型  说明 
 text  String  从匹配最外层规则的第一个token开始到当前位置的文本
 type  int  包围规则的token类型
 line  int  该规则的第一个字符所在行号(从1计数)
 pos  int  该规则的第一个字符在所在行中的位置(从0计数)
 channel  int  该规则所在channel
 
预定义的tree grammar规则属性
 属性 类型  说明 
 text  String  该规则匹配的第一个节点开始推导出的文本
 start  Object  第一个匹配该规则的树节点
 st  StringTemplate  规则计算出的模板,通常是重写规则的结果。引用当前规则时,仅在after动作中可用

4 属性作用域
什么是动态作用域(dynamic scoping)
sample:
void foo(){int x=0; bar();}
void bar(){int y=x;}

调用链中的方法可以访问之前定义的局部变量

 
属性扮演规则间通信中间记录的角色,ANTLR提供了两种属性作用域:全局作用域和规则内作用域。
 
规则内作用域
sample:
代码[1]规则内作用域
 
全局作用域
sample:
代码[2]全局作用域
 
5 在动作中引用属性
ANTLR会对动作中带$和%前缀的表达式做特殊处理:填入相应值/代码。
%引用的是模板表达式,留在其他笔记中说明。
 名称  说明
 $tokenRef  token本身引用
ID {$ID}(ELSE stat)?{if($ELSE!=null)...}
 $tokenRef.attr  token属性引用
id=ID {$id.text} INT {$INT.line}
 $listLabel  由+=操作符标识的标签,表示一个List
ids+=ID (',' ids+=ID)* {$ids}
 $ruleRef  规则有动态作用域且无歧义的情况下,parser/tree grammar中规则才可这样引用。该表达式是Stack
$block.size()
 $ruleRef.attr  规则属性
e=expr {$e.value, $expre.tree}
 $lexerRuleRef  lexer规则引用,是个Token
(DIGIT {$DIGIT, $DIGIT.text})+
 $attr  规则返回值、参数或预定义属性
r[int x] returns[Token t]:{$t=$start; $x};
 $enclosingRule.attr  规则返回值、参数或预定义属性的全限定名
r[int x] returns[Token t]:{$r.t=$r.start; $r.x};
 $globalScopeName  全局动态作用域引用
$symbols.size()
 $x::y  动态作用域x中属性y引用
$CScope::symbols
 $x[-1]::y 动态作用域x前一个作用域中属性y引用
$block[-1]::symbols
 $x[-i]::y  动态作用域x前i个作用域中属性y引用
$block[-i]::symbols
 $x[i]::y  动态作用域Stack中从栈底第i个作用域中属性y引用(从0计数)
$block[2]::symbols
 $x[0]::y  动态作用域Stack中栈底作用域中属性y引用(从0计数)
$block[0]::symbols
 
 
 
代码
[1]规则内作用域
说明:变量已定义,在嵌套代码块中跟踪变量定义所在层次,输出未定义变量及其层次
文法定义
grammar T;

@members{
int level = 0;
boolean isDefined(String variable){
boolean result = false;
    //注:不要将中文注释放到文法定义文件中,至少在我的环境中是这样
    //这里索引i从level-1的原因是:代码块(block)层次从1计数,而block对应的Stack从0计数
for(int i=level-1; i>=0; i--){
if($block[i]::symbols.contains(variable)){
System.out.println(variable + " found in nesting level " + (i+1));
result = true;
}
}
return result;
}
}

prog : block;

block
scope{
List symbols;
}
@init{
$block::symbols = new ArrayList();
level++;
}
@after{
System.out.println("symbols level " + level + " = " + $block::symbols);
level--;
}
: '{'decl* stat+'}'
;

decl : 'int' ID {$block::symbols.add($ID.text);}';'
;

stat : ID '=' INT ';'
{

System.err.println("undefined variable level  " + level + ": "+ $ID.text);
}
}
| block
;

ID  : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*;
INT : '0'..'9'+;
WS  :   ( ' '| '\t'| '\r'| '\n') {$channel=HIDDEN;};
测试输入
{
int i;
int j;
i = 0;
{
int i;
int x;
x = 5;
}
x = 3;
}
[2]全局作用域sample
说明:变量已定义,在方法和代码块中跟踪变量定义所在层次名称,输出未定义变量及其层次名称
文法定义
grammar T;
scope CScope{
String name;
List symbols;
}

@members{
  boolean isDefined(String variable){
        boolean result = false;
          for(int i=$CScope.size()-1; i>=0; i--){
                if($CScope[i]::symbols.contains(variable)){
                          System.out.println(">" + variable + " found in " +$CScope[i]::name);
                          result = true;
                  }
          }
          return result;
}
 }

prog scope CScope;
@init {
$CScope::symbols = new ArrayList();
$CScope::name = "global";
}
@after {
System.out.println("global symbols = " + $CScope::symbols);
}
: decl* func*;
func scope CScope;
@init {
$CScope::symbols = new ArrayList();
}
@after {
System.out.println("function " + $CScope::name + "()'s symbols = " +$CScope::symbols);
}
: 'void' ID{$CScope::name=$ID.text;} '(' ')' '{' decl* stat+ '}'
;
block scope CScope;
@init {
$CScope::symbols = new ArrayList();
$CScope::name = "level " + $CScope.size();
}
@after{
System.out.println("code block level " + $CScope.size() + " symbols = " +$CScope::symbols);
}
: '{'decl* stat+'}'
;
//注:不要将中文注释放到文法定义文件中,至少在我的环境中是这样
//查看生成的代码,decl()中调用了Stack#peek(),
//即,会使用动态作用域Stack中当前的作用域,最终还是使用block(引用该decl)的作用域
decl : 'int' ID {$CScope::symbols.add($ID.text);}';'
;
stat : ID '=' INT ';'
{
if(!isDefined($ID.text)){
System.err.println("undefined variable in  " + $CScope::name + ": "+ $ID.text);
}
}
| block
;

ID  : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*;
INT : '0'..'9'+;
WS  :   ( ' '| '\t'| '\r'| '\n') {$channel=HIDDEN;};
测试输入
int i;
void f() {
     int i;
     {
int i;
i = 2;
     }
     i = 1;
}
void g(){
     i = 0;
     x = 3;
}

最新文章

  1. 灾难 bzoj 2815
  2. Java for LeetCode 206 Reverse Linked List
  3. HTML入门的简单学习
  4. .htaccess设置静态资源缓存(即浏览器缓存)
  5. Apache httpd和JBoss构建高可用集群环境
  6. DB2_SQL_常用知识点&amp;实践
  7. sql中临时表的创建和使用【本文转自多人博客】
  8. mac下安装redis
  9. Spark的TorrentBroadcast:概念和原理
  10. Js中JSON.stringify()与JSON.parse()与eval()详解及使用案例
  11. STL 源代码剖析 算法 stl_algo.h -- partition
  12. 夏令营讲课内容整理 Day 7.
  13. Windows下python2.7安装64位mysqlclient
  14. excel表格公式无效、不生效的解决方案及常见问题、常用函数
  15. 使用freemarker生成xml模板
  16. Java高并发 -- 线程池
  17. (Alpha)Let&#39;s-版本发布说明
  18. mongodb系列~ mongodb慢语句(3)
  19. windows7 安装虚拟机,xsheel连接不上的问题,记录一下
  20. asp.net 将数据导成Excel文件

热门文章

  1. MySQL为数据表的指定字段插入数据
  2. 基于SOCK4网络协议的代理服务器端代码示例
  3. linux下安装vtune_amplifier_xe_2015_update4
  4. Linux-守护进程的实现
  5. 恢复drop数据
  6. [处理器、单片机]ARM
  7. SharePoint 2013 开发——获取用户配置文件属性内容(User Profile)
  8. (三)获取iphone的IMSI
  9. Tab的键的妙用
  10. &lt;转&gt;用thinkPHP实现验证码的功能