├── .gitignore ├── .travis.yml ├── project.clj ├── src └── brainfuck │ └── compiler.clj ├── LICENSE ├── test └── brainfuck │ └── compiler_test.clj └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | .hgignore 11 | .hg/ 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: clojure 3 | lein: lein2 4 | jdk: 5 | - openjdk7 6 | - oraclejdk7 7 | branches: 8 | except: 9 | - gh-pages 10 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject brainfuck "0.3.2" 2 | :description "brainfuck interpreter" 3 | :url "https://github.com/pyr/brainfuck" 4 | :license {:name "MIT License" 5 | :url "http://opensource.org/licenses/MIT"} 6 | :dependencies [[org.clojure/clojure "1.7.0"]]) 7 | -------------------------------------------------------------------------------- /src/brainfuck/compiler.clj: -------------------------------------------------------------------------------- 1 | (ns brainfuck.compiler) 2 | 3 | (defn parse [text] 4 | (loop [stack nil, [op & text] text, code nil] 5 | (cond 6 | (nil? op) (reverse code) 7 | ((set "+-.,><") op) (recur stack text (conj code [op])) 8 | (= \[ op) (recur (conj stack code) text nil) 9 | (= \] op) (let [[top & btm] stack] 10 | (recur btm text (conj top [\[ (reverse code)]))) 11 | :else (recur stack text code)))) 12 | 13 | (defn exec [{:keys [reg pos] :as vm} [op code]] 14 | (case op 15 | \+ (update-in vm [:reg pos] (fnil inc 0)) 16 | \- (update-in vm [:reg pos] (fnil dec 0)) 17 | \. (do (print (char (nth reg pos))) vm) 18 | \, (assoc-in vm [:reg pos] (.read *in*)) 19 | \> (let [i (inc pos)] 20 | (assoc vm :pos i :reg (if (>= i (count reg)) (conj reg 0) reg))) 21 | \< (update-in vm [:pos] dec) 22 | \[ (loop [{:keys [reg pos] :as vm} vm] 23 | (if (zero? (nth reg pos)) vm (recur (reduce exec vm code)))) 24 | vm)) 25 | 26 | (defn run [text] 27 | (reduce exec {:reg [0] :pos 0} (parse text))) 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Pierre-Yves Ritschard 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /test/brainfuck/compiler_test.clj: -------------------------------------------------------------------------------- 1 | (ns brainfuck.compiler-test 2 | (:require [clojure.test :refer :all] 3 | [brainfuck.compiler :refer :all])) 4 | 5 | (deftest bf-test 6 | (testing "hello world" 7 | (is (= "Hello World!\n" 8 | (with-out-str 9 | (run (str "++++++++" 10 | "[>++++[>++>+++>+++>+<<<<-]" 11 | ">+>+>->>+[<]<-]>>.>---.+++++++.." 12 | "+++.>>.<-.<.+++.------.--------.>>+.>++.")))))) 13 | 14 | 15 | (testing "cat" 16 | (is (= "foobar" 17 | (with-in-str "foobar" 18 | (with-out-str 19 | (run "-,+[-.,+]")))))) 20 | 21 | (testing "rot13" 22 | (is (= "sbbone" 23 | (with-in-str "foobar" 24 | (with-out-str 25 | (run (str "-,+[" 26 | "-[" 27 | ">>++++[>++++++++<-]" 28 | "<+<-[>+>+>-[>>>]<[[>+<-]>>+>]<<<<<-]" 29 | "]>>>[-]+>--[-[<->+++[-]]]<[++++++++++++<" 30 | "[>-[>+>>]>[+[<+>-]>+>>]<<<<<-]>>[<+>-]>" 31 | "[-[-<<[-]>>]<<[<<->>-]>>]<<[<<+>>-]]<" 32 | "[-]<.[-]<-,+]")))))))) 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # brainfuck 2 | 3 | [![Build Status](https://secure.travis-ci.org/pyr/brainfuck.png)](http://travis-ci.org/pyr/brainfuck) 4 | 5 | A brainfuck interpreter library. 6 | Brainfuck code is first parsed into a simplistic AST. 7 | The AST execution continuously augments a virtual machine 8 | containing register cells and a pointer to the current position. 9 | 10 | Brainfuck input and output respects clojure's `*in*` and `*out*`, 11 | for instance: 12 | 13 | ```clojure 14 | (with-out-str 15 | (with-in-str "foobar" 16 | (run "-,+[-.,+]"))) ;; #=> "foobar" 17 | ``` 18 | 19 | The AST is a simple list of vectors, containing either one 20 | or two members. The first member is the character representing 21 | the operation, the second member is only present for the `\[` operation 22 | and contains the branch's code. 23 | 24 | The AST interpreter augments a virtual machine implemented as 25 | a clojure persistent hash-map with two keys: 26 | 27 | - `reg`: Arbitrary width (grown as needed) register cells 28 | - `pos`: Current cell offset 29 | 30 | The initial virtual machine is thus defined as: 31 | 32 | ```clojure 33 | {:reg [0] 34 | :pos 0} 35 | ``` 36 | 37 | This library exports three functions: 38 | 39 | - `parse`: Given an input brainfuck program, yields a simplistic AST. 40 | - `exec`: Augments a virtual machine with the result of the next operation. 41 | - `run`: Parse and execute a program with an initial virtual machine. 42 | 43 | ## Examples 44 | 45 | See the `test` directory for additional examples 46 | 47 | ## License 48 | 49 | Copyright © 2015 Pierre-Yves Ritschard 50 | 51 | Distributed under the MIT License, see LICENSE for details. 52 | 53 | --------------------------------------------------------------------------------