Here, this should reify some stuff... I'm not sure exactly what you were going 
for, so hopefully this helps some. I certainly found writing it enlightening.

**Examples**

* Asterisks denote that the remark is open to discussion, especially since I 
introduce a lot of new ideas in this post (sorry! I got excited). I likely 
touch on the remark again later in the post.

1\. Simple case

Simple functionality; can use a template/macro from the same module.
    
    
    # bod.nim
    template whatIsYourFavoriteColor*(body: untyped): untyped =
      echo("Blue")
      body
      echo("AAAUUUUGHH")
    
    when isMainModule:
      wrapwith whatIsYourFavoriteColor
      echo("Yello--")
    
    
    Run
    
    
    c -r bod.nim
    Blue
    Yell--
    AAAUUUUGHH
    
    
    Run

2\. Inner scope

Wrap statements allowed in any statement list.*
    
    
    # inn.nim
    import bod
    block:
      wrapwith whatIsYourFavoriteColor
      echo("Purple!")
    echo("It was green...")
    
    
    Run
    
    
    c -r inn.nim
    Blue
    Purple!
    AAAUUUUGHH
    It was green...
    
    
    Run

3\. Multiple arguments

Arguments are allowed* and are passed before the statement list.
    
    
    # wrapargs.nim
    import macros
    func replaceImpl(source, dest, body: NimNode): NimNode =
      result = body.copyNimTree
      for i, son in result:
        if son == source:
          result[i] = dest
        else:
          result[i] = replaceImpl(source, dest, son)
    macro replace(source, dest, body: untyped): untyped =
      replaceImpl(source, dest, body)
    
    wrapwith replace(2, 3)
    echo(2 + 1 + 2)
    echo($2)
    
    
    Run
    
    
    c -r wrapargs.nim
    7
    3
    
    
    Run

4\. Chaining
    
    
    wrapwith A
    wrapwith B
    
    
    Run

is resolved in order and `A` receives `wrapwith B` as part of the argument 
statement list.
    
    
    wrapwith A, B
    
    Run

is resolved in order and `A` does not receive B.
    
    
    # tricky.nim
    import wrapargs
    wrapwith replace(wrapwith replace(2, 3), echo("gotcha!"))
    wrapwith replace(2, 3)
    echo(2)
    
    
    Run
    
    
    c -r tricky.nim
    gotcha!
    2
    
    
    Run

However:
    
    
    # fancy.nim
    import wrapargs
    wrapwith replace(wrapwith replace(2, 3), echo("gotcha!")), replace(2, 3)
    echo(2)
    
    
    Run
    
    
    c -r fancy.nim
    3
    
    
    Run

**Grammar**

A wrap statement would grammatically be (I think) `wrapStmt = 'wrapwith' 
qualifiedIdent par (comma qualifiedIdent par)`* and be added as a statement.

* I think there is an argument to be made that only fully-qualified identifiers 
should be allowed. This would make debugging wrap statements and wrap 
statement-related issues much easier. However, it would break precedent and be 
generally inconsistent with Nim's lax nature regarding qualification 
(`{.pure.}` notwithstanding).

**Placement**

Wrap statements may or may not be placed in a top-level statement*,

Wrap statements must, in order to be easily locatable, come before all other 
statements in the statement list except imports, pragmas, docstrings, macro 
definitions, and template definitions**. Wrap statements are allowed after 
templates and macro definitions so that a wrap statement may use a macro 
located in the same module as it appears in***.

Wrap statements must appear within a `stmtList`.

* As I wrote this I realized that there's no technical reason not to allow wrap 
statements to appear in any statement list, so I include it as a topic of 
discussion here.

** Or maybe this restriction should only apply to top-level wrap statements, 
given that scopes should generally be much, much smaller and much, much easier 
to rake through than the root scope.

*** This is a questionable call and needs discussion

**Semantics**

For semantics, consider `wrapwith X` in:
    
    
    before
    wrapwith X
    after
    
    Run

Where `before` and after signify a sequence of statements. Note, however, that 
the code snippet only necessitates one actual `stmtList`, of which `before` and 
`after` are subsequences.

`X` is invoked as a macro/template normally with all the statements in `after` 
as a `stmtList`. The result, `X'`, must either be a `stmtList` or a statement*.

If `X'` is a `stmtList`, then the code snippet is replaced with a `stmtList` 
containing all the statements in `before` followed by all the statements in 
`X'`. Colloquially, the snippet becomes `before & X(after)`.

If `X'` is not a `stmtList`, but instead a statement, the code snippet is 
replaced with a `stmtList` of all the statements in `before` followed by `X'`. 
Colloquially, the snippet becomes `before & [X(after)]`.

* I include the restriction that it must be a statement (if not a `stmtList`) 
because my understanding is that that will work best with Nim's current way of 
doing things. Please correct me if I'm wrong and an expression would actually 
work fine.

**Semantics (multiple macros)**

` wrapwith` statements are resolved in their order in the code, which also 
handles the case:
    
    
    wrapwith A
    wrapwith B
    
    Run

In this case, `A` receives the statement `wrapwith B` as part of the `stmtList` 
its passed.

To avoid this, use the following syntax:
    
    
    before
    wrapwith A, B
    after
    
    Run

If a wrap statement is supplied multiple macros/templates, they are resolved in 
order*

In this case, the statements in `after` are passed to `A`, producing `A'`. If 
`A'` is a statement, it is wrapped in a `stmtList`; either way, it is then 
passed to `B`, producing `B'`. The statements in `B'` then replace `wrapwith A, 
B` and `after` in the code snippet. Colloquially, the snippet becomes the 
result of:
    
    
    var A' = A(after)
    if A'.kind isnot nnkStmtList:
      A' = nnkStmtList.newTree(A')
    var B' = B(A')
    if B'.kind isnot nnkStmtList:
      B' = nnkStmtList.newTree(B')
    return before & B'
    
    
    Run

* This should also be discussed. I personally see no reason to not resolve them 
in order, but I know (for example) Python decorators are resolved in reverse 
order, from the inside out.

**Semantics (arguments)**

In a wrap statement, the templates/macros may be supplied arguments* which will 
precede** the statement list in the template/macro invocation. For instance,
    
    
    block:
      wrapwith A(x, y)
      body
    
    
    Run

Is essentially the same as
    
    
    block:
      A(x, y, body)
    
    
    Run

* Perhaps worth discussion? I see no reason not to allow this.

** There is an argument to be made that arguments should come after the body, 
so that `body.macro(arg)` can be idiomatically used. The precedent, however, 
set by templates, is that the body comes last.

Reply via email to