前言
仅生成给出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;
}

ANTLR3完全参考指南读书笔记[05]的更多相关文章

  1. ANTLR3完全参考指南读书笔记[01]

    引用 Terence Parr. The Definitive ANTLR Reference, Building Domain Specific Languages(antlr3 version). ...

  2. ANTLR3完全参考指南读书笔记[06]

    前言 这段时间在公司忙的跟狗似的,但忙的是没多少技术含量的活儿. 终于将AST IR和tree grammar过了一遍,计划明天写完这部分的读书笔记.   内容 1 内部表示AST构建 2 树文法   ...

  3. ANTLR3完全参考指南读书笔记[02]

    前言 程序语言是什么? 用wiki上的描述,程序语言是一种人工设计的语言,用于通过指令与机器交互:程序语言是编程程序的标记,而程序是一种计算或算法的描述.详细介绍和背景信息参考: Programmin ...

  4. ANTLR3完全参考指南读书笔记[08]

    前言 不要让用户被那些“专业术语”吓住! 用心设计的提示和反馈信息是软件设计者的“职业良心”.   内容 1 存在哪些错误? 2 美化错误提示 3 错误恢复策略   1 存在哪些错误? 在DSL语言开 ...

  5. ANTLR3完全参考指南读书笔记[07]

    前言 真正意义上的程序员都很懒,懒的连多余的一行代码也不写. 如果能将底层满手油污的活儿都可以交给别人去做,自己就扮演个智囊团成员的角色,生活会比想象中的还要惬意. 严格的按照指令执行长时间不知疲倦的 ...

  6. ANTLR3完全参考指南读书笔记[04]

    前言 学习框架或第三方库的方法是什么 (1)少量的浏览manual或tutoral,只关注程序所需的特征,再完善其详细内容和特征的认识? (2)花大量的时间研究详细内容,再考虑程序实现? 这是个先有鸡 ...

  7. ANTLR3完全参考指南读书笔记[03]

    前言 文中第4章内容有点多,有点枯燥,但不坚持一下,之前所做的工作就白做了. 再次确认一下总体目标: protege4编辑器中Class Definition中语法解析和错误提示: Java虚拟机规范 ...

  8. 强化学习读书笔记 - 05 - 蒙特卡洛方法(Monte Carlo Methods)

    强化学习读书笔记 - 05 - 蒙特卡洛方法(Monte Carlo Methods) 学习笔记: Reinforcement Learning: An Introduction, Richard S ...

  9. HTTP权威指南读书笔记

    HTTP权威指南笔记 读书有两种境界,第一种境界是将书读薄,另一种是读厚.本篇文章就是HTTP权威指南的读书笔记,算是读书的第一重境界,将厚书读薄.文章对HTTP的一些关键概念做了比较详细的概述,通读 ...

随机推荐

  1. [示例]NSPredicate基础-查询数组中负荷条件的子集

    代码: #import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepo ...

  2. NOIP2009解题报告

    09年的题总体来说 没有难题,但是每道题除了第一题都要认真的慢慢写才能AC, 第一题: R国和S国正陷入战火之中,双方都互派间谍,潜入对方内部,伺机行动. 历经艰险后,潜伏于S国的R国间谍小C终于摸清 ...

  3. Android ScrollView与ViewPager滑动冲突

    前段时间做项目碰到在ScrollView里添加ViewPager,但是发现ViewPager的左右滑动和ScrollView的滑动冲突了,解决这个问题的方法是重写ScrollView. 代码: pub ...

  4. hadoop 8步走

    1.1读取hdfs中的文件.每一行解析成一个<k,v>.每一个键值对调用一次map函数        解析成2个<k,v>,分别是<0, hello you>< ...

  5. IOS开发中--点击imageView上的Button没有任何反应

    点击imageView上的Button没有任何反应:    解决方法:设置图片的userInteractionEnabled为YES,使该imageView可以与用户进行交互

  6. 打饭助手之NABC

    Need: 同学们在早上跑操后要吃早饭,还有中午打饭时人更是多.常常要排很长的队伍,造成时间的浪费,和焦急的等待.因此我们需要错开打饭的高峰期,来避免打饭排队的悲哀. Approach: 通过获取摄像 ...

  7. ASP.NET读取EXCEL文件的三种经典方法(转)

    1.方法一:采用OleDB读取EXCEL文件:  把EXCEL文件当做一个数据源来进行数据的读取操作,实例如下:public DataSet ExcelToDS(string Path) {  str ...

  8. Mapper类/Reducer类中的setup方法和cleanup方法以及run方法的介绍

    在hadoop的源码中,基类Mapper类和Reducer类中都是只包含四个方法:setup方法,cleanup方法,run方法,map方法.如下所示: 其方法的调用方式是在run方法中,如下所示: ...

  9. 有关WAMPSERVER 环境搭建 如何修改端口,MySQL数据库的修改

    环境搭建 http://share.weiyun.com/88896747fedd4e8b19afebea18f7684c 一.修改Apache的监听端口 1.在界面中选Apache,弹出隐藏菜单选项 ...

  10. 为自己的系统定制openstack ceilometer

    一.目的 最近研究了一下ceilometer,目的做一个监控系统,对系统中发生的事件进行处理.ceilometer对openstack各个组件信息的收集方式主要由 推 和  拉 两种. “推”: 就是 ...