ox

The Ox programming language, compiler and tools (WIP)
Log | Files | Refs | README | LICENSE

oxdesign.ox (6323B)



i64  a = 42; // like a "let" statement or final variable, runtime constant.
#i32 X = 42; // compile time constant
~i64 z = 42; // make a integer 32bit that is a variable (can be updated) `~` initialised with 2.

z = 67;

str b = str("some string somehow");
chr c = 'c';

arr<i32>[14] = arr[1, 2, 3];
set<str> names = {};

fx add (x i32, y i32) i32 {
    return x + y;
}

fx flip(a some, b some) (some, some) => b, a

fx add (i i32, j i32) i32 {
    return i + j;
}

struct Person {
    
}

fx main() {
    print("Hello, world!");
}

fx fetch_url(str url) str?! async {
    str res = await fetch(url);
    return res;
}

// default types
// i8, i16, i32, i64,
// u8, u16, u32, u64,
// f8, f16, f32, f64,
// str, chr, bool,

// arrays (fixed) and lists (dynamic)
arr<i32>[16] ages;  // fixed slice of 16
vec<i32> bobs;      // dynamic list
set<str> names;     // sets

struct<Person> {
    i32 age;
    str name;
}

fn say_hello () void {
    print("hello");     // print is part of stdlib.local which is auto imported
    warn("hello");      // this print to stderr instead
    print_("hello\n");  // all of the above but with `_` won't insert a trailing \n
    fatal("hello");     // fatal print, returning non-zero
}

// strings are really arrays of special u32, aka vec<chr>
typedef chr u32; // (but u32 is not a rune/chr, as the whole UTF-16 range does not cover u32)

// exported function returns one i32
fx add (i32 a, b) i32 => a + b;

// fx = function is exported
// fn = function is static

fx passed (Person  p) {} // ownership passed and cannot be used after
fn borrow (Person& p) {} // borrowed and unmutable
fn mutate (Person~ p) {} // borrowed and mutable
fn unsafe (Person* p) unsafe {} // C style passing of pointer, must tag `unsafe`

// lambda style function definition
fx add = (i32 a, b) i32 => a + b;

// void return is implicit, so is void type arg
fx say_hello() {
    print("hello");
}

// multiple return values go into parens
fx flip(i32 a, b) (i32, i32) => b, a;

struct Person {
    str name,
    i16 age,
    i32 number,
    str street,
    str suburb,
    str postcode,
    str country,
    // string interpolation `$.` is identifying a type component
    fx address () str => "$.number, $.street, $.suburb $.postcode\n$.country"
}

extend Person {
    ...
}

// lambda style main function
fn main() void => print("hello world");

fn main(i32 argc, arr<str> argv) int {
    if argc != 0 {
        print("usage...");
        return 0;
    }
    print("hello world! $argc"); // string interpolation similar to Dart's
    return 0;
}

fn rename({str name}) str {} // enforce the label to be passed: rename(name:"jack")

// if statement don't have required parens, they're optional

if a == b {
    do_this();
} else if a == c {
    do_that();
} else {
    do_nothing();
}

// inline if can be done as is
if cache_miss() { print("cached missed!"); }

// ternary if
i32 bla = cond ? 42 : 420;

// for statements
// classic for statement use commas separated init, cond and modifier
for (i32 i = 0, i < 42, i++) {}

for i32 i in 0..< 42 {} // shorthand for loop with for each

for chr c in word {} // for each .. in

for (chr c, i32 i) in word {} // for each in with index, parens optional

// while loops
while cond == true {
    // body
}

// infinite loop until broken
loop {}

// switch
switch (action.key) {
    KEY_ENTER {
        open_door();
    }
    KEY_UP { move_up(); }
    default {
        warn("unsupported!")
    }
}

// match expr
chr c = match key {
    61 => 'a';
    62 => 'b';
    default => '_';
}


// dealing with nil values. They're not allowed unless unsafe in play.
Person? p = find("jack");

if Person x = p {
    print("found jack of ${x.age} age");  // safe usage
}

if p {
    print("found jack of ${p!.age} age"); // force unwrap
}

Person* p = NULL; // is allowed. NULL is a constant 0xffffff of some kind.

// not allowed: Person&? Person~?

// null coalescing of maybes
print("jack may be of ${p?.age ?? \"some unknown\"}");

i32 res = some_test() ?? 42;

// namespacing

ns main;  // is default and not required, this unit will require a main function
ns tools; // loosely required to be in tools/

// using namespaces, importing, namespaces are forced lowercase to not impact types

use math;                       // bring all of math functions in, to be prefixed by math.something
use math { random, sin, cos };  // limit the import to these symbols, directly accessible: sin()
use math as mth;                // alias math to `mth`


// async operations using the Aloha assignment operator `~=`

i32? response = await some_long_action();

// error handling

fn fetch_data({str url}) async str! {
    // fetch the data of the website, parse the body content
    // return the body content
    await ...
    return content;
}

struct Error {
    u16 code,
    str message,
    Error? cause
}

str website_data = await try fetch_data(url: "fleacebook.com") or Error e => print("could not fetch data ${e.message}");


fx flip(i32 a, b) (i32, i32)! {

}


// APPENDIX

// keywords
// return, break, continue, if, else, for, while, loop, goto, defer, heap, free,
// type, ext, union, arr, vec, set, typedef, fx, fn, maybe, mut, ref, ptr, voidptr,
// unsafe, inline


// types
// all of the
// stdlib.local auto imported
// equivalent of `use always { print, print_, warn, warn_, fatal }`
// print, print_, warn, warn_, fatal

// FFI interaction with C and C types
// core FFI aliases
typedef voidptr = void*;
typedef cstr    = char*;
typedef ccstr   = const char*;

use math;
use c <stdio.h>;
use c <stdlib.h>;
use c <stdint.h>;
use c "include/termbox2.h";

extern c {
    fn printf(ccstr, ...) int;
    fn free(voidptr);
    fn malloc(size_t) voidptr;
    fn tb_print(i32, i32, u16, u16, cstr); // x, y, fg, bg, text

    // NOTES
    // don't use `const char*` etc. and warn against it at compile time
    // use cstr or ccstr instead to avoid the baggage of weird
    //     pointer precedence and const position
    //
}

fn main() {
    voidptr ptr = malloc(65128);
    // invalid: *ptr = 420 // voidptr should be undereferenceable
    // no deref of voidptr without a cast
    ptr<u8> bytes = cast(ptr<u8> p);
    bytes[0] = 42;
    free(ptr);

    // presume init called etc.
    tb_print(12, 12, TB_MAGENTA, TB_BLACK, c_const_str("some words of wisdom"));
}