Further tutorial updates (#5176)

* tut1: added multiline comments
* tut2: replaced expr/stmt with untyped/typed
* added some more template/macro example
* remove immediate pragma from example
This commit is contained in:
jlp765 2017-01-07 18:18:45 +10:00 • committed by Andreas Rumpf
commit 86c73cdfc2
2 changed files with 85 additions and 15 deletions

View file

@ -125,6 +125,19 @@ Documentation comments are tokens; they are only allowed at certain places in
the input file as they belong to the syntax tree! This feature enables simpler the input file as they belong to the syntax tree! This feature enables simpler
documentation generators. documentation generators.
Multiline comments are started with ``#[`` and terminated with ``]#``. Multiline
comments can also be nested.
.. code-block:: nim
#[
You can have any Nim code text commented
out inside this with no indentation restrictions.
yes("May I ask a pointless question?")
#[
Note: these can be nested!!
]#
]#
You can also use the `discard statement`_ together with *long string You can also use the `discard statement`_ together with *long string
literals* to create block comments: literals* to create block comments:

View file

@ -557,7 +557,7 @@ To *invoke* a template, call it like a procedure.
Example: Example:
.. code-block:: nim .. code-block:: nim
template `!=` (a, b: expr): expr = template `!=` (a, b: untyped): untyped =
# this definition exists in the System module # this definition exists in the System module
not (a == b) not (a == b)
@ -603,19 +603,20 @@ Turning the ``log`` proc into a template solves this problem:
x = 4 x = 4
log("x has the value: " & $x) log("x has the value: " & $x)
The parameters' types can be ordinary types or the meta types ``expr`` The parameters' types can be ordinary types or the meta types ``untyped``,
(stands for *expression*), ``stmt`` (stands for *statement*) or ``typedesc`` ``typed``, or ``typedesc``.
(stands for *type description*). If the template has no explicit return type, ``typedesc`` stands for *type description*, and ``untyped`` means symbol lookups and
``stmt`` is used for consistency with procs and methods. type resolution is not performed before the expression is passed to the template.
If there is a ``stmt`` parameter it should be the last in the template If the template has no explicit return type,
declaration. The reason is that statements can be passed to a template ``void`` is used for consistency with procs and methods.
via a special ``:`` syntax:
To pass a block of statements to a template, use 'untyped' for the last parameter:
.. code-block:: nim .. code-block:: nim
template withFile(f: expr, filename: string, mode: FileMode, template withFile(f: untyped, filename: string, mode: FileMode,
body: stmt): stmt {.immediate.} = body: untyped): typed =
let fn = filename let fn = filename
var f: File var f: File
if open(f, fn, mode): if open(f, fn, mode):
@ -636,7 +637,6 @@ avoid a common bug: to forget to close the file. Note how the
``let fn = filename`` statement ensures that ``filename`` is evaluated only ``let fn = filename`` statement ensures that ``filename`` is evaluated only
once. once.
Macros Macros
====== ======
@ -672,7 +672,7 @@ variable number of arguments:
# ``macros`` module: # ``macros`` module:
import macros import macros
macro debug(n: varargs[expr]): stmt = macro debug(n: varargs[untyped]): typed =
# `n` is a Nim AST that contains a list of expressions; # `n` is a Nim AST that contains a list of expressions;
# this macro returns a list of statements (n is passed for proper line # this macro returns a list of statements (n is passed for proper line
# information): # information):
@ -723,7 +723,7 @@ regular expressions:
.. code-block:: nim .. code-block:: nim
macro case_token(n: stmt): stmt = macro case_token(n: typed): typed =
# 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
@ -815,7 +815,7 @@ modified source code implementing the macro:
import macros, strutils import macros, strutils
macro readCfgAndBuildSource(cfgFilename: string): stmt = macro readCfgAndBuildSource(cfgFilename: string): typed =
let let
inputString = slurp(cfgFilename.strVal) inputString = slurp(cfgFilename.strVal)
var var
@ -941,7 +941,7 @@ macro:
import macros, strutils import macros, strutils
macro readCfgAndBuildAST(cfgFilename: string): stmt = macro readCfgAndBuildAST(cfgFilename: string): typed =
let let
inputString = slurp(cfgFilename.strVal) inputString = slurp(cfgFilename.strVal)
@ -992,6 +992,63 @@ example you add ``echo treeRepr(result)`` you should get the same output as
using the ``dumpTree`` macro, but of course you can call that at any point of using the ``dumpTree`` macro, but of course you can call that at any point of
the macro where you might be having troubles. the macro where you might be having troubles.
Example Templates and Macros
============================
Lifting Procs
+++++++++++++
.. code-block:: nim
import math
template liftScalarProc(fname) =
## Lift a proc taking one scalar parameter and returning a
## scalar value (eg ``proc sssss[T](x: T): float``),
## to provide templated procs that can handle a single
## parameter of seq[T] or nested seq[seq[]] or the same type
##
## .. code-block:: Nim
## liftScalarProc(abs)
## # now abs(@[@[1,-2], @[-2,-3]]) == @[@[1,2], @[2,3]]
proc fname[T](x: openarray[T]): auto =
var temp: T
type outType = type(fname(temp))
result = newSeq[outType](x.len)
for i in 0..<x.len:
result[i] = fname(x[i])
liftScalarProc(sqrt) # make sqrt() work for sequences
echo sqrt(@[4.0, 16.0, 25.0, 36.0]) # => @[2.0, 4.0, 5.0, 6.0]
Identifier Mangling
+++++++++++++++++++
.. code-block:: nim
proc echoHW() =
echo "Hello world"
proc echoHW0() =
echo "Hello world 0"
proc echoHW1() =
echo "Hello world 1"
template joinSymbols(a, b: untyped): untyped =
`a b`()
joinSymbols(echo, HW)
macro str2Call(s1, s2): typed =
result = newNimNode(nnkStmtList)
for i in 0..1:
# combines s1, s2 and an integer into an proc identifier
# that is called in a statement list
result.add(newCall(!($s1 & $s2 & $i)))
str2Call("echo", "HW")
# Output:
# Hello world
# Hello world 0
# Hello world 1
Compilation to JavaScript Compilation to JavaScript
========================= =========================