本章学习一种新的作用域,叫做数据聚合作用域(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. 戏说java多线程之CyclicBarrier(循环栅栏)的CyclicBarrier(int parties)构造方法

    CyclicBarrier是JDK 1.5 concurrent包出现的一个用于解决多条线程阻塞,当达到一定条件时一起放行的一个类.我们先来看这样一个简单的需求. 现在我有一个写入数据的类,继承Run ...

  2. CF908D Arbitrary Arrangement

    题目大意: 给定三个数\(k\) , \(p_a\) , \(p_b\) 每次有\(\frac{p_a}{p_a+p_b}\)的概率往后面添加一个'a' 每次有\(\frac{p_b}{p_a+p_b ...

  3. Android中Activity.this,getApplicationContext(),getBaseContext()和this详解

    转自:http://android.tgbus.com/Android/tutorial/201103/346236.shtml 在使用Android上下文参数的时候经常分不清Activity.thi ...

  4. LTS和其他解决方案的比较(官方)

    主要根据LTS支持的几种任务(实时任务.定时任务.Cron任务,Repeat任务)和其他一些 开源框架在应用场景上做比较. 实时任务,实时执行 这种场景下,当任务量比较小的时候,单机都可以完成的时候. ...

  5. NancyFX 第六章 视图引擎

    正如其他的Web工具包,Nancy也有视图的概念,用来描述在浏览器上看到的输出 视图的定义 你可能没有之前没有接触过"视图"的概念,或是仅仅是从其他工具包例如ASP.NET MVC ...

  6. WebStorm配置Vue开发环境

    虽然最新版的前端开发利器WebStorm支持了Vue,但是大部分人的WebStorm依然是默认不支持Vue的老版本(比如之前的我),所以需要手动添加WebStorm对Vue的支持.要想让WebStor ...

  7. 设计模式——简单工厂模式(C++实现)

    #include <iostream> #include <string> using namespace std; class COperator { public: ; p ...

  8. ajax 图片上传

    html 部分: <form action="" id='myForm' enctype="multipart/form-data"> <di ...

  9. Django+xadmin打造在线教育平台(二)

    三.xadmin后台管理 3.1.xadmin的安装 django2.0的安装(源码安装方式): https://github.com/sshwsfc/xadmin/tree/django2 把zip ...

  10. NGINX详解

    目录 1.    基础概念    1 2.    版本选择    1 3.    服务安装    1 4.    模块说明    1 5.    配置说明    1 5.1    目录结构    1 ...