diff options
Diffstat (limited to 'tests')
34 files changed, 1313 insertions, 60 deletions
diff --git a/tests/async/tasyncdiscard.nim b/tests/async/tasyncdiscard.nim index 71aba29e2..e7c87ad42 100644 --- a/tests/async/tasyncdiscard.nim +++ b/tests/async/tasyncdiscard.nim @@ -36,4 +36,4 @@ proc main {.async.} = discard await g() echo 6 -asyncCheck main() +waitFor(main()) diff --git a/tests/async/tasynctry.nim b/tests/async/tasynctry.nim index f77198e2e..5930f296f 100644 --- a/tests/async/tasynctry.nim +++ b/tests/async/tasynctry.nim @@ -48,7 +48,7 @@ proc catch() {.async.} = except OSError, EInvalidField: assert false -asyncCheck catch() +waitFor catch() proc test(): Future[bool] {.async.} = result = false @@ -92,13 +92,13 @@ proc test4(): Future[int] {.async.} = result = 2 var x = test() -assert x.read +assert x.waitFor() x = test2() -assert x.read +assert x.waitFor() var y = test3() -assert y.read == 2 +assert y.waitFor() == 2 y = test4() -assert y.read == 2 +assert y.waitFor() == 2 diff --git a/tests/ccgbugs/twrong_string_asgn.nim b/tests/ccgbugs/twrong_string_asgn.nim index b62e70e7c..669b7f8f5 100644 --- a/tests/ccgbugs/twrong_string_asgn.nim +++ b/tests/ccgbugs/twrong_string_asgn.nim @@ -16,4 +16,4 @@ x.callback = proc () = finished = true -while not finished: discard +while not finished: poll() diff --git a/tests/enum/tenum.nim b/tests/enum/tenum.nim index b081212e6..6d9bdd539 100644 --- a/tests/enum/tenum.nim +++ b/tests/enum/tenum.nim @@ -6,3 +6,9 @@ type var en: E en = a + +# Bug #4066 +import macros +macro genEnum(): untyped = newNimNode(nnkEnumTy).add(newEmptyNode(), newIdentNode("geItem1")) +type GeneratedEnum = genEnum() +doAssert(type(geItem1) is GeneratedEnum) diff --git a/tests/float/tfloat4.nim b/tests/float/tfloat4.nim index 960c4e5f7..006b4d88f 100644 --- a/tests/float/tfloat4.nim +++ b/tests/float/tfloat4.nim @@ -1,3 +1,8 @@ +discard """ + file: "tfloat4.nim" + output: "passed all tests." + exitcode: 0 +""" import math, strutils proc c_sprintf(buf, fmt: cstring) {.importc:"sprintf", header: "<stdio.h>", varargs.} @@ -11,8 +16,9 @@ proc floatToStr(f: float64): string = return add(result, ch) + let testFloats = [ - "0", "-1", "1", "1.", ".3", "3.3", "-.3", "-99.99", + "0", "-0", "0.", "0.0", "-0.", "-0.0", "-1", "1", "1.", ".3", "3.3", "-.3", "-99.99", "1.1e10", "-2e100", "1.234e-10", "1.234e+10", "-inf", "inf", "+inf", "3.14159265358979323846264338327950288", @@ -25,18 +31,20 @@ let testFloats = [ ] for num in testFloats: - assert num.parseFloat.floatToStr.parseFloat == num.parseFloat + doAssert num.parseFloat.floatToStr.parseFloat == num.parseFloat -assert "0".parseFloat == 0.0 -assert "-.1".parseFloat == -0.1 -assert "2.5e1".parseFloat == 25.0 -assert "1e10".parseFloat == 10_000_000_000.0 -assert "0.000_005".parseFloat == 5.000_000e-6 -assert "1.234_567e+2".parseFloat == 123.4567 -assert "1e1_00".parseFloat == "1e100".parseFloat -assert "3.1415926535897932384626433".parseFloat == +doAssert "0".parseFloat == 0.0 +doAssert "-.1".parseFloat == -0.1 +doAssert "2.5e1".parseFloat == 25.0 +doAssert "1e10".parseFloat == 10_000_000_000.0 +doAssert "0.000_005".parseFloat == 5.000_000e-6 +doAssert "1.234_567e+2".parseFloat == 123.4567 +doAssert "1e1_00".parseFloat == "1e100".parseFloat +doAssert "3.1415926535897932384626433".parseFloat == 3.1415926535897932384626433 -assert "2.71828182845904523536028747".parseFloat == +doAssert "2.71828182845904523536028747".parseFloat == 2.71828182845904523536028747 -assert 0.00097656250000000021684043449710088680149056017398834228515625 == +doAssert 0.00097656250000000021684043449710088680149056017398834228515625 == "0.00097656250000000021684043449710088680149056017398834228515625".parseFloat + +echo("passed all tests.") diff --git a/tests/float/tfloat5.nim b/tests/float/tfloat5.nim new file mode 100644 index 000000000..aa7dc6c53 --- /dev/null +++ b/tests/float/tfloat5.nim @@ -0,0 +1,15 @@ +discard """ + file: "tfloat5.nim" + output: '''0 : 0.0 +0 : 0.0 +0 : 0.0 +0 : 0.0''' +""" + +import parseutils + +var f: float +echo "*".parseFloat(f), " : ", f +echo "/".parseFloat(f), " : ", f +echo "+".parseFloat(f), " : ", f +echo "-".parseFloat(f), " : ", f diff --git a/tests/float/tfloat6.nim b/tests/float/tfloat6.nim new file mode 100644 index 000000000..721abd721 --- /dev/null +++ b/tests/float/tfloat6.nim @@ -0,0 +1,21 @@ +discard """ + file: "tfloat6.nim" + output: '''1e-06 : 1e-06 +1e-06 : 1e-06 +0.001 : 0.001 +1e-06 : 1e-06 +1e-06 : 1e-06 +10.000001 : 10.000001 +100.000001 : 100.000001''' +""" + +import strutils + +echo "0.00_0001".parseFloat(), " : ", 1E-6 +echo "0.00__00_01".parseFloat(), " : ", 1E-6 +echo "0.0_01".parseFloat(), " : ", 0.001 +echo "0.00_000_1".parseFloat(), " : ", 1E-6 +echo "0.00000_1".parseFloat(), " : ", 1E-6 + +echo "1_0.00_0001".parseFloat(), " : ", 10.000001 +echo "1__00.00_0001".parseFloat(), " : ", 1_00.000001 diff --git a/tests/float/tfloat7.nim b/tests/float/tfloat7.nim new file mode 100644 index 000000000..2337d1dd4 --- /dev/null +++ b/tests/float/tfloat7.nim @@ -0,0 +1,26 @@ +discard """ + file: "tfloat6.nim" + output: '''passed. +passed. +passed. +passed. +passed. +passed. +passed.''' +""" + +import strutils +template expect_fail(x: expr) = + try: + discard x + echo("expected to fail!") + except ValueError: + echo("passed.") + +expect_fail("1_0._00_0001".parseFloat()) +expect_fail("_1_0_00.0001".parseFloat()) +expect_fail("10.00.01".parseFloat()) +expect_fail("10.00E_01".parseFloat()) +expect_fail("10.00E_01".parseFloat()) +expect_fail("10.00E".parseFloat()) +expect_fail("10.00A".parseFloat()) diff --git a/tests/gc/thavlak.nim b/tests/gc/thavlak.nim new file mode 100644 index 000000000..efab49e36 --- /dev/null +++ b/tests/gc/thavlak.nim @@ -0,0 +1,457 @@ +discard """ + output: '''Welcome to LoopTesterApp, Nim edition +Constructing Simple CFG... +15000 dummy loops +Constructing CFG... +Performing Loop Recognition +1 Iteration +Another 50 iterations... +.................................................. +Found 1 loops (including artificial root node) (50)''' +""" + +# bug #3184 + +import tables +import sequtils +import sets + +type + BasicBlock = object + inEdges: seq[ref BasicBlock] + outEdges: seq[ref BasicBlock] + name: int + +proc newBasicBlock(name: int): ref BasicBlock = + new(result) + result.inEdges = newSeq[ref BasicBlock]() + result.outEdges = newSeq[ref BasicBlock]() + result.name = name + +proc hash(x: ref BasicBlock): int {.inline.} = + result = x.name + +type + BasicBlockEdge = object + fr: ref BasicBlock + to: ref BasicBlock + + Cfg = object + basicBlockMap: Table[int, ref BasicBlock] + edgeList: seq[BasicBlockEdge] + startNode: ref BasicBlock + +proc newCfg(): Cfg = + result.basicBlockMap = initTable[int, ref BasicBlock]() + result.edgeList = newSeq[BasicBlockEdge]() + +proc createNode(self: var Cfg, name: int): ref BasicBlock = + result = self.basicBlockMap.getOrDefault(name) + if result == nil: + result = newBasicBlock(name) + self.basicBlockMap.add name, result + + if self.startNode == nil: + self.startNode = result + +proc addEdge(self: var Cfg, edge: BasicBlockEdge) = + self.edgeList.add(edge) + +proc getNumNodes(self: Cfg): int = + self.basicBlockMap.len + +proc newBasicBlockEdge(cfg: var Cfg, fromName: int, toName: int): BasicBlockEdge = + result.fr = cfg.createNode(fromName) + result.to = cfg.createNode(toName) + result.fr.outEdges.add(result.to) + result.to.inEdges.add(result.fr) + cfg.addEdge(result) + +type + SimpleLoop = object + basicBlocks: seq[ref BasicBlock] # TODO: set here + children: seq[ref SimpleLoop] # TODO: set here + parent: ref SimpleLoop + header: ref BasicBlock + isRoot: bool + isReducible: bool + counter: int + nestingLevel: int + depthLevel: int + +proc newSimpleLoop(): ref SimpleLoop = + new(result) + result.basicBlocks = newSeq[ref BasicBlock]() + result.children = newSeq[ref SimpleLoop]() + result.parent = nil + result.header = nil + result.isRoot = false + result.isReducible = true + result.counter = 0 + result.nestingLevel = 0 + result.depthLevel = 0 + +proc addNode(self: ref SimpleLoop, bb: ref BasicBlock) = + self.basicBlocks.add bb + +proc addChildLoop(self: ref SimpleLoop, loop: ref SimpleLoop) = + self.children.add loop + +proc setParent(self: ref SimpleLoop, parent: ref SimpleLoop) = + self.parent = parent + self.parent.addChildLoop(self) + +proc setHeader(self: ref SimpleLoop, bb: ref BasicBlock) = + self.basicBlocks.add(bb) + self.header = bb + +proc setNestingLevel(self: ref SimpleLoop, level: int) = + self.nestingLevel = level + if level == 0: self.isRoot = true + +var loop_counter: int = 0 + +type + Lsg = object + loops: seq[ref SimpleLoop] + root: ref SimpleLoop + +proc createNewLoop(self: var Lsg): ref SimpleLoop = + result = newSimpleLoop() + loop_counter += 1 + result.counter = loop_counter + +proc addLoop(self: var Lsg, l: ref SimpleLoop) = + self.loops.add l + +proc newLsg(): Lsg = + result.loops = newSeq[ref SimpleLoop]() + result.root = result.createNewLoop() + result.root.setNestingLevel(0) + result.addLoop(result.root) + +proc getNumLoops(self: Lsg): int = + self.loops.len + +type + UnionFindNode = object + parent: ref UnionFindNode + bb: ref BasicBlock + l: ref SimpleLoop + dfsNumber: int + +proc newUnionFindNode(): ref UnionFindNode = + new(result) + when false: + result.parent = nil + result.bb = nil + result.l = nil + result.dfsNumber = 0 + +proc initNode(self: ref UnionFindNode, bb: ref BasicBlock, dfsNumber: int) = + self.parent = self + self.bb = bb + self.dfsNumber = dfsNumber + +proc findSet(self: ref UnionFindNode): ref UnionFindNode = + var nodeList = newSeq[ref UnionFindNode]() + result = self + + while result != result.parent: + var parent = result.parent + if parent != parent.parent: nodeList.add result + result = parent + + for iter in nodeList: iter.parent = result.parent + +proc union(self: ref UnionFindNode, unionFindNode: ref UnionFindNode) = + self.parent = unionFindNode + + +const + BB_TOP = 0 # uninitialized + BB_NONHEADER = 1 # a regular BB + BB_REDUCIBLE = 2 # reducible loop + BB_SELF = 3 # single BB loop + BB_IRREDUCIBLE = 4 # irreducible loop + BB_DEAD = 5 # a dead BB + BB_LAST = 6 # Sentinel + + # # Marker for uninitialized nodes. + UNVISITED = -1 + + # # Safeguard against pathologic algorithm behavior. + MAXNONBACKPREDS = (32 * 1024) + +type + HavlakLoopFinder = object + cfg: Cfg + lsg: Lsg + +proc newHavlakLoopFinder(cfg: Cfg, lsg: Lsg): HavlakLoopFinder = + result.cfg = cfg + result.lsg = lsg + +proc isAncestor(w: int, v: int, last: seq[int]): bool = + w <= v and v <= last[w] + +proc dfs(currentNode: ref BasicBlock, nodes: var seq[ref UnionFindNode], number: var Table[ref BasicBlock, int], last: var seq[int], current: int): int = + var stack = @[(currentNode, current)] + while stack.len > 0: + let (currentNode, current) = stack.pop() + nodes[current].initNode(currentNode, current) + number[currentNode] = current + + result = current + for target in currentNode.outEdges: + if number[target] == UNVISITED: + stack.add((target, result+1)) + #result = dfs(target, nodes, number, last, result + 1) + last[number[currentNode]] = result + +proc findLoops(self: var HavlakLoopFinder): int = + var startNode = self.cfg.startNode + if startNode == nil: return 0 + var size = self.cfg.getNumNodes + + var nonBackPreds = newSeq[HashSet[int]]() + var backPreds = newSeq[seq[int]]() + var number = initTable[ref BasicBlock, int]() + var header = newSeq[int](size) + var types = newSeq[int](size) + var last = newSeq[int](size) + var nodes = newSeq[ref UnionFindNode]() + + for i in 1..size: + nonBackPreds.add initSet[int](1) + backPreds.add newSeq[int]() + nodes.add newUnionFindNode() + + # Step a: + # - initialize all nodes as unvisited. + # - depth-first traversal and numbering. + # - unreached BB's are marked as dead. + # + for v in self.cfg.basicBlockMap.values: number[v] = UNVISITED + var res = dfs(startNode, nodes, number, last, 0) + + # Step b: + # - iterate over all nodes. + # + # A backedge comes from a descendant in the DFS tree, and non-backedges + # from non-descendants (following Tarjan). + # + # - check incoming edges 'v' and add them to either + # - the list of backedges (backPreds) or + # - the list of non-backedges (nonBackPreds) + # + for w in 0 .. <size: + header[w] = 0 + types[w] = BB_NONHEADER + + var nodeW = nodes[w].bb + if nodeW != nil: + for nodeV in nodeW.inEdges: + var v = number[nodeV] + if v != UNVISITED: + if isAncestor(w, v, last): + backPreds[w].add v + else: + nonBackPreds[w].incl v + else: + types[w] = BB_DEAD + + # Start node is root of all other loops. + header[0] = 0 + + # Step c: + # + # The outer loop, unchanged from Tarjan. It does nothing except + # for those nodes which are the destinations of backedges. + # For a header node w, we chase backward from the sources of the + # backedges adding nodes to the set P, representing the body of + # the loop headed by w. + # + # By running through the nodes in reverse of the DFST preorder, + # we ensure that inner loop headers will be processed before the + # headers for surrounding loops. + + for w in countdown(size - 1, 0): + # this is 'P' in Havlak's paper + var nodePool = newSeq[ref UnionFindNode]() + + var nodeW = nodes[w].bb + if nodeW != nil: # dead BB + # Step d: + for v in backPreds[w]: + if v != w: + nodePool.add nodes[v].findSet + else: + types[w] = BB_SELF + + # Copy nodePool to workList. + # + var workList = newSeq[ref UnionFindNode]() + for x in nodePool: workList.add x + + if nodePool.len != 0: types[w] = BB_REDUCIBLE + + # work the list... + # + while workList.len > 0: + var x = workList[0] + workList.del(0) + + # Step e: + # + # Step e represents the main difference from Tarjan's method. + # Chasing upwards from the sources of a node w's backedges. If + # there is a node y' that is not a descendant of w, w is marked + # the header of an irreducible loop, there is another entry + # into this loop that avoids w. + # + + # The algorithm has degenerated. Break and + # return in this case. + # + var nonBackSize = nonBackPreds[x.dfsNumber].len + if nonBackSize > MAXNONBACKPREDS: return 0 + + for iter in nonBackPreds[x.dfsNumber]: + var y = nodes[iter] + var ydash = y.findSet + + if not isAncestor(w, ydash.dfsNumber, last): + types[w] = BB_IRREDUCIBLE + nonBackPreds[w].incl ydash.dfsNumber + else: + if ydash.dfsNumber != w and not nodePool.contains(ydash): + workList.add ydash + nodePool.add ydash + + # Collapse/Unionize nodes in a SCC to a single node + # For every SCC found, create a loop descriptor and link it in. + # + if (nodePool.len > 0) or (types[w] == BB_SELF): + var l = self.lsg.createNewLoop + + l.setHeader(nodeW) + l.isReducible = types[w] != BB_IRREDUCIBLE + + # At this point, one can set attributes to the loop, such as: + # + # the bottom node: + # iter = backPreds(w).begin(); + # loop bottom is: nodes(iter).node; + # + # the number of backedges: + # backPreds(w).size() + # + # whether this loop is reducible: + # types(w) != BB_IRREDUCIBLE + # + nodes[w].l = l + + for node in nodePool: + # Add nodes to loop descriptor. + header[node.dfsNumber] = w + node.union(nodes[w]) + + # Nested loops are not added, but linked together. + var node_l = node.l + if node_l != nil: + node_l.setParent(l) + else: + l.addNode(node.bb) + + self.lsg.addLoop(l) + + result = self.lsg.getNumLoops + + +type + LoopTesterApp = object + cfg: Cfg + lsg: Lsg + +proc newLoopTesterApp(): LoopTesterApp = + result.cfg = newCfg() + result.lsg = newLsg() + +proc buildDiamond(self: var LoopTesterApp, start: int): int = + var bb0 = start + var x1 = newBasicBlockEdge(self.cfg, bb0, bb0 + 1) + var x2 = newBasicBlockEdge(self.cfg, bb0, bb0 + 2) + var x3 = newBasicBlockEdge(self.cfg, bb0 + 1, bb0 + 3) + var x4 = newBasicBlockEdge(self.cfg, bb0 + 2, bb0 + 3) + result = bb0 + 3 + +proc buildConnect(self: var LoopTesterApp, start1: int, end1: int) = + var x1 = newBasicBlockEdge(self.cfg, start1, end1) + +proc buildStraight(self: var LoopTesterApp, start: int, n: int): int = + for i in 0..n-1: + self.buildConnect(start + i, start + i + 1) + result = start + n + +proc buildBaseLoop(self: var LoopTesterApp, from1: int): int = + var header = self.buildStraight(from1, 1) + var diamond1 = self.buildDiamond(header) + var d11 = self.buildStraight(diamond1, 1) + var diamond2 = self.buildDiamond(d11) + var footer = self.buildStraight(diamond2, 1) + + self.buildConnect(diamond2, d11) + self.buildConnect(diamond1, header) + self.buildConnect(footer, from1) + result = self.buildStraight(footer, 1) + +proc run(self: var LoopTesterApp) = + echo "Welcome to LoopTesterApp, Nim edition" + echo "Constructing Simple CFG..." + + var x1 = self.cfg.createNode(0) + var x2 = self.buildBaseLoop(0) + var x3 = self.cfg.createNode(1) + self.buildConnect(0, 2) + + echo "15000 dummy loops" + + for i in 1..15000: + var h = newHavlakLoopFinder(self.cfg, newLsg()) + var res = h.findLoops + + echo "Constructing CFG..." + var n = 2 + + for parlooptrees in 1..10: + var x6 = self.cfg.createNode(n + 1) + self.buildConnect(2, n + 1) + n += 1 + for i in 1..100: + var top = n + n = self.buildStraight(n, 1) + for j in 1..25: n = self.buildBaseLoop(n) + var bottom = self.buildStraight(n, 1) + self.buildConnect n, top + n = bottom + self.buildConnect(n, 1) + + echo "Performing Loop Recognition\n1 Iteration" + + var h = newHavlakLoopFinder(self.cfg, newLsg()) + var loops = h.findLoops + + echo "Another 50 iterations..." + + var sum = 0 + for i in 1..50: + write stdout, "." + flushFile(stdout) + var hlf = newHavlakLoopFinder(self.cfg, newLsg()) + sum += hlf.findLoops + #echo getOccupiedMem() + echo "\nFound ", loops, " loops (including artificial root node) (", sum, ")" + +var l = newLoopTesterApp() +l.run diff --git a/tests/gc/tlists.nim b/tests/gc/tlists.nim new file mode 100644 index 000000000..26b32396c --- /dev/null +++ b/tests/gc/tlists.nim @@ -0,0 +1,37 @@ +discard """ + output: '''Success''' +""" + +# bug #3793 + +import os +import math +import lists +import strutils + +proc mkleak() = + # allocate 10 MB via linked lists + let numberOfLists = 100 + for i in countUp(1, numberOfLists): + var leakList = initDoublyLinkedList[string]() + let numberOfLeaks = 50000 + for j in countUp(1, numberOfLeaks): + let leakSize = 200 + let leaked = newString(leakSize) + leakList.append(leaked) + +proc mkManyLeaks() = + for i in 0..0: + when false: echo getOccupiedMem() + mkleak() + when false: echo getOccupiedMem() + # Force a full collection. This should free all of the + # lists and bring the memory usage down to a few MB's. + GC_fullCollect() + when false: echo getOccupiedMem() + if getOccupiedMem() > 8 * 200 * 50_000 * 2: + echo GC_getStatistics() + quit "leaking" + echo "Success" + +mkManyLeaks() diff --git a/tests/generics/tgenerictmpl2.nim b/tests/generics/tgenerictmpl2.nim new file mode 100644 index 000000000..0ecaf9ded --- /dev/null +++ b/tests/generics/tgenerictmpl2.nim @@ -0,0 +1,31 @@ +discard """ + output: '''1 +1 +1 +1 +999 +999 +999 +2''' +""" + +# test if we can pass explicit generic arguments to generic templates +# based on bug report #3496 + +proc tproc[T](t: T = 999) = echo t +template ttmpl[T](t: T = 999) = echo t + +tproc(1) +tproc[int](1) +ttmpl(1) +ttmpl[int](1) #<- crash case #1 + +tproc[int]() +discard tproc[int] +ttmpl[int]() #<- crash case #2 +ttmpl[int] #<- crash case #3 + +# but still allow normal use of [] on non-generic templates + +template tarr: expr = [1, 2, 3, 4] +echo tarr[1] diff --git a/tests/js/tclosures.nim b/tests/js/tclosures.nim index c0d93814c..0ec4f4743 100644 --- a/tests/js/tclosures.nim +++ b/tests/js/tclosures.nim @@ -2,7 +2,7 @@ discard """ action: run """ -import math, strutils +import math, random, strutils const consolePrefix = "jsCallbacks" asm """ diff --git a/tests/js/test2.nim b/tests/js/test2.nim index f6976d058..0f460d6f8 100644 --- a/tests/js/test2.nim +++ b/tests/js/test2.nim @@ -1,7 +1,8 @@ discard """ output: '''foo js 3.14 -7''' +7 +1''' """ # This file tests the JavaScript generator @@ -29,3 +30,13 @@ proc test(x: C, T: typedesc): T = cast[T](x) echo 7.test(int8) + +# #4222 +const someConst = [ "1"] + +proc procThatRefersToConst() # Forward decl +procThatRefersToConst() # Call bar before it is defined + +proc procThatRefersToConst() = + var i = 0 # Use a var index, otherwise nim will constfold foo[0] + echo someConst[i] # JS exception here: foo is still not initialized (undefined) diff --git a/tests/js/testtojsstr.nim b/tests/js/testtojsstr.nim new file mode 100644 index 000000000..03ac89e20 --- /dev/null +++ b/tests/js/testtojsstr.nim @@ -0,0 +1,8 @@ +discard """ + output = "И\n" +""" + +let s: string = "И\n" +let cs = s.cstring + +echo $s diff --git a/tests/lexer/tstrlits.nim b/tests/lexer/tstrlits.nim index f5b7ce937..cc8872f60 100644 --- a/tests/lexer/tstrlits.nim +++ b/tests/lexer/tstrlits.nim @@ -1,6 +1,6 @@ discard """ file: "tstrlits.nim" - output: "a\"\"long string\"\"\"\"\"abc\"def" + output: "a\"\"long string\"\"\"\"\"abc\"def_'2'●" """ # Test the new different string literals @@ -11,9 +11,13 @@ const raw = r"abc""def" + escaped = "\x5f'\50'\u25cf" + + stdout.write(rawQuote) stdout.write(tripleEmpty) stdout.write(raw) +stdout.write(escaped) #OUT a""long string"""""abc"def diff --git a/tests/macros/tgettypeinst.nim b/tests/macros/tgettypeinst.nim new file mode 100644 index 000000000..22e03a119 --- /dev/null +++ b/tests/macros/tgettypeinst.nim @@ -0,0 +1,122 @@ +discard """ +""" + +import macros, strUtils + +proc symToIdent(x: NimNode): NimNode = + case x.kind: + of nnkCharLit..nnkUInt64Lit: + result = newNimNode(x.kind) + result.intVal = x.intVal + of nnkFloatLit..nnkFloat64Lit: + result = newNimNode(x.kind) + result.floatVal = x.floatVal + of nnkStrLit..nnkTripleStrLit: + result = newNimNode(x.kind) + result.strVal = x.strVal + of nnkIdent, nnkSym: + result = newIdentNode($x) + else: + result = newNimNode(x.kind) + for c in x: + result.add symToIdent(c) + +macro testX(x,inst0: typed; recurse: static[bool]; implX: stmt): typed = + let inst = x.getTypeInst + let impl = x.getTypeImpl + let inst0r = inst0.symToIdent.treeRepr + let instr = inst.symToIdent.treeRepr + #echo inst0r + #echo instr + doAssert(instr == inst0r) + var impl0 = + if implX.kind == nnkNilLit: inst0 + else: implX[0][2] + let impl0r = impl0.symToIdent.treerepr + let implr = impl.symToIdent.treerepr + #echo impl0r + #echo implr + doAssert(implr == impl0r) + template echoString(s:string) = echo s.replace("\n","\n ") + result = newStmtList() + #result.add getAst(echoString(" " & inst0.repr)) + #result.add getAst(echoString(" " & inst.repr)) + #result.add getAst(echoString(" " & impl0.repr)) + #result.add getAst(echoString(" " & impl.repr)) + if recurse: + template testDecl(n, m :typed) = + testV(n, false): + type _ = m + result.add getAst(testDecl(inst.symToIdent, impl.symToIdent)) + +template testV(inst, recurse, impl) = + block: + #echo "testV(" & astToStr(inst) & ", " & $recurse & "):" & astToStr(impl) + var x: inst + testX(x, inst, recurse, impl) +template testT(inst, recurse) = + block: + type myType = inst + testV(myType, recurse): + type _ = inst + +template test(inst) = + testT(inst, false) + testV(inst, true, nil) +template test(inst, impl) = testV(inst, true, impl) + +type + Model = object of RootObj + User = object of Model + name : string + password : string + + Tree = object of RootObj + value : int + left,right : ref Tree + + MyEnum = enum + valueA, valueB, valueC + + MySet = set[MyEnum] + MySeq = seq[int] + MyIntPtr = ptr int + MyIntRef = ref int + + GenericObject[T] = object + value:T + Foo[N:static[int],T] = object + Bar[N:static[int],T] = object + #baz:Foo[N+1,GenericObject[T]] + baz:Foo[N,GenericObject[T]] + +test(bool) +test(char) +test(int) +test(float) +test(ptr int) +test(ref int) +test(array[1..10,Bar[2,Foo[3,float]]]) +test(distinct Bar[2,Foo[3,float]]) +test(tuple[a:int,b:Foo[-1,float]]) +#test(MyEnum): +# type _ = enum +# valueA, valueB, valueC +test(set[MyEnum]) +test(seq[int]) +test(Bar[2,Foo[3,float]]): + type _ = object + baz: Foo[2, GenericObject[Foo[3, float]]] +test(Model): + type _ = object of RootObj +test(User): + type _ = object of Model + name: string + password: string +test(Tree): + type _ = object of RootObj + value: int + left: ref Tree + right: ref Tree +test(proc (a: int, b: Foo[2,float])) +test(proc (a: int, b: Foo[2,float]): Bar[3,int]) diff --git a/tests/manyloc/keineschweine/lib/vehicles.nim b/tests/manyloc/keineschweine/lib/vehicles.nim index 94ebf9f57..ddfb43b38 100644 --- a/tests/manyloc/keineschweine/lib/vehicles.nim +++ b/tests/manyloc/keineschweine/lib/vehicles.nim @@ -1,6 +1,6 @@ import sfml, chipmunk, - sg_assets, sfml_stuff, keineschweine + sg_assets, sfml_stuff, "../keineschweine" proc accel*(obj: PVehicle, dt: float) = diff --git a/tests/manyloc/nake/nakefile.nim b/tests/manyloc/nake/nakefile.nim index e91b86986..2055d7834 100644 --- a/tests/manyloc/nake/nakefile.nim +++ b/tests/manyloc/nake/nakefile.nim @@ -1,5 +1,5 @@ import nake -import httpclient, zip/zipfiles, times, math, sequtils +import httpclient, zip/zipfiles, times, random, sequtils nakeImports randomize() @@ -145,7 +145,7 @@ task "download", "download game assets": echo "Extracted the libs dir. Copy the ones you need to this dir." task "zip-lib", "zip up the libs dir": - var z: TZipArchive + var z: ZipArchive if not z.open("libs-" & getDateStr() & ".zip", fmReadWrite): quit "Could not open zip" for file in walkDirRec("libs", {pcFile, pcDir}): diff --git a/tests/manyloc/named_argument_bug/main.nim.cfg b/tests/manyloc/named_argument_bug/main.nim.cfg index 27cf8e688..7df7a0e97 100644 --- a/tests/manyloc/named_argument_bug/main.nim.cfg +++ b/tests/manyloc/named_argument_bug/main.nim.cfg @@ -1,2 +1,3 @@ # this file only exists to mark 'main.nim' as the main file +--path:"$projectpath" diff --git a/tests/manyloc/named_argument_bug/tri_engine/math/circle.nim b/tests/manyloc/named_argument_bug/tri_engine/math/circle.nim index 7e7517998..b95cfa379 100644 --- a/tests/manyloc/named_argument_bug/tri_engine/math/circle.nim +++ b/tests/manyloc/named_argument_bug/tri_engine/math/circle.nim @@ -1,6 +1,6 @@ import - tri_engine/config, - tri_engine/math/vec + ../config, + vec type TCircle* = tuple[p: TV2[TR], r: TR] diff --git a/tests/manyloc/named_argument_bug/tri_engine/math/vec.nim b/tests/manyloc/named_argument_bug/tri_engine/math/vec.nim index 3b57acb8e..926958fe4 100644 --- a/tests/manyloc/named_argument_bug/tri_engine/math/vec.nim +++ b/tests/manyloc/named_argument_bug/tri_engine/math/vec.nim @@ -1,6 +1,6 @@ import macros, - tri_engine/config + "../config" type TV2*[T:SomeNumber=TR] = array[0..1, T] diff --git a/tests/overload/tstmtoverload.nim b/tests/overload/tstmtoverload.nim index f1944b637..75584bcab 100644 --- a/tests/overload/tstmtoverload.nim +++ b/tests/overload/tstmtoverload.nim @@ -10,7 +10,7 @@ template test(loopCount: int, extraI: int, testBody: stmt): stmt = template test(loopCount: int, extraF: float, testBody: stmt): stmt = block: - test(loopCount, round(extraF), testBody) + test(loopCount, round(extraF).int, testBody) template test(loopCount: int, testBody: stmt): stmt = block: diff --git a/tests/parallel/twrong_refcounts.nim b/tests/parallel/twrong_refcounts.nim index db32a96d8..57e0588a0 100644 --- a/tests/parallel/twrong_refcounts.nim +++ b/tests/parallel/twrong_refcounts.nim @@ -2,7 +2,7 @@ discard """ output: "Success" """ -import math, threadPool +import math, random, threadPool # --- diff --git a/tests/parser/tdo.nim b/tests/parser/tdo.nim new file mode 100644 index 000000000..7bd1f7411 --- /dev/null +++ b/tests/parser/tdo.nim @@ -0,0 +1,79 @@ +discard """ + output: '''true +true +true +true inner B''' +""" + +template withValue(a, b, c, d, e: untyped) = + if c: + d + else: + e + +template withValue(a, b, c, d: untyped) = + if c: + d + +const + EVENT_READ = 1 + EVENT_WRITE = 2 + FLAG_HANDLE = 3 + EVENT_MASK = 3 + +var s: string + +proc main = + var value = false + var fd = 8888 + var event = 0 + s.withValue(fd, value) do: + if value: + var oe = (EVENT_MASK) + if (oe xor event) != 0: + if (oe and EVENT_READ) != 0 and (event and EVENT_READ) == 0: + discard + if (oe and EVENT_WRITE) != 0 and (event and EVENT_WRITE) == 0: + discard + if (oe and EVENT_READ) == 0 and (event and EVENT_READ) != 0: + discard + if (oe and EVENT_WRITE) == 0 and (event and EVENT_WRITE) != 0: + discard + else: + raise newException(ValueError, "error") + do: + raise newException(ValueError, "Descriptor is not registered in queue") + +proc main2 = + var unused = 8 + # test 'then' branch: + s.withValue(unused, true) do: + echo "true" + do: + echo "false" + + # test overloading: + s.withValue(unused, false) do: + echo "cannot come here" + + # test 'else' branch: + s.withValue(unused, false) do: + echo "false" + do: + echo "true" + + # test proper nesting: + s.withValue(unused, false) do: + echo "false" + s.withValue(unused, false) do: + echo "false inner A" + do: + echo "true inner A" + do: + echo "true" + s.withValue(unused, false) do: + echo "false inner B" + do: + echo "true inner B" + +main2() diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim index 1ac9c8092..538582ba8 100644 --- a/tests/stdlib/tmath.nim +++ b/tests/stdlib/tmath.nim @@ -1,4 +1,4 @@ -import math +import math, random import unittest import sets diff --git a/tests/stdlib/tparscfg.nim b/tests/stdlib/tparscfg.nim index 4c11ccf61..7022d071b 100644 --- a/tests/stdlib/tparscfg.nim +++ b/tests/stdlib/tparscfg.nim @@ -1,25 +1,37 @@ +import parsecfg -import - os, parsecfg, strutils, streams +## Creating a configuration file. +var dict1=newConfig() +dict1.setSectionKey("","charset","utf-8") +dict1.setSectionKey("Package","name","hello") +dict1.setSectionKey("Package","--threads","on") +dict1.setSectionKey("Author","name","lihf8515") +dict1.setSectionKey("Author","qq","10214028") +dict1.setSectionKey("Author","email","lihaifeng@wxm.com") +dict1.writeConfig("config.ini") + +## Reading a configuration file. +var dict2 = loadConfig("config.ini") +var charset = dict2.getSectionValue("","charset") +var threads = dict2.getSectionValue("Package","--threads") +var pname = dict2.getSectionValue("Package","name") +var name = dict2.getSectionValue("Author","name") +var qq = dict2.getSectionValue("Author","qq") +var email = dict2.getSectionValue("Author","email") +echo charset +echo threads +echo pname +echo name +echo qq +echo email + +## Modifying a configuration file. +var dict3 = loadConfig("config.ini") +dict3.setSectionKey("Author","name","lhf") +dict3.writeConfig("config.ini") + +## Deleting a section key in a configuration file. +var dict4 = loadConfig("config.ini") +dict4.delSectionKey("Author","email") +dict4.writeConfig("config.ini") -var f = newFileStream(paramStr(1), fmRead) -if f != nil: - var p: TCfgParser - open(p, f, paramStr(1)) - while true: - var e = next(p) - case e.kind - of cfgEof: - echo("EOF!") - break - of cfgSectionStart: ## a ``[section]`` has been parsed - echo("new section: " & e.section) - of cfgKeyValuePair: - echo("key-value-pair: " & e.key & ": " & e.value) - of cfgOption: - echo("command: " & e.key & ": " & e.value) - of cfgError: - echo(e.msg) - close(p) -else: - echo("cannot open: " & paramStr(1)) diff --git a/tests/stdlib/tunittest.nim b/tests/stdlib/tunittest.nim index 4b210c23b..73113ac68 100644 --- a/tests/stdlib/tunittest.nim +++ b/tests/stdlib/tunittest.nim @@ -26,7 +26,7 @@ test "unittest multiple requires": require(true) -import math +import math, random from strutils import parseInt proc defectiveRobot() = randomize() diff --git a/tests/template/t2do.nim b/tests/template/t2do.nim index b87e3328c..ec364c5f3 100644 --- a/tests/template/t2do.nim +++ b/tests/template/t2do.nim @@ -15,8 +15,9 @@ template toFloatHelper(result: expr; tooSmall, tooLarge: stmt) {.immediate.} = tooLarge proc toFloat*(a: int): float = - toFloatHelper(result) - do: raise newException(ValueError, "number too small"): - raise newException(ValueError, "number too large") + toFloatHelper(result) do: + raise newException(ValueError, "number too small") + do: + raise newException(ValueError, "number too large") echo toFloat(8) diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index 150c55edc..125643d5a 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -152,6 +152,9 @@ proc gcTests(r: var TResults, cat: Category, options: string) = testWithoutBoehm "closureleak" testWithoutMs "refarrayleak" + testWithoutBoehm "tlists" + testWithoutBoehm "thavlak" + test "stackrefleak" test "cyclecollector" @@ -223,7 +226,7 @@ proc jsTests(r: var TResults, cat: Category, options: string) = "varres/tvartup", "misc/tints", "misc/tunsignedinc"]: test "tests/" & testfile & ".nim" - for testfile in ["pure/strutils", "pure/json"]: + for testfile in ["pure/strutils", "pure/json", "pure/random", "pure/times"]: test "lib/" & testfile & ".nim" # ------------------------- manyloc ------------------------------------------- diff --git a/tests/threads/ttryrecv.nim b/tests/threads/ttryrecv.nim index be79fadae..4a98e6c27 100644 --- a/tests/threads/ttryrecv.nim +++ b/tests/threads/ttryrecv.nim @@ -4,7 +4,7 @@ discard """ # bug #1816 -from math import random +from random import random from os import sleep type PComm = ptr Channel[int] diff --git a/tests/trmacros/tstatic_t_bug.nim b/tests/trmacros/tstatic_t_bug.nim new file mode 100644 index 000000000..cdfa53514 --- /dev/null +++ b/tests/trmacros/tstatic_t_bug.nim @@ -0,0 +1,24 @@ +discard """ + output: "optimized" +""" +# bug #4227 +type Vector64[N: static[int]] = array[N, int] + +proc `*`*[N: static[int]](a: Vector64[N]; b: float64): Vector64[N] = + result = a + +proc `+=`*[N: static[int]](a: var Vector64[N]; b: Vector64[N]) = + echo "regular" + +proc linearCombinationMut[N: static[int]](a: float64, v: var Vector64[N], w: Vector64[N]) {. inline .} = + echo "optimized" + +template rewriteLinearCombinationMut*{v += `*`(w, a)}(a: float64, v: var Vector64, w: Vector64): auto = + linearCombinationMut(a, v, w) + +proc main() = + const scaleVal = 9.0 + var a, b: Vector64[7] + a += b * scaleval + +main() diff --git a/tests/usingstmt/tthis.nim b/tests/usingstmt/tthis.nim new file mode 100644 index 000000000..83d75d08c --- /dev/null +++ b/tests/usingstmt/tthis.nim @@ -0,0 +1,15 @@ + +# bug #4177 + +type + Parent = object of RootObj + parentField: int + Child = object of Parent + childField: int + +{.this: self.} +proc sumFields(self: Child): int = + result = parentField + childField # Error: undeclared identifier: 'parentField' + +proc sumFieldsWorks(self: Child): int = + result = self.parentField + childField diff --git a/tests/vm/meta.nim b/tests/vm/meta.nim new file mode 100644 index 000000000..2aa01b5b3 --- /dev/null +++ b/tests/vm/meta.nim @@ -0,0 +1,240 @@ +# +# meta.nim +# + +import tables +import macros + +type + NodeSeq* = seq[NimNode] + Ident* = tuple[name: string, exported: bool] + Bracket* = seq[Ident] + Field* = tuple[identifier: Ident, type_name: string, default: string] + FieldSeq* = seq[Field] + TypeDef* = object + identifier*: Ident + fields*: FieldSeq + is_ref*: bool + object_type*: string + base_type*: string + TypeDefSeq* = seq[TypeDef] + Proc* = tuple[identifier: Ident, params: FieldSeq, + returns: Ident, generics: FieldSeq, body: NimNode] + ProcSeq* = seq[Proc] + +# Ident procs +proc newIdent*(name: string, exported = false): Ident = + result.name = name + result.exported = exported + +proc newIdent*(node: NimNode): Ident = + case node.kind: + of nnkPostfix: + result = newIdent(node[1]) + result.exported = true + of nnkIdent, nnkSym: + result.name = $(node) + else: + let msg = "newIdent cannot initialize from node kind: " & $(node.kind) + raise newException(ValueError, msg) + +proc render*(i: Ident): NimNode {.compileTime.} = + if i.name == nil: + return newNimNode(nnkEmpty) + + if i.exported: + result = newNimNode(nnkPostfix) + result.add(ident "*") + result.add(ident i.name) + else: + result = ident i.name + +proc `$`*(identifier: Ident): string = identifier.name + +converter toString*(x: Ident): string = x.name + +proc newBracket*(node: NimNode): Bracket = + result = @[] + case node.kind: + of nnkBracket: + for child in node: + if child.kind != nnkIdent: + let msg = "Bracket members can only be nnkIdent not kind: " & $(node.kind) + raise newException(ValueError, msg) + result.add(newIdent(child)) + else: + let msg = "newBracket must initialize from node kind nnkBracket not: " & $(node.kind) + raise newException(ValueError, msg) + +# Field procs +proc newField*(identifier: Ident, type_name: string, default: string = nil): Field = + result.identifier = identifier + result.type_name = type_name + result.default = default + +proc newField*(node: NimNode): Field = + case node.kind: + of nnkIdentDefs: + if node.len > 3: + let msg = "newField cannot initialize from nnkIdentDefs with multiple names" + raise newException(ValueError, msg) + result.identifier = newIdent(node[0]) + result.type_name = $(node[1]) + case node[2].kind: + of nnkIdent: + result.default = $(node[2]) + else: + result.default = nil + else: + let msg = "newField cannot initialize from node kind: " & $(node.kind) + raise newException(ValueError, msg) + +# FieldSeq procs +proc newFieldSeq*(node: NimNode): FieldSeq = + result = @[] + case node.kind: + of nnkIdentDefs: + let + type_name = $(node[node.len - 2]) + default_node = node[node.len - 1] + var default: string + case default_node.kind: + of nnkIdent: + default = $(default_node) + else: + default = nil + for i in 0..node.len - 3: + let name = newIdent(node[i]) + result.add(newField(name, type_name, default)) + of nnkRecList, nnkVarSection, nnkGenericParams: + for child in node: + result = result & newFieldSeq(child) + else: + let msg = "newFieldSeq cannot initialize from node kind: " & $(node.kind) + raise newException(ValueError, msg) + +proc render*(f: Field): NimNode {.compileTime.} = + let identifier = f.identifier.render() + let type_name = if f.type_name != nil: ident(f.type_name) else: newEmptyNode() + let default = if f.default != nil: ident(f.default) else: newEmptyNode() + newIdentDefs(identifier, type_name, default) + +proc render*(fs: FieldSeq): NimNode {.compileTime.} = + result = newNimNode(nnkRecList) + for field in fs: + result.add(field.render()) + +# TypeDef procs +proc newTypeDef*(identifier: Ident, is_ref = false, + object_type = "object", + base_type: string = nil): TypeDef {.compileTime.} = + result.identifier = identifier + result.fields = @[] + result.is_ref = is_ref + result.object_type = "object" + result.base_type = base_type + +proc newTypeDef*(node: NimNode): TypeDef {.compileTime.} = + case node.kind: + of nnkTypeDef: + result.identifier = newIdent($(node[0])) + var object_node: NimNode + case node[2].kind: + of nnkRefTy: + object_node = node[2][0] + result.is_ref = true + of nnkObjectTy: + object_node = node[2] + result.is_ref = false + else: + let msg = "newTypeDef could not parse RefTy/ObjectTy, found: " & $(node[2].kind) + raise newException(ValueError, msg) + case object_node[1].kind: + of nnkOfInherit: + result.base_type = $(object_node[1][0]) + else: + result.base_type = "object" + result.fields = newFieldSeq(object_node[2]) + else: + let msg = "newTypeDef cannot initialize from node kind: " & $(node.kind) + raise newException(ValueError, msg) + +proc render*(typedef: TypeDef): NimNode {.compileTime.} = + result = newNimNode(nnkTypeDef) + result.add(typedef.identifier.render) + result.add(newEmptyNode()) + let object_node = newNimNode(nnkObjectTy) + object_node.add(newEmptyNode()) + if typedef.base_type == nil: + object_node.add(newEmptyNode()) + else: + var base_type = newNimNode(nnkOfInherit) + base_type.add(ident(typedef.base_type)) + object_node.add(base_type) + let fields = typedef.fields.render() + object_node.add(fields) + if typedef.is_ref: + let ref_node = newNimNode(nnkRefTy) + ref_node.add(object_node) + result.add(ref_node) + else: + result.add(object_node) + +proc newTypeDefSeq*(node: NimNode): TypeDefSeq = + result = @[] + case node.kind: + of nnkTypeSection: + for child in node: + result.add(newTypeDef(child)) + else: + let msg = "newTypeSection could not parse TypeDef, found: " & $(node.kind) + raise newException(ValueError, msg) + +proc render*(typeseq: TypeDefSeq): NimNode {.compileTime.} = + result = newNimNode(nnkTypeSection) + for typedef in typeseq: + result.add(typedef.render()) + +proc newProc*(identifier: Ident, params: FieldSeq = nil, + returns: Ident, generics: FieldSeq = nil): Proc = + result.identifier = identifier + result.params = params + result.returns = returns + result.generics = generics + +proc newProc*(node: NimNode): Proc = + case node.kind: + of nnkProcDef, nnkMethodDef: + result.identifier = newIdent(node[0]) + case node[2].kind: + of nnkGenericParams: + result.generics = newFieldSeq(node[2]) + else: result.generics = nil + let formal_params = node[3] + case formal_params[0].kind: + of nnkIdent: + result.returns = newIdent(formal_params[0]) + else: discard + result.params = @[] + for i in 1..formal_params.len - 1: + let param = formal_params[i] + for field in newFieldSeq(param): + result.params.add(field) + result.body = node[6] + else: + let msg = "newProc cannot initialize from node kind: " & $(node.kind) + raise newException(ValueError, msg) + +proc render*(procdef: Proc): NimNode {.compileTime.} = + result = newNimNode(nnkProcDef) + result.add(procdef.identifier.render()) + result.add(newEmptyNode()) + result.add(newEmptyNode()) + let formal_params = newNimNode(nnkFormalParams) + formal_params.add(procdef.returns.render()) + for param in procdef.params: + formal_params.add(param.render()) + result.add(formal_params) + result.add(newEmptyNode()) + result.add(newEmptyNode()) + result.add(procdef.body) diff --git a/tests/vm/tcomponent.nim b/tests/vm/tcomponent.nim new file mode 100644 index 000000000..efeba2a6d --- /dev/null +++ b/tests/vm/tcomponent.nim @@ -0,0 +1,132 @@ +discard """ + output: '''`:)` @ 0,0 +FOO: blah''' +""" + +# +# magic.nim +# + +# bug #3729 + +import macros, sequtils, tables +import strutils +import future, meta + +type + Component = object + fields: FieldSeq + field_index: seq[string] + procs: ProcSeq + procs_index: seq[string] + + Registry = object + field_index: seq[string] + procs_index: seq[string] + components: Table[string, Component] + builtin: Component + +proc newRegistry(): Registry = + result.field_index = @[] + result.procs_index = @[] + result.components = initTable[string, Component]() + +var registry {.compileTime.} = newRegistry() + +proc validateComponent(r: var Registry, name: string, c: Component) = + if r.components.hasKey(name): + let msg = "`component` macro cannot consume duplicated identifier: " & name + raise newException(ValueError, msg) + + for field_name in c.field_index: + if r.field_index.contains(field_name): + let msg = "`component` macro cannot delcare duplicated field: " & field_name + raise newException(ValueError, msg) + r.field_index.add(field_name) + + for proc_name in c.procs_index: + if r.procs_index.contains(proc_name): + let msg = "`component` macro cannot delcare duplicated proc: " & proc_name + raise newException(ValueError, msg) + r.procs_index.add(proc_name) + +proc addComponent(r: var Registry, name: string, c: Component) = + r.validateComponent(name, c) + r.components.add(name, c) + +proc parse_component(body: NimNode): Component = + result.field_index = @[] + result.procs_index = @[] + for node in body: + case node.kind: + of nnkVarSection: + result.fields = newFieldSeq(node) + for field in result.fields: + result.field_index.add(field.identifier.name) + of nnkMethodDef, nnkProcDef: + let new_proc = meta.newProc(node) + result.procs = result.procs & @[new_proc] + for procdef in result.procs: + result.procs_index.add(procdef.identifier.name) + else: discard + +macro component*(name: expr, body: stmt): stmt {.immediate.} = + let component = parse_component(body) + registry.addComponent($name, component) + parseStmt("discard") + +macro component_builtins(body: stmt): stmt {.immediate.} = + let builtin = parse_component(body) + registry.field_index = builtin.field_index + registry.procs_index = builtin.procs_index + registry.builtin = builtin + +proc bind_methods*(component: var Component, identifier: Ident): seq[NimNode] = + result = @[] + for procdef in component.procs.mitems: + let this_field = newField(newIdent("this"), identifier) + procdef.params.insert(this_field, 0) + result.add(procdef.render()) + +macro bind_components*(type_name, component_names: expr): stmt {.immediate.} = + result = newStmtList() + let identifier = newIdent(type_name) + let components = newBracket(component_names) + var entity_type = newTypeDef(identifier, true, "object", "RootObj") + entity_type.fields = registry.builtin.fields + for component_name, component in registry.components: + if components.contains(newIdent(component_name)): + entity_type.fields = entity_type.fields & component.fields + # TODO why doesn't the following snippet work instead of the one above? + # for name in components: + # echo "Registering $1 to $2" % [name.name, identifier.name] + # let component = registry.components[name.name] + # entity_type.fields = entity_type.fields & component.fields + let type_section: TypeDefSeq = @[entity_type] + result.add type_section.render + var builtin = registry.builtin + let builtin_methods = bind_methods(builtin, identifier) + for builtin_proc in builtin_methods: + result.add(builtin_proc) + echo "SIGSEV here" + for component in registry.components.mvalues(): + for method_proc in bind_methods(component, identifier): + result.add(method_proc) + +component_builtins: + proc foo(msg: string) = + echo "FOO: $1" % msg + +component position: + var x*, y*: int + +component name: + var name*: string + proc render*(x, y: int) = echo "`$1` @ $2,$3" % [this.name, $x, $y] + +bind_components(Entity, [position, name]) + +var e = new(Entity) +e.name = ":)" +e.render(e.x, e.y) +e.foo("blah") |