Basic Syntax

Primitive Types

TypeDescriptionMinMaxPrecision
u8unsigned 8 bit integer0255Whole numbers
u16unsigned 16 bit integer065536Whole numbers
u32unsigned 32 bit integer04,294,967,295Whole numbers
u64unsigned 64 bit integer01.844 × 10^19Whole numbers
i8signed 8 bit integer-128127Whole numbers
i16signed 16 bit integer-3276832767Whole numbers
i32signed 32 bit integer-2,147,483,6482,147,483,647Whole numbers
i64signed 64 bit integer-9.223 × 10^189.223 × 10^18Whole numbers
f3232 bit floating point number±3.402 × 10^-38±3.402 × 10^38≈ 6 - 9 digits
f6464 bit floating point number±1.798 × 10^-308±1.798 × 10^308≈ 16 digits

Program Structure

// Core module imports, Core modules contain the fundamental
// primitives which cannot be implemented in Flint itself
use Core.print

// Main function entry point
def main():
    print("Hello, World!\n");

Compiling & Testing

flintc hello.ft --out hello   # build binary 'hello'
flintc main.ft --test         # build test binary 'test'

Comments

// line comment
/* block comment */

Indentation

Scope is defined by indentation using hard-tabs (also accepted as 4 spaces per level). Statements end with ;.

use Core.print

def main():
    i32 x = 5;
    if x > 3:
        print("big\n");

Variables

// Typed declaration
i32 x = 5;

// Reassignment
x = 7;

// '_' digit separators
u32 n = 4_294_967_290;
  • Statically typed, the type is fixed at declaration.
  • No shadowing: the same identifier can only be declared once per scope.
  • Integer overflow/underflow clamps to the type's min/max instead of wrapping.
  • Integer division truncates the fractional part (100 / 30 is 3).

Inferred Typing

// Every literal has a "default-inferred" type
// inferred as i32
x := 42;

// inferred as f32
pi := 3.14;

// inferred str
msg := "Hello!";

:= is only allowed for variables.

Operators

// Arithmetic
n1 + n2;
n1 - n2;
n1 * n2;
n1 / n2;

// String concatenation
hello + world;

// Comparison
x == y;
x != y;

// Increment (also -=, *=, /=)
x++;
x += 1;

Standard order of operations; use parentheses for grouping. Do not compare floats with == (IEEE-754 imprecision).

Type Casting

// explicit cast, fractional part is cut off
i32 val = i32(3.3);

// implicit cast allowed if no info is lost
f32 f = 3;

// any type casts implicitly to str
str s = "n = " + n; 

String Interpolation

print($"x = {x}, y = {y}\n");

Boolean Logic

a and b;
a or b;
not a;

Branching

if cond:
    ...
else if cond:
    ...
else:
    ...

Loops

// While loop
while cond:
    ...

// Do-While loop
do:
    // runs at least once
    ...
while cond;

// C-Style for loop
for i32 i = 0; i < 5; i++:
    // i scoped to the loop
    ...

// Enhanced for loop
for (idx, elem) in iterable:
    ...

break and continue work as expected; i++ is i += 1.

Enums

// auto-numbered 0, 1, 2
enum MyEnum:
    TAG1, TAG2, TAG3;

// explicit values
enum Flags:
    A = 1,
    B = 2,
    C = 4;
MyEnum e = MyEnum.TAG1;
if e == MyEnum.TAG1:
    ...

// Enums can be cast to strings and to integers
str name = str(e);
i32 id = i32(e);

Switch

No case/break/fallthrough. Statement form:

switch result:
    BIGGER:
        print("is bigger\n");
    SMALLER:
        print("is smaller\n");

Expression form:

i32 result = switch e:
    VAL1 -> 1;
    VAL2 -> 2;

Functions

// no params, no return
def greet():
    ...

// typed params + return
def add(i32 a, i32 b) -> i32:
    ...

// multiple returns (group)
def split() -> (i32, i32):
    ...

// CLI args
def main(str[] args):
    ...

// Main function may return exit code
def main(str[] args) -> i32:
    ...
  • Declaration order is irrelevant; call any function from anywhere.
  • Parameters are immutable by default (mut keyword opts in).
  • Multiple returns: return (a, b); and (a, b) := split();.
  • Discard a return value with _.

Tests

use Core.assert

test "adds numbers":
    i32 result = add(2, 3);
    assert(result == 5);