diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index ff262f1a60..b4beb5a7e9 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1604,15 +1604,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 optOverflowCheck notin p.options: unaryExpr(p, e, d, "$1 - 1") + if optOverflowCheck notin p.options: unaryExpr(p, e, d, "($1 - 1)") else: unaryExpr(p, e, d, "#subInt($1, 1)") of mPred: # XXX: range checking? - if optOverflowCheck notin p.options: binaryExpr(p, e, d, "$1 - $2") + if optOverflowCheck notin p.options: binaryExpr(p, e, d, "($1 - $2)") else: binaryExpr(p, e, d, "#subInt($1, $2)") of mSucc: # XXX: range checking? - if optOverflowCheck notin p.options: binaryExpr(p, e, d, "$1 + $2") + if optOverflowCheck notin p.options: binaryExpr(p, e, d, "($1 + $2)") else: binaryExpr(p, e, d, "#addInt($1, $2)") of mInc: if optOverflowCheck notin p.options: diff --git a/compiler/commands.nim b/compiler/commands.nim index 38c8dd2949..7219c168ad 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -291,8 +291,12 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = of "excludepath": expectArg(switch, arg, pass, info) let path = processPath(arg) - lists.excludeStr(options.searchPaths, path) - lists.excludeStr(options.lazyPaths, path) + 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) of "nimcache": expectArg(switch, arg, pass, info) options.nimcacheDir = processPath(arg) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 6687e2e8ec..96d8b3d111 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)") @@ -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) 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 = 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..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 != "": + if optRun notin gGlobalOptions and arguments != "" and options.command.normalize != "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, "") 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/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 -
+", "\\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)
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.
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
diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim
index f1eed00042..42cdc682fb 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.
@@ -23,20 +27,69 @@ 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],int>`_ or `initSet[type]() <#initSet>`_
+ ## before calling other procs on it.
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 procs 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`.
+ ## 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]>`_.
+ ##
+ ## Card stands for the `cardinality
+ ## `_ of a set.
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
@@ -73,12 +126,24 @@ 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`.
+ ## 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
@@ -109,38 +174,124 @@ 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:
s.data[index].slot = seDeleted
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()
-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 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:
+ ##
+ ## .. 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] =
+ ## 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:
+ ##
+ ## .. 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`.
+ ## 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)
@@ -152,57 +303,190 @@ 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))
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`
+ ## 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]()
for item in s1:
if not contains(s2, item):
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
for item in s2:
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:
if item in s2: return false
return true
+proc `<`*[A](s, t: TSet[A]): bool =
+ ## 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 =
+ ## 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
+ for item in s:
+ if not(t.contains(item)):
+ result = false
+ 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))
+
# ------------------------------ ordered set ------------------------------
type
@@ -210,16 +494,48 @@ 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],int>`_ or `initOrderedSet[type]()
+ ## <#initOrderedSet>`_ before calling other procs on it.
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 procs 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`.
+ ## 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]>`_.
+ ##
+ ## Card stands for the `cardinality
+ ## `_ of a set.
result = s.counter
template forAllOrderedPairs(yieldStmt: stmt) {.dirty, immediate.} =
@@ -230,7 +546,24 @@ 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
@@ -238,7 +571,16 @@ 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
@@ -264,53 +606,279 @@ 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()
-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 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]
+ ## 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] =
+ ## 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:
+ ##
+ ## .. 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`.
+ ## 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()
-proc `<`*[A](s, t: TSet[A]): bool =
- ## Is s a strict subset of t?
- s.counter != t.counter and s <= t
+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
-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
+ block lenTest:
+ var values: TSet[int]
+ assert(not values.isValid)
+ assert values.len == 0
+ assert values.card == 0
-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))
+ 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))
+
+ 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([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(["a", "b"])
+ b = toSet(["b", "c"])
+ c = union(a, b)
+ assert c == toSet(["a", "b", "c"])
+ var d = intersection(a, b)
+ assert d == toSet(["b"])
+ var e = difference(a, b)
+ assert e == toSet(["a"])
+ var f = symmetricDifference(a, b)
+ 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(["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([1, 2, 3])
+ var b = a.map(proc (x: int): string = $x)
+ assert b == toSet(["1", "2", "3"])
+
+ 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 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))
+ values.incl(2)
+ assert values.contains(2)
+
+ block toSeqAndString:
+ var a = toOrderedSet([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
+
+ 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
+
+ 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()
diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim
index ce9df09e10..dcf2ab4811 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: result = 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/lib/pure/json.nim b/lib/pure/json.nim
index 508e564c57..a45900f297 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:
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index a7f4f7d916..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)
@@ -1363,7 +1364,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)
@@ -1507,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:
@@ -1523,6 +1533,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].} =
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
diff --git a/lib/pure/xmldom.nim b/lib/pure/xmldom.nim
index 47e94243eb..d63b6c5dd3 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,11 +157,11 @@ 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
@@ -175,13 +175,13 @@ proc findNodes(nl: PNode, name: string): seq[PNode] =
if i.FNodeType == ElementNode:
if i.FNodeName == name or name == "*":
r.add(i)
-
+
if not isNil(i.childNodes):
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] = @[]
@@ -192,23 +192,23 @@ proc findNodesNS(nl: PNode, namespaceURI: string, localName: string): seq[PNode]
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.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,21 +222,21 @@ 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")
# 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,
+ 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,24 +296,24 @@ 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 =
## 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,
+ 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 not isNil(n.childNodes) 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 not isNil(n.childNodes) 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 isNil(n.FParentNode) or isNil(n.FParentNode.childNodes):
+ 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 isNil(n.FParentNode) or isNil(n.FParentNode.childNodes):
+ 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
@@ -519,13 +523,13 @@ 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,
+ 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 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.")
-
+
# 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 = @[]
-
+
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 not isNil(tmp):
for i in low(tmp.len())..high(tmp.len()):
n.childNodes.add(cloneNode(tmp[i], deep))
return newNode
@@ -597,34 +601,39 @@ 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 not isNil(n.attributes) 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 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``.
## 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 isNil(n.childNodes):
+ 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 =
- if s == "" or s == nil:
+ if isNil(s) or s == "":
return True
for i in items(s):
if i != ' ':
@@ -635,18 +644,18 @@ 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 isNil(n.childNodes) 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:
+
+ if isNil(curTextNode):
curTextNode = n.childNodes[i]
else:
PText(curTextNode).data.add(PText(n.childNodes[i]).data)
@@ -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 not isNil(n.childNodes):
+ 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 not isNil(n.childNodes):
+ 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,7 +750,7 @@ 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 =
@@ -751,9 +762,9 @@ proc setNamedItem*(NList: var seq[PNode], arg: PNode): PNode =
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:
+ if isNil(item):
NList.add(arg)
return nil
else:
@@ -765,22 +776,22 @@ 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 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:
@@ -792,18 +803,18 @@ 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 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.")
#Exceptions end
-
+
var item: PNode = NList.getNamedItemNS(arg.namespaceURI(), arg.localName())
- if item == nil:
+ if isNil(item):
NList.add(arg)
return nil
else:
@@ -815,7 +826,7 @@ 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):
@@ -823,13 +834,13 @@ proc setNamedItemNS*(NList: var seq[PAttr], arg: PAttr): PAttr =
# 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:
@@ -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,27 +884,35 @@ proc tagName*(el: PElement): string =
# Procedures
proc getAttribute*(el: PElement, name: string): string =
## Retrieves an attribute value by ``name``
+ 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 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
-
+
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 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 isNil(el.attributes):
+ 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.
- return el.attributes.getNamedItem(name) != nil
+ ## Returns ``true`` when an attribute with a given ``name`` is specified
+ ## on this element , ``false`` otherwise.
+ if isNil(el.attributes):
+ return false
+ 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
- return el.attributes.getNamedItemNS(namespaceURI, localName) != nil
+ ## ``namespaceURI`` is specified on this element , ``false`` otherwise
+ if isNil(el.attributes):
+ return false
+ return not isNil(el.attributes.getNamedItemNS(namespaceURI, localName))
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 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``
- 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 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:
+ 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 not isNil(el.attributes):
+ 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,
+
+ if not isNil(newAttr.FOwnerElement):
+ raise newException(EInuseAttributeErr,
"This attribute is in use by another element, use cloneNode")
# Exceptions end
-
+
if isNil(el.attributes): 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,
+
+ if not isNil(newAttr.FOwnerElement):
+ raise newException(EInuseAttributeErr,
"This attribute is in use by another element, use cloneNode")
# Exceptions end
-
+
if isNil(el.attributes): 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 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)
@@ -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 not isNil(n.attributes):
+ for i in items(n.attributes):
+ result.add(" " & i.name & "=\"" & addEscaped(i.value) & "\"")
+
+ 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
diff --git a/lib/system.nim b/lib/system.nim
index 3e4d3fb9e7..963211008d 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
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
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
diff --git a/web/babelpkglist.nim b/web/babelpkglist.nim
index 8745c9f997..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 = ""
@@ -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:
@@ -51,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)