holyc-lang compiles HolyC through an SSA IR, and it says up front it is for fun
HolyC compiler & transpiler
At a glance
- What is it?
- This is a from-scratch C implementation of a compiler for HolyC, the language of Terry A. Davis and TempleOS, with an ahead-of-time path that emits x86_64 and AArch64 assembly as text, a JIT that maps machine code into executable memory, a REPL, an LSP and an assembler. The README opens with a warning that errors are hit and miss and that this is more for fun than for serious software development.
- Who is it for?
- holyc-lang fits a language implementer who wants a readable, from-scratch compiler to study, and a TempleOS enthusiast who wants to compile HolyC on a modern machine with an AOT path, a JIT and a REPL. It does not fit anyone shipping production HolyC, because the compiler is a beta with hit-and-miss error messages, and the HolyC-to-C transpiler is described as varying states of broken.
- Can I use it commercially?
- Yes. BSD-2-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 47 days ago.
- What is it written in?
- Mainly C, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
HolyC is Terry A. Davis's language, and the compiler is new
The subject of this project is HolyC, the programming language created by Terry A. Davis, the programmer behind TempleOS. The repository is a compiler for that language, implemented in C, and the README describes it as a holyc compiler built from scratch in C.
The first thing the README says, above the logo, is a warning, and the warning is the most important sentence in the file. The compiler is in a working state and most features are implemented. Errors are a bit hit and miss. This is more for fun than for serious software development.
That framing should shape any evaluation. This is a hobby-grade compiler for an unusual language, and the author says so before anyone else gets a chance to point it out. The features list carries the same tone, ending with a myriad of pernicious bugs, which is a funny way of saying that the failure modes are numerous and not yet catalogued.
The rest of the feature list is genuinely substantial for a project at this stage. There is an AOT compiler, which uses clang internally to create and link object files. There is a JIT compiler invoked as hcc -jit <files...>. There is a REPL, an LSP with a neovim setup example, an assembler used in asm {} blocks, and a disassembler.
The language itself is worth a sentence, because it explains why a compiler is interesting here. HolyC is the language of TempleOS, which is a complete operating system written in one language, and HolyC is unusual in that a program is not a set of statements you invoke but a set of definitions you call into existence, with the last expression in a file acting as the entry point. That is why the examples in this README end with a bare call.
U0 Main()
{
"Hello world\n";
}
Main;The final Main; is not punctuation. It is how the program runs.
AST, then an SSA IR, then assembly as text, then gcc
The pipeline is described in one paragraph of the introduction, and it is worth following because it explains the design decisions.
The compiler creates an AST, lowers it to an SSA based IR, and emits x86_64 and AArch64 assembly code as text which is fed into gcc to assemble. So the compiler does not invoke the assembler itself. It produces a text file of assembly and hands it to the system compiler.
There is a stated reason for that choice, and it is about legibility rather than performance. Basic optimisations like constant folding and some dead code elimination occur, but broadly it should be easy to see how the assembly maps to the source code. A compiler that emitted machine code directly would be faster to produce and much harder to read; this one keeps a human-readable artefact in the middle, which is the right trade for a project whose purpose is partly pedagogical.
The two optimisation passes mentioned are the standard first ones, and the README is careful not to claim more. A partial dead code elimination pass on top of an SSA form is enough to make the output readable without turning the compiler into an optimizer project.
Floating point arithmetic is supported, as are most of the major language features, and the example in the README is a demonstration of the object model rather than of the arithmetic.
class SomethingWithAnAge
{
I64 age;
};
class Person : SomethingWithAnAge
{
U8 name[1<<5];
};Inheritance, fixed-size arrays declared with a shift, and the type suffixes I64 and U8 are all visible there, and the allocations in the rest of that snippet use MAlloc and MemCpy, which come from the standard library the compiler installs alongside itself.
The JIT is the same pipeline with a different final step
The JIT is described as an alternative to the AOT path, and the difference is entirely in what happens at the end. The AOT path emits assembly text and feeds it to gcc, while the JIT maps machine code to executable memory and then runs it, and this is done with a builtin assembler.
That is the right design for a language like this, and it is also why the JIT is what makes the REPL possible. A JIT-compiled program can be executed in the current process, which is exactly what an interactive session needs, and it does not require a separate link step or a system toolchain call per statement.
The build enables it by default. The Makefile's all target passes -DHCC_ENABLE_JIT=on to CMake, and the README notes that the REPL requires a JIT-enabled build, which is the Makefile default.
Two practical limits are visible in the documentation. The JIT can only load shared libraries, so .o files still have to go on an AOT command line. And the JIT path uses dlopen for libraries, with the name form probing for lib<name>.{dylib,so} across the dynamic linker's default paths, the configured install prefix, /opt/homebrew/lib, /usr/local/lib and the system library directories.
There is also a control flow graph generator, which is a genuinely useful tool for a compiler project. The invocation is hcc -cfg ./<file>.HC, which writes a dot file, and dot -Tpng renders it, with the -cfg-png and -cfg-svg variants requiring graphviz. Seeing the control flow graph of a compiled function is a good way to check that the frontend did what you expected before you worry about the backend.
The REPL is the part you will actually use
The REPL gets the most detailed documentation in the README, and the reason is that a REPL is where a language becomes usable for exploration. It is invoked as hcc -repl and requires a JIT-enabled build.
The semantics are unusual and worth reading carefully. Functions, classes, globals and #defines accumulate as you type, statements execute immediately, and the value of a trailing expression is echoed. That last part is a direct expression of the HolyC model, where a trailing expression is meaningful, and it is the same rule that makes the bare Main; at the end of a file work.
The ergonomics are built in rather than bolted on. There is line editing, persistent history in ~/.hcc_repl_history, and tab completion over everything you have defined. Uf("Name") disassembles a JIT-compiled function, and ReplDel("name") removes a global, function, class, union or #define from the session so the name can be redefined from scratch. That second one solves a real problem in a session-oriented language, where you often want to change a definition and start again.
Session configuration goes in an rc file at ~/.hcc_rc.HC, which can contain any HolyC functions you want available. To silence the welcome message you define HCC_NO_REPL_HELLO in that rc file.
Two conveniences deserve a mention. Memory in this state is semi-managed in a best effort way to prevent crashes, which is a candid description of what a REPL can promise. And the shell can be invoked directly, by calling Sh(...) or by using the @ prefix, where the @ prefix denotes the syntax for invoking your environment's shell.
The REPL also inherits the language's own quirks, which is a feature if you came for HolyC and a warning if you did not.
#link and #ifjit are the two directives that change what gets built
The README documents two directives in detail, and both of them address a problem that a compiler without a build system has to solve on its own.
The first is #link, which records a shared library dependency in the source itself, so neither the compile command nor the REPL needs extra flags.
#link "./mylib.so" /* literal path, relative to the working directory */
#link <sdl2> /* library name, like a linker's -lsdl2 */
extern "c" I64 MyLibAdd(I64 a, I64 b);There are two forms, and they behave differently in the two compilation modes. For AOT builds the path form is passed to the linker verbatim and the name form becomes -l<name>, with -L for Homebrew and /usr/local added when present. In the JIT and the REPL the library is dlopen'd instead, with the name form probing the paths listed above. Duplicate #links are ignored, so headers can #link the library they wrap, which is a nice touch that removes a class of build configuration.
The second is conditional compilation on the execution mode. #ifjit and #ifaot select code depending on whether the program is being JIT-compiled, through hcc -jit or the REPL, or built ahead of time. They are shorthand for #ifdef __HCC_JIT__ and #ifdef __HCC_AOT__, exactly one of which is always defined, and they compose with #else and #endif like any other conditional.
U8 *Mode()
{
#ifjit
return "jit";
#else
return "aot";
#endif
}The reason this matters is that the two modes have different capabilities. The JIT cannot load object files, and the REPL behaves differently from a compiled program in how it holds state, so code that has to work in both needs a way to tell them apart.
The Makefile wraps CMake, and the JIT is on by default
Building needs a C compiler, gcc or clang, a Linux or macOS x86_64 machine, make and cmake. On Windows the README asks you to install WSL2 and then follow the same steps.
The simple path is the Makefile at the root of the repository, which wraps CMake and provides a default target that configures the project with the compiler, the build type, the install prefix, the flags and JIT enabled, then builds it.
On macOS the sequence is make, then make install, then make unit-test. On Linux the same three steps with sudo in front of the install.
make && sudo make install && make unit-testThe install prefix can be overridden with make INSTALL_PREFIX=<prefix>, and the underlying CMake invocation is the standard one.
cmake -S ./src \
-B ./build \
-G 'Unix Makefiles' \
-DCMAKE_C_COMPILER=gcc \
-DCMAKE_BUILD_TYPE=ReleaseThe install step installs the compiler and the holyc libraries for strings, hashtables, I/O, maths, networking and JSON parsing, which live in ./src/holyc-lib/. If you want sqlite3 as well, add -DHCC_LINK_SQLITE3=1 to the Makefile or to the CMake configuration.
The test targets are worth understanding because they are written in the language itself. The unit-test target compiles ./src/tests/run.HC with the freshly built compiler and runs the resulting test-runner, and jit-unit-test does the same with run_jit.HC. The lsp-test target is described as hermetic in the Makefile comment: it builds the tos library from the tree into a local prefix and compiles the LSP harness against that, so the suite tests in-tree sources and never whatever happens to be installed in /usr/local. That is the right way to test a self-hosting-ish toolchain, and it is a small detail that says the author has been bitten by the alternative.
The language server is bundled with the compiler and runs as hcc -lsp. On its own it is not particularly useful, the README says, since it is meant to be used with an editor, and there is a neovim configuration example with jump to definition and other features described as working.
A beta compiler, a BSD licence, and a test suite written in HolyC
The version record tells you where this project is. The recent releases are v0.0.15-beta on 2026-07-28, v0.0.14-beta on 2026-07-03 and v0.0.13-beta on 2026-06-20, so every published version is a beta and the numbering is still in the zeroes. The repository is not archived, the default branch is main, and the last push was on 2026-09-06, so the work is current even though the released version lags it.
The licence is BSD-2-Clause, with the COPYING file at the repository root, and the top-level entries are a small C project: src/, assets/, packaging/, a Makefile, a.clang-format file and the.github/ directory.
The experimental transpiler is the part to set expectations about. There is experimental support for transpiling HolyC to C, which is in varying states of broken and not properly supported. That is an unusual level of candour, and it means the transpiler is an experiment to look at rather than a feature to plan around.
The compatibility statement is similarly specific. The compiler will compile HolyC source code to an x86_64 or AArch64 compatible binary, which has been tested on AMD Linux and an Intel Mac, and on an M1 Mac and Fedora Linux on an M4 using QEMU.
The combination of a working pipeline, a REPL, an LSP, a test suite written in the language itself and an honest warning at the top of the README is a decent package for a project whose author says it is for fun. The mistake would be to read the feature list as a production readiness signal.
Editorial conclusion
holyc-lang fits a language implementer who wants a readable, from-scratch compiler to study, and a TempleOS enthusiast who wants to compile HolyC on a modern machine with an AOT path, a JIT and a REPL. It does not fit anyone shipping production HolyC, because the compiler is a beta with hit-and-miss error messages, and the HolyC-to-C transpiler is described as varying states of broken. Before committing, read the warning at the top of the README, build with the Makefile defaults so the JIT is enabled, and run make unit-test to see how the existing test suite behaves on your platform.
Frequently asked questions
How do I build the HolyC compiler?
You need a C compiler, gcc or clang, make, cmake and a Linux or macOS x86_64 machine. The Makefile at the repository root wraps CMake, so on Linux you run make, then sudo make install, then make unit-test. On Windows the README asks you to install WSL2 first.
Does holyc-lang support a JIT and a REPL?
Yes. The JIT is invoked as hcc -jit <files...> and the REPL as hcc -repl, and both require a JIT-enabled build, which the Makefile enables by default with -DHCC_ENABLE_JIT=on. The REPL accumulates definitions as you type, echoes trailing expression values, and keeps history in ~/.hcc_repl_history.
What does the HolyC compiler pipeline do?
It creates an AST, lowers it to an SSA based IR, and emits x86_64 or AArch64 assembly code as text which is fed into gcc to assemble. Constant folding and some dead code elimination occur, and the README says the design keeps it easy to see how the assembly maps to the source.
Can HolyC code link against shared libraries?
Yes, with the #link directive, which records the dependency in the source so neither the compile command nor the REPL needs extra flags. It takes a literal path or a library name, and for AOT builds the name form becomes -l<name>, while the JIT and the REPL dlopen the library instead. The JIT can only load shared libraries, so object files still have to go on an AOT command line.
How does HolyC code tell JIT and AOT builds apart?
With the #ifjit and #ifaot directives, which are shorthand for #ifdef __HCC_JIT__ and #ifdef __HCC_AOT__, exactly one of which is always defined. They compose with #else and #endif like any other conditional, which matters because the two modes have different capabilities.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/jamesbarford-holyc-lang)Was this useful?