tastspec.nim 22 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088
  1. discard """
  2. action: compile
  3. """
  4. # this test should ensure that the AST doesn't change slightly without it getting noticed.
  5. import ../ast_pattern_matching
  6. template expectNimNode(arg: untyped): NimNode = arg
  7. ## This template here is just to be injected by `myquote`, so that
  8. ## a nice error message appears when the captured symbols are not of
  9. ## type `NimNode`.
  10. proc substitudeComments(symbols, values, n: NimNode): NimNode =
  11. ## substitutes all nodes of kind nnkCommentStmt to parameter
  12. ## symbols. Consumes the argument `n`.
  13. if n.kind == nnkCommentStmt:
  14. values.add newCall(bindSym"newCommentStmtNode", newLit(n.strVal))
  15. # Gensym doesn't work for parameters. These identifiers won't
  16. # clash unless an argument is constructed to clash here.
  17. symbols.add ident("comment" & $values.len & "_XObBdOnh6meCuJK2smZV")
  18. return symbols[^1]
  19. for i in 0 ..< n.len:
  20. n[i] = substitudeComments(symbols, values, n[i])
  21. return n
  22. macro myquote*(args: varargs[untyped]): untyped =
  23. expectMinLen(args, 1)
  24. # This is a workaround for #10430 where comments are removed in
  25. # template expansions. This workaround lifts all comments
  26. # statements to be arguments of the temporary template.
  27. let extraCommentSymbols = newNimNode(nnkBracket)
  28. let extraCommentGenExpr = newNimNode(nnkBracket)
  29. let body = substitudeComments(
  30. extraCommentSymbols, extraCommentGenExpr, args[^1]
  31. )
  32. let formalParams = nnkFormalParams.newTree(ident"untyped")
  33. for i in 0 ..< args.len-1:
  34. formalParams.add nnkIdentDefs.newTree(
  35. args[i], ident"untyped", newEmptyNode()
  36. )
  37. for sym in extraCommentSymbols:
  38. formalParams.add nnkIdentDefs.newTree(
  39. sym, ident"untyped", newEmptyNode()
  40. )
  41. let templateSym = genSym(nskTemplate)
  42. let templateDef = nnkTemplateDef.newTree(
  43. templateSym,
  44. newEmptyNode(),
  45. newEmptyNode(),
  46. formalParams,
  47. nnkPragma.newTree(ident"dirty"),
  48. newEmptyNode(),
  49. args[^1]
  50. )
  51. let templateCall = newCall(templateSym)
  52. for i in 0 ..< args.len-1:
  53. let symName = args[i]
  54. # identifiers and quoted identifiers are allowed.
  55. if symName.kind == nnkAccQuoted:
  56. symName.expectLen 1
  57. symName[0].expectKind nnkIdent
  58. else:
  59. symName.expectKind nnkIdent
  60. templateCall.add newCall(bindSym"expectNimNode", symName)
  61. for expr in extraCommentGenExpr:
  62. templateCall.add expr
  63. let getAstCall = newCall(bindSym"getAst", templateCall)
  64. result = newStmtList(templateDef, getAstCall)
  65. macro testAddrAst(arg: typed): bool =
  66. arg.expectKind nnkStmtListExpr
  67. arg[0].expectKind(nnkVarSection)
  68. arg[1].expectKind({nnkAddr, nnkCall})
  69. result = newLit(arg[1].kind == nnkCall)
  70. const newAddrAst: bool = testAddrAst((var x: int; addr(x)))
  71. static:
  72. echo "new addr ast: ", newAddrAst
  73. # TODO test on matching failures
  74. proc peelOff*(arg: NimNode, kinds: set[NimNodeKind]): NimNode {.compileTime.} =
  75. ## Peel off nodes of a specific kinds.
  76. if arg.len == 1 and arg.kind in kinds:
  77. arg[0].peelOff(kinds)
  78. else:
  79. arg
  80. proc peelOff*(arg: NimNode, kind: NimNodeKind): NimNode {.compileTime.} =
  81. ## Peel off nodes of a specific kind.
  82. if arg.len == 1 and arg.kind == kind:
  83. arg[0].peelOff(kind)
  84. else:
  85. arg
  86. static:
  87. template testPattern(pattern, astArg: untyped): untyped =
  88. let ast = quote do: `astArg`
  89. ast.matchAst:
  90. of `pattern`:
  91. echo "ok"
  92. template testPatternFail(pattern, astArg: untyped): untyped =
  93. let ast = quote do: `astArg`
  94. ast.matchAst:
  95. of `pattern`:
  96. error("this should not match", ast)
  97. else:
  98. echo "OK"
  99. testPattern nnkIntLit(intVal = 42), 42
  100. testPattern nnkInt8Lit(intVal = 42), 42'i8
  101. testPattern nnkInt16Lit(intVal = 42), 42'i16
  102. testPattern nnkInt32Lit(intVal = 42), 42'i32
  103. testPattern nnkInt64Lit(intVal = 42), 42'i64
  104. testPattern nnkUInt8Lit(intVal = 42), 42'u8
  105. testPattern nnkUInt16Lit(intVal = 42), 42'u16
  106. testPattern nnkUInt32Lit(intVal = 42), 42'u32
  107. testPattern nnkUInt64Lit(intVal = 42), 42'u64
  108. #testPattern nnkFloat64Lit(floatVal = 42.0), 42.0
  109. testPattern nnkFloat32Lit(floatVal = 42.0), 42.0'f32
  110. #testPattern nnkFloat64Lit(floatVal = 42.0), 42.0'f64
  111. testPattern nnkStrLit(strVal = "abc"), "abc"
  112. testPattern nnkRStrLit(strVal = "abc"), r"abc"
  113. testPattern nnkTripleStrLit(strVal = "abc"), """abc"""
  114. testPattern nnkCharLit(intVal = 32), ' '
  115. testPattern nnkNilLit(), nil
  116. testPattern nnkIdent(strVal = "myIdentifier"), myIdentifier
  117. testPatternFail nnkInt8Lit(intVal = 42), 42'i16
  118. testPatternFail nnkInt16Lit(intVal = 42), 42'i8
  119. # this should be just `block` but it doesn't work that way anymore because of VM.
  120. macro scope(arg: untyped): untyped =
  121. let procSym = genSym(nskProc)
  122. result = quote do:
  123. proc `procSym`() {.compileTime.} =
  124. `arg`
  125. `procSym`()
  126. static:
  127. ## Command call
  128. scope:
  129. let ast = myquote:
  130. echo "abc", "xyz"
  131. ast.matchAst:
  132. of nnkCommand(ident"echo", "abc", "xyz"):
  133. echo "ok"
  134. ## Call with ``()``
  135. scope:
  136. let ast = myquote:
  137. echo("abc", "xyz")
  138. ast.matchAst:
  139. of nnkCall(ident"echo", "abc", "xyz"):
  140. echo "ok"
  141. ## Infix operator call
  142. macro testInfixOperatorCall(ast: untyped): untyped =
  143. ast.matchAst(errorSym):
  144. of nnkInfix(
  145. ident"&",
  146. nnkStrLit(strVal = "abc"),
  147. nnkStrLit(strVal = "xyz")
  148. ):
  149. echo "ok1"
  150. of nnkInfix(
  151. ident"+",
  152. nnkIntLit(intVal = 5),
  153. nnkInfix(
  154. ident"*",
  155. nnkIntLit(intVal = 3),
  156. nnkIntLit(intVal = 4)
  157. )
  158. ):
  159. echo "ok2"
  160. of nnkCall(
  161. nnkAccQuoted(
  162. ident"+"
  163. ),
  164. nnkIntLit(intVal = 3),
  165. nnkIntLit(intVal = 4)
  166. ):
  167. echo "ok3"
  168. testInfixOperatorCall("abc" & "xyz")
  169. testInfixOperatorCall(5 + 3 * 4)
  170. testInfixOperatorCall(`+`(3, 4))
  171. ## Prefix operator call
  172. scope:
  173. let ast = myquote:
  174. ? "xyz"
  175. ast.matchAst(err):
  176. of nnkPrefix(
  177. ident"?",
  178. nnkStrLit(strVal = "xyz")
  179. ):
  180. echo "ok"
  181. ## Postfix operator call
  182. scope:
  183. let ast = myquote:
  184. proc identifier*
  185. ast[0].matchAst(err):
  186. of nnkPostfix(
  187. ident"*",
  188. ident"identifier"
  189. ):
  190. echo "ok"
  191. ## Call with named arguments
  192. macro testCallWithNamedArguments(ast: untyped): untyped =
  193. ast.peelOff(nnkStmtList).matchAst:
  194. of nnkCall(
  195. ident"writeLine",
  196. nnkExprEqExpr(
  197. ident"file",
  198. ident"stdout"
  199. ),
  200. nnkStrLit(strVal = "hallo")
  201. ):
  202. echo "ok"
  203. testCallWithNamedArguments:
  204. writeLine(file=stdout, "hallo")
  205. ## Call with raw string literal
  206. scope:
  207. let ast = myquote:
  208. echo"abc"
  209. ast.matchAst(err):
  210. of nnkCallStrLit(
  211. ident"echo",
  212. nnkRStrLit(strVal = "abc")
  213. ):
  214. echo "ok"
  215. ## Dereference operator ``[]``
  216. scope:
  217. # The dereferece operator exists only on a typed ast.
  218. macro testDereferenceOperator(ast: typed): untyped =
  219. ast.matchAst(err):
  220. of nnkDerefExpr(_):
  221. echo "ok"
  222. var x: ptr int
  223. testDereferenceOperator(x[])
  224. ## Addr operator
  225. scope:
  226. # The addr operator exists only on a typed ast.
  227. macro testAddrOperator(ast: untyped): untyped =
  228. echo ast.treeRepr
  229. ast.matchAst(err):
  230. of nnkAddr(ident"x"):
  231. echo "old nim"
  232. of nnkCall(ident"addr", ident"x"):
  233. echo "ok"
  234. var x: int
  235. testAddrOperator(addr(x))
  236. ## Cast operator
  237. scope:
  238. let ast = myquote:
  239. cast[T](x)
  240. ast.matchAst:
  241. of nnkCast(ident"T", ident"x"):
  242. echo "ok"
  243. ## Object access operator ``.``
  244. scope:
  245. let ast = myquote:
  246. x.y
  247. ast.matchAst:
  248. of nnkDotExpr(ident"x", ident"y"):
  249. echo "ok"
  250. ## Array access operator ``[]``
  251. macro testArrayAccessOperator(ast: untyped): untyped =
  252. ast.matchAst:
  253. of nnkBracketExpr(ident"x", ident"y"):
  254. echo "ok"
  255. testArrayAccessOperator(x[y])
  256. ## Parentheses
  257. scope:
  258. let ast = myquote:
  259. (1, 2, (3))
  260. ast.matchAst:
  261. of nnkPar(nnkIntLit(intVal = 1), nnkIntLit(intVal = 2), nnkPar(nnkIntLit(intVal = 3))):
  262. echo "ok"
  263. ## Curly braces
  264. scope:
  265. let ast = myquote:
  266. {1, 2, 3}
  267. ast.matchAst:
  268. of nnkCurly(nnkIntLit(intVal = 1), nnkIntLit(intVal = 2), nnkIntLit(intVal = 3)):
  269. echo "ok"
  270. scope:
  271. let ast = myquote:
  272. {a: 3, b: 5}
  273. ast.matchAst:
  274. of nnkTableConstr(
  275. nnkExprColonExpr(ident"a", nnkIntLit(intVal = 3)),
  276. nnkExprColonExpr(ident"b", nnkIntLit(intVal = 5))
  277. ):
  278. echo "ok"
  279. ## Brackets
  280. scope:
  281. let ast = myquote:
  282. [1, 2, 3]
  283. ast.matchAst:
  284. of nnkBracket(nnkIntLit(intVal = 1), nnkIntLit(intVal = 2), nnkIntLit(intVal = 3)):
  285. echo "ok"
  286. ## Ranges
  287. scope:
  288. let ast = myquote:
  289. 1..3
  290. ast.matchAst:
  291. of nnkInfix(
  292. ident"..",
  293. nnkIntLit(intVal = 1),
  294. nnkIntLit(intVal = 3)
  295. ):
  296. echo "ok"
  297. ## If expression
  298. scope:
  299. let ast = myquote:
  300. if cond1: expr1 elif cond2: expr2 else: expr3
  301. ast.matchAst:
  302. of {nnkIfExpr, nnkIfStmt}(
  303. {nnkElifExpr, nnkElifBranch}(`cond1`, `expr1`),
  304. {nnkElifExpr, nnkElifBranch}(`cond2`, `expr2`),
  305. {nnkElseExpr, nnkElse}(`expr3`)
  306. ):
  307. echo "ok"
  308. ## Documentation Comments
  309. scope:
  310. let ast = myquote:
  311. ## This is a comment
  312. ## This is part of the first comment
  313. stmt1
  314. ## Yet another
  315. ast.matchAst:
  316. of nnkStmtList(
  317. nnkCommentStmt(),
  318. `stmt1`,
  319. nnkCommentStmt()
  320. ):
  321. echo "ok"
  322. else:
  323. echo "warning!"
  324. echo ast.treeRepr
  325. echo "TEST causes no fail, because of a regression in Nim."
  326. scope:
  327. let ast = myquote:
  328. {.emit: "#include <stdio.h>".}
  329. ast.matchAst:
  330. of nnkPragma(
  331. nnkExprColonExpr(
  332. ident"emit",
  333. nnkStrLit(strVal = "#include <stdio.h>") # the "argument"
  334. )
  335. ):
  336. echo "ok"
  337. scope:
  338. let ast = myquote:
  339. {.pragma: cdeclRename, cdecl.}
  340. ast.matchAst:
  341. of nnkPragma(
  342. nnkExprColonExpr(
  343. ident"pragma", # this is always first when declaring a new pragma
  344. ident"cdeclRename" # the name of the pragma
  345. ),
  346. ident"cdecl"
  347. ):
  348. echo "ok"
  349. scope:
  350. let ast = myquote:
  351. if cond1:
  352. stmt1
  353. elif cond2:
  354. stmt2
  355. elif cond3:
  356. stmt3
  357. else:
  358. stmt4
  359. ast.matchAst:
  360. of nnkIfStmt(
  361. nnkElifBranch(`cond1`, `stmt1`),
  362. nnkElifBranch(`cond2`, `stmt2`),
  363. nnkElifBranch(`cond3`, `stmt3`),
  364. nnkElse(`stmt4`)
  365. ):
  366. echo "ok"
  367. scope:
  368. let ast = myquote:
  369. x = 42
  370. ast.matchAst:
  371. of nnkAsgn(ident"x", nnkIntLit(intVal = 42)):
  372. echo "ok"
  373. scope:
  374. let ast = myquote:
  375. stmt1
  376. stmt2
  377. stmt3
  378. ast.matchAst:
  379. of nnkStmtList(`stmt1`, `stmt2`, `stmt3`):
  380. assert stmt1.strVal == "stmt1"
  381. assert stmt2.strVal == "stmt2"
  382. assert stmt3.strVal == "stmt3"
  383. echo "ok"
  384. ## Case statement
  385. scope:
  386. let ast = myquote:
  387. case expr1
  388. of expr2, expr3..expr4:
  389. stmt1
  390. of expr5:
  391. stmt2
  392. elif cond1:
  393. stmt3
  394. else:
  395. stmt4
  396. ast.matchAst:
  397. of nnkCaseStmt(
  398. `expr1`,
  399. nnkOfBranch(`expr2`, {nnkRange, nnkInfix}(_, `expr3`, `expr4`), `stmt1`),
  400. nnkOfBranch(`expr5`, `stmt2`),
  401. nnkElifBranch(`cond1`, `stmt3`),
  402. nnkElse(`stmt4`)
  403. ):
  404. echo "ok"
  405. ## While statement
  406. scope:
  407. let ast = myquote:
  408. while expr1:
  409. stmt1
  410. ast.matchAst:
  411. of nnkWhileStmt(`expr1`, `stmt1`):
  412. echo "ok"
  413. ## For statement
  414. scope:
  415. let ast = myquote:
  416. for ident1, ident2 in expr1:
  417. stmt1
  418. ast.matchAst:
  419. of nnkForStmt(`ident1`, `ident2`, `expr1`, `stmt1`):
  420. echo "ok"
  421. ## Try statement
  422. scope:
  423. let ast = myquote:
  424. try:
  425. stmt1
  426. except e1, e2:
  427. stmt2
  428. except e3:
  429. stmt3
  430. except:
  431. stmt4
  432. finally:
  433. stmt5
  434. ast.matchAst:
  435. of nnkTryStmt(
  436. `stmt1`,
  437. nnkExceptBranch(`e1`, `e2`, `stmt2`),
  438. nnkExceptBranch(`e3`, `stmt3`),
  439. nnkExceptBranch(`stmt4`),
  440. nnkFinally(`stmt5`)
  441. ):
  442. echo "ok"
  443. ## Return statement
  444. scope:
  445. let ast = myquote:
  446. return expr1
  447. ast.matchAst:
  448. of nnkReturnStmt(`expr1`):
  449. echo "ok"
  450. ## Continue statement
  451. scope:
  452. let ast = myquote:
  453. continue
  454. ast.matchAst:
  455. of nnkContinueStmt:
  456. echo "ok"
  457. ## Break statement
  458. scope:
  459. let ast = myquote:
  460. break otherLocation
  461. ast.matchAst:
  462. of nnkBreakStmt(ident"otherLocation"):
  463. echo "ok"
  464. ## Block statement
  465. scope:
  466. template blockStatement {.dirty.} =
  467. block name:
  468. discard
  469. let ast = getAst(blockStatement())
  470. ast.matchAst:
  471. of nnkBlockStmt(ident"name", nnkStmtList):
  472. echo "ok"
  473. ## Asm statement
  474. scope:
  475. let ast = myquote:
  476. asm """some asm"""
  477. ast.matchAst:
  478. of nnkAsmStmt(
  479. nnkEmpty(), # for pragmas
  480. nnkTripleStrLit(strVal = "some asm"),
  481. ):
  482. echo "ok"
  483. ## Import section
  484. scope:
  485. let ast = myquote:
  486. import math
  487. ast.matchAst:
  488. of nnkImportStmt(ident"math"):
  489. echo "ok"
  490. scope:
  491. let ast = myquote:
  492. import math except pow
  493. ast.matchAst:
  494. of nnkImportExceptStmt(ident"math",ident"pow"):
  495. echo "ok"
  496. scope:
  497. let ast = myquote:
  498. import strutils as su
  499. ast.matchAst:
  500. of nnkImportStmt(
  501. nnkInfix(
  502. ident"as",
  503. ident"strutils",
  504. ident"su"
  505. )
  506. ):
  507. echo "ok"
  508. ## From statement
  509. scope:
  510. let ast = myquote:
  511. from math import pow
  512. ast.matchAst:
  513. of nnkFromStmt(ident"math", ident"pow"):
  514. echo "ok"
  515. ## Export statement
  516. scope:
  517. let ast = myquote:
  518. export unsigned
  519. ast.matchAst:
  520. of nnkExportStmt(ident"unsigned"):
  521. echo "ok"
  522. scope:
  523. let ast = myquote:
  524. export math except pow # we're going to implement our own exponentiation
  525. ast.matchAst:
  526. of nnkExportExceptStmt(ident"math",ident"pow"):
  527. echo "ok"
  528. ## Include statement
  529. scope:
  530. let ast = myquote:
  531. include blocks
  532. ast.matchAst:
  533. of nnkIncludeStmt(ident"blocks"):
  534. echo "ok"
  535. ## Var section
  536. scope:
  537. let ast = myquote:
  538. var a = 3
  539. ast.matchAst:
  540. of nnkVarSection(
  541. nnkIdentDefs(
  542. ident"a",
  543. nnkEmpty(), # or nnkIdent(...) if the variable declares the type
  544. nnkIntLit(intVal = 3),
  545. )
  546. ):
  547. echo "ok"
  548. ## Let section
  549. scope:
  550. let ast = myquote:
  551. let a = 3
  552. ast.matchAst:
  553. of nnkLetSection(
  554. nnkIdentDefs(
  555. ident"a",
  556. nnkEmpty(), # or nnkIdent(...) for the type
  557. nnkIntLit(intVal = 3),
  558. )
  559. ):
  560. echo "ok"
  561. ## Const section
  562. scope:
  563. let ast = myquote:
  564. const a = 3
  565. ast.matchAst:
  566. of nnkConstSection(
  567. nnkConstDef( # not nnkConstDefs!
  568. ident"a",
  569. nnkEmpty(), # or nnkIdent(...) if the variable declares the type
  570. nnkIntLit(intVal = 3), # required in a const declaration!
  571. )
  572. ):
  573. echo "ok"
  574. ## Type section
  575. scope:
  576. let ast = myquote:
  577. type A = int
  578. ast.matchAst:
  579. of nnkTypeSection(
  580. nnkTypeDef(
  581. ident"A",
  582. nnkEmpty(),
  583. ident"int"
  584. )
  585. ):
  586. echo "ok"
  587. scope:
  588. let ast = myquote:
  589. type MyInt = distinct int
  590. ast.peelOff({nnkTypeSection}).matchAst:
  591. of# ...
  592. nnkTypeDef(
  593. ident"MyInt",
  594. nnkEmpty(),
  595. nnkDistinctTy(
  596. ident"int"
  597. )
  598. ):
  599. echo "ok"
  600. scope:
  601. let ast = myquote:
  602. type A[T] = expr1
  603. ast.matchAst:
  604. of nnkTypeSection(
  605. nnkTypeDef(
  606. ident"A",
  607. nnkGenericParams(
  608. nnkIdentDefs(
  609. ident"T",
  610. nnkEmpty(), # if the type is declared with options, like
  611. # ``[T: SomeInteger]``, they are given here
  612. nnkEmpty()
  613. )
  614. ),
  615. `expr1`
  616. )
  617. ):
  618. echo "ok"
  619. scope:
  620. let ast = myquote:
  621. type IO = object of RootObj
  622. ast.peelOff(nnkTypeSection).matchAst:
  623. of nnkTypeDef(
  624. ident"IO",
  625. nnkEmpty(),
  626. nnkObjectTy(
  627. nnkEmpty(), # no pragmas here
  628. nnkOfInherit(
  629. ident"RootObj" # inherits from RootObj
  630. ),
  631. nnkEmpty()
  632. )
  633. ):
  634. echo "ok"
  635. scope:
  636. macro testRecCase(ast: untyped): untyped =
  637. ast.peelOff({nnkStmtList, nnkTypeSection})[2].matchAst:
  638. of nnkObjectTy(
  639. nnkPragma(
  640. ident"inheritable"
  641. ),
  642. nnkEmpty(),
  643. nnkRecList( # list of object parameters
  644. nnkIdentDefs(
  645. ident"name",
  646. ident"string",
  647. nnkEmpty()
  648. ),
  649. nnkRecCase( # case statement within object (not nnkCaseStmt)
  650. nnkIdentDefs(
  651. ident"isFat",
  652. ident"bool",
  653. nnkEmpty()
  654. ),
  655. nnkOfBranch(
  656. ident"true",
  657. nnkRecList( # again, a list of object parameters
  658. nnkIdentDefs(
  659. ident"m",
  660. nnkBracketExpr(
  661. ident"array",
  662. nnkIntLit(intVal = 100000),
  663. ident"T"
  664. ),
  665. nnkEmpty()
  666. )
  667. )
  668. ),
  669. nnkOfBranch(
  670. ident"false",
  671. nnkRecList(
  672. nnkIdentDefs(
  673. ident"m",
  674. nnkBracketExpr(
  675. ident"array",
  676. nnkIntLit(intVal = 10),
  677. ident"T"
  678. ),
  679. nnkEmpty()
  680. )
  681. )
  682. )
  683. )
  684. )
  685. ):
  686. echo "ok"
  687. testRecCase:
  688. type Obj[T] = object {.inheritable.}
  689. name: string
  690. case isFat: bool
  691. of true:
  692. m: array[100_000, T]
  693. of false:
  694. m: array[10, T]
  695. scope:
  696. let ast = myquote:
  697. type X = enum
  698. First
  699. ast.peelOff({nnkStmtList, nnkTypeSection})[2].matchAst:
  700. of nnkEnumTy(
  701. nnkEmpty(),
  702. ident"First" # you need at least one nnkIdent or the compiler complains
  703. ):
  704. echo "ok"
  705. scope:
  706. let ast = myquote:
  707. type Con = concept x,y,z
  708. (x & y & z) is string
  709. ast.peelOff({nnkStmtList, nnkTypeSection}).matchAst:
  710. of nnkTypeDef(_, _, nnkTypeClassTy(nnkArgList, _, _, nnkStmtList)):
  711. # note this isn't nnkConceptTy!
  712. echo "ok"
  713. scope:
  714. let astX = myquote:
  715. type
  716. A[T: static[int]] = object
  717. let ast = astX.peelOff({nnkStmtList, nnkTypeSection})
  718. ast.matchAst(err): # this is a sub ast for this a findAst or something like that is useful
  719. of nnkTypeDef(_, nnkGenericParams( nnkIdentDefs( ident"T", nnkCall( ident"[]", ident"static", _ ), _ )), _):
  720. echo "ok"
  721. else:
  722. echo "foobar"
  723. echo ast.treeRepr
  724. scope:
  725. let ast = myquote:
  726. type MyProc[T] = proc(x: T)
  727. ast.peelOff({nnkStmtList, nnkTypeSection}).matchAst(err):
  728. of nnkTypeDef(
  729. ident"MyProc",
  730. nnkGenericParams, # here, not with the proc
  731. nnkProcTy( # behaves like a procedure declaration from here on
  732. nnkFormalParams, _
  733. )
  734. ):
  735. echo "ok"
  736. ## Mixin statement
  737. macro testMixinStatement(ast: untyped): untyped =
  738. ast.peelOff(nnkStmtList).matchAst:
  739. of nnkMixinStmt(ident"x"):
  740. echo "ok"
  741. testMixinStatement:
  742. mixin x
  743. ## Bind statement
  744. macro testBindStmt(ast: untyped): untyped =
  745. ast[0].matchAst:
  746. of `node` @ nnkBindStmt(ident"x"):
  747. echo "ok"
  748. testBindStmt:
  749. bind x
  750. ## Procedure declaration
  751. macro testProcedureDeclaration(ast: untyped): untyped =
  752. # NOTE this is wrong in astdef
  753. ast.peelOff(nnkStmtList).matchAst:
  754. of nnkProcDef(
  755. nnkPostfix(ident"*", ident"hello"), # the exported proc name
  756. nnkEmpty, # patterns for term rewriting in templates and macros (not procs)
  757. nnkGenericParams( # generic type parameters, like with type declaration
  758. nnkIdentDefs(
  759. ident"T",
  760. ident"SomeInteger", _
  761. )
  762. ),
  763. nnkFormalParams(
  764. ident"int", # the first FormalParam is the return type. nnkEmpty if there is none
  765. nnkIdentDefs(
  766. ident"x",
  767. ident"int", # type type (required for procs, not for templates)
  768. nnkIntLit(intVal = 3) # a default value
  769. ),
  770. nnkIdentDefs(
  771. ident"y",
  772. ident"float32",
  773. nnkEmpty
  774. )
  775. ),
  776. nnkPragma(ident"inline"),
  777. nnkEmpty, # reserved slot for future use
  778. `meat` @ nnkStmtList # the meat of the proc
  779. ):
  780. echo "ok got meat: ", meat.lispRepr
  781. testProcedureDeclaration:
  782. proc hello*[T: SomeInteger](x: int = 3, y: float32): int {.inline.} = discard
  783. scope:
  784. var ast = myquote:
  785. proc foobar(a, b: int): void
  786. ast = ast[3]
  787. ast.matchAst: # sub expression
  788. of nnkFormalParams(
  789. _, # return would be here
  790. nnkIdentDefs(
  791. ident"a", # the first parameter
  792. ident"b", # directly to the second parameter
  793. ident"int", # their shared type identifier
  794. nnkEmpty, # default value would go here
  795. )
  796. ):
  797. echo "ok"
  798. scope:
  799. let ast = myquote:
  800. proc hello(): var int
  801. ast[3].matchAst: # subAst
  802. of nnkFormalParams(
  803. nnkVarTy(
  804. ident"int"
  805. )
  806. ):
  807. echo "ok"
  808. ## Iterator declaration
  809. scope:
  810. let ast = myquote:
  811. iterator nonsense[T](x: seq[T]): float {.closure.} =
  812. discard
  813. ast.matchAst:
  814. of nnkIteratorDef(ident"nonsense", nnkEmpty, _, _, _, _, _):
  815. echo "ok"
  816. ## Converter declaration
  817. scope:
  818. let ast = myquote:
  819. converter toBool(x: float): bool
  820. ast.matchAst:
  821. of nnkConverterDef(ident"toBool",_,_,_,_,_,_):
  822. echo "ok"
  823. ## Template declaration
  824. scope:
  825. let ast = myquote:
  826. template optOpt{expr1}(a: int): int
  827. ast.matchAst:
  828. of nnkTemplateDef(ident"optOpt", nnkStmtList(`expr1`), _, _, _, _, _):
  829. echo "ok"