A practice prompt we wrote. No company or candidate report names it, so it carries no company tag.

How to answer

The happy path is a few lines of recursion, so the grade comes from structure and from what you do with bad input. Customers send malformed config and filter strings every day, and the error message is part of the product.

  1. Write the grammar down first. The prompt is spoken, so write the example as you understand it, add(1, mul(2, 3)), and confirm it isn’t (add 1 (mul 2 3)) before writing the grammar. It fits on one line of the board: expr := number | name "(" [expr ("," expr)*] ")". Then ask the questions the grammar raises: negative and decimal numbers, whitespace, case in names, which functions exist and how many arguments each takes, and whether an error is an exception or a message.
  2. Split into three stages. A tokenizer turns characters into tokens with positions. A parser turns tokens into a tree. An evaluator walks the tree. Say why: each stage can be tested alone, syntax errors stay apart from math errors, and the tree can be checked before anything runs.
  3. Keep a position on every node. The tokenizer records offsets and the tree keeps them, so a later stage (an arity check, a division by zero) can still point at the source.
  4. List malformed inputs before you code. Missing and extra parentheses, an empty argument, trailing input, an unknown function, the wrong number of arguments, division by zero, an empty string. Each error names what was expected, what was found and where. Where more than one token is valid, name them all: after an argument, a comma is as valid as a closing parenthesis.
  5. Guard the depth. Deep enough nesting raises Python’s RecursionError. Cap the depth and raise your own error, and mention the explicit-stack alternative.
  6. Choose the number type. Floats make add(0.1, 0.2) wrong in the last digit. Use Fraction if the answer must be exact; Decimal keeps decimal inputs exact but still rounds div(1, 3).

The trap is eval(): it runs arbitrary code, and when it fails you can’t say where.

Follow-ups

What the interviewer may ask next, once your first answer is on the table.

  • Add variables, so an expression can read a name from a context the caller passes in. Which stage changes?
  • The input can be nested thousands of levels deep. What breaks, and how would you parse it without recursion?
  • Add an if function that must not evaluate the branch it doesn’t take. What changes in the evaluator?
  • How would you report every error in the input instead of stopping at the first?

Where answers go wrong

  • Calls eval on the input, or rewrites the innermost call with a regex until one number is left, so it cannot say where malformed input goes wrong.
  • Evaluates while parsing, so a syntax error at the end of the input is found only after the arithmetic before it has run, and syntax errors come out mixed with math errors.

Answer this in two minutes

Write the answer you would say out loud. The clock starts with your first word.

Two minutes

Model answer

“I’m reading it as add(1, mul(2, 3)), calls with parentheses and commas. The grammar is expr := number | name '(' [expr (',' expr)*] ')'.