0と1だけだけど、機械語じゃない言語「Whirl」
言語機能としての「グリルチキン」とはなにか、というツッコミは とりあえず置いとくとして、引き算すらないというのはどういうことか。 ちなみに命令は0と1しかない。
あはは。なんかだか可笑しい。
そこはかとなくCASLを思い出してしまいました。
で、これくらい軽量なら実装かんたんだなー、と思ってRubyで書いてみた。
工夫無しのベタ書きの長文。
なんか仕様がわかりずらくて(Sets A to B が A->B と B->A の両方で使われてるような・・・)2時間もかかってしまった。
ついでに、リンク先からサンプルとして「X+Y」と「Hello,World」を貰って追加しておく。
# # Whirl Interpreter # (http://www.bigzaphod.org/whirl/) # # # 'Ring' base class # class Ring def initialize(hardware) @hardware = hardware self.reset @ring = [] end def reset @index = 0 @direction = 1 @value = 0 end def reverse @direction = -@direction end def rotate @index += @direction if @index == -1 @index = @ring.length - 1 elsif @index == @ring.length @index = 0 end end def command @ring[@index] end def value return @value end def value=(val) @value = val end end # # OperationRing class # class OperationRing < Ring def initialize(hardware) super @ring = [ {:name => "OPE:Noop", :proc => self.method("_noop")}, {:name => "OPE:Exit", :proc => method("_exit")}, {:name => "OPE:One", :proc => method("_one")}, {:name => "OPE:Zero", :proc => method("_zero")}, {:name => "OPE:Load", :proc => method("_load")}, {:name => "OPE:Store", :proc => method("_store")}, {:name => "OPE:PAdd", :proc => method("_padd")}, {:name => "OPE:DAdd", :proc => method("_dadd")}, {:name => "OPE:Logic", :proc => method("_logic")}, {:name => "OPE:If", :proc => method("_if")}, {:name => "OPE:Intl0", :proc => method("_intl0")}, {:name => "OPE:Ascl0", :proc => method("_ascl0")}, ] end def _noop end def _exit @program_pointer_position = 9999999999 end def _one self.value = 1 end def _zero self.value = 0 end def _load self.value = @hardware.memval end def _store @hardware.memval = self.value end def _padd @hardware.program_position_pointer = @hardware.program_position_pointer + self.value end def _dadd @hardware.memory_position_pointer = @hardware.memory_position_pointer + self.value end def _logic if @hardware.memval == 0 self.value = 0 else self.value = self.value & 1 end end def _if if @hardware.memval != 0 _padd end end def _intl0 if self.value == 0 @hardware.memval = STDIN.gets.to_i else STDOUT.print @hardware.memval end end def _ascl0 if self.value == 0 @hardware.memval = STDIN.gets[0] else STDOUT.print @hardware.memval.chr end end end # # MathRing class # class MathRing <Ring def initialize(hardware) super @ring = [ {:name => "MAT:Noop", :proc => method("_noop")}, {:name => "MAT:Load", :proc => method("_load")}, {:name => "MAT:Store", :proc => method("_store")}, {:name => "MAT:Add", :proc => method("_add")}, {:name => "MAT:Mult", :proc => method("_mult")}, {:name => "MAT:Div", :proc => method("_div")}, {:name => "MAT:Zero", :proc => method("_zero")}, {:name => "MAT:<", :proc => method("_lessthan")}, {:name => "MAT:>", :proc => method("_greaterthan")}, {:name => "MAT:=", :proc => method("_equal")}, {:name => "MAT:Not", :proc => method("_not")}, {:name => "MAT:Neg", :proc => method("_neg")}, ] end def _noop end def _load self.value = @hardware.memval end def _store @hardware.memval = self.value end def _add self.value = self.value + @hardware.memval end def _mult self.value = self.value * @hardware.memval end def _div self.value = self.value / @hardware.memval end def _zero self.value = 0 end def _lessthan if self.value < @hardware.memval self.value = 1 else self.value = 0 end end def _greaterthan if self.value > @hardware.memval self.value = 1 else self.value = 0 end end def _equal if self.value == @hardware.memval self.value = 1 else self.value = 0 end end def _not if self.value != 0 self.value = 0 else self.value = 1 end end def _neg self.value = self.value * -1 end end # # Whirl class (Interpreter) # class Whirl def initialize @rings = [OperationRing.new(self), MathRing.new(self)] end def program_position_pointer @program_position_pointer end def program_position_pointer=(ppp) @program_position_pointer = ppp end def memory_position_pointer @memory_position_pointer end def memory_position_pointer=(mpp) @memory_position_pointer = mpp end def memval @memval_hash[@memory_position_pointer] ||= 0 return @memval_hash[@memory_position_pointer] end def memval=(value) @memval_hash[@memory_position_pointer] = value end def run(command_string, debug = false) @ring_index = 0 @program_position_pointer = 0 @memory_position_pointer = 0 @memval_hash = {} @rings[0].reset @rings[1].reset puts "**** Whirl ****" prev_command = nil while (program_position_pointer < command_string.length) current_position = self.program_position_pointer command_char = command_string[current_position..current_position] case command_char when "0" puts "\t\tREVERSE!" if debug @rings[@ring_index]::reverse if prev_command == "0" cmd = @rings[@ring_index]::command puts "\t#{cmd[:name]}" if debug cmd[:proc].call @ring_index = 1 - @ring_index prev_command = nil else prev_command = command_char end when "1" puts "\t\tROTATE!" if debug @rings[@ring_index]::rotate prev_command = command_char else p command_char raise "Command not found. <#{command_char}>" end self.program_position_pointer += 1 if current_position == self.program_position_pointer end print "\n[end]\n\n" end end # #TEST # whirl = Whirl.new #TEST(1) x+y code = " 011000001111000011111000001111000011111000001111000011111000 001100100000110011111000111000111100011001110000000001111100 01000111110011001111100010001100".gsub("\n", "") whirl.run(code) #TEST(2) 'Hello, World!' #(Sample code by Kang Seonghoon.) code = " 110011100111000001111100000001000011111000011111100000000010 000011001111100001100010000010011111000100000000000001001111 100000111110001000000000000000001000111110010000001100001111 100011000000000100111110011100111000111000001000111000001111 100000111110010000011111000110011111100001111000001111000001 110011111100001111000110011100000111000100011111000001111100 100000110000000111000001110001111100011111000111000001000001 000011000111110001000001000000011100000111001000111110001111 000001111000011111100001111110000011110000000000000000011110 000011100111000011110011111000111110001111100000100000000000 000000000000111110001110000001110000011100011100111110001000 100000000011100001111100110000000010011111000111100000111100 111100010011100000111110000011111001100111100010001111000000 000001000111110010000010011110011001110001000111110001100000 100011111000011110011100111111000111100000111100011111000000 011110000011100100001111000100011111001100011111000111100000 111001110001100111100100000000000000011111000001111100010010 000011100001111100100000100011100000111000110011110001001111 110001100000111100011111000111100000111001000011110001001111 100000111110000000011110000011110000000000000000111000001110 000011000001100000111000111000001100111110000111111001001110 000011111000001100011000001001111110000011100110011111000000 000111000001110000111100001100".gsub("\n", "") whirl.run(code)
実行すると
$ ruby whirl_ruby.rb **** Whirl **** 1 3 4 [end] **** Whirl **** Hello, World! [end]
という感じ。動くもんだなぁ。
公式のRuby版*1は、さすがに整理されていて綺麗。
でもまぁ、仕様通りに動けばすべてOK!
これ、表示を「ピタゴラスイッチ」みたいなギミックいっぱいにすれば、見ている分には楽しいかも。
やんないけど。