使用Python語言設計基於HTML的C語言文法加亮顯示程式2005-2006學年第1學期 編 譯 原 理 課 程 設 計 報 告 班級 02計(二) 學號 19 姓名 劉曉明 成績 指導教師 盧朝輝 一、 設計目的 加深對編譯原理的進一步認識,加強實踐動手能力和程式的開發能力培養,提高分析問題和解決問題的能力。 二、 設計任務 1.單詞識別 C語言常數 C語言識別項 2.程式的文本處理 將C語言的所有注釋字母大寫 將C語言的所有保留字大寫 3.遞迴下降分析 三、 設計過程 1.總體設計 通過讀入C語言源檔案之後產生相關的詞法分析,並輸出成經過詞法加亮的HTML檔案用於顯示。另外輸出單詞符號表。產生的HTML檔案的檔案名稱為out.html,單詞符號表檔案為token.txt。 運行方法為:進入dist檔案夾運行main *.c。這裡的*替換為C語言檔案名稱,尾碼名為C,但是也可以使用其他尾碼名。使用預設設定的啟動請直接雙擊dist目錄下run.bat檔案,預設分析sample.c檔案。 程式分為三個模組:HTML模組負責提供HTML檔案產生相關的細節;wordfix模組提供詞法分析的步驟;main模組提供了檔案I/O和程式總體控制。 2.HTML.py 實現了HTML檔案的相關細節。包含以下函數: writehead() 用於產生HTML檔案頭 writeline(line) 用於輸出一些資料到HTML檔案並加入兩種換行,分別實現HTML源檔案和HTML顯示格式的換行 writeident(line) 輸出標識符到HTML檔案 writekeyword(line) 輸出關鍵字到HTML檔案 writecomment(line) 輸出注釋和預先處理串到HTML檔案 writeconst(line) 輸出常數和字串等常量到HTML檔案 writeoper(line) 輸出算符和界符到HTML檔案 writetail(line) 輸出HTML檔案的結尾並關閉HTML檔案 fixmark(instr) 由於瀏覽器無法顯示一些特殊字元,只能事先在HTML檔案中轉換成其他字串,fixmark函數提供這種轉換。需要轉換的字元包括幾種空白和&、"、>、<等。 HTML模組最後提供了單元測試用的主方法。模組只有一個成員outfile用於全域的儲存輸出HTML的檔案控制代碼。 3.main.py 提供了程式啟動和檔案I/O的操作。包括以下函數: openfile(filename) 在具備錯誤處理的情況下開啟檔案的服務,開啟成功則返迴文件控制代碼,失敗則返回False showfile(filename) 提供檔案開啟測試和顯示功能 主方法提供了檔案開啟,設定各個模組全域變數等服務。並開啟了詞法分析器。 4.wordfix.py 詞法分析模組,包含如下幾個成員: infile 用於讀入的源檔案,使用C語言的子集 outfile 用於輸出的HTML檔案控制代碼,程式中不直接使用 tokenfile 用於輸出單詞符號的檔案控制代碼 outlines 輸出轉換大小寫以後的來源程式,字串數組 htmllines 輸出轉換成功的HTML檔案,字串數組 identlist 標識符列表,字串數組 digitlist 數字常量列表,字串數組 stringlist 字串列表,字串數組,儲存從程式中讀出的C語言字串。 keywords C語言和少數C++的保留字列表 模組包含的函數如下: IsDigit(d) 判斷輸入的字元是否是數字,返回True和False IsChar(c) 判斷輸入的字元是否是陰文字元,包括大寫和小寫 IsBlank(b) 判斷輸入字元是否是空白,空白包括空格、定位字元、分行符號、斷行符號 IsKeyword(word) 判斷輸入的單詞是否是一個保留字,如果是則返回所在的位置,如果不是則返回False wordfix() 詞法分析函數,按照文檔中的狀態轉換圖編寫。因為程式需要輸出具有格式和保留注釋的來源程式,所以掃描器每次讀入的不再是緩衝區,而是每次讀入一行。對讀入的行進行識別和處理。 模組的最後也提供了單元測試的主函數。 5.單詞識別過程 每次讀入一行之後前將行開頭的空白去除並寫入HTML文檔,之後查看是否還有其他符號,如果沒有則是一個空行,繼續下一行。如果有內容區分幾種情況,讀入的第一個字元如果是英文字母或底線則進入標識符的識別。標識符的下一步允許有英文字母、底線和數字,最終讀入其他字元而停止。得到標識符後尋找保留字表,並判定是否是保留字,之後分別處理。 如果第一個字元是數字則進入數字識別,數位下一個字元允許是數字和小數點。識別之後存入數字常數列表。 如果下面字元是'//'或'#'則識別為單行注釋,在HTML檔案上也顯示為單行注釋,但是在更改大小寫時候會區分。單行注釋以換行為結束 如果下面字元是'/*'則進入了多行注釋,程式也進入了多行注釋狀態。這是在接下來的同一行中尋找'*/'字串,如果找到則退出多行注釋狀態。之後開始輸出多行注釋。並在下一次讀入一行的時候判斷是否出於多行注釋狀態,如果仍然出於多行注釋狀態則繼續尋找'*/'字串,找到則退出多行注釋狀態,找不到則繼續輸出注釋到HTML檔案。 如果讀入了'"',即雙引號則開始進入字串識別狀態,字串中的任何字元都不被識別為其他詞法符號。字串不允許直接換行。 之後是雙字元算符處理,包括較為常用的12個算符。 還有單字元算符處理,包括常用的22個算符。 最下面的例外處理,所有例外符號都作為其他符號而繼續寫入源檔案中。 程式的異常處理部分主要是為了處理讀入字串數組序號以外的索引而設定的。發生原因就是不可預料的行結束。這裡要按照之前的不同狀態來設定行結束的不同處理。 四、 設計體會 這次課程設計當中我實踐了最新學到的編譯原理知識和接觸尚不足20天的Python語言,實踐當中有了一些體會。首先是編譯原理中知識的實用性很強,在後來的編程中都是非常具體的指導,具體到變數設定。編譯原理課程中的很多工具,比如狀態轉換圖、有限自動機等都是很好的工具,可以極大的降低編譯器設計的難度。書上提供的詞法分析器例子也給了我很大的協助。 另外,關於Python語言,是我在上月25日才開始接觸的一門語言,給了我很深的印象,他是一種讓人在編程中感到舒服的語言。使用Python編寫詞法分析器也是這個學期多次編程實踐中唯一一次提前完成任務的例子。我通過11個小時的編程就完成了約460行程式的編寫和調試工作。這個時間還包括編製C語言例子和HTML檔案處理和顏色配置等等工作。 在本次課程設計中除因為時間緊迫而使用Windows系統外,其他的軟體工具全部為開源軟體,包括:Python2.4.2、vim6.3、Notepad++3.4、gcc3.3.3、grep。這也讓我對完全使用開源軟體進行工作有了信心。 #setup.py from distutils.core import setup import py2exe setup(console=["main.py","HTML.py","wordfix.py"]) # run : python setup.py py2exe # -*- coding: gb2312 -*- ##### functions ##### import sys import os import HTML import wordfix ########## global variables ########## #infile='' outfile=open('out.html','w') ########## functions ########## def openfile(filename): 'return a file handle to read' 'if return a False then failed' 'else return a file handle' if os.path.isfile(filename) == False: return False try: f=open(filename,'r') except IOError,detail: if str(detail)[1:8]=='Errno 2': return False else: print detail else: return f def showfile(filename): 'test a text file if it can show out' #print 'in showfile()' #f=open(filename,'r') f=openfile(filename) if f==False: print 'File Not Found!' sys.exit() while True: line=f.readline() if line=='': break if line[-1]=='/n': line=line[0:len(line)-1] print line f.close() ##### ##### if __name__=='__main__': # main() print 'main()' #print 'WordFix v 0.1' #print 'Copyright@1999-2006, Harry Gashero Liu.' if len(sys.argv)<2: print 'not enough params, exit!' sys.exit() else: #print 'input file:',sys.argv[1] pass #showfile(sys.argv[1]) #f.close() f=openfile(sys.argv[1]) if f==False: print 'open file failed' else: wordfix.infile=f HTML.outfile=outfile HTML.writehead() wordfix.outfile=outfile tokenfile=open('token.txt','w') wordfix.tokenfile=tokenfile wordfix.wordfix() HTML.writetail() print 'end of program' print wordfix.identlist print wordfix.digitlist print wordfix.stringlist print wordfix.outlines #fff=open('othertxt.txt','w') #for x in wordfix.outlines: # fff.write(x+'/n') #fff.close() #fff=open('list.txt','w') #for x in wordfix.identlist: # fff.write(x+'/n') #fff.close() # -*- coding: gb2312 -*- # the output file handle outfile='' def writehead(): "write a HTML file's header" outfile.write('<html><head>/n') outfile.write('<title>word fix result</title>/n') outfile.write('</head>/n<body bgcolor="#E0E8FF">/n') def writeline(line): "write a HTML section to file" outfile.write(fixmark(line)+'<BR>/n') def writeident(line): "write ident in gray" outfile.write('<font size=4 color="#0000FF">'+/ fixmark(line)+'</font>') def writekeyword(line): "write keyword in green" outfile.write('<font size=4 color="#00DD00"><b>'+ / fixmark(line)+'</b></font>') def writecomment(line): "write comment in light blue" outfile.write('<font size=4 color="#FF00FF">'+ / fixmark(line)+'</font>') def writeconst(line): "write const in red" outfile.write('<font size=4 color="#FF0000">'+ / fixmark(line)+'</font>') def writeoper(line): "write operator in yellow" outfile.write('<font size=4 color="#000000"><b>'+ / fixmark(line)+'</b></font>') def writetail(): "write a HTML file's tail" outfile.write('</body>/n</html>/n') outfile.close() def fixmark(instr): 'fix space to html space' newc='' for c in instr: if c==' ': newc+=' ' elif c=='/t': newc+=' ' elif c=='&': newc+='&' elif c=='"': newc+='"' elif c=='>': newc+='>' elif c=='<': newc+='<' elif c=='/n': newc+='<br>' else: newc+=c return newc # unit testing if __name__=='__main__': f=open('test.html','w') outfile=f ########## writehead() writeident('python ') writekeyword('int void shit ') writeline('') writecomment('a comment then') writeconst('12345') writeoper('** +++ -- new delete') writetail() f.close() # -*- coding: gb2312 -*- import HTML ##### 全域變數 ##### infile='' #輸入檔案,讀入的來源程式,檔案控制代碼類型,已經開啟 outfile='' #輸出檔案,輸出HTML來源程式,檔案控制代碼類型,已經開啟 tokenfile='' #詞法分析的單詞符號輸出檔案,檔案控制代碼類型,已經開啟 outlines=[] #輸出字串列表,包含修改過的來源程式 htmllines=[] #輸出HTML字串列表 identlist=[] #輸出的標識符表格 digitlist=[] #輸出的常數表格 stringlist=[] #輸出的字串表格 keywords=['auto','break','case','char','continue','default',/ 'do','double','else','entry','enum','extern','for',/ 'float','goto','if','int','long','new','NULL','register',/ 'return','short','signed','sizeof','static','struct',/ 'switch','typedef','union','unsigned','void','while'] def IsDigit(d): "判斷輸入字元是否為數字" if d in ['0','1','2','3','4','5','6','7','8','9']: return True else: return False def IsChar(c): "判斷輸入字元是否是英文字元,包括大寫和小寫" if (ord(c)>=ord('a') and ord(c)<=ord('z')) or / (ord(c)>=ord('A') and ord(c)<=ord('Z')): return True else: return False def IsBlank(b): "判斷輸入字元是否是空白,包括空格、定位字元、換行、斷行符號" if b in [' ','/t','/n','/r']: return True else: return False def IsKeyword(word): "判斷輸入的標識符是否是一個關鍵字" try: nnn=keywords.index(word) return nnn except ValueError: return False def wordfix(): 'word fix' newline='' ch='' start=0 nowpos=0 word='' state='' #initial HTML.outfile=outfile while True: line=infile.readline() if line=='': break if line[-1]=='/n': line=line[0:len(line)-1] # start process newline='' start=0 nowpos=0 word='' print 'LINE: ',line if state=='multicomment': #在多行注釋狀態中 newline=line try: if line.index(r'*/')!=-1: state='' except ValueError: #沒有找到多行注釋的結束符 state='multicomment' outlines.append(newline.upper()) HTML.writecomment(newline) HTML.writeline('') continue else: state='' while True: try: start=nowpos nowpos+=1 ch=line[start] #print 'doing char : ',ch,' :: ',ord(ch),'nowpos=',nowpos while IsBlank(ch): #去除所有空白 state='blank' newline+=ch HTML.writecomment(ch) start+=1 nowpos+=1 ch=line[start:nowpos] if ch=='': HTML.writeline('') break if IsChar(ch) or ch=='_': #識別標識符 state='ident' nowpos+=1 ch=line[nowpos-1:nowpos] while IsChar(ch) or IsDigit(ch) or ch=='_': nowpos+=1 ch=line[nowpos-1:nowpos] #if ch=='': # break nowpos-=1 word=line[start:nowpos] if IsKeyword(word)==False: #標識符 identlist.append(word) newline+=word tokenfile.write('ID/t/t'+word+'/n') HTML.writeident(word) else: #關鍵字 newline+=word.upper() tokenfile.write('KEY/t/t'+word+'/n') HTML.writekeyword(word) start=nowpos continue #================================ if IsDigit(ch): #識別常數 state='digit' nowpos+=1 ch=line[nowpos-1:nowpos] while IsDigit(ch) or ch=='.': nowpos+=1 ch=line[nowpos-1:nowpos] #if ch=='': # break nowpos-=1 word=line[start:nowpos] digitlist.append(word) newline+=word tokenfile.write('DIGIT/t/t'+word+'/n') HTML.writeconst(word) start=nowpos continue #================================== elif (line[start:start+2]=='//') or ch=='#': #單行注釋,C語言預先處理也作為單行注釋 state='singlecomment' print 'a single comment' word=line[start:] if ch!='#': newline+=word.upper() else: newline+=word HTML.writecomment(word) HTML.writeline('') outlines.append(newline) break #=================================== elif line[start:start+2]=='/*': #多行注釋 state='multicomment' print 'go into multi comment' try: #可以找到多行注釋結束符 nowpos=line[start+1:].index('*/') state='' nowpos+=(start+3) word=line[start:] newline+=word.upper() HTML.writecomment(word) HTML.writeline('') outlines.append(newline) start=nowpos break except ValueError: #沒有在本行找到多行注釋的結束,本行結束 state='multicomment' word=line[start:] newline+=word.upper() HTML.writecomment(word) HTML.writeline('') outlines.append(newline) break #======================================== elif ch=='"': #識別字串 state='string' try: nowpos=line[start+1:].index('"') state='' nowpos+=(start+2) word=line[start:nowpos] newline+=word HTML.writeconst(word) stringlist.append(word) start=nowpos continue except ValueError: #沒有找到字串的結束,是個錯誤,不處理 state='' word=line[start:] newline+=word HTML.writeconst(word) HTML.writeline('') outlines.append(newline) break elif line[start:start+2]=='++' or line[start:start+2]=='--' or/ line[start:start+2]=='==' or line[start:start+2]=='!=' or/ line[start:start+2]=='<<' or line[start:start+2]=='>>' or/ line[start:start+2]=='+=' or line[start:start+2]=='-=' or/ line[start:start+2]=='*=' or line[start:start+2]=='/=' or/ line[start:start+2]=='&&' or line[start:start+2]=='||': word=line[start:start+2] newline+=word nowpos+=1 HTML.writeoper(word) tokenfile.write('OPER/t/t'+word+'/n') continue elif ch=='+' or ch=='-' or ch=='(' or ch==')' or/ ch=='[' or ch==']' or ch=='*' or ch=='/' or/ ch==',' or ch=='=' or ch=='{' or ch=='}' or/ ch==';' or ch=='&' or ch=='%' or ch=='~' or/ ch=='|' or ch=='^' or ch=='?' or ch==':' or/ ch=='<' or ch=='>': state='signal' word=ch newline+=word HTML.writeoper(word) tokenfile.write('OPER/t/t'+word+'/n') continue else: state='other sign' newline+=ch #print 'doing char : ',ch,' :: ',ord(ch),' in else' tokenfile.write('sign/t/t'+ch+'/n') continue except IndexError: #讀入到了一行的末尾 if state=='blank': #在處理空白時讀到行末 outlines.append(newline) #HTML.writecomment(newline) break elif state=='ident': #在處理標識符時讀到行末 word=line[start:] newline+=word if IsKeyword(word)==False: #標識符 identlist.append(word) newline+=word tokenfile.write('ID :: '+word+'/n') HTML.writeident(word) else: #關鍵字 newline+=word tokenfile.write('KEY :: '+word+'/n') HTML.writekeyword(word) outlines.append(newline) HTML.writeline('') break elif state=='singlecomment': print 'singlecomment here' elif state=='digit': #在識別數字時讀到行末 word=line[start:] newline+=word digitlist.append(word) tokenfile.write('DIGIT :: '+word+'/n') HTML.writeconst(word) break else: #HTML.writecomment(newline) HTML.writeline('') outlines.append(newline) break ########## main() to unit testing ########## if __name__=="__main__" : if IsDigit('4'): print 'digit 4' if IsChar('c'): print 'char c' if IsBlank(' '): print 'blank' if IsKeyword('int'): print 'keyword int: ',IsKeyword('int') print 'end' ################################################## |