summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2012-08-30 22:55:32 +0200
committerAraq <rumpf_a@web.de>2012-08-30 22:55:32 +0200
commit1d14cb1ad8d4d98ea19daa2d5ab6ded59d58553a (patch)
treec32929ff723358f440415d7610fd283f58f17a91
parent1786e3099141f6548e9f6cd4f4998d32f602b71d (diff)
downloadNim-1d14cb1ad8d4d98ea19daa2d5ab6ded59d58553a.tar.gz
next steps towards term rewriting macros; simple examples work
-rwxr-xr-xcompiler/ast.nim17
-rwxr-xr-xcompiler/commands.nim3
-rwxr-xr-xcompiler/options.nim6
-rwxr-xr-xcompiler/parser.nim29
-rw-r--r--compiler/patterns.nim16
-rwxr-xr-xcompiler/pragmas.nim6
-rwxr-xr-xcompiler/renderer.nim9
-rwxr-xr-xcompiler/sem.nim6
-rwxr-xr-xcompiler/semstmts.nim16
-rwxr-xr-xcompiler/semtempl.nim14
-rwxr-xr-xcompiler/wordrecg.nim4
-rwxr-xr-xdoc/advopt.txt1
-rwxr-xr-xlib/core/macros.nim2
-rw-r--r--lib/pure/unittest.nim4
-rw-r--r--tests/reject/tdisallowif.nim28
-rw-r--r--tests/run/tpatterns.nim17
-rwxr-xr-xtodo.txt2
17 files changed, 124 insertions, 56 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index dcf60af6b..52502c5de 100755
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -176,7 +176,7 @@ type
     nkFromStmt,           # a from * import statement
     nkIncludeStmt,        # an include statement
     nkBindStmt,           # a bind statement
-    nkPatternStmt,        # a pattern statement ('as' 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
@@ -687,13 +687,14 @@ const
     skMacro, skTemplate, skConverter, skEnumField, skLet, skStub}
   PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, nfAllConst}
   namePos* = 0
-  genericParamsPos* = 1
-  paramsPos* = 2
-  pragmasPos* = 3
-  patternPos* = 4    # empty except for term rewriting macros
-  bodyPos* = 5       # position of body; use rodread.getBody() instead!
-  resultPos* = 6
-  dispatcherPos* = 7 # caution: if method has no 'result' it can be position 5!
+  patternPos* = 1    # empty except for term rewriting macros
+  genericParamsPos* = 2
+  paramsPos* = 3
+  pragmasPos* = 4
+  exceptionPos* = 5  # will be used for exception tracking
+  bodyPos* = 6       # position of body; use rodread.getBody() instead!
+  resultPos* = 7
+  dispatcherPos* = 8 # caution: if method has no 'result' it can be position 5!
 
   nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix,
                   nkCommand, nkCallStrLit}
diff --git a/compiler/commands.nim b/compiler/commands.nim
index b7c64eb4b..b6ef02fe1 100755
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -184,6 +184,7 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool =
   of "taintmode": result = contains(gGlobalOptions, optTaintMode)
   of "tlsemulation": result = contains(gGlobalOptions, optTlsEmulation)
   of "implicitstatic": result = contains(gOptions, optImplicitStatic)
+  of "patterns": result = contains(gOptions, optPatterns)
   else: InvalidCmdLineOption(passCmd1, switch, info)
   
 proc processPath(path: string): string = 
@@ -315,6 +316,8 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) =
   of "taintmode": ProcessOnOffSwitchG({optTaintMode}, arg, pass, info)
   of "implicitstatic":
     ProcessOnOffSwitch({optImplicitStatic}, arg, pass, info)
+  of "patterns":
+    ProcessOnOffSwitch({optPatterns}, arg, pass, info)
   of "opt":
     expectArg(switch, arg, pass, info)
     case arg.normalize
diff --git a/compiler/options.nim b/compiler/options.nim
index de257aa15..c6b2053b1 100755
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -26,8 +26,9 @@ type                          # please make sure we have under 32 options
     optByRef,                 # use pass by ref for objects
                               # (for interfacing with C)
     optProfiler,              # profiler turned on
-    optImplicitStatic         # optimization: implicit at compile time
+    optImplicitStatic,        # optimization: implicit at compile time
                               # evaluation
+    optPatterns               # en/disable pattern matching
   TOptions* = set[TOption]
   TGlobalOption* = enum       # **keep binary compatible**
     gloptNone, optForceFullMake, optBoehmGC, optRefcGC, optDeadCodeElim, 
@@ -82,7 +83,8 @@ const
 var 
   gOptions*: TOptions = {optObjCheck, optFieldCheck, optRangeCheck, 
                          optBoundsCheck, optOverflowCheck, optAssert, optWarns, 
-                         optHints, optStackTrace, optLineTrace}
+                         optHints, optStackTrace, optLineTrace,
+                         optPatterns}
   gGlobalOptions*: TGlobalOptions = {optRefcGC, optThreadAnalysis}
   gExitcode*: int8
   searchPaths*: TLinkedList
diff --git a/compiler/parser.nim b/compiler/parser.nim
index 4623b6eed..daa6f9b7f 100755
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -700,10 +700,12 @@ proc parseDoBlock(p: var TParser): PNode =
   eat(p, tkColon)
   result = newNodeI(nkDo, info)
   addSon(result, ast.emptyNode)       # no name part
+  addSon(result, ast.emptyNode)       # no pattern part
   addSon(result, ast.emptyNode)       # no generic parameters
   addSon(result, params)
   addSon(result, pragmas)
   skipComment(p, result)
+  addSon(result, ast.emptyNode)       # no exception list
   addSon(result, parseStmt(p))
 
 proc parseDoBlocks(p: var TParser, call: PNode) =
@@ -720,14 +722,16 @@ proc parseProcExpr(p: var TParser, isExpr: bool): PNode =
   let hasSignature = p.tok.tokType in {tkParLe, tkColon}
   params = parseParamList(p)
   pragmas = optPragmas(p)
-  if (p.tok.tokType == tkEquals) and isExpr: 
+  if p.tok.tokType == tkEquals and isExpr: 
     result = newNodeI(nkLambda, info)
     addSon(result, ast.emptyNode)       # no name part
+    addSon(result, ast.emptyNode)       # no pattern
     addSon(result, ast.emptyNode)       # no generic parameters
     addSon(result, params)
     addSon(result, pragmas)
     getTok(p)
     skipComment(p, result)
+    addSon(result, ast.emptyNode)       # no exception list
     addSon(result, parseStmt(p))
   else: 
     result = newNodeI(nkProcTy, info)
@@ -961,23 +965,14 @@ proc parseYieldOrDiscard(p: var TParser, kind: TNodeKind): PNode =
   optInd(p, result)
   addSon(result, parseExpr(p))
 
-proc parseBreakOrContinue(p: var TParser, kind: TNodeKind): PNode = 
+proc parseBreakOrContinue(p: var TParser, kind: TNodeKind): PNode =
   result = newNodeP(kind, p)
   getTok(p)
   optInd(p, result)
   case p.tok.tokType
   of tkEof, tkSad, tkDed: addSon(result, ast.emptyNode)
   else: addSon(result, parseSymbol(p))
-  
-proc parseAs(p: var TParser): PNode =
-  result = newNodeP(nkPatternStmt, p)
-  getTok(p)                 # skip `as`
-  if p.tok.tokType == tkColon:
-    eat(p, tkColon)
-    addSon(result, parseStmt(p))
-  else:
-    addSon(result, parseExpr(p))
-  
+
 proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode =
   result = newNodeP(kind, p)
   while true:
@@ -1175,17 +1170,24 @@ proc parseGenericParamList(p: var TParser): PNode =
   optPar(p)
   eat(p, tkBracketRi)
 
+proc parsePattern(p: var TParser): PNode =
+  eat(p, tkCurlyLe)
+  result = parseStmt(p)
+  eat(p, tkCurlyRi)
+
 proc parseRoutine(p: var TParser, kind: TNodeKind): PNode = 
   result = newNodeP(kind, p)
   getTok(p)
   optInd(p, result)
   addSon(result, identVis(p))
+  if p.tok.tokType == tkCurlyLe: addSon(result, parsePattern(p))
+  else: addSon(result, ast.emptyNode)
   if p.tok.tokType == tkBracketLe: addSon(result, parseGenericParamList(p))
   else: addSon(result, ast.emptyNode)
   addSon(result, parseParamList(p))
   if p.tok.tokType == tkCurlyDotLe: addSon(result, parsePragma(p))
   else: addSon(result, ast.emptyNode)
-  # empty pattern:
+  # empty exception tracking:
   addSon(result, ast.emptyNode)
   if p.tok.tokType == tkEquals: 
     getTok(p)
@@ -1522,7 +1524,6 @@ proc complexOrSimpleStmt(p: var TParser): PNode =
   of tkWhen: result = parseIfOrWhen(p, nkWhenStmt)
   of tkVar: result = parseSection(p, nkVarSection, parseVariable)
   of tkBind: result = parseBind(p)
-  of tkAs: result = parseAs(p)
   else: result = simpleStmt(p)
   
 proc parseStmt(p: var TParser): PNode = 
diff --git a/compiler/patterns.nim b/compiler/patterns.nim
index 51bb1fb69..7109d9975 100644
--- a/compiler/patterns.nim
+++ b/compiler/patterns.nim
@@ -92,7 +92,15 @@ 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: result = true
+    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):
         for i in countup(0, sonsLen(p) - 1):
@@ -121,8 +129,6 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
       let param = params.sons[i].sym
       let x = IdNodeTableGetLazy(ctx.mapping, param)
       # couldn't bind parameter:
-      if isNil(x): 
-        echo "couldn't bind ", param.name.s
-        return nil
+      if isNil(x): return nil
       result.add(x)
-
+    markUsed(n, s)
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index 47abd0178..14bfedd95 100755
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -41,7 +41,7 @@ const
     wFatal, wDefine, wUndef, wCompile, wLink, wLinkSys, wPure, wPush, wPop,
     wBreakpoint, wWatchpoint, wPassL, wPassC, wDeadCodeElim, wDeprecated,
     wFloatChecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
-    wLinearScanEnd}
+    wLinearScanEnd, wPatterns}
   lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl, 
     wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader, 
     wDeprecated, wExtern, wThread, wImportcpp, wImportobjc, wNoStackFrame}
@@ -302,6 +302,7 @@ proc processOption(c: PContext, n: PNode) =
           excl(gOptions, optOptimizeSize)
         else: LocalError(n.info, errNoneSpeedOrSizeExpected)
     of wImplicitStatic: OnOff(c, n, {optImplicitStatic})
+    of wPatterns: OnOff(c, n, {optPatterns})
     else: LocalError(n.info, errOptionExpected)
   
 proc processPush(c: PContext, n: PNode, start: int) = 
@@ -643,7 +644,8 @@ proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
              wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, 
              wLinedir, wStacktrace, wLinetrace, wOptimization,
              wCallConv, 
-             wDebugger, wProfiler, wFloatChecks, wNanChecks, wInfChecks: 
+             wDebugger, wProfiler, wFloatChecks, wNanChecks, wInfChecks,
+             wPatterns:
             processOption(c, it) # calling conventions (boring...):
           of firstCallConv..lastCallConv: 
             assert(sym != nil)
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index 7ceabaa33..88fae67cc 100755
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -588,13 +588,13 @@ proc gwhile(g: var TSrcGen, n: PNode) =
 
 proc gpattern(g: var TSrcGen, n: PNode) = 
   var c: TContext
-  put(g, tkAs, "as")
-  putWithSpace(g, tkColon, ":")
+  put(g, tkCurlyLe, "{")
   initContext(c)
-  if longMode(n) or (lsub(n.sons[0]) + g.lineLen > maxLineLen): 
+  if longMode(n) or (lsub(n.sons[0]) + g.lineLen > maxLineLen):
     incl(c.flags, rfLongMode)
   gcoms(g)                    # a good place for comments
   gstmts(g, n.sons[0], c)
+  put(g, tkCurlyRi, "}")
 
 proc gpragmaBlock(g: var TSrcGen, n: PNode) = 
   var c: TContext
@@ -666,6 +666,7 @@ 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])
   gsub(g, n.sons[genericParamsPos])
   gsub(g, n.sons[paramsPos])
   gsub(g, n.sons[pragmasPos])
@@ -1045,7 +1046,7 @@ 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 nkPatternStmt: gpattern(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/sem.nim b/compiler/sem.nim
index 8d0e4e168..8052d8b00 100755
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -104,7 +104,7 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
 
 proc applyPatterns(c: PContext, n: PNode): PNode =
   # fast exit:
-  if c.patterns.len == 0: return n
+  if c.patterns.len == 0 or optPatterns notin gOptions: return n
   result = n
   # we apply the last pattern first, so that pattern overriding is possible;
   # however the resulting AST would better not trigger the old rule then
@@ -113,7 +113,11 @@ proc applyPatterns(c: PContext, n: PNode): PNode =
     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)
 
 include seminst, semcall
 
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 84d7b5b45..2a16c32e2 100755
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -770,6 +770,10 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
   else:
     s.typ = newTypeS(tyProc, c)
     rawAddSon(s.typ, nil)
+  if n.sons[patternPos].kind != nkEmpty:
+    n.sons[patternPos] = semPattern(c, n.sons[patternPos])
+    c.patterns.add(s)
+  
   var proto = SearchForProc(c, s, c.tab.tos-2) # -2 because we have a scope
                                                # open for parameters
   if proto == nil: 
@@ -1202,17 +1206,7 @@ proc SemStmt(c: PContext, n: PNode): PNode =
     result = semPragmaBlock(c, n)
   of nkStaticStmt:
     result = semStaticStmt(c, n)
-  of nkPatternStmt:
-    let pat = semPatternStmt(c, n)
-    let s = getCurrOwner()
-    if s.kind in routineKinds and s.ast.sons[patternPos].kind == nkEmpty:
-      s.ast.sons[patternPos] = pat
-      c.patterns.add(s)
-    else:
-      LocalError(n.info, errXNotAllowedHere, "'as'")
-    # replace by an empty statement:
-    result = newNodeI(nkNilLit, n.info)
-  else: 
+  else:
     # in interactive mode, we embed the expression in an 'echo':
     if gCmd == cmdInteractive:
       result = buildEchoStmt(c, semExpr(c, n))
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index ce5dfe3a2..f77dbf1a2 100755
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -172,10 +172,11 @@ proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
   else:
     n.sons[namePos] = semTemplBody(c, n.sons[namePos])
   openScope(c)
-  for i in genericParamsPos..bodyPos:
+  for i in patternPos..bodyPos:
     n.sons[i] = semTemplBody(c, n.sons[i])
   closeScope(c)
 
+proc semPattern(c: PContext, n: PNode): PNode
 proc semTemplBody(c: var TemplCtx, n: PNode): PNode = 
   result = n
   case n.kind
@@ -396,7 +397,10 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
     s.typ.n = newNodeI(nkFormalParams, n.info)
     rawAddSon(s.typ, newTypeS(tyStmt, c))
     addSon(s.typ.n, newNodeIT(nkType, n.info, s.typ.sons[0]))
-      
+  if n.sons[patternPos].kind != nkEmpty:
+    n.sons[patternPos] = semPattern(c, n.sons[patternPos])
+    c.patterns.add(s)
+
   var ctx: TemplCtx
   ctx.toBind = initIntSet()
   ctx.c = c
@@ -422,7 +426,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
   else:
     SymTabReplace(c.tab.stack[curScope], proto, s)
 
-proc semPatternStmt(c: PContext, n: PNode): PNode =
+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.
@@ -432,5 +436,7 @@ proc semPatternStmt(c: PContext, n: PNode): PNode =
   ctx.c = c
   ctx.owner = getCurrOwner()
   ctx.bodyKind = bkPattern
-  result = semTemplBody(ctx, n.sons[0])
+  result = semTemplBody(ctx, n)
+  if result.kind in {nkStmtList, nkStmtListExpr} and result.len == 1:
+    result = result.sons[0]
   closeScope(c.tab)
diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim
index a68db8eb2..8397ce5d2 100755
--- a/compiler/wordrecg.nim
+++ b/compiler/wordrecg.nim
@@ -51,7 +51,7 @@ type
     wFastcall, wClosure, wNoconv, wOn, wOff, wChecks, wRangechecks, 
     wBoundchecks, wOverflowchecks, wNilchecks,
     wFloatchecks, wNanChecks, wInfChecks,
-    wAssertions, wWarnings, 
+    wAssertions, wPatterns, wWarnings,
     wHints, wOptimization, wSpeed, wSize, wNone, 
     wDeadCodeElim, wSafecode, 
     wPragma,
@@ -131,7 +131,7 @@ const
     "overflowchecks", "nilchecks",
     "floatchecks", "nanchecks", "infchecks",
 
-    "assertions", "warnings", "hints", 
+    "assertions", "patterns", "warnings", "hints", 
     "optimization", "speed", "size", "none", 
     "deadcodeelim", "safecode", 
     "pragma",
diff --git a/doc/advopt.txt b/doc/advopt.txt
index 9a642addb..dba0a009a 100755
--- a/doc/advopt.txt
+++ b/doc/advopt.txt
@@ -55,6 +55,7 @@ Advanced options:
   --taintMode:on|off        turn taint mode on|off
   --symbolFiles:on|off      turn symbol files on|off (experimental)
   --implicitStatic:on|off   turn implicit compile time evaluation on|off
+  --patterns:on|off         turn pattern matching on|off
   --skipCfg                 do not read the general configuration file
   --skipUserCfg             do not read the user's configuration file
   --skipParentCfg           do not read the parent dirs' configuration files
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index 54d242238..272b9a45e 100755
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -46,7 +46,7 @@ type
     nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt, 

     nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkStaticStmt,

     nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkFromStmt, 

-    nnkIncludeStmt, nnkBindStmt, nnkPatternStmt,

+    nnkIncludeStmt, nnkBindStmt, nnkPattern,

     nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr, 

     nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy, 

     nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen, 

diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim
index 3a52cb916..b2cc54cdf 100644
--- a/lib/pure/unittest.nim
+++ b/lib/pure/unittest.nim
@@ -92,7 +92,9 @@ template fail* =
   TestStatusIMPL = FAILED

   checkpoints = @[]

 

-macro check*(conditions: stmt): stmt =

+macro check*(conditions: stmt): stmt =
+  let conditions = callsite()

+  
   proc standardRewrite(e: PNimrodNode): PNimrodNode =

     template rewrite(Exp, lineInfoLit: expr, expLit: string): stmt =

       if not Exp:

diff --git a/tests/reject/tdisallowif.nim b/tests/reject/tdisallowif.nim
new file mode 100644
index 000000000..002bc7491
--- /dev/null
+++ b/tests/reject/tdisallowif.nim
@@ -0,0 +1,28 @@
+discard """
+  line: 24
+  errormsg: "usage of 'disallowIf' is a user-defined error"
+"""
+
+template optZero{x+x}(x: int): int = x*3
+template andthen{x*3}(x: int): int = x*4
+template optSubstr1{x = substr(x, a, b)}(x: string, a, b: int) = setlen(x, b+1)
+
+template disallowIf{
+  if cond: action
+  else: action2
+}(cond: bool, action, action2: stmt) {.error.} = action
+
+var y = 12
+echo y+y
+
+var s: array[0..2, string]
+s[0] = "hello"
+s[0] = substr(s[0], 0, 2)
+
+echo s[0]
+
+if true:
+  echo "do it"
+  echo "more branches"
+else:
+  nil
diff --git a/tests/run/tpatterns.nim b/tests/run/tpatterns.nim
new file mode 100644
index 000000000..4a83d167c
--- /dev/null
+++ b/tests/run/tpatterns.nim
@@ -0,0 +1,17 @@
+discard """
+  output: '''48
+hel'''
+"""
+
+template optZero{x+x}(x: int): int = x*3
+template andthen{x*3}(x: int): int = x*4
+template optSubstr1{x = substr(x, a, b)}(x: string, a, b: int) = setlen(x, b+1)
+
+var y = 12
+echo y+y
+
+var s: array[0..2, string]
+s[0] = "hello"
+s[0] = substr(s[0], 0, 2)
+
+echo s[0]
diff --git a/todo.txt b/todo.txt
index a64a5b951..432b476b8 100755
--- a/todo.txt
+++ b/todo.txt
@@ -3,7 +3,7 @@ version 0.9.0
 
 - make 'm: stmt' use overloading resolution
 
-- implement the high level optimizer
+- improve pattern matching: introduce meta operators, statement list support
 - make 'bind' default for templates and introduce 'mixin'
 
 - implement "closure tuple consists of a single 'ref'" optimization