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
 

Reply via email to