parampatterns.nim 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  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 strutils, ast, astalgo, types, msgs, idents, renderer, wordrecg, trees
  12. # we precompile the pattern here for efficiency into some internal
  13. # stack based VM :-) Why? Because it's fun; I did no benchmarks to see if that
  14. # actually improves performance.
  15. type
  16. TAliasRequest* = enum # first byte of the bytecode determines alias checking
  17. aqNone = 1, # no alias analysis requested
  18. aqShouldAlias, # with some other param
  19. aqNoAlias # request noalias
  20. TOpcode = enum
  21. ppEof = 1, # end of compiled pattern
  22. ppOr, # we could short-cut the evaluation for 'and' and 'or',
  23. ppAnd, # but currently we don't
  24. ppNot,
  25. ppSym,
  26. ppAtom,
  27. ppLit,
  28. ppIdent,
  29. ppCall,
  30. ppSymKind,
  31. ppNodeKind,
  32. ppLValue,
  33. ppLocal,
  34. ppSideEffect,
  35. ppNoSideEffect
  36. TPatternCode = string
  37. const
  38. MaxStackSize* = 64 ## max required stack size by the VM
  39. proc patternError(n: PNode) =
  40. localError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
  41. proc add(code: var TPatternCode, op: TOpcode) {.inline.} =
  42. add(code, chr(ord(op)))
  43. proc whichAlias*(p: PSym): TAliasRequest =
  44. if p.constraint != nil:
  45. result = TAliasRequest(p.constraint.strVal[0].ord)
  46. else:
  47. result = aqNone
  48. proc compileConstraints(p: PNode, result: var TPatternCode) =
  49. case p.kind
  50. of nkCallKinds:
  51. if p.sons[0].kind != nkIdent:
  52. patternError(p.sons[0])
  53. return
  54. let op = p.sons[0].ident
  55. if p.len == 3:
  56. if op.s == "|" or op.id == ord(wOr):
  57. compileConstraints(p.sons[1], result)
  58. compileConstraints(p.sons[2], result)
  59. result.add(ppOr)
  60. elif op.s == "&" or op.id == ord(wAnd):
  61. compileConstraints(p.sons[1], result)
  62. compileConstraints(p.sons[2], result)
  63. result.add(ppAnd)
  64. else:
  65. patternError(p)
  66. elif p.len == 2 and (op.s == "~" or op.id == ord(wNot)):
  67. compileConstraints(p.sons[1], result)
  68. result.add(ppNot)
  69. else:
  70. patternError(p)
  71. of nkAccQuoted, nkPar:
  72. if p.len == 1:
  73. compileConstraints(p.sons[0], result)
  74. else:
  75. patternError(p)
  76. of nkIdent:
  77. let spec = p.ident.s.normalize
  78. case spec
  79. of "atom": result.add(ppAtom)
  80. of "lit": result.add(ppLit)
  81. of "sym": result.add(ppSym)
  82. of "ident": result.add(ppIdent)
  83. of "call": result.add(ppCall)
  84. of "alias": result[0] = chr(aqShouldAlias.ord)
  85. of "noalias": result[0] = chr(aqNoAlias.ord)
  86. of "lvalue": result.add(ppLValue)
  87. of "local": result.add(ppLocal)
  88. of "sideeffect": result.add(ppSideEffect)
  89. of "nosideeffect": result.add(ppNoSideEffect)
  90. else:
  91. # check all symkinds:
  92. internalAssert int(high(TSymKind)) < 255
  93. for i in low(TSymKind)..high(TSymKind):
  94. if cmpIgnoreStyle(($i).substr(2), spec) == 0:
  95. result.add(ppSymKind)
  96. result.add(chr(i.ord))
  97. return
  98. # check all nodekinds:
  99. internalAssert int(high(TNodeKind)) < 255
  100. for i in low(TNodeKind)..high(TNodeKind):
  101. if cmpIgnoreStyle($i, spec) == 0:
  102. result.add(ppNodeKind)
  103. result.add(chr(i.ord))
  104. return
  105. patternError(p)
  106. else:
  107. patternError(p)
  108. proc semNodeKindConstraints*(p: PNode): PNode =
  109. ## does semantic checking for a node kind pattern and compiles it into an
  110. ## efficient internal format.
  111. assert p.kind == nkCurlyExpr
  112. result = newNodeI(nkStrLit, p.info)
  113. result.strVal = newStringOfCap(10)
  114. result.strVal.add(chr(aqNone.ord))
  115. if p.len >= 2:
  116. for i in 1.. <p.len:
  117. compileConstraints(p.sons[i], result.strVal)
  118. if result.strVal.len > MaxStackSize-1:
  119. internalError(p.info, "parameter pattern too complex")
  120. else:
  121. patternError(p)
  122. result.strVal.add(ppEof)
  123. type
  124. TSideEffectAnalysis* = enum
  125. seUnknown, seSideEffect, seNoSideEffect
  126. proc checkForSideEffects*(n: PNode): TSideEffectAnalysis =
  127. case n.kind
  128. of nkCallKinds:
  129. # only calls can produce side effects:
  130. let op = n.sons[0]
  131. if op.kind == nkSym and isRoutine(op.sym):
  132. let s = op.sym
  133. if sfSideEffect in s.flags:
  134. return seSideEffect
  135. # assume no side effect:
  136. result = seNoSideEffect
  137. elif tfNoSideEffect in op.typ.flags:
  138. # indirect call without side effects:
  139. result = seNoSideEffect
  140. else:
  141. # indirect call: assume side effect:
  142. return seSideEffect
  143. # we need to check n[0] too: (FwithSideEffectButReturnsProcWithout)(args)
  144. for i in 0 .. <n.len:
  145. let ret = checkForSideEffects(n.sons[i])
  146. if ret == seSideEffect: return ret
  147. elif ret == seUnknown and result == seNoSideEffect:
  148. result = seUnknown
  149. of nkNone..nkNilLit:
  150. # an atom cannot produce a side effect:
  151. result = seNoSideEffect
  152. else:
  153. # assume no side effect:
  154. result = seNoSideEffect
  155. for i in 0 .. <n.len:
  156. let ret = checkForSideEffects(n.sons[i])
  157. if ret == seSideEffect: return ret
  158. elif ret == seUnknown and result == seNoSideEffect:
  159. result = seUnknown
  160. type
  161. TAssignableResult* = enum
  162. arNone, # no l-value and no discriminant
  163. arLValue, # is an l-value
  164. arLocalLValue, # is an l-value, but local var; must not escape
  165. # its stack frame!
  166. arDiscriminant, # is a discriminant
  167. arStrange # it is a strange beast like 'typedesc[var T]'
  168. proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult =
  169. ## 'owner' can be nil!
  170. result = arNone
  171. case n.kind
  172. of nkEmpty:
  173. if n.typ != nil and n.typ.kind == tyVar:
  174. result = arLValue
  175. of nkSym:
  176. let kinds = if isUnsafeAddr: {skVar, skResult, skTemp, skParam, skLet}
  177. else: {skVar, skResult, skTemp}
  178. if n.sym.kind in kinds:
  179. if owner != nil and owner.id == n.sym.owner.id and
  180. sfGlobal notin n.sym.flags:
  181. result = arLocalLValue
  182. else:
  183. result = arLValue
  184. elif n.sym.kind == skParam and n.sym.typ.kind == tyVar:
  185. result = arLValue
  186. elif n.sym.kind == skType:
  187. let t = n.sym.typ.skipTypes({tyTypeDesc})
  188. if t.kind == tyVar: result = arStrange
  189. of nkDotExpr:
  190. if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind in
  191. {tyVar, tyPtr, tyRef}:
  192. result = arLValue
  193. else:
  194. result = isAssignable(owner, n.sons[0], isUnsafeAddr)
  195. if result != arNone and sfDiscriminant in n.sons[1].sym.flags:
  196. result = arDiscriminant
  197. of nkBracketExpr:
  198. if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind in
  199. {tyVar, tyPtr, tyRef}:
  200. result = arLValue
  201. else:
  202. result = isAssignable(owner, n.sons[0], isUnsafeAddr)
  203. of nkHiddenStdConv, nkHiddenSubConv, nkConv:
  204. # Object and tuple conversions are still addressable, so we skip them
  205. # XXX why is 'tyOpenArray' allowed here?
  206. if skipTypes(n.typ, abstractPtrs-{tyTypeDesc}).kind in
  207. {tyOpenArray, tyTuple, tyObject}:
  208. result = isAssignable(owner, n.sons[1], isUnsafeAddr)
  209. elif compareTypes(n.typ, n.sons[1].typ, dcEqIgnoreDistinct):
  210. # types that are equal modulo distinction preserve l-value:
  211. result = isAssignable(owner, n.sons[1], isUnsafeAddr)
  212. of nkHiddenDeref, nkDerefExpr, nkHiddenAddr:
  213. result = arLValue
  214. of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
  215. result = isAssignable(owner, n.sons[0], isUnsafeAddr)
  216. of nkCallKinds:
  217. # builtin slice keeps lvalue-ness:
  218. if getMagic(n) in {mArrGet, mSlice}:
  219. result = isAssignable(owner, n.sons[1], isUnsafeAddr)
  220. elif n.typ != nil and n.typ.kind == tyVar:
  221. result = arLValue
  222. of nkStmtList, nkStmtListExpr:
  223. if n.typ != nil:
  224. result = isAssignable(owner, n.lastSon, isUnsafeAddr)
  225. of nkVarTy:
  226. # XXX: The fact that this is here is a bit of a hack.
  227. # The goal is to allow the use of checks such as "foo(var T)"
  228. # within concepts. Semantically, it's not correct to say that
  229. # nkVarTy denotes an lvalue, but the example above is the only
  230. # possible code which will get us here
  231. result = arLValue
  232. else:
  233. discard
  234. proc isLValue*(n: PNode): bool =
  235. isAssignable(nil, n) in {arLValue, arLocalLValue, arStrange}
  236. proc matchNodeKinds*(p, n: PNode): bool =
  237. # matches the parameter constraint 'p' against the concrete AST 'n'.
  238. # Efficiency matters here.
  239. var stack {.noinit.}: array[0..MaxStackSize, bool]
  240. # empty patterns are true:
  241. stack[0] = true
  242. var sp = 1
  243. template push(x: bool) =
  244. stack[sp] = x
  245. inc sp
  246. let code = p.strVal
  247. var pc = 1
  248. while true:
  249. case TOpcode(code[pc])
  250. of ppEof: break
  251. of ppOr:
  252. stack[sp-2] = stack[sp-1] or stack[sp-2]
  253. dec sp
  254. of ppAnd:
  255. stack[sp-2] = stack[sp-1] and stack[sp-2]
  256. dec sp
  257. of ppNot: stack[sp-1] = not stack[sp-1]
  258. of ppSym: push n.kind == nkSym
  259. of ppAtom: push isAtom(n)
  260. of ppLit: push n.kind in {nkCharLit..nkNilLit}
  261. of ppIdent: push n.kind == nkIdent
  262. of ppCall: push n.kind in nkCallKinds
  263. of ppSymKind:
  264. let kind = TSymKind(code[pc+1])
  265. push n.kind == nkSym and n.sym.kind == kind
  266. inc pc
  267. of ppNodeKind:
  268. let kind = TNodeKind(code[pc+1])
  269. push n.kind == kind
  270. inc pc
  271. of ppLValue: push isAssignable(nil, n) in {arLValue, arLocalLValue}
  272. of ppLocal: push isAssignable(nil, n) == arLocalLValue
  273. of ppSideEffect: push checkForSideEffects(n) == seSideEffect
  274. of ppNoSideEffect: push checkForSideEffects(n) != seSideEffect
  275. inc pc
  276. result = stack[sp-1]