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

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

    背景

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

    • 设计思路:

      1. 设计函数绘图语言的文法,使其适合递归下降分析;

      2. 设计语法树的结构,用于存放表达式的语法树;

      3. 设计递归下降子程序,分析句子并构造表达式的语法树;

      4. 设计测试程序和测试用例,检验分析器是否正确。

    • 消除无二义/无左递归完整的EBNF文法:

    • 表达式的语法树:

    • 用Pycharm写了三个.py文件:
      • parserclass.py

      • parserfunc.py

      • parsermain.py

      • 输入流是词法分析器得到的记号流,输出流是语法树

      • 测试文本序列(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 :parserclass.py - 构造语法树节点类

    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
    
    

    Step 2 :parserfunc.py - 构造语法分析器类

    #!/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)+'
    ')
        def back(self,x):
            print('exit from ' + str(x) + '
    ')
        def call_match(self,x):
            print('match token ' + str(x) + '
    ')
        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} !
    ".format(LineNo, descrip, string))
            self.scanner.CloseScanner()
            sys.exit(1)
    
        def PrintSyntaxTree(self,root,indent):      #打印语法树 - 先序遍历并打印表达式的语法树
            for temp in range(indent):  # 缩进
                print('	',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 !
    ")
                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 !
    ")
            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
    
    

    Step 3 :parsermain.py - 完成I/O流

    #!/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")
    
    

    实现结果

    • 对于测试文本(1)FOR T FROM 0 TO 2*PI STEP PI/50 DRAW(COS(t),sin(t));的测试运行结果如下:

    • 换一组测试文本(2)进行的测试运行结果如下:

  • 相关阅读:
    VNC远程控制软件是什么?有没有更好的远程桌面控制解决方案?
    目前国内最热门的四款远程桌面控制软件
    深入Redis命令的执行过程
    深入Redis客户端(redis客户端属性、redis缓冲区、关闭redis客户端)
    Springboot拦截器的使用
    Springboot使用Undertow
    Jenkins parallel并行构建
    Jenkins使用docker构建
    Redis 的键命令、HyperLogLog 命令、脚本命令、连接命令、服务器命令
    Redis Set和ZSet常用命令
  • 原文地址:https://www.cnblogs.com/ymjun/p/11963230.html
Copyright © 2011-2022 走看看