Skip to content

Commit

Permalink
Quote do now works with result in block (nim-lang#7343)
Browse files Browse the repository at this point in the history
* Fix result not being able to use in quote do

This fixes the annoying issue of not be able to use result inside a
quote do block. It works by a simple trick. The quote do mechanic is
based on dynamically creating a template and immediately calling it with
the arguments found within the quote do block. Since this is called in
the scope of the macro the result variable is shadowed. This trick works
by changing all occurences of result (which shouldn't cause any issues
as result isn't used for anything else for the same reason) to another
name and then passing in an IdentNode with result as a named parameter
with that name.

Note that currently this just replaces it with a fixed named variable
"res" which should be changed to a non-colliding, dynamically created
name.

* Fix hard coded parameter "res" to anonymous symbol

This fixes the hard coded parameter "res" to be an anonymous symbol
instead so it won't collide with other parts of the argument list.

* Add test case for result in quote do block

A simple test case based on GitHub issue nim-lang#7323 on how you can't put
result in a quote do block. This test verifies that it actually works
correctly now.

* Add test for explicit capturing of result

* Rebased against devel
  • Loading branch information
PMunch authored and narimiran committed Nov 1, 2018
1 parent 7da015f commit 33d38c0
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 3 deletions.
15 changes: 12 additions & 3 deletions compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1812,6 +1812,7 @@ proc processQuotations(c: PContext; n: var PNode, op: string,
ids.add n
return


if n.kind == nkPrefix:
checkSonsLen(n, 2, c.config)
if n[0].kind == nkIdent:
Expand All @@ -1822,6 +1823,9 @@ proc processQuotations(c: PContext; n: var PNode, op: string,
n.sons[0] = newIdentNode(getIdent(c.cache, examinedOp.substr(op.len)), n.info)
elif n.kind == nkAccQuoted and op == "``":
returnQuote n[0]
elif n.kind == nkIdent:
if n.ident.s == "result":
n = ids[0]

for i in 0 ..< n.safeLen:
processQuotations(c, n.sons[i], op, quotes, ids)
Expand All @@ -1833,15 +1837,18 @@ proc semQuoteAst(c: PContext, n: PNode): PNode =
var
quotedBlock = n[^1]
op = if n.len == 3: expectString(c, n[1]) else: "``"
quotes = newSeq[PNode](1)
quotes = newSeq[PNode](2)
# the quotes will be added to a nkCall statement
# leave some room for the callee symbol
ids = newSeq[PNode]()
# leave some room for the callee symbol and the result symbol
ids = newSeq[PNode](1)
# this will store the generated param names
# leave some room for the result symbol

if quotedBlock.kind != nkStmtList:
localError(c.config, n.info, errXExpected, "block")

# This adds a default first field to pass the result symbol
ids[0] = newAnonSym(c, skParam, n.info).newSymNode
processQuotations(c, quotedBlock, op, quotes, ids)

var dummyTemplate = newProcNode(
Expand All @@ -1860,6 +1867,8 @@ proc semQuoteAst(c: PContext, n: PNode): PNode =

var tmpl = semTemplateDef(c, dummyTemplate)
quotes[0] = tmpl[namePos]
# This adds a call to newIdentNode("result") as the first argument to the template call
quotes[1] = newNode(nkCall, n.info, @[newIdentNode(getIdent(c.cache, "newIdentNode"), n.info), newStrNode(nkStrLit, "result")])
result = newNode(nkCall, n.info, @[
createMagic(c.graph, "getAst", mExpandToAst).newSymNode,
newNode(nkCall, n.info, quotes)])
Expand Down
17 changes: 17 additions & 0 deletions tests/macros/tquotedo.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import macros

macro mac(): untyped =
quote do:
proc test(): int =
(proc(): int = result = 123)()

mac()
echo test()

macro foobar(arg: untyped): untyped =
result = arg
result.add quote do:
`result`

foobar:
echo "Hallo Welt"

0 comments on commit 33d38c0

Please sign in to comment.