自己生成プログラム
実行すると自分自身のコードを吐き出すプログラム。こういうのってなんていうんだっけ。自己複製?
コード→文字列の変換のエスケープが不要になるrubyが一番作りやすくて、しくみも理解しやすいかも。
注意したところは、改行文字を直接使わないようにしたこと。そうすることで改行が機種ごとに違っても二段階以降は同じものが生成されるようになるから。あと、コードを無用に切り詰めないこと。
ruby
lines = %{ print "lines = %{" print lines print "}" print lines } print "lines = %{" print lines print "}" print lines
(要最後の空行)
もしくは
lines = %{ puts "lines = %{" puts lines.strip puts "}" puts lines.strip } puts "lines = %{" puts lines.strip puts "}" puts lines.strip
python
lines = r""" print("lines = r\"\"\"") print(lines.strip()) print("\"\"\"") print(lines.strip()) """ print("lines = r\"\"\"") print(lines.strip()) print("\"\"\"") print(lines.strip())
もしくは
lines = [ 'print("lines = [")', 'for line in lines: print("%s," % repr(line))', 'print("]")', 'for line in lines: print(line)', ] print("lines = [") for line in lines: print("%s," % repr(line)) print("]") for line in lines: print(line)
haskell
(ここからはコードをエスケープして文字列リテラルに埋め込んでますが、下部のコードと同じ内容である点は一緒です)
ls = [ "main = do", " putStrLn \"ls = [\"", " mapM_ (putStrLn.stringline) contents", " putStrLn \" \\\"\\\"]\"", " mapM_ putStrLn contents", " where", " contents = reverse.tail.reverse $ ls", " stringline line = \" \" ++ show line ++ \",\"", ""] main = do putStrLn "ls = [" mapM_ (putStrLn.stringline) contents putStrLn " \"\"]" mapM_ putStrLn contents where contents = reverse.tail.reverse $ ls stringline line = " " ++ show line ++ ","
c
static const char * const lines[] = { "int putchar(int);", "int puts(const char *);", "void eputs(const char *str) {", " putchar(\' \'); putchar(\' \'); putchar(\'\\\"\');", " for (; *str; ++str) {", " char ch = *str;", " if (ch == \'\\\\\' || ch == \'\\\'\' || ch == \'\\\"\') putchar(\'\\\\\');", " putchar(ch);", " }", " puts(\"\\\",\");", "}", "int main() {", " const char * const *cursor;", " puts(\"static const char * const lines[] = {\");", " for (cursor = lines; *cursor; ++cursor) eputs(*cursor);", " puts(\" 0};\");", " for (cursor = lines; *cursor; ++cursor) puts(*cursor);", " return 0;", "}", 0}; int putchar(int); int puts(const char *); void eputs(const char *str) { putchar(' '); putchar(' '); putchar('\"'); for (; *str; ++str) { char ch = *str; if (ch == '\\' || ch == '\'' || ch == '\"') putchar('\\'); putchar(ch); } puts("\","); } int main() { const char * const *cursor; puts("static const char * const lines[] = {"); for (cursor = lines; *cursor; ++cursor) eputs(*cursor); puts(" 0};"); for (cursor = lines; *cursor; ++cursor) puts(*cursor); return 0; }
java
class Self { private static final String[] lines = { " public static void main(String[] args) {", " System.out.println(\"class Self {\");", " System.out.println(\" private static final String[] lines = {\");", " for (int i = 0; i < lines.length; i += 1) printStr(lines[i]);", " System.out.println(\" };\");", " for (int i = 0; i < lines.length; i += 1) System.out.println(lines[i]);", " System.out.println(\"}\");", " }", " private static void printStr(String line) {", " System.out.print(\" \\\"\");", " for (int i = 0; i < line.length(); i += 1) {", " char ch = line.charAt(i);", " if (ch == \'\\\'\' || ch == \'\\\"\' || ch == \'\\\\\') System.out.print(\'\\\\\');", " System.out.print(ch);", " }", " System.out.println(\"\\\",\");", " };", }; public static void main(String[] args) { System.out.println("class Self {"); System.out.println(" private static final String[] lines = {"); for (int i = 0; i < lines.length; i += 1) printStr(lines[i]); System.out.println(" };"); for (int i = 0; i < lines.length; i += 1) System.out.println(lines[i]); System.out.println("}"); } private static void printStr(String line) { System.out.print(" \""); for (int i = 0; i < line.length(); i += 1) { char ch = line.charAt(i); if (ch == '\'' || ch == '\"' || ch == '\\') System.out.print('\\'); System.out.print(ch); } System.out.println("\","); }; }
しくみ
以下のような構成でつくると自己生成するコードになる
コードの前半部 コード後半部埋め込みリテラル コード後半部 mainコード 前半部を出力 後半部リテラル内容をリテラル形式に変換して出力 後半部をリテラルから出力 ユーティリティコード リテラル形式変換 ...
作り方は以下のような感じ
本質は、コードをリテラルにして、実行でそれを二つ出力させること。一つはコード本体として、もう一つは出力させるリテラルデータとして。そしてそのために、そのリテラル自体をリテラル形式に変換すること。
このタイプで有名なコードはprintfなどで、format文字列を使うタイプで、format文字列自体を適用させて使うものです。以下は、フォーマットを使うruby版です:
s = %{s = %%{%s};puts sprintf(s, s)};puts sprintf(s, s)
そのPython版:
s = 's = %s;print(s %% repr(s))';print(s % repr(s))
ダブルクオートのASCIIコードを利用した場合:
s = "s = %c%s%c;print(s %% (34, s, 34))";print(s % (34, s, 34))
これを省略を多用したcにした場合(以下のコードはEOFまで改行なしにする)
main(){char*s="main(){char*s=%c%s%c;printf(s,34,s,34);}";printf(s,34,s,34);}
これに似たコードが良く見かけるタイプだと思います。(出る警告は、mainの戻り値が暗黙int、printfが暗黙、mainのreturnがない、改行なしでEOF)
コードの入ったformat文字にそのformatを埋め込むことで、コード部分を二つ出力させていることになっているんですね。