Python Expression Evaluator
This challenge involves building a robust expression evaluator in Python that can parse and compute the results of mathematical expressions. This is a fundamental programming task with applications ranging from simple calculators to more complex scientific computing tools and scripting languages.
Problem Description
Your task is to create a Python function, evaluate_expression(expression_string), that takes a string representing a mathematical expression and returns its computed numerical value. The evaluator should support basic arithmetic operations: addition (+), subtraction (-), multiplication (*), and division (/). It should also handle parentheses for controlling the order of operations and support integer and floating-point numbers.
Key Requirements:
- Supported Operations:
+,-,*,/. - Operator Precedence: Multiplication and division should be evaluated before addition and subtraction.
- Parentheses: Expressions within parentheses should be evaluated first. Nested parentheses are allowed.
- Number Types: Support both integers and floating-point numbers.
- Whitespace: Ignore whitespace characters (spaces, tabs) within the expression string.
- Error Handling: The function should gracefully handle invalid expressions (e.g., syntax errors, division by zero) by raising appropriate exceptions.
Expected Behavior:
- The function should return a numerical type (int or float) representing the evaluated result.
- For division, standard floating-point division should be used.
Edge Cases to Consider:
- Expressions with only numbers.
- Expressions with only operators (invalid).
- Expressions starting or ending with operators (invalid).
- Division by zero.
- Unbalanced parentheses.
- Invalid characters in the expression.
Examples
Example 1:
Input: "3 + 5 * (2 - 8) / 2"
Output: -12.0
Explanation:
1. Parentheses first: (2 - 8) = -6
2. Expression becomes: "3 + 5 * -6 / 2"
3. Multiplication/Division (left to right):
5 * -6 = -30
-30 / 2 = -15.0
4. Expression becomes: "3 + -15.0"
5. Addition: 3 + -15.0 = -12.0
Example 2:
Input: "10 / 2 + 3 * (4 - 1)"
Output: 14.0
Explanation:
1. Parentheses first: (4 - 1) = 3
2. Expression becomes: "10 / 2 + 3 * 3"
3. Multiplication/Division (left to right):
10 / 2 = 5.0
3 * 3 = 9
4. Expression becomes: "5.0 + 9"
5. Addition: 5.0 + 9 = 14.0
Example 3:
Input: "((5 + 3) * 2) - 10"
Output: 6.0
Explanation:
1. Innermost parentheses: (5 + 3) = 8
2. Expression becomes: "(8 * 2) - 10"
3. Next parentheses: (8 * 2) = 16
4. Expression becomes: "16 - 10"
5. Subtraction: 16 - 10 = 6.0
Example 4 (Error Case):
Input: "5 / 0"
Output: Raises ZeroDivisionError
Explanation: Attempting to divide by zero.
Example 5 (Error Case):
Input: "3 + (5 - 2"
Output: Raises ValueError (or similar, indicating unbalanced parentheses)
Explanation: Mismatched parentheses.
Constraints
- The input
expression_stringwill be a string. - The expression will only contain digits (
0-9), decimal points (.), operators (+,-,*,/), parentheses ((,)), and whitespace. - Numbers will be valid positive or negative integers or floating-point numbers.
- The maximum nesting depth of parentheses will not exceed 100.
- The length of the expression string will not exceed 1000 characters.
- The evaluator should aim for reasonable performance, avoiding brute-force or overly inefficient algorithms for typical expression lengths.
Notes
- This problem is a classic application of parsing techniques, often solved using stacks (e.g., Shunting-Yard algorithm for conversion to Reverse Polish Notation, or directly evaluating using two stacks).
- Consider how you will handle operator precedence and associativity.
- Thoroughly test your error handling for various invalid inputs.
- You might find it helpful to first tokenize the expression string into a list of numbers and operators.