from graphviz import Digraph
from lark import Lark, Tree, Token
from lark import Transformer, v_args
from lark.visitors import Interpreter
import os
grammar = """
?start: (stmt _NL?)*

?stmt: PRINT expr                          -> print_stmt
     | NAME EQUAL expr                     -> assign_stmt
     | NAME PLUS_EQUAL expr                -> add_assign_stmt
     | NAME MINUS_EQUAL expr               -> subtract_assign_stmt
     | IF expr THEN stmt+ ELSE stmt+ END   -> if_stmt
     | DO stmt+ END WHILE expr             -> do_while_stmt  # 确保 'do-while' 循环有 'END'
     | expr                                -> expr_stmt
     | _NL                                 -> newline  // 新增空行处理

?expr: or_expr

?or_expr: and_expr
        | or_expr "or" and_expr            -> logical_or

?and_expr: equality_expr
         | and_expr "and" equality_expr    -> logical_and

?equality_expr: comparison_expr
              | equality_expr EQ comparison_expr  -> eq
              | equality_expr NE comparison_expr  -> ne

?comparison_expr: term
                | comparison_expr GT term                  -> gt
                | comparison_expr GE term                  -> ge
                | comparison_expr LE term                  -> le
                | comparison_expr LT term                  -> lt

?term: factor
     | term "*" factor               -> mul
     | term "/" factor               -> div
     | term "%" factor               -> mod
     | term "**" factor              -> pow

?factor: NUMBER                      
       | NAME                        -> var
       | "(" expr ")"                -> grouped_expr

%import common.CNAME -> NAME
%import common.NUMBER
%import common.WS
%import common.NEWLINE -> _NL  // 直接使用 common.NEWLINE 并重命名为 _NL

// 忽略空白和注释
%ignore WS
%ignore /#.*/

PRINT: "print"
EQUAL: "="
PLUS_EQUAL: "+="
MINUS_EQUAL: "-="
IF: "if"
THEN: "then"
ELSE: "else"
END: "end"
DO: "do"
WHILE: "while"
GT: ">"
GE: ">="
EQ: "=="
NE: "!="
LE: "<="
LT: "<"
MOD: "%"
POW: "**"
"""
class SimpleLangInterpreter(Interpreter):
    def __init__(self):
        super().__init__()
        self.variables = {}

    def visit(self, tree_or_token):
        if isinstance(tree_or_token, Tree):
            return super().visit(tree_or_token)
        elif isinstance(tree_or_token, Token):
            return self.visit_token(tree_or_token)
        else:
            raise ValueError(f"Unexpected type: {type(tree_or_token)}")

    def visit_token(self, token):
        if token.type == "NUMBER":
            return int(token.value)
        elif token.type == "NAME":
            name = str(token.value)
            if name in self.variables:
                return self.variables[name]
            else:
                raise ValueError(f"Undefined variable '{name}'")
        elif token.type == "BOOLEAN":  
            return token.value == "True"
        else:
            raise ValueError(f"Unrecognized token type: {token.type}")

    def assign_stmt(self, tree):
        name = str(tree.children[0])
        value = self.visit(tree.children[2])
        self.variables[name] = value

    def add_assign_stmt(self, tree):
        name = str(tree.children[0])
        if name in self.variables:
            current_value = self.variables[name]
            right_value = self.visit(tree.children[2])
            new_value = current_value + right_value
            self.variables[name] = new_value
        else:
            raise ValueError(f"Undefined variable '{name}'")

    def subtract_assign_stmt(self, tree):
        name = str(tree.children[0])
        if name in self.variables:
            current_value = self.variables[name]
            right_value = self.visit(tree.children[2])
            new_value = current_value - right_value
            self.variables[name] = new_value
        else:
            raise ValueError(f"Undefined variable '{name}'")

    def print_stmt(self, tree):
        value = self.visit(tree.children[1])
        print(value)

    def if_stmt(self, tree):
        condition = self.visit(tree.children[1])
        if condition:
            self.visit_children(tree.children[3])
        else:
            self.visit_children(tree.children[5])

    def do_while_stmt(self, tree):
        while True:
            for child in tree.children[1:-1]:
                if isinstance(child, Tree):
                    self.visit(child)
            condition = self.visit(tree.children[-1])
            if not condition:
                break

    def logical_or(self, tree):
        left = self.visit(tree.children[0])
        right = self.visit(tree.children[2])
        return left or right

    def logical_and(self, tree):
        left = self.visit(tree.children[0])
        right = self.visit(tree.children[2])
        return left and right

    def eq(self, tree):
        left = self.visit(tree.children[0])
        right = self.visit(tree.children[2])
        return left == right

    def ne(self, tree):
        left = self.visit(tree.children[0])
        right = self.visit(tree.children[2])
        return left != right

    def le(self, tree):
        left = self.visit(tree.children[0])
        right = self.visit(tree.children[2])
        return left <= right

    def lt(self, tree):
        left = self.visit(tree.children[0])
        right = self.visit(tree.children[2])
        return left < right

    def ge(self, tree):
        left = self.visit(tree.children[0])
        right = self.visit(tree.children[2])
        return left >= right

    def gt(self, tree):
        left = self.visit(tree.children[0])
        right = self.visit(tree.children[2])
        return left > right

    def add(self, tree):
        left = self.visit(tree.children[0])
        right = self.visit(tree.children[1])
        return left + right

    def sub(self, tree):
        left = self.visit(tree.children[0])
        right = self.visit(tree.children[1])
        return left - right

    def mul(self, tree):
        left = self.visit(tree.children[0])
        right = self.visit(tree.children[1])
        return left * right

    def div(self, tree):
        left = self.visit(tree.children[0])
        right = self.visit(tree.children[1])
        return left / right

    def mod(self, tree):
        left = self.visit(tree.children[0])
        right = self.visit(tree.children[1])
        return left % right

    def pow(self, tree):
        left = self.visit(tree.children[0])
        right = self.visit(tree.children[1])
        return left ** right

    def var(self, tree):
        name = str(tree.children[0])
        if name in self.variables:
            return self.variables[name]
        else:
            raise ValueError(f"Undefined variable '{name}'")

    def expr_stmt(self, tree):
        result = self.visit(tree.children[0])
        return result


code = """
x = 10
y = 5
z = 2

print x / y 
print x ** y  
print x % y  

if x <= y then
 print x
else
 print y
end

i = 0
do
  print i
  i += 1
end  
while i < 5 
"""

parser = Lark(grammar, parser='lalr', lexer='basic')
parsed_tree = parser.parse(code)

# 可视化解析树
def draw_tree(tree, graph, parent=None, count=0):
    node_id = str(count)
    label = tree.data if isinstance(tree, Tree) else str(tree)
    graph.node(node_id, label)

    if parent is not None:
        graph.edge(parent, node_id)

    count += 1
    for child in tree.children:
        if isinstance(child, Tree):
            count = draw_tree(child, graph, node_id, count)
        elif isinstance(child, Token):
            leaf_id = str(count)
            graph.node(leaf_id, str(child))
            graph.edge(node_id, leaf_id)
            count += 1
    return count

def visualize_tree(tree):
    graph = Digraph(format="png")
    draw_tree(tree, graph)
    return graph

os.environ["PATH"] += os.pathsep + "/usr/local/bin"
tree_graph = visualize_tree(parsed_tree)
tree_graph.render('simple_lang_tree_demo_exp1')
tree_graph.view()

# 解释并执行代码
interpreter = SimpleLangInterpreter()
interpreter.visit(parsed_tree)


这段代码实现了以下几个主要功能:

  1. 定义语法规则:使用 Lark 库定义了一种简单编程语言的语法规则。该语法规则涵盖了变量赋值、算术运算、逻辑运算、条件语句(if-then-else)、循环语句(do-while)以及打印语句等基本编程结构。

  2. 解析代码:通过 Lark 解析器,将输入的简单编程语言代码解析为抽象语法树(AST)。解析器使用 lalr 算法和 basic 词法分析器。

  3. 可视化解析树:利用 graphviz 库将解析得到的抽象语法树进行可视化。draw_tree 函数递归地遍历语法树的节点,并将其转换为 graphviz 的图形表示,最后将图形渲染为 png 格式的文件,并在默认图像查看器中打开。

  4. 解释执行代码:定义了一个 SimpleLangInterpreter 类,继承自 Interpreter,实现了对解析后的抽象语法树的解释执行。该类包含了对各种语法结构(如赋值语句、条件语句、循环语句等)的具体解释逻辑,并在执行过程中维护一个变量字典来存储变量的值。

以下是对代码的详细分析:

语法定义

grammar = """
?start: (stmt _NL?)*

?stmt: PRINT expr                          -> print_stmt
     | NAME EQUAL expr                     -> assign_stmt
     | NAME PLUS_EQUAL expr                -> add_assign_stmt
     | NAME MINUS_EQUAL expr               -> subtract_assign_stmt
     | IF expr THEN stmt+ ELSE stmt+ END   -> if_stmt
     | DO stmt+ END WHILE expr             -> do_while_stmt  # 确保 'do-while' 循环有 'END'
     | expr                                -> expr_stmt
     | _NL                                 -> newline  // 新增空行处理
...

这段代码定义了简单语言的语法规则,包括语句(stmt)、表达式(expr)以及各种操作符和关键字。

解析代码

parser = Lark(grammar, parser='lalr', lexer='basic')
parsed_tree = parser.parse(code)

这部分代码创建了一个 Lark 解析器,并使用它解析输入的代码,得到抽象语法树 parsed_tree

可视化解析树

def draw_tree(tree, graph, parent=None, count=0):
    node_id = str(count)
    label = tree.data if isinstance(tree, Tree) else str(tree)
    graph.node(node_id, label)
  ...
    return count

def visualize_tree(tree):
    graph = Digraph(format="png")
    draw_tree(tree, graph)
    return graph

os.environ["PATH"] += os.pathsep + "/usr/local/bin"
tree_graph = visualize_tree(parsed_tree)
tree_graph.render('simple_lang_tree_demo_exp1')
tree_graph.view()

draw_tree 函数递归地遍历语法树,将每个节点添加到 graphviz 的图形对象中。visualize_tree 函数创建一个图形对象,并调用 draw_tree 函数生成可视化图形,最后将图形渲染并查看。

解释执行代码

class SimpleLangInterpreter(Interpreter):
    def __init__(self):
        super().__init__()
        self.variables = {}
  ...
    def visit(self, tree_or_token):
      ...
    def assign_stmt(self, tree):
      ...
    def add_assign_stmt(self, tree):
      ...
    def subtract_assign_stmt(self, tree):
      ...
    def print_stmt(self, tree):
      ...
    def if_stmt(self, tree):
      ...
    def do_while_stmt(self, tree):
      ...
    def logical_or(self, tree):
      ...
    def logical_and(self, tree):
      ...
    def eq(self, tree):
      ...
    def ne(self, tree):
      ...
    def le(self, tree):
      ...
    def lt(self, tree):
      ...
    def ge(self, tree):
      ...
    def gt(self, tree):
      ...
    def add(self, tree):
      ...
    def sub(self, tree):
      ...
    def mul(self, tree):
      ...
    def div(self, tree):
      ...
    def mod(self, tree):
      ...
    def pow(self, tree):
      ...
    def var(self, tree):
      ...
    def expr_stmt(self, tree):
      ...

interpreter = SimpleLangInterpreter()
interpreter.visit(parsed_tree)

SimpleLangInterpreter 类继承自 Interpreter,实现了对各种语法结构的解释逻辑。visit 方法根据节点类型调用相应的处理方法,如 assign_stmt 处理赋值语句,if_stmt 处理条件语句等。最后,通过 interpreter.visit(parsed_tree) 解释执行抽象语法树。

综上所述,这段代码实现了一个简单编程语言的解析、可视化和解释执行的完整流程。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐