summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ccgexprs.nim223
-rw-r--r--compiler/ccgstmts.nim214
-rw-r--r--compiler/ccgtypes.nim11
-rw-r--r--compiler/cgen.nim15
-rw-r--r--compiler/docgen.nim2
-rw-r--r--compiler/extccomp.nim4
-rw-r--r--compiler/jsgen.nim1248
-rw-r--r--compiler/jstypes.nim148
-rw-r--r--compiler/lexer.nim139
-rw-r--r--compiler/msgs.nim7
-rw-r--r--compiler/nimconf.nim2
-rw-r--r--compiler/options.nim3
-rw-r--r--compiler/parser.nim1065
-rw-r--r--compiler/renderer.nim9
-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.nim535
-rw-r--r--compiler/types.nim35
-rw-r--r--doc/grammar.txt393
-rw-r--r--doc/manual.txt328
-rw-r--r--doc/tut1.txt7
-rw-r--r--lib/impure/re.nim3
-rw-r--r--lib/pure/httpclient.nim2
-rw-r--r--lib/pure/pegs.nim9
-rw-r--r--lib/pure/times.nim4
-rw-r--r--lib/system/jssys.nim5
-rw-r--r--tests/compile/tcommontype.nim20
-rw-r--r--tests/manyloc/argument_parser/ex_wget.nim87
-rw-r--r--tests/manyloc/argument_parser/ex_wget.nimrod.cfg1
-rw-r--r--tests/manyloc/keineschweine/keineschweine.nimrod.cfg1
-rw-r--r--tests/manyloc/nake/nake.nim83
-rw-r--r--tests/manyloc/nake/nakefile.nim (renamed from tests/manyloc/keineschweine/nakefile.nim)2
-rw-r--r--tests/manyloc/nake/nakefile.nimrod.cfg (renamed from tests/manyloc/keineschweine/nakefile.nimrod.cfg)0
-rw-r--r--tests/reject/temptycaseobj.nim2
-rw-r--r--tests/reject/tind1.nim2
-rw-r--r--tests/reject/tmissingnl.nim2
-rw-r--r--tests/run/tstmtexprs.nim66
-rw-r--r--tests/specials.nim3
-rw-r--r--todo.txt19
-rw-r--r--web/index.txt9
43 files changed, 2791 insertions, 2386 deletions
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 6a2ffe752..a50b39d47 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,129 @@ 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)
+    if n.sons[0].kind == nkSym and n.sons[0].sym.magic != mNone:
+      genMagicExpr(p, n, d, n.sons[0].sym.magic)
     else:
-      genCall(p, e, d)
+      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..f09252b48 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))
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..8107fcd82 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -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/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/msgs.nim b/compiler/msgs.nim
index 6062ebd7f..e11e47bac 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -714,7 +714,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 +789,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..08a5ba010 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!)
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/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/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..bca77e06a 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -12,52 +12,6 @@
 
 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 +40,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)
@@ -125,20 +64,125 @@ 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)
+
+proc meaningfulStmt(n: PNode): PNode =
+  assert n.kind == nkStmtListExpr
+  var last = n.len-1
+  while last > 0 and n.sons[last].kind in {nkPragma, nkCommentStmt, nkEmpty}:
+    dec last
+  result = n.sons[last]
+
+proc ImplicitlyDiscardable(n: PNode): bool =
+  var n = n
+  while n.kind == nkStmtListExpr: n = meaningfulStmt(n)
+  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}:
+    for it in n: fixNilType(it)
+  n.typ = nil
+
+var EnforceVoidContext = PType(kind: tyStmt)
+
+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:
+      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 not hasElse:
+    for it in n: discardCheck(it.lastSon)
+    result.kind = nkIfStmt
+  else:
+    for it in n:
+      let j = it.len-1
+      it.sons[j] = fitNode(c, typ, it.sons[j])
+    result.kind = nkIfExpr
+  # propagate any enforced VoidContext:
+  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 +191,83 @@ 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)
+      x.sons[0] = semExprBranchScope(c, x.sons[0])
+      typ = commonType(typ, x.sons[0].typ)
+      hasElse = true
+    else:
+      illFormedAst(x)
   if chckCovered and (covered != toCover(n.sons[0].typ)): 
     localError(n.info, errNotAllCasesCovered)
   closeScope(c.tab)
+  if isEmptyType(typ) or not hasElse:
+    for i in 1..n.len-1: discardCheck(n.sons[i].lastSon)
+  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])
+  # propagate any enforced VoidContext:
+  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):
+    discardCheck(n.sons[0])
+    for i in 1..n.len-1: discardCheck(n.sons[i].lastSon)
+  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])
+  # propagate any enforced VoidContext:
+  result.typ = typ
   
 proc fitRemoveHiddenConv(c: PContext, typ: Ptype, n: PNode): PNode = 
   result = fitNode(c, typ, n)
@@ -484,10 +590,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 +626,8 @@ 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:
+  result.typ = n.sons[length-1].typ
   closeScope(c.tab)
 
 proc semRaise(c: PContext, n: PNode): PNode = 
@@ -535,36 +639,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):
@@ -792,22 +866,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 +1076,21 @@ 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
-    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
-
 proc semStmtList(c: PContext, n: PNode): PNode =
   # these must be last statements in a block:
   const
     LastBlockStmts = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt}
   result = n
   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 +1112,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:
+        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 +1137,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/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
diff --git a/doc/grammar.txt b/doc/grammar.txt
index b95af89ef..217007281 100644
--- a/doc/grammar.txt
+++ b/doc/grammar.txt
@@ -1,204 +1,189 @@
-module ::= ([COMMENT] [SAD] stmt)*
-
-comma ::= ',' [COMMENT] [IND]
-semicolon ::= ';' [COMMENT] [IND]
-
-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] [IND]
-optPar ::= [IND] | [SAD]
-
-lowestExpr ::= 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)*
-
-indexExpr ::= expr
-
-castExpr ::= 'cast' '[' optInd typeDesc optPar ']' '(' optInd expr optPar ')'
-symbol ::= '`' (KEYWORD | IDENT | operator | '(' ')' | '[' ']' | '{' '}'
-               | '=' | literal)+ '`'
-         | IDENT
-         
-primaryPrefix ::= (prefixOperator | 'bind') optInd
-primarySuffix ::= '.' optInd symbol [generalizedLit]
-                | '(' optInd namedExprList optPar ')'
-                | '[' optInd [indexExpr (comma indexExpr)* [comma]] optPar ']'
-                | '{' optInd [indexExpr (comma indexExpr)* [comma]] optPar '}'
-
-primary ::= primaryPrefix* (symbol [generalizedLit] | 
-                            constructor | castExpr)
-            primarySuffix*
-
-generalizedLit ::= GENERALIZED_STR_LIT | GENERALIZED_TRIPLESTR_LIT
-
-literal ::= 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
-
-constructor ::= literal
-          | '[' optInd colonExprList optPar ']'
-          | '{' optInd ':' | colonExprList optPar '}'
-          | '(' optInd colonExprList optPar ')'
-
-colonExpr ::= expr [':' expr]
-colonExprList ::= [colonExpr (comma colonExpr)* [comma]]
-
-namedExpr ::= expr ['=' expr]
-namedExprList ::= [namedExpr (comma namedExpr)* [comma]]
-
-exprOrType ::= lowestExpr
-            | 'if' expr ':' expr ('elif' expr ':' expr)* 'else' ':' expr
-            | 'var' exprOrType
-            | 'ref' exprOrType
-            | 'ptr' exprOrType
-            | 'type' exprOrType
-            | 'tuple' tupleDesc
-
-expr ::= exprOrType
-     | 'proc' paramList [pragma] ['=' stmt]
-     | 'iterator' paramList [pragma] ['=' stmt]
-
-exprList ::= [expr (comma expr)* [comma]]
-
-
-qualifiedIdent ::= symbol ['.' symbol]
-
-typeDesc ::= (exprOrType
-         | 'proc' paramList [pragma]
-         | 'iterator' paramList [pragma] ) 
-           ['not' expr]  # for now only 'not nil' suffix is supported
-
-macroStmt ::= ':' [stmt] ('of' [exprList] ':' stmt
-                         |'elif' expr ':' stmt
-                         |'except' exceptList ':' stmt )*
-                         ['else' ':' stmt]
-
-pragmaBlock ::= pragma [':' stmt]
-
-simpleStmt ::= returnStmt
-             | yieldStmt
-             | discardStmt
-             | raiseStmt
-             | breakStmt
-             | continueStmt
-             | pragmaBlock
-             | importStmt
-             | fromStmt
-             | includeStmt
-             | exprStmt
-complexStmt ::= ifStmt | whileStmt | caseStmt | tryStmt | forStmt
-              | blockStmt | staticStmt | asmStmt
-              | procDecl | iteratorDecl | macroDecl | templateDecl | methodDecl
-              | constSection | letSection | varSection
-              | typeSection | whenStmt | bindStmt
-
-indPush ::= IND # and push indentation onto the stack
-indPop ::= # pop indentation from the stack
-
-stmt ::= simpleStmt [SAD]
- | indPush (complexStmt | simpleStmt)
-  ([SAD] (complexStmt | simpleStmt))*
-   DED indPop
-
-exprStmt ::= lowestExpr ['=' expr | [expr (comma expr)*] [macroStmt]]
-returnStmt ::= 'return' [expr]
-yieldStmt ::= 'yield' expr
-discardStmt ::= 'discard' expr
-raiseStmt ::= 'raise' [expr]
-breakStmt ::= 'break' [symbol]
-continueStmt ::= 'continue'
-ifStmt ::= 'if' expr ':' stmt ('elif' expr ':' stmt)* ['else' ':' stmt]
-whenStmt ::= 'when' expr ':' stmt ('elif' expr ':' stmt)* ['else' ':' stmt]
-caseStmt ::= 'case' expr [':'] ('of' exprList ':' stmt)*
-                               ('elif' expr ':' stmt)*
-                               ['else' ':' stmt]
-whileStmt ::= 'while' expr ':' stmt
-forStmt ::= 'for' symbol (comma symbol)* 'in' expr ':' stmt
-exceptList ::= [qualifiedIdent (comma qualifiedIdent)*]
-
-tryStmt ::= 'try' ':' stmt
-           ('except' exceptList ':' stmt)*
-           ['finally' ':' stmt]
-asmStmt ::= 'asm' [pragma] (STR_LIT | RSTR_LIT | TRIPLESTR_LIT)
-blockStmt ::= 'block' [symbol] ':' stmt
-staticStmt ::= 'static' ':' stmt
-filename ::= symbol | STR_LIT | RSTR_LIT | TRIPLESTR_LIT
-importStmt ::= 'import' filename (comma filename)*
-includeStmt ::= 'include' filename (comma filename)*
-bindStmt ::= 'bind' qualifiedIdent (comma qualifiedIdent)*
-fromStmt ::= 'from' filename 'import' symbol (comma symbol)*
-
-pragma ::= '{.' optInd (colonExpr [comma])* optPar ('.}' | '}')
-
-param ::= symbol (comma symbol)* (':' typeDesc ['=' expr] | '=' expr)
-paramList ::= ['(' [param (comma|semicolon param)*] optPar ')'] [':' typeDesc]
-
-genericConstraint ::= 'object' | 'tuple' | 'enum' | 'proc' | 'ref' | 'ptr' 
-                    | 'var' | 'distinct' | 'iterator' | primary
-genericConstraints ::= genericConstraint ( '|' optInd genericConstraint )*
-
-genericParam ::= symbol [':' genericConstraints] ['=' expr]
-genericParams ::= '[' genericParam (comma|semicolon genericParam)* optPar ']'
-
-
-routineDecl := symbol ['*'] [genericParams] paramList [pragma] ['=' stmt]
-procDecl ::= 'proc' routineDecl
-macroDecl ::= 'macro' routineDecl
-iteratorDecl ::= 'iterator' routineDecl
-templateDecl ::= 'template' routineDecl
-methodDecl ::= 'method' routineDecl
-
-colonAndEquals ::= [':' typeDesc] '=' expr
-
-constDecl ::= symbol ['*'] [pragma] colonAndEquals [COMMENT | IND COMMENT]
-            | COMMENT
-constSection ::= 'const' indPush constDecl (SAD constDecl)* DED indPop
-letSection ::= 'let' indPush constDecl (SAD constDecl)* DED indPop
-
-typeDef ::= typeDesc | objectDef | enumDef | 'distinct' typeDesc
-
-objectField ::= symbol ['*'] [pragma]
-objectIdentPart ::= objectField (comma objectField)* ':' typeDesc
-                    [COMMENT|IND COMMENT]
-
-objectWhen ::= 'when' expr ':' [COMMENT] objectPart
-              ('elif' expr ':' [COMMENT] objectPart)*
-              ['else' ':' [COMMENT] objectPart]
-objectCase ::= 'case' expr ':' typeDesc [COMMENT]
-              ('of' exprList ':' [COMMENT] objectPart)*
-              ['else' ':' [COMMENT] objectPart]
-
-objectPart ::= objectWhen | objectCase | objectIdentPart | 'nil'
-             | indPush objectPart (SAD objectPart)* DED indPop
-tupleDesc ::= '[' optInd [param (comma|semicolon param)*] optPar ']'
-
-objectDef ::= 'object' [pragma] ['of' typeDesc] objectPart
-enumField ::= symbol ['=' expr]
-enumDef ::= 'enum' (enumField [comma] [COMMENT | IND COMMENT])+
-
-typeDecl ::= COMMENT
-           | symbol ['*'] [genericParams] ['=' typeDef] [COMMENT | IND COMMENT]
-
-typeSection ::= 'type' indPush typeDecl (SAD typeDecl)* DED indPop
-
-colonOrEquals ::= ':' typeDesc ['=' expr] | '=' expr
-varField ::= symbol ['*'] [pragma]
-varPart ::= symbol (comma symbol)* colonOrEquals [COMMENT | IND COMMENT]
-varSection ::= 'var' (varPart
-                   | indPush (COMMENT|varPart)
-                     (SAD (COMMENT|varPart))* DED indPop)
+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)*
+symbol = '`' (KEYW|IDENT|operator|'(' ')'|'[' ']'|'{' '}'|'='|literal)+ '`'
+       | IDENT
+indexExpr = expr
+indexExprList = indexExpr ^+ comma
+exprColonEqExpr = expr (':'|'=' expr)?
+exprList = expr ^+ comma
+dotExpr = expr '.' optInd ('type' | 'addr' | symbol)
+qualifiedIdent = symbol ('.' optInd ('type' | 'addr' | symbol))?
+exprColonEqExprList = exprColonEqExpr (comma exprColonEqExpr)* (comma)?
+setOrTableConstr = '{' ((exprColonEqExpr comma)* | ':' ) '}'
+castExpr = 'cast' '[' optInd typeDesc optPar ']' '(' optInd expr optPar ')'
+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 ')'
+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 ']'
+primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
+              | doBlocks
+              | '.' optInd ('type' | 'addr' | symbol) generalizedLit?
+              | '[' optInd indexExprList optPar ']'
+              | '{' optInd indexExprList optPar '}'
+condExpr = expr colcom expr optInd
+        ('elif' expr colcom expr optInd)*
+         'else' colcom expr
+ifExpr = 'if' condExpr
+whenExpr = 'when' condExpr
+pragma = '{.' optInd (exprColonExpr comma?)* optPar ('.}' | '}')
+identVis = symbol opr?  # postfix position
+identWithPragma = identVis pragma?
+declColonEquals = identWithPragma (comma identWithPragma)* comma?
+                  (':' optInd typeDesc)? ('=' optInd expr)?
+identColonEquals = ident (comma ident)* comma?
+     (':' optInd typeDesc)? ('=' optInd expr)?)
+inlTupleDecl = 'tuple'
+    [' optInd  (identColonEquals (comma/semicolon)?)*  optPar ']'
+extTupleDecl = 'tuple'
+    COMMENT? (IND{>} identColonEquals (IND{=} identColonEquals)*)?
+paramList = '(' identColonEquals ^* (comma/semicolon) ')'
+paramListArrow = paramList? ('->' optInd typeDesc)?
+paramListColon = paramList? (':' optInd typeDesc)?
+doBlock = 'do' paramListArrow pragmas? colcom stmt
+doBlocks = doBlock ^* IND{=}
+procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)?
+expr = (ifExpr
+      | whenExpr
+      | caseExpr
+      | tryStmt)
+      / simpleExpr
+typeKeyw = 'var' | 'ref' | 'ptr' | 'shared' | 'type' | 'tuple'
+         | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum'
+primary = typeKeyw typeDescK
+        /  prefixOperator* identOrLiteral primarySuffix*
+        / 'addr' primary
+        / 'static' primary
+        / 'bind' primary
+typeDesc = simpleExpr
+typeDefAux = simpleExpr
+exprStmt = simpleExpr
+         (( '=' optInd expr )
+         / ( expr ^+ comma
+             doBlocks
+              / ':' stmt? ( IND{=} 'of' exprList ':' stmt 
+                          | IND{=} 'elif' expr ':' stmt
+                          | IND{=} 'except' exprList ':' stmt
+                          | IND{=} 'else' ':' stmt )*
+           ))?
+importStmt = 'import' optInd expr
+              ((comma expr)*
+              / 'except' optInd (expr ^+ comma))
+includeStmt = 'include' optInd expr ^+ comma
+fromStmt = 'from' expr 'import' optInd expr (comma expr)*
+returnStmt = 'return' optInd expr?
+raiseStmt = 'raise' optInd expr?
+yieldStmt = 'yield' optInd expr?
+discardStmt = 'discard' optInd expr?
+breakStmt = 'break' optInd expr?
+continueStmt = 'break' optInd expr?
+condStmt = expr colcom stmt COMMENT?
+           (IND{=} 'elif' expr colcom stmt)*
+           (IND{=} 'else' colcom stmt)?
+ifStmt = 'if' condStmt
+whenStmt = 'when' condStmt
+whileStmt = 'while' expr colcom stmt
+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)
+tryStmt = 'try' colcom stmt &(IND{=}? 'except'|'finally')
+           (IND{=}? 'except' exprList colcom stmt)*
+           (IND{=}? 'finally' colcom stmt)?
+exceptBlock = 'except' colcom stmt
+forStmt = 'for' symbol (comma symbol)* 'in' expr colcom stmt
+blockStmt = 'block' symbol? colcom stmt
+staticStmt = 'static' colcom stmt
+asmStmt = 'asm' pragma? (STR_LIT | RSTR_LIT | TRIPLE_STR_LIT)
+genericParam = symbol (comma symbol)* (colon expr)? ('=' optInd expr)?
+genericParamList = '[' optInd
+  genericParam ^* (comma/semicolon) optPar ']'
+pattern = '{' stmt '}'
+indAndComment = (IND{>} COMMENT)? | COMMENT?
+routine = optInd identVis pattern? genericParamList?
+  paramListColon pragma? ('=' COMMENT? stmt)? indAndComment
+commentStmt = COMMENT
+section(p) = COMMENT? p / (IND{>} (p / COMMENT)^+IND{=} DED)
+constant = identWithPragma (colon typedesc)? '=' optInd expr indAndComment
+enum = 'enum' optInd (symbol optInd ('=' optInd expr COMMENT?)? comma?)+
+objectWhen = 'when' expr colcom objectPart COMMENT?
+            ('elif' expr colcom objectPart COMMENT?)*
+            ('else' colcom objectPart COMMENT?)?
+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)
+objectPart = IND{>} objectPart^+IND{=} DED
+           / objectWhen / objectCase / 'nil' / declColonEquals
+object = 'object' pragma? ('of' typeDesc)? COMMENT? objectPart
+distinct = 'distinct' optInd typeDesc
+typeDef = identWithPragma genericParamList? '=' optInd typeDefAux
+            indAndComment?
+varTuple = '(' optInd identWithPragma ^+ comma optPar ')' '=' optInd expr
+variable = (varTuple / identColonEquals) indAndComment
+bindStmt = 'bind' optInd qualifiedIdent ^+ comma
+mixinStmt = 'mixin' optInd qualifiedIdent ^+ comma
+pragmaStmt = pragma (':' COMMENT? stmt)?
+simpleStmt = ((returnStmt | raiseStmt | yieldStmt | discardStmt | breakStmt
+           | continueStmt | pragmaStmt | importStmt | exportStmt | fromStmt
+           | includeStmt | commentStmt) / exprStmt) COMMENT?
+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
+stmt = (IND{>} complexOrSimpleStmt^+(IND{=} / ';') DED)
+     / simpleStmt
diff --git a/doc/manual.txt b/doc/manual.txt
index 8ca7b697e..132f6d038 100644
--- a/doc/manual.txt
+++ b/doc/manual.txt
@@ -23,14 +23,28 @@ This document describes the lexis, the syntax, and the semantics of Nimrod.
 
 The language constructs are explained using an extended BNF, in
 which ``(a)*`` means 0 or more ``a``'s, ``a+`` means 1 or more ``a``'s, and
-``(a)?`` means an optional *a*; an alternative spelling for optional parts is
-``[a]``. The ``|`` symbol is used to mark alternatives
-and has the lowest precedence. Parentheses may be used to group elements.
+``(a)?`` means an optional *a*. Parentheses may be used to group elements.
+
+``&`` is the lookahead operator; ``&a`` means that an ``a`` is expected but
+not consumed. It will be consumed in the following rule.
+
+The ``|``, ``/`` symbols are used to mark alternatives and have the lowest 
+precedence. ``/`` is the ordered choice that requires the parser to try the 
+alternatives in the given order. ``/`` is often used to ensure the grammar
+is not ambiguous. 
+
 Non-terminals start with a lowercase letter, abstract terminal symbols are in
 UPPERCASE. Verbatim terminal symbols (including keywords) are quoted
 with ``'``. An example::
 
-  ifStmt ::= 'if' expr ':' stmts ('elif' expr ':' stmts)* ['else' stmts]
+  ifStmt = 'if' expr ':' stmts ('elif' expr ':' stmts)* ('else' stmts)?
+  
+The binary ``^*`` operator is used as a shorthand for 0 or more occurances
+separated by its second argument; likewise ``^+`` means 1 or more 
+occurances: ``a ^+ b`` is short for ``a (b a)*`` 
+and ``a ^* b`` is short for ``(a (b a)*)?``. Example::
+
+  arrayConstructor = '[' expr ^* ',' ']'
 
 Other parts of Nimrod - like scoping rules or runtime semantics are only
 described in an informal manner for now.
@@ -50,7 +64,7 @@ An `identifier`:idx: is a symbol declared as a name for a variable, type,
 procedure, etc. The region of the program over which a declaration applies is
 called the `scope`:idx: of the declaration. Scopes can be nested. The meaning
 of an identifier is determined by the smallest enclosing scope in which the
-identifier is declared.
+identifier is declared unless overloading resolution rules suggest otherwise.
 
 An expression specifies a computation that produces a value or location.
 Expressions that produce locations are called `l-values`:idx:. An l-value
@@ -93,28 +107,31 @@ Nimrod's standard grammar describes an `indentation sensitive`:idx: language.
 This means that all the control structures are recognized by indentation.
 Indentation consists only of spaces; tabulators are not allowed.
 
-The terminals ``IND`` (indentation), ``DED`` (dedentation) and ``SAD``
-(same indentation) are generated by the scanner, denoting an indentation.
+The indentation handling is implemented as follows: The lexer annotates the
+following token with the preceeding number of spaces; indentation is not
+a separate token. This trick allows parsing of Nimrod with only 1 token of
+lookahead.
+
+The parser uses a stack of indentation levels: the stack consists of integers
+counting the spaces. The indentation information is queried at strategic 
+places in the parser but ignored otherwise: The pseudo terminal ``IND{>}``
+denotes an indentation that consists of more spaces than the entry at the top
+of the stack; IND{=} an indentation that has the same number of spaces. ``DED``
+is another pseudo terminal that describes the *action* of popping a value
+from the stack, ``IND{>}`` then implies to push onto the stack.
 
-These terminals are only generated for lines that are not empty.
+With this notation we can now easily define the core of the grammar: A block of
+statements (simplified example)::
 
-The parser and the scanner communicate over a stack which indentation terminal
-should be generated: the stack consists of integers counting the spaces. The
-stack is initialized with a zero on its top. The scanner reads from the stack:
-If the current indentation token consists of more spaces than the entry at the
-top of the stack, a ``IND`` token is generated, else if it consists of the same
-number of spaces, a ``SAD`` token is generated. If it consists of fewer spaces,
-a ``DED`` token is generated for any item on the stack that is greater than the
-current. These items are later popped from the stack by the parser. At the end
-of the file, a ``DED`` token is generated for each number remaining on the
-stack that is larger than zero.
+  ifStmt = 'if' expr ':' stmt
+           (IND{=} 'elif' expr ':' stmt)* 
+           (IND{=} 'else' ':' stmt)?
+
+  simpleStmt = ifStmt / ...
+
+  stmt = IND{>} stmt ^+ IND{=} DED  # list of statements
+       / simpleStmt                 # or a simple statement
 
-Because the grammar contains some optional ``IND`` tokens, the scanner cannot
-push new indentation levels. This has to be done by the parser. The symbol
-``indPush`` indicates that an ``IND`` token is expected; the current number of
-leading spaces is pushed onto the stack by the parser. The symbol ``indPop``
-denotes that the parser pops an item from the indentation stack. No token is
-consumed by ``indPop``.
 
 
 Comments
@@ -416,11 +433,11 @@ and not the two tokens `{.`:tok:, `.}`:tok:.
 Syntax
 ======
 
-This section lists Nimrod's standard syntax in ENBF. How the parser receives
-indentation tokens is already described in the `Lexical Analysis`_ section.
+This section lists Nimrod's standard syntax. How the parser handles
+the indentation is already described in the `Lexical Analysis`_ section.
 
 Nimrod allows user-definable operators.
-Binary operators have 10 different levels of precedence. 
+Binary operators have 10 different levels of precedence.
 
 Relevant character
 ------------------
@@ -1040,7 +1057,7 @@ an ``object`` type or a ``ref object`` type:
 .. code-block:: nimrod
   var student = TStudent(name: "Anton", age: 5, id: 3)
 
-For a ``ref object`` type ``new`` is invoked implicitly.
+For a ``ref object`` type ``system.new`` is invoked implicitly.
 
 
 Object variants
@@ -1701,43 +1718,30 @@ Statements and expressions
 ==========================
 
 Nimrod uses the common statement/expression paradigm: `Statements`:idx: do not
-produce a value in contrast to expressions. Call expressions are statements.
-If the called procedure returns a value, it is not a valid statement
-as statements do not produce values. To evaluate an expression for
-side-effects and throw its value away, one can use the ``discard`` statement.
+produce a value in contrast to expressions. However, some expressions are 
+statements.
 
 Statements are separated into `simple statements`:idx: and
 `complex statements`:idx:.
 Simple statements are statements that cannot contain other statements like
 assignments, calls or the ``return`` statement; complex statements can
 contain other statements. To avoid the `dangling else problem`:idx:, complex
-statements always have to be intended::
-
-  simpleStmt ::= returnStmt
-             | yieldStmt
-             | discardStmt
-             | raiseStmt
-             | breakStmt
-             | continueStmt
-             | pragma
-             | importStmt
-             | fromStmt
-             | includeStmt
-             | exprStmt
-  complexStmt ::= ifStmt | whileStmt | caseStmt | tryStmt | forStmt
-                   | blockStmt | asmStmt
-                   | procDecl | iteratorDecl | macroDecl | templateDecl
-                   | constSection | letSection 
-                   | typeSection | whenStmt | varSection
+statements always have to be intended. The details can be found in the grammar.
 
 
+Statement list expression
+-------------------------
 
-Discard statement
------------------
+Statements can also occur in an expression context that looks 
+like ``(stmt1; stmt2; ...; ex)``. This is called
+an `statement list expression`:idx: or ``(;)``. The type 
+of ``(stmt1; stmt2; ...; ex)`` is the type of ``ex``. All the other statements
+must be of type ``void``. (One can use ``discard`` to produce a ``void`` type.)
+``(;)`` does not introduce a new scope.
 
-Syntax::
 
-  discardStmt ::= 'discard' expr
+Discard statement
+-----------------
 
 Example:
 
@@ -1766,16 +1770,6 @@ been declared with the `discardable`:idx: pragma:
 Var statement
 -------------
 
-Syntax::
-
-  colonOrEquals ::= ':' typeDesc ['=' expr] | '=' expr
-  varField ::= symbol ['*'] [pragma]
-  varPart ::= symbol (comma symbol)* [comma] colonOrEquals [COMMENT | IND COMMENT]
-  varSection ::= 'var' (varPart
-                     | indPush (COMMENT|varPart)
-                       (SAD (COMMENT|varPart))* DED indPop)
-
-
 `Var`:idx: statements declare new local and global variables and
 initialize them. A comma separated list of variables can be used to specify
 variables of the same type:
@@ -1839,14 +1833,6 @@ For let variables the same pragmas are available as for ordinary variables.
 Const section
 -------------
 
-Syntax::
-
-  colonAndEquals ::= [':' typeDesc] '=' expr
-
-  constDecl ::= symbol ['*'] [pragma] colonAndEquals [COMMENT | IND COMMENT]
-              | COMMENT
-  constSection ::= 'const' indPush constDecl (SAD constDecl)* DED indPop
-
 `Constants`:idx: are symbols which are bound to a value. The constant's value
 cannot change. The compiler must be able to evaluate the expression in a
 constant declaration at compile time.
@@ -1877,10 +1863,6 @@ they contain such a type.
 Static statement/expression
 ---------------------------
 
-Syntax::
-  staticExpr ::= 'static' '(' optInd expr optPar ')'
-  staticStmt ::= 'static' ':' stmt
-  
 A `static`:idx: statement/expression can be used to enforce compile 
 time evaluation explicitly. Enforced compile time evaluation can even evaluate
 code that has side effects: 
@@ -1902,10 +1884,6 @@ support the FFI at compile time.
 If statement
 ------------
 
-Syntax::
-
-  ifStmt ::= 'if' expr ':' stmt ('elif' expr ':' stmt)* ['else' ':' stmt]
-
 Example:
 
 .. code-block:: nimrod
@@ -1928,15 +1906,25 @@ the ``:`` are executed. This goes on until the last ``elif``. If all
 conditions fail, the ``else`` part is executed. If there is no ``else``
 part, execution continues with the statement after the ``if`` statement.
 
+The scoping for an ``if`` statement is slightly subtle to support an important 
+use case. A new scope starts for the ``if``/``elif`` condition and ends after
+the corresponding *then* block:
 
-Case statement
---------------
+.. code-block:: nimrod
+  if {| (let m = input =~ re"(\w+)=\w+"; m.isMatch):
+    echo "key ", m[0], " value ", m[1]  |}
+  elif {| (let m = input =~ re""; m.isMatch):
+    echo "new m in this scope" |}
+  else:
+    # 'm' not declared here
 
-Syntax::
+In the example the scopes have been enclosed in ``{|  |}``. 
 
-  caseStmt ::= 'case' expr [':'] ('of' sliceExprList ':' stmt)*
-                                 ('elif' expr ':' stmt)*
-                                 ['else' ':' stmt]
+**Note**: These scoping rules will be active in 0.9.4.
+
+
+Case statement
+--------------
 
 Example:
 
@@ -1998,10 +1986,6 @@ a list of its elements:
 When statement
 --------------
 
-Syntax::
-
-  whenStmt ::= 'when' expr ':' stmt ('elif' expr ':' stmt)* ['else' ':' stmt]
-
 Example:
 
 .. code-block:: nimrod
@@ -2032,10 +2016,6 @@ within ``object`` definitions.
 Return statement
 ----------------
 
-Syntax::
-
-  returnStmt ::= 'return' [expr]
-
 Example:
 
 .. code-block:: nimrod
@@ -2063,10 +2043,6 @@ variables, ``result`` is initialized to (binary) zero:
 Yield statement
 ---------------
 
-Syntax::
-
-  yieldStmt ::= 'yield' expr
-
 Example:
 
 .. code-block:: nimrod
@@ -2083,10 +2059,6 @@ for further information.
 Block statement
 ---------------
 
-Syntax::
-
-  blockStmt ::= 'block' [symbol] ':' stmt
-
 Example:
 
 .. code-block:: nimrod
@@ -2108,10 +2080,6 @@ block to specify which block is to leave.
 Break statement
 ---------------
 
-Syntax::
-
-  breakStmt ::= 'break' [symbol]
-
 Example:
 
 .. code-block:: nimrod
@@ -2125,10 +2093,6 @@ absent, the innermost block is left.
 While statement
 ---------------
 
-Syntax::
-
-  whileStmt ::= 'while' expr ':' stmt
-
 Example:
 
 .. code-block:: nimrod
@@ -2147,10 +2111,6 @@ so that they can be left with a ``break`` statement.
 Continue statement
 ------------------
 
-Syntax::
-
-  continueStmt ::= 'continue'
-
 A `continue`:idx: statement leads to the immediate next iteration of the
 surrounding loop construct. It is only allowed within a loop. A continue
 statement is syntactic sugar for a nested block:
@@ -2173,9 +2133,6 @@ Is equivalent to:
 
 Assembler statement
 -------------------
-Syntax::
-
-  asmStmt ::= 'asm' [pragma] (STR_LIT | RSTR_LIT | TRIPLESTR_LIT)
 
 The direct embedding of `assembler`:idx: code into Nimrod code is supported
 by the unsafe ``asm`` statement. Identifiers in the assembler code that refer to
@@ -2203,8 +2160,7 @@ Example:
   var y = if x > 8: 9 else: 10
 
 An if expression always results in a value, so the ``else`` part is
-required. ``Elif`` parts are also allowed (but unlikely to be good
-style).
+required. ``Elif`` parts are also allowed.
 
 When expression
 ---------------
@@ -2311,18 +2267,8 @@ procedure declaration defines an identifier and associates it with a block
 of code. 
 A procedure may call itself recursively. A parameter may be given a default
 value that is used if the caller does not provide a value for this parameter.
-The syntax is::
-
-  param ::= symbol (comma symbol)* (':' typeDesc ['=' expr] | '=' expr)
-  paramList ::= ['(' [param (comma param)*] [SAD] ')'] [':' typeDesc]
-
-  genericParam ::= symbol [':' typeDesc] ['=' expr]
-  genericParams ::= '[' genericParam (comma genericParam)* [SAD] ']'
 
-  procDecl ::= 'proc' symbol ['*'] [genericParams] paramList [pragma]
-               ['=' stmt]
-
-If the ``= stmt`` part is missing, it is a `forward`:idx: declaration. If
+If the proc declaration has no body, it is a `forward`:idx: declaration. If
 the proc returns a value, the procedure body can access an implicitly declared
 variable named `result`:idx: that represents the return value. Procs can be
 overloaded. The overloading resolution algorithm tries to find the proc that is
@@ -2417,24 +2363,14 @@ Do notation
 As a special more convenient notation, proc expressions involved in procedure
 calls can use the ``do`` keyword:
 
-Syntax::
-  primarySuffix ::= 'do' ['(' namedExprList ')'] ['->' typeDesc] ':'
-
-As a start, let's repeat the example from the previous section:
-
 .. code-block:: nimrod
-  cities.sort do (x,y: string) -> int:
+  sort(cities) do (x,y: string) -> int:
     cmp(x.len, y.len)
 
 ``do`` is written after the parentheses enclosing the regular proc params. 
 The proc expression represented by the do block is appended to them.
-Again, let's see the equivalent of the previous example:
 
-.. code-block:: nimrod
-  sort(cities) do (x,y: string) -> int:
-    cmp(x.len, y.len)
-
-Finally, more than one ``do`` block can appear in a single call:
+More than one ``do`` block can appear in a single call:
 
 .. code-block:: nimrod
   proc performWithUndo(task: proc(), undo: proc()) = ...
@@ -2635,30 +2571,16 @@ evaluation or dead code elimination do not work with methods.
 Iterators and the for statement
 ===============================
 
-Syntax::
-
-  forStmt ::= 'for' symbol (comma symbol)* [comma] 'in' expr ':' stmt
-
-  param ::= symbol (comma symbol)* [comma] ':' typeDesc
-  paramList ::= ['(' [param (comma param)* [comma]] ')'] [':' typeDesc]
-
-  genericParam ::= symbol [':' typeDesc]
-  genericParams ::= '[' genericParam (comma genericParam)* [comma] ']'
-
-  iteratorDecl ::= 'iterator' symbol ['*'] [genericParams] paramList [pragma]
-               ['=' stmt]
-
 The `for`:idx: statement is an abstract mechanism to iterate over the elements
 of a container. It relies on an `iterator`:idx: to do so. Like ``while``
 statements, ``for`` statements open an `implicit block`:idx:, so that they
 can be left with a ``break`` statement. 
 
-The ``for`` loop declares
-iteration variables (``x`` in the example) - their scope reaches until the
+The ``for`` loop declares iteration variables - their scope reaches until the
 end of the loop body. The iteration variables' types are inferred by the
 return type of the iterator.
 
-An iterator is similar to a procedure, except that it is always called in the
+An iterator is similar to a procedure, except that it can be called in the
 context of a ``for`` loop. Iterators provide a way to specify the iteration over
 an abstract type. A key role in the execution of a ``for`` loop plays the
 ``yield`` statement in the called iterator. Whenever a ``yield`` statement is
@@ -2686,9 +2608,10 @@ The compiler generates code as if the programmer would have written this:
     echo(ch)
     inc(i)
 
-If the iterator yields a tuple, there have to be as many iteration variables
+If the iterator yields a tuple, there can be as many iteration variables
 as there are components in the tuple. The i'th iteration variable's type is
-the type of the i'th component.
+the type of the i'th component. In other words, implicit tuple unpacking in a 
+for loop context is supported.
 
 
 Implict items/pairs invocations
@@ -2792,23 +2715,10 @@ iterator that has already finished its work.
 Type sections
 =============
 
-Syntax::
-
-  typeDef ::= typeDesc | objectDef | enumDef
-
-  genericParam ::= symbol [':' typeDesc]
-  genericParams ::= '[' genericParam (comma genericParam)* [comma] ']'
-
-  typeDecl ::= COMMENT
-             | symbol ['*'] [genericParams] ['=' typeDef] [COMMENT|IND COMMENT]
-
-  typeSection ::= 'type' indPush typeDecl (SAD typeDecl)* DED indPop
-
-
 Example:
 
 .. code-block:: nimrod
-  type # example demonstrates mutually recursive types
+  type # example demonstrating mutually recursive types
     PNode = ref TNode # a traced pointer to a TNode
     TNode = object
       le, ri: PNode   # left and right subtrees
@@ -2822,7 +2732,8 @@ Example:
 A `type`:idx: section begins with the ``type`` keyword. It contains multiple
 type definitions. A type definition binds a type to a name. Type definitions
 can be recursive or even mutually recursive. Mutually recursive types are only
-possible within a single ``type`` section.
+possible within a single ``type`` section. Nominal types like ``objects`` 
+or ``enums`` can only be defined in a ``type`` section.
 
 
 Exception handling
@@ -2831,14 +2742,6 @@ Exception handling
 Try statement
 -------------
 
-Syntax::
-
-  qualifiedIdent ::= symbol ['.' symbol]
-  exceptList ::= [qualifiedIdent (comma qualifiedIdent)* [comma]]
-  tryStmt ::= 'try' ':' stmt
-             ('except' exceptList ':' stmt)*
-             ['finally' ':' stmt]
-
 Example:
 
 .. code-block:: nimrod
@@ -2863,15 +2766,14 @@ Example:
       close(f)
 
 
-
 The statements after the `try`:idx: are executed in sequential order unless
 an exception ``e`` is raised. If the exception type of ``e`` matches any
-of the list ``exceptlist`` the corresponding statements are executed.
+listed in an ``except`` clause the corresponding statements are executed.
 The statements following the ``except`` clauses are called
 `exception handlers`:idx:.
 
 The empty `except`:idx: clause is executed if there is an exception that is
-in no list. It is similar to an ``else`` clause in ``if`` statements.
+not listed otherwise. It is similar to an ``else`` clause in ``if`` statements.
 
 If there is a `finally`:idx: clause, it is always executed after the
 exception handlers.
@@ -2916,10 +2818,6 @@ statements. Example:
 Raise statement
 ---------------
 
-Syntax::
-
-  raiseStmt ::= 'raise' [expr]
-
 Example:
 
 .. code-block:: nimrod
@@ -2948,17 +2846,21 @@ This allows for a Lisp-like `condition system`:idx:\:
 .. code-block:: nimrod
   var myFile = open("broken.txt", fmWrite)
   try:
-    onRaise(proc (e: ref E_Base): bool =
+    onRaise do (e: ref E_Base)-> bool:
       if e of EIO:
         stdout.writeln "ok, writing to stdout instead"
       else:
         # do raise other exceptions:
         result = true
-    )
     myFile.writeln "writing to broken file"
   finally:
     myFile.close()
 
+``OnRaise`` can only *filter* raised exceptions, it cannot transform one
+exception into another. (Nor should ``onRaise`` raise an exception though 
+this is currently not enforced.) This restriction keeps the exception tracking
+analysis sound.
+
 
 Effect system
 =============
@@ -3447,10 +3349,6 @@ Symbol binding within templates happens after template instantiation:
 Bind statement
 --------------
 
-Syntax::
-  
-  bindStmt ::= 'bind' IDENT (comma IDENT)*
-
 Exporting a template is a often a leaky abstraction as it can depend on 
 symbols that are not visible from a client module. However, to compensate for
 this case, a `bind`:idx: statement can be used: It declares all identifiers
@@ -3715,18 +3613,11 @@ Statement Macros
 ----------------
 
 Statement macros are defined just as expression macros. However, they are
-invoked by an expression following a colon::
-
-  exprStmt ::= lowestExpr ['=' expr | [expr (comma expr)* [comma]] [macroStmt]]
-  macroStmt ::= ':' [stmt] ('of' [sliceExprList] ':' stmt
-                          | 'elif' expr ':' stmt
-                          | 'except' exceptList ':' stmt )*
-                           ['else' ':' stmt]
+invoked by an expression following a colon.
 
 The following example outlines a macro that generates a lexical analyzer from
 regular expressions:
 
-
 .. code-block:: nimrod
   import macros
 
@@ -3799,7 +3690,7 @@ instantiation type using the param name:
   var tree = new(TBinaryTree[int])
 
 When used with macros and .compileTime. procs on the other hand, the compiler
-don't need to instantiate the code multiple times, because types then can be
+does not need to instantiate the code multiple times, because types then can be
 manipulated using the unified internal symbol representation. In such context
 typedesc acts as any other type. One can create variables, store typedesc
 values inside containers and so on. For example, here is how one can create 
@@ -4358,13 +4249,6 @@ the compiler encounters any static error.
 Pragmas
 =======
 
-Syntax::
-
-  colonExpr ::= expr [':' expr]
-  colonExprList ::= [colonExpr (comma colonExpr)* [comma]]
-
-  pragma ::= '{.' optInd (colonExpr [comma])* [SAD] ('.}' | '}')
-
 Pragmas are Nimrod's method to give the compiler additional information /
 commands without introducing a massive number of new keywords. Pragmas are
 processed on the fly during semantic checking. Pragmas are enclosed in the
@@ -4411,10 +4295,10 @@ calls to any base class destructors in both user-defined and generated
 destructors.
 
 A destructor is attached to the type it destructs; expressions of this type
-can then only be used in *destructible contexts*:
+can then only be used in *destructible contexts* and as parameters:
 
 .. code-block:: nimrod
-  type  
+  type
     TMyObj = object
       x, y: int
       p: pointer
@@ -4425,9 +4309,15 @@ can then only be used in *destructible contexts*:
   proc open: TMyObj =
     result = TMyObj(x: 1, y: 2, p: alloc(3))
  
+  proc work(o: TMyObj) =
+    echo o.x
+    # No destructor invoked here for 'o' as 'o' is a parameter.
+
   proc main() =
     # destructor automatically invoked at the end of the scope:
     var x = open()
+    # valid: pass 'x' to some other proc:
+    work(x)
     
     # Error: usage of a type with a destructor in a non destructible context
     echo open()
@@ -4849,8 +4739,8 @@ a dynamic library (``.dll`` files for Windows, ``lib*.so`` files for UNIX).
 The non-optional argument has to be the name of the dynamic library:
 
 .. code-block:: Nimrod
-  proc gtk_image_new(): PGtkWidget {.
-    cdecl, dynlib: "libgtk-x11-2.0.so", importc.}
+  proc gtk_image_new(): PGtkWidget
+    {.cdecl, dynlib: "libgtk-x11-2.0.so", importc.}
 
 In general, importing a dynamic library does not require any special linker
 options or linking with import libraries. This also implies that no *devel*
@@ -4894,6 +4784,10 @@ strings, because they are precompiled.
 **Note**: Passing variables to the ``dynlib`` pragma will fail at runtime 
 because of order of initialization problems.
 
+**Note**: A ``dynlib`` import can be overriden with 
+the ``--dynlibOverride:name`` command line option. The Compiler User Guide
+contains further information.
+
 
 Dynlib pragma for export
 ------------------------
@@ -4971,7 +4865,7 @@ Nimrod supports the `actor model`:idx: of concurrency natively:
   type
     TMsgKind = enum
       mLine, mEof
-    TMsg = object {.pure, final.}
+    TMsg = object
       case k: TMsgKind
       of mEof: nil
       of mLine: data: string
diff --git a/doc/tut1.txt b/doc/tut1.txt
index 7c2a35f94..2ca2a8ddd 100644
--- a/doc/tut1.txt
+++ b/doc/tut1.txt
@@ -515,6 +515,13 @@ contain indentation at certain places for better readability:
 As a rule of thumb, indentation within expressions is allowed after operators,
 an open parenthesis and after commas.
 
+With parenthesis and semicolons ``(;)`` you can use statements where only 
+an expression is allowed:
+
+.. code-block:: nimrod
+  # computes fac(4) at compile time:
+  const fac4 = (var x = 1; for i in 1..4: x *= i; x)
+
 
 Procedures
 ==========
diff --git a/lib/impure/re.nim b/lib/impure/re.nim
index 57dc3a313..ef02a3b1d 100644
--- a/lib/impure/re.nim
+++ b/lib/impure/re.nim
@@ -239,8 +239,9 @@ template `=~` *(s: string, pattern: TRegEx): expr =
   ##   else:
   ##     echo("syntax error")
   ##
+  bind maxSubPatterns
   when not definedInScope(matches):
-    var matches {.inject.}: array[0..re.maxSubPatterns-1, string]
+    var matches {.inject.}: array[0..maxSubPatterns-1, string]
   match(s, pattern, matches)
 
 # ------------------------- more string handling ------------------------------
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index ab6cb04a9..15cc6abb8 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -264,10 +264,10 @@ proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
   if r.scheme == "https":
     when defined(ssl):
       sslContext.wrapSocket(s)
+      port = TPort(443)
     else:
       raise newException(EHttpRequestErr,
                 "SSL support is not available. Cannot connect over SSL.")
-    port = TPort(443)
   if r.port != "":
     port = TPort(r.port.parseInt)
   
diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim
index 4e31ffc0c..240ea0945 100644
--- a/lib/pure/pegs.nim
+++ b/lib/pure/pegs.nim
@@ -867,8 +867,9 @@ template `=~`*(s: string, pattern: TPeg): bool =
   ##   else:
   ##     echo("syntax error")
   ##  
+  bind maxSubpatterns
   when not definedInScope(matches):
-    var matches {.inject.}: array[0..pegs.maxSubpatterns-1, string]
+    var matches {.inject.}: array[0..maxSubpatterns-1, string]
   match(s, pattern, matches)
 
 # ------------------------- more string handling ------------------------------
@@ -1740,8 +1741,10 @@ when isMainModule:
     assert matches[0] == "a"
   else:
     assert false
-    
-  if match("abcdefg", peg"c {d} ef {g}", matches, 2): 
+
+  when not definedInScope(matches):
+    var matches: array[0..maxSubpatterns-1, string]
+  if match("abcdefg", peg"c {d} ef {g}", matches, 2):
     assert matches[0] == "d"
     assert matches[1] == "g"
   else:
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index 03a05aea1..b57b9e959 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -463,6 +463,8 @@ when not defined(JS):
     
 elif defined(JS):
   proc newDate(): TTime {.importc: "new Date".}
+  proc internGetTime(): TTime {.importc: "new Date", tags: [].}
+  
   proc newDate(value: float): TTime {.importc: "new Date".}
   proc newDate(value: string): TTime {.importc: "new Date".}
   proc getTime(): TTime =
@@ -494,7 +496,7 @@ elif defined(JS):
     result.yearday = 0
   
   proc TimeInfoToTime*(timeInfo: TTimeInfo): TTime =
-    result = getTime()
+    result = internGetTime()
     result.setSeconds(timeInfo.second)
     result.setMinutes(timeInfo.minute)
     result.setHours(timeInfo.hour)
diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim
index 1c43bfdc7..850dd1e11 100644
--- a/lib/system/jssys.nim
+++ b/lib/system/jssys.nim
@@ -620,4 +620,9 @@ proc isObj(obj, subclass: PNimType): bool {.compilerproc.} =
     x = x.base
   return true
 
+proc addChar(x: string, c: char) {.compilerproc, noStackFrame.} =
+  asm """
+    `x`[`x`.length-1] = `c`; `x`.push(0);
+  """
+
 {.pop.}
diff --git a/tests/compile/tcommontype.nim b/tests/compile/tcommontype.nim
new file mode 100644
index 000000000..8215ebd5e
--- /dev/null
+++ b/tests/compile/tcommontype.nim
@@ -0,0 +1,20 @@
+type
+  TAnimal=object {.inheritable.}
+  PAnimal=ref TAnimal
+
+  TDog=object of TAnimal
+  PDog=ref TDog
+
+  TCat=object of TAnimal
+  PCat=ref TCat
+
+  TAnimalArray=array[0..2,PAnimal]
+
+proc newDog():PDog = new(result)
+proc newCat():PCat = new(result)
+proc test(a:openarray[PAnimal])=
+  echo("dummy")
+
+#test(newDog(),newCat()) #does not work
+var myarray:TAnimalArray=[newDog(),newCat(),newDog()] #does not work
+#var myarray2:TAnimalArray=[newDog(),newDog(),newDog()] #does not work either
diff --git a/tests/manyloc/argument_parser/ex_wget.nim b/tests/manyloc/argument_parser/ex_wget.nim
new file mode 100644
index 000000000..d36947b34
--- /dev/null
+++ b/tests/manyloc/argument_parser/ex_wget.nim
@@ -0,0 +1,87 @@
+import argument_parser, tables, strutils, parseutils
+
+## Example defining a subset of wget's functionality
+
+const
+  PARAM_VERSION = @["-V", "--version"]
+  PARAM_HELP = @["-h", "--help"]
+  PARAM_BACKGROUND = @["-b", "--background"]
+  PARAM_OUTPUT = @["-o", "--output"]
+  PARAM_NO_CLOBBER = @["-nc", "--no-clobber"]
+  PARAM_PROGRESS = "--progress"
+  PARAM_NO_PROXY = "--no-proxy"
+
+
+template P(tnames: varargs[string], thelp: string, ttype = PK_EMPTY,
+    tcallback: Tparameter_callback = nil) =
+  ## Helper to avoid repetition of parameter adding boilerplate.
+  params.add(new_parameter_specification(ttype, custom_validator = tcallback,
+    help_text = thelp, names = tnames))
+
+
+template got(param: varargs[string]) =
+  ## Just dump the detected options on output.
+  if result.options.hasKey(param[0]): echo("Found option '$1'." % [param[0]])
+
+
+proc parse_progress(parameter: string; value: var Tparsed_parameter): string =
+  ## Custom parser and validator of progress types for PARAM_PROGRESS.
+  ##
+  ## If the user specifies the PARAM_PROGRESS option this proc will be called
+  ## so we can validate the input. The proc returns a non empty string if
+  ## something went wrong with the description of the error, otherwise
+  ## execution goes ahead.
+  ##
+  ## This validator only accepts values without changing the final output.
+  if value.str_val == "bar" or value.str_val == "dot":
+    return
+
+  result = "The string $1 is not valid, use bar or dot." % [value.str_val]
+
+
+proc process_commandline(): Tcommandline_results =
+  ## Parses the commandline.
+  ##
+  ## Returns a Tcommandline_results with at least two positional parameter,
+  ## where the last parameter is implied to be the destination of the copying.
+  var params: seq[Tparameter_specification] = @[]
+
+  P(PARAM_VERSION, "Shows the version of the program")
+  P(PARAM_HELP, "Shows this help on the commandline", PK_HELP)
+  P(PARAM_BACKGROUND, "Continues execution in the background")
+  P(PARAM_OUTPUT, "Specifies a specific output file name", PK_STRING)
+  P(PARAM_NO_CLOBBER, "Skip downloads that would overwrite existing files")
+  P(PARAM_PROGRESS, "Select progress look (bar or dot)",
+    PK_STRING, parse_progress)
+  P(PARAM_NO_PROXY, "Don't use proxies even if available")
+
+  result = parse(params)
+
+  if result.positional_parameters.len < 1:
+    echo "Missing URL(s) to download"
+    echo_help(params)
+    quit()
+
+  got(PARAM_NO_CLOBBER)
+  got(PARAM_BACKGROUND)
+  got(PARAM_NO_PROXY)
+
+  if result.options.hasKey(PARAM_VERSION[0]):
+    echo "Version 3.1415"
+    quit()
+
+  if result.options.hasKey(PARAM_OUTPUT[0]):
+    if result.positional_parameters.len > 1:
+      echo "Error: can't use $1 option with multiple URLs" % [PARAM_OUTPUT[0]]
+      echo_help(params)
+      quit()
+    echo "Will download to $1" % [result.options[PARAM_OUTPUT[0]].str_val]
+
+  if result.options.hasKey(PARAM_PROGRESS):
+    echo "Will use progress type $1" % [result.options[PARAM_PROGRESS].str_val]
+
+
+when isMainModule:
+  let args = process_commandline()
+  for param in args.positional_parameters:
+    echo "Downloading $1" % param.str_val
diff --git a/tests/manyloc/argument_parser/ex_wget.nimrod.cfg b/tests/manyloc/argument_parser/ex_wget.nimrod.cfg
new file mode 100644
index 000000000..4ea571d31
--- /dev/null
+++ b/tests/manyloc/argument_parser/ex_wget.nimrod.cfg
@@ -0,0 +1 @@
+# This file exists only to mark 'ex_wget' as the project file
diff --git a/tests/manyloc/keineschweine/keineschweine.nimrod.cfg b/tests/manyloc/keineschweine/keineschweine.nimrod.cfg
index 048285ad9..ca6c75f6e 100644
--- a/tests/manyloc/keineschweine/keineschweine.nimrod.cfg
+++ b/tests/manyloc/keineschweine/keineschweine.nimrod.cfg
@@ -6,3 +6,4 @@ path = "dependencies/enet"
 path = "dependencies/genpacket"
 path = "enet_server"
 debugger = off
+warning[SmallLshouldNotBeUsed] = off
diff --git a/tests/manyloc/nake/nake.nim b/tests/manyloc/nake/nake.nim
new file mode 100644
index 000000000..eade28c70
--- /dev/null
+++ b/tests/manyloc/nake/nake.nim
@@ -0,0 +1,83 @@
+discard """
+DO AS THOU WILST PUBLIC LICENSE
+
+Whoever should stumble upon this document is henceforth and forever
+entitled to DO AS THOU WILST with aforementioned document and the
+contents thereof. 
+
+As said in the Olde Country, `Keepe it Gangster'."""
+
+import strutils, parseopt, tables, os
+
+type
+  PTask* = ref object
+    desc*: string
+    action*: TTaskFunction
+  TTaskFunction* = proc() {.closure.}
+var 
+  tasks* = initTable[string, PTask](16)
+
+proc newTask*(desc: string; action: TTaskFunction): PTask
+proc runTask*(name: string) {.inline.}
+proc shell*(cmd: varargs[string, `$`]): int {.discardable.}
+proc cd*(dir: string) {.inline.}
+
+template nakeImports*(): stmt {.immediate.} =
+  import tables, parseopt, strutils, os
+
+template task*(name: string; description: string; body: stmt): stmt {.dirty, immediate.} =
+  block:
+    var t = newTask(description, proc() {.closure.} =
+      body)
+    tasks[name] = t
+
+proc newTask*(desc: string; action: TTaskFunction): PTask =
+  new(result)
+  result.desc = desc
+  result.action = action
+proc runTask*(name: string) = tasks[name].action()
+
+proc shell*(cmd: varargs[string, `$`]): int =
+  result = execShellCmd(cmd.join(" "))
+proc cd*(dir: string) = setCurrentDir(dir)
+template withDir*(dir: string; body: stmt): stmt =
+  ## temporary cd
+  ## withDir "foo":
+  ##   # inside foo
+  ## #back to last dir
+  var curDir = getCurrentDir()
+  cd(dir)
+  body
+  cd(curDir)
+
+when isMainModule:
+  if not existsFile("nakefile.nim"):
+    echo "No nakefile.nim found. Current working dir is ", getCurrentDir()
+    quit 1
+  var args = ""
+  for i in 1..paramCount():
+    args.add paramStr(i)
+    args.add " "
+  quit(shell("nimrod", "c", "-r", "nakefile.nim", args))
+else:
+  addQuitProc(proc() {.noconv.} =
+    var 
+      task: string
+      printTaskList: bool
+    for kind, key, val in getOpt():
+      case kind
+      of cmdLongOption, cmdShortOption:
+        case key.tolower
+        of "tasks", "t":
+          printTaskList = true
+        else: 
+          echo "Unknown option: ", key, ": ", val
+      of cmdArgument:
+        task = key
+      else: nil
+    if printTaskList or task.isNil or not(tasks.hasKey(task)):
+      echo "Available tasks:"
+      for name, task in pairs(tasks):
+        echo name, " - ", task.desc
+      quit 0
+    tasks[task].action())
diff --git a/tests/manyloc/keineschweine/nakefile.nim b/tests/manyloc/nake/nakefile.nim
index f175321b9..700f9ab49 100644
--- a/tests/manyloc/keineschweine/nakefile.nim
+++ b/tests/manyloc/nake/nakefile.nim
@@ -76,7 +76,7 @@ task "testskel", "create skeleton test dir for testing":
 
 task "clean", "cleanup generated files":
   var dirs = @["nimcache", "server"/"nimcache"]
-  dirs.each(proc(x: var string) =
+  dirs.map(proc(x: var string) =
     if existsDir(x): removeDir(x))
 
 task "download", "download game assets":
diff --git a/tests/manyloc/keineschweine/nakefile.nimrod.cfg b/tests/manyloc/nake/nakefile.nimrod.cfg
index 6f3e86fe6..6f3e86fe6 100644
--- a/tests/manyloc/keineschweine/nakefile.nimrod.cfg
+++ b/tests/manyloc/nake/nakefile.nimrod.cfg
diff --git a/tests/reject/temptycaseobj.nim b/tests/reject/temptycaseobj.nim
index 5977cb92b..5c012746e 100644
--- a/tests/reject/temptycaseobj.nim
+++ b/tests/reject/temptycaseobj.nim
@@ -1,6 +1,6 @@
 discard """
   line: 11
-  errormsg: "identifier expected, but found '[same indentation]'"
+  errormsg: "identifier expected, but found 'keyword of'"
 """
 
 type
diff --git a/tests/reject/tind1.nim b/tests/reject/tind1.nim
index f3f3cacf7..f3fd952cc 100644
--- a/tests/reject/tind1.nim
+++ b/tests/reject/tind1.nim
@@ -1,6 +1,6 @@
 discard """
   line: 24
-  errormsg: "invalid indentation"
+  errormsg: "expression expected, but found 'keyword else'"
 """
 
 import macros
diff --git a/tests/reject/tmissingnl.nim b/tests/reject/tmissingnl.nim
index c2f97a807..33b7debf1 100644
--- a/tests/reject/tmissingnl.nim
+++ b/tests/reject/tmissingnl.nim
@@ -1,7 +1,7 @@
 discard """
   file: "tmissingnl.nim"
   line: 7
-  errormsg: "newline expected, but found 'keyword var'"
+  errormsg: "invalid indentation"
 """
 
 import strutils var s: seq[int] = @[0, 1, 2, 3, 4, 5, 6]
diff --git a/tests/run/tstmtexprs.nim b/tests/run/tstmtexprs.nim
new file mode 100644
index 000000000..a69acd98b
--- /dev/null
+++ b/tests/run/tstmtexprs.nim
@@ -0,0 +1,66 @@
+discard """
+  output: '''(bar: bar)
+1244
+6
+abcdefghijklmnopqrstuvwxyz
+145 23'''
+"""
+
+import strutils
+
+when true:
+  proc test(foo: proc (x, y: int): bool) =
+    echo foo(5, 5)
+
+
+  type Foo = object
+    bar: string
+
+  proc newfoo(): Foo =
+    result.bar = "bar"
+
+  echo($newfoo())
+   
+
+  proc retInt(x, y: int): int = 
+    if (var yy = 0; yy != 0):
+      echo yy
+    else:
+      echo(try: parseInt("1244") except EINvalidValue: -1)
+    result = case x
+             of 23: 3
+             of 64: 
+                    case y
+                    of 1: 2
+                    of 2: 3
+                    of 3: 6
+                    else: 8
+             else: 1
+
+  echo retInt(64, 3)
+
+  proc buildString(): string =
+    result = ""
+    for x in 'a'..'z':
+      result.add(x)
+
+  echo buildString()
+
+#test(
+#  proc (x, y: int): bool =
+#  if x == 5: return true
+#  if x == 2: return false
+#  if y == 78: return true
+#)
+
+proc q(): int {.discardable.} = 145
+proc p(): int =
+  q()
+
+proc p2(a: int): int =
+  # result enforces a void context:
+  if a == 2:
+    result = 23
+  q()
+
+echo p(), " ", p2(2)
diff --git a/tests/specials.nim b/tests/specials.nim
index 30bb6f423..156a289d2 100644
--- a/tests/specials.nim
+++ b/tests/specials.nim
@@ -216,7 +216,8 @@ proc compileManyLoc(r: var TResults, options: string) =
   for kind, dir in os.walkDir("tests/manyloc"):
     if kind == pcDir:
       let mainfile = findMainFile(dir)
-      compileSingleTest(r, mainfile, options)
+      if mainfile != ".nim":
+        compileSingleTest(r, mainfile, options)
 
 proc compileSpecialTests(r: var TResults, options: string) =
   compileRodFiles(r, options)
diff --git a/todo.txt b/todo.txt
index 65a9127d1..764ba1e13 100644
--- a/todo.txt
+++ b/todo.txt
@@ -8,7 +8,6 @@ version 0.9.2
 - CGEN: ``restrict`` pragma + backend support; computed goto support
 - document NimMain and check whether it works for threading
 
-
 Bugs
 ====
 
@@ -29,14 +28,14 @@ version 0.9.4
 =============
 
 - macros as type pragmas
-- ``try`` as an expression
+- effect propagation for callbacks
 - provide tool/API to track leaks/object counts
 - hybrid GC
 - use big blocks in the allocator
 - implement full 'not nil' checking
 - make 'bind' default for templates and introduce 'mixin';
   special rule for ``[]=``
-- implicit deref for parameter matching; overloading based on 'var T'
+- implicit deref for parameter matching
 - ``=`` should be overloadable; requires specialization for ``=``; general
   lift mechanism in the compiler is already implemented for 'fields'
 - lazy overloading resolution:
@@ -54,9 +53,14 @@ version 0.9.X
 - improve the compiler as a service
 - better support for macros that rewrite procs
 - macros need access to types and symbols (partially implemented)
-- rethink the syntax/grammar:
-  * parser is not strict enough with newlines
-  * change comment handling in the AST
+- perhaps: change comment handling in the AST
+- enforce 'simpleExpr' more often --> doesn't work; tkProc is
+  part of primary!
+- the typeDesc/expr unification is weird and only necessary because of
+  the ambiguous a[T] construct: It would be easy to support a[expr] for
+  generics but require a[.typeDesc] if that's required; this would also
+  allow [.ref T.](x) for a more general type conversion construct; for
+  templates that would work too: T([.ref int])
 
 
 Concurrency
@@ -96,7 +100,8 @@ Not essential for 1.0.0
 - mocking support with ``tyProxy`` that does: fallback for ``.`` operator
   - overloading of ``.``? Special case ``.=``?
 - allow implicit forward declarations of procs via a pragma (so that the
-  wrappers can deactivate it)
+  wrappers can deactivate it): better solution: introduce the notion of a 
+  'proc section' that is similar to a type section.
 - implement the "snoopResult" pragma; no, make a strutils with string append
   semantics instead ...
 - implement "closure tuple consists of a single 'ref'" optimization
diff --git a/web/index.txt b/web/index.txt
index 99beb3743..9b90e94de 100644
--- a/web/index.txt
+++ b/web/index.txt
@@ -100,12 +100,7 @@ Nimrod plays nice with others
 Roadmap to 1.0
 ==============
 
-Version 0.9.2
-  * better interaction between macros, templates and overloading
-  * the symbol binding rules for generics and templates may change again
-
 Version 0.9.x
-  * message passing performance will be greatly improved
-  * the syntactic distinction between statements and expressions will be
-    removed
+  * the symbol binding rules for templates will change
+  * a shared memory garbage collected heap will be provided
   * the need for forward declarations may be removed