
An unnamed systems programming language designed around versatility and simplicity. The language is a work in progress, currently the compiler produces standalone executables for basic programs (functions, operators, primitive types) using an LLVM backend.
Here are some snippets which demonstrate the ideas for the language.
Structure, enum and union types
Node : struct {
type : NodeType,
};
BinaryNode : struct {
...Node,
lhs : ptr[Node],
rhs : ptr[Node],
};
ValueNode : struct {
...Node,
value : Value,
};
NodeType : enum[int] {
PLUS = 0,
MINUS,
TIMES,
DIVIDE,
VALUE,
};
Value : union {
f : float,
i : int,
};
Builtin types
A : ptr;
B : buffer[int, #10];
Pointer types
Later we can define pointer types with generic types and overloading.
ptr[x] : ptr;
/*ptr[x] + int : (l: ptr[x], r: int) -> ptr[x] = {
size := #type[x]().size; // # means calculate at compile time
return ptr + size*r; // we need to use size at run-time so don't # it
}*/
print_node : (node: ptr[Node]) -> void = {
if node->type == NodeType.PLUS {
print("plus");
} else if node->type == NodeType.MINUS {
print("minus");
}
}
Generic functions
add_value[Key, Value] : (map: HashMap[Key, Value], key: Key, value: Value) -> void = {
/* implementation */
}
Generic types
vec[ct: Numericals, num: int = #3] : type[#create_vec_type[ct](num)];
create_vec_type[ct] : (ct: Type, num: int) -> StructType = {
t := struct StructType {};
i := 0;
components := "xyzw";
while i < num {
add_value(t.members, make_string(components[i]), type[ct]());
}
return t;
}
vec3[ct : Numericals = float] : vec[ct];
vec4[ct : Numericals = float] : vec[ct, #4];
vec2[ct : Numericals = float] : vec[ct, #2];
Meta-types
Numericals : [int, float, NodeType]; // enum is redundant here since it reduces to an int
ComposesNode : [Node]; // cannot mix struct and primitive / enum types
Operator overloading
vec3[x] + vec3[y] : (l: vec3[x], r: vec3[y]) -> vec3[x] = {
return vec3[x]{
x = l.x + r.x,
y = l.y + r.y,
z = l.z + r.z,
}
}
Examples
Example 1: run time type information (RTTI)
Structs : [struct];
RTTI[t: Structs] : type[#add_rtti_to_type(t)];
add_rtti_to_type : (type: Type) -> Type = {
if (!in_map(types.members, "rtti")) {
add_value(type.members, "rtti", type[Type]());
add_value(type.default_values, "rtti", allocate(type));
} else {
*get_value(type.default_values, "rtti") = type;
}
}
Entity : RTTI[struct {
id : int,
position : vec3,
rotation : vec4,
}];
Static : RTTI[struct {
...Entity,
}];
Actor : RTTI[struct {
...Entity,
health : int,
}];
Player : RTTI[struct {
...Actor,
unlocks : int,
}];
Example 2: rust-style optional
Optional[x] : ptr[x];
ResolvedOptional[x] : Exclude[Optional[x], type[#nullptr]];
// disallow implicit and explicit casting
(y)Optional[x] : (this: Optional[x]) -> ptr[x] = {
#assert(false);
}
// allow casting for the resolved type
(y)ResolvedOptional[x] : (this: Optional[x]) -> ptr[x] = {
return (ptr[x])this;
}
example_function : (x: int) -> Optional[int] = {
if (x == 1) {
return nullptr;
}
return allocate(2);
}
test_example_function : () -> void = {
res := example_function();
// can't do:
// print(*res);
// (ptr[x])res;
// pass_as_raw_pointer(res);
if (res != nullptr) {
// now valid as the type has narrowed
print(*res);
}
}
Example 3: struct-based optional
Check[x] : struct {
error : string,
res : x,
};
ResolvedCheck[x] : struct {
...Check[x],
error := "",
};
get_member_type[t] : (name: string) -> Type = {
return get_member(type[t]().members, name);
}
Check[x].y : (this: Check[x]) -> type[#get_member_type[Check[x]](y)] = {
#assert(y != "res");
return this.y;
}
ResolvedCheck[x].y : (this: ResolvedCheck[x]) -> Check[x].[y] = {
// note that inside overloads, all user-overloads are ignored
return this.y;
}
function_to_check : () -> Check[int] = {
return struct Check[int] {
error = "",
res = 314,
};
}
test_check : () -> void = {
some_result := function_to_check();
// can't do:
// print(some_result.res);
if (some_result.error != "") {
print(some_result.res);
}
}
Example 4: constructor and destructor
// @module, @private, @scoped, etc...
File : struct {
unix_file_pointer : ptr,
};
// destructor
~File : (file: File) -> void = {
if (file.unix_file_pointer != nullptr) {
fclose(file.unix_file_pointer);
}
}
// constructor, combined with limiting File
// access (eg. @private) this is equiv.
open_file : (name: string) -> Check[File] = {
file := struct File {
unix_file_pointer = fopen(name),
};
if (file.unix_file_pointer == nullptr) {
return struct Check[File] {
res = file,
error = "failed to open",
};
}
return struct Check[File] {
res = file,
};
}
Example 5: "polymorphism"
Furniture : struct {
sit : ptr[(this: ptr[this]) -> void],
};
Table : struct {
// "this" is replaced with the current struct's type when spread
...Furniture,
// set default value without changing type
sit := &sit_on_table,
message : string = "That's an antique",
};
Chair : struct {
...Furniture,
sit := &sit_on_chair,
legs : int,
};
sit_on_table : (table: ptr[Table]) -> void = {
print(table->message);
}
sit_on_chair : (chair: ptr[Chair]) -> void = {
print(chair->legs);
}
test_polymorphism : () -> void = {
new_table := struct Table {
message = "He loved sitting on it",
};
ptr_to_furniture : ptr[Furniture] = &new_table;
// should print "He loved sitting on it"
(*(ptr_to_furniture->sit))();
// or just
ptr_to_furniture->sit();
}