![logo](https://satish.dev/assets/img/splang/logo.svg)
# splang

**splang** is an esoteric programming language written in the form of a playlist. A writeup of the language can be found in the [splang documentation](https://satish.dev/projects/splang/).

Programs, or splangs, are playlists whose track metadata encode operations. Take a look at the [splang helloworld](https://open.spotify.com/playlist/7cPsICy0U7WM8Q67SuYXds?si=c292b92145604f56) for a sample splang playlist. Use the splang interpreter to execute these playlists programmatically.

The most convenient way to run splang code is by creating a Spotify playlist and using a developer token. Refer to the [Spotify API documentation](https://developer.spotify.com/documentation/web-api/) and [Spotipy API documentation](https://spotipy.readthedocs.io/en/latest/) for environment setup. However, you can also run splang code using a local JSON file that contains the playlist data.

## 🛠️ Installation and Use

### 📦 Development Setup

To get started with development, clone the [splang repository](https://github.com/NamanSatish/splang) and set up your environment:

Ensure Python **>=3.11+** is installed. It's recommended to use a virtual environment:

```bash
python3 -m venv .venv
source .venv/bin/activate
```

Install dependencies:

```bash
pip install --upgrade pip setuptools wheel
pip install -e ".[dev]"
```

This installs the project in editable mode along with development tools like `pytest`.

### 🚀 General Setup

To install splang for general use, you can install it directly from PyPI:

```bash
pip install splang
```

#### ▶️ Run with Spotify developer access:

```bash
splang
```

Ensure you have a valid Spotify developer token set up in your environment, i.e export `SPOTIPY_CLIENT_ID`, `SPOTIPY_CLIENT_SECRET`, and `SPOTIPY_REDIRECT_URI` as per the Spotipy documentation.

#### ▶️ Run with a local playlist file:

```bash
splang helloworld.json
```
---

#### ▶️ Run the example helloworld:

```bash
splang -hello
```


## 📜 Splang v1.0 Language Reference

| **Keyterm** | **Description**                                                                       |
| ----------- | ------------------------------------------------------------------------------------- |
| LS          | Last Second's place, e.g 3:45 --> 5                                                   |
| FS          | First Second's place , e.g 3:45 --> 4                                                 |
| TrackID     | The unique ID of a song in Spotify's database.                                        |
| (song + n)  | The song n positions after the current song in the playlist.                          |
| Stack       | A LIFO data structure, operates on single elements.                                   |
| Heap        | A data structure that stores values at a specific index, operates on single elements. |
| RA Stack    | A LIFO data structure that stores return addresses for function calls.                |

| **Time/Opcode** | **Command**       | **Parameter(s)** | **Description**                                                                                                                    |
| -------- | ----------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| 0:00     | `NOP`             | None             | No operation, does nothing.                                                                                                        |
| 0:01     | `HALT`            | None             | Halts the program.                                                                                                                 |
| 0:02     | `LABEL`           | 1                | Label, defines the (song + 1)'s trackID as a label.                                                                                |
| 0:03     | `JUMP`            | 1                | Jumps to the label defined by the (song + 1) trackID.                                                                              |
| 0:04     | `JUMPZ`           | 1                | Jumps to the label defined by the (song + 1) trackID if the top of the stack is 0.                                                 |
| 0:05     | `JUMPNZ`          | 1                | Jumps to the label defined by the (song + 1) trackID if the top of the stack is not 0.                                             |
| 0:06     | `JUMPZ_HEAP`      | 1                | Jumps to the label defined by the (song + 1) trackID if the value at the index defined by the (song + 1) trackID in heap is 0.     |
| 0:07     | `JUMPNZ_HEAP`     | 1                | Jumps to the label defined by the (song + 1) trackID if the value at the index defined by the (song + 1) trackID in heap is not 0. |
| 0:08     | `CALL`            | 1                | Pushes return address to RA stack and jumps to the label defined by the (song + 1) trackID.                                        |
| 0:09     | `RETURN`          | None             | Pops the return address from the RA stack and jumps to it.                                                                         |
| 0:10     | `ADD`             | None             | Adds the top two elements of the stack and pushes the result.                                                                      |
| 0:11     | `SUB`             | None             | Subtracts the top two elements of the stack and pushes the result.                                                                 |
| 0:12     | `MUL`             | None             | Multiplies the top two elements of the stack and pushes the result.                                                                |
| 0:13     | `DIV`             | None             | Divides the top two elements of the stack and pushes the result.                                                                   |
| 0:14     | `MOD`             | None             | Modulus of the top two elements of the stack (b mod a) and pushes the result.                                                      |
| 0:15     | `POW`             | None             | Raises the top element of the stack to the power of the second top element and pushes the result.                                  |
| 0:16     | `Not_implemented` | None             | Not implemented.                                                                                                                   |
| 0:17     | `Not_implemented` | None             | Not implemented.                                                                                                                   |
| 0:18     | `Not_implemented` | None             | Not implemented.                                                                                                                   |
| 0:19     | `Not_implemented` | None             | Not implemented.                                                                                                                   |
| 0:20     | `PUSH_LS`         | 1                | Pushes the LS of the (song + 1) duration to the stack.                                                                             |
| 0:21     | `PUSH_FS`         | 1                | Pushes the FS of the (song + 1) duration to the stack.                                                                             |
| 0:22     | `SHIFT_R_LS`      | 1                | Shifts the value of the top element of the stack to the right, adding the LS of the (song + 1)'s duration.                         |
| 0:23     | `SHIFT_L_LS`      | 1                | Shifts the value of the top element of the stack to the left, adding the LS of the (song + 1)'s duration.                          |
| 0:24     | `SHIFT_R_FS`      | 1                | Shifts the value of the top element of the stack to the right, adding the FS of the (song + 1)'s duration.                         |
| 0:25     | `SHIFT_L_FS`      | 1                | Shifts the value of the top element of the stack to the left, adding the FS of the (song + 1)'s duration.                          |
| 0:26     | `POP`             | None             | Pops the top element of the stack.                                                                                                 |
| 0:27     | `DUP`             | None             | Duplicates the top element of the stack.                                                                                           |
| 0:28     | `SWAP`            | None             | Swaps the top two elements of the stack.                                                                                           |
| 0:29     | `Not_implemented` | None             | Not implemented.                                                                                                                   |
| 0:30     | `STORE`           | 1                | Stores the top element of the stack in heap at the index defined by the (song + 1)'s trackID.                                      |
| 0:31     | `STORE_TOP`       | None             | Stores the top element of the stack in the heap at the index defined by the second element of the stack.                           |
| 0:32     | `LOAD`            | 1                | Loads the value at the index defined by the (song + 1)'s trackID from heap to the stack.                                           |
| 0:33     | `LOAD_TOP`        | None             | Loads the value at the index defined by the top element of the stack from heap to the stack.                                       |
| 0:34     | `INC_HEAP`        | 1                | Increments the value at the index defined by the (song + 1)'s trackID in heap.                                                     |
| 0:35     | `DEC_HEAP`        | 1                | Decrements the value at the index defined by the (song + 1)'s trackID in heap.                                                     |
| 0:36     | `INC`             | None             | Increments the top element of the stack.                                                                                           |
| 0:37     | `DEC`             | None             | Decrements the top element of the stack.                                                                                           |
| 0:38     | `Not_implemented` | None             | Not implemented.                                                                                                                   |
| 0:39     | `Not_implemented` | None             | Not implemented.                                                                                                                   |
| 0:40     | `STDIN_INT`       | None             | Reads an integer until a newline from stdin and pushes it to the stack.                                                            |
| 0:41     | `STDIN`           | None             | Reads a string until a newline from stdin and pushes each character to the stack as an ASCII value.                                |
| 0:42     | `STDOUT_INT`      | None             | Pops the top element of the stack and prints it to stdout as an integer.                                                           |
| 0:43     | `STDOUT`          | None             | Pops the top element of the stack and prints it to stdout as an ASCII character.                                                   |
| 0:44     | `READ_CHAR`       | 1                | Reads the first character of the (song + 1)'s title and pushes it to the stack as an ASCII value.                                  |
| 0:45     | `LISTEN`          | 1                | Sleeps for the full duration of the (song + 1).                                                                                    |
| 0:46     | `Not_implemented` | None             | Not implemented.                                                                                                                   |
| 0:47     | `Not_implemented` | None             | Not implemented.                                                                                                                   |
| 0:48     | `Not_implemented` | None             | Not implemented.                                                                                                                   |
| 0:49     | `Not_implemented` | None             | Not implemented.                                                                                                                   |
| 0:50     | `AND`             | None             | Logical AND of the top two elements of the stack and pushes the result.                                                            |
| 0:51     | `OR`              | None             | Logical OR of the top two elements of the stack and pushes the result.                                                             |
| 0:52     | `XOR`             | None             | Logical XOR of the top two elements of the stack and pushes the result.                                                            |
| 0:53     | `NOT`             | None             | Logical NOT of the top element of the stack and pushes the result.                                                                 |
| 0:54     | `EQUAL`           | None             | Checks if the top two elements of the stack are equal and pushes the result.                                                       |
| 0:55     | `NOT_EQUAL`       | None             | Checks if the top two elements of the stack are not equal and pushes the result.                                                   |
| 0:56     | `GREATER`         | None             | Checks if the top element of the stack is greater than the second top element and pushes the result.                               |
| 0:57     | `LESS`            | None             | Checks if the top element of the stack is less than the second top element and pushes the result.                                  |
| 0:58     | `GREATER_EQUAL`   | None             | Checks if the top element of the stack is greater than or equal to the second top element and pushes the result.                   |
| 0:59     | `LESS_EQUAL`      | None             | Checks if the top element of the stack is less than or equal to the second top element and pushes the result.                      |