If you’ve ever wanted to peek under the hood of a programming language—to really understand how virtual machines work, how concurrency models are implemented, or how compilers translate high-level code into bytecode—you need a codebase that’s both powerful enough to be interesting and clean enough to be comprehensible. That’s where Cardinal comes in.
Cardinal is a lightweight concurrent scripting language that brings together the elegance of Wren with the clarity needed for education and research. In this post, I’ll introduce you to what makes Cardinal special, explore its syntax and design philosophy, and explain why it might be the perfect tool for your next language implementation course, research project, or embedded scripting needs.
Cardinal is a C++ rewrite of the Wren programming language, designed with a specific focus: making language implementation accessible for students, researchers, and anyone curious about how programming languages work under the hood. While it preserves Wren’s core goals of minimalism, speed, and expressive object-oriented design, Cardinal goes further by prioritizing code readability and educational value.
The language draws inspiration from several influential languages:
What sets Cardinal apart is its implementation philosophy. The entire VM and runtime are compact, heavily commented, and written in modern C++20. There are no external dependencies, making it trivial to build and study. Whether you’re teaching a programming languages course, conducting research on type systems, or just want to embed a scripting language in your application, Cardinal provides a clean foundation to build upon.
Cardinal’s syntax will feel immediately familiar if you’ve worked with languages like JavaScript, Python, or Ruby. Here’s the classic first program:
System.print("Hello, world!")
Classes are first-class citizens in Cardinal, forming the core abstraction of the language:
class Bird {
construct new(species) {
_species = species
}
species { _species }
sing() {
System.print("The %(_species) sings!")
}
}
var cardinal = Bird.new("cardinal")
cardinal.sing() // The cardinal sings!
Notice the string interpolation with %(_species)
—a clean way to embed expressions directly in strings. Cardinal uses a constructor pattern with construct new()
and private fields prefixed with underscores. Getters can be defined simply by writing a method without parameters.
One of Cardinal’s most interesting features is its built-in support for lightweight concurrency through fibers. Fibers enable coroutine-style concurrency without the complexity of threads:
class Counter {
static countTo(n) {
return Fiber.new {
for (i in 1..n) {
Fiber.yield(i)
}
}
}
}
var counter = Counter.countTo(5)
while (!counter.isDone) {
System.print(counter.call())
}
// Prints: 1, 2, 3, 4, 5
This example demonstrates how fibers can yield values, allowing you to write generator-style code that’s both elegant and efficient. The fiber suspends execution at each yield
, returning control to the caller, and resumes where it left off on the next call()
.
Cardinal is more than just inspired by Wren—it’s a deliberate C++ reimplementation that maintains compatibility with Wren’s syntax and semantics while improving on the educational and research aspects. The original Wren is written in C and focuses on being a minimal, fast embeddable scripting language.
Cardinal takes Wren’s solid foundation and asks: “What if we rebuilt this with modern C++, extensive comments, and education as a first-class goal?” The result is a codebase that:
For researchers and educators, this means you can use Cardinal to teach the same concepts you’d teach with Wren, but with a codebase that’s more approachable for study and modification. Students can trace through the code and understand not just what it does, but why particular design decisions were made.
Cardinal was designed from the ground up with academic use cases in mind. Here’s why it’s particularly well-suited for educational and research contexts:
The VM and runtime are deliberately kept small and readable. Each component is heavily commented, making it suitable for:
The clean separation of concerns means you can focus on one aspect at a time—study the garbage collector without getting lost in parser details, or examine the bytecode compiler without worrying about the runtime.
Cardinal provides an excellent platform for:
The small footprint enables fast iteration—you can modify the language, rebuild, and test your changes in seconds rather than minutes. And because it compiles to bytecode, you can experiment with runtime optimizations and see their effects immediately.
Cardinal is sized perfectly for semester-long projects:
The codebase is large enough to be interesting but small enough that students can grasp the whole system by the end of a semester.
Beyond its educational value, Cardinal is a fully functional language suitable for real-world embedding:
Building Cardinal is straightforward. The project includes a helpful Python build script that handles everything:
# Build Cardinal
python3 utils/build.py --build
# Run tests
python3 utils/build.py --test
# Run benchmarks
python3 utils/build.py --benchmark
You can also use CMake directly if you prefer. The project is well-documented with clear build instructions for multiple platforms and toolchains.
In future posts, I’ll dive deeper into Cardinal’s implementation:
Cardinal represents a unique intersection: a language that’s simple enough to understand fully, yet sophisticated enough to explore real implementation challenges. Whether you’re teaching the next generation of language implementers, researching novel language features, or just curious about how scripting languages work, Cardinal provides a clean, approachable foundation.
If you’re interested in programming language implementation, I encourage you to check out Cardinal on GitHub. The codebase is waiting to be explored, modified, and extended—and that’s exactly what it was designed for.
In the next post, we’ll take a deeper look at Cardinal’s bytecode compiler and see how it transforms source code into executable instructions in a single pass.