语句的语义取决于其语法结构和相关符号;前者说明了了要“做什么”,后者说明了操作“什么对象”。所以即使语法结构正确的,如果被操作的对象不合法,语句也是不合法的。语言一般有很多语义规则,有些是运行时的(dynamic semantics),比如“不能除以零”、“不能越界访问数组”;有些是编译时的(static semantics)。运行时&编译时的界限取决于具体的语言,python是动态类型的语言,所有的值编译时都不会指定类型,解释器在运行时检查类型和执行语义规则;C++则是另一个极端,所有的值必须有静态的类型,运行时不做检查;java既有静态类型检查,又在运行时检查类型。无论动态的或静态的类型检查,只要能阻止不相容的类型操作,语言就是“类型安全”的。

本章讲述了如何实施静态类型规则,将会讨论一下几种模式:

  • Pattern 20 Computing Static Expression Types,保证类型安全的第一步是计算表达式的类型;
  • Pattern 21 Automatic Type Promotion, 将操作数的类型提升至相同或相容的类型;
  • Pattern 22 Enforcing Static Type Safety,检查类型相容性,保证类型安全;
  • Pattern 23 Enforcing Polymorphic Type Safety, 面向对象类型语言里面的对象类型相容,多态的引用类型;

    下面分别讲述这些模式,以类c语言的规则为样例。

Pattern 20 Computing Static Expression Types

展示了在显示声明类型的语言,比如C,如何计算表达式的类型

下面的表格说明了类型计算规则。

Subexpression Result Type
true,false boolean
!«expr » boolean
f(«args») 函数f的返回类型
«expr» bop «expr» bop代表二元运算符,因为两侧的操作数的类型是一致的,所以可以选取左侧操作数的类型为表达式类型
«expr» relop «expr» boolean,relop是代表关系操作符

实现

假设我们已经构建了AST,和scope tree,进行类型计算就是另一轮AST遍历,可以通过Tree Pattern Matcher技术来寻找表达式节点并计算类型。

具体的实现代码请参考原书。

Pattern 21 Automatic Type Promotion

描述了如何自动、安全地提升数学运算操作数的类型

类型提升将单个运算符的所有操作数提升至相同类型或相容类型,这本质上是CPU对操作数的要求。编程语言能够自动地转换类型,只要“转换“不丢失信息;比如4可以安全地转换成4.0,而4.5不能转换成4;这种安全的类型转换被称之为”类型提升”,因为它拓宽了类型。

这里有一个简单的方式描述类型提升规则;首先将类型按最“窄”到最“宽”进行排序,然后如果i&ltj,我们可以进行从TYPEi到TYPEj的提升。

为了实现类型提升,需要两个函数,第一个是根据运算符和运算数类型返回结果类型;第二个从运算符和目标类型判断是否需要对操作数执行类型提升。

resultType(type1, op, type2)
resultType(char, "<", int) == boolean
resultType(char, "+", int) == int
resultType(boolean, "<", boolean) == void promoteFromTo(type, op, destination-type)
promoteFromTo(char, "+", int) == int
promoteFromTo(int, "+", int) == null

结果void意味着该表达式不合法,null意味着不需要类型转换。按这个规则,表达式’a’+3+4.2的类型提升可以表示为(float)((int)’a’+3)+4.2,AST如图:



每个节点都知道自己的求值类型,提升类型。

具体的java实现代码,请参考原书。

Pattern 22 Enforcing Static Type Safety

通过静态检查,发现表达式或语句里面类型不相容的类型

概括起来,类型相容包含两个方面:

  • 对操作数类型,操作符是有定义的,即resultType(operandtype1, op, operandtype2) != void;
  • 如果我们需要一个类型T的值,那么提供的值必须是T或可以被提升为T。

在Pattern 21的基础上增加类型检查不是困难,只要出现void都意味着类型不相容;对于点操作符,函数调用,return语句等需要特殊处理。

具体的实现代码,请参考原书。

Pattern 23 Enforcing Polymorphic Type Safety

检查对象引用赋值的类型相容性

对象类型引用赋值的相容性判断如下:

public class PointerType extends Symbol implements Type {
public boolean canAssignTo(Type destType) {
// if not a pointer, return false
if ( !(destType instanceof PointerType) ) return false;
// What type is the target pointing at?
Type destTargetType = ((PointerType)destType).targetType;
Type srcTargetType = this.targetType;
// if this and target are object pointers, check polymorphism
if ( destTargetType instanceof ClassSymbol &&
this.targetType instanceof ClassSymbol )
{
ClassSymbol thisClass = (ClassSymbol)srcTargetType;
ClassSymbol targetClass = (ClassSymbol)destTargetType;
// Finally! Here it is: the polymorphic type check :)
return thisClass.isInstanceof(targetClass);
}
// not comparing object pointers; types we point at must be the same
// For example: int *p; int *q; p = q;
return srcTargetType == destTargetType;
}
}
/** Return true if 'ancestor' is this class or above in hierarchy */
public boolean isInstanceof(ClassSymbol ancestor) {
ClassSymbol t = this;
while ( t!=null ) {
if ( t == ancestor ) return true;
t = t.superClass;
}
return false;
}

《Language Implementation Patterns》之 强类型规则的更多相关文章

  1. 《Language Implementation Patterns》之 解释器

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

  2. 《Language Implementation Patterns》之 数据聚合符号表

    本章学习一种新的作用域,叫做数据聚合作用域(data aggregate scope),和其他作用域一样包含符号,并在scope tree里面占据一个位置. 区别在于:作用域之外的代码能够通过一种特殊 ...

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

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

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

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

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

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

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

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

  7. 《Language Implementation Patterns》之 符号表

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

  8. Saga的实现模式——进化(Saga implementation patterns – variations)

    在之前的几个博客中,我主要讲了两个saga的实现模式: 基于command的控制者模式 基于事件的观察者模式 当然,这些都不是实现saga的唯一方式.我们甚至可以将这些结合起来. 发布者——收集者 回 ...

  9. Saga的实现模式——观察者(Saga implementation patterns – Observer)

    https://lostechies.com/jimmybogard/2013/03/11/saga-implementation-patterns-observer/ 侵删. NServiceBus ...

随机推荐

  1. 初识lucene(想看代码的跳过)

    最早是在百度贴吧里看到的lucene这个名称,只知道跟搜索引擎有关,因为工作中一直以来没有类似的需求,所以没有花时间学习这方面的知识. 刚过完年,公司不忙,自己闲不住把<Netty权威指南> ...

  2. [NOI2006]神奇口袋

    题面在这里 题意 开始时袋中有\(t\)种小球,第\(i\)种小球有\(t_i\)个,之后每次等概率取出一个球,第\(i\)次取球时观察这个球的颜色\(c_i\)放回并向袋中加入\(d\)个颜色为\( ...

  3. [Luogu3455][POI2007]ZAP-Queries

    BZOJ(权限题) Luogu 题目描述 Byteasar the Cryptographer works on breaking the code of BSA (Byteotian Securit ...

  4. BZOJ1001 狼抓兔子

    最小割 代码 # include <bits/stdc++.h> # define IL inline # define RG register # define Fill(a, b) m ...

  5. Kruskal重构树(货车运输)

    ... 和Kruskal生成树一样 本来是u,v连一条f的边 现在变成新建一个点,点权为f,u v都像它连无边权的边 (实际上应该是u的根和v的根) 这样树有一些性质: 1.二叉树 2.原树与新树两点 ...

  6. ES2015 类中的静态方法

    在ES2015中,终于不用用函数原型来实现类系统,可以直接使用关键字class,下面是对class的静态属性的研究: 举例:一个Node类,每一个Node类之间都可以建立从属关系,每一个Node实例下 ...

  7. Kendo UI ASP.Net MVC 实现多图片及时显示加上传(其中有借鉴别人的代码,自己又精简了一下,如有冒犯,请多原谅!)

    View: <div class="demo-section k-content"> @(Html.Kendo().Upload() .Name("files ...

  8. WPF自学入门(八)WPF窗体之间的交互

    今天我们一起来看一下WPF窗体之间的交互-窗体之间的传值.有两个窗体,一个是父窗体,一个是子窗体.要将父窗体的文本框中的值传递给子窗体中的控件.我们该怎么实现? 接下来我们一起来实现窗体之间的传值,在 ...

  9. Problem : 1002 ( A + B Problem II )

    经验总结:一定要注意输出的格式,字符的空格,空行,一定要观察清楚.如本题的最后一个输出结果后面没有空行.最后代码实现的时候需要判断一下,代码如下 !=n) cout<<endl; Prob ...

  10. cookie 使用方法

    //写cookies setCookie: function(name, value) { let days = 1 let exp = new Date() exp.setTime(exp.getT ...