编译原理上机——函数绘图语言(五):编译器与解释器

相关篇目

编译原理上机——函数绘图语言(一)
编译原理上机——函数绘图语言(二):词法分析器
编译原理上机——函数绘图语言(三):语法分析器
编译原理上机——函数绘图语言(四):语义分析器
完整代码
Gitee开源代码

说明

这是编译原理上机的最后一部分代码了,激动不?兴奋不?
最后一篇代码是基于编译原理上机——函数绘图语言(四):语义分析器的改写,将其改为方便于编译器与解释器读取的代码.(大同小异,大同小异)

文件罗列


test.py测试文件(这个可以自己改)
createtable.py创建符号表
scanner.py扫描器
lexer.py词法分析器
以上文件与编译原理上机——函数绘图语言(四):语义分析器中同名文件相同


myparser.py语法分析器+语义分析器+中间代码生成器+中间代码优化器
mycompile.py编译器
interpreter.py解释器
main.py主函数
output.py目标代码(解释器没有该文件)


语法分析器

文件名为myparser.py

# -*- coding: utf-8 -*-
"""
Created on Wed Dec 2 19:36:32 2020

@author: FishPotatoChen

Copyright (c) 2020 FishPotatoChen All rights reserved.
"""

# 语法分析器

import ast
import math
import os

import astunparse

import scanner


class Parser:
    def __init__(self, path):
        # 中间代码生成
        self.state_of_origin = [0, 0]  # 原点默认值
        self.state_of_scale = [1, 1]  # 图像放大默认值
        self.state_of_rot = 0  # 图像旋转默认值
        self.state_of_for = [0, 0, 0]  # 绘图的状态参数
        self.draw = ['', '']  # 画图
        self.MidCode = []
        # 中间代码生成结束
        self.scanner = scanner.Scanner(path)
        self.dataflow = self.scanner.analyze()
        self.pos = 0  # 数据读取位置
        self.length = len(self.dataflow)  # 数据流长度

    def analyze(self):
        while(self.pos < self.length):
            # 匹配保留字
            # S->'ORIGIN'T|'SCALE'T|'ROT'T|'FOR'P
            # T->'IS('E','E')'|'IS'E
            # E为一个算数表达式
            # P为FOR后特殊结构
            if self.dataflow[self.pos][1]['TYPE'] == 'KEYWORD':
                self.output(self.dataflow[self.pos][0])
        # 转换为列表
        return [eval(x) for x in self.MidCode]

    def output(self, string):
        # 输出
        # 因为输出语句结构相同
        # 所以放在同一个函数中
        # 实际上,语法分析到得到语法树便结束了
        self.pos += 1
        if string == 'ORIGIN':
            self.ORIGIN()
        elif string == 'SCALE':
            self.SCALE()
        elif string == 'ROT':
            self.ROT()
        elif string == 'FOR':
            self.FOR()
            # 因为Python深复制与浅复制的问题
            # 我直接使用了字符串
            # 输出的时候转换为列表
            # 可以将中间代码输出至文件
            # 增加6+2图的上下层隔离性
            self.MidCode.append(str((self.state_of_origin, self.state_of_scale,
                                     self.state_of_rot, self.state_of_for, self.draw)))
        else:
            raise SyntaxError()

    def ORIGIN(self):
        self.matchstring('IS')
        templist = self.matchparameter()
        # 更改平移状态
        self.state_of_origin[0] = eval(templist[0])
        self.state_of_origin[1] = eval(templist[1])

    def SCALE(self):
        self.matchstring('IS')
        templist = self.matchparameter()
        # 更改缩放状态
        self.state_of_scale[0] = eval(templist[0])
        self.state_of_scale[1] = eval(templist[1])

    def ROT(self):
        self.matchstring('IS')
        # 更改旋转状态
        self.state_of_rot = eval(self.matchexpression())

    def FOR(self):
        self.matchstring('T')
        self.matchstring('FROM')
        # 更改FOR状态
        self.state_of_for[0] = eval(self.matchexpression())
        self.matchstring('TO')
        self.state_of_for[1] = eval(self.matchexpression())
        self.matchstring('STEP')
        self.state_of_for[2] = eval(self.matchexpression())
        self.matchstring('DRAW')
        templist = self.matchparameter()
        # 更改画图状态
        self.draw[0] = templist[0]
        self.draw[1] = templist[1]

    def matchstring(self, string):
        # 匹配一个特定的字符串
        if self.dataflow[self.pos][0] == string:
            self.pos += 1
        else:
            raise SyntaxError()

    # matchparameter与matchexpression的区别
    # 前者匹配双表达式
    # 或者既可以匹配双表达式又可以匹配单表达式
    # 分开原因:
    # 考虑到ROT后面是单表达式而ORIGIN与SCALE后面都是算表达式
    # 并且FOR后面既有单表达式又有双表达式
    # 所以特此区分
    def matchparameter(self):
        # 匹配(E,E)
        # 即匹配参数列表
        # 如:
        # ORIGIN IS (5,5);
        # 后面的括号中包括括号的部分都是参数列表
        temp = self.matchexpression()  # 缓冲区
        # 转换为列表[E,E]
        if temp[0] == '(' and temp[-1] == ')':
            temp = temp[1:-1].split(',')
        else:
            raise SyntaxError()
        return temp

    def matchexpression(self):
        # 匹配E或者(E,E)
        # 即匹配一个算数表达式
        # 如
        # 5*2-LN(3)
        # (5*2-3,tan(0.1))
        temp = ''  # 缓冲区
        while(self.dataflow[self.pos][0] != ';' and self.pos < self.length and self.dataflow[self.pos][1]['TYPE'] != 'KEYWORD'):
            if self.dataflow[self.pos][1]['TYPE'] == 'FUNC':
                temp += self.dataflow[self.pos][1]['FUNCTION']
            elif self.dataflow[self.pos][1]['TYPE'] == 'CONST_ID':
                temp += self.dataflow[self.pos][1]['VALUE']
            else:
                temp += self.dataflow[self.pos][0]
            self.pos += 1
        if self.dataflow[self.pos][0] == ';':
            self.pos += 1  # 跳过结尾的分号
        return temp

编译器

编译器主体

文件名为mycompile.py

# -*- coding: utf-8 -*-
"""
Created on Wed Dec 4 11:12:17 2020

@author: FishPotatoChen

Copyright (c) 2020 FishPotatoChen All rights reserved.
"""

# 编译器

import myparser


class Compile():
    def __init__(self, input_path, output_path):
        self.path = output_path
        self.MidCode = myparser.Parser(input_path).analyze()
        with open(self.path, 'w', encoding='utf-8') as f:
            # 增加头文件,即turtle库
            f.write(
'''# @author: FishPotatoChen
# Copyright (c) 2020 FishPotatoChen All rights reserved.

import math
import turtle

import numpy as np

turtle.setup(width=1.0,height=1.0)
turtle.penup()
'''
            )

    def create(self):
        with open(self.path, 'a') as f:
            #操作每一次画图
            for i in self.MidCode:
                f.write(
'''
for T in np.arange('''+str(i[3][0])+''', '''+str(i[3][1])+''', '''+str(i[3][2])+'''):
    x = '''+str(i[4][0])+'''*'''+str(i[1][0])+'''
    y = '''+str(i[4][1])+'''*'''+str(i[1][1])+'''
    x_rot = x*math.cos('''+str(i[2])+''')+y*math.sin('''+str(i[2])+''')+'''+str(i[0][0])+'''
    y_rot = y*math.cos('''+str(i[2])+''')-x*math.sin('''+str(i[2])+''')+'''+str(i[0][1])+'''
    turtle.goto(x_rot,y_rot)
    turtle.dot()
'''
                )
            f.write('\nturtle.done()')

主函数

文件名为main.py

# -*- coding: utf-8 -*-
"""
Created on Wed Dec 4 11:12:45 2020

@author: FishPotatoChen

Copyright (c) 2020 FishPotatoChen All rights reserved.
"""

import os

import mycompile

if __name__ == '__main__':
    path = os.getcwd()
    mycompile.Compile(path+r'\test.txt', path+r'\output.py').create()

解释器

解释器主体

文件名为interpreter.py

# -*- coding: utf-8 -*-
"""
Created on Wed Dec 5 8:37:29 2020

@author: FishPotatoChen

Copyright (c) 2020 FishPotatoChen All rights reserved.
"""

# 解释器

import myparser
import math
import turtle
import numpy as np


class Interpreter():
    def __init__(self, path):
        self.MidCode = myparser.Parser(path).analyze()
        # 初始化画布
        turtle.setup(width=1.0, height=1.0)
        turtle.penup()

    def create(self):
        # 解析中间代码
        for i in self.MidCode:
            # 操作每一次画图
            for T in np.arange(i[3][0], i[3][1], i[3][2]):
                x = eval(i[4][0])*i[1][0]
                y = eval(i[4][1])*i[1][1]
                x_rot = x * math.cos(i[2])+y*math.sin(i[2])+i[0][0]
                y_rot = y * math.cos(i[2])-x*math.sin(i[2])+i[0][1]
                turtle.goto(x_rot, y_rot)
                turtle.dot()
        turtle.done()

主函数

文件名为main.py

# -*- coding: utf-8 -*-
"""
Created on Wed Dec 4 11:12:45 2020

@author: FishPotatoChen

Copyright (c) 2020 FishPotatoChen All rights reserved.
"""

import os

import interpreter

if __name__ == '__main__':
    path = os.getcwd()
    interpreter.Interpreter(path+r'\test.txt').create()

测试

测试文件

文件名为test.txt

scale is (100,100);
for t from 0 to 2*pi step pi/50 draw (cos(t),sin(t));
scale is (100/2,100);
origin is (100,0);
rot is pi/2;
for t from 0 to 2*pi step pi/50 draw (cos(t),sin(t));

目标代码

文件名为output.py注意这是运行编译器的main.py后得到的文件,对于编译器,还需运行目标代码才能得到绘图结果。这就像C/C++编译完成后生成的.exe文件,想要得到C/C++的结果还需运行.exe文件。

# @author: FishPotatoChen
# Copyright (c) 2020 FishPotatoChen All rights reserved.

import math
import turtle

import numpy as np

turtle.setup(width=1.0,height=1.0)
turtle.penup()

for T in np.arange(0, 6.283185307179586, 0.06283185307179587):
    x = math.cos(T)*100
    y = math.sin(T)*100
    x_rot = x*math.cos(0)+y*math.sin(0)+0
    y_rot = y*math.cos(0)-x*math.sin(0)+0
    turtle.goto(x_rot,y_rot)
    turtle.dot()

for T in np.arange(0, 6.283185307179586, 0.06283185307179587):
    x = math.cos(T)*50.0
    y = math.sin(T)*100
    x_rot = x*math.cos(1.5707963267948966)+y*math.sin(1.5707963267948966)+100
    y_rot = y*math.cos(1.5707963267948966)-x*math.sin(1.5707963267948966)+0
    turtle.goto(x_rot,y_rot)
    turtle.dot()

turtle.done()

绘图结果

绘制结果

说明

绘制的初始原点为全图最中心,因为我觉得在左上角对用户不是很友好。
绘图流程:

  1. 修改比例;
  2. 旋转图形;
  3. 平移图形。

附录

为了方便起见,我在这里将所有的代码总结在这

支持库

mathPython数学函数支持库
re匹配正则表达式,也就是匹配正规式
os系统调用,在此主要用来获得当前路径
turtlePython海龟库,用于画图
numpyPython科学计算库,在此主要用于解决range不支持小数问题
使用前请务必安装支持库

符号表

文件名为createtable.py

# -*- coding: utf-8 -*-
"""
Created on Mon Nov 23 20:05:45 2020

@author: FishPotatoChen

Copyright (c) 2020 FishPotatoChen All rights reserved.
"""

import math

TOKEN = {
    # 常量
    'PI': {'TYPE': 'CONST_ID', 'VALUE': 'math.pi', 'FUNCTION': None},
    'E': {'TYPE': 'CONST_ID', 'VALUE': 'math.e', 'FUNCTION': None},
    # 变量
    'T': {'TYPE': 'SYMBOL', 'VALUE': None, 'FUNCTION': None},
    # 函数
    'SIN': {'TYPE': 'FUNC', 'VALUE': None, 'FUNCTION': 'math.sin'},
    'COS': {'TYPE': 'FUNC', 'VALUE': None, 'FUNCTION': 'math.cos'},
    'TAN': {'TYPE': 'FUNC', 'VALUE': None, 'FUNCTION': 'math.tan'},
    'LN': {'TYPE': 'FUNC', 'VALUE': None, 'FUNCTION': 'math.log'},
    'EXP': {'TYPE': 'FUNC', 'VALUE': None, 'FUNCTION': 'math.exp'},
    'SQRT': {'TYPE': 'FUNC', 'VALUE': None, 'FUNCTION': 'math.sqrt'},
    # 保留字
    'ORIGIN': {'TYPE': 'KEYWORD', 'VALUE': None, 'FUNCTION': None},
    'SCALE': {'TYPE': 'KEYWORD', 'VALUE': None, 'FUNCTION': None},
    'ROT': {'TYPE': 'KEYWORD', 'VALUE': None, 'FUNCTION': None},
    'IS': {'TYPE': 'KEYWORD', 'VALUE': None, 'FUNCTION': None},
    'FOR': {'TYPE': 'KEYWORD', 'VALUE': None, 'FUNCTION': None},
    'FROM': {'TYPE': 'KEYWORD', 'VALUE': None, 'FUNCTION': None},
    'TO': {'TYPE': 'KEYWORD', 'VALUE': None, 'FUNCTION': None},
    'STEP': {'TYPE': 'KEYWORD', 'VALUE': None, 'FUNCTION': None},
    'DRAW': {'TYPE': 'KEYWORD', 'VALUE': None, 'FUNCTION': None},
    # 运算符
    '+': {'TYPE': 'OP', 'VALUE': None, 'FUNCTION': None},
    '-': {'TYPE': 'OP', 'VALUE': None, 'FUNCTION': None},
    '*': {'TYPE': 'OP', 'VALUE': None, 'FUNCTION': None},
    '/': {'TYPE': 'OP', 'VALUE': None, 'FUNCTION': None},
    '**': {'TYPE': 'OP', 'VALUE': None, 'FUNCTION': None},
    # 记号
    '(': {'TYPE': 'MARK', 'VALUE': None, 'FUNCTION': None},
    ')': {'TYPE': 'MARK', 'VALUE': None, 'FUNCTION': None},
    ',': {'TYPE': 'MARK', 'VALUE': None, 'FUNCTION': None},
    # 结束符
    ';': {'TYPE': 'END', 'VALUE': None, 'FUNCTION': None},
    # 空
    '': {'TYPE': 'EMPTY', 'VALUE': None, 'FUNCTION': None},
    # 数字
    '0': {'TYPE': 'NUMBER', 'VALUE': 0.0, 'FUNCTION': None},
    '1': {'TYPE': 'NUMBER', 'VALUE': 1.0, 'FUNCTION': None},
    '2': {'TYPE': 'NUMBER', 'VALUE': 2.0, 'FUNCTION': None},
    '3': {'TYPE': 'NUMBER', 'VALUE': 3.0, 'FUNCTION': None},
    '4': {'TYPE': 'NUMBER', 'VALUE': 4.0, 'FUNCTION': None},
    '5': {'TYPE': 'NUMBER', 'VALUE': 5.0, 'FUNCTION': None},
    '6': {'TYPE': 'NUMBER', 'VALUE': 6.0, 'FUNCTION': None},
    '7': {'TYPE': 'NUMBER', 'VALUE': 7.0, 'FUNCTION': None},
    '8': {'TYPE': 'NUMBER', 'VALUE': 8.0, 'FUNCTION': None},
    '9': {'TYPE': 'NUMBER', 'VALUE': 9.0, 'FUNCTION': None},
    '.': {'TYPE': 'NUMBER', 'VALUE': None, 'FUNCTION': None},
}

扫描器

文件名为scanner.py,输入为未处理的文件,输出为去掉注释之后经过简单处理的文件

# -*- coding: utf-8 -*-
"""
Created on Mon Nov 23 20:05:45 2020

@author: FishPotatoChen

Copyright (c) 2020 FishPotatoChen All rights reserved.
"""

# 输入处理器
import re

import lexer


class Scanner():
    def __init__(self, path):
        # 读入文件位置
        self.path = path
        # 设置缓冲区
        self.text = ""
        with open(self.path, "r") as f:
            lines = f.readlines()
            for line in lines:
                # 将文件中注释去掉
                self.text = self.text + \
                    line.split("//")[0].split("--")[0].split("\n")[0]
        self.text = self.text.upper().strip()
        self.lexer = lexer.Lexer()
        self.output_lists = []

    def analyze(self):
        sentences = re.split("(;)", self.text)
        # No.0
        # 识别
        # E->E;|ε
        # 用于记录状态机状态,当state == True时,意味着可以读入一个E,当state == False时,意味着可以读入一个;
        state = True
        for sentence in sentences:
            if state and sentence != ";":
                state = False
                self.lexer.getToken(sentence)
                self.output_lists.extend(self.lexer.output_list)
            elif sentence == ";":
                state = True
            else:
                raise SyntaxError()
        if state:
            raise SyntaxError()
        # No.0   识别结束
        return self.output_lists

词法分析器

文件名为lexer.py,输入为经过简单处理的文件,输出为记号流

# -*- coding: utf-8 -*-
"""
Created on Mon Nov 23 20:05:45 2020

@author: FishPotatoChen

Copyright (c) 2020 FishPotatoChen All rights reserved.
"""

# 词法分析器

import math
import re

import createtable


class Lexer:
    def __init__(self):
        # 从文件中读取记号表
        self.TOKEN = createtable.TOKEN

    def getToken(self, sentence):
        self.output_list = []
        if sentence:
            tokens = sentence.split()
            for token in tokens:
                try:
                    # No.0
                    # 首先识别直接可以识别的记号
                    # 正规式为ORIGIN|SCALE|ROT|IS|FOR|FROM|TO|STEP|DRAW|ε
                    self.output_token(token)
                    # No.0  识别结束
                except:
                    # 找不到就进入更高级的DFA中识别
                    self.argument_lexer(token)
            self.output_token(';')

    # 构造更为复杂、高级的、多种类的识别表达式
    def argument_lexer(self, argument):
        # 扫描位置
        i = 0
        # 字符串长度
        length = len(argument)
        while(i < length):
            # 临时字符串,即缓冲区
            temp = ''
            if argument[i] in ['P', 'S', 'C', 'L', 'E', 'T', '*']:
                # No.1
                # 识别"*"还是"**"的过程是一个上下文有关文法
                if argument[i] == '*':
                    i += 1
                    if i >= length:
                        self.output_token(argument[i])
                        break
                    elif argument[i] == '*':
                        self.output_token('**')
                    else:
                        i -= 1
                        self.output_token(argument[i])
                # No.1  识别结束
                else:
                    # No.2
                    # DFA判断全为字母的字符串
                    # 正规式为PI|E|T|SIN|COS|TAN|LOG|EXP|SQRT
                    temp = re.findall(r"[A-Z]+", argument[i:])[0]
                    # 看该串是否接受
                    self.output_token(temp)
                    i += len(temp)-1
                    if i >= length:
                        break
                    # No.2  识别结束
            elif argument[i] in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.']:
                # No.3
                # 识别数字
                if argument[i] == '.':
                    # 识别开头为"."的数字
                    # 正规式为.[0-9]+
                    # 如:.52=>0.52
                    i += 1
                    temp = re.findall(r"\d+", argument[i:])[0]
                    i += len(temp)-1
                    temp = '0.' + temp
                    self.output_token(temp, False)
                else:
                    # 识别一般数字
                    # 正规式为[0-9]+.?[0-9]*
                    # 如:5.52=>5.52;12=>12
                    temp = re.findall(r"\d+\.?\d*", argument[i:])[0]
                    i += len(temp)-1
                    self.output_token(temp, False)
                if i >= length:
                    break
                # No.3   识别结束
            else:
                # No.4
                # 识别其他字符
                # 正规式为+|-|/|(|)|,|ε
                self.output_token(argument[i])
            i += 1

    # 输出函数
    # 因为解释器可以不用输出至屏幕,但是题目要求输出至屏幕
    # 所以特别写了一个函数用于输出,便于接下来的语法分析器调用文法分析器
    # 当输出不为屏幕时,将输出嵌入代码会变得不好更改
    # 如果写成独立函数,直接更改输出函数即可
    def output_token(self, token, NotNumber=True):
        if NotNumber:
            self.output_list.append([token, self.TOKEN[token]])
        else:
            tempdic = {token: {'TYPE': 'NUMBER',
                               'VALUE': float(token), 'FUNCTION': None}}
            self.output_list.append([token, tempdic[token]])

语法分析器+语义分析器+中间代码优化+中间代码生成

文件名为myparser.py,输入为记号流,输出为中间代码

# -*- coding: utf-8 -*-
"""
Created on Wed Dec 2 19:36:32 2020

@author: FishPotatoChen

Copyright (c) 2020 FishPotatoChen All rights reserved.
"""

# 语法分析器

import math
import os

import scanner


class Parser:
    def __init__(self, path):
        # 中间代码生成
        self.state_of_origin = [0, 0]  # 原点默认值
        self.state_of_scale = [1, 1]  # 图像放大默认值
        self.state_of_rot = 0  # 图像旋转默认值
        self.state_of_for = [0, 0, 0]  # 绘图的状态参数
        self.draw = ['', '']  # 画图
        self.MidCode = []
        # 中间代码生成结束
        self.scanner = scanner.Scanner(path)
        self.dataflow = self.scanner.analyze()
        self.pos = 0  # 数据读取位置
        self.length = len(self.dataflow)  # 数据流长度

    def analyze(self):
        while(self.pos < self.length):
            # 匹配保留字
            # S->'ORIGIN'T|'SCALE'T|'ROT'T|'FOR'P
            # T->'IS('E','E')'|'IS'E
            # E为一个算数表达式
            # P为FOR后特殊结构
            if self.dataflow[self.pos][1]['TYPE'] == 'KEYWORD':
                self.output(self.dataflow[self.pos][0])
        # 转换为列表
        return [eval(x) for x in self.MidCode]

    def output(self, string):
        # 输出
        # 因为输出语句结构相同
        # 所以放在同一个函数中
        # 实际上,语法分析到得到语法树便结束了
        self.pos += 1
        if string == 'ORIGIN':
            self.ORIGIN()
        elif string == 'SCALE':
            self.SCALE()
        elif string == 'ROT':
            self.ROT()
        elif string == 'FOR':
            self.FOR()
            # 因为Python深复制与浅复制的问题
            # 我直接使用了字符串
            # 输出的时候转换为列表
            # 可以将中间代码输出至文件
            # 增加6+2图的上下层隔离性
            self.MidCode.append(str((self.state_of_origin, self.state_of_scale,
                                     self.state_of_rot, self.state_of_for, self.draw)))
        else:
            raise SyntaxError()

    def ORIGIN(self):
        self.matchstring('IS')
        templist = self.matchparameter()
        # 更改平移状态
        self.state_of_origin[0] = eval(templist[0])
        self.state_of_origin[1] = eval(templist[1])

    def SCALE(self):
        self.matchstring('IS')
        templist = self.matchparameter()
        # 更改缩放状态
        self.state_of_scale[0] = eval(templist[0])
        self.state_of_scale[1] = eval(templist[1])

    def ROT(self):
        self.matchstring('IS')
        # 更改旋转状态
        self.state_of_rot = eval(self.matchexpression())

    def FOR(self):
        self.matchstring('T')
        self.matchstring('FROM')
        # 更改FOR状态
        self.state_of_for[0] = eval(self.matchexpression())
        self.matchstring('TO')
        self.state_of_for[1] = eval(self.matchexpression())
        self.matchstring('STEP')
        self.state_of_for[2] = eval(self.matchexpression())
        self.matchstring('DRAW')
        templist = self.matchparameter()
        # 更改画图状态
        self.draw[0] = templist[0]
        self.draw[1] = templist[1]

    def matchstring(self, string):
        # 匹配一个特定的字符串
        if self.dataflow[self.pos][0] == string:
            self.pos += 1
        else:
            raise SyntaxError()

    # matchparameter与matchexpression的区别
    # 前者匹配双表达式
    # 或者既可以匹配双表达式又可以匹配单表达式
    # 分开原因:
    # 考虑到ROT后面是单表达式而ORIGIN与SCALE后面都是算表达式
    # 并且FOR后面既有单表达式又有双表达式
    # 所以特此区分
    def matchparameter(self):
        # 匹配(E,E)
        # 即匹配参数列表
        # 如:
        # ORIGIN IS (5,5);
        # 后面的括号中包括括号的部分都是参数列表
        temp = self.matchexpression()  # 缓冲区
        # 转换为列表[E,E]
        if temp[0] == '(' and temp[-1] == ')':
            temp = temp[1:-1].split(',')
        else:
            raise SyntaxError()
        return temp

    def matchexpression(self):
        # 匹配E或者(E,E)
        # 即匹配一个算数表达式
        # 如
        # 5*2-LN(3)
        # (5*2-3,tan(0.1))
        temp = ''  # 缓冲区
        while(self.dataflow[self.pos][0] != ';' and self.pos < self.length and self.dataflow[self.pos][1]['TYPE'] != 'KEYWORD'):
            if self.dataflow[self.pos][1]['TYPE'] == 'FUNC':
                temp += self.dataflow[self.pos][1]['FUNCTION']
            elif self.dataflow[self.pos][1]['TYPE'] == 'CONST_ID':
                temp += self.dataflow[self.pos][1]['VALUE']
            else:
                temp += self.dataflow[self.pos][0]
            self.pos += 1
        if self.dataflow[self.pos][0] == ';':
            self.pos += 1  # 跳过结尾的分号
        return temp

中间代码之后

解释器

所需的文件

test.py测试文件(这个可以自己改)
createtable.py创建符号表
scanner.py扫描器
lexer.py词法分析器
myparser.py语法分析器+语义分析器+中间代码生成器+中间代码优化器
interpreter.py解释器
main.py主函数

主函数

文件名为main.py

# -*- coding: utf-8 -*-
"""
Created on Wed Dec 4 11:12:45 2020

@author: FishPotatoChen

Copyright (c) 2020 FishPotatoChen All rights reserved.
"""

import os

import interpreter

if __name__ == '__main__':
    path = os.getcwd()
    interpreter.Interpreter(path+r'\test.txt').create()

解释器主体

文件名为interpreter.py,输入为中间代码,输出为绘制的图像

# -*- coding: utf-8 -*-
"""
Created on Wed Dec 5 8:37:29 2020

@author: FishPotatoChen

Copyright (c) 2020 FishPotatoChen All rights reserved.
"""

# 解释器

import myparser
import math
import turtle
import numpy as np


class Interpreter():
    def __init__(self, path):
        self.MidCode = myparser.Parser(path).analyze()
        # 初始化画布
        turtle.setup(width=1.0, height=1.0)
        turtle.penup()

    def create(self):
        # 解析中间代码
        for i in self.MidCode:
            # 操作每一次画图
            for T in np.arange(i[3][0], i[3][1], i[3][2]):
                x = eval(i[4][0])*i[1][0]
                y = eval(i[4][1])*i[1][1]
                x_rot = x * math.cos(i[2])+y*math.sin(i[2])+i[0][0]
                y_rot = y * math.cos(i[2])-x*math.sin(i[2])+i[0][1]
                turtle.goto(x_rot, y_rot)
                turtle.dot()
        turtle.done()

编译器

所需的文件

test.py测试文件(这个可以自己改)
createtable.py创建符号表
scanner.py扫描器
lexer.py词法分析器
myparser.py语法分析器+语义分析器+中间代码生成器+中间代码优化器
mycompile.py编译器
main.py主函数
output.py目标代码(运行编译器的主函数后自动生成)

编译器

文件名为mycompile.py,输入为中间代码,输出为目标代码output.py

# -*- coding: utf-8 -*-
"""
Created on Wed Dec 4 11:12:17 2020

@author: FishPotatoChen

Copyright (c) 2020 FishPotatoChen All rights reserved.
"""

# 编译器

import myparser


class Compile():
    def __init__(self, input_path, output_path):
        self.path = output_path
        self.MidCode = myparser.Parser(input_path).analyze()
        with open(self.path, 'w', encoding='utf-8') as f:
            # 增加头文件,即turtle库
            f.write(
                '''# @author: FishPotatoChen
# Copyright (c) 2020 FishPotatoChen All rights reserved.

import math
import turtle

import numpy as np

# 初始化画布
turtle.setup(width=1.0,height=1.0)
turtle.penup()
'''
            )

    def create(self):
        with open(self.path, 'a', encoding='utf-8') as f:
            # 操作每一次画图
            for i in self.MidCode:
                f.write(
                    '''
# 绘图
for T in np.arange('''+str(i[3][0])+''', '''+str(i[3][1])+''', '''+str(i[3][2])+'''):
    x = '''+str(i[4][0])+'''*'''+str(i[1][0])+'''
    y = '''+str(i[4][1])+'''*'''+str(i[1][1])+'''
    x_rot = x*math.cos('''+str(i[2])+''')+y*math.sin('''+str(i[2])+''')+'''+str(i[0][0])+'''
    y_rot = y*math.cos('''+str(i[2])+''')-x*math.sin('''+str(i[2])+''')+'''+str(i[0][1])+'''
    turtle.goto(x_rot,y_rot)
    turtle.dot()
'''
                )
            f.write('\nturtle.done()')
编译器使用方法

运行主函数后生成目标代码output.py,运行目标代码得到绘制的图形

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FishPotatoChen

谢谢您的支持,我会更努力的~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值