merge devel
This commit is contained in:
commit
73b7ec9009
309 changed files with 11866 additions and 9591 deletions
132
tests/array/t7818.nim
Normal file
132
tests/array/t7818.nim
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
discard """
|
||||
output: "OK"
|
||||
"""
|
||||
|
||||
# bug #7818
|
||||
# this is not a macro bug, but array construction bug
|
||||
# I use macro to avoid object slicing
|
||||
# see #7712 and #7637
|
||||
import macros
|
||||
|
||||
type
|
||||
Vehicle[T] = object of RootObj
|
||||
tire: T
|
||||
Car[T] = object of Vehicle[T]
|
||||
Bike[T] = object of Vehicle[T]
|
||||
|
||||
macro peek(n: typed): untyped =
|
||||
let val = getTypeImpl(n).treeRepr
|
||||
newLit(val)
|
||||
|
||||
block test_t7818:
|
||||
var v = Vehicle[int](tire: 3)
|
||||
var c = Car[int](tire: 4)
|
||||
var b = Bike[int](tire: 2)
|
||||
|
||||
let y = peek([c, b, v])
|
||||
let z = peek([v, c, b])
|
||||
doAssert(y == z)
|
||||
|
||||
block test_t7906_1:
|
||||
proc init(x: typedesc, y: int): ref x =
|
||||
result = new(ref x)
|
||||
result.tire = y
|
||||
|
||||
var v = init(Vehicle[int], 3)
|
||||
var c = init(Car[int], 4)
|
||||
var b = init(Bike[int], 2)
|
||||
|
||||
let y = peek([c, b, v])
|
||||
let z = peek([v, c, b])
|
||||
doAssert(y == z)
|
||||
|
||||
block test_t7906_2:
|
||||
var v = Vehicle[int](tire: 3)
|
||||
var c = Car[int](tire: 4)
|
||||
var b = Bike[int](tire: 2)
|
||||
|
||||
let y = peek([c.addr, b.addr, v.addr])
|
||||
let z = peek([v.addr, c.addr, b.addr])
|
||||
doAssert(y == z)
|
||||
|
||||
block test_t7906_3:
|
||||
type
|
||||
Animal[T] = object of RootObj
|
||||
hair: T
|
||||
Mammal[T] = object of Animal[T]
|
||||
Monkey[T] = object of Mammal[T]
|
||||
|
||||
var v = Animal[int](hair: 3)
|
||||
var c = Mammal[int](hair: 4)
|
||||
var b = Monkey[int](hair: 2)
|
||||
|
||||
let z = peek([c.addr, b.addr, v.addr])
|
||||
let y = peek([v.addr, c.addr, b.addr])
|
||||
doAssert(y == z)
|
||||
|
||||
type
|
||||
Fruit[T] = ref object of RootObj
|
||||
color: T
|
||||
Apple[T] = ref object of Fruit[T]
|
||||
Banana[T] = ref object of Fruit[T]
|
||||
|
||||
proc testArray[T](x: array[3, Fruit[T]]): string =
|
||||
result = ""
|
||||
for c in x:
|
||||
result.add $c.color
|
||||
|
||||
proc testOpenArray[T](x: openArray[Fruit[T]]): string =
|
||||
result = ""
|
||||
for c in x:
|
||||
result.add $c.color
|
||||
|
||||
block test_t7906_4:
|
||||
var v = Fruit[int](color: 3)
|
||||
var c = Apple[int](color: 4)
|
||||
var b = Banana[int](color: 2)
|
||||
|
||||
let y = peek([c, b, v])
|
||||
let z = peek([v, c, b])
|
||||
doAssert(y == z)
|
||||
|
||||
block test_t7906_5:
|
||||
var a = Fruit[int](color: 1)
|
||||
var b = Apple[int](color: 2)
|
||||
var c = Banana[int](color: 3)
|
||||
|
||||
doAssert(testArray([a, b, c]) == "123")
|
||||
doAssert(testArray([b, c, a]) == "231")
|
||||
|
||||
doAssert(testOpenArray([a, b, c]) == "123")
|
||||
doAssert(testOpenArray([b, c, a]) == "231")
|
||||
|
||||
doAssert(testOpenArray(@[a, b, c]) == "123")
|
||||
doAssert(testOpenArray(@[b, c, a]) == "231")
|
||||
|
||||
proc testArray[T](x: array[3, ptr Vehicle[T]]): string =
|
||||
result = ""
|
||||
for c in x:
|
||||
result.add $c.tire
|
||||
|
||||
proc testOpenArray[T](x: openArray[ptr Vehicle[T]]): string =
|
||||
result = ""
|
||||
for c in x:
|
||||
result.add $c.tire
|
||||
|
||||
block test_t7906_6:
|
||||
var u = Vehicle[int](tire: 1)
|
||||
var v = Bike[int](tire: 2)
|
||||
var w = Car[int](tire: 3)
|
||||
|
||||
doAssert(testArray([u.addr, v.addr, w.addr]) == "123")
|
||||
doAssert(testArray([w.addr, u.addr, v.addr]) == "312")
|
||||
|
||||
doAssert(testOpenArray([u.addr, v.addr, w.addr]) == "123")
|
||||
doAssert(testOpenArray([w.addr, u.addr, v.addr]) == "312")
|
||||
|
||||
doAssert(testOpenArray(@[u.addr, v.addr, w.addr]) == "123")
|
||||
doAssert(testOpenArray(@[w.addr, u.addr, v.addr]) == "312")
|
||||
|
||||
echo "OK"
|
||||
|
||||
|
||||
19
tests/async/t7985.nim
Normal file
19
tests/async/t7985.nim
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
discard """
|
||||
file: "t7985.nim"
|
||||
exitcode: 0
|
||||
output: "(value: 1)"
|
||||
"""
|
||||
import json, asyncdispatch
|
||||
|
||||
proc getData(): Future[JsonNode] {.async.} =
|
||||
result = %*{"value": 1}
|
||||
|
||||
type
|
||||
MyData = object
|
||||
value: BiggestInt
|
||||
|
||||
proc main() {.async.} =
|
||||
let data = to(await(getData()), MyData)
|
||||
echo data
|
||||
|
||||
waitFor(main())
|
||||
|
|
@ -1,18 +1,25 @@
|
|||
discard """
|
||||
errormsg: "invalid control flow: 'yield' within a constructor"
|
||||
line: 16
|
||||
output: '''
|
||||
@[1, 2, 3, 4]
|
||||
123
|
||||
'''
|
||||
"""
|
||||
|
||||
# bug #5314, bug #6626
|
||||
|
||||
import asyncdispatch
|
||||
|
||||
proc bar(): Future[int] {.async.} =
|
||||
await sleepAsync(500)
|
||||
result = 3
|
||||
proc bar(i: int): Future[int] {.async.} =
|
||||
await sleepAsync(2)
|
||||
result = i
|
||||
|
||||
proc foo(): Future[seq[int]] {.async.} =
|
||||
await sleepAsync(500)
|
||||
result = @[1, 2, await bar(), 4] # <--- The bug is here
|
||||
await sleepAsync(2)
|
||||
result = @[1, 2, await bar(3), 4] # <--- The bug is here
|
||||
|
||||
proc foo2() {.async.} =
|
||||
await sleepAsync(2)
|
||||
echo(await bar(1), await bar(2), await bar(3))
|
||||
|
||||
echo waitFor foo()
|
||||
waitFor foo2()
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ discard """
|
|||
disabled: "windows"
|
||||
output: "Matched"
|
||||
"""
|
||||
import asyncdispatch
|
||||
import asyncdispatch, strutils
|
||||
|
||||
# Tests to ensure our exception trace backs are friendly.
|
||||
|
||||
|
|
@ -82,7 +82,7 @@ Async traceback:
|
|||
asyncmacro\.nim\(\d+?\)\s+?a
|
||||
asyncmacro\.nim\(\d+?\)\s+?a_continue
|
||||
## Resumes an async procedure
|
||||
asyncmacro\.nim\(\d+?\)\s+?aIter
|
||||
tasync_traceback\.nim\(\d+?\)\s+?aIter
|
||||
asyncfutures\.nim\(\d+?\)\s+?read
|
||||
\]#
|
||||
Exception message: b failure
|
||||
|
|
@ -110,17 +110,33 @@ Async traceback:
|
|||
## Executes pending callbacks
|
||||
asyncmacro\.nim\(\d+?\)\s+?foo_continue
|
||||
## Resumes an async procedure
|
||||
asyncmacro\.nim\(\d+?\)\s+?fooIter
|
||||
tasync_traceback\.nim\(\d+?\)\s+?fooIter
|
||||
asyncfutures\.nim\(\d+?\)\s+?read
|
||||
\]#
|
||||
Exception message: bar failure
|
||||
Exception type:
|
||||
"""
|
||||
|
||||
if result.match(re(expected)):
|
||||
echo("Matched")
|
||||
else:
|
||||
echo("Not matched!")
|
||||
let resLines = splitLines(result.strip)
|
||||
let expLines = splitLines(expected.strip)
|
||||
|
||||
if resLines.len != expLines.len:
|
||||
echo("Not matched! Wrong number of lines!")
|
||||
echo()
|
||||
echo(result)
|
||||
quit(QuitFailure)
|
||||
|
||||
var ok = true
|
||||
for i in 0 ..< resLines.len:
|
||||
if not resLines[i].match(re(expLines[i])):
|
||||
echo "Not matched! Line ", i + 1
|
||||
echo "Expected:"
|
||||
echo expLines[i]
|
||||
echo "Actual:"
|
||||
echo resLines[i]
|
||||
ok = false
|
||||
|
||||
if ok:
|
||||
echo("Matched")
|
||||
else:
|
||||
quit(QuitFailure)
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@ const
|
|||
|
||||
var clientCount = 0
|
||||
|
||||
proc sendMessages(client: TAsyncFD) {.async.} =
|
||||
proc sendMessages(client: AsyncFD) {.async.} =
|
||||
for i in 0 .. <messagesToSend:
|
||||
await send(client, "Message " & $i & "\c\L")
|
||||
|
||||
proc launchSwarm(port: TPort) {.async.} =
|
||||
proc launchSwarm(port: Port) {.async.} =
|
||||
for i in 0 .. <swarmSize:
|
||||
var sock = newAsyncNativeSocket()
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ proc launchSwarm(port: TPort) {.async.} =
|
|||
await sendMessages(sock)
|
||||
closeSocket(sock)
|
||||
|
||||
proc readMessages(client: TAsyncFD) {.async.} =
|
||||
proc readMessages(client: AsyncFD) {.async.} =
|
||||
while true:
|
||||
var line = await recvLine(client)
|
||||
if line == "":
|
||||
|
|
@ -37,7 +37,7 @@ proc readMessages(client: TAsyncFD) {.async.} =
|
|||
else:
|
||||
doAssert false
|
||||
|
||||
proc createServer(port: TPort) {.async.} =
|
||||
proc createServer(port: Port) {.async.} =
|
||||
var server = newAsyncNativeSocket()
|
||||
block:
|
||||
var name: Sockaddr_in
|
||||
|
|
@ -55,8 +55,8 @@ proc createServer(port: TPort) {.async.} =
|
|||
while true:
|
||||
asyncCheck readMessages(await accept(server))
|
||||
|
||||
asyncCheck createServer(TPort(10335))
|
||||
asyncCheck launchSwarm(TPort(10335))
|
||||
asyncCheck createServer(Port(10335))
|
||||
asyncCheck launchSwarm(Port(10335))
|
||||
while true:
|
||||
poll()
|
||||
if clientCount == swarmSize: break
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ Multiple except branches
|
|||
Multiple except branches 2
|
||||
'''
|
||||
"""
|
||||
import asyncdispatch
|
||||
import asyncdispatch, strutils
|
||||
|
||||
# Here we are testing the ability to catch exceptions.
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ proc catch() {.async.} =
|
|||
try:
|
||||
await foobar()
|
||||
except:
|
||||
echo("Generic except: ", getCurrentExceptionMsg())
|
||||
echo("Generic except: ", getCurrentExceptionMsg().splitLines[0])
|
||||
|
||||
try:
|
||||
await foobar()
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
discard """
|
||||
file: "tasynctry2.nim"
|
||||
errormsg: "\'yield\' cannot be used within \'try\' in a non-inlined iterator"
|
||||
line: 15
|
||||
line: 14
|
||||
"""
|
||||
import asyncdispatch
|
||||
|
||||
{.experimental: "oldIterTransf".}
|
||||
|
||||
proc foo(): Future[bool] {.async.} = discard
|
||||
|
||||
proc test5(): Future[int] {.async.} =
|
||||
|
|
|
|||
|
|
@ -68,15 +68,15 @@ block:
|
|||
block:
|
||||
var t: CritBitTree[int]
|
||||
t["a"] = 1
|
||||
doAssert $t == "{a: 1}"
|
||||
doAssert $t == """{"a": 1}"""
|
||||
block:
|
||||
var t: CritBitTree[string]
|
||||
t["a"] = "1"
|
||||
doAssert $t == """{a: "1"}"""
|
||||
doAssert $t == """{"a": "1"}"""
|
||||
block:
|
||||
var t: CritBitTree[char]
|
||||
t["a"] = '1'
|
||||
doAssert $t == "{a: '1'}"
|
||||
doAssert $t == """{"a": '1'}"""
|
||||
|
||||
|
||||
# Test escaping behavior
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ discard """
|
|||
"""
|
||||
|
||||
import tables
|
||||
from hashes import THash
|
||||
from hashes import Hash
|
||||
|
||||
# Test with int
|
||||
block:
|
||||
|
|
@ -66,7 +66,7 @@ block:
|
|||
# The same test with a custom hash(s: string) does
|
||||
# work though.
|
||||
block:
|
||||
proc hash(x: int): THash {.inline.} =
|
||||
proc hash(x: int): Hash {.inline.} =
|
||||
echo "overloaded hash"
|
||||
result = x
|
||||
var t = initTable[int, int]()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import sets
|
||||
import hashes
|
||||
import algorithm
|
||||
|
||||
block setEquality:
|
||||
var
|
||||
|
|
@ -35,7 +37,7 @@ block setWithSequences:
|
|||
doAssert( not s.contains(@[4, 5, 6]) )
|
||||
|
||||
block setClearWorked:
|
||||
var s = initSet[char]()
|
||||
var s = initSet[char]()
|
||||
|
||||
for c in "this is a test":
|
||||
s.incl(c)
|
||||
|
|
@ -68,12 +70,54 @@ block orderedSetClearWorked:
|
|||
for c in "eat at joes":
|
||||
s.incl(c)
|
||||
|
||||
r = ""
|
||||
r = ""
|
||||
for c in items(s):
|
||||
add(r, c)
|
||||
|
||||
doAssert r == "zeat jos"
|
||||
|
||||
block hashForHashedSet:
|
||||
let
|
||||
seq1 = "This is the test."
|
||||
seq2 = "the test is This."
|
||||
s1 = seq1.toSet()
|
||||
s2 = seq2.toSet()
|
||||
var hashSeq: seq[Hash] = @[]
|
||||
doAssert s1 == s2
|
||||
doAssert hash(s1) == hash(s2)
|
||||
|
||||
block hashForOrderdSet:
|
||||
let
|
||||
str = "This is the test."
|
||||
rstr = str.reversed
|
||||
|
||||
var
|
||||
s1 = initOrderedSet[char]()
|
||||
s2 = initOrderedSet[char]()
|
||||
r = initOrderedSet[char]()
|
||||
expected: Hash
|
||||
added: seq[char] = @[]
|
||||
reversed: Hash
|
||||
radded: seq[char] = @[]
|
||||
|
||||
expected = 0
|
||||
for c in str:
|
||||
if (not (c in added)):
|
||||
expected = expected !& hash(c)
|
||||
added.add(c)
|
||||
s1.incl(c)
|
||||
s2.incl(c)
|
||||
expected = !$expected
|
||||
doAssert hash(s1) == expected
|
||||
doAssert hash(s1) == hash(s2)
|
||||
doAssert hash(s1) != hash(r)
|
||||
|
||||
reversed = 0
|
||||
for c in rstr:
|
||||
if (not (c in radded)):
|
||||
reversed = reversed !& hash(c)
|
||||
radded.add(c)
|
||||
r.incl(c)
|
||||
reversed = !$reversed
|
||||
doAssert hash(r) == reversed
|
||||
doAssert hash(s1) != reversed
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# bug #3313
|
||||
import unittest, future
|
||||
|
||||
import unittest, sugar
|
||||
{.experimental: "notnil".}
|
||||
type
|
||||
ListNodeKind = enum
|
||||
lnkNil, lnkCons
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@ template accept(x) =
|
|||
|
||||
template reject(x) =
|
||||
static: assert(not compiles(x))
|
||||
|
||||
{.experimental: "notnil".}
|
||||
type
|
||||
TRefObj = ref object
|
||||
x: int
|
||||
|
||||
THasNotNils = object of TObject
|
||||
THasNotNils = object of RootObj
|
||||
a: TRefObj not nil
|
||||
b: TRefObj not nil
|
||||
c: TRefObj
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
discard """
|
||||
file: "tstatret.nim"
|
||||
line: 9
|
||||
errormsg: "statement not allowed after"
|
||||
errormsg: "unreachable statement after 'return'"
|
||||
"""
|
||||
# no statement after return
|
||||
proc main() =
|
||||
|
|
|
|||
|
|
@ -15,6 +15,6 @@ type TFoo = object
|
|||
converter toPtr*(some: var TFoo): ptr TFoo = (addr some)
|
||||
|
||||
|
||||
proc zoot(x: ptr TFoo) = nil
|
||||
proc zoot(x: ptr TFoo) = discard
|
||||
var x: Tfoo
|
||||
zoot(x)
|
||||
|
|
|
|||
|
|
@ -1,15 +1,11 @@
|
|||
discard """
|
||||
file: "tnil.nim"
|
||||
output: '''0x1
|
||||
|
||||
nil
|
||||
|
||||
nil
|
||||
|
||||
output: '''1
|
||||
0
|
||||
0
|
||||
'''
|
||||
disabled: "windows"
|
||||
"""
|
||||
|
||||
{.experimental: "notnil".}
|
||||
type
|
||||
MyPointer = distinct pointer
|
||||
MyString = distinct string
|
||||
|
|
@ -17,7 +13,8 @@ type
|
|||
MyInt = distinct int
|
||||
|
||||
proc foo(a: MyPointer) =
|
||||
echo a.repr
|
||||
# workaround a Windows 'repr' difference:
|
||||
echo cast[int](a)
|
||||
|
||||
foo(cast[MyPointer](1))
|
||||
foo(cast[MyPointer](nil))
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ type
|
|||
EIO2 = ref object of EIO
|
||||
|
||||
proc q() {.tags: [FIO].} =
|
||||
nil
|
||||
discard
|
||||
|
||||
proc raiser(): int =
|
||||
writeLine stdout, "arg"
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
discard """
|
||||
cmd: "nim check $file"
|
||||
errormsg: "The `in` modifier can be used only with imported types"
|
||||
errormsg: "the 'in' modifier can be used only with imported types"
|
||||
nimout: '''
|
||||
tinvalidinout.nim(14, 7) Error: The `out` modifier can be used only with imported types
|
||||
tinvalidinout.nim(17, 9) Error: The `in` modifier can be used only with imported types
|
||||
tinvalidinout.nim(18, 9) Error: The `in` modifier can be used only with imported types
|
||||
tinvalidinout.nim(14, 7) Error: the 'out' modifier can be used only with imported types
|
||||
tinvalidinout.nim(17, 9) Error: the 'in' modifier can be used only with imported types
|
||||
tinvalidinout.nim(18, 9) Error: the 'in' modifier can be used only with imported types
|
||||
'''
|
||||
"""
|
||||
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ semiProblem()
|
|||
# bug #844
|
||||
|
||||
import json
|
||||
proc parseResponse(): PJsonNode =
|
||||
proc parseResponse(): JsonNode =
|
||||
result = % { "key1": % { "key2": % "value" } }
|
||||
for key, val in result["key1"]:
|
||||
var excMsg = key & "("
|
||||
|
|
|
|||
14
tests/generics/module_with_generics.nim
Normal file
14
tests/generics/module_with_generics.nim
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
type
|
||||
Base[T] = ref object {.inheritable.}
|
||||
value*: T
|
||||
|
||||
Derived[T] = ref object of Base[T]
|
||||
derivedValue*: T
|
||||
|
||||
proc makeDerived*[T](v: T): Derived[T] =
|
||||
new result
|
||||
result.value = v
|
||||
|
||||
proc setBaseValue*[T](a: Base[T], value: T) =
|
||||
a.value = value
|
||||
|
||||
12
tests/generics/t3977.nim
Normal file
12
tests/generics/t3977.nim
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
discard """
|
||||
output: '''42'''
|
||||
"""
|
||||
|
||||
type
|
||||
Foo[N: static[int]] = object
|
||||
|
||||
proc foo[N](x: Foo[N]) =
|
||||
echo N
|
||||
|
||||
var f1: Foo[42]
|
||||
f1.foo
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
discard """
|
||||
output: "seq[float]\n0"
|
||||
targets: "c cpp"
|
||||
"""
|
||||
|
||||
# https://github.com/nim-lang/Nim/issues/5602
|
||||
|
||||
import typetraits
|
||||
import typetraits, module_with_generics
|
||||
|
||||
type
|
||||
Foo[T] = object of RootObj
|
||||
|
|
@ -16,3 +17,8 @@ proc p[T](f: Foo[T]): T =
|
|||
var s: Bar[float]
|
||||
echo p(s).len # the bug was: p(s) should return seq[float], but returns float instead
|
||||
|
||||
# Test overloading and code generation when
|
||||
# downcasting is required for generic types:
|
||||
var d = makeDerived(10)
|
||||
setBaseValue(d, 20)
|
||||
|
||||
|
|
|
|||
407
tests/iter/tyieldintry.nim
Normal file
407
tests/iter/tyieldintry.nim
Normal file
|
|
@ -0,0 +1,407 @@
|
|||
discard """
|
||||
targets: "c cpp"
|
||||
output: "ok"
|
||||
"""
|
||||
var closureIterResult = newSeq[int]()
|
||||
|
||||
proc checkpoint(arg: int) =
|
||||
closureIterResult.add(arg)
|
||||
|
||||
type
|
||||
TestException = object of Exception
|
||||
AnotherException = object of Exception
|
||||
|
||||
proc testClosureIterAux(it: iterator(): int, exceptionExpected: bool, expectedResults: varargs[int]) =
|
||||
closureIterResult.setLen(0)
|
||||
|
||||
var exceptionCaught = false
|
||||
|
||||
try:
|
||||
for i in it():
|
||||
closureIterResult.add(i)
|
||||
except TestException:
|
||||
exceptionCaught = true
|
||||
|
||||
if closureIterResult != @expectedResults or exceptionCaught != exceptionExpected:
|
||||
if closureIterResult != @expectedResults:
|
||||
echo "Expected: ", @expectedResults
|
||||
echo "Actual: ", closureIterResult
|
||||
if exceptionCaught != exceptionExpected:
|
||||
echo "Expected exception: ", exceptionExpected
|
||||
echo "Got exception: ", exceptionCaught
|
||||
doAssert(false)
|
||||
|
||||
proc test(it: iterator(): int, expectedResults: varargs[int]) =
|
||||
testClosureIterAux(it, false, expectedResults)
|
||||
|
||||
proc testExc(it: iterator(): int, expectedResults: varargs[int]) =
|
||||
testClosureIterAux(it, true, expectedResults)
|
||||
|
||||
proc raiseException() =
|
||||
raise newException(TestException, "Test exception!")
|
||||
|
||||
block:
|
||||
iterator it(): int {.closure.} =
|
||||
var i = 5
|
||||
while i != 0:
|
||||
yield i
|
||||
if i == 3:
|
||||
yield 123
|
||||
dec i
|
||||
|
||||
test(it, 5, 4, 3, 123, 2, 1)
|
||||
|
||||
block:
|
||||
iterator it(): int {.closure.} =
|
||||
yield 0
|
||||
try:
|
||||
checkpoint(1)
|
||||
raiseException()
|
||||
except TestException:
|
||||
checkpoint(2)
|
||||
yield 3
|
||||
checkpoint(4)
|
||||
finally:
|
||||
checkpoint(5)
|
||||
|
||||
checkpoint(6)
|
||||
|
||||
test(it, 0, 1, 2, 3, 4, 5, 6)
|
||||
|
||||
block:
|
||||
iterator it(): int {.closure.} =
|
||||
yield 0
|
||||
try:
|
||||
yield 1
|
||||
checkpoint(2)
|
||||
finally:
|
||||
checkpoint(3)
|
||||
yield 4
|
||||
checkpoint(5)
|
||||
yield 6
|
||||
|
||||
test(it, 0, 1, 2, 3, 4, 5, 6)
|
||||
|
||||
block:
|
||||
iterator it(): int {.closure.} =
|
||||
yield 0
|
||||
try:
|
||||
yield 1
|
||||
raiseException()
|
||||
yield 2
|
||||
finally:
|
||||
checkpoint(3)
|
||||
yield 4
|
||||
checkpoint(5)
|
||||
yield 6
|
||||
|
||||
testExc(it, 0, 1, 3, 4, 5, 6)
|
||||
|
||||
block:
|
||||
iterator it(): int {.closure.} =
|
||||
try:
|
||||
try:
|
||||
raiseException()
|
||||
except AnotherException:
|
||||
yield 123
|
||||
finally:
|
||||
checkpoint(3)
|
||||
finally:
|
||||
checkpoint(4)
|
||||
|
||||
testExc(it, 3, 4)
|
||||
|
||||
block:
|
||||
iterator it(): int {.closure.} =
|
||||
try:
|
||||
yield 1
|
||||
raiseException()
|
||||
except AnotherException:
|
||||
checkpoint(123)
|
||||
finally:
|
||||
checkpoint(2)
|
||||
checkpoint(3)
|
||||
|
||||
testExc(it, 1, 2)
|
||||
|
||||
block:
|
||||
iterator it(): int {.closure.} =
|
||||
try:
|
||||
yield 0
|
||||
try:
|
||||
yield 1
|
||||
try:
|
||||
yield 2
|
||||
raiseException()
|
||||
except AnotherException:
|
||||
yield 123
|
||||
finally:
|
||||
yield 3
|
||||
except AnotherException:
|
||||
yield 124
|
||||
finally:
|
||||
yield 4
|
||||
checkpoint(1234)
|
||||
except:
|
||||
yield 5
|
||||
checkpoint(6)
|
||||
finally:
|
||||
checkpoint(7)
|
||||
yield 8
|
||||
checkpoint(9)
|
||||
|
||||
test(it, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
|
||||
|
||||
block:
|
||||
iterator it(): int {.closure.} =
|
||||
try:
|
||||
yield 0
|
||||
return 2
|
||||
finally:
|
||||
checkpoint(1)
|
||||
checkpoint(123)
|
||||
|
||||
test(it, 0, 1)
|
||||
|
||||
block:
|
||||
iterator it(): int {.closure.} =
|
||||
try:
|
||||
try:
|
||||
yield 0
|
||||
raiseException()
|
||||
finally:
|
||||
checkpoint(1)
|
||||
except TestException:
|
||||
yield 2
|
||||
return
|
||||
finally:
|
||||
yield 3
|
||||
|
||||
checkpoint(123)
|
||||
|
||||
test(it, 0, 1, 2, 3)
|
||||
|
||||
block:
|
||||
iterator it(): int {.closure.} =
|
||||
try:
|
||||
try:
|
||||
yield 0
|
||||
raiseException()
|
||||
finally:
|
||||
return # Return in finally should stop exception propagation
|
||||
except AnotherException:
|
||||
yield 2
|
||||
return
|
||||
finally:
|
||||
yield 3
|
||||
checkpoint(123)
|
||||
|
||||
test(it, 0, 3)
|
||||
|
||||
block: # Yield in yield
|
||||
iterator it(): int {.closure.} =
|
||||
template foo(): int =
|
||||
yield 1
|
||||
2
|
||||
|
||||
for i in 0 .. 2:
|
||||
checkpoint(0)
|
||||
yield foo()
|
||||
|
||||
test(it, 0, 1, 2, 0, 1, 2, 0, 1, 2)
|
||||
|
||||
block:
|
||||
iterator it(): int {.closure.} =
|
||||
let i = if true:
|
||||
yield 0
|
||||
1
|
||||
else:
|
||||
2
|
||||
yield i
|
||||
|
||||
test(it, 0, 1)
|
||||
|
||||
block:
|
||||
iterator it(): int {.closure.} =
|
||||
var foo = 123
|
||||
let i = try:
|
||||
yield 0
|
||||
raiseException()
|
||||
1
|
||||
except TestException as e:
|
||||
assert(e.msg == "Test exception!")
|
||||
case foo
|
||||
of 1:
|
||||
yield 123
|
||||
2
|
||||
of 123:
|
||||
yield 5
|
||||
6
|
||||
else:
|
||||
7
|
||||
yield i
|
||||
|
||||
test(it, 0, 5, 6)
|
||||
|
||||
block:
|
||||
iterator it(): int {.closure.} =
|
||||
proc voidFoo(i1, i2, i3: int) =
|
||||
checkpoint(i1)
|
||||
checkpoint(i2)
|
||||
checkpoint(i3)
|
||||
|
||||
proc foo(i1, i2, i3: int): int =
|
||||
voidFoo(i1, i2, i3)
|
||||
i3
|
||||
|
||||
proc bar(i1: int): int =
|
||||
checkpoint(i1)
|
||||
|
||||
template tryexcept: int =
|
||||
try:
|
||||
yield 1
|
||||
raiseException()
|
||||
123
|
||||
except TestException:
|
||||
yield 2
|
||||
checkpoint(3)
|
||||
4
|
||||
|
||||
let e1 = true
|
||||
|
||||
template ifelse1: int =
|
||||
if e1:
|
||||
yield 10
|
||||
11
|
||||
else:
|
||||
12
|
||||
|
||||
template ifelse2: int =
|
||||
if ifelse1() == 12:
|
||||
yield 20
|
||||
21
|
||||
else:
|
||||
yield 22
|
||||
23
|
||||
|
||||
let i = foo(bar(0), tryexcept, ifelse2)
|
||||
discard foo(bar(0), tryexcept, ifelse2)
|
||||
voidFoo(bar(0), tryexcept, ifelse2)
|
||||
yield i
|
||||
|
||||
test(it,
|
||||
|
||||
# let i = foo(bar(0), tryexcept, ifelse2)
|
||||
0, # bar(0)
|
||||
1, 2, 3, # tryexcept
|
||||
10, # ifelse1
|
||||
22, # ifelse22
|
||||
0, 4, 23, # foo
|
||||
|
||||
# discard foo(bar(0), tryexcept, ifelse2)
|
||||
0, # bar(0)
|
||||
1, 2, 3, # tryexcept
|
||||
10, # ifelse1
|
||||
22, # ifelse22
|
||||
0, 4, 23, # foo
|
||||
|
||||
# voidFoo(bar(0), tryexcept, ifelse2)
|
||||
0, # bar(0)
|
||||
1, 2, 3, # tryexcept
|
||||
10, # ifelse1
|
||||
22, # ifelse22
|
||||
0, 4, 23, # foo
|
||||
|
||||
23 # i
|
||||
)
|
||||
|
||||
block:
|
||||
iterator it(): int {.closure.} =
|
||||
checkpoint(0)
|
||||
for i in 0 .. 1:
|
||||
try:
|
||||
yield 1
|
||||
raiseException()
|
||||
except TestException as e:
|
||||
doAssert(e.msg == "Test exception!")
|
||||
yield 2
|
||||
except AnotherException:
|
||||
yield 123
|
||||
except:
|
||||
yield 1234
|
||||
finally:
|
||||
yield 3
|
||||
checkpoint(4)
|
||||
yield 5
|
||||
|
||||
test(it, 0, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5)
|
||||
|
||||
block:
|
||||
iterator it(): int {.closure.} =
|
||||
var i = 5
|
||||
template foo(): bool =
|
||||
yield i
|
||||
true
|
||||
|
||||
while foo():
|
||||
dec i
|
||||
if i == 0:
|
||||
break
|
||||
|
||||
test(it, 5, 4, 3, 2, 1)
|
||||
|
||||
block: # Short cirquits
|
||||
iterator it(): int {.closure.} =
|
||||
template trueYield: bool =
|
||||
yield 1
|
||||
true
|
||||
|
||||
template falseYield: bool =
|
||||
yield 0
|
||||
false
|
||||
|
||||
if trueYield or falseYield:
|
||||
discard falseYield and trueYield
|
||||
|
||||
if falseYield and trueYield:
|
||||
checkpoint(123)
|
||||
|
||||
test(it, 1, 0, 0)
|
||||
|
||||
block: #7969
|
||||
type
|
||||
SomeObj = object
|
||||
id: int
|
||||
|
||||
iterator it(): int {.closure.} =
|
||||
template yieldAndSomeObj: SomeObj =
|
||||
var s: SomeObj
|
||||
s.id = 2
|
||||
yield 1
|
||||
s
|
||||
|
||||
checkpoint(yieldAndSomeObj().id)
|
||||
|
||||
var i = 5
|
||||
case i
|
||||
of 0:
|
||||
checkpoint(123)
|
||||
of 1, 2, 5:
|
||||
checkpoint(3)
|
||||
else:
|
||||
checkpoint(123)
|
||||
|
||||
test(it, 1, 2, 3)
|
||||
|
||||
block: # yield in blockexpr
|
||||
iterator it(): int {.closure.} =
|
||||
yield(block:
|
||||
checkpoint(1)
|
||||
yield 2
|
||||
3
|
||||
)
|
||||
|
||||
test(it, 1, 2, 3)
|
||||
|
||||
|
||||
echo "ok"
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
discard """
|
||||
file: "thexlit.nim"
|
||||
output: "equal"
|
||||
"""
|
||||
|
||||
var t=0x950412DE
|
||||
|
||||
if t==0x950412DE:
|
||||
echo "equal"
|
||||
else:
|
||||
echo "not equal"
|
||||
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
|
||||
type
|
||||
TArray = array[0x0012..0x0013, int]
|
||||
|
||||
var a: TArray
|
||||
|
||||
echo a[0x0012] #OUT 0
|
||||
|
||||
27
tests/lexer/tlexermisc.nim
Normal file
27
tests/lexer/tlexermisc.nim
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
discard """
|
||||
action: run
|
||||
output: "equal"
|
||||
"""
|
||||
|
||||
var t=0x950412DE
|
||||
|
||||
if t==0x950412DE:
|
||||
echo "equal"
|
||||
else:
|
||||
echo "not equal"
|
||||
|
||||
type
|
||||
TArray = array[0x0012..0x0013, int]
|
||||
|
||||
var a: TArray
|
||||
|
||||
doAssert a[0x0012] == 0
|
||||
|
||||
|
||||
# #7884
|
||||
|
||||
type Obj = object
|
||||
ö: int
|
||||
|
||||
let o = Obj(ö: 1)
|
||||
doAssert o.ö == 1
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
discard """
|
||||
file: "tunderscores.nim"
|
||||
line: 8
|
||||
errormsg: "invalid token: _"
|
||||
errormsg: "invalid token: trailing underscore"
|
||||
"""
|
||||
# Bug #502670
|
||||
|
||||
|
|
|
|||
154
tests/macros/tstructuredlogging.nim
Normal file
154
tests/macros/tstructuredlogging.nim
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
discard """
|
||||
output: '''
|
||||
main started: a=10, b=inner-b, c=10, d=some-d, x=16, z=20
|
||||
exiting: a=12, b=overriden-b, c=100, msg=bye bye, x=16
|
||||
'''
|
||||
"""
|
||||
|
||||
import macros, tables
|
||||
|
||||
template scopeHolder =
|
||||
0 # scope revision number
|
||||
|
||||
type
|
||||
BindingsSet = Table[string, NimNode]
|
||||
|
||||
proc actualBody(n: NimNode): NimNode =
|
||||
# skip over the double StmtList node introduced in `mergeScopes`
|
||||
result = n.body
|
||||
if result.kind == nnkStmtList and result[0].kind == nnkStmtList:
|
||||
result = result[0]
|
||||
|
||||
iterator bindings(n: NimNode, skip = 0): (string, NimNode) =
|
||||
for i in skip ..< n.len:
|
||||
let child = n[i]
|
||||
if child.kind in {nnkAsgn, nnkExprEqExpr}:
|
||||
let name = $child[0]
|
||||
let value = child[1]
|
||||
yield (name, value)
|
||||
|
||||
proc scopeRevision(scopeHolder: NimNode): int =
|
||||
# get the revision number from a scopeHolder sym
|
||||
assert scopeHolder.kind == nnkSym
|
||||
var revisionNode = scopeHolder.getImpl.actualBody[0]
|
||||
result = int(revisionNode.intVal)
|
||||
|
||||
proc lastScopeHolder(scopeHolders: NimNode): NimNode =
|
||||
# get the most recent scopeHolder from a symChoice node
|
||||
if scopeHolders.kind in {nnkClosedSymChoice, nnkOpenSymChoice}:
|
||||
var bestScopeRev = 0
|
||||
assert scopeHolders.len > 0
|
||||
for scope in scopeHolders:
|
||||
let rev = scope.scopeRevision
|
||||
if result == nil or rev > bestScopeRev:
|
||||
result = scope
|
||||
bestScopeRev = rev
|
||||
else:
|
||||
result = scopeHolders
|
||||
|
||||
assert result.kind == nnkSym
|
||||
|
||||
macro mergeScopes(scopeHolders: typed, newBindings: untyped): untyped =
|
||||
var
|
||||
bestScope = scopeHolders.lastScopeHolder
|
||||
bestScopeRev = bestScope.scopeRevision
|
||||
|
||||
var finalBindings = initTable[string, NimNode]()
|
||||
for k, v in bindings(bestScope.getImpl.actualBody, skip = 1):
|
||||
finalBindings[k] = v
|
||||
|
||||
for k, v in bindings(newBindings):
|
||||
finalBindings[k] = v
|
||||
|
||||
var newScopeDefinition = newStmtList(newLit(bestScopeRev + 1))
|
||||
|
||||
for k, v in finalBindings:
|
||||
newScopeDefinition.add newAssignment(newIdentNode(k), v)
|
||||
|
||||
result = quote:
|
||||
template scopeHolder = `newScopeDefinition`
|
||||
|
||||
template scope(newBindings: untyped) {.dirty.} =
|
||||
mergeScopes(bindSym"scopeHolder", newBindings)
|
||||
|
||||
type
|
||||
TextLogRecord = object
|
||||
line: string
|
||||
|
||||
StdoutLogRecord = object
|
||||
|
||||
template setProperty(r: var TextLogRecord, key: string, val: string, isFirst: bool) =
|
||||
if not first: r.line.add ", "
|
||||
r.line.add key
|
||||
r.line.add "="
|
||||
r.line.add val
|
||||
|
||||
template setEventName(r: var StdoutLogRecord, name: string) =
|
||||
stdout.write(name & ": ")
|
||||
|
||||
template setProperty(r: var StdoutLogRecord, key: string, val: auto, isFirst: bool) =
|
||||
when not isFirst: stdout.write ", "
|
||||
stdout.write key
|
||||
stdout.write "="
|
||||
stdout.write $val
|
||||
|
||||
template flushRecord(r: var StdoutLogRecord) =
|
||||
stdout.write "\n"
|
||||
stdout.flushFile
|
||||
|
||||
macro logImpl(scopeHolders: typed,
|
||||
logStmtProps: varargs[untyped]): untyped =
|
||||
let lexicalScope = scopeHolders.lastScopeHolder.getImpl.actualBody
|
||||
var finalBindings = initOrderedTable[string, NimNode]()
|
||||
|
||||
for k, v in bindings(lexicalScope, skip = 1):
|
||||
finalBindings[k] = v
|
||||
|
||||
for k, v in bindings(logStmtProps, skip = 1):
|
||||
finalBindings[k] = v
|
||||
|
||||
finalBindings.sort(system.cmp)
|
||||
|
||||
let eventName = logStmtProps[0]
|
||||
assert eventName.kind in {nnkStrLit}
|
||||
let record = genSym(nskVar, "record")
|
||||
|
||||
result = quote:
|
||||
var `record`: StdoutLogRecord
|
||||
setEventName(`record`, `eventName`)
|
||||
|
||||
var isFirst = true
|
||||
for k, v in finalBindings:
|
||||
result.add newCall(newIdentNode"setProperty",
|
||||
record, newLit(k), v, newLit(isFirst))
|
||||
isFirst = false
|
||||
|
||||
result.add newCall(newIdentNode"flushRecord", record)
|
||||
|
||||
template log(props: varargs[untyped]) {.dirty.} =
|
||||
logImpl(bindSym"scopeHolder", props)
|
||||
|
||||
scope:
|
||||
a = 12
|
||||
b = "original-b"
|
||||
|
||||
scope:
|
||||
x = 16
|
||||
b = "overriden-b"
|
||||
|
||||
scope:
|
||||
c = 100
|
||||
|
||||
proc main =
|
||||
scope:
|
||||
c = 10
|
||||
|
||||
scope:
|
||||
z = 20
|
||||
|
||||
log("main started", a = 10, b = "inner-b", d = "some-d")
|
||||
|
||||
main()
|
||||
|
||||
log("exiting", msg = "bye bye")
|
||||
|
||||
173
tests/macros/ttemplatesymbols.nim
Normal file
173
tests/macros/ttemplatesymbols.nim
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
import
|
||||
macros, algorithm, strutils
|
||||
|
||||
proc normalProc(x: int) =
|
||||
echo x
|
||||
|
||||
template templateWithtouParams =
|
||||
echo 10
|
||||
|
||||
proc overloadedProc(x: int) =
|
||||
echo x
|
||||
|
||||
proc overloadedProc(x: string) =
|
||||
echo x
|
||||
|
||||
proc overloadedProc[T](x: T) =
|
||||
echo x
|
||||
|
||||
template normalTemplate(x: int) =
|
||||
echo x
|
||||
|
||||
template overloadedTemplate(x: int) =
|
||||
echo x
|
||||
|
||||
template overloadedTemplate(x: string) =
|
||||
echo x
|
||||
|
||||
macro normalMacro(x: int): untyped =
|
||||
discard
|
||||
|
||||
macro macroWithoutParams: untyped =
|
||||
discard
|
||||
|
||||
macro inspectSymbol(sym: typed, expected: static[string]): untyped =
|
||||
if sym.kind == nnkSym:
|
||||
echo "Symbol node:"
|
||||
let res = sym.getImpl.repr & "\n"
|
||||
echo res
|
||||
# echo "|", res, "|"
|
||||
# echo "|", expected, "|"
|
||||
if expected.len > 0: assert res == expected
|
||||
elif sym.kind in {nnkClosedSymChoice, nnkOpenSymChoice}:
|
||||
echo "Begin sym choice:"
|
||||
var results = newSeq[string](0)
|
||||
for innerSym in sym:
|
||||
results.add innerSym.getImpl.repr
|
||||
sort(results, cmp[string])
|
||||
let res = results.join("\n") & "\n"
|
||||
echo res
|
||||
if expected.len > 0: assert res == expected
|
||||
echo "End symchoice."
|
||||
else:
|
||||
echo "Non-symbol node: ", sym.kind
|
||||
if expected.len > 0: assert $sym.kind == expected
|
||||
|
||||
macro inspectUntyped(sym: untyped, expected: static[string]): untyped =
|
||||
let res = sym.repr
|
||||
echo "Untyped node: ", res
|
||||
assert res == expected
|
||||
|
||||
inspectSymbol templateWithtouParams, "nnkCommand"
|
||||
# this template is expanded, because bindSym was not used
|
||||
# the end result is the template body (nnkCommand)
|
||||
|
||||
inspectSymbol bindSym("templateWithtouParams"), """
|
||||
template templateWithtouParams() =
|
||||
echo 10
|
||||
|
||||
"""
|
||||
|
||||
inspectSymbol macroWithoutParams, "nnkEmpty"
|
||||
# Just like the template above, the macro was expanded
|
||||
|
||||
inspectSymbol bindSym("macroWithoutParams"), """
|
||||
macro macroWithoutParams(): untyped =
|
||||
discard
|
||||
|
||||
"""
|
||||
|
||||
inspectSymbol normalMacro, """
|
||||
macro normalMacro(x: int): untyped =
|
||||
discard
|
||||
|
||||
"""
|
||||
# Since the normalMacro has params, it's automatically
|
||||
# treated as a symbol here (no need for `bindSym`)
|
||||
|
||||
inspectSymbol bindSym("normalMacro"), """
|
||||
macro normalMacro(x: int): untyped =
|
||||
discard
|
||||
|
||||
"""
|
||||
|
||||
inspectSymbol normalTemplate, """
|
||||
template normalTemplate(x: int) =
|
||||
echo x
|
||||
|
||||
"""
|
||||
|
||||
inspectSymbol bindSym("normalTemplate"), """
|
||||
template normalTemplate(x: int) =
|
||||
echo x
|
||||
|
||||
"""
|
||||
|
||||
inspectSymbol overloadedTemplate, """
|
||||
template overloadedTemplate(x: int) =
|
||||
echo x
|
||||
|
||||
template overloadedTemplate(x: string) =
|
||||
echo x
|
||||
|
||||
"""
|
||||
|
||||
inspectSymbol bindSym("overloadedTemplate"), """
|
||||
template overloadedTemplate(x: int) =
|
||||
echo x
|
||||
|
||||
template overloadedTemplate(x: string) =
|
||||
echo x
|
||||
|
||||
"""
|
||||
|
||||
inspectUntyped bindSym("overloadedTemplate"), """bindSym("overloadedTemplate")"""
|
||||
# binSym is active only in the presense of `typed` params.
|
||||
# `untyped` params still get the raw AST
|
||||
|
||||
inspectSymbol normalProc, """
|
||||
proc normalProc(x: int) =
|
||||
echo [x]
|
||||
|
||||
"""
|
||||
|
||||
inspectSymbol bindSym("normalProc"), """
|
||||
proc normalProc(x: int) =
|
||||
echo [x]
|
||||
|
||||
"""
|
||||
|
||||
inspectSymbol overloadedProc, """
|
||||
proc overloadedProc(x: int) =
|
||||
echo [x]
|
||||
|
||||
proc overloadedProc(x: string) =
|
||||
echo [x]
|
||||
|
||||
proc overloadedProc[T](x: T) =
|
||||
echo x
|
||||
|
||||
"""
|
||||
# XXX: There seems to be a repr rendering problem above.
|
||||
# Notice that `echo [x]`
|
||||
|
||||
inspectSymbol overloadedProc[float], """
|
||||
proc overloadedProc(x: T) =
|
||||
echo [x]
|
||||
|
||||
"""
|
||||
# As expected, when we select a specific generic, the
|
||||
# AST is no longer a symChoice
|
||||
|
||||
inspectSymbol bindSym("overloadedProc"), """
|
||||
proc overloadedProc(x: int) =
|
||||
echo [x]
|
||||
|
||||
proc overloadedProc(x: string) =
|
||||
echo [x]
|
||||
|
||||
proc overloadedProc[T](x: T) =
|
||||
echo x
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -388,13 +388,13 @@ type
|
|||
cdecl.}
|
||||
|
||||
##cp property emulators
|
||||
template defGetter(otype: typedesc, memberType: typedesc, memberName: expr, procName: expr): stmt {.immediate.} =
|
||||
template defGetter(otype: typedesc, memberType: typedesc, memberName, procName: untyped) =
|
||||
proc `get procName`*(obj: otype): memberType {.cdecl.} =
|
||||
return obj.memberName
|
||||
template defSetter(otype: typedesc, memberType: typedesc, memberName: expr, procName: expr): stmt {.immediate.} =
|
||||
template defSetter(otype: typedesc, memberType: typedesc, memberName, procName: untyped) =
|
||||
proc `set procName`*(obj: otype, value: memberType) {.cdecl.} =
|
||||
obj.memberName = value
|
||||
template defProp(otype: typedesc, memberType: typedesc, memberName: expr, procName: expr): stmt {.immediate.} =
|
||||
template defProp(otype: typedesc, memberType: typedesc, memberName, procName: untyped) =
|
||||
defGetter(otype, memberType, memberName, procName)
|
||||
defSetter(otype, memberType, memberName, procName)
|
||||
|
||||
|
|
@ -908,7 +908,7 @@ proc getShapes*(arb: PArbiter, a, b: var PShape) {.inline.} =
|
|||
|
||||
#/ A macro shortcut for defining and retrieving the shapes from an arbiter.
|
||||
#define CP_ARBITER_GET_SHAPES(arb, a, b) cpShape *a, *b; cpArbiterGetShapes(arb, &a, &b);
|
||||
template getShapes*(arb: PArbiter, name1, name2: expr): stmt {.immediate.} =
|
||||
template getShapes*(arb: PArbiter, name1, name2: untyped) =
|
||||
var name1, name2: PShape
|
||||
getShapes(arb, name1, name2)
|
||||
|
||||
|
|
@ -923,7 +923,7 @@ template getShapes*(arb: PArbiter, name1, name2: expr): stmt {.immediate.} =
|
|||
|
||||
#/ A macro shortcut for defining and retrieving the bodies from an arbiter.
|
||||
#define CP_ARBITER_GET_BODIES(arb, a, b) cpBody *a, *b; cpArbiterGetBodies(arb, &a, &b);
|
||||
template getBodies*(arb: PArbiter, name1, name2: expr): stmt {.immediate.} =
|
||||
template getBodies*(arb: PArbiter, name1, name2: untyped) =
|
||||
var name1, name2: PBOdy
|
||||
getBodies(arb, name1, name2)
|
||||
|
||||
|
|
@ -947,11 +947,11 @@ proc getDepth*(arb: PArbiter; i: cint): CpFloat {.
|
|||
cdecl, importc: "cpArbiterGetDepth", dynlib: Lib.}
|
||||
|
||||
##Shapes
|
||||
template defShapeSetter(memberType: typedesc, memberName: expr, procName: expr, activates: bool): stmt {.immediate.} =
|
||||
template defShapeSetter(memberType: typedesc, memberName: untyped, procName: untyped, activates: bool) =
|
||||
proc `set procName`*(obj: PShape, value: memberType) {.cdecl.} =
|
||||
if activates and obj.body != nil: obj.body.activate()
|
||||
obj.memberName = value
|
||||
template defShapeProp(memberType: typedesc, memberName: expr, procName: expr, activates: bool): stmt {.immediate.} =
|
||||
template defShapeProp(memberType: typedesc, memberName: untyped, procName: untyped, activates: bool) =
|
||||
defGetter(PShape, memberType, memberName, procName)
|
||||
defShapeSetter(memberType, memberName, procName, activates)
|
||||
|
||||
|
|
@ -1272,11 +1272,11 @@ proc activateBodies(constraint: PConstraint) {.inline.} =
|
|||
# cpConstraintActivateBodies(constraint); \
|
||||
# constraint->member = value; \
|
||||
# }
|
||||
template defConstraintSetter(memberType: typedesc, member: expr, name: expr): stmt {.immediate.} =
|
||||
template defConstraintSetter(memberType: typedesc, member, name: untyped) =
|
||||
proc `set name`*(constraint: PConstraint, value: memberType) {.cdecl.} =
|
||||
activateBodies(constraint)
|
||||
constraint.member = value
|
||||
template defConstraintProp(memberType: typedesc, member: expr, name: expr): stmt {.immediate.} =
|
||||
template defConstraintProp(memberType: typedesc, member, name: untyped) =
|
||||
defGetter(PConstraint, memberType, member, name)
|
||||
defConstraintSetter(memberType, member, name)
|
||||
# CP_DefineConstraintStructGetter(cpSpace*, CP_PRIVATE(space), Space)
|
||||
|
|
@ -1306,18 +1306,18 @@ proc getImpulse*(constraint: PConstraint): CpFloat {.inline.} =
|
|||
# cpConstraintActivateBodies(constraint); \
|
||||
# ((struct *)constraint)->member = value; \
|
||||
# }
|
||||
template constraintCheckCast(constraint: PConstraint, ctype: expr): stmt {.immediate.} =
|
||||
template constraintCheckCast(constraint: PConstraint, ctype: untyped) =
|
||||
assert(constraint.klass == `ctype getClass`(), "Constraint is the wrong class")
|
||||
template defCGetter(ctype: expr, memberType: typedesc, member: expr, name: expr): stmt {.immediate.} =
|
||||
template defCGetter(ctype: untyped, memberType: typedesc, member, name: untyped) =
|
||||
proc `get ctype name`*(constraint: PConstraint): memberType {.cdecl.} =
|
||||
constraintCheckCast(constraint, ctype)
|
||||
result = cast[`P ctype`](constraint).member
|
||||
template defCSetter(ctype: expr, memberType: typedesc, member: expr, name: expr): stmt {.immediate.} =
|
||||
template defCSetter(ctype: untyped, memberType: typedesc, member, name: untyped) =
|
||||
proc `set ctype name`*(constraint: PConstraint, value: memberType) {.cdecl.} =
|
||||
constraintCheckCast(constraint, ctype)
|
||||
activateBodies(constraint)
|
||||
cast[`P ctype`](constraint).member = value
|
||||
template defCProp(ctype: expr, memberType: typedesc, member: expr, name: expr): stmt {.immediate.} =
|
||||
template defCProp(ctype: untyped, memberType: typedesc, member, name: untyped) =
|
||||
defCGetter(ctype, memberType, member, name)
|
||||
defCSetter(ctype, memberType, member, name)
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ const
|
|||
ENET_VERSION_MAJOR* = 1
|
||||
ENET_VERSION_MINOR* = 3
|
||||
ENET_VERSION_PATCH* = 3
|
||||
template ENET_VERSION_CREATE(major, minor, patch: expr): expr =
|
||||
template ENET_VERSION_CREATE(major, minor, patch: untyped): untyped =
|
||||
(((major) shl 16) or ((minor) shl 8) or (patch))
|
||||
|
||||
const
|
||||
|
|
@ -277,22 +277,22 @@ when defined(Linux) or true:
|
|||
dataLength*: csize
|
||||
TENetSocketSet* = Tfd_set
|
||||
## see if these are different on win32, if not then get rid of these
|
||||
template ENET_HOST_TO_NET_16*(value: expr): expr =
|
||||
template ENET_HOST_TO_NET_16*(value: untyped): untyped =
|
||||
(htons(value))
|
||||
template ENET_HOST_TO_NET_32*(value: expr): expr =
|
||||
template ENET_HOST_TO_NET_32*(value: untyped): untyped =
|
||||
(htonl(value))
|
||||
template ENET_NET_TO_HOST_16*(value: expr): expr =
|
||||
template ENET_NET_TO_HOST_16*(value: untyped): untyped =
|
||||
(ntohs(value))
|
||||
template ENET_NET_TO_HOST_32*(value: expr): expr =
|
||||
template ENET_NET_TO_HOST_32*(value: untyped): untyped =
|
||||
(ntohl(value))
|
||||
|
||||
template ENET_SOCKETSET_EMPTY*(sockset: expr): expr =
|
||||
template ENET_SOCKETSET_EMPTY*(sockset: untyped): untyped =
|
||||
FD_ZERO(addr((sockset)))
|
||||
template ENET_SOCKETSET_ADD*(sockset, socket: expr): expr =
|
||||
template ENET_SOCKETSET_ADD*(sockset, socket: untyped): untyped =
|
||||
FD_SET(socket, addr((sockset)))
|
||||
template ENET_SOCKETSET_REMOVE*(sockset, socket: expr): expr =
|
||||
template ENET_SOCKETSET_REMOVE*(sockset, socket: untyped): untyped =
|
||||
FD_CLEAR(socket, addr((sockset)))
|
||||
template ENET_SOCKETSET_CHECK*(sockset, socket: expr): expr =
|
||||
template ENET_SOCKETSET_CHECK*(sockset, socket: untyped): untyped =
|
||||
FD_ISSET(socket, addr((sockset)))
|
||||
|
||||
when defined(Windows):
|
||||
|
|
@ -606,7 +606,7 @@ proc protocolCommandSize*(commandNumber: cuchar): csize{.
|
|||
|
||||
{.pop.}
|
||||
|
||||
from hashes import `!$`, `!&`, THash, hash
|
||||
proc hash*(x: TAddress): THash {.nimcall, noSideEffect.} =
|
||||
from hashes import `!$`, `!&`, Hash, hash
|
||||
proc hash*(x: TAddress): Hash {.nimcall, noSideEffect.} =
|
||||
result = !$(hash(x.host.int32) !& hash(x.port.int16))
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
import macros, macro_dsl, estreams
|
||||
from strutils import format
|
||||
|
||||
template newLenName(): stmt {.immediate.} =
|
||||
template newLenName() =
|
||||
let lenName {.inject.} = ^("len"& $lenNames)
|
||||
inc(lenNames)
|
||||
|
||||
template defPacketImports*(): stmt {.immediate, dirty.} =
|
||||
template defPacketImports*() {.dirty.} =
|
||||
import macros, macro_dsl, estreams
|
||||
from strutils import format
|
||||
|
||||
macro defPacket*(typeNameN: expr, typeFields: expr): stmt {.immediate.} =
|
||||
macro defPacket*(typeNameN: untyped, typeFields: untyped): untyped =
|
||||
result = newNimNode(nnkStmtList)
|
||||
let
|
||||
typeName = quoted2ident(typeNameN)
|
||||
|
|
@ -60,7 +60,7 @@ macro defPacket*(typeNameN: expr, typeFields: expr): stmt {.immediate.} =
|
|||
let
|
||||
name = typeFields[i][0]
|
||||
dotName = packetID.dot(name)
|
||||
resName = newIdentNode(!"result").dot(name)
|
||||
resName = newIdentNode("result").dot(name)
|
||||
case typeFields[i][1].kind
|
||||
of nnkBracketExpr: #ex: paddedstring[32, '\0'], array[range, type]
|
||||
case $typeFields[i][1][0].ident
|
||||
|
|
@ -141,7 +141,7 @@ macro defPacket*(typeNameN: expr, typeFields: expr): stmt {.immediate.} =
|
|||
|
||||
const emptyFields = {nnkEmpty, nnkNilLit}
|
||||
var objFields = newNimNode(nnkRecList)
|
||||
for i in 0.. < len(typeFields):
|
||||
for i in 0 ..< len(typeFields):
|
||||
let fname = typeFields[i][0]
|
||||
constructorParams.add(newNimNode(nnkIdentDefs).und(
|
||||
fname,
|
||||
|
|
@ -200,7 +200,7 @@ proc iddefs*(a: string; b: NimNode): NimNode {.compileTime.} =
|
|||
proc varTy*(a: NimNode): NimNode {.compileTime.} =
|
||||
result = newNimNode(nnkVarTy).und(a)
|
||||
|
||||
macro forwardPacket*(typeName: expr, underlyingType: expr): stmt {.immediate.} =
|
||||
macro forwardPacket*(typeName: untyped, underlyingType: untyped): untyped =
|
||||
var
|
||||
packetID = ^"p"
|
||||
streamID = ^"s"
|
||||
|
|
@ -234,7 +234,7 @@ macro forwardPacket*(typeName: expr, underlyingType: expr): stmt {.immediate.} =
|
|||
echo "unknown type:", repr(underlyingtype)
|
||||
echo(repr(result))
|
||||
|
||||
template forwardPacketT*(typeName: expr; underlyingType: expr): stmt {.dirty, immediate.} =
|
||||
template forwardPacketT*(typeName: untyped; underlyingType: untyped) {.dirty.} =
|
||||
proc `read typeName`*(buffer: PBuffer): typeName =
|
||||
#discard readData(s, addr result, sizeof(result))
|
||||
var res: underlyingType
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ proc und*(a: NimNode; b: varargs[NimNode]): NimNode {.compileTime.} =
|
|||
|
||||
proc `^`*(a: string): NimNode {.compileTime.} =
|
||||
## new ident node
|
||||
result = newIdentNode(!a)
|
||||
result = newIdentNode(a)
|
||||
proc `[]`*(a, b: NimNode): NimNode {.compileTime.} =
|
||||
## new bracket expression: node[node] not to be confused with node[indx]
|
||||
result = newNimNode(nnkBracketExpr).und(a, b)
|
||||
|
|
@ -34,7 +34,7 @@ proc emptyNode*(): NimNode {.compileTime.} =
|
|||
proc dot*(left, right: NimNode): NimNode {.compileTime.} =
|
||||
result = newNimNode(nnkDotExpr).und(left, right)
|
||||
proc prefix*(a: string, b: NimNode): NimNode {.compileTime.} =
|
||||
result = newNimNode(nnkPrefix).und(newIdentNode(!a), b)
|
||||
result = newNimNode(nnkPrefix).und(newIdentNode(a), b)
|
||||
|
||||
proc quoted2ident*(a: NimNode): NimNode {.compileTime.} =
|
||||
if a.kind != nnkAccQuoted:
|
||||
|
|
@ -45,13 +45,13 @@ proc quoted2ident*(a: NimNode): NimNode {.compileTime.} =
|
|||
result = ^pname
|
||||
|
||||
|
||||
macro `?`(a: expr): expr =
|
||||
macro `?`(a: untyped): untyped =
|
||||
## Character literal ?A #=> 'A'
|
||||
result = ($a[1].ident)[0].lit
|
||||
## echo(?F,?a,?t,?t,?y)
|
||||
|
||||
when isMainModule:
|
||||
macro foo(x: stmt): stmt =
|
||||
macro foo(x: untyped) =
|
||||
result = newNimNode(nnkStmtList)
|
||||
result.add(newNimNode(nnkCall).und(!!"echo", "Hello thar".lit))
|
||||
result.add(newCall("echo", lit("3 * 45 = "), (3.lit.infix("*", 45.lit))))
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ proc write*(buffer: PBuffer; val: var string) =
|
|||
setLen buffer.data, buffer.pos + length.int
|
||||
copyMem(addr buffer.data[buffer.pos], addr val[0], length.int)
|
||||
inc buffer.pos, length.int
|
||||
proc write*[T: TNumber|bool|char|byte](buffer: PBuffer; val: T) =
|
||||
proc write*[T: SomeNumber|bool|char|byte](buffer: PBuffer; val: T) =
|
||||
var v: T
|
||||
shallowCopy v, val
|
||||
writeBE buffer, v
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ type
|
|||
TInputFinishedProc* = proc()
|
||||
TKeyCallback = proc()
|
||||
PKeyClient* = ref object
|
||||
onKeyDown: TTable[int32, TKeyCallback]
|
||||
onKeyUp: TTable[int32, TKeyCallback]
|
||||
onKeyDown: Table[int32, TKeyCallback]
|
||||
onKeyUp: Table[int32, TKeyCallback]
|
||||
name: string
|
||||
PTextInput* = ref object
|
||||
text*: string
|
||||
|
|
@ -134,5 +134,5 @@ iterator pollEvents*(window: PRenderWindow): PEvent =
|
|||
of EvtMouseButtonReleased: addButtonEvent(event.mouseButton.button, up)
|
||||
of EvtTextEntered: recordText(activeInput, event.text)
|
||||
of EvtMouseMoved: setMousePos(event.mouseMove.x, event.mouseMove.y)
|
||||
else: nil
|
||||
else: discard
|
||||
yield(addr event)
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ type
|
|||
of Projectile:
|
||||
bullet*: PBulletRecord
|
||||
else:
|
||||
nil
|
||||
discard
|
||||
PBulletRecord* = ref TBulletRecord
|
||||
TBulletRecord* = object
|
||||
id*: int16
|
||||
|
|
@ -115,10 +115,10 @@ var
|
|||
cfg: PZoneSettings
|
||||
SpriteSheets* = initTable[string, PSpriteSheet](64)
|
||||
SoundCache * = initTable[string, PSoundRecord](64)
|
||||
nameToVehID*: TTable[string, int]
|
||||
nameToItemID*: TTable[string, int]
|
||||
nameToObjID*: TTable[string, int]
|
||||
nameToBulletID*: TTable[string, int]
|
||||
nameToVehID*: Table[string, int]
|
||||
nameToItemID*: Table[string, int]
|
||||
nameToObjID*: Table[string, int]
|
||||
nameToBulletID*: Table[string, int]
|
||||
activeState = Lobby
|
||||
|
||||
proc newSprite*(filename: string; errors: var seq[string]): PSpriteSheet
|
||||
|
|
@ -126,7 +126,7 @@ proc load*(ss: PSpriteSheet): bool {.discardable.}
|
|||
proc newSound*(filename: string; errors: var seq[string]): PSoundRecord
|
||||
proc load*(s: PSoundRecord): bool {.discardable.}
|
||||
|
||||
proc validateSettings*(settings: PJsonNode; errors: var seq[string]): bool
|
||||
proc validateSettings*(settings: JsonNode; errors: var seq[string]): bool
|
||||
proc loadSettings*(rawJson: string, errors: var seq[string]): bool
|
||||
proc loadSettingsFromFile*(filename: string, errors: var seq[string]): bool
|
||||
|
||||
|
|
@ -135,17 +135,17 @@ proc fetchItm*(itm: string): PItemRecord
|
|||
proc fetchObj*(name: string): PObjectRecord
|
||||
proc fetchBullet(name: string): PBulletRecord
|
||||
|
||||
proc importLevel(data: PJsonNode; errors: var seq[string]): PLevelSettings
|
||||
proc importVeh(data: PJsonNode; errors: var seq[string]): PVehicleRecord
|
||||
proc importObject(data: PJsonNode; errors: var seq[string]): PObjectRecord
|
||||
proc importItem(data: PJsonNode; errors: var seq[string]): PItemRecord
|
||||
proc importPhys(data: PJsonNode): TPhysicsRecord
|
||||
proc importAnim(data: PJsonNode; errors: var seq[string]): PAnimationRecord
|
||||
proc importHandling(data: PJsonNode): THandlingRecord
|
||||
proc importBullet(data: PJsonNode; errors: var seq[string]): PBulletRecord
|
||||
proc importSoul(data: PJsonNode): TSoulRecord
|
||||
proc importExplosion(data: PJsonNode; errors: var seq[string]): TExplosionRecord
|
||||
proc importSound*(data: PJsonNode; errors: var seq[string]; fieldName: string = nil): PSoundRecord
|
||||
proc importLevel(data: JsonNode; errors: var seq[string]): PLevelSettings
|
||||
proc importVeh(data: JsonNode; errors: var seq[string]): PVehicleRecord
|
||||
proc importObject(data: JsonNode; errors: var seq[string]): PObjectRecord
|
||||
proc importItem(data: JsonNode; errors: var seq[string]): PItemRecord
|
||||
proc importPhys(data: JsonNode): TPhysicsRecord
|
||||
proc importAnim(data: JsonNode; errors: var seq[string]): PAnimationRecord
|
||||
proc importHandling(data: JsonNode): THandlingRecord
|
||||
proc importBullet(data: JsonNode; errors: var seq[string]): PBulletRecord
|
||||
proc importSoul(data: JsonNode): TSoulRecord
|
||||
proc importExplosion(data: JsonNode; errors: var seq[string]): TExplosionRecord
|
||||
proc importSound*(data: JsonNode; errors: var seq[string]; fieldName: string = nil): PSoundRecord
|
||||
|
||||
## this is the only pipe between lobby and main.nim
|
||||
proc getActiveState*(): TGameState =
|
||||
|
|
@ -203,7 +203,7 @@ iterator playableVehicles*(): PVehicleRecord =
|
|||
if v.playable:
|
||||
yield v
|
||||
|
||||
template allAssets*(body: stmt) {.dirty.}=
|
||||
template allAssets*(body: untyped) {.dirty.}=
|
||||
block:
|
||||
var assetType = FGraphics
|
||||
for file, asset in pairs(SpriteSheets):
|
||||
|
|
@ -212,7 +212,7 @@ template allAssets*(body: stmt) {.dirty.}=
|
|||
for file, asset in pairs(SoundCache):
|
||||
body
|
||||
|
||||
template cacheImpl(procName, cacheName, resultType: expr; body: stmt) {.dirty, immediate.} =
|
||||
template cacheImpl(procName, cacheName, resultType, body: untyped) {.dirty.} =
|
||||
proc procName*(filename: string; errors: var seq[string]): resulttype =
|
||||
if hasKey(cacheName, filename):
|
||||
return cacheName[filename]
|
||||
|
|
@ -220,7 +220,7 @@ template cacheImpl(procName, cacheName, resultType: expr; body: stmt) {.dirty, i
|
|||
body
|
||||
cacheName[filename] = result
|
||||
|
||||
template checkFile(path: expr): stmt {.dirty, immediate.} =
|
||||
template checkFile(path: untyped) {.dirty.} =
|
||||
if not existsFile(path):
|
||||
errors.add("File missing: "& path)
|
||||
|
||||
|
|
@ -243,7 +243,7 @@ proc expandPath*(assetType: TAssetType; fileName: string): string =
|
|||
case assetType
|
||||
of FGraphics: result.add "gfx/"
|
||||
of FSound: result.add "sfx/"
|
||||
else: nil
|
||||
else: discard
|
||||
result.add fileName
|
||||
proc expandPath*(fc: ScFileChallenge): string {.inline.} =
|
||||
result = expandPath(fc.assetType, fc.file)
|
||||
|
|
@ -280,10 +280,10 @@ else:
|
|||
if not s.soundBuf.isNil:
|
||||
result = true
|
||||
|
||||
template addError(e: expr): stmt {.immediate.} =
|
||||
template addError(e: untyped) =
|
||||
errors.add(e)
|
||||
result = false
|
||||
proc validateSettings*(settings: PJsonNode, errors: var seq[string]): bool =
|
||||
proc validateSettings*(settings: JsonNode, errors: var seq[string]): bool =
|
||||
result = true
|
||||
if settings.kind != JObject:
|
||||
addError("Settings root must be an object")
|
||||
|
|
@ -328,10 +328,10 @@ proc loadSettingsFromFile*(filename: string, errors: var seq[string]): bool =
|
|||
result = loadSettings(readFile(filename), errors)
|
||||
|
||||
proc loadSettings*(rawJson: string, errors: var seq[string]): bool =
|
||||
var settings: PJsonNode
|
||||
var settings: JsonNode
|
||||
try:
|
||||
settings = parseJson(rawJson)
|
||||
except EJsonParsingError:
|
||||
except JsonParsingError:
|
||||
errors.add("JSON parsing error: "& getCurrentExceptionMsg())
|
||||
return
|
||||
except:
|
||||
|
|
@ -407,21 +407,21 @@ proc fetchObj*(name: string): PObjectRecord =
|
|||
proc fetchBullet(name: string): PBulletRecord =
|
||||
return cfg.bullets[nameToBulletID[name]]
|
||||
|
||||
proc getField(node: PJsonNode, field: string, target: var float) =
|
||||
proc getField(node: JsonNode, field: string, target: var float) =
|
||||
if not node.hasKey(field):
|
||||
return
|
||||
if node[field].kind == JFloat:
|
||||
target = node[field].fnum
|
||||
elif node[field].kind == JInt:
|
||||
target = node[field].num.float
|
||||
proc getField(node: PJsonNode, field: string, target: var int) =
|
||||
proc getField(node: JsonNode, field: string, target: var int) =
|
||||
if not node.hasKey(field):
|
||||
return
|
||||
if node[field].kind == JInt:
|
||||
target = node[field].num.int
|
||||
elif node[field].kind == JFloat:
|
||||
target = node[field].fnum.int
|
||||
proc getField(node: PJsonNode; field: string; target: var bool) =
|
||||
proc getField(node: JsonNode; field: string; target: var bool) =
|
||||
if not node.hasKey(field):
|
||||
return
|
||||
case node[field].kind
|
||||
|
|
@ -431,19 +431,19 @@ proc getField(node: PJsonNode; field: string; target: var bool) =
|
|||
target = (node[field].num != 0)
|
||||
of JFloat:
|
||||
target = (node[field].fnum != 0.0)
|
||||
else: nil
|
||||
else: discard
|
||||
|
||||
template checkKey(node: expr; key: string): stmt =
|
||||
template checkKey(node: untyped; key: string) =
|
||||
if not hasKey(node, key):
|
||||
return
|
||||
|
||||
proc importTrail(data: PJsonNode; errors: var seq[string]): TTrailRecord =
|
||||
proc importTrail(data: JsonNode; errors: var seq[string]): TTrailRecord =
|
||||
checkKey(data, "trail")
|
||||
result.anim = importAnim(data["trail"], errors)
|
||||
result.timer = 1000.0
|
||||
getField(data["trail"], "timer", result.timer)
|
||||
result.timer /= 1000.0
|
||||
proc importLevel(data: PJsonNode; errors: var seq[string]): PLevelSettings =
|
||||
proc importLevel(data: JsonNode; errors: var seq[string]): PLevelSettings =
|
||||
new(result)
|
||||
result.size = vec2i(5000, 5000)
|
||||
result.starfield = @[]
|
||||
|
|
@ -456,7 +456,7 @@ proc importLevel(data: PJsonNode; errors: var seq[string]): PLevelSettings =
|
|||
if level.hasKey("starfield"):
|
||||
for star in level["starfield"].items:
|
||||
result.starfield.add(newSprite(star.str, errors))
|
||||
proc importPhys(data: PJsonNode): TPhysicsRecord =
|
||||
proc importPhys(data: JsonNode): TPhysicsRecord =
|
||||
result.radius = 20.0
|
||||
result.mass = 10.0
|
||||
|
||||
|
|
@ -466,7 +466,7 @@ proc importPhys(data: PJsonNode): TPhysicsRecord =
|
|||
phys.getField("mass", result.mass)
|
||||
when not defined(NoChipmunk):
|
||||
result.moment = momentForCircle(result.mass, 0.0, result.radius, VectorZero) * MomentMult
|
||||
proc importHandling(data: PJsonNode): THandlingRecord =
|
||||
proc importHandling(data: JsonNode): THandlingRecord =
|
||||
result.thrust = 45.0
|
||||
result.topSpeed = 100.0 #unused
|
||||
result.reverse = 30.0
|
||||
|
|
@ -483,7 +483,7 @@ proc importHandling(data: PJsonNode): THandlingRecord =
|
|||
hand.getField("reverse", result.reverse)
|
||||
hand.getField("strafe", result.strafe)
|
||||
hand.getField("rotation", result.rotation)
|
||||
proc importAnim(data: PJsonNode, errors: var seq[string]): PAnimationRecord =
|
||||
proc importAnim(data: JsonNode, errors: var seq[string]): PAnimationRecord =
|
||||
new(result)
|
||||
result.angle = 0.0
|
||||
result.delay = 1000.0
|
||||
|
|
@ -502,26 +502,26 @@ proc importAnim(data: PJsonNode, errors: var seq[string]): PAnimationRecord =
|
|||
|
||||
result.angle = radians(result.angle) ## comes in as degrees
|
||||
result.delay /= 1000 ## delay comes in as milliseconds
|
||||
proc importSoul(data: PJsonNode): TSoulRecord =
|
||||
proc importSoul(data: JsonNode): TSoulRecord =
|
||||
result.energy = 10000
|
||||
result.health = 1
|
||||
checkKey(data, "soul")
|
||||
let soul = data["soul"]
|
||||
soul.getField("energy", result.energy)
|
||||
soul.getField("health", result.health)
|
||||
proc importExplosion(data: PJsonNode; errors: var seq[string]): TExplosionRecord =
|
||||
proc importExplosion(data: JsonNode; errors: var seq[string]): TExplosionRecord =
|
||||
checkKey(data, "explode")
|
||||
let expl = data["explode"]
|
||||
result.anim = importAnim(expl, errors)
|
||||
result.sound = importSound(expl, errors, "sound")
|
||||
proc importSound*(data: PJsonNode; errors: var seq[string]; fieldName: string = nil): PSoundRecord =
|
||||
proc importSound*(data: JsonNode; errors: var seq[string]; fieldName: string = nil): PSoundRecord =
|
||||
if data.kind == JObject:
|
||||
checkKey(data, fieldName)
|
||||
result = newSound(data[fieldName].str, errors)
|
||||
elif data.kind == JString:
|
||||
result = newSound(data.str, errors)
|
||||
|
||||
proc importVeh(data: PJsonNode; errors: var seq[string]): PVehicleRecord =
|
||||
proc importVeh(data: JsonNode; errors: var seq[string]): PVehicleRecord =
|
||||
new(result)
|
||||
result.playable = false
|
||||
if data.kind != JArray or data.len != 2 or
|
||||
|
|
@ -538,7 +538,7 @@ proc importVeh(data: PJsonNode; errors: var seq[string]): PVehicleRecord =
|
|||
vehdata.getField("playable", result.playable)
|
||||
if result.anim.spriteSheet.isNil and result.playable:
|
||||
result.playable = false
|
||||
proc importObject(data: PJsonNode; errors: var seq[string]): PObjectRecord =
|
||||
proc importObject(data: JsonNode; errors: var seq[string]): PObjectRecord =
|
||||
new(result)
|
||||
if data.kind != JArray or data.len != 2:
|
||||
result.name = "(broken)"
|
||||
|
|
@ -546,7 +546,7 @@ proc importObject(data: PJsonNode; errors: var seq[string]): PObjectRecord =
|
|||
result.name = data[0].str
|
||||
result.anim = importAnim(data[1], errors)
|
||||
result.physics = importPhys(data[1])
|
||||
proc importItem(data: PJsonNode; errors: var seq[string]): PItemRecord =
|
||||
proc importItem(data: JsonNode; errors: var seq[string]): PItemRecord =
|
||||
new(result)
|
||||
if data.kind != JArray or data.len != 3:
|
||||
result.name = "(broken)"
|
||||
|
|
@ -576,15 +576,15 @@ proc importItem(data: PJsonNode; errors: var seq[string]): PItemRecord =
|
|||
of "ammo":
|
||||
result.kind = Ammo
|
||||
of "utility":
|
||||
nil
|
||||
discard
|
||||
else:
|
||||
errors.add "Invalid item type \""&data[1].str&"\" for item "&result.name
|
||||
|
||||
proc importBullet(data: PJsonNode; errors: var seq[string]): PBulletRecord =
|
||||
proc importBullet(data: JsonNode; errors: var seq[string]): PBulletRecord =
|
||||
new(result)
|
||||
result.id = -1
|
||||
|
||||
var bdata: PJsonNode
|
||||
var bdata: JsonNode
|
||||
if data.kind == JArray:
|
||||
result.name = data[0].str
|
||||
bdata = data[1]
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@ defPacketImports()
|
|||
type
|
||||
PacketID* = char
|
||||
|
||||
template idpacket(pktName, id, s2c, c2s: expr): stmt {.immediate, dirty.} =
|
||||
template idpacket(pktName, id, s2c, c2s: untyped) {.dirty.} =
|
||||
let `H pktName`* {.inject.} = id
|
||||
defPacket(`Sc pktName`, s2c)
|
||||
defPacket(`Cs pktName`, c2s)
|
||||
|
||||
forwardPacketT(uint8, int8)
|
||||
forwardPacketT(uint16, int16)
|
||||
forwardPacketT(TPort, int16)
|
||||
forwardPacketT(Port, int16)
|
||||
|
||||
idPacket(Login, 'a',
|
||||
tuple[id: int32; alias: string; sessionKey: string],
|
||||
|
|
@ -22,7 +22,7 @@ defPacket(CsZoneJoinReq, tuple[session: ScLogin])
|
|||
|
||||
defPacket(ScZoneRecord, tuple[
|
||||
name: string = "", desc: string = "",
|
||||
ip: string = "", port: TPort = 0.Tport])
|
||||
ip: string = "", port: Port = 0.Port])
|
||||
idPacket(ZoneList, 'z',
|
||||
tuple[network: string = "", zones: seq[ScZoneRecord]],
|
||||
tuple[time: string])
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ type
|
|||
type1 = typedesc
|
||||
type2 = typedesc
|
||||
|
||||
proc typePairs(A, B: type1; C, D: type2) = nil
|
||||
proc typePairs(A, B: type1; C, D: type2) = discard
|
||||
|
||||
accept typePairs(int, int, TFoo, TFOO)
|
||||
accept typePairs(TBAR, TBar, TBAR, TBAR)
|
||||
|
|
@ -55,7 +55,7 @@ accept typePairs(int, int, string, string)
|
|||
reject typePairs(TBAR, TBar, TBar, TFoo)
|
||||
reject typePairs(string, int, TBAR, TBAR)
|
||||
|
||||
proc typePairs2[T: typedesc, U: typedesc](A, B: T; C, D: U) = nil
|
||||
proc typePairs2[T: typedesc, U: typedesc](A, B: T; C, D: U) = discard
|
||||
|
||||
accept typePairs2(int, int, TFoo, TFOO)
|
||||
accept typePairs2(TBAR, TBar, TBAR, TBAR)
|
||||
|
|
@ -71,12 +71,12 @@ proc dontBind(a: typedesc, b: typedesc) =
|
|||
accept dontBind(int, float)
|
||||
accept dontBind(TFoo, TFoo)
|
||||
|
||||
proc dontBind2(a, b: typedesc) = nil
|
||||
proc dontBind2(a, b: typedesc) = discard
|
||||
|
||||
accept dontBind2(int, float)
|
||||
accept dontBind2(TBar, int)
|
||||
|
||||
proc bindArg(T: typedesc, U: typedesc, a, b: T, c, d: U) = nil
|
||||
proc bindArg(T: typedesc, U: typedesc, a, b: T, c, d: U) = discard
|
||||
|
||||
accept bindArg(int, string, 10, 20, "test", "nest")
|
||||
accept bindArg(int, int, 10, 20, 30, 40)
|
||||
|
|
|
|||
23
tests/misc/tsemfold.nim
Normal file
23
tests/misc/tsemfold.nim
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
discard """
|
||||
action: run
|
||||
"""
|
||||
|
||||
doAssertRaises(OverflowError): discard low(int8) - 1'i8
|
||||
doAssertRaises(OverflowError): discard high(int8) + 1'i8
|
||||
doAssertRaises(OverflowError): discard abs(low(int8))
|
||||
doAssertRaises(DivByZeroError): discard 1 mod 0
|
||||
doAssertRaises(DivByZeroError): discard 1 div 0
|
||||
doAssertRaises(OverflowError): discard low(int8) div -1'i8
|
||||
|
||||
doAssertRaises(OverflowError): discard low(int64) - 1'i64
|
||||
doAssertRaises(OverflowError): discard high(int64) + 1'i64
|
||||
|
||||
type E = enum eA, eB
|
||||
doAssertRaises(OverflowError): discard eA.pred
|
||||
doAssertRaises(OverflowError): discard eB.succ
|
||||
|
||||
doAssertRaises(OverflowError): discard low(int8) * -1
|
||||
doAssertRaises(OverflowError): discard low(int64) * -1
|
||||
doAssertRaises(OverflowError): discard high(int8) * 2
|
||||
doAssertRaises(OverflowError): discard high(int64) * 2
|
||||
|
||||
|
|
@ -40,11 +40,11 @@ proc mustRehash(length, counter: int): bool {.inline.} =
|
|||
assert(length > counter)
|
||||
result = (length * 2 < counter * 3) or (length - counter < 4)
|
||||
|
||||
proc nextTry(h, maxHash: THash): THash {.inline.} =
|
||||
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
|
||||
result = ((5 * h) + 1) and maxHash
|
||||
|
||||
template rawGetImpl() =
|
||||
var h: THash = hash(key) and high(t.data) # start with real hash value
|
||||
var h: Hash = hash(key) and high(t.data) # start with real hash value
|
||||
while t.data[h].slot != seEmpty:
|
||||
if t.data[h].key == key and t.data[h].slot == seFilled:
|
||||
return h
|
||||
|
|
@ -52,7 +52,7 @@ template rawGetImpl() =
|
|||
result = -1
|
||||
|
||||
template rawInsertImpl() =
|
||||
var h: THash = hash(key) and high(data)
|
||||
var h: Hash = hash(key) and high(data)
|
||||
while data[h].slot == seFilled:
|
||||
h = nextTry(h, high(data))
|
||||
data[h].key = key
|
||||
|
|
@ -162,7 +162,7 @@ iterator values*[A](t: TCountTable[A]): int =
|
|||
if t.data[h].val != 0: yield t.data[h].val
|
||||
|
||||
proc RawGet[A](t: TCountTable[A], key: A): int =
|
||||
var h: THash = hash(key) and high(t.data) # start with real hash value
|
||||
var h: Hash = hash(key) and high(t.data) # start with real hash value
|
||||
while t.data[h].val != 0:
|
||||
if t.data[h].key == key: return h
|
||||
h = nextTry(h, high(t.data))
|
||||
|
|
@ -181,7 +181,7 @@ proc hasKey*[A](t: TCountTable[A], key: A): bool =
|
|||
|
||||
proc rawInsert[A](t: TCountTable[A], data: var seq[tuple[key: A, val: int]],
|
||||
key: A, val: int) =
|
||||
var h: THash = hash(key) and high(data)
|
||||
var h: Hash = hash(key) and high(data)
|
||||
while data[h].val != 0: h = nextTry(h, high(data))
|
||||
data[h].key = key
|
||||
data[h].val = val
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
discard """
|
||||
errormsg: "number 0x123'u8 out of valid range"
|
||||
errormsg: "number out of range: '0x123'u8'"
|
||||
"""
|
||||
|
||||
# Bug #1179
|
||||
|
|
|
|||
8
tests/misc/åäö.nim
Normal file
8
tests/misc/åäö.nim
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
discard """
|
||||
action: run
|
||||
"""
|
||||
|
||||
# Tests that module names can contain multi byte characters
|
||||
|
||||
let a = 1
|
||||
doAssert åäö.a == 1
|
||||
4
tests/modules/definitions.nim
Normal file
4
tests/modules/definitions.nim
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
var v*: int
|
||||
proc p* = echo "proc p called"
|
||||
template t* = echo "template t expanded"
|
||||
|
||||
3
tests/modules/proxy_module.nim
Normal file
3
tests/modules/proxy_module.nim
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import definitions
|
||||
export definitions except p
|
||||
|
||||
|
|
@ -3,6 +3,7 @@ discard """
|
|||
"""
|
||||
|
||||
# bug #6682
|
||||
{.experimental: "notnil".}
|
||||
|
||||
type
|
||||
Fields = enum
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ discard """
|
|||
line: 22
|
||||
errormsg: "type mismatch"
|
||||
"""
|
||||
|
||||
{.experimental: "notnil".}
|
||||
type
|
||||
PObj = ref TObj not nil
|
||||
TObj = object
|
||||
|
|
@ -15,8 +15,8 @@ type
|
|||
proc p(x: string not nil): int =
|
||||
result = 45
|
||||
|
||||
proc q(x: MyString) = nil
|
||||
proc q2(x: string) = nil
|
||||
proc q(x: MyString) = discard
|
||||
proc q2(x: string) = discard
|
||||
|
||||
q2(nil)
|
||||
q(nil)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ discard """
|
|||
"""
|
||||
|
||||
import strutils
|
||||
|
||||
{.experimental: "notnil".}
|
||||
|
||||
type
|
||||
TObj = object
|
||||
|
|
@ -18,13 +18,13 @@ proc q(s: superstring) =
|
|||
echo s
|
||||
|
||||
proc p2() =
|
||||
var a: string = "I am not nil"
|
||||
var a: string = "I am not nil"
|
||||
q(a) # but this should and does not
|
||||
|
||||
p2()
|
||||
|
||||
proc q(x: pointer not nil) =
|
||||
nil
|
||||
discard
|
||||
|
||||
proc p() =
|
||||
var x: pointer
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@ discard """
|
|||
"""
|
||||
|
||||
import strutils
|
||||
|
||||
{.experimental: "notnil".}
|
||||
|
||||
type
|
||||
TObj = object
|
||||
x, y: int
|
||||
|
||||
proc q(x: pointer not nil) =
|
||||
nil
|
||||
discard
|
||||
|
||||
proc p() =
|
||||
var x: pointer
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ discard """
|
|||
|
||||
# bug #584
|
||||
# Testprogram for 'not nil' check
|
||||
|
||||
{.experimental: "notnil".}
|
||||
const testWithResult = true
|
||||
|
||||
type
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ discard ""
|
|||
type
|
||||
TObj = ref object
|
||||
|
||||
{.experimental: "notnil".}
|
||||
|
||||
proc check(a: TObj not nil) =
|
||||
echo repr(a)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ discard """
|
|||
"""
|
||||
|
||||
# bug #2216
|
||||
{.experimental: "notnil".}
|
||||
|
||||
type
|
||||
A[T] = ref object
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ discard """
|
|||
errormsg: "fields not initialized: bar"
|
||||
line: "13"
|
||||
"""
|
||||
|
||||
{.experimental: "notnil".}
|
||||
# bug #2355
|
||||
type
|
||||
Foo = object
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ discard """
|
|||
import strutils
|
||||
|
||||
{.warning[ProveField]: on.}
|
||||
|
||||
{.experimental: "notnil".}
|
||||
type
|
||||
TNodeKind = enum
|
||||
nkBinary, nkTernary, nkStr
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
discard """
|
||||
line: 11
|
||||
errormsg: "identifier expected, but found 'keyword of'"
|
||||
errormsg: "identifier expected, but got 'keyword of'"
|
||||
"""
|
||||
|
||||
type
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ discard """
|
|||
type
|
||||
PTest = ref object
|
||||
|
||||
proc test(x: PTest, y: int) = nil
|
||||
proc test(x: PTest, y: int) = discard
|
||||
|
||||
var buf: PTest
|
||||
buf.test()
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@ output: '''baz
|
|||
a
|
||||
b
|
||||
c
|
||||
x: 1, y: test 1
|
||||
x: 2, y: test 2
|
||||
x: 10, y: test 3
|
||||
x: 4, y: test 4
|
||||
'''
|
||||
"""
|
||||
|
||||
|
|
@ -35,3 +39,15 @@ templateForwarding fooVarargs, "test".len > 3, Foo(x: 10), Foo(x: 100), Foo(x: 1
|
|||
|
||||
procForwarding "a", "b", "c"
|
||||
|
||||
proc hasKeywordArgs(x = 10, y = "y") =
|
||||
echo "x: ", x, ", y: ", y
|
||||
|
||||
proc hasRegularArgs(x: int, y: string) =
|
||||
echo "x: ", x, ", y: ", y
|
||||
|
||||
templateForwarding(hasRegularArgs, true, 1, "test 1")
|
||||
templateForwarding hasKeywordArgs, true, 2, "test 2"
|
||||
|
||||
templateForwarding(hasKeywordArgs, true, y = "test 3")
|
||||
templateForwarding hasKeywordArgs, true, y = "test 4", x = 4
|
||||
|
||||
|
|
|
|||
|
|
@ -1,432 +0,0 @@
|
|||
#? braces
|
||||
|
||||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements some common generic algorithms.
|
||||
|
||||
type
|
||||
SortOrder* = enum { ## sort order
|
||||
Descending, Ascending
|
||||
}
|
||||
|
||||
|
||||
type(
|
||||
DummyAlias = int
|
||||
OtherAlias = distinct char
|
||||
|
||||
SomeObject = object of RootObj { ## declaration here
|
||||
fieldA, fieldB: int
|
||||
case order: SortOrder {
|
||||
of Descending {a: string}
|
||||
of Ascending {b: seq[char]}
|
||||
}
|
||||
}
|
||||
|
||||
MyConcept = concept x {
|
||||
x is int
|
||||
}
|
||||
)
|
||||
|
||||
{.deprecated: [TSortOrder: SortOrder].}
|
||||
|
||||
|
||||
proc `*`*(x: int, order: SortOrder): int @inline {
|
||||
## flips `x` if ``order == Descending``;
|
||||
## if ``order == Ascending`` then `x` is returned.
|
||||
## `x` is supposed to be the result of a comparator, ie ``< 0`` for
|
||||
## *less than*, ``== 0`` for *equal*, ``> 0`` for *greater than*.
|
||||
var y = order.ord - 1
|
||||
result = (x xor y) - y
|
||||
}
|
||||
|
||||
proc fill*[T](a: var openArray[T], first, last: Natural, value: T) {
|
||||
## fills the array ``a[first..last]`` with `value`.
|
||||
var x = first
|
||||
while x <= last {
|
||||
a[x] = value
|
||||
inc(x)
|
||||
}
|
||||
}
|
||||
|
||||
proc fill*[T](a: var openArray[T], value: T) {
|
||||
## fills the array `a` with `value`.
|
||||
fill(a, 0, a.high, value)
|
||||
}
|
||||
|
||||
proc reverse*[T](a: var openArray[T], first, last: Natural) {
|
||||
## reverses the array ``a[first..last]``.
|
||||
var x = first
|
||||
var y = last
|
||||
while x < y {
|
||||
swap(a[x], a[y])
|
||||
dec(y)
|
||||
inc(x)
|
||||
}
|
||||
}
|
||||
|
||||
proc reverse*[T](a: var openArray[T]) {
|
||||
## reverses the array `a`.
|
||||
reverse(a, 0, a.high)
|
||||
}
|
||||
|
||||
proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T] {
|
||||
## returns the reverse of the array `a[first..last]`.
|
||||
assert last >= first-1
|
||||
var i = last - first
|
||||
var x = first.int
|
||||
result = newSeq[T](i + 1)
|
||||
while i >= 0 {
|
||||
result[i] = a[x]
|
||||
dec(i)
|
||||
inc(x)
|
||||
}
|
||||
}
|
||||
|
||||
proc reversed*[T](a: openArray[T]): seq[T] {
|
||||
## returns the reverse of the array `a`.
|
||||
reversed(a, 0, a.high)
|
||||
}
|
||||
|
||||
proc binarySearch*[T](a: openArray[T], key: T): int {
|
||||
## binary search for `key` in `a`. Returns -1 if not found.
|
||||
var b = len(a)
|
||||
while result < b {
|
||||
var mid = (result + b) div 2
|
||||
if a[mid] < key { result = mid + 1 } else { b = mid }
|
||||
}
|
||||
if result >= len(a) or a[result] != key { result = -1 }
|
||||
}
|
||||
|
||||
proc smartBinarySearch*[T](a: openArray[T], key: T): int {
|
||||
## ``a.len`` must be a power of 2 for this to work.
|
||||
var step = a.len div 2
|
||||
while step > 0 {
|
||||
if a[result or step] <= key { result = result or step }
|
||||
step = step shr 1
|
||||
}
|
||||
if a[result] != key { result = -1 }
|
||||
}
|
||||
|
||||
const (
|
||||
onlySafeCode = true
|
||||
)
|
||||
|
||||
proc lowerBound*[T](a: openArray[T], key: T, cmp: proc(x,y: T): int @closure): int {
|
||||
## same as binarySearch except that if key is not in `a` then this
|
||||
## returns the location where `key` would be if it were. In other
|
||||
## words if you have a sorted sequence and you call
|
||||
## insert(thing, elm, lowerBound(thing, elm))
|
||||
## the sequence will still be sorted.
|
||||
##
|
||||
## `cmp` is the comparator function to use, the expected return values are
|
||||
## the same as that of system.cmp.
|
||||
##
|
||||
## example::
|
||||
##
|
||||
## var arr = @[1,2,3,5,6,7,8,9]
|
||||
## arr.insert(4, arr.lowerBound(4))
|
||||
## # after running the above arr is `[1,2,3,4,5,6,7,8,9]`
|
||||
result = a.low
|
||||
var count = a.high - a.low + 1
|
||||
var step, pos: int
|
||||
while count != 0 {
|
||||
step = count div 2
|
||||
pos = result + step
|
||||
if cmp(a[pos], key) < 0 {
|
||||
result = pos + 1
|
||||
count -= step + 1
|
||||
} else {
|
||||
count = step
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proc lowerBound*[T](a: openArray[T], key: T): int { lowerBound(a, key, cmp[T]) }
|
||||
proc merge[T](a, b: var openArray[T], lo, m, hi: int,
|
||||
cmp: proc (x, y: T): int @closure, order: SortOrder) {
|
||||
template `<-` (a, b) {
|
||||
when false {
|
||||
a = b
|
||||
} elif onlySafeCode {
|
||||
shallowCopy(a, b)
|
||||
} else {
|
||||
copyMem(addr(a), addr(b), sizeof(T))
|
||||
}
|
||||
}
|
||||
# optimization: If max(left) <= min(right) there is nothing to do!
|
||||
# 1 2 3 4 ## 5 6 7 8
|
||||
# -> O(n) for sorted arrays.
|
||||
# On random data this safes up to 40% of merge calls
|
||||
if cmp(a[m], a[m+1]) * order <= 0 { return }
|
||||
var j = lo
|
||||
# copy a[j..m] into b:
|
||||
assert j <= m
|
||||
when onlySafeCode {
|
||||
var bb = 0
|
||||
while j <= m {
|
||||
b[bb] <- a[j]
|
||||
inc(bb)
|
||||
inc(j)
|
||||
}
|
||||
} else {
|
||||
copyMem(addr(b[0]), addr(a[j]), sizeof(T)*(m-j+1))
|
||||
j = m+1
|
||||
}
|
||||
var i = 0
|
||||
var k = lo
|
||||
# copy proper element back:
|
||||
while k < j and j <= hi {
|
||||
if cmp(b[i], a[j]) * order <= 0 {
|
||||
a[k] <- b[i]
|
||||
inc(i)
|
||||
} else {
|
||||
a[k] <- a[j]
|
||||
inc(j)
|
||||
}
|
||||
inc(k)
|
||||
}
|
||||
# copy rest of b:
|
||||
when onlySafeCode {
|
||||
while k < j {
|
||||
a[k] <- b[i]
|
||||
inc(k)
|
||||
inc(i)
|
||||
}
|
||||
} else {
|
||||
if k < j { copyMem(addr(a[k]), addr(b[i]), sizeof(T)*(j-k)) }
|
||||
}
|
||||
}
|
||||
|
||||
proc sort*[T](a: var openArray[T],
|
||||
cmp: proc (x, y: T): int @closure,
|
||||
order = SortOrder.Ascending) {
|
||||
## Default Nim sort (an implementation of merge sort). The sorting
|
||||
## is guaranteed to be stable and the worst case is guaranteed to
|
||||
## be O(n log n).
|
||||
## The current implementation uses an iterative
|
||||
## mergesort to achieve this. It uses a temporary sequence of
|
||||
## length ``a.len div 2``. Currently Nim does not support a
|
||||
## sensible default argument for ``cmp``, so you have to provide one
|
||||
## of your own. However, the ``system.cmp`` procs can be used:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## sort(myIntArray, system.cmp[int])
|
||||
##
|
||||
## # do not use cmp[string] here as we want to use the specialized
|
||||
## # overload:
|
||||
## sort(myStrArray, system.cmp)
|
||||
##
|
||||
## You can inline adhoc comparison procs with the `do notation
|
||||
## <manual.html#procedures-do-notation>`_. Example:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## people.sort do (x, y: Person) -> int:
|
||||
## result = cmp(x.surname, y.surname)
|
||||
## if result == 0:
|
||||
## result = cmp(x.name, y.name)
|
||||
var n = a.len
|
||||
var b: seq[T]
|
||||
newSeq(b, n div 2)
|
||||
var s = 1
|
||||
while s < n {
|
||||
var m = n-1-s
|
||||
while m >= 0 {
|
||||
merge(a, b, max(m-s+1, 0), m, m+s, cmp, order)
|
||||
dec(m, s*2)
|
||||
}
|
||||
s = s*2
|
||||
}
|
||||
}
|
||||
|
||||
proc sorted*[T](a: openArray[T], cmp: proc(x, y: T): int {.closure.},
|
||||
order = SortOrder.Ascending): seq[T] {
|
||||
## returns `a` sorted by `cmp` in the specified `order`.
|
||||
result = newSeq[T](a.len)
|
||||
for i in 0 .. a.high { result[i] = a[i] }
|
||||
sort(result, cmp, order)
|
||||
}
|
||||
|
||||
template sortedByIt*(seq1, op: untyped): untyped {
|
||||
## Convenience template around the ``sorted`` proc to reduce typing.
|
||||
##
|
||||
## The template injects the ``it`` variable which you can use directly in an
|
||||
## expression. Example:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## type Person = tuple[name: string, age: int]
|
||||
## var
|
||||
## p1: Person = (name: "p1", age: 60)
|
||||
## p2: Person = (name: "p2", age: 20)
|
||||
## p3: Person = (name: "p3", age: 30)
|
||||
## p4: Person = (name: "p4", age: 30)
|
||||
## people = @[p1,p2,p4,p3]
|
||||
##
|
||||
## echo people.sortedByIt(it.name)
|
||||
##
|
||||
## Because the underlying ``cmp()`` is defined for tuples you can do
|
||||
## a nested sort like in the following example:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## echo people.sortedByIt((it.age, it.name))
|
||||
##
|
||||
var result = sorted(seq1, proc(x, y: type[seq1[0]]): int {
|
||||
var it {.inject.} = x
|
||||
let a = op
|
||||
it = y
|
||||
let b = op
|
||||
result = cmp(a, b)
|
||||
})
|
||||
result
|
||||
}
|
||||
|
||||
proc isSorted*[T](a: openarray[T],
|
||||
cmp: proc(x, y: T): int {.closure.},
|
||||
order = SortOrder.Ascending): bool {
|
||||
## Checks to see whether `a` is already sorted in `order`
|
||||
## using `cmp` for the comparison. Parameters identical
|
||||
## to `sort`
|
||||
result = true
|
||||
for i in 0..<len(a)-1 {
|
||||
case cmp(a[i],a[i+1]) * order > 0 {
|
||||
of true { return false }
|
||||
of false {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proc product*[T](x: openArray[seq[T]]): seq[seq[T]] {
|
||||
## produces the Cartesian product of the array. Warning: complexity
|
||||
## may explode.
|
||||
result = newSeq[seq[T]]()
|
||||
if x.len == 0 { return }
|
||||
if x.len == 1 {
|
||||
result = @x
|
||||
return
|
||||
}
|
||||
var (
|
||||
indexes = newSeq[int](x.len)
|
||||
initial = newSeq[int](x.len)
|
||||
index = 0
|
||||
)
|
||||
var next = newSeq[T]()
|
||||
next.setLen(x.len)
|
||||
for i in 0..(x.len-1) {
|
||||
if len(x[i]) == 0 { return }
|
||||
initial[i] = len(x[i])-1
|
||||
}
|
||||
indexes = initial
|
||||
while true {
|
||||
while indexes[index] == -1 {
|
||||
indexes[index] = initial[index]
|
||||
index += 1
|
||||
if index == x.len { return }
|
||||
indexes[index] -= 1
|
||||
}
|
||||
for ni, i in indexes {
|
||||
next[ni] = x[ni][i]
|
||||
}
|
||||
var res: seq[T]
|
||||
shallowCopy(res, next)
|
||||
result.add(res)
|
||||
index = 0
|
||||
indexes[index] -= 1
|
||||
}
|
||||
}
|
||||
|
||||
proc nextPermutation*[T](x: var openarray[T]): bool {.discardable.} {
|
||||
## Calculates the next lexicographic permutation, directly modifying ``x``.
|
||||
## The result is whether a permutation happened, otherwise we have reached
|
||||
## the last-ordered permutation.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## var v = @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
## v.nextPermutation()
|
||||
## echo v # @[0, 1, 2, 3, 4, 5, 6, 7, 9, 8]
|
||||
if x.len < 2 {
|
||||
return false }
|
||||
|
||||
var i = x.high
|
||||
while i > 0 and x[i-1] >= x[i] { dec i }
|
||||
if i == 0 { return false }
|
||||
|
||||
var j = x.high
|
||||
while j >= i and x[j] <= x[i-1] { dec j }
|
||||
|
||||
swap x[j], x[i-1]
|
||||
x.reverse(i, x.high)
|
||||
|
||||
result = true
|
||||
}
|
||||
|
||||
proc prevPermutation*[T](x: var openarray[T]): bool @discardable {
|
||||
## Calculates the previous lexicographic permutation, directly modifying
|
||||
## ``x``. The result is whether a permutation happened, otherwise we have
|
||||
## reached the first-ordered permutation.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## var v = @[0, 1, 2, 3, 4, 5, 6, 7, 9, 8]
|
||||
## v.prevPermutation()
|
||||
## echo v # @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
if x.len < 2 { return false }
|
||||
|
||||
var i = x.high
|
||||
while i > 0 and x[i-1] <= x[i] {
|
||||
dec i
|
||||
}
|
||||
if i == 0 { return false }
|
||||
|
||||
x.reverse(i, x.high)
|
||||
|
||||
var j = x.high
|
||||
while j >= i and x[j-1] < x[i-1] {
|
||||
dec j
|
||||
}
|
||||
swap x[i-1], x[j]
|
||||
|
||||
result = true
|
||||
}
|
||||
|
||||
when isMainModule {
|
||||
# Tests for lowerBound
|
||||
var arr = @[1,2,3,5,6,7,8,9]
|
||||
assert arr.lowerBound(0) == 0
|
||||
assert arr.lowerBound(4) == 3
|
||||
assert arr.lowerBound(5) == 3
|
||||
assert arr.lowerBound(10) == 8
|
||||
arr = @[1,5,10]
|
||||
try {
|
||||
assert arr.lowerBound(4) == 1
|
||||
assert arr.lowerBound(5) == 1
|
||||
assert arr.lowerBound(6) == 2
|
||||
} except ValueError {}
|
||||
# Tests for isSorted
|
||||
var srt1 = [1,2,3,4,4,4,4,5]
|
||||
var srt2 = ["iello","hello"]
|
||||
var srt3 = [1.0,1.0,1.0]
|
||||
var srt4: seq[int] = @[]
|
||||
assert srt1.isSorted(cmp) == true
|
||||
assert srt2.isSorted(cmp) == false
|
||||
assert srt3.isSorted(cmp) == true
|
||||
var srtseq = newSeq[int]()
|
||||
assert srtseq.isSorted(cmp) == true
|
||||
# Tests for reversed
|
||||
var arr1 = @[0,1,2,3,4]
|
||||
assert arr1.reversed() == @[4,3,2,1,0]
|
||||
for i in 0 .. high(arr1) {
|
||||
assert arr1.reversed(0, i) == arr1.reversed()[high(arr1) - i .. high(arr1)]
|
||||
assert arr1.reversed(i, high(arr1)) == arr1.reversed()[0 .. high(arr1) - i]
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,10 @@ discard """
|
|||
output: '''140
|
||||
5-120-120
|
||||
359
|
||||
77'''
|
||||
77
|
||||
-4
|
||||
-1
|
||||
-1'''
|
||||
"""
|
||||
#import math
|
||||
import sequtils
|
||||
|
|
@ -25,3 +28,11 @@ let a = [2,4,8].map do (d:int) -> int: d + 1
|
|||
echo a[0], a[1], a[2]
|
||||
|
||||
echo(foo 8, foo 8)
|
||||
|
||||
# bug #7582
|
||||
proc f(x: int): int = x
|
||||
|
||||
echo f -4
|
||||
|
||||
echo int -1 # doesn't compile
|
||||
echo int `-` 1 # compiles
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
discard """
|
||||
file: "tinvcolonlocation1.nim"
|
||||
line: 8
|
||||
column: 3
|
||||
errormsg: "':' expected"
|
||||
column: 7
|
||||
errormsg: "expected: ':', but got: 'echo'"
|
||||
"""
|
||||
try #<- missing ':'
|
||||
echo "try"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
discard """
|
||||
file: "tinvcolonlocation2.nim"
|
||||
line: 11
|
||||
column: 1
|
||||
errormsg: "':' expected"
|
||||
column: 8
|
||||
errormsg: "expected: ':', but got: 'keyword finally'"
|
||||
"""
|
||||
try:
|
||||
echo "try"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
discard """
|
||||
file: "tinvcolonlocation3.nim"
|
||||
line: 12
|
||||
column: 3
|
||||
errormsg: "':' expected"
|
||||
column: 7
|
||||
errormsg: "expected: ':', but got: 'echo'"
|
||||
"""
|
||||
try:
|
||||
echo "try"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
discard """
|
||||
errormsg: "identifier expected, but found 'keyword when'"
|
||||
errormsg: "identifier expected, but got 'keyword when'"
|
||||
"""
|
||||
|
||||
# bug #2123
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
discard """
|
||||
errormsg: '''identifier expected, but found 'echo 4'''
|
||||
errormsg: '''in expression '4 2': identifier expected, but found '4'''
|
||||
line: 6
|
||||
"""
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,6 @@ discard """
|
|||
errormsg: "redefinition of \'foo\'"
|
||||
"""
|
||||
|
||||
proc foo(a: int, b: string) = nil
|
||||
proc foo(a: int, b: string) = nil
|
||||
proc foo(a: int, b: string) = discard
|
||||
proc foo(a: int, b: string) = discard
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ type
|
|||
TRange = range[0..40]
|
||||
|
||||
proc p(r: TRange) =
|
||||
nil
|
||||
discard
|
||||
|
||||
var
|
||||
r: TRange
|
||||
|
|
|
|||
|
|
@ -66,6 +66,18 @@ block: # "IpAddress/Sockaddr conversion"
|
|||
doAssert(ipaddr_1 == ipaddr_2)
|
||||
doAssert($ipaddr_1 == $ipaddr_2)
|
||||
|
||||
if sockaddr.ss_family == AF_INET.toInt:
|
||||
var sockaddr4: Sockaddr_in
|
||||
copyMem(addr sockaddr4, addr sockaddr, sizeof(sockaddr4))
|
||||
fromSockAddr(sockaddr4, socklen, ipaddr_2, port_2)
|
||||
elif sockaddr.ss_family == AF_INET6.toInt:
|
||||
var sockaddr6: Sockaddr_in6
|
||||
copyMem(addr sockaddr6, addr sockaddr, sizeof(sockaddr6))
|
||||
fromSockAddr(sockaddr6, socklen, ipaddr_2, port_2)
|
||||
|
||||
doAssert(ipaddr_1 == ipaddr_2)
|
||||
doAssert($ipaddr_1 == $ipaddr_2)
|
||||
|
||||
|
||||
# ipv6 address of example.com
|
||||
test("2606:2800:220:1:248:1893:25c8:1946")
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ Raises
|
|||
true
|
||||
true
|
||||
true
|
||||
true
|
||||
'''
|
||||
"""
|
||||
# test os path creation, iteration, and deletion
|
||||
|
|
@ -129,3 +130,12 @@ echo fileExists("../dest/a/b/file.txt")
|
|||
|
||||
echo fileExists("../dest/a/b/c/fileC.txt")
|
||||
removeDir("../dest")
|
||||
|
||||
# Test get/set modification times
|
||||
# Should support at least microsecond resolution
|
||||
import times
|
||||
let tm = fromUnix(0) + 100.microseconds
|
||||
writeFile("a", "")
|
||||
setLastModificationTime("a", tm)
|
||||
echo getLastModificationTime("a") == tm
|
||||
removeFile("a")
|
||||
|
|
@ -10,4 +10,47 @@ proc `$`(o: Obj): string = "foobar"
|
|||
|
||||
var o: Obj
|
||||
doAssert fmt"{o}" == "foobar"
|
||||
doAssert fmt"{o:10}" == "foobar "
|
||||
doAssert fmt"{o:10}" == "foobar "
|
||||
|
||||
# see issue #7933
|
||||
var str = "abc"
|
||||
doAssert fmt">7.1 :: {str:>7.1}" == ">7.1 :: a"
|
||||
doAssert fmt">7.2 :: {str:>7.2}" == ">7.2 :: ab"
|
||||
doAssert fmt">7.3 :: {str:>7.3}" == ">7.3 :: abc"
|
||||
doAssert fmt">7.9 :: {str:>7.9}" == ">7.9 :: abc"
|
||||
doAssert fmt">7.0 :: {str:>7.0}" == ">7.0 :: "
|
||||
doAssert fmt" 7.1 :: {str:7.1}" == " 7.1 :: a "
|
||||
doAssert fmt" 7.2 :: {str:7.2}" == " 7.2 :: ab "
|
||||
doAssert fmt" 7.3 :: {str:7.3}" == " 7.3 :: abc "
|
||||
doAssert fmt" 7.9 :: {str:7.9}" == " 7.9 :: abc "
|
||||
doAssert fmt" 7.0 :: {str:7.0}" == " 7.0 :: "
|
||||
doAssert fmt"^7.1 :: {str:^7.1}" == "^7.1 :: a "
|
||||
doAssert fmt"^7.2 :: {str:^7.2}" == "^7.2 :: ab "
|
||||
doAssert fmt"^7.3 :: {str:^7.3}" == "^7.3 :: abc "
|
||||
doAssert fmt"^7.9 :: {str:^7.9}" == "^7.9 :: abc "
|
||||
doAssert fmt"^7.0 :: {str:^7.0}" == "^7.0 :: "
|
||||
str = "äöüe\u0309\u0319o\u0307\u0359"
|
||||
doAssert fmt"^7.1 :: {str:^7.1}" == "^7.1 :: ä "
|
||||
doAssert fmt"^7.2 :: {str:^7.2}" == "^7.2 :: äö "
|
||||
doAssert fmt"^7.3 :: {str:^7.3}" == "^7.3 :: äöü "
|
||||
doAssert fmt"^7.0 :: {str:^7.0}" == "^7.0 :: "
|
||||
# this is actually wrong, but the unicode module has no support for graphemes
|
||||
doAssert fmt"^7.4 :: {str:^7.4}" == "^7.4 :: äöüe "
|
||||
doAssert fmt"^7.9 :: {str:^7.9}" == "^7.9 :: äöüe\u0309\u0319o\u0307\u0359"
|
||||
|
||||
# see issue #7932
|
||||
doAssert fmt"{15:08}" == "00000015" # int, works
|
||||
doAssert fmt"{1.5:08}" == "000001.5" # float, works
|
||||
doAssert fmt"{1.5:0>8}" == "000001.5" # workaround using fill char works for positive floats
|
||||
doAssert fmt"{-1.5:0>8}" == "0000-1.5" # even that does not work for negative floats
|
||||
doAssert fmt"{-1.5:08}" == "-00001.5" # works
|
||||
doAssert fmt"{1.5:+08}" == "+00001.5" # works
|
||||
doAssert fmt"{1.5: 08}" == " 00001.5" # works
|
||||
|
||||
# only add explicitly requested sign if value != -0.0 (neg zero)
|
||||
doAssert fmt"{-0.0:g}" == "-0"
|
||||
doassert fmt"{-0.0:+g}" == "-0"
|
||||
doassert fmt"{-0.0: g}" == "-0"
|
||||
doAssert fmt"{0.0:g}" == "0"
|
||||
doAssert fmt"{0.0:+g}" == "+0"
|
||||
doAssert fmt"{0.0: g}" == " 0"
|
||||
|
|
|
|||
|
|
@ -28,6 +28,12 @@ t.checkFormat("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
|
|||
|
||||
t.checkFormat("yyyyMMddhhmmss", "20380119031407")
|
||||
|
||||
# issue 7620
|
||||
let t7620_am = parse("4/15/2017 12:01:02 AM +0", "M/d/yyyy' 'h:mm:ss' 'tt' 'z", utc())
|
||||
t7620_am.checkFormat("M/d/yyyy' 'h:mm:ss' 'tt' 'z", "4/15/2017 12:01:02 AM +0")
|
||||
let t7620_pm = parse("4/15/2017 12:01:02 PM +0", "M/d/yyyy' 'h:mm:ss' 'tt' 'z", utc())
|
||||
t7620_pm.checkFormat("M/d/yyyy' 'h:mm:ss' 'tt' 'z", "4/15/2017 12:01:02 PM +0")
|
||||
|
||||
let t2 = fromUnix(160070789).utc # Mon 27 Jan 16:06:29 GMT 1975
|
||||
t2.checkFormat("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
|
||||
" ss t tt y yy yyy yyyy yyyyy z zz zzz",
|
||||
|
|
|
|||
10
tests/stdlib/tunittestexceptiontype.nim
Normal file
10
tests/stdlib/tunittestexceptiontype.nim
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
discard """
|
||||
exitcode: 1
|
||||
outputsub: '''exception type is [ValueError]'''
|
||||
"""
|
||||
|
||||
import unittest
|
||||
|
||||
suite "exception from test":
|
||||
test "show exception type":
|
||||
raise newException(ValueError, "exception type is")
|
||||
|
|
@ -6,6 +6,7 @@ doAssert "@[23, 45]" == $(@[23, 45])
|
|||
doAssert "[32, 45]" == $([32, 45])
|
||||
doAssert """@["", "foo", "bar"]""" == $(@["", "foo", "bar"])
|
||||
doAssert """["", "foo", "bar"]""" == $(["", "foo", "bar"])
|
||||
doAssert """["", "foo", "bar"]""" == $(@["", "foo", "bar"].toOpenArray(0, 2))
|
||||
|
||||
# bug #2395
|
||||
let alphaSet: set[char] = {'a'..'c'}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@ discard """
|
|||
2
|
||||
3
|
||||
4
|
||||
2
|
||||
1
|
||||
2
|
||||
3
|
||||
'''
|
||||
"""
|
||||
|
||||
|
|
@ -47,3 +51,38 @@ foo(toOpenArray(arr, 8, 12))
|
|||
|
||||
var seqq = @[1, 2, 3, 4, 5]
|
||||
foo(toOpenArray(seqq, 1, 3))
|
||||
|
||||
# empty openArray issue #7904
|
||||
foo(toOpenArray(seqq, 0, -1))
|
||||
foo(toOpenArray(seqq, 1, 0))
|
||||
doAssertRaises(IndexError):
|
||||
foo(toOpenArray(seqq, 0, -2))
|
||||
|
||||
foo(toOpenArray(arr, 9, 8))
|
||||
foo(toOpenArray(arr, 0, -1))
|
||||
foo(toOpenArray(arr, 1, 0))
|
||||
doAssertRaises(IndexError):
|
||||
foo(toOpenArray(arr, 10, 8))
|
||||
|
||||
# test openArray of openArray
|
||||
proc oaEmpty(a: openArray[int]) =
|
||||
foo(toOpenArray(a, 0, -1))
|
||||
|
||||
proc oaFirstElm(a: openArray[int]) =
|
||||
foo(toOpenArray(a, 0, 0))
|
||||
|
||||
oaEmpty(toOpenArray(seqq, 0, -1))
|
||||
oaEmpty(toOpenArray(seqq, 1, 0))
|
||||
oaEmpty(toOpenArray(seqq, 1, 2))
|
||||
oaFirstElm(toOpenArray(seqq, 1, seqq.len-1))
|
||||
|
||||
var arrNeg: array[-3 .. -1, int] = [1, 2, 3]
|
||||
foo(toOpenArray(arrNeg, -3, -1))
|
||||
foo(toOpenArray(arrNeg, 0, -1))
|
||||
foo(toOpenArray(arrNeg, -3, -4))
|
||||
doAssertRaises(IndexError):
|
||||
foo(toOpenArray(arrNeg, -4, -1))
|
||||
doAssertRaises(IndexError):
|
||||
foo(toOpenArray(arrNeg, -1, 0))
|
||||
doAssertRaises(IndexError):
|
||||
foo(toOpenArray(arrNeg, -1, -3))
|
||||
|
|
|
|||
|
|
@ -11,3 +11,27 @@ template someTemplate[T](): tuple[id: int32, obj: T] =
|
|||
|
||||
let ret = someTemplate[SomeObj]()
|
||||
|
||||
# https://github.com/nim-lang/Nim/issues/7829
|
||||
proc inner*[T](): int =
|
||||
discard
|
||||
|
||||
template outer*[A](): untyped =
|
||||
inner[A]()
|
||||
|
||||
template outer*[B](x: int): untyped =
|
||||
inner[B]()
|
||||
|
||||
var i1 = outer[int]()
|
||||
var i2 = outer[int](i1)
|
||||
|
||||
# https://github.com/nim-lang/Nim/issues/7883
|
||||
template t1[T: int|int64](s: string): T =
|
||||
var t: T
|
||||
t
|
||||
|
||||
template t1[T: int|int64](x: int, s: string): T =
|
||||
var t: T
|
||||
t
|
||||
|
||||
var i3: int = t1[int]("xx")
|
||||
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ proc nimcacheDir(filename, options: string, target: TTarget): string =
|
|||
proc callCompiler(cmdTemplate, filename, options: string,
|
||||
target: TTarget, extraOptions=""): TSpec =
|
||||
let nimcache = nimcacheDir(filename, options, target)
|
||||
let options = options & " --nimCache:" & nimcache.quoteShell & extraOptions
|
||||
let options = options & " " & ("--nimCache:" & nimcache).quoteShell & extraOptions
|
||||
let c = parseCmdLine(cmdTemplate % ["target", targetToCmd[target],
|
||||
"options", options, "file", filename.quoteShell,
|
||||
"filedir", filename.getFileDir()])
|
||||
|
|
@ -212,18 +212,18 @@ proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest, target: TTarg
|
|||
elif expected.tfile == "" and extractFilename(expected.file) != extractFilename(given.file) and
|
||||
"internal error:" notin expected.msg:
|
||||
r.addResult(test, target, expected.file, given.file, reFilesDiffer)
|
||||
elif expected.line != given.line and expected.line != 0 or
|
||||
elif expected.line != given.line and expected.line != 0 or
|
||||
expected.column != given.column and expected.column != 0:
|
||||
r.addResult(test, target, $expected.line & ':' & $expected.column,
|
||||
$given.line & ':' & $given.column,
|
||||
$given.line & ':' & $given.column,
|
||||
reLinesDiffer)
|
||||
elif expected.tfile != "" and extractFilename(expected.tfile) != extractFilename(given.tfile) and
|
||||
"internal error:" notin expected.msg:
|
||||
r.addResult(test, target, expected.tfile, given.tfile, reFilesDiffer)
|
||||
elif expected.tline != given.tline and expected.tline != 0 or
|
||||
elif expected.tline != given.tline and expected.tline != 0 or
|
||||
expected.tcolumn != given.tcolumn and expected.tcolumn != 0:
|
||||
r.addResult(test, target, $expected.tline & ':' & $expected.tcolumn,
|
||||
$given.tline & ':' & $given.tcolumn,
|
||||
$given.tline & ':' & $given.tcolumn,
|
||||
reLinesDiffer)
|
||||
else:
|
||||
r.addResult(test, target, expected.msg, given.msg, reSuccess)
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ discard """
|
|||
type
|
||||
TMsgKind = enum
|
||||
mLine, mEof
|
||||
TMsg = object {.pure, final.}
|
||||
TMsg = object
|
||||
case k: TMsgKind
|
||||
of mEof: nil
|
||||
of mEof: discard
|
||||
of mLine: data: string
|
||||
|
||||
var
|
||||
|
|
|
|||
78
tests/threads/tthreadvars.nim
Normal file
78
tests/threads/tthreadvars.nim
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
discard """
|
||||
output: '''
|
||||
10
|
||||
1111
|
||||
1222
|
||||
3030303
|
||||
3060606
|
||||
6060606
|
||||
6121212
|
||||
3030903
|
||||
3061206
|
||||
3031503
|
||||
3061806
|
||||
5050505
|
||||
5101010
|
||||
'''
|
||||
"""
|
||||
|
||||
import typetraits
|
||||
|
||||
var tls1 {.threadvar.}: int
|
||||
var g0: int
|
||||
var g1 {.global.}: int
|
||||
|
||||
proc customInc(x: var int, delta: int) =
|
||||
x += delta
|
||||
|
||||
customInc(tls1, 10)
|
||||
echo tls1
|
||||
|
||||
proc nonGenericProc: int =
|
||||
var local: int
|
||||
var nonGenericTls {.threadvar.}: int
|
||||
var nonGenericGlobal {.global.}: int
|
||||
var nonGenericMixedPragmas {.global, threadvar.}: int
|
||||
|
||||
customInc local, 1000
|
||||
customInc nonGenericTls, 1
|
||||
customInc nonGenericGlobal, 10
|
||||
customInc nonGenericMixedPragmas, 100
|
||||
|
||||
return local + nonGenericTls + nonGenericGlobal + nonGenericMixedPragmas
|
||||
|
||||
proc genericProc(T: typedesc): int =
|
||||
var local: int
|
||||
var genericTls {.threadvar.}: int
|
||||
var genericGlobal {.global.}: int
|
||||
var genericMixedPragmas {.global, threadvar.}: int
|
||||
|
||||
customInc local, T.name.len * 1000000
|
||||
customInc genericTls, T.name.len * 1
|
||||
customInc genericGlobal, T.name.len * 100
|
||||
customInc genericMixedPragmas, T.name.len * 10000
|
||||
|
||||
return local + genericTls + genericGlobal + genericMixedPragmas
|
||||
|
||||
echo nonGenericProc()
|
||||
echo nonGenericProc()
|
||||
|
||||
echo genericProc(int)
|
||||
echo genericProc(int)
|
||||
|
||||
echo genericProc(string)
|
||||
echo genericProc(string)
|
||||
|
||||
proc echoInThread[T]() {.thread.} =
|
||||
echo genericProc(T)
|
||||
echo genericProc(T)
|
||||
|
||||
proc newEchoThread(T: typedesc) =
|
||||
var t: Thread[void]
|
||||
createThread(t, echoInThread[T])
|
||||
joinThreads(t)
|
||||
|
||||
newEchoThread int
|
||||
newEchoThread int
|
||||
newEchoThread float
|
||||
|
||||
|
|
@ -5,7 +5,7 @@ true'''
|
|||
|
||||
import pegs
|
||||
|
||||
template optPeg{peg(pattern)}(pattern: string{lit}): TPeg =
|
||||
template optPeg{peg(pattern)}(pattern: string{lit}): Peg =
|
||||
var gl {.global, gensym.} = peg(pattern)
|
||||
gl
|
||||
|
||||
|
|
|
|||
245
tests/typerel/t4799.nim
Normal file
245
tests/typerel/t4799.nim
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
discard """
|
||||
output: "OK"
|
||||
"""
|
||||
|
||||
type
|
||||
GRBase[T] = ref object of RootObj
|
||||
val: T
|
||||
GRC[T] = ref object of GRBase[T]
|
||||
GRD[T] = ref object of GRBase[T]
|
||||
|
||||
proc testGR[T](x: varargs[GRBase[T]]): string =
|
||||
result = ""
|
||||
for c in x:
|
||||
result.add $c.val
|
||||
|
||||
block test_t4799_1:
|
||||
var rgv = GRBase[int](val: 3)
|
||||
var rgc = GRC[int](val: 4)
|
||||
var rgb = GRD[int](val: 2)
|
||||
doAssert(testGR(rgb, rgc, rgv) == "243")
|
||||
doAssert(testGR(rgc, rgv, rgb) == "432")
|
||||
doAssert(testGR(rgv, rgb, rgc) == "324")
|
||||
doAssert(testGR([rgb, rgc, rgv]) == "243")
|
||||
doAssert(testGR([rgc, rgv, rgb]) == "432")
|
||||
doAssert(testGR([rgv, rgb, rgc]) == "324")
|
||||
|
||||
type
|
||||
PRBase[T] = object of RootObj
|
||||
val: T
|
||||
PRC[T] = object of PRBase[T]
|
||||
PRD[T] = object of PRBase[T]
|
||||
|
||||
proc testPR[T](x: varargs[ptr PRBase[T]]): string =
|
||||
result = ""
|
||||
for c in x:
|
||||
result.add $c.val
|
||||
|
||||
block test_t4799_2:
|
||||
var pgv = PRBase[int](val: 3)
|
||||
var pgc = PRC[int](val: 4)
|
||||
var pgb = PRD[int](val: 2)
|
||||
doAssert(testPR(pgb.addr, pgc.addr, pgv.addr) == "243")
|
||||
doAssert(testPR(pgc.addr, pgv.addr, pgb.addr) == "432")
|
||||
doAssert(testPR(pgv.addr, pgb.addr, pgc.addr) == "324")
|
||||
doAssert(testPR([pgb.addr, pgc.addr, pgv.addr]) == "243")
|
||||
doAssert(testPR([pgc.addr, pgv.addr, pgb.addr]) == "432")
|
||||
doAssert(testPR([pgv.addr, pgb.addr, pgc.addr]) == "324")
|
||||
|
||||
type
|
||||
RBase = ref object of RootObj
|
||||
val: int
|
||||
RC = ref object of RBase
|
||||
RD = ref object of RBase
|
||||
|
||||
proc testR(x: varargs[RBase]): string =
|
||||
result = ""
|
||||
for c in x:
|
||||
result.add $c.val
|
||||
|
||||
block test_t4799_3:
|
||||
var rv = RBase(val: 3)
|
||||
var rc = RC(val: 4)
|
||||
var rb = RD(val: 2)
|
||||
doAssert(testR(rb, rc, rv) == "243")
|
||||
doAssert(testR(rc, rv, rb) == "432")
|
||||
doAssert(testR(rv, rb, rc) == "324")
|
||||
doAssert(testR([rb, rc, rv]) == "243")
|
||||
doAssert(testR([rc, rv, rb]) == "432")
|
||||
doAssert(testR([rv, rb, rc]) == "324")
|
||||
|
||||
type
|
||||
PBase = object of RootObj
|
||||
val: int
|
||||
PC = object of PBase
|
||||
PD = object of PBase
|
||||
|
||||
proc testP(x: varargs[ptr PBase]): string =
|
||||
result = ""
|
||||
for c in x:
|
||||
result.add $c.val
|
||||
|
||||
block test_t4799_4:
|
||||
var pv = PBase(val: 3)
|
||||
var pc = PC(val: 4)
|
||||
var pb = PD(val: 2)
|
||||
doAssert(testP(pb.addr, pc.addr, pv.addr) == "243")
|
||||
doAssert(testP(pc.addr, pv.addr, pb.addr) == "432")
|
||||
doAssert(testP(pv.addr, pb.addr, pc.addr) == "324")
|
||||
doAssert(testP([pb.addr, pc.addr, pv.addr]) == "243")
|
||||
doAssert(testP([pc.addr, pv.addr, pb.addr]) == "432")
|
||||
doAssert(testP([pv.addr, pb.addr, pc.addr]) == "324")
|
||||
|
||||
type
|
||||
PSBase[T, V] = ref object of RootObj
|
||||
val: T
|
||||
color: V
|
||||
PSRC[T] = ref object of PSBase[T, int]
|
||||
PSRD[T] = ref object of PSBase[T, int]
|
||||
|
||||
proc testPS[T, V](x: varargs[PSBase[T, V]]): string =
|
||||
result = ""
|
||||
for c in x:
|
||||
result.add c.val
|
||||
result.add $c.color
|
||||
|
||||
block test_t4799_5:
|
||||
var a = PSBase[string, int](val: "base", color: 1)
|
||||
var b = PSRC[string](val: "rc", color: 2)
|
||||
var c = PSRD[string](val: "rd", color: 3)
|
||||
|
||||
doAssert(testPS(a, b, c) == "base1rc2rd3")
|
||||
doAssert(testPS(b, a, c) == "rc2base1rd3")
|
||||
doAssert(testPS(c, b, a) == "rd3rc2base1")
|
||||
doAssert(testPS([a, b, c]) == "base1rc2rd3")
|
||||
doAssert(testPS([b, a, c]) == "rc2base1rd3")
|
||||
doAssert(testPS([c, b, a]) == "rd3rc2base1")
|
||||
|
||||
type
|
||||
SBase[T, V] = ref object of RootObj
|
||||
val: T
|
||||
color: V
|
||||
SRC = ref object of SBase[string, int]
|
||||
SRD = ref object of SBase[string, int]
|
||||
|
||||
proc testS[T, V](x: varargs[SBase[T, V]]): string =
|
||||
result = ""
|
||||
for c in x:
|
||||
result.add c.val
|
||||
result.add $c.color
|
||||
|
||||
block test_t4799_6:
|
||||
var a = SBase[string, int](val: "base", color: 1)
|
||||
var b = SRC(val: "rc", color: 2)
|
||||
var c = SRD(val: "rd", color: 3)
|
||||
|
||||
doAssert(testS(a, b, c) == "base1rc2rd3")
|
||||
doAssert(testS(b, a, c) == "rc2base1rd3")
|
||||
doAssert(testS(c, b, a) == "rd3rc2base1")
|
||||
doAssert(testS([a, b, c]) == "base1rc2rd3")
|
||||
# this is not varargs bug, but array construction bug
|
||||
# see #7955
|
||||
#doAssert(testS([b, c, a]) == "rc2rd3base1")
|
||||
#doAssert(testS([c, b, a]) == "rd3rc2base1")
|
||||
|
||||
proc test_inproc() =
|
||||
block test_inproc_1:
|
||||
var rgv = GRBase[int](val: 3)
|
||||
var rgc = GRC[int](val: 4)
|
||||
var rgb = GRD[int](val: 2)
|
||||
doAssert(testGR(rgb, rgc, rgv) == "243")
|
||||
doAssert(testGR(rgc, rgv, rgb) == "432")
|
||||
doAssert(testGR(rgv, rgb, rgc) == "324")
|
||||
doAssert(testGR([rgb, rgc, rgv]) == "243")
|
||||
doAssert(testGR([rgc, rgv, rgb]) == "432")
|
||||
doAssert(testGR([rgv, rgb, rgc]) == "324")
|
||||
|
||||
block test_inproc_2:
|
||||
var pgv = PRBase[int](val: 3)
|
||||
var pgc = PRC[int](val: 4)
|
||||
var pgb = PRD[int](val: 2)
|
||||
doAssert(testPR(pgb.addr, pgc.addr, pgv.addr) == "243")
|
||||
doAssert(testPR(pgc.addr, pgv.addr, pgb.addr) == "432")
|
||||
doAssert(testPR(pgv.addr, pgb.addr, pgc.addr) == "324")
|
||||
doAssert(testPR([pgb.addr, pgc.addr, pgv.addr]) == "243")
|
||||
doAssert(testPR([pgc.addr, pgv.addr, pgb.addr]) == "432")
|
||||
doAssert(testPR([pgv.addr, pgb.addr, pgc.addr]) == "324")
|
||||
|
||||
test_inproc()
|
||||
|
||||
template reject(x) =
|
||||
static: assert(not compiles(x))
|
||||
|
||||
block test_t4799_7:
|
||||
type
|
||||
Vehicle[T] = ref object of RootObj
|
||||
tire: T
|
||||
Car[T] = object of Vehicle[T]
|
||||
Bike[T] = object of Vehicle[T]
|
||||
|
||||
proc testVehicle[T](x: varargs[Vehicle[T]]): string {.used.} =
|
||||
result = ""
|
||||
for c in x:
|
||||
result.add $c.tire
|
||||
|
||||
var v = Vehicle[int](tire: 3)
|
||||
var c = Car[int](tire: 4)
|
||||
var b = Bike[int](tire: 2)
|
||||
|
||||
reject:
|
||||
echo testVehicle(b, c, v)
|
||||
|
||||
block test_t4799_8:
|
||||
type
|
||||
Vehicle = ref object of RootObj
|
||||
tire: int
|
||||
Car = object of Vehicle
|
||||
Bike = object of Vehicle
|
||||
|
||||
proc testVehicle(x: varargs[Vehicle]): string {.used.} =
|
||||
result = ""
|
||||
for c in x:
|
||||
result.add $c.tire
|
||||
|
||||
var v = Vehicle(tire: 3)
|
||||
var c = Car(tire: 4)
|
||||
var b = Bike(tire: 2)
|
||||
|
||||
reject:
|
||||
echo testVehicle(b, c, v)
|
||||
|
||||
type
|
||||
PGVehicle[T] = ptr object of RootObj
|
||||
tire: T
|
||||
PGCar[T] = object of PGVehicle[T]
|
||||
PGBike[T] = object of PGVehicle[T]
|
||||
|
||||
proc testVehicle[T](x: varargs[PGVehicle[T]]): string {.used.} =
|
||||
result = ""
|
||||
for c in x:
|
||||
result.add $c.tire
|
||||
|
||||
var pgc = PGCar[int](tire: 4)
|
||||
var pgb = PGBike[int](tire: 2)
|
||||
|
||||
reject:
|
||||
echo testVehicle(pgb, pgc)
|
||||
|
||||
type
|
||||
RVehicle = ptr object of RootObj
|
||||
tire: int
|
||||
RCar = object of RVehicle
|
||||
RBike = object of RVehicle
|
||||
|
||||
proc testVehicle(x: varargs[RVehicle]): string {.used.} =
|
||||
result = ""
|
||||
for c in x:
|
||||
result.add $c.tire
|
||||
|
||||
var rc = RCar(tire: 4)
|
||||
var rb = RBike(tire: 2)
|
||||
|
||||
reject:
|
||||
echo testVehicle(rb, rc)
|
||||
|
||||
echo "OK"
|
||||
20
tests/typerel/t4799_1.nim
Normal file
20
tests/typerel/t4799_1.nim
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
discard """
|
||||
outputsub: '''ObjectAssignmentError'''
|
||||
exitcode: "1"
|
||||
"""
|
||||
|
||||
type
|
||||
Vehicle[T] = object of RootObj
|
||||
tire: T
|
||||
Car[T] = object of Vehicle[T]
|
||||
Bike[T] = object of Vehicle[T]
|
||||
|
||||
proc testVehicle[T](x: varargs[Vehicle[T]]): string =
|
||||
result = ""
|
||||
for c in x:
|
||||
result.add $c.tire
|
||||
|
||||
var v = Vehicle[int](tire: 3)
|
||||
var c = Car[int](tire: 4)
|
||||
var b = Bike[int](tire: 2)
|
||||
echo testVehicle b, c, v
|
||||
20
tests/typerel/t4799_2.nim
Normal file
20
tests/typerel/t4799_2.nim
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
discard """
|
||||
outputsub: '''ObjectAssignmentError'''
|
||||
exitcode: "1"
|
||||
"""
|
||||
|
||||
type
|
||||
Vehicle[T] = object of RootObj
|
||||
tire: T
|
||||
Car[T] = object of Vehicle[T]
|
||||
Bike[T] = object of Vehicle[T]
|
||||
|
||||
proc testVehicle[T](x: varargs[Vehicle[T]]): string =
|
||||
result = ""
|
||||
for c in x:
|
||||
result.add $c.tire
|
||||
|
||||
var v = Vehicle[int](tire: 3)
|
||||
var c = Car[int](tire: 4)
|
||||
var b = Bike[int](tire: 2)
|
||||
echo testVehicle([b, c, v])
|
||||
20
tests/typerel/t4799_3.nim
Normal file
20
tests/typerel/t4799_3.nim
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
discard """
|
||||
outputsub: '''ObjectAssignmentError'''
|
||||
exitcode: "1"
|
||||
"""
|
||||
|
||||
type
|
||||
Vehicle = object of RootObj
|
||||
tire: int
|
||||
Car = object of Vehicle
|
||||
Bike = object of Vehicle
|
||||
|
||||
proc testVehicle(x: varargs[Vehicle]): string =
|
||||
result = ""
|
||||
for c in x:
|
||||
result.add $c.tire
|
||||
|
||||
var v = Vehicle(tire: 3)
|
||||
var c = Car(tire: 4)
|
||||
var b = Bike(tire: 2)
|
||||
echo testVehicle([b, c, v])
|
||||
|
|
@ -18,7 +18,7 @@ proc works() =
|
|||
sort(f, system.cmp[int])
|
||||
outp(f)
|
||||
|
||||
proc weird(json_params: TTable) =
|
||||
proc weird(json_params: Table) =
|
||||
var f = @[3, 2, 1]
|
||||
# The following line doesn't compile: type mismatch. Why?
|
||||
sort(f, system.cmp[int])
|
||||
|
|
@ -29,4 +29,4 @@ when isMainModule:
|
|||
sort(t, system.cmp[int])
|
||||
outp(t)
|
||||
works()
|
||||
weird(initTable[string, TJsonNode]())
|
||||
weird(initTable[string, JsonNode]())
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
discard """
|
||||
errormsg: "internal error: invalid kind for last(tyEmpty)"
|
||||
errormsg: "internal error: invalid kind for lastOrd(tyEmpty)"
|
||||
"""
|
||||
var q = false
|
||||
discard (if q: {} else: {})
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
discard """
|
||||
file: "tparameterizedparent3.nim"
|
||||
line: 13
|
||||
errormsg: "redefinition of 'color'"
|
||||
errormsg: "attempt to redefine: 'color'"
|
||||
"""
|
||||
# bug #5264
|
||||
type
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
discard """
|
||||
file: "tparameterizedparent4.nim"
|
||||
line: 23
|
||||
errormsg: "redefinition of 'grain'"
|
||||
errormsg: "attempt to redefine: 'grain'"
|
||||
"""
|
||||
# bug #5264
|
||||
type
|
||||
|
|
|
|||
12
tests/vm/tref.nim
Normal file
12
tests/vm/tref.nim
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
static:
|
||||
var
|
||||
a: ref string
|
||||
b: ref string
|
||||
new a
|
||||
|
||||
a[] = "Hello world"
|
||||
b = a
|
||||
|
||||
b[5] = 'c'
|
||||
doAssert a[] == "Hellocworld"
|
||||
doAssert b[] == "Hellocworld"
|
||||
|
|
@ -82,3 +82,11 @@ block:
|
|||
|
||||
assert fileExists("MISSINGFILE") == false
|
||||
assert dirExists("MISSINGDIR") == false
|
||||
|
||||
# #7210
|
||||
block:
|
||||
static:
|
||||
proc f(size: int): int =
|
||||
var some = newStringOfCap(size)
|
||||
result = size
|
||||
doAssert f(4) == 4
|
||||
Loading…
Add table
Add a link
Reference in a new issue