From 25a65484bfed6eccf7af52072dd48faacd809649 Mon Sep 17 00:00:00 2001 From: Amazing Coder Date: Thu, 29 Aug 2024 20:50:38 +0800 Subject: [PATCH 1/4] v9.0-support to handle python assignment statements. --- README.md | 12 ++ ast.py => abs_syntax_tree.py | 29 ++++- assignments.txt | 5 + ast_v9.png | Bin 0 -> 40699 bytes func_test.py | 59 ++++++++++ genastdot.py | 54 ++++++--- interpreter.py | 214 +++++++++++++++++++++++++---------- sip_token.py | 22 ++++ 8 files changed, 318 insertions(+), 77 deletions(-) rename ast.py => abs_syntax_tree.py (66%) create mode 100644 assignments.txt create mode 100644 ast_v9.png create mode 100644 func_test.py create mode 100644 sip_token.py diff --git a/README.md b/README.md index 1af3c5b..26d2225 100644 --- a/README.md +++ b/README.md @@ -45,3 +45,15 @@ support unary operators (+, -) python genastdot.py "5---2" > ast.dot && dot -Tpng -o ast_v8.png ast.dot ``` ![ast_v8.png](ast_v8.png) + + +### v9.0 +support to handle python assignment statements. + +```shell +python interpreter.py assignments.txt +``` + +```shell +python genastdot.py assignments.txt > ast.dot && dot -Tpng -o ast_v9.png ast.dot +``` \ No newline at end of file diff --git a/ast.py b/abs_syntax_tree.py similarity index 66% rename from ast.py rename to abs_syntax_tree.py index 9450aa3..73d7a25 100644 --- a/ast.py +++ b/abs_syntax_tree.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- """ -@file: ast.py +@file: abs_syntax_tree.py @author: amazing coder @date: 2024/8/28 @desc: abstract-syntax tree (AST) 抽象语法树节点定义 @@ -14,7 +14,7 @@ """ -class AST(object): +class AST(object): """ ASTs represent the operator-operand model. 每一个 AST 节点都代表一个运算符和一个操作数 @@ -51,3 +51,28 @@ class UnaryOp(AST): def __init__(self, op, expr): self.token = self.op = op self.expr = expr + +class Assign(AST): + """ + 赋值运算符节点,也是非叶子节点,代表一个赋值运算符 + 比如 a = 2 这个表达式,a 是变量,2是值, 都是叶子节点,= 是赋值运算符节点 + """ + def __init__(self, left, op, right): + self.left = left + self.token = self.op = op + self.right = right + +class Var(AST): + """ + 变量节点,也是叶子节点,代表一个变量 + """ + def __init__(self, token): + self.token = token + self.value = token.value + +class NoOp(AST): + pass + +class Compound(AST): + def __init__(self): + self.children = [] \ No newline at end of file diff --git a/assignments.txt b/assignments.txt new file mode 100644 index 0000000..46895eb --- /dev/null +++ b/assignments.txt @@ -0,0 +1,5 @@ +a=1 +b=2 +c=a+b +d=a+b-c +e=45+d \ No newline at end of file diff --git a/ast_v9.png b/ast_v9.png new file mode 100644 index 0000000000000000000000000000000000000000..7089939182572ad4ad3c17f8da3419d0b232fac0 GIT binary patch literal 40699 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

wLs`AoS0YQ9fa}j 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 len(self.text) - 1: + return None + else: + return self.text[peek_pos] + def integer(self): """return a multi-digit integer""" result = '' @@ -65,6 +63,17 @@ def integer(self): self.advance() return int(result) + def identifier(self): + """return a multi-digit identifier""" + result = '' + while self.current_char is not None and self.current_char.isalnum(): + result += self.current_char + self.advance() + + if result in PYTHON_RESERVED_KEYWORDS: + return self.error() + return Token(ID, result) + def get_next_token(self): """this function breaking a sentence apart into tokens.""" while self.current_char is not None: @@ -91,6 +100,15 @@ def get_next_token(self): if self.current_char == ')': self.advance() return Token(RPAREN, ')') + if self.current_char.isalpha(): + return self.identifier() + if self.current_char == '=': + self.advance() + return Token(ASSIGN, '=') + if self.current_char == '\\' and self.peek() == 'n': + self.advance() + self.advance() + return Token(REPL, '\\n') self.error() return Token(EOF, None) @@ -125,25 +143,62 @@ def term(self): node = BinOp(left=node, op=token, right=self.factor()) return node - def factor(self): - """返回参与运算的数,支持整型或者带括号的表达式 INTEGER | LPAREN expr RPAREN | (PLUS|MINUS) factor""" + def variable(self): + node = Var(self.current_token) + self.eat(ID) + return node + + def empty(self): + return NoOp() + + def assignment_statement(self): + """ + assignment_statement : variable ASSIGN expr + """ + left = self.variable() token = self.current_token - if self.current_token.type == PLUS: - self.eat(PLUS) - return UnaryOp(op=token, expr=self.factor()) - elif self.current_token.type == MINUS: - self.eat(MINUS) - return UnaryOp(op=token, expr=self.factor()) - elif self.current_token.type == INTEGER: - self.eat(INTEGER) - return Num(token) - elif self.current_token.type == LPAREN: - self.eat(LPAREN) - node = self.expr() - self.eat(RPAREN) - return node + self.eat(ASSIGN) + right = self.expr() + node = Assign(left=left, op=token, right=right) + return node + + def statement(self): + """statement : assignment_statement | empty""" + if self.current_token.type == ID: + node = self.assignment_statement() else: - self.error() + node = self.empty() + return node + + def statements(self): + """ + statements : statement + | statement REPL statement_list + """ + node = self.statement() + results = [node] + while self.current_token.type == ID: + results.append(self.statement()) + + return results + + def compound_statement(self): + """ + compound_statement : statement_list + """ + # self.eat(REPL) + nodes = self.statements() + # self.eat(REPL) + + root = Compound() + for node in nodes: + root.children.append(node) + return root + + def program(self): + """program : compound_statement """ + node = self.compound_statement() + return node def expr(self): """表达式解析:term((PLUS|MINUS) term)* . @@ -161,8 +216,34 @@ def expr(self): node = BinOp(left=node, op=token, right=self.term()) return node + def factor(self): + """返回参与运算的数,支持整型或者带括号的表达式 INTEGER | LPAREN expr RPAREN | (PLUS|MINUS) factor | variable""" + token = self.current_token + if self.current_token.type == PLUS: + self.eat(PLUS) + return UnaryOp(op=token, expr=self.factor()) + elif self.current_token.type == MINUS: + self.eat(MINUS) + return UnaryOp(op=token, expr=self.factor()) + elif self.current_token.type == INTEGER: + self.eat(INTEGER) + return Num(token) + elif self.current_token.type == LPAREN: + self.eat(LPAREN) + node = self.expr() + self.eat(RPAREN) + return node + elif self.current_token.type == ID: + node = self.variable() + return node + else: + self.error() + def parse(self): - return self.expr() + node = self.program() + if self.current_token.type != EOF: + self.error() + return node class NodeVisitor(object): @@ -178,6 +259,7 @@ def generic_visit(self, node): class Interpreter(NodeVisitor): def __init__(self, parser): self.parser = parser + self.GLOBAL_SCOPE = {} def visit_BinOp(self, node): if node.op.type == PLUS: @@ -198,6 +280,25 @@ def visit_UnaryOp(self, node): elif node.op.type == MINUS: return -self.visit(node.expr) + def visit_Assign(self, node): + var_name = node.left.value + self.GLOBAL_SCOPE[var_name] = self.visit(node.right) + + def visit_NoOp(self, node): + pass + + def visit_Compound(self, node): + for child in node.children: + self.visit(child) + + def visit_Var(self, node): + var_name = node.value + val = self.GLOBAL_SCOPE.get(var_name) + if val is None: + raise NameError(repr(var_name)) + else: + return val + def visit(self, node): if isinstance(node, BinOp): return self.visit_BinOp(node) @@ -205,41 +306,30 @@ def visit(self, node): return self.visit_Num(node) elif isinstance(node, UnaryOp): return self.visit_UnaryOp(node) + elif isinstance(node, Var): + return self.visit_Var(node) + elif isinstance(node, Assign): + return self.visit_Assign(node) + elif isinstance(node, Compound): + return self.visit_Compound(node) + elif isinstance(node, NoOp): + return self.visit_NoOp(node) def interpret(self): tree = self.parser.parse() return self.visit(tree) -def test_unary_op(): - """ - 测试一元运算符, text=6---1 - """ - text = '5---2' - six_tok = Num(Token(INTEGER, 5)) - one_tok = Num(Token(INTEGER, 2)) - minus_tok = Token(MINUS, '-') - exp_node = BinOp(six_tok, minus_tok, UnaryOp(minus_tok, UnaryOp(minus_tok, one_tok))) - interpreter = Interpreter(None) - print(interpreter.visit(exp_node)) - - def main(): - test_unary_op() - while True: - try: - text = input('input a express like "10+2*3+16/(4+4)-(3-2)*2"(Only single digit integers are allowed in the input)> ') - except EOFError: - break - - if not text: - continue - analyzer = Analyzer(text) - parser = Parser(analyzer) - interpreter = Interpreter(parser) - print(interpreter.interpret()) + import sys + text = open(sys.argv[1], 'r').read() + print(f"begin parse input: {text}") + lexer = Analyzer(text) + parser = Parser(lexer) + interpreter = Interpreter(parser) + result = interpreter.interpret() + print(interpreter.GLOBAL_SCOPE) if __name__ == '__main__': main() - diff --git a/sip_token.py b/sip_token.py new file mode 100644 index 0000000..f29423f --- /dev/null +++ b/sip_token.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +@file: sip_token.py +@author: amazing coder +@date: 2024/8/29 +@desc: +""" +class Token(object): + def __init__(self, type, value): + self.type = type + self.value = value + + def __str__(self): + return 'Token({type}, {value})'.format( + type=self.type, + value=repr(self.value) + ) + + def __repr__(self): + return self.__str__() From 3e1ab33b1d03e44070943c8cf6b809da358250da Mon Sep 17 00:00:00 2001 From: Amazing Coder Date: Thu, 29 Aug 2024 20:58:40 +0800 Subject: [PATCH 2/4] update README.md --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 26d2225..e5f46e6 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,8 @@ # simple_interpreter (A python interpreter implemented in python.) -一个用 python 实现的简单python解释器 - -分版本逐步实现完整功能,适合初学者学习解释器的工作原理 +一个用 python 实现的简单python解释器,分版本(与分支对应)逐步实现一个简单的python解释器功能,适合初学者了解解释器的工作原理 ## 版本说明 +为了方便渐进式学习进度,每一个版本都创建了一个独立的分支,比如 v1.0版本对应的分支名为 v1.0, 该分支只实现了 v1.0 的功能,以此类推,逐步进行功能迭代。 ### v1.0 only support single-digit integers + From c08bafdebb6d9cce3942d0109a6907b0c0879177 Mon Sep 17 00:00:00 2001 From: Amazing Coder Date: Sat, 31 Aug 2024 18:05:43 +0800 Subject: [PATCH 3/4] v10.0 : handle variable not defined error --- README.md | 5 +- assignments.txt | 4 +- func_test.py | 4 +- genptdot.py | 18 ++++---- interpreter.py | 89 +++++++++++++++++++++++++++++++++--- spi_symbol.py | 68 +++++++++++++++++++++++++++ sip_token.py => spi_token.py | 2 +- 7 files changed, 169 insertions(+), 21 deletions(-) create mode 100644 spi_symbol.py rename sip_token.py => spi_token.py (95%) diff --git a/README.md b/README.md index e5f46e6..6a80c49 100644 --- a/README.md +++ b/README.md @@ -55,4 +55,7 @@ python interpreter.py assignments.txt ```shell python genastdot.py assignments.txt > ast.dot && dot -Tpng -o ast_v9.png ast.dot -``` \ No newline at end of file +``` + +### v10.0 +增加符号表记录变量的定义,以处理使用未处理的变量 diff --git a/assignments.txt b/assignments.txt index 46895eb..f5cba79 100644 --- a/assignments.txt +++ b/assignments.txt @@ -1,5 +1,5 @@ -a=1 +a=1.34 b=2 c=a+b d=a+b-c -e=45+d \ No newline at end of file +e=45+f \ No newline at end of file diff --git a/func_test.py b/func_test.py index 1c89ab0..6d0a32a 100644 --- a/func_test.py +++ b/func_test.py @@ -7,7 +7,7 @@ @date: 2024/8/29 @desc: """ -from sip_token import Token +from spi_token import Token from abs_syntax_tree import Num, BinOp, UnaryOp from interpreter import Analyzer, Parser, Interpreter, INTEGER, MINUS @@ -31,7 +31,7 @@ def test_analyzer(): print(text) analyzer = Analyzer(text) token = analyzer.get_next_token() - while token.type != 'EOF': + while token.symbol_type != 'EOF': print(token) token = analyzer.get_next_token() diff --git a/genptdot.py b/genptdot.py index 2198cd1..f4792c8 100644 --- a/genptdot.py +++ b/genptdot.py @@ -59,7 +59,7 @@ def eat(self, token_type): # type and if they match then "eat" the current token # and assign the next token to the self.current_token, # otherwise raise an exception. - if self.current_token.type == token_type: + if self.current_token.symbol_type == token_type: self.current_node.add(TokenNode(self.current_token.value)) self.current_token = self.lexer.get_next_token() else: @@ -73,9 +73,9 @@ def factor(self): self.current_node = node token = self.current_token - if token.type == INTEGER: + if token.symbol_type == INTEGER: self.eat(INTEGER) - elif token.type == LPAREN: + elif token.symbol_type == LPAREN: self.eat(LPAREN) self.expr() self.eat(RPAREN) @@ -91,11 +91,11 @@ def term(self): self.factor() - while self.current_token.type in (MUL, DIV): + while self.current_token.symbol_type in (MUL, DIV): token = self.current_token - if token.type == MUL: + if token.symbol_type == MUL: self.eat(MUL) - elif token.type == DIV: + elif token.symbol_type == DIV: self.eat(DIV) self.factor() @@ -119,11 +119,11 @@ def expr(self): self.term() - while self.current_token.type in (PLUS, MINUS): + while self.current_token.symbol_type in (PLUS, MINUS): token = self.current_token - if token.type == PLUS: + if token.symbol_type == PLUS: self.eat(PLUS) - elif token.type == MINUS: + elif token.symbol_type == MINUS: self.eat(MINUS) self.term() diff --git a/interpreter.py b/interpreter.py index 131eff9..c120f49 100644 --- a/interpreter.py +++ b/interpreter.py @@ -15,14 +15,16 @@ v7.0 : using ASTs represent the operator-operand model of arithmetic expressions. v8.0 : support unary operators (+, -) v9.0 : support to handle python assignment statements. +v10.0 : handle variable not defined error """ import keyword from abs_syntax_tree import BinOp, Num, UnaryOp, Var, NoOp, Compound, Assign -from sip_token import Token +from spi_token import Token +from spi_symbol import VarSymbol, SymbolTable -INTEGER, PLUS, EOF, MINUS, MUL, DIV, LPAREN, RPAREN, ID, ASSIGN, REPL = 'INTEGER', 'PLUS', 'EOF', 'MINUS', 'MUL', 'DIV', 'LPAREN', 'RPAREN', 'ID', 'ASSIGN', 'REPL' +INTEGER, FLOAT, PLUS, EOF, MINUS, MUL, DIV, LPAREN, RPAREN, ID, ASSIGN, REPL = 'INTEGER', 'FLOAT', 'PLUS', 'EOF', 'MINUS', 'MUL', 'DIV', 'LPAREN', 'RPAREN', 'ID', 'ASSIGN', 'REPL' PYTHON_RESERVED_KEYWORDS = {key: Token(key, key) for key in keyword.kwlist} class Analyzer(object): @@ -55,13 +57,21 @@ def peek(self): else: return self.text[peek_pos] - def integer(self): + def number(self): """return a multi-digit integer""" result = '' while self.current_char is not None and self.current_char.isdigit(): result += self.current_char self.advance() - return int(result) + if self.current_char == '.': + result += self.current_char + self.advance() + while self.current_char is not None and self.current_char.isdigit(): + result += self.current_char + self.advance() + return float(result) + else: + return int(result) def identifier(self): """return a multi-digit identifier""" @@ -81,7 +91,8 @@ def get_next_token(self): self.skip_whitespace() continue if self.current_char.isdigit(): - return Token(INTEGER, self.integer()) + number = self.number() + return Token(INTEGER, number) if isinstance(number, int) else Token(FLOAT, number) if self.current_char == '+': self.advance() return Token(PLUS, '+') @@ -228,6 +239,9 @@ def factor(self): elif self.current_token.type == INTEGER: self.eat(INTEGER) return Num(token) + elif self.current_token.type == FLOAT: + self.eat(FLOAT) + return Num(token) elif self.current_token.type == LPAREN: self.eat(LPAREN) node = self.expr() @@ -317,12 +331,75 @@ def visit(self, node): def interpret(self): tree = self.parser.parse() + symbol_builder = SymbolTableBuilder() + symbol_builder.visit(tree) return self.visit(tree) +class SymbolTableBuilder(NodeVisitor): + def __init__(self): + self.symtab = SymbolTable() + + def visit_BinOp(self, node): + self.visit(node.left) + self.visit(node.right) + + def visit_Num(self, node): + pass + + def visit_UnaryOp(self, node): + self.visit(node.expr) + + def visit_Compound(self, node): + for child in node.children: + self.visit(child) + + def visit_NoOp(self, node): + pass + + def visit_VarDecl(self, node): + type_name = node.type_node.value + type_symbol = self.symtab.lookup(type_name) + var_name = node.var_node.value + var_symbol = VarSymbol(var_name, type_symbol) + self.symtab.define(var_symbol) + + def visit_Assign(self, node): + # python代码中赋值就是定义,没有声明 + var_name = node.left.value + var_symbol = VarSymbol(var_name, None) + self.symtab.define(var_symbol) + self.visit(node.right) + + def visit_Var(self, node): + var_name = node.value + var_symbol = self.symtab.lookup(var_name) + if var_symbol is None: + raise NameError(repr(var_name)) + + def visit(self, node): + if isinstance(node, BinOp): + return self.visit_BinOp(node) + elif isinstance(node, Num): + return self.visit_Num(node) + elif isinstance(node, UnaryOp): + return self.visit_UnaryOp(node) + elif isinstance(node, Var): + return self.visit_Var(node) + elif isinstance(node, Assign): + return self.visit_Assign(node) + elif isinstance(node, Compound): + return self.visit_Compound(node) + elif isinstance(node, NoOp): + return self.visit_NoOp(node) + + + def main(): import sys - text = open(sys.argv[1], 'r').read() + py_file = sys.argv[1] + # py_file = 'assignments.txt' + text = open(py_file, 'r').read() print(f"begin parse input: {text}") lexer = Analyzer(text) parser = Parser(lexer) diff --git a/spi_symbol.py b/spi_symbol.py new file mode 100644 index 0000000..2780feb --- /dev/null +++ b/spi_symbol.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +@file: spi_symbol.py +@author: amazing coder +@date: 2024/8/31 +@desc: 增加通用符号类 +""" + +class Symbol(object): + def __int__(self, name, type=None): + self.name = name + self.symbol_type = type + + +class BuiltinTypeSymbol(Symbol): + def __init__(self, name): + super().__int__(self, name) + + def __str__(self): + return self.name + + __repr__ = __str__ + + +class VarSymbol(Symbol): + def __init__(self, name, type=None): + # python 定义时可以不指定类型 + super().__int__(name, type) + + def __str__(self): + return f'VarSymbol:name={self.name}: type={str(self.symbol_type)}' + + __repr__ = __str__ + + +class SymbolTable(object): + def __init__(self): + self._symbols = {} + + def __str__(self): + return 'Symbols: {symbols}'.format(symbols=[value for value in self._symbols.values()]) + + __repr__ = __str__ + + def define(self, symbol): + print('Define: %s' % symbol) + self._symbols[symbol.name] = symbol + return symbol + + def lookup(self, name): + print('Lookup: %s' % name) + symbol = self._symbols.get(name) + return symbol + + +def test_class(): + int_type = BuiltinTypeSymbol('INTEGER') + float_type = BuiltinTypeSymbol('FLOAT') + var_x = VarSymbol('x', int_type) + var_y = VarSymbol('y', float_type) + print(var_x) + print(var_y) + + +if __name__ == '__main__': + test_class() \ No newline at end of file diff --git a/sip_token.py b/spi_token.py similarity index 95% rename from sip_token.py rename to spi_token.py index f29423f..55cb73e 100644 --- a/sip_token.py +++ b/spi_token.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- """ -@file: sip_token.py +@file: spi_token.py @author: amazing coder @date: 2024/8/29 @desc: From 26c42e2863a892ae9b3ef496ffee2570e4234d69 Mon Sep 17 00:00:00 2001 From: Amazing Coder Date: Fri, 14 Mar 2025 15:16:43 +0800 Subject: [PATCH 4/4] format code with autopep8 --- spi_symbol.py | 7 ++++--- spi_token.py | 4 +++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/spi_symbol.py b/spi_symbol.py index 2780feb..791cd08 100644 --- a/spi_symbol.py +++ b/spi_symbol.py @@ -8,6 +8,7 @@ @desc: 增加通用符号类 """ + class Symbol(object): def __int__(self, name, type=None): self.name = name @@ -16,7 +17,7 @@ def __int__(self, name, type=None): class BuiltinTypeSymbol(Symbol): def __init__(self, name): - super().__int__(self, name) + super().__int__(name) def __str__(self): return self.name @@ -30,7 +31,7 @@ def __init__(self, name, type=None): super().__int__(name, type) def __str__(self): - return f'VarSymbol:name={self.name}: type={str(self.symbol_type)}' + return f'VarSymbol:name={self.name}: type={self.symbol_type}' __repr__ = __str__ @@ -65,4 +66,4 @@ def test_class(): if __name__ == '__main__': - test_class() \ No newline at end of file + test_class() diff --git a/spi_token.py b/spi_token.py index 55cb73e..87bada4 100644 --- a/spi_token.py +++ b/spi_token.py @@ -7,6 +7,8 @@ @date: 2024/8/29 @desc: """ + + class Token(object): def __init__(self, type, value): self.type = type @@ -19,4 +21,4 @@ def __str__(self): ) def __repr__(self): - return self.__str__() + return self.__str__()