造个轮子--用Python写个语法解析器

前言

目的纯粹,基于Python做一个简单的新的简单的编程语言。一方面是开拓视野,另一方面是作为毕设的临时过渡方案(没错,先前提到的算法平台,没有把握快速开发完毕,即便我使用大量的脚手架完成开发,但是算法容器,rpc算法调度中间件都需要自己造轮子,难度较大,此外还有用户部分的UI设计等等,最重要的是,那帮老师根本无法理解这种项目。没有必要搞太"花里胡哨"但是尽管如此,这个项目我后期还是要开发的,主要原因在于算法容器和rpc算法调度中间件,这个对我来说是非常值得去做的。里面涉及到的思想是非常受用的。虽然我现在在脑子里面构思好了,要怎么做,但是这个编码量实在太大。并且目标院校改考11408,现在导致我很被动,因此,我决定写一个sample computer language。同时为了加快开发进度,直接使用Python进行编写,后期转到Pypy,然后编译出这个语言的编译器。

那么目标的话,就是做到简单,直接做中文的,给小孩子锻炼思维的。当然,这也是为了方便给我讲故事。能在那帮尸位素餐的老师面前多说点他们能够理解的东西。没办法一个普通院校,很多老师水平也就那样,很无奈,但是没有办法改变。

选型

针对人群

有样没样,样子要像,那么这个编程语言的主要目的话,就是易学易用。推出中文编程,兼容Python,方便培养小学生锻炼编程思维,适合一到两年级的小学生进行学习。不同于图形化编程,Hlang可以体验到更加真实的编程环境,并且不会增加难度。既可以培养孩子的逻辑思维,同时还可以。。。 算了,编不下去了,就是个dome,同时用来应付应付毕设。

目标

没有目标,就是混~~ 本文目标,实现一个简单的语法解析器。反正随便写个几千行代码就能交个差,一帮混子!

技术实现

基于Python,体现体现思想,不追求运行效率,重在好学,给小孩子玩玩儿。不是总有某些家长说啥,英语难计算机简单的嘛?来,那就用用这个~~

本文目标

写一个简单的语法解析器,然后下班~ 高数玩腻了,就玩这个,这个玩腻了就学英语。

效果

ok,我们先来看到我们的实现效果:

这个就是一个简易的语法解析器。

实现

扯远了,我们来看看是如何进行实现的。

首先是定义好我们的标准合法字符:

ini 复制代码
TT_INT = "整数"
TT_FLOAT = "浮点数"
TT_PLUS = "加号"
TT_DIV = "除号"
TT_MINUS = "减号"
TT_LPAREN = "左括号"
TT_RPAREN = "右括号"
TT_MUL = "乘"
TT_POWER = "次幂"

DIGITS = "123456789"

然后我们定义一个Token把这些对象封装起来

python 复制代码
class Token:

    def __init__(self,is_type,is_value=None):
        self.is_type = is_type
        self.is_value = is_value


    def __repr__(self):
        if self.is_value:
            return "|类型:{},值:{}|".format(self.is_type,self.is_value)
        return "|类型:{}|".format(self.is_type)

    def __str__(self):
        if(self.is_value):
            return "|{}|".format(self.is_value)
        return "|这个对象没有值,类型为:{}|".format(self.is_type)

在这里我们要做的目的很简单,那就是,把接下来输入的内容,或者文本内容,进行读取,然后解析出东西,把合法的字符收集起来。注意,我们这里还没有什么变量的概念,在这里只是负责解析好基本的合法字符。至于变量的引入要到后面,因为这个时候要设计清楚基本的语法规范,然后就是照着一顿借鉴就完了。

字符指针

之后的话,我们定义好了Token,那么就要去读取解析文本,这个没有办法,我们只能一个字符一个字符进行扫描。为了方便,因此,这里对字符指针进行一个简单封装。

python 复制代码
class Position:

    def __init__(self, idx, ln, col):
        self.idx = idx
        self.ln = ln
        self.col = col

    def advance(self, cur_char):
        
        self.idx += 1
        self.col += 1

        if cur_char == '\n':
            self.ln += 1
            self.col = 0
        return self

错误类型

之后的话,我们还要去定义错误。比如,当我输入一个非法字符之后要报个错,就像Python一样:

所以我们也要来个这个东西:

python 复制代码
"""
顶级错误(老大)
"""
class HlangError:
    def __init__(self, pos_ln,in_fn,error_name, details):
        """
        :param pos_ln: 错误行
        :param in_fn: 输入文件
        :param error_name: 错误名称
        :param details: 错误细节,说明
        """
        self.pos_ln = pos_ln
        self.in_fn = in_fn
        self.error_name = error_name
        self.details = details

    def as_string(self):
        red_code = "\033[91m"
        reset_code = "\033[0m"
        result = f'{self.error_name}: {self.details}\n'
        result += f'来自 {self.in_fn}, line {self.pos_ln + 1}'
        return red_code+result+reset_code


class IllegalCharError(HlangError):
    """
    非法字符错误
    """
    def __init__(self, pos_ln,in_fn, details):
        super().__init__(pos_ln, in_fn, '非法字符', details)

语法解析

那么之后的话,就可以开始我们的语法解析了: 这个代码的话,很简单,就是往死里加入就好了:

python 复制代码
"""
语法解析器
"""
class Lexer:
    def __init__(self, in_fn, text):
        """
        :param in_fn: 从哪里输入的文本(文本所在文件,标准输入,输出也是一个文件)
        其实就是文件名~~~
        :param text: 待解析文本
        """
        self.in_fn = in_fn
        self.text = text
        self.pos = Position(-1, 0, -1)
        self.cur_char = None
        self.advance()
        #基本的符号处理
        self.char_pro_base = {
            '+':TT_PLUS,
            '-':TT_MINUS,
            '*':TT_MUL,
            '/':TT_DIV,
            '^':TT_POWER,
            '(':TT_LPAREN,
            ')':TT_RPAREN
        }

    def advance(self):
        self.pos.advance(self.cur_char)
        self.cur_char = self.text[self.pos.idx] if self.pos.idx < len(self.text) else None

    def __char_process(self,tokens,TT):
        """
        处理基本字符的方法,
        添加Token,并且移动字符指针
        :return:
        """
        tokens.append(Token(TT))
        self.advance()

    def make_tokens(self):
        """
        将文本当中的字符添加到语法解析器当中,将符合语法规范的内容,封装为Token,
        (就像Spring将对象信息再封装为Wapper一样,方便后续进行操作。)
        :return:
        """
        tokens = []
        while self.cur_char != None:
            if self.cur_char in ' \t':
                #制表符(空格),没有意义,往前移动
                self.advance()
            elif self.cur_char in DIGITS:
                #如果是数字,自动往前搜索,并且将数字进行添加,并且判断类型,
                #数字比较特殊,不是一个字符一个字符参与的(后面还要定义关键字也是类似的)
                tokens.append(self.make_number())
            else:
                TT = self.char_pro_base.get(self.cur_char)
                if(TT):
                    self.__char_process(tokens,TT)
                else:
                    char = self.cur_char
                    self.advance()
                    return [], IllegalCharError(self.pos.ln,self.in_fn, "'" + char + "'")
        return tokens, None

    def make_number(self):
        num_str = ''
        dot_count = 0

        while self.cur_char != None and self.cur_char in DIGITS + '.':

            if self.cur_char == '.':
                if dot_count == 1: break
                dot_count += 1
                num_str += '.'
            else:
                num_str += self.cur_char
            self.advance()

        if dot_count == 0:
            return Token(TT_INT, int(num_str))
        else:
            return Token(TT_FLOAT, float(num_str))

之后的话,别忘了还需要要一个run作为入口,run起来:

python 复制代码
"""
语言解析,运行入口
"""
def run(fn, text):
    lexer = Lexer(fn, text)
    tokens, error = lexer.make_tokens()
    return tokens, error

交互

最后的最后,就是我们的交互了:

python 复制代码
"""
Hlang is a Sample Language shell
Just a sample example for learning by Huterox
"""
import basic

while True:
    input_text = input("交互终端:")
    result, error = basic.run('<标准输入>', input_text)

    if error: print(error.as_string())
    else: print(result)

然后搞定,so 简单

相关推荐
明月与玄武2 小时前
Python编程的真谛:超越语法,理解编程本质
python·编程语言
Mirageef2 天前
aardio界面和控件
编程语言
Mirageef4 天前
aardio批处理脚本
编程语言
楽码4 天前
理解go指针和值传递
后端·go·编程语言
帽儿山的枪手8 天前
什么是字节流?
c语言·go·编程语言
楽码8 天前
一文看懂隐藏功能!语言的逃逸分析
后端·go·编程语言
神经星星8 天前
【TVM教程】microTVM TFLite 指南
人工智能·机器学习·编程语言
马可奥勒留8 天前
mojo🔥学习笔记——变量
编程语言
Mirageef9 天前
aardio-给控制台化妆
编程语言
楽码9 天前
一文看懂!编程语言访问变量指针和复制值
后端·go·编程语言