ralix 0.2.0

A simple, type-safe, tree walking interpreter
# Statements

Statements are instructions that perform an action. In Ralix, statements only.
Statements are separated by semicolons (";") which are optional. But it's
recommended to use them to avoid syntax errors.

## Binding statement

The binding statement is used to create a new variable binding. You can specify
the type of the value. If you want it's type to be specified automatically you can
use the `let` statements

```c
// Create a variable `x` of type `int` with a value of 5
int x = 5;

// Create a variable `y` and let the compiler infer its type
str y = "hello";

// Auto binds the type `float`
let pi = 3.14;
```

## `const` statement

The `const` statement is used to create a new constant binding. Constants are
immutable and must have their type specified.

```c
// Create a constant `PI` of type `float`
const float PI = 3.14159;
```

## Type Alias Statements

Using the type keyword you can create your own type aliases. Once a type alias defined
it cannot be changed afterwards.

```c
type MyStr = str?;
MyStr my_value = "hehe! I'm in danger!"; // Don't judge. This line came
                                         // to my mind for no reason
```

You also can use the types you got from the `typeof` expression. And also
use them in binding statements.

```rust
const float PI = 3.14159;
let MyFloat = typeof PI;
MyFloat my_type_is_same_as_PI = 1.2;
```

## `fn` statement

The `fn` statement is used to create a new function. Functions can have parameters
and a return type. Optionally you can also bind `const fn` statements.
This is allowed because functions are just binding statements that the value is
just a regular function.

```c
// Create a function `add` that takes two integers and returns an integer
fn add(int a, int b) -> int: a + b;
```

For flexibility you can use "type generics" in function parameters and return types.

```rust
fn first[T](arr[T] x) -> T? : x[0]
```

## `return` statement

The `return` statement is used to exit a function and optionally return a value.

```c
fn get_greeting() -> str: {
    return "Hello, Ralix!";
}
```

## Expression statement

An expression statement is an expression that is followed by a semicolon.
The value of the expression is discarded unless it's the last expression
statement that has been evaluated. This can be useful when using scope expressions.

```c
// The function call is an expression statement
println("Hello, World!");
```

## Assignment

An assignment statement is used to change the value of an existing variable.

```go
int x = 5;
x = 10;

map[str, arr[int]] items = #{ "a": [0,1,2], "b": [3,4,5] };
items["b"] = [6,7,8];

arr[float] nums = [1.0, 2.7, 3.3, float(4)];
nums[2] = 5.8;
```

> [!IMPORTANT]
> Note that index assignment operations can only update _existing values_.
> If you wanna add a new value to a new hash-map using a key that hash-map
> isn't using this operation will simply do nothing

## `get` statement

The `get` statements can be used to import file modules for `ralix`.
The `get` statements first executes the module using it's content.
Also all modules cannot be executed twice.

```ts
get my_module/my_submodule; // `my_submodule` is a "module" from now on.
```

To access parent directories you can use TwoDots ("..") token

```ts
get mod/sub_mod/.. // This is equivalent to `get mod`
```

Using braces lets you bind the values instantly.

```ts
get utils/math { add, sub, mul, div }; // `add`, `sub`, `mul`, `div` are 
                                       // automatically had bind to the scope
                                       // as well as with the `math` module
```

### "as" keyword

Using `as` keyword lets you rename the imported identifiers.

```ts
get module_a { itemA };
get module_b { itemA as itemB }; // `itemB` will be treated as `itemA` from
                                 // module `module_b`
```