Change 18254 by jhi@lyta on 2002/12/07 16:26:27
Add user-definable To-mappings. (So if you don't agree
with the Unicode mappings, you can make your own.)
Resolves [perl #3741].
Affected files ...
.... //depot/maint-5.8/perl/MANIFEST#12 edit
.... //depot/maint-5.8/perl/lib/utf8_heavy.pl#3 edit
.... //depot/maint-5.8/perl/pod/perldiag.pod#10 edit
.... //depot/maint-5.8/perl/pod/perlunicode.pod#4 edit
.... //depot/maint-5.8/perl/t/op/lc_user.t#1 add
Differences ...
==== //depot/maint-5.8/perl/MANIFEST#12 (text) ====
Index: perl/MANIFEST
--- perl/MANIFEST#11~18234~ Mon Dec 2 14:30:41 2002
+++ perl/MANIFEST Sat Dec 7 08:26:27 2002
@@ -2563,6 +2563,7 @@
t/op/int.t See if int works
t/op/join.t See if join works
t/op/lc.t See if lc, uc, lcfirst, ucfirst, quotemeta work
+t/op/lc_user.t See if user-defined lc et alia work
t/op/length.t See if length works
t/op/lex_assign.t See if ops involving lexicals or pad temps work
t/op/lfs.t See if large files work for perlio
==== //depot/maint-5.8/perl/lib/utf8_heavy.pl#3 (text) ====
Index: perl/lib/utf8_heavy.pl
--- perl/lib/utf8_heavy.pl#2~18080~ Sun Nov 3 21:23:04 2002
+++ perl/lib/utf8_heavy.pl Sat Dec 7 08:26:27 2002
@@ -11,7 +11,8 @@
sub croak { require Carp; Carp::croak(@_) }
##
-## "SWASH" == "SWATCH HASH". A "swatch" is a swatch of the Unicode landscape
+## "SWASH" == "SWATCH HASH". A "swatch" is a swatch of the Unicode landscape.
+## It's a data structure that encodes a set of Unicode characters.
##
sub SWASHNEW {
@@ -87,10 +88,10 @@
## It could be a user-defined property.
##
- my $caller = caller(1);
+ my $caller1 = caller(1);
- if (defined $caller && $type =~ /^(?:\w+)$/) {
- my $prop = $caller . "::" . ( $wasIs ? "Is" : "" ) . $type;
+ if (defined $caller1 && $type =~ /^(?:\w+)$/) {
+ my $prop = $caller1 . "::" . ( $wasIs ? "Is" : "" ) . $type;
if (exists &{$prop}) {
no strict 'refs';
@@ -99,10 +100,29 @@
}
}
+ ##
+ ## See if it's a user-level "To".
+ ##
+
+ my $caller0 = caller(0);
+
+ if (defined $caller0 && $type =~ /^To(?:\w+)$/) {
+ my $map = $caller0 . "::" . $type;
+ if (exists &{$map}) {
+ no strict 'refs';
+
+ $list = &{$map};
+ last GETFILE;
+ }
+ }
+
##
- ## Last attempt -- see if it's a "To" name (e.g. "ToLower")
+ ## Last attempt -- see if it's a standard "To" name
+ ## (e.g. "ToLower") ToTitle is used by ucfirst().
+ ## The user-level way to access ToDigit() and ToFold()
+ ## is to use Unicode::UCD.
##
- if ($type =~ /^To([A-Z][A-Za-z]+)$/)
+ if ($type =~ /^To(Digit|Fold|Lower|Title|Upper)$/)
{
$file = "unicore/To/$1.pl";
## would like to test to see if $file actually exists....
@@ -122,7 +142,7 @@
##
## If we reach here, it was due to a 'last GETFILE' above
- ## (exception: user-defined properties), so we
+ ## (exception: user-defined properties and mappings), so we
## have a filename, so now we load it if we haven't already.
## If we have, return the cached results. The cache key is the
## file to load.
@@ -162,10 +182,10 @@
if ($minbits < 32) {
my $top = 0;
- while ($list =~ /^([0-9a-fA-F]+)(?:\t([0-9a-fA-F]+)?)(?:\t([0-9a-fA-F]+))?/mg)
{
+ while ($list =~ /^([0-9a-fA-F]+)(?:[\t]([0-9a-fA-F]+)?)(?:[
+\t]([0-9a-fA-F]+))?/mg) {
my $min = hex $1;
- my $max = hex(defined $2 ? $2 : $1);
- my $val = hex(defined $3 ? $3 : "");
+ my $max = defined $2 ? hex $2 : $min;
+ my $val = defined $3 ? hex $3 : 0;
$val += $max - $min if defined $3;
$top = $val if $val > $top;
}
@@ -239,10 +259,15 @@
pos $_ = 0;
if ($bits > 1) {
LINE:
- while (/^([0-9a-fA-F]+)(?:\t([0-9a-fA-F]+)?)(?:\t([0-9a-fA-F]+))?/mg) {
- my $min = hex $1;
- my $max = (defined $2 ? hex $2 : $min);
- my $val = hex $3;
+ while (/^([0-9a-fA-F]+)(?:[ \t]([0-9a-fA-F]+)?)?(?:[
+\t]([0-9a-fA-F]+))?/mg) {
+ chomp;
+ my ($a, $b, $c) = ($1, $2, $3);
+ croak "$type: illegal mapping '$_'"
+ if $type =~ /^To/ &&
+ !(defined $a && defined $c);
+ my $min = hex $a;
+ my $max = defined $b ? hex $b : $min;
+ my $val = defined $c ? hex $c : 0;
next if $max < $start;
print "$min $max $val\n" if DEBUG;
if ($none) {
@@ -273,8 +298,9 @@
else {
LINE:
while (/^([0-9a-fA-F]+)(?:[ \t]+([0-9a-fA-F]+))?/mg) {
+ chomp;
my $min = hex $1;
- my $max = (defined $2 ? hex $2 : $min);
+ my $max = defined $2 ? hex $2 : $min;
next if $max < $start;
if ($min < $start) {
$min = $start;
==== //depot/maint-5.8/perl/pod/perldiag.pod#10 (text) ====
Index: perl/pod/perldiag.pod
--- perl/pod/perldiag.pod#9~18234~ Mon Dec 2 14:30:41 2002
+++ perl/pod/perldiag.pod Sat Dec 7 08:26:27 2002
@@ -3685,6 +3685,13 @@
(F) Your version of the C library apparently doesn't do times(). I
suspect you're not running on Unix.
+=item To%s: illegal mapping '%s'
+
+(F) You tried to define a customized To-mapping for lc(), lcfirst,
+uc(), or ucfirst() (or their string-inlined versions), but you
+specified an illegal mapping.
+See L<perlunicode/"User-Defined Character Properties">.
+
=item Too few args to syscall
(F) There has to be at least one argument to syscall() to specify the
==== //depot/maint-5.8/perl/pod/perlunicode.pod#4 (text) ====
Index: perl/pod/perlunicode.pod
--- perl/pod/perlunicode.pod#3~18242~ Tue Dec 3 07:04:07 2002
+++ perl/pod/perlunicode.pod Sat Dec 7 08:26:27 2002
@@ -616,10 +616,10 @@
=head2 User-Defined Character Properties
You can define your own character properties by defining subroutines
-whose names begin with "In" or "Is". The subroutines must be
-visible in the package that uses the properties. The user-defined
-properties can be used in the regular expression C<\p> and C<\P>
-constructs.
+whose names begin with "In" or "Is". The subroutines must be defined
+in the C<main> package. The user-defined properties can be used in the
+regular expression C<\p> and C<\P> constructs. Note that the effect
+is compile-time and immutable once defined.
The subroutines must return a specially-formatted string, with one
or more newline-separated lines. Each line must be one of the following:
@@ -697,6 +697,56 @@
+utf8::IsCn
END
}
+
+You can also define your own mappings to be used in the lc(),
+lcfirst(), uc(), and ucfirst() (or their string-inlined versions).
+The principle is the same: define subroutines in the C<main> package
+with names like C<ToLower> (for lc() and lcfirst()), C<ToTitle> (for
+the first character in ucfirst()), and C<ToUpper> (for uc(), and the
+rest of the characters in ucfirst()).
+
+The string returned by the subroutines needs now to be three
+hexadecimal numbers separated by tabulators: start of the source
+range, end of the source range, and start of the destination range.
+For example:
+
+ sub ToUpper {
+ return <<END;
+ 0061\t0063\t0041
+ END
+ }
+
+defines an uc() mapping that causes only the characters "a", "b", and
+"c" to be mapped to "A", "B", "C", all other characters will remain
+unchanged.
+
+If there is no source range to speak of, that is, the mapping is from
+a single character to another single character, leave the end of the
+source range empty, but the two tabulator characters are still needed.
+For example:
+
+ sub ToLower {
+ return <<END;
+ 0041\t\t0061
+ END
+ }
+
+defines a lc() mapping that causes only "A" to be mapped to "a", all
+other characters will remain unchanged.
+
+(For serious hackers only) If you want to introspect the default
+mappings, you can find the data in the directory
+C<$Config{privlib}>/F<unicore/To/>. The mapping data is returned as
+the here-document, and the C<utf8::ToSpecFoo> are special exception
+mappings derived from <$Config{privlib}>/F<unicore/SpecialCasing.txt>.
+The C<Digit> and C<Fold> mappings that one can see in the directory
+are not directly user-accessible, one can use either the
+C<Unicode::UCD> module, or just match case-insensitively (that's when
+the C<Fold> mapping is used).
+
+A final note on the user-defined property tests and mappings: they
+will be used only if the scalar has been marked as having Unicode
+characters. Old byte-style strings will not be affected.
=head2 Character Encodings for Input and Output
==== //depot/maint-5.8/perl/t/op/lc_user.t#1 (text) ====
Index: perl/t/op/lc_user.t
--- /dev/null Tue May 5 13:32:27 1998
+++ perl/t/op/lc_user.t Sat Dec 7 08:26:27 2002
@@ -0,0 +1,26 @@
+BEGIN {
+ chdir 't';
+ @INC = '../lib';
+ require './test.pl';
+}
+
+plan tests => 4;
+
+sub ToUpper {
+ return <<END;
+0061 0063 0041
+END
+}
+
+is("\Ufoo\x{101}", "foo\x{101}", "no changes on 'foo'");
+is("\Ubar\x{101}", "BAr\x{101}", "changing 'ab' on 'bar' ");
+
+sub ToLower {
+ return <<END;
+0041 0061
+END
+}
+
+is("\LFOO\x{100}", "FOO\x{100}", "no changes on 'FOO'");
+is("\LBAR\x{100}", "BaR\x{100}", "changing 'A' on 'BAR' ");
+
End of Patch.