Socialify

Folder ..

Viewing README.md
298 lines (216 loc) • 12.4 KB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
<!-- # mana
Interpreted Toy Programming Language written in Go -->

# 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 <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:

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