Merge branch 'devel' into IOSelector_unregister_fix

This commit is contained in:
Dominik Picheta 2019-10-22 11:31:38 -07:00 • committed by GitHub
commit 92fa7e0579
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
94 changed files with 1729 additions and 1223 deletions

View file

@ -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
## ========
##

View file

@ -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

View file

@ -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

View file

@ -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()

View file

@ -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"])

View file

@ -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.

View file

@ -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>"""

View file

@ -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:

View file

@ -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

View file

@ -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.

View file

@ -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()

View file

@ -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]

View file

@ -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()

View file

@ -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

View file

@ -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

View file

@ -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 =

View file

@ -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))

View file

@ -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)

View file

@ -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)

View file

@ -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:

View file

@ -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

View file

@ -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