Hone logo
Hone
Problems

Python AST Transformer for Simple Expression Simplification

This challenge requires you to build a Python program that transforms an Abstract Syntax Tree (AST) of simple arithmetic expressions. The goal is to simplify expressions by evaluating constant arithmetic operations. This is a fundamental task in compilers and interpreters, allowing for code optimization and analysis.

Problem Description

You will be given a Python AST representing a simple arithmetic expression. Your task is to create a transformer that traverses this AST and replaces nodes representing constant arithmetic operations (like 2 + 3 or 5 * 10) with their evaluated results.

Key Requirements:

  • AST Traversal: You must use Python's built-in ast module to parse Python code into an AST and then traverse it.
  • Constant Folding: Identify nodes where binary operations (ast.Add, ast.Sub, ast.Mult, ast.Div) involve only constant numeric operands (integers or floats).
  • Evaluation: Evaluate these constant binary operations and replace the entire operation node with a new ast.Constant node containing the computed result.
  • Handling Non-Constants: If an operation involves non-constant operands (variables, function calls, etc.), it should remain unchanged.
  • Output: The transformer should return a modified AST. For demonstration purposes, you will then unparse this modified AST back into Python code.

Expected Behavior:

Given an expression like x + (2 * 3) + 5, the transformer should identify 2 * 3, evaluate it to 6, and replace the subtree with a Constant(6). The expression would then conceptually become x + 6 + 5. If a subsequent pass or further transformation were to occur, 6 + 5 could also be simplified. For this challenge, a single pass is sufficient.

Edge Cases to Consider:

  • Division by zero: Your evaluation logic should handle potential ZeroDivisionError exceptions. If an error occurs, the operation node should remain unchanged.
  • Floating-point arithmetic: Ensure correct handling of floating-point numbers and their operations.
  • Nested constant expressions: The transformer should be able to handle expressions like (2 + 3) * (5 - 2).

Examples

Example 1:

Input Python code:

result = 10 + 5 * 2

Input AST (simplified representation for clarity):

Module
  body:
    Assign
      targets:
        Name(id='result')
      value:
        BinOp(left=Constant(value=10), op=Add, right=BinOp(left=Constant(value=5), op=Mult, right=Constant(value=2)))

Output Python code after transformation:

result = 10 + 10

Explanation: The transformer evaluates 5 * 2 to 10 and replaces the multiplication subtree with Constant(10). The 10 + 10 part remains as is because the outer + operation isn't fully constant before the inner expression is processed. A subsequent pass would be needed to evaluate 10 + 10.

Example 2:

Input Python code:

a = 7 - (3 + 4)

Input AST (simplified representation for clarity):

Module
  body:
    Assign
      targets:
        Name(id='a')
      value:
        BinOp(left=Constant(value=7), op=Sub, right=BinOp(left=Constant(value=3), op=Add, right=Constant(value=4)))

Output Python code after transformation:

a = 7 - 7

Explanation: The transformer evaluates 3 + 4 to 7 and replaces the addition subtree with Constant(7).

Example 3: (Division by Zero)

Input Python code:

safe_division = 10 / 2
unsafe_division = 5 / 0

Input AST (simplified representation for clarity):

Module
  body:
    Assign
      targets:
        Name(id='safe_division')
      value:
        BinOp(left=Constant(value=10), op=Div, right=Constant(value=2))
    Assign
      targets:
        Name(id='unsafe_division')
      value:
        BinOp(left=Constant(value=5), op=Div, right=Constant(value=0))

Output Python code after transformation:

safe_division = 5.0
unsafe_division = 5 / 0

Explanation: 10 / 2 is evaluated to 5.0. For 5 / 0, a ZeroDivisionError would occur during evaluation, so the original BinOp node is preserved.

Constraints

  • The input will be a valid Python string containing only simple arithmetic expressions.
  • Expressions will involve integers and floats.
  • The transformer should handle ast.Add, ast.Sub, ast.Mult, and ast.Div operations.
  • You do not need to handle exponentiation (ast.Pow), modulo (ast.Mod), or bitwise operations.
  • The transformation should be performed in a single pass. If an expression results in a constant, it should be evaluated. If it results in an operation that could be further simplified by a subsequent pass (like 10 + 10), it should remain as is in this single pass.

Notes

  • The ast module is your primary tool. Familiarize yourself with ast.parse(), ast.NodeTransformer, and ast.unparse().
  • You'll likely want to override the visit_BinOp method in your ast.NodeTransformer subclass.
  • When evaluating, be mindful of the difference between integer division (//) and float division (/) if you were to extend this challenge. For this problem, only standard division (/) is relevant, which results in a float.
  • Consider how to check if an operand is a constant value (i.e., an ast.Constant node).
  • The ast.Constant node in Python 3.8+ replaces ast.Num and ast.Str. Ensure your solution is compatible with modern Python versions.
Loading editor...
python