summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ast.nim6
-rw-r--r--compiler/c2nim/clex.nim16
-rw-r--r--compiler/ccgexprs.nim233
-rw-r--r--compiler/ccgstmts.nim214
-rw-r--r--compiler/ccgtypes.nim11
-rw-r--r--compiler/cgen.nim49
-rw-r--r--compiler/commands.nim22
-rw-r--r--compiler/docgen.nim2
-rw-r--r--compiler/extccomp.nim6
-rw-r--r--compiler/jsgen.nim1248
-rw-r--r--compiler/jstypes.nim148
-rw-r--r--compiler/lambdalifting.nim2
-rw-r--r--compiler/lexer.nim139
-rw-r--r--compiler/main.nim57
-rw-r--r--compiler/msgs.nim22
-rw-r--r--compiler/nimconf.nim2
-rw-r--r--compiler/options.nim9
-rw-r--r--compiler/parser.nim1065
-rw-r--r--compiler/pas2nim/paslex.nim16
-rw-r--r--compiler/renderer.nim9
-rw-r--r--compiler/rodread.nim6
-rw-r--r--compiler/rodwrite.nim3
-rw-r--r--compiler/sem.nim42
-rw-r--r--compiler/semdata.nim2
-rw-r--r--compiler/semdestruct.nim213
-rw-r--r--compiler/semexprs.nim205
-rw-r--r--compiler/semfold.nim7
-rw-r--r--compiler/semstmts.nim566
-rw-r--r--compiler/semtypes.nim3
-rw-r--r--compiler/service.nim3
-rw-r--r--compiler/sigmatch.nim9
-rw-r--r--compiler/suggest.nim61
-rw-r--r--compiler/types.nim35
33 files changed, 2404 insertions, 2027 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index c43bef926..eb12c977f 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -275,6 +275,7 @@ const
 
   sfDirty* = sfPure
     # template is not hygienic (old styled template)
+    # module, compiled from a dirty-buffer
 
   sfAnon* = sfDiscardable
     # symbol name that was generated by the compiler
@@ -680,7 +681,6 @@ type
     size*: BiggestInt         # the size of the type in bytes
                               # -1 means that the size is unkwown
     align*: int               # the type's alignment requirements
-    containerID*: int         # used for type checking of generics
     loc*: TLoc
 
   TPair*{.final.} = object 
@@ -768,6 +768,9 @@ const
                   nkCommand, nkCallStrLit, nkHiddenCallConv}
 
   nkLambdaKinds* = {nkLambda, nkDo}
+  declarativeDefs* = {nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef}
+  procDefs* = nkLambdaKinds + declarativeDefs
+
   nkSymChoices* = {nkClosedSymChoice, nkOpenSymChoice}
   nkStrKinds* = {nkStrLit..nkTripleStrLit}
 
@@ -1017,7 +1020,6 @@ proc assignType(dest, src: PType) =
   dest.n = src.n
   dest.size = src.size
   dest.align = src.align
-  dest.containerID = src.containerID
   dest.destructor = src.destructor
   # this fixes 'type TLock = TSysLock':
   if src.sym != nil:
diff --git a/compiler/c2nim/clex.nim b/compiler/c2nim/clex.nim
index db8527aa0..3681fa8e1 100644
--- a/compiler/c2nim/clex.nim
+++ b/compiler/c2nim/clex.nim
@@ -13,7 +13,7 @@
 
 
 import 
-  options, msgs, strutils, platform, lexbase, llstream
+  options, msgs, strutils, platform, nimlexbase, llstream
 
 const 
   MaxLineLength* = 80         # lines longer than this lead to a warning
@@ -338,8 +338,8 @@ proc getNumber(L: var TLexer, tok: var TToken) =
   
 proc HandleCRLF(L: var TLexer, pos: int): int = 
   case L.buf[pos]
-  of CR: result = lexbase.HandleCR(L, pos)
-  of LF: result = lexbase.HandleLF(L, pos)
+  of CR: result = nimlexbase.HandleCR(L, pos)
+  of LF: result = nimlexbase.HandleLF(L, pos)
   else: result = pos
   
 proc escape(L: var TLexer, tok: var TToken, allowEmpty=false) = 
@@ -405,10 +405,10 @@ proc getString(L: var TLexer, tok: var TToken) =
       Inc(pos)
       break
     of CR: 
-      pos = lexbase.HandleCR(L, pos)
+      pos = nimlexbase.HandleCR(L, pos)
       buf = L.buf
     of LF: 
-      pos = lexbase.HandleLF(L, pos)
+      pos = nimlexbase.HandleLF(L, pos)
       buf = L.buf
     of lexbase.EndOfFile: 
       var line2 = L.linenumber
@@ -449,7 +449,7 @@ proc scanLineComment(L: var TLexer, tok: var TToken) =
   while true: 
     inc(pos, 2)               # skip //
     add(tok.s, '#')
-    while not (buf[pos] in {CR, LF, lexbase.EndOfFile}): 
+    while not (buf[pos] in {CR, LF, nimlexbase.EndOfFile}): 
       add(tok.s, buf[pos])
       inc(pos)
     pos = handleCRLF(L, pos)
@@ -490,7 +490,7 @@ proc scanStarComment(L: var TLexer, tok: var TToken) =
         break 
       else: 
         add(tok.s, '*')
-    of lexbase.EndOfFile: 
+    of nimlexbase.EndOfFile: 
       lexMessage(L, errTokenExpected, "*/")
     else: 
       add(tok.s, buf[pos])
@@ -743,7 +743,7 @@ proc getTok(L: var TLexer, tok: var TToken) =
         getDirective(L, tok)
     of '"': getString(L, tok)
     of '\'': getCharLit(L, tok)
-    of lexbase.EndOfFile: 
+    of nimlexbase.EndOfFile: 
       tok.xkind = pxEof
     else: 
       tok.s = $c
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 6a2ffe752..2f7804384 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -9,9 +9,6 @@
 
 # included from cgen.nim
 
-proc lenField: PRope {.inline.} = 
-  result = toRope(if gCmd != cmdCompileToCpp: "Sup.len" else: "len")
-
 # -------------------------- constant expressions ------------------------
 
 proc intLiteral(i: biggestInt): PRope =
@@ -207,8 +204,6 @@ proc asgnComplexity(n: PNode): int =
         result += asgnComplexity(t)
     else: nil
 
-proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags)
-
 proc optAsgnLoc(a: TLoc, t: PType, field: PRope): TLoc =
   result.k = locField
   result.s = a.s
@@ -345,11 +340,6 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
     linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
   else: InternalError("genAssignment(" & $ty.kind & ')')
 
-proc expr(p: BProc, e: PNode, d: var TLoc)
-proc initLocExpr(p: BProc, e: PNode, result: var TLoc) =
-  initLoc(result, locNone, e.typ, OnUnknown)
-  expr(p, e, result)
-
 proc getDestLoc(p: BProc, d: var TLoc, typ: PType) =
   if d.k == locNone: getTemp(p, typ, d)
 
@@ -377,15 +367,15 @@ proc putIntoDest(p: BProc, d: var TLoc, t: PType, r: PRope) =
     d.a = -1
 
 proc binaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) =
-  var b: TLoc
+  var a, b: TLoc
   if d.k != locNone: InternalError(e.info, "binaryStmt")
-  InitLocExpr(p, e.sons[1], d)
+  InitLocExpr(p, e.sons[1], a)
   InitLocExpr(p, e.sons[2], b)
-  lineCg(p, cpsStmts, frmt, rdLoc(d), rdLoc(b))
+  lineCg(p, cpsStmts, frmt, rdLoc(a), rdLoc(b))
 
 proc unaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) =
   var a: TLoc
-  if (d.k != locNone): InternalError(e.info, "unaryStmt")
+  if d.k != locNone: InternalError(e.info, "unaryStmt")
   InitLocExpr(p, e.sons[1], a)
   lineCg(p, cpsStmts, frmt, [rdLoc(a)])
 
@@ -840,44 +830,6 @@ proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
   else:
     genAssignment(p, d, tmp, {}) # no need for deep copying
 
-proc genIfExpr(p: BProc, n: PNode, d: var TLoc) =
-  #
-  #  if (!expr1) goto L1;
-  #  thenPart
-  #  goto LEnd
-  #  L1:
-  #  if (!expr2) goto L2;
-  #  thenPart2
-  #  goto LEnd
-  #  L2:
-  #  elsePart
-  #  Lend:
-  #
-  var
-    it: PNode
-    a, tmp: TLoc
-    Lend, Lelse: TLabel
-  getTemp(p, n.typ, tmp)      # force it into a temp!
-  Lend = getLabel(p)
-  for i in countup(0, sonsLen(n) - 1):
-    it = n.sons[i]
-    case it.kind
-    of nkElifExpr:
-      initLocExpr(p, it.sons[0], a)
-      Lelse = getLabel(p)
-      lineF(p, cpsStmts, "if (!$1) goto $2;$n", [rdLoc(a), Lelse])
-      expr(p, it.sons[1], tmp)
-      lineF(p, cpsStmts, "goto $1;$n", [Lend])
-      fixLabel(p, Lelse)
-    of nkElseExpr:
-      expr(p, it.sons[0], tmp)
-    else: internalError(n.info, "genIfExpr()")
-  fixLabel(p, Lend)
-  if d.k == locNone:
-    d = tmp
-  else:
-    genAssignment(p, d, tmp, {}) # no need for deep copying
-
 proc genEcho(p: BProc, n: PNode) =
   # this unusal way of implementing it ensures that e.g. ``echo("hallo", 45)``
   # is threadsafe.
@@ -889,8 +841,6 @@ proc genEcho(p: BProc, n: PNode) =
   linefmt(p, cpsStmts, "printf($1$2);$n",
           makeCString(repeatStr(n.len-1, "%s") & tnl), args)
 
-include ccgcalls
-
 proc genStrConcat(p: BProc, e: PNode, d: var TLoc) =
   #   <Nimrod code>
   #   s = 'Hello ' & name & ', how do you feel?' & 'z'
@@ -1791,11 +1741,10 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) =
   else:
     putIntoDest(p, d, t, tmp)
 
-proc genBlock(p: BProc, t: PNode, d: var TLoc)
-proc expr(p: BProc, e: PNode, d: var TLoc) =
-  case e.kind
+proc expr(p: BProc, n: PNode, d: var TLoc) =
+  case n.kind
   of nkSym:
-    var sym = e.sym
+    var sym = n.sym
     case sym.Kind
     of skMethod:
       if sym.getBody.kind == nkEmpty or sfDispatcher in sym.flags:
@@ -1808,22 +1757,22 @@ proc expr(p: BProc, e: PNode, d: var TLoc) =
     of skProc, skConverter, skIterator:
       genProc(p.module, sym)
       if sym.loc.r == nil or sym.loc.t == nil:
-        InternalError(e.info, "expr: proc not init " & sym.name.s)
+        InternalError(n.info, "expr: proc not init " & sym.name.s)
       putLocIntoDest(p, d, sym.loc)
     of skConst:
       if sfFakeConst in sym.flags:
         if sfGlobal in sym.flags: genVarPrototype(p.module, sym)
         putLocIntoDest(p, d, sym.loc)
       elif isSimpleConst(sym.typ):
-        putIntoDest(p, d, e.typ, genLiteral(p, sym.ast, sym.typ))
+        putIntoDest(p, d, n.typ, genLiteral(p, sym.ast, sym.typ))
       else:
         genComplexConst(p, sym, d)
     of skEnumField:
-      putIntoDest(p, d, e.typ, toRope(sym.position))
+      putIntoDest(p, d, n.typ, toRope(sym.position))
     of skVar, skForVar, skResult, skLet:
       if sfGlobal in sym.flags: genVarPrototype(p.module, sym)
       if sym.loc.r == nil or sym.loc.t == nil:
-        InternalError(e.info, "expr: var not init " & sym.name.s)
+        InternalError(n.info, "expr: var not init " & sym.name.s)
       if sfThread in sym.flags:
         AccessThreadLocalVar(p, sym)
         if emulatedThreadVars(): 
@@ -1834,75 +1783,139 @@ proc expr(p: BProc, e: PNode, d: var TLoc) =
         putLocIntoDest(p, d, sym.loc)
     of skTemp:
       if sym.loc.r == nil or sym.loc.t == nil:
-        InternalError(e.info, "expr: temp not init " & sym.name.s)
+        InternalError(n.info, "expr: temp not init " & sym.name.s)
       putLocIntoDest(p, d, sym.loc)
     of skParam:
       if sym.loc.r == nil or sym.loc.t == nil:
-        InternalError(e.info, "expr: param not init " & sym.name.s)
+        InternalError(n.info, "expr: param not init " & sym.name.s)
       putLocIntoDest(p, d, sym.loc)
-    else: InternalError(e.info, "expr(" & $sym.kind & "); unknown symbol")
+    else: InternalError(n.info, "expr(" & $sym.kind & "); unknown symbol")
+  of nkNilLit:
+    if not isEmptyType(n.typ):
+      putIntoDest(p, d, n.typ, genLiteral(p, n))
   of nkStrLit..nkTripleStrLit, nkIntLit..nkUInt64Lit,
-     nkFloatLit..nkFloat128Lit, nkNilLit, nkCharLit:
-    putIntoDest(p, d, e.typ, genLiteral(p, e))
+     nkFloatLit..nkFloat128Lit, nkCharLit:
+    putIntoDest(p, d, n.typ, genLiteral(p, n))
   of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand,
      nkCallStrLit:
-    if e.sons[0].kind == nkSym and e.sons[0].sym.magic != mNone:
-      genMagicExpr(p, e, d, e.sons[0].sym.magic)
+    genLineDir(p, n)
+    let op = n.sons[0]
+    if n.typ.isNil:
+      # discard the value:
+      var a: TLoc
+      if op.kind == nkSym and op.sym.magic != mNone:
+        genMagicExpr(p, n, a, op.sym.magic)
+      else:
+        genCall(p, n, a)
     else:
-      genCall(p, e, d)
+      # load it into 'd':
+      if op.kind == nkSym and op.sym.magic != mNone:
+        genMagicExpr(p, n, d, op.sym.magic)
+      else:
+        genCall(p, n, d)
   of nkCurly:
-    if isDeepConstExpr(e) and e.len != 0:
-      putIntoDest(p, d, e.typ, genSetNode(p, e))
+    if isDeepConstExpr(n) and n.len != 0:
+      putIntoDest(p, d, n.typ, genSetNode(p, n))
     else:
-      genSetConstr(p, e, d)
+      genSetConstr(p, n, d)
   of nkBracket:
-    if isDeepConstExpr(e) and e.len != 0:
-      exprComplexConst(p, e, d)
-    elif skipTypes(e.typ, abstractVarRange).kind == tySequence:
-      genSeqConstr(p, e, d)
+    if isDeepConstExpr(n) and n.len != 0:
+      exprComplexConst(p, n, d)
+    elif skipTypes(n.typ, abstractVarRange).kind == tySequence:
+      genSeqConstr(p, n, d)
     else:
-      genArrayConstr(p, e, d)
+      genArrayConstr(p, n, d)
   of nkPar:
-    if isDeepConstExpr(e) and e.len != 0:
-      exprComplexConst(p, e, d)
+    if isDeepConstExpr(n) and n.len != 0:
+      exprComplexConst(p, n, d)
     else:
-      genTupleConstr(p, e, d)
-  of nkObjConstr: genObjConstr(p, e, d)
-  of nkCast: genCast(p, e, d)
-  of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, e, d)
-  of nkHiddenAddr, nkAddr: genAddr(p, e, d)
+      genTupleConstr(p, n, d)
+  of nkObjConstr: genObjConstr(p, n, d)
+  of nkCast: genCast(p, n, d)
+  of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, d)
+  of nkHiddenAddr, nkAddr: genAddr(p, n, d)
   of nkBracketExpr:
-    var ty = skipTypes(e.sons[0].typ, abstractVarRange)
+    var ty = skipTypes(n.sons[0].typ, abstractVarRange)
     if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.sons[0], abstractVarRange)
     case ty.kind
-    of tyArray, tyArrayConstr: genArrayElem(p, e, d)
-    of tyOpenArray, tyVarargs: genOpenArrayElem(p, e, d)
-    of tySequence, tyString: genSeqElem(p, e, d)
-    of tyCString: genCStringElem(p, e, d)
-    of tyTuple: genTupleElem(p, e, d)
-    else: InternalError(e.info, "expr(nkBracketExpr, " & $ty.kind & ')')
-  of nkDerefExpr, nkHiddenDeref: genDeref(p, e, d)
-  of nkDotExpr: genRecordField(p, e, d)
-  of nkCheckedFieldExpr: genCheckedRecordField(p, e, d)
-  of nkBlockExpr: genBlock(p, e, d)
-  of nkStmtListExpr: genStmtListExpr(p, e, d)
-  of nkIfExpr: genIfExpr(p, e, d)
-  of nkObjDownConv: downConv(p, e, d)
-  of nkObjUpConv: upConv(p, e, d)
-  of nkChckRangeF: genRangeChck(p, e, d, "chckRangeF")
-  of nkChckRange64: genRangeChck(p, e, d, "chckRange64")
-  of nkChckRange: genRangeChck(p, e, d, "chckRange")
-  of nkStringToCString: convStrToCStr(p, e, d)
-  of nkCStringToString: convCStrToStr(p, e, d)
+    of tyArray, tyArrayConstr: genArrayElem(p, n, d)
+    of tyOpenArray, tyVarargs: genOpenArrayElem(p, n, d)
+    of tySequence, tyString: genSeqElem(p, n, d)
+    of tyCString: genCStringElem(p, n, d)
+    of tyTuple: genTupleElem(p, n, d)
+    else: InternalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')')
+  of nkDerefExpr, nkHiddenDeref: genDeref(p, n, d)
+  of nkDotExpr: genRecordField(p, n, d)
+  of nkCheckedFieldExpr: genCheckedRecordField(p, n, d)
+  of nkBlockExpr, nkBlockStmt: genBlock(p, n, d)
+  of nkStmtListExpr: genStmtListExpr(p, n, d)
+  of nkStmtList:
+    for i in countup(0, sonsLen(n) - 1): genStmts(p, n.sons[i])
+  of nkIfExpr, nkIfStmt: genIf(p, n, d)
+  of nkObjDownConv: downConv(p, n, d)
+  of nkObjUpConv: upConv(p, n, d)
+  of nkChckRangeF: genRangeChck(p, n, d, "chckRangeF")
+  of nkChckRange64: genRangeChck(p, n, d, "chckRange64")
+  of nkChckRange: genRangeChck(p, n, d, "chckRange")
+  of nkStringToCString: convStrToCStr(p, n, d)
+  of nkCStringToString: convCStrToStr(p, n, d)
   of nkLambdaKinds:
-    var sym = e.sons[namePos].sym
+    var sym = n.sons[namePos].sym
     genProc(p.module, sym)
     if sym.loc.r == nil or sym.loc.t == nil:
-      InternalError(e.info, "expr: proc not init " & sym.name.s)
+      InternalError(n.info, "expr: proc not init " & sym.name.s)
     putLocIntoDest(p, d, sym.loc)
-  of nkClosure: genClosure(p, e, d)
-  of nkMetaNode: expr(p, e.sons[0], d)
-  else: InternalError(e.info, "expr(" & $e.kind & "); unknown node kind")
+  of nkClosure: genClosure(p, n, d)
+  of nkMetaNode: expr(p, n.sons[0], d)
+
+  of nkEmpty:  nil
+  of nkWhileStmt: genWhileStmt(p, n)
+  of nkVarSection, nkLetSection: genVarStmt(p, n)
+  of nkConstSection: genConstStmt(p, n)
+  of nkForStmt: internalError(n.info, "for statement not eliminated")
+  of nkCaseStmt: genCase(p, n, d)
+  of nkReturnStmt: genReturnStmt(p, n)
+  of nkBreakStmt: genBreakStmt(p, n)
+  of nkAsgn: genAsgn(p, n, fastAsgn=false)
+  of nkFastAsgn:
+    # transf is overly aggressive with 'nkFastAsgn', so we work around here.
+    # See tests/run/tcnstseq3 for an example that would fail otherwise.
+    genAsgn(p, n, fastAsgn=p.prc != nil)
+  of nkDiscardStmt:
+    if n.sons[0].kind != nkEmpty:
+      var a: TLoc
+      genLineDir(p, n)
+      initLocExpr(p, n.sons[0], a)
+  of nkAsmStmt: genAsmStmt(p, n)
+  of nkTryStmt:
+    if gCmd == cmdCompileToCpp: genTryCpp(p, n, d)
+    else: genTry(p, n, d)
+  of nkRaiseStmt: genRaiseStmt(p, n)
+  of nkTypeSection:
+    # we have to emit the type information for object types here to support
+    # separate compilation:
+    genTypeSection(p.module, n)
+  of nkCommentStmt, nkIteratorDef, nkIncludeStmt, 
+     nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, 
+     nkFromStmt, nkTemplateDef, nkMacroDef: 
+    nil
+  of nkPragma: genPragma(p, n)
+  of nkProcDef, nkMethodDef, nkConverterDef: 
+    if (n.sons[genericParamsPos].kind == nkEmpty): 
+      var prc = n.sons[namePos].sym
+      if (optDeadCodeElim notin gGlobalOptions and
+          sfDeadCodeElim notin getModule(prc).flags) or
+          ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or
+          (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or
+          (prc.kind == skMethod): 
+        # we have not only the header: 
+        if prc.getBody.kind != nkEmpty or lfDynamicLib in prc.loc.flags: 
+          genProc(p.module, prc)
+  of nkParForStmt: genParForStmt(p, n)
+  of nkState: genState(p, n)
+  of nkGotoState: genGotoState(p, n)
+  of nkBreakState: genBreakState(p, n)
+  else: InternalError(n.info, "expr(" & $n.kind & "); unknown node kind")
 
 proc genNamedConstExpr(p: BProc, n: PNode): PRope =
   if n.kind == nkExprColonExpr: result = genConstExpr(p, n.sons[1])
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index e11678861..b25ad613c 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -102,6 +102,11 @@ proc genSimpleBlock(p: BProc, stmts: PNode) {.inline.} =
   genStmts(p, stmts)
   endBlock(p)
 
+proc exprBlock(p: BProc, n: PNode, d: var TLoc) =
+  startBlock(p)
+  expr(p, n, d)
+  endBlock(p)
+
 template preserveBreakIdx(body: stmt): stmt {.immediate.} =
   var oldBreakIdx = p.breakIdx
   body
@@ -212,42 +217,48 @@ proc genConstStmt(p: BProc, t: PNode) =
           appf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n", 
                [getTypeDesc(p.module, c.typ), c.loc.r, genConstExpr(p, c.ast)])
     
-proc genIfStmt(p: BProc, n: PNode) = 
+proc genIf(p: BProc, n: PNode, d: var TLoc) =
   #
-  #  if (!expr1) goto L1;
-  #  { thenPart }
+  #  { if (!expr1) goto L1;
+  #   thenPart }
   #  goto LEnd
   #  L1:
-  #  if (!expr2) goto L2;
-  #  { thenPart2 }
+  #  { if (!expr2) goto L2;
+  #   thenPart2 }
   #  goto LEnd
   #  L2:
   #  { elsePart }
   #  Lend:
-  var 
+  var
     a: TLoc
     Lelse: TLabel
+  if not isEmptyType(n.typ) and d.k == locNone:
+    getTemp(p, n.typ, d)
   genLineDir(p, n)
-  var Lend = getLabel(p)
+  let Lend = getLabel(p)
   for i in countup(0, sonsLen(n) - 1): 
-    var it = n.sons[i]
-    case it.kind
-    of nkElifBranch: 
+    let it = n.sons[i]
+    if it.len == 2: 
+      when newScopeForIf: startBlock(p)
       initLocExpr(p, it.sons[0], a)
       Lelse = getLabel(p)
       inc(p.labels)
       lineFF(p, cpsStmts, "if (!$1) goto $2;$n",
-            "br i1 $1, label %LOC$3, label %$2$n" & "LOC$3: $n",
+            "br i1 $1, label %LOC$3, label %$2$nLOC$3: $n",
             [rdLoc(a), Lelse, toRope(p.labels)])
-      genSimpleBlock(p, it.sons[1])
-      if sonsLen(n) > 1: 
+      when not newScopeForIf: startBlock(p)
+      expr(p, it.sons[1], d)
+      endBlock(p)
+      if sonsLen(n) > 1:
         lineFF(p, cpsStmts, "goto $1;$n", "br label %$1$n", [Lend])
       fixLabel(p, Lelse)
-    of nkElse:
-      genSimpleBlock(p, it.sons[0])
-    else: internalError(n.info, "genIfStmt()")
+    elif it.len == 1:
+      startBlock(p)
+      expr(p, it.sons[0], d)
+      endBlock(p)
+    else: internalError(n.info, "genIf()")
   if sonsLen(n) > 1: fixLabel(p, Lend)
-  
+
 proc blockLeaveActions(p: BProc, howMany: int) = 
   var L = p.nestedTryStmts.len
   # danger of endless recursion! we workaround this here by a temp stack
@@ -310,16 +321,15 @@ proc genWhileStmt(p: BProc, t: PNode) =
 proc genBlock(p: BProc, t: PNode, d: var TLoc) =
   preserveBreakIdx:
     p.breakIdx = startBlock(p)
-    if t.sons[0].kind != nkEmpty: 
+    if t.sons[0].kind != nkEmpty:
       # named block?
       assert(t.sons[0].kind == nkSym)
       var sym = t.sons[0].sym
       sym.loc.k = locOther
       sym.loc.a = p.breakIdx
-    if t.kind == nkBlockExpr: genStmtListExpr(p, t.sons[1], d)
-    else: genStmts(p, t.sons[1])
+    expr(p, t.sons[1], d)
     endBlock(p)
-  
+
 proc genParForStmt(p: BProc, t: PNode) =
   assert(sonsLen(t) == 3)
   inc(p.withinLoop)
@@ -409,42 +419,45 @@ proc genCaseGenericBranch(p: BProc, b: PNode, e: TLoc,
       initLocExpr(p, b.sons[i], x)
       lineCg(p, cpsStmts, eqFormat, [rdCharLoc(e), rdCharLoc(x), labl])
 
-proc genCaseSecondPass(p: BProc, t: PNode, labId, until: int): TLabel = 
+proc genCaseSecondPass(p: BProc, t: PNode, d: var TLoc, 
+                       labId, until: int): TLabel = 
   var Lend = getLabel(p)
-  for i in 1..until: 
+  for i in 1..until:
     lineF(p, cpsStmts, "LA$1: ;$n", [toRope(labId + i)])
     if t.sons[i].kind == nkOfBranch:
       var length = sonsLen(t.sons[i])
-      genSimpleBlock(p, t.sons[i].sons[length - 1])
+      exprBlock(p, t.sons[i].sons[length - 1], d)
       lineF(p, cpsStmts, "goto $1;$n", [Lend])
-    else: 
-      genSimpleBlock(p, t.sons[i].sons[0])
+    else:
+      exprBlock(p, t.sons[i].sons[0], d)
   result = Lend
 
-proc genIfForCaseUntil(p: BProc, t: PNode, rangeFormat, eqFormat: TFormatStr,
-                       until: int, a: TLoc): TLabel = 
+proc genIfForCaseUntil(p: BProc, t: PNode, d: var TLoc,
+                       rangeFormat, eqFormat: TFormatStr,
+                       until: int, a: TLoc): TLabel =
   # generate a C-if statement for a Nimrod case statement
   var labId = p.labels
-  for i in 1..until: 
+  for i in 1..until:
     inc(p.labels)
     if t.sons[i].kind == nkOfBranch: # else statement
-      genCaseGenericBranch(p, t.sons[i], a, rangeFormat, eqFormat, 
+      genCaseGenericBranch(p, t.sons[i], a, rangeFormat, eqFormat,
                            con("LA", toRope(p.labels)))
-    else: 
+    else:
       lineF(p, cpsStmts, "goto LA$1;$n", [toRope(p.labels)])
-  if until < t.len-1: 
+  if until < t.len-1:
     inc(p.labels)
     var gotoTarget = p.labels
     lineF(p, cpsStmts, "goto LA$1;$n", [toRope(gotoTarget)])
-    result = genCaseSecondPass(p, t, labId, until)
+    result = genCaseSecondPass(p, t, d, labId, until)
     lineF(p, cpsStmts, "LA$1: ;$n", [toRope(gotoTarget)])
   else:
-    result = genCaseSecondPass(p, t, labId, until)
+    result = genCaseSecondPass(p, t, d, labId, until)
 
-proc genCaseGeneric(p: BProc, t: PNode, rangeFormat, eqFormat: TFormatStr) = 
+proc genCaseGeneric(p: BProc, t: PNode, d: var TLoc, 
+                    rangeFormat, eqFormat: TFormatStr) = 
   var a: TLoc
   initLocExpr(p, t.sons[0], a)
-  var Lend = genIfForCaseUntil(p, t, rangeFormat, eqFormat, sonsLen(t)-1, a)
+  var Lend = genIfForCaseUntil(p, t, d, rangeFormat, eqFormat, sonsLen(t)-1, a)
   fixLabel(p, Lend)
 
 proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel, 
@@ -459,12 +472,12 @@ proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel,
     appcg(p.module, branches[j], "if (#eqStrings($1, $2)) goto $3;$n", 
          [rdLoc(e), rdLoc(x), labl])
 
-proc genStringCase(p: BProc, t: PNode) = 
+proc genStringCase(p: BProc, t: PNode, d: var TLoc) =
   # count how many constant strings there are in the case:
   var strings = 0
-  for i in countup(1, sonsLen(t) - 1): 
+  for i in countup(1, sonsLen(t) - 1):
     if t.sons[i].kind == nkOfBranch: inc(strings, sonsLen(t.sons[i]) - 1)
-  if strings > stringCaseThreshold: 
+  if strings > stringCaseThreshold:
     var bitMask = math.nextPowerOfTwo(strings) - 1
     var branches: seq[PRope]
     newSeq(branches, bitMask + 1)
@@ -482,23 +495,17 @@ proc genStringCase(p: BProc, t: PNode) =
     linefmt(p, cpsStmts, "switch (#hashString($1) & $2) {$n", 
             rdLoc(a), toRope(bitMask))
     for j in countup(0, high(branches)):
-      when false:
-        let interior = cast[int](interiorAllocatedPtr(addr(branches[0])))+
-                                 2*sizeof(pointer)
-        let brn = cast[int](cast[pointer](branches))
-        if interior != brn:
-          echo "BUG! ", interior, "-", brn
       if branches[j] != nil:
         lineF(p, cpsStmts, "case $1: $n$2break;$n", 
              [intLiteral(j), branches[j]])
     lineF(p, cpsStmts, "}$n") # else statement:
-    if t.sons[sonsLen(t) - 1].kind != nkOfBranch: 
+    if t.sons[sonsLen(t)-1].kind != nkOfBranch: 
       lineF(p, cpsStmts, "goto LA$1;$n", [toRope(p.labels)]) 
     # third pass: generate statements
-    var Lend = genCaseSecondPass(p, t, labId, sonsLen(t)-1)
+    var Lend = genCaseSecondPass(p, t, d, labId, sonsLen(t)-1)
     fixLabel(p, Lend)
-  else: 
-    genCaseGeneric(p, t, "", "if (#eqStrings($1, $2)) goto $3;$n")
+  else:
+    genCaseGeneric(p, t, d, "", "if (#eqStrings($1, $2)) goto $3;$n")
   
 proc branchHasTooBigRange(b: PNode): bool = 
   for i in countup(0, sonsLen(b)-2): 
@@ -533,14 +540,14 @@ proc genCaseRange(p: BProc, branch: PNode) =
     else:
       lineF(p, cpsStmts, "case $1:$n", [genLiteral(p, branch[j])])
 
-proc genOrdinalCase(p: BProc, n: PNode) = 
+proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
   # analyse 'case' statement:
   var splitPoint = IfSwitchSplitPoint(p, n)
   
   # generate if part (might be empty):
   var a: TLoc
   initLocExpr(p, n.sons[0], a)
-  var Lend = if splitPoint > 0: genIfForCaseUntil(p, n, 
+  var Lend = if splitPoint > 0: genIfForCaseUntil(p, n, d,
                     rangeFormat = "if ($1 >= $2 && $1 <= $3) goto $4;$n",
                     eqFormat = "if ($1 == $2) goto $3;$n", 
                     splitPoint, a) else: nil
@@ -553,28 +560,29 @@ proc genOrdinalCase(p: BProc, n: PNode) =
       var branch = n[i]
       if branch.kind == nkOfBranch: 
         genCaseRange(p, branch)
-        genSimpleBlock(p, branch.lastSon)
       else: 
         # else part of case statement:
         lineF(p, cpsStmts, "default:$n")
-        genSimpleBlock(p, branch[0])
         hasDefault = true
+      exprBlock(p, branch.lastSon, d)
       lineF(p, cpsStmts, "break;$n")
     if (hasAssume in CC[ccompiler].props) and not hasDefault: 
       lineF(p, cpsStmts, "default: __assume(0);$n")
     lineF(p, cpsStmts, "}$n")
   if Lend != nil: fixLabel(p, Lend)
   
-proc genCaseStmt(p: BProc, t: PNode) = 
+proc genCase(p: BProc, t: PNode, d: var TLoc) = 
   genLineDir(p, t)
+  if not isEmptyType(t.typ) and d.k == locNone:
+    getTemp(p, t.typ, d)
   case skipTypes(t.sons[0].typ, abstractVarRange).kind
-  of tyString: 
-    genStringCase(p, t)
+  of tyString:
+    genStringCase(p, t, d)
   of tyFloat..tyFloat128: 
-    genCaseGeneric(p, t, "if ($1 >= $2 && $1 <= $3) goto $4;$n", 
-                   "if ($1 == $2) goto $3;$n") 
-  else: 
-    genOrdinalCase(p, t)
+    genCaseGeneric(p, t, d, "if ($1 >= $2 && $1 <= $3) goto $4;$n", 
+                            "if ($1 == $2) goto $3;$n")
+  else:
+    genOrdinalCase(p, t, d)
   
 proc hasGeneralExceptSection(t: PNode): bool = 
   var length = sonsLen(t)
@@ -586,7 +594,7 @@ proc hasGeneralExceptSection(t: PNode): bool =
     inc(i)
   result = false
 
-proc genTryStmtCpp(p: BProc, t: PNode) =
+proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
   # code to generate:
   #
   # XXX: There should be a standard dispatch algorithm
@@ -607,6 +615,8 @@ proc genTryStmtCpp(p: BProc, t: PNode) =
   #      }
   #  }
   #  finallyPart();
+  if not isEmptyType(t.typ) and d.k == locNone:
+    getTemp(p, t.typ, d)
   var
     exc: PRope
     i, length, blen: int
@@ -615,7 +625,7 @@ proc genTryStmtCpp(p: BProc, t: PNode) =
   discard cgsym(p.module, "E_Base")
   add(p.nestedTryStmts, t)
   startBlock(p, "try {$n")
-  genStmts(p, t.sons[0])
+  expr(p, t.sons[0], d)
   length = sonsLen(t)
   endBlock(p, ropecg(p.module, "} catch (NimException& $1) {$n", [exc]))
   if optStackTrace in p.Options:
@@ -629,7 +639,7 @@ proc genTryStmtCpp(p: BProc, t: PNode) =
     if blen == 1:
       # general except section:
       catchAllPresent = true
-      genSimpleBlock(p, t.sons[i].sons[0])
+      exprBlock(p, t.sons[i].sons[0], d)
     else:
       var orExpr: PRope = nil
       for j in countup(0, blen - 2):
@@ -639,7 +649,7 @@ proc genTryStmtCpp(p: BProc, t: PNode) =
               "#isObj($1.exp->m_type, $2)",
               [exc, genTypeInfo(p.module, t.sons[i].sons[j].typ)])
       lineF(p, cpsStmts, "if ($1) ", [orExpr])
-      genSimpleBlock(p, t.sons[i].sons[blen-1])
+      exprBlock(p, t.sons[i].sons[blen-1], d)
     inc(i)
   
   # reraise the exception if there was no catch all
@@ -649,7 +659,7 @@ proc genTryStmtCpp(p: BProc, t: PNode) =
     startBlock(p)
     var finallyBlock = t.lastSon
     if finallyBlock.kind == nkFinally:
-      genStmts(p, finallyBlock.sons[0])
+      expr(p, finallyBlock.sons[0], d)
     line(p, cpsStmts, ~"throw;$n")
     endBlock(p)
   
@@ -658,9 +668,9 @@ proc genTryStmtCpp(p: BProc, t: PNode) =
   
   discard pop(p.nestedTryStmts)
   if (i < length) and (t.sons[i].kind == nkFinally):
-    genSimpleBlock(p, t.sons[i].sons[0])
+    exprBlock(p, t.sons[i].sons[0], d)
   
-proc genTryStmt(p: BProc, t: PNode) = 
+proc genTry(p: BProc, t: PNode, d: var TLoc) = 
   # code to generate:
   #
   # XXX: There should be a standard dispatch algorithm
@@ -689,6 +699,8 @@ proc genTryStmt(p: BProc, t: PNode) =
   #  if (exception not cleared)
   #    propagateCurrentException();
   #
+  if not isEmptyType(t.typ) and d.k == locNone:
+    getTemp(p, t.typ, d)
   genLineDir(p, t)
   var safePoint = getTempName()
   discard cgsym(p.module, "E_Base")
@@ -698,7 +710,7 @@ proc genTryStmt(p: BProc, t: PNode) =
   startBlock(p, "if ($1.status == 0) {$n", [safePoint])
   var length = sonsLen(t)
   add(p.nestedTryStmts, t)
-  genStmts(p, t.sons[0])
+  expr(p, t.sons[0], d)
   linefmt(p, cpsStmts, "#popSafePoint();$n")
   endBlock(p)
   startBlock(p, "else {$n")
@@ -714,7 +726,7 @@ proc genTryStmt(p: BProc, t: PNode) =
       if i > 1: lineF(p, cpsStmts, "else")
       startBlock(p)
       linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint)
-      genStmts(p, t.sons[i].sons[0])
+      expr(p, t.sons[i].sons[0], d)
       linefmt(p, cpsStmts, "#popCurrentException();$n")
       endBlock(p)
     else:
@@ -728,7 +740,7 @@ proc genTryStmt(p: BProc, t: PNode) =
       if i > 1: line(p, cpsStmts, "else ")
       startBlock(p, "if ($1) {$n", [orExpr])
       linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint)
-      genStmts(p, t.sons[i].sons[blen-1])
+      expr(p, t.sons[i].sons[blen-1], d)
       linefmt(p, cpsStmts, "#popCurrentException();$n")
       endBlock(p)
     inc(i)
@@ -736,7 +748,7 @@ proc genTryStmt(p: BProc, t: PNode) =
   discard pop(p.nestedTryStmts)
   endBlock(p) # end of else block
   if i < length and t.sons[i].kind == nkFinally:
-    genSimpleBlock(p, t.sons[i].sons[0])
+    exprBlock(p, t.sons[i].sons[0], d)
   linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", safePoint)
 
 proc genAsmOrEmitStmt(p: BProc, t: PNode): PRope = 
@@ -860,61 +872,5 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
 
 proc genStmts(p: BProc, t: PNode) = 
   var a: TLoc
-  case t.kind
-  of nkEmpty: 
-    nil
-  of nkStmtList: 
-    for i in countup(0, sonsLen(t) - 1): genStmts(p, t.sons[i])
-  of nkBlockStmt: genBlock(p, t, a)
-  of nkIfStmt: genIfStmt(p, t)
-  of nkWhileStmt: genWhileStmt(p, t)
-  of nkVarSection, nkLetSection: genVarStmt(p, t)
-  of nkConstSection: genConstStmt(p, t)
-  of nkForStmt: internalError(t.info, "for statement not eliminated")
-  of nkCaseStmt: genCaseStmt(p, t)
-  of nkReturnStmt: genReturnStmt(p, t)
-  of nkBreakStmt: genBreakStmt(p, t)
-  of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand, 
-     nkCallStrLit, nkClosure: 
-    genLineDir(p, t)
-    initLocExpr(p, t, a)
-  of nkAsgn: genAsgn(p, t, fastAsgn=false)
-  of nkFastAsgn: 
-    # transf is overly aggressive with 'nkFastAsgn', so we work around here.
-    # See tests/run/tcnstseq3 for an example that would fail otherwise.
-    genAsgn(p, t, fastAsgn=p.prc != nil)
-  of nkDiscardStmt:
-    if t.sons[0].kind != nkEmpty:
-      genLineDir(p, t)
-      initLocExpr(p, t.sons[0], a)
-  of nkAsmStmt: genAsmStmt(p, t)
-  of nkTryStmt: 
-    if gCmd == cmdCompileToCpp: genTryStmtCpp(p, t)
-    else: genTryStmt(p, t)
-  of nkRaiseStmt: genRaiseStmt(p, t)
-  of nkTypeSection: 
-    # we have to emit the type information for object types here to support
-    # separate compilation:
-    genTypeSection(p.module, t)
-  of nkCommentStmt, nkNilLit, nkIteratorDef, nkIncludeStmt, 
-     nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, 
-     nkFromStmt, nkTemplateDef, nkMacroDef: 
-    nil
-  of nkPragma: genPragma(p, t)
-  of nkProcDef, nkMethodDef, nkConverterDef: 
-    if (t.sons[genericParamsPos].kind == nkEmpty): 
-      var prc = t.sons[namePos].sym
-      if (optDeadCodeElim notin gGlobalOptions and
-          sfDeadCodeElim notin getModule(prc).flags) or
-          ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or
-          (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or
-          (prc.kind == skMethod): 
-        # we have not only the header: 
-        if prc.getBody.kind != nkEmpty or lfDynamicLib in prc.loc.flags: 
-          genProc(p.module, prc)
-  of nkParForStmt: genParForStmt(p, t)
-  of nkState: genState(p, t)
-  of nkGotoState: genGotoState(p, t)
-  of nkBreakState: genBreakState(p, t)
-  else: internalError(t.info, "genStmts(" & $t.kind & ')')
-  
+  expr(p, t, a)
+  InternalAssert a.k in {locNone, locTemp}
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index d37e79d6b..6f54f7e2f 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -137,17 +137,6 @@ proc mangleName(s: PSym): PRope =
       app(result, toRope(s.id))
     s.loc.r = result
 
-proc isCompileTimeOnly(t: PType): bool =
-  result = t.kind in {tyTypedesc, tyExpr}
-
-proc containsCompileTimeOnly(t: PType): bool =
-  if isCompileTimeOnly(t): return true
-  if t.sons != nil:
-    for i in 0 .. <t.sonsLen:
-      if t.sons[i] != nil and isCompileTimeOnly(t.sons[i]):
-        return true
-  return false
-
 proc typeName(typ: PType): PRope =
   result = if typ.sym != nil: typ.sym.name.s.mangle.toRope
            else: ~"TY"
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 753576aa0..f034f6675 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -552,9 +552,21 @@ proc genVarPrototype(m: BModule, sym: PSym)
 proc requestConstImpl(p: BProc, sym: PSym)
 proc genProc(m: BModule, prc: PSym)
 proc genStmts(p: BProc, t: PNode)
+proc expr(p: BProc, n: PNode, d: var TLoc)
 proc genProcPrototype(m: BModule, sym: PSym)
+proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc)
+proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags)
+proc intLiteral(i: biggestInt): PRope
+proc genLiteral(p: BProc, n: PNode): PRope
 
-include "ccgexprs.nim", "ccgstmts.nim"
+proc initLocExpr(p: BProc, e: PNode, result: var TLoc) =
+  initLoc(result, locNone, e.typ, OnUnknown)
+  expr(p, e, result)
+
+proc lenField: PRope {.inline.} =
+  result = toRope(if gCmd != cmdCompileToCpp: "Sup.len" else: "len")
+
+include ccgcalls, "ccgstmts.nim", "ccgexprs.nim"
 
 # ----------------------------- dynamic library handling -----------------
 # We don't finalize dynamic libs as this does the OS for us.
@@ -588,6 +600,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) =
             [loadlib, getStrLit(m, lib.path.strVal)]) 
     else:
       var p = newProc(nil, m)
+      p.options = p.options - {optStackTrace, optEndb}
       var dest: TLoc
       initLocExpr(p, lib.path, dest)
       app(m.s[cfsVars], p.s(cpsLocals))
@@ -1266,10 +1279,6 @@ proc updateCachedModule(m: BModule) =
 
   addFileToLink(cfilenoext)
 
-proc updateCachedModules* =
-  for m in cgenModules():
-    if m.fromCache: m.updateCachedModule
-
 proc myClose(b: PPassContext, n: PNode): PNode = 
   result = n
   if b == nil or passes.skipCodegen(n): return 
@@ -1279,24 +1288,28 @@ proc myClose(b: PPassContext, n: PNode): PNode =
     genStmts(m.initProc, n)
   # cached modules need to registered too: 
   registerModuleToMain(m.module)
-  
+
   if sfMainModule in m.module.flags: 
     var disp = generateMethodDispatchers()
     for i in 0..sonsLen(disp)-1: genProcAux(m, disp.sons[i].sym)
-    genMainProc(m) 
-    # we need to process the transitive closure because recursive module
-    # deps are allowed (and the system module is processed in the wrong
-    # order anyway)
-    if generatedHeader != nil: finishModule(generatedHeader)
-    while gForwardedProcsCounter > 0:
-      for m in cgenModules():
-        if not m.fromCache:
-          finishModule(m)
+    genMainProc(m)
+
+proc cgenWriteModules* =
+  # we need to process the transitive closure because recursive module
+  # deps are allowed (and the system module is processed in the wrong
+  # order anyway)
+  if generatedHeader != nil: finishModule(generatedHeader)
+  while gForwardedProcsCounter > 0:
     for m in cgenModules():
       if not m.fromCache:
-        writeModule(m, pending=true)
-    writeMapping(gMapping)
-    if generatedHeader != nil: writeHeader(generatedHeader)
+        finishModule(m)
+  for m in cgenModules():
+    if m.fromCache:
+      m.updateCachedModule
+    else:
+      m.writeModule(pending=true)
+  writeMapping(gMapping)
+  if generatedHeader != nil: writeHeader(generatedHeader)
 
 const cgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose)
 
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 27da03bbe..63704b328 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -207,6 +207,22 @@ proc processPath(path: string, notRelativeToProj = false): string =
     "projectname", options.gProjectName,
     "projectpath", options.gProjectPath])
 
+proc trackDirty(arg: string, info: TLineInfo) =
+  var a = arg.split(',')
+  if a.len != 4: LocalError(info, errTokenExpected,
+                            "DIRTY_BUFFER,ORIGINAL_FILE,LINE,COLUMN")
+  var line, column: int
+  if parseUtils.parseInt(a[2], line) <= 0:
+    LocalError(info, errInvalidNumber, a[1])
+  if parseUtils.parseInt(a[3], column) <= 0:
+    LocalError(info, errInvalidNumber, a[2])
+  
+  gDirtyBufferIdx = a[0].fileInfoIdx
+  gDirtyOriginalIdx = a[1].fileInfoIdx
+ 
+  optTrackPos = newLineInfo(gDirtyBufferIdx, line, column)
+  msgs.addCheckpoint(optTrackPos)
+
 proc track(arg: string, info: TLineInfo) = 
   var a = arg.split(',')
   if a.len != 3: LocalError(info, errTokenExpected, "FILE,LINE,COLUMN")
@@ -215,7 +231,8 @@ proc track(arg: string, info: TLineInfo) =
     LocalError(info, errInvalidNumber, a[1])
   if parseUtils.parseInt(a[2], column) <= 0:
     LocalError(info, errInvalidNumber, a[2])
-  msgs.addCheckpoint(newLineInfo(a[0], line, column))
+  optTrackPos = newLineInfo(a[0], line, column)
+  msgs.addCheckpoint(optTrackPos)
 
 proc dynlibOverride(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) =
   if pass in {passCmd2, passPP}:
@@ -469,6 +486,9 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) =
   of "track":
     expectArg(switch, arg, pass, info)
     track(arg, info)
+  of "trackdirty":
+    expectArg(switch, arg, pass, info)
+    trackDirty(arg, info)
   of "suggest": 
     expectNoArg(switch, arg, pass, info)
     incl(gGlobalOptions, optSuggest)
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index 2b7c567c6..9929b4bd9 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -237,7 +237,7 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
     of tkSymbol: 
       dispA(result, "<span class=\"Identifier\">$1</span>", 
             "\\spanIdentifier{$1}", [toRope(esc(d.target, literal))])
-    of tkInd, tkSad, tkDed, tkSpaces, tkInvalid: 
+    of tkSpaces, tkInvalid: 
       app(result, literal)
     of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi, 
        tkBracketDotLe, tkBracketDotRi, tkCurlyDotLe, tkCurlyDotRi, tkParDotLe, 
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index bb2f09151..44396100b 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -579,7 +579,7 @@ proc CallCCompiler*(projectfile: string) =
       else:
         rawMessage(errGenerated, " execution of an external program failed; " &
                    "rerun with --parallelBuild:1 to see the error message")
-  if optNoLinking notin gGlobalOptions:
+  if optNoLinking notin gGlobalOptions and cmds.len > 0:
     # call the linker:
     var it = PStrEntry(toLink.head)
     var objfiles = ""
@@ -603,7 +603,9 @@ proc CallCCompiler*(projectfile: string) =
       if optGenGuiApp in gGlobalOptions: buildGui = cc[c].buildGui
       else: buildGui = ""
       var exefile: string
-      if optGenDynLib in gGlobalOptions:
+      if options.outFile.len > 0: 
+        exefile = options.outFile
+      elif optGenDynLib in gGlobalOptions:
         exefile = platform.os[targetOS].dllFrmt % splitFile(projectFile).name
         buildDll = cc[c].buildDll
       else:
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index f2e317f47..a76f8a883 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -8,18 +8,17 @@
 #
 
 # This is the EMCAScript (also known as JavaScript) code generator.
-# **Invariant: each expression occurs only once in the generated
-# code!**
+# Soon also a Luajit code generator. ;-)
 
-import 
+import
   ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp,
   options, nversion, nimsets, msgs, crc, bitsets, idents, lists, types, os,
   times, ropes, math, passes, ccgutils, wordrecg, renderer, rodread, rodutils,
   intsets, cgmeth
 
-# implementation
-
-type 
+type
+  TTarget = enum
+    targetJS, targetLua
   TJSGen = object of TPassContext
     module: PSym
 
@@ -34,36 +33,45 @@ type
     etyString,                # JavaScript's string
     etyObject,                # JavaScript's reference to an object
     etyBaseIndex              # base + index needed
-  TCompRes{.final.} = object 
-    kind: TJSTypeKind
-    com: PRope               # computation part
-                             # address if this is a (address, index)-tuple
+  TResKind = enum
+    resNone,                  # not set
+    resExpr,                  # is some complex expression
+    resVal                    # is a temporary/value/l-value
+  TCompRes = object
+    kind: TResKind
+    typ: TJSTypeKind
     res: PRope               # result part; index if this is an
                              # (address, index)-tuple
+    address: PRope           # address of an (address, index)-tuple
   
-  TBlock{.final.} = object 
+  TBlock = object 
     id: int                  # the ID of the label; positive means that it
                              # has been used (i.e. the label should be emitted)
     isLoop: bool             # whether it's a 'block' or 'while'
   
-  TGlobals{.final.} = object 
+  TGlobals = object 
     typeInfo, code: PRope
     forwarded: seq[PSym]
     generatedSyms: TIntSet
     typeInfoGenerated: TIntSet
 
   PGlobals = ref TGlobals
-  TProc{.final.} = object 
+  PProc = ref TProc
+  TProc = object
     procDef: PNode
     prc: PSym
-    locals: PRope
+    locals, body: PRope
     options: TOptions
     module: BModule
     g: PGlobals
     BeforeRetNeeded: bool
-    unique: int
+    target: TTarget # duplicated here for faster dispatching
+    unique: int    # for temp identifier generation
     blocks: seq[TBlock]
+    up: PProc     # up the call chain; required for closure support
 
+template `|`(a, b: expr): expr {.immediate.} =
+  (if p.target == targetJS: a else: b)
 
 proc newGlobals(): PGlobals = 
   new(result)
@@ -72,25 +80,35 @@ proc newGlobals(): PGlobals =
   result.typeInfoGenerated = initIntSet()
 
 proc initCompRes(r: var TCompRes) = 
-  r.com = nil
+  r.address = nil
   r.res = nil
-  r.kind = etyNone
-
-proc initProc(p: var TProc, globals: PGlobals, module: BModule, procDef: PNode, 
-              options: TOptions) = 
-  p.blocks = @[]
-  p.options = options
-  p.module = module
-  p.procDef = procDef
-  p.g = globals
-  if procDef != nil: p.prc = procDef.sons[namePos].sym
+  r.typ = etyNone
+  r.kind = resNone
+
+proc rdLoc(a: TCompRes): PRope {.inline.} =
+  result = a.res
+  when false:
+    if a.typ != etyBaseIndex:
+      result = a.res
+    else:
+      result = ropef("$1[$2]", a.address, a.res)
+
+proc newProc(globals: PGlobals, module: BModule, procDef: PNode, 
+              options: TOptions): PProc =
+  result = PProc(
+    blocks: @[],
+    options: options,
+    module: module,
+    procDef: procDef,
+    g: globals)
+  if procDef != nil: result.prc = procDef.sons[namePos].sym
   
 const 
   MappedToObject = {tyObject, tyArray, tyArrayConstr, tyTuple, tyOpenArray, 
     tySet, tyVar, tyRef, tyPtr, tyBigNum, tyVarargs}
 
 proc mapType(typ: PType): TJSTypeKind = 
-  var t = skipTypes(typ, abstractInst)
+  let t = skipTypes(typ, abstractInst)
   case t.kind
   of tyVar, tyRef, tyPtr: 
     if skipTypes(t.sons[0], abstractInst).kind in mappedToObject: 
@@ -140,173 +158,20 @@ proc mangleName(s: PSym): PRope =
 
 proc makeJSString(s: string): PRope = strutils.escape(s).toRope
 
-proc genTypeInfo(p: var TProc, typ: PType): PRope
-proc genObjectFields(p: var TProc, typ: PType, n: PNode): PRope = 
-  var 
-    s, u: PRope
-    length: int
-    field: PSym
-    b: PNode
-  result = nil
-  case n.kind
-  of nkRecList: 
-    length = sonsLen(n)
-    if length == 1: 
-      result = genObjectFields(p, typ, n.sons[0])
-    else: 
-      s = nil
-      for i in countup(0, length - 1): 
-        if i > 0: app(s, ", " & tnl)
-        app(s, genObjectFields(p, typ, n.sons[i]))
-      result = ropef("{kind: 2, len: $1, offset: 0, " &
-          "typ: null, name: null, sons: [$2]}", [toRope(length), s])
-  of nkSym: 
-    field = n.sym
-    s = genTypeInfo(p, field.typ)
-    result = ropef("{kind: 1, offset: \"$1\", len: 0, " &
-        "typ: $2, name: $3, sons: null}", 
-                   [mangleName(field), s, makeJSString(field.name.s)])
-  of nkRecCase: 
-    length = sonsLen(n)
-    if (n.sons[0].kind != nkSym): InternalError(n.info, "genObjectFields")
-    field = n.sons[0].sym
-    s = genTypeInfo(p, field.typ)
-    for i in countup(1, length - 1): 
-      b = n.sons[i]           # branch
-      u = nil
-      case b.kind
-      of nkOfBranch: 
-        if sonsLen(b) < 2: 
-          internalError(b.info, "genObjectFields; nkOfBranch broken")
-        for j in countup(0, sonsLen(b) - 2): 
-          if u != nil: app(u, ", ")
-          if b.sons[j].kind == nkRange: 
-            appf(u, "[$1, $2]", [toRope(getOrdValue(b.sons[j].sons[0])), 
-                                 toRope(getOrdValue(b.sons[j].sons[1]))])
-          else: 
-            app(u, toRope(getOrdValue(b.sons[j])))
-      of nkElse: 
-        u = toRope(lengthOrd(field.typ))
-      else: internalError(n.info, "genObjectFields(nkRecCase)")
-      if result != nil: app(result, ", " & tnl)
-      appf(result, "[SetConstr($1), $2]", 
-           [u, genObjectFields(p, typ, lastSon(b))])
-    result = ropef("{kind: 3, offset: \"$1\", len: $3, " &
-        "typ: $2, name: $4, sons: [$5]}", [mangleName(field), s, 
-        toRope(lengthOrd(field.typ)), makeJSString(field.name.s), result])
-  else: internalError(n.info, "genObjectFields")
-  
-proc genObjectInfo(p: var TProc, typ: PType, name: PRope) = 
-  var s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " &
-                "finalizer: null};$n", [name, toRope(ord(typ.kind))])
-  prepend(p.g.typeInfo, s)
-  appf(p.g.typeInfo, "var NNI$1 = $2;$n", 
-       [toRope(typ.id), genObjectFields(p, typ, typ.n)])
-  appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)])
-  if (typ.kind == tyObject) and (typ.sons[0] != nil): 
-    appf(p.g.typeInfo, "$1.base = $2;$n", 
-         [name, genTypeInfo(p, typ.sons[0])])
-
-proc genTupleFields(p: var TProc, typ: PType): PRope =
-  var s: PRope = nil
-  for i in 0 .. <typ.len:
-    if i > 0: app(s, ", " & tnl)
-    s.appf("{kind: 1, offset: \"Field$1\", len: 0, " &
-           "typ: $2, name: \"Field$1\", sons: null}",
-           [i.toRope, genTypeInfo(p, typ.sons[i])])
-  result = ropef("{kind: 2, len: $1, offset: 0, " &
-                 "typ: null, name: null, sons: [$2]}", [toRope(typ.len), s])
-
-proc genTupleInfo(p: var TProc, typ: PType, name: PRope) = 
-  var s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " &
-                "finalizer: null};$n", [name, toRope(ord(typ.kind))])
-  prepend(p.g.typeInfo, s)
-  appf(p.g.typeInfo, "var NNI$1 = $2;$n", 
-       [toRope(typ.id), genTupleFields(p, typ)])
-  appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)])
-
-proc genEnumInfo(p: var TProc, typ: PType, name: PRope) =
-  let length = sonsLen(typ.n)
-  var s: PRope = nil
-  for i in countup(0, length - 1): 
-    if (typ.n.sons[i].kind != nkSym): InternalError(typ.n.info, "genEnumInfo")
-    let field = typ.n.sons[i].sym
-    if i > 0: app(s, ", " & tnl)
-    let extName = if field.ast == nil: field.name.s else: field.ast.strVal
-    appf(s, "{kind: 1, offset: $1, typ: $2, name: $3, len: 0, sons: null}", 
-         [toRope(field.position), name, makeJSString(extName)])
-  var n = ropef("var NNI$1 = {kind: 2, offset: 0, typ: null, " &
-      "name: null, len: $2, sons: [$3]};$n", [toRope(typ.id), toRope(length), s])
-  s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " &
-      "finalizer: null};$n", [name, toRope(ord(typ.kind))])
-  prepend(p.g.typeInfo, s)
-  app(p.g.typeInfo, n)
-  appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)])
-  if typ.sons[0] != nil:
-    appf(p.g.typeInfo, "$1.base = $2;$n", 
-         [name, genTypeInfo(p, typ.sons[0])])
-
-proc genTypeInfo(p: var TProc, typ: PType): PRope = 
-  var t = typ
-  if t.kind == tyGenericInst: t = lastSon(t)
-  result = ropef("NTI$1", [toRope(t.id)])
-  if ContainsOrIncl(p.g.TypeInfoGenerated, t.id): return 
-  case t.kind
-  of tyDistinct: 
-    result = genTypeInfo(p, typ.sons[0])
-  of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyFloat128: 
-    var s = ropef(
-      "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n", 
-              [result, toRope(ord(t.kind))])
-    prepend(p.g.typeInfo, s)
-  of tyVar, tyRef, tyPtr, tySequence, tyRange, tySet: 
-    var s = ropef(
-      "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n", 
-              [result, toRope(ord(t.kind))])
-    prepend(p.g.typeInfo, s)
-    appf(p.g.typeInfo, "$1.base = $2;$n", 
-         [result, genTypeInfo(p, typ.sons[0])])
-  of tyArrayConstr, tyArray: 
-    var s = ropef(
-      "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n",
-              [result, toRope(ord(t.kind))])
-    prepend(p.g.typeInfo, s)
-    appf(p.g.typeInfo, "$1.base = $2;$n", 
-         [result, genTypeInfo(p, typ.sons[1])])
-  of tyEnum: genEnumInfo(p, t, result)
-  of tyObject: genObjectInfo(p, t, result)
-  of tyTuple: genTupleInfo(p, t, result)
-  else: InternalError("genTypeInfo(" & $t.kind & ')')
-  
-proc gen(p: var TProc, n: PNode, r: var TCompRes)
-proc genStmt(p: var TProc, n: PNode, r: var TCompRes)
-proc genProc(oldProc: var TProc, prc: PSym, r: var TCompRes)
-proc genConstant(p: var TProc, c: PSym, r: var TCompRes)
-
-proc mergeExpr(a, b: PRope): PRope = 
-  if (a != nil): 
-    if b != nil: result = ropef("($1, $2)", [a, b])
-    else: result = a
-  else: 
-    result = b
+include jstypes
   
-proc mergeExpr(r: TCompRes): PRope = 
-  result = mergeExpr(r.com, r.res)
+proc gen(p: PProc, n: PNode, r: var TCompRes)
+proc genStmt(p: PProc, n: PNode)
+proc genProc(oldProc: PProc, prc: PSym): PRope
+proc genConstant(p: PProc, c: PSym)
 
-proc mergeStmt(r: TCompRes): PRope = 
-  if r.res == nil: result = r.com
-  elif r.com == nil: result = r.res
-  else: result = ropef("$1$2", [r.com, r.res])
-  
-proc useMagic(p: var TProc, name: string) =
+proc useMagic(p: PProc, name: string) =
   if name.len == 0: return
   var s = magicsys.getCompilerProc(name)
   if s != nil:
     internalAssert s.kind in {skProc, skMethod, skConverter}
     if not p.g.generatedSyms.containsOrIncl(s.id):
-      var r: TCompRes
-      genProc(p, s, r)
-      app(p.g.code, mergeStmt(r))
+      app(p.g.code, genProc(p, s))
   else:
     # we used to exclude the system module from this check, but for DLL
     # generation support this sloppyness leads to hard to detect bugs, so
@@ -314,19 +179,67 @@ proc useMagic(p: var TProc, name: string) =
     if p.prc != nil: GlobalError(p.prc.info, errSystemNeeds, name)
     else: rawMessage(errSystemNeeds, name)
 
-proc genAnd(p: var TProc, a, b: PNode, r: var TCompRes) = 
-  var x, y: TCompRes
-  gen(p, a, x)
-  gen(p, b, y)
-  r.res = ropef("($1 && $2)", [mergeExpr(x), mergeExpr(y)])
+proc isSimpleExpr(n: PNode): bool =
+  # calls all the way down --> can stay expression based
+  if n.kind in nkCallKinds+{nkBracketExpr, nkBracket, nkCurly, nkDotExpr, nkPar,
+                            nkObjConstr}:
+    for c in n:
+      if not c.isSimpleExpr: return false
+    result = true
+  elif n.isAtom:
+    result = true
+
+proc getTemp(p: PProc): PRope =
+  inc(p.unique)
+  result = ropef("Tmp$1", [toRope(p.unique)])
+  appf(p.locals, "var $1;$n" | "local $1;$n", [result])
 
-proc genOr(p: var TProc, a, b: PNode, r: var TCompRes) = 
+proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) =
+  assert r.kind == resNone
   var x, y: TCompRes
-  gen(p, a, x)
-  gen(p, b, y)
-  r.res = ropef("($1 || $2)", [mergeExpr(x), mergeExpr(y)])
-
-type 
+  if a.isSimpleExpr and b.isSimpleExpr:
+    gen(p, a, x)
+    gen(p, b, y)
+    r.kind = resExpr
+    r.res = ropef("($1 && $2)" | "($1 and $2)", [x.rdLoc, y.rdLoc])
+  else:
+    r.res = p.getTemp
+    r.kind = resVal
+    # while a and b:
+    # -->
+    # while true:
+    #   aa
+    #   if not a: tmp = false
+    #   else:
+    #     bb
+    #     tmp = b
+    # tmp
+    gen(p, a, x)
+    p.body.appf("if (!$1) $2 = false; else {" |
+                "if not $1 then $2 = false; else", x.rdLoc, r.rdLoc)
+    gen(p, b, y)
+    p.body.appf("$2 = $1; }" |
+                "$2 = $1 end", y.rdLoc, r.rdLoc)
+
+proc genOr(p: PProc, a, b: PNode, r: var TCompRes) =
+  assert r.kind == resNone
+  var x, y: TCompRes
+  if a.isSimpleExpr and b.isSimpleExpr:
+    gen(p, a, x)
+    gen(p, b, y)
+    r.kind = resExpr
+    r.res = ropef("($1 || $2)" | "($1 or $2)", [x.rdLoc, y.rdLoc])
+  else:
+    r.res = p.getTemp
+    r.kind = resVal
+    gen(p, a, x)
+    p.body.appf("if ($1) $2 = true; else {" |
+                "if $1 then $2 = true; else", x.rdLoc, r.rdLoc)
+    gen(p, b, y)
+    p.body.appf("$2 = $1; }" |
+                "$2 = $1 end", y.rdLoc, r.rdLoc)
+
+type
   TMagicFrmt = array[0..3, string]
 
 const # magic checked op; magic unchecked op; checked op; unchecked op
@@ -430,86 +343,84 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
     ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr($1)", "cstrToNimstr($1)"], 
     ["", "", "$1", "$1"]]
 
-proc binaryExpr(p: var TProc, n: PNode, r: var TCompRes, magic, frmt: string) = 
+proc binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
   var x, y: TCompRes
   useMagic(p, magic)
   gen(p, n.sons[1], x)
   gen(p, n.sons[2], y)
-  r.res = ropef(frmt, [x.res, y.res])
-  r.com = mergeExpr(x.com, y.com)
+  r.res = ropef(frmt, [x.rdLoc, y.rdLoc])
+  r.kind = resExpr
 
-proc binaryStmt(p: var TProc, n: PNode, r: var TCompRes, magic, frmt: string) = 
-  var x, y: TCompRes
-  useMagic(p, magic)
-  gen(p, n.sons[1], x)
-  gen(p, n.sons[2], y)
-  if x.com != nil: appf(r.com, "$1;$n", [x.com])
-  if y.com != nil: appf(r.com, "$1;$n", [y.com])
-  appf(r.com, frmt, [x.res, y.res])
-
-proc unaryExpr(p: var TProc, n: PNode, r: var TCompRes, magic, frmt: string) = 
+proc unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
   useMagic(p, magic)
   gen(p, n.sons[1], r)
-  r.res = ropef(frmt, [r.res])
+  r.res = ropef(frmt, [r.rdLoc])
+  r.kind = resExpr
 
-proc arith(p: var TProc, n: PNode, r: var TCompRes, op: TMagic) = 
-  var 
+proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
+  var
     x, y: TCompRes
-    i: int
-  if optOverflowCheck in p.options: i = 0
-  else: i = 1
+  let i = ord(optOverflowCheck notin p.options)
   useMagic(p, ops[op][i])
-  if sonsLen(n) > 2: 
+  if sonsLen(n) > 2:
     gen(p, n.sons[1], x)
     gen(p, n.sons[2], y)
-    r.res = ropef(ops[op][i + 2], [x.res, y.res])
-    r.com = mergeExpr(x.com, y.com)
-  else: 
+    r.res = ropef(ops[op][i + 2], [x.rdLoc, y.rdLoc])
+  else:
     gen(p, n.sons[1], r)
-    r.res = ropef(ops[op][i + 2], [r.res])
+    r.res = ropef(ops[op][i + 2], [r.rdLoc])
+  r.kind = resExpr
 
-proc genLineDir(p: var TProc, n: PNode, r: var TCompRes) = 
-  var line: int
-  line = toLinenumber(n.info)
-  if optLineDir in p.Options: 
-    appf(r.com, "// line $2 \"$1\"$n", 
+proc genLineDir(p: PProc, n: PNode) =
+  let line = toLinenumber(n.info)
+  if optLineDir in p.Options:
+    appf(p.body, "// line $2 \"$1\"$n",
          [toRope(toFilename(n.info)), toRope(line)])
-  if ({optStackTrace, optEndb} * p.Options == {optStackTrace, optEndb}) and
-      ((p.prc == nil) or not (sfPure in p.prc.flags)): 
+  if {optStackTrace, optEndb} * p.Options == {optStackTrace, optEndb} and
+      ((p.prc == nil) or sfPure notin p.prc.flags):
     useMagic(p, "endb")
-    appf(r.com, "endb($1);$n", [toRope(line)])
+    appf(p.body, "endb($1);$n", [toRope(line)])
   elif ({optLineTrace, optStackTrace} * p.Options ==
       {optLineTrace, optStackTrace}) and
       ((p.prc == nil) or not (sfPure in p.prc.flags)): 
-    appf(r.com, "F.line = $1;$n", [toRope(line)])
+    appf(p.body, "F.line = $1;$n", [toRope(line)])
   
-proc genWhileStmt(p: var TProc, n: PNode, r: var TCompRes) = 
-  var 
-    cond, stmt: TCompRes
-    length, labl: int
-  genLineDir(p, n, r)
+proc genWhileStmt(p: PProc, n: PNode) =
+  var
+    cond: TCompRes
+  internalAssert isEmptyType(n.typ)
+  genLineDir(p, n)
   inc(p.unique)
-  length = len(p.blocks)
+  var length = len(p.blocks)
   setlen(p.blocks, length + 1)
   p.blocks[length].id = - p.unique
   p.blocks[length].isLoop = true
-  labl = p.unique
+  let labl = p.unique.toRope
+  appf(p.body, "L$1: while (true) {$n" | "while true do$n", labl)
   gen(p, n.sons[0], cond)
-  genStmt(p, n.sons[1], stmt)
-  if p.blocks[length].id > 0: 
-    appf(r.com, "L$3: while ($1) {$n$2}$n", 
-         [mergeExpr(cond), mergeStmt(stmt), toRope(labl)])
-  else: 
-    appf(r.com, "while ($1) {$n$2}$n", [mergeExpr(cond), mergeStmt(stmt)])
+  appf(p.body, "if (!$1) break L$2;$n" | "if not $1 then goto ::L$2:: end;$n",
+       [cond.res, labl])
+  genStmt(p, n.sons[1])
+  appf(p.body, "}$n" | "end$n", [])
   setlen(p.blocks, length)
 
-proc genTryStmt(p: var TProc, n: PNode, r: var TCompRes) = 
+proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) =
+  if src.kind != resNone:
+    if dest.kind != resNone:
+      p.body.appf("$1 = $2;$n", dest.rdLoc, src.rdLoc)
+    else:
+      p.body.appf("$1;$n", src.rdLoc)
+    src.kind = resNone
+    src.res = nil
+
+proc genTry(p: PProc, n: PNode, r: var TCompRes) = 
   # code to generate:
   #
   #  var sp = {prev: excHandler, exc: null};
   #  excHandler = sp;
   #  try {
   #    stmts;
+  #    TMP = e
   #  } catch (e) {
   #    if (e.typ && e.typ == NTI433 || e.typ == NTI2321) {
   #      stmts;
@@ -522,116 +433,113 @@ proc genTryStmt(p: var TProc, n: PNode, r: var TCompRes) =
   #    stmts;
   #    excHandler = excHandler.prev;
   #  }
-  #
-  var 
-    i, length, blen: int
-    safePoint, orExpr, epart: PRope
-    a: TCompRes
-  genLineDir(p, n, r)
+  genLineDir(p, n)
   inc(p.unique)
-  safePoint = ropef("Tmp$1", [toRope(p.unique)])
-  appf(r.com, 
+  var safePoint = ropef("Tmp$1", [toRope(p.unique)])
+  appf(p.body, 
        "var $1 = {prev: excHandler, exc: null};$n" & "excHandler = $1;$n", 
        [safePoint])
-  if optStackTrace in p.Options: app(r.com, "framePtr = F;" & tnl)
-  app(r.com, "try {" & tnl)
-  length = sonsLen(n)
-  genStmt(p, n.sons[0], a)
-  app(r.com, mergeStmt(a))
-  i = 1
-  epart = nil
-  while (i < length) and (n.sons[i].kind == nkExceptBranch): 
-    blen = sonsLen(n.sons[i])
+  if optStackTrace in p.Options: app(p.body, "framePtr = F;" & tnl)
+  app(p.body, "try {" & tnl)
+  var length = sonsLen(n)
+  var a: TCompRes
+  gen(p, n.sons[0], a)
+  if not isEmptyType(n.typ):
+    r.kind = resVal
+    r.res = getTemp(p)
+  moveInto(p, a, r)
+  var i = 1
+  if length > 1 and n.sons[i].kind == nkExceptBranch:
+    appf(p.body, "} catch (EXC) {$n")
+  while i < length and n.sons[i].kind == nkExceptBranch: 
+    let blen = sonsLen(n.sons[i])
     if blen == 1: 
       # general except section:
-      if i > 1: app(epart, "else {" & tnl)
-      genStmt(p, n.sons[i].sons[0], a)
-      app(epart, mergeStmt(a))
-      if i > 1: app(epart, '}' & tnl)
-    else: 
-      orExpr = nil
+      if i > 1: app(p.body, "else {" & tnl)
+      gen(p, n.sons[i].sons[0], a)
+      moveInto(p, a, r)
+      if i > 1: app(p.body, '}' & tnl)
+    else:
+      var orExpr: PRope = nil
       useMagic(p, "isObj")
       for j in countup(0, blen - 2): 
-        if (n.sons[i].sons[j].kind != nkType): 
+        if n.sons[i].sons[j].kind != nkType: 
           InternalError(n.info, "genTryStmt")
         if orExpr != nil: app(orExpr, "||")
         appf(orExpr, "isObj($1.exc.m_type, $2)", 
              [safePoint, genTypeInfo(p, n.sons[i].sons[j].typ)])
-      if i > 1: app(epart, "else ")
-      appf(epart, "if ($1.exc && $2) {$n", [safePoint, orExpr])
-      genStmt(p, n.sons[i].sons[blen - 1], a)
-      appf(epart, "$1}$n", [mergeStmt(a)])
+      if i > 1: app(p.body, "else ")
+      appf(p.body, "if ($1.exc && ($2)) {$n", [safePoint, orExpr])
+      gen(p, n.sons[i].sons[blen - 1], a)
+      moveInto(p, a, r)
+      appf(p.body, "}$n")
     inc(i)
-  if epart != nil: appf(r.com, "} catch (EXC) {$n$1", [epart])
-  app(r.com, "} finally {" & tnl & "excHandler = excHandler.prev;" & tnl)
-  if (i < length) and (n.sons[i].kind == nkFinally): 
-    genStmt(p, n.sons[i].sons[0], a)
-    app(r.com, mergeStmt(a))
-  app(r.com, '}' & tnl)
-
-proc genRaiseStmt(p: var TProc, n: PNode, r: var TCompRes) = 
-  var 
-    a: TCompRes
-    typ: PType
-  genLineDir(p, n, r)
-  if n.sons[0].kind != nkEmpty: 
+  app(p.body, "} finally {" & tnl & "excHandler = excHandler.prev;" & tnl)
+  if i < length and n.sons[i].kind == nkFinally:
+    gen(p, n.sons[i].sons[0], a)
+    moveInto(p, a, r)
+  app(p.body, '}' & tnl)
+
+proc genRaiseStmt(p: PProc, n: PNode) =
+  genLineDir(p, n)
+  if n.sons[0].kind != nkEmpty:
+    var a: TCompRes
     gen(p, n.sons[0], a)
-    if a.com != nil: appf(r.com, "$1;$n", [a.com])
-    typ = skipTypes(n.sons[0].typ, abstractPtrs)
+    let typ = skipTypes(n.sons[0].typ, abstractPtrs)
     useMagic(p, "raiseException")
-    appf(r.com, "raiseException($1, $2);$n", 
-         [a.res, makeJSString(typ.sym.name.s)])
-  else: 
+    appf(p.body, "raiseException($1, $2);$n",
+         [a.rdLoc, makeJSString(typ.sym.name.s)])
+  else:
     useMagic(p, "reraiseException")
-    app(r.com, "reraiseException();" & tnl)
+    app(p.body, "reraiseException();" & tnl)
 
-proc genCaseStmt(p: var TProc, n: PNode, r: var TCompRes) = 
-  var 
+proc genCase(p: PProc, n: PNode, r: var TCompRes) = 
+  var
     cond, stmt: TCompRes
-    it, e, v: PNode
-    stringSwitch: bool
-  genLineDir(p, n, r)
+  genLineDir(p, n)
   gen(p, n.sons[0], cond)
-  if cond.com != nil: appf(r.com, "$1;$n", [cond.com])
-  stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString
+  let stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString
   if stringSwitch: 
     useMagic(p, "toJSStr")
-    appf(r.com, "switch (toJSStr($1)) {$n", [cond.res])
-  else: 
-    appf(r.com, "switch ($1) {$n", [cond.res])
+    appf(p.body, "switch (toJSStr($1)) {$n", [cond.rdLoc])
+  else:
+    appf(p.body, "switch ($1) {$n", [cond.rdLoc])
+  if not isEmptyType(n.typ):
+    r.kind = resVal
+    r.res = getTemp(p)
   for i in countup(1, sonsLen(n) - 1): 
-    it = n.sons[i]
+    let it = n.sons[i]
     case it.kind
     of nkOfBranch: 
       for j in countup(0, sonsLen(it) - 2): 
-        e = it.sons[j]
+        let e = it.sons[j]
         if e.kind == nkRange: 
-          v = copyNode(e.sons[0])
-          while (v.intVal <= e.sons[1].intVal): 
+          var v = copyNode(e.sons[0])
+          while v.intVal <= e.sons[1].intVal: 
             gen(p, v, cond)
-            if cond.com != nil: internalError(v.info, "jsgen.genCaseStmt")
-            appf(r.com, "case $1: ", [cond.res])
+            appf(p.body, "case $1: ", [cond.rdLoc])
             Inc(v.intVal)
-        else: 
-          gen(p, e, cond)
-          if cond.com != nil: internalError(e.info, "jsgen.genCaseStmt")
+        else:
           if stringSwitch: 
             case e.kind
-            of nkStrLit..nkTripleStrLit: appf(r.com, "case $1: ", 
+            of nkStrLit..nkTripleStrLit: appf(p.body, "case $1: ", 
                 [makeJSString(e.strVal)])
             else: InternalError(e.info, "jsgen.genCaseStmt: 2")
           else: 
-            appf(r.com, "case $1: ", [cond.res])
-      genStmt(p, lastSon(it), stmt)
-      appf(r.com, "$n$1break;$n", [mergeStmt(stmt)])
-    of nkElse: 
-      genStmt(p, it.sons[0], stmt)
-      appf(r.com, "default: $n$1break;$n", [mergeStmt(stmt)])
+            gen(p, e, cond)
+            appf(p.body, "case $1: ", [cond.rdLoc])
+      gen(p, lastSon(it), stmt)
+      moveInto(p, stmt, r)
+      appf(p.body, "$nbreak;$n")
+    of nkElse:
+      appf(p.body, "default: $n")
+      gen(p, it.sons[0], stmt)
+      moveInto(p, stmt, r)
+      appf(p.body, "break;$n")
     else: internalError(it.info, "jsgen.genCaseStmt")
-  appf(r.com, "}$n", [])
+  appf(p.body, "}$n")
 
-proc genStmtListExpr(p: var TProc, n: PNode, r: var TCompRes)
-proc genBlock(p: var TProc, n: PNode, r: var TCompRes) = 
+proc genBlock(p: PProc, n: PNode, r: var TCompRes) = 
   var 
     idx, labl: int
     sym: PSym
@@ -646,22 +554,18 @@ proc genBlock(p: var TProc, n: PNode, r: var TCompRes) =
   setlen(p.blocks, idx + 1)
   p.blocks[idx].id = - p.unique # negative because it isn't used yet
   labl = p.unique
-  if n.kind == nkBlockExpr: genStmtListExpr(p, n.sons[1], r)
-  else: genStmt(p, n.sons[1], r)
-  if p.blocks[idx].id > 0: 
-    # label has been used:
-    r.com = ropef("L$1: do {$n$2} while(false);$n", [toRope(labl), r.com])
+  appf(p.body, "L$1: do {$n", toRope(labl))
+  gen(p, n.sons[1], r)
+  appf(p.body, "} while(false);$n")
   setlen(p.blocks, idx)
 
-proc genBreakStmt(p: var TProc, n: PNode, r: var TCompRes) = 
-  var 
-    idx: int
-    sym: PSym
-  genLineDir(p, n, r)
+proc genBreakStmt(p: PProc, n: PNode) = 
+  var idx: int
+  genLineDir(p, n)
   if n.sons[0].kind != nkEmpty: 
     # named break?
     assert(n.sons[0].kind == nkSym)
-    sym = n.sons[0].sym
+    let sym = n.sons[0].sym
     assert(sym.loc.k == locOther)
     idx = sym.loc.a
   else:
@@ -671,73 +575,48 @@ proc genBreakStmt(p: var TProc, n: PNode, r: var TCompRes) =
     if idx < 0 or not p.blocks[idx].isLoop:
       InternalError(n.info, "no loop to break")
   p.blocks[idx].id = abs(p.blocks[idx].id) # label is used
-  appf(r.com, "break L$1;$n", [toRope(p.blocks[idx].id)])
+  appf(p.body, "break L$1;$n", [toRope(p.blocks[idx].id)])
 
-proc genAsmStmt(p: var TProc, n: PNode, r: var TCompRes) = 
-  genLineDir(p, n, r)
+proc genAsmStmt(p: PProc, n: PNode) = 
+  genLineDir(p, n)
   assert(n.kind == nkAsmStmt)
   for i in countup(0, sonsLen(n) - 1): 
     case n.sons[i].Kind
-    of nkStrLit..nkTripleStrLit: app(r.com, n.sons[i].strVal)
-    of nkSym: app(r.com, mangleName(n.sons[i].sym))
+    of nkStrLit..nkTripleStrLit: app(p.body, n.sons[i].strVal)
+    of nkSym: app(p.body, mangleName(n.sons[i].sym))
     else: InternalError(n.sons[i].info, "jsgen: genAsmStmt()")
   
-proc genIfStmt(p: var TProc, n: PNode, r: var TCompRes) = 
-  var 
-    toClose: int
-    cond, stmt: TCompRes
-    it: PNode
-  toClose = 0
+proc genIf(p: PProc, n: PNode, r: var TCompRes) = 
+  var cond, stmt: TCompRes
+  var toClose = 0
+  if not isEmptyType(n.typ):
+    r.kind = resVal
+    r.res = getTemp(p)
   for i in countup(0, sonsLen(n) - 1): 
-    it = n.sons[i]
+    let it = n.sons[i]
     if sonsLen(it) != 1: 
-      gen(p, it.sons[0], cond)
-      genStmt(p, it.sons[1], stmt)
-      if i > 0: 
-        appf(r.com, "else {$n", [])
+      if i > 0:
+        appf(p.body, "else {$n", [])
         inc(toClose)
-      if cond.com != nil: appf(r.com, "$1;$n", [cond.com])
-      appf(r.com, "if ($1) {$n$2}", [cond.res, mergeStmt(stmt)])
-    else: 
-      # else part:
-      genStmt(p, it.sons[0], stmt)
-      appf(r.com, "else {$n$1}$n", [mergeStmt(stmt)])
-  app(r.com, repeatChar(toClose, '}') & tnl)
-
-proc genIfExpr(p: var TProc, n: PNode, r: var TCompRes) = 
-  var 
-    toClose: int
-    cond, stmt: TCompRes
-    it: PNode
-  toClose = 0
-  for i in countup(0, sonsLen(n) - 1): 
-    it = n.sons[i]
-    if sonsLen(it) != 1: 
       gen(p, it.sons[0], cond)
+      appf(p.body, "if ($1) {$n", cond.rdLoc)
       gen(p, it.sons[1], stmt)
-      if i > 0: 
-        app(r.res, ": (")
-        inc(toClose)
-      r.com = mergeExpr(r.com, cond.com)
-      r.com = mergeExpr(r.com, stmt.com)
-      appf(r.res, "($1) ? ($2)", [cond.res, stmt.res])
-    else: 
+    else:
       # else part:
+      appf(p.body, "else {$n")
       gen(p, it.sons[0], stmt)
-      r.com = mergeExpr(r.com, stmt.com)
-      appf(r.res, ": ($1)", [stmt.res])
-  app(r.res, repeatChar(toClose, ')'))
+    moveInto(p, stmt, r)
+    appf(p.body, "}$n")
+  app(p.body, repeatChar(toClose, '}') & tnl)
 
-proc generateHeader(p: var TProc, typ: PType): PRope = 
-  var 
-    param: PSym
-    name: PRope
+proc generateHeader(p: PProc, typ: PType): PRope =
   result = nil
-  for i in countup(1, sonsLen(typ.n) - 1): 
+  for i in countup(1, sonsLen(typ.n) - 1):
     if result != nil: app(result, ", ")
     assert(typ.n.sons[i].kind == nkSym)
-    param = typ.n.sons[i].sym
-    name = mangleName(param)
+    var param = typ.n.sons[i].sym
+    if isCompileTimeOnly(param.typ): continue
+    var name = mangleName(param)
     app(result, name)
     if mapType(param.typ) == etyBaseIndex: 
       app(result, ", ")
@@ -754,56 +633,49 @@ proc needsNoCopy(y: PNode): bool =
   result = (y.kind in nodeKindsNeedNoCopy) or
       (skipTypes(y.typ, abstractInst).kind in {tyRef, tyPtr, tyVar})
 
-proc genAsgnAux(p: var TProc, x, y: PNode, r: var TCompRes, 
-                noCopyNeeded: bool) = 
+proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
   var a, b: TCompRes
   gen(p, x, a)
   gen(p, y, b)
   case mapType(x.typ)
-  of etyObject: 
-    if a.com != nil: appf(r.com, "$1;$n", [a.com])
-    if b.com != nil: appf(r.com, "$1;$n", [b.com])
-    if needsNoCopy(y) or noCopyNeeded: 
-      appf(r.com, "$1 = $2;$n", [a.res, b.res])
-    else: 
+  of etyObject:
+    if needsNoCopy(y) or noCopyNeeded:
+      appf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
+    else:
       useMagic(p, "NimCopy")
-      appf(r.com, "$1 = NimCopy($2, $3);$n", 
+      appf(p.body, "$1 = NimCopy($2, $3);$n",
            [a.res, b.res, genTypeInfo(p, y.typ)])
   of etyBaseIndex: 
-    if (a.kind != etyBaseIndex) or (b.kind != etyBaseIndex): 
+    if a.typ != etyBaseIndex or b.typ != etyBaseIndex: 
       internalError(x.info, "genAsgn")
-    appf(r.com, "$1 = $2; $3 = $4;$n", [a.com, b.com, a.res, b.res])
-  else: 
-    if a.com != nil: appf(r.com, "$1;$n", [a.com])
-    if b.com != nil: appf(r.com, "$1;$n", [b.com])
-    appf(r.com, "$1 = $2;$n", [a.res, b.res])
+    appf(p.body, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res])
+  else:
+    appf(p.body, "$1 = $2;$n", [a.res, b.res])
 
-proc genAsgn(p: var TProc, n: PNode, r: var TCompRes) = 
-  genLineDir(p, n, r)
-  genAsgnAux(p, n.sons[0], n.sons[1], r, false)
+proc genAsgn(p: PProc, n: PNode) = 
+  genLineDir(p, n)
+  genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=false)
 
-proc genFastAsgn(p: var TProc, n: PNode, r: var TCompRes) = 
-  genLineDir(p, n, r)
-  genAsgnAux(p, n.sons[0], n.sons[1], r, true)
+proc genFastAsgn(p: PProc, n: PNode) = 
+  genLineDir(p, n)
+  genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=true)
 
-proc genSwap(p: var TProc, n: PNode, r: var TCompRes) = 
+proc genSwap(p: PProc, n: PNode) = 
   var a, b: TCompRes
   gen(p, n.sons[1], a)
   gen(p, n.sons[2], b)
   inc(p.unique)
-  var tmp = ropef("Tmp$1", [toRope(p.unique)])
+  let tmp = ropef("Tmp$1", [toRope(p.unique)])
   case mapType(skipTypes(n.sons[1].typ, abstractVar))
   of etyBaseIndex: 
     inc(p.unique)
-    var tmp2 = ropef("Tmp$1", [toRope(p.unique)])
-    if (a.kind != etyBaseIndex) or (b.kind != etyBaseIndex): 
+    let tmp2 = ropef("Tmp$1", [toRope(p.unique)])
+    if a.typ != etyBaseIndex or b.typ != etyBaseIndex: 
       internalError(n.info, "genSwap")
-    appf(r.com, "var $1 = $2; $2 = $3; $3 = $1;$n", [tmp, a.com, b.com])
-    appf(r.com, "var $1 = $2; $2 = $3; $3 = $1", [tmp2, a.res, b.res])
-  else: 
-    if a.com != nil: appf(r.com, "$1;$n", [a.com])
-    if b.com != nil: appf(r.com, "$1;$n", [b.com])
-    appf(r.com, "var $1 = $2; $2 = $3; $3 = $1", [tmp, a.res, b.res])
+    appf(p.body, "var $1 = $2; $2 = $3; $3 = $1;$n", [tmp, a.address, b.address])
+    appf(p.body, "var $1 = $2; $2 = $3; $3 = $1", [tmp2, a.res, b.res])
+  else:
+    appf(p.body, "var $1 = $2; $2 = $3; $3 = $1", [tmp, a.res, b.res])
 
 proc getFieldPosition(f: PNode): int =
   case f.kind
@@ -811,10 +683,10 @@ proc getFieldPosition(f: PNode): int =
   of nkSym: result = f.sym.position
   else: InternalError(f.info, "genFieldPosition")
 
-proc genFieldAddr(p: var TProc, n: PNode, r: var TCompRes) = 
+proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) = 
   var a: TCompRes
-  r.kind = etyBaseIndex
-  var b = if n.kind == nkHiddenAddr: n.sons[0] else: n
+  r.typ = etyBaseIndex
+  let b = if n.kind == nkHiddenAddr: n.sons[0] else: n
   gen(p, b.sons[0], a)
   if skipTypes(b.sons[0].typ, abstractVarRange).kind == tyTuple:
     r.res = makeJSString("Field" & $getFieldPosition(b.sons[1]))
@@ -823,10 +695,12 @@ proc genFieldAddr(p: var TProc, n: PNode, r: var TCompRes) =
     var f = b.sons[1].sym
     if f.loc.r == nil: f.loc.r = mangleName(f)
     r.res = makeJSString(ropeToStr(f.loc.r))
-  r.com = mergeExpr(a)
+  InternalAssert a.typ != etyBaseIndex
+  r.address = a.res
+  r.kind = resExpr
 
-proc genFieldAccess(p: var TProc, n: PNode, r: var TCompRes) = 
-  r.kind = etyNone
+proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) = 
+  r.typ = etyNone
   gen(p, n.sons[0], r)
   if skipTypes(n.sons[0].typ, abstractVarRange).kind == tyTuple:
     r.res = ropef("$1.Field$2", [r.res, getFieldPosition(n.sons[1]).toRope])
@@ -835,34 +709,37 @@ proc genFieldAccess(p: var TProc, n: PNode, r: var TCompRes) =
     var f = n.sons[1].sym
     if f.loc.r == nil: f.loc.r = mangleName(f)
     r.res = ropef("$1.$2", [r.res, f.loc.r])
+  r.kind = resExpr
 
-proc genCheckedFieldAddr(p: var TProc, n: PNode, r: var TCompRes) = 
+proc genCheckedFieldAddr(p: PProc, n: PNode, r: var TCompRes) = 
   genFieldAddr(p, n.sons[0], r) # XXX
   
-proc genCheckedFieldAccess(p: var TProc, n: PNode, r: var TCompRes) = 
+proc genCheckedFieldAccess(p: PProc, n: PNode, r: var TCompRes) = 
   genFieldAccess(p, n.sons[0], r) # XXX
   
-proc genArrayAddr(p: var TProc, n: PNode, r: var TCompRes) = 
+proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = 
   var 
     a, b: TCompRes
     first: biggestInt
-  r.kind = etyBaseIndex
+  r.typ = etyBaseIndex
   gen(p, n.sons[0], a)
   gen(p, n.sons[1], b)
-  r.com = mergeExpr(a)
+  InternalAssert a.typ != etyBaseIndex and b.typ != etyBaseIndex
+  r.address = a.res
   var typ = skipTypes(n.sons[0].typ, abstractPtrs)
   if typ.kind in {tyArray, tyArrayConstr}: first = FirstOrd(typ.sons[0])
   else: first = 0
   if optBoundsCheck in p.options and not isConstExpr(n.sons[1]): 
     useMagic(p, "chckIndx")
-    b.res = ropef("chckIndx($1, $2, $3.length)-$2", 
-                  [b.res, toRope(first), a.res]) 
-    # XXX: BUG: a.res evaluated twice!
-  elif first != 0: 
-    b.res = ropef("($1)-$2", [b.res, toRope(first)])
-  r.res = mergeExpr(b)
-
-proc genArrayAccess(p: var TProc, n: PNode, r: var TCompRes) = 
+    r.res = ropef("chckIndx($1, $2, $3.length)-$2", 
+                  [b.res, toRope(first), a.res])
+  elif first != 0:
+    r.res = ropef("($1)-$2", [b.res, toRope(first)])
+  else:
+    r.res = b.res
+  r.kind = resExpr
+
+proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) = 
   var ty = skipTypes(n.sons[0].typ, abstractVarRange)
   if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.sons[0], abstractVarRange)
   case ty.kind
@@ -872,38 +749,40 @@ proc genArrayAccess(p: var TProc, n: PNode, r: var TCompRes) =
   of tyTuple: 
     genFieldAddr(p, n, r)
   else: InternalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')')
-  r.kind = etyNone
-  r.res = ropef("$1[$2]", [r.com, r.res])
-  r.com = nil
+  r.typ = etyNone
+  if r.res == nil: InternalError(n.info, "genArrayAccess")
+  r.res = ropef("$1[$2]", [r.address, r.res])
+  r.address = nil
+  r.kind = resExpr
 
-proc genAddr(p: var TProc, n: PNode, r: var TCompRes) = 
-  var s: PSym
+proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
   case n.sons[0].kind
-  of nkSym: 
-    s = n.sons[0].sym
+  of nkSym:
+    let s = n.sons[0].sym
     if s.loc.r == nil: InternalError(n.info, "genAddr: 3")
     case s.kind
-    of skVar, skLet, skResult: 
-      if mapType(n.typ) == etyObject: 
+    of skVar, skLet, skResult:
+      r.kind = resExpr
+      if mapType(n.typ) == etyObject:
         # make addr() a no-op:
-        r.kind = etyNone
+        r.typ = etyNone
         r.res = s.loc.r
-        r.com = nil
-      elif sfGlobal in s.flags: 
+        r.address = nil
+      elif sfGlobal in s.flags:
         # globals are always indirect accessible
-        r.kind = etyBaseIndex
-        r.com = toRope("Globals")
+        r.typ = etyBaseIndex
+        r.address = toRope("Globals")
         r.res = makeJSString(ropeToStr(s.loc.r))
-      elif sfAddrTaken in s.flags: 
-        r.kind = etyBaseIndex
-        r.com = s.loc.r
+      elif sfAddrTaken in s.flags:
+        r.typ = etyBaseIndex
+        r.address = s.loc.r
         r.res = toRope("0")
-      else: 
+      else:
         InternalError(n.info, "genAddr: 4")
     else: InternalError(n.info, "genAddr: 2")
-  of nkCheckedFieldExpr: 
+  of nkCheckedFieldExpr:
     genCheckedFieldAddr(p, n, r)
-  of nkDotExpr: 
+  of nkDotExpr:
     genFieldAddr(p, n, r)
   of nkBracketExpr:
     var ty = skipTypes(n.sons[0].typ, abstractVarRange)
@@ -917,27 +796,27 @@ proc genAddr(p: var TProc, n: PNode, r: var TCompRes) =
     else: InternalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')')
   else: InternalError(n.info, "genAddr")
   
-proc genSym(p: var TProc, n: PNode, r: var TCompRes) = 
+proc genSym(p: PProc, n: PNode, r: var TCompRes) = 
   var s = n.sym
   case s.kind
-  of skVar, skLet, skParam, skTemp, skResult: 
-    if s.loc.r == nil: 
+  of skVar, skLet, skParam, skTemp, skResult:
+    if s.loc.r == nil:
       InternalError(n.info, "symbol has no generated name: " & s.name.s)
     var k = mapType(s.typ)
-    if k == etyBaseIndex: 
-      r.kind = etyBaseIndex
-      if {sfAddrTaken, sfGlobal} * s.flags != {}: 
-        r.com = ropef("$1[0]", [s.loc.r])
+    if k == etyBaseIndex:
+      r.typ = etyBaseIndex
+      if {sfAddrTaken, sfGlobal} * s.flags != {}:
+        r.address = ropef("$1[0]", [s.loc.r])
         r.res = ropef("$1[1]", [s.loc.r])
-      else: 
-        r.com = s.loc.r
+      else:
+        r.address = s.loc.r
         r.res = con(s.loc.r, "_Idx")
-    elif (k != etyObject) and (sfAddrTaken in s.flags): 
+    elif k != etyObject and sfAddrTaken in s.flags:
       r.res = ropef("$1[0]", [s.loc.r])
-    else: 
+    else:
       r.res = s.loc.r
   of skConst:
-    genConstant(p, s, r)
+    genConstant(p, s)
     if s.loc.r == nil:
       InternalError(n.info, "symbol has no generated name: " & s.name.s)
     r.res = s.loc.r
@@ -953,64 +832,72 @@ proc genSym(p: var TProc, n: PNode, r: var TCompRes) =
     elif sfForward in s.flags:
       p.g.forwarded.add(s)
     elif not p.g.generatedSyms.containsOrIncl(s.id):
-      var r2: TCompRes
-      genProc(p, s, r2)
-      app(p.locals, mergeStmt(r2))
-      #app(r.com, mergeStmt(r2))
+      let newp = genProc(p, s)
+      var owner = p
+      while owner != nil and owner.prc != s.owner:
+        owner = owner.up
+      if owner != nil: app(owner.locals, newp)
+      else: app(p.g.code, newp)
   else:
     if s.loc.r == nil:
       InternalError(n.info, "symbol has no generated name: " & s.name.s)
     r.res = s.loc.r
+  r.kind = resVal
   
-proc genDeref(p: var TProc, n: PNode, r: var TCompRes) = 
-  var a: TCompRes
+proc genDeref(p: PProc, n: PNode, r: var TCompRes) = 
   if mapType(n.sons[0].typ) == etyObject: 
     gen(p, n.sons[0], r)
-  else: 
+  else:
+    var a: TCompRes
     gen(p, n.sons[0], a)
-    if a.kind != etyBaseIndex: InternalError(n.info, "genDeref")
-    r.res = ropef("$1[$2]", [a.com, a.res])
+    if a.typ != etyBaseIndex: InternalError(n.info, "genDeref")
+    r.res = ropef("$1[$2]", [a.address, a.res])
 
-proc genArg(p: var TProc, n: PNode, r: var TCompRes) =
+proc genArg(p: PProc, n: PNode, r: var TCompRes) =
   var a: TCompRes
   gen(p, n, a)
-  if a.kind == etyBaseIndex: 
-    app(r.res, a.com)
+  if a.typ == etyBaseIndex:
+    app(r.res, a.address)
     app(r.res, ", ")
     app(r.res, a.res)
   else:
-    app(r.res, mergeExpr(a))
+    app(r.res, a.res)
 
-proc genArgs(p: var TProc, n: PNode, r: var TCompRes) =
+proc genArgs(p: PProc, n: PNode, r: var TCompRes) =
   app(r.res, "(")
   for i in countup(1, sonsLen(n) - 1): 
+    let it = n.sons[i]
+    if it.typ.isCompileTimeOnly: continue  
     if i > 1: app(r.res, ", ")
-    genArg(p, n.sons[i], r)
+    genArg(p, it, r)
   app(r.res, ")")
+  r.kind = resExpr
 
-proc genCall(p: var TProc, n: PNode, r: var TCompRes) = 
+proc genCall(p: PProc, n: PNode, r: var TCompRes) = 
   gen(p, n.sons[0], r)
   genArgs(p, n, r)
 
-proc genInfixCall(p: var TProc, n: PNode, r: var TCompRes) =
+proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) =
   gen(p, n.sons[1], r)
-  if r.kind == etyBaseIndex:
-    if r.com == nil:
+  if r.typ == etyBaseIndex:
+    if r.address == nil:
       GlobalError(n.info, "cannot invoke with infix syntax")
-    r.res = ropef("$1[0]", [r.res, r.com])
-    r.com = nil
+    r.res = ropef("$1[$2]", [r.address, r.res])
+    r.address = nil
+    r.typ = etyNone
   app(r.res, ".")
   var op: TCompRes
   gen(p, n.sons[0], op)
-  app(r.res, mergeExpr(op))
+  app(r.res, op.res)
   
   app(r.res, "(")
   for i in countup(2, sonsLen(n) - 1):
     if i > 2: app(r.res, ", ")
     genArg(p, n.sons[i], r)
   app(r.res, ")")
+  r.kind = resExpr
 
-proc genEcho(p: var TProc, n: PNode, r: var TCompRes) =
+proc genEcho(p: PProc, n: PNode, r: var TCompRes) =
   useMagic(p, "rawEcho")
   app(r.res, "rawEcho")
   genArgs(p, n, r)
@@ -1019,8 +906,8 @@ proc putToSeq(s: string, indirect: bool): PRope =
   result = toRope(s)
   if indirect: result = ropef("[$1]", [result])
   
-proc createVar(p: var TProc, typ: PType, indirect: bool): PRope
-proc createRecordVarAux(p: var TProc, rec: PNode, c: var int): PRope = 
+proc createVar(p: PProc, typ: PType, indirect: bool): PRope
+proc createRecordVarAux(p: PProc, rec: PNode, c: var int): PRope = 
   result = nil
   case rec.kind
   of nkRecList: 
@@ -1038,7 +925,7 @@ proc createRecordVarAux(p: var TProc, rec: PNode, c: var int): PRope =
     inc(c)
   else: InternalError(rec.info, "createRecordVarAux")
   
-proc createVar(p: var TProc, typ: PType, indirect: bool): PRope = 
+proc createVar(p: PProc, typ: PType, indirect: bool): PRope = 
   var t = skipTypes(typ, abstractInst)
   case t.kind
   of tyInt..tyInt64, tyEnum, tyChar: 
@@ -1075,7 +962,7 @@ proc createVar(p: var TProc, typ: PType, indirect: bool): PRope =
   of tyObject: 
     result = toRope("{")
     var c = 0
-    if not (tfFinal in t.flags) or (t.sons[0] != nil): 
+    if tfFinal notin t.flags or t.sons[0] != nil:
       inc(c)
       appf(result, "m_type: $1", [genTypeInfo(p, t)])
     while t != nil: 
@@ -1095,38 +982,37 @@ proc isIndirect(v: PSym): bool =
   result = (sfAddrTaken in v.flags) and (mapType(v.typ) != etyObject) and
     v.kind notin {skProc, skConverter, skMethod, skIterator}
 
-proc genVarInit(p: var TProc, v: PSym, n: PNode, r: var TCompRes) = 
+proc genVarInit(p: PProc, v: PSym, n: PNode) = 
   var 
     a: TCompRes
     s: PRope
   if n.kind == nkEmpty: 
-    appf(r.com, "var $1 = $2;$n", 
+    appf(p.body, "var $1 = $2;$n", 
          [mangleName(v), createVar(p, v.typ, isIndirect(v))])
   else: 
     discard mangleName(v)
     gen(p, n, a)
     case mapType(v.typ)
     of etyObject: 
-      if a.com != nil: appf(r.com, "$1;$n", [a.com])
       if needsNoCopy(n): 
         s = a.res
       else: 
         useMagic(p, "NimCopy")
         s = ropef("NimCopy($1, $2)", [a.res, genTypeInfo(p, n.typ)])
     of etyBaseIndex: 
-      if (a.kind != etyBaseIndex): InternalError(n.info, "genVarInit")
+      if (a.typ != etyBaseIndex): InternalError(n.info, "genVarInit")
       if {sfAddrTaken, sfGlobal} * v.flags != {}: 
-        appf(r.com, "var $1 = [$2, $3];$n", [v.loc.r, a.com, a.res])
+        appf(p.body, "var $1 = [$2, $3];$n", [v.loc.r, a.address, a.res])
       else: 
-        appf(r.com, "var $1 = $2; var $1_Idx = $3;$n", [v.loc.r, a.com, a.res])
-      return 
-    else: 
-      if a.com != nil: appf(r.com, "$1;$n", [a.com])
+        appf(p.body, "var $1 = $2; var $1_Idx = $3;$n", [
+             v.loc.r, a.address, a.res])
+      return
+    else:
       s = a.res
-    if isIndirect(v): appf(r.com, "var $1 = [$2];$n", [v.loc.r, s])
-    else: appf(r.com, "var $1 = $2;$n", [v.loc.r, s])
+    if isIndirect(v): appf(p.body, "var $1 = [$2];$n", [v.loc.r, s])
+    else: appf(p.body, "var $1 = $2;$n", [v.loc.r, s])
   
-proc genVarStmt(p: var TProc, n: PNode, r: var TCompRes) = 
+proc genVarStmt(p: PProc, n: PNode) = 
   for i in countup(0, sonsLen(n) - 1): 
     var a = n.sons[i]
     if a.kind == nkCommentStmt: continue 
@@ -1134,56 +1020,43 @@ proc genVarStmt(p: var TProc, n: PNode, r: var TCompRes) =
     assert(a.sons[0].kind == nkSym)
     var v = a.sons[0].sym
     if lfNoDecl in v.loc.flags: continue 
-    genLineDir(p, a, r)
-    genVarInit(p, v, a.sons[2], r)
+    genLineDir(p, a)
+    genVarInit(p, v, a.sons[2])
 
-proc genConstant(p: var TProc, c: PSym, r: var TCompRes) =
+proc genConstant(p: PProc, c: PSym) =
   if lfNoDecl notin c.loc.flags and not p.g.generatedSyms.containsOrIncl(c.id):
-    genLineDir(p, c.ast, r)
-    genVarInit(p, c, c.ast, r)
-    app(p.g.code, r.com)
-    r.com = nil
-
-when false:
-  proc genConstStmt(p: var TProc, n: PNode, r: var TCompRes) =
-    genLineDir(p, n, r)
-    for i in countup(0, sonsLen(n) - 1):
-      if n.sons[i].kind == nkCommentStmt: continue
-      assert(n.sons[i].kind == nkConstDef)
-      var c = n.sons[i].sons[0].sym
-      if c.ast != nil and c.typ.kind in ConstantDataTypes and
-          lfNoDecl notin c.loc.flags:
-        genLineDir(p, n.sons[i], r)
-        genVarInit(p, c, c.ast, r)
-
-proc genNew(p: var TProc, n: PNode, r: var TCompRes) =
+    let oldBody = p.body
+    p.body = nil
+    genLineDir(p, c.ast)
+    genVarInit(p, c, c.ast)
+    app(p.g.code, p.body)
+    p.body = oldBody
+
+proc genNew(p: PProc, n: PNode) =
   var a: TCompRes
   gen(p, n.sons[1], a)
   var t = skipTypes(n.sons[1].typ, abstractVar).sons[0]
-  if a.com != nil: appf(r.com, "$1;$n", [a.com])
-  appf(r.com, "$1 = $2;$n", [a.res, createVar(p, t, true)])
+  appf(p.body, "$1 = $2;$n", [a.res, createVar(p, t, true)])
 
-proc genNewSeq(p: var TProc, n: PNode, r: var TCompRes) =
+proc genNewSeq(p: PProc, n: PNode) =
   var x, y: TCompRes
   gen(p, n.sons[1], x)
   gen(p, n.sons[2], y)
-  if x.com != nil: appf(r.com, "$1;$n", [x.com])
-  if y.com != nil: appf(r.com, "$1;$n", [y.com])
-  var t = skipTypes(n.sons[1].typ, abstractVar).sons[0]
-  appf(r.com, "$1 = new Array($2); for (var i=0;i<$2;++i) {$1[i]=$3;}", [
-    x.res, y.res, createVar(p, t, false)])
+  let t = skipTypes(n.sons[1].typ, abstractVar).sons[0]
+  appf(p.body, "$1 = new Array($2); for (var i=0;i<$2;++i) {$1[i]=$3;}", [
+    x.rdLoc, y.rdLoc, createVar(p, t, false)])
 
-proc genOrd(p: var TProc, n: PNode, r: var TCompRes) =
+proc genOrd(p: PProc, n: PNode, r: var TCompRes) =
   case skipTypes(n.sons[1].typ, abstractVar).kind
   of tyEnum, tyInt..tyInt64, tyChar: gen(p, n.sons[1], r)
   of tyBool: unaryExpr(p, n, r, "", "($1 ? 1:0)")
   else: InternalError(n.info, "genOrd")
   
-proc genConStrStr(p: var TProc, n: PNode, r: var TCompRes) =
+proc genConStrStr(p: PProc, n: PNode, r: var TCompRes) =
   var a: TCompRes
 
   gen(p, n.sons[1], a)
-  r.com = mergeExpr(r.com, a.com)
+  r.kind = resExpr
   if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyChar:
     r.res.app(ropef("[$1].concat(", [a.res]))
   else:
@@ -1191,21 +1064,18 @@ proc genConStrStr(p: var TProc, n: PNode, r: var TCompRes) =
 
   for i in countup(2, sonsLen(n) - 2):
     gen(p, n.sons[i], a)
-    r.com = mergeExpr(r.com, a.com)
-
     if skipTypes(n.sons[i].typ, abstractVarRange).kind == tyChar:
       r.res.app(ropef("[$1],", [a.res]))
     else:
       r.res.app(ropef("$1.slice(0,-1),", [a.res]))
 
   gen(p, n.sons[sonsLen(n) - 1], a)
-  r.com = mergeExpr(r.com, a.com)
   if skipTypes(n.sons[sonsLen(n) - 1].typ, abstractVarRange).kind == tyChar:
     r.res.app(ropef("[$1, 0])", [a.res]))
   else:
     r.res.app(ropef("$1)", [a.res]))
 
-proc genRepr(p: var TProc, n: PNode, r: var TCompRes) =
+proc genRepr(p: PProc, n: PNode, r: var TCompRes) =
   var t = skipTypes(n.sons[1].typ, abstractVarRange)
   case t.kind
   of tyInt..tyUInt64:
@@ -1213,13 +1083,14 @@ proc genRepr(p: var TProc, n: PNode, r: var TCompRes) =
   of tyEnum, tyOrdinal:
     gen(p, n.sons[1], r)
     useMagic(p, "cstrToNimstr")
+    r.kind = resExpr
     r.res = ropef("cstrToNimstr($1.node.sons[$2].name)", 
                  [genTypeInfo(p, t), r.res])
   else:
     # XXX:
     internalError(n.info, "genRepr: Not implemented")
 
-proc genOf(p: var TProc, n: PNode, r: var TCompRes) =
+proc genOf(p: PProc, n: PNode, r: var TCompRes) =
   var x: TCompRes
   let t = skipTypes(n.sons[2].typ, abstractVarRange+{tyRef, tyPtr, tyTypeDesc})
   gen(p, n.sons[1], x)
@@ -1228,17 +1099,16 @@ proc genOf(p: var TProc, n: PNode, r: var TCompRes) =
   else:
     useMagic(p, "isObj")
     r.res = ropef("isObj($1.m_type, $2)", [x.res, genTypeInfo(p, t)])
-  r.com = mergeExpr(r.com, x.com)
+  r.kind = resExpr
 
-proc genReset(p: var TProc, n: PNode, r: var TCompRes) =
+proc genReset(p: PProc, n: PNode) =
   var x: TCompRes
   useMagic(p, "genericReset")
   gen(p, n.sons[1], x)
-  r.res = ropef("$1 = genericReset($1, $2)", [x.res, 
+  appf(p.body, "$1 = genericReset($1, $2);$n", [x.res, 
                 genTypeInfo(p, n.sons[1].typ)])
-  r.com = mergeExpr(r.com, x.com)
 
-proc genMagic(p: var TProc, n: PNode, r: var TCompRes) =
+proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
   var 
     a: TCompRes
     line, filen: PRope
@@ -1248,7 +1118,7 @@ proc genMagic(p: var TProc, n: PNode, r: var TCompRes) =
   of mAnd: genAnd(p, n.sons[1], n.sons[2], r)
   of mAddi..mStrToStr: arith(p, n, r, op)
   of mRepr: genRepr(p, n, r)
-  of mSwap: genSwap(p, n, r)
+  of mSwap: genSwap(p, n)
   of mUnaryLt:
     # XXX: range checking?
     if not (optOverflowCheck in p.Options): unaryExpr(p, n, r, "", "$1 - 1")
@@ -1261,21 +1131,21 @@ proc genMagic(p: var TProc, n: PNode, r: var TCompRes) =
     # XXX: range checking?
     if not (optOverflowCheck in p.Options): binaryExpr(p, n, r, "", "$1 - $2")
     else: binaryExpr(p, n, r, "addInt", "addInt($1, $2)")
-  of mAppendStrCh: binaryStmt(p, n, r, "addChar", "$1 = addChar($1, $2)")
+  of mAppendStrCh: binaryExpr(p, n, r, "addChar", "addChar($1, $2)")
   of mAppendStrStr:
     if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString:
-        binaryStmt(p, n, r, "", "$1 += $2")
+        binaryExpr(p, n, r, "", "$1 += $2")
     else:
-      binaryStmt(p, n, r, "", "$1 = ($1.slice(0,-1)).concat($2)")
+      binaryExpr(p, n, r, "", "$1 = ($1.slice(0,-1)).concat($2)")
     # XXX: make a copy of $2, because of Javascript's sucking semantics
-  of mAppendSeqElem: binaryStmt(p, n, r, "", "$1.push($2)")
+  of mAppendSeqElem: binaryExpr(p, n, r, "", "$1.push($2)")
   of mConStrStr: genConStrStr(p, n, r)
   of mEqStr: binaryExpr(p, n, r, "eqStrings", "eqStrings($1, $2)")
   of mLeStr: binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) <= 0)")
   of mLtStr: binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)")
   of mIsNil: unaryExpr(p, n, r, "", "$1 == null")
   of mEnumToStr: genRepr(p, n, r)
-  of mNew, mNewFinalize: genNew(p, n, r)
+  of mNew, mNewFinalize: genNew(p, n)
   of mSizeOf: r.res = toRope(getSize(n.sons[1].typ))
   of mChr, mArrToSeq: gen(p, n.sons[1], r)      # nothing to do
   of mOrd: genOrd(p, n, r)
@@ -1288,13 +1158,13 @@ proc genMagic(p: var TProc, n: PNode, r: var TCompRes) =
     else:
       unaryExpr(p, n, r, "", "($1.length-1)")
   of mInc:
-    if not (optOverflowCheck in p.Options): binaryStmt(p, n, r, "", "$1 += $2")
-    else: binaryStmt(p, n, r, "addInt", "$1 = addInt($1, $2)")
+    if not (optOverflowCheck in p.Options): binaryExpr(p, n, r, "", "$1 += $2")
+    else: binaryExpr(p, n, r, "addInt", "$1 = addInt($1, $2)")
   of ast.mDec:
-    if not (optOverflowCheck in p.Options): binaryStmt(p, n, r, "", "$1 -= $2")
-    else: binaryStmt(p, n, r, "subInt", "$1 = subInt($1, $2)")
-  of mSetLengthStr: binaryStmt(p, n, r, "", "$1.length = ($2)-1")
-  of mSetLengthSeq: binaryStmt(p, n, r, "", "$1.length = $2")
+    if not (optOverflowCheck in p.Options): binaryExpr(p, n, r, "", "$1 -= $2")
+    else: binaryExpr(p, n, r, "subInt", "$1 = subInt($1, $2)")
+  of mSetLengthStr: binaryExpr(p, n, r, "", "$1.length = ($2)-1")
+  of mSetLengthSeq: binaryExpr(p, n, r, "", "$1.length = $2")
   of mCard: unaryExpr(p, n, r, "SetCard", "SetCard($1)")
   of mLtSet: binaryExpr(p, n, r, "SetLt", "SetLt($1, $2)")
   of mLeSet: binaryExpr(p, n, r, "SetLe", "SetLe($1, $2)")
@@ -1302,14 +1172,14 @@ proc genMagic(p: var TProc, n: PNode, r: var TCompRes) =
   of mMulSet: binaryExpr(p, n, r, "SetMul", "SetMul($1, $2)")
   of mPlusSet: binaryExpr(p, n, r, "SetPlus", "SetPlus($1, $2)")
   of mMinusSet: binaryExpr(p, n, r, "SetMinus", "SetMinus($1, $2)")
-  of mIncl: binaryStmt(p, n, r, "", "$1[$2] = true")
-  of mExcl: binaryStmt(p, n, r, "", "delete $1[$2]")
+  of mIncl: binaryExpr(p, n, r, "", "$1[$2] = true")
+  of mExcl: binaryExpr(p, n, r, "", "delete $1[$2]")
   of mInSet: binaryExpr(p, n, r, "", "($1[$2] != undefined)")
   of mNLen..mNError:
     localError(n.info, errCannotGenerateCodeForX, n.sons[0].sym.name.s)
-  of mNewSeq: genNewSeq(p, n, r)
+  of mNewSeq: genNewSeq(p, n)
   of mOf: genOf(p, n, r)
-  of mReset: genReset(p, n, r)
+  of mReset: genReset(p, n)
   of mEcho: genEcho(p, n, r)
   of mSlurp, mStaticExec:
     localError(n.info, errXMustBeCompileTime, n.sons[0].sym.name.s)
@@ -1317,94 +1187,95 @@ proc genMagic(p: var TProc, n: PNode, r: var TCompRes) =
     genCall(p, n, r)
     #else internalError(e.info, 'genMagic: ' + magicToStr[op]);
   
-proc genSetConstr(p: var TProc, n: PNode, r: var TCompRes) = 
-  var 
+proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) = 
+  var
     a, b: TCompRes
   useMagic(p, "SetConstr")
   r.res = toRope("SetConstr(")
+  r.kind = resExpr
   for i in countup(0, sonsLen(n) - 1): 
     if i > 0: app(r.res, ", ")
     var it = n.sons[i]
     if it.kind == nkRange: 
       gen(p, it.sons[0], a)
       gen(p, it.sons[1], b)
-      r.com = mergeExpr(r.com, mergeExpr(a.com, b.com))
       appf(r.res, "[$1, $2]", [a.res, b.res])
     else: 
       gen(p, it, a)
-      r.com = mergeExpr(r.com, a.com)
       app(r.res, a.res)
   app(r.res, ")")
 
-proc genArrayConstr(p: var TProc, n: PNode, r: var TCompRes) = 
+proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) = 
   var a: TCompRes
   r.res = toRope("[")
+  r.kind = resExpr
   for i in countup(0, sonsLen(n) - 1): 
     if i > 0: app(r.res, ", ")
     gen(p, n.sons[i], a)
-    r.com = mergeExpr(r.com, a.com)
     app(r.res, a.res)
   app(r.res, "]")
 
-proc genTupleConstr(p: var TProc, n: PNode, r: var TCompRes) = 
+proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) = 
   var a: TCompRes
   r.res = toRope("{")
+  r.kind = resExpr
   for i in countup(0, sonsLen(n) - 1):
     if i > 0: app(r.res, ", ")
     var it = n.sons[i]
     if it.kind == nkExprColonExpr: it = it.sons[1]
     gen(p, it, a)
-    r.com = mergeExpr(r.com, a.com)
     appf(r.res, "Field$1: $2", [i.toRope, a.res])
   r.res.app("}")
 
-proc genObjConstr(p: var TProc, n: PNode, r: var TCompRes) =
+proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
   # XXX inheritance?
   var a: TCompRes
   r.res = toRope("{")
+  r.kind = resExpr
   for i in countup(0, sonsLen(n) - 1):
     if i > 0: app(r.res, ", ")
     var it = n.sons[i]
     InternalAssert it.kind == nkExprColonExpr
     gen(p, it.sons[1], a)
-    r.com = mergeExpr(r.com, a.com)
     var f = it.sons[0].sym
     if f.loc.r == nil: f.loc.r = mangleName(f)
     appf(r.res, "$1: $2", [f.loc.r, a.res])
   r.res.app("}")
 
-proc genConv(p: var TProc, n: PNode, r: var TCompRes) = 
+proc genConv(p: PProc, n: PNode, r: var TCompRes) = 
   var dest = skipTypes(n.typ, abstractVarRange)
   var src = skipTypes(n.sons[1].typ, abstractVarRange)
   gen(p, n.sons[1], r)
   if (dest.kind != src.kind) and (src.kind == tyBool): 
     r.res = ropef("(($1)? 1:0)", [r.res])
+    r.kind = resExpr
   
-proc upConv(p: var TProc, n: PNode, r: var TCompRes) = 
+proc upConv(p: PProc, n: PNode, r: var TCompRes) = 
   gen(p, n.sons[0], r)        # XXX
   
-proc genRangeChck(p: var TProc, n: PNode, r: var TCompRes, magic: string) = 
+proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) = 
   var a, b: TCompRes
   gen(p, n.sons[0], r)
   if optRangeCheck in p.options: 
     gen(p, n.sons[1], a)
     gen(p, n.sons[2], b)
-    r.com = mergeExpr(r.com, mergeExpr(a.com, b.com))
     useMagic(p, "chckRange")
     r.res = ropef("chckRange($1, $2, $3)", [r.res, a.res, b.res])
+    r.kind = resExpr
 
-proc convStrToCStr(p: var TProc, n: PNode, r: var TCompRes) = 
+proc convStrToCStr(p: PProc, n: PNode, r: var TCompRes) = 
   # we do an optimization here as this is likely to slow down
   # much of the code otherwise:
   if n.sons[0].kind == nkCStringToString: 
     gen(p, n.sons[0].sons[0], r)
-  else: 
+  else:
     gen(p, n.sons[0], r)
     if r.res == nil: InternalError(n.info, "convStrToCStr")
     useMagic(p, "toJSStr")
     r.res = ropef("toJSStr($1)", [r.res])
+    r.kind = resExpr
 
-proc convCStrToStr(p: var TProc, n: PNode, r: var TCompRes) = 
+proc convCStrToStr(p: PProc, n: PNode, r: var TCompRes) = 
   # we do an optimization here as this is likely to slow down
   # much of the code otherwise:
   if n.sons[0].kind == nkStringToCString: 
@@ -1414,44 +1285,44 @@ proc convCStrToStr(p: var TProc, n: PNode, r: var TCompRes) =
     if r.res == nil: InternalError(n.info, "convCStrToStr")
     useMagic(p, "cstrToNimstr")
     r.res = ropef("cstrToNimstr($1)", [r.res])
+    r.kind = resExpr
 
-proc genReturnStmt(p: var TProc, n: PNode, r: var TCompRes) = 
-  var a: TCompRes
+proc genReturnStmt(p: PProc, n: PNode) = 
   if p.procDef == nil: InternalError(n.info, "genReturnStmt")
   p.BeforeRetNeeded = true
   if (n.sons[0].kind != nkEmpty): 
-    genStmt(p, n.sons[0], a)
-    if a.com != nil: appf(r.com, "$1;$n", mergeStmt(a))
-  else: 
-    genLineDir(p, n, r)
-  app(r.com, "break BeforeRet;" & tnl)
+    genStmt(p, n.sons[0])
+  else:
+    genLineDir(p, n)
+  app(p.body, "break BeforeRet;" & tnl)
 
-proc genProcBody(p: var TProc, prc: PSym, r: TCompRes): PRope = 
-  if optStackTrace in prc.options: 
+proc genProcBody(p: PProc, prc: PSym): PRope = 
+  if optStackTrace in prc.options:
     result = ropef("var F={procname:$1,prev:framePtr,filename:$2,line:0};$n" &
         "framePtr = F;$n", [makeJSString(prc.owner.name.s & '.' & prc.name.s), 
                             makeJSString(toFilename(prc.info))])
-  else: 
+  else:
     result = nil
-  if p.beforeRetNeeded: 
-    appf(result, "BeforeRet: do {$n$1} while (false); $n", [mergeStmt(r)])
+  if p.beforeRetNeeded:
+    appf(result, "BeforeRet: do {$n$1} while (false); $n", [p.body])
   else: 
-    app(result, mergeStmt(r))
+    app(result, p.body)
   if prc.typ.callConv == ccSysCall: 
     result = ropef("try {$n$1} catch (e) {$n" &
         " alert(\"Unhandled exception:\\n\" + e.message + \"\\n\"$n}", [result])
   if optStackTrace in prc.options: 
     app(result, "framePtr = framePtr.prev;" & tnl)
 
-proc genProc(oldProc: var TProc, prc: PSym, r: var TCompRes) = 
-  var 
-    p: TProc
+proc genProc(oldProc: PProc, prc: PSym): PRope = 
+  var
     resultSym: PSym
     name, returnStmt, resultAsgn, header: PRope
     a: TCompRes
   #if gVerbosity >= 3: 
   #  echo "BEGIN generating code for: " & prc.name.s
-  initProc(p, oldProc.g, oldProc.module, prc.ast, prc.options)
+  var p = newProc(oldProc.g, oldProc.module, prc.ast, prc.options)
+  p.target = oldProc.target
+  p.up = oldProc
   returnStmt = nil
   resultAsgn = nil
   name = mangleName(prc)
@@ -1461,112 +1332,58 @@ proc genProc(oldProc: var TProc, prc: PSym, r: var TCompRes) =
     resultAsgn = ropef("var $# = $#;$n", [mangleName(resultSym), 
         createVar(p, resultSym.typ, isIndirect(resultSym))])
     gen(p, prc.ast.sons[resultPos], a)
-    if a.com != nil: appf(returnStmt, "$1;$n", [a.com])
     returnStmt = ropef("return $#;$n", [a.res])
-  genStmt(p, prc.getBody, r)
-  r.com = ropef("function $#($#) {$n$#$#$#$#}$n",
+  genStmt(p, prc.getBody)
+  result = ropef("function $#($#) {$n$#$#$#$#}$n",
                 [name, header, p.locals, resultAsgn, 
-                 genProcBody(p, prc, r), returnStmt])
-  r.res = nil  
+                 genProcBody(p, prc), returnStmt])
   #if gVerbosity >= 3:
   #  echo "END   generated code for: " & prc.name.s
-  
-proc genStmtListExpr(p: var TProc, n: PNode, r: var TCompRes) = 
-  var a: TCompRes
-  # watch out this trick: ``function () { stmtList; return expr; }()``
-  r.res = toRope("function () {")
-  for i in countup(0, sonsLen(n) - 2): 
-    genStmt(p, n.sons[i], a)
-    app(r.res, mergeStmt(a))
-  gen(p, lastSon(n), a)
-  if a.com != nil: appf(r.res, "$1;$n", [a.com])
-  appf(r.res, "return $1; }()", [a.res])
-
-proc genStmt(p: var TProc, n: PNode, r: var TCompRes) = 
-  var a: TCompRes
-  r.kind = etyNone
-  r.com = nil
-  r.res = nil
-  case n.kind
-  of nkNilLit, nkEmpty: nil
-  of nkStmtList: 
-    for i in countup(0, sonsLen(n) - 1): 
-      genStmt(p, n.sons[i], a)
-      app(r.com, mergeStmt(a))
-  of nkBlockStmt: genBlock(p, n, r)
-  of nkIfStmt: genIfStmt(p, n, r)
-  of nkWhileStmt: genWhileStmt(p, n, r)
-  of nkVarSection, nkLetSection: genVarStmt(p, n, r)
-  of nkConstSection: nil
-  of nkForStmt, nkParForStmt: 
-    internalError(n.info, "for statement not eliminated")
-  of nkCaseStmt: genCaseStmt(p, n, r)
-  of nkReturnStmt: genReturnStmt(p, n, r)
-  of nkBreakStmt: genBreakStmt(p, n, r)
-  of nkAsgn: genAsgn(p, n, r)
-  of nkFastAsgn: genFastAsgn(p, n, r)
-  of nkDiscardStmt: 
-    if n.sons[0].kind != nkEmpty:
-      genLineDir(p, n, r)
-      gen(p, n.sons[0], r)
-      app(r.res, ';' & tnl)
-  of nkAsmStmt: genAsmStmt(p, n, r)
-  of nkTryStmt: genTryStmt(p, n, r)
-  of nkRaiseStmt: genRaiseStmt(p, n, r)
-  of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt, 
-     nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, 
-     nkFromStmt, nkTemplateDef, nkMacroDef, nkPragma: nil
-  of nkProcDef, nkMethodDef, nkConverterDef:
-    var s = n.sons[namePos].sym
-    if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}:
-      #var r2: TCompRes
-      genSym(p, n.sons[namePos], r)
-      r.res = nil
-  of nkGotoState, nkState:
-    internalError(n.info, "first class iterators not implemented")
-  else:
-    genLineDir(p, n, r)
-    gen(p, n, r)
-    app(r.res, ';' & tnl)
-
-proc gen(p: var TProc, n: PNode, r: var TCompRes) = 
-  var f: BiggestFloat
-  r.kind = etyNone
-  r.com = nil
+
+proc genStmt(p: PProc, n: PNode) =
+  var r: TCompRes
+  gen(p, n, r)
+  if r.res != nil: appf(p.body, "$#;$n", r.res)
+
+proc gen(p: PProc, n: PNode, r: var TCompRes) =
+  r.typ = etyNone
+  r.kind = resNone
+  #r.address = nil
   r.res = nil
   case n.kind
-  of nkSym: 
+  of nkSym:
     genSym(p, n, r)
-  of nkCharLit..nkInt64Lit: 
+  of nkCharLit..nkInt64Lit:
     r.res = toRope(n.intVal)
-  of nkNilLit: 
-    if mapType(n.typ) == etyBaseIndex: 
-      r.kind = etyBaseIndex
-      r.com = toRope"null"
+  of nkNilLit:
+    if isEmptyType(n.typ):
+      nil
+    elif mapType(n.typ) == etyBaseIndex:
+      r.typ = etyBaseIndex
+      r.address = toRope"null"
       r.res = toRope"0"
-    else: 
+    else:
       r.res = toRope"null"
-  of nkStrLit..nkTripleStrLit: 
+  of nkStrLit..nkTripleStrLit:
     if skipTypes(n.typ, abstractVarRange).kind == tyString: 
       useMagic(p, "cstrToNimstr")
       r.res = ropef("cstrToNimstr($1)", [makeJSString(n.strVal)])
     else: 
       r.res = makeJSString(n.strVal)
+    r.kind = resExpr
   of nkFloatLit..nkFloat64Lit: 
-    f = n.floatVal
+    let f = n.floatVal
     if f != f: r.res = toRope"NaN"
     elif f == 0.0: r.res = toRope"0.0"
     elif f == 0.5 * f: 
       if f > 0.0: r.res = toRope"Infinity"
       else: r.res = toRope"-Infinity"
     else: r.res = toRope(f.ToStrMaxPrecision)
-  of nkBlockExpr: genBlock(p, n, r)
-  of nkIfExpr: genIfExpr(p, n, r)
   of nkCallKinds:
     if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic != mNone): 
       genMagic(p, n, r)
     elif n.sons[0].kind == nkSym and sfInfixCall in n.sons[0].sym.flags and
-      n.len >= 2:
+        n.len >= 2:
       genInfixCall(p, n, r)
     else: 
       genCall(p, n, r)
@@ -1588,7 +1405,6 @@ proc gen(p: var TProc, n: PNode, r: var TCompRes) =
   of nkChckRange: genRangeChck(p, n, r, "chckRange")
   of nkStringToCString: convStrToCStr(p, n, r)
   of nkCStringToString: convCStrToStr(p, n, r)
-  of nkStmtListExpr: genStmtListExpr(p, n, r)
   of nkEmpty: nil
   of nkLambdaKinds: 
     let s = n.sons[namePos].sym
@@ -1596,11 +1412,46 @@ proc gen(p: var TProc, n: PNode, r: var TCompRes) =
     r.res = s.loc.r
     if lfNoDecl in s.loc.flags or s.magic != mNone or isGenericRoutine(s): nil
     elif not p.g.generatedSyms.containsOrIncl(s.id):
-      var r2: TCompRes
-      genProc(p, s, r2)
-      app(r.com, mergeStmt(r2))
+      app(p.locals, genProc(p, s))
   of nkMetaNode: gen(p, n.sons[0], r)
   of nkType: r.res = genTypeInfo(p, n.typ)
+  of nkStmtList, nkStmtListExpr:
+    # this shows the distinction is nice for backends and should be kept
+    # in the frontend
+    let isExpr = not isEmptyType(n.typ)
+    for i in countup(0, sonsLen(n) - 1 - isExpr.ord):
+      genStmt(p, n.sons[i])
+    if isExpr:
+      gen(p, lastSon(n), r)
+  of nkBlockStmt, nkBlockExpr: genBlock(p, n, r)
+  of nkIfStmt, nkIfExpr: genIf(p, n, r)
+  of nkWhileStmt: genWhileStmt(p, n)
+  of nkVarSection, nkLetSection: genVarStmt(p, n)
+  of nkConstSection: nil
+  of nkForStmt, nkParForStmt: 
+    internalError(n.info, "for statement not eliminated")
+  of nkCaseStmt: genCase(p, n, r)
+  of nkReturnStmt: genReturnStmt(p, n)
+  of nkBreakStmt: genBreakStmt(p, n)
+  of nkAsgn: genAsgn(p, n)
+  of nkFastAsgn: genFastAsgn(p, n)
+  of nkDiscardStmt:
+    if n.sons[0].kind != nkEmpty:
+      genLineDir(p, n)
+      gen(p, n.sons[0], r)
+  of nkAsmStmt: genAsmStmt(p, n)
+  of nkTryStmt: genTry(p, n, r)
+  of nkRaiseStmt: genRaiseStmt(p, n)
+  of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt, 
+     nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, 
+     nkFromStmt, nkTemplateDef, nkMacroDef, nkPragma: nil
+  of nkProcDef, nkMethodDef, nkConverterDef:
+    var s = n.sons[namePos].sym
+    if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}:
+      genSym(p, n.sons[namePos], r)
+      r.res = nil
+  of nkGotoState, nkState:
+    internalError(n.info, "first class iterators not implemented")
   else: InternalError(n.info, "gen: unknown node type: " & $n.kind)
   
 var globals: PGlobals
@@ -1616,26 +1467,25 @@ proc genHeader(): PRope =
       "var framePtr = null;$n" & "var excHandler = null;$n", 
                  [toRope(versionAsString)])
 
-proc genModule(p: var TProc, n: PNode, r: var TCompRes) = 
-  genStmt(p, n, r)
-  if optStackTrace in p.options: 
-    r.com = ropef("var F = {procname:$1,prev:framePtr,filename:$2,line:0};$n" &
-        "framePtr = F;$n" & "$3" & "framePtr = framePtr.prev;$n", [
+proc genModule(p: PProc, n: PNode) = 
+  if optStackTrace in p.options:
+    appf(p.body, "var F = {procname:$1,prev:framePtr,filename:$2,line:0};$n" &
+                 "framePtr = F;$n", [
         makeJSString("module " & p.module.module.name.s), 
-        makeJSString(toFilename(p.module.module.info)), r.com])
+        makeJSString(toFilename(p.module.module.info))])
+  genStmt(p, n)
+  if optStackTrace in p.options:
+    appf(p.body, "framePtr = framePtr.prev;$n")
 
 proc myProcess(b: PPassContext, n: PNode): PNode = 
   if passes.skipCodegen(n): return n
-  var 
-    p: TProc
-    r: TCompRes
   result = n
   var m = BModule(b)
   if m.module == nil: InternalError(n.info, "myProcess")
-  initProc(p, globals, m, nil, m.module.options)
-  genModule(p, n, r)
+  var p = newProc(globals, m, nil, m.module.options)
+  genModule(p, n)
   app(p.g.code, p.locals)
-  app(p.g.code, mergeStmt(r))
+  app(p.g.code, p.body)
 
 proc myClose(b: PPassContext, n: PNode): PNode = 
   if passes.skipCodegen(n): return n
@@ -1644,23 +1494,15 @@ proc myClose(b: PPassContext, n: PNode): PNode =
   if sfMainModule in m.module.flags:
     for prc in globals.forwarded:
       if not globals.generatedSyms.containsOrIncl(prc.id):
-        var 
-          p: TProc
-          r: TCompRes
-        initProc(p, globals, m, nil, m.module.options)
-        genProc(p, prc, r)
-        app(p.g.code, mergeStmt(r))
+        var p = newProc(globals, m, nil, m.module.options)
+        app(p.g.code, genProc(p, prc))
     
     var disp = generateMethodDispatchers()
     for i in 0..sonsLen(disp)-1: 
       let prc = disp.sons[i].sym
       if not globals.generatedSyms.containsOrIncl(prc.id):
-        var 
-          p: TProc
-          r: TCompRes
-        initProc(p, globals, m, nil, m.module.options)
-        genProc(p, prc, r)
-        app(p.g.code, mergeStmt(r))
+        var p = newProc(globals, m, nil, m.module.options)
+        app(p.g.code, genProc(p, prc))
 
     # write the file:
     var code = con(globals.typeInfo, globals.code)
diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim
new file mode 100644
index 000000000..0be1e99dc
--- /dev/null
+++ b/compiler/jstypes.nim
@@ -0,0 +1,148 @@
+#
+#
+#           The Nimrod Compiler
+#        (c) Copyright 2013 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Type info generation for the JS backend.
+
+proc genTypeInfo(p: PProc, typ: PType): PRope
+proc genObjectFields(p: PProc, typ: PType, n: PNode): PRope = 
+  var 
+    s, u: PRope
+    length: int
+    field: PSym
+    b: PNode
+  result = nil
+  case n.kind
+  of nkRecList: 
+    length = sonsLen(n)
+    if length == 1: 
+      result = genObjectFields(p, typ, n.sons[0])
+    else: 
+      s = nil
+      for i in countup(0, length - 1): 
+        if i > 0: app(s, ", " & tnl)
+        app(s, genObjectFields(p, typ, n.sons[i]))
+      result = ropef("{kind: 2, len: $1, offset: 0, " &
+          "typ: null, name: null, sons: [$2]}", [toRope(length), s])
+  of nkSym: 
+    field = n.sym
+    s = genTypeInfo(p, field.typ)
+    result = ropef("{kind: 1, offset: \"$1\", len: 0, " &
+        "typ: $2, name: $3, sons: null}", 
+                   [mangleName(field), s, makeJSString(field.name.s)])
+  of nkRecCase: 
+    length = sonsLen(n)
+    if (n.sons[0].kind != nkSym): InternalError(n.info, "genObjectFields")
+    field = n.sons[0].sym
+    s = genTypeInfo(p, field.typ)
+    for i in countup(1, length - 1): 
+      b = n.sons[i]           # branch
+      u = nil
+      case b.kind
+      of nkOfBranch: 
+        if sonsLen(b) < 2: 
+          internalError(b.info, "genObjectFields; nkOfBranch broken")
+        for j in countup(0, sonsLen(b) - 2): 
+          if u != nil: app(u, ", ")
+          if b.sons[j].kind == nkRange: 
+            appf(u, "[$1, $2]", [toRope(getOrdValue(b.sons[j].sons[0])), 
+                                 toRope(getOrdValue(b.sons[j].sons[1]))])
+          else: 
+            app(u, toRope(getOrdValue(b.sons[j])))
+      of nkElse: 
+        u = toRope(lengthOrd(field.typ))
+      else: internalError(n.info, "genObjectFields(nkRecCase)")
+      if result != nil: app(result, ", " & tnl)
+      appf(result, "[SetConstr($1), $2]", 
+           [u, genObjectFields(p, typ, lastSon(b))])
+    result = ropef("{kind: 3, offset: \"$1\", len: $3, " &
+        "typ: $2, name: $4, sons: [$5]}", [mangleName(field), s, 
+        toRope(lengthOrd(field.typ)), makeJSString(field.name.s), result])
+  else: internalError(n.info, "genObjectFields")
+  
+proc genObjectInfo(p: PProc, typ: PType, name: PRope) = 
+  var s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " &
+                "finalizer: null};$n", [name, toRope(ord(typ.kind))])
+  prepend(p.g.typeInfo, s)
+  appf(p.g.typeInfo, "var NNI$1 = $2;$n", 
+       [toRope(typ.id), genObjectFields(p, typ, typ.n)])
+  appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)])
+  if (typ.kind == tyObject) and (typ.sons[0] != nil): 
+    appf(p.g.typeInfo, "$1.base = $2;$n", 
+         [name, genTypeInfo(p, typ.sons[0])])
+
+proc genTupleFields(p: PProc, typ: PType): PRope =
+  var s: PRope = nil
+  for i in 0 .. <typ.len:
+    if i > 0: app(s, ", " & tnl)
+    s.appf("{kind: 1, offset: \"Field$1\", len: 0, " &
+           "typ: $2, name: \"Field$1\", sons: null}",
+           [i.toRope, genTypeInfo(p, typ.sons[i])])
+  result = ropef("{kind: 2, len: $1, offset: 0, " &
+                 "typ: null, name: null, sons: [$2]}", [toRope(typ.len), s])
+
+proc genTupleInfo(p: PProc, typ: PType, name: PRope) = 
+  var s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " &
+                "finalizer: null};$n", [name, toRope(ord(typ.kind))])
+  prepend(p.g.typeInfo, s)
+  appf(p.g.typeInfo, "var NNI$1 = $2;$n", 
+       [toRope(typ.id), genTupleFields(p, typ)])
+  appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)])
+
+proc genEnumInfo(p: PProc, typ: PType, name: PRope) =
+  let length = sonsLen(typ.n)
+  var s: PRope = nil
+  for i in countup(0, length - 1): 
+    if (typ.n.sons[i].kind != nkSym): InternalError(typ.n.info, "genEnumInfo")
+    let field = typ.n.sons[i].sym
+    if i > 0: app(s, ", " & tnl)
+    let extName = if field.ast == nil: field.name.s else: field.ast.strVal
+    appf(s, "{kind: 1, offset: $1, typ: $2, name: $3, len: 0, sons: null}", 
+         [toRope(field.position), name, makeJSString(extName)])
+  var n = ropef("var NNI$1 = {kind: 2, offset: 0, typ: null, " &
+      "name: null, len: $2, sons: [$3]};$n", [toRope(typ.id), toRope(length), s])
+  s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " &
+      "finalizer: null};$n", [name, toRope(ord(typ.kind))])
+  prepend(p.g.typeInfo, s)
+  app(p.g.typeInfo, n)
+  appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)])
+  if typ.sons[0] != nil:
+    appf(p.g.typeInfo, "$1.base = $2;$n", 
+         [name, genTypeInfo(p, typ.sons[0])])
+
+proc genTypeInfo(p: PProc, typ: PType): PRope = 
+  var t = typ
+  if t.kind == tyGenericInst: t = lastSon(t)
+  result = ropef("NTI$1", [toRope(t.id)])
+  if ContainsOrIncl(p.g.TypeInfoGenerated, t.id): return 
+  case t.kind
+  of tyDistinct: 
+    result = genTypeInfo(p, typ.sons[0])
+  of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyFloat128: 
+    var s = ropef(
+      "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n", 
+              [result, toRope(ord(t.kind))])
+    prepend(p.g.typeInfo, s)
+  of tyVar, tyRef, tyPtr, tySequence, tyRange, tySet: 
+    var s = ropef(
+      "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n", 
+              [result, toRope(ord(t.kind))])
+    prepend(p.g.typeInfo, s)
+    appf(p.g.typeInfo, "$1.base = $2;$n", 
+         [result, genTypeInfo(p, typ.sons[0])])
+  of tyArrayConstr, tyArray: 
+    var s = ropef(
+      "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n",
+              [result, toRope(ord(t.kind))])
+    prepend(p.g.typeInfo, s)
+    appf(p.g.typeInfo, "$1.base = $2;$n", 
+         [result, genTypeInfo(p, typ.sons[1])])
+  of tyEnum: genEnumInfo(p, t, result)
+  of tyObject: genObjectInfo(p, t, result)
+  of tyTuple: genTupleInfo(p, t, result)
+  else: InternalError("genTypeInfo(" & $t.kind & ')')
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index ebcd67046..8d4946ab5 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -104,8 +104,6 @@ discard """
 """
 
 const
-  declarativeDefs* = {nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef}
-  procDefs* = nkLambdaKinds + declarativeDefs
   upName* = ":up" # field name for the 'up' reference
   paramName* = ":env"
   envName* = ":env"
diff --git a/compiler/lexer.nim b/compiler/lexer.nim
index bf9bf5343..6660ff65c 100644
--- a/compiler/lexer.nim
+++ b/compiler/lexer.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nimrod Compiler
-#        (c) Copyright 2012 Andreas Rumpf
+#        (c) Copyright 2013 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -58,8 +58,7 @@ type
     tkParDotLe, tkParDotRi,   # (. and .)
     tkComma, tkSemiColon,
     tkColon, tkColonColon, tkEquals, tkDot, tkDotDot,
-    tkOpr, tkComment, tkAccent, tkInd, tkSad, 
-    tkDed, # pseudo token types used by the source renderers:
+    tkOpr, tkComment, tkAccent,
     tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr,
     
   TTokTypes* = set[TTokType]
@@ -91,8 +90,8 @@ const
     ")", "[", "]", "{", "}", "[.", ".]", "{.", ".}", "(.", ".)",
     ",", ";",
     ":", "::", "=", ".", "..",
-    "tkOpr", "tkComment", "`", "[new indentation]", 
-    "[same indentation]", "[dedentation]", "tkSpaces", "tkInfixOpr", 
+    "tkOpr", "tkComment", "`",
+    "tkSpaces", "tkInfixOpr",
     "tkPrefixOpr", "tkPostfixOpr"]
 
 type 
@@ -102,7 +101,8 @@ type
     base2, base8, base16
   TToken* = object            # a Nimrod token
     tokType*: TTokType        # the type of the token
-    indent*: int              # the indentation; only valid if tokType = tkIndent
+    indent*: int              # the indentation; != -1 if the token has been
+                              # preceeded with indentation
     ident*: PIdent            # the parsed identifier
     iNumber*: BiggestInt      # the parsed integer literal
     fNumber*: BiggestFloat    # the parsed floating point literal
@@ -113,8 +113,6 @@ type
   
   TLexer* = object of TBaseLexer
     fileIdx*: int32
-    indentStack*: seq[int]    # the indentation stack
-    dedent*: int              # counter for DED token generation
     indentAhead*: int         # if > 0 an indendation has already been read
                               # this is needed because scanning comments
                               # needs so much look-ahead
@@ -122,9 +120,6 @@ type
 
 var gLinesCompiled*: int  # all lines that have been compiled
 
-proc pushInd*(L: var TLexer, indent: int)
-
-proc popInd*(L: var TLexer)
 proc isKeyword*(kind: TTokType): bool
 proc openLexer*(lex: var TLexer, fileidx: int32, inputstream: PLLStream)
 proc rawGetTok*(L: var TLexer, tok: var TToken)
@@ -154,31 +149,14 @@ proc isNimrodIdentifier*(s: string): bool =
       inc(i)
     result = true
 
-proc pushInd(L: var TLexer, indent: int) = 
-  var length = len(L.indentStack)
-  setlen(L.indentStack, length + 1)
-  if (indent > L.indentStack[length - 1]): 
-    L.indentstack[length] = indent
-  else: 
-    InternalError("pushInd")
-  
-proc popInd(L: var TLexer) = 
-  var length = len(L.indentStack)
-  setlen(L.indentStack, length - 1)
-
-proc findIdent(L: TLexer, indent: int): bool = 
-  for i in countdown(len(L.indentStack) - 1, 0): 
-    if L.indentStack[i] == indent: 
-      return true
-
 proc tokToStr*(tok: TToken): string = 
   case tok.tokType
   of tkIntLit..tkInt64Lit: result = $tok.iNumber
   of tkFloatLit..tkFloat64Lit: result = $tok.fNumber
   of tkInvalid, tkStrLit..tkCharLit, tkComment: result = tok.literal
-  of tkParLe..tkColon, tkEof, tkInd, tkSad, tkDed, tkAccent: 
+  of tkParLe..tkColon, tkEof, tkAccent: 
     result = tokTypeToStr[tok.tokType]
-  else: 
+  else:
     if tok.ident != nil:
       result = tok.ident.s
     else: 
@@ -216,7 +194,6 @@ proc fillToken(L: var TToken) =
   
 proc openLexer(lex: var TLexer, fileIdx: int32, inputstream: PLLStream) = 
   openBaseLexer(lex, inputstream)
-  lex.indentStack = @[0]
   lex.fileIdx = fileIdx
   lex.indentAhead = - 1
   inc(lex.Linenumber, inputstream.lineOffset) 
@@ -434,9 +411,10 @@ proc GetNumber(L: var TLexer): TToken =
           result.tokType = tkInt64Lit
         elif result.tokType != tkInt64Lit: 
           lexMessage(L, errInvalidNumber, result.literal)
-  except EInvalidValue: lexMessage(L, errInvalidNumber, result.literal)
-  except EOverflow: lexMessage(L, errNumberOutOfRange, result.literal)
-  except EOutOfRange: lexMessage(L, errNumberOutOfRange, result.literal)
+  except EInvalidValue:
+    lexMessage(L, errInvalidNumber, result.literal)
+  except EOverflow, EOutOfRange:
+    lexMessage(L, errNumberOutOfRange, result.literal)
   L.bufpos = endpos
 
 proc handleHexChar(L: var TLexer, xi: var int) = 
@@ -651,24 +629,6 @@ proc getOperator(L: var TLexer, tok: var TToken) =
     Inc(pos)
   endOperator(L, tok, pos, h)
 
-proc handleIndentation(L: var TLexer, tok: var TToken, indent: int) = 
-  tok.indent = indent
-  var i = high(L.indentStack)
-  if indent > L.indentStack[i]: 
-    tok.tokType = tkInd
-  elif indent == L.indentStack[i]: 
-    tok.tokType = tkSad
-  else: 
-    # check we have the indentation somewhere in the stack:
-    while (i >= 0) and (indent != L.indentStack[i]): 
-      dec(i)
-      inc(L.dedent)
-    dec(L.dedent)
-    tok.tokType = tkDed
-    if i < 0: 
-      tok.tokType = tkSad     # for the parser it is better as SAD
-      lexMessage(L, errInvalidIndentation)
-
 proc scanComment(L: var TLexer, tok: var TToken) = 
   var pos = L.bufpos
   var buf = L.buf 
@@ -705,53 +665,45 @@ proc scanComment(L: var TLexer, tok: var TToken) =
     else:
       if buf[pos] > ' ': 
         L.indentAhead = indent
-        inc(L.dedent)
-      break 
+      break
   L.bufpos = pos
 
-proc skip(L: var TLexer, tok: var TToken) = 
+proc skip(L: var TLexer, tok: var TToken) =
   var pos = L.bufpos
   var buf = L.buf
-  while true: 
+  while true:
     case buf[pos]
-    of ' ': 
+    of ' ':
       Inc(pos)
-    of Tabulator: 
+    of Tabulator:
       lexMessagePos(L, errTabulatorsAreNotAllowed, pos)
-      inc(pos)                # BUGFIX
-    of CR, LF: 
+      inc(pos)
+    of CR, LF:
       pos = HandleCRLF(L, pos)
       buf = L.buf
       var indent = 0
-      while buf[pos] == ' ': 
+      while buf[pos] == ' ':
         Inc(pos)
         Inc(indent)
-      if (buf[pos] > ' '): 
-        handleIndentation(L, tok, indent)
-        break 
-    else: 
+      if buf[pos] > ' ':
+        tok.indent = indent
+        break
+    else:
       break                   # EndOfFile also leaves the loop
   L.bufpos = pos
 
-proc rawGetTok(L: var TLexer, tok: var TToken) = 
+proc rawGetTok(L: var TLexer, tok: var TToken) =
   fillToken(tok)
-  if L.dedent > 0:
-    dec(L.dedent)
-    if L.indentAhead >= 0: 
-      handleIndentation(L, tok, L.indentAhead)
-      L.indentAhead = - 1
-    else:
-      tok.tokType = tkDed
-    return
+  if L.indentAhead >= 0:
+    tok.indent = L.indentAhead
+    L.indentAhead = -1
+  else:
+    tok.indent = -1
   skip(L, tok)
-  # got an documentation comment or tkIndent, return that:
-  if tok.toktype != tkInvalid: return
   var c = L.buf[L.bufpos]
-  if c in SymStartChars - {'r', 'R', 'l'}: 
+  if c in SymStartChars - {'r', 'R', 'l'}:
     getSymbol(L, tok)
-  elif c in {'0'..'9'}: 
-    tok = getNumber(L)
-  else: 
+  else:
     case c
     of '#': 
       scanComment(L, tok)
@@ -769,10 +721,10 @@ proc rawGetTok(L: var TLexer, tok: var TToken) =
     of 'l': 
       # if we parsed exactly one character and its a small L (l), this
       # is treated as a warning because it may be confused with the number 1
-      if not (L.buf[L.bufpos + 1] in (SymChars + {'_'})): 
+      if L.buf[L.bufpos+1] notin (SymChars + {'_'}):
         lexMessage(L, warnSmallLshouldNotBeUsed)
       getSymbol(L, tok)
-    of 'r', 'R': 
+    of 'r', 'R':
       if L.buf[L.bufPos + 1] == '\"': 
         Inc(L.bufPos)
         getString(L, tok, true)
@@ -780,7 +732,7 @@ proc rawGetTok(L: var TLexer, tok: var TToken) =
         getSymbol(L, tok)
     of '(': 
       Inc(L.bufpos)
-      if (L.buf[L.bufPos] == '.') and (L.buf[L.bufPos + 1] != '.'): 
+      if L.buf[L.bufPos] == '.' and L.buf[L.bufPos+1] != '.': 
         tok.toktype = tkParDotLe
         Inc(L.bufpos)
       else: 
@@ -790,29 +742,29 @@ proc rawGetTok(L: var TLexer, tok: var TToken) =
       Inc(L.bufpos)
     of '[': 
       Inc(L.bufpos)
-      if (L.buf[L.bufPos] == '.') and (L.buf[L.bufPos + 1] != '.'): 
+      if L.buf[L.bufPos] == '.' and L.buf[L.bufPos+1] != '.':
         tok.toktype = tkBracketDotLe
         Inc(L.bufpos)
-      else: 
+      else:
         tok.toktype = tkBracketLe
-    of ']': 
+    of ']':
       tok.toktype = tkBracketRi
       Inc(L.bufpos)
-    of '.': 
-      if L.buf[L.bufPos + 1] == ']': 
+    of '.':
+      if L.buf[L.bufPos+1] == ']': 
         tok.tokType = tkBracketDotRi
         Inc(L.bufpos, 2)
-      elif L.buf[L.bufPos + 1] == '}': 
+      elif L.buf[L.bufPos+1] == '}': 
         tok.tokType = tkCurlyDotRi
         Inc(L.bufpos, 2)
-      elif L.buf[L.bufPos + 1] == ')': 
+      elif L.buf[L.bufPos+1] == ')': 
         tok.tokType = tkParDotRi
         Inc(L.bufpos, 2)
       else: 
         getOperator(L, tok)
     of '{': 
       Inc(L.bufpos)
-      if (L.buf[L.bufPos] == '.') and (L.buf[L.bufPos+1] != '.'): 
+      if L.buf[L.bufPos] == '.' and L.buf[L.bufPos+1] != '.':
         tok.toktype = tkCurlyDotLe
         Inc(L.bufpos)
       else: 
@@ -838,13 +790,16 @@ proc rawGetTok(L: var TLexer, tok: var TToken) =
       tok.tokType = tkCharLit
       getCharacter(L, tok)
       tok.tokType = tkCharLit
+    of '0'..'9':
+      tok = getNumber(L)
     else:
       if c in OpChars: 
         getOperator(L, tok)
       elif c == nimlexbase.EndOfFile:
         tok.toktype = tkEof
+        tok.indent = 0
       else:
-        tok.literal = c & ""
+        tok.literal = $c
         tok.tokType = tkInvalid
         lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')')
         Inc(L.bufpos)
diff --git a/compiler/main.nim b/compiler/main.nim
index 46ef65e81..2ff7691d8 100644
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -16,7 +16,7 @@ import
   wordrecg, sem, semdata, idents, passes, docgen, extccomp,
   cgen, jsgen, cgendata, json, nversion,
   platform, nimconf, importer, passaux, depends, evals, types, idgen,
-  tables, docgen2, service, magicsys, parser, crc, ccgutils
+  tables, docgen2, service, magicsys, parser, crc, ccgutils, sigmatch
 
 const
   has_LLVM_Backend = false
@@ -64,7 +64,7 @@ proc crcChanged(fileIdx: int32): bool =
     gMemCacheData[fileIdx].crcStatus = if result: crcHasChanged
                                        else: crcNotChanged
     # echo "TESTING CRC: ", fileIdx.toFilename, " ", result
-    
+  
   case gMemCacheData[fileIdx].crcStatus:
   of crcHasChanged:
     result = true
@@ -89,38 +89,38 @@ proc addDep(x: Psym, dep: int32) =
   growCache gMemCacheData, dep
   gMemCacheData[x.position].deps.safeAdd(dep)
 
-proc ResetModule(fileIdx: int32) =
-  echo "HARD RESETTING ", fileIdx.toFilename
+proc resetModule(fileIdx: int32) =
+  # echo "HARD RESETTING ", fileIdx.toFilename
   gMemCacheData[fileIdx].needsRecompile = Yes
   gCompiledModules[fileIdx] = nil
   cgendata.gModules[fileIdx] = nil
+  resetSourceMap(fileIdx)
 
-proc ResetAllModules =
+proc resetAllModules =
   for i in 0..gCompiledModules.high:
     if gCompiledModules[i] != nil:
-      ResetModule(i.int32)
+      resetModule(i.int32)
 
-  for m in cgenModules():
-    echo "CGEN MODULE FOUND"
+  # for m in cgenModules(): echo "CGEN MODULE FOUND"
 
 proc checkDepMem(fileIdx: int32): TNeedRecompile  =
   template markDirty =
-    ResetModule(fileIdx)
+    resetModule(fileIdx)
     return Yes
 
   if gMemCacheData[fileIdx].needsRecompile != Maybe:
     return gMemCacheData[fileIdx].needsRecompile
 
   if optForceFullMake in gGlobalOptions or
-     curCaasCmd != lastCaasCmd or
-     crcChanged(fileIdx): markDirty
+     crcChanged(fileIdx):
+       markDirty
   
   if gMemCacheData[fileIdx].deps != nil:
     gMemCacheData[fileIdx].needsRecompile = Probing
     for dep in gMemCacheData[fileIdx].deps:
       let d = checkDepMem(dep)
       if d in { Yes, Recompiled }:
-        echo fileIdx.toFilename, " depends on ", dep.toFilename, " ", d
+        # echo fileIdx.toFilename, " depends on ", dep.toFilename, " ", d
         markDirty
   
   gMemCacheData[fileIdx].needsRecompile = No
@@ -213,6 +213,9 @@ proc rodPass =
   if optSymbolFiles in gGlobalOptions:
     registerPass(rodwritePass)
 
+proc codegenPass =
+  registerPass cgenPass
+
 proc semanticPasses =
   registerPass verbosePass
   registerPass semPass
@@ -251,14 +254,11 @@ proc CommandCompileToC =
     # echo "CHECK DEP COMPLETE"
 
   compileProject()
-
-  if compilationCachePresent:
-    updateCachedModules()
-
+  cgenWriteModules()
   if gCmd != cmdRun:
     extccomp.CallCCompiler(changeFileExt(gProjectFull, ""))
 
-  if optCaasEnabled in gGlobalOptions:
+  if isServing:
     # caas will keep track only of the compilation commands
     lastCaasCmd = curCaasCmd
     resetCgenModules()
@@ -377,10 +377,23 @@ proc CommandScan =
     rawMessage(errCannotOpenFile, f)
   
 proc CommandSuggest =
-  msgs.gErrorMax = high(int)  # do not stop after first error
-  semanticPasses()
-  rodPass()
-  compileProject()
+  if isServing:
+    # XXX: hacky work-around ahead
+    # Currently, it's possible to issue a idetools command, before
+    # issuing the first compile command. This will leave the compiler
+    # cache in a state where "no recompilation is necessary", but the
+    # cgen pass was never executed at all.
+    CommandCompileToC()
+    if gDirtyBufferIdx != 0:
+      discard compileModule(gDirtyBufferIdx, {sfDirty})
+      resetModule(gDirtyBufferIdx)
+    if optDef in gGlobalOptions:
+      defFromSourceMap(optTrackPos)
+  else:
+    msgs.gErrorMax = high(int)  # do not stop after first error
+    semanticPasses()
+    rodPass()
+    compileProject()
 
 proc wantMainModule =
   if gProjectFull.len == 0:
@@ -404,7 +417,7 @@ proc requireMainModuleOption =
 proc resetMemory =
   resetCompilationLists()
   ccgutils.resetCaches()
-  ResetAllModules()
+  resetAllModules()
   resetRopeCache()
   resetSysTypes()
   gOwners = @[]
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 6062ebd7f..16ef06b61 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -526,13 +526,17 @@ proc SuggestWriteln*(s: string) =
     else: 
       Writeln(stdout, s)
       stdoutSocket.send(s & "\c\L")
-    
+
 proc SuggestQuit*() =
-  if not isServing: quit(0)
-  elif not isNil(stdoutSocket):
-    stdoutSocket.send("\c\L")
+  if not isServing:
+    quit(0)
+  elif isWorkingWithDirtyBuffer:
+    # No need to compile the rest if we are working with a
+    # throw-away buffer. Incomplete dot expressions frequently
+    # found in dirty buffers will result in errors few steps
+    # from now anyway.
     raise newException(ESuggestDone, "suggest done")
-  
+
 # this format is understood by many text editors: it is the same that
 # Borland and Freepascal use
 const
@@ -606,6 +610,7 @@ proc `??`* (info: TLineInfo, filename: string): bool =
   result = filename in info.toFilename
 
 var checkPoints*: seq[TLineInfo] = @[]
+var optTrackPos*: TLineInfo
 
 proc addCheckpoint*(info: TLineInfo) = 
   checkPoints.add(info)
@@ -714,7 +719,7 @@ var
 
 proc writeSurroundingSrc(info: TLineInfo) =
   const indent = "  "
-  MsgWriteln(indent & info.sourceLine.data)
+  MsgWriteln(indent & info.sourceLine.ropeToStr)
   MsgWriteln(indent & repeatChar(info.col, ' ') & '^')
 
 proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, 
@@ -789,8 +794,9 @@ proc sourceLine*(i: TLineInfo): PRope =
     for line in lines(i.toFullPath):
       addSourceLine i.fileIndex, line.string
 
-  InternalAssert i.fileIndex < fileInfos.len and
-                 i.line <= fileInfos[i.fileIndex].lines.len
+  InternalAssert i.fileIndex < fileInfos.len
+  # can happen if the error points to EOF:
+  if i.line > fileInfos[i.fileIndex].lines.len: return nil
 
   result = fileInfos[i.fileIndex].lines[i.line-1]
 
diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim
index 0f0b76827..3bd97ccb2 100644
--- a/compiler/nimconf.nim
+++ b/compiler/nimconf.nim
@@ -19,7 +19,7 @@ import
 proc ppGetTok(L: var TLexer, tok: var TToken) = 
   # simple filter
   rawGetTok(L, tok)
-  while tok.tokType in {tkInd, tkSad, tkDed, tkComment}: rawGetTok(L, tok)
+  while tok.tokType in {tkComment}: rawGetTok(L, tok)
   
 proc parseExpr(L: var TLexer, tok: var TToken): bool
 proc parseAtom(L: var TLexer, tok: var TToken): bool = 
diff --git a/compiler/options.nim b/compiler/options.nim
index a7d513dc5..f67ded163 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -7,13 +7,14 @@
 #    distribution, for details about the copyright.
 #
 
-import 
+import
   os, lists, strutils, strtabs
   
 const
   hasTinyCBackend* = defined(tinyc)
   useEffectSystem* = true
   hasFFI* = defined(useFFI)
+  newScopeForIf* = false      # XXX activate for 0.9.4
 
 type                          # please make sure we have under 32 options
                               # (improves code efficiency a lot!)
@@ -106,10 +107,16 @@ var
   gLastCmdTime*: float        # when caas is enabled, we measure each command
   gListFullPaths*: bool
   isServing*: bool = false
+  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.
 
 proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools}
 proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc
 
+template isWorkingWithDirtyBuffer*: expr =
+  gDirtyBufferIdx != 0
+
 template compilationCachePresent*: expr =
   {optCaasEnabled, optSymbolFiles} * gGlobalOptions != {}
 
diff --git a/compiler/parser.nim b/compiler/parser.nim
index 769aa7a3e..e2167f460 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nimrod Compiler
-#        (c) Copyright 2012 Andreas Rumpf
+#        (c) Copyright 2013 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -12,17 +12,28 @@
 # it uses several helper routines to keep the parser small. A special
 # efficient algorithm is used for the precedence levels. The parser here can
 # be seen as a refinement of the grammar, as it specifies how the AST is built
-# from the grammar and how comments belong to the AST.
+# from the grammar and how comments belong to the AST. 
+
+
+# In fact the grammar is generated from this file:
+when isMainModule:
+  import pegs
+  var outp = open("compiler/grammar.txt", fmWrite)
+  for line in lines("compiler/parser.nim"):
+    if line =~ peg" \s* '#| ' {.*}":
+      outp.writeln matches[0]
+  outp.close
 
 import
-  llstream, lexer, idents, strutils, ast, msgs
+  llstream, lexer, idents, strutils, ast, astalgo, msgs
 
 type
   TParser*{.final.} = object  # a TParser object represents a module that
                               # is being parsed
+    currInd: int              # current indentation
+    firstTok: bool
     lex*: TLexer              # the lexer that is used for parsing
     tok*: TToken              # the current token
-  
 
 proc ParseAll*(p: var TParser): PNode
 proc openParser*(p: var TParser, filename: string, inputstream: PLLStream)
@@ -68,6 +79,7 @@ proc OpenParser*(p: var TParser, fileIdx: int32, inputStream: PLLStream) =
   initToken(p.tok)
   OpenLexer(p.lex, fileIdx, inputstream)
   getTok(p)                   # read the first token
+  p.firstTok = true
 
 proc OpenParser*(p: var TParser, filename: string, inputStream: PLLStream) =
   openParser(p, filename.fileInfoIdx, inputStream)
@@ -81,51 +93,64 @@ proc parMessage(p: TParser, msg: TMsgKind, arg: string = "") =
 proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) = 
   lexMessage(p.lex, msg, prettyTok(tok))
 
-proc skipComment(p: var TParser, node: PNode) = 
-  if p.tok.tokType == tkComment: 
-    if node != nil: 
+template withInd(p: expr, body: stmt) {.immediate.} =
+  let oldInd = p.currInd
+  p.currInd = p.tok.indent
+  body
+  p.currInd = oldInd
+
+template realInd(p): bool = p.tok.indent > p.currInd
+template sameInd(p): bool = p.tok.indent == p.currInd
+template sameOrNoInd(p): bool = p.tok.indent == p.currInd or p.tok.indent < 0
+
+proc rawSkipComment(p: var TParser, node: PNode) =
+  if p.tok.tokType == tkComment:
+    if node != nil:
       if node.comment == nil: node.comment = ""
       add(node.comment, p.tok.literal)
-    else: 
+    else:
       parMessage(p, errInternal, "skipComment")
     getTok(p)
 
-proc skipInd(p: var TParser) = 
-  if p.tok.tokType == tkInd: getTok(p)
-  
-proc optPar(p: var TParser) = 
-  if p.tok.tokType == tkSad or p.tok.tokType == tkInd: getTok(p)
-  
-proc optInd(p: var TParser, n: PNode) = 
+proc skipComment(p: var TParser, node: PNode) =
+  if p.tok.indent < 0: rawSkipComment(p, node)
+
+proc skipInd(p: var TParser) =
+  if p.tok.indent >= 0:
+    if not realInd(p): parMessage(p, errInvalidIndentation)
+
+proc optPar(p: var TParser) =
+  if p.tok.indent >= 0:
+    if p.tok.indent < p.currInd: parMessage(p, errInvalidIndentation)
+
+proc optInd(p: var TParser, n: PNode) =
   skipComment(p, n)
   skipInd(p)
 
-proc ExpectNl(p: TParser) = 
-  if p.tok.tokType notin {tkEof, tkSad, tkInd, tkDed, tkComment}: 
-    lexMessage(p.lex, errNewlineExpected, prettyTok(p.tok))
+proc getTokNoInd(p: var TParser) =
+  getTok(p)
+  if p.tok.indent >= 0: parMessage(p, errInvalidIndentation)
 
-proc expectIdentOrKeyw(p: TParser) = 
-  if p.tok.tokType != tkSymbol and not isKeyword(p.tok.tokType): 
+proc expectIdentOrKeyw(p: TParser) =
+  if p.tok.tokType != tkSymbol and not isKeyword(p.tok.tokType):
     lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok))
   
-proc ExpectIdent(p: TParser) = 
-  if p.tok.tokType != tkSymbol: 
+proc ExpectIdent(p: TParser) =
+  if p.tok.tokType != tkSymbol:
     lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok))
   
-proc Eat(p: var TParser, TokType: TTokType) = 
+proc Eat(p: var TParser, TokType: TTokType) =
   if p.tok.TokType == TokType: getTok(p)
   else: lexMessage(p.lex, errTokenExpected, TokTypeToStr[tokType])
   
-proc parLineInfo(p: TParser): TLineInfo = 
+proc parLineInfo(p: TParser): TLineInfo =
   result = getLineInfo(p.lex)
 
-proc indAndComment(p: var TParser, n: PNode) = 
-  if p.tok.tokType == tkInd: 
-    var info = parLineInfo(p)
-    getTok(p)
-    if p.tok.tokType == tkComment: skipComment(p, n)
-    else: LocalError(info, errInvalidIndentation)
-  else: 
+proc indAndComment(p: var TParser, n: PNode) =
+  if p.tok.indent > p.currInd:
+    if p.tok.tokType == tkComment: rawSkipComment(p, n)
+    else: parMessage(p, errInvalidIndentation)
+  else:
     skipComment(p, n)
   
 proc newNodeP(kind: TNodeKind, p: TParser): PNode = 
@@ -195,7 +220,41 @@ proc getPrecedence(tok: TToken): int =
 proc isOperator(tok: TToken): bool = 
   result = getPrecedence(tok) >= 0
 
-proc parseSymbol(p: var TParser): PNode = 
+#| module = stmt ^* (';' / IND{=})
+#|
+#| comma = ',' COMMENT?
+#| semicolon = ';' COMMENT?
+#| colon = ':' COMMENT?
+#| colcom = ':' COMMENT?
+#| 
+#| operator =  OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9
+#|          | 'or' | 'xor' | 'and'
+#|          | 'is' | 'isnot' | 'in' | 'notin' | 'of'
+#|          | 'div' | 'mod' | 'shl' | 'shr' | 'not' | 'addr' | 'static' | '..'
+#| 
+#| prefixOperator = operator
+#| 
+#| optInd = COMMENT?
+#| optPar = (IND{>} | IND{=})?
+#| 
+#| simpleExpr = assignExpr (OP0 optInd assignExpr)*
+#| assignExpr = orExpr (OP1 optInd orExpr)*
+#| orExpr = andExpr (OP2 optInd andExpr)*
+#| andExpr = cmpExpr (OP3 optInd cmpExpr)*
+#| cmpExpr = sliceExpr (OP4 optInd sliceExpr)*
+#| sliceExpr = ampExpr (OP5 optInd ampExpr)*
+#| ampExpr = plusExpr (OP6 optInd plusExpr)*
+#| plusExpr = mulExpr (OP7 optInd mulExpr)*
+#| mulExpr = dollarExpr (OP8 optInd dollarExpr)*
+#| dollarExpr = primary (OP9 optInd primary)*
+
+proc colcom(p: var TParser, n: PNode) =
+  eat(p, tkColon)
+  skipComment(p, n)
+
+proc parseSymbol(p: var TParser): PNode =
+  #| symbol = '`' (KEYW|IDENT|operator|'(' ')'|'[' ']'|'{' '}'|'='|literal)+ '`'
+  #|        | IDENT
   case p.tok.tokType
   of tkSymbol: 
     result = newIdentNodeP(p.tok.ident, p)
@@ -231,31 +290,32 @@ proc parseSymbol(p: var TParser): PNode =
           parMessage(p, errIdentifierExpected, p.tok)
         break
     eat(p, tkAccent)
-  else: 
+  else:
     parMessage(p, errIdentifierExpected, p.tok)
     getTok(p) # BUGFIX: We must consume a token here to prevent endless loops!
     result = ast.emptyNode
 
 proc indexExpr(p: var TParser): PNode = 
+  #| indexExpr = expr
   result = parseExpr(p)
 
 proc indexExprList(p: var TParser, first: PNode, k: TNodeKind, 
                    endToken: TTokType): PNode = 
+  #| indexExprList = indexExpr ^+ comma
   result = newNodeP(k, p)
   addSon(result, first)
   getTok(p)
   optInd(p, result)
-  while p.tok.tokType notin {endToken, tkEof, tkSad}:
+  while p.tok.tokType notin {endToken, tkEof}:
     var a = indexExpr(p)
     addSon(result, a)
     if p.tok.tokType != tkComma: break 
     getTok(p)
-    optInd(p, a)
+    skipComment(p, a)
   optPar(p)
   eat(p, endToken)
 
-proc exprColonEqExpr(p: var TParser): PNode =
-  var a = parseExpr(p)
+proc colonOrEquals(p: var TParser, a: PNode): PNode =
   if p.tok.tokType == tkColon:
     result = newNodeP(nkExprColonExpr, p)
     getTok(p)
@@ -271,7 +331,13 @@ proc exprColonEqExpr(p: var TParser): PNode =
   else:
     result = a
 
+proc exprColonEqExpr(p: var TParser): PNode =
+  #| exprColonEqExpr = expr (':'|'=' expr)?
+  var a = parseExpr(p)
+  result = colonOrEquals(p, a)
+
 proc exprList(p: var TParser, endTok: TTokType, result: PNode) = 
+  #| exprList = expr ^+ comma
   getTok(p)
   optInd(p, result)
   while (p.tok.tokType != endTok) and (p.tok.tokType != tkEof): 
@@ -283,6 +349,7 @@ proc exprList(p: var TParser, endTok: TTokType, result: PNode) =
   eat(p, endTok)
 
 proc dotExpr(p: var TParser, a: PNode): PNode =
+  #| dotExpr = expr '.' optInd ('type' | 'addr' | symbol)
   var info = p.lex.getlineInfo
   getTok(p)
   optInd(p, a)
@@ -301,40 +368,31 @@ proc dotExpr(p: var TParser, a: PNode): PNode =
     addSon(result, parseSymbol(p))
 
 proc qualifiedIdent(p: var TParser): PNode = 
-  result = parseSymbol(p)     #optInd(p, result);
+  #| qualifiedIdent = symbol ('.' optInd ('type' | 'addr' | symbol))?
+  result = parseSymbol(p)
   if p.tok.tokType == tkDot: result = dotExpr(p, result)
 
-proc qualifiedIdentListAux(p: var TParser, endTok: TTokType, result: PNode) = 
-  getTok(p)
-  optInd(p, result)
-  while (p.tok.tokType != endTok) and (p.tok.tokType != tkEof): 
-    var a = qualifiedIdent(p)
-    addSon(result, a)         #optInd(p, a);
-    if p.tok.tokType != tkComma: break 
-    getTok(p)
-    optInd(p, a)
-  eat(p, endTok)
-
-proc exprColonEqExprListAux(p: var TParser, endTok: TTokType, result: PNode) = 
+proc exprColonEqExprListAux(p: var TParser, endTok: TTokType, result: PNode) =
   assert(endTok in {tkCurlyRi, tkCurlyDotRi, tkBracketRi, tkParRi})
   getTok(p)
   optInd(p, result)
-  while (p.tok.tokType != endTok) and (p.tok.tokType != tkEof) and
-      (p.tok.tokType != tkSad) and (p.tok.tokType != tkInd): 
+  while p.tok.tokType != endTok and p.tok.tokType != tkEof:
     var a = exprColonEqExpr(p)
     addSon(result, a)
     if p.tok.tokType != tkComma: break 
     getTok(p)
-    optInd(p, a)
+    skipComment(p, a)
   optPar(p)
   eat(p, endTok)
 
-proc exprColonEqExprList(p: var TParser, kind: TNodeKind, 
-                         endTok: TTokType): PNode = 
+proc exprColonEqExprList(p: var TParser, kind: TNodeKind,
+                         endTok: TTokType): PNode =
+  #| exprColonEqExprList = exprColonEqExpr (comma exprColonEqExpr)* (comma)?
   result = newNodeP(kind, p)
   exprColonEqExprListAux(p, endTok, result)
 
 proc setOrTableConstr(p: var TParser): PNode =
+  #| setOrTableConstr = '{' ((exprColonEqExpr comma)* | ':' ) '}'
   result = newNodeP(nkCurly, p)
   getTok(p) # skip '{'
   optInd(p, result)
@@ -342,17 +400,18 @@ proc setOrTableConstr(p: var TParser): PNode =
     getTok(p) # skip ':'
     result.kind = nkTableConstr
   else:
-    while p.tok.tokType notin {tkCurlyRi, tkEof, tkSad, tkInd}: 
+    while p.tok.tokType notin {tkCurlyRi, tkEof}:
       var a = exprColonEqExpr(p)
       if a.kind == nkExprColonExpr: result.kind = nkTableConstr
       addSon(result, a)
       if p.tok.tokType != tkComma: break 
       getTok(p)
-      optInd(p, a)
+      skipComment(p, a)
   optPar(p)
   eat(p, tkCurlyRi) # skip '}'
 
 proc parseCast(p: var TParser): PNode = 
+  #| castExpr = 'cast' '[' optInd typeDesc optPar ']' '(' optInd expr optPar ')'
   result = newNodeP(nkCast, p)
   getTok(p)
   eat(p, tkBracketLe)
@@ -366,15 +425,6 @@ proc parseCast(p: var TParser): PNode =
   optPar(p)
   eat(p, tkParRi)
 
-proc parseAddr(p: var TParser): PNode = 
-  result = newNodeP(nkAddr, p)
-  getTok(p)
-  eat(p, tkParLe)
-  optInd(p, result)
-  addSon(result, parseExpr(p))
-  optPar(p)
-  eat(p, tkParRi)
-
 proc setBaseFlags(n: PNode, base: TNumericalBase) = 
   case base
   of base10: nil
@@ -396,16 +446,98 @@ proc parseGStrLit(p: var TParser, a: PNode): PNode =
     getTok(p)
   else:
     result = a
-  
-proc identOrLiteral(p: var TParser): PNode = 
+
+type
+  TPrimaryMode = enum pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix
+
+proc complexOrSimpleStmt(p: var TParser): PNode
+proc simpleExpr(p: var TParser, mode = pmNormal): PNode
+
+proc semiStmtList(p: var TParser, result: PNode) =
+  result.add(complexOrSimpleStmt(p))
+  while p.tok.tokType == tkSemicolon:
+    getTok(p)
+    optInd(p, result)
+    result.add(complexOrSimpleStmt(p))
+  result.kind = nkStmtListExpr
+
+proc parsePar(p: var TParser): PNode =
+  #| parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try'
+  #|         | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let'
+  #|         | 'when' | 'var' | 'mixin'
+  #| par = '(' optInd (&parKeyw complexOrSimpleStmt ^+ ';' 
+  #|                  | simpleExpr ('=' expr (';' complexOrSimpleStmt ^+ ';' )? )?
+  #|                             | (':' expr)? (',' (exprColonEqExpr comma?)*)?  )?
+  #|         optPar ')'
+  #
+  # unfortunately it's ambiguous: (expr: expr) vs (exprStmt); however a 
+  # leading ';' could be used to enforce a 'stmt' context ...
+  result = newNodeP(nkPar, p)
+  getTok(p)
+  optInd(p, result)
+  if p.tok.tokType in {tkDiscard, tkInclude, tkIf, tkWhile, tkCase, 
+                       tkTry, tkFinally, tkExcept, tkFor, tkBlock, 
+                       tkConst, tkLet, tkWhen, tkVar,
+                       tkMixin}:
+    # XXX 'bind' used to be an expression, so we exclude it here;
+    # tests/reject/tbind2 fails otherwise.
+    semiStmtList(p, result)
+  elif p.tok.tokType == tkSemicolon:
+    # '(;' enforces 'stmt' context:
+    getTok(p)
+    optInd(p, result)
+    semiStmtList(p, result)
+  elif p.tok.tokType != tkParRi:
+    var a = simpleExpr(p)
+    if p.tok.tokType == tkEquals:
+      # special case: allow assignments
+      getTok(p)
+      optInd(p, result)
+      let b = parseExpr(p)
+      let asgn = newNodeI(nkAsgn, a.info, 2)
+      asgn.sons[0] = a
+      asgn.sons[1] = b
+      result.add(asgn)
+    elif p.tok.tokType == tkSemicolon:
+      # stmt context:
+      result.add(a)
+      semiStmtList(p, result)
+    else:
+      a = colonOrEquals(p, a)
+      result.add(a)
+      if p.tok.tokType == tkComma:
+        getTok(p)
+        skipComment(p, a)
+        while p.tok.tokType != tkParRi and p.tok.tokType != tkEof:
+          var a = exprColonEqExpr(p)
+          addSon(result, a)
+          if p.tok.tokType != tkComma: break 
+          getTok(p)
+          skipComment(p, a)
+  optPar(p)
+  eat(p, tkParRi)
+
+proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode = 
+  #| generalizedLit = GENERALIZED_STR_LIT | GENERALIZED_TRIPLESTR_LIT
+  #| identOrLiteral = generalizedLit | symbol 
+  #|                | INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT
+  #|                | UINT_LIT | UINT8_LIT | UINT16_LIT | UINT32_LIT | UINT64_LIT
+  #|                | FLOAT_LIT | FLOAT32_LIT | FLOAT64_LIT
+  #|                | STR_LIT | RSTR_LIT | TRIPLESTR_LIT
+  #|                | CHAR_LIT
+  #|                | NIL
+  #|                | par | arrayConstr | setOrTableConstr
+  #|                | castExpr
+  #| tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')'
+  #| arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']'
   case p.tok.tokType
-  of tkSymbol: 
+  of tkSymbol:
     result = newIdentNodeP(p.tok.ident, p)
     getTok(p)
     result = parseGStrLit(p, result)
   of tkAccent: 
     result = parseSymbol(p)       # literals
-  of tkIntLit: 
+  of tkIntLit:
     result = newIntNodeP(nkIntLit, p.tok.iNumber, p)
     setBaseFlags(result, p.tok.base)
     getTok(p)
@@ -478,7 +610,10 @@ proc identOrLiteral(p: var TParser): PNode =
     getTok(p)
   of tkParLe:
     # () constructor
-    result = exprColonEqExprList(p, nkPar, tkParRi)
+    if mode in {pmTypeDesc, pmTypeDef}:
+      result = exprColonEqExprList(p, nkPar, tkParRi)
+    else:
+      result = parsePar(p)
   of tkCurlyLe:
     # {} constructor
     result = setOrTableConstr(p)
@@ -493,8 +628,13 @@ proc identOrLiteral(p: var TParser): PNode =
     result = ast.emptyNode
 
 proc primarySuffix(p: var TParser, r: PNode): PNode =
+  #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
+  #|               | doBlocks
+  #|               | '.' optInd ('type' | 'addr' | symbol) generalizedLit?
+  #|               | '[' optInd indexExprList optPar ']'
+  #|               | '{' optInd indexExprList optPar '}'
   result = r
-  while true:
+  while p.tok.indent < 0:
     case p.tok.tokType
     of tkParLe: 
       var a = result
@@ -519,69 +659,70 @@ proc primarySuffix(p: var TParser, r: PNode): PNode =
       result = indexExprList(p, result, nkCurlyExpr, tkCurlyRi)
     else: break
 
-type
-  TPrimaryMode = enum pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix
-
 proc primary(p: var TParser, mode: TPrimaryMode): PNode
 
-proc lowestExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode =
+proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode =
   result = primary(p, mode)
   # expand while operators have priorities higher than 'limit'
   var opPrec = getPrecedence(p.tok)
   let modeB = if mode == pmTypeDef: pmTypeDesc else: mode
-  while opPrec >= limit:
+  # the operator itself must not start on a new line:
+  while opPrec >= limit and p.tok.indent < 0:
     var leftAssoc = ord(IsLeftAssociative(p.tok))
     var a = newNodeP(nkInfix, p)
     var opNode = newIdentNodeP(p.tok.ident, p) # skip operator:
     getTok(p)
-    optInd(p, opNode)         
+    optInd(p, opNode)
     # read sub-expression with higher priority:
-    var b = lowestExprAux(p, opPrec + leftAssoc, modeB)
+    var b = simpleExprAux(p, opPrec + leftAssoc, modeB)
     addSon(a, opNode)
     addSon(a, result)
     addSon(a, b)
     result = a
     opPrec = getPrecedence(p.tok)
   
-proc lowestExpr(p: var TParser, mode = pmNormal): PNode = 
-  result = lowestExprAux(p, -1, mode)
-
-proc parseIfExpr(p: var TParser, kind: TNodeKind): PNode = 
+proc simpleExpr(p: var TParser, mode = pmNormal): PNode =
+  result = simpleExprAux(p, -1, mode)
+
+proc parseIfExpr(p: var TParser, kind: TNodeKind): PNode =
+  #| condExpr = expr colcom expr optInd
+  #|         ('elif' expr colcom expr optInd)*
+  #|          'else' colcom expr
+  #| ifExpr = 'if' condExpr
+  #| whenExpr = 'when' condExpr
   result = newNodeP(kind, p)
-  while true: 
+  while true:
     getTok(p)                 # skip `if`, `elif`
     var branch = newNodeP(nkElifExpr, p)
     addSon(branch, parseExpr(p))
-    eat(p, tkColon)
-    optInd(p, branch)
+    colcom(p, branch)
     addSon(branch, parseExpr(p))
     optInd(p, branch)
     addSon(result, branch)
     if p.tok.tokType != tkElif: break 
   var branch = newNodeP(nkElseExpr, p)
   eat(p, tkElse)
-  eat(p, tkColon)
-  optInd(p, branch)
+  colcom(p, branch)
   addSon(branch, parseExpr(p))
   addSon(result, branch)
 
-proc parsePragma(p: var TParser): PNode = 
+proc parsePragma(p: var TParser): PNode =
+  #| pragma = '{.' optInd (exprColonExpr comma?)* optPar ('.}' | '}')
   result = newNodeP(nkPragma, p)
   getTok(p)
   optInd(p, result)
-  while (p.tok.tokType != tkCurlyDotRi) and (p.tok.tokType != tkCurlyRi) and
-      (p.tok.tokType != tkEof) and (p.tok.tokType != tkSad): 
+  while p.tok.tokType notin {tkCurlyDotRi, tkCurlyRi, tkEof}:
     var a = exprColonEqExpr(p)
     addSon(result, a)
-    if p.tok.tokType == tkComma: 
+    if p.tok.tokType == tkComma:
       getTok(p)
-      optInd(p, a)
+      skipComment(p, a)
   optPar(p)
   if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}: getTok(p)
   else: parMessage(p, errTokenExpected, ".}")
   
 proc identVis(p: var TParser): PNode = 
-  # identifier with visability
+  #| identVis = symbol opr?  # postfix position
   var a = parseSymbol(p)
   if p.tok.tokType == tkOpr: 
     result = newNodeP(nkPostfix, p)
@@ -592,6 +733,7 @@ proc identVis(p: var TParser): PNode =
     result = a
   
 proc identWithPragma(p: var TParser): PNode = 
+  #| identWithPragma = identVis pragma?
   var a = identVis(p)
   if p.tok.tokType == tkCurlyDotLe: 
     result = newNodeP(nkPragmaExpr, p)
@@ -599,14 +741,18 @@ proc identWithPragma(p: var TParser): PNode =
     addSon(result, parsePragma(p))
   else: 
     result = a
-  
-type 
+
+type
   TDeclaredIdentFlag = enum 
     withPragma,               # identifier may have pragma
     withBothOptional          # both ':' and '=' parts are optional
   TDeclaredIdentFlags = set[TDeclaredIdentFlag]
 
 proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode = 
+  #| declColonEquals = identWithPragma (comma identWithPragma)* comma?
+  #|                   (':' optInd typeDesc)? ('=' optInd expr)?
+  #| identColonEquals = ident (comma ident)* comma?
+  #|      (':' optInd typeDesc)? ('=' optInd expr)?)
   var a: PNode
   result = newNodeP(nkIdentDefs, p)
   while true: 
@@ -635,53 +781,53 @@ proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode =
   else: 
     addSon(result, ast.emptyNode)
   
-proc parseTuple(p: var TParser, indentAllowed = false): PNode = 
+proc parseTuple(p: var TParser, indentAllowed = false): PNode =
+  #| inlTupleDecl = 'tuple'
+  #|     [' optInd  (identColonEquals (comma/semicolon)?)*  optPar ']'
+  #| extTupleDecl = 'tuple'
+  #|     COMMENT? (IND{>} identColonEquals (IND{=} identColonEquals)*)?
   result = newNodeP(nkTupleTy, p)
   getTok(p)
   if p.tok.tokType == tkBracketLe:
     getTok(p)
     optInd(p, result)
-    while (p.tok.tokType == tkSymbol) or (p.tok.tokType == tkAccent): 
+    while p.tok.tokType in {tkSymbol, tkAccent}:
       var a = parseIdentColonEquals(p, {})
       addSon(result, a)
-      if p.tok.tokType notin {tkComma, tkSemicolon}: break 
+      if p.tok.tokType notin {tkComma, tkSemicolon}: break
       getTok(p)
-      optInd(p, a)
+      skipComment(p, a)
     optPar(p)
     eat(p, tkBracketRi)
   elif indentAllowed:
     skipComment(p, result)
-    if p.tok.tokType == tkInd:
-      pushInd(p.lex, p.tok.indent)
-      getTok(p)
-      skipComment(p, result)
-      while true:
-        case p.tok.tokType
-        of tkSad:
-          getTok(p)
-        of tkSymbol, tkAccent:
-          var a = parseIdentColonEquals(p, {})
-          skipComment(p, a)
-          addSon(result, a)
-        of tkDed:
-          getTok(p)
-          break
-        of tkEof:
-          break
-        else:
-          parMessage(p, errIdentifierExpected, p.tok)
-          break
-      popInd(p.lex)
-
-proc parseParamList(p: var TParser, retColon = true): PNode = 
+    if realInd(p):
+      withInd(p):
+        skipComment(p, result)
+        while true:
+          case p.tok.tokType
+          of tkSymbol, tkAccent:
+            var a = parseIdentColonEquals(p, {})
+            skipComment(p, a)
+            addSon(result, a)
+          of tkEof: break
+          else:
+            parMessage(p, errIdentifierExpected, p.tok)
+            break
+          if not sameInd(p): break
+
+proc parseParamList(p: var TParser, retColon = true): PNode =
+  #| paramList = '(' identColonEquals ^* (comma/semicolon) ')'
+  #| paramListArrow = paramList? ('->' optInd typeDesc)?
+  #| paramListColon = paramList? (':' optInd typeDesc)?
   var a: PNode
   result = newNodeP(nkFormalParams, p)
   addSon(result, ast.emptyNode) # return type
-  if p.tok.tokType == tkParLe:
+  if p.tok.tokType == tkParLe and p.tok.indent < 0:
     getTok(p)
     optInd(p, result)
-    while true: 
-      case p.tok.tokType      #optInd(p, a);
+    while true:
+      case p.tok.tokType
       of tkSymbol, tkAccent: 
         a = parseIdentColonEquals(p, {withBothOptional})
       of tkParRi: 
@@ -692,21 +838,24 @@ proc parseParamList(p: var TParser, retColon = true): PNode =
       addSon(result, a)
       if p.tok.tokType notin {tkComma, tkSemicolon}: break 
       getTok(p)
-      optInd(p, a)
+      skipComment(p, a)
     optPar(p)
     eat(p, tkParRi)
   let hasRet = if retColon: p.tok.tokType == tkColon
                else: p.tok.tokType == tkOpr and IdentEq(p.tok.ident, "->")
-  if hasRet:
+  if hasRet and p.tok.indent < 0:
     getTok(p)
     optInd(p, result)
     result.sons[0] = parseTypeDesc(p)
 
 proc optPragmas(p: var TParser): PNode =
-  if p.tok.tokType == tkCurlyDotLe: result = parsePragma(p)
-  else: result = ast.emptyNode
+  if p.tok.tokType == tkCurlyDotLe and (p.tok.indent < 0 or realInd(p)):
+    result = parsePragma(p)
+  else:
+    result = ast.emptyNode
 
 proc parseDoBlock(p: var TParser): PNode =
+  #| doBlock = 'do' paramListArrow pragmas? colcom stmt
   let info = parLineInfo(p)
   getTok(p)
   let params = parseParamList(p, retColon=false)
@@ -718,26 +867,27 @@ proc parseDoBlock(p: var TParser): PNode =
                        pragmas = pragmas)
 
 proc parseDoBlocks(p: var TParser, call: PNode) =
-  while p.tok.tokType == tkDo:
+  #| doBlocks = doBlock ^* IND{=}
+  if p.tok.tokType == tkDo:
     addSon(call, parseDoBlock(p))
-    
+    while sameInd(p) and p.tok.tokType == tkDo:
+      addSon(call, parseDoBlock(p))      
+
 proc parseProcExpr(p: var TParser, isExpr: bool): PNode = 
+  #| procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)?
   # either a proc type or a anonymous proc
-  var 
-    pragmas, params: PNode
-    info: TLineInfo
-  info = parLineInfo(p)
+  let info = parLineInfo(p)
   getTok(p)
-  let hasSignature = p.tok.tokType in {tkParLe, tkColon}
-  params = parseParamList(p)
-  pragmas = optPragmas(p)
+  let hasSignature = p.tok.tokType in {tkParLe, tkColon} and p.tok.indent < 0
+  let params = parseParamList(p)
+  let pragmas = optPragmas(p)
   if p.tok.tokType == tkEquals and isExpr: 
     getTok(p)
     skipComment(p, result)
     result = newProcNode(nkLambda, info, parseStmt(p),
                          params = params,
                          pragmas = pragmas)
-  else: 
+  else:
     result = newNodeI(nkProcTy, info)
     if hasSignature:
       addSon(result, params)
@@ -752,7 +902,8 @@ proc isExprStart(p: TParser): bool =
     result = true
   else: result = false
   
-proc parseTypeDescKAux(p: var TParser, kind: TNodeKind, mode: TPrimaryMode): PNode = 
+proc parseTypeDescKAux(p: var TParser, kind: TNodeKind, 
+                       mode: TPrimaryMode): PNode = 
   result = newNodeP(kind, p)
   getTok(p)
   optInd(p, result)
@@ -760,25 +911,30 @@ proc parseTypeDescKAux(p: var TParser, kind: TNodeKind, mode: TPrimaryMode): PNo
     addSon(result, primary(p, mode))
 
 proc parseExpr(p: var TParser): PNode = 
-  #
-  #expr ::= lowestExpr
-  #     | 'if' expr ':' expr ('elif' expr ':' expr)* 'else' ':' expr
-  #     | 'when' expr ':' expr ('elif' expr ':' expr)* 'else' ':' expr
-  #
+  #| expr = (ifExpr
+  #|       | whenExpr
+  #|       | caseExpr
+  #|       | tryStmt)
+  #|       / simpleExpr
   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 tkTry: result = parseTry(p)
+  of tkTry: result = parseTry(p)
+  else: result = simpleExpr(p)
 
 proc parseObject(p: var TParser): PNode
 proc parseDistinct(p: var TParser): PNode
 proc parseEnum(p: var TParser): PNode
 
 proc primary(p: var TParser, mode: TPrimaryMode): PNode = 
-  # prefix operator?
+  #| typeKeyw = 'var' | 'ref' | 'ptr' | 'shared' | 'type' | 'tuple'
+  #|          | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum'
+  #| primary = typeKeyw typeDescK
+  #|         /  prefixOperator* identOrLiteral primarySuffix*
+  #|         / 'addr' primary
+  #|         / 'static' primary
+  #|         / 'bind' primary
   if isOperator(p.tok):
     let isSigil = IsSigilLike(p.tok)
     result = newNodeP(nkPrefix, p)
@@ -831,30 +987,48 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
       getTok(p)
   of tkAddr:
     result = newNodeP(nkAddr, p)
-    getTok(p)
+    getTokNoInd(p)
     addSon(result, primary(p, pmNormal))
   of tkStatic:
     result = newNodeP(nkStaticExpr, p)
-    getTok(p)
+    getTokNoInd(p)
     addSon(result, primary(p, pmNormal))
-  of tkBind: 
+  of tkBind:
     result = newNodeP(nkBind, p)
     getTok(p)
     optInd(p, result)
     addSon(result, primary(p, pmNormal))
   else:
-    result = identOrLiteral(p)
+    result = identOrLiteral(p, mode)
     if mode != pmSkipSuffix:
       result = primarySuffix(p, result)
 
-proc parseTypeDesc(p: var TParser): PNode = 
-  result = lowestExpr(p, pmTypeDesc)
+proc parseTypeDesc(p: var TParser): PNode =
+  #| typeDesc = simpleExpr
+  result = simpleExpr(p, pmTypeDesc)
 
 proc parseTypeDefAux(p: var TParser): PNode = 
-  result = lowestExpr(p, pmTypeDef)
+  #| typeDefAux = simpleExpr
+  result = simpleExpr(p, pmTypeDef)
+
+proc makeCall(n: PNode): PNode =
+  if n.kind in nkCallKinds:
+    result = n
+  else:
+    result = newNodeI(nkCall, n.info)
+    result.add n
 
 proc parseExprStmt(p: var TParser): PNode = 
-  var a = lowestExpr(p)
+  #| exprStmt = simpleExpr
+  #|          (( '=' optInd expr )
+  #|          / ( expr ^+ comma
+  #|              doBlocks
+  #|               / ':' stmt? ( IND{=} 'of' exprList ':' stmt 
+  #|                           | IND{=} 'elif' expr ':' stmt
+  #|                           | IND{=} 'except' exprList ':' stmt
+  #|                           | IND{=} 'else' ':' stmt )*
+  #|            ))?
+  var a = simpleExpr(p)
   if p.tok.tokType == tkEquals: 
     getTok(p)
     optInd(p, result)
@@ -863,33 +1037,31 @@ proc parseExprStmt(p: var TParser): PNode =
     addSon(result, a)
     addSon(result, b)
   else:
-    var call = if a.kind == nkCall: a
-               else: newNode(nkCommand, a.info, @[a])
-    while true:
-      if not isExprStart(p): break 
-      var e = parseExpr(p)
-      addSon(call, e)
-      if p.tok.tokType != tkComma: break 
-      getTok(p)
-      optInd(p, a)
-    if p.tok.tokType == tkDo:
-      parseDoBlocks(p, call)
-      return    
-    result = if call.sonsLen <= 1: a
-             else: call
-    if p.tok.tokType == tkColon:
-      result = call
+    if p.tok.indent < 0 and isExprStart(p):
+      result = newNode(nkCommand, a.info, @[a])
+      while true:
+        var e = parseExpr(p)
+        addSon(result, e)
+        if p.tok.tokType != tkComma: break 
+        getTok(p)
+        optInd(p, result)
+    else:
+      result = a
+    if p.tok.tokType == tkDo and p.tok.indent < 0:
+      result = makeCall(result)
+      parseDoBlocks(p, result)
+      return result
+    if p.tok.tokType == tkColon and p.tok.indent < 0:
+      result = makeCall(result)
       getTok(p)
       skipComment(p, result)
-      if p.tok.tokType == tkSad: getTok(p)
       if p.tok.TokType notin {tkOf, tkElif, tkElse, tkExcept}:
         let body = parseStmt(p)
         addSon(result, newProcNode(nkDo, body.info, body))
-      while true:
-        if p.tok.tokType == tkSad: getTok(p)
+      while sameInd(p):
         var b: PNode
         case p.tok.tokType
-        of tkOf: 
+        of tkOf:
           b = newNodeP(nkOfBranch, p)
           exprList(p, tkColon, b)
         of tkElif: 
@@ -900,7 +1072,7 @@ proc parseExprStmt(p: var TParser): PNode =
           eat(p, tkColon)
         of tkExcept: 
           b = newNodeP(nkExceptBranch, p)
-          qualifiedIdentListAux(p, tkColon, b)
+          exprList(p, tkColon, b)
           skipComment(p, b)
         of tkElse: 
           b = newNodeP(nkElse, p)
@@ -912,6 +1084,9 @@ proc parseExprStmt(p: var TParser): PNode =
         if b.kind == nkElse: break
 
 proc parseImport(p: var TParser, kind: TNodeKind): PNode =
+  #| importStmt = 'import' optInd expr
+  #|               ((comma expr)*
+  #|               / 'except' optInd (expr ^+ comma))
   result = newNodeP(kind, p)
   getTok(p)                   # skip `import` or `export`
   optInd(p, result)
@@ -922,29 +1097,33 @@ proc parseImport(p: var TParser, kind: TNodeKind): PNode =
       result.kind = succ(kind)
     getTok(p)
     optInd(p, result)
-    while p.tok.tokType notin {tkEof, tkSad, tkDed}:
+    while true:
+      # was: while p.tok.tokType notin {tkEof, tkSad, tkDed}:
       a = parseExpr(p)
       if a.kind == nkEmpty: break 
       addSon(result, a)
       if p.tok.tokType != tkComma: break 
       getTok(p)
       optInd(p, a)
-  expectNl(p)
+  #expectNl(p)
 
 proc parseIncludeStmt(p: var TParser): PNode =
+  #| includeStmt = 'include' optInd expr ^+ comma
   result = newNodeP(nkIncludeStmt, p)
   getTok(p)                   # skip `import` or `include`
   optInd(p, result)
-  while p.tok.tokType notin {tkEof, tkSad, tkDed}:
+  while true:
+    # was: while p.tok.tokType notin {tkEof, tkSad, tkDed}:
     var a = parseExpr(p)
     if a.kind == nkEmpty: break
     addSon(result, a)
     if p.tok.tokType != tkComma: break 
     getTok(p)
     optInd(p, a)
-  expectNl(p)
+  #expectNl(p)
 
-proc parseFromStmt(p: var TParser): PNode = 
+proc parseFromStmt(p: var TParser): PNode =
+  #| fromStmt = 'from' expr 'import' optInd expr (comma expr)*
   result = newNodeP(nkFromStmt, p)
   getTok(p)                   # skip `from`
   optInd(p, result)
@@ -952,38 +1131,41 @@ proc parseFromStmt(p: var TParser): PNode =
   addSon(result, a)           #optInd(p, a);
   eat(p, tkImport)
   optInd(p, result)
-  while p.tok.tokType notin {tkEof, tkSad, tkDed}:
+  while true:
+    # p.tok.tokType notin {tkEof, tkSad, tkDed}:
     a = parseExpr(p)
     if a.kind == nkEmpty: break
     addSon(result, a)
     if p.tok.tokType != tkComma: break 
     getTok(p)
     optInd(p, a)
-  expectNl(p)
+  #expectNl(p)
 
 proc parseReturnOrRaise(p: var TParser, kind: TNodeKind): PNode = 
+  #| returnStmt = 'return' optInd expr?
+  #| raiseStmt = 'raise' optInd expr?
+  #| yieldStmt = 'yield' optInd expr?
+  #| discardStmt = 'discard' optInd expr?
+  #| breakStmt = 'break' optInd expr?
+  #| continueStmt = 'break' optInd expr?
   result = newNodeP(kind, p)
   getTok(p)
-  optInd(p, result)
-  case p.tok.tokType
-  of tkEof, tkSad, tkDed: addSon(result, ast.emptyNode)
-  else: addSon(result, parseExpr(p))
-  
-proc parseYieldOrDiscard(p: var TParser, kind: TNodeKind): PNode = 
-  result = newNodeP(kind, p)
-  getTok(p)
-  optInd(p, result)
-  addSon(result, parseExpr(p))
-
-proc parseBreakOrContinue(p: var TParser, kind: TNodeKind): PNode =
-  result = newNodeP(kind, p)
-  getTok(p)
-  optInd(p, result)
-  case p.tok.tokType
-  of tkEof, tkSad, tkDed: addSon(result, ast.emptyNode)
-  else: addSon(result, parseSymbol(p))
+  if p.tok.tokType == tkComment:
+    skipComment(p, result)
+    addSon(result, ast.emptyNode)
+  elif p.tok.indent >= 0 and p.tok.indent <= p.currInd or
+      p.tok.tokType == tkEof:
+    # NL terminates:
+    addSon(result, ast.emptyNode)
+  else:
+    addSon(result, parseExpr(p))
 
 proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode =
+  #| condStmt = expr colcom stmt COMMENT?
+  #|            (IND{=} 'elif' expr colcom stmt)*
+  #|            (IND{=} 'else' colcom stmt)?
+  #| ifStmt = 'if' condStmt
+  #| whenStmt = 'when' condStmt
   result = newNodeP(kind, p)
   while true:
     getTok(p)                 # skip `if`, `when`, `elif`
@@ -995,8 +1177,8 @@ proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode =
     addSon(branch, parseStmt(p))
     skipComment(p, branch)
     addSon(result, branch)
-    if p.tok.tokType != tkElif: break
-  if p.tok.tokType == tkElse:
+    if p.tok.tokType != tkElif or not sameOrNoInd(p): break
+  if p.tok.tokType == tkElse and sameOrNoInd(p):
     var branch = newNodeP(nkElse, p)
     eat(p, tkElse)
     eat(p, tkColon)
@@ -1004,17 +1186,24 @@ proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode =
     addSon(branch, parseStmt(p))
     addSon(result, branch)
 
-proc parseWhile(p: var TParser): PNode = 
+proc parseWhile(p: var TParser): PNode =
+  #| whileStmt = 'while' expr colcom stmt
   result = newNodeP(nkWhileStmt, p)
   getTok(p)
   optInd(p, result)
   addSon(result, parseExpr(p))
-  eat(p, tkColon)
-  skipComment(p, result)
+  colcom(p, result)
   addSon(result, parseStmt(p))
 
-proc parseCase(p: var TParser): PNode = 
-  var 
+proc parseCase(p: var TParser): PNode =
+  #| ofBranch = 'of' exprList colcom stmt
+  #| ofBranches = ofBranch (IND{=} ofBranch)*
+  #|                       (IND{=} 'elif' expr colcom stmt)*
+  #|                       (IND{=} 'else' colcom stmt)?
+  #| caseStmt = 'case' expr ':'? COMMENT?
+  #|             (IND{>} ofBranches DED
+  #|             | IND{=} ofBranches)
+  var
     b: PNode
     inElif= false
     wasIndented = false
@@ -1024,57 +1213,57 @@ proc parseCase(p: var TParser): PNode =
   if p.tok.tokType == tkColon: getTok(p)
   skipComment(p, result)
   
-  if p.tok.tokType == tkInd:
-    pushInd(p.lex, p.tok.indent)
-    getTok(p)
+  let oldInd = p.currInd
+  if realInd(p):
+    p.currInd = p.tok.indent
     wasIndented = true
   
-  while true: 
-    if p.tok.tokType == tkSad: getTok(p)
+  while sameInd(p):
     case p.tok.tokType
-    of tkOf: 
-      if inElif: break 
+    of tkOf:
+      if inElif: break
       b = newNodeP(nkOfBranch, p)
       exprList(p, tkColon, b)
-    of tkElif: 
+    of tkElif:
       inElif = true
       b = newNodeP(nkElifBranch, p)
       getTok(p)
       optInd(p, b)
       addSon(b, parseExpr(p))
       eat(p, tkColon)
-    of tkElse: 
+    of tkElse:
       b = newNodeP(nkElse, p)
       getTok(p)
       eat(p, tkColon)
-    else: break 
+    else: break
     skipComment(p, b)
     addSon(b, parseStmt(p))
     addSon(result, b)
     if b.kind == nkElse: break
   
   if wasIndented:
-    if p.tok.tokType != tkEof: eat(p, tkDed)
-    popInd(p.lex)
+    p.currInd = oldInd
     
-proc parseTry(p: var TParser): PNode = 
+proc parseTry(p: var TParser): PNode =
+  #| tryStmt = 'try' colcom stmt &(IND{=}? 'except'|'finally')
+  #|            (IND{=}? 'except' exprList colcom stmt)*
+  #|            (IND{=}? 'finally' colcom stmt)?
   result = newNodeP(nkTryStmt, p)
   getTok(p)
   eat(p, tkColon)
   skipComment(p, result)
   addSon(result, parseStmt(p))
   var b: PNode = nil
-  while true: 
-    if p.tok.tokType == tkSad: getTok(p)
+  while sameOrNoInd(p):
     case p.tok.tokType
     of tkExcept: 
       b = newNodeP(nkExceptBranch, p)
-      qualifiedIdentListAux(p, tkColon, b)
+      exprList(p, tkColon, b)
     of tkFinally: 
       b = newNodeP(nkFinally, p)
-      getTok(p)
+      getTokNoInd(p)
       eat(p, tkColon)
-    else: break 
+    else: break
     skipComment(p, b)
     addSon(b, parseStmt(p))
     addSon(result, b)
@@ -1082,52 +1271,48 @@ proc parseTry(p: var TParser): PNode =
   if b == nil: parMessage(p, errTokenExpected, "except")
 
 proc parseExceptBlock(p: var TParser, kind: TNodeKind): PNode =
+  #| exceptBlock = 'except' colcom stmt
   result = newNodeP(kind, p)
-  getTok(p)
-  eat(p, tkColon)
-  skipComment(p, result)
+  getTokNoInd(p)
+  colcom(p, result)
   addSon(result, parseStmt(p))
 
-proc parseFor(p: var TParser): PNode = 
+proc parseFor(p: var TParser): PNode =
+  #| forStmt = 'for' symbol (comma symbol)* 'in' expr colcom stmt
   result = newNodeP(nkForStmt, p)
-  getTok(p)
-  optInd(p, result)
+  getTokNoInd(p)
   var a = parseSymbol(p)
   addSon(result, a)
-  while p.tok.tokType == tkComma: 
+  while p.tok.tokType == tkComma:
     getTok(p)
     optInd(p, a)
     a = parseSymbol(p)
     addSon(result, a)
   eat(p, tkIn)
   addSon(result, parseExpr(p))
-  eat(p, tkColon)
-  skipComment(p, result)
+  colcom(p, result)
   addSon(result, parseStmt(p))
 
 proc parseBlock(p: var TParser): PNode = 
+  #| blockStmt = 'block' symbol? colcom stmt
   result = newNodeP(nkBlockStmt, p)
-  getTok(p)
-  optInd(p, result)
-  case p.tok.tokType
-  of tkEof, tkSad, tkDed, tkColon: addSon(result, ast.emptyNode)
+  getTokNoInd(p)
+  if p.tok.tokType == tkColon: addSon(result, ast.emptyNode)
   else: addSon(result, parseSymbol(p))
-  eat(p, tkColon)
-  skipComment(p, result)
+  colcom(p, result)
   addSon(result, parseStmt(p))
 
 proc parseStatic(p: var TParser): PNode =
+  #| staticStmt = 'static' colcom stmt
   result = newNodeP(nkStaticStmt, p)
-  getTok(p)
-  optInd(p, result)
-  eat(p, tkColon)
-  skipComment(p, result)
+  getTokNoInd(p)
+  colcom(p, result)
   addSon(result, parseStmt(p))
   
-proc parseAsm(p: var TParser): PNode = 
+proc parseAsm(p: var TParser): PNode =
+  #| asmStmt = 'asm' pragma? (STR_LIT | RSTR_LIT | TRIPLE_STR_LIT)
   result = newNodeP(nkAsmStmt, p)
-  getTok(p)
-  optInd(p, result)
+  getTokNoInd(p)
   if p.tok.tokType == tkCurlyDotLe: addSon(result, parsePragma(p))
   else: addSon(result, ast.emptyNode)
   case p.tok.tokType
@@ -1141,7 +1326,8 @@ proc parseAsm(p: var TParser): PNode =
     return 
   getTok(p)
 
-proc parseGenericParam(p: var TParser): PNode = 
+proc parseGenericParam(p: var TParser): PNode =
+  #| genericParam = symbol (comma symbol)* (colon expr)? ('=' optInd expr)?
   var a: PNode
   result = newNodeP(nkIdentDefs, p)
   while true: 
@@ -1168,89 +1354,96 @@ proc parseGenericParam(p: var TParser): PNode =
     addSon(result, ast.emptyNode)
 
 proc parseGenericParamList(p: var TParser): PNode = 
+  #| genericParamList = '[' optInd
+  #|   genericParam ^* (comma/semicolon) optPar ']'
   result = newNodeP(nkGenericParams, p)
   getTok(p)
   optInd(p, result)
-  while (p.tok.tokType == tkSymbol) or (p.tok.tokType == tkAccent): 
+  while p.tok.tokType in {tkSymbol, tkAccent}: 
     var a = parseGenericParam(p)
     addSon(result, a)
     if p.tok.tokType notin {tkComma, tkSemicolon}: break 
     getTok(p)
-    optInd(p, a)
+    skipComment(p, a)
   optPar(p)
   eat(p, tkBracketRi)
 
 proc parsePattern(p: var TParser): PNode =
+  #| pattern = '{' stmt '}'
   eat(p, tkCurlyLe)
   result = parseStmt(p)
   eat(p, tkCurlyRi)
 
+proc validInd(p: var TParser): bool =
+  result = p.tok.indent < 0 or p.tok.indent > p.currInd
+
 proc parseRoutine(p: var TParser, kind: TNodeKind): PNode = 
+  #| indAndComment = (IND{>} COMMENT)? | COMMENT?
+  #| routine = optInd identVis pattern? genericParamList?
+  #|   paramListColon pragma? ('=' COMMENT? stmt)? indAndComment
   result = newNodeP(kind, p)
   getTok(p)
   optInd(p, result)
   addSon(result, identVis(p))
-  if p.tok.tokType == tkCurlyLe: addSon(result, parsePattern(p))
+  if p.tok.tokType == tkCurlyLe and p.validInd: addSon(result, p.parsePattern)
   else: addSon(result, ast.emptyNode)
-  if p.tok.tokType == tkBracketLe: addSon(result, parseGenericParamList(p))
-  else: addSon(result, ast.emptyNode)
-  addSon(result, parseParamList(p))
-  if p.tok.tokType == tkCurlyDotLe: addSon(result, parsePragma(p))
+  if p.tok.tokType == tkBracketLe and p.validInd:
+    result.add(p.parseGenericParamList)
+  else:
+    addSon(result, ast.emptyNode)
+  addSon(result, p.parseParamList)
+  if p.tok.tokType == tkCurlyDotLe and p.validInd: addSon(result, p.parsePragma)
   else: addSon(result, ast.emptyNode)
   # empty exception tracking:
   addSon(result, ast.emptyNode)
-  if p.tok.tokType == tkEquals: 
+  if p.tok.tokType == tkEquals and p.validInd: 
     getTok(p)
     skipComment(p, result)
     addSon(result, parseStmt(p))
-  else: 
+  else:
     addSon(result, ast.emptyNode)
-  indAndComment(p, result)    # XXX: document this in the grammar!
+  indAndComment(p, result)
   
 proc newCommentStmt(p: var TParser): PNode =
+  #| commentStmt = COMMENT
   result = newNodeP(nkCommentStmt, p)
   result.info.line = result.info.line - int16(1) - int16(p.tok.iNumber)
+  result.comment = p.tok.literal
+  getTok(p)
 
-type 
+type
   TDefParser = proc (p: var TParser): PNode {.nimcall.}
 
-proc parseSection(p: var TParser, kind: TNodeKind, 
-                  defparser: TDefParser): PNode = 
+proc parseSection(p: var TParser, kind: TNodeKind,
+                  defparser: TDefParser): PNode =
+  #| section(p) = COMMENT? p / (IND{>} (p / COMMENT)^+IND{=} DED)
   result = newNodeP(kind, p)
   getTok(p)
   skipComment(p, result)
-  case p.tok.tokType
-  of tkInd: 
-    pushInd(p.lex, p.tok.indent)
-    getTok(p)
-    skipComment(p, result)
-    while true: 
-      case p.tok.tokType
-      of tkSad: 
-        getTok(p)
-      of tkSymbol, tkAccent: 
-        var a = defparser(p)
-        skipComment(p, a)
-        addSon(result, a)
-      of tkDed: 
-        getTok(p)
-        break 
-      of tkEof: 
-        break                 # BUGFIX
-      of tkComment: 
-        var a = newCommentStmt(p)
-        skipComment(p, a)
-        addSon(result, a)
-      else: 
-        parMessage(p, errIdentifierExpected, p.tok)
-        break 
-    popInd(p.lex)
-  of tkSymbol, tkAccent, tkParLe: 
+  if realInd(p):
+    withInd(p):
+      skipComment(p, result)
+      while sameInd(p):
+        case p.tok.tokType
+        of tkSymbol, tkAccent: 
+          var a = defparser(p)
+          skipComment(p, a)
+          addSon(result, a)
+        of tkComment: 
+          var a = newCommentStmt(p)
+          addSon(result, a)
+        else: 
+          parMessage(p, errIdentifierExpected, p.tok)
+          break
+    if result.len == 0: parMessage(p, errIdentifierExpected, p.tok)
+  elif p.tok.tokType in {tkSymbol, tkAccent, tkParLe} and p.tok.indent < 0:
     # tkParLe is allowed for ``var (x, y) = ...`` tuple parsing
     addSon(result, defparser(p))
-  else: parMessage(p, errIdentifierExpected, p.tok)
+  else: 
+    parMessage(p, errIdentifierExpected, p.tok)
   
-proc parseConstant(p: var TParser): PNode = 
+proc parseConstant(p: var TParser): PNode =
+  #| constant = identWithPragma (colon typedesc)? '=' optInd expr indAndComment
   result = newNodeP(nkConstDef, p)
   addSon(result, identWithPragma(p))
   if p.tok.tokType == tkColon: 
@@ -1262,60 +1455,73 @@ proc parseConstant(p: var TParser): PNode =
   eat(p, tkEquals)
   optInd(p, result)
   addSon(result, parseExpr(p))
-  indAndComment(p, result)    # XXX: special extension!
+  indAndComment(p, result)
   
 proc parseEnum(p: var TParser): PNode = 
-  var a, b: PNode
+  #| enum = 'enum' optInd (symbol optInd ('=' optInd expr COMMENT?)? comma?)+
   result = newNodeP(nkEnumTy, p)
-  a = nil
   getTok(p)
   addSon(result, ast.emptyNode)
   optInd(p, result)
-  while true: 
-    case p.tok.tokType
-    of tkEof, tkSad, tkDed: break 
-    else: a = parseSymbol(p)
-    optInd(p, a)
-    if p.tok.tokType == tkEquals: 
+  while true:
+    var a = parseSymbol(p)
+    if p.tok.indent >= 0 and p.tok.indent <= p.currInd:
+      add(result, a)
+      break
+    if p.tok.tokType == tkEquals and p.tok.indent < 0: 
       getTok(p)
       optInd(p, a)
-      b = a
+      var b = a
       a = newNodeP(nkEnumFieldDef, p)
       addSon(a, b)
       addSon(a, parseExpr(p))
       skipComment(p, a)
-    if p.tok.tokType == tkComma: 
+    if p.tok.tokType == tkComma and p.tok.indent < 0:
       getTok(p)
-      optInd(p, a)
+      rawSkipComment(p, a)
+    else:
+      skipComment(p, a)
     addSon(result, a)
+    if p.tok.indent >= 0 and p.tok.indent <= p.currInd or
+        p.tok.tokType == tkEof:
+      break
   if result.len <= 1:
     lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok))
 
 proc parseObjectPart(p: var TParser): PNode
 proc parseObjectWhen(p: var TParser): PNode = 
+  #| objectWhen = 'when' expr colcom objectPart COMMENT?
+  #|             ('elif' expr colcom objectPart COMMENT?)*
+  #|             ('else' colcom objectPart COMMENT?)?
   result = newNodeP(nkRecWhen, p)
-  while true: 
+  while sameInd(p): 
     getTok(p)                 # skip `when`, `elif`
     var branch = newNodeP(nkElifBranch, p)
     optInd(p, branch)
     addSon(branch, parseExpr(p))
-    eat(p, tkColon)
-    skipComment(p, branch)
+    colcom(p, branch)
     addSon(branch, parseObjectPart(p))
     skipComment(p, branch)
     addSon(result, branch)
-    if p.tok.tokType != tkElif: break 
-  if p.tok.tokType == tkElse: 
+    if p.tok.tokType != tkElif: break
+  if p.tok.tokType == tkElse and sameInd(p):
     var branch = newNodeP(nkElse, p)
     eat(p, tkElse)
-    eat(p, tkColon)
-    skipComment(p, branch)
+    colcom(p, branch)
     addSon(branch, parseObjectPart(p))
+    skipComment(p, branch)
     addSon(result, branch)
 
 proc parseObjectCase(p: var TParser): PNode = 
+  #| objectBranch = 'of' exprList colcom objectPart
+  #| objectBranches = objectBranch (IND{=} objectBranch)*
+  #|                       (IND{=} 'elif' expr colcom objectPart)*
+  #|                       (IND{=} 'else' colcom objectPart)?
+  #| objectCase = 'case' identWithPragma ':' typeDesc ':'? COMMENT?
+  #|             (IND{>} objectBranches DED
+  #|             | IND{=} objectBranches)
   result = newNodeP(nkRecCase, p)
-  getTok(p)
+  getTokNoInd(p)
   var a = newNodeP(nkIdentDefs, p)
   addSon(a, identWithPragma(p))
   eat(p, tkColon)
@@ -1325,12 +1531,11 @@ proc parseObjectCase(p: var TParser): PNode =
   if p.tok.tokType == tkColon: getTok(p)
   skipComment(p, result)
   var wasIndented = false
-  if p.tok.tokType == tkInd:
-    pushInd(p.lex, p.tok.indent)
-    getTok(p)
+  let oldInd = p.currInd
+  if realInd(p):
+    p.currInd = p.tok.indent
     wasIndented = true
-  while true: 
-    if p.tok.tokType == tkSad: getTok(p)
+  while sameInd(p):
     var b: PNode
     case p.tok.tokType
     of tkOf: 
@@ -1348,72 +1553,79 @@ proc parseObjectCase(p: var TParser): PNode =
       fields = newNodeP(nkNilLit, p) # don't break further semantic checking
     addSon(b, fields)
     addSon(result, b)
-    if b.kind == nkElse: break 
+    if b.kind == nkElse: break
   if wasIndented:
-    eat(p, tkDed)
-    popInd(p.lex)
+    p.currInd = oldInd
   
 proc parseObjectPart(p: var TParser): PNode = 
-  case p.tok.tokType
-  of tkInd: 
+  #| objectPart = IND{>} objectPart^+IND{=} DED
+  #|            / objectWhen / objectCase / 'nil' / declColonEquals
+  if realInd(p):
     result = newNodeP(nkRecList, p)
-    pushInd(p.lex, p.tok.indent)
-    getTok(p)
-    skipComment(p, result)
-    while true: 
-      case p.tok.tokType
-      of tkSad: 
-        getTok(p)
-      of tkCase, tkWhen, tkSymbol, tkAccent, tkNil: 
-        addSon(result, parseObjectPart(p))
-      of tkDed: 
-        getTok(p)
-        break 
-      of tkEof: 
-        break 
-      else: 
-        parMessage(p, errIdentifierExpected, p.tok)
-        break 
-    popInd(p.lex)
-  of tkWhen: 
-    result = parseObjectWhen(p)
-  of tkCase: 
-    result = parseObjectCase(p)
-  of tkSymbol, tkAccent: 
-    result = parseIdentColonEquals(p, {withPragma})
-    skipComment(p, result)
-  of tkNil: 
-    result = newNodeP(nkNilLit, p)
-    getTok(p)
-  else: result = ast.emptyNode
+    withInd(p):
+      rawSkipComment(p, result)
+      while sameInd(p):
+        case p.tok.tokType
+        of tkCase, tkWhen, tkSymbol, tkAccent, tkNil: 
+          addSon(result, parseObjectPart(p))
+        else:
+          parMessage(p, errIdentifierExpected, p.tok)
+          break
+  else:
+    case p.tok.tokType
+    of tkWhen:
+      result = parseObjectWhen(p)
+    of tkCase:
+      result = parseObjectCase(p)
+    of tkSymbol, tkAccent:
+      result = parseIdentColonEquals(p, {withPragma})
+      skipComment(p, result)
+    of tkNil:
+      result = newNodeP(nkNilLit, p)
+      getTok(p)
+    else:
+      result = ast.emptyNode
   
 proc parseObject(p: var TParser): PNode = 
+  #| object = 'object' pragma? ('of' typeDesc)? COMMENT? objectPart
   result = newNodeP(nkObjectTy, p)
   getTok(p)
-  if p.tok.tokType == tkCurlyDotLe: addSon(result, parsePragma(p))
-  else: addSon(result, ast.emptyNode)
-  if p.tok.tokType == tkOf: 
+  if p.tok.tokType == tkCurlyDotLe and p.validInd:
+    addSon(result, parsePragma(p))
+  else:
+    addSon(result, ast.emptyNode)
+  if p.tok.tokType == tkOf and p.tok.indent < 0:
     var a = newNodeP(nkOfInherit, p)
     getTok(p)
     addSon(a, parseTypeDesc(p))
     addSon(result, a)
   else: 
     addSon(result, ast.emptyNode)
-  skipComment(p, result)
+  if p.tok.tokType == tkComment:
+    skipComment(p, result)
+  # an initial IND{>} HAS to follow:
+  if not realInd(p):
+    addSon(result, emptyNode)
+    return
   addSon(result, parseObjectPart(p))
 
 proc parseDistinct(p: var TParser): PNode = 
+  #| distinct = 'distinct' optInd typeDesc
   result = newNodeP(nkDistinctTy, p)
   getTok(p)
   optInd(p, result)
   addSon(result, parseTypeDesc(p))
 
 proc parseTypeDef(p: var TParser): PNode = 
+  #| typeDef = identWithPragma genericParamList? '=' optInd typeDefAux
+  #|             indAndComment?
   result = newNodeP(nkTypeDef, p)
   addSon(result, identWithPragma(p))
-  if p.tok.tokType == tkBracketLe: addSon(result, parseGenericParamList(p))
-  else: addSon(result, ast.emptyNode)
-  if p.tok.tokType == tkEquals: 
+  if p.tok.tokType == tkBracketLe and p.validInd:
+    addSon(result, parseGenericParamList(p))
+  else:
+    addSon(result, ast.emptyNode)
+  if p.tok.tokType == tkEquals:
     getTok(p)
     optInd(p, result)
     addSon(result, parseTypeDefAux(p))
@@ -1421,16 +1633,17 @@ proc parseTypeDef(p: var TParser): PNode =
     addSon(result, ast.emptyNode)
   indAndComment(p, result)    # special extension!
   
-proc parseVarTuple(p: var TParser): PNode = 
+proc parseVarTuple(p: var TParser): PNode =
+  #| varTuple = '(' optInd identWithPragma ^+ comma optPar ')' '=' optInd expr
   result = newNodeP(nkVarTuple, p)
   getTok(p)                   # skip '('
   optInd(p, result)
-  while (p.tok.tokType == tkSymbol) or (p.tok.tokType == tkAccent): 
+  while p.tok.tokType in {tkSymbol, tkAccent}: 
     var a = identWithPragma(p)
     addSon(result, a)
     if p.tok.tokType != tkComma: break 
     getTok(p)
-    optInd(p, a)
+    skipComment(p, a)
   addSon(result, ast.emptyNode)         # no type desc
   optPar(p)
   eat(p, tkParRi)
@@ -1438,12 +1651,15 @@ proc parseVarTuple(p: var TParser): PNode =
   optInd(p, result)
   addSon(result, parseExpr(p))
 
-proc parseVariable(p: var TParser): PNode = 
+proc parseVariable(p: var TParser): PNode =
+  #| variable = (varTuple / identColonEquals) indAndComment
   if p.tok.tokType == tkParLe: result = parseVarTuple(p)
   else: result = parseIdentColonEquals(p, {withPragma})
-  indAndComment(p, result)    # special extension!
+  indAndComment(p, result)
   
 proc parseBind(p: var TParser, k: TNodeKind): PNode =
+  #| bindStmt = 'bind' optInd qualifiedIdent ^+ comma
+  #| mixinStmt = 'mixin' optInd qualifiedIdent ^+ comma
   result = newNodeP(k, p)
   getTok(p)
   optInd(p, result)
@@ -1452,26 +1668,32 @@ proc parseBind(p: var TParser, k: TNodeKind): PNode =
     addSon(result, a)
     if p.tok.tokType != tkComma: break
     getTok(p)
-    optInd(p, a)  
-  expectNl(p)
+    optInd(p, a)
+  #expectNl(p)
   
 proc parseStmtPragma(p: var TParser): PNode =
+  #| pragmaStmt = pragma (':' COMMENT? stmt)?
   result = parsePragma(p)
-  if p.tok.tokType == tkColon:
+  if p.tok.tokType == tkColon and p.tok.indent < 0:
     let a = result
     result = newNodeI(nkPragmaBlock, a.info)
     getTok(p)
+    skipComment(p, result)
     result.add a
     result.add parseStmt(p)
 
 proc simpleStmt(p: var TParser): PNode = 
+  #| simpleStmt = ((returnStmt | raiseStmt | yieldStmt | discardStmt | breakStmt
+  #|            | continueStmt | pragmaStmt | importStmt | exportStmt | fromStmt
+  #|            | includeStmt | commentStmt) / exprStmt) COMMENT?
+  #|
   case p.tok.tokType
   of tkReturn: result = parseReturnOrRaise(p, nkReturnStmt)
   of tkRaise: result = parseReturnOrRaise(p, nkRaiseStmt)
   of tkYield: result = parseReturnOrRaise(p, nkYieldStmt)
   of tkDiscard: result = parseReturnOrRaise(p, nkDiscardStmt)
-  of tkBreak: result = parseBreakOrContinue(p, nkBreakStmt)
-  of tkContinue: result = parseBreakOrContinue(p, nkContinueStmt)
+  of tkBreak: result = parseReturnOrRaise(p, nkBreakStmt)
+  of tkContinue: result = parseReturnOrRaise(p, nkContinueStmt)
   of tkCurlyDotLe: result = parseStmtPragma(p)
   of tkImport: result = parseImport(p, nkImportStmt)
   of tkExport: result = parseImport(p, nkExportStmt)
@@ -1481,9 +1703,23 @@ proc simpleStmt(p: var TParser): PNode =
   else:
     if isExprStart(p): result = parseExprStmt(p)
     else: result = ast.emptyNode
-  if result.kind != nkEmpty: skipComment(p, result)
+  if result.kind notin {nkEmpty, nkCommentStmt}: skipComment(p, result)
   
-proc complexOrSimpleStmt(p: var TParser): PNode = 
+proc complexOrSimpleStmt(p: var TParser): PNode =
+  #| complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt
+  #|                     | tryStmt | finallyStmt | exceptStmt | forStmt
+  #|                     | blockStmt | staticStmt | asmStmt
+  #|                     | 'proc' routine
+  #|                     | 'method' routine
+  #|                     | 'iterator' routine
+  #|                     | 'macro' routine
+  #|                     | 'template' routine
+  #|                     | 'converter' routine
+  #|                     | 'type' section(typeDef)
+  #|                     | 'const' section(constant)
+  #|                     | ('let' | 'var') section(variable)
+  #|                     | bindStmt | mixinStmt)
+  #|                     / simpleStmt
   case p.tok.tokType
   of tkIf: result = parseIfOrWhen(p, nkIfStmt)
   of tkWhile: result = parseWhile(p)
@@ -1510,26 +1746,31 @@ proc complexOrSimpleStmt(p: var TParser): PNode =
   of tkMixin: result = parseBind(p, nkMixinStmt)
   else: result = simpleStmt(p)
   
-proc parseStmt(p: var TParser): PNode = 
-  if p.tok.tokType == tkInd: 
+proc parseStmt(p: var TParser): PNode =
+  #| stmt = (IND{>} complexOrSimpleStmt^+(IND{=} / ';') DED)
+  #|      / simpleStmt
+  if p.tok.indent > p.currInd:
     result = newNodeP(nkStmtList, p)
-    pushInd(p.lex, p.tok.indent)
-    getTok(p)
-    while true: 
-      case p.tok.tokType
-      of tkSad, tkSemicolon: getTok(p)
-      of tkEof: break 
-      of tkDed: 
-        getTok(p)
-        break 
-      else: 
+    withInd(p):
+      while true:
+        if p.tok.indent == p.currInd:
+          nil
+        elif p.tok.tokType == tkSemicolon:
+          while p.tok.tokType == tkSemicolon: getTok(p)
+        else:
+          if p.tok.indent > p.currInd:
+            parMessage(p, errInvalidIndentation)
+          break
+        if p.tok.toktype in {tkCurlyRi, tkParRi, tkCurlyDotRi, tkBracketRi}:
+          # XXX this ensures tnamedparamanonproc still compiles;
+          # deprecate this syntax later
+          break
         var a = complexOrSimpleStmt(p)
-        if a.kind == nkEmpty:
-          # XXX this needs a proper analysis;
-          if isKeyword(p.tok.tokType): parMessage(p, errInvalidIndentation)
-          break 
-        addSon(result, a)
-    popInd(p.lex)
+        if a.kind != nkEmpty:
+          addSon(result, a)
+        else:
+          parMessage(p, errExprExpected, p.tok)
+          getTok(p)
   else:
     # the case statement is only needed for better error messages:
     case p.tok.tokType
@@ -1538,38 +1779,35 @@ proc parseStmt(p: var TParser): PNode =
       parMessage(p, errComplexStmtRequiresInd)
       result = ast.emptyNode
     else:
+      if p.tok.indent >= 0: parMessage(p, errInvalidIndentation)
       result = simpleStmt(p)
       if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
-      if p.tok.tokType == tkSemicolon: getTok(p)
-      if p.tok.tokType == tkSad: getTok(p)
+      #while p.tok.tokType == tkSemicolon: getTok(p)
   
 proc parseAll(p: var TParser): PNode = 
   result = newNodeP(nkStmtList, p)
-  while true: 
-    case p.tok.tokType
-    of tkSad: getTok(p)
-    of tkDed, tkInd: 
-      parMessage(p, errInvalidIndentation)
+  while p.tok.tokType != tkEof: 
+    var a = complexOrSimpleStmt(p)
+    if a.kind != nkEmpty: 
+      addSon(result, a)    
+    else:
+      parMessage(p, errExprExpected, p.tok)
+      # bugfix: consume a token here to prevent an endless loop:
       getTok(p)
-    of tkEof: break
-    else: 
-      var a = complexOrSimpleStmt(p)
-      if a.kind == nkEmpty: 
-        parMessage(p, errExprExpected, p.tok)
-        # bugfix: consume a token here to prevent an endless loop:
-        getTok(p)
-      addSon(result, a)
+    if p.tok.indent != 0:
+      parMessage(p, errInvalidIndentation)
 
-proc parseTopLevelStmt(p: var TParser): PNode = 
+proc parseTopLevelStmt(p: var TParser): PNode =
   result = ast.emptyNode
-  while true: 
+  while true:
+    if p.tok.indent != 0: 
+      if p.firstTok and p.tok.indent < 0: nil
+      else: parMessage(p, errInvalidIndentation)
+    p.firstTok = false
     case p.tok.tokType
-    of tkSad, tkSemicolon: getTok(p)
-    of tkDed, tkInd: 
-      parMessage(p, errInvalidIndentation)
-      getTok(p)
-    of tkEof: break 
-    else: 
+    of tkSemicolon: getTok(p)
+    of tkEof: break
+    else:
       result = complexOrSimpleStmt(p)
       if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
       break
@@ -1583,4 +1821,3 @@ proc parseString(s: string, filename: string = "", line: int = 0): PNode =
 
   result = parser.parseAll
   CloseParser(parser)
-  
diff --git a/compiler/pas2nim/paslex.nim b/compiler/pas2nim/paslex.nim
index 598a27158..94e664832 100644
--- a/compiler/pas2nim/paslex.nim
+++ b/compiler/pas2nim/paslex.nim
@@ -11,7 +11,7 @@
 # the scanner module.
 
 import
-  hashes, options, msgs, strutils, platform, idents, lexbase, llstream
+  hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream
 
 const
   MaxLineLength* = 80         # lines longer than this lead to a warning
@@ -273,8 +273,8 @@ proc getNumber10(L: var TLexer, tok: var TToken) =
 
 proc HandleCRLF(L: var TLexer, pos: int): int =
   case L.buf[pos]
-  of CR: result = lexbase.HandleCR(L, pos)
-  of LF: result = lexbase.HandleLF(L, pos)
+  of CR: result = nimlexbase.HandleCR(L, pos)
+  of LF: result = nimlexbase.HandleLF(L, pos)
   else: result = pos
 
 proc getString(L: var TLexer, tok: var TToken) =
@@ -286,7 +286,7 @@ proc getString(L: var TLexer, tok: var TToken) =
       inc(pos)
       while true:
         case buf[pos]
-        of CR, LF, lexbase.EndOfFile:
+        of CR, LF, nimlexbase.EndOfFile:
           lexMessage(L, errClosingQuoteExpected)
           break
         of '\'':
@@ -362,7 +362,7 @@ proc scanLineComment(L: var TLexer, tok: var TToken) =
   while true:
     inc(pos, 2)               # skip //
     add(tok.literal, '#')
-    while not (buf[pos] in {CR, LF, lexbase.EndOfFile}):
+    while not (buf[pos] in {CR, LF, nimlexbase.EndOfFile}):
       add(tok.literal, buf[pos])
       inc(pos)
     pos = handleCRLF(L, pos)
@@ -391,7 +391,7 @@ proc scanCurlyComment(L: var TLexer, tok: var TToken) =
     of '}':
       inc(pos)
       break
-    of lexbase.EndOfFile: lexMessage(L, errTokenExpected, "}")
+    of nimlexbase.EndOfFile: lexMessage(L, errTokenExpected, "}")
     else:
       add(tok.literal, buf[pos])
       inc(pos)
@@ -415,7 +415,7 @@ proc scanStarComment(L: var TLexer, tok: var TToken) =
         break
       else:
         add(tok.literal, '*')
-    of lexbase.EndOfFile:
+    of nimlexbase.EndOfFile:
       lexMessage(L, errTokenExpected, "*)")
     else:
       add(tok.literal, buf[pos])
@@ -561,7 +561,7 @@ proc getTok(L: var TLexer, tok: var TToken) =
       getNumber16(L, tok)
     of '%':
       getNumber2(L, tok)
-    of lexbase.EndOfFile:
+    of nimlexbase.EndOfFile:
       tok.xkind = pxEof
     else:
       tok.literal = c & ""
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index b4ef52100..d68d2e549 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -81,13 +81,13 @@ proc addTok(g: var TSrcGen, kind: TTokType, s: string) =
 
 proc addPendingNL(g: var TSrcGen) = 
   if g.pendingNL >= 0: 
-    addTok(g, tkInd, "\n" & repeatChar(g.pendingNL))
+    addTok(g, tkSpaces, "\n" & repeatChar(g.pendingNL))
     g.lineLen = g.pendingNL
     g.pendingNL = - 1
 
 proc putNL(g: var TSrcGen, indent: int) = 
   if g.pendingNL >= 0: addPendingNL(g)
-  else: addTok(g, tkInd, "\n")
+  else: addTok(g, tkSpaces, "\n")
   g.pendingNL = indent
   g.lineLen = indent
 
@@ -939,10 +939,9 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     gsub(g, n.sons[0])
     put(g, tkDotDot, "..")
     gsub(g, n.sons[1])
-  of nkDerefExpr: 
+  of nkDerefExpr:
     gsub(g, n.sons[0])
-    putWithSpace(g, tkOpr, "^") 
-    # unfortunately this requires a space, because ^. would be only one opr
+    put(g, tkOpr, "[]")
   of nkAccQuoted:
     put(g, tkAccent, "`")
     if n.len > 0: gsub(g, n.sons[0])
diff --git a/compiler/rodread.nim b/compiler/rodread.nim
index d0a63cd99..562eaebab 100644
--- a/compiler/rodread.nim
+++ b/compiler/rodread.nim
@@ -328,9 +328,6 @@ proc decodeType(r: PRodReader, info: TLineInfo): PType =
     result.align = decodeVInt(r.s, r.pos)
   else: 
     result.align = 2
-  if r.s[r.pos] == '@': 
-    inc(r.pos)
-    result.containerID = decodeVInt(r.s, r.pos)
   decodeLoc(r, result.loc, info)
   while r.s[r.pos] == '^': 
     inc(r.pos)
@@ -1012,9 +1009,6 @@ proc writeType(f: TFile; t: PType) =
   if t.align != 2:
     f.write('=')
     f.write($t.align)
-  if t.containerID != 0: 
-    f.write('@')
-    f.write($t.containerID)
   for i in countup(0, sonsLen(t) - 1): 
     if t.sons[i] == nil: 
       f.write("^()")
diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim
index b64347ba9..0221977bf 100644
--- a/compiler/rodwrite.nim
+++ b/compiler/rodwrite.nim
@@ -233,9 +233,6 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) =
   if t.align != 2: 
     add(result, '=')
     encodeVInt(t.align, result)
-  if t.containerID != 0: 
-    add(result, '@')
-    encodeVInt(t.containerID, result)
   encodeLoc(w, t.loc, result)
   for i in countup(0, sonsLen(t) - 1): 
     if t.sons[i] == nil: 
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 805af9e31..92b25b1ba 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -58,6 +58,46 @@ proc fitNode(c: PContext, formal: PType, arg: PNode): PNode =
     result = copyNode(arg)
     result.typ = formal
 
+var CommonTypeBegin = PType(kind: tyExpr)
+
+proc commonType*(x, y: PType): PType =
+  # new type relation that is used for array constructors,
+  # if expressions, etc.:
+  if x == nil: return x
+  if y == nil: return y
+  var a = skipTypes(x, {tyGenericInst})
+  var b = skipTypes(y, {tyGenericInst})
+  result = x
+  if a.kind in {tyExpr, tyNil}: result = y
+  elif b.kind in {tyExpr, tyNil}: result = x
+  elif a.kind == tyStmt: result = a
+  elif b.kind == tyStmt: result = b
+  elif a.kind == tyTypeDesc:
+    # turn any concrete typedesc into the abstract typedesc type
+    if a.sons == nil: result = a
+    else: result = newType(tyTypeDesc, a.owner)
+  elif b.kind in {tyArray, tyArrayConstr, tySet, tySequence} and 
+      a.kind == b.kind:
+    # check for seq[empty] vs. seq[int]
+    let idx = ord(b.kind in {tyArray, tyArrayConstr})
+    if a.sons[idx].kind == tyEmpty: return y
+    #elif b.sons[idx].kind == tyEmpty: return x
+  else:
+    var k = tyNone
+    if a.kind in {tyRef, tyPtr}:
+      k = a.kind
+      if b.kind != a.kind: return x
+      a = a.sons[0]
+      b = b.sons[0]
+    if a.kind == tyObject and b.kind == tyObject:
+      result = commonSuperclass(a, b)
+      # this will trigger an error later:
+      if result.isNil: return x
+      if k != tyNone:
+        let r = result
+        result = NewType(k, r.owner)
+        result.addSonSkipIntLit(r)
+
 proc isTopLevel(c: PContext): bool {.inline.} = 
   result = c.tab.tos <= 2
 
@@ -66,7 +106,7 @@ proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
 
 proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
   # like newSymS, but considers gensym'ed symbols
-  if n.kind == nkSym: 
+  if n.kind == nkSym:
     result = n.sym
     InternalAssert sfGenSym in result.flags
     InternalAssert result.kind == kind
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index 4981b64c9..afce365f9 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -230,7 +230,7 @@ proc markIndirect*(c: PContext, s: PSym) {.inline.} =
     incl(s.flags, sfAddrTaken)
     # XXX add to 'c' for global analysis
 
-proc illFormedAst*(n: PNode) = 
+proc illFormedAst*(n: PNode) =
   GlobalError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
 
 proc checkSonsLen*(n: PNode, length: int) = 
diff --git a/compiler/semdestruct.nim b/compiler/semdestruct.nim
new file mode 100644
index 000000000..2383ac649
--- /dev/null
+++ b/compiler/semdestruct.nim
@@ -0,0 +1,213 @@
+#
+#
+#           The Nimrod Compiler
+#        (c) Copyright 2013 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements destructors.
+
+
+# special marker values that indicates that we are
+# 1) AnalyzingDestructor: currently analyzing the type for destructor 
+# generation (needed for recursive types)
+# 2) DestructorIsTrivial: completed the analysis before and determined
+# that the type has a trivial destructor
+var AnalyzingDestructor, DestructorIsTrivial: PSym
+new(AnalyzingDestructor)
+new(DestructorIsTrivial)
+
+var
+  destructorName = getIdent"destroy_"
+  destructorParam = getIdent"this_"
+  destructorPragma = newIdentNode(getIdent"destructor", UnknownLineInfo())
+  rangeDestructorProc*: PSym
+
+proc instantiateDestructor(c: PContext, typ: PType): bool
+
+proc doDestructorStuff(c: PContext, s: PSym, n: PNode) =
+  let t = s.typ.sons[1].skipTypes({tyVar})
+  t.destructor = s
+  # automatically insert calls to base classes' destructors
+  if n.sons[bodyPos].kind != nkEmpty:
+    for i in countup(0, t.sonsLen - 1):
+      # when inheriting directly from object
+      # there will be a single nil son
+      if t.sons[i] == nil: continue
+      if instantiateDestructor(c, t.sons[i]):
+        n.sons[bodyPos].addSon(newNode(nkCall, t.sym.info, @[
+            useSym(t.sons[i].destructor),
+            n.sons[paramsPos][1][0]]))
+
+proc destroyField(c: PContext, field: PSym, holder: PNode): PNode =
+  if instantiateDestructor(c, field.typ):
+    result = newNode(nkCall, field.info, @[
+      useSym(field.typ.destructor),
+      newNode(nkDotExpr, field.info, @[holder, useSym(field)])])
+
+proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode =
+  var nonTrivialFields = 0
+  result = newNode(nkCaseStmt, n.info, @[])
+  # case x.kind
+  result.addSon(newNode(nkDotExpr, n.info, @[holder, n.sons[0]]))
+  for i in countup(1, n.len - 1):
+    # of A, B:
+    var caseBranch = newNode(n[i].kind, n[i].info, n[i].sons[0 .. -2])
+    let recList = n[i].lastSon
+    var destroyRecList = newNode(nkStmtList, n[i].info, @[])
+    template addField(f: expr): stmt =
+      let stmt = destroyField(c, f, holder)
+      if stmt != nil:
+        destroyRecList.addSon(stmt)
+        inc nonTrivialFields
+        
+    case recList.kind
+    of nkSym:
+      addField(recList.sym)
+    of nkRecList:
+      for j in countup(0, recList.len - 1):
+        addField(recList[j].sym)
+    else:
+      internalAssert false
+      
+    caseBranch.addSon(destroyRecList)
+    result.addSon(caseBranch)
+  # maybe no fields were destroyed?
+  if nonTrivialFields == 0:
+    result = nil
+ 
+proc generateDestructor(c: PContext, t: PType): PNode =
+  ## generate a destructor for a user-defined object or tuple type
+  ## returns nil if the destructor turns out to be trivial
+  
+  template addLine(e: expr): stmt =
+    if result == nil: result = newNode(nkStmtList)
+    result.addSon(e)
+
+  # XXX: This may be true for some C-imported types such as
+  # Tposix_spawnattr
+  if t.n == nil or t.n.sons == nil: return
+  internalAssert t.n.kind == nkRecList
+  let destructedObj = newIdentNode(destructorParam, UnknownLineInfo())
+  # call the destructods of all fields
+  for s in countup(0, t.n.sons.len - 1):
+    case t.n.sons[s].kind
+    of nkRecCase:
+      let stmt = destroyCase(c, t.n.sons[s], destructedObj)
+      if stmt != nil: addLine(stmt)
+    of nkSym:
+      let stmt = destroyField(c, t.n.sons[s].sym, destructedObj)
+      if stmt != nil: addLine(stmt)
+    else:
+      internalAssert false
+  # base classes' destructors will be automatically called by
+  # semProcAux for both auto-generated and user-defined destructors
+
+proc instantiateDestructor(c: PContext, typ: PType): bool =
+  # returns true if the type already had a user-defined
+  # destructor or if the compiler generated a default
+  # member-wise one
+  var t = skipTypes(typ, {tyConst, tyMutable})
+  
+  if t.destructor != nil:
+    # XXX: This is not entirely correct for recursive types, but we need
+    # it temporarily to hide the "destroy is already defined" problem
+    return t.destructor notin [AnalyzingDestructor, DestructorIsTrivial]
+  
+  case t.kind
+  of tySequence, tyArray, tyArrayConstr, tyOpenArray, tyVarargs:
+    if instantiateDestructor(c, t.sons[0]):
+      if rangeDestructorProc == nil:
+        rangeDestructorProc = SymtabGet(c.tab, getIdent"nimDestroyRange")
+      t.destructor = rangeDestructorProc
+      return true
+    else:
+      return false
+  of tyTuple, tyObject:
+    t.destructor = AnalyzingDestructor
+    let generated = generateDestructor(c, t)
+    if generated != nil:
+      internalAssert t.sym != nil
+      var i = t.sym.info
+      let fullDef = newNode(nkProcDef, i, @[
+        newIdentNode(destructorName, i),
+        emptyNode,
+        emptyNode,
+        newNode(nkFormalParams, i, @[
+          emptyNode,
+          newNode(nkIdentDefs, i, @[
+            newIdentNode(destructorParam, i),
+            useSym(t.sym),
+            emptyNode]),
+          ]),
+        newNode(nkPragma, i, @[destructorPragma]),
+        emptyNode,
+        generated
+        ])
+      discard semProc(c, fullDef)
+      internalAssert t.destructor != nil
+      return true
+    else:
+      t.destructor = DestructorIsTrivial
+      return false
+  else:
+    return false
+
+proc insertDestructors(c: PContext,
+                       varSection: PNode): tuple[outer, inner: PNode] =
+  # Accepts a var or let section.
+  #
+  # When a var section has variables with destructors
+  # the var section is split up and finally blocks are inserted
+  # immediately after all "destructable" vars
+  #
+  # In case there were no destrucable variables, the proc returns
+  # (nil, nil) and the enclosing stmt-list requires no modifications.
+  #
+  # Otherwise, after the try blocks are created, the rest of the enclosing
+  # stmt-list should be inserted in the most `inner` such block (corresponding
+  # to the last variable).
+  #
+  # `outer` is a statement list that should replace the original var section.
+  # It will include the new truncated var section followed by the outermost
+  # try block.
+  let totalVars = varSection.sonsLen
+  for j in countup(0, totalVars - 1):
+    let
+      varId = varSection[j][0]
+      varTyp = varId.sym.typ
+      info = varId.info
+
+    if varTyp != nil and instantiateDestructor(c, varTyp) and 
+        sfGlobal notin varId.sym.flags:
+      var tryStmt = newNodeI(nkTryStmt, info)
+
+      if j < totalVars - 1:
+        var remainingVars = newNodeI(varSection.kind, info)
+        remainingVars.sons = varSection.sons[(j+1)..(-1)]
+        let (outer, inner) = insertDestructors(c, remainingVars)
+        if outer != nil:
+          tryStmt.addSon(outer)
+          result.inner = inner
+        else:
+          result.inner = newNodeI(nkStmtList, info)
+          result.inner.addSon(remainingVars)
+          tryStmt.addSon(result.inner)
+      else:
+        result.inner = newNodeI(nkStmtList, info)
+        tryStmt.addSon(result.inner)
+
+      tryStmt.addSon(
+        newNode(nkFinally, info, @[
+          semStmt(c, newNode(nkCall, info, @[
+            useSym(varTyp.destructor),
+            useSym(varId.sym)]))]))
+
+      result.outer = newNodeI(nkStmtList, info)
+      varSection.sons.setLen(j+1)
+      result.outer.addSon(varSection)
+      result.outer.addSon(tryStmt)
+
+      return
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 8b1e5aea1..93dfc2f37 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -19,24 +19,6 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym, semCheck = true): PNode =
 
 proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
 
-proc performProcvarCheck(c: PContext, n: PNode, s: PSym) =
-  var smoduleId = getModule(s).id
-  if sfProcVar notin s.flags and s.typ.callConv == ccDefault and
-      smoduleId != c.module.id and smoduleId != c.friendModule.id: 
-    LocalError(n.info, errXCannotBePassedToProcVar, s.name.s)
-
-proc semProcvarCheck(c: PContext, n: PNode) =
-  let n = n.skipConv
-  if n.kind == nkSym and n.sym.kind in {skProc, skMethod, skIterator,
-                                        skConverter}:
-    performProcvarCheck(c, n, n.sym)
-
-proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} =
-  if efAllowDestructor notin flags and n.kind in nkCallKinds+{nkObjConstr}:
-    if instantiateDestructor(c, n.typ):
-      LocalError(n.info, errGenerated,
-        "usage of a type with a destructor in a non destructible context")
-
 proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   # same as 'semExprWithType' but doesn't check for proc vars
   result = semExpr(c, n, flags)
@@ -420,10 +402,10 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
       indexType = idx.typ
       x = x.sons[1]
     
-    addSon(result, semExprWithType(c, x))
-    var typ = skipTypes(result.sons[0].typ, {tyGenericInst, tyVar, tyOrdinal})
-    # turn any concrete typedesc into the absract typedesc type
-    if typ.kind == tyTypeDesc: typ.sons = nil
+    let yy = semExprWithType(c, x)
+    var typ = yy.typ
+    addSon(result, yy)
+    #var typ = skipTypes(result.sons[0].typ, {tyGenericInst, tyVar, tyOrdinal})
     for i in countup(1, sonsLen(n) - 1): 
       x = n.sons[i]
       if x.kind == nkExprColonExpr and sonsLen(x) == 2: 
@@ -433,10 +415,15 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
           localError(x.info, errInvalidOrderInArrayConstructor)
         x = x.sons[1]
       
-      n.sons[i] = semExprWithType(c, x, flags*{efAllowDestructor})
-      addSon(result, fitNode(c, typ, n.sons[i]))
+      let xx = semExprWithType(c, x, flags*{efAllowDestructor})
+      result.add xx
+      typ = commonType(typ, xx.typ)
+      #n.sons[i] = semExprWithType(c, x, flags*{efAllowDestructor})
+      #addSon(result, fitNode(c, typ, n.sons[i]))
       inc(lastIndex)
     addSonSkipIntLit(result.typ, typ)
+    for i in 0 .. <result.len:
+      result.sons[i] = fitNode(c, typ, result.sons[i])
   result.typ.sons[0] = makeRangeType(c, 0, sonsLen(result) - 1, n.info)
 
 proc fixAbstractType(c: PContext, n: PNode) = 
@@ -781,16 +768,6 @@ proc buildEchoStmt(c: PContext, n: PNode): PNode =
   # and check 'arg' for semantics again:
   addSon(result, semExpr(c, arg))
 
-proc discardCheck(result: PNode) =
-  if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}:
-    if result.kind == nkNilLit:
-      # XXX too much work and fixing would break bootstrapping:
-      #Message(n.info, warnNilStatement)
-      result.typ = nil
-    elif not ImplicitlyDiscardable(result) and result.typ.kind != tyError and
-        gCmd != cmdInteractive:
-      localError(result.info, errDiscardValue)
-
 proc semExprNoType(c: PContext, n: PNode): PNode =
   result = semExpr(c, n, {efWantStmt})
   discardCheck(result)
@@ -1125,14 +1102,16 @@ proc semAsgn(c: PContext, n: PNode): PNode =
     var
       rhs = semExprWithType(c, n.sons[1], 
         if lhsIsResult: {efAllowDestructor} else: {})
-    if lhsIsResult 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)
+    if lhsIsResult:
+      n.typ = EnforceVoidContext
+      if 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)
@@ -1167,11 +1146,16 @@ proc semProcBody(c: PContext, n: PNode): PNode =
     # ``result``:
     if result.kind == nkSym and result.sym == c.p.resultSym:
       nil
-    elif result.kind == nkNilLit or ImplicitlyDiscardable(result):
-      # intended semantic: if it's 'discardable' and the context allows for it,
-      # discard it. This is bad for chaining but nicer for C wrappers. 
-      # ambiguous :-(
+    elif result.kind == nkNilLit:
+      # or ImplicitlyDiscardable(result):
+      # new semantic: 'result = x' triggers the void context
       result.typ = nil
+    elif result.kind == nkStmtListExpr and result.typ.kind == tyNil:
+      # to keep backwards compatibility bodies like:
+      #   nil
+      #   # comment
+      # are not expressions:
+      fixNilType(result)
     else:
       var a = newNodeI(nkAsgn, n.info, 2)
       a.sons[0] = newSymNode(c.p.resultSym)
@@ -1458,26 +1442,35 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
   of mQuoteAst: result = semQuoteAst(c, n)
   else: result = semDirectOp(c, n, flags)
 
-proc semIfExpr(c: PContext, n: PNode): PNode = 
-  result = n
-  checkMinSonsLen(n, 2)
-  var typ: PType = nil
+proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
+  # If semCheck is set to false, ``when`` will return the verbatim AST of
+  # the correct branch. Otherwise the AST will be passed through semStmt.
+  result = nil
+  
+  template setResult(e: expr) =
+    if semCheck: result = semStmt(c, e) # do not open a new scope!
+    else: result = e
+
   for i in countup(0, sonsLen(n) - 1): 
     var it = n.sons[i]
     case it.kind
-    of nkElifExpr: 
+    of nkElifBranch, nkElifExpr: 
       checkSonsLen(it, 2)
-      it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0]))
-      it.sons[1] = semExprWithType(c, it.sons[1])
-      if typ == nil: typ = it.sons[1].typ
-      else: it.sons[1] = fitNode(c, typ, it.sons[1])
-    of nkElseExpr: 
+      var e = semConstExpr(c, it.sons[0])
+      if e.kind != nkIntLit: InternalError(n.info, "semWhen")
+      elif e.intVal != 0 and result == nil:
+        setResult(it.sons[1]) 
+    of nkElse, nkElseExpr:
       checkSonsLen(it, 1)
-      it.sons[0] = semExprWithType(c, it.sons[0])
-      if typ != nil: it.sons[0] = fitNode(c, typ, it.sons[0])
-      else: InternalError(it.info, "semIfExpr")
+      if result == nil: 
+        setResult(it.sons[0])
     else: illFormedAst(n)
-  result.typ = typ
+  if result == nil:
+    result = newNodeI(nkEmpty, n.info) 
+  # The ``when`` statement implements the mechanism for platform dependent
+  # code. Thus we try to ensure here consistent ID allocation after the
+  # ``when`` statement.
+  IDsynchronizationPoint(200)
 
 proc semSetConstr(c: PContext, n: PNode): PNode = 
   result = newNodeI(nkCurly, n.info)
@@ -1644,26 +1637,21 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
     it.sons[1] = e
     # XXX object field name check for 'case objects' if the kind is static?
 
-proc semStmtListExpr(c: PContext, n: PNode): PNode = 
-  result = n
-  checkMinSonsLen(n, 1)
-  var length = sonsLen(n)
-  for i in countup(0, length - 2): 
-    n.sons[i] = semStmt(c, n.sons[i])
-  if length > 0: 
-    n.sons[length - 1] = semExprWithType(c, n.sons[length - 1])
-    n.typ = n.sons[length - 1].typ
-
-proc semBlockExpr(c: PContext, n: PNode): PNode = 
+proc semBlock(c: PContext, n: PNode): PNode =
   result = n
   Inc(c.p.nestedBlockCounter)
   checkSonsLen(n, 2)
   openScope(c.tab)            # BUGFIX: label is in the scope of block!
-  if n.sons[0].kind notin {nkEmpty, nkSym}:
-    # nkSym for gensym'ed labels:
-    addDecl(c, newSymS(skLabel, n.sons[0], c))
-  n.sons[1] = semStmtListExpr(c, n.sons[1])
+  if n.sons[0].kind != nkEmpty:
+    var labl = newSymG(skLabel, n.sons[0], c)
+    if sfGenSym notin labl.flags:
+      addDecl(c, labl)
+      n.sons[0] = newSymNode(labl, n.sons[0].info)
+    suggestSym(n.sons[0], labl)
+  n.sons[1] = semExpr(c, n.sons[1])
   n.typ = n.sons[1].typ
+  if isEmptyType(n.typ): n.kind = nkBlockStmt
+  else: n.kind = nkBlockExpr
   closeScope(c.tab)
   Dec(c.p.nestedBlockCounter)
 
@@ -1683,63 +1671,11 @@ proc buildCall(n: PNode): PNode =
   else:
     result = 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, idAnon, 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 fixImmediateParams(n: PNode): PNode =
   # XXX: Temporary work-around until we carry out
   # the planned overload resolution reforms
   for i in 1 .. <safeLen(n):
     if n[i].kind == nkDo: n.sons[i] = n[i][bodyPos]
-  
   result = n
 
 proc semExport(c: PContext, n: PNode): PNode =
@@ -1899,7 +1835,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   of nkObjConstr: result = semObjConstr(c, n, flags)
   of nkLambdaKinds: result = semLambda(c, n, flags)
   of nkDerefExpr: result = semDeref(c, n)
-  of nkAddr: 
+  of nkAddr:
     result = n
     checkSonsLen(n, 1)
     n.sons[0] = semExprWithType(c, n.sons[0])
@@ -1910,9 +1846,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     checkSonsLen(n, 1)
     n.sons[0] = semExpr(c, n.sons[0], flags)
   of nkCast: result = semCast(c, n)
-  of nkIfExpr: result = semIfExpr(c, n)
-  of nkStmtListExpr: result = semStmtListExpr(c, n)
-  of nkBlockExpr: result = semBlockExpr(c, n)
+  of nkIfExpr, nkIfStmt: result = semIf(c, n)
   of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkHiddenCallConv: 
     checkSonsLen(n, 2)
   of nkStringToCString, nkCStringToString, nkObjDownConv, nkObjUpConv: 
@@ -1929,22 +1863,19 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   of nkStaticExpr:
     result = semStaticExpr(c, n)
   of nkAsgn: result = semAsgn(c, n)
-  of nkBlockStmt: result = semBlock(c, n)
-  of nkStmtList: result = semStmtList(c, n)
+  of nkBlockStmt, nkBlockExpr: result = semBlock(c, n)
+  of nkStmtList, nkStmtListExpr: result = semStmtList(c, n)
   of nkRaiseStmt: result = semRaise(c, n)
   of nkVarSection: result = semVarOrLet(c, n, skVar)
   of nkLetSection: result = semVarOrLet(c, n, skLet)
   of nkConstSection: result = semConst(c, n)
   of nkTypeSection: result = SemTypeSection(c, n)
-  of nkIfStmt: result = SemIf(c, n)
   of nkDiscardStmt: result = semDiscard(c, n)
   of nkWhileStmt: result = semWhile(c, n)
   of nkTryStmt: result = semTry(c, n)
   of nkBreakStmt, nkContinueStmt: result = semBreakOrContinue(c, n)
   of nkForStmt, nkParForStmt: result = semFor(c, n)
-  of nkCaseStmt:
-    if efWantStmt in flags: result = semCase(c, n)
-    else: result = semCaseExpr(c, n)
+  of nkCaseStmt: result = semCase(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 8142b5e03..cc1f4b5ee 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -378,18 +378,17 @@ proc getConstIfExpr(c: PSym, n: PNode): PNode =
   result = nil
   for i in countup(0, sonsLen(n) - 1): 
     var it = n.sons[i]
-    case it.kind
-    of nkElifExpr: 
+    if it.len == 2:
       var e = getConstExpr(c, it.sons[0])
       if e == nil: return nil
       if getOrdValue(e) != 0: 
         if result == nil: 
           result = getConstExpr(c, it.sons[1])
           if result == nil: return 
-    of nkElseExpr: 
+    elif it.len == 1:
       if result == nil: result = getConstExpr(c, it.sons[0])
     else: internalError(it.info, "getConstIfExpr()")
-  
+
 proc partialAndExpr(c: PSym, n: PNode): PNode = 
   # partial evaluation
   result = n
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 9e9de7260..2c3adfeda 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -10,54 +10,10 @@
 ## this module does the semantic checking of statements
 #  included from sem.nim
 
+var EnforceVoidContext = PType(kind: tyStmt)
+
 proc semCommand(c: PContext, n: PNode): PNode =
   result = semExprNoType(c, n)
-
-proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
-  # If semCheck is set to false, ``when`` will return the verbatim AST of
-  # the correct branch. Otherwise the AST will be passed through semStmt.
-  result = nil
-  
-  template setResult(e: expr) =
-    if semCheck: result = semStmt(c, e) # do not open a new scope!
-    else: result = e
-
-  for i in countup(0, sonsLen(n) - 1): 
-    var it = n.sons[i]
-    case it.kind
-    of nkElifBranch, nkElifExpr: 
-      checkSonsLen(it, 2)
-      var e = semConstExpr(c, it.sons[0])
-      if e.kind != nkIntLit: InternalError(n.info, "semWhen")
-      elif e.intVal != 0 and result == nil:
-        setResult(it.sons[1]) 
-    of nkElse, nkElseExpr:
-      checkSonsLen(it, 1)
-      if result == nil: 
-        setResult(it.sons[0])
-    else: illFormedAst(n)
-  if result == nil: 
-    result = newNodeI(nkNilLit, n.info) 
-  # The ``when`` statement implements the mechanism for platform dependent
-  # code. Thus we try to ensure here consistent ID allocation after the
-  # ``when`` statement.
-  IDsynchronizationPoint(200)
-
-proc semIf(c: PContext, n: PNode): PNode = 
-  result = n
-  for i in countup(0, sonsLen(n) - 1): 
-    var it = n.sons[i]
-    case it.kind
-    of nkElifBranch: 
-      checkSonsLen(it, 2)
-      it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0]))
-      openScope(c.tab)
-      it.sons[1] = semStmt(c, it.sons[1])
-      closeScope(c.tab)
-    of nkElse: 
-      if sonsLen(it) == 1: it.sons[0] = semStmtScope(c, it.sons[0])
-      else: illFormedAst(it)
-    else: illFormedAst(n)
   
 proc semDiscard(c: PContext, n: PNode): PNode = 
   result = n
@@ -86,21 +42,6 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode =
   elif (c.p.nestedLoopCounter <= 0) and (c.p.nestedBlockCounter <= 0): 
     localError(n.info, errInvalidControlFlowX, 
                renderTree(n, {renderNoComments}))
-  
-proc semBlock(c: PContext, n: PNode): PNode = 
-  result = n
-  Inc(c.p.nestedBlockCounter)
-  checkSonsLen(n, 2)
-  openScope(c.tab)            # BUGFIX: label is in the scope of block!
-  if n.sons[0].kind != nkEmpty:
-    var labl = newSymG(skLabel, n.sons[0], c)
-    if sfGenSym notin labl.flags:
-      addDecl(c, labl)
-      n.sons[0] = newSymNode(labl, n.sons[0].info)
-    suggestSym(n.sons[0], labl)
-  n.sons[1] = semStmt(c, n.sons[1])
-  closeScope(c.tab)
-  Dec(c.p.nestedBlockCounter)
 
 proc semAsm(con: PContext, n: PNode): PNode = 
   checkSonsLen(n, 2)
@@ -117,6 +58,8 @@ proc semWhile(c: PContext, n: PNode): PNode =
   n.sons[1] = semStmt(c, n.sons[1])
   dec(c.p.nestedLoopCounter)
   closeScope(c.tab)
+  if n.sons[1].typ == EnforceVoidContext:
+    result.typ = EnforceVoidContext
 
 proc toCover(t: PType): biggestInt = 
   var t2 = skipTypes(t, abstractVarRange-{tyTypeDesc})
@@ -125,20 +68,126 @@ proc toCover(t: PType): biggestInt =
   else:
     result = lengthOrd(skipTypes(t, abstractVar-{tyTypeDesc}))
 
-proc semCase(c: PContext, n: PNode): PNode = 
-  # check selector:
+proc performProcvarCheck(c: PContext, n: PNode, s: PSym) =
+  var smoduleId = getModule(s).id
+  if sfProcVar notin s.flags and s.typ.callConv == ccDefault and
+      smoduleId != c.module.id and smoduleId != c.friendModule.id: 
+    LocalError(n.info, errXCannotBePassedToProcVar, s.name.s)
+
+proc semProcvarCheck(c: PContext, n: PNode) =
+  let n = n.skipConv
+  if n.kind == nkSym and n.sym.kind in {skProc, skMethod, skIterator,
+                                        skConverter}:
+    performProcvarCheck(c, n, n.sym)
+
+proc semProc(c: PContext, n: PNode): PNode
+
+include semdestruct
+
+proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} =
+  if efAllowDestructor notin flags and n.kind in nkCallKinds+{nkObjConstr}:
+    if instantiateDestructor(c, n.typ):
+      LocalError(n.info, errGenerated,
+        "usage of a type with a destructor in a non destructible context")
+
+proc newDeref(n: PNode): PNode {.inline.} =  
+  result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0])
+  addSon(result, n)
+
+proc semExprBranch(c: PContext, n: PNode): PNode =
+  result = semExpr(c, n)
+  if result.typ != nil:
+    # XXX tyGenericInst here?
+    semProcvarCheck(c, result)
+    if result.typ.kind == tyVar: result = newDeref(result)
+    semDestructorCheck(c, result, {})
+
+proc semExprBranchScope(c: PContext, n: PNode): PNode =
+  openScope(c.tab)
+  result = semExprBranch(c, n)
+  closeScope(c.tab)
+
+const
+  skipForDiscardable = {nkIfStmt, nkIfExpr, nkCaseStmt, nkOfBranch,
+    nkElse, nkStmtListExpr, nkTryStmt, nkFinally, nkExceptBranch,
+    nkElifBranch, nkElifExpr, nkElseExpr, nkBlockStmt, nkBlockExpr}
+
+proc ImplicitlyDiscardable(n: PNode): bool =
+  var n = n
+  while n.kind in skipForDiscardable: n = n.lastSon
+  result = isCallExpr(n) and n.sons[0].kind == nkSym and 
+           sfDiscardable in n.sons[0].sym.flags
+
+proc fixNilType(n: PNode) =
+  if isAtom(n):
+    if n.kind != nkNilLit and n.typ != nil:
+      localError(n.info, errDiscardValue)
+  elif n.kind in {nkStmtList, nkStmtListExpr}:
+    n.kind = nkStmtList
+    for it in n: fixNilType(it)
+  n.typ = nil
+
+proc discardCheck(result: PNode) =
+  if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}:
+    if result.kind == nkNilLit:
+      result.typ = nil
+    elif ImplicitlyDiscardable(result):
+      var n = result
+      result.typ = nil
+      while n.kind in skipForDiscardable:
+        n = n.lastSon
+        n.typ = nil
+    elif result.typ.kind != tyError and gCmd != cmdInteractive:
+      if result.typ.kind == tyNil:
+        fixNilType(result)
+      else:
+        localError(result.info, errDiscardValue)
+
+proc semIf(c: PContext, n: PNode): PNode = 
+  result = n
+  var typ = CommonTypeBegin
+  var hasElse = false
+  for i in countup(0, sonsLen(n) - 1): 
+    var it = n.sons[i]
+    if it.len == 2:
+      when newScopeForIf: openScope(c.tab)
+      it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0]))
+      when not newScopeForIf: openScope(c.tab)
+      it.sons[1] = semExprBranch(c, it.sons[1])
+      typ = commonType(typ, it.sons[1].typ)
+      closeScope(c.tab)
+    elif it.len == 1:
+      hasElse = true
+      it.sons[0] = semExprBranchScope(c, it.sons[0])
+      typ = commonType(typ, it.sons[0].typ)
+    else: illFormedAst(it)
+  if isEmptyType(typ) or typ.kind == tyNil or not hasElse:
+    for it in n: discardCheck(it.lastSon)
+    result.kind = nkIfStmt
+    # propagate any enforced VoidContext:
+    if typ == EnforceVoidContext: result.typ = EnforceVoidContext
+  else:
+    for it in n:
+      let j = it.len-1
+      it.sons[j] = fitNode(c, typ, it.sons[j])
+    result.kind = nkIfExpr
+    result.typ = typ
+
+proc semCase(c: PContext, n: PNode): PNode =
   result = n
   checkMinSonsLen(n, 2)
   openScope(c.tab)
   n.sons[0] = semExprWithType(c, n.sons[0])
   var chckCovered = false
   var covered: biggestint = 0
+  var typ = CommonTypeBegin
+  var hasElse = false
   case skipTypes(n.sons[0].Typ, abstractVarRange-{tyTypeDesc}).Kind
-  of tyInt..tyInt64, tyChar, tyEnum: 
+  of tyInt..tyInt64, tyChar, tyEnum:
     chckCovered = true
-  of tyFloat..tyFloat128, tyString, tyError: 
+  of tyFloat..tyFloat128, tyString, tyError:
     nil
-  else: 
+  else:
     LocalError(n.info, errSelectorMustBeOfCertainTypes)
     return
   for i in countup(1, sonsLen(n) - 1): 
@@ -147,21 +196,89 @@ proc semCase(c: PContext, n: PNode): PNode =
     of nkOfBranch: 
       checkMinSonsLen(x, 2)
       semCaseBranch(c, n, x, i, covered)
-      var length = sonsLen(x)
-      x.sons[length - 1] = semStmtScope(c, x.sons[length - 1])
-    of nkElifBranch: 
+      var last = sonsLen(x)-1
+      x.sons[last] = semExprBranchScope(c, x.sons[last])
+      typ = commonType(typ, x.sons[last].typ)
+    of nkElifBranch:
       chckCovered = false
       checkSonsLen(x, 2)
+      when newScopeForIf: openScope(c.tab)
       x.sons[0] = forceBool(c, semExprWithType(c, x.sons[0]))
-      x.sons[1] = semStmtScope(c, x.sons[1])
-    of nkElse: 
+      when not newScopeForIf: openScope(c.tab)
+      x.sons[1] = semExprBranch(c, x.sons[1])
+      typ = commonType(typ, x.sons[1].typ)
+      closeScope(c.tab)
+    of nkElse:
       chckCovered = false
       checkSonsLen(x, 1)
-      x.sons[0] = semStmtScope(c, x.sons[0])
-    else: illFormedAst(x)
-  if chckCovered and (covered != toCover(n.sons[0].typ)): 
-    localError(n.info, errNotAllCasesCovered)
+      x.sons[0] = semExprBranchScope(c, x.sons[0])
+      typ = commonType(typ, x.sons[0].typ)
+      hasElse = true
+    else:
+      illFormedAst(x)
+  if chckCovered:
+    if covered == toCover(n.sons[0].typ):
+      hasElse = true
+    else:
+      localError(n.info, errNotAllCasesCovered)
   closeScope(c.tab)
+  if isEmptyType(typ) or typ.kind == tyNil or not hasElse:
+    for i in 1..n.len-1: discardCheck(n.sons[i].lastSon)
+    # propagate any enforced VoidContext:
+    if typ == EnforceVoidContext:
+      result.typ = EnforceVoidContext
+  else:
+    for i in 1..n.len-1:
+      var it = n.sons[i]
+      let j = it.len-1
+      it.sons[j] = fitNode(c, typ, it.sons[j])
+    result.typ = typ
+
+proc semTry(c: PContext, n: PNode): PNode =
+  result = n
+  inc c.p.inTryStmt
+  checkMinSonsLen(n, 2)
+  var typ = CommonTypeBegin
+  n.sons[0] = semExprBranchScope(c, n.sons[0])
+  typ = commonType(typ, n.sons[0].typ)
+  var check = initIntSet()
+  for i in countup(1, sonsLen(n) - 1): 
+    var a = n.sons[i]
+    checkMinSonsLen(a, 1)
+    var length = sonsLen(a)
+    if a.kind == nkExceptBranch:
+      # XXX what does this do? so that ``except [a, b, c]`` is supported?
+      if length == 2 and a.sons[0].kind == nkBracket:
+        a.sons[0..0] = a.sons[0].sons
+        length = a.sonsLen
+
+      for j in countup(0, length-2):
+        var typ = semTypeNode(c, a.sons[j], nil)
+        if typ.kind == tyRef: typ = typ.sons[0]
+        if typ.kind != tyObject:
+          LocalError(a.sons[j].info, errExprCannotBeRaised)
+        a.sons[j] = newNodeI(nkType, a.sons[j].info)
+        a.sons[j].typ = typ
+        if ContainsOrIncl(check, typ.id):
+          localError(a.sons[j].info, errExceptionAlreadyHandled)
+    elif a.kind != nkFinally: 
+      illFormedAst(n)
+    # last child of an nkExcept/nkFinally branch is a statement:
+    a.sons[length-1] = semExprBranchScope(c, a.sons[length-1])
+    typ = commonType(typ, a.sons[length-1].typ)
+  dec c.p.inTryStmt
+  if isEmptyType(typ) or typ.kind == tyNil:
+    discardCheck(n.sons[0])
+    for i in 1..n.len-1: discardCheck(n.sons[i].lastSon)
+    if typ == EnforceVoidContext:
+      result.typ = EnforceVoidContext
+  else:
+    n.sons[0] = fitNode(c, typ, n.sons[0])
+    for i in 1..n.len-1:
+      var it = n.sons[i]
+      let j = it.len-1
+      it.sons[j] = fitNode(c, typ, it.sons[j])
+    result.typ = typ
   
 proc fitRemoveHiddenConv(c: PContext, typ: Ptype, n: PNode): PNode = 
   result = fitNode(c, typ, n)
@@ -484,10 +601,6 @@ proc semForVars(c: PContext, n: PNode): PNode =
   n.sons[length-1] = SemStmt(c, n.sons[length-1])
   Dec(c.p.nestedLoopCounter)
 
-proc newDeref(n: PNode): PNode {.inline.} =  
-  result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0])
-  addSon(result, n)
-
 proc implicitIterator(c: PContext, it: string, arg: PNode): PNode =
   result = newNodeI(nkCall, arg.info)
   result.add(newIdentNode(it.getIdent, arg.info))
@@ -524,6 +637,9 @@ proc semFor(c: PContext, n: PNode): PNode =
       result = semForFields(c, n, call.sons[0].sym.magic)
   else:
     result = semForVars(c, n)
+  # propagate any enforced VoidContext:
+  if n.sons[length-1].typ == EnforceVoidContext:
+    result.typ = EnforceVoidContext
   closeScope(c.tab)
 
 proc semRaise(c: PContext, n: PNode): PNode = 
@@ -535,36 +651,6 @@ proc semRaise(c: PContext, n: PNode): PNode =
     if typ.kind != tyRef or typ.sons[0].kind != tyObject: 
       localError(n.info, errExprCannotBeRaised)
 
-proc semTry(c: PContext, n: PNode): PNode = 
-  result = n
-  inc c.p.inTryStmt
-  checkMinSonsLen(n, 2)
-  n.sons[0] = semStmtScope(c, n.sons[0])
-  var check = initIntSet()
-  for i in countup(1, sonsLen(n) - 1): 
-    var a = n.sons[i]
-    checkMinSonsLen(a, 1)
-    var length = sonsLen(a)
-    if a.kind == nkExceptBranch:
-      if length == 2 and a.sons[0].kind == nkBracket:
-        a.sons[0..0] = a.sons[0].sons
-        length = a.sonsLen
-
-      for j in countup(0, length - 2):
-        var typ = semTypeNode(c, a.sons[j], nil)
-        if typ.kind == tyRef: typ = typ.sons[0]
-        if typ.kind != tyObject:
-          LocalError(a.sons[j].info, errExprCannotBeRaised)
-        a.sons[j] = newNodeI(nkType, a.sons[j].info)
-        a.sons[j].typ = typ
-        if ContainsOrIncl(check, typ.id):
-          localError(a.sons[j].info, errExceptionAlreadyHandled)
-    elif a.kind != nkFinally: 
-      illFormedAst(n) 
-    # last child of an nkExcept/nkFinally branch is a statement:
-    a.sons[length - 1] = semStmtScope(c, a.sons[length - 1])
-  dec c.p.inTryStmt
-
 proc addGenericParamListToScope(c: PContext, n: PNode) =
   if n.kind != nkGenericParams: illFormedAst(n)
   for i in countup(0, sonsLen(n)-1):
@@ -607,9 +693,6 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
       openScope(c.tab)
       pushOwner(s)
       if s.magic == mNone: s.typ.kind = tyGenericBody
-      if s.typ.containerID != 0: 
-        InternalError(a.info, "semTypeSection: containerID")
-      s.typ.containerID = s.typ.id
       # XXX for generic type aliases this is not correct! We need the
       # underlying Id really: 
       #
@@ -678,6 +761,9 @@ proc SemTypeSection(c: PContext, n: PNode): PNode =
 
 proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) =
   s.typ = semProcTypeNode(c, n, genericParams, nil, s.kind)
+  if s.kind notin {skMacro, skTemplate}:
+    if s.typ.sons[0] != nil and s.typ.sons[0].kind == tyStmt:
+      localError(n.info, errGenerated, "invalid return type: 'stmt'")
 
 proc addParams(c: PContext, n: PNode, kind: TSymKind) = 
   for i in countup(1, sonsLen(n)-1): 
@@ -792,22 +878,6 @@ proc activate(c: PContext, n: PNode) =
     else:
       nil
 
-proc instantiateDestructor*(c: PContext, typ: PType): bool
-
-proc doDestructorStuff(c: PContext, s: PSym, n: PNode) =
-  let t = s.typ.sons[1].skipTypes({tyVar})
-  t.destructor = s
-  # automatically insert calls to base classes' destructors
-  if n.sons[bodyPos].kind != nkEmpty:
-    for i in countup(0, t.sonsLen - 1):
-      # when inheriting directly from object
-      # there will be a single nil son
-      if t.sons[i] == nil: continue
-      if instantiateDestructor(c, t.sons[i]):
-        n.sons[bodyPos].addSon(newNode(nkCall, t.sym.info, @[
-            useSym(t.sons[i].destructor),
-            n.sons[paramsPos][1][0]]))
-
 proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
   if s.typ.sons[0] != nil and
       (s.kind != skIterator or s.typ.callConv == ccClosure):
@@ -1018,203 +1088,34 @@ proc semStaticStmt(c: PContext, n: PNode): PNode =
     result = newNodeI(nkDiscardStmt, n.info, 1)
     result.sons[0] = emptyNode
 
-# special marker values that indicates that we are
-# 1) AnalyzingDestructor: currently analyzing the type for destructor 
-# generation (needed for recursive types)
-# 2) DestructorIsTrivial: completed the analysis before and determined
-# that the type has a trivial destructor
-var AnalyzingDestructor, DestructorIsTrivial: PSym
-new(AnalyzingDestructor)
-new(DestructorIsTrivial)
-
-var
-  destructorName = getIdent"destroy_"
-  destructorParam = getIdent"this_"
-  destructorPragma = newIdentNode(getIdent"destructor", UnknownLineInfo())
-  rangeDestructorProc*: PSym
-
-proc destroyField(c: PContext, field: PSym, holder: PNode): PNode =
-  if instantiateDestructor(c, field.typ):
-    result = newNode(nkCall, field.info, @[
-      useSym(field.typ.destructor),
-      newNode(nkDotExpr, field.info, @[holder, useSym(field)])])
-
-proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode =
-  var nonTrivialFields = 0
-  result = newNode(nkCaseStmt, n.info, @[])
-  # case x.kind
-  result.addSon(newNode(nkDotExpr, n.info, @[holder, n.sons[0]]))
-  for i in countup(1, n.len - 1):
-    # of A, B:
-    var caseBranch = newNode(n[i].kind, n[i].info, n[i].sons[0 .. -2])
-    let recList = n[i].lastSon
-    var destroyRecList = newNode(nkStmtList, n[i].info, @[])
-    template addField(f: expr): stmt =
-      let stmt = destroyField(c, f, holder)
-      if stmt != nil:
-        destroyRecList.addSon(stmt)
-        inc nonTrivialFields
-        
-    case recList.kind
-    of nkSym:
-      addField(recList.sym)
-    of nkRecList:
-      for j in countup(0, recList.len - 1):
-        addField(recList[j].sym)
-    else:
-      internalAssert false
-      
-    caseBranch.addSon(destroyRecList)
-    result.addSon(caseBranch)
-  # maybe no fields were destroyed?
-  if nonTrivialFields == 0:
-    result = nil
- 
-proc generateDestructor(c: PContext, t: PType): PNode =
-  ## generate a destructor for a user-defined object or tuple type
-  ## returns nil if the destructor turns out to be trivial
-  
-  template addLine(e: expr): stmt =
-    if result == nil: result = newNode(nkStmtList)
-    result.addSon(e)
-
-  # XXX: This may be true for some C-imported types such as
-  # Tposix_spawnattr
-  if t.n == nil or t.n.sons == nil: return
-  internalAssert t.n.kind == nkRecList
-  let destructedObj = newIdentNode(destructorParam, UnknownLineInfo())
-  # call the destructods of all fields
-  for s in countup(0, t.n.sons.len - 1):
-    case t.n.sons[s].kind
-    of nkRecCase:
-      let stmt = destroyCase(c, t.n.sons[s], destructedObj)
-      if stmt != nil: addLine(stmt)
-    of nkSym:
-      let stmt = destroyField(c, t.n.sons[s].sym, destructedObj)
-      if stmt != nil: addLine(stmt)
-    else:
-      internalAssert false
-
-  # base classes' destructors will be automatically called by
-  # semProcAux for both auto-generated and user-defined destructors
-
-proc instantiateDestructor*(c: PContext, typ: PType): bool =
-  # returns true if the type already had a user-defined
-  # destructor or if the compiler generated a default
-  # member-wise one
-  var t = skipTypes(typ, {tyConst, tyMutable})
-  
-  if t.destructor != nil:
-    # XXX: This is not entirely correct for recursive types, but we need
-    # it temporarily to hide the "destroy is already defined" problem
-    return t.destructor notin [AnalyzingDestructor, DestructorIsTrivial]
-  
-  case t.kind
-  of tySequence, tyArray, tyArrayConstr, tyOpenArray, tyVarargs:
-    if instantiateDestructor(c, t.sons[0]):
-      if rangeDestructorProc == nil:
-        rangeDestructorProc = SymtabGet(c.tab, getIdent"nimDestroyRange")
-      t.destructor = rangeDestructorProc
-      return true
-    else:
-      return false
-  of tyTuple, tyObject:
-    t.destructor = AnalyzingDestructor
-    let generated = generateDestructor(c, t)
-    if generated != nil:
-      internalAssert t.sym != nil
-      var i = t.sym.info
-      let fullDef = newNode(nkProcDef, i, @[
-        newIdentNode(destructorName, i),
-        emptyNode,
-        emptyNode,
-        newNode(nkFormalParams, i, @[
-          emptyNode,
-          newNode(nkIdentDefs, i, @[
-            newIdentNode(destructorParam, i),
-            useSym(t.sym),
-            emptyNode]),
-          ]),
-        newNode(nkPragma, i, @[destructorPragma]),
-        emptyNode,
-        generated
-        ])
-      discard semProc(c, fullDef)
-      internalAssert t.destructor != nil
-      return true
+proc usesResult(n: PNode): bool =
+  # nkStmtList(expr) properly propagates the void context,
+  # so we don't need to process that all over again:
+  if n.kind notin {nkStmtList, nkStmtListExpr} + procDefs:
+    if isAtom(n):
+      result = n.kind == nkSym and n.sym.kind == skResult
+    elif n.kind == nkReturnStmt:
+      result = true
     else:
-      t.destructor = DestructorIsTrivial
-      return false
-  else:
-    return false
-
-proc insertDestructors(c: PContext,
-                       varSection: PNode): tuple[outer, inner: PNode] =
-  # Accepts a var or let section.
-  #
-  # When a var section has variables with destructors
-  # the var section is split up and finally blocks are inserted
-  # immediately after all "destructable" vars
-  #
-  # In case there were no destrucable variables, the proc returns
-  # (nil, nil) and the enclosing stmt-list requires no modifications.
-  #
-  # Otherwise, after the try blocks are created, the rest of the enclosing
-  # stmt-list should be inserted in the most `inner` such block (corresponding
-  # to the last variable).
-  #
-  # `outer` is a statement list that should replace the original var section.
-  # It will include the new truncated var section followed by the outermost
-  # try block.
-  let totalVars = varSection.sonsLen
-  for j in countup(0, totalVars - 1):
-    let
-      varId = varSection[j][0]
-      varTyp = varId.sym.typ
-      info = varId.info
-
-    if varTyp != nil and instantiateDestructor(c, varTyp) and 
-        sfGlobal notin varId.sym.flags:
-      var tryStmt = newNodeI(nkTryStmt, info)
-
-      if j < totalVars - 1:
-        var remainingVars = newNodeI(varSection.kind, info)
-        remainingVars.sons = varSection.sons[(j+1)..(-1)]
-        let (outer, inner) = insertDestructors(c, remainingVars)
-        if outer != nil:
-          tryStmt.addSon(outer)
-          result.inner = inner
-        else:
-          result.inner = newNodeI(nkStmtList, info)
-          result.inner.addSon(remainingVars)
-          tryStmt.addSon(result.inner)
-      else:
-        result.inner = newNodeI(nkStmtList, info)
-        tryStmt.addSon(result.inner)
-
-      tryStmt.addSon(
-        newNode(nkFinally, info, @[
-          semStmt(c, newNode(nkCall, info, @[
-            useSym(varTyp.destructor),
-            useSym(varId.sym)]))]))
-
-      result.outer = newNodeI(nkStmtList, info)
-      varSection.sons.setLen(j+1)
-      result.outer.addSon(varSection)
-      result.outer.addSon(tryStmt)
-
-      return
-
-proc ImplicitlyDiscardable(n: PNode): bool =
-  result = isCallExpr(n) and n.sons[0].kind == nkSym and 
-           sfDiscardable in n.sons[0].sym.flags
+      for c in n:
+        if usesResult(c): return true
 
 proc semStmtList(c: PContext, n: PNode): PNode =
   # these must be last statements in a block:
   const
     LastBlockStmts = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt}
   result = n
+  result.kind = nkStmtList
   var length = sonsLen(n)
+  var voidContext = false
+  var last = length-1
+  # by not allowing for nkCommentStmt etc. we ensure nkStmtListExpr actually
+  # really *ends* in the expression that produces the type: The compiler now
+  # relies on this fact and it's too much effort to change that. And arguably
+  #  'R(); #comment' shouldn't produce R's type anyway.
+  #while last > 0 and n.sons[last].kind in {nkPragma, nkCommentStmt,
+  #                                         nkNilLit, nkEmpty}:
+  #  dec last
   for i in countup(0, length - 1):
     case n.sons[i].kind
     of nkFinally, nkExceptBranch:
@@ -1236,7 +1137,16 @@ proc semStmtList(c: PContext, n: PNode): PNode =
       n.sons.setLen(i+1)
       return
     else:
-      n.sons[i] = semStmt(c, n.sons[i])
+      n.sons[i] = semExpr(c, n.sons[i])
+      if n.sons[i].typ == EnforceVoidContext or usesResult(n.sons[i]):
+        voidContext = true
+        n.typ = EnforceVoidContext
+      elif i != last or voidContext:
+        discardCheck(n.sons[i])
+      else:
+        n.typ = n.sons[i].typ
+        if not isEmptyType(n.typ):
+          n.kind = nkStmtListExpr
       case n.sons[i].kind
       of nkVarSection, nkLetSection:
         let (outer, inner) = insertDestructors(c, n.sons[i])
@@ -1252,15 +1162,17 @@ proc semStmtList(c: PContext, n: PNode): PNode =
           of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: nil
           else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
       else: nil
-  
-  # a statement list (s; e) has the type 'e':
-  if result.kind == nkStmtList and result.len > 0:
-    var lastStmt = lastSon(result)
-    if lastStmt.kind != nkNilLit and not ImplicitlyDiscardable(lastStmt):
-      result.typ = lastStmt.typ
-      #localError(lastStmt.info, errGenerated,
-      #  "Last expression must be explicitly returned if it " &
-      #  "is discardable or discarded")
+  if result.len == 1:
+    result = result.sons[0]
+  when false:
+    # a statement list (s; e) has the type 'e':
+    if result.kind == nkStmtList and result.len > 0:
+      var lastStmt = lastSon(result)
+      if lastStmt.kind != nkNilLit and not ImplicitlyDiscardable(lastStmt):
+        result.typ = lastStmt.typ
+        #localError(lastStmt.info, errGenerated,
+        #  "Last expression must be explicitly returned if it " &
+        #  "is discardable or discarded")
 
 proc SemStmt(c: PContext, n: PNode): PNode = 
   # now: simply an alias:
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index b3a82c413..3abc994ef 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -770,9 +770,6 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
     return newOrPrevType(tyError, prev, c)
   elif s.typ.kind != tyGenericBody:
     isConcrete = false
-  elif s.typ.containerID == 0: 
-    InternalError(n.info, "semtypes.semGeneric")
-    return newOrPrevType(tyError, prev, c)
   elif sonsLen(n) != sonsLen(s.typ): 
     LocalError(n.info, errWrongNumberOfArguments)
     return newOrPrevType(tyError, prev, c)
diff --git a/compiler/service.nim b/compiler/service.nim
index b3c7fbc83..8e8fe20bf 100644
--- a/compiler/service.nim
+++ b/compiler/service.nim
@@ -65,6 +65,9 @@ proc serve*(action: proc (){.nimcall.}) =
     curCaasCmd = cmd
     processCmdLine(passCmd2, cmd)
     action()
+    gDirtyBufferIdx = 0
+    gDirtyOriginalIdx = 0
+    gErrorCounter = 0
 
   let typ = getConfigVar("server.type")
   case typ
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index ddda493ee..e1882e1fb 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -559,11 +559,10 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
       # simply no match for now:
       nil
     elif x.kind == tyGenericInst and 
-          (f.sons[0].containerID == x.sons[0].containerID) and
-          (sonsLen(x) - 1 == sonsLen(f)): 
-      assert(x.sons[0].kind == tyGenericBody)
-      for i in countup(1, sonsLen(f) - 1): 
-        if x.sons[i].kind == tyGenericParam: 
+          (f.sons[0] == x.sons[0]) and
+          (sonsLen(x) - 1 == sonsLen(f)):
+      for i in countup(1, sonsLen(f) - 1):
+        if x.sons[i].kind == tyGenericParam:
           InternalError("wrong instantiated type!")
         elif typeRel(c, f.sons[i], x.sons[i]) <= isSubtype: return 
       result = isGeneric
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index 130666f4d..18e6dbddf 100644
--- a/compiler/suggest.nim
+++ b/compiler/suggest.nim
@@ -9,7 +9,9 @@
 
 ## This file implements features required for IDE support.
 
-# imported from sigmatch.nim
+# included from sigmatch.nim
+
+import algorithm
 
 const
   sep = '\t'
@@ -238,12 +240,69 @@ proc findDefinition(node: PNode, s: PSym) =
     SuggestWriteln(SymToStr(s, isLocal=false, sectionDef))
     SuggestQuit()
 
+type
+  TSourceMap = object
+    lines: seq[TLineMap]
+  
+  TEntry = object
+    pos: int
+    sym: PSym
+
+  TLineMap = object
+    entries: seq[TEntry]
+
+var
+  gSourceMaps: seq[TSourceMap] = @[]
+
+proc ensureIdx[T](x: var T, y: int) =
+  if x.len <= y: x.setLen(y+1)
+
+proc ensureSeq[T](x: var seq[T]) =
+  if x == nil: newSeq(x, 0)
+
+proc resetSourceMap*(fileIdx: int32) =
+  ensureIdx(gSourceMaps, fileIdx)
+  gSourceMaps[fileIdx].lines = @[]
+
+proc addToSourceMap(sym: Psym, info: TLineInfo) =
+  ensureIdx(gSourceMaps, info.fileIndex)
+  ensureSeq(gSourceMaps[info.fileIndex].lines)
+  ensureIdx(gSourceMaps[info.fileIndex].lines, info.line)
+  ensureSeq(gSourceMaps[info.fileIndex].lines[info.line].entries)
+  gSourceMaps[info.fileIndex].lines[info.line].entries.add(TEntry(pos: info.col, sym: sym))
+
+proc defFromLine(entries: var seq[TEntry], col: int32) =
+  if entries == nil: return
+  # The sorting is done lazily here on purpose.
+  # No need to pay the price for it unless the user requests
+  # "goto definition" on a particular line
+  sort(entries) do (a,b: TEntry) -> int:
+    return cmp(a.pos, b.pos)
+  
+  for e in entries:
+    # currently, the line-infos for most expressions point to
+    # one position past the end of the expression. This means
+    # that the first expr that ends after the cursor column is
+    # the one we are looking for.
+    if e.pos >= col:
+      SuggestWriteln(SymToStr(e.sym, isLocal=false, sectionDef))
+      return
+
+proc defFromSourceMap*(i: TLineInfo) =
+  if not ((i.fileIndex < gSourceMaps.len) and
+          (gSourceMaps[i.fileIndex].lines != nil) and
+          (i.line < gSourceMaps[i.fileIndex].lines.len)): return
+  
+  defFromLine(gSourceMaps[i.fileIndex].lines[i.line].entries, i.col)
+
 proc suggestSym*(n: PNode, s: PSym) {.inline.} =
   ## misnamed: should be 'symDeclared'
   if optUsages in gGlobalOptions:
     findUsages(n, s)
   if optDef in gGlobalOptions:
     findDefinition(n, s)
+  if isServing:
+    addToSourceMap(s, n.info)
 
 proc markUsed(n: PNode, s: PSym) =
   incl(s.flags, sfUsed)
diff --git a/compiler/types.nim b/compiler/types.nim
index 4b528d9a2..6f47a7f2d 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -486,7 +486,9 @@ proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
   of tyPtr, tyRef, tyVar, tyMutable, tyConst: 
     result = typeToStr[t.kind] & typeToString(t.sons[0])
   of tyRange:
-    result = "range " & rangeToStr(t.n) & "(" & typeToString(t.sons[0]) & ")"
+    result = "range " & rangeToStr(t.n)
+    if prefer != preferExported:
+      result.add("(" & typeToString(t.sons[0]) & ")")
   of tyProc:
     result = if tfIterator in t.flags: "iterator (" else: "proc ("
     for i in countup(1, sonsLen(t) - 1): 
@@ -904,6 +906,26 @@ proc inheritanceDiff*(a, b: PType): int =
     inc(result)
   result = high(int)
 
+proc commonSuperclass*(a, b: PType): PType =
+  # quick check: are they the same?
+  if sameObjectTypes(a, b): return a
+
+  # simple algorithm: we store all ancestors of 'a' in a ID-set and walk 'b'
+  # up until the ID is found:
+  assert a.kind == tyObject
+  assert b.kind == tyObject
+  var x = a
+  var ancestors = initIntSet()
+  while x != nil:
+    x = skipTypes(x, skipPtrs)
+    ancestors.incl(x.id)
+    x = x.sons[0]
+  var y = b
+  while y != nil:
+    y = skipTypes(y, skipPtrs)
+    if ancestors.contains(y.id): return y
+    y = y.sons[0]
+
 proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool
 proc typeAllowedNode(marker: var TIntSet, n: PNode, kind: TSymKind): bool = 
   result = true
@@ -1261,3 +1283,14 @@ proc compatibleEffects*(formal, actual: PType): bool =
       result = compatibleEffectsAux(st, real.sons[tagEffects])
       if not result: return
   result = true
+
+proc isCompileTimeOnly*(t: PType): bool {.inline.} =
+  result = t.kind in {tyTypedesc, tyExpr}
+
+proc containsCompileTimeOnly*(t: PType): bool =
+  if isCompileTimeOnly(t): return true
+  if t.sons != nil:
+    for i in 0 .. <t.sonsLen:
+      if t.sons[i] != nil and isCompileTimeOnly(t.sons[i]):
+        return true
+  return false