To parse and evaluate arithmetic expressions with integers, parentheses, binary + - * /, correct precedence/associativity, and unary +/-, we can implement the shunting-yard algorithm variant that outputs RPN (or directly evaluates using operator and operand stacks). Below is a compact, robust Python implementation that handles whitespace, unary operators, integer division with truncation toward zero, and error reporting.python
import re
class EvalError(Exception):
pass
def precedence(op):
return {'+':1, '-':1, '*':2, '/':2}.get(op, 0)
def apply_op(a, b, op):
if op == '+': return a + b
if op == '-': return a - b
if op == '*': return a * b
if op == '/':
if b == 0: raise EvalError("Division by zero")
return int(a / b) # truncate toward zero
raise EvalError(f"Unknown operator {op}")
def evaluate(expr):
# Tokenize: integers, operators, parens. Skip spaces.
tokens = re.findall(r'\d+|[()+\-*/]', expr.replace(' ', ''))
if not tokens:
raise EvalError("Empty expression")
values = [] # operand stack
ops = [] # operator stack
def pop_apply():
if len(values) < 2 or not ops:
raise EvalError("Malformed expression")
b = values.pop()
a = values.pop()
values.append(apply_op(a, b, ops.pop()))
i = 0
while i < len(tokens):
tok = tokens[i]
if re.fullmatch(r'\d+', tok):
values.append(int(tok))
elif tok == '(':
ops.append(tok)
elif tok == ')':
while ops and ops[-1] != '(':
pop_apply()
if not ops or ops[-1] != '(':
raise EvalError("Mismatched parenthesis")
ops.pop()
else: # operator + - * /
# determine unary: if at start or after '(' or another operator
is_unary = (i == 0) or (tokens[i-1] in '()+-*/')
if is_unary:
# unary + is no-op; unary - is multiply by -1
if tok == '+':
# skip unary plus
i += 1
continue
elif tok == '-':
# treat unary minus as (0 - nextValue)
values.append(0)
else:
raise EvalError("Unsupported unary operator")
# handle precedence and left-associativity
while ops and ops[-1] != '(' and precedence(ops[-1]) >= precedence(tok):
pop_apply()
ops.append(tok)
i += 1
while ops:
if ops[-1] == '(' or ops[-1] == ')':
raise EvalError("Mismatched parenthesis")
pop_apply()
if len(values) != 1:
raise EvalError("Malformed expression")
return values[0]
# Example:
# print(evaluate(" -3 + 2 * (1 + 5) / 3 ")) -> -3 + 2*(6)/3 = -3 + 4 = 1
Key points:- Tokenization via regex ignores whitespace and separates numbers/operators.- Unary plus/minus handled by checking token context; unary '-' implemented by injecting 0 before it.- Operator stack enforces precedence and left associativity; apply_op does error checks (division by zero).- Time complexity: O(n) where n = token count. Space: O(n) for stacks.Edge cases:- Empty input, mismatched parentheses, malformed sequences (e.g., "1 + * 2"), division by zero, very large integers (use Python big ints or add bounds). Alternative: convert to RPN explicitly then evaluate (same complexity).