ASCII の記号([[:punct:]] = !"#$%&'()*+,-./:;<=>?@[\]^_{|}~`)だけで書かれた、
実際に動く Perl プログラム集。英数字・空白は 1 文字も使っていません。
perl japh.pl # Just another Perl hacker,
perl hello-ja.pl # こんにちは、記号だけの世界!
perl fizzbuzz.pl # FizzBuzz 1..100
perl sierpinski.pl # シェルピンスキーのピラミッド (AA)
echo 'Hello' | perl rev.pl # olleH (バイト単位の反転なので ASCII 向き)検証環境: perl 5.42.0 (darwin)。全プログラム perl -w で無警告。
Perl の | ^ ~ は「両辺が文字列なら文字列のビット演算」になる。
記号のコードポイントを組み合わせれば任意のバイトが作れる。
('`'|'%') # 0x60 | 0x25 = 0x65 -> 'e'
('^'^'.') # 0x5e ^ 0x2e = 0x70 -> 'p'
('|'^'/') # 0x7c ^ 0x2f = 0x53 -> 'S'
('`'^'@') # 0x60 ^ 0x40 = 0x20 -> ' '
('!'^'.'^':') # 3 つ XOR すると数字 '1' も作れる
(~('!'^'#'^'@')) # ~ で 0x80 以上へ → UTF-8 の日本語も出せるenc.pl / strenc.pl がこの変換を総当たりでやる。
print と書けないので、グロブに対して文字列で組み立てたメソッド名を呼ぶ。
ハンドルへのメソッド呼び出しでは IO::File が自動ロードされる。
$;='STDOUT'; $:='print'; # 実際は上のビット演算で組む
*{$;}->$:('...'); # = print STDOUT '...'&{'CORE::sprintf'}(...) のようにシンボリック参照で組み込み関数も呼べる
(sprintf join chr ord length reverse substr uc warn などは可。
print sort eval require は不可)。
for も while も書けない。そこでリテラル正規表現の中に (?{...}) を置き、
末尾の (?!)(常に失敗)でバックトラックを強制する。
$_ の 1 文字ごとに開始位置がずれて再試行されるので、
文字列長 = ループ回数になる。
$_ = &{'CORE::sprintf'}('%100s',''); # 100 個の空白 = 100 回まわす
/.(?{ ...ここが loop body... })(?!)/;注意: perl 5.42 では実行時に組み立てたパターン内の (?{...}) は
Eval-group not allowed at runtime で禁止される(use re 'eval' が必要)。
Acme::EyeDrops 的な ''=~('('.'?'.'{'. ... ) はもう通らないので、
(?{...}) はソースにリテラルで書き、中身だけを記号で構成している。
- 変数:
$;$:$?$%$-$=$~$"$@$_@_、 さらに${'!!'}や$_{'!'}で無限に増やせる - 数値:
!''が 1、$?-$?が 0、++$?でインクリメント、 大きい数は('!'^'.'^':')などで"100"を作って数値文脈に置く - 条件分岐:
?:&&||
※ C/Python の -~$x == $x+1 は Perl では使えない(~ が符号なしを返すため
-~0 は -1.8e19 になる)。++ を使う。
| ファイル | 中身 |
|---|---|
enc.pl |
文字列 → 記号だけの式 に変換するライブラリ (E()) |
strenc.pl |
上の CLI。perl strenc.pl 'print' |
build.pl |
骨組みを組んで下の 5 本を生成する。perl build.pl |
japh.pl hello-ja.pl fizzbuzz.pl sierpinski.pl rev.pl |
生成物(記号のみ) |
build.pl は生成後に「英数字・空白が混ざっていないか」も検査する。
fizzbuzz.pl:
$; = 'STDOUT'; $: = 'print'; $" = '';
$_ = &{'CORE::sprintf'}('%100s', '');
/.(?{ $?++;
$" .= ($? % 15 ? ($? % 3 ? ($? % 5 ? $? : 'Buzz') : 'Fizz') : 'FizzBuzz') . "\n"
})(?!)/;
*{$;}->$:($");sierpinski.pl:
$; = 'STDOUT'; $: = 'print';
$- = 16; # 行数
$= = 32; # 桁数
$@ = 0; $" = '';
$_ = &{'CORE::sprintf'}('%512s', '');
/.(?{ $~ = $? - $- + 1 + $%; # x = col - rows + 1 + row
$" .= (($~ < $@) || ($~ > $%) || ($~ & ($% - $~))) ? ' ' : '#';
($= <= ++$?) && ($? -= $?, ++$%, $" .= "\n") # 行末で折り返す
})(?!)/;
*{$;}->$:($");