tut3: improved the wording

This commit is contained in:
Araq 2018-11-21 20:45:31 +01:00
commit 1cc8b7814d

View file

@ -13,40 +13,38 @@ Introduction
"With Great Power Comes Great Responsibility." -- Spider Man's Uncle "With Great Power Comes Great Responsibility." -- Spider Man's Uncle
This document is a tutorial for the macros of the *Nim* programming This document is a tutorial about Nim's macro system.
language. A macro enables to formulate and distribute Nim syntax tree A macro is a function that is executed at compile time and transforms
transformations as a normal library. The arguments of a macro are a Nim syntax tree into a different tree.
passed a syntax tree, and its job is it to create another syntax tree
for the compiler. The way this works in Nim is, whenever the compiler
encounters a call expression to a macro, The compiler evaluates the
macro at compile time with the syntax tree from the invocation, and
then it replaces the call to the macro by the result of the macro.
Examples of things that can be implemented in macros: Examples of things that can be implemented in macros:
* An assert macro that prints both sides of a comparison operator, if * An assert macro that prints both sides of a comparison operator, if
the assertion fails. ``myAssert(a == b)`` that converts to the assertion fails. ``myAssert(a == b)`` is converted to
``if a != b: quit($a " != " $b)`` ``if a != b: quit($a " != " $b)``
* A debug macro that prints the value and the name of the symbol. * A debug macro that prints the value and the name of the symbol.
``myDebugEcho(a)`` that converts to ``echo "a: ", a`` ``myDebugEcho(a)`` is converted to ``echo "a: ", a``
* Symbolic differentiation of an expression. * Symbolic differentiation of an expression.
``diff(a*pow(x,3) + b*pow(x,2) + c*x + d, x)`` that converts to ``diff(a*pow(x,3) + b*pow(x,2) + c*x + d, x)`` is converted to
``3*a*pow(x,2) + 2*a*x + c`` ``3*a*pow(x,2) + 2*a*x + c``
Macro Arguments Macro Arguments
--------------- ---------------
The types of macro arguments have two faces. One face is used for The types of macro arguments have two faces. One face is used for
the overload resolution, and the other face is used for the semantic the overload resolution, and the other face is used within the macro
checking of the macro implementation. For example body. For example, if ``macro foo(arg: int)`` is called in an
``macro foo(arg: int)`` will be called in an expression ``foo(x)``, if expression ``foo(x)``, ``x`` has to be of a type compatible to int, but
``x`` is of type int, but for the semantic checking of the macro *within* the macro's body ``arg`` has the type ``NimNode``, not ``int``!
implementation, ``arg`` has the type ``NimNode``, not ``int`` as you might Why it is done this way will become obvious later, when we have seen
expect, because ``x`` will be passed as a symbol (``NimNode``), not as concrete examples.
an integer. There are two ways to pass arguments to a macro, either typed or
untyped. There are two ways to pass arguments to a macro, an argument can be
either ``typed`` or ``untyped``.
Untyped Arguments Untyped Arguments
----------------- -----------------
@ -54,27 +52,34 @@ Untyped Arguments
Untyped macro arguments are passed to the macro before they are Untyped macro arguments are passed to the macro before they are
semantically checked. This means the syntax tree that is passed down semantically checked. This means the syntax tree that is passed down
to the macro does not need to make sense for Nim yet, the only to the macro does not need to make sense for Nim yet, the only
limitation for the syntax in an untyped macro argument is, it needs to limitation is that it needs to be parseable. Usually the macro does
be parseable by the Nim parser. The semantic of this syntax is not check the argument either but uses it in the transformation's
entirely up to the macro implementation. In this case the macro is result somehow. The result of a macro expansion is always checked
responsible to implement its own semantic checking on the by the compiler, so apart from weird error messages nothing bad
argument. The upside for untyped arguments is, the syntax tree is can happen.
quite predictable and less complex than for typed arguments. Untyped
arguments have the type ``untyped`` in arguments list. The downside for an ``untyped`` argument is that these do not play
well with Nim's overloading resolution.
The upside for untyped arguments is that the syntax tree is
quite predictable and less complex compared to its ``typed``
counterpart.
Typed Arguments Typed Arguments
--------------- ---------------
For typed arguments, the semantic checker runs on the argument and For typed arguments, the semantic checker runs on the argument and
does transformations on it, before it is passed to the macro. Here does transformations on it, before it is passed to the macro. Here
identifier nodes will already be resolved as symbols, implicit type identifier nodes are resolved as symbols, implicit type
conversions are visible in the tree as calls, templates will be conversions are visible in the tree as calls, templates are
expanded and probably most importantly, nodes have type information. expanded and probably most importantly, nodes have type information.
Typed arguments can have the type ``typed`` in the arguments list. Typed arguments can have the type ``typed`` in the arguments list.
But all other types, such as ``int``, ``float`` or ``MyObjectType`` But all other types, such as ``int``, ``float`` or ``MyObjectType``
are typed arguments as well, and they will be passed to the macro as a are typed arguments as well, and they are passed to the macro as a
syntax tree. syntax tree.
Static Arguments Static Arguments
---------------- ----------------
@ -97,7 +102,7 @@ but in the macro body ``arg`` is just like a normal parameter of type
Code blocks as arguments Code blocks as arguments
------------------------ ------------------------
In Nim it is possible to pass the last argument of a call expression in a It is possible to pass the last argument of a call expression in a
separate code block with indentation. For example the following code separate code block with indentation. For example the following code
example is a valid (but not a recommended) way to call ``echo``: example is a valid (but not a recommended) way to call ``echo``:
@ -108,10 +113,10 @@ example is a valid (but not a recommended) way to call ``echo``:
let b = "ld!" let b = "ld!"
a & b a & b
For macros this way of calling is useful for example to implement an For macros this way of calling is very useful; syntax trees of arbitrary
embedded domain specific language. Syntax trees of arbitrary
complexity can be passed to macros with this notation. complexity can be passed to macros with this notation.
The Syntax Tree The Syntax Tree
--------------- ---------------
@ -119,7 +124,7 @@ In order to build a Nim syntax tree one needs to know how Nim source
code is represented as a syntax tree, and how such a tree needs to code is represented as a syntax tree, and how such a tree needs to
look like so that the Nim compiler will understand it. The nodes of the look like so that the Nim compiler will understand it. The nodes of the
Nim syntax tree are documented in the `macros <macros.html>`_ module. Nim syntax tree are documented in the `macros <macros.html>`_ module.
But a probably more interesting and interactive way to explore the Nim But a more interactive way to explore the Nim
syntax tree is with ``macros.treeRepr``, it converts a syntax tree syntax tree is with ``macros.treeRepr``, it converts a syntax tree
into a multi line string for printing on the console. It can be used into a multi line string for printing on the console. It can be used
to explore how the argument expressions are represented in tree form to explore how the argument expressions are represented in tree form
@ -147,6 +152,7 @@ but does nothing else. Here is an example of such a tree representation:
# Ident "b" # Ident "b"
# StrLit "abcdef" # StrLit "abcdef"
Custom sematic checking Custom sematic checking
----------------------- -----------------------
@ -163,6 +169,7 @@ be created with the ``macros.error`` proc.
macro myAssert(arg: untyped): untyped = macro myAssert(arg: untyped): untyped =
arg.expectKind nnkInfix arg.expectKind nnkInfix
Generating Code Generating Code
--------------- ---------------