<編譯原理 - 函數繪圖語言解釋器(1)詞法分析器 - python>
背景
-
編譯原理上機實現一個對函數繪圖語言的解釋器 - 用除C外的不一樣種語言實現git
-
解釋器分爲三個實現塊:數組
-
詞法分析器:用於識別一條語句中的關鍵詞是否符合預先定義的規則。緩存
-
語法分析器:用來肯定一條語句是否知足語法規則。函數
-
解釋器:用來肯定知足語法規則的句子,在乎思上是否符合要求。測試
-
-
設計思路:優化
-
設計記號:詞法分析器讀取一個序列並根據構詞規則把序列轉化爲記號流spa
-
定義一個字典:把全部符合一個模式的保留字、常量名、參數名、函數名等放進字典。字典是個數組,其元素的類型和記號的類型相同設計
-
設計程序的結構,具體見下面的代碼
-
-
用Pycharm寫了三個.py文件:
-
scannerclass.py
-
scannerfunc.py
-
scannermain.py
-
輸入流是序列(存儲在.txt文本),輸出流是「字典」(一個個識別好的記號對象)
-
測試文本序列(1):
FOR T FROM 0 TO 2*PI STEP PI/50 DRAW(COS(t),sin(t));
-
測試文本序列(2):
-
//------------------This is zhushi!!------------------------ ORIGIN IS (100,300); // Sets the offset of the origin ROT IS 0; // Set rotation Angle. SCALE IS (1,1); // Set the abscissa and ordinate scale. FOR T FROM 0 TO 200 STEP 1 DRAW (T,0); // The trajectory of the x-coordinate. FOR T FROM 0 TO 150 STEP 1 DRAW (0,-T); // The trajectory of the y-coordinate. FOR T FROM 0 TO 120 STEP 1 DRAW (T,-T); // The trajectory of the function f[t]=t. ```
函數繪圖語言介紹
-
語句介紹
-
函數繪圖源程序舉例介紹
-
畫出的圖形介紹
Step 1 :scannerclass.py - 構造枚舉類 記號類 符號表
from enum import Enum import math Token_Type = Enum('Token_Type', ('ORIGIN', 'SCALE', 'ROT', 'IS', 'TO', 'STEP', 'DRAW', 'FOR', 'FROM', #保留字 'T', #參數 'SEMICO', 'L_BRACKET','R_BRACKET','COMMA', #分隔符 'PLUS','MINUS','MUL','DIV','POWER', #運算符 'FUNC', #函數符 'CONST_ID', #常數 'NONTOKEN', #空記號 'ERRTOKEN')) #出錯記號 class Tokens: #記號類 #type:記號類別 #lexeme:輸入的字符串/屬性 #value:常數值 #funcptr:函數指針 def __init__(self,type,lexeme,value,funcptr): self.lexeme=lexeme self.value=value self.funcptr=funcptr if type in Token_Type: self.type = type else: print("Invalid type") # 後續待填充 Alphabet=dict([('PI',Tokens(Token_Type.CONST_ID,"PI",3.1415926,None)), ## 符號表 ('E',Tokens(Token_Type.CONST_ID,"E",2.71828,None)), ## 左key右value ('T',Tokens(Token_Type.T,'T',0.0,None)), ('SIN',Tokens(Token_Type.FUNC,'SIN',0.0,math.sin)), # math.sin / math.sinh ('COS',Tokens(Token_Type.FUNC,'COS',0.0,math.cos)), ('TAN',Tokens(Token_Type.FUNC,'TAN',0.0,math.tan)), ('LN',Tokens(Token_Type.FUNC,'LN',0.0,math.log)), ('EXP',Tokens(Token_Type.FUNC,'EXP',0.0,math.exp)), ('SQRT',Tokens(Token_Type.FUNC,'SQRT',0.0,math.sqrt)), # 後續操做待填充 ('ORIGIN',Tokens(Token_Type.ORIGIN,'ORIGIN',0.0,None)), ('SCALE',Tokens(Token_Type.SCALE,'SCALE',0.0,None)), ('ROT',Tokens(Token_Type.ROT,'ROT',0.0,None)), ('IS',Tokens(Token_Type.IS,'IS',0.0,None)), ('FOR',Tokens(Token_Type.FOR,'FOR',0.0,None)), ('FROM',Tokens(Token_Type.FROM,'FROM',0.0,None)), ('TO',Tokens(Token_Type.TO,'TO',0.0,None)), ('STEP',Tokens(Token_Type.STEP, 'STEP', 0.0, None)), ('DRAW',Tokens(Token_Type.DRAW, 'DRAW', 0.0, None))])
Step 2 :scannerfunc.py - 構造詞法分析器類
import scannerclass as sc import os class scanner(): ##——————初始化詞法分析器 def __init__(self,file_name): #輸入要輸入字符流的文件名 self.LineNo = 0 #記錄字符所在行的行號 self.TokenBuffer = '' #待識別記號緩存區 self.file_name=r'C:\Users\62473\Desktop\\'+file_name #此處根據我的狀況作調整 if os.path.exists(self.file_name): self.fp = open(self.file_name, "r") #文件指針 else: self.fp = None ##——————關閉詞法分析器 def CloseScanner(self): if self.fp!=None: self.fp.close() ##——————從輸入流中讀入一個字符 def GetChar(self): Char = self.fp.read(1) return Char ##——————輸入流回退一個字符 def BackChar(self,Char): ## 非二進制打開方式不能直接seek目前位置回溯,因此用tell()-1方式從頭跳轉前一位置 if Char != '': self.fp.seek(self.fp.tell()-1) ##——————加入字符到TokenBuffer待識別字符串中 def AddCharToString(self,Char): self.TokenBuffer+=Char ##——————清空TokenBuffer字符串 def EmptyString(self): self.TokenBuffer='' ##——————識別的字符串查表 def JudgeKeyToken(self): Token=sc.Alphabet.get(self.TokenBuffer,sc.Tokens(sc.Token_Type.ERRTOKEN,self.TokenBuffer,0.0,None)) return Token ##——————獲取記號 # 此函數由DFA轉化而來(有必要的話能夠寫個模擬dfa函數)此函數輸出一個記號。每調用該函數一次,僅僅得到一個記號。 # 所以,要得到源程序的全部記號,就要重複調用這個函數。上面聲明的函數都被此函數調用過 # 由於沒有自定義變量,因此只須要查表不須要構造其餘東西 # 輸出一個記號,沒有輸入 def GetToken(self): Char = '' ##字符流 type = '' ##指向返回輸出的Tokens對象 self.EmptyString() #清空緩衝區 while(1): Char = self.GetChar() if Char == '': type = sc.Tokens(sc.Token_Type.NONTOKEN,Char,0.0,None) return type if Char == '\n': self.LineNo=self.LineNo+1 if ~Char.isspace(): break self.AddCharToString(Char) ##若不是空格、TAB、回車、文件結束符等,則先加入到記號的字符緩衝區中 if Char.isalpha():## 判斷是不是英文 while(1): Char = self.GetChar() if Char.isalnum(): self.AddCharToString(Char) else: break self.BackChar(Char) type = self.JudgeKeyToken() type.lexeme = self.TokenBuffer return type elif Char.isdigit(): while(1): Char = self.GetChar() if Char.isdigit(): self.AddCharToString(Char) else: break if Char == '.': self.AddCharToString(Char) while(1): Char = self.GetChar() if Char.isdigit(): self.AddCharToString(Char) else: break self.BackChar(Char) type = sc.Tokens(sc.Token_Type.CONST_ID,self.TokenBuffer,float(self.TokenBuffer),None) return type else: if Char == ';': type = sc.Tokens(sc.Token_Type.SEMICO,Char,0.0,None) elif Char == '(': type = sc.Tokens(sc.Token_Type.L_BRACKET,Char,0.0,None) elif Char == ')': type = sc.Tokens(sc.Token_Type.R_BRACKET, Char, 0.0, None) elif Char == ',': type = sc.Tokens(sc.Token_Type.COMMA, Char, 0.0, None) elif Char == '+': type = sc.Tokens(sc.Token_Type.PLUS, Char, 0.0, None) elif Char == '-': ##多是行分割或減號 Char = self.GetChar() if Char == '-': while Char != '\n' and Char != '': Char = self.GetChar() self.BackChar(Char) return self.GetToken() else: self.BackChar(Char) type = sc.Tokens(sc.Token_Type.MINUS, '-', 0.0, None) elif Char == '/': ##多是註釋分割或除號 Char = self.GetChar() if Char == '/': while Char != '\n' and Char != '': Char = self.GetChar() self.BackChar(Char) return self.GetToken() else: self.BackChar(Char) type = sc.Tokens(sc.Token_Type.DIV, '/', 0.0, None) elif Char == '*': Char = self.GetChar() if (Char == '*'): type = sc.Tokens(sc.Token_Type.POWER, '**', 0.0, None) else: self.BackChar(Char) type = sc.Tokens(sc.Token_Type.MUL, '*', 0.0, None) else: type = sc.Tokens(sc.Token_Type.ERRTOKEN, Char, 0.0, None) return type
Step 3 :scannermain.py - 完成I/O流
import scannerclass as sc import scannerfunc as sf file_name = 'test.txt' ##放在桌面的測試文本 scanner = sf.scanner(file_name) if scanner.fp != None: print(' 記號類別 字符串 常數值 函數指針\n') print('——————————————————————') while(1): token = scanner.GetToken() #輸出一個記號 if token.type == sc.Token_Type.ERRTOKEN: ##優化空格 #記號的類別不是錯誤或者空格,就打印出他的內容 continue elif token.type != sc.Token_Type.NONTOKEN: ## 到了語法分析時這塊須要改爲ERRTOKEN,由於須要輸出NONTOKEN print("{:20s},{:>12s},{:12f},{}".format(token.type, token.lexeme,token.value,token.funcptr)) else: break ## 文件結束符直接跳下一行讀取數據放在語法分析器裏面完成以前的bug else: print('Open Error!')
實現結果
-
對於測試文本(1)
FOR T FROM 0 TO 2*PI STEP PI/50 DRAW(COS(t),sin(t));
的測試運行結果以下: -
換一組測試文本(2)進行的測試運行結果以下:
原文出處:https://www.cnblogs.com/ymjun/p/11773681.html