造个轮子用Python写个编程语言判断与循环

2023年 8月 21日 75.0k 0

前言

okey,很好,在上一篇文章当中,我们实现了这个基本的逻辑运算,所以的话,在这里,我们将可以实现到我们的这个判断和循环了。由于这里的话,我们的操作其实和先前的操作都是类似的,所以在代码层面上的操作非常,简单。但是在语法层面上面看起来有点抽象。

那么当然先看到这里实现的效果:
在这里插入图片描述

这里的话,没办法,我觉得如果使用中文的话,实在是太别扭了,所以换回来了,对应的报错也是换回来了,但是相关的提示后面还是会做成中文的,但是这个翻译为语义的话,实在是太麻烦了。

所以在这里,我们再来看看这个语法表述。

语法描述

首先,我们要做的很简单,就是实现这个判断语句 if else 又或者 if elif else 这样的结构。当然我们还有循环要实现,但是这里的话我们先来看到这个判断。

判断

首先我们明白一个道理,那就是这个IF里面的这个条件的话,可以有非常多的内容,也就是说,这个IF里面包含了这个布尔运算,而这个布尔运算的话,又包含了表达式(然后这个表达式包含了很多信息)并且这个IF这样的运算应该是要放在后面进行判断的。也就是说,IF 作为一个节点在运行的时候,是放在后面的。

还记得,我们一开始举的例子吗:
在这里插入图片描述
在解析成AST的时候,其实这个 IF 就是其中一个节点,他也是一样没有任何区别。只是,这个节点的话,有明显的顺序要求,要先IF,然后有ElSE或者ELIF。

循环

那么同样的,循环也是一样的,只是在循环这边会有跟多的操作而已,这里没有什么不同。当然 具体的,在代码的代码实现里面可以看到非常的清晰明了。

之后的话,我们来看到我们现在的语法的表示:

((KEYWORD:AND|KEYWORD:OR) comp-expr)*

comp-expr	: NOT comp-expr 			: arith-expr ((EE|LT|GT|LTE|GTE)
arith-expr)*

arith-expr	: term ((PLUS|MINUS) term)*

term		: factor ((MUL|DIV) factor)*

factor		: (PLUS|MINUS) factor 			: power

power		: atom (POW factor)*

atom 		: INT|FLOAT|IDENTIFIER 			: LPAREN expr RPAREN 			: if-expr
			: for-expr 			: while-expr

if-expr		: KEYWORD:IF expr KEYWORD:THEN expr
			  (KEYWORD:ELIF expr KEYWORD:THEN expr)*
			  (KEYWORD:ELSE expr)?

for-expr	: KEYWORD:FOR IDENTIFIER EQ expr KEYWORD:TO expr 
			  (KEYWORD:STEP expr)? KEYWORD:THEN expr

while-expr	: KEYWORD:WHILE expr KEYWORD:THEN expr

是的在这里的话,我们的循环有两个

词法解析

那么同样的老规矩了,不管要怎么实现,第一步都是需要先处理这个。
那么先定义关键词:

KEYWORDS = [
    'var',
    'and',
    'or',
    'not',
    'if',
    'elif',
    'else',
    'for',
    'to',
    'step',
    'while',
    'then'
]

因为这块的话,只要做关键词配对就ok了,所以的话,我们这里在词法解析这部分的代码不用动。

语法解析

虽然在词法解析这里我们什么都不用动,但是在这边语法解析这里还是要处理的。这个时候的话,我们这里的语法解析已经写的很复杂了。

定义节点

那么在这里的话,我们定义了三个节点:

IfNode 类表示 if 语句的节点。它具有以下属性和方法:

cases:表示 if 语句的条件和对应的代码块的列表。
else_case:表示 if 语句的 else 分支的代码块(可选)。
pos_start:表示节点在源代码中的起始位置,为第一个条件的起始位置。
pos_end:表示节点在源代码中的结束位置,为 else 分支的结束位置(如果存在),否则为最后一个条件的结束位置。

ForNode 类表示 for 循环的节点。它具有以下属性和方法:

var_name_tok:表示循环变量的令牌(token)对象。
start_value_node:表示循环变量的起始值的节点对象。
end_value_node:表示循环变量的结束值的节点对象。
step_value_node:表示循环变量的步长值的节点对象。
body_node:表示循环体的节点对象。
pos_start:表示节点在源代码中的起始位置,为变量名的起始位置。
pos_end:表示节点在源代码中的结束位置,为循环体的结束位置。

WhileNode 类表示 while 循环的节点。它具有以下属性和方法:

condition_node:表示循环条件的节点对象。
body_node:表示循环体的节点对象。
pos_start:表示节点在源代码中的起始位置,为条件的起始位置。
pos_end:表示节点在源代码中的结束位置,为循环体的结束位置。

class IfNode:
    def __init__(self, cases, else_case):
        self.cases = cases
        self.else_case = else_case
    
        self.pos_start = self.cases[0][0].pos_start
        self.pos_end = (self.else_case or self.cases[len(self.cases) - 1][0]).pos_end

class ForNode:
    def __init__(self, var_name_tok, start_value_node, end_value_node, step_value_node, body_node):
        self.var_name_tok = var_name_tok
        self.start_value_node = start_value_node
        self.end_value_node = end_value_node
        self.step_value_node = step_value_node
        self.body_node = body_node
    
        self.pos_start = self.var_name_tok.pos_start
        self.pos_end = self.body_node.pos_end

class WhileNode:
    def __init__(self, condition_node, body_node):
        self.condition_node = condition_node
        self.body_node = body_node
    
        self.pos_start = self.condition_node.pos_start
        self.pos_end = self.body_node.pos_end

生成节点

然后的话,我们要做的就是生成这个节点,那么这个节点的话,就是这样处理:

首先我们这里还是有三个方法:
在这里插入图片描述

判断节点

ok,这里的话,我们一个一个来看到这个节点,我们先来看到这个判断节点的处理。首先的话,这个if,节点必然有一个then节点。所以这里的问题,在于我们还要去判断这个闭合。

我们先直接看到代码:

  res = ParseResult()
        cases = []
        else_case = None
		
		#s首先我们看到这个节点是不是if节点
        if not self.current_tok.matches(TT_KEYWORD, 'if'):
            return res.failure(InvalidSyntaxError(
                self.current_tok.pos_start, self.current_tok.pos_end,
                f"Expected 'if'"
            ))

        res.register_advancement()
        self.advance()
		#节点内部的情况节点
		#  if
		# /
		# condition
        condition = res.register(self.expr())
        if res.error: return res
		#步入到下一个节点,由于前面解析condition的时候,我们也是会移动我们的token指针的
		#所以当我们结束这个condition的组合的时候,回到当前的if节点,那么下一个节点如果没有
		#then,那么就错了
        if not self.current_tok.matches(TT_KEYWORD, 'then'):
            return res.failure(InvalidSyntaxError(
                self.current_tok.pos_start, self.current_tok.pos_end,
                f"Expected 'then'"
            ))
		
		#接下来的操作同理
        res.register_advancement()
        self.advance()

        expr = res.register(self.expr())
        if res.error: return res
        cases.append((condition, expr))

        while self.current_tok.matches(TT_KEYWORD, 'elif'):
            res.register_advancement()
            self.advance()

            condition = res.register(self.expr())
            if res.error: return res

            if not self.current_tok.matches(TT_KEYWORD, 'then'):
                return res.failure(InvalidSyntaxError(
                    self.current_tok.pos_start, self.current_tok.pos_end,
                    f"Expected 'then'"
                ))

            res.register_advancement()
            self.advance()

            expr = res.register(self.expr())
            if res.error: return res
            cases.append((condition, expr))

        if self.current_tok.matches(TT_KEYWORD, 'else'):
            res.register_advancement()
            self.advance()

            else_case = res.register(self.expr())
            if res.error: return res

        return res.success(IfNode(cases, else_case))

循环节点

之后的话,是我们的循环节点,这里的原理都是类似的:

for_expr 方法首先判断当前令牌是否为关键字 'for',如果不是则抛出语法错误。然后解析变量名和等号,获取循环变量的起始值。接下来判断当前令牌是否为关键字 'to',如果不是则抛出语法错误。然后解析结束值。如果当前令牌是关键字 'step',则解析步长值;否则步长值设为 None。最后判断当前令牌是否为关键字 'then',如果不是则抛出语法错误。解析循环体的表达式并返回生成的 ForNode 对象。

while_expr 方法首先判断当前令牌是否为关键字 'while',如果不是则抛出语法错误。解析循环条件的表达式。然后判断当前令牌是否为关键字 'then',如果不是则抛出语法错误。解析循环体的表达式并返回生成的 WhileNode 对象。

    def for_expr(self):
		"""
		for i=1 to 6 step 1 then ...
		这个是我们的这个for循环的结构,所以我们解析的时候
		我们要按照这个去处理
		先解析 内部的这个i=x
		然后是 to
		然后是 step
		之后是then
		"""
        res = ParseResult()

        if not self.current_tok.matches(TT_KEYWORD, 'for'):
            return res.failure(InvalidSyntaxError(
                self.current_tok.pos_start, self.current_tok.pos_end,
                f"Expected 'for'"
            ))

        res.register_advancement()
        self.advance()

        if self.current_tok.type != TT_IDENTIFIER:
            return res.failure(InvalidSyntaxError(
                self.current_tok.pos_start, self.current_tok.pos_end,
                f"Expected identifier"
            ))

        var_name = self.current_tok
        res.register_advancement()
        self.advance()

        if self.current_tok.type != TT_EQ:
            return res.failure(InvalidSyntaxError(
                self.current_tok.pos_start, self.current_tok.pos_end,
                f"Expected '='"
            ))

        res.register_advancement()
        self.advance()

        start_value = res.register(self.expr())
        if res.error: return res

        if not self.current_tok.matches(TT_KEYWORD, 'to'):
            return res.failure(InvalidSyntaxError(
                self.current_tok.pos_start, self.current_tok.pos_end,
                f"Expected 'to'"
            ))

        res.register_advancement()
        self.advance()

        end_value = res.register(self.expr())
        if res.error: return res

        if self.current_tok.matches(TT_KEYWORD, 'step'):
            res.register_advancement()
            self.advance()

            step_value = res.register(self.expr())
            if res.error: return res
        else:
            step_value = None

        if not self.current_tok.matches(TT_KEYWORD, 'then'):
            return res.failure(InvalidSyntaxError(
                self.current_tok.pos_start, self.current_tok.pos_end,
                f"Expected 'then'"
            ))

        res.register_advancement()
        self.advance()

        body = res.register(self.expr())
        if res.error: return res

        return res.success(ForNode(var_name, start_value, end_value, step_value, body))

    def while_expr(self):
		"""
		这个也是类似先是 while() then 处理
		"""
        res = ParseResult()

        if not self.current_tok.matches(TT_KEYWORD, 'while'):
            return res.failure(InvalidSyntaxError(
                self.current_tok.pos_start, self.current_tok.pos_end,
                f"Expected 'while'"
            ))

        res.register_advancement()
        self.advance()

        condition = res.register(self.expr())
        if res.error: return res

        if not self.current_tok.matches(TT_KEYWORD, 'then'):
            return res.failure(InvalidSyntaxError(
                self.current_tok.pos_start, self.current_tok.pos_end,
                f"Expected 'then'"
            ))

        res.register_advancement()
        self.advance()

        body = res.register(self.expr())
        if res.error: return res

        return res.success(WhileNode(condition, body))

解释器

okey,之后的话,就是我们解释器的内容了。这里的话我们的大体执行流程还是和以前一样的,但是区别的话,就是,这里我们要维护的信息更多了。因为先前AST已经构造了了,我们只要判断当前节点的类型,然后执行对应的操作,是结合左右子树,还是什么什么。

但是在这里的话,我们在这边的操作现在变得复杂了一点。

处理判断节点

首先的的话,我们这里直接看到我们的这个函数:

    def visit_IfNode(self, node, context):
        res = RTResult()
        for condition, expr in node.cases:
            condition_value = res.register(self.visit(condition, context))
            if res.error: return res

            if condition_value.is_true():
                expr_value = res.register(self.visit(expr, context))
                if res.error: return res
                return res.success(expr_value)

        if node.else_case:
            else_value = res.register(self.visit(node.else_case, context))
            if res.error: return res
            return res.success(else_value)

        return res.success(None)

现在对Number这个家伙的话,我们又加入了一个方法,is_true,这个方法没啥。
我们这边不是有判断嘛:
在这里插入图片描述
没啥东西。

循环处理

这个循环处理的话,就稍微麻烦一点,这里的话我们主要看到for循环

 def visit_ForNode(self, node, context):
        res = RTResult()
		
		"""
		这个下面的这个是我们的for循环,所以的话
		for i=1 to 6 step 1 then ...
		"""
        start_value = res.register(self.visit(node.start_value_node, context))
        if res.error: return res

        end_value = res.register(self.visit(node.end_value_node, context))
        if res.error: return res

        if node.step_value_node:
            step_value = res.register(self.visit(node.step_value_node, context))
            if res.error: return res
        else:
            step_value = Number(1)

        i = start_value.value

        if step_value.value >= 0:
            condition = lambda: i  end_value.value

        while condition():
        	#循环处理里面的结果,然后把结果存起来
            context.symbol_table.set(node.var_name_tok.value, Number(i))
            i += step_value.value

            res.register(self.visit(node.body_node, context))
            if res.error: return res

        return res.success(None)

之后的话,这个while也是一样的。

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论