Author: pmichaud
Date: Wed Apr 4 20:20:48 2007
New Revision: 17988
Modified:
trunk/runtime/parrot/library/String/Utils.pir
trunk/t/library/string_utils.t
Log:
[library]:
* Add 'convert_string_to_int' and 'convert_digits_to_string'.
* Update tests.
Modified: trunk/runtime/parrot/library/String/Utils.pir
==============================================================================
--- trunk/runtime/parrot/library/String/Utils.pir (original)
+++ trunk/runtime/parrot/library/String/Utils.pir Wed Apr 4 20:20:48 2007
@@ -60,97 +60,144 @@
.return ( res )
.end
-=item convert_radix(source, radix, [,pos])
-Convert characters from C<source> starting at C<pos> to
-a character string according to C<radix>. If C<pos> is
-omitted, then zero (start of string) is assumed. The C<radix>
-may either be an integer radix (up to 36) or one of
-'x', 'd', 'o', or 'b' to indicate a radix of 16, 10,
-8, or 2.
-
-Returns the converted string and the number of
-characters of source used in the conversion.
-
-If the sequence starts with square brackets, then
-multiple conversions may be specified separated by
-commas and a closing square bracket.
-
- (result, len) = convert_radix('41', 'x') # 'A'
- (result, len) = convert_radix('101', 8) # 'A'
- (result, len) = convert_radix('[41,43]', 'x') # 'AC'
- (result, len) = convert_radix('[65,67]', 'd') # 'AC'
+=item convert_string_to_int(source [, radix [, pos]])
+
+Convert characters from C<source> starting at C<pos> to
+an integer according to C<radix>. If C<pos> is specified,
+start converting at C<pos>, otherwise start from the
+beginning of the string. C<radix> may be either an
+integer radix (up to 36) or one of 'x', 'd', 'o', or 'b'
+to indicate a radix of 16, 10, 8, or 2.
+
+Returns the integer value of the converted string, and
+the number of characters used in the conversion. Conversion
+stops at the first character that isn't in the valid range
+according to C<radix>.
+
+ (result, len) = convert_string_to_int('101') # (101, 3)
+ (result, len) = convert_string_to_int('101', 2) # (5, 3)
+ (result, len) = convert_string_to_int('101', 8) # (65, 3)
+ (result, len) = convert_string_to_int('ff', 'x') # (255, 2)
=cut
-.sub 'convert_radix'
+.sub 'convert_string_to_int'
.param string source
- .param string radix
+ .param string radix :optional
+ .param int has_radix :opt_flag
.param int pos :optional
.param int has_pos :opt_flag
+ .local int base
+ base = 10
+
if has_pos goto have_pos
pos = 0
have_pos:
- .local int startpos
- startpos = pos
- .local int base
+ unless has_radix goto have_base
base = radix
if base > 0 goto have_base
- radix = downcase radix
- base = index " b o d x", radix
- if base <= 0 goto err_radix
+ base = index ' bB oOdD xX', radix
+ if base < 2 goto err_radix
+ base &= 0xfe
have_base:
- .local int decnum, isbracketed
- .local string result
- decnum = 0
- result = ''
- $S0 = substr source, pos, 1
- isbracketed = iseq $S0, '['
- pos += isbracketed
+ .local int result, startpos
+ result = 0
+ startpos = pos
+
scan_char_loop:
+ ## Figure out the digit's value (case-insensitive). Using C<downcase>
+ ## for case-insensitive lookup won't work on systems w/o ICU, so
+ ## an index and shift is a quick way to solve this.
$S0 = substr source, pos, 1
- $S0 = downcase $S0
.local int digitval
- digitval = index '0123456789abcdefghijklmnopqrstuvwxyz', $S0
+ digitval = index '0 1 2 3 4 5 6 7 8 9
aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ', $S0
if digitval < 0 goto scan_char_end
+ digitval >>= 1
if digitval >= base goto scan_char_end
- decnum *= base
- decnum += digitval
+ result *= base
+ result += digitval
inc pos
goto scan_char_loop
scan_char_end:
- $S1 = chr decnum
- concat result, $S1
- unless isbracketed goto scan_end
- if $S0 == ']' goto scan_end
- if $S0 != ',' goto err_bracket
- inc pos
- decnum = 0
- goto scan_char_loop
- scan_end:
- pos += isbracketed
- end:
$I0 = pos - startpos
.return (result, $I0)
err_radix:
- .local string message
- message = "Invalid radix specified: "
- concat message, radix
- goto err_throw
- err_bracket:
- message = "Invalid character in bracketed conversion"
- err_throw:
+ $S0 = "Invalid radix: "
+ concat $S0, radix
$P0 = new .Exception
- $P0['message'] = message
+ $P0['_message'] = $S0
throw $P0
- goto end
+ .return (0, 0)
.end
+=item C<convert_digits_to_string(source [, radix [, pos])>
+
+Converts a decimal, hexadecimal, octal, or binary digit sequence
+given in C<source> (offset by C<pos>) and according to C<radix>
+into its corresponding codepoint(s). Returns the converted
+codepoints and the number of source characters used in the
+conversion.
+
+=cut
+
+.sub 'convert_digits_to_string'
+ .param string source
+ .param string radix :optional
+ .param int has_radix :opt_flag
+ .param int pos :optional
+ .param int has_pos :opt_flag
+
+ if has_pos goto have_pos
+ pos = 0
+ have_pos:
+ if has_radix goto have_radix
+ radix = '10'
+ have_radix:
+
+ .local int startpos, isbracketed
+ startpos = pos
+ $S0 = substr source, pos, 1
+ if $S0 == '[' goto bracketed
+ ($I0, $I1) = 'convert_string_to_int'(source, radix, pos)
+ $S1 = chr $I0
+ .return ($S1, $I1)
+ bracketed:
+ $S1 = ''
+ bracketed_loop:
+ inc pos
+ ($I0, $I1) = 'convert_string_to_int'(source, radix, pos)
+ if $I1 == 0 goto bracketed_end
+ $S0 = chr $I0
+ concat $S1, $S0
+ pos += $I1
+ bracketed_end:
+ $S0 = substr source, pos, 1
+ if $S0 == ',' goto bracketed_loop
+ if $S0 != ']' goto err_bracketed
+ inc pos
+ $I1 = pos - startpos
+ .return ($S1, $I1)
+
+ err_bracketed:
+ $S0 = "Missing close ']' at offset "
+ $S1 = pos
+ $S0 .= $S1
+ $S0 .= ", found '"
+ $S1 = substr source, pos, 1
+ $S0 .= $S1
+ $S0 .= "'"
+ $P0 = new .Exception
+ $P0['_message'] = $S0
+ throw $P0
+ .return ('', 0)
+.end
+
=back
=head1 AUTHORS
Modified: trunk/t/library/string_utils.t
==============================================================================
--- trunk/t/library/string_utils.t (original)
+++ trunk/t/library/string_utils.t Wed Apr 4 20:20:48 2007
@@ -12,7 +12,7 @@
=cut
-.const string TESTS = '24'
+.const string TESTS = '29'
.sub main :main
load_bytecode 'Test/Builder.pir'
@@ -31,34 +31,41 @@
$I0 = istrue $P0
test.'ok'($I0, 'loaded chomp')
- $P0 = get_hll_global ['String';'Utils'], 'convert_radix'
+ $P0 = get_hll_global ['String';'Utils'], 'convert_string_to_int'
$I0 = istrue $P0
- test.'ok'($I0, 'loaded radix')
+ test.'ok'($I0, 'loaded convert_string_to_int')
+
+ test_radix('' , 'x', 0, 0 , 0, 'null string')
+ test_radix('nothing', 'x', 0, 0 , 0, 'no leading digits')
# \x conversions
- test_radix('41' , 'x', 0, 'A' , 2, '\x41')
- test_radix('42G' , 'x', 0, 'B' , 2, '\x42')
- test_radix('[41]' , 'x', 0, 'A' , 4, '\x[41]')
- test_radix('[41,42]', 'x', 0, 'AB' , 7, '\x[41,42]')
- test_radix('0a' , 'x', 0, "\n" , 2, '\x0a')
- test_radix('000a' , 'x', 0, "\n" , 4, '\x000a')
- test_radix('ab0aX' , 'x', 2, "\n" , 2, 'pos offset')
- test_radix('0A' , 'x', 0, "\n" , 2, '\x0a')
- test_radix('000A' , 'x', 0, "\n" , 4, '\x000a')
- test_radix('AB0AX' , 'x', 2, "\n" , 2, 'pos offset')
- test_radix('[41,42]', 'x', 1, 'A' , 2, 'pos offset')
- test_radix('[41,42]', 'x', 4, 'B' , 2, 'pos offset')
+ test_radix('41' , 'x', 0, 65 , 2, 'x41')
+ test_radix('42G' , 'x', 0, 66 , 2, 'x42G')
+ test_radix('0a' , 'x', 0, 10 , 2, 'x0a')
+ test_radix('000a' , 'x', 0, 10 , 4, 'x000a')
+ test_radix('abcd' , 'x', 0, 0xabcd, 4, 'xabcd')
+ test_radix('ab0aX' , 'x', 2, 10 , 2, 'pos offset')
+ test_radix('0A' , 'x', 0, 10 , 2, 'x0A')
+ test_radix('000A' , 'x', 0, 10 , 4, 'x000A')
+ test_radix('ABCD' , 'x', 0, 0xabcd, 4, 'xABCD')
+ test_radix('AB0AX' , 'x', 2, 10 , 2, 'pos offset')
# \o conversions
- test_radix('41' , 'o', 0, '!' , 2, '\o41')
- test_radix('428' , 'o', 0, '"' , 2, '\o42')
- test_radix('[41]' , 'o', 0, '!' , 4, '\o[41]')
- test_radix('[41,42]', 'o', 0, '!"' , 7, '\o[41,42]')
- test_radix('012' , 'o', 0, "\n" , 3, '\o012')
- test_radix('00012' , 'o', 0, "\n" , 5, '\o00012')
- test_radix('12012' , 'o', 2, "\n" , 3, 'pos offset')
- test_radix('[41,42]', 'o', 1, '!' , 2, 'pos offset')
- test_radix('[41,42]', 'o', 4, '"' , 2, 'pos offset')
+ test_radix('41' , 'o', 0, 33 , 2, 'o41')
+ test_radix('42G' , 'o', 0, 34 , 2, 'o42G')
+ test_radix('12' , 'o', 0, 10 , 2, 'o12')
+ test_radix('0012' , 'o', 0, 10 , 4, 'o0012')
+ test_radix('1238' , 'o', 0, 83 , 3, 'o1238')
+ test_radix('1212X' , 'o', 2, 10 , 2, 'pos offset')
+
+ test_radix_digits('41' , 'x', 0, 'A' , 2, '\x41')
+ test_radix_digits('41XYZ' , 'x', 0, 'A' , 2, '\x41')
+ test_radix_digits('[41,42]' , 'x', 0, 'AB', 7, '\x[41,42]')
+ test_radix_digits('[41,42]XYZ', 'x', 0, 'AB', 7, '\x[41,42]')
+ test_radix_digits('[41,42]' , 'x', 1, 'A' , 2, '\x41')
+ test_radix_digits('[41,42]' , 'x', 4, 'B', 2, '\x42')
+ test_radix_digits('2000' , 'x', 0, unicode:"\u2000", 4, '\x2000')
+ test_radix_digits('1680' , 'x', 0, unicode:"\u1680", 4, '\x1680')
.end
@@ -66,17 +73,17 @@
.param string source
.param string radix
.param int pos
- .param string target
+ .param int target
.param int len
.param string description
.param string todo :named('todo') :optional
.param int has_todo :opt_flag
- .local pmc convert_radix
- .local string t_target
- .local int t_len, ok_target, ok_len, ok
- convert_radix = get_hll_global ['String';'Utils'], 'convert_radix'
- (t_target, t_len) = convert_radix(source, radix, pos)
+ .local pmc convert
+ convert = get_hll_global ['String';'Utils'], 'convert_string_to_int'
+
+ .local int t_target, t_len, ok_target, ok_len, ok
+ (t_target, t_len) = convert(source, radix, pos)
ok_target = iseq t_target, target
ok_len = iseq t_len, len
@@ -93,4 +100,34 @@
.return ()
.end
+.sub test_radix_digits
+ .param string source
+ .param string radix
+ .param int pos
+ .param int target
+ .param int len
+ .param string description
+ .param string todo :named('todo') :optional
+ .param int has_todo :opt_flag
+
+ .local pmc convert
+ convert = get_hll_global ['String';'Utils'], 'convert_digits_to_string'
+
+ .local int t_target, t_len, ok_target, ok_len, ok
+ (t_target, t_len) = convert(source, radix, pos)
+
+ ok_target = iseq t_target, target
+ ok_len = iseq t_len, len
+ ok = and ok_target, ok_len
+
+ .local pmc test
+ test = get_global '$test'
+
+ if has_todo goto todo_test
+ test.'ok'(ok, description)
+ .return ()
+ todo_test:
+ test.'todo'(ok, description, todo)
+ .return ()
+.end