Himansh Raj

H++: A Bit-Level Programming Language

· 4 min read

H++: A Bit-Level Programming Language

I built H++, a statically-typed, natively-compiled programming language with exactly one primitive type: the bit. Everything else — int, long, byte — is just a named abstraction over some number of bits. The compiler maps those abstractions straight to x86-64 machine instructions with no runtime overhead.

I made it because I wanted to actually build a compiler front to back, and I wanted a language whose type system was radical enough to teach me something in the process.

Why I made H++

Most languages hand you a fixed set of primitives and hide what they mean at the hardware level. I wanted to invert that: start from the smallest possible unit — a single bit — and let every other type be defined in terms of it.

def byte bit[8];
def int  bit[32];
def long bit[64];

Two types are equivalent if they have the same bit width, and small-to-large widening is implicit. That's the whole model. int isn't special — it's just bit[32] with a nicer name, and the def that creates it adds zero runtime cost. Forcing myself to design around that one rule made the type system unusually clean to reason about.

Building the compiler

The reason I really did this was to walk the entire compilation pipeline myself:

Source (.hpp)
  → Lexer (tokens)
  → Macro Expansion
  → Parser (AST)
  → Semantic Analysis (type checking)
  → Code Generation (x86-64 NASM)
  → Assembler (nasm)
  → Linker (ld)
  → Executable

There's no interpreter and no VM. H++ compiles to real x86-64 Linux executables through NASM and ld. You can also stop early and inspect the intermediate stages — --dump-tokens and --dump-ast were the debugging tools I leaned on most while building it.

Getting from "a string of source text" to "an ELF binary that runs" without leaning on an existing backend is genuinely humbling. Every stage has to hand off a correct structure to the next one, or the failure shows up much later and much more cryptically.

The features that came out of it

To make the bit-primitive idea actually usable, H++ grew a real toolkit:

  • A module systemimport std/io; or import std/{io, fmt, mem};.
  • A macro systemmacro get(a, i) { int_get(a, i) } for compile-time expansion.
  • Inline assemblyasm linux { syscall } for direct hardware access.
  • Operator overloading — define what int + int means down at the register level.
  • A standard library — 73 syscall wrappers plus 40+ utility functions covering I/O, memory, strings, arrays, random, and time.
  • External and user libraries — link .o files or write your own .hdef + .asm/.hpp modules.

Because primitives are so thin, a lot of the "standard library" is really just well-named wrappers around syscalls and bit operations — which keeps the mental model consistent all the way down.

What I learned

  1. A minimal core forces the hard decisions early. With only bit to build on, I couldn't paper over type-system questions — I had to answer them precisely.
  2. Code generation is where theory meets reality. Turning a clean AST into correct NASM is where all the abstraction has to cash out into actual registers and instructions.
  3. Zero-cost abstractions are a design discipline, not a slogan. Every feature had to justify that it added a name, not a runtime cost.

H++ is a learning language — I'm not pitching it as production infrastructure. But building it took language and compiler design from something I'd read about to something I'd actually done.

If you want to look at the code, here it is: H++ on GitHub.