summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2012-09-03 00:55:44 +0200
committerAraq <rumpf_a@web.de>2012-09-03 00:55:44 +0200
commitaf7c92c0038763db2ba7d7049d7d18363b15089e (patch)
tree1849c68021e717adceab0533c5a66a6699b76b6c /compiler
parentb56df72a325e4991128d808dc6c9edade3d0f293 (diff)
downloadNim-af7c92c0038763db2ba7d7049d7d18363b15089e.tar.gz
term rewriting macros fully implemented; still buggy
Diffstat (limited to 'compiler')
-rwxr-xr-xcompiler/ast.nim23
-rwxr-xr-xcompiler/ccgexprs.nim4
-rw-r--r--compiler/evaltempl.nim62
-rwxr-xr-xcompiler/msgs.nim9
-rw-r--r--compiler/parampatterns.nim205
-rw-r--r--compiler/patterns.nim200
-rwxr-xr-xcompiler/renderer.nim17
-rwxr-xr-xcompiler/rodread.nim3
-rwxr-xr-xcompiler/rodwrite.nim3
-rwxr-xr-xcompiler/sem.nim50
-rwxr-xr-xcompiler/semexprs.nim29
-rwxr-xr-xcompiler/semfold.nim12
-rwxr-xr-xcompiler/semstmts.nim2
-rwxr-xr-xcompiler/semtempl.nim122
-rwxr-xr-xcompiler/semtypes.nim11
-rwxr-xr-xcompiler/trees.nim16
-rwxr-xr-xcompiler/types.nim8
17 files changed, 658 insertions, 118 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 52502c5de..2b0fe6470 100755
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -176,7 +176,6 @@ type
     nkFromStmt,           # a from * import statement
     nkIncludeStmt,        # an include statement
     nkBindStmt,           # a bind statement
-    nkPattern,            # a pattern statement ('as' statement)
     nkCommentStmt,        # a comment statement
     nkStmtListExpr,       # a statement list followed by an expr; this is used
                           # to allow powerful multi-line templates
@@ -201,6 +200,8 @@ type
     nkProcTy,             # proc type
     nkEnumTy,             # enum body
     nkEnumFieldDef,       # `ident = expr` in an enumeration
+    nkArgList,            # argument list
+    nkPattern,            # a special pattern; used for matching
     nkReturnToken,        # token used for interpretation
     nkClosure,            # (prc, env)-pair (internally used for code gen)
     nkGotoState,          # used for the state machine (for iterators)
@@ -584,7 +585,7 @@ type
                               # for a conditional:
                               # 1 iff the symbol is defined, else 0
                               # (or not in symbol table)
-                              # for modules, a unique index correspinding
+                              # for modules, a unique index corresponding
                               # to the order of compilation 
     offset*: int              # offset of record field
     loc*: TLoc
@@ -616,6 +617,7 @@ type
     align*: int               # the type's alignment requirements
     containerID*: int         # used for type checking of generics
     loc*: TLoc
+    constraint*: PNode        # additional constraints like 'lit|result'
 
   TPair*{.final.} = object 
     key*, val*: PObject
@@ -865,13 +867,22 @@ proc newSymNode*(sym: PSym, info: TLineInfo): PNode =
   result.typ = sym.typ
   result.info = info
 
-proc newNodeI(kind: TNodeKind, info: TLineInfo): PNode = 
-  result = newNode(kind)
+proc newNodeI(kind: TNodeKind, info: TLineInfo): PNode =
+  new(result)
+  result.kind = kind
+  result.info = info
+
+proc newNodeI*(kind: TNodeKind, info: TLineInfo, children: int): PNode =
+  new(result)
+  result.kind = kind
   result.info = info
+  if children > 0:
+    newSeq(result.sons, children)
 
 proc newNode*(kind: TNodeKind, info: TLineInfo, sons: TNodeSeq = @[],
              typ: PType = nil): PNode =
-  result = newNode(kind)
+  new(result)
+  result.kind = kind
   result.info = info
   result.typ = typ
   # XXX use shallowCopy here for ownership transfer:
@@ -1184,3 +1195,5 @@ proc hasPattern*(s: PSym): bool {.inline.} =
 iterator items*(n: PNode): PNode =
   for i in 0.. <n.len: yield n.sons[i]
 
+proc isAtom*(n: PNode): bool {.inline.} =
+  result = n.kind >= nkNone and n.kind <= nkNilLit
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index ba4111e9e..d10b37432 100755
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -1246,10 +1246,10 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     of mIncl:
       var ts = "NI" & $(size * 8)
       binaryStmtInExcl(p, e, d,
-          "$1 |=(1<<((" & ts & ")($2)%(sizeof(" & ts & ")*8)));$n")
+          "$1 |=((" & ts & ")(1)<<(($2)%(sizeof(" & ts & ")*8)));$n")
     of mExcl:
       var ts = "NI" & $(size * 8)
-      binaryStmtInExcl(p, e, d, "$1 &= ~(1 << ((" & ts & ")($2) % (sizeof(" &
+      binaryStmtInExcl(p, e, d, "$1 &= ~((" & ts & ")(1) << (($2) % (sizeof(" &
           ts & ")*8)));$n")
     of mCard:
       if size <= 4: unaryExprChar(p, e, d, "#countBits32($1)")
diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim
index d7006d34d..265fe3fe1 100644
--- a/compiler/evaltempl.nim
+++ b/compiler/evaltempl.nim
@@ -19,14 +19,17 @@ type
     mapping: TIdTable # every gensym'ed symbol needs to be mapped to some
                       # new symbol
 
-proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx): PNode =
-  #inc genSymBaseId
+proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
   case templ.kind
   of nkSym:
     var s = templ.sym
     if s.owner.id == c.owner.id:
       if s.kind == skParam:
-        result = copyTree(actual.sons[s.position])
+        let x = actual.sons[s.position]
+        if x.kind == nkArgList:
+          for y in items(x): result.add(y)
+        else:
+          result.add copyTree(x)
       else:
         InternalAssert sfGenSym in s.flags
         var x = PSym(IdTableGet(c.mapping, s))
@@ -34,16 +37,42 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx): PNode =
           x = copySym(s, false)
           x.owner = c.genSymOwner
           IdTablePut(c.mapping, s, x)
-        result = newSymNode(x, templ.info)
+        result.add newSymNode(x, templ.info)
     else:
-      result = copyNode(templ)
+      result.add copyNode(templ)
   of nkNone..nkIdent, nkType..nkNilLit: # atom
-    result = copyNode(templ)
+    result.add copyNode(templ)
   else:
-    result = copyNode(templ)
-    newSons(result, sonsLen(templ))
+    var res = copyNode(templ)
     for i in countup(0, sonsLen(templ) - 1): 
-      result.sons[i] = evalTemplateAux(templ.sons[i], actual, c)
+      evalTemplateAux(templ.sons[i], actual, c, res)
+    result.add res
+
+when false:
+  proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx): PNode =
+    case templ.kind
+    of nkSym:
+      var s = templ.sym
+      if s.owner.id == c.owner.id:
+        if s.kind == skParam:
+          result = copyTree(actual.sons[s.position])
+        else:
+          InternalAssert sfGenSym in s.flags
+          var x = PSym(IdTableGet(c.mapping, s))
+          if x == nil:
+            x = copySym(s, false)
+            x.owner = c.genSymOwner
+            IdTablePut(c.mapping, s, x)
+          result = newSymNode(x, templ.info)
+      else:
+        result = copyNode(templ)
+    of nkNone..nkIdent, nkType..nkNilLit: # atom
+      result = copyNode(templ)
+    else:
+      result = copyNode(templ)
+      newSons(result, sonsLen(templ))
+      for i in countup(0, sonsLen(templ) - 1): 
+        result.sons[i] = evalTemplateAux(templ.sons[i], actual, c)
 
 proc evalTemplateArgs(n: PNode, s: PSym): PNode =
   # if the template has zero arguments, it can be called without ``()``
@@ -78,6 +107,19 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym): PNode =
   ctx.owner = tmpl
   ctx.genSymOwner = genSymOwner
   initIdTable(ctx.mapping)
-  result = evalTemplateAux(tmpl.getBody, args, ctx)
+  
+  let body = tmpl.getBody
+  if isAtom(body): 
+    result = newNodeI(nkPar, body.info)
+    evalTemplateAux(body, args, ctx, result)
+    if result.len == 1: result = result.sons[0]
+    else:
+      GlobalError(result.info, errIllFormedAstX,
+                  renderTree(result, {renderNoComments}))
+  else:
+    result = copyNode(body)
+    #evalTemplateAux(body, args, ctx, result)
+    for i in countup(0, safeLen(body) - 1):
+      evalTemplateAux(body.sons[i], args, ctx, result)
   
   dec(evalTemplateCounter)
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index f61e03e79..d0ddf7721 100755
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -109,7 +109,7 @@ type
     hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, 
     hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, 
     hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath, 
-    hintConditionAlwaysTrue,
+    hintConditionAlwaysTrue, hintPattern,
     hintUser
 
 const 
@@ -152,7 +152,7 @@ const
     errExceptionAlreadyHandled: "exception already handled", 
     errYieldNotAllowedHere: "'yield' only allowed in an iterator",
     errYieldNotAllowedInTryStmt: "'yield' cannot be used within 'try' in a non-inlined iterator",
-    errInvalidNumberOfYieldExpr: "invalid number of \'yield\' expresions", 
+    errInvalidNumberOfYieldExpr: "invalid number of \'yield\' expressions", 
     errCannotReturnExpr: "current routine cannot return an expression", 
     errAttemptToRedefine: "redefinition of \'$1\'", 
     errStmtInvalidAfterReturn: "statement not allowed after \'return\', \'break\' or \'raise\'", 
@@ -366,6 +366,7 @@ const
     hintConf: "used config file \'$1\' [Conf]", 
     hintPath: "added path: '$1' [Path]",
     hintConditionAlwaysTrue: "condition is always true: '$1' [CondTrue]",
+    hintPattern: "$1 [Pattern]",
     hintUser: "$1 [User]"]
 
 const
@@ -378,10 +379,10 @@ const
     "AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap",
     "ImplicitClosure", "EachIdentIsTuple", "User"]
 
-  HintsToStr*: array[0..14, string] = ["Success", "SuccessX", "LineTooLong", 
+  HintsToStr*: array[0..15, string] = ["Success", "SuccessX", "LineTooLong", 
     "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", 
     "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf", 
-    "Path", "CondTrue",
+    "Path", "CondTrue", "Pattern",
     "User"]
 
 const 
diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim
new file mode 100644
index 000000000..83585dbd4
--- /dev/null
+++ b/compiler/parampatterns.nim
@@ -0,0 +1,205 @@
+#
+#
+#           The Nimrod Compiler
+#        (c) Copyright 2012 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements the pattern matching features for term rewriting
+## macro support.
+
+import strutils, ast, astalgo, types, msgs, idents, renderer, wordrecg
+
+# we precompile the pattern here for efficiency into some internal
+# stack based VM :-) Why? Because it's fun; I did no benchmarks to see if that
+# actually improves performance.
+type
+  TAliasRequest* = enum # first byte of the bytecode determines alias checking
+    aqNone = 1,         # no alias analysis requested
+    aqShouldAlias,      # with what?
+    aqNoAlias           # request noalias
+  TOpcode = enum
+    ppEof = 1, # end of compiled pattern
+    ppOr,      # we could short-cut the evaluation for 'and' and 'or',
+    ppAnd,     # but currently we don't
+    ppNot, 
+    ppSym,
+    ppAtom,
+    ppLit,
+    ppIdent,
+    ppCall,
+    ppSymKind,
+    ppNodeKind,
+    ppSideEffect,
+    ppNoSideEffect
+  TPatternCode = string
+
+const
+  MaxStackSize* = 64 ## max required stack size by the VM
+
+proc patternError(n: PNode) = 
+  LocalError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
+
+proc add(code: var TPatternCode, op: TOpcode) {.inline.} =
+  add(code, chr(ord(op)))
+
+proc whichAlias*(p: PSym): TAliasRequest =
+  if p.typ.constraint != nil:
+    result = TAliasRequest(p.typ.constraint.strVal[0].ord)
+
+proc compileConstraints(p: PNode, result: var TPatternCode) =
+  case p.kind
+  of nkCallKinds:
+    if p.sons[0].kind != nkIdent: 
+      patternError(p.sons[0])
+      return
+    let op = p.sons[0].ident
+    if p.len == 3:
+      if op.s == "|" or op.id == ord(wOr):
+        compileConstraints(p.sons[1], result)
+        compileConstraints(p.sons[2], result)
+        result.add(ppOr)
+      elif op.s == "&" or op.id == ord(wAnd):
+        compileConstraints(p.sons[1], result)
+        compileConstraints(p.sons[2], result)
+        result.add(ppAnd)
+      else:
+        patternError(p)
+    elif p.len == 2 and (op.s == "~" or op.id == ord(wNot)):
+      compileConstraints(p.sons[1], result)
+      result.add(ppNot)
+    else:
+      patternError(p)
+  of nkAccQuoted, nkPar:
+    if p.len == 1:
+      compileConstraints(p.sons[0], result)
+    else:
+      patternError(p)
+  of nkIdent:
+    let spec = p.ident.s.normalize
+    case spec
+    of "atom":  result.add(ppAtom)
+    of "lit":   result.add(ppLit)
+    of "sym":   result.add(ppSym)
+    of "ident": result.add(ppIdent)
+    of "call":  result.add(ppCall)
+    of "alias": result[0] = chr(aqShouldAlias.ord)
+    of "noalias": result[0] = chr(aqNoAlias.ord)
+    of "sideeffect": result.add(ppSideEffect)
+    of "nosideeffect": result.add(ppNoSideEffect)
+    else:
+      # check all symkinds:
+      InternalAssert int(high(TSymKind)) < 255
+      for i in low(TSymKind)..high(TSymKind):
+        if cmpIgnoreStyle(($i).substr(2), spec) == 0:
+          result.add(ppSymKind)
+          result.add(chr(i.ord))
+          return
+      # check all nodekinds:
+      InternalAssert int(high(TNodeKind)) < 255
+      for i in low(TNodeKind)..high(TNodeKind):
+        if cmpIgnoreStyle($i, spec) == 0:
+          result.add(ppSymKind)
+          result.add(chr(i.ord))
+          return
+      patternError(p)
+  else:
+    patternError(p)
+
+proc semNodeKindConstraints*(p: PNode): PNode =
+  ## does semantic checking for a node kind pattern and compiles it into an
+  ## efficient internal format.
+  assert p.kind == nkCurlyExpr
+  result = newNodeI(nkStrLit, p.info)
+  result.strVal = newStringOfCap(10)
+  result.strVal.add(chr(aqNone.ord))
+  if p.len >= 2:
+    for i in 1.. <p.len:
+      compileConstraints(p.sons[i], result.strVal)
+    if result.strVal.len > maxStackSize-1:
+      InternalError(p.info, "parameter pattern too complex")
+  else:
+    patternError(p)
+  result.strVal.add(ppEof)
+
+type
+  TSideEffectAnalysis = enum
+    seUnknown, seSideEffect, seNoSideEffect
+
+proc checkForSideEffects(n: PNode): TSideEffectAnalysis =
+  # XXX is 'raise' a side effect?
+  case n.kind
+  of nkCallKinds:
+    # only calls can produce side effects:
+    let op = n.sons[0]
+    if op.kind == nkSym and isRoutine(op.sym):
+      let s = n.sym
+      if sfSideEffect in s.flags:
+        return seSideEffect
+      # assume no side effect:
+      result = seNoSideEffect
+    elif tfNoSideEffect in op.typ.flags:
+      # indirect call without side effects:
+      result = seNoSideEffect
+    else:
+      # indirect call: we don't know
+      result = seUnknown
+    # we need to check n[0] too: (FwithSideEffectButReturnsProcWithout)(args)
+    for i in 0 .. <n.len:
+      let ret = checkForSideEffects(n.sons[i])
+      if ret == seSideEffect: return ret
+      elif ret == seUnknown and result == seNoSideEffect:
+        result = seUnknown
+  of nkNone..nkNilLit:
+    # an atom cannot produce a side effect:
+    result = seNoSideEffect
+  else:
+    for i in 0 .. <n.len:
+      let ret = checkForSideEffects(n.sons[i])
+      if ret == seSideEffect: return ret
+      elif ret == seUnknown and result == seNoSideEffect:
+        result = seUnknown
+
+proc matchNodeKinds*(p, n: PNode): bool =
+  # matches the parameter constraint 'p' against the concrete AST 'n'. 
+  # Efficiency matters here.
+  var stack {.noinit.}: array[0..maxStackSize, bool]
+  # empty patterns are true:
+  stack[0] = true
+  var sp = 1
+  
+  template push(x: bool) =
+    stack[sp] = x
+    inc sp
+  
+  let code = p.strVal
+  var pc = 1
+  while true:
+    case TOpcode(code[pc])
+    of ppEof: break
+    of ppOr: 
+      stack[sp-2] = stack[sp-1] or stack[sp-2]
+      dec sp
+    of ppAnd:
+      stack[sp-2] = stack[sp-1] and stack[sp-2]
+      dec sp
+    of ppNot: stack[sp-1] = not stack[sp-1]
+    of ppSym: push n.kind == nkSym
+    of ppAtom: push isAtom(n)
+    of ppLit: push n.kind in {nkCharLit..nkNilLit}
+    of ppIdent: push n.kind == nkIdent
+    of ppCall: push n.kind in nkCallKinds
+    of ppSymKind:
+      let kind = TSymKind(code[pc+1])
+      push n.kind == nkSym and n.sym.kind == kind
+      inc pc
+    of ppNodeKind:
+      let kind = TNodeKind(code[pc+1])
+      push n.kind == kind
+      inc pc
+    of ppSideEffect: push checkForSideEffects(n) != seNoSideEffect
+    of ppNoSideEffect: push checkForSideEffects(n) == seNoSideEffect
+    inc pc
+  result = stack[sp-1]
diff --git a/compiler/patterns.nim b/compiler/patterns.nim
index 7109d9975..ceadfe350 100644
--- a/compiler/patterns.nim
+++ b/compiler/patterns.nim
@@ -10,13 +10,16 @@
 ## This module implements the pattern matching features for term rewriting
 ## macro support.
 
-import ast, astalgo, types, semdata, sigmatch, msgs, idents
+import
+  ast, astalgo, types, semdata, sigmatch, msgs, idents, aliases, parampatterns,
+  trees
 
 type
   TPatternContext = object
     owner: PSym
     mapping: TIdNodeTable  # maps formal parameters to nodes
     c: PContext
+    subMatch: bool         # subnode matches are special
   PPatternContext = var TPatternContext
 
 proc matches(c: PPatternContext, p, n: PNode): bool
@@ -53,38 +56,88 @@ proc sameTrees(a, b: PNode): bool =
         result = true
 
 proc inSymChoice(sc, x: PNode): bool =
-  if sc.kind in {nkOpenSymChoice, nkClosedSymChoice}:
+  if sc.kind == nkClosedSymChoice:
     for i in 0.. <sc.len:
       if sc.sons[i].sym == x.sym: return true
+  elif sc.kind == nkOpenSymChoice:
+    # same name suffices for open sym choices!
+    result = sc.sons[0].sym.name.id == x.sym.name.id
 
 proc checkTypes(c: PPatternContext, p: PSym, n: PNode): bool =
-  # XXX tyVarargs is special here; lots of other special cases
+  # check param constraints first here as this quite optimized:
+  if p.typ.constraint != nil:
+    result = matchNodeKinds(p.typ.constraint, n)
+    if not result: return
   if isNil(n.typ):
-    result = p.typ.kind == tyStmt
+    result = p.typ.kind in {tyEmpty, tyStmt}
   else:
     result = sigmatch.argtypeMatches(c.c, p.typ, n.typ)
 
+proc isPatternParam(c: PPatternContext, p: PNode): bool {.inline.} =
+  result = p.kind == nkSym and p.sym.kind == skParam and p.sym.owner == c.owner
+
+proc matchChoice(c: PPatternContext, p, n: PNode): bool =
+  for i in 1 .. <p.len:
+    if matches(c, p.sons[i], n): return true
+
+proc bindOrCheck(c: PPatternContext, param: PSym, n: PNode): bool =
+  var pp = IdNodeTableGetLazy(c.mapping, param)
+  if pp != nil:
+    # check if we got the same pattern (already unified):
+    result = sameTrees(pp, n) #matches(c, pp, n)
+  elif checkTypes(c, param, n) and 
+      (param.ast == nil or checkConstraints(c, param.ast, n)):
+    IdNodeTablePutLazy(c.mapping, param, n)
+    result = true
+
+proc matchStar(c: PPatternContext, p, n: PNode): bool =
+  # match ``op*param``
+  # this is quite hard: 
+  # match against: f(a, ..., f(b, c, f(...)))
+  # we have different semantics if there is a choice as left operand:
+
+  proc matchStarAux(c: PPatternContext, op, n, arglist: PNode) =
+    if n.kind in nkCallKinds and matches(c, op, n.sons[0]):
+      for i in 1..sonsLen(n)-1: matchStarAux(c, op, n.sons[i], arglist)
+    else:
+      add(arglist, n)
+
+  if n.kind notin nkCallKinds: return false
+  if p.sons[0].kind != nkPattern:
+    if matches(c, p.sons[0], n.sons[0]):
+      var arglist = newNodeI(nkArgList, n.info)
+      arglist.typ = p.sons[1].sym.typ
+      matchStarAux(c, p.sons[0], n, arglist)
+      result = bindOrCheck(c, p.sons[1].sym, arglist)
+  else:
+    # well it matches somehow ...
+    if matches(c, p.sons[0], n.sons[0]):
+      result = bindOrCheck(c, p.sons[1].sym, n)
+
 proc matches(c: PPatternContext, p, n: PNode): bool =
-  # XXX special treatment: statement list,
-  # ignore comments, nkPar, hidden conversions
-  # f(..X) ~> how can 'X' stand for all remaining parameters? -> introduce
-  # a new local node kind (alias of nkReturnToken or something)
-  if p.kind == nkSym and p.sym.kind == skParam and p.sym.owner == c.owner:
-    var pp = IdNodeTableGetLazy(c.mapping, p.sym)
-    if pp != nil:
-      # check if we got the same pattern (already unified):
-      result = matches(c, pp, n)
-    elif checkTypes(c, p.sym, n) and 
-        (p.sym.ast == nil or checkConstraints(c, p.sym.ast, n)):
-      IdNodeTablePutLazy(c.mapping, p.sym, n)
-      result = true
+  # hidden conversions (?)
+  if isPatternParam(c, p):
+    result = bindOrCheck(c, p.sym, n)
   elif n.kind == nkSym and inSymChoice(p, n):
     result = true
   elif n.kind == nkSym and n.sym.kind == skConst:
     # try both:
-    if sameTrees(p, n): result = true
-    elif matches(c, p, n.sym.ast):
-      result = true
+    if p.kind == nkSym: result = p.sym == n.sym
+    elif matches(c, p, n.sym.ast): result = true
+  elif p.kind == nkPattern:
+    # pattern operators: | *
+    let opr = p.sons[0].ident.s
+    case opr
+    of "|": result = matchChoice(c, p, n)
+    of "*": result = matchStar(c, p, n)
+    of "~": result = not matches(c, p.sons[1], n)
+    else: InternalError(p.info, "invalid pattern")
+    # template {add(a, `&` * b)}(a: string{noalias}, b: varargs[string]) = 
+    #   add(a, b)
+  elif p.kind == nkCurlyExpr:
+    assert isPatternParam(c, p.sons[1])
+    if matches(c, p.sons[0], n):
+      result = bindOrCheck(c, p.sons[1].sym, n)
   elif sameKinds(p, n):
     case p.kind
     of nkSym: result = p.sym == n.sym
@@ -92,21 +145,52 @@ proc matches(c: PPatternContext, p, n: PNode): bool =
     of nkCharLit..nkInt64Lit: result = p.intVal == n.intVal
     of nkFloatLit..nkFloat64Lit: result = p.floatVal == n.floatVal
     of nkStrLit..nkTripleStrLit: result = p.strVal == n.strVal
-    of nkEmpty, nkNilLit, nkType: 
+    of nkEmpty, nkNilLit, nkType:
       result = true
-      # of nkStmtList:
-      # both are statement lists; we need to ignore comment statements and
-      # 'nil' statements and check whether p <: n which is however trivially
-      # checked as 'applyRule' is checked after every created statement
-      # already; We need to ensure that the matching span is passed to the
-      # macro and NOT simply 'n'!
-      # XXX
     else:
-      if sonsLen(p) == sonsLen(n):
+      var plen = sonsLen(p)
+      # special rule for p(X) ~ f(...); this also works for stuff like
+      # partial case statements, etc! - Not really ... :-/
+      if plen <= sonsLen(n):
+        let v = lastSon(p)
+        if isPatternParam(c, v) and v.sym.typ.kind == tyVarargs:
+          for i in countup(0, plen - 2):
+            if not matches(c, p.sons[i], n.sons[i]): return
+          var arglist = newNodeI(nkArgList, n.info, sonsLen(n) - plen + 1)
+          # f(1, 2, 3)
+          # p(X)
+          for i in countup(0, sonsLen(n) - plen):
+            arglist.sons[i] = n.sons[i + plen - 1]
+          # check or bind 'X':
+          return bindOrCheck(c, v.sym, arglist)
+      if plen == sonsLen(n):
         for i in countup(0, sonsLen(p) - 1):
           if not matches(c, p.sons[i], n.sons[i]): return
         result = true
 
+proc matchStmtList(c: PPatternContext, p, n: PNode): PNode =
+  proc matchRange(c: PPatternContext, p, n: PNode, i: int): bool =
+    for j in 0 .. <p.len:
+      if not matches(c, p.sons[j], n.sons[i+j]):
+        # we need to undo any bindings:
+        if not isNil(c.mapping.data): reset(c.mapping)
+        return false
+    result = true
+  
+  if p.kind == nkStmtList and n.kind == p.kind and p.len < n.len:
+    let n = flattenStmts(n)
+    # no need to flatten 'p' here as that has already been done
+    for i in 0 .. n.len - p.len:
+      if matchRange(c, p, n, i):
+        c.subMatch = true
+        result = newNodeI(nkStmtList, n.info, 3)
+        result.sons[0] = extractRange(nkStmtList, n, 0, i-1)
+        result.sons[1] = extractRange(nkStmtList, n, i, i+p.len-1)
+        result.sons[2] = extractRange(nkStmtList, n, i+p.len, n.len-1)
+        break
+  elif matches(c, p, n):
+    result = n
+
 # writeln(X, a); writeln(X, b); --> writeln(X, a, b)
 
 proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
@@ -115,20 +199,48 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
   ctx.owner = s
   ctx.c = c
   # we perform 'initIdNodeTable' lazily for performance
-  if matches(ctx, s.ast.sons[patternPos], n):
-    # each parameter should have been bound; we simply setup a call and
-    # let semantic checking deal with the rest :-)
-    # this also saves type checking if we allow for type checking errors
-    # as in 'system.compiles' and simply discard the results. But an error
-    # may have been desired in the first place! Meh, it's good enough for
-    # a first implementation:
-    result = newNodeI(nkCall, n.info)
-    result.add(newSymNode(s, n.info))
-    let params = s.typ.n
+  var m = matchStmtList(ctx, s.ast.sons[patternPos], n)
+  if isNil(m): return nil
+  # each parameter should have been bound; we simply setup a call and
+  # let semantic checking deal with the rest :-)
+  result = newNodeI(nkCall, n.info)
+  result.add(newSymNode(s, n.info))
+  let params = s.typ.n
+  for i in 1 .. < params.len:
+    let param = params.sons[i].sym
+    let x = IdNodeTableGetLazy(ctx.mapping, param)
+    # couldn't bind parameter:
+    if isNil(x): return nil
+    result.add(x)
+  # perform alias analysis here:
+  if params.len >= 2:
     for i in 1 .. < params.len:
       let param = params.sons[i].sym
-      let x = IdNodeTableGetLazy(ctx.mapping, param)
-      # couldn't bind parameter:
-      if isNil(x): return nil
-      result.add(x)
-    markUsed(n, s)
+      case whichAlias(param)
+      of aqNone: nil
+      of aqShouldAlias:
+        # it suffices that it aliases for sure with *some* other param:
+        var ok = false
+        for j in 1 .. < result.len:
+          if j != i and result.sons[j].typ != nil:
+            if aliases.isPartOf(result[i], result[j]) == arYes:
+              ok = true
+              break
+        # constraint not fullfilled:
+        if not ok: return nil
+      of aqNoAlias:
+        # it MUST not alias with any other param:
+        var ok = true
+        for j in 1 .. < result.len:
+          if j != i and result.sons[j].typ != nil:
+            if aliases.isPartOf(result[i], result[j]) != arNo:
+              ok = false
+              break
+        # constraint not fullfilled:
+        if not ok: return nil
+
+  markUsed(n, s)
+  if ctx.subMatch:
+    assert m.len == 3
+    m.sons[1] = result
+    result = m
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index 88fae67cc..8a8e1e849 100755
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -367,7 +367,7 @@ proc lsub(n: PNode): int =
     else: result = len(atom(n))
   of succ(nkEmpty)..pred(nkTripleStrLit), succ(nkTripleStrLit)..nkNilLit: 
     result = len(atom(n))
-  of nkCall, nkBracketExpr, nkCurlyExpr, nkConv:
+  of nkCall, nkBracketExpr, nkCurlyExpr, nkConv, nkPattern:
     result = lsub(n.sons[0]) + lcomma(n, 1) + 2
   of nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv: result = lsub(n[1])
   of nkCast: result = lsub(n.sons[0]) + lsub(n.sons[1]) + len("cast[]()")
@@ -377,6 +377,7 @@ proc lsub(n: PNode): int =
   of nkCommand: result = lsub(n.sons[0]) + lcomma(n, 1) + 1
   of nkExprEqExpr, nkAsgn, nkFastAsgn: result = lsons(n) + 3
   of nkPar, nkCurly, nkBracket, nkClosure: result = lcomma(n) + 2
+  of nkArgList: result = lcomma(n)
   of nkTableConstr:
     result = if n.len > 0: lcomma(n) + 2 else: len("{:}")
   of nkClosedSymChoice, nkOpenSymChoice: 
@@ -666,7 +667,9 @@ proc gproc(g: var TSrcGen, n: PNode) =
     put(g, tkSymbol, renderDefinitionName(n.sons[namePos].sym))
   else:
     gsub(g, n.sons[namePos])
-  gsub(g, n.sons[patternPos])
+  
+  if n.sons[patternPos].kind != nkEmpty:
+    gpattern(g, n.sons[patternPos])
   gsub(g, n.sons[genericParamsPos])
   gsub(g, n.sons[paramsPos])
   gsub(g, n.sons[pragmasPos])
@@ -774,7 +777,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
   of nkRStrLit: put(g, tkRStrLit, atom(n))
   of nkCharLit: put(g, tkCharLit, atom(n))
   of nkNilLit: put(g, tkNil, atom(n))    # complex expressions
-  of nkCall, nkConv, nkDotCall: 
+  of nkCall, nkConv, nkDotCall, nkPattern:
     if sonsLen(n) >= 1: gsub(g, n.sons[0])
     put(g, tkParLe, "(")
     gcomma(g, n, 1)
@@ -859,6 +862,8 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     put(g, tkCurlyLe, "{")
     gcomma(g, n, c)
     put(g, tkCurlyRi, "}")
+  of nkArgList:
+    gcomma(g, n, c)
   of nkTableConstr:
     put(g, tkCurlyLe, "{")
     if n.len > 0: gcomma(g, n, c)
@@ -922,8 +927,9 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     gsub(g, n.sons[2])
   of nkPrefix: 
     gsub(g, n.sons[0])
-    put(g, tkSpaces, space)
-    gsub(g, n.sons[1])
+    if n.len > 1:
+      put(g, tkSpaces, space)
+      gsub(g, n.sons[1])
   of nkPostfix: 
     gsub(g, n.sons[1])
     gsub(g, n.sons[0])
@@ -1046,7 +1052,6 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
   of nkCaseStmt, nkRecCase: gcase(g, n)
   of nkMacroStmt: gmacro(g, n)
   of nkTryStmt: gtry(g, n)
-  of nkPattern: gpattern(g, n)
   of nkForStmt, nkParForStmt: gfor(g, n)
   of nkBlockStmt, nkBlockExpr: gblock(g, n)
   of nkStaticStmt: gstaticStmt(g, n)
diff --git a/compiler/rodread.nim b/compiler/rodread.nim
index 7c924511f..4461641db 100755
--- a/compiler/rodread.nim
+++ b/compiler/rodread.nim
@@ -330,6 +330,9 @@ proc decodeType(r: PRodReader, info: TLineInfo): PType =
   if r.s[r.pos] == '@': 
     inc(r.pos)
     result.containerID = decodeVInt(r.s, r.pos)
+  if r.s[r.pos] == '`':
+    inc(r.pos)
+    result.constraint = decodeNode(r, UnknownLineInfo())
   decodeLoc(r, result.loc, info)
   while r.s[r.pos] == '^': 
     inc(r.pos)
diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim
index 75e8a02a7..5be9a2439 100755
--- a/compiler/rodwrite.nim
+++ b/compiler/rodwrite.nim
@@ -233,6 +233,9 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) =
   if t.containerID != 0: 
     add(result, '@')
     encodeVInt(t.containerID, result)
+  if t.constraint != nil:
+    add(result, '`')
+    encodeNode(w, UnknownLineInfo(), t.constraint, result)
   encodeLoc(w, t.loc, result)
   for i in countup(0, sonsLen(t) - 1): 
     if t.sons[i] == nil: 
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 8052d8b00..24bee4a22 100755
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -15,7 +15,7 @@ import
   magicsys, parser, nversion, nimsets, semfold, importer,
   procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch,
   semthreads, intsets, transf, evals, idgen, aliases, cgmeth, lambdalifting,
-  evaltempl, patterns
+  evaltempl, patterns, parampatterns
 
 proc semPass*(): TPass
 # implementation
@@ -83,6 +83,7 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym, semCheck = true): PNode
 
 proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, 
                   semCheck: bool = true): PNode
+proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode
 
 proc semWhen(c: PContext, n: PNode, semCheck: bool = true): PNode
 
@@ -102,6 +103,26 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
     return n
   result = evalTypedExpr(c, e)
 
+proc evalPattern(c: PContext, n: PNode, info: TLineInfo): PNode =
+  InternalAssert n.kind == nkCall and n.sons[0].kind == nkSym
+  # we need to ensure that the resulting AST is semchecked. However, it's
+  # aweful to semcheck before macro invocation, so we don't and treat
+  # templates and macros as immediate in this context.
+  var rule: string
+  if optHints in gOptions and hintPattern in gNotes:
+    rule = renderTree(n, {renderNoComments})
+  let s = n.sons[0].sym
+  case s.kind
+  of skMacro:
+    result = semMacroExpr(c, n, n, s)
+  of skTemplate:
+    result = semTemplateExpr(c, n, s)
+  else:
+    result = semDirectOp(c, n, {})
+  if optHints in gOptions and hintPattern in gNotes:
+    Message(info, hintPattern, rule & " --> '" & 
+      renderTree(result, {renderNoComments}) & "'")
+
 proc applyPatterns(c: PContext, n: PNode): PNode =
   # fast exit:
   if c.patterns.len == 0 or optPatterns notin gOptions: return n
@@ -110,14 +131,25 @@ proc applyPatterns(c: PContext, n: PNode): PNode =
   # however the resulting AST would better not trigger the old rule then
   # anymore ;-)
   for i in countdown(<c.patterns.len, 0):
-    let x = applyRule(c, c.patterns[i], result)
-    if not isNil(x):
-      assert x.kind == nkCall
-      inc(evalTemplateCounter)
-      if evalTemplateCounter > 100:
-        GlobalError(n.info, errTemplateInstantiationTooNested)
-      result = semExpr(c, x)
-      dec(evalTemplateCounter)
+    let pattern = c.patterns[i]
+    if not isNil(pattern):
+      let x = applyRule(c, pattern, result)
+      if not isNil(x):
+        assert x.kind in {nkStmtList, nkCall}
+        inc(evalTemplateCounter)
+        if evalTemplateCounter > 100:
+          GlobalError(n.info, errTemplateInstantiationTooNested)
+        # deactivate this pattern:
+        c.patterns[i] = nil
+        if x.kind == nkStmtList:
+          assert x.len == 3
+          x.sons[1] = evalPattern(c, x.sons[1], n.info)
+          result = flattenStmts(x)
+        else:
+          result = evalPattern(c, x, n.info)
+        dec(evalTemplateCounter)
+        # activate this pattern again:
+        c.patterns[i] = pattern
 
 include seminst, semcall
 
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 1bf412a26..fe698bffb 100755
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -65,18 +65,17 @@ proc inlineConst(n: PNode, s: PSym): PNode {.inline.} =
   result.typ = s.typ
   result.info = n.info
 
+proc performProcvarCheck(c: PContext, n: PNode, s: PSym) =
+  # XXX this not correct; it's valid to pass to templates and macros.
+  # We really need another post nkCallConv check for this. Or maybe do it
+  # in transform().
+  var smoduleId = getModule(s).id
+  if sfProcVar notin s.flags and s.typ.callConv == ccDefault and
+      smoduleId != c.module.id and smoduleId != c.friendModule.id: 
+    LocalError(n.info, errXCannotBePassedToProcVar, s.name.s)
+  
 proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = 
   case s.kind
-  of skProc, skMethod, skIterator, skConverter: 
-    var smoduleId = getModule(s).id
-    if sfProcVar notin s.flags and s.typ.callConv == ccDefault and
-        smoduleId != c.module.id and smoduleId != c.friendModule.id: 
-      LocalError(n.info, errXCannotBePassedToProcVar, s.name.s)
-    result = symChoice(c, n, s, scClosed)
-    if result.kind == nkSym:
-      markIndirect(c, result.sym)
-      if isGenericRoutine(result.sym):
-        LocalError(n.info, errInstantiateXExplicitely, s.name.s)
   of skConst:
     markUsed(n, s)
     case skipTypes(s.typ, abstractInst).kind
@@ -105,7 +104,8 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
     # if a proc accesses a global variable, it is not side effect free:
     if sfGlobal in s.flags:
       incl(c.p.owner.flags, sfSideEffect)
-    elif s.kind == skParam and s.typ.kind == tyExpr:
+    elif s.kind == skParam and s.typ.kind == tyExpr and s.typ.n != nil:
+      # XXX see the hack in sigmatch.nim ...
       return s.typ.n
     result = newSymNode(s, n.info)
     # We cannot check for access to outer vars for example because it's still
@@ -1438,6 +1438,13 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     var s = lookUp(c, n)
     semCaptureSym(s, c.p.owner)
     result = semSym(c, n, s, flags)
+    if s.kind in {skProc, skMethod, skIterator, skConverter}:
+      performProcvarCheck(c, n, s)
+      result = symChoice(c, n, s, scClosed)
+      if result.kind == nkSym:
+        markIndirect(c, result.sym)
+        if isGenericRoutine(result.sym):
+          LocalError(n.info, errInstantiateXExplicitely, s.name.s)
   of nkSym:
     # because of the changed symbol binding, this does not mean that we
     # don't have to check the symbol for semantics here again!
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index 2d611aac5..c2958ca5e 100755
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -196,8 +196,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
       let x = b.intVal|+|1
       if (x and -x) == x and x >= 0:
         result = makeRange(a.typ, 0, b.intVal)
-  of mModI, mModI64, mModU:
-    # so ... if you ever wondered about modulo's signedness; this defines it:
+  of mModU:
     let a = n.sons[1]
     let b = n.sons[2]
     if b.kind in {nkIntLit..nkUInt64Lit}:
@@ -205,6 +204,15 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
         result = makeRange(a.typ, 0, b.intVal-1)
       else:
         result = makeRange(a.typ, b.intVal+1, 0)
+  of mModI, mModI64:
+    # so ... if you ever wondered about modulo's signedness; this defines it:
+    let a = n.sons[1]
+    let b = n.sons[2]
+    if b.kind in {nkIntLit..nkUInt64Lit}:
+      if b.intVal >= 0:
+        result = makeRange(a.typ, -(b.intVal-1), b.intVal-1)
+      else:
+        result = makeRange(a.typ, b.intVal+1, -(b.intVal+1))
   of mDivI, mDivI64, mDivU:
     binaryOp(`|div|`)
   of mMinI, mMinI64:
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 2a16c32e2..04766ae58 100755
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -933,6 +933,8 @@ proc semStaticStmt(c: PContext, n: PNode): PNode =
   result = evalStaticExpr(c.module, a)
   if result.isNil:
     LocalError(n.info, errCannotInterpretNodeX, renderTree(n))
+  elif result.kind == nkEmpty:
+    result = newNodeI(nkNilLit, n.info)
 
 # special marker values that indicates that we are
 # 1) AnalyzingDestructor: currenlty analyzing the type for destructor 
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)
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 8ec3c337c..7c1318ada 100755
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -826,9 +826,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
   of nkCurlyExpr:
     result = semTypeNode(c, n.sons[0], nil)
     if result != nil:
-      result = copyType(result, getCurrOwner(), false)
-      for i in countup(1, n.len - 1):
-        result.rawAddSon(semTypeNode(c, n.sons[i], nil))
+      result = copyType(result, getCurrOwner(), true)
+      result.constraint = semNodeKindConstraints(n)
   of nkWhenStmt:
     var whenResult = semWhen(c, n, false)
     if whenResult.kind == nkStmtList: whenResult.kind = nkStmtListType
@@ -844,6 +843,12 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
     of mOrdinal: result = semOrdinal(c, n, prev)
     of mSeq: result = semContainer(c, n, tySequence, "seq", prev)
     of mVarargs: result = semVarargs(c, n, prev)
+    of mExpr, mTypeDesc:
+      result = semTypeNode(c, n.sons[0], nil)
+      if result != nil:
+        result = copyType(result, getCurrOwner(), false)
+        for i in countup(1, n.len - 1):
+          result.rawAddSon(semTypeNode(c, n.sons[i], nil))
     else: result = semGeneric(c, n, s, prev)
   of nkIdent, nkDotExpr, nkAccQuoted: 
     var s = semTypeIdent(c, n)
diff --git a/compiler/trees.nim b/compiler/trees.nim
index e2c9b1b16..08b89a76e 100755
--- a/compiler/trees.nim
+++ b/compiler/trees.nim
@@ -151,3 +151,19 @@ proc whichPragma*(n: PNode): TSpecialWord =
   let key = if n.kind == nkExprColonExpr: n.sons[0] else: n
   if key.kind == nkIdent: result = whichKeyword(key.ident)
 
+proc unnestStmts(n, result: PNode) =
+  if n.kind == nkStmtList:
+    for x in items(n): unnestStmts(x, result)
+  elif n.kind notin {nkCommentStmt, nkNilLit}:
+    result.add(n)
+
+proc flattenStmts*(n: PNode): PNode =
+  ## flattens a nested statement list; used for pattern matching
+  result = newNodeI(nkStmtList, n.info)
+  unnestStmts(n, result)
+  if result.len == 1:
+    result = result.sons[0]
+
+proc extractRange*(k: TNodeKind, n: PNode, a, b: int): PNode =
+  result = newNodeI(k, n.info, b-a+1)
+  for i in 0 .. b-a: result.sons[i] = n.sons[i+a]
diff --git a/compiler/types.nim b/compiler/types.nim
index aca168ba8..8a01e6c76 100755
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -507,7 +507,8 @@ proc base(t: PType): PType =
 
 proc firstOrd(t: PType): biggestInt = 
   case t.kind
-  of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs: result = 0
+  of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyProxy:
+    result = 0
   of tySet, tyVar: result = firstOrd(t.sons[0])
   of tyArray, tyArrayConstr: result = firstOrd(t.sons[0])
   of tyRange: 
@@ -564,6 +565,7 @@ proc lastOrd(t: PType): biggestInt =
     result = t.n.sons[sonsLen(t.n) - 1].sym.position
   of tyGenericInst, tyDistinct, tyConst, tyMutable: 
     result = lastOrd(lastSon(t))
+  of tyProxy: result = 0
   else: 
     InternalError("invalid kind for last(" & $t.kind & ')')
     result = 0
@@ -591,7 +593,7 @@ type
                             # (few elements expected)
 
 proc initSameTypeClosure: TSameTypeClosure =
-  # we do the initialization lazy for performance (avoids memory allocations)
+  # we do the initialization lazily for performance (avoids memory allocations)
   nil
 
 proc containsOrIncl(c: var TSameTypeClosure, a, b: PType): bool =
@@ -613,7 +615,7 @@ proc SameTypeOrNil*(a, b: PType): bool =
     result = true
   else: 
     if a == nil or b == nil: result = false
-    else: 
+    else:
       var c = initSameTypeClosure()
       result = SameTypeAux(a, b, c)