The AlteRnaTive Impala Compiler.
A compiler that supports C++17 and CMake are required to build the project. Additionally, this project depends on Thorin. Use the following commands to build the program:
mkdir build
cd build
cmake .. -DThorin_DIR=<path/to/thorin>
make -j
Note that path/to/thorin
is usually thorin/build/share/anydsl/cmake
.
Artic is in alpha stage, which means that it should be able to compile any valid program. There might still be bugs lurking in, and if you find one, please report it on the issues tab, preferably with a minimal reproducing example.
Once built, Artic can be run with the following command:
bin/artic [files]
The test suite can be run using:
make test
Additionally, a coverage report can be generated when the CODE_COVERAGE
CMake variable is
set to ON
or TRUE
:
make coverage
The documentation for the compiler internals can be found here.
The syntax follows that of Impala, whenever possible. Some notable changes compared to the syntax of Impala are:
struct S[T] {
elem: T
}
fn select[T](cond: bool, a: T, b: T) -> T {
if cond { a } else { b }
}
fn main() -> i32 {
S[i32] { elem = select[i32](true, 0, 1) }.elem
}
struct Vec3f {
x: f32, y: f32, z: f32
}
struct Len[T] {
len: fn(T) -> f32
}
implicit = Len[Vec3f] { len = | v | sqrt(v.x v.x + v.y v.y + v.z * v.z) }; fn longest(a: Vec3f, b: Vec3f, implicit l: Len[Vec3f]) = if (l.len(a) > l.len(b)) { a } else { b };
- The type inference algorithm is now bidirectional type checking, which means
that type information is propagated _locally_, not globally. This gives improved
error messages and better support for advanced type system features, at the cost
of slightly more type annotations:
```rust
let x = |i| i; // artic needs a type annotation on `i` or on `x`
x(1) // impala would see this as a constraint that `x` is a function on integers
// The `range` function now takes a loop body and
// returns a function that performs the actual looping
fn @range(body: fn (i32) -> i32) {
fn loop(beg: i32, end: i32) -> () {
if beg < end {
@body(beg);
loop(beg + 1, end)
}
}
loop
}
// ... later in the source code ...
for i in range(0, 10) {
print(i)
}
// This is equivalent to:
range(|i| { print(i) })(0, 10)
// This function will be exported in the generated LLVM module.
// Note that only functions of order 1 (functions that do not take
// other functions as arguments) can be exported.
#[export]
fn foo() -> i32 { 1 }
fn bar() = A::foo();
mod A {
fn foo() = 1;
}
use
keyword, optionally with
another name by using as
:
mod A {
use super::C;
fn foo() { C::baz() }
mod B {
fn bar() { super::foo() }
}
}
mod C {
use super::A::B as D;
fn baz() { D::bar }
}
let t = (1, 2);
t(1) // valid in impala, invalid in artic
// valid alternatives in artic:
let t1 = t.1;
match t { (_, t1) => ... }
let (_, t1) = t;
fn foo(x: f32, (y: f32, z: f32)) -> ... { ... }
fn foo(x as (y: f32, z: f32)) { ... }
=
sign instead of braces if their body is just an expression:
fn foo() -> i32 = 1;
return
and
are not recursive:
fn foo() = 1;
=
sign instead of :
to give values to their members
(this is for consistency with the use of :
for type annotations, structure types are not affected):
let p = Pair { x = 1, y = 2 };
match p {
Pair { x = x_, y = y_ } => x_
}
let p = Pair { x = 1, y = 2 };
let q = p .{ y = 3 }; // q is a structure with x = 1, y = 3
...
symbol to indicate they do not capture everything:
fn foo(Pair { x = f, ... }) = f;
struct S {
x: i32 = 1,
y: i64 = 3
}
static x = S { y = 2 }; // x is a structure with x = 1, y = 2
struct S(i32, i64);
struct T;
struct S(i32, i64);
let s = S(1, 2);
let x = s.0;
let y = s.1;
let t = (1, 2);
let z = t.0;
let w = t.1;
enum E {
A,
B(i32),
C {
x: i32,
y: i64
}
}
let [x, y] = [1, 2];
let simd[z, w] = simd[1, 2];
let x : i32 = (1 : i32) : i32;
let x : u8 = 1; // `x` types as u8
let y = 1; // `y` types as i32 (default when no annotation is present)
let z : f32 = 1; // `z` types as f32
// impala would complain that this match is missing a default case
match x {
(true, (_, true)) => 1,
(true, (_, false)) => 2,
(_, (1, _)) => 3,
(_, (_, false)) => 4,
(_, (_, true)) => 5
}
if let
and while let
expressions are supported:
if let (Option[Foo]::Some(y), 1) = x {
foo(y)
} else {
bar();
}
while let Option[Bar]::Some(x) = get_next() {
process(x);
}
@@
sign for call-site annotations has been replaced by @
:
@@foo(1, x); // valid in impala, useless (counted as two annotations) with artic
@foo(1, x); // valid in artic, invalid in impala
addrspace
:
// Equivalent to &[1]i32 in impala
fn foo(p: &addrspace(1)i32) = *p;
[0; 4] // Equivalent to [0, 0, 0, 0]