Chapter 1
use "io/fio"
use "prelude"
rec Sphere(center: Point, radius: f64, color: Color, albedo: f32)
rec Plane(center: Point, normal: Vec3, color: Color, albedo: f32)
@entry
fn main argc: i32, argv: str* = {
if argc != 2 {
println "Please provide a file to cat"
exit -1
}
var file = fopen argv[1] as str, "r"
if file as u64 == 0u64 {
printf "Could not open file %s\n", argv[1]
exit -1
}
var eof = false
var c: char
while eof != true {
c = fgetc file
if c == EOF then
eof = true
else print c
}
fclose file
}
exp fn clamp self: Color -> Color =
Color {
red: (max (min self.red, 1.0), 0.0),
green: (max (min self.green, 1.0), 0.0),
blue: (max (min self.blue, 1.0), 0.0)
}
fn print color: Color = printf "%d %d %d\n", (color.red * 255.0) as u8, (color.green * 255.0) as u8, (color.blue * 255.0) as u8
fn get_color sphere: Sphere, light: Light, ray: Ray, dist: f64 -> Color = {
val hit_point = add (to_vec3 ray.origin), mul ray.direction, dist
val normal = normal sphere, hit_point
val dir_to_light = mul light.direction, -1f64
val light_pow = (max (dot normal, dir_to_light) as f32, 0.0) * light.intensity as f32
val light_refl = sphere.albedo / PI as f32
val color = mul (mul (mul sphere.color, light.color), light_pow), light_refl
clamp color
}
Syntax
Primitive Types
Primitive numeric types
Signed
i8
i16
i32
i64
Unsigned
u8
u16
u32
u64
Floating
f32
f64
String and char
str
char
Other
bool
type* // pointer to `type`
&type // reference to `type`
[type] // slice of `type`
[type, n] // array of `type` with `n` length
Literals
There are a few main literals for aatbe.
Numeric
Integer
0
123
987
Hex
0x123
0xcafe
0xbeef
Floating
1.23
123.789
For integer numeric and floating point literals a suffix can be added to specify the value type
255u8 // unsigned 8 bit number
123456789u64 // unsigned 64 bit number
-128i16 // signed 16 bit number
32f32 // 32 bit floating point number
1.768f64 // 64 bit floating point number
Strings and characters
"Hello World!\n" // Simple escape sequences are supported
'a' // character literal `a`
Other
Booleans
true
false
Symbols
Format: Identifier separated by _ underscore with : prefix. Example:
:Symbol
:Foo
:Bar_Baz
Unit value
Equivalent to void/null in other languages, but itself is a value.
()
First Class Citizens
First class citizens are top level file structures, all support type parameters.
Functions
// format
<exp> <extern> fn snake_case param1: type1, param2: type2 (-> ReturnType) = <expr body>
// examples
fn foo () = { }
fn bar baz: i32 -> bool = { }
exp fn export_process param1: Type1, truth: bool -> ProcessedType2 = { }
extern fn fopen stream: FILE* -> i32
Modifiers
Export
To export a function from a file, the exp keyword is used somewhere in the function declaration, before the fn keyword.
External
To bind a function to an externally linked one, you use the extern keyword. External functions must not have a function body.
Imports
For importing files, the syntax is use "path", with either a relative path or absolute path for resolved external dependencies (currently only aatbe's stdlib is supported).
use "relative/path/to/file" // imports ./relative/path/to/file.aat
use "fmt/print" // imports stdlib/fmt/print.aat
use "math/vec3"
use "prelude" // imports stdlib/prelude.aat
Example
project/
├── main.aat
├── top.aat
└── module
├── foo.aat
└── bar.aat
// main.aat
use "module/foo" // imports project/module/foo.aat
use "top" // imports project/top.aat
// foo.aat
use "bar" // imports project/module/bar.aat
use "../top.aat" // imports project/top.aat
Records
Analogue to C like structures.
rec Name(first: str, last: str)
Usage and initialization
// given the previous definition for Name
fn new :Name, first_val: str, last_val: str -> Name
= Name {
first: first_val,
last: last_val,
}
fn example () = {
val name_a = Name {
first: "John",
last: "Jim",
}
val name_b = new :Name, "Jane", "Doe"
}
Typedefs
Newtype pattern
Newtype is a way to isolate values of a certain type, with a new name. Example definition and usage:
type Age = u8
type Name = str
fn example () = {
val age = Age 18u8
val name = Name "Jack"
*age // accesses the value of age (u8)
*name // accesses the value of name (str)
}
Variant types
type Variant
= CaseA str, bool
| CaseB i32, str
| CaseC bool
fn example () = {
val caseA = CaseA "foo", true
val caseC = CaseC false
// Both caseA and caseC have the same parent type of `Variant
// But they have a different variant type which can be matched on
caseA.1 // access the str (first) field
caseA.2 // access the bool (second) field
}
Misc Typedefs
type union = u8 | u16 | i32 // pretty much UB, works in the language, no idea why
type Opaque // used to forward define an opaque type
Operations
Mathematical operations
The common basic math ops are available, including order of operation.
Infix
Math
+ // addition
- // subtraction
* // multiplication
/ // division
% // modulo (remainder)
Bitwise
| // bitwise OR
& // bitwise AND
^ // bitwise XOR
Boolean
Comparison
== // Equality
!= // Inequality
> // Greater
>= // Greater than or equals
< // Lower
<= // Lower than or equals
Composition
|| // boolean OR
&& // boolean AND
Prefix
! // boolean negation
- // numeric negation
& // reference / address of
* // dereference
Logic
Branching
For branching, there is support for an if/else statement pair.
if <ret> cond <then> then_expr else else_expr
Examples
if true {
// do something
} else {
// do something else
}
if a == b then
// do something
else
// do something else
if ret foo.cond {
ret "foo"
} else {
ret "bar"
}
if a == b {
// then
} else if foo {
// otherwise
} else {
// fallback
}
A particularity of Åtbe is the if ret syntax. The syntax is used to turn if/else statements into expressions. Similar to the ? (ternary) operator in other languages, or if/else statement pairs in Rust and Scala.
For if ret, the last statement in the body of both the then block and else block needs to have the same type as it will be used as a return value from either of the branches.
Loops
There are currently two types of loops available, while and until loops.
while/until cond expr
Usage
while true {
// The while loop continues execution while the condition is true
}
until false {
// The until loop continues execution until the condition is true
}
The two type of loops are basically opposites. For example, if your condition is while a != b it can be rewritten as until a == b.