improvements to addInt and $ for integer types (#18592)

* improvements to $(SomeInteger) and addInt
* remove mIntToStr, mInt64ToStr
* improvements
* fix tests/pragmas/tinjectstmt.nim; the diff is harmless, cgen code is identical with -d:danger or debug mode
* rm tests/system/tstrmantle.nim
* revert compiler/jsgen.nim for -d:nimVersion140
This commit is contained in:
Timothee Cour 2021-08-19 02:33:52 -07:00 • committed by GitHub
commit 394f4ac7bb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 162 additions and 186 deletions

View file

@ -652,8 +652,8 @@ type
mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot, mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot,
mUnaryPlusI, mBitnotI, mUnaryPlusI, mBitnotI,
mUnaryPlusF64, mUnaryMinusF64, mUnaryPlusF64, mUnaryMinusF64,
mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mCharToStr, mBoolToStr,
mFloatToStr, # for -d:nimVersion140 mIntToStr, mInt64ToStr, mFloatToStr, # for -d:nimVersion140
mCStrToStr, mCStrToStr,
mStrToStr, mEnumToStr, mStrToStr, mEnumToStr,
mAnd, mOr, mAnd, mOr,
@ -722,8 +722,8 @@ const
mEqRef, mEqProc, mLePtr, mLtPtr, mEqCString, mXor, mEqRef, mEqProc, mLePtr, mLtPtr, mEqCString, mXor,
mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI, mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI,
mUnaryPlusF64, mUnaryMinusF64, mUnaryPlusF64, mUnaryMinusF64,
mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mCharToStr, mBoolToStr,
mFloatToStr, mIntToStr, mInt64ToStr, mFloatToStr,
mCStrToStr, mCStrToStr,
mStrToStr, mEnumToStr, mStrToStr, mEnumToStr,
mAnd, mOr, mAnd, mOr,

View file

@ -29,18 +29,34 @@ const
# doAssert res == digits100 # doAssert res == digits100
proc utoa2Digits*(buf: var openArray[char]; pos: int; digits: uint32) {.inline.} = proc utoa2Digits*(buf: var openArray[char]; pos: int; digits: uint32) {.inline.} =
assert(digits <= 99)
buf[pos] = digits100[2 * digits] buf[pos] = digits100[2 * digits]
buf[pos+1] = digits100[2 * digits + 1] buf[pos+1] = digits100[2 * digits + 1]
#copyMem(buf, unsafeAddr(digits100[2 * digits]), 2 * sizeof((char))) #copyMem(buf, unsafeAddr(digits100[2 * digits]), 2 * sizeof((char)))
proc trailingZeros2Digits*(digits: uint32): int32 {.inline.} = proc trailingZeros2Digits*(digits: uint32): int32 {.inline.} =
assert(digits <= 99)
return trailingZeros100[digits] return trailingZeros100[digits]
func addIntImpl*(result: var string, origin: uint64) = when defined(js):
proc numToString(a: SomeInteger): cstring {.importjs: "((#) + \"\")".}
func addChars[T](result: var string, x: T, start: int, n: int) {.inline.} =
let old = result.len
result.setLen old + n
template impl =
for i in 0..<n: result[old + i] = x[start + i]
when nimvm: impl
else:
when defined(js) or defined(nimscript): impl
else:
{.noSideEffect.}:
copyMem result[old].addr, x[start].unsafeAddr, n
func addChars[T](result: var string, x: T) {.inline.} =
addChars(result, x, 0, x.len)
func addIntImpl(result: var string, x: uint64) {.inline.} =
var tmp {.noinit.}: array[24, char] var tmp {.noinit.}: array[24, char]
var num = origin var num = x
var next = tmp.len - 1 var next = tmp.len - 1
const nbatch = 100 const nbatch = 100
@ -60,17 +76,39 @@ func addIntImpl*(result: var string, origin: uint64) =
tmp[next] = digits100[index + 1] tmp[next] = digits100[index + 1]
tmp[next - 1] = digits100[index] tmp[next - 1] = digits100[index]
dec next dec next
let n = result.len addChars(result, tmp, next, tmp.len - next)
let length = tmp.len - next
result.setLen n + length
when nimvm:
for i in 0..<length:
result[n+i] = tmp[next+i]
else:
when defined(js) or defined(nimscript):
for i in 0..<length:
result[n+i] = tmp[next+i]
else:
{.noSideEffect.}:
copyMem result[n].addr, tmp[next].addr, length
func addInt*(result: var string, x: uint64) =
when nimvm: addIntImpl(result, x)
else:
when not defined(js): addIntImpl(result, x)
else:
addChars(result, numToString(x))
proc addInt*(result: var string; x: int64) =
## Converts integer to its string representation and appends it to `result`.
runnableExamples:
var s = "foo"
s.addInt(45)
assert s == "foo45"
template impl =
var num: uint64
if x < 0:
if x == low(int64):
num = uint64(x)
else:
num = uint64(-x)
let base = result.len
setLen(result, base + 1)
result[base] = '-'
else:
num = uint64(x)
addInt(result, num)
when nimvm: impl()
else:
when defined(js):
addChars(result, numToString(x))
else: impl()
proc addInt*(result: var string; x: int) {.inline.} =
addInt(result, int64(x))

View file

@ -1,3 +1,5 @@
from std/private/digitsutils import addInt
template toLocation*(result: var string, file: string | cstring, line: int, col: int) = template toLocation*(result: var string, file: string | cstring, line: int, col: int) =
## avoids spurious allocations ## avoids spurious allocations
# Hopefully this can be re-used everywhere so that if a user needs to customize, # Hopefully this can be re-used everywhere so that if a user needs to customize,
@ -5,11 +7,8 @@ template toLocation*(result: var string, file: string | cstring, line: int, col:
result.add file result.add file
if line > 0: if line > 0:
result.add "(" result.add "("
# simplify this after moving moving `include strmantle` above import assertions` addInt(result, line)
when declared(addInt): result.addInt line
else: result.add $line
if col > 0: if col > 0:
result.add ", " result.add ", "
when declared(addInt): result.addInt col addInt(result, col)
else: result.add $col
result.add ")" result.add ")"

View file

@ -2463,14 +2463,13 @@ when notJSnotNims:
else: else:
{.error: "Only closure iterator is allowed!".} {.error: "Only closure iterator is allowed!".}
from std/private/digitsutils import addInt
export addInt
when defined(js): when defined(js):
include "system/jssys" include "system/jssys"
include "system/reprjs" include "system/reprjs"
when defined(js) or defined(nimscript):
proc addInt*(result: var string; x: int64) =
result.add $x
proc quit*(errormsg: string, errorcode = QuitFailure) {.noreturn.} = proc quit*(errormsg: string, errorcode = QuitFailure) {.noreturn.} =
## A shorthand for `echo(errormsg); quit(errorcode)`. ## A shorthand for `echo(errormsg); quit(errorcode)`.
when defined(nimscript) or defined(js) or (hostOS == "standalone"): when defined(nimscript) or defined(js) or (hostOS == "standalone"):
@ -2918,7 +2917,7 @@ proc addQuoted*[T](s: var string, x: T) =
s.addEscapedChar(x) s.addEscapedChar(x)
s.add("'") s.add("'")
# prevent temporary string allocation # prevent temporary string allocation
elif T is SomeSignedInt: elif T is SomeInteger:
s.addInt(x) s.addInt(x)
elif T is SomeFloat: elif T is SomeFloat:
s.addFloat(x) s.addFloat(x)

View file

@ -12,7 +12,6 @@ import std/private/miscdollars
type InstantiationInfo = tuple[filename: string, line: int, column: int] type InstantiationInfo = tuple[filename: string, line: int, column: int]
proc `$`(x: int): string {.magic: "IntToStr", noSideEffect.}
proc `$`(info: InstantiationInfo): string = proc `$`(info: InstantiationInfo): string =
# The +1 is needed here # The +1 is needed here
# instead of overriding `$` (and changing its meaning), consider explicit name. # instead of overriding `$` (and changing its meaning), consider explicit name.
@ -108,7 +107,9 @@ template doAssertRaises*(exception: typedesc, code: untyped) =
wrong = true wrong = true
except exception: except exception:
discard discard
except Exception as e: raiseAssert(begin & " raised '" & $e.name & "'" & msgEnd) except Exception as e:
mixin `$` # alternatively, we could define $cstring in this module
raiseAssert(begin & " raised '" & $e.name & "'" & msgEnd)
except: raisedForeign() except: raisedForeign()
if wrong: if wrong:
raiseAssert(begin & " nothing was raised" & msgEnd) raiseAssert(begin & " nothing was raised" & msgEnd)

View file

@ -1,45 +1,31 @@
## `$` is Nim's general way of spelling `toString`:idx:.
runnableExamples:
assert $0.1 == "0.1"
assert $(-2*3) == "-6"
import std/private/digitsutils import std/private/digitsutils
import system/formatfloat import system/formatfloat
export addFloat export addFloat
proc `$`*(x: int): string {.magic: "IntToStr", noSideEffect.} proc `$`*(x: int): string {.raises: [].} =
## The stringify operator for an integer argument. Returns `x` ## Outplace version of `addInt`.
## converted to a decimal string. `$` is Nim's general way of result.addInt(x)
## spelling `toString`:idx:.
template dollarImpl(x: uint | uint64, result: var string) = proc `$`*(x: int64): string {.raises: [].} =
addIntImpl(result, x) ## Outplace version of `addInt`.
result.addInt(x)
when defined(js): proc `$`*(x: uint64): string {.raises: [].} =
import std/private/since ## Outplace version of `addInt`.
since (1, 3): addInt(result, x)
proc `$`*(x: uint): string =
## Caveat: currently implemented as $(cast[int](x)), tied to current
## semantics of js' Number type.
# for c, see strmantle.`$`
when nimvm:
dollarImpl(x, result)
else:
result = $(int(x))
proc `$`*(x: uint64): string = # same as old `ctfeWhitelist` behavior, whether or not this is a good idea.
## Compatibility note: template gen(T) =
## the results may change in future releases if/when js target implements # xxx simplify this by supporting this in compiler: int{lit} | uint64{lit} | int64{lit}
## 64bit ints. func `$`*(x: T{lit}): string {.compileTime.} = result.addInt(x)
# pending https://github.com/nim-lang/RFCs/issues/187 gen(int)
when nimvm: gen(uint64)
dollarImpl(x, result) gen(int64)
else:
result = $(cast[int](x))
else:
proc `$`*(x: uint64): string {.noSideEffect, raises: [].} =
## The stringify operator for an unsigned integer argument. Returns `x`
## converted to a decimal string.
dollarImpl(x, result)
proc `$`*(x: int64): string {.magic: "Int64ToStr", noSideEffect.}
## The stringify operator for an integer argument. Returns `x`
## converted to a decimal string.
func `$`*(x: float | float32): string = func `$`*(x: float | float32): string =
## Outplace version of `addFloat`. ## Outplace version of `addFloat`.

View file

@ -8,18 +8,17 @@ proc distinctBase(T: typedesc, recursive: static bool = true): typedesc {.magic:
proc repr*(x: NimNode): string {.magic: "Repr", noSideEffect.} proc repr*(x: NimNode): string {.magic: "Repr", noSideEffect.}
proc repr*(x: int): string {.magic: "IntToStr", noSideEffect.} proc repr*(x: int): string =
## repr for an integer argument. Returns `x` ## Same as $x
## converted to a decimal string. $x
proc repr*(x: int64): string {.magic: "Int64ToStr", noSideEffect.} proc repr*(x: int64): string =
## repr for an integer argument. Returns `x` ## Same as $x
## converted to a decimal string. $x
proc repr*(x: uint64): string {.noSideEffect.} = proc repr*(x: uint64): string {.noSideEffect.} =
## repr for an unsigned integer argument. Returns `x` ## Same as $x
## converted to a decimal string. $x
$x #Calls `$` from system/strmantle.nim
proc repr*(x: float): string = proc repr*(x: float): string =
## Same as $x ## Same as $x

View file

@ -43,32 +43,6 @@ proc hashString(s: string): int {.compilerproc.} =
h = h + h shl 15 h = h + h shl 15
result = cast[int](h) result = cast[int](h)
proc addInt*(result: var string; x: int64) =
## Converts integer to its string representation and appends it to `result`.
##
## .. code-block:: Nim
## var
## a = "123"
## b = 45
## a.addInt(b) # a <- "12345"
var num: uint64
if x < 0:
if x == low(int64):
num = uint64(x)
else:
num = uint64(-x)
let base = result.len
setLen(result, base + 1)
result[base] = '-'
else:
num = uint64(x)
addIntImpl(result, num)
proc nimIntToStr(x: int): string {.compilerRtl.} =
result = newStringOfCap(sizeof(x)*4)
result.addInt x
proc c_strtod(buf: cstring, endptr: ptr cstring): float64 {. proc c_strtod(buf: cstring, endptr: ptr cstring): float64 {.
importc: "strtod", header: "<stdlib.h>", noSideEffect.} importc: "strtod", header: "<stdlib.h>", noSideEffect.}
@ -240,10 +214,6 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
when defined(nimHasInvariant): when defined(nimHasInvariant):
{.pop.} # staticBoundChecks {.pop.} # staticBoundChecks
proc nimInt64ToStr(x: int64): string {.compilerRtl.} =
result = newStringOfCap(sizeof(x)*4)
result.addInt x
proc nimBoolToStr(x: bool): string {.compilerRtl.} = proc nimBoolToStr(x: bool): string {.compilerRtl.} =
return if x: "true" else: "false" return if x: "true" else: "false"

View file

@ -10,5 +10,5 @@ highlight;;skProc;;1;;7;;4
highlight;;skTemplate;;2;;7;;4 highlight;;skTemplate;;2;;7;;4
highlight;;skTemplate;;2;;7;;4 highlight;;skTemplate;;2;;7;;4
highlight;;skTemplate;;2;;7;;4 highlight;;skTemplate;;2;;7;;4
highlight;;skProc;;3;;8;;1 highlight;;skFunc;;3;;8;;1
""" """

View file

@ -7,15 +7,18 @@ ok0
ok1 ok1
onInject: 3 onInject: 3
onInject: 4 onInject: 4
0
onInject: 5 onInject: 5
0
onInject: 6 onInject: 6
1
onInject: 7 onInject: 7
onInject: 8 onInject: 8
1
onInject: 9
onInject: 10
onInject: 11
2 2
ok2 ok2
onInject: 9 onInject: 12
''' '''
""" """
@ -25,7 +28,7 @@ onInject: 9
{.injectStmt.} pragma can be used to inject a statement before every {.injectStmt.} pragma can be used to inject a statement before every
other statement in the current module. It's now undocumented and may be removed other statement in the current module. It's now undocumented and may be removed
in the future and replaced with something more general and without its limitations. in the future and replaced with something more general and without its limitations.
e.g. (e.g. doesn't work in VM or js backends). (e.g. doesn't work in VM or js backends).
]# ]#
from system/ansi_c import c_printf from system/ansi_c import c_printf
@ -44,5 +47,5 @@ proc main()=
echo a echo a
echo "ok2" echo "ok2"
static: main() # xxx injectStmt not honred in VM static: main() # xxx injectStmt not honored in VM
main() main()

View file

@ -102,39 +102,40 @@ block: # #14350, #16674, #16686 for JS
doAssert nil2 == cstring("") doAssert nil2 == cstring("")
block: block:
when defined(js): # bug #18591
let a1 = -1'i8
let a2 = uint8(a1)
# if `uint8(a1)` changes meaning to `cast[uint8](a1)` in future, update this test;
# until then, this is the correct semantics.
let a3 = $a2
doAssert a2 < 3
doAssert a3 == "-1"
proc intToStr(a: uint8): cstring {.importjs: "(# + \"\")".}
doAssert $intToStr(a2) == "-1"
else:
block: block:
let x = -1'i8 let x = -1'i8
let y = uint32(x) let y = uint32(x)
doAssert $y == "4294967295" doAssert $y == "4294967295"
block: block:
let x = -1'i16 let x = -1'i16
let y = uint32(x) let y = uint32(x)
doAssert $y == "4294967295" doAssert $y == "4294967295"
block: block:
let x = -1'i32 let x = -1'i32
let y = uint32(x) let y = uint32(x)
doAssert $y == "4294967295" doAssert $y == "4294967295"
block:
let x = 4294967295'u32
doAssert $x == "4294967295"
block:
doAssert $(4294967295'u32) == "4294967295"
block: block:
proc foo1(arg: int): string = proc foo1(arg: int): string =
let x = uint32(arg) let x = uint32(arg)
$x $x
doAssert $foo1(-1) == "4294967295" doAssert $foo1(-1) == "4294967295"
block:
let x = 4294967295'u32
doAssert $x == "4294967295"
block:
doAssert $(4294967295'u32) == "4294967295"
proc main()= proc main()=
block: block:
@ -159,6 +160,32 @@ proc main()=
doAssert $uint32.high == "4294967295" doAssert $uint32.high == "4294967295"
block: # addInt
var res = newStringOfCap(24)
template test2(a, b) =
res.setLen(0)
res.addInt a
doAssert res == b
for i in 0 .. 9:
res.addInt int64(i)
doAssert res == "0123456789"
res.setLen(0)
for i in -9 .. 0:
res.addInt int64(i)
doAssert res == "-9-8-7-6-5-4-3-2-10"
when not defined(js):
test2 high(int64), "9223372036854775807"
test2 low(int64), "-9223372036854775808"
test2 high(int32), "2147483647"
test2 low(int32), "-2147483648"
test2 high(int16), "32767"
test2 low(int16), "-32768"
test2 high(int8), "127"
test2 low(int8), "-128"
static: main() static: main()
main() main()

View file

@ -1,46 +0,0 @@
var res = newStringOfCap(24)
for i in 0 .. 9:
res.addInt int64(i)
doAssert res == "0123456789"
res.setLen(0)
for i in -9 .. 0:
res.addInt int64(i)
doAssert res == "-9-8-7-6-5-4-3-2-10"
res.setLen(0)
res.addInt high(int64)
doAssert res == "9223372036854775807"
res.setLen(0)
res.addInt low(int64)
doAssert res == "-9223372036854775808"
res.setLen(0)
res.addInt high(int32)
doAssert res == "2147483647"
res.setLen(0)
res.addInt low(int32)
doAssert res == "-2147483648"
res.setLen(0)
res.addInt high(int16)
doAssert res == "32767"
res.setLen(0)
res.addInt low(int16)
doAssert res == "-32768"
res.setLen(0)
res.addInt high(int8)
doAssert res == "127"
res.setLen(0)
res.addInt low(int8)
doAssert res == "-128"