语法制导翻译概述
什么是语法制导翻译
- 语法制导翻译使用CFG(上下文无关文法)来引导对语言的翻译,是一种面向文法的翻译技术
语法制导翻译的基本思想
-
如何表示语义信息?
- 为CFG中的文法符号设置语义属性,用来表示语法成分对应的语义信息
-
如何计算语义属性?
- 文法符号的语义属性值是用与文法符号所在产生式(语法规则)相关联的语义规则来计算的
- 对于给定的输入串x ,构建x的语法分析树,并利用与产生式(语法规则)相关联的语义规则来计算分析树中各结点对应的语义属性值
两个概念
- 将语义规则同语法规则(产生式)联系起来要涉及两个概念
- 语法制导定义(Syntax-Directed Definitions, SDD)
- 语法制导翻译方案 (Syntax-Directed Translation Scheme , SDT )
语法制导定义(SDD)
- SDD是对CFG的推广
- 将每个文法符号和一个语义属性集合相关联
- 将每个产生式和一组语义规则相关联,这些规则用于计算该产生式中各文法符号的属性值
- 如果X是一个文法符号,a是X的一个属性,则用X.a表示属性a在某个标号为X的分析树结点上的值
【例】语法制导SDD
- 每一个产生式对应一个语义规则
- 第二个产生式的语义规则:设置非终结符T的语义属性type,当产生式右部是终结符int时,非终结符T的语义属性type就是int;当产生式右部是终结符real时,非终结符T的语义属性type就是real
- L和L1是同一个非终结符,用下角标区分L在不同位置的出现
- 第一个产生式的语义规则:设置非终结符L的语义属性inh,它由T的type属性定义
- 第四个产生式的语义规则:设置非终结符L1的语义属性inh,它由L的inh属性定义
语法制导翻译方案(SDT)
- SDT是在产生式右部嵌入了程序片段的CFG,这些程序片段称为语义动作
- 按照惯例,语义动作放在花括号内
【例】语法制导翻译方案SDT
- 一个语义动作在产生式中的位置决定了这个动作的执行时间
- 第一个产生式语义动作是指,当分析出T之后,就可以使用T的type属性的值来计算L的inh属性值
SDD与SDT
- SDD
- 是关于语言翻译的高层次规格说明
- 隐蔽了许多具体实现细节,使用户不必显式地说明翻译发生的顺序
- SDT
- 可以看作是对SDD的一种补充,是SDD的具体实施方案
- 显式地指明了语义规则的计算顺序,以便说明某些实现细节
语法制导定义SDD
- 语法制导定义SDD是对CFG的推广
- 将每个文法符号和一个语义属性集合相关联
- 将每个产生式和一组语义规则相关联,用来计算该产生式中各文法符号的属性值
- 文法符号的属性
- 综合属性 (synthesized attribute)
- 继承属性 (inherited attribute)
综合属性
- 在分析树的结点N上的非终结符A的综合属性只能通过N的子结点或 N本身的属性值来定义
【例】综合属性
- E是E1、+、T的父节点
- E的val属性值是由它的两个子节点E1和T的属性值来定义的,因此,val是E的一个综合属性
- 终结符可以具有综合属性
- 终结符的综合属性值是由词法分析器提供的词法值,因此在SDD中没有计算终结符属性值的语义规则
继承属性
在分析树结点N上的非终结符A的继承属性只能通过N的父结点、N的兄弟结点或N本身的属性值来定义
【例】继承属性
- T和L互为兄弟
- L的inh属性是由T的type属性定义的,因此,inh是L的一个继承属性
- 终结符没有继承属性
- 终结符从词法分析器处获得的属性值被归为综合属性值
【例】带有综合属性的SDD
digit
是一个终结符,它的lexval
综合属性由词法分析器提供E
的val
综合属性由它的子节点E1
和T
提供T
的val
综合属性由它的子节点T1
和F
提供F
的val
综合属性由终结符digit
提供- 语义规则通常写成表达式的形式,用来计算文法符号的属性值,但是有时候,语义规则的目的就是产生一个副作用,比如说打印一个运算结果或者是符号表。这时,语义规则通常被写成过程调用的形式,可以把它看成是用来定义产生式左部文法符号L的综合属性的规则,因为它用到了子节点E的属性值
- 有了这个SDD,对于输入句子的分析树,就可以根据语义规则计算树中各个节点的对应的属性值
- 假如现在输入的符号串是
3*5+4n
,右侧就是它的分析树- 注释分析树( Annotated parse tree ) :每个节点都带有属性值的分析树
- 树中标记为
digit
的三个节点,对应输入串中的3个常数,它们的综合属性值是由词法分析器提供的词法值,分别是3
、5
、4
- 树中标记为
F
的三个节点,是对第七个产生式的应用,根据第七个产生式的语义规则可以看出F
的val
值是由子节点digit
的lexval
值定义,所以它们的属性值分别为3
、5
、4
- 树中标记为
T
的两个节点,是对第五个产生式的应用,根据第五个产生式的语义规则可以看出T
的val
值是由子节点E
的val
值定义,所以它们的属性值分别为3
、4
- 树中标记为
T
的一个节点,是对第四个产生式的应用,根据第四个产生式的语义规则可以看出T
的val
值是由它的两个子节点T
和E
的val
值相乘定义,所以它的属性值分别为15
- 树中标记为
E
的一个节点,是对第三个产生式的应用,根据第三个产生式的语义规则可以看出E
的val
值是由子节点T
的val
值定义,所以它的属性值分别为15
- 树中标记为
E
的一个节点,是对第二个产生式的应用,根据第二个产生式的语义规则可以看出E
的val
值是由它的两个子节点E
和T
的val
值相加定义,所以它的属性值分别为19
- 树中标记为
E
的一个节点,是对第一个产生式的应用,根据第一个产生式的语义规则可以看出该规则用于产生一个副作用,也就是说打印E
的val
值,那么,可以把它看作对产生式左部符号L
的虚综合属性进行定义的规则,因为它用到了子节点E
的val
值
【例】带有继承属性的SDD
- 由第一个和第四个产生式可以看出,
L
的inh
属性由它的兄弟节点T
和父节点L
定义,因此,inh
是L
的一个继承属性 - 在第四个和第五个产生式的语义规则中,存在副作用
addtype
,其中终结符id
的综合属性lexeme
是由词法分析器提供的词法值,它表示,构成标识符id
的字符序列。这个副作用的功能是在符号表中为标识符id.lexeme
创建一条记录并且将标识符id
的类型设置为L.inh
所表示的类型。由于在这个副作用中使用了L
的子节点id
和它自身的属性值,因此,这个副作用可以看作是定义产生式左部符号L
的虚综合属性的一条语义规则 - 有了这个SDD,对于输入句子的分析树,就可以根据语义规则计算树中各个节点的对应的属性值
- 树中标记为
id
的三个节点,对应输入串中的3个终结符,它们的综合属性值是由词法分析器提供的词法值,分别是a
、b
、c
- 根节点
D
是对第一个产生式的应用,根据第一个产生式的语义规则可以看出L
的inh
属性是由兄弟节点T
的type
属性定义的 - 节点
L
是对第四个产生式的应用,根据第四个产生式的第一条语义规则可以看出L
的inh
属性是由父节点L
的inh
属性定义的;- 根据第四个产生式的第二条语义规则用来生成一个副作用,也就是说,在符号表中为
id.lexme
,也就是c
创建一条记录,并且将c
的类型设置为L.inh
所代表的类型real
- 根据第四个产生式的第二条语义规则用来生成一个副作用,也就是说,在符号表中为
- ……
- 树中标记为
属性文法
- 一个没有副作用的SDD有时也称为属性文法
- 属性文法的规则仅仅通过其它属性值和常量来定义一个属性值
【例】属性文法
SDD的求值顺序
- SDD为CFG中的文法符号设置语义属性
- 对于给定的输入串x,应用语义规则计算分析树中各结点对应的属性值
- 按照什么顺序计算属性值?
- 语义规则建立了属性之间的依赖关系,在对语法分析树节点的一个属性求值之前,必须首先求出这个属性值所依赖的所有属性值
依赖图
- 依赖图是一个描述了分析树中结点属性间依赖关系的有向图
- 分析树中每个标号为X的结点的每个属性a都对应着依赖图中的一个结点
- 如果属性X.a的值依赖于属性Y.b的值,则依赖图中有一条从Y.b的结点指向X.a的结点的有向边
【例】注释分析树对应的依赖图
- 将继承属性放在语法分析树中对应结点的左边;将综合属性放在语法分析树中对应结点的右边
- 由第一个产生式的语义规则可以看出:
L
的in
属性依赖T
的type
属性,因此,依赖图中有一条T
结点的type
属性指向L
结点的in
属性的一条有向边 - 由第四个产生式的第二条语义规则用来生成一个副作用,也就是说,在符号表中将
id.lexme
所代表的标识符的类型设置为L.in
的类型,可以看作是定义产生式左部的非终结符L
的一个虚综合属性的规则。在依赖图中为L
设置一个虚综合属性,该属性由它的子节点id
的lexeme
属性和它自身的in
属性,因此,在依赖图中分别从L
的in
属性和id
的lexeme
属性引出一条指向L
的虚综合属性的有向边
属性值的计算顺序
- 可行的求值顺序是满足下列条件的结点序列N1, N2, … , Nk :如果依赖图中有一条从结点Ni到 Nj 的边(Ni→Nj), 那么i < j(即:在节点序列中,Ni 排在Nj 前面)
- 这样的排序将一个有向图变成了一个线性排序,这个排序称为这个图的拓扑排序(topological sort)
【例】拓扑排序
-
先计算出1234,然后由4计算出5,再由53计算出6,由5计算出7,由72计算出8,……
-
因为1234结点互不依赖,所以内部顺序可随意调换,……
-
对于只具有综合属性的SDD ,可以按照任何自底向上的顺序计算它们的值
-
对于同时具有继承属性和综合属性的SDD,不能保证存在一个顺序来对各个节点上的属性进行求值
【例】同时具有继承属性和综合属性的SDD
-
s是A的综合属性,s由A的子节点B的i属性定义;i是B的继承属性,i由B的父节点A的s属性定义
-
依赖图中存在环,无法确定一个顺序来对各个节点上的属性进行求值
-
从计算的角度看,给定一个SDD,很难确定是否存在某棵语法分析树,使得SDD的属性之间存在循环依赖关系
-
幸运的是,存在一个SDD的有用子类,它们能够保证对每棵语法分析树都存在一个求值顺序,因为它们不允许产生带有环的依赖图
-
不仅如此,接下来介绍的两类SDD可以和自顶向下及自底向上的语法分析过程一起高效地实现
- S-属性定义 (S-Attributed Definitions, S-SDD)
- L-属性定义 (L-Attributed Definitions, L-SDD)
S-属性定义和L-属性定义
S-属性定义
- 仅仅使用综合属性的SDD称为S属性的SDD,或S-属性定义、S-SDD
【例】S-属性定义
- 如果一个SDD是S属性的,写就是说它的结点的属性值都依赖与它的子结点的属性值,那么,就可以按照语法分析树节点的任何自底向上顺序来计算它的各个属性值
- S-属性定义可以在自底向上的语法分析过程中实现
L-属性定义
- L-属性定义(也称为L属性的SDD或L-SDD)的直观含义:
- 在一个产生式所关联的各属性之间,依赖图的边可以从左到右,但不能从右到左(因此称为L属性的,L是Left的首字母)
- L-SDD的正式定义:
- 一个SDD是L-属性定义,当且仅当它的每个属性要么是一个综合属性,要么是满足如下条件的继承属性:
- 假设存在一个产生式A→X1X2…Xn,其右部符号Xi (1 ≤ i ≤ n)的继承属性仅依赖于下列属性:
- 父节点A的继承属性
- 产生式中Xi左边的符号 X1, X2, … , Xi-1 的属性
- Xi本身的属性,但Xi 的全部属性不能在依赖图中形成环路
- 假设存在一个产生式A→X1X2…Xn,其右部符号Xi (1 ≤ i ≤ n)的继承属性仅依赖于下列属性:
- 一个SDD是L-属性定义,当且仅当它的每个属性要么是一个综合属性,要么是满足如下条件的继承属性:
- 每个S-属性定义都是L-属性定义
【思考】为什么右部符号Xi (1 ≤ i ≤ n)的继承属性仅依赖于父节点A的继承属性,而不能是父节点A的综合属性?
- 因为父节点A的的综合属性s依赖于子节点的继承属性i和综合属性s,如果子节点Xi的继承属性i再依赖于父节点的综合属性s,就会造成循环依赖,因此,子节点的继承属性只能依赖于父节点的继承属性而不能依赖于父节点的综合属性
【例】L-属性定义
- 第一个产生式的第一个语法规则中T’的inh属性仅依赖于它左边的F的属性值,不违反L-SDD的定义中对继承属性的限制
- 第二个产生式的第一个语法规则中T’的inh属性仅依赖于它的父节点T’的inh属性和它左边的F的val属性,不违反L-SDD的定义中对继承属性的限制
【例】非L-属性定义
- 第二个产生式的第二个语法规则中Q的i属性依赖于它右边的R的s属性,违反了L-SDD的定义中对继承属性的限制
语法制导翻译方案SDT
- 语法制导翻译方案(SDT)是在产生式右部中嵌入了程序片段(称为语义动作)的CFG
- SDT可以看作是SDD的具体实施方案
- 本节主要关注如何使用SDT来实现两类重要的SDD,因为在这两种情况下,SDT可在语法分析过程中实现
- 基本文法可以使用LR分析技术,且SDD是S属性的
- 基本文法可以使用LL分析技术,且SDD是L属性的
【例】SDT
S-SDD转换为SDT
- 将一个S-SDD转换为SDT的方法:将每个语义动作都放在产生式的最后
- 因为S-SDD中的所有属性都是综合属性,只有当所有的子节点都分析完毕以后才可以计算父节点的综合属性,所以语义动作都放在产生式的最右边
【例】将S-SDD转换为SDT
S-SDD的SDT实现
- 如果一个S-SDD的基本文法可以使用LR分析技术(自底向上),那么它的SDT可以在LR语法分析过程中实现
【例】S-SDD的SDT实现
- 该S-SDD的基础文法是LR文法,可以用LR分析技术进行语法分析
- 当归约发生时执行相应的语义动作
扩展的LR语法分析栈:
将语义动作中的抽象定义式改写成具体可执行的栈操作:
- A的综合属性a由它的子节点X、Y、Z的x、y、z属性定义
- 因为语义动作在产生式的最后,所以当把x、y、z归约为A时,该语义动作将执行
- 归约前,栈顶top存放Z对应的记录,top-1存放Y对应的记录,top-2存放X对应的记录;归约以后,XYZ出栈,A进栈,存放在X所在的位置,top-2成为新的栈顶top
【例】S-SDD的SDT实现中的语法分析栈变化
- 初始时,语法分析栈中状态为0,符号是栈底标记符$,属性是空
- 输入指针指向输入串中的第一个符号3,状态0遇到数字digit,采取移入动作后进入状态5,d的lexval属性值是由词法分析器提供的词法值,也就是3
- 状态5是一个归约状态:将d归约成F,根据第七个产生式的语义规则:F的属性值等于digit的属性值,所以属性栈不做操作,只将状态5和符号d出栈即可,然后将F进栈;状态0遇到F,进入状态3:
- 状态3是一个归约状态:将F归约成T,属性栈不做操作,将状态3和符号F出栈,T进栈,状态0遇到T,进入状态2:
- 输入指针指向输入串中的下一个符号
*
,状态2遇到*
,*
入栈,状态7入栈,无属性值:
- 输入指针指向输入串中的下一个符号5,状态7遇到digit,d入栈、状态5入栈,属性值5;归约:d出栈、状态5出栈,F入栈;状态7遇到F,状态10入栈;
- 状态10归约:将T的属性值3与F的属性值5相乘作为T的属性值15,状态2、7、10出栈,符号T、
*
、F
出栈,属性5出栈,符号T入栈,状态0遇到T,状态2入栈:
- ……
- 最终结果:
L-SDD转换为SDT
- 将计算某个非终结符号A的继承属性的动作插入到产生式右部中紧靠在A的本次出现之前的位置上
- 将计算一个产生式左部符号的综合属性的动作放置在这个产生式右部的最右端
【例】L-SDD转换为SDT
L-SDD的SDT实现
- 如果一个L-SDD的基本文法可以使用LL分析技术(自顶向下),那么它的SDT可以在LL(自顶向下)或LR(自底向上)语法分析过程中实现,LL分析具体又分为两种清况:
- 在非递归的预测分析过程中进行语义翻译
- 在递归的预测分析过程中进行语义翻译
【例】L-SDD的SDT实现
- 因为具有相同左部T’的产生式2和3的SELECT集互不相交,所以该S-SDD的基础文法是LL文法,可以用LR分析技术进行语法分析
在非递归的预测分析过程中进行翻译
扩展语法分析栈:
- 因为继承属性是在它即将出现时计算,综合属性是当它的所有子节点都分析完毕以后才可以计算,所以将继承属性和综合属性分开存放
- A的继承属性就存放在它本身的记录中;A的综合属性存放在它本身的记录之下
【例】在非递归的预测分析过程中(自顶向下)进行翻译
- 将6个语义动作分别起名叫a1-a6
- 初始时刻,语法分析栈中只有文法的开始符号T,因为T没有继承属性,所以T记录的属性字段为空;因为T有综合属性,所以T记录的下一个字段Tsyn的属性字段值为val;
- 输入指针指向输入串中的第一个符号3,根据第一个产生式,将T替换成F{a1}T'{a2}:T出栈,因为T的综合属性val要等即将进栈的子节点T’分析完毕才可以计算,所以Tsyn不出栈,F{a1}T'{a2}进栈:
- 栈顶非终结符F替换成digit{a6},digit的属性值由词法分析器提供,也就是3,与当前输入指针指向的3匹配成功,digit出栈;由语义动作a6可知,要用digit的属性值计算E的属性值,所以digit出栈前,要将属性值备份到{a6}字段中:
- 执行{a6}的栈操作:将digit的属性值给F的val属性,{a6}出栈,由语义动作a1可知,要用F的属性值计算T’的属性值,所以F出栈前,要将属性值备份到{a1}字段中,执行{a1}的栈操作:将F的属性值给T’的inh属性,{a1}出栈:
- 输入指针指向输入串中的下一个符号
*
,选择产生式2,栈顶非终结符T’替换成*{a3}T1'{a4}
,由语义动作a3可知,要用T’的属性值计算T1’的属性值,所以T’出栈前,要将T’的属性值存到{a3}中:
- 栈顶
*
与当前输入指针指向的*
匹配成功,*
出栈;输入指针指向输入串中的下一个符号5,选择产生式4,栈顶非终结符F替换成digit{a6},digit的属性值由词法分析器提供,也就是5,与当前输入指针指向的5匹配成功,digit出栈;由语义动作a6可知,要用digit的属性值计算E的属性值,所以digit出栈前,要将属性值备份到{a6}字段中,输入指针指向输入串中的下一个符号$
:
-
执行{a6}的栈操作,……
-
最终结果:
- 分析栈中的每一个记录都对应着一段执行代码
- 综合记录出栈时,要将综合属性值复制给后面特定的语义动作
- 变量展开时(即变量本身的记录出栈时),如果其含有继承属性,则要将继承属性值复制给后面特定的语义动作
【例】一个SDT一旦确定,这个SDT中各个符号对应的执行代码也就确定
在递归的预测分析过程中进行翻译
- 为每个非终结符A构造一个函数,A的每个继承属性对应该函数的一个形参,函数的返回值是A的综合属性值。对出现在A产生式中的每个文法符号的每个属性都设置一个局部变量
- 非终结符A的代码根据当前的输入决定使用哪个产生式
- 与每个产生式有关的代码执行如下动作:从左到右考虑产生式右部的词法单元、非终结符及语义动作
- 对于带有综合属性x的词法单元 X,把x的值保存在局部变量X.x中;然后产生一个匹配 X的调用,并继续输入
- 对于非终结符B,产生一个右部带有函数调用的赋值语句c := B(b1 , b2 , …, bk),其中, b1 , b2 , …, bk是代表B的继承属性的变量,c是代表B的综合属性的变量
- 对于每个动作,将其代码复制到语法分析器,并把对属性的引用改为对相应变量的引用
【例】在递归的预测分析过程中进行翻译
L-SDD的自底向上翻译
- 给定一个以LL文法为基础的L-SDD,可以修改这个文法,并在LR语法分析过程中计算这个新文法之上的SDD
- 首先构造SDT,在各个非终结符之前放置语义动作来计算它的继承属性, 并在产生式后端放置语义动作计算综合属性
- 对每个内嵌的语义动作,向文法中引入一个标记非终结符来替换它。每个这样的位置都有一个不同的标记,并且对于任意一个标记M都有一个产生式M→ε
- 如果标记非终结符M在某个产生式A→α{a}β中替换了语义动作a,对a进行修改得到a' ,并且将a’关联到M→ε 上。动作a'
- 将动作a需要的A或α中符号的任何属性作为M的继承属性进行复制
- 按照a中的方法计算各个属性,但是将计算得到的这些属性作为M的综合属性