summary refs log tree commit diff stats
path: root/compiler/semtempl.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/semtempl.nim')
-rwxr-xr-xcompiler/semtempl.nim122
1 files changed, 103 insertions, 19 deletions
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index f77dbf1a2..efdfce78f 100755
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -89,7 +89,7 @@ proc semBindStmt(c: PContext, n: PNode, toBind: var TIntSet): PNode =
         for x in items(sc): toBind.incl(x.sym.id)
     else:
       illFormedAst(a)
-  result = newNodeI(nkEmpty, n.info)
+  result = newNodeI(nkNilLit, n.info)
   
 proc replaceIdentBySym(n: var PNode, s: PNode) =
   case n.kind
@@ -98,16 +98,10 @@ proc replaceIdentBySym(n: var PNode, s: PNode) =
   of nkIdent, nkAccQuoted, nkSym: n = s
   else: illFormedAst(n)
 
-# This code here is the first pass over a template's body. The same code also
-# implements the first pass over a pattern's body:
-
 type
-  TBodyKind = enum
-    bkTemplate, bkPattern
   TemplCtx {.pure, final.} = object
     c: PContext
     toBind: TIntSet
-    bodyKind: TBodyKind
     owner: PSym
 
 proc getIdentNode(c: var TemplCtx, n: PNode): PNode =
@@ -188,8 +182,6 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
         result = newSymNode(s, n.info)
       elif Contains(c.toBind, s.id):
         result = symChoice(c.c, n, s, scClosed)
-      elif c.bodyKind == bkPattern:
-        result = symChoice(c.c, n, s, scOpen)
       elif s.owner == c.owner and sfGenSym in s.flags:
         # template tmp[T](x: var seq[T]) =
         # var yz: T
@@ -305,8 +297,9 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
     # so we use the generic code for nkDotExpr too
     if n.kind == nkDotExpr or n.kind == nkAccQuoted:
       let s = QualifiedLookUp(c.c, n, {})
-      if s != nil and Contains(c.toBind, s.id):
-        return symChoice(c.c, n, s, scClosed)
+      if s != nil:
+        if Contains(c.toBind, s.id):
+          return symChoice(c.c, n, s, scClosed)
     result = n
     for i in countup(0, sonsLen(n) - 1):
       result.sons[i] = semTemplBody(c, n.sons[i])
@@ -405,7 +398,6 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
   ctx.toBind = initIntSet()
   ctx.c = c
   ctx.owner = s
-  ctx.bodyKind = bkTemplate
   if sfDirty in s.flags:
     n.sons[bodyPos] = semTemplBodyDirty(ctx, n.sons[bodyPos])
   else:
@@ -426,17 +418,109 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
   else:
     SymTabReplace(c.tab.stack[curScope], proto, s)
 
+proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
+  template templToExpand(s: expr): expr =
+    s.kind == skTemplate and (s.typ.len == 1 or sfImmediate in s.flags)
+  
+  proc handleSym(c: var TemplCtx, n: PNode, s: PSym): PNode =
+    if s != nil:
+      if s.owner == c.owner and s.kind == skParam:
+        incl(s.flags, sfUsed)
+        result = newSymNode(s, n.info)
+      elif Contains(c.toBind, s.id):
+        result = symChoice(c.c, n, s, scClosed)
+      elif templToExpand(s):
+        result = semPatternBody(c, semTemplateExpr(c.c, n, s, false))
+      else:
+        result = symChoice(c.c, n, s, scOpen)
+    else:
+      result = n
+  
+  proc expectParam(c: var TemplCtx, n: PNode): PNode =
+    let s = QualifiedLookUp(c.c, n, {})
+    if s != nil and s.owner == c.owner and s.kind == skParam:
+      incl(s.flags, sfUsed)
+      result = newSymNode(s, n.info)
+    else:
+      localError(n.info, errInvalidExpression)
+      result = n
+  
+  result = n
+  case n.kind
+  of nkIdent:
+    let s = QualifiedLookUp(c.c, n, {})
+    result = handleSym(c, n, s)
+  of nkBindStmt:
+    result = semBindStmt(c.c, n, c.toBind)
+  of nkEmpty, nkSym..nkNilLit: nil
+  of nkCurlyExpr:
+    # we support '(pattern){x}' to bind a subpattern to a parameter 'x':
+    if n.len != 2 or n.sons[1].kind != nkIdent:
+      localError(n.info, errInvalidExpression)
+    else:
+      n.sons[0] = semPatternBody(c, n.sons[0])
+      n.sons[1] = expectParam(c, n.sons[1])
+  of nkCallKinds:
+    let s = QualifiedLookUp(c.c, n.sons[0], {})
+    if s != nil:
+      if s.owner == c.owner and s.kind == skParam: nil
+      elif Contains(c.toBind, s.id): nil
+      elif templToExpand(s):
+        return semPatternBody(c, semTemplateExpr(c.c, n, s, false))
+    
+    if n.kind == nkInfix and n.sons[0].kind == nkIdent:
+      # we interpret `*` and `|` only as pattern operators if they occur in
+      # infix notation, so that '`*`(a, b)' can be used for verbatim matching:
+      let opr = n.sons[0]
+      if opr.ident.s == "*":
+        result = newNodeI(nkPattern, n.info, n.len)
+        result.sons[0] = opr
+        result.sons[1] = semPatternBody(c, n.sons[1])
+        result.sons[2] = expectParam(c, n.sons[2])
+        return
+      elif opr.ident.s == "|":
+        result = newNodeI(nkPattern, n.info, n.len)
+        result.sons[0] = opr
+        result.sons[1] = semPatternBody(c, n.sons[1])
+        result.sons[2] = semPatternBody(c, n.sons[2])
+        return
+    
+    if n.kind == nkPrefix and n.sons[0].kind == nkIdent:
+      let opr = n.sons[0]
+      if opr.ident.s == "~":
+        result = newNodeI(nkPattern, n.info, n.len)
+        result.sons[0] = opr
+        result.sons[1] = semPatternBody(c, n.sons[1])
+        return
+    
+    for i in countup(0, sonsLen(n) - 1):
+      result.sons[i] = semPatternBody(c, n.sons[i])
+  else:
+    # dotExpr is ambiguous: note that we explicitely allow 'x.TemplateParam',
+    # so we use the generic code for nkDotExpr too
+    case n.kind 
+    of nkDotExpr, nkAccQuoted:
+      let s = QualifiedLookUp(c.c, n, {})
+      if s != nil:
+        if Contains(c.toBind, s.id):
+          return symChoice(c.c, n, s, scClosed)
+        return symChoice(c.c, n, s, scOpen)
+    of nkPar:
+      if n.len == 1: return semPatternBody(c, n.sons[0])
+    else: nil
+    for i in countup(0, sonsLen(n) - 1):
+      result.sons[i] = semPatternBody(c, n.sons[i])
+
 proc semPattern(c: PContext, n: PNode): PNode =
-  # not much to do here: We don't replace operators ``$``, ``*``, ``+``,
-  # ``|``, ``~`` as meta operators and strip the leading ``\`` of all
-  # operators.
   openScope(c.tab)
   var ctx: TemplCtx
   ctx.toBind = initIntSet()
   ctx.c = c
   ctx.owner = getCurrOwner()
-  ctx.bodyKind = bkPattern
-  result = semTemplBody(ctx, n)
-  if result.kind in {nkStmtList, nkStmtListExpr} and result.len == 1:
-    result = result.sons[0]
+  result = flattenStmts(semPatternBody(ctx, n))
+  if result.kind in {nkStmtList, nkStmtListExpr}:
+    if result.len == 1:
+      result = result.sons[0]
+    elif result.len == 0:
+      LocalError(n.info, errInvalidExpression)
   closeScope(c.tab)