FC is a systems and embedded programming language that transpiles to C11. Its syntax and feature set are drawn from the ML family — lexical scoping, option types, tuples, tagged unions, pattern matching, structural equality, type inference, generics, and first-class functions — but deliberately pared to fit C's low-level execution model: manual memory management, unrestricted side effects, zero runtime, and no garbage collector or borrow checker. C is the grounding constraint, not a foundation to escape. Where that model carries footguns, FC smooths the sharpest ones — bounds-checked access, no undefined behavior, deterministic left-to-right evaluation, fat-pointer strings — keeping ML's ergonomics while staying close to the machine.
FC was developed with heavy AI assistance — primarily Anthropic's Claude Opus 4.6/4.7/4.8 driven through Claude Code. The human author (Stephen Swensen) designed the language, authored the specification, set architectural direction, reviewed every change, drove design decisions, and took responsibility for correctness, style, and the licensing posture. The AI generated the bulk of the C compiler implementation, standard library, test suite, and specification prose under that direction.
This is disclosed up front because FC is also intended as a demonstration of what a well-directed human/AI collaboration can produce on a non-trivial language-design and compiler-construction task, and because readers evaluating the compiler — or considering FC as a reference for their own language work — deserve to know how it was built.
- C11 target — generates portable, self-contained C using
<stdint.h>types and_Static_assert; the whole runtime dependency is six libc symbols - Indentation-based syntax — offside rule, spaces only
- Type inference — directional (bottom-up, inside-out), no global unification
- Monomorphized generics — zero runtime cost, including const parameters (
wide<256>) that parameterize layout - Manual memory management — follows C's philosophy; escape analysis catches dangling stack pointers at compile time
- No null — option types (
T?) replace nullable values, and result types (T!) carry errors that can't be silently discarded - Expressions everywhere —
if,match, andloopproduce values;matchis exhaustive - Defined behavior by default — bounds checks, wrapping signed overflow, masked shifts, left-to-right evaluation, with
unguarded/checkedto opt out or in per region - Real C interop —
externfunctions, structs, unions, and constants bound to actual headers, plus variadics and opaque handles - Batteries and tooling — eight stdlib modules, an in-process language server (
fcc --lsp) with a VSCode extension, and opt-in FC-level backtraces
See FEATURES.md for the complete inventory.
FC is at version 1.0.0-rc.7. The compiler implements the features in the language specification, with 2000+ tests passing on gcc and clang across Linux and Windows (MSYS2/UCRT64). Breaking changes are still possible during the release-candidate phase as the surface settles.
Beyond the test suite, a few real programs exercise the language and stdlib in practice. The largest is wolf-fc, a ~10,000-line port of id Software's Wolfenstein 3D written in FC. It uses a game loop, SDL bindings via extern, manual alloc/free with defer, modules and namespaces, structs and unions, slices, options, string interpolation, closures, and five of the eight stdlib modules (io, sys, math, random, text). It runs on Linux and Windows.
Smaller programs in demos/ round out the surface:
fasteroids(~1300 lines) — vector-style Asteroids clone, SDL2.face-invaders(~1500 lines) — Space Invaders clone, SDL2.fuzzel-fobble(~1500 lines) — Puzzle Bobble clone, SDL2.fibbles(~640 lines) — Snake/Nibbles clone, SDL2 graphics + audio.fing(~160 lines) —pingclone, usesstd::net(raw ICMP).furl(~220 lines) —curl-style HTTP client, usesstd::net(TCP).
fing and furl cover std::net, which wolf-fc doesn't use.
euler-fc picks up the two stdlib modules no demo reaches — Project Euler problems solved in FC, using std::data's array_list and std::wideint's const-generic uwide<'n> big integers. It is also the one FC codebase written 100% by hand, with no AI assistance. Given that the specification and compiler were developed with heavy AI involvement, solving real problems in the language unaided is the deliberate counterweight — and the honest read on whether FC is actually pleasant for a human to write.
Requires a C11 compiler (GCC, Clang, etc.).
make # build the fcc compiler (release, -O2)
make dev # clean rebuild at -O0 for clearer diagnostics during development
make clean # remove build artifactsThis produces the fcc binary at ./build/<os>/fcc (where <os> is linux, windows, or macos; fcc.exe on Windows). The per-OS subdirectory lets a shared source tree across two operating systems — e.g. WSL Linux + MSYS2 on the same Windows box accessing the WSL filesystem via \\wsl.localhost\... — hold both binaries without one stomping the other. make print-bin echoes the path for scripts.
make defaults to -O2; override with OPT= (e.g. make OPT=-O0 or make OPT="-O0 -fsanitize=address,undefined"). make clean is required when switching OPT values since Make doesn't track CFLAGS changes.
fcc follows the GNU coding-standards install conventions:
sudo make install # installs to /usr/local by default
make install PREFIX=$HOME/.local # user-local install (no sudo)
make uninstall # removeThe default install layout (with PREFIX=/usr/local):
/usr/local/bin/fcc # the compiler
/usr/local/share/fcc/stdlib/*.fc # the standard library
PREFIX, DESTDIR, bindir, and datadir are all overridable per the GNU conventions, so distro/package builds (PREFIX=/usr DESTDIR=/build/staging make install) work out of the box.
Caveat: until
fccgrows automatic stdlib path resolution, you currently need to pass stdlib files explicitly on the command line (e.g.fcc /usr/local/share/fcc/stdlib/*.fc your-program.fc).
After make install, fcc is on $PATH:
fcc input.fc # compile to input.c
fcc input.fc -o output.c # compile to a specific output file
fcc --version # or -V — print version and build infoThe compiler transpiles .fc source to a .c file. To build and run the result:
fcc hello.fc -o hello.c
cc -std=c11 -o hello hello.c
./helloFrom the source tree before installing, run the just-built binary with $(make -s print-bin) input.fc, or use ./run.sh to compile and execute in one shot (see Quick Run below).
fcc --version (and the short form fcc -V) prints three lines: a SemVer-prefixed identifier with the commit hash and commit date, the auto-detected target triple, and the build environment.
fcc 1.0.0-rc.7 (abcdef012345 26.05.03)
Target: linux x86_64 gnu
Built: 2026-05-03 with -O2 (cc 13.3.0)
The 1.0.0-rc.7 prefix is hand-maintained in the VERSION file at the repo root. Everything in the parentheses is derived at build time from git: a 12-character commit hash, the commit date in UTC yy.mm.dd form, and a -dirty suffix when the working tree has uncommitted changes (the resulting binary is intentionally not commit-stable in that case). Outside a git checkout (tarball builds) the parenthetical falls back to (nogit unknown).
run.sh compiles an FC file, links it with the standard library, runs the binary, and prints the exit code:
./run.sh hello.fc # compile + run
./run.sh --flag debug hello.fc # compile with a flag enabled
./run.sh main.fc lib.fc # compile multiple files togethermake check # run full test suite (alias of test-all)
make test-all # run all tests with both gcc and clang
make test-gcc # run all tests with gcc
make test-clang # run all tests with clang
make test-gcc FILTER=closures # run only tests matching a pattern
make test-gcc FILTER=stdlib/data # patterns match against category/test_nameTests live in tests/cases/, organized into subdirectories by category (e.g., expressions/, structs/, generics/, modules/, etc.).
Single-file tests are an .fc file optionally paired with:
.expected_exit— expected exit code (0–255). If omitted, the expected exit code is 0..error— expected compiler error message (substring match); the test must fail to compile.
Most tests use assert (which calls abort(), exit code 134) for correctness checks and omit .expected_exit, so a passing test simply exits 0.
Multi-file tests use a subdirectory within a category dir, containing multiple .fc files plus an optional expected_exit or error file (no dot prefix), and an optional deps file listing external dependencies (one per line, e.g., stdlib/io.fc). Use subdirectories for tests that need multiple source files or dependencies.
The test runner (tests/run_tests.sh) compiles each FC file to C, compiles the C with -Wall -Werror, runs the binary, and checks the result. All intermediate files go into a system temp directory that is automatically cleaned up on exit.
src/— The compiler, written in C11. Pipeline: lexer → parser → pass1 (declaration collection) → pass2 (type checking) → monomorphization → codegen (C11 emission).stdlib/— Standard library modules (std::io,std::sys, etc.), written in FC.spec/— Language specification (fc-spec.html, best viewed in a browser) and formal grammar.tests/cases/— Integration tests organized by functional areas.FEATURES.md— Complete inventory of language and tooling features.
For code examples, see the full language specification in spec/fc-spec.html.
BSD 2-Clause. See LICENSE.