基于PLY的解释器

2023-12-06 21:32
文章标签 解释器 ply

本文主要是介绍基于PLY的解释器,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

今年过年回家本来要好好放松一下的,但是闲了几天发现很无聊,于是乎想起来了以前想学的东西,但是没来得及学的,那就是解释器,但是我没把电脑带回来,大家一定想不到,我这篇博客竟然是在树莓派中写的,哈哈。废话不多说,现在开始。

首先给出这次的代码:https://download.csdn.net/download/shixiongtao/12116774。大家自己首先下载运行一下看一下效果。

虽然功能非常简单,但是基本上体现了一个完整的过程。

PLY是lex和yacc的纯python实现,需要用到两个包 ply.lex和ply.yacc,ply.lex是把程序分割为一个个的标记(token),ply.yacc则是负责根据token生成语法,并执行。


###################################################tokens = ['ID', 'FLOAT', 'INT'] literals = ['=', '+', '-', '*', '/', '(', ')']t_ignore = " \t"t_ID = r'[a-zA-Z_][a-zA-Z_0-9]*'def t_FLOAT(t):r'\d+\.\d+'t.value = float(t.value)print('t_FLOAT', t.value)return tdef t_INT(t):r'\d+'t.value = int(t.value)print('t_INT', t.value)return tdef t_newline(t):r'\n+'t.lexer.lineno += t.value.count("\n")def t_error(t):print("Illegal character '%s'" % t.value[0])t.lexer.skip(1)# Build the lexer
import ply.lex as lex
lex.lex()###################################################
# dictionary of ids
ids = {}# Parsing rulesprecedence = (('left', '+', '-'),('left', '*', '/'),('right', 'UMINUS'),
)def p_statement_assign(p):'statement : ID "=" expression'ids[p[1]] = p[3]for i in range(len(p)):print('p_statement_assign', 'pos', i, 'value', p[i])def p_statement_expr(p):'statement : expression'print(p[1])for i in range(len(p)):print('p_statement_expr', 'pos', i, 'value', p[i])def p_expression_plus(p):'''expression : expression '+' expression'''p[0] = p[1] + p[3]for i in range(len(p)):print('p_expression_plus', 'pos', i, 'value', p[i])def p_expression_minus(p):'''expression : expression '-' expression'''p[0] = p[1] - p[3]for i in range(len(p)):print('p_expression_minus', 'pos', i, 'value', p[i])def p_expression_times(p):'''expression : expression '*' expression'''p[0] = p[1] * p[3]for i in range(len(p)):print('p_expression_times', 'pos', i, 'value', p[i])def p_expression_div(p):'''expression : expression '/' expression'''p[0] = p[1] / p[3]for i in range(len(p)):print('p_expression_div', 'pos', i, 'value', p[i])def p_expression_uminus(p):"expression : '-' expression %prec UMINUS"p[0] = -p[2]for i in range(len(p)):print('p_expression_uminus', 'pos', i, 'value', p[i])def p_expression_group(p):"expression : '(' expression ')'"p[0] = p[2]for i in range(len(p)):print('p_expression_group', 'pos', i, 'value', p[i])def p_expression_float(p):"expression : FLOAT"p[0] = p[1]for i in range(len(p)):print('p_expression_float', 'pos', i, 'value', p[i])def p_expression_int(p):"expression : INT"p[0] = p[1]for i in range(len(p)):print('p_expression_int', 'pos', i, 'value', p[i])def p_expression_id(p):"expression : ID"try:p[0] = ids[p[1]]except LookupError:print("Undefined name '%s'" % p[1])p[0] = 0for i in range(len(p)):print('p_expression_id', 'pos', i, 'value', p[i])def p_error(p):if p:print("Syntax error at '%s'" % p.value)else:print("Syntax error at EOF")###################################################
import ply.yacc as yacc
yacc.yacc()while True:try:s = input('calc > ')except EOFError:breakif not s:continueyacc.parse(s)print('ids', ids)

咱们开始一行一行的解析。

tokens = ['ID', 'FLOAT', 'INT'] 

在ply.lex中会默认读取我们声明的tokens变量,在这个变量中存储的是可能的标记种类。

比如a = 1+1.1,在这段程序中包含了三种标记,a(ID),1(INT),1.1(FLOAT)。

literals = ['=', '+', '-', '*', '/', '(', ')']

这一行生命的是字面量,同样的ply.lex会默认读取我们声明的literals变量,其实这也算是token只不过这个token特殊的地方在于,这个token的type和value是同一个值。

t_ignore = " \t"

这一行代表省略所有的空格。

t_ID = r'[a-zA-Z_][a-zA-Z_0-9]*'

这一行给出具体的ID这个token的正则表达式,以a-z,A-Z,和下划线为开头,以a-z,A-Z,0-9和下划线,重复任意次。

def t_FLOAT(t):r'\d+\.\d+'t.value = float(t.value)print('t_FLOAT', t.value)return t

前面的t_ID是给出ID这个token的正则表达式,ply.lex的token对应的正则表达式也可以以函数定义,这里给出了FLOAT的正则表达式定义,r'\d+\.\d+',整数出现多次,小数点,整数出现多次。在进入到这个函数的时候,首先t就是匹配到的token,t.value就是具体匹配到的字符串,这里需要把字符串转换成float数字,t.value = float(t.value),之后再返回t。

def t_INT(t):r'\d+'t.value = int(t.value)print('t_INT', t.value)return t

这个是对应INT这个token处理的函数。

def t_newline(t):r'\n+'t.lexer.lineno += t.value.count("\n")

这个是识别行号的处理函数,当识别到一个新行的时候,通过t.lexer.lineno来累加行号。

def t_error(t):print("Illegal character '%s'" % t.value[0])t.lexer.skip(1)

这是处理错误的函数,在检查的错误的时候,这里通过简单的打印,并跳过这个错误字符。

# Build the lexer
import ply.lex as lex
lex.lex()

最终导入ply.lex包,这个包就会自动读取上面写的规则。需要注意的是,上面这个规则定义的顺序决定了规则匹配的优先级,先定义的规则,在匹配的时候具有较高的优先级,具体来说,t_FLOAT的优先级必须比t_INT的优先级要高,如果弄反了,那么一个浮点数就会解析为两个整数,和一个错误。

上面就是ply.lex部分,下面开始ply.yacc部分。

ids = {}

首先定义变量存储的字典。

precedence = (('left', '+', '-'),('left', '*', '/'),('right', 'UMINUS'),
)

这里声明了操作符的优先级。

def p_statement_assign(p):'statement : ID "=" expression'ids[p[1]] = p[3]for i in range(len(p)):print('p_statement_assign', 'pos', i, 'value', p[i])

这里给出第一个模式,将一个表达式的值赋值给一个ID。

这里通过ids这个字典,将所有的变量名和变量值对应存储起来。最好的for循环是为了打印p中的内存,是为了学习使用,并不是必须的,下面的都是这样。

def p_statement_expr(p):'statement : expression'print(p[1])for i in range(len(p)):print('p_statement_expr', 'pos', i, 'value', p[i])

第二个模式,当单独输入一个变量时,直接打印这个变量的值。

def p_expression_plus(p):'''expression : expression '+' expression'''p[0] = p[1] + p[3]for i in range(len(p)):print('p_expression_plus', 'pos', i, 'value', p[i])def p_expression_minus(p):'''expression : expression '-' expression'''p[0] = p[1] - p[3]for i in range(len(p)):print('p_expression_minus', 'pos', i, 'value', p[i])def p_expression_times(p):'''expression : expression '*' expression'''p[0] = p[1] * p[3]for i in range(len(p)):print('p_expression_times', 'pos', i, 'value', p[i])def p_expression_div(p):'''expression : expression '/' expression'''p[0] = p[1] / p[3]for i in range(len(p)):print('p_expression_div', 'pos', i, 'value', p[i])

这四个模式是四则运算。

def p_expression_uminus(p):"expression : '-' expression %prec UMINUS"p[0] = -p[2]for i in range(len(p)):print('p_expression_uminus', 'pos', i, 'value', p[i])

这个模式是对变量取负数。

def p_expression_group(p):"expression : '(' expression ')'"p[0] = p[2]for i in range(len(p)):print('p_expression_group', 'pos', i, 'value', p[i])

这个是括号对应的模式。

def p_expression_float(p):"expression : FLOAT"p[0] = p[1]for i in range(len(p)):print('p_expression_float', 'pos', i, 'value', p[i])def p_expression_int(p):"expression : INT"p[0] = p[1]for i in range(len(p)):print('p_expression_int', 'pos', i, 'value', p[i])def p_expression_id(p):"expression : ID"try:p[0] = ids[p[1]]except LookupError:print("Undefined name '%s'" % p[1])p[0] = 0for i in range(len(p)):print('p_expression_id', 'pos', i, 'value', p[i])

浮点数,整数,ID的模式。

def p_error(p):if p:print("Syntax error at '%s'" % p.value)else:print("Syntax error at EOF")

最后是错误处理。

最后就是通过输入字符串,并解释字符串,输出中间详细信息,和最终的结果。

import ply.yacc as yacc
yacc.yacc()while True:try:s = input('calc > ')except EOFError:breakif not s:continueyacc.parse(s)print('ids', ids)

最后给出一个运行实例。

>>> ================================ RESTART ================================
>>> 
calc > a = 1
t_INT 1
p_expression_int pos 0 value 1
p_expression_int pos 1 value 1
p_statement_assign pos 0 value None
p_statement_assign pos 1 value a
p_statement_assign pos 2 value =
p_statement_assign pos 3 value 1
ids {'a': 1}
calc > b = 2
t_INT 2
p_expression_int pos 0 value 2
p_expression_int pos 1 value 2
p_statement_assign pos 0 value None
p_statement_assign pos 1 value b
p_statement_assign pos 2 value =
p_statement_assign pos 3 value 2
ids {'b': 2, 'a': 1}
calc > c = a + b + (3 * 4 / 5) - 6
p_expression_id pos 0 value 1
p_expression_id pos 1 value a
p_expression_id pos 0 value 2
p_expression_id pos 1 value b
p_expression_plus pos 0 value 3
p_expression_plus pos 1 value 1
p_expression_plus pos 2 value +
p_expression_plus pos 3 value 2
t_INT 3
p_expression_int pos 0 value 3
p_expression_int pos 1 value 3
t_INT 4
p_expression_int pos 0 value 4
p_expression_int pos 1 value 4
p_expression_times pos 0 value 12
p_expression_times pos 1 value 3
p_expression_times pos 2 value *
p_expression_times pos 3 value 4
t_INT 5
p_expression_int pos 0 value 5
p_expression_int pos 1 value 5
p_expression_div pos 0 value 2.4
p_expression_div pos 1 value 12
p_expression_div pos 2 value /
p_expression_div pos 3 value 5
p_expression_group pos 0 value 2.4
p_expression_group pos 1 value (
p_expression_group pos 2 value 2.4
p_expression_group pos 3 value )
p_expression_plus pos 0 value 5.4
p_expression_plus pos 1 value 3
p_expression_plus pos 2 value +
p_expression_plus pos 3 value 2.4
t_INT 6
p_expression_int pos 0 value 6
p_expression_int pos 1 value 6
p_expression_minus pos 0 value -0.5999999999999996
p_expression_minus pos 1 value 5.4
p_expression_minus pos 2 value -
p_expression_minus pos 3 value 6
p_statement_assign pos 0 value None
p_statement_assign pos 1 value c
p_statement_assign pos 2 value =
p_statement_assign pos 3 value -0.5999999999999996
ids {'c': -0.5999999999999996, 'b': 2, 'a': 1}
calc > 

到这块,这段简单的程序就能支持简单的四则运算了,下面接着要学的就是支持if,else,for循环了。

这篇关于基于PLY的解释器的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/463413

相关文章

java设计模式(行为型模式:状态模式、观察者模式、中介者模式、迭代器模式、访问者模式、备忘录模式、解释器模式)

6,行为型模式 6.5 状态模式 6.5.1 概述 【例】通过按钮来控制一个电梯的状态,一个电梯有开门状态,关门状态,停止状态,运行状态。每一种状态改变,都有可能要根据其他状态来更新处理。例如,如果电梯门现在处于运行时状态,就不能进行开门操作,而如果电梯门是停止状态,就可以执行开门操作。 类图如下: 代码如下: public interface ILift {//电梯的4个状态//

数据分析-第三方库(工具包):Numpy【使用ndarray对象处理多维数组】【比Python原生list运算效率高:①内存块风格;②支持并行化运算;③底层用C编写,内部解除了GIL(全局解释器锁)】

一、Numpy优势 Numpy运算速度上的优势Numpy的数组内存块风格Numpy的并行化运算 1、Numpy介绍 Numpy(Numerical Python)是一个开源的Python科学计算库,用于快速处理任意维度的数组。 Numpy支持常见的数组和矩阵操作。对于同样的数值计算任务,使用Numpy比直接使用Python要简洁的多。 Numpy使用ndarray对象来处理多维数组,

Java设计模式【解释器模式】-行为型

1. 介绍 1.1 什么是解释器模式? 解释器模式(Interpreter Pattern)是一种行为型设计模式,它为某种语言定义其文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子。通俗来说,解释器模式用于解析和处理一种特定的语言或表达式,使其能够被转换为计算机能够理解的形式。 解释器模式通常用来构建简单的语言解释器或表达式求值引擎,例如计算器、命令解析器等。 1.2 优缺

-bash: ./log.sh: /bin/bash^M: 坏的解释器: 没有那个文件或目录

这个错误通常是由于脚本文件中的换行符格式不正确。你可以使用以下方法解决: 转换换行符格式: 使用 dos2unix 工具将脚本文件中的 CRLF(Windows 风格)换行符转换为 LF(Unix 风格)dos2unix log.sh 手动修复: 使用 sed 命令来删除不必要的 ^M 字符: sed -i 's/\r$//' log.sh 或者在编辑器中(如 vim)打开脚本,并使用 :

在pycharm中选择虚拟环境的解释器出错Project Interpreter Error: Please Specify a different SDK Name

在pycharm中新建了一个Django的项目,选择的是一个虚拟环境的python解释器,在打开.py文件的时候显示解释器有问题。进入到she设置中仍然有问题。报错Project Interpreter Error: Please Specify a different SDK Name。 此时看了一下,只有一个系统的环境变量下的python和虚拟环境下的python,并没有重名的情况,于是在s

python基础-GIL全局解释器锁

GIL介绍GIL与LockGIL与多线程线程进程适用范围 GIL介绍 ”’ 定义: In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. T

设计模式 15 解释器模式

设计模式 15 创建型模式(5):工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式结构型模式(7):适配器模式、桥接模式、组合模式、装饰者模式、外观模式、享元模式、代理模式行为型模式(11):责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式、访问者模式 文章目录 设计模式 15解释器模式(Interpreter

python------python解释器,pycharm下载配置

目录 ​编辑 如何下载python解释器 如何下载pycharm 如何配置pycharm环境 ps 如何下载python解释器 访问Python官方网站的下载页面:Python下载页面。根据您的操作系统(Windows、macOS、Linux)选择合适的版本。对于Windows用户,通常会有两个版本可供选择:32位和64位。点击您选择的版本旁边的“Download

在 Mac 上安装 MIT Scheme 解释器的命令行方法

在 Mac 上安装 MIT Scheme 解释器的命令行方法 步骤如下: 第 1 步,安装 brew 。确保计算机已经连上了网络,然后打开 Mac 上的终端,输入如下的命令:   ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"       第 2 步,

MATLAB 手写Ply文件点云读取类(86)

MATLAB 手写Ply文件点云读取类(86) 一、算法介绍二、算法实现1.PLYReader.m 类文件2.可视化 一、算法介绍 PLY是一种常见的点云文件格式,这里尝试手写一个读点云的类,查看是否能正常读取,结果将可视化,具体代码如下: 二、算法实现 1.PLYReader.m 类文件 classdef PLYReaderpropertiesVertices %