#!/usr/local/bin/perl -l
# leo (leonardo script) - reverse input to ʇndʇno
#
# Tom Christiassen
# tchrist@perl.com
use 5.010_000;
use utf8;
use strict;
use autodie;
use warnings qw[ FATAL all ];
use open qw[ :std :utf8 ];
use autouse
"Unicode::Normalize" => qw[ NFD NFC NFKD NFKC ];
use constant BOTH_WAYS => 0;
#################################################################
sub flip_diacriticals($);
# heredoc beaᵘtification routines
sub dequeue($$);
sub strip_qq($);
sub strip_q($);
sub xbrace_quote(@);
sub reverse_mark_flip($);
sub main();
#################################################################
main();
exit();
#################################################################
sub main() { sub
uʍopəpᴉƨdn($);
for my $input (reverse <>) {
chomp $input;
my $ʇndʇno = uʍopəpᴉƨdn($input);
say $ʇndʇno;
}
}
#################################################################
sub uʍopəpᴉƨdn($) {
local $_ = shift();
$_ = /[^\x00-\x7F]/ # Unicode?
? reverse_mark_flip($_)
: reverse ($_);
# this is the best we can do for either case
0 and s/[Jj]/ſ\x{323}/g; # long s + combining dot below
# Placeholders below indicated by □ for chars I haven't
# yet found an upside-down version of. This can be deceptive
# if you don't have one of the normal things in your font set!
if (BOTH_WAYS) {
tr [abcdefghijklmnopqrstuvwxyzɐqɔpəɟ⅁ɥᴉ□ʞlɯuodbɹƨʇnʌʍxʎ□]
[ɐqɔpəɟ⅁ɥᴉ□ʞlɯuodbɹsʇnʌʍxʎ□abcdefghijklmnopqrstuvwxyz];
tr [ABCDEFGHIJKLMNOPQRSTUVWXYZɐqƆpƎℲ⅁ɥI□ʞ⅂ƜИOdbᴚƨʇnɅMX⅄□]
[ɐqƆpƎℲ⅁ɥI□ʞ⅂ƜИOdbᴚsʇnɅMX⅄□ABCDEFGHIJKLMNOPQRSTUVWXYZ];
} else {
tr [abcdefghijklmnopqrstuvwxyz]
qɔpəɟƃɥᴉɾʞlɯuodbɹsʇnʌʍxʎz]; # punt to other case
#[ɐqɔpəɟ⅁ɥᴉ□ʞlɯuodbɹƨʇnʌʍxʎ□]; # punt to other case
# [ɐqɔpəɟ□ɥᴉ□ʞlɯuodbɹsʇnʌʍxʎ□]; # missing in the casing
tr [ABCDEFGHIJKLMNOPQRSTUVWXYZ]
qƆpƎℲ⅁ɥI□ʞ⅂ƜИOdbᴚsʇnɅMX⅄□]; # punt to other case
# [□□Ɔ□ƎℲ⅁□I□□⅂ƜИO□□ᴚ□□□ɅMX⅄□]; # missing in the casing
}
tr [-¯_#&'"“”‘’!¡?¿,.]
[-_¯#⅋'"„□□,¡!¿?ʻ˙];
tr [0123456789]
[0□□ʕ□□9□86] if 0;
# sure wish these next two looked better
tr [()<>{}[]]
[)(><}{\]\[];
tr#/\\#\\/#;
# NFC unlikely to be of much help,
# but one is "supposed" to do this
return NFC($_);
}
# reverse string by graphemes, inverting all the marks
sub reverse_mark_flip($) {
my $string = shift();
# first decompose to pull out grapheme units
my $nfd = NFD($string);
# reverse the string by grapheme units
my @graphemes = $nfd =~ /\X/g;
# put it back together reversed
$string = join q[] => reverse @graphemes;
# if there are marks, we have hard work to do
if ($string =~ /\pM/) {
$string = flip_diacriticals($string);
}
return $string;
}
# This autoloading stub replaces itself with the real function,
# then jumps directly into its replacement via magic goto.
#
# HEY LIKE I'M SORRY ALREADY, OK! It's just too hard to get
# this right—and look ok—any other way. Really, I *tried*.
#
sub flip_diacriticals($) {
binmode(DATA, ":utf8");
local $/ = q[];
local $_;
my($lhs, $rhs) = ( q[], q[] );
while (<DATA>) {
next if m{ \A \s* \# }x;
my @pair = m{ < ( \p{HexDigit} + ) > }gmx;
next unless @pair == 2;
$lhs .= xbrace_quote( @pair);
$rhs .= xbrace_quote(reverse @pair);
}
my $redefinition = strip_q <<'END_OF_START'
|Q|
|Q| no warnings "redefine";
|Q|
|Q| sub flip_diacriticals($) {
|Q| # haven't touched @_ yet
|Q| my $string = shift();
|Q| $string =~
|Q|
END_OF_START
. strip_qq <<"END_OF_TRANSLITERATION"
|QQ|
|QQ| tr[$lhs]
|QQ| [$rhs];
|QQ|
END_OF_TRANSLITERATION
. strip_q <<'END_OF_FUNCTION'
|Q|
|Q| return $string;
|Q| }
|Q|
|Q| 1; # eval happiness
|Q|
END_OF_FUNCTION
# this ̬ is the end of the eval string build up
; # DO NOT DELETE
# that ̂ was the end of the eval string build up
##say $redefinition;
eval $redefinition || die;
goto \&flip_diacriticals;
}
sub dequeue($$) {
my($leader, $body) = @_;
$body =~ s/^\s*\Q$leader\E ?//gm;
return $body;
}
sub strip_q($) {
my $body = shift();
return dequeue('|Q|', $body);
}
sub strip_qq($) {
my $body = shift();
return dequeue("|QQ|", $body);
}
sub xbrace_quote(@) {
return join q[] => map { q[\x{] . $_ . q[}] } @_;
}
__END__
̈ 776 <0308> COMBINING DIAERESIS
̤ 804 <0324> COMBINING DIAERESIS BELOW
̃ 771 <0303> COMBINING TILDE
̰ 816 <0330> COMBINING TILDE BELOW
́ 769 <0301> COMBINING ACUTE ACCENT
̗ 791 <0317> COMBINING ACUTE ACCENT BELOW
̀ 768 <0300> COMBINING GRAVE ACCENT
̖ 790 <0316> COMBINING GRAVE ACCENT BELOW
̆ 774 <0306> COMBINING BREVE
̯ 815 <032F> COMBINING INVERTED BREVE BELOW
̑ 785 <0311> COMBINING INVERTED BREVE
̮ 814 <032E> COMBINING BREVE BELOW
̭ 813 <032D> COMBINING CIRCUMFLEX ACCENT BELOW
̌ 780 <030C> COMBINING CARON
̂ 770 <0302> COMBINING CIRCUMFLEX ACCENT
̬ 812 <032C> COMBINING CARON BELOW
̧ 807 <0327> COMBINING CEDILLA
̉ 777 <0309> COMBINING HOOK ABOVE
̇ 775 <0307> COMBINING DOT ABOVE
̣ 803 <0323> COMBINING DOT BELOW
̳ 819 <0333> COMBINING DOUBLE LOW LINE
̿ 831 <033F> COMBINING DOUBLE OVERLINE
̅ 773 <0305> COMBINING OVERLINE
̲ 818 <0332> COMBINING LOW LINE
̄ 772 <0304> COMBINING MACRON
̱ 817 <0331> COMBINING MACRON BELOW
̍ 781 <030D> COMBINING VERTICAL LINE ABOVE
̩ 809 <0329> COMBINING VERTICAL LINE BELOW