tcustom_pragma.nim 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. {.experimental: "notnil".}
  2. import macros, asyncmacro, asyncfutures
  3. block:
  4. template myAttr() {.pragma.}
  5. proc myProc():int {.myAttr.} = 2
  6. const hasMyAttr = myProc.hasCustomPragma(myAttr)
  7. static:
  8. doAssert(hasMyAttr)
  9. block:
  10. template myAttr(a: string) {.pragma.}
  11. type
  12. MyObj = object
  13. myField1, myField2 {.myAttr: "hi".}: int
  14. MyGenericObj[T] = object
  15. myField1, myField2 {.myAttr: "hi".}: int
  16. MyOtherObj = MyObj
  17. var o: MyObj
  18. static:
  19. doAssert o.myField2.hasCustomPragma(myAttr)
  20. doAssert(not o.myField1.hasCustomPragma(myAttr))
  21. doAssert(not o.myField1.hasCustomPragma(MyObj))
  22. doAssert(not o.myField1.hasCustomPragma(MyOtherObj))
  23. var ogen: MyGenericObj[int]
  24. static:
  25. doAssert ogen.myField2.hasCustomPragma(myAttr)
  26. doAssert(not ogen.myField1.hasCustomPragma(myAttr))
  27. doAssert(not ogen.myField1.hasCustomPragma(MyGenericObj))
  28. doAssert(not ogen.myField1.hasCustomPragma(MyGenericObj))
  29. import custom_pragma
  30. block: # A bit more advanced case
  31. type
  32. Subfield {.defaultValue: "catman".} = object
  33. `c`* {.serializationKey: "cc".}: float
  34. MySerializable = object
  35. a {.serializationKey"asdf", defaultValue: 5.} : int
  36. b {.custom_pragma.defaultValue"hello".} : int
  37. field: Subfield
  38. d {.alternativeKey("df", 5).}: float
  39. e {.alternativeKey(V = 5).}: seq[bool]
  40. proc myproc(x: int, s: string) {.alternativeKey(V = 5), serializationKey"myprocSS".} =
  41. echo x, s
  42. var s: MySerializable
  43. const aDefVal = s.a.getCustomPragmaVal(defaultValue)
  44. static: doAssert(aDefVal == 5)
  45. const aSerKey = s.a.getCustomPragmaVal(serializationKey)
  46. static: doAssert(aSerKey == "asdf")
  47. const cSerKey = getCustomPragmaVal(s.field.c, serializationKey)
  48. static: doAssert(cSerKey == "cc")
  49. const procSerKey = getCustomPragmaVal(myproc, serializationKey)
  50. static: doAssert(procSerKey == "myprocSS")
  51. static: doAssert(hasCustomPragma(myproc, alternativeKey))
  52. const hasFieldCustomPragma = s.field.hasCustomPragma(defaultValue)
  53. static: doAssert(hasFieldCustomPragma == false)
  54. # pragma on an object
  55. static:
  56. doAssert Subfield.hasCustomPragma(defaultValue)
  57. doAssert(Subfield.getCustomPragmaVal(defaultValue) == "catman")
  58. doAssert hasCustomPragma(type(s.field), defaultValue)
  59. proc foo(s: var MySerializable) =
  60. static: doAssert(s.a.getCustomPragmaVal(defaultValue) == 5)
  61. foo(s)
  62. block: # ref types
  63. type
  64. Node = object of RootObj
  65. left {.serializationKey:"l".}, right {.serializationKey:"r".}: NodeRef
  66. NodeRef = ref Node
  67. NodePtr = ptr Node
  68. SpecialNodeRef = ref object of NodeRef
  69. data {.defaultValue"none".}: string
  70. MyFile {.defaultValue: "closed".} = ref object
  71. path {.defaultValue: "invalid".}: string
  72. TypeWithoutPragma = object
  73. var s = NodeRef()
  74. const
  75. leftSerKey = getCustomPragmaVal(s.left, serializationKey)
  76. rightSerKey = getCustomPragmaVal(s.right, serializationKey)
  77. static:
  78. doAssert leftSerKey == "l"
  79. doAssert rightSerKey == "r"
  80. var specS = SpecialNodeRef()
  81. const
  82. dataDefVal = hasCustomPragma(specS.data, defaultValue)
  83. specLeftSerKey = hasCustomPragma(specS.left, serializationKey)
  84. static:
  85. doAssert dataDefVal == true
  86. doAssert specLeftSerKey == true
  87. var ptrS = NodePtr(nil)
  88. const
  89. ptrRightSerKey = getCustomPragmaVal(ptrS.right, serializationKey)
  90. static:
  91. doAssert ptrRightSerKey == "r"
  92. var f = MyFile()
  93. const
  94. fileDefVal = f.getCustomPragmaVal(defaultValue)
  95. filePathDefVal = f.path.getCustomPragmaVal(defaultValue)
  96. static:
  97. doAssert fileDefVal == "closed"
  98. doAssert filePathDefVal == "invalid"
  99. static:
  100. doAssert TypeWithoutPragma.hasCustomPragma(defaultValue) == false
  101. block:
  102. type
  103. VariantKind = enum
  104. variInt,
  105. variFloat
  106. variString
  107. variNestedCase
  108. Variant = object
  109. case kind: VariantKind
  110. of variInt: integer {.serializationKey: "int".}: BiggestInt
  111. of variFloat: floatp: BiggestFloat
  112. of variString: str {.serializationKey: "string".}: string
  113. of variNestedCase:
  114. case nestedKind: VariantKind
  115. of variInt..variNestedCase: nestedItem {.defaultValue: "Nimmers of the world, unite!".}: int
  116. let vari = Variant(kind: variInt)
  117. const
  118. hasIntSerKey = vari.integer.hasCustomPragma(serializationKey)
  119. strSerKey = vari.str.getCustomPragmaVal(serializationKey)
  120. nestedItemDefVal = vari.nestedItem.getCustomPragmaVal(defaultValue)
  121. static:
  122. doAssert hasIntSerKey
  123. doAssert strSerKey == "string"
  124. doAssert nestedItemDefVal == "Nimmers of the world, unite!"
  125. block:
  126. template simpleAttr {.pragma.}
  127. type Annotated {.simpleAttr.} = object
  128. proc generic_proc[T]() =
  129. doAssert Annotated.hasCustomPragma(simpleAttr)
  130. #--------------------------------------------------------------------------
  131. # Pragma on proc type
  132. type
  133. MyAnnotatedProcType {.defaultValue(4).} = proc(x: int)
  134. let a {.defaultValue(4).}: proc(x: int) = nil
  135. var b: MyAnnotatedProcType = nil
  136. var c: proc(x: int): void {.defaultValue(5).} = nil
  137. var d {.defaultValue(44).}: MyAnnotatedProcType = nil
  138. static:
  139. doAssert hasCustomPragma(a, defaultValue)
  140. doAssert hasCustomPragma(MyAnnotatedProcType, defaultValue)
  141. doAssert hasCustomPragma(b, defaultValue)
  142. doAssert hasCustomPragma(typeof(c), defaultValue)
  143. doAssert getCustomPragmaVal(d, defaultValue) == 44
  144. doAssert getCustomPragmaVal(typeof(d), defaultValue) == 4
  145. # bug #8371
  146. template thingy {.pragma.}
  147. type
  148. Cardinal = enum
  149. north, east, south, west
  150. Something = object
  151. a: float32
  152. case cardinal: Cardinal
  153. of north:
  154. b {.thingy.}: int
  155. of east:
  156. c: int
  157. of south: discard
  158. else: discard
  159. var foo: Something
  160. foo.cardinal = north
  161. doAssert foo.b.hasCustomPragma(thingy) == true
  162. proc myproc(s: string): int =
  163. {.thingy.}:
  164. s.len
  165. doAssert myproc("123") == 3
  166. let xx = compiles:
  167. proc myproc_bad(s: string): int =
  168. {.not_exist.}:
  169. s.len
  170. doAssert: xx == false
  171. macro checkSym(s: typed{nkSym}): untyped =
  172. let body = s.getImpl.body
  173. doAssert body[1].kind == nnkPragmaBlock
  174. doAssert body[1][0].kind == nnkPragma
  175. doAssert body[1][0][0] == bindSym"thingy"
  176. checkSym(myproc)
  177. # var and let pragmas
  178. block:
  179. template myAttr() {.pragma.}
  180. template myAttr2(x: int) {.pragma.}
  181. template myAttr3(x: string) {.pragma.}
  182. type
  183. MyObj2 = ref object
  184. MyObjNotNil = MyObj2 not nil
  185. let a {.myAttr,myAttr2(2),myAttr3:"test".}: int = 0
  186. let b {.myAttr,myAttr2(2),myAttr3:"test".} = 0
  187. var x {.myAttr,myAttr2(2),myAttr3:"test".}: int = 0
  188. var y {.myAttr,myAttr2(2),myAttr3:"test".}: int
  189. var z {.myAttr,myAttr2(2),myAttr3:"test".} = 0
  190. var z2 {.myAttr.}: MyObjNotNil
  191. template check(s: untyped) =
  192. doAssert s.hasCustomPragma(myAttr)
  193. doAssert s.hasCustomPragma(myAttr2)
  194. doAssert s.getCustomPragmaVal(myAttr2) == 2
  195. doAssert s.hasCustomPragma(myAttr3)
  196. doAssert s.getCustomPragmaVal(myAttr3) == "test"
  197. check(a)
  198. check(b)
  199. check(x)
  200. check(y)
  201. check(z)
  202. # pragma with multiple fields
  203. block:
  204. template myAttr(first: string, second: int, third: float) {.pragma.}
  205. let a {.myAttr("one", 2, 3.0).} = 0
  206. let ps = a.getCustomPragmaVal(myAttr)
  207. doAssert ps.first == ps[0] and ps.first == "one"
  208. doAssert ps.second == ps[1] and ps.second == 2
  209. doAssert ps.third == ps[2] and ps.third == 3.0
  210. # pragma with implicit&explicit generic types
  211. block:
  212. template fooBar[T](x: T; c: static[int] = 42; m: char) {.pragma.}
  213. var e {.fooBar("foo", 123, 'u').}: int
  214. doAssert(hasCustomPragma(e, fooBar))
  215. doAssert(getCustomPragmaVal(e, fooBar).c == 123)
  216. block:
  217. macro expectedAst(expectedRepr: static[string], input: untyped): untyped =
  218. doAssert input.treeRepr & "\n" == expectedRepr
  219. return input
  220. macro expectedAstRepr(expectedRepr: static[string], input: untyped): untyped =
  221. doAssert input.repr == expectedRepr
  222. return input
  223. const procTypeAst = """
  224. ProcTy
  225. FormalParams
  226. Empty
  227. IdentDefs
  228. Ident "x"
  229. Ident "int"
  230. Empty
  231. Pragma
  232. Ident "async"
  233. """
  234. type
  235. Foo = proc (x: int) {.expectedAst(procTypeAst), async.}
  236. static: doAssert Foo is proc(x: int): Future[void]
  237. const asyncProcTypeAst = """
  238. proc (s: string): Future[void] {..}"""
  239. # using expectedAst would show `OpenSymChoice` for Future[void], which is fragile.
  240. type
  241. Bar = proc (s: string) {.async, expectedAstRepr(asyncProcTypeAst).}
  242. static: doAssert Bar is proc(x: string): Future[void]
  243. const typeAst = """
  244. TypeDef
  245. PragmaExpr
  246. Ident "Baz"
  247. Pragma
  248. Empty
  249. ObjectTy
  250. Empty
  251. Empty
  252. RecList
  253. IdentDefs
  254. Ident "x"
  255. Ident "string"
  256. Empty
  257. """
  258. type
  259. Baz {.expectedAst(typeAst).} = object
  260. x: string
  261. static: doAssert Baz.x is string
  262. const procAst = """
  263. ProcDef
  264. Ident "bar"
  265. Empty
  266. Empty
  267. FormalParams
  268. Ident "string"
  269. IdentDefs
  270. Ident "s"
  271. Ident "string"
  272. Empty
  273. Empty
  274. Empty
  275. StmtList
  276. ReturnStmt
  277. Ident "s"
  278. """
  279. proc bar(s: string): string {.expectedAst(procAst).} =
  280. return s
  281. static: doAssert bar("x") == "x"
  282. #------------------------------------------------------
  283. # bug #13909
  284. template dependency*(id: string, weight = 0.0) {.pragma.}
  285. type
  286. MyObject* = object
  287. provider*: proc(obj: string): pointer {.dependency("Data/" & obj, 16.1), noSideEffect.}
  288. proc myproc(obj: string): string {.dependency("Data/" & obj, 16.1).} =
  289. result = obj
  290. # bug 12523
  291. template myCustomPragma {.pragma.}
  292. type
  293. RefType = ref object
  294. field {.myCustomPragma.}: int
  295. ObjType = object
  296. field {.myCustomPragma.}: int
  297. RefType2 = ref ObjType
  298. block:
  299. let x = RefType()
  300. for fieldName, fieldSym in fieldPairs(x[]):
  301. doAssert hasCustomPragma(fieldSym, myCustomPragma)
  302. block:
  303. let x = RefType2()
  304. for fieldName, fieldSym in fieldPairs(x[]):
  305. doAssert hasCustomPragma(fieldSym, myCustomPragma)
  306. # bug 8457
  307. block:
  308. template world {.pragma.}
  309. type
  310. Hello = ref object
  311. a: float32
  312. b {.world.}: int
  313. discard Hello(a: 1.0, b: 12)
  314. # test routines
  315. block:
  316. template prag {.pragma.}
  317. proc hello {.prag.} = discard
  318. iterator hello2: int {.prag.} = discard
  319. template hello3(x: int): int {.prag.} = x
  320. macro hello4(x: int): int {.prag.} = x
  321. func hello5(x: int): int {.prag.} = x
  322. doAssert hello.hasCustomPragma(prag)
  323. doAssert hello2.hasCustomPragma(prag)
  324. doAssert hello3.hasCustomPragma(prag)
  325. doAssert hello4.hasCustomPragma(prag)
  326. doAssert hello5.hasCustomPragma(prag)
  327. # test push doesn't break
  328. block:
  329. template prag {.pragma.}
  330. {.push prag.}
  331. proc hello = discard
  332. iterator hello2: int = discard
  333. template hello3(x: int): int = x
  334. macro hello4(x: int): int = x
  335. func hello5(x: int): int = x
  336. type
  337. Foo = enum a
  338. Bar[T] = ref object of RootObj
  339. x: T
  340. case y: bool
  341. of false: discard
  342. else:
  343. when true: discard
  344. for a in [1]: discard a
  345. {.pop.}
  346. # issue #11511
  347. when false:
  348. template myAttr {.pragma.}
  349. type TObj = object
  350. a {.myAttr.}: int
  351. macro hasMyAttr(t: typedesc): untyped =
  352. let objTy = t.getType[1].getType
  353. let recList = objTy[2]
  354. let sym = recList[0]
  355. assert sym.kind == nnkSym and sym.eqIdent("a")
  356. let hasAttr = sym.hasCustomPragma(myAttr)
  357. newLit(hasAttr)
  358. doAssert hasMyAttr(TObj)
  359. # bug #11415
  360. template noserialize() {.pragma.}
  361. type
  362. Point[T] = object
  363. x, y: T
  364. ReplayEventKind = enum
  365. FoodAppeared, FoodEaten, DirectionChanged
  366. ReplayEvent = object
  367. case kind: ReplayEventKind
  368. of FoodEaten, FoodAppeared: # foodPos is in multiple branches
  369. foodPos {.noserialize.}: Point[float]
  370. of DirectionChanged:
  371. playerPos: float
  372. let ev = ReplayEvent(
  373. kind: FoodEaten,
  374. foodPos: Point[float](x: 5.0, y: 1.0)
  375. )
  376. doAssert ev.foodPos.hasCustomPragma(noserialize)
  377. when false:
  378. # misc
  379. {.pragma: haha.}
  380. {.pragma: hoho.}
  381. template hehe(key, val: string, haha) {.pragma.}
  382. type A {.haha, hoho, haha, hehe("hi", "hu", "he").} = int
  383. assert A.getCustomPragmaVal(hehe) == (key: "hi", val: "hu", haha: "he")
  384. template hehe(key, val: int) {.pragma.}
  385. var bb {.haha, hoho, hehe(1, 2), haha, hehe("hi", "hu", "he").} = 3
  386. # left-to-right priority/override order for getCustomPragmaVal
  387. assert bb.getCustomPragmaVal(hehe) == (key: "hi", val: "hu", haha: "he")
  388. {.experimental: "dynamicBindSym".}
  389. # const
  390. block:
  391. template myAttr() {.pragma.}
  392. template myAttr2(x: int) {.pragma.}
  393. template myAttr3(x: string) {.pragma.}
  394. type
  395. MyObj2 = ref object
  396. const a {.myAttr,myAttr2(2),myAttr3:"test".}: int = 0
  397. const b {.myAttr,myAttr2(2),myAttr3:"test".} = 0
  398. macro forceHasCustomPragma(x: untyped, y: typed): untyped =
  399. var x = bindSym(x.repr)
  400. for c in x:
  401. if c.symKind == nskConst:
  402. x = c
  403. break
  404. result = getAst(hasCustomPragma(x, y))
  405. macro forceGetCustomPragmaVal(x: untyped, y: typed): untyped =
  406. var x = bindSym(x.repr)
  407. for c in x:
  408. if c.symKind == nskConst:
  409. x = c
  410. break
  411. result = getAst(getCustomPragmaVal(x, y))
  412. template check(s: untyped) =
  413. doAssert forceHasCustomPragma(s, myAttr)
  414. doAssert forceHasCustomPragma(s, myAttr2)
  415. doAssert forceGetCustomPragmaVal(s, myAttr2) == 2
  416. doAssert forceHasCustomPragma(s, myAttr3)
  417. doAssert forceGetCustomPragmaVal(s, myAttr3) == "test"
  418. check(a)
  419. check(b)