manual: cleanup the documentation; document for-loop macros; ensure the examples compile
This commit is contained in:
parent
c08efb4c51
commit
70c0ff1be2
1 changed files with 114 additions and 22 deletions
110
doc/manual.rst
110
doc/manual.rst
|
|
@ -3959,6 +3959,9 @@ Any statements following the ``defer`` in the current block will be considered
|
||||||
to be in an implicit try block:
|
to be in an implicit try block:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
|
proc main =
|
||||||
var f = open("numbers.txt")
|
var f = open("numbers.txt")
|
||||||
defer: close(f)
|
defer: close(f)
|
||||||
f.write "abc"
|
f.write "abc"
|
||||||
|
|
@ -3967,6 +3970,9 @@ to be in an implicit try block:
|
||||||
Is rewritten to:
|
Is rewritten to:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
|
proc main =
|
||||||
var f = open("numbers.txt")
|
var f = open("numbers.txt")
|
||||||
try:
|
try:
|
||||||
f.write "abc"
|
f.write "abc"
|
||||||
|
|
@ -4037,6 +4043,8 @@ to explicitly define which exceptions a proc/iterator/method/converter is
|
||||||
allowed to raise. The compiler verifies this:
|
allowed to raise. The compiler verifies this:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
proc p(what: bool) {.raises: [IOError, OSError].} =
|
proc p(what: bool) {.raises: [IOError, OSError].} =
|
||||||
if what: raise newException(IOError, "IO")
|
if what: raise newException(IOError, "IO")
|
||||||
else: raise newException(OSError, "OS")
|
else: raise newException(OSError, "OS")
|
||||||
|
|
@ -4056,6 +4064,9 @@ A ``raises`` list can also be attached to a proc type. This affects type
|
||||||
compatibility:
|
compatibility:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
:status: 1
|
||||||
|
|
||||||
type
|
type
|
||||||
Callback = proc (s: string) {.raises: [IOError].}
|
Callback = proc (s: string) {.raises: [IOError].}
|
||||||
var
|
var
|
||||||
|
|
@ -4114,8 +4125,11 @@ exception is an *effect*. Other effects can also be defined. A user defined
|
||||||
effect is a means to *tag* a routine and to perform checks against this tag:
|
effect is a means to *tag* a routine and to perform checks against this tag:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
:status: 1
|
||||||
|
|
||||||
type IO = object ## input/output effect
|
type IO = object ## input/output effect
|
||||||
proc readLine(): string {.tags: [IO].}
|
proc readLine(): string {.tags: [IO].} = discard
|
||||||
|
|
||||||
proc no_IO_please() {.tags: [].} =
|
proc no_IO_please() {.tags: [].} =
|
||||||
# the compiler prevents this:
|
# the compiler prevents this:
|
||||||
|
|
@ -4167,6 +4181,8 @@ introduce type parameters or to instantiate a generic proc, iterator or type.
|
||||||
The following example shows a generic binary tree can be modelled:
|
The following example shows a generic binary tree can be modelled:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
type
|
type
|
||||||
BinaryTree*[T] = ref object # BinaryTree is a generic type with
|
BinaryTree*[T] = ref object # BinaryTree is a generic type with
|
||||||
# generic param ``T``
|
# generic param ``T``
|
||||||
|
|
@ -4175,7 +4191,7 @@ The following example shows a generic binary tree can be modelled:
|
||||||
|
|
||||||
proc newNode*[T](data: T): BinaryTree[T] =
|
proc newNode*[T](data: T): BinaryTree[T] =
|
||||||
# constructor for a node
|
# constructor for a node
|
||||||
result = BinaryTree(le: nil, ri: nil, data: data)
|
result = BinaryTree[T](le: nil, ri: nil, data: data)
|
||||||
|
|
||||||
proc add*[T](root: var BinaryTree[T], n: BinaryTree[T]) =
|
proc add*[T](root: var BinaryTree[T], n: BinaryTree[T]) =
|
||||||
# insert a node into the tree
|
# insert a node into the tree
|
||||||
|
|
@ -4611,6 +4627,8 @@ in any required way. For example, here is how one might define the classic
|
||||||
type is an instance of it:
|
type is an instance of it:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
import future, typetraits
|
import future, typetraits
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -4815,6 +4833,8 @@ Open symbols are looked up in two different contexts: Both the context
|
||||||
at definition and the context at instantiation are considered:
|
at definition and the context at instantiation are considered:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
type
|
type
|
||||||
Index = distinct int
|
Index = distinct int
|
||||||
|
|
||||||
|
|
@ -4837,6 +4857,8 @@ Mixin statement
|
||||||
A symbol can be forced to be open by a `mixin`:idx: declaration:
|
A symbol can be forced to be open by a `mixin`:idx: declaration:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
proc create*[T](): ref T =
|
proc create*[T](): ref T =
|
||||||
# there is no overloaded 'init' here, so we need to state that it's an
|
# there is no overloaded 'init' here, so we need to state that it's an
|
||||||
# open symbol explicitly:
|
# open symbol explicitly:
|
||||||
|
|
@ -4914,6 +4936,7 @@ performed before the expression is passed to the template. This means that for
|
||||||
example *undeclared* identifiers can be passed to the template:
|
example *undeclared* identifiers can be passed to the template:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
template declareInt(x: untyped) =
|
template declareInt(x: untyped) =
|
||||||
var x: int
|
var x: int
|
||||||
|
|
@ -4923,6 +4946,8 @@ example *undeclared* identifiers can be passed to the template:
|
||||||
|
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
:status: 1
|
||||||
|
|
||||||
template declareInt(x: typed) =
|
template declareInt(x: typed) =
|
||||||
var x: int
|
var x: int
|
||||||
|
|
@ -4947,6 +4972,8 @@ You can pass a block of statements as a last parameter to a template via a
|
||||||
special ``:`` syntax:
|
special ``:`` syntax:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
template withFile(f, fn, mode, actions: untyped): untyped =
|
template withFile(f, fn, mode, actions: untyped): untyped =
|
||||||
var f: File
|
var f: File
|
||||||
if open(f, fn, mode):
|
if open(f, fn, mode):
|
||||||
|
|
@ -4970,6 +4997,9 @@ the block needs to be of type ``untyped``. Because symbol lookups are then
|
||||||
delayed until template instantiation time:
|
delayed until template instantiation time:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
:status: 1
|
||||||
|
|
||||||
template t(body: typed) =
|
template t(body: typed) =
|
||||||
block:
|
block:
|
||||||
body
|
body
|
||||||
|
|
@ -4992,6 +5022,8 @@ The same code works with ``untyped`` as the passed body is not required to be
|
||||||
type-checked:
|
type-checked:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
template t(body: untyped) =
|
template t(body: untyped) =
|
||||||
block:
|
block:
|
||||||
body
|
body
|
||||||
|
|
@ -5012,6 +5044,8 @@ 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:
|
also ``varargs[untyped]`` so that not even the number of parameters is fixed:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
template hideIdentifiers(x: varargs[untyped]) = discard
|
template hideIdentifiers(x: varargs[untyped]) = discard
|
||||||
|
|
||||||
hideIdentifiers(undeclared1, undeclared2)
|
hideIdentifiers(undeclared1, undeclared2)
|
||||||
|
|
@ -5055,6 +5089,7 @@ Identifier construction
|
||||||
In templates identifiers can be constructed with the backticks notation:
|
In templates identifiers can be constructed with the backticks notation:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
template typedef(name: untyped, typ: typedesc) =
|
template typedef(name: untyped, typ: typedesc) =
|
||||||
type
|
type
|
||||||
|
|
@ -5116,6 +5151,7 @@ Per default templates are `hygienic`:idx:\: Local identifiers declared in a
|
||||||
template cannot be accessed in the instantiation context:
|
template cannot be accessed in the instantiation context:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
template newException*(exceptn: typedesc, message: string): untyped =
|
template newException*(exceptn: typedesc, message: string): untyped =
|
||||||
var
|
var
|
||||||
|
|
@ -5173,6 +5209,9 @@ rewritten to ``f(x)``. Therefore the dot syntax has some limitations when it
|
||||||
is used to invoke templates/macros:
|
is used to invoke templates/macros:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
:status: 1
|
||||||
|
|
||||||
template declareVar(name: untyped) =
|
template declareVar(name: untyped) =
|
||||||
const name {.inject.} = 45
|
const name {.inject.} = 45
|
||||||
|
|
||||||
|
|
@ -5183,13 +5222,16 @@ is used to invoke templates/macros:
|
||||||
Another common example is this:
|
Another common example is this:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
:status: 1
|
||||||
|
|
||||||
from sequtils import toSeq
|
from sequtils import toSeq
|
||||||
|
|
||||||
iterator something: string =
|
iterator something: string =
|
||||||
yield "Hello"
|
yield "Hello"
|
||||||
yield "World"
|
yield "World"
|
||||||
|
|
||||||
var info = toSeq(something())
|
var info = something().toSeq
|
||||||
|
|
||||||
The problem here is that the compiler already decided that ``something()`` as
|
The problem here is that the compiler already decided that ``something()`` as
|
||||||
an iterator is not callable in this context before ``toSeq`` gets its
|
an iterator is not callable in this context before ``toSeq`` gets its
|
||||||
|
|
@ -5221,6 +5263,8 @@ The following example implements a powerful ``debug`` command that accepts a
|
||||||
variable number of arguments:
|
variable number of arguments:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
# to work with Nim syntax trees, we need an API that is defined in the
|
# to work with Nim syntax trees, we need an API that is defined in the
|
||||||
# ``macros`` module:
|
# ``macros`` module:
|
||||||
import macros
|
import macros
|
||||||
|
|
@ -5278,6 +5322,8 @@ instantiating context. There is a way to use bound identifiers
|
||||||
builtin can be used for that:
|
builtin can be used for that:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
import macros
|
import macros
|
||||||
|
|
||||||
macro debug(n: varargs[typed]): untyped =
|
macro debug(n: varargs[typed]): untyped =
|
||||||
|
|
@ -5373,6 +5419,41 @@ This is a simple syntactic transformation into:
|
||||||
proc p() = discard
|
proc p() = discard
|
||||||
|
|
||||||
|
|
||||||
|
For loop macros
|
||||||
|
---------------
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
|
import macros
|
||||||
|
|
||||||
|
macro enumerate(x: ForLoopStmt): untyped =
|
||||||
|
expectKind x, nnkForStmt
|
||||||
|
# we strip off the first for loop variable and use
|
||||||
|
# it as an integer counter:
|
||||||
|
result = newStmtList()
|
||||||
|
result.add newVarStmt(x[0], newLit(0))
|
||||||
|
var body = x[^1]
|
||||||
|
if body.kind != nnkStmtList:
|
||||||
|
body = newTree(nnkStmtList, body)
|
||||||
|
body.add newCall(bindSym"inc", x[0])
|
||||||
|
var newFor = newTree(nnkForStmt)
|
||||||
|
for i in 1..x.len-3:
|
||||||
|
newFor.add x[i]
|
||||||
|
# transform enumerate(X) to 'X'
|
||||||
|
newFor.add x[^2][1]
|
||||||
|
newFor.add body
|
||||||
|
result.add newFor
|
||||||
|
|
||||||
|
for a, b in enumerate(items([1, 2, 3])):
|
||||||
|
echo a, " ", b
|
||||||
|
|
||||||
|
for a2, b2 in enumerate([1, 2, 3, 5]):
|
||||||
|
echo a2, " ", b2
|
||||||
|
|
||||||
|
|
||||||
Special Types
|
Special Types
|
||||||
=============
|
=============
|
||||||
|
|
@ -5447,6 +5528,7 @@ one can use a named alias or an explicit `typedesc` generic param:
|
||||||
Once bound, typedesc params can appear in the rest of the proc signature:
|
Once bound, typedesc params can appear in the rest of the proc signature:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
template declareVariableWithType(T: typedesc, value: T) =
|
template declareVariableWithType(T: typedesc, value: T) =
|
||||||
var x: T = value
|
var x: T = value
|
||||||
|
|
@ -5458,12 +5540,14 @@ Overload resolution can be further influenced by constraining the set of
|
||||||
types that will match the typedesc param:
|
types that will match the typedesc param:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
template maxval(T: typedesc[int]): int = high(int)
|
template maxval(T: typedesc[int]): int = high(int)
|
||||||
template maxval(T: typedesc[float]): float = Inf
|
template maxval(T: typedesc[float]): float = Inf
|
||||||
|
|
||||||
var i = int.maxval
|
var i = int.maxval
|
||||||
var f = float.maxval
|
var f = float.maxval
|
||||||
|
when false:
|
||||||
var s = string.maxval # error, maxval is not implemented for string
|
var s = string.maxval # error, maxval is not implemented for string
|
||||||
|
|
||||||
The constraint can be a concrete type or a type class.
|
The constraint can be a concrete type or a type class.
|
||||||
|
|
@ -5494,8 +5578,8 @@ the expression, where the unknown field or proc name is passed to
|
||||||
an ``untyped`` parameter:
|
an ``untyped`` parameter:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
a.b # becomes `.`(a, "b")
|
a.b # becomes `.`(a, b)
|
||||||
a.b(c, d) # becomes `.`(a, "b", c, d)
|
a.b(c, d) # becomes `.`(a, b, c, d)
|
||||||
|
|
||||||
The matched dot operators can be symbols of any callable kind (procs,
|
The matched dot operators can be symbols of any callable kind (procs,
|
||||||
templates and macros), depending on the desired effect:
|
templates and macros), depending on the desired effect:
|
||||||
|
|
@ -5525,7 +5609,7 @@ operator `.=`
|
||||||
This operator will be matched against assignments to missing fields.
|
This operator will be matched against assignments to missing fields.
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
a.b = c # becomes `.=`(a, "b", c)
|
a.b = c # becomes `.=`(a, b, c)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -6086,6 +6170,9 @@ module name followed by an ``except`` list to prevent some symbols to be
|
||||||
imported:
|
imported:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
:status: 1
|
||||||
|
|
||||||
import strutils except `%`, toUpper
|
import strutils except `%`, toUpper
|
||||||
|
|
||||||
# doesn't work then:
|
# doesn't work then:
|
||||||
|
|
@ -6146,6 +6233,8 @@ an ``import`` to list the symbols one likes to use without explicit
|
||||||
full qualification:
|
full qualification:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
from strutils import `%`
|
from strutils import `%`
|
||||||
|
|
||||||
echo "$1" % "abc"
|
echo "$1" % "abc"
|
||||||
|
|
@ -6340,7 +6429,7 @@ collector to not consider objects of this type as part of a cycle:
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
type
|
type
|
||||||
Node = ref NodeObj
|
Node = ref NodeObj
|
||||||
NodeObj {.acyclic, final.} = object
|
NodeObj {.acyclic.} = object
|
||||||
left, right: Node
|
left, right: Node
|
||||||
data: string
|
data: string
|
||||||
|
|
||||||
|
|
@ -6348,7 +6437,7 @@ Or if we directly use a ref object:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
type
|
type
|
||||||
Node = ref object {.acyclic, final.}
|
Node = ref object {.acyclic.}
|
||||||
left, right: Node
|
left, right: Node
|
||||||
data: string
|
data: string
|
||||||
|
|
||||||
|
|
@ -6568,7 +6657,7 @@ factor.
|
||||||
immediate pragma
|
immediate pragma
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
See `Typed vs untyped parameters`_.
|
The immediate pragma is obsolete. See `Typed vs untyped parameters`_.
|
||||||
|
|
||||||
|
|
||||||
compilation option pragmas
|
compilation option pragmas
|
||||||
|
|
@ -7814,8 +7903,11 @@ Parallel statement
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c --threads:on $1"
|
||||||
|
|
||||||
# Compute PI in an inefficient way
|
# Compute PI in an inefficient way
|
||||||
import strutils, math, threadpool
|
import strutils, math, threadpool
|
||||||
|
{.experimental.}
|
||||||
|
|
||||||
proc term(k: float): float = 4 * math.pow(-1, k) / (2*k + 1)
|
proc term(k: float): float = 4 * math.pow(-1, k) / (2*k + 1)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue