meowlang/README.md
Gwendolyn d6bdd08002 loooots of stuff
the typechecker project can collect all the top level types from a file, which is pretty cool I think
(except for pointers, those aren't implemented yet...)
2022-02-13 02:41:16 +01:00

351 lines
5.8 KiB
Markdown

# 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:
=
+=
-=
/=
*=
%=
&=
|=
^=
<<=
>>=
```