From a0d757a5388cebff742a94e943459343388eaff1 Mon Sep 17 00:00:00 2001 From: nrubin29 Date: Sun, 8 Oct 2017 17:27:17 -0400 Subject: [PATCH] Did some cleanup. --- ast.py | 112 ++++++++++++++++++++++--------------------------- calculator.py | 5 +-- common.py | 30 ++----------- rules.py | 14 ++++++- token_value.py | 4 -- vartypes.py | 2 +- 6 files changed, 67 insertions(+), 100 deletions(-) delete mode 100644 token_value.py diff --git a/ast.py b/ast.py index c262cbb..6a18915 100644 --- a/ast.py +++ b/ast.py @@ -1,112 +1,98 @@ """ This file contains the Ast class, which represents an abstract syntax tree which can be evaluated. """ -from typing import Dict - import copy +from typing import Dict from common import RuleMatch, remove, left_assoc, Token from rules import rule_process_map, rule_process_value_map -from vartypes import Variable class Ast: - def __init__(self, ast: RuleMatch): - self.ast = self._fixed(ast) + def __init__(self, root: RuleMatch): + self.root = self._fixed(root) - def _fixed(self, ast): + def _fixed(self, node): # print('**_fixed ast', ast) - if not isinstance(ast, RuleMatch): - return ast + if not isinstance(node, RuleMatch): + return node # This removes extraneous symbols from the tree. - for i in range(len(ast.matched) - 1, -1, -1): - if ast.matched[i].name in remove: - del ast.matched[i] + for i in range(len(node.matched) - 1, -1, -1): + if node.matched[i].name in remove: + del node.matched[i] # This flattens rules with a single matched rule. - if len(ast.matched) is 1 and isinstance(ast.matched[0], RuleMatch): - return self._fixed(ast.matched[0]) + if len(node.matched) is 1 and isinstance(node.matched[0], RuleMatch): + return self._fixed(node.matched[0]) # This makes left-associative operations left-associative. for token_name, rule in left_assoc.items(): - if len(ast.matched) == 3 and ast.matched[1].name == token_name and isinstance(ast.matched[2], RuleMatch) and len(ast.matched[2].matched) == 3 and ast.matched[2].matched[1].name == token_name: - ast.matched[0] = RuleMatch(rule, [ast.matched[0], ast.matched[1], ast.matched[2].matched[0]]) - ast.matched[1] = ast.matched[2].matched[1] - ast.matched[2] = ast.matched[2].matched[2] - return self._fixed(ast) + if len(node.matched) == 3 and node.matched[1].name == token_name and isinstance(node.matched[2], RuleMatch) and len(node.matched[2].matched) == 3 and node.matched[2].matched[1].name == token_name: + node.matched[0] = RuleMatch(rule, [node.matched[0], node.matched[1], node.matched[2].matched[0]]) + node.matched[1] = node.matched[2].matched[1] + node.matched[2] = node.matched[2].matched[2] + return self._fixed(node) # This converts implicit multiplication to regular multiplication. - if ast.name == 'mui': - return self._fixed(RuleMatch('mul', [ast.matched[0], Token('MUL', '*'), ast.matched[1]])) + if node.name == 'mui': + return self._fixed(RuleMatch('mul', [node.matched[0], Token('MUL', '*'), node.matched[1]])) # This flattens matrix rows into parent matrix rows. - if ast.name == 'mrw': - for i in range(len(ast.matched) - 1, -1, -1): - if ast.matched[i].name == 'mrw': - ast.matched[i:] = ast.matched[i].matched - return self._fixed(ast) + if node.name == 'mrw': + for i in range(len(node.matched) - 1, -1, -1): + if node.matched[i].name == 'mrw': + node.matched[i:] = node.matched[i].matched + return self._fixed(node) # This flattens matrix bodies into parent matrix bodies. - if ast.name == 'mbd': - for i in range(len(ast.matched) - 1, -1, -1): - if ast.matched[i].name == 'mbd': - ast.matched[i:] = ast.matched[i].matched - return self._fixed(ast) + if node.name == 'mbd': + for i in range(len(node.matched) - 1, -1, -1): + if node.matched[i].name == 'mbd': + node.matched[i:] = node.matched[i].matched + return self._fixed(node) - if isinstance(ast, RuleMatch): - for i in range(len(ast.matched)): - ast.matched[i] = self._fixed(ast.matched[i]) + if isinstance(node, RuleMatch): + for i in range(len(node.matched)): + node.matched[i] = self._fixed(node.matched[i]) - return ast + return node def evaluate(self, vrs: Dict[str, RuleMatch]): - return self._evaluate(self.ast, vrs) + return self._evaluate(self.root, vrs) - def _evaluate(self, ast, vrs: Dict[str, RuleMatch]): - if ast.name == 'asn': - return {ast.matched[0].value: ast.matched[1]} + def _evaluate(self, node, vrs: Dict[str, RuleMatch]): + if node.name == 'asn': + return {node.matched[0].value: node.matched[1]} - for token in ast.matched: + for token in node.matched: if isinstance(token, RuleMatch) and not token.value: token.value = self._evaluate(token, vrs) - values = [token.value for token in ast.matched if isinstance(token, RuleMatch) and token.value] - tokens = [token for token in ast.matched if not isinstance(token, RuleMatch)] + values = [token.value for token in node.matched if isinstance(token, RuleMatch) and token.value] + tokens = [token for token in node.matched if not isinstance(token, RuleMatch)] - if ast.matched[0].name == 'IDT': - return self._evaluate(copy.deepcopy(vrs[ast.matched[0].value]), vrs) + if node.matched[0].name == 'IDT': + return self._evaluate(copy.deepcopy(vrs[node.matched[0].value]), vrs) - elif ast.name in rule_process_value_map: - process = rule_process_value_map[ast.name](values, tokens) + elif node.name in rule_process_value_map: + process = rule_process_value_map[node.name](values, tokens) else: - process = rule_process_map[ast.name](values, tokens[0] if len(tokens) > 0 else None) # This extra rule is part of the num hotfix. + process = rule_process_map[node.name](values, tokens[0] if len(tokens) > 0 else None) # This extra rule is part of the num hotfix. return process.value def infix(self) -> str: - # TODO: Add parentheses where needed. - return self._infix(self.ast) + # TODO: Add parentheses and missing tokens. + return self._infix(self.root) - def _infix(self, ast: RuleMatch) -> str: - return ' '.join(map(lambda t: t.value if isinstance(t, Token) else self._infix(t), ast.matched)) + def _infix(self, node: RuleMatch) -> str: + return ' '.join(map(lambda t: t.value if isinstance(t, Token) else self._infix(t), node.matched)) def __str__(self): - return self._str(self.ast) # + '\n>> ' + self.infix() + return str(self.root) # + '\n>> ' + self.infix() def __repr__(self): return str(self) - - def _str(self, ast, depth=0) -> str: - output = (('\t' * depth) + ast.name + ' = ' + str(ast.value if ast.value else None)) + '\n' - - for matched in ast.matched: - if isinstance(matched, RuleMatch) and matched.matched: - output += self._str(matched, depth + 1) - - else: - output += (('\t' * (depth + 1)) + matched.name + ': ' + matched.value) + '\n' - - return output diff --git a/calculator.py b/calculator.py index 610c4c1..5d6e655 100644 --- a/calculator.py +++ b/calculator.py @@ -6,8 +6,7 @@ import re from ast import Ast -from common import Token, token_map, rules_map, RuleMatch, Process -from token_value import Value +from common import Token, token_map, rules_map, RuleMatch, Value class Calculator: @@ -25,7 +24,7 @@ def evaluate(self, eqtn: str, verbose=True) -> Value: res = ast.evaluate(self.vrs) if isinstance(res, Value): - ast.ast.value = res + ast.root.value = res if verbose: print(ast) diff --git a/common.py b/common.py index 6dd0020..0b5700f 100644 --- a/common.py +++ b/common.py @@ -2,12 +2,11 @@ This file contains important information for the calculator. """ -from collections import OrderedDict +from collections import OrderedDict, namedtuple from typing import List -from token_value import Token -from vartypes import Number, Matrix, MatrixRow - +Token = namedtuple('Token', ('name', 'value')) +Value = namedtuple('Value', ('type', 'value')) class RuleMatch: def __init__(self, name: str, matched: List[Token]): @@ -33,23 +32,6 @@ def _str(self, ast, depth=0) -> str: return output - # def __str__(self): - # return 'RuleMatch(' + ', '.join(map(str, [self.name, self.matched])) + ')' - # - # def __repr__(self): - # return str(self) - - -class Process: - def __init__(self, operation, operands: List, raw_args=False): - self.operation = operation - self.operands = operands - - if raw_args: - self.value = operation(operands) - else: - self.value = operation(*operands) - token_map = OrderedDict(( (r'\d+(?:\.\d+)?', 'NUM'), @@ -111,9 +93,3 @@ def key_at(self, i): 'ADD': 'add', 'MUL': 'mul', } - -value_map = { - 'NUM': Number, - 'MAT': Matrix, - 'MRW': MatrixRow -} diff --git a/rules.py b/rules.py index 123c0b4..dbeff1e 100644 --- a/rules.py +++ b/rules.py @@ -3,11 +3,21 @@ """ from typing import List -from common import Token, Process -from token_value import Value +from common import Token, Value from vartypes import Number, MatrixRow, Matrix, Variable +class Process: + def __init__(self, operation, operands: List, raw_args=False): + self.operation = operation + self.operands = operands + + if raw_args: + self.value = operation(operands) + else: + self.value = operation(*operands) + + def var(_, tokens: List[Token]) -> Process: return Process(Variable.new, tokens, raw_args=True) diff --git a/token_value.py b/token_value.py deleted file mode 100644 index e6f4f67..0000000 --- a/token_value.py +++ /dev/null @@ -1,4 +0,0 @@ -from collections import namedtuple - -Token = namedtuple('Token', ('name', 'value')) -Value = namedtuple('Value', ('type', 'value')) \ No newline at end of file diff --git a/vartypes.py b/vartypes.py index 0cfe6c9..fd7a755 100644 --- a/vartypes.py +++ b/vartypes.py @@ -5,7 +5,7 @@ import copy -from token_value import Value, Token +from common import Value, Token class Type(metaclass=ABCMeta):