Hello, Sysl
import { Tabs, TabItem, Steps, Aside } from ‘@astrojs/starlight/components’;
Create a file called hello.sysl:
// Every executable Sysl program exports a function called `main` that returns// an `int` — the process exit code. Strings print via `puts`; expressions// print via `println`.
main() -> int puts("hello, sysl") 0That’s it. There’s no preamble, no stdio.h, no module declaration required for a standalone
file. puts is a builtin; so are print, println, putchar, len, append, and a
handful of others.
Running it
Section titled “Running it”sbt "syslCliJVM/run run hello.sysl"Output:
hello, syslThe interpreter is single-threaded and walks the typed AST directly. It’s fast enough for development and slow enough that you’ll want to move to one of the native backends for real work.
sbt "syslCliJVM/run compile hello.sysl --emit tof -o /tmp/hello.tof"sbt "triscCliJVM/run run /tmp/hello.tof"Output:
hello, syslTRISC is a 16-bit RISC teaching ISA — five instruction formats, a couple dozen instructions, deterministic cycle counts. Kernels, drivers, and whole operating systems fit inside well under a megabyte.
sbt "syslCliJVM/run compile hello.sysl --backend llvm --emit ll -o /tmp/hello.ll"clang /tmp/hello.ll -o /tmp/hello/tmp/helloOutput:
hello, syslThe LLVM backend emits IR that goes through the standard optimisation pipeline. Compile times are clang’s, runtime performance is clang’s, and the binary has no Sysl runtime dependency beyond a handful of intrinsics.
What the compiler produced
Section titled “What the compiler produced”Under the hood, puts("hello, sysl") compiles to:
- A read-only string constant holding the twelve bytes
hello, sysl\0. - A call to the runtime’s
putssymbol with the string’s fat pointer{ptr: *u8, len: i64}packed into registers. return 0in the calling convention’s return slot.
Everything else — string layout, the (ptr, len) calling convention, the main symbol, the
return path — is the same across all three backends. The same source file produces bytecode
for the interpreter, TRISC assembly, and LLVM IR without changing a line.
A slightly bigger hello
Section titled “A slightly bigger hello”Interpolated strings (s"..."), format strings (f"..."), and generics all work on every
backend too:
greet[T](name: T) = puts(s"hello, $name")
main() -> int greet("world") greet(42) // auto-converted via str() greet(3.14)
val version = 1 val build = 0xBEEF puts(f"sysl v$version.0 build $build%04x")
0Output:
hello, worldhello, 42hello, 3.140000sysl v1.0 build beefThe greet[T] function is monomorphised — one copy per concrete T — exactly like Go or
Rust. There is no boxing, no interface dispatch, no heap allocation for the call.
Where to go next
Section titled “Where to go next”- Tour of the language — walk through every major feature with runnable code.
- Functions — block bodies, expression bodies, defaults, named arguments, contracts, closures, generics.
- Standard library — what you get for free.