#!/bin/perl

$indent = 4;
$shiftwidth = 4;
$l = '{'; $r = '}';
$tempvar = '1';

while ($ARGV[0] =~ '^-') {
    $_ = shift;
  last if /^--/;
    if (/^-D/) {
	$debug++;
	open(body,'>-');
	next;
    }
    if (/^-n/) {
	$assumen++;
	next;
    }
    if (/^-p/) {
	$assumep++;
	next;
    }
    die "I don't recognize this switch: $_";
}

unless ($debug) {
    open(body,">/tmp/sperl$$") || do Die("Can't open temp file.");
}

if (!$assumen && !$assumep) {
    print body
'while ($ARGV[0] =~ /^-/) {
    $_ = shift;
  last if /^--/;
    if (/^-n/) {
	$nflag++;
	next;
    }
    die "I don\'t recognize this switch: $_";
}

';
}

print body '
#ifdef PRINTIT
#ifdef ASSUMEP
$printit++;
#else
$printit++ unless $nflag;
#endif
#endif
line: while (<>) {
';

line: while (<>) {
    s/[ \t]*(.*)\n$/$1/;
    if (/^:/) {
	s/^:[ \t]*//;
	$label = do make_label($_);
	if ($. == 1) {
	    $toplabel = $label;
	}
	$_ = "$label:";
	if ($lastlinewaslabel++) {$_ .= "\t;";}
	if ($indent >= 2) {
	    $indent -= 2;
	    $indmod = 2;
	}
	next;
    } else {
	$lastlinewaslabel = '';
    }
    $addr1 = '';
    $addr2 = '';
    if (s/^([0-9]+)//) {
	$addr1 = "$1";
    }
    elsif (s/^\$//) {
	$addr1 = 'eof()';
    }
    elsif (s|^/||) {
	$addr1 = '/';
	delim: while (s:^([^(|)\\/]*)([(|)\\/])::) {
	    $prefix = $1;
	    $delim = $2;
	    if ($delim eq '\\') {
		s/(.)(.*)/$2/;
		$ch = $1;
		$delim = '' if index("(|)",$ch) >= 0;
		$delim .= $1;
	    }
	    elsif ($delim ne '/') {
		$delim = '\\' . $delim;
	    }
	    $addr1 .= $prefix;
	    $addr1 .= $delim;
	    if ($delim eq '/') {
		last delim;
	    }
	}
    }
    if (s/^,//) {
	if (s/^([0-9]+)//) {
	    $addr2 = "$1";
	} elsif (s/^\$//) {
	    $addr2 = "eof()";
	} elsif (s|^/||) {
	    $addr2 = '/';
	    delim: while (s:^([^(|)\\/]*)([(|)\\/])::) {
		$prefix = $1;
		$delim = $2;
		if ($delim eq '\\') {
		    s/(.)(.*)/$2/;
		    $ch = $1;
		    $delim = '' if index("(|)",$ch) >= 0;
		    $delim .= $1;
		}
		elsif ($delim ne '/') {
		    $delim = '\\' . $delim;
		}
		$addr2 .= $prefix;
		$addr2 .= $delim;
		if ($delim eq '/') {
		    last delim;
		}
	    }
	} else {
	    do Die("Invalid second address at line $.: $_");
	}
	$addr1 .= " .. $addr2";
    }
					# a { to keep vi happy
    if ($_ eq '}') {
	$indent -= 4;
	next;
    }
    if (s/^!//) {
	$if = 'unless';
	$else = "$r else $l\n";
    } else {
	$if = 'if';
	$else = '';
    }
    if (s/^{//) {	# a } to keep vi happy
	$indmod = 4;
	$redo = $_;
	$_ = '';
	$rmaybe = '';
    } else {
	$rmaybe = "\n$r";
	if ($addr2 || $addr1) {
	    $space = substr('        ',0,$shiftwidth);
	} else {
	    $space = '';
	}
	$_ = do transmogrify();
    }

    if ($addr1) {
	if ($_ !~ /[\n{}]/ && $rmaybe && !$change &&
	  $_ !~ / if / && $_ !~ / unless /) {
	    s/;$/ $if $addr1;/;
	    $_ = substr($_,$shiftwidth,1000);
	} else {
	    $command = $_;
	    $_ = "$if ($addr1) $l\n$change$command$rmaybe";
	}
	$change = '';
	next line;
    }
} continue {
    @lines = split(/\n/,$_);
    while ($#lines >= 0) {
	$_ = shift(lines);
	unless (s/^ *<<--//) {
	    print body substr("\t\t\t\t\t\t\t\t\t\t\t\t",0,$indent / 8),
		substr('        ',0,$indent % 8);
	}
	print body $_, "\n";
    }
    $indent += $indmod;
    $indmod = 0;
    if ($redo) {
	$_ = $redo;
	$redo = '';
	redo line;
    }
}

print body "}\n";
if ($appendseen || $tseen || !$assumen) {
    $printit++ if $dseen || (!$assumen && !$assumep);
    print body '
continue {
#ifdef PRINTIT
#ifdef DSEEN
#ifdef ASSUMEP
    print if $printit++;
#else
    if ($printit) { print;} else { $printit++ unless $nflag; }
#endif
#else
    print if $printit;
#endif
#else
    print;
#endif
#ifdef TSEEN
    $tflag = \'\';
#endif
#ifdef APPENDSEEN
    if ($atext) { print $atext; $atext = \'\'; }
#endif
}
';
}

close body;

unless ($debug) {
    open(head,">/tmp/sperl2$$") || do Die("Can't open temp file 2.\n");
    print head "#define PRINTIT\n" if ($printit);
    print head "#define APPENDSEEN\n" if ($appendseen);
    print head "#define TSEEN\n" if ($tseen);
    print head "#define DSEEN\n" if ($dseen);
    print head "#define ASSUMEN\n" if ($assumen);
    print head "#define ASSUMEP\n" if ($assumep);
    if ($opens) {print head "$opens\n";}
    open(body,"/tmp/sperl$$") || do Die("Can't reopen temp file.");
    while (<body>) {
	print head $_;
    }
    close head;

    print "#!/bin/perl\n\n";
    open(body,"cc -E /tmp/sperl2$$ |") ||
	do Die("Can't reopen temp file.");
    while (<body>) {
	/^# [0-9]/ && next;
	/^[ \t]*$/ && next;
	s/^<><>//;
	print;
    }
}

`/bin/rm -f /tmp/sperl$$ /tmp/sperl2$$`;

sub Die {
    `/bin/rm -f /tmp/sperl$$ /tmp/sperl2$$`;
    die $_[0];
}
sub make_filehandle {
    $fname = $_ = $_[0];
    s/[^a-zA-Z]/_/g;
    s/^_*//;
    if (/^([a-z])([a-z]*)$/) {
	$first = $1;
	$rest = $2;
	$first =~ y/a-z/A-Z/;
	$_ = $first . $rest;
    }
    if (!$seen{$_}) {
	$opens .= "open($_,'>$fname') || die \"Can't create $fname.\";\n";
    }
    $seen{$_} = $_;
}

sub make_label {
    $label = $_[0];
    $label =~ s/[^a-zA-Z0-9]/_/g;
    if ($label =~ /^[0-9_]/) { $label = 'L' . $label; }
    $label = substr($label,0,8);
    if ($label =~ /^([a-z])([a-z]*)$/) {
	$first = $1;
	$rest = $2;
	$first =~ y/a-z/A-Z/;
	$label = $first . $rest;
    }
    $label;
}

sub transmogrify {
    {	# case
	if (/^d/) {
	    $dseen++;
	    $_ = '
<<--#ifdef PRINTIT
$printit = \'\';
<<--#endif
next line;';
	    next;
	}

	if (/^n/) {
	    $_ =
'<<--#ifdef PRINTIT
<<--#ifdef DSEEN
<<--#ifdef ASSUMEP
print if $printit++;
<<--#else
if ($printit) { print;} else { $printit++ unless $nflag; }
<<--#endif
<<--#else
print if $printit;
<<--#endif
<<--#else
print;
<<--#endif
<<--#ifdef APPENDSEEN
if ($atext) {print $atext; $atext = \'\';}
<<--#endif
$_ = <>;
<<--#ifdef TSEEN
$tflag = \'\';
<<--#endif';
	    next;
	}

	if (/^a/) {
	    $appendseen++;
	    $command = $space .  '$atext .=' . "\n<<--'";
	    $lastline = 0;
	    while (<>) {
		s/^[ \t]*//;
		s/^[\\]//;
		unless (s|\\$||) { $lastline = 1;}
		s/'/\\'/g;
		s/^([ \t]*\n)/<><>$1/;
		$command .= $_;
		$command .= '<<--';
		last if $lastline;
	    }
	    $_ = $command . "';";
	    last;
	}

	if (/^[ic]/) {
	    if (/^c/) { $change = 1; }
	    $addr1 = '$iter = (' . $addr1 . ')';
	    $command = $space .  'if ($iter == 1) { print' . "\n<<--'";
	    $lastline = 0;
	    while (<>) {
		s/^[ \t]*//;
		s/^[\\]//;
		unless (s/\\$//) { $lastline = 1;}
		s/'/\\'/g;
		s/^([ \t]*\n)/<><>$1/;
		$command .= $_;
		$command .= '<<--';
		last if $lastline;
	    }
	    $_ = $command . "';}";
	    if ($change) {
		$dseen++;
		$change = "$_\n";
		$_ = "
<<--#ifdef PRINTIT
$space\$printit = '';
<<--#endif
${space}next line;";
	    }
	    last;
	}

	if (/^s/) {
	    $delim = substr($_,1,1);
	    $len = length($_);
	    $repl = $end = 0;
	    for ($i = 2; $i < $len; $i++) {
		$c = substr($_,$i,1);
		if ($c eq '\\') {
		    $i++;
		    if ($i >= $len) {
			$_ .= 'n';
			$_ .= <>;
			$len = length($_);
			$_ = substr($_,0,--$len);
		    }
		    elsif (!$repl && index("(|)",substr($_,$i,1)) >= 0) {
			$i--;
			$len--;
			$_ = substr($_,0,$i) . substr($_,$i+1,10000);
		    }
		}
		elsif ($c eq $delim) {
		    if ($repl) {
			$end = $i;
			last;
		    } else {
			$repl = $i;
		    }
		}
		elsif (!$repl && index("(|)",$c) >= 0) {
		    $_ = substr($_,0,$i) . '\\' . substr($_,$i,10000);
		    $i++;
		    $len++;
		}
	    }
	    print "repl $repl end $end $_\n";
	    do Die("Malformed substitution at line $.") unless $end;
	    $pat = substr($_, 0, $repl + 1);
	    $repl = substr($_, $repl + 1, $end - $repl - 1);
	    $end = substr($_, $end + 1, 1000);
	    $dol = '$';
	    $repl =~ s'&'$&'g;
	    $repl =~ s/[\\]([0-9])/$dol$1/g;
	    $subst = "$pat$repl$delim";
	    $cmd = '';
	    while ($end) {
		if ($end =~ s/^g//) { $subst .= 'g'; next; }
		if ($end =~ s/^p//) { $cmd .= ' && (print)'; next; }
		if ($end =~ s/^w[ \t]*//) {
		    $fh = do make_filehandle($end);
		    $cmd .= " && (print $fh \$_)";
		    $end = '';
		    next;
		}
		do Die("Unrecognized substitution command ($end) at line $.");
	    }
	    $_ = $subst . $cmd . ';';
	    next;
	}

	if (/^p/) {
	    $_ = 'print;';
	    next;
	}

	if (/^w/) {
	    s/^w[ \t]*//;
	    $fh = do make_filehandle($_);
	    $_ = "print $fh \$_;";
	    next;
	}

	if (/^r/) {
	    $appendseen++;
	    s/^r[ \t]*//;
	    $file = $_;
	    $_ = "\$atext .= `cat $file 2>/dev/null`;";
	    next;
	}

	if (/^P/) {
	    $_ =
'if (/(^[^\n]*\n)/) {
    print $1;
}';
	    next;
	}

	if (/^D/) {
	    $_ =
's/^[^\n]*\n//;
if ($_) {redo line;}
next line;';
	    next;
	}

	if (/^N/) {
	    $_ = '
$_ .= <>;
<<--#ifdef TSEEN
$tflag = \'\';
<<--#endif';
	    next;
	}

	if (/^h/) {
	    $_ = '$hold = $_;';
	    next;
	}

	if (/^H/) {
	    $_ = '$hold .= $_ ? $_ : "\n";';
	    next;
	}

	if (/^g/) {
	    $_ = '$_ = $hold;';
	    next;
	}

	if (/^G/) {
	    $_ = '$_ .= $hold ? $hold : "\n";';
	    next;
	}

	if (/^x/) {
	    $_ = '($_, $hold) = ($hold, $_);';
	    next;
	}

	if (/^b$/) {
	    $_ = 'next line;';
	    next;
	}

	if (/^b/) {
	    s/^b[ \t]*//;
	    $lab = do make_label($_);
	    if ($lab eq $toplabel) {
		$_ = 'redo line;';
	    } else {
		$_ = "goto $lab;";
	    }
	    next;
	}

	if (/^t$/) {
	    $_ = 'next line if $tflag;';
	    $tseen++;
	    next;
	}

	if (/^t/) {
	    s/^t[ \t]*//;
	    $lab = do make_label($_);
	    if ($lab eq $toplabel) {
		$_ = 'if ($tflag) {$tflag = \'\'; redo line;}';
	    } else {
		$_ = "if (\$tflag) {\$tflag = ''; goto $lab;}";
	    }
	    $tseen++;
	    next;
	}

	if (/^=/) {
	    $_ = 'print "$.\n";';
	    next;
	}

	if (/^q/) {
	    $_ =
'close(ARGV);
@ARGV = ();
next line;';
	    next;
	}
    } continue {
	if ($space) {
	    s/^/$space/;
	    s/(\n)(.)/$1$space$2/g;
	}
	last;
    }
    $_;
}

