Skip to content

Commit

Permalink
rename case statement macro from match to case (nim-lang#16923)
Browse files Browse the repository at this point in the history
* rename case statement macro from match to `case`

* fix test
  • Loading branch information
metagn authored and ardek66 committed Mar 26, 2021
1 parent 945d3bf commit 268af26
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 16 deletions.
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ with other backends. see #9125. Use `-d:nimLegacyJsRound` for previous behavior.
- `os.copyFile` is now 2.5x faster on OSX, by using `copyfile` from `copyfile.h`;
use `-d:nimLegacyCopyFile` for OSX < 10.5.

- The required name of case statement macros for the experimental
`caseStmtMacros` feature has changed from `match` to `` `case` ``.

## Compiler changes

Expand Down
6 changes: 3 additions & 3 deletions compiler/semstmts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -861,10 +861,10 @@ proc handleForLoopMacro(c: PContext; n: PNode; flags: TExprFlags): PNode =

proc handleCaseStmtMacro(c: PContext; n: PNode; flags: TExprFlags): PNode =
# n[0] has been sem'checked and has a type. We use this to resolve
# 'match(n[0])' but then we pass 'n' to the 'match' macro. This seems to
# '`case`(n[0])' but then we pass 'n' to the `case` macro. This seems to
# be the best solution.
var toResolve = newNodeI(nkCall, n.info)
toResolve.add newIdentNode(getIdent(c.cache, "match"), n.info)
toResolve.add newIdentNode(getIdent(c.cache, "case"), n.info)
toResolve.add n[0]

var errors: CandidateErrors
Expand All @@ -875,7 +875,7 @@ proc handleCaseStmtMacro(c: PContext; n: PNode; flags: TExprFlags): PNode =
markUsed(c, n[0].info, match)
onUse(n[0].info, match)

# but pass 'n' to the 'match' macro, not 'n[0]':
# but pass 'n' to the `case` macro, not 'n[0]':
r.call[1] = n
let toExpand = semResolvedCall(c, r, r.call, {})
case match.kind
Expand Down
24 changes: 11 additions & 13 deletions doc/manual_experimental.rst
Original file line number Diff line number Diff line change
Expand Up @@ -947,11 +947,10 @@ the documentation of `spawn <#parallel-amp-spawn-spawn-statement>`_ for details.
Case statement macros
=====================

A macro that needs to be called `match`:idx: can be used to rewrite
``case`` statements in order to implement `pattern matching`:idx: for
certain types. The following example implements a simplistic form of
pattern matching for tuples, leveraging the existing equality operator
for tuples (as provided in ``system.==``):
Macros named `case` can rewrite `case` statements for certain types in order to
implement `pattern matching`:idx:. The following example implements a
simplistic form of pattern matching for tuples, leveraging the existing
equality operator for tuples (as provided in ``system.==``):

.. code-block:: nim
:test: "nim c $1"
Expand All @@ -960,7 +959,7 @@ for tuples (as provided in ``system.==``):
import macros
macro match(n: tuple): untyped =
macro `case`(n: tuple): untyped =
result = newTree(nnkIfStmt)
let selector = n[0]
for i in 1 ..< n.len:
Expand All @@ -973,8 +972,7 @@ for tuples (as provided in ``system.==``):
let cond = newCall("==", selector, it[j])
result.add newTree(nnkElifBranch, cond, it[^1])
else:
error "'match' cannot handle this node", it
echo repr result
error "custom 'case' for tuple cannot handle this node", it
case ("foo", 78)
of ("foo", 78): echo "yes"
Expand All @@ -985,12 +983,12 @@ for tuples (as provided in ``system.==``):
Currently case statement macros must be enabled explicitly
via ``{.experimental: "caseStmtMacros".}``.

``match`` macros are subject to overload resolution. First the
``case``'s selector expression is used to determine which ``match``
macro to call. To this macro is then passed the complete ``case``
statement body and the macro is evaluated.
`case` macros are subject to overload resolution. The type of the
`case` statement's selector expression is matched against the type
of the first argument of the `case` macro. Then the complete `case`
statement is passed in place of the argument and the macro is evaluated.

In other words, the macro needs to transform the full ``case`` statement
In other words, the macro needs to transform the full `case` statement
but only the statement's selector expression is used to determine which
macro to call.

Expand Down
35 changes: 35 additions & 0 deletions tests/macros/tcasestmtmacro.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
discard """
output: '''
yes
'''
"""

{.experimental: "caseStmtMacros".}

import macros

macro `case`(n: tuple): untyped =
result = newTree(nnkIfStmt)
let selector = n[0]
for i in 1 ..< n.len:
let it = n[i]
case it.kind
of nnkElse, nnkElifBranch, nnkElifExpr, nnkElseExpr:
result.add it
of nnkOfBranch:
for j in 0..it.len-2:
let cond = newCall("==", selector, it[j])
result.add newTree(nnkElifBranch, cond, it[^1])
else:
error "custom 'case' for tuple cannot handle this node", it

var correct = false

case ("foo", 78)
of ("foo", 78):
correct = true
echo "yes"
of ("bar", 88): echo "no"
else: discard

doAssert correct

0 comments on commit 268af26

Please sign in to comment.