自顶向下的基本语义分析程序设计实验,主要介绍文法设计及相应代码构造。内容仅供参考
文章目录
一、实验内容
实现一个《程序设计语言子集》的编译系统。包括:词法分析、语法分析、语义分析、符号表、出错处理等。
语言成分如下:
- 数据类型:整型、布尔类型
- 简单变量
- 算术表达式(+ , × \times ×)
- 布尔表达式(and, or, not)
- 语句:
- 赋值语句
- 分支语句(if-then, if -then-else)
- 循环语句(while)
- 定义语句
输入内容:
- 输入串如果是合法的程序段,输出相应中间代码、符号表等相关信息。
- 若输入串是非法的程序段,指出错误位置及错误原因(词法错误、语法错误、语义错误等)。
二、设计概述
1.程序流程
实验采用自顶向下的递归下降语法分析法。通过设计符合实验要求的文法,消除左递归和提取左公因子,构造适合的自顶向下分析的翻译模式和相应的递归下降翻译器,实现一遍扫描的自上而下分析。设计的程序工作流程如下:
对于输入语句,首先进行预处理,消除语句中的一些转义符和与语义分析无关的符号。经过预处理后,语句进入语义分析器或递归下降翻译器中进行自顶向下翻译,最终输出四元式。
2.递归下降翻译器的设计
在完成适合于自顶向下翻译的翻译模式设计后,根据[1]中所给出的以下方法设计递归下降翻译器。
- 为每一个非终结符A构造一个函数过程,函数返回A的综合属性。
- 非终结符A对应的函数过程中,根据当前的输入符号决定使用的哪个产生式候选。
- 每条产生式对应的程序代码中,按照从左至右的次序,对于终结符、非终结符和语义动作分别作以下工作:
- 对于带有综合属性x的终结符X,把x的值存入为X.x设置的变量中。然后进入匹配X的产生式候选对应程序段,并继续读入下一个输入符号。
- 对于非终结符B,产生一个赋值语句 c = B ( b 1 , b 2 , … , b n ) c=B(b_1,b_2,\dots,b_n) c=B(b1,b2,…,bn),其中 b 1 , b 2 , … , b n b_1,b_2,\dots,b_n b1,b2,…,bn是B继承属性对应的变量,c是B综合属性对应的变量,B()为B对应的函数过程。
- 对于语义动作,产生对应动作的代码,用代表属性的变量代替对属性的引用。
3 .文法设计及翻译模式的构造
根据实验内容,需要识别赋值语句、分支语句、循环语句和定义语句,还包括算术表达式和布尔表达式。由于复杂文法设计和后续翻译模式的构造比较困难,因此设计的文法比较简单,能处理情况有限。为了实现不带回溯的自顶向下语法分析,在消除一个翻译模式的基本文法的左递归时需要同时考虑属性。这里在构造翻译模式时,使用了[1]中所给出的如下方法:
该方法通过继承属性在A和R的产生式之间传递属性值,从而实现在计算左边非终结符的综合属性时,它所依赖的所有属性均已计算出来。
三、具体文法设计及翻译模式构造
1.语句
自顶向下翻译从开始符号S出发,根据当前输入符号匹配不同的产生式。为了能够识别嵌套分支语句和循环语句,需要在产生式中加入括号和分号。这里设计的定义语句只能识别一个标识符。
S
→
if P then
{
S
1
}
;
∣
if P then
{
S
1
}
e
l
s
e
{
S
2
}
;
∣
while P do
{
S
1
}
;
∣
int id ;
∣
id := E ;
T
→
id | num
\begin{aligned} S \to & \text{ if P then }\{S_1\} ;\\ & | \text{ if P then }\{S_1\} \ else \ \{S_2\} ; \\ & | \text{ while P do } \{S_1\} ;\\ & | \text{ int id ;} \\ & | \text{ id := E ;} \\ T \to & \text{ id | num} \\ \end{aligned}
S→T→ if P then {S1};∣ if P then {S1} else {S2};∣ while P do {S1};∣ int id ;∣ id := E ; id | num
提取左公因子后有:
S
→
if P then
{
S
1
}
S
′
∣
while P do
{
S
1
}
;
∣
int id ;
∣
id := E ;
S
′
→
else
{
S
2
}
;
∣
;
\begin{aligned} S \to & \text{ if P then } \{S_1\} S' \\ & | \text{ while P do } \{S_1\}; \\ & | \text{ int id ;} \\ & | \text{ id := E ;} \\ S' \to & \text{ else }\{S_2\} ; | ; \\ \end{aligned}
S→S′→ if P then {S1}S′∣ while P do {S1};∣ int id ;∣ id := E ; else {S2};∣;
构造的翻译模式(原产生式右部用颜色标出)如下,其中S’.i为S’的继承属性,S’.s为S’的综合属性。语义动作中出现的函数功能见[1]
S
→
i
f
P
t
h
e
n
{
M
1
.
q
u
a
d
=
n
e
x
t
q
u
a
d
}
{
S
1
}
{
S
′
.
i
.
n
e
x
t
l
i
s
t
=
S
1
.
n
e
x
t
l
i
s
t
S
′
.
i
.
t
r
u
e
l
s
i
t
=
P
.
t
r
u
e
l
i
s
t
S
′
.
i
.
f
a
l
s
e
l
s
i
t
=
P
.
f
a
l
s
e
l
s
i
t
S
′
.
i
.
M
1
.
q
u
a
d
=
M
1
.
q
u
a
d
}
S
′
{
S
.
n
e
x
t
l
i
s
t
=
S
′
.
s
.
n
e
x
t
l
i
s
t
}
S
→
while
{
M
1
.
q
u
a
d
=
n
e
x
t
q
u
a
d
}
P do
{
M
2
.
q
u
a
d
=
n
e
x
t
q
u
a
d
}
{
S
2
}
;
{
b
a
c
k
p
a
t
c
h
(
S
1
.
n
e
x
t
l
i
s
t
,
M
1
.
q
u
a
d
)
b
a
c
k
p
a
t
c
h
(
P
1
.
t
r
u
e
l
s
i
t
,
M
2
.
q
u
a
d
)
S
.
n
e
x
t
l
i
s
t
=
P
.
f
a
l
s
e
l
i
s
t
e
m
i
t
(
j
,
_
,
_
,
M
1
.
q
u
a
d
)
}
S
→
int id ;
{
e
n
t
e
r
(
i
d
.
n
a
m
e
,
i
n
t
)
S
.
n
e
x
t
l
i
s
t
=
m
a
k
e
l
i
s
t
(
)
=
0
}
S
→
id := E ;
{
e
m
i
t
(
:
=
,
E
.
p
l
a
c
e
,
_
,
i
d
.
n
a
m
e
)
S
.
n
e
x
t
l
i
s
t
=
m
a
k
e
l
i
s
t
(
)
=
0
}
S
′
→
;
{
b
a
c
k
p
a
t
c
h
(
S
′
.
i
.
t
r
u
e
l
s
i
t
,
S
′
.
i
.
M
1
.
q
u
a
d
)
S
′
.
s
.
n
e
x
t
l
i
s
t
=
m
e
r
g
e
(
S
′
.
i
.
f
a
l
s
e
l
s
i
t
,
S
′
.
i
.
n
e
x
t
l
i
s
t
)
}
S
′
→
{
N
.
n
e
x
t
l
i
s
t
=
m
a
k
e
l
i
s
t
(
n
e
x
t
q
u
a
d
)
e
m
i
t
(
j
,
_
,
_
,
0
)
}
e
l
s
e
{
M
2
.
q
u
a
d
=
n
e
x
t
q
u
a
d
}
{
S
2
}
;
{
b
a
c
k
p
a
t
c
h
(
S
′
.
i
.
t
r
u
e
l
s
i
t
,
S
′
.
i
.
M
1
.
q
u
a
d
)
b
a
c
k
p
a
t
c
h
(
S
′
.
i
.
f
a
l
s
e
l
i
s
t
,
S
′
.
i
.
M
2
.
q
u
a
d
)
S
′
.
s
.
n
e
x
t
l
i
s
t
=
m
e
r
g
e
(
S
′
.
i
.
n
e
x
t
l
s
i
t
,
N
.
n
e
x
t
l
i
s
t
,
S
2
.
n
e
x
t
l
i
s
t
)
}
\begin{aligned} S \to & {\color{orange}\ if\ P\ then \ } \{M_1.quad=nextquad\} \\ & {\color{orange}\{S_1 \} \ } \{S'.i.nextlist = S_1.nextlist \\ & \qquad \quad S'.i.truelsit = P.truelist \\ & \qquad \quad S'.i.falselsit = P.falselsit \\ & \qquad \quad S'.i.M_1.quad = M_1.quad\} \\ & {\color{orange}S' \ } \{S.nextlist=S'.s.nextlist\} \\ \\ S \to & {\color{orange}\text{ while }} \{M_1.quad = nextquad \} \\ & {\color{orange}\text{ P do }} \{M_2.quad = nextquad \} \\ & {\color{orange}\{S_2\}; \ } \{backpatch(S_1.nextlist,M_1.quad) \\ & \qquad \quad backpatch(P_1.truelsit,M_2.quad) \\ & \qquad \quad S.nextlist = P.falselist \\ & \qquad \quad emit(j,\_,\_,M_1.quad)\} \\ \\ S \to & {\color{orange}\text{ int id ; }} \{enter(id.name,int) \\ & \qquad \qquad S.nextlist= makelist()=0\} \\ \\ S \to & {\color{orange}\text{ id := E ; }} \{emit(:=,E.place,\_,id.name) \\ & \qquad \qquad S.nextlist= makelist()=0\} \\ \\ S' \to & {\color{orange}\text{ ; }} \{backpatch(S'.i.truelsit,S'.i.M_1.quad) \\ & \quad S'.s.nextlist = merge(S'.i.falselsit,S'.i.nextlist)\} \\ \\ S' \to & \ \{N.nextlist=makelist(nextquad) \\ & \quad emit(j,\_,\_,0)\} \\ & {\color{orange}else \ } \{M_2.quad=nextquad\} \\ & {\color{orange}\{S_2\}; \ } \{backpatch(S'.i.truelsit,S'.i.M_1.quad) \\ & \qquad \quad backpatch(S'.i.falselist,S'.i.M_2.quad) \\ & \qquad \quad S'.s.nextlist = merge(S'.i.nextlsit,N.nextlist,S_2.nextlist)\} \end{aligned}
S→S→S→S→S′→S′→ if P then {M1.quad=nextquad}{S1} {S′.i.nextlist=S1.nextlistS′.i.truelsit=P.truelistS′.i.falselsit=P.falselsitS′.i.M1.quad=M1.quad}S′ {S.nextlist=S′.s.nextlist} while {M1.quad=nextquad} P do {M2.quad=nextquad}{S2}; {backpatch(S1.nextlist,M1.quad)backpatch(P1.truelsit,M2.quad)S.nextlist=P.falselistemit(j,_,_,M1.quad)} int id ; {enter(id.name,int)S.nextlist=makelist()=0} id := E ; {emit(:=,E.place,_,id.name)S.nextlist=makelist()=0} ; {backpatch(S′.i.truelsit,S′.i.M1.quad)S′.s.nextlist=merge(S′.i.falselsit,S′.i.nextlist)} {N.nextlist=makelist(nextquad)emit(j,_,_,0)}else {M2.quad=nextquad}{S2}; {backpatch(S′.i.truelsit,S′.i.M1.quad)backpatch(S′.i.falselist,S′.i.M2.quad)S′.s.nextlist=merge(S′.i.nextlsit,N.nextlist,S2.nextlist)}
2.算术表达式
addop表示加号和乘号
E
→
E addop T
∣
T
T
→
id | num
\begin{aligned} E \to & \text{E addop T} \\ & | \text{ T} \\ T \to & \text{ id | num} \\ \end{aligned}
E→T→E addop T∣ T id | num
消除左递归后
E
→
TE’
E
′
→
addop TE’ |
ε
T
→
id | num
\begin{aligned} E \to & \text{TE'} \\ E' \to & \text{addop TE' | } \varepsilon \\ T \to & \text{ id | num} \\ \end{aligned}
E→E′→T→TE’addop TE’ | ε id | num
构造的翻译模式
E
→
T
{E’.i = T.place }
E
′
{E.place = E’.s }
E
′
→
addop T
{
E
1
′
.
i
=
n
e
w
t
e
m
p
e
m
i
t
(
a
d
d
o
p
,
E
′
.
i
,
T
.
p
l
a
c
e
,
E
1
′
.
i
)
}
E
1
′
{
E
.
s
=
E
1
′
.
s
}
E
′
→
ε
{
E
′
.
s
=
E
′
.
i
}
T
→
id | num
{
T
.
p
l
a
c
e
=
i
d
.
n
a
m
e
∣
n
u
m
}
\begin{aligned} E \to & {\color{orange}T} \text{ \{E'.i = T.place \}} \\ & {\color{orange}E'} \text{ \{E.place = E'.s \}} \\ \\ E' \to & {\color{orange} \text{addop T }} \{E_1'.i = newtemp \\ & \qquad \qquad emit(addop,E'.i,T.place,E_1'.i) \} \\ & {\color{orange}E_1'} \quad \{E.s = E_1'.s \} \\ \\ E' \to & {\color{orange}\varepsilon} \quad \{E'.s = E'.i \} \\ \\ T \to & {\color{orange}\text{ id | num}} \quad \{T.place = id.name | num \} \\ \end{aligned}
E→E′→E′→T→T {E’.i = T.place }E′ {E.place = E’.s }addop T {E1′.i=newtempemit(addop,E′.i,T.place,E1′.i)}E1′{E.s=E1′.s}ε{E′.s=E′.i} id | num{T.place=id.name∣num}
3.布尔表达式
relop表示大于号和小于号
P
→
P or P
∣
P and P
∣
not P
∣
T
1
r
e
l
o
p
T
2
∣
T
\begin{aligned} P \to & \text{ P or P} \\ & | \text{ P and P} \\ & | \text { not P} \\ & | T_1 \ relop \ T_2 \\ & | \text { T } \\ \end{aligned}
P→ P or P∣ P and P∣ not P∣T1 relop T2∣ T
消除左递归后
P
→
not PP’
∣
T
1
r
e
l
o
p
T
2
∣
TP’
P
′
→
or PP’
∣
and PP’
∣
ε
\begin{aligned} P \to & \text { not PP'} \\ & | T_1 \ relop \ T_2 \\ & | \text { TP'} \\ P' \to & \text{ or PP'} \\ & | \text { and PP'} \\ & | \varepsilon \\ \end{aligned}
P→P′→ not PP’∣T1 relop T2∣ TP’ or PP’∣ and PP’∣ε
构造的翻译模式
P
→
n
o
t
P
1
{
P
1
′
.
i
.
t
r
u
e
l
i
s
t
=
P
1
.
f
a
l
s
e
l
i
s
t
P
1
′
.
i
.
f
a
l
s
e
l
i
s
t
=
P
1
.
t
r
u
e
l
i
s
t
}
P
1
′
{
P
.
t
r
u
e
l
i
s
t
=
P
1
′
.
s
.
t
r
u
e
l
i
s
t
P
.
f
a
l
s
e
l
i
s
t
=
P
1
′
.
s
.
f
a
l
s
e
l
i
s
t
}
P
→
T
1
r
e
l
o
p
T
2
{
P
1
′
.
i
.
t
r
u
e
l
i
s
t
=
m
a
k
e
l
i
s
t
(
n
e
x
t
q
u
a
d
)
P
1
′
.
i
.
f
a
l
s
e
l
i
s
t
=
m
a
k
e
l
i
s
t
(
n
e
x
t
q
u
a
d
+
1
)
e
m
i
t
(
j
r
e
l
o
p
,
T
1
.
p
l
a
c
e
,
T
2
.
p
l
a
c
e
,
0
)
e
m
i
t
(
j
,
_
,
_
,
0
)
}
P
1
′
{
P
.
t
r
u
e
l
i
s
t
=
P
1
′
.
s
.
t
r
u
e
l
i
s
t
P
.
f
a
l
s
e
l
i
s
t
=
P
1
′
.
s
.
f
a
l
s
e
l
i
s
t
}
P
→
T
{
P
1
′
.
i
.
t
r
u
e
l
i
s
t
=
m
a
k
e
l
i
s
t
(
n
e
x
t
q
u
a
d
)
P
1
′
.
i
.
f
a
l
s
e
l
i
s
t
=
m
a
k
e
l
i
s
t
(
n
e
x
t
q
u
a
d
+
1
)
e
m
i
t
(
j
n
z
,
T
,
_
,
0
)
e
m
i
t
(
j
,
_
,
_
,
0
)
}
P
1
′
{
P
.
t
r
u
e
l
i
s
t
=
P
1
′
.
s
.
t
r
u
e
l
i
s
t
P
.
f
a
l
s
e
l
i
s
t
=
P
1
′
.
s
.
f
a
l
s
e
l
i
s
t
}
P
′
→
o
r
{
M
.
q
u
a
d
=
n
e
x
t
q
u
a
d
}
P
{
b
a
c
k
p
a
t
c
h
(
P
′
.
i
.
f
a
l
s
e
l
i
s
t
,
M
.
q
u
a
d
)
P
1
′
.
i
.
t
r
u
e
l
i
s
t
=
m
e
r
g
e
(
P
′
.
i
.
t
r
u
e
l
i
s
t
,
P
.
t
r
u
e
l
i
s
t
)
P
1
′
.
i
.
f
a
l
s
e
l
i
s
t
=
P
.
f
a
l
s
e
l
i
s
t
}
P
1
′
{
P
′
.
s
.
t
r
u
e
l
i
s
t
=
P
1
′
.
s
.
t
r
u
e
l
i
s
t
P
′
.
s
.
f
a
l
s
e
l
i
s
t
=
P
1
′
.
s
.
f
a
l
s
e
l
i
s
t
}
P
′
→
a
n
d
{
M
.
q
u
a
d
=
n
e
x
t
q
u
a
d
}
P
{
b
a
c
k
p
a
t
c
h
(
P
′
.
i
.
t
r
u
e
l
i
s
t
,
M
.
q
u
a
d
)
P
1
′
.
i
.
t
r
u
e
l
i
s
t
=
P
.
t
r
u
e
l
i
s
t
P
1
′
.
i
.
f
a
l
s
e
l
i
s
t
=
m
e
r
g
e
(
P
′
.
i
.
f
a
l
s
e
l
i
s
t
,
P
.
f
a
l
s
e
l
i
s
t
)
}
P
1
′
{
P
′
.
s
.
t
r
u
e
l
i
s
t
=
P
1
′
.
s
.
t
r
u
e
l
i
s
t
P
′
.
s
.
f
a
l
s
e
l
i
s
t
=
P
1
′
.
s
.
f
a
l
s
e
l
i
s
t
}
P
′
→
ε
{
P
′
.
s
.
t
r
u
e
l
i
s
t
=
P
′
.
i
.
t
r
u
e
l
i
s
t
P
′
.
s
.
f
a
l
s
e
l
i
s
t
=
P
′
.
i
.
f
a
l
s
e
l
i
s
t
}
\begin{aligned} P \to & {\color{orange}not \ P_1 } \{P_1'.i.truelist = P_1.falselist \\ & \qquad \quad P_1'.i.falselist = P_1.truelist \} \\ & {\color{orange} P_1'} \{P.truelist = P_1'.s.truelist \\ & \quad P.falselist = P_1'.s.falselist \} \\ \\ P \to & {\color{orange}T_1 \ relop \ T_2} \{P_1'.i.truelist = makelist(nextquad) \\ & \qquad \qquad P_1'.i.falselist = makelist(nextquad+1) \\ & \qquad \qquad emit(jrelop,T_1.place,T_2.place,0) \\ & \qquad \qquad emit(j,\_,\_,0) \} \\ & {\color{orange} P_1'} \{P.truelist = P_1'.s.truelist \\ & \quad P.falselist = P_1'.s.falselist \} \\ \\ P \to & {\color{orange}T} \{P_1'.i.truelist = makelist(nextquad) \\ & \quad P_1'.i.falselist = makelist(nextquad+1) \\ & \quad emit(jnz,T,\_,0) \\ & \quad emit(j,\_,\_,0) \} \\ & {\color{orange} P_1'} \{P.truelist = P_1'.s.truelist \\ & \quad P.falselist = P_1'.s.falselist \} \\ \\ P' \to & {\color{orange} \ or } \{M.quad = nextquad \} \\ & {\color{orange}P} \{backpatch(P'.i.falselist,M.quad) \\ & \quad P_1'.i.truelist = merge(P'.i.truelist,P.truelist) \\ & \quad P_1'.i.falselist = P.falselist \} \\ & {\color{orange} P_1'} \{P'.s.truelist = P_1'.s.truelist \\ & \quad P'.s.falselist = P_1'.s.falselist \} \\ \\ \\ P' \to & {\color{orange} \ and } \{M.quad = nextquad \} \\ & {\color{orange}P} \{backpatch(P'.i.truelist,M.quad) \\ & \quad P_1'.i.truelist = P.truelist \\ & \quad P_1'.i.falselist = merge(P'.i.falselist,P.falselist) \} \\ & {\color{orange} P_1'} \{P'.s.truelist = P_1'.s.truelist \\ & \quad P'.s.falselist = P_1'.s.falselist \} \\ \\ P' \to & {\color{orange}\varepsilon} \{P'.s.truelist = P'.i.truelist \\ & \quad P'.s.falselist = P'.i.falselist \} \\ \end{aligned}
P→P→P→P′→P′→P′→not P1{P1′.i.truelist=P1.falselistP1′.i.falselist=P1.truelist}P1′{P.truelist=P1′.s.truelistP.falselist=P1′.s.falselist}T1 relop T2{P1′.i.truelist=makelist(nextquad)P1′.i.falselist=makelist(nextquad+1)emit(jrelop,T1.place,T2.place,0)emit(j,_,_,0)}P1′{P.truelist=P1′.s.truelistP.falselist=P1′.s.falselist}T{P1′.i.truelist=makelist(nextquad)P1′.i.falselist=makelist(nextquad+1)emit(jnz,T,_,0)emit(j,_,_,0)}P1′{P.truelist=P1′.s.truelistP.falselist=P1′.s.falselist} or{M.quad=nextquad}P{backpatch(P′.i.falselist,M.quad)P1′.i.truelist=merge(P′.i.truelist,P.truelist)P1′.i.falselist=P.falselist}P1′{P′.s.truelist=P1′.s.truelistP′.s.falselist=P1′.s.falselist} and{M.quad=nextquad}P{backpatch(P′.i.truelist,M.quad)P1′.i.truelist=P.truelistP1′.i.falselist=merge(P′.i.falselist,P.falselist)}P1′{P′.s.truelist=P1′.s.truelistP′.s.falselist=P1′.s.falselist}ε{P′.s.truelist=P′.i.truelistP′.s.falselist=P′.i.falselist}
四、程序设计说明
程序实现采用python。
1.语义分析器
为了能够在递归过程中控制搜索指示器的移动,将语义分析器构造为一个类,完成对输入语句的词法分析、语法分析、语义分析和中间代码(四元式)生成,其类成员及类方法说明如下:
类成员 | 类型 | 类方法 |
---|---|---|
i | int | 搜索指示器,指向下一个输入符号。开始时指向第一个输入符号 |
line | 字符串 | 当前输入的语句 |
string | list | 保存line经词法分析后的结果,其元素为对应位置的输入符号的二元式(单词种别编码,单词符号的属性值) |
symbolList | list | 符号表,保存定义的标识符和其类型,其元素为[idname,type] |
midcodeList | list | 保存产生的各条四元式 |
nextquad | int | 下一条四元式地址,初值为100 |
newtemp | int | 下一个临时变量,配合getnewtemp()使用 |
dic | dic | 保留字表2,用于将种别编码转为相应的单词符号,其元素格式为{key=对应种别编码,value=保留字} |
类方法 | 说明 |
---|---|
semantic_analysis(line) | 实现对输入语句的词法分析、语法分析、语义分析和中间代码(四元式)生成 |
getnextword() | 获取下一个输入字符(当前搜索指示器所指符号),返回值为(种别编码,属性值) 注:该函数并不移进搜索指示器i |
syntax_error( expect) | 语法错误处理,输出出现错误的位置并终止分析 |
semantic_error(idname) | 语义错误处理,输出未定义的变量名并终止分析 |
result_output() | 输出分析结束后的符号表和产生的四元式 |
possess_S() | 对应非终结符S的程序,返回值为综合属性S.nextlist |
possess_S2(P_list, M1_quad=‘’, S2_i_nextlist=‘’) | 对应非终结符S‘的程序,输入为S’的继承属性 S ′ . i . t r u e l i s t , S ′ . i . f a l s e l s i t , S ′ . i . f a l s e l s i t , S ′ . i . M _ 1 _ q u a d , S ′ . i . n e x t l s i t S'.i.truelist\ ,S'.i.falselsit\ , S'.i.falselsit,\ S'.i.M\_1\_quad, \ S'.i.nextlsit S′.i.truelist ,S′.i.falselsit ,S′.i.falselsit, S′.i.M_1_quad, S′.i.nextlsit 其中P_list=( S ′ . i . t r u e l s i t , S ′ . i . f a l s e l i s t S'.i.truelsit,\ S'.i.falselist S′.i.truelsit, S′.i.falselist) 返回值为综合属性 S ′ . s . n e x t l s i t S'.s.nextlsit S′.s.nextlsit |
possess_E() | 对应非终结符E的程序,返回值为综合属性E.place |
possess_E2() | 对应非终结符E’的程序,输入为E’的继承属性E’.i,返回值为综合属性E’.s |
possess_T() | 对应非终结符T的程序,返回值为综合属性T_place |
possess_P() | 对应非终结符P的程序,返回值为综合属性P.place |
possess_P2() | 对应非终结符P’的程序,输入为P’的继承属性 P ′ . i . t r u e l s i t , P ′ . i . f a l s e l i s t P'.i.truelsit,P'.i.falselist P′.i.truelsit,P′.i.falselist 返回值为综合属性 P ′ . s . t r u e l s i t , P ′ . s . f a l s e l i s t P'.s.truelsit,P'.s.falselist P′.s.truelsit,P′.s.falselist |
enter(idname, idtype) | 将输入的标识符和其对应的类型存入符号表中 |
lookup(idname) | 检查符号表中有无相应标识符的入口,如有返回相应索引,否则出现语义错误 |
emit(op, arg1, arg2, result) | 产生四元式并保存到midcodeList中 |
getnewtemp() | 返回下一个临时变量,例如T1 |
getWordByCode(wordcode) | 查找保留字表2,根据种别编码返回相应的单词符号 |
makeList(nextquad) | 产生以nextquad为链首的链表。具体代码实现时,只创建了逻辑上的链表,并没有真正开辟内存空间。该函数实际上可以省略。 |
backpatch(arg, M_quad) | 用M_quad回填传入的以arg为链首的四元式 |
merge(arg1, arg2, arg3) | 把arg1, arg2, arg3为链首的三条链合并,值最大的作为新链首,返回新链首 注:这里arg3可能为空 |
下面结合具体代码和翻译模式来说明设计概述中提到的递归下降翻译器的构造。
1)非终结符E
非终结符E相关的翻译模式如下:
E
→
T
{E’.i = T.place }
E
′
{E.place = E’.s }
E
′
→
addop T
{
E
1
′
.
i
=
n
e
w
t
e
m
p
e
m
i
t
(
a
d
d
o
p
,
E
′
.
i
,
T
.
p
l
a
c
e
,
E
1
′
.
i
)
}
E
1
′
{
E
.
s
=
E
1
′
.
s
}
E
′
→
ε
{
E
′
.
s
=
E
′
.
i
}
\begin{aligned} E \to & {\color{orange}T} \text{ \{E'.i = T.place \}} \\ & {\color{orange}E'} \text{ \{E.place = E'.s \}} \\ \\ E' \to & {\color{orange} \text{addop T }} \{E_1'.i = newtemp \\ & \qquad \qquad emit(addop,E'.i,T.place,E_1'.i) \} \\ & {\color{orange}E_1'} \quad \{E.s = E_1'.s \} \\ \\ E' \to & {\color{orange}\varepsilon} \quad \{E'.s = E'.i \} \\ \end{aligned}
E→E′→E′→T {E’.i = T.place }E′ {E.place = E’.s }addop T {E1′.i=newtempemit(addop,E′.i,T.place,E1′.i)}E1′{E.s=E1′.s}ε{E′.s=E′.i}
由于非终结符E、T没有继承属性,因此其对应函数过程没有参数,只需要赋值语句接受函数过程返回的综合属性值即可。而非终结符E’有继承属性E’.i,调用对应函数时需要传入其继承属性。构造E和E’的函数过程时,对每一条产生式,从左到右,根据上文提到的构造方法进行构造。
对于E,只有一条产生式。首先遇到非终结符T,调用对应函数过程,并保存其返回值。T后紧跟的语义动作就是保存返回值。因此可以将这两步合二为一,即有 E ′ . i = T ( ) E'.i=T() E′.i=T()。同理,调用E’时也这样处理。需要注意的是E’有继承属性,所以在调用E’时,需要传入其继承属性,则可以得到 E . s = E ′ ( E ′ . i ) E.s=E'(E'.i) E.s=E′(E′.i)。最后返回E.s综合属性。对应代码如下:
def possess_E(self):
"""返回值为E_place"""
E2_i = self.possess_T()
E_place = self.possess_E2(E2_i)
return E_place
对于E’,有两条产生式,需要根据当前输入符号来决定匹配哪条产生式。若输入符号为addop(即加号或者乘号)时,匹配第一条产生式,保存addop对应的单词符号并继续读入下一个输入符号,调用T。这里T后的语义动作没有保存其返回值,因此需要引入变量保存,即 T . p l a c e = T ( ) T.place=T() T.place=T()。接下来执行T后的语义动作为 E 1 ′ . i E'_1.i E1′.i赋值newtemp,产生相应的四元式。之后,调用 E 1 ′ E_1' E1′的对应的函数过程(调用自己)。这里可以合并紧跟的语义动作,即有 E . s = E ′ ( E 1 ′ . i ) E.s=E'(E_1'.i) E.s=E′(E1′.i)。最后返回E’.s综合属性。若输入符号为其他符号,默认获得匹配(更精确做法是判断当前输入符号是否属于FOLLOW(E’) ),产生式中语义动作可简写为 r e t u r n E ′ . i return\ E'.i return E′.i,减少引入的变量。对应代码如下:
def possess_E2(self, E2_i=''):
"""返回值为E2.s综合属性"""
t = self.getnextword()
# 识别到加号或乘号
if t[0] == '12' or t[0] == '13':
self.i += 1
T_place = self.possess_T()
# E'1
E2_1_i = self.getnewtemp()
# 产生四元式
op = self.getWordByCode(t[0])
self.emit(op, E2_i, T_place, E2_1_i)
# 返回E2'.s
return self.possess_E2(E2_1_i)
# 输入其他符号时默认获得匹配
return E2_i
2)非终结符P
非终结符P相关部分的翻译模式如下:
P
→
T
1
r
e
l
o
p
T
2
{
P
1
′
.
i
.
t
r
u
e
l
i
s
t
=
m
a
k
e
l
i
s
t
(
n
e
x
t
q
u
a
d
)
P
1
′
.
i
.
f
a
l
s
e
l
i
s
t
=
m
a
k
e
l
i
s
t
(
n
e
x
t
q
u
a
d
+
1
)
e
m
i
t
(
j
r
e
l
o
p
,
T
1
.
p
l
a
c
e
,
T
2
.
p
l
a
c
e
,
0
)
e
m
i
t
(
j
,
_
,
_
,
0
)
}
P
1
′
{
P
.
t
r
u
e
l
i
s
t
=
P
1
′
.
s
.
t
r
u
e
l
i
s
t
P
.
f
a
l
s
e
l
i
s
t
=
P
1
′
.
s
.
f
a
l
s
e
l
i
s
t
}
P
′
→
a
n
d
{
M
.
q
u
a
d
=
n
e
x
t
q
u
a
d
}
P
{
b
a
c
k
p
a
t
c
h
(
P
′
.
i
.
t
r
u
e
l
i
s
t
,
M
.
q
u
a
d
)
P
1
′
.
i
.
t
r
u
e
l
i
s
t
=
P
.
t
r
u
e
l
i
s
t
P
1
′
.
i
.
f
a
l
s
e
l
i
s
t
=
m
e
r
g
e
(
P
′
.
i
.
f
a
l
s
e
l
i
s
t
,
P
.
f
a
l
s
e
l
i
s
t
)
}
P
1
′
{
P
′
.
s
.
t
r
u
e
l
i
s
t
=
P
1
′
.
s
.
t
r
u
e
l
i
s
t
P
′
.
s
.
f
a
l
s
e
l
i
s
t
=
P
1
′
.
s
.
f
a
l
s
e
l
i
s
t
}
\begin{aligned} P \to & {\color{orange}T_1 \ relop \ T_2} \{P_1'.i.truelist = makelist(nextquad) \\ & \qquad \qquad P_1'.i.falselist = makelist(nextquad+1) \\ & \qquad \qquad emit(jrelop,T_1.place,T_2.place,0) \\ & \qquad \qquad emit(j,\_,\_,0) \} \\ & {\color{orange} P_1'} \{P.truelist = P_1'.s.truelist \\ & \quad P.falselist = P_1'.s.falselist \} \\ \\ P' \to & {\color{orange} \ and } \{M.quad = nextquad \} \\ & {\color{orange}P} \{backpatch(P'.i.truelist,M.quad) \\ & \quad P_1'.i.truelist = P.truelist \\ & \quad P_1'.i.falselist = merge(P'.i.falselist,P.falselist) \} \\ & {\color{orange} P_1'} \{P'.s.truelist = P_1'.s.truelist \\ & \quad P'.s.falselist = P_1'.s.falselist \} \\ \end{aligned}
P→P′→T1 relop T2{P1′.i.truelist=makelist(nextquad)P1′.i.falselist=makelist(nextquad+1)emit(jrelop,T1.place,T2.place,0)emit(j,_,_,0)}P1′{P.truelist=P1′.s.truelistP.falselist=P1′.s.falselist} and{M.quad=nextquad}P{backpatch(P′.i.truelist,M.quad)P1′.i.truelist=P.truelistP1′.i.falselist=merge(P′.i.falselist,P.falselist)}P1′{P′.s.truelist=P1′.s.truelistP′.s.falselist=P1′.s.falselist}
对于非终结符P,匹配到这一条产生式时,首先,需要调用两次非终结符对应的函数过程,同时将其返回值保存。在两次调用T()的过程中,还需要识别一次终结符relop。然后开始执行四条语义动作,创建两条新链,产生两条待回填的四元式。此后,调用P’对应的函数,同时传入相应的继承属性,即
P
1
′
(
P
1
′
.
t
r
u
e
l
i
s
t
,
P
1
′
.
f
a
l
s
e
l
i
s
t
)
P'_1(P_1'.truelist,\ P_1'.falselist)
P1′(P1′.truelist, P1′.falselist)。对应代码如下:
def possess_P(self):
"""返回P_truelist和falselist两个综合属性"""
# 识别到not
if self.getnextword()[0] == '3':
self.i += 1
# 产生式
P1_list = self.possess_P()
# 语义动作
P2_i_truelist = P1_list[1]
P2_i_falselist = P1_list[0]
P_list = self.possess_P2(P2_i_truelist, P2_i_falselist)
return P_list
T1_place = self.possess_T()
t = self.getnextword()
# 识别到> <
if t[0] == '17' or t[0] == '18':
self.i += 1
# 产生式
T2_place = self.possess_T()
# 语义动作
P2_i_truelist = self.makeList(self.nextquad)
P2_i_falselist = self.makeList(self.nextquad + 1)
op = 'j' + self.getWordByCode(t[0])
self.emit(op, T1_place, T2_place, '0')
self.emit('j', '_', '_', '0')
# 产生式
P_list = self.possess_P2(P2_i_truelist, P2_i_falselist)
return P_list
# 识别到单个标识符
P2_i_truelist = self.makeList(self.nextquad)
P2_i_falselist = self.makeList(self.nextquad + 1)
self.emit('jnz', T1_place, '_', '0')
self.emit('j', '_', '_', '0')
P_list = self.possess_P2(P2_i_truelist, P2_i_falselist)
return P_list
对于P’的以and为首的产生式,and之后紧跟的语义动作是为了保存下一条四元式,以便后续回填。P之后的第一条语义动作就是完成回填,第三条语句将待回填的链进行合并。对应的代码如下:
def possess_P2(self, P2_i_truelist='', P2_i_falselist=''):
"""返回P'.s.truelist和P'.s.falselist两个综合属性,输入为相应的继承属性"""
# 识别到or
if self.getnextword()[0] == '2':
self.i += 1
M_quad = str(self.nextquad)
P_list = self.possess_P()
self.backpatch(P2_i_falselist, M_quad)
# P'1
P2_1_i_truelist = self.merge(P2_i_truelist, P_list[0])
P2_1_i_falselist = P_list[1]
P2_list = self.possess_P2(P2_1_i_truelist, P2_1_i_falselist)
return P2_list
# 识别到and
if self.getnextword()[0] == '1':
self.i += 1
M_quad = str(self.nextquad)
P_list = self.possess_P()
self.backpatch(P2_i_truelist, M_quad)
# P'1
P2_1_i_truelist = P_list[0]
P2_1_i_falselist = self.merge(P2_i_falselist, P_list[1])
P2_list = self.possess_P2(P2_1_i_truelist, P2_1_i_falselist)
return P2_list
# 输入其他符号时默认获得匹配
return P2_i_truelist, P2_i_falselist
五、测试结果
参考资料
[1] 陈火旺,刘春林,谭庆平,赵克佳,刘越. 程序设计语言编译原理(第3版). 北京:国防工业出版社,2010
写在最后:自顶向下的基本语义分析程序实现的核心就是文中设计概述所提到的方法。有了正确的文法和翻译模式,代码构造也会相应地容易一些。以上内容仅供参考,笔者水平有限,错误和不足之处在所难免,还请各位大佬轻喷。