System · shipped in 0.1.4.6
LibL
Loment's standard library, in C's shape: the same function names, the same argument order and the same return values as libc — for people who already know C. One library with two faces, not two libraries.
Available since 0.1.4.6
A release build accepts use libl as of 0.1.4.6, and a release
build can link the system's real libc.so.6 and run it end to end. The lock that
held both back in 0.1.4.5 is open, and the release note says so.
"Available" is not "complete". Every name here is checked against the system's glibc, and the set that is missing is written out below rather than left for a reader to discover at the link step. The page also keeps the reasons, because a face of libc without them is a promise nobody can audit.
Why a second face
The standard library already existed, and it contained no libc name at all
— not strlen, not printf, not malloc. That was
not an omission; it was a shape. Its functions take an explicit length rather than searching
for a terminator, address into a buffer with an offset, and never take a variable number of
arguments. There are no NUL-terminated strings in the language and no variadic functions in
it, so the library was written the way the language is.
That shape is right for people writing Loment, and it is a wall for people who know C. A
second face answers a different question: what would this be called if you had never
heard of Loment? strcmp, which returns the same thing C's does.
qsort. isdigit. Nothing to learn.
The two faces point at the same bytes. isdigit calls the
library's own character predicate; memcpy calls its own copy routine. So the
failure you would expect from two faces — the same byte answering differently
depending on which name you asked — is structurally impossible rather than merely
avoided.
Two ways in
LibL is the library's name; std is still the package's name.
Both entries reach the same package, and they differ in how much they pull in with them.
use std
The whole package. What everything written before LibL already does, and it keeps working. The cost is that a package-wide entry pulls every module into one unit, and this language's emitted-symbol space is flat — so all of those top-level names have to be unique together, and compile time grows faster than the module count.
use libl
The four libc faces and what they depend on — six modules. A small face: it compiles quickly and it has almost nothing to collide with. This is the entry to use when libc names are all you wanted.
What it covers
One libc header maps to one module, and that mapping is itself part of following libc.
<string.h>
strlen, strcpy, strncpy, strcat,
strcmp, strncmp, strchr, strrchr,
strstr, strspn, strcspn, strpbrk,
strtok_r, memchr, memcmp, memcpy,
memmove, memset.
<ctype.h>
isdigit, isalpha, isalnum,
isspace, isupper, islower,
isxdigit, ispunct, isprint, and the rest of the
set, with toupper and tolower.
<stdlib.h>
The half that can be built without a heap: atoi, atol,
strtol, strtoul, abs, labs,
llabs, qsort, bsearch.
<stdio.h>
The formatting half: snprintf, sprintf, and the rest of the
printf formats. The file half arrived in 0.1.4.6 as a separate
host module — fopen, fclose,
fread, fwrite, fseek, ftell,
fflush, feof, fgets, fputs,
puts, putchar, getchar — because those need
system calls, and the core library makes none.
Plus one function libc does not have, and it is the interesting one — see below.
The control is libc itself
Every other part of the standard library is judged by comparing what Loment computes against
what Python computes. That is the wrong control for this question, and provably so:
on exactly the edges that define "behaves like libc" — whether strncpy
zero-fills, whether strcmp returns a sign or a difference, whether
strchr finds the terminator, whether memmove is safe when the
ranges overlap — Python and libc are not the same answer. Comparing against Python
would let two implementations be wrong together and pass.
So the criterion compiles a C program against the system's real glibc, runs the same set of
calls on both sides, and compares standard output byte for byte, reporting
the first line that differs. Two normalisations are deliberate, because otherwise the test
would be measuring implementation details rather than semantics: the standard only promises
the sign of strcmp, so both the sign and the difference are checked;
and glibc's character predicates return a bitmask rather than 1, so both sides are
reduced to a yes-or-no before comparison.
What the language forced
The rule for this work was "however libc implements it, LibL implements it that way". The language is not C, so four places had to differ and one had to be added. Each one is written down rather than hidden.
A string literal carries no terminator
So strlen on a literal reads past the end. In the first probe it returned
17 for the empty string, having run on until it happened to meet a zero
byte. C does not need a function for this because "abc" in C already
carries its NUL. This is not an omission — the two str
types are not the same thing: this language's carries a length, C's does not.
LibL adds cstr_of to convert one into the other, and every NUL-string
function in the library needs it.
No variadic functions
So snprintf takes one more argument than libc's:
snprintf(buf, cap, fmt, args, nargs), where args points at an
array of 64-bit values. The conversion specifier still decides how each one is read,
exactly as in libc. This is the only signature in the library that
deviates, and it is not purely a loss: libc cannot count its arguments, so
giving it too few reads whatever is on the stack; here, too few means the conversion
stops and what was produced so far is finished properly.
No function pointers
So qsort's fourth argument is an enumeration saying what to compare by, not
a comparison function. That is a real difference and it is not a downgrade in one
direction: it moves "what does this sort compare by" out of a runtime address and into
a constant in the binary, so it is answerable without reading the
source. The price is stated with it: an ordering that is not one of the
built-in kinds cannot be expressed — you write the loop yourself.
No module-level mutable global
So strtok becomes strtok_r, with the cursor held by the
caller — which POSIX already offers, so this is not a downgrade. A
rand that keeps a hidden seed cannot exist either; the shape this language
uses takes the state from the caller, and it is deliberately not named
rand. And there is no errno: strtol saturates to
the boundary value and returns normally, so the caller compares against the boundary
instead.
What it is not
Not a replacement for libc
LibL is a source library, not an archive a C object file can be linked
against. C code cannot call into it; that needs a "Loment to object file" path, which
does not exist. What it replaces is narrower and worth saying precisely: a
Loment program having to hand-write strcmp.
Not the whole of libc
realloc and free are missing on purpose. realloc
needs the old length, and the old length lives in a heap header private to the
compiler; free's name is already taken by the builtin, so a library
function by that name would not resolve at the call site. malloc and
calloc are here as of 0.1.4.6 — spellings over the builtin heap with
no length header of their own, which is what lets the builtin free accept
what they return. stdin, stdout and stderr would
be module-level mutable globals, and the language has none. Beyond exit
and abort, nothing from the process or environment families. And
%f/%e/%g are not "not done" but "cannot be done
yet": the upstream formatter is software floating point whose parse direction needs a
wide division the layer deliberately does not have.
Not finished, and not pretending to be
Writing it turned up defects in the surrounding system — among them a helper whose overlap check compared offsets where libc compares addresses, a formatter that emits IR the system assembler rejects, and a dependency check that blames the wrong module. Those are filed as issues rather than fixed in passing, and the library's own criterion is deliberately narrow: it claims these modules are legal units, and it does not claim their dependencies are.