← Prism
Prism
Overview Spec Draft Releases Blog
Features
defer orelse zeroinit raw bounds-check auto-unreachable auto-static
GitHub ↗

A pragmatic C

A itch that needed a scratch.

Dawn Larsson
Dawn Larsson

C isn't bad. For better or worse it's shaped our modern world, and one of the most direct ways we can think and communicate with our hardware — it's effectively the modern day's assembly. Any hardware ever made supports C. The sheer amount of C that runs every second is staggering, and we owe a great deal to the many engineers who've had the discipline to write it 1978-style for decades while keeping our lights on for much longer than some of us have even been alive.

But C has live wires hanging out of the ceiling, and you have to hand-crank the shower to get nice cold well water — or if you're a car person, it's a couch seat without a seatbelt.

Instead of moving house or bulldozing our home, what if we could just... not let the live wires hang out of the ceiling? Tuck them away. Cap them properly?

This is the theme of Prism. It's a transpiler that aims to make marginal improvements over time — taking small steps to improve our real-world codebases and projects with realistic, pragmatic changes.

Evolved out of building a build tool for a game engine, comes Prism. A classic scratching-your-own-itch type of deal, but with the right kind of feature creep and slight insanity that reshaped its original goal into: safer & a bit more sane C, today.

The human cost of C

The biggest challenge of being a programmer is being human. You're not just juggling what you want to implement, but all the other factors in your life that flush your cache faster than your manager's ability to assign you tickets.

One of the first features was defer — Go-style. Manual cleanup branches are one of those things that really add to the mental overhead of writing pure C, and a big bug source. It requires a fair bit of mental work to track cleanup paths, not to mention the added resistance to adding new features.

The code every C programmer has written a version of this week:

FILE *f = fopen(path, "r");
if (!f) return -1;

Buffer *buf = buffer_alloc();
if (!buf) { fclose(f); return -1; }

int result = process(buf);
buffer_free(buf);
fclose(f);
return result;

With Prism:

FILE *f = fopen(path, "r") orelse return -1;
defer fclose(f);

Buffer *buf = buffer_alloc() orelse return -1;
defer buffer_free(buf);

return process(buf);

Same compiler. Same binary. The cleanup ladder is gone.

Building it

Defer was quite the challenge to get started with. It's a critical feature that just can't ever fail — it requires a deep control flow understanding that has to be perfect. As a parsing transpiler without an AST, it's even harder. Prism evolved quite a bit to find the sweet spot of being robust and fast. Initially Prism was a single-pass control flow analysis of your code — today it's two passes: one where we build a static snapshot, and a second where we emit code.

Prism has now grown into having defer, orelse, zero-init, runtime bounds checking, and build system features. It's becoming the tool I've always wanted. You're able to run C like scripts — prism run build.c -- <args> — before you had to chain several commands or write a build script. It's such a minor touch, but it's exactly the kind of silent DX win that actually makes a difference in your happiness as a programmer.

C tells tales from generations of people — an evolving language much like any other. It's also a story of marginal improvements.

It's been the best and worst half year working on this. There is so much to what C is. It's a mature language shaped by generations of engineers, and Prism is just one small attempt to honour that by making it a little better.

One percent at a time

You'd be surprised by what compounding small improvements can do. It's filing away at the anxiety of writing C. It's making C enjoyable, and giving room for the human. Software will never be perfect, bug-free, or perfectly secure — we should be more grounded about our limits and avoid being absolutists. In a world where more than ever having a healthy, grounded perspective is a quiet act of revolution.

Many languages that try to be a better C really aren't — they're their own languages, and that's fantastic, but not actually C. I find either the authors had too many strong opinions, or moved too quickly.

With Prism, a core objective is to ship reasonable defaults and let you dial in exactly what you want — or opt out of anything that conflicts with your requirements.

All features of Prism are opt-out. No question.

The pragmatic thing is to work with what we have, and make it a little better.

Where Prism is going

I see a modern C on the horizon. Something we don't have to fight, nor be anxious about. It's an effort the standards committee just can't do — and we have decades of tooling and code that can't just go away. It's a mature, robust asset with the largest mind share on the planet. We should leverage what we have today, for a better tomorrow.

Prism is still early. A lot of work goes into finding the most obscure and darkest corners of C and testing rigorously. It turns out parsing a language with 50 years of warts is a hard one — but well worth it.

Prism isn't going to move fast — it's meant to be robust, and to keep fixing parsing edge cases and weird C corners. I have a draft specification that gives you a good idea of the features planned.

If you find Prism useful, please star it on GitHub, leave feedback, or consider sponsoring the effort. Any feedback is genuinely appreciated.


— Dawn

Sponsor

Prism is built and maintained by one person, in the open, for free.

If it saves you time or makes your C safer, sponsoring is how you keep that going.

GitHub Sponsorsgithub.com/sponsors/dawnlarsson
Get in touch

Available for consulting across design, branding, and engineering.

Compiler work, systems programming, C codebase hardening, dev tooling, or a visual identity for your project.

dawn@dawn.daySerious inquiries only.