The shell
Run loom with no arguments and it reads lines. That is the whole
of it: a prompt, a short list of commands, and a rule about the file
extension.
$ 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 ▸ exitThe commands
| Line | What it does |
|---|---|
run PROGRAM.lc [ARGS] | Opens a compiled artifact and calls it — Running programs |
luce PROGRAM.luc [ARGS] | Compiles a source file if needed, then runs it |
edit FILE | Opens the editor on one file |
clear | Clears the screen |
help | Lists the rest — a test in the repository fails if it ever stops naming one of them |
exit or quit | Leaves |
PROGRAM.lc / PROGRAM.luc | A bare path runs directly: the sugar for the two commands above |
Anything else is named back to you, so a typo reads as a typo rather than as a missing feature:
loom: unknown command rnu (try help)
edit takes exactly one file, and a second one is refused rather
than ignored: an editor opening the wrong file is worse than one that does not
open.
What the shell deliberately does not have
No variables. No history file. No configuration. No pipes, no redirection, no
globbing, no cd. No aliases and no prompt customisation.
This is not a list of things that have not been got to yet. Everything durable belongs to the programs and to the filesystem, not to the shell. loom's job is starting programs; a shell that accumulated state would become a thing you have to manage, back up, and reason about when a program misbehaves. Where a real shell is what you want, loom's command forms are exactly the ones you type into it:
$ loom run build/programs/wordcount.lc hello.luc 11 distinct words in hello.luc 2 args 2 print 2 name 1 func 1 main $ loom run build/programs/wordcount.lc hello.luc > counts.txt
Colour, and when there is none
The shell is coloured when a terminal is reading it and plain when anything else is. One palette travels with the shell, and when it is disabled every code renders as the empty string — so the printing code never branches on colour support and there is no path where half a message is escaped and half is not.
Colour is off when standard output is not a terminal, or when
NO_COLOR is set to anything at all. That rule reaches further than
it looks: clear writes its escape sequence only where escape
sequences are read, because a clear in a piped script would
otherwise put one in somebody's log file.
Failure text is never coloured. It is written on standard error, which may not be the same terminal standard output is.
A person and a script are different about failure
loom reads lines the same way whether they come from a keyboard or a pipe, but it exits differently, and the difference is deliberate.
An interactive loom always exits 0. A person at a prompt has already been told that a program failed — the trap is on their screen — and wants the prompt back. A shell that exited nonzero because of something typed an hour ago would be reporting on the person rather than on a program.
A script piped into loom exits with the worst status any line
produced. Nobody is watching it. echo "run boom.lc" | loom
inside a build is the only thing that will ever know that the program did not
run, so the worst thing any line did becomes loom's own status.
$ printf 'help\nrun no/such/program.lc\nhelp\n' | loom ; echo "status=$?"
loom: cannot read no/such/program.lc: no such file
status=1The shell keeps going after a failed line — it is not a set -e
script — but it remembers. An exit in the middle does not wipe what
came before it, and a typo counts too: a command that was not recognised is a
command that did not run.
One line, one program
A command line is split on spaces and tabs, and that is the entire parser.
There are no quotes and no escapes, so an argument with a space in it is a thing
to pass through a real shell — loom run calc.lc "2 + 3 * (10 - 4)" —
rather than something loom pretends to handle.
There is a cap on how many words a line may have, and a line that reached it is refused rather than truncated:
loom: too many arguments
Running the part that fits would be running a different command from the one that was typed, which is the one kind of failure a person does not think to look for.
While a program is running
Nothing bounds how long a program runs. An interactive one blocks on
key_read for as long as it likes, and loom is not a supervisor: it
made the call and it is waiting for it to return. What it does guarantee is the
other side of that — the screen is restored before any trap is
reported, so a full-screen program that crashed hands the terminal back
in the state it borrowed it in, and its message lands where you can read it.
Runaway recursion is caught. Call depth is policy rather than a native-stack
accident: generated code carries how many frames it may still take, and traps
call_depth_exceeded at the call that would exhaust it. loom sets that
number — conservatively, deep enough for any reasonable program and shallow
enough that a runaway one reports promptly and well inside the machine's own
stack.