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 inkwell crate
    • On some systems you may need to set LLVM_SYS_170_PREFIX to your LLVM install prefix

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 -O3 as 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; and use 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 null on error; check with std::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; inlines path/to/module.pie at 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::Name to 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, and struct are automatically wrapped into an implicit main module.

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 null on error.
  • std::fs::write(string path, string contents) -> bool: writes a file. Returns true on 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: ...".

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.

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 null to 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.