Having written the Beatnik interpreter a few months ago, I was recently reminded that my Befunge interpreter still didn’t have a Clojurescript version and, given it’s been almost exactly five years since that post, it’s about time. For the impatient, it’s hosted on Github pages. By default it loads ‘Hello World’, and there’s a couple of other examples on the ‘choose examples’ list, or you can load an arbitrary Befunge program as well using the ‘load file’ option. For longer programs, try reducing the ‘Time per step’ to get a more reasonable speed.
The core Befunge interpreter is mostly as-per the 2012 blog post, but I had to make a number of changes to make in run in-browser with Clojurescript, as it’s still actually also a valid Clojure program as well. I’d like to talk a little bit about those changes, the new bits that are the interpreter, and a few things that I’ve learnt over the last five years.
(defn charcode [c] #?(:clj (int c) :cljs (.charCodeAt c 0)))
and get a function that does the right thing in each language. This is also useful when you want to print to console in Clojure, but store in a variable for print-to-screen for Clojurescript.
Second was macros and testing. There’s a copy of the PyFunge test suite in the code base, and I’d previously just said “just run (runPyfungeTests) at your repl, it’s all fine”. This time around though, I understood macros better (in large part thanks to the “Clojure for the Brave and True” macro tutorial). Because of the homoiconicity of Clojure, they’re actually a lot less scary and easier to write than some languages (yeah, I’m looking at you, Rust macro system). I still have to lookup the syntax every time to remember the difference between ‘ v.s. ` v.s. ~, but once you get that, and understand that some bits are just so you output a list of those symbols, and some bits are used to generate the bits you’re going to output, they make a lot of sense. This meant I could now write a much better test that automagically generates Clojure tests from the Befunge code in the test folders, which means there’s now a proper Travis CI build.
After that, it was mostly a bog-standard Reagent app, with some use of Figwheel to do live reloading (which is still such a great development experience). The one extra thing that I hadn’t done before was using core.async to deal with things that you need to run in a loop as opposed to reacting to state changes (e.g. uploading a file, or running the Befunge program). Marcin Kulik has a good guide on this, and I also used Michael McClintock’s guide on uploading (although without the CSV parse step).