![]() |
1 year ago | |
---|---|---|
compiler | 1 year ago | |
parser | 1 year ago | |
typechecker | 1 year ago | |
visitorgenerator | 1 year ago | |
.gitignore | 1 year ago | |
MeowLang.sln | 1 year ago | |
README.md | 1 year ago |
README.md
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:
=
+=
-=
/=
*=
%=
&=
|=
^=
<<=
>>=