# New Ticket Created by  "jason switzer" 
# Please include the string:  [perl #59222]
# in the subject line of all future correspondence about this issue. 
# <URL: http://rt.perl.org/rt3/Ticket/Display.html?id=59222 >


This patch adds radix notation (:\d<...>) to the string-to-number conversion
routine Perl6Str::get_number. The tests haven't been written and
incorporated to test specifically the string conversion but the spectest
suite passes as of r31354 (parrot) and r22313 (t/spec). Cordination with
moritz_ will take place to determine the location and the extent of the
tests that will be created.

Not all radix constructs are supported yet (in the works). Currently does
not support nested notation, such as :10<12*23**3> or functional forms such
as :10('1234').

Thanks,

Jason "s1n" Switzer
Index: perl6str.pmc
===================================================================
--- perl6str.pmc	(revision 31353)
+++ perl6str.pmc	(working copy)
@@ -23,6 +23,58 @@
 #include <ctype.h>
 #include <math.h>
 
+FLOATVAL parse_number(const char** start, const char* stop, FLOATVAL radix) {
+    FLOATVAL number = 0.0;
+    //continue until the end or until we've hit a non-digit
+    while (*start < stop && isxdigit((unsigned char)**start)) {
+        /* number must not contain values less than the radix */
+        unsigned int current;
+        if (isdigit(**start)) current = (**start) - '0';
+        else current = tolower(**start) - 'a' + 10;
+        //number += ((*start) - '0') * frac;
+        if (current >= radix)
+            return number;
+
+        number = number * radix + current;
+        (*start)++;
+
+        /* skip past the digit seperator _ */
+        if (*start < stop && **start == '_') {
+            if(!isxdigit((unsigned char)*((*start) + 1))) {
+                return number;
+            }
+            (*start)++;
+        }
+    }
+    return number;
+}
+
+FLOATVAL parse_fraction(const char** start, const char* stop, FLOATVAL radix) {
+    FLOATVAL frac = 1.0;
+    FLOATVAL number = 0.0;
+    while (*start < stop && isxdigit((unsigned char)**start)) {
+        frac /= radix;
+        unsigned int current;
+        if (isdigit(**start)) current = (**start) - '0';
+        else current = tolower(**start) - 'a' + 10;
+
+        /* if we discover scientific notation, stop parsing */
+        if (tolower(**start) == 'e' && radix == 10)
+            return number;
+        if (current >= radix)
+            return 0;
+        
+        number += current * frac;
+        (*start)++;
+        
+        /* skip past the digit seperator _ */
+        if (*start < stop && **start == '_'
+            && isdigit((unsigned char)**(start + 1)))
+            (*start)++;
+    }
+    return number;
+}
+
 pmclass Perl6Str
     extends  String
     provides string
@@ -63,12 +115,14 @@
 
     FLOATVAL get_number() {
         STRING   *s    = (STRING *)PMC_str_val(SELF);
-        FLOATVAL  sign = 1.0;
-        FLOATVAL  f    = 0.0;
+        FLOATVAL sign = 1.0;
+        FLOATVAL f    = 0.0;
+        FLOATVAL radix = 10.0;
+        int radfound = 0;
 
-        if (s) {
+        if (s && s->strstart) {
             const char *s1         = s->strstart;
-            const char * const end = s1 + s->bufused;
+            const char * const end = s1 + strlen(s1);
 
             /* skip leading whitespace */
             while (s1 < end && isspace((unsigned char)*s1))
@@ -92,92 +146,59 @@
                     case 'x':
                     case 'X':
                         s1++;
-                        while (s1 < end && isxdigit((unsigned char)*s1)) {
-                            f *= 16;
-                            if (isdigit((unsigned char)*s1))
-                                f += (*s1) - '0';
-                            else
-                                f += tolower((unsigned char)*s1) - 'a' + 10;
-
-                            s1++;
-
-                            if (s1 < end && *s1 == '_')
-                                s1++;
-                        }
-                        return sign * f;
+                        return parse_number(&s1, end, 16);
                     case 'd':
                     case 'D':
                         s1++;
-                        while (s1 < end && isdigit((unsigned char)*s1)) {
-                            f = f * 10 + (*s1) - '0';
-                            s1++;
-
-                            if (s1 < end && *s1 == '_')
-                                s1++;
-                        }
-
-                        return sign * f;
+                        return parse_number(&s1, end, 10);
                     case 'o':
                     case 'O':
                         s1++;
-                        while (s1 < end && isdigit((unsigned char)*s1)
-                                        && *s1 <= '7') {
-                            f = f * 8 + (*s1) - '0';
-
-                            s1++;
-
-                            if (s1 < end && *s1 == '_')
-                                s1++;
-                        }
-
-                        return sign * f;
+                        return parse_number(&s1, end, 8);
                     case 'b':
                     case 'B':
                         s1++;
-                        while (s1 < end && (*s1 == '0' || *s1 == '1')) {
-                            f = f * 2 + (*s1) - '0';
-                            s1++;
-
-                            if (s1 < end && *s1 == '_')
-                                s1++;
-                        }
-
-                        return sign * f;
+                        return parse_number(&s1, end, 2);
                     default:
                         break;
                 }
                 if (s1 < end && *s1 == '_'
-                && isdigit((unsigned char)*(s1+1)))
+                    && isdigit((unsigned char)*(s1 + 1)))
                     s1++;
             }
 
-            /* handle integer part */
-            while (s1 < end && isdigit((unsigned char)*s1)) {
-                f = f * 10 + (*s1) - '0';
+            /* handle \d+:([0..9A..Za..z]) radix notation */
+            if (s1 < end && *s1 == ':') {
                 s1++;
+                radfound = 1;
+		
+                if (s1 >= end)
+                    return f;
 
-                if (s1 < end && *s1 == '_' && isdigit((unsigned char)*(s1+1)))
+                radix = parse_number(&s1, end, 10);
+                if(*s1 != '<') {
+                    return 0.0;
+                } else {
                     s1++;
+                }
             }
 
-            /* handle floating point part */
+
+            /* handle integer part */
+            if (s1 < end && isxdigit((unsigned char)*s1)) {
+                f = parse_number(&s1, end, radix);
+            }
+
+            /* handle doubleing point part */
             if (s1 < end && *s1 == '.') {
-                FLOATVAL frac = 1.0;
                 s1++;
-                while (s1 < end && isdigit((unsigned char)*s1)) {
-                    frac /= 10;
-                    f    += ((*s1) - '0') * frac;
-                    s1++;
-                    if (s1 < end && *s1 == '_'
-                    && isdigit((unsigned char)*(s1+1)))
-                        s1++;
-                }
+                f += parse_fraction(&s1, end, radix);
             }
 
             /* handle exponential part */
             if (s1 < end && (*s1 == 'e' || *s1 == 'E')) {
-                INTVAL exp_val  = 0;
-                INTVAL exp_sign = 1;
+                int exp_val  = 0;
+                int exp_sign = 1;
                 s1++;
 
                 if (s1 < end && *s1 == '+')
@@ -197,6 +218,10 @@
 
                 return sign * f * pow(10.0, exp_val * exp_sign);
             }
+            
+            //FIXME how to check for missing >?
+            if (radfound && *s1 != '>')
+                return 0.0;
         }
 
         return sign * f;

Reply via email to