parseBiggestFloat is now builtin

This commit is contained in:
Araq
2014-07-16 00:41:03 +02:00
parent 396b5619fc
commit a7911addf7
10 changed files with 309 additions and 95 deletions

View File

@@ -553,7 +553,7 @@ type
mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq,
mIsPartOf, mAstToStr, mParallel,
mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast,
mNewString, mNewStringOfCap,
mNewString, mNewStringOfCap, mParseBiggestFloat,
mReset,
mArray, mOpenArray, mRange, mSet, mSeq, mVarargs,
mOrdinal,

View File

@@ -1640,7 +1640,8 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet,
mInSet:
genSetOp(p, e, d, op)
of mNewString, mNewStringOfCap, mCopyStr, mCopyStrLast, mExit:
of mNewString, mNewStringOfCap, mCopyStr, mCopyStrLast, mExit,
mParseBiggestFloat:
var opr = e.sons[0].sym
if lfNoDecl notin opr.loc.flags:
discard cgsym(p.module, opr.loc.r.ropeToStr)

View File

@@ -50,6 +50,7 @@ proc initDefines*() =
defineSymbol("nimunion")
defineSymbol("nimnewshared")
defineSymbol("nimrequiresnimframe")
defineSymbol("nimparsebiggestfloatmagic")
# add platform specific symbols:
case targetCPU

View File

@@ -16,7 +16,7 @@ import ast except getstr
import
strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, unsigned,
parser, vmdeps, idents, trees, renderer, options, transf
parser, vmdeps, idents, trees, renderer, options, transf, parseutils
from semfold import leValueConv, ordinalValToString
from evaltempl import evalTemplate
@@ -776,6 +776,18 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
createStr regs[ra]
regs[ra].node.strVal = substr(regs[rb].node.strVal,
regs[rc].intVal.int, regs[rd].intVal.int)
of opcParseFloat:
decodeBC(rkInt)
inc pc
assert c.code[pc].opcode == opcParseFloat
let rd = c.code[pc].regA
var rcAddr = addr(regs[rc])
if rcAddr.kind == rkRegisterAddr: rcAddr = rcAddr.regAddr
elif regs[rc].kind != rkFloat:
myreset(regs[rc])
regs[rc].kind = rkFloat
regs[ra].intVal = parseBiggestFloat(regs[rb].node.strVal,
rcAddr.floatVal, regs[rd].intVal.int)
of opcRangeChck:
let rb = instr.regB
let rc = instr.regC

View File

@@ -66,7 +66,7 @@ type
opcMulSet, opcPlusSet, opcMinusSet, opcSymdiffSet, opcConcatStr,
opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq,
opcSwap, opcIsNil, opcOf, opcIs,
opcSubStr, opcConv, opcCast, opcQuit, opcReset,
opcSubStr, opcParseFloat, opcConv, opcCast, opcQuit, opcReset,
opcNarrowS, opcNarrowU,
opcAddStrCh,

View File

@@ -852,6 +852,24 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
c.freeTemp(tmp1)
c.freeTemp(tmp2)
c.freeTemp(tmp3)
of mParseBiggestFloat:
if dest < 0: dest = c.getTemp(n.typ)
var d2: TRegister
# skip 'nkHiddenAddr':
let d2AsNode = n.sons[2].sons[0]
if needsAsgnPatch(d2AsNode):
d2 = c.getTemp(getSysType(tyFloat))
else:
d2 = c.genx(d2AsNode)
var
tmp1 = c.genx(n.sons[1])
tmp3 = c.genx(n.sons[3])
c.gABC(n, opcParseFloat, dest, tmp1, d2)
c.gABC(n, opcParseFloat, tmp3)
c.freeTemp(tmp1)
c.freeTemp(tmp3)
c.genAsgnPatch(d2AsNode, d2)
c.freeTemp(d2)
of mReset:
unused(n, dest)
var d = c.genx(n.sons[1])

View File

@@ -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.
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
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] == '-':
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.} =

View File

@@ -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 {.

View File

@@ -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

View File

@@ -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