parampatterns.nim 11 KB

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