2019-04-04 – A curious Perl Quine

Save the following program to /tmp/quine.pl

    Illegal division by zero at /tmp/quine.pl line 1.

Run it with perl /tmp/quine.pl and it prints its own source code.

It’s easy to make a “cheating quine” in many languages, where a syntax error in the source provokes the parser to emit an error message that matches the source. I posted several cheating quine examples on Twitter including

	  File "quine.py", line 1
		File "quine.py", line 1
		^
	IndentationError: unexpected indent

The Perl quine at the start of this post is a different kind of cheat: the program parses OK, and it runs briefly until the division by zero error is raised. It is quite sensitive to details of the filename: for example ./quine.pl does not work.

This error message is a program?!

This little program gets into a lot of perl’s do-what-I-mean parsing.

The / character is quite context-sensitive, and can be parsed as a division operator or the start of a regex. Small perturbations of this program make it into a regex parse error rather than runnable code. In this case both / appear in an operator context.

The other non-words in this program are 1., which is just a number, and . which is the concatenation operator.

So what do the words mean?

Bare words in Perl can be subroutine names, method names, package or class names, or (in non-strict mode) un-delimited strings, and maybe other things I have forgotten!

Perl also has an unusual method invocation syntax called “indirect object syntax” which has the form

    method object args

most frequently seen looking like

    print $filehandle "message";
    my $instance = new Class(args);

although Perl’s preferred syntax is

    $filehandle->print("message");
    my $instance = Class->new(args);

The perlobj documentation says

To parse this code, Perl uses a heuristic based on what package names it has seen, what subroutines exist in the current package, what barewords it has previously seen, and other input. Needless to say, heuristics can produce very surprising results!

How does it parse?

Starting from the right,

    pl line 1.

is parsed as the method call

    line->pl(1.)

where line is a package (class) name and pl is the method.

In the middle of the program, at, tmp, and quine are parsed as barewords, i.e. strings. The expression parses as:

    (("at" / "tmp") / "quine") . line->pl(1.)

On the left there are two nested indirect object method calls,

    division->Illegal(zero->by( ... ))

The innermost expression, which gets evaluated first, is

    "at" / "tmp"

And this immediately raises a division by zero exception.


⇐ 2019-01-11 ⇐ Open source as a byproduct ⇐ ⇒ I am NOT working on leap seconds this week ⇒ 2019-05-21 ⇒