backstage

合唱音源の新着情報の舞台裏

寿司言語を作ってみた

f:id:s2terminal:20140724204827j:plain

マジメな記事 を書いていて休日ながら疲れてしまったので、ちょっとプログラムでも書こうと思いました。

なんかRuby gemが作りたかったので、作ってみました。

3時間ほどで完成しました。

すし言語

github.com

Ruby上に実装したプログラミング言語です。

すしとRubyはともに日本を代表する文化であり、相性が良いに違いありません。

文法

文法 意味
pointer_increment
すし pointer_decrement
すすし pointer_value_increment
すすすし pointer_value_decrement
すすすすし output
すすすすすし input
すすすすすすし jump_forward
すすすすすすすし jump_backward

いわゆるBrainfu*k派生言語です。

インストール

gemにしましたが、面倒だったので rake release はしていません。 specific_installを使って下さい。

$ gem install specific_install
$ gem specific_install -l https://github.com/s2terminal/sushi_lang.git

Hello World

しすすしすすしすすしすすしすすしすすしすすしすすしすすしすすすすすすしすしすすしすすしすすしすすしすすしすすしすすしすすししすすすしすすすすすすすしすしすすすすししすすしすすしすすしすすしすすしすすしすすしすすすすすすしすしすすしすすしすすしすすししすすすしすすすすすすすしすしすすしすすすすしすすしすすしすすしすすしすすしすすしすすしすすすすしすすすすしすすしすすしすすしすすすすしすすすすすすしすすすしすすすすすすすししすすしすすしすすしすすしすすしすすしすすしすすしすすすすすすしすしすすしすすしすすしすすししすすすしすすすすすすすしすしすすすすししすすしすすしすすしすすしすすしすすしすすしすすしすすしすすしすすしすすすすすすしすしすすしすすしすすしすすしすすししすすすしすすすすすすすしすしすすすすししすすしすすしすすしすすしすすしすすしすすしすすしすすすすすすしすしすすしすすしすすししすすすしすすすすすすすしすしすすすすしすすしすすしすすしすすすすしすすすしすすすしすすすしすすすしすすすしすすすしすすすすしすすすしすすすしすすすしすすすしすすすしすすすしすすすしすすすしすすすすしすすすすすすしすすすしすすすすすすすししすすしすすしすすしすすしすすしすすしすすしすすしすすすすすすしすしすすしすすしすすしすすししすすすしすすすすすすすしすしすすしすすすすしすすすすすすしすすすしすすすすすすすしすすしすすしすすしすすしすすしすすしすすしすすしすすしすすしすすすすし

$ cat sample/sample_script.sushi 
しすすしすすしすすしすすしすすしすすしすすしすすしすすしすすすすすすしすしすすしすすしすすしすすしすすしすすしすすしすすししすすすしすすすすすすすしすしすすすすししすすしすすしすすしすすしすすしすすしすすしすすすすすすしすしすすしすすしすすしすすししすすすしすすすすすすすしすしすすしすすすすしすすしすすしすすしすすしすすしすすしすすしすすすすしすすすすしすすしすすしすすしすすすすしすすすすすすしすすすしすすすすすすすししすすしすすしすすしすすしすすしすすしすすしすすしすすすすすすしすしすすしすすしすすしすすししすすすしすすすすすすすしすしすすすすししすすしすすしすすしすすしすすしすすしすすしすすしすすしすすしすすしすすすすすすしすしすすしすすしすすしすすしすすししすすすしすすすすすすすしすしすすすすししすすしすすしすすしすすしすすしすすしすすしすすしすすすすすすしすしすすしすすしすすししすすすしすすすすすすすしすしすすすすしすすしすすしすすしすすすすしすすすしすすすしすすすしすすすしすすすしすすすしすすすすしすすすしすすすしすすすしすすすしすすすしすすすしすすすしすすすしすすすすしすすすすすすしすすすしすすすすすすすししすすしすすしすすしすすしすすしすすしすすしすすしすすすすすすしすしすすしすすしすすしすすししすすすしすすすすすすすしすしすすしすすすすしすすすすすすしすすすしすすすすすすすしすすしすすしすすしすすしすすしすすしすすしすすしすすしすすしすすすすし

$ sushi_lang sample/sample_script.sushi
Hello World!

 実装

メインの sushi_lang/sushi_lang.rb はこんな感じ

  class Sushi
    NETA = [
      :pointer_increment,       # >
      :pointer_decrement,       # <
      :pointer_value_increment, # +
      :pointer_value_decrement, # -
      :output,                  # .
      :input,                   # ,
      :jump_forward,            # [
      :jump_backward            # ]
    ]

    def initialize(filename)
      # Compiler
      @source = filename
      @object_code = []
      @jump = {}

      # Turing Machine
      @tape = []
      @pointer = 0
      @line = 0
    end

    def compile
      File.open(@source) do |file|
        su_count = 0
        line = 0
        jump_stack = []

        file.each_char{|char|
          case char
            when ""
              su_count += 1
            when ""
              code = NETA[su_count]

              # jump
              if code == :jump_forward
                jump_stack.push(line)
              elsif code == :jump_backward
                left_curly_brace = jump_stack.pop
                @jump[line] = left_curly_brace
                @jump[left_curly_brace] = line
              end

              @object_code.push(code)
              su_count = 0
              line += 1
          end
        }

      end
    end

    def execute
      until @object_code[@line].nil?
        self.send(@object_code[@line])
        @line += 1
      end
    end

    private

    # ++p;
    def pointer_increment
      @pointer += 1
    end

    # --p;
    def pointer_decrement
      @pointer -= 1
    end

    # ++*p;
    def pointer_value_increment
      @tape[@pointer] ||= 0
      @tape[@pointer] += 1
    end

    # --*p;
    def pointer_value_decrement
      @tape[@pointer] ||= 0
      @tape[@pointer] -= 1
    end

    # putchar(*p);
    def output
      print @tape[@pointer].chr
    end

    # *p = getchar();
    def input
      @tape[@pointer] ||= 0
      @tape[@pointer] = STDIN.getc
    end

    # while (*p) {
    def jump_forward
      @line = @jump[@line] if @tape[@pointer] == 0
    end

    # }
    def jump_backward
      @line = @jump[@line] if @tape[@pointer] != 0
    end
  end

sendを使って全部プライベートメソッド化している所とかちょっと格好つけてみました。

まとめ

 参考文献