The language takes a lot of inspiration from "Jai" which is being worked on by Jonathan Blow. My syntax is similar or equal to his in many places.
Let's just show some code:
Spoiler
#import "io";
fact :: (n : int) -> int {
if n == 1 return 1;
else return n * fact(n - 1);
}
main :: () {
print("fact(5) = " .. fact(5));
}
Spoiler
#import "io";
main :: () {
for i: 1, 10 {
if i == 4 continue;
if i == 8 break;
print("i = " .. i);
}
}
Spoiler
x := 5; // assignment declaration with type inference
X :: 5; // constant declaration
// regular declarations with types
// i actually haven't even implemented these yet...
a : string;
b : int = 4;
Spoiler
a := 1;
b := 2;
a, b = b, a;
print(a .. " " .. B)/>;
Spoiler
#import "io";
test :: (n : int) -> bool, bool, int {
return (n % 2 == 0), (n % 2 == 1), n * 3;
}
main :: () {
a := false;
b := false;
c := 0;
// i haven't implemented multiple declarations in a single line YET
a, b, c = test(3);
print(a .. " " .. b .. " " .. c);
}
Spoiler
#import "io";
main :: () {
defer print("bye");
print("hi");
}
This example prints "hi" and then "bye". Defer statements execute in the opposite order in which they were declared:
#import "io";
main :: () {
for i : 1, 5 defer print("i: " .. i);
}
This prints "5", "4", "3", "2", "1", even though it looks like it goes "1", "2", "3", "4", "5". The point is: the order in which defer statements run is guaranteed and specified in the language spec.
And by the way, the "io" import imports this file:
Spoiler
print :: #foreign (s : string);
Keep in mind, this is VERY early in development. I am still missing MANY features (arrays and structs to name a few).
Also, function declarations aren't special; they don't have to be at global scope. You can declare a function anywhere:
Spoiler
fact :: (n : int) -> int {
tail_recursive_fact :: (x : int, a : int) -> int {
if x == 0 return a;
return tail_recursive_fact(x - 1, x * a);
}
return tail_recursive_fact(n, 1);
}
However, one feature that I DO have, is compile-time code execution. Any code that would be written to run at runtime, normally, can be ran at compile time. A few examples:
Spoiler
#import "io";
#run print("Hello, Compile-Time World!");
Spoiler
#import "io";
fact :: (n : int) -> int {
if n == 1 return 1;
else return n * fact(n - 1);
}
main :: () {
print("fact(5) = " .. #run fact(5));
}
The generated code from this program that actually runs at runtime does not even contain the "fact" function. It simply runs this lua code:
print("fact(5) = " .. tostring(120))
Spoiler
#import "io";
depth := 0;
of_depth :: (char : string) -> string {
result := "";
i := 0;
while i < depth {
result ..= char;
i++;
}
return result;
}
fact :: (n : int) -> int {
depth++;
print(of_depth(">") .. " fact(" .. n .. ")");
defer {
print(of_depth("<") .. " fact(" .. n .. ")");
depth--;
}
if n == 1 return 1;
else return n * fact(n - 1);
}
main :: () {
print("fact(5) = " .. #run fact(5));
}
So, in this example, when I run the compiler, it prints this:
> fact(5)
>> fact(4)
>>> fact(3)
>>>> fact(2)
>>>>> fact(1)
<<<<< fact(1)
<<<< fact(2)
<<< fact(3)
<< fact(4)
< fact(5)
And when I run the outputted Lua program, it prints this: "fact(5) = 120".
Unlike Lua, you can't just write code. You have to put code in procedures. So, some of these examples are technically incomplete.
So, like I said, I have a LOT more work to do. Here's some things I would like to add:
- arrays
- structs
- polymorphism
- procedure overloading
- first class types
- statically linked libraries (maybe?)
- dynamic libraries (maybe?)
But I am VERY pleased with what I have so far.
Let me know what you guys think! Thanks!