Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/evaporei/hisp

🌵 A lisp REPL interpreter made in Haskell
https://github.com/evaporei/hisp

haskell interpreter lisp repl

Last synced: 2 months ago
JSON representation

🌵 A lisp REPL interpreter made in Haskell

Awesome Lists containing this project

README

        


hisp-logo




A lisp REPL interpreter made in Haskell.


## Language Features

- [x] Numbers (`Float`)
- [x] Addition (`+`)
- [x] Subtraction (`-`)
- [x] Booleans (`true`, `false`)
- [x] Comparison operators (`=`, `>`, `<`, `>=`, `<=`)
- [x] def
- [x] if
- [x] lambdas

## How to run

### With Docker

Just run:
```
docker build . -t hisp
docker run -it hisp
```

> Observation: the build tools of Haskell themselves seem to be very heavy, I've got a 1GB Docker image on my computer just with a 3 line Dockerfile :fearful:

### Without Docker

`hisp` can be compiled/executed using either using `stack` or just by using `ghc`.

#### Using Stack

You will need `stack` installed on your computer, which is a tool for developing `Haskell` projects.

To compile and run:

```shell
stack run
```

#### Using just GHC

You will need `ghc` installed on your computer, which is the `Haskell` compiler.

This repository has a shell script (already with `chmod +x`, to run like a binary) called `compile_and_run.sh`. Just run it like this:

```shell
./compile_and_run.sh
```


hisp-terminal-repl-example

## Project structure

Since `hisp` is built with `stack`, the folders follow the standard of it.

```
hisp
│ README.md
│ ...
└─── app
│ └─── Main.hs
└─── src
│ └─── ...
└─── test
└─── ...
```

- The `app` folder contains the `main` function that starts the REPL.
- The `src` folder contains the code that is consumed by the `main` function, so it is much like a library folder.
- The `test` folder, well, as the name suggests, it has the code for the tests.

## Tests

Tests are run using `stack`, you should install it first. To run them, just use:

```shell
stack test
```

## Lint

To run the linter, you will have to install first `hlint`. To check if everything is correct:

```shell
hlint .
```

## How it works

Well it has 3 main steps, like many interpreters:

### 1. Tokenize

Considering someone wants to see the result of the following `lisp` code:


hisp-input-diagram

First we take it as raw input (`String`) and divide it into tokens, which is a list of values (`[String]`):


hisp-tokenize-diagram

### 2. Parse

Now that we have those `tokens`, we can convert them to something meaningful to us, that we can interpret.
```rust
enum Expression {
Symbol(String),
Number(Float),
List([Expression])
Function(([Expression]) -> Expression),
}
```

The code above is a pseudocode with a `Rust`-like syntax. The idea is that you can have a value that is **either** a `Symbol` (holding a String), a `Number` (holding a Float), a `List` (holding a list of `Expression`) **or** a `Function` (holding a function that maps a list of `Expression` to a single `Expression`).

```rust
let four = Expression::Number(4.0);
let plus_sign = Expression::Symbol("+");
```


hisp-parse-diagram

### 3. Evaluate

After we have the values that represent the code we need to execute, we will interpret it!


hisp-evaluate-diagram

To actually get to that `5.0` in the end, we must have somewhere defined that `+` receives a bunch of `Number`s and then sum them.
We could have an `Environment` that contains the standard functions/operators.
```rust
struct Environment {
data: HashMap// map of keys and values
}
```
Imagine this like a `class` that has a map of things like `+`, `-`, `=`, ... to values like `Expression::Function(implementation)`.

```javascript
function sum_implementation (values) {
return values.reduce((acc, curr) => acc + curr, 0)
}

environment = {
"+": sum_implementation,
"-": subtract_implementation,
...
}
```
Above there is a JSON/JavaScript-like visualization.

That is it! It was just an overview, but you can see all of this on this repository, just a bit more complex to make things like `def`s, `if`s and `lambda`s available to the language.

If you really want to understand more, you can follow some tutorials that are below :slightly_smiling_face:

## Notes

This project is heavly inspired by:

- [Rust Lisp REPL interpreter](https://m.stopa.io/risp-lisp-in-rust-90a0dad5b116)
- [Python Lisp REPL interpreter](https://norvig.com/lispy.html)

Also, I don't really know `Haskell` well, so the code probably could be a lot better. I did my best with what I know about the language at the time.

## License
>You can check out the full license [here](https://github.com/otaviopace/hisp/blob/master/LICENSE.md)

This project is licensed under the terms of the **WTFPL** license.
You just DO WHAT THE FUCK YOU WANT TO.