新プログラミング言語「ゴリスペース」を公開しました

霊長類の中でも特にオランウータン・チンパンジー・ボノボ・ゴリラの知能は高く、人間の5才児程度に相当し、ある程度は抽象的な思考もこなせることが分かっています。言葉に関しても手話を操るチンパンジーやゴリラの話は有名でしょう。

中でもゴリラについては実際に森に住むゴリラと人間が言葉でやり取りをすることさえ可能で、京都大学の山極寿一教授は10種類ほどのゴリラ語を話すことができるといいます。そう考えると彼らの為のプログラミング言語があってもおかしくありません。いや、むしろ無いことの方がおかしく思われます。

そこで私はゴリラ用プログラミング言語「ゴリスペース」を開発しました。*1

Hello World

ウホホホホウホホホ、ウホホ。ウッホホ。ウホホホホウホウッホホホホ。ウホホホホ、ウホホウホウホホーイウッホウホホホーイ。ウホホホホウホウホホホホ、ウホホウホホホホ。ウッホホウッホホホ、ウホホホウホホ。ウッホウホウッホホ、ウホホホーイウッホホ。ウホホーイウホホホ、ウホ、ウホホホホ。ウホホホホ。ウホウッホホウッホホホウホホホウッホホホホ、ウッホホ。ウホホホウホホホウホホホーイ。ウッホホホ。ウホホホホーイウホ、ウホホホウホホホホ、ウホホウホホホウッホホホホウッホホホホウホホホホウッホホホホウッホ、ウホホホ。ウホホホウホホホホーイ。ウッホウホーイウホホホホウホウホホホホウホホホホウホホホウッホホウッホホウホホホウッホホ、ウッホホウッホホウッホホウホホホーイウッホホホ、ウホーイウホホホウホホホウホホホホ、ウホホホ、ウホホホ。ウッホホホホウホホ、ウホホホホ、ウホ。ウホホホ、ウホ。ウホホホホーイ。ウッホホホホ、ウホホホーイ。ウホホウホウホホホウホホホ、ウホウッホホホホ、ウホウッホ、ウホホホウッホホホ。ウッホホホウッホホホ、ウホホホホーイ。ウッホ。ウホホホーイウホホウホホホ。ウホウホホホホ。ウホウッホウッホホホウホ。ウッホホウッホホホ。ウッホホホホウッホホウホホホホーイウッホホホホウホホーイウホホホホウホホホホ、ウホ。ウホホホホ。ウホホホ、ウッホ、ウッホホホホ、ウッホ、ウホウホホホウッホホホホ、ウホホホホウホーイウッホ、ウホホホーイ。ウホ、ウホホホ。ウホホウホホホホウホホホホウッホホ、ウッホウホホウッホホホ。ウッホウホホ、ウホホウホーイウッホ。ウホーイ。ウホウホホホホウホ。ウホホウホホ、ウッホウッホホ。ウホ、ウホホホウッホウホホ。ウホウホホーイ。ウッホホホホ、ウホーイウホウホ。ウホホ。ウホホホ、ウホウホホ。ウホホホホウホ。ウッホホホ。ウホホホ、ウッホ。ウホホホホ、ウホホーイ。ウッホホホ、ウホホホホーイウホホホホ、ウホホウホホホーイウホーイ。ウホホホホーイ
$ bin/gorispace samples/helloworld.gs 
Hello World

 
フィボナッチ数列

ウホウホウホホホホウッホウホホホウホホホウッホホホウホホホウホホウホホウホホホホーイ。ウッホウホーイウホホホ。ウホウホホホホ。ウホウホウッホホホホウッホホホホ、ウホホウッホホホホウッホホ。ウッホホホウッホウホーイ。ウッホホホホウホホホホーイ。ウホホホホウホ。ウホ、ウホホホウホホ。ウッホホホウッホホホウッホホホホ。ウホホホウッホホホホ、ウッホホホホウッホホ、ウホーイ。ウッホホホホウホーイウホウホホウホホ、ウホホウホウッホホホホウホホ、ウホホホホ。ウホウホホホウホウホーイウッホホウホーイウホ、ウホホホホウホホ。ウホホホホ、ウホホホホウッホホホホウッホホホホ、ウホホウッホホホホ。ウッホウホホホホウッホホホホウホーイウッホ。ウホホーイウホホ、ウホホホ。ウホホホ、ウホホホホウホホホホウッホウッホホホウホホホホ、ウホウホホホウホホホホウッホウホホホーイ。ウッホホ、ウホホホホーイウホホホホ、ウホウホ。ウホウホホウッホホホホウッホホホホ。ウホホ、ウッホホホホウッホウッホホホウホウホホホーイウッホホ、ウホホーイウホホホウホウホホ、ウホホホホ、ウホホウッホホホ、ウッホホホホ。ウッホホホ。ウッホ、ウホホホ。ウホウッホホウホホーイ。ウッホホホウホホーイウホホホホ。ウホホ。ウホウホホホウホホホ、ウッホホ、ウッホウッホホホホウッホ、ウッホホホホ、ウッホホウホホーイウッホホホホウホーイウホ、ウホホウホ。ウホホホホ。ウホホホホ、ウッホホホ。ウホホホホ。ウホホ。ウホホホ、ウホホホ。ウホウホホホーイウッホホウホーイウホホホウホホホ。ウホ、ウホホホホウホホホホウッホホホホウホホ、ウホホーイウッホウホホホホーイウッホホウッホウホホ、ウホホホホ、ウホホホホ。ウホホ、ウホーイ。ウホホホホ、ウホホホホ。ウホホ、ウッホホホホウホホーイウホホウホーイウホホ、ウッホホホ。ウホホホホーイウホホ、ウッホホホホウホホウホホウホウッホホホホ、ウホホ、ウッホホ、ウホホホウホホホーイウッホウホホーイウホホホホ、ウホホホ、ウホーイ。ウホ、ウホホホウッホホウホーイ。ウホ、ウホホーイウホホホウホホホホ。ウホホホ、ウホホウッホホウホホホーイウホホウホーイウッホホホホウッホホホホ、ウッホホホウホ、ウッホホウホウホホホ。ウホウホホホ。ウホホホホウホホウッホホホウホホーイウッホホ、ウッホホホホ、ウッホホホホ、ウホホホ。ウホホホーイウッホホ、ウホホ、ウホホーイウホホホウッホホウホホーイウホホウッホホ。ウホホウホウホホホウッホホホホウホ。ウッホ。ウホホホホウホホホーイウッホホウホホホーイウホホホホウホ、ウホホウホホホホ、ウホホウッホウホホ。ウホホホホーイウッホホホウッホホホホウッホホホ、ウホホホウホホホホウホ、ウッホホウホホホホーイウッホホウホホホホウホ、ウッホ、ウホウホーイウホホホホウホホホホウホホ、ウホホホホ、ウッホウホホホホ。ウホホーイウホホホ、ウホホーイウッホホ。ウッホウッホホホホウホホウホホホーイ。ウッホホホホ。ウッホホホホ、ウッホホホホ。ウホホホホウホホーイ。ウホーイウホホホホ。ウホホーイ。ウッホホ、ウホーイ。ウホホホーイウホホホホウホ。ウッホホホウホウホホホホーイウホーイウホホホーイウホーイ
$ bin/gorispace samples/fibonacci.gs 
How many? 5
1
1
2
3
5
8
13

 
その他のサンプル
http://github.com/technohippy/gorispace/tree/master/samples

  • 言語解説

ゴリスペースはスタックベースのプログラミング言語で、IMP(Instruction Modification Parameter)、コマンド、パラメータの3つ組で一つの命令を表現します。

IMP 意味
ウホ スタック操作
ウッホウホ 整数演算
ウッホウッホ ヒープアクセス
ウホーイ フロー制御
ウッホウホーイ 入出力
スタック操作 IMP: ウホ
コマンド パラメータ 意味
ウホ 数値 数値をスタックにプッシュ
ウホーイウホ - スタックトップを複製
ウホーイウッホ - スタックの1番目と2番目を交換
ウホーイウホーイ - スタックトップを破棄
整数演算 IMP: ウッホウホ
コマンド パラメータ 意味
ウホウホ - スタックの上から二つを足し算
ウホウッホ - スタックの上から二つを引き算
ウホウホーイ - スタックの上から二つを掛け算
ウッホウホ - スタックの上から二つを割り算
ウッホウッホ - スタックの上から二つで剰余
ヒープアクセス IMP: ウッホウッホ
コマンド パラメータ 意味
ウホ - 値をアドレスに格納
ウッホ - アドレスから値をスタックに
フロー制御 IMP: ウホーイ
コマンド パラメータ 意味
ウホウホ ラベル ラベル定義
ウホウッホ ラベル サブルーチン呼び出し
ウホウホーイ ラベル 無条件ジャンプ
ウッホウホ ラベル スタックトップがゼロならジャンプ
ウッホウッホ ラベル スタックトップが負ならジャンプ
ウッホウホーイ - サブルーチン終了
ウホーイウホーイ - プログラム終了
入出力 IMP: ウッホウホーイ
コマンド パラメータ 意味
ウホウホ - スタックトップの文字を出力
ウホウッホ - スタックトップの数値を出力
ウッホウホ - 文字を読み込みアドレスに格納
ウッホウッホ - 数値を読み込みアドレスに格納
数値 数値は二進数で表し、ウホ が 0、ウッホ が 1、ウホーイ が終端記号
ラベル ラベルは ウホ と ウッホ の列で表現され、ウホーイ が終端記号


なお、「ホ」の連続は「ホ」として扱われ、「ウ」「ッ」「ホ」「ー」「イ」以外の文字は全て無視されます。

・・・

ということで、そろそろ分かってると思うけど、ゴリスペースは要するにWhitespaceの亜種です(Brainf*ckを元にしてもよかったんだけど、それだとまんまOok!になってしまうので敢えてWhitespaceを選択)。Whitespaceの各トークン?との対応は次のような感じ。

Whitespace Gorispace
[Space] ウホ
[Tab] ウッホ
[LF] ウホーイ

http://github.com/technohippy/gorispace/tree/master/src/gorispace.rb

$KCODE='u'

require 'strscan'

@ops = { 
  /AA([AB]+)(C)/ => :stack_push,     /ACA/ => :stack_copy,
  /ABA([AB]+)(C)/ => :stack_copynth, /ACB/ => :stack_swap,
  /ACC/ => :stack_discard,           #/ABC([AB]+)(C)/ => :stack_slide,
  /BAAA/ => :arithmetic_add,         /BAAB/ => :arithmetic_sub,
  /BAAC/ => :arithmetic_mul,         /BABA/ => :arithmetic_div,
  /BABB/ => :arithmetic_mod,         /BBA/ => :heap_store,
  /BBB/ => :heap_restore,            /CAA([AB]+)C/ => :flow_mark,
  /CAB([AB]+)C/ => :flow_call,       /CAC([AB]+)C/ => :flow_jump,
  /CBA([AB]+)C/ => :flow_zero,       /CBB([AB]+)C/ => :flow_nega,
  /CBC/ => :flow_return,             /CCC/ => :flow_exit,
  /BCAA/ => :io_outchar,             /BCAB/ => :io_outnum,
  /BCBA/ => :io_readchar,            /BCBB/ => :io_readnum
}

@instructions = []
@stack = []
@heap = {}
@marks = {}
@call_stack = []

def scan(code)
  scanner = StringScanner.new(code)
  until scanner.eos?
    converted = false
    @ops.keys.each do |key|
      if scanner.scan(key)
        command = @ops[key]
        param = scanner[1]
        if scanner[2]
          sign = param[0] == ?A ? 1 : -1
          abs = 0 
          param[1..-1].each_byte do |c| 
            abs <<= 1
            abs |= 1 if c == ?B
          end 
          param = sign * abs 
        end 
        @instructions << [command, param]
        @marks[param] = @instructions.size - 1 if command == :flow_mark
        converted = true
        break
      end 
    end 
    break unless converted
  end 
end

def execute
  pc = 0
  while pc < @instructions.size
    old_stack_size = @stack.size
    old_heap_size = @heap.size
    command, param = *@instructions[pc]
    case command
    when :stack_push
      @stack.push(param)
    when :stack_copy
      @stack.push(@stack.last)
    when :stack_copynth
      @stack.push(@stack[param])
    when :stack_swap
      val1 = @stack.pop
      val2 = @stack.pop
      @stack.push(val1)
      @stack.push(val2)
    when :stack_discard
      @stack.pop
    #when :stack_slide
    when :arithmetic_add
      val1 = @stack.pop
      val2 = @stack.pop
      @stack.push(val2 + val1)
    when :arithmetic_sub
      val1 = @stack.pop
      val2 = @stack.pop
      @stack.push(val2 - val1)
    when :arithmetic_mul
      val1 = @stack.pop
      val2 = @stack.pop
      @stack.push(val2 * val1)
    when :arithmetic_div
      val1 = @stack.pop
      val2 = @stack.pop
      @stack.push(val2 / val1)
    when :arithmetic_mod
      val1 = @stack.pop
      val2 = @stack.pop
      @stack.push(val2 % val1)
    when :heap_store
      val = @stack.pop
      addr = @stack.pop
      @heap[addr] = val
    when :heap_restore
      addr = @stack.pop
      @stack.push(@heap[addr])
    when :flow_mark
      # do nothing
    when :flow_call
      @call_stack.push(pc)
      pc = @marks[param]
    when :flow_jump
      pc = @marks[param]
    when :flow_zero
      pc = @marks[param] if @stack.pop == 0
    when :flow_nega
      pc = @marks[param] if @stack.pop < 0
    when :flow_return
      pc = @call_stack.pop
    when :flow_exit
      exit(0)
    when :io_outchar
      print(@stack.pop.chr)
    when :io_outnum
      print(@stack.pop)
    when :io_readchar
      @heap[@stack.pop] = $stdin.readline[0]
    when :io_readnum
      @heap[@stack.pop] = $stdin.readline.to_i
    end
    @stack.compact!
    pc += 1
  end
end

def gorispace(code)
  code = code.gsub(/[^ウッホーイ]/, '').gsub(/ホ+/, 'ホ').gsub(/ウホーイ/, 'C').gsub(/ウッホ/, 'B').gsub(/ウホ/, 'A').gsub(/[^ABC]/, '')
  scan(code)
  execute
end

if __FILE__ == $0
  unless ARGV.empty?
    gorispace(ARGF.read)
  else
    puts('filename required')
  end
end

http://d.hatena.ne.jp/technohippy/20090119#1232380298

*1:なお、オランウータン用プログラミング言語としては、Ook!という言語がすでに存在します