尝试用GO写python编译器:创建互动式命令号窗口REPL

2024-04-30 21:48

本文主要是介绍尝试用GO写python编译器:创建互动式命令号窗口REPL,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

上一节完成的词法解析器存在一些明显问题,例如在识别数字时,面对字符串“123abc",它会识别为两者情况,分别为NUMBER:123,然后是IDENTIFIER:anc,实际上这样的字符串应该被认为是语法错误。另外我们还有一些”连号“操作符没有处理,例如“==, !=, – ,++"等。

本节我们将在上一节的基础上对词法解析器做进一步完善。首先在token.go里面添加新的操作符定义:
···
const (
ILLEGAL = “ILLEGAL”
EOF = “EOF”
IDENTIFIER = “IDENTIFIER” //变量类型对应的归类
NUMBER = “NUMBER” //数值类型对应的归类
ASSIGN = “=” //赋值操作符
PLUS = “+” //加号操作符
LPAR = “(”
RPAR = “)”
LBRACE = “{”
RBRACE = “}”
COMMA = “,”
COLON = “:”
DEF = “def” //关键字
INT = “int”
RETURN = “return”
ASSERT = “assert”
AND = “and”

//第三节添加
TRUE = "True"
FALSE = "False"
IF = "if"
ELSE = "else"
EQUAL = "=="
NOTEQUAL = "!="
GREATEREQUAL = ">="
LESSEQUAL = "<="
MINUS = "-"
BANG = "!"
ASTERISK = "*"
SLASH = "/"
LT = "<"
GT = ">"
//第三节添加

)

var keywords = map[string]TokenType {
“def” : DEF,
“int” : INT,
“return” : RETURN,
“assert” : ASSERT,
“and” : AND,
//第三节添加
“if” : IF ,
“else” : ELSE,
“True” : TRUE,
“False” : FALSE,
//第三节添加
}
···
现在有关问题在于,有些操作符必须读取双字符才能认定,在lexer.go中的NextToken函数,它的switch只能接收单个字符,因此要识别">=", "!="等这些符号时,我们需要在读取到第一个字符时,还需要再读取下一个字符,这样获取到两个字符后才能做出判断,于是我们在lexer.go里面再添加一个函数:

func (l *Lexer) peekChar() byte {if l.readPosition >= len(l.input) {return 0 } else {return l.input[l.readPosition]}
}

当解析器读取到符号"=", “!”, “<”, ">"时,它还需要借助上面的函数获取下一个字符,这样才能决定当前遇到的操作符是哪一种,代码如下:

func (l *Lexer) NextToken() token.Token{//读取一个字符,判断是否属于特定分类var tok token.Token//忽略空格,回车,换行等特定字符l.skipSpecialChar()switch l.ch {。。。。//第三节添加case '-':tok = newToken(token.MINUS, l.ch)case '*':tok = newToken(token.ASTERISK, l.ch)case '/':tok = newToken(token.SLASH, l.ch)case '=' :if l.peekChar() == '=' {//遇到==操作符ch := l.ch l.readChar()tok = token.Token{Type: token.EQUAL, Literal: string(ch) + string(l.ch}} else {tok = newToken(token.ASSIGN, l.ch)}case '!':if l.peekChar() == '=' {//操作符!=ch := l.ch l.readChar()tok = token.Token{Type: token.NOEQUAL, Literal: string(ch) + string(l.ch)}} else {tok = newToken(token.BANG, l.ch)}case '<':if l.peekChar() == '=' {//操作符!=ch := l.ch l.readChar()tok = token.Token{Type: token.LESSEQUAL, Literal: string(ch) + string(l.ch)}} else {tok = newToken(token.LT, l.ch)}case '>':if l.peekChar() == '=' {//操作符!=ch := l.ch l.readChar()tok = token.Token{Type: token.GREATEREQUAL, Literal: string(ch) + string(l.ch)}} else {tok = newToken(token.GT, l.ch)}。。。。}。。。。     
}

现在我们可以再次完善用于测试的python代码,在lexer_test.go里面进行修改如下:


func TestNextToken2(t *testing.T) {input := `def add(x, y):assert 0 <= x <= yz = x + yreturn z`tests := []struct {expectedType token.TokenType expectedLiteral string } {{token.DEF, "def"},{token.IDENTIFIER, "add"},{token.LPAR, "("},{token.IDENTIFIER, "x"},{token.COMMA, ","},{token.IDENTIFIER, "y"},{token.RPAR, ")"},{token.COLON, ":"},//第三节添加{token.ASSERT, "assert"},{token.NUMBER, "0"},{token.LESSEQUAL, "<="},{token.IDENTIFIER, "x"},{token.LESSEQUAL, "<="},{token.IDENTIFIER, "y"},//第三节添加{token.IDENTIFIER, "z"},{token.ASSIGN, "="},{token.IDENTIFIER, "x"},{token.PLUS, "+"},{token.IDENTIFIER, "z"},{token.RETURN, "return"},{token.IDENTIFIER, "z"},}l := New(input)for i, tt := range tests {tok := l.NextToken()if tok.Type != tt.expectedType {t.Fatalf("test[%d] - tokenType wrong. expected=%q, got=%q",i, tt.expectedType, tok.Type)if tok.Literal != tt.expectedLiteral {t.Fatalf("tests[%d] - literal wrong. expected=%q, got=%q",i, tt.expectedLiteral, tok.Literal)}}}
}

执行go test后,可以发现上面用例可以通过,这意味着我们的词法解析请求已经能够识别比较复杂的python代码了。

有过Python开发经验的同学都知道,在命令号行窗口输入命令python后,我们可以进入一个互动环境,在里面可以直接输入代码,点击回车就能直接运行,现在我们也来实现这个功能。在根目录创建文件夹repl,然后在里面创建文件repl.go,然后输入代码如下:

package repl import ("bufio""fmt""io""lexer""token"
)const PROMPT = ">>"func Start(in io.Reader, out io.Writer) {scanner := bufio.NewScanner(in) //从控制台获取输入for {fmt.Printf(PROMPT)scanned := scanner.Scan() //点击回车后返回输入内容if !scanned {return //没有输入内容}lien := scanner.Text() //当前输入的内容l := lexer.New(line) for tok := l.NextToken(); tok.Type != token.EOF; tok = l.NextToken() {fmt.Printf("%+v\n", tok) //输出解析的结果}}
}

接下来我们把main.go的内容输入如下:

package repl import ("bufio""fmt""io""lexer""token"
)const PROMPT = ">>"func Start(in io.Reader, out io.Writer) {scanner := bufio.NewScanner(in) //从控制台获取输入for {fmt.Printf(PROMPT)scanned := scanner.Scan() //点击回车后返回输入内容if !scanned {return //没有输入内容}line := scanner.Text() //当前输入的内容l := lexer.New(line) for tok := l.NextToken(); tok.Type != token.EOF; tok = l.NextToken() {fmt.Printf("%+v\n", tok) //输出解析的结果}}
}

上面代码运行后结果如下:
请添加图片描述
可以看到,我们当前完成的工作还真有一点Python编译环境的味道。完整代码请点击这里{https://github.com/wycl16514/-GO-python-REPL.git}

这篇关于尝试用GO写python编译器:创建互动式命令号窗口REPL的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

python: 多模块(.py)中全局变量的导入

文章目录 global关键字可变类型和不可变类型数据的内存地址单模块(单个py文件)的全局变量示例总结 多模块(多个py文件)的全局变量from x import x导入全局变量示例 import x导入全局变量示例 总结 global关键字 global 的作用范围是模块(.py)级别: 当你在一个模块(文件)中使用 global 声明变量时,这个变量只在该模块的全局命名空

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

30常用 Maven 命令

Maven 是一个强大的项目管理和构建工具,它广泛用于 Java 项目的依赖管理、构建流程和插件集成。Maven 的命令行工具提供了大量的命令来帮助开发人员管理项目的生命周期、依赖和插件。以下是 常用 Maven 命令的使用场景及其详细解释。 1. mvn clean 使用场景:清理项目的生成目录,通常用于删除项目中自动生成的文件(如 target/ 目录)。共性规律:清理操作

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多

nudepy,一个有趣的 Python 库!

更多资料获取 📚 个人网站:ipengtao.com 大家好,今天为大家分享一个有趣的 Python 库 - nudepy。 Github地址:https://github.com/hhatto/nude.py 在图像处理和计算机视觉应用中,检测图像中的不适当内容(例如裸露图像)是一个重要的任务。nudepy 是一个基于 Python 的库,专门用于检测图像中的不适当内容。该

pip-tools:打造可重复、可控的 Python 开发环境,解决依赖关系,让代码更稳定

在 Python 开发中,管理依赖关系是一项繁琐且容易出错的任务。手动更新依赖版本、处理冲突、确保一致性等等,都可能让开发者感到头疼。而 pip-tools 为开发者提供了一套稳定可靠的解决方案。 什么是 pip-tools? pip-tools 是一组命令行工具,旨在简化 Python 依赖关系的管理,确保项目环境的稳定性和可重复性。它主要包含两个核心工具:pip-compile 和 pip