summary refs log tree commit diff stats
path: root/tests/macros
diff options
context:
space:
mode:
Diffstat (limited to 'tests/macros')
-rw-r--r--tests/macros/tdumpastgen.nim28
-rw-r--r--tests/macros/tforloop_macro1.nim44
-rw-r--r--tests/macros/tgensym.nim2
-rw-r--r--tests/macros/tmacro1.nim59
-rw-r--r--tests/macros/tstructuredlogging.nim154
-rw-r--r--tests/macros/ttemplatesymbols.nim173
-rw-r--r--tests/macros/twrapiterator.nim19
7 files changed, 475 insertions, 4 deletions
diff --git a/tests/macros/tdumpastgen.nim b/tests/macros/tdumpastgen.nim
index faed77225..0a1836886 100644
--- a/tests/macros/tdumpastgen.nim
+++ b/tests/macros/tdumpastgen.nim
@@ -2,16 +2,33 @@ discard """
 msg: '''nnkStmtList.newTree(
   nnkVarSection.newTree(
     nnkIdentDefs.newTree(
-      newIdentNode(!"x"),
+      newIdentNode("x"),
       newEmptyNode(),
       nnkCall.newTree(
         nnkDotExpr.newTree(
-          newIdentNode(!"foo"),
-          newIdentNode(!"create")
+          newIdentNode("baz"),
+          newIdentNode("create")
         ),
         newLit(56)
       )
     )
+  ),
+  nnkProcDef.newTree(
+    newIdentNode("foo"),
+    newEmptyNode(),
+    newEmptyNode(),
+    nnkFormalParams.newTree(
+      newEmptyNode()
+    ),
+    newEmptyNode(),
+    newEmptyNode(),
+    nnkStmtList.newTree(
+      newCommentStmtNode("This is a docstring"),
+      nnkCommand.newTree(
+        newIdentNode("echo"),
+        newLit("bar")
+      )
+    )
   )
 )'''
 """
@@ -21,5 +38,8 @@ msg: '''nnkStmtList.newTree(
 import macros
 
 dumpAstGen:
-  var x = foo.create(56)
+  var x = baz.create(56)
 
+  proc foo() =
+    ## This is a docstring
+    echo "bar"
diff --git a/tests/macros/tforloop_macro1.nim b/tests/macros/tforloop_macro1.nim
new file mode 100644
index 000000000..a8f45c7ac
--- /dev/null
+++ b/tests/macros/tforloop_macro1.nim
@@ -0,0 +1,44 @@
+discard """
+  output: '''0 1
+1 2
+2 3
+0 1
+1 2
+2 3
+0 1
+1 2
+2 3
+3 5'''
+"""
+
+import macros
+
+macro mymacro(): untyped =
+  result = newLit([1, 2, 3])
+
+for a, b in mymacro():
+  echo a, " ", b
+
+macro enumerate(x: ForLoopStmt): untyped =
+  expectKind x, nnkForStmt
+  # we strip off the first for loop variable and use
+  # it as an integer counter:
+  result = newStmtList()
+  result.add newVarStmt(x[0], newLit(0))
+  var body = x[^1]
+  if body.kind != nnkStmtList:
+    body = newTree(nnkStmtList, body)
+  body.add newCall(bindSym"inc", x[0])
+  var newFor = newTree(nnkForStmt)
+  for i in 1..x.len-3:
+    newFor.add x[i]
+  # transform enumerate(X) to 'X'
+  newFor.add x[^2][1]
+  newFor.add body
+  result.add newFor
+
+for a, b in enumerate(items([1, 2, 3])):
+  echo a, " ", b
+
+for a2, b2 in enumerate([1, 2, 3, 5]):
+  echo a2, " ", b2
diff --git a/tests/macros/tgensym.nim b/tests/macros/tgensym.nim
index 955168939..1237b8bf7 100644
--- a/tests/macros/tgensym.nim
+++ b/tests/macros/tgensym.nim
@@ -28,6 +28,7 @@ macro async2(prc: untyped): untyped =
   # -> iterator nameIter(): FutureBase {.closure.} = <proc_body>
   # Changing this line to: newIdentNode($prc[0].ident & "Iter") # will make it work.
   var iteratorNameSym = genSym(nskIterator, $prc[0].ident & "Iter")
+  assert iteratorNameSym.symKind == nskIterator
   #var iteratorNameSym = newIdentNode($prc[0].ident & "Iter")
   var procBody = prc[6].convertReturns(retFutureSym)
 
@@ -42,6 +43,7 @@ macro async2(prc: untyped): untyped =
   var varNameIter = newVarStmt(varNameIterSym, iteratorNameSym)
   outerProcBody.add varNameIter
   var varFirstSym = genSym(nskVar, "first")
+  assert varFirstSym.symKind ==  nskVar
   var varFirst = newVarStmt(varFirstSym, newCall(varNameIterSym))
   outerProcBody.add varFirst
 
diff --git a/tests/macros/tmacro1.nim b/tests/macros/tmacro1.nim
index ac6bd02a5..ac2bf9094 100644
--- a/tests/macros/tmacro1.nim
+++ b/tests/macros/tmacro1.nim
@@ -18,5 +18,64 @@ macro test*(a: untyped): untyped =
   t.b = true
   t.z = 4.5
 
+
 test:
   "hi"
+
+import strutils
+
+template assertNot(arg: untyped): untyped =
+  assert(not(arg))
+
+
+proc foo(arg: int): void =
+  discard
+
+proc foo(arg: float): void =
+  discard
+
+static:
+  ## test eqIdent
+  let a = "abc_def"
+  let b = "abcDef"
+  let c = "AbcDef"
+  let d = nnkBracketExpr.newTree() # not an identifier at all
+
+  assert eqIdent(             a ,              b )
+  assert eqIdent(newIdentNode(a),              b )
+  assert eqIdent(             a , newIdentNode(b))
+  assert eqIdent(newIdentNode(a), newIdentNode(b))
+
+  assert eqIdent(               a ,                b )
+  assert eqIdent(genSym(nskLet, a),                b )
+  assert eqIdent(               a , genSym(nskLet, b))
+  assert eqIdent(genSym(nskLet, a), genSym(nskLet, b))
+
+  assert eqIdent(newIdentNode(  a), newIdentNode(  b))
+  assert eqIdent(genSym(nskLet, a), newIdentNode(  b))
+  assert eqIdent(newIdentNode(  a), genSym(nskLet, b))
+  assert eqIdent(genSym(nskLet, a), genSym(nskLet, b))
+
+  assertNot eqIdent(             c ,              b )
+  assertNot eqIdent(newIdentNode(c),              b )
+  assertNot eqIdent(             c , newIdentNode(b))
+  assertNot eqIdent(newIdentNode(c), newIdentNode(b))
+
+  assertNot eqIdent(               c ,                b )
+  assertNot eqIdent(genSym(nskLet, c),                b )
+  assertNot eqIdent(               c , genSym(nskLet, b))
+  assertNot eqIdent(genSym(nskLet, c), genSym(nskLet, b))
+
+  assertNot eqIdent(newIdentNode(  c), newIdentNode(  b))
+  assertNot eqIdent(genSym(nskLet, c), newIdentNode(  b))
+  assertNot eqIdent(newIdentNode(  c), genSym(nskLet, b))
+  assertNot eqIdent(genSym(nskLet, c), genSym(nskLet, b))
+
+  # eqIdent on non identifier at all
+  assertNot eqIdent(a,d)
+
+  # eqIdent on sym choice
+  let fooSym = bindSym"foo"
+  assert fooSym.kind in {nnkOpenSymChoice, nnkClosedSymChoice}
+  assert    fooSym.eqIdent("fOO")
+  assertNot fooSym.eqIdent("bar")
diff --git a/tests/macros/tstructuredlogging.nim b/tests/macros/tstructuredlogging.nim
new file mode 100644
index 000000000..05bb52a40
--- /dev/null
+++ b/tests/macros/tstructuredlogging.nim
@@ -0,0 +1,154 @@
+discard """
+output: '''
+main started: a=10, b=inner-b, c=10, d=some-d, x=16, z=20
+exiting: a=12, b=overriden-b, c=100, msg=bye bye, x=16
+'''
+"""
+
+import macros, tables
+
+template scopeHolder =
+  0 # scope revision number
+
+type
+  BindingsSet = Table[string, NimNode]
+
+proc actualBody(n: NimNode): NimNode =
+  # skip over the double StmtList node introduced in `mergeScopes`
+  result = n.body
+  if result.kind == nnkStmtList and result[0].kind == nnkStmtList:
+    result = result[0]
+
+iterator bindings(n: NimNode, skip = 0): (string, NimNode) =
+  for i in skip ..< n.len:
+    let child = n[i]
+    if child.kind in {nnkAsgn, nnkExprEqExpr}:
+      let name = $child[0]
+      let value = child[1]
+      yield (name, value)
+
+proc scopeRevision(scopeHolder: NimNode): int =
+  # get the revision number from a scopeHolder sym
+  assert scopeHolder.kind == nnkSym
+  var revisionNode = scopeHolder.getImpl.actualBody[0]
+  result = int(revisionNode.intVal)
+
+proc lastScopeHolder(scopeHolders: NimNode): NimNode =
+  # get the most recent scopeHolder from a symChoice node
+  if scopeHolders.kind in {nnkClosedSymChoice, nnkOpenSymChoice}:
+    var bestScopeRev = 0
+    assert scopeHolders.len > 0
+    for scope in scopeHolders:
+      let rev = scope.scopeRevision
+      if result == nil or rev > bestScopeRev:
+        result = scope
+        bestScopeRev = rev
+  else:
+    result = scopeHolders
+
+  assert result.kind == nnkSym
+
+macro mergeScopes(scopeHolders: typed, newBindings: untyped): untyped =
+  var
+    bestScope = scopeHolders.lastScopeHolder
+    bestScopeRev = bestScope.scopeRevision
+
+  var finalBindings = initTable[string, NimNode]()
+  for k, v in bindings(bestScope.getImpl.actualBody, skip = 1):
+    finalBindings[k] = v
+
+  for k, v in bindings(newBindings):
+    finalBindings[k] = v
+
+  var newScopeDefinition = newStmtList(newLit(bestScopeRev + 1))
+
+  for k, v in finalBindings:
+    newScopeDefinition.add newAssignment(newIdentNode(k), v)
+
+  result = quote:
+    template scopeHolder = `newScopeDefinition`
+
+template scope(newBindings: untyped) {.dirty.} =
+  mergeScopes(bindSym"scopeHolder", newBindings)
+
+type
+  TextLogRecord = object
+    line: string
+
+  StdoutLogRecord = object
+
+template setProperty(r: var TextLogRecord, key: string, val: string, isFirst: bool) =
+  if not first: r.line.add ", "
+  r.line.add key
+  r.line.add "="
+  r.line.add val
+
+template setEventName(r: var StdoutLogRecord, name: string) =
+  stdout.write(name & ": ")
+
+template setProperty(r: var StdoutLogRecord, key: string, val: auto, isFirst: bool) =
+  when not isFirst: stdout.write ", "
+  stdout.write key
+  stdout.write "="
+  stdout.write $val
+
+template flushRecord(r: var StdoutLogRecord) =
+  stdout.write "\n"
+  stdout.flushFile
+
+macro logImpl(scopeHolders: typed,
+              logStmtProps: varargs[untyped]): untyped =
+  let lexicalScope = scopeHolders.lastScopeHolder.getImpl.actualBody
+  var finalBindings = initOrderedTable[string, NimNode]()
+
+  for k, v in bindings(lexicalScope, skip = 1):
+    finalBindings[k] = v
+
+  for k, v in bindings(logStmtProps, skip = 1):
+    finalBindings[k] = v
+
+  finalBindings.sort(system.cmp)
+
+  let eventName = logStmtProps[0]
+  assert eventName.kind in {nnkStrLit}
+  let record = genSym(nskVar, "record")
+
+  result = quote:
+    var `record`: StdoutLogRecord
+    setEventName(`record`, `eventName`)
+
+  var isFirst = true
+  for k, v in finalBindings:
+    result.add newCall(newIdentNode"setProperty",
+                       record, newLit(k), v, newLit(isFirst))
+    isFirst = false
+
+  result.add newCall(newIdentNode"flushRecord", record)
+
+template log(props: varargs[untyped]) {.dirty.} =
+  logImpl(bindSym"scopeHolder", props)
+
+scope:
+  a = 12
+  b = "original-b"
+
+scope:
+  x = 16
+  b = "overriden-b"
+
+scope:
+  c = 100
+
+proc main =
+  scope:
+    c = 10
+
+  scope:
+    z = 20
+
+  log("main started", a = 10, b = "inner-b", d = "some-d")
+
+main()
+
+log("exiting", msg = "bye bye")
+
diff --git a/tests/macros/ttemplatesymbols.nim b/tests/macros/ttemplatesymbols.nim
new file mode 100644
index 000000000..8d9c9ec02
--- /dev/null
+++ b/tests/macros/ttemplatesymbols.nim
@@ -0,0 +1,173 @@
+import
+  macros, algorithm, strutils
+
+proc normalProc(x: int) =
+  echo x
+
+template templateWithtouParams =
+  echo 10
+
+proc overloadedProc(x: int) =
+  echo x
+
+proc overloadedProc(x: string) =
+  echo x
+
+proc overloadedProc[T](x: T) =
+  echo x
+
+template normalTemplate(x: int) =
+  echo x
+
+template overloadedTemplate(x: int) =
+  echo x
+
+template overloadedTemplate(x: string) =
+  echo x
+
+macro normalMacro(x: int): untyped =
+  discard
+
+macro macroWithoutParams: untyped =
+  discard
+
+macro inspectSymbol(sym: typed, expected: static[string]): untyped =
+  if sym.kind == nnkSym:
+    echo "Symbol node:"
+    let res = sym.getImpl.repr & "\n"
+    echo res
+    # echo "|", res, "|"
+    # echo "|", expected, "|"
+    if expected.len > 0: assert res == expected
+  elif sym.kind in {nnkClosedSymChoice, nnkOpenSymChoice}:
+    echo "Begin sym choice:"
+    var results = newSeq[string](0)
+    for innerSym in sym:
+      results.add innerSym.getImpl.repr
+    sort(results, cmp[string])
+    let res = results.join("\n") & "\n"
+    echo res
+    if expected.len > 0: assert res == expected
+    echo "End symchoice."
+  else:
+    echo "Non-symbol node: ", sym.kind
+    if expected.len > 0: assert $sym.kind == expected
+
+macro inspectUntyped(sym: untyped, expected: static[string]): untyped =
+  let res = sym.repr
+  echo "Untyped node: ", res
+  assert res == expected
+
+inspectSymbol templateWithtouParams, "nnkCommand"
+  # this template is expanded, because bindSym was not used
+  # the end result is the template body (nnkCommand)
+
+inspectSymbol bindSym("templateWithtouParams"), """
+template templateWithtouParams() =
+  echo 10
+
+"""
+
+inspectSymbol macroWithoutParams, "nnkEmpty"
+  # Just like the template above, the macro was expanded
+
+inspectSymbol bindSym("macroWithoutParams"), """
+macro macroWithoutParams(): untyped =
+  discard
+
+"""
+
+inspectSymbol normalMacro, """
+macro normalMacro(x: int): untyped =
+  discard
+
+"""
+  # Since the normalMacro has params, it's automatically
+  # treated as a symbol here (no need for `bindSym`)
+
+inspectSymbol bindSym("normalMacro"), """
+macro normalMacro(x: int): untyped =
+  discard
+
+"""
+
+inspectSymbol normalTemplate, """
+template normalTemplate(x: int) =
+  echo x
+
+"""
+
+inspectSymbol bindSym("normalTemplate"), """
+template normalTemplate(x: int) =
+  echo x
+
+"""
+
+inspectSymbol overloadedTemplate, """
+template overloadedTemplate(x: int) =
+  echo x
+
+template overloadedTemplate(x: string) =
+  echo x
+
+"""
+
+inspectSymbol bindSym("overloadedTemplate"), """
+template overloadedTemplate(x: int) =
+  echo x
+
+template overloadedTemplate(x: string) =
+  echo x
+
+"""
+
+inspectUntyped bindSym("overloadedTemplate"), """bindSym("overloadedTemplate")"""
+  # binSym is active only in the presense of `typed` params.
+  # `untyped` params still get the raw AST
+
+inspectSymbol normalProc, """
+proc normalProc(x: int) =
+  echo [x]
+
+"""
+
+inspectSymbol bindSym("normalProc"), """
+proc normalProc(x: int) =
+  echo [x]
+
+"""
+
+inspectSymbol overloadedProc, """
+proc overloadedProc(x: int) =
+  echo [x]
+
+proc overloadedProc(x: string) =
+  echo [x]
+
+proc overloadedProc[T](x: T) =
+  echo x
+
+"""
+  # XXX: There seems to be a repr rendering problem above.
+  # Notice that `echo [x]`
+
+inspectSymbol overloadedProc[float], """
+proc overloadedProc(x: T) =
+  echo [x]
+
+"""
+  # As expected, when we select a specific generic, the
+  # AST is no longer a symChoice
+
+inspectSymbol bindSym("overloadedProc"), """
+proc overloadedProc(x: int) =
+  echo [x]
+
+proc overloadedProc(x: string) =
+  echo [x]
+
+proc overloadedProc[T](x: T) =
+  echo x
+
+"""
+
diff --git a/tests/macros/twrapiterator.nim b/tests/macros/twrapiterator.nim
new file mode 100644
index 000000000..e153ae980
--- /dev/null
+++ b/tests/macros/twrapiterator.nim
@@ -0,0 +1,19 @@
+
+import macros
+
+# bug #7093
+
+macro foobar(arg: untyped): untyped =
+  let procDef = quote do:
+    proc foo(): void =
+      echo "bar"
+
+
+  result = newStmtList(
+    arg, procDef
+  )
+
+  echo result.repr
+
+iterator bar(): int {.foobar.} =
+  discard