用Python开发一个自制的编程语言(虚拟机解释型)[3] 语法分析器(1)

本文基于上一篇完成的词法分析器,继续用Python开发语法分析器的AST部分。目标是分析Token流语法关系并转换为AST树,搭建其框架。采用类和对象实现主要语法结构,介绍了Identical、Obj等相关类,还提及AST类的dumps和loads方法可保存AST树。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在上一篇文章中,我们完成了词法分析器,下面我们继续努力,今要开发的是语法分析器的AST部分,让我们开始吧!

​​​​​用Python开发一个自制的编程语言(虚拟机解释型)[2] 词法分析器(2)_自制编程语言基于python_嘉定世外的JinJiayang的博客-CSDN博客

目标分析

我们在上一篇文章中已经完成了一个简单的词法分析器,将代码映射成了Token流,这次我们要分析Token流中的语法关系,并将其转换为AST树(语法树)。

注:为了方便起见,我们将语义分析的部分拆分进语法分析和执行两大板块中。

这篇文章只需搭出一个AST的框架即可。

注:我们的大多数代码都是用面向对象框架实现,感兴趣的话可以用面向过程的语法重写。

具体实现

首先在\sjserver目录下建立文件sjast.py,这个文件用于定义语法树的结构。并在\sjparser目录下建立文件sentenceparser.py,但这个文件我们这次不会用到。

我们采用类和对象实现AST树的主要语法结构,后期会涉及到大量类型检查。

sjast.py:

from typing import List, Dict
import pickle


t = 0


class Identical:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return f"identical-{self.name}"

    __repr__ = __str__


class Obj(object):
    def __init__(self, value=""):
        self.value = value
        self.line = -1

    def __str__(self):
        return repr(self.value)

    __repr__ = __str__


class This(object):
    pass


this = This()


class Statement(object):
    def __init__(self):
        self.type = ""
        self.line = -1
        self.value: Dict[str] = {}

    def __str__(self):
        return f"{{'{self.type}': {self.value}}}"

    __repr__ = __str__

    def append(self, name, value):
        self.value[name] = value


class StatementUnion(object):
    def __init__(self):
        self.value: List[Statement] = []

    def __str__(self):
        return str(self.value)

    __repr__ = __str__

    def append(self, value):
        self.value.append(value)


class AST(StatementUnion):
    def __init__(self, debug=False, version=1):
        super(AST, self).__init__()
        self.debug = debug
        self.version = version

    def dumps(self, path):
        with open(f"{path}", "wb") as f:
            pickle_obj = pickle.dumps(self, protocol=pickle.HIGHEST_PROTOCOL)
            f.write(pickle_obj)

    def loads(self, path):
        with open(f"{path}", "rb") as f:
            obj = pickle.loads(f.read())
            self.value = obj.value
            self.debug = obj.debug
            self.version = obj.version

这些代码并不是很复杂,我们慢慢分析:

Identical

标识符。代表着一个变量名、一个类或是其他。类似于C++代码中

int a = 1;
std::cout << a;

中的a。因为我们ScrJ语言不存在指针,所以没有奇奇怪怪的class。

__str__方法和__repr__方法是自定义对象的字符串表示,有兴趣的可以关注我这篇文章:

Python 类和对象——魔法方法(一)-CSDN博客

Obj

没有太大用处,主要是我前面自己写代码的时候为了解决一个bug加上去的

Obj表示Obj.value,自指,一般后续不大会用到。

This

虚拟对象。

当出现this关键字是,sentenceparser解析为this常量,其值随运行而变化(我们ScrJ是一个动态的语言),this在其他语言中大家应该很熟悉了。

Statement

这个类是我们后续很多操作的基础,表示一个语句或表达式。

Statement对象(简称s)具有三个属性:type: str、line: int和value: Dict[str].

s.type表示s的类型,如声明、赋值、函数调用等。

s.line表示s处于第几行,在后续的错误处理堆栈中会用到(尽管我自己也没怎么搞懂

s.value表示s的信息,比较复杂、灵活,我们在下一节中讲到。 

特别地,我们将表达式如1+1仍然抽象成一个Statement对象,而不使用额外的类来表示。

StatementUnion

没有什么好过多介绍的,表示一个语句联合体,如if{...},while{...},不作为Statement而是StatementUnion处理,方便后续操作。

AST

我们将AST看作是StatementUnion和Statement的嵌套。AST类派生自StatementUnion,我们将所有代码仍然看作一个语句联合体。

需要注意的是,AST类有两个重要方法,分别是dumps和loads。这两个方法可以将AST树保存到本地,也就是说,我们执行完parser部分后,可以转换为.sjast文件,下次可直接运行该文件而跳过parser的部分。parser涉及大量CPU计算,会浪费很多时间,保存为.sjast文件后执行的过程类似虚拟机执行的过程,如Java、Python的字节码。

dumps和loads的原理很简单,只运用了pickle库,网上有大量资源。

END

好了,今天到此为止,下一篇我们将详细讲述如何根据符号流解析AST树,保证让你拥有一个属于自己的parser。

如果你也想尝试这个项目的话,创作不易,不妨点个赞,关注本专栏,thanks~

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

嘉定世外的JinJiayang

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值