Parse and evaluate nested expressions with validation
Company: Uber
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: Medium
Interview Round: Take-home Project
Quick Answer: This question evaluates a developer's ability to design and implement a robust parser and evaluator for a parenthesized expression language, testing parsing, lexical scoping and variable binding, operator semantics, error detection, and safe integer arithmetic.
Constraints
- length(expr) <= 1e5
- maximum nesting depth <= 1e4
- integer = optional leading '-' then one or more digits
- variable = lowercase letter then zero or more lowercase letters/digits
- sum/prod require k >= 2 subexpressions; set requires exactly 3 args with a variable first
- all intermediate and final values must fit in a 32-bit signed integer; otherwise InvalidExpression
Examples
Input: ("(sum 1 2)",)
Expected Output: 3
Explanation: 1 + 2 = 3.
Input: ("(prod 2 3 4)",)
Expected Output: 24
Explanation: 2 * 3 * 4 = 24; prod accepts more than two args.
Input: ("(set x 3 (sum x 4))",)
Expected Output: 7
Explanation: Bind x = 3, then evaluate (sum x 4) = 3 + 4 = 7.
Input: ("(set x 1 (set x 2 (prod x x)))",)
Expected Output: 4
Explanation: Inner set shadows outer x with 2, so (prod x x) = 2 * 2 = 4.
Input: ("(sum -3 5)",)
Expected Output: 2
Explanation: Negative integers are valid: -3 + 5 = 2.
Input: ("(sum (prod 2 3) 4 )",)
Expected Output: 10
Explanation: Extra and trailing spaces are ignored: (prod 2 3) = 6, then 6 + 4 = 10.
Input: ("(set a 5 (set b 10 (sum a b)))",)
Expected Output: 15
Explanation: Nested bindings a = 5, b = 10, then a + b = 15.
Input: ("(sum 1 2",)
Expected Output: "InvalidExpression"
Explanation: Missing closing parenthesis.
Input: ("(sum 1)",)
Expected Output: "InvalidExpression"
Explanation: sum requires at least 2 arguments.
Input: ("(foo 1 2)",)
Expected Output: "InvalidExpression"
Explanation: Unknown operator 'foo'.
Input: ("(sum x 1)",)
Expected Output: "InvalidExpression"
Explanation: Variable x is referenced but never bound.
Input: ("(set x 1)",)
Expected Output: "InvalidExpression"
Explanation: set must have exactly 3 arguments (var, value, body); body is missing.
Input: ("",)
Expected Output: "InvalidExpression"
Explanation: Empty expression.
Input: ("(prod 2147483647 2)",)
Expected Output: "InvalidExpression"
Explanation: 2147483647 * 2 overflows the 32-bit signed range.
Input: ("(set 1 2 (sum 1 2))",)
Expected Output: "InvalidExpression"
Explanation: set's first argument must be a variable, not the integer 1.
Hints
- Tokenize first: emit '(' and ')' as standalone tokens and collapse runs of non-space, non-paren characters into atom tokens. This makes extra/leading/trailing spaces trivial to handle.
- Use recursive descent: parse() reads one expression (atom or compound); parse_compound() reads the operator then dispatches. Validate arity inside each operator branch and require the closing ')'.
- For scoping, pass an immutable copy (or a parent-linked chain) into the body of set so inner bindings shadow outer ones without mutating the caller's scope.
- Guard every add/multiply/literal with a 32-bit range check, and after the top-level parse confirm all tokens were consumed (a trailing token like an unmatched ')' means invalid).