Skip to content

Commit

Permalink
exp. features now in correct manual, closes nim-lang#11932 (nim-lang#…
Browse files Browse the repository at this point in the history
  • Loading branch information
metagn authored and EchoPouet committed Jun 13, 2020
1 parent 8ade84d commit 81fbdfd
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 171 deletions.
170 changes: 3 additions & 167 deletions doc/manual.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1734,22 +1734,9 @@ dereferencing operations for reference types:
n.data = 9
# no need to write n[].data; in fact n[].data is highly discouraged!
Automatic dereferencing is also performed for the first argument of a routine
call. But currently this feature has to be only enabled
via ``{.experimental: "implicitDeref".}``:

.. code-block:: nim
{.experimental: "implicitDeref".}
proc depth(x: NodeObj): int = ...
var
n: Node
new(n)
echo n.depth
# no need to write n[].depth either
Automatic dereferencing can be performed for the first argument of a routine
call, but this is an experimental feature and is described `here
<manual_experimental.html#type-bound-operations>`_.

In order to simplify structural type checking, recursive tuples are not valid:

Expand Down Expand Up @@ -5341,52 +5328,6 @@ powerful programming construct that still suffices. So the "check list" is:
(4) Else: Use a macro.


For Loop Macro
--------------

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
{.experimental: "forLoopMacros".}
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
# now wrap the whole macro in a block to create a new scope
result = quote do:
block: `result`
for a, b in enumerate(items([1, 2, 3])):
echo a, " ", b
# without wrapping the macro in a block, we'd need to choose different
# names for `a` and `b` here to avoid redefinition errors
for a, b in enumerate([1, 2, 3, 5]):
echo a, " ", b
Currently for loop macros must be enabled explicitly
via ``{.experimental: "forLoopMacros".}``.


Special Types
=============

Expand Down Expand Up @@ -5822,111 +5763,6 @@ iterator in which case the overloading resolution takes place:
var x = 4
write(stdout, x) # not ambiguous: uses the module C's x
Code reordering
~~~~~~~~~~~~~~~

**Note**: Code reordering is experimental and must be enabled via the
``{.experimental.}`` pragma.

The code reordering feature can implicitly rearrange procedure, template, and
macro definitions along with variable declarations and initializations at the top
level scope so that, to a large extent, a programmer should not have to worry
about ordering definitions correctly or be forced to use forward declarations to
preface definitions inside a module.

..
NOTE: The following was documentation for the code reordering precursor,
which was {.noForward.}.
In this mode, procedure definitions may appear out of order and the compiler
will postpone their semantic analysis and compilation until it actually needs
to generate code using the definitions. In this regard, this mode is similar
to the modus operandi of dynamic scripting languages, where the function
calls are not resolved until the code is executed. Here is the detailed
algorithm taken by the compiler:

1. When a callable symbol is first encountered, the compiler will only note
the symbol callable name and it will add it to the appropriate overload set
in the current scope. At this step, it won't try to resolve any of the type
expressions used in the signature of the symbol (so they can refer to other
not yet defined symbols).

2. When a top level call is encountered (usually at the very end of the
module), the compiler will try to determine the actual types of all of the
symbols in the matching overload set. This is a potentially recursive process
as the signatures of the symbols may include other call expressions, whose
types will be resolved at this point too.

3. Finally, after the best overload is picked, the compiler will start
compiling the body of the respective symbol. This in turn will lead the
compiler to discover more call expressions that need to be resolved and steps
2 and 3 will be repeated as necessary.

Please note that if a callable symbol is never used in this scenario, its
body will never be compiled. This is the default behavior leading to best
compilation times, but if exhaustive compilation of all definitions is
required, using ``nim check`` provides this option as well.

Example:

.. code-block:: nim
{.experimental: "codeReordering".}
proc foo(x: int) =
bar(x)
proc bar(x: int) =
echo(x)
foo(10)
Variables can also be reordered as well. Variables that are *initialized* (i.e.
variables that have their declaration and assignment combined in a single
statement) can have their entire initialization statement reordered. Be wary of
what code is executed at the top level:

.. code-block:: nim
{.experimental: "codeReordering".}
proc a() =
echo(foo)
var foo = 5
a() # outputs: "5"
..
TODO: Let's table this for now. This is an *experimental feature* and so the
specific manner in which ``declared`` operates with it can be decided in
eventuality, because right now it works a bit weirdly.
The values of expressions involving ``declared`` are decided *before* the
code reordering process, and not after. As an example, the output of this
code is the same as it would be with code reordering disabled.

.. code-block:: nim
{.experimental: "codeReordering".}
proc x() =
echo(declared(foo))
var foo = 4
x() # "false"
It is important to note that reordering *only* works for symbols at top level
scope. Therefore, the following will *fail to compile:*

.. code-block:: nim
{.experimental: "codeReordering".}
proc a() =
b()
proc b() =
echo("Hello!")
a()
Compiler Messages
=================
Expand Down
185 changes: 181 additions & 4 deletions doc/manual_experimental.rst
Original file line number Diff line number Diff line change
Expand Up @@ -203,10 +203,122 @@ useful only when interfacing with imported types having such semantics.
Automatic dereferencing
=======================

If the `experimental mode <manual.html#pragmas-experimental-pragma>`_ is active
and no other match is found, the first argument ``a`` is dereferenced
automatically if it's a pointer type and overloading resolution is tried
with ``a[]`` instead.
Automatic dereferencing is performed for the first argument of a routine call.
This feature has to be only enabled via ``{.experimental: "implicitDeref".}``:

.. code-block:: nim
{.experimental: "implicitDeref".}
proc depth(x: NodeObj): int = ...
var
n: Node
new(n)
echo n.depth
# no need to write n[].depth either
Code reordering
===============

The code reordering feature can implicitly rearrange procedure, template, and
macro definitions along with variable declarations and initializations at the top
level scope so that, to a large extent, a programmer should not have to worry
about ordering definitions correctly or be forced to use forward declarations to
preface definitions inside a module.

..
NOTE: The following was documentation for the code reordering precursor,
which was {.noForward.}.
In this mode, procedure definitions may appear out of order and the compiler
will postpone their semantic analysis and compilation until it actually needs
to generate code using the definitions. In this regard, this mode is similar
to the modus operandi of dynamic scripting languages, where the function
calls are not resolved until the code is executed. Here is the detailed
algorithm taken by the compiler:

1. When a callable symbol is first encountered, the compiler will only note
the symbol callable name and it will add it to the appropriate overload set
in the current scope. At this step, it won't try to resolve any of the type
expressions used in the signature of the symbol (so they can refer to other
not yet defined symbols).

2. When a top level call is encountered (usually at the very end of the
module), the compiler will try to determine the actual types of all of the
symbols in the matching overload set. This is a potentially recursive process
as the signatures of the symbols may include other call expressions, whose
types will be resolved at this point too.

3. Finally, after the best overload is picked, the compiler will start
compiling the body of the respective symbol. This in turn will lead the
compiler to discover more call expressions that need to be resolved and steps
2 and 3 will be repeated as necessary.

Please note that if a callable symbol is never used in this scenario, its
body will never be compiled. This is the default behavior leading to best
compilation times, but if exhaustive compilation of all definitions is
required, using ``nim check`` provides this option as well.

Example:

.. code-block:: nim
{.experimental: "codeReordering".}
proc foo(x: int) =
bar(x)
proc bar(x: int) =
echo(x)
foo(10)
Variables can also be reordered as well. Variables that are *initialized* (i.e.
variables that have their declaration and assignment combined in a single
statement) can have their entire initialization statement reordered. Be wary of
what code is executed at the top level:

.. code-block:: nim
{.experimental: "codeReordering".}
proc a() =
echo(foo)
var foo = 5
a() # outputs: "5"
..
TODO: Let's table this for now. This is an *experimental feature* and so the
specific manner in which ``declared`` operates with it can be decided in
eventuality, because right now it works a bit weirdly.
The values of expressions involving ``declared`` are decided *before* the
code reordering process, and not after. As an example, the output of this
code is the same as it would be with code reordering disabled.

.. code-block:: nim
{.experimental: "codeReordering".}
proc x() =
echo(declared(foo))
var foo = 4
x() # "false"
It is important to note that reordering *only* works for symbols at top level
scope. Therefore, the following will *fail to compile:*

.. code-block:: nim
{.experimental: "codeReordering".}
proc a() =
b()
proc b() =
echo("Hello!")
a()
Automatic self insertions
Expand Down Expand Up @@ -245,6 +357,25 @@ if no other interpretation of the call is possible:
echo self, self.childField, " ", sumFields(self)
Named argument overloading
==========================

Routines with the same type signature can be called differently if a parameter
has different names. This does not need an ``experimental`` switch, but is an
unstable feature.

.. code-block::nim
proc foo(x: int) =
echo "Using x: ", x
proc foo(y: int) =
echo "Using y: ", y
foo(x = 2)
# Using x: 2
foo(y = 2)
# Using y: 2
Do notation
===========

Expand Down Expand Up @@ -899,6 +1030,52 @@ but only the statement's selector expression is used to determine which
macro to call.


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
{.experimental: "forLoopMacros".}
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
# now wrap the whole macro in a block to create a new scope
result = quote do:
block: `result`
for a, b in enumerate(items([1, 2, 3])):
echo a, " ", b
# without wrapping the macro in a block, we'd need to choose different
# names for `a` and `b` here to avoid redefinition errors
for a, b in enumerate([1, 2, 3, 5]):
echo a, " ", b
Currently for loop macros must be enabled explicitly
via ``{.experimental: "forLoopMacros".}``.


Term rewriting macros
=====================

Expand Down
Loading

0 comments on commit 81fbdfd

Please sign in to comment.