Created
March 10, 2015 22:34
-
-
Save amnn/65e3e83e9fac8be43266 to your computer and use it in GitHub Desktop.
Brainfuck interpreter written in Clojure
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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