⚠️ Warning: This is a draft ⚠️
This means it might contain formatting issues, incorrect code, conceptual problems, or other severe issues.
If you want to help to improve and eventually enable this page, please fork RosettaGit's repository and open a merge request on GitHub.
{{implementation|SNUSP}}{{collection|RCSNUSP}} These [[Ruby]] implementations of SNUSP profiles are partially derived from [[RCSNUSP/Tcl]]. == Core SNUSP ==
$stdout.sync = true $stdin.sync = true class CoreSNUSP Bounce = { :ruld => {:right => :up, :left => :down, :up => :right, :down => :left }, :lurd => {:right => :down, :left => :up, :up => :left, :down => :right} } Delta = {:up => [-1, 0], :down => [1, 0], :left => [0,-1], :right => [0, 1]} Dispatch = Hash.new(:unknown).update({ ">" => :right, "<" => :left, "+" => :incr, "-" => :decr, "/" => :ruld, "\\"=> :lurd, "?" => :skipz, "!" => :skip, "." => :write, "," => :read, '"' => :dump, }) def initialize(text, args={}) @data = Hash.new(0) @dptr = 0 @program, @pc = read_program(text) @height = @program.size @width = @program[0].nil? ? 0 : @program[0].size @pdir = :right @input = args[:input] end def read_program(text) pc = [0,0] program = [] max = 0 text.each_line.each_with_index do |line, lineno| line.chomp! max = [max, line.length].max if not (idx = line.index("$")).nil? pc = [lineno, idx] end program << line.split(//) end # pad short lines program.map! {|row| row.concat([""] * (max - row.size))} [program, pc] end def current_command @program[@pc[0]].nil? and return nil @program[@pc[0]][@pc[1]] end def run p @program if $DEBUG command = current_command p [@pc, command] if $DEBUG catch :have_exit_command do until command.nil? self.send Dispatch[command] move command = current_command p [@pc, command] if $DEBUG end end end def move delta = Delta[@pdir] @pc = [ @pc[0] + delta[0], @pc[1] + delta[1]] if @pc[0] < 0 or @pc[0] > @height or @pc[1] < 0 or @pc[1] > @width raise IndexError, "program counter out of bounds: #{@pc.inspect}" end end def right @dptr += 1 end def left @dptr -= 1 end def incr if @dptr < 0 raise IndexError, "data pointer less than zero: #@dptr" end @data[@dptr] += 1 p ["data:",@dptr, @data[@dptr]] if $DEBUG end def decr if @dptr < 0 raise IndexError, "data pointer less than zero: #@dptr" end @data[@dptr] -= 1 p ["data:",@dptr, @data[@dptr]] if $DEBUG end def ruld p @pdir if $DEBUG @pdir = Bounce[:ruld][@pdir] p @pdir if $DEBUG end def lurd p @pdir if $DEBUG @pdir = Bounce[:lurd][@pdir] p @pdir if $DEBUG end def skipz p ["data:",@dptr, @data[@dptr]] if $DEBUG move if @data[@dptr] == 0 end def skip move end def write p "output: #{@data[@dptr]} = '#{@data[@dptr].chr}'" if $DEBUG $stdout.print @data[@dptr].chr end def read @data[@dptr] = if @input.nil? # blocking read from stdin $stdin.getc else @input.slice!(0) end p "read '#{@data[@dptr]}'" if $DEBUG end def dump p [@dptr, @data] end def unknown; end end ############################################################################## if __FILE__ == $0 puts "Empty Program 1" emptyProgram = '' snusp = CoreSNUSP.new(emptyProgram) snusp.run puts "done" puts "Hello World" # brainfuck #helloworld = '++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.' # # http://c2.com/cgi/wiki?SnuspLanguage -- "Is there a simple way to translate an arbitrary Brainfuck program into Core SNUSP?" # # a[bc]d # #translates to # # a!/=?\d # \cb/ # #helloworld = <<'PROGRAM' #++++++++++!/ ### ======================= ?\>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.' # \-<<<<+>+++>++++++++++>+++++++>/ #PROGRAM helloWorld = <<'PROGRAM' /++++!/ ### ===== ?\>++.>+.+++++++..+++\ \+++\ | /+>+++++++>/ /++++++++++<<.++>./ $+++/ | \+++++++++>\ \+++++.>.+++.-----\ \==-<<<<+>+++/ /=.>.+>.--------.-/ PROGRAM snusp = CoreSNUSP.new(helloWorld) snusp.run puts "done" end
Output:
$ ruby rc_coresnusp.rb
Empty Program 1
done
Hello World
Hello World!
done
== Modular SNUSP ==
require 'rc_coresnusp.rb' class ModularSNUSP < CoreSNUSP Dispatch = self.superclass::Dispatch.update({ "@" => :enter, "#" => :leave, }) def initialize(text, args={}) super @execution_stack = [] end def enter p @execution_stack if $DEBUG current_pc = @pc move @execution_stack << [@pc, @pdir] p @execution_stack if $DEBUG @pc = current_pc end def leave p @execution_stack if $DEBUG throw :have_exit_command if @execution_stack.empty? @pc, @pdir = @execution_stack.pop p @execution_stack if $DEBUG end end ############################################################################## if __FILE__ == $0 puts "Empty Program 2" emptyProgram = '$#' snusp = ModularSNUSP.new(emptyProgram) snusp.run puts "done" puts "Goodbye World" goodbyeWorld = <<'PROGRAM' @\G.@\o.o.@\d.--b.@\y.@\e.>@\comma.@\.<-@\W.+@\o.+++r.------l.@\d.>+.!=>\ | | \@------|# | \@@+@@++|+++#- \\ - /+++++/ | \@@@@=+++++# | \===--------!\===!\-----|-------#-------/ \+++++.# nl \@@+@@@+++++# \!#+++++++++++++++++++++++#!/ PROGRAM snusp = ModularSNUSP.new(goodbyeWorld) snusp.run puts "done" puts "Modular: Ackerman" # this program requires two characters to be read on standard input: # the numbers for j and i in that order ackerman = <<'PROGRAM' @\\ /==!/atoi------------------------------------------------# / / | | / ### === \!==\!====\ (recursion) >\,@/>,@/=ACK==!\?\<+# | | | A(0,j) -> j+1 > j i \<?\+>-@/# | | A(i,0) -> A(i-1,1) @ \@\>@\->@/@\<-@/# A(i,j) -> A(i-1, A(i, j-1)) \=\ [0]->[2] | | | > + #/?<<+>>-\!==/ | \==!/-<<+>>?\# [2]->[0] + + \->>+<<?/# | #\?>>+<<-/ + + | + + \@\>>>@\<<# copy [1]->[3] and advance + + [1]->[3][4] | | + + #/?<<<+>+>>-\!====/ \==!/-<<<+>>>?\# [4]->[1] + + \->>+>+<<<?/# #\?>>>+<<<-/ + + + + print newline + + /-\ + \+++CR.---NL.!\?/<<# | | /<=!/!#++++\ \ ### == @\@/.>@/.# /+\ atoi | /=\/+++\ wiki /==div===/ \!\++++/ | \++/ | /-\ \/ \?\<!\?/#!===+<<<\ /-\ wiki \<==@\>@\>>!/?!/=<?\>!\?/<<# | | #\->->+</ \=!\=?!/->>+<<?\# #\?<<+>>-/ PROGRAM snusp = ModularSNUSP.new(ackerman, :input => '33') # calculate A(3,3) snusp.run puts puts "done" end
Output:
$ ruby rc_modularsnusp.rb
Empty Program 2
done
Goodbye World
Goodbye, World!
done
Modular: Ackerman
61
done
== Bloated SNUSP == User input is still line-oriented -- must press Enter before Ruby sees input.
require 'rc_modularsnusp.rb' # here, need input to be non-blocking, so other "threads" can keep working. # we'll have to simulate blocking when reading a character from stdin. see read() below require 'fcntl' $stdin.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK) SNUSPThread = Struct.new(:pc, :pdir, :execution_stack, :dptr) class BloatedSNUSP < ModularSNUSP Dispatch = self.superclass::Dispatch.update({ ":" => :up, ";" => :down, "&" => :split, "%" => :rand, }) def initialize(text, args={}) super @dptr = [0,0] @threads = {} @thread_counter = 0 add_thread end def add_thread @thread_counter += 1 @threads[@thread_counter] = SNUSPThread.new(@pc.dup, @pdir, @execution_stack, @dptr.dup) end def run p @program if $DEBUG until @threads.empty? stopped_thread_ids = [] # threads are run in arbitrary order p @threads if $DEBUG @threads.each_key do |thread_id| thread = @threads[thread_id] @pc = thread.pc @pdir = thread.pdir @dptr = thread.dptr @execution_stack = thread.execution_stack if tick(thread_id) # save state thread.pc = @pc thread.pdir = @pdir thread.dptr = @dptr thread.execution_stack = @execution_stack else stopped_thread_ids << thread_id end end stopped_thread_ids.each {|id| @threads.delete(id)} end end def tick(thread_id) command = current_command p [thread_id, @pc, command] if $DEBUG return false if command.nil? # thread complete return false if self.send(Dispatch[command]) == :have_exit_command move command = current_command p [@pc, command] if $DEBUG true end def leave p @execution_stack if $DEBUG return :have_exit_command if @execution_stack.empty? @pc, @pdir = @execution_stack.pop p @execution_stack if $DEBUG end def read # we want input to be blocking. However, actual blocking on stdin will halt # all other running "threads". So, what we do here is to set stdin to be # non-blocking (at top of this file), and if we fail to read a character, # we backup the program counter so we attempt to read again at the next tick. char = if @input.nil? begin $stdin.sysread(1) rescue SystemCallError nil end else @input.slice!(0) end if char.nil? p "no data to read" if $DEBUG # backup, so we can poll again in the next tick. reverse_direction = {:up => :down, :down => :up, :right => :left, :left => :right} @pdir = reverse_direction[@pdir] move @pdir = reverse_direction[@pdir] else @data[@dptr] = char p "read '#{@data[@dptr]}'" if $DEBUG end end def left @dptr[0] -= 1 end def right @dptr[0] += 1 end def up @dptr[1] -= 1 end def down @dptr[1] += 1 end def incr if @dptr[0] < 0 or @dptr[1] < 0 raise IndexError, "data pointer less than zero: #{@dptr.inspect}" end @data[@dptr] += 1 p ["data:",@dptr, @data[@dptr]] if $DEBUG end def decr if @dptr[0] < 0 or @dptr[1] < 0 raise IndexError, "data pointer less than zero: #{@dptr.inspect}" end @data[@dptr] -= 1 p ["data:",@dptr, @data[@dptr]] if $DEBUG end def split move add_thread end def rand @data[@dptr] = Kernel.rand(1+@data[@dptr]) end end ############################################################################## if __FILE__ == $0 # This prints out a random number of random length: puts "Bloated: random" random = <<'PROGRAM' $!/+++++++++%++++++++++++++++++++++++++++++++++++++++++++++++.!/-\ \ ### ============================== <#!?%+++++++++++++++>===\?/ PROGRAM snusp = BloatedSNUSP.new(random) snusp.run puts "done" # This (from the esolangs wiki) prints exclamation marks until you press a key: puts "Bloated: BangBang" bang = <<'PROGRAM' /==.==<==\ | | /+++++++++++==&\==>===?!/==<<==# \+++++++++++\ | $==>==+++++++++++/ \==>==,==# PROGRAM snusp = BloatedSNUSP.new(bang) snusp.run puts "done" $stdin.read # clear stdin before next interactive program # programs from http://www.baumanfamily.com/john/esoteric.html puts "Bloated: randout" randout = <<'PROGRAM' //?\! ### .= /!=\/\/\/\/=!\\ |\-/++\ | ++++++++ \/ | + + | ++++++++ | + + | ++++++++ | + + < \/\/\/\/ $==&\=+/ \+=%>?!/====# | | | \==>==,==# PROGRAM snusp = BloatedSNUSP.new(randout) snusp.run puts "done" end
Output:
```txt
$ ruby rc_bloatedsnusp.rb
Bloated: random
3476158785073done
Bloated: BangBang
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!j!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
done
Bloated: randout
35382407644156390115:505854:46783082311335:4940492329597706::155
2done
</div>