diff options
-rw-r--r-- | compiler/ccgexprs.nim | 10 | ||||
-rw-r--r-- | compiler/ccgstmts.nim | 2 | ||||
-rw-r--r-- | compiler/commands.nim | 9 | ||||
-rw-r--r-- | compiler/options.nim | 6 | ||||
-rw-r--r-- | compiler/passes.nim | 8 | ||||
-rw-r--r-- | lib/system.nim | 1 | ||||
-rw-r--r-- | lib/system/strmantle.nim | 293 | ||||
-rw-r--r-- | lib/system/sysstr.nim | 286 |
8 files changed, 311 insertions, 304 deletions
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 9b31167e3..b8a7f3b08 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -164,7 +164,7 @@ proc canMove(n: PNode): bool = # result = false proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = - if dest.storage == OnStack or not usesNativeGC(p.config): + if dest.storage == OnStack or not usesWriteBarrier(p.config): linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) elif dest.storage == OnHeap: # location is on heap @@ -256,7 +256,7 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = # (for objects, etc.): if needToCopy notin flags or tfShallow in skipTypes(dest.t, abstractVarRange).flags: - if dest.storage == OnStack or not usesNativeGC(p.config): + if dest.storage == OnStack or not usesWriteBarrier(p.config): useStringh(p.module) linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n", @@ -290,7 +290,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = if (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode): genRefAssign(p, dest, src, flags) else: - if dest.storage == OnStack or not usesNativeGC(p.config): + if dest.storage == OnStack or not usesWriteBarrier(p.config): linefmt(p, cpsStmts, "$1 = #copyString($2);$n", dest.rdLoc, src.rdLoc) elif dest.storage == OnHeap: # we use a temporary to care for the dreaded self assignment: @@ -1150,7 +1150,7 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) = addf(p.module.s[cfsTypeInit3], "$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)]) let args = [getTypeDesc(p.module, typ), ti, sizeExpr] - if a.storage == OnHeap and usesNativeGC(p.config): + if a.storage == OnHeap and usesWriteBarrier(p.config): # use newObjRC1 as an optimization if canFormAcycle(a.t): linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", a.rdLoc) @@ -1181,7 +1181,7 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope) = genTypeInfo(p.module, seqtype, dest.lode.info), length] var call: TLoc initLoc(call, locExpr, dest.lode, OnHeap) - if dest.storage == OnHeap and usesNativeGC(p.config): + if dest.storage == OnHeap and usesWriteBarrier(p.config): if canFormAcycle(dest.t): linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", dest.rdLoc) else: diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 7bb929d2b..8a0e07686 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -16,7 +16,7 @@ const # above X strings a hash-switch for strings is generated proc registerGcRoot(p: BProc, v: PSym) = - if p.config.selectedGC in {gcMarkAndSweep, gcGenerational, gcV2, gcRefc} and + if p.config.selectedGC in {gcMarkAndSweep, gcDestructors, gcV2, gcRefc} and containsGarbageCollectedRef(v.loc.t): # we register a specialized marked proc here; this has the advantage # that it works out of the box for thread local storage then :-) diff --git a/compiler/commands.nim b/compiler/commands.nim index 866405f9f..ef5a2a40f 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -215,7 +215,8 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo of "refc": result = conf.selectedGC == gcRefc of "v2": result = conf.selectedGC == gcV2 of "markandsweep": result = conf.selectedGC == gcMarkAndSweep - of "generational": result = conf.selectedGC == gcGenerational + of "generational": result = false + of "destructors": result = conf.selectedGC == gcDestructors of "go": result = conf.selectedGC == gcGo of "none": result = conf.selectedGC == gcNone of "stack", "regions": result = conf.selectedGC == gcRegions @@ -435,9 +436,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "markandsweep": conf.selectedGC = gcMarkAndSweep defineSymbol(conf.symbols, "gcmarkandsweep") - of "generational": - conf.selectedGC = gcGenerational - defineSymbol(conf.symbols, "gcgenerational") + of "destructors": + conf.selectedGC = gcDestructors + defineSymbol(conf.symbols, "gcdestructors") of "go": conf.selectedGC = gcGo defineSymbol(conf.symbols, "gogc") diff --git a/compiler/options.nim b/compiler/options.nim index c6e5c5b9f..98c20a9b5 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -103,8 +103,8 @@ type cmdJsonScript # compile a .json build file TStringSeq* = seq[string] TGCMode* = enum # the selected GC - gcNone, gcBoehm, gcGo, gcRegions, gcMarkAndSweep, gcRefc, - gcV2, gcGenerational + gcNone, gcBoehm, gcGo, gcRegions, gcMarkAndSweep, gcDestructors, + gcRefc, gcV2 IdeCmd* = enum ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod, @@ -368,7 +368,7 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool = else: discard proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in {cmdDoc, cmdIdeTools} -proc usesNativeGC*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc +proc usesWriteBarrier*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc template compilationCachePresent*(conf: ConfigRef): untyped = conf.symbolFiles in {v2Sf, writeOnlySf} diff --git a/compiler/passes.nim b/compiler/passes.nim index 5477d277f..45c726f2a 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -179,11 +179,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool { if graph.stopCompile(): break var n = parseTopLevelStmt(p) if n.kind == nkEmpty: break - #if {sfNoForward, sfReorder} * module.flags != {}: - when true: - # we now process the full AST in one go, so that destructor injection for top - # level statements works correctly. - if graph.stopCompile(): break + if {sfNoForward, sfReorder} * module.flags != {}: # read everything, no streaming possible var sl = newNodeI(nkStmtList, n.info) sl.add n @@ -195,7 +191,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool { sl = reorder(graph, sl, module) discard processTopLevelStmt(sl, a) break - #elif not processTopLevelStmt(n, a): break + elif not processTopLevelStmt(n, a): break closeParsers(p) if s.kind != llsStdIn: break closePasses(graph, a) diff --git a/lib/system.nim b/lib/system.nim index 9e91bc7db..d8ecfd55f 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -3264,6 +3264,7 @@ when not defined(JS): #and not defined(nimscript): {.push stack_trace: off, profiler:off.} when hasAlloc: include "system/sysstr" {.pop.} + when hasAlloc: include "system/strmantle" when hostOS != "standalone": include "system/sysio" when hasThreadSupport: diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim new file mode 100644 index 000000000..eca6c553c --- /dev/null +++ b/lib/system/strmantle.nim @@ -0,0 +1,293 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2018 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Compilerprocs for strings that do not depend on the string implementation. + +proc cmpStrings(a, b: NimString): int {.inline, compilerProc.} = + if a == b: return 0 + when defined(nimNoNil): + let alen = if a == nil: 0 else: a.len + let blen = if b == nil: 0 else: b.len + else: + if a == nil: return -1 + if b == nil: return 1 + let alen = a.len + let blen = b.len + let minlen = min(alen, blen) + if minlen > 0: + result = c_memcmp(addr a.data, addr b.data, minlen.csize) + if result == 0: + result = alen - blen + else: + result = alen - blen + +proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} = + if a == b: return true + when defined(nimNoNil): + let alen = if a == nil: 0 else: a.len + let blen = if b == nil: 0 else: b.len + else: + if a == nil or b == nil: return false + let alen = a.len + let blen = b.len + if alen == blen: + if alen == 0: return true + return equalMem(addr(a.data), addr(b.data), alen) + +proc hashString(s: string): int {.compilerproc.} = + # the compiler needs exactly the same hash function! + # this used to be used for efficient generation of string case statements + var h = 0 + for i in 0..len(s)-1: + h = h +% ord(s[i]) + h = h +% h shl 10 + h = h xor (h shr 6) + h = h +% h shl 3 + h = h xor (h shr 11) + h = h +% h shl 15 + result = h + +proc add*(result: var string; x: int64) = + let base = result.len + setLen(result, base + sizeof(x)*4) + var i = 0 + var y = x + while true: + var d = y div 10 + result[base+i] = chr(abs(int(y - d*10)) + ord('0')) + inc(i) + y = d + if y == 0: break + if x < 0: + result[base+i] = '-' + inc(i) + setLen(result, base+i) + # mirror the string: + for j in 0..i div 2 - 1: + swap(result[base+j], result[base+i-j-1]) + +proc nimIntToStr(x: int): string {.compilerRtl.} = + result = newStringOfCap(sizeof(x)*4) + result.add x + +proc add*(result: var string; x: float) = + when nimvm: + result.add $x + else: + var buf: array[0..64, char] + when defined(nimNoArrayToCstringConversion): + var n: int = c_sprintf(addr buf, "%.16g", x) + else: + var n: int = c_sprintf(buf, "%.16g", x) + var hasDot = false + for i in 0..n-1: + if buf[i] == ',': + buf[i] = '.' + hasDot = true + elif buf[i] in {'a'..'z', 'A'..'Z', '.'}: + hasDot = true + if not hasDot: + buf[n] = '.' + buf[n+1] = '0' + buf[n+2] = '\0' + # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' + # of '-1.#IND' are produced. + # We want to get rid of these here: + if buf[n-1] in {'n', 'N', 'D', 'd'}: + result.add "nan" + elif buf[n-1] == 'F': + if buf[0] == '-': + result.add "-inf" + else: + result.add "inf" + else: + var i = 0 + while buf[i] != '\0': + result.add buf[i] + inc i + +proc nimFloatToStr(f: float): string {.compilerproc.} = + result = newStringOfCap(8) + result.add f + +proc c_strtod(buf: cstring, endptr: ptr cstring): float64 {. + importc: "strtod", header: "<stdlib.h>", noSideEffect.} + +const + IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'} + powtens = [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22] + +proc nimParseBiggestFloat(s: string, number: var BiggestFloat, + start = 0): int {.compilerProc.} = + # This routine attempt to parse float that can parsed quickly. + # ie whose integer part can fit inside a 53bits integer. + # their real exponent must also be <= 22. If the float doesn't follow + # these restrictions, transform the float into this form: + # INTEGER * 10 ^ exponent and leave the work to standard `strtod()`. + # This avoid the problems of decimal character portability. + # see: http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ + var + i = start + sign = 1.0 + kdigits, fdigits = 0 + exponent: int + integer: uint64 + frac_exponent = 0 + exp_sign = 1 + first_digit = -1 + has_sign = false + + # Sign? + if s[i] == '+' or s[i] == '-': + has_sign = true + if s[i] == '-': + sign = -1.0 + 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 + + if s[i] in {'0'..'9'}: + first_digit = (s[i].ord - '0'.ord) + # Integer part? + while s[i] in {'0'..'9'}: + inc(kdigits) + integer = integer * 10'u64 + (s[i].ord - '0'.ord).uint64 + inc(i) + while s[i] == '_': inc(i) + + # Fractional part? + if s[i] == '.': + inc(i) + # if no integer part, Skip leading zeros + if kdigits <= 0: + while s[i] == '0': + inc(frac_exponent) + inc(i) + while s[i] == '_': inc(i) + + if first_digit == -1 and s[i] in {'0'..'9'}: + first_digit = (s[i].ord - '0'.ord) + # get fractional part + while s[i] in {'0'..'9'}: + inc(fdigits) + inc(frac_exponent) + integer = integer * 10'u64 + (s[i].ord - '0'.ord).uint64 + inc(i) + while s[i] == '_': inc(i) + + # if has no digits: return error + if kdigits + fdigits <= 0 and + (i == start or # no char consumed (empty string). + (i == start + 1 and has_sign)): # or only '+' or '- + return 0 + + if s[i] in {'e', 'E'}: + inc(i) + if s[i] == '+' or s[i] == '-': + if s[i] == '-': + exp_sign = -1 + + 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) # underscores are allowed and ignored + + var real_exponent = exp_sign*exponent - frac_exponent + let exp_negative = real_exponent < 0 + var abs_exponent = abs(real_exponent) + + # if exponent greater than can be represented: +/- zero or infinity + if abs_exponent > 999: + if exp_negative: + number = 0.0*sign + else: + number = Inf*sign + return i - start + + # if integer is representable in 53 bits: fast path + # max fast path integer is 1<<53 - 1 or 8999999999999999 (16 digits) + let digits = kdigits + fdigits + if digits <= 15 or (digits <= 16 and first_digit <= 8): + # max float power of ten with set bits above the 53th bit is 10^22 + if abs_exponent <= 22: + if exp_negative: + number = sign * integer.float / powtens[abs_exponent] + else: + number = sign * integer.float * powtens[abs_exponent] + return i - start + + # if exponent is greater try to fit extra exponent above 22 by multiplying + # integer part is there is space left. + let slop = 15 - kdigits - fdigits + if abs_exponent <= 22 + slop and not exp_negative: + number = sign * integer.float * powtens[slop] * powtens[abs_exponent-slop] + return i - start + + # if failed: slow path with strtod. + var t: array[500, char] # flaviu says: 325 is the longest reasonable literal + var ti = 0 + let maxlen = t.high - "e+000".len # reserve enough space for exponent + + result = i - start + i = start + # re-parse without error checking, any error should be handled by the code above. + if s[i] == '.': i.inc + while s[i] in {'0'..'9','+','-'}: + if ti < maxlen: + t[ti] = s[i]; inc(ti) + inc(i) + while s[i] in {'.', '_'}: # skip underscore and decimal point + inc(i) + + # insert exponent + t[ti] = 'E'; inc(ti) + t[ti] = (if exp_negative: '-' else: '+'); inc(ti) + inc(ti, 3) + + # insert adjusted exponent + t[ti-1] = ('0'.ord + abs_exponent mod 10).char; abs_exponent = abs_exponent div 10 + t[ti-2] = ('0'.ord + abs_exponent mod 10).char; abs_exponent = abs_exponent div 10 + t[ti-3] = ('0'.ord + abs_exponent mod 10).char + + when defined(nimNoArrayToCstringConversion): + number = c_strtod(addr t, nil) + else: + number = c_strtod(t, nil) + +proc nimInt64ToStr(x: int64): string {.compilerRtl.} = + result = newStringOfCap(sizeof(x)*4) + result.add x + +proc nimBoolToStr(x: bool): string {.compilerRtl.} = + return if x: "true" else: "false" + +proc nimCharToStr(x: char): string {.compilerRtl.} = + result = newString(1) + result[0] = x diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index 90ae91cf5..24ba02af6 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -20,37 +20,6 @@ proc resize(old: int): int {.inline.} = elif old < 65536: result = old * 2 else: result = old * 3 div 2 # for large arrays * 3/2 is better -proc cmpStrings(a, b: NimString): int {.inline, compilerProc.} = - if a == b: return 0 - when defined(nimNoNil): - let alen = if a == nil: 0 else: a.len - let blen = if b == nil: 0 else: b.len - else: - if a == nil: return -1 - if b == nil: return 1 - let alen = a.len - let blen = b.len - let minlen = min(alen, blen) - if minlen > 0: - result = c_memcmp(addr a.data, addr b.data, minlen.csize) - if result == 0: - result = alen - blen - else: - result = alen - blen - -proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} = - if a == b: return true - when defined(nimNoNil): - let alen = if a == nil: 0 else: a.len - let blen = if b == nil: 0 else: b.len - else: - if a == nil or b == nil: return false - let alen = a.len - let blen = b.len - if alen == blen: - if alen == 0: return true - return equalMem(addr(a.data), addr(b.data), alen) - when declared(allocAtomic): template allocStr(size: untyped): untyped = cast[NimString](allocAtomic(size)) @@ -162,19 +131,6 @@ proc copyDeepString(src: NimString): NimString {.inline.} = result.len = src.len copyMem(addr(result.data), addr(src.data), src.len + 1) -proc hashString(s: string): int {.compilerproc.} = - # the compiler needs exactly the same hash function! - # this used to be used for efficient generation of string case statements - var h = 0 - for i in 0..len(s)-1: - h = h +% ord(s[i]) - h = h +% h shl 10 - h = h xor (h shr 6) - h = h +% h shl 3 - h = h xor (h shr 11) - h = h +% h shl 15 - result = h - proc addChar(s: NimString, c: char): NimString = # is compilerproc! if s == nil: @@ -246,7 +202,7 @@ proc appendChar(dest: NimString, c: char) {.compilerproc, inline.} = inc(dest.len) proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} = - var n = max(newLen, 0) + let n = max(newLen, 0) if s == nil: result = mnewString(newLen) elif n <= s.space: @@ -340,243 +296,3 @@ proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {. result = cast[PGenericSeq](newSeq(typ, newLen)) else: result = setLengthSeq(s, typ.base.size, newLen) - -# --------------- other string routines ---------------------------------- -proc add*(result: var string; x: int64) = - let base = result.len - setLen(result, base + sizeof(x)*4) - var i = 0 - var y = x - while true: - var d = y div 10 - result[base+i] = chr(abs(int(y - d*10)) + ord('0')) - inc(i) - y = d - if y == 0: break - if x < 0: - result[base+i] = '-' - inc(i) - setLen(result, base+i) - # mirror the string: - for j in 0..i div 2 - 1: - swap(result[base+j], result[base+i-j-1]) - -proc nimIntToStr(x: int): string {.compilerRtl.} = - result = newStringOfCap(sizeof(x)*4) - result.add x - -proc add*(result: var string; x: float) = - when nimvm: - result.add $x - else: - var buf: array[0..64, char] - when defined(nimNoArrayToCstringConversion): - var n: int = c_sprintf(addr buf, "%.16g", x) - else: - var n: int = c_sprintf(buf, "%.16g", x) - var hasDot = false - for i in 0..n-1: - if buf[i] == ',': - buf[i] = '.' - hasDot = true - elif buf[i] in {'a'..'z', 'A'..'Z', '.'}: - hasDot = true - if not hasDot: - buf[n] = '.' - buf[n+1] = '0' - buf[n+2] = '\0' - # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' - # of '-1.#IND' are produced. - # We want to get rid of these here: - if buf[n-1] in {'n', 'N', 'D', 'd'}: - result.add "nan" - elif buf[n-1] == 'F': - if buf[0] == '-': - result.add "-inf" - else: - result.add "inf" - else: - var i = 0 - while buf[i] != '\0': - result.add buf[i] - inc i - -proc nimFloatToStr(f: float): string {.compilerproc.} = - result = newStringOfCap(8) - result.add f - -proc c_strtod(buf: cstring, endptr: ptr cstring): float64 {. - importc: "strtod", header: "<stdlib.h>", noSideEffect.} - -const - IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'} - powtens = [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, - 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, - 1e20, 1e21, 1e22] - -proc nimParseBiggestFloat(s: string, number: var BiggestFloat, - start = 0): int {.compilerProc.} = - # This routine attempt to parse float that can parsed quickly. - # ie whose integer part can fit inside a 53bits integer. - # their real exponent must also be <= 22. If the float doesn't follow - # these restrictions, transform the float into this form: - # INTEGER * 10 ^ exponent and leave the work to standard `strtod()`. - # This avoid the problems of decimal character portability. - # see: http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ - var - i = start - sign = 1.0 - kdigits, fdigits = 0 - exponent: int - integer: uint64 - frac_exponent = 0 - exp_sign = 1 - first_digit = -1 - has_sign = false - - # Sign? - if s[i] == '+' or s[i] == '-': - has_sign = true - if s[i] == '-': - sign = -1.0 - 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 - - if s[i] in {'0'..'9'}: - first_digit = (s[i].ord - '0'.ord) - # Integer part? - while s[i] in {'0'..'9'}: - inc(kdigits) - integer = integer * 10'u64 + (s[i].ord - '0'.ord).uint64 - inc(i) - while s[i] == '_': inc(i) - - # Fractional part? - if s[i] == '.': - inc(i) - # if no integer part, Skip leading zeros - if kdigits <= 0: - while s[i] == '0': - inc(frac_exponent) - inc(i) - while s[i] == '_': inc(i) - - if first_digit == -1 and s[i] in {'0'..'9'}: - first_digit = (s[i].ord - '0'.ord) - # get fractional part - while s[i] in {'0'..'9'}: - inc(fdigits) - inc(frac_exponent) - integer = integer * 10'u64 + (s[i].ord - '0'.ord).uint64 - inc(i) - while s[i] == '_': inc(i) - - # if has no digits: return error - if kdigits + fdigits <= 0 and - (i == start or # no char consumed (empty string). - (i == start + 1 and has_sign)): # or only '+' or '- - return 0 - - if s[i] in {'e', 'E'}: - inc(i) - if s[i] == '+' or s[i] == '-': - if s[i] == '-': - exp_sign = -1 - - 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) # underscores are allowed and ignored - - var real_exponent = exp_sign*exponent - frac_exponent - let exp_negative = real_exponent < 0 - var abs_exponent = abs(real_exponent) - - # if exponent greater than can be represented: +/- zero or infinity - if abs_exponent > 999: - if exp_negative: - number = 0.0*sign - else: - number = Inf*sign - return i - start - - # if integer is representable in 53 bits: fast path - # max fast path integer is 1<<53 - 1 or 8999999999999999 (16 digits) - let digits = kdigits + fdigits - if digits <= 15 or (digits <= 16 and first_digit <= 8): - # max float power of ten with set bits above the 53th bit is 10^22 - if abs_exponent <= 22: - if exp_negative: - number = sign * integer.float / powtens[abs_exponent] - else: - number = sign * integer.float * powtens[abs_exponent] - return i - start - - # if exponent is greater try to fit extra exponent above 22 by multiplying - # integer part is there is space left. - let slop = 15 - kdigits - fdigits - if abs_exponent <= 22 + slop and not exp_negative: - number = sign * integer.float * powtens[slop] * powtens[abs_exponent-slop] - return i - start - - # if failed: slow path with strtod. - var t: array[500, char] # flaviu says: 325 is the longest reasonable literal - var ti = 0 - let maxlen = t.high - "e+000".len # reserve enough space for exponent - - result = i - start - i = start - # re-parse without error checking, any error should be handled by the code above. - if s[i] == '.': i.inc - while s[i] in {'0'..'9','+','-'}: - if ti < maxlen: - t[ti] = s[i]; inc(ti) - inc(i) - while s[i] in {'.', '_'}: # skip underscore and decimal point - inc(i) - - # insert exponent - t[ti] = 'E'; inc(ti) - t[ti] = (if exp_negative: '-' else: '+'); inc(ti) - inc(ti, 3) - - # insert adjusted exponent - t[ti-1] = ('0'.ord + abs_exponent mod 10).char; abs_exponent = abs_exponent div 10 - t[ti-2] = ('0'.ord + abs_exponent mod 10).char; abs_exponent = abs_exponent div 10 - t[ti-3] = ('0'.ord + abs_exponent mod 10).char - - when defined(nimNoArrayToCstringConversion): - number = c_strtod(addr t, nil) - else: - number = c_strtod(t, nil) - -proc nimInt64ToStr(x: int64): string {.compilerRtl.} = - result = newStringOfCap(sizeof(x)*4) - result.add x - -proc nimBoolToStr(x: bool): string {.compilerRtl.} = - return if x: "true" else: "false" - -proc nimCharToStr(x: char): string {.compilerRtl.} = - result = newString(1) - result[0] = x |