doc: Trim .txt files trailing whitespace
via OSX: find . -name '*.txt' -exec sed -i '' -E 's/[[:space:]]+$//' {} +
This commit is contained in:
parent
51488766e7
commit
0b44d812f1
33 changed files with 566 additions and 566 deletions
|
|
@ -14,20 +14,20 @@ optional *a*. Parentheses may be used to group elements.
|
|||
``&`` is the lookahead operator; ``&a`` means that an ``a`` is expected but
|
||||
not consumed. It will be consumed in the following rule.
|
||||
|
||||
The ``|``, ``/`` symbols are used to mark alternatives and have the lowest
|
||||
precedence. ``/`` is the ordered choice that requires the parser to try the
|
||||
The ``|``, ``/`` symbols are used to mark alternatives and have the lowest
|
||||
precedence. ``/`` is the ordered choice that requires the parser to try the
|
||||
alternatives in the given order. ``/`` is often used to ensure the grammar
|
||||
is not ambiguous.
|
||||
is not ambiguous.
|
||||
|
||||
Non-terminals start with a lowercase letter, abstract terminal symbols are in
|
||||
UPPERCASE. Verbatim terminal symbols (including keywords) are quoted
|
||||
with ``'``. An example::
|
||||
|
||||
ifStmt = 'if' expr ':' stmts ('elif' expr ':' stmts)* ('else' stmts)?
|
||||
|
||||
|
||||
The binary ``^*`` operator is used as a shorthand for 0 or more occurrences
|
||||
separated by its second argument; likewise ``^+`` means 1 or more
|
||||
occurrences: ``a ^+ b`` is short for ``a (b a)*``
|
||||
separated by its second argument; likewise ``^+`` means 1 or more
|
||||
occurrences: ``a ^+ b`` is short for ``a (b a)*``
|
||||
and ``a ^* b`` is short for ``(a (b a)*)?``. Example::
|
||||
|
||||
arrayConstructor = '[' expr ^* ',' ']'
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@ program execution. Unless explicitly classified, an error is a static error.
|
|||
|
||||
A `checked runtime error`:idx: is an error that the implementation detects
|
||||
and reports at runtime. The method for reporting such errors is via *raising
|
||||
exceptions* or *dying with a fatal error*. However, the implementation
|
||||
exceptions* or *dying with a fatal error*. However, the implementation
|
||||
provides a means to disable these runtime checks. See the section pragmas_
|
||||
for details.
|
||||
for details.
|
||||
|
||||
Whether a checked runtime error results in an exception or in a fatal error at
|
||||
runtime is implementation specific. Thus the following program is always
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ Exception tracking
|
|||
------------------
|
||||
|
||||
Nim supports exception tracking. The `raises`:idx: pragma can be used
|
||||
to explicitly define which exceptions a proc/iterator/method/converter is
|
||||
to explicitly define which exceptions a proc/iterator/method/converter is
|
||||
allowed to raise. The compiler verifies this:
|
||||
|
||||
.. code-block:: nim
|
||||
|
|
@ -24,7 +24,7 @@ An empty ``raises`` list (``raises: []``) means that no exception may be raised:
|
|||
result = false
|
||||
|
||||
|
||||
A ``raises`` list can also be attached to a proc type. This affects type
|
||||
A ``raises`` list can also be attached to a proc type. This affects type
|
||||
compatibility:
|
||||
|
||||
.. code-block:: nim
|
||||
|
|
@ -35,7 +35,7 @@ compatibility:
|
|||
|
||||
proc p(x: string) =
|
||||
raise newException(OSError, "OS")
|
||||
|
||||
|
||||
c = p # type error
|
||||
|
||||
|
||||
|
|
@ -46,30 +46,30 @@ possibly raised exceptions; the algorithm operates on ``p``'s call graph:
|
|||
raise ``system.Exception`` (the base type of the exception hierarchy) and
|
||||
thus any exception unless ``T`` has an explicit ``raises`` list.
|
||||
However if the call is of the form ``f(...)`` where ``f`` is a parameter
|
||||
of the currently analysed routine it is ignored. The call is optimistically
|
||||
of the currently analysed routine it is ignored. The call is optimistically
|
||||
assumed to have no effect. Rule 2 compensates for this case.
|
||||
2. Every expression of some proc type within a call that is not a call
|
||||
itself (and not nil) is assumed to be called indirectly somehow and thus
|
||||
2. Every expression of some proc type within a call that is not a call
|
||||
itself (and not nil) is assumed to be called indirectly somehow and thus
|
||||
its raises list is added to ``p``'s raises list.
|
||||
3. Every call to a proc ``q`` which has an unknown body (due to a forward
|
||||
declaration or an ``importc`` pragma) is assumed to
|
||||
3. Every call to a proc ``q`` which has an unknown body (due to a forward
|
||||
declaration or an ``importc`` pragma) is assumed to
|
||||
raise ``system.Exception`` unless ``q`` has an explicit ``raises`` list.
|
||||
4. Every call to a method ``m`` is assumed to
|
||||
4. Every call to a method ``m`` is assumed to
|
||||
raise ``system.Exception`` unless ``m`` has an explicit ``raises`` list.
|
||||
5. For every other call the analysis can determine an exact ``raises`` list.
|
||||
6. For determining a ``raises`` list, the ``raise`` and ``try`` statements
|
||||
6. For determining a ``raises`` list, the ``raise`` and ``try`` statements
|
||||
of ``p`` are taken into consideration.
|
||||
|
||||
Rules 1-2 ensure the following works:
|
||||
Rules 1-2 ensure the following works:
|
||||
|
||||
.. code-block:: nim
|
||||
proc noRaise(x: proc()) {.raises: [].} =
|
||||
# unknown call that might raise anything, but valid:
|
||||
x()
|
||||
|
||||
|
||||
proc doRaise() {.raises: [IOError].} =
|
||||
raise newException(IOError, "IO")
|
||||
|
||||
|
||||
proc use() {.raises: [].} =
|
||||
# doesn't compile! Can raise IOError!
|
||||
noRaise(doRaise)
|
||||
|
|
@ -82,21 +82,21 @@ Tag tracking
|
|||
------------
|
||||
|
||||
The exception tracking is part of Nim's `effect system`:idx:. Raising an
|
||||
exception is an *effect*. Other effects can also be defined. A user defined
|
||||
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:
|
||||
|
||||
.. code-block:: nim
|
||||
type IO = object ## input/output effect
|
||||
proc readLine(): string {.tags: [IO].}
|
||||
|
||||
|
||||
proc no_IO_please() {.tags: [].} =
|
||||
# the compiler prevents this:
|
||||
let x = readLine()
|
||||
|
||||
A tag has to be a type name. A ``tags`` list - like a ``raises`` list - can
|
||||
A tag has to be a type name. A ``tags`` list - like a ``raises`` list - can
|
||||
also be attached to a proc type. This affects type compatibility.
|
||||
|
||||
The inference for tag tracking is analogous to the inference for
|
||||
The inference for tag tracking is analogous to the inference for
|
||||
exception tracking.
|
||||
|
||||
|
||||
|
|
@ -105,7 +105,7 @@ Read/Write tracking
|
|||
|
||||
**Note**: Read/write tracking is not yet implemented!
|
||||
|
||||
The inference for read/write tracking is analogous to the inference for
|
||||
The inference for read/write tracking is analogous to the inference for
|
||||
exception tracking.
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ Alternatively, the ``distinct`` type modifier can be applied to the type class
|
|||
to allow each param matching the type class to bind to a different type.
|
||||
|
||||
If a proc param doesn't have a type specified, Nim will use the
|
||||
``distinct auto`` type class (also known as ``any``). Note this behavior is
|
||||
``distinct auto`` type class (also known as ``any``). Note this behavior is
|
||||
deprecated for procs; templates, however, support them:
|
||||
|
||||
.. code-block:: nim
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ With this notation we can now easily define the core of the grammar: A block of
|
|||
statements (simplified example)::
|
||||
|
||||
ifStmt = 'if' expr ':' stmt
|
||||
(IND{=} 'elif' expr ':' stmt)*
|
||||
(IND{=} 'elif' expr ':' stmt)*
|
||||
(IND{=} 'else' ':' stmt)?
|
||||
|
||||
simpleStmt = ifStmt / ...
|
||||
|
|
@ -156,7 +156,7 @@ contain the following `escape sequences`:idx:\ :
|
|||
================== ===================================================
|
||||
|
||||
|
||||
Strings in Nim may contain any 8-bit value, even embedded zeros. However
|
||||
Strings in Nim may contain any 8-bit value, even embedded zeros. However
|
||||
some operations may interpret the first binary zero as a terminator.
|
||||
|
||||
|
||||
|
|
@ -174,7 +174,7 @@ be whitespace between the opening ``"""`` and the newline),
|
|||
the newline (and the preceding whitespace) is not included in the string. The
|
||||
ending of the string literal is defined by the pattern ``"""[^"]``, so this:
|
||||
|
||||
.. code-block:: nim
|
||||
.. code-block:: nim
|
||||
""""long string within quotes""""
|
||||
|
||||
Produces::
|
||||
|
|
@ -187,9 +187,9 @@ Raw string literals
|
|||
|
||||
Terminal symbol in the grammar: ``RSTR_LIT``.
|
||||
|
||||
There are also raw string literals that are preceded with the
|
||||
letter ``r`` (or ``R``) and are delimited by matching double quotes (just
|
||||
like ordinary string literals) and do not interpret the escape sequences.
|
||||
There are also raw string literals that are preceded with the
|
||||
letter ``r`` (or ``R``) and are delimited by matching double quotes (just
|
||||
like ordinary string literals) and do not interpret the escape sequences.
|
||||
This is especially convenient for regular expressions or Windows paths:
|
||||
|
||||
.. code-block:: nim
|
||||
|
|
@ -201,21 +201,21 @@ To produce a single ``"`` within a raw string literal, it has to be doubled:
|
|||
.. code-block:: nim
|
||||
|
||||
r"a""b"
|
||||
|
||||
|
||||
Produces::
|
||||
|
||||
|
||||
a"b
|
||||
|
||||
``r""""`` is not possible with this notation, because the three leading
|
||||
quotes introduce a triple quoted string literal. ``r"""`` is the same
|
||||
as ``"""`` since triple quoted string literals do not interpret escape
|
||||
``r""""`` is not possible with this notation, because the three leading
|
||||
quotes introduce a triple quoted string literal. ``r"""`` is the same
|
||||
as ``"""`` since triple quoted string literals do not interpret escape
|
||||
sequences either.
|
||||
|
||||
|
||||
Generalized raw string literals
|
||||
-------------------------------
|
||||
|
||||
Terminal symbols in the grammar: ``GENERALIZED_STR_LIT``,
|
||||
Terminal symbols in the grammar: ``GENERALIZED_STR_LIT``,
|
||||
``GENERALIZED_TRIPLESTR_LIT``.
|
||||
|
||||
The construct ``identifier"string literal"`` (without whitespace between the
|
||||
|
|
@ -281,7 +281,7 @@ Numerical constants are of a single type and have the form::
|
|||
DEC_LIT = digit ( ['_'] digit )*
|
||||
OCT_LIT = '0' ('o' | 'c' | 'C') octdigit ( ['_'] octdigit )*
|
||||
BIN_LIT = '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )*
|
||||
|
||||
|
||||
INT_LIT = HEX_LIT
|
||||
| DEC_LIT
|
||||
| OCT_LIT
|
||||
|
|
@ -383,7 +383,7 @@ The following strings denote other tokens::
|
|||
` ( ) { } [ ] , ; [. .] {. .} (. .)
|
||||
|
||||
|
||||
The `slice`:idx: operator `..`:tok: takes precedence over other tokens that
|
||||
contain a dot: `{..}`:tok: are the three tokens `{`:tok:, `..`:tok:, `}`:tok:
|
||||
The `slice`:idx: operator `..`:tok: takes precedence over other tokens that
|
||||
contain a dot: `{..}`:tok: are the three tokens `{`:tok:, `..`:tok:, `}`:tok:
|
||||
and not the two tokens `{.`:tok:, `.}`:tok:.
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ model low level lockfree mechanisms:
|
|||
.. code-block:: nim
|
||||
var dummyLock {.compileTime.}: int
|
||||
var atomicCounter {.guard: dummyLock.}: int
|
||||
|
||||
|
||||
template atomicRead(x): expr =
|
||||
{.locks: [dummyLock].}:
|
||||
memoryReadBarrier()
|
||||
|
|
@ -135,30 +135,30 @@ Lock levels are used to enforce a global locking order in order to prevent
|
|||
deadlocks at compile-time. A lock level is an constant integer in the range
|
||||
0..1_000. Lock level 0 means that no lock is acquired at all.
|
||||
|
||||
If a section of code holds a lock of level ``M`` than it can also acquire any
|
||||
If a section of code holds a lock of level ``M`` than it can also acquire any
|
||||
lock of level ``N < M``. Another lock of level ``M`` cannot be acquired. Locks
|
||||
of the same level can only be acquired *at the same time* within a
|
||||
of the same level can only be acquired *at the same time* within a
|
||||
single ``locks`` section:
|
||||
|
||||
.. code-block:: nim
|
||||
var a, b: TLock[2]
|
||||
var x: TLock[1]
|
||||
# invalid locking order: TLock[1] cannot be acquired before TLock[2]:
|
||||
{.locks: [x].}:
|
||||
{.locks: [x].}:
|
||||
{.locks: [a].}:
|
||||
...
|
||||
# valid locking order: TLock[2] acquired before TLock[1]:
|
||||
{.locks: [a].}:
|
||||
{.locks: [a].}:
|
||||
{.locks: [x].}:
|
||||
...
|
||||
|
||||
# invalid locking order: TLock[2] acquired before TLock[2]:
|
||||
{.locks: [a].}:
|
||||
{.locks: [a].}:
|
||||
{.locks: [b].}:
|
||||
...
|
||||
|
||||
# valid locking order, locks of the same level acquired at the same time:
|
||||
{.locks: [a, b].}:
|
||||
{.locks: [a, b].}:
|
||||
...
|
||||
|
||||
|
||||
|
|
@ -187,7 +187,7 @@ level. This then means that the routine may acquire locks of up to this level.
|
|||
This is essential so that procs can be called within a ``locks`` section:
|
||||
|
||||
.. code-block:: nim
|
||||
proc p() {.locks: 3.} = discard
|
||||
proc p() {.locks: 3.} = discard
|
||||
|
||||
var a: TLock[4]
|
||||
{.locks: [a].}:
|
||||
|
|
@ -195,6 +195,6 @@ This is essential so that procs can be called within a ``locks`` section:
|
|||
p()
|
||||
|
||||
|
||||
As usual ``locks`` is an inferred effect and there is a subtype
|
||||
As usual ``locks`` is an inferred effect and there is a subtype
|
||||
relation: ``proc () {.locks: N.}`` is a subtype of ``proc () {.locks: M.}``
|
||||
iff (M <= N).
|
||||
|
|
|
|||
|
|
@ -81,8 +81,8 @@ A module alias can be introduced via the ``as`` keyword:
|
|||
|
||||
echo su.format("$1", "lalelu")
|
||||
|
||||
The original module name is then not accessible. The
|
||||
notations ``path/to/module`` or ``path.to.module`` or ``"path/to/module"``
|
||||
The original module name is then not accessible. The
|
||||
notations ``path/to/module`` or ``path.to.module`` or ``"path/to/module"``
|
||||
can be used to refer to a module in subdirectories:
|
||||
|
||||
.. code-block:: nim
|
||||
|
|
@ -104,7 +104,7 @@ Likewise the following does not make sense as the name is ``strutils`` already:
|
|||
From import statement
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
After the ``from`` statement a module name follows followed by
|
||||
After the ``from`` statement a module name follows followed by
|
||||
an ``import`` to list the symbols one likes to use without explict
|
||||
full qualification:
|
||||
|
||||
|
|
@ -115,8 +115,8 @@ full qualification:
|
|||
# always possible: full qualification:
|
||||
echo strutils.replace("abc", "a", "z")
|
||||
|
||||
It's also possible to use ``from module import nil`` if one wants to import
|
||||
the module but wants to enforce fully qualified access to every symbol
|
||||
It's also possible to use ``from module import nil`` if one wants to import
|
||||
the module but wants to enforce fully qualified access to every symbol
|
||||
in ``module``.
|
||||
|
||||
|
||||
|
|
@ -134,15 +134,15 @@ modules don't need to import a module's dependencies:
|
|||
# module A
|
||||
import B
|
||||
export B.MyObject
|
||||
|
||||
|
||||
proc `$`*(x: MyObject): string = "my object"
|
||||
|
||||
|
||||
.. code-block:: nim
|
||||
# module C
|
||||
import A
|
||||
|
||||
# B.MyObject has been imported implicitly here:
|
||||
|
||||
# B.MyObject has been imported implicitly here:
|
||||
var x: MyObject
|
||||
echo($x)
|
||||
|
||||
|
|
@ -185,7 +185,7 @@ following places:
|
|||
Module scope
|
||||
~~~~~~~~~~~~
|
||||
All identifiers of a module are valid from the point of declaration until
|
||||
the end of the module. Identifiers from indirectly dependent modules are *not*
|
||||
the end of the module. Identifiers from indirectly dependent modules are *not*
|
||||
available. The `system`:idx: module is automatically imported in every module.
|
||||
|
||||
If a module imports an identifier by two different modules, each occurrence of
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ current module:
|
|||
Method call syntax
|
||||
------------------
|
||||
|
||||
For object oriented programming, the syntax ``obj.method(args)`` can be used
|
||||
For object oriented programming, the syntax ``obj.method(args)`` can be used
|
||||
instead of ``method(obj, args)``. The parentheses can be omitted if there are no
|
||||
remaining arguments: ``obj.len`` (instead of ``len(obj)``).
|
||||
|
||||
|
|
@ -128,7 +128,7 @@ This method call syntax is not restricted to objects, it can be used
|
|||
to supply any type of first argument for procedures:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
|
||||
echo("abc".len) # is the same as echo(len("abc"))
|
||||
echo("abc".toUpper())
|
||||
echo({'a', 'b', 'c'}.card)
|
||||
|
|
@ -143,11 +143,11 @@ See also: `Limitations of the method call syntax`_.
|
|||
Properties
|
||||
----------
|
||||
Nim has no need for *get-properties*: Ordinary get-procedures that are called
|
||||
with the *method call syntax* achieve the same. But setting a value is
|
||||
with the *method call syntax* achieve the same. But setting a value is
|
||||
different; for this a special setter syntax is needed:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
|
||||
type
|
||||
Socket* = ref object of RootObj
|
||||
FHost: int # cannot be accessed from the outside of the module
|
||||
|
|
@ -157,7 +157,7 @@ different; for this a special setter syntax is needed:
|
|||
proc `host=`*(s: var Socket, value: int) {.inline.} =
|
||||
## setter of hostAddr
|
||||
s.FHost = value
|
||||
|
||||
|
||||
proc host*(s: Socket): int {.inline.} =
|
||||
## getter of hostAddr
|
||||
s.FHost
|
||||
|
|
@ -180,18 +180,18 @@ more argument in this case:
|
|||
.. code-block:: nim
|
||||
proc optarg(x: int, y: int = 0): int = x + y
|
||||
proc singlearg(x: int): int = 20*x
|
||||
|
||||
|
||||
echo optarg 1, " ", singlearg 2 # prints "1 40"
|
||||
|
||||
|
||||
let fail = optarg 1, optarg 8 # Wrong. Too many arguments for a command call
|
||||
let x = optarg(1, optarg 8) # traditional procedure call with 2 arguments
|
||||
let y = 1.optarg optarg 8 # same thing as above, w/o the parenthesis
|
||||
assert x == y
|
||||
|
||||
The command invocation syntax also can't have complex expressions as arguments.
|
||||
For example: (`anonymous procs`_), ``if``, ``case`` or ``try``. The (`do
|
||||
notation`_) is limited, but usable for a single proc (see the example in the
|
||||
corresponding section). Function calls with no arguments still needs () to
|
||||
The command invocation syntax also can't have complex expressions as arguments.
|
||||
For example: (`anonymous procs`_), ``if``, ``case`` or ``try``. The (`do
|
||||
notation`_) is limited, but usable for a single proc (see the example in the
|
||||
corresponding section). Function calls with no arguments still needs () to
|
||||
distinguish between a call and the function itself as a first class value.
|
||||
|
||||
|
||||
|
|
@ -221,7 +221,7 @@ the proc's name.
|
|||
cmp(x.len, y.len))
|
||||
|
||||
|
||||
Procs as expressions can appear both as nested procs and inside top level
|
||||
Procs as expressions can appear both as nested procs and inside top level
|
||||
executable code.
|
||||
|
||||
|
||||
|
|
@ -237,10 +237,10 @@ calls can use the ``do`` keyword:
|
|||
sort(cities) do (x,y: string) -> int:
|
||||
cmp(x.len, y.len)
|
||||
# Less parenthesis using the method plus command syntax:
|
||||
cities = cities.map do (x:string) -> string:
|
||||
cities = cities.map do (x:string) -> string:
|
||||
"City of " & x
|
||||
|
||||
``do`` is written after the parentheses enclosing the regular proc params.
|
||||
``do`` is written after the parentheses enclosing the regular proc params.
|
||||
The proc expression represented by the do block is appended to them.
|
||||
|
||||
More than one ``do`` block can appear in a single call:
|
||||
|
|
@ -261,11 +261,11 @@ Nonoverloadable builtins
|
|||
The following builtin procs cannot be overloaded for reasons of implementation
|
||||
simplicity (they require specialized semantic checking)::
|
||||
|
||||
declared, defined, definedInScope, compiles, low, high, sizeOf,
|
||||
declared, defined, definedInScope, compiles, low, high, sizeOf,
|
||||
is, of, shallowCopy, getAst, astToStr, spawn, procCall
|
||||
|
||||
Thus they act more like keywords than like ordinary identifiers; unlike a
|
||||
keyword however, a redefinition may `shadow`:idx: the definition in
|
||||
Thus they act more like keywords than like ordinary identifiers; unlike a
|
||||
keyword however, a redefinition may `shadow`:idx: the definition in
|
||||
the ``system`` module. From this list the following should not be written in dot
|
||||
notation ``x.f`` since ``x`` cannot be type checked before it gets passed
|
||||
to ``f``::
|
||||
|
|
@ -342,11 +342,11 @@ returned value is an l-value and can be modified by the caller:
|
|||
|
||||
proc WriteAccessToG(): var int =
|
||||
result = g
|
||||
|
||||
|
||||
WriteAccessToG() = 6
|
||||
assert g == 6
|
||||
|
||||
It is a compile time error if the implicitly introduced pointer could be
|
||||
It is a compile time error if the implicitly introduced pointer could be
|
||||
used to access a location beyond its lifetime:
|
||||
|
||||
.. code-block:: nim
|
||||
|
|
@ -354,7 +354,7 @@ used to access a location beyond its lifetime:
|
|||
var g = 0
|
||||
result = g # Error!
|
||||
|
||||
For iterators, a component of a tuple return type can have a ``var`` type too:
|
||||
For iterators, a component of a tuple return type can have a ``var`` type too:
|
||||
|
||||
.. code-block:: nim
|
||||
iterator mpairs(a: var seq[string]): tuple[key: int, val: var string] =
|
||||
|
|
@ -384,28 +384,28 @@ dispatch.
|
|||
x: int
|
||||
PlusExpr = ref object of Expression
|
||||
a, b: Expression
|
||||
|
||||
|
||||
method eval(e: Expression): int =
|
||||
# override this base method
|
||||
quit "to override!"
|
||||
|
||||
|
||||
method eval(e: Literal): int = return e.x
|
||||
|
||||
|
||||
method eval(e: PlusExpr): int =
|
||||
# watch out: relies on dynamic binding
|
||||
result = eval(e.a) + eval(e.b)
|
||||
|
||||
|
||||
proc newLit(x: int): Literal =
|
||||
new(result)
|
||||
result.x = x
|
||||
|
||||
|
||||
proc newPlus(a, b: Expression): PlusExpr =
|
||||
new(result)
|
||||
result.a = a
|
||||
result.b = b
|
||||
|
||||
echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
|
||||
|
||||
|
||||
In the example the constructors ``newLit`` and ``newPlus`` are procs
|
||||
because they should use static binding, but ``eval`` is a method because it
|
||||
requires dynamic binding.
|
||||
|
|
@ -418,24 +418,24 @@ dispatching:
|
|||
Thing = ref object of RootObj
|
||||
Unit = ref object of Thing
|
||||
x: int
|
||||
|
||||
|
||||
method collide(a, b: Thing) {.inline.} =
|
||||
quit "to override!"
|
||||
|
||||
|
||||
method collide(a: Thing, b: Unit) {.inline.} =
|
||||
echo "1"
|
||||
|
||||
|
||||
method collide(a: Unit, b: Thing) {.inline.} =
|
||||
echo "2"
|
||||
|
||||
|
||||
var a, b: Unit
|
||||
new a
|
||||
new b
|
||||
collide(a, b) # output: 2
|
||||
|
||||
|
||||
Invocation of a multi-method cannot be ambiguous: collide 2 is preferred over
|
||||
collide 1 because the resolution works from left to right.
|
||||
Invocation of a multi-method cannot be ambiguous: collide 2 is preferred over
|
||||
collide 1 because the resolution works from left to right.
|
||||
In the example ``Unit, Thing`` is preferred over ``Thing, Unit``.
|
||||
|
||||
**Performance note**: Nim does not produce a virtual method table, but
|
||||
|
|
@ -450,7 +450,7 @@ Iterators and the for statement
|
|||
The `for`:idx: statement is an abstract mechanism to iterate over the elements
|
||||
of a container. It relies on an `iterator`:idx: to do so. Like ``while``
|
||||
statements, ``for`` statements open an `implicit block`:idx:, so that they
|
||||
can be left with a ``break`` statement.
|
||||
can be left with a ``break`` statement.
|
||||
|
||||
The ``for`` loop declares iteration variables - their scope reaches until the
|
||||
end of the loop body. The iteration variables' types are inferred by the
|
||||
|
|
@ -486,7 +486,7 @@ The compiler generates code as if the programmer would have written this:
|
|||
|
||||
If the iterator yields a tuple, there can be as many iteration variables
|
||||
as there are components in the tuple. The i'th iteration variable's type is
|
||||
the type of the i'th component. In other words, implicit tuple unpacking in a
|
||||
the type of the i'th component. In other words, implicit tuple unpacking in a
|
||||
for loop context is supported.
|
||||
|
||||
Implict items/pairs invocations
|
||||
|
|
@ -498,11 +498,11 @@ ie. an ``items`` iterator is implicitly invoked:
|
|||
|
||||
.. code-block:: nim
|
||||
for x in [1,2,3]: echo x
|
||||
|
||||
|
||||
If the for loop has exactly 2 variables, a ``pairs`` iterator is implicitly
|
||||
invoked.
|
||||
|
||||
Symbol lookup of the identifiers ``items``/``pairs`` is performed after
|
||||
Symbol lookup of the identifiers ``items``/``pairs`` is performed after
|
||||
the rewriting step, so that all overloads of ``items``/``pairs`` are taken
|
||||
into account.
|
||||
|
||||
|
|
@ -511,7 +511,7 @@ First class iterators
|
|||
---------------------
|
||||
|
||||
There are 2 kinds of iterators in Nim: *inline* and *closure* iterators.
|
||||
An `inline iterator`:idx: is an iterator that's always inlined by the compiler
|
||||
An `inline iterator`:idx: is an iterator that's always inlined by the compiler
|
||||
leading to zero overhead for the abstraction, but may result in a heavy
|
||||
increase in code size. Inline iterators are second class citizens;
|
||||
They can be passed as parameters only to other inlining code facilities like
|
||||
|
|
@ -522,7 +522,7 @@ In contrast to that, a `closure iterator`:idx: can be passed around more freely:
|
|||
.. code-block:: nim
|
||||
iterator count0(): int {.closure.} =
|
||||
yield 0
|
||||
|
||||
|
||||
iterator count2(): int {.closure.} =
|
||||
var x = 1
|
||||
yield x
|
||||
|
|
@ -539,7 +539,7 @@ Closure iterators have other restrictions than inline iterators:
|
|||
|
||||
1. ``yield`` in a closure iterator can not occur in a ``try`` statement.
|
||||
2. For now, a closure iterator cannot be evaluated at compile time.
|
||||
3. ``return`` is allowed in a closure iterator (but rarely useful) and ends
|
||||
3. ``return`` is allowed in a closure iterator (but rarely useful) and ends
|
||||
iteration.
|
||||
4. Neither inline nor closure iterators can be recursive.
|
||||
|
||||
|
|
@ -547,7 +547,7 @@ Iterators that are neither marked ``{.closure.}`` nor ``{.inline.}`` explicitly
|
|||
default to being inline, but this may change in future versions of the
|
||||
implementation.
|
||||
|
||||
The ``iterator`` type is always of the calling convention ``closure``
|
||||
The ``iterator`` type is always of the calling convention ``closure``
|
||||
implicitly; the following example shows how to use iterators to implement
|
||||
a `collaborative tasking`:idx: system:
|
||||
|
||||
|
|
@ -650,7 +650,7 @@ parameters of an outer factory proc:
|
|||
Converters
|
||||
==========
|
||||
|
||||
A converter is like an ordinary proc except that it enhances
|
||||
A converter is like an ordinary proc except that it enhances
|
||||
the "implicitly convertible" type relation (see `Convertible relation`_):
|
||||
|
||||
.. code-block:: nim
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ templates and macros), depending on the desired effect:
|
|||
The following dot operators are available:
|
||||
|
||||
operator `.`
|
||||
------------
|
||||
------------
|
||||
This operator will be matched against both field accesses and method calls.
|
||||
|
||||
operator `.()`
|
||||
|
|
|
|||
|
|
@ -176,8 +176,8 @@ The rules for compile-time computability are:
|
|||
1. Literals are compile-time computable.
|
||||
2. Type conversions are compile-time computable.
|
||||
3. Procedure calls of the form ``p(X)`` are compile-time computable if
|
||||
``p`` is a proc without side-effects (see the `noSideEffect pragma
|
||||
<#pragmas-nosideeffect-pragma>`_ for details) and if ``X`` is a
|
||||
``p`` is a proc without side-effects (see the `noSideEffect pragma
|
||||
<#pragmas-nosideeffect-pragma>`_ for details) and if ``X`` is a
|
||||
(possibly empty) list of compile-time computable arguments.
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
Taint mode
|
||||
==========
|
||||
|
||||
The Nim compiler and most parts of the standard library support
|
||||
a taint mode. Input strings are declared with the `TaintedString`:idx:
|
||||
The Nim compiler and most parts of the standard library support
|
||||
a taint mode. Input strings are declared with the `TaintedString`:idx:
|
||||
string type declared in the ``system`` module.
|
||||
|
||||
If the taint mode is turned on (via the ``--taintMode:on`` command line
|
||||
If the taint mode is turned on (via the ``--taintMode:on`` command line
|
||||
option) it is a distinct string type which helps to detect input
|
||||
validation errors:
|
||||
|
||||
|
|
@ -13,7 +13,7 @@ validation errors:
|
|||
echo "your name: "
|
||||
var name: TaintedString = stdin.readline
|
||||
# it is safe here to output the name without any input validation, so
|
||||
# we simply convert `name` to string to make the compiler happy:
|
||||
# we simply convert `name` to string to make the compiler happy:
|
||||
echo "hi, ", name.string
|
||||
|
||||
If the taint mode is turned off, ``TaintedString`` is simply an alias for
|
||||
|
|
|
|||
|
|
@ -16,17 +16,17 @@ Example:
|
|||
|
||||
assert(5 != 6) # the compiler rewrites that to: assert(not (5 == 6))
|
||||
|
||||
The ``!=``, ``>``, ``>=``, ``in``, ``notin``, ``isnot`` operators are in fact
|
||||
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)``.
|
||||
| ``a in b`` is transformed into ``contains(b, a)``.
|
||||
| ``notin`` and ``isnot`` have the obvious meanings.
|
||||
|
||||
The "types" of templates can be the symbols ``expr`` (stands for *expression*),
|
||||
``stmt`` (stands for *statement*) 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 expressions are
|
||||
The "types" of templates can be the symbols ``expr`` (stands for *expression*),
|
||||
``stmt`` (stands for *statement*) 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 expressions are
|
||||
expected.
|
||||
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ So ordinary templates cannot receive undeclared identifiers:
|
|||
|
||||
.. code-block:: nim
|
||||
|
||||
template declareInt(x: expr) =
|
||||
template declareInt(x: expr) =
|
||||
var x: int
|
||||
|
||||
declareInt(x) # error: unknown identifier: 'x'
|
||||
|
|
@ -51,7 +51,7 @@ receive undeclared identifiers:
|
|||
|
||||
.. code-block:: nim
|
||||
|
||||
template declareInt(x: expr) {.immediate.} =
|
||||
template declareInt(x: expr) {.immediate.} =
|
||||
var x: int
|
||||
|
||||
declareInt(x) # valid
|
||||
|
|
@ -75,11 +75,11 @@ special ``:`` syntax:
|
|||
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.
|
||||
|
||||
|
|
@ -92,9 +92,9 @@ bound from the definition scope of the template:
|
|||
|
||||
.. code-block:: nim
|
||||
# Module A
|
||||
var
|
||||
var
|
||||
lastId = 0
|
||||
|
||||
|
||||
template genId*: expr =
|
||||
inc(lastId)
|
||||
lastId
|
||||
|
|
@ -102,10 +102,10 @@ bound from the definition scope of the template:
|
|||
.. 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``
|
||||
As in generics symbol binding can be influenced via ``mixin`` or ``bind``
|
||||
statements.
|
||||
|
||||
|
||||
|
|
@ -117,11 +117,11 @@ In templates identifiers can be constructed with the backticks notation:
|
|||
|
||||
.. code-block:: nim
|
||||
|
||||
template typedef(name: expr, typ: typedesc) {.immediate.} =
|
||||
template typedef(name: expr, typ: typedesc) {.immediate.} =
|
||||
type
|
||||
`T name`* {.inject.} = typ
|
||||
`P name`* {.inject.} = ref `T name`
|
||||
|
||||
|
||||
typedef(myint, int)
|
||||
var x: PMyInt
|
||||
|
||||
|
|
@ -150,7 +150,7 @@ shadowed by the same argument name even when fully qualified:
|
|||
|
||||
tstLev(levA)
|
||||
# produces: 'levA levA'
|
||||
|
||||
|
||||
But the global symbol can properly be captured by a ``bind`` statement:
|
||||
|
||||
.. code-block:: nim
|
||||
|
|
@ -177,14 +177,14 @@ 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): expr =
|
||||
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)
|
||||
|
|
@ -196,7 +196,7 @@ 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
|
||||
``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
|
||||
|
|
@ -204,7 +204,7 @@ template parameter, it is an inject'ed symbol:
|
|||
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")
|
||||
|
|
@ -215,7 +215,7 @@ 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
|
||||
|
||||
|
|
@ -334,9 +334,9 @@ 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
|
||||
``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``
|
||||
(aka `symbols`:idx:) instead of using unbound identifiers. The ``bindSym``
|
||||
builtin can be used for that:
|
||||
|
||||
.. code-block:: nim
|
||||
|
|
@ -418,8 +418,8 @@ powerful programming construct that still suffices. So the "check list" is:
|
|||
Macros as pragmas
|
||||
-----------------
|
||||
|
||||
Whole routines (procs, iterators etc.) can also be passed to a template or
|
||||
a macro via the pragma notation:
|
||||
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: stmt) = discard
|
||||
|
|
|
|||
|
|
@ -3,32 +3,32 @@ Term rewriting macros
|
|||
|
||||
Term rewriting macros are macros or templates that have not only
|
||||
a *name* but also a *pattern* that is searched for after the semantic checking
|
||||
phase of the compiler: This means they provide an easy way to enhance the
|
||||
phase of the compiler: This means they provide an easy way to enhance the
|
||||
compilation pipeline with user defined optimizations:
|
||||
|
||||
.. code-block:: nim
|
||||
template optMul{`*`(a, 2)}(a: int): int = a+a
|
||||
|
||||
|
||||
let x = 3
|
||||
echo x * 2
|
||||
|
||||
The compiler now rewrites ``x * 2`` as ``x + x``. The code inside the
|
||||
curlies is the pattern to match against. The operators ``*``, ``**``,
|
||||
``|``, ``~`` have a special meaning in patterns if they are written in infix
|
||||
``|``, ``~`` have a special meaning in patterns if they are written in infix
|
||||
notation, so to match verbatim against ``*`` the ordinary function call syntax
|
||||
needs to be used.
|
||||
|
||||
|
||||
Unfortunately optimizations are hard to get right and even the tiny example
|
||||
is **wrong**:
|
||||
is **wrong**:
|
||||
|
||||
.. code-block:: nim
|
||||
template optMul{`*`(a, 2)}(a: int): int = a+a
|
||||
|
||||
|
||||
proc f(): int =
|
||||
echo "side effect!"
|
||||
result = 55
|
||||
|
||||
|
||||
echo f() * 2
|
||||
|
||||
We cannot duplicate 'a' if it denotes an expression that has a side effect!
|
||||
|
|
@ -36,11 +36,11 @@ Fortunately Nim supports side effect analysis:
|
|||
|
||||
.. code-block:: nim
|
||||
template optMul{`*`(a, 2)}(a: int{noSideEffect}): int = a+a
|
||||
|
||||
|
||||
proc f(): int =
|
||||
echo "side effect!"
|
||||
result = 55
|
||||
|
||||
|
||||
echo f() * 2 # not optimized ;-)
|
||||
|
||||
You can make one overload matching with a constraint and one without, and the
|
||||
|
|
@ -53,13 +53,13 @@ blindly:
|
|||
|
||||
.. code-block:: nim
|
||||
template mulIsCommutative{`*`(a, b)}(a, b: int): int = b*a
|
||||
|
||||
|
||||
What optimizers really need to do is a *canonicalization*:
|
||||
|
||||
.. code-block:: nim
|
||||
template canonMul{`*`(a, b)}(a: int{lit}, b: int): int = b*a
|
||||
|
||||
The ``int{lit}`` parameter pattern matches against an expression of
|
||||
The ``int{lit}`` parameter pattern matches against an expression of
|
||||
type ``int``, but only if it's a literal.
|
||||
|
||||
|
||||
|
|
@ -67,7 +67,7 @@ type ``int``, but only if it's a literal.
|
|||
Parameter constraints
|
||||
---------------------
|
||||
|
||||
The `parameter constraint`:idx: expression can use the operators ``|`` (or),
|
||||
The `parameter constraint`:idx: expression can use the operators ``|`` (or),
|
||||
``&`` (and) and ``~`` (not) and the following predicates:
|
||||
|
||||
=================== =====================================================
|
||||
|
|
@ -75,7 +75,7 @@ Predicate Meaning
|
|||
=================== =====================================================
|
||||
``atom`` The matching node has no children.
|
||||
``lit`` The matching node is a literal like "abc", 12.
|
||||
``sym`` The matching node must be a symbol (a bound
|
||||
``sym`` The matching node must be a symbol (a bound
|
||||
identifier).
|
||||
``ident`` The matching node must be an identifier (an unbound
|
||||
identifier).
|
||||
|
|
@ -101,15 +101,15 @@ Predicate Meaning
|
|||
``enumfield`` A symbol which is a field in an enumeration.
|
||||
``forvar`` A for loop variable.
|
||||
``label`` A label (used in ``block`` statements).
|
||||
``nk*`` The matching AST must have the specified kind.
|
||||
``nk*`` The matching AST must have the specified kind.
|
||||
(Example: ``nkIfStmt`` denotes an ``if`` statement.)
|
||||
``alias`` States that the marked parameter needs to alias
|
||||
``alias`` States that the marked parameter needs to alias
|
||||
with *some* other parameter.
|
||||
``noalias`` States that *every* other parameter must not alias
|
||||
with the marked parameter.
|
||||
=================== =====================================================
|
||||
|
||||
Predicates that share their name with a keyword have to be escaped with
|
||||
Predicates that share their name with a keyword have to be escaped with
|
||||
backticks: `` `const` ``.
|
||||
The ``alias`` and ``noalias`` predicates refer not only to the matching AST,
|
||||
but also to every other bound parameter; syntactically they need to occur after
|
||||
|
|
@ -151,14 +151,14 @@ constant folding, so the following does not work:
|
|||
The reason is that the compiler already transformed the 1 into "1" for
|
||||
the ``echo`` statement. However, a term rewriting macro should not change the
|
||||
semantics anyway. In fact they can be deactivated with the ``--patterns:off``
|
||||
command line option or temporarily with the ``patterns`` pragma.
|
||||
command line option or temporarily with the ``patterns`` pragma.
|
||||
|
||||
|
||||
The ``{}`` operator
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A pattern expression can be bound to a pattern parameter via the ``expr{param}``
|
||||
notation:
|
||||
notation:
|
||||
|
||||
.. code-block:: nim
|
||||
template t{(0|1|2){x}}(x: expr): expr = x+1
|
||||
|
|
@ -176,7 +176,7 @@ The ``~`` operator is the **not** operator in patterns:
|
|||
template t{x = (~x){y} and (~x){z}}(x, y, z: bool): stmt =
|
||||
x = y
|
||||
if x: x = z
|
||||
|
||||
|
||||
var
|
||||
a = false
|
||||
b = true
|
||||
|
|
@ -189,12 +189,12 @@ The ``*`` operator
|
|||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The ``*`` operator can *flatten* a nested binary expression like ``a & b & c``
|
||||
to ``&(a, b, c)``:
|
||||
to ``&(a, b, c)``:
|
||||
|
||||
.. code-block:: nim
|
||||
var
|
||||
calls = 0
|
||||
|
||||
|
||||
proc `&&`(s: varargs[string]): string =
|
||||
result = s[0]
|
||||
for i in 1..len(s)-1: result.add s[i]
|
||||
|
|
@ -211,8 +211,8 @@ to ``&(a, b, c)``:
|
|||
|
||||
The second operator of `*` must be a parameter; it is used to gather all the
|
||||
arguments. The expression ``"my" && (space & "awe" && "some " ) && "concat"``
|
||||
is passed to ``optConc`` in ``a`` as a special list (of kind ``nkArgList``)
|
||||
which is flattened into a call expression; thus the invocation of ``optConc``
|
||||
is passed to ``optConc`` in ``a`` as a special list (of kind ``nkArgList``)
|
||||
which is flattened into a call expression; thus the invocation of ``optConc``
|
||||
produces:
|
||||
|
||||
.. code-block:: nim
|
||||
|
|
@ -245,7 +245,7 @@ all the arguments, but also the matched operators in reverse polish notation:
|
|||
|
||||
var x, y, z: Matrix
|
||||
|
||||
echo x + y * z - x
|
||||
echo x + y * z - x
|
||||
|
||||
This passes the expression ``x + y * z - x`` to the ``optM`` macro as
|
||||
an ``nnkArgList`` node containing::
|
||||
|
|
@ -265,7 +265,7 @@ an ``nnkArgList`` node containing::
|
|||
Parameters
|
||||
----------
|
||||
|
||||
Parameters in a pattern are type checked in the matching process. If a
|
||||
Parameters in a pattern are type checked in the matching process. If a
|
||||
parameter is of the type ``varargs`` it is treated specially and it can match
|
||||
0 or more arguments in the AST to be matched against:
|
||||
|
||||
|
|
@ -275,7 +275,7 @@ parameter is of the type ``varargs`` it is treated specially and it can match
|
|||
((write|writeLine){w})(f, y)
|
||||
}(x, y: varargs[expr], f: File, w: expr) =
|
||||
w(f, x, y)
|
||||
|
||||
|
||||
|
||||
|
||||
Example: Partial evaluation
|
||||
|
|
@ -310,7 +310,7 @@ The following example shows how some form of hoisting can be implemented:
|
|||
|
||||
The ``optPeg`` template optimizes the case of a peg constructor with a string
|
||||
literal, so that the pattern will only be parsed once at program startup and
|
||||
stored in a global ``gl`` which is then re-used. This optimization is called
|
||||
stored in a global ``gl`` which is then re-used. This optimization is called
|
||||
hoisting because it is comparable to classical loop hoisting.
|
||||
|
||||
|
||||
|
|
@ -343,7 +343,7 @@ ordinary routines.
|
|||
Move optimization
|
||||
-----------------
|
||||
|
||||
The ``call`` constraint is particularly useful to implement a move
|
||||
The ``call`` constraint is particularly useful to implement a move
|
||||
optimization for types that have copying semantics:
|
||||
|
||||
.. code-block:: nim
|
||||
|
|
|
|||
|
|
@ -301,7 +301,7 @@ matches better than just ``T`` then.
|
|||
proc sayHi(x: var int): string =
|
||||
# matches a var int
|
||||
result = $(x + 10)
|
||||
|
||||
|
||||
proc sayHello(x: int) =
|
||||
var m = x # a mutable version of x
|
||||
echo sayHi(x) # matches the non-var version of sayHi
|
||||
|
|
|
|||
|
|
@ -18,6 +18,6 @@ Example:
|
|||
A type section begins with the ``type`` keyword. It contains multiple
|
||||
type definitions. A type definition binds a type to a name. Type definitions
|
||||
can be recursive or even mutually recursive. Mutually recursive types are only
|
||||
possible within a single ``type`` section. Nominal types like ``objects``
|
||||
possible within a single ``type`` section. Nominal types like ``objects``
|
||||
or ``enums`` can only be defined in a ``type`` section.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue