summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xcompiler/astalgo.nim7
-rwxr-xr-xcompiler/pragmas.nim11
-rwxr-xr-xcompiler/seminst.nim4
-rwxr-xr-xcompiler/semstmts.nim36
-rwxr-xr-xdoc/manual.txt23
-rwxr-xr-xtodo.txt1
-rwxr-xr-xweb/news.txt1
7 files changed, 76 insertions, 7 deletions
diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim
index 41fbd3cf1..70cb8653e 100755
--- a/compiler/astalgo.nim
+++ b/compiler/astalgo.nim
@@ -91,6 +91,7 @@ type
 proc InitSymTab*(tab: var TSymTab)
 proc DeinitSymTab*(tab: var TSymTab)
 proc SymTabGet*(tab: TSymTab, s: PIdent): PSym
+proc SymTabGet*(tab: TSymTab, s: PIdent, filter: TSymKinds): PSym
 proc SymTabLocalGet*(tab: TSymTab, s: PIdent): PSym
 proc SymTabAdd*(tab: var TSymTab, e: PSym)
 proc SymTabAddAt*(tab: var TSymTab, e: PSym, at: Natural)
@@ -703,6 +704,12 @@ proc SymTabGet(tab: TSymTab, s: PIdent): PSym =
     if result != nil: return 
   result = nil
 
+proc SymTabGet*(tab: TSymTab, s: PIdent, filter: TSymKinds): PSym =
+  for i in countdown(tab.tos - 1, 0): 
+    result = StrTableGet(tab.stack[i], s)
+    if result != nil and result.kind in filter: return
+  result = nil
+
 proc SymTabAddAt(tab: var TSymTab, e: PSym, at: Natural) = 
   StrTableAdd(tab.stack[at], e)
 
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index d4a635058..348bf9bc6 100755
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -431,17 +431,20 @@ proc processPragma(c: PContext, n: PNode, i: int) =
   for j in i+1 .. sonsLen(n)-1: addSon(body, n.sons[j])
   userPragma.ast = body
   StrTableAdd(c.userPragmas, userPragma)
-        
-proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) = 
-  if n == nil: return 
+
+proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
+  if n == nil: return
   for i in countup(0, sonsLen(n) - 1): 
     var it = n.sons[i]
     var key = if it.kind == nkExprColonExpr: it.sons[0] else: it
     if key.kind == nkIdent: 
       var userPragma = StrTableGet(c.userPragmas, key.ident)
       if userPragma != nil: 
+        inc c.InstCounter
+        if c.InstCounter > 100: 
+          GlobalError(it.info, errRecursiveDependencyX, userPragma.name.s)
         pragma(c, sym, userPragma.ast, validPragmas)
-        # XXX BUG: possible infinite recursion!
+        dec c.InstCounter
       else:
         var k = whichKeyword(key.ident)
         if k in validPragmas: 
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index f44b80223..a2f4129ea 100755
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -139,6 +139,8 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   # generates an instantiated proc
   if c.InstCounter > 1000: InternalError(fn.ast.info, "nesting too deep")
   inc(c.InstCounter)
+  # careful! we copy the whole AST including the possibly nil body!
+  var n = copyTree(fn.ast)
   # NOTE: for access of private fields within generics from a different module
   # we set the friend module:
   var oldFriend = c.friendModule
@@ -148,8 +150,6 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   # keep the owner if it's an inner proc (for proper closure transformations):
   if fn.owner.kind == skModule:
     result.owner = getCurrOwner().owner
-  # careful! we copy the whole AST including the possibly nil body!
-  var n = copyTree(fn.ast)
   result.ast = n
   pushOwner(result)
   openScope(c.tab)
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index eb7660dd6..bc7f00c87 100755
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -644,8 +644,42 @@ proc addResult(c: PContext, t: PType, info: TLineInfo, owner: TSymKind) =
 
 proc addResultNode(c: PContext, n: PNode) = 
   if c.p.resultSym != nil: addSon(n, newSymNode(c.p.resultSym))
+
+proc copyExcept(n: PNode, i: int): PNode =
+  result = copyNode(n)
+  for j in 0.. <n.len:
+    if j != i: result.add(n.sons[j])
+
+proc lookupMacro(c: PContext, n: PNode): PSym =
+  if n.kind == nkSym:
+    result = n.sym
+    if result.kind notin {skMacro, skTemplate}: result = nil
+  else:
+    result = SymtabGet(c.Tab, considerAcc(n), {skMacro, skTemplate})
+
+proc semProcAnnotation(c: PContext, prc: PNode): PNode =
+  var n = prc.sons[pragmasPos]
+  if n == nil or n.kind == nkEmpty: return
+  for i in countup(0, <n.len):
+    var it = n.sons[i]
+    var key = if it.kind == nkExprColonExpr: it.sons[0] else: it
+    let m = lookupMacro(c, key)
+    if m == nil: continue
+    # we transform ``proc p {.m, rest.}`` into ``m: proc p {.rest.}`` and
+    # let the semantic checker deal with it:
+    var x = newNodeI(nkMacroStmt, n.info)
+    x.add(newSymNode(m))
+    prc.sons[pragmasPos] = copyExcept(n, i)
+    if it.kind == nkExprColonExpr:
+      # pass pragma argument to the macro too:
+      x.add(it.sons[1])
+    x.add(prc)
+    # recursion assures that this works for multiple macro annotations too:
+    return semStmt(c, x)
   
 proc semLambda(c: PContext, n: PNode): PNode = 
+  result = semProcAnnotation(c, n)
+  if result != nil: return result
   result = n
   checkSonsLen(n, bodyPos + 1)
   var s = newSym(skProc, getIdent":anonymous", getCurrOwner())
@@ -686,6 +720,8 @@ proc instantiateDestructor*(c: PContext, typ: PType): bool
 
 proc semProcAux(c: PContext, n: PNode, kind: TSymKind, 
                 validPragmas: TSpecialWords): PNode = 
+  result = semProcAnnotation(c, n)
+  if result != nil: return result
   result = n
   checkSonsLen(n, bodyPos + 1)
   var s = semIdentDef(c, n.sons[0], kind)
diff --git a/doc/manual.txt b/doc/manual.txt
index 1641f5338..8f9186ec6 100755
--- a/doc/manual.txt
+++ b/doc/manual.txt
@@ -1680,7 +1680,8 @@ code that has side effects:
   static:

     echo "echo at compile time"

 

-It's a static error if the compiler cannot perform the evaluation at compile time.

+It's a static error if the compiler cannot perform the evaluation at compile 
+time.

 

 The current implementation poses some restrictions for compile time

 evaluation: Code which contains ``cast`` or makes use of the foreign function

@@ -2958,6 +2959,26 @@ powerful programming construct that still suffices. So the "check list" is:
 (2) Else: Use a generic proc/iterator, if possible.

 (3) Else: Use a template, if possible.

 (4) Else: Use a macro.

+
+
+Macros as pragmas

+~~~~~~~~~~~~~~~~~

+
+Whole routines (procs, iterators etc.) can also be passed to a template or 
+a macro via the pragma notation: 
+
+.. code-block:: nimrod
+  template m(s: stmt) = nil
+
+  proc p() {.m.} = nil
+
+This is a simple syntactic transformation into:
+
+.. code-block:: nimrod
+  template m(s: stmt) = nil
+
+  m:
+    proc p() = nil
 

 

 Modules

diff --git a/todo.txt b/todo.txt
index 9b9755664..c77b8680f 100755
--- a/todo.txt
+++ b/todo.txt
@@ -11,6 +11,7 @@ New pragmas:
 - ``borrow`` needs to take type classes into account
 - make templates hygienic by default: try to gensym() everything in the 'block'
   of a template; find a better solution for gensym instead of `*ident`
+- make use of ``tyIter`` to fix the implicit items/pairs issue
 - ``=`` should be overloadable; requires specialization for ``=``
 - optimize genericAssign in the code generator
 - fix remaining closure bugs:
diff --git a/web/news.txt b/web/news.txt
index c7dbc5971..8a53b6853 100755
--- a/web/news.txt
+++ b/web/news.txt
@@ -118,6 +118,7 @@ Language Additions
 - The precedence for operators starting with ``@`` is different now 
   allowing for *sigil-like* operators.
 - Stand-alone ``finally`` and ``except`` blocks are now supported.
+- Macros and templates can now be invoked as pragmas.
 
 
 2012-02-09 Version 0.8.14 released