<編譯原理 - 函式繪圖語言直譯器(1)詞法分析器 - python>
阿新 • • 發佈:2019-10-31
<編譯原理 - 函式繪圖語言直譯器(1)詞法分析器 - python>
背景
編譯原理上機實現一個對函式繪圖語言的直譯器 - 用除C外的不同種語言實現
- 直譯器分為三個實現塊:
詞法分析器:用於識別一條語句中的關鍵詞是否符合預先定義的規則。
語法分析器:用來確定一條語句是否滿足語法規則。
直譯器:用來確定滿足語法規則的句子,在意思上是否符合要求。
- 設計思路:
設計記號:詞法分析器讀取一個序列並根據構詞規則把序列轉化為記號流
定義一個字典:把所有符合一個模式的保留字、常量名、引數名、函式名等放進字典。字典是個陣列,其元素的型別和記號的型別相同
設計程式的結構,具體見下面的程式碼
- 用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):
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,int(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:
print("{:20s},{:>12s},{:12f},{}".format(token.type, token.lexeme,token.value,token.funcptr))
else:
break
else:
print('Open Error!')
實現結果
對於測試文字(1)
FOR T FROM 0 TO 2*PI STEP PI/50 DRAW(COS(t),sin(t));
的測試執行結果如下:
換一組測試文字(2)進行的測試執行結果如下: