tut3: improved the wording
This commit is contained in:
parent
30480605c1
commit
1cc8b7814d
1 changed files with 41 additions and 34 deletions
75
doc/tut3.rst
75
doc/tut3.rst
|
|
@ -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
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue