A tree-walking interpreter for a subset of Python, built from scratch in C++. Implements the full pipeline from raw source code to execution: lexing, parsing, AST construction, and interpretation with scoped variable resolution.
- Variables — assignment and lookup with proper scoping
- Data types — integers, strings, and booleans
- Arithmetic operators —
+,-,*,/ - Comparison operators —
==,!=,<,>,<=,>= - Logical operators —
and,or,not - Compound assignment —
+=,-=,*=,/= - Conditionals —
if/else - Functions — definition, calls, return values, and parameter passing
- Scoped execution — nested scopes with parent scope lookup
print()— supports multiple arguments of mixed types- Comments — single-line (
#) and multi-line (""") - Indentation-based block parsing — mirrors Python's actual syntax
The interpreter is structured as a classic four-stage pipeline:
Source Code (.py)
│
▼
Lexer
Tokenizes raw text into a stream of tokens (identifiers, operators,
keywords, literals, indentation markers, etc.)
│
▼
Parser
Consumes the token stream and builds an Abstract Syntax Tree (AST)
using recursive descent parsing. Handles operator precedence via
layered grammar rules: logicExpr → expr → mathExpr → term → factor
│
▼
AST (Abstract Syntax Tree)
A tree of typed nodes representing the program's structure.
Node types include: BlockNode, FunctNode, FunctCallNode, CondNode,
BinOpNode, UnOpNode, AssignmentNode, VarNode, IntNode, StrNode,
BoolNode, RtnNode, NoOpNullNode
│
▼
Interpreter
Tree-walking evaluator that traverses the AST and executes each node.
Manages a linked chain of Scope objects to handle variable visibility
across function calls and nested blocks.
- A C++ compiler with C++11 support or later (e.g.
g++,clang++)
g++ -o mypython mypython.cpp./mypython yourscript.pydef add(a, b):
return a + b
result = add(3, 4)
print(result)$ ./mypython example.py
7This interpreter targets a subset of Python. The following are not currently supported:
whileandforloops- Lists, dictionaries, and other collection types
- Classes and objects
- Floating point numbers
elifchains (onlyif/else)- Standard library imports
- Nested function definitions
Scope resolution — Variable lookup walks up the parent scope chain, so inner functions can reference variables from enclosing scopes. Each function call creates a new child Scope that is destroyed on return.
Operator precedence — Handled structurally through the grammar rather than a precedence table. The parser uses layered expression rules (logicExpr → expr → mathExpr → term → factor) to naturally enforce precedence without explicit priority values.
Type dispatch — The interpreter uses dynamic_cast to determine operand types at runtime and dispatch to the appropriate evaluation function (evalIntOp, evalStringOp, evalBoolOp).