pass typedesc as NimNode to macros (#11400)

* change typedesc's design in macros

* Manual and changelog entry.

* add link to RFC
This commit is contained in:
Arne Döring 2019-06-05 09:17:04 +02:00 • committed by Andreas Rumpf
commit cb45527e37
6 changed files with 54 additions and 39 deletions

View file

@ -52,6 +52,12 @@
- With the exception of `uint` and `uint64`, conversion to unsigned types - With the exception of `uint` and `uint64`, conversion to unsigned types
are now range checked during runtime. are now range checked during runtime.
- Macro arguments of type `typedesc` are now passed in to the macro as
`NimNode` like every other type. Use either `typed` or
`static[typedesc]` for a behavior that is identical in new and old
Nim.
RFC: `Pass typedesc as NimNode to macros
<https://github.com/nim-lang/RFCs/issues/148>`_.
#### Breaking changes in the standard library #### Breaking changes in the standard library

View file

@ -1554,11 +1554,7 @@ proc activate(c: PContext, n: PNode) =
proc maybeAddResult(c: PContext, s: PSym, n: PNode) = proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
if s.kind == skMacro: if s.kind == skMacro:
let resultType = let resultType = sysTypeFromName(c.graph, n.info, "NimNode")
if s.typ.sons[0] != nil and s.typ.sons[0].kind == tyTypeDesc:
s.typ.sons[0]
else:
sysTypeFromName(c.graph, n.info, "NimNode")
addResult(c, resultType, n.info, s.kind) addResult(c, resultType, n.info, s.kind)
addResultNode(c, n) addResultNode(c, n)
elif s.typ.sons[0] != nil and not isInlineIterator(s): elif s.typ.sons[0] != nil and not isInlineIterator(s):

View file

@ -869,8 +869,8 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
var a = copySym(param) var a = copySym(param)
a.typ = staticType.base a.typ = staticType.base
addDecl(c, a) addDecl(c, a)
elif param.typ != nil and param.typ.kind == tyTypeDesc: #elif param.typ != nil and param.typ.kind == tyTypeDesc:
addDecl(c, param) # addDecl(c, param)
else: else:
# within a macro, every param has the type NimNode! # within a macro, every param has the type NimNode!
let nn = getSysSym(c.graph, param.info, "NimNode") let nn = getSysSym(c.graph, param.info, "NimNode")

View file

@ -2028,8 +2028,8 @@ proc setupMacroParam(x: PNode, typ: PType): TFullReg =
case typ.kind case typ.kind
of tyStatic: of tyStatic:
putIntoReg(result, x) putIntoReg(result, x)
of tyTypeDesc: #of tyTypeDesc:
putIntoReg(result, x) # putIntoReg(result, x)
else: else:
result.kind = rkNode result.kind = rkNode
var n = x var n = x

View file

@ -4828,16 +4828,8 @@ While macros enable advanced compile-time code transformations, they
cannot change Nim's syntax. However, this is no real restriction because cannot change Nim's syntax. However, this is no real restriction because
Nim's syntax is flexible enough anyway. Nim's syntax is flexible enough anyway.
To write macros, one needs to know how the Nim concrete syntax is converted Debug Example
to an AST. -------------
There are two ways to invoke a macro:
(1) invoking a macro like a procedure call (`expression macros`)
(2) invoking a macro with the special ``macrostmt`` syntax (`statement macros`)
Expression Macros
-----------------
The following example implements a powerful ``debug`` command that accepts a The following example implements a powerful ``debug`` command that accepts a
variable number of arguments: variable number of arguments:
@ -4943,22 +4935,23 @@ However, the symbols ``write``, ``writeLine`` and ``stdout`` are already bound
and are not looked up again. As the example shows, ``bindSym`` does work with and are not looked up again. As the example shows, ``bindSym`` does work with
overloaded symbols implicitly. overloaded symbols implicitly.
Case-Of Macro
-------------
Statement Macros In Nim it is possible to have a macro with the syntax of a *case-of*
---------------- expression just with the difference that all of branches are passed to
and processed by the macro implementation. It is then up the macro
Statement macros are defined just as expression macros. However, they are implementation to transform the *of-branches* into a valid Nim
invoked by an expression following a colon. statement. The following example should show how this feature could be
used for a lexical analyzer.
The following example outlines a macro that generates a lexical analyzer from
regular expressions:
.. code-block:: nim .. code-block:: nim
import macros import macros
macro case_token(n: untyped): untyped = macro case_token(args: varargs[untyped]): untyped =
echo args.treeRepr
# creates a lexical analyzer from regular expressions # creates a lexical analyzer from regular expressions
# ... (implementation is an exercise for the reader :-) # ... (implementation is an exercise for the reader ;-)
discard discard
case_token: # this colon tells the parser it is a macro statement case_token: # this colon tells the parser it is a macro statement
@ -5001,8 +4994,8 @@ This is a simple syntactic transformation into:
proc p() = discard proc p() = discard
For loop macros For Loop Macro
--------------- --------------
A macro that takes as its only input parameter an expression of the special A macro that takes as its only input parameter an expression of the special
type ``system.ForLoopStmt`` can rewrite the entirety of a ``for`` loop: type ``system.ForLoopStmt`` can rewrite the entirety of a ``for`` loop:
@ -5197,8 +5190,10 @@ Once bound, type params can appear in the rest of the proc signature:
declareVariableWithType int, 42 declareVariableWithType int, 42
Overload resolution can be further influenced by constraining the set of Overload resolution can be further influenced by constraining the set
types that will match the type param: of types that will match the type param. This works in practice to
attaching attributes to types via templates. The constraint can be a
concrete type or a type class.
.. code-block:: nim .. code-block:: nim
:test: "nim c $1" :test: "nim c $1"
@ -5211,14 +5206,34 @@ types that will match the type param:
when false: when false:
var s = string.maxval # error, maxval is not implemented for string var s = string.maxval # error, maxval is not implemented for string
The constraint can be a concrete type or a type class. template isNumber(t: typedesc[object]): string = "Don't think so."
template isNumber(t: typedesc[SomeInteger]): string = "Yes!"
template isNumber(t: typedesc[SomeFloat]): string = "Maybe, could be NaN."
echo "is int a number? ", isNumber(int)
echo "is float a number? ", isNumber(float)
echo "is RootObj a number? ", isNumber(RootObj)
Passing ``typedesc`` almost identical, just with the differences that
the macro is not instantiated generically. The type expression is
simply passed as a ``NimNode`` to the macro, like everything else.
.. code-block:: nim
import macros
macro forwardType(arg: typedesc): typedesc =
# ``arg`` is of type ``NimNode``
let tmp: NimNode = arg
result = tmp
var tmp: forwardType(int)
typeof operator typeof operator
--------------- ---------------
**Note**: ``typeof(x)`` can also be written as ``type(x)`` but ``type(x)`` **Note**: ``typeof(x)`` can for historical reasons also be written as
is discouraged. ``type(x)`` but ``type(x)`` is discouraged.
You can obtain the type of a given expression by constructing a ``typeof`` You can obtain the type of a given expression by constructing a ``typeof``
value from it (in many other languages this is known as the `typeof`:idx: value from it (in many other languages this is known as the `typeof`:idx:
@ -6989,5 +7004,3 @@ Threads and exceptions
The interaction between threads and exceptions is simple: A *handled* exception The interaction between threads and exceptions is simple: A *handled* exception
in one thread cannot affect any other thread. However, an *unhandled* exception in one thread cannot affect any other thread. However, an *unhandled* exception
in one thread terminates the whole *process*! in one thread terminates the whole *process*!

View file

@ -14,7 +14,7 @@ template selectType(x: int): type =
template simpleTypeTempl: type = template simpleTypeTempl: type =
string string
macro typeFromMacro: type = string macro typeFromMacro: type = bindSym"string"
# The tests below check that the result variable of the # The tests below check that the result variable of the
# selected type matches the literal types in the code: # selected type matches the literal types in the code: