《Language Implementation Patterns》之 数据聚合符号表
本章学习一种新的作用域,叫做数据聚合作用域(data aggregate scope),和其他作用域一样包含符号,并在scope tree里面占据一个位置。
区别在于:作用域之外的代码能够通过一种特殊的表达式user.name来访问数据成员;以下两个模式分别描述非面向对象语言和面向对象语言的数据聚合作用域。
- Pattern 18, Symbol Table for Data Aggregates,描述了如何定义和访问简单的数据聚合,比如C struct;
- Pattern 19, Symbol Table for Classes, 描述了如何处理拥有基类、包含方法的数据聚合。
struct和class非常相似,都是一个符号,自定义类型,一个作用域;最大的区别在于class有一个superclass,相当于一个外围嵌套作用域。、
struct scope
先看一个struct的例子:
// start of global scope
struct A {
int x;
struct B { int y; };
B b;
struct C { int z; };
C c;
};
A a;
void f()
{
struct D {
int i;
};
D d;
d.i = a.b.y;
}
对应的scope tree如下:

stuct定义了一个scope,该scope的外围scope就是定义所处的scope;在struct内部,我们像之前一样寻找符号,因此int符号最终引用的是全局的int类型;对于像expr.x这样的表达式,要确定expr的类型,然后在类型对应的scope里面寻找x。使问题变得复杂的地方在于,表达式expr.x在寻找x的时候,只能严格地在对应类型的scope里面寻找,而不能像普通名字一样,继续在外围scope寻找。
class scope
class可以有一个superclass,这就使得class可能有两个父scope,一个是通常的外围嵌套的scope,一个是superclass对应的scope。在做符号引用的时候,追随哪个父scope都有可能。
// start of global scope
class A {
public:
int x;
void foo()
{ ; }
};
class B : public A {
int y;
void foo()
{
int z = x + y;
}
};
上面类定义对应的scope tree如下:

从Scope B指向Scope A的箭头是横向的,这里表达的意思是,ClassB和ClassA的scope处在同一层级。在成员函数内部,面向对象语言一般会优先使用superClass这条路径来搜寻符号。
前置引用
class里面允许提前引用一个名字定义:
class A {
void foo() { x = 3; } // forward reference to field x
int x;
};
解决这个问题,可以使用多轮次处理AST的方式,第一轮只进行符号定义,第二轮再处理符号引用。
但是又会引入另一个问题,有时候前置引用一个名字是非法的,比如以下的C++代码:
// globals
int main() {
x = y; // shouldn't see global x or local y; ERROR!
int y;
}
int x;
解决这个问题的方法是引入token index的概念,在引用local和global scope的场合,如果引用处的token index小于定义处的token index,那么该引用是非法的。
Pattern 18, Symbol Table for Data Aggregates
关于如何实现简单的struct scope,还是用类似上一章的表格来表示,仅添加几项如下:
| Upon | Actions |
| struct declaration S | def S as a StructSymbol object in the current scope and push it as the current scope. |
| Member access «expr».x | Compute the type of «expr» using the previous rule and this one recursively. Ref x only in that type’s scope, not in any enclosing scopes. |
Pattern 19, Symbol Table for Classes
上面说过需要多轮次的AST访问才能构建好Class的scope tree;AST node里面记录对应的scope信息以便下一轮访问。
为了看清楚这种联系,看一段简单的代码:
int main() {
int x;
x = 3;
}
最终的scope tree和AST如下图所示,省略了中间步骤:

x对应的VariableSymbol包含了一个指向定义AST node的指针;x的引用节点和定义节点都指向了同一个Symbol。
Class的scope有两个可能的父scope,因此在scope的类里面定义了两个方法:
/** Where to look next for symbols; superclass or enclosing scope */
public Scope getParentScope();
/** Scope in which this scope defined. For global scope, it's null */
public Scope getEnclosingScope();
对于非Class的scope来说,getParentScope返回的就是EnclosingScope;对于Class的scope来说,getParentScope默认返回superClassScope,如果没有则返回EnclosingScope。
Class的scope tree构造需要两轮AST遍历,第一轮构造出struct的Scope Tree结构:
| Upon | Actions |
| Class declaration C | 在当前scope构造ClassSymbol,sym,并push sym入栈成为新的current scope,sym的def字段指向对应类名ID的AST节点,ID AST节点的symbol字段指向sym;sym的superclasss scope指向自身 |
第二轮将superclass scope指针设置好:
| Upon | Actions |
| Class declaration C | 设t为C的superclasss的ID AST节点. 引用t所定义的scope, 得到sym. 设置t.symbol=sym. 设置C对应scope的superclass指针 =sym. |
在class的方法内部访问符号x的规则如下:先在class scope里面寻找,然后顺着superClass链寻找,最后在global scope里面寻找。
通过《expr》.x的方式访问名字x的规则:先在class scope里面寻找,然后顺着superClass链寻找。
具体的实现代码,请参考原书。
《Language Implementation Patterns》之 数据聚合符号表的更多相关文章
- 《Language Implementation Patterns》之 符号表
前面的章节我们学会了如何解析语言.构建AST,如何访问重写AST,有了这些基础,我们可以开始进行"语义分析"了. 在分析语义的一个基本方面是要追踪"符号",符号 ...
- 《Language Implementation Patterns》之 解释器
前面讲述了如何验证语句,这章讲述如何构建一个解释器来执行语句,解释器有两种,高级解释器直接执行语句源码或AST这样的中间结构,低级解释器执行执行字节码(更接近机器指令的形式). 高级解释器比较适合DS ...
- 《Language Implementation Patterns》之 构建语法树
如果要解释执行或转换一段语言,那么就无法在识别语法规则的同时达到目标,只有那些简单的,比如将wiki markup转换成html的功能,可以通过一遍解析来完成,这种应用叫做 syntax-direct ...
- 《Language Implementation Patterns》之 强类型规则
语句的语义取决于其语法结构和相关符号:前者说明了了要"做什么",后者说明了操作"什么对象".所以即使语法结构正确的,如果被操作的对象不合法,语句也是不合法的.语 ...
- 《Language Implementation Patterns》之 增强解析模式
上一章节讲述了基本的语言解析模式,LL(k)足以应付大多数的任务,但是对一些复杂的语言仍然显得不足,已付出更多的复杂度.和运行时效率为代价,我们可以得到能力更强的Parser. Pattern 5 : ...
- 《Language Implementation Patterns》之访问&重写语法树
每个编程的人都学习过树遍历算法,但是AST的遍历并不是开始想象的那么简单.有几个因素会影响遍历算法:1)是否拥有节点的源码:2)是否子节点的访问方式是统一的:3)ast是homogeneous或het ...
- 《Language Implementation Patterns》之 语言翻译器
语言翻译器可以从一种计算机语言翻译成另外一种语言,比如一种DSL的标量乘法axb翻译成java就变成a*b:如果DSL里面有矩阵运算,就需要翻译成for循环.翻译器需要完全理解输入语言的所有结构,并选 ...
- Django 08 Django模型基础3(关系表的数据操作、表关联对象的访问、多表查询、聚合、分组、F、Q查询)
Django 08 Django模型基础3(关系表的数据操作.表关联对象的访问.多表查询.聚合.分组.F.Q查询) 一.关系表的数据操作 #为了能方便学习,我们进入项目的idle中去执行我们的操作,通 ...
- iOS 符号表恢复 & 逆向支付宝
推荐序 本文介绍了恢复符号表的技巧,并且利用该技巧实现了在 Xcode 中对目标程序下符号断点调试,该技巧可以显著地减少逆向分析时间.在文章的最后,作者以支付宝为例,展示出通过在 UIAlertVie ...
随机推荐
- Linux安装jdk1.8和配置环境变量
每次感觉配这个都很简单,但每次都要查一下,毕竟配错一点 后面都比较麻烦,记录一下,方便以后查看. linux 下安装jdk和windows下的安装是一样的,之前在windows安装的时候是先下载压缩包 ...
- Spring Boot Actutaur + Telegraf + InFluxDB + Grafana 构建监控平台
完成一套精准,漂亮图形化监控系统从这里开始第一步 Telegraf是收集和报告指标和数据的代理 它是TICK堆栈的一部分,是一个用于收集和报告指标的插件驱动的服务器代理.Telegraf拥有插件或集成 ...
- Java sax、dom、pull解析xml
-------------------------------------SAX解析xml---------------------------------- >Sax定义 SAX是一个解析速度 ...
- jquery中的事件与应用
mouseover和mouseenter的区别 mouseenter的定义是当鼠标穿过该元素才会被执行,而mouseover是当鼠标穿过该元素或者其子元素皆会触发该事件 mouseleave一般是与m ...
- C++标准库bitset类型(简单使用方法)
转自此人博客 ```cpp #include<bister> using std::bitset; ``` 一句话定义:可自定义位数,用作记录二进制的数据类型. 一,定义和初始化 ```c ...
- POJ 2195 Going Home (费用流)
题面 On a grid map there are n little men and n houses. In each unit time, every little man can move o ...
- 【洛谷3047】[USACO12FEB]附近的牛Nearby Cows
题面 题目描述 Farmer John has noticed that his cows often move between nearby fields. Taking this into acc ...
- eclipse 精确查询
---恢复内容开始--- ctrl+H(一般都是这个,如果无效看你的自定义快捷键) 输入\b 查询的字符串 \b 后面的正则表达式选框必须选
- centos7 升级 git(2.14.3) 版本
下载 wget https://www.kernel.org/pub/software/scm/git/git-2.14.3.tar.gz 安装依赖包 yum install curl-devel ...
- Php显示中文时乱码
<?php echo "你们好哈"; ?> 显示乱码,解决办法 <?php header("content-type:text/html; charse ...