Welcome
Welcome to the Vega programming language! In this documentation will start from roots of the language and continue until every detail of it is explained with examples.
This documentation is written for Vega version 1.0.0
PDF version of this documentation is provided in the GitHub repository and this version can be read offline by typing orbit doc --doc
Preamble
In the software world, there are many programming languages with different paradigms and approaches for completing a given task.
Naturally, this has led to many interesting - and often fascinating - outcomes. It is undeniable that C++, Rust, Java, Erlang, Go, Haskell, and OCaml are both inspired by their priors and influenced many came after.
Therefore, it is very normal that many languages of this diverse world became torchbearers for Vega's design. To elaborate further, Vega is influenced by:
- Lua for its simple syntax and small set of keywords
- Golang for its single and robust way to do things, along with its profound system call library and simple FFI to cooperate with the system and 3rd party libraries
- Scala for its dynamic typing system
- Erlang for its lightweight and flexible concurrency
- Rust for its balanced approach to the functional and imperative programming paradigms
Vega is licensed with BSD-3 Clause License. Hence, please feel free to tinker internals, look how they work and release your own Vega version.
- Nix-Enthusiast
Keywords
Keywords are reserved words that provide core functionality to the programming language.
Vega has 23 keywords, each linked to its relevant section.
| Keyword | Look At | 
|---|---|
| run | Multithreading and Concurrency | 
| return | Functions and Behaviors | 
| if | Conditional Branching | 
| try | Error Handling | 
| catch | Error Handling | 
| end | Scopes | 
| is | Reflection | 
| and | Conditional Branching and math | 
| or | Conditional Branching and math | 
| xor | Conditional Branching and math | 
| not | Conditional Branching and math | 
| bsh | math | 
| true | Control Flow | 
| false | Control Flow | 
| nil | Variables | 
| noreturn | Functions | 
| mut | Variables | 
| maybe | Variables | 
| ! | Error Handling | 
| pub | Orb | 
| import | Importing Orbs | 
| as | Importing Orbs and behaviors | 
| external | FFI | 
Data Types
Data is an informative value, which is either in a structured or unstructured form and capable of both being derived and deriving others.
The primary purpose of any programming language is to store, transform, and manipulate data. Vega provides several distinct data types to accomplish this purpose.
Primitives
Primitives are the built-in essential types to build the rest of the language. Other types not mentioned here are either derived or built upon these primitives.
Integers
Integers are binary representations of whole values.
| Bits/Sign | Signed | Unsigned | 
|---|---|---|
| \(8\) | i8 | u8 | 
| \(16\) | i16 | u16 | 
| \(32\) | i32 | u32 | 
| \(64\) | i64 | u64 | 
| \(128\) | i128 | u128 | 
| \(pointer\)1 | int | uint | 
| \(2^{pointer}\) | ibig | 
Floats
The IEEE 754-compliant binary representation of real values with limited precision.
| Bits | Type | 
|---|---|
| \(16\) | f16 | 
| \(32\) | f32 | 
| \(64\) | f64 | 
| \(80\) | f80 | 
| \(128\) | f128 | 
| \(pointer\) | float | 
| \(2^{pointer}\) | fbig | 
FFI Numbers
The aliases of integer and float primitives used to import numeric types in their appropriate C representation from external libraries.
| C | Signed | Unsigned | 
|---|---|---|
| char | c_char | c_u_char | 
| short | c_short | c_u_short | 
| int | c_int | c_u_int | 
| long | c_long | c_u_long | 
| long long | c_long_long | c_u_long_long | 
| float | c_float | |
| double | c_double | |
| long double | c_long_double | 
bool
A single-bit value which can be either true or false.
Although one bit suffices for a boolean compiler has to allocate one byte for it, since the smallest memory block is 1 byte.
Main := |_, _|
good := true
bad := false
end
char
An u32 value that represents a single Unicode Scalar Value.
Main := |_, _|
a := 'a'
potato := '🥔'
end
- 
\(pointer\) is the pointer size of the system. ↩ 
Composite data types
Composite data types (CDTs) are used for storing more complex data in a structured composition in the memory.
Arrays
List ([N:t]T)
Growable array which allocated from heap to store T and the optional sentinel t, where t is T, with the minimum size of N.
Main := |_, _|
x := [1, 2, 3]
x := char['h', 'e', 'l', 'l', 'o']
end
Tuples ({})
An ordered set with finite size.
Declared using {T,V,..,N}
Main := |_, _|
coordinates := {74.5, 91.4}
end
Enums
Declared using '{} notation.
Enums are both tagged numbers and sum types.
Main := |_, _|
Base := '{
  /* C++ - like tagged numbers */
  Base2 i8 := 2
  Base8 i8 := 8
  Base16 i8 := 16
  
  /* Aliasing (type of the right hand side will be used) */
  Binary := Base.Base2
  Octal := Base.Base8
  Hexadecimal := Base.Base16
  /* Sum types */
  Other u8
}
myHex := Base.Hexadecimal
end
Records
Declared using #{} notation
Records are the data types to store data in a structured composition.
Main := |_, _|
Person := #{
  name str
  age u8
  id any
  faxNumber ?str
}
Vase(T) := #{ inner: T }
/*It's not necessary to state fields unless it is necessary to write in a specific order*/
Jan := { 
  "Jan"
  20
  "8675309"
  nil
}
vaseOfi8 := Vase(i8){ 23 }
end
str
Declared using "" notation.
str is basically an immutable and internable []u8 and used for containing information in text with UTF-8 encoding.
Main := |_, _|
msg := "Hello, World!"
end
Algebraic Data Types
Rationals
| Bits | Type | 
|---|---|
| \(8\) | r8 | 
| \(16\) | r16 | 
| \(32\) | r64 | 
| \(64\) | r32 | 
| \(128\) | r128 | 
| \(pointer\) | rat | 
| \(2^{pointer}\) | brat | 
Complex
| Bits | Type | 
|---|---|
| \(8\) | c8 | 
| \(16\) | c16 | 
| \(32\) | c64 | 
| \(64\) | c32 | 
| \(128\) | c128 | 
| \(pointer\) | cpx | 
| \(2^{pointer}\) | bcpx | 
Matrix (#[L:W]T)
A special list with the optional length of L and width of W, for advanced mathematical calculations.
Main := |_, _|
matrix_1 := #[1, 2, 3; 4, 5, 6; 7, 8, 9] 
matrix_2 := [3:3]int#[ 1 2 3
               4 5 6
               7 8 9 ]  
end               
Notations
Notations are short prefixes or suffixes to distinguish ambiguous literal values or prevent the compiler from using the default type for that literal.
Binary Notation (0b)
Binary notation is mostly used for bit flags.
 import std/testing/Assert
HAS_SWORD := 0b10000000
HAS_WAND := 0b01000000
HAS_ARROW := 0b00100000
HAS_ARMOR := 0b00010000
HAS_MINION := 0b00001000
MAGE := 0b00000100
WARRIOR := 0b00000010
ARCHER := 0b00000001
Main := |_, _|
/*Let's create a fellow archer with an armor, minion, and arrow*/
archer := 0b00111001
Assert archer and HAS_ARROW
Assert archer and HAS_ARMOR
Assert archer and HAS_MINION
Assert archer and ARCHER
end
Octal Notation (0o)
Octal notation is mostly used for file permissions of POSIX-compliant systems.
 import std/testing/Assert
EXEC := 0o1
WRITE := 0o2
READ := 0o4
RWX := 0o7
RE := 0o5
Main := |_, _|
perm := 0o755
user := (perm bsh 16) and RWX
group := ((perm bsh 8) and 0xFF) and RE
other := (perm and 0xFF) and RE
Assert user = RWX
Assert group = RE
Assert other = RE
end
Hexadecimal Notation (0x)
Hexadecimal notation is mostly used for reading/writing raw data as bytes.
 import std/testing/Assert
 import std/io/Read
MAGIC := [0x7f, 0x45, 0x4C, 0x46]
Main := |_, _|
read_bytes := Read("my_elf")
Assert read_bytes[..4] = MAGIC
end
Integer notation (_iN or _uN)
Integer notation is used for disambiguating a written compile-time integer to the requested type. N stands for the bit size of the integer.
 import std/testing/Assert
Main := |_, _|
number := 12.34_i16
number_big := 12.34_ibig
Assert number is i16
Assert number_big is ibig
end
Float notation (_fN)
Float notation is used for disambiguating a written compile-time float to the requested type. N stands for the bit size of the float.
 import std/testing/Assert
Main := |_, _|
number := 12.34_f16
number_big := 12.34_fbig
Assert number is f16
Assert number_big is fbig
end
Thousands Separator (_)
Thousands Separator is used to assist the readability of big integers.
 import std/testing/Assert
Main := |_, _|
Assert 100000000 = 100_000_000
end
Variables and Scopes
Variables are named pointers that point to data in the call stack or the heap.
Scopes are boundaries defined by functions (including anonymous ones), enums, unions, structs, or classes.
Vega takes a simple yet robust approach to both variables and scopes, ensuring behavioral correctness and type safety.
Variables
Vega variables are immutable and non-nullable by default, meaning their value cannot be changed or set to nil without explicit declaration.
Declaration
Declared using := (inferred form) or T := (typed form).
The type is assigned as T if it is given; otherwise, type inference will attempt to infer the type. If inference fails, any will be assigned.
integer := 4
floating := 3.14
message := "Hello from Vega!"
Nullable Variables
Declared using maybe.
If the inferred form is used and type inference fails, maybe any will be assigned.
maybe optional_int := 4
maybe optional_float float := 3.14
maybe optional_any := nil /* ?any */
Mutable Variables
Declared using mut.
Mutations are performed with <-.
If the inferred form is used and type inference fails, mut any will be assigned.
mut mutable_int := 4
mut mutable_float float := 3.14
mut maybe mutable_any := nil
mutable_int <- 5
mutable_float <- 4.14
mutable_any <- "Hi!"
The mut modifier must be written before the maybe
The reason lies on the lexical logic:
mut maybe x := 1 means
x is a mutable integer that may not have a value.
while maybe mut x := 1 means
x is maybe a mutable integer.