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
.