Skip to content

Commit

Permalink
Move input/output from instance variables to parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
Maumagnaguagno committed Jan 4, 2024
1 parent 0b02df1 commit c57b3cc
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 64 deletions.
27 changes: 12 additions & 15 deletions MindFreak.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module MindFreak
extend self

attr_reader :pointer
attr_accessor :input, :output, :debug
attr_writer :debug

HELP = "MindFreak filename.bf [mode=interpreter|bytecode|bytecode2|rb|c] [bounds=#{TAPE_DEFAULT_SIZE = 500}] [EOF=0|-1|any integer|unchanged]"

Expand All @@ -26,9 +26,6 @@ module MindFreak

MULTIPLY = ?*.ord

@input = STDIN
@output = STDOUT

#-----------------------------------------------
# Check
#-----------------------------------------------
Expand All @@ -45,7 +42,7 @@ def check(program)
# Run interpreter
#-----------------------------------------------

def run_interpreter(program, tape, eof = 0)
def run_interpreter(program, tape, eof = 0, input = STDIN, output = STDOUT)
program_size = program.size
program_counter = -1
@pointer = 0
Expand All @@ -61,9 +58,9 @@ def run_interpreter(program, tape, eof = 0)
when BACKWARD
@pointer -= 1
when WRITE
@output.putc(tape[@pointer])
output.putc(tape[@pointer])
when READ
tape[@pointer] = @input.getbyte || eof || next
tape[@pointer] = input.getbyte || eof || next
when JUMP
if tape[@pointer] == 0
control = 1
Expand All @@ -83,7 +80,7 @@ def run_interpreter(program, tape, eof = 0)
# Run bytecode
#-----------------------------------------------

def run_bytecode(program, tape, eof = 0)
def run_bytecode(program, tape, eof = 0, input = STDIN, output = STDOUT)
# Bytecode interpreter does not support optimizations
bytecode = bytecode(program)
program_size = bytecode.size
Expand All @@ -98,10 +95,10 @@ def run_bytecode(program, tape, eof = 0)
when FORWARD # Pointer
@pointer += arg
when WRITE # Write
arg > 1 ? @output.print(tape[@pointer].chr * arg) : @output.putc(tape[@pointer])
arg > 1 ? output.print(tape[@pointer].chr * arg) : output.putc(tape[@pointer])
when READ # Read
@input.read(arg - 1)
tape[@pointer] = @input.getbyte || eof || next
input.read(arg - 1)
tape[@pointer] = input.getbyte || eof || next
when JUMP # Jump if zero
program_counter = arg if tape[@pointer] == 0
when JUMPBACK # Return unless zero
Expand All @@ -115,7 +112,7 @@ def run_bytecode(program, tape, eof = 0)
# Run bytecode2
#-----------------------------------------------

def run_bytecode2(program, tape, eof = 0)
def run_bytecode2(program, tape, eof = 0, input = STDIN, output = STDOUT)
# Bytecode2 interpreter support optimizations
bytecode = optimize(bytecode(program), tape[0] == 0)
program_size = bytecode.size
Expand All @@ -135,10 +132,10 @@ def run_bytecode2(program, tape, eof = 0)
@pointer += arg
when WRITE # Write
c = tape[offset ? offset + @pointer : @pointer]
arg > 1 ? @output.print(c.chr * arg) : @output.putc(c)
arg > 1 ? output.print(c.chr * arg) : output.putc(c)
when READ # Read
@input.read(arg - 1)
tape[offset ? offset + @pointer : @pointer] = @input.getbyte || eof || next
input.read(arg - 1)
tape[offset ? offset + @pointer : @pointer] = input.getbyte || eof || next
when JUMP # Jump if zero
program_counter = arg if tape[@pointer] == 0
when JUMPBACK # Return unless zero
Expand Down
15 changes: 7 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,18 +89,17 @@ The main of this project is just an example of the API, all modes can be execute
## API
[**MindFreak**](MindFreak.rb) is a module with 4 attributes:
- ``attr_reader :pointer``, with the position of the current cell for interpreted execution modes, ``nil`` is the default.
- ``attr_accessor :input``, read external data from an object that responds to ``getbyte`` and ``read``, ``STDIN`` is the default.
- ``attr_accessor :output``, write external data to an object that responds to ``putc`` and ``print``, ``STDOUT`` is the default.
- ``attr_accessor :debug``, print warnings when assigned to anything but ``false`` or ``nil``, ``nil`` is the default.
- ``attr_writer :debug``, print warnings when assigned to anything but ``false`` or ``nil``, ``nil`` is the default.

The methods require a String containing the program and an Array or Hash to be used as tape.
The bytecode generated is an Array of Arrays and differ from the basic to the optimized version.
Input and output can be redirected from STDIN/STDOUT to objects that respond to getbyte/read and putc/print, respectively, such as a StringIO object.
- ``check(program)`` is used to sanitize the input program and check if brackets are balanced, modifies the program string, returns ``nil``.
- ``run_interpreter(program, tape, eof = 0)`` executes the slow interpreter, reading from input, writing to output while using the provided tape.
- ``run_bytecode(program, tape, eof = 0)`` executes the bytecode interpreter, reading from input, writing to output while using the provided tape.
- ``run_bytecode2(program, tape, eof = 0)`` executes the optimized bytecode interpreter, reading from input, writing to output while using the provided tape.
- ``to_ruby(program, tape = TAPE_DEFAULT_SIZE, eof = 0, input = 'STDIN', output = 'STDOUT')`` returns a string with a equivalent Ruby program. If tape is Array or Hash the string will not contain tape and pointer declaration so ``eval`` will use external variables, otherwise tape is interpreted as size.
- ``to_c(program, tape = TAPE_DEFAULT_SIZE, eof = 0, type = 'unsigned int')`` returns a string with a equivalent C program. The type contains the cell type being used. If no bounded tape is provided, tape is interpreted as size.
- ``run_interpreter(program, tape, eof = 0, input = STDIN, output = STDOUT)`` executes the slow interpreter, reading from input, writing to output while using the provided tape.
- ``run_bytecode(program, tape, eof = 0, input = STDIN, output = STDOUT)`` executes the bytecode interpreter, reading from input, writing to output while using the provided tape.
- ``run_bytecode2(program, tape, eof = 0, input = STDIN, output = STDOUT)`` executes the optimized bytecode interpreter, reading from input, writing to output while using the provided tape.
- ``to_ruby(program, tape = TAPE_DEFAULT_SIZE, eof = 0, input = 'STDIN', output = 'STDOUT')`` returns a String with a equivalent Ruby program. If tape is Array or Hash the string will not contain tape and pointer declaration so ``eval`` will use external variables, otherwise tape is interpreted as size.
- ``to_c(program, tape = TAPE_DEFAULT_SIZE, eof = 0, type = 'unsigned int')`` returns a String with an equivalent C program. The type contains the cell type being used. If no bounded tape is provided, tape is interpreted as size.
- ``bytecode(program)`` returns an Array with the bytecodes.
- ``optimize(bytecode, blank_tape = false)`` returns an Array with the optimized bytecodes, which can be further optimized if the tape is blank.

Expand Down
82 changes: 41 additions & 41 deletions tests/rorschach.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class Rorschach < Test::Unit::TestCase
HELLO = ',,,.,.,..,.>.'

def test_attributes
[:pointer, :input, :input=, :output, :output=, :debug, :debug=].each {|att| assert_respond_to(MindFreak, att)}
[:pointer, :debug=].each {|att| assert_respond_to(MindFreak, att)}
end

#-----------------------------------------------
Expand Down Expand Up @@ -72,34 +72,34 @@ def test_run_interpreter_io
# Using StringIO to simulate input/output
program = HELLO.dup
tape = [0, 33]
MindFreak.input = StringIO.new(' Helo')
MindFreak.output = StringIO.new
input = StringIO.new(' Helo')
output = StringIO.new
assert_nil(MindFreak.check(program))
MindFreak.run_interpreter(program, tape)
MindFreak.run_interpreter(program, tape, 0, input, output)
assert_equal([111, 33], tape)
assert_equal(1, MindFreak.pointer)
assert_equal(' Helo', MindFreak.input.string)
assert_equal('Hello!', MindFreak.output.string)
assert_equal(' Helo', input.string)
assert_equal('Hello!', output.string)
end

def test_run_interpreter_io_read_eof
# Consider any integer as EOF
program = ','
tape = [0]
MindFreak.input = StringIO.new
input = StringIO.new
assert_nil(MindFreak.check(program))
MindFreak.run_interpreter(program, tape)
MindFreak.run_interpreter(program, tape, 0, input)
assert_equal([0], tape)
assert_equal(0, MindFreak.pointer)
assert_equal('', MindFreak.input.string)
MindFreak.run_interpreter(program, tape, 255)
assert_equal('', input.string)
MindFreak.run_interpreter(program, tape, 255, input)
assert_equal([255], tape)
assert_equal(0, MindFreak.pointer)
assert_equal('', MindFreak.input.string)
MindFreak.run_interpreter(program, tape = [], nil)
assert_equal('', input.string)
MindFreak.run_interpreter(program, tape = [], nil, input)
assert_equal([], tape)
assert_equal(0, MindFreak.pointer)
assert_equal('', MindFreak.input.string)
assert_equal('', input.string)
end

#-----------------------------------------------
Expand Down Expand Up @@ -132,34 +132,34 @@ def test_run_bytecode_io
# Using StringIO to simulate input/output
program = HELLO.dup
tape = [0, 33]
MindFreak.input = StringIO.new(' Helo')
MindFreak.output = StringIO.new
input = StringIO.new(' Helo')
output = StringIO.new
assert_nil(MindFreak.check(program))
MindFreak.run_bytecode(program, tape)
MindFreak.run_bytecode(program, tape, 0, input, output)
assert_equal([111, 33], tape)
assert_equal(1, MindFreak.pointer)
assert_equal(' Helo', MindFreak.input.string)
assert_equal('Hello!', MindFreak.output.string)
assert_equal(' Helo', input.string)
assert_equal('Hello!', output.string)
end

def test_run_bytecode_io_read_eof
# Consider any integer as EOF
program = ','
tape = [0]
MindFreak.input = StringIO.new
input = StringIO.new
assert_nil(MindFreak.check(program))
MindFreak.run_bytecode(program, tape)
MindFreak.run_bytecode(program, tape, 0, input)
assert_equal([0], tape)
assert_equal(0, MindFreak.pointer)
assert_equal('', MindFreak.input.string)
MindFreak.run_bytecode(program, tape, 255)
assert_equal('', input.string)
MindFreak.run_bytecode(program, tape, 255, input)
assert_equal([255], tape)
assert_equal(0, MindFreak.pointer)
assert_equal('', MindFreak.input.string)
MindFreak.run_bytecode(program, tape = [], nil)
assert_equal('', input.string)
MindFreak.run_bytecode(program, tape = [], nil, input)
assert_equal([], tape)
assert_equal(0, MindFreak.pointer)
assert_equal('', MindFreak.input.string)
assert_equal('', input.string)
end

#-----------------------------------------------
Expand Down Expand Up @@ -192,34 +192,34 @@ def test_run_bytecode2_io
# Using StringIO to simulate input/output
program = HELLO.dup
tape = [0, 33]
MindFreak.input = StringIO.new(' Helo')
MindFreak.output = StringIO.new
input = StringIO.new(' Helo')
output = StringIO.new
assert_nil(MindFreak.check(program))
MindFreak.run_bytecode2(program, tape)
MindFreak.run_bytecode2(program, tape, 0, input, output)
assert_equal([111, 33], tape)
assert_equal(0, MindFreak.pointer)
assert_equal(' Helo', MindFreak.input.string)
assert_equal('Hello!', MindFreak.output.string)
assert_equal(' Helo', input.string)
assert_equal('Hello!', output.string)
end

def test_run_bytecode2_io_read_eof
# Consider any integer as EOF
program = ','
tape = [0]
MindFreak.input = StringIO.new
input = StringIO.new
assert_nil(MindFreak.check(program))
MindFreak.run_bytecode2(program, tape)
MindFreak.run_bytecode2(program, tape, 0, input)
assert_equal([0], tape)
assert_equal(0, MindFreak.pointer)
assert_equal('', MindFreak.input.string)
MindFreak.run_bytecode2(program, tape, 255)
assert_equal('', input.string)
MindFreak.run_bytecode2(program, tape, 255, input)
assert_equal([255], tape)
assert_equal(0, MindFreak.pointer)
assert_equal('', MindFreak.input.string)
MindFreak.run_bytecode2(program, tape = [], nil)
assert_equal('', input.string)
MindFreak.run_bytecode2(program, tape = [], nil, input)
assert_equal([], tape)
assert_equal(0, MindFreak.pointer)
assert_equal('', MindFreak.input.string)
assert_equal('', input.string)
end

#-----------------------------------------------
Expand Down Expand Up @@ -857,9 +857,9 @@ def test_bytecode_hello_world
MindFreak.optimize(MindFreak.bytecode(program))
)
# Using StringIO to simulate output
MindFreak.output = StringIO.new
MindFreak.run_interpreter(program, Array.new(10, 0))
assert_equal('Hello World!', MindFreak.output.string)
output = StringIO.new
MindFreak.run_interpreter(program, Array.new(10, 0), 0, nil, output)
assert_equal('Hello World!', output.string)
end

def test_bytecode_mandelbrot
Expand All @@ -873,7 +873,7 @@ def test_bytecode_mandelbrot
assert_equal(4115, bytecode.size)
assert_equal(2177, MindFreak.optimize(bytecode).size)
# Compare output
File.write(file_c, MindFreak.to_c(program, MindFreak::TAPE_DEFAULT_SIZE, -1))
File.write(file_c, MindFreak.to_c(program, MindFreak::TAPE_DEFAULT_SIZE))
['gcc', 'clang'].each {|cc| assert_equal(MANDELBROT, `./#{file_exe}`) if system("#{cc} #{file_c} -o #{file_exe} -O2 -s")}
ensure
File.delete(file_c) if File.exist?(file_c)
Expand Down

0 comments on commit c57b3cc

Please sign in to comment.