• Quineを書きやすい言語
• evalや %() %w() などの記法が充実
• Quine + α で何をするかが大事
• Ruby committer 遠藤さんの本があるよ
Quine friendlyなRuby
RubyとQuine
eval s=%(code)
eval s=%w(c o d e w i t h s p a c e).join
• インデント・改行はフォーマットの前処理で消す
• 消し方:セミコロンに置き換えるか単に消すかはRipperで判定
標準ライブラリRipperに任せる
def ast(code) = 位置情報を除去(Ripper.sexp(code)
)
original_ast = ast(original_code
)
minimized_code = original_cod
e
while minimized_code.match? /n/
a = minimized_code.sub(/n */, ''
)
b = minimized_code.sub(/n */, ';'
)
if ast(a) == original_as
t
minimized_code =
a
elsif ast(b) == original_as
t
minimized_code =
b
else
rais
e
end
end
元のコードのS式
インデント・改行を消したS式
セミコロンに置き換えたS式
変化していなければ消してOK
変化(localvar)
rule = $*[00]&.to_i|| 3
0
h ||= $*[01]&.to_i || 3
2
[ % if % TRICK-2022 %% %
]
# ] if a /%{(?<a>)/ =~ %} if + %} - %
[
# } if b /%<(?<b>)/ =~ %> if - %> + %
{
# > if c /%+(?<c>)/ =~ %+ if + %+ - %
<
# + if d /%-(?<d>)/ =~ %- if - %- + %
+
# - if e f, g, h, / =~ %/ if def e(f, g, h, *)
=
# + if d /%-(?<d>)/ =~ %- if g.map! do rule.[] _1.join.to_i 2 end.
!
# > if c /%+(?<c>)/ =~ %+ if !(g = *[g[-1], *g, g[0]].each_cons(3)
)
# } if b /%<(?<b>)/ =~ %> if puts(g.join.tr '01', ' #') || h > 0 &
&
# ] if a /%{(?<a>)/ =~ %} if !g ||= [*g = [0] * h, 1] + g if h -=
1
eval(File.read f ||= __FILE__)
https://github.com/tric/trick2022/tree/master/04-tompng
https://gist.github.com/tompng/00772bba6a49a467778b072cb78f7aa9
姿1 初期状態
5つの姿に変化する
rule =$*[00]&.to_i || 30
h ||= $*[01]&.to_i || 32
[ % if % TRICK-2022 %% %]
# ] if a /%{(?<a>)/ =~ %} if + %} - %[
# } if b /%<(?<b>)/ =~ %> if - %> + %{
# > if c /%+(?<c>)/ =~ %+ if + %+ - %<
# + if d /%-(?<d>)/ =~ %- if - %- + %+
# - if e f, g, h, / =~ %/ if def e(f, g, h, *) =
# + if d /%-(?<d>)/ =~ %- if g.map! do rule.[] _1.join.to_i 2 end.!
# > if c /%+(?<c>)/ =~ %+ if !(g = *[g[-1], *g, g[0]].each_cons(3))
# } if b /%<(?<b>)/ =~ %> if puts(g.join.tr '01', ' #') || h > 0 &&
# ] if a /%{(?<a>)/ =~ %} if !g ||= [*g = [0] * h, 1] + g if h -= 1
eval(File.read f ||= __FILE__)
79.
姿2
5つの姿に変化する
rule = $*[00]&.to_i|| 30
h ||= $*[01]&.to_i || 32
[ % if % TRICK-2022 %% %]
# ] if a /%{(?<a>)/ =~ %} if + %} - %[
# } if b /%<(?<b>)/ =~ %> if - %> + %{
# > if c /%+(?<c>)/ =~ %+ if + %+ - %<
# + if d /%-(?<d>)/ =~ %- if - %- + %+
# - if e f, g, h, / =~ %/ if def e(f, g, h, *) =
# + if d /%-(?<d>)/ =~ %- if g.map! do rule.[] _1.join.to_i 2 end.!
# > if c /%+(?<c>)/ =~ %+ if !(g = *[g[-1], *g, g[0]].each_cons(3))
# } if b /%<(?<b>)/ =~ %> if puts(g.join.tr '01', ' #') || h > 0 &
&
# ] if a /%{(?<a>)/ =~ %} if !g ||= [*g = [0] * h, 1] + g if h -= 1
eval(File.read f ||= __FILE__)
80.
姿3
5つの姿に変化する
rule = $*[00]&.to_i|| 30
h ||= $*[01]&.to_i || 32
[ % if % TRICK-2022 %% %]
# ] if a /%{(?<a>)/ =~ %} if + %} - %[
# } if b /%<(?<b>)/ =~ %> if - %> + %{
# > if c /%+(?<c>)/ =~ %+ if + %+ - %<
# + if d /%-(?<d>)/ =~ %- if - %- + %+
# - if e f, g, h, / =~ %/ if def e(f, g, h, *) =
# + if d /%-(?<d>)/ =~ %- if g.map! do rule.[] _1.join.to_i 2 end.!
# > if c /%+(?<c>)/ =~ %+ if !(g = *[g[-1], *g, g[0]].each_cons(3)
)
# } if b /%<(?<b>)/ =~ %> if puts(g.join.tr '01', ' #') || h > 0 &&
# ] if a /%{(?<a>)/ =~ %} if !g ||= [*g = [0] * h, 1] + g if h -= 1
eval(File.read f ||= __FILE__)
81.
姿4
5つの姿に変化する
rule = $*[00]&.to_i|| 30
h ||= $*[01]&.to_i || 32
[ % if % TRICK-2022 %% %]
# ] if a /%{(?<a>)/ =~ %} if + %} - %[
# } if b /%<(?<b>)/ =~ %> if - %> + %{
# > if c /%+(?<c>)/ =~ %+ if + %+ - %<
# + if d /%-(?<d>)/ =~ %- if - %- + %+
# - if e f, g, h, / =~ %/ if def e(f, g, h, *) =
# + if d /%-(?<d>)/ =~ %- if g.map! do rule.[] _1.join.to_i 2 end.
!
# > if c /%+(?<c>)/ =~ %+ if !(g = *[g[-1], *g, g[0]].each_cons(3))
# } if b /%<(?<b>)/ =~ %> if puts(g.join.tr '01', ' #') || h > 0 &&
# ] if a /%{(?<a>)/ =~ %} if !g ||= [*g = [0] * h, 1] + g if h -= 1
eval(File.read f ||= __FILE__)
82.
姿5
5つの姿に変化する
rule = $*[00]&.to_i|| 30
h ||= $*[01]&.to_i || 32
[ % if % TRICK-2022 %% %]
# ] if a /%{(?<a>)/ =~ %} if + %} - %[
# } if b /%<(?<b>)/ =~ %> if - %> + %{
# > if c /%+(?<c>)/ =~ %+ if + %+ - %<
# + if d /%-(?<d>)/ =~ %- if - %- + %+
# - if e f, g, h, / =~ %/ if def e(f, g, h, *)
=
# + if d /%-(?<d>)/ =~ %- if g.map! do rule.[] _1.join.to_i 2 end.!
# > if c /%+(?<c>)/ =~ %+ if !(g = *[g[-1], *g, g[0]].each_cons(3))
# } if b /%<(?<b>)/ =~ %> if puts(g.join.tr '01', ' #') || h > 0 &&
# ] if a /%{(?<a>)/ =~ %} if !g ||= [*g = [0] * h, 1] + g if h -= 1
eval(File.read f ||= __FILE__)
83.
初期状態に戻る
5つの姿に変化する
rule = $*[00]&.to_i|| 30
h ||= $*[01]&.to_i || 32
[ % if % TRICK-2022 %% %]
# ] if a /%{(?<a>)/ =~ %} if + %} - %[
# } if b /%<(?<b>)/ =~ %> if - %> + %{
# > if c /%+(?<c>)/ =~ %+ if + %+ - %<
# + if d /%-(?<d>)/ =~ %- if - %- + %+
# - if e f, g, h, / =~ %/ if def e(f, g, h, *) =
# + if d /%-(?<d>)/ =~ %- if g.map! do rule.[] _1.join.to_i 2 end.!
# > if c /%+(?<c>)/ =~ %+ if !(g = *[g[-1], *g, g[0]].each_cons(3))
# } if b /%<(?<b>)/ =~ %> if puts(g.join.tr '01', ' #') || h > 0 &&
# ] if a /%{(?<a>)/ =~ %} if !g ||= [*g = [0] * h, 1] + g if h -= 1
eval(File.read f ||= __FILE__)
84.
下から逆順に実行される
Most Orderly
rule =$*[00]&.to_i || 3
0
h ||= $*[01]&.to_i || 3
2
[ % if % TRICK-2022 %% %
]
# ] if a /%{(?<a>)/ =~ %} if + %} - %
[
# } if b /%<(?<b>)/ =~ %> if - %> + %
{
# > if c /%+(?<c>)/ =~ %+ if + %+ - %
<
# + if d /%-(?<d>)/ =~ %- if - %- + %
+
# - if e f, g, h, / =~ %/ if def e(f, g, h, *)
=
# + if d /%-(?<d>)/ =~ %- if g.map! do rule.[] _1.join.to_i 2 end.
!
# > if c /%+(?<c>)/ =~ %+ if !(g = *[g[-1], *g, g[0]].each_cons(3)
)
# } if b /%<(?<b>)/ =~ %> if puts(g.join.tr '01', ' #') || h > 0 &
&
# ] if a /%{(?<a>)/ =~ %} if !g ||= [*g = [0] * h, 1] + g if h -= 1
eval(File.read f ||= __FILE__)
85.
ローカル変数のTRICK
a /2#/ ←a(regexp)
a = 4
a /2#/ ← a / 2 # comment
/(?<a>)/ =~ '' ← ローカル変数aが定義される
a /%{(?<a>)/ =~ '' if false #} if fals
e
a /%{(?<a>)/ =~ '' if false #} if false
eval File.read(__FILE__)
01/31 ローカル変数に普通に代入, defa(*)=1
変更履歴
rule = ARGV[0]&.to_i || 3
0
n = ARGV[1]&.to_i || 3
2
f ||= __FILE_
_
def a(*) = def b(*) = def c(*) = def d(*) =
1
a /a+%!/i, a = %
(
# !.ord; b /b+%?/i, b = %
{
# ?.ord; c /c+%$/i, c = %
[
# $.ord; d /d+%@/i, d = %
|
# @.ord; e s, i, f if i < n && def e(s, i, f) = self
.
# |.ord; s = t.map { rule[_1.join.to_i 2] }
;
# ].ord; t = s.rotate(-1).zip s, s.rotate(1)
;
# }.ord; puts s.map { (32 + 3 * _1).chr }.join
;
# ).ord; s ||= [*[0] * n, 1, *[0] * n]; i = i.to_i + 1
;
eval(File.read f
)
88.
02/01 def a(*)= 1を消す
変更履歴
rule = ARGV[0]&.to_i || 3
0
h ||= ARGV[1]&.to_i || 3
2
f ||= __FILE_
_
a /e+%{/i, a = %
[
# } if false; b /f+%</m, b = %
(
# > if false; c /g+%+/n, c = %
>
# + if false; d /h+%-/o, d = %
}
# - if false; e f, g, h if def e(f, g, h) = % &
&
# } if false; g = i.map { rule.[] _1.join.to_i 2
}
# > if false; i = g.rotate(-1).zip(g, g.rotate(1)
)
# ) if false; puts g.join.tr('01', ' #'); 0 < h &
&
# ] if false; h -= 1; g ||= [*i = [0] * h, 1, *i
]
eval(File.read f)
89.
02/08 named captureでローカル変数定義
変更履歴
rule= ARGV[0]&.to_i || 3
0
h ||= ARGV[1]&.to_i || 3
2
f ||= __FILE_
_
a /l*%{(?<a>)/i =~ %
[
# } if false; b /k*%<(?<b>)/m =~ %
(
# > if false; c /j*%+(?<c>)/n =~ %
}
# + if false; d /i*%-(?<d>)/o =~ %
>
# - if false; e f, g, h if def e(f, g, h) = % &
&
# > if false; g.map! do rule.[] _1.join.to_i 2 en
d
# } if false; g = *([g[-1], *g, g[0]].each_cons 3
)
# ) if false; puts g.join.tr('01', ' #'); 0 < h &
&
# ] if false; h = ~-h; g ||= [*i = [0] * h, 1] +
i
eval(File.read f)
90.
02/17 縦のラインを揃える
変更履歴
rule =$*[00]&.to_i || 3
0
h ||= $*[01]&.to_i || 3
2
% # if a /%{(?<a>)/ =~ %} if +%} - %
[
# } if b /%<(?<b>)/ =~ %> if -%> + %
(
# > if c /%+(?<c>)/ =~ %+ if +%+ - %
}
# + if d /%-(?<d>)/ =~ %- if -%- + %
>
# - if e f, g, h, / =~ %/ if def e(f, g, h, *)
=
# > if d /%-(?<d>)/ =~ %- if g.map! do rule.[] _1.join.to_i 2 end.
!
# } if c /%+(?<c>)/ =~ %+ if !(g = *[g[-1], *g, g[0]].each_cons(3)
)
# ) if b /%<(?<b>)/ =~ %> if puts(g.join.tr '01', ' #') || 0 < h &
&
# ] if a /%{(?<a>)/ =~ %} if !g ||= [*g = [0] * h, 1] + g if h -=
1
eval(File.read f ||= __FILE__)
91.
02/23 より上下対称に
変更履歴
rule =$*[00]&.to_i || 3
0
h ||= $*[01]&.to_i || 3
2
[ % if % TRICK-2022 %% %
]
# ] if a /%{(?<a>)/ =~ %} if + %} - %
[
# } if b /%<(?<b>)/ =~ %> if - %> + %
{
# > if c /%+(?<c>)/ =~ %+ if + %+ - %
<
# + if d /%-(?<d>)/ =~ %- if - %- + %
+
# - if e f, g, h, / =~ %/ if def e(f, g, h, *)
=
# + if d /%-(?<d>)/ =~ %- if g.map! do rule.[] _1.join.to_i 2 end.
!
# > if c /%+(?<c>)/ =~ %+ if !(g = *[g[-1], *g, g[0]].each_cons(3)
)
# } if b /%<(?<b>)/ =~ %> if puts(g.join.tr '01', ' #') || h > 0 &
&
# ] if a /%{(?<a>)/ =~ %} if !g ||= [*g = [0] * h, 1] + g if h -=
1
eval(File.read f ||= __FILE__)
[tomoya:localvar]% ruby strict_colorizer.rb-n 5 localvar.r
b
rule = $*[00]&.to_i || 30
h ||= $*[01]&.to_i || 32
[ % if % TRICK-2022 %% %]
# ] if a /%{(?<a>)/ =~ %} if + %} - %[
# } if b /%<(?<b>)/ =~ %> if - %> + %{
# > if c /%+(?<c>)/ =~ %+ if + %+ - %<
# + if d /%-(?<d>)/ =~ %- if - %- + %+
# - if e f, g, h, / =~ %/ if def e(f, g, h, *) =
# + if d /%-(?<d>)/ =~ %- if g.map! do rule.[] _1.join.to_i 2 end.!
# > if c /%+(?<c>)/ =~ %+ if !(g = *[g[-1], *g, g[0]].each_cons(3))
# } if b /%<(?<b>)/ =~ %> if puts(g.join.tr '01', ' #') || h > 0 &&
# ] if a /%{(?<a>)/ =~ %} if !g ||= [*g = [0] * h, 1] + g if h -= 1
eval(File.read f ||= __FILE__
)
rule = $*[00]&.to_i || 30
h ||= $*[01]&.to_i || 32
[ % if % TRICK-2022 %% %]
# ] if a /%{(?<a>)/ =~ %} if + %} - %[
# } if b /%<(?<b>)/ =~ %> if - %> + %{
# > if c /%+(?<c>)/ =~ %+ if + %+ - %<
# + if d /%-(?<d>)/ =~ %- if - %- + %+
# - if e f, g, h, / =~ %/ if def e(f, g, h, *) =
# + if d /%-(?<d>)/ =~ %- if g.map! do rule.[] _1.join.to_i 2 end.!
# > if c /%+(?<c>)/ =~ %+ if !(g = *[g[-1], *g, g[0]].each_cons(3))
# } if b /%<(?<b>)/ =~ %> if puts(g.join.tr '01', ' #') || h > 0 &
&
# ] if a /%{(?<a>)/ =~ %} if !g ||= [*g = [0] * h, 1] + g if h -= 1
eval(File.read f ||= __FILE__
)
rule = $*[00]&.to_i || 30
h ||= $*[01]&.to_i || 32
[ % if % TRICK-2022 %% %]
# ] if a /%{(?<a>)/ =~ %} if + %} - %[
# } if b /%<(?<b>)/ =~ %> if - %> + %{
# > if c /%+(?<c>)/ =~ %+ if + %+ - %<
# + if d /%-(?<d>)/ =~ %- if - %- + %+
# - if e f, g, h, / =~ %/ if def e(f, g, h, *) =
# + if d /%-(?<d>)/ =~ %- if g.map! do rule.[] _1.join.to_i 2 end.!
# > if c /%+(?<c>)/ =~ %+ if !(g = *[g[-1], *g, g[0]].each_cons(3)
)
# } if b /%<(?<b>)/ =~ %> if puts(g.join.tr '01', ' #') || h > 0 &&
# ] if a /%{(?<a>)/ =~ %} if !g ||= [*g = [0] * h, 1] + g if h -= 1
eval(File.read f ||= __FILE__
)
rule = $*[00]&.to_i || 30
h ||= $*[01]&.to_i || 32
[ % if % TRICK-2022 %% %]
# ] if a /%{(?<a>)/ =~ %} if + %} - %[
# } if b /%<(?<b>)/ =~ %> if - %> + %{
# > if c /%+(?<c>)/ =~ %+ if + %+ - %<
line.untaint if RUBY_VERSION< '2.7'
if IRB.conf[:MEASURE] && IRB.conf[:MEASURE_CALLBACKS].empty
?
IRB.set_measure_callbac
k
end
+ # Assignment expression check should be done before @context.evaluate to handle
code like `a /2#/ if false; a = 1`
+ is_assignment = assignment_expression?(line)
if IRB.conf[:MEASURE] && !IRB.conf[:MEASURE_CALLBACKS].empty
?
..
.
@context.evaluate(line, line_no, exception: exc
)
..
.
end
if @context.echo
?
- if assignment_expression?(line)
+ if is_assignment
if @context.echo_on_assignment
?
output_value(@context.echo_on_assignment? == :truncate
)
end
else
output_valu
e
end
end
assignment̲expression?(line) を移動しただけ
IRB 謎の変更
← evaluate
Rubyの仕様の安定化に役立つ…かもしれない
IRBにとって辛そうなコード
Did = <<you,:
know #{:that}
you
# can nest
symbol
?
# => ["know thatn", :symbol?]
for
i in
j do
puts :hello
end
# combinatio
n
def self.for(x = <<~A + :
heredoc#{:in
}
A
# comment
do
)
=begi
n
embdo
c
=en
d
=
1