编译原理实验报告
1 / 15
实验一
1. 词法正规式描述、变换后的正规文法、状态图
标识符
id->letter(letter|digit)*
letter->A|B|…|Z|a|b|…|z
digit->0|1|2|3|4|…|9
S->aB’|bB’|…|zB’|AB’|BB’|…|ZB’
B’->0B’|1B’|…|9B’
正规
式
正规
文法
状态
图
0(1|2|3|4|5|6|7)(0|1|2|3|4|5|6|7)*
S->01B|02B|…|07B
B->0B|1B|…|7B|
八进制整数
正规
式
正规
文法
十进制整数
正规
式
正规
文法
十六进制整数
正规
式
S->0(x|X)(1B’|2B’|…|9B’|aB’|bB’|…|fB’|AB’|…|FB’)
正规
B’->0B’|1B’|…|7B’|
文法
识别这三种数字的状态图
0 | (1|2|3|4|5|6|7|8|9)(0|1|2|3|4|5|6|7|8|9)*
S->0|1B|2B|…|9B
B->0B|1B|…|9B|
0x(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f)(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f)*
2 / 15
状态图
2. 词法分析的数据结构与算法
Scan()函数:
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
读入当前字符 ch
while ch 是空格 do
下一字符 ch
case ch of
isdigit(ch):
//跳过空格
//是数字字符
chbuf; 下一字符ch
while isdigit(ch) do
chbuf; 下一字符ch
回送 ch(ungetc(ch,stdin))
将 buf 数字字符串变成数 attr(=autio(buff))
返回 NUM
//整数
isalpha(ch):
//是字母
chbuf; 下一字符ch
while isalpha(ch) or isdigit(ch)
do
chbuf; 下一字符ch
回送 ch;
key = isKeyword(buf);
if key >= 0 return key;
Lookup( buf ) attr
返回 IDN
//标识符
':' : 下一字符→ch;
if ch 等于'='
error;
return ASG;
3 / 15
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
33.
34.
return ADD;
return SUB;
return MUL;
return DIV;
return EQ;
return GT;
return LT;
return LP;
return RP;
return SEMI;
'+' :
'-' :
'*' :
'/' :
'=' :
'>' :
'<' :
'(' :
')' :
';' :
other :
error;
end of case;
思考题
1. 词法分析能否采用空格来区分单词?
我认为应该根据使用的语言种类进行具体分析。
使用拼音文字语言的词法分析可以用空格区分单词,但是也存在例外情况。比如关键字、
标识符与常数之间因为没有确定的运算符或界符做间隔时应用空格区分,反之可不用空
格区分。所以空格区分可以作为词法分析中的一项功能,但并非不同符号间一定要用空
格区分,比如若词法分析因为赋值语句“x+y=z”的字符之间没有空格区分而将其识别为
一个变量名就会出错。
如果某种语言规定以空白作为符号间界符那么用空格作为区分单词的方法便是可行的。
对于 C 语言来说如果以空格作为初选单词的方式,后续将初步分割的部分再进行词法分
析也是可行的,但是实际上完全没有必要。
2. 程序设计中哪些环节影响词法分析的效率?如何提高效率?
(1)关键字的识别对效率有重要影响。每次识别出标识符后都会与关键字表进行比较,
顺序比较法在关键字数量较少的情况下影响不明显,但是如果关键字数量较多则查询时
间就会明显影响到分析效率。
提高效率的方法:
1. 综合考虑关键字数量合理安排保留字表
如按照哈希链表的方式存放,数组不同单元链接不同字母开头的关键字,同一链表
中关键字按同一字母开头。
2. 在预选过程中先判断标识符长度是否已经超出关键字的最大长度,从而减少与关键
字表的比较次数。
4 / 15
1. 语法制导定义
产生式
S → id
S → if
S1
=
E
;
C
then
S → while
S1
C
do
实验二
语义规则
S.code = E.code ++ gen(id.place’:=’E.place)
C.true = newlabel;
C.false = S.next;
S1.next = S.next;
S.code = C.code ++ gen(E.true’:’) ++ S1.code
S.begin = newlabel;
C.true = newlabel;
C.false = S.next;
S1.next = S.begin;
S.code = gen(S.begin’:’) ++ C.code ++
gen(E.true’:’) ++ S1.code ++ gen(‘goto’S.begin);
S → begin B end
B → S
B → S ; B1
C → E1
>
E2
B.next = S.next;
S.code = B.code ++ gen(S.next’:’)
S.next = B.next;
B.code = S.code;.
B1.next = B.next;
S.next = newlabel;
B.code = S.code ++ gen(S.next’:’) ++ B1.code
C.code = E1.code ++ E2.code ++
gen(‘if’E1.place’>’E2.place’goto’C.true) ++
gen(‘goto’C.false)
C → E1
<
E2
C.code = E1.code ++ E2.code ++
gen(‘if’E1.place’<’E2.place’goto’C.true) ++
gen(‘goto’C.false)
C → E1
=
E2
C.code = E1.code ++ E2.code ++
gen(‘if’E1.place’=’E2.place’goto’C.true) ++
gen(‘goto’C.false)
E → E1
+
T
E → E1
-
T
T → T1
*
F
T → T1
/
F
E.place = newtemp;
E.code = E1.code ++ T.code ++
gen(E.place’:=’E1.place’+’T.place)
E.place = newtemp;
E.code = E1.code ++ T.code ++
gen(E.place’:=’E1.place’-’T.place)
T.place = newtemp;
T.code = T1.code ++ F.code ++
gen(T.place’:=’T1.place’*’F.place)
T.place = newtemp;
T.code = T1.code ++ F.code ++
gen(T.place’:=’T1.place’/’F.place)
F → (
E
)
F.place = E.place;
5 / 15
F → id
F → int8
F → int10
F → int16
F.code = E.code
F.place = id.name;
F.code = ‘ ‘
F.place = int8.value;
F.code = ‘ ‘
F.place = int10.value;
F.code = ‘ ‘
F.place = int16.value;
F.code = ‘ ‘
2. 改写后的产生式集合
需要消除左递归并提取左因子的产生式 改写后的产生式
B -> S
B -> S ; B1
B -> S | S ; B’
C -> E1 > E2
C -> E1 E1 = E2
E -> E + T
E -> E - T
E -> T
T -> T1 * F
T -> T1 * F
T -> F
3. 化简后的语法图
S 的语法图
S
C 的语法图
C -> EC’
C’ -> >E | < E | = E
E -> TE’
E’ -> + TE’ | -TE’
T -> FT’
T -> *FT’ | /FT’
If
While
Id
C
C
=
then
do
E
6 / 15
E 的语法图
T 的语法图
F 的语法图
4. 递归子程序的算法
void S(struct
{
S_Attr *pS)
struct C_Attr aC
struct
S_Attr aS
struct
E_Attr aE
struct B_Attr aB
7 / 15
char idn [50];
if(lookhead==IF)
{
aC.iTrue = newlabel();
aC.iFalse = pS->iNext;
aS.iNext = pS->iNext;
match(IF);
C(&aC);
match(THEN);
S(&aS);
sprintf_s(pS->pCode,MAX,"\t%s\nL%d:\n%s",aC.pCode,aC.iTrue,aS.pCode);
}
else if(lookhead== WHILE)
{
pS->iBegin = newlabel();
aC.iTrue = newlabel();
aC.iFalse = pS->iNext;
aS.iNext = pS->iBegin;
match(WHILE);
C(&aC);
match(DO);
S(&aS);
sprintf(pS->pCode,"L%d:\t%s\nL%d:\t%s\tgoto\tL%d",pS->iBegin,aC.pCode,aC.iTru
e,aS.pCode,pS->iBegin);
}
else if(lookhead== 'IDN')
{
strcpy_s(idn,50,attr2);
match('IDN');
match('ASG');
E(&aE);
if(flag==0)
match('SEMI');
sprintf(pS->pCode,"%s\n\t%s:=%s",aE.pCode,idn,aE.place);
}
else if(lookhead==BEGIN)
{
flag++;
aB.iNext = pS->iNext;
match(BEGIN);
B(&aB);
match(END);
flag--;
8 / 15