From b6bfe38ff5283f77d5e5e78da06d2710bc16afb6 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 24 Aug 2022 19:38:30 +0800 Subject: [PATCH] move formatfloat out of system (#20195) * move formatfloat out of system * fixes doc * Update changelog.md * careless * fixes * deprecate system/formatfloat * better handling --- changelog.md | 3 +- compiler/ic/ic.nim | 2 +- compiler/lexer.nim | 2 +- compiler/renderer.nim | 2 +- compiler/rodutils.nim | 2 +- compiler/ropes.nim | 2 +- compiler/sem.nim | 3 + compiler/semfold.nim | 2 +- compiler/types.nim | 2 +- compiler/vm.nim | 3 + compiler/vmmarshal.nim | 2 +- compiler/vmops.nim | 4 +- lib/core/macros.nim | 2 +- lib/packages/docutils/rstgen.nim | 2 +- lib/pure/json.nim | 2 +- lib/std/formatfloat.nim | 142 ++++++++++++++++++++++ lib/{system => std/private}/dragonbox.nim | 27 ++-- lib/{system => std/private}/schubfach.nim | 12 +- lib/std/syncio.nim | 2 +- lib/system/chcks.nim | 2 + lib/system/dollars.nim | 13 +- lib/system/formatfloat.nim | 141 +-------------------- lib/system/repr_v2.nim | 3 + 23 files changed, 201 insertions(+), 176 deletions(-) create mode 100644 lib/std/formatfloat.nim rename lib/{system => std/private}/dragonbox.nim (98%) rename lib/{system => std/private}/schubfach.nim (96%) diff --git a/changelog.md b/changelog.md index 80692e99645d..36f27ecf30d1 100644 --- a/changelog.md +++ b/changelog.md @@ -6,8 +6,7 @@ - `addr` is now available for all addressable locations, `unsafeAddr` is now deprecated and an alias for `addr`. -- `io` and `assertions` are about to move out of the `system` module. - You may instead import `std/syncio` and `std/assertions`. +- `io`, `assertions`, `formatfloat` are about to move out of the `system` module. You may instead import `std/syncio`, `std/assertions` and `std/formatfloat`. The `-d:nimPreviewSlimSystem` option makes these imports required. - The `gc:v2` option is removed. diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index 38b6987f99f3..2ac441b3ef86 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -15,7 +15,7 @@ import ".." / [ast, idents, lineinfos, msgs, ropes, options, from os import removeFile, isAbsolute when defined(nimPreviewSlimSystem): - import std/[syncio, assertions] + import std/[syncio, assertions, formatfloat] type PackedConfig* = object diff --git a/compiler/lexer.nim b/compiler/lexer.nim index e795d52c0802..f2c94e06a276 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -20,7 +20,7 @@ import wordrecg, lineinfos, pathutils, parseutils when defined(nimPreviewSlimSystem): - import std/assertions + import std/[assertions, formatfloat] const MaxLineLength* = 80 # lines longer than this lead to a warning diff --git a/compiler/renderer.nim b/compiler/renderer.nim index f847aa0944eb..d8c46fc9211c 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -18,7 +18,7 @@ import lexer, options, idents, strutils, ast, msgs, lineinfos when defined(nimPreviewSlimSystem): - import std/[syncio, assertions] + import std/[syncio, assertions, formatfloat] type TRenderFlag* = enum diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim index 95b7b2d9e3bd..e2e9e1eb2762 100644 --- a/compiler/rodutils.nim +++ b/compiler/rodutils.nim @@ -42,7 +42,7 @@ when not declared(signbit): proc signbit*(x: SomeFloat): bool {.inline.} = result = c_signbit(x) != 0 -import system/formatfloat +import std/formatfloat proc toStrMaxPrecision*(f: BiggestFloat | float32): string = const literalPostfix = when f is float32: "f" else: "" diff --git a/compiler/ropes.nim b/compiler/ropes.nim index 677a3ce096ba..3a2fbe44e273 100644 --- a/compiler/ropes.nim +++ b/compiler/ropes.nim @@ -61,7 +61,7 @@ import from pathutils import AbsoluteFile when defined(nimPreviewSlimSystem): - import std/[assertions, syncio] + import std/[assertions, syncio, formatfloat] type diff --git a/compiler/sem.nim b/compiler/sem.nim index 8fac158bae96..b6ee76cc9bf4 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -25,6 +25,9 @@ when defined(nimfix): when not defined(leanCompiler): import spawn +when defined(nimPreviewSlimSystem): + import std/formatfloat + # implementation proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 72c8abaa98b1..f627bffc736e 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -18,7 +18,7 @@ import from system/memory import nimCStrLen when defined(nimPreviewSlimSystem): - import std/assertions + import std/[assertions, formatfloat] proc errorType*(g: ModuleGraph): PType = ## creates a type representing an error state diff --git a/compiler/types.nim b/compiler/types.nim index 6855776412e3..cd7d11b94d09 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -14,7 +14,7 @@ import lineinfos, int128, modulegraphs, astmsgs when defined(nimPreviewSlimSystem): - import std/assertions + import std/[assertions, formatfloat] type TPreferedDesc* = enum diff --git a/compiler/vm.nim b/compiler/vm.nim index 28df27ef389d..1717fe1194fe 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -18,6 +18,9 @@ import gorgeimpl, lineinfos, btrees, macrocacheimpl, modulegraphs, sighashes, int128, vmprofiler +when defined(nimPreviewSlimSystem): + import std/formatfloat + import ast except getstr from semfold import leValueConv, ordinalValToString from evaltempl import evalTemplate diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim index 83c441283ee1..e44bc0bea3b9 100644 --- a/compiler/vmmarshal.nim +++ b/compiler/vmmarshal.nim @@ -13,7 +13,7 @@ import streams, json, intsets, tables, ast, astalgo, idents, types, msgs, options, lineinfos when defined(nimPreviewSlimSystem): - import std/assertions + import std/[assertions, formatfloat] proc ptrToInt(x: PNode): int {.inline.} = result = cast[int](x) # don't skip alignment diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 01aa5a4b95d2..fef76940e433 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -28,12 +28,12 @@ from std/os import getEnv, existsEnv, delEnv, putEnv, envPairs, from std/times import cpuTime from std/hashes import hash from std/osproc import nil -from system/formatfloat import addFloatRoundtrip, addFloatSprintf when defined(nimPreviewSlimSystem): import std/syncio - +else: + from std/formatfloat import addFloatRoundtrip, addFloatSprintf # There are some useful procs in vmconv. import vmconv, vmmarshal diff --git a/lib/core/macros.nim b/lib/core/macros.nim index ad87a0f64e2f..ecf85935f81b 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -11,7 +11,7 @@ include "system/inclrtl" import std/private/since when defined(nimPreviewSlimSystem): - import std/assertions + import std/[assertions, formatfloat] ## This module contains the interface to the compiler's abstract syntax diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 6232cb0be570..791935da87eb 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -44,7 +44,7 @@ import strutils, os, hashes, strtabs, rstast, rst, highlite, tables, sequtils, when defined(nimPreviewSlimSystem): - import std/[assertions, syncio] + import std/[assertions, syncio, formatfloat] import ../../std/private/since diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 928bc0fbfb8d..d0b1a4051b2d 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -165,7 +165,7 @@ import options # xxx remove this dependency using same approach as https://githu import std/private/since when defined(nimPreviewSlimSystem): - import std/[syncio, assertions] + import std/[syncio, assertions, formatfloat] export tables.`$` diff --git a/lib/std/formatfloat.nim b/lib/std/formatfloat.nim new file mode 100644 index 000000000000..6f2383760597 --- /dev/null +++ b/lib/std/formatfloat.nim @@ -0,0 +1,142 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2022 Nim contributors +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +when defined(nimPreviewSlimSystem): + import std/assertions +else: + {.deprecated: "formatfloat is about to move out of system; use `-d:nimPreviewSlimSystem` and import `std/formatfloat`".} + +proc c_memcpy(a, b: pointer, size: csize_t): pointer {.importc: "memcpy", header: "", discardable.} + +proc addCstringN(result: var string, buf: cstring; buflen: int) = + # no nimvm support needed, so it doesn't need to be fast here either + let oldLen = result.len + let newLen = oldLen + buflen + result.setLen newLen + c_memcpy(result[oldLen].addr, buf, buflen.csize_t) + +import std/private/[dragonbox, schubfach] + +proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: BiggestFloat): int = + ## This is the implementation to format floats. + ## + ## returns the amount of bytes written to `buf` not counting the + ## terminating '\0' character. + result = toChars(buf, value, forceTrailingDotZero=true) + buf[result] = '\0' + +proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: float32): int = + result = float32ToChars(buf, value, forceTrailingDotZero=true) + buf[result] = '\0' + +proc c_sprintf(buf, frmt: cstring): cint {.header: "", + importc: "sprintf", varargs, noSideEffect.} + +proc writeToBuffer(buf: var array[65, char]; value: cstring) = + var i = 0 + while value[i] != '\0': + buf[i] = value[i] + inc i + +proc writeFloatToBufferSprintf*(buf: var array[65, char]; value: BiggestFloat): int = + ## This is the implementation to format floats. + ## + ## returns the amount of bytes written to `buf` not counting the + ## terminating '\0' character. + var n: int = c_sprintf(addr buf, "%.16g", value) + 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' + result = n + 2 + else: + result = n + # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' or 'nan(ind)' + # of '-1.#IND' are produced. + # We want to get rid of these here: + if buf[n-1] in {'n', 'N', 'D', 'd', ')'}: + writeToBuffer(buf, "nan") + result = 3 + elif buf[n-1] == 'F': + if buf[0] == '-': + writeToBuffer(buf, "-inf") + result = 4 + else: + writeToBuffer(buf, "inf") + result = 3 + +proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat | float32): int {.inline.} = + when defined(nimPreviewFloatRoundtrip) or defined(nimPreviewSlimSystem): + writeFloatToBufferRoundtrip(buf, value) + else: + writeFloatToBufferSprintf(buf, value) + +proc addFloatRoundtrip*(result: var string; x: float | float32) = + when nimvm: + doAssert false + else: + var buffer {.noinit.}: array[65, char] + let n = writeFloatToBufferRoundtrip(buffer, x) + result.addCstringN(cstring(buffer[0].addr), n) + +proc addFloatSprintf*(result: var string; x: float) = + when nimvm: + doAssert false + else: + var buffer {.noinit.}: array[65, char] + let n = writeFloatToBufferSprintf(buffer, x) + result.addCstringN(cstring(buffer[0].addr), n) + +proc nimFloatToString(a: float): cstring = + ## ensures the result doesn't print like an integer, i.e. return 2.0, not 2 + # print `-0.0` properly + asm """ + function nimOnlyDigitsOrMinus(n) { + return n.toString().match(/^-?\d+$/); + } + if (Number.isSafeInteger(`a`)) + `result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0" + else { + `result` = `a`+"" + if(nimOnlyDigitsOrMinus(`result`)){ + `result` = `a`+".0" + } + } + """ + +proc addFloat*(result: var string; x: float | float32) {.inline.} = + ## Converts float to its string representation and appends it to `result`. + runnableExamples: + var + s = "foo:" + b = 45.67 + s.addFloat(45.67) + assert s == "foo:45.67" + template impl = + when defined(nimPreviewFloatRoundtrip) or defined(nimPreviewSlimSystem): + addFloatRoundtrip(result, x) + else: + addFloatSprintf(result, x) + when defined(js): + when nimvm: impl() + else: + result.add nimFloatToString(x) + else: impl() + +when defined(nimPreviewSlimSystem): + func `$`*(x: float | float32): string = + ## Outplace version of `addFloat`. + result.addFloat(x) diff --git a/lib/system/dragonbox.nim b/lib/std/private/dragonbox.nim similarity index 98% rename from lib/system/dragonbox.nim rename to lib/std/private/dragonbox.nim index 34ae9e21004a..23adff3857c6 100644 --- a/lib/system/dragonbox.nim +++ b/lib/std/private/dragonbox.nim @@ -30,7 +30,6 @@ when defined(nimPreviewSlimSystem): const dtoaMinBufferLength*: cint = 64 -## -------------------------------------------------------------------------------------------------- ## This file contains an implementation of Junekey Jeon's Dragonbox algorithm. ## ## It is a simplified version of the reference implementation found here: @@ -38,14 +37,13 @@ const ## ## The reference implementation also works with single-precision floating-point numbers and ## has options to configure the rounding mode. -## -------------------------------------------------------------------------------------------------- template dragonbox_Assert*(x: untyped): untyped = assert(x) -## ================================================================================================== -## -## ================================================================================================== +# ================================================================================================== +# +# ================================================================================================== type ValueType* = float @@ -106,10 +104,11 @@ proc isZero*(this: Double): bool {.noSideEffect.} = proc signBit*(this: Double): int {.noSideEffect.} = return ord((this.bits and signMask) != 0) + +# ================================================================================================== +# +# ================================================================================================== ## namespace -## ================================================================================================== -## -## ================================================================================================== ## Returns floor(x / 2^n). ## ## Technically, right-shift of negative integers is implementation defined... @@ -133,9 +132,9 @@ proc floorLog10ThreeQuartersPow2*(e: int32): int32 {.inline.} = dragonbox_Assert(e <= 1500) return floorDivPow2(e * 1262611 - 524031, 22) -## ================================================================================================== -## -## ================================================================================================== +# ================================================================================================== +# +# ================================================================================================== type uint64x2* {.bycopy.} = object @@ -1040,9 +1039,9 @@ proc toDecimal64*(ieeeSignificand: uint64; ieeeExponent: uint64): FloatingDecima dec(q) return FloatingDecimal64(significand: q, exponent: minusK + kappa) -## ================================================================================================== -## ToChars -## ================================================================================================== +# ================================================================================================== +# ToChars +# ================================================================================================== when false: template `+!`(x: cstring; offset: int): cstring = cast[cstring](cast[uint](x) + uint(offset)) diff --git a/lib/system/schubfach.nim b/lib/std/private/schubfach.nim similarity index 96% rename from lib/system/schubfach.nim rename to lib/std/private/schubfach.nim index 7d6861e35231..5b965aaa747d 100644 --- a/lib/system/schubfach.nim +++ b/lib/std/private/schubfach.nim @@ -3,12 +3,12 @@ ## Distributed under the Boost Software License, Version 1.0. ## (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt) -## -------------------------------------------------------------------------------------------------- +# -------------------------------------------------------------------------------------------------- ## This file contains an implementation of the Schubfach algorithm as described in ## ## [1] Raffaello Giulietti, "The Schubfach way to render doubles", ## https://drive.google.com/open?id=1luHhyQF9zKlM8yJ1nebU0OgVYhfC6CBN -## -------------------------------------------------------------------------------------------------- +# -------------------------------------------------------------------------------------------------- import std/private/digitsutils @@ -19,9 +19,9 @@ when defined(nimPreviewSlimSystem): template sf_Assert(x: untyped): untyped = assert(x) -## ================================================================================================== -## -## ================================================================================================== +# ================================================================================================== +# +# ================================================================================================== type ValueType = float32 @@ -68,7 +68,7 @@ proc isZero(this: Single): bool {.noSideEffect.} = proc signBit(this: Single): int {.noSideEffect.} = return int((this.bits and signMask) != 0) -## ================================================================================================== +# ================================================================================================== ## Returns floor(x / 2^n). ## ## Technically, right-shift of negative integers is implementation defined... diff --git a/lib/std/syncio.nim b/lib/std/syncio.nim index eab96254a897..22e981198010 100644 --- a/lib/std/syncio.nim +++ b/lib/std/syncio.nim @@ -11,7 +11,7 @@ include system/inclrtl import std/private/since -import system/formatfloat +import std/formatfloat # ----------------- IO Part ------------------------------------------------ type diff --git a/lib/system/chcks.nim b/lib/system/chcks.nim index 1e1ce9c873fc..bdf2903d2cc6 100644 --- a/lib/system/chcks.nim +++ b/lib/system/chcks.nim @@ -9,6 +9,8 @@ # Implementation of some runtime checks. include system/indexerrors +when defined(nimPreviewSlimSystem): + import std/formatfloat proc raiseRangeError(val: BiggestInt) {.compilerproc, noinline.} = when hostOS == "standalone": diff --git a/lib/system/dollars.nim b/lib/system/dollars.nim index b060aedb0de9..4ff3d0ae6cf5 100644 --- a/lib/system/dollars.nim +++ b/lib/system/dollars.nim @@ -4,8 +4,14 @@ runnableExamples: assert $(-2*3) == "-6" import std/private/[digitsutils, miscdollars] -import system/formatfloat -export addFloat + +when not defined(nimPreviewSlimSystem): + import std/formatfloat + export addFloat + + func `$`*(x: float | float32): string = + ## Outplace version of `addFloat`. + result.addFloat(x) proc `$`*(x: int): string {.raises: [].} = ## Outplace version of `addInt`. @@ -27,9 +33,6 @@ gen(int) gen(uint64) gen(int64) -func `$`*(x: float | float32): string = - ## Outplace version of `addFloat`. - result.addFloat(x) proc `$`*(x: bool): string {.magic: "BoolToStr", noSideEffect.} ## The stringify operator for a boolean argument. Returns `x` diff --git a/lib/system/formatfloat.nim b/lib/system/formatfloat.nim index aada3e1bf061..70dd857d5ad1 100644 --- a/lib/system/formatfloat.nim +++ b/lib/system/formatfloat.nim @@ -1,135 +1,6 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2019 Nim contributors -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -when defined(nimPreviewSlimSystem): - import std/assertions - -proc c_memcpy(a, b: pointer, size: csize_t): pointer {.importc: "memcpy", header: "", discardable.} - -proc addCstringN(result: var string, buf: cstring; buflen: int) = - # no nimvm support needed, so it doesn't need to be fast here either - let oldLen = result.len - let newLen = oldLen + buflen - result.setLen newLen - c_memcpy(result[oldLen].addr, buf, buflen.csize_t) - -import dragonbox, schubfach - -proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: BiggestFloat): int = - ## This is the implementation to format floats. - ## - ## returns the amount of bytes written to `buf` not counting the - ## terminating '\0' character. - result = toChars(buf, value, forceTrailingDotZero=true) - buf[result] = '\0' - -proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: float32): int = - result = float32ToChars(buf, value, forceTrailingDotZero=true) - buf[result] = '\0' - -proc c_sprintf(buf, frmt: cstring): cint {.header: "", - importc: "sprintf", varargs, noSideEffect.} - -proc writeToBuffer(buf: var array[65, char]; value: cstring) = - var i = 0 - while value[i] != '\0': - buf[i] = value[i] - inc i - -proc writeFloatToBufferSprintf*(buf: var array[65, char]; value: BiggestFloat): int = - ## This is the implementation to format floats. - ## - ## returns the amount of bytes written to `buf` not counting the - ## terminating '\0' character. - var n: int = c_sprintf(addr buf, "%.16g", value) - 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' - result = n + 2 - else: - result = n - # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' or 'nan(ind)' - # of '-1.#IND' are produced. - # We want to get rid of these here: - if buf[n-1] in {'n', 'N', 'D', 'd', ')'}: - writeToBuffer(buf, "nan") - result = 3 - elif buf[n-1] == 'F': - if buf[0] == '-': - writeToBuffer(buf, "-inf") - result = 4 - else: - writeToBuffer(buf, "inf") - result = 3 - -proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat | float32): int {.inline.} = - when defined(nimPreviewFloatRoundtrip): - writeFloatToBufferRoundtrip(buf, value) - else: - writeFloatToBufferSprintf(buf, value) - -proc addFloatRoundtrip*(result: var string; x: float | float32) = - when nimvm: - doAssert false - else: - var buffer {.noinit.}: array[65, char] - let n = writeFloatToBufferRoundtrip(buffer, x) - result.addCstringN(cstring(buffer[0].addr), n) - -proc addFloatSprintf*(result: var string; x: float) = - when nimvm: - doAssert false - else: - var buffer {.noinit.}: array[65, char] - let n = writeFloatToBufferSprintf(buffer, x) - result.addCstringN(cstring(buffer[0].addr), n) - -proc nimFloatToString(a: float): cstring = - ## ensures the result doesn't print like an integer, i.e. return 2.0, not 2 - # print `-0.0` properly - asm """ - function nimOnlyDigitsOrMinus(n) { - return n.toString().match(/^-?\d+$/); - } - if (Number.isSafeInteger(`a`)) - `result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0" - else { - `result` = `a`+"" - if(nimOnlyDigitsOrMinus(`result`)){ - `result` = `a`+".0" - } - } - """ - -proc addFloat*(result: var string; x: float | float32) {.inline.} = - ## Converts float to its string representation and appends it to `result`. - runnableExamples: - var - s = "foo:" - b = 45.67 - s.addFloat(45.67) - assert s == "foo:45.67" - template impl = - when defined(nimPreviewFloatRoundtrip): - addFloatRoundtrip(result, x) - else: - addFloatSprintf(result, x) - when defined(js): - when nimvm: impl() - else: - result.add nimFloatToString(x) - else: impl() +when not defined(nimPreviewSlimSystem): + import std/formatfloat + export formatfloat + {.deprecated: "use `std/formatfloat`".} +else: + {.error: "use `std/formatfloat`".} diff --git a/lib/system/repr_v2.nim b/lib/system/repr_v2.nim index 6ab5f3c3f744..0e9bec0f3b99 100644 --- a/lib/system/repr_v2.nim +++ b/lib/system/repr_v2.nim @@ -1,5 +1,8 @@ include system/inclrtl +when defined(nimPreviewSlimSystem): + import std/formatfloat + proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} ## imported from typetraits