Luce Base
LuceEngineeringLuciaOS

Luce Base

The language design

Status: design draft, revision 3, 2026-09-02. Not yet implemented. This document is complete on its own: it states every rule of the language and the reason for it, and it does not require reading the Luce 1.0 specification. Where a rule is shared with full Luce, this document says so once and states the rule anyway.

Luce Base is C, reorganised. It keeps what makes C the language that everything else is written in: values with predictable layout, pointers, manual memory, a plain calling convention, and no runtime to speak of. It replaces the parts of C that exist only because C is fifty years old: header files, the preprocessor, null, integer promotion, switch fallthrough, return-code error handling, and void* generics.

A Base program is made of structs with functions and initialisers inside them, modules instead of headers, generics instead of macros, tagged unions with exhaustive matching, interfaces, defer, optionals instead of null, and a fallible result type instead of return codes. Memory is managed by hand through explicit allocators. There is no reference counting, no garbage collector, no hidden allocation, and no runtime beyond a trap reporter and program startup.

Base is a profile of the Luce language, compiled by the same compiler as full Luce. A Base module is a file ending in .lucb. Full Luce, with its reference-counted classes and collections, can import a Base module as an ordinary module; Chapter 18 states that contract. A Base program that never touches full Luce needs nothing from it.

The three sentences that summarise the design:

Base gives up nothing that portable C can do. Where a C capability is unsafe, Base admits it with a restriction that makes it checkable; it never omits it.

Values copy. Pointers point and are never null unless they say so. Spans carry their length. Allocators are explicit. Arithmetic, bounds, and shifts are checked. Failure is visible. Nothing runs that you did not write.

A better C that reads like Python, calls C in both directions with no glue, and is the language Luce's own runtime is written in.

1.1 How to read this document#

Each chapter states its rules first, then the reasons under the heading Why. The rules are normative. The reasons are there so that the next person to change a rule knows what it was protecting. Chapter 21 is the grammar; Chapter 22 is a one-page translation table from C; Chapter 23 lists the handful of places where Base and full Luce differ and why.

Code in this document is Base source unless marked otherwise. A fenced block marked c is C, shown for comparison.

1.2 What it looks like#

struct Cursor:
    var data: const u8[]
    var offset: usize

    mutating func advance(self, count: usize) -> unit!:
        if self.offset + count > self.data.length:
            error(cursor.past_end, "advance past the end of the input")
        self.offset += count

    func remaining(self) -> const u8[]:
        return self.data[self.offset..]

func first_line(text: str) -> str:
    var cursor = Cursor(data = text.bytes(), offset = 0)
    while let byte = cursor.remaining().first():
        if byte == u8('\n'): break
        try cursor.advance(1) catch failure:
            recover ()
    return str.from_bytes_unchecked(text.bytes()[0..<cursor.offset])

Everything in that example has a C counterpart and costs what the C would cost. The span const u8[] is a pointer and a length. offset + count traps on overflow instead of wrapping. error(...) returns a two-word error value; try checks it. Nothing allocates.

Contents#

1. IntroductionLuce Base is C, reorganised. It keeps what makes C the language that everything else is written in: values with predictable layout, pointers, manual memory, a plain calling...2. Design principlesThese are the tests every rule in the rest of the document had to pass.3. Source textSource is UTF-8. A byte-order mark is accepted and ignored only at byte zero. NUL bytes, invalid UTF-8, misleading bidirectional control characters, and look-alike punctuation...4. Literalstrue, false, and none. none takes its optional type from context; it is never a universal null.5. TypesBase is statically and nominally typed. Every expression has one type. Inference is local: from an initialiser to its binding, from arguments to generic parameters, from a...6. Bindings and initialisationlet width = 1920 let title: str = "Preview" var frame = 0 frame += 17. ExpressionsLeft to right, always: receiver then arguments, operands, array elements, interpolation fields, constructor arguments. A backend may reorder only when traps and side effects...8. Control flowControl flow is structured. Base has if, while, for, match, labeled break and continue, return, defer, errdefer, and inline assembly. It has no exceptions and, in this...9. Functions and methodspub func clamp(value: f64, minimum: f64, maximum: f64) -> f64: if value < minimum: return minimum if value > maximum: return maximum return value10. Structs, enums, and unionspub struct Style: pub let color: Color = Color(0.0, 0.0, 0.0, 1.0) pub let line_width: f64 = 1.0 var cache: Layout*?11. Absence, failure, and trapsBase separates three conditions: absence is data, T?; recoverable failure crosses a function boundary as T! with an Error; a trap is a violated invariant and is not catchable....12. MemoryNo Base operation allocates. There are no built-in collections; a list, map, or string builder is a library type that takes an Allocator. Every allocation is a call the...13. Genericsfunc firstT -> T?: if values.length == 0: return none return values[0]14. Interfacespub interface Writer: mutating func write(self, bytes: const u8[]) -> usize!15. Atomics and volatilevar ready: @bool var hits: @u64 var head: @Node*?16. Modules, packages, and testsOne file is one module; its path is its package-relative path: src/image/color.lucb is image.color. There is no module declaration and no re-export. Module cycles are errors....17. Calling CBase is the layer C bindings are written in. There is no marshalling: a Base pointer is a C pointer, a Base struct is a C struct, cstr is char*. What full Luce needs three...18. Working with full LuceThis chapter is the contract between a full Luce program and the Base modules it imports. It is written so that the compiler enforces every rule and so that a reader of either...19. Compilation and runtimeSource is tokenised, laid out, parsed, resolved, and typed into the same typed intermediate representation as full Luce, then lowered to the canonical machine representation...20. ExclusionsAbsent from Base, each with the reason it is not a loss:21. GrammarRepetition is {...}, optional syntax is [...], quoted text is a token. NEWLINE, INDENT, and DEDENT come from the layout lexer; RAW_LINE is a physical line captured without...22. C to Baseint *p = &x; let p: i32* = &x const T *p p: const T* T *const *pp pp: const (T*)* volatile uint32_t *reg reg: volatile...23. Relationship to Luce 1.0Base is a profile of Luce, and this document restates every shared rule so that it can be read alone. For a reader who knows full Luce, these are the differences, and the...