summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2014-07-16 00:41:03 +0200
committerAraq <rumpf_a@web.de>2014-07-16 00:41:03 +0200
commita7911addf71fdfbf5008afd938b49116d462d908 (patch)
treed1ce9e4306882f722da587bce7992ebe632aab24 /lib
parent396b5619fc72ccc4a2f2110fd59b21e0a8a7250e (diff)
downloadNim-a7911addf71fdfbf5008afd938b49116d462d908.tar.gz
parseBiggestFloat is now builtin
Diffstat (limited to 'lib')
-rw-r--r--lib/pure/parseutils.nim166
-rw-r--r--lib/system/ansi_c.nim5
-rw-r--r--lib/system/jssys.nim84
-rw-r--r--lib/system/sysstr.nim111
4 files changed, 274 insertions, 92 deletions
diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim
index 9b3f79a74..06ee07aa8 100644
--- a/lib/pure/parseutils.nim
+++ b/lib/pure/parseutils.nim
@@ -231,94 +231,96 @@ proc parseInt*(s: string, number: var int, start = 0): int {.
   else:
     number = int(res)
 
-proc parseBiggestFloat*(s: string, number: var BiggestFloat, start = 0): int {.
-  rtl, extern: "npuParseBiggestFloat", noSideEffect.} =
-  ## parses a float starting at `start` and stores the value into `number`.
-  ## Result is the number of processed chars or 0 if a parsing error
-  ## occurred.
-
-  type struct_lconv {.importc: "struct lconv",header:"<locale.h>".} =
-    object
-      # Unneeded fields have been omitted.
-      decimal_point: cstring
-
-  proc localeconv(): ptr struct_lconv {.importc, header: "<locale.h>",
-    noSideEffect.}
-
-  proc strtod(buf: cstring, endptr: ptr cstring): float64 {.importc,
-    header: "<stdlib.h>", noSideEffect.}
-
-  # This routine leverages `strtod()` for the non-trivial task of
-  # parsing floating point numbers correctly. Because `strtod()` is
-  # locale-dependent with respect to the radix character, we create
-  # a copy where the decimal point is replaced with the locale's
-  # radix character.
-
-  var
-    i = start
-    sign = 1.0
-    t = ""
-    hasdigits = false
-  
-  # Sign?
-  if s[i] == '+' or s[i] == '-':
-    if s[i] == '-':
+when defined(nimParseBiggestFloatMagic):
+  proc parseBiggestFloat*(s: string, number: var BiggestFloat, start = 0): int {.
+    magic: "ParseBiggestFloat", importc: "nimParseBiggestFloat", noSideEffect.}
+    ## parses a float starting at `start` and stores the value into `number`.
+    ## Result is the number of processed chars or 0 if a parsing error
+    ## occurred.
+else:
+  proc tenToThePowerOf(b: int): BiggestFloat =
+    var b = b
+    var a = 10.0
+    result = 1.0
+    while true:
+      if (b and 1) == 1:
+        result *= a
+      b = b shr 1
+      if b == 0: break
+      a *= a
+
+  proc parseBiggestFloat*(s: string, number: var BiggestFloat, start = 0): int {.
+    rtl, extern: "npuParseBiggestFloat", noSideEffect.} =
+    ## parses a float starting at `start` and stores the value into `number`.
+    ## Result is the number of processed chars or 0 if there occured a parsing
+    ## error.
+    var
+      esign = 1.0
+      sign = 1.0
+      i = start
+      exponent: int
+      flags: int
+    number = 0.0
+    if s[i] == '+': inc(i)
+    elif s[i] == '-':
       sign = -1.0
-    add(t, s[i])
-    inc(i)
-
-  # NaN?
-  if s[i] == 'N' or s[i] == 'n':
-    if s[i+1] == 'A' or s[i+1] == 'a':
-      if s[i+2] == 'N' or s[i+2] == 'n':
-        if s[i+3] notin IdentChars:
-          number = NaN
-          return i+3 - start
-    return 0
-
-  # Inf?
-  if s[i] == 'I' or s[i] == 'i':
-    if s[i+1] == 'N' or s[i+1] == 'n':
-      if s[i+2] == 'F' or s[i+2] == 'f':
-        if s[i+3] notin IdentChars: 
-          number = Inf*sign
-          return i+3 - start
-    return 0
-
-  # Integer part?
-  while s[i] in {'0'..'9'}:
-    hasdigits = true
-    add(t, s[i])
-    inc(i)
-    while s[i] == '_': inc(i)
-
-  # Fractional part?
-  if s[i] == '.':
-    add(t, localeconv().decimal_point)
-    inc(i)
-    while s[i] in {'0'..'9'}:
-      hasdigits = true
-      add(t, s[i])
       inc(i)
-      while s[i] == '_': inc(i)
-  if not hasdigits:
-    return 0
-
-  # Exponent?
-  if s[i] in {'e', 'E'}:
-    add(t, s[i])
-    inc(i)
-    if s[i] in {'+', '-'}:
-      add(t, s[i])
-      inc(i)
-    if s[i] notin {'0'..'9'}:
+    if s[i] == 'N' or s[i] == 'n':
+      if s[i+1] == 'A' or s[i+1] == 'a':
+        if s[i+2] == 'N' or s[i+2] == 'n':
+          if s[i+3] notin IdentChars:
+            number = NaN
+            return i+3 - start
+      return 0
+    if s[i] == 'I' or s[i] == 'i':
+      if s[i+1] == 'N' or s[i+1] == 'n':
+        if s[i+2] == 'F' or s[i+2] == 'f':
+          if s[i+3] notin IdentChars: 
+            number = Inf*sign
+            return i+3 - start
       return 0
     while s[i] in {'0'..'9'}:
-      add(t, s[i])
+      # Read integer part
+      flags = flags or 1
+      number = number * 10.0 + toFloat(ord(s[i]) - ord('0'))
       inc(i)
       while s[i] == '_': inc(i)
-  number = strtod(t, nil)
-  result = i - start
+    # Decimal?
+    if s[i] == '.':
+      var hd = 1.0
+      inc(i)
+      while s[i] in {'0'..'9'}:
+        # Read fractional part
+        flags = flags or 2
+        number = number * 10.0 + toFloat(ord(s[i]) - ord('0'))
+        hd = hd * 10.0
+        inc(i)
+        while s[i] == '_': inc(i)
+      number = number / hd # this complicated way preserves precision
+    # Again, read integer and fractional part
+    if flags == 0: return 0
+    # Exponent?
+    if s[i] in {'e', 'E'}:
+      inc(i)
+      if s[i] == '+':
+        inc(i)
+      elif s[i] == '-':
+        esign = -1.0
+        inc(i)
+      if s[i] notin {'0'..'9'}:
+        return 0
+      while s[i] in {'0'..'9'}:
+        exponent = exponent * 10 + ord(s[i]) - ord('0')
+        inc(i)
+        while s[i] == '_': inc(i)
+    # Calculate Exponent
+    let hd = tenToThePowerOf(exponent)
+    if esign > 0.0: number = number * hd
+    else:           number = number / hd
+    # evaluate sign
+    number = number * sign
+    result = i - start
+
 
 proc parseFloat*(s: string, number: var float, start = 0): int {.
   rtl, extern: "npuParseFloat", noSideEffect.} =
diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim
index da101cc2c..511a006d3 100644
--- a/lib/system/ansi_c.nim
+++ b/lib/system/ansi_c.nim
@@ -106,13 +106,10 @@ proc c_fopen(filename, mode: cstring): C_TextFileStar {.
   importc: "fopen", header: "<stdio.h>".}
 proc c_fclose(f: C_TextFileStar) {.importc: "fclose", header: "<stdio.h>".}
 
-proc c_sprintf(buf, frmt: cstring): int {.header: "<stdio.h>", 
+proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>", 
   importc: "sprintf", varargs, noSideEffect.}
   # we use it only in a way that cannot lead to security issues
 
-proc c_localeconv():ptr cstring {.header: "<locale.h>",
-  importc: "localeconv", noSideEffect.}
-
 proc c_fread(buf: pointer, size, n: int, f: C_BinaryFileStar): int {.
   importc: "fread", header: "<stdio.h>".}
 proc c_fseek(f: C_BinaryFileStar, offset: clong, whence: int): int {.
diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim
index 52f8873cf..1196a81ef 100644
--- a/lib/system/jssys.nim
+++ b/lib/system/jssys.nim
@@ -639,3 +639,87 @@ proc addChar(x: string, c: char) {.compilerproc, asmNoStackFrame.} =
   """
 
 {.pop.}
+
+proc tenToThePowerOf(b: int): BiggestFloat =
+  var b = b
+  var a = 10.0
+  result = 1.0
+  while true:
+    if (b and 1) == 1:
+      result *= a
+    b = b shr 1
+    if b == 0: break
+    a *= a
+
+const
+  IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
+
+# XXX use JS's native way here
+proc nimParseBiggestFloat(s: string, number: var BiggestFloat, start = 0): int {.
+                          compilerProc.} =
+  var
+    esign = 1.0
+    sign = 1.0
+    i = start
+    exponent: int
+    flags: int
+  number = 0.0
+  if s[i] == '+': inc(i)
+  elif s[i] == '-':
+    sign = -1.0
+    inc(i)
+  if s[i] == 'N' or s[i] == 'n':
+    if s[i+1] == 'A' or s[i+1] == 'a':
+      if s[i+2] == 'N' or s[i+2] == 'n':
+        if s[i+3] notin IdentChars:
+          number = NaN
+          return i+3 - start
+    return 0
+  if s[i] == 'I' or s[i] == 'i':
+    if s[i+1] == 'N' or s[i+1] == 'n':
+      if s[i+2] == 'F' or s[i+2] == 'f':
+        if s[i+3] notin IdentChars: 
+          number = Inf*sign
+          return i+3 - start
+    return 0
+  while s[i] in {'0'..'9'}:
+    # Read integer part
+    flags = flags or 1
+    number = number * 10.0 + toFloat(ord(s[i]) - ord('0'))
+    inc(i)
+    while s[i] == '_': inc(i)
+  # Decimal?
+  if s[i] == '.':
+    var hd = 1.0
+    inc(i)
+    while s[i] in {'0'..'9'}:
+      # Read fractional part
+      flags = flags or 2
+      number = number * 10.0 + toFloat(ord(s[i]) - ord('0'))
+      hd = hd * 10.0
+      inc(i)
+      while s[i] == '_': inc(i)
+    number = number / hd # this complicated way preserves precision
+  # Again, read integer and fractional part
+  if flags == 0: return 0
+  # Exponent?
+  if s[i] in {'e', 'E'}:
+    inc(i)
+    if s[i] == '+':
+      inc(i)
+    elif s[i] == '-':
+      esign = -1.0
+      inc(i)
+    if s[i] notin {'0'..'9'}:
+      return 0
+    while s[i] in {'0'..'9'}:
+      exponent = exponent * 10 + ord(s[i]) - ord('0')
+      inc(i)
+      while s[i] == '_': inc(i)
+  # Calculate Exponent
+  let hd = tenToThePowerOf(exponent)
+  if esign > 0.0: number = number * hd
+  else:           number = number / hd
+  # evaluate sign
+  number = number * sign
+  result = i - start
diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim
index 183ea0c8d..b3dc9c14e 100644
--- a/lib/system/sysstr.nim
+++ b/lib/system/sysstr.nim
@@ -252,15 +252,114 @@ proc nimIntToStr(x: int): string {.compilerRtl.} =
 
 proc nimFloatToStr(f: float): string {.compilerproc.} =
   var buf: array [0..64, char]
-  var n:int = c_sprintf(buf, "%.16g", f)
+  var n: int = c_sprintf(buf, "%.16g", f)
+  var hasDot = false
   for i in 0..n-1:
-    if buf[i] notin {'0'..'9','-'}:
-      return $buf
-  buf[n] = c_localeconv()[0]
-  buf[n+1] = '0'
-  buf[n+2] = '\0'
+    if buf[i] == ',':
+      buf[i] = '.'
+      hasDot = true
+    elif buf[i] in {'e', 'E', '.'}: 
+      hasDot = true
+  if not hasDot:
+    buf[n] = '.'
+    buf[n+1] = '0'
+    buf[n+2] = '\0'
   result = $buf
 
+proc strtod(buf: cstring, endptr: ptr cstring): float64 {.importc,
+  header: "<stdlib.h>", noSideEffect.}
+
+var decimalPoint: char
+
+proc getDecimalPoint(): char =
+  result = decimalPoint
+  if result == '\0':
+    if strtod("0,5", nil) == 0.5: result = ','
+    else: result = '.'
+    # yes this is threadsafe in practice, spare me:
+    decimalPoint = result
+
+const
+  IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
+
+proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
+                          start = 0): int {.compilerProc.} =
+  # This routine leverages `strtod()` for the non-trivial task of
+  # parsing floating point numbers correctly. Because `strtod()` is
+  # locale-dependent with respect to the radix character, we create
+  # a copy where the decimal point is replaced with the locale's
+  # radix character.
+  var
+    i = start
+    sign = 1.0
+    t: array[128, char]
+    ti = 0
+    hasdigits = false
+
+  template addToBuf(c) =
+    if ti < t.high:
+      t[ti] = c; inc(ti)
+  
+  # Sign?
+  if s[i] == '+' or s[i] == '-':
+    if s[i] == '-':
+      sign = -1.0
+    t[ti] = s[i]
+    inc(i); inc(ti)
+
+  # NaN?
+  if s[i] == 'N' or s[i] == 'n':
+    if s[i+1] == 'A' or s[i+1] == 'a':
+      if s[i+2] == 'N' or s[i+2] == 'n':
+        if s[i+3] notin IdentChars:
+          number = NaN
+          return i+3 - start
+    return 0
+
+  # Inf?
+  if s[i] == 'I' or s[i] == 'i':
+    if s[i+1] == 'N' or s[i+1] == 'n':
+      if s[i+2] == 'F' or s[i+2] == 'f':
+        if s[i+3] notin IdentChars: 
+          number = Inf*sign
+          return i+3 - start
+    return 0
+
+  # Integer part?
+  while s[i] in {'0'..'9'}:
+    hasdigits = true
+    addToBuf(s[i])
+    inc(i);
+    while s[i] == '_': inc(i)
+
+  # Fractional part?
+  if s[i] == '.':
+    addToBuf(getDecimalPoint())
+    inc(i)
+    while s[i] in {'0'..'9'}:
+      hasdigits = true
+      addToBuf(s[i])
+      inc(i)
+      while s[i] == '_': inc(i)
+  if not hasdigits:
+    return 0
+
+  # Exponent?
+  if s[i] in {'e', 'E'}:
+    addToBuf(s[i])
+    inc(i)
+    if s[i] in {'+', '-'}:
+      addToBuf(s[i])
+      inc(i)
+    if s[i] notin {'0'..'9'}:
+      return 0
+    while s[i] in {'0'..'9'}:
+      addToBuf(s[i])
+      inc(i)
+      while s[i] == '_': inc(i)
+  number = strtod(t, nil)
+  result = i - start
+
 proc nimInt64ToStr(x: int64): string {.compilerRtl.} =
   result = newString(sizeof(x)*4)
   var i = 0