Systems code,checked before it runs.
Peeper is an experimental systems programming language. Its compiler checks types, paths, moves and borrows, then builds a native program through LLVM, on Linux, macOS and Windows.
1struct Point { x: i32, y: i32 }23fn distance(a: &Point, b: &Point) -> i32 {4 return (b.x - a.x) + (b.y - a.y);5}67fn main() {8 let start = Point.{ x = 1, y = 2 };9 let end: Point = .{ x = 4, y = 6 };10 println("distance:", distance(&start, &end));11}How it compiles
You write a loop.
Follow one line, total = total + i, from the source file to the processor. Every step shows what the compiler really wrote for this program.
Errors
Errors that point at the cause
When the compiler stops a program, it shows every place that matters and says why, so you fix the cause instead of the place it surfaced.
- Every label is a place in your code: where the reference was made, where the borrow starts, where the reference is used.
- A note says which rule it was, in plain words.
- Many errors carry a fix, which
peeper fixor your editor applies.
[T0037]: `count` is borrowed as `&mut` while a reference to it is still in use --> src/main.peep:8:12 | 6 | let mut count = 1;7 | let view = &count; | ------ reference made here8 | update(&mut count, 2); | ^^^^^^^^^^ borrowed as `&mut` here9 | println("count:", view.*); | ------ reference used later here | = note: a `&mut` argument takes hold when the call startsCompilation failed with 1 error(s)
What you get
A compiler that has your back
Peeper is young, but its checks are already strict. Here is what the compiler does for every program today.
Moves and borrows, checked
A struct moves when it is passed, and a
&mutborrow cannot overlap a reference still in use. The compiler finds both before the program runs.Constants at compile time
constvalues, and the expressions that use them, are worked out while compiling. Their types can come from where they are used.Checks that stay on
An index, a division or a shift the compiler cannot prove safe is checked when the program runs. A failure names the file, line and column.
Fixes, not only errors
Many errors carry a fix.
peeper fixapplies them, and an editor offers them as quick fixes.An editor that keeps up
The language server re-checks only the modules an edit affects, and keeps answering while the code is broken.
Native on three systems
Linux, macOS and Windows, each with its own pinned LLVM toolchain, so a build never depends on what is on your PATH.
The language
Small, explicit, readable
One loop keyword, structs that move, arrays that copy, and references you write out. Every sample here runs as shown.
1const Limit = 10; // its type comes from where it is used2const Answer: i32 = Limit * 4 + 2; // computed while compiling34fn square(x: i64) -> i64 {5 return x * x;6}78fn gcd(a: i64, b: i64) -> i64 {9 if b == 0 {10 return a;11 }12 return gcd(b, a % b);13}1415fn main() -> i32 {16 println("square:", square(Limit));17 println("gcd:", gcd(48, 18));18 return Answer - 42; // the exit code: 019}More in the language tour.
Get started
Build it in four commands
Peeper builds from source with its own LLVM toolchain, so nothing on your machine changes. On Windows, run pwsh scripts/setup.ps1 for the second step.
# get the source
$ git clone https://github.com/PeeperLanguage/compiler.git && cd compiler
# fetch the pinned LLVM toolchain
$ bash scripts/setup.sh
# build the compiler
$ bash scripts/build.sh
# run a program
$ build/bin/peeper run examples/01_functions.peep
- Linux x86-64
- Linux ARM64
- macOS ARM64
- Windows x86-64
Peeper is young. Help shape it.
The language changes quickly while it is pre-release. Read how the compiler works, try the examples, and open an issue for what you find.