diff options
-rw-r--r-- | changelog.md | 6 | ||||
-rw-r--r-- | compiler/semstmts.nim | 6 | ||||
-rw-r--r-- | compiler/semtypes.nim | 4 | ||||
-rw-r--r-- | compiler/vm.nim | 4 | ||||
-rw-r--r-- | doc/manual.rst | 71 | ||||
-rw-r--r-- | tests/metatype/ttypeselectors.nim | 2 |
6 files changed, 54 insertions, 39 deletions
diff --git a/changelog.md b/changelog.md index f75958a73..31cb3d167 100644 --- a/changelog.md +++ b/changelog.md @@ -52,6 +52,12 @@ - With the exception of `uint` and `uint64`, conversion to unsigned types 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 diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 655b9c5c0..525de63e8 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1554,11 +1554,7 @@ proc activate(c: PContext, n: PNode) = proc maybeAddResult(c: PContext, s: PSym, n: PNode) = if s.kind == skMacro: - let resultType = - if s.typ.sons[0] != nil and s.typ.sons[0].kind == tyTypeDesc: - s.typ.sons[0] - else: - sysTypeFromName(c.graph, n.info, "NimNode") + let resultType = sysTypeFromName(c.graph, n.info, "NimNode") addResult(c, resultType, n.info, s.kind) addResultNode(c, n) elif s.typ.sons[0] != nil and not isInlineIterator(s): diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index dd9aea0f8..484fcfcd6 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -869,8 +869,8 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) = var a = copySym(param) a.typ = staticType.base addDecl(c, a) - elif param.typ != nil and param.typ.kind == tyTypeDesc: - addDecl(c, param) + #elif param.typ != nil and param.typ.kind == tyTypeDesc: + # addDecl(c, param) else: # within a macro, every param has the type NimNode! let nn = getSysSym(c.graph, param.info, "NimNode") diff --git a/compiler/vm.nim b/compiler/vm.nim index f2d989a8d..8b36b461b 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -2028,8 +2028,8 @@ proc setupMacroParam(x: PNode, typ: PType): TFullReg = case typ.kind of tyStatic: putIntoReg(result, x) - of tyTypeDesc: - putIntoReg(result, x) + #of tyTypeDesc: + # putIntoReg(result, x) else: result.kind = rkNode var n = x diff --git a/doc/manual.rst b/doc/manual.rst index 92d23f456..0de96077e 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -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 Nim's syntax is flexible enough anyway. -To write macros, one needs to know how the Nim concrete syntax is converted -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 ------------------ +Debug Example +------------- The following example implements a powerful ``debug`` command that accepts a 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 overloaded symbols implicitly. +Case-Of Macro +------------- -Statement Macros ----------------- - -Statement macros are defined just as expression macros. However, they are -invoked by an expression following a colon. - -The following example outlines a macro that generates a lexical analyzer from -regular expressions: +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 +implementation to transform the *of-branches* into a valid Nim +statement. The following example should show how this feature could be +used for a lexical analyzer. .. code-block:: nim 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 - # ... (implementation is an exercise for the reader :-) + # ... (implementation is an exercise for the reader ;-) discard 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 -For loop macros ---------------- +For Loop Macro +-------------- 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: @@ -5197,8 +5190,10 @@ Once bound, type params can appear in the rest of the proc signature: declareVariableWithType int, 42 -Overload resolution can be further influenced by constraining the set of -types that will match the type param: +Overload resolution can be further influenced by constraining the set +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 :test: "nim c $1" @@ -5211,14 +5206,34 @@ types that will match the type param: when false: 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 --------------- -**Note**: ``typeof(x)`` can also be written as ``type(x)`` but ``type(x)`` -is discouraged. +**Note**: ``typeof(x)`` can for historical reasons also be written as +``type(x)`` but ``type(x)`` is discouraged. 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: @@ -6989,5 +7004,3 @@ Threads and exceptions 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 terminates the whole *process*! - - diff --git a/tests/metatype/ttypeselectors.nim b/tests/metatype/ttypeselectors.nim index eb857271d..52e5415be 100644 --- a/tests/metatype/ttypeselectors.nim +++ b/tests/metatype/ttypeselectors.nim @@ -14,7 +14,7 @@ template selectType(x: int): type = template simpleTypeTempl: type = string -macro typeFromMacro: type = string +macro typeFromMacro: type = bindSym"string" # The tests below check that the result variable of the # selected type matches the literal types in the code: |