├── add.bf ├── mul.bf ├── hello_world.bf ├── license.txt ├── interpreter.rb ├── lexer.rb ├── ruby_code_generator.rb ├── parser.rb └── hello_world.rb /add.bf: -------------------------------------------------------------------------------- 1 | ,>++++++[<-------->-],[<+>-]<. -------------------------------------------------------------------------------- /mul.bf: -------------------------------------------------------------------------------- 1 | ,>,>++++++++[<------<------>>-]<<[>[>+>+<<-]>>[<<+>>-]<<<-]>>>++++++[<++++++++>-],<.>. -------------------------------------------------------------------------------- /hello_world.bf: -------------------------------------------------------------------------------- 1 | ++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>. -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2008 Ninh Bui (Phusion) 5 | Everyone is permitted to copy and distribute verbatim or modified 6 | copies of this license document, and changing it is allowed as long 7 | as the name is changed. 8 | 9 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 10 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 11 | 12 | 0. You just DO WHAT THE FUCK YOU WANT TO. -------------------------------------------------------------------------------- /interpreter.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'lexer' 3 | require 'parser' 4 | 5 | module Brainfuck 6 | class Interpreter 7 | def initialize(file_path) 8 | @file_path = file_path 9 | @lexer = Lexer.new(file_path) 10 | @parser = Parser.new(@lexer) 11 | @addresses = [0] * 3000 12 | @ptr = 0 13 | end 14 | 15 | def interpret 16 | @parser.parse 17 | children = @parser.ast.children 18 | children.each { |child| child.accept(self) } 19 | end 20 | 21 | def visit_incr_ptr(ast) 22 | #puts 'debug: >' 23 | @ptr += 1 24 | end 25 | 26 | def visit_decr_ptr(ast) 27 | #puts 'debug: <' 28 | @ptr -= 1 29 | end 30 | 31 | def visit_incr_ptd_byte(ast) 32 | #puts 'debug: +' 33 | @addresses[@ptr] += 1 34 | end 35 | 36 | def visit_decr_ptd_byte(ast) 37 | #puts 'debug: -' 38 | @addresses[@ptr] -= 1 39 | end 40 | 41 | def visit_output_ptd_byte(ast) 42 | #puts 'debug: .' 43 | print @addresses[@ptr].chr 44 | end 45 | 46 | def visit_input_ptd_byte(ast) 47 | #puts 'debug: ,' 48 | @addresses[@ptr] = $stdin.getc 49 | end 50 | 51 | def visit_eof(ast) 52 | puts 53 | end 54 | 55 | def visit_while(ast) 56 | #print '[' 57 | while(@addresses[@ptr] != 0) 58 | children = ast.children 59 | children.each { |child| child.accept(self) } 60 | end 61 | #print ']' 62 | end 63 | end 64 | end 65 | 66 | file_path = ARGV[0] 67 | interpreter = Brainfuck::Interpreter.new(file_path) 68 | interpreter.interpret -------------------------------------------------------------------------------- /lexer.rb: -------------------------------------------------------------------------------- 1 | module Brainfuck 2 | class Lexer 3 | attr_reader :column 4 | attr_reader :line 5 | attr_reader :source_stream 6 | 7 | def initialize(file_path) 8 | @source_stream = File.new(file_path, 'r') 9 | @column = 1 10 | @line = 1 11 | end 12 | 13 | def tokenize(c) 14 | case c 15 | when '<' 16 | return :decr_ptr 17 | when '>' 18 | return :incr_ptr 19 | when '+' 20 | return :incr_ptd_byte 21 | when '-' 22 | return :decr_ptd_byte 23 | when '[' 24 | return :begin_while 25 | when ']' 26 | return :end_while 27 | when '.' 28 | return :output_ptd_byte 29 | when ',' 30 | return :input_ptd_byte 31 | when nil 32 | return :eof 33 | else 34 | raise "Unknown token at line #{@line} and column #{@column}" 35 | end 36 | end 37 | 38 | def peek 39 | next_char = @source_stream.getc 40 | if next_char != nil 41 | @source_stream.ungetc(next_char) 42 | next_char = next_char.chr 43 | end 44 | return tokenize(next_char) 45 | end 46 | 47 | def recognize! 48 | consume 49 | return tokenize(@current_char) 50 | end 51 | 52 | protected 53 | def consume 54 | @current_char = @source_stream.getc 55 | @current_char = @current_char.chr if @current_char != nil 56 | 57 | @column += 1 58 | 59 | if @current_char == '\n' 60 | @column = 1 61 | @line += 1 62 | end 63 | 64 | # This line should be here because of possible tail recursion optimization 65 | consume if @current_char =~ /\r/ 66 | end 67 | end 68 | end -------------------------------------------------------------------------------- /ruby_code_generator.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'lexer' 3 | require 'parser' 4 | 5 | module Brainfuck 6 | class RubyCodeGenerator 7 | def initialize(file_path) 8 | @file_path = file_path 9 | @lexer = Lexer.new(file_path) 10 | @parser = Parser.new(@lexer) 11 | end 12 | 13 | def emit 14 | puts "addresses = [0] * 3000" 15 | puts "ptr = 0" 16 | @parser.parse 17 | children = @parser.ast.children 18 | children.each { |child| child.accept(self) } 19 | end 20 | 21 | def visit_incr_ptr(ast) 22 | #puts 'debug: >' 23 | puts "ptr += 1" 24 | end 25 | 26 | def visit_decr_ptr(ast) 27 | #puts 'debug: <' 28 | puts "ptr -= 1" 29 | end 30 | 31 | def visit_incr_ptd_byte(ast) 32 | #puts 'debug: +' 33 | puts "addresses[ptr] += 1" 34 | end 35 | 36 | def visit_decr_ptd_byte(ast) 37 | #puts 'debug: -' 38 | puts "addresses[ptr] -= 1" 39 | end 40 | 41 | def visit_output_ptd_byte(ast) 42 | #puts 'debug: .' 43 | puts "print addresses[ptr].chr" 44 | end 45 | 46 | def visit_input_ptd_byte(ast) 47 | #puts 'debug: ,' 48 | puts "addresses[ptr] = $stdin.getc" 49 | end 50 | 51 | def visit_eof(ast) 52 | puts "puts" 53 | end 54 | 55 | def visit_while(ast) 56 | #print '[' 57 | puts "while(addresses[ptr] != 0)" 58 | children = ast.children 59 | children.each { |child| child.accept(self) } 60 | puts "end" 61 | #print ']' 62 | end 63 | end 64 | end 65 | 66 | file_path = ARGV[0] 67 | generator = Brainfuck::RubyCodeGenerator.new(file_path) 68 | generator.emit -------------------------------------------------------------------------------- /parser.rb: -------------------------------------------------------------------------------- 1 | require 'lexer' 2 | 3 | module Brainfuck 4 | class ParseTreeNode 5 | attr_accessor :token 6 | attr_accessor :parent 7 | attr_accessor :children 8 | 9 | def initialize(token = :start, parent = nil, children = []) 10 | @token = token 11 | @parent = parent 12 | @children = children 13 | end 14 | 15 | def accept(visitor) 16 | visitor.send("visit_#{@token}", self) 17 | end 18 | end 19 | 20 | class Parser 21 | attr_accessor :ast 22 | 23 | def initialize(lexer) 24 | @ast = ParseTreeNode.new 25 | @lexer = lexer 26 | end 27 | 28 | def parse 29 | # Start parsing start symbol 30 | parse_statements(@ast) 31 | end 32 | 33 | def parse_statements(parent) 34 | begin 35 | token = @lexer.peek 36 | if token == :begin_while 37 | parse_while(parent) 38 | elsif token == :end_while 39 | return 40 | else 41 | # Parse terminal statements 42 | parse_terminal_statement(parent) 43 | end 44 | end while(token != :eof) 45 | end 46 | 47 | def parse_terminal_statement(parent) 48 | token = @lexer.recognize! 49 | #puts token.to_s 50 | parent.children << ParseTreeNode.new(token, parent, nil) 51 | end 52 | 53 | def parse_while(parent) 54 | expect(:begin_while) 55 | while_node = ParseTreeNode.new(:while, parent) 56 | parse_statements(while_node) 57 | parent.children << while_node 58 | expect(:end_while) 59 | end 60 | 61 | def expect(sym) 62 | token = @lexer.recognize! 63 | raise "Unexpected '#{token}' at line #{@lexer.line} on column #{@lexer.column}" if token != sym 64 | end 65 | 66 | end 67 | end -------------------------------------------------------------------------------- /hello_world.rb: -------------------------------------------------------------------------------- 1 | addresses = [0] * 3000 2 | ptr = 0 3 | addresses[ptr] += 1 4 | addresses[ptr] += 1 5 | addresses[ptr] += 1 6 | addresses[ptr] += 1 7 | addresses[ptr] += 1 8 | addresses[ptr] += 1 9 | addresses[ptr] += 1 10 | addresses[ptr] += 1 11 | addresses[ptr] += 1 12 | addresses[ptr] += 1 13 | while(addresses[ptr] != 0) 14 | ptr += 1 15 | addresses[ptr] += 1 16 | addresses[ptr] += 1 17 | addresses[ptr] += 1 18 | addresses[ptr] += 1 19 | addresses[ptr] += 1 20 | addresses[ptr] += 1 21 | addresses[ptr] += 1 22 | ptr += 1 23 | addresses[ptr] += 1 24 | addresses[ptr] += 1 25 | addresses[ptr] += 1 26 | addresses[ptr] += 1 27 | addresses[ptr] += 1 28 | addresses[ptr] += 1 29 | addresses[ptr] += 1 30 | addresses[ptr] += 1 31 | addresses[ptr] += 1 32 | addresses[ptr] += 1 33 | ptr += 1 34 | addresses[ptr] += 1 35 | addresses[ptr] += 1 36 | addresses[ptr] += 1 37 | ptr += 1 38 | addresses[ptr] += 1 39 | ptr -= 1 40 | ptr -= 1 41 | ptr -= 1 42 | ptr -= 1 43 | addresses[ptr] -= 1 44 | end 45 | ptr += 1 46 | addresses[ptr] += 1 47 | addresses[ptr] += 1 48 | print addresses[ptr].chr 49 | ptr += 1 50 | addresses[ptr] += 1 51 | print addresses[ptr].chr 52 | addresses[ptr] += 1 53 | addresses[ptr] += 1 54 | addresses[ptr] += 1 55 | addresses[ptr] += 1 56 | addresses[ptr] += 1 57 | addresses[ptr] += 1 58 | addresses[ptr] += 1 59 | print addresses[ptr].chr 60 | print addresses[ptr].chr 61 | addresses[ptr] += 1 62 | addresses[ptr] += 1 63 | addresses[ptr] += 1 64 | print addresses[ptr].chr 65 | ptr += 1 66 | addresses[ptr] += 1 67 | addresses[ptr] += 1 68 | print addresses[ptr].chr 69 | ptr -= 1 70 | ptr -= 1 71 | addresses[ptr] += 1 72 | addresses[ptr] += 1 73 | addresses[ptr] += 1 74 | addresses[ptr] += 1 75 | addresses[ptr] += 1 76 | addresses[ptr] += 1 77 | addresses[ptr] += 1 78 | addresses[ptr] += 1 79 | addresses[ptr] += 1 80 | addresses[ptr] += 1 81 | addresses[ptr] += 1 82 | addresses[ptr] += 1 83 | addresses[ptr] += 1 84 | addresses[ptr] += 1 85 | addresses[ptr] += 1 86 | print addresses[ptr].chr 87 | ptr += 1 88 | print addresses[ptr].chr 89 | addresses[ptr] += 1 90 | addresses[ptr] += 1 91 | addresses[ptr] += 1 92 | print addresses[ptr].chr 93 | addresses[ptr] -= 1 94 | addresses[ptr] -= 1 95 | addresses[ptr] -= 1 96 | addresses[ptr] -= 1 97 | addresses[ptr] -= 1 98 | addresses[ptr] -= 1 99 | print addresses[ptr].chr 100 | addresses[ptr] -= 1 101 | addresses[ptr] -= 1 102 | addresses[ptr] -= 1 103 | addresses[ptr] -= 1 104 | addresses[ptr] -= 1 105 | addresses[ptr] -= 1 106 | addresses[ptr] -= 1 107 | addresses[ptr] -= 1 108 | print addresses[ptr].chr 109 | ptr += 1 110 | addresses[ptr] += 1 111 | print addresses[ptr].chr 112 | ptr += 1 113 | print addresses[ptr].chr 114 | puts 115 | --------------------------------------------------------------------------------