Building the Python REPL
Tags: python
Published: 2020-10-10 12:50:55

One of the features that come with Python is the python REPL(popularly known as the python interpreter). REPL stands for Read, Evaluate, Print, Loop. REPL enables us to write python code one line at a time. Though most python programs are run as scripts, the REPL can come in handy for quick testing of parts of code or some functions.  

In this article, I will build a python REPL (a minimal one, one with fewer features). Building a REPL is not a tedious task as the python developers have done most of the work(damage)😁. 

### Final Product
![final_demo.gif](https://raw.githubusercontent.com/amalshaji/pyrepl/master/files/final_demo.gif)

The focus of this article will be one two built-in functions __*eval()*__ and __*exec()*__.

Another important thing is understanding the difference between a `statement` and an `expression.` An expression evaluates a value, but a statement does(mean) something. In Python, the following are expressions,

```python
# python REPL

>>> 2+3
5
>>> 2*4
8
>>> "amal is great"
'amal is great'
>>> "python goes b" + "r"*20
'python goes brrrrrrrrrrrrrrrrrrrr'

# These above evaluates to a final answer, thus called expressions
```

```python

# these are statements

>>> from math import pow
>>> b = -1
>>> a = b
>>> for _ in range(10):
...     pass
...
```

__eval()__ is used for evaluating expressions. `eval("some expressions as string goes here")` will evaluate to whatever is input as a string. 

```python
>>> eval("2+3")
5
>>> eval("2**3")
8
>>> eval("print('python3 > python2')")
python3 > python2
```

__exec()__ is used for executing statements. 

```python
>>> exec("a=10")
>>> a
10
>>> exec("a+45")
>>> exec("b=a+45")
>>> b
55
>>> exec("from math import sqrt")
>>> sqrt(45)
6.708203932499369
```

That's it, and now we can mimic the REPL with ease. First, let's create the REPL function, which executes infinitely until `ctrl-c.` For each loop, we'll take input and evaluate it. If the evaluation fails, we'll execute the same string. If both fail, we'll print out the error. 

```python
def repl() -> None:
    try:
        while True:
            try:
                _in = input(">> ")
                try:
                    print(eval(_in))
                except:
                    out = exec(_in)
                    if out != None:
                        print(out)
            except Exception as e:
                print(f"Error: {e}")
    except KeyboardInterrupt as e:
        print("\nExiting...")
```


### Program Flow

![repl](https://cdn.hashnode.com/res/hashnode/image/upload/v1602307057294/3R7sfJtNF.png)


### Demo

![demo](https://j.gifs.com/WLG3lv.gif)


We have successfully re-created the REPL. But this looks very boring, let's add some colors to it. 

#### Install colorama
```bash
pip install colorama
```

Now we add RED color to errors and GREEN to successful code run. The final code is,
```python
from colorama import Fore, init

init(autoreset=True)

success = lambda input: f"{Fore.GREEN}{input}"
failure = lambda input: f"{Fore.RED}{input}"


def repl() -> None:
    try:
        while True:
            try:
                _in = input(">>> ")
                try:
                    print(success(eval(_in)))
                except:
                    out = exec(_in)
                    if out != None:
                        print(success(out))
            except Exception as e:
                print(failure(f"Error: {e}"))
    except KeyboardInterrupt as e:
        print("\nExiting...")


if __name__ == "__main__":
    print()
    print("Welcome to python nano-REPL")
    print("crtl-c to quit")
    print()
    repl()
```

`init(autoreset=True)` resets the style after each style change.

The above code is packaged for easy use.

#### Install

```bash
pip install py3repl
```

#### Usage

```bash
# call pyrepl from the command line

pyrepl

# if this fails, try

python -m py3repl.pyrepl
```


### Final Demo

![easter egg](https://raw.githubusercontent.com/amalshaji/pyrepl/master/files/finaldemo.gif)

### Drawbacks
- You can't navigate back and forth a line or between lines in Linux terminal(tested on WSL2)
- Only supports single-line statements

Repo: https://github.com/amalshaji/pyrepl

There may be a lot of bugs. Please report if you find any.