luciferreeves / mana

Interpreted Toy Programming Language written in Go

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Mana

Mana is a toy programming language written in Go. It is a dynamically typed, interpreted language with a C-like syntax.

Note: The language is still in development and is not yet usable. The documentation below is a work in progress and is subject to change.

Development Roadmap

Implementation Status Specification Example Tests
LetStatement ✔️ Let Statements are used to declare variables let x = 5; ✔️
ReturnStatement ✔️ Return Statements are used to return values from functions return 5; ✔️
ExpressionStatement ✔️ Expression Statements are used to evaluate expressions 5 + 5; ✔️
IdentifierExpression ✔️ Identifier Expressions are used to reference variables x ✔️
IntegerLiteralExpression ✔️ Integer Literal Expressions are used to represent integer values 5 ✔️
PrefixExpression ✔️ Prefix Expressions are used to represent prefix operators !true ✔️
InfixExpression ✔️ Infix Expressions are used to represent infix operators 5 + 5 ✔️
BooleanLiteralExpression ✔️ Boolean Literal Expressions are used to represent boolean values true ✔️
IfExpression ✔️ If Expressions are used to represent conditional statements if (true) { return 5; } ✔️
BlockStatement ✔️ Block Statements are used to represent blocks of code { let x = 5; return x; } ✔️
FunctionLiteralExpression ✔️ Function Literal Expressions are used to represent function definitions fn(x) { return x; } ✔️
CallExpression ✔️ Call Expressions are used to call functions add(5, 5) ✔️
StringLiteralExpression ✔️ String Literal Expressions are used to represent string values "Hello, World!" ✔️
BuiltInFunctions ✔️ Built-in Functions are functions that are built into the language len("Hello, World!") ✔️
ArrayLiteralExpression ✔️ Array Literal Expressions are used to represent array values [1, 2, 3] ✔️
IndexExpression ✔️ Index Expressions are used to index into arrays myArray[0] ✔️
HashLiteralExpression ✔️ Hash Literal Expressions are used to represent hash values {"key": "value"} ✔️

*NYI = Not Yet Implemented

REPL

Mana ships with a Read-Eval-Print-Loop (REPL) that can be used to evaluate Mana code. The REPL can be started by running the mana execultable. Note that you will need to build the project before you can run the REPL.

/path/to/mana

Mana will then start the REPL and you can start typing Mana code. The REPL will evaluate the code and print the result.

Hello <username>! Welcome to Mana REPL!

███╗░░░███╗░█████╗░███╗░░██╗░█████╗░
████╗░████║██╔══██╗████╗░██║██╔══██╗
██╔████╔██║███████║██╔██╗██║███████║
██║╚██╔╝██║██╔══██║██║╚████║██╔══██║
██║░╚═╝░██║██║░░██║██║░╚███║██║░░██║
╚═╝░░░░░╚═╝╚═╝░░╚═╝╚═╝░░╚══╝╚═╝░░╚═╝

>>> let x = 5;

Syntax

Mana has a C-like syntax. The following is an example of a simple program written in Mana:

let x = 5;   // declare a variable named x and assign it the value 5
let y = 10;  // declare a variable named y and assign it the value 10

// this is a function that adds two numbers together
let add = fn(x, y) {
    return x + y;
};

// this will either add x and y if x is less than y, or return the difference between x and y
let result = if (x < y) {
    add(x, y)
} else {
    subtract(x, y)
}; // result = 15


puts(result); // prints the value of result to the console

Types

Mana is a dynamically typed language. This means that the type of a variable is determined at runtime. The following are the types that Mana supports:

Type Description Example
Integer A 64-bit signed integer 5
Boolean A boolean value true
String A string value "foo"

Operators

Mana supports the following operators:

Operator Description Example
+ Addition 5 + 5
- Subtraction 5 - 5
* Multiplication 5 * 5
/ Division 5 / 5
! Logical NOT !true
< Less Than 5 < 5
> Greater Than 5 > 5
== Equal To 5 == 5
!= Not Equal To 5 != 5

Variables

Variables are declared using the let keyword. The variable name is followed by an equals sign and an expression. The expression is evaluated and the result is assigned to the variable.

let x = 5;

Conditionals

Mana supports If-Else conditionals. An IfExpression in Mana is composed of two parts: the condition and the consequence. The condition is an expression that evaluates to a boolean value. The consequence is a BlockStatement that is executed if the condition evaluates to true. The consequence is optional. If the condition evaluates to false and there is no consequence, then the IfExpression evaluates to null. If there is a consequence, then the IfExpression evaluates to the value of the last statement in the consequence.

if (x < y) {
    x + y;
} else {
    x - y;
}

Functions

Mana supports first-class functions. This means that functions can be passed as arguments to other functions, returned from functions, and assigned to variables. The following is an example of a function definition in Mana.

Functions are defined using the fn keyword. The function name is followed by a list of parameters in parentheses. The function body is enclosed in curly braces. The function body is a BlockStatement, which means that it is a list of statements enclosed in curly braces. The last statement in the function body is the return statement, which is used to return a value from the function. Functions, themselves, are ExpressionStatements, which means that they evaluate to a value. The value that a function evaluates to is the value that is returned from the function.

fn add(x, y) {
    return x + y;
}

Built-in Functions

Mana has a number of built-in functions that are available to the programmer. These functions are built into the language and can be used without having to define them. The following is a list of built-in functions that are available in Mana:

Function Description Parameters Example Output
len Returns the length of a string or an array string len("Hello, World!") 13
push Appends an element to the end of an array array, expression push([1, 2, 3], 4) [1, 2, 3, 4]
first Returns the first element of an array array first([1, 2, 3]) 1
last Returns the last element of an array array last([1, 2, 3]) 3
rest Returns all but the first element of an array array rest([1, 2, 3]) [2, 3]
puts Prints a value to the console expression puts("Hello, World!") Hello, World!

Strings

Strings in Mana are enclosed in double quotes. Strings are immutable, which means that once a string is created, it cannot be changed. Strings can be concatenated using the + operator.

let greeting = "Hello, ";
let name = "World!";
let message = greeting + name; // message = "Hello, World!"

Arrays

Arrays in Mana are ordered collections of values. Arrays are created using square brackets. Arrays can contain values of any type, including other arrays. Arrays are indexed using square brackets. The index is an integer value that represents the position of the element in the array. Arrays are zero-indexed, which means that the first element in the array is at index 0.

let numbers = [1, 2, 3, 4, 5];

let first = numbers[0]; // first = 1

let last = numbers[len(numbers) - 1]; // last = 5
let mixed = [1, "two", true, fn(x) { x * x; }];
let c = 69;

let f = mixed[3](5); // f = 25

let u = ["one", "two", 3][5 - 4]; // u = "two"

let k = mixed[c - 67]; // k = true

Hashes

Hashes in Mana are unordered collections of key-value pairs. Hashes are created using curly braces. Hashes can contain keys of supported types (Boolean, Integers, and Strings - Objects which implement the Hashable interface). Hashes can contain values of any type, including other hashes. Hashes are indexed using square brackets. The index is a key that represents the key of the element in the hash.

let person = {
    "name": "Alice",
    "age": 30,
    "isStudent": false,
    "address": {
        "street": "123 Main St",
        "city": "Anytown"
    }
};

let name = person["name"]; // name = "Alice"
let city = person["address"]["city"]; // city = "Anytown"

Building Advanced Functions

Map

let map = fn(arr, f) {
    let iter = fn(arr, accumulated) {
        if (len(arr) == 0) {
            accumulated
        } else {
            iter(rest(arr), push(accumulated, f(first(arr))));
        }
    };

    iter(arr, []);
};

let a = [1, 2, 3, 4, 5];
let double = fn(x) { x * 2; };

let doubled = map(a, double); // doubled = [2, 4, 6, 8, 10]

Reduce

let reduce = fn(arr, initial, f) {
    let iter = fn(arr, result) {
        if (len(arr) == 0) {
            result
        } else {
            iter(rest(arr), f(result, first(arr)));
        }
    };

    iter(arr, initial);
};

let a = [1, 2, 3, 4, 5];

let sum = fn(arr) {
    reduce(arr, 0, fn(initial, el) { initial + el; });
};

sum(a); // sum = 15

Filter

let filter = fn(arr, f) {
    let iter = fn(arr, filtered) {
        if (len(arr) == 0) {
            filtered
        } else {
            let x = first(arr);
            if (f(x)) {
                iter(rest(arr), push(filtered, x));
            } else {
                iter(rest(arr), filtered);
            }
        }
    };

    iter(arr, []);
};

let a = [1, 2, 3, 4, 5];

let two = fn(x) { x == 2; };

let filtered = filter(a, two); // filtered = [2]

Building the Project

To build the project, you will need to have Go installed on your machine. You can download Go from the official website. Once you have Go installed, you can build the project by running the following command:

go build

This will create an executable file named mana in the root of the project directory.

Running the Tests

To run the tests, you can use the go test command. This will run all of the tests in the project.

go test ./...

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

About

Interpreted Toy Programming Language written in Go

License:Apache License 2.0


Languages

Language:Go 100.0%