summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ast.nim15
-rw-r--r--compiler/ccgcalls.nim2
-rw-r--r--compiler/ccgexprs.nim6
-rw-r--r--compiler/ccgstmts.nim7
-rw-r--r--compiler/ccgutils.nim2
-rw-r--r--compiler/cgen.nim46
-rw-r--r--compiler/cgendata.nim2
-rw-r--r--compiler/commands.nim5
-rw-r--r--compiler/docgen.nim284
-rw-r--r--compiler/evals.nim4
-rw-r--r--compiler/extccomp.nim37
-rw-r--r--compiler/jsgen.nim2
-rw-r--r--compiler/lists.nim18
-rw-r--r--compiler/main.nim62
-rw-r--r--compiler/nimconf.nim5
-rw-r--r--compiler/nimrod.dot591
-rw-r--r--compiler/nimrod.nim26
-rw-r--r--compiler/nimrod.nimrod.cfg (renamed from compiler/nimrod.cfg)1
-rw-r--r--compiler/options.nim2
-rw-r--r--compiler/pragmas.nim8
-rw-r--r--compiler/renderer.nim9
-rw-r--r--compiler/ropes.nim5
-rw-r--r--compiler/sem.nim50
-rw-r--r--compiler/semexprs.nim18
-rw-r--r--compiler/seminst.nim5
-rw-r--r--compiler/semstmts.nim2
-rw-r--r--compiler/semthreads.nim3
-rw-r--r--compiler/semtypes.nim9
-rw-r--r--compiler/sigmatch.nim248
-rw-r--r--compiler/testability.nim5
-rw-r--r--compiler/types.nim25
-rw-r--r--compiler/wordrecg.nim5
32 files changed, 658 insertions, 851 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 5a5d87d06..0790df0c4 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -238,7 +238,7 @@ type
     sfMainModule,     # module is the main module
     sfSystemModule,   # module is the system module
     sfNoReturn,       # proc never returns (an exit proc)
-    sfAddrTaken,      # the variable's address is taken (ex- or implicitely);
+    sfAddrTaken,      # the variable's address is taken (ex- or implicitly);
                       # *OR*: a proc is indirectly called (used as first class)
     sfCompilerProc,   # proc is a compiler proc, that is a C proc that is
                       # needed for the code generator
@@ -257,7 +257,7 @@ type
                       # for interfacing with C++, JS
     sfNamedParamCall, # symbol needs named parameter call syntax in target
                       # language; for interfacing with Objective C
-    sfDiscardable,    # returned value may be discarded implicitely
+    sfDiscardable,    # returned value may be discarded implicitly
     sfDestructor,     # proc is destructor
     sfGenSym          # symbol is 'gensym'ed; do not add to symbol table
 
@@ -335,12 +335,19 @@ type
     tyConst, tyMutable, tyVarargs, 
     tyIter, # unused
     tyProxy # used as errornous type (for idetools)
-    tyTypeClass,
+    tyTypeClass
+    tyAnd
+    tyOr
+    tyNot
+    tyAnything
+    tyParametricTypeClass # structured similarly to tyGenericInst
+                          # lastSon is the body of the type class
 
 const
   tyPureObject* = tyTuple
   GcTypeKinds* = {tyRef, tySequence, tyString}
   tyError* = tyProxy # as an errornous node should match everything
+  tyTypeClasses* = {tyTypeClass, tyParametricTypeClass, tyAnd, tyOr, tyNot, tyAnything}
 
 type
   TTypeKinds* = set[TTypeKind]
@@ -377,6 +384,7 @@ type
                       # 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
+    tfNot,            # type class with a negative check
     tfCapturesEnv,    # whether proc really captures some environment
     tfByCopy,         # pass object/tuple by copy (C backend)
     tfByRef,          # pass object/tuple by reference (C backend)
@@ -1416,3 +1424,4 @@ proc isAtom*(n: PNode): bool {.inline.} =
 proc isEmptyType*(t: PType): bool {.inline.} =
   ## 'void' and 'stmt' types are often equivalent to 'nil' these days:
   result = t == nil or t.kind in {tyEmpty, tyStmt}
+
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
index 1d6df3c15..07fba95a3 100644
--- a/compiler/ccgcalls.nim
+++ b/compiler/ccgcalls.nim
@@ -290,6 +290,7 @@ proc genCall(p: BProc, e: PNode, d: var TLoc) =
     genNamedParamCall(p, e, d)
   else:
     genPrefixCall(p, nil, e, d)
+  postStmtActions(p)
   when false:
     if d.s == onStack and containsGarbageCollectedRef(d.t): keepAlive(p, d)
 
@@ -303,6 +304,7 @@ proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
     genNamedParamCall(p, ri, d)
   else:
     genPrefixCall(p, le, ri, d)
+  postStmtActions(p)
   when false:
     if d.s == onStack and containsGarbageCollectedRef(d.t): keepAlive(p, d)
 
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 15f7f0f34..daa0c8d88 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -254,6 +254,7 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
   if needToCopy notin flags or 
       tfShallow in skipTypes(dest.t, abstractVarRange).flags:
     if dest.s == OnStack or not usesNativeGC():
+      useStringh(p.module)
       linefmt(p, cpsStmts,
            "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
            addrLoc(dest), addrLoc(src), rdLoc(dest))
@@ -330,6 +331,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
     if needsComplexAssignment(dest.t):
       genGenericAsgn(p, dest, src, flags)
     else:
+      useStringh(p.module)
       linefmt(p, cpsStmts,
            "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($1));$n",
            rdLoc(dest), rdLoc(src))
@@ -341,11 +343,13 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
            "#genericAssignOpenArray((void*)$1, (void*)$2, $1Len0, $3);$n",
            addrLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t))
     else:
+      useStringh(p.module)
       linefmt(p, cpsStmts,
            "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($1[0])*$1Len0);$n",
            rdLoc(dest), rdLoc(src))
   of tySet:
     if mapType(ty) == ctArray:
+      useStringh(p.module)
       linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, $3);$n",
               rdLoc(dest), rdLoc(src), toRope(getSize(dest.t)))
     else:
@@ -1391,6 +1395,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
       lineF(p, cpsStmts, lookupOpr[op],
            [rdLoc(i), toRope(size), rdLoc(d), rdLoc(a), rdLoc(b)])
     of mEqSet:
+      useStringh(p.module)
       binaryExprChar(p, e, d, "(memcmp($1, $2, " & $(size) & ")==0)")
     of mMulSet, mPlusSet, mMinusSet, mSymDiffSet:
       # we inline the simple for loop for better code generation:
@@ -1642,6 +1647,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
     if d.k == locNone: getTemp(p, e.typ, d)
     if getSize(e.typ) > 8:
       # big set:
+      useStringh(p.module)
       lineF(p, cpsStmts, "memset($1, 0, sizeof($1));$n", [rdLoc(d)])
       for i in countup(0, sonsLen(e) - 1):
         if e.sons[i].kind == nkRange:
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 75cabf414..ac4bbb79f 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -906,7 +906,12 @@ proc genPragma(p: BProc, n: PNode) =
     of wEmit: genEmit(p, it)
     of wBreakpoint: genBreakPoint(p, it)
     of wWatchpoint: genWatchpoint(p, it)
-    else: nil
+    of wInjectStmt: 
+      var p = newProc(nil, p.module)
+      p.options = p.options - {optLineTrace, optStackTrace}
+      genStmts(p, it.sons[1])
+      p.module.injectStmt = p.s(cpsStmts)
+    else: discard
 
 proc FieldDiscriminantCheckNeeded(p: BProc, asgn: PNode): bool = 
   if optFieldCheck in p.options:
diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim
index c37754511..310f7204a 100644
--- a/compiler/ccgutils.nim
+++ b/compiler/ccgutils.nim
@@ -86,7 +86,7 @@ proc GetUniqueType*(key: PType): PType =
     if result == nil:
       gCanonicalTypes[k] = key
       result = key
-  of tyTypeDesc, tyTypeClass:
+  of tyTypeDesc, tyTypeClasses:
     InternalError("value expected, but got a type")
   of tyGenericParam:
     InternalError("GetUniqueType")
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 910e675e1..6ccef5fde 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -72,6 +72,11 @@ proc isSimpleConst(typ: PType): bool =
       {tyTuple, tyObject, tyArray, tyArrayConstr, tySet, tySequence} and not
       (t.kind == tyProc and t.callConv == ccClosure)
 
+proc useStringh(m: BModule) =
+  if not m.includesStringh:
+    m.includesStringh = true
+    discard lists.IncludeStr(m.headerFiles, "<string.h>")
+
 proc useHeader(m: BModule, sym: PSym) = 
   if lfHeader in sym.loc.Flags: 
     assert(sym.annex != nil)
@@ -284,6 +289,9 @@ proc genLineDir(p: BProc, t: PNode) =
     linefmt(p, cpsStmts, "nimln($1, $2);$n",
             line.toRope, t.info.quotedFilename)
 
+proc postStmtActions(p: BProc) {.inline.} =
+  app(p.s(cpsStmts), p.module.injectStmt)
+
 proc accessThreadLocalVar(p: BProc, s: PSym)
 proc emulatedThreadVars(): bool {.inline.}
 
@@ -358,19 +366,24 @@ proc resetLoc(p: BProc, loc: var TLoc) =
       # field, so disabling this should be safe:
       genObjectInit(p, cpsStmts, loc.t, loc, true)
     else:
+      useStringh(p.module)
       linefmt(p, cpsStmts, "memset((void*)$1, 0, sizeof($2));$n",
               addrLoc(loc), rdLoc(loc))
       # XXX: We can be extra clever here and call memset only 
       # on the bytes following the m_type field?
       genObjectInit(p, cpsStmts, loc.t, loc, true)
 
-proc constructLoc(p: BProc, loc: TLoc, section = cpsStmts) =
+proc constructLoc(p: BProc, loc: TLoc, isTemp = false) =
   if not isComplexValueType(skipTypes(loc.t, abstractRange)):
-    linefmt(p, section, "$1 = 0;$n", rdLoc(loc))
+    linefmt(p, cpsStmts, "$1 = 0;$n", rdLoc(loc))
   else:
-    linefmt(p, section, "memset((void*)$1, 0, sizeof($2));$n",
-            addrLoc(loc), rdLoc(loc))
-    genObjectInit(p, section, loc.t, loc, true)
+    if not isTemp or containsGarbageCollectedRef(loc.t):
+      # don't use memset for temporary values for performance if we can
+      # avoid it:
+      useStringh(p.module)
+      linefmt(p, cpsStmts, "memset((void*)$1, 0, sizeof($2));$n",
+              addrLoc(loc), rdLoc(loc))
+    genObjectInit(p, cpsStmts, loc.t, loc, true)
 
 proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) =
   if sfNoInit notin v.flags:
@@ -396,7 +409,7 @@ proc getTemp(p: BProc, t: PType, result: var TLoc) =
   result.t = getUniqueType(t)
   result.s = OnStack
   result.flags = {}
-  constructLoc(p, result)
+  constructLoc(p, result, isTemp=true)
 
 proc keepAlive(p: BProc, toKeepAlive: TLoc) =
   when false:
@@ -418,6 +431,7 @@ proc keepAlive(p: BProc, toKeepAlive: TLoc) =
     if not isComplexValueType(skipTypes(toKeepAlive.t, abstractVarRange)):
       linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(result), rdLoc(toKeepAlive))
     else:
+      useStringh(p.module)
       linefmt(p, cpsStmts,
            "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
            addrLoc(result), addrLoc(toKeepAlive), rdLoc(result))
@@ -933,9 +947,9 @@ proc genMainProc(m: BModule) =
   const 
     CommonMainBody =
         "\tsystemDatInit();$n" &
+        "\tsystemInit();$n" &
         "$1" &
         "$2" &
-        "\tsystemInit();$n" &
         "$3" &
         "$4"
     PosixNimMain = 
@@ -1108,6 +1122,9 @@ proc newPostInitProc(m: BModule): BProc =
   # little hack so that unique temporaries are generated:
   result.labels = 200_000
 
+proc initProcOptions(m: BModule): TOptions = 
+  if sfSystemModule in m.module.flags: gOptions-{optStackTrace} else: gOptions
+
 proc rawNewModule(module: PSym, filename: string): BModule =
   new(result)
   InitLinkedList(result.headerFiles)
@@ -1120,7 +1137,7 @@ proc rawNewModule(module: PSym, filename: string): BModule =
   result.module = module
   result.typeInfoMarker = initIntSet()
   result.initProc = newProc(nil, result)
-  result.initProc.options = gOptions
+  result.initProc.options = initProcOptions(result)
   result.preInitProc = newPreInitProc(result)
   result.postInitProc = newPostInitProc(result)
   initNodeTable(result.dataCache)
@@ -1128,7 +1145,12 @@ proc rawNewModule(module: PSym, filename: string): BModule =
   result.forwardedProcs = @[]
   result.typeNodesName = getTempName()
   result.nimTypesName = getTempName()
-  result.PreventStackTrace = sfSystemModule in module.flags
+  # no line tracing for the init sections of the system module so that we
+  # don't generate a TFrame which can confuse the stack botton initialization:
+  if sfSystemModule in module.flags:
+    result.PreventStackTrace = true
+    excl(result.preInitProc.options, optStackTrace)
+    excl(result.postInitProc.options, optStackTrace)
 
 proc nullify[T](arr: var T) =
   for i in low(arr)..high(arr):
@@ -1141,7 +1163,7 @@ proc resetModule*(m: var BModule) =
   m.declaredProtos = initIntSet()
   initIdTable(m.forwTypeCache)
   m.initProc = newProc(nil, m)
-  m.initProc.options = gOptions
+  m.initProc.options = initProcOptions(m)
   m.preInitProc = newPreInitProc(m)
   m.postInitProc = newPostInitProc(m)
   initNodeTable(m.dataCache)
@@ -1231,7 +1253,7 @@ proc myProcess(b: PPassContext, n: PNode): PNode =
   result = n
   if b == nil or passes.skipCodegen(n): return
   var m = BModule(b)
-  m.initProc.options = gOptions
+  m.initProc.options = initProcOptions(m)
   genStmts(m.initProc, n)
 
 proc finishModule(m: BModule) = 
@@ -1318,7 +1340,7 @@ proc myClose(b: PPassContext, n: PNode): PNode =
   if b == nil or passes.skipCodegen(n): return 
   var m = BModule(b)
   if n != nil: 
-    m.initProc.options = gOptions
+    m.initProc.options = initProcOptions(m)
     genStmts(m.initProc, n)
   # cached modules need to registered too: 
   registerModuleToMain(m.module)
diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim
index b5888d0f4..a803c0ba1 100644
--- a/compiler/cgendata.nim
+++ b/compiler/cgendata.nim
@@ -91,6 +91,7 @@ type
     FrameDeclared*: bool      # hack for ROD support so that we don't declare
                               # a frame var twice in an init proc
     isHeaderFile*: bool       # C source file is the header file
+    includesStringh*: bool    # C source file already includes ``<string.h>``
     cfilename*: string        # filename of the module (including path,
                               # without extension)
     typeCache*: TIdTable      # cache the generated types
@@ -110,6 +111,7 @@ type
     labels*: natural          # for generating unique module-scope names
     extensionLoaders*: array['0'..'9', PRope] # special procs for the
                                               # OpenGL wrapper
+    injectStmt*: PRope
 
 var
   mainModProcs*, mainModInit*, mainDatInit*: PRope # parts of the main module
diff --git a/compiler/commands.nim b/compiler/commands.nim
index d3266930b..fa7841275 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -249,10 +249,13 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) =
     expectArg(switch, arg, pass, info)
     addPath(processPath(arg), info)
   of "babelpath":
-    if pass in {passCmd2, passPP}:
+    if pass in {passCmd2, passPP} and not options.gNoBabelPath:
       expectArg(switch, arg, pass, info)
       let path = processPath(arg, notRelativeToProj=true)
       babelpath(path, info)
+  of "nobabelpath":
+    expectNoArg(switch, arg, pass, info)
+    options.gNoBabelPath = true
   of "excludepath":
     expectArg(switch, arg, pass, info)
     let path = processPath(arg)
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index 9929b4bd9..d44018a2b 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -11,10 +11,10 @@
 # semantic checking is done for the code. Cross-references are generated
 # by knowing how the anchors are going to be named.
 
-import 
-  ast, strutils, strtabs, options, msgs, os, ropes, idents, 
-  wordrecg, syntaxes, renderer, lexer, rstast, rst, rstgen, times, highlite, 
-  importer, sempass2
+import
+  ast, strutils, strtabs, options, msgs, os, ropes, idents,
+  wordrecg, syntaxes, renderer, lexer, rstast, rst, rstgen, times, highlite,
+  importer, sempass2, json
 
 type
   TSections = array[TSymKind, PRope]
@@ -25,7 +25,7 @@ type
     indexValFilename: string
 
   PDoc* = ref TDocumentor
-  
+
 proc compilerMsgHandler(filename: string, line, col: int,
                         msgKind: rst.TMsgKind, arg: string) {.procvar.} =
   # translate msg kind:
@@ -41,69 +41,69 @@ proc compilerMsgHandler(filename: string, line, col: int,
   of mwUnknownSubstitution: k = warnUnknownSubstitutionX
   of mwUnsupportedLanguage: k = warnLanguageXNotSupported
   GlobalError(newLineInfo(filename, line, col), k, arg)
-  
+
 proc parseRst(text, filename: string,
               line, column: int, hasToc: var bool,
               rstOptions: TRstParseOptions): PRstNode =
   result = rstParse(text, filename, line, column, hasToc, rstOptions,
                     options.FindFile, compilerMsgHandler)
 
-proc newDocumentor*(filename: string, config: PStringTable): PDoc = 
+proc newDocumentor*(filename: string, config: PStringTable): PDoc =
   new(result)
   initRstGenerator(result[], (if gCmd != cmdRst2Tex: outHtml else: outLatex),
                    options.gConfigVars, filename, {roSupportRawDirective},
                    options.FindFile, compilerMsgHandler)
   result.id = 100
 
-proc dispA(dest: var PRope, xml, tex: string, args: openarray[PRope]) = 
+proc dispA(dest: var PRope, xml, tex: string, args: openarray[PRope]) =
   if gCmd != cmdRst2Tex: appf(dest, xml, args)
   else: appf(dest, tex, args)
-  
-proc getVarIdx(varnames: openarray[string], id: string): int = 
-  for i in countup(0, high(varnames)): 
-    if cmpIgnoreStyle(varnames[i], id) == 0: 
+
+proc getVarIdx(varnames: openarray[string], id: string): int =
+  for i in countup(0, high(varnames)):
+    if cmpIgnoreStyle(varnames[i], id) == 0:
       return i
   result = -1
 
-proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openarray[string], 
-                         varvalues: openarray[PRope]): PRope = 
+proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openarray[string],
+                         varvalues: openarray[PRope]): PRope =
   var i = 0
   var L = len(frmt)
   result = nil
   var num = 0
-  while i < L: 
-    if frmt[i] == '$': 
+  while i < L:
+    if frmt[i] == '$':
       inc(i)                  # skip '$'
       case frmt[i]
-      of '#': 
+      of '#':
         app(result, varvalues[num])
         inc(num)
         inc(i)
-      of '$': 
+      of '$':
         app(result, "$")
         inc(i)
-      of '0'..'9': 
+      of '0'..'9':
         var j = 0
-        while true: 
+        while true:
           j = (j * 10) + Ord(frmt[i]) - ord('0')
           inc(i)
-          if (i > L + 0 - 1) or not (frmt[i] in {'0'..'9'}): break 
+          if (i > L + 0 - 1) or not (frmt[i] in {'0'..'9'}): break
         if j > high(varvalues) + 1: internalError("ropeFormatNamedVars")
         num = j
         app(result, varvalues[j - 1])
-      of 'A'..'Z', 'a'..'z', '\x80'..'\xFF': 
+      of 'A'..'Z', 'a'..'z', '\x80'..'\xFF':
         var id = ""
-        while true: 
+        while true:
           add(id, frmt[i])
           inc(i)
-          if not (frmt[i] in {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}): break 
+          if not (frmt[i] in {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}): break
         var idx = getVarIdx(varnames, id)
         if idx >= 0: app(result, varvalues[idx])
         else: rawMessage(errUnkownSubstitionVar, id)
-      of '{': 
+      of '{':
         var id = ""
         inc(i)
-        while frmt[i] != '}': 
+        while frmt[i] != '}':
           if frmt[i] == '\0': rawMessage(errTokenExpected, "}")
           add(id, frmt[i])
           inc(i)
@@ -124,17 +124,17 @@ proc genComment(d: PDoc, n: PNode): string =
   var dummyHasToc: bool
   if n.comment != nil and startsWith(n.comment, "##"):
     renderRstToOut(d[], parseRst(n.comment, toFilename(n.info),
-                               toLineNumber(n.info), toColumn(n.info), 
+                               toLineNumber(n.info), toColumn(n.info),
                                dummyHasToc, d.options + {roSkipPounds}), result)
 
-proc genRecComment(d: PDoc, n: PNode): PRope = 
+proc genRecComment(d: PDoc, n: PNode): PRope =
   if n == nil: return nil
   result = genComment(d, n).toRope
-  if result == nil: 
+  if result == nil:
     if n.kind notin {nkEmpty..nkNilLit}:
       for i in countup(0, len(n)-1):
         result = genRecComment(d, n.sons[i])
-        if result != nil: return 
+        if result != nil: return
   else:
     n.comment = nil
 
@@ -158,10 +158,10 @@ proc extractDocComment*(s: PSym, d: PDoc = nil): string =
     else:
       result = n.comment.substr(2).replace("\n##", "\n").strip
 
-proc isVisible(n: PNode): bool = 
+proc isVisible(n: PNode): bool =
   result = false
-  if n.kind == nkPostfix: 
-    if n.len == 2 and n.sons[0].kind == nkIdent: 
+  if n.kind == nkPostfix:
+    if n.len == 2 and n.sons[0].kind == nkIdent:
       var v = n.sons[0].ident
       result = v.id == ord(wStar) or v.id == ord(wMinus)
   elif n.kind == nkSym:
@@ -171,36 +171,36 @@ proc isVisible(n: PNode): bool =
     result = {sfExported, sfFromGeneric, sfForward}*n.sym.flags == {sfExported}
   elif n.kind == nkPragmaExpr:
     result = isVisible(n.sons[0])
-    
-proc getName(d: PDoc, n: PNode, splitAfter = -1): string = 
+
+proc getName(d: PDoc, n: PNode, splitAfter = -1): string =
   case n.kind
   of nkPostfix: result = getName(d, n.sons[1], splitAfter)
   of nkPragmaExpr: result = getName(d, n.sons[0], splitAfter)
   of nkSym: result = esc(d.target, n.sym.renderDefinitionName, splitAfter)
   of nkIdent: result = esc(d.target, n.ident.s, splitAfter)
-  of nkAccQuoted: 
-    result = esc(d.target, "`") 
+  of nkAccQuoted:
+    result = esc(d.target, "`")
     for i in 0.. <n.len: result.add(getName(d, n[i], splitAfter))
     result.add esc(d.target, "`")
   else:
     internalError(n.info, "getName()")
     result = ""
 
-proc getRstName(n: PNode): PRstNode = 
+proc getRstName(n: PNode): PRstNode =
   case n.kind
   of nkPostfix: result = getRstName(n.sons[1])
   of nkPragmaExpr: result = getRstName(n.sons[0])
   of nkSym: result = newRstNode(rnLeaf, n.sym.renderDefinitionName)
   of nkIdent: result = newRstNode(rnLeaf, n.ident.s)
-  of nkAccQuoted: 
+  of nkAccQuoted:
     result = getRstName(n.sons[0])
     for i in 1 .. <n.len: result.text.add(getRstName(n[i]).text)
   else:
     internalError(n.info, "getRstName()")
     result = nil
 
-proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) = 
-  if not isVisible(nameNode): return 
+proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
+  if not isVisible(nameNode): return
   var name = toRope(getName(d, nameNode))
   var result: PRope = nil
   var literal = ""
@@ -208,73 +208,89 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
   var comm = genRecComment(d, n)  # call this here for the side-effect!
   var r: TSrcGen
   initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments})
-  while true: 
+  while true:
     getNextTok(r, kind, literal)
     case kind
-    of tkEof: 
-      break 
-    of tkComment: 
-      dispA(result, "<span class=\"Comment\">$1</span>", "\\spanComment{$1}", 
+    of tkEof:
+      break
+    of tkComment:
+      dispA(result, "<span class=\"Comment\">$1</span>", "\\spanComment{$1}",
             [toRope(esc(d.target, literal))])
-    of tokKeywordLow..tokKeywordHigh: 
-      dispA(result, "<span class=\"Keyword\">$1</span>", "\\spanKeyword{$1}", 
+    of tokKeywordLow..tokKeywordHigh:
+      dispA(result, "<span class=\"Keyword\">$1</span>", "\\spanKeyword{$1}",
             [toRope(literal)])
-    of tkOpr: 
-      dispA(result, "<span class=\"Operator\">$1</span>", "\\spanOperator{$1}", 
+    of tkOpr:
+      dispA(result, "<span class=\"Operator\">$1</span>", "\\spanOperator{$1}",
             [toRope(esc(d.target, literal))])
-    of tkStrLit..tkTripleStrLit: 
-      dispA(result, "<span class=\"StringLit\">$1</span>", 
+    of tkStrLit..tkTripleStrLit:
+      dispA(result, "<span class=\"StringLit\">$1</span>",
             "\\spanStringLit{$1}", [toRope(esc(d.target, literal))])
-    of tkCharLit: 
-      dispA(result, "<span class=\"CharLit\">$1</span>", "\\spanCharLit{$1}", 
+    of tkCharLit:
+      dispA(result, "<span class=\"CharLit\">$1</span>", "\\spanCharLit{$1}",
             [toRope(esc(d.target, literal))])
-    of tkIntLit..tkUInt64Lit: 
-      dispA(result, "<span class=\"DecNumber\">$1</span>", 
+    of tkIntLit..tkUInt64Lit:
+      dispA(result, "<span class=\"DecNumber\">$1</span>",
             "\\spanDecNumber{$1}", [toRope(esc(d.target, literal))])
-    of tkFloatLit..tkFloat128Lit: 
-      dispA(result, "<span class=\"FloatNumber\">$1</span>", 
+    of tkFloatLit..tkFloat128Lit:
+      dispA(result, "<span class=\"FloatNumber\">$1</span>",
             "\\spanFloatNumber{$1}", [toRope(esc(d.target, literal))])
-    of tkSymbol: 
-      dispA(result, "<span class=\"Identifier\">$1</span>", 
+    of tkSymbol:
+      dispA(result, "<span class=\"Identifier\">$1</span>",
             "\\spanIdentifier{$1}", [toRope(esc(d.target, literal))])
-    of tkSpaces, tkInvalid: 
+    of tkSpaces, tkInvalid:
       app(result, literal)
-    of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi, 
-       tkBracketDotLe, tkBracketDotRi, tkCurlyDotLe, tkCurlyDotRi, tkParDotLe, 
-       tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot, 
-       tkAccent, tkColonColon, 
-       tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr: 
-      dispA(result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}", 
+    of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi,
+       tkBracketDotLe, tkBracketDotRi, tkCurlyDotLe, tkCurlyDotRi, tkParDotLe,
+       tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot,
+       tkAccent, tkColonColon,
+       tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr:
+      dispA(result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}",
             [toRope(esc(d.target, literal))])
   inc(d.id)
-  app(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"), 
-                                        ["name", "header", "desc", "itemID"], 
+  app(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"),
+                                        ["name", "header", "desc", "itemID"],
                                         [name, result, comm, toRope(d.id)]))
-  app(d.toc[k], ropeFormatNamedVars(getConfigVar("doc.item.toc"), 
+  app(d.toc[k], ropeFormatNamedVars(getConfigVar("doc.item.toc"),
                                     ["name", "header", "desc", "itemID"], [
       toRope(getName(d, nameNode, d.splitAfter)), result, comm, toRope(d.id)]))
   setIndexTerm(d[], $d.id, getName(d, nameNode))
 
-proc checkForFalse(n: PNode): bool = 
+proc genJSONItem(d: PDoc, n, nameNode: PNode, k: TSymKind): PJsonNode =
+  if not isVisible(nameNode): return
+  var
+    name = getName(d, nameNode)
+    comm = genRecComment(d, n).ropeToStr()
+    r: TSrcGen
+
+  initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments})
+
+  result = %{ "name": %name, "type": %($k) }
+
+  if comm != nil and comm != "":
+    result["description"] = %comm
+  if r.buf != nil:
+    result["code"] = %r.buf
+
+proc checkForFalse(n: PNode): bool =
   result = n.kind == nkIdent and IdentEq(n.ident, "false")
-  
-proc traceDeps(d: PDoc, n: PNode) = 
+
+proc traceDeps(d: PDoc, n: PNode) =
   const k = skModule
   if d.section[k] != nil: app(d.section[k], ", ")
-  dispA(d.section[k], 
-        "<a class=\"reference external\" href=\"$1.html\">$1</a>", 
+  dispA(d.section[k],
+        "<a class=\"reference external\" href=\"$1.html\">$1</a>",
         "$1", [toRope(getModuleName(n))])
 
-proc generateDoc*(d: PDoc, n: PNode) = 
+proc generateDoc*(d: PDoc, n: PNode) =
   case n.kind
   of nkCommentStmt: app(d.modDesc, genComment(d, n))
-  of nkProcDef: 
+  of nkProcDef:
     when useEffectSystem: documentRaises(n)
     genItem(d, n, n.sons[namePos], skProc)
   of nkMethodDef:
     when useEffectSystem: documentRaises(n)
     genItem(d, n, n.sons[namePos], skMethod)
-  of nkIteratorDef: 
+  of nkIteratorDef:
     when useEffectSystem: documentRaises(n)
     genItem(d, n, n.sons[namePos], skIterator)
   of nkMacroDef: genItem(d, n, n.sons[namePos], skMacro)
@@ -284,27 +300,69 @@ proc generateDoc*(d: PDoc, n: PNode) =
     genItem(d, n, n.sons[namePos], skConverter)
   of nkTypeSection, nkVarSection, nkLetSection, nkConstSection:
     for i in countup(0, sonsLen(n) - 1):
-      if n.sons[i].kind != nkCommentStmt: 
+      if n.sons[i].kind != nkCommentStmt:
         # order is always 'type var let const':
-        genItem(d, n.sons[i], n.sons[i].sons[0], 
+        genItem(d, n.sons[i], n.sons[i].sons[0],
                 succ(skType, ord(n.kind)-ord(nkTypeSection)))
-  of nkStmtList: 
+  of nkStmtList:
     for i in countup(0, sonsLen(n) - 1): generateDoc(d, n.sons[i])
-  of nkWhenStmt: 
+  of nkWhenStmt:
     # generate documentation for the first branch only:
     if not checkForFalse(n.sons[0].sons[0]):
       generateDoc(d, lastSon(n.sons[0]))
   of nkImportStmt:
-    for i in 0 .. sonsLen(n)-1: traceDeps(d, n.sons[i]) 
+    for i in 0 .. sonsLen(n)-1: traceDeps(d, n.sons[i])
   of nkFromStmt, nkImportExceptStmt: traceDeps(d, n.sons[0])
   else: nil
 
-proc genSection(d: PDoc, kind: TSymKind) = 
+proc generateJson(d: PDoc, n: PNode, jArray: PJsonNode = nil): PJsonNode =
+  case n.kind
+  of nkCommentStmt:
+    if n.comment != nil and startsWith(n.comment, "##"):
+      let stripped = n.comment.substr(2).strip
+      result = %{ "comment": %stripped }
+  of nkProcDef:
+    when useEffectSystem: documentRaises(n)
+    result = genJSONItem(d, n, n.sons[namePos], skProc)
+  of nkMethodDef:
+    when useEffectSystem: documentRaises(n)
+    result = genJSONItem(d, n, n.sons[namePos], skMethod)
+  of nkIteratorDef:
+    when useEffectSystem: documentRaises(n)
+    result = genJSONItem(d, n, n.sons[namePos], skIterator)
+  of nkMacroDef:
+    result = genJSONItem(d, n, n.sons[namePos], skMacro)
+  of nkTemplateDef:
+    result = genJSONItem(d, n, n.sons[namePos], skTemplate)
+  of nkConverterDef:
+    when useEffectSystem: documentRaises(n)
+    result = genJSONItem(d, n, n.sons[namePos], skConverter)
+  of nkTypeSection, nkVarSection, nkLetSection, nkConstSection:
+    for i in countup(0, sonsLen(n) - 1):
+      if n.sons[i].kind != nkCommentStmt:
+        # order is always 'type var let const':
+        result = genJSONItem(d, n.sons[i], n.sons[i].sons[0],
+                succ(skType, ord(n.kind)-ord(nkTypeSection)))
+  of nkStmtList:
+    var elem = jArray
+    if elem == nil: elem = newJArray()
+    for i in countup(0, sonsLen(n) - 1):
+      var r = generateJson(d, n.sons[i], elem)
+      if r != nil:
+        elem.add(r)
+        if result == nil: result = elem
+  of nkWhenStmt:
+    # generate documentation for the first branch only:
+    if not checkForFalse(n.sons[0].sons[0]) and jArray != nil:
+      discard generateJson(d, lastSon(n.sons[0]), jArray)
+  else: nil
+
+proc genSection(d: PDoc, kind: TSymKind) =
   const sectionNames: array[skModule..skTemplate, string] = [
-    "Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Methods", 
+    "Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Methods",
     "Iterators", "Converters", "Macros", "Templates"
   ]
-  if d.section[kind] == nil: return 
+  if d.section[kind] == nil: return
   var title = sectionNames[kind].toRope
   d.section[kind] = ropeFormatNamedVars(getConfigVar("doc.section"), [
       "sectionid", "sectionTitle", "sectionTitleID", "content"], [
@@ -313,7 +371,7 @@ proc genSection(d: PDoc, kind: TSymKind) =
       "sectionid", "sectionTitle", "sectionTitleID", "content"], [
       ord(kind).toRope, title, toRope(ord(kind) + 50), d.toc[kind]])
 
-proc genOutFile(d: PDoc): PRope = 
+proc genOutFile(d: PDoc): PRope =
   var
     code, content: PRope
     title = ""
@@ -321,7 +379,7 @@ proc genOutFile(d: PDoc): PRope =
   var tmp = ""
   renderTocEntries(d[], j, 1, tmp)
   var toc = tmp.toRope
-  for i in countup(low(TSymKind), high(TSymKind)): 
+  for i in countup(low(TSymKind), high(TSymKind)):
     genSection(d, i)
     app(toc, d.toc[i])
   if toc != nil:
@@ -329,30 +387,30 @@ proc genOutFile(d: PDoc): PRope =
   for i in countup(low(TSymKind), high(TSymKind)): app(code, d.section[i])
   if d.meta[metaTitle].len != 0: title = d.meta[metaTitle]
   else: title = "Module " & extractFilename(changeFileExt(d.filename, ""))
-  
+
   let bodyname = if d.hasToc: "doc.body_toc" else: "doc.body_no_toc"
-  content = ropeFormatNamedVars(getConfigVar(bodyname), ["title", 
+  content = ropeFormatNamedVars(getConfigVar(bodyname), ["title",
       "tableofcontents", "moduledesc", "date", "time", "content"],
-      [title.toRope, toc, d.modDesc, toRope(getDateStr()), 
+      [title.toRope, toc, d.modDesc, toRope(getDateStr()),
       toRope(getClockStr()), code])
-  if optCompileOnly notin gGlobalOptions: 
+  if optCompileOnly notin gGlobalOptions:
     # XXX what is this hack doing here? 'optCompileOnly' means raw output!?
-    code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title", 
-        "tableofcontents", "moduledesc", "date", "time", 
-        "content", "author", "version"], 
-        [title.toRope, toc, d.modDesc, toRope(getDateStr()), 
-                     toRope(getClockStr()), content, d.meta[metaAuthor].toRope, 
+    code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title",
+        "tableofcontents", "moduledesc", "date", "time",
+        "content", "author", "version"],
+        [title.toRope, toc, d.modDesc, toRope(getDateStr()),
+                     toRope(getClockStr()), content, d.meta[metaAuthor].toRope,
                      d.meta[metaVersion].toRope])
-  else: 
+  else:
     code = content
   result = code
 
 proc generateIndex*(d: PDoc) =
   if optGenIndex in gGlobalOptions:
-    writeIndexFile(d[], splitFile(options.outFile).dir / 
+    writeIndexFile(d[], splitFile(options.outFile).dir /
                         splitFile(d.filename).name & indexExt)
 
-proc writeOutput*(d: PDoc, filename, outExt: string, useWarning = false) = 
+proc writeOutput*(d: PDoc, filename, outExt: string, useWarning = false) =
   var content = genOutFile(d)
   if optStdout in gGlobalOptions:
     writeRope(stdout, content)
@@ -361,7 +419,7 @@ proc writeOutput*(d: PDoc, filename, outExt: string, useWarning = false) =
 
 proc CommandDoc*() =
   var ast = parseFile(gProjectMainIdx)
-  if ast == nil: return 
+  if ast == nil: return
   var d = newDocumentor(gProjectFull, options.gConfigVars)
   d.hasToc = true
   generateDoc(d, ast)
@@ -388,12 +446,26 @@ proc CommandRst2TeX*() =
   splitter = "\\-"
   CommandRstAux(gProjectFull, TexExt)
 
+proc CommandJSON*() =
+  var ast = parseFile(gProjectMainIdx)
+  if ast == nil: return
+  var d = newDocumentor(gProjectFull, options.gConfigVars)
+  d.hasToc = true
+  var json = generateJson(d, ast)
+  var content = newRope(pretty(json))
+
+  if optStdout in gGlobalOptions:
+    writeRope(stdout, content)
+  else:
+    echo getOutFile(gProjectFull, JsonExt)
+    writeRope(content, getOutFile(gProjectFull, JsonExt), useWarning = false)
+
 proc CommandBuildIndex*() =
   var content = mergeIndexes(gProjectFull).toRope
-  
-  let code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title", 
-      "tableofcontents", "moduledesc", "date", "time", 
-      "content", "author", "version"], 
-      ["Index".toRope, nil, nil, toRope(getDateStr()), 
+
+  let code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title",
+      "tableofcontents", "moduledesc", "date", "time",
+      "content", "author", "version"],
+      ["Index".toRope, nil, nil, toRope(getDateStr()),
                    toRope(getClockStr()), content, nil, nil])
   writeRope(code, getOutFile("theindex", HtmlExt))
diff --git a/compiler/evals.nim b/compiler/evals.nim
index 7e2c8a41d..b4ea973e8 100644
--- a/compiler/evals.nim
+++ b/compiler/evals.nim
@@ -550,9 +550,7 @@ proc evalSym(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
   of skProc, skConverter, skMacro, skType:
     result = n
     #result = s.getBody
-  of skForVar:
-    result = evalGlobalVar(c, s, flags)
-  of skVar, skLet, skTemp, skResult:
+  of skVar, skLet, skForVar, skTemp, skResult:
     if sfGlobal notin s.flags:
       result = evalVariable(c.tos, s, flags)
     else:
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index 13eb972f6..bbbbbfb8e 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -463,9 +463,9 @@ proc getCompileOptions: string =
 proc getLinkOptions: string =
   result = linkOptions
   for linkedLib in items(cLinkedLibs):
-    result.add(cc[ccompiler].linkLibCmd % linkedLib.quoteIfContainsWhite)
+    result.add(cc[ccompiler].linkLibCmd % linkedLib.quoteShell)
   for libDir in items(cLibs):
-    result.add([cc[ccompiler].linkDirCmd, libDir.quoteIfContainsWhite])
+    result.add([cc[ccompiler].linkDirCmd, libDir.quoteShell])
 
 proc needsExeExt(): bool {.inline.} =
   result = (optGenScript in gGlobalOptions and targetOS == osWindows) or
@@ -485,10 +485,10 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
   var includeCmd, compilePattern: string
   if not noAbsolutePaths(): 
     # compute include paths:
-    includeCmd = cc[c].includeCmd & quoteIfContainsWhite(libpath)
+    includeCmd = cc[c].includeCmd & quoteShell(libpath)
 
     for includeDir in items(cIncludes):
-      includeCmd.add([cc[c].includeCmd, includeDir.quoteIfContainsWhite])
+      includeCmd.add([cc[c].includeCmd, includeDir.quoteShell])
 
     compilePattern = JoinPath(ccompilerpath, exe)
   else: 
@@ -501,17 +501,17 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
                   toObjFile(cfile) 
                 else: 
                   completeCFilePath(toObjFile(cfile))
-  cfile = quoteIfContainsWhite(AddFileExt(cfile, cExt))
-  objfile = quoteIfContainsWhite(objfile)
-  result = quoteIfContainsWhite(compilePattern % [
+  cfile = quoteShell(AddFileExt(cfile, cExt))
+  objfile = quoteShell(objfile)
+  result = quoteShell(compilePattern % [
     "file", cfile, "objfile", objfile, "options", options, 
     "include", includeCmd, "nimrod", getPrefixDir(), "lib", libpath])
   add(result, ' ')
   addf(result, cc[c].compileTmpl, [
     "file", cfile, "objfile", objfile, 
     "options", options, "include", includeCmd, 
-    "nimrod", quoteIfContainsWhite(getPrefixDir()), 
-    "lib", quoteIfContainsWhite(libpath)])
+    "nimrod", quoteShell(getPrefixDir()), 
+    "lib", quoteShell(libpath)])
 
 proc footprint(filename: string): TCrc32 =
   result = crcFromFile(filename) ><
@@ -590,7 +590,7 @@ proc CallCCompiler*(projectfile: string) =
     while it != nil:
       let objFile = if noAbsolutePaths(): it.data.extractFilename else: it.data
       add(objfiles, ' ')
-      add(objfiles, quoteIfContainsWhite(
+      add(objfiles, quoteShell(
           addFileExt(objFile, cc[ccompiler].objExt)))
       it = PStrEntry(it.next)
 
@@ -602,8 +602,8 @@ proc CallCCompiler*(projectfile: string) =
       var linkerExe = getConfigVar(c, ".linkerexe")
       if len(linkerExe) == 0: linkerExe = cc[c].linkerExe
       if needsExeExt(): linkerExe = addFileExt(linkerExe, "exe")
-      if noAbsolutePaths(): linkCmd = quoteIfContainsWhite(linkerExe)
-      else: linkCmd = quoteIfContainsWhite(JoinPath(ccompilerpath, linkerExe))
+      if noAbsolutePaths(): linkCmd = quoteShell(linkerExe)
+      else: linkCmd = quoteShell(JoinPath(ccompilerpath, linkerExe))
       if optGenGuiApp in gGlobalOptions: buildGui = cc[c].buildGui
       else: buildGui = ""
       var exefile: string
@@ -614,20 +614,21 @@ proc CallCCompiler*(projectfile: string) =
         exefile = splitFile(projectFile).name & platform.os[targetOS].exeExt
         buildDll = ""
       if options.outFile.len > 0: 
-        exefile = options.outFile
+        exefile = options.outFile.expandTilde
       if not noAbsolutePaths():
-        exefile = joinPath(splitFile(projectFile).dir, exefile)
-      exefile = quoteIfContainsWhite(exefile)
+        if not exeFile.isAbsolute():
+          exefile = joinPath(splitFile(projectFile).dir, exefile)
+      exefile = quoteShell(exefile)
       let linkOptions = getLinkOptions()
-      linkCmd = quoteIfContainsWhite(linkCmd % ["builddll", builddll,
+      linkCmd = quoteShell(linkCmd % ["builddll", builddll,
           "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
           "exefile", exefile, "nimrod", getPrefixDir(), "lib", libpath])
       linkCmd.add ' '
       addf(linkCmd, cc[c].linkTmpl, ["builddll", builddll,
           "buildgui", buildgui, "options", linkOptions,
           "objfiles", objfiles, "exefile", exefile,
-          "nimrod", quoteIfContainsWhite(getPrefixDir()),
-          "lib", quoteIfContainsWhite(libpath)])
+          "nimrod", quoteShell(getPrefixDir()),
+          "lib", quoteShell(libpath)])
       if optCompileOnly notin gGlobalOptions: execExternalProgram(linkCmd)
   else:
     linkCmd = ""
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 54ee43069..a3c88824d 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -130,7 +130,7 @@ proc mapType(typ: PType): TJSTypeKind =
     result = etyObject
   of tyNil: result = etyNull
   of tyGenericInst, tyGenericParam, tyGenericBody, tyGenericInvokation, tyNone, 
-     tyForward, tyEmpty, tyExpr, tyStmt, tyTypeDesc, tyTypeClass: 
+     tyForward, tyEmpty, tyExpr, tyStmt, tyTypeDesc, tyTypeClasses: 
     result = etyNone
   of tyProc: result = etyProc
   of tyCString: result = etyString
diff --git a/compiler/lists.nim b/compiler/lists.nim
index 67b32f919..22b1a183a 100644
--- a/compiler/lists.nim
+++ b/compiler/lists.nim
@@ -91,13 +91,17 @@ proc Remove*(list: var TLinkedList, entry: PListEntry) =
   if entry.prev != nil: entry.prev.next = entry.next
 
 proc bringToFront*(list: var TLinkedList, entry: PListEntry) =
-  if entry == list.head: return
-  if entry == list.tail: list.tail = entry.prev
-  if entry.next != nil: entry.next.prev = entry.prev
-  if entry.prev != nil: entry.prev.next = entry.next
-  entry.prev = nil
-  entry.next = list.head
-  list.head = entry
+  when true:
+    list.remove entry
+    list.prepend entry
+  else:
+    if entry == list.head: return
+    if entry == list.tail: list.tail = entry.prev
+    if entry.next != nil: entry.next.prev = entry.prev
+    if entry.prev != nil: entry.prev.next = entry.next
+    entry.prev = nil
+    entry.next = list.head
+    list.head = entry
 
 proc ExcludeStr*(list: var TLinkedList, data: string) =
   var it = list.head
diff --git a/compiler/main.nim b/compiler/main.nim
index 7cfc6d406..9ffe99454 100644
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -9,8 +9,8 @@
 
 # implements the command dispatcher and several commands
 
-import 
-  llstream, strutils, ast, astalgo, lexer, syntaxes, renderer, options, msgs, 
+import
+  llstream, strutils, ast, astalgo, lexer, syntaxes, renderer, options, msgs,
   os, condsyms, rodread, rodwrite, times,
   wordrecg, sem, semdata, idents, passes, docgen, extccomp,
   cgen, jsgen, json, nversion,
@@ -98,7 +98,7 @@ proc CommandCompileToC =
       # rodread.rodcompilerProcs
       # rodread.gTypeTable
       # rodread.gMods
-      
+
       # !! ropes.cache
       # semthreads.computed?
       #
@@ -166,7 +166,7 @@ proc commandEval(exp: string) =
 proc CommandPrettyOld =
   var projectFile = addFileExt(mainCommandArg(), NimExt)
   var module = parseFile(projectFile.fileInfoIdx)
-  if module != nil: 
+  if module != nil:
     renderModule(module, getOutFile(mainCommandArg(), "pretty." & NimExt))
 
 proc CommandPretty =
@@ -175,24 +175,24 @@ proc CommandPretty =
   registerPass(prettyPass)
   compileProject()
   pretty.overwriteFiles()
-  
+
 proc CommandScan =
   var f = addFileExt(mainCommandArg(), nimExt)
   var stream = LLStreamOpen(f, fmRead)
-  if stream != nil: 
-    var 
+  if stream != nil:
+    var
       L: TLexer
       tok: TToken
     initToken(tok)
     openLexer(L, f, stream)
-    while true: 
+    while true:
       rawGetTok(L, tok)
       PrintTok(tok)
-      if tok.tokType == tkEof: break 
+      if tok.tokType == tkEof: break
     CloseLexer(L)
-  else: 
+  else:
     rawMessage(errCannotOpenFile, f)
-  
+
 proc CommandSuggest =
   if isServing:
     # XXX: hacky work-around ahead
@@ -246,7 +246,7 @@ proc resetMemory =
   for i in low(buckets)..high(buckets):
     buckets[i] = nil
   idAnon = nil
-  
+
   # XXX: clean these global vars
   # ccgstmts.gBreakpoints
   # ccgthreadvars.nimtv
@@ -262,7 +262,7 @@ proc resetMemory =
   # rodread.rodcompilerProcs
   # rodread.gTypeTable
   # rodread.gMods
-  
+
   # !! ropes.cache
   # semthreads.computed?
   #
@@ -289,7 +289,7 @@ const
 proc MainCommand* =
   when SimiluateCaasMemReset:
     gGlobalOptions.incl(optCaasEnabled)
-      
+
   # In "nimrod serve" scenario, each command must reset the registered passes
   clearPasses()
   gLastCmdTime = epochTime()
@@ -301,7 +301,7 @@ proc MainCommand* =
   passes.gIncludeFile = includeModule
   passes.gImportModule = importModule
   case command.normalize
-  of "c", "cc", "compile", "compiletoc": 
+  of "c", "cc", "compile", "compiletoc":
     # compile means compileToC currently
     gCmd = cmdCompileToC
     wantMainModule()
@@ -325,13 +325,13 @@ proc MainCommand* =
     when hasTinyCBackend:
       extccomp.setCC("tcc")
       CommandCompileToC()
-    else: 
+    else:
       rawMessage(errInvalidCommandX, command)
-  of "js", "compiletojs": 
+  of "js", "compiletojs":
     gCmd = cmdCompileToJS
     wantMainModule()
     CommandCompileToJS()
-  of "compiletollvm": 
+  of "compiletollvm":
     gCmd = cmdCompileToLLVM
     wantMainModule()
     when has_LLVM_Backend:
@@ -353,21 +353,27 @@ proc MainCommand* =
     wantMainModule()
     DefineSymbol("nimdoc")
     CommandDoc2()
-  of "rst2html": 
+  of "rst2html":
     gCmd = cmdRst2html
     LoadConfigs(DocConfig)
     wantMainModule()
     CommandRst2Html()
-  of "rst2tex": 
+  of "rst2tex":
     gCmd = cmdRst2tex
     LoadConfigs(DocTexConfig)
     wantMainModule()
     CommandRst2TeX()
+  of "jsondoc":
+    gCmd = cmdDoc
+    LoadConfigs(DocConfig)
+    wantMainModule()
+    DefineSymbol("nimdoc")
+    CommandJSON()
   of "buildindex":
     gCmd = cmdDoc
     LoadConfigs(DocConfig)
     CommandBuildIndex()
-  of "gendepend": 
+  of "gendepend":
     gCmd = cmdGenDepend
     wantMainModule()
     CommandGenDepend()
@@ -400,16 +406,16 @@ proc MainCommand* =
     gCmd = cmdCheck
     wantMainModule()
     CommandCheck()
-  of "parse": 
+  of "parse":
     gCmd = cmdParse
     wantMainModule()
     discard parseFile(gProjectMainIdx)
-  of "scan": 
+  of "scan":
     gCmd = cmdScan
     wantMainModule()
     CommandScan()
     MsgWriteln("Beware: Indentation tokens depend on the parser\'s state!")
-  of "i": 
+  of "i":
     gCmd = cmdInteractive
     CommandInteractive()
   of "e":
@@ -427,12 +433,14 @@ proc MainCommand* =
   of "serve":
     isServing = true
     gGlobalOptions.incl(optCaasEnabled)
-    msgs.gErrorMax = high(int)  # do not stop after first error     
+    msgs.gErrorMax = high(int)  # do not stop after first error
     serve(MainCommand)
   else:
     rawMessage(errInvalidCommandX, command)
-  
-  if msgs.gErrorCounter == 0 and gCmd notin {cmdInterpret, cmdRun, cmdDump}:
+
+  if (msgs.gErrorCounter == 0 and
+      gCmd notin {cmdInterpret, cmdRun, cmdDump} and
+      gVerbosity > 0):
     rawMessage(hintSuccessX, [$gLinesCompiled,
                formatFloat(epochTime() - gLastCmdTime, ffDecimal, 3),
                formatSize(getTotalMem())])
diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim
index 507812d9c..7ec566a01 100644
--- a/compiler/nimconf.nim
+++ b/compiler/nimconf.nim
@@ -243,11 +243,6 @@ proc LoadConfigs*(cfg: string) =
     readConfigFile(pd / cfg)
     
     if gProjectName.len != 0:
-      var conffile = changeFileExt(gProjectFull, "cfg")
-      if conffile != pd / cfg and existsFile(conffile):
-        readConfigFile(conffile)
-        rawMessage(warnConfigDeprecated, conffile)
-      
       # new project wide config file:
       readConfigFile(changeFileExt(gProjectFull, "nimrod.cfg"))
  
diff --git a/compiler/nimrod.dot b/compiler/nimrod.dot
deleted file mode 100644
index e9663d7c5..000000000
--- a/compiler/nimrod.dot
+++ /dev/null
@@ -1,591 +0,0 @@
-digraph nimrod {
-times -> strutils;
-os -> strutils;
-os -> times;
-posix -> times;
-os -> posix;
-nhashes -> strutils;
-nstrtabs -> os;
-nstrtabs -> nhashes;
-nstrtabs -> strutils;
-options -> os;
-options -> lists;
-options -> strutils;
-options -> nstrtabs;
-msgs -> options;
-msgs -> strutils;
-msgs -> os;
-crc -> strutils;
-platform -> strutils;
-ropes -> msgs;
-ropes -> strutils;
-ropes -> platform;
-ropes -> nhashes;
-ropes -> crc;
-idents -> nhashes;
-idents -> strutils;
-ast -> msgs;
-ast -> nhashes;
-ast -> nversion;
-ast -> options;
-ast -> strutils;
-ast -> crc;
-ast -> ropes;
-ast -> idents;
-ast -> lists;
-astalgo -> ast;
-astalgo -> nhashes;
-astalgo -> strutils;
-astalgo -> options;
-astalgo -> msgs;
-astalgo -> ropes;
-astalgo -> idents;
-condsyms -> ast;
-condsyms -> astalgo;
-condsyms -> msgs;
-condsyms -> nhashes;
-condsyms -> platform;
-condsyms -> strutils;
-condsyms -> idents;
-hashes -> strutils;
-strtabs -> os;
-strtabs -> hashes;
-strtabs -> strutils;
-osproc -> strutils;
-osproc -> os;
-osproc -> strtabs;
-osproc -> streams;
-osproc -> posix;
-extccomp -> lists;
-extccomp -> ropes;
-extccomp -> os;
-extccomp -> strutils;
-extccomp -> osproc;
-extccomp -> platform;
-extccomp -> condsyms;
-extccomp -> options;
-extccomp -> msgs;
-wordrecg -> nhashes;
-wordrecg -> strutils;
-wordrecg -> idents;
-commands -> os;
-commands -> msgs;
-commands -> options;
-commands -> nversion;
-commands -> condsyms;
-commands -> strutils;
-commands -> extccomp;
-commands -> platform;
-commands -> lists;
-commands -> wordrecg;
-llstream -> strutils;
-lexbase -> llstream;
-lexbase -> strutils;
-scanner -> nhashes;
-scanner -> options;
-scanner -> msgs;
-scanner -> strutils;
-scanner -> platform;
-scanner -> idents;
-scanner -> lexbase;
-scanner -> llstream;
-scanner -> wordrecg;
-nimconf -> llstream;
-nimconf -> nversion;
-nimconf -> commands;
-nimconf -> os;
-nimconf -> strutils;
-nimconf -> msgs;
-nimconf -> platform;
-nimconf -> condsyms;
-nimconf -> scanner;
-nimconf -> options;
-nimconf -> idents;
-nimconf -> wordrecg;
-pnimsyn -> llstream;
-pnimsyn -> scanner;
-pnimsyn -> idents;
-pnimsyn -> strutils;
-pnimsyn -> ast;
-pnimsyn -> msgs;
-pbraces -> llstream;
-pbraces -> scanner;
-pbraces -> idents;
-pbraces -> strutils;
-pbraces -> ast;
-pbraces -> msgs;
-pbraces -> pnimsyn;
-rnimsyn -> scanner;
-rnimsyn -> options;
-rnimsyn -> idents;
-rnimsyn -> strutils;
-rnimsyn -> ast;
-rnimsyn -> msgs;
-rnimsyn -> lists;
-filters -> llstream;
-filters -> os;
-filters -> wordrecg;
-filters -> idents;
-filters -> strutils;
-filters -> ast;
-filters -> astalgo;
-filters -> msgs;
-filters -> options;
-filters -> rnimsyn;
-ptmplsyn -> llstream;
-ptmplsyn -> os;
-ptmplsyn -> wordrecg;
-ptmplsyn -> idents;
-ptmplsyn -> strutils;
-ptmplsyn -> ast;
-ptmplsyn -> astalgo;
-ptmplsyn -> msgs;
-ptmplsyn -> options;
-ptmplsyn -> rnimsyn;
-ptmplsyn -> filters;
-syntaxes -> strutils;
-syntaxes -> llstream;
-syntaxes -> ast;
-syntaxes -> astalgo;
-syntaxes -> idents;
-syntaxes -> scanner;
-syntaxes -> options;
-syntaxes -> msgs;
-syntaxes -> pnimsyn;
-syntaxes -> pbraces;
-syntaxes -> ptmplsyn;
-syntaxes -> filters;
-syntaxes -> rnimsyn;
-paslex -> nhashes;
-paslex -> options;
-paslex -> msgs;
-paslex -> strutils;
-paslex -> platform;
-paslex -> idents;
-paslex -> lexbase;
-paslex -> wordrecg;
-paslex -> scanner;
-pasparse -> os;
-pasparse -> llstream;
-pasparse -> scanner;
-pasparse -> paslex;
-pasparse -> idents;
-pasparse -> wordrecg;
-pasparse -> strutils;
-pasparse -> ast;
-pasparse -> astalgo;
-pasparse -> msgs;
-pasparse -> options;
-rodread -> os;
-rodread -> options;
-rodread -> strutils;
-rodread -> nversion;
-rodread -> ast;
-rodread -> astalgo;
-rodread -> msgs;
-rodread -> platform;
-rodread -> condsyms;
-rodread -> ropes;
-rodread -> idents;
-rodread -> crc;
-trees -> ast;
-trees -> astalgo;
-trees -> scanner;
-trees -> msgs;
-trees -> strutils;
-types -> ast;
-types -> astalgo;
-types -> trees;
-types -> msgs;
-types -> strutils;
-types -> platform;
-magicsys -> ast;
-magicsys -> astalgo;
-magicsys -> nhashes;
-magicsys -> msgs;
-magicsys -> platform;
-magicsys -> nversion;
-magicsys -> times;
-magicsys -> idents;
-magicsys -> rodread;
-nimsets -> ast;
-nimsets -> astalgo;
-nimsets -> trees;
-nimsets -> nversion;
-nimsets -> msgs;
-nimsets -> platform;
-nimsets -> bitsets;
-nimsets -> types;
-nimsets -> rnimsyn;
-passes -> strutils;
-passes -> lists;
-passes -> options;
-passes -> ast;
-passes -> astalgo;
-passes -> llstream;
-passes -> msgs;
-passes -> platform;
-passes -> os;
-passes -> condsyms;
-passes -> idents;
-passes -> rnimsyn;
-passes -> types;
-passes -> extccomp;
-passes -> math;
-passes -> magicsys;
-passes -> nversion;
-passes -> nimsets;
-passes -> syntaxes;
-passes -> times;
-passes -> rodread;
-treetab -> nhashes;
-treetab -> ast;
-treetab -> astalgo;
-treetab -> types;
-semdata -> strutils;
-semdata -> lists;
-semdata -> options;
-semdata -> scanner;
-semdata -> ast;
-semdata -> astalgo;
-semdata -> trees;
-semdata -> treetab;
-semdata -> wordrecg;
-semdata -> ropes;
-semdata -> msgs;
-semdata -> platform;
-semdata -> os;
-semdata -> condsyms;
-semdata -> idents;
-semdata -> rnimsyn;
-semdata -> types;
-semdata -> extccomp;
-semdata -> math;
-semdata -> magicsys;
-semdata -> nversion;
-semdata -> nimsets;
-semdata -> pnimsyn;
-semdata -> times;
-semdata -> passes;
-semdata -> rodread;
-lookups -> ast;
-lookups -> astalgo;
-lookups -> idents;
-lookups -> semdata;
-lookups -> types;
-lookups -> msgs;
-lookups -> options;
-lookups -> rodread;
-lookups -> rnimsyn;
-importer -> strutils;
-importer -> os;
-importer -> ast;
-importer -> astalgo;
-importer -> msgs;
-importer -> options;
-importer -> idents;
-importer -> rodread;
-importer -> lookups;
-importer -> semdata;
-importer -> passes;
-rodwrite -> os;
-rodwrite -> options;
-rodwrite -> strutils;
-rodwrite -> nversion;
-rodwrite -> ast;
-rodwrite -> astalgo;
-rodwrite -> msgs;
-rodwrite -> platform;
-rodwrite -> condsyms;
-rodwrite -> ropes;
-rodwrite -> idents;
-rodwrite -> crc;
-rodwrite -> rodread;
-rodwrite -> passes;
-rodwrite -> importer;
-semfold -> strutils;
-semfold -> lists;
-semfold -> options;
-semfold -> ast;
-semfold -> astalgo;
-semfold -> trees;
-semfold -> treetab;
-semfold -> nimsets;
-semfold -> times;
-semfold -> nversion;
-semfold -> platform;
-semfold -> math;
-semfold -> msgs;
-semfold -> os;
-semfold -> condsyms;
-semfold -> idents;
-semfold -> rnimsyn;
-semfold -> types;
-evals -> strutils;
-evals -> magicsys;
-evals -> lists;
-evals -> options;
-evals -> ast;
-evals -> astalgo;
-evals -> trees;
-evals -> treetab;
-evals -> nimsets;
-evals -> msgs;
-evals -> os;
-evals -> condsyms;
-evals -> idents;
-evals -> rnimsyn;
-evals -> types;
-evals -> passes;
-evals -> semfold;
-procfind -> ast;
-procfind -> astalgo;
-procfind -> msgs;
-procfind -> semdata;
-procfind -> types;
-procfind -> trees;
-pragmas -> os;
-pragmas -> platform;
-pragmas -> condsyms;
-pragmas -> ast;
-pragmas -> astalgo;
-pragmas -> idents;
-pragmas -> semdata;
-pragmas -> msgs;
-pragmas -> rnimsyn;
-pragmas -> wordrecg;
-pragmas -> ropes;
-pragmas -> options;
-pragmas -> strutils;
-pragmas -> lists;
-pragmas -> extccomp;
-pragmas -> math;
-pragmas -> magicsys;
-pragmas -> trees;
-sem -> strutils;
-sem -> nhashes;
-sem -> lists;
-sem -> options;
-sem -> scanner;
-sem -> ast;
-sem -> astalgo;
-sem -> trees;
-sem -> treetab;
-sem -> wordrecg;
-sem -> ropes;
-sem -> msgs;
-sem -> os;
-sem -> condsyms;
-sem -> idents;
-sem -> rnimsyn;
-sem -> types;
-sem -> platform;
-sem -> math;
-sem -> magicsys;
-sem -> pnimsyn;
-sem -> nversion;
-sem -> nimsets;
-sem -> semdata;
-sem -> evals;
-sem -> semfold;
-sem -> importer;
-sem -> procfind;
-sem -> lookups;
-sem -> rodread;
-sem -> pragmas;
-sem -> passes;
-rst -> os;
-rst -> msgs;
-rst -> strutils;
-rst -> platform;
-rst -> nhashes;
-rst -> ropes;
-rst -> options;
-highlite -> nhashes;
-highlite -> options;
-highlite -> msgs;
-highlite -> strutils;
-highlite -> platform;
-highlite -> idents;
-highlite -> lexbase;
-highlite -> wordrecg;
-highlite -> scanner;
-docgen -> ast;
-docgen -> astalgo;
-docgen -> strutils;
-docgen -> nhashes;
-docgen -> options;
-docgen -> nversion;
-docgen -> msgs;
-docgen -> os;
-docgen -> ropes;
-docgen -> idents;
-docgen -> wordrecg;
-docgen -> math;
-docgen -> syntaxes;
-docgen -> rnimsyn;
-docgen -> scanner;
-docgen -> rst;
-docgen -> times;
-docgen -> highlite;
-ccgutils -> ast;
-ccgutils -> astalgo;
-ccgutils -> ropes;
-ccgutils -> lists;
-ccgutils -> nhashes;
-ccgutils -> strutils;
-ccgutils -> types;
-ccgutils -> msgs;
-cgmeth -> options;
-cgmeth -> ast;
-cgmeth -> astalgo;
-cgmeth -> msgs;
-cgmeth -> idents;
-cgmeth -> rnimsyn;
-cgmeth -> types;
-cgmeth -> magicsys;
-cgen -> ast;
-cgen -> astalgo;
-cgen -> strutils;
-cgen -> nhashes;
-cgen -> trees;
-cgen -> platform;
-cgen -> magicsys;
-cgen -> extccomp;
-cgen -> options;
-cgen -> nversion;
-cgen -> nimsets;
-cgen -> msgs;
-cgen -> crc;
-cgen -> bitsets;
-cgen -> idents;
-cgen -> lists;
-cgen -> types;
-cgen -> ccgutils;
-cgen -> os;
-cgen -> times;
-cgen -> ropes;
-cgen -> math;
-cgen -> passes;
-cgen -> rodread;
-cgen -> wordrecg;
-cgen -> rnimsyn;
-cgen -> treetab;
-cgen -> cgmeth;
-jsgen -> ast;
-jsgen -> astalgo;
-jsgen -> strutils;
-jsgen -> nhashes;
-jsgen -> trees;
-jsgen -> platform;
-jsgen -> magicsys;
-jsgen -> extccomp;
-jsgen -> options;
-jsgen -> nversion;
-jsgen -> nimsets;
-jsgen -> msgs;
-jsgen -> crc;
-jsgen -> bitsets;
-jsgen -> idents;
-jsgen -> lists;
-jsgen -> types;
-jsgen -> os;
-jsgen -> times;
-jsgen -> ropes;
-jsgen -> math;
-jsgen -> passes;
-jsgen -> ccgutils;
-jsgen -> wordrecg;
-jsgen -> rnimsyn;
-jsgen -> rodread;
-interact -> llstream;
-interact -> strutils;
-interact -> ropes;
-interact -> nstrtabs;
-interact -> msgs;
-passaux -> strutils;
-passaux -> ast;
-passaux -> astalgo;
-passaux -> passes;
-passaux -> msgs;
-passaux -> options;
-depends -> os;
-depends -> options;
-depends -> ast;
-depends -> astalgo;
-depends -> msgs;
-depends -> ropes;
-depends -> idents;
-depends -> passes;
-depends -> importer;
-transf -> strutils;
-transf -> lists;
-transf -> options;
-transf -> ast;
-transf -> astalgo;
-transf -> trees;
-transf -> treetab;
-transf -> evals;
-transf -> msgs;
-transf -> os;
-transf -> idents;
-transf -> rnimsyn;
-transf -> types;
-transf -> passes;
-transf -> semfold;
-transf -> magicsys;
-transf -> cgmeth;
-main -> llstream;
-main -> strutils;
-main -> ast;
-main -> astalgo;
-main -> scanner;
-main -> syntaxes;
-main -> rnimsyn;
-main -> options;
-main -> msgs;
-main -> os;
-main -> lists;
-main -> condsyms;
-main -> paslex;
-main -> pasparse;
-main -> rodread;
-main -> rodwrite;
-main -> ropes;
-main -> trees;
-main -> wordrecg;
-main -> sem;
-main -> semdata;
-main -> idents;
-main -> passes;
-main -> docgen;
-main -> extccomp;
-main -> cgen;
-main -> jsgen;
-main -> platform;
-main -> interact;
-main -> nimconf;
-main -> importer;
-main -> passaux;
-main -> depends;
-main -> transf;
-main -> evals;
-main -> types;
-parseopt -> os;
-parseopt -> strutils;
-nimrod -> times;
-nimrod -> commands;
-nimrod -> scanner;
-nimrod -> condsyms;
-nimrod -> options;
-nimrod -> msgs;
-nimrod -> nversion;
-nimrod -> nimconf;
-nimrod -> ropes;
-nimrod -> extccomp;
-nimrod -> strutils;
-nimrod -> os;
-nimrod -> platform;
-nimrod -> main;
-nimrod -> parseopt;
-}
diff --git a/compiler/nimrod.nim b/compiler/nimrod.nim
index 3fa80cebb..2bc94e3f8 100644
--- a/compiler/nimrod.nim
+++ b/compiler/nimrod.nim
@@ -13,9 +13,9 @@ when defined(gcc) and defined(windows):
   else:
     {.link: "icons/nimrod_icon.o".}
 
-import 
-  commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes, 
-  extccomp, strutils, os, platform, main, parseopt, service
+import
+  commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes,
+  extccomp, strutils, os, osproc, platform, main, parseopt, service
 
 when hasTinyCBackend:
   import tccgen
@@ -23,7 +23,7 @@ when hasTinyCBackend:
 when defined(profiler) or defined(memProfiler):
   {.hint: "Profiling support is turned on!".}
   import nimprof
-  
+
 proc prependCurDir(f: string): string =
   when defined(unix):
     if os.isAbsolute(f): result = f
@@ -61,12 +61,18 @@ proc HandleCmdLine() =
           tccgen.run()
       if optRun in gGlobalOptions:
         if gCmd == cmdCompileToJS:
-          var ex = quoteIfContainsWhite(
+          var ex = quoteShell(
             completeCFilePath(changeFileExt(gProjectFull, "js").prependCurDir))
           execExternalProgram("node " & ex & ' ' & service.arguments)
         else:
-          var ex = quoteIfContainsWhite(
-            changeFileExt(gProjectFull, exeExt).prependCurDir)
+          var binPath: string
+          if options.outFile.len > 0:
+            # If the user specified an outFile path, use that directly.
+            binPath = options.outFile.prependCurDir
+          else:
+            # Figure out ourselves a valid binary name.
+            binPath = changeFileExt(gProjectFull, exeExt).prependCurDir
+          var ex = quoteShell(binPath)
           execExternalProgram(ex & ' ' & service.arguments)
 
 when defined(GC_setMaxPause):
@@ -76,5 +82,7 @@ when compileOption("gc", "v2") or compileOption("gc", "refc"):
   # the new correct mark&sweet collector is too slow :-/
   GC_disableMarkAndSweep()
 condsyms.InitDefines()
-HandleCmdLine()
-quit(int8(msgs.gErrorCounter > 0))
+
+when not defined(selftest):
+  HandleCmdLine()
+  quit(int8(msgs.gErrorCounter > 0))
diff --git a/compiler/nimrod.cfg b/compiler/nimrod.nimrod.cfg
index ac8f732f1..9fa1b8cba 100644
--- a/compiler/nimrod.cfg
+++ b/compiler/nimrod.nimrod.cfg
@@ -11,4 +11,5 @@ path:"$projectPath/.."
 path:"$lib/packages/docutils"
 
 define:booting
+import:testability
 
diff --git a/compiler/options.nim b/compiler/options.nim
index ea6b91321..c62744485 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -111,6 +111,7 @@ var
   gDirtyBufferIdx* = 0'i32    # indicates the fileIdx of the dirty version of
                               # the tracked source X, saved by the CAAS client.
   gDirtyOriginalIdx* = 0'i32  # the original source file of the dirtified buffer.
+  gNoBabelPath* = false
 
 proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools}
 proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc
@@ -132,6 +133,7 @@ const
   NimExt* = "nim"
   RodExt* = "rod"
   HtmlExt* = "html"
+  JsonExt* = "json"
   TexExt* = "tex"
   IniExt* = "ini"
   DefaultConfig* = "nimrod.cfg"
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index 8c2425de3..6f1e7af25 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -43,7 +43,8 @@ const
     wFatal, wDefine, wUndef, wCompile, wLink, wLinkSys, wPure, wPush, wPop,
     wBreakpoint, wWatchpoint, wPassL, wPassC, wDeadCodeElim, wDeprecated,
     wFloatChecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
-    wLinearScanEnd, wPatterns, wEffects, wNoForward, wComputedGoto}
+    wLinearScanEnd, wPatterns, wEffects, wNoForward, wComputedGoto,
+    wInjectStmt}
   lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl, 
     wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader, 
     wDeprecated, wExtern, wThread, wImportcpp, wImportobjc, wNoStackFrame,
@@ -722,6 +723,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
         of wOperator:
           if sym == nil: invalidPragma(it)
           else: sym.position = expectIntLit(c, it)
+        of wInjectStmt:
+          if it.kind != nkExprColonExpr:
+            localError(it.info, errExprExpected)
+          else: 
+            it.sons[1] = c.semExpr(c, it.sons[1])
         else: invalidPragma(it)
       else: invalidPragma(it)
   else: processNote(c, it)
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index f6fb0f8c0..61babed66 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -421,10 +421,11 @@ proc lsub(n: PNode): int =
   of nkElifExpr: result = lsons(n) + len("_elif_:_")
   of nkElseExpr: result = lsub(n.sons[0]) + len("_else:_") # type descriptions
   of nkTypeOfExpr: result = lsub(n.sons[0]) + len("type_")
-  of nkRefTy: result = lsub(n.sons[0]) + len("ref_")
-  of nkPtrTy: result = lsub(n.sons[0]) + len("ptr_")
-  of nkVarTy: result = lsub(n.sons[0]) + len("var_")
-  of nkDistinctTy: result = lsub(n.sons[0]) + len("Distinct_")
+  of nkRefTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("ref")
+  of nkPtrTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("ptr")
+  of nkVarTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("var")
+  of nkDistinctTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) +
+                                                               len("Distinct")
   of nkTypeDef: result = lsons(n) + 3
   of nkOfInherit: result = lsub(n.sons[0]) + len("of_")
   of nkProcTy: result = lsons(n) + len("proc_")
diff --git a/compiler/ropes.nim b/compiler/ropes.nim
index 707c29123..ce81aae37 100644
--- a/compiler/ropes.nim
+++ b/compiler/ropes.nim
@@ -283,11 +283,10 @@ proc ropef(frmt: TFormatStr, args: varargs[PRope]): PRope =
   assert(RopeInvariant(result))
 
 {.push stack_trace: off, line_trace: off.}
-proc `~`*(r: expr[string]): PRope =
+proc `~`*(r: string): PRope =
   # this is the new optimized "to rope" operator
   # the mnemonic is that `~` looks a bit like a rope :)
-  var r {.global.} = r.ropef
-  return r
+  return r.ropef
 {.pop.}
 
 proc appf(c: var PRope, frmt: TFormatStr, args: varargs[PRope]) = 
diff --git a/compiler/sem.nim b/compiler/sem.nim
index ea53afbeb..3ace623bc 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -90,6 +90,24 @@ proc commonType*(x, y: PType): PType =
     let idx = ord(b.kind in {tyArray, tyArrayConstr})
     if a.sons[idx].kind == tyEmpty: return y
     #elif b.sons[idx].kind == tyEmpty: return x
+  elif a.kind == tyRange and b.kind == tyRange:
+    # consider:  (range[0..3], range[0..4]) here. We should make that
+    # range[0..4]. But then why is (range[0..4], 6) not range[0..6]?
+    # But then why is (2,4) not range[2..4]? But I think this would break
+    # too much code. So ... it's the same range or the base type. This means
+    #  type(if b: 0 else 1) == int and not range[0..1]. For now. In the long
+    # run people expect ranges to work properly within a tuple.
+    if not sameType(a, b):
+      result = skipTypes(a, {tyRange}).skipIntLit
+    when false:
+      if a.kind != tyRange and b.kind == tyRange:
+        # XXX This really needs a better solution, but a proper fix now breaks
+        # code.
+        result = a #.skipIntLit
+      elif a.kind == tyRange and b.kind != tyRange:
+        result = b #.skipIntLit
+      elif a.kind in IntegralTypes and a.n != nil:
+        result = a #.skipIntLit
   else:
     var k = tyNone
     if a.kind in {tyRef, tyPtr}:
@@ -103,7 +121,7 @@ proc commonType*(x, y: PType): PType =
       if result.isNil: return x
       if k != tyNone:
         let r = result
-        result = NewType(k, r.owner)
+        result = newType(k, r.owner)
         result.addSonSkipIntLit(r)
 
 proc isTopLevel(c: PContext): bool {.inline.} = 
@@ -140,26 +158,27 @@ proc IsOpImpl(c: PContext, n: PNode): PNode
 proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
                   semCheck: bool = true): PNode
 
-proc symFromType(t: PType, info: TLineInfo): PSym =
-  if t.sym != nil: return t.sym
-  result = newSym(skType, getIdent"AnonType", t.owner, info)
-  result.flags.incl sfAnon
-  result.typ = t
+when false:
+  proc symFromType(t: PType, info: TLineInfo): PSym =
+    if t.sym != nil: return t.sym
+    result = newSym(skType, getIdent"AnonType", t.owner, info)
+    result.flags.incl sfAnon
+    result.typ = t
 
-proc symNodeFromType(c: PContext, t: PType, info: TLineInfo): PNode =
-  result = newSymNode(symFromType(t, info), info)
-  result.typ = makeTypeDesc(c, t)
+  proc symNodeFromType(c: PContext, t: PType, info: TLineInfo): PNode =
+    result = newSymNode(symFromType(t, info), info)
+    result.typ = makeTypeDesc(c, t)
 
 proc createEvalContext(c: PContext, mode: TEvalMode): PEvalContext =
   result = newEvalContext(c.module, mode)
   result.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 = tryExpr(c, n)
+    if result == nil:
+      result = newSymNode(errorSym(c, n))
+    elif result.typ == nil:
       result = newSymNode(getSysSym"void")
     else:
-      result = symNodeFromType(c, e.typ, n.info)
+      result.typ = makeTypeDesc(c, result.typ)
 
   result.handleIsOperator = proc (n: PNode): PNode =
     result = IsOpImpl(c, n)
@@ -210,7 +229,8 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode =
     of tyTypeDesc:
       if n.kind == nkStmtList: result.kind = nkStmtListType
       var typ = semTypeNode(c, result, nil)
-      result = symNodeFromType(c, typ, n.info)
+      result.typ = makeTypeDesc(c, typ)
+      #result = symNodeFromType(c, typ, n.info)
     else:
       result = semExpr(c, result)
       result = fitNode(c, s.typ.sons[0], result)
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 337224aef..310aabc32 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -191,7 +191,7 @@ proc isCastable(dst, src: PType): bool =
 proc isSymChoice(n: PNode): bool {.inline.} =
   result = n.kind in nkSymChoices
 
-proc semConv(c: PContext, n: PNode, s: PSym): PNode =
+proc semConv(c: PContext, n: PNode): PNode =
   if sonsLen(n) != 2:
     LocalError(n.info, errConvNeedsOneArg)
     return n
@@ -322,7 +322,7 @@ proc isOpImpl(c: PContext, n: PNode): PNode =
     var match: bool
     let t2 = n[2].typ
     case t2.kind
-    of tyTypeClass:
+    of tyTypeClasses:
       var m: TCandidate
       InitCandidate(m, t2)
       match = matchUserTypeClass(c, m, emptyNode, t2, t1) != nil
@@ -508,7 +508,7 @@ proc fixAbstractType(c: PContext, n: PNode) =
         changeType(it.sons[1], s, check=true)
         n.sons[i] = it.sons[1]
     of nkBracket: 
-      # an implicitely constructed array (passed to an open array):
+      # an implicitly constructed array (passed to an open array):
       n.sons[i] = semArrayConstr(c, it, {})
     else: 
       nil
@@ -738,8 +738,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
   elif t != nil and t.kind == tyTypeDesc:
     if n.len == 1: return semObjConstr(c, n, flags)
     let destType = t.skipTypes({tyTypeDesc, tyGenericInst})
-    result = semConv(c, n, symFromType(destType, n.info))
-    return
+    return semConv(c, n)
   else:
     result = overloadedCallOpr(c, n)
     # Now that nkSym does not imply an iteration over the proc/iterator space,
@@ -1048,7 +1047,9 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
     # The result so far is a tyTypeDesc bound 
     # a tyGenericBody. The line below will substitute
     # it with the instantiated type.
-    result = symNodeFromType(c, semTypeNode(c, n, nil), n.info)
+    result = n
+    result.typ = makeTypeDesc(c, semTypeNode(c, n, nil))
+    #result = symNodeFromType(c, semTypeNode(c, n, nil), n.info)
   of tyTuple: 
     checkSonsLen(n, 2)
     n.sons[0] = makeDeref(n.sons[0])
@@ -1883,7 +1884,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     result = semExpr(c, n.sons[0], flags)
   of nkTypeOfExpr, nkTupleTy, nkRefTy..nkEnumTy:
     var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc})
-    result = symNodeFromType(c, typ, n.info)
+    result.typ = makeTypeDesc(c, typ)
+    #result = symNodeFromType(c, typ, n.info)
   of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: 
     # check if it is an expression macro:
     checkMinSonsLen(n, 1)
@@ -1906,7 +1908,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
       of skType:
         # XXX think about this more (``set`` procs)
         if n.len == 2:
-          result = semConv(c, n, s)
+          result = semConv(c, n)
         elif n.len == 1:
           result = semObjConstr(c, n, flags)
         elif Contains(c.AmbiguousSymbols, s.id): 
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index 0cf5086a8..d7d64fd54 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -20,7 +20,8 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
     if a.kind != nkSym: 
       InternalError(a.info, "instantiateGenericParamList; no symbol")
     var q = a.sym
-    if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyTypeClass, tyExpr}: continue
+    if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyExpr}+tyTypeClasses:
+      continue
     var s = newSym(skType, q.name, getCurrOwner(), q.info)
     s.flags = s.flags + {sfUsed, sfFromGeneric}
     var t = PType(IdTableGet(pt, q.typ))
@@ -193,7 +194,7 @@ proc fixupProcType(c: PContext, genericType: PType,
   if result == nil: return
 
   case genericType.kind
-  of tyGenericParam, tyTypeClass:
+  of tyGenericParam, tyTypeClasses:
     result = inst.concreteTypes[genericType.sym.position]
   of tyTypeDesc:
     result = inst.concreteTypes[genericType.sym.position]
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index da8ba50a8..a1805fdec 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -1087,6 +1087,8 @@ proc semIterator(c: PContext, n: PNode): PNode =
   # -- at least for 0.9.2.
   if s.typ.callConv == ccClosure:
     incl(s.typ.flags, tfCapturesEnv)
+  else:
+    s.typ.callConv = ccInline
   when false:
     if s.typ.callConv != ccInline: 
       s.typ.callConv = ccClosure
diff --git a/compiler/semthreads.nim b/compiler/semthreads.nim
index 595ab0454..eded99325 100644
--- a/compiler/semthreads.nim
+++ b/compiler/semthreads.nim
@@ -380,7 +380,8 @@ proc analyseThreadProc*(prc: PSym) =
   var formals = skipTypes(prc.typ, abstractInst).n
   for i in 1 .. formals.len-1:
     var formal = formals.sons[i].sym 
-    c.mapping[formal.id] = toTheirs # thread receives foreign data!
+    # the input is copied and belongs to the thread:
+    c.mapping[formal.id] = toMine
   discard analyse(c, prc.getBody)
 
 proc needsGlobalAnalysis*: bool =
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 92f47f585..6c9c476d9 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -676,8 +676,13 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
       if lifted != nil:
         paramType.sons[i] = lifted
         result = paramType
-    
-    if result != nil:
+
+    if paramType.lastSon.kind == tyTypeClass:
+      result = paramType
+      result.kind = tyParametricTypeClass
+      result = addImplicitGeneric(copyType(result,
+                                           getCurrOwner(), false))
+    elif result != nil:
       result.kind = tyGenericInvokation
       result.sons.setLen(result.sons.len - 1)
   of tyTypeClass:
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 00f3b2b10..cacf4782e 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -203,7 +203,7 @@ proc describeArgs*(c: PContext, n: PNode, startIdx = 1): string =
     add(result, argTypeToString(arg))
     if i != sonsLen(n) - 1: add(result, ", ")
 
-proc typeRel*(c: var TCandidate, f, a: PType): TTypeRelation
+proc typeRel*(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation
 proc concreteType(c: TCandidate, t: PType): PType = 
   case t.kind
   of tyArrayConstr: 
@@ -213,7 +213,7 @@ proc concreteType(c: TCandidate, t: PType): PType =
     addSonSkipIntLit(result, t.sons[1]) # XXX: semantic checking for the type?
   of tyNil:
     result = nil              # what should it be?
-  of tyGenericParam: 
+  of tyGenericParam, tyAnything:
     result = t
     while true: 
       result = PType(idTableGet(c.bindings, t))
@@ -385,8 +385,23 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
   else:
     result = isNone
 
-proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation = 
-  # is a subtype of f?
+proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation =
+  # typeRel can be used to establish various relationships between types:
+  #
+  # 1) When used with concrete types, it will check for type equivalence
+  # or a subtype relationship. 
+  #
+  # 2) When used with a concrete type against a type class (such as generic
+  # signature of a proc), it will check whether the concrete type is a member
+  # of the designated type class.
+  #
+  # 3) When used with two type classes, it will check whether the types
+  # matching the first type class are a strict subset of the types matching
+  # the other. This allows us to compare the signatures of generic procs in
+  # order to give preferrence to the most specific one:
+  #
+  # seq[seq[any]] is a strict subset of seq[any] and hence more specific.
+  
   result = isNone
   assert(f != nil)
   assert(a != nil)
@@ -397,6 +412,50 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
     return typeRel(c, f, lastSon(a))
   if a.kind == tyVar and f.kind != tyVar:
     return typeRel(c, f, a.sons[0])
+  
+  template bindingRet(res) =
+    when res == isGeneric: put(c.bindings, f, a)
+    return res
+ 
+  case a.kind
+  of tyOr:
+    # seq[int|string] vs seq[number]
+    # both int and string must match against number
+    for branch in a.sons:
+      if typeRel(c, f, branch, false) == isNone:
+        return isNone
+
+    return isGeneric
+
+  of tyAnd:
+    # seq[Sortable and Iterable] vs seq[Sortable]
+    # only one match is enough
+    for branch in a.sons:
+      if typeRel(c, f, branch, false) != isNone:
+        return isGeneric
+
+    return isNone
+
+  of tyNot:
+    case f.kind
+    of tyNot:
+      # seq[!int] vs seq[!number]
+      # seq[float] matches the first, but not the second
+      # we must turn the problem around:
+      # is number a subset of int? 
+      return typeRel(c, a.lastSon, f.lastSon)
+ 
+    else:
+      # negative type classes are essentially infinite,
+      # so only the `any` type class is their superset
+      return if f.kind == tyAnything: isGeneric
+             else: isNone
+
+  of tyAnything:
+    return if f.kind == tyAnything: isGeneric
+           else: isNone
+  else: nil
+
   case f.kind
   of tyEnum: 
     if a.kind == f.kind and sameEnumTypes(f, a): result = isEqual
@@ -485,9 +544,12 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
   of tyOrdinal:
     if isOrdinalType(a):
       var x = if a.kind == tyOrdinal: a.sons[0] else: a
-      
-      result = typeRel(c, f.sons[0], x)
-      if result < isGeneric: result = isNone
+     
+      if f.sonsLen == 0:
+        result = isGeneric
+      else:
+        result = typeRel(c, f.sons[0], x)
+        if result < isGeneric: result = isNone
     elif a.kind == tyGenericParam:
       result = isGeneric
   of tyForward: InternalError("forward type in typeRel()")
@@ -574,13 +636,17 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
           (a.sons[1].kind == tyChar): 
         result = isConvertible
     else: nil
-  of tyEmpty: 
+
+  of tyEmpty:
     if a.kind == tyEmpty: result = isEqual
-  of tyGenericInst: 
+
+  of tyGenericInst:
     result = typeRel(c, lastSon(f), a)
-  of tyGenericBody: 
+
+  of tyGenericBody:
     let ff = lastSon(f)
     if ff != nil: result = typeRel(c, ff, a)
+
   of tyGenericInvokation:
     var x = a.skipGenericAlias
     if x.kind == tyGenericInvokation or f.sons[0].kind != tyGenericBody:
@@ -604,6 +670,38 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
           if x == nil or x.kind in {tyGenericInvokation, tyGenericParam}:
             InternalError("wrong instantiated type!")
           put(c.bindings, f.sons[i], x)
+  
+  of tyAnd:
+    for branch in f.sons:
+      if typeRel(c, branch, a) == isNone:
+        return isNone
+
+    bindingRet isGeneric
+
+  of tyOr:
+    for branch in f.sons:
+      if typeRel(c, branch, a) != isNone:
+        bindingRet isGeneric
+
+    return isNone
+
+  of tyNot:
+    for branch in f.sons:
+      if typeRel(c, branch, a) != isNone:
+        return isNone
+    
+    bindingRet isGeneric
+
+  of tyAnything:
+    var prev = PType(idTableGet(c.bindings, f))
+    if prev == nil:
+      var concrete = concreteType(c, a)
+      if concrete != nil and doBind:
+        put(c.bindings, f, concrete)
+      return isGeneric
+    else:
+      return typeRel(c, prev, a)
+    
   of tyGenericParam, tyTypeClass:
     var x = PType(idTableGet(c.bindings, f))
     if x == nil:
@@ -634,7 +732,7 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
         if concrete == nil:
           result = isNone
         else:
-          put(c.bindings, f, concrete)
+          if doBind: put(c.bindings, f, concrete)
     elif a.kind == tyEmpty:
       result = isGeneric
     elif x.kind == tyGenericParam:
@@ -809,8 +907,8 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, argType: PType,
         InternalAssert a.len > 0
         r = typeRel(m, f.lastSon, a.lastSon)
       else:
-        let match = matchTypeClass(m, fMaybeExpr, a)
-        if match != isGeneric: r = isNone
+        let match = matchTypeClass(m.bindings, fMaybeExpr, a)
+        if not match: r = isNone
         else:
           # XXX: Ideally, this should happen much earlier somewhere near 
           # semOpAux, but to do that, we need to be able to query the 
@@ -827,7 +925,7 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, argType: PType,
         
     if r == isGeneric:
       put(m.bindings, f, arg.typ)
-  of tyTypeClass:
+  of tyTypeClass, tyParametricTypeClass:
     if fMaybeExpr.n != nil:
       let match = matchUserTypeClass(c, m, arg, fMaybeExpr, a)
       if match != nil:
@@ -1130,7 +1228,7 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
   var f = 1
   while f < sonsLen(m.callee.n):
     var formal = m.callee.n.sons[f].sym
-    if not ContainsOrIncl(marker, formal.position): 
+    if not ContainsOrIncl(marker, formal.position):
       if formal.ast == nil:
         if formal.typ.kind == tyVarargs:
           var container = newNodeIT(nkBracket, n.info, arrayConstr(c, n.info))
@@ -1145,7 +1243,7 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
         setSon(m.call, formal.position + 1, copyTree(formal.ast))
     inc(f)
 
-proc argtypeMatches*(c: PContext, f, a: PType): bool = 
+proc argtypeMatches*(c: PContext, f, a: PType): bool =
   var m: TCandidate
   initCandidate(m, f)
   let res = paramTypesMatch(c, m, f, a, ast.emptyNode, nil)
@@ -1155,3 +1253,121 @@ proc argtypeMatches*(c: PContext, f, a: PType): bool =
   result = res != nil
 
 include suggest
+
+tests:
+  var dummyOwner = newSym(skModule, getIdent("test_module"), nil, UnknownLineInfo())
+  
+  proc `|` (t1, t2: PType): PType =
+    result = newType(tyOr, dummyOwner)
+    result.rawAddSon(t1)
+    result.rawAddSon(t2)
+
+  proc `&` (t1, t2: PType): PType =
+    result = newType(tyAnd, dummyOwner)
+    result.rawAddSon(t1)
+    result.rawAddSon(t2)
+
+  proc `!` (t: PType): PType =
+    result = newType(tyNot, dummyOwner)
+    result.rawAddSon(t)
+
+  proc seq(t: PType): PType =
+    result = newType(tySequence, dummyOwner)
+    result.rawAddSon(t)
+
+  proc array(x: int, t: PType): PType =
+    result = newType(tyArray, dummyOwner)
+    
+    var n = newNodeI(nkRange, UnknownLineInfo())
+    addSon(n, newIntNode(nkIntLit, 0))
+    addSon(n, newIntNode(nkIntLit, x))
+    let range = newType(tyRange, dummyOwner)
+    
+    result.rawAddSon(range)
+    result.rawAddSon(t)
+
+  suite "type classes":
+    let
+      int = newType(tyInt, dummyOwner)
+      float = newType(tyFloat, dummyOwner)
+      string = newType(tyString, dummyOwner)
+      ordinal = newType(tyOrdinal, dummyOwner)
+      any = newType(tyAnything, dummyOwner)
+      number = int | float
+
+    var TFoo = newType(tyObject, dummyOwner)
+    TFoo.sym = newSym(skType, getIdent"TFoo", dummyOwner, UnknownLineInfo())
+
+    var T1 = newType(tyGenericParam, dummyOwner)
+    T1.sym = newSym(skType, getIdent"T1", dummyOwner, UnknownLineInfo())
+    T1.sym.position = 0
+
+    var T2 = newType(tyGenericParam, dummyOwner)
+    T2.sym = newSym(skType, getIdent"T2", dummyOwner, UnknownLineInfo())
+    T2.sym.position = 1
+
+    setup:
+      var c: TCandidate
+      InitCandidate(c, nil)
+
+    template yes(x, y) =
+      test astToStr(x) & " is " & astToStr(y):
+        check typeRel(c, y, x) == isGeneric
+
+    template no(x, y) =
+      test astToStr(x) & " is not " & astToStr(y):
+        check typeRel(c, y, x) == isNone
+    
+    yes seq(any), array(10, int) | seq(any)
+    # Sure, seq[any] is directly included
+
+    yes seq(int), seq(any)
+    yes seq(int), seq(number)
+    # Sure, the int sequence is certainly
+    # part of the number sequences (and all sequences)
+    
+    no seq(any), seq(float)
+    # Nope, seq[any] includes types that are not seq[float] (e.g. seq[int])
+
+    yes seq(int|string), seq(any)
+    # Sure
+ 
+    yes seq(int&string), seq(any)
+    # Again
+    
+    yes seq(int&string), seq(int)
+    # A bit more complicated
+    # seq[int&string] is not a real type, but it's analogous to
+    # seq[Sortable and Iterable], which is certainly a subset of seq[Sortable]
+
+    no seq(int|string), seq(int|float)
+    # Nope, seq[string] is not included in not included in
+    # the seq[int|float] set
+    
+    no seq(!(int|string)), seq(string)
+    # A sequence that is neither seq[int] or seq[string]
+    # is obviously not seq[string]
+     
+    no seq(!int), seq(number)
+    # Now your head should start to hurt a bit
+    # A sequence that is not seq[int] is not necessarily a number sequence
+    # it could well be seq[string] for example
+    
+    yes seq(!(int|string)), seq(!string)
+    # all sequnece types besides seq[int] and seq[string]
+    # are subset of all sequence types that are not seq[string]
+
+    no seq(!(int|string)), seq(!(string|TFoo))
+    # Nope, seq[TFoo] is included in the first set, but not in the second
+    
+    no seq(!string), seq(!number)
+    # Nope, seq[int] in included in the first set, but not in the second
+
+    yes seq(!number), seq(any)
+    yes seq(!int), seq(any)
+    no seq(any), seq(!any)
+    no seq(!int), seq(!any)
+    
+    yes int, ordinal
+    no  string, ordinal
+
diff --git a/compiler/testability.nim b/compiler/testability.nim
new file mode 100644
index 000000000..ceefd0a5e
--- /dev/null
+++ b/compiler/testability.nim
@@ -0,0 +1,5 @@
+template tests*(body: stmt) {.immediate.} =
+  when defined(selftest):
+    when not defined(unittest): import unittest
+    body
+
diff --git a/compiler/types.nim b/compiler/types.nim
index 4dec9ea2f..7e07a0667 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -382,33 +382,34 @@ proc mutateTypeAux(marker: var TIntSet, t: PType, iter: TTypeMutator,
     if t.n != nil: result.n = mutateNode(marker, t.n, iter, closure)
   assert(result != nil)
 
-proc mutateType(t: PType, iter: TTypeMutator, closure: PObject): PType = 
+proc mutateType(t: PType, iter: TTypeMutator, closure: PObject): PType =
   var marker = InitIntSet()
   result = mutateTypeAux(marker, t, iter, closure)
 
-proc ValueToString(a: PNode): string = 
+proc ValueToString(a: PNode): string =
   case a.kind
   of nkCharLit..nkUInt64Lit: result = $(a.intVal)
   of nkFloatLit..nkFloat128Lit: result = $(a.floatVal)
   of nkStrLit..nkTripleStrLit: result = a.strVal
   else: result = "<invalid value>"
 
-proc rangeToStr(n: PNode): string = 
+proc rangeToStr(n: PNode): string =
   assert(n.kind == nkRange)
   result = ValueToString(n.sons[0]) & ".." & ValueToString(n.sons[1])
 
 const 
-  typeToStr: array[TTypeKind, string] = ["None", "bool", "Char", "empty", 
-    "Array Constructor [$1]", "nil", "expr", "stmt", "typeDesc", 
-    "GenericInvokation", "GenericBody", "GenericInst", "GenericParam", 
-    "distinct $1", "enum", "ordinal[$1]", "array[$1, $2]", "object", "tuple", 
-    "set[$1]", "range[$1]", "ptr ", "ref ", "var ", "seq[$1]", "proc", 
+  typeToStr: array[TTypeKind, string] = ["None", "bool", "Char", "empty",
+    "Array Constructor [$1]", "nil", "expr", "stmt", "typeDesc",
+    "GenericInvokation", "GenericBody", "GenericInst", "GenericParam",
+    "distinct $1", "enum", "ordinal[$1]", "array[$1, $2]", "object", "tuple",
+    "set[$1]", "range[$1]", "ptr ", "ref ", "var ", "seq[$1]", "proc",
     "pointer", "OpenArray[$1]", "string", "CString", "Forward",
     "int", "int8", "int16", "int32", "int64",
     "float", "float32", "float64", "float128",
     "uint", "uint8", "uint16", "uint32", "uint64",
     "bignum", "const ",
-    "!", "varargs[$1]", "iter[$1]", "Error Type", "TypeClass"]
+    "!", "varargs[$1]", "iter[$1]", "Error Type", "TypeClass",
+    "ParametricTypeClass", "and", "or", "not", "any"]
 
 proc consToStr(t: PType): string =
   if t.len > 0: result = t.typeToString
@@ -421,7 +422,7 @@ proc constraintsToStr(t: PType): string =
     if i > 0: result.add(sep)
     result.add(t.sons[i].consToStr)
 
-proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string = 
+proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
   var t = typ
   result = ""
   if t == nil: return 
@@ -861,7 +862,7 @@ proc SameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
   of tyGenericParam, tyGenericInvokation, tyGenericBody, tySequence,
      tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr,
      tyArray, tyProc, tyConst, tyMutable, tyVarargs, tyIter,
-     tyOrdinal, tyTypeClass:
+     tyOrdinal, tyTypeClasses:
     CycleCheck()    
     result = sameChildrenAux(a, b, c) and sameFlags(a, b)
     if result and (a.kind == tyProc):
@@ -1042,7 +1043,7 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind,
     # XXX er ... no? these should not be allowed!
   of tyEmpty:
     result = taField in flags
-  of tyTypeClass:
+  of tyTypeClasses:
     result = true
   of tyGenericBody, tyGenericParam, tyForward, tyNone, tyGenericInvokation:
     result = false
diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim
index b37a7bb4f..39b19646e 100644
--- a/compiler/wordrecg.nim
+++ b/compiler/wordrecg.nim
@@ -60,7 +60,7 @@ type
     wPassc, wPassl, wBorrow, wDiscardable,
     wFieldChecks, 
     wWatchPoint, wSubsChar, 
-    wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto,
+    wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto, wInjectStmt,
     wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit, 
     wNoStackFrame,
     wImplicitStatic, wGlobal, wCodegenDecl,
@@ -142,7 +142,8 @@ const
     "compiletime", "noinit",
     "passc", "passl", "borrow", "discardable", "fieldchecks",
     "watchpoint",
-    "subschar", "acyclic", "shallow", "unroll", "linearscanend", "computedgoto",
+    "subschar", "acyclic", "shallow", "unroll", "linearscanend",
+    "computedgoto", "injectstmt",
     "write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit",
     "nostackframe", "implicitstatic", "global", "codegendecl",