Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

macros.quote: document hard to use op; add more useful examples #16489

Merged
merged 6 commits into from
Jan 6, 2021
Merged
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 71 additions & 23 deletions lib/core/macros.nim
Original file line number Diff line number Diff line change
Expand Up @@ -566,34 +566,82 @@ proc getAst*(macroOrTemplate: untyped): NimNode {.magic: "ExpandToAst", noSideEf
## macro FooMacro() =
## var ast = getAst(BarTemplate())

proc quote*(bl: typed, op = "``"): NimNode {.magic: "QuoteAst", noSideEffect.}
proc quote*(bl: typed, op = "``"): NimNode {.magic: "QuoteAst", noSideEffect.} =
## Quasi-quoting operator.
## Accepts an expression or a block and returns the AST that represents it.
## Within the quoted AST, you are able to interpolate NimNode expressions
## from the surrounding scope. If no operator is given, quoting is done using
## backticks. Otherwise, the given operator must be used as a prefix operator
## for any interpolated expression.
##
## Example:
##
## .. code-block:: nim
##
## macro check(ex: untyped) =
## # this is a simplified version of the check macro from the
## # unittest module.
##
## # If there is a failed check, we want to make it easy for
## # the user to jump to the faulty line in the code, so we
## # get the line info here:
## var info = ex.lineinfo
##
## # We will also display the code string of the failed check:
## var expString = ex.toStrLit
##
## # Finally we compose the code to implement the check:
## result = quote do:
## if not `ex`:
## echo `info` & ": Check failed: " & `expString`
## for any interpolated expression. The original meaning of the interpolation
## operator may be obtained by escaping it (by prefixing it with itself) when used
## as a unary operator:
## e.g. `@` is escaped as `@@`, `&%` is escaped as `&%` and so on; see examples.
timotheecour marked this conversation as resolved.
Show resolved Hide resolved
runnableExamples:
macro check(ex: untyped) =
# this is a simplified version of the check macro from the
# unittest module.

# If there is a failed check, we want to make it easy for
# the user to jump to the faulty line in the code, so we
# get the line info here:
var info = ex.lineinfo

# We will also display the code string of the failed check:
var expString = ex.toStrLit

# Finally we compose the code to implement the check:
result = quote do:
if not `ex`:
echo `info` & ": Check failed: " & `expString`
check 1 + 1 == 2

runnableExamples:
# example showing how to define a symbol that requires backtick without
# quoting it.
var destroyCalled = false
macro bar() =
let s = newTree(nnkAccQuoted, ident"=destroy")
# let s = ident"`=destroy`" # this would not work
result = quote do:
type Foo = object
# proc `=destroy`(a: var Foo) = destroyCalled = true # this would not work
proc `s`(a: var Foo) = destroyCalled = true
block:
let a = Foo()
bar()
doAssert destroyCalled

runnableExamples:
# custom `op`
var destroyCalled = false
macro bar() =
var x = 1.5
result = quote("@") do:
type Foo = object
proc `=destroy`(a: var Foo) =
doAssert @x == 1.5
doAssert compiles(@x == 1.5)
let b1 = @[1,2]
let b2 = @@[1,2]
doAssert $b1 == "[1, 2]"
doAssert $b2 == "@[1, 2]"
destroyCalled = true
block:
let a = Foo()
bar()
doAssert destroyCalled

proc `&%`(x: int): int = 1
proc `&%`(x, y: int): int = 2

macro bar2() =
var x = 3
result = quote("&%") do:
var y = &%x # quoting operator
doAssert &%&%y == 1 # unary operator => need to escape
doAssert y &% y == 2 # binary operator => no need to escape
doAssert y == 3
bar2()

proc expectKind*(n: NimNode, k: NimNodeKind) {.compileTime.} =
## Checks that `n` is of kind `k`. If this is not the case,
Expand Down