Page 1 of 1

Quine Relay: a circular quine in 50 languages

Posted: Tue Dec 03, 2013 12:33 am UTC
by 3rdtry
I thought this was so cool it deserved its own thread: Quine relay, an uroboros program with 50 programming languages. As its name indicates, it's a Ruby program that when run outputs a Scala program, that when run outputs a Scheme program, and so on with Bash, Smalltalk, Tcl, Unlambda, Vala, Verilog, Whitespace, Ada, ALGOL69, Awk, Boo, Brainfuck, C, C++, C#, Clojure, Cobol, CoffeeScript, Common Lisp, Forth, FORTRAN77, Fortran90, Go, Groovy, Haskell, Icon, INTERCAL, Jasmin, Java, LLVM asm, Logo, Lua, Makefile, MSIL, NodeJS, Objective-C, OCaml, Octave, Parrot, Pascal, Perl, PHP, Pike, Prolog, Python, R and REXX, which outputs back the original Ruby program.

This is its code, and this is the first iteration. I haven't tested all of them (stupid buggy scheme-mit package in Ubuntu) but I'm assuming they work.

I mean, I often struggle to see how a quine works in just one language, but 50? In just 54 lines of code? Including Brainfuck, Whitespace and Intercal? :shock:

The guy who made it has also made tons of other cool things, including "ASCII fluid dynamics".

Re: Quine Relay: a circular quine in 50 languages

Posted: Tue Dec 03, 2013 9:30 am UTC
by Jplus
FWIW, there's a name for this kind of thing: iterative multiquine.

Anyway, yes, it's very cool.

Re: Quine Relay: a circular quine in 50 languages

Posted: Sat Dec 14, 2013 2:40 am UTC
by skeptical scientist
3rdtry wrote:I thought this was so cool it deserved its own thread: [url=http://pastebin.com/3694KjJf]this is the first iteration.

That is some extreme quote escaping.

I'm not sure how the creator did it, but I know how I would make a multiquine. The basic idea of a simple quine is something like this python quine.

Code: Select all

_PROGRAM = '_PROGRAM = %s\nprint _PROGRAM %% repr(_PROGRAM)'
print _PROGRAM % repr(_PROGRAM)

Nothing clever, just a straightforward use of a simple skeleton,

Code: Select all

_PROGRAM = %s
print _PROGRAM % repr(_PROGRAM)

and we fill out the skeleton by replacing the placeholder %s with the skeleton, modified to be a python string literal. Python (which this is written in) already has a repr() function which is perfect for this, because it escapes our string to produce the appropriate string literal.

In another language, we might have to write our own version of repr (which doesn't have to be perfect, just sufficient for the string we are using). An example is this C quine.

Code: Select all

#include <stdio.h>

char* _PROGRAM = "#include <stdio.h>\n\nchar* _PROGRAM = %s;\n\nvoid repr(const char* input, char* output) {\n  *output++ = \'\"\';\n  while (*input != \'\\0\') {\n    switch (*input) {\n      case \'\\n\':\n        *output++ = \'\\\\\';\n        *output++ = \'n\';\n        break;\n      case \'\\\\\':\n        *output++ = \'\\\\\';\n        *output++ = \'\\\\\';\n        break;\n      case \'\\\'\':\n        *output++ = \'\\\\\';\n        *output++ = \'\\\'\';\n        break;\n      case \'\\\"\':\n        *output++ = \'\\\\\';\n        *output++ = \'\\\"\';\n        break;\n      default:\n        *output++ = *input;\n    }\n    ++input;\n  }\n  *output++ = \'\"\';\n  *output++ = \'\\0\';\n}\n\nint main(void) {\n  char output[10000];\n  repr(_PROGRAM, output);\n  printf(_PROGRAM, output);\n}\n";

void repr(const char* input, char* output) {
  *output++ = '"';
  while (*input != '\0') {
    switch (*input) {
      case '\n':
        *output++ = '\\';
        *output++ = 'n';
        break;
      case '\\':
        *output++ = '\\';
        *output++ = '\\';
        break;
      case '\'':
        *output++ = '\\';
        *output++ = '\'';
        break;
      case '\"':
        *output++ = '\\';
        *output++ = '\"';
        break;
      default:
        *output++ = *input;
    }
    ++input;
  }
  *output++ = '"';
  *output++ = '\0';
}

int main(void) {
  char output[10000];
  repr(_PROGRAM, output);
  printf(_PROGRAM, output);
}

Although it is much longer, it takes exactly the same form as the original python quine, except that it contains a definition of the repr() function.


To get a multiquine, we can combine these two programs. Here is the python version:

Code: Select all

_PYTHON_SKELETON = "_PYTHON_SKELETON = %s\n_C_SKELETON = %s\n\ndef repr_for_c(string):\n  return \'\"%%s\"\' %% repr(string)[1:-1].replace(\'\"\',\'\\\\\"\')\n\nprint _C_SKELETON %% (repr_for_c(_PYTHON_SKELETON), repr_for_c(_C_SKELETON))\n"
_C_SKELETON = "#include <stdio.h>\n\nchar* _PYTHON_SKELETON = %s;\nchar* _C_SKELETON = %s;\n\nvoid repr_for_py(const char* input, char* output) {\n  *output++ = \'\"\';\n  while (*input != \'\\0\') {\n    switch (*input) {\n      case \'\\n\':\n        *output++ = \'\\\\\';\n        *output++ = \'n\';\n        break;\n      case \'\\\\\':\n        *output++ = \'\\\\\';\n        *output++ = \'\\\\\';\n        break;\n      case \'\\\'\':\n        *output++ = \'\\\\\';\n        *output++ = \'\\\'\';\n        break;\n      case \'\\\"\':\n        *output++ = \'\\\\\';\n        *output++ = \'\\\"\';\n        break;\n      default:\n        *output++ = *input;\n    }\n    ++input;\n  }\n  *output++ = \'\"\';\n  *output++ = \'\\0\';\n}\n\nint main(void) {\n  char py_skeleton[10000];\n  repr_for_py(_PYTHON_SKELETON, py_skeleton);\n  char c_skeleton[10000];\n  repr_for_py(_C_SKELETON, c_skeleton);\n  printf(_PYTHON_SKELETON, py_skeleton, c_skeleton);\n}\n"

def repr_for_c(string):
  return '"%s"' % repr(string)[1:-1].replace('"','\\"')

print _C_SKELETON % (repr_for_c(_PYTHON_SKELETON), repr_for_c(_C_SKELETON))

Here the python skeleton is a program to print the C skeleton, with the placeholders replaced with the python and C skeletons, respectively. The C skeleton is the opposite: a program to print the python skeleton, with the placeholders replaced with the python and C skeletons. I am making use of the fact that the format string syntax is similar in both python and C, but this is unnecessary; I could use some other placeholder string and do a replace. The only other difference is that the python version needs to produce C literals rather than python literals, and vice versa.

Using this same idea, the multiquine can be extended in a straightforward way to any number of languages, although in some languages (like brainfuck) "escaping a string" makes no sense; instead, the repr_for_brainfuck() function needs to produce a series of brainfuck statements that results in an in-memory representation of the string in some sort of controlled way. This isn't quite as bad as it sounds as programmatically generating brainfuck is much easier than writing brainfuck by hand. The way this works is with the following skeleton for language k:
_SKELETON_FOR_LANGUAGE_1 = placeholder_1
_SKELETON_FOR_LANGUAGE_2 = placeholder_2
...
_SKELETON_FOR_LANGUAGE_n = placeholder_n

* definition of a function which returns a representation in language n+1 for the input string *

* instructions to take _SKELETON_FOR_LANGUAGE_{k+1}, replace placeholders 1...n with repr_for_next_language(_SKELETON_FOR_LANGUAGE_1...n), and print the modified string *

Re: Quine Relay: a circular quine in 50 languages

Posted: Sat Dec 14, 2013 9:40 am UTC
by Jplus
The languages of the Perl family (Perl, Python, Ruby, PHP) are notoriously easy to write quines in, though. Most languages require much more work, especially the esoteric ones like Whitespace, Brainfuck and INTERCAL (which all appear in the list!).

Re: Quine Relay: a circular quine in 50 languages

Posted: Mon Dec 16, 2013 3:12 am UTC
by 3rdtry
Very nice, but it's not a real quine if it doesn't have snow and christmas music :wink:.

Re: Quine Relay: a circular quine in 50 languages

Posted: Sun Dec 22, 2013 11:55 pm UTC
by phlip
Why am I not surprised that all of the quines linked in this thread are by the same person as the spinning-globe quine that was my previous favourite quine ever...