summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xcompiler/ast.nim7
-rwxr-xr-xcompiler/evals.nim35
-rwxr-xr-xcompiler/msgs.nim17
-rwxr-xr-xcompiler/parser.nim8
-rwxr-xr-xcompiler/sem.nim11
-rwxr-xr-xcompiler/semexprs.nim138
-rwxr-xr-xcompiler/semfold.nim11
-rwxr-xr-xcompiler/semgnrc.nim23
-rwxr-xr-xcompiler/seminst.nim11
-rwxr-xr-xcompiler/semtypes.nim52
-rwxr-xr-xcompiler/semtypinst.nim3
-rwxr-xr-xcompiler/sigmatch.nim32
-rwxr-xr-xcompiler/types.nim37
-rwxr-xr-xdoc/manual.txt68
-rwxr-xr-xlib/core/macros.nim666
-rwxr-xr-xlib/pure/os.nim2
-rwxr-xr-xlib/system.nim27
-rw-r--r--tests/compile/tisop.nim42
-rw-r--r--tests/compile/tmacrotypes.nim12
-rw-r--r--tests/compile/ttypeclasses.nim4
-rw-r--r--tests/reject/tcaseexpr1.nim30
-rw-r--r--tests/reject/tenummix.nim2
-rwxr-xr-xtests/run/tcasestm.nim24
-rw-r--r--tests/run/trettypeinference.nim33
-rwxr-xr-xweb/news.txt33
25 files changed, 829 insertions, 499 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 814784029..aa94644aa 100755
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -350,6 +350,8 @@ type
                       # pass of semProcTypeNode performed after instantiation.
                       # this won't be needed if we don't perform this redundant
                       # second pass (stay tuned).
+    tfRetType         # marks return types in proc (used to detect type classes 
+                      # used as return types for return type inference)
     tfAll,            # type class requires all constraints to be met (default)
     tfAny,            # type class requires any constraint to be met
     tfCapturesEnv,    # whether proc really captures some environment
@@ -779,6 +781,11 @@ proc add*(father, son: PNode) =
 proc `[]`*(n: PNode, i: int): PNode {.inline.} =
   result = n.sons[i]
 
+# son access operators with support for negative indices
+template `{}`*(n: PNode, i: int): expr = n[i -| n]
+template `{}=`*(n: PNode, i: int, s: PNode): stmt =
+  n.sons[i -| n] = s
+  
 var emptyNode* = newNode(nkEmpty)
 # There is a single empty node that is shared! Do not overwrite it!
 
diff --git a/compiler/evals.nim b/compiler/evals.nim
index 9c73a6b78..226415815 100755
--- a/compiler/evals.nim
+++ b/compiler/evals.nim
@@ -41,6 +41,7 @@ type
     callsite: PNode           # for 'callsite' magic
     mode*: TEvalMode
     globals*: TIdNodeTable    # state of global vars
+    getType*: proc(n: PNode): PNode
   
   PEvalContext* = ref TEvalContext
 
@@ -521,7 +522,7 @@ proc evalSym(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
   else: result = nil
   if result == nil or {sfImportc, sfForward} * s.flags != {}:
     result = raiseCannotEval(c, n.info)
-  
+
 proc evalIncDec(c: PEvalContext, n: PNode, sign: biggestInt): PNode = 
   result = evalAux(c, n.sons[1], {efLValue})
   if isSpecial(result): return 
@@ -875,6 +876,27 @@ proc evalTypeTrait*(n: PNode, context: PSym): PNode =
   else:
     internalAssert false
 
+proc evalIsOp*(n: PNode): PNode =
+  InternalAssert n.sonsLen == 3 and
+    n[1].kind == nkSym and n[1].sym.kind == skType and
+    n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
+  
+  let t1 = n[1].sym.typ
+
+  if n[2].kind in {nkStrLit..nkTripleStrLit}:
+    case n[2].strVal.normalize
+    of "closure":
+      let t = skipTypes(t1, abstractRange)
+      result = newIntNode(nkIntLit, ord(t.kind == tyProc and
+                                        t.callConv == ccClosure))
+  else:
+    let t2 = n[2].typ
+    var match = if t2.kind == tyTypeClass: matchTypeClass(t2, t1)
+                else: sameType(t1, t2)
+    result = newIntNode(nkIntLit, ord(match))
+
+  result.typ = n.typ
+
 proc expectString(n: PNode) =
   if n.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
     GlobalError(n.info, errStringLiteralExpected)
@@ -968,6 +990,9 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
   of mTypeTrait:
     n.sons[1] = evalAux(c, n.sons[1], {})
     result = evalTypeTrait(n, c.module)
+  of mIs:
+    n.sons[1] = evalAux(c, n.sons[1], {})
+    result = evalIsOp(n)
   of mSlurp: result = evalSlurp(evalAux(c, n.sons[1], {}), c.module)
   of mStaticExec:
     let cmd = evalAux(c, n.sons[1], {})
@@ -1067,7 +1092,10 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
     result = evalAux(c, n.sons[1], {})
     if isSpecial(result): return 
     if result.kind != nkIdent: stackTrace(c, n, errFieldXNotFound, "ident")
-  of mNGetType: result = evalAux(c, n.sons[1], {})
+  of mNGetType:
+    var ast = evalAux(c, n.sons[1], {})
+    InternalAssert c.getType != nil
+    result = c.getType(ast)
   of mNStrVal: 
     result = evalAux(c, n.sons[1], {})
     if isSpecial(result): return 
@@ -1128,7 +1156,8 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
     var a = result
     result = evalAux(c, n.sons[2], {efLValue})
     if isSpecial(result): return 
-    a.typ = result.typ        # XXX: exception handling?
+    InternalAssert result.kind == nkSym and result.sym.kind == skType
+    a.typ = result.sym.typ
     result = emptyNode
   of mNSetStrVal:
     result = evalAux(c, n.sons[1], {efLValue})
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 0ad79f597..c793c1c68 100755
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -575,19 +575,22 @@ proc inCheckpoint*(current: TLineInfo): TCheckPointResult =
 type
   TErrorHandling = enum doNothing, doAbort, doRaise
 
-proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) = 
-  if msg == errInternal: 
-    assert(false)             # we want a stack trace here
+proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) =
+  template maybeTrace =
+    if defined(debug) or gVerbosity >= 3:
+      writeStackTrace()
+
+  if msg == errInternal:
+    writeStackTrace() # we always want a stack trace here
   if msg >= fatalMin and msg <= fatalMax: 
-    if gVerbosity >= 3: assert(false)
+    maybeTrace()
     quit(1)
   if msg >= errMin and msg <= errMax: 
-    if gVerbosity >= 3: assert(false)
+    maybeTrace()
     inc(gErrorCounter)
     options.gExitcode = 1'i8
     if gErrorCounter >= gErrorMax or eh == doAbort: 
-      if gVerbosity >= 3: assert(false)
-      quit(1)                 # one error stops the compiler
+      quit(1)                        # one error stops the compiler
     elif eh == doRaise:
       raiseRecoverableError(s)
   
diff --git a/compiler/parser.nim b/compiler/parser.nim
index cdbe42c7e..7612980c5 100755
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -743,7 +743,7 @@ proc isExprStart(p: TParser): bool =
   case p.tok.tokType
   of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, tkProc, tkBind, 
      tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr, 
-     tkTuple, tkType, tkWhen:
+     tkTuple, tkType, tkWhen, tkCase:
     result = true
   else: result = false
   
@@ -763,9 +763,9 @@ proc parseExpr(p: var TParser): PNode =
   case p.tok.tokType:
   of tkIf: result = parseIfExpr(p, nkIfExpr)
   of tkWhen: result = parseIfExpr(p, nkWhenExpr)
+  of tkCase: result = parseCase(p)
   else: result = lowestExpr(p)
   # XXX needs proper support:
-  #of tkCase: result = parseCase(p)
   #of tkTry: result = parseTry(p)
 
 proc primary(p: var TParser, skipSuffix = false): PNode = 
@@ -1044,9 +1044,9 @@ proc parseCase(p: var TParser): PNode =
     if b.kind == nkElse: break
   
   if wasIndented:
-    eat(p, tkDed)
+    if p.tok.tokType != tkEof: eat(p, tkDed)
     popInd(p.lex)
-
+    
 proc parseTry(p: var TParser): PNode = 
   result = newNodeP(nkTryStmt, p)
   getTok(p)
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 397581f63..0a6159dfa 100755
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -43,6 +43,7 @@ proc addParams(c: PContext, n: PNode, kind: TSymKind)
 proc addResult(c: PContext, t: PType, info: TLineInfo, owner: TSymKind)
 proc addResultNode(c: PContext, n: PNode)
 proc instGenericContainer(c: PContext, n: PNode, header: PType): PType
+proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
 
 proc typeMismatch(n: PNode, formal, actual: PType) = 
   if formal.kind != tyError and actual.kind != tyError: 
@@ -156,8 +157,18 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
   markUsed(n, sym)
   if sym == c.p.owner:
     GlobalError(n.info, errRecursiveDependencyX, sym.name.s)
+
   if c.evalContext == nil:
     c.evalContext = newEvalContext(c.module, "", emStatic)
+    c.evalContext.getType = proc (n: PNode): PNode =
+      var e = tryExpr(c, n)
+      if e == nil:
+        result = symNodeFromType(c, errorType(c), n.info)
+      elif e.typ == nil:
+        result = newSymNode(getSysSym"void")
+      else:
+        result = symNodeFromType(c, e.typ, n.info)
+
   result = evalMacroCall(c.evalContext, n, nOrig, sym)
   if semCheck: result = semAfterMacroCall(c, result, sym)
 
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 7c147e778..e9dc5a8e9 100755
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -281,18 +281,29 @@ proc semOf(c: PContext, n: PNode): PNode =
   n.typ = getSysType(tyBool)
   result = n
 
-proc semIs(c: PContext, n: PNode): PNode = 
-  if sonsLen(n) == 3:
-    n.typ = getSysType(tyBool)
-    let a = semTypeNode(c, n[1], nil)
-    n.sons[1] = newNodeIT(nkType, n[1].info, a)
-    if n[2].kind notin {nkStrLit..nkTripleStrLit}:
-      let b = semTypeNode(c, n[2], nil)
-      n.sons[2] = newNodeIT(nkType, n[2].info, b)
-  else:
+proc semIs(c: PContext, n: PNode): PNode =
+  if sonsLen(n) != 3:
     LocalError(n.info, errXExpectsTwoArguments, "is")
+
   result = n
+  n.typ = getSysType(tyBool)
+  
+  n.sons[1] = semExprWithType(c, n[1])
+  if n[1].typ.kind != tyTypeDesc:
+    LocalError(n[0].info, errTypeExpected)
+
+  if n[2].kind notin {nkStrLit..nkTripleStrLit}:
+    let t2 = semTypeNode(c, n[2], nil)
+    n.sons[2] = newNodeIT(nkType, n[2].info, t2)
 
+  if n[1].typ.sonsLen == 0:
+    # this is a typedesc variable, leave for evals
+    return
+  else:
+    let t1 = n[1].typ.sons[0]
+    # BUGFIX: don't evaluate this too early: ``T is void``
+    if not containsGenericType(t1): result = evalIsOp(n)
+  
 proc semOpAux(c: PContext, n: PNode, tailToExclude = 1) =
   for i in countup(1, sonsLen(n) - tailToExclude):
     var a = n.sons[i]
@@ -1048,8 +1059,20 @@ proc semAsgn(c: PContext, n: PNode): PNode =
     localError(a.info, errXCannotBeAssignedTo, 
                renderTree(a, {renderNoComments}))
   else:
-    n.sons[1] = semExprWithType(c, n.sons[1])
-    n.sons[1] = fitNode(c, le, n.sons[1])
+    var 
+      rhs = semExprWithType(c, n.sons[1])
+      lhs = n.sons[0]
+    if lhs.kind == nkSym and lhs.sym.kind == skResult and
+       lhs.sym.typ.kind == tyGenericParam:
+      if matchTypeClass(lhs.typ, rhs.typ):
+        InternalAssert c.p.resultSym != nil
+        lhs.typ = rhs.typ
+        c.p.resultSym.typ = rhs.typ
+        c.p.owner.typ.sons[0] = rhs.typ
+      else:
+        typeMismatch(n, lhs.typ, rhs.typ)
+
+    n.sons[1] = fitNode(c, le, rhs)
     fixAbstractType(c, n)
     asgnToResultVar(c, n, n.sons[0], n.sons[1])
   result = n
@@ -1214,12 +1237,7 @@ proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym,
   else:
     result = semDirectOp(c, n, flags)
 
-proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode =
-  # we replace this node by a 'true' or 'false' node:
-  if sonsLen(n) != 2: return semDirectOp(c, n, flags)
-  result = newIntNode(nkIntLit, 0)
-  result.info = n.info
-  result.typ = getSysType(tyBool)
+proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   # watch out, hacks ahead:
   let oldErrorCount = msgs.gErrorCounter
   let oldErrorMax = msgs.gErrorMax
@@ -1241,8 +1259,8 @@ proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode =
   let oldProcCon = c.p
   c.generics = newGenericsCache()
   try:
-    discard semExpr(c, n.sons[1])
-    result.intVal = ord(msgs.gErrorCounter == oldErrorCount)
+    result = semExpr(c, n, flags)
+    if msgs.gErrorCounter != oldErrorCount: result = nil
   except ERecoverableError:
     nil
   # undo symbol table changes (as far as it's possible):
@@ -1259,6 +1277,14 @@ proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode =
   msgs.gErrorCounter = oldErrorCount
   msgs.gErrorMax = oldErrorMax
 
+proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode =
+  # we replace this node by a 'true' or 'false' node:
+  if sonsLen(n) != 2: return semDirectOp(c, n, flags)
+  
+  result = newIntNode(nkIntLit, ord(tryExpr(c, n, flags) != nil))
+  result.info = n.info
+  result.typ = getSysType(tyBool)
+
 proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode =
   if sonsLen(n) == 3:
     # XXX ugh this is really a hack: shallowCopy() can be overloaded only
@@ -1352,19 +1378,28 @@ proc semSetConstr(c: PContext, n: PNode): PNode =
         m = fitNode(c, typ, n.sons[i])
       addSon(result, m)
 
-proc semTableConstr(c: PContext, n: PNode): PNode = 
-  # we simply transform ``{key: value, key2: value}`` to 
-  # ``[(key, value), (key2, value2)]``
+proc semTableConstr(c: PContext, n: PNode): PNode =
+  # we simply transform ``{key: value, key2, key3: value}`` to 
+  # ``[(key, value), (key2, value2), (key3, value2)]``
   result = newNodeI(nkBracket, n.info)
+  var lastKey = 0
   for i in 0..n.len-1:
     var x = n.sons[i]
     if x.kind == nkExprColonExpr and sonsLen(x) == 2:
+      for j in countup(lastKey, i-1):
+        var pair = newNodeI(nkPar, x.info)
+        pair.add(n.sons[j])
+        pair.add(x[1])
+        result.add(pair)
+
       var pair = newNodeI(nkPar, x.info)
       pair.add(x[0])
       pair.add(x[1])
       result.add(pair)
-    else:
-      illFormedAst(x)
+
+      lastKey = i+1
+
+  if lastKey != n.len: illFormedAst(n)
   result = semExpr(c, result)
 
 type 
@@ -1507,6 +1542,57 @@ proc semMacroStmt(c: PContext, n: PNode, flags: TExprFlags,
                renderTree(a, {renderNoComments}))
     result = errorNode(c, n)
 
+proc semCaseExpr(c: PContext, caseStmt: PNode): PNode =
+  # The case expression is simply rewritten to a StmtListExpr:
+  #   var res {.noInit, genSym.}: type(values)
+  #
+  #   case E
+  #   of X: res = value1
+  #   of Y: res = value2
+  # 
+  #   res
+  var
+    info = caseStmt.info
+    resVar = newSym(skVar, getIdent":res", getCurrOwner(), info)
+    resNode = newSymNode(resVar, info)
+    resType: PType
+
+  resVar.flags = { sfGenSym, sfNoInit }
+
+  for i in countup(1, caseStmt.len - 1):
+    var cs = caseStmt[i]
+    case cs.kind
+    of nkOfBranch, nkElifBranch, nkElse:
+      # the value is always the last son regardless of the branch kind
+      cs.checkMinSonsLen 1
+      var value = cs{-1}
+      if value.kind == nkStmtList: value.kind = nkStmtListExpr
+
+      value = semExprWithType(c, value)
+      if resType == nil:
+        resType = value.typ
+      elif not sameType(resType, value.typ):
+        # XXX: semeType is a bit too harsh.
+        # work on finding a common base type.
+        # this will be useful for arrays/seq too:
+        # [ref DerivedA, ref DerivedB, ref Base]
+        typeMismatch(cs, resType, value.typ)
+
+      cs{-1} = newNode(nkAsgn, cs.info, @[resNode, value])
+    else:
+      IllFormedAst(caseStmt)
+
+  result = newNode(nkStmtListExpr, info, @[
+    newNode(nkVarSection, info, @[
+      newNode(nkIdentDefs, info, @[
+        resNode,
+        symNodeFromType(c, resType, info),
+        emptyNode])]),
+    caseStmt,
+    resNode])
+
+  result = semStmtListExpr(c, result)
+    
 proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = 
   result = n
   if gCmd == cmdIdeTools: suggestExpr(c, n)
@@ -1686,7 +1772,9 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   of nkTryStmt: result = semTry(c, n)
   of nkBreakStmt, nkContinueStmt: result = semBreakOrContinue(c, n)
   of nkForStmt, nkParForStmt: result = semFor(c, n)
-  of nkCaseStmt: result = semCase(c, n)
+  of nkCaseStmt:
+    if efWantStmt in flags: result = semCase(c, n)
+    else: result = semCaseExpr(c, n)
   of nkReturnStmt: result = semReturn(c, n)
   of nkAsmStmt: result = semAsm(c, n)
   of nkYieldStmt: result = semYield(c, n)
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index c2958ca5e..d0805d921 100755
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -610,17 +610,6 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
           result = newIntNodeT(sonsLen(a), n)
         else:
           result = magicCall(m, n)
-      of mIs:
-        # BUGFIX: don't evaluate this too early: ``T is void``
-        if not containsGenericType(n[1].typ):
-          if n[2].kind in {nkStrLit..nkTripleStrLit}:
-            case n[2].strVal.normalize
-            of "closure":
-              let t = skipTypes(n[1].typ, abstractRange)
-              result = newIntNodeT(ord(t.kind == tyProc and
-                                       t.callConv == ccClosure), n)
-          elif not containsGenericType(n[2].typ):
-            result = newIntNodeT(ord(sameType(n[1].typ, n[2].typ)), n)
       of mAstToStr:
         result = newStrNodeT(renderTree(n[1], {renderNoComments}), n)
       of mConStrStr:
diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim
index 2751aa1e1..bffa8ad8e 100755
--- a/compiler/semgnrc.nim
+++ b/compiler/semgnrc.nim
@@ -261,16 +261,16 @@ proc semGenericStmt(c: PContext, n: PNode,
       else: 
         a.sons[2] = semGenericStmt(c, a.sons[2], flags+{withinTypeDesc}, toBind)
   of nkEnumTy: 
-    checkMinSonsLen(n, 1)
-    if n.sons[0].kind != nkEmpty: 
-      n.sons[0] = semGenericStmt(c, n.sons[0], flags+{withinTypeDesc}, toBind)
-    for i in countup(1, sonsLen(n) - 1): 
-      var a: PNode
-      case n.sons[i].kind
-      of nkEnumFieldDef: a = n.sons[i].sons[0]
-      of nkIdent: a = n.sons[i]
-      else: illFormedAst(n)
-      addDeclAt(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c), c.tab.tos-1)
+    if n.sonsLen > 0:
+      if n.sons[0].kind != nkEmpty: 
+        n.sons[0] = semGenericStmt(c, n.sons[0], flags+{withinTypeDesc}, toBind)
+      for i in countup(1, sonsLen(n) - 1): 
+        var a: PNode
+        case n.sons[i].kind
+        of nkEnumFieldDef: a = n.sons[i].sons[0]
+        of nkIdent: a = n.sons[i]
+        else: illFormedAst(n)
+        addDeclAt(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c), c.tab.tos-1)
   of nkObjectTy, nkTupleTy: 
     nil
   of nkFormalParams: 
@@ -306,6 +306,9 @@ proc semGenericStmt(c: PContext, n: PNode,
     n.sons[bodyPos] = semGenericStmtScope(c, body, flags, toBind)
     closeScope(c.tab)
   of nkPragma, nkPragmaExpr: nil
+  of nkExprColonExpr:
+    checkMinSonsLen(n, 2)
+    result.sons[1] = semGenericStmt(c, n.sons[1], flags, toBind)
   else: 
     for i in countup(0, sonsLen(n) - 1): 
       result.sons[i] = semGenericStmt(c, n.sons[i], flags, toBind)
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index 61210c0f8..8e164531a 100755
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -25,8 +25,13 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
     s.flags = s.flags + {sfUsed, sfFromGeneric}
     var t = PType(IdTableGet(pt, q.typ))
     if t == nil:
-      LocalError(a.info, errCannotInstantiateX, s.name.s)
-      t = errorType(c)
+      if tfRetType in q.typ.flags:
+        # keep the generic type and allow the return type to be bound 
+        # later by semAsgn in return type inference scenario
+        t = q.typ
+      else:
+        LocalError(a.info, errCannotInstantiateX, s.name.s)
+        t = errorType(c)
     elif t.kind == tyGenericParam: 
       InternalError(a.info, "instantiateGenericParamList: " & q.name.s)
     elif t.kind == tyGenericInvokation:
@@ -163,7 +168,6 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
     result.typ = newTypeS(tyProc, c)
     rawAddSon(result.typ, nil)
   result.typ.callConv = fn.typ.callConv
-  ParamsTypeCheck(c, result.typ)
   var oldPrc = GenericCacheGet(c, entry)
   if oldPrc == nil:
     c.generics.generics.add(entry)
@@ -174,6 +178,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
     if fn.kind != skTemplate:
       instantiateBody(c, n, result)
       sideEffectsCheck(c, result)
+    ParamsTypeCheck(c, result.typ)
   else:
     result = oldPrc
   popInfoContext()
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 5362d6d4a..c41727b10 100755
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -584,22 +584,20 @@ proc paramTypeClass(c: PContext, paramType: PType, procKind: TSymKind):
       result.typ = newTypeS(tyTypeDesc, c)
       result.typ.sons = paramType.sons
   of tyDistinct:
-    # type T1 = distinct expr
-    # type S1 = distinct Sortable
-    # proc x(a, b: T1, c, d: S1)
-    # This forces bindOnce behavior for the type class, equivalent to
-    # proc x[T, S](a, b: T, c, d: S)
     result = paramTypeClass(c, paramType.lastSon, procKind)
-    result.id = paramType.sym.name
+    # disable the bindOnce behavior for the type class
+    result.id = nil
+    return
   of tyGenericBody:
     # type Foo[T] = object
     # proc x(a: Foo, b: Foo) 
     result.typ = newTypeS(tyTypeClass, c)
     result.typ.addSonSkipIntLit(paramType)
-    result.id = paramType.sym.name # bindOnce by default
   of tyTypeClass:
     result.typ = copyType(paramType, getCurrOwner(), false)
   else: nil
+  # bindOnce by default
+  if paramType.sym != nil: result.id = paramType.sym.name
 
 proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
                    paramType: PType, paramName: string,
@@ -619,7 +617,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
       let s = SymtabGet(c.tab, paramTypId)
       # tests/run/tinterf triggers this:
       if s != nil: result = s.typ
-      else: 
+      else:
         LocalError(info, errCannotInstantiateX, paramName)
         result = errorType(c)
     else:
@@ -684,8 +682,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
     if skipTypes(typ, {tyGenericInst}).kind == tyEmpty: continue
     for j in countup(0, length-3): 
       var arg = newSymG(skParam, a.sons[j], c)
-      var finalType = liftParamType(c, kind, genericParams, typ, arg.name.s,
-                                    arg.info).skipIntLit
+      var finalType = liftParamType(c, kind, genericParams, typ,
+                                    arg.name.s, arg.info).skipIntLit
       arg.typ = finalType
       arg.position = counter
       inc(counter)
@@ -703,6 +701,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
     if skipTypes(r, {tyGenericInst}).kind != tyEmpty:
       if r.sym == nil or sfAnon notin r.sym.flags:
         r = liftParamType(c, kind, genericParams, r, "result", n.sons[0].info)
+        r.flags.incl tfRetType
       result.sons[0] = skipIntLit(r)
       res.typ = result.sons[0]
 
@@ -800,22 +799,25 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
       LocalError(n.info, errTypeExpected)
       result = newOrPrevType(tyError, prev, c)
   of nkCallKinds:
-    let op = n.sons[0].ident
-    if op.id in {ord(wAnd), ord(wOr)} or op.s == "|":
-      var
-        t1 = semTypeNode(c, n.sons[1], nil)
-        t2 = semTypeNode(c, n.sons[2], nil)
-      if   t1 == nil: 
-        LocalError(n.sons[1].info, errTypeExpected)
-        result = newOrPrevType(tyError, prev, c)
-      elif t2 == nil: 
-        LocalError(n.sons[2].info, errTypeExpected)
-        result = newOrPrevType(tyError, prev, c)
+    if n[0].kind == nkIdent:
+      let op = n.sons[0].ident
+      if op.id in {ord(wAnd), ord(wOr)} or op.s == "|":
+        var
+          t1 = semTypeNode(c, n.sons[1], nil)
+          t2 = semTypeNode(c, n.sons[2], nil)
+        if   t1 == nil: 
+          LocalError(n.sons[1].info, errTypeExpected)
+          result = newOrPrevType(tyError, prev, c)
+        elif t2 == nil: 
+          LocalError(n.sons[2].info, errTypeExpected)
+          result = newOrPrevType(tyError, prev, c)
+        else:
+          result = newTypeS(tyTypeClass, c)
+          result.addSonSkipIntLit(t1)
+          result.addSonSkipIntLit(t2)
+          result.flags.incl(if op.id == ord(wAnd): tfAll else: tfAny)
       else:
-        result = newTypeS(tyTypeClass, c)
-        result.addSonSkipIntLit(t1)
-        result.addSonSkipIntLit(t2)
-        result.flags.incl(if op.id == ord(wAnd): tfAll else: tfAny)
+        result = semTypeExpr(c, n)
     else:
       result = semTypeExpr(c, n)
   of nkCurlyExpr:
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index d75594dff..70453c6db 100755
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -59,6 +59,7 @@ type
 
 proc ReplaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType
 proc ReplaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym
+proc ReplaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode
 
 proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode =
   result = copyNode(n)
@@ -66,7 +67,7 @@ proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode =
   for i in 0 .. safeLen(n)-1: 
     # XXX HACK: ``f(a, b)``, avoid to instantiate `f` 
     if i == 0: result.add(n[i])
-    else: result.add(prepareNode(cl, n[i]))
+    else: result.add(ReplaceTypeVarsN(cl, n[i]))
 
 proc ReplaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode =
   if n == nil: return
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 7e482b3d2..8f1ca5f36 100755
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -11,7 +11,7 @@
 ## the call to overloaded procs, generic procs and operators.
 
 import 
-  intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst, 
+  intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst,
   magicsys, condsyms, idents, lexer, options
 
 type
@@ -257,32 +257,6 @@ proc tupleRel(c: var TCandidate, f, a: PType): TTypeRelation =
           var y = a.n.sons[i].sym
           if x.name.id != y.name.id: return isNone
 
-proc matchTypeClass(c: var TCandidate, typeClass, t: PType): TTypeRelation =
-  for i in countup(0, typeClass.sonsLen - 1):
-    let req = typeClass.sons[i]
-    var match = req.kind == skipTypes(t, {tyRange, tyGenericInst}).kind
-      
-    if not match:
-      case req.kind
-      of tyGenericBody:
-        if t.kind == tyGenericInst and t.sons[0] == req:
-          match = true
-          put(c.bindings, typeClass, t)
-      of tyTypeClass:
-        match = matchTypeClass(c, req, t) == isGeneric
-      else: nil
-    elif t.kind in {tyObject}:
-      match = sameType(t, req)
-
-    if tfAny in typeClass.flags:
-      if match: return isGeneric
-    else:
-      if not match: return isNone
-
-  # if the loop finished without returning, either all constraints matched
-  # or none of them matched.
-  result = if tfAny in typeClass.flags: isNone else: isGeneric
-  
 proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
   proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
     result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar)
@@ -325,6 +299,10 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
         result = isNone
   else: nil
 
+proc matchTypeClass(c: var TCandidate, f, a: PType): TTypeRelation =
+  result = if matchTypeClass(c.bindings, f, a): isGeneric
+           else: isNone
+
 proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation = 
   # is a subtype of f?
   result = isNone
diff --git a/compiler/types.nim b/compiler/types.nim
index d8879f1b4..e02d93233 100755
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -626,9 +626,9 @@ proc SameTypeOrNil*(a, b: PType, flags: TTypeCmpFlags = {}): bool =
       var c = initSameTypeClosure()
       c.flags = flags
       result = SameTypeAux(a, b, c)
-  
+
 proc equalParam(a, b: PSym): TParamsEquality = 
-  if SameTypeOrNil(a.typ, b.typ): 
+  if SameTypeOrNil(a.typ, b.typ, {TypeDescExactMatch}): 
     if a.ast == b.ast: 
       result = paramsEqual
     elif a.ast != nil and b.ast != nil: 
@@ -904,7 +904,38 @@ proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]],
     if i >= a.sonslen or a.sons[i] == nil: return false
     a = a.sons[i]
   result = a.kind == last
-  
+
+proc matchTypeClass*(bindings: var TIdTable, typeClass, t: PType): bool =
+  for i in countup(0, typeClass.sonsLen - 1):
+    let req = typeClass.sons[i]
+    var match = req.kind == skipTypes(t, {tyRange, tyGenericInst}).kind
+      
+    if not match:
+      case req.kind
+      of tyGenericBody:
+        if t.kind == tyGenericInst and t.sons[0] == req:
+          match = true
+          IdTablePut(bindings, typeClass, t)
+      of tyTypeClass:
+        match = matchTypeClass(bindings, req, t)
+      else: nil
+    elif t.kind in {tyObject}:
+      match = sameType(t, req)
+
+    if tfAny in typeClass.flags:
+      if match: return true
+    else:
+      if not match: return false
+
+  # if the loop finished without returning, either all constraints matched
+  # or none of them matched.
+  result = if tfAny in typeClass.flags: false else: true
+
+proc matchTypeClass*(typeClass, typ: PType): bool =
+  var bindings: TIdTable
+  initIdTable(bindings)
+  result = matchTypeClass(bindings, typeClass, typ)
+
 proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool =
   assert(kind in {skVar, skLet, skConst, skParam, skResult})
   # if we have already checked the type, return true, because we stop the
diff --git a/doc/manual.txt b/doc/manual.txt
index 4b7dc1aa5..a85c1e753 100755
--- a/doc/manual.txt
+++ b/doc/manual.txt
@@ -2237,6 +2237,28 @@ An if expression always results in a value, so the ``else`` part is
 required. ``Elif`` parts are also allowed (but unlikely to be good
 style).
 
+When expression
+~~~~~~~~~~~~~~~
+
+Just like an `if expression`, but corresponding to the when statement.
+
+Case expression
+~~~~~~~~~~~~~~~
+
+The `case expression` is again very similar to the case statement:
+
+.. code-block:: nimrod
+  var favoriteFood = case animal
+    of "dog": "bones"
+    of "cat": "mice"
+    elif animal.endsWith"whale": "plankton"
+    else:
+      echo "I'm not sure what to serve, but everybody loves ice cream"
+      "ice cream"
+
+As seen in the above example, the case expression can also introduce side
+effects. When multiple statements are given for a branch, Nimrod will use
+the last expression as the result value, much like in an `expr` template.
 
 Table constructor
 ~~~~~~~~~~~~~~~~~
@@ -2244,10 +2266,10 @@ Table constructor
 A `table constructor`:idx: is syntactic sugar for an array constructor:
 
 .. code-block:: nimrod
-  {"key1": "value1", "key2": "value2"}
+  {"key1": "value1", "key2", "key3": "value2"}
   
   # is the same as:
-  [("key1", "value1"), ("key2", "value2")]
+  [("key1", "value1"), ("key2", "value2"), ("key3", "value")]
 
 
 The empty table can be written ``{:}`` (in contrast to the empty set 
@@ -2940,6 +2962,13 @@ from the proc body. This is usually used with the ``auto`` type class:
 .. code-block:: nimrod
   proc makePair(a, b): auto = (first: a, second: b)
 
+The return type will be treated as additional generic param and can be
+explicitly specified at call sites as any other generic param.
+
+Future versions of nimrod may also support overloading based on the return type
+of the overloads. In such settings, the expected result type at call sites may 
+also influence the inferred return type.
+
 Symbol lookup in generics
 ~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -3431,7 +3460,7 @@ typedesc
 
 `typedesc` is a special type allowing you to treat types as compile-time values
 (i.e. if types are compile-time values and all values have a type, then 
- typedesc must be their type).
+typedesc must be their type).
 
 When used as a regular proc param, typedesc acts as a type class. The proc
 will be instantiated for each unique type parameter and you can refer to the
@@ -3457,15 +3486,14 @@ a type-safe wrapper for the unsafe `printf` function form C:
   macro safePrintF(formatString: string{lit}, args: vararg[expr]): expr =
     var i = 0
     for c in formatChars(formatString):
-      const FormatChars = {
-        'c': char,
-        'd', 'i', 'x', 'X': int,
-        'f', 'e', 'E', 'g', 'G': float,
-        's': string,
-        'p': pointer,
-      }
+      var expectedType = case c
+        of 'c': char
+        of 'd', 'i', 'x', 'X': int
+        of 'f', 'e', 'E', 'g', 'G': float
+        of 's': string
+        of 'p': pointer
+        else: EOutOfRange
       
-      var expectedType = find(FormatChars, c, EOutOfRange)
       var actualType = args[i].getType
       inc i
 
@@ -3642,15 +3670,20 @@ proc with no side effects:
 
 destructor pragma
 -----------------
-`RAII`:idx:
-`automatic variables`:idx:
-`destructors`:idx:
+
 The `destructor` pragma is used to mark a proc to act as a type destructor.
-The proc must have a single parameter, having a concrete type. 
+The proc must have a single parameter with a concrete type (the name of a
+generic type is allowed too).
+
 Destructors will be automatically invoked when a local stack variable goes 
-out of scope. If a record type features a field with destructable type and 
+out of scope.
+
+If a record type features a field with destructable type and 
 the user have not provided explicit implementation, Nimrod will automatically
-generate a destructor for the record type.
+generate a destructor for the record type. Nimrod will automatically insert
+calls to any base class destructors in both user-defined and generated
+destructors.
+
 
 procvar pragma
 --------------
@@ -3658,7 +3691,6 @@ The `procvar`:idx: pragma is used to mark a proc that it can be passed to a
 procedural variable.
 
 
-
 compileTime pragma
 ------------------
 The `compileTime`:idx: pragma is used to mark a proc to be used at compile
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index b43105f44..8fdaf7a6c 100755
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -1,194 +1,188 @@
-#

-#

-#            Nimrod's Runtime Library

-#        (c) Copyright 2012 Andreas Rumpf

-#

-#    See the file "copying.txt", included in this

-#    distribution, for details about the copyright.

-#

-

-

-## This module contains the interface to the compiler's abstract syntax 

-## tree (`AST`:idx:). Macros operate on this tree.

-

-## .. include:: ../doc/astspec.txt

-

-type

-  TNimrodNodeKind* = enum

-    nnkNone, nnkEmpty, nnkIdent, nnkSym, 

-    nnkType, nnkCharLit, nnkIntLit, nnkInt8Lit, 

-    nnkInt16Lit, nnkInt32Lit, nnkInt64Lit, nnkUIntLit, nnkUInt8Lit, 

-    nnkUInt16Lit, nnkUInt32Lit, nnkUInt64Lit, nnkFloatLit, 

-    nnkFloat32Lit, nnkFloat64Lit, nnkFloat128Lit, nnkStrLit, nnkRStrLit, 

-    nnkTripleStrLit, nnkNilLit, nnkMetaNode, nnkDotCall, 

-    nnkCommand, nnkCall, nnkCallStrLit, nnkExprEqExpr, 

-    nnkExprColonExpr, nnkIdentDefs, nnkVarTuple, nnkInfix, 

-    nnkPrefix, nnkPostfix, nnkPar, nnkCurly, nnkCurlyExpr,

-    nnkBracket, nnkBracketExpr, nnkPragmaExpr, nnkRange, 

-    nnkDotExpr, nnkCheckedFieldExpr, nnkDerefExpr, nnkIfExpr, 

-    nnkElifExpr, nnkElseExpr, nnkLambda, nnkDo, nnkAccQuoted, 

+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2012 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+
+## This module contains the interface to the compiler's abstract syntax
+## tree (`AST`:idx:). Macros operate on this tree.
+
+## .. include:: ../doc/astspec.txt
+
+type
+  TNimrodNodeKind* = enum
+    nnkNone, nnkEmpty, nnkIdent, nnkSym,
+    nnkType, nnkCharLit, nnkIntLit, nnkInt8Lit,
+    nnkInt16Lit, nnkInt32Lit, nnkInt64Lit, nnkUIntLit, nnkUInt8Lit,
+    nnkUInt16Lit, nnkUInt32Lit, nnkUInt64Lit, nnkFloatLit,
+    nnkFloat32Lit, nnkFloat64Lit, nnkFloat128Lit, nnkStrLit, nnkRStrLit,
+    nnkTripleStrLit, nnkNilLit, nnkMetaNode, nnkDotCall,
+    nnkCommand, nnkCall, nnkCallStrLit, nnkExprEqExpr,
+    nnkExprColonExpr, nnkIdentDefs, nnkVarTuple, nnkInfix,
+    nnkPrefix, nnkPostfix, nnkPar, nnkCurly, nnkCurlyExpr,
+    nnkBracket, nnkBracketExpr, nnkPragmaExpr, nnkRange,
+    nnkDotExpr, nnkCheckedFieldExpr, nnkDerefExpr, nnkIfExpr,
+    nnkElifExpr, nnkElseExpr, nnkLambda, nnkDo, nnkAccQuoted,
     nnkTableConstr, nnkBind,
     nnkClosedSymChoice,
     nnkOpenSymChoice,
-    nnkHiddenStdConv,

-    nnkHiddenSubConv, nnkHiddenCallConv, nnkConv, nnkCast, nnkStaticExpr,

-    nnkAddr, nnkHiddenAddr, nnkHiddenDeref, nnkObjDownConv, 

-    nnkObjUpConv, nnkChckRangeF, nnkChckRange64, nnkChckRange, 

-    nnkStringToCString, nnkCStringToString, nnkAsgn, 

-    nnkFastAsgn, nnkGenericParams, nnkFormalParams, nnkOfInherit, 

-    nnkModule, nnkProcDef, nnkMethodDef, nnkConverterDef, 

-    nnkMacroDef, nnkTemplateDef, nnkIteratorDef, nnkOfBranch, 

-    nnkElifBranch, nnkExceptBranch, nnkElse, nnkMacroStmt, 

-    nnkAsmStmt, nnkPragma, nnkPragmaBlock, nnkIfStmt, nnkWhenStmt, 

-    nnkForStmt, nnkParForStmt, nnkWhileStmt, nnkCaseStmt, 

-    nnkTypeSection, nnkVarSection, nnkLetSection, nnkConstSection, 

-    nnkConstDef, nnkTypeDef, 

-    nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt, 

-    nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkStaticStmt,

-    nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkFromStmt, 

-    nnkIncludeStmt, nnkBindStmt, nnkMixinStmt,

-    nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr, 

-    nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy, 

-    nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen, 

-    nnkRefTy, nnkPtrTy, nnkVarTy, 

-    nnkConstTy, nnkMutableTy,

-    nnkDistinctTy, 

-    nnkProcTy, nnkEnumTy, 
-    nnkEnumFieldDef, 
+    nnkHiddenStdConv,
+    nnkHiddenSubConv, nnkHiddenCallConv, nnkConv, nnkCast, nnkStaticExpr,
+    nnkAddr, nnkHiddenAddr, nnkHiddenDeref, nnkObjDownConv,
+    nnkObjUpConv, nnkChckRangeF, nnkChckRange64, nnkChckRange,
+    nnkStringToCString, nnkCStringToString, nnkAsgn,
+    nnkFastAsgn, nnkGenericParams, nnkFormalParams, nnkOfInherit,
+    nnkModule, nnkProcDef, nnkMethodDef, nnkConverterDef,
+    nnkMacroDef, nnkTemplateDef, nnkIteratorDef, nnkOfBranch,
+    nnkElifBranch, nnkExceptBranch, nnkElse, nnkMacroStmt,
+    nnkAsmStmt, nnkPragma, nnkPragmaBlock, nnkIfStmt, nnkWhenStmt,
+    nnkForStmt, nnkParForStmt, nnkWhileStmt, nnkCaseStmt,
+    nnkTypeSection, nnkVarSection, nnkLetSection, nnkConstSection,
+    nnkConstDef, nnkTypeDef,
+    nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt,
+    nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkStaticStmt,
+    nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkFromStmt,
+    nnkIncludeStmt, nnkBindStmt, nnkMixinStmt,
+    nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr,
+    nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy,
+    nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen,
+    nnkRefTy, nnkPtrTy, nnkVarTy,
+    nnkConstTy, nnkMutableTy,
+    nnkDistinctTy,
+    nnkProcTy, nnkEnumTy,
+    nnkEnumFieldDef,
     nnkArglist, nnkPattern
-    nnkReturnToken

-  TNimNodeKinds* = set[TNimrodNodeKind]

-  TNimrodTypeKind* = enum

-    ntyNone, ntyBool, ntyChar, ntyEmpty, 

-    ntyArrayConstr, ntyNil, ntyExpr, ntyStmt, 

-    ntyTypeDesc, ntyGenericInvokation, ntyGenericBody, ntyGenericInst, 

-    ntyGenericParam, ntyDistinct, ntyEnum, ntyOrdinal, 

-    ntyArray, ntyObject, ntyTuple, ntySet, 

-    ntyRange, ntyPtr, ntyRef, ntyVar, 

-    ntySequence, ntyProc, ntyPointer, ntyOpenArray, 

-    ntyString, ntyCString, ntyForward, ntyInt, 

-    ntyInt8, ntyInt16, ntyInt32, ntyInt64, 

-    ntyFloat, ntyFloat32, ntyFloat64, ntyFloat128

-  TNimTypeKinds* = set[TNimrodTypeKind]

-  TNimrodSymKind* = enum

-    nskUnknown, nskConditional, nskDynLib, nskParam, 

-    nskGenericParam, nskTemp, nskType, nskConst, 

-    nskVar, nskProc, nskMethod, nskIterator, 

-    nskConverter, nskMacro, nskTemplate, nskField, 

-    nskEnumField, nskForVar, nskModule, nskLabel, 

-    nskStub

-  TNimSymKinds* = set[TNimrodSymKind]

-

-type

-  TNimrodIdent* = object of TObject

-    ## represents a Nimrod identifier in the AST

-

-  TNimrodSymbol {.final.} = object # hidden

-  TNimrodType {.final.} = object   # hidden

-  

-  PNimrodType* {.compilerproc.} = ref TNimrodType

-    ## represents a Nimrod type in the compiler; currently this is not very

-    ## useful as there is no API to deal with Nimrod types.

-  

-  PNimrodSymbol* {.compilerproc.} = ref TNimrodSymbol

-    ## represents a Nimrod *symbol* in the compiler; a *symbol* is a looked-up

-    ## *ident*.

-  

-const

-  nnkLiterals* = {nnkCharLit..nnkNilLit}

-  nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,

-                   nnkCallStrLit}

-

-proc `[]`*(n: PNimrodNode, i: int): PNimrodNode {.magic: "NChild".}

-  ## get `n`'s `i`'th child.

-

-proc `[]=`*(n: PNimrodNode, i: int, child: PNimrodNode) {.magic: "NSetChild".}

-  ## set `n`'s `i`'th child to `child`.

-

-proc `!`*(s: string): TNimrodIdent {.magic: "StrToIdent".}

-  ## constructs an identifier from the string `s`

-

-proc `$`*(i: TNimrodIdent): string {.magic: "IdentToStr".}

-  ## converts a Nimrod identifier to a string

-

-proc `$`*(s: PNimrodSymbol): string {.magic: "IdentToStr".}

-  ## converts a Nimrod symbol to a string

-
-proc `==`*(a, b: TNimrodIdent): bool {.magic: "EqIdent", noSideEffect.}

-  ## compares two Nimrod identifiers

-

-proc `==`*(a, b: PNimrodNode): bool {.magic: "EqNimrodNode", noSideEffect.}

-  ## compares two Nimrod nodes

-

-proc len*(n: PNimrodNode): int {.magic: "NLen".}

-  ## returns the number of children of `n`.

-

-proc add*(father, child: PNimrodNode) {.magic: "NAdd".}

-  ## adds the `child` to the `father` node

-

-proc add*(father: PNimrodNode, children: varargs[PNimrodNode]) {.

-  magic: "NAddMultiple".}

-  ## adds each child of `children` to the `father` node

-

-proc del*(father: PNimrodNode, idx = 0, n = 1) {.magic: "NDel".}

-  ## deletes `n` children of `father` starting at index `idx`. 

-

-proc kind*(n: PNimrodNode): TNimrodNodeKind {.magic: "NKind".}

-  ## returns the `kind` of the node `n`.

-

-proc intVal*(n: PNimrodNode): biggestInt {.magic: "NIntVal".}

-proc floatVal*(n: PNimrodNode): biggestFloat {.magic: "NFloatVal".}

-proc symbol*(n: PNimrodNode): PNimrodSymbol {.magic: "NSymbol".}

-proc ident*(n: PNimrodNode): TNimrodIdent {.magic: "NIdent".}

-proc typ*(n: PNimrodNode): PNimrodType {.magic: "NGetType".}

-proc strVal*(n: PNimrodNode): string  {.magic: "NStrVal".}

-

-proc `intVal=`*(n: PNimrodNode, val: biggestInt) {.magic: "NSetIntVal".}

-proc `floatVal=`*(n: PNimrodNode, val: biggestFloat) {.magic: "NSetFloatVal".}

-proc `symbol=`*(n: PNimrodNode, val: PNimrodSymbol) {.magic: "NSetSymbol".}

-proc `ident=`*(n: PNimrodNode, val: TNimrodIdent) {.magic: "NSetIdent".}

-proc `typ=`*(n: PNimrodNode, typ: PNimrodType) {.magic: "NSetType".}

-proc `strVal=`*(n: PNimrodNode, val: string) {.magic: "NSetStrVal".}

-

-proc newNimNode*(kind: TNimrodNodeKind,

-                 n: PNimrodNode=nil): PNimrodNode {.magic: "NNewNimNode".}

-

-proc copyNimNode*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimNode".}

-proc copyNimTree*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimTree".}

-

-proc error*(msg: string) {.magic: "NError".}

-  ## writes an error message at compile time

-

-proc warning*(msg: string) {.magic: "NWarning".}

-  ## writes a warning message at compile time

-

-proc hint*(msg: string) {.magic: "NHint".}

-  ## writes a hint message at compile time

-

-proc newStrLitNode*(s: string): PNimrodNode {.compileTime.} =

-  ## creates a string literal node from `s`

-  result = newNimNode(nnkStrLit)

-  result.strVal = s

-

-proc newIntLitNode*(i: biggestInt): PNimrodNode {.compileTime.} =

-  ## creates a int literal node from `i`

-  result = newNimNode(nnkIntLit)

-  result.intVal = i

-

-proc newFloatLitNode*(f: biggestFloat): PNimrodNode {.compileTime.} =

-  ## creates a float literal node from `f`

-  result = newNimNode(nnkFloatLit)

-  result.floatVal = f

-

-proc newIdentNode*(i: TNimrodIdent): PNimrodNode {.compileTime.} =

-  ## creates an identifier node from `i`

-  result = newNimNode(nnkIdent)

-  result.ident = i

-

-proc newIdentNode*(i: string): PNimrodNode {.compileTime.} =

-  ## creates an identifier node from `i`

-  result = newNimNode(nnkIdent)

-  result.ident = !i

+    nnkReturnToken
+  TNimNodeKinds* = set[TNimrodNodeKind]
+  TNimrodTypeKind* = enum
+    ntyNone, ntyBool, ntyChar, ntyEmpty,
+    ntyArrayConstr, ntyNil, ntyExpr, ntyStmt,
+    ntyTypeDesc, ntyGenericInvokation, ntyGenericBody, ntyGenericInst,
+    ntyGenericParam, ntyDistinct, ntyEnum, ntyOrdinal,
+    ntyArray, ntyObject, ntyTuple, ntySet,
+    ntyRange, ntyPtr, ntyRef, ntyVar,
+    ntySequence, ntyProc, ntyPointer, ntyOpenArray,
+    ntyString, ntyCString, ntyForward, ntyInt,
+    ntyInt8, ntyInt16, ntyInt32, ntyInt64,
+    ntyFloat, ntyFloat32, ntyFloat64, ntyFloat128
+  TNimTypeKinds* = set[TNimrodTypeKind]
+  TNimrodSymKind* = enum
+    nskUnknown, nskConditional, nskDynLib, nskParam,
+    nskGenericParam, nskTemp, nskType, nskConst,
+    nskVar, nskProc, nskMethod, nskIterator,
+    nskConverter, nskMacro, nskTemplate, nskField,
+    nskEnumField, nskForVar, nskModule, nskLabel,
+    nskStub
+  TNimSymKinds* = set[TNimrodSymKind]
+
+type
+  TNimrodIdent* = object of TObject
+    ## represents a Nimrod identifier in the AST
+
+  TNimrodSymbol {.final.} = object # hidden
+  PNimrodSymbol* {.compilerproc.} = ref TNimrodSymbol
+    ## represents a Nimrod *symbol* in the compiler; a *symbol* is a looked-up
+    ## *ident*.
+
+const
+  nnkLiterals* = {nnkCharLit..nnkNilLit}
+  nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
+                   nnkCallStrLit}
+
+proc `[]`*(n: PNimrodNode, i: int): PNimrodNode {.magic: "NChild".}
+  ## get `n`'s `i`'th child.
+
+proc `[]=`*(n: PNimrodNode, i: int, child: PNimrodNode) {.magic: "NSetChild".}
+  ## set `n`'s `i`'th child to `child`.
+
+proc `!`*(s: string): TNimrodIdent {.magic: "StrToIdent".}
+  ## constructs an identifier from the string `s`
+
+proc `$`*(i: TNimrodIdent): string {.magic: "IdentToStr".}
+  ## converts a Nimrod identifier to a string
+
+proc `$`*(s: PNimrodSymbol): string {.magic: "IdentToStr".}
+  ## converts a Nimrod symbol to a string
+
+proc `==`*(a, b: TNimrodIdent): bool {.magic: "EqIdent", noSideEffect.}
+  ## compares two Nimrod identifiers
+
+proc `==`*(a, b: PNimrodNode): bool {.magic: "EqNimrodNode", noSideEffect.}
+  ## compares two Nimrod nodes
+
+proc len*(n: PNimrodNode): int {.magic: "NLen".}
+  ## returns the number of children of `n`.
+
+proc add*(father, child: PNimrodNode) {.magic: "NAdd".}
+  ## adds the `child` to the `father` node
+
+proc add*(father: PNimrodNode, children: varargs[PNimrodNode]) {.
+  magic: "NAddMultiple".}
+  ## adds each child of `children` to the `father` node
+
+proc del*(father: PNimrodNode, idx = 0, n = 1) {.magic: "NDel".}
+  ## deletes `n` children of `father` starting at index `idx`.
+
+proc kind*(n: PNimrodNode): TNimrodNodeKind {.magic: "NKind".}
+  ## returns the `kind` of the node `n`.
+
+proc intVal*(n: PNimrodNode): biggestInt {.magic: "NIntVal".}
+proc floatVal*(n: PNimrodNode): biggestFloat {.magic: "NFloatVal".}
+proc symbol*(n: PNimrodNode): PNimrodSymbol {.magic: "NSymbol".}
+proc ident*(n: PNimrodNode): TNimrodIdent {.magic: "NIdent".}
+proc typ*(n: PNimrodNode): typedesc {.magic: "NGetType".}
+proc strVal*(n: PNimrodNode): string  {.magic: "NStrVal".}
+
+proc `intVal=`*(n: PNimrodNode, val: biggestInt) {.magic: "NSetIntVal".}
+proc `floatVal=`*(n: PNimrodNode, val: biggestFloat) {.magic: "NSetFloatVal".}
+proc `symbol=`*(n: PNimrodNode, val: PNimrodSymbol) {.magic: "NSetSymbol".}
+proc `ident=`*(n: PNimrodNode, val: TNimrodIdent) {.magic: "NSetIdent".}
+proc `typ=`*(n: PNimrodNode, typ: typedesc) {.magic: "NSetType".}
+proc `strVal=`*(n: PNimrodNode, val: string) {.magic: "NSetStrVal".}
+
+proc newNimNode*(kind: TNimrodNodeKind,
+                 n: PNimrodNode=nil): PNimrodNode {.magic: "NNewNimNode".}
+
+proc copyNimNode*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimNode".}
+proc copyNimTree*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimTree".}
+
+proc error*(msg: string) {.magic: "NError".}
+  ## writes an error message at compile time
+
+proc warning*(msg: string) {.magic: "NWarning".}
+  ## writes a warning message at compile time
+
+proc hint*(msg: string) {.magic: "NHint".}
+  ## writes a hint message at compile time
+
+proc newStrLitNode*(s: string): PNimrodNode {.compileTime.} =
+  ## creates a string literal node from `s`
+  result = newNimNode(nnkStrLit)
+  result.strVal = s
+
+proc newIntLitNode*(i: biggestInt): PNimrodNode {.compileTime.} =
+  ## creates a int literal node from `i`
+  result = newNimNode(nnkIntLit)
+  result.intVal = i
+
+proc newFloatLitNode*(f: biggestFloat): PNimrodNode {.compileTime.} =
+  ## creates a float literal node from `f`
+  result = newNimNode(nnkFloatLit)
+  result.floatVal = f
+
+proc newIdentNode*(i: TNimrodIdent): PNimrodNode {.compileTime.} =
+  ## creates an identifier node from `i`
+  result = newNimNode(nnkIdent)
+  result.ident = i
+
+proc newIdentNode*(i: string): PNimrodNode {.compileTime.} =
+  ## creates an identifier node from `i`
+  result = newNimNode(nnkIdent)
+  result.ident = !i
 
 type
   TBindSymRule* = enum   ## specifies how ``bindSym`` behaves
@@ -212,157 +206,157 @@ proc bindSym*(ident: string, rule: TBindSymRule = brClosed): PNimrodNode {.
   ## returned even if the symbol is not ambiguous.
 
 proc callsite*(): PNimrodNode {.magic: "NCallSite".}
-  ## returns the AST if the invokation expression that invoked this macro. 
-

-proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} =

-  ## converts the AST `n` to the concrete Nimrod code and wraps that

-  ## in a string literal node

-  return newStrLitNode(repr(n))

-

-proc lineinfo*(n: PNimrodNode): string {.magic: "NLineInfo".}

-  ## returns the position the node appears in the original source file

-  ## in the form filename(line, col)

-

-proc parseExpr*(s: string): PNimrodNode {.magic: "ParseExprToAst".}

-  ## Compiles the passed string to its AST representation.

-  ## Expects a single expression.

-

-proc parseStmt*(s: string): PNimrodNode {.magic: "ParseStmtToAst".}

-  ## Compiles the passed string to its AST representation.

-  ## Expects one or more statements.

-

-proc getAst*(macroOrTemplate: expr): PNimrodNode {.magic: "ExpandToAst".}

-  ## Obtains the AST nodes returned from a macro or template invocation.

-  ## Example:

-  ## 

-  ## .. code-block:: nimrod

-  ##

-  ##   macro FooMacro() = 

-  ##     var ast = getAst(BarTemplate())

-

-template emit*(s: expr): stmt =

-  ## accepts a single string argument and treats it as nimrod code

-  ## that should be inserted verbatim in the program

-  ## Example:

-  ## 

-  ##   emit("echo " & '"' & "hello world".toUpper & '"')

+  ## returns the AST if the invokation expression that invoked this macro.
+
+proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} =
+  ## converts the AST `n` to the concrete Nimrod code and wraps that
+  ## in a string literal node
+  return newStrLitNode(repr(n))
+
+proc lineinfo*(n: PNimrodNode): string {.magic: "NLineInfo".}
+  ## returns the position the node appears in the original source file
+  ## in the form filename(line, col)
+
+proc parseExpr*(s: string): PNimrodNode {.magic: "ParseExprToAst".}
+  ## Compiles the passed string to its AST representation.
+  ## Expects a single expression.
+
+proc parseStmt*(s: string): PNimrodNode {.magic: "ParseStmtToAst".}
+  ## Compiles the passed string to its AST representation.
+  ## Expects one or more statements.
+
+proc getAst*(macroOrTemplate: expr): PNimrodNode {.magic: "ExpandToAst".}
+  ## Obtains the AST nodes returned from a macro or template invocation.
+  ## Example:
+  ##
+  ## .. code-block:: nimrod
+  ##
+  ##   macro FooMacro() =
+  ##     var ast = getAst(BarTemplate())
+
+template emit*(s: expr): stmt =
+  ## accepts a single string argument and treats it as nimrod code
+  ## that should be inserted verbatim in the program
+  ## Example:
+  ##
+  ##   emit("echo " & '"' & "hello world".toUpper & '"')
   ##
   block:
     const evaluated = s
     eval: result = evaluated.parseStmt
-

-proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} =

-  ## checks that `n` is of kind `k`. If this is not the case,

-  ## compilation aborts with an error message. This is useful for writing

-  ## macros that check the AST that is passed to them.

-  if n.kind != k: error("macro expects a node of kind: " & repr(k))

-

-proc expectMinLen*(n: PNimrodNode, min: int) {.compileTime.} =

-  ## checks that `n` has at least `min` children. If this is not the case,

-  ## compilation aborts with an error message. This is useful for writing

-  ## macros that check its number of arguments. 

-  if n.len < min: error("macro expects a node with " & $min & " children")

-

-proc expectLen*(n: PNimrodNode, len: int) {.compileTime.} =

-  ## checks that `n` has exactly `len` children. If this is not the case,

-  ## compilation aborts with an error message. This is useful for writing

-  ## macros that check its number of arguments. 

-  if n.len != len: error("macro expects a node with " & $len & " children")

-
-proc newCall*(theProc: PNimrodNode,

-              args: varargs[PNimrodNode]): PNimrodNode {.compileTime.} =

-  ## produces a new call node. `theProc` is the proc that is called with

-  ## the arguments ``args[0..]``.

-  result = newNimNode(nnkCall)

-  result.add(theProc)

-  result.add(args)

-

-proc newCall*(theProc: TNimrodIdent,

-              args: varargs[PNimrodNode]): PNimrodNode {.compileTime.} =

-  ## produces a new call node. `theProc` is the proc that is called with

-  ## the arguments ``args[0..]``.

-  result = newNimNode(nnkCall)

-  result.add(newIdentNode(theProc))

-  result.add(args)

-  

-proc newCall*(theProc: string,

-              args: varargs[PNimrodNode]): PNimrodNode {.compileTime.} =

-  ## produces a new call node. `theProc` is the proc that is called with

-  ## the arguments ``args[0..]``.

-  result = newNimNode(nnkCall)

-  result.add(newIdentNode(theProc))

-  result.add(args)

-

-proc nestList*(theProc: TNimrodIdent,

-               x: PNimrodNode): PNimrodNode {.compileTime.} =

-  ## nests the list `x` into a tree of call expressions:

-  ## ``[a, b, c]`` is transformed into ``theProc(a, theProc(c, d))``.

-  var L = x.len

-  result = newCall(theProc, x[L-2], x[L-1])

-  var a = result

-  for i in countdown(L-3, 0):

-    a = newCall(theProc, x[i], copyNimTree(a))

-

-proc treeRepr*(n: PNimrodNode): string {.compileTime.} =

-  ## Convert the AST `n` to a human-readable tree-like string.

-  ##

-  ## See also `repr` and `lispRepr`.

-  proc traverse(res: var string, level: int, n: PNimrodNode) =

-    for i in 0..level-1: res.add "  "

-    res.add(($n.kind).substr(3))

-    

-    case n.kind

-    of nnkEmpty: nil # same as nil node in this representation

-    of nnkNilLit: res.add(" nil")

-    of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal)

-    of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal)

-    of nnkStrLit..nnkTripleStrLit: res.add(" " & $n.strVal)

-    of nnkIdent: res.add(" !\"" & $n.ident & '"')

+
+proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} =
+  ## checks that `n` is of kind `k`. If this is not the case,
+  ## compilation aborts with an error message. This is useful for writing
+  ## macros that check the AST that is passed to them.
+  if n.kind != k: error("macro expects a node of kind: " & repr(k))
+
+proc expectMinLen*(n: PNimrodNode, min: int) {.compileTime.} =
+  ## checks that `n` has at least `min` children. If this is not the case,
+  ## compilation aborts with an error message. This is useful for writing
+  ## macros that check its number of arguments.
+  if n.len < min: error("macro expects a node with " & $min & " children")
+
+proc expectLen*(n: PNimrodNode, len: int) {.compileTime.} =
+  ## checks that `n` has exactly `len` children. If this is not the case,
+  ## compilation aborts with an error message. This is useful for writing
+  ## macros that check its number of arguments.
+  if n.len != len: error("macro expects a node with " & $len & " children")
+
+proc newCall*(theProc: PNimrodNode,
+              args: varargs[PNimrodNode]): PNimrodNode {.compileTime.} =
+  ## produces a new call node. `theProc` is the proc that is called with
+  ## the arguments ``args[0..]``.
+  result = newNimNode(nnkCall)
+  result.add(theProc)
+  result.add(args)
+
+proc newCall*(theProc: TNimrodIdent,
+              args: varargs[PNimrodNode]): PNimrodNode {.compileTime.} =
+  ## produces a new call node. `theProc` is the proc that is called with
+  ## the arguments ``args[0..]``.
+  result = newNimNode(nnkCall)
+  result.add(newIdentNode(theProc))
+  result.add(args)
+
+proc newCall*(theProc: string,
+              args: varargs[PNimrodNode]): PNimrodNode {.compileTime.} =
+  ## produces a new call node. `theProc` is the proc that is called with
+  ## the arguments ``args[0..]``.
+  result = newNimNode(nnkCall)
+  result.add(newIdentNode(theProc))
+  result.add(args)
+
+proc nestList*(theProc: TNimrodIdent,
+               x: PNimrodNode): PNimrodNode {.compileTime.} =
+  ## nests the list `x` into a tree of call expressions:
+  ## ``[a, b, c]`` is transformed into ``theProc(a, theProc(c, d))``.
+  var L = x.len
+  result = newCall(theProc, x[L-2], x[L-1])
+  var a = result
+  for i in countdown(L-3, 0):
+    a = newCall(theProc, x[i], copyNimTree(a))
+
+proc treeRepr*(n: PNimrodNode): string {.compileTime.} =
+  ## Convert the AST `n` to a human-readable tree-like string.
+  ##
+  ## See also `repr` and `lispRepr`.
+  proc traverse(res: var string, level: int, n: PNimrodNode) =
+    for i in 0..level-1: res.add "  "
+    res.add(($n.kind).substr(3))
+
+    case n.kind
+    of nnkEmpty: nil # same as nil node in this representation
+    of nnkNilLit: res.add(" nil")
+    of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal)
+    of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal)
+    of nnkStrLit..nnkTripleStrLit: res.add(" " & $n.strVal)
+    of nnkIdent: res.add(" !\"" & $n.ident & '"')
     of nnkSym: res.add(" \"" & $n.symbol & '"')
-    of nnkNone: assert false

-    else:

-      for j in 0..n.len-1:

-        res.add "\n"

-        traverse(res, level + 1, n[j])

-

-  result = ""

-  traverse(result, 0, n)

-

-proc lispRepr*(n: PNimrodNode): string {.compileTime.} =

-  ## Convert the AST `n` to a human-readable lisp-like string,

-  ##

-  ## See also `repr` and `treeRepr`.

-  

-  result = ($n.kind).substr(3)

-  add(result, "(")

-  

-  case n.kind

-  of nnkEmpty: nil # same as nil node in this representation

-  of nnkNilLit: add(result, "nil")

-  of nnkCharLit..nnkInt64Lit: add(result, $n.intVal)

-  of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)

-  of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal)

-  of nnkIdent: add(result, "!\"" & $n.ident & '"')

-  of nnkSym, nnkNone: assert false

-  else:

-    add(result, lispRepr(n[0]))

-    for j in 1..n.len-1:

-      add(result, ", ")

-      add(result, lispRepr(n[j]))

-

-  add(result, ")")

-

-macro dumpTree*(s: stmt): stmt = echo s.treeRepr

-  ## Accepts a block of nimrod code and prints the parsed abstract syntax

-  ## tree using the `toTree` function. Printing is done *at compile time*.

-  ##

-  ## You can use this as a tool to explore the Nimrod's abstract syntax 

-  ## tree and to discover what kind of nodes must be created to represent

-  ## a certain expression/statement.

-

-macro dumpLisp*(s: stmt): stmt = echo s.lispRepr

-  ## Accepts a block of nimrod code and prints the parsed abstract syntax

-  ## tree using the `toLisp` function. Printing is done *at compile time*.

-  ##

-  ## See `dumpTree`.

-

+    of nnkNone: assert false
+    else:
+      for j in 0..n.len-1:
+        res.add "\n"
+        traverse(res, level + 1, n[j])
+
+  result = ""
+  traverse(result, 0, n)
+
+proc lispRepr*(n: PNimrodNode): string {.compileTime.} =
+  ## Convert the AST `n` to a human-readable lisp-like string,
+  ##
+  ## See also `repr` and `treeRepr`.
+
+  result = ($n.kind).substr(3)
+  add(result, "(")
+
+  case n.kind
+  of nnkEmpty: nil # same as nil node in this representation
+  of nnkNilLit: add(result, "nil")
+  of nnkCharLit..nnkInt64Lit: add(result, $n.intVal)
+  of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)
+  of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal)
+  of nnkIdent: add(result, "!\"" & $n.ident & '"')
+  of nnkSym, nnkNone: assert false
+  else:
+    add(result, lispRepr(n[0]))
+    for j in 1..n.len-1:
+      add(result, ", ")
+      add(result, lispRepr(n[j]))
+
+  add(result, ")")
+
+macro dumpTree*(s: stmt): stmt = echo s.treeRepr
+  ## Accepts a block of nimrod code and prints the parsed abstract syntax
+  ## tree using the `toTree` function. Printing is done *at compile time*.
+  ##
+  ## You can use this as a tool to explore the Nimrod's abstract syntax
+  ## tree and to discover what kind of nodes must be created to represent
+  ## a certain expression/statement.
+
+macro dumpLisp*(s: stmt): stmt = echo s.lispRepr
+  ## Accepts a block of nimrod code and prints the parsed abstract syntax
+  ## tree using the `toLisp` function. Printing is done *at compile time*.
+  ##
+  ## See `dumpTree`.
+
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index 5a298db5b..68634e4ca 100755
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -149,7 +149,7 @@ else: # UNIX-like operating system
     FileSystemCaseSensitive* = true
     ExeExt* = ""
     ScriptExt* = ""
-    DynlibFormat* = "lib$1.so"
+    DynlibFormat* = when defined(macosx): "lib$1.dylib" else: "lib$1.so"
 
 when defined(macosx) or defined(bsd):
   var
diff --git a/lib/system.nim b/lib/system.nim
index 575c6abc7..99f1e621a 100755
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -51,10 +51,11 @@ type
   `nil` {.magic: "Nil".}
   expr* {.magic: Expr.} ## meta type to denote an expression (for templates)
   stmt* {.magic: Stmt.} ## meta type to denote a statement (for templates)
-  typeDesc* {.magic: TypeDesc.} ## meta type to denote
-                                ## a type description (for templates)
-  void* {.magic: "VoidType".}  ## meta type to denote the absense of any type
-  
+  typeDesc* {.magic: TypeDesc.} ## meta type to denote a type description
+  void* {.magic: "VoidType".}   ## meta type to denote the absense of any type
+  auto* = expr
+  any* = distinct auto
+
   TSignedInt* = int|int8|int16|int32|int64
     ## type class matching all signed integer types
 
@@ -111,6 +112,11 @@ proc new*[T](a: var ref T) {.magic: "New", noSideEffect.}
   ## creates a new object of type ``T`` and returns a safe (traced)
   ## reference to it in ``a``.
 
+proc new(T: typedesc): ref T =
+  ## creates a new object of type ``T`` and returns a safe (traced)
+  ## reference to it as result value
+  new(result)
+  
 proc internalNew*[T](a: var ref T) {.magic: "New", noSideEffect.}
   ## leaked implementation detail. Do not use.
 
@@ -538,7 +544,7 @@ proc abs*(x: int64): int64 {.magic: "AbsI64", noSideEffect.}
   ## checking is turned on).
 
 type
-  IntMax32 = distinct int|int8|int16|int32
+  IntMax32 = int|int8|int16|int32
 
 proc `+%` *(x, y: IntMax32): IntMax32 {.magic: "AddU", noSideEffect.}
 proc `+%` *(x, y: Int64): Int64 {.magic: "AddU", noSideEffect.}
@@ -1315,11 +1321,10 @@ iterator items*(a: cstring): char {.inline.} =
     yield a[i]
     inc(i)
 
-when not defined(booting):
-  iterator items*(E: typedesc[enum]): E =
-    ## iterates over the values of the enum ``E``.
-    for v in low(E)..high(E):
-      yield v
+iterator items*(E: typedesc[enum]): E =
+  ## iterates over the values of the enum ``E``.
+  for v in low(E)..high(E):
+    yield v
 
 iterator pairs*[T](a: openarray[T]): tuple[key: int, val: T] {.inline.} =
   ## iterates over each item of `a`. Yields ``(index, a[index])`` pairs.
@@ -2111,7 +2116,7 @@ proc `/`*(x, y: int): float {.inline, noSideEffect.} =
   ## integer division that results in a float.
   result = toFloat(x) / toFloat(y)
 
-template `-|`(b, s: expr): expr =
+template `-|`*(b, s: expr): expr =
   (if b >= 0: b else: s.len + b)
 
 proc `[]`*(s: string, x: TSlice[int]): string {.inline.} =
diff --git a/tests/compile/tisop.nim b/tests/compile/tisop.nim
new file mode 100644
index 000000000..509cc4e95
--- /dev/null
+++ b/tests/compile/tisop.nim
@@ -0,0 +1,42 @@
+import typetraits
+
+type
+  TRecord = (tuple) or (object)
+  
+  TFoo[T, U] = object
+    x: int
+
+    when T is string:
+      y: float
+    else:
+      y: string
+
+    when U is TRecord:
+      z: float
+
+  E = enum A, B, C
+
+macro m(t: typedesc): typedesc =
+  if t is enum:
+    result = string
+  else:
+    result = int
+
+var f: TFoo[int, int]
+static: assert(f.y.type.name == "string")
+
+when compiles(f.z):
+  {.error: "Foo should not have a `z` field".}
+
+proc p(a, b) =
+  when a.type is int:
+    static: assert false
+
+  var f: TFoo[m(a.type), b.type]
+  static:
+    assert f.x.type.name == "int"
+    assert f.y.type.name == "float"
+    assert f.z.type.name == "float"
+
+p(A, f)
+
diff --git a/tests/compile/tmacrotypes.nim b/tests/compile/tmacrotypes.nim
new file mode 100644
index 000000000..7697dba27
--- /dev/null
+++ b/tests/compile/tmacrotypes.nim
@@ -0,0 +1,12 @@
+import macros, typetraits
+
+macro checkType(ex, expected: expr): stmt {.immediate.} =
+  var t = ex.typ
+  assert t.name == expected.strVal
+
+proc voidProc = echo "hello"
+proc intProc(a, b): int = 10
+
+checkType(voidProc(), "void")
+checkType(intProc(10, 20.0), "int")
+checkType(noproc(10, 20.0), "Error Type")
diff --git a/tests/compile/ttypeclasses.nim b/tests/compile/ttypeclasses.nim
index e22ede1dc..5e23e8489 100644
--- a/tests/compile/ttypeclasses.nim
+++ b/tests/compile/ttypeclasses.nim
@@ -2,8 +2,8 @@ type
   TFoo[T] = object
     val: T
 
-  T1 = distinct expr
-  T2 = distinct expr
+  T1 = expr
+  T2 = expr
 
 proc takesExpr(x, y) =
   echo x, y
diff --git a/tests/reject/tcaseexpr1.nim b/tests/reject/tcaseexpr1.nim
new file mode 100644
index 000000000..e5e08e25e
--- /dev/null
+++ b/tests/reject/tcaseexpr1.nim
@@ -0,0 +1,30 @@
+discard """
+  file: "tcaseexpr1.nim"
+
+  line: 29
+  errormsg: "type mismatch: got (string) but expected 'int'"
+
+  line: 23
+  errormsg: "not all cases are covered"
+"""
+
+type
+  E = enum A, B, C
+
+proc foo(x): auto =
+  return case x
+    of 1..9: "digit"
+    else: "number"
+
+var r = foo(10)
+
+var x = C
+
+var t1 = case x:
+  of A: "a"
+  of B: "b"
+
+var t2 = case x:
+  of A: 10
+  of B, C: "23"
+
diff --git a/tests/reject/tenummix.nim b/tests/reject/tenummix.nim
index 55c0c05a9..77b7f44d2 100644
--- a/tests/reject/tenummix.nim
+++ b/tests/reject/tenummix.nim
@@ -1,6 +1,6 @@
 discard """
   file: "system.nim"
-  line: 643
+  line: 649
   errormsg: "type mismatch"
 """
 
diff --git a/tests/run/tcasestm.nim b/tests/run/tcasestm.nim
index cb63e0c51..003ec6e50 100755
--- a/tests/run/tcasestm.nim
+++ b/tests/run/tcasestm.nim
@@ -1,6 +1,6 @@
 discard """
   file: "tcasestm.nim"
-  output: "ayyy"
+  output: "ayyydd"
 """
 # Test the case statement
 
@@ -22,16 +22,18 @@ of "aa", "bb": write(stdout, "Du bist nicht mein Meister")
 of "cc", "hash", "when": nil
 of "will", "it", "finally", "be", "generated": nil
 
-case i
-of 1..5, 8, 9: nil
-of 6, 7: nil
-elif x == "Ha": 
-  nil
-elif x == "yyy":
-  write(stdout, x)
-else:
-  nil
-
+var z = case i
+  of 1..5, 8, 9: "aa"
+  of 6, 7: "bb"
+  elif x == "Ha": 
+    "cc"
+  elif x == "yyy":
+    write(stdout, x)
+    "dd"
+  else:
+    "zz"
+
+echo z
 #OUT ayyy
 
 
diff --git a/tests/run/trettypeinference.nim b/tests/run/trettypeinference.nim
new file mode 100644
index 000000000..41b4aa5ef
--- /dev/null
+++ b/tests/run/trettypeinference.nim
@@ -0,0 +1,33 @@
+discard """
+  msg:    "instantiated for string\ninstantiated for int\ninstantiated for bool"
+  output: "int\nseq[string]\nA\nB\n100\ntrue"
+"""
+
+import typetraits
+
+proc plus(a, b): auto = a + b
+proc makePair(a, b): auto = (first: a, second: b)
+
+proc `+`(a, b: string): seq[string] = @[a, b]
+
+var i = plus(10, 20)
+var s = plus("A", "B")
+
+var p = makePair("key", 100)
+static: assert p[0].type is string
+
+echo i.type.name
+echo s.type.name
+
+proc inst(a): auto =
+  static: echo "instantiated for ", a.type.name
+  result = a
+
+echo inst("A")
+echo inst("B")
+echo inst(100)
+echo inst(true)
+
+# XXX: [string, tyGenericParam] is cached instead of [string, string]
+# echo inst[string, string]("C")
+
diff --git a/web/news.txt b/web/news.txt
index fb133b6fe..a3ba80072 100755
--- a/web/news.txt
+++ b/web/news.txt
@@ -2,6 +2,39 @@
 News
 ====
 
+2012-XX-XX Version 0.9.XX released
+==================================
+
+Version 0.8.XX has been released! Get it `here <download.html>`_. 
+
+Bugfixes
+--------
+
+
+
+Library Additions
+-----------------
+
+
+
+Changes affecting backwards compatibility
+-----------------------------------------
+
+
+
+Compiler Additions
+------------------
+
+
+
+Language Additions
+------------------
+
+- ``case expressions`` are now supported.
+- table constructors now mimic more closely the syntax of case... of...
+- Nimrod can now infer the return type of a proc from its body
+
+
 2012-09-23 Version 0.9.0 released
 =================================