summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xcompiler/ast.nim43
-rwxr-xr-xcompiler/evals.nim36
-rwxr-xr-xcompiler/parser.nim16
-rwxr-xr-xcompiler/semexprs.nim45
-rwxr-xr-xcompiler/semfold.nim1
-rwxr-xr-xcompiler/semstmts.nim2
-rwxr-xr-xlib/core/macros.nim49
-rwxr-xr-xlib/pure/parseutils.nim102
-rw-r--r--lib/pure/uuid.nim30
-rwxr-xr-xlib/system.nim9
-rwxr-xr-xtests/accept/compile/tdumpast.nim39
-rw-r--r--tests/accept/run/tstringinterp.nim71
-rw-r--r--tests/accept/run/tusingstatement.nim102
13 files changed, 492 insertions, 53 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index bb9803830..85ec2c9b3 100755
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -57,10 +57,10 @@ type
     nkStrLit,             # a string literal ""
     nkRStrLit,            # a raw string literal r""
     nkTripleStrLit,       # a triple string literal """
-    nkMetaNode,           # difficult to explain; represents itself
-                          # (used for macros)
     nkNilLit,             # the nil literal
                           # end of atoms
+    nkMetaNode,           # difficult to explain; represents itself
+                          # (used for macros)
     nkDotCall,            # used to temporarily flag a nkCall node; 
                           # this is used
                           # for transforming ``s.len`` to ``len(s)``
@@ -313,6 +313,7 @@ type
   TMagic* = enum # symbols that require compiler magic:
     mNone, mDefined, mDefinedInScope, mLow, mHigh, mSizeOf, mIs, mOf,
     mEcho, mShallowCopy, mSlurp,
+    mAstToYaml, mParseExprToAst, mParseStmtToAst, mExpandMacroToAst,
     mUnaryLt, mSucc, 
     mPred, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray, 
     mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, mGCref, 
@@ -610,6 +611,23 @@ proc copyTree*(src: PNode): PNode
 
 proc discardSons*(father: PNode)
 
+proc len*(n: PNode): int {.inline.} =
+  if isNil(n.sons): result = 0
+  else: result = len(n.sons)
+  
+proc safeLen*(n: PNode): int {.inline.} =
+  ## works even for leaves.
+  if n.kind in {nkNone..nkNilLit} or isNil(n.sons): result = 0
+  else: result = len(n.sons)
+  
+proc add*(father, son: PNode) =
+  assert son != nil
+  if isNil(father.sons): father.sons = @[]
+  add(father.sons, son)
+  
+proc `[]`*(n: PNode, i: int): PNode {.inline.} =
+  result = n.sons[i]
+
 var emptyNode* = newNode(nkEmpty)
 # There is a single empty node that is shared! Do not overwrite it!
 
@@ -754,6 +772,10 @@ proc newNodeIT(kind: TNodeKind, info: TLineInfo, typ: PType): PNode =
   result.info = info
   result.typ = typ
 
+proc newMetaNodeIT*(tree: PNode, info: TLineInfo, typ: PType): PNode =
+  result = newNodeIT(nkMetaNode, info, typ)
+  result.add(tree)
+
 proc NewType(kind: TTypeKind, owner: PSym): PType = 
   new(result)
   result.kind = kind
@@ -866,23 +888,6 @@ proc sonsLen(n: PNode): int =
   if isNil(n.sons): result = 0
   else: result = len(n.sons)
   
-proc len*(n: PNode): int {.inline.} =
-  if isNil(n.sons): result = 0
-  else: result = len(n.sons)
-  
-proc safeLen*(n: PNode): int {.inline.} =
-  ## works even for leaves.
-  if n.kind in {nkNone..nkNilLit} or isNil(n.sons): result = 0
-  else: result = len(n.sons)
-  
-proc add*(father, son: PNode) =
-  assert son != nil
-  if isNil(father.sons): father.sons = @[]
-  add(father.sons, son)  
-  
-proc `[]`*(n: PNode, i: int): PNode {.inline.} =
-  result = n.sons[i]
-  
 proc newSons(father: PNode, length: int) = 
   if isNil(father.sons): father.sons = @[]
   setlen(father.sons, len(father.sons) + length)
diff --git a/compiler/evals.nim b/compiler/evals.nim
index 55157ea45..65c64f4d3 100755
--- a/compiler/evals.nim
+++ b/compiler/evals.nim
@@ -15,7 +15,8 @@
 
 import 
   strutils, magicsys, lists, options, ast, astalgo, trees, treetab, nimsets, 
-  msgs, os, condsyms, idents, renderer, types, passes, semfold, transf
+  msgs, os, condsyms, idents, renderer, types, passes, semfold, transf, 
+  parser, ropes
 
 type 
   PStackFrame* = ref TStackFrame
@@ -751,6 +752,31 @@ proc evalRepr(c: PEvalContext, n: PNode): PNode =
 proc isEmpty(n: PNode): bool = 
   result = (n != nil) and (n.kind == nkEmpty)
 
+# The lexer marks multi-line strings as residing at the line where they are closed
+# This function returns the line where the string begins
+# Maybe the lexer should mark both the beginning and the end of expressions, then
+# this function could be removed
+proc stringStartingLine(s: PNode): int =
+  var totalLines = 0
+  for ln in splitLines(s.strVal): inc totalLines
+
+  result = s.info.line - totalLines
+
+proc evalParseExpr(c: PEvalContext, n: Pnode): Pnode =
+  var code = evalAux(c, n.sons[1], {})
+  var ast = parseString(code.getStrValue, code.info.toFilename, code.stringStartingLine)
+
+  if sonsLen(ast) != 1:
+    GlobalError(code.info, errExprExpected, "multiple statements")
+
+  result = ast.sons[0]
+  result.typ = newType(tyExpr, c.module)
+
+proc evalParseStmt(c: PEvalContext, n: Pnode): Pnode =
+  var code = evalAux(c, n.sons[1], {})
+  result = parseString(code.getStrValue, code.info.toFilename, code.stringStartingLine)
+  result.typ = newType(tyStmt, c.module)
+
 proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode = 
   var m = getMagic(n)
   case m
@@ -775,6 +801,8 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
   of mAppendStrCh: result = evalAppendStrCh(c, n)
   of mAppendStrStr: result = evalAppendStrStr(c, n)
   of mAppendSeqElem: result = evalAppendSeqElem(c, n)
+  of mParseExprToAst: result = evalParseExpr(c, n)
+  of mParseStmtToAst: result = evalParseStmt(c, n)
   of mNLen: 
     result = evalAux(c, n.sons[1], {efLValue})
     if isSpecial(result): return 
@@ -981,6 +1009,9 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
     if (a == b) or
         (b.kind in {nkNilLit, nkEmpty}) and (a.kind in {nkNilLit, nkEmpty}): 
       result.intVal = 1
+  of mAstToYaml:
+    var ast = evalAux(c, n.sons[1], {efLValue})
+    result = newStrNode(nkStrLit, ast.treeToYaml.ropeToStr)
   of mNHint: 
     result = evalAux(c, n.sons[1], {})
     if isSpecial(result): return 
@@ -1034,6 +1065,9 @@ proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
   dec(gNestedEvals)
   if gNestedEvals <= 0: stackTrace(c, n, errTooManyIterations)
   case n.kind                 # atoms:
+  of nkMetaNode:
+    result = copyTree(n.sons[0])
+    result.typ = n.typ
   of nkEmpty: result = n
   of nkSym: result = evalSym(c, n, flags)
   of nkType..nkNilLit: result = copyNode(n) # end of atoms
diff --git a/compiler/parser.nim b/compiler/parser.nim
index 003bd9219..df21c9916 100755
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -30,6 +30,11 @@ proc closeParser*(p: var TParser)
 proc parseTopLevelStmt*(p: var TParser): PNode
   # implements an iterator. Returns the next top-level statement or
   # emtyNode if end of stream.
+
+proc parseString*(s: string, filename: string = "", line: int = 0): PNode
+  # filename and line could be set optionally, when the string originates 
+  # from a certain source file. This way, the compiler could generate
+  # correct error messages referring to the original source.
   
 # helpers for the other parsers
 proc getPrecedence*(tok: TToken): int
@@ -1369,3 +1374,14 @@ proc parseTopLevelStmt(p: var TParser): PNode =
       result = complexOrSimpleStmt(p)
       if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
       break
+
+proc parseString(s: string, filename: string = "", line: int = 0): PNode =
+  var stream = LLStreamOpen(s)
+  stream.lineOffset = line
+
+  var parser : TParser
+  OpenParser(parser, filename, stream)
+
+  result = parser.parseAll
+  CloseParser(parser)
+  
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index f86a4f60d..6dbbba7b8 100755
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -8,6 +8,7 @@
 #
 
 # this module does the semantic checking for expressions
+
 const 
   ConstAbstractTypes = {tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128, 
     tyArrayConstr, tyTuple, tySet}
@@ -384,6 +385,10 @@ proc isAssignable(c: PContext, n: PNode): TAssignableResult =
   else: 
     nil
 
+proc isCallExpr(n: PNode): bool = 
+  result = n.kind in {nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand,
+                      nkCallStrLit}
+
 proc newHiddenAddrTaken(c: PContext, n: PNode): PNode = 
   if n.kind == nkHiddenDeref: 
     checkSonsLen(n, 1)
@@ -742,7 +747,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] = semConstExpr(c, n.sons[1])
+    n.sons[1] = semAndEvalConstExpr(c, n.sons[1])
     if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal}).kind in
         {tyInt..tyInt64}: 
       var idx = getOrdValue(n.sons[1])
@@ -883,11 +888,38 @@ proc setMs(n: PNode, s: PSym): PNode =
   n.sons[0] = newSymNode(s)
   n.sons[0].info = n.info
 
+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)
+
+proc semExpandMacroToAst(c: PContext, n: PNode, flags: TExprFlags): PNode =
+  if sonsLen(n) == 2:
+    if not isCallExpr(n.sons[1]):
+      GlobalError(n.info, errXisNoMacroOrTemplate, n.renderTree)
+
+    var macroCall = n.sons[1]
+
+    var s = qualifiedLookup(c, macroCall.sons[0], {checkUndeclared})
+    if s == nil:
+      GlobalError(n.info, errUndeclaredIdentifier, macroCall.sons[0].renderTree)
+
+    var expanded : Pnode
+
+    case s.kind
+    of skMacro: expanded = semMacroExpr(c, macroCall, s, false)
+    of skTemplate: expanded = semTemplateExpr(c, macroCall, s, false)
+    else: GlobalError(n.info, errXisNoMacroOrTemplate, s.name.s)
+
+    var macroRetType = newTypeS(s.typ.sons[0].kind, c)
+    result = newMetaNodeIT(expanded, n.info, macroRetType)
+  else:
+    result = semDirectOp(c, n, flags)
+
 proc semSlurp(c: PContext, n: PNode, flags: TExprFlags): PNode = 
   if sonsLen(n) == 2:
-    var a = c.semConstExpr(c, n.sons[1])
-    if a.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}: 
-      GlobalError(a.info, errStringLiteralExpected)
+    var a = expectStringArg(c, n, 0)
     try:
       var content = readFile(a.strVal)
       result = newStrNode(nkStrLit, content)
@@ -921,6 +953,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
     else:
       result = semDirectOp(c, n, flags)
   of mSlurp: result = semSlurp(c, n, flags)
+  of mExpandMacroToAst: result = semExpandMacroToAst(c, n, flags)
   else: result = semDirectOp(c, n, flags)
 
 proc semIfExpr(c: PContext, n: PNode): PNode = 
@@ -1078,10 +1111,6 @@ proc semBlockExpr(c: PContext, n: PNode): PNode =
   closeScope(c.tab)
   Dec(c.p.nestedBlockCounter)
 
-proc isCallExpr(n: PNode): bool = 
-  result = n.kind in {nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand,
-                      nkCallStrLit}
-
 proc semMacroStmt(c: PContext, n: PNode, semCheck = true): PNode = 
   checkMinSonsLen(n, 2)
   var a: PNode
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index 61e63a69f..570656a39 100755
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -208,6 +208,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
   of mNewString, mNewStringOfCap, 
      mExit, mInc, ast.mDec, mEcho, mAssert, mSwap, mAppendStrCh, 
      mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq, 
+     mParseExprToAst, mParseStmtToAst,
      mNLen..mNError, mEqRef: 
     nil
   else: InternalError(a.info, "evalOp(" & $m & ')')
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index ce870a3ad..decc8a2d7 100755
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -19,7 +19,7 @@ proc semWhen(c: PContext, n: PNode): PNode =
     case it.kind
     of nkElifBranch: 
       checkSonsLen(it, 2)
-      var e = semConstBoolExpr(c, it.sons[0])
+      var e = semAndEvalConstExpr(c, it.sons[0])
       if (e.kind != nkIntLit): InternalError(n.info, "semWhen")
       if (e.intVal != 0) and (result == nil): 
         result = semStmt(c, it.sons[1]) # do not open a new scope!
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index 3b11e774c..bebcab677 100755
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -19,7 +19,7 @@ type
     nnkType, nnkCharLit, nnkIntLit, nnkInt8Lit, 

     nnkInt16Lit, nnkInt32Lit, nnkInt64Lit, nnkFloatLit, 

     nnkFloat32Lit, nnkFloat64Lit, nnkStrLit, nnkRStrLit, 

-    nnkTripleStrLit, nnkMetaNode, nnkNilLit, nnkDotCall, 

+    nnkTripleStrLit, nnkNilLit, nnkMetaNode, nnkDotCall, 

     nnkCommand, nnkCall, nnkCallStrLit, nnkExprEqExpr, 

     nnkExprColonExpr, nnkIdentDefs, nnkVarTuple, nnkInfix, 

     nnkPrefix, nnkPostfix, nnkPar, nnkCurly, 

@@ -184,6 +184,53 @@ proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} =
   ## in a string literal node

   return newStrLitNode(repr(n))

 

+proc prettyPrint*(n: PNimrodNode): string {.compileTime.} =

+  ## Convert the AST `n` to a human-readable string

+  ##

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

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

+  ## a certain expression/statement

+  if n == nil: return "nil"

+

+  result = $n.kind

+  add(result, "(")

+  

+  case n.kind

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

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

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

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

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

+  of nnkIdent: add(result, $n.ident)

+  of nnkSym, nnkNone: assert false

+  else:

+    add(result, prettyPrint(n[0]))

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

+      add(result, ", ")

+      add(result, prettyPrint(n[j]))

+

+  add(result, ")")

+

+proc toYaml*(n: PNimrodNode): string {.magic: "AstToYaml".}

+  ## Converts the AST `n` to an YAML string

+  ##

+  ## Provides more detailed, potentially harder to digest information

+  ## than `prettyPrint`

+

+proc parseExpr*(s: string) : expr {.magic: "ParseExprToAst".}

+  ## Compiles the passed string to its AST representation

+  ## Expects a single expression

+

+proc parseStmt*(s: string) : stmt {.magic: "ParseStmtToAst".}

+  ## Compiles the passed string to its AST representation

+  ## Expects one or more statements

+

+proc getAst*(macroOrTemplate: expr): expr {.magic: "ExpandMacroToAst".}

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

+  ## example:

+  ## macro FooMacro() = 

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

+  

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

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

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

diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim
index d3346ecde..1f4e54b96 100755
--- a/lib/pure/parseutils.nim
+++ b/lib/pure/parseutils.nim
@@ -76,6 +76,18 @@ proc parseIdent*(s: string, ident: var string, start = 0): int =
     ident = substr(s, start, i-1)
     result = i-start
 
+proc parseIdent*(s: string, start = 0): string =
+  ## parses an identifier and stores it in ``ident``. 
+  ## Returns the parsed identifier or an empty string in case of an error.
+  result = ""
+  var i = start
+
+  if s[i] in IdentStartChars:
+    inc(i)
+    while s[i] in IdentChars: inc(i)
+    
+    result = substr(s, start, i-1)
+
 proc parseToken*(s: string, token: var string, validChars: set[char],
                  start = 0): int {.inline, deprecated.} =
   ## parses a token and stores it in ``token``. Returns
@@ -254,4 +266,94 @@ proc parseFloat*(s: string, number: var float, start = 0): int {.
   result = parseBiggestFloat(s, bf, start)
   number = bf
   
+proc isEscaped*(s: string, pos: int) : bool =
+  assert pos >= 0 and pos < s.len
+
+  var
+    backslashes = 0
+    j = pos - 1
+
+  while j >= 0:
+    if s[j] == '\\':
+      inc backslashes
+      dec j
+    else:
+      break
+
+  return backslashes mod 2 != 0
+
+type
+  TInterpolatedKind* = enum
+    ikString, ikExpr
+
+  TInterpStrFragment* = tuple[kind: TInterpolatedKind, value: string]
+
+iterator interpolatedFragments*(s: string): TInterpStrFragment =
+  var 
+    i = 0
+    tokenStart = 0
+
+  proc token(kind: TInterpolatedKind, value: string): TInterpStrFragment =
+    result.kind = kind
+    result.value = value
+
+  while i < s.len:
+    # The $ sign marks the start of an interpolation.
+    #
+    # It's followed either by a varialbe name or an opening bracket 
+    # (so it should be before the end of the string)
+    # if the dollar sign is escaped, don't trigger interpolation
+    if s[i] == '$' and i < (s.len - 1) and not isEscaped(s, i):
+      # Interpolation starts here.
+      # Return any string that we've ran over so far.
+      if i != tokenStart:
+        yield token(ikString, s[tokenStart..i-1])
+
+      var next = s[i+1]
+      if next == '{':
+        # Complex expression: ${foo(bar) in {1..100}}
+        # Find closing braket, while respecting any nested brackets
+        inc i
+        tokenStart = i + 1
+
+        var
+          brackets = {'{', '}'}
+          nestingCount = 1
+        
+        while i < s.len:
+          inc i, skipUntil(s, brackets, i+1) + 1
+          
+          if not isEscaped(s, i):
+            if s[i] == '}':
+              dec nestingCount
+              if nestingCount == 0: break
+            else:
+              inc nestingCount
+
+        yield token(ikExpr, s[tokenStart..(i-1)])
+
+        tokenStart = i + 1
+        
+      else:
+        tokenStart = i + 1
+        var identifier = parseIdent(s, i+1)
+        
+        if identifier.len >  0:
+          inc i, identifier.len
+
+          yield token(ikExpr, s[tokenStart..i])
+
+          tokenStart = i + 1
+
+        else:
+          raise newException(EInvalidValue, "Unable to parse a varible name at " & s[i..s.len])
+       
+    inc i
+  #end while
+
+  # We've reached the end of the string without finding a new interpolation.
+  # Return the last fragment at string.
+  if i != tokenStart:
+    yield token(ikString, s[tokenStart..i])
+
 {.pop.}
diff --git a/lib/pure/uuid.nim b/lib/pure/uuid.nim
new file mode 100644
index 000000000..36fa9e445
--- /dev/null
+++ b/lib/pure/uuid.nim
@@ -0,0 +1,30 @@
+# This module implements the RFC 4122 specification for generating universally unique identifiers
+# http://en.wikipedia.org/wiki/Universally_unique_identifier
+
+# This module is a work-in-progress
+# If you want to help with the implementation, take a loot at:
+# http://dsource.org/projects/tango/docs/current/tango.util.uuid.Uuid.html
+
+type TUuid* = array[0..15, char]
+
+when defined(windows):
+  # This is actually available only on Windows 2000+
+  type PUuid* {.importc: "UUID __RPC_FAR *", header: "<Rpc.h>".} = ptr TUuid
+  proc uuid1Sys*(uuid: PUuid) {.importc: "UuidCreateSequential", header: "<Rpc.h>".}
+
+else:
+  type PUuid {.importc: "uuid_t", header: "<uuid/uuid.h>".} = ptr TUuid
+  proc uuid1Sys*(uuid: PUuid) {.importc: "uuid_generate_time", header: "<uuid/uuid.h>".}
+
+# v1 UUIDs include the MAC address of the machine generating the ID and a timestamp
+# This scheme has the strongest guaranty of uniqueness, but discloses when the ID was generated
+proc uuidMacTime* : TUuid = uuid1Sys(addr(result))
+
+# v4 UUID are created entirely using a random number generator.
+# Some bits have fixed value in order to indicate the UUID type
+proc uuidRandom*[RandomGenerator](rand: RandomGenerator) : TUuid = nil
+
+# v3 and v5 UUIDs are derived from given namespace and name using a secure hashing algorithm.
+# v3 uses MD5, v5 uses SHA1.
+proc uuidByName*[Hash](namespace: TUuid, name: string, hasher: Hash, v: int) : TUuid = nil
+
diff --git a/lib/system.nim b/lib/system.nim
index 2e754ece7..3fc4733b2 100755
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -710,7 +710,7 @@ proc `&` * (x, y: string): string {.
 proc `&` * (x: char, y: string): string {.
   magic: "ConStrStr", noSideEffect, merge.}
   ## is the `concatenation operator`. It concatenates `x` and `y`.
-  
+
 # implementation note: These must all have the same magic value "ConStrStr" so
 # that the merge optimization works properly. 
 
@@ -895,7 +895,12 @@ type # these work for most platforms:
   PFloat64* = ptr Float64 ## an alias for ``ptr float64``
   PInt64* = ptr Int64 ## an alias for ``ptr int64``
   PInt32* = ptr Int32 ## an alias for ``ptr int32``
-  
+
+type TOptional*[T] = object
+  case hasValue* : bool
+  of true: value*: T
+  of false: nil
+ 
 proc toFloat*(i: int): float {.
   magic: "ToFloat", noSideEffect, importc: "toFloat".}
   ## converts an integer `i` into a ``float``. If the conversion
diff --git a/tests/accept/compile/tdumpast.nim b/tests/accept/compile/tdumpast.nim
index fb31af0ec..8561c6e42 100755
--- a/tests/accept/compile/tdumpast.nim
+++ b/tests/accept/compile/tdumpast.nim
@@ -2,28 +2,26 @@
 
 import macros
 
-proc dumpit(n: PNimrodNode): string {.compileTime.} = 
-  if n == nil: return "nil"
-  result = $n.kind
-  add(result, "(")
-  case n.kind
-  of nnkEmpty: nil # same as nil node in this representation 
-  of nnkNilLit:                  add(result, "nil")
-  of nnkCharLit..nnkInt64Lit:    add(result, $n.intVal)
-  of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)
-  of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal)
-  of nnkIdent:                   add(result, $n.ident)
-  of nnkSym, nnkNone:            assert false
-  else:
-    add(result, dumpit(n[0]))
-    for j in 1..n.len-1:
-      add(result, ", ")
-      add(result, dumpit(n[j]))
-  add(result, ")")
+template plus(a, b: expr): expr =
+  a + b
+
+macro call(e: expr): expr =
+  return newCall("foo", newStrLitNode("bar"))
   
-macro dumpAST(n: stmt): stmt = 
+macro dumpAST(n: stmt): stmt =
   # dump AST as a side-effect and return the inner node
-  echo dumpit(n)
+  echo n.prettyPrint
+  echo n.toYaml
+
+  var plusAst = getAst(plus(1, 2))
+  echo plusAst.prettyPrint
+
+  var callAst = getAst(call())
+  echo callAst.prettyPrint
+
+  var e = parseExpr("foo(bar + baz)")
+  echo e.prettyPrint
+
   result = n[1]
   
 dumpAST:
@@ -32,4 +30,3 @@ dumpAST:
   
   proc sub(x, y: int): int = return x - y
 
-
diff --git a/tests/accept/run/tstringinterp.nim b/tests/accept/run/tstringinterp.nim
new file mode 100644
index 000000000..55baae7ec
--- /dev/null
+++ b/tests/accept/run/tstringinterp.nim
@@ -0,0 +1,71 @@
+discard """
+  file: "tstringinterp.nim"
+  output: "Hello Alice, 64 | Hello Bob, 10"
+"""
+
+import macros, parseutils, strutils
+
+proc concat(strings: openarray[string]) : string =
+  result = newString(0)
+  for s in items(strings): result.add(s)
+
+# This will run though the intee
+template ProcessInterpolations(e: expr) =
+  var 
+    s = e[1].strVal
+    
+  for f in interpolatedFragments(s):
+    if f.kind  == ikString:
+      addString(f.value)
+    else:
+      addExpr(f.value)
+
+macro formatStyleInterpolation(e: expr): expr =
+  var 
+    formatString = ""
+    arrayNode = newNimNode(nnkBracket)
+    idx = 1
+
+  proc addString(s: string) =
+    formatString.add(s)
+
+  proc addExpr(e: expr) =
+    arrayNode.add(e)
+    formatString.add("$" & $(idx))
+    inc idx
+    
+  ProcessInterpolations(e)
+
+  result = parseExpr("\"x\" % [y]")
+  result[1].strVal = formatString
+  result[2] = arrayNode
+
+macro concatStyleInterpolation(e: expr): expr =
+  var args : seq[PNimrodNode]
+  newSeq(args, 0)
+
+  proc addString(s: string)  = args.add(newStrLitNode(s))
+  proc addExpr(e: expr)      = args.add(e)
+
+  ProcessInterpolations(e)
+
+  result = newCall("concat", args)
+
+###
+
+proc sum(a, b, c: int): int =
+  return (a + b + c)
+
+var 
+  alice = "Alice"
+  bob = "Bob"
+  a = 10
+  b = 20
+  c = 34
+
+var
+  s1 = concatStyleInterpolation"Hello ${alice}, ${sum (a, b, c)}}"
+  s2 = formatStyleInterpolation"Hello ${bob}, ${sum (alice.len, bob.len, 2)}"
+
+write(stdout, s1 & " | " & s2)
+
diff --git a/tests/accept/run/tusingstatement.nim b/tests/accept/run/tusingstatement.nim
new file mode 100644
index 000000000..0017af556
--- /dev/null
+++ b/tests/accept/run/tusingstatement.nim
@@ -0,0 +1,102 @@
+discard """
+  file: "tusingstatement.nim"
+  output: "Using test.Closing test."
+"""
+
+import 
+  macros
+
+# This macro mimics the using statement from C#
+#
+# XXX: 
+#  It doen't match the C# version exactly yet.
+#  In particular, it's not recursive, which prevents it from dealing 
+#  with exceptions thrown from the variable initializers when multiple.
+#  variables are used.
+#
+#  Also, since nimrod relies less on exceptions in general, a more
+#  idiomatic definition could be:
+#  var x = init()
+#  if opened(x): 
+#    try:
+#      body
+#    finally:
+#      close(x)
+#
+#  `opened` here could be an overloaded proc which any type can define.
+#  A common practice can be returing an Optional[Resource] obj for which
+#  `opened` is defined to `optional.hasValue`
+macro using(e: expr) : stmt =
+  if e.len != 2:
+    error "Using statement: unexpected number of arguments. Got " &
+      $e.len & ", expected: 1 or more variable assignments and a block"
+
+  var args = e[0]
+  var body = e[1]
+  
+  var 
+    variables : seq[PNimrodNode]
+    closingCalls : seq[PNimrodNode]
+
+  newSeq(variables, 0)
+  newSeq(closingCalls, 0)
+
+  for i in countup(1, args.len-1):
+    if args[i].kind == nnkExprEqExpr:
+      var varName = args[i][0]
+      var varValue = args[i][1]
+ 
+      var varAssignment = newNimNode(nnkIdentDefs)
+      varAssignment.add(varName)
+      varAssignment.add(newNimNode(nnkEmpty)) # empty means no type
+      varAssignment.add(varValue)
+      variables.add(varAssignment)
+
+      closingCalls.add(newCall(!"close", varName))
+    else:
+      error "Using statement: Unexpected expression. Got " &
+        $args[i].kind & " instead of assignment."
+  
+  var varSection = newNimNode(nnkVarSection)
+  varSection.add(variables)
+
+  var finallyBlock = newNimNode(nnkStmtList)
+  finallyBlock.add(closingCalls)
+
+  # XXX: Use a template here once getAst is working properly
+  var targetAst = parseStmt"""block:
+    var
+      x = foo()
+      y = bar()
+
+    try:
+      body()
+
+    finally:
+      close x
+      close y
+  """
+
+  targetAst[0][1][0] = varSection
+  targetAst[0][1][1][0] = body
+  targetAst[0][1][1][1][0] = finallyBlock
+  
+  return targetAst
+
+type 
+  TResource* = object
+    field*: string
+
+proc openResource(param: string): TResource =
+  result.field = param
+
+proc close(r: var TResource) =
+  write(stdout, "Closing " & r.field & ".")
+
+proc use(r: var TResource) =
+  write(stdout, "Using " & r.field & ".")
+
+using(r = openResource("test")):
+  use r
+
+