2 releases

0.0.2 Aug 23, 2024
0.0.1 Aug 21, 2024

#124 in Programming languages

MIT license

1.5MB
2K SLoC

Crates.io Version Build

AbySS: Advanced-scripting by Symbolic Syntax

logo

AbySS (Advanced-scripting by Symbolic Syntax) is a programming language designed to combine the thrill of casting spells with the power of advanced scripting. AbySS aims to provide an intuitive and symbolically rich syntax that allows developers to interact with their code as if they were performing magic. Whether you're scripting a simple operation or crafting complex systems, AbySS offers a unique and immersive experience.

Key Features

  • Symbolic Syntax: AbySS emphasizes a symbolically intuitive syntax, making the code easy to read and write, while retaining powerful functionality.
  • Spellcasting-inspired Programming: The language's design mimics the experience of casting spells, with reserved keywords that evoke a magical theme.
  • Interactive Spellcasting: AbySS supports interactive scripting through an interpreter, allowing real-time execution and feedback.
  • Structured Sorcery: AbySS encourages structured programming, combining the flexibility of scripting with the rigor of structured code.
  • VSCode Extension: Syntax highlighting, code completion, and snippets are available through the AbySS Codex Familiar VSCode extension.

Table of Contents

Installation

You can install AbySS from crates.io using Cargo:

cargo install abyss-lang

Alternatively, you can install AbySS by cloning the repository and building it locally. cargo-llvm-cov is supported for test coverage analysis.

git clone https://github.com/your-repository/abyss.git
cd abyss
cargo install --path .

For test coverage with cargo-llvm-cov, install the tool as follows:

rustup component add llvm-tools-preview
cargo install cargo-llvm-cov

Getting Started

To start using AbySS, you can either enter the interactive interpreter mode or run .aby script files.

Running the Interpreter

You can start the AbySS interpreter with the following command:

abyss cast

Running Scripts

To run a .aby script file, use the following command:

abyss invoke <script.aby>

Formatting Code

AbySS provides a built-in code formatter that helps maintain consistent code style across your scripts. To format your .aby scripts, use the following command:

abyss align <script.aby>

This command automatically formats your code according to the language's style guidelines.

Language Syntax

Basic Syntax

AbySS uses a symbolic and intuitive syntax inspired by magical themes. Below are some of the core elements of the language.

  • Comments: Comments in AbySS are marked with // for single-line comments and /* */ for multi-line comments.
// This is a single-line comment
/*
  This is a multi-line comment
*/

Types

AbySS supports the following primitive types:

  • arcana: Represents integers (e.g., 42, -3).
  • aether: Represents floating-point numbers (e.g., 3.14, -1.0).
  • rune: Represents strings (e.g., "Hello, World").
  • omen: Represents boolean values, with boon for true and hex for false.
  • abyss: Represents the void type, indicating no value.
forge x: arcana = 10;
forge pi: aether = 3.14;
forge message: rune = "Hello, AbySS";
forge is_active: omen = boon;
  • arcana: Derived from the Latin word for "secret" or "mystery," and also referencing the structured numbering of tarot cards, arcana represents integer values, symbolizing the hidden foundations of calculations in programming.
  • aether: Inspired by the classical element of ether, believed to fill the universe beyond the terrestrial sphere, aether represents floating-point numbers, capturing the idea of fluid, continuous quantities.
  • rune: Borrowed from ancient writing systems used in magic, rune represents strings, suggesting the idea that words and symbols hold power in programming as they do in mystical inscriptions.
  • omen: Drawing from the concept of a prophetic sign, omen represents boolean values. The keywords boon and hex are used for true and false, respectively — boon originates from Old English, meaning a blessing or benefit, while hex comes from Germanic folklore, signifying a curse or spell, reinforcing the mystical theme of the language.
  • abyss: Symbolizing infinite nothingness, abyss represents the void type, indicating no value is returned, and is also the name of the language, reflecting its philosophy of exploring the depths of symbolic scripting.

Type Casting

In AbySS, type casting is achieved using the trans keyword. This allows the conversion of values from one type to another.

trans: Short for "transformation," trans enables the conversion of one type into another, reflecting the idea of magical transformations in programming.

forge x: arcana = trans(3.14 as arcana);

Variable Declaration

Variables are declared using the forge keyword. You must explicitly specify the variable type.

forge x: arcana = 42;
forge greeting: rune = "Welcome to AbySS!";

To declare mutable variables, use the morph keyword with forge.

forge morph counter: arcana = 10;
counter += 5;
  • forge: Derived from the concept of a blacksmith forging items, this keyword represents the creation and declaration of new variables, symbolizing the act of crafting something new.
  • morph: Inspired by transformation, morph is used to indicate mutable variables that can change their form or value over time.

Conditionals

In AbySS, you can use the oracle construct to handle conditionals. One approach is to define conditions directly within each pattern, allowing for flexible and readable branching logic. This method lets you skip a separate conditional statement and write all conditions within the branches themselves.

  • oracle: Reflecting the role of an oracle in ancient mythology, this keyword is used for conditionals, where decisions and predictions are made based on input.

Pattern-based Oracle

forge x: arcana = -5;
oracle {
    (x > 0) => unveil("x is positive");
    (x < 0) => unveil("x is negative");
    _ => unveil("x is zero");
};

In this example, the conditions are written directly within the patterns. The oracle evaluates each condition in sequence and executes the matching branch. If no specific conditions are met, the default pattern _ is executed.

Boolean-based Oracle

Another way to use oracle is by explicitly writing a condition as part of the oracle expression, which can be evaluated as an omen (boolean) value.

oracle (x > 0) {
    (boon) => unveil("x is positive");
    (hex) => unveil("x is non-positive");
};

Here, the condition x > 0 is evaluated as boon (true) or hex (false), and the appropriate branch executes based on this result.

Value-based Oracle

You can also use expressions or assignments within oracle for more complex evaluations.

forge y: arcana = 42;
oracle (z = y * 2) {
    (z > 50) => unveil("z is greater than 50");
    (z == 50) => unveil("z equals 50");
    _ => unveil("z is less than 50");
};

In this case, z is assigned the result of y * 2, and the appropriate branch is chosen based on the computed value of z.

Multiple Condition-based Oracle

You can also handle multiple conditions within a single oracle construct, allowing for complex branching based on multiple variables.

forge a: arcana = 3;
forge b: arcana = 2;

oracle (a, b) {
    (1, 2) => unveil("a is 1 and b is 2");
    (3, 2) => unveil("a is 3 and b is 2");
    _ => unveil("Other combination");
};

In this example, oracle evaluates multiple conditions (a and b) together and executes the corresponding branch based on their values. If no specific conditions are met, the default pattern _ is used.

This flexibility makes oracle a powerful tool for creating readable and intuitive branching logic in AbySS.

Loops

In AbySS, loops are managed using the orbit keyword. This construct allows for both simple and complex loop structures, with additional control provided by the resume and eject keywords. Below, we explore a range of use cases for loops in AbySS, from basic to advanced scenarios.

  • orbit: Inspired by the celestial motion of planets, orbit represents loops, where the code revolves around a central task repeatedly until a condition changes.
  • resume: Reflecting the continuation of interrupted tasks, resume allows the loop to skip the current iteration and move on to the next.
  • eject: Analogous to an emergency exit or escape mechanism, eject breaks out of loops, terminating the iteration prematurely.

Simple Loop

The most basic form of a loop in AbySS iterates over a range of values. In this example, the loop iterates from 0 to 5 (exclusive of 5).

orbit (i = 0..5) {
    unveil(i);
}

This loop prints the numbers from 0 to 4 using the unveil function. The .. operator defines a half-open range, which excludes the upper bound.

Closed Range Loop

You can create a closed range loop using the ..= operator, which includes the upper bound in the iteration.

orbit (i = 0..=10) {
    unveil(i);
}

In this loop, the numbers from 0 to 10 (inclusive) are printed.

Infinite Loop

By omitting the loop parameters, you can create an infinite loop that runs until a certain condition is met. The loop can be terminated using the eject keyword, which breaks the loop when the condition is satisfied.

forge morph i: arcana = 0;
orbit {
    oracle (i == 100) {
        (boon) => eject; // Break the loop when i equals 100
    };
    i += 1;
};
unveil(i); // Prints 100 after loop termination

This example increments i in each iteration until it reaches 100, at which point the loop exits.

Loop with Multiple Parameters

AbySS allows you to define loops with more than one parameter, making it easier to iterate over multiple ranges simultaneously. Here's an example where two loop variables, i and j, are defined:

orbit (i = 0..3, j = 0..3) {
    unveil(i, " ", j);
}

In this loop, i and j both iterate over the range from 0 to 2, and each pair of values is printed. This pattern is useful when you need to perform operations that depend on two or more varying values simultaneously.

Resume Keyword

The resume keyword allows you to skip the current iteration of a loop and move on to the next iteration. This can be used to control the flow of nested loops.

orbit (i = 0..3) {
    orbit (j = 0..3) {
        oracle (i == j) {
            (boon) => resume j; // Skip the iteration when i equals j
        };
        unveil(i, " ", j);
    }
}

In this example, the inner loop skips the iteration whenever i is equal to j.

Eject Keyword

The eject keyword breaks the loop entirely. In nested loops, you can specify which loop to break by passing the loop variable as an argument.

orbit (i = 0..3) {
    orbit (j = 0..3) {
        unveil(i, " ", j);
        oracle (i == 2) {
            (boon) => eject i; // Break the outer loop when i equals 2
        };
    }
}

Here, the outer loop breaks entirely when i equals 2, effectively terminating both the inner and outer loops.

These examples illustrate the flexibility of the orbit construct in AbySS, which allows for sophisticated looping patterns with easy-to-read syntax. The ability to control flow with resume and eject further enhances the language's expressiveness in handling loops.

Functions

In AbySS, functions are defined using the engrave keyword. Functions allow you to encapsulate reusable code blocks and return values. You can define functions with parameters, specify return types, and call these functions within your scripts.

  • engrave: Symbolizing the act of carving a magical circle, engrave is used for function definitions, representing the meticulous process of inscribing reusable spells into the code.

Function Definition

Functions are defined using the engrave keyword, followed by the function name, parameters, optional return type, and the function body enclosed in {}.

engrave add(a: arcana, b: arcana) -> arcana {
    reveal a + b;
};

In this example, the add function takes two parameters a and b of type arcana (integer), and returns their sum as an arcana.

Function Calls

You can call a function by using its name followed by parentheses, and pass arguments matching the function's parameters.

forge result: arcana = add(3, 5);
unveil(result); // Outputs: 8

This example calls the add function with the arguments 3 and 5, stores the result in the result variable, and prints it using unveil.

Nested Function Calls

AbySS allows you to call functions within other function calls, enabling complex operations to be broken down into simpler components.

engrave add_three_numbers(x: arcana, y: arcana, z: arcana) -> arcana {
    reveal add(add(x, y), z);
};

forge result: arcana = add_three_numbers(1, 2, 3);
unveil(result); // Outputs: 6

In this example, the add_three_numbers function calls the add function twice to sum three numbers.

Return Values

Functions in AbySS use the reveal keyword to return values. If a function does not explicitly return a value, it implicitly returns abyss.

engrave greet() -> rune {
    reveal "Hello, AbySS!";
};

unveil(greet()); // Outputs: Hello, AbySS!

This function greet returns a rune (string) and prints it using unveil.

Recursive Functions

AbySS supports recursive function calls, allowing functions to call themselves.

engrave factorial(n: arcana) -> arcana {
    oracle (n <= 1) {
        (boon) => reveal 1;
    };
    reveal n * factorial(n - 1);
};

forge result: arcana = factorial(5);
unveil(result); // Outputs: 120

This recursive function calculates the factorial of a number.

Input/Output

For output, AbySS uses the unveil function to print values to the console. You can pass multiple variables or expressions as arguments, separated by commas. This allows you to concatenate different elements in the output.

  • unveil: Chosen for its metaphorical meaning of revealing something hidden, unveil is used for output operations, making the internal state of the program visible to the user.
unveil("Hello, AbySS");
unveil("x + 42 = ", x + 42);

In the example above, the second unveil statement prints both the string "x + 42 = " and the result of the expression x + 42 on the same line.

For input, AbySS provides the summon function to read user input during script execution. The summon function takes a prompt message and an expected type as arguments.

  • summon: Represents the act of calling forth something from the user, summon is used for standard input operations. You can specify a prompt and the type of input expected (e.g., arcana, aether, rune).
forge name: rune = summon("Input your name: ", rune);
forge age: arcana = summon("Input your age: ", arcana);
unveil("Hello, ", name, "! You are ", age, " years old.");

In this example, the user is prompted to enter their name and age. The inputs are then stored in the name and age variables, and both are printed using unveil.

VSCode Extension

The AbySS Codex Familiar VSCode extension provides additional support for AbySS development, including:

  • Syntax highlighting for keywords, types, constants, and operators.
  • Code snippets for common structures like forge, unveil, and oracle.
  • Auto-completion for key AbySS constructs.

To install the extension, search for "AbySS Codex Familiar" in the Visual Studio Code Extensions Marketplace, or download it from the GitHub repository.

Roadmap

  • Collection Types: Implement collection types such as lists and dictionaries for handling multiple values (Work-in-progress).
  • Struct Implementation: Enable the definition and use of custom data structures (TBD).
  • Generics Introduction: Introduce generics to allow functions and data structures to be more flexible and reusable with different types (TBD).
  • Module System: Introduce the ability to import functions and variables from other files (TBD).
  • Error Handling: Implement robust error handling (TBD).
  • File I/O: Introduce input functionality and file handling (TBD).
  • Standard Library: Develop a standard library with common functions and utilities (TBD).
  • Interpreter Enhancements: Improve the interactive interpreter with better real-time feedback, debugging capabilities, and performance optimizations (TBD).

License

AbySS is open-source software licensed under the MIT License. See the LICENSE file for details.

Dependencies

~7–18MB
~191K SLoC