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. It could be compared to how C++ has its own version (C++11, C++14, C++17, C++20, etc.) with each having thousands of page of documentation to formalize the language.

Atlas77 is a statically typed, compiled programming language 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.

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
  • It should work 😁

NB: This is a work in progress document. The syntax is subject to change.

Current Syntax of Atlas77

1. Introduction

Atlas77 is a simple, easy-to-use, and powerful programming language. It is designed to be easy to learn and use, while still being powerful enough to handle complex tasks. This document describes the syntax of Atlas77, including the rules for writing code in the language (WIP).

2. Hello, World!

Here is a simple "Hello, World!" program written in Atlas77:

import "std/io"

func main() -> int64 {
    print("Hello, World!")
}

Do know that strings are not implemented yet, so this is just an example of how it will look like

Save this code to a .atlas file, then run it directly with atlas run <FILE_PATH>

3. Comments

Comments in Atlas77 are similar to comments in other programming languages. There are two types of comments: single-line comments and multi-line comments.

3.1. Single-line Comments

Single-line comments start with // and continue until the end of the line. For example:

// This is a single-line comment

let x: i64 = 5; // This is also a single-line comment

3.2. Multi-line Comments

Multi-line comments start with /* and end with */. For example:

NB: Multi-line comments aren't supported yet (you'll see a lot of WIPs in this document)

/*
This is a multi-line comment.
    /*
        NOTE: Multi-line comments can be nested.
    */

It can span multiple lines.
*/

Comments are parsed as tokens by the compiler, to allow future documentation features.

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"

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

    const y: i64 = 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
i88-bit signed integer💤
i1616-bit signed integer💤
i3232-bit signed integer💤
i6464-bit signed integer
isizePlatform-dependent signed integer💤
u88-bit unsigned integer💤
u1616-bit unsigned integer💤
u3232-bit unsigned integer💤
u6464-bit unsigned integer
usizePlatform-dependent unsigned integer💤
f3232-bit floating-point number💤
f6464-bit floating-point number
boolBoolean value (true or false)
charUnicode character💭
strString💭
arrayArray (syntax: [YourType])💭

Note: The str and array types will be mutable and resizable. More powerful types for both Strings and Arrays will be implemented in the future (e.g., Vec<YourType> for arrays & String).

NB: The char type is not implemented yet, but it will be a 32-bit Unicode character.

NB 2: Since this is a VM-based language, all numeric types smaller than 64 bits (e.g., u8, u16, u32) are internally represented as 64-bit values for simplicity and consistency. However, they will behave as their original types, respecting their size and overflow semantics. In the future, packed types may be introduced to optimize memory usage for arrays (e.g., [u8] could be represented as [u8x8], [u16] as [u16x4], etc.). At present, the minimum memory size for numeric types is 8 bytes.

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"

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

func 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💤

Note: Nested if-else (i.e. if {} else if {} else {}) isn't supported yet.

import "std/io"

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

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

    let i: i64 = 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"

func main() {
    print("Hello, World!");
}

As of writing this document, the following standard libraries are available:

  • std/io: Input/output functions
    • print_int(i: i64): Print an integer to the console
    • print_float(f: f64): Print a floating-point number to the console
    • print_uint(u: u64): Print an unsigned integer to the console
    • print_bool(b: bool): Print a boolean value to the console

Yes it is very limited, but tbh str, array & structs are still not implemented, so it's a bit hard to implement more complex functions.

9 Arrays

10. Enums

WIP

Example:

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

11. Class & Structs

WIP

Current state of std/fs:

extern read_dir(path: str) -> [str]
extern read_file(path: str) -> str
extern write_file(path: str, content: str)
extern remove_file(path: str)
extern file_exists(path: str) -> bool

public class File {
    private:
        flag: i64;
        content: str;
        path: str;
    public:
        func create() -> File {
            return new File {
                flag: 0,
                content: None,
                path: "",
            };
        }
        func read(path: str) -> File {
            let file: Self = File::create();
            file.path = path;
            file.content = Some(read_file(path));
            return file;
        }
        func write(&self, content: str) {
            write_file(self.path, content);
        }
        func remove(&self) {
            remove_file(self.path);
        }
        func exists(&self) -> bool {
            return file_exists(self.path);
        }
        func read_dir(path: str) -> [str] {
            return read_dir(path);
        }
        func read_file(path: str) -> str {
            return read_file(path);
        }
}

12. Generics

As of now generics are only implemented for external functions.

Example:

extern generic_func<T>(val: T) -> T
extern other_generic_func<T, G>(t: [T], g: [G]) -> [T]

[!NOTE] You can't return a generic that is not used in any parameter. Type inference is not implemented yet.

WIP

13. Traits

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
    

Programming a Guessing Game

Setting up

First, create a new atlas77 file named guessing_game.atlas

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/listList manipulation functions
std/timeTime functions
std/mathMath functions
std/vecVector functions

You can see what is implemented with the checkboxes below.

std/io

  • print<T>(val: T) -> unit: Print a string to the standard output.
  • println<T>(val: T) -> unit: Print a string to the standard output followed by a newline.
  • input() -> str: Read a line from the standard input.

std/fs

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

NB: The File type is not implemented yet.

  • open(filename: str, mode: str) -> File: Open a file in the specified mode.
  • read(file: File) -> str: Read the content of a file.
  • write(file: File, content: str) -> unit: Write content to a file.
  • close(file: File) -> unit: Close a file.
  • exists(filename: str) -> bool: Check if a file exists.
  • remove(filename: str) -> unit: Remove a file.
  • rename(old: str, new: str) -> unit: Rename a file.

std/str

  • String: A string type that can be used to manipulate strings.

NB: The String type is not implemented yet.

Implements

  • Indexable<char>: A trait that defines the get and set methods.

NB: The Indexable trait is not implemented yet.

Methods

  • str_len(s: String) -> uint64: Get the length of a string.
  • concat(s1: String, s2: String) -> String: Concatenate two strings.
  • split(s: String, sep: str) -> List: Split a string into a list of substrings.
  • join(lst: List, sep: str) -> String: Join a list of strings into a single string.
  • replace(s: String, old: str, new: str) -> String: Replace all occurrences of a substring in a string.
  • to_upper(s: String) -> String: Convert a string to uppercase.
  • to_lower(s: String) -> String: Convert a string to lowercase.
  • trim(s: String) -> String: Remove leading and trailing whitespace from a string.
  • starts_with(s: String, prefix: str) -> bool: Check if a string starts with a prefix.
  • ends_with(s: String, suffix: str) -> bool: Check if a string ends with a suffix.
  • contains(s: String, sub: str) -> bool: Check if a string contains a substring.
  • find(s: String, sub: str) -> int64: Find the index of the first occurrence of a substring in a string.
  • rfind(s: String, sub: str) -> int64: Find the index of the last occurrence of a substring in a string.
  • slice(s: String, start: int64, end: int64) -> String: Get a substring of a string.

Operators

  • +: Concatenate two strings.
  • []: Get a character from a string by index.
  • []=: Set a character in a string by index.

std/list

  • len<T>(lst: [T]) -> uint64: Get the length of a list.
  • push<T>(lst: [T], val: T) -> unit: Add an element to the end of a list.
  • pop<T>(lst: [T]) -> T: Remove and return the last element of a list.
  • slice<T>(lst: [T], start: uint64, end: uint64) -> [T]: Get a slice of a list.
  • remove<T>(lst: [T], index: uint64) -> T: Remove and return an element from a list by index.

std/vec

  • Vec<T>: A vector is a dynamic array that can grow or shrink in size. It has a bunch of methods to manipulate the data.

Implements

  • Indexable<T>: A trait that defines the get and set methods.

Methods

  • Vec(data: [T]) -> Vec<T>: Create a new vector from a list.
  • len() -> uint64: Get the length of the vector.
  • is_empty() -> bool: Check if the vector is empty.
  • push(val: T) -> unit: Add an element to the end of the vector.
  • pop() -> T: Remove and return the last element of the vector.
  • get(index: uint64) -> T: Get an element from the vector by index.
  • set(index: uint64, val: T) -> unit: Set an element in the vector by index.

Operators

  • []: Get an element from the vector by index.
  • []=: Set an element in the vector by index.

std/time

  • Time: A time type that can be used to represent time.

  • now() -> Time: Get the current time.

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

  • sleep(ms: uint64) -> unit: Sleep for the specified number of milliseconds.

  • sleep_us(us: uint64) -> unit: Sleep for the specified number of microseconds.

  • sleep_ns(ns: uint64) -> unit: Sleep for the specified number of nanoseconds.

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

  • elapsed_us(start: Time, end: Time) -> uint64: Calculate the elapsed time between two time values in microseconds.

  • elapsed_ns(start: Time, end: Time) -> uint64: Calculate the elapsed time between two time values in nanoseconds.

  • parse(s: str, fmt: str) -> Time: Parse a string into a time value.

  • add(t: Time, ms: uint64) -> Time: Add milliseconds to a time value.

std/math

  • 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.