import ast import sys class ExpressionEvaluator(ast.NodeVisitor): """ A NodeVisitor that evaluates an AST expression and prints the evaluation sequence of its subexpressions. """ def __init__(self): self.results = [] def visit_BinOp(self, node): """Handles binary operations (e.g., +, -, *, /, %, **, //, &, |, ^, <<, >>).""" self.visit(node.left) left_val = self.results[-1][1] self.visit(node.right) right_val = self.results[-1][1] op_symbol = { ast.Add: '+', ast.Sub: '-', ast.Mult: '*', ast.Div: '/', ast.Mod: '%', ast.Pow: '**', ast.FloorDiv: '//', ast.BitAnd: '&', ast.BitOr: '|', ast.BitXor: '^', ast.LShift: '<<', ast.RShift: '>>' }.get(type(node.op), 'UnknownOp') sub_expression = f"{left_val} {op_symbol} {right_val}" result = eval(sub_expression) # Evaluate the sub-expression self.results.append((sub_expression, result)) return result def visit_UnaryOp(self, node): """Handles unary operations (e.g., -, ~).""" self.visit(node.operand) operand_val = self.results[-1][1] op_symbol = { ast.USub: '-', ast.UAdd: '+', ast.Invert: '~' }.get(type(node.op), 'UnknownUnaryOp') sub_expression = f"{op_symbol}{operand_val}" result = eval(sub_expression) self.results.append((sub_expression, result)) return result def visit_Compare(self, node): """Handles comparison operations (e.g., ==, !=, <, <=, >, >=).""" self.visit(node.left) left_val = self.results[-1][1] comparison_results = [] for i, comparator in enumerate(node.comparators): self.visit(comparator) right_val = self.results[-1][1] op_symbol = { ast.Eq: '==', ast.NotEq: '!=', ast.Lt: '<', ast.LtE: '<=', ast.Gt: '>', ast.GtE: '>=' }.get(type(node.ops[i]), 'UnknownCompOp') sub_expression = f"{left_val} {op_symbol} {right_val}" result = eval(sub_expression) self.results.append((sub_expression, result)) comparison_results.append(result) left_val = right_val # For chained comparisons like a < b < c # Combine results for chained comparisons (e.g., 1 < 2 < 3) final_result = all(comparison_results) if comparison_results else True return final_result def visit_BoolOp(self, node): """Handles logical operations (e.g., and, or).""" operand_vals = [] for value in node.values: self.visit(value) operand_vals.append(self.results[-1][1]) op_symbol = { ast.And: 'and', ast.Or: 'or' }.get(type(node.op), 'UnknownBoolOp') sub_expression = f" {op_symbol} ".join(map(str, operand_vals)) result = eval(sub_expression) self.results.append((sub_expression, result)) return result def visit_Constant(self, node): """Handles constant values (numbers, strings, booleans, None).""" self.results.append((str(node.value), node.value)) return node.value def generic_visit(self, node): """Fallback for unhandled node types.""" if isinstance(node, ast.expr): # For expressions not explicitly handled, try to evaluate directly try: # This handles simple expressions like just a number or a string # that are not part of a larger operation. result = eval(ast.unparse(node)) self.results.append((ast.unparse(node), result)) return result except Exception: pass super().generic_visit(node) def evaluate_expression_sequence(expression): """ Parses a Python expression, evaluates its subexpressions in order, and prints the sequence of evaluations and their results. """ try: tree = ast.parse(expression, mode='eval') evaluator = ExpressionEvaluator() evaluator.visit(tree.body) print(f"Expression: {expression.rstrip()}") print("<>:") index = 1 for [sub_expr, result] in evaluator.results: print(f" [{index}] '{sub_expr}' evaluates to: {result}") index += 1 except SyntaxError as e: print(f"Error: Invalid Python expression. {e}") except NameError: print("Error: The expression contains variables, which are not allowed.") except Exception as e: print(f"An unexpected error occurred: {e}") if __name__ == "__main__": if len(sys.argv) > 1: with open(sys.argv[1], 'r') as f: lines = f.readlines() for i, line in enumerate(lines): if line.lstrip()[0] != '#': print(f"\n{'=' * 35}") evaluate_expression_sequence(line) else: while True: user_input = input("Enter a Python expression (no variables), or 'quit' to quit: ") if user_input.lower() in ['exit', 'quit']: break if user_input: evaluate_expression_sequence(user_input) print("-" * 20, end = "\n\n")