Execute SNUSP/Ruby: Difference between revisions
(add Ruby) |
m (→Core SNUSP: IO improvements) |
||
Line 2: | Line 2: | ||
With gratitude to [[RCSNUSP/Tcl]] |
With gratitude to [[RCSNUSP/Tcl]] |
||
== Core SNUSP == |
== Core SNUSP == |
||
<lang ruby> |
<lang ruby>$stdout.sync = true |
||
$stdin.sync = true |
|||
class CoreSNUSP |
|||
Bounce = { |
Bounce = { |
||
:ruld => {:right => :up, :left => :down, :up => :right, :down => :left }, |
:ruld => {:right => :up, :left => :down, :up => :right, :down => :left }, |
||
Line 19: | Line 22: | ||
"." => :write, |
"." => :write, |
||
"," => :read, |
"," => :read, |
||
'"' => :dump, |
|||
}) |
}) |
||
def initialize(text) |
def initialize(text, args={}) |
||
@data = Hash.new(0) |
@data = Hash.new(0) |
||
@dptr = 0 |
@dptr = 0 |
||
Line 28: | Line 32: | ||
@width = @program[0].nil? ? 0 : @program[0].size |
@width = @program[0].nil? ? 0 : @program[0].size |
||
@pdir = :right |
@pdir = :right |
||
@input = args[:input] |
|||
end |
end |
||
Line 77: | Line 82: | ||
@dptr += 1 |
@dptr += 1 |
||
end |
end |
||
def left |
def left |
||
@dptr -= 1 |
@dptr -= 1 |
||
end |
end |
||
def incr |
def incr |
||
if @dptr < 0 |
if @dptr < 0 |
||
Line 87: | Line 94: | ||
p ["data:",@dptr, @data[@dptr]] if $DEBUG |
p ["data:",@dptr, @data[@dptr]] if $DEBUG |
||
end |
end |
||
def decr |
def decr |
||
if @dptr < 0 |
if @dptr < 0 |
||
Line 94: | Line 102: | ||
p ["data:",@dptr, @data[@dptr]] if $DEBUG |
p ["data:",@dptr, @data[@dptr]] if $DEBUG |
||
end |
end |
||
def ruld |
def ruld |
||
p @pdir if $DEBUG |
p @pdir if $DEBUG |
||
Line 99: | Line 108: | ||
p @pdir if $DEBUG |
p @pdir if $DEBUG |
||
end |
end |
||
def lurd |
def lurd |
||
p @pdir if $DEBUG |
p @pdir if $DEBUG |
||
Line 104: | Line 114: | ||
p @pdir if $DEBUG |
p @pdir if $DEBUG |
||
end |
end |
||
def skipz |
def skipz |
||
p ["data:",@dptr, @data[@dptr]] if $DEBUG |
p ["data:",@dptr, @data[@dptr]] if $DEBUG |
||
move if @data[@dptr] == 0 |
move if @data[@dptr] == 0 |
||
end |
end |
||
def skip |
def skip |
||
move |
move |
||
end |
end |
||
def write |
def write |
||
p "output: #{@data[@dptr]} = '#{@data[@dptr].chr}'" if $DEBUG |
|||
$stdout.print @data[@dptr].chr |
|||
end |
end |
||
def read |
def read |
||
@data[@dptr] = |
@data[@dptr] = if @input.nil? |
||
# blocking read from stdin |
|||
$stdin.getc |
|||
else |
|||
@input.slice!(0) |
|||
end |
|||
p "read '#{@data[@dptr]}'" if $DEBUG |
p "read '#{@data[@dptr]}'" if $DEBUG |
||
end |
end |
||
def dump |
|||
p [@dptr, @data] |
|||
end |
|||
def unknown; end |
def unknown; end |
||
end |
end |
||
Line 149: | Line 174: | ||
helloWorld = <<'PROGRAM' |
helloWorld = <<'PROGRAM' |
||
/++++!/==========?\>++.>+.+++++++..+++\ |
/++++!/===========?\>++.>+.+++++++..+++\ |
||
\+++\ |/+>+++++++>/ /++++++++++<<.++>./ |
\+++\ | /+>+++++++>/ /++++++++++<<.++>./ |
||
$+++/ |\+++++++++>\ \+++++.>.+++.-----\ |
$+++/ | \+++++++++>\ \+++++.>.+++.-----\ |
||
\=-<<<<+>+++/ /=.>.+>.--------.-/ |
\==-<<<<+>+++/ /=.>.+>.--------.-/ |
||
PROGRAM |
PROGRAM |
||
snusp = CoreSNUSP.new(helloWorld) |
snusp = CoreSNUSP.new(helloWorld) |
||
Line 159: | Line 184: | ||
end</lang> |
end</lang> |
||
Output: |
Output: |
||
<pre>$ ruby rc_coresnusp.rb |
<pre>$ ruby rc_coresnusp.rb |
Revision as of 15:17, 1 August 2009
With gratitude to RCSNUSP/Tcl
Core SNUSP
<lang ruby>$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</lang>
Output:
$ ruby rc_coresnusp.rb Empty Program 1 done Hello World Hello World! done
Modular SNUSP
<lang ruby>require 'rc_coresnusp.rb'
class ModularSNUSP < CoreSNUSP
Dispatch = self.superclass::Dispatch.update({ "@" => :enter, "#" => :leave, }) def initialize(text) 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 + + /-\ + \.+++.!\?/<<# | | /<=!/!#++++\ \========@\@/.>@/.# /+\ atoi
| /=\/+++\ wiki /==div===/ \!\++++/ | \++/ | /-\ \/ \?\<!\?/#!===+<<<\ /-\ wiki \<==@\>@\>>!/?!/=<?\>!\?/<<# | | #\->->+</ \=!\=?!/->>+<<?\# #\?<<+>>-/
PROGRAM snusp = ModularSNUSP.new(ackerman) snusp.run puts "done"
end</lang> Output:
$ echo '33' | ruby rc_modularsnusp.rb Empty Program 2 done Goodbye World Goodbye, World! done Modular: Ackerman 61done
Bloated SNUSP
The threading is not correctly implemented -- the "BangBang" example is not working.
<lang ruby>require 'rc_modularsnusp.rb'
SNUSPThread = Struct.new(:pc, :pdir, :execution_stack, :dptr)
class BloatedSNUSP < ModularSNUSP
Dispatch = self.superclass::Dispatch.update({ ":" => :up, ";" => :down, "&" => :split, "%" => :rand, }) def initialize(text) super @dptr = [0,0] @threads = {} @thread_counter = 0 add_thread end def add_thread @thread_counter += 1 @threads[@thread_counter] = SNUSPThread.new(@pc, @pdir, @execution_stack, @dptr) 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 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"
end </lang>
Output:
$ ruby rc_bloatedsnusp.rb Bloated: random 426844146737605042562646108481049454738617585534012246132619612443400509062done Bloated: BangBang
The program hangs there, awaiting user input, without printing any exclamation marks. Hmmm.