Techniques needed to write an arithmetic expression parser
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
An arithmetic expression parser turns text like 2 + 3 * 4 into a structure a program can evaluate correctly. The hard part is not reading the characters, but preserving operator precedence, associativity, parentheses, and useful error messages.
Start With Tokenization
Before parsing, convert the raw string into tokens. This separates concerns: the tokenizer handles characters, while the parser handles grammar.
For arithmetic expressions, common token types are:
- numbers
- identifiers
- operators such as
+,-,*,/ - parentheses
A simple tokenizer in Python can walk the string one character at a time:
Without tokenization, the parser becomes cluttered with character-level details.
Encode Precedence In The Grammar
The usual technique is to split the grammar into levels. Each level handles one precedence band.
A classic grammar looks like this:
- expression: addition and subtraction
- term: multiplication and division
- factor: literals, names, unary operators, and parenthesized expressions
This shape naturally gives * and / higher precedence than + and -.
Recursive Descent Is A Practical Choice
For a small arithmetic language, recursive descent is often the best first parser. It is readable, easy to debug, and maps directly onto the grammar.
Here is a compact parser that builds an abstract syntax tree:
This parser already handles precedence, associativity, unary minus, and parentheses.
Build An AST Instead Of Evaluating Immediately
It is tempting to compute results during parsing, but an AST gives you more flexibility. You can:
- evaluate later
- optimize expressions
- generate code
- print useful debugging output
An evaluator can be a separate pass:
Separating parsing from evaluation makes the code easier to test.
Know When To Use Other Techniques
Recursive descent is excellent for simple grammars, but it is not the only option.
- Pratt parsers are great when you want a concise expression parser with many operators.
- Shunting-yard is useful if you want postfix output or stack-based evaluation.
- Parser generators are better when the grammar grows beyond a small expression language.
If your goal is only arithmetic expressions, recursive descent or Pratt parsing is usually enough.
Common Pitfalls
- Using a left-recursive grammar directly in recursive descent. That causes infinite recursion.
- Mixing tokenization and parsing in the same function. The code becomes hard to reason about.
- Forgetting unary operators. Expressions like
-5 * 2then fail or parse incorrectly. - Evaluating too early. Once parsing and execution are intertwined, later features become harder to add.
- Returning vague errors. Reporting the unexpected token and position saves a lot of debugging time.
Summary
- Tokenize first, then parse.
- Represent precedence through grammar structure such as expression, term, and factor.
- Recursive descent is a practical technique for arithmetic expressions.
- Build an AST instead of evaluating during parsing.
- Consider Pratt or shunting-yard when the operator set becomes more complex.
.png&w=3840&q=75)
Tackling System Design Interview Problems
A short course that equips you with the skills to approach system design interviews methodically.
Start the free courseInterview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.