1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
引言
《编译原理》是计算机专业的一门重要的专业课程,其中包含大量软件设计思想。通过课程设计,实现一些重要的算法,或设计一个完整的编译程序模型,能够进一步加深理解和掌握所学知识,对提高自己的软件设计水平具有十分重要的意义。
我选择的是老师给的第31题,并予以扩充。即对任意给定的文法G构造LR(1)项目集规范族,其中要实现CLOSURE(I)、GO(I,X)、FIRST集合等。在此基础上,构造了LR(1)分析表。然后对输入的句子进行语法分析,给出接受或出错报告。程序采用文件输入输出方式。其中包括两个输入文件:文法grammar.txt,以及输入串input.txt;两个输出文件:项目集items.txt和文法的LR(1)分析表action_table.txt。由于语法分析的结果只给出接受或错误报告,比较简单。所以直接在屏幕上输出,也便于用户查看。
在具体编写程序过程中,对文法操作的各个功能模块独立成为一个子程序,而对具体输入串的分析则放在main()函数中进行。各个变量及函数的意义和用法我将在叙述程序设计的总体方案中详细给出。
程序的总体算法思想来自《编译原理》课程。具体实现由我独立完成。程序用C/C++语言编写。在Microsoft Visual C++ 2005环境下调试通过。
第一章 概述
1.1 设计内容
对任意给定的上下文无关文法G,构造其LR(1)项目集族,并且在此基础上进一步构造其LR(1)分析表。然后分析输入的“句子”。
1.2 设计要求
对输入的文法G(要求是上下文无关文法),在程序中实现CLOSURE(I)、GO(I,X)、FIRST等的构造,并利用这些功能函数构造出LR(1)项目集族。并且输出结果。在此基础上构造出G的LR(1)分析表(这个表也输出给用户),并对输入的“句子”进行语法分析,给出分析结果。
第二章 设计的基本原理
2.1 CLOSURE(I)的构造
CLOSURE(I)表示和I中项目可以识别同样活前缀的所有项目的集合。它可以由以下方法得到:
⑴ I中的所有项目都属于CLOSURE(I);
⑵ 若项目[A→α·Bβ,a]属于CLOSURE(I),B→ξ是一个产生式,那么,对于FIRST(βa)中的每一个终结符b,如果[B→·ξ,b]原来不在CLOSURE(I)中,则把它加进去;
⑶ 重复执行步骤⑵,直到CLOSURE(I)不再增大为止。
2.2 GO(I,X)的构造
GO(I,X) = CLOSURE(J)
其中 J={任何形如[A→αX·β,a]的项目|[A→α·Xβ,a]属于I}
2.3 FIRST集合的构造
在这个程序中使用的是FIRST(βa),这基于每一个非终结符的FIRST集合(终结符的FIRST就是它本身)。所以需要对每一个非终结符构造其FIRST集合。方法如下:
连续使用下面的规则,直到每个集合FIRST不再增大为止。
⑴ 若X属于VT,则FIRST(X)= {X}。
⑵ 若X属于VN,且有产生式X→a…,则把a加入到FIRST(X)中;若X→ε也是一条产生式,则把ε也加入到FIRST(X)中。
⑶ 若X→Y…是一个产生式且Y属于VN,则把FIRST(Y)中的所有非ε元素都加入到FIRST(X)中;若X→Y1Y2…Yk是一个产生式,Y1,…,Yi-1都是非终结符,而且,对于任何j,1<= j <= i-1,FIRST(YJ)都含有ε(即Y1…Yi-1=>ε),则把FIRST(Yi)中的所有非ε元素都加入到FIRST(X)中;特别的,若所有的FIRST(YJ)都含有ε,j=1,2,3…k,则把ε加入到FIRST(X)中。
2.4 LR(1)分析表的构造
在实现GO(I,X)时,记录下状态的转化。得到分析表中的移进部分。然后,再扫描所有的项目集,找到其中包含归约项目的那些项目集,根据其中的项目,得到分析表中那些归约的部分。
第三章 程序设计
3.1总体方案设计
在main()函数中读入文法。并对文法进行扩展,同时记录下文法的所有终结符和非终结符。对每一个非终结符计算它的FIRST集合。以备在计算CLOSURE(I)时使用。然后,调用GO()函数。完成LR(1)项目集族的计算。计算的结果记录到items.txt中。并且记录下状态之间的转换关系。接下来,调用get_action()根据上面的项目集族和记录的状态转换数组获得LR(1)分析表。然后就可以对输入的句子进行语法检查。程序中主要变量以及函数的说明如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
|
3.2各模块设计
3.2.1 读入模块:Read_G()
文法要为上下文无关文法。输入文件的格式为:首先输入产生式的条数;每条产生式的第一个字符为非终结符。以$结尾。输入的同时用tempofinput[temp] = true来记录字符temp。为统计有哪些非终结符和终结符作准备。这些都通过ASCLL码对应位是否为true来判断。
3.2.2 计算FIRST模块:get_first()
先设置flag1表示本轮扫描first_vn中有没有新增加的内容。要是有,还要进行下一次扫描。每一轮扫描所有的产生式,在扫描每一个产生式的时候,设置一个下标指针t用来保证不会扫过本产生式,还设置flag2表示t的位置是否是一个可以推导出ε的非终结符。是的话,还要进行下一个t位置的检查。如果t走到产生式的最后位置的下一个位置,则表明ε属于此产生式左边非终结符的FIRST集合;
3.2.3 判断项目数否在项目集里:is_in(proj temp,int T)
Scan项目集原有的每一个项目,和新生成的项目作比较。若有相同的就返回true,否则返回false
3.2.4 获得计算closure(I)时需要的First(βa):gete_expc(proj temp)
设置flag表示是否还要进行下一轮计算(即此次处理的为非终结符且它的FIRST中有ε),若处理的位置已经超过了产生式的长度,则直接把项目中的那个搜索字符添加进去。这个模块的返回结果放在buffer数组中。
3.2.5 项目集的CLOSURE计算:e_closure(int T)
在Go()函数中会生成items[T]的一些基本项目。对items[T]中已经有的每一个项目检查在”·”之后的是否为非终结符;若是,则计算FIRST(βa),把每一个buffer中的元素和相应的产生式构成一个项目,加入到项目集中。(注意,此时的项目集的大小是随着项目的不断加入而变大的,所以可以用for循环保证项目集中不会有遗漏。)
3.2.6 检查项目集是否已经在项目集族里:is_contained()
把已经有的项目集和新生成的项目集进行比较,要是有相等的话则表示已经存在相同的项目集合,此时返回相同的那个项目集的编号。否则,返回0。
3.2.7 GO()函数的实现:go()
第一步制作一个初始项目(即拓展文法的第一条产生式),然后用e_closure构造项目集0。在程序中Ccount 作为项目集的计数从0开始到n(包括n),所以在for循环中是<= Ccount。即扫描每一个项目集,对每一个项目在”·”之后的终结符,向后移动一位“·”的位置生成新的项目,暂存在buf数组中。然后,预生成项目集,并且求其CLOSURE,再判断新的项目集是否已经存在,若存在了,就撤销这个项目集,并设置相应的trans。否则就生成新的项目集,也设置相应的trans。在以上过程中,每次确定生成一个项目集的时候都把它输出到items.txt中。
3.2.8 LR(1)分析表的构造:get_action()
Scan每一个项目集,若其中有规约项目,则转换表增加一个归约项(用负值表示)。然后,根据trans数组中的元素,构造转换表中的移进项(用正值表示)。 接受项目也是一个归约项,用0表示。生成的转换表输出到action_table.txt中。
3.2.9 对输入串的语法分析:在main()中实现
用stack模拟语法分析的过程,先在stack中放入(0,#),然后,用当前栈顶状态和当前输入字符查找action_table。根据action_table中的值的情况做相应处理(即执行移进和归约动作)。若遇到接受项目则给出接受提示,程序结束。若遇到出错的情况给出出错提示,也结束程序。
第四章 程序测试
本程序在Dev-C++和Microsoft Visual C++ 2005中调试通过。下面给出两个调试实例:
4.1 课本115页文法的LR(1) 分析器的构造和语法分析
输入文法:
1
|
|
生成的项目集族:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
|
生成的转换表:
1 2 3 |
|
输入句子测试:
输入句子:aaaabab#
输出截图:
输入句子:abbab#
输出截图:
4.2 表达式文法的LR(1)分析器的构造和语法分析
输入文法:
1
|
|
生成的项目集族:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
|
生成的转换表:
1 2 3 4 5 6 7 8 9 10 |
|
输入句子测试:
输入句子:i+i+#
输出截图
输入句子:(i)+ii#
输出截图
第五章 结论
通过以上章节的分析与说明,可以看到,本程序在功能上完全实现了课程设计的要求。并且扩展了一些功能。即对任意给定的上下文无关文法都可以构造出相应的LR(1) 项目集族和LR(1)分析表。并且允许用户使用生成的LR(1)分析表对具体的句子进行分析。
到这里,这次的编译原理课程设计基本上已经完成了,我选择的这道题目较其他的选题有一定的难度。但是,我在克服这些难关的过程中获得了更多的是快乐和信心。同时,通过亲手构造一个简单的LR(1)分析器,我对LR(1)分析方法也有了更深一步的认识和掌握。
参考文献:
- 陈火旺 刘春林等 《程序设计语言编译原理》(第三版)国防工业出版社,2000
- 陈意云 张昱 《编译原理》 高等教育出版社 2003
- 陈意云 张昱 《编译原理习题精选与解析》高等教育出版社 2003
附录 程序清单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 |
|