Evaluate arithmetic expression with variables
Company: Snowflake
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: Medium
Interview Round: Technical Screen
Quick Answer: This question evaluates implementation skills for a linear-time expression parser and evaluator, covering tokenization, variable substitution, operator precedence and associativity (including unary +/−), parentheses handling, integer arithmetic semantics (division truncation), error detection, and adherence to O(L) time and space constraints.
Constraints
- 0 <= len(expr) <= 10^5
- Integer operands are non-negative; the final result fits in a signed 64-bit integer (wrap on overflow).
- Variable names are non-empty alphabetic strings; values are integers.
- Division truncates toward zero (e.g. -7 / 2 = -3).
- Invalid input (unknown variable, invalid token, dangling operator, mismatched parentheses) returns the string "ERROR".
Examples
Input: ('a + b * 2', {'a': 3, 'b': 4})
Expected Output: 11
Explanation: b*2 = 8 binds before +, then a + 8 = 11 (precedence).
Input: ('(a + b) * 2', {'a': 3, 'b': 4})
Expected Output: 14
Explanation: Parentheses force (3+4)=7 first, then 7*2 = 14.
Input: ('-a + 5', {'a': 10})
Expected Output: -5
Explanation: Unary minus on the variable: -10 + 5 = -5.
Input: ('7 / 2', {})
Expected Output: 3
Explanation: Integer division truncates toward zero.
Input: ('-7 / 2', {})
Expected Output: -3
Explanation: Truncation toward zero gives -3, not the floored -4.
Input: (' 10 - 3 - 2 ', {})
Expected Output: 5
Explanation: Left-to-right: (10-3)-2 = 5; surrounding/inner spaces ignored.
Input: ('2 * (3 + 4) - 5', {})
Expected Output: 9
Explanation: 2*(7) = 14, then 14 - 5 = 9.
Input: ('(a + b', {'a': 1, 'b': 2})
Expected Output: 'ERROR'
Explanation: Mismatched parenthesis: no closing ')'.
Input: ('a + ', {'a': 1})
Expected Output: 'ERROR'
Explanation: Dangling '+' with no right-hand operand.
Input: ('x', {'a': 1})
Expected Output: 'ERROR'
Explanation: Variable 'x' is not present in the dictionary.
Input: ('+-+5', {})
Expected Output: -5
Explanation: Stacked unary operators: +(-(+5)) = -5.
Input: ('0', {})
Expected Output: 0
Explanation: Single literal, boundary case.
Hints
- Use recursive descent with three precedence levels: parse_expr handles + and -, parse_term handles * and /, and parse_factor handles unary signs, parentheses, numbers, and variables.
- Track a single index into the string and a skip_spaces helper so whitespace never reaches the operator logic.
- For truncate-toward-zero division, compute abs(a) // abs(b) and negate when the operand signs differ — Python's // floors toward negative infinity, which gives the wrong answer for negatives.
- After the top-level parse, if the index hasn't reached the end of the string the input had trailing garbage — return ERROR.