Skip to content

Instantly share code, notes, and snippets.

@amnn
Created March 10, 2015 22:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save amnn/65e3e83e9fac8be43266 to your computer and use it in GitHub Desktop.
Save amnn/65e3e83e9fac8be43266 to your computer and use it in GitHub Desktop.
Brainfuck interpreter written in Clojure
(ns brainfuck
(:refer-clojure :exclude [replace])
(:require [clojure.string :refer [join replace]]))
;;;;;; Parser ;;;;;;
(defn- wrap-outer-brackets
"Wrap string in square brackets so that its contents are read into a vector
by `read-string`."
[s] (str \[ s \]))
(defn- parse
"Take the source as a string, and return a clojure vector of vectors
data structure in which individual operations appear as symbols. The
input command (`,`) will be represented by an `i`.
If the input is malformed, outputs nil.
E.g. (parse \"+[,.]\") becomes '[+ [i .]]"
[src]
(try
(->> (replace src \, \i)
(join \space)
wrap-outer-brackets
read-string)
(catch RuntimeException e
nil)))
;;;;;; Memory Operations ;;;;;;
(def empty [(repeat 0) 0 (repeat 0)])
(defn- shift-left [[l h r]]
[(rest l) (first l) (cons h r)])
(defn- shift-right [[l h r]]
[(cons h l) (first r) (rest r)])
(defn- head [[_ h _]] h)
(defn- update-head [f [l h r]] [l (f h) r])
(defn- replace-head [[l _ r] x] [l x r])
(def inc-head (partial update-head #(-> % inc (mod 256))))
(def dec-head (partial update-head #(-> % dec (mod 256))))
;;;;;; Evaluator ;;;;;;
(defn- eval-bfck [state ast]
(reduce
(fn [state op]
(case op
> (update-in state [:mem] shift-right)
< (update-in state [:mem] shift-left)
+ (update-in state [:mem] inc-head)
- (update-in state [:mem] dec-head)
. (update-in state [:out] conj
(-> state :mem head char))
i (if (-> state :in empty?)
(reduced nil)
(-> state
(update-in [:in] rest)
(update-in [:mem] replace-head
(-> state :in first int))))
;; [...]
(loop [state state]
(if (-> state :mem head zero?)
state
(recur (eval-bfck state op))))))
state ast))
(defn evaluate-string
"Evaluate the Brainfuck source code in `source` using `input` as a source of
characters for the `,` input command.
Either returns a sequence of output characters, or `nil` if there was
insufficient input."
[source input]
(when-let [ast (parse source)]
(when-let [{:keys [out]}
(eval-bfck {:in input
:mem empty
:out []}
ast)]
out)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment