Getting Started
Overview
Rivet's goal is to be a very powerful programming language and at the same time easy to use, with a syntax that is the result of mixing Go + Zig + C#, and by other languages such as Python, Lua, TypeScript, D, etc.
Rivet uses the C programming language as its main backend.
Before continuing, I assume you know how to use a console, otherwise you can read this tutorial: The Linux command line for beginners.
Run the compiler
Dependencies
-
The compiler requires Python 3.
-
The Rivet compiler currently generates C code, so a C compiler, which supports C11, is required to generate executables. Over time the compiler will add support for generating binaries directly without the need for a C compiler.
The compiler has been tested on linux.
Just execute python3 rivetc some_file.ri
.
You can see all available compiler options by using the -h
/--help
flag.
python3 rivetc -h
Hello World!
Let's start with the typical Hello World!
:
We create a file called hello_world.ri
with the following content:
import std/console;
func main() {
console.writeln("Hello World!");
}
Then we compile that file:
$ python3 rivetc hello_world.ri
We'll get an executable called hello_world
as output, so we run it:
$ ./hello_world
We should see this output:
Hello World!
Excellent! You have compiled your first program in Rivet!
Editor/IDE support
- LiteXL and Pragtical (Syntax-highlighting only).
Code Structure
Comments
// This is a single line comment.
/*
This is a multiline comment.
*/
You can use comments to make reminders, notes, or similar things in your code.
Entry point
In Rivet, the entry point of a program is a function named main
.
func main() {
// code goes here
}
Top-level declarations
On the top level only declarations are allowed.
import { import_list, ... } from module;
const FOO: int32 := 0;
static foo: int32 := 0;
alias Foo := Baz;
enum Foo { /* ... */ }
trait Foo { /* ... */ }
struct Foo { /* ... */ }
extend Foo { /* ... */ }
func foo() { /* ... */ }
test "foo" { /* ... */ }
Functions
Functions contain a series of arguments, a return type, and a body with multiple statements.
The way to declare functions in Rivet is as follows:
func <name>(<args>) [-> return_type] {
...
}
For example:
func add(a: int32, b: int32) -> int32 {
return a + b;
}
add
returns the result of adding the arguments a
and b
.
Functions can have 0 arguments.
// `f1` returns a simple numeric value of type `int32`.
func f1() -> int32 {
return 0;
}
// `f2` takes an argument of type `int32` and prints it to the console.
func f2(a: int32) {
println("a: {}", a);
}
// `f3` takes no arguments and returns void.
func f3() { }
A function body is made up of 1 or more statements and can be empty.
func x() {
/* empty body */
}
func y() {
my_var := 1; // statement
}
Arguments
The arguments are declared as follows: <name>: <type> [:= default_value]
,
for example: arg1: int32
, arg2: bool := false
.
The arguments are immutable.
They can also have default values, this bypasses the need to pass the
argument each time the function is called: arg1: int32 = 5
.
So, if we have a function called f5
with a default value argument,
we can call it in 3 ways:
func f5(arg1: int32 := 5) {
println("arg1: {}", arg1);
}
f5(); // use the default value `5`
f5(100); // will print 100 instead of 5 to the console
// this uses a feature called `named argument`, which allows an optional
// argument to be given a value by its name in any order
f5(arg1: 500); // will print 500 instead of 5 to the console
Statements
Each statement must end with a semicolon.
Variables
Variables are like boxes that contain values.
Variables are declared as follows: [mut] <name>[: <type>] := <value>;
.
Example:
x: int32 := 1;
We have created a variable called x
, which contains the value 1 and is
of type int32
.
The type of the variable can be omitted.
x := 1; // via inference, the compiler knows that `x` is an `int32`.
By default, all variables are immutable, that is, their values do not change.
To change the value of a variable you have to declare it with mut
.
mut x := 1;
x = 2; // this is valid
y := 1;
y = 2; // error: `y` is immutable
Multiple values can be assigned on a single line via tuple-destructuring, example:
(a, b, c) := (1, 2, 3);
(c: int32, d: int32, e: int32) := (4, 5, 6);
(f, g, h) := tuple_fn();
// this is a short form for:
a := 1;
b := 2;
c := 3;
c: int32 := 4;
d: int32 := 5;
e: int32 := 6;
tmp_tuple_fn := tuple_fn();
f := tmp_tuple_fn.0;
g := tmp_tuple_fn.1;
h := tmp_tuple_fn.2;