前言
仅生成给出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. C# 定时器事件(设置时间间隔,间歇性执行某一函数,控制台程序)

    定时器事件代码 static void Main(string[] args) { Method(); #region 定时器事件 Timer aTimer = new Timer(); aTimer ...

  2. sdut 2605 A^X mod P

    http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=2605 这个题卡的是优化,直观解法是在求x^y时 ...

  3. Python邮件脚本

    def sendmail(): import smtplib from email.mime.text import MIMEText from email.utils import formatad ...

  4. tab模块

    #!/usr/bin/env python # python startup file import sys import readline import rlcompleter import ate ...

  5. wechat

  6. async = require('async')

    var mongoose = require('mongoose'), async = require('async'); mongoose.connect('localhost', 'learn-m ...

  7. nginx查看配置文件nginx.conf路径

      当你执行 nginx -t 得时候,nginx会去测试你得配置文件得语法,并告诉你配置文件是否写得正确,同时也告诉了你配置文件得路径:  # nginx -t nginx: the configu ...

  8. 对项目的测试--Resharper

    初学 这里做个记录. 1:安装后,Resharper会用他自己的英文智能提示,替换掉 vs2010的智能提示,所以我们要换回到vs2010的智能提示 2:快捷键.是使用vs2010的快捷键还是使用 R ...

  9. java中利用JFrame创建窗体 【转】

    1. 一个简单的swing public class Test(){ public static void main(String[] args){ JFrame frame = new JFrame ...

  10. ButterKnife的配置

    1.打开settings选择Plugins 安装 安装完成后会提示重启AS. 2.在build.gradle文件中添加: compile 'com.jakewharton:butterknife:7. ...