laria icon indicating copy to clipboard operation
laria copied to clipboard

Laria is (... will be) a statically typed, embeddable scripting language inspired by Rust.

Laria

This is the repository for Laria, a (somewhat) statically typed (eventually) embeddable scripting language.

Currently, Laria is a very early work in progress, so it isn't quite ready for production use. The codebase may be quite messy, as the project is currently in the prototyping phase. Many oddities are likely placeholders or prototypes.

The syntax of Laria borrows heavily from Rust. Here is an example demonstrating my plans for the language, though note that most of this hasn't been implemented and is subject to change.

fn hello(what: string) {
    // By default, function parameters are passed by value.
    // However, you can easily require pass-by-reference by using
    // a `ref` type. For example, to say that `what` should be passed
    // by reference, you would write `what: ref string`.
    // References are currently implemented as reference-counted pointers
    // under the hood.

    println!("Hello, {}!", what);
}

struct Vec2 {
    x: f32,
    y: f32,
}

impl Vec2 {
    fn new(x: f32, y: f32) -> Self {
        // Like in Rust, static functions are represented by
        // omitting `self` from the parameter list.
        Self { x, y }
    }

    fn zero() -> Self {
        Self {
            x = 0.0,
            y = 0.0,
        }
    }

    fn length(ref self) -> f32 {
        // Laria has a special shorthand for `self` arguments, much like Rust.
        // Here, `ref self` is equivalent to `self: ref Vec2`.
        f32::sqrt(self.x * self.x + self.y * self.y)
    }

    fn normalize(ref mut self) {
        let length = self.length();
        self.x /= length;
        self.y /= length;
    }
}

// Generic parameters are denoted with square brackets.
enum Either[T, U] {
    Left(T),
    Right(U),
}

fn naive_pow(base: i32, exp: i32) -> i32 {
    let mut res = 1;

    for x in 0..exp {
        res *= base;
    }

    res
}

fn main() {
    // "Hello, world!"
    hello("world");

    let mut a = "world";
    // Laria currently uses clone semantics.
    let b = a;
    // `a` is modified, but `b` is not
    a = "hello";
    // "hello world"
    println!("{} {}", a, b);

    // Laria allows for variable shadowing within a scope.
    let mut a = "world";
    // `ref mut a` creates a ref-counted pointer which can be used
    // to modify `a`.
    let b = ref mut a;
    // Note that `b` must be dereferenced to modify its pointee.
    *b = "hello";
    // "hello hello"
    println!("{} {}", b, a);

    let mut my_vec = Vec2::new(4, 0);
    let mut my_other_vec = Vec2::zero();
    my_other_vec.y = 3;

    my_vec += my_other_vec;
    my_vec.normalize();
    assert_eq!(my_vec.x, 0.8);
    assert_eq!(my_vec.y, 0.6);

    // This is a partial application.
    // It is equivalent to a lambda:
    // let square = lam(base) -> naive_pow(base, 2);
    let square = naive_pow(?, 2);
    assert_eq!(square(4), 16);

    // U can't be inferred here,
    // so the type parameters must be explicitly stated
    let my_either = Either[i32, bool]::Left(512);

    match my_either {
        Left(val) => println!("An i32 was inside my_either: {}", val),
        Right(val) => println!("A boolean was inside my_either: {}", val),
    }

    // You can pattern match in `if` expressions, similar to `if let` in Rust,
    // by using `x matches pat`. Unlike `if let`, this expression works
    // anywhere and doesn't cause confusion with precedence
    // (`let true = false && true;` vs `if let true = false && true {}`).
    if my_either matches Either::Left(inner) && inner == 512 {
        println!("my_either (still) contains `512`!");
    }

    // But if square brackets are for generics, wouldn't it be confusing to see
    // `i[i32, 4]` vs `[i32; 4]`? Similarly, in `lol[4]`, is `lol` an array or
    // a type with a const generic parameter? Laria simply avoids all this by
    // not giving array indexing or array types special syntax.
    //
    // TODO: seems odd that array literals still use brackets, but I don't see
    // many other options (angle brackets are ambiguous with greater-than,
    // an `array!(...)` macro seems weird)
    let my_array: array[i32, 4] = [1, 2, 3, 4];
    println!("the first element of my_array is {}", my_array.at(1).unwrap());
}