From c57b3cc87cb227f61b03cd2eabf13f3f7ef96402 Mon Sep 17 00:00:00 2001 From: Mau Magnaguagno Date: Thu, 4 Jan 2024 07:35:23 -0300 Subject: [PATCH] Move input/output from instance variables to parameters --- MindFreak.rb | 27 +++++++-------- README.md | 15 ++++----- tests/rorschach.rb | 82 +++++++++++++++++++++++----------------------- 3 files changed, 60 insertions(+), 64 deletions(-) diff --git a/MindFreak.rb b/MindFreak.rb index 3e3276d..7ef1bb8 100644 --- a/MindFreak.rb +++ b/MindFreak.rb @@ -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]" @@ -26,9 +26,6 @@ module MindFreak MULTIPLY = ?*.ord - @input = STDIN - @output = STDOUT - #----------------------------------------------- # Check #----------------------------------------------- @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/README.md b/README.md index 93fbb0d..137e89d 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/tests/rorschach.rb b/tests/rorschach.rb index 1b3ffff..407f010 100644 --- a/tests/rorschach.rb +++ b/tests/rorschach.rb @@ -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 #----------------------------------------------- @@ -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 #----------------------------------------------- @@ -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 #----------------------------------------------- @@ -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 #----------------------------------------------- @@ -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 @@ -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)