#!/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($) { my $_ = 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[]; my $_; my($lhs, $rhs) = ( q[], q[] ); while () { 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