Merge branch 'devel' into IOSelector_unregister_fix
This commit is contained in:
commit
92fa7e0579
94 changed files with 1729 additions and 1223 deletions
|
|
@ -94,6 +94,18 @@
|
|||
##
|
||||
## db.close()
|
||||
##
|
||||
##
|
||||
## Note
|
||||
## ====
|
||||
## This module does not implement any ORM features such as mapping the types from the schema.
|
||||
## Instead, a ``seq[string]`` is returned for each row.
|
||||
##
|
||||
## The reasoning is as follows:
|
||||
## 1. it's close to what many DBs offer natively (char**)
|
||||
## 2. it hides the number of types that the DB supports
|
||||
## (int? int64? decimal up to 10 places? geo coords?)
|
||||
## 3. it's convenient when all you do is to forward the data to somewhere else (echo, log, put the data into a new query)
|
||||
##
|
||||
## See also
|
||||
## ========
|
||||
##
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ proc logImpl(console: Console) {.importcpp: "log", varargs.}
|
|||
proc debugImpl(console: Console) {.importcpp: "debug", varargs.}
|
||||
proc infoImpl(console: Console) {.importcpp: "info", varargs.}
|
||||
proc errorImpl(console: Console) {.importcpp: "error", varargs.}
|
||||
proc warnImpl(console: Console) {.importcpp: "warn", varargs.}
|
||||
|
||||
proc makeConsoleCall(console: NimNode, procName: NimNode, args: NimNode): NimNode =
|
||||
result = newCall(procName, console)
|
||||
|
|
@ -41,4 +42,28 @@ macro info*(console: Console, args: varargs[RootRef, convertToConsoleLoggable]):
|
|||
macro error*(console: Console, args: varargs[RootRef, convertToConsoleLoggable]): untyped =
|
||||
makeConsoleCall(console, bindSym "errorImpl", args)
|
||||
|
||||
var console* {.importc, nodecl.}: Console
|
||||
|
||||
macro warn*(console: Console, args: varargs[RootRef, convertToConsoleLoggable]): untyped =
|
||||
## https://developer.mozilla.org/en-US/docs/Web/API/Console/warn
|
||||
makeConsoleCall(console, bindSym "warnImpl", args)
|
||||
|
||||
proc clear*(console: Console) {.importcpp: "clear".} ## https://developer.mozilla.org/en-US/docs/Web/API/Console/clear
|
||||
|
||||
proc count*(console: Console, label = "".cstring) {.importcpp: "count".} ## https://developer.mozilla.org/en-US/docs/Web/API/Console/count
|
||||
|
||||
proc countReset*(console: Console, label = "".cstring) {.importcpp: "countReset".} ## https://developer.mozilla.org/en-US/docs/Web/API/Console/countReset
|
||||
|
||||
proc group*(console: Console, label = "".cstring) {.importcpp: "group".} ## https://developer.mozilla.org/en-US/docs/Web/API/Console/group
|
||||
|
||||
proc groupCollapsed*(console: Console, label = "".cstring) {.importcpp: "groupCollapsed".} ## https://developer.mozilla.org/en-US/docs/Web/API/Console/groupCollapsed
|
||||
|
||||
proc groupEnd*(console: Console) {.importcpp: "groupEnd".} ## https://developer.mozilla.org/en-US/docs/Web/API/Console/groupEnd
|
||||
|
||||
proc time*(console: Console, label = "".cstring) {.importcpp: "time".} ## https://developer.mozilla.org/en-US/docs/Web/API/Console/time
|
||||
|
||||
proc timeEnd*(console: Console, label = "".cstring) {.importcpp: "timeEnd".} ## https://developer.mozilla.org/en-US/docs/Web/API/Console/timeEnd
|
||||
|
||||
proc timeLog*(console: Console, label = "".cstring) {.importcpp: "timeLog".} ## https://developer.mozilla.org/en-US/docs/Web/API/Console/timeLog
|
||||
|
||||
|
||||
var console* {.importc, nodecl.}: Console
|
||||
|
|
|
|||
|
|
@ -56,100 +56,119 @@
|
|||
|
||||
const
|
||||
cb64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
invalidChar = 255
|
||||
|
||||
template encodeInternal(s: typed, lineLen: int, newLine: string): untyped =
|
||||
## encodes `s` into base64 representation. After `lineLen` characters, a
|
||||
## `newline` is added.
|
||||
var total = ((len(s) + 2) div 3) * 4
|
||||
let numLines = (total + lineLen - 1) div lineLen
|
||||
if numLines > 0: inc(total, (numLines - 1) * newLine.len)
|
||||
template encodeInternal(s: typed): untyped =
|
||||
## encodes `s` into base64 representation.
|
||||
proc encodeSize(size: int): int =
|
||||
return (size * 4 div 3) + 6
|
||||
|
||||
result.setLen(encodeSize(s.len))
|
||||
|
||||
result = newString(total)
|
||||
var
|
||||
i = 0
|
||||
r = 0
|
||||
currLine = 0
|
||||
while i < s.len - 2:
|
||||
let
|
||||
a = ord(s[i])
|
||||
b = ord(s[i+1])
|
||||
c = ord(s[i+2])
|
||||
result[r] = cb64[a shr 2]
|
||||
result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
||||
result[r+2] = cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
|
||||
result[r+3] = cb64[c and 0x3F]
|
||||
inc(r, 4)
|
||||
inc(i, 3)
|
||||
inc(currLine, 4)
|
||||
# avoid index out of bounds when lineLen == encoded length
|
||||
if currLine >= lineLen and i != s.len-2 and r < total:
|
||||
for x in items(newLine):
|
||||
result[r] = x
|
||||
inc(r)
|
||||
currLine = 0
|
||||
inputIndex = 0
|
||||
outputIndex = 0
|
||||
inputEnds = s.len - s.len mod 3
|
||||
n: uint32
|
||||
b: uint32
|
||||
|
||||
if i < s.len-1:
|
||||
let
|
||||
a = ord(s[i])
|
||||
b = ord(s[i+1])
|
||||
result[r] = cb64[a shr 2]
|
||||
result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
||||
result[r+2] = cb64[((b and 0x0F) shl 2)]
|
||||
result[r+3] = '='
|
||||
if r+4 != result.len:
|
||||
setLen(result, r+4)
|
||||
elif i < s.len:
|
||||
let a = ord(s[i])
|
||||
result[r] = cb64[a shr 2]
|
||||
result[r+1] = cb64[(a and 3) shl 4]
|
||||
result[r+2] = '='
|
||||
result[r+3] = '='
|
||||
if r+4 != result.len:
|
||||
setLen(result, r+4)
|
||||
else:
|
||||
if r != result.len:
|
||||
setLen(result, r)
|
||||
#assert(r == result.len)
|
||||
discard
|
||||
template inputByte(exp: untyped) =
|
||||
b = uint32(s[inputIndex])
|
||||
n = exp
|
||||
inc inputIndex
|
||||
|
||||
proc encode*[T: SomeInteger|char](s: openArray[T], lineLen = 75,
|
||||
newLine = ""): string =
|
||||
## Encodes ``s`` into base64 representation. After ``lineLen`` characters, a
|
||||
## ``newline`` is added.
|
||||
template outputChar(x: untyped) =
|
||||
result[outputIndex] = cb64[x and 63]
|
||||
inc outputIndex
|
||||
|
||||
template outputChar(c: char) =
|
||||
result[outputIndex] = c
|
||||
inc outputIndex
|
||||
|
||||
while inputIndex != inputEnds:
|
||||
inputByte(b shl 16)
|
||||
inputByte(n or b shl 8)
|
||||
inputByte(n or b shl 0)
|
||||
outputChar(n shr 18)
|
||||
outputChar(n shr 12)
|
||||
outputChar(n shr 6)
|
||||
outputChar(n shr 0)
|
||||
|
||||
var padding = s.len mod 3
|
||||
if padding == 1:
|
||||
inputByte(b shl 16)
|
||||
outputChar(n shr 18)
|
||||
outputChar(n shr 12)
|
||||
outputChar('=')
|
||||
outputChar('=')
|
||||
|
||||
elif padding == 2:
|
||||
inputByte(b shl 16)
|
||||
inputByte(n or b shl 8)
|
||||
outputChar(n shr 18)
|
||||
outputChar(n shr 12)
|
||||
outputChar(n shr 6)
|
||||
outputChar('=')
|
||||
|
||||
result.setLen(outputIndex)
|
||||
|
||||
proc encode*[T: SomeInteger|char](s: openarray[T]): string =
|
||||
## Encodes `s` into base64 representation.
|
||||
##
|
||||
## This procedure encodes an openarray (array or sequence) of either integers
|
||||
## or characters.
|
||||
##
|
||||
## **See also:**
|
||||
## * `encode proc<#encode,string,int,string>`_ for encoding a string
|
||||
## * `encode proc<#encode,string>`_ for encoding a string
|
||||
## * `decode proc<#decode,string>`_ for decoding a string
|
||||
runnableExamples:
|
||||
assert encode(['n', 'i', 'm']) == "bmlt"
|
||||
assert encode(@['n', 'i', 'm']) == "bmlt"
|
||||
assert encode([1, 2, 3, 4, 5]) == "AQIDBAU="
|
||||
encodeInternal(s, lineLen, newLine)
|
||||
encodeInternal(s)
|
||||
|
||||
proc encode*(s: string, lineLen = 75, newLine = ""): string =
|
||||
## Encodes ``s`` into base64 representation. After ``lineLen`` characters, a
|
||||
## ``newline`` is added.
|
||||
proc encode*(s: string): string =
|
||||
## Encodes ``s`` into base64 representation.
|
||||
##
|
||||
## This procedure encodes a string.
|
||||
##
|
||||
## **See also:**
|
||||
## * `encode proc<#encode,openArray[T],int,string>`_ for encoding an openarray
|
||||
## * `encode proc<#encode,openArray[T]>`_ for encoding an openarray
|
||||
## * `decode proc<#decode,string>`_ for decoding a string
|
||||
runnableExamples:
|
||||
assert encode("Hello World") == "SGVsbG8gV29ybGQ="
|
||||
assert encode("Hello World", 3, "\n") == "SGVs\nbG8g\nV29ybGQ="
|
||||
encodeInternal(s, lineLen, newLine)
|
||||
encodeInternal(s)
|
||||
|
||||
proc decodeByte(b: char): int {.inline.} =
|
||||
case b
|
||||
of '+': result = ord('>')
|
||||
of '0'..'9': result = ord(b) + 4
|
||||
of 'A'..'Z': result = ord(b) - ord('A')
|
||||
of 'a'..'z': result = ord(b) - 71
|
||||
else: result = 63
|
||||
proc encodeMIME*(s: string, lineLen = 75, newLine = "\r\n"): string =
|
||||
## Encodes ``s`` into base64 representation as lines.
|
||||
## Used in email MIME forma, use ``lineLen`` and ``newline``.
|
||||
##
|
||||
## This procedure encodes a string according to MIME spec.
|
||||
##
|
||||
## **See also:**
|
||||
## * `encode proc<#encode,string>`_ for encoding a string
|
||||
## * `decode proc<#decode,string>`_ for decoding a string
|
||||
runnableExamples:
|
||||
assert encodeMIME("Hello World", 4, "\n") == "SGVs\nbG8g\nV29y\nbGQ="
|
||||
for i, c in encode(s):
|
||||
if i != 0 and (i mod lineLen == 0):
|
||||
result.add(newLine)
|
||||
result.add(c)
|
||||
|
||||
proc initDecodeTable*(): array[256, char] =
|
||||
# computes a decode table at compile time
|
||||
for i in 0 ..< 256:
|
||||
let ch = char(i)
|
||||
var code = invalidChar
|
||||
if ch >= 'A' and ch <= 'Z': code = i - 0x00000041
|
||||
if ch >= 'a' and ch <= 'z': code = i - 0x00000047
|
||||
if ch >= '0' and ch <= '9': code = i + 0x00000004
|
||||
if ch == '+' or ch == '-': code = 0x0000003E
|
||||
if ch == '/' or ch == '_': code = 0x0000003F
|
||||
result[i] = char(code)
|
||||
|
||||
const
|
||||
decodeTable = initDecodeTable()
|
||||
|
||||
proc decode*(s: string): string =
|
||||
## Decodes string ``s`` in base64 representation back into its original form.
|
||||
|
|
@ -161,53 +180,58 @@ proc decode*(s: string): string =
|
|||
runnableExamples:
|
||||
assert decode("SGVsbG8gV29ybGQ=") == "Hello World"
|
||||
assert decode(" SGVsbG8gV29ybGQ=") == "Hello World"
|
||||
const Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'}
|
||||
var total = ((len(s) + 3) div 4) * 3
|
||||
# total is an upper bound, as we will skip arbitrary whitespace:
|
||||
result = newString(total)
|
||||
if s.len == 0: return
|
||||
|
||||
proc decodeSize(size: int): int =
|
||||
return (size * 3 div 4) + 6
|
||||
|
||||
template inputChar(x: untyped) =
|
||||
let x = int decode_table[ord(s[inputIndex])]
|
||||
inc inputIndex
|
||||
if x == invalidChar:
|
||||
raise newException(ValueError,
|
||||
"Invalid base64 format character " & repr(s[inputIndex]) &
|
||||
" at location " & $inputIndex & ".")
|
||||
|
||||
template outputChar(x: untyped) =
|
||||
result[outputIndex] = char(x and 255)
|
||||
inc outputIndex
|
||||
|
||||
# pre allocate output string once
|
||||
result.setLen(decodeSize(s.len))
|
||||
var
|
||||
i = 0
|
||||
r = 0
|
||||
while true:
|
||||
while i < s.len and s[i] in Whitespace: inc(i)
|
||||
if i < s.len-3:
|
||||
let
|
||||
a = s[i].decodeByte
|
||||
b = s[i+1].decodeByte
|
||||
c = s[i+2].decodeByte
|
||||
d = s[i+3].decodeByte
|
||||
inputIndex = 0
|
||||
outputIndex = 0
|
||||
inputLen = s.len
|
||||
inputEnds = 0
|
||||
# strip trailing characters
|
||||
while s[inputLen - 1] in {'\n', '\r', ' ', '='}:
|
||||
dec inputLen
|
||||
# hot loop: read 4 characters at at time
|
||||
inputEnds = inputLen - 4
|
||||
while inputIndex <= inputEnds:
|
||||
while s[inputIndex] in {'\n', '\r', ' '}:
|
||||
inc inputIndex
|
||||
inputChar(a)
|
||||
inputChar(b)
|
||||
inputChar(c)
|
||||
inputChar(d)
|
||||
outputChar(a shl 2 or b shr 4)
|
||||
outputChar(b shl 4 or c shr 2)
|
||||
outputChar(c shl 6 or d shr 0)
|
||||
# do the last 2 or 3 characters
|
||||
var leftLen = abs((inputIndex - inputLen) mod 4)
|
||||
if leftLen == 2:
|
||||
inputChar(a)
|
||||
inputChar(b)
|
||||
outputChar(a shl 2 or b shr 4)
|
||||
elif leftLen == 3:
|
||||
inputChar(a)
|
||||
inputChar(b)
|
||||
inputChar(c)
|
||||
outputChar(a shl 2 or b shr 4)
|
||||
outputChar(b shl 4 or c shr 2)
|
||||
result.setLen(outputIndex)
|
||||
|
||||
result[r] = chr((a shl 2) and 0xff or ((b shr 4) and 0x03))
|
||||
result[r+1] = chr((b shl 4) and 0xff or ((c shr 2) and 0x0F))
|
||||
result[r+2] = chr((c shl 6) and 0xff or (d and 0x3F))
|
||||
inc(r, 3)
|
||||
inc(i, 4)
|
||||
else: break
|
||||
assert i == s.len
|
||||
# adjust the length:
|
||||
if i > 0 and s[i-1] == '=':
|
||||
dec(r)
|
||||
if i > 1 and s[i-2] == '=': dec(r)
|
||||
setLen(result, r)
|
||||
|
||||
when isMainModule:
|
||||
assert encode("leasure.") == "bGVhc3VyZS4="
|
||||
assert encode("easure.") == "ZWFzdXJlLg=="
|
||||
assert encode("asure.") == "YXN1cmUu"
|
||||
assert encode("sure.") == "c3VyZS4="
|
||||
|
||||
const testInputExpandsTo76 = "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
|
||||
const testInputExpands = "++++++++++++++++++++++++++++++"
|
||||
const longText = """Man is distinguished, not only by his reason, but by this
|
||||
singular passion from other animals, which is a lust of the mind,
|
||||
that by a perseverance of delight in the continued and indefatigable
|
||||
generation of knowledge, exceeds the short vehemence of any carnal
|
||||
pleasure."""
|
||||
const tests = ["", "abc", "xyz", "man", "leasure.", "sure.", "easure.",
|
||||
"asure.", longText, testInputExpandsTo76, testInputExpands]
|
||||
|
||||
for t in items(tests):
|
||||
assert decode(encode(t)) == t
|
||||
assert decode(encode(t, lineLen = 40)) == t
|
||||
assert decode(encode(t, lineLen = 76)) == t
|
||||
|
|
|
|||
|
|
@ -387,6 +387,7 @@ proc assign*(dest: var IntSet, src: IntSet) =
|
|||
else:
|
||||
dest.counter = src.counter
|
||||
dest.max = src.max
|
||||
dest.elems = src.elems
|
||||
newSeq(dest.data, src.data.len)
|
||||
|
||||
var it = src.head
|
||||
|
|
@ -653,3 +654,19 @@ when isMainModule:
|
|||
xs = toSeq(items(x))
|
||||
xs.sort(cmp[int])
|
||||
assert xs == @[1, 4, 7, 1001, 1056]
|
||||
|
||||
proc bug12366 =
|
||||
var
|
||||
x = initIntSet()
|
||||
y = initIntSet()
|
||||
n = 3584
|
||||
|
||||
for i in 0..n:
|
||||
x.incl(i)
|
||||
y.incl(i)
|
||||
|
||||
let z = symmetricDifference(x, y)
|
||||
doAssert z.len == 0
|
||||
doAssert $z == "{}"
|
||||
|
||||
bug12366()
|
||||
|
|
|
|||
|
|
@ -1019,9 +1019,9 @@ when isMainModule and not defined(release):
|
|||
# --> {1, 3, 5}
|
||||
|
||||
block toSeqAndString:
|
||||
var a = toHashSet([2, 4, 5])
|
||||
var a = toHashSet([2, 7, 5])
|
||||
var b = initHashSet[int]()
|
||||
for x in [2, 4, 5]: b.incl(x)
|
||||
for x in [2, 7, 5]: b.incl(x)
|
||||
assert($a == $b)
|
||||
#echo a
|
||||
#echo toHashSet(["no", "esc'aping", "is \" provided"])
|
||||
|
|
|
|||
|
|
@ -112,29 +112,32 @@ proc hash*[T: proc](x: T): Hash {.inline.} =
|
|||
else:
|
||||
result = hash(pointer(x))
|
||||
|
||||
const
|
||||
prime = uint(11)
|
||||
|
||||
proc hash*(x: int): Hash {.inline.} =
|
||||
## Efficient hashing of integers.
|
||||
result = x
|
||||
result = cast[Hash](cast[uint](x) * prime)
|
||||
|
||||
proc hash*(x: int64): Hash {.inline.} =
|
||||
## Efficient hashing of `int64` integers.
|
||||
result = cast[int](x)
|
||||
result = cast[Hash](cast[uint](x) * prime)
|
||||
|
||||
proc hash*(x: uint): Hash {.inline.} =
|
||||
## Efficient hashing of unsigned integers.
|
||||
result = cast[int](x)
|
||||
result = cast[Hash](x * prime)
|
||||
|
||||
proc hash*(x: uint64): Hash {.inline.} =
|
||||
## Efficient hashing of `uint64` integers.
|
||||
result = cast[int](x)
|
||||
result = cast[Hash](cast[uint](x) * prime)
|
||||
|
||||
proc hash*(x: char): Hash {.inline.} =
|
||||
## Efficient hashing of characters.
|
||||
result = ord(x)
|
||||
result = cast[Hash](cast[uint](ord(x)) * prime)
|
||||
|
||||
proc hash*[T: Ordinal](x: T): Hash {.inline.} =
|
||||
## Efficient hashing of other ordinal types (e.g. enums).
|
||||
result = ord(x)
|
||||
result = cast[Hash](cast[uint](ord(x)) * prime)
|
||||
|
||||
proc hash*(x: float): Hash {.inline.} =
|
||||
## Efficient hashing of floats.
|
||||
|
|
|
|||
|
|
@ -10,21 +10,33 @@
|
|||
## Do yourself a favor and import the module
|
||||
## as ``from htmlgen import nil`` and then fully qualify the macros.
|
||||
##
|
||||
## *Note*: The Karax project (``nimble install karax``) has a better
|
||||
## way to achieve the same, see `https://github.com/pragmagic/karax/blob/master/tests/nativehtmlgen.nim`_
|
||||
## for an example.
|
||||
##
|
||||
##
|
||||
## This module implements a simple `XML`:idx: and `HTML`:idx: code
|
||||
## generator. Each commonly used HTML tag has a corresponding macro
|
||||
## that generates a string with its HTML representation.
|
||||
##
|
||||
## MathML
|
||||
## ======
|
||||
##
|
||||
## `MathML <https://wikipedia.org/wiki/MathML>`_ is supported, MathML is part of HTML5.
|
||||
## `MathML <https://wikipedia.org/wiki/MathML>`_ is an Standard ISO/IEC 40314 from year 2015.
|
||||
## MathML allows you to `draw advanced math on the web <https://developer.mozilla.org/en-US/docs/Web/MathML/Element/math#Examples>`_,
|
||||
## `visually similar to Latex math. <https://developer.mozilla.org/en-US/docs/Web/MathML/Element/semantics#Example>`_
|
||||
##
|
||||
## Examples
|
||||
## ========
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## var nim = "Nim"
|
||||
## echo h1(a(href="http://nim-lang.org", nim))
|
||||
## echo h1(a(href="https://nim-lang.org", nim))
|
||||
##
|
||||
## Writes the string::
|
||||
##
|
||||
## <h1><a href="http://nim-lang.org">Nim</a></h1>
|
||||
## <h1><a href="https://nim-lang.org">Nim</a></h1>
|
||||
##
|
||||
|
||||
import
|
||||
|
|
@ -604,9 +616,195 @@ macro wbr*(e: varargs[untyped]): untyped =
|
|||
## generates the HTML ``wbr`` element.
|
||||
result = xmlCheckedTag(e, "wbr", commonAttr, "", true)
|
||||
|
||||
|
||||
macro math*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``math`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/math#Examples
|
||||
result = xmlCheckedTag(e, "math", "mathbackground mathcolor href overflow" & commonAttr)
|
||||
|
||||
macro maction*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``maction`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/maction
|
||||
result = xmlCheckedTag(e, "maction", "mathbackground mathcolor href" & commonAttr)
|
||||
|
||||
macro menclose*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``menclose`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/menclose
|
||||
result = xmlCheckedTag(e, "menclose", "mathbackground mathcolor href notation" & commonAttr)
|
||||
|
||||
macro merror*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``merror`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/merror
|
||||
result = xmlCheckedTag(e, "merror", "mathbackground mathcolor href" & commonAttr)
|
||||
|
||||
macro mfenced*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mfenced`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mfenced
|
||||
result = xmlCheckedTag(e, "mfenced", "mathbackground mathcolor href open separators" & commonAttr)
|
||||
|
||||
macro mfrac*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mfrac`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mfrac
|
||||
result = xmlCheckedTag(e, "mfrac", "mathbackground mathcolor href linethickness numalign" & commonAttr)
|
||||
|
||||
macro mglyph*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mglyph`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mglyph
|
||||
result = xmlCheckedTag(e, "mglyph", "mathbackground mathcolor href src valign" & commonAttr)
|
||||
|
||||
macro mi*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mi`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mi
|
||||
result = xmlCheckedTag(e, "mi", "mathbackground mathcolor href mathsize mathvariant" & commonAttr)
|
||||
|
||||
macro mlabeledtr*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mlabeledtr`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mlabeledtr
|
||||
result = xmlCheckedTag(e, "mlabeledtr", "mathbackground mathcolor href columnalign groupalign rowalign" & commonAttr)
|
||||
|
||||
macro mmultiscripts*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mmultiscripts`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mmultiscripts
|
||||
result = xmlCheckedTag(e, "mmultiscripts", "mathbackground mathcolor href subscriptshift superscriptshift" & commonAttr)
|
||||
|
||||
macro mn*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mn`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mn
|
||||
result = xmlCheckedTag(e, "mn", "mathbackground mathcolor href mathsize mathvariant" & commonAttr)
|
||||
|
||||
macro mo*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mo`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mo
|
||||
result = xmlCheckedTag(e, "mo",
|
||||
"mathbackground mathcolor fence form largeop lspace mathsize mathvariant movablelimits rspace separator stretchy symmetric" & commonAttr)
|
||||
|
||||
macro mover*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mover`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mover
|
||||
result = xmlCheckedTag(e, "mover", "mathbackground mathcolor accent href" & commonAttr)
|
||||
|
||||
macro mpadded*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mpadded`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mpadded
|
||||
result = xmlCheckedTag(e, "mpadded", "mathbackground mathcolor depth href lspace voffset" & commonAttr)
|
||||
|
||||
macro mphantom*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mphantom`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mphantom
|
||||
result = xmlCheckedTag(e, "mphantom", "mathbackground" & commonAttr)
|
||||
|
||||
macro mroot*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mroot`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mroot
|
||||
result = xmlCheckedTag(e, "mroot", "mathbackground mathcolor href" & commonAttr)
|
||||
|
||||
macro mrow*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mrow`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mrow
|
||||
result = xmlCheckedTag(e, "mrow", "mathbackground mathcolor href" & commonAttr)
|
||||
|
||||
macro ms*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``ms`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/ms
|
||||
result = xmlCheckedTag(e, "ms", "mathbackground mathcolor href lquote mathsize mathvariant rquote" & commonAttr)
|
||||
|
||||
macro mspace*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mspace`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mspace
|
||||
result = xmlCheckedTag(e, "mspace", "mathbackground mathcolor href linebreak" & commonAttr)
|
||||
|
||||
macro msqrt*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``msqrt`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/msqrt
|
||||
result = xmlCheckedTag(e, "msqrt", "mathbackground mathcolor href" & commonAttr)
|
||||
|
||||
macro mstyle*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mstyle`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mstyle
|
||||
result = xmlCheckedTag(e, "mstyle", ("mathbackground mathcolor href decimalpoint displaystyle " &
|
||||
"infixlinebreakstyle scriptlevel scriptminsize scriptsizemultiplier" & commonAttr))
|
||||
|
||||
macro msub*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``msub`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/msub
|
||||
result = xmlCheckedTag(e, "msub", "mathbackground mathcolor href subscriptshift" & commonAttr)
|
||||
|
||||
macro msubsup*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``msubsup`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/msubsup
|
||||
result = xmlCheckedTag(e, "msubsup", "mathbackground mathcolor href subscriptshift superscriptshift" & commonAttr)
|
||||
|
||||
macro msup*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``msup`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/msup
|
||||
result = xmlCheckedTag(e, "msup", "mathbackground mathcolor href superscriptshift" & commonAttr)
|
||||
|
||||
macro mtable*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mtable`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mtable
|
||||
result = xmlCheckedTag(e, "mtable", ("mathbackground mathcolor href align " &
|
||||
"alignmentscope columnalign columnlines columnspacing columnwidth " &
|
||||
"displaystyle equalcolumns equalrows frame framespacing groupalign " &
|
||||
"rowalign rowlines rowspacing side width" & commonAttr))
|
||||
|
||||
macro mtd*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mtd`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mtd
|
||||
result = xmlCheckedTag(e, "mtd",
|
||||
"mathbackground mathcolor href columnalign columnspan groupalign rowalign rowspan" & commonAttr)
|
||||
|
||||
macro mtext*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mtext`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mtext
|
||||
result = xmlCheckedTag(e, "mtext", "mathbackground mathcolor href mathsize mathvariant" & commonAttr)
|
||||
|
||||
macro munder*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``munder`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/munder
|
||||
result = xmlCheckedTag(e, "munder", "mathbackground mathcolor href accentunder align" & commonAttr)
|
||||
|
||||
macro munderover*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``munderover`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/munderover
|
||||
result = xmlCheckedTag(e, "munderover", "mathbackground mathcolor href accentunder accent align" & commonAttr)
|
||||
|
||||
macro semantics*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``semantics`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/semantics
|
||||
result = xmlCheckedTag(e, "semantics", "mathbackground mathcolor href definitionURL encoding cd src" & commonAttr)
|
||||
|
||||
macro annotation*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``annotation`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/semantics
|
||||
result = xmlCheckedTag(e, "annotation", "mathbackground mathcolor href definitionURL encoding cd src" & commonAttr)
|
||||
|
||||
macro `annotation-xml`*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``annotation-xml`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/semantics
|
||||
result = xmlCheckedTag(e, "annotation", "mathbackground mathcolor href definitionURL encoding cd src" & commonAttr)
|
||||
|
||||
|
||||
runnableExamples:
|
||||
let nim = "Nim"
|
||||
assert h1(a(href = "http://nim-lang.org", nim)) ==
|
||||
"""<h1><a href="http://nim-lang.org">Nim</a></h1>"""
|
||||
assert h1(a(href = "https://nim-lang.org", nim)) ==
|
||||
"""<h1><a href="https://nim-lang.org">Nim</a></h1>"""
|
||||
assert form(action = "test", `accept-charset` = "Content-Type") ==
|
||||
"""<form action="test" accept-charset="Content-Type"></form>"""
|
||||
|
||||
|
||||
assert math(
|
||||
semantics(
|
||||
mrow(
|
||||
msup(
|
||||
mi("x"),
|
||||
mn("42")
|
||||
)
|
||||
)
|
||||
)
|
||||
) == "<math><semantics><mrow><msup><mi>x</mi><mn>42</mn></msup></mrow></semantics></math>"
|
||||
|
||||
assert math(
|
||||
semantics(
|
||||
annotation(encoding = "application/x-tex", title = "Latex on Web", r"x^{2} + y")
|
||||
)
|
||||
) == """<math><semantics><annotation encoding="application/x-tex" title="Latex on Web">x^{2} + y</annotation></semantics></math>"""
|
||||
|
|
|
|||
|
|
@ -454,7 +454,7 @@ proc generateHeaders(requestUrl: Uri, httpMethod: string,
|
|||
|
||||
# Proxy auth header.
|
||||
if not proxy.isNil and proxy.auth != "":
|
||||
let auth = base64.encode(proxy.auth, newline = "")
|
||||
let auth = base64.encode(proxy.auth)
|
||||
add(result, "Proxy-Authorization: basic " & auth & "\c\L")
|
||||
|
||||
for key, val in headers:
|
||||
|
|
|
|||
|
|
@ -833,50 +833,37 @@ proc parseJson(p: var JsonParser): JsonNode =
|
|||
of tkError, tkCurlyRi, tkBracketRi, tkColon, tkComma, tkEof:
|
||||
raiseParseErr(p, "{")
|
||||
|
||||
when not defined(js):
|
||||
iterator parseJsonFragments*(s: Stream, filename: string = ""): JsonNode =
|
||||
## Parses from a stream `s` into `JsonNodes`. `filename` is only needed
|
||||
## for nice error messages.
|
||||
## The JSON fragments are separated by whitespace. This can be substantially
|
||||
## faster than the comparable loop
|
||||
## ``for x in splitWhitespace(s): yield parseJson(x)``.
|
||||
## This closes the stream `s` after it's done.
|
||||
var p: JsonParser
|
||||
p.open(s, filename)
|
||||
try:
|
||||
discard getTok(p) # read first token
|
||||
while p.tok != tkEof:
|
||||
yield p.parseJson()
|
||||
finally:
|
||||
p.close()
|
||||
iterator parseJsonFragments*(s: Stream, filename: string = ""): JsonNode =
|
||||
## Parses from a stream `s` into `JsonNodes`. `filename` is only needed
|
||||
## for nice error messages.
|
||||
## The JSON fragments are separated by whitespace. This can be substantially
|
||||
## faster than the comparable loop
|
||||
## ``for x in splitWhitespace(s): yield parseJson(x)``.
|
||||
## This closes the stream `s` after it's done.
|
||||
var p: JsonParser
|
||||
p.open(s, filename)
|
||||
try:
|
||||
discard getTok(p) # read first token
|
||||
while p.tok != tkEof:
|
||||
yield p.parseJson()
|
||||
finally:
|
||||
p.close()
|
||||
|
||||
proc parseJson*(s: Stream, filename: string = ""): JsonNode =
|
||||
## Parses from a stream `s` into a `JsonNode`. `filename` is only needed
|
||||
## for nice error messages.
|
||||
## If `s` contains extra data, it will raise `JsonParsingError`.
|
||||
## This closes the stream `s` after it's done.
|
||||
var p: JsonParser
|
||||
p.open(s, filename)
|
||||
try:
|
||||
discard getTok(p) # read first token
|
||||
result = p.parseJson()
|
||||
eat(p, tkEof) # check if there is no extra data
|
||||
finally:
|
||||
p.close()
|
||||
proc parseJson*(s: Stream, filename: string = ""): JsonNode =
|
||||
## Parses from a stream `s` into a `JsonNode`. `filename` is only needed
|
||||
## for nice error messages.
|
||||
## If `s` contains extra data, it will raise `JsonParsingError`.
|
||||
## This closes the stream `s` after it's done.
|
||||
var p: JsonParser
|
||||
p.open(s, filename)
|
||||
try:
|
||||
discard getTok(p) # read first token
|
||||
result = p.parseJson()
|
||||
eat(p, tkEof) # check if there is no extra data
|
||||
finally:
|
||||
p.close()
|
||||
|
||||
proc parseJson*(buffer: string): JsonNode =
|
||||
## Parses JSON from `buffer`.
|
||||
## If `buffer` contains extra data, it will raise `JsonParsingError`.
|
||||
result = parseJson(newStringStream(buffer), "input")
|
||||
|
||||
proc parseFile*(filename: string): JsonNode =
|
||||
## Parses `file` into a `JsonNode`.
|
||||
## If `file` contains extra data, it will raise `JsonParsingError`.
|
||||
var stream = newFileStream(filename, fmRead)
|
||||
if stream == nil:
|
||||
raise newException(IOError, "cannot read from file: " & filename)
|
||||
result = parseJson(stream, filename)
|
||||
else:
|
||||
when defined(js):
|
||||
from math import `mod`
|
||||
type
|
||||
JSObject = object
|
||||
|
|
@ -946,38 +933,32 @@ else:
|
|||
result = newJNull()
|
||||
|
||||
proc parseJson*(buffer: string): JsonNode =
|
||||
return parseNativeJson(buffer).convertObject()
|
||||
when nimvm:
|
||||
return parseJson(newStringStream(buffer), "input")
|
||||
else:
|
||||
return parseNativeJson(buffer).convertObject()
|
||||
|
||||
# -- Json deserialiser macro. --
|
||||
else:
|
||||
proc parseJson*(buffer: string): JsonNode =
|
||||
## Parses JSON from `buffer`.
|
||||
## If `buffer` contains extra data, it will raise `JsonParsingError`.
|
||||
result = parseJson(newStringStream(buffer), "input")
|
||||
|
||||
proc createJsonIndexer(jsonNode: NimNode,
|
||||
index: string | int | NimNode): NimNode
|
||||
{.compileTime.} =
|
||||
when index is string:
|
||||
let indexNode = newStrLitNode(index)
|
||||
elif index is int:
|
||||
let indexNode = newIntLitNode(index)
|
||||
elif index is NimNode:
|
||||
let indexNode = index
|
||||
proc parseFile*(filename: string): JsonNode =
|
||||
## Parses `file` into a `JsonNode`.
|
||||
## If `file` contains extra data, it will raise `JsonParsingError`.
|
||||
var stream = newFileStream(filename, fmRead)
|
||||
if stream == nil:
|
||||
raise newException(IOError, "cannot read from file: " & filename)
|
||||
result = parseJson(stream, filename)
|
||||
|
||||
result = newNimNode(nnkBracketExpr).add(
|
||||
jsonNode,
|
||||
indexNode
|
||||
)
|
||||
|
||||
proc transformJsonIndexer(jsonNode: NimNode): NimNode =
|
||||
case jsonNode.kind
|
||||
of nnkBracketExpr:
|
||||
result = newNimNode(nnkCurlyExpr)
|
||||
else:
|
||||
result = jsonNode.copy()
|
||||
|
||||
for child in jsonNode:
|
||||
result.add(transformJsonIndexer(child))
|
||||
# -- Json deserialiser. --
|
||||
|
||||
template verifyJsonKind(node: JsonNode, kinds: set[JsonNodeKind],
|
||||
ast: string) =
|
||||
if node.kind notin kinds:
|
||||
if node == nil:
|
||||
raise newException(KeyError, "key not found: " & ast)
|
||||
elif node.kind notin kinds:
|
||||
let msg = "Incorrect JSON kind. Wanted '$1' in '$2' but got '$3'." % [
|
||||
$kinds,
|
||||
ast,
|
||||
|
|
@ -985,588 +966,232 @@ template verifyJsonKind(node: JsonNode, kinds: set[JsonNodeKind],
|
|||
]
|
||||
raise newException(JsonKindError, msg)
|
||||
|
||||
proc getEnum(node: JsonNode, ast: string, T: typedesc): T =
|
||||
when T is SomeInteger:
|
||||
# TODO: I shouldn't need this proc.
|
||||
proc convert[T](x: BiggestInt): T = T(x)
|
||||
verifyJsonKind(node, {JInt}, ast)
|
||||
return convert[T](node.getBiggestInt())
|
||||
else:
|
||||
verifyJsonKind(node, {JString}, ast)
|
||||
return parseEnum[T](node.getStr())
|
||||
|
||||
proc toIdentNode(typeNode: NimNode): NimNode =
|
||||
## Converts a Sym type node (returned by getType et al.) into an
|
||||
## Ident node. Placing Sym type nodes inside the resulting code AST is
|
||||
## unsound (according to @Araq) so this is necessary.
|
||||
case typeNode.kind
|
||||
of nnkSym:
|
||||
return newIdentNode($typeNode)
|
||||
of nnkBracketExpr:
|
||||
result = typeNode
|
||||
for i in 0..<len(result):
|
||||
result[i] = newIdentNode($result[i])
|
||||
of nnkIdent:
|
||||
return typeNode
|
||||
else:
|
||||
doAssert false, "Cannot convert typeNode to an ident node: " & $typeNode.kind
|
||||
when defined(nimFixedForwardGeneric):
|
||||
# The following forward declarations don't work in older versions of Nim
|
||||
|
||||
proc createGetEnumCall(jsonNode, kindType: NimNode): NimNode =
|
||||
# -> getEnum(`jsonNode`, `kindType`)
|
||||
result = newCall(bindSym("getEnum"), jsonNode, toStrLit(jsonNode), kindType)
|
||||
# forward declare all initFromJson
|
||||
|
||||
proc createOfBranchCond(ofBranch, getEnumCall: NimNode): NimNode =
|
||||
## Creates an expression that acts as the condition for an ``of`` branch.
|
||||
var cond = newIdentNode("false")
|
||||
for ofCond in ofBranch:
|
||||
if ofCond.kind == nnkRecList:
|
||||
break
|
||||
proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: string)
|
||||
proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: string)
|
||||
proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: string)
|
||||
proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: string)
|
||||
proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: string)
|
||||
proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: string)
|
||||
proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: string)
|
||||
proc initFromJson[S,T](dst: var array[S,T]; jsonNode: JsonNode; jsonPath: string)
|
||||
proc initFromJson[T](dst: var Table[string,T];jsonNode: JsonNode; jsonPath: string)
|
||||
proc initFromJson[T](dst: var OrderedTable[string,T];jsonNode: JsonNode; jsonPath: string)
|
||||
proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: string)
|
||||
proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: string)
|
||||
proc initFromJson[T: distinct](dst: var T;jsonNode: JsonNode; jsonPath: string)
|
||||
proc initFromJson[T: object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: string)
|
||||
|
||||
let comparison = infix(getEnumCall, "==", ofCond)
|
||||
cond = infix(cond, "or", comparison)
|
||||
# initFromJson definitions
|
||||
|
||||
return cond
|
||||
|
||||
proc processObjField(field, jsonNode: NimNode): seq[NimNode] {.compileTime.}
|
||||
proc processOfBranch(ofBranch, jsonNode, kindType,
|
||||
kindJsonNode: NimNode): seq[NimNode] {.compileTime.} =
|
||||
## Processes each field inside of an object's ``of`` branch.
|
||||
## For each field a new ExprColonExpr node is created and put in the
|
||||
## resulting list.
|
||||
##
|
||||
## Sample ``ofBranch`` AST:
|
||||
##
|
||||
## .. code-block::plain
|
||||
## OfBranch of 0, 1:
|
||||
## IntLit 0 foodPos: float
|
||||
## IntLit 1 enemyPos: float
|
||||
## RecList
|
||||
## Sym "foodPos"
|
||||
## Sym "enemyPos"
|
||||
result = @[]
|
||||
let getEnumCall = createGetEnumCall(kindJsonNode, kindType)
|
||||
|
||||
for branchField in ofBranch[^1]:
|
||||
let objFields = processObjField(branchField, jsonNode)
|
||||
|
||||
for objField in objFields:
|
||||
let exprColonExpr = newNimNode(nnkExprColonExpr)
|
||||
result.add(exprColonExpr)
|
||||
# Add the name of the field.
|
||||
exprColonExpr.add(toIdentNode(objField[0]))
|
||||
|
||||
# Add the value of the field.
|
||||
let cond = createOfBranchCond(ofBranch, getEnumCall)
|
||||
exprColonExpr.add(newIfStmt(
|
||||
(cond, objField[1])
|
||||
))
|
||||
|
||||
proc processElseBranch(recCaseNode, elseBranch, jsonNode, kindType,
|
||||
kindJsonNode: NimNode): seq[NimNode] {.compileTime.} =
|
||||
## Processes each field inside of a variant object's ``else`` branch.
|
||||
##
|
||||
## ..code-block::plain
|
||||
## Else
|
||||
## RecList
|
||||
## Sym "other"
|
||||
result = @[]
|
||||
let getEnumCall = createGetEnumCall(kindJsonNode, kindType)
|
||||
|
||||
# We need to build up a list of conditions from each ``of`` branch so that
|
||||
# we can then negate it to get ``else``.
|
||||
var cond = newIdentNode("false")
|
||||
for i in 1 ..< len(recCaseNode):
|
||||
if recCaseNode[i].kind == nnkElse:
|
||||
break
|
||||
|
||||
cond = infix(cond, "or", createOfBranchCond(recCaseNode[i], getEnumCall))
|
||||
|
||||
# Negate the condition.
|
||||
cond = prefix(cond, "not")
|
||||
|
||||
for branchField in elseBranch[^1]:
|
||||
let objFields = processObjField(branchField, jsonNode)
|
||||
|
||||
for objField in objFields:
|
||||
let exprColonExpr = newNimNode(nnkExprColonExpr)
|
||||
result.add(exprColonExpr)
|
||||
# Add the name of the field.
|
||||
exprColonExpr.add(toIdentNode(objField[0]))
|
||||
|
||||
# Add the value of the field.
|
||||
let ifStmt = newIfStmt((cond, objField[1]))
|
||||
exprColonExpr.add(ifStmt)
|
||||
|
||||
proc createConstructor(typeSym, jsonNode: NimNode): NimNode {.compileTime.}
|
||||
|
||||
proc detectDistinctType(typeSym: NimNode): NimNode =
|
||||
let
|
||||
typeImpl = getTypeImpl(typeSym)
|
||||
typeInst = getTypeInst(typeSym)
|
||||
result = if typeImpl.typeKind == ntyDistinct: typeImpl else: typeInst
|
||||
|
||||
proc processObjField(field, jsonNode: NimNode): seq[NimNode] =
|
||||
## Process a field from a ``RecList``.
|
||||
##
|
||||
## The field will typically be a simple ``Sym`` node, but for object variants
|
||||
## it may also be a ``RecCase`` in which case things become complicated.
|
||||
result = @[]
|
||||
case field.kind
|
||||
of nnkSym:
|
||||
# Ordinary field. For example, `name: string`.
|
||||
let exprColonExpr = newNimNode(nnkExprColonExpr)
|
||||
result.add(exprColonExpr)
|
||||
|
||||
# Add the field name.
|
||||
exprColonExpr.add(toIdentNode(field))
|
||||
|
||||
# Add the field value.
|
||||
# -> jsonNode["`field`"]
|
||||
let indexedJsonNode = createJsonIndexer(jsonNode, $field)
|
||||
let typeNode = detectDistinctType(field)
|
||||
exprColonExpr.add(createConstructor(typeNode, indexedJsonNode))
|
||||
of nnkRecCase:
|
||||
# A "case" field that introduces a variant.
|
||||
let exprEqExpr = newNimNode(nnkExprEqExpr)
|
||||
result.add(exprEqExpr)
|
||||
|
||||
# Add the "case" field name (usually "kind").
|
||||
exprEqExpr.add(toIdentNode(field[0]))
|
||||
|
||||
# -> jsonNode["`field[0]`"]
|
||||
let kindJsonNode = createJsonIndexer(jsonNode, $field[0])
|
||||
|
||||
# Add the "case" field's value.
|
||||
let kindType = toIdentNode(getTypeInst(field[0]))
|
||||
let getEnumSym = bindSym("getEnum")
|
||||
let astStrLit = toStrLit(kindJsonNode)
|
||||
let getEnumCall = newCall(getEnumSym, kindJsonNode, astStrLit, kindType)
|
||||
exprEqExpr.add(getEnumCall)
|
||||
|
||||
# Iterate through each `of` branch.
|
||||
for i in 1 ..< field.len:
|
||||
case field[i].kind
|
||||
of nnkOfBranch:
|
||||
result.add processOfBranch(field[i], jsonNode, kindType, kindJsonNode)
|
||||
of nnkElse:
|
||||
result.add processElseBranch(field, field[i], jsonNode, kindType, kindJsonNode)
|
||||
else:
|
||||
doAssert false, "Expected OfBranch or Else node kinds, got: " & $field[i].kind
|
||||
else:
|
||||
doAssert false, "Unable to process object field: " & $field.kind
|
||||
|
||||
doAssert result.len > 0
|
||||
|
||||
proc processFields(obj: NimNode,
|
||||
jsonNode: NimNode): seq[NimNode] {.compileTime.} =
|
||||
## Process all the fields of an ``ObjectTy`` and any of its
|
||||
## parent type's fields (via inheritance).
|
||||
result = @[]
|
||||
case obj.kind
|
||||
of nnkObjectTy:
|
||||
expectKind(obj[2], nnkRecList)
|
||||
for field in obj[2]:
|
||||
let nodes = processObjField(field, jsonNode)
|
||||
result.add(nodes)
|
||||
|
||||
# process parent type fields
|
||||
case obj[1].kind
|
||||
of nnkBracketExpr:
|
||||
assert $obj[1][0] == "ref"
|
||||
result.add(processFields(getType(obj[1][1]), jsonNode))
|
||||
of nnkSym:
|
||||
result.add(processFields(getType(obj[1]), jsonNode))
|
||||
proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: string) =
|
||||
verifyJsonKind(jsonNode, {JString, JNull}, jsonPath)
|
||||
# since strings don't have a nil state anymore, this mapping of
|
||||
# JNull to the default string is questionable. `none(string)` and
|
||||
# `some("")` have the same potentional json value `JNull`.
|
||||
if jsonNode.kind == JNull:
|
||||
dst = ""
|
||||
else:
|
||||
dst = jsonNode.str
|
||||
|
||||
proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: string) =
|
||||
verifyJsonKind(jsonNode, {JBool}, jsonPath)
|
||||
dst = jsonNode.bval
|
||||
|
||||
proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: string) =
|
||||
dst = jsonNode.copy
|
||||
|
||||
proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: string) =
|
||||
verifyJsonKind(jsonNode, {JInt}, jsonPath)
|
||||
dst = T(jsonNode.num)
|
||||
|
||||
proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: string) =
|
||||
verifyJsonKind(jsonNode, {JInt, JFloat}, jsonPath)
|
||||
if jsonNode.kind == JFloat:
|
||||
dst = T(jsonNode.fnum)
|
||||
else:
|
||||
dst = T(jsonNode.num)
|
||||
|
||||
proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: string) =
|
||||
verifyJsonKind(jsonNode, {JString}, jsonPath)
|
||||
dst = parseEnum[T](jsonNode.getStr)
|
||||
|
||||
proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: string) =
|
||||
verifyJsonKind(jsonNode, {JArray}, jsonPath)
|
||||
dst.setLen jsonNode.len
|
||||
for i in 0 ..< jsonNode.len:
|
||||
initFromJson(dst[i], jsonNode[i], jsonPath & "[" & $i & "]")
|
||||
|
||||
proc initFromJson[S,T](dst: var array[S,T]; jsonNode: JsonNode; jsonPath: string) =
|
||||
verifyJsonKind(jsonNode, {JArray}, jsonPath)
|
||||
for i in 0 ..< jsonNode.len:
|
||||
initFromJson(dst[i], jsonNode[i], jsonPath & "[" & $i & "]")
|
||||
|
||||
proc initFromJson[T](dst: var Table[string,T];jsonNode: JsonNode; jsonPath: string) =
|
||||
dst = initTable[string, T]()
|
||||
verifyJsonKind(jsonNode, {JObject}, jsonPath)
|
||||
for key in keys(jsonNode.fields):
|
||||
initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath & "." & key)
|
||||
|
||||
proc initFromJson[T](dst: var OrderedTable[string,T];jsonNode: JsonNode; jsonPath: string) =
|
||||
dst = initOrderedTable[string,T]()
|
||||
verifyJsonKind(jsonNode, {JObject}, jsonPath)
|
||||
for key in keys(jsonNode.fields):
|
||||
initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath & "." & key)
|
||||
|
||||
proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: string) =
|
||||
if jsonNode.kind == JNull:
|
||||
dst = nil
|
||||
else:
|
||||
dst = new(ref T)
|
||||
initFromJson(dst[], jsonNode, jsonPath)
|
||||
|
||||
proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: string) =
|
||||
if jsonNode != nil and jsonNode.kind != JNull:
|
||||
dst = some(default(T))
|
||||
initFromJson(dst.get, jsonNode, jsonPath)
|
||||
|
||||
macro assignDistinctImpl[T : distinct](dst: var T;jsonNode: JsonNode; jsonPath: string) =
|
||||
let typInst = getTypeInst(dst)
|
||||
let typImpl = getTypeImpl(dst)
|
||||
let baseTyp = typImpl[0]
|
||||
result = quote do:
|
||||
initFromJson( `baseTyp`(`dst`), `jsonNode`, `jsonPath`)
|
||||
|
||||
proc initFromJson[T : distinct](dst: var T; jsonNode: JsonNode; jsonPath: string) =
|
||||
assignDistinctImpl(dst, jsonNode, jsonPath)
|
||||
|
||||
proc detectIncompatibleType(typeExpr, lineinfoNode: NimNode): void =
|
||||
if typeExpr.kind == nnkTupleConstr:
|
||||
error("Use a named tuple instead of: " & typeExpr.repr, lineinfoNode)
|
||||
|
||||
proc foldObjectBody(dst, typeNode, tmpSym, jsonNode, jsonPath: NimNode, depth: int): void {.compileTime.} =
|
||||
if depth > 150:
|
||||
error("recursion limit reached", typeNode)
|
||||
case typeNode.kind
|
||||
of nnkEmpty:
|
||||
discard
|
||||
of nnkTupleTy:
|
||||
for identDefs in obj:
|
||||
expectKind(identDefs, nnkIdentDefs)
|
||||
let nodes = processObjField(identDefs[0], jsonNode)
|
||||
result.add(nodes)
|
||||
else:
|
||||
doAssert false, "Unable to process field type: " & $obj.kind
|
||||
of nnkRecList, nnkTupleTy:
|
||||
for it in typeNode:
|
||||
foldObjectBody(dst, it, tmpSym, jsonNode, jsonPath, depth + 1)
|
||||
|
||||
proc processType(typeName: NimNode, obj: NimNode,
|
||||
jsonNode: NimNode, isRef: bool): NimNode {.compileTime.} =
|
||||
## Process a type such as ``Sym "float"`` or ``ObjectTy ...``.
|
||||
##
|
||||
## Sample ``ObjectTy``:
|
||||
##
|
||||
## .. code-block::plain
|
||||
## ObjectTy
|
||||
## Empty
|
||||
## InheritanceInformation
|
||||
## RecList
|
||||
## Sym "events"
|
||||
case obj.kind
|
||||
of nnkObjectTy, nnkTupleTy:
|
||||
# Create object constructor.
|
||||
result =
|
||||
if obj.kind == nnkObjectTy: newNimNode(nnkObjConstr)
|
||||
else: newNimNode(nnkPar)
|
||||
of nnkIdentDefs:
|
||||
typeNode.expectLen 3
|
||||
let fieldSym = typeNode[0]
|
||||
let fieldNameLit = newLit(fieldSym.strVal)
|
||||
let fieldType = typeNode[1]
|
||||
|
||||
if obj.kind == nnkObjectTy:
|
||||
result.add(typeName) # Name of the type to construct.
|
||||
# Detecting incompatiple tuple types in `assignObjectImpl` only
|
||||
# would be much cleaner, but the ast for tuple types does not
|
||||
# contain usable type information.
|
||||
detectIncompatibleType(fieldType, fieldSym)
|
||||
|
||||
# Process each object/tuple field and add it as an exprColonExpr
|
||||
result.add(processFields(obj, jsonNode))
|
||||
dst.add quote do:
|
||||
initFromJson(`tmpSym`.`fieldSym`, getOrDefault(`jsonNode`,`fieldNameLit`), `jsonPath` & "." & `fieldNameLit`)
|
||||
|
||||
# Object might be null. So we need to check for that.
|
||||
if isRef:
|
||||
result = quote do:
|
||||
verifyJsonKind(`jsonNode`, {JObject, JNull}, astToStr(`jsonNode`))
|
||||
if `jsonNode`.kind == JNull:
|
||||
nil
|
||||
of nnkRecCase:
|
||||
let kindSym = typeNode[0][0]
|
||||
let kindNameLit = newLit(kindSym.strVal)
|
||||
let kindType = typeNode[0][1]
|
||||
let kindOffsetLit = newLit(uint(getOffset(kindSym)))
|
||||
dst.add quote do:
|
||||
var kindTmp: `kindType`
|
||||
initFromJson(kindTmp, `jsonNode`[`kindNameLit`], `jsonPath` & "." & `kindNameLit`)
|
||||
when defined js:
|
||||
`tmpSym`.`kindSym` = kindTmp
|
||||
else:
|
||||
`result`
|
||||
when nimVm:
|
||||
`tmpSym`.`kindSym` = kindTmp
|
||||
else:
|
||||
# fuck it, assign kind field anyway
|
||||
((cast[ptr `kindType`](cast[uint](`tmpSym`.addr) + `kindOffsetLit`))[]) = kindTmp
|
||||
dst.add nnkCaseStmt.newTree(nnkDotExpr.newTree(tmpSym, kindSym))
|
||||
for i in 1 ..< typeNode.len:
|
||||
foldObjectBody(dst, typeNode[i], tmpSym, jsonNode, jsonPath, depth + 1)
|
||||
|
||||
of nnkOfBranch, nnkElse:
|
||||
let ofBranch = newNimNode(typeNode.kind)
|
||||
for i in 0 ..< typeNode.len-1:
|
||||
ofBranch.add copyNimTree(typeNode[i])
|
||||
let dstInner = newNimNode(nnkStmtListExpr)
|
||||
foldObjectBody(dstInner, typeNode[^1], tmpSym, jsonNode, jsonPath, depth + 1)
|
||||
# resOuter now contains the inner stmtList
|
||||
ofBranch.add dstInner
|
||||
dst[^1].expectKind nnkCaseStmt
|
||||
dst[^1].add ofBranch
|
||||
|
||||
of nnkObjectTy:
|
||||
typeNode[0].expectKind nnkEmpty
|
||||
typeNode[1].expectKind {nnkEmpty, nnkOfInherit}
|
||||
if typeNode[1].kind == nnkOfInherit:
|
||||
let base = typeNode[1][0]
|
||||
var impl = getTypeImpl(base)
|
||||
while impl.kind in {nnkRefTy, nnkPtrTy}:
|
||||
impl = getTypeImpl(impl[0])
|
||||
foldObjectBody(dst, impl, tmpSym, jsonNode, jsonPath, depth + 1)
|
||||
let body = typeNode[2]
|
||||
foldObjectBody(dst, body, tmpSym, jsonNode, jsonPath, depth + 1)
|
||||
|
||||
else:
|
||||
result = quote do:
|
||||
verifyJsonKind(`jsonNode`, {JObject}, astToStr(`jsonNode`));
|
||||
`result`
|
||||
error("unhandled kind: " & $typeNode.kind, typeNode)
|
||||
|
||||
of nnkEnumTy:
|
||||
let instType = toIdentNode(getTypeInst(typeName))
|
||||
let getEnumCall = createGetEnumCall(jsonNode, instType)
|
||||
result = quote do:
|
||||
(
|
||||
`getEnumCall`
|
||||
)
|
||||
of nnkSym:
|
||||
let name = normalize($typeName.getTypeImpl())
|
||||
case name
|
||||
of "string":
|
||||
result = quote do:
|
||||
(
|
||||
verifyJsonKind(`jsonNode`, {JString, JNull}, astToStr(`jsonNode`));
|
||||
if `jsonNode`.kind == JNull: "" else: `jsonNode`.str
|
||||
)
|
||||
of "biggestint":
|
||||
result = quote do:
|
||||
(
|
||||
verifyJsonKind(`jsonNode`, {JInt}, astToStr(`jsonNode`));
|
||||
`jsonNode`.num
|
||||
)
|
||||
of "bool":
|
||||
result = quote do:
|
||||
(
|
||||
verifyJsonKind(`jsonNode`, {JBool}, astToStr(`jsonNode`));
|
||||
`jsonNode`.bval
|
||||
)
|
||||
|
||||
macro assignObjectImpl[T](dst: var T; jsonNode: JsonNode; jsonPath: string) =
|
||||
let typeSym = getTypeInst(dst)
|
||||
result = newStmtList()
|
||||
if typeSym.kind in {nnkTupleTy, nnkTupleConstr}:
|
||||
# both, `dst` and `typeSym` don't have good lineinfo. But nothing
|
||||
# else is available here.
|
||||
detectIncompatibleType(typeSym, dst)
|
||||
foldObjectBody(result, typeSym, dst, jsonNode, jsonPath, 0)
|
||||
else:
|
||||
if name.startsWith("int") or name.startsWith("uint"):
|
||||
result = quote do:
|
||||
(
|
||||
verifyJsonKind(`jsonNode`, {JInt}, astToStr(`jsonNode`));
|
||||
`jsonNode`.num.`obj`
|
||||
)
|
||||
elif name.startsWith("float"):
|
||||
result = quote do:
|
||||
(
|
||||
verifyJsonKind(`jsonNode`, {JInt, JFloat}, astToStr(`jsonNode`));
|
||||
if `jsonNode`.kind == JFloat: `jsonNode`.fnum.`obj` else: `jsonNode`.num.`obj`
|
||||
)
|
||||
else:
|
||||
doAssert false, "Unable to process nnkSym " & $typeName
|
||||
else:
|
||||
doAssert false, "Unable to process type: " & $obj.kind
|
||||
foldObjectBody(result, typeSym.getTypeImpl, dst, jsonNode, jsonPath, 0)
|
||||
|
||||
doAssert(not result.isNil(), "processType not initialised.")
|
||||
proc initFromJson[T : object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: string) =
|
||||
assignObjectImpl(dst, jsonNode, jsonPath)
|
||||
|
||||
import options
|
||||
proc workaroundMacroNone[T](): Option[T] =
|
||||
none(T)
|
||||
proc to*[T](node: JsonNode, t: typedesc[T]): T =
|
||||
## `Unmarshals`:idx: the specified node into the object type specified.
|
||||
##
|
||||
## Known limitations:
|
||||
##
|
||||
## * Heterogeneous arrays are not supported.
|
||||
## * Sets in object variants are not supported.
|
||||
## * Not nil annotations are not supported.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## let jsonNode = parseJson("""
|
||||
## {
|
||||
## "person": {
|
||||
## "name": "Nimmer",
|
||||
## "age": 21
|
||||
## },
|
||||
## "list": [1, 2, 3, 4]
|
||||
## }
|
||||
## """)
|
||||
##
|
||||
## type
|
||||
## Person = object
|
||||
## name: string
|
||||
## age: int
|
||||
##
|
||||
## Data = object
|
||||
## person: Person
|
||||
## list: seq[int]
|
||||
##
|
||||
## var data = to(jsonNode, Data)
|
||||
## doAssert data.person.name == "Nimmer"
|
||||
## doAssert data.person.age == 21
|
||||
## doAssert data.list == @[1, 2, 3, 4]
|
||||
|
||||
proc depth(n: NimNode, current = 0): int =
|
||||
result = 1
|
||||
for child in n:
|
||||
let d = 1 + child.depth(current + 1)
|
||||
if d > result:
|
||||
result = d
|
||||
|
||||
proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
|
||||
## Accepts a type description, i.e. "ref Type", "seq[Type]", "Type" etc.
|
||||
##
|
||||
## The ``jsonNode`` refers to the node variable that we are deserialising.
|
||||
##
|
||||
## Returns an object constructor node.
|
||||
# echo("--createConsuctor-- \n", treeRepr(typeSym))
|
||||
# echo()
|
||||
|
||||
if depth(jsonNode) > 150:
|
||||
error("The `to` macro does not support ref objects with cycles.", jsonNode)
|
||||
|
||||
case typeSym.kind
|
||||
of nnkBracketExpr:
|
||||
var bracketName = ($typeSym[0]).normalize
|
||||
case bracketName
|
||||
of "option":
|
||||
# TODO: Would be good to verify that this is Option[T] from
|
||||
# options module I suppose.
|
||||
let lenientJsonNode = transformJsonIndexer(jsonNode)
|
||||
|
||||
let optionGeneric = typeSym[1]
|
||||
let value = createConstructor(typeSym[1], jsonNode)
|
||||
let workaround = bindSym("workaroundMacroNone") # TODO: Nim Bug: This shouldn't be necessary.
|
||||
|
||||
result = quote do:
|
||||
(
|
||||
if `lenientJsonNode`.isNil or `jsonNode`.kind == JNull: `workaround`[`optionGeneric`]() else: some[`optionGeneric`](`value`)
|
||||
)
|
||||
of "table", "orderedtable":
|
||||
let tableKeyType = typeSym[1]
|
||||
if ($tableKeyType).cmpIgnoreStyle("string") != 0:
|
||||
error("JSON doesn't support keys of type " & $tableKeyType)
|
||||
let tableValueType = typeSym[2]
|
||||
|
||||
let forLoopKey = genSym(nskForVar, "key")
|
||||
let indexerNode = createJsonIndexer(jsonNode, forLoopKey)
|
||||
let constructorNode = createConstructor(tableValueType, indexerNode)
|
||||
|
||||
let tableInit =
|
||||
if bracketName == "table":
|
||||
bindSym("initTable")
|
||||
else:
|
||||
bindSym("initOrderedTable")
|
||||
|
||||
# Create a statement expression containing a for loop.
|
||||
result = quote do:
|
||||
(
|
||||
var map = `tableInit`[`tableKeyType`, `tableValueType`]();
|
||||
verifyJsonKind(`jsonNode`, {JObject}, astToStr(`jsonNode`));
|
||||
for `forLoopKey` in keys(`jsonNode`.fields): map[
|
||||
`forLoopKey`] = `constructorNode`;
|
||||
map
|
||||
)
|
||||
of "ref":
|
||||
# Ref type.
|
||||
var typeName = $typeSym[1]
|
||||
# Remove the `:ObjectType` suffix.
|
||||
if typeName.endsWith(":ObjectType"):
|
||||
typeName = typeName[0 .. ^12]
|
||||
|
||||
let obj = getType(typeSym[1])
|
||||
result = processType(newIdentNode(typeName), obj, jsonNode, true)
|
||||
of "range":
|
||||
let typeNode = typeSym
|
||||
# Deduce the base type from one of the endpoints
|
||||
let baseType = getType(typeNode[1])
|
||||
|
||||
result = createConstructor(baseType, jsonNode)
|
||||
of "seq":
|
||||
let seqT = typeSym[1]
|
||||
let forLoopI = genSym(nskForVar, "i")
|
||||
let indexerNode = createJsonIndexer(jsonNode, forLoopI)
|
||||
let constructorNode = createConstructor(detectDistinctType(seqT), indexerNode)
|
||||
|
||||
# Create a statement expression containing a for loop.
|
||||
result = quote do:
|
||||
(
|
||||
var list: `typeSym` = @[];
|
||||
verifyJsonKind(`jsonNode`, {JArray}, astToStr(`jsonNode`));
|
||||
for `forLoopI` in 0 ..< `jsonNode`.len: list.add(`constructorNode`);
|
||||
list
|
||||
)
|
||||
of "array":
|
||||
let arrayT = typeSym[2]
|
||||
let forLoopI = genSym(nskForVar, "i")
|
||||
let indexerNode = createJsonIndexer(jsonNode, forLoopI)
|
||||
let constructorNode = createConstructor(arrayT, indexerNode)
|
||||
|
||||
# Create a statement expression containing a for loop.
|
||||
result = quote do:
|
||||
(
|
||||
var list: `typeSym`;
|
||||
verifyJsonKind(`jsonNode`, {JArray}, astToStr(`jsonNode`));
|
||||
for `forLoopI` in 0 ..< `jsonNode`.len: list[
|
||||
`forLoopI`] = `constructorNode`;
|
||||
list
|
||||
)
|
||||
of "tuple":
|
||||
let typeNode = getTypeImpl(typeSym)
|
||||
result = createConstructor(typeNode, jsonNode)
|
||||
else:
|
||||
# Generic type or some `seq[T]` alias
|
||||
let obj = getType(typeSym)
|
||||
case obj.kind
|
||||
of nnkBracketExpr:
|
||||
# probably a `seq[T]` alias
|
||||
let typeNode = getTypeImpl(typeSym)
|
||||
result = createConstructor(typeNode, jsonNode)
|
||||
else:
|
||||
# generic type
|
||||
result = processType(typeSym, obj, jsonNode, false)
|
||||
of nnkSym:
|
||||
# Handle JsonNode.
|
||||
if ($typeSym).cmpIgnoreStyle("jsonnode") == 0:
|
||||
return jsonNode
|
||||
|
||||
# Handle all other types.
|
||||
let obj = getType(typeSym)
|
||||
let typeNode = getTypeImpl(typeSym)
|
||||
if typeNode.typeKind == ntyDistinct:
|
||||
result = createConstructor(typeNode, jsonNode)
|
||||
elif obj.kind == nnkBracketExpr:
|
||||
# When `Sym "Foo"` turns out to be a `ref object` or `tuple`
|
||||
result = createConstructor(obj, jsonNode)
|
||||
else:
|
||||
result = processType(typeSym, obj, jsonNode, false)
|
||||
of nnkTupleTy:
|
||||
result = processType(typeSym, typeSym, jsonNode, false)
|
||||
of nnkPar, nnkTupleConstr:
|
||||
# TODO: The fact that `jsonNode` here works to give a good line number
|
||||
# is weird. Specifying typeSym should work but doesn't.
|
||||
error("Use a named tuple instead of: " & $toStrLit(typeSym), jsonNode)
|
||||
of nnkDistinctTy:
|
||||
var baseType = typeSym
|
||||
# solve nested distinct types
|
||||
while baseType.typeKind == ntyDistinct:
|
||||
let impl = getTypeImpl(baseType[0])
|
||||
if impl.typeKind != ntyDistinct:
|
||||
baseType = baseType[0]
|
||||
break
|
||||
baseType = impl
|
||||
let ret = createConstructor(baseType, jsonNode)
|
||||
let typeInst = getTypeInst(typeSym)
|
||||
result = quote do:
|
||||
(
|
||||
`typeInst`(`ret`)
|
||||
)
|
||||
else:
|
||||
doAssert false, "Unable to create constructor for: " & $typeSym.kind
|
||||
|
||||
doAssert(not result.isNil(), "Constructor not initialised.")
|
||||
|
||||
proc postProcess(node: NimNode): NimNode
|
||||
proc postProcessValue(value: NimNode): NimNode =
|
||||
## Looks for object constructors and calls the ``postProcess`` procedure
|
||||
## on them. Otherwise it just returns the node as-is.
|
||||
case value.kind
|
||||
of nnkObjConstr:
|
||||
result = postProcess(value)
|
||||
else:
|
||||
result = value
|
||||
for i in 0 ..< len(result):
|
||||
result[i] = postProcessValue(result[i])
|
||||
|
||||
proc postProcessExprColonExpr(exprColonExpr, resIdent: NimNode): NimNode =
|
||||
## Transform each field mapping in the ExprColonExpr into a simple
|
||||
## field assignment. Special processing is performed if the field mapping
|
||||
## has an if statement.
|
||||
##
|
||||
## ..code-block::plain
|
||||
## field: (if true: 12) -> if true: `resIdent`.field = 12
|
||||
expectKind(exprColonExpr, nnkExprColonExpr)
|
||||
let fieldName = exprColonExpr[0]
|
||||
let fieldValue = exprColonExpr[1]
|
||||
case fieldValue.kind
|
||||
of nnkIfStmt:
|
||||
doAssert fieldValue.len == 1, "Cannot postProcess two ElifBranches."
|
||||
expectKind(fieldValue[0], nnkElifBranch)
|
||||
|
||||
let cond = fieldValue[0][0]
|
||||
let bodyValue = postProcessValue(fieldValue[0][1])
|
||||
doAssert(bodyValue.kind != nnkNilLit)
|
||||
result =
|
||||
quote do:
|
||||
if `cond`:
|
||||
`resIdent`.`fieldName` = `bodyValue`
|
||||
else:
|
||||
let fieldValue = postProcessValue(fieldValue)
|
||||
doAssert(fieldValue.kind != nnkNilLit)
|
||||
result =
|
||||
quote do:
|
||||
`resIdent`.`fieldName` = `fieldValue`
|
||||
|
||||
|
||||
proc postProcess(node: NimNode): NimNode =
|
||||
## The ``createConstructor`` proc creates a ObjConstr node which contains
|
||||
## if statements for fields that may not be assignable (due to an object
|
||||
## variant). Nim doesn't handle this, but may do in the future.
|
||||
##
|
||||
## For simplicity, we post process the object constructor into multiple
|
||||
## assignments.
|
||||
##
|
||||
## For example:
|
||||
##
|
||||
## ..code-block::plain
|
||||
## Object( (var res = Object();
|
||||
## field: if true: 12 -> if true: res.field = 12;
|
||||
## ) res)
|
||||
result = newNimNode(nnkStmtListExpr)
|
||||
|
||||
expectKind(node, nnkObjConstr)
|
||||
|
||||
# Create the type.
|
||||
# -> var res = Object()
|
||||
var resIdent = genSym(nskVar, "res")
|
||||
var resType = node[0]
|
||||
|
||||
var objConstr = newTree(nnkObjConstr, resType)
|
||||
result.add newVarStmt(resIdent, objConstr)
|
||||
|
||||
# Process each ExprColonExpr.
|
||||
for i in 1..<len(node):
|
||||
if node[i].kind == nnkExprEqExpr:
|
||||
objConstr.add newTree(nnkExprColonExpr, node[i][0], node[i][1])
|
||||
else:
|
||||
result.add postProcessExprColonExpr(node[i], resIdent)
|
||||
|
||||
# Return the `res` variable.
|
||||
result.add(resIdent)
|
||||
|
||||
|
||||
macro to*(node: JsonNode, T: typedesc): untyped =
|
||||
## `Unmarshals`:idx: the specified node into the object type specified.
|
||||
##
|
||||
## Known limitations:
|
||||
##
|
||||
## * Heterogeneous arrays are not supported.
|
||||
## * Sets in object variants are not supported.
|
||||
## * Not nil annotations are not supported.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## let jsonNode = parseJson("""
|
||||
## {
|
||||
## "person": {
|
||||
## "name": "Nimmer",
|
||||
## "age": 21
|
||||
## },
|
||||
## "list": [1, 2, 3, 4]
|
||||
## }
|
||||
## """)
|
||||
##
|
||||
## type
|
||||
## Person = object
|
||||
## name: string
|
||||
## age: int
|
||||
##
|
||||
## Data = object
|
||||
## person: Person
|
||||
## list: seq[int]
|
||||
##
|
||||
## var data = to(jsonNode, Data)
|
||||
## doAssert data.person.name == "Nimmer"
|
||||
## doAssert data.person.age == 21
|
||||
## doAssert data.list == @[1, 2, 3, 4]
|
||||
|
||||
let typeNode = getTypeImpl(T)
|
||||
expectKind(typeNode, nnkBracketExpr)
|
||||
doAssert(($typeNode[0]).normalize == "typedesc")
|
||||
|
||||
# Create `temp` variable to store the result in case the user calls this
|
||||
# on `parseJson` (see bug #6604).
|
||||
result = newNimNode(nnkStmtListExpr)
|
||||
let temp = genSym(nskLet, "temp")
|
||||
result.add quote do:
|
||||
let `temp` = `node`
|
||||
|
||||
let constructor = createConstructor(typeNode[1], temp)
|
||||
result.add(postProcessValue(constructor))
|
||||
|
||||
# echo(treeRepr(result))
|
||||
# echo(toStrLit(result))
|
||||
initFromJson(result, node, "")
|
||||
|
||||
when false:
|
||||
import os
|
||||
|
|
|
|||
|
|
@ -1143,6 +1143,15 @@ proc recv*(socket: Socket, data: var string, size: int, timeout = -1,
|
|||
flags = {SocketFlag.SafeDisconn}): int =
|
||||
## Higher-level version of ``recv``.
|
||||
##
|
||||
## Reads **up to** ``size`` bytes from ``socket`` into ``buf``.
|
||||
##
|
||||
## For buffered sockets this function will attempt to read all the requested
|
||||
## data. It will read this data in ``BufferSize`` chunks.
|
||||
##
|
||||
## For unbuffered sockets this function makes no effort to read
|
||||
## all the data requested. It will return as much data as the operating system
|
||||
## gives it.
|
||||
##
|
||||
## When 0 is returned the socket's connection has been closed.
|
||||
##
|
||||
## This function will throw an OSError exception when an error occurs. A value
|
||||
|
|
@ -1171,6 +1180,15 @@ proc recv*(socket: Socket, size: int, timeout = -1,
|
|||
flags = {SocketFlag.SafeDisconn}): string {.inline.} =
|
||||
## Higher-level version of ``recv`` which returns a string.
|
||||
##
|
||||
## Reads **up to** ``size`` bytes from ``socket`` into ``buf``.
|
||||
##
|
||||
## For buffered sockets this function will attempt to read all the requested
|
||||
## data. It will read this data in ``BufferSize`` chunks.
|
||||
##
|
||||
## For unbuffered sockets this function makes no effort to read
|
||||
## all the data requested. It will return as much data as the operating system
|
||||
## gives it.
|
||||
##
|
||||
## When ``""`` is returned the socket's connection has been closed.
|
||||
##
|
||||
## This function will throw an OSError exception when an error occurs.
|
||||
|
|
|
|||
171
lib/pure/os.nim
171
lib/pure/os.nim
|
|
@ -15,18 +15,18 @@
|
|||
## import os
|
||||
##
|
||||
## let myFile = "/path/to/my/file.nim"
|
||||
##
|
||||
##
|
||||
## let pathSplit = splitPath(myFile)
|
||||
## assert pathSplit.head == "/path/to/my"
|
||||
## assert pathSplit.tail == "file.nim"
|
||||
##
|
||||
##
|
||||
## assert parentDir(myFile) == "/path/to/my"
|
||||
##
|
||||
##
|
||||
## let fileSplit = splitFile(myFile)
|
||||
## assert fileSplit.dir == "/path/to/my"
|
||||
## assert fileSplit.name == "file"
|
||||
## assert fileSplit.ext == ".nim"
|
||||
##
|
||||
##
|
||||
## assert myFile.changeFileExt("c") == "/path/to/my/file.c"
|
||||
|
||||
##
|
||||
|
|
@ -232,11 +232,92 @@ proc splitPath*(path: string): tuple[head, tail: string] {.
|
|||
result.head = ""
|
||||
result.tail = path
|
||||
|
||||
proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1", raises: [].} =
|
||||
## Checks whether a given `path` is absolute.
|
||||
##
|
||||
## On Windows, network paths are considered absolute too.
|
||||
runnableExamples:
|
||||
assert not "".isAbsolute
|
||||
assert not ".".isAbsolute
|
||||
when defined(posix):
|
||||
assert "/".isAbsolute
|
||||
assert not "a/".isAbsolute
|
||||
assert "/a/".isAbsolute
|
||||
|
||||
if len(path) == 0: return false
|
||||
|
||||
when doslikeFileSystem:
|
||||
var len = len(path)
|
||||
result = (path[0] in {'/', '\\'}) or
|
||||
(len > 1 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':')
|
||||
elif defined(macos):
|
||||
# according to https://perldoc.perl.org/File/Spec/Mac.html `:a` is a relative path
|
||||
result = path[0] != ':'
|
||||
elif defined(RISCOS):
|
||||
result = path[0] == '$'
|
||||
elif defined(posix):
|
||||
result = path[0] == '/'
|
||||
|
||||
when FileSystemCaseSensitive:
|
||||
template `!=?`(a, b: char): bool = a != b
|
||||
else:
|
||||
template `!=?`(a, b: char): bool = toLowerAscii(a) != toLowerAscii(b)
|
||||
|
||||
when doslikeFileSystem:
|
||||
proc isAbsFromCurrentDrive(path: string): bool {.noSideEffect, raises: []} =
|
||||
## An absolute path from the root of the current drive (e.g. "\foo")
|
||||
path.len > 0 and
|
||||
(path[0] == AltSep or
|
||||
(path[0] == DirSep and
|
||||
(path.len == 1 or path[1] notin {DirSep, AltSep, ':'})))
|
||||
|
||||
proc isUNCPrefix(path: string): bool {.noSideEffect, raises: []} =
|
||||
path[0] == DirSep and path[1] == DirSep
|
||||
|
||||
proc sameRoot(path1, path2: string): bool {.noSideEffect, raises: []} =
|
||||
## Return true if path1 and path2 have a same root.
|
||||
##
|
||||
## Detail of windows path formats:
|
||||
## https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats
|
||||
|
||||
assert(isAbsolute(path1))
|
||||
assert(isAbsolute(path2))
|
||||
|
||||
let
|
||||
len1 = path1.len
|
||||
len2 = path2.len
|
||||
assert(len1 != 0 and len2 != 0)
|
||||
|
||||
if isAbsFromCurrentDrive(path1) and isAbsFromCurrentDrive(path2):
|
||||
return true
|
||||
elif len1 == 1 or len2 == 1:
|
||||
return false
|
||||
else:
|
||||
if path1[1] == ':' and path2[1] == ':':
|
||||
return path1[0].toLowerAscii() == path2[0].toLowerAscii()
|
||||
else:
|
||||
var
|
||||
p1, p2: PathIter
|
||||
pp1 = next(p1, path1)
|
||||
pp2 = next(p2, path2)
|
||||
if pp1[1] - pp1[0] == 1 and pp2[1] - pp2[0] == 1 and
|
||||
isUNCPrefix(path1) and isUNCPrefix(path2):
|
||||
#UNC
|
||||
var h = 0
|
||||
while p1.hasNext(path1) and p2.hasNext(path2) and h < 2:
|
||||
pp1 = next(p1, path1)
|
||||
pp2 = next(p2, path2)
|
||||
let diff = pp1[1] - pp1[0]
|
||||
if diff != pp2[1] - pp2[0]:
|
||||
return false
|
||||
for i in 0..diff:
|
||||
if path1[i + pp1[0]] !=? path2[i + pp2[0]]:
|
||||
return false
|
||||
inc h
|
||||
return h == 2
|
||||
else:
|
||||
return false
|
||||
|
||||
proc relativePath*(path, base: string; sep = DirSep): string {.
|
||||
noSideEffect, rtl, extern: "nos$1", raises: [].} =
|
||||
## Converts `path` to a path relative to `base`.
|
||||
|
|
@ -245,6 +326,10 @@ proc relativePath*(path, base: string; sep = DirSep): string {.
|
|||
## this can be useful to ensure the relative path only contains `'/'`
|
||||
## so that it can be used for URL constructions.
|
||||
##
|
||||
## On windows, if a root of `path` and a root of `base` are different,
|
||||
## returns `path` as is because it is impossible to make a relative path.
|
||||
## That means an absolute path can be returned.
|
||||
##
|
||||
## See also:
|
||||
## * `splitPath proc <#splitPath,string>`_
|
||||
## * `parentDir proc <#parentDir,string>`_
|
||||
|
|
@ -256,9 +341,13 @@ proc relativePath*(path, base: string; sep = DirSep): string {.
|
|||
assert relativePath("/Users/me/bar/z.nim", "/Users/me", '/') == "bar/z.nim"
|
||||
assert relativePath("", "/users/moo", '/') == ""
|
||||
|
||||
# Todo: If on Windows, path and base do not agree on the drive letter,
|
||||
# return `path` as is.
|
||||
if path.len == 0: return ""
|
||||
|
||||
when doslikeFileSystem:
|
||||
if isAbsolute(path) and isAbsolute(base):
|
||||
if not sameRoot(path, base):
|
||||
return path
|
||||
|
||||
var f, b: PathIter
|
||||
var ff = (0, -1)
|
||||
var bb = (0, -1) # (int, int)
|
||||
|
|
@ -645,32 +734,6 @@ proc cmpPaths*(pathA, pathB: string): int {.
|
|||
else:
|
||||
result = cmpIgnoreCase(a, b)
|
||||
|
||||
proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1".} =
|
||||
## Checks whether a given `path` is absolute.
|
||||
##
|
||||
## On Windows, network paths are considered absolute too.
|
||||
runnableExamples:
|
||||
assert not "".isAbsolute
|
||||
assert not ".".isAbsolute
|
||||
when defined(posix):
|
||||
assert "/".isAbsolute
|
||||
assert not "a/".isAbsolute
|
||||
assert "/a/".isAbsolute
|
||||
|
||||
if len(path) == 0: return false
|
||||
|
||||
when doslikeFileSystem:
|
||||
var len = len(path)
|
||||
result = (path[0] in {'/', '\\'}) or
|
||||
(len > 1 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':')
|
||||
elif defined(macos):
|
||||
# according to https://perldoc.perl.org/File/Spec/Mac.html `:a` is a relative path
|
||||
result = path[0] != ':'
|
||||
elif defined(RISCOS):
|
||||
result = path[0] == '$'
|
||||
elif defined(posix):
|
||||
result = path[0] == '/'
|
||||
|
||||
proc unixToNativePath*(path: string, drive=""): string {.
|
||||
noSideEffect, rtl, extern: "nos$1".} =
|
||||
## Converts an UNIX-like path to a native one.
|
||||
|
|
@ -2658,6 +2721,47 @@ when not weirdTarget and (defined(linux) or defined(solaris) or defined(bsd) or
|
|||
len = readlink(procPath, result, len)
|
||||
setLen(result, len)
|
||||
|
||||
when defined(openbsd):
|
||||
proc isExecutable(path: string): bool =
|
||||
let p = getFilePermissions(path)
|
||||
result = fpUserExec in p and fpGroupExec in p and fpOthersExec in p
|
||||
|
||||
proc getApplOpenBsd(): string =
|
||||
# similar to getApplHeuristic, but checks current working directory
|
||||
when declared(paramStr):
|
||||
result = ""
|
||||
|
||||
# POSIX guaranties that this contains the executable
|
||||
# as it has been executed by the calling process
|
||||
let exePath = string(paramStr(0))
|
||||
|
||||
if len(exePath) == 0:
|
||||
return ""
|
||||
|
||||
if exePath[0] == DirSep:
|
||||
# path is absolute
|
||||
result = exePath
|
||||
else:
|
||||
# not an absolute path, check if it's relative to the current working directory
|
||||
for i in 1..<len(exePath):
|
||||
if exePath[i] == DirSep:
|
||||
result = joinPath(getCurrentDir(), exePath)
|
||||
break
|
||||
|
||||
if len(result) > 0:
|
||||
if isExecutable(result):
|
||||
return expandFilename(result)
|
||||
|
||||
return ""
|
||||
|
||||
# search in path
|
||||
for p in split(string(getEnv("PATH")), {PathSep}):
|
||||
var x = joinPath(p, exePath)
|
||||
if existsFile(x) and isExecutable(x):
|
||||
return expandFilename(x)
|
||||
else:
|
||||
result = ""
|
||||
|
||||
when not (defined(windows) or defined(macosx) or weirdTarget):
|
||||
proc getApplHeuristic(): string =
|
||||
when declared(paramStr):
|
||||
|
|
@ -2761,6 +2865,9 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noN
|
|||
result = getApplFreebsd()
|
||||
elif defined(haiku):
|
||||
result = getApplHaiku()
|
||||
elif defined(openbsd):
|
||||
result = getApplOpenBsd()
|
||||
|
||||
# little heuristic that may work on other POSIX-like systems:
|
||||
if result.len == 0:
|
||||
result = getApplHeuristic()
|
||||
|
|
|
|||
|
|
@ -561,7 +561,7 @@ proc writeConfig*(dict: Config, filename: string) =
|
|||
dict.writeConfig(fileStream)
|
||||
|
||||
proc getSectionValue*(dict: Config, section, key: string): string =
|
||||
## Gets the Key value of the specified Section.
|
||||
## Gets the Key value of the specified Section, returns an empty string if the key does not exist.
|
||||
if dict.hasKey(section):
|
||||
if dict[section].hasKey(key):
|
||||
result = dict[section][key]
|
||||
|
|
|
|||
|
|
@ -84,11 +84,11 @@ include "system/inclrtl"
|
|||
{.push debugger: off.}
|
||||
|
||||
when defined(JS):
|
||||
type ui = uint32
|
||||
type Ui = uint32
|
||||
|
||||
const randMax = 4_294_967_295u32
|
||||
else:
|
||||
type ui = uint64
|
||||
type Ui = uint64
|
||||
|
||||
const randMax = 18_446_744_073_709_551_615u64
|
||||
|
||||
|
|
@ -106,7 +106,7 @@ type
|
|||
## Many procs have two variations: one that takes in a Rand parameter and
|
||||
## another that uses the default generator. The procs that use the default
|
||||
## generator are **not** thread-safe!
|
||||
a0, a1: ui
|
||||
a0, a1: Ui
|
||||
|
||||
when defined(JS):
|
||||
var state = Rand(
|
||||
|
|
@ -118,8 +118,8 @@ else:
|
|||
a0: 0x69B4C98CB8530805u64,
|
||||
a1: 0xFED1DD3004688D67CAu64) # global for backwards compatibility
|
||||
|
||||
proc rotl(x, k: ui): ui =
|
||||
result = (x shl k) or (x shr (ui(64) - k))
|
||||
proc rotl(x, k: Ui): Ui =
|
||||
result = (x shl k) or (x shr (Ui(64) - k))
|
||||
|
||||
proc next*(r: var Rand): uint64 =
|
||||
## Computes a random ``uint64`` number using the given state.
|
||||
|
|
@ -195,11 +195,11 @@ proc skipRandomNumbers*(s: var Rand) =
|
|||
else:
|
||||
const helper = [0xbeac0467eba5facbu64, 0xd86b048b86aa9922u64]
|
||||
var
|
||||
s0 = ui 0
|
||||
s1 = ui 0
|
||||
s0 = Ui 0
|
||||
s1 = Ui 0
|
||||
for i in 0..high(helper):
|
||||
for b in 0 ..< 64:
|
||||
if (helper[i] and (ui(1) shl ui(b))) != 0:
|
||||
if (helper[i] and (Ui(1) shl Ui(b))) != 0:
|
||||
s0 = s0 xor s.a0
|
||||
s1 = s1 xor s.a1
|
||||
discard next(s)
|
||||
|
|
@ -210,7 +210,7 @@ proc random*(max: int): int {.benign, deprecated:
|
|||
"Deprecated since v0.18.0; use 'rand' instead".} =
|
||||
while true:
|
||||
let x = next(state)
|
||||
if x < randMax - (randMax mod ui(max)):
|
||||
if x < randMax - (randMax mod Ui(max)):
|
||||
return int(x mod uint64(max))
|
||||
|
||||
proc random*(max: float): float {.benign, deprecated:
|
||||
|
|
@ -247,7 +247,7 @@ proc rand*(r: var Rand; max: Natural): int {.benign.} =
|
|||
if max == 0: return
|
||||
while true:
|
||||
let x = next(r)
|
||||
if x <= randMax - (randMax mod ui(max)):
|
||||
if x <= randMax - (randMax mod Ui(max)):
|
||||
return int(x mod (uint64(max)+1u64))
|
||||
|
||||
proc rand*(max: int): int {.benign.} =
|
||||
|
|
@ -570,8 +570,8 @@ proc initRand*(seed: int64): Rand =
|
|||
let now = getTime()
|
||||
var r2 = initRand(now.toUnix * 1_000_000_000 + now.nanosecond)
|
||||
doAssert seed != 0 # 0 causes `rand(int)` to always return 0 for example.
|
||||
result.a0 = ui(seed shr 16)
|
||||
result.a1 = ui(seed and 0xffff)
|
||||
result.a0 = Ui(seed shr 16)
|
||||
result.a1 = Ui(seed and 0xffff)
|
||||
discard next(result)
|
||||
|
||||
proc randomize*(seed: int64) {.benign.} =
|
||||
|
|
@ -642,7 +642,7 @@ when not defined(nimscript):
|
|||
## * `randomize proc<#randomize,int64>`_ that accepts a seed
|
||||
## * `initRand proc<#initRand,int64>`_
|
||||
when defined(JS):
|
||||
let time = int64(times.epochTime() * 1000)
|
||||
let time = int64(times.epochTime() * 1000) and 0x7fff_ffff
|
||||
randomize(time)
|
||||
else:
|
||||
let now = times.getTime()
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ One very nice advantage over regular expressions is that ``scanf`` is
|
|||
extensible with ordinary Nim procs. The proc is either enclosed in ``${}``
|
||||
or in ``$[]``. ``${}`` matches and binds the result
|
||||
to a variable (that was passed to the ``scanf`` macro) while ``$[]`` merely
|
||||
optional tokens.
|
||||
matches optional tokens without any result binding.
|
||||
|
||||
|
||||
In this example, we define a helper proc ``someSep`` that skips some separators
|
||||
|
|
|
|||
|
|
@ -180,7 +180,6 @@ macro `[]`*(lc: ListComprehension, comp, typ: untyped): untyped {.deprecated.} =
|
|||
newNimNode(nnkBracket))),
|
||||
result))))
|
||||
|
||||
|
||||
macro dump*(x: typed): untyped =
|
||||
## Dumps the content of an expression, useful for debugging.
|
||||
## It accepts any expression and prints a textual representation
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ proc delOutputFormatter*(formatter: OutputFormatter) =
|
|||
keepIf(formatters, proc (x: OutputFormatter): bool =
|
||||
x != formatter)
|
||||
|
||||
proc newConsoleOutputFormatter*(outputLevel: OutputLevel = PRINT_ALL,
|
||||
proc newConsoleOutputFormatter*(outputLevel: OutputLevel = OutputLevel.PRINT_ALL,
|
||||
colorOutput = true): <//>ConsoleOutputFormatter =
|
||||
ConsoleOutputFormatter(
|
||||
outputLevel: outputLevel,
|
||||
|
|
@ -207,7 +207,7 @@ proc defaultConsoleFormatter*(): <//>ConsoleOutputFormatter =
|
|||
colorOutput = true
|
||||
elif existsEnv("NIMTEST_NO_COLOR"):
|
||||
colorOutput = false
|
||||
var outputLevel = PRINT_ALL
|
||||
var outputLevel = OutputLevel.PRINT_ALL
|
||||
if envOutLvl.len > 0:
|
||||
for opt in countup(low(OutputLevel), high(OutputLevel)):
|
||||
if $opt == envOutLvl:
|
||||
|
|
@ -240,17 +240,17 @@ method failureOccurred*(formatter: ConsoleOutputFormatter,
|
|||
method testEnded*(formatter: ConsoleOutputFormatter, testResult: TestResult) =
|
||||
formatter.isInTest = false
|
||||
|
||||
if formatter.outputLevel != PRINT_NONE and
|
||||
(formatter.outputLevel == PRINT_ALL or testResult.status == FAILED):
|
||||
if formatter.outputLevel != OutputLevel.PRINT_NONE and
|
||||
(formatter.outputLevel == OutputLevel.PRINT_ALL or testResult.status == TestStatus.FAILED):
|
||||
let prefix = if testResult.suiteName.len > 0: " " else: ""
|
||||
template rawPrint() = echo(prefix, "[", $testResult.status, "] ",
|
||||
testResult.testName)
|
||||
when not defined(ECMAScript):
|
||||
if formatter.colorOutput and not defined(ECMAScript):
|
||||
var color = case testResult.status
|
||||
of OK: fgGreen
|
||||
of FAILED: fgRed
|
||||
of SKIPPED: fgYellow
|
||||
of TestStatus.OK: fgGreen
|
||||
of TestStatus.FAILED: fgRed
|
||||
of TestStatus.SKIPPED: fgYellow
|
||||
styledEcho styleBright, color, prefix, "[", $testResult.status, "] ",
|
||||
resetStyle, testResult.testName
|
||||
else:
|
||||
|
|
@ -318,11 +318,11 @@ method testEnded*(formatter: JUnitOutputFormatter, testResult: TestResult) =
|
|||
formatter.stream.writeLine("\t\t<testcase name=\"$#\" time=\"$#\">" % [
|
||||
xmlEscape(testResult.testName), timeStr])
|
||||
case testResult.status
|
||||
of OK:
|
||||
of TestStatus.OK:
|
||||
discard
|
||||
of SKIPPED:
|
||||
of TestStatus.SKIPPED:
|
||||
formatter.stream.writeLine("<skipped />")
|
||||
of FAILED:
|
||||
of TestStatus.FAILED:
|
||||
let failureMsg = if formatter.testStackTrace.len > 0 and
|
||||
formatter.testErrors.len > 0:
|
||||
xmlEscape(formatter.testErrors[^1])
|
||||
|
|
@ -498,7 +498,7 @@ template test*(name, body) {.dirty.} =
|
|||
|
||||
if shouldRun(when declared(testSuiteName): testSuiteName else: "", name):
|
||||
checkpoints = @[]
|
||||
var testStatusIMPL {.inject.} = OK
|
||||
var testStatusIMPL {.inject.} = TestStatus.OK
|
||||
|
||||
for formatter in formatters:
|
||||
formatter.testStarted(name)
|
||||
|
|
@ -518,7 +518,7 @@ template test*(name, body) {.dirty.} =
|
|||
fail()
|
||||
|
||||
finally:
|
||||
if testStatusIMPL == FAILED:
|
||||
if testStatusIMPL == TestStatus.FAILED:
|
||||
programResult = 1
|
||||
let testResult = TestResult(
|
||||
suiteName: when declared(testSuiteName): testSuiteName else: "",
|
||||
|
|
@ -558,7 +558,7 @@ template fail* =
|
|||
bind ensureInitialized
|
||||
|
||||
when declared(testStatusIMPL):
|
||||
testStatusIMPL = FAILED
|
||||
testStatusIMPL = TestStatus.FAILED
|
||||
else:
|
||||
programResult = 1
|
||||
|
||||
|
|
@ -589,7 +589,7 @@ template skip* =
|
|||
## skip()
|
||||
bind checkpoints
|
||||
|
||||
testStatusIMPL = SKIPPED
|
||||
testStatusIMPL = TestStatus.SKIPPED
|
||||
checkpoints = @[]
|
||||
|
||||
macro check*(conditions: untyped): untyped =
|
||||
|
|
|
|||
207
lib/system.nim
207
lib/system.nim
|
|
@ -671,7 +671,7 @@ include "system/inclrtl"
|
|||
const NoFakeVars* = defined(nimscript) ## `true` if the backend doesn't support \
|
||||
## "fake variables" like `var EBADF {.importc.}: cint`.
|
||||
|
||||
when not defined(JS) and not defined(gcDestructors):
|
||||
when not defined(JS) and not defined(nimSeqsV2):
|
||||
type
|
||||
TGenericSeq {.compilerproc, pure, inheritable.} = object
|
||||
len, reserved: int
|
||||
|
|
@ -684,7 +684,7 @@ when not defined(JS) and not defined(gcDestructors):
|
|||
NimString = ptr NimStringDesc
|
||||
|
||||
when not defined(JS) and not defined(nimscript):
|
||||
when not defined(gcDestructors):
|
||||
when not defined(nimSeqsV2):
|
||||
template space(s: PGenericSeq): int {.dirty.} =
|
||||
s.reserved and not (seqShallowFlag or strlitFlag)
|
||||
when not defined(nimV2):
|
||||
|
|
@ -1020,7 +1020,7 @@ when not defined(JS):
|
|||
## assert len(x) == 3
|
||||
## x[0] = 10
|
||||
result = newSeqOfCap[T](len)
|
||||
when defined(gcDestructors):
|
||||
when defined(nimSeqsV2):
|
||||
cast[ptr int](addr result)[] = len
|
||||
else:
|
||||
var s = cast[PGenericSeq](result)
|
||||
|
|
@ -1323,65 +1323,51 @@ proc `mod`*(x, y: int16): int16 {.magic: "ModI", noSideEffect.}
|
|||
proc `mod`*(x, y: int32): int32 {.magic: "ModI", noSideEffect.}
|
||||
proc `mod`*(x, y: int64): int64 {.magic: "ModI", noSideEffect.}
|
||||
|
||||
when defined(nimNewShiftOps):
|
||||
|
||||
when defined(nimOldShiftRight) or not defined(nimAshr):
|
||||
const shrDepMessage = "`shr` will become sign preserving."
|
||||
proc `shr`*(x: int, y: SomeInteger): int {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
|
||||
proc `shr`*(x: int8, y: SomeInteger): int8 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
|
||||
proc `shr`*(x: int16, y: SomeInteger): int16 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
|
||||
proc `shr`*(x: int32, y: SomeInteger): int32 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
|
||||
proc `shr`*(x: int64, y: SomeInteger): int64 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
|
||||
else:
|
||||
proc `shr`*(x: int, y: SomeInteger): int {.magic: "AshrI", noSideEffect.}
|
||||
## Computes the `shift right` operation of `x` and `y`, filling
|
||||
## vacant bit positions with the sign bit.
|
||||
##
|
||||
## **Note**: `Operator precedence <manual.html#syntax-precedence>`_
|
||||
## is different than in *C*.
|
||||
##
|
||||
## See also:
|
||||
## * `ashr proc <#ashr,int,SomeInteger>`_ for arithmetic shift right
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## 0b0001_0000'i8 shr 2 == 0b0000_0100'i8
|
||||
## 0b0000_0001'i8 shr 1 == 0b0000_0000'i8
|
||||
## 0b1000_0000'i8 shr 4 == 0b1111_1000'i8
|
||||
## -1 shr 5 == -1
|
||||
## 1 shr 5 == 0
|
||||
## 16 shr 2 == 4
|
||||
## -16 shr 2 == -4
|
||||
proc `shr`*(x: int8, y: SomeInteger): int8 {.magic: "AshrI", noSideEffect.}
|
||||
proc `shr`*(x: int16, y: SomeInteger): int16 {.magic: "AshrI", noSideEffect.}
|
||||
proc `shr`*(x: int32, y: SomeInteger): int32 {.magic: "AshrI", noSideEffect.}
|
||||
proc `shr`*(x: int64, y: SomeInteger): int64 {.magic: "AshrI", noSideEffect.}
|
||||
|
||||
|
||||
proc `shl`*(x: int, y: SomeInteger): int {.magic: "ShlI", noSideEffect.}
|
||||
## Computes the `shift left` operation of `x` and `y`.
|
||||
when defined(nimOldShiftRight) or not defined(nimAshr):
|
||||
const shrDepMessage = "`shr` will become sign preserving."
|
||||
proc `shr`*(x: int, y: SomeInteger): int {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
|
||||
proc `shr`*(x: int8, y: SomeInteger): int8 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
|
||||
proc `shr`*(x: int16, y: SomeInteger): int16 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
|
||||
proc `shr`*(x: int32, y: SomeInteger): int32 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
|
||||
proc `shr`*(x: int64, y: SomeInteger): int64 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
|
||||
else:
|
||||
proc `shr`*(x: int, y: SomeInteger): int {.magic: "AshrI", noSideEffect.}
|
||||
## Computes the `shift right` operation of `x` and `y`, filling
|
||||
## vacant bit positions with the sign bit.
|
||||
##
|
||||
## **Note**: `Operator precedence <manual.html#syntax-precedence>`_
|
||||
## is different than in *C*.
|
||||
##
|
||||
## See also:
|
||||
## * `ashr proc <#ashr,int,SomeInteger>`_ for arithmetic shift right
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## 1'i32 shl 4 == 0x0000_0010
|
||||
## 1'i64 shl 4 == 0x0000_0000_0000_0010
|
||||
proc `shl`*(x: int8, y: SomeInteger): int8 {.magic: "ShlI", noSideEffect.}
|
||||
proc `shl`*(x: int16, y: SomeInteger): int16 {.magic: "ShlI", noSideEffect.}
|
||||
proc `shl`*(x: int32, y: SomeInteger): int32 {.magic: "ShlI", noSideEffect.}
|
||||
proc `shl`*(x: int64, y: SomeInteger): int64 {.magic: "ShlI", noSideEffect.}
|
||||
else:
|
||||
proc `shr`*(x, y: int): int {.magic: "ShrI", noSideEffect.}
|
||||
proc `shr`*(x, y: int8): int8 {.magic: "ShrI", noSideEffect.}
|
||||
proc `shr`*(x, y: int16): int16 {.magic: "ShrI", noSideEffect.}
|
||||
proc `shr`*(x, y: int32): int32 {.magic: "ShrI", noSideEffect.}
|
||||
proc `shr`*(x, y: int64): int64 {.magic: "ShrI", noSideEffect.}
|
||||
## 0b0001_0000'i8 shr 2 == 0b0000_0100'i8
|
||||
## 0b0000_0001'i8 shr 1 == 0b0000_0000'i8
|
||||
## 0b1000_0000'i8 shr 4 == 0b1111_1000'i8
|
||||
## -1 shr 5 == -1
|
||||
## 1 shr 5 == 0
|
||||
## 16 shr 2 == 4
|
||||
## -16 shr 2 == -4
|
||||
proc `shr`*(x: int8, y: SomeInteger): int8 {.magic: "AshrI", noSideEffect.}
|
||||
proc `shr`*(x: int16, y: SomeInteger): int16 {.magic: "AshrI", noSideEffect.}
|
||||
proc `shr`*(x: int32, y: SomeInteger): int32 {.magic: "AshrI", noSideEffect.}
|
||||
proc `shr`*(x: int64, y: SomeInteger): int64 {.magic: "AshrI", noSideEffect.}
|
||||
|
||||
proc `shl`*(x, y: int): int {.magic: "ShlI", noSideEffect.}
|
||||
proc `shl`*(x, y: int8): int8 {.magic: "ShlI", noSideEffect.}
|
||||
proc `shl`*(x, y: int16): int16 {.magic: "ShlI", noSideEffect.}
|
||||
proc `shl`*(x, y: int32): int32 {.magic: "ShlI", noSideEffect.}
|
||||
proc `shl`*(x, y: int64): int64 {.magic: "ShlI", noSideEffect.}
|
||||
|
||||
proc `shl`*(x: int, y: SomeInteger): int {.magic: "ShlI", noSideEffect.}
|
||||
## Computes the `shift left` operation of `x` and `y`.
|
||||
##
|
||||
## **Note**: `Operator precedence <manual.html#syntax-precedence>`_
|
||||
## is different than in *C*.
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## 1'i32 shl 4 == 0x0000_0010
|
||||
## 1'i64 shl 4 == 0x0000_0000_0000_0010
|
||||
proc `shl`*(x: int8, y: SomeInteger): int8 {.magic: "ShlI", noSideEffect.}
|
||||
proc `shl`*(x: int16, y: SomeInteger): int16 {.magic: "ShlI", noSideEffect.}
|
||||
proc `shl`*(x: int32, y: SomeInteger): int32 {.magic: "ShlI", noSideEffect.}
|
||||
proc `shl`*(x: int64, y: SomeInteger): int64 {.magic: "ShlI", noSideEffect.}
|
||||
|
||||
when defined(nimAshr):
|
||||
proc ashr*(x: int, y: SomeInteger): int {.magic: "AshrI", noSideEffect.}
|
||||
|
|
@ -1518,54 +1504,105 @@ template `>%`*(x, y: untyped): untyped = y <% x
|
|||
|
||||
|
||||
# unsigned integer operations:
|
||||
proc `not`*[T: SomeUnsignedInt](x: T): T {.magic: "BitnotI", noSideEffect.}
|
||||
proc `not`*(x: uint): uint {.magic: "BitnotI", noSideEffect.}
|
||||
## Computes the `bitwise complement` of the integer `x`.
|
||||
proc `not`*(x: uint8): uint8 {.magic: "BitnotI", noSideEffect.}
|
||||
proc `not`*(x: uint16): uint16 {.magic: "BitnotI", noSideEffect.}
|
||||
proc `not`*(x: uint32): uint32 {.magic: "BitnotI", noSideEffect.}
|
||||
proc `not`*(x: uint64): uint64 {.magic: "BitnotI", noSideEffect.}
|
||||
|
||||
when defined(nimNewShiftOps):
|
||||
proc `shr`*[T: SomeUnsignedInt](x: T, y: SomeInteger): T {.magic: "ShrI", noSideEffect.}
|
||||
## Computes the `shift right` operation of `x` and `y`.
|
||||
proc `shl`*[T: SomeUnsignedInt](x: T, y: SomeInteger): T {.magic: "ShlI", noSideEffect.}
|
||||
## Computes the `shift left` operation of `x` and `y`.
|
||||
else:
|
||||
proc `shr`*[T: SomeUnsignedInt](x, y: T): T {.magic: "ShrI", noSideEffect.}
|
||||
## Computes the `shift right` operation of `x` and `y`.
|
||||
proc `shl`*[T: SomeUnsignedInt](x, y: T): T {.magic: "ShlI", noSideEffect.}
|
||||
## Computes the `shift left` operation of `x` and `y`.
|
||||
proc `shr`*(x: uint, y: SomeInteger): uint {.magic: "ShrI", noSideEffect.}
|
||||
## Computes the `shift right` operation of `x` and `y`.
|
||||
proc `shr`*(x: uint8, y: SomeInteger): uint8 {.magic: "ShrI", noSideEffect.}
|
||||
proc `shr`*(x: uint16, y: SomeInteger): uint16 {.magic: "ShrI", noSideEffect.}
|
||||
proc `shr`*(x: uint32, y: SomeInteger): uint32 {.magic: "ShrI", noSideEffect.}
|
||||
proc `shr`*(x: uint64, y: SomeInteger): uint64 {.magic: "ShrI", noSideEffect.}
|
||||
|
||||
proc `and`*[T: SomeUnsignedInt](x, y: T): T {.magic: "BitandI", noSideEffect.}
|
||||
proc `shl`*(x: uint, y: SomeInteger): uint {.magic: "ShlI", noSideEffect.}
|
||||
## Computes the `shift left` operation of `x` and `y`.
|
||||
proc `shl`*(x: uint8, y: SomeInteger): uint8 {.magic: "ShlI", noSideEffect.}
|
||||
proc `shl`*(x: uint16, y: SomeInteger): uint16 {.magic: "ShlI", noSideEffect.}
|
||||
proc `shl`*(x: uint32, y: SomeInteger): uint32 {.magic: "ShlI", noSideEffect.}
|
||||
proc `shl`*(x: uint64, y: SomeInteger): uint64 {.magic: "ShlI", noSideEffect.}
|
||||
|
||||
proc `and`*(x, y: uint): uint {.magic: "BitandI", noSideEffect.}
|
||||
## Computes the `bitwise and` of numbers `x` and `y`.
|
||||
proc `and`*(x, y: uint8): uint8 {.magic: "BitandI", noSideEffect.}
|
||||
proc `and`*(x, y: uint16): uint16 {.magic: "BitandI", noSideEffect.}
|
||||
proc `and`*(x, y: uint32): uint32 {.magic: "BitandI", noSideEffect.}
|
||||
proc `and`*(x, y: uint64): uint64 {.magic: "BitandI", noSideEffect.}
|
||||
|
||||
proc `or`*[T: SomeUnsignedInt](x, y: T): T {.magic: "BitorI", noSideEffect.}
|
||||
proc `or`*(x, y: uint): uint {.magic: "BitorI", noSideEffect.}
|
||||
## Computes the `bitwise or` of numbers `x` and `y`.
|
||||
proc `or`*(x, y: uint8): uint8 {.magic: "BitorI", noSideEffect.}
|
||||
proc `or`*(x, y: uint16): uint16 {.magic: "BitorI", noSideEffect.}
|
||||
proc `or`*(x, y: uint32): uint32 {.magic: "BitorI", noSideEffect.}
|
||||
proc `or`*(x, y: uint64): uint64 {.magic: "BitorI", noSideEffect.}
|
||||
|
||||
proc `xor`*[T: SomeUnsignedInt](x, y: T): T {.magic: "BitxorI", noSideEffect.}
|
||||
proc `xor`*(x, y: uint): uint {.magic: "BitxorI", noSideEffect.}
|
||||
## Computes the `bitwise xor` of numbers `x` and `y`.
|
||||
proc `xor`*(x, y: uint8): uint8 {.magic: "BitxorI", noSideEffect.}
|
||||
proc `xor`*(x, y: uint16): uint16 {.magic: "BitxorI", noSideEffect.}
|
||||
proc `xor`*(x, y: uint32): uint32 {.magic: "BitxorI", noSideEffect.}
|
||||
proc `xor`*(x, y: uint64): uint64 {.magic: "BitxorI", noSideEffect.}
|
||||
|
||||
proc `==`*[T: SomeUnsignedInt](x, y: T): bool {.magic: "EqI", noSideEffect.}
|
||||
proc `==`*(x, y: uint): bool {.magic: "EqI", noSideEffect.}
|
||||
## Compares two unsigned integers for equality.
|
||||
proc `==`*(x, y: uint8): bool {.magic: "EqI", noSideEffect.}
|
||||
proc `==`*(x, y: uint16): bool {.magic: "EqI", noSideEffect.}
|
||||
proc `==`*(x, y: uint32): bool {.magic: "EqI", noSideEffect.}
|
||||
proc `==`*(x, y: uint64): bool {.magic: "EqI", noSideEffect.}
|
||||
|
||||
proc `+`*[T: SomeUnsignedInt](x, y: T): T {.magic: "AddU", noSideEffect.}
|
||||
proc `+`*(x, y: uint): uint {.magic: "AddU", noSideEffect.}
|
||||
## Binary `+` operator for unsigned integers.
|
||||
proc `+`*(x, y: uint8): uint8 {.magic: "AddU", noSideEffect.}
|
||||
proc `+`*(x, y: uint16): uint16 {.magic: "AddU", noSideEffect.}
|
||||
proc `+`*(x, y: uint32): uint32 {.magic: "AddU", noSideEffect.}
|
||||
proc `+`*(x, y: uint64): uint64 {.magic: "AddU", noSideEffect.}
|
||||
|
||||
proc `-`*[T: SomeUnsignedInt](x, y: T): T {.magic: "SubU", noSideEffect.}
|
||||
proc `-`*(x, y: uint): uint {.magic: "SubU", noSideEffect.}
|
||||
## Binary `-` operator for unsigned integers.
|
||||
proc `-`*(x, y: uint8): uint8 {.magic: "SubU", noSideEffect.}
|
||||
proc `-`*(x, y: uint16): uint16 {.magic: "SubU", noSideEffect.}
|
||||
proc `-`*(x, y: uint32): uint32 {.magic: "SubU", noSideEffect.}
|
||||
proc `-`*(x, y: uint64): uint64 {.magic: "SubU", noSideEffect.}
|
||||
|
||||
proc `*`*[T: SomeUnsignedInt](x, y: T): T {.magic: "MulU", noSideEffect.}
|
||||
proc `*`*(x, y: uint): uint {.magic: "MulU", noSideEffect.}
|
||||
## Binary `*` operator for unsigned integers.
|
||||
proc `*`*(x, y: uint8): uint8 {.magic: "MulU", noSideEffect.}
|
||||
proc `*`*(x, y: uint16): uint16 {.magic: "MulU", noSideEffect.}
|
||||
proc `*`*(x, y: uint32): uint32 {.magic: "MulU", noSideEffect.}
|
||||
proc `*`*(x, y: uint64): uint64 {.magic: "MulU", noSideEffect.}
|
||||
|
||||
proc `div`*[T: SomeUnsignedInt](x, y: T): T {.magic: "DivU", noSideEffect.}
|
||||
proc `div`*(x, y: uint): uint {.magic: "DivU", noSideEffect.}
|
||||
## Computes the integer division for unsigned integers.
|
||||
## This is roughly the same as ``trunc(x/y)``.
|
||||
proc `div`*(x, y: uint8): uint8 {.magic: "DivU", noSideEffect.}
|
||||
proc `div`*(x, y: uint16): uint16 {.magic: "DivU", noSideEffect.}
|
||||
proc `div`*(x, y: uint32): uint32 {.magic: "DivU", noSideEffect.}
|
||||
proc `div`*(x, y: uint64): uint64 {.magic: "DivU", noSideEffect.}
|
||||
|
||||
proc `mod`*[T: SomeUnsignedInt](x, y: T): T {.magic: "ModU", noSideEffect.}
|
||||
proc `mod`*(x, y: uint): uint {.magic: "ModU", noSideEffect.}
|
||||
## Computes the integer modulo operation (remainder) for unsigned integers.
|
||||
## This is the same as ``x - (x div y) * y``.
|
||||
proc `mod`*(x, y: uint8): uint8 {.magic: "ModU", noSideEffect.}
|
||||
proc `mod`*(x, y: uint16): uint16 {.magic: "ModU", noSideEffect.}
|
||||
proc `mod`*(x, y: uint32): uint32 {.magic: "ModU", noSideEffect.}
|
||||
proc `mod`*(x, y: uint64): uint64 {.magic: "ModU", noSideEffect.}
|
||||
|
||||
proc `<=`*[T: SomeUnsignedInt](x, y: T): bool {.magic: "LeU", noSideEffect.}
|
||||
proc `<=`*(x, y: uint): bool {.magic: "LeU", noSideEffect.}
|
||||
## Returns true if ``x <= y``.
|
||||
proc `<=`*(x, y: uint8): bool {.magic: "LeU", noSideEffect.}
|
||||
proc `<=`*(x, y: uint16): bool {.magic: "LeU", noSideEffect.}
|
||||
proc `<=`*(x, y: uint32): bool {.magic: "LeU", noSideEffect.}
|
||||
proc `<=`*(x, y: uint64): bool {.magic: "LeU", noSideEffect.}
|
||||
|
||||
proc `<`*[T: SomeUnsignedInt](x, y: T): bool {.magic: "LtU", noSideEffect.}
|
||||
proc `<`*(x, y: uint): bool {.magic: "LtU", noSideEffect.}
|
||||
## Returns true if ``unsigned(x) < unsigned(y)``.
|
||||
proc `<`*(x, y: uint8): bool {.magic: "LtU", noSideEffect.}
|
||||
proc `<`*(x, y: uint16): bool {.magic: "LtU", noSideEffect.}
|
||||
proc `<`*(x, y: uint32): bool {.magic: "LtU", noSideEffect.}
|
||||
proc `<`*(x, y: uint64): bool {.magic: "LtU", noSideEffect.}
|
||||
|
||||
# floating point operations:
|
||||
proc `+`*(x: float32): float32 {.magic: "UnaryPlusF64", noSideEffect.}
|
||||
|
|
@ -2074,10 +2111,10 @@ const hasAlloc = (hostOS != "standalone" or not defined(nogc)) and not defined(n
|
|||
|
||||
when not defined(JS) and not defined(nimscript) and hostOS != "standalone":
|
||||
include "system/cgprocs"
|
||||
when not defined(JS) and not defined(nimscript) and hasAlloc and not defined(gcDestructors):
|
||||
when not defined(JS) and not defined(nimscript) and hasAlloc and not defined(nimSeqsV2):
|
||||
proc addChar(s: NimString, c: char): NimString {.compilerproc, benign.}
|
||||
|
||||
when not defined(gcDestructors) or defined(nimscript):
|
||||
when not defined(nimSeqsV2) or defined(nimscript):
|
||||
proc add*[T](x: var seq[T], y: T) {.magic: "AppendSeqElem", noSideEffect.}
|
||||
## Generic proc for adding a data item `y` to a container `x`.
|
||||
##
|
||||
|
|
@ -2104,7 +2141,7 @@ proc add*[T](x: var seq[T], y: openArray[T]) {.noSideEffect.} =
|
|||
setLen(x, xl + y.len)
|
||||
for i in 0..high(y): x[xl+i] = y[i]
|
||||
|
||||
when defined(gcDestructors):
|
||||
when defined(nimSeqsV2):
|
||||
template movingCopy(a, b) =
|
||||
a = move(b)
|
||||
else:
|
||||
|
|
@ -3002,7 +3039,7 @@ proc `==`*[T](x, y: seq[T]): bool {.noSideEffect.} =
|
|||
else:
|
||||
when not defined(JS):
|
||||
proc seqToPtr[T](x: seq[T]): pointer {.inline, noSideEffect.} =
|
||||
when defined(gcDestructors):
|
||||
when defined(nimSeqsV2):
|
||||
result = cast[NimSeqV2[T]](x).p
|
||||
else:
|
||||
result = cast[pointer](x)
|
||||
|
|
@ -3098,7 +3135,7 @@ when not defined(js):
|
|||
name: cstring
|
||||
PNimType = ptr TNimType
|
||||
|
||||
when defined(gcDestructors) and not defined(nimscript):
|
||||
when defined(nimSeqsV2) and not defined(nimscript):
|
||||
include "core/strs"
|
||||
include "core/seqs"
|
||||
|
||||
|
|
@ -3747,7 +3784,7 @@ when not defined(JS): #and not defined(nimscript):
|
|||
{.pop.}
|
||||
{.push stack_trace: off, profiler:off.}
|
||||
when hasAlloc:
|
||||
when not defined(gcDestructors):
|
||||
when not defined(nimSeqsV2):
|
||||
include "system/sysstr"
|
||||
{.pop.}
|
||||
when hasAlloc: include "system/strmantle"
|
||||
|
|
@ -4162,7 +4199,7 @@ proc shallow*(s: var string) {.noSideEffect, inline.} =
|
|||
## perform deep copies of `s`.
|
||||
##
|
||||
## This is only useful for optimization purposes.
|
||||
when not defined(JS) and not defined(nimscript) and not defined(gcDestructors):
|
||||
when not defined(JS) and not defined(nimscript) and not defined(nimSeqsV2):
|
||||
var s = cast[PGenericSeq](s)
|
||||
if s == nil:
|
||||
s = cast[PGenericSeq](newString(0))
|
||||
|
|
|
|||
|
|
@ -439,7 +439,7 @@ proc getStackTrace(e: ref Exception): string =
|
|||
proc getStackTraceEntries*(e: ref Exception): seq[StackTraceEntry] =
|
||||
## Returns the attached stack trace to the exception ``e`` as
|
||||
## a ``seq``. This is not yet available for the JS backend.
|
||||
when not defined(gcDestructors):
|
||||
when not defined(nimSeqsV2):
|
||||
shallowCopy(result, e.trace)
|
||||
else:
|
||||
result = move(e.trace)
|
||||
|
|
|
|||
|
|
@ -254,7 +254,7 @@ proc forAllChildren(cell: PCell, op: WalkOp) =
|
|||
of tyRef: # common case
|
||||
forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
|
||||
of tySequence:
|
||||
when not defined(gcDestructors):
|
||||
when not defined(nimSeqsV2):
|
||||
var d = cast[ByteAddress](cellToUsr(cell))
|
||||
var s = cast[PGenericSeq](d)
|
||||
if s != nil:
|
||||
|
|
@ -304,7 +304,7 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
|||
zeroMem(result, size)
|
||||
when defined(memProfiler): nimProfile(size)
|
||||
|
||||
when not defined(gcDestructors):
|
||||
when not defined(nimSeqsV2):
|
||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||
# `newObj` already uses locks, so no need for them here.
|
||||
let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
|
||||
|
|
|
|||
|
|
@ -112,6 +112,8 @@ proc c_setvbuf(f: File, buf: pointer, mode: cint, size: csize): cint {.
|
|||
|
||||
proc c_fprintf(f: File, frmt: cstring): cint {.
|
||||
importc: "fprintf", header: "<stdio.h>", varargs, discardable.}
|
||||
proc c_fputc(c: char, f: File): cint {.
|
||||
importc: "fputc", header: "<stdio.h>".}
|
||||
|
||||
## When running nim in android app stdout goes no where, so echo gets ignored
|
||||
## To redreict echo to the android logcat use -d:androidNDK
|
||||
|
|
@ -212,6 +214,10 @@ when defined(windows):
|
|||
var i = c_fprintf(f, "%s", s)
|
||||
while i < s.len:
|
||||
if s[i] == '\0':
|
||||
let w = c_fputc('\0', f)
|
||||
if w != 0:
|
||||
if doRaise: raiseEIO("cannot write string to file")
|
||||
break
|
||||
inc i
|
||||
else:
|
||||
let w = c_fprintf(f, "%s", unsafeAddr s[i])
|
||||
|
|
@ -653,10 +659,10 @@ when defined(windows) and appType == "console" and
|
|||
|
||||
proc readFile*(filename: string): TaintedString {.tags: [ReadIOEffect], benign.} =
|
||||
## Opens a file named `filename` for reading, calls `readAll
|
||||
## <#readAll>`_ and closes the file afterwards. Returns the string.
|
||||
## Raises an IO exception in case of an error. If # you need to call
|
||||
## <#readAll,File>`_ and closes the file afterwards. Returns the string.
|
||||
## Raises an IO exception in case of an error. If you need to call
|
||||
## this inside a compile time macro you can use `staticRead
|
||||
## <#staticRead>`_.
|
||||
## <system.html#staticRead,string>`_.
|
||||
var f: File
|
||||
if open(f, filename):
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -152,7 +152,9 @@ when defined(boehmgc):
|
|||
proc nimGC_setStackBottom(theStackBottom: pointer) = discard
|
||||
|
||||
proc initGC() =
|
||||
boehmGC_set_all_interior_pointers(0)
|
||||
when defined(boehmNoIntPtr):
|
||||
# See #12286
|
||||
boehmGC_set_all_interior_pointers(0)
|
||||
boehmGCinit()
|
||||
when hasThreadSupport:
|
||||
boehmGC_allow_register_threads()
|
||||
|
|
@ -516,7 +518,7 @@ else:
|
|||
else:
|
||||
include "system/gc"
|
||||
|
||||
when not declared(nimNewSeqOfCap) and not defined(gcDestructors):
|
||||
when not declared(nimNewSeqOfCap) and not defined(nimSeqsV2):
|
||||
proc nimNewSeqOfCap(typ: PNimType, cap: int): pointer {.compilerproc.} =
|
||||
when defined(gcRegions):
|
||||
let s = mulInt(cap, typ.base.size) # newStr already adds GenericSeqSize
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ when not defined(useNimRtl):
|
|||
reprAux(result, cast[pointer](cast[ByteAddress](p) + i*bs), typ.base, cl)
|
||||
add result, "]"
|
||||
|
||||
when defined(gcDestructors):
|
||||
when defined(nimSeqsV2):
|
||||
type
|
||||
GenericSeq = object
|
||||
len: int
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue