# # # The Nim 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 ast, astalgo, types, semdata, sigmatch, msgs, idents, aliases, parampatterns, trees type TPatternContext = object owner: PSym mapping: seq[PNode] # maps formal parameters to nodes formals: int c: PContext subMatch: bool # subnode matches are special PPatternContext = var TPatternContext proc getLazy(c: PPatternContext, sym: PSym): PNode = if not isNil(c.mapping): result = c.mapping[sym.position] proc putLazy(c: PPatternContext, sym: PSym, n: PNode) = if isNil(c.mapping): newSeq(c.mapping, c.formals) c.mapping[sym.position] = n proc matches(c: PPatternContext, p, n: PNode): bool proc canonKind(n: PNode): TNodeKind = ## nodekind canonilization for pattern matching result = n.kind case result of nkCallKinds: result = nkCall of nkStrLit..nkTripleStrLit: result = nkStrLit of nkFastAsgn: 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 = if sameKinds(a, b): case a.kind of nkSym: result = a.sym == b.sym of nkIdent: result = a.ident.id == b.ident.id of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal 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 result = true proc inSymChoice(sc, x: PNode): bool = if sc.kind == nkClosedSymChoice: for i in 0..= 2: for i in 1 ..< params.len: let param = params.sons[i].sym if whichAlias(param) != aqNone: return true proc addToArgList(result, n: PNode) = if n.typ != nil and n.typ.kind != tyStmt: if n.kind != nkArgList: result.add(n) else: for i in 0 ..< n.len: result.add(n.sons[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) 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 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 let x = getLazy(ctx, param) # couldn't bind parameter: if isNil(x): return nil result.add(x) 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 case whichAlias(param) of aqNone: discard of aqShouldAlias: # it suffices that it aliases for sure with *some* other param: var ok = false for arg in items(args): if arg != rs and aliases.isPartOf(rs, arg) == arYes: ok = true break # constraint not fulfilled: if not ok: return nil of aqNoAlias: # it MUST not alias with any other param: var ok = true for arg in items(args): if arg != rs and aliases.isPartOf(rs, arg) != arNo: ok = false break # constraint not fulfilled: if not ok: return nil markUsed(n.info, s, c.graph.usageSym) if ctx.subMatch: assert m.len == 3 m.sons[1] = result result = m