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

Atlas77 is an experimental systems-style language with explicit ownership, deterministic cleanup, and a C-oriented toolchain.

This book is split into two tracks:

  • Learn Atlas77: practical, step-by-step onboarding
  • Language Reference: lower-level semantics and detailed behavior

If you are new, start with Getting Started and keep examples small.

Getting Started

This section gets you from zero to running code.

  1. Install the toolchain.
  2. Write a Hello World program.
  3. Run check and build commands repeatedly while you learn.

Next chapters:

  • Installation
  • Hello, World!

Installation

Atlas77 currently uses a Rust-based compiler frontend and emits C.

Typical prerequisites:

  • Rust toolchain for atlas77 compiler builds
  • A C compiler backend (tinycc, gcc, clang, or msvc)

Quick verification commands:

atlas77 --help
atlas77 check src/main.atlas

If check runs, your environment is ready for basic development.

Hello, World!

import "std/io";

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

Build with TinyCC for quick iteration:

atlas77 build src/main.atlas -c tinycc -o ./build

Programming A Guessing Game

This chapter is a guided exercise.

Goals:

  • Read input from the terminal
  • Parse and compare values
  • Use loops and conditions

Minimal loop structure:

import "std/io";

fun main() {
    let running: bool = true;
    while running {
        print("Guess: ");
        let guess = input();
        println(guess.c_str());
        // add parse + compare logic here
        break;
    }
}

Learn Atlas77

This part of the book teaches Atlas77 through small, practical steps.

It is intentionally less technical than the full language manual. If you want implementation details (compiler passes, ownership internals, ABI notes), use the Atlas77 Language Manual chapter.

How to read this section:

  1. Start with Hello Atlas77.
  2. Follow chapters in order.
  3. Copy snippets into small files and run them often.
  4. Keep code simple until ownership and pointers feel natural.

You can always come back to the reference later.

Hello Atlas77

Atlas77 programs are built around a main function.

import "std/io";

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

Build And Run

For fast local iteration, start with TinyCC:

atlas77 build src/main.atlas -c tinycc -o ./build

For stricter checks with common system toolchains:

atlas77 build src/main.atlas -c gcc -r
atlas77 build src/main.atlas -c clang -r

To type-check without producing a final binary:

atlas77 check src/main.atlas

What To Remember

  • Keep your first programs tiny.
  • Compile frequently.
  • Use check early to catch errors before code grows.

Core Syntax

This chapter covers the pieces you use constantly: values, functions, and structs.

Variables And Types

fun main() {
    let age: int64 = 42;
    let score: float64 = 99.5;
    let active: bool = true;
    let letter: char = 'A';
    let name: *uint8 = "Atlas";
}

Functions

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

fun greet(name: *uint8) -> unit {
    println(name);
}

If a function does not return data, use unit.

Structs With Factory Functions

public struct User {
public:
    name: *uint8;
    level: int32;

    fun new(name: *uint8, level: int32) -> User {
        return User { .name = name, .level = level };
    }
}

fun main() {
    let u = User::new("Gip", 1);
    println(u.name);
}

Common style in Atlas77 is Type::new(…) instead of constructor syntax.

Ownership Basics

Atlas77 uses ownership and deterministic cleanup.

You do not rely on a garbage collector.

Copy vs Move

Simple values are copied:

let a: int64 = 10;
let b: int64 = a; // copy

Resource-like values are usually moved:

let f = open_file_handle();
let g = move(f);
// f is invalid after move

If you use f after move, the compiler will reject it.

Destructors

Use a destructor when your type owns memory or a handle.

import "std/memory";

public struct Buffer {
private:
    raw: *uint8;
public:
    ~Buffer() {
        if this->raw != null {
            free(this->raw);
        }
    }
}

The compiler inserts cleanup at scope boundaries so values are released deterministically.

Practical Rule

When in doubt:

  1. Decide who owns each resource.
  2. Move ownership only when needed.
  3. Add a destructor for owned resources.

Working With The Standard Library

Start with a small subset of std and use it everywhere:

  • std/io for input/output
  • std/string for owned text
  • std/vector for growable arrays
  • std/optional and std/expected for safe control flow

Input And Output

import "std/io";

fun main() {
    print("Your name: ");
    let name = input();
    println(name.c_str());
}

String

import "std/string";

fun main() {
    let base = String::from_chars("atlas");
    let loud = base.to_upper();
    println(loud.c_str());
}

Vector

import "std/vector";

fun main() {
    let v = Vector<int64>::with_capacity(2);
    v.push(1);
    v.push(2);
    v.push(3);

    let last = v.pop();
}

optional And expected<T, E>

Use optional for “might be missing” values.

import "std/optional";

fun find_id(ok: bool) -> optional<int64> {
    if ok {
        return optional<int64>::of(42);
    }
    return optional<int64>::empty();
}

Use expected for “value or error” results.

import "std/expected";

fun parse_age(v: int64) -> expected<int64, *uint8> {
    if v < 0 {
        return expected<int64, *uint8>::unexpected("age cannot be negative");
    }
    return expected<int64, *uint8>::expect(v);
}

Generics And Function Pointers

You can stay productive in Atlas77 without these features at first. Learn them when your code starts repeating patterns.

Generics

fun pick_first<T>(a: T, _b: T) -> T {
    return a;
}

fun main() {
    let x = pick_first<int64>(100, 200);
}

Generics let you write one function for many types.

Function Pointers

This pattern is useful for callbacks and reusable behavior.

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

fun apply_twice(fn: fun(int32, int32) -> int32, a: int32, b: int32) -> int32 {
    return fn(a, b) + fn(b, a);
}

fun main() {
    let add_fn: fun(int32, int32) -> int32 = add;
    let r = apply_twice(add_fn, 4, 5);
}

Method pointers are also possible:

struct Foo {
public:
    const_add_pointer: fun(*const Foo, int32) -> int32;

    fun add_const(*const this, value: int32) -> int32 {
        return value + 100;
    }
}

fun main() {
    let foo = Foo { .const_add_pointer = Foo::add_const };
    let out = foo.const_add_pointer(&foo, 11);
}

C Interop And Project Shape

Atlas77 is designed to work with C libraries through extern declarations.

Declaring C Symbols

extern struct FILE {
    handle: uint64;
}

extern fun printf<T>(fmt: *uint8, value: T);

fun main() {
    printf("%s\n", "Hello from Atlas77");
}

Keep bindings small and test one function at a time.

Real App Shape (Raylib-Style)

The current raylib wrapper in this repository is prototype-level, but the app shape is useful:

fun main() {
    InitWindow(800, 450, "demo");
    SetTargetFPS(60);

    while (!WindowShouldClose()) {
        // update
        // draw
    }

    CloseWindow();
}

This is a good default structure for games or interactive tools.

Toolchain Guidance

atlas77 build src/main.atlas -c tinycc -o ./build
atlas77 build src/main.atlas -c gcc -r --c-arg=-lraylib
  • Use tinycc for quick iteration.
  • Use gcc or clang for release checks and stricter diagnostics.

Language Reference

This section summarizes Atlas77 core syntax and concepts.

  • Primitive types
  • Functions and structs
  • Pointers and ownership rules
  • Generics and intrinsics

For a deeper technical document, see Atlas77 Language Manual.

Atlas77 v0.8.0 Language Manual

This manual documents Atlas77 as it exists in v0.8.0. It focuses on behavior that is visible in the compiler pipeline and in the current standard library implementation.

1. Language Fundamentals

1.1 Primitive Types

Atlas77 defines the following primitive scalar/value types in the language reference:

TypeMeaningSize
int8Signed integer1 byte
int16Signed integer2 bytes
int32Signed integer4 bytes
int64Signed integer8 bytes
uint8Unsigned integer1 byte
uint16Unsigned integer2 bytes
uint32Unsigned integer4 bytes
uint64Unsigned integer8 bytes
float32IEEE-like floating point4 bytes
float64IEEE-like floating point8 bytes
booltrue/false1 byte
charSingle Unicode code point4 bytes
stringUTF-8 byte stringVariable
unitEmpty value type0 bytes (semantic)

Status note: the generated language-reference page also states that some primitive types are planned or not fully implemented yet. Treat compiler support as authoritative over table intent.

fun main() {
    let a: int64 = 42;
    let b: uint8 = 255;
    let c: float64 = 3.14159;
    let ok: bool = true;
    let ch: char = 'A';
    let s: string = "atlas";
}

1.2 Structs

Atlas77 structs are nominal types with fields and methods. In current code style, object creation is done with factory/static methods such as Type::new(…) or with struct literals.

public struct Person {
public:
    name: string;
    age: int32;

    fun new(name: string, age: int32) -> Person {
        return Person { .name = name, .age = age };
    }

    fun birthday(*this) {
        this->age = this->age + 1;
    }
}

fun main() {
    let p = Person::new("Ada", 30);
    p.birthday();
}

Member access uses . on values and -> through pointers to values.

1.3 Pointers

Atlas77 uses explicit raw pointers:

  • *T for mutable pointer access
  • *const T for immutable pointer access
fun read_first(ptr: *const int64) -> int64 {
    return *ptr;
}

fun set_first(ptr: *int64, v: int64) {
    *ptr = v;
}

NB: References are not yet implemented, but is a planned feature for future releases.

2. The Atlas77 Memory Model

Source of truth: ownership specification in docs/ownership_spec.md.

2.1 Value Semantics: Trivial vs Resource Types

Trivially copyable values can be copied implicitly. Resource values require deterministic drop and cannot be implicitly copied.

let x: int64 = 10;
let y: int64 = x; // copy is valid
let r: Resource = make_resource();
let r2: Resource = r; // invalid: non-trivial implicit copy

2.2 Ownership Transfer with move(x)

move(x) transfers ownership and invalidates x for later use.

let a = build_resource();
let b = move(a);
use(a); // invalid after move

Compiler diagnostics include moved/deleted/consumed and potentially-moved variants at branch joins.

2.3 Deterministic Cleanup and Destructors

The compiler inserts delete operations in HIR, and lowers to destructor calls (and free when needed) in generated C.

public struct FileHandle {
private:
    raw: *uint8;
public:
    ~FileHandle() {
        if this->raw != null {
            free(this->raw);
        }
    }
}

Scope-exit cleanup, return-path cleanup, and reassignment pre-delete are handled by ownership/lifecycle passes.

3. Standard Library Reference

3.1 std/memory

Current low-level primitives:

  • malloc(size)
  • free(ptr)
  • memcpy(dest, src, size)
  • sizeof() and alignof() wrappers
  • move(from) intrinsic
import "std/memory";

fun main() {
    let buf = malloc<uint8>(128);
    // ... use buf
    free(buf);
}

3.2 Vector

Vector in std/vector owns heap storage and tracks length/capacity.

  • push doubles capacity when full (0 -> 1 -> 2 -> 4 …)
  • pop and take move values out
  • destructor deletes elements in range [0, length)
import "std/vector";

fun main() {
    let v = Vector<int64>::with_capacity(2);
    v.push(10);
    v.push(20);
    v.push(30); // growth occurs

    let last = v.pop();
    let first = v.take(0);
}

Ownership note: element type T should be designed with Atlas77 drop semantics in mind, since vector cleanup calls delete on stored elements.

3.3 String

String in std/string is an owning byte-buffer wrapper over *uint8 with explicit len/capacity.

  • UTF-8 bytes, length in bytes
  • c_str() ensures trailing NUL and may reallocate
  • with_capacity reserves space manually
import "std/string";

fun main() {
    let s = String::from_chars("hello");
    let t = s.to_upper();
    let u = s.concat(&t);
    print(u.c_str());
}

Implementation note: string behavior currently reflects pragmatic std internals, not a finalized immutable string design.

3.4 optional

optional is a tagged union-like utility for nullable values.

  • optional::of(value)
  • optional::empty()
  • has_value(), is_empty(), value(), value_or(default)
import "std/optional";

fun maybe_even(n: int64) -> optional<int64> {
    if (n % 2 == 0) {
        return optional<int64>::of(n);
    }
    return optional<int64>::empty();
}

value() consumes and panics on empty.

3.5 expected<T, E>

expected<T, E> models success or error outcomes.

  • expected<T, E>::expect(v)
  • expected<T, E>::unexpected(e)
  • is_expected(), is_unexpected()
  • expected_value(), unexpected_value(), *_or(default)
import "std/expected";

fun parse_nonzero(v: int64) -> expected<int64, string> {
    if (v == 0) {
        return expected<int64, string>::unexpected("zero is not allowed");
    }
    return expected<int64, string>::expect(v);
}

3.6 io

std/io provides print, println, input, panic, printf plus FILE/Stdin interop-facing types.

import "std/io";

fun main() {
    print("Name: ");
    let name = input();
    println(name.c_str());
}

3.7 Experimental shared_ptr

std/experimental/smart_ptr.atlas currently includes shared_ptr with a strong counter block.

  • copy increments counter
  • release decrements and deletes at zero
  • no weak pointers
  • no cycle handling
import "std/experimental/smart_ptr";

fun main() {
    let p = shared_ptr<int64>::make(10);
    let q = p.copy();
    // both point to same rc_block
}

3.8 Raylib Wrapper State

Current raylib binding code is an extern-heavy prototype, not a finished package story:

  • requires raylib.h and host linker setup
  • mixes extern structs/functions with Atlas-side helpers
  • demonstrates practical app loop structure
extern fun InitWindow(width: int32, height: int32, title: *const uint8);
extern fun WindowShouldClose() -> bool;
extern fun BeginDrawing();
extern fun EndDrawing();

fun main() {
    InitWindow(800, 450, "demo");
    while (!WindowShouldClose()) {
        BeginDrawing();
        EndDrawing();
    }
}

Planned direction: expose library gating with a compiler flag before introducing a full build configuration format.

4. C Interoperability

4.1 Extern Functions and Structs

Use extern fun and extern struct to declare C ABI symbols.

extern struct FILE {
    handle: uint64;
}

extern fun printf<T>(fmt: *uint8, value: T);

fun main() {
    printf("%s\n", "hello from atlas77");
}

4.2 Atlas77 to C Pipeline

The compiler flow in libraries/lib.rs is:

  1. Parse source to AST.
  2. Lower AST to HIR.
  3. Monomorphize generics.
  4. Type check.
  5. Run ownership pass and inject delete semantics.
  6. Lower HIR to LIR.
  7. Emit C99 source and generated header in build/.
  8. Invoke selected C compiler backend.

The generated artifacts include:

  • build/output.atlas
  • build/output.atlas_lir
  • build/output.atlas_c.c
  • build/__atlas77_header.h

4.3 TCC vs GCC/Clang Workflow

CLI behavior from libraries/main.rs and libraries/lib.rs:

  • Build defaults to TinyCC (tinycc or tcc)
  • Release and debug are mutually exclusive CLI flags
  • GCC/Clang/MSVC/Intel are selectable backends
  • Extra linker/compiler flags are passed through with repeated –c-arg

Example commands:

atlas77 build src/main.atlas -c tinycc -o ./build
atlas77 build src/main.atlas -c gcc -r --c-arg=-lm --c-arg=-lraylib
atlas77 build src/main.atlas -c clang -r
atlas77 check src/main.atlas

Practical guidance:

  • Use TinyCC for fast local iteration.
  • Use GCC or Clang for stricter diagnostics and release optimization validation.

5. Advanced Features

5.1 Generics

Atlas77 supports generic structs and functions with explicit type parameters.

public struct Pair<T> {
public:
    a: T;
    b: T;
}

fun pick_first<T>(x: T, _y: T) -> T {
    return x;
}

fun main() {
    let p = Pair<int64> { .a = 1, .b = 2 };
    let v = pick_first<int64>(p.a, p.b);
}

Current codebase also demonstrates generic constraints in library helpers (for example copy<T: std::copyable> in std/memory).

5.2 Function Pointers

Atlas77 supports typed function pointers, including static functions, generic function instances, and method pointers.

struct Foo {
public:
    fp: fun(int32, int32) -> int32;
    add_const_fp: fun(*const Foo, int32) -> int32;

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

    fun add_const(*const this, value: int32) -> int32 {
        return value + 100;
    }
}

fun pick_first<T>(a: T, _b: T) -> T {
    return a;
}

fun main() {
    let add_fn: fun(int32, int32) -> int32 = Foo::add;
    let foo = Foo {
        .fp = add_fn,
        .add_const_fp = Foo::add_const,
    };

    let r1 = foo.fp(2, 3);
    let r2 = foo.add_const_fp(&foo, 11);

    let pick_i64: fun(int64, int64) -> int64 = pick_first<int64>;
    let r3 = pick_i64(10, 20);
}

5.3 Intrinsics

Core intrinsics in std/memory:

  • size_of()
  • align_of()
  • move(value)

User-facing wrappers:

  • sizeof()
  • alignof()
import "std/memory";

fun main() {
    let s = sizeof<int64>();
    let a = alignof<int64>();

    let x = String::new();
    let y = move(x);
    // x is invalid after move
}

6. Real-World Application Structure

The raylib sample demonstrates a typical Atlas77 program skeleton for interactive apps:

  1. Extern declarations for host APIs.
  2. Domain structs/enums for runtime state.
  3. Initialization block.
  4. Main loop with update + draw phases.
  5. Deterministic shutdown.
fun main() {
    InitWindow(800, 450, "2d camera");
    SetTargetFPS(60);

    while (!WindowShouldClose()) {
        // update
        // draw
    }

    CloseWindow();
}

This structure maps cleanly onto Atlas77 ownership rules because state lifetime is explicit and scoped.

7. Operational Notes and Caveats

  • Atlas77 is under active development; not all documented types/features are equally mature.
  • Standard library modules are practical and evolving, not frozen ABI contracts.
  • Pointer-heavy code must be audited manually; ownership pass does not replace C interop discipline.
  • Use check and multiple C backends in CI to catch backend-specific issues early.

Memory Model

Atlas77 uses explicit ownership and deterministic destruction.

Core ideas:

  • Trivial values are copyable.
  • Resource values are moved.
  • move(x) invalidates the source value.
  • Cleanup is inserted by compiler passes and lowered to generated C code.
let a = make_resource();
let b = move(a);
// a is invalid after move

Error Handling

Atlas77 standard style favors explicit result types.

Use optional for maybe-present values:

import "std/optional";

fun find_id(ok: bool) -> optional<int64> {
    if ok { return optional<int64>::of(1); }
    return optional<int64>::empty();
}

Use expected<T, E> for value-or-error outcomes:

import "std/expected";

fun parse(x: int64) -> expected<int64, string> {
    if x < 0 { return expected<int64, string>::unexpected("negative"); }
    return expected<int64, string>::expect(x);
}

Reserved Keywords

This page tracks language keywords reserved by the parser and semantic passes.

Common keywords in examples include:

  • fun
  • struct
  • extern
  • import
  • public
  • private
  • return
  • if
  • else
  • while
  • let
  • const

Use descriptive identifiers and avoid reusing keyword names as variables.

Generics

Generics let you write reusable code for multiple types.

public struct Pair<T> {
public:
    left: T;
    right: T;
}

fun pick_first<T>(a: T, _b: T) -> T {
    return a;
}

Monomorphization happens in the compiler pipeline before type checking and lowering to C.

Standard Library

Note:
The standard library is a work in progress. Some modules are complete while others are still in development. This documentation reflects the current state of the implemented modules.

Overview

The Atlas77 standard library provides essential data structures, utilities, and I/O functions. The library is organized into modules that can be imported individually.

Each module is now documented on its own page with complete API reference, usage examples, and best practices.

std/io

Module for terminal I/O and panic/printf interop helpers.

import "std/io";

fun main() {
    print("Name: ");
    let name = input();
    println(name.c_str());
}

Key APIs:

  • print
  • println
  • input
  • panic
  • printf

std/string

String is an owning UTF-8 byte-buffer abstraction.

import "std/string";

fun main() {
    let s = String::from_chars("atlas");
    let up = s.to_upper();
    println(up.c_str());
}

Useful methods:

  • String::new
  • String::from_chars
  • len
  • trim
  • to_upper / to_lower
  • concat
  • c_str

std/vector

Vector is a growable heap-backed sequence.

import "std/vector";

fun main() {
    let v = Vector<int64>::with_capacity(1);
    v.push(10);
    v.push(20);
    let x = v.pop();
}

Current behavior:

  • Tracks length and capacity
  • Grows by doubling capacity
  • Deletes elements on destructor

std/optional

optional represents an optional value.

import "std/optional";

fun maybe_value(ok: bool) -> optional<int64> {
    if ok { return optional<int64>::of(42); }
    return optional<int64>::empty();
}

Key APIs:

  • of
  • empty
  • has_value
  • is_empty
  • value
  • value_or

std/expected

expected<T, E> represents success (T) or error (E).

import "std/expected";

fun parse_age(age: int64) -> expected<int64, string> {
    if age < 0 {
        return expected<int64, string>::unexpected("invalid age");
    }
    return expected<int64, string>::expect(age);
}

Key APIs:

  • expect
  • unexpected
  • is_expected
  • is_unexpected
  • expected_value / unexpected_value
  • expected_value_or / unexpected_value_or

std/mem

Memory helpers and low-level intrinsics.

import "std/memory";

fun main() {
    let p = malloc<uint8>(64);
    free(p);
}

Core APIs:

  • malloc
  • free
  • memcpy
  • sizeof / size_of
  • alignof / align_of
  • move

Blue Engine

Blue Engine bindings in this repository are experimental.

Current state:

  • Binding prototypes exist in libraries/blue_engine
  • API and integration details are still evolving
  • Expect breaking changes while language/runtime features stabilize

Roadmap

Near-term priorities include:

  • Stabilize ownership and lifecycle behavior
  • Improve std module completeness
  • Strengthen build/toolchain ergonomics
  • Expand documentation with examples over specifications

Long-term features are tracked in repository planning docs and issues.