I finally took a stab at writing a quine, a program that outputs a copy of itself. I’d learned about quines when I first started programming, maybe even in middle school, but whereever I read about it said something along the lines of “but wait, before you read how to do it, why not try it yourself?”. So I’ve diligently avoided looking at an example of a quine since. This afternoon I decided to actually try it though, and it turned out less difficult than I thought. I knew that generally (unless you’re using some weird quirk of the programming language) the way to do it would be to have the text of the program stored as data (ex. a string) and then have the program itself be capable of reading the data and printing it out twice, once to be a copy of the data and once again to be a copy of the program.

  My first result ended up like this:

data = "data = \n\
instructions = data.splitlines()[-1].split()\n\
for code in instructions:\n\
    if code == 'n': print()\n\
    elif code == 'q': print(chr(34),end=chr(0))\n\
    elif chr(58) in code:\n\
        starti, endi = code.split(chr(58))\n\
        print(chr(10).join(data.splitlines()[int(starti):int(endi)]))\n\
    elif chr(59) in code:\n\
        starti, endi = code.split(chr(59))\n\
        print((chr(92)+'n'+chr(92)+chr(10)).join(data.splitlines()[int(starti):int(endi)]),end=chr(0))\n\
    else:\n\
        print(data.splitlines()[int(code)],end=chr(0))\n\
0 q 0;14 q n 1:13"
instructions = data.splitlines()[-1].split()
for code in instructions:
    if code == 'n': print()
    elif code == 'q': print(chr(34),end=chr(0))
    elif chr(58) in code:
        starti, endi = code.split(chr(58))
        print(chr(10).join(data.splitlines()[int(starti):int(endi)]))
    elif chr(59) in code:
        starti, endi = code.split(chr(59))
        print((chr(92)+'n'+chr(92)+chr(10)).join(data.splitlines()[int(starti):int(endi)]),end=chr(0))
    else:
        print(data.splitlines()[int(code)],end=chr(0))

  I used chr() because I didn’t want to have to worry about escaping special characters in quotes.

 Essentially the program is an interpreter capable of processing a few instructions. The possible codes it processes are as follows:

  • n : print a newline
  • q : print a quote character "
  • a:b : print the range a:b of lines in the data variable
  • a;b : print the range a:b of lines in the data variable, but at \n\ at the end of each line
  • a : print line a of the data variable without a newline

 The text of the interpreter program is stored in the data variable, along with the line data = and the code that the interpreter interpreter runs 0 q 0;14 q n 1:13. The code that the interpreter runs works like this:

  • 0 : print line 0, “data =”
  • q 0;14 q : copy out exact full text of data (including \n chars) with quotes around it
  • n : add a newline to separate data section and interpreter
  • 1:13 : print out interpreter (does not include data = line and the line being interpreted)

 This works, but the interpreter is clearly overkill since theres only one program it’s actually supposed to run. Still, it was a good idea to design something with more “infrastructure” first. It allows me to then gradually make the program more efficient by removing unneccessary “infrastructure”. Here’s the same idea, but with the program “built in”:

data = "data = \n\
lines = data.splitlines()\n\
text = (chr(92)+'n'+chr(92)+chr(10)).join(data.splitlines()[0:len(lines)])\n\
print(lines[0]+chr(34)+text+chr(34))\n\
print(chr(10).join(lines[1:len(lines)]))"
lines = data.splitlines()
text = (chr(92)+'n'+chr(92)+chr(10)).join(data.splitlines()[0:len(lines)])
print(lines[0]+chr(34)+text+chr(34))
print(chr(10).join(lines[1:len(lines)]))