brainfuck 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -2,3 +2,5 @@ pkg/*
2
2
  *.gem
3
3
  .bundle
4
4
  coverage
5
+ *.rbc
6
+ *.bfc
@@ -1,29 +1,17 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- brainfuck (0.1.0)
5
- highline
4
+ brainfuck (0.2.0)
6
5
  parslet
7
6
 
8
7
  GEM
9
8
  remote: http://rubygems.org/
10
9
  specs:
11
- blankslate (2.1.2.3)
12
- diff-lcs (1.1.2)
13
- highline (1.6.1)
14
- parslet (1.0.0)
15
- blankslate (~> 2.1.2.3)
16
- rspec (2.2.0)
17
- rspec-core (~> 2.2)
18
- rspec-expectations (~> 2.2)
19
- rspec-mocks (~> 2.2)
20
- rspec-core (2.2.1)
21
- rspec-expectations (2.2.0)
22
- diff-lcs (~> 1.1.2)
23
- rspec-mocks (2.2.0)
24
- simplecov (0.3.7)
25
- simplecov-html (>= 0.3.7)
26
- simplecov-html (0.3.9)
10
+ blankslate (2.1.2.4)
11
+ minitest (2.0.2)
12
+ mocha (0.9.12)
13
+ parslet (1.2.0)
14
+ blankslate (~> 2.0)
27
15
 
28
16
  PLATFORMS
29
17
  java
@@ -31,8 +19,5 @@ PLATFORMS
31
19
 
32
20
  DEPENDENCIES
33
21
  brainfuck!
34
- bundler
35
- highline
36
- parslet
37
- rspec
38
- simplecov
22
+ minitest
23
+ mocha
data/Rakefile CHANGED
@@ -1,17 +1,19 @@
1
1
  require 'bundler'
2
2
  Bundler::GemHelper.install_tasks
3
3
 
4
- require 'simplecov'
5
- SimpleCov.start do
6
- add_group "Lib", 'lib'
4
+ require 'rake/testtask'
5
+ Rake::TestTask.new do |t|
6
+ t.libs << "test"
7
+ t.test_files = FileList['test/**/*_test.rb'] - FileList['test/acceptance/**/*_test.rb']
8
+ t.verbose = true
7
9
  end
8
10
 
9
- require 'rspec/core'
10
- require 'rspec/core/rake_task'
11
- RSpec::Core::RakeTask.new(:spec) do |spec|
12
- spec.pattern = FileList['spec/**/*_spec.rb']
11
+ require 'rake/testtask'
12
+ desc 'Run acceptance tests'
13
+ Rake::TestTask.new :acceptance do |t|
14
+ t.libs << "test"
15
+ t.test_files = FileList['test/acceptance/**/*_test.rb']
16
+ t.verbose = true
13
17
  end
14
- task
15
18
 
16
- task :default => :spec
17
- task :test => [:spec]
19
+ task :default => [:test, :acceptance]
data/Readme.md CHANGED
@@ -1,31 +1,37 @@
1
1
  # brainfuck
2
2
 
3
- Just another Brainfuck interpreter in Ruby!
3
+ An implementation of Brainfuck on the [Rubinius](http://rubini.us) VM.
4
+
4
5
  (If you don't know what Brainfuck is, you definitely
5
6
  [should](http://en.wikipedia.org/wiki/Brainfuck)).
6
7
 
7
- This interpreter works with MRI 1.8.7, 1.9.2 and JRuby 1.5.5.
8
-
9
- ## UPDATE: Known caveats solved since 0.1.0!
10
-
11
- Thanks to a complete rewrite using Kaspar Schiess' `parslet` (which you should
12
- definitely [check it out](http://github.com/kschiess/parslet)) nested loops
13
- work flawlessly. So yes, you can now run that high-security online payment
14
- system you wrote in Brainfuck :)
8
+ Obviously... needs Rubinius!
15
9
 
16
10
  ## Installation and usage
17
11
 
18
12
  You just `gem install brainfuck` (or `gem 'brainfuck'` in your Gemfile)!
19
13
 
20
- And then: `brainfuck my_file.bf`
14
+ And then:
15
+
16
+ $ brainfuck my_file.bf
17
+
18
+ Or if you just want to generate the compiled bytecode in `my_file.bfc`:
19
+
20
+ $ brainfuck -C my_file.bf
21
21
 
22
22
  You can also require the gem and use inline brainfuck in your ruby scripts like this:
23
23
 
24
24
  require 'brainfuck'
25
25
 
26
- Brainfuck.run "+++>+++<---"
26
+ # Brainfuck needs an object binding to do its stuff.
27
+ bnd = Object.new
28
+ def bnd.get; binding; end
29
+ bnd = bnd.get
30
+
31
+ Brainfuck::CodeLoader.execute_code "+++>+++<---", bnd, nil
27
32
  # => [0, 3]
28
33
 
34
+
29
35
  ## Note on Patches/Pull Requests
30
36
 
31
37
  * Fork the project.
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'brainfuck'
3
- Brainfuck.run File.read(ARGV.first)
3
+ Brainfuck::Main.new.main ARGV.first
@@ -9,16 +9,15 @@ Gem::Specification.new do |s|
9
9
  s.authors = ["Josep M. Bach"]
10
10
  s.email = ["josep.m.bach@gmail.com"]
11
11
  s.homepage = "http://github.com/txus/brainfuck"
12
- s.summary = %q{Another Brainfuck interpreter in Ruby}
13
- s.description = %q{Another Brainfuck interpreter in Ruby}
12
+ s.summary = %q{An implementation of Brainfuck on the Rubinius VM.}
13
+ s.description = %q{An implementation of Brainfuck on the Rubinius VM.}
14
14
 
15
15
  s.rubyforge_project = "brainfuck"
16
16
 
17
- s.add_runtime_dependency "highline"
18
17
  s.add_runtime_dependency "parslet"
19
- s.add_development_dependency "rspec"
20
- s.add_development_dependency "bundler"
21
- s.add_development_dependency "simplecov"
18
+
19
+ s.add_development_dependency "minitest"
20
+ s.add_development_dependency "mocha"
22
21
 
23
22
  s.files = `git ls-files`.split("\n")
24
23
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -1,22 +1,9 @@
1
1
  require 'parslet'
2
- require 'highline/system_extensions'
3
2
 
4
- require 'brainfuck/stack'
5
3
  require 'brainfuck/ast'
6
-
4
+ require 'brainfuck/lexer'
7
5
  require 'brainfuck/parser'
8
- require 'brainfuck/interpreter'
9
-
10
- module Brainfuck
11
- class << self
12
- def run code
13
- Interpreter.stack.clear
14
-
15
- code = Parser.clean code
16
- parsed = Parser.new.parse code
17
- ast = Interpreter.new.apply parsed
18
- ast.each(&:eval)
19
- Interpreter.stack.to_a
20
- end
21
- end
22
- end
6
+ require 'brainfuck/stages'
7
+ require 'brainfuck/compiler'
8
+ require 'brainfuck/main'
9
+ require 'brainfuck/code_loader'
@@ -1,30 +1,138 @@
1
1
  module Brainfuck
2
2
  module AST
3
- class FwdNode < Struct.new(:stack)
4
- def eval; stack.fwd ; end
3
+ class FwdNode
4
+ def bytecode(g)
5
+ g.push_local 1
6
+ g.meta_push_1
7
+ g.meta_send_op_plus 0
8
+ g.set_local 1
9
+
10
+ # Check the contents of the new cell
11
+ g.push_local 0
12
+ g.swap_stack
13
+ g.send :[], 1, false
14
+
15
+ fin = g.new_label
16
+ g.git fin
17
+ g.pop
18
+
19
+ # If the cell is nil set it to 0
20
+ g.push_local 0
21
+ g.push_local 1
22
+ g.meta_push_0
23
+ g.send :[]=, 2, false
24
+
25
+ # Otherwise, do nothing
26
+ fin.set!
27
+ end
5
28
  end
6
- class BwdNode < Struct.new(:stack)
7
- def eval; stack.bwd ; end
29
+ class BwdNode
30
+ def bytecode(g)
31
+ g.push_local 1
32
+ g.meta_push_1
33
+ g.meta_send_op_minus 0
34
+ g.set_local 1
35
+
36
+ # Check the contents of the new cell
37
+ g.push_local 0
38
+ g.swap_stack
39
+ g.send :[], 1, false
40
+
41
+ fin = g.new_label
42
+ g.git fin
43
+ g.pop
44
+
45
+ # If the cell is nil set it to 0
46
+ g.push_local 0
47
+ g.push_local 1
48
+ g.meta_push_0
49
+ g.send :[]=, 2, false
50
+
51
+ # Otherwise, do nothing
52
+ fin.set!
53
+ end
8
54
  end
9
55
 
10
- class IncNode < Struct.new(:stack)
11
- def eval; stack.inc ; end
56
+ class IncNode
57
+ def bytecode(g)
58
+ g.push_local 0
59
+ g.push_local 1
60
+
61
+ g.dup_many(2)
62
+ g.send :[], 1, false
63
+ g.meta_push_1
64
+ g.meta_send_op_plus 0
65
+
66
+ g.send :[]=, 2, false
67
+ g.pop
68
+ end
12
69
  end
13
- class DecNode < Struct.new(:stack)
14
- def eval; stack.dec ; end
70
+ class DecNode
71
+ def bytecode(g)
72
+ g.push_local 0
73
+ g.push_local 1
74
+
75
+ g.dup_many(2)
76
+ g.send :[], 1, false
77
+ g.meta_push_1
78
+ g.meta_send_op_minus 0
79
+
80
+ g.send :[]=, 2, false
81
+ g.pop
82
+ end
15
83
  end
16
- class PutsNode < Struct.new(:stack)
17
- def eval; stack.puts ; end
84
+ class PutsNode
85
+ def bytecode(g)
86
+ g.push_const :STDOUT
87
+
88
+ g.push_local 0
89
+ g.push_local 1
90
+ g.send :[], 1, false
91
+
92
+ g.send :putc, 1, true
93
+ end
94
+ end
95
+ class GetsNode
96
+ def bytecode(g)
97
+ g.push :self
98
+ g.push_literal "stty raw -echo"
99
+ g.send :system, 1, true
100
+ g.pop
101
+
102
+ g.push_local 0
103
+ g.push_local 1
104
+
105
+ g.push_const :STDIN
106
+ g.send :getc, 0, false
107
+
108
+ g.send :[]=, 2, false
109
+
110
+ g.push :self
111
+ g.push_literal "stty -raw echo"
112
+ g.send :system, 1, true
113
+ g.pop
114
+ end
18
115
  end
19
- class GetsNode < Struct.new(:stack)
20
- def eval; stack.gets ; end
116
+ class IterationNode < Struct.new(:exp)
117
+ def bytecode(g)
118
+ repeat = g.new_label
119
+ repeat.set!
120
+
121
+ exp.bytecode(g)
122
+
123
+ g.push_local 0
124
+ g.push_local 1
125
+ g.send :[], 1, true
126
+ g.meta_push_0
127
+ g.meta_send_op_equal 0
128
+
129
+ g.gif repeat
130
+ end
21
131
  end
22
- class IterationNode < Struct.new(:stack, :exp)
23
- def eval
24
- until stack.current == 0
25
- exp.each do |node|
26
- node.eval
27
- end
132
+ class Script < Struct.new(:exp)
133
+ def bytecode(g)
134
+ exp.each do |e|
135
+ e.bytecode(g)
28
136
  end
29
137
  end
30
138
  end
@@ -0,0 +1,31 @@
1
+ module Brainfuck
2
+ class CodeLoader < Rubinius::CodeLoader
3
+
4
+ def self.execute_code(code, binding, from_module, print = Compiler::Print.new)
5
+ cm = Compiler.compile_for_eval(code, binding.variables,
6
+ "(eval)", 1, print)
7
+ cm.scope = binding.static_scope.dup
8
+ cm.name = :__eval__
9
+
10
+ script = Rubinius::CompiledMethod::Script.new(cm, "(eval)", true)
11
+ script.eval_binding = binding
12
+ script.eval_source = code
13
+
14
+ cm.scope.script = script
15
+
16
+ be = Rubinius::BlockEnvironment.new
17
+ be.under_context(binding.variables, cm)
18
+ be.from_eval!
19
+ be.call
20
+ end
21
+
22
+ # Takes a .bf file name, compiles it if needed and executes it.
23
+ def self.execute_file(name, compile_to = nil, print = Compiler::Print.new)
24
+ cm = Compiler.compile_if_needed(name, compile_to, print)
25
+ ss = ::Rubinius::StaticScope.new Object
26
+ code = Object.new
27
+ ::Rubinius.attach_method(:__run__, cm, ss, code)
28
+ code.__run__
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,81 @@
1
+ module Brainfuck
2
+ class Compiler < Rubinius::Compiler
3
+
4
+ def self.compiled_filename(filename)
5
+ if filename =~ /.bf$/
6
+ filename + "c"
7
+ else
8
+ filename + ".compiled.bfc"
9
+ end
10
+ end
11
+
12
+ def self.always_recompile=(flag)
13
+ @always_recompile = flag
14
+ end
15
+
16
+ def self.compile_if_needed(file, output = nil, print = Print.new)
17
+ compiled = output || compiled_filename(file)
18
+ needed = @always_recompile || !File.exists?(compiled) ||
19
+ File.stat(compiled).mtime < File.stat(file).mtime
20
+ if needed
21
+ compile_file(file, compiled, print)
22
+ else
23
+ Brainfuck::CodeLoader.new(compiled).load_compiled_file(compiled, 0)
24
+ end
25
+ end
26
+
27
+
28
+ def self.compile_file(file, output = nil, print = Print.new)
29
+ compiler = new :brainfuck_file, :compiled_file
30
+ parser = compiler.parser
31
+
32
+ parser.input file
33
+
34
+ compiler.generator = Rubinius::Generator.new
35
+ compiler.writer.name = output || compiled_filename(file)
36
+
37
+ parser.print = print
38
+ compiler.packager.print.bytecode = true if print.asm?
39
+
40
+ begin
41
+ compiler.run
42
+
43
+ rescue Exception => e
44
+ compiler_error "Error trying to compile brainfuck: #{file}", e
45
+ end
46
+ end
47
+
48
+ def self.compile_for_eval(code, variable_scope, file = "(eval)", line = 0, print = Print.new)
49
+ compiler = new :brainfuck_code, :compiled_method
50
+ parser = compiler.parser
51
+
52
+ parser.input code, file, line
53
+ compiler.generator.root = Rubinius::AST::EvalExpression
54
+ compiler.generator.variable_scope = variable_scope
55
+
56
+ parser.print = print
57
+ compiler.packager.print.bytecode = true if print.asm?
58
+
59
+ begin
60
+ compiler.run
61
+ rescue Exception => e
62
+ compiler_error "Error trying to compile brainfuck: #{file}", e
63
+ end
64
+ end
65
+
66
+ class Print < Struct.new(:sexp, :ast, :asm)
67
+ def sexp?
68
+ @sexp
69
+ end
70
+
71
+ def ast?
72
+ @ast
73
+ end
74
+
75
+ def asm?
76
+ @asm
77
+ end
78
+ end
79
+
80
+ end
81
+ end