语义分析
语义分析也称为类型检查,上下文相关分析
负责检查程序(抽象语法树)的上下文相关的属性
- 这是具体语言相关的,典型的情况包括
-
- 变量在使用前先进行声明
- 每个表达式都有合适的类型
- 函数调用和函数的定义一致
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YX2SxtEw-1642042136401)(…/picture/12.png)]
符号表
用来存储程序中的符号(标识符)相关属性信息,是进行上下文语义合法性检查的依据。
-
类型
-
作用域
-
访问控制信息
-
目标代码生成阶段:符号表是对符号名进行地址分配的依据。
-
变量符号由它被定义的存储类别和被定义的位置等来确定将来被分配的存储位置
-
- 存储类别:公共区,文件静态区,函数静态区,运行时动态区。
- 变量次序:变量在某个区中所处的具体位置。
符号常见属性
- 符号的名字
-
- 常用作查询相应表项的关键字,一般不重名
- 重名标识符定义将按照该标识符在程序中的作用域和可见性规则进行相应的处理
- 在允许操作重载的语言中,函数名、过程名是可以重名,通过参数个数和类型以及返回值类型来区别。
- 符号的类型:
-
- 常量符号、变量符号、函数/过程符号、类型属性:决定该符号所标识的内容在存储空间的存储格式,还决定了可以对其实施的运算操作。
- 符号的存储类别和存储分配信息:
-
- 存储类别:静态数据区,动态数据区,代码区,栈区
- 存储分配信息:该符号数据单元的大小(字节数)、相对某个存储基地址的偏移位置。
- 符号的作用域和可见性
- 其他属性
符号表的实现方式:哈希表:查找 O ( 1 ) O(1) O(1),平衡树:节约空间,查找 O ( log N ) O(\log N) O(logN)
创建符号表的时间:至少在静态语义分析之前创建
- 可以在语法分析的同时创建:如果词法分析器被语法分析器调用,则符号表的相应表项可以由语法分析器写入,可以在同时写入更多的属性信息,并且知道是否是正在声明的符号
- 在语法分析之后,语义检查之前创建:容易获得符号的更多属性,也容易处理同一作用域内声明的符号。
符号表处理作用域:一张表的方式
- 进入作用域,插入元素
- 退出作用域,删除元素
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I1WyJVLL-1642042136405)(…/picture/13.png)]
采用符号表构成的栈:
-
进入作用域时,插入新的符号表
-
退出作用域时,删除栈顶符号表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BtBSHNk3-1642042136406)(…/picture/14.png)]
语义检查
检查程序结构(控制结构和数据结构)的一致性或完整性
- 控制流检查:控制流语句必须使控制转移到合法的地方
-
- 例如:一个跳转语句会使控制转移到一个由标号指明的后续语句,如果标号没有对应到语句,就出现语义错误
- 唯一性检查:某些对象,如标识符,枚举类型和元素等,在源程序的一个指定上下文范围内只允许定义一次,因此语义分析要确保他们的定义唯一。
- 名字的上下文相关性检查:名字的出现在遵循作用域与可见性前提下应该满足一定的上下文相关性。
-
- 变量在使用前必须声明,在外部不能访问私有变量。
- 类型检查:
-
- 运算的类型检查需要搞清楚是否与给定运算兼容。
- 源程序中使用的标识符是否已声明过或是否与已声明的类型相矛盾。
- 是否返回值的类型与定义的类型相同。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3wJvKWKj-1642042136407)(…/picture/15.png)]
现代的编译器中的语义分析,除了语义分析外,还要负责生成中间代码或目标代码
翻译模式
语义规则:给出了属性计算的定义,没有属性计算的次序等实现细节
翻译模式:适合语法制导语义计算的另一种描述形式(给出使用语义规则进行计算的次序,把实现细节表示出来)
形式上类似于属性文法,但允许由 { } \{\} {}括起来的语义动作出现在产生式右端的任何位置,以此显示的表达属性计算的次序。
- 语法制导定义:是一种抽象的翻译说明,可不涉及翻译的细节,如属性文法
- 语法制导翻译方案:规定了计算次序等的具体实现细节,可以看作是对 S D D SDD SDD的一种补充,是 S D D SDD SDD的具体实现方案。
设计翻译模式的原则:
- 设计翻译模式时,必须保证当某个动作引用一个属性时,它必须是有定义的
- L − L- L−属性文法本身就能确保每个动作不会引用尚未计算出来的属性。
当只需要综合属性时:为每一个语义规则建立一个包含赋值的动作,并把这个动作放在相应的产生式右边的末尾。
既有综合属性又有继承属性:
- 产生式右边符号的继承属性必须在这个符号以前的动作中计算出来
- 一个动作不能引用这个动作右边的符号的综合属性
- 产生式左边非终结符的综合属性只有在它所引用的所有属性都计算出来以后才能计算,计算这种属性的动作通常可放在产生式右端的末尾。
S − S- S−翻译模式
仅含有综合属性
是一种仅涉及综合属性的情形,通常将语义动作集合置于相应产生式右端的末尾。
一般基于自顶向上分析(如 L R LR LR分析)过程,通过增加存放属性值域的语义栈来实现
若假设语义栈由向量 v v v表示,归约前栈顶的位置为 t o p top top,栈上第 i i i个位置对应符号的综合属性值 x x x用 v [ i ] . x v[i].x v[i].x表示
例如:
A → X Y Z { v [ t o p − 2 ] . a : = f ( v [ t o p − 2 ] . x , v [ t o p − 1 ] . y , v [ t o p ] . z ; } A \to XYZ \qquad \{v[top-2].a := f(v[top-2].x, v[top-1].y,v[top].z;\} A→XYZ{v[top−2].a:=f(v[top−2].x,v[top−1].y,v[top].z;}
一般语法分析完成后,语义分析也完成。适合于一遍扫描自下而上分析。
例子:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lfWPE6gw-1642042136408)(…/picture/17.png)]
变换之后:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FWDRl5nM-1642042136410)(…/picture/18.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hthtO26S-1642042136411)(…/picture/26.png)]
L − L- L−翻译模式
既可以包含综合属性,也可以包含继承属性,满足两个条件:
对于每个产生式 A → X 1 X 2 … X n A \to X_1X_2\dots X_n A→X1X2…Xn其每个语义规则中的属性或者是综合属性,或者是 X i X_i Xi是一个继承属性且这个继承属性且这个继承属性仅依赖于:
- 产生式中 X i X_i Xi左边符号 X 1 , X 2 , … , X i − 1 X_1,X_2,\dots,X_{i-1} X1,X2,…,Xi−1的属性
- A A A的继承属性
S − S- S−属性文法一定是 L − L- L−属性文法,因为它只有综合属性
适合于一遍扫描自上而下的分析。当用一个产生式推导或者匹配输入串时,执行语义规则
在递归下降 L L ( 1 ) LL(1) LL(1)分析程序的设计中,每个非终结符都对应一个分析子函数,分析程序从文法开始符号所对应的分析子函数开始执行。
分析子函数根据下一个输入符号确定自顶向下分析过程中应使用的产生式,并根据产生式右端依次出现的符号来设计其行为
- 遇到终结符,判断当前输入的单词符号是否与该终结符相配。若相配,继续读取下一个输入符号,否则,报错。
- 遇到非终结符,调用相应的分析子函数。
分析子函数
- 对非终结符 A A A, A A A的每个继承属性对应该函数的一个形参,函数的返回值是 A A A的综合属性值。
- 根据当前的输入符号决定使用哪个产生式,产生式对应的代码根据右端结构来构造,具体如下:
-
- 若遇到的一个终结符 X X X,将其综合属性的值 x x x保存至专门为 X . x X.x X.x声明的变量;判断当前输入的单词符号是否与该终结符相配,若相配,继续读取下一个字符;否则报错。
- 对于非终结符 B B B,利用对应于 B B B的子函数 P a r s e B Parse B ParseB产生赋值语句 c : = P a r s e B ( b 1 , … , b k ) c:=ParseB(b_1,\dots, b_k) c:=ParseB(b1,…,bk),其中,参量 b 1 , b 2 , … , b k b_1,b_2,\dots,b_k b1,b2,…,bk对应 B B B的各类继承属性,变量 c c c对应 B B B的综合属性;若有多个综合属性,则可使用记录类型的变量
- 若遇到一个语义动作集合,直接复制其中每一语义动作对应的代码,只是注意将属性的访问替换为相应变量的访问。
自顶向下的程序:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-azyZP8pf-1642042136413)(…/picture/19.png)]
MatchToken函数用于判断正在处理的终结符与当前输入符号是否匹配:
不匹配直接报错,若匹配则词法分析程序读入下一个符号。
lookahead是当前扫描的输入符号
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gS7AcJkf-1642042136414)(…/picture/20.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yM0C2VMr-1642042136415)(…/picture/21.png)]
下面翻译模式可用于将二进制无符号定点小数转化为十进制小数
N
→
.
{
S
.
f
:
=
1
}
S
{
p
r
i
n
t
(
S
.
v
a
l
)
}
S
→
{
B
.
f
:
=
S
.
f
}
B
{
S
1
.
f
:
=
S
.
f
+
1
}
S
1
{
S
.
v
a
l
:
=
B
.
v
a
l
+
S
1
.
v
a
l
}
S
→
ε
{
S
.
v
a
l
:
=
0
}
B
→
0
{
B
.
v
a
l
:
=
0
}
B
→
1
{
B
.
v
a
l
:
=
2
−
B
.
f
}
N \to . \quad \{S.f:=1\} \quad S \{print(S.val)\}\\ S \to \{B.f:=S.f\} \quad B \{S_1.f:=S.f+1\} \quad S_1 \{S.val:=B.val+S_1.val\} \\ S \to \varepsilon \quad \{S.val := 0\} \\ B \to 0 \quad \{B.val:=0\} \\ B \to 1 \quad \{B.val:=2^{-B.f}\}
N→.{S.f:=1}S{print(S.val)}S→{B.f:=S.f}B{S1.f:=S.f+1}S1{S.val:=B.val+S1.val}S→ε{S.val:=0}B→0{B.val:=0}B→1{B.val:=2−B.f}
① 产生式左边的属性如果在右边出现,则采用函数带参形式。
② 然后根据产生式一句一句的去推。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lmy9UE8l-1642042136416)(…/picture/22.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6o0xvZiD-1642042136417)(…/picture/23.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KBlHdNaF-1642042136418)(…/picture/24.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-byot5zc8-1642042136420)(…/picture/25.png)]
建立翻译模式
把所有的语义动作都放在产生式的末尾
- 语义动作的执行时机统一
转换方式
- 加入新产生式: M → ε M \to \varepsilon M→ε
- 把嵌入在产生式中的每个语义动作用不用的非终结符 M M M代替,并把这个动作放在产生式 M → ε M\to \varepsilon M→ε的末尾
消除翻译模式中的左递归
语义动作是在相同位置上的符号被展开(匹配成功)时执行的。
为了构造不带回溯的自顶向下语法分析,必须消除文法中的左递归。
当消除一个翻译模式的基本文法的左递归时,同时考虑属性计算
- 适合带综合属性的翻译模式
对于消除左递归中,引入的非终结符。
需要给他增加两个属性
- 继承属性i: R . i R.i R.i表示自上而下扩展到R之前,之前表达式的值
- 综合属性s: R . s R.s R.s表示自下而上把 R R R匹配完之后, R R R之前所有表达式的值和分析完 R R R之后表达式的值
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zRh9jfPc-1642042136421)(…/picture/27.png)]
首先把继承属性进行原来产生式的赋值(原本产生式的右端),然后再通过综合属性给原来产生式左端赋值。
往前走一步,可以把原来产生式的值传递给 R R R的继承属性
再往前一步,把 R R R的综合属性传递给后面表达式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dZWAIg6a-1642042136423)(C:/Users/DELL/AppData/Roaming/Typora/typora-user-images/image-20211130182821525.png)]
PDF16 P50例子
之前表达式的值
- 综合属性s: R . s R.s R.s表示自下而上把 R R R匹配完之后, R R R之前所有表达式的值和分析完 R R R之后表达式的值
[外链图片转存中…(img-zRh9jfPc-1642042136421)]
首先把继承属性进行原来产生式的赋值(原本产生式的右端),然后再通过综合属性给原来产生式左端赋值。
往前走一步,可以把原来产生式的值传递给 R R R的继承属性
再往前一步,把 R R R的综合属性传递给后面表达式
[外链图片转存中…(img-dZWAIg6a-1642042136423)]