# 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](#building-the-project) before you can run the REPL. ```bash /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. ```text Hello ! 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: ```rust 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. ```rust 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. ```rust 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. ```rust 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. ```rust 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. ```rust let numbers = [1, 2, 3, 4, 5]; let first = numbers[0]; // first = 1 let last = numbers[len(numbers) - 1]; // last = 5 ``` ```rust 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. ```rust 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** ```rust 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** ```rust 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** ```rust 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](https://golang.org/). Once you have Go installed, you can build the project by running the following command: ```bash 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. ```bash go test ./... ``` ## License This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.