zoukankan      html  css  js  c++  java
  • <编译原理

    <编译原理 - 函数绘图语言解释器(1)词法分析器 - python>

    背景

    • 编译原理上机实现一个对函数绘图语言的解释器 - 用除C外的不同种语言实现

    • 解释器分为三个实现块:

      • 词法分析器:用于识别一条语句中的关键词是否符合预先定义的规则。

      • 语法分析器:用来确定一条语句是否满足语法规则。

      • 解释器:用来确定满足语法规则的句子,在意思上是否符合要求。

    • 设计思路:

      1. 设计记号:词法分析器读取一个序列并根据构词规则把序列转化为记号流

      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:Users62473Desktop\'+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 == '
    ':
                    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 != '
    ' 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 != '
    ' 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('       记号类别              字符串     常数值  函数指针
    ')
        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)进行的测试运行结果如下:

  • 相关阅读:
    BZOJ 1192 鬼谷子的钱袋
    浅谈双连通分量、强连通分量
    BZOJ 1047 理想的正方形
    浅谈单调队列、单调栈
    django ORM之manytomany跨表
    django DateTimeField和DateField和TimeField
    django 一对多的添加记录create 和save 方法,update 更新和save()方法的区别,查询API的方法
    Python生成随机验证码
    obj.t2c.all-------django自动生成第三张多对多关系表中另一张表信息在前端的显示
    Form组件验证之ajax提交数据并显示错误信息
  • 原文地址:https://www.cnblogs.com/ymjun/p/11773681.html
Copyright © 2011-2022 走看看