Getting Started
Learn Vexel
This guide walks you through Vexel from your first program all the way to structs, pattern matching, and SDL2 graphics. It assumes you have written some code before but does not assume any specific language background.
1. Installation
Download vexel.exe from the Downloads page and place it somewhere on your PATH,
or just put it in your project directory and call it with ./vexel.
No installation wizard, no package manager, no prerequisites. It is a single self-contained executable.
Verify the installation by running:
vexel --version
You should see something like Vexel 0.1.0 printed to stdout.
2. Hello, World
Create a file called hello.vx with the following content:
fn main():
print("Hello, World!") Run it with:
vexel run hello.vx
Vexel programs always start in a function called main.
The fn keyword declares a function.
Indentation (4 spaces or 1 tab) defines the function body. No curly braces.
3. Variables and Types
Use let to declare a mutable variable
and const for a value that cannot be reassigned.
Every variable has an explicit type annotation after a colon.
fn main():
let name: str = "Vexel"
let version: float = 0.1
let count: int = 42
let active: bool = true
const PI: float = 3.14159
count = count + 1 # ok, let is mutable
# PI = 3.0 # error: cannot assign to const
print(name)
print(count)
The four primitive types are int (64-bit integer),
float (64-bit double),
str (string), and
bool.
There is also null for unset references.
4. Functions
Functions are declared with fn followed by a name, a typed parameter list, and an optional return type after ->.
fn add(a: int, b: int) -> int:
return a + b
fn greet(name: str):
print("Hello, " + name + "!")
fn main():
print(add(3, 4)) # 7
greet("Vexel") # Hello, Vexel! Functions without a return type annotation implicitly return nothing (equivalent to void). Recursive functions work as expected. Functions are first-class: you can store them in variables and pass them as arguments.
5. Control Flow
Vexel has if / elif / else, while,
three forms of for, and break / continue.
fn main():
# if / elif / else
let score: int = 82
if score >= 90:
print("A")
elif score >= 80:
print("B")
else:
print("C or below")
# for range: 0, 1, 2, 3, 4
for i in range(5):
print(i)
# for-each over an array
let words: [str] = ["hello", "world"]
for w in words:
print(w)
# while loop
let n: int = 0
while n < 3:
print(n)
n = n + 1 6. Arrays
Arrays are homogeneous and dynamically sized. The type syntax is [T].
Use push to append, pop to remove the last element, and len to get the length.
fn main():
let nums: [int] = [10, 20, 30]
push(nums, 40)
print(len(nums)) # 4
print(nums[0]) # 10
print(nums[3]) # 40
for i, v in enumerate(nums):
print(i)
print(v) 7. Structs
Structs group related fields together. Methods are defined inside the struct body using fn with self as the first parameter.
struct Vec2:
x: float
y: float
fn length(self) -> float:
return (self.x * self.x + self.y * self.y) * 0.5
fn main():
let v: Vec2 = Vec2(x: 3.0, y: 4.0)
print(v.x) # 3.0
print(v.length()) # 12.5 8. Enums and Pattern Matching
Enums define a type that can be one of several named variants. Use match to dispatch on the active variant.
enum Direction:
North
South
East
West
fn describe(d: Direction):
match d:
case North: print("Going north")
case South: print("Going south")
default: print("Going east or west") 9. Interfaces
Interfaces define a set of method signatures. A struct satisfies an interface by implementing all of its methods using impl InterfaceName for StructName.
interface Drawable:
fn draw(self)
struct Circle:
radius: float
impl Drawable for Circle:
fn draw(self):
print("Drawing circle") 10. Error Handling
Use assert for invariants that should never be violated.
Use try / catch to recover from runtime errors.
fn main():
assert 1 + 1 == 2, "math is broken"
try:
let x: int = risky()
print(x)
catch err:
print("Caught: " + err) 11. Modules and Imports
Split code across files using import.
All top-level declarations from the imported file become available in the current scope.
Use import ... as name to access them through a namespace.
# utils.vx
fn clamp(v: int, lo: int, hi: int) -> int:
if v < lo: return lo
if v > hi: return hi
return v
# main.vx
import "utils.vx" as utils
fn main():
print(utils.clamp(150, 0, 100)) # 100 12. Your First Game: Bouncing Square
With SDL2 support you can open a window, draw shapes, and run a game loop in about 25 lines. Compile with --sdl2:
fn main():
let ok: int = sdl2_init("Bouncing Square", 800, 600)
if ok == 0: return
let x: int = 100
let y: int = 100
let vx: int = 4
let vy: int = 3
let running: int = 1
while running == 1:
running = sdl2_poll()
sdl2_clear(20, 20, 40)
sdl2_set_color(255, 80, 80, 255)
sdl2_draw_rect(x, y, 40, 40)
sdl2_present()
sdl2_delay(16)
x = x + vx
y = y + vy
if x > 760 or x < 0: vx = -vx
if y > 560 or y < 0: vy = -vy
sdl2_quit() # Run it
vexel compile game.vx --sdl2 -o game
./game