<編譯原理 - 函數繪圖語言解釋器(1)詞法分析器 - python>

<編譯原理 - 函數繪圖語言解釋器(1)詞法分析器 - python>

背景

  • 編譯原理上機實現一個對函數繪圖語言的解釋器 - 用除C外的不一樣種語言實現git

  • 解釋器分爲三個實現塊:數組

    • 詞法分析器:用於識別一條語句中的關鍵詞是否符合預先定義的規則。緩存

    • 語法分析器:用來肯定一條語句是否知足語法規則。函數

    • 解釋器:用來肯定知足語法規則的句子,在乎思上是否符合要求。測試

  • 設計思路:優化

    1. 設計記號:詞法分析器讀取一個序列並根據構詞規則把序列轉化爲記號流spa

    2. 定義一個字典:把全部符合一個模式的保留字、常量名、參數名、函數名等放進字典。字典是個數組,其元素的類型和記號的類型相同設計

    3. 設計程序的結構,具體見下面的代碼

  • 用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

相關文章
相關標籤/搜索