summary refs log tree commit diff stats
path: root/tests/astspec
diff options
context:
space:
mode:
Diffstat (limited to 'tests/astspec')
-rw-r--r--tests/astspec/tastspec.nim1119
1 files changed, 1119 insertions, 0 deletions
diff --git a/tests/astspec/tastspec.nim b/tests/astspec/tastspec.nim
new file mode 100644
index 000000000..c99d8ec79
--- /dev/null
+++ b/tests/astspec/tastspec.nim
@@ -0,0 +1,1119 @@
+discard """
+action: compile
+"""
+
+# this test should ensure that the AST doesn't change slightly without it getting noticed.
+
+import ../ast_pattern_matching
+
+template expectNimNode(arg: untyped): NimNode = arg
+  ## This template here is just to be injected by `myquote`, so that
+  ## a nice error message appears when the captured symbols are not of
+  ## type `NimNode`.
+
+proc substitudeComments(symbols, values, n: NimNode): NimNode =
+  ## substitutes all nodes of kind nnkCommentStmt to parameter
+  ## symbols. Consumes the argument `n`.
+  if n.kind == nnkCommentStmt:
+    values.add newCall(bindSym"newCommentStmtNode", newLit(n.strVal))
+    # Gensym doesn't work for parameters. These identifiers won't
+    # clash unless an argument is constructed to clash here.
+    symbols.add ident("comment" & $values.len & "_XObBdOnh6meCuJK2smZV")
+    return symbols[^1]
+  for i in 0 ..< n.len:
+    n[i] = substitudeComments(symbols, values, n[i])
+  return n
+
+macro myquote*(args: varargs[untyped]): untyped =
+  expectMinLen(args, 1)
+
+  # This is a workaround for #10430 where comments are removed in
+  # template expansions. This workaround lifts all comments
+  # statements to be arguments of the temporary template.
+
+  let extraCommentSymbols = newNimNode(nnkBracket)
+  let extraCommentGenExpr = newNimNode(nnkBracket)
+  let body = substitudeComments(
+    extraCommentSymbols, extraCommentGenExpr, args[^1]
+  )
+
+  let formalParams = nnkFormalParams.newTree(ident"untyped")
+  for i in 0 ..< args.len-1:
+    formalParams.add nnkIdentDefs.newTree(
+      args[i], ident"untyped", newEmptyNode()
+    )
+  for sym in extraCommentSymbols:
+    formalParams.add nnkIdentDefs.newTree(
+      sym, ident"untyped", newEmptyNode()
+    )
+
+  let templateSym = genSym(nskTemplate)
+  let templateDef = nnkTemplateDef.newTree(
+    templateSym,
+    newEmptyNode(),
+    newEmptyNode(),
+    formalParams,
+    nnkPragma.newTree(ident"dirty"),
+    newEmptyNode(),
+    args[^1]
+  )
+
+  let templateCall = newCall(templateSym)
+  for i in 0 ..< args.len-1:
+    let symName = args[i]
+    # identifiers and quoted identifiers are allowed.
+    if symName.kind == nnkAccQuoted:
+      symName.expectLen 1
+      symName[0].expectKind nnkIdent
+    else:
+      symName.expectKind nnkIdent
+    templateCall.add newCall(bindSym"expectNimNode", symName)
+  for expr in extraCommentGenExpr:
+    templateCall.add expr
+  let getAstCall = newCall(bindSym"getAst", templateCall)
+  result = newStmtList(templateDef, getAstCall)
+
+
+macro testAddrAst(arg: typed): bool =
+  arg.expectKind nnkStmtListExpr
+  arg[0].expectKind(nnkVarSection)
+  arg[1].expectKind({nnkAddr, nnkCall})
+  result = newLit(arg[1].kind == nnkCall)
+
+const newAddrAst: bool = testAddrAst((var x: int; addr(x)))
+
+static:
+  echo "new addr ast: ", newAddrAst
+
+# TODO test on matching failures
+
+proc peelOff*(arg: NimNode, kinds: set[NimNodeKind]): NimNode {.compileTime.} =
+  ## Peel off  nodes of a specific kinds.
+  if arg.len == 1 and arg.kind in kinds:
+    arg[0].peelOff(kinds)
+  else:
+    arg
+
+proc peelOff*(arg: NimNode, kind: NimNodeKind): NimNode {.compileTime.} =
+  ## Peel off nodes of a specific kind.
+  if arg.len == 1 and arg.kind == kind:
+    arg[0].peelOff(kind)
+  else:
+    arg
+
+static:
+  template testPattern(pattern, astArg: untyped): untyped =
+    let ast = quote do: `astArg`
+    ast.matchAst:
+    of `pattern`:
+      echo "ok"
+
+  template testPatternFail(pattern, astArg: untyped): untyped =
+    let ast = quote do: `astArg`
+    ast.matchAst:
+    of `pattern`:
+      error("this should not match", ast)
+    else:
+      echo "OK"
+
+
+  testPattern nnkIntLit(intVal = 42), 42
+  testPattern nnkInt8Lit(intVal = 42), 42'i8
+  testPattern nnkInt16Lit(intVal = 42), 42'i16
+  testPattern nnkInt32Lit(intVal = 42), 42'i32
+  testPattern nnkInt64Lit(intVal = 42), 42'i64
+  testPattern nnkUInt8Lit(intVal = 42), 42'u8
+  testPattern nnkUInt16Lit(intVal = 42), 42'u16
+  testPattern nnkUInt32Lit(intVal = 42), 42'u32
+  testPattern nnkUInt64Lit(intVal = 42), 42'u64
+  #testPattern nnkFloat64Lit(floatVal = 42.0), 42.0
+  testPattern nnkFloat32Lit(floatVal = 42.0), 42.0'f32
+  #testPattern nnkFloat64Lit(floatVal = 42.0), 42.0'f64
+  testPattern nnkStrLit(strVal = "abc"), "abc"
+  testPattern nnkRStrLit(strVal = "abc"), r"abc"
+  testPattern nnkTripleStrLit(strVal = "abc"), """abc"""
+  testPattern nnkCharLit(intVal = 32), ' '
+  testPattern nnkNilLit(), nil
+  testPattern nnkIdent(strVal = "myIdentifier"), myIdentifier
+
+  testPatternFail nnkInt8Lit(intVal = 42), 42'i16
+  testPatternFail nnkInt16Lit(intVal = 42), 42'i8
+
+
+
+# this should be just `block` but it doesn't work that way anymore because of VM.
+macro scope(arg: untyped): untyped =
+  let procSym = genSym(nskProc)
+  result = quote do:
+    proc `procSym`() {.compileTime.} =
+      `arg`
+
+    `procSym`()
+
+static:
+  ## Command call
+  scope:
+
+    let ast = myquote:
+      echo "abc", "xyz"
+
+    ast.matchAst:
+    of nnkCommand(ident"echo", "abc", "xyz"):
+      echo "ok"
+
+  ## Call with ``()``
+
+  scope:
+    let ast = myquote:
+      echo("abc", "xyz")
+
+    ast.matchAst:
+    of nnkCall(ident"echo", "abc", "xyz"):
+      echo "ok"
+
+  ## Infix operator call
+
+  macro testInfixOperatorCall(ast: untyped): untyped =
+    ast.matchAst(errorSym):
+    of nnkInfix(
+      ident"&",
+      nnkStrLit(strVal = "abc"),
+      nnkStrLit(strVal = "xyz")
+    ):
+      echo "ok1"
+    of nnkInfix(
+      ident"+",
+      nnkIntLit(intVal = 5),
+      nnkInfix(
+        ident"*",
+        nnkIntLit(intVal = 3),
+        nnkIntLit(intVal = 4)
+      )
+    ):
+      echo "ok2"
+    of nnkCall(
+      nnkAccQuoted(
+        ident"+"
+      ),
+      nnkIntLit(intVal = 3),
+      nnkIntLit(intVal = 4)
+    ):
+      echo "ok3"
+
+  testInfixOperatorCall("abc" & "xyz")
+  testInfixOperatorCall(5 + 3 * 4)
+  testInfixOperatorCall(`+`(3, 4))
+
+
+  ## Prefix operator call
+
+  scope:
+
+    let ast = myquote:
+      ? "xyz"
+
+    ast.matchAst(err):
+    of nnkPrefix(
+      ident"?",
+      nnkStrLit(strVal = "xyz")
+    ):
+      echo "ok"
+
+
+  ## Postfix operator call
+
+  scope:
+
+    let ast = myquote:
+      proc identifier*
+
+    ast[0].matchAst(err):
+    of nnkPostfix(
+      ident"*",
+      ident"identifier"
+    ):
+      echo "ok"
+
+
+  ## Call with named arguments
+
+  macro testCallWithNamedArguments(ast: untyped): untyped =
+    ast.peelOff(nnkStmtList).matchAst:
+    of nnkCall(
+      ident"writeLine",
+      nnkExprEqExpr(
+        ident"file",
+        ident"stdout"
+      ),
+      nnkStrLit(strVal = "hallo")
+    ):
+      echo "ok"
+
+  testCallWithNamedArguments:
+    writeLine(file=stdout, "hallo")
+
+  ## Call with raw string literal
+  scope:
+    let ast = myquote:
+      echo"abc"
+
+
+    ast.matchAst(err):
+    of nnkCallStrLit(
+      ident"echo",
+      nnkRStrLit(strVal = "abc")
+    ):
+      echo "ok"
+
+  ## Dereference operator ``[]``
+
+  scope:
+    # The dereferece operator exists only on a typed ast.
+    macro testDereferenceOperator(ast: typed): untyped =
+      ast.matchAst(err):
+      of nnkDerefExpr(_):
+        echo "ok"
+
+    var x: ptr int
+    testDereferenceOperator(x[])
+
+
+
+  ## Addr operator
+
+  scope:
+    # The addr operator exists only on a typed ast.
+    macro testAddrOperator(ast: untyped): untyped =
+      echo ast.treeRepr
+      ast.matchAst(err):
+      of nnkAddr(ident"x"):
+        echo "old nim"
+      of nnkCall(ident"addr", ident"x"):
+        echo "ok"
+
+    var x: int
+    testAddrOperator(addr(x))
+
+
+  ## Cast operator
+
+  scope:
+
+    let ast = myquote:
+      cast[T](x)
+
+    ast.matchAst:
+    of nnkCast(ident"T", ident"x"):
+      echo "ok"
+
+
+  ## Object access operator ``.``
+
+  scope:
+
+    let ast = myquote:
+      x.y
+
+    ast.matchAst:
+    of nnkDotExpr(ident"x", ident"y"):
+      echo "ok"
+
+  ## Array access operator ``[]``
+
+  macro testArrayAccessOperator(ast: untyped): untyped =
+    ast.matchAst:
+    of nnkBracketExpr(ident"x", ident"y"):
+      echo "ok"
+
+  testArrayAccessOperator(x[y])
+
+  ## Parentheses
+
+  scope:
+    let ast = myquote:
+      (a + b) * c
+
+    ast.matchAst:
+    of nnkInfix(ident"*", nnkPar(nnkInfix(ident"+", ident"a", ident"b")), ident"c"):
+      echo "parentheses ok"
+
+  ## Tuple Constructors
+
+  scope:
+    let ast = myquote:
+      (1, 2, 3)
+      (a: 1, b: 2, c: 3)
+      (1,)
+      (a: 1)
+      ()
+
+    for it in ast:
+      echo it.lispRepr
+      it.matchAst:
+      of nnkTupleConstr(nnkIntLit(intVal = 1), nnkIntLit(intVal = 2), nnkIntLit(intVal = 3)):
+        echo "simple tuple ok"
+      of nnkTupleConstr(
+        nnkExprColonExpr(ident"a", nnkIntLit(intVal = 1)),
+        nnkExprColonExpr(ident"b", nnkIntLit(intVal = 2)),
+        nnkExprColonExpr(ident"c", nnkIntLit(intVal = 3))
+      ):
+        echo "named tuple ok"
+      of nnkTupleConstr(nnkIntLit(intVal = 1)):
+        echo "one tuple ok"
+      of nnkTupleConstr(nnkExprColonExpr(ident"a", nnkIntLit(intVal = 1))):
+        echo "named one tuple ok"
+      of nnkTupleConstr():
+       echo "empty tuple ok"
+
+  ## Curly braces
+
+  scope:
+
+    let ast = myquote:
+      {1, 2, 3}
+
+    ast.matchAst:
+    of nnkCurly(nnkIntLit(intVal = 1), nnkIntLit(intVal = 2), nnkIntLit(intVal = 3)):
+      echo "ok"
+
+  scope:
+
+    let ast = myquote:
+      {a: 3, b: 5}
+
+    ast.matchAst:
+    of nnkTableConstr(
+      nnkExprColonExpr(ident"a", nnkIntLit(intVal = 3)),
+      nnkExprColonExpr(ident"b", nnkIntLit(intVal = 5))
+    ):
+      echo "ok"
+
+
+  ## Brackets
+
+  scope:
+
+    let ast = myquote:
+      [1, 2, 3]
+
+    ast.matchAst:
+    of nnkBracket(nnkIntLit(intVal = 1), nnkIntLit(intVal = 2), nnkIntLit(intVal = 3)):
+      echo "ok"
+
+
+  ## Ranges
+
+  scope:
+
+    let ast = myquote:
+      1..3
+
+    ast.matchAst:
+    of nnkInfix(
+      ident"..",
+      nnkIntLit(intVal = 1),
+      nnkIntLit(intVal = 3)
+    ):
+      echo "ok"
+
+
+  ## If expression
+
+  scope:
+
+    let ast = myquote:
+      if cond1: expr1 elif cond2: expr2 else: expr3
+
+    ast.matchAst:
+    of {nnkIfExpr, nnkIfStmt}(
+      {nnkElifExpr, nnkElifBranch}(`cond1`, `expr1`),
+      {nnkElifExpr, nnkElifBranch}(`cond2`, `expr2`),
+      {nnkElseExpr, nnkElse}(`expr3`)
+    ):
+      echo "ok"
+
+  ## Documentation Comments
+
+  scope:
+
+    let ast = myquote:
+      ## This is a comment
+      ## This is part of the first comment
+      stmt1
+      ## Yet another
+
+    ast.matchAst:
+    of nnkStmtList(
+      nnkCommentStmt(),
+      `stmt1`,
+      nnkCommentStmt()
+    ):
+      echo "ok"
+    else:
+      echo "warning!"
+      echo ast.treeRepr
+      echo "TEST causes no fail, because of a regression in Nim."
+
+  scope:
+    let ast = myquote:
+      {.emit: "#include <stdio.h>".}
+
+    ast.matchAst:
+    of nnkPragma(
+      nnkExprColonExpr(
+        ident"emit",
+        nnkStrLit(strVal = "#include <stdio.h>") # the "argument"
+      )
+    ):
+      echo "ok"
+
+  scope:
+    let ast = myquote:
+      {.pragma: cdeclRename, cdecl.}
+
+    ast.matchAst:
+    of nnkPragma(
+      nnkExprColonExpr(
+        ident"pragma", # this is always first when declaring a new pragma
+        ident"cdeclRename" # the name of the pragma
+      ),
+      ident"cdecl"
+    ):
+      echo "ok"
+
+
+
+  scope:
+    let ast = myquote:
+      if cond1:
+        stmt1
+      elif cond2:
+        stmt2
+      elif cond3:
+        stmt3
+      else:
+        stmt4
+
+    ast.matchAst:
+    of nnkIfStmt(
+      nnkElifBranch(`cond1`, `stmt1`),
+      nnkElifBranch(`cond2`, `stmt2`),
+      nnkElifBranch(`cond3`, `stmt3`),
+      nnkElse(`stmt4`)
+    ):
+      echo "ok"
+
+
+
+  scope:
+    let ast = myquote:
+      x = 42
+
+    ast.matchAst:
+    of nnkAsgn(ident"x", nnkIntLit(intVal = 42)):
+      echo "ok"
+
+
+
+  scope:
+    let ast = myquote:
+      stmt1
+      stmt2
+      stmt3
+
+    ast.matchAst:
+    of nnkStmtList(`stmt1`, `stmt2`, `stmt3`):
+      assert stmt1.strVal == "stmt1"
+      assert stmt2.strVal == "stmt2"
+      assert stmt3.strVal == "stmt3"
+      echo "ok"
+
+  ## Case statement
+
+  scope:
+
+    let ast = myquote:
+      case expr1
+      of expr2, expr3..expr4:
+        stmt1
+      of expr5:
+        stmt2
+      elif cond1:
+        stmt3
+      else:
+        stmt4
+
+    ast.matchAst:
+    of nnkCaseStmt(
+      `expr1`,
+      nnkOfBranch(`expr2`, {nnkRange, nnkInfix}(_, `expr3`, `expr4`), `stmt1`),
+      nnkOfBranch(`expr5`, `stmt2`),
+      nnkElifBranch(`cond1`, `stmt3`),
+      nnkElse(`stmt4`)
+    ):
+      echo "ok"
+
+  ## While statement
+
+  scope:
+
+    let ast = myquote:
+      while expr1:
+        stmt1
+
+    ast.matchAst:
+    of nnkWhileStmt(`expr1`, `stmt1`):
+      echo "ok"
+
+
+  ## For statement
+
+  scope:
+
+    let ast = myquote:
+      for ident1, ident2 in expr1:
+        stmt1
+
+    ast.matchAst:
+    of nnkForStmt(`ident1`, `ident2`, `expr1`, `stmt1`):
+      echo "ok"
+
+
+  ## Try statement
+
+  scope:
+
+    let ast = myquote:
+      try:
+        stmt1
+      except e1, e2:
+        stmt2
+      except e3:
+        stmt3
+      except:
+        stmt4
+      finally:
+        stmt5
+
+    ast.matchAst:
+    of nnkTryStmt(
+      `stmt1`,
+      nnkExceptBranch(`e1`, `e2`, `stmt2`),
+      nnkExceptBranch(`e3`, `stmt3`),
+      nnkExceptBranch(`stmt4`),
+      nnkFinally(`stmt5`)
+    ):
+      echo "ok"
+
+
+  ## Return statement
+
+  scope:
+
+    let ast = myquote:
+      return expr1
+
+    ast.matchAst:
+    of nnkReturnStmt(`expr1`):
+      echo "ok"
+
+
+  ## Continue statement
+
+  scope:
+    let ast = myquote:
+      continue
+
+    ast.matchAst:
+    of nnkContinueStmt:
+      echo "ok"
+
+  ## Break statement
+
+  scope:
+
+    let ast = myquote:
+      break otherLocation
+
+    ast.matchAst:
+    of nnkBreakStmt(ident"otherLocation"):
+      echo "ok"
+
+  ## Block statement
+
+  scope:
+
+    template blockStatement {.dirty.} =
+      block name:
+        discard
+
+    let ast = getAst(blockStatement())
+
+    ast.matchAst:
+    of nnkBlockStmt(ident"name", nnkStmtList):
+      echo "ok"
+
+  ## Asm statement
+
+  scope:
+
+    let ast = myquote:
+      asm """some asm"""
+
+    ast.matchAst:
+    of nnkAsmStmt(
+      nnkEmpty(), # for pragmas
+      nnkTripleStrLit(strVal = "some asm"),
+    ):
+      echo "ok"
+
+  ## Import section
+
+  scope:
+
+    let ast = myquote:
+      import math
+
+    ast.matchAst:
+    of nnkImportStmt(ident"math"):
+      echo "ok"
+
+  scope:
+
+    let ast = myquote:
+      import math except pow
+
+    ast.matchAst:
+    of nnkImportExceptStmt(ident"math",ident"pow"):
+      echo "ok"
+
+  scope:
+
+    let ast = myquote:
+      import strutils as su
+
+    ast.matchAst:
+    of nnkImportStmt(
+      nnkInfix(
+        ident"as",
+        ident"strutils",
+        ident"su"
+      )
+    ):
+      echo "ok"
+
+  ## From statement
+
+  scope:
+
+    let ast = myquote:
+      from math import pow
+
+    ast.matchAst:
+    of nnkFromStmt(ident"math", ident"pow"):
+      echo "ok"
+
+  ## Export statement
+
+  scope:
+
+    let ast = myquote:
+      export unsigned
+
+    ast.matchAst:
+    of nnkExportStmt(ident"unsigned"):
+      echo "ok"
+
+  scope:
+
+    let ast = myquote:
+      export math except pow # we're going to implement our own exponentiation
+
+    ast.matchAst:
+    of nnkExportExceptStmt(ident"math",ident"pow"):
+      echo "ok"
+
+  ## Include statement
+
+  scope:
+
+    let ast = myquote:
+      include blocks
+
+    ast.matchAst:
+    of nnkIncludeStmt(ident"blocks"):
+      echo "ok"
+
+  ## Var section
+
+  scope:
+
+    let ast = myquote:
+      var a = 3
+
+    ast.matchAst:
+    of nnkVarSection(
+      nnkIdentDefs(
+        ident"a",
+        nnkEmpty(), # or nnkIdent(...) if the variable declares the type
+        nnkIntLit(intVal = 3),
+      )
+    ):
+      echo "ok"
+
+  ## Let section
+
+  scope:
+
+    let ast = myquote:
+      let a = 3
+
+    ast.matchAst:
+    of nnkLetSection(
+      nnkIdentDefs(
+        ident"a",
+        nnkEmpty(), # or nnkIdent(...) for the type
+        nnkIntLit(intVal = 3),
+      )
+    ):
+      echo "ok"
+
+  ## Const section
+
+  scope:
+
+    let ast = myquote:
+      const a = 3
+
+    ast.matchAst:
+    of nnkConstSection(
+      nnkConstDef( # not nnkConstDefs!
+        ident"a",
+        nnkEmpty(), # or nnkIdent(...) if the variable declares the type
+        nnkIntLit(intVal = 3), # required in a const declaration!
+      )
+    ):
+      echo "ok"
+
+  ## Type section
+
+  scope:
+
+    let ast = myquote:
+      type A = int
+
+    ast.matchAst:
+    of nnkTypeSection(
+      nnkTypeDef(
+        ident"A",
+        nnkEmpty(),
+        ident"int"
+      )
+    ):
+      echo "ok"
+
+  scope:
+
+    let ast = myquote:
+      type MyInt = distinct int
+
+    ast.peelOff({nnkTypeSection}).matchAst:
+    of# ...
+      nnkTypeDef(
+      ident"MyInt",
+      nnkEmpty(),
+      nnkDistinctTy(
+        ident"int"
+      )
+    ):
+      echo "ok"
+
+  scope:
+
+    let ast = myquote:
+      type A[T] = expr1
+
+    ast.matchAst:
+    of nnkTypeSection(
+      nnkTypeDef(
+        ident"A",
+        nnkGenericParams(
+          nnkIdentDefs(
+            ident"T",
+            nnkEmpty(), # if the type is declared with options, like
+                        # ``[T: SomeInteger]``, they are given here
+            nnkEmpty()
+          )
+        ),
+        `expr1`
+      )
+    ):
+      echo "ok"
+
+  scope:
+
+    let ast = myquote:
+      type IO = object of RootObj
+
+    ast.peelOff(nnkTypeSection).matchAst:
+    of nnkTypeDef(
+      ident"IO",
+      nnkEmpty(),
+      nnkObjectTy(
+        nnkEmpty(), # no pragmas here
+        nnkOfInherit(
+          ident"RootObj" # inherits from RootObj
+        ),
+        nnkEmpty()
+      )
+    ):
+      echo "ok"
+
+  scope:
+    macro testRecCase(ast: untyped): untyped =
+      ast.peelOff({nnkStmtList, nnkTypeSection}).matchAst:
+      of nnkTypeDef(
+        nnkPragmaExpr(
+          ident"Obj",
+          nnkPragma(ident"inheritable")
+        ),
+        nnkGenericParams(
+        nnkIdentDefs(
+          ident"T",
+          nnkEmpty(),
+          nnkEmpty())
+        ),
+        nnkObjectTy(
+        nnkEmpty(),
+        nnkEmpty(),
+        nnkRecList( # list of object parameters
+          nnkIdentDefs(
+            ident"name",
+            ident"string",
+            nnkEmpty()
+          ),
+          nnkRecCase( # case statement within object (not nnkCaseStmt)
+            nnkIdentDefs(
+              ident"isFat",
+              ident"bool",
+              nnkEmpty()
+            ),
+            nnkOfBranch(
+              ident"true",
+              nnkRecList( # again, a list of object parameters
+                nnkIdentDefs(
+                  ident"m",
+                  nnkBracketExpr(
+                    ident"array",
+                    nnkIntLit(intVal = 100000),
+                    ident"T"
+                  ),
+                  nnkEmpty()
+                )
+              )
+            ),
+            nnkOfBranch(
+              ident"false",
+              nnkRecList(
+                nnkIdentDefs(
+                  ident"m",
+                  nnkBracketExpr(
+                    ident"array",
+                    nnkIntLit(intVal = 10),
+                    ident"T"
+                  ),
+                  nnkEmpty()
+                  )
+                )
+              )
+            )
+          )
+        )
+      ):
+        echo "ok"
+
+    testRecCase:
+      type Obj[T] {.inheritable.} = object
+        name: string
+        case isFat: bool
+        of true:
+          m: array[100_000, T]
+        of false:
+          m: array[10, T]
+
+  scope:
+
+    let ast = myquote:
+      type X = enum
+        First
+
+    ast.peelOff({nnkStmtList, nnkTypeSection})[2].matchAst:
+    of nnkEnumTy(
+      nnkEmpty(),
+      ident"First" # you need at least one nnkIdent or the compiler complains
+    ):
+      echo "ok"
+
+  scope:
+
+    let ast = myquote:
+      type Con = concept x,y,z
+        (x & y & z) is string
+
+    ast.peelOff({nnkStmtList, nnkTypeSection}).matchAst:
+    of nnkTypeDef(_, _, nnkTypeClassTy(nnkArgList, _, _, nnkStmtList)):
+      # note this isn't nnkConceptTy!
+      echo "ok"
+
+
+  scope:
+
+    let astX = myquote:
+      type
+        A[T: static[int]] = object
+
+    let ast = astX.peelOff({nnkStmtList, nnkTypeSection})
+
+    ast.matchAst(err):  # this is a sub ast for this a findAst or something like that is useful
+    of nnkTypeDef(_, nnkGenericParams( nnkIdentDefs( ident"T", nnkCall( ident"[]", ident"static", _ ), _ )), _):
+      echo "ok"
+    else:
+      echo "foobar"
+      echo ast.treeRepr
+
+
+  scope:
+    let ast = myquote:
+      type MyProc[T] = proc(x: T)
+
+    ast.peelOff({nnkStmtList, nnkTypeSection}).matchAst(err):
+    of nnkTypeDef(
+      ident"MyProc",
+      nnkGenericParams, # here, not with the proc
+      nnkProcTy( # behaves like a procedure declaration from here on
+        nnkFormalParams, _
+      )
+    ):
+      echo "ok"
+
+  ## Mixin statement
+
+  macro testMixinStatement(ast: untyped): untyped =
+    ast.peelOff(nnkStmtList).matchAst:
+    of nnkMixinStmt(ident"x"):
+      echo "ok"
+
+  testMixinStatement:
+    mixin x
+
+  ## Bind statement
+
+
+  macro testBindStmt(ast: untyped): untyped =
+    ast[0].matchAst:
+    of `node` @ nnkBindStmt(ident"x"):
+      echo "ok"
+
+  testBindStmt:
+    bind x
+
+  ## Procedure declaration
+
+  macro testProcedureDeclaration(ast: untyped): untyped =
+    # NOTE this is wrong in astdef
+
+    ast.peelOff(nnkStmtList).matchAst:
+    of nnkProcDef(
+      nnkPostfix(ident"*", ident"hello"), # the exported proc name
+      nnkEmpty, # patterns for term rewriting in templates and macros (not procs)
+      nnkGenericParams( # generic type parameters, like with type declaration
+        nnkIdentDefs(
+          ident"T",
+          ident"SomeInteger", _
+        )
+      ),
+      nnkFormalParams(
+        ident"int", # the first FormalParam is the return type. nnkEmpty if there is none
+        nnkIdentDefs(
+          ident"x",
+          ident"int", # type type (required for procs, not for templates)
+          nnkIntLit(intVal = 3) # a default value
+        ),
+        nnkIdentDefs(
+          ident"y",
+          ident"float32",
+          nnkEmpty
+        )
+      ),
+      nnkPragma(ident"inline"),
+      nnkEmpty, # reserved slot for future use
+      `meat` @ nnkStmtList # the meat of the proc
+    ):
+      echo "ok got meat: ", meat.lispRepr
+
+  testProcedureDeclaration:
+    proc hello*[T: SomeInteger](x: int = 3, y: float32): int {.inline.} = discard
+
+  scope:
+    var ast = myquote:
+      proc foobar(a, b: int): void
+
+    ast = ast[3]
+
+    ast.matchAst:  # sub expression
+    of nnkFormalParams(
+      _, # return would be here
+      nnkIdentDefs(
+        ident"a", # the first parameter
+        ident"b", # directly to the second parameter
+        ident"int", # their shared type identifier
+        nnkEmpty, # default value would go here
+      )
+    ):
+      echo "ok"
+
+  scope:
+
+    let ast = myquote:
+      proc hello(): var int
+
+    ast[3].matchAst: # subAst
+    of nnkFormalParams(
+      nnkVarTy(
+        ident"int"
+      )
+    ):
+      echo "ok"
+
+  ## Iterator declaration
+
+  scope:
+
+    let ast = myquote:
+      iterator nonsense[T](x: seq[T]): float {.closure.} =
+        discard
+
+    ast.matchAst:
+    of nnkIteratorDef(ident"nonsense", nnkEmpty, _, _, _, _, _):
+      echo "ok"
+
+  ## Converter declaration
+
+  scope:
+
+    let ast = myquote:
+      converter toBool(x: float): bool
+
+    ast.matchAst:
+    of nnkConverterDef(ident"toBool",_,_,_,_,_,_):
+      echo "ok"
+
+  ## Template declaration
+
+  scope:
+    let ast = myquote:
+      template optOpt{expr1}(a: int): int
+
+    ast.matchAst:
+    of nnkTemplateDef(ident"optOpt", nnkStmtList(`expr1`), _, _, _, _, _):
+      echo "ok"