patterns.nim 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2012 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## This module implements the pattern matching features for term rewriting
  10. ## macro support.
  11. import
  12. ast, astalgo, types, semdata, sigmatch, msgs, idents, aliases, parampatterns,
  13. trees
  14. type
  15. TPatternContext = object
  16. owner: PSym
  17. mapping: seq[PNode] # maps formal parameters to nodes
  18. formals: int
  19. c: PContext
  20. subMatch: bool # subnode matches are special
  21. PPatternContext = var TPatternContext
  22. proc getLazy(c: PPatternContext, sym: PSym): PNode =
  23. if not isNil(c.mapping):
  24. result = c.mapping[sym.position]
  25. proc putLazy(c: PPatternContext, sym: PSym, n: PNode) =
  26. if isNil(c.mapping): newSeq(c.mapping, c.formals)
  27. c.mapping[sym.position] = n
  28. proc matches(c: PPatternContext, p, n: PNode): bool
  29. proc canonKind(n: PNode): TNodeKind =
  30. ## nodekind canonilization for pattern matching
  31. result = n.kind
  32. case result
  33. of nkCallKinds: result = nkCall
  34. of nkStrLit..nkTripleStrLit: result = nkStrLit
  35. of nkFastAsgn: result = nkAsgn
  36. else: discard
  37. proc sameKinds(a, b: PNode): bool {.inline.} =
  38. result = a.kind == b.kind or a.canonKind == b.canonKind
  39. proc sameTrees(a, b: PNode): bool =
  40. if sameKinds(a, b):
  41. case a.kind
  42. of nkSym: result = a.sym == b.sym
  43. of nkIdent: result = a.ident.id == b.ident.id
  44. of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal
  45. of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
  46. of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
  47. of nkEmpty, nkNilLit: result = true
  48. of nkType: result = sameTypeOrNil(a.typ, b.typ)
  49. else:
  50. if sonsLen(a) == sonsLen(b):
  51. for i in countup(0, sonsLen(a) - 1):
  52. if not sameTrees(a.sons[i], b.sons[i]): return
  53. result = true
  54. proc inSymChoice(sc, x: PNode): bool =
  55. if sc.kind == nkClosedSymChoice:
  56. for i in 0.. <sc.len:
  57. if sc.sons[i].sym == x.sym: return true
  58. elif sc.kind == nkOpenSymChoice:
  59. # same name suffices for open sym choices!
  60. result = sc.sons[0].sym.name.id == x.sym.name.id
  61. proc checkTypes(c: PPatternContext, p: PSym, n: PNode): bool =
  62. # check param constraints first here as this is quite optimized:
  63. if p.constraint != nil:
  64. result = matchNodeKinds(p.constraint, n)
  65. if not result: return
  66. if isNil(n.typ):
  67. result = p.typ.kind in {tyVoid, tyStmt}
  68. else:
  69. result = sigmatch.argtypeMatches(c.c, p.typ, n.typ)
  70. proc isPatternParam(c: PPatternContext, p: PNode): bool {.inline.} =
  71. result = p.kind == nkSym and p.sym.kind == skParam and p.sym.owner == c.owner
  72. proc matchChoice(c: PPatternContext, p, n: PNode): bool =
  73. for i in 1 .. <p.len:
  74. if matches(c, p.sons[i], n): return true
  75. proc bindOrCheck(c: PPatternContext, param: PSym, n: PNode): bool =
  76. var pp = getLazy(c, param)
  77. if pp != nil:
  78. # check if we got the same pattern (already unified):
  79. result = sameTrees(pp, n) #matches(c, pp, n)
  80. elif n.kind == nkArgList or checkTypes(c, param, n):
  81. putLazy(c, param, n)
  82. result = true
  83. proc gather(c: PPatternContext, param: PSym, n: PNode) =
  84. var pp = getLazy(c, param)
  85. if pp != nil and pp.kind == nkArgList:
  86. pp.add(n)
  87. else:
  88. pp = newNodeI(nkArgList, n.info, 1)
  89. pp.sons[0] = n
  90. putLazy(c, param, pp)
  91. proc matchNested(c: PPatternContext, p, n: PNode, rpn: bool): bool =
  92. # match ``op * param`` or ``op *| param``
  93. proc matchStarAux(c: PPatternContext, op, n, arglist: PNode,
  94. rpn: bool): bool =
  95. result = true
  96. if n.kind in nkCallKinds and matches(c, op.sons[1], n.sons[0]):
  97. for i in 1..sonsLen(n)-1:
  98. if not matchStarAux(c, op, n[i], arglist, rpn): return false
  99. if rpn: arglist.add(n.sons[0])
  100. elif n.kind == nkHiddenStdConv and n.sons[1].kind == nkBracket:
  101. let n = n.sons[1]
  102. for i in 0.. <n.len:
  103. if not matchStarAux(c, op, n[i], arglist, rpn): return false
  104. elif checkTypes(c, p.sons[2].sym, n):
  105. add(arglist, n)
  106. else:
  107. result = false
  108. if n.kind notin nkCallKinds: return false
  109. if matches(c, p.sons[1], n.sons[0]):
  110. var arglist = newNodeI(nkArgList, n.info)
  111. if matchStarAux(c, p, n, arglist, rpn):
  112. result = bindOrCheck(c, p.sons[2].sym, arglist)
  113. proc matches(c: PPatternContext, p, n: PNode): bool =
  114. let n = skipHidden(n)
  115. if nfNoRewrite in n.flags:
  116. result = false
  117. elif isPatternParam(c, p):
  118. result = bindOrCheck(c, p.sym, n)
  119. elif n.kind == nkSym and p.kind == nkIdent:
  120. result = p.ident.id == n.sym.name.id
  121. elif n.kind == nkSym and inSymChoice(p, n):
  122. result = true
  123. elif n.kind == nkSym and n.sym.kind == skConst:
  124. # try both:
  125. if p.kind == nkSym: result = p.sym == n.sym
  126. elif matches(c, p, n.sym.ast): result = true
  127. elif p.kind == nkPattern:
  128. # pattern operators: | *
  129. let opr = p.sons[0].ident.s
  130. case opr
  131. of "|": result = matchChoice(c, p, n)
  132. of "*": result = matchNested(c, p, n, rpn=false)
  133. of "**": result = matchNested(c, p, n, rpn=true)
  134. of "~": result = not matches(c, p.sons[1], n)
  135. else: internalError(p.info, "invalid pattern")
  136. # template {add(a, `&` * b)}(a: string{noalias}, b: varargs[string]) =
  137. # add(a, b)
  138. elif p.kind == nkCurlyExpr:
  139. if p.sons[1].kind == nkPrefix:
  140. if matches(c, p.sons[0], n):
  141. gather(c, p.sons[1].sons[1].sym, n)
  142. result = true
  143. else:
  144. assert isPatternParam(c, p.sons[1])
  145. if matches(c, p.sons[0], n):
  146. result = bindOrCheck(c, p.sons[1].sym, n)
  147. elif sameKinds(p, n):
  148. case p.kind
  149. of nkSym: result = p.sym == n.sym
  150. of nkIdent: result = p.ident.id == n.ident.id
  151. of nkCharLit..nkInt64Lit: result = p.intVal == n.intVal
  152. of nkFloatLit..nkFloat64Lit: result = p.floatVal == n.floatVal
  153. of nkStrLit..nkTripleStrLit: result = p.strVal == n.strVal
  154. of nkEmpty, nkNilLit, nkType:
  155. result = true
  156. else:
  157. var plen = sonsLen(p)
  158. # special rule for p(X) ~ f(...); this also works for stuff like
  159. # partial case statements, etc! - Not really ... :-/
  160. let v = lastSon(p)
  161. if isPatternParam(c, v) and v.sym.typ.kind == tyVarargs:
  162. var arglist: PNode
  163. if plen <= sonsLen(n):
  164. for i in countup(0, plen - 2):
  165. if not matches(c, p.sons[i], n.sons[i]): return
  166. if plen == sonsLen(n) and lastSon(n).kind == nkHiddenStdConv and
  167. lastSon(n).sons[1].kind == nkBracket:
  168. # unpack varargs:
  169. let n = lastSon(n).sons[1]
  170. arglist = newNodeI(nkArgList, n.info, n.len)
  171. for i in 0.. <n.len: arglist.sons[i] = n.sons[i]
  172. else:
  173. arglist = newNodeI(nkArgList, n.info, sonsLen(n) - plen + 1)
  174. # f(1, 2, 3)
  175. # p(X)
  176. for i in countup(0, sonsLen(n) - plen):
  177. arglist.sons[i] = n.sons[i + plen - 1]
  178. return bindOrCheck(c, v.sym, arglist)
  179. elif plen-1 == sonsLen(n):
  180. for i in countup(0, plen - 2):
  181. if not matches(c, p.sons[i], n.sons[i]): return
  182. arglist = newNodeI(nkArgList, n.info)
  183. return bindOrCheck(c, v.sym, arglist)
  184. if plen == sonsLen(n):
  185. for i in countup(0, sonsLen(p) - 1):
  186. if not matches(c, p.sons[i], n.sons[i]): return
  187. result = true
  188. proc matchStmtList(c: PPatternContext, p, n: PNode): PNode =
  189. proc matchRange(c: PPatternContext, p, n: PNode, i: int): bool =
  190. for j in 0 .. <p.len:
  191. if not matches(c, p.sons[j], n.sons[i+j]):
  192. # we need to undo any bindings:
  193. if not isNil(c.mapping): c.mapping = nil
  194. return false
  195. result = true
  196. if p.kind == nkStmtList and n.kind == p.kind and p.len < n.len:
  197. let n = flattenStmts(n)
  198. # no need to flatten 'p' here as that has already been done
  199. for i in 0 .. n.len - p.len:
  200. if matchRange(c, p, n, i):
  201. c.subMatch = true
  202. result = newNodeI(nkStmtList, n.info, 3)
  203. result.sons[0] = extractRange(nkStmtList, n, 0, i-1)
  204. result.sons[1] = extractRange(nkStmtList, n, i, i+p.len-1)
  205. result.sons[2] = extractRange(nkStmtList, n, i+p.len, n.len-1)
  206. break
  207. elif matches(c, p, n):
  208. result = n
  209. proc aliasAnalysisRequested(params: PNode): bool =
  210. if params.len >= 2:
  211. for i in 1 .. < params.len:
  212. let param = params.sons[i].sym
  213. if whichAlias(param) != aqNone: return true
  214. proc addToArgList(result, n: PNode) =
  215. if n.typ != nil and n.typ.kind != tyStmt:
  216. if n.kind != nkArgList: result.add(n)
  217. else:
  218. for i in 0 .. <n.len: result.add(n.sons[i])
  219. proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
  220. ## returns a tree to semcheck if the rule triggered; nil otherwise
  221. var ctx: TPatternContext
  222. ctx.owner = s
  223. ctx.c = c
  224. ctx.formals = sonsLen(s.typ)-1
  225. var m = matchStmtList(ctx, s.ast.sons[patternPos], n)
  226. if isNil(m): return nil
  227. # each parameter should have been bound; we simply setup a call and
  228. # let semantic checking deal with the rest :-)
  229. result = newNodeI(nkCall, n.info)
  230. result.add(newSymNode(s, n.info))
  231. let params = s.typ.n
  232. let requiresAA = aliasAnalysisRequested(params)
  233. var args: PNode
  234. if requiresAA:
  235. args = newNodeI(nkArgList, n.info)
  236. for i in 1 .. < params.len:
  237. let param = params.sons[i].sym
  238. let x = getLazy(ctx, param)
  239. # couldn't bind parameter:
  240. if isNil(x): return nil
  241. result.add(x)
  242. if requiresAA: addToArgList(args, x)
  243. # perform alias analysis here:
  244. if requiresAA:
  245. for i in 1 .. < params.len:
  246. var rs = result.sons[i]
  247. let param = params.sons[i].sym
  248. case whichAlias(param)
  249. of aqNone: discard
  250. of aqShouldAlias:
  251. # it suffices that it aliases for sure with *some* other param:
  252. var ok = false
  253. for arg in items(args):
  254. if arg != rs and aliases.isPartOf(rs, arg) == arYes:
  255. ok = true
  256. break
  257. # constraint not fulfilled:
  258. if not ok: return nil
  259. of aqNoAlias:
  260. # it MUST not alias with any other param:
  261. var ok = true
  262. for arg in items(args):
  263. if arg != rs and aliases.isPartOf(rs, arg) != arNo:
  264. ok = false
  265. break
  266. # constraint not fulfilled:
  267. if not ok: return nil
  268. markUsed(n.info, s, c.graph.usageSym)
  269. if ctx.subMatch:
  270. assert m.len == 3
  271. m.sons[1] = result
  272. result = m