diff options
Diffstat (limited to 'tests/macros')
-rw-r--r-- | tests/macros/tdumpastgen.nim | 28 | ||||
-rw-r--r-- | tests/macros/tforloop_macro1.nim | 44 | ||||
-rw-r--r-- | tests/macros/tgensym.nim | 2 | ||||
-rw-r--r-- | tests/macros/tmacro1.nim | 59 | ||||
-rw-r--r-- | tests/macros/tstructuredlogging.nim | 154 | ||||
-rw-r--r-- | tests/macros/ttemplatesymbols.nim | 173 | ||||
-rw-r--r-- | tests/macros/twrapiterator.nim | 19 |
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 |