meowlang/README.md

5.8 KiB

meow

most awesome language, mainly because it's not object-oriented and it's called meow.

Multiple dispatch!

Weird syntax!

Implicit interfaces, except we call them constraints! whyyyy did I do this?

<imports>

<declarations>

imports

import "std:strings" as str // standard library
import "std:strings/ext" as strext // nested 
import "foo:bar" as fb // file bar from project foo
import ":foo/bar" // relative to project root
import "foo" // relative to current file
import "./foo" as whatever // relative to current file
import "../foobar" // relative to current file

access public thingies from imports with :

import foo;

foo:somefunction();
foo:SomeType
foo:SomeConstraint

functions

Functions are by default private to the current file, functions with pub are accessible from other files/projects.

fn x() {}
pub fn y() {}

Functions can take the [c_export] attribute to make them callable when compiled as a static library, and to generate C headers for them.

[c_export] pub fn foo(a: int, b: Foo, c: Bar): int {

}

[c_export("renamed_foo2")] pub fn foo2(a: int, b: Foo, c: Bar): int, int {

}

Functions can be overloaded on parameter and return types

fn a(): s32 {
    return -1;
}
fn a(): u32 {
    return 1;
}

fn a(x: i32, y: i32): (i32, i32) {
    return y, x;
}

fn a(x: u32): u32 {
    return x + 1;
} 

constraints

constraints are kind of weird because they

  • can have multiple types, like, a constraint can constrain multiple types together
  • can be used both as a constraint on generic types and as a type themselves
    • but only can be used as a type when they constrain only one type, and then they're kinda like an interface?
  • fuck this is weird
// this one can be used only as a constraint for generic type parameters
constraint Foo on A B {
    some_func(A, B);
    some_other_func(A): B;
}

// this one can be used both as a constraint for generic type parameters or as an interface type 
constraint Bar on A {
    f1(A): A;
    f2(A): i32;
}
// usage as type parameter constraints
fn foo<T1, T2>(T1 x) 
    where Foo T1 T2
{
    T2 y = some_other_func(x);
    some_func(x, y);
}

fn bar<T>(T a): i32
    where Bar T
{
    T b = f1(a);
    return f2(b);
}

// usage as interface types

Bar x = getSomeTypeThatIsBar();
Bar y = f1(x);

constraints can be embedded in other constraints like this:

constraint Arithmetic on A {
    operator+ (A, A) -> A;
    operator- (A, A) -> A;
    operator* (A, A) -> A;
    operator/ (A, A) -> A;
}

constraint Foo on A {
    Arithmetic A;
    some_func(A) -> A;
}

literals

numeric

Numeric literals are untyped but literals with a decimal point can not be used as integers. They can contain underscores between digits which are ignored, e.g. 1_000 or 1_0_0_0. Unprefixed numeric literals are decimal, prefixes exist for hexadecimal (0x), octal (0o) and binary (0b).

character

The character literal is a single unicode character enclosed in single quotes, its value is the character's unicode code point.

bool

true and false

string

String literals are enclosed in double quotes (e.g. "hello world"). Their type is an u8 array with length of the string in bytes.

types

basic types

  • bool: bool
  • signed integer types: s8, s16, s32, s64, s128
  • unsigned integer types: u8, u16, u32, u64, u128
  • floating point types: f32, f64 (possibly more?)
  • decimal type? decimal
  • complex types? c64, c128 (does this refer to the total size or the size of each component?)

compound types

array & slice
declaration
// array
[u8; 10]
// slice
[u8]
use


let a: [u8] = "foo";
let b: u8 = a[1]; 
let d: [s32; 4] = []{1,2,3,4};
let e = [u8]{1,2,3,4};

let f = d[1:3]; // f has type [s32] (slice) now


type Arr = [u8];
let g = Arr[]{1,2,3};

struct
declaration
struct Point {
    x: s32;
    y: s32;
    z: s32;
}
type Point = struct {
    x: s32;
    y: s32;
    z: s32;
}


type Options = struct {
    api: struct {
        int foo;
    };
}
use
let p1 = Point{
    x: 1,
    y: 2,
    z: 3,
};
let p2: Point = {
    x: 2,
    y: 3,
    z: 4,
};

let x = p1.x;

p1.x = 2;

let opt = Options{
    api: {
        foo: 1
    }
};

let a: Options@api = opt.api; // yeah this is weird, try to avoid it

enum
declaration
enum A {
    Foo,
    Bar(u8)
}

type A = enum {
    Foo,
    Bar(u8)
}

type B = enum {
    X,
    Y(enum { // this is terrible but you can do it if you want for consistency reasons
        A,
        B
    })
}

use
let x = A'Foo;
let y = A'Bar(10);
let z = B'Y(B@Y'A); // don't do this why would you do this
tuple
declaration
type X = (s8, s8);
tuple X(s8, s8);
type Y = (struct {int a; int b}, enum {A, B}); // why would you do this aaaaa
use
let a: X = (1,2);
let b = a.0;
let c = a.1;
let d = Y({a: 1, b: 2}, Y@1'A); // oh no
let e: Y@1 = d.1;

pointers

Pointers are taken with the & operator and dereferenced with the * operator. They are mostly similar to C I think. Pointer types have a * appended to the type they point to.

let a: int = 1;
let b: int* = &a;
let c = *b;

A special void* type exists???? does it really? maybe? kinda yes? aaaaaaa

Pointer arithmetic works the same as in C, except that arithmetic on void pointers is allowed?

let a: [u8; 2] = {0,1};
let b: u8* = &a[0];
let c: u8* = b + 1;
let d: u8 = *b; // 0
let e: u8 = *c; // 1
*(&a[0]+1) = 2; // a is now {0,2} 

operator precedence

. (struct member access), [] array subscripting
    
 unaries: + - ! ~, pointer dereference *, pointer reference &, 
 binary operators:
 * / %
 + -
 << >>
 > >= < <=
 == !=
 &
 ^
 |
 &&
 ^^
 ||
 lowest precedence, assignment:
 =
 +=
 -=
 /=
 *=
 %=
 &=
 |=
 ^=
 <<=
 >>=