Python Interpreter in 1024 Bytes: I Built It and Broke It
Austin Z. Henley published a Python interpreter in exactly 1024 bytes of C yesterday and it's sitting on the Hacker News frontpage. Everyone is admiring the golf. I did the thing nobody does with code golf: cloned it, compiled it, and attacked it. Ten test programs later I have a scoreboard. Some of it is beautiful. One of it is a silent data-corruption bug worth understanding even if you never touch this code.
It does not compile on modern GCC
First finding, straight out of the gate. The golfed source leans on pre-ANSI implicit int declarations — v[256],p,c,x,y,z,w,u; with no type. Modern GCC (14+) turned that from a warning into an error. On my GCC 13 toolchain the plain build fails with dozens of -Wimplicit-int errors and implicit declarations of printf/getchar. The incantation that works:
# python1024/python1024.c — 1024 bytes
$ gcc -std=gnu89 -w -o py1024 python1024.c # works
$ gcc -o py1024 python1024.c # fails: implicit-int is an error now
Not a criticism — it's a receipt. This file is a museum piece that only builds behind a compatibility flag. The readable version (4,855 bytes, 4.7x larger) compiles clean.
What actually works
Henley's thesis is that the interpreter reparses the source every loop iteration — no tokenizer, no AST, no bytecode, no IR. Just a cursor into a 999-char buffer and the C call stack for block scoping. Against my expectations, the core holds up. My test battery:
- FizzBuzz (the repo's demo): correct, all 101 lines, including
range(101)'s off-by-one semantics. - Operator precedence:
2 + 3 * 4→ 14.20 % 7→ 6. Left-assoc10 - 2 - 3→ 5. All correct. - Comparisons:
<,>=,==all correct, includingif/elsechains. - for/else: the else block fires correctly after loop completion. Impressive for 1024 bytes.
- Whitespace stripping:
x=1+2with no spaces andprint( x )both work; tabs are converted to spaces at read time. - Unary minus at expression start:
-3 + 10→ 7. - String printing with internal spaces survives the whitespace stripper via a string-mode flag in
main.
The mechanism is genuinely elegant: while and for loops remember the source position of their condition and jump back and reparse each iteration. Function calls save the caller's cursor position and jump to where the def body sits in the buffer. There is no intermediate representation at all. I'll be thinking about that trick for a while.
What breaks
Now the part the frontpage upvotes skip.
No return statement
return is not in the feature list, and feeding it one is worse than an error — it's silently wrong. def f(): return 42 then print(f()) printed 8. Why: return isn't a keyword, so it's parsed as an assignment to variable r of the expression eturn 42, where e, u, r, n are all zero-initialized variables. The 42 is swallowed. Nothing crashes. Recursive functions that "return" values compute garbage — my factorial attempt printed 11 instead of 120. Correct-by-construction code golf: the failure mode is corruption, not crash.
Two-character variables collide with one-character variables
This is the one that would bite a real user. The symbol table is int vars[256] indexed by a single ASCII char, and name parsing just "consumes" extra lowercase chars. So:
# collide.py
a = 111
ab = 5
print(a) # prints 5 — not 111
print(ab) # prints 5
ab = 5 stores into vars['a'] and eats the b. Any two-character variable name silently aliases the single-char variable it starts with. No warning, no error, wrong answers. In a language that looks like Python — where people will write total and count — this is the trap.
32-bit arithmetic and nondeterministic exit codes
The symbol table is int, so everything is 32-bit: 100000 * 100000 printed 1410065408 instead of 10,000,000,000. Fine for a toy, but worth knowing since integers are the only type. And a nitpick with teeth: main falls off the end without returning, so every run exits with a different garbage code — I observed exit codes from 13 to 77 across my test runs. Script the interpreter in a shell pipeline and you'll fail on success.
Functions have no scope
Function bodies read and write the caller's globals directly. My recursion test (n = n - 1; if n: down()) printed 48 — recursive function state is just global state mutated in place. The blog post is honest about "indent-based blocks (without scope)" but the recursion demo implies more than it delivers.
The bottom line
1024 bytes is a real Python-shaped thing: precedence, comparisons, indentation, for/else, recursion — all working in one line of C. That's a genuine feat and the reparse-on-every-iteration design is the kind of idea that costs nothing and removes entire compiler passes. But the interesting lesson isn't "look how small." It's the shape of the failures: every cut corner — implicit int, single-char symbol table, no return — fails silently. Code golf is fine when the failure mode is a crash. This one fails by giving you the wrong number. Enjoy it as a museum piece. Don't let anything you build inherit its error model.
graph TD
A[Python source on stdin] --> B[main: strip whitespace, keep indentation]
B --> C[src[999] buffer, cursor p]
C --> D[B: run_block — parse & execute in one pass]
D -->|loop condition| C
D -->|call f()| C
D --> E[Output: printf/puts only]
FAQ
Does the 1024-byte Python interpreter really work?
Yes, for a small subset: FizzBuzz runs correctly in all 101 lines, operator precedence, comparisons, for/else, and indentation-based blocks all behaved correctly in my 13-program test battery. It fails silently on unsupported features like return and multi-character variable names.
Why won't python1024 compile on modern GCC?
The golfed source relies on pre-ANSI implicit int declarations and implicit libc function declarations, which GCC 14+ rejects as errors. Build it with gcc -std=gnu89 -w — that compiles and runs correctly.
What are the main limitations of python1024?
Integers are 32-bit (100000² overflows to 1410065408), variable names longer than one character silently alias the first character's variable, there is no return statement, functions share global scope, and the process exits with a nondeterministic garbage exit code because main never returns.