本學期編譯原理的一個大作業,我的選題是算術表達式的詞法語法語義分析,當時由於學得比較渣,只用了遞歸下降的方法進行了分析。
首先,用戶輸入算術表達式,其中算術表達式可以包含基本運算符,括號,數字,以及用戶自定義變量。
詞法分析,檢查單詞變量是否正確;語法分析,檢查算術表達式語法是否正確並輸出生成語法樹;語義分析,輸出四元表達式。
最終效果圖:
例如輸入:

詞法分析結果:

語法分析結果:

語義分析結果:

算術表達式的組成語法如下:
無符號整數 = 〈數字〉{〈數字〉}
〈標識符〉= 〈字母〉{〈字母〉|〈數字〉}
表達式〉= [+|-]〈項〉{〈加減運算符〉〈項〉}
〈項〉= 〈因子〉{〈乘除運算符〉〈因子〉}
〈因子〉= 〈標識符〉|〈無符號整數〉|‘(’〈表達式〉‘)’
〈加減運算符〉= +|-
〈乘除運算符〉= *|/
注意:
#標識符以字母開頭,僅包含字母和數字
#字母包含大寫和小寫字母
符號文法表示:
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=[] #四元式
源碼:
#-*- 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()
