本学期编译原理的一个大作业,我的选题是算术表达式的词法语法语义分析,当时由于学得比较渣,只用了递归下降的方法进行了分析。
首先,用户输入算术表达式,其中算术表达式可以包含基本运算符,括号,数字,以及用户自定义变量。
词法分析,检查单词变量是否正确;语法分析,检查算术表达式语法是否正确并输出生成语法树;语义分析,输出四元表达式。
最终效果图:
例如输入:
词法分析结果:
语法分析结果:
语义分析结果:
算术表达式的组成语法如下:
无符号整数 = 〈数字〉{〈数字〉}
〈标识符〉= 〈字母〉{〈字母〉|〈数字〉}
表达式〉= [+|-]〈项〉{〈加减运算符〉〈项〉}
〈项〉= 〈因子〉{〈乘除运算符〉〈因子〉}
〈因子〉= 〈标识符〉|〈无符号整数〉|‘(’〈表达式〉‘)’
〈加减运算符〉= +|-
〈乘除运算符〉= *|/
注意:
#标识符以字母开头,仅包含字母和数字
#字母包含大写和小写字母
符号文法表示:
Indentifer: 标识符 digit:数字 M:表达式
项:T 因子:F
M -> +E|-E|E
E -> E+T|E-T|T
T -> T*F|T/F|F
F -> (E)|indentifer|digit
消除左递归,改进文法:
1. M -> +E|-E|E
2. E -> TE~
3. E~ -> +TE~|-TE~|&
4. T -> FT~
5. T~ -> *FT~|/FT~|&
6. F -> (E)|indentifer|digit
1.词法分析
单词类别定义
运算符:( , ) , + , - , * , / 类别码:3
标识符:〈字母〉{〈字母〉|〈数字〉} 类别码:1
无符号整数:〈数字〉{〈数字〉} 类别码:2
设计思路
依次接受所输入的字符串,根据DFA进行判断单词类型,将单词及符号内码存入符号表字典,将单词存入单词栈
1.如若接收到字母说明为标识符,接着一直接受字母和数字,直到出现非数字和非字母符号
2.如若在运算符后接收到数字,则说明为无符号整数,一直接受直到出现非数字符号
3.如若接受到运算符,则继续处理
简单绘制的DFA:
数据结构
符号表:dic={}
单词栈:table=[]输入数据
2.语法分析
为文法中的每一个非终结符号设计对应的处理程序,处理程序按照具体的文法接受顺序设计,每当程序选择其中一个文法时,将其保存并打印出来,若单词栈中的所有单词都被接受,则说明语法正确,其他情况,则说明语法错误
数据结构
dic={} #符号表
table=[] #单词栈
wenfa=[] #字符串文法
3.语义分析与中间代码生成
设计思路
这里我运用的依旧是递归下降的思想,我并没有利用语法分析的结果,而是利用词法分析的结果为每一个非终结符号设计相应的程序, 当结果足够生成四元式时,将其输出。将结果赋给给临时变量,传递给父项。
数据结构
table=[] #单词栈
siyuan=[] #四元式
源码:
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 | #-*- coding=utf-8 -*- letter = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' number = '0123456789' operater = '+-*/()' dic = {} #符号表 table = [] #单词栈 wenfa = [] #字符串文法 siyuan = [] #四元式 ##################################### 词法分析 ###################################### def cifa(string): #词法分析 print '' m = 0 state = 0 #1:为标识符 2:为数字串 3:为运算符 for i in range ( len (string)): if string[i] in operater : #如果是运算符 if state = = 1 : #state=1表明其前面的为标识符 print string[m:i], '是标识符,类型码:1' dic[string[m:i]] = 1 table.append(string[m:i]) elif state = = 2 : #state=2表明其前面的为数字 print string[m:i], '是数字,类型码:2' dic[string[m:i]] = 2 table.append(string[m:i]) m = i + 1 state = 3 print string[i], '是运算符,类型码:3' dic[string[i]] = 3 table.append(string[i]) elif string[i] in number: #如果是数字 if i = = m: #判断此时的数字是否为整数的第一个数字,若是则改变状态为无符号整数 state = 2 elif string[i] in letter: #如果是字母 if state = = 2 : #判断此时的状态,若state=2表明状态为无符号整数,而整数内不能包含字母,故报错 print '词法分析检测到错误,数字串中不能包含字母' exit( 0 ) if i = = m: #判断此时的字母是否为标识符的第一个字母,若是则改变状态为标识符 state = 1 else : #当输入的字符均不符合以上判断,则说明为非法字符,故报错 print '词法分析检测到非法字符' exit( 0 ) if state = = 1 : #当字符串检查完后,若字符串最后部分为标识符,应将其print出来 print string[m:], '是标识符,类型码:3' dic[string[m:]] = 1 table.append(string[m:]) elif state = = 2 : #若字符串最后部分为无符号整数,应将其print出来 print string[m:], '是无符号整数,类型码:2' dic[string[m:]] = 2 table.append(string[m:]) table.append( '#' ) print '字符栈:' ,table, '\n词法正确' ################################### 语法分析 ##################################### ''' 基本文法: M -> +E|-E|E E -> TE~ E~ -> +TE~|-TE~|& T -> FT~ T~ -> *FT~|/FT~|& F -> (E)|indentifer|digit ''' class yufa(): #语法分析程序 def __init__( self ): self .i = 0 #栈指针 try : #用异常处理程序捕获程序的错误,出现异常则报错 self .m() except : print '\n语法分析程序检查到错误' exit( 0 ) def m( self ): #PM程序 if (table[ self .i] = = '+' ): self .i + = 1 wenfa.append( 'M -> +E' ) self .e() elif (table[ self .i] = = '-' ): self .i + = 1 wenfa.append( 'M -> -E' ) self .e() else : wenfa.append( 'M -> E' ) self .e() if ( self .i is not len (table) - 1 ): #语法分析结束时,若单词栈指针与单词表长度不相等,报错 print "\n语法分析程序检查到错误,'('前应该有运算符" exit( 0 ) else : print '\n字符串语法是:' #若一切正确,则输出语法树文法 for i in wenfa: print i print '语法正确' def e( self ): #PE程序 wenfa.append( 'E -> TE1' ) self .t() self .e1() def e1( self ): #PE1程序 if (table[ self .i] = = '+' ): self .i + = 1 wenfa.append( 'E1 -> +TE1' ) self .t() self .e1() elif (table[ self .i] = = '-' ): self .i + = 1 wenfa.append( 'E1 -> -TE1' ) self .t() self .e1() else : wenfa.append( 'E1 -> &' ) def t( self ): #PT程序 wenfa.append( 'T -> FT1' ) self .f() self .t1() def t1( self ): #PT1程序 if (table[ self .i] = = '*' ): self .i + = 1 wenfa.append( 'T1 -> *FT1' ) self .f() self .t1() elif (table[ self .i] = = '/' ): self .i + = 1 wenfa.append( 'T1 -> /FT1' ) self .f() self .t1() else : wenfa.append( 'T1 -> &' ) def f( self ): #PF程序 if (table[ self .i] = = '(' ): wenfa.append( 'F -> (E)' ) self .i + = 1 self .e() if (table[ self .i]! = ')' ): raise Exception self .i + = 1 elif (dic[table[ self .i]] = = 1 ): wenfa.append( 'F -> Indentifer ' + str (table[ self .i])) self .i + = 1 elif (dic[table[ self .i]] = = 2 ): wenfa.append( 'F -> Digit ' + str (table[ self .i])) self .i + = 1 else : raise Exception #若均不符合,则引出异常 ####################################### 语义分析 ####################################### class yuyi: def __init__( self ): print '\n语义分析结果(四元式):' self .i = 0 #栈指针 self .flag = 0 #记录临时变量T数目 self .m() for i in siyuan: #输出四元式结果 print i def m( self ): #PM程序 if (table[ self .i] = = '+' ): self .i + = 1 ret1 = self .e() siyuan.append( '(+,0,' + ret1 + ',out)' ) self .flag + = 1 elif (table[ self .i] = = '-' ): self .i + = 1 ret2 = self .e() siyuan.append( '(-,0,' + ret2 + ',out)' ) self .flag + = 1 else : ret3 = self .e() siyuan.append( '(=,' + ret3 + ',0,out)' ) def e( self ): #PE程序 ret1 = self .t() ret2,ret3 = self .e1() if (ret2! = '&' ): #若ret2不为&,则可以产生四元式,否则将变量传递给父项 self .flag + = 1 siyuan.append( '(' + ret2 + ',' + ret1 + ',' + ret3 + ',T' + str ( self .flag) + ')' ) return 'T' + str ( self .flag) else : return ret1 def e1( self ): #PE1程序 if (table[ self .i] = = '+' ): self .i + = 1 ret1 = self .t() ret2,ret3 = self .e1() if (ret2 = = '&' ): return '+' ,ret1 else : self .flag + = 1 siyuan.append( '(' + ret2 + ',' + ret1 + ',' + ret3 + ',T' + str ( self .flag) + ')' ) return '+' , 'T' + str ( self .flag) elif (table[ self .i] = = '-' ): self .i + = 1 ret1 = self .t() ret2,ret3 = self .e1() if (ret2 = = '&' ): return '-' ,ret1 else : self .flag + = 1 siyuan.append( '(' + ret2 + ',' + ret1 + ',' + ret3 + ',T' + str ( self .flag) + ')' ) return '-' , 'T' + str ( self .flag) else : return '&' , '&' def t( self ): #PT程序 ret1 = self .f() ret2,ret3 = self .t1() if (ret2! = '&' ): self .flag + = 1 siyuan.append( '(' + ret2 + ',' + ret1 + ',' + ret3 + ',T' + str ( self .flag) + ')' ) return 'T' + str ( self .flag) else : return ret1 def t1( self ): #PT1程序 if (table[ self .i] = = '*' ): self .i + = 1 ret1 = self .f() ret2,ret3 = self .t1() if (ret2 = = '&' ): return '*' ,ret1 else : self .flag + = 1 siyuan.append( '(' + ret2 + ',' + ret1 + ',' + ret3 + ',T' + str ( self .flag) + ')' ) return '*' , 'T' + str ( self .flag) elif (table[ self .i] = = '/' ): self .i + = 1 ret1 = self .f() ret2,ret3 = self .t1() if (ret2 = = '&' ): #若ret2不为&,则可以产生四元式,否则将变量传递给父项 return '/' ,ret1 else : self .flag + = 1 siyuan.append( '(' + ret2 + ',' + ret1 + ',' + ret3 + ',T' + str ( self .flag) + ')' ) return '/' , 'T' + str ( self .flag) else : return '&' , '&' def f( self ): #PF程序 if (table[ self .i] = = '(' ): self .i + = 1 ret1 = self .e() self .i + = 1 return str (ret1) elif (dic[table[ self .i]] = = 1 ): #当为标识符时,传递给父项 temp = self .i self .i + = 1 return table[temp] elif (dic[table[ self .i]] = = 2 ): #当为整数时,传递给父项 temp = self .i self .i + = 1 return table[temp] ####################################### 主程序 ####################################### if __name__ = = '__main__' : string = raw_input ( '请输入表达式:' ) cifa(string) yufa() yuyi() |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?