功能大升级!
这个自己实现的程序解释器已经支持
- 变量运算
- 分号间隔多条语句
- 变量未定义报错
这是从一个普通计算器到高级计算器的重大飞跃!可以参考上一篇文章比较这个版本的代码变化
程序解释器 Version 13
"""
compound_statement: (statement SEMI)*
statement: assign_statement | empty
empty :
assign_statement: variable ASSIGN expr
expr: term((PLUS|MINUS)term)*
term: factor(MUL|DIV)factor)*
factor: NUMBER| LPAR expr RPAR | (PLUS|MINUS) factor | variable
variable: ID
"""
import json
import copy
NUMBER, PLUS, MINUS, MUL, DIV, LPAR, RPAR, EOF = 'NUMBER', 'PLUS', 'MINUS', 'MUL', 'DIV', '(', ')', 'EOF'
SEMI = 'SEMI'
ASSIGN = 'ASSIGN'
ID = 'ID'
class AST:
pass
class CompoundStatement(AST):
def __init__(self, node_type, statement_list):
self.type = node_type
self.children = statement_list
def __repr__(self):
return f'{{"type":"{self.type}", "children":{self.children}}}'
__str__ = __repr__
class BinOp(AST):
def __init__(self, node_type, left_node, right_node):
self.type = node_type
self.left_node = left_node
self.right_node = right_node
def __repr__(self):
return f'{{"type":"{self.type}", "left":{self.left_node}, "right":{self.right_node}}}'
__str__ = __repr__
class UniOp(AST):
def __init__(self, node_type, child_node):
self.type = node_type
self.child_node = child_node
def __repr__(self):
return f'{{"type":"{self.type}", "child":{self.child_node}}}'
__str__ = __repr__
class Leaf(AST):
def __init__(self, node_type, value):
self.type = node_type
self.value = value
def __repr__(self):
return f'{{"type":"{self.type}", "value":{self.value}}}'
__str__ = __repr__
class Token:
def __init__(self, token_type, value):
self.type = token_type
self.value = value
def __repr__(self,):
return f'Token({self.type}, {self.value})'
class Lexer:
def __init__(self, text):
self.text = text
self.pos = 0
self.current_char = self.text[self.pos]
def copy(self,):
l = Lexer(self.text)
l.pos = self.pos
def error(self,):
raise Exception('Lexer Error!')
def advance(self):
self.pos += 1
if self.pos >= len(self.text):
self.current_char = None
else:
self.current_char = self.text[self.pos]
def skip_space(self):
if self.current_char == ' ':
while self.current_char == ' ':
self.advance()
def get_next_token(self, ):
self.skip_space()
if self.current_char is None:
return Token(EOF, None)
if self.current_char.isdigit():
number = ''
while self.current_char is not None and self.current_char.isdigit():
number += self.current_char
self.advance()
return Token(NUMBER, int(number))
if self.current_char.isalpha():
_id = ''
while self.current_char is not None and self.current_char.isalnum():
_id += self.current_char
self.advance()
return Token(ID, _id)
if self.current_char == '+':
self.advance()
return Token(PLUS, '+')
if self.current_char == '-':
self.advance()
return Token(MINUS, '-')
if self.current_char == '*':
self.advance()
return Token(MUL, '*')
if self.current_char == '/':
self.advance()
return Token(DIV, '/')
if self.current_char == '(':
self.advance()
return Token(LPAR, '(')
if self.current_char == ')':
self.advance()
return Token(RPAR, ')')
if self.current_char == ';':
self.advance()
return Token(SEMI, ';')
if self.current_char == '=':
self.advance()
return Token(ASSIGN, '=')
self.error()
class Parser:
"""
statement_list: (statement SEMI)*
statement: assign_statement | empty
empty:
assign_statement: variable ASSIGN expr
expr: term((PLUS|MINUS)term)*
term: factor(MUL|DIV)factor)*
factor: NUMBER| LPAR expr RPAR | (PLUS|MINUS) factor | variable
variable: ID
"""
def __init__(self, lexer):
self.lexer = lexer
self.current_token = self.lexer.get_next_token()
def error(self, msg = ''):
raise Exception(f'Parse Error! {msg}')
def peek(self,):
if self.current_token is None:
return None
return copy.copy(self.lexer).get_next_token()
def eat(self, token_type):
if type(token_type) == list and self.current_token.type not in token_type:
self.error(f'current token not in list, expect {token_type}, got {self.current_token}.')
elif type(token_type) == str and self.current_token.type != token_type:
self.error(f'current token not match, expect {token_type}, got {self.current_token}.')
else:
self.current_token = self.lexer.get_next_token()
def variable(self,):
f = self.current_token
self.eat(ID)
return Leaf(ID, f.value)
def factor(self,):
f = self.current_token
if f.type == NUMBER:
self.eat(NUMBER)
return Leaf(NUMBER, f.value)
elif f.type == LPAR:
self.eat(LPAR)
expr = self.expr()
self.eat(RPAR)
return expr
elif f.type in [PLUS, MINUS]:
self.eat(f.type)
return UniOp(f.type, self.factor())
else:
return self.variable()
self.error()
def term(self,):
"""
term: factor(MUL|DIV)factor)*
"""
node = self.factor()
while(self.current_token.type in [MUL, DIV]):
op = self.current_token
self.eat([MUL, DIV])
f = self.factor()
_node = BinOp(op.type, node, f)
node = _node
return node
def expr(self,):
node = self.term()
while(self.current_token.type in [PLUS, MINUS]):
op = self.current_token
self.eat([PLUS, MINUS])
t = self.term()
_node = BinOp(op.type, node, t)
node = _node
return node
def assign_statement(self,):
"""
assign_statement: VAR ASSIGN expr
"""
var = self.variable()
op = self.current_token
self.eat(ASSIGN)
expr_node = self.expr()
return BinOp(ASSIGN, var, expr_node)
def empty(self):
return Leaf('EMPTY', None)
def statement(self,):
"""
statement: assign_statement | empty
"""
if self.current_token.type == ID and self.peek().type == ASSIGN:
return self.assign_statement()
else:
return self.empty()
def compound_statement(self):
node = self.statement()
node_list = [node]
while self.current_token.type != EOF:
self.eat(SEMI)
node_list.append(self.statement())
return CompoundStatement('CompoundStatement', node_list)
def ast(self):
return self.compound_statement()
class Interpreter:
def __init__(self, parser):
self.parser = parser
self.GLOBAL_SCOPE = {}
def error(self, msg=''):
raise Exception(f'Interpeter Error! {msg}')
def compute(self):
ast = self.parser.ast()
self.visit_node(ast)
return self.GLOBAL_SCOPE
def visit_BinOp(self, node):
if node.type == PLUS:
return self.visit_node(node.left_node) + self.visit_node(node.right_node)
if node.type == MINUS:
return self.visit_node(node.left_node) - self.visit_node(node.right_node)
if node.type == MUL:
return self.visit_node(node.left_node) * self.visit_node(node.right_node)
if node.type == DIV:
return self.visit_node(node.left_node) / self.visit_node(node.right_node)
def visit_UniOp(self, node):
if node.type == PLUS:
return self.visit_node(node.child_node)
if node.type == MINUS:
return - self.visit_node(node.child_node)
def visit_statement(self, node):
if node.type == ASSIGN:
var = node.left_node
expr_result = self.visit_node(node.right_node)
self.GLOBAL_SCOPE[var.value] = expr_result
def visit_CompoundStatement(self, node):
for statement in node.children:
self.visit_statement(statement)
def visit_Leaf(self, node):
if node.type == NUMBER:
return node.value
elif node.type == ID:
if node.value in self.GLOBAL_SCOPE:
return self.GLOBAL_SCOPE[node.value]
else:
self.error(f"variable '{node.value}' not defined!")
def visit_node(self, node):
if isinstance(node, Leaf):
return self.visit_Leaf(node)
elif isinstance(node, BinOp):
return self.visit_BinOp(node)
elif isinstance(node, UniOp):
return self.visit_UniOp(node)
elif isinstance(node, CompoundStatement):
self.visit_CompoundStatement(node)
def main():
while True:
try:
text = input('cal> ')
if text:
lexer = Lexer(text)
parser = Parser(lexer)
interpreter = Interpreter(parser)
print(interpreter.compute())
print()
except Exception as e:
print(e)
print()
main()