From bd8f5c8392fc6f28381170d029f67801789fd78d Mon Sep 17 00:00:00 2001 From: Billingsly Wetherfordshire Date: Thu, 19 Jun 2014 13:00:11 -0500 Subject: [PATCH 01/37] json.== handles nil now --- lib/pure/json.nim | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 4e369b854d..871713f1ce 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -621,9 +621,13 @@ proc `%`*(elements: openArray[PJsonNode]): PJsonNode = proc `==`* (a,b: PJsonNode): bool = ## Check two nodes for equality - if a.kind != b.kind: false + if a.isNil: + if b.isNil: return true + return false + elif b.isNil or a.kind != b.kind: + return false else: - case a.kind + return case a.kind of JString: a.str == b.str of JInt: From f59ca2736dac0e544c11aaca431408931d3e21de Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Fri, 27 Jun 2014 16:57:01 +0200 Subject: [PATCH 02/37] Fixed `==` for PTables, added test. --- lib/pure/collections/tables.nim | 7 +++++-- tests/table/ptables.nim | 9 +++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index ce9df09e10..146cb76c9a 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -246,7 +246,8 @@ template equalsImpl() = # different insertion orders mean different 'data' seqs, so we have # to use the slow route here: for key, val in s: - if not hasKey(t, key): return false + # prefix notation leads to automatic dereference in case of PTable + if not t.hasKey(key): return false if t[key] != val: return false return true @@ -332,7 +333,9 @@ proc `$`*[A, B](t: PTable[A, B]): string = dollarImpl() proc `==`*[A, B](s, t: PTable[A, B]): bool = - equalsImpl() + if isNil(s): result = isNil(t) + elif isNil(t): result = false + else: equalsImpl() proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): PTable[C, B] = ## Index the collection with the proc provided. diff --git a/tests/table/ptables.nim b/tests/table/ptables.nim index ec52d08c33..79a9aab171 100644 --- a/tests/table/ptables.nim +++ b/tests/table/ptables.nim @@ -104,6 +104,15 @@ block countTableTest1: block SyntaxTest: var x = newTable[int, string]({:}) +block nilTest: + var i, j: PTable[int, int] = nil + assert i == j + j = newTable[int, int]() + assert i != j + assert j != i + i = newTable[int, int]() + assert i == j + proc orderedTableSortTest() = var t = newOrderedTable[string, int](2) for key, val in items(data): t[key] = val From ed68286c592b0f2ff0d27496c740b9e9f6b761a7 Mon Sep 17 00:00:00 2001 From: Flaviu Tamas Date: Tue, 22 Jul 2014 15:34:49 -0400 Subject: [PATCH 03/37] Fix #1392 --- compiler/ccgexprs.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 95167e1576..691feeb472 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1578,15 +1578,15 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mGetTypeInfo: genGetTypeInfo(p, e, d) of mSwap: genSwap(p, e, d) of mUnaryLt: - if not (optOverflowCheck in p.options): unaryExpr(p, e, d, "$1 - 1") + if not (optOverflowCheck in p.options): unaryExpr(p, e, d, "($1 - 1)") else: unaryExpr(p, e, d, "#subInt($1, 1)") of mPred: # XXX: range checking? - if not (optOverflowCheck in p.options): binaryExpr(p, e, d, "$1 - $2") + if not (optOverflowCheck in p.options): binaryExpr(p, e, d, "($1 - $2)") else: binaryExpr(p, e, d, "#subInt($1, $2)") of mSucc: # XXX: range checking? - if not (optOverflowCheck in p.options): binaryExpr(p, e, d, "$1 + $2") + if not (optOverflowCheck in p.options): binaryExpr(p, e, d, "($1 + $2)") else: binaryExpr(p, e, d, "#addInt($1, $2)") of mInc: if not (optOverflowCheck in p.options): From 26ba9e6d3286adddaff0d3c13e91d973f443bf51 Mon Sep 17 00:00:00 2001 From: jfhg Date: Wed, 23 Jul 2014 22:47:16 +0200 Subject: [PATCH 04/37] fix build on DragonFly BSD and FreeBSD --- lib/pure/osproc.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 04a0c2403a..c74fa1ceb6 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -763,7 +763,7 @@ elif not defined(useNimRtl): discard write(data.pErrorPipe[writeIdx], addr error, sizeof(error)) exitnow(1) - when defined(macosx): + when defined(macosx) or defined(freebsd): var environ {.importc.}: cstringArray proc startProcessAfterFork(data: ptr TStartProcessData) = @@ -793,7 +793,7 @@ elif not defined(useNimRtl): discard fcntl(data.pErrorPipe[writeIdx], F_SETFD, FD_CLOEXEC) if data.optionPoUsePath: - when defined(macosx): + when defined(macosx) or defined(freebsd): # MacOSX doesn't have execvpe, so we need workaround. # On MacOSX we can arrive here only from fork, so this is safe: environ = data.sysEnv From 2577c92ec757f57d9238c362922f01bb807aff54 Mon Sep 17 00:00:00 2001 From: Joshua Cearley Date: Wed, 23 Jul 2014 21:30:03 -0500 Subject: [PATCH 05/37] Add better support for unsigned ints via typeinfo. --- lib/core/typeinfo.nim | 53 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index edb4d11885..93b90e120a 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -420,6 +420,59 @@ proc setBiggestInt*(x: TAny, y: biggestInt) = of tyUInt32: cast[ptr uint32](x.value)[] = uint32(y) else: assert false +proc getUInt*(x: TAny): uint = + ## retrieve the uint value out of `x`, `x` needs to represent an uint. + assert skipRange(x.rawtype).kind == tyUInt + result = cast[ptr uint](x.value)[] + +proc getUInt8*(x: TAny): uint8 = + ## retrieve the uint8 value out of `x`, `x` needs to represent an + ## uint8. + assert skipRange(x.rawtype).kind == tyUInt8 + result = cast[ptr uint8](x.value)[] + +proc getUInt16*(x: TAny): uint16 = + ## retrieve the uint16 value out of `x`, `x` needs to represent an + ## uint16. + assert skipRange(x.rawtype).kind == tyUInt16 + result = cast[ptr uint16](x.value)[] + +proc getUInt32*(x: TAny): uint32 = + ## retrieve the uint32 value out of `x`, `x` needs to represent an + ## uint32. + assert skipRange(x.rawtype).kind == tyUInt32 + result = cast[ptr uint32](x.value)[] + +proc getUInt64*(x: TAny): uint64 = + ## retrieve the uint64 value out of `x`, `x` needs to represent an + ## uint64. + assert skipRange(x.rawtype).kind == tyUInt64 + result = cast[ptr uint64](x.value)[] + +proc getBiggestUint*(x: TAny): uint64 = + ## retrieve the unsigned integer value out of `x`. `x` needs to + ## represent an unsigned integer. + var t = skipRange(x.rawtype) + case t.kind + of akUInt: result = uint64(cast[ptr uint](x.value)[]) + of akUInt8: result = uint64(cast[ptr uint8](x.value)[]) + of akUInt16: result = uint64(cast[ptr uint16](x.value)[]) + of akUInt32: result = uint64(cast[ptr uint32](x.value)[]) + of akUInt64: result = uint64(cast[ptr uint64](x.value)[]) + else: assert false + +proc setBiggestUint*(x: TAny; y: uint64) = + ## sets the unsigned integer value of `c`. `c` needs to represent an + ## unsigned integer. + var t = skipRange(x.rawtype) + case t.kind: + of akUInt: result = cast[ptr uint](x.value)[] = uint(y) + of akUInt8: result = cast[ptr uint8](x.value)[] = uint8(y) + of akUInt16: result = cast[ptr uint16](x.value)[] = uint16(y) + of akUInt32: result = cast[ptr uint32](x.value)[] = uint32(y) + of akUInt64: result = cast[ptr uint64](x.value)[] = uint64(y) + else: assert false + proc getChar*(x: TAny): char = ## retrieve the char value out of `x`. `x` needs to represent a char. var t = skipRange(x.rawtype) From 18003ff1966fc54ed1b2b39988dda6961ce35c80 Mon Sep 17 00:00:00 2001 From: Clay Sweetser Date: Thu, 24 Jul 2014 18:17:20 -0400 Subject: [PATCH 06/37] Added stylistic consistancy. --- lib/pure/collections/tables.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 146cb76c9a..dcf2ab4811 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -335,7 +335,7 @@ proc `$`*[A, B](t: PTable[A, B]): string = proc `==`*[A, B](s, t: PTable[A, B]): bool = if isNil(s): result = isNil(t) elif isNil(t): result = false - else: equalsImpl() + else: result = equalsImpl() proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): PTable[C, B] = ## Index the collection with the proc provided. From 030c2d6deb6f8538627009ad4fc77e56e4f2b714 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 26 Jul 2014 02:14:26 +0100 Subject: [PATCH 07/37] Fixes base64 crash. --- lib/pure/base64.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/base64.nim b/lib/pure/base64.nim index 4e59a6ca63..7b3b0e6f5a 100644 --- a/lib/pure/base64.nim +++ b/lib/pure/base64.nim @@ -58,7 +58,7 @@ template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediat if r+4 != result.len: setLen(result, r+4) else: - assert(r == result.len) + #assert(r == result.len) proc encode*[T:TInteger|char](s: openarray[T], lineLen = 75, newLine="\13\10"): string = ## encodes `s` into base64 representation. After `lineLen` characters, a From b10bf62963349828068f735771a87f0d4a5214d8 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sat, 26 Jul 2014 11:57:26 +0200 Subject: [PATCH 08/37] Documents pretty command. --- doc/advopt.txt | 1 + doc/nimrodc.txt | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/doc/advopt.txt b/doc/advopt.txt index 08465e4572..7a11e90419 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -12,6 +12,7 @@ Advanced commands: module dependency graph //dump dump all defined conditionals and search paths //check checks the project for syntax and semantic + //pretty homogenizes source code style //idetools compiler support for IDEs: possible options: --track:FILE,LINE,COL track a file/cursor position --trackDirty:DIRTY_FILE,ORIG_FILE,LINE,COL diff --git a/doc/nimrodc.txt b/doc/nimrodc.txt index 428c42f39d..90fad7f9c8 100644 --- a/doc/nimrodc.txt +++ b/doc/nimrodc.txt @@ -540,6 +540,58 @@ in C/C++). **Note**: This pragma will not exist for the LLVM backend. +Source code style +================= + +Nimrod allows you to `mix freely case and underscores as identifier separators +`_, so variables named ``MyPrecioussInt`` and +``my_preciouss_int`` are equivalent: + +.. code-block:: Nimrod + var MyPrecioussInt = 3 + # Following line compiles fine! + echo my_preciouss_int + +Since this can lead to many variants of the same source code (you can use +`nimgrep `_ instead of your typical ``grep`` to ignore style +problems) the compiler provides the command ``pretty`` to help unifying the +style of source code. Running ``nimrod pretty ugly_test.nim`` with this +example will generate a secondary file named ``ugly_test.pretty.nim`` with the +following content: + +.. code-block:: Nimrod + var MyPrecioussInt = 3 + # Following line compiles fine! + echo MyPrecioussInt + +During execution the ``pretty`` command will also run on Nimrod's standard +library, since it doesn't differentiate the standard library as something +special, and hence will warn of many *errors* which are out of your hand to +fix, creating respective ``.pretty.nim`` files all the way. You can ignore +these errors if they don't belong to your source and simply compare your +original version to the new pretty one. In fact, running ``pretty`` on our test +file will show the following:: + + Hint: ugly_test [Processing] + ugly_test.nim(1, 4) Error: name should be: myPrecioussInt + ugly_test.nim(1, 4) Error: name should be: myPrecioussInt + +At the moment ``pretty`` will homogenize the style of symbols but will leave +important changes for you to review. In this case the command is warning that a +variable name should not start with a capital letter, which is usually reserved +to `object types `_. To learn about the accepted `camel case +style `_ read `Coding Guidelines in +the Internals of Nimrod Compiler `_ or `Coding +Guidelines `_ and `NEP 1 +: Style Guide for Nimrod Code +`_ +from the Nimrod `GitHub wiki`_. + +This command is safe to run because it will never attempt to overwrite your +existing sources, but the respective ``.pretty.nim`` files **will** be +overwritten without notice. + + DynlibOverride ============== From 90e3c99baec7204bd5d6aba423bc0ffa5a97392b Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sat, 26 Jul 2014 16:58:34 +0200 Subject: [PATCH 09/37] Adds TSet.isValid(). --- lib/pure/collections/sets.nim | 101 ++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index f1eed00042..090bc971ff 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -27,6 +27,20 @@ type data: TKeyValuePairSeq[A] counter: int +proc isValid*[A](s: TSet[A]): bool = + ## Returns `true` if the set has been initialized with `initSet <#initSet>`_. + ## + ## Most operations over an uninitialized set will crash at runtime and + ## `assert `_ in debug builds. You can use this proc in + ## your own methods to verify that sets passed to your procs are correctly + ## initialized. Example: + ## + ## .. code-block :: nimrod + ## proc savePreferences(options: TSet[string]) = + ## assert options.isValid, "Pass an initialized set!" + ## # Do stuff here, may crash in release builds! + result = not s.data.isNil + proc len*[A](s: TSet[A]): int = ## returns the number of keys in `s`. result = s.counter @@ -37,6 +51,7 @@ proc card*[A](s: TSet[A]): int = iterator items*[A](s: TSet[A]): A = ## iterates over any key in the table `t`. + assert s.isValid, "The set needs to be initialized." for h in 0..high(s.data): if s.data[h].slot == seFilled: yield s.data[h].key @@ -73,12 +88,14 @@ proc mget*[A](s: var TSet[A], key: A): var A = ## value as 'key' or raises the ``EInvalidKey`` exception. This is useful ## when one overloaded 'hash' and '==' but still needs reference semantics ## for sharing. + assert s.isValid, "The set needs to be initialized." var index = rawGet(s, key) if index >= 0: result = t.data[index].key else: raise newException(EInvalidKey, "key not found: " & $key) proc contains*[A](s: TSet[A], key: A): bool = ## returns true iff `key` is in `s`. + assert s.isValid, "The set needs to be initialized." var index = rawGet(s, key) result = index >= 0 @@ -110,14 +127,18 @@ template containsOrInclImpl() {.dirty.} = proc incl*[A](s: var TSet[A], key: A) = ## includes an element `key` in `s`. + assert s.isValid, "The set needs to be initialized." inclImpl() proc incl*[A](s: var TSet[A], other: TSet[A]) = ## includes everything in `other` in `s` + assert s.isValid, "The set `s` needs to be initialized." + assert other.isValid, "The set `other` needs to be initialized." for item in other: incl(s, item) proc excl*[A](s: var TSet[A], key: A) = ## excludes `key` from the set `s`. + assert s.isValid, "The set needs to be initialized." var index = rawGet(s, key) if index >= 0: s.data[index].slot = seDeleted @@ -125,11 +146,14 @@ proc excl*[A](s: var TSet[A], key: A) = proc excl*[A](s: var TSet[A], other: TSet[A]) = ## excludes everything in `other` from `s`. + assert s.isValid, "The set `s` needs to be initialized." + assert other.isValid, "The set `other` needs to be initialized." for item in other: excl(s, item) proc containsOrIncl*[A](s: var TSet[A], key: A): bool = ## returns true if `s` contains `key`, otherwise `key` is included in `s` ## and false is returned. + assert s.isValid, "The set needs to be initialized." containsOrInclImpl() proc initSet*[A](initialSize=64): TSet[A] = @@ -153,22 +177,29 @@ template dollarImpl(): stmt {.dirty.} = proc `$`*[A](s: TSet[A]): string = ## The `$` operator for hash sets. + assert s.isValid, "The set needs to be initialized." dollarImpl() proc union*[A](s1, s2: TSet[A]): TSet[A] = ## returns a new set of all items that are contained in at ## least one of `s1` and `s2` + assert s1.isValid, "The set `s1` needs to be initialized." + assert s2.isValid, "The set `s2` needs to be initialized." result = s1 incl(result, s2) proc intersection*[A](s1, s2: TSet[A]): TSet[A] = ## returns a new set of all items that are contained in both `s1` and `s2` + assert s1.isValid, "The set `s1` needs to be initialized." + assert s2.isValid, "The set `s2` needs to be initialized." result = initSet[A](min(s1.data.len, s2.data.len)) for item in s1: if item in s2: incl(result, item) proc difference*[A](s1, s2: TSet[A]): TSet[A] = ## returns a new set of all items that are contained in `s1`, but not in `s2` + assert s1.isValid, "The set `s1` needs to be initialized." + assert s2.isValid, "The set `s2` needs to be initialized." result = initSet[A]() for item in s1: if not contains(s2, item): @@ -177,6 +208,8 @@ proc difference*[A](s1, s2: TSet[A]): TSet[A] = proc symmetricDifference*[A](s1, s2: TSet[A]): TSet[A] = ## returns a new set of all items that are contained in either ## `s1` or `s2`, but not both + assert s1.isValid, "The set `s1` needs to be initialized." + assert s2.isValid, "The set `s2` needs to be initialized." result = s1 for item in s2: if containsOrIncl(result, item): excl(result, item) @@ -199,6 +232,8 @@ proc `-+-`*[A](s1, s2: TSet[A]): TSet[A] {.inline.} = proc disjoint*[A](s1, s2: TSet[A]): bool = ## returns true iff `s1` and `s2` have no items in common + assert s1.isValid, "The set `s1` needs to be initialized." + assert s2.isValid, "The set `s2` needs to be initialized." for item in s1: if item in s2: return false return true @@ -314,3 +349,69 @@ proc `==`*[A](s, t: TSet[A]): bool = proc map*[A, B](data: TSet[A], op: proc (x: A): B {.closure.}): TSet[B] = result = initSet[B]() for item in data: result.incl(op(item)) + +proc testModule() = + ## Internal micro test to validate docstrings and such. + block isValidTest: + var options: TSet[string] + proc savePreferences(options: TSet[string]) = + assert options.isValid, "Pass an initialized set!" + options = initSet[string]() + options.savePreferences + + block lenTest: + var values: TSet[int] + assert(not values.isValid) + assert values.len == 0 + assert values.card == 0 + + block setIterator: + type pair = tuple[a, b: int] + var a, b = initSet[pair]() + a.incl((2, 3)) + a.incl((3, 2)) + a.incl((2, 3)) + for x, y in a.items: + b.incl((x - 2, y + 1)) + assert a.len == b.card + assert a.len == 2 + echo b + + block setContains: + var values = initSet[int]() + assert(not values.contains(2)) + values.incl(2) + assert values.contains(2) + values.excl(2) + assert(not values.contains(2)) + + block toSeqAndString: + var a = toSet[int]([2, 4, 5]) + var b = initSet[int]() + for x in [2, 4, 5]: b.incl(x) + assert($a == $b) + + block setOperations: + var + a = toset[string](["a", "b"]) + b = toset[string](["b", "c"]) + c = union(a, b) + assert c == toSet[string](["a", "b", "c"]) + var d = intersection(a, b) + assert d == toSet[string](["b"]) + var e = difference(a, b) + assert e == toSet[string](["a"]) + var f = symmetricDifference(a, b) + assert f == toSet[string](["a", "c"]) + # Alias test. + assert a + b == toSet[string](["a", "b", "c"]) + assert a * b == toSet[string](["b"]) + assert a - b == toSet[string](["a"]) + assert a -+- b == toSet[string](["a", "c"]) + assert disjoint(a, b) == false + assert disjoint(a, b - a) == true + + + echo "Micro tests run successfully." + +when isMainModule and not defined(release): testModule() From cdecac11c2f2311349083049a5947c467ef0e92b Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sat, 26 Jul 2014 18:58:51 +0200 Subject: [PATCH 10/37] Adds TOrderedSet.isValid(). --- lib/pure/collections/sets.nim | 58 +++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 090bc971ff..9e6aa80afd 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -249,6 +249,21 @@ type data: TOrderedKeyValuePairSeq[A] counter, first, last: int +proc isValid*[A](s: TOrderedSet[A]): bool = + ## Returns `true` if the ordered set has been initialized with `initSet + ## <#initOrderedSet>`_. + ## + ## Most operations over an uninitialized ordered set will crash at runtime + ## and `assert `_ in debug builds. You can use this proc + ## in your own methods to verify that ordered sets passed to your procs are + ## correctly initialized. Example: + ## + ## .. code-block :: nimrod + ## proc saveTarotCards(cards: TOrderedSet[int]) = + ## assert cards.isValid, "Pass an initialized set!" + ## # Do stuff here, may crash in release builds! + result = not s.data.isNil + proc len*[A](s: TOrderedSet[A]): int {.inline.} = ## returns the number of keys in `s`. result = s.counter @@ -266,6 +281,7 @@ template forAllOrderedPairs(yieldStmt: stmt) {.dirty, immediate.} = iterator items*[A](s: TOrderedSet[A]): A = ## iterates over any key in the set `s` in insertion order. + assert s.isValid, "The set needs to be initialized." forAllOrderedPairs: yield s.data[h].key @@ -274,6 +290,7 @@ proc rawGet[A](s: TOrderedSet[A], key: A): int = proc contains*[A](s: TOrderedSet[A], key: A): bool = ## returns true iff `key` is in `s`. + assert s.isValid, "The set needs to be initialized." var index = rawGet(s, key) result = index >= 0 @@ -300,15 +317,19 @@ proc enlarge[A](s: var TOrderedSet[A]) = proc incl*[A](s: var TOrderedSet[A], key: A) = ## includes an element `key` in `s`. + assert s.isValid, "The set needs to be initialized." inclImpl() proc incl*[A](s: var TSet[A], other: TOrderedSet[A]) = ## includes everything in `other` in `s` + assert s.isValid, "The set `s` needs to be initialized." + assert other.isValid, "The set `other` needs to be initialized." for item in other: incl(s, item) proc containsOrIncl*[A](s: var TOrderedSet[A], key: A): bool = ## returns true if `s` contains `key`, otherwise `key` is included in `s` ## and false is returned. + assert s.isValid, "The set needs to be initialized." containsOrInclImpl() proc initOrderedSet*[A](initialSize=64): TOrderedSet[A] = @@ -327,6 +348,7 @@ proc toOrderedSet*[A](keys: openArray[A]): TOrderedSet[A] = proc `$`*[A](s: TOrderedSet[A]): string = ## The `$` operator for ordered hash sets. + assert s.isValid, "The set needs to be initialized." dollarImpl() proc `<`*[A](s, t: TSet[A]): bool = @@ -411,6 +433,42 @@ proc testModule() = assert disjoint(a, b) == false assert disjoint(a, b - a) == true + block isValidTest: + var cards: TOrderedSet[string] + proc saveTarotCards(cards: TOrderedSet[string]) = + assert cards.isValid, "Pass an initialized set!" + cards = initOrderedSet[string]() + cards.saveTarotCards + + block lenTest: + var values: TOrderedSet[int] + assert(not values.isValid) + assert values.len == 0 + assert values.card == 0 + + block setIterator: + type pair = tuple[a, b: int] + var a, b = initOrderedSet[pair]() + a.incl((2, 3)) + a.incl((3, 2)) + a.incl((2, 3)) + for x, y in a.items: + b.incl((x - 2, y + 1)) + assert a.len == b.card + assert a.len == 2 + + block setContains: + var values = initOrderedSet[int]() + assert(not values.contains(2)) + values.incl(2) + assert values.contains(2) + + block toSeqAndString: + var a = toOrderedSet[int]([2, 4, 5]) + var b = initOrderedSet[int]() + for x in [2, 4, 5]: b.incl(x) + assert($a == $b) + # assert(a == b) # https://github.com/Araq/Nimrod/issues/1413 echo "Micro tests run successfully." From dd47fa90375f9bd6172625ec8f97f04cdb04e990 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sat, 26 Jul 2014 18:59:59 +0200 Subject: [PATCH 11/37] Moves TSet procs to their code block. --- lib/pure/collections/sets.nim | 42 +++++++++++++++++------------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 9e6aa80afd..c3895b8fab 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -238,6 +238,27 @@ proc disjoint*[A](s1, s2: TSet[A]): bool = if item in s2: return false return true +proc `<`*[A](s, t: TSet[A]): bool = + ## Is s a strict subset of t? + s.counter != t.counter and s <= t + +proc `<=`*[A](s, t: TSet[A]): bool = + ## Is s a subset of t? + result = false + if s.counter > t.counter: return + result = true + for item in s: + if not(t.contains(item)): + result = false + return + +proc `==`*[A](s, t: TSet[A]): bool = + s.counter == t.counter and s <= t + +proc map*[A, B](data: TSet[A], op: proc (x: A): B {.closure.}): TSet[B] = + result = initSet[B]() + for item in data: result.incl(op(item)) + # ------------------------------ ordered set ------------------------------ type @@ -351,27 +372,6 @@ proc `$`*[A](s: TOrderedSet[A]): string = assert s.isValid, "The set needs to be initialized." dollarImpl() -proc `<`*[A](s, t: TSet[A]): bool = - ## Is s a strict subset of t? - s.counter != t.counter and s <= t - -proc `<=`*[A](s, t: TSet[A]): bool = - ## Is s a subset of t? - result = false - if s.counter > t.counter: return - result = true - for item in s: - if not(t.contains(item)): - result = false - return - -proc `==`*[A](s, t: TSet[A]): bool = - s.counter == t.counter and s <= t - -proc map*[A, B](data: TSet[A], op: proc (x: A): B {.closure.}): TSet[B] = - result = initSet[B]() - for item in data: result.incl(op(item)) - proc testModule() = ## Internal micro test to validate docstrings and such. block isValidTest: From 6c3b967de358e0b6504ccd8e138799369aab9d1c Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sat, 26 Jul 2014 19:12:57 +0200 Subject: [PATCH 12/37] Adds test cases for remaining TSet procs. --- lib/pure/collections/sets.nim | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index c3895b8fab..4af2b7ad49 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -425,6 +425,10 @@ proc testModule() = assert e == toSet[string](["a"]) var f = symmetricDifference(a, b) assert f == toSet[string](["a", "c"]) + assert d < a and d < b + assert((a < a) == false) + assert d <= a and d <= b + assert((a <= a)) # Alias test. assert a + b == toSet[string](["a", "b", "c"]) assert a * b == toSet[string](["b"]) @@ -433,6 +437,11 @@ proc testModule() = assert disjoint(a, b) == false assert disjoint(a, b - a) == true + block mapSet: + var a = toSet[int]([1, 2, 3]) + var b = a.map(proc (x: int): string = $x) + assert b == toSet[string](["1", "2", "3"]) + block isValidTest: var cards: TOrderedSet[string] proc saveTarotCards(cards: TOrderedSet[string]) = From d21b682268d22e38186ddd8a24b5ad8d78442e5c Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sat, 26 Jul 2014 21:40:10 +0200 Subject: [PATCH 13/37] Adds TOrderedSet.init(), wraps initOrderedSet around it. --- lib/pure/collections/sets.nim | 57 ++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 4af2b7ad49..4cdc8cf514 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -266,7 +266,11 @@ type slot: TSlotEnum, next: int, key: A] TOrderedKeyValuePairSeq[A] = seq[TOrderedKeyValuePair[A]] TOrderedSet* {. - final, myShallow.}[A] = object ## set that remembers insertion order + final, myShallow.}[A] = object ## \ + ## A generic hash set that remembers insertion order. + ## + ## Use `init() <#init,TOrderedSet[A]>`_ or `initOrderedSet[type]() + ## <#initOrderedSet>`_ before calling other procs on it. data: TOrderedKeyValuePairSeq[A] counter, first, last: int @@ -353,14 +357,41 @@ proc containsOrIncl*[A](s: var TOrderedSet[A], key: A): bool = assert s.isValid, "The set needs to be initialized." containsOrInclImpl() -proc initOrderedSet*[A](initialSize=64): TOrderedSet[A] = - ## creates a new ordered hash set that is empty. `initialSize` needs to be - ## a power of two. +proc init*[A](s: var TOrderedSet[A], initialSize=64) = + ## Initializes an ordered hash set. + ## + ## The `initialSize` parameter needs to be a power of too. You can use + ## `math.nextPowerOfTwo() `_ to guarantee that at + ## runtime. All set variables have to be initialized before you can use them + ## with other procs from this module with the exception of `isValid() + ## <#isValid,TOrderedSet[A]>`_ and `len() <#len,TOrderedSet[A]>`_. + ## + ## You can call this method on a previously initialized ordered hash set to + ## discard its values. At the moment this is the only method to remove + ## elements from an ordered hash set. Example: + ## + ## .. code-block :: + ## var a: TOrderedSet[int] + ## a.init(4) + ## a.incl(2) + ## a.init + ## assert a.len == 0 and a.isValid assert isPowerOfTwo(initialSize) - result.counter = 0 - result.first = -1 - result.last = -1 - newSeq(result.data, initialSize) + s.counter = 0 + s.first = -1 + s.last = -1 + newSeq(s.data, initialSize) + +proc initOrderedSet*[A](initialSize=64): TOrderedSet[A] = + ## Convenience wrapper around `init() <#init,TOrderedSet[A]>`_. + ## + ## Returns an empty ordered hash set you can assign directly in ``var`` + ## blocks in a single line. Example: + ## + ## .. code-block :: + ## var a = initOrderedSet[int](4) + ## a.incl(2) + result.init(initialSize) proc toOrderedSet*[A](keys: openArray[A]): TOrderedSet[A] = ## creates a new ordered hash set that contains the given `keys`. @@ -479,6 +510,16 @@ proc testModule() = assert($a == $b) # assert(a == b) # https://github.com/Araq/Nimrod/issues/1413 + block initBlocks: + var a: TOrderedSet[int] + a.init(4) + a.incl(2) + a.init + assert a.len == 0 and a.isValid + a = initOrderedSet[int](4) + a.incl(2) + assert a.len == 1 + echo "Micro tests run successfully." when isMainModule and not defined(release): testModule() From 0bfe956c05f1bbf21b96b4c7319adfabfced600d Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sat, 26 Jul 2014 22:10:15 +0200 Subject: [PATCH 14/37] Adds TSet.init(), wraps initSet around it. --- lib/pure/collections/sets.nim | 52 +++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 4cdc8cf514..988a68b980 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -23,7 +23,11 @@ type TSlotEnum = enum seEmpty, seFilled, seDeleted TKeyValuePair[A] = tuple[slot: TSlotEnum, key: A] TKeyValuePairSeq[A] = seq[TKeyValuePair[A]] - TSet* {.final, myShallow.}[A] = object ## a generic hash set + TSet* {.final, myShallow.}[A] = object ## \ + ## A generic hash set. + ## + ## Use `init() <#init,TSet[A]>`_ or `initSet[type]() <#initSet>`_ before + ## calling other procs on it. data: TKeyValuePairSeq[A] counter: int @@ -156,12 +160,39 @@ proc containsOrIncl*[A](s: var TSet[A], key: A): bool = assert s.isValid, "The set needs to be initialized." containsOrInclImpl() -proc initSet*[A](initialSize=64): TSet[A] = - ## creates a new hash set that is empty. `initialSize` needs to be - ## a power of two. +proc init*[A](s: var TSet[A], initialSize=64) = + ## Initializes a hash set. + ## + ## The `initialSize` parameter needs to be a power of too. You can use + ## `math.nextPowerOfTwo() `_ to guarantee that at + ## runtime. All set variables have to be initialized before you can use them + ## with other procs from this module with the exception of `isValid() + ## <#isValid,TSet[A]>`_ and `len() <#len,TSet[A]>`_. + ## + ## You can call this method on a previously initialized hash set, which will + ## discard all its values. This might be more convenient than iterating over + ## existing values and calling `excl() <#excl,TSet[A],A>`_ on them. Example: + ## + ## .. code-block :: + ## var a: TSet[int] + ## a.init(4) + ## a.incl(2) + ## a.init + ## assert a.len == 0 and a.isValid assert isPowerOfTwo(initialSize) - result.counter = 0 - newSeq(result.data, initialSize) + s.counter = 0 + newSeq(s.data, initialSize) + +proc initSet*[A](initialSize=64): TSet[A] = + ## Convenience wrapper around `init() <#init,TSet[A]>`_. + ## + ## Returns an empty hash set you can assign directly in ``var`` blocks in a + ## single line. Example: + ## + ## .. code-block :: + ## var a = initSet[int](4) + ## a.incl(2) + result.init(initialSize) proc toSet*[A](keys: openArray[A]): TSet[A] = ## creates a new hash set that contains the given `keys`. @@ -520,6 +551,15 @@ proc testModule() = a.incl(2) assert a.len == 1 + var b: TSet[int] + b.init(4) + b.incl(2) + b.init + assert b.len == 0 and b.isValid + b = initSet[int](4) + b.incl(2) + assert b.len == 1 + echo "Micro tests run successfully." when isMainModule and not defined(release): testModule() From 48a13a76c8d754496b604f96bb2cf3238ff24937 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sat, 26 Jul 2014 22:42:32 +0200 Subject: [PATCH 15/37] Adds more docstrings to the sets module. --- lib/pure/collections/sets.nim | 435 +++++++++++++++++++++++++++++----- 1 file changed, 374 insertions(+), 61 deletions(-) diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 988a68b980..f63805e4e7 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -9,6 +9,10 @@ ## The ``sets`` module implements an efficient hash set and ordered hash set. ## +## Hash sets are different from the `built in set type +## `_. Sets allow you to store any value that can be +## `hashed `_ and they don't contain duplicate entries. +## ## **Note**: The data types declared here have *value semantics*: This means ## that ``=`` performs a copy of the set. @@ -26,8 +30,8 @@ type TSet* {.final, myShallow.}[A] = object ## \ ## A generic hash set. ## - ## Use `init() <#init,TSet[A]>`_ or `initSet[type]() <#initSet>`_ before - ## calling other procs on it. + ## Use `init() <#init,TSet[A],int>`_ or `initSet[type]() <#initSet>`_ + ## before calling other procs on it. data: TKeyValuePairSeq[A] counter: int @@ -36,7 +40,7 @@ proc isValid*[A](s: TSet[A]): bool = ## ## Most operations over an uninitialized set will crash at runtime and ## `assert `_ in debug builds. You can use this proc in - ## your own methods to verify that sets passed to your procs are correctly + ## your own procs to verify that sets passed to your procs are correctly ## initialized. Example: ## ## .. code-block :: nimrod @@ -46,15 +50,42 @@ proc isValid*[A](s: TSet[A]): bool = result = not s.data.isNil proc len*[A](s: TSet[A]): int = - ## returns the number of keys in `s`. + ## Returns the number of keys in `s`. + ## + ## Due to an implementation detail you can call this proc on variables which + ## have not been initialized yet. The proc will return zero as the length + ## then. Example: + ## + ## .. code-block:: + ## + ## var values: TSet[int] + ## assert(not values.isValid) + ## assert values.len == 0 result = s.counter proc card*[A](s: TSet[A]): int = - ## alias for `len`. + ## Alias for `len() <#len,TSet[A]>`_. result = s.counter iterator items*[A](s: TSet[A]): A = - ## iterates over any key in the table `t`. + ## Iterates over keys in the set `s`. + ## + ## If you need a sequence with the keys you can use `sequtils.toSeq() + ## `_ on the iterator. Usage example: + ## + ## .. code-block:: + ## type + ## pair = tuple[a, b: int] + ## var + ## a, b = initSet[pair]() + ## a.incl((2, 3)) + ## a.incl((3, 2)) + ## a.incl((2, 3)) + ## for x, y in a.items: + ## b.incl((x - 2, y + 1)) + ## assert a.len == 2 + ## echo b + ## # --> {(a: 1, b: 3), (a: 0, b: 4)} assert s.isValid, "The set needs to be initialized." for h in 0..high(s.data): if s.data[h].slot == seFilled: yield s.data[h].key @@ -98,7 +129,17 @@ proc mget*[A](s: var TSet[A], key: A): var A = else: raise newException(EInvalidKey, "key not found: " & $key) proc contains*[A](s: TSet[A], key: A): bool = - ## returns true iff `key` is in `s`. + ## Returns true iff `key` is in `s`. + ## + ## Example: + ## + ## .. code-block:: + ## var values = initSet[int]() + ## assert(not values.contains(2)) + ## values.incl(2) + ## assert values.contains(2) + ## values.excl(2) + ## assert(not values.contains(2)) assert s.isValid, "The set needs to be initialized." var index = rawGet(s, key) result = index >= 0 @@ -130,18 +171,43 @@ template containsOrInclImpl() {.dirty.} = inc(s.counter) proc incl*[A](s: var TSet[A], key: A) = - ## includes an element `key` in `s`. + ## Includes an element `key` in `s`. + ## + ## This doesn't do anything if `key` is already in `s`. Example: + ## + ## .. code-block:: + ## var values = initSet[int]() + ## values.incl(2) + ## values.incl(2) + ## assert values.len == 1 assert s.isValid, "The set needs to be initialized." inclImpl() proc incl*[A](s: var TSet[A], other: TSet[A]) = - ## includes everything in `other` in `s` + ## Includes all elements from `other` into `s`. + ## + ## Example: + ## + ## .. code-block:: + ## var values = initSet[int]() + ## values.incl(2) + ## var others = toSet([6, 7]) + ## values.incl(others) + ## assert values.len == 3 assert s.isValid, "The set `s` needs to be initialized." assert other.isValid, "The set `other` needs to be initialized." for item in other: incl(s, item) proc excl*[A](s: var TSet[A], key: A) = - ## excludes `key` from the set `s`. + ## Excludes `key` from the set `s`. + ## + ## This doesn't do anything if `key` is not found in `s`. Example: + ## + ## .. code-block:: + ## var s = toSet([2, 3, 6, 7]) + ## s.excl(2) + ## s.excl(2) + ## assert s.len == 3 assert s.isValid, "The set needs to be initialized." var index = rawGet(s, key) if index >= 0: @@ -149,14 +215,33 @@ proc excl*[A](s: var TSet[A], key: A) = dec(s.counter) proc excl*[A](s: var TSet[A], other: TSet[A]) = - ## excludes everything in `other` from `s`. + ## Excludes everything in `other` from `s`. + ## + ## Example: + ## + ## .. code-block:: + ## var + ## numbers = toSet([1, 2, 3, 4, 5]) + ## even = toSet([2, 4, 6, 8]) + ## numbers.excl(even) + ## echo numbers + ## # --> {1, 3, 5} assert s.isValid, "The set `s` needs to be initialized." assert other.isValid, "The set `other` needs to be initialized." for item in other: excl(s, item) proc containsOrIncl*[A](s: var TSet[A], key: A): bool = - ## returns true if `s` contains `key`, otherwise `key` is included in `s` - ## and false is returned. + ## Includes `key` in the set `s` and tells if `key` was added to `s`. + ## + ## The difference with regards to the `incl() <#incl,TSet[A],A>`_ proc is + ## that this proc returns `true` if `key` was already present in `s`. The + ## proc will return false if `key` was added as a new value to `s` during + ## this call. Example: + ## + ## .. code-block:: + ## var values = initSet[int]() + ## assert values.containsOrIncl(2) == false + ## assert values.containsOrIncl(2) == true assert s.isValid, "The set needs to be initialized." containsOrInclImpl() @@ -169,7 +254,7 @@ proc init*[A](s: var TSet[A], initialSize=64) = ## with other procs from this module with the exception of `isValid() ## <#isValid,TSet[A]>`_ and `len() <#len,TSet[A]>`_. ## - ## You can call this method on a previously initialized hash set, which will + ## You can call this proc on a previously initialized hash set, which will ## discard all its values. This might be more convenient than iterating over ## existing values and calling `excl() <#excl,TSet[A],A>`_ on them. Example: ## @@ -184,7 +269,8 @@ proc init*[A](s: var TSet[A], initialSize=64) = newSeq(s.data, initialSize) proc initSet*[A](initialSize=64): TSet[A] = - ## Convenience wrapper around `init() <#init,TSet[A]>`_. + ## Wrapper around `init() <#init,TSet[A],int>`_ for initialization of hash + ## sets. ## ## Returns an empty hash set you can assign directly in ``var`` blocks in a ## single line. Example: @@ -195,7 +281,14 @@ proc initSet*[A](initialSize=64): TSet[A] = result.init(initialSize) proc toSet*[A](keys: openArray[A]): TSet[A] = - ## creates a new hash set that contains the given `keys`. + ## Creates a new hash set that contains the given `keys`. + ## + ## Example: + ## + ## .. code-block:: + ## var numbers = toSet([1, 2, 3, 4, 5]) + ## assert numbers.contains(2) + ## assert numbers.contains(4) result = initSet[A](nextPowerOfTwo(keys.len+10)) for key in items(keys): result.incl(key) @@ -207,20 +300,51 @@ template dollarImpl(): stmt {.dirty.} = result.add("}") proc `$`*[A](s: TSet[A]): string = - ## The `$` operator for hash sets. + ## Converts the set `s` to a string, mostly for logging purposes. + ## + ## Don't use this proc for serialization, the representation may change at + ## any moment and values are not escaped. Example: + ## + ## Example: + ## + ## .. code-block:: + ## echo toSet([2, 4, 5]) + ## # --> {2, 4, 5} + ## echo toSet(["no", "esc'aping", "is \" provided"]) + ## # --> {no, esc'aping, is " provided} assert s.isValid, "The set needs to be initialized." dollarImpl() proc union*[A](s1, s2: TSet[A]): TSet[A] = - ## returns a new set of all items that are contained in at - ## least one of `s1` and `s2` + ## Returns the union of the sets `s1` and `s2`. + ## + ## The union of two sets is represented mathematically as *A ∪ B* and is the + ## set of all objects that are members of `s1`, `s2` or both. Example: + ## + ## .. code-block:: + ## var + ## a = toSet(["a", "b"]) + ## b = toSet(["b", "c"]) + ## c = union(a, b) + ## assert c == toSet(["a", "b", "c"]) assert s1.isValid, "The set `s1` needs to be initialized." assert s2.isValid, "The set `s2` needs to be initialized." result = s1 incl(result, s2) proc intersection*[A](s1, s2: TSet[A]): TSet[A] = - ## returns a new set of all items that are contained in both `s1` and `s2` + ## Returns the intersection of the sets `s1` and `s2`. + ## + ## The intersection of two sets is represented mathematically as *A ∩ B* and + ## is the set of all objects that are members of `s1` and `s2` at the same + ## time. Example: + ## + ## .. code-block:: + ## var + ## a = toSet(["a", "b"]) + ## b = toSet(["b", "c"]) + ## c = intersection(a, b) + ## assert c == toSet(["b"]) assert s1.isValid, "The set `s1` needs to be initialized." assert s2.isValid, "The set `s2` needs to be initialized." result = initSet[A](min(s1.data.len, s2.data.len)) @@ -228,7 +352,18 @@ proc intersection*[A](s1, s2: TSet[A]): TSet[A] = if item in s2: incl(result, item) proc difference*[A](s1, s2: TSet[A]): TSet[A] = - ## returns a new set of all items that are contained in `s1`, but not in `s2` + ## Returns the difference of the sets `s1` and `s2`. + ## + ## The difference of two sets is represented mathematically as *A \ B* and is + ## the set of all objects that are members of `s1` and not members of `s2`. + ## Example: + ## + ## .. code-block:: + ## var + ## a = toSet(["a", "b"]) + ## b = toSet(["b", "c"]) + ## c = difference(a, b) + ## assert c == toSet(["a"]) assert s1.isValid, "The set `s1` needs to be initialized." assert s2.isValid, "The set `s2` needs to be initialized." result = initSet[A]() @@ -237,8 +372,18 @@ proc difference*[A](s1, s2: TSet[A]): TSet[A] = incl(result, item) proc symmetricDifference*[A](s1, s2: TSet[A]): TSet[A] = - ## returns a new set of all items that are contained in either - ## `s1` or `s2`, but not both + ## Returns the symmetric difference of the sets `s1` and `s2`. + ## + ## The symmetric difference of two sets is represented mathematically as *A △ + ## B* or *A ⊖ B* and is the set of all objects that are members of `s1` or + ## `s2` but not both at the same time. Example: + ## + ## .. code-block:: + ## var + ## a = toSet(["a", "b"]) + ## b = toSet(["b", "c"]) + ## c = symmetricDifference(a, b) + ## assert c == toSet(["a", "c"]) assert s1.isValid, "The set `s1` needs to be initialized." assert s2.isValid, "The set `s2` needs to be initialized." result = s1 @@ -246,23 +391,32 @@ proc symmetricDifference*[A](s1, s2: TSet[A]): TSet[A] = if containsOrIncl(result, item): excl(result, item) proc `+`*[A](s1, s2: TSet[A]): TSet[A] {.inline.} = - ## alias for `union` + ## Alias for `union(s1, s2) <#union>`_. result = union(s1, s2) proc `*`*[A](s1, s2: TSet[A]): TSet[A] {.inline.} = - ## alias for `intersection` + ## Alias for `intersection(s1, s2) <#intersection>`_. result = intersection(s1, s2) proc `-`*[A](s1, s2: TSet[A]): TSet[A] {.inline.} = - ## alias for `difference` + ## Alias for `difference(s1, s2) <#difference>`_. result = difference(s1, s2) proc `-+-`*[A](s1, s2: TSet[A]): TSet[A] {.inline.} = - ## alias for `symmetricDifference` + ## Alias for `symmetricDifference(s1, s2) <#symmetricDifference>`_. result = symmetricDifference(s1, s2) proc disjoint*[A](s1, s2: TSet[A]): bool = - ## returns true iff `s1` and `s2` have no items in common + ## Returns true iff the sets `s1` and `s2` have no items in common. + ## + ## Example: + ## + ## .. code-block:: + ## var + ## a = toSet(["a", "b"]) + ## b = toSet(["b", "c"]) + ## assert disjoint(a, b) == false + ## assert disjoint(a, b - a) == true assert s1.isValid, "The set `s1` needs to be initialized." assert s2.isValid, "The set `s2` needs to be initialized." for item in s1: @@ -270,11 +424,33 @@ proc disjoint*[A](s1, s2: TSet[A]): bool = return true proc `<`*[A](s, t: TSet[A]): bool = - ## Is s a strict subset of t? + ## Returns true if `s` is a strict or proper subset of `t`. + ## + ## A strict or proper subset `s` has all of its members in `t` but `t` has + ## more elements than `s`. Example: + ## + ## .. code-block:: + ## var + ## a = toSet(["a", "b"]) + ## b = toSet(["b", "c"]) + ## c = intersection(a, b) + ## assert c < a and c < b + ## assert((a < a) == false) s.counter != t.counter and s <= t proc `<=`*[A](s, t: TSet[A]): bool = - ## Is s a subset of t? + ## Returns true if `s` is subset of `t`. + ## + ## A subset `s` has all of its members in `t` and `t` doesn't necessarily + ## have more members than `s`. That is, `s` can be equal to `t`. Example: + ## + ## .. code-block:: + ## var + ## a = toSet(["a", "b"]) + ## b = toSet(["b", "c"]) + ## c = intersection(a, b) + ## assert c <= a and c <= b + ## assert((a <= a)) result = false if s.counter > t.counter: return result = true @@ -284,9 +460,27 @@ proc `<=`*[A](s, t: TSet[A]): bool = return proc `==`*[A](s, t: TSet[A]): bool = + ## Returns true if both `s` and `t` have the same members and set size. + ## + ## Example: + ## + ## .. code-block:: + ## var + ## a = toSet([1, 2]) + ## b = toSet([1]) + ## b.incl(2) + ## assert a == b s.counter == t.counter and s <= t proc map*[A, B](data: TSet[A], op: proc (x: A): B {.closure.}): TSet[B] = + ## Returns a new set after applying `op` on each of the elements of `data`. + ## + ## You can use this proc to transform the elements from a set. Example: + ## + ## .. code-block:: + ## var a = toSet([1, 2, 3]) + ## var b = a.map(proc (x: int): string = $x) + ## assert b == toSet(["1", "2", "3"]) result = initSet[B]() for item in data: result.incl(op(item)) @@ -300,7 +494,7 @@ type final, myShallow.}[A] = object ## \ ## A generic hash set that remembers insertion order. ## - ## Use `init() <#init,TOrderedSet[A]>`_ or `initOrderedSet[type]() + ## Use `init() <#init,TOrderedSet[A],int>`_ or `initOrderedSet[type]() ## <#initOrderedSet>`_ before calling other procs on it. data: TOrderedKeyValuePairSeq[A] counter, first, last: int @@ -311,7 +505,7 @@ proc isValid*[A](s: TOrderedSet[A]): bool = ## ## Most operations over an uninitialized ordered set will crash at runtime ## and `assert `_ in debug builds. You can use this proc - ## in your own methods to verify that ordered sets passed to your procs are + ## in your own procs to verify that ordered sets passed to your procs are ## correctly initialized. Example: ## ## .. code-block :: nimrod @@ -321,11 +515,21 @@ proc isValid*[A](s: TOrderedSet[A]): bool = result = not s.data.isNil proc len*[A](s: TOrderedSet[A]): int {.inline.} = - ## returns the number of keys in `s`. + ## Returns the number of keys in `s`. + ## + ## Due to an implementation detail you can call this proc on variables which + ## have not been initialized yet. The proc will return zero as the length + ## then. Example: + ## + ## .. code-block:: + ## + ## var values: TOrderedSet[int] + ## assert(not values.isValid) + ## assert values.len == 0 result = s.counter proc card*[A](s: TOrderedSet[A]): int {.inline.} = - ## alias for `len`. + ## Alias for `len() <#len,TOrderedSet[A]>`_. result = s.counter template forAllOrderedPairs(yieldStmt: stmt) {.dirty, immediate.} = @@ -336,7 +540,23 @@ template forAllOrderedPairs(yieldStmt: stmt) {.dirty, immediate.} = h = nxt iterator items*[A](s: TOrderedSet[A]): A = - ## iterates over any key in the set `s` in insertion order. + ## Iterates over keys in the ordered set `s` in insertion order. + ## + ## If you need a sequence with the keys you can use `sequtils.toSeq() + ## `_ on the iterator. Usage example: + ## + ## .. code-block:: + ## var a = initOrderedSet[int]() + ## for value in [9, 2, 1, 5, 1, 8, 4, 2]: + ## a.incl(value) + ## for value in a.items: + ## echo "Got ", value + ## # --> Got 9 + ## # --> Got 2 + ## # --> Got 1 + ## # --> Got 5 + ## # --> Got 8 + ## # --> Got 4 assert s.isValid, "The set needs to be initialized." forAllOrderedPairs: yield s.data[h].key @@ -345,7 +565,15 @@ proc rawGet[A](s: TOrderedSet[A], key: A): int = rawGetImpl() proc contains*[A](s: TOrderedSet[A], key: A): bool = - ## returns true iff `key` is in `s`. + ## Returns true iff `key` is in `s`. + ## + ## Example: + ## + ## .. code-block:: + ## var values = initOrderedSet[int]() + ## assert(not values.contains(2)) + ## values.incl(2) + ## assert values.contains(2) assert s.isValid, "The set needs to be initialized." var index = rawGet(s, key) result = index >= 0 @@ -372,19 +600,45 @@ proc enlarge[A](s: var TOrderedSet[A]) = swap(s.data, n) proc incl*[A](s: var TOrderedSet[A], key: A) = - ## includes an element `key` in `s`. + ## Includes an element `key` in `s`. + ## + ## This doesn't do anything if `key` is already in `s`. Example: + ## + ## .. code-block:: + ## var values = initOrderedSet[int]() + ## values.incl(2) + ## values.incl(2) + ## assert values.len == 1 assert s.isValid, "The set needs to be initialized." inclImpl() proc incl*[A](s: var TSet[A], other: TOrderedSet[A]) = - ## includes everything in `other` in `s` + ## Includes all elements from `other` into `s`. + ## + ## Example: + ## + ## .. code-block:: + ## var values = initOrderedSet[int]() + ## values.incl(2) + ## var others = toOrderedSet([6, 7]) + ## values.incl(others) + ## assert values.len == 3 assert s.isValid, "The set `s` needs to be initialized." assert other.isValid, "The set `other` needs to be initialized." for item in other: incl(s, item) proc containsOrIncl*[A](s: var TOrderedSet[A], key: A): bool = - ## returns true if `s` contains `key`, otherwise `key` is included in `s` - ## and false is returned. + ## Includes `key` in the set `s` and tells if `key` was added to `s`. + ## + ## The difference with regards to the `incl() <#incl,TOrderedSet[A],A>`_ proc + ## is that this proc returns `true` if `key` was already present in `s`. The + ## proc will return false if `key` was added as a new value to `s` during + ## this call. Example: + ## + ## .. code-block:: + ## var values = initOrderedSet[int]() + ## assert values.containsOrIncl(2) == false + ## assert values.containsOrIncl(2) == true assert s.isValid, "The set needs to be initialized." containsOrInclImpl() @@ -397,9 +651,9 @@ proc init*[A](s: var TOrderedSet[A], initialSize=64) = ## with other procs from this module with the exception of `isValid() ## <#isValid,TOrderedSet[A]>`_ and `len() <#len,TOrderedSet[A]>`_. ## - ## You can call this method on a previously initialized ordered hash set to - ## discard its values. At the moment this is the only method to remove - ## elements from an ordered hash set. Example: + ## You can call this proc on a previously initialized ordered hash set to + ## discard its values. At the moment this is the only proc to remove elements + ## from an ordered hash set. Example: ## ## .. code-block :: ## var a: TOrderedSet[int] @@ -414,7 +668,8 @@ proc init*[A](s: var TOrderedSet[A], initialSize=64) = newSeq(s.data, initialSize) proc initOrderedSet*[A](initialSize=64): TOrderedSet[A] = - ## Convenience wrapper around `init() <#init,TOrderedSet[A]>`_. + ## Wrapper around `init() <#init,TOrderedSet[A],int>`_ for initialization of + ## ordered hash sets. ## ## Returns an empty ordered hash set you can assign directly in ``var`` ## blocks in a single line. Example: @@ -425,12 +680,30 @@ proc initOrderedSet*[A](initialSize=64): TOrderedSet[A] = result.init(initialSize) proc toOrderedSet*[A](keys: openArray[A]): TOrderedSet[A] = - ## creates a new ordered hash set that contains the given `keys`. + ## Creates a new ordered hash set that contains the given `keys`. + ## + ## Example: + ## + ## .. code-block:: + ## var numbers = toOrderedSet([1, 2, 3, 4, 5]) + ## assert numbers.contains(2) + ## assert numbers.contains(4) result = initOrderedSet[A](nextPowerOfTwo(keys.len+10)) for key in items(keys): result.incl(key) proc `$`*[A](s: TOrderedSet[A]): string = - ## The `$` operator for ordered hash sets. + ## Converts the ordered hash set `s` to a string, mostly for logging purposes. + ## + ## Don't use this proc for serialization, the representation may change at + ## any moment and values are not escaped. Example: + ## + ## Example: + ## + ## .. code-block:: + ## echo toOrderedSet([2, 4, 5]) + ## # --> {2, 4, 5} + ## echo toOrderedSet(["no", "esc'aping", "is \" provided"]) + ## # --> {no, esc'aping, is " provided} assert s.isValid, "The set needs to be initialized." dollarImpl() @@ -459,7 +732,7 @@ proc testModule() = b.incl((x - 2, y + 1)) assert a.len == b.card assert a.len == 2 - echo b + #echo b block setContains: var values = initSet[int]() @@ -469,40 +742,73 @@ proc testModule() = values.excl(2) assert(not values.contains(2)) + values.incl(4) + var others = toSet([6, 7]) + values.incl(others) + assert values.len == 3 + + values.init + assert values.containsOrIncl(2) == false + assert values.containsOrIncl(2) == true + var + a = toSet([1, 2]) + b = toSet([1]) + b.incl(2) + assert a == b + + block exclusions: + var s = toSet([2, 3, 6, 7]) + s.excl(2) + s.excl(2) + assert s.len == 3 + + var + numbers = toSet([1, 2, 3, 4, 5]) + even = toSet([2, 4, 6, 8]) + numbers.excl(even) + #echo numbers + # --> {1, 3, 5} + block toSeqAndString: - var a = toSet[int]([2, 4, 5]) + var a = toSet([2, 4, 5]) var b = initSet[int]() for x in [2, 4, 5]: b.incl(x) assert($a == $b) + #echo a + #echo toSet(["no", "esc'aping", "is \" provided"]) + + #block orderedToSeqAndString: + # echo toOrderedSet([2, 4, 5]) + # echo toOrderedSet(["no", "esc'aping", "is \" provided"]) block setOperations: var - a = toset[string](["a", "b"]) - b = toset[string](["b", "c"]) + a = toSet(["a", "b"]) + b = toSet(["b", "c"]) c = union(a, b) - assert c == toSet[string](["a", "b", "c"]) + assert c == toSet(["a", "b", "c"]) var d = intersection(a, b) - assert d == toSet[string](["b"]) + assert d == toSet(["b"]) var e = difference(a, b) - assert e == toSet[string](["a"]) + assert e == toSet(["a"]) var f = symmetricDifference(a, b) - assert f == toSet[string](["a", "c"]) + assert f == toSet(["a", "c"]) assert d < a and d < b assert((a < a) == false) assert d <= a and d <= b assert((a <= a)) # Alias test. - assert a + b == toSet[string](["a", "b", "c"]) - assert a * b == toSet[string](["b"]) - assert a - b == toSet[string](["a"]) - assert a -+- b == toSet[string](["a", "c"]) + assert a + b == toSet(["a", "b", "c"]) + assert a * b == toSet(["b"]) + assert a - b == toSet(["a"]) + assert a -+- b == toSet(["a", "c"]) assert disjoint(a, b) == false assert disjoint(a, b - a) == true block mapSet: - var a = toSet[int]([1, 2, 3]) + var a = toSet([1, 2, 3]) var b = a.map(proc (x: int): string = $x) - assert b == toSet[string](["1", "2", "3"]) + assert b == toSet(["1", "2", "3"]) block isValidTest: var cards: TOrderedSet[string] @@ -528,6 +834,13 @@ proc testModule() = assert a.len == b.card assert a.len == 2 + #block orderedSetIterator: + # var a = initOrderedSet[int]() + # for value in [9, 2, 1, 5, 1, 8, 4, 2]: + # a.incl(value) + # for value in a.items: + # echo "Got ", value + block setContains: var values = initOrderedSet[int]() assert(not values.contains(2)) @@ -535,7 +848,7 @@ proc testModule() = assert values.contains(2) block toSeqAndString: - var a = toOrderedSet[int]([2, 4, 5]) + var a = toOrderedSet([2, 4, 5]) var b = initOrderedSet[int]() for x in [2, 4, 5]: b.incl(x) assert($a == $b) From b87ec14579ac89b56be9d0b5788f3788f836245a Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sun, 27 Jul 2014 00:21:23 +0200 Subject: [PATCH 16/37] Factors common documentation in fragment to avoid repetition. --- doc/manual.txt | 32 +------------------------------- doc/sets_fragment.txt | 40 ++++++++++++++++++++++++++++++++++++++++ doc/tut1.txt | 41 +---------------------------------------- 3 files changed, 42 insertions(+), 71 deletions(-) create mode 100644 doc/sets_fragment.txt diff --git a/doc/manual.txt b/doc/manual.txt index 54c1477e8c..53700ae807 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -1221,38 +1221,8 @@ branch switch ``system.reset`` has to be used. Set type -------- -The set type models the mathematical notion of a set. The set's -basetype can only be an ordinal type. The reason is that sets are implemented -as high performance bit vectors. - -Sets can be constructed via the set constructor: ``{}`` is the empty set. The -empty set is type compatible with any special set type. The constructor -can also be used to include elements (and ranges of elements) in the set: - -.. code-block:: nimrod - - {'a'..'z', '0'..'9'} # This constructs a set that contains the - # letters from 'a' to 'z' and the digits - # from '0' to '9' - -These operations are supported by sets: - -================== ======================================================== -operation meaning -================== ======================================================== -``A + B`` union of two sets -``A * B`` intersection of two sets -``A - B`` difference of two sets (A without B's elements) -``A == B`` set equality -``A <= B`` subset relation (A is subset of B or equal to B) -``A < B`` strong subset relation (A is a real subset of B) -``e in A`` set membership (A contains element e) -``A -+- B`` symmetric set difference (= (A - B) + (B - A)) -``card(A)`` the cardinality of A (number of elements in A) -``incl(A, elem)`` same as A = A + {elem} -``excl(A, elem)`` same as A = A - {elem} -================== ======================================================== +.. include:: sets_fragment.txt Reference and pointer types --------------------------- diff --git a/doc/sets_fragment.txt b/doc/sets_fragment.txt new file mode 100644 index 0000000000..fba3552690 --- /dev/null +++ b/doc/sets_fragment.txt @@ -0,0 +1,40 @@ +The set type models the mathematical notion of a set. The set's +basetype can only be an ordinal type. The reason is that sets are implemented +as high performance bit vectors. + +Sets can be constructed via the set constructor: ``{}`` is the empty set. The +empty set is type compatible with any concrete set type. The constructor +can also be used to include elements (and ranges of elements): + +.. code-block:: nimrod + type + TCharSet = set[char] + var + x: TCharSet + x = {'a'..'z', '0'..'9'} # This constructs a set that contains the + # letters from 'a' to 'z' and the digits + # from '0' to '9' + +These operations are supported by sets: + +================== ======================================================== +operation meaning +================== ======================================================== +``A + B`` union of two sets +``A * B`` intersection of two sets +``A - B`` difference of two sets (A without B's elements) +``A == B`` set equality +``A <= B`` subset relation (A is subset of B or equal to B) +``A < B`` strong subset relation (A is a real subset of B) +``e in A`` set membership (A contains element e) +``e notin A`` A does not contain element e +``contains(A, e)`` A contains element e +``A -+- B`` symmetric set difference (= (A - B) + (B - A)) +``card(A)`` the cardinality of A (number of elements in A) +``incl(A, elem)`` same as ``A = A + {elem}`` +``excl(A, elem)`` same as ``A = A - {elem}`` +================== ======================================================== + +Sets are often used to define a type for the *flags* of a procedure. This is +a much cleaner (and type safe) solution than just defining integer +constants that should be ``or``'ed together. diff --git a/doc/tut1.txt b/doc/tut1.txt index a2aa835ee5..55eb0ebd74 100644 --- a/doc/tut1.txt +++ b/doc/tut1.txt @@ -1117,47 +1117,8 @@ avoid this common programming error. Sets ---- -The set type models the mathematical notion of a set. The set's -basetype can only be an ordinal type. The reason is that sets are implemented -as high performance bit vectors. - -Sets can be constructed via the set constructor: ``{}`` is the empty set. The -empty set is type compatible with any concrete set type. The constructor -can also be used to include elements (and ranges of elements): - -.. code-block:: nimrod - type - TCharSet = set[char] - var - x: TCharSet - x = {'a'..'z', '0'..'9'} # This constructs a set that contains the - # letters from 'a' to 'z' and the digits - # from '0' to '9' - -These operations are supported by sets: - -================== ======================================================== -operation meaning -================== ======================================================== -``A + B`` union of two sets -``A * B`` intersection of two sets -``A - B`` difference of two sets (A without B's elements) -``A == B`` set equality -``A <= B`` subset relation (A is subset of B or equal to B) -``A < B`` strong subset relation (A is a real subset of B) -``e in A`` set membership (A contains element e) -``e notin A`` A does not contain element e -``contains(A, e)`` A contains element e -``A -+- B`` symmetric set difference (= (A - B) + (B - A)) -``card(A)`` the cardinality of A (number of elements in A) -``incl(A, elem)`` same as ``A = A + {elem}`` -``excl(A, elem)`` same as ``A = A - {elem}`` -================== ======================================================== - -Sets are often used to define a type for the *flags* of a procedure. This is -a much cleaner (and type safe) solution than just defining integer -constants that should be ``or``'ed together. +.. include:: sets_fragment.txt Arrays ------ From e9417b55cfea809638de7f09865115e3ec6907c9 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sun, 27 Jul 2014 09:42:49 +0200 Subject: [PATCH 17/37] Adds definition of card term to sets module. --- lib/pure/collections/sets.nim | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index f63805e4e7..42cdc682fb 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -65,6 +65,9 @@ proc len*[A](s: TSet[A]): int = proc card*[A](s: TSet[A]): int = ## Alias for `len() <#len,TSet[A]>`_. + ## + ## Card stands for the `cardinality + ## `_ of a set. result = s.counter iterator items*[A](s: TSet[A]): A = @@ -530,6 +533,9 @@ proc len*[A](s: TOrderedSet[A]): int {.inline.} = proc card*[A](s: TOrderedSet[A]): int {.inline.} = ## Alias for `len() <#len,TOrderedSet[A]>`_. + ## + ## Card stands for the `cardinality + ## `_ of a set. result = s.counter template forAllOrderedPairs(yieldStmt: stmt) {.dirty, immediate.} = From f345b0278bf6ac2db389a17297ff03fde7891743 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sun, 27 Jul 2014 20:24:39 +0200 Subject: [PATCH 18/37] Adds os.copyDirWithPermissions(). --- lib/pure/os.nim | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index a7f4f7d916..b189ec224d 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1363,7 +1363,13 @@ proc createDir*(dir: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} = proc copyDir*(source, dest: string) {.rtl, extern: "nos$1", tags: [FWriteIO, FReadIO].} = - ## Copies a directory from `source` to `dest`. If this fails, `EOS` is raised. + ## Copies a directory from `source` to `dest`. + ## + ## If this fails, `EOS` is raised. On the Windows platform this proc will + ## copy the attributes from `source` into `dest`. On other platforms created + ## files and directories will inherit the default permissions of a newly + ## created file/directory for the user. To preserve attributes recursively on + ## these platforms use `copyDirWithPermissions() <#copyDirWithPermissions>`_. createDir(dest) for kind, path in walkDir(source): var noSource = path.substr(source.len()+1) @@ -1523,6 +1529,37 @@ proc copyFileWithPermissions*(source, dest: string, if not ignorePermissionErrors: raise +proc copyDirWithPermissions*(source, dest: string, + ignorePermissionErrors = true) {.rtl, extern: "nos$1", + tags: [FWriteIO, FReadIO].} = + ## Copies a directory from `source` to `dest` preserving file permissions. + ## + ## If this fails, `EOS` is raised. This is a wrapper proc around `copyDir() + ## <#copyDir>`_ and `copyFileWithPermissions() <#copyFileWithPermissions>`_ + ## on non Windows platforms. On Windows this proc is just a wrapper for + ## `copyDir() <#copyDir>`_ since that proc already copies attributes. + ## + ## On non Windows systems permissions are copied after the file or directory + ## itself has been copied, which won't happen atomically and could lead to a + ## race condition. If `ignorePermissionErrors` is true, errors while + ## reading/setting file attributes will be ignored, otherwise will raise + ## `OSError`. + createDir(dest) + when not defined(Windows): + try: + setFilePermissions(dest, getFilePermissions(source)) + except: + if not ignorePermissionErrors: + raise + for kind, path in walkDir(source): + var noSource = path.substr(source.len()+1) + case kind + of pcFile: + copyFileWithPermissions(path, dest / noSource, ignorePermissionErrors) + of pcDir: + copyDirWithPermissions(path, dest / noSource, ignorePermissionErrors) + else: discard + proc inclFilePermissions*(filename: string, permissions: set[TFilePermission]) {. rtl, extern: "nos$1", tags: [FReadDir, FWriteDir].} = From d597767d70992cd879eec88a1c025b93187b5352 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sun, 27 Jul 2014 20:27:38 +0200 Subject: [PATCH 19/37] Adds hyperlinks to copyFile* docstrings. --- lib/pure/os.nim | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index b189ec224d..a70bfa7f10 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -955,11 +955,12 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1", ## ## If this fails, `EOS` is raised. On the Windows platform this proc will ## copy the source file's attributes into dest. On other platforms you need - ## to use getFilePermissions and setFilePermissions to copy them by hand (or - ## use the convenience copyFileWithPermissions() proc), otherwise `dest` will - ## inherit the default permissions of a newly created file for the user. If - ## `dest` already exists, the file attributes will be preserved and the - ## content overwritten. + ## to use `getFilePermissions() <#getFilePermissions>`_ and + ## `setFilePermissions() <#setFilePermissions>`_ to copy them by hand (or use + ## the convenience `copyFileWithPermissions() <#copyFileWithPermissions>`_ + ## proc), otherwise `dest` will inherit the default permissions of a newly + ## created file for the user. If `dest` already exists, the file attributes + ## will be preserved and the content overwritten. when defined(Windows): when useWinUnicode: let s = newWideCString(source) @@ -1513,14 +1514,17 @@ proc copyFileWithPermissions*(source, dest: string, ignorePermissionErrors = true) = ## Copies a file from `source` to `dest` preserving file permissions. ## - ## This is a wrapper proc around copyFile, getFilePermissions and - ## setFilePermissions on non Windows platform. On windows this proc is just a - ## wrapper for copyFile since that proc already copies attributes. + ## This is a wrapper proc around `copyFile() <#copyFile>`_, + ## `getFilePermissions() <#getFilePermissions>`_ and `setFilePermissions() + ## <#setFilePermissions>`_ on non Windows platform. On Windows this proc is + ## just a wrapper for `copyFile() <#copyFile>`_ since that proc already + ## copies attributes. ## - ## On non windows systems permissions are copied after the file itself has + ## On non Windows systems permissions are copied after the file itself has ## been copied, which won't happen atomically and could lead to a race - ## condition. If ignorePermissionErrors is true, errors while reading/setting - ## file attributes will be ignored, otherwise will raise `OSError`. + ## condition. If `ignorePermissionErrors` is true, errors while + ## reading/setting file attributes will be ignored, otherwise will raise + ## `OSError`. copyFile(source, dest) when not defined(Windows): try: From d9b5ae13be7c7f9e92a5af4fa4274eae8c57397f Mon Sep 17 00:00:00 2001 From: def Date: Mon, 28 Jul 2014 17:41:00 +0200 Subject: [PATCH 20/37] Allow arguments for "nimrod run" --- compiler/nimrod.nim | 2 +- compiler/service.nim | 2 +- compiler/tccgen.nim | 8 +++----- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/compiler/nimrod.nim b/compiler/nimrod.nim index efe3d83bfe..ea7621b095 100644 --- a/compiler/nimrod.nim +++ b/compiler/nimrod.nim @@ -58,7 +58,7 @@ proc handleCmdLine() = if msgs.gErrorCounter == 0: when hasTinyCBackend: if gCmd == cmdRun: - tccgen.run() + tccgen.run(service.arguments) if optRun in gGlobalOptions: if gCmd == cmdCompileToJS: var ex: string diff --git a/compiler/service.nim b/compiler/service.nim index 2b861e1c76..eab9952764 100644 --- a/compiler/service.nim +++ b/compiler/service.nim @@ -59,7 +59,7 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) = inc argsCount if pass == passCmd2: - if optRun notin gGlobalOptions and arguments != "": + if optRun notin gGlobalOptions and arguments != "" and options.command != "run": rawMessage(errArgsNeedRunOption, []) proc serve*(action: proc (){.nimcall.}) = diff --git a/compiler/tccgen.nim b/compiler/tccgen.nim index 9ed6db8a18..a571d416ef 100644 --- a/compiler/tccgen.nim +++ b/compiler/tccgen.nim @@ -68,11 +68,9 @@ proc compileCCode*(ccode: string) = setupEnvironment() discard compileString(gTinyC, ccode) -proc run*() = - var a: array[0..1, cstring] - a[0] = "" - a[1] = "" - var err = tinyc.run(gTinyC, 0'i32, cast[cstringArray](addr(a))) != 0'i32 +proc run*(args: string) = + var s = @[cstring(gProjectName)] & map(split(args), proc(x: string): cstring = cstring(x)) + var err = tinyc.run(gTinyC, cint(len(s)), cast[cstringArray](addr(s[0]))) != 0'i32 closeCCState(gTinyC) if err: rawMessage(errExecutionOfProgramFailed, "") From 69949c07b72ceacc18f2cb6ba66c580b006484dc Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 28 Jul 2014 23:22:39 +0200 Subject: [PATCH 21/37] Escapes properly code blocks without highlite support. --- lib/packages/docutils/rstgen.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index fdbca4ca84..e9bae69b55 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -775,7 +775,7 @@ proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) = dispA(d.target, result, "
", "\\begin{rstpre}\n", [])
   if lang == langNone:
     d.msgHandler(d.filename, 1, 0, mwUnsupportedLanguage, langstr)
-    result.add(m.text)
+    for letter in m.text: escChar(d.target, result, letter)
   else:
     var g: TGeneralTokenizer
     initGeneralTokenizer(g, m.text)

From 1274953507deb1c1210a1e0f152950d5ffe5ad45 Mon Sep 17 00:00:00 2001
From: def 
Date: Tue, 29 Jul 2014 02:16:57 +0200
Subject: [PATCH 22/37] normalize "run" command

---
 compiler/service.nim | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/compiler/service.nim b/compiler/service.nim
index eab9952764..22f5c6e333 100644
--- a/compiler/service.nim
+++ b/compiler/service.nim
@@ -59,7 +59,7 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
       inc argsCount
           
   if pass == passCmd2:
-    if optRun notin gGlobalOptions and arguments != "" and options.command != "run":
+    if optRun notin gGlobalOptions and arguments != "" and options.command.normalize != "run":
       rawMessage(errArgsNeedRunOption, [])
 
 proc serve*(action: proc (){.nimcall.}) =

From 06bbd6e7d720c05550d3ee9a71691a28a84a9e79 Mon Sep 17 00:00:00 2001
From: def 
Date: Tue, 29 Jul 2014 19:52:41 +0200
Subject: [PATCH 23/37] flush -> flushFile in doc

---
 lib/system.nim | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/system.nim b/lib/system.nim
index 753205777e..d77b4fdee4 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -2031,7 +2031,7 @@ proc echo*[T](x: varargs[T, `$`]) {.magic: "Echo", tags: [FWriteIO], gcsafe.}
   ## Special built-in that takes a variable number of arguments. Each argument
   ## is converted to a string via ``$``, so it works for user-defined
   ## types that have an overloaded ``$`` operator.
-  ## It is roughly equivalent to ``writeln(stdout, x); flush(stdout)``, but
+  ## It is roughly equivalent to ``writeln(stdout, x); flushFile(stdout)``, but
   ## available for the JavaScript target too.
   ##
   ## Unlike other IO operations this is guaranteed to be thread-safe as

From 779f2b37aa5d99f07747a6c2dac9c56633a0f431 Mon Sep 17 00:00:00 2001
From: Dominik Picheta 
Date: Tue, 29 Jul 2014 21:29:39 +0100
Subject: [PATCH 24/37] Fixes typeinfo after PR #1408 broke it.

---
 lib/core/typeinfo.nim | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim
index 93b90e120a..57e11664b5 100644
--- a/lib/core/typeinfo.nim
+++ b/lib/core/typeinfo.nim
@@ -454,11 +454,11 @@ proc getBiggestUint*(x: TAny): uint64 =
   ## represent an unsigned integer.
   var t = skipRange(x.rawtype)
   case t.kind
-  of akUInt: result = uint64(cast[ptr uint](x.value)[])
-  of akUInt8: result = uint64(cast[ptr uint8](x.value)[])
-  of akUInt16: result = uint64(cast[ptr uint16](x.value)[])
-  of akUInt32: result = uint64(cast[ptr uint32](x.value)[])
-  of akUInt64: result = uint64(cast[ptr uint64](x.value)[])
+  of tyUInt: result = uint64(cast[ptr uint](x.value)[])
+  of tyUInt8: result = uint64(cast[ptr uint8](x.value)[])
+  of tyUInt16: result = uint64(cast[ptr uint16](x.value)[])
+  of tyUInt32: result = uint64(cast[ptr uint32](x.value)[])
+  of tyUInt64: result = uint64(cast[ptr uint64](x.value)[])
   else: assert false
 
 proc setBiggestUint*(x: TAny; y: uint64) =
@@ -466,11 +466,11 @@ proc setBiggestUint*(x: TAny; y: uint64) =
   ## unsigned integer.
   var t = skipRange(x.rawtype)
   case t.kind:
-  of akUInt: result = cast[ptr uint](x.value)[] = uint(y)
-  of akUInt8: result = cast[ptr uint8](x.value)[] = uint8(y)
-  of akUInt16: result = cast[ptr uint16](x.value)[] = uint16(y)
-  of akUInt32: result = cast[ptr uint32](x.value)[] = uint32(y)
-  of akUInt64: result = cast[ptr uint64](x.value)[] = uint64(y)
+  of tyUInt: cast[ptr uint](x.value)[] = uint(y)
+  of tyUInt8: cast[ptr uint8](x.value)[] = uint8(y)
+  of tyUInt16: cast[ptr uint16](x.value)[] = uint16(y)
+  of tyUInt32: cast[ptr uint32](x.value)[] = uint32(y)
+  of tyUInt64: cast[ptr uint64](x.value)[] = uint64(y)
   else: assert false
 
 proc getChar*(x: TAny): char =

From 1b6abccba2c220ba153e29b8a6ae4ad3038a00c3 Mon Sep 17 00:00:00 2001
From: Clay Sweetser 
Date: Tue, 29 Jul 2014 21:13:21 -0400
Subject: [PATCH 25/37] Fix #813

Also add a notice that the linked list stuff in the compiler should be (one day) removed.
---
 compiler/commands.nim | 12 +++++++++---
 compiler/lists.nim    |  8 +++++---
 2 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/compiler/commands.nim b/compiler/commands.nim
index 38c8dd2949..6edbbd61a2 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -290,9 +290,15 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
     options.gNoBabelPath = true
   of "excludepath":
     expectArg(switch, arg, pass, info)
-    let path = processPath(arg)
-    lists.excludeStr(options.searchPaths, path)
-    lists.excludeStr(options.lazyPaths, path)
+    let
+      path = processPath(arg)
+      strippedPath = removeTrailingDirSep(path)
+    echo repr(options.searchPaths)
+    lists.excludePath(options.searchPaths, path)
+    lists.excludePath(options.lazyPaths, path)
+    lists.excludePath(options.searchPaths, strippedPath)
+    lists.excludePath(options.lazyPaths, strippedPath)
+    echo repr(options.searchPaths)
   of "nimcache":
     expectArg(switch, arg, pass, info)
     options.nimcacheDir = processPath(arg)
diff --git a/compiler/lists.nim b/compiler/lists.nim
index dd4f5d6be5..efffe60fe3 100644
--- a/compiler/lists.nim
+++ b/compiler/lists.nim
@@ -8,7 +8,8 @@
 #
 
 # This module implements a generic doubled linked list.
-
+# TODO Remove this and replace it with something sensible
+import os
 type 
   PListEntry* = ref TListEntry
   TListEntry* = object of TObject
@@ -103,11 +104,12 @@ proc bringToFront*(list: var TLinkedList, entry: PListEntry) =
     entry.next = list.head
     list.head = entry
 
-proc excludeStr*(list: var TLinkedList, data: string) =
+proc excludePath*(list: var TLinkedList, data: string) =
   var it = list.head
   while it != nil:
     let nxt = it.next
-    if PStrEntry(it).data == data: remove(list, it)
+    if cmpPaths(PStrEntry(it).data, data) == 0:
+      remove(list, it)
     it = nxt
 
 proc find*(list: TLinkedList, fn: TCompareProc, closure: pointer): PListEntry = 

From 6a09fc358e5e799fdf5c0f79774d475d2fb02e6f Mon Sep 17 00:00:00 2001
From: Clay Sweetser 
Date: Tue, 29 Jul 2014 21:23:18 -0400
Subject: [PATCH 26/37] Clean up a bit.

---
 compiler/commands.nim | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/compiler/commands.nim b/compiler/commands.nim
index 6edbbd61a2..ee2f2a7f7b 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -290,14 +290,14 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
     options.gNoBabelPath = true
   of "excludepath":
     expectArg(switch, arg, pass, info)
-    let
-      path = processPath(arg)
-      strippedPath = removeTrailingDirSep(path)
+    let path = processPath(arg)
     echo repr(options.searchPaths)
     lists.excludePath(options.searchPaths, path)
     lists.excludePath(options.lazyPaths, path)
-    lists.excludePath(options.searchPaths, strippedPath)
-    lists.excludePath(options.lazyPaths, strippedPath)
+    if (len(path) > 0) and (path[len(path) - 1] == DirSep)::
+      let strippedPath = removeTrailingDirSep(path)
+      lists.excludePath(options.searchPaths, strippedPath)
+      lists.excludePath(options.lazyPaths, strippedPath)
     echo repr(options.searchPaths)
   of "nimcache":
     expectArg(switch, arg, pass, info)

From 9b4b4bcbc5b6464da31b75ab6bf529066bf8315d Mon Sep 17 00:00:00 2001
From: Clay Sweetser 
Date: Tue, 29 Jul 2014 21:31:46 -0400
Subject: [PATCH 27/37] Clean up more

---
 compiler/commands.nim | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/compiler/commands.nim b/compiler/commands.nim
index ee2f2a7f7b..4fec420f77 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -294,8 +294,8 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
     echo repr(options.searchPaths)
     lists.excludePath(options.searchPaths, path)
     lists.excludePath(options.lazyPaths, path)
-    if (len(path) > 0) and (path[len(path) - 1] == DirSep)::
-      let strippedPath = removeTrailingDirSep(path)
+    if (len(path) > 0) and (path[len(path) - 1] == DirSep):
+      let strippedPath = path[0 .. (len(path) - 2)]
       lists.excludePath(options.searchPaths, strippedPath)
       lists.excludePath(options.lazyPaths, strippedPath)
     echo repr(options.searchPaths)

From 2c6406703c47467289c5a53387f6fd64a9f516ce Mon Sep 17 00:00:00 2001
From: def 
Date: Wed, 30 Jul 2014 23:23:02 +0200
Subject: [PATCH 28/37] Add some nil checks for xmldom (and clean up a bit)

---
 lib/pure/xmldom.nim | 419 +++++++++++++++++++++++---------------------
 1 file changed, 223 insertions(+), 196 deletions(-)

diff --git a/lib/pure/xmldom.nim b/lib/pure/xmldom.nim
index 47e94243eb..98e4104e43 100644
--- a/lib/pure/xmldom.nim
+++ b/lib/pure/xmldom.nim
@@ -45,7 +45,7 @@ const
   DocumentFragmentNode* = 11
 
   # Nodes which are childless - Not sure about AttributeNode
-  childlessObjects = {DocumentNode, AttributeNode, TextNode, 
+  childlessObjects = {DocumentNode, AttributeNode, TextNode,
     CDataSectionNode, ProcessingInstructionNode, CommentNode}
   # Illegal characters
   illegalChars = {'>', '<', '&', '"'}
@@ -69,21 +69,21 @@ type
     FOwnerDocument: PDocument # Read-Only
     FParentNode: PNode # Read-Only
     prefix*: string # Setting this should change some values... TODO!
-  
+
   PElement* = ref Element
   Element = object of Node
     FTagName: string # Read-only
-  
+
   PCharacterData* = ref CharacterData
   CharacterData = object of Node
     data*: string
-    
+
   PDocument* = ref Document
   Document = object of Node
     FImplementation: PDOMImplementation # Read-only
     FDocumentElement: PElement # Read-only
-    
-  PAttr* = ref Attr  
+
+  PAttr* = ref Attr
   Attr = object of Node
     FName: string # Read-only
     FSpecified: bool # Read-only
@@ -95,13 +95,13 @@ type
 
   PText* = ref Text
   Text = object of CharacterData
-  
+
   PComment* = ref comment
   Comment = object of CharacterData
-  
+
   PCDataSection* = ref CDataSection
   CDataSection = object of Text
-    
+
   PProcessingInstruction* = ref ProcessingInstruction
   ProcessingInstruction = object of Node
     data*: string
@@ -111,8 +111,8 @@ type
 proc getDOM*(): PDOMImplementation =
   ## Returns a DOMImplementation
   new(result)
-  result.Features = @[(name: "core", version: "2.0"), 
-                      (name: "core", version: "1.0"), 
+  result.Features = @[(name: "core", version: "2.0"),
+                      (name: "core", version: "1.0"),
                       (name: "XML", version: "2.0")]
 
 proc createDocument*(dom: PDOMImplementation, namespaceURI: string, qualifiedName: string): PDocument =
@@ -121,28 +121,28 @@ proc createDocument*(dom: PDOMImplementation, namespaceURI: string, qualifiedNam
   new(doc)
   doc.FNamespaceURI = namespaceURI
   doc.FImplementation = dom
-  
+
   var elTag: PElement
   new(elTag)
   elTag.FTagName = qualifiedName
   elTag.FNodeName = qualifiedName
   doc.FDocumentElement = elTag
   doc.FNodeType = DocumentNode
-  
+
   return doc
-  
+
 proc createDocument*(dom: PDOMImplementation, n: PElement): PDocument =
   ## Creates an XML Document object of the specified type with its document element.
-  
+
   # This procedure is not in the specification, it's provided for the parser.
   var doc: PDocument
   new(doc)
   doc.FDocumentElement = n
   doc.FImplementation = dom
   doc.FNodeType = DocumentNode
-  
+
   return doc
-  
+
 proc hasFeature*(dom: PDOMImplementation, feature: string, version: string = ""): bool =
   ## Returns ``true`` if this ``version`` of the DomImplementation implements ``feature``, otherwise ``false``
   for iName, iVersion in items(dom.Features):
@@ -157,58 +157,58 @@ proc hasFeature*(dom: PDOMImplementation, feature: string, version: string = "")
 
 # Document
 # Attributes
-  
+
 proc implementation*(doc: PDocument): PDOMImplementation =
   return doc.FImplementation
-  
-proc documentElement*(doc: PDocument): PElement = 
+
+proc documentElement*(doc: PDocument): PElement =
   return doc.FDocumentElement
 
 # Internal procedures
 proc findNodes(nl: PNode, name: string): seq[PNode] =
   # Made for getElementsByTagName
   var r: seq[PNode] = @[]
-  if isNil(nl.childNodes): return @[]
+  if nl.childNodes == nil: return @[]
   if nl.childNodes.len() == 0: return @[]
 
   for i in items(nl.childNodes):
     if i.FNodeType == ElementNode:
       if i.FNodeName == name or name == "*":
         r.add(i)
-        
-      if not isNil(i.childNodes):
+
+      if i.childNodes != nil:
         if i.childNodes.len() != 0:
           r.add(findNodes(i, name))
-    
+
   return r
-  
+
 proc findNodesNS(nl: PNode, namespaceURI: string, localName: string): seq[PNode] =
   # Made for getElementsByTagNameNS
   var r: seq[PNode] = @[]
-  if isNil(nl.childNodes): return @[]
+  if nl.childNodes == nil: return @[]
   if nl.childNodes.len() == 0: return @[]
 
   for i in items(nl.childNodes):
     if i.FNodeType == ElementNode:
       if (i.FNamespaceURI == namespaceURI or namespaceURI == "*") and (i.FLocalName == localName or localName == "*"):
         r.add(i)
-        
-      if not isNil(i.childNodes):
+
+      if i.childNodes != nil:
         if i.childNodes.len() != 0:
           r.add(findNodesNS(i, namespaceURI, localName))
-    
+
   return r
-    
+
 
 #Procedures
 proc createAttribute*(doc: PDocument, name: string): PAttr =
   ## Creates an Attr of the given name. Note that the Attr instance can then be set on an Element using the setAttributeNode method.
-  ## To create an attribute with a qualified name and namespace URI, use the createAttributeNS method. 
-  
+  ## To create an attribute with a qualified name and namespace URI, use the createAttributeNS method.
+
   # Check if name contains illegal characters
   if illegalChars in name:
     raise newException(EInvalidCharacterErr, "Invalid character")
-  
+
   var AttrNode: PAttr
   new(AttrNode)
   AttrNode.FName = name
@@ -222,7 +222,7 @@ proc createAttribute*(doc: PDocument, name: string): PAttr =
 
 proc createAttributeNS*(doc: PDocument, namespaceURI: string, qualifiedName: string): PAttr =
   ## Creates an attribute of the given qualified name and namespace URI
-  
+
   # Check if name contains illegal characters
   if illegalChars in namespaceURI or illegalChars in qualifiedName:
     raise newException(EInvalidCharacterErr, "Invalid character")
@@ -231,12 +231,12 @@ proc createAttributeNS*(doc: PDocument, namespaceURI: string, qualifiedName: str
     if namespaceURI == nil:
       raise newException(ENamespaceErr, "When qualifiedName contains a prefix namespaceURI cannot be nil")
     elif qualifiedName.split(':')[0].toLower() == "xml" and namespaceURI != "http://www.w3.org/XML/1998/namespace":
-      raise newException(ENamespaceErr, 
+      raise newException(ENamespaceErr,
         "When the namespace prefix is \"xml\" namespaceURI has to be \"http://www.w3.org/XML/1998/namespace\"")
     elif qualifiedName.split(':')[1].toLower() == "xmlns" and namespaceURI != "http://www.w3.org/2000/xmlns/":
-      raise newException(ENamespaceErr, 
+      raise newException(ENamespaceErr,
         "When the namespace prefix is \"xmlns\" namespaceURI has to be \"http://www.w3.org/2000/xmlns/\"")
-  
+
   var AttrNode: PAttr
   new(AttrNode)
   AttrNode.FName = qualifiedName
@@ -250,7 +250,7 @@ proc createAttributeNS*(doc: PDocument, namespaceURI: string, qualifiedName: str
     AttrNode.prefix = nil
     AttrNode.FLocalName = qualifiedName
   AttrNode.value = ""
-  
+
   AttrNode.FNodeType = AttributeNode
   return AttrNode
 
@@ -265,12 +265,12 @@ proc createCDATASection*(doc: PDocument, data: string): PCDATASection =
   return CData
 
 proc createComment*(doc: PDocument, data: string): PComment =
-  ## Creates a Comment node given the specified string. 
+  ## Creates a Comment node given the specified string.
   var Comm: PComment
   new(Comm)
   Comm.data = data
   Comm.nodeValue = data
-  
+
   Comm.FNodeType = CommentNode
   return Comm
 
@@ -282,11 +282,11 @@ proc createDocumentFragment*(doc: PDocument): PDocumentFragment =
 
 proc createElement*(doc: PDocument, tagName: string): PElement =
   ## Creates an element of the type specified.
-  
+
   # Check if name contains illegal characters
   if illegalChars in tagName:
     raise newException(EInvalidCharacterErr, "Invalid character")
-    
+
   var elNode: PElement
   new(elNode)
   elNode.FTagName = tagName
@@ -296,9 +296,9 @@ proc createElement*(doc: PDocument, tagName: string): PElement =
   elNode.FNamespaceURI = nil
   elNode.childNodes = @[]
   elNode.attributes = @[]
-  
+
   elNode.FNodeType = ElementNode
-  
+
   return elNode
 
 proc createElementNS*(doc: PDocument, namespaceURI: string, qualifiedName: string): PElement =
@@ -307,13 +307,13 @@ proc createElementNS*(doc: PDocument, namespaceURI: string, qualifiedName: strin
     if namespaceURI == nil:
       raise newException(ENamespaceErr, "When qualifiedName contains a prefix namespaceURI cannot be nil")
     elif qualifiedName.split(':')[0].toLower() == "xml" and namespaceURI != "http://www.w3.org/XML/1998/namespace":
-      raise newException(ENamespaceErr, 
+      raise newException(ENamespaceErr,
         "When the namespace prefix is \"xml\" namespaceURI has to be \"http://www.w3.org/XML/1998/namespace\"")
-        
+
   # Check if name contains illegal characters
   if illegalChars in namespaceURI or illegalChars in qualifiedName:
     raise newException(EInvalidCharacterErr, "Invalid character")
-    
+
   var elNode: PElement
   new(elNode)
   elNode.FTagName = qualifiedName
@@ -327,18 +327,18 @@ proc createElementNS*(doc: PDocument, namespaceURI: string, qualifiedName: strin
   elNode.FNamespaceURI = namespaceURI
   elNode.childNodes = @[]
   elNode.attributes = @[]
-  
+
   elNode.FNodeType = ElementNode
-  
+
   return elNode
 
-proc createProcessingInstruction*(doc: PDocument, target: string, data: string): PProcessingInstruction = 
-  ## Creates a ProcessingInstruction node given the specified name and data strings. 
-  
+proc createProcessingInstruction*(doc: PDocument, target: string, data: string): PProcessingInstruction =
+  ## Creates a ProcessingInstruction node given the specified name and data strings.
+
   #Check if name contains illegal characters
   if illegalChars in target:
     raise newException(EInvalidCharacterErr, "Invalid character")
-    
+
   var PI: PProcessingInstruction
   new(PI)
   PI.FTarget = target
@@ -347,13 +347,13 @@ proc createProcessingInstruction*(doc: PDocument, target: string, data: string):
   return PI
 
 proc createTextNode*(doc: PDocument, data: string): PText = #Propably TextNode
-  ## Creates a Text node given the specified string. 
+  ## Creates a Text node given the specified string.
   var txtNode: PText
   new(txtNode)
   txtNode.data = data
   txtNode.nodeValue = data
   txtNode.FNodeName = "#text"
-  
+
   txtNode.FNodeType = TextNode
   return txtNode
 
@@ -363,22 +363,22 @@ discard """proc getElementById*(doc: PDocument, elementId: string): PElement =
 
 proc getElementsByTagName*(doc: PDocument, tagName: string): seq[PNode] =
   ## Returns a NodeList of all the Elements with a given tag name in
-  ## the order in which they are encountered in a preorder traversal of the Document tree. 
+  ## the order in which they are encountered in a preorder traversal of the Document tree.
   var result: seq[PNode] = @[]
   if doc.FDocumentElement.FNodeName == tagName or tagName == "*":
     result.add(doc.FDocumentElement)
-  
+
   result.add(doc.FDocumentElement.findNodes(tagName))
   return result
-  
+
 proc getElementsByTagNameNS*(doc: PDocument, namespaceURI: string, localName: string): seq[PNode] =
   ## Returns a NodeList of all the Elements with a given localName and namespaceURI
-  ## in the order in which they are encountered in a preorder traversal of the Document tree. 
+  ## in the order in which they are encountered in a preorder traversal of the Document tree.
   var result: seq[PNode] = @[]
   if doc.FDocumentElement.FLocalName == localName or localName == "*":
     if doc.FDocumentElement.FNamespaceURI == namespaceURI or namespaceURI == "*":
       result.add(doc.FDocumentElement)
-      
+
   result.add(doc.FDocumentElement.findNodesNS(namespaceURI, localName))
   return result
 
@@ -406,7 +406,7 @@ proc importNode*(doc: PDocument, importedNode: PNode, deep: bool): PNode =
     if deep:
       for i in low(tmp.len())..high(tmp.len()):
         n.childNodes.add(importNode(doc, tmp[i], deep))
-        
+
     return n
   of ElementNode:
     var n: PNode
@@ -414,7 +414,7 @@ proc importNode*(doc: PDocument, importedNode: PNode, deep: bool): PNode =
     n = importedNode
     n.FOwnerDocument = doc
     n.FParentNode = nil
-    
+
     var tmpA: seq[PAttr] = n.attributes
     n.attributes = @[]
     # Import the Element node's attributes
@@ -426,7 +426,7 @@ proc importNode*(doc: PDocument, importedNode: PNode, deep: bool): PNode =
     if deep:
       for i in low(tmp.len())..high(tmp.len()):
         n.childNodes.add(importNode(doc, tmp[i], deep))
-        
+
     return n
   of ProcessingInstructionNode, TextNode, CDataSectionNode, CommentNode:
     var n: PNode
@@ -437,27 +437,27 @@ proc importNode*(doc: PDocument, importedNode: PNode, deep: bool): PNode =
     return n
   else:
     raise newException(ENotSupportedErr, "The type of node being imported is not supported")
-  
+
 
 # Node
 # Attributes
-  
+
 proc firstChild*(n: PNode): PNode =
   ## Returns this node's first child
 
-  if n.childNodes.len() > 0:
+  if n.childNodes != nil and n.childNodes.len() > 0:
     return n.childNodes[0]
   else:
     return nil
-  
+
 proc lastChild*(n: PNode): PNode =
   ## Returns this node's last child
 
-  if n.childNodes.len() > 0:
+  if n.childNodes != nil and n.childNodes.len() > 0:
     return n.childNodes[n.childNodes.len() - 1]
   else:
     return nil
-  
+
 proc localName*(n: PNode): string =
   ## Returns this nodes local name
 
@@ -465,15 +465,17 @@ proc localName*(n: PNode): string =
 
 proc namespaceURI*(n: PNode): string =
   ## Returns this nodes namespace URI
-  
+
   return n.FNamespaceURI
-  
-proc `namespaceURI=`*(n: PNode, value: string) = 
+
+proc `namespaceURI=`*(n: PNode, value: string) =
   n.FNamespaceURI = value
 
 proc nextSibling*(n: PNode): PNode =
   ## Returns the next sibling of this node
 
+  if n.FParentNode == nil or n.FParentNode.childNodes == nil:
+    return nil
   var nLow: int = low(n.FParentNode.childNodes)
   var nHigh: int = high(n.FParentNode.childNodes)
   for i in nLow..nHigh:
@@ -500,17 +502,19 @@ proc parentNode*(n: PNode): PNode =
   ## Returns the parent node of this node
 
   return n.FParentNode
-  
+
 proc previousSibling*(n: PNode): PNode =
   ## Returns the previous sibling of this node
 
+  if n.FParentNode == nil or n.FParentNode.childNodes == nil:
+    return nil
   var nLow: int = low(n.FParentNode.childNodes)
   var nHigh: int = high(n.FParentNode.childNodes)
   for i in nLow..nHigh:
     if n.FParentNode.childNodes[i] == n:
       return n.FParentNode.childNodes[i - 1]
   return nil
-  
+
 proc `prefix=`*(n: PNode, value: string) =
   ## Modifies the prefix of this node
 
@@ -522,10 +526,10 @@ proc `prefix=`*(n: PNode, value: string) =
   if n.FNamespaceURI == nil:
     raise newException(ENamespaceErr, "namespaceURI cannot be nil")
   elif value.toLower() == "xml" and n.FNamespaceURI != "http://www.w3.org/XML/1998/namespace":
-    raise newException(ENamespaceErr, 
+    raise newException(ENamespaceErr,
       "When the namespace prefix is \"xml\" namespaceURI has to be \"http://www.w3.org/XML/1998/namespace\"")
   elif value.toLower() == "xmlns" and n.FNamespaceURI != "http://www.w3.org/2000/xmlns/":
-    raise newException(ENamespaceErr, 
+    raise newException(ENamespaceErr,
       "When the namespace prefix is \"xmlns\" namespaceURI has to be \"http://www.w3.org/2000/xmlns/\"")
   elif value.toLower() == "xmlns" and n.FNodeType == AttributeNode:
     raise newException(ENamespaceErr, "An AttributeNode cannot have a prefix of \"xmlns\"")
@@ -543,33 +547,33 @@ proc `prefix=`*(n: PNode, value: string) =
 proc appendChild*(n: PNode, newChild: PNode) =
   ## Adds the node newChild to the end of the list of children of this node.
   ## If the newChild is already in the tree, it is first removed.
-  
+
   # Check if n contains newChild
-  if not IsNil(n.childNodes):
+  if n.childNodes != nil:
     for i in low(n.childNodes)..high(n.childNodes):
       if n.childNodes[i] == newChild:
         raise newException(EHierarchyRequestErr, "The node to append is already in this nodes children.")
-  
+
   # Check if newChild is from this nodes document
   if n.FOwnerDocument != newChild.FOwnerDocument:
     raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.")
-  
+
   if n == newChild:
     raise newException(EHierarchyRequestErr, "You can't add a node into itself")
-  
+
   if n.nodeType in childlessObjects:
     raise newException(ENoModificationAllowedErr, "Cannot append children to a childless node")
-  
-  if isNil(n.childNodes): n.childNodes = @[]
-    
+
+  if n.childNodes == nil: n.childNodes = @[]
+
   newChild.FParentNode = n
   for i in low(n.childNodes)..high(n.childNodes):
     if n.childNodes[i] == newChild:
       n.childNodes[i] = newChild
-    
+
   n.childNodes.add(newChild)
 
-proc cloneNode*(n: PNode, deep: bool): PNode = 
+proc cloneNode*(n: PNode, deep: bool): PNode =
   ## Returns a duplicate of this node, if ``deep`` is `true`, Element node's children are copied
   case n.FNodeType
   of AttributeNode:
@@ -586,7 +590,7 @@ proc cloneNode*(n: PNode, deep: bool): PNode =
     # Import the childNodes
     var tmp: seq[PNode] = n.childNodes
     n.childNodes = @[]
-    if deep:
+    if deep and tmp != nil:
       for i in low(tmp.len())..high(tmp.len()):
         n.childNodes.add(cloneNode(tmp[i], deep))
     return newNode
@@ -597,29 +601,34 @@ proc cloneNode*(n: PNode, deep: bool): PNode =
     return newNode
 
 proc hasAttributes*(n: PNode): bool =
-  ## Returns whether this node (if it is an element) has any attributes. 
-  return n.attributes.len() > 0
+  ## Returns whether this node (if it is an element) has any attributes.
+  return n.attributes != nil and n.attributes.len() > 0
 
-proc hasChildNodes*(n: PNode): bool = 
+proc hasChildNodes*(n: PNode): bool =
   ## Returns whether this node has any children.
-  return n.childNodes.len() > 0
+  return n.childNodes != nil and n.childNodes.len() > 0
 
 proc insertBefore*(n: PNode, newChild: PNode, refChild: PNode): PNode =
   ## Inserts the node ``newChild`` before the existing child node ``refChild``.
   ## If ``refChild`` is nil, insert ``newChild`` at the end of the list of children.
-  
+
   # Check if newChild is from this nodes document
   if n.FOwnerDocument != newChild.FOwnerDocument:
     raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.")
-    
+
+  if n.childNodes == nil:
+    n.ChildNodes = @[]
+
   for i in low(n.childNodes)..high(n.childNodes):
     if n.childNodes[i] == refChild:
       n.childNodes.insert(newChild, i - 1)
-    return
+      return
+
+  n.ChildNodes.add(newChild)
 
 proc isSupported*(n: PNode, feature: string, version: string): bool =
-  ## Tests whether the DOM implementation implements a specific 
-  ## feature and that feature is supported by this node. 
+  ## Tests whether the DOM implementation implements a specific
+  ## feature and that feature is supported by this node.
   return n.FOwnerDocument.FImplementation.hasFeature(feature, version)
 
 proc isEmpty(s: string): bool =
@@ -635,17 +644,17 @@ proc normalize*(n: PNode) =
   ## Merges all seperated TextNodes together, and removes any empty TextNodes
   var curTextNode: PNode = nil
   var i: int = 0
-  
+
   var newChildNodes: seq[PNode] = @[]
   while True:
-    if i >= n.childNodes.len:
+    if n.childNodes == nil or i >= n.childNodes.len:
       break
     if n.childNodes[i].nodeType == TextNode:
-      
+
       #If the TextNode is empty, remove it
       if PText(n.childNodes[i]).data.isEmpty():
         inc(i)
-      
+
       if curTextNode == nil:
         curTextNode = n.childNodes[i]
       else:
@@ -656,35 +665,37 @@ proc normalize*(n: PNode) =
       newChildNodes.add(curTextNode)
       newChildNodes.add(n.childNodes[i])
       curTextNode = nil
-    
+
     inc(i)
   n.childNodes = newChildNodes
 
 proc removeChild*(n: PNode, oldChild: PNode): PNode =
   ## Removes the child node indicated by ``oldChild`` from the list of children, and returns it.
-  for i in low(n.childNodes)..high(n.childNodes):
-    if n.childNodes[i] == oldChild:
-      result = n.childNodes[i]
-      n.childNodes.delete(i)
-      return result
-      
+  if n.childNodes != nil:
+    for i in low(n.childNodes)..high(n.childNodes):
+      if n.childNodes[i] == oldChild:
+        result = n.childNodes[i]
+        n.childNodes.delete(i)
+        return result
+
   raise newException(ENotFoundErr, "Node not found")
-    
+
 proc replaceChild*(n: PNode, newChild: PNode, oldChild: PNode): PNode =
   ## Replaces the child node ``oldChild`` with ``newChild`` in the list of children, and returns the ``oldChild`` node.
-  
+
   # Check if newChild is from this nodes document
   if n.FOwnerDocument != newChild.FOwnerDocument:
     raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.")
-  
-  for i in low(n.childNodes)..high(n.childNodes):
-    if n.childNodes[i] == oldChild:
-      result = n.childNodes[i]
-      n.childNodes[i] = newChild
-      return result
-  
+
+  if n.childNodes != nil:
+    for i in low(n.childNodes)..high(n.childNodes):
+      if n.childNodes[i] == oldChild:
+        result = n.childNodes[i]
+        n.childNodes[i] = newChild
+        return result
+
   raise newException(ENotFoundErr, "Node not found")
-  
+
 # NamedNodeMap
 
 proc getNamedItem*(NList: seq[PNode], name: string): PNode =
@@ -693,22 +704,22 @@ proc getNamedItem*(NList: seq[PNode], name: string): PNode =
     if i.nodeName() == name:
       return i
   return nil
-  
+
 proc getNamedItem*(NList: seq[PAttr], name: string): PAttr =
   ## Retrieves a node specified by ``name``. If this node cannot be found returns ``nil``
   for i in items(NList):
     if i.nodeName() == name:
       return i
   return nil
-      
+
 proc getNamedItemNS*(NList: seq[PNode], namespaceURI: string, localName: string): PNode =
   ## Retrieves a node specified by ``localName`` and ``namespaceURI``. If this node cannot be found returns ``nil``
   for i in items(NList):
     if i.namespaceURI() == namespaceURI and i.localName() == localName:
       return i
   return nil
-  
-proc getNamedItemNS*(NList: seq[PAttr], namespaceURI: string, localName: string): PAttr = 
+
+proc getNamedItemNS*(NList: seq[PAttr], namespaceURI: string, localName: string): PAttr =
   ## Retrieves a node specified by ``localName`` and ``namespaceURI``. If this node cannot be found returns ``nil``
   for i in items(NList):
     if i.NamespaceURI() == namespaceURI and i.LocalName() == localName:
@@ -716,7 +727,7 @@ proc getNamedItemNS*(NList: seq[PAttr], namespaceURI: string, localName: string)
   return nil
 
 proc item*(NList: seq[PNode], index: int): PNode =
-  ## Returns the ``index`` th item in the map. 
+  ## Returns the ``index`` th item in the map.
   ## If ``index`` is greater than or equal to the number of nodes in this map, this returns ``nil``.
   if index >= NList.len(): return nil
   else: return NList[index]
@@ -729,9 +740,9 @@ proc removeNamedItem*(NList: var seq[PNode], name: string): PNode =
       result = NList[i]
       NList.delete(i)
       return result
-  
+
   raise newException(ENotFoundErr, "Node not found")
-  
+
 proc removeNamedItemNS*(NList: var seq[PNode], namespaceURI: string, localName: string): PNode =
   ## Removes a node specified by local name and namespace URI
   for i in low(NList)..high(NList):
@@ -739,19 +750,19 @@ proc removeNamedItemNS*(NList: var seq[PNode], namespaceURI: string, localName:
       result = NList[i]
       NList.delete(i)
       return result
-  
+
   raise newException(ENotFoundErr, "Node not found")
 
 proc setNamedItem*(NList: var seq[PNode], arg: PNode): PNode =
   ## Adds ``arg`` as a ``Node`` to the ``NList``
   ## If a node with the same name is already present in this map, it is replaced by the new one.
-  if not isNil(NList):
+  if NList != nil:
     if NList.len() > 0:
       #Check if newChild is from this nodes document
       if NList[0].FOwnerDocument != arg.FOwnerDocument:
         raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.")
   #Exceptions End
-  
+
   var item: PNode = NList.getNamedItem(arg.NodeName())
   if item == nil:
     NList.add(arg)
@@ -765,19 +776,19 @@ proc setNamedItem*(NList: var seq[PNode], arg: PNode): PNode =
         break
     NList[index] = arg
     return item # Return the replaced node
-    
+
 proc setNamedItem*(NList: var seq[PAttr], arg: PAttr): PAttr =
   ## Adds ``arg`` as a ``Node`` to the ``NList``
   ## If a node with the same name is already present in this map, it is replaced by the new one.
-  if not IsNil(NList):
+  if NList != nil:
     if NList.len() > 0:
       # Check if newChild is from this nodes document
       if NList[0].FOwnerDocument != arg.FOwnerDocument:
         raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.")
-        
+
   if arg.FOwnerElement != nil:
     raise newException(EInuseAttributeErr, "This attribute is in use by another element, use cloneNode")
-        
+
   # Exceptions end
   var item: PAttr = NList.getNamedItem(arg.nodeName())
   if item == nil:
@@ -792,16 +803,16 @@ proc setNamedItem*(NList: var seq[PAttr], arg: PAttr): PAttr =
         break
     NList[index] = arg
     return item # Return the replaced node
-    
+
 proc setNamedItemNS*(NList: var seq[PNode], arg: PNode): PNode =
   ## Adds a node using its ``namespaceURI`` and ``localName``
-  if not IsNil(NList):
+  if NList != nil:
     if NList.len() > 0:
       # Check if newChild is from this nodes document
       if NList[0].FOwnerDocument != arg.FOwnerDocument:
         raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.")
   #Exceptions end
-        
+
   var item: PNode = NList.getNamedItemNS(arg.namespaceURI(), arg.localName())
   if item == nil:
     NList.add(arg)
@@ -815,18 +826,18 @@ proc setNamedItemNS*(NList: var seq[PNode], arg: PNode): PNode =
         break
     NList[index] = arg
     return item # Return the replaced node
-    
+
 proc setNamedItemNS*(NList: var seq[PAttr], arg: PAttr): PAttr =
   ## Adds a node using its ``namespaceURI`` and ``localName``
-  if not isNil(NList):
+  if NList != nil:
     if NList.len() > 0:
       # Check if newChild is from this nodes document
       if NList[0].FOwnerDocument != arg.FOwnerDocument:
         raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.")
-        
+
   if arg.FOwnerElement != nil:
     raise newException(EInuseAttributeErr, "This attribute is in use by another element, use cloneNode")
-        
+
   # Exceptions end
   var item: PAttr = NList.getNamedItemNS(arg.namespaceURI(), arg.localName())
   if item == nil:
@@ -841,8 +852,8 @@ proc setNamedItemNS*(NList: var seq[PAttr], arg: PAttr): PAttr =
         break
     NList[index] = arg
     return item # Return the replaced node
-    
-# CharacterData - Decided to implement this, 
+
+# CharacterData - Decided to implement this,
 # Didn't add the procedures, because you can just edit .data
 
 # Attr
@@ -851,13 +862,13 @@ proc name*(a: PAttr): string =
   ## Returns the name of the Attribute
 
   return a.FName
-  
+
 proc specified*(a: PAttr): bool =
   ## Specifies whether this attribute was specified in the original document
 
   return a.FSpecified
-  
-proc ownerElement*(a: PAttr): PElement = 
+
+proc ownerElement*(a: PAttr): PElement =
   ## Returns this Attributes owner element
 
   return a.FOwnerElement
@@ -873,6 +884,8 @@ proc tagName*(el: PElement): string =
 # Procedures
 proc getAttribute*(el: PElement, name: string): string =
   ## Retrieves an attribute value by ``name``
+  if el.attributes == nil:
+    return nil
   var attribute = el.attributes.getNamedItem(name)
   if attribute != nil:
     return attribute.value
@@ -881,19 +894,25 @@ proc getAttribute*(el: PElement, name: string): string =
 
 proc getAttributeNS*(el: PElement, namespaceURI: string, localName: string): string =
   ## Retrieves an attribute value by ``localName`` and ``namespaceURI``
+  if el.attributes == nil:
+    return nil
   var attribute = el.attributes.getNamedItemNS(namespaceURI, localName)
   if attribute != nil:
     return attribute.value
   else:
     return nil
-    
+
 proc getAttributeNode*(el: PElement, name: string): PAttr =
   ## Retrieves an attribute node by ``name``
   ## To retrieve an attribute node by qualified name and namespace URI, use the `getAttributeNodeNS` method
+  if el.attributes == nil:
+    return nil
   return el.attributes.getNamedItem(name)
 
 proc getAttributeNodeNS*(el: PElement, namespaceURI: string, localName: string): PAttr =
   ## Retrieves an `Attr` node by ``localName`` and ``namespaceURI``
+  if el.attributes == nil:
+    return nil
   return el.attributes.getNamedItemNS(namespaceURI, localName)
 
 proc getElementsByTagName*(el: PElement, name: string): seq[PNode] =
@@ -909,103 +928,110 @@ proc getElementsByTagNameNS*(el: PElement, namespaceURI: string, localName: stri
   result = el.findNodesNS(namespaceURI, localName)
 
 proc hasAttribute*(el: PElement, name: string): bool =
-  ## Returns ``true`` when an attribute with a given ``name`` is specified 
-  ## on this element , ``false`` otherwise. 
+  ## Returns ``true`` when an attribute with a given ``name`` is specified
+  ## on this element , ``false`` otherwise.
+  if el.attributes == nil:
+    return false
   return el.attributes.getNamedItem(name) != nil
 
 proc hasAttributeNS*(el: PElement, namespaceURI: string, localName: string): bool =
   ## Returns ``true`` when an attribute with a given ``localName`` and
-  ## ``namespaceURI`` is specified on this element , ``false`` otherwise 
+  ## ``namespaceURI`` is specified on this element , ``false`` otherwise
+  if el.attributes == nil:
+    return false
   return el.attributes.getNamedItemNS(namespaceURI, localName) != nil
 
 proc removeAttribute*(el: PElement, name: string) =
   ## Removes an attribute by ``name``
-  for i in low(el.attributes)..high(el.attributes):
-    if el.attributes[i].FName == name:
-      el.attributes.delete(i)
-      
+  if el.attributes != nil:
+    for i in low(el.attributes)..high(el.attributes):
+      if el.attributes[i].FName == name:
+        el.attributes.delete(i)
+
 proc removeAttributeNS*(el: PElement, namespaceURI: string, localName: string) =
   ## Removes an attribute by ``localName`` and ``namespaceURI``
-  for i in low(el.attributes)..high(el.attributes):
-    if el.attributes[i].FNamespaceURI == namespaceURI and 
-        el.attributes[i].FLocalName == localName:
-      el.attributes.delete(i)
-  
+  if el.attributes != nil:
+    for i in low(el.attributes)..high(el.attributes):
+      if el.attributes[i].FNamespaceURI == namespaceURI and
+          el.attributes[i].FLocalName == localName:
+        el.attributes.delete(i)
+
 proc removeAttributeNode*(el: PElement, oldAttr: PAttr): PAttr =
   ## Removes the specified attribute node
   ## If the attribute node cannot be found raises ``ENotFoundErr``
-  for i in low(el.attributes)..high(el.attributes):
-    if el.attributes[i] == oldAttr:
-      result = el.attributes[i]
-      el.attributes.delete(i)
-      return result
-  
+  if el.attributes != nil:
+    for i in low(el.attributes)..high(el.attributes):
+      if el.attributes[i] == oldAttr:
+        result = el.attributes[i]
+        el.attributes.delete(i)
+        return result
+
   raise newException(ENotFoundErr, "oldAttr is not a member of el's Attributes")
 
 proc setAttributeNode*(el: PElement, newAttr: PAttr): PAttr =
   ## Adds a new attribute node, if an attribute with the same `nodeName` is
   ## present, it is replaced by the new one and the replaced attribute is
   ## returned, otherwise ``nil`` is returned.
-  
+
   # Check if newAttr is from this nodes document
   if el.FOwnerDocument != newAttr.FOwnerDocument:
-    raise newException(EWrongDocumentErr, 
+    raise newException(EWrongDocumentErr,
       "This node belongs to a different document, use importNode.")
-        
+
   if newAttr.FOwnerElement != nil:
-    raise newException(EInuseAttributeErr, 
+    raise newException(EInuseAttributeErr,
       "This attribute is in use by another element, use cloneNode")
   # Exceptions end
-  
-  if isNil(el.attributes): el.attributes = @[]
+
+  if el.attributes == nil: el.attributes = @[]
   return el.attributes.setNamedItem(newAttr)
-  
+
 proc setAttributeNodeNS*(el: PElement, newAttr: PAttr): PAttr =
-  ## Adds a new attribute node, if an attribute with the localName and 
+  ## Adds a new attribute node, if an attribute with the localName and
   ## namespaceURI of ``newAttr`` is present, it is replaced by the new one
   ## and the replaced attribute is returned, otherwise ``nil`` is returned.
-  
+
   # Check if newAttr is from this nodes document
   if el.FOwnerDocument != newAttr.FOwnerDocument:
-    raise newException(EWrongDocumentErr, 
+    raise newException(EWrongDocumentErr,
       "This node belongs to a different document, use importNode.")
-        
+
   if newAttr.FOwnerElement != nil:
-    raise newException(EInuseAttributeErr, 
+    raise newException(EInuseAttributeErr,
       "This attribute is in use by another element, use cloneNode")
   # Exceptions end
-  
-  if isNil(el.attributes): el.attributes = @[]
+
+  if el.attributes == nil: el.attributes = @[]
   return el.attributes.setNamedItemNS(newAttr)
 
 proc setAttribute*(el: PElement, name: string, value: string) =
   ## Adds a new attribute, as specified by ``name`` and ``value``
-  ## If an attribute with that name is already present in the element, its 
+  ## If an attribute with that name is already present in the element, its
   ## value is changed to be that of the value parameter
-  ## Raises the EInvalidCharacterErr if the specified ``name`` contains 
+  ## Raises the EInvalidCharacterErr if the specified ``name`` contains
   ## illegal characters
   var AttrNode = el.FOwnerDocument.createAttribute(name)
   # Check if name contains illegal characters
   if illegalChars in name:
     raise newException(EInvalidCharacterErr, "Invalid character")
-    
+
   discard el.setAttributeNode(AttrNode)
   # Set the info later, the setAttributeNode checks
   # if FOwnerElement is nil, and if it isn't it raises an exception
   AttrNode.FOwnerElement = el
   AttrNode.FSpecified = True
   AttrNode.value = value
-  
+
 proc setAttributeNS*(el: PElement, namespaceURI, localName, value: string) =
-  ## Adds a new attribute, as specified by ``namespaceURI``, ``localName`` 
+  ## Adds a new attribute, as specified by ``namespaceURI``, ``localName``
   ## and ``value``.
-  
+
   # Check if name contains illegal characters
   if illegalChars in namespaceURI or illegalChars in localName:
     raise newException(EInvalidCharacterErr, "Invalid character")
-    
+
   var AttrNode = el.FOwnerDocument.createAttributeNS(namespaceURI, localName)
-    
+
   discard el.setAttributeNodeNS(AttrNode)
   # Set the info later, the setAttributeNode checks
   # if FOwnerElement is nil, and if it isn't it raises an exception
@@ -1013,19 +1039,19 @@ proc setAttributeNS*(el: PElement, namespaceURI, localName, value: string) =
   AttrNode.FSpecified = True
   AttrNode.value = value
 
-# Text  
+# Text
 proc splitData*(TextNode: PText, offset: int): PText =
-  ## Breaks this node into two nodes at the specified offset, 
+  ## Breaks this node into two nodes at the specified offset,
   ## keeping both in the tree as siblings.
-  
+
   if offset > TextNode.data.len():
     raise newException(EIndexSizeErr, "Index out of bounds")
-  
+
   var left: string = TextNode.data.substr(0, offset)
   TextNode.data = left
   var right: string = TextNode.data.substr(offset, TextNode.data.len())
-  
-  if TextNode.FParentNode != nil:
+
+  if TextNode.FParentNode != nil and TextNode.FParentNode.childNodes != nil:
     for i in low(TextNode.FParentNode.childNodes)..high(TextNode.FParentNode.childNodes):
       if TextNode.FParentNode.childNodes[i] == TextNode:
         var newNode: PText = TextNode.FOwnerDocument.createTextNode(right)
@@ -1042,10 +1068,10 @@ proc target*(PI: PProcessingInstruction): string =
 
   return PI.FTarget
 
-    
+
 # --Other stuff--
 # Writer
-proc addEscaped(s: string): string = 
+proc addEscaped(s: string): string =
   result = ""
   for c in items(s):
     case c
@@ -1057,10 +1083,11 @@ proc addEscaped(s: string): string =
 
 proc nodeToXml(n: PNode, indent: int = 0): string =
   result = repeatChar(indent, ' ') & "<" & n.nodeName
-  for i in items(n.Attributes):
-    result.add(" " & i.name & "=\"" & addEscaped(i.value) & "\"")
-  
-  if n.childNodes.len() == 0:
+  if n.attributes != nil:
+    for i in items(n.attributes):
+      result.add(" " & i.name & "=\"" & addEscaped(i.value) & "\"")
+
+  if n.childNodes == nil or n.childNodes.len() == 0:
     result.add("/>") # No idea why this doesn't need a \n :O
   else:
     # End the beginning of this tag

From 7b9e2881d8eab90007a83fce88c12dae5d5193f9 Mon Sep 17 00:00:00 2001
From: def 
Date: Thu, 31 Jul 2014 11:28:26 +0200
Subject: [PATCH 29/37] Add "struct" to POSIX flock

---
 lib/posix/posix.nim | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim
index 8e66336c2e..a5ee05abbd 100644
--- a/lib/posix/posix.nim
+++ b/lib/posix/posix.nim
@@ -90,7 +90,7 @@ type
     d_ino*: Tino  ## File serial number.
     d_name*: array [0..255, char] ## Name of entry.
 
-  Tflock* {.importc: "flock", final, pure,
+  Tflock* {.importc: "struct flock", final, pure,
             header: "".} = object ## flock type
     l_type*: cshort   ## Type of lock; F_RDLCK, F_WRLCK, F_UNLCK.
     l_whence*: cshort ## Flag for starting offset.

From 0dd46ee8451115d0485a76cfc3f80ce893310159 Mon Sep 17 00:00:00 2001
From: def 
Date: Fri, 1 Aug 2014 01:45:58 +0200
Subject: [PATCH 30/37] Set kind of literal nodes as resExpr

---
 compiler/jsgen.nim | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 6687e2e8ec..8fa9838ee4 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -1532,6 +1532,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
     genSym(p, n, r)
   of nkCharLit..nkInt64Lit:
     r.res = toRope(n.intVal)
+    r.kind = resExpr
   of nkNilLit:
     if isEmptyType(n.typ):
       discard
@@ -1539,8 +1540,10 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
       r.typ = etyBaseIndex
       r.address = toRope"null" | toRope"nil"
       r.res = toRope"0"
+      r.kind = resExpr
     else:
       r.res = toRope"null" | toRope"nil"
+      r.kind = resExpr
   of nkStrLit..nkTripleStrLit:
     if skipTypes(n.typ, abstractVarRange).kind == tyString: 
       useMagic(p, "cstrToNimstr")
@@ -1556,6 +1559,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
       if f > 0.0: r.res = toRope"Infinity"
       else: r.res = toRope"-Infinity"
     else: r.res = toRope(f.toStrMaxPrecision)
+    r.kind = resExpr
   of nkCallKinds:
     if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic != mNone): 
       genMagic(p, n, r)

From f17de0b13ac0e683e3a100eb0c3ea5ff23ff7ce2 Mon Sep 17 00:00:00 2001
From: def 
Date: Fri, 1 Aug 2014 02:06:07 +0200
Subject: [PATCH 31/37] Fix typo in mHigh

---
 compiler/jsgen.nim | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 6687e2e8ec..662ee837b6 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -1321,7 +1321,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
   of mLengthSeq, mLengthOpenArray, mLengthArray:
     unaryExpr(p, n, r, "", "$1.length")
   of mHigh:
-    if skipTypes(n.sons[0].typ, abstractVar).kind == tyString:
+    if skipTypes(n.sons[1].typ, abstractVar).kind == tyString:
       unaryExpr(p, n, r, "", "($1.length-2)")
     else:
       unaryExpr(p, n, r, "", "($1.length-1)")

From dc756a76187aa94c9eca18d070a9710ac4193218 Mon Sep 17 00:00:00 2001
From: def 
Date: Fri, 1 Aug 2014 02:23:13 +0200
Subject: [PATCH 32/37] replace nil comparisons with calls to isNil

---
 lib/pure/xmldom.nim | 104 ++++++++++++++++++++++----------------------
 1 file changed, 52 insertions(+), 52 deletions(-)

diff --git a/lib/pure/xmldom.nim b/lib/pure/xmldom.nim
index 98e4104e43..d63b6c5dd3 100644
--- a/lib/pure/xmldom.nim
+++ b/lib/pure/xmldom.nim
@@ -168,7 +168,7 @@ proc documentElement*(doc: PDocument): PElement =
 proc findNodes(nl: PNode, name: string): seq[PNode] =
   # Made for getElementsByTagName
   var r: seq[PNode] = @[]
-  if nl.childNodes == nil: return @[]
+  if isNil(nl.childNodes): return @[]
   if nl.childNodes.len() == 0: return @[]
 
   for i in items(nl.childNodes):
@@ -176,7 +176,7 @@ proc findNodes(nl: PNode, name: string): seq[PNode] =
       if i.FNodeName == name or name == "*":
         r.add(i)
 
-      if i.childNodes != nil:
+      if not isNil(i.childNodes):
         if i.childNodes.len() != 0:
           r.add(findNodes(i, name))
 
@@ -185,7 +185,7 @@ proc findNodes(nl: PNode, name: string): seq[PNode] =
 proc findNodesNS(nl: PNode, namespaceURI: string, localName: string): seq[PNode] =
   # Made for getElementsByTagNameNS
   var r: seq[PNode] = @[]
-  if nl.childNodes == nil: return @[]
+  if isNil(nl.childNodes): return @[]
   if nl.childNodes.len() == 0: return @[]
 
   for i in items(nl.childNodes):
@@ -193,7 +193,7 @@ proc findNodesNS(nl: PNode, namespaceURI: string, localName: string): seq[PNode]
       if (i.FNamespaceURI == namespaceURI or namespaceURI == "*") and (i.FLocalName == localName or localName == "*"):
         r.add(i)
 
-      if i.childNodes != nil:
+      if not isNil(i.childNodes):
         if i.childNodes.len() != 0:
           r.add(findNodesNS(i, namespaceURI, localName))
 
@@ -228,7 +228,7 @@ proc createAttributeNS*(doc: PDocument, namespaceURI: string, qualifiedName: str
     raise newException(EInvalidCharacterErr, "Invalid character")
   # Exceptions
   if qualifiedName.contains(':'):
-    if namespaceURI == nil:
+    if isNil(namespaceURI):
       raise newException(ENamespaceErr, "When qualifiedName contains a prefix namespaceURI cannot be nil")
     elif qualifiedName.split(':')[0].toLower() == "xml" and namespaceURI != "http://www.w3.org/XML/1998/namespace":
       raise newException(ENamespaceErr,
@@ -304,7 +304,7 @@ proc createElement*(doc: PDocument, tagName: string): PElement =
 proc createElementNS*(doc: PDocument, namespaceURI: string, qualifiedName: string): PElement =
   ## Creates an element of the given qualified name and namespace URI.
   if qualifiedName.contains(':'):
-    if namespaceURI == nil:
+    if isNIl(namespaceURI):
       raise newException(ENamespaceErr, "When qualifiedName contains a prefix namespaceURI cannot be nil")
     elif qualifiedName.split(':')[0].toLower() == "xml" and namespaceURI != "http://www.w3.org/XML/1998/namespace":
       raise newException(ENamespaceErr,
@@ -445,7 +445,7 @@ proc importNode*(doc: PDocument, importedNode: PNode, deep: bool): PNode =
 proc firstChild*(n: PNode): PNode =
   ## Returns this node's first child
 
-  if n.childNodes != nil and n.childNodes.len() > 0:
+  if not isNil(n.childNodes) and n.childNodes.len() > 0:
     return n.childNodes[0]
   else:
     return nil
@@ -453,7 +453,7 @@ proc firstChild*(n: PNode): PNode =
 proc lastChild*(n: PNode): PNode =
   ## Returns this node's last child
 
-  if n.childNodes != nil and n.childNodes.len() > 0:
+  if not isNil(n.childNodes) and n.childNodes.len() > 0:
     return n.childNodes[n.childNodes.len() - 1]
   else:
     return nil
@@ -474,7 +474,7 @@ proc `namespaceURI=`*(n: PNode, value: string) =
 proc nextSibling*(n: PNode): PNode =
   ## Returns the next sibling of this node
 
-  if n.FParentNode == nil or n.FParentNode.childNodes == nil:
+  if isNil(n.FParentNode) or isNil(n.FParentNode.childNodes):
     return nil
   var nLow: int = low(n.FParentNode.childNodes)
   var nHigh: int = high(n.FParentNode.childNodes)
@@ -506,7 +506,7 @@ proc parentNode*(n: PNode): PNode =
 proc previousSibling*(n: PNode): PNode =
   ## Returns the previous sibling of this node
 
-  if n.FParentNode == nil or n.FParentNode.childNodes == nil:
+  if isNil(n.FParentNode) or isNil(n.FParentNode.childNodes):
     return nil
   var nLow: int = low(n.FParentNode.childNodes)
   var nHigh: int = high(n.FParentNode.childNodes)
@@ -523,7 +523,7 @@ proc `prefix=`*(n: PNode, value: string) =
   if illegalChars in value:
     raise newException(EInvalidCharacterErr, "Invalid character")
 
-  if n.FNamespaceURI == nil:
+  if isNil(n.FNamespaceURI):
     raise newException(ENamespaceErr, "namespaceURI cannot be nil")
   elif value.toLower() == "xml" and n.FNamespaceURI != "http://www.w3.org/XML/1998/namespace":
     raise newException(ENamespaceErr,
@@ -549,7 +549,7 @@ proc appendChild*(n: PNode, newChild: PNode) =
   ## If the newChild is already in the tree, it is first removed.
 
   # Check if n contains newChild
-  if n.childNodes != nil:
+  if not isNil(n.childNodes):
     for i in low(n.childNodes)..high(n.childNodes):
       if n.childNodes[i] == newChild:
         raise newException(EHierarchyRequestErr, "The node to append is already in this nodes children.")
@@ -564,7 +564,7 @@ proc appendChild*(n: PNode, newChild: PNode) =
   if n.nodeType in childlessObjects:
     raise newException(ENoModificationAllowedErr, "Cannot append children to a childless node")
 
-  if n.childNodes == nil: n.childNodes = @[]
+  if isNil(n.childNodes): n.childNodes = @[]
 
   newChild.FParentNode = n
   for i in low(n.childNodes)..high(n.childNodes):
@@ -590,7 +590,7 @@ proc cloneNode*(n: PNode, deep: bool): PNode =
     # Import the childNodes
     var tmp: seq[PNode] = n.childNodes
     n.childNodes = @[]
-    if deep and tmp != nil:
+    if deep and not isNil(tmp):
       for i in low(tmp.len())..high(tmp.len()):
         n.childNodes.add(cloneNode(tmp[i], deep))
     return newNode
@@ -602,11 +602,11 @@ proc cloneNode*(n: PNode, deep: bool): PNode =
 
 proc hasAttributes*(n: PNode): bool =
   ## Returns whether this node (if it is an element) has any attributes.
-  return n.attributes != nil and n.attributes.len() > 0
+  return not isNil(n.attributes) and n.attributes.len() > 0
 
 proc hasChildNodes*(n: PNode): bool =
   ## Returns whether this node has any children.
-  return n.childNodes != nil and n.childNodes.len() > 0
+  return not isNil(n.childNodes) and n.childNodes.len() > 0
 
 proc insertBefore*(n: PNode, newChild: PNode, refChild: PNode): PNode =
   ## Inserts the node ``newChild`` before the existing child node ``refChild``.
@@ -616,7 +616,7 @@ proc insertBefore*(n: PNode, newChild: PNode, refChild: PNode): PNode =
   if n.FOwnerDocument != newChild.FOwnerDocument:
     raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.")
 
-  if n.childNodes == nil:
+  if isNil(n.childNodes):
     n.ChildNodes = @[]
 
   for i in low(n.childNodes)..high(n.childNodes):
@@ -633,7 +633,7 @@ proc isSupported*(n: PNode, feature: string, version: string): bool =
 
 proc isEmpty(s: string): bool =
 
-  if s == "" or s == nil:
+  if isNil(s) or s == "":
     return True
   for i in items(s):
     if i != ' ':
@@ -647,7 +647,7 @@ proc normalize*(n: PNode) =
 
   var newChildNodes: seq[PNode] = @[]
   while True:
-    if n.childNodes == nil or i >= n.childNodes.len:
+    if isNil(n.childNodes) or i >= n.childNodes.len:
       break
     if n.childNodes[i].nodeType == TextNode:
 
@@ -655,7 +655,7 @@ proc normalize*(n: PNode) =
       if PText(n.childNodes[i]).data.isEmpty():
         inc(i)
 
-      if curTextNode == nil:
+      if isNil(curTextNode):
         curTextNode = n.childNodes[i]
       else:
         PText(curTextNode).data.add(PText(n.childNodes[i]).data)
@@ -671,7 +671,7 @@ proc normalize*(n: PNode) =
 
 proc removeChild*(n: PNode, oldChild: PNode): PNode =
   ## Removes the child node indicated by ``oldChild`` from the list of children, and returns it.
-  if n.childNodes != nil:
+  if not isNil(n.childNodes):
     for i in low(n.childNodes)..high(n.childNodes):
       if n.childNodes[i] == oldChild:
         result = n.childNodes[i]
@@ -687,7 +687,7 @@ proc replaceChild*(n: PNode, newChild: PNode, oldChild: PNode): PNode =
   if n.FOwnerDocument != newChild.FOwnerDocument:
     raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.")
 
-  if n.childNodes != nil:
+  if not isNil(n.childNodes):
     for i in low(n.childNodes)..high(n.childNodes):
       if n.childNodes[i] == oldChild:
         result = n.childNodes[i]
@@ -756,7 +756,7 @@ proc removeNamedItemNS*(NList: var seq[PNode], namespaceURI: string, localName:
 proc setNamedItem*(NList: var seq[PNode], arg: PNode): PNode =
   ## Adds ``arg`` as a ``Node`` to the ``NList``
   ## If a node with the same name is already present in this map, it is replaced by the new one.
-  if NList != nil:
+  if not isNil(NList):
     if NList.len() > 0:
       #Check if newChild is from this nodes document
       if NList[0].FOwnerDocument != arg.FOwnerDocument:
@@ -764,7 +764,7 @@ proc setNamedItem*(NList: var seq[PNode], arg: PNode): PNode =
   #Exceptions End
 
   var item: PNode = NList.getNamedItem(arg.NodeName())
-  if item == nil:
+  if isNil(item):
     NList.add(arg)
     return nil
   else:
@@ -780,18 +780,18 @@ proc setNamedItem*(NList: var seq[PNode], arg: PNode): PNode =
 proc setNamedItem*(NList: var seq[PAttr], arg: PAttr): PAttr =
   ## Adds ``arg`` as a ``Node`` to the ``NList``
   ## If a node with the same name is already present in this map, it is replaced by the new one.
-  if NList != nil:
+  if not isNil(NList):
     if NList.len() > 0:
       # Check if newChild is from this nodes document
       if NList[0].FOwnerDocument != arg.FOwnerDocument:
         raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.")
 
-  if arg.FOwnerElement != nil:
+  if not isNil(arg.FOwnerElement):
     raise newException(EInuseAttributeErr, "This attribute is in use by another element, use cloneNode")
 
   # Exceptions end
   var item: PAttr = NList.getNamedItem(arg.nodeName())
-  if item == nil:
+  if isNil(item):
     NList.add(arg)
     return nil
   else:
@@ -806,7 +806,7 @@ proc setNamedItem*(NList: var seq[PAttr], arg: PAttr): PAttr =
 
 proc setNamedItemNS*(NList: var seq[PNode], arg: PNode): PNode =
   ## Adds a node using its ``namespaceURI`` and ``localName``
-  if NList != nil:
+  if not isNil(NList):
     if NList.len() > 0:
       # Check if newChild is from this nodes document
       if NList[0].FOwnerDocument != arg.FOwnerDocument:
@@ -814,7 +814,7 @@ proc setNamedItemNS*(NList: var seq[PNode], arg: PNode): PNode =
   #Exceptions end
 
   var item: PNode = NList.getNamedItemNS(arg.namespaceURI(), arg.localName())
-  if item == nil:
+  if isNil(item):
     NList.add(arg)
     return nil
   else:
@@ -829,18 +829,18 @@ proc setNamedItemNS*(NList: var seq[PNode], arg: PNode): PNode =
 
 proc setNamedItemNS*(NList: var seq[PAttr], arg: PAttr): PAttr =
   ## Adds a node using its ``namespaceURI`` and ``localName``
-  if NList != nil:
+  if not isNil(NList):
     if NList.len() > 0:
       # Check if newChild is from this nodes document
       if NList[0].FOwnerDocument != arg.FOwnerDocument:
         raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.")
 
-  if arg.FOwnerElement != nil:
+  if not isNil(arg.FOwnerElement):
     raise newException(EInuseAttributeErr, "This attribute is in use by another element, use cloneNode")
 
   # Exceptions end
   var item: PAttr = NList.getNamedItemNS(arg.namespaceURI(), arg.localName())
-  if item == nil:
+  if isNil(item):
     NList.add(arg)
     return nil
   else:
@@ -884,20 +884,20 @@ proc tagName*(el: PElement): string =
 # Procedures
 proc getAttribute*(el: PElement, name: string): string =
   ## Retrieves an attribute value by ``name``
-  if el.attributes == nil:
+  if isNil(el.attributes):
     return nil
   var attribute = el.attributes.getNamedItem(name)
-  if attribute != nil:
+  if not isNil(attribute):
     return attribute.value
   else:
     return nil
 
 proc getAttributeNS*(el: PElement, namespaceURI: string, localName: string): string =
   ## Retrieves an attribute value by ``localName`` and ``namespaceURI``
-  if el.attributes == nil:
+  if isNil(el.attributes):
     return nil
   var attribute = el.attributes.getNamedItemNS(namespaceURI, localName)
-  if attribute != nil:
+  if not isNil(attribute):
     return attribute.value
   else:
     return nil
@@ -905,13 +905,13 @@ proc getAttributeNS*(el: PElement, namespaceURI: string, localName: string): str
 proc getAttributeNode*(el: PElement, name: string): PAttr =
   ## Retrieves an attribute node by ``name``
   ## To retrieve an attribute node by qualified name and namespace URI, use the `getAttributeNodeNS` method
-  if el.attributes == nil:
+  if isNil(el.attributes):
     return nil
   return el.attributes.getNamedItem(name)
 
 proc getAttributeNodeNS*(el: PElement, namespaceURI: string, localName: string): PAttr =
   ## Retrieves an `Attr` node by ``localName`` and ``namespaceURI``
-  if el.attributes == nil:
+  if isNil(el.attributes):
     return nil
   return el.attributes.getNamedItemNS(namespaceURI, localName)
 
@@ -930,27 +930,27 @@ proc getElementsByTagNameNS*(el: PElement, namespaceURI: string, localName: stri
 proc hasAttribute*(el: PElement, name: string): bool =
   ## Returns ``true`` when an attribute with a given ``name`` is specified
   ## on this element , ``false`` otherwise.
-  if el.attributes == nil:
+  if isNil(el.attributes):
     return false
-  return el.attributes.getNamedItem(name) != nil
+  return not isNil(el.attributes.getNamedItem(name))
 
 proc hasAttributeNS*(el: PElement, namespaceURI: string, localName: string): bool =
   ## Returns ``true`` when an attribute with a given ``localName`` and
   ## ``namespaceURI`` is specified on this element , ``false`` otherwise
-  if el.attributes == nil:
+  if isNil(el.attributes):
     return false
-  return el.attributes.getNamedItemNS(namespaceURI, localName) != nil
+  return not isNil(el.attributes.getNamedItemNS(namespaceURI, localName))
 
 proc removeAttribute*(el: PElement, name: string) =
   ## Removes an attribute by ``name``
-  if el.attributes != nil:
+  if not isNil(el.attributes):
     for i in low(el.attributes)..high(el.attributes):
       if el.attributes[i].FName == name:
         el.attributes.delete(i)
 
 proc removeAttributeNS*(el: PElement, namespaceURI: string, localName: string) =
   ## Removes an attribute by ``localName`` and ``namespaceURI``
-  if el.attributes != nil:
+  if not isNil(el.attributes):
     for i in low(el.attributes)..high(el.attributes):
       if el.attributes[i].FNamespaceURI == namespaceURI and
           el.attributes[i].FLocalName == localName:
@@ -959,7 +959,7 @@ proc removeAttributeNS*(el: PElement, namespaceURI: string, localName: string) =
 proc removeAttributeNode*(el: PElement, oldAttr: PAttr): PAttr =
   ## Removes the specified attribute node
   ## If the attribute node cannot be found raises ``ENotFoundErr``
-  if el.attributes != nil:
+  if not isNil(el.attributes):
     for i in low(el.attributes)..high(el.attributes):
       if el.attributes[i] == oldAttr:
         result = el.attributes[i]
@@ -978,12 +978,12 @@ proc setAttributeNode*(el: PElement, newAttr: PAttr): PAttr =
     raise newException(EWrongDocumentErr,
       "This node belongs to a different document, use importNode.")
 
-  if newAttr.FOwnerElement != nil:
+  if not isNil(newAttr.FOwnerElement):
     raise newException(EInuseAttributeErr,
       "This attribute is in use by another element, use cloneNode")
   # Exceptions end
 
-  if el.attributes == nil: el.attributes = @[]
+  if isNil(el.attributes): el.attributes = @[]
   return el.attributes.setNamedItem(newAttr)
 
 proc setAttributeNodeNS*(el: PElement, newAttr: PAttr): PAttr =
@@ -996,12 +996,12 @@ proc setAttributeNodeNS*(el: PElement, newAttr: PAttr): PAttr =
     raise newException(EWrongDocumentErr,
       "This node belongs to a different document, use importNode.")
 
-  if newAttr.FOwnerElement != nil:
+  if not isNil(newAttr.FOwnerElement):
     raise newException(EInuseAttributeErr,
       "This attribute is in use by another element, use cloneNode")
   # Exceptions end
 
-  if el.attributes == nil: el.attributes = @[]
+  if isNil(el.attributes): el.attributes = @[]
   return el.attributes.setNamedItemNS(newAttr)
 
 proc setAttribute*(el: PElement, name: string, value: string) =
@@ -1051,7 +1051,7 @@ proc splitData*(TextNode: PText, offset: int): PText =
   TextNode.data = left
   var right: string = TextNode.data.substr(offset, TextNode.data.len())
 
-  if TextNode.FParentNode != nil and TextNode.FParentNode.childNodes != nil:
+  if not isNil(TextNode.FParentNode) and not isNil(TextNode.FParentNode.childNodes):
     for i in low(TextNode.FParentNode.childNodes)..high(TextNode.FParentNode.childNodes):
       if TextNode.FParentNode.childNodes[i] == TextNode:
         var newNode: PText = TextNode.FOwnerDocument.createTextNode(right)
@@ -1083,11 +1083,11 @@ proc addEscaped(s: string): string =
 
 proc nodeToXml(n: PNode, indent: int = 0): string =
   result = repeatChar(indent, ' ') & "<" & n.nodeName
-  if n.attributes != nil:
+  if not isNil(n.attributes):
     for i in items(n.attributes):
       result.add(" " & i.name & "=\"" & addEscaped(i.value) & "\"")
 
-  if n.childNodes == nil or n.childNodes.len() == 0:
+  if isNil(n.childNodes) or n.childNodes.len() == 0:
     result.add("/>") # No idea why this doesn't need a \n :O
   else:
     # End the beginning of this tag

From eada55fc78fbe43aa8eb756c992d4b40473f1c23 Mon Sep 17 00:00:00 2001
From: def 
Date: Fri, 1 Aug 2014 22:48:55 +0200
Subject: [PATCH 33/37] Make nimrepl and trimcc tools compile

---
 tools/nimrepl.nim | 2 +-
 tools/trimcc.nim  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/tools/nimrepl.nim b/tools/nimrepl.nim
index 8e43b4431d..4b6379bd59 100644
--- a/tools/nimrepl.nim
+++ b/tools/nimrepl.nim
@@ -24,7 +24,7 @@ proc execCode(code: string): string =
     f.close()
     result = osproc.execProcess(
       "$# $# --verbosity:0 --hint[Conf]:off temp.nim" % [nimExe, runCmd],
-      {poStdErrToStdOut})
+      options = {poStdErrToStdOut})
   else:
     result = "cannot open file 'temp.nim'"
 
diff --git a/tools/trimcc.nim b/tools/trimcc.nim
index 6271d2b9a6..4d96861953 100644
--- a/tools/trimcc.nim
+++ b/tools/trimcc.nim
@@ -18,7 +18,7 @@ proc walker(dir: string) =
       else:
         echo "Required: ", path
         # copy back:
-        moveFile(dest=path, sourc=newName(path))
+        moveFile(dest=path, source=newName(path))
     of pcDir:
       walker(path)
     else: discard

From 23f64dd63d6c8f178f5fca1ec6e7706844808359 Mon Sep 17 00:00:00 2001
From: Varriount 
Date: Sat, 2 Aug 2014 00:16:10 -0400
Subject: [PATCH 34/37] Update commands.nim

---
 compiler/commands.nim | 2 --
 1 file changed, 2 deletions(-)

diff --git a/compiler/commands.nim b/compiler/commands.nim
index 4fec420f77..7219c168ad 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -291,14 +291,12 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
   of "excludepath":
     expectArg(switch, arg, pass, info)
     let path = processPath(arg)
-    echo repr(options.searchPaths)
     lists.excludePath(options.searchPaths, path)
     lists.excludePath(options.lazyPaths, path)
     if (len(path) > 0) and (path[len(path) - 1] == DirSep):
       let strippedPath = path[0 .. (len(path) - 2)]
       lists.excludePath(options.searchPaths, strippedPath)
       lists.excludePath(options.lazyPaths, strippedPath)
-    echo repr(options.searchPaths)
   of "nimcache":
     expectArg(switch, arg, pass, info)
     options.nimcacheDir = processPath(arg)

From 9fd6464a746187faad61b92bafcf98410f6f88d3 Mon Sep 17 00:00:00 2001
From: Grzegorz Adam Hankiewicz 
Date: Sat, 2 Aug 2014 20:44:37 +0200
Subject: [PATCH 35/37] Updates js.high usage for line ending dots. Refs #1291
 and #1292.

---
 web/babelpkglist.nim | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/web/babelpkglist.nim b/web/babelpkglist.nim
index 8745c9f997..8de6047efc 100644
--- a/web/babelpkglist.nim
+++ b/web/babelpkglist.nim
@@ -38,8 +38,7 @@ proc processContent(content: string) =
       else: pkg["url"].str
     let
       desc = pkg["description"].str
-      # Review array index access when #1291 is solved.
-      dot = if desc.high > 0 and desc[ 0 and desc[desc.high] in endings: "" else: "."
       listItem = li(a(href=pkgWeb, pkg["name"].str), " ", desc & dot)
     if pkg["url"].str.startsWith("git://github.com/nimrod-code") or
        "official" in pkg["tags"].elems:

From bddfe007b6975687102e924cebab0497ea93584f Mon Sep 17 00:00:00 2001
From: Grzegorz Adam Hankiewicz 
Date: Sat, 2 Aug 2014 20:51:21 +0200
Subject: [PATCH 36/37] Adds placeholder text for js disabled browsers. Refs
 #1292.

---
 doc/lib.txt          | 6 ++++--
 web/babelpkglist.nim | 6 ++----
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/doc/lib.txt b/doc/lib.txt
index 2da753007b..5bacfcc4f9 100644
--- a/doc/lib.txt
+++ b/doc/lib.txt
@@ -594,7 +594,8 @@ compiler.
 
 .. raw:: html
 
-  
+
If you are reading this you are missing + babelpkglist.js or have javascript disabled in your browser.
Unofficial packages ------------------- @@ -605,7 +606,8 @@ Nimrod programming language. .. raw:: html -
+
If you are reading this you are missing + babelpkglist.js or have javascript disabled in your browser.
diff --git a/web/babelpkglist.nim b/web/babelpkglist.nim index 8de6047efc..e8f9efc1bd 100644 --- a/web/babelpkglist.nim +++ b/web/babelpkglist.nim @@ -50,19 +50,17 @@ proc processContent(content: string) = var officialPkgListDiv = document.getElementById("officialPkgList") - officialPkgListDiv.innerHTML.add( + officialPkgListDiv.innerHTML = p("There are currently " & $officialCount & " official packages in the Babel package repository.") & ul(officialList) - ) var unofficialPkgListDiv = document.getElementById("unofficialPkgList") - unofficialPkgListDiv.innerHTML.add( + unofficialPkgListDiv.innerHTML = p("There are currently " & $unofficialCount & " unofficial packages in the Babel package repository.") & ul(unofficialList) - ) proc gotPackageList(apiReply: TData) {.exportc.} = let decoded = decodeContent($apiReply.content) From ca937cdba8246eb21357e4147a568709c62a5463 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sat, 2 Aug 2014 20:53:56 +0200 Subject: [PATCH 37/37] Uses case insensitive comparison for sorting. Refs #1292. --- web/babelpkglist.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/babelpkglist.nim b/web/babelpkglist.nim index e8f9efc1bd..5da7d60a9c 100644 --- a/web/babelpkglist.nim +++ b/web/babelpkglist.nim @@ -21,7 +21,7 @@ proc processContent(content: string) = var jsonArr = jsonDoc.elems jsonArr.sort do (x, y: PJsonNode) -> int: - system.cmp(x["name"].str, y["name"].str) + strutils.cmpIgnoreCase(x["name"].str, y["name"].str) var officialList = ""