类型系统
类型系统是一门语言所有的类型的集合,操作这些类型的规则,以及类型之间怎么相互作用
- 根据领域的需求,设计自己的类型系统的特征
-
设计类型系统
类型:是针对一组数值,以及在这组数值之上的一组操作
定规矩:可以检查施加在数据上的操作是否合法,通过类型检查降低计算出错的概率取舍和权衡
类型都是对象/支持非对象化的基础数据类型
- 字符串是原生数据类型/是一个普通的类
- 是静态类型语言/是动态类型语言
- 静态类型语言(全部或者几乎全部的类型检查是在编译期进行的)
- 程序错误较少(检查类型是否匹配,以及进行缺省的类型转换)
- 性能更高(不需要在运行时再去做类型检查和转换)
- 动态类型语言(类型的检查是在运行期进行的)
- 语言太严格
- 编程效率低(要一遍遍编译)
- 强类型
- 弱类型
- 静态类型语言(全部或者几乎全部的类型检查是在编译期进行的)
- 是否符合这门语言想解决的问题
类型检查、类型推导和类型转换
a = b + 10
类型推导(Type Inference)
如果 b 是一个浮点型,b+10 的结果也是浮点型。如果 b 是字符串型,实际的结果是字符串的连接 ```javascript case ScriptParser.ADD: if (type1 == PrimitiveType.String || type2 == PrimitiveType.String){
} else if (type1 instanceof PrimitiveType && type2 instanceof PrimitiveType){type = PrimitiveType.String;
} else {// 类型“向上”对齐,比如一个int和一个float,取float
type = PrimitiveType.getUpperType(type1,type2);
} break;console.log("operand should be PrimitiveType for additive operation");
private Object add(Object obj1, Object obj2, Type targetType) { Object rtn = null; if (targetType == PrimitiveType.String) { result = String.valueOf(obj1) + String.valueOf(obj2); } else if (targetType == PrimitiveType.Integer) { result = ((Number)obj1).intValue() + ((Number)obj2).intValue(); } else if (targetType == PrimitiveType.Float) { result = ((Number)obj1).floatValue()+ ((Number)obj2).floatValue(); } … return result; }
<a name="lbS8M"></a>
#### S 属性(Synthesized Attribute)
如果一种属性能够从下级节点推导出来,那么这种属性就叫做 S 属性,是通过下级节点和自身来确定的<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/155952/1644497749131-161042a8-8bbf-45df-9f58-76a1dd0937c8.png#clientId=ue5b26832-0c31-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=418&id=uf4e0390b&margin=%5Bobject%20Object%5D&name=image.png&originHeight=418&originWidth=1108&originalType=binary&ratio=1&rotation=0&showTitle=false&size=255754&status=done&style=none&taskId=u7a0a02e7-3b5d-4dcf-91a1-dbbc98f8d43&title=&width=1108)
<a name="KUJSr"></a>
#### I 属性(Inherited Attribute)
AST 中某个节点的属性是由上级节点、兄弟节点和它自身来决定的
```javascript
int a;
variableDeclarators
: typeType variableDeclarator (',' variableDeclarator)*
;
variableDeclarator
: variableDeclaratorId ('=' variableInitializer)?
;
variableDeclaratorId
: IDENTIFIER ('[' ']')*
;
typeType
: (classOrInterfaceType| functionType | primitiveType) ('[' ']')*
;
// Go 语言两种声明变量的方式
var a int = 10 //第一种
a := 10 //第二种
类型检查(Type Checking)
当右边的值计算完,赋值给 a 的时候,要检查左右两边的类型是否匹配
- 赋值语句(检查赋值操作左边和右边的类型是否匹配)
- 变量声明语句(因为变量声明语句中也会有初始化部分,所以也需要类型匹配)
- 函数传参(调用函数的时候,传入的参数要符合形参的要求)
- 函数返回值(从函数中返回一个值的时候,要符合函数返回值的规定)
类型转换(Type Conversion)
如果 a 的类型是浮点型,而右边传过来的是整型,一般要进行缺省的类型转换
强制做类型转换,只有到运行期才能检查出错误// MySQL 自动将'2'转换成了数字
select 1 + '2';
引用消解
在程序里使用变量、函数、类等符号时,我们需要知道它们指的是谁,要能对应到定义它们的地方 ```javascriptinclude
int a = 1;
void main() { a = 2; int a = 3; int b = a; printf(“in func: a=%d b=%d \n”, a, b); }
// output in func: a=3 b=3
<a name="Q0UT0"></a>
### 变量的引用消解
比对变量
<a name="TnTb2"></a>
### 函数的引用消解
比对函数名称,参数和返回值(可以叫函数原型,或者函数的类型)<br />需要返回值、参数个数、每个参数的类型都能匹配
面向对象编程语言:当一个参数需要一个对象时,程序中提供其子类的一个实例也可以
```javascript
class MyClass1{} //父类
class MyClass2 extends MyClass1{} //子类
MyClass1 obj1;
MyClass2 obj2;
function fun(MyClass1 obj){} //参数需要父类的实例
fun(obj2); //提供子类的实例
强类型编程语言:考虑某个实参是否能够被自动转换成形参所要求的类型(需要 double 类型的地方,传一个 int 也可以)
命名空间:
play.PlayScriptCompiler.Compile() //Java语言
play::PlayScriptCompiler.Compile() //C++语言
左值和右值
左值
a + 3;
a = 3;
第3行应该取出 a 的地址,或者说 a 的引用,然后用赋值操作把 3 这个值写到 a 的内存地址。这时,我们说取出来的是 a 的左值(L-value)
- 赋值表达式的左边
- 带有初始化的变量声明语句中的变量
- 当给函数形参赋值的时候
- 一元操作符: ++ 和 ––
- 其他需要改变变量内容的操作
判断表达式是否能生成合格的左值:
出现在赋值语句左边的,必须是能够获得左值的表达式
- 一个变量
- 一个类的属性
-
右值
属性计算
属性计算是做上下文分析,或者说语义分析的一种算法
它的变量定义是哪个(这就引用到定义该变量的 Symbol)
- 它的类型是什么
- 它的作用域是什么
- 这个节点求值时,是否该返回左值
- 能否正确地返回一个左值
- 它的值是什么
属性文法(Attribute Grammar)
正则文法,上下文无关文法
属性文法:在上下文无关文法的基础上做了一些增强,使之能够计算属性值 ```javascript // 上下文无关文法表达加法和乘法运算的例子 add → add + mul add → mul mul → mul * primary mul → primary primary → “(“ add “)” primary → integer
// 对 value 属性进行计算的属性文法 add1 → add1 + mul [ add1.value = add2.value + mul.value ] add → mul [ add.value = mul.value ] mul1 → mul2 primary [ mul1.value = mul2.value primary.value ] mul → primary [ mul.value = primary.value ] primary → “(“ add “)” [ primary.value = add.value ] primary → integer [ primary.value = strToInt(integer.str) ] ``` 特点:它会基于语法规则,增加一些与语义处理有关的规则
语义分析过程
类型和作用域解析
把自定义类、函数和和作用域的树都分析出来
可以使用在前,声明在后
类型的消解
把所有出现引用到类型的地方都消解掉
变量声明、函数参数声明、类的继承等等
引用的消解和 S 属性的类型的推导
对所有的变量、函数调用,都跟它的定义关联起来,并完成所有的类型计算
做类型检查
做一些语义合法性的检查
- break 只能出现在循环语句中
- 某个函数声明了返回值一定要有 return 语句