summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xcompiler/ast.nim3
-rwxr-xr-xcompiler/evals.nim2
-rwxr-xr-xcompiler/msgs.nim3
-rwxr-xr-xcompiler/sem.nim1
-rwxr-xr-xcompiler/semexprs.nim100
-rwxr-xr-xlib/core/macros.nim29
-rwxr-xr-xlib/system.nim4
-rwxr-xr-xweb/news.txt1
8 files changed, 129 insertions, 14 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 88cf5fd61..46be0379f 100755
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -415,7 +415,7 @@ type
     mDefined, mDefinedInScope, mCompiles,
     mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf,
     mEcho, mShallowCopy, mSlurp, mStaticExec,
-    mParseExprToAst, mParseStmtToAst, mExpandToAst,
+    mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst,
     mUnaryLt, mSucc, 
     mPred, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray, 
     mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, mGCref, 
@@ -723,6 +723,7 @@ const
 
   nkLambdaKinds* = {nkLambda, nkDo}
   nkSymChoices* = {nkClosedSymChoice, nkOpenSymChoice}
+  nkStrKinds* = {nkStrLit..nkTripleStrLit}
 
   skLocalVars* = {skVar, skLet, skForVar, skParam}
 
diff --git a/compiler/evals.nim b/compiler/evals.nim
index c6248e823..0d960c952 100755
--- a/compiler/evals.nim
+++ b/compiler/evals.nim
@@ -898,7 +898,7 @@ proc evalIsOp*(n: PNode): PNode =
   result.typ = n.typ
 
 proc expectString(n: PNode) =
-  if n.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
+  if n.kind notin nkStrKinds:
     GlobalError(n.info, errStringLiteralExpected)
 
 proc evalSlurp*(e: PNode, module: PSym): PNode =
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 50d7e2904..45075cba8 100755
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -457,6 +457,9 @@ proc newLineInfo*(filename: string, line, col: int): TLineInfo {.inline.} =
 fileInfos.add(newFileInfo("", "command line"))
 var gCmdLineInfo* = newLineInfo(int32(0), 1, 1)
 
+fileInfos.add(newFileInfo("", "compilation artifact"))
+var gCodegenLineInfo* = newLineInfo(int32(1), 1, 1)
+
 proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} =
   raise newException(ERecoverableError, msg)
 
diff --git a/compiler/sem.nim b/compiler/sem.nim
index bd1ad1468..911eafb08 100755
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -45,6 +45,7 @@ proc instGenericContainer(c: PContext, n: PNode, header: PType): PType
 proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
 proc fixImmediateParams(n: PNode): PNode
 proc activate(c: PContext, n: PNode)
+proc semQuoteAst(c: PContext, n: PNode): PNode
 
 proc typeMismatch(n: PNode, formal, actual: PType) = 
   if formal.kind != tyError and actual.kind != tyError: 
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 2b39f6190..a047fed20 100755
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -1241,25 +1241,100 @@ proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym =
     LocalError(n.info, errXisNoMacroOrTemplate, n.renderTree)
     result = errorSym(c, n)
 
-proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym,
-                    flags: TExprFlags): PNode =
-  if sonsLen(n) == 2:
-    var macroCall = n[1]
-    var expandedSym = expectMacroOrTemplateCall(c, macroCall)
+proc expectString(c: PContext, n: PNode): string =
+  var n = semConstExpr(c, n)
+  if n.kind in nkStrKinds:
+    return n.strVal
+  else:
+    LocalError(n.info, errStringLiteralExpected)
+
+proc getMagicSym(magic: TMagic): PSym =
+  result = newSym(skProc, getIdent($magic), GetCurrOwner(), gCodegenLineInfo)
+  result.magic = magic
+
+proc newAnonSym(kind: TSymKind, info: TLineInfo,
+                owner = getCurrOwner()): PSym =
+  result = newSym(kind, idAnon, owner, info)
+  result.flags = { sfGenSym }
 
-    macroCall.sons[0] = newSymNode(expandedSym, macroCall.info)
-    markUsed(n, expandedSym)
+proc semExpandToAst(c: PContext, n: PNode): PNode =
+  var macroCall = n[1]
+  var expandedSym = expectMacroOrTemplateCall(c, macroCall)
 
-    for i in countup(1, macroCall.len-1):
-      macroCall.sons[i] = semExprWithType(c, macroCall[i], {})
+  macroCall.sons[0] = newSymNode(expandedSym, macroCall.info)
+  markUsed(n, expandedSym)
 
-    # Preserve the magic symbol in order to be handled in evals.nim
+  for i in countup(1, macroCall.len-1):
+    macroCall.sons[i] = semExprWithType(c, macroCall[i], {})
+
+  # Preserve the magic symbol in order to be handled in evals.nim
+  InternalAssert n.sons[0].sym.magic == mExpandToAst
+  n.typ = getSysSym("PNimrodNode").typ # expandedSym.getReturnType
+  result = n
+
+proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym,
+                    flags: TExprFlags = {}): PNode =
+  if sonsLen(n) == 2:
     n.sons[0] = newSymNode(magicSym, n.info)
-    n.typ = getSysSym("PNimrodNode").typ # expandedSym.getReturnType
-    result = n
+    result = semExpandToAst(c, n)
   else:
     result = semDirectOp(c, n, flags)
 
+proc processQuotations(n: var PNode, op: string,
+                       quotes: var seq[PNode],
+                       ids: var seq[PNode]) =
+  template returnQuote(q) =
+    quotes.add q
+    n = newIdentNode(getIdent($quotes.len), n.info)
+    ids.add n
+    return
+
+  if n.kind == nkPrefix:
+    checkSonsLen(n, 2)
+    if n[0].kind == nkIdent:
+      var examinedOp = n[0].ident.s
+      if examinedOp == op:
+        returnQuote n[1]
+      elif examinedOp.startsWith(op):
+        n.sons[0] = newIdentNode(getIdent(examinedOp.substr(op.len)), n.info)
+  elif n.kind == nkAccQuoted and op == "``":
+    returnQuote n[0]
+ 
+  if not n.isAtom:
+    for i in 0 .. <n.len:
+      processQuotations(n.sons[i], op, quotes, ids)
+
+proc semQuoteAst(c: PContext, n: PNode): PNode =
+  InternalAssert n.len == 2 or n.len == 3
+  # We transform the do block into a template with a param for
+  # each interpolation. We'll pass this template to getAst.
+  var
+    doBlk = n{-1}
+    op = if n.len == 3: expectString(c, n[1]) else: "``"
+    quotes = newSeq[PNode](1)
+      # the quotes will be added to a nkCall statement 
+      # leave some room for the callee symbol
+    ids = newSeq[PNode]()
+      # this will store the generated param names
+
+  internalAssert doBlk.kind == nkDo
+  processQuotations(doBlk.sons[bodyPos], op, quotes, ids)
+  
+  doBlk.sons[namePos] = newAnonSym(skTemplate, n.info).newSymNode
+  if ids.len > 0:
+    doBlk[paramsPos].sons.setLen(2)
+    doBlk[paramsPos].sons[0] = getSysSym("stmt").newSymNode # return type
+    ids.add getSysSym("expr").newSymNode # params type
+    ids.add emptyNode # no default value
+    doBlk[paramsPos].sons[1] = newNode(nkIdentDefs, n.info, ids)
+  
+  var tmpl = semTemplateDef(c, doBlk)
+  quotes[0] = tmpl[namePos]
+  result = newNode(nkCall, n.info, @[
+    getMagicSym(mExpandToAst).newSymNode,
+    newNode(nkCall, n.info, quotes)])
+  result = semExpandToAst(c, result)
+
 proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   # watch out, hacks ahead:
   let oldErrorCount = msgs.gErrorCounter
@@ -1335,6 +1410,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
   of mEcho: result = semEcho(c, setMs(n, s))
   of mShallowCopy: result = semShallowCopy(c, n, flags)
   of mExpandToAst: result = semExpandToAst(c, n, s, flags)
+  of mQuoteAst: result = semQuoteAst(c, n)
   else: result = semDirectOp(c, n, flags)
 
 proc semIfExpr(c: PContext, n: PNode): PNode = 
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index be6aecab5..b80de27fa 100755
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -234,6 +234,35 @@ proc getAst*(macroOrTemplate: expr): PNimrodNode {.magic: "ExpandToAst".}
   ##   macro FooMacro() =
   ##     var ast = getAst(BarTemplate())
 
+proc quote*(bl: stmt, op = "``"): PNimrodNode {.magic: "QuoteAst".}
+  ## Quasi-quoting operator.
+  ## Accepts an expression or a block and returns the AST that represents it.
+  ## Within the quoted AST, you are able to interpolate PNimrodNode expressions
+  ## from the surrounding scope. If no operator is given, quoting is done using
+  ## backticks. Otherwise, the given operator must be used as a prefix operator
+  ## for any interpolated expression. The original meaning of the interpolation
+  ## operator may be obtained by escaping it (by prefixing it with itself):
+  ## e.g. `@` is escaped as `@@`, `@@` is escaped as `@@@` and so on.
+  ##
+  ## Example:
+  ##   
+  ##   macro check(ex: expr): stmt =
+  ##     # this is a simplified version of the check macro from the
+  ##     # unittest module.
+  ##
+  ##     # If there is a failed check, we want to make it easy for
+  ##     # the user to jump to the faulty line in the code, so we
+  ##     # get the line info here:
+  ##     var info = ex.lineinfo
+  ##
+  ##     # We will also display the code string of the failed check:
+  ##     var expString = ex.toStrLit
+  ##
+  ##     # Finally we compose the code to implement the check:
+  ##     result = quote do:
+  ##       if not `ex`:
+  ##         echo `info` & ": Check failed: " & `expString`
+  
 template emit*(e: expr[string]): stmt =
   ## accepts a single string argument and treats it as nimrod code
   ## that should be inserted verbatim in the program
diff --git a/lib/system.nim b/lib/system.nim
index 5a4c491e1..b681ed51e 100755
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -364,6 +364,10 @@ proc newSeq*[T](s: var seq[T], len: int) {.magic: "NewSeq", noSideEffect.}
   ## This is equivalent to ``s = @[]; setlen(s, len)``, but more
   ## efficient since no reallocation is needed.
 
+proc newSeq*[T](len = 0): seq[T] =
+  ## creates a new sequence of type ``seq[T]`` with length ``len``.
+  newSeq(result, len)
+
 proc len*[TOpenArray: openArray|varargs](x: TOpenArray): int {.
   magic: "LengthOpenArray", noSideEffect.}
 proc len*(x: string): int {.magic: "LengthStr", noSideEffect.}
diff --git a/web/news.txt b/web/news.txt
index c7f3077e1..b73a32268 100755
--- a/web/news.txt
+++ b/web/news.txt
@@ -16,6 +16,7 @@ Library Additions
 -----------------
 
 - Added ``system.onRaise`` to support a condition system.
+- Added ``macros.quote`` for AST quasi-quoting.
 
 
 Changes affecting backwards compatibility