summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xcompiler/ast.nim18
-rwxr-xr-xcompiler/ccgexprs.nim2
-rwxr-xr-xcompiler/ecmasgen.nim2
-rwxr-xr-xcompiler/evals.nim44
-rwxr-xr-xcompiler/msgs.nim3
-rwxr-xr-xcompiler/parser.nim36
-rwxr-xr-xcompiler/sem.nim11
-rwxr-xr-xcompiler/semdata.nim2
-rwxr-xr-xcompiler/semexprs.nim8
-rwxr-xr-xcompiler/semfold.nim2
-rw-r--r--compiler/semmagic.nim14
-rwxr-xr-xcompiler/semstmts.nim106
-rwxr-xr-xcompiler/types.nim7
-rwxr-xr-xcompiler/wordrecg.nim7
-rwxr-xr-xdoc/manual.txt53
-rwxr-xr-xlib/system.nim15
-rwxr-xr-xweb/news.txt3
17 files changed, 260 insertions, 73 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 978b43b5e..d9ec70450 100755
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -371,7 +371,7 @@ type
   TMagic* = enum # symbols that require compiler magic:
     mNone,
     mDefined, mDefinedInScope, mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf,
-    mEcho, mShallowCopy, mSlurp,
+    mEcho, mShallowCopy, mSlurp, mStaticExec,
     mParseExprToAst, mParseStmtToAst, mExpandToAst,
     mUnaryLt, mSucc, 
     mPred, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray, 
@@ -542,6 +542,9 @@ type
                               # for easy generation of proper error messages
                               # for variant record fields the discriminant
                               # expression
+                              # for modules, it's a placeholder for compiler
+                              # generated code that will be appended to the
+                              # module after the sem pass (see appendToModule)
     options*: TOptions
     position*: int            # used for many different things:
                               # for enum fields its position;
@@ -568,6 +571,9 @@ type
                               # for record types a nkRecord node
                               # for enum types a list of symbols
                               # else: unused
+    destructor*: PSym         # destructor. warning: nil here may not necessary
+                              # mean that there is no destructor.
+                              # see instantiateDestructor in types.nim
     owner*: PSym              # the 'owner' of the type
     sym*: PSym                # types have the sym associated with them
                               # it is used for converting types to strings
@@ -740,6 +746,16 @@ proc linkTo*(s: PSym, t: PType): PSym {.discardable.} =
   s.typ = t
   result = s
 
+proc appendToModule*(m: PSym, n: PNode) =
+  ## The compiler will use this internally to add nodes that will be
+  ## appended to the module after the sem pass
+  if m.ast == nil:
+    m.ast = newNode(nkStmtList)
+    m.ast.sons = @[n]
+  else:
+    assert m.ast.kind == nkStmtList
+    m.ast.sons.add(n)
+  
 const                         # for all kind of hash tables:
   GrowthFactor* = 2           # must be power of 2, > 0
   StartSize* = 8              # must be power of 2, > 0
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index dbe528ab5..7a0face5e 100755
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -1437,6 +1437,8 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   of mArrToSeq: genArrToSeq(p, e, d)
   of mNLen..mNError:
     localError(e.info, errCannotGenerateCodeForX, e.sons[0].sym.name.s)
+  of mSlurp, mStaticExec:
+    localError(e.info, errXMustBeCompileTime, e.sons[0].sym.name.s)
   else: internalError(e.info, "genMagicExpr: " & $op)
 
 proc genConstExpr(p: BProc, n: PNode): PRope
diff --git a/compiler/ecmasgen.nim b/compiler/ecmasgen.nim
index ecdbb022e..07754ee13 100755
--- a/compiler/ecmasgen.nim
+++ b/compiler/ecmasgen.nim
@@ -1173,6 +1173,8 @@ proc genMagic(p: var TProc, n: PNode, r: var TCompRes) =
     localError(n.info, errCannotGenerateCodeForX, n.sons[0].sym.name.s)
   of mNewSeq: binaryStmt(p, n, r, "", "$1 = new Array($2)")
   of mEcho: genEcho(p, n, r)
+  of mSlurp, mStaticExec:
+    localError(n.info, errXMustBeCompileTime, n.sons[0].sym.name.s)
   else:
     genCall(p, n, r)
     #else internalError(e.info, 'genMagic: ' + magicToStr[op]);
diff --git a/compiler/evals.nim b/compiler/evals.nim
index b2fded071..5c77a4d94 100755
--- a/compiler/evals.nim
+++ b/compiler/evals.nim
@@ -16,7 +16,7 @@
 import 
   strutils, magicsys, lists, options, ast, astalgo, trees, treetab, nimsets, 
   msgs, os, condsyms, idents, renderer, types, passes, semfold, transf, 
-  parser, ropes, rodread, idgen
+  parser, ropes, rodread, idgen, osproc, streams
 
 type 
   PStackFrame* = ref TStackFrame
@@ -910,6 +910,43 @@ proc evalTypeTrait*(n: PNode, context: PSym): PNode =
   else:
     internalAssert false
 
+proc expectString(n: PNode) =
+  if n.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
+    GlobalError(n.info, errStringLiteralExpected)
+
+proc evalSlurp*(e: PNode, module: PSym): PNode =
+  expectString(e)
+  try:
+    var filename = e.strVal.FindFile
+    var content = readFile(filename)
+    result = newStrNode(nkStrLit, content)
+    result.typ = getSysType(tyString)
+    result.info = e.info
+    # we produce a fake include statement for every slurped filename, so that
+    # the module dependencies are accurate:    
+    appendToModule(module, newNode(nkIncludeStmt, e.info, @[
+      newStrNode(nkStrLit, filename)]))
+  except EIO:
+    GlobalError(e.info, errCannotOpenFile, e.strVal)
+
+proc readOutput(p: PProcess): string =
+  result = ""
+  var output = p.outputStream
+  discard p.waitForExit
+  while not output.atEnd:
+    result.add(output.readLine)
+
+proc evalStaticExec*(cmd, input: PNode): PNode =
+  expectString(cmd)
+  var p = startCmd(cmd.strVal)
+  if input != nil:
+    expectString(input)
+    p.inputStream.write(input.strVal)
+    p.inputStream.close()
+  result = newStrNode(nkStrLit, p.readOutput)
+  result.typ = getSysType(tyString)
+  result.info = cmd.info
+
 proc evalExpandToAst(c: PEvalContext, original: PNode): PNode =
   var
     n = original.copyTree
@@ -960,6 +997,11 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
   of mParseStmtToAst: result = evalParseStmt(c, n)
   of mExpandToAst: result = evalExpandToAst(c, n)
   of mTypeTrait: result = evalTypeTrait(n, c.module)
+  of mSlurp: result = evalSlurp(evalAux(c, n.sons[1], {}), c.module)
+  of mStaticExec:
+    let cmd = evalAux(c, n.sons[1], {})
+    let input = if n.sonsLen == 3: evalAux(c, n.sons[2], {}) else: nil
+    result = evalStaticExec(cmd, input)
   of mNLen:
     result = evalAux(c, n.sons[1], {efLValue})
     if isSpecial(result): return 
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 17cfeae4d..2af512af7 100755
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -94,7 +94,7 @@ type
     errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate, 
     errXhasSideEffects, errIteratorExpected, errLetNeedsInit,
     errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX,
-    errXCannotBeClosure,
+    errXCannotBeClosure, errXMustBeCompileTime,
     errUser,
     warnCannotOpenFile, 
     warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, 
@@ -327,6 +327,7 @@ const
     errWrongSymbolX: "usage of \'$1\' is a user-defined error", 
     errIllegalCaptureX: "illegal capture '$1'",
     errXCannotBeClosure: "'$1' cannot have 'closure' calling convention",
+    errXMustBeCompileTime: "'$1' can only be used in compile-time context",
     errUser: "$1", 
     warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
     warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]", 
diff --git a/compiler/parser.nim b/compiler/parser.nim
index 78e39bcd2..4baee5b43 100755
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -57,6 +57,8 @@ proc optInd*(p: var TParser, n: PNode)
 proc indAndComment*(p: var TParser, n: PNode)
 proc setBaseFlags*(n: PNode, base: TNumericalBase)
 proc parseSymbol*(p: var TParser): PNode
+proc parseTry(p: var TParser): PNode
+proc parseCase(p: var TParser): PNode
 # implementation
 
 proc getTok(p: var TParser) = 
@@ -468,6 +470,11 @@ proc primarySuffix(p: var TParser, r: PNode): PNode =
       addSon(result, a)
       exprColonEqExprListAux(p, nkExprEqExpr, tkParRi, tkEquals, result)
       parseDoBlocks(p, result)
+    of tkDo:
+      var a = result
+      result = newNodeP(nkCall, p)
+      addSon(result, a)
+      parseDoBlocks(p, result)
     of tkDot:
       result = dotExpr(p, result)
       result = parseGStrLit(p, result)
@@ -704,6 +711,8 @@ proc parseExpr(p: var TParser): PNode =
   case p.tok.tokType:
   of tkIf: result = parseIfExpr(p, nkIfExpr)
   of tkWhen: result = parseIfExpr(p, nkWhenExpr)
+  of tkTry: result = parseTry(p)
+  of tkCase: result = parseCase(p)
   else: result = lowestExpr(p)
 
 proc primary(p: var TParser, skipSuffix = false): PNode = 
@@ -944,13 +953,19 @@ proc parseWhile(p: var TParser): PNode =
 proc parseCase(p: var TParser): PNode = 
   var 
     b: PNode
-    inElif: bool
+    inElif= false
+    wasIndented = false
   result = newNodeP(nkCaseStmt, p)
   getTok(p)
   addSon(result, parseExpr(p))
   if p.tok.tokType == tkColon: getTok(p)
   skipComment(p, result)
-  inElif = false
+  
+  if p.tok.tokType == tkInd:
+    pushInd(p.lex, p.tok.indent)
+    getTok(p)
+    wasIndented = true
+  
   while true: 
     if p.tok.tokType == tkSad: getTok(p)
     case p.tok.tokType
@@ -973,8 +988,12 @@ proc parseCase(p: var TParser): PNode =
     skipComment(p, b)
     addSon(b, parseStmt(p))
     addSon(result, b)
-    if b.kind == nkElse: break 
+    if b.kind == nkElse: break
   
+  if wasIndented:
+    eat(p, tkDed)
+    popInd(p.lex)
+
 proc parseTry(p: var TParser): PNode = 
   result = newNodeP(nkTryStmt, p)
   getTok(p)
@@ -998,7 +1017,14 @@ proc parseTry(p: var TParser): PNode =
     addSon(result, b)
     if b.kind == nkFinally: break 
   if b == nil: parMessage(p, errTokenExpected, "except")
-  
+
+proc parseExceptBlock(p: var TParser, kind: TNodeKind): PNode =
+  result = newNodeP(kind, p)
+  getTok(p)
+  eat(p, tkColon)
+  skipComment(p, result)
+  addSon(result, parseStmt(p))
+
 proc parseFor(p: var TParser): PNode = 
   result = newNodeP(nkForStmt, p)
   getTok(p)
@@ -1393,6 +1419,8 @@ proc complexOrSimpleStmt(p: var TParser): PNode =
   of tkWhile: result = parseWhile(p)
   of tkCase: result = parseCase(p)
   of tkTry: result = parseTry(p)
+  of tkFinally: result = parseExceptBlock(p, nkFinally)
+  of tkExcept: result = parseExceptBlock(p, nkExceptBranch)
   of tkFor: result = parseFor(p)
   of tkBlock: result = parseBlock(p)
   of tkStatic: result = parseStatic(p)
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 8afbe3c51..721b4b040 100755
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -89,9 +89,6 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
     return nil
   result = evalTypedExpr(c, e)
 
-proc semAndEvalConstExpr(c: PContext, n: PNode): PNode = 
-  result = semConstExpr(c, n)
-
 include seminst, semcall
 
 proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode = 
@@ -219,12 +216,8 @@ proc myClose(context: PPassContext, n: PNode): PNode =
   else:
     InternalError(n.info, "n is not nil") #result := n;
   addCodeForGenerics(c, result)
-  # we produce a fake include statement for every slurped filename, so that
-  # the module dependencies are accurate:
-  var ics = newNode(nkIncludeStmt)
-  for s in items(c.slurpedFiles): ics.add(newStrNode(nkStrLit, s))
-  result.add(ics)
-  
+  if c.module.ast != nil:
+    result.add(c.module.ast)
   checkThreads(c)
   popOwner()
   popProcCon(c)
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index f97da4717..81e45f71c 100755
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -73,7 +73,6 @@ type
     filename*: string          # the module's filename
     userPragmas*: TStrTable
     evalContext*: PEvalContext
-    slurpedFiles*: seq[string]
 
 var
   gGenericsCache: PGenericsCache # save for modularity
@@ -153,7 +152,6 @@ proc newContext(module: PSym, nimfile: string): PContext =
   result.filename = nimfile
   result.includedFiles = initIntSet()
   initStrTable(result.userPragmas)
-  result.slurpedFiles = @[]
   if optSymbolFiles notin gGlobalOptions:
     # re-usage of generic instantiations across module boundaries is
     # very nice for code size:
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index fe35607cb..ac9075d4f 100755
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -491,12 +491,6 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
         skipTypes(t.sons[i], abstractInst).kind == tyVar:
       n.sons[i] = analyseIfAddressTaken(c, n.sons[i])
   
-
-proc expectStringArg(c: PContext, n: PNode, i: int): PNode =
-  result = c.semAndEvalConstExpr(n.sons[i+1])
-  if result.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
-    GlobalError(result.info, errStringLiteralExpected)
-  
 include semmagic
 
 proc evalAtCompileTime(c: PContext, n: PNode): PNode =
@@ -901,7 +895,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
     checkSonsLen(n, 2)
     n.sons[0] = makeDeref(n.sons[0])
     # [] operator for tuples requires constant expression:
-    n.sons[1] = semAndEvalConstExpr(c, n.sons[1])
+    n.sons[1] = semConstExpr(c, n.sons[1])
     if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal}).kind in
         {tyInt..tyInt64}: 
       var idx = getOrdValue(n.sons[1])
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index 82ee7de13..565155791 100755
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -207,7 +207,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
      mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh, 
      mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq, 
      mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait,
-     mNLen..mNError, mEqRef: 
+     mNLen..mNError, mEqRef, mSlurp, mStaticExec: 
     nil
   of mRand:
     result = newIntNodeT(math.random(a.getInt.int), n)
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index e6861f2e4..b7e890e67 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -14,19 +14,6 @@ proc semIsPartOf(c: PContext, n: PNode, flags: TExprFlags): PNode =
   var r = isPartOf(n[1], n[2])
   result = newIntNodeT(ord(r), n)
   
-proc semSlurp(c: PContext, n: PNode, flags: TExprFlags): PNode = 
-  assert sonsLen(n) == 2
-  var a = expectStringArg(c, n, 0)
-  try:
-    var filename = a.strVal.FindFile
-    var content = readFile(filename)
-    result = newStrNode(nkStrLit, content)
-    result.typ = getSysType(tyString)
-    result.info = n.info
-    c.slurpedFiles.add(a.strVal)
-  except EIO:
-    GlobalError(a.info, errCannotOpenFile, a.strVal)
-
 proc expectIntLit(c: PContext, n: PNode): int =
   let x = c.semConstExpr(c, n)
   case x.kind
@@ -56,7 +43,6 @@ proc semTypeTraits(c: PContext, n: PNode): PNode =
 proc magicsAfterOverloadResolution(c: PContext, n: PNode, 
                                    flags: TExprFlags): PNode =
   case n[0].sym.magic
-  of mSlurp: result = semSlurp(c, n, flags)
   of mIsPartOf: result = semIsPartOf(c, n, flags)
   of mTypeTrait: result = semTypeTraits(c, n)
   of mAstToStr:
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 202b7416a..82f43e787 100755
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -27,7 +27,7 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
     case it.kind
     of nkElifBranch, nkElifExpr: 
       checkSonsLen(it, 2)
-      var e = semAndEvalConstExpr(c, it.sons[0])
+      var e = semConstExpr(c, it.sons[0])
       if e.kind != nkIntLit: InternalError(n.info, "semWhen")
       if e.intVal != 0 and result == nil:
         setResult(it.sons[1]) 
@@ -772,6 +772,10 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
       incl(s.flags, sfForward)
     elif sfBorrow in s.flags: semBorrow(c, n, s)
   sideEffectsCheck(c, s)
+  if result.sons[namePos].sym.name.id == ord(wDestroy):
+    if s.typ.sons.len == 2:
+      let typ = s.typ.sons[1].skipTypes({tyVar})
+      typ.destructor = s
   if s.typ.callConv == ccClosure and s.owner.kind == skModule:
     localError(s.info, errXCannotBeClosure, s.name.s)
   closeScope(c.tab)           # close scope for parameters
@@ -860,6 +864,62 @@ proc semStaticStmt(c: PContext, n: PNode): PNode =
   if result.isNil:
     LocalError(n.info, errCannotInterpretNodeX, renderTree(n))
 
+proc insertDestructors(c: PContext, varSection: PNode):
+  tuple[outer: PNode, 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(varTyp):
+      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, @[
+            semSym(c, varId, varTyp.destructor, {}),
+            semSym(c, varId, varId.sym, {})]))]))
+
+      result.outer = newNodeI(nkStmtList, info)
+      varSection.sons.setLen(j+1)
+      result.outer.addSon(varSection)
+      result.outer.addSon(tryStmt)
+
+      return
+
 proc SemStmt(c: PContext, n: PNode): PNode = 
   const                       # must be last statements in a block:
     LastBlockStmts = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt}
@@ -875,13 +935,43 @@ proc SemStmt(c: PContext, n: PNode): PNode =
   of nkBlockStmt: result = semBlock(c, n)
   of nkStmtList: 
     var length = sonsLen(n)
-    for i in countup(0, length - 1): 
-      n.sons[i] = semStmt(c, n.sons[i])
-      if n.sons[i].kind in LastBlockStmts: 
-        for j in countup(i + 1, length - 1): 
-          case n.sons[j].kind
-          of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: nil
-          else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
+    for i in countup(0, length - 1):
+      case n.sons[i].kind
+      of nkFinally, nkExceptBranch:
+        # stand-alone finally and except blocks are
+        # transformed into regular try blocks:
+        #
+        # var f = fopen("somefile") | var f = fopen("somefile")
+        # finally: fcsole(f)        | try:
+        # ...                       |   ...
+        #                           | finally:
+        #                           |   fclose(f)
+        var tryStmt = newNodeI(nkTryStmt, n.sons[i].info)
+        var body = newNodeI(nkStmtList, n.sons[i].info)
+        if i < n.sonsLen - 1:
+          body.sons = n.sons[(i+1)..(-1)]
+        tryStmt.addSon(body)
+        tryStmt.addSon(n.sons[i])
+        n.sons[i] = semTry(c, tryStmt)
+        n.sons.setLen(i+1)
+        return
+      else:
+        n.sons[i] = semStmt(c, n.sons[i])
+        case n.sons[i].kind
+        of nkVarSection, nkLetSection:
+          let (outer, inner) = insertDestructors(c, n.sons[i])
+          if outer != nil:
+            n.sons[i] = outer
+            for j in countup(i+1, length-1):
+              inner.addSon(SemStmt(c, n.sons[j]))
+            n.sons.setLen(i+1)
+            return
+        of LastBlockStmts: 
+          for j in countup(i + 1, length - 1): 
+            case n.sons[j].kind
+            of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: nil
+            else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
+        else: nil
   of nkRaiseStmt: result = semRaise(c, n)
   of nkVarSection: result = semVarOrLet(c, n, skVar)
   of nkLetSection: result = semVarOrLet(c, n, skLet)
diff --git a/compiler/types.nim b/compiler/types.nim
index b3737b134..2f201b9de 100755
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -1061,4 +1061,11 @@ proc getReturnType*(s: PSym): PType =
 proc getSize(typ: PType): biggestInt = 
   result = computeSize(typ)
   if result < 0: InternalError("getSize(" & $typ.kind & ')')
+
+proc instantiateDestructor*(typ: PType): bool =
+  # return true if the type already had a user-defined
+  # destructor or if the compiler generated a default
+  # member-wise one
+  if typ.destructor != nil: return true
+  return false
   
diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim
index 30a6b3c2c..af482966b 100755
--- a/compiler/wordrecg.nim
+++ b/compiler/wordrecg.nim
@@ -36,6 +36,9 @@ type
     wColon, wColonColon, wEquals, wDot, wDotDot,
     wStar, wMinus,
     wMagic, wThread, wFinal, wProfiler, wObjChecks,
+
+    wDestroy,
+    
     wImmediate, wImportCpp, wImportObjC,
     wImportCompilerProc,
     wImportc, wExportc, wIncompleteStruct,
@@ -110,7 +113,9 @@ const
 
     ":", "::", "=", ".", "..",
     "*", "-",
-    "magic", "thread", "final", "profiler", "objchecks", 
+    "magic", "thread", "final", "profiler", "objchecks",
+
+    "destroy",
     
     "immediate", "importcpp", "importobjc",
     "importcompilerproc", "importc", "exportc", "incompletestruct",
diff --git a/doc/manual.txt b/doc/manual.txt
index 98a8d7122..5359dfc57 100755
--- a/doc/manual.txt
+++ b/doc/manual.txt
@@ -388,37 +388,37 @@ indentation tokens is already described in the `Lexical Analysis`_ section.
 

 Nimrod allows user-definable operators.

 Binary operators have 10 different levels of precedence. 

-
-Relevant character
-------------------
-
+

+Relevant character

+------------------

+

 An operator symbol's *relevant character* is its first

 character unless the first character is ``\`` and its length is greater than 1

-then it is the second character.
-
-This rule allows to escape operator symbols with ``\`` and keeps the operator's
-precedence and associativity; this is useful for meta programming.
-
-
-Associativity
--------------
-
-All binary operators are left-associative, except binary operators whose
+then it is the second character.

+

+This rule allows to escape operator symbols with ``\`` and keeps the operator's

+precedence and associativity; this is useful for meta programming.

+

+

+Associativity

+-------------

+

+All binary operators are left-associative, except binary operators whose

 relevant char is ``^``.

-
-Precedence
-----------
+

+Precedence

+----------

 

 For operators that are not keywords the precedence is determined by the

 following rules: 

 

 If the operator ends with ``=`` and its relevant character is none of 

 ``<``, ``>``, ``!``, ``=``, ``~``, ``?``, it is an *assignment operator* which

-has the lowest precedence.
-
-If the operator's relevant character is ``@`` it is a `sigil-like`:idx: 
-operator which binds stronger than a ``primarySuffix``: ``@x.abc`` is parsed
-as ``(@x).abc`` whereas ``$x.abc`` is parsed as ``$(x.abc)``.
+has the lowest precedence.

+

+If the operator's relevant character is ``@`` it is a `sigil-like`:idx: 

+operator which binds stronger than a ``primarySuffix``: ``@x.abc`` is parsed

+as ``(@x).abc`` whereas ``$x.abc`` is parsed as ``$(x.abc)``.

 

 

 Otherwise precedence is determined by the relevant character.

@@ -1879,6 +1879,15 @@ handled, it is propagated through the call stack. This means that often
 the rest of the procedure - that is not within a ``finally`` clause -

 is not executed (if an exception occurs).

 

+`except`:idx: and `finally`:idx: can also be used as a stand-alone statements.

+Any statements following them in the current block will be considered to be 

+in an implicit try block:

+

+.. code-block:: nimrod

+  var f = fopen("numbers.txt", "r")

+  finally: fcsole(f)

+  ...

+

 

 Return statement

 ~~~~~~~~~~~~~~~~

diff --git a/lib/system.nim b/lib/system.nim
index 9a9984911..449b56c2f 100755
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -2168,10 +2168,21 @@ proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo".}
   ## the `typeinfo` module instead.
   
 proc slurp*(filename: string): string {.magic: "Slurp".}
-  ## compiletime ``readFile`` proc for easy `resource`:idx: embedding:
+proc staticRead*(filename: string): string {.magic: "Slurp".}
+  ## compile-time ``readFile`` proc for easy `resource`:idx: embedding:
   ## .. code-block:: nimrod
   ##
-  ##   const myResource = slurp"mydatafile.bin"
+  ##   const myResource = staticRead"mydatafile.bin"
+  ##
+
+proc staticExec*(command: string, input = ""): string {.magic: "StaticExec".}
+  ## executes an external process at compile-time.
+  ## if `input` is not an empty string, it will be passed as a standard input
+  ## to the executed program.
+  ## .. code-block:: nimrod
+  ##
+  ##   const buildInfo = "Revision " & staticExec("git rev-parse HEAD") & 
+  ##                     "\nCompiled on " & staticExec("uname -v")
   ##
 
 proc `+=`*[T](x, y: ordinal[T]) {.magic: "Inc", noSideEffect.}
diff --git a/web/news.txt b/web/news.txt
index 3ad0eb4d8..847c36561 100755
--- a/web/news.txt
+++ b/web/news.txt
@@ -27,6 +27,8 @@ Library Additions
   assignments.
 - Added ``system.eval`` that can execute an anonymous block of code at
   compile time as if was a macro.
+- Added ``system.staticExec`` for compile-time execution of external programs
+- Added ``system.staticRead`` as a synonym for slurp
 - Added ``macros.emit`` that can emit an arbitrary computed string as nimrod
   code during compilation.
 - Added ``strutils.parseEnum``.
@@ -102,6 +104,7 @@ Language Additions
 - ``when`` expressions are now allowed just like ``if`` expressions.
 - The precedence for operators starting with ``@`` is different now 
   allowing for *sigil-like* operators.
+- Stand-alone ``finally`` and ``except`` blocks are now supported.
 
 
 2012-02-09 Version 0.8.14 released