Hatena::ブログ(Diary)

mirichiの日記 このページをアンテナに追加 RSSフィード

2016-10-31

連鎖性言語を作る11

原因がまったく想像できない難解なバグに遭遇した場合、怪しいところから調べるのではなく、当たり前に大丈夫なはずのところから本当に大丈夫であることを確定していくほうが結果的に速く原因に辿り着けることがある。急がば回れという話だが、真剣におかしいところが不明な時はどこか見落としがあるはずで、見落とすということはそれはつまり大丈夫だと思い込んでいる場所なわけなので、このアプローチはそれなりに正しい。

という前振りとはぜんぜん関係ないのだが、言語を(というか実際にやっているのは言語処理系を)作るのもやったことないことの塊なので、地道にひとつひとつ積み上げて、それが可能なこと、それから問題が発生するなら解決法の模索を、踏みしめるようにじっくりと進めることが大切である。言語処理系開発の疑似体験とでも言おうか。この経験が今後何かに生きるとも思えないし、このシリーズの記事が誰かの役に立つともイマイチ思えないのだが、まあ、これが趣味なので。

ということで今回は地味に半歩ほど進める感じで、Rubyからオレ言語を呼び出すインターフェイスを考える。

続きを読む

2016-10-30

連鎖性言語を作る10

オレ言語のコードをRubyコードに変換してevalすれば、VMで実行するのと比べてディスパッチが無くなって速くなる。毎回evalするとRubyのコンパイルが発生してオーバーヘッドがあるので、evalでProcを生成して、実行時はそれを呼ぶだけ、という形にしておくのが望ましい。これはテキストを編集してうんぬん、というだけなので簡単であり省略。

新たなチャレンジとして、オレ言語→Rubyトランスレータなどがよいかもしれない。変換したコードをrbファイルで出力しておいて、それをrequireして呼び出すことで実行できる、みたいな。Rubyを呼び出すことはできていたが、Rubyから呼ぶインターフェイスは今まで無かったのでそのへんを構築するという課題も解消する。んじゃこれいってみよう。

というところまで考えて、ふと思った。

Rubyコードに変換してevalするだけってのは、本当に簡単なのか?印象としては簡単っぽいけども、やったことがあればまだしも、やったことの無いことに対して簡単なはずだから省略ってのはちょっとアレなのではないか。簡単ならすぐできるわけだし、とりあえずやっておけばいいんじゃないか。と。

やってみよう。

続きを読む

2016-10-29

連鎖性言語を作る9

VMの命令列にコンパイルして実行するようになったのでなんとなくVMっぽい気がする感じになったところで、命令が2個しかないのは物足りない。これを増やすことを考えてみよう。オレ言語はスタック指向であり、スタックマシンのVMとは相性がよい。相性がよいと言うか、言語がシンプルなせいでほぼそのままストレートに組み込みワードをVM命令化することができる。

実際のところ、コードジェネレータをこのようにして、

module CodeGenerator
  def self.generate(ast)
    ast.instance_variable_set(:@bytecode, ast.map{|d|
      case d
      when :swap
        Instruction.new(:swap, nil)
      when :drop
        Instruction.new(:drop, nil)
      when :dup
        Instruction.new(:dup, nil)
      when Symbol
        Instruction.new(:call, d)
      when OreSymbol
        Instruction.new(:push, d.sym)
      else
        Instruction.new(:push, d)
      end
    })
  end
end

VMをこのようにすれば

  def run(ast)
    unless ast.instance_variable_defined?(:@bytecode)
      CodeGenerator.generate(ast)
    end

    ast.instance_variable_get(:@bytecode).each do |d|
      case d.insn
      when :swap
        @stack[-2], @stack[-1] = @stack[-1], @stack[-2]
      when :drop
        @stack.pop
      when :dup
        @stack << @stack[-1]
      when :call
        @words[d.data].call
      when :push
        @stack << d.data
      else
        raise
      end
      break if @break_flg
    end
  end

swap、drop、dupの3つの組み込みワードはVM命令化できる。じゃあこの調子で全部VM命令化してしまえばいいのかと言うとそうでもない。VMとかの専門家ではないのでイマイチだがちょっと考えてみる。

続きを読む