语义分析一般是和语法分析组合在一起执行的,语法分析完成前一步语法树分析的构建(调用某个产生式完成一步规约,形成当前的树节点),然后语义分析便接着调用相应产生式配备的语义动作或子程序,完成属性文法所要求的语义动作(比如类型转换或生成中间代码)。所以对于属性文法而言,属性的加工和使用过程便是语义处理的意义。
属性文法
一个属性文法是一个三元组,A=(G,V,F),一个上下文无关文法G;一个属性的有穷集V和关于属性的谓词函数的有穷集F。每个断言与文法的某产生式相联。如果对G中的某一输入串而言(句子),A中的所有断言对该输入串的语法树结点的属性全为真,则该串也是A语言中的句子,如下便是一个关于属性文法的例子
由于产生式中出现2个T,加以区别右边T用T1,T2来代替
E→T1+T2 {T1.t=int AND T2.t=int}//谓词,要求若是符合该产生式,则进行相加的两元素必须都是整型
E→T1orT2 {T1.t=bool AND T2.t=bool}
T→num {T.t∶=int} //属性加工,声明该变量为整型,该标识符数据类型属性被赋值为“整型”
T→true {T.t∶=bool}
T→false {T.t∶=bool}
称t为T的一属性。则这带有属性的方法称为属性文法
属性文法便是为所有标识符配备一些属性的文法,这些属性不仅可以描述设计语言的语法有效补充,又可以为语义分析提供足够的数据支持。在推导语法树的过程中,标识符的属性值也在不断加工并通过赋值规则不断层层传递。
对于产生式A->α都有一套与这相关联的语义规则,每队规则的形式b:=f(c1,c2,…)
(1)如果b是A的一个属性,c1,c2…为右部产生式文法符号的属性或则A的其它属性,则称b为A的综合属性。
(2)如果b是右部产生式文法符号X的属性A的一个属性,c1,c2…为A或产生式右边任务文法符号的属性,则称b为X的继承属性。 注意:
- 非终结符既可以有综合属性也可以有继承属性,文法开始符号没有继承属性。
- 终结符只有综合属性,它由词法分析器提供
综合属性
产生式 | 语义规则 |
---|---|
L->E | print(E.val) |
E->E1 + T | E.val = E1.val + T.val |
E->T | E.val = T.val |
T->T1 * F | T.val = T1.val * F.val |
T->F | T.val = F.val |
F->(E) | F.val = E.val |
F-> digit | F.val = digit.lexval |
- 终结符digit仅有综合属性,lexval值由词法分析程序提供
- 左部E的属性值的计算来自它的右部的非终结符满足条件1,称val为E的综合属性
继承属性
产生式 | 语义规则 |
---|---|
D->TL | L.in=T.type |
T->int | T.type = integer |
L->L1,id | addtype(id.entry,L.in) L1.in = L.in |
L-> id | addtype(id.entry,L.in) |
- T.type = integer,非终结符T,满足条件1,称type为T的综合属性
- L.in=T.type,非终结符L,满足条件1,称in为L的继承属性
- addtype(id.entry,L.in)为把终结符类型信息便当在符号表中
计算语义规则
计算语义规则在属性文法的基础上进行处理的。例有属性文法:
产生式 | 语义规则 |
---|---|
D->TL | L.in=T.type |
T->int | T.type = integer |
L->L1,id | addtype(id.entry,L.in) L1.in = L.in |
L-> id | addtype(id.entry,L.in) |
当输入字符串为“int id1, id2, id3” 时
(1)构造语法树
(2)结合文法属于构造依赖图
从“依赖图”中,我们可以得到主义规则的计算顺序。用这个顺序来计算语义规则就能得到输入符号串的翻译。
注意: 从“依赖图”中发现循环依赖关系,那此文法将不能计算。
属性文法的翻译
属性文法的翻译分为遍历树的和一遍扫描的.
- 遍历树:深度优先,从左到右遍历方法。如果有需要可以多次遍历。
- 一遍扫描:在语法分析的同时计属性值,而不是语法分析构造之后进行属性计算,而且无需构造语法树。对于编译程序来讲非常重要。
S-属性文法和自下而上翻译
S-属性文法是只含有综合属性的,例有属性文法:
产生式 | 语义规则 |
---|---|
L->E | print(E.val) |
E->E1 + T | E.val = E1.val + T.val |
E->T | E.val = T.val |
T->T1 * F | T.val = T1.val * F.val |
T->F | T.val = F.val |
F->(E) | F.val = E.val |
F-> digit | F.val = digit.lexval |
当输入字符串为“2+3*5” 时
(1)LR分析表构造《自底向上语法分析LR(1)》这里简单的用语法树代替
(2)分析步骤
步骤 | 语义栈 | 符号栈 | 输入符串 | 动作 |
---|---|---|---|---|
1 | - | # | 2+3*5# | |
2 | -- | #2 | +3*5# | |
3 | -2 | #F1 | +3*5# | F1-> 2 |
4 | -2 | #T2 | +3*5# | T2-> F1 |
5 | -2 | #E1 | +3*5# | E1->T2 |
6 | -2- | #E1+ | 3*5 | |
7 | -2-- | #E1+3 | *5# | |
8 | -2-3 | #E1+F2 | * 5# | F2->3 |
9 | -2-3 | #E1+T1 | * 5# | T1->F2 |
10 | -2-3- | #E1+T1* | 5# | |
11 | -2-3-- | #E1+T1*5 | # | |
12 | -2-3-5 | #E1+T1*F | # | F->5 |
13 | -2-15 | #E1+T | # | T->T1*F |
14 | -17 | #E | # | E->E1+T |
15 | acc |
L-属性文法和自上而下翻译
对每个产生式A->X1X2…Xn,其每个语义规则中的每个属性或者综合属性或者Xj(1<=j<=n)的一个继承属性仅依赖于
- 产生式Xj在左边符号X1,X2…Xj-1
- A的继承属性
S-属性文法一定是L-属性文法,因为1,2的限定只针对继承属性。
带有属性的左递归消除方式
假设文法为
A->A1Y {A.a = g(A1,Y.y)}
A->X {A.a = f(X.x)}
其中g,f是任意函数
文法转换为
A->XR
R->YR|ε
再加上语义动作,引入R的继承属性i和属合属性s
A-> X {R.i = f(X.x)} R {A.a = R.s}
R->Y {R1.i = g(R.i,Y.y)} R1 {R.s = R1.s}
R->ε {R.s = R.i}
例:给定属性文法G[E]
产生式 | 语义规则 |
---|---|
E-> E addop T | print(addop.Lexeme) |
E-> T | |
T -> num | print(num.val) |
注:其中addop表示+或者-
因为要使用自上而下,引进LL(1)算法,由于左递归的关系,改写文法为
产生式 |
---|
E-> ER |
R -> addop T {print(addop.Lexeme) }R1 |
R -> ε |
T -> num {print(num.val)} |
当输入字符串为“2+3-5” 时
(1)LL分析表构造<<自顶向下语法分析LL>>这里简单的用语法树代替
(2)分析步骤
(略)
L-属性文法和自下而上翻译
自上而下的计算继续属性有2种方法:
- 去掉嵌入在产生式中间的动作
例如文法G[E]
E->TR
R->+T {print(’+’)} R1
R->-T {print(’-’)} R1
R->ε
T->num {print(num.val)}
引入M和N,将原嵌入在产生式中间的动作都放到产生式后面
E->TR
R->+T M R1
R->-T N R1
R->ε
T->num {print(num.val)}
M->ε {print(’+’)}
N->ε {print(’-’)}
- 用综合属性代替继承属性
例如文法G[D]
D->L:T
T->integer | char
L->L,id|id
重新构造文法,借以实现综合属性代替继承属性
D->id L
L -> ,id L|:T
T->integer | char