@emasaka
自己紹介
●   @emasaka
●   本名:高橋正和
●   40代 フリーター
●   ブログ
    「本を読む」http://emasaka.blog65.fc2.com/
●   代表作:Bash on Rails
近況
nagaino作りました
●   複数の短縮URLを1リクエストで多段展開する
    Web APIサービス

                    http://nagaino.herokuapp.com/
今日の話
Perl
Perlのソースフィルタ
●   ソースを読み込んでからパースするまでの間
    に、スクリプトからソースを直接いじれる
Filter::SQL
●   Perlのソース中にインラインでSQL文を埋め
    込む
●   奥一穂氏作
どっちかというとAcme
(ネタ系)モジュールで
    使われる
Acme::Bleach
●   自分を読みんでいるソースファイルを書き換え
    て、空白とタブだけにしてしまう
●   (でも実行できる)
Acme::Brainfuck
●   Perlのソース中にインラインでBrainf*ckを埋
    め込む
Acme::W
●   Perlの識別子を草だらけにする
    ●   print → WWwwWW
Acme::Metification
●   ソース中に“meta 2, 5”と書くと、その行が、
    2〜5行目の内容に置きかわる
    ●   指定範囲に自分が入っていると無限再帰
●   “recursemeta depth => 10, 2, 5”と書くと、
    10レベルまで再帰
Rubyで(超簡易版)
ソースフィルタ
Rubyのソースにパッチ
●   ruby.cのload_file_internal()
diff ­up ruby­1.9.2­p290/ruby.c filter­ruby­1.9.2­p290/ruby.c
­­­ ruby­1.9.2­p290/ruby.c      2010­12­23 19:49:13.000000000 +0900
+++ filter­ruby­1.9.2­p290/ruby.c       2011­10­28 21:50:44.452623054 
+0900
@@ ­1618,7 +1618,16 @@ load_file_internal(VALUE arg)
        return (VALUE)rb_parser_compile_string(parser, fname, f, 
line_start);
     }
     rb_funcall(f, set_encoding, 2, rb_enc_from_encoding(enc), 
rb_str_new_cstr("­"));
­    tree = rb_parser_compile_file(parser, fname, f, line_start);
+    {
+       VALUE source_filter = rb_intern("source_filter");
+       if (rb_obj_respond_to(rb_cObject, source_filter, 1)) {
+           VALUE s1 = rb_funcall(f, rb_intern("read"), 0);
+           VALUE s2 = rb_funcall(rb_mKernel, source_filter, 1, s1);
+           tree = rb_parser_compile_string(parser, fname, s2, 
line_start);
+       } else {
+           tree = rb_parser_compile_file(parser, fname, f, line_start);
+       }
+    }
     rb_funcall(f, set_encoding, 1, rb_parser_encoding(parser));
     if (script && tree && rb_parser_end_seen_p(parser)) {
        rb_define_global_const("DATA", f);
内容
●   source_filterメソッドが定義されていれば
    ●   ファイルの内容をStringに読み込む
    ●   source_filterに通す
    ●   結果をパース
●   定義されていなければいつも通り
利用例
例1

「Rubyのメソッド呼び
出しが“.”なのは直感に
     反する。
    “->”に」
フィルタ(arrow.rb)

def source_filter(str)
  str.gsub(/­>/, '.')
end
サンプル(arrow_sample.rb)

[3, 5, 7]­>map {|i| i + 2 }­>each do |i|
  puts i­>to_s
end
実行

$ ruby ­rarrow arrow_sample.rb
5
7
9
              コマンドライン
              で直接指定
例2:さらに
●   “$”“{”を無視
●   “}”を“end”に
●   “function”を“def”に
●   “this”を“self”に
フィルタ(arrow2.rb)

def source_filter(str)
  str.gsub(/­>/, '.').gsub(/}/, 'end').
    gsub(/function/, 'def').
    gsub(/this/, 'self').
    delete('${')
end
サンプル(arrow2_sample.rb)

class Hoge {
  function foo() {
    puts($this­>class());
    return $this;
  }
}

Hoge­>new­>foo();
実行

$ ruby ­rarrow2 arrow2_sample.rb
Hoge
例3




「Rubyはrequireとinclude
     が混乱する」
フィルタ(include.rb)

def source_filter(str)
  str.gsub(/^s*#s*includes+<(.[^>]*)>$/,
           'require "1"' )
end
サンプル(include_sample.rb)

#include <uri>

u = URI.parse("http://example.com:80/hoge?
fuga#foo")
p u
実行

$ ruby ­rinclude include_sample.rb
#<URI::HTTP:0x8463c84 
URL:http://example.com/hoge?fuga#foo>
例4



“ennnnnd”で5レベル
 まとめてendしたい
サンプル(ennd_sample.rb)

module MyModule
  class MyClass
    def my_method
      10.times do |i|
        if (i % 2) == 0
          puts 'even'
        else
          puts 'odd'
        ennnnnd

MyModule::MyClass.new.my_method
フィルタ(ennd.rb)

def source_filter(str)
  str.gsub(/e(n{2,})d/) do
    Array.new($1.length, 'end').join(';')
  end
end
実行

$ ruby ­rennd ennd_sample.rb
even
odd
even
odd
even
odd
even
odd
even
odd
例5




“end”なしでインデントで
      endしたい
すでにendless rubyが
  あるのでパス
例6




 縦書きで
Rubyを書く
サンプル(tate_sample.rb)
      e           (
      n           1
      d  e     i  .
         n     f  .
         d  p     2
            u  (  0
            t  i  )
            s     .
               %  e
            i     a
               2  c
               )  h
                   
               =  d
               =  o
                   
               0  |
                  i
                  |
フィルタ(tate.rb)

def source_filter(str)
  str.split(/n/).inject([]) {|r, s|
    s.each_char.with_index do |c, i|
      r[i] = r[i] ? r[i] + c : c
    end
    r
  }.reverse.join("n")
end
実行

$ ruby ­rtate tate_sample.rb
2
4
6
8
10
12
14
16
18
20
例7




スペースとタブで
 Rubyを書く
サンプル(bleach_sample.rb)



    見えません
フィルタ(bleach.rb)

def source_filter(str)
  [str.delete("^ t").
   tr(" t", '01') ].pack('b*')
end
実行

$ ruby ­rbleach bleach_sample.rb
Hello, World!
例8



  見えないとつまらないので

“ダァシエリイェス!!”で
   Rubyを書く
サンプル(kq_sample.rb)
ダァダァダァダァシエリイェス!!シエリイェス!!
シエリイェス!!ダァシエリイェス!!ダァシエリイェス!!ダァ
シエリイェス!!シエリイェス!!シエリイェス!!ダァダァダァ
シエリイェス!!ダァシエリイェス!!シエリイェス!!シエリイェス!!ダァ
シエリイェス!!シエリイェス!!ダァダァシエリイェス!!シエリイェス!!
シエリイェス!!ダァダァダァダァダァ
ダァシエリイェス!!ダァダァシエリイェス!!シエリイェス!!
シエリイェス!!ダァダァシエリイェス!!ダァダァ
ダァダァダァシエリイェス!!ダァダァ
シエリイェス!!ダァシエリイェス!!ダァシエリイェス!!ダァ
ダァシエリイェス!!シエリイェス!!ダァダァダァ
シエリイェス!!シエリイェス!!ダァシエリイェス!!シエリイェス!!ダァ
ダァダァシエリイェス!!シエリイェス!!ダァシエリイェス!!
シエリイェス!!ダァシエリイェス!!シエリイェス!!シエリイェス!!シエリイェス!!
ダァシエリイェス!!シエリイェス!!ダァダァダァ
シエリイェス!!シエリイェス!!ダァシエリイェス!!ダァダァ
ダァダァダァダァダァシエリイェス!!
ダァダァシエリイェス!!シエリイェス!!シエリイェス!!ダァ
シエリイェス!!ダァシエリイェス!!ダァシエリイェス!!シエリイェス!!
シエリイェス!!シエリイェス!!ダァシエリイェス!!シエリイェス!!ダァ
ダァシエリイェス!!ダァダァシエリイェス!!シエリイェス!!
シエリイェス!!ダァダァダァシエリイェス!!シエリイェス!!
ダァシエリイェス!!シエリイェス!!ダァダァダァ
シエリイェス!!ダァダァシエリイェス!!シエリイェス!!ダァ
シエリイェス!!ダァダァダァダァシエリイェス!!
ダァダァシエリイェス!!シエリイェス!!シエリイェス!!ダァ
ダァシエリイェス!!ダァダァダァシエリイェス!!
ダァシエリイェス!!ダァダァダァダァ
フィルタ(kq.rb)

# ­*­ coding: utf­8 ­*­
def source_filter(str)
  [str.force_encoding(Encoding::UTF_8).
   scan(/ダァ|シエリイェス!!/).
   map {|s| s == 'ダァ' ? '0' : '1' }.
   join('') ].pack('b*')
end
実行

$ ruby ­rkq kq_sample.rb
Hello, World!
終

メタメタプログラミングRuby