編譯原理上機實現一個對函數繪圖語言的解釋器 - 用除C外的不一樣種語言實現python
設計函數繪圖語言的文法,使其適合遞歸降低分析;函數
設計語法樹的結構,用於存放表達式的語法樹;測試
設計遞歸降低子程序,分析句子並構造表達式的語法樹;設計
設計測試程序和測試用例,檢驗分析器是否正確。3d
消除無二義/無左遞歸完整的EBNF文法:指針
parserclass.pycode
parserfunc.pyorm
parsermain.pyblog
輸入流是詞法分析器獲得的記號流,輸出流是語法樹遞歸
FOR T FROM 0 TO 2*PI STEP PI/50 DRAW(COS(T),SIN(T));
//------------------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.
import scannerclass as sc class ExprNode(object): ## 語法樹節點類 def __init__(self,item): ## item 表示符號類型Token_Type self.item = item #表示對應的元素 if self.item == sc.Token_Type.PLUS or self.item == sc.Token_Type.MINUS or \ self.item == sc.Token_Type.MUL or self.item == sc.Token_Type.DIV or \ self.item == sc.Token_Type.POWER: # 運算符 - 兩個孩子 self.left=None self.right=None elif self.item == sc.Token_Type.FUNC: self.FuncPtr = None self.center = None ## 一個孩子 self.value = None ## 傳入的全部類型都有value def __str__(self): ## 葉子節點 return str(self.item) #print 一個 Node 類時會打印 __str__ 的返回值 def GetExprValue(self): if self.item == sc.Token_Type.PLUS: self.value = self.right.value + self.left.value elif self.item == sc.Token_Type.MINUS: self.value = self.left.value - self.right.value elif self.item == sc.Token_Type.MUL: self.value = self.left.value * self.right.value elif self.item == sc.Token_Type.DIV: self.value = self.left.value / self.right.value elif self.item == sc.Token_Type.POWER: self.value = self.left.value ** self.right.value elif self.item == sc.Token_Type.FUNC: self.value = self.FuncPtr(self.center.value) return self.value
#!/usr/bin/env python # encoding: utf-8 ''' @author: 黃龍士 @license: (C) Copyright 2019-2021,China. @contact: iym070010@163.com @software: xxxxxxx @file: parserfunc.py @time: 2019/11/26 19:47 @desc: ''' import parserclass as ps import scannerclass as sc import scannerfunc as sf import sys import numpy as np class Parsef(object): def __init__(self,scanner): self.scanner = scanner ## 傳入的一個初始化後的scanner self.token = None self.Parameter,self.Origin_x,self.Origin_y,self.Scale_x,self.Scale_y,self.Rot_angle = (0,0,0,1,1,0) self.x_ptr, self.y_ptr = (None,None) self.Tvalue = 0 # len(Tvalue)==1 def enter(self,x): print('enter in '+str(x)+'\n') def back(self,x): print('exit from ' + str(x) + '\n') def call_match(self,x): print('match token ' + str(x) + '\n') def Tree_trace(self,x): self.PrintSyntaxTree(x, 1) #打印語法樹 def FetchToken(self): # 調用詞法分析器的GetToken,保存獲得結果 self.token = self.scanner.GetToken() while self.token.type == sc.Token_Type.NONTOKEN: ## 若是讀取的是空格,則再繼續讀取,因此token不多是空格 self.token = self.scanner.GetToken() if (self.token.type == sc.Token_Type.ERRTOKEN): self.SyntaxError(1) ## 若是獲得的記號是非法輸入errtoken,則指出一個詞法錯誤 ## 匹配當前記號 def MatchToken(self,The_Token): if (self.token.type != The_Token): self.SyntaxError(2) # 若失敗,則指出一個語法錯誤 if The_Token == sc.Token_Type.SEMICO: self.scanner.fp.readline() last = self.scanner.fp.tell() str = self.scanner.fp.readline() if len(str) == 0: ## 最後一行讀完,直接退出,token仍是sc.Token_Type.SEMICO return else: self.scanner.fp.seek(last) ## 不然就返回到以前的位置 self.FetchToken() # 若成功,則獲取下一個 ### //語法錯誤處理 def SyntaxError(self,x): if x == 1: print(self.token.type) self.ErrMsg(self.scanner.LineNo, " 錯誤記號 ", self.token.lexeme) elif x == 2: self.ErrMsg(self.scanner.LineNo, " 不是預期記號 ", self.token.lexeme) ## 打印錯誤信息 def ErrMsg(self,LineNo,descrip,string): print("Line No {:3d}:{:s} {:s} !\n".format(LineNo, descrip, string)) self.scanner.CloseScanner() sys.exit(1) def PrintSyntaxTree(self,root,indent): #打印語法樹 - 先序遍歷並打印表達式的語法樹 for temp in range(indent): # 縮進 print('\t',end=" ") if root.item == sc.Token_Type.PLUS: print('+ ') elif root.item == sc.Token_Type.MINUS: print('- ') elif root.item == sc.Token_Type.MUL: print('* ') elif root.item == sc.Token_Type.DIV: print('/ ') elif root.item == sc.Token_Type.POWER: print('** ') elif root.item == sc.Token_Type.FUNC: print('{} '.format(root.FuncPtr)) elif root.item == sc.Token_Type.CONST_ID: ## print('{:5f} '.format(root.value)) elif root.item == sc.Token_Type.T: print('{} '.format(root.value)) else: print("Error Tree Node !\n") sys.exit(0) if root.item == sc.Token_Type.CONST_ID or root.item == sc.Token_Type.T: # 葉子節點返回 return ## 常數和參數只有葉子節點 #常數:右值;參數:左值地址 elif root.item == sc.Token_Type.FUNC: #遞歸打印一個孩子節點 self.PrintSyntaxTree(root.center, indent + 1) # 函數有孩子節點和指向函數名的指針 else: # 遞歸打印兩個孩子節點 二元運算:左右孩子的內部節點 self.PrintSyntaxTree(root.left, indent + 1) self.PrintSyntaxTree(root.right, indent + 1) #繪圖語言解釋器入口(與主程序的外部接口) def Parser(self): #語法分析器的入口 self.enter("Parser") if (self.scanner.fp == None): #初始化詞法分析器 print("Open Source File Error !\n") else: self.FetchToken() #獲取第一個記號 self.Program() #遞歸降低分析 self.scanner.CloseScanner() #關閉詞法分析器 self.back("Parser") def Program(self): self.enter("Program") # 每句話 while (self.token.type != sc.Token_Type.SEMICO): #記號類型不是分隔符 - 若是最後一行讀完了,則記號還是分隔符;不然不會是分隔符 self.Statement() #轉到每一種文法 self.MatchToken(sc.Token_Type.SEMICO) #匹配到分隔符 self.call_match(";") self.back("Program") ##----------Statement的遞歸子程序 開始狀態 def Statement(self): ##轉到每一種文法 ## 構造的文法 self.enter("Statement") if self.token.type == sc.Token_Type.ORIGIN: self.OriginStatement() elif self.token.type == sc.Token_Type.SCALE: self.ScaleStatement() elif self.token.type == sc.Token_Type.ROT: self.RotStatement() elif self.token.type == sc.Token_Type.FOR: self.ForStatement() elif self.token.type == sc.Token_Type.CONST_ID or self.token.type == sc.Token_Type.L_BRACKET or \ self.token.type == sc.Token_Type.MINUS: self.Expression() else: self.SyntaxError(2) self.back("Statement") ##----------OriginStatement的遞歸子程序 ##eg:origin is (20, 200); def OriginStatement(self): self.enter("OriginStatement") self.MatchToken(sc.Token_Type.ORIGIN) self.call_match("ORIGIN") self.MatchToken(sc.Token_Type.IS) self.call_match("IS") self.MatchToken(sc.Token_Type.L_BRACKET) ## eg:origin is ( self.call_match("(") tmp = self.Expression() self.Origin_x = tmp.GetExprValue() # 獲取橫座標點平移距離 self.MatchToken(sc.Token_Type.COMMA) ## eg: , self.call_match(",") tmp = self.Expression() self.Origin_y = tmp.GetExprValue() #獲取縱座標的平移距離 self.MatchToken(sc.Token_Type.R_BRACKET) ##eg: ) self.call_match(")") self.back("OriginStatement") ## ----------ScaleStatement的遞歸子程序 ## eg: scale is (40, 40); def ScaleStatement(self): self.enter("ScaleStatement") self.MatchToken(sc.Token_Type.SCALE) self.call_match("SCALE") self.MatchToken(sc.Token_Type.IS) self.call_match("IS") self.MatchToken(sc.Token_Type.L_BRACKET) ## eg: scale is ( self.call_match("(") tmp = self.Expression() self.Scale_x = tmp.GetExprValue() ## 獲取橫座標的比例因子 self.MatchToken(sc.Token_Type.COMMA) ## eg:, self.call_match(",") tmp = self.Expression() self.Scale_y = tmp.GetExprValue() ## 獲取縱座標的比例因子 self.MatchToken(sc.Token_Type.R_BRACKET) ## eg:) self.call_match(")") self.back("ScaleStatement") ## ----------RotStatement的遞歸子程序 ## eg: rot is 0; def RotStatement(self): self.enter("RotStatement") self.MatchToken(sc.Token_Type.ROT) self.call_match("ROT") self.MatchToken(sc.Token_Type.IS) ## eg: rot is self.call_match("IS") tmp = self.Expression() self.Rot_angle = tmp.GetExprValue() ## 獲取旋轉角度 self.back("RotStatement") ## ----------ForStatement的遞歸子程序 ## 對右部文法符號的展開->遇到終結符號直接匹配,遇到非終結符就調用相應子程序 ## ForStatement中惟一的非終結符是Expression,他出如今5個不一樣位置,分別表明循環的起始、終止、步長、橫座標、縱座標,須要5個樹節點指針保存這5棵語法樹 ## eg:for T from 0 to 2 * pi step pi / 50 draw (t, -sin(t)) ## ExprNode *start_ptr, *end_ptr, *step_ptr, *x_ptr, *y_ptr;//指向各表達式語法樹根節點 def ForStatement(self): Start, End, Step = (0.0,0.0,0.0) ## 繪圖起點、終點、步長 self.enter("ForStatement") ## 遇到非終結符就調用相應子程序 self.MatchToken(sc.Token_Type.FOR) self.call_match("FOR") self.MatchToken(sc.Token_Type.T) self.call_match("T") self.MatchToken(sc.Token_Type.FROM) self.call_match("FROM") ## eg:for T from start_ptr = self.Expression() ## 得到參數起點表達式的語法樹 ## 'NoneType' object has no attribute 'GetExprValue' Start = start_ptr.GetExprValue() ## 計算參數起點表達式的值 self.MatchToken(sc.Token_Type.TO) self.call_match("TO") ## eg: to end_ptr = self.Expression() ## 構造參數終點表達式語法樹 End = end_ptr.GetExprValue() ## 計算參數終點表達式的值 eg: step 2 * pi self.MatchToken(sc.Token_Type.STEP) self.call_match("STEP") ## eg: step step_ptr = self.Expression() ## 構造參數步長表達式語法樹 Step = step_ptr.GetExprValue() ## 計算參數步長表達式的值 eg: pi / 50 並存起來 self.Tvalue = np.arange(Start,End,Step) self.MatchToken(sc.Token_Type.DRAW) self.call_match("DRAW") self.MatchToken(sc.Token_Type.L_BRACKET) self.call_match("(") ## eg: draw( self.x_ptr = self.Expression() ## 跟節點 eg: t 把x_ptr存起來 self.x_ptr = self.x_ptr.value # self.x_ptr = self.x_ptr.GetExprValue() ## 直接存儲二元組便可 self.MatchToken(sc.Token_Type.COMMA) self.call_match(",") ## eg:, self.y_ptr = self.Expression() ## 根節點 把x_ptr存起來 self.y_ptr = self.y_ptr.value # self.y_ptr = self.y_ptr.GetExprValue() self.MatchToken(sc.Token_Type.R_BRACKET) self.call_match(")") self.back("ForStatement") ## ----------Expression的遞歸子程序 ## 把函數設計爲語法樹節點的指針,在函數內引進2個語法樹節點的指針變量,分別做爲Expression左右操做數(Term)的語法樹節點指針 ## 表達式應該是由正負號或無符號開頭、由若干個項以加減號鏈接而成。 def Expression(self): ## 展開右部,而且構造語法樹 self.enter("Expression") left = self.Term() ## 分析左操做數且獲得其語法樹 while (self.token.type == sc.Token_Type.PLUS or self.token.type == sc.Token_Type.MINUS): token_tmp = self.token.type self.MatchToken(token_tmp) right = self.Term() ## 分析右操做數且獲得其語法樹 left = self.MakeExprNode_Operate(token_tmp, left, right) ## 構造運算的語法樹,結果爲左子樹 self.Tree_trace(left) ## 打印表達式的語法樹 self.back("Expression") return left ## 返回最終表達式的語法樹 ## ----------Term的遞歸子程序 ## 項是由若干個因子以乘除號鏈接而成 def Term(self): left = self.Factor() while (self.token.type == sc.Token_Type.MUL or self.token.type == sc.Token_Type.DIV): token_tmp = self.token.type self.MatchToken(token_tmp) right = self.Factor() left = self.MakeExprNode_Operate(token_tmp, left, right) return left ## ----------Factor的遞歸子程序 ## 因子則多是一個標識符或一個數字,或是一個以括號括起來的子表達式 def Factor(self): if self.token.type == sc.Token_Type.PLUS: ## 匹配一元加運算 self.MatchToken(sc.Token_Type.PLUS) right = self.Factor() ## 一元加:+E 轉化爲 E; left = None ## 到時候若是左孩子是None則不打印 right = self.MakeExprNode_Operate(sc.Token_Type.PLUS, left, right) elif self.token.type == sc.Token_Type.MINUS: self.MatchToken(sc.Token_Type.MINUS) right = self.Factor() left = ps.ExprNode(sc.Token_Type.CONST_ID) left.value = 0.0 right = self.MakeExprNode_Operate(sc.Token_Type.MINUS,left,right) else: right = self.Component() ## 匹配非終結符Component return right ## ----------Component的遞歸子程序 ## 冪 def Component(self): ## 右結合 left = self.Atom() if self.token.type == sc.Token_Type.POWER: ## 冪運算 self.MatchToken(sc.Token_Type.POWER) right = self.Component() ## 遞歸調用Component以實現POWER的右結合 left = self.MakeExprNode_Operate(sc.Token_Type.POWER, left, right) return left ## ----------Atom的遞歸子程序 ## 包括括號函數常數參數 def Atom(self): if self.token.type == sc.Token_Type.CONST_ID: const_value = self.token.value ## 保存當前常數值 self.MatchToken(sc.Token_Type.CONST_ID) address = self.MakeExprNode_Const(sc.Token_Type.CONST_ID, const_value) elif self.token.type == sc.Token_Type.T: self.MatchToken(sc.Token_Type.T) if len(self.Tvalue) == 1: address = self.MakeExprNode_Const(sc.Token_Type.T,0.0) ## 暫時用0替代 else: address = self.MakeExprNode_Const(sc.Token_Type.T, self.Tvalue) elif self.token.type == sc.Token_Type.FUNC: funcptr_value = self.token.funcptr ## 保存當前函數指針 self.MatchToken(sc.Token_Type.FUNC) self.MatchToken(sc.Token_Type.L_BRACKET) tmp = self.Expression() address = self.MakeExprNode_Operate(sc.Token_Type.FUNC, funcptr_value, tmp) self.MatchToken(sc.Token_Type.R_BRACKET) self.call_match(")") elif self.token.type == sc.Token_Type.L_BRACKET: self.MatchToken(sc.Token_Type.L_BRACKET) self.call_match("(") address = self.Expression() self.MatchToken(sc.Token_Type.R_BRACKET) self.call_match(")") else: self.SyntaxError(2) return address ## 根節點 ## 生成語法樹的一個節點 - 運算節點 函數節點 def MakeExprNode_Operate(self,item,left,right): ExprPtr = ps.ExprNode(item) ## 接收記號的類別 if item == sc.Token_Type.FUNC: ExprPtr.FuncPtr = left ExprPtr.center = right else: ExprPtr.left = left ExprPtr.right = right ExprPtr.GetExprValue() ## 更新下本身的value return ExprPtr ## 常數節點 變量節點 def MakeExprNode_Const(self,item,value): ExprPtr = ps.ExprNode(item) ## 接收記號的類別 ExprPtr.value = value return ExprPtr
#!/usr/bin/env python # encoding: utf-8 ''' @author: 黃龍士 @license: (C) Copyright 2019-2021,China. @contact: iym070010@163.com @software: xxxxxxx @file: parsermain.py @time: 2019/11/26 22:31 @desc: ''' import scannerfunc as sf import parserfunc as pf import semanticfunc as paint import os file_name = 'test.txt' scanner = sf.scanner(file_name) ##semantic = paint.semantic(scanner) ##semantic.initPaint() ##semantic.Parser() parser = pf.Parsef(scanner) parser.Parser() # os.system("pause")
FOR T FROM 0 TO 2*PI STEP PI/50 DRAW(COS(t),sin(t));
的測試運行結果以下: