Making the Elm-Brainfuck-IDE

Today, I write about how I created my Elm-Brainfuck-IDE project. I also discuss the design choices that I went through and how I used typed CSS in Elm.

I decided to learn Elm. I created the Rust-Elm-Template that incorporates Elm programs into a Rust web-view to have “native” desktop applications written in a web-based language. I then created Elm-Markdown-Latex to convert markdown documents into LaTeX documents. However, the problem with these two projects is that I wrote very little Elm in both of these. For the Rust-Elm-Template, I basically wrote no Elm code (I didn’t know it well at the time) and for the Elm-Markdown-Latex project, I still had to depend on an external CSS file and a template HTML document. What I wanted was a fully contained web application that’s entirely written in Elm. No external CSS files, no external HTML documents - just Elm.

A brief introduction to Brainfuck

I decide to redo a popular personal project of mine - an IDE for Brainfuck. Brainfuck is an esoteric programming language that is known for being very difficult to comprehend due to the fact that Brainfuck programs are made up of combinations of 8 characters: + - > < [ ] . ,. Since it only has these few operators, it’s a surprisingly simple programming language when you get to know it. The concept is simple: You have an infinitely sized array (initialized with zeros) and you have operators that can move a pointer and change the value at that pointer in the array. You then have the following operators:

Operator What it does
+ Increment the value at the pointer
- Decrement the value at the pointer
> Move the pointer to the right by one cell
< Move the pointer to the left by one cell
[ If the value at the pointer is zero, jump to the next matching ] character
] If the value at the pointer is not zero, jump back to the previous matching [ character
. Output the ASCII representation of the value at the pointer
, Input a character and put its decimal representation as the value at the pointer

For example, if you wanted to print out the word “Hi”, you would look up the letters “H” and “i” in the ASCII table and find their decimal representation (72 and 105 respectively) and then write a program to output those. As a very basic example:

+++++ +++++
+++++ +++++
+++++ +++++
+++++ +++++
+++++ +++++
+++++ +++++
+++++ +++++
++ .         Prints "H"
+++++ +++++
+++++ +++++
+++++ +++++
+++ .        Prints "i"

This allows you to write very basic programs that take into account input, output as well as loops and basic conditionals (using loops to check if a cell’s value is zero). In a strange way, it’s somewhat similar to the lambda calculus Programming Computable Functions - you have the ability to increase a value (succ) or decrease a value (pred) and perform loops and conditionals if a value is zero. That aside, Brainfuck is a very simply language and should be easy to create an interpreter for.

In previous years, I’ve tried creating a Brainfuck IDE to help improve my understanding of the esoteric language. I initially created a Java-based desktop application using the Swing framework to help others write Brainfuck programs in a better coding environment than just a text editor. It included various panels, such as a memory output panel to display the current state of the program, as well as a settings panel that included different helper tools, such as ASCII value printing and formatting buttons. A few years after creating that, I worked on a web-based Brainfuck IDE that was written using NodeJS. Unfortunately, I never came close to finishing it to a standard that I was pleased with.

Project planning

So, what better way to learn Elm than to redo some old project in it? I like the idea of having a web-based application, similar to my NodeJS project - it allows you to easily showcase the work you’ve done online without requiring someone to download it manually. I sit down and begin to plan the main “aims” for the project and any extensions that I’d implement to push the limits of what I can do in the language. This planning phase is heavily inspired by my third year project at university, where we are tasked to outline the aims and extensions of our projects. This lead me to the following aims:

  • Create a Brainfuck interpreter in Elm
  • Create a GUI in Elm that lets users write and execute Brainfuck code
  • Use Elm to write CSS, instead of using an external CSS file

In addition, I decide to have the following options as extensions:

  • Implement code formatting/minimization
  • Add a visualizer to show the current memory
  • Add a “live interpreter” mode which evaluates programs while you type them
  • Add a Brainfuck to Ook! converter, inspired by my Java-based project
  • Add a method to change the number of bits a program uses when interpreting

Creating a UI in Elm

Elm comes with the elm-lang/html package which is used to create HTML documents in Elm. It provides functions to create different HTML elements which is (somewhat) intuitive. For example, the following HTML code:

<div>
    <p>hello</p>
</div>

can be written in Elm:

div [] [
  p [] [ text "hello" ]
  ]

However, this does not avoid the fact that in the end, you’re basically writing HTML, except in a different language that looks a lot more complicated than raw HTML. So I begin to search for alternatives. I quickly discover the mdgriffith/elm-ui package, which is basically a different way of representing layouts without using HTML or CSS. It heavily revolves around using rows and columns and using a form of styling similar to CSS. After playing around with it for a bit, I discover that I find the library difficult to grasp - especially since I’ve used HTML in the past and know the core components inside and out.

So, I stick with the plain ol’ elm-lang/html package. I create my UI using the basic HTML elements (div, textarea and button) and create a UI that’s good enough for basic functionality. But now I want to style it. Luckily, elm-lang/html includes basic styling using the style function! The style function lets you provide a key-value paired list of style options and their corresponding values, as shown below.

myStyle : Attribute msg
myStyle =
  style
    [ ("backgroundColor", "red")
    , ("height", "90px")
    , ("width", "100%")
    ]

However, there are better ways to go about doing so. This method still requires me to assume that all of my CSS is perfect, and doesn’t allow me to easily modify the CSS as I see fit based on the logic of the interpreter. Sure, this can be sorted out easily by using classes or IDs, but I personally don’t like the design pattern of having to refer to classes and IDs all the time. Luckily, the incredibly popular rtfeldman/elm-css package comes to my rescue! Boasting typed CSS and being a “drop-in replacement for elm-lang/html”, I decide to switch to a better CSS system. Now, I may be very biased here, but typed CSS is awesome. Since it’s typed, any invalid CSS is shown as a compilation error in your Elm program, which ensures that your CSS isn’t doing stuff you wouldn’t expect (most of the time). It’s simple to integrate into my project (despite its example in its README file not actually working).

Overall, I end up with a powerful method of writing HTML and CSS that is well-typed and all errors are checked at compile-time, which ensures that no dodgy HTML or CSS is generated (as is possible with regular HTML where you can basically write anything and it’ll parse it as if it were fine).

Creating the Brainfuck interpreter

After creating the base UI (now looking spectacular), I begin working on the Brainfuck interpreter. This is a truly exciting part to the project, since in my previous projects (Java and NodeJS), I ended up using a Brainfuck interpreter created by someone else and have never written an interpreter by myself before. In addition to that, I’m writing it in Elm which is a purely functional programming language, so as you’d expect, there are different constructs to adhere to.

Sticking with the “Elm-like thinking”, I begin by deciding on what the underlying model would be to represent a program in execution. I decide to include the program itself (as a String), an output string which keeps track of the outputted characters (via the . operator) and an instruction pointer that keeps track of which instruction the program is currently running (so, sort of like a program counter). I then make a model for the memory that is basically our “infinite array”. It simply includes a pointer and a dictionary which maps array indices to their corresponding value, as opposed to having an infinite array (it’s easier to traverse and look up values). Basically:

Program = {
    program : String,
    programCounter : Int,
    output : String
}

Memory = {
    pointer : Int,
    data : Dictionary Int Int
}

From this, I create a function that takes in a program as a string, initializes an empty program model with the string, and initializes an empty memory model. My main plan is to basically increment the program counter, look at the instruction at that location and update the memory as defined by the operator. This turns out trivial for the >, <, + and - operators which simply modify the memory’s pointer or values in the dictionary. The . operator is also relatively simple, as it simply appends the character of the current memory’s point to the program output.

So far, everything’s looking quite good. I decide to ignore inputs (the , operator) for the time being, as it’s rarely used. I begin to work on looping. In order to handle looping, it has to be able to determine the next matching [ or ] character, at any given point in time. To handle this, I extend the model of my program to include a jump map - a mapping of the indices of brackets to their corresponding matching bracket. For example, [[][]] would produce the following mapping, based on the character index (starting from zero):

[[][]]
0 <-> 5
1 <-> 2
3 <-> 4

This is implemented rather trivially using reductions and recursion (key aspects of functional programming). By using a jump map, this lets the interpreter know which bracket to jump back or forward to. By using the jump map along with updating the program counter to the location of the matching [ or ] operator, this allows the interpreter to handle loops!

To implement the input operator (,), I take inspiration from copy’s brainfuck interpreter which handles it being parsing the entire input before executing the program (as opposed to say, fatiherikli’s brainfuck visualizer which inputs characters one at a time when needed. Using this approach works very well for the functionally pure language Elm, since it doesn’t require having to pause execution and wait for an input (and thus, having to deal with program states and extra code here there and everywhere).

Limitations

Despite completing all of the main project objectives and (over the course of a few weeks) the project extensions, there are still some things with the project that don’t work as intended.

There are still edge cases that my interpreter doesn’t catch. For example, infinite loops such as [ +- ] will cause the web application to freeze as it cannot evaluate an infinite loop.

Also, (the most frustrating limitation in my opinion) is the fact that pressing the tab key doesn’t insert a tab character - instead it focuses on the next element. Unfortunately, this is the default behaviour in web applications and can be mitigated using JavaScript. Since the main aim of the project was to write the entire project in Elm, the use of JavaScript was outside of the scope of the project. I was able to detect when the tab key was pressed by using Elm’s subscription system, however I was unable to determine the location of the current user’s caret position and thus, was unable to implement a proper tab key. This is a pretty major flaw, as the project aimed to be a coding environment for Brainfuck and indentation helps to improve readability of the code.

Reflections

Overall, I am very proud of the result of the project. I was able to gain a good understanding of the Elm architecture and primarily the Model-View-Controller software engineering design pattern. I was able to brush up on my functional programming skills since Elm is a functional programming language which required a completely different mindset and I believe I performed well through the project. I was able to create a web application that is very easily extensible (adding new features is as simple as updating the ‘controller’ part of the program) and create a project that can be used as a template for future Elm-based web applications.

Written on December 7, 2019