本章学习一种新的作用域,叫做数据聚合作用域(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》之 数据聚合符号表的更多相关文章

  1. 《Language Implementation Patterns》之 符号表

    前面的章节我们学会了如何解析语言.构建AST,如何访问重写AST,有了这些基础,我们可以开始进行"语义分析"了. 在分析语义的一个基本方面是要追踪"符号",符号 ...

  2. 《Language Implementation Patterns》之 解释器

    前面讲述了如何验证语句,这章讲述如何构建一个解释器来执行语句,解释器有两种,高级解释器直接执行语句源码或AST这样的中间结构,低级解释器执行执行字节码(更接近机器指令的形式). 高级解释器比较适合DS ...

  3. 《Language Implementation Patterns》之 构建语法树

    如果要解释执行或转换一段语言,那么就无法在识别语法规则的同时达到目标,只有那些简单的,比如将wiki markup转换成html的功能,可以通过一遍解析来完成,这种应用叫做 syntax-direct ...

  4. 《Language Implementation Patterns》之 强类型规则

    语句的语义取决于其语法结构和相关符号:前者说明了了要"做什么",后者说明了操作"什么对象".所以即使语法结构正确的,如果被操作的对象不合法,语句也是不合法的.语 ...

  5. 《Language Implementation Patterns》之 增强解析模式

    上一章节讲述了基本的语言解析模式,LL(k)足以应付大多数的任务,但是对一些复杂的语言仍然显得不足,已付出更多的复杂度.和运行时效率为代价,我们可以得到能力更强的Parser. Pattern 5 : ...

  6. 《Language Implementation Patterns》之访问&重写语法树

    每个编程的人都学习过树遍历算法,但是AST的遍历并不是开始想象的那么简单.有几个因素会影响遍历算法:1)是否拥有节点的源码:2)是否子节点的访问方式是统一的:3)ast是homogeneous或het ...

  7. 《Language Implementation Patterns》之 语言翻译器

    语言翻译器可以从一种计算机语言翻译成另外一种语言,比如一种DSL的标量乘法axb翻译成java就变成a*b:如果DSL里面有矩阵运算,就需要翻译成for循环.翻译器需要完全理解输入语言的所有结构,并选 ...

  8. Django 08 Django模型基础3(关系表的数据操作、表关联对象的访问、多表查询、聚合、分组、F、Q查询)

    Django 08 Django模型基础3(关系表的数据操作.表关联对象的访问.多表查询.聚合.分组.F.Q查询) 一.关系表的数据操作 #为了能方便学习,我们进入项目的idle中去执行我们的操作,通 ...

  9. iOS 符号表恢复 & 逆向支付宝

    推荐序 本文介绍了恢复符号表的技巧,并且利用该技巧实现了在 Xcode 中对目标程序下符号断点调试,该技巧可以显著地减少逆向分析时间.在文章的最后,作者以支付宝为例,展示出通过在 UIAlertVie ...

随机推荐

  1. 安裝pycharm

    一路按照這個教程走下來的.大體無誤. http://www.jianshu.com/p/042324342bf4 除了激活碼那裏,已經被cancel了,查找了很多辦法,最後發現衹要換成三個選項之一的 ...

  2. 【BZOJ3671】【NOI2014】随机数据生成器(贪心)

    [BZOJ3671][NOI2014]随机数据生成器(贪心) 题面 BZOJ 题解 前面的模拟 真的就是语文阅读理解题目 理解清楚题目意思 然后就会发现要求的就是一个贪心 从小往大枚举,检查当前数能不 ...

  3. 【CJOJ P1957】【NOIP2010冲刺十模拟赛】数字积木

    [NOIP2010冲刺十模拟赛]数字积木 Description 小明有一款新式积木,每个积木上都有一个数,一天小明突发奇想,要是把所有的积木排成一排,所形成的数目最大是多少呢? 你的任务就是读入n个 ...

  4. [THUWC 2017]在美妙的数学王国中畅游

    bzoj5020 \[答案误差只要小于 10^{-7}\] 题解 Taylor展开式: \[若f(x)的n阶导数在[a, b]内连续,则f(x)在x_{0}\in[a, b]可表示为\] \[f(x) ...

  5. JAVA面试题二

    JAVA 综合面试题 2007-08-12 目录 Java面试题整理 9 Java面向对象 9 1. super()与this()的区别? 9 2. 作用域public,protected,priva ...

  6. mac 配置虚拟主机

    http://www.upwqy.com/details/4.html 编辑httpd.conf文件,输入命令: vim /etc/apache2/httpd.conf 编辑httpd-vhosts. ...

  7. Storm 入门的Demo教程

    Strom介绍 Storm是Twitter开源的分布式实时大数据处理框架,最早开源于github,从0.9.1版本之后,归于Apache社区,被业界称为实时版Hadoop.随着越来越多的场景对Hado ...

  8. JavaScript ES6 的let和const

    1 作用域和提升 1.1 作用域(Scope) 某个变量名或者函数名,在某个程序片段中是否可见或者可访问,如果是,那么这个程序片段就是这个变量名或者函数名的作用域.比如: var name = &qu ...

  9. [转]Ubuntu Linux 安装 .7z 解压和压缩文件

    [转]Ubuntu Linux 安装 .7z 解压和压缩文件 http://blog.csdn.net/zqlovlg/article/details/8033456 安装方法: sudo apt-g ...

  10. .NET Core开源API网关 – Ocelot中文文档

    Ocelot是一个用.NET Core实现并且开源的API网关,它功能强大,包括了:路由.请求聚合.服务发现.认证.鉴权.限流熔断.并内置了负载均衡器与Service Fabric.Butterfly ...