我的编程空间,编程开发者的网络收藏夹
学习永远不晚

利用 Python 开发一个 Python 解释器

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

利用 Python 开发一个 Python 解释器

前言:

计算机只能理解机器码。归根结底,编程语言只是一串文字,目的是为了让人类更容易编写他们想让计算机做的事情。真正的魔法是由编译器和解释器完成,它们弥合了两者之间的差距。解释器逐行读取代码并将其转换为机器码。

在本文中,我们将设计一个可以执行算术运算的解释器。

我们不会重新造轮子。文章将使用由 David M. Beazley 开发的词法解析器 ——PLY(Python Lex-Yacc(https://github.com/dabeaz/ply))

PLY 可以通过以下方式下载:

$ pip install ply 

我们将粗略地浏览一下创建解释器所需的基础知识。欲了解更多,请参阅这个 GitHub 仓库(https://github.com/dabeaz/ply)

1.标记(Token)

标记是为解释器提供有意义信息的最小字符单位。标记包含一对名称和属性值。

让我们从创建标记名称列表开始。这是一个必要的步骤。

tokens = (  
    # 数据类型  
    "NUM",  
    "FLOAT",  
    # 算术运算  
    "PLUS",  
    "MINUS",  
    "MUL",  
    "DIV",  
    # 括号  
    "LPAREN",  
    "RPAREN",  
) 

2.词法分析器(Lexer)

将语句转换为标记的过程称为标记化或词法分析。执行词法分析的程序是词法分析器。

# 标记的正则表达  
t_PLUS   = r"\+"  
t_MINUS  = r"\-"  
t_MUL    = r"\*"  
t_DIV    = r"/"  
t_LPAREN = r"\("  
t_RPAREN = r"\)"  
t_POW    = r"\^"  
# 忽略空格和制表符  
t_ignore = " \t"  
# 为每个规则添加动作  
def t_FLOAT(t):  
    r"""\d+\.\d+"""  
    t.value = float(t.value)  
    return t  
def t_NUM(t):  
    r"""\d+"""  
    t.value = int(t.value)  
    return t  
# 未定义规则字符的错误处理  
def t_error(t):  
    # 此处的 t.value 包含未标记的其余输入  
    print(f"keyword not found: {t.value[0]}\nline {t.lineno}")  
    t.lexer.skip(1)  
# 如果遇到 \n 则将其设为新的一行  
def t_newline(t):  
    r"""\n+"""  
    t.lexer.lineno += t.value.count("\n") 

为导入词法分析器,我们将使用:

import ply.lex as lex

t_ 是一个特殊的前缀,表示定义标记的规则。每条词法规则都是用正则表达式制作的,与 Python 中的 re 模块兼容。正则表达式能够根据规则扫描输入并搜索符合的符号串。正则表达式定义的文法称为正则文法。正则文法定义的语言则称为正则语言。

定义好了规则,我们将构建词法分析器:

data = 'a = 2 +(10 -8)/1.0'  
lexlexer = lex.lex()  
lexer.input(data)  
while tok := lexer.token():  
    print(tok) 

为了传递输入字符串,我们使用lexer.input(data)lexer.token() 将返回下一个 LexToken 实例,最后返回 None。根据上述规则,代码 2 + ( 10 -8)/1.0 的标记将是:

紫色字符代表的是标记的名称,其后是标记的具体内容。

3.巴科斯-诺尔范式(Backus-Naur Form,BNF)

大多数编程语言都可以用上下文无关文法来编写。它比常规语言更复杂。对于上下文无关文法,我们用上下文无关语法,它是描述语言中所有可能语法的规则集。BNF 是一种定义语法的方式,它描述了编程语言的语法。

让我们看看例子:

symbol : alternative1 | alternative2 …

根据产生式,: 的左侧被替换为右侧的其中一个值替换。右侧的值由 | 分隔(可理解为 symbol 定义为 alternative1 alternative2或…… 等等)。

对于我们的这个算术解释器,语法规格如下:

expression : expression '+' expression  
           | expression '-' expression  
           | expression '/' expression  
           | expression '*' expression  
           | expression '^' expression  
           | +expression  
           | -expression  
           | ( expression )  
           | NUM  
           | FLOAT 

输入的标记是诸如 NUMFLOAT、+、-、*、/ 之类的符号,称作终端(无法继续分解或产生其他符号的字符)。一个表达式由终端和规则集组成,例如 expression 则称为非终端。

4.解析器(Parser)

我们将使用 YACC(Yet Another Compiler Compiler) 作为解析器生成器。导入模块:import ply.yacc as yacc

from operator import (add, sub, mul, truediv, pow)  
# 我们的解释器支持的运算符列表  
ops = {  
    "+": add,  
    "-": sub,  
    "*": mul,  
    "/": truediv,  
    "^": pow,  
} 
def p_expression(p):  
    """expression : expression PLUS expression  
                  | expression MINUS expression  
                  | expression DIV expression  
                  | expression MUL expression  
                  | expression POW expression"""  
    if (p[2], p[3]) == ("/", 0):  
        # 如果除以 0,则将“INF”(无限)作为值  
        p[0] = float("INF")  
    else:  
        p[0] = ops[p[2]](p[1], p[3])  
def p_expression_uplus_or_expr(p):  
    """expression : PLUS expression %prec UPLUS  
                  | LPAREN expression RPAREN"""  
    p[0] = p[2]  
def p_expression_uminus(p):  
    """expression : MINUS expression %prec UMINUS"""  
    p[0] = -p[2]  
def p_expression_num(p):  
    """expression : NUM  
                  | FLOAT"""  
    p[0] = p[1]  
# 语法错误时的规则  
def p_error(p):  
    print(f"Syntax error in {p.value}") 

在文档字符串中,我们将添加适当的语法规范。p 列表中的的元素与语法符号一一对应,如下所示:

expression : expression PLUS expression  
p[0]         p[1]       p[2] p[3] 

在上文中,%prec UPLUS%prec UMINUS 是用来表示自定义运算的。%prec 即是 precedence 的缩写。在符号中本来没有 UPLUS 和 UMINUS 这个说法(在本文中这两个自定义运算表示一元正号和符号,其实 UPLUS 和 UMINUS 只是个名字,想取什么就取什么)。之后,我们可以添加基于表达式的规则。YACC 允许为每个令牌分配优先级。

我们可以使用以下方法设置它:

precedence = (  
    ("left", "PLUS", "MINUS"),  
    ("left", "MUL", "DIV"),  
    ("left", "POW"),  
    ("right", "UPLUS", "UMINUS")  
) 

在优先级声明中,标记按优先级从低到高的顺序排列。PLUS MINUS 优先级相同并且具有左结合性(运算从左至右执行)。MULDIV 的优先级高于 PLUS 和 MINUS,也具有左结合性。POW 亦是如此,不过优先级更高。UPLUS 和 UMINUS 则是具有右结合性(运算从右至左执行)。

要解析输入我们将使用:

parser = yacc.yacc()  
result = parser.parse(data)  
print(result) 

完整代码如下:

#####################################  
# 引入模块                           #  
#####################################  
from logging import (basicConfig, INFO, getLogger)  
from operator import (add, sub, mul, truediv, pow)  
import ply.lex as lex  
import ply.yacc as yacc  
# 我们的解释器支持的运算符列表  
ops = {  
    "+": add,  
    "-": sub,  
    "*": mul,  
    "/": truediv,  
    "^": pow,  
}  
#####################################  
# 标记集                             #  
#####################################  
tokens = (  
    # 数据类型  
    "NUM",  
    "FLOAT",  
    # 算术运算  
    "PLUS",  
    "MINUS",  
    "MUL",  
    "DIV",  
    "POW",  
    # 括号  
    "LPAREN",  
    "RPAREN",  
)  
#####################################  
# 标记的正则表达式                    #  
#####################################  
t_PLUS   = r"\+"  
t_MINUS  = r"\-"  
t_MUL    = r"\*"  
t_DIV    = r"/"  
t_LPAREN = r"\("  
t_RPAREN = r"\)"  
t_POW    = r"\^"  
# 忽略空格和制表符  
t_ignore = " \t"  
# 为每个规则添加动作  
def t_FLOAT(t):  
    r"""\d+\.\d+"""  
    t.value = float(t.value)  
    return t  
def t_NUM(t):  
    r"""\d+"""  
    t.value = int(t.value)  
    return t  
# 未定义规则字符的错误处理  
def t_error(t):  
    # 此处的 t.value 包含未标记的其余输入  
    print(f"keyword not found: {t.value[0]}\nline {t.lineno}")  
    t.lexer.skip(1)  
# 如果看到 \n 则将其设为新的一行 
 def t_newline(t):  
    r"""\n+"""  
    t.lexer.lineno += t.value.count("\n")  
#####################################  
# 设置符号优先级                      #  
#####################################  
precedence = (  
    ("left", "PLUS", "MINUS"),  
    ("left", "MUL", "DIV"),  
    ("left", "POW"),  
    ("right", "UPLUS", "UMINUS")  
)  
#####################################  
# 书写 BNF 规则                      #  
#####################################  
def p_expression(p):  
    """expression : expression PLUS expression  
                  | expression MINUS expression  
                  | expression DIV expression  
                  | expression MUL expression  
                  | expression POW expression"""  
    if (p[2], p[3]) == ("/", 0):  
        # 如果除以 0,则将“INF”(无限)作为值  
        p[0] = float("INF")  
    else:  
        p[0] = ops[p[2]](p[1], p[3])  
def p_expression_uplus_or_expr(p):  
    """expression : PLUS expression %prec UPLUS  
                  | LPAREN expression RPAREN"""  
    p[0] = p[2]  
def p_expression_uminus(p):  
    """expression : MINUS expression %prec UMINUS"""  
    p[0] = -p[2]  
def p_expression_num(p):  
    """expression : NUM  
                  | FLOAT"""  
    p[0] = p[1]  
# 语法错误时的规则  
def p_error(p):  
    print(f"Syntax error in {p.value}") 
 #####################################  
# 主程式                             #  
#####################################  
if __name__ == "__main__":  
    basicConfig(level=INFO, filename="logs.txt") 
    lexlexer = lex.lex()  
    parser = yacc.yacc()  
    while True:  
        try:  
            result = parser.parse(  
                input(">>>"),  
                debug=getLogger())  
            print(result)  
        except AttributeError:  
            print("invalid syntax") 

到此这篇关于利用 Python 开发一个 Python 解释器的文章就介绍到这了,更多相关Python 解释器内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

利用 Python 开发一个 Python 解释器

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

如何利用Python开发一个Python解释器

本篇文章给大家分享的是有关如何利用Python开发一个Python解释器,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。前言:计算机只能理解机器码。归根结底,编程语言只是一串文字
2023-06-22

如何利用Python来开发一个QQ机器人

今天就跟大家聊聊有关如何利用Python来开发一个QQ机器人,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。安装方法可在 Python个版本下使用,用 pip 安装:pip insta
2023-06-17

利用Python开发一个自动答题程序

这篇文章主要为大家详细介绍了如何利用Python开发一个自动答题程序,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起了解一下
2023-02-03

利用python开启一个内置下载服务器

场景:当某个同事需要你传服务器上某个文件给他的时候,其实可以利用python开启一个下载器,直接给个url给同事就可以了。1)查看python的版本(因为python2与python3开启的命令有些差异)[root@LVS-DR01 ~]#
2023-01-31

利用Python Paramiko开发l

1.Paramiko模块下的demo.py程序    前面利用Python中的Paramiko模块可以进行SSH的连接,以及用来传送文件(SFTP),但是无论是哪一种方式,连接都是短暂的,并非是长连的,即一次执行一个命令或上传与下载一个文件
2023-01-31

利用Android开发一个音乐播放器

本篇文章为大家展示了利用Android开发一个音乐播放器,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。读取本地音乐文件源代码:import android.media.MediaPlayer;imp
2023-05-31

如何利用MySQL和Python开发一个简单的问答网站

如何利用MySQL和Python开发一个简单的问答网站引言:问答网站是目前互联网上非常受欢迎的在线社交平台之一,它提供了一个可以让用户提问问题并获取其他用户解答的平台。本文将详细介绍如何使用MySQL数据库和Python编程语言开发一个简单
2023-10-22

如何利用MySQL和Python开发一个简单的电商平台

如何利用MySQL和Python开发电商平台本文介绍利用MySQL和Python创建一个简单的电商平台。文章详细阐述了数据库设计、Python代码(包括Flask应用程序、数据库连接和路由)、部署和优化建议。该平台包含产品、订单和用户表,并提供获取、创建和删除数据的API路由。通过优化技巧,您可以提高性能,确保安全性,并扩展平台以满足更大规模的需求。
如何利用MySQL和Python开发一个简单的电商平台
2024-04-09

使用Python制作一个极简四则运算解释器

这篇文章主要介绍了使用Python制作一个极简四则运算解释器,在使用工具之前,至少也要了解工具的作用,需要的朋友可以参考下
2023-05-14

如何利用MySQL和Python开发一个简单的电商平台

要使用MySQL和Python开发一个简单的电商平台,需要进行以下步骤:1. 安装MySQL和Python首先需要在本地安装MySQL数据库和Python开发环境。可以从官方网站下载并按照说明进行安装配置。2. 创建数据库和表使用MySQL
2023-10-10

详解如何在ChatGPT内构建一个Python解释器

这篇文章主要为大家详细介绍了如何在ChatGPT内构建一个Python解释器,文中的示例代码讲解详细,具有一定的学习价值,需要的可以参考一下
2023-02-15

怎么利用Python开发App

本篇内容介绍了“怎么利用Python开发App”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!准备工作利用Python开发app需要用到Pyt
2023-07-06

Python开发建议:如何高效利用Python开发工具

Python是一种强大的编程语言,被广泛应用于各个领域的软件开发中。无论是大型项目还是小型工具,Python都能提供高效的开发方式和强大的工具支持。在本篇文章中,我将探讨一些如何高效利用Python开发工具的建议。首先,选择合适的集成开发环
Python开发建议:如何高效利用Python开发工具
2023-11-23

使用Python怎么开发一个个人云盘应用

本篇文章为大家展示了使用Python怎么开发一个个人云盘应用,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。python的数据类型有哪些?python的数据类型:1. 数字类型,包括int(整型)、l
2023-06-14

利用C++开发一个protobuf动态解析工具

数据库中存储的protobuf序列化的内容,有时候查问题想直接解析查看内容。很多编码在网上很容易找到编解码工具,但protobuf没有找到编解码工具,可能这样的需求比较少吧,那就自己用C++实现一个,感兴趣的可以了解一下
2023-01-03

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录