Expression Language Interpreter
Build an interpreter in C++ for a tiny programming language with variables, arithmetic, comparisons, if and while, and functions you can define and call, including recursive ones like factorial. The interpreter reads a source file or runs an interactive prompt: a scanner turns the text into tokens, a recursive descent parser builds a syntax tree, and a tree-walking evaluator runs it with nested scopes for variables. When something is wrong, errors point to the exact line and column with a helpful message, instead of just failing. You'll learn how programming languages work inside: scanning, grammars and parsing, syntax trees, environments and scopes, function calls, and reporting errors well, with modern C++ (variants, smart pointers) and tests.
- Estimate
- ~14.5h
- Steps
- 7
- Completed by
- 0
- Proposed by
- codeseed.app
CMake · Boost.Program_options · GoogleTest
Project roadmap
- 01
Define the language
~2hWrite LANGUAGE.md: the syntax (let x = 1; assignment; +, -, *, / and %; comparisons and and, or and not; if and else; while; fn name(params) { ... }; return; print(...)), the value types (numbers, booleans, strings and functions), the rules for scopes, and example programs (FizzBuzz, factorial and fibonacci) with their expected output. Write the grammar in a simple notation. Set up a CMake project with Boost (for Program_options). Done when: LANGUAGE.md fully defines the language with examples.
- 02
Scan the source
~1.5hWrite a scanner that turns source text into tokens (numbers, strings with escapes, names, keywords, operators and punctuation), each with its line and column, skipping whitespace and comments. Report unknown characters and unterminated strings with their position. Done when: unit tests show each example program scans into the right tokens.
- 03
Parse into a syntax tree
~3hWrite a recursive descent parser following the grammar, with one function per precedence level for expressions, building a syntax tree of expression and statement nodes (with std::unique_ptr or std::variant). On a syntax error, report the line, column and what was expected (such as expected ')' after arguments), and recover to the next statement so several errors can be reported at once. Done when: the example programs parse, and broken programs give clear errors with positions.
- 04
Evaluate expressions and statements
~2.5hWrite a tree-walking evaluator with values as a std::variant, and environments as a chain of scopes (each with a map of names and a pointer to the enclosing scope). Implement expressions, variables, assignment, if, while and print, with runtime errors (undefined variable, wrong types for an operator and division by zero) reported with the position. Done when: programs using variables, loops and conditions produce the expected output.
- 05
Add functions
~2.5hImplement function definitions (capturing the scope they were defined in, so they're closures), calls with argument count checks, return (for example using an exception or a special result to unwind), and recursion with a depth limit that reports stack overflow instead of crashing. Add a few built-in functions (such as len, str and clock). Done when: factorial, fibonacci and a closure-based counter all work.
- 06
Build the command line and prompt
~1hRun a file given as an argument (with Boost.Program_options for options such as --tokens and --ast, which print the tokens or syntax tree for debugging), or start an interactive prompt that keeps variables between lines and shows errors without exiting. Done when: files run, the prompt works, and the debug options print useful output.
- 07
Test it
~2hWrite tests (with GoogleTest or Catch2) for the scanner, parser and evaluator, plus a folder of test programs with their expected output (including error messages with positions) that a test runner compares automatically. Run the tests with AddressSanitizer. Done when: all tests pass with no sanitizer errors.
Ready to build this?
Get a GitHub repo and start building. Your AI reviewer checks each step as you go.
Tech stack