parseBiggestFloat is now builtin

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

View file

@ -553,7 +553,7 @@ type
mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq, mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq,
mIsPartOf, mAstToStr, mParallel, mIsPartOf, mAstToStr, mParallel,
mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast, mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast,
mNewString, mNewStringOfCap, mNewString, mNewStringOfCap, mParseBiggestFloat,
mReset, mReset,
mArray, mOpenArray, mRange, mSet, mSeq, mVarargs, mArray, mOpenArray, mRange, mSet, mSeq, mVarargs,
mOrdinal, 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, of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet,
mInSet: mInSet:
genSetOp(p, e, d, op) genSetOp(p, e, d, op)
of mNewString, mNewStringOfCap, mCopyStr, mCopyStrLast, mExit: of mNewString, mNewStringOfCap, mCopyStr, mCopyStrLast, mExit,
mParseBiggestFloat:
var opr = e.sons[0].sym var opr = e.sons[0].sym
if lfNoDecl notin opr.loc.flags: if lfNoDecl notin opr.loc.flags:
discard cgsym(p.module, opr.loc.r.ropeToStr) discard cgsym(p.module, opr.loc.r.ropeToStr)

View file

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

View file

@ -16,7 +16,7 @@ import ast except getstr
import import
strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, unsigned, 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 semfold import leValueConv, ordinalValToString
from evaltempl import evalTemplate from evaltempl import evalTemplate
@ -776,6 +776,18 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
createStr regs[ra] createStr regs[ra]
regs[ra].node.strVal = substr(regs[rb].node.strVal, regs[ra].node.strVal = substr(regs[rb].node.strVal,
regs[rc].intVal.int, regs[rd].intVal.int) 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: of opcRangeChck:
let rb = instr.regB let rb = instr.regB
let rc = instr.regC let rc = instr.regC

View file

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

View file

@ -852,6 +852,24 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
c.freeTemp(tmp1) c.freeTemp(tmp1)
c.freeTemp(tmp2) c.freeTemp(tmp2)
c.freeTemp(tmp3) 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: of mReset:
unused(n, dest) unused(n, dest)
var d = c.genx(n.sons[1]) var d = c.genx(n.sons[1])

View file

@ -231,94 +231,96 @@ proc parseInt*(s: string, number: var int, start = 0): int {.
else: else:
number = int(res) number = int(res)
proc parseBiggestFloat*(s: string, number: var BiggestFloat, start = 0): int {. when defined(nimParseBiggestFloatMagic):
rtl, extern: "npuParseBiggestFloat", noSideEffect.} = proc parseBiggestFloat*(s: string, number: var BiggestFloat, start = 0): int {.
## parses a float starting at `start` and stores the value into `number`. magic: "ParseBiggestFloat", importc: "nimParseBiggestFloat", noSideEffect.}
## Result is the number of processed chars or 0 if a parsing error ## parses a float starting at `start` and stores the value into `number`.
## occurred. ## 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>".} = proc parseBiggestFloat*(s: string, number: var BiggestFloat, start = 0): int {.
object rtl, extern: "npuParseBiggestFloat", noSideEffect.} =
# Unneeded fields have been omitted. ## parses a float starting at `start` and stores the value into `number`.
decimal_point: cstring ## Result is the number of processed chars or 0 if there occured a parsing
## error.
proc localeconv(): ptr struct_lconv {.importc, header: "<locale.h>", var
noSideEffect.} esign = 1.0
sign = 1.0
proc strtod(buf: cstring, endptr: ptr cstring): float64 {.importc, i = start
header: "<stdlib.h>", noSideEffect.} exponent: int
flags: int
# This routine leverages `strtod()` for the non-trivial task of number = 0.0
# parsing floating point numbers correctly. Because `strtod()` is if s[i] == '+': inc(i)
# locale-dependent with respect to the radix character, we create elif s[i] == '-':
# 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] == '-':
sign = -1.0 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) inc(i)
while s[i] == '_': inc(i) if s[i] == 'N' or s[i] == 'n':
if not hasdigits: if s[i+1] == 'A' or s[i+1] == 'a':
return 0 if s[i+2] == 'N' or s[i+2] == 'n':
if s[i+3] notin IdentChars:
# Exponent? number = NaN
if s[i] in {'e', 'E'}: return i+3 - start
add(t, s[i]) return 0
inc(i) if s[i] == 'I' or s[i] == 'i':
if s[i] in {'+', '-'}: if s[i+1] == 'N' or s[i+1] == 'n':
add(t, s[i]) if s[i+2] == 'F' or s[i+2] == 'f':
inc(i) if s[i+3] notin IdentChars:
if s[i] notin {'0'..'9'}: number = Inf*sign
return i+3 - start
return 0 return 0
while s[i] in {'0'..'9'}: 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) inc(i)
while s[i] == '_': inc(i) while s[i] == '_': inc(i)
number = strtod(t, nil) # Decimal?
result = i - start 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 {. proc parseFloat*(s: string, number: var float, start = 0): int {.
rtl, extern: "npuParseFloat", noSideEffect.} = rtl, extern: "npuParseFloat", noSideEffect.} =

View file

@ -106,13 +106,10 @@ proc c_fopen(filename, mode: cstring): C_TextFileStar {.
importc: "fopen", header: "<stdio.h>".} importc: "fopen", header: "<stdio.h>".}
proc c_fclose(f: C_TextFileStar) {.importc: "fclose", 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.} importc: "sprintf", varargs, noSideEffect.}
# we use it only in a way that cannot lead to security issues # 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 {. proc c_fread(buf: pointer, size, n: int, f: C_BinaryFileStar): int {.
importc: "fread", header: "<stdio.h>".} importc: "fread", header: "<stdio.h>".}
proc c_fseek(f: C_BinaryFileStar, offset: clong, whence: int): int {. 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.} {.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.} = proc nimFloatToStr(f: float): string {.compilerproc.} =
var buf: array [0..64, char] 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: for i in 0..n-1:
if buf[i] notin {'0'..'9','-'}: if buf[i] == ',':
return $buf buf[i] = '.'
buf[n] = c_localeconv()[0] hasDot = true
buf[n+1] = '0' elif buf[i] in {'e', 'E', '.'}:
buf[n+2] = '\0' hasDot = true
if not hasDot:
buf[n] = '.'
buf[n+1] = '0'
buf[n+2] = '\0'
result = $buf 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.} = proc nimInt64ToStr(x: int64): string {.compilerRtl.} =
result = newString(sizeof(x)*4) result = newString(sizeof(x)*4)
var i = 0 var i = 0