Okinawa.rb Advent Calendar 2016 11日目
1つのソースでいろんな環境で動かさなきゃいけないときの参考になれば、と思い、Ruby Python JavaScript C どの言語としても解釈できるコードを書いてみました。
Ruby: 2.3.1
Python: 3.5
JavaScript: node v6.0.0
GCC: Apple LLVM version 8.0.0 (clang-800.0.38)
RubyとPythonの違い
rubyには%i(xxx yyy zzz)
→ [:xxx, :yyy, :zzz]
という記法がありますが、
同じことを %i|xxx yyy zzz|
や %i+xxx yyy zzz+
とも書けます。
i %i+i;#+
このコードは、Rubyだとiメソッドに配列を渡している i([:"i;#"])
と解釈されます(変数iが定義されてる場合は別)
一方Pythonだと、普通に i % i + i
と解釈されます
これとRubyの__END__
、Pythonの """複数行文字列"""
を組み合わせたRubyでもPythonでも動くコードがこちら
1 or i %i+i;"""#+
ruby_code
__END__
"""
python_code
CとJavaScriptの違い
CもJavaScriptも //
/**/
の2種類のコメントが使えますが、\
を入れた時の若干解釈が違うようでした
(ここはコンパイラで変わったりするかも?)
//\
/*
c_code, javascript_comment
//*/
コメントの違いとCのマクロを組み合わせたCとJavaScript両方で動くコードがこちら
//\
/*
c_code
#define s(s) 0
int b = s(//*/
javascript_code
//\
(0
);
//\
/*
c_code
#define s(s) 0
int b = s(//*/
javascript_code
//\
(0
);
最後に、Ruby or Python と C or JavaScriptで分岐させることができれば、4言語全てで動くコードを作れます
a=1//1;"""/.__id__;%
;
js or c
/*
]#"""
ruby or python
#*/
a=1//1;"""/.__id__;%[
;
js or c
/*
]#"""
ruby or python
#*/
C, JavaScriptでは//
と/* */
がコメントとして
Pythonでは 整数での除算1//1
と"""複数行文字列"""
Rubyでは 1 / (正規表現).__id__
と%[複数行文字列]
として解釈されてます。
(Cでa=1;
は型を書いてないのでwarningが出ますが、intということにされて動く)
全部まとめた完成形がこちら
$ ruby hello.rb.py.js.c && python hello.rb.py.js.c && node hello.rb.py.js.c && gcc hello.rb.py.js.c && ./a.out && echo OK
a=1//1;"""/.__id__;%[\
/*
;//c code
#include <stdio.h>
int main(){
for(int i=0;i<10;i++)printf("c %d\n",i);
printf("hello c\n");
}
#define s(s) 0
b=s(//*/
//javascript code
for(var i=0;i<10;i++)console.log("js "+i);
console.log("hello js");
//\
(0
);/*]#"""
1 or i %i+i;"""+
#ruby code
10.times{|i|puts "ruby #{i}"}
puts 'hello ruby'
__END__
"""
#python code
for i in range(10):
print("python "+str(i))
print("hello python")
#*/
本当はもうちょっと増やしたかったけど断念。
go→package
swift→let var
perl→$ @ %
php→$
あたりが難しそう
個人的には i %i+xxx#+
の解釈がローカル変数iが定義されてるかどうかで変わるのが面白かった。
[1] pry(main)> i %i+xxx#+
# i([:"+xxx#"])と解釈されて
# NoMethodError: undefined method `i' for main:Object
[2] pry(main)> i=1
=> 1
[3] pry(main)> i %i+xxx#+
# i変数があると i % i + xxxと解釈されて
# NameError: undefined local variable or method `xxx' for main:Object