Lyricdev
GitHub

Getting started

This chapter installs the toolchain, makes a package and runs it. Lyric compiles a program through C into one native binary, so besides the toolchain you need a C compiler.

Installing

Lyric 5 is in development; nothing is released yet. Every change to main is built into the dev prerelease on GitHub: https://github.com/lyriclang/lyric/releases/tag/dev. Take the archive for your system — Linux (x64 or ARM64), macOS (ARM64 or x64) or Windows (x64) — and unpack it where you keep tools:

mkdir -p ~/.local/lyric
tar -xzf lyric-5.0.0-dev.*-linux-x64.tar.gz -C ~/.local/lyric
export PATH="$HOME/.local/lyric:$PATH"

The archive holds the command lyric5, the standard library and the runtime's C source. Until the first release the command is called lyric5; this Book writes lyric, and every lyric here is lyric5 for now.

The C compiler is zig, version 0.16.0. lyric5 toolchain install zig fetches it from zig's mirrors, checks zig's signature and puts it under ~/.lyric, where lyric looks before your PATH; a zig, clang or gcc on the PATH works too, and LYRIC_CC names one over both. lyric5 --version says which one it found:

$ lyric5 --version
lyric 5.0.0-dev.20261023+6d7bd38
  edition     5
  c compiler  zig 0.16.0 (/usr/bin/zig)

The runtime comes prebuilt for your system. A build with another C compiler than zig 0.16.0, for another target, or with another collector or a sanitizer builds it from its source first, once; that build takes a little longer than the ones after it.

Several toolchains

lyric toolchain keeps toolchains side by side under ~/.lyric (LYRIC_HOME puts them elsewhere) and checks each before it is in place: the channel's list is signed, and every archive is checked against it. ~/.lyric/bin on your PATH runs the default:

lyric toolchain install stable     # or dev, or a version such as 5.0.0
lyric toolchain list               # the installed ones; --available, what the channels offer
lyric toolchain default 5.0.0
lyric toolchain update             # the newest of the default's channel, beside it

A package can name the least toolchain it needs, in lyric.toml:

[package]
name = "hello"
version = "0.1.0"
toolchain = "5.1"

A command in that package then runs with an installed toolchain that is 5.1 or later — the default where it is — whichever one you started; at a terminal, one is offered for installing where none is. LYRIC_TOOLCHAIN=local runs the one you started, and LYRIC_TOOLCHAIN=5.1.0 a version.

lyric toolchain asks the mirror first and GitHub after it. What one of them serves wrongly is said in a warning and the other asked: only a signature and a hash make anything acceptable, wherever it came from. LYRIC_MIRROR names a mirror of your own in place of both, with its key in LYRIC_MIRROR_KEY.

To check a first download by hand, before you have a lyric to do it for you, use minisign and the key of its channel:

dev      RWQM0eXpRYf8iCHBHUgYJPQOdE1oXy+fpGIov6Kz1PlP7Wp2llW+CCOv
stable   RWRzuPHBZrHnLIGQOgkuBJqmlH9C0P5rHYKFHlPd/8S1dreWrbTox657
reserve  RWShnZuifvfSSrKoQURRq3baivxxQTHuAXLJxOgsLU0Sz/R1JJ1P8RTH

A signature says that a file came out of the project's release pipeline. It says nothing about whether the code is good, and a toolchain trusts the keys it was built with for as long as it runs.

lyric env says what a build here reads and where each value comes from — the C compiler and zig (installed or PATH), the profile, and each lint's level with who set it (-D, [lints]).

Coming from Lyric 4

lyric fix --from-4 migrates a Lyric 4 program: run it in the directory of its lyric.json, or name one file. It reads the program with Lyric 4.6's own checker, so the program has to check with 4.6 first, then rewrites the files in place — the module headers go, the imports name Lyric 5's modules, lyric.json becomes lyric.toml, resume c becomes c.next()!, {avg:N2} becomes {avg:,.2f} — and lists in fix-from-4.txt what it leaves to you. --dry-run shows the changes as a diff first. Then lyric check says what is left.

A package

A program lives in a package: a directory with a manifest, lyric.toml, and its source under src/. lyric new makes one:

$ lyric new hello
created program package 'hello' in /home/you/hello

The manifest names the package and its version:

[package]
name = "hello"
version = "0.1.0"

and src/main.lyr is the program:

import std.io { println };

fn main(): void {
    println("Hello, world!");
}
Hello, world!

A program starts at main. println comes from the standard library's module std.io, and the import at the top names what the file takes from it. A statement ends with ;.

Building and running

In the package's directory, lyric run builds the program and runs it:

$ cd hello
$ lyric run
   Compiling hello v0.1.0 (src/main.lyr)
     Parsing 13 modules 0.21s
   Analyzing 13 modules 0.70s
  Evaluating compile-time sites 0.24s
    Lowering 10 functions 0.07s
    Emitting 3 C units 0.04s
   Compiling 3 of 3 C units of hello 0.31s
     Linking hello (out/debug/x86_64-linux-gnu/hello) 0.05s
    Finished debug [opt 0, debug info] in 1.72s
    Produced out/debug/x86_64-linux-gnu/hello (558.4 KB)
     Running out/debug/x86_64-linux-gnu/hello
Hello, world!

The lines before the greeting are the build's, on the standard error: each step that does work says what it works on and, when it is done, how long it took; Finished says how long it all took, Produced what came of it. Run it again and nothing has changed — no step is said, and a Summary says the C units came from the cache. In a log, where a line cannot be finished later, each step says its line as it starts and a Timings line gives the times at the end. The first build of a profile also compiles the runtime, once for every program on the machine. -v says more: each package, each C unit with its time, each command; -q says none of these lines, only the findings.

lyric build builds without running; the binary lands under out/, by profile and target — out/debug/x86_64-linux-gnu/hello on Linux. The default profile is debug: quick to build, with checks and debug information. --profile release builds the optimized program:

$ lyric build --profile release
$ ./out/release/x86_64-linux-gnu/hello
Hello, world!

lyric check says what a build would say about the program, without making a binary — the quickest way to hear about a mistake. The chapter What the compiler tells you reads what it says.

Debugging

A debug build carries debug information, and its C names a field and a local as the program does, so a debugger shows p.x, not a name the compiler made. lyric dap is the debug adapter an editor starts: it builds the program and hands it to LLVM's lldb-dap, which you install beside the toolchain (LYRIC_LLDB_DAP names one that is not on the PATH); a panic stops the program where it happens. In plain lldb, command source lldb/lyric.lldbinit from the toolchain's directory shows Lyric's strings, arrays and enums.

On Windows lyric dap is measured, not promised: LLVM 20's lldb-dap.exe needs the Python its lldb was built with (python310.dll) and, on the machines measured, ends as it starts even then. The program carries a PDB there, which Visual Studio's debugger reads.

Next, a first program that does a little more than greet.