diff options
-rw-r--r-- | compiler/parampatterns.nim | 9 | ||||
-rw-r--r-- | compiler/patterns.nim | 40 | ||||
-rwxr-xr-x | compiler/semtempl.nim | 10 | ||||
-rw-r--r-- | tests/patterns/tcse.nim | 13 | ||||
-rw-r--r-- | tests/patterns/tnoalias.nim | 16 | ||||
-rw-r--r-- | tests/patterns/tstar.nim | 2 | ||||
-rw-r--r-- | tests/patterns/tstmtlist.nim | 13 | ||||
-rw-r--r-- | tests/specials.nim | 4 |
8 files changed, 78 insertions, 29 deletions
diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index 83585dbd4..44e41f7a0 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -101,7 +101,7 @@ proc compileConstraints(p: PNode, result: var TPatternCode) = InternalAssert int(high(TNodeKind)) < 255 for i in low(TNodeKind)..high(TNodeKind): if cmpIgnoreStyle($i, spec) == 0: - result.add(ppSymKind) + result.add(ppNodeKind) result.add(chr(i.ord)) return patternError(p) @@ -135,7 +135,7 @@ proc checkForSideEffects(n: PNode): TSideEffectAnalysis = # only calls can produce side effects: let op = n.sons[0] if op.kind == nkSym and isRoutine(op.sym): - let s = n.sym + let s = op.sym if sfSideEffect in s.flags: return seSideEffect # assume no side effect: @@ -199,7 +199,8 @@ proc matchNodeKinds*(p, n: PNode): bool = let kind = TNodeKind(code[pc+1]) push n.kind == kind inc pc - of ppSideEffect: push checkForSideEffects(n) != seNoSideEffect - of ppNoSideEffect: push checkForSideEffects(n) == seNoSideEffect + of ppSideEffect: push checkForSideEffects(n) == seSideEffect + of ppNoSideEffect: push checkForSideEffects(n) != seSideEffect inc pc result = stack[sp-1] + diff --git a/compiler/patterns.nim b/compiler/patterns.nim index 110fae08a..402283b76 100644 --- a/compiler/patterns.nim +++ b/compiler/patterns.nim @@ -59,9 +59,9 @@ proc inSymChoice(sc, x: PNode): bool = 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 = - # check param constraints first here as this quite optimized: + # check param constraints first here as this is quite optimized: if p.typ.constraint != nil: result = matchNodeKinds(p.typ.constraint, n) if not result: return @@ -86,7 +86,7 @@ proc bindOrCheck(c: PPatternContext, param: PSym, n: PNode): bool = IdNodeTablePutLazy(c.mapping, param, n) result = true -proc matchStar(c: PPatternContext, p, n: PNode): bool = +proc matchNested(c: PPatternContext, p, n: PNode): bool = # match ``op*param`` proc matchStarAux(c: PPatternContext, op, n, arglist: PNode) = @@ -109,6 +109,8 @@ proc matches(c: PPatternContext, p, n: PNode): bool = # hidden conversions (?) if isPatternParam(c, p): result = bindOrCheck(c, p.sym, n) + elif n.kind == nkSym and p.kind == nkIdent: + result = p.ident.id == n.sym.name.id elif n.kind == nkSym and inSymChoice(p, n): result = true elif n.kind == nkSym and n.sym.kind == skConst: @@ -120,7 +122,7 @@ proc matches(c: PPatternContext, p, n: PNode): bool = let opr = p.sons[0].ident.s case opr of "|": result = matchChoice(c, p, n) - of "*": result = matchStar(c, p, n) + of "*": result = matchNested(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]) = @@ -142,17 +144,29 @@ proc matches(c: PPatternContext, p, n: PNode): bool = 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: + let v = lastSon(p) + if isPatternParam(c, v) and v.sym.typ.kind == tyVarargs: + var arglist: PNode + if plen <= sonsLen(n): + for i in countup(0, plen - 2): + if not matches(c, p.sons[i], n.sons[i]): return + if plen == sonsLen(n) and lastSon(n).kind == nkHiddenStdConv and + lastSon(n).sons[1].kind == nkBracket: + # unpack varargs: + let n = lastSon(n).sons[1] + arglist = newNodeI(nkArgList, n.info, n.len) + for i in 0.. <n.len: arglist.sons[i] = n.sons[i] + else: + 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] + return bindOrCheck(c, v.sym, arglist) + elif plen-1 == sonsLen(n): 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': + arglist = newNodeI(nkArgList, n.info) return bindOrCheck(c, v.sym, arglist) if plen == sonsLen(n): for i in countup(0, sonsLen(p) - 1): diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index efdfce78f..0887e1789 100755 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -423,6 +423,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode = s.kind == skTemplate and (s.typ.len == 1 or sfImmediate in s.flags) proc handleSym(c: var TemplCtx, n: PNode, s: PSym): PNode = + result = n if s != nil: if s.owner == c.owner and s.kind == skParam: incl(s.flags, sfUsed) @@ -432,9 +433,9 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode = elif templToExpand(s): result = semPatternBody(c, semTemplateExpr(c.c, n, s, false)) else: - result = symChoice(c.c, n, s, scOpen) - else: - result = n + nil + # we keep the ident unbound for matching instantiated symbols and + # more flexibility proc expectParam(c: var TemplCtx, n: PNode): PNode = let s = QualifiedLookUp(c.c, n, {}) @@ -504,7 +505,8 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode = if s != nil: if Contains(c.toBind, s.id): return symChoice(c.c, n, s, scClosed) - return symChoice(c.c, n, s, scOpen) + else: + return newIdentNode(s.name, n.info) of nkPar: if n.len == 1: return semPatternBody(c, n.sons[0]) else: nil diff --git a/tests/patterns/tcse.nim b/tests/patterns/tcse.nim new file mode 100644 index 000000000..ff04f7d83 --- /dev/null +++ b/tests/patterns/tcse.nim @@ -0,0 +1,13 @@ +discard """ + output: "4" +""" + +template cse{f(a, a, x)}(a: expr{(nkDotExpr|call|nkBracketExpr)&noSideEffect}, + f: expr, x: varargs[expr]): expr = + let aa = a + f(aa, aa, x)+4 + +var + a: array[0..10, int] + i = 3 +echo a[i] + a[i] diff --git a/tests/patterns/tnoalias.nim b/tests/patterns/tnoalias.nim new file mode 100644 index 000000000..1d5671362 --- /dev/null +++ b/tests/patterns/tnoalias.nim @@ -0,0 +1,16 @@ +discard """ + output: "23" +""" + +template optslice{a = b + c}(a: expr{noalias}, b, c: expr): stmt = + a = b + inc a, c + +var + x = 12 + y = 10 + z = 13 + +x = y+z + +echo x diff --git a/tests/patterns/tstar.nim b/tests/patterns/tstar.nim index 6dbff3cd6..55d841c3a 100644 --- a/tests/patterns/tstar.nim +++ b/tests/patterns/tstar.nim @@ -15,5 +15,5 @@ template optConc{ `&&` * a }(a: expr): expr = &&a let space = " " echo "my" && (space & "awe" && "some " ) && "concat" -# check that it's been properly optimized: +# check that it's been optimized properly: doAssert calls == 1 diff --git a/tests/patterns/tstmtlist.nim b/tests/patterns/tstmtlist.nim index 391c93d47..20cb5d688 100644 --- a/tests/patterns/tstmtlist.nim +++ b/tests/patterns/tstmtlist.nim @@ -1,18 +1,19 @@ discard """ output: '''0 -|12|34 +|12| +34 ''' """ template optWrite{ - write(stdout, x) - write(stdout, y) -}(x, y: string) = - write(stdout, "|", x, y, "|") + write(f, x) + ((write|writeln){w})(f, y) +}(x, y: varargs[expr], f, w: expr) = + w(f, "|", x, y, "|") if true: echo "0" write stdout, "1" - write stdout, "2" + writeln stdout, "2" write stdout, "3" echo "4" diff --git a/tests/specials.nim b/tests/specials.nim index bc5b801eb..698c39a2c 100644 --- a/tests/specials.nim +++ b/tests/specials.nim @@ -124,7 +124,6 @@ proc runGcTests(r: var TResults, options: string) = test "gcleak" test "gcleak2" test "gctest" - # disabled for now as it somehow runs very slowly ('delete' bug?) but works: test "gcleak3" test "weakrefs" @@ -188,6 +187,9 @@ proc runSpecialTests(r: var TResults, options: string) = runThreadTests(r, options & " --threads:on") runIOTests(r, options) + for t in os.walkFiles("tests/patterns/t*.nim"): + runSingleTest(r, t, options) + proc rejectSpecialTests(r: var TResults, options: string) = rejectThreadTests(r, options) |