diff options
Diffstat (limited to 'compiler/patterns.nim')
-rw-r--r-- | compiler/patterns.nim | 192 |
1 files changed, 113 insertions, 79 deletions
diff --git a/compiler/patterns.nim b/compiler/patterns.nim index 2d2aeba76..32ec7fb53 100644 --- a/compiler/patterns.nim +++ b/compiler/patterns.nim @@ -11,8 +11,10 @@ ## macro support. import - ast, astalgo, types, semdata, sigmatch, msgs, idents, aliases, parampatterns, - trees + ast, types, semdata, sigmatch, idents, aliases, parampatterns, trees + +when defined(nimPreviewSlimSystem): + import std/assertions type TPatternContext = object @@ -21,31 +23,36 @@ type formals: int c: PContext subMatch: bool # subnode matches are special + mappingIsFull: bool PPatternContext = var TPatternContext proc getLazy(c: PPatternContext, sym: PSym): PNode = - if not isNil(c.mapping): + if c.mappingIsFull: result = c.mapping[sym.position] + else: + result = nil proc putLazy(c: PPatternContext, sym: PSym, n: PNode) = - if isNil(c.mapping): newSeq(c.mapping, c.formals) + if not c.mappingIsFull: + newSeq(c.mapping, c.formals) + c.mappingIsFull = true c.mapping[sym.position] = n proc matches(c: PPatternContext, p, n: PNode): bool proc canonKind(n: PNode): TNodeKind = - ## nodekind canonilization for pattern matching + ## nodekind canonicalization for pattern matching result = n.kind case result of nkCallKinds: result = nkCall of nkStrLit..nkTripleStrLit: result = nkStrLit - of nkFastAsgn: result = nkAsgn + of nkFastAsgn, nkSinkAsgn: result = nkAsgn else: discard proc sameKinds(a, b: PNode): bool {.inline.} = result = a.kind == b.kind or a.canonKind == b.canonKind -proc sameTrees(a, b: PNode): bool = +proc sameTrees*(a, b: PNode): bool = if sameKinds(a, b): case a.kind of nkSym: result = a.sym == b.sym @@ -56,18 +63,25 @@ proc sameTrees(a, b: PNode): bool = of nkEmpty, nkNilLit: result = true of nkType: result = sameTypeOrNil(a.typ, b.typ) else: - if sonsLen(a) == sonsLen(b): - for i in countup(0, sonsLen(a) - 1): - if not sameTrees(a.sons[i], b.sons[i]): return + if a.len == b.len: + for i in 0..<a.len: + if not sameTrees(a[i], b[i]): return result = true + else: + result = false + else: + result = false proc inSymChoice(sc, x: PNode): bool = if sc.kind == nkClosedSymChoice: + result = false for i in 0..<sc.len: - if sc.sons[i].sym == x.sym: return true + if sc[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 + result = sc[0].sym.name.id == x.sym.name.id + else: + result = false proc checkTypes(c: PPatternContext, p: PSym, n: PNode): bool = # check param constraints first here as this is quite optimized: @@ -75,7 +89,7 @@ proc checkTypes(c: PPatternContext, p: PSym, n: PNode): bool = result = matchNodeKinds(p.constraint, n) if not result: return if isNil(n.typ): - result = p.typ.kind in {tyVoid, tyStmt} + result = p.typ.kind in {tyVoid, tyTyped} else: result = sigmatch.argtypeMatches(c.c, p.typ, n.typ, fromHlo = true) @@ -83,8 +97,9 @@ 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 + result = false + for i in 1..<p.len: + if matches(c, p[i], n): return true proc bindOrCheck(c: PPatternContext, param: PSym, n: PNode): bool = var pp = getLazy(c, param) @@ -94,6 +109,8 @@ proc bindOrCheck(c: PPatternContext, param: PSym, n: PNode): bool = elif n.kind == nkArgList or checkTypes(c, param, n): putLazy(c, param, n) result = true + else: + result = false proc gather(c: PPatternContext, param: PSym, n: PNode) = var pp = getLazy(c, param) @@ -101,7 +118,7 @@ proc gather(c: PPatternContext, param: PSym, n: PNode) = pp.add(n) else: pp = newNodeI(nkArgList, n.info, 1) - pp.sons[0] = n + pp[0] = n putLazy(c, param, pp) proc matchNested(c: PPatternContext, p, n: PNode, rpn: bool): bool = @@ -109,24 +126,28 @@ proc matchNested(c: PPatternContext, p, n: PNode, rpn: bool): bool = proc matchStarAux(c: PPatternContext, op, n, arglist: PNode, rpn: bool): bool = result = true - if n.kind in nkCallKinds and matches(c, op.sons[1], n.sons[0]): - for i in 1..sonsLen(n)-1: + if n.kind in nkCallKinds and matches(c, op[1], n[0]): + for i in 1..<n.len: if not matchStarAux(c, op, n[i], arglist, rpn): return false - if rpn: arglist.add(n.sons[0]) - elif n.kind == nkHiddenStdConv and n.sons[1].kind == nkBracket: - let n = n.sons[1] + if rpn: arglist.add(n[0]) + elif n.kind == nkHiddenStdConv and n[1].kind == nkBracket: + let n = n[1] for i in 0..<n.len: if not matchStarAux(c, op, n[i], arglist, rpn): return false - elif checkTypes(c, p.sons[2].sym, n): - add(arglist, n) + elif checkTypes(c, p[2].sym, n): + arglist.add(n) else: result = false if n.kind notin nkCallKinds: return false - if matches(c, p.sons[1], n.sons[0]): + if matches(c, p[1], n[0]): var arglist = newNodeI(nkArgList, n.info) if matchStarAux(c, p, n, arglist, rpn): - result = bindOrCheck(c, p.sons[2].sym, arglist) + result = bindOrCheck(c, p[2].sym, arglist) + else: + result = false + else: + result = false proc matches(c: PPatternContext, p, n: PNode): bool = let n = skipHidden(n) @@ -141,27 +162,34 @@ proc matches(c: PPatternContext, p, n: PNode): bool = elif n.kind == nkSym and n.sym.kind == skConst: # try both: if p.kind == nkSym: result = p.sym == n.sym - elif matches(c, p, n.sym.ast): result = true + elif matches(c, p, n.sym.astdef): result = true + else: result = false elif p.kind == nkPattern: # pattern operators: | * - let opr = p.sons[0].ident.s + let opr = p[0].ident.s case opr of "|": result = matchChoice(c, p, n) of "*": result = matchNested(c, p, n, rpn=false) of "**": result = matchNested(c, p, n, rpn=true) - of "~": result = not matches(c, p.sons[1], n) - else: doAssert(false, "invalid pattern") + of "~": result = not matches(c, p[1], n) + else: + result = false + doAssert(false, "invalid pattern") # template {add(a, `&` * b)}(a: string{noalias}, b: varargs[string]) = - # add(a, b) + # a.add(b) elif p.kind == nkCurlyExpr: - if p.sons[1].kind == nkPrefix: - if matches(c, p.sons[0], n): - gather(c, p.sons[1].sons[1].sym, n) + if p[1].kind == nkPrefix: + if matches(c, p[0], n): + gather(c, p[1][1].sym, n) result = true + else: + result = false else: - assert isPatternParam(c, p.sons[1]) - if matches(c, p.sons[0], n): - result = bindOrCheck(c, p.sons[1].sym, n) + assert isPatternParam(c, p[1]) + if matches(c, p[0], n): + result = bindOrCheck(c, p[1].sym, n) + else: + result = false elif sameKinds(p, n): case p.kind of nkSym: result = p.sym == n.sym @@ -172,80 +200,84 @@ proc matches(c: PPatternContext, p, n: PNode): bool = of nkEmpty, nkNilLit, nkType: result = true else: - var plen = sonsLen(p) # special rule for p(X) ~ f(...); this also works for stuff like # partial case statements, etc! - Not really ... :-/ + result = false 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: + if p.len <= n.len: + for i in 0..<p.len - 1: + if not matches(c, p[i], n[i]): return + if p.len == n.len and lastSon(n).kind == nkHiddenStdConv and + lastSon(n)[1].kind == nkBracket: # unpack varargs: - let n = lastSon(n).sons[1] + let n = lastSon(n)[1] arglist = newNodeI(nkArgList, n.info, n.len) - for i in 0..<n.len: arglist.sons[i] = n.sons[i] + for i in 0..<n.len: arglist[i] = n[i] else: - arglist = newNodeI(nkArgList, n.info, sonsLen(n) - plen + 1) + arglist = newNodeI(nkArgList, n.info, n.len - p.len + 1) # f(1, 2, 3) # p(X) - for i in countup(0, sonsLen(n) - plen): - arglist.sons[i] = n.sons[i + plen - 1] + for i in 0..n.len - p.len: + arglist[i] = n[i + p.len - 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 + elif p.len-1 == n.len: + for i in 0..<p.len - 1: + if not matches(c, p[i], n[i]): return arglist = newNodeI(nkArgList, n.info) 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 + if p.len == n.len: + for i in 0..<p.len: + if not matches(c, p[i], n[i]): return result = true + else: + result = false 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]): + for j in 0..<p.len: + if not matches(c, p[j], n[i+j]): # we need to undo any bindings: - if not isNil(c.mapping): c.mapping = nil + c.mapping = @[] + c.mappingIsFull = false return false result = true if p.kind == nkStmtList and n.kind == p.kind and p.len < n.len: + result = nil let n = flattenStmts(n) # no need to flatten 'p' here as that has already been done - for i in 0 .. n.len - p.len: + 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) + result[0] = extractRange(nkStmtList, n, 0, i-1) + result[1] = extractRange(nkStmtList, n, i, i+p.len-1) + result[2] = extractRange(nkStmtList, n, i+p.len, n.len-1) break elif matches(c, p, n): result = n + else: + result = nil proc aliasAnalysisRequested(params: PNode): bool = + result = false if params.len >= 2: - for i in 1 ..< params.len: - let param = params.sons[i].sym + for i in 1..<params.len: + let param = params[i].sym if whichAlias(param) != aqNone: return true proc addToArgList(result, n: PNode) = - if n.typ != nil and n.typ.kind != tyStmt: + if n.typ != nil and n.typ.kind != tyTyped: if n.kind != nkArgList: result.add(n) else: - for i in 0 ..< n.len: result.add(n.sons[i]) + for i in 0..<n.len: result.add(n[i]) proc applyRule*(c: PContext, s: PSym, n: PNode): PNode = ## returns a tree to semcheck if the rule triggered; nil otherwise - var ctx: TPatternContext - ctx.owner = s - ctx.c = c - ctx.formals = sonsLen(s.typ)-1 - var m = matchStmtList(ctx, s.ast.sons[patternPos], n) + var ctx = TPatternContext(owner: s, c: c, formals: s.typ.paramsLen) + var m = matchStmtList(ctx, s.ast[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 :-) @@ -253,11 +285,13 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode = result.add(newSymNode(s, n.info)) let params = s.typ.n let requiresAA = aliasAnalysisRequested(params) - var args: PNode - if requiresAA: - args = newNodeI(nkArgList, n.info) - for i in 1 ..< params.len: - let param = params.sons[i].sym + var args: PNode = + if requiresAA: + newNodeI(nkArgList, n.info) + else: + nil + for i in 1..<params.len: + let param = params[i].sym let x = getLazy(ctx, param) # couldn't bind parameter: if isNil(x): return nil @@ -265,9 +299,9 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode = if requiresAA: addToArgList(args, x) # perform alias analysis here: if requiresAA: - for i in 1 ..< params.len: - var rs = result.sons[i] - let param = params.sons[i].sym + for i in 1..<params.len: + var rs = result[i] + let param = params[i].sym case whichAlias(param) of aqNone: discard of aqShouldAlias: @@ -289,8 +323,8 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode = # constraint not fulfilled: if not ok: return nil - markUsed(c.config, n.info, s, c.graph.usageSym) + markUsed(c, n.info, s) if ctx.subMatch: assert m.len == 3 - m.sons[1] = result + m[1] = result result = m |