前言
仅生成给出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. static详解

    static关键字用来修饰属性.方法,称这些属性.方法为静态属性.静态方法. static关键字声明一个属性或方法是和类相关的,而不是和类的某个特定的实例相关,因此,这类属性或方法也称为“类属性”或“ ...

  2. “Unable to execute dex: Multiple dex files”如何解决?

    遇到报错: [2014-02-13 17:27:03 - Dex Loader] Unable to execute dex: Multiple dex files define Lcom/kkdia ...

  3. 佳佳的魔法药水 (vijos 1285)

    题目大意: 给出N种药水的价格,然后给出一些形如A B C 的关系,表示 A药水+B药水 可以组合出 C药水(保证 A+B 不会得到多种药水). 要求得到1号药水的最少花费和相应的方案数. N< ...

  4. 数列F[19] + F[13]的值

    已知数列如下:F[1]=1, F[2]=1, F[3]=5,......,F[n] =F[n-1] + 2*F[n-2],求F[19] + F[13]? #include <stdio.h> ...

  5. Spark运行环境的安装

    scala-2.9.3:一种编程语言,下载地址:http://www.scala-lang.org/download/    spark-1.4.0:必须是编译好的Spark,如果下载的是Source ...

  6. 【NOIP模拟题】【二分】【倍增】【链表】【树规】

    3 计算几何3.1 题意描述花花对计算几何有着浓厚的兴趣.他经常对着平面直角坐标系发呆,思考一些有趣的问题.今天,他想到了一个十分有意思的题目:首先,花花会在x 轴正半轴和y 轴正半轴分别挑选n 个点 ...

  7. javascript作用域(Scope),简述上下文(context)和作用域的定义

    网页制作Webjx文章简介:这篇文章将正面解决这个问题:简述上下文(context)和作用域的定义,分析可以让我们掌控上下文的两种方法,最后深入一种高效的方案,它能有效解决我所碰到的90%的问题. 作 ...

  8. Codeforces 235C

    题目大意: 给定一个字符串,接下来再给n个字符串,求原字符串中含有多少个当前给定字符串的循环同构体的字符串的个数 以初始字符串构建后缀自动机,在自动机上前进的时候,比如当前需要匹配的字符串为aba,到 ...

  9. DotNetBar v12.4.0.2 Fully Cracked

    更新信息: http://www.devcomponents.com/customeronly/releasenotes.asp?p=dnbwf&v=12.4.0.2 如果遇到破解问题可以与我 ...

  10. [转]shell基本算术运算

    from:http://www.cnblogs.com/yfanqiu/archive/2012/05/10/2494031.html#undefined shell程序中的操作默认都是字符串操作,在 ...