Keyboard shortcuts

Press ← or β†’ to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

The Atlas77 Programming Language

by Gipson62

This version of the documentation is currently only experimental and thoughts I gathered while developing the language. It may not reflect the current state of the language.

The documentation as a whole will be available offline in the compiler itself in future updates.

Introduction

Welcome to The Atlas77 Programming Language, an experimental book about the Atlas77 programming language. Experimental because I’m trying to gather and lay down my thoughts about the language as I develop it to normalize the language in itself.

Atlas77 is a statically typed, compiled programming language running on a VM that I do in my free time. I hope to be able to use it for future projects. Don’t expect it to be production-ready anytime soon.

I would describe it as a modern take of C++. And for that it takes inspiration from many languages like:

  • Rust/C++/C# for the syntax
  • Java/CPython for the runtime

Goals

  • Having a core::graphics module that links to Vulkan/OpenGL/DirectX.
  • Having a core::sdl module that links to SDL.
  • Bootstrapping the compiler.
  • Having a core::ffi module that allows interfacing with C libraries.
  • Having a core::ffi module that allows interfacing with Rust libraries.
  • Making a game with it.
  • Linking to blue_engine (see blue_engine documentation).
  • It should work 😁

Getting Started with Atlas 77

Welcome to Atlas 77! This guide will help you install the language and write your first program.

Your First Program

Here is a simple β€œHello, Atlas!” program:

import "std/io";

fun main() {
    println("Hello, Atlas!");
}

Save this code to a .atlas file, then run it with:

atlas_77 run <FILE_PATH>

For example:

atlas_77 run hello.atlas

Creating a Project

To create a new Atlas 77 project with standard structure:

atlas_77 init my_project
cd my_project
atlas_77 run

This creates a basic project template with all necessary files.

Next Steps

Comments

Comments document code and are ignored by the compiler.

Single-line Comments

Single-line comments start with // and continue to the end of the line:

// This is a comment
let x: int64 = 42;  // Comments can appear at the end of lines too

Note: Multi-line comments are not yet implemented.

In the future I’ll add support for documentation comments, though the syntax is not yet decided.

4. Variables

Variables in Atlas77 are either mutable or immutable. The design follows in some sense TypeScript/JavaScript, with the const & let keywords. Variables can be declared using the let keyword, which creates a mutable variable, or the const keyword, which creates an immutable variable.

import "std/io";

fun main() -> int64 {
    let x: int64 = 5;
    x = 10;
    print(x); // Output: 10

    const y: int64 = 5;
    y = 10; // Error: Cannot assign to a constant variable
}

5. Data Types

Atlas77 has several built-in data types, including integers, floating-point numbers, booleans, strings, and arrays. The following table lists the built-in data types in Atlas77:

Data TypeDescriptionState
int88-bit signed integerπŸ’€
int1616-bit signed integerπŸ’€
int3232-bit signed integerπŸ’€
int6464-bit signed integerβœ…
isizePlatform-dependent signed integerπŸ’€
uint88-bit unsigned integerπŸ’€
uint1616-bit unsigned integerπŸ’€
uint3232-bit unsigned integerπŸ’€
uint6464-bit unsigned integerβœ…
usizePlatform-dependent unsigned integerπŸ’€
float3232-bit floating-point numberπŸ’€
float6464-bit floating-point numberβœ…
boolBoolean value (true or false)βœ…
charUnicode characterβœ…
stringStringβœ…
arrayArray (syntax: [YourType])πŸ’­

6. Functions

Functions in Atlas77 are defined using the func keyword, followed by the function name, parameters, return type, and body. The return type of a function is specified after the -> symbol. For example:

import "std/io";

fun add(x: int64, y: int64) -> int64{
    return x + y;
}

fun main() -> int64 {
    let result: int64 = add(5, 10);
    print(result); // Output: 15
}

7. Control Structures

Atlas77 supports several control structures, including if statements, match expression, while loops, and for loops. The syntax for these control structures is similar to other programming languages. For example:

Control StructureDescriptionState
if statementConditional statementβœ…
match expressionPattern matching expressionπŸ’€
while loopLoop with a conditionβœ…
for loopLoop over a range or collectionπŸ’€
import "std/io";

fun main() -> int64 {
    let x = 5;

    if x > 0 {
        print("x is positive");
    } else if x < 0 {
        print("x is negative");
    } else {
        print("x is zero");
    }
    

    let i = 0;
    while i < 5 {
        print(i);
        i += 1;
    }
}

8. The standard library

Atlas77 comes with a relatively small standard library, which includes functions & types for input/output, file handling, string & list manipulation, time & math functions. The standard library is imported using the import keyword, followed by the library name. For example:

import "std/io";

fun main() {
    println("Hello, World!");
}

Check out the current state of the standard library.

9. Arrays

Arrays in Atlas77 are used to store multiple values of the same type. They are defined using square brackets []. For example:

import "std/io";

fun main() -> int64 {
    let numbers: [int64] = [1, 2, 3, 4, 5];
    let i = 0;
    while i < 5 {
        print(numbers[i]);
        i += 1;
    }
}

If you want, you can also allocate an empty array with a specific size:

import "std/io";

fun main() -> int64 {
    let size: int64 = 5;
    // Allocates an array of 5 int64s initialized to 0
    let numbers: [int64] = new [int64; size];
    let i = 0;
    while i < size {
        numbers[i] = i * 2; // Assign values
        print(numbers[i]);
        i += 1;
    }
}

10. Enums

Enums in Atlas77 are used to define a type that can have a set of named values. They are defined using the enum keyword. For example:

public enum Color {
    Red = 1;
    Yellow;
    Green = 3;
    Purple;
    Blue = 5;
}

11. Class & Structs

WIP

Current state of std/fs would be a good enough example of how classes/structs work in Atlas77:

private extern read_dir(path: string) -> [string];
private extern read_file(path: string) -> string;
private extern write_file(path: string, content: string);
private extern remove_file(path: string);
private extern file_exists(path: string) -> bool;
private extern close_file(path: string);

//NB: This struct works for now, but because of the lack of move/copy semantics in Atlas,
// it may lead to unexpected behavior.
public struct File {
private:
    content: string;
public:
    path: string;
public:
    /// Creates a new File object with the given path
    /// Note: The file is not opened until the open() method is called
    File(path: string) {
        this.content = "";
        this.path = path;
    }

    ~File() {
        //Following the RAII pattern, we close the file when it goes out of scope
        this.close();
    }

    fun read(this) -> string {
        let content = read_file(this.path);
        this.content = content;
        return this.content;
    }

    fun open(this) {
        this.content = read_file(this.path);
        return;
    }

    fun close(this) {
        close_file(this.path);
        return;
    }

    fun write(this, content: string) {
        write_file(this.path, content);
        return;
    }

    fun remove(this) {
        remove_file(this.path);
        return;
    }

    fun exists(this) -> bool {
        return file_exists(this.path);
    }

    fun read_dir(this, path: string) -> [string] {
        return read_dir(path);
    }

    fun read_file(this, path: string) -> string {
        return read_file(path);
    }
}

12. Generics

As of now you can define generic for external functions & structs. For example:

extern identity<T>(value: T) -> T;
struct Box<T> {
    value: T;
    Box(value: T) {
        self.value = value;
    }
    fun get_value(this) -> T {
        return this.value;
    }
}

13. Concepts

The name is still to be decided

Installation

Prerequisites

  • Rust Compiler
    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
    

NB: This is only for Unix-based systems (or WSL for Windows). For Windows, you can use Scoop

scoop install main/rust

Or directly from their website: Rust

Installation

Once Rust is installed, you can install Atlas77 using Cargo.

  1. Install it from Cargo
    cargo install atlas_77
    
  2. Use it as a CLI
    atlas_77 --help
    
  3. Enjoy!

Or you can clone the repository and build it yourself.

  1. Clone the repository
    git clone https://github.com/atlas77-lang/atlas77.git && cd atlas77
    
  2. Build it
    cargo build --release
    
  3. Use it as a CLI
    ./target/release/atlas_77 --help
    

Hello, World!

Programming a Guessing Game in Atlas77

Setting up

First create a new Atlas77 project:

atlas77 init guessing_game
cd guessing_game

Then open the src/main.atlas file in your favorite text editor.

Writing the Game Code

Language Reference

This document provides a comprehensive reference for Atlas 77 syntax, semantics, and language features.

Syntax Overview

Comments

Atlas 77 supports single-line comments only:

// This is a single-line comment
let x: int64 = 42; // Comments can appear at end of line

Note

Multi-line comments (/* ... */) are not yet implemented.

Keywords

Reserved keywords in Atlas 77 include: let, const, fun, struct, import, return, if, else, while, for, new, delete, public, private, this, and others.

Refer to Reserved Keywords for the complete list.

Variables and Constants

Variables are declared with let and constants with const. Type annotations are mandatory:

let x: int64 = 42;
let name: string = "Atlas";
const PI: float64 = 3.14159;

// Type inference is limited; explicit types are required
let result = 10; // Types can be inferred for initialized variables.

Types

Atlas 77 is statically typed and strongly typed. Here are the fundamental types:

Primitive Types

TypeDescriptionSize
int8Signed 8-bit integer1 byte
int16Signed 16-bit integer2 bytes
int32Signed 32-bit integer4 bytes
int64Signed 64-bit integer8 bytes
uint8Unsigned 8-bit integer1 byte
uint16Unsigned 16-bit integer2 bytes
uint32Unsigned 32-bit integer4 bytes
uint64Unsigned 64-bit integer8 bytes
float3232-bit floating point4 bytes
float6464-bit floating point8 bytes
boolBoolean (true / false)1 byte
charSingle Unicode character4 bytes
stringUTF-8 string (length in bytes)Variable
unitEmpty type (like void)β€”

Strings

Strings are UTF-8 encoded and their length is measured in bytes, not characters:

let greeting: string = "Hello, Atlas!";

Note

String type is currently temporary and may change in future versions.

Arrays

Arrays have fixed length known at compile-time:

let numbers: [int64] = [1, 2, 3, 4, 5];
let items: [string] = ["apple", "banana", "cherry"];

Generics

Atlas 77 supports parametric types (generics) with explicit type parameters (no type inference):

struct Box<T> {
public:
    value: T;
    Box(val: T) {
        this.value = val;
    }
}

fun get_value<T>(box: Box<T>) -> T {
    return box.value;
}

let int_box: Box<int64> = new Box<int64>(42);
let value: int64 = get_value::<int64>(int_box);

Type parameters must be explicitly specified at call sites. For more details, see Generics.

Functions

Functions are declared with fun keyword and require explicit return types:

fun add(a: int64, b: int64) -> int64 {
    return a + b;
}

fun greet(name: string) -> unit {
    println("Hello, " + name);
}

// Functions that return nothing use return type 'unit'
fun perform_action() -> unit {
    // No return statement needed for unit
}

Return Type Specification

Return types are mandatory for all functions. Use unit for functions that don’t return a value.

Operators and Precedence

Atlas 77 uses operator precedence similar to C/C++.

Operator Precedence Table

Listed from highest to lowest precedence:

PrecedenceOperatorAssociativityDescription
1() [] .Left-to-rightFunction call, array index, member access
2! - + ~Right-to-leftLogical NOT, unary minus, unary plus, bitwise NOT
3* / %Left-to-rightMultiplication, division, modulo
4+ -Left-to-rightAddition, subtraction
5<< >>Left-to-rightBitwise shift left, shift right
6< <= > >=Left-to-rightRelational operators
7== !=Left-to-rightEquality operators
8&Left-to-rightBitwise AND
9^Left-to-rightBitwise XOR
10|Left-to-rightBitwise OR
11&&Left-to-rightLogical AND
12||Left-to-rightLogical OR
13= += -= *= /= etc.Right-to-leftAssignment operators

Note

Operator overloading is planned for a future version. Some operators have not been implemented yet.

Structs

Structs are product types that group related data:

struct Person {
private:
    age: int32;
public:
    name: string;
    email: string;
    
    Person(name: string, email: string, age: int32) {
        this.name = name;
        this.email = email;
        this.age = age;
    }
    
    fun display(this: Person) -> unit {
        println(this.name);
    }
}

Visibility Rules

By default, all struct fields and methods are private. To make fields or methods public, precede them with a public: block:

struct Config {
private:
    internal_state: int64;
public:
    api_key: string;
    timeout: int32;
}
  • Only api_key and timeout are accessible outside the struct
  • internal_state is private and only accessible within struct methods

Methods

Methods are functions associated with a struct. The receiver (this) is passed explicitly:

fun display(this) -> unit {
    println(this.name);
}

References

References allow you to work with values without taking ownership. References are not nullable.

Warning

Reference design is still a work in progress. There is no guarantee that the current design will remain stable.

Reference Syntax

let x: int64 = 42;
let mutable_ref: &int64 = &x;      // Mutable reference
let immutable_ref: &const int64 = &const x;  // Immutable reference

Reference Behavior

  • References are trivially copyable and are copied implicitly
  • References are not rebindable (may change in future versions)
  • References cannot be null; all references point to valid values
  • Taking a reference: &my_var (mutable), &const my_var (immutable)

Copy and Move Semantics

Copy Semantics (Implicit)

Primitive types and references are implicitly copyable:

let a: int64 = 10;
let b: int64 = a;  // 'a' is copied; both 'a' and 'b' exist

let ref_a: &int64 = &a;
let ref_b: &int64 = ref_a;  // Reference is copied

Move Semantics (Default for Custom Types)

For custom structs, values are moved by default unless the struct implements a Copy constructor:

struct Resource {
public:
    data: string;
}

let r1: Resource = Resource("data");
let r2: Resource = r1;  // 'r1' is moved to 'r2'; 'r1' no longer accessible
// Using 'r1' here would be a compile error

Copy Constructor (Opt-in)

To make a custom type copyable, implement a Copy constructor:

struct CopyableData {
public:
    value: int64;
    
    CopyableData(val: int64) {
        this.value = val;
    }
    
    // Copy constructor (opt-in copyability)
    fun Copy(this: CopyableData) -> CopyableData {
        return CopyableData(this.value);
    }
}

let d1: CopyableData = CopyableData(100);
let d2: CopyableData = d1;  // Now 'd1' is copied, both exist

[!Warning] The copyability of all standard library types is uncertain. Treat standard library types as potentially non-copyable until verified.

Memory Management

Atlas 77 uses manual memory management with automatic cleanup at scope boundaries.

Allocation and Deallocation

// Allocate memory with 'new'
let ptr: &MyStruct = &new MyStruct(...);

// Deallocate explicitly with 'delete'
delete ptr;

Automatic Deallocation (RAII)

The compiler automatically inserts delete instructions at the end of each scope for variables that haven’t been moved:

fun example() -> unit {
    let resource: MyStruct = MyStruct();
    // ... use resource ...
} // Compiler automatically calls delete on 'resource' here

Destructors

When a value is deleted, its destructor is called before memory is freed. Destructors clean up resources:

struct File {
    path: string;
    
    // Destructor: called by delete
    fun ~File(this: File) -> unit {
        // Close file, cleanup resources
    }
}

Note

The current runtime is marked as deprecated. Precise semantics of destructors and scope-based cleanup may evolve.

Error Handling

Atlas 77 provides Option<T> and Result<T, E> types for error handling. There is no implicit error propagation (no try/? operator); handle errors explicitly.

Option Type

struct Option<T> {
public:
    has_value: bool;
    value: T;  // Only valid if has_value is true
}

fun find_user(id: int64) -> Option<User> {
    if id > 0 {
        return Option(true, User(id));
    } else {
        return Option(false, User(0));
    }
}

// Using Option
let user_opt: Option<User> = find_user(1);
if user_opt.has_value {
    println("Found user");
} else {
    println("User not found");
}

Result Type

struct Result<T, E> {
public:
    is_ok: bool;
    ok_value: T;    // Only valid if is_ok is true
    err_value: E;   // Only valid if is_ok is false
}

fun parse_int(s: string) -> Result<int64, string> {
    // Try to parse
    if success {
        return Result(true, parsed_value, "");
    } else {
        return Result(false, 0, "Failed to parse");
    }
}

// Using Result
let result: Result<int64, string> = parse_int("42");
if result.is_ok {
    let number: int64 = result.ok_value;
    println("Parsed: " + number);
} else {
    println("Error: " + result.err_value);
}

Unwrapping

let result: Result<int64, string> = parse_int("42");
let number: int64 = result.ok_value; // Unwrap: use value directly (risky!)

Note

There is no automatic error propagation syntax (try / ? operator) by design to maintain explicit control and avoid β€œhidden magic” in error handling.

Modules and Imports

Current Import System

Use the import statement to include modules:

import "std/io";
import "std/fs";
import "std/string";

fun main() -> unit {
    println("Hello, Atlas!");
}

Imports use file paths (not yet package-based). The compiler locates module files relative to the standard library.

Warning

Multiple imports of the same module may result in duplication even with guard mechanisms. This is a known limitation of the current system.

Future: Package-Based Imports

Package-based imports and selective imports are planned for after the compiler bootstrap phase. Expected syntax:

// Planned (not yet available):
// import mypackage::io;
// from mypackage import io, fs;

Control Flow

If-Else

if condition {
    // ...
} else if other_condition {
    // ...
} else {
    // ...
}

While Loops

while x < 10 {
    x = x + 1;
}

For Loops (Basic)

Basic for loops are currently supported:

for i in 0..10 {  // Range iteration is planned
    println(i);
}

Note

Range-based for-loops (for x in range) are planned for a future version.

Planned Features

The following features are actively developed and planned for future releases:

  • Operator Overloading – Define custom behavior for operators like +, -, *, etc.
  • For-loops over Ranges – Iterate over ranges with syntax like for i in 0..10
  • Pattern Matching – Destructure and match values
  • First-class Functions and Closures – Functions as values, anonymous functions, closures
  • Async/Await – Asynchronous programming support
  • Traits (or β€œConcepts”) – Define shared behavior across types
  • Union Types – Discriminated unions for variant data
  • Package System – Dependency management and package organization
  • Package Manager – Tool for downloading and managing packages
  • Copy & Move Semantics Refinement – Better documentation and ergonomics
  • Dead Code Elimination – Optimization pass to remove unused code
  • Cranelift Backend – Native code generation (in addition to VM execution)
  • Compiler Error Recovery – Better error messages and recovery from multiple errors

Known Limitations and Footguns

  • No multiline comments – Only single-line comments with //
  • No pattern matching – Cannot destructure values into patterns
  • No break/continue – Loop control statements not yet implemented
  • No variadics – Variable argument functions not supported
  • No named parameters – All parameters are positional
  • No inheritance – Structs use composition instead of inheritance
  • No type inference – All type annotations must be explicit
  • String type is temporary – May change in future versions
  • stdlib copyability uncertain – Assume standard library types are non-copyable

For detailed examples and tutorials, see:

Memory Model

Atlas 77 employs manual memory management with automatic scope-based cleanup. This document explains how memory allocation, deallocation, and ownership work.

Allocation with new

Memory is allocated using the new keyword:

struct Person {
public:
    name: string;
    age: int32;
    
    Person(name: string, age: int32) {
        this.name = name;
        this.age = age;
    }
}

let person: &Person = &new Person("Alice", 30);

Deallocation with delete

Memory is freed using the delete keyword:

delete person;  // Free memory and call destructor

The delete operation:

  1. Calls the destructor (if one exists)
  2. Frees the allocated memory
  3. Invalidates the reference

Automatic Scope-Based Cleanup (RAII)

The compiler automatically inserts delete instructions at the end of each scope for variables that haven’t been moved yet:

fun process_file() -> unit {
    let file: File = File("data.txt");
    
    // ... use file ...
    
} // Compiler automatically calls: delete file;

This pattern is called RAII (Resource Acquisition Is Initialization):

  • Resources are acquired during object construction (new)
  • Resources are released automatically at scope exit via destructors

Example: File Resource Management

struct File {
    path: string;
    is_open: bool;
    
    // Constructor acquires resource
    File(path: string) {
        this.path = path;
        this.is_open = true;
        // Open file
    }
    
    // Destructor releases resource
    fun ~File(this: File) -> unit {
        if this.is_open {
            // Close file
            this.is_open = false;
        }
    }
}

fun read_and_process() -> unit {
    let file: File = File("input.txt");
    let content: string = file.read();
    
    // ...
    
} // File destructor called here; file is automatically closed

Ownership and Move Semantics

Atlas 77 uses move semantics by default for custom types. When you assign a value, ownership transfers to the new variable:

struct Box<T> {
public:
    value: T;
}

let box1: Box<int64> = Box(42);
let box2: Box<int64> = box1;  // Ownership moves from box1 to box2

// box1 is no longer accessible; moved values cannot be used
// println(box1.value);  // ERROR: box1 has been moved

Once a value is moved, the original variable becomes inaccessible. This prevents use-after-free bugs:

fun take_ownership(b: Box<int64>) -> unit {
    println(b.value);
    // b is deleted at end of scope
}

let box: Box<int64> = Box(100);
take_ownership(box);  // Ownership transfers to function
// println(box.value);  // ERROR: box has been moved

Copy Semantics

Implicit Copy (Primitive Types and References)

Primitive types and references are implicitly copyable:

let x: int64 = 42;
let y: int64 = x;  // 'x' is copied; both x and y exist

let ref_x: &int64 = &x;
let ref_y: &int64 = ref_x;  // Reference is copied

Opt-In Copy (Custom Types)

To make a custom type copyable, implement a Copy constructor:

struct MyData {
public:
    value: int64;
    
    MyData(val: int64) {
        this.value = val;
    }
    
    // Copy constructor
    fun Copy(this: MyData) -> MyData {
        return MyData(this.value);
    }
}

let data1: MyData = MyData(100);
let data2: MyData = data1;  // Now data1 is copied; both data1 and data2 exist

Without the Copy constructor, assignment moves instead of copying.

References

References allow borrowing values without transferring ownership. References in Atlas 77 are:

  • Not nullable – always point to valid values
  • Trivially copyable – copying is implicit and cheap
  • Not rebindable – may change in future versions

Reference Syntax

let value: int64 = 42;

// Mutable reference
let mutable_ref: &int64 = &value;

// Immutable reference
let immutable_ref: &const int64 = &const value;

Using References

fun modify_value(ref: &int64) -> unit {
    *ref = 100;  // Dereference and modify
}

let x: int64 = 42;
modify_value(&x);
println(x);  // 100

Warning

Reference design is still evolving and heavily inspired by Rust. Current behavior and semantics may change.

Destructors

Destructors are special methods that clean up resources when an object is deleted:

struct Resource {
public:
    ptr: int64;  // Some resource pointer
    
    Resource() {
        // Acquire resource
        this.ptr = allocate();
    }
    
    // Destructor: called by delete
    fun ~Resource(this: Resource) -> unit {
        if this.ptr != 0 {
            deallocate(this.ptr);
            this.ptr = 0;
        }
    }
}

Destructors are called:

  1. When delete is explicitly called
  2. When scope exits (automatic cleanup)
  3. In the correct order for nested scopes

Destructor Execution Order

For scoped values, destructors are called in reverse order of construction (LIFO):

fun example() -> unit {
    let a: Resource = Resource();  // Constructed first
    let b: Resource = Resource();  // Constructed second
    
    // ...
    
} // Destructors called: ~b, then ~a (reverse order)

Scope-Based Cleanup Rules

  1. Single scope: Variables deleted at end of scope
  2. Early returns: Variables deleted before returning
  3. Moved values: Not deleted (ownership transferred)
  4. Conditional scopes: Variables deleted when block exits
fun conditional_cleanup(flag: bool) -> unit {
    let resource1: Resource = Resource();
    
    if flag {
        let resource2: Resource = Resource();
        // resource2 deleted here
    }
    
    // resource1 deleted here
}

Lifetime and Validity

References must remain valid for their entire lifetime:

fun create_ref() -> &int64 {
    let x: int64 = 42;
    return &x;  // ERROR: x will be deleted at end of scope
}

This is a common pitfall. Values cannot outlive their owners:

fun valid_reference(x: &int64) -> &int64 {
    return x;  // OK: reference comes from parameter, guaranteed to be valid
}

Current Limitations

  • Runtime is deprecated – Precise semantics may evolve
  • Destructor semantics uncertain – Copy/move interaction with destructors being refined
  • stdlib copyability uncertain – Assume standard library types are non-copyable unless documented

Future Improvements

  • Smart Pointers (rc_ptr<T>) – Reference-counted pointers for shared ownership
  • Guaranteed Copy Constructor Optimization – Automatic elision of unnecessary copies
  • Move Semantics Refinement – Clearer rules for implicit copying vs. moving

See Language Reference for details on copy/move semantics.

Error Handling in Atlas 77

Atlas 77 provides explicit error handling through Option<T> and Result<T, E> types. There is no implicit error propagation; errors must be handled explicitly to avoid β€œhidden magic.”

Philosophy

Error handling in Atlas 77 is explicit and intentional:

  • No automatic exception throwing or catching
  • No implicit try/? operator for error propagation
  • Errors must be checked and handled at the point they occur
  • This gives you maximum control over error recovery

Option Type

The Option<T> type represents a value that may or may not exist:

struct Option<T> {
public:
    has_value: bool;
    value: T;  // Only valid when has_value is true
}

Use Option<T> for fallible operations that don’t need error details:

fun find_user(id: int64) -> Option<User> {
    if id > 0 {
        // Found user
        let user: User = User(id);
        return Option(true, user);
    } else {
        // Not found
        return Option(false, User(0));  // Default user
    }
}

Checking Option Values

let user_opt: Option<User> = find_user(42);

// Check if value exists
if user_opt.is_some() {
    let user: User = user_opt.unwrap();
    println("Found user: " + user.name);
} else {
    println("User not found");
}

Result Type

The Result<T, E> type represents either a success with a value, or a failure with an error:

enum ResultTag {
    ERR = 0;
    OK = 256;
}
struct Result<T, E> {
private:
    tag: ResultTag;
    ok_value: T;
    err_value: E;
}

Use Result<T, E> when operations can fail and you need to communicate why:

fun parse_int(s: string) -> Result<int64, string> {
    // Try to parse string to integer
    if is_valid_number(s) {
        let number: int64 = convert_to_int(s);
        return Result::<int64, string>::ok(number);
    } else {
        return Result::<int64, string>::err("Invalid number format");
    }
}

Checking Result Values

let result: Result<int64, string> = parse_int("42");

// Explicit check with is_ok
if result.is_ok() {
    let number: int64 = result.unwrap();
    println("Parsed: " + number);
} else {
    let error: string = result.unwrap_err();
    println("Error: " + error);
}

Unwrapping Results

If you’re confident a Result is Ok, you can unwrap directly (at your own risk):

let result: Result<int64, string> = parse_int("42");
let number: int64 = result.unwrap();  // Unwrap without check (risky!)

// If is_ok is false, you'll get a default/undefined value
// Use only when you're certain the operation succeeded

Warning: Unwrapping without checking can lead to using garbage values if the operation failed. Always check is_ok first unless you have a strong reason not to.

Panic

For unrecoverable errors, use panic() to abort the program:

import "std/io";

fun critical_operation() {
    if invalid_state {
        panic("Critical error: invalid state detected!");
    }
}

panic() will:

  1. Print the error message
  2. Terminate the program immediately
  3. Bypass normal cleanup (use sparingly)

Pattern Examples

Option Handling

struct User {
public:
    id: int64;
    name: string;
}

fun get_user_name(id: int64) -> Option<string> {
    let user: Option<User> = find_user(id);
    
    if user.is_some() {
        return Option::<string>::ok(user.unwrap().name);
    } else {
        return Option::<string>::none();
    }
}

// Usage
let name_opt: Option<string> = get_user_name(1);
if name_opt.is_some() {
    print("User: ");
    println(name_opt.unwrap().value);
} else {
    println("User not found");
}

Result Chaining

When multiple fallible operations depend on each other:

fun read_and_parse() -> Result<int64, string> {
    // First operation
    let content: Result<string, string> = read_file("numbers.txt");
    if !content.is_ok() {
        return Result::<int64, string>::err(content.unwrap_err());
    }
    
    // Second operation (depends on first)
    let number: Result<int64, string> = parse_int(content.ok_value);
    if !number.is_ok() {
        return Result::<int64, string>::err(number.unwrap_err());
    }
    
    // Success
    return Result::<int64, string>::ok(number.unwrap());
}

Nested Results with Meaningful Errors

struct ParseError {
public:
    line: int64;
    column: int64;
    message: string;
}

fun parse_config(path: string) -> Result<Config, ParseError> {
    // Try to read file
    let content: Result<string, string> = read_file(path);
    if !content.is_ok() {
        let error: ParseError = new ParseError(0, 0, content.unwrap_err());
        return Result::<Config, ParseError>::err(error);
    }
    
    // Try to parse content
    let config: Result<Config, ParseError> = parse_config_text(content.unwrap());
    return config;
}

Best Practices

  1. Check explicitly – Always use is_ok()/is_err() checks before accessing values
  2. Provide context – Include helpful error information in Result<T, E>
  3. Fail fast – Return errors early rather than propagating invalid states
  4. Use Option for simple cases – When you only need to know if something exists
  5. Use Result for complex cases – When you need to communicate failure reasons
  6. Avoid panics in libraries – Let caller decide how to handle errors
  7. Document error cases – Explain what errors an operation can produce

Future Improvements

In the future, Atlas 77 may introduce:

  • Discriminated unions for more flexible error types
  • Error traits for better composability

However, the core design will remain explicit and visible, avoiding hidden control flow.


See also:

Reserved Keywords

List of all of the reserved keywords in the language:

Branching and control flow

  • if/else
  • while/for
  • break/continue
  • return

Declarations

  • public/private (access modifiers for declarations)
  • import (for importing modules/packages)
  • package (for defining packages/modules)
  • fun (for defining functions)
  • let/const (for defining variables)
  • struct (for defining structures)
  • enum (for defining enumerations)
  • class (for defining classes)

Not used yet, but reserved in case of future use.

  • concept (for defining concepts which are like interfaces)

Not used yet, but reserved because it’s a feature planned for the future

  • extern (for defining external functions/variables)

Primitives and types

Primitives types are considered to be keywords in Atlas 77, though this might change in the future.

  • string (for string type)
  • char (for character type)
  • int8/int16/int32/int64 (for signed integer types)
  • uint8/uint16/uint32/uint64 (for unsigned integer types)
  • float32/float64 (for floating-point types)
  • bool (for boolean type)
  • unit (for unit type)
  • true/false (for boolean literals)
  • This/this (for referring to the current instance in classes/structs)

Other keywords

  • new/delete (for memory management)
  • as (for type casting)
  • comptime (for compile-time evaluation)

Not used yet, but reserved in case of future use.

  • operator (for operator overloading)

Not used yet, but reserved in case of future use.

Standard Library

Note

All the standard library is a work in progress. Most of the modules aren’t implement or finished yet. The documentation is here to give an idea of what the standard library will look like.

ModuleDescription
std/ioInput/output functions
std/fsFile handling functions
std/stringString manipulation functions
std/vectorVector manipulation functions
std/timeTime functions
std/mathMath functions
std/optionOption type and functions
std/resultResult type and functions
std/boxBox type and functions
std/iterIterator type and functions
std/mapMap type and functions

You can see what is implemented with the checkboxes below.

std/io

  • print<T>(val: T) -> unit: Print a value to the standard output.
  • println<T>(val: T) -> unit: Print a value to the standard output followed by a newline.
  • input() -> str: Read a line from the standard input.
  • panic(msg: string) -> !: Abort the program with an error message.

std/fs

Structs

  • File: A file type that can be used to read and write files.

NB: File is not stable yet. BEWARE.

Lack of copy/move semantics makes it tricky to handle files safely.

struct File {
private:
    content: string;
public:
    path: string;
    File(path: string) {
        this.content = "";
        this.path = path;
    }
}

Methods

  • read(this: File) -> string: Read the entire content of the file.
  • open(path: string) -> File: Open a file for reading and writing.

Does the same shit as β€œread()” but it makes more sense to open once then use read() later if the file got updated

  • close(this: File) -> unit: Close the file.

NB: This is called automatically in the destructor, BUT because of the current state of the memory, you might wanna do it manually just to be sure.

  • exists(this: File) -> bool: Check if the file exists.
  • read_dir(this: File) -> [string]: Read the contents of a directory.
  • read_file(this: File) -> string: Read the entire content of the file and returns it to you. It does not set File.content to the content of the file.

Functions

  • read_dir(path: string) -> [string]: Read the contents of a directory.
  • read_file(path: string) -> string: Read the entire content of a file.
  • write_file(path: string, content: string) -> unit: Write content to a file.
  • remove_file(path: string) -> unit: Remove a file.
  • file_exists(path: string) -> bool: Check if a file exists.
  • close_file(path: string) -> unit: Close a file.

std/string

Structs

  • String: A string type that can be used to manipulate strings.
struct String {
    s: string;
    len: uint64;
    String(s: string) {
        self.s = s;
        self.len = str_len(s);
    }
}

Methods

  • String::from_chars(chars: [char]) -> String: Create a new String struct from a list of characters.
  • String::str_len(s: string) -> uint64: Get the length of a string primitive.
  • len(this: String) -> uint64: Get the length of the String.
  • is_empty(this: String) -> bool: Check if the String is empty.
  • concat(this: String, other: String) -> String: Concatenate two Strings.
  • push(this: String, c: char) -> unit: Add a character to the end of the String.
  • push_str(this: String, s: String) -> unit: Add a String to the end of the String.
  • find(this: String, sub_string: String) -> Option<uint64>: Find the index of a substring in the String.

Not stable yet. BEWARE.

  • get(this: String, index: uint64) -> char: Get a character from the String by index.
  • set(this: String, index: uint64, c: char) -> unit: Set a character in the String by index.
  • to_str(this: String) -> string: Convert the String struct to a string primitive.
  • to_chars(this: String) -> [char]: Convert the String struct to a list of characters.
  • to_upper(this: String) -> String: Convert the String to uppercase.
  • to_lower(this: String) -> String: Convert the String to lowercase
  • trim(this: String) -> String: Trim whitespace from both ends of the String.
  • split(this: String, sep: string) -> [String]: Split the String by a separator.
  • into_iter(this: String) -> Iter<char>: Create an iterator (from std/iter) over the characters of the String.

Functions

  • str_len(s: string) -> uint64: Get the length of a string primitive.
  • trim(s: string) -> string: Trim whitespace from both ends of a string.
  • to_upper(s: string) -> string: Convert a string to uppercase.
  • to_lower(s: string) -> string: Convert a string to lowercase.
  • split(s: string, sep: string) -> [string]: Split a string by a separator.
  • str_cmp(s1: string, s2: string) -> uint64: Compare two strings.
  • to_chars(s: string) -> [char]: Convert a string to a list of characters.
  • from_chars(s: [char]) -> string: Convert a list of characters to a string.

std/vector

Structs

  • Vector<T>: A vector is a dynamic array that can grow or shrink in size. It has a bunch of methods to manipulate the data.
struct Vector<T> {
private:
    data: [T];
public:
    length: uint64;
    capacity: uint64;
    Vector(data: [T]) {
        this.length = len(data);
        this.capacity = this.length;
        this.data = data;
    }
}

Methods

  • len(this: Vector<T>) -> uint64: Get the length of the vector.
  • is_empty(this: Vector<T>) -> bool: Check if the vector is empty.
  • push(this: Vector<T>, val: T) -> unit: Add an element to the end of the vector.
  • pop(this: Vector<T>) -> T: Remove and return the last element of the vector.
  • get(this: Vector<T>, index: uint64) -> T: Get an element from the vector by index.
  • set(this: Vector<T>, index: uint64, val: T) -> unit: Set an element in the vector by index.
  • into_iter(this: Vector<T>) -> Iter<T>: Create an iterator (from std/iter) over the elements of the vector.
  • Vector::<T>::with_capacity(capacity: uint64) -> Vector<T>: Create a new vector with a specified capacity.

Functions

  • len<T>(lst: [T]) -> uint64: Get the length of a list.
  • slice<T>(lst: [T], start: uint64, end: uint64) -> [T]: Get a slice of a list from start to end.

std/time

Structs

  • Time: A time type that can be used to represent time.
struct Time {
public:
    sec: int64;
    nsec: int64;
    Time(sec: int64, nsec: int64) {
        this.sec = sec;
        this.nsec = nsec;
    }
}

Methods

  • Time::now() -> Time: Get the current time.
  • format(this: Time, fmt: str) -> str: Format the time as a string.
  • to_iso_string(this: Time) -> str: Convert the time to an ISO 8601 string.

Yeah well, I don’t know why this function exists, but who knows, maybe it will be useful one day.

  • sleep(this: Time) -> unit: Sleep for the specified time.

NB: This is a blocking sleep. Might not work. BEWARE.

  • elapsed(this: Time, since: Time) -> uint64: Calculate the elapsed time between two time values in milliseconds.

Functions

  • now() -> Time: Get the current time.
  • sleep(t: Time) -> unit: Sleep for the specified time.

NB: This is a blocking sleep. Might not work. BEWARE.

  • format_time(t: Time, fmt: str) -> str: Format the time as a string.

std/math

Functions

  • abs(x: int64) -> int64: Compute the absolute value of a integer number.
  • abs_f(x: float64) -> float64: Compute the absolute value of a floating-point number.
  • round(x: float64) -> int64: Round a floating-point number to the nearest integer.
  • random(min: int64, max: int64) -> int64: Generate a random integer number in the range [min, max].
  • pow(x: int64, y: int64) -> int64: Compute x raised to the power of y.
  • pow_f(x: float64, y: float64) -> float64: Compute x raised to the power of y.
  • min(x: int64, y: int64) -> int64: Compute the minimum of two integer numbers.
  • min_f(x: float64, y: float64) -> float64: Compute the minimum of two floating-point numbers.
  • max(x: int64, y: int64) -> int64: Compute the maximum of two integer numbers.
  • max_f(x: float64, y: float64) -> float64: Compute the maximum of two floating-point numbers.

std/option

Structs

  • Option<T>: A type that represents an optional value, which can either be SOME or NONE.
private enum OptionTag {
	NONE = 0;
	SOME = 256;
}

struct Option<T> {
private:
  	tag: OptionTag;
  	data: T;
  	Option(data: T, tag: OptionTag) {
		this.tag = tag;
		this.data = data;
	}
}

Methods

  • is_some(this: Option<T>) -> bool: Check if the option is SOME.
  • is_none(this: Option<T>) -> bool: Check if the option is NONE.
  • unwrap(this: Option<T>) -> T: Get the value inside the option if it is SOME, otherwise panic.
  • unwrap_or(this: Option<T>, default: T) -> T: Get the value inside the option if it is SOME, otherwise return the default value.
  • map<U>(this: Option<T>, f: (T) -> U) -> Option<U>: Apply a function to the value inside the option if it is SOME, otherwise return NONE.

Don’t exist yet.

std/result

Structs

  • Result<T, E>: A type that represents either a success OK carrying the type T or an error ERR carrying the type E.
private enum ResultTag {
    OK = 0;
    ERR = 256;
}

struct Result<T, E> {
private:
    data: T;
    err: E;
    tag: ResultTag;
    Result(data: T, err: E, tag: ResultTag) {
        this.data = data;
        this.err = err;
        this.tag = tag;
    }
}

Methods

  • Result::<T, E>::ok(value: T) -> Result<T, E>: Create a new OK result.
  • Result::<T, E>::err(error: E) -> Result<T, E>: Create a new ERR result.
  • is_ok(this: Result<T, E>) -> bool: Check if the result is OK.
  • is_err(this: Result<T, E>) -> bool: Check if the result is ERR.
  • unwrap(this: Result<T, E>) -> T: Get the value inside the result if it is OK, otherwise panic.
  • unwrap_err(this: Result<T, E>) -> E: Get the error inside the result if it is ERR, otherwise panic.
  • unwrap_or(this: Result<T, E>, default: T) -> T: Get the value inside the result if it is OK, otherwise return the default value.
  • unwrap_err_or(this: Result<T, E>, default: E) -> E: Get the error inside the result if it is ERR, otherwise return the default error.
  • map<U>(this: Result<T, E>, f: (T) -> U) -> Result<U, E>: Apply a function to the value inside the result if it is OK, otherwise return the ERR.

Don’t exist yet.

  • map_err<F>(this: Result<T, E>, f: (E) -> F) -> Result<T, F>: Apply a function to the error inside the result if it is ERR, otherwise return the OK.

Don’t exist yet.

std/box

Structs

  • Box<T>: A type that represents a heap-allocated value of type T.
struct Box<T> {
private:
    data: T;
public:
    Box(data: T) {
        this.data = data;
    }
}

Methods

  • get(this: Box<T>) -> T: Get the value inside the box.
  • set(this: Box<T>, value: T) -> unit: Set the value inside the box.

std/iter

Structs

  • Iter<T>: An iterator type that can be used to iterate over a collection of type T. (as of now it only iters over a Vector<T>.)
struct Iter<T> {
private:
    data: Vector<T>;
    index: uint64;
public:
    Iter(data: Vector<T>) {
        this.data = data;
        this.index = 0_uint64;
    }
}

Methods

  • next(this: Iter<T>) -> Option<T>: Get the next element in the iterator.
  • peek(this: Iter<T>) -> Option<&const T>: Peek at the next element in the iterator without advancing it.
  • has_next(this: Iter<T>) -> bool: Check if the iterator has more elements.
  • Iter::<T>::from_array(data: [T]) -> Iter<T>: Create a new iterator from an array.
  • Iter::<T>::from_string(data: string) -> Iter<char>: Create a new iterator from a primitive string.

std/map

Structs

  • Map<K, V>: A type that represents a key-value store.
struct Map<K, V> {
private:
    keys: Vector<K>;
    values: Vector<V>;
public:
    Map() {
        this.keys = new Vector<K>(new [K; 0]);
        this.values = new Vector<V>(new [V; 0]);
    }
}

Methods

  • insert(this: Map<K, V>, key: K, value: V) -> unit: Insert a key-value pair into the map.
  • get(this: Map<K, V>, key: K) -> Option<V>: Get a value from the map by key.
  • remove(this: Map<K, V>, key: K) -> unit: Remove a key-value pair from the map.
  • contains(this: Map<K, V>, key: K) -> bool: Check if a key exists in the map.
  • size(this: Map<K, V>) -> uint64: Get the number of key-value pairs in the map.

Experimental Modules (Not Yet Included)

The following modules are under active development and are not included in the current standard library:

std/rc_ptr

Status: Experimental, work in progress

Reference-counted smart pointers for shared ownership:

// Planned usage (not yet available):
// let ptr: rc_ptr<Data> = rc_ptr::new(Data());
// let cloned: rc_ptr<Data> = ptr.clone();  // Reference count increases

std/cast

Status: Experimental, work in progress

Type casting and conversion utilities:

// Planned usage (not yet available):
// let num: int64 = 42;
// let as_float: float64 = cast<float64>(num);

Notes

  • All modules are works in progress and subject to change.
  • Most modules are not yet fully implemented or finalized.
  • For experimental modules listed above, expect significant API changes.
  • Standard library types may not be copyable; assume move semantics unless documented otherwise.

Blue Engine Library

Blue Engine is the experimental FFI testbed connecting Atlas77 to Rust. It serves as one of the core libraries shipped with Atlas77 and can be imported directly:

import blue_engine::triangle;
import blue_engine::Engine;
import blue_engine::ObjectSettings;
import blue_engine::call_update_function;
import std::result;
import std::time;

fun main() {
  let engine = Engine::init();
  triangle("my_triangle", ObjectSettings::init("default"), &engine.renderer, &engine.objects).expect("Failed to create triangle");
  let start = Time::now();
  call_update_function(&engine, "update", start);
}

fun update(delta: Time, engine: &Engine) {
  return;
}

The objective is to replicate the public API and general usage patterns of the original Blue Engine crate while adapting it to Atlas77’s execution model, type system, and VM. This library is under active development. Interfaces may change, features may remain incomplete, and stability is not guaranteed.

Current scope: a safe Atlas77 wrapper layer built entirely on extern_ptr and extern_fn, exposing Rust engine components without unsafe surface area in Atlas77.

Atlas 77 Language Roadmap

This document outlines the planned features and improvements for Atlas 77. Features are organized by category and development status.

Current Version

Atlas 77 is currently in early development (v0.6). The compiler and runtime are functional but not production-ready. Expect breaking changes and API modifications.

Language Features

Implemented βœ…

  • Static typing – All variables require explicit type annotations
  • Structs – Product types with fields and methods
  • Functions – First-class declaration with explicit return types
  • Generics – Parametric types with explicit type parameters
  • Control flow – if/else, while loops
  • Memory management – new/delete with RAII cleanup
  • References – Borrowing without ownership transfer
  • Copy/Move semantics – Copy implicit for primitives, move for custom types
  • Error types – Option<T> and Result<T, E> for error handling
  • Comments – Single-line comments with //

Planned 🎯

High Priority

  1. Operator Overloading

    • Allow custom implementations of +, -, *, /, ==, <, etc.
    • Enable ergonomic syntax for user-defined types
    • Status: Design phase
  2. For-loops over Ranges

    • Syntax: for i in 0..10 { ... }
    • Support inclusive ranges: 0..=10
    • Status: Design phase
  3. Pattern Matching

    • Match expressions
    • Status: Design phase
  4. Copy & Move Semantics Refinement

    • Better ergonomics for ownership patterns
    • Improved compiler diagnostics
    • Status: In progress
  5. Dead Code Elimination

    • Optimization pass to remove unused variables, functions, and code
    • Status: Planned
  6. Compiler Improvements

    • Error Recovery – Report multiple errors instead of stopping at first
    • Better diagnostics – More helpful error messages with suggestions
    • Optimization passes – Constant folding, inlining, etc.
    • Status: Ongoing
  7. Code Generation Backends

    • Cranelift Backend – Native code generation (in addition to VM)
    • Status: Cranelift in progress

Medium Priority

  1. First-class Functions and Closures

    • Functions as values: fun_ptr: fun(int64) -> int64
    • Anonymous functions: fun (x: int64) -> int64 { x * 2 }
    • Closures that capture variables
    • Status: Design phase
  2. Concepts

    • Define shared behavior and interfaces
    • Generic constraints on type parameters
    • Default implementations
    • Status: Design phase
  3. Union Types

  • Discriminated unions for variant data
  • Similar to Rust enums with pattern matching
  • Status: Design phase
  1. Smart Pointers

    • rc_ptr<T> – Reference-counted pointers for shared ownership
    • Status: Experimental (std/rc_ptr, WIP)
  2. Type Casting

    • Explicit casting utilities in std/cast
    • Support for numeric and structural conversions
    • Status: Experimental (std/cast, WIP)

Lower Priority

  1. Async/Await
  • Non-blocking I/O support
  • async functions and await expressions
  • Status: Design phase
  1. Package System

    • Organize code into packages
    • Namespace support for avoiding name collisions
    • Status: Design phase
  2. Package Manager

    • Dependency resolution and management
    • Binary distribution via crates.io
    • Status: Design phase

Standard Library Roadmap

Core Modules (v1.0)

  • βœ… std/io – Input/output (print, input, panic) note: println WIP
  • βœ… std/fs – File system operations
  • βœ… std/string – String manipulation
  • βœ… std/vector – Dynamic arrays
  • βœ… std/option – Optional values
  • βœ… std/result – Error handling with results
  • βœ… std/box – Heap-allocated values
  • βœ… std/iter – Iterators and iteration
  • βœ… std/map – Key-value collections
  • βœ… std/time – Time and duration
  • βœ… std/math – Mathematical functions

Experimental Modules (v0.6)

  • πŸ”§ std/rc_ptr – Reference-counted smart pointers (WIP)
  • πŸ”§ std/cast – Type conversion utilities (WIP)

Future Modules (v2.0+)

  • πŸ“‹ std/sync – Threads and synchronization
  • πŸ“‹ std/net – Network I/O and TCP/UDP sockets
  • πŸ“‹ std/json – JSON serialization/deserialization
  • πŸ“‹ std/regex – Regular expressions
  • πŸ“‹ std/hash – Cryptographic and hash functions
  • πŸ“‹ core/graphics – Graphics rendering (Vulkan/OpenGL/DirectX)
  • πŸ“‹ core/sdl – SDL 2.0 bindings
  • πŸ“‹ core/ffi – C FFI (foreign function interface)
  • πŸ“‹ core/ffi_rust – Rust FFI

Runtime & VM

Current Status

  • VM-based interpreter (deprecated, to be replaced)
  • Basic execution model for testing and learning
  • Not optimized for performance

Planned Improvements

  1. Cranelift Backend – Compile to native code for performance
  2. Optimizations – Constant folding, dead code elimination, inlining
  3. Error Recovery – Report multiple compilation errors
  4. Better Diagnostics – Improving the context and suggestions in error messages

Tooling

atlas_77 CLI

Implemented βœ…

  • atlas_77 init <project> – Create new project
  • atlas_77 build <file> – Compile project
  • atlas_77 run <file> – Execute file or project
  • atlas_77 help – Show help

Planned 🎯

  • atlas_77 test – Run tests
  • atlas_77 doc – Generate documentation

Build Artifacts

  • Default location: ./builds/<project_name>/
  • Binary format: VM bytecode (temporary)
  • Native code (future)

Compiler Features

Optimizations

  • πŸ“‹ Dead code elimination
  • πŸ“‹ Constant folding
  • πŸ“‹ Function inlining
  • πŸ“‹ Loop unrolling
  • πŸ“‹ Register allocation (with native backend)

Milestone Timeline

  • v0.3.x:

    • Initial language design
    • Basic parser implementation
    • Basic AST visitor runtime
  • v0.4.x:

    • Prototype language design
    • Basic parser and AST
  • v0.5.x (completed):

    • Proof of concept compiler
    • Basic syntax and semantics
  • vO.6.x (current):

    • Core language features
    • Basic standard library
    • Move & copy semantics

[!Note] Nothing is set in stone; timelines may shift based on what I want to work on and community feedback.

  • v0.7.x:
    • Cranelift backend MVP
  • Future versions:
    • Operator overloading
    • Pattern matching
    • First-class functions & closures
    • Concepts

Note

Timeline is aspirational and subject to change based on development priorities and community feedback.

Contributing

The language is actively developed. Feature requests, bug reports, and discussions are welcome. Development happens in the compiler repository.

See Also