summary refs log tree commit diff stats
path: root/doc/manual/templates.txt
diff options
context:
space:
mode:
Diffstat (limited to 'doc/manual/templates.txt')
-rw-r--r--doc/manual/templates.txt498
1 files changed, 0 insertions, 498 deletions
diff --git a/doc/manual/templates.txt b/doc/manual/templates.txt
deleted file mode 100644
index f01a703cd..000000000
--- a/doc/manual/templates.txt
+++ /dev/null
@@ -1,498 +0,0 @@
-Templates
-=========
-
-A template is a simple form of a macro: It is a simple substitution
-mechanism that operates on Nim's abstract syntax trees. It is processed in
-the semantic pass of the compiler.
-
-The syntax to *invoke* a template is the same as calling a procedure.
-
-Example:
-
-.. code-block:: nim
-  template `!=` (a, b: untyped): untyped =
-    # this definition exists in the System module
-    not (a == b)
-
-  assert(5 != 6) # the compiler rewrites that to: assert(not (5 == 6))
-
-The ``!=``, ``>``, ``>=``, ``in``, ``notin``, ``isnot`` operators are in fact
-templates:
-
-| ``a > b`` is transformed into ``b < a``.
-| ``a in b`` is transformed into ``contains(b, a)``.
-| ``notin`` and ``isnot`` have the obvious meanings.
-
-The "types" of templates can be the symbols ``untyped``,
-``typed`` or ``typedesc`` (stands for *type
-description*). These are "meta types", they can only be used in certain
-contexts. Real types can be used too; this implies that ``typed`` expressions
-are expected.
-
-
-Typed vs untyped parameters
----------------------------
-
-An ``untyped`` parameter means that symbol lookups and type resolution is not
-performed before the expression is passed to the template. This means that for
-example *undeclared* identifiers can be passed to the template:
-
-.. code-block:: nim
-
-  template declareInt(x: untyped) =
-    var x: int
-
-  declareInt(x) # valid
-  x = 3
-
-
-.. code-block:: nim
-
-  template declareInt(x: typed) =
-    var x: int
-
-  declareInt(x) # invalid, because x has not been declared and so has no type
-
-A template where every parameter is ``untyped`` is called an `immediate`:idx:
-template. For historical reasons templates can be explicitly annotated with
-an ``immediate`` pragma and then these templates do not take part in
-overloading resolution and the parameters' types are *ignored* by the
-compiler. Explicit immediate templates are now deprecated.
-
-**Note**: For historical reasons ``stmt`` is an alias for ``typed`` and
-``expr`` an alias for ``untyped``, but new code should use the newer,
-clearer names.
-
-
-Passing a code block to a template
-----------------------------------
-
-You can pass a block of statements as a last parameter to a template via a
-special ``:`` syntax:
-
-.. code-block:: nim
-  template withFile(f, fn, mode, actions: untyped): untyped =
-    var f: File
-    if open(f, fn, mode):
-      try:
-        actions
-      finally:
-        close(f)
-    else:
-      quit("cannot open: " & fn)
-
-  withFile(txt, "ttempl3.txt", fmWrite):
-    txt.writeLine("line 1")
-    txt.writeLine("line 2")
-
-In the example the two ``writeLine`` statements are bound to the ``actions``
-parameter.
-
-
-Usually to pass a block of code to a template the parameter that accepts
-the block needs to be of type ``untyped``. Because symbol lookups are then
-delayed until template instantiation time:
-
-.. code-block:: nim
-  template t(body: typed) =
-    block:
-      body
-
-  t:
-    var i = 1
-    echo i
-
-  t:
-    var i = 2  # fails with 'attempt to redeclare i'
-    echo i
-
-The above code fails with the mysterious error message that ``i`` has already
-been declared. The reason for this is that the ``var i = ...`` bodies need to
-be type-checked before they are passed to the ``body`` parameter and type
-checking in Nim implies symbol lookups. For the symbol lookups to succeed
-``i`` needs to be added to the current (i.e. outer) scope. After type checking
-these additions to the symbol table are not rolled back (for better or worse).
-The same code works with ``untyped`` as the passed body is not required to be
-type-checked:
-
-.. code-block:: nim
-  template t(body: untyped) =
-    block:
-      body
-
-  t:
-    var i = 1
-    echo i
-
-  t:
-    var i = 2  # compiles
-    echo i
-
-
-Varargs of untyped
-------------------
-
-In addition to the ``untyped`` meta-type that prevents type checking there is
-also ``varargs[untyped]`` so that not even the number of parameters is fixed:
-
-.. code-block:: nim
-  template hideIdentifiers(x: varargs[untyped]) = discard
-
-  hideIdentifiers(undeclared1, undeclared2)
-
-However, since a template cannot iterate over varargs, this feature is
-generally much more useful for macros.
-
-**Note**: For historical reasons ``varargs[expr]`` is not equivalent
-to ``varargs[untyped]``.
-
-
-Symbol binding in templates
----------------------------
-
-A template is a `hygienic`:idx: macro and so opens a new scope. Most symbols are
-bound from the definition scope of the template:
-
-.. code-block:: nim
-  # Module A
-  var
-    lastId = 0
-
-  template genId*: untyped =
-    inc(lastId)
-    lastId
-
-.. code-block:: nim
-  # Module B
-  import A
-
-  echo genId() # Works as 'lastId' has been bound in 'genId's defining scope
-
-As in generics symbol binding can be influenced via ``mixin`` or ``bind``
-statements.
-
-
-
-Identifier construction
------------------------
-
-In templates identifiers can be constructed with the backticks notation:
-
-.. code-block:: nim
-
-  template typedef(name: untyped, typ: typedesc) =
-    type
-      `T name`* {.inject.} = typ
-      `P name`* {.inject.} = ref `T name`
-
-  typedef(myint, int)
-  var x: PMyInt
-
-In the example ``name`` is instantiated with ``myint``, so \`T name\` becomes
-``Tmyint``.
-
-
-Lookup rules for template parameters
-------------------------------------
-
-A parameter ``p`` in a template is even substituted in the expression ``x.p``.
-Thus template arguments can be used as field names and a global symbol can be
-shadowed by the same argument name even when fully qualified:
-
-.. code-block:: nim
-  # module 'm'
-
-  type
-    Lev = enum
-      levA, levB
-
-  var abclev = levB
-
-  template tstLev(abclev: Lev) =
-    echo abclev, " ", m.abclev
-
-  tstLev(levA)
-  # produces: 'levA levA'
-
-But the global symbol can properly be captured by a ``bind`` statement:
-
-.. code-block:: nim
-  # module 'm'
-
-  type
-    Lev = enum
-      levA, levB
-
-  var abclev = levB
-
-  template tstLev(abclev: Lev) =
-    bind m.abclev
-    echo abclev, " ", m.abclev
-
-  tstLev(levA)
-  # produces: 'levA levB'
-
-
-Hygiene in templates
---------------------
-
-Per default templates are `hygienic`:idx:\: Local identifiers declared in a
-template cannot be accessed in the instantiation context:
-
-.. code-block:: nim
-
-  template newException*(exceptn: typedesc, message: string): untyped =
-    var
-      e: ref exceptn  # e is implicitly gensym'ed here
-    new(e)
-    e.msg = message
-    e
-
-  # so this works:
-  let e = "message"
-  raise newException(EIO, e)
-
-
-Whether a symbol that is declared in a template is exposed to the instantiation
-scope is controlled by the `inject`:idx: and `gensym`:idx: pragmas: gensym'ed
-symbols are not exposed but inject'ed are.
-
-The default for symbols of entity ``type``, ``var``, ``let`` and ``const``
-is ``gensym`` and for ``proc``, ``iterator``, ``converter``, ``template``,
-``macro`` is ``inject``. However, if the name of the entity is passed as a
-template parameter, it is an inject'ed symbol:
-
-.. code-block:: nim
-  template withFile(f, fn, mode: untyped, actions: untyped): untyped =
-    block:
-      var f: File  # since 'f' is a template param, it's injected implicitly
-      ...
-
-  withFile(txt, "ttempl3.txt", fmWrite):
-    txt.writeLine("line 1")
-    txt.writeLine("line 2")
-
-
-The ``inject`` and ``gensym`` pragmas are second class annotations; they have
-no semantics outside of a template definition and cannot be abstracted over:
-
-.. code-block:: nim
-  {.pragma myInject: inject.}
-
-  template t() =
-    var x {.myInject.}: int # does NOT work
-
-
-To get rid of hygiene in templates, one can use the `dirty`:idx: pragma for
-a template. ``inject`` and ``gensym`` have no effect in ``dirty`` templates.
-
-
-
-Limitations of the method call syntax
--------------------------------------
-
-The expression ``x`` in ``x.f`` needs to be semantically checked (that means
-symbol lookup and type checking) before it can be decided that it needs to be
-rewritten to ``f(x)``. Therefore the dot syntax has some limiations when it
-is used to invoke templates/macros:
-
-.. code-block:: nim
-  template declareVar(name: untyped) =
-    const name {.inject.} = 45
-
-  # Doesn't compile:
-  unknownIdentifier.declareVar
-
-
-Another common example is this:
-
-.. code-block:: nim
-  from sequtils import toSeq
-
-  iterator something: string =
-    yield "Hello"
-    yield "World"
-
-  var info = toSeq(something())
-
-The problem here is that the compiler already decided that ``something()`` as
-an iterator is not callable in this context before ``toSeq`` gets its
-chance to convert it into a sequence.
-
-
-Macros
-======
-
-A macro is a special kind of low level template. Macros can be used
-to implement `domain specific languages`:idx:.
-
-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 abstract syntax tree.
-
-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
-variable number of arguments:
-
-.. code-block:: nim
-  # to work with Nim syntax trees, we need an API that is defined in the
-  # ``macros`` module:
-  import macros
-
-  macro debug(n: varargs[untyped]): untyped =
-    # `n` is a Nim AST that contains the whole macro invocation
-    # this macro returns a list of statements:
-    result = newNimNode(nnkStmtList, n)
-    # iterate over any argument that is passed to this macro:
-    for i in 0..n.len-1:
-      # add a call to the statement list that writes the expression;
-      # `toStrLit` converts an AST to its string representation:
-      add(result, newCall("write", newIdentNode("stdout"), toStrLit(n[i])))
-      # add a call to the statement list that writes ": "
-      add(result, newCall("write", newIdentNode("stdout"), newStrLitNode(": ")))
-      # add a call to the statement list that writes the expressions value:
-      add(result, newCall("writeLine", newIdentNode("stdout"), n[i]))
-
-  var
-    a: array [0..10, int]
-    x = "some string"
-  a[0] = 42
-  a[1] = 45
-
-  debug(a[0], a[1], x)
-
-The macro call expands to:
-
-.. code-block:: nim
-  write(stdout, "a[0]")
-  write(stdout, ": ")
-  writeLine(stdout, a[0])
-
-  write(stdout, "a[1]")
-  write(stdout, ": ")
-  writeLine(stdout, a[1])
-
-  write(stdout, "x")
-  write(stdout, ": ")
-  writeLine(stdout, x)
-
-
-Arguments that are passed to a ``varargs`` parameter are wrapped in an array
-constructor expression. This is why ``debug`` iterates over all of ``n``'s
-children.
-
-
-BindSym
--------
-
-The above ``debug`` macro relies on the fact that ``write``, ``writeLine`` and
-``stdout`` are declared in the system module and thus visible in the
-instantiating context. There is a way to use bound identifiers
-(aka `symbols`:idx:) instead of using unbound identifiers. The ``bindSym``
-builtin can be used for that:
-
-.. code-block:: nim
-  import macros
-
-  macro debug(n: varargs[typed]): untyped =
-    result = newNimNode(nnkStmtList, n)
-    for x in n:
-      # we can bind symbols in scope via 'bindSym':
-      add(result, newCall(bindSym"write", bindSym"stdout", toStrLit(x)))
-      add(result, newCall(bindSym"write", bindSym"stdout", newStrLitNode(": ")))
-      add(result, newCall(bindSym"writeLine", bindSym"stdout", x))
-
-  var
-    a: array [0..10, int]
-    x = "some string"
-  a[0] = 42
-  a[1] = 45
-
-  debug(a[0], a[1], x)
-
-The macro call expands to:
-
-.. code-block:: nim
-  write(stdout, "a[0]")
-  write(stdout, ": ")
-  writeLine(stdout, a[0])
-
-  write(stdout, "a[1]")
-  write(stdout, ": ")
-  writeLine(stdout, a[1])
-
-  write(stdout, "x")
-  write(stdout, ": ")
-  writeLine(stdout, x)
-
-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.
-
-
-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:
-
-.. code-block:: nim
-  import macros
-
-  macro case_token(n: untyped): untyped =
-    # creates a lexical analyzer from regular expressions
-    # ... (implementation is an exercise for the reader :-)
-    discard
-
-  case_token: # this colon tells the parser it is a macro statement
-  of r"[A-Za-z_]+[A-Za-z_0-9]*":
-    return tkIdentifier
-  of r"0-9+":
-    return tkInteger
-  of r"[\+\-\*\?]+":
-    return tkOperator
-  else:
-    return tkUnknown
-
-
-**Style note**: For code readability, it is the best idea to use the least
-powerful programming construct that still suffices. So the "check list" is:
-
-(1) Use an ordinary proc/iterator, if possible.
-(2) Else: Use a generic proc/iterator, if possible.
-(3) Else: Use a template, if possible.
-(4) Else: Use a macro.
-
-
-Macros as pragmas
------------------
-
-Whole routines (procs, iterators etc.) can also be passed to a template or
-a macro via the pragma notation:
-
-.. code-block:: nim
-  template m(s: untyped) = discard
-
-  proc p() {.m.} = discard
-
-This is a simple syntactic transformation into:
-
-.. code-block:: nim
-  template m(s: untyped) = discard
-
-  m:
-    proc p() = discard
-