summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xcompiler/docgen.nim9
-rwxr-xr-xcompiler/renderer.nim26
-rwxr-xr-xcompiler/semexprs.nim23
-rwxr-xr-xconfig/nimrod.cfg1
-rwxr-xr-xdoc/lib.txt9
-rwxr-xr-xlib/pure/collections/lists.nim326
-rw-r--r--lib/pure/collections/tables.nim (renamed from lib/pure/collections/hashtables.nim)229
-rw-r--r--tests/accept/run/tlists.nim0
-rw-r--r--tests/accept/run/ttables.nim18
-rwxr-xr-xtodo.txt5
-rwxr-xr-xweb/index.txt13
11 files changed, 418 insertions, 241 deletions
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index 64973cddd..f51c8a8c1 100755
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -264,7 +264,7 @@ proc renderIndexTerm(d: PDoc, n: PRstNode): PRope =
 
 proc genComment(d: PDoc, n: PNode): PRope = 
   var dummyHasToc: bool
-  if (n.comment != nil) and startsWith(n.comment, "##"): 
+  if n.comment != nil and startsWith(n.comment, "##"): 
     result = renderRstToOut(d, rstParse(n.comment, true, toFilename(n.info), 
                                         toLineNumber(n.info), toColumn(n.info), 
                                         dummyHasToc))
@@ -385,8 +385,9 @@ proc renderHeadline(d: PDoc, n: PRstNode): PRope =
     d.tocPart[length].refname = refname
     d.tocPart[length].n = n
     d.tocPart[length].header = result
-    result = dispF("<h$1><a class=\"toc-backref\" id=\"$2\" href=\"#$2_toc\">$3</a></h$1>", 
-                   "\\rsth$4{$3}\\label{$2}$n", [toRope(n.level), 
+    result = dispF(
+        "<h$1><a class=\"toc-backref\" id=\"$2\" href=\"#$2_toc\">$3</a></h$1>", 
+        "\\rsth$4{$3}\\label{$2}$n", [toRope(n.level), 
         d.tocPart[length].refname, result, 
         toRope(chr(n.level - 1 + ord('A')) & "")])
   else: 
@@ -405,7 +406,7 @@ proc renderOverline(d: PDoc, n: PRstNode): PRope =
   else: 
     result = dispF("<h$1 id=\"$2\"><center>$3</center></h$1>", 
                    "\\rstov$4{$3}\\label{$2}$n", [toRope(n.level), 
-        toRope(rstnodeToRefname(n)), t, toRope(chr(n.level - 1 + ord('A')) & "")])
+        toRope(rstnodeToRefname(n)), t, toRope($chr(n.level - 1 + ord('A')))])
   
 proc renderRstToRst(d: PDoc, n: PRstNode): PRope
 proc renderRstSons(d: PDoc, n: PRstNode): PRope = 
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index 3ee8450b2..8dc629eb4 100755
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -25,13 +25,13 @@ type
   TSrcGen*{.final.} = object 
     indent*: int
     lineLen*: int
-    pos*: int                 # current position for iteration over the buffer
-    idx*: int                 # current token index for iteration over the buffer
+    pos*: int              # current position for iteration over the buffer
+    idx*: int              # current token index for iteration over the buffer
     tokens*: TRenderTokSeq
     buf*: string
-    pendingNL*: int           # negative if not active; else contains the
-                              # indentation value
-    comStack*: seq[PNode]     # comment stack
+    pendingNL*: int        # negative if not active; else contains the
+                           # indentation value
+    comStack*: seq[PNode]  # comment stack
     flags*: TRenderFlags
 
 
@@ -123,13 +123,13 @@ proc toNimChar(c: Char): string =
   
 proc makeNimString(s: string): string = 
   result = "\""
-  for i in countup(0, len(s) + 0 - 1): add(result, toNimChar(s[i]))
+  for i in countup(0, len(s)-1): add(result, toNimChar(s[i]))
   add(result, '\"')
 
 proc putComment(g: var TSrcGen, s: string) = 
   var i = 0
   var comIndent = 1
-  var isCode = (len(s) >= 2) and (s[0 + 1] != ' ')
+  var isCode = (len(s) >= 2) and (s[1] != ' ')
   var ind = g.lineLen
   var com = ""
   while true: 
@@ -166,9 +166,8 @@ proc putComment(g: var TSrcGen, s: string) =
       while s[j] > ' ': inc(j)
       if not isCode and (g.lineLen + (j - i) > MaxLineLen): 
         put(g, tkComment, com)
-        com = ""
         optNL(g, ind)
-        com = com & '#' & repeatChar(comIndent)
+        com = '#' & repeatChar(comIndent)
       while s[i] > ' ': 
         add(com, s[i])
         inc(i)
@@ -198,7 +197,7 @@ proc maxLineLength(s: string): int =
 
 proc putRawStr(g: var TSrcGen, kind: TTokType, s: string) = 
   var i = 0
-  var hi = len(s) + 0 - 1
+  var hi = len(s) - 1
   var str = ""
   while i <= hi: 
     case s[i]
@@ -219,7 +218,7 @@ proc putRawStr(g: var TSrcGen, kind: TTokType, s: string) =
   put(g, kind, str)
 
 proc containsNL(s: string): bool = 
-  for i in countup(0, len(s) + 0 - 1): 
+  for i in countup(0, len(s) - 1): 
     case s[i]
     of '\x0D', '\x0A': 
       return true
@@ -513,8 +512,7 @@ proc gstmts(g: var TSrcGen, n: PNode, c: TContext) =
     if rfLongMode in c.flags: dedent(g)
   
 proc gif(g: var TSrcGen, n: PNode) = 
-  var 
-    c: TContext
+  var c: TContext
   gsub(g, n.sons[0].sons[0])
   initContext(c)
   putWithSpace(g, tkColon, ":")
@@ -826,7 +824,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
   of nkAccQuoted:
     put(g, tkAccent, "`")
     if n.len > 0: gsub(g, n.sons[0])
-    for i in 0 .. <n.len:
+    for i in 1 .. <n.len:
       put(g, tkSpaces, Space)
       gsub(g, n.sons[i])
     put(g, tkAccent, "`")
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 17c4b7427..25272b37d 100755
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -34,10 +34,11 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   else:
     GlobalError(n.info, errExprXHasNoType, 
                 renderTree(result, {renderNoComments}))
+
+proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
+  result = symChoice(c, n, s)
   
 proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = 
-  if s.kind == skType and efAllowType notin flags:
-    GlobalError(n.info, errATypeHasNoValue)
   case s.kind
   of skProc, skMethod, skIterator, skConverter: 
     if not (sfProcVar in s.flags) and (s.typ.callConv == ccDefault) and
@@ -56,25 +57,29 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
     # It is clear that ``[]`` means two totally different things. Thus, we
     # copy `x`'s AST into each context, so that the type fixup phase can
     # deal with two different ``[]``.
-    #      
+    #
     markUsed(n, s)
-    if s.typ.kind in ConstAbstractTypes: 
+    if s.typ.kind in ConstAbstractTypes:
       result = copyTree(s.ast)
       result.typ = s.typ
       result.info = n.info
-    else: 
+    else:
       result = newSymNode(s, n.info)
   of skMacro: result = semMacroExpr(c, n, s)
   of skTemplate: result = semTemplateExpr(c, n, s)
-  of skVar: 
+  of skVar:
     markUsed(n, s)
     # if a proc accesses a global variable, it is not side effect free:
     if sfGlobal in s.flags: incl(c.p.owner.flags, sfSideEffect)
     result = newSymNode(s, n.info)
-  of skGenericParam: 
+  of skGenericParam:
     if s.ast == nil: InternalError(n.info, "no default for")
     result = semExpr(c, s.ast)
-  else: 
+  of skType:
+    if efAllowType notin flags: GlobalError(n.info, errATypeHasNoValue)
+    markUsed(n, s)
+    result = newSymNode(s, n.info)
+  else:
     markUsed(n, s)
     result = newSymNode(s, n.info)
 
@@ -1037,7 +1042,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     var s = qualifiedLookup(c, n.sons[0], {checkUndeclared})
     if s != nil and s.kind in {skProc, skMethod, skConverter, skIterator}: 
       # type parameters: partial generic specialization
-      n.sons[0] = semSym(c, n.sons[0], s, flags)
+      n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s)
       result = explicitGenericInstantiation(c, n, s)
     else: 
       result = semArrayAccess(c, n, flags)
diff --git a/config/nimrod.cfg b/config/nimrod.cfg
index 7e8d4b777..32849a68a 100755
--- a/config/nimrod.cfg
+++ b/config/nimrod.cfg
@@ -16,6 +16,7 @@ cc = gcc
 
 path="$lib/core"
 path="$lib/pure"
+path="$lib/pure/collections"
 path="$lib/impure"
 path="$lib/wrappers"
 path="$lib/wrappers/cairo"
diff --git a/doc/lib.txt b/doc/lib.txt
index 6e5a84cc1..23dd93aea 100755
--- a/doc/lib.txt
+++ b/doc/lib.txt
@@ -46,10 +46,13 @@ Core
 Collections and algorithms
 --------------------------
 
-* `hashtables <hashtables.html>`_
-  Nimrod hash table support.
+* `tables <tables.html>`_
+  Nimrod hash table support. Contains tables, ordered tables and count tables.
+* `sets <sets.html>`_
+  Nimrod hash and bit set support.
 * `lists <lists.html>`_
-  Nimrod linked list support.
+  Nimrod linked list support. Contains singly and doubly linked lists and
+  circular lists ("rings").
   
 
 String handling
diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim
index 930fd776e..10caac336 100755
--- a/lib/pure/collections/lists.nim
+++ b/lib/pure/collections/lists.nim
@@ -23,14 +23,19 @@ type
     next*: ref TSinglyLinkedNode[T]
     value*: T
   PSinglyLinkedNode*[T] = ref TSinglyLinkedNode[T]
+
+  TSinglyLinkedList*[T] {.pure, final.} = object ## a singly linked list
+    head*, tail*: PSinglyLinkedNode[T]
   
-  TRingNode[T] {.pure, 
-      final.} = object ## a node a ring list consists of
-    next*, prev*: ref TRingNode[T]
-    value*: T
-    
-  PRingNode*[T] = ref TRingNode[T]
+  TDoublyLinkedList*[T] {.pure, final.} = object ## a doubly linked list
+    head*, tail*: PDoublyLinkedNode[T]
 
+  TSinglyLinkedRing*[T] {.pure, final.} = object ## a singly linked ring
+    head*: PSinglyLinkedNode[T]
+  
+  TDoublyLinkedRing*[T] {.pure, final.} = object ## a doubly linked ring
+    head*: PDoublyLinkedNode[T]
+    
 proc newDoublyLinkedNode*[T](value: T): PDoublyLinkedNode[T] =
   ## creates a new doubly linked node with the given `value`.
   new(result)
@@ -41,124 +46,249 @@ proc newSinglyLinkedNode*[T](value: T): PSinglyLinkedNode[T] =
   new(result)
   result.value = value
 
-iterator items*[T](n: PDoublyLinkedNode[T]): T = 
-  ## yields every value of `x`.
-  var it = n
+template itemsListImpl() =
+  var it = L.head
   while it != nil:
     yield it.value
     it = it.next
 
-iterator items*[T](n: PSinglyLinkedNode[T]): T = 
-  ## yields every value of `x`.
-  var it = n
-  while it != nil:
-    yield it.value
-    it = it.next
+template itemsRingImpl() =
+  var it = L.head
+  if it != nil:
+    while true:
+      yield it.value
+      it = it.next
+      if it == L.head: break
 
-iterator nodes*[T](n: PSinglyLinkedNode[T]): PSinglyLinkedNode[T] = 
-  ## iterates over every node of `x`. Removing the current node from the
-  ## list during traversal is supported.
-  var it = n
+template nodesListImpl() =
+  var it = L.head
   while it != nil:
     var nxt = it.next
     yield it
     it = nxt
 
-iterator nodes*[T](n: PDoublyLinkedNode[T]): PDoublyLinkedNode[T] = 
+template nodesRingImpl() =
+  var it = L.head
+  if it != nil:
+    while true:
+      var nxt = it.next
+      yield it
+      it = nxt
+      if it == L.head: break
+
+template findImpl() =
+  for x in nodes(L):
+    if x.value == value: return x
+
+iterator items*[T](L: TDoublyLinkedList[T]): T = 
+  ## yields every value of `L`.
+  itemsListImpl()
+
+iterator items*[T](L: TSinglyLinkedList[T]): T = 
+  ## yields every value of `L`.
+  itemsListImpl()
+
+iterator items*[T](L: TSinglyLinkedRing[T]): T = 
+  ## yields every value of `L`.
+  itemsRingImpl()
+
+iterator items*[T](L: TDoublyLinkedRing[T]): T = 
+  ## yields every value of `L`.
+  itemsRingImpl()
+
+iterator nodes*[T](L: TSinglyLinkedList[T]): PSinglyLinkedNode[T] = 
   ## iterates over every node of `x`. Removing the current node from the
   ## list during traversal is supported.
-  var it = n
-  while it != nil:
-    var nxt = it.next
-    yield it
-    it = nxt
+  nodesListImpl()
 
-proc `$`*[list: PSinglyLinkedNode|PDoublyLinkedNode](n: list): string = 
-  ## turns a list into its string representation.
+iterator nodes*[T](L: TDoublyLinkedList[T]): PDoublyLinkedNode[T] = 
+  ## iterates over every node of `x`. Removing the current node from the
+  ## list during traversal is supported.
+  nodesListImpl()
+
+iterator nodes*[T](L: TSinglyLinkedRing[T]): PSinglyLinkedNode[T] = 
+  ## iterates over every node of `x`. Removing the current node from the
+  ## list during traversal is supported.
+  nodesRingImpl()
+
+iterator nodes*[T](L: TDoublyLinkedRing[T]): PDoublyLinkedNode[T] = 
+  ## iterates over every node of `x`. Removing the current node from the
+  ## list during traversal is supported.
+  nodesRingImpl()
+
+template dollarImpl() =
   result = "["
-  for x in nodes(n):
+  for x in nodes(L):
     if result.len > 1: result.add(", ")
     result.add($x.value)
   result.add("]")
 
-proc find*[list: PSinglyLinkedNode|PDoublyLinkedNode, T](
-           n: list, value: T): list = 
+proc `$`*[T](L: TSinglyLinkedList[T]): string = 
+  ## turns a list into its string representation.
+  dollarImpl()
+
+proc `$`*[T](L: TDoublyLinkedList[T]): string = 
+  ## turns a list into its string representation.
+  dollarImpl()
+
+proc `$`*[T](L: TSinglyLinkedRing[T]): string = 
+  ## turns a list into its string representation.
+  dollarImpl()
+
+proc `$`*[T](L: TDoublyLinkedRing[T]): string = 
+  ## turns a list into its string representation.
+  dollarImpl()
+
+proc find*[T](L: TSinglyLinkedList[T], value: T): PSinglyLinkedNode[T] = 
   ## searches in the list for a value. Returns nil if the value does not
   ## exist.
-  for x in nodes(n):
-    if x.value == value: return x
+  findImpl()
+
+proc find*[T](L: TDoublyLinkedList[T], value: T): PDoublyLinkedNode[T] = 
+  ## searches in the list for a value. Returns nil if the value does not
+  ## exist.
+  findImpl()
+
+proc find*[T](L: TSinglyLinkedRing[T], value: T): PSinglyLinkedNode[T] = 
+  ## searches in the list for a value. Returns nil if the value does not
+  ## exist.
+  findImpl()
 
-proc contains*[list: PSinglyLinkedNode|PDoublyLinkedNode, T](
-           n: list, value: T): list = 
+proc find*[T](L: TDoublyLinkedRing[T], value: T): PDoublyLinkedNode[T] = 
+  ## searches in the list for a value. Returns nil if the value does not
+  ## exist.
+  findImpl()
+
+proc contains*[T](L: TSinglyLinkedList[T], value: T): bool {.inline.} = 
   ## searches in the list for a value. Returns false if the value does not
   ## exist, true otherwise.
-  for x in nodes(n):
-    if x.value == value: return true
-
-proc prepend*[T](head: var PSinglyLinkedNode[T], 
-                 toAdd: PSinglyLinkedNode[T]) {.inline.} = 
-  ## prepends a node to `head`. Efficiency: O(1).
-  toAdd.next = head
-  head = toAdd
-
-proc prepend*[T](head: var PSinglyLinkedNode[T], x: T) {.inline.} = 
-  ## creates a new node with the value `x` and prepends that node to `head`.
-  ## Efficiency: O(1).
-  preprend(head, newSinglyLinkedNode(x))
-
-proc append*[T](head: var PSinglyLinkedNode[T], 
-                toAdd: PSinglyLinkedNode[T]) = 
-  ## appends a node to `head`. Efficiency: O(n).
-  if head == nil:
-    head = toAdd
-  else:
-    var it = head
-    while it.next != nil: it = it.next
-    it.next = toAdd
-
-proc append*[T](head: var PSinglyLinkedNode[T], x: T) {.inline.} = 
-  ## creates a new node with the value `x` and appends that node to `head`.
-  ## Efficiency: O(n).
-  append(head, newSinglyLinkedNode(x))
-
-
-proc prepend*[T](head: var PDoublyLinkedNode[T], 
-                 toAdd: PDoublyLinkedNode[T]) {.inline.} = 
-  ## prepends a node to `head`. Efficiency: O(1).
-  if head == nil:
-    head = toAdd
-    # head.prev stores the last node:
-    head.prev = toAdd
-  else:
-    toAdd.next = head
-    toAdd.prev = head.prev # copy pointer to last element
-    head.prev = toAdd
-    head = toAdd
-
-proc prepend*[T](head: var PDoublyLinkedNode[T], x: T) {.inline.} = 
-  ## creates a new node with the value `x` and prepends that node to `head`.
-  ## Efficiency: O(1).
-  preprend(head, newDoublyLinkedNode(x))
-
-proc append*[T](head: var PDoublyLinkedNode[T], 
-                toAdd: PDoublyLinkedNode[T]) {.inline.} = 
-  ## appends a node to `head`. Efficiency: O(1).
-  if head == nil:
-    head = toAdd
-    # head.prev stores the last node:
-    head.prev = toAdd
+  result = find(L, value) != nil
+
+proc contains*[T](L: TDoublyLinkedList[T], value: T): bool {.inline.} = 
+  ## searches in the list for a value. Returns false if the value does not
+  ## exist, true otherwise.
+  result = find(L, value) != nil
+
+proc contains*[T](L: TSinglyLinkedRing[T], value: T): bool {.inline.} = 
+  ## searches in the list for a value. Returns false if the value does not
+  ## exist, true otherwise.
+  result = find(L, value) != nil
+
+proc contains*[T](L: TDoublyLinkedRing[T], value: T): bool {.inline.} = 
+  ## searches in the list for a value. Returns false if the value does not
+  ## exist, true otherwise.
+  result = find(L, value) != nil
+
+proc prepend*[T](L: var TSinglyLinkedList[T], 
+                 n: PSinglyLinkedNode[T]) {.inline.} = 
+  ## prepends a node to `L`. Efficiency: O(1).
+  n.next = L.head
+  L.head = n
+
+proc prepend*[T](L: var TSinglyLinkedList[T], value: T) {.inline.} = 
+  ## prepends a node to `L`. Efficiency: O(1).
+  prepend(L, newSinglyLinkedNode(value))
+  
+proc append*[T](L: var TDoublyLinkedList[T], n: PDoublyLinkedNode[T]) = 
+  ## appends a node `n` to `L`. Efficiency: O(1).
+  n.next = nil
+  n.prev = L.tail
+  if L.tail != nil: 
+    assert(L.tail.next == nil)
+    L.tail.next = n
+  L.tail = n
+  if L.head == nil: L.head = n
+
+proc append*[T](L: var TDoublyLinkedList[T], value: T) = 
+  ## appends a value to `L`. Efficiency: O(1).
+  append(L, newDoublyLinkedNode(value))
+
+proc prepend*[T](L: var TDoublyLinkedList[T], n: PDoublyLinkedNode[T]) = 
+  ## prepends a node `n` to `L`. Efficiency: O(1).
+  n.prev = nil
+  n.next = L.head
+  if L.head != nil:
+    assert(L.head.prev == nil)
+    L.head.prev = n
+  L.head = n
+  if L.tail == nil: L.tail = n
+
+proc prepend*[T](L: var TDoublyLinkedList[T], value: T) = 
+  ## prepends a value to `L`. Efficiency: O(1).
+  prepend(L, newDoublyLinkedNode(value))
+  
+proc remove*[T](L: var TDoublyLinkedList[T], n: PDoublyLinkedNode[T]) = 
+  ## removes `n` from `L`. Efficiency: O(1).
+  if n == L.tail: L.tail = n.prev
+  if n == L.head: L.head = n.next
+  if n.next != nil: n.next.prev = n.prev
+  if n.prev != nil: n.prev.next = n.next
+
+
+proc prepend*[T](L: var TSinglyLinkedRing[T], n: PSinglyLinkedNode[T]) = 
+  ## prepends a node `n` to `L`. Efficiency: O(1).
+  if L.head != nil: 
+    n.next = L.head    
+    L.head.next = n
+  else: 
+    n.next = n
+  L.head = n
+
+proc prepend*[T](L: var TSinglyLinkedRing[T], value: T) = 
+  ## prepends a value to `L`. Efficiency: O(1).
+  prepend(L, newSinglyLinkedNode(value))
+
+proc append*[T](L: var TDoublyLinkedRing[T], n: PDoublyLinkedNode[T]) = 
+  ## appends a node `n` to `L`. Efficiency: O(1).
+  if L.tail != nil: 
+    L.tail.next = n
+    n.prev = L.tail
+    n.next = L.head
   else:
-    var last = head.prev
-    assert last.next == nil
-    last.next = toAdd
-    toAdd.prev = last
-    head.prev = toAdd # new last element
+    # both head and tail are nil:
+    assert L.head == nil
+    L.head = n
+    n.prev = n
+    n.next = n
+  L.tail = n
 
-proc append*[T](head: var PDoublyLinkedNode[T], x: T) {.inline.} = 
-  ## creates a new node with the value `x` and appends that node to `head`.
-  ## Efficiency: O(1).
-  append(head, newDoublyLinkedNode(x))
+proc append*[T](L: var TDoublyLinkedRing[T], value: T) = 
+  ## appends a value to `L`. Efficiency: O(1).
+  append(L, newDoublyLinkedNode(value))
 
+proc prepend*[T](L: var TDoublyLinkedRing[T], n: PDoublyLinkedNode[T]) = 
+  ## prepends a node `n` to `L`. Efficiency: O(1).
+  if L.head != nil: 
+    L.head.prev = n
+    n.prev = L.tail
+    n.next = L.head
+  else:
+    # both head and tail are nil:
+    assert L.tail == nil
+    L.tail = n
+    n.prev = n
+    n.next = n
+  L.head = n
 
+proc prepend*[T](L: var TDoublyLinkedRing[T], value: T) = 
+  ## prepends a value to `L`. Efficiency: O(1).
+  prepend(L, newDoublyLinkedNode(value))
+  
+proc remove*[T](L: var TDoublyLinkedRing[T], n: PDoublyLinkedNode[T]) = 
+  ## removes `n` from `L`. Efficiency: O(1).
+  if n == L.tail: 
+    if n == L.head:
+      # only element:
+      L.tail = nil
+      L.head = nil
+    else:
+      L.tail = n.prev
+  elif n == L.head: 
+    L.head = n.next
+  n.next.prev = n.prev
+  n.prev.next = n.next
+  # break cycles for the GC; not necessary, but might help:
+  n.next = nil
+  n.prev = nil
 
 
diff --git a/lib/pure/collections/hashtables.nim b/lib/pure/collections/tables.nim
index 4f4c4f5a0..f132051da 100644
--- a/lib/pure/collections/hashtables.nim
+++ b/lib/pure/collections/tables.nim
@@ -7,37 +7,45 @@
 #    distribution, for details about the copyright.
 #
 
-## The ``hashtables`` module implements an efficient hash table that is
+## The ``tables`` module implements an efficient hash table that is
 ## a mapping from keys to values.
+##
+## Note: The data types declared here have *value semantics*: This means that
+## ``=`` performs a copy of the hash table. If you are overly concerned with
+## efficiency and don't need this behaviour, you can define the symbol
+## ``shallowADT`` to compile a version that uses shallow copies instead.
 
 import
   os, hashes, math
 
+when defined(shallowADT):
+  {.pragma: myShallow, shallow.}
+else:
+  {.pragma: myShallow.}
+
 type
   TSlotEnum = enum seEmpty, seFilled, seDeleted
   TKeyValuePair[A, B] = tuple[slot: TSlotEnum, key: A, val: B]
   TKeyValuePairSeq[A, B] = seq[TKeyValuePair[A, B]]
-  THashTable[A, B] = object of TObject
+  TTable* {.final, myShallow.}[A, B] = object
     data: TKeyValuePairSeq[A, B]
     counter: int
 
-  PHashTable*[A, B] = ref THashTable[A, B] ## use this type to declare tables
-
-proc len*[A, B](t: THashTable[A, B]): int =
+proc len*[A, B](t: TTable[A, B]): int =
   ## returns the number of keys in `t`.
   result = t.counter
 
-iterator pairs*[A, B](t: THashTable[A, B]): tuple[key: A, val: B] =
+iterator pairs*[A, B](t: TTable[A, B]): tuple[key: A, val: B] =
   ## iterates over any (key, value) pair in the table `t`.
   for h in 0..high(t.data):
     if t.data[h].slot == seFilled: yield (t.data[h].key, t.data[h].val)
 
-iterator keys*[A, B](t: THashTable[A, B]): A =
+iterator keys*[A, B](t: TTable[A, B]): A =
   ## iterates over any key in the table `t`.
   for h in 0..high(t.data):
     if t.data[h].slot == seFilled: yield t.data[h].key
 
-iterator values*[A, B](t: THashTable[A, B]): B =
+iterator values*[A, B](t: TTable[A, B]): B =
   ## iterates over any value in the table `t`.
   for h in 0..high(t.data):
     if t.data[h].slot == seFilled: yield t.data[h].val
@@ -68,10 +76,10 @@ template rawInsertImpl() =
   data[h].val = val
   data[h].slot = seFilled
 
-proc RawGet[A, B](t: THashTable[A, B], key: A): int =
+proc RawGet[A, B](t: TTable[A, B], key: A): int =
   rawGetImpl()
 
-proc `[]`*[A, B](t: THashTable[A, B], key: A): B =
+proc `[]`*[A, B](t: TTable[A, B], key: A): B =
   ## retrieves the value at ``t[key]``. If `key` is not in `t`,
   ## default empty value for the type `B` is returned
   ## and no exception is raised. One can check with ``hasKey`` whether the key
@@ -79,15 +87,15 @@ proc `[]`*[A, B](t: THashTable[A, B], key: A): B =
   var index = RawGet(t, key)
   if index >= 0: result = t.data[index].val
 
-proc hasKey*[A, B](t: THashTable[A, B], key: A): bool =
+proc hasKey*[A, B](t: TTable[A, B], key: A): bool =
   ## returns true iff `key` is in the table `t`.
   result = rawGet(t, key) >= 0
 
-proc RawInsert[A, B](t: var THashTable[A, B], data: var TKeyValuePairSeq[A, B],
+proc RawInsert[A, B](t: var TTable[A, B], data: var TKeyValuePairSeq[A, B],
                      key: A, val: B) =
   rawInsertImpl()
 
-proc Enlarge[A, B](t: var THashTable[A, B]) =
+proc Enlarge[A, B](t: var TTable[A, B]) =
   var n: TKeyValuePairSeq[A, B]
   newSeq(n, len(t.data) * growthFactor)
   for i in countup(0, high(t.data)):
@@ -103,24 +111,30 @@ template PutImpl() =
     RawInsert(t, t.data, key, val)
     inc(t.counter)
 
-proc `[]=`*[A, B](t: var THashTable[A, B], key: A, val: B) =
+proc `[]=`*[A, B](t: var TTable[A, B], key: A, val: B) =
   ## puts a (key, value)-pair into `t`.
   putImpl()
 
-proc del*[A, B](t: var THashTable[A, B], key: A) =
+proc del*[A, B](t: var TTable[A, B], key: A) =
   ## deletes `key` from hash table `t`.
   var index = RawGet(t, key)
   if index >= 0:
     t.data[index].slot = seDeleted
     dec(t.counter)
 
-proc initHashTable*[A, B](initialSize = 64): THashTable[A, B] =
-  ## creates a new string table that is empty. `initialSize` needs to be
+proc initTable*[A, B](initialSize=64): TTable[A, B] =
+  ## creates a new hash table table that is empty. `initialSize` needs to be
   ## a power of two.
   assert isPowerOfTwo(initialSize)
   result.counter = 0
   newSeq(result.data, initialSize)
 
+proc toTable*[A, B](pairs: openarray[tuple[key: A, 
+                    val: B]]): TTable[A, B] =
+  ## creates a new hash table that contains the given `pairs`.
+  result = initTable[A](nextPowerOfTwo(pairs.len+10))
+  for key, val in items(pairs): result[key] = val
+
 template dollarImpl(): stmt =
   if t.len == 0:
     result = "{:}"
@@ -133,7 +147,7 @@ template dollarImpl(): stmt =
       result.add($val)
     result.add("}")
 
-proc `$`*[A, B](t: THashTable[A, B]): string =
+proc `$`*[A, B](t: TTable[A, B]): string =
   ## The `$` operator for string tables.
   dollarImpl()
 
@@ -143,11 +157,12 @@ type
   TOrderedKeyValuePair[A, B] = tuple[
     slot: TSlotEnum, next: int, key: A, val: B]
   TOrderedKeyValuePairSeq[A, B] = seq[TOrderedKeyValuePair[A, B]]
-  TOrderedHashTable*[A, B] {.final.} = object
+  TOrderedTable* {.
+      final, myShallow.}[A, B] = object ## table that remembers insertion order
     data: TOrderedKeyValuePairSeq[A, B]
     counter, first, last: int
 
-proc len*[A, B](t: TOrderedHashTable[A, B]): int {.inline.} =
+proc len*[A, B](t: TOrderedTable[A, B]): int {.inline.} =
   ## returns the number of keys in `t`.
   result = t.counter
 
@@ -158,26 +173,26 @@ template forAllOrderedPairs(yieldStmt: stmt) =
     if t.data[h].slot == seFilled: yieldStmt
     i = nxt
 
-iterator pairs*[A, B](t: TOrderedHashTable[A, B]): tuple[key: A, val: B] =
+iterator pairs*[A, B](t: TOrderedTable[A, B]): tuple[key: A, val: B] =
   ## iterates over any (key, value) pair in the table `t` in insertion
   ## order.
   forAllOrderedPairs:
     yield (t.data[h].key, t.data[h].val)
 
-iterator keys*[A, B](t: TOrderedHashTable[A, B]): A =
+iterator keys*[A, B](t: TOrderedTable[A, B]): A =
   ## iterates over any key in the table `t` in insertion order.
   forAllOrderedPairs:
     yield t.data[h].key
 
-iterator values*[A, B](t: TOrderedHashTable[A, B]): B =
+iterator values*[A, B](t: TOrderedTable[A, B]): B =
   ## iterates over any value in the table `t` in insertion order.
   forAllOrderedPairs:
     yield t.data[h].val
 
-proc RawGet[A, B](t: TOrderedHashTable[A, B], key: A): int =
+proc RawGet[A, B](t: TOrderedTable[A, B], key: A): int =
   rawGetImpl()
 
-proc `[]`*[A, B](t: TOrderedHashTable[A, B], key: A): B =
+proc `[]`*[A, B](t: TOrderedTable[A, B], key: A): B =
   ## retrieves the value at ``t[key]``. If `key` is not in `t`,
   ## default empty value for the type `B` is returned
   ## and no exception is raised. One can check with ``hasKey`` whether the key
@@ -185,11 +200,11 @@ proc `[]`*[A, B](t: TOrderedHashTable[A, B], key: A): B =
   var index = RawGet(t, key)
   if index >= 0: result = t.data[index].val
 
-proc hasKey*[A, B](t: TOrderedHashTable[A, B], key: A): bool =
+proc hasKey*[A, B](t: TOrderedTable[A, B], key: A): bool =
   ## returns true iff `key` is in the table `t`.
   result = rawGet(t, key) >= 0
 
-proc RawInsert[A, B](t: TOrderedHashTable[A, B], 
+proc RawInsert[A, B](t: TOrderedTable[A, B], 
                      data: var TOrderedKeyValuePairSeq[A, B],
                      key: A, val: B) =
   rawInsertImpl()
@@ -198,39 +213,19 @@ proc RawInsert[A, B](t: TOrderedHashTable[A, B],
   if last >= 0: data[last].next = h
   lastEntry = h
 
-proc Enlarge[A, B](t: TOrderedHashTable[A, B]) =
+proc Enlarge[A, B](t: TOrderedTable[A, B]) =
   var n: TOrderedKeyValuePairSeq[A, B]
   newSeq(n, len(t.data) * growthFactor)
   forAllOrderedPairs:
     RawInsert(t, n, t.data[h].key, t.data[h].val)
   swap(t.data, n)
 
-proc `[]=`*[A, B](t: TOrderedHashTable[A, B], key: A, val: B) =
+proc `[]=`*[A, B](t: TOrderedTable[A, B], key: A, val: B) =
   ## puts a (key, value)-pair into `t`.
-  var index = RawGet(t, key)
-  if index >= 0:
-    t.data[index].val = val
-  else:
-    if mustRehash(len(t.data), t.counter): Enlarge(t)
-    RawInsert(t, t.data, key, val)
-    inc(t.counter)
-
-proc del*[A, B](t: TOrderedHashTable[A, B], key: A) =
-  ## deletes `key` from hash table `t`. Warning: It's inefficient for ordered
-  ## tables: O(n).
-  var index = RawGet(t, key)
-  if index >= 0:
-    var i = t.first
-    while i >= 0:
-      var nxt = t.data[i].next
-      if nxt == index: XXX
-      i = nxt
-    
-    t.data[index].slot = seDeleted
-    dec(t.counter)
+  putImpl()
 
-proc initHashTable*[A, B](initialSize = 64): TOrderedHashTable[A, B] =
-  ## creates a new string table that is empty. `initialSize` needs to be
+proc initOrderedTable*[A, B](initialSize=64): TOrderedTable[A, B] =
+  ## creates a new ordered hash table that is empty. `initialSize` needs to be
   ## a power of two.
   assert isPowerOfTwo(initialSize)
   result.counter = 0
@@ -238,17 +233,21 @@ proc initHashTable*[A, B](initialSize = 64): TOrderedHashTable[A, B] =
   result.last = -1
   newSeq(result.data, initialSize)
 
-proc `$`*[A, B](t: TOrderedHashTable[A, B]): string =
+proc toOrderedTable*[A, B](pairs: openarray[tuple[key: A, 
+                           val: B]]): TOrderedTable[A, B] =
+  ## creates a new ordered hash table that contains the given `pairs`.
+  result = initOrderedTable[A, B](nextPowerOfTwo(pairs.len+10))
+  for key, val in items(pairs): result[key] = val
+
+proc `$`*[A, B](t: TOrderedTable[A, B]): string =
   ## The `$` operator for hash tables.
   dollarImpl()
 
 # ------------------------------ count tables -------------------------------
 
-const
-  deletedCount = -1
-
 type
-  TCountTable*[A] {.final.} = object
+  TCountTable* {.final, myShallow.}[
+      A] = object ## table that counts the number of each key
     data: seq[tuple[key: A, val: int]]
     counter: int
 
@@ -259,30 +258,28 @@ proc len*[A](t: TCountTable[A]): int =
 iterator pairs*[A](t: TCountTable[A]): tuple[key: A, val: int] =
   ## iterates over any (key, value) pair in the table `t`.
   for h in 0..high(t.data):
-    if t.data[h].slot == seFilled: yield (t.data[h].key, t.data[h].val)
+    if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val)
 
 iterator keys*[A](t: TCountTable[A]): A =
   ## iterates over any key in the table `t`.
   for h in 0..high(t.data):
-    if t.data[h].slot == seFilled: yield t.data[h].key
+    if t.data[h].val != 0: yield t.data[h].key
 
 iterator values*[A](t: TCountTable[A]): int =
   ## iterates over any value in the table `t`.
   for h in 0..high(t.data):
-    if t.data[h].slot == seFilled: yield t.data[h].val
+    if t.data[h].val != 0: yield t.data[h].val
 
 proc RawGet[A](t: TCountTable[A], key: A): int =
   var h: THash = hash(key) and high(t.data) # start with real hash value
-  while t.data[h].slot != seEmpty:
-    if t.data[h].key == key and t.data[h].slot == seFilled:
-      return h
+  while t.data[h].val != 0:
+    if t.data[h].key == key: return h
     h = nextTry(h, high(t.data))
   result = -1
 
-proc `[]`*[A](t: TCountTable[A], key: A): B =
+proc `[]`*[A](t: TCountTable[A], key: A): int =
   ## retrieves the value at ``t[key]``. If `key` is not in `t`,
-  ## default empty value for the type `B` is returned
-  ## and no exception is raised. One can check with ``hasKey`` whether the key
+  ## 0 is returned. One can check with ``hasKey`` whether the key
   ## exists.
   var index = RawGet(t, key)
   if index >= 0: result = t.data[index].val
@@ -291,62 +288,92 @@ proc hasKey*[A](t: TCountTable[A], key: A): bool =
   ## returns true iff `key` is in the table `t`.
   result = rawGet(t, key) >= 0
 
-proc RawInsert[A](t: TCountTable[A], data: var TKeyValuePairSeq[A, B],
-                     key: A, val: int) =
+proc RawInsert[A](t: TCountTable[A], data: var seq[tuple[key: A, val: int]],
+                  key: A, val: int) =
   var h: THash = hash(key) and high(data)
-  while data[h].slot == seFilled:
-    h = nextTry(h, high(data))
+  while data[h].val != 0: h = nextTry(h, high(data))
   data[h].key = key
   data[h].val = val
-  data[h].slot = seFilled
 
 proc Enlarge[A](t: TCountTable[A]) =
-  var n: TKeyValuePairSeq[A, B]
+  var n: seq[tuple[key: A, val: int]]
   newSeq(n, len(t.data) * growthFactor)
   for i in countup(0, high(t.data)):
-    if t.data[i].slot == seFilled: RawInsert(t, n, t.data[i].key, t.data[i].val)
+    if t.data[i].val != 0: RawInsert(t, n, t.data[i].key, t.data[i].val)
   swap(t.data, n)
 
 proc `[]=`*[A](t: TCountTable[A], key: A, val: int) =
-  ## puts a (key, value)-pair into `t`.
-  var index = RawGet(t, key)
-  if index >= 0:
-    t.data[index].val = val
-  else:
-    if mustRehash(len(t.data), t.counter): Enlarge(t)
-    RawInsert(t, t.data, key, val)
-    inc(t.counter)
+  ## puts a (key, value)-pair into `t`. `val` has to be positive.
+  assert val > 0
+  PutImpl()
 
-proc del*[A](t: TCountTable[A], key: A) =
-  ## deletes `key` from hash table `t`.
-  var index = RawGet(t, key)
-  if index >= 0:
-    t.data[index].slot = seDeleted
-
-proc newHashTable*[A, B](initialSize = 64): PHashTable[A, B] =
-  ## creates a new string table that is empty. `initialSize` needs to be
+proc initCountTable*[A](initialSize=64): TCountTable[A] =
+  ## creates a new count table that is empty. `initialSize` needs to be
   ## a power of two.
   assert isPowerOfTwo(initialSize)
-  new(result)
   result.counter = 0
   newSeq(result.data, initialSize)
 
+proc toCountTable*[A](keys: openArray[A]): TCountTable[A] =
+  ## creates a new count table with every key in `keys` having a count of 1.
+  result = initCountTable[A](nextPowerOfTwo(keys.len+10))
+  for key in items(keys): result[key] = 1
+
 proc `$`*[A](t: TCountTable[A]): string =
-  ## The `$` operator for string tables.
-  if t.len == 0:
-    result = "{:}"
+  ## The `$` operator for count tables.
+  dollarImpl()
+
+proc inc*[A](t: TCountTable[A], key: A, val = 1) = 
+  ## increments `t[key]` by `val`.
+  var index = RawGet(t, key)
+  if index >= 0:
+    inc(t.data[index].val, val)
   else:
-    result = "{"
-    for key, val in pairs(t):
-      if result.len > 1: result.add(", ")
-      result.add($key)
-      result.add(": ")
-      result.add($val)
-    result.add("}")
+    if mustRehash(len(t.data), t.counter): Enlarge(t)
+    RawInsert(t, t.data, key, val)
+    inc(t.counter)
 
+proc Smallest*[A](t: TCountTable[A]): tuple[key: A, val: int] =
+  ## returns the largest (key,val)-pair. Efficiency: O(n)
+  assert t.len > 0
+  var minIdx = 0
+  for h in 1..high(t.data):
+    if t.data[h].val > 0 and t.data[minIdx].val > t.data[h].val: minIdx = h
+  result.key = t.data[minIdx].key
+  result.val = t.data[minIdx].val
+
+proc Largest*[A](t: TCountTable[A]): tuple[key: A, val: int] =
+  ## returns the (key,val)-pair with the largest `val`. Efficiency: O(n)
+  assert t.len > 0
+  var maxIdx = 0
+  for h in 1..high(t.data):
+    if t.data[maxIdx].val < t.data[h].val: maxIdx = h
+  result.key = t.data[maxIdx].key
+  result.val = t.data[maxIdx].val
+
+proc sort*[A](t: var TCountTable[A]) = 
+  ## sorts the count table so that the entry with the highest counter comes 
+  ## first. This is destructive! You must not modify `t` afterwards!
+  ## You can use the iterators `pairs`,  `keys`, and `values` to iterate over
+  ## `t` in the sorted order.
+
+  # we use shellsort here; fast enough and simple
+  var h = 1
+  while true:
+    h = 3 * h + 1
+    if h >= t.data.high: break
+  while true: 
+    h = h div 3
+    for i in countup(h, t.data.high):
+      var j = i
+      while t.data[j-h].val < t.data[j].val:
+        swap(t.data[j], t.data[j-h])
+        j = j-h
+        if j < h: break
+    if h == 1: break
 
 when isMainModule:
-  var table = newHashTable[string, float]()
+  var table = initHashTable[string, float]()
   table["test"] = 1.2345
   table["111"] = 1.000043
   echo table
diff --git a/tests/accept/run/tlists.nim b/tests/accept/run/tlists.nim
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/accept/run/tlists.nim
diff --git a/tests/accept/run/ttables.nim b/tests/accept/run/ttables.nim
new file mode 100644
index 000000000..c7033bf70
--- /dev/null
+++ b/tests/accept/run/ttables.nim
@@ -0,0 +1,18 @@
+discard """
+  output: '''true'''
+"""
+
+import hashes, tables
+
+var t = initTable[tuple[x, y: int], string]()
+t[(0,0)] = "00"
+t[(1,0)] = "10"
+t[(0,1)] = "01"
+t[(1,1)] = "11"
+
+for x in 0..1:
+  for y in 0..1:
+    assert t[(x,y)] == $x & $y
+
+echo "true"
+
diff --git a/todo.txt b/todo.txt
index d57f86f5d..d060a17e9 100755
--- a/todo.txt
+++ b/todo.txt
@@ -5,9 +5,6 @@
 
 * add --deadlock_prevention:on|off switch? timeout for locks?
   
-* implicit ref/ptr->var conversion; the compiler may store an object
-  implicitly on the heap for write barrier efficiency! (Especially
-  important for multi-threading!)
 
 
 High priority (version 0.9.0)
@@ -47,6 +44,8 @@ To implement
 
 Low priority
 ------------
+- implicit ref/ptr->var conversion; the compiler may store an object
+  implicitly on the heap for write barrier efficiency
 - resizing of strings/sequences could take into account the memory that
   is allocated
 - typeAllowed() for parameters...
diff --git a/web/index.txt b/web/index.txt
index 09e18c5aa..1add676fd 100755
--- a/web/index.txt
+++ b/web/index.txt
@@ -103,21 +103,16 @@ Roadmap to 1.0
 ==============
 
 Version 0.8.x
-  * general expressions as generic parameters
+  * threading
 
 Version 0.9.0
   * closures and anonymous procs
-  * provide an API for object serialization
-
-Version 1.0.0
-  * stress testing with a better test suite
-  * fix symbol files to make the compiler incremental
+  * recursive iterators/coroutines
 
 
 Planned features beyond 1.0
 ===========================
 
-* Threading with a transactional memory modell (the type system may be 
-  enhanced to support extensive compile-time checks for this).
-* Recursive iterators/coroutines.
 * Other code generators: LLVM, EcmaScript.
+* Symbol files to make the compiler incremental.
+