perl.gg / obfuscation

Self-modifying Loops

2025-12-07

Loops. For, while, until, foreach. Perl has plenty of ways to repeat yourself.

But what if you wanted to build a loop out of eval and regex? What if your code rewrote itself on every iteration?

#!/usr/bin/env perl use feature qw|say|; $_ = <<'EOF'; 10; s~(\d+)(?{ say qq($1) })~$1-1~e; sleep 1; $1 ? eval : say q(Countdown complete!); EOF eval;
Run it. Watch it count down from 10 to 1. No for loop. No while. Just a heredoc that eats itself.

Part 1: THE SETUP

$_ = <<'EOF'; ...code... EOF
We're stuffing a chunk of Perl code into $ as a string. The single quotes around EOF mean no interpolation happens yet. It's just text.

That text happens to be valid Perl. And we're about to run it.

Part 2: THE STARTING NUMBER

10;
First line of our heredoc. Just a number sitting there.

In Perl, a bare number is a valid statement. It evaluates to itself and does nothing. But it's there in the string, waiting to be found by a regex.

Part 3: THE ENGINE

This is where it gets weird:
s~(\d+)(?{ say qq($1) })~$1-1~e;
Let's pull it apart:
PIECE WHAT IT DOES ----------- -------------------------------------------- s~~~ Substitution operator (using ~ as delimiter) (\d+) Capture one or more digits into $1 (?{ ... }) **Embedded code block** - runs during the match say qq($1) Print the captured number $1-1 Replacement: the number minus one e Evaluate replacement as Perl code
So this regex:
1. Finds the number (10 on first run) 2. Prints it (via the embedded code block) 3. Replaces it with itself minus one (10 becomes 9)
The string in $
literally changes. After one pass:
BEFORE: "10;\ns~..." AFTER: "9;\ns~..."
The code is rewriting itself.
.--. |o_o | |:_/ | // \ \ (| | ) /'\_ _/`\ \___)=(___/

Part 4: THE PAUSE

sleep 1;
Slows it down so you can watch the countdown. Without this, it blasts through in milliseconds.

Part 5: THE LOOP CONDITION

$1 ? eval : say q(Countdown complete!);
This is a ternary operator doing the work of a while loop.
IF $1 IS TRUTHY THEN eval (run $_ again) IF $1 IS FALSY THEN print the goodbye message
When does $1 become falsy? When it hits zero.
10 -> truthy -> eval again 9 -> truthy -> eval again ... 1 -> truthy -> eval again 0 -> falsy -> print message and stop
No explicit loop counter. No increment. The regex IS the counter.

Part 6: THE KICKOFF

eval;
Outside the heredoc, this single statement starts everything.

eval with no argument evaluates $_. So it runs our heredoc code. That code modifies itself and calls eval again. Which runs the modified code. Which modifies itself and calls eval again.

Turtles all the way down.

Part 7: THE FULL WALKTHROUGH

Here's what happens step by step:
ITERATION $_ CONTAINS $1 ACTION --------- ------------ --- ------------------------- 1 "10;..." 10 Print 10, replace with 9 2 "9;..." 9 Print 9, replace with 8 3 "8;..." 8 Print 8, replace with 7 4 "7;..." 7 Print 7, replace with 6 5 "6;..." 6 Print 6, replace with 5 6 "5;..." 5 Print 5, replace with 4 7 "4;..." 4 Print 4, replace with 3 8 "3;..." 3 Print 3, replace with 2 9 "2;..." 2 Print 2, replace with 1 10 "1;..." 1 Print 1, replace with 0 11 "0;..." 0 $1 is falsy, print goodbye
The string transforms on every pass. The code literally changes between executions.

Part 8: THE ANNOTATED VERSION

#!/usr/bin/env perl use feature qw|say|; # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Store code-as-data in $_ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ $_ = <<'EOF'; 10; s~(\d+)(?{ say qq($1) })~$1-1~e; sleep 1; $1 ? eval : say q(Countdown complete!); EOF # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Single eval kicks off the chain # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # eval with no args runs $_ # The code in $_ modifies itself and calls eval again # This continues until $1 becomes 0 eval;

Part 9: WHY WOULD YOU DO THIS

You wouldn't. Not in production.

But it demonstrates some powerful Perl concepts:

* **Code is data** (heredoc as executable string) * **Data is code** (eval turns strings into actions) * Regex can execute code (embedded code blocks) * Regex can do math (the /e modifier) * Self-modification is possible (and terrifying)
This is the kind of thing that makes Perl both loved and feared. The language doesn't stop you from doing weird stuff. It hands you a chainsaw and says "have fun."

Part 10: VARIATIONS

Count up instead of down:
$_ = <<'EOF'; 1; s~(\d+)(?{ say qq($1) })~$1+1~e; sleep 1; $1 < 10 ? eval : say q(Done!); EOF eval;
Fibonacci sequence:
$_ = <<'EOF'; 1 1; s~(\d+)\s+(\d+)(?{ say $2 })~$2 . q( ) . ($1+$2)~e; sleep 1; $2 < 100 ? eval : say q(Done!); EOF eval;
The pattern is always the same: code that rewrites itself, then evals itself, until some condition stops the recursion.

THE TAKEAWAY

Perl treats code and data as interchangeable. A string can become a program. A program can rewrite itself mid-execution. The regex engine can run arbitrary code while matching.

Is this practical? Rarely.

Is it powerful? Absolutely.

Is it Perl? Hell yes.

. /'\ / \ / \ / _ \ / (_) \ /___________\ | | | | _| |_ |_____| "Code that writes code"

Created By: Wildcard Wizard. Copyright 2026