使用 `Lark` 库定义了一种简单编程语言的语法规则
使用 `Lark` 库定义了一种简单编程语言的语法规则
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)
这段代码实现了以下几个主要功能:
-
定义语法规则:使用
Lark库定义了一种简单编程语言的语法规则。该语法规则涵盖了变量赋值、算术运算、逻辑运算、条件语句(if-then-else)、循环语句(do-while)以及打印语句等基本编程结构。 -
解析代码:通过
Lark解析器,将输入的简单编程语言代码解析为抽象语法树(AST)。解析器使用lalr算法和basic词法分析器。 -
可视化解析树:利用
graphviz库将解析得到的抽象语法树进行可视化。draw_tree函数递归地遍历语法树的节点,并将其转换为graphviz的图形表示,最后将图形渲染为png格式的文件,并在默认图像查看器中打开。 -
解释执行代码:定义了一个
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) 解释执行抽象语法树。
综上所述,这段代码实现了一个简单编程语言的解析、可视化和解释执行的完整流程。
更多推荐
所有评论(0)