loom
Luce LuciaOS

loom

loom is the environment that runs compiled Luce. It opens a program, hands it the world, and gets out of the way. There is no engine inside it, no code generator, and no state of its own — ordinary files, the real terminal, and one honest boundary.

Luce is the language. luce is its compiler. loom is where a Luce program is a program — the thing you type at, the thing that starts it, and the thing it asks when it wants to print a line or read a key.

Thirty seconds

Write a program, run it, look at what appeared beside it.

hello.luc
func main(args: list(string)):
    print("hello, loom")
    for name in args:
        print(name)
Shell
$ loom luce hello.luc world
hello, loom
world
$ ls
hello.lc	hello.luc
$ loom run hello.lc one two
hello, loom
one
two

loom luce compiled the source and ran it. What it left behind — hello.lc — is machine code: a tagged shared library. loom run hello.lc is one dlopen, one symbol lookup, one call. No compiler is consulted, nothing is decoded, and the second run does not need the source file at all. It takes milliseconds, and almost all of them are the operating system's.

Run loom with no arguments and you get the same commands at a prompt instead.

Shell
$ loom
loom — the luce environment. help lists commands.
loom ▸ help
  run PROGRAM.lc [ARGS]   run a compiled Luce program
  luce PROGRAM.luc [ARGS] compile and run a Luce source file
  edit FILE              open the Luce editor (LOOM_EDITOR overrides)
  clear                  clear the screen
  exit                   leave loom
  a bare PROGRAM.lc or .luc path runs it directly
loom ▸ exit

The four parts

loom is one small binary made of four things, and this site has a page for each of them.

What loom is not

Most of what makes loom fast is what is missing from it.

  • No engine. There is no interpreter and no bytecode inside loom. A .lc is machine code and loom calls it. (The interpreter exists, but it ships in nothing: it is the differential oracle the test suite compares the compiled path against.)
  • No code generator. loom links no LLVM at all. Building is luce's job, and loom runs that binary when it needs a program compiled. A machine that only runs Luce programs needs no LLVM installed and, once the artifacts are built, no luce either.
  • No disk image, no database, no registry. Programs read and write ordinary OS files. There is nothing to initialise, nothing to migrate, and nothing loom keeps between runs except the artifact cache — which is content-keyed and safe to delete at any time.
  • No state of its own. The shell has no variables, no history file, no configuration and no cd. Everything durable belongs to the programs and to the filesystem.

What loom does keep is a promise: a program's behaviour must not depend on who started it. A program run under loom run, a standalone binary built with luce build --emit=exe, and the same program under the test oracle all report a failure in the same words and exit with the same number.

Programs to run

The repository ships userland written in Luce, compiled and linked by the freshly built compiler on every build, so none of it can quietly rot:

ProgramWhat it is
editor.lcThe full-screen editor this site has a page about
adventure.lcA text adventure in five files — the first Luce program written as a project, with a save file
life.lcConway's Game of Life on a multi-dimensional array, drawn on the terminal
calc.lcA recursive-descent expression calculator, and the worked example for recoverable errors
bf.lc, dice.lc, sort.lc, stats.lc, wordcount.lc, hello.lc Smaller programs, each one proving something the language can do
Shell
$ loom run build/programs/life.lc

Building it

loom is built from the LuciaOS repository along with the compiler, in one command:

Shell
$ ./build.sh
$ ls build
lib	loom	luce	programs

Everything is Zig 0.16. LLVM is a build prerequisite of luce and of nothing else — the code generator calls libLLVM in process — and a C compiler driver (cc) is a prerequisite of building at all, because producing a compiled program is a link. loom itself links neither.

The build installs build/loom, build/luce, build/lib/libluce_rt.a (Luce's semantics as a linkable library, which every compiled artifact carries) and build/programs/*.lc. Put build/ on your PATH and the two binaries find each other: loom looks for the compiler beside its own executable first and on PATH after, which is how a toolchain finds its tools.


The language itself — types, ownership, failure, the standard library — is documented at luce.luciaos.com. This site is about the tool that runs it, and about where that tool is going.