Welcome to Pie
Pie is a small, experimental programming language with a JIT compiler built on LLVM. It is currently in early alpha; breaking changes and missing features are expected.
- Status: alpha; type checker and stdlib evolving
- Runtime: LLVM JIT + a small GC-like runtime with reference counting helpers
- This book: a practical guide to get you writing Pie quickly
Getting Started
Prerequisites:
- Rust toolchain (stable)
- LLVM 17 installed and discoverable by the
inkwellcrate- On some systems you may need to set
LLVM_SYS_170_PREFIXto your LLVM install prefix
- On some systems you may need to set
Build and run from the Pie crate directory:
cd pie/pie
cargo run --release -- ../examples/working_example.pie
You should see output similar to:
- Executing program...
- Program finished with exit code: 0
Run another example (HTTP server):
cargo run --release -- ../examples/http_test.pie
Then open http://localhost:8080/ in your browser.
CLI usage:
pie <file.pie>
Optimization:
- Pass
-O,-O1,-O2, or-O3as an extra argument to select JIT optimization level.
Language Tour
Basics:
- Declarations:
let <type> <name> = <expr>; - Functions:
def <ret> <name>(<type> <arg>, ...) { ... } - Modules:
module Name { ... }(nestable) - Imports:
use path/to/file;anduse pie/std;
Types:
- Built-ins:
int,float,bool,string,list,map,void - Structs:
module demo {
struct Person { name: string, age: int }
}
Expressions and statements:
- Literals: numbers, strings "...", chars 'a', lists
[1, 2], maps{ "k": 1, "v": 2 } - Arithmetic and comparisons:
+ - * / % == != < <= > >= && || & | ^ - Assignment and compound assignment:
= += -= *= /= - Control flow:
if { ... } elif { ... } else { ... },while { ... },for x in <iter> { ... } - Calls and qualification:
std::print("hi"),lib::math::square(7)
Example:
use pie/std;
module demo { struct Person { name: string, age: int } }
def void main() {
let int x = 7 * 7;
std::print("square(7): " + x);
let Person p = demo::Person { name: "Ada", age: 37 };
std::print(p.name);
}
Iteration:
use pie/std;
def void main() {
let any it = std::iter::range(0, 5, 1);
for i in it { std::print(i); }
}
Notes:
- Strings + numbers concatenate via
+. - Many stdlib operations return
nullon error; check withstd::is_null(x).
Modules and Imports
Imports:
use pie/std;enables the standard library. This import is handled specially and not inlined into your program.use path/to/module;inlinespath/to/module.pieat compile time. Paths are resolved relative to the current file.
Modules:
module math {
def int add(int a, int b) { return a + b; }
}
module app {
def void main() { std::print(math::add(2, 3)); }
}
Qualification:
- Use
Module::Nameto refer to items inside modules. - Member access with
.is parsed but currently has dynamic/"any" typing; prefer module paths for named items.
Entry point:
- Top-level
def,let, andstructare automatically wrapped into an implicitmainmodule.
Standard Library
Import with:
use pie/std;
Modules and highlights:
- std::print, std::print_err, std::args
- std::fs::{read, write}
- std::to_string, std::to_int
- std::list::{new, len, get, set, push, pop, remove, add_in_place}
- std::map::{new, set, get, to_json, from_json}
- std::http::{get, post, put, delete, request, serve, serve_routes}
- std::iter::{new, next, range}
- std::rand::{int, int_range, float, float_range, ratio}
- std::num::{add_in_place, sub_in_place, mul_in_place, div_in_place}
- std::is_null, std::rc::{inc_ref, dec_ref}
See subpages for details and examples.
IO
- std::print(any val): prints to stdout.
- std::print_err(any val): prints to stderr.
- std::args() -> list: returns CLI args as a list of strings.
Filesystem:
- std::fs::read(string path) -> string | null: reads a file. Returns
nullon error. - std::fs::write(string path, string contents) -> bool: writes a file. Returns
trueon success.
Example:
use pie/std;
def void main() {
std::print("Hello, world");
let list args = std::args();
std::print(args);
}
Lists
- std::list::new() -> list
- std::list::len(list) -> int
- std::list::get(list, int idx) -> any | null
- std::list::set(list, int idx, any val)
- std::list::push(list, any val)
- std::list::pop(list) -> any | null
- std::list::remove(list, int idx) -> any | null
- std::list::add_in_place(list, int idx, int delta): adds to an int element in place
Example:
use pie/std;
def void main() {
let list xs = std::list::new();
std::list::push(xs, 1);
std::list::push(xs, 2);
std::print(std::list::len(xs));
}
Maps
- std::map::new() -> map
- std::map::set(map, any key, any val): keys are stringified
- std::map::get(map, any key) -> any | null
- std::map::to_json(map) -> string
- std::map::from_json(string) -> map | null
Example:
use pie/std;
def void main() {
let map m = std::map::new();
std::map::set(m, "a", 1);
std::print(std::map::get(m, "a"));
}
Strings
- std::to_string(any) -> string: converts a value to a string.
- std::to_int(any) -> int | null: parses/convert to int where possible (string, int, bool).
Notes:
- Pie concatenates strings with numbers via
+.
Example:
use pie/std;
def void main() {
std::print("num: " + 42);
let int x = std::to_int("12");
std::print(x);
}
HTTP
Methods:
- std::http::get() -> string ("GET"), similarly
post,put,delete.
Requests:
- std::http::request(string method, string url, map headers, string body) -> string
- Returns response body or an error string like
"http error: ...".
- Returns response body or an error string like
Servers:
- std::http::serve(int port, string body): serves constant body on all routes.
- std::http::serve_routes(int port, map routes): dynamic routing via a map.
- Route values can be a string body or a map with keys:
status(int),headers(map),body(string). - Route keys:
"GET /", or just"/"as fallback. Trailing slashes are normalized.
- Route values can be a string body or a map with keys:
Example:
use pie/std;
def void main() {
let map routes = std::map::new();
let map home = std::map::new();
std::map::set(home, "status", 200);
std::map::set(home, "body", "Hello from PIE");
std::map::set(routes, "GET /", home);
std::http::serve_routes(8080, routes);
}
Iterators
- std::iter::new(any x) -> iterator | null: wraps a string, list, or map into an iterator.
- std::iter::next(iterator) -> any | null: advances the iterator.
- std::iter::range(int start, int stop, int step) -> iterator: numeric range.
Use with for:
use pie/std;
def void main() {
let any it = std::iter::range(0, 3, 1);
for i in it { std::print(i); }
}
Random
- std::rand::int() -> int
- std::rand::int_range(int min, int max) -> int | null
- std::rand::float() -> float
- std::rand::float_range(float min, float max) -> float | null
- std::rand::ratio(int num, int denom) -> bool | null
Reference Counting and Nulls
- std::is_null(any) -> bool: tests if a value is
null. - std::rc::inc_ref(any) and std::rc::dec_ref(any): low-level refcount adjustments. You typically do not need these in normal code.
Note:
- Many stdlib functions return
nullto indicate failure (e.g., out-of-range, parse errors).
Examples
See the repository examples/ directory. A few highlights:
working_example.pie:
use pie/std;
use lib/math;
module demo {
struct Person { name: string, age: int }
}
def void main() {
std::print("square(7) from lib/math: " + lib::math::square(7));
let Person p = demo::Person { name: "Ada", age: 37 };
std::print(p.name);
}
http_test.pie: minimal web server with route map.
Contributing
Contributions are welcome. Please reach out before large changes. Contact: Discord _nonnewport_.
- Rustfmt and clippy are appreciated
- Keep code readable and small PRs when possible
- Add or update examples and book pages when you introduce new language features or stdlib functions
License
Pie is distributed under the MIT license. See LICENSE at the repository root.