Permalink
Browse files

first commit

  • Loading branch information...
0 parents commit 189f6ffb012bb3e0dc161ed1fa75637ac7203a40 @txus committed Dec 2, 2010
Showing with 449 additions and 0 deletions.
  1. +3 −0 .gitignore
  2. +4 −0 Gemfile
  3. +33 −0 Gemfile.lock
  4. +15 −0 Rakefile
  5. +49 −0 Readme.md
  6. +3 −0 bin/brainfuck
  7. +26 −0 brainfuck.gemspec
  8. +21 −0 examples/hello_world.bf
  9. +102 −0 lib/brainfuck.rb
  10. +3 −0 lib/brainfuck/version.rb
  11. +107 −0 spec/acceptance_spec.rb
  12. +65 −0 spec/interpreter_spec.rb
  13. +18 −0 spec/spec_helper.rb
3 .gitignore
@@ -0,0 +1,3 @@
+pkg/*
+*.gem
+.bundle
4 Gemfile
@@ -0,0 +1,4 @@
+source "http://rubygems.org"
+
+# Specify your gem's dependencies in brainfuck.gemspec
+gemspec
33 Gemfile.lock
@@ -0,0 +1,33 @@
+PATH
+ remote: .
+ specs:
+ brainfuck (0.0.1)
+ highline
+
+GEM
+ remote: http://rubygems.org/
+ specs:
+ diff-lcs (1.1.2)
+ highline (1.6.1)
+ rspec (2.2.0)
+ rspec-core (~> 2.2)
+ rspec-expectations (~> 2.2)
+ rspec-mocks (~> 2.2)
+ rspec-core (2.2.1)
+ rspec-expectations (2.2.0)
+ diff-lcs (~> 1.1.2)
+ rspec-mocks (2.2.0)
+ simplecov (0.3.7)
+ simplecov-html (>= 0.3.7)
+ simplecov-html (0.3.9)
+
+PLATFORMS
+ java
+ ruby
+
+DEPENDENCIES
+ brainfuck!
+ bundler
+ highline
+ rspec
+ simplecov
15 Rakefile
@@ -0,0 +1,15 @@
+require 'bundler'
+Bundler::GemHelper.install_tasks
+
+require 'simplecov'
+SimpleCov.start do
+ add_group "Lib", 'lib'
+end
+
+require 'rspec/core'
+require 'rspec/core/rake_task'
+RSpec::Core::RakeTask.new(:spec) do |spec|
+ spec.pattern = FileList['spec/**/*_spec.rb']
+end
+
+task :default => :spec
49 Readme.md
@@ -0,0 +1,49 @@
+# brainfuck
+
+Just another Brainfuck interpreter in Ruby!
+(If you don't know what Brainfuck is, you definitely
+[should][http://en.wikipedia.org/wiki/Brainfuck]).
@oriolgual
oriolgual added a line comment Dec 2, 2010

Learn Markdown please

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
+This interpreter works with MRI 1.8.7, 1.9.2 and JRuby 1.5.5.
+
+## Known caveats (yes, before anything)
+
+Ok, I admit it. Nested loops don't work in an extremely reliable manner, so to
+speak. I suggest you NOT TO USE THIS IN PRODUCTION APPS. Yes, forget about
+using this interpreter for that high-security online payment system you wrote in Brainfuck.
+
+Oh and why are nested loops tricky with this interpreter? I was tired and Civilization 5
+was installed in my laptop and... You know, forks and pull requests are always welcome! :)
+
+## Installation and usage
+
+You just `gem install brainfuck`!
+
+And then: `brainfuck my_file.bf`
+
+You can also require the gem and use inline brainfuck in your ruby scripts like this:
+
+ require 'brainfuck'
+
+ interpreter = Brainfuck.new
+ interpreter.compile "+++>+++<---"
+
+ interpreter.cells
+ # => [0, 3]
+
+It's very basic, and needs *a lot* of refactoring, but for now... there you go! ;)
+
+## Note on Patches/Pull Requests
+
+* Fork the project.
+* Make your feature addition or bug fix.
+* Add specs for it. This is important so I don't break it in a
+ future version unintentionally.
+* Commit, do not mess with rakefile, version, or history.
+ If you want to have your own version, that is fine but bump version
+ in a commit by itself I can ignore when I pull.
+* Send me a pull request. Bonus points for topic branches.
+
+## Copyright
+
+Copyright (c) 2010 Josep M. Bach. See LICENSE for details.
3 bin/brainfuck
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require 'brainfuck'
+Brainfuck::Interpreter.new.compile File.read(ARGV.first)
26 brainfuck.gemspec
@@ -0,0 +1,26 @@
+# -*- encoding: utf-8 -*-
+$:.push File.expand_path("../lib", __FILE__)
+require "brainfuck/version"
+
+Gem::Specification.new do |s|
+ s.name = "brainfuck"
+ s.version = Brainfuck::VERSION
+ s.platform = Gem::Platform::RUBY
+ s.authors = ["Josep M. Bach"]
+ s.email = ["josep.m.bach@gmail.com"]
+ s.homepage = "http://github.com/txus/brainfuck"
+ s.summary = %q{Another Brainfuck interpreter in Ruby}
+ s.description = %q{Another Brainfuck interpreter in Ruby}
+
+ s.rubyforge_project = "brainfuck"
+
+ s.add_runtime_dependency "highline"
+ s.add_development_dependency "rspec"
+ s.add_development_dependency "bundler"
+ s.add_development_dependency "simplecov"
+
+ s.files = `git ls-files`.split("\n")
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
+ s.require_paths = ["lib"]
+end
21 examples/hello_world.bf
@@ -0,0 +1,21 @@
++++++ +++++ initialize counter (cell #0) to 10
+[ use loop to set the next four cells to 70/100/30/10
+ > +++++ ++ add 7 to cell #1
+ > +++++ +++++ add 10 to cell #2
+ > +++ add 3 to cell #3
+ > + add 1 to cell #4
+ <<<< - decrement counter (cell #0)
+]
+> ++ . print 'H'
+> + . print 'e'
++++++ ++ . print 'l'
+. print 'l'
++++ . print 'o'
+> ++ . print ' '
+<< +++++ +++++ +++++ . print 'W'
+> . print 'o'
++++ . print 'r'
+----- - . print 'l'
+----- --- . print 'd'
+> + . print '!'
+> . print '\n'
102 lib/brainfuck.rb
@@ -0,0 +1,102 @@
+require 'highline/system_extensions'
+
+module Brainfuck
+ class Interpreter
+ include HighLine::SystemExtensions
+
+ INSTRUCTIONS = %w{> < + - [ ] . ,}
+
+ attr_accessor :cells
+ attr_reader :pointer
+ attr_reader :code
+
+ def initialize
+ @cells = [0]
+ @pointer = 0
+ @code = nil
+ end
+
+ def current
+ @cells[pointer] ||= 0
+ end
+ def current=(value)
+ @cells[pointer] = (value % 255) rescue 0
+ end
+
+ def forward
+ @pointer += 1
+ initialize_cell_if_nil
+ end
+ def backward
+ @pointer -= 1
+ ensure_pointer_is_above_zero
+ initialize_cell_if_nil
+ end
+ def increase
+ @cells[pointer] = (@cells[pointer] + 1) % 255
+ end
+ def decrease
+ @cells[pointer] = (@cells[pointer] - 1) % 255
+ end
+
+ def compile(code)
+ @code = clean(code)
+ run
+ end
+
+ def run(from = nil, to = nil)
+ index = 0
+ context = 0
+
+ c = code.dup
+
+ if from && to
+ context = from
+ c = c.slice(from...to)
+ end
+
+ c.each_char do |char|
+ index += 1
+ case char
+ when '>'
+ forward
+ when '<'
+ backward
+ when '+'
+ increase
+ when '-'
+ decrease
+ when '.'
+ stdout.print current.chr
+ when ','
+ self.current = get_character
+ when '['
+ @start = index
+ when ']'
+ @end = index unless @end && @start < @end
+ run(@start, @end) unless current == 0
+ @start = context || nil
+ end
+ end
+ end
+
+ private
+
+ def clean(code)
+ code.chars.select {|c| INSTRUCTIONS.include? c }.join
+ end
+
+ def stdout
+ $stdout
+ end
+
+ def initialize_cell_if_nil
+ @cells[pointer] ||= 0
+ end
+
+ def ensure_pointer_is_above_zero
+ raise "Tried to access cell #{@pointer}." if @pointer < 0
+ end
+
+ end
+end
3 lib/brainfuck/version.rb
@@ -0,0 +1,3 @@
+module Brainfuck
+ VERSION = "0.0.1"
+end
107 spec/acceptance_spec.rb
@@ -0,0 +1,107 @@
+require 'spec_helper'
+
+module Brainfuck
+ describe Interpreter, "acceptance specs" do
+
+ describe "without loops nor user input" do
+ let(:code) do
+ <<-EOS
+ ++++>++++---.<--.
+ EOS
+ end
+ it "sets two cells to 2 and 1" do
+ subject.should_receive(:code).and_return code
+ subject.compile(code)
+ subject.cells.should == [2,1]
+ end
+ it "prints 2 and 1" do
+ output = double('output')
+ subject.should_receive(:stdout).twice.and_return(output)
+ output.should_receive(:print).with(1.chr).ordered
+ output.should_receive(:print).with(2.chr).ordered
+
+ subject.compile(code)
+ end
+ end
+
+ describe "with user input" do
+ let(:code) do
+ <<-EOS
+ ,++++
+ EOS
+ end
+ it "sets the first cell to a + 4" do
+ input = double('input')
+ subject.should_receive(:get_character).once.and_return 97
+
+ subject.compile(code)
+ subject.current.should == 101
+ end
+ end
+
+ describe "with loops" do
+ let(:code) do
+ <<-EOS
+ ++++[-]+-+
+ EOS
+ end
+ it "runs the loop 4 times" do
+ subject.compile(code)
+ subject.current.should == 1
+ end
+ end
+
+ describe "cell hopping examples" do
+
+ it "transfers the content from one cell to another" do
+ subject.cells = [10]
+ subject.compile("[>+<-]")
+ subject.cells.should == [0,10]
+ end
+
+ it "transfers the content from one cell to the third" do
+ subject.cells = [10]
+ subject.compile("[>+<-]>[>+<-]")
+ subject.cells.should == [0,0,10]
+ end
+
+ it "transfers the content from one cell to the third and back to the second" do
+ subject.cells = [10]
+ subject.compile("[>+<-]>[>+<-]>[<+>-]")
+ subject.cells.should == [0,10,0]
+ end
+
+ end
+
+ describe "hello world" do
+
+ it "displays hello world" do
+ subject.compile <<-EOS
+ +++++ +++++
+ [
+ > +++++ ++
+ > +++++ +++++
+ > +++
+ > +
+ <<<< -
+ ]
+ > ++ .
+ > + .
+ +++++ ++ .
+ .
+ +++ .
+ > ++ .
+ << +++++ +++++ +++++ .
+ > .
+ +++ .
+ ----- - .
+ ----- --- .
+ > + .
+ > .
+ EOS
+
+ end
+ end
+
+ end
+end
65 spec/interpreter_spec.rb
@@ -0,0 +1,65 @@
+require 'spec_helper'
+
+module Brainfuck
+ describe Interpreter do
+ subject { Interpreter.new }
+
+ it "initializes with an array of cells with a single 0 element" do
+ subject.cells.should == [0]
+ end
+
+ describe "#clean" do
+ it "cleans the code from comments and whitespace" do
+ subject.send(:clean, " < > h - + ..,hb [lk]f").should == '<>-+..,[]'
+ end
+ end
+
+ describe "#compile" do
+ it "cleans the code first and runs it second" do
+ code = double('code')
+ subject.should_receive(:clean).once.with(code).ordered.and_return("")
+ subject.should_receive(:run).once
+
+ subject.compile(code)
+ end
+ end
+
+ describe "#run" do
+ it "converts > into forward" do
+ subject.stub(:code).and_return ">"
+ subject.should_receive(:forward).once
+ subject.run
+ end
+ it "converts < into backward" do
+ subject.stub(:code).and_return "<"
+ subject.should_receive(:backward).once
+ subject.run
+ end
+ it "converts + into increase" do
+ subject.stub(:code).and_return "+"
+ subject.should_receive(:increase).once
+ subject.run
+ end
+ it "converts - into decrease" do
+ subject.stub(:code).and_return "-"
+ subject.should_receive(:decrease).once
+ subject.run
+ end
+ it "converts , into get_character" do
+ subject.stub(:code).and_return ","
+ subject.should_receive(:get_character).once
+ subject.run
+ end
+ it "converts . into stdout.print" do
+ subject.stub(:code).and_return "."
+ output = double('output')
+ subject.should_receive(:stdout).once.and_return(output)
+ subject.should_receive(:current).once.and_return(117)
+ output.should_receive(:print).once
+
+ subject.run
+ end
+ end
+
+ end
+end
18 spec/spec_helper.rb
@@ -0,0 +1,18 @@
+require 'bundler'
+require 'simplecov'
+SimpleCov.start do
+ add_group "Lib", "lib"
+end
+
+begin
+ Bundler.setup(:default, :development)
+rescue Bundler::BundlerError => e
+ $stderr.puts e.message
+ $stderr.puts "Run `bundle install` to install missing gems"
+ exit e.status_code
+end
+$LOAD_PATH.unshift(File.dirname(__FILE__))
+$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
+
+require 'brainfuck'
+require 'rspec'

0 comments on commit 189f6ff

Please sign in to comment.