Skip to content
induwara.lk
Premium
Opinioncompilerspythonlearning-to-code

A Python interpreter in 1024 bytes is a compiler course

Austin Henley squeezed a working Python interpreter into 1024 bytes of C. The interesting part isn't the golf — it's how small a real interpreter actually is.

Induwara Ashinsana6 min read
Screenshot of 1024 bytes of minified C source code implementing a small Python interpreter
Image: austinhenley.com

Austin Z. Henley built a working Python interpreter in 1024 bytes of C, and published the whole thing at austinhenley.com/blog/python1024. It runs FizzBuzz. It handles recursion. It fits in less space than the average React component.

I want to argue that the byte count is the least interesting thing here. The real finding is buried underneath it: a language interpreter that most CS students treat as a final-year boss fight is, stripped of everything optional, about a page of code.


🔍 What actually fits in 1024 bytes

Henley set the limit first and worked backwards. His stated rule was no "macro shenanigans or library tomfoolery" — just GNU C89 and libc. He tried 512 bytes first and gave up on it.

Here is what survived the cut:

Feature Supported?
Integer variables (single lowercase letter)
Arithmetic + - * % with correct precedence
Comparisons < > <= >= ==
if / else
while and for x in range(y), both with else
Function definitions, calls, recursion
Indentation-based blocks
print of a string literal or integer expression
Comments, integer truthiness
Multi-character variable names
Function arguments
Error handling
Comparison chaining (a < b < c)

That is not a toy calculator. Indentation-sensitive blocks plus recursion plus operator precedence is a genuine language. The internal state is almost comically small: a 999-byte fixed array holding the raw Python source, and a 256-entry symbol table for variables.

Key takeaway: The gap between "I can write a for loop" and "I can write the thing that runs a for loop" is far narrower than the university syllabus makes it look.


⚡ The trick is executing while you parse

The reason this fits at all is an architectural choice, not a golfing one. Henley uses a recursive descent parser that executes as it parses. There is no tokenizer stage handing off to a parser stage handing off to a bytecode compiler handing off to a VM.

That single decision deletes most of what a textbook interpreter carries:

  • No token struct, no token array, no lexer loop
  • No AST nodes, no allocation, no tree walker
  • No intermediate representation, no bytecode, no dispatch table
  • No separate pass boundaries to keep in sync

Compare the shapes:

Stage Textbook interpreter This one
Lexing Separate pass, produces tokens Inline, character by character
Parsing Builds an AST Function call stack is the tree
Evaluation Walks the AST or runs bytecode Happens during the parse
Memory Heap-allocated nodes Fixed 999-byte buffer + 256 slots

The call stack of the parser becomes the expression tree. parse_sum calls parse_term calls parse_factor, and precedence falls out of the nesting for free. If you have ever stared at a grammar and wondered where precedence "lives", this is the answer: it lives in which function calls which.

I've watched people bounce off compiler courses because they hit lexer generators and parser tables in week two and concluded the whole field is machinery. It isn't. The machinery exists to handle scale, error messages, and optimisation. The core idea is small enough to hold in your head.


🛠️ Code golf is a bad habit and a good exercise

The honest version of this project was over 4,800 bytes. Getting to 1024 meant a second, deliberately worse copy of the same program. Henley's techniques were the usual C golfing set:

  1. Single-letter identifiers everywhere
  2. Implicit int declarations (legal in C89, gone in modern C)
  3. Comparing raw ASCII values instead of character literals
  4. Ternaries and the comma operator instead of statements
  5. Bitwise & and | standing in for && and ||
  6. Function parameters reused as scratch variables

His own verdict on the process was that it was "quite tedious", mostly because of shuttling between the readable version and the minified one. That is the part I'd underline for anyone tempted to copy the style.

Golfing is a fine way to learn what a language actually permits. It is a terrible way to write code anyone else has to maintain, including you in three weeks.

If you want to see how much of your own code is incidental rather than essential, run it through our HTML/CSS/JS minifier and look at the ratio. That's your ceiling for mechanical savings. Everything below it is a design change, which is what Henley actually did by picking single-pass execution.


🎓 Why this is a good weekend for a Sri Lankan CS student

Compiler and language modules at UCSC, Moratuwa, SLIIT and NSBM tend to be the ones students dread. Part of that is the material. A larger part, I think, is that the assignments start at industrial scale — write a lexer, write a parser, produce three-address code — before anyone has felt a language work.

A 1024-byte interpreter inverts that. You get the dopamine hit first.

Practical version of the exercise, on any hardware you already own:

  • Zero setup cost. GCC or Clang, one .c file. No LLVM, no ANTLR, no 8GB of toolchain on a laptop that's already struggling.
  • Scope it to an afternoon. Integers, four operators, print, if, while. That's it. Add functions on day two.
  • Test in the browser first. Write the Python programs you intend to support in our online Python compiler, confirm the behaviour you're copying, then make your C match it. Prototype the C side in the online C compiler if you don't want to set up a local toolchain yet.
  • Ship it publicly. Henley's is on GitHub at AZHenley/python1024. A small, finished, working interpreter on your profile says more to a hiring manager than a half-built clone of something large.

That last point matters here more than it does elsewhere. Sri Lankan grads competing for remote roles are usually screened on a portfolio, not a transcript. "I wrote a language" is a five-minute interview story with a live demo attached.


💡 What this means for you

Henley's framing for the project was that he writes code by hand on weekends "to feel human". Fair enough. But the transferable lesson isn't about craft nostalgia.

  • Hard constraints produce better design decisions than good intentions. The 1024-byte cap is what forced single-pass execution. A vague goal of "keep it simple" would not have.
  • Most complexity in a system is optional. Error handling, multi-character identifiers and function arguments were the first things off the boat. Note that they're also the first things a real product needs, which tells you where the actual work in language design sits.
  • Scope down until you finish. A 1024-byte interpreter that runs FizzBuzz beats a 40,000-line abandoned one.

If you've been putting off the compilers module, or you've had "write a small language" sitting in your notes for two years, this is the smallest possible on-ramp. One file. One weekend. No dependencies.

#compilers#python#learning-to-code
IA

Induwara Ashinsana

Information Systems student at UCSC and Executive Director at Ryzera Technologies. Writes about software, AI, and what it means for builders in Sri Lanka.

About the author →

Keep reading