boba
aims to be a safe, static and implicitly typed language. The language is designed to be syntactically similar to Rust and semantically equivalent to C. The compiler supports the following language constructs:
str
: Static immutable string stored in the read-only memory of the binary. Equivalent to&'static str
in Rust.i32
: 32-bit signed number data type. Equivalent toint
in C.bool
: 8-bit boolean data type used to denote eithertrue
orfalse
. Internally,true
is stored as1
andfalse
is0
.char
: 8-bit ASCII character.[T; length]
: An array with elements of typeT
and compile time knownlength
.
An array is a collection of values enclosed within square brackets. It compiles to a pointer to the first element.
Like any other expression, arrays can be passed into the print statement to print all the values in the array, assigned to variables, passed into functions but returning an array from a function will result in segmentation fault.
Subscript expressions can be used to access any element of the array. Currently, a subscript expression can only be used as a r-value.
fn sum(array: [i32; 5]) -> i32 {
let mut total = 0;
for (let mut i = 0; i < 5; i = i + 1) {
total = total + array[i];
}
return total;
}
fn main() {
let array = [1, 2, 3, 4, 5];
let total = sum(array);
println("Sum of {} = {}", array, total); // Sum of [1, 2, 3, 4, 5] = 15
}
The symbol -
followed by a value of type i32
is an unary negation expression. It will evaluate to a value of type i32
.
let two = 2;
let minus_two = -two;
The symbol !
followed by a value of type bool
is a logical not expression. It will evaluate to a value of type bool
.
let going_to_rain = true;
if !going_to_rain {
go_out();
} else {
stay_in();
}
Here, both a
and b
are values of type i32
. All numerical expressions evaluates to a value of type i32
.
- Addition:
a + b
- Subtraction:
a - b
- Multiplication:
a * b
- Division:
a / b
- Modulus:
a % b
Here, both a
and b
are values of type i32
. All comparison expressions evaluates to a value of type bool
.
- Greater:
a > b
- Greater Equal:
a >= b
- Lesser:
a < b
- Lesser Equal:
a <= b
- Equality:
a == b
- Non Equality:
a != b
Here, both a
and b
are values of type bool
. Both the logical expressions evaluate to a value of type bool
.
- Or:
a || b
- And:
a && b
It is a compiler built-in function that takes a format string and the expressions to be printed as arguments. The {}
s inside the format string will be replaced by the value of expressions and displayed on a newline in stdout
.
Note: Arguments will be evaluated from right-to-left in order to push arguments onto the stack in correct order.
Internally, the format string is compiled to a C style format string and a call to libc’s printf()
.
fn is_even(num: i32) -> bool {
return num % 2 == 0;
}
println("Hello, world");
let name = "oxxo";
println("Namaskara, {}!", name);
let number = 10;
println("{} is even?: {}", number, is_even(number));
Variables can be declared in both global and local scopes. Globally declared variables cannot be reassigned but local variables can be declared with the mut
keyword to state that it can be reassigned with an other value of the same type.
let size = 23;
let mut temperature = 35;
The types of all variables will be inferred at the time of declaration.
The if
statement follows Rust’s syntax but C’s semantics. In other words, it is a conditional statement, not an expression.
if 2 > 1 {
println("two is greater than one");
} else {
println("two is less than one");
}
The following else
statement is optional and can be skipped without any additional syntax.
A while
loop can be declared using the while
keyword followed by a condition and the body of the loop in curly braces.
let mut a = 5;
while a > 0 {
println("{}", a);
a = a - 1;
}
Syntax for for
loop is similar to that of C. Internally, a for
loop desugars into a while
loop.
for (let mut a = 5; a > 0; a = a - 1) {
println("{}", a);
}
The syntax for function declaration is identical to that of Rust. However, the compiler cannot handle functions with more than six parameters for the same reason mentioned above.
fn factorial(num: i32) -> i32 {
if num == 0 {
return 1;
} else {
return num * factorial(num - 1);
}
}
fn main() {
println("{}", factorial(5));
}
Every valid program should contain a main()
function because main()
is the entry point for all programs. It can explicitly specify the return type as i32
or not specify the return type and let the compiler implicitly add instructions to return a 0
.
Functions can be declared in any order and can be called from any local scope in the source file.
It is used to return the value of an expression from a function. This expression’s type should be the same as the return type specified in the function signature.
fn greet() -> str {
return "hello";
}
fn is_even(num: i32) -> bool {
return num % 2 == 0;
}