deniz.in

Markets

Weather

Loading weather

· via Hacker News – Front Page (native)

Developer fits a working Python interpreter into 1024 bytes of C

Austin Z. Henley squeezed a Python-subset interpreter that runs FizzBuzz into exactly 1,024 bytes of C, using a parse-as-you-execute design with no intermediate representation.

Developer fits a working Python interpreter into 1024 bytes of C

A weekend challenge with hard limits

Austin Z. Henley has published a working interpreter for a subset of Python that occupies exactly 1,024 bytes of C source code, small enough to paste into a chat message. Written as a weekend exercise in hand-coding, the program executes a FizzBuzz script that reads like ordinary Python, complete with def, colons, indentation-sensitive blocks and parenthesis-free if statements. The write-up was featured on the front page of Hacker News.

Henley set the initial bar at 512 bytes of plain C, barring himself from preprocessor tricks and library workarounds. His first attempt took the familiar route: a recursive descent parser that grew from evaluating 1 + 2 to handling assignments like x = 1 + 2 * 3 and conditionals such as if x > y: z = 3. As he tells it, the result was a calculator rather than a Python, and it had already blown the byte budget. He responded by listing the surface features that make code read as Python, and by doubling the limit.

Parsing and executing in one pass

The real CPython implementation tokenizes source, parses an abstract syntax tree, analyzes and optimizes it, emits bytecode and then interprets that bytecode. Henley's interpreter does none of this. All state lives in a handful of global variables: a 999-character buffer holding the program text with most spaces removed, a 256-entry integer array serving as the symbol table, and a cursor into the source.

Expressions are evaluated as they are parsed, so there is no intermediate representation at any point. There is also no error handling of any kind, according to Henley: the parser assumes keywords are spelled correctly and skips known characters using position arithmetic, for instance hopping straight over the characters of "in range(" after a for keyword. Most whitespace is stripped on input, while indentation and spaces inside string literals survive. Variable names are restricted to a single lowercase character, which lets the character's value index the symbol table directly.

Control flow by reparsing

Blocks execute until indentation falls below the level at which the block began, and nesting is handled by the C call stack itself. Loops avoid compilation entirely: the interpreter records where the loop condition sits in the source and jumps back to reparse it, and the body, on every iteration. Functions use the same trick. The symbol table stores the source position of a function's body; a call saves the caller's location, jumps to the body, runs it, and restores the position when the end is reached. print is special-cased, and for loops are hard-wired to range. Henley describes the result as maintaining remarkably little state given that nothing is compiled.

Golfing the bytes away

Once a readable version worked, Henley shrank it using techniques from a long-running Stack Overflow thread on golfing in C, several of which depend on behaviors specific to GNU C89: implicit int declarations, globals that are zero-initialized for free, ASCII codes instead of character literals, the ternary and comma operators, and bitwise operations in place of logical ones. He distinguishes this from the library tricks he banned, leaning only on the compiler's default libc linking. A parser function that spans a dozen readable lines compresses into a single terse statement built from ASCII arithmetic. The final readable version exceeds 4,800 bytes; the golfed build lands on exactly 1,024.

Features were cut along the way, and comparison operators were next in line, since truthiness, testing if n % 15: rather than comparing against zero, achieves the same effect in FizzBuzz. Henley estimates that a version aimed solely at FizzBuzz could drop below 800 bytes.

Why it matters

The project is a compact demonstration of how little machinery a language needs in order to feel like Python. Removing tokenization passes, ASTs and bytecode forces the essential questions into view: how statements are recognized, how nesting maps to structure, and how control flow can be modeled as positions in text. The total absence of error handling makes it a demonstration rather than a tool, but that is precisely the point: nearly everything a production interpreter adds beyond this exists for reliability, optimization and diagnostics, not basic execution. For educators and language implementers, the 1,024-byte interpreter is a legible extreme of the parse-and-execute designs behind many teaching interpreters, and a fresh example of how a hard byte budget can act as a design constraint that clarifies rather than merely restricts.

  • #python
  • #c
  • #code-golf
  • #interpreter
  • #programming-languages

Related posts