From ff35e0308760659354d373ca867d85c735b8b32a Mon Sep 17 00:00:00 2001 From: Marko Zivanovic Date: Fri, 17 Apr 2015 16:16:18 +0200 Subject: [PATCH] WIP --- .gitignore | 11 +++++++ project.clj | 6 ++++ src/brainfuck.clj | 69 +++++++++++++++++++++++++++++++++++++++++ test/brainfuck_test.clj | 43 +++++++++++++++++++++++++ 4 files changed, 129 insertions(+) create mode 100644 .gitignore create mode 100644 project.clj create mode 100644 src/brainfuck.clj create mode 100644 test/brainfuck_test.clj diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c53038e --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +/target +/classes +/checkouts +pom.xml +pom.xml.asc +*.jar +*.class +/.lein-* +/.nrepl-port +.hgignore +.hg/ diff --git a/project.clj b/project.clj new file mode 100644 index 0000000..33d7039 --- /dev/null +++ b/project.clj @@ -0,0 +1,6 @@ +(defproject brainfuck "0.1.0-SNAPSHOT" + :description "FIXME: write description" + :url "http://example.com/FIXME" + :license {:name "Eclipse Public License" + :url "http://www.eclipse.org/legal/epl-v10.html"} + :dependencies [[org.clojure/clojure "1.6.0"]]) diff --git a/src/brainfuck.clj b/src/brainfuck.clj new file mode 100644 index 0000000..d4298f9 --- /dev/null +++ b/src/brainfuck.clj @@ -0,0 +1,69 @@ +(ns brainfuck) + +(defn inc-data + [{:keys [data dp] :as state}] + (let [curval (get data dp 0)] + (assoc state :data + (assoc data dp (if (= curval 255) 0 (inc curval)))))) + +(defn dec-data + [{:keys [data dp] :as state}] + (let [curval (get data dp 0)] + (assoc state :data + (assoc data dp (if (= curval 0) 255 (dec curval)))))) + +(defn output + [{:keys [data dp output] :as state}] + (assoc state :output (conj output (char (get data dp 0))))) + +(defn input + [{:keys [input data dp] :as state}] + (assoc state :data (assoc data dp (int (first input))) :input (rest input))) + +(defn jump-fwd + [{:keys [code input data ip dp] :as state}] + state) + +(defn jump-bwd + [{:keys [code input data ip dp] :as state}] + state) + +(defn execute-current-instruction + [{:keys [code ip dp] :as state}] + + (let [instruction (nth code ip)] + (case instruction + \> (assoc state dp (inc dp)) + \< (assoc state dp (dec dp)) + \+ (inc-data state) + \- (dec-data state) + \. (output state) + \, (input state) + \[ (jump-fwd state) + \] (jump-bwd state) + state) + ) + ) + +(defn execute-step + "Execute single BF instruction returning current state of the + output" + [{:keys [code input data ip dp] :as state} ] + + (if (< ip (count code)) + (let [new-state (execute-current-instruction state)] + (println new-state) + (recur (assoc new-state :ip (inc ip)))) + output)) + +(defn execute-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] + + (println (str "Executing code: " source)) + (execute-step {:code source :input input :data {} :ip 0 :dp 0 :output []})) + diff --git a/test/brainfuck_test.clj b/test/brainfuck_test.clj new file mode 100644 index 0000000..17da2ff --- /dev/null +++ b/test/brainfuck_test.clj @@ -0,0 +1,43 @@ +(ns brainfuck-test + (:require [clojure.test :refer :all] + [clojure.string :as s] + [brainfuck :refer [execute-string]])) + +(defn- ord->str [n] (str (char n))) + +(deftest basic-program-tests + (testing "output until inclusive 0" + (let [in (-> (range 1 256) vec (conj 0) + (->> (map char) (apply str)))] + (= in (execute-string "+[,.]" in)))) + + (testing "output n exclamation marks" + (let [pluses (s/join (repeat (int \!) \+)) + src (str ">" pluses "<,[>.<-]") + ! #(s/join (repeat % \!))] + (are [n] (= (execute-string src (ord->str n)) + (! n)) + 1 2 3 4 5 6 7 8 9 10))) + + (testing "memory 0 initialized" + (is (= (execute-string ".>." "") + "\0\0"))) + + (testing "memory operations" + (is (= (execute-string ".>.+.<." "") + "\0\0\1\0"))) + + (testing "input output" + (are [n] (= (execute-string ".,." (ord->str n)) + (str "\0" (char n))) + 11 12 13 14 15 16 17 18 19 20)) + + (testing "loops" + (let [z #(s/join (repeat % "\0"))] + (are [n] (= (execute-string "[>.<],[>.<-]" (ord->str n)) + (z n)) + 21 22 23 24 25 26 27 28 29 30)))) + +(deftest error-handling-test + (testing "insufficient input" + (is (nil? (execute-string "," "")))))