I think this is what you're looking for: import std/macros macro decorator(procedure: untyped): untyped = expectKind(procedure, nnkProcDef) # 1. ensure macro accepts only procedures procedure[^1].add( # 2. modify the procedure body in some way quote do: echo "decorated stuff in procedure body" ) return procedure # 3. return the modified procedure at compile-time (decorated) proc test() {.decorator.} = echo "normal stuff in procedure body" test() # normal stuff in procedure body # decorated stuff in procedure body Run
>From my understanding, decorators modify a function in some way. What you want >to do is create a macro that accepts a procedure definition, and modify then >return that procedure. You use the macro's name as the procedure's pragma to >pass the procedure into the macro. During compile-time the macro is expanded, >applies the transformations, and returns the newly modified procedure. For adding something like `@Authentication(userId)` might look like this as a proof-of-concept: # modified example above import std/macros macro decorator(id, procedure: untyped): untyped = expectKind(id, nnkIdent) expectKind(procedure, nnkProcDef) let idStr = id.repr procedure[^1].add( quote do: echo "printing identifier: ", `idStr` ) return procedure proc test() {.decorator(identifierThing).} = echo "normal stuff in procedure body" test() # normal stuff in procedure body # printing identifier: identifierThing Run Check out these for learning how macros work: <https://nim-lang.org/docs/macros.html> (the module itself) <https://nim-lang.org/docs/tut3.html> <https://nim-by-example.github.io/macros/> <https://dev.to/beef331/demystification-of-macros-in-nim-13n8> (this one's really good and simple)