Build and run
fels build, fels run hello.fels, and a build file that is a Fels program too.
A zone is an arena with a name and a block. Whatever you allocate in it lives until the block
ends, and then all of it goes away in one step. You never call free, and nothing runs in the
background to clean up after you.
/// How many different bytes are in `text`?count_distinct :: fn(text: []const u8) -> usize { zone scratch: Arena { seen := scratch.new_slice(bool, 256); for i in 0..256 { seen[i] = false; } n: usize = 0; for b in text { if !seen[b] { seen[b] = true; n += 1; } } return n; } // `seen` is gone here, all at once}The compiler keeps track of what belongs to a zone, so nothing slips out of its block:
error: zone-owned value from `scratch` cannot be returned --> escape.fels:4:9 │4 │ return buf; │ ^^^^^^^^^^^A function that can fail says so in its type. fail returns an error, ? passes it on and
catch handles it. There are no exceptions, and nothing unwinds the stack.
ParseError :: errors { empty, not_a_digit: u8,}
parse :: fn(s: []const u8) -> i32 ! ParseError { if s.len == 0 { fail .empty; } n := 0; for c in s { if c < '0' || c > '9' { fail .not_a_digit(c); } n = n * 10 + (c - '0') as i32; } return n;}
main :: fn() -> i32 { a := parse("42") catch err { yield 0; }; return a;}#run calls a function while your program is being compiled, and the result goes into the
binary as a constant. Macros and code quotes write code that the compiler checks like code you
typed yourself.
fib :: fn(n: u64) -> u64 { if n < 2 { return n; } return fib(n - 1) + fib(n - 2);}
FIB_30 :: #run fib(30); // worked out while compilingBuild and run
fels build, fels run hello.fels, and a build file that is a Fels program too.
Tests
fels test runs every #test function. --filter picks some of them.
Packages
fels add fetches a package from a Git repository, and a lock file pins its version.
Editors
fels lsp gives errors, completion, hover and go to definition. There is a Zed extension
and a tree-sitter grammar.
C without glue
fels bindgen turns a C header into Fels. #extern calls a DLL, #export hands your
functions to C.
Its own linker
felsld is written in Fels, writes Windows executables and DLLs, and links itself.
The imgui package brings Dear ImGui and ImPlot. This live chart is about 40 lines of Fels.
Fels is young, and the language still changes. It runs on 64-bit Windows and Linux, builds programs for both, and can make UEFI applications and kernels for x86-64 and 64-bit ARM. The known issues list what is still rough, and the changelog what changed in each release.