parampatterns.nim 11 KB

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