Markdown code blocks part 4 (#20189)
No logic was added, just 8 more files have been migrated.
This commit is contained in:
parent
8155837cde
commit
713f39083e
9 changed files with 365 additions and 341 deletions
|
|
@ -18,11 +18,11 @@ General Guidelines
|
||||||
* (debatable) In nim sources, for links, prefer ``[link text](link.html)`` to `\`link text<link.html>\`_`:code:
|
* (debatable) In nim sources, for links, prefer ``[link text](link.html)`` to `\`link text<link.html>\`_`:code:
|
||||||
since the syntax is simpler and markdown is more common (likewise, `nim rst2html`:cmd: also supports it in ``rst`` files).
|
since the syntax is simpler and markdown is more common (likewise, `nim rst2html`:cmd: also supports it in ``rst`` files).
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
proc someproc*(s: string, foo: int) =
|
proc someproc*(s: string, foo: int) =
|
||||||
## Use single backticks for inline code, e.g.: `s` or `someExpr(true)`.
|
## Use single backticks for inline code, e.g.: `s` or `someExpr(true)`.
|
||||||
## Use a backlash to follow with alphanumeric char: `int8`\s are great.
|
## Use a backlash to follow with alphanumeric char: `int8`\s are great.
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Module-level documentation
|
Module-level documentation
|
||||||
|
|
@ -32,23 +32,24 @@ Documentation of a module is placed at the top of the module itself. Each line o
|
||||||
Sometimes `##[ multiline docs containing code ]##` is preferable, see ``lib/pure/times.nim``.
|
Sometimes `##[ multiline docs containing code ]##` is preferable, see ``lib/pure/times.nim``.
|
||||||
Code samples are encouraged, and should follow the general RST syntax:
|
Code samples are encouraged, and should follow the general RST syntax:
|
||||||
|
|
||||||
.. code-block:: Nim
|
````Nim
|
||||||
|
|
||||||
## The `universe` module computes the answer to life, the universe, and everything.
|
## The `universe` module computes the answer to life, the universe, and everything.
|
||||||
##
|
##
|
||||||
## .. code-block::
|
## ```
|
||||||
## doAssert computeAnswerString() == 42
|
## doAssert computeAnswerString() == 42
|
||||||
|
## ```
|
||||||
|
````
|
||||||
|
|
||||||
|
|
||||||
Within this top-level comment, you can indicate the authorship and copyright of the code, which will be featured in the produced documentation.
|
Within this top-level comment, you can indicate the authorship and copyright of the code, which will be featured in the produced documentation.
|
||||||
|
|
||||||
.. code-block:: Nim
|
```Nim
|
||||||
|
|
||||||
## This is the best module ever. It provides answers to everything!
|
## This is the best module ever. It provides answers to everything!
|
||||||
##
|
##
|
||||||
## :Author: Steve McQueen
|
## :Author: Steve McQueen
|
||||||
## :Copyright: 1965
|
## :Copyright: 1965
|
||||||
##
|
##
|
||||||
|
```
|
||||||
|
|
||||||
Leave a space between the last line of top-level documentation and the beginning of Nim code (the imports, etc.).
|
Leave a space between the last line of top-level documentation and the beginning of Nim code (the imports, etc.).
|
||||||
|
|
||||||
|
|
@ -57,28 +58,29 @@ Procs, Templates, Macros, Converters, and Iterators
|
||||||
|
|
||||||
The documentation of a procedure should begin with a capital letter and should be in present tense. Variables referenced in the documentation should be surrounded by single tick marks:
|
The documentation of a procedure should begin with a capital letter and should be in present tense. Variables referenced in the documentation should be surrounded by single tick marks:
|
||||||
|
|
||||||
.. code-block:: Nim
|
```Nim
|
||||||
|
|
||||||
proc example1*(x: int) =
|
proc example1*(x: int) =
|
||||||
## Prints the value of `x`.
|
## Prints the value of `x`.
|
||||||
echo x
|
echo x
|
||||||
|
```
|
||||||
|
|
||||||
Whenever an example of usage would be helpful to the user, you should include one within the documentation in RST format as below.
|
Whenever an example of usage would be helpful to the user, you should include one within the documentation in RST format as below.
|
||||||
|
|
||||||
.. code-block:: Nim
|
````Nim
|
||||||
|
|
||||||
proc addThree*(x, y, z: int8): int =
|
proc addThree*(x, y, z: int8): int =
|
||||||
## Adds three `int8` values, treating them as unsigned and
|
## Adds three `int8` values, treating them as unsigned and
|
||||||
## truncating the result.
|
## truncating the result.
|
||||||
##
|
##
|
||||||
## .. code-block::
|
## ```
|
||||||
## # things that aren't suitable for a `runnableExamples` go in code-block:
|
## # things that aren't suitable for a `runnableExamples` go in code-block:
|
||||||
## echo execCmdEx("git pull")
|
## echo execCmdEx("git pull")
|
||||||
## drawOnScreen()
|
## drawOnScreen()
|
||||||
|
## ```
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
# `runnableExamples` is usually preferred to ``code-block``, when possible.
|
# `runnableExamples` is usually preferred to ``code-block``, when possible.
|
||||||
doAssert addThree(3, 125, 6) == -122
|
doAssert addThree(3, 125, 6) == -122
|
||||||
result = x +% y +% z
|
result = x +% y +% z
|
||||||
|
````
|
||||||
|
|
||||||
The command `nim doc`:cmd: will then correctly syntax highlight the Nim code within the documentation.
|
The command `nim doc`:cmd: will then correctly syntax highlight the Nim code within the documentation.
|
||||||
|
|
||||||
|
|
@ -87,8 +89,7 @@ Types
|
||||||
|
|
||||||
Exported types should also be documented. This documentation can also contain code samples, but those are better placed with the functions to which they refer.
|
Exported types should also be documented. This documentation can also contain code samples, but those are better placed with the functions to which they refer.
|
||||||
|
|
||||||
.. code-block:: Nim
|
```Nim
|
||||||
|
|
||||||
type
|
type
|
||||||
NamedQueue*[T] = object ## Provides a linked data structure with names
|
NamedQueue*[T] = object ## Provides a linked data structure with names
|
||||||
## throughout. It is named for convenience. I'm making
|
## throughout. It is named for convenience. I'm making
|
||||||
|
|
@ -96,12 +97,12 @@ Exported types should also be documented. This documentation can also contain co
|
||||||
name*: string ## The name of the item
|
name*: string ## The name of the item
|
||||||
val*: T ## Its value
|
val*: T ## Its value
|
||||||
next*: ref NamedQueue[T] ## The next item in the queue
|
next*: ref NamedQueue[T] ## The next item in the queue
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
You have some flexibility when placing the documentation:
|
You have some flexibility when placing the documentation:
|
||||||
|
|
||||||
.. code-block:: Nim
|
```Nim
|
||||||
|
|
||||||
type
|
type
|
||||||
NamedQueue*[T] = object
|
NamedQueue*[T] = object
|
||||||
## Provides a linked data structure with names
|
## Provides a linked data structure with names
|
||||||
|
|
@ -110,11 +111,11 @@ You have some flexibility when placing the documentation:
|
||||||
name*: string ## The name of the item
|
name*: string ## The name of the item
|
||||||
val*: T ## Its value
|
val*: T ## Its value
|
||||||
next*: ref NamedQueue[T] ## The next item in the queue
|
next*: ref NamedQueue[T] ## The next item in the queue
|
||||||
|
```
|
||||||
|
|
||||||
Make sure to place the documentation beside or within the object.
|
Make sure to place the documentation beside or within the object.
|
||||||
|
|
||||||
.. code-block:: Nim
|
```Nim
|
||||||
|
|
||||||
type
|
type
|
||||||
## Bad: this documentation disappears because it annotates the `type` keyword
|
## Bad: this documentation disappears because it annotates the `type` keyword
|
||||||
## above, not `NamedQueue`.
|
## above, not `NamedQueue`.
|
||||||
|
|
@ -123,14 +124,14 @@ Make sure to place the documentation beside or within the object.
|
||||||
## is not what we want.
|
## is not what we want.
|
||||||
val*: T ## Its value
|
val*: T ## Its value
|
||||||
next*: ref NamedQueue[T] ## The next item in the queue
|
next*: ref NamedQueue[T] ## The next item in the queue
|
||||||
|
```
|
||||||
|
|
||||||
Var, Let, and Const
|
Var, Let, and Const
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
When declaring module-wide constants and values, documentation is encouraged. The placement of doc comments is similar to the `type` sections.
|
When declaring module-wide constants and values, documentation is encouraged. The placement of doc comments is similar to the `type` sections.
|
||||||
|
|
||||||
.. code-block:: Nim
|
```Nim
|
||||||
|
|
||||||
const
|
const
|
||||||
X* = 42 ## An awesome number.
|
X* = 42 ## An awesome number.
|
||||||
SpreadArray* = [
|
SpreadArray* = [
|
||||||
|
|
@ -138,25 +139,26 @@ When declaring module-wide constants and values, documentation is encouraged. Th
|
||||||
[2,3,1],
|
[2,3,1],
|
||||||
[3,1,2],
|
[3,1,2],
|
||||||
] ## Doc comment for `SpreadArray`.
|
] ## Doc comment for `SpreadArray`.
|
||||||
|
```
|
||||||
|
|
||||||
Placement of comments in other areas is usually allowed, but will not become part of the documentation output and should therefore be prefaced by a single hash (`#`).
|
Placement of comments in other areas is usually allowed, but will not become part of the documentation output and should therefore be prefaced by a single hash (`#`).
|
||||||
|
|
||||||
.. code-block:: Nim
|
```Nim
|
||||||
|
|
||||||
const
|
const
|
||||||
BadMathVals* = [
|
BadMathVals* = [
|
||||||
3.14, # pi
|
3.14, # pi
|
||||||
2.72, # e
|
2.72, # e
|
||||||
0.58, # gamma
|
0.58, # gamma
|
||||||
] ## A bunch of badly rounded values.
|
] ## A bunch of badly rounded values.
|
||||||
|
```
|
||||||
|
|
||||||
Nim supports Unicode in comments, so the above can be replaced with the following:
|
Nim supports Unicode in comments, so the above can be replaced with the following:
|
||||||
|
|
||||||
.. code-block:: Nim
|
```Nim
|
||||||
|
|
||||||
const
|
const
|
||||||
BadMathVals* = [
|
BadMathVals* = [
|
||||||
3.14, # π
|
3.14, # π
|
||||||
2.72, # e
|
2.72, # e
|
||||||
0.58, # γ
|
0.58, # γ
|
||||||
] ## A bunch of badly rounded values (including π!).
|
] ## A bunch of badly rounded values (including π!).
|
||||||
|
```
|
||||||
|
|
|
||||||
16
doc/drnim.md
16
doc/drnim.md
|
|
@ -22,11 +22,11 @@ DrNim's command-line options are the same as the Nim compiler's.
|
||||||
DrNim currently only checks the sections of your code that are marked
|
DrNim currently only checks the sections of your code that are marked
|
||||||
via `staticBoundChecks: on`:
|
via `staticBoundChecks: on`:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
{.push staticBoundChecks: on.}
|
{.push staticBoundChecks: on.}
|
||||||
# <--- code section here ---->
|
# <--- code section here ---->
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
```
|
||||||
|
|
||||||
DrNim currently only tries to prove array indexing or subrange checks,
|
DrNim currently only tries to prove array indexing or subrange checks,
|
||||||
overflow errors are *not* prevented. Overflows will be checked for in
|
overflow errors are *not* prevented. Overflows will be checked for in
|
||||||
|
|
@ -53,8 +53,7 @@ Motivating Example
|
||||||
The follow example highlights what DrNim can easily do, even
|
The follow example highlights what DrNim can easily do, even
|
||||||
without additional annotations:
|
without additional annotations:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
{.push staticBoundChecks: on.}
|
{.push staticBoundChecks: on.}
|
||||||
|
|
||||||
proc sum(a: openArray[int]): int =
|
proc sum(a: openArray[int]): int =
|
||||||
|
|
@ -64,6 +63,7 @@ without additional annotations:
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
||||||
echo sum([1, 2, 3])
|
echo sum([1, 2, 3])
|
||||||
|
```
|
||||||
|
|
||||||
This program contains a famous "index out of bounds" bug. DrNim
|
This program contains a famous "index out of bounds" bug. DrNim
|
||||||
detects it and produces the following error message::
|
detects it and produces the following error message::
|
||||||
|
|
@ -125,8 +125,7 @@ Example: insertionSort
|
||||||
|
|
||||||
**Note**: This example does not yet work with DrNim.
|
**Note**: This example does not yet work with DrNim.
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
import std / logic
|
import std / logic
|
||||||
|
|
||||||
proc insertionSort(a: var openArray[int]) {.
|
proc insertionSort(a: var openArray[int]) {.
|
||||||
|
|
@ -142,6 +141,7 @@ Example: insertionSort
|
||||||
{.invariant: forall(j in 1..k, i in 0..<j, j == t or a[i] <= a[j]).}
|
{.invariant: forall(j in 1..k, i in 0..<j, j == t or a[i] <= a[j]).}
|
||||||
swap a[t], a[t-1]
|
swap a[t], a[t-1]
|
||||||
dec t
|
dec t
|
||||||
|
```
|
||||||
|
|
||||||
Unfortunately, the invariants required to prove that this code is correct take more
|
Unfortunately, the invariants required to prove that this code is correct take more
|
||||||
code than the imperative instructions. However, this effort can be compensated
|
code than the imperative instructions. However, this effort can be compensated
|
||||||
|
|
@ -155,14 +155,14 @@ This is required, but not sufficient to describe that a `sort` operation
|
||||||
was performed. For example, the same postcondition is true for this proc
|
was performed. For example, the same postcondition is true for this proc
|
||||||
which doesn't sort at all:
|
which doesn't sort at all:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
import std / logic
|
import std / logic
|
||||||
|
|
||||||
proc insertionSort(a: var openArray[int]) {.
|
proc insertionSort(a: var openArray[int]) {.
|
||||||
ensures: forall(i in 1..<a.len, a[i-1] <= a[i]).} =
|
ensures: forall(i in 1..<a.len, a[i-1] <= a[i]).} =
|
||||||
# does not sort, overwrites `a`'s contents!
|
# does not sort, overwrites `a`'s contents!
|
||||||
for i in 0..<a.len: a[i] = i
|
for i in 0..<a.len: a[i] = i
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,12 @@ difficult is the ``newString`` proc: If it is simply wrapped, it
|
||||||
should not be evaluated at compile time! On other occasions it can
|
should not be evaluated at compile time! On other occasions it can
|
||||||
and should be evaluated:
|
and should be evaluated:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
proc toUpper(s: string): string =
|
proc toUpper(s: string): string =
|
||||||
result = newString(len(s))
|
result = newString(len(s))
|
||||||
for i in 0..len(s) - 1:
|
for i in 0..len(s) - 1:
|
||||||
result[i] = toUpper(s[i])
|
result[i] = toUpper(s[i])
|
||||||
|
```
|
||||||
|
|
||||||
No, it really can always be evaluated. The code generator should transform
|
No, it really can always be evaluated. The code generator should transform
|
||||||
``s = "\0\0\0..."`` back into ``s = newString(...)``.
|
``s = "\0\0\0..."`` back into ``s = newString(...)``.
|
||||||
|
|
|
||||||
|
|
@ -41,10 +41,11 @@ recognize it as Nim source file).
|
||||||
If we use `generateXML` code shown above and call the SCF file `xmlGen.nimf`
|
If we use `generateXML` code shown above and call the SCF file `xmlGen.nimf`
|
||||||
In your `main.nim`:
|
In your `main.nim`:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
include "xmlGen.nimf"
|
include "xmlGen.nimf"
|
||||||
|
|
||||||
echo generateXML("John Smith","42")
|
echo generateXML("John Smith","42")
|
||||||
|
```
|
||||||
|
|
||||||
Pipe operator
|
Pipe operator
|
||||||
=============
|
=============
|
||||||
|
|
@ -150,7 +151,7 @@ Example::
|
||||||
|
|
||||||
The filter transforms this into:
|
The filter transforms this into:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
proc generateHTMLPage(title, currentTab, content: string,
|
proc generateHTMLPage(title, currentTab, content: string,
|
||||||
tabs: openArray[string]): string =
|
tabs: openArray[string]): string =
|
||||||
result = ""
|
result = ""
|
||||||
|
|
@ -173,6 +174,7 @@ The filter transforms this into:
|
||||||
" A dollar: $.\n" &
|
" A dollar: $.\n" &
|
||||||
" </div>\n" &
|
" </div>\n" &
|
||||||
"</body>\n")
|
"</body>\n")
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Each line that does not start with the meta character (ignoring leading
|
Each line that does not start with the meta character (ignoring leading
|
||||||
|
|
|
||||||
37
doc/hcr.md
37
doc/hcr.md
|
|
@ -26,8 +26,7 @@ code when `F9` is pressed. The important lines are marked with `#***`.
|
||||||
To install SDL2 you can use `nimble install sdl2`:cmd:.
|
To install SDL2 you can use `nimble install sdl2`:cmd:.
|
||||||
|
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
# logic.nim
|
# logic.nim
|
||||||
import sdl2
|
import sdl2
|
||||||
|
|
||||||
|
|
@ -83,10 +82,10 @@ To install SDL2 you can use `nimble install sdl2`:cmd:.
|
||||||
discard renderer.fillRect(rect)
|
discard renderer.fillRect(rect)
|
||||||
delay(16)
|
delay(16)
|
||||||
renderer.present()
|
renderer.present()
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
# mymain.nim
|
# mymain.nim
|
||||||
import logic
|
import logic
|
||||||
|
|
||||||
|
|
@ -97,42 +96,44 @@ To install SDL2 you can use `nimble install sdl2`:cmd:.
|
||||||
destroy()
|
destroy()
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Compile this example via:
|
Compile this example via:
|
||||||
|
|
||||||
```cmd
|
```cmd
|
||||||
nim c --hotcodereloading:on mymain.nim
|
nim c --hotcodereloading:on mymain.nim
|
||||||
```
|
```
|
||||||
|
|
||||||
Now start the program and KEEP it running!
|
Now start the program and KEEP it running!
|
||||||
|
|
||||||
.. code:: cmd
|
```cmd
|
||||||
# Unix:
|
# Unix:
|
||||||
mymain &
|
mymain &
|
||||||
# or Windows (click on the .exe)
|
# or Windows (click on the .exe)
|
||||||
mymain.exe
|
mymain.exe
|
||||||
# edit
|
# edit
|
||||||
|
```
|
||||||
|
|
||||||
For example, change the line:
|
For example, change the line:
|
||||||
|
|
||||||
```nim
|
```nim
|
||||||
discard renderer.setDrawColor(255, 128, 128, 0)
|
discard renderer.setDrawColor(255, 128, 128, 0)
|
||||||
```
|
```
|
||||||
|
|
||||||
into:
|
into:
|
||||||
|
|
||||||
```nim
|
```nim
|
||||||
discard renderer.setDrawColor(255, 255, 128, 0)
|
discard renderer.setDrawColor(255, 255, 128, 0)
|
||||||
```
|
```
|
||||||
|
|
||||||
(This will change the color of the rectangle.)
|
(This will change the color of the rectangle.)
|
||||||
|
|
||||||
Then recompile the project, but do not restart or quit the mymain.exe program!
|
Then recompile the project, but do not restart or quit the mymain.exe program!
|
||||||
|
|
||||||
```cmd
|
```cmd
|
||||||
nim c --hotcodereloading:on mymain.nim
|
nim c --hotcodereloading:on mymain.nim
|
||||||
```
|
```
|
||||||
|
|
||||||
Now give the `mymain` SDL window the focus, press F9, and watch the
|
Now give the `mymain` SDL window the focus, press F9, and watch the
|
||||||
updated version of the program.
|
updated version of the program.
|
||||||
|
|
@ -146,7 +147,7 @@ One can use the special event handlers `beforeCodeReload` and
|
||||||
`afterCodeReload` to reset the state of a particular variable or to force
|
`afterCodeReload` to reset the state of a particular variable or to force
|
||||||
the execution of certain statements:
|
the execution of certain statements:
|
||||||
|
|
||||||
.. code-block:: Nim
|
```Nim
|
||||||
var
|
var
|
||||||
settings = initTable[string, string]()
|
settings = initTable[string, string]()
|
||||||
lastReload: Time
|
lastReload: Time
|
||||||
|
|
@ -159,6 +160,7 @@ the execution of certain statements:
|
||||||
afterCodeReload:
|
afterCodeReload:
|
||||||
lastReload = now()
|
lastReload = now()
|
||||||
resetProgramState()
|
resetProgramState()
|
||||||
|
```
|
||||||
|
|
||||||
On each code reload, Nim will first execute all `beforeCodeReload`:idx:
|
On each code reload, Nim will first execute all `beforeCodeReload`:idx:
|
||||||
handlers registered in the previous version of the program and then all
|
handlers registered in the previous version of the program and then all
|
||||||
|
|
@ -167,7 +169,7 @@ that any handlers appearing in modules that weren't reloaded will also be
|
||||||
executed. To prevent this behavior, one can guard the code with the
|
executed. To prevent this behavior, one can guard the code with the
|
||||||
`hasModuleChanged()`:idx: API:
|
`hasModuleChanged()`:idx: API:
|
||||||
|
|
||||||
.. code-block:: Nim
|
```Nim
|
||||||
import mydb
|
import mydb
|
||||||
|
|
||||||
var myCache = initTable[Key, Value]()
|
var myCache = initTable[Key, Value]()
|
||||||
|
|
@ -175,6 +177,7 @@ executed. To prevent this behavior, one can guard the code with the
|
||||||
afterCodeReload:
|
afterCodeReload:
|
||||||
if hasModuleChanged(mydb):
|
if hasModuleChanged(mydb):
|
||||||
resetCache(myCache)
|
resetCache(myCache)
|
||||||
|
```
|
||||||
|
|
||||||
The hot code reloading is based on dynamic library hot swapping in the native
|
The hot code reloading is based on dynamic library hot swapping in the native
|
||||||
targets and direct manipulation of the global namespace in the JavaScript
|
targets and direct manipulation of the global namespace in the JavaScript
|
||||||
|
|
@ -203,8 +206,7 @@ runtime demands of the example code above. An example of compiling
|
||||||
``nimhcr.nim`` and ``nimrtl.nim`` when the source dir of Nim is installed
|
``nimhcr.nim`` and ``nimrtl.nim`` when the source dir of Nim is installed
|
||||||
with choosenim follows.
|
with choosenim follows.
|
||||||
|
|
||||||
.. code:: console
|
```console
|
||||||
|
|
||||||
# Unix/MacOS
|
# Unix/MacOS
|
||||||
# Make sure you are in the directory containing your .nim files
|
# Make sure you are in the directory containing your .nim files
|
||||||
$ cd your-source-directory
|
$ cd your-source-directory
|
||||||
|
|
@ -215,6 +217,7 @@ with choosenim follows.
|
||||||
|
|
||||||
# verify that you have two files named libnimhcr and libnimrtl in your
|
# verify that you have two files named libnimhcr and libnimrtl in your
|
||||||
# source directory (.dll for Windows, .so for Unix, .dylib for MacOS)
|
# source directory (.dll for Windows, .so for Unix, .dylib for MacOS)
|
||||||
|
```
|
||||||
|
|
||||||
All modules of the project will be compiled to separate dynamic link
|
All modules of the project will be compiled to separate dynamic link
|
||||||
libraries placed in the `nimcache` directory. Please note that during
|
libraries placed in the `nimcache` directory. Please note that during
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,7 @@
|
||||||
.. contents::
|
.. contents::
|
||||||
|
|
||||||
|
|
||||||
.. raw:: html
|
> "yes, I'm the creator" -- Araq, 2013-07-26 19:28:32.
|
||||||
<blockquote><p>
|
|
||||||
"yes, I'm the creator" -- Araq, 2013-07-26 19:28:32.
|
|
||||||
</p></blockquote>
|
|
||||||
|
|
||||||
Note: this is mostly outdated, see instead `nimsuggest <nimsuggest.html>`_
|
Note: this is mostly outdated, see instead `nimsuggest <nimsuggest.html>`_
|
||||||
|
|
||||||
|
|
@ -246,11 +243,12 @@ skConst
|
||||||
| **Fourth column**: the type of the const value.
|
| **Fourth column**: the type of the const value.
|
||||||
| **Docstring**: always the empty string.
|
| **Docstring**: always the empty string.
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
const SOME_SEQUENCE = @[1, 2]
|
const SOME_SEQUENCE = @[1, 2]
|
||||||
--> col 2: $MODULE.SOME_SEQUENCE
|
--> col 2: $MODULE.SOME_SEQUENCE
|
||||||
col 3: seq[int]
|
col 3: seq[int]
|
||||||
col 7: ""
|
col 7: ""
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
skEnumField
|
skEnumField
|
||||||
|
|
@ -260,11 +258,12 @@ skEnumField
|
||||||
| **Fourth column**: enum type grouping other enum fields.
|
| **Fourth column**: enum type grouping other enum fields.
|
||||||
| **Docstring**: always the empty string.
|
| **Docstring**: always the empty string.
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
Open(filename, fmWrite)
|
Open(filename, fmWrite)
|
||||||
--> col 2: system.FileMode.fmWrite
|
--> col 2: system.FileMode.fmWrite
|
||||||
col 3: FileMode
|
col 3: FileMode
|
||||||
col 7: ""
|
col 7: ""
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
skForVar
|
skForVar
|
||||||
|
|
@ -274,13 +273,14 @@ skForVar
|
||||||
| **Fourth column**: type of the var.
|
| **Fourth column**: type of the var.
|
||||||
| **Docstring**: always the empty string.
|
| **Docstring**: always the empty string.
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
proc looper(filename = "tests.nim") =
|
proc looper(filename = "tests.nim") =
|
||||||
for letter in filename:
|
for letter in filename:
|
||||||
echo letter
|
echo letter
|
||||||
--> col 2: $MODULE.looper.letter
|
--> col 2: $MODULE.looper.letter
|
||||||
col 3: char
|
col 3: char
|
||||||
col 7: ""
|
col 7: ""
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
skIterator, skClosureIterator
|
skIterator, skClosureIterator
|
||||||
|
|
@ -295,13 +295,14 @@ posterior instances of the iterator.
|
||||||
| **Fourth column**: signature of the iterator including return type.
|
| **Fourth column**: signature of the iterator including return type.
|
||||||
| **Docstring**: docstring if available.
|
| **Docstring**: docstring if available.
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
let
|
let
|
||||||
text = "some text"
|
text = "some text"
|
||||||
letters = toSeq(runes(text))
|
letters = toSeq(runes(text))
|
||||||
--> col 2: unicode.runes
|
--> col 2: unicode.runes
|
||||||
col 3: iterator (string): Rune
|
col 3: iterator (string): Rune
|
||||||
col 7: "iterates over any unicode character of the string `s`."
|
col 7: "iterates over any unicode character of the string `s`."
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
skLabel
|
skLabel
|
||||||
|
|
@ -311,13 +312,14 @@ skLabel
|
||||||
| **Fourth column**: always the empty string.
|
| **Fourth column**: always the empty string.
|
||||||
| **Docstring**: always the empty string.
|
| **Docstring**: always the empty string.
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
proc test(text: string) =
|
proc test(text: string) =
|
||||||
var found = -1
|
var found = -1
|
||||||
block loops:
|
block loops:
|
||||||
--> col 2: $MODULE.test.loops
|
--> col 2: $MODULE.test.loops
|
||||||
col 3: ""
|
col 3: ""
|
||||||
col 7: ""
|
col 7: ""
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
skLet
|
skLet
|
||||||
|
|
@ -327,12 +329,13 @@ skLet
|
||||||
| **Fourth column**: the type of the let variable.
|
| **Fourth column**: the type of the let variable.
|
||||||
| **Docstring**: always the empty string.
|
| **Docstring**: always the empty string.
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
let
|
let
|
||||||
text = "some text"
|
text = "some text"
|
||||||
--> col 2: $MODULE.text
|
--> col 2: $MODULE.text
|
||||||
col 3: string
|
col 3: string
|
||||||
col 7: ""
|
col 7: ""
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
skMacro
|
skMacro
|
||||||
|
|
@ -347,12 +350,13 @@ posterior instances of the macro.
|
||||||
| **Fourth column**: signature of the macro including return type.
|
| **Fourth column**: signature of the macro including return type.
|
||||||
| **Docstring**: docstring if available.
|
| **Docstring**: docstring if available.
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
proc testMacro() =
|
proc testMacro() =
|
||||||
expect(EArithmetic):
|
expect(EArithmetic):
|
||||||
--> col 2: idetools_api.expect
|
--> col 2: idetools_api.expect
|
||||||
col 3: proc (varargs[expr], stmt): stmt
|
col 3: proc (varargs[expr], stmt): stmt
|
||||||
col 7: ""
|
col 7: ""
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
skMethod
|
skMethod
|
||||||
|
|
@ -384,7 +388,7 @@ This may change in the future.
|
||||||
| **Fourth column**: signature of the method including return type.
|
| **Fourth column**: signature of the method including return type.
|
||||||
| **Docstring**: docstring if available.
|
| **Docstring**: docstring if available.
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
method eval(e: PExpr): int = quit "to override!"
|
method eval(e: PExpr): int = quit "to override!"
|
||||||
method eval(e: PLiteral): int = e.x
|
method eval(e: PLiteral): int = e.x
|
||||||
method eval(e: PPlusExpr): int = eval(e.a) + eval(e.b)
|
method eval(e: PPlusExpr): int = eval(e.a) + eval(e.b)
|
||||||
|
|
@ -392,6 +396,7 @@ This may change in the future.
|
||||||
--> col 2: $MODULE.eval
|
--> col 2: $MODULE.eval
|
||||||
col 3: proc (PPlusExpr): int
|
col 3: proc (PPlusExpr): int
|
||||||
col 7: ""
|
col 7: ""
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
skParam
|
skParam
|
||||||
|
|
@ -401,12 +406,13 @@ skParam
|
||||||
| **Fourth column**: the type of the parameter.
|
| **Fourth column**: the type of the parameter.
|
||||||
| **Docstring**: always the empty string.
|
| **Docstring**: always the empty string.
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
proc reader(filename = "tests.nim") =
|
proc reader(filename = "tests.nim") =
|
||||||
let text = readFile(filename)
|
let text = readFile(filename)
|
||||||
--> col 2: $MODULE.reader.filename
|
--> col 2: $MODULE.reader.filename
|
||||||
col 3: string
|
col 3: string
|
||||||
col 7: ""
|
col 7: ""
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
skProc
|
skProc
|
||||||
|
|
@ -425,7 +431,7 @@ returned by idetools returns also the pragmas for the proc.
|
||||||
| **Fourth column**: signature of the proc including return type.
|
| **Fourth column**: signature of the proc including return type.
|
||||||
| **Docstring**: docstring if available.
|
| **Docstring**: docstring if available.
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
open(filename, fmWrite)
|
open(filename, fmWrite)
|
||||||
--> col 2: system.Open
|
--> col 2: system.Open
|
||||||
col 3: proc (var File, string, FileMode, int): bool
|
col 3: proc (var File, string, FileMode, int): bool
|
||||||
|
|
@ -434,6 +440,7 @@ returned by idetools returns also the pragmas for the proc.
|
||||||
|
|
||||||
Default mode is readonly. Returns true iff the file could be opened.
|
Default mode is readonly. Returns true iff the file could be opened.
|
||||||
This throws no exception if the file could not be opened."
|
This throws no exception if the file could not be opened."
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
skResult
|
skResult
|
||||||
|
|
@ -443,12 +450,13 @@ skResult
|
||||||
| **Fourth column**: the type of the result.
|
| **Fourth column**: the type of the result.
|
||||||
| **Docstring**: always the empty string.
|
| **Docstring**: always the empty string.
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
proc getRandomValue() : int =
|
proc getRandomValue() : int =
|
||||||
return 4
|
return 4
|
||||||
--> col 2: $MODULE.getRandomValue.result
|
--> col 2: $MODULE.getRandomValue.result
|
||||||
col 3: int
|
col 3: int
|
||||||
col 7: ""
|
col 7: ""
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
skTemplate
|
skTemplate
|
||||||
|
|
@ -463,7 +471,7 @@ posterior instances of the template.
|
||||||
| **Fourth column**: signature of the template including return type.
|
| **Fourth column**: signature of the template including return type.
|
||||||
| **Docstring**: docstring if available.
|
| **Docstring**: docstring if available.
|
||||||
|
|
||||||
.. code-block:: nim
|
`````nim
|
||||||
let
|
let
|
||||||
text = "some text"
|
text = "some text"
|
||||||
letters = toSeq(runes(text))
|
letters = toSeq(runes(text))
|
||||||
|
|
@ -474,13 +482,15 @@ posterior instances of the template.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
let
|
let
|
||||||
numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
|
numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||||
odd_numbers = toSeq(filter(numeric) do (x: int) -> bool:
|
odd_numbers = toSeq(filter(numeric) do (x: int) -> bool:
|
||||||
if x mod 2 == 1:
|
if x mod 2 == 1:
|
||||||
result = true)
|
result = true)
|
||||||
assert odd_numbers == @[1, 3, 5, 7, 9]"
|
assert odd_numbers == @[1, 3, 5, 7, 9]"
|
||||||
|
```
|
||||||
|
`````
|
||||||
|
|
||||||
|
|
||||||
skType
|
skType
|
||||||
|
|
@ -490,12 +500,13 @@ skType
|
||||||
| **Fourth column**: the type.
|
| **Fourth column**: the type.
|
||||||
| **Docstring**: always the empty string.
|
| **Docstring**: always the empty string.
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
proc writeTempFile() =
|
proc writeTempFile() =
|
||||||
var output: File
|
var output: File
|
||||||
--> col 2: system.File
|
--> col 2: system.File
|
||||||
col 3: File
|
col 3: File
|
||||||
col 7: ""
|
col 7: ""
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
skVar
|
skVar
|
||||||
|
|
@ -505,7 +516,7 @@ skVar
|
||||||
| **Fourth column**: the type of the var.
|
| **Fourth column**: the type of the var.
|
||||||
| **Docstring**: always the empty string.
|
| **Docstring**: always the empty string.
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
proc writeTempFile() =
|
proc writeTempFile() =
|
||||||
var output: File
|
var output: File
|
||||||
output.open("/tmp/somefile", fmWrite)
|
output.open("/tmp/somefile", fmWrite)
|
||||||
|
|
@ -513,6 +524,7 @@ skVar
|
||||||
--> col 2: $MODULE.writeTempFile.output
|
--> col 2: $MODULE.writeTempFile.output
|
||||||
col 3: File
|
col 3: File
|
||||||
col 7: ""
|
col 7: ""
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Test suite
|
Test suite
|
||||||
|
|
|
||||||
|
|
@ -42,25 +42,25 @@ Bootstrapping the compiler
|
||||||
|
|
||||||
Compiling the compiler is a simple matter of running:
|
Compiling the compiler is a simple matter of running:
|
||||||
|
|
||||||
.. code:: cmd
|
```cmd
|
||||||
|
|
||||||
nim c koch.nim
|
nim c koch.nim
|
||||||
koch boot -d:release
|
koch boot -d:release
|
||||||
|
```
|
||||||
|
|
||||||
For a debug version use:
|
For a debug version use:
|
||||||
|
|
||||||
.. code:: cmd
|
```cmd
|
||||||
|
|
||||||
nim c koch.nim
|
nim c koch.nim
|
||||||
koch boot
|
koch boot
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
And for a debug version compatible with GDB:
|
And for a debug version compatible with GDB:
|
||||||
|
|
||||||
.. code:: cmd
|
```cmd
|
||||||
|
|
||||||
nim c koch.nim
|
nim c koch.nim
|
||||||
koch boot --debuginfo --linedir:on
|
koch boot --debuginfo --linedir:on
|
||||||
|
```
|
||||||
|
|
||||||
The `koch`:cmd: program is Nim's maintenance script. It is a replacement for
|
The `koch`:cmd: program is Nim's maintenance script. It is a replacement for
|
||||||
make and shell scripting with the advantage that it is much more portable.
|
make and shell scripting with the advantage that it is much more portable.
|
||||||
|
|
@ -73,10 +73,10 @@ Reproducible builds
|
||||||
|
|
||||||
Set the compilation timestamp with the `SOURCE_DATE_EPOCH` environment variable.
|
Set the compilation timestamp with the `SOURCE_DATE_EPOCH` environment variable.
|
||||||
|
|
||||||
.. code:: cmd
|
```cmd
|
||||||
|
|
||||||
export SOURCE_DATE_EPOCH=$(git log -n 1 --format=%at)
|
export SOURCE_DATE_EPOCH=$(git log -n 1 --format=%at)
|
||||||
koch boot # or `./build_all.sh`
|
koch boot # or `./build_all.sh`
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Debugging the compiler
|
Debugging the compiler
|
||||||
|
|
@ -98,10 +98,10 @@ focus on the changes introduced by that one specific commit.
|
||||||
compilation fails. This exit code tells `git bisect`:cmd: to skip the
|
compilation fails. This exit code tells `git bisect`:cmd: to skip the
|
||||||
current commit:
|
current commit:
|
||||||
|
|
||||||
.. code:: cmd
|
```cmd
|
||||||
|
|
||||||
git bisect start bad-commit good-commit
|
git bisect start bad-commit good-commit
|
||||||
git bisect run ./koch temp -r c test-source.nim
|
git bisect run ./koch temp -r c test-source.nim
|
||||||
|
```
|
||||||
|
|
||||||
You can also bisect using custom options to build the compiler, for example if
|
You can also bisect using custom options to build the compiler, for example if
|
||||||
you don't need a debug version of the compiler (which runs slower), you can replace
|
you don't need a debug version of the compiler (which runs slower), you can replace
|
||||||
|
|
@ -141,8 +141,7 @@ enabled. Here are compiler options that are of interest when debugging:
|
||||||
|
|
||||||
Another method to build and run the compiler is directly through `koch`:cmd:\:
|
Another method to build and run the compiler is directly through `koch`:cmd:\:
|
||||||
|
|
||||||
.. code:: cmd
|
```cmd
|
||||||
|
|
||||||
koch temp [options] c test.nim
|
koch temp [options] c test.nim
|
||||||
|
|
||||||
# (will build with js support)
|
# (will build with js support)
|
||||||
|
|
@ -150,6 +149,7 @@ Another method to build and run the compiler is directly through `koch`:cmd:\:
|
||||||
|
|
||||||
# (will build with doc support)
|
# (will build with doc support)
|
||||||
koch temp [options] doc test.nim
|
koch temp [options] doc test.nim
|
||||||
|
```
|
||||||
|
|
||||||
Debug logging
|
Debug logging
|
||||||
-------------
|
-------------
|
||||||
|
|
@ -166,17 +166,16 @@ is being used. One very common way to achieve this is to use the `mdbg` conditio
|
||||||
which will be true only in contexts, processing expressions and statements from
|
which will be true only in contexts, processing expressions and statements from
|
||||||
the currently compiled main module:
|
the currently compiled main module:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
# inside some compiler module
|
# inside some compiler module
|
||||||
if mdbg:
|
if mdbg:
|
||||||
debug someAstNode
|
debug someAstNode
|
||||||
|
```
|
||||||
|
|
||||||
Using the `isCompilerDebug`:nim: condition along with inserting some statements
|
Using the `isCompilerDebug`:nim: condition along with inserting some statements
|
||||||
into the testcase provides more granular logging:
|
into the testcase provides more granular logging:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
# compilermodule.nim
|
# compilermodule.nim
|
||||||
if isCompilerDebug():
|
if isCompilerDebug():
|
||||||
debug someAstNode
|
debug someAstNode
|
||||||
|
|
@ -186,21 +185,21 @@ into the testcase provides more granular logging:
|
||||||
{.define(nimCompilerDebug).}
|
{.define(nimCompilerDebug).}
|
||||||
let a = 2.5 * 3
|
let a = 2.5 * 3
|
||||||
{.undef(nimCompilerDebug).}
|
{.undef(nimCompilerDebug).}
|
||||||
|
```
|
||||||
|
|
||||||
Logging can also be scoped to a specific filename as well. This will of course
|
Logging can also be scoped to a specific filename as well. This will of course
|
||||||
match against every module with that name.
|
match against every module with that name.
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
if `??`(conf, n.info, "module.nim"):
|
if `??`(conf, n.info, "module.nim"):
|
||||||
debug(n)
|
debug(n)
|
||||||
|
```
|
||||||
|
|
||||||
The above examples also makes use of the `debug`:nim: proc, which is able to
|
The above examples also makes use of the `debug`:nim: proc, which is able to
|
||||||
print a human-readable form of an arbitrary AST tree. Other common ways to print
|
print a human-readable form of an arbitrary AST tree. Other common ways to print
|
||||||
information about the internal compiler types include:
|
information about the internal compiler types include:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
# pretty print PNode
|
# pretty print PNode
|
||||||
|
|
||||||
# pretty prints the Nim ast
|
# pretty prints the Nim ast
|
||||||
|
|
@ -246,23 +245,24 @@ information about the internal compiler types include:
|
||||||
|
|
||||||
# print the structure of any type
|
# print the structure of any type
|
||||||
repr(someVar)
|
repr(someVar)
|
||||||
|
```
|
||||||
|
|
||||||
Here are some other helpful utilities:
|
Here are some other helpful utilities:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
# how did execution reach this location?
|
# how did execution reach this location?
|
||||||
writeStackTrace()
|
writeStackTrace()
|
||||||
|
```
|
||||||
|
|
||||||
These procs may not already be imported by the module you're editing.
|
These procs may not already be imported by the module you're editing.
|
||||||
You can import them directly for debugging:
|
You can import them directly for debugging:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
from astalgo import debug
|
from astalgo import debug
|
||||||
from types import typeToString
|
from types import typeToString
|
||||||
from renderer import renderTree
|
from renderer import renderTree
|
||||||
from msgs import `??`
|
from msgs import `??`
|
||||||
|
```
|
||||||
|
|
||||||
Native debugging
|
Native debugging
|
||||||
----------------
|
----------------
|
||||||
|
|
@ -280,12 +280,12 @@ and `exitingDebugSection()`:nim:.
|
||||||
* LLDB execute `command source tools/compiler.lldb` at startup
|
* LLDB execute `command source tools/compiler.lldb` at startup
|
||||||
#. Use one of the scoping helpers like so:
|
#. Use one of the scoping helpers like so:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
if isCompilerDebug():
|
if isCompilerDebug():
|
||||||
enteringDebugSection()
|
enteringDebugSection()
|
||||||
else:
|
else:
|
||||||
exitingDebugSection()
|
exitingDebugSection()
|
||||||
|
```
|
||||||
|
|
||||||
A caveat of this method is that all breakpoints and watchpoints are enabled or
|
A caveat of this method is that all breakpoints and watchpoints are enabled or
|
||||||
disabled. Also, due to a bug, only breakpoints can be constrained for LLDB.
|
disabled. Also, due to a bug, only breakpoints can be constrained for LLDB.
|
||||||
|
|
@ -448,8 +448,9 @@ Tests with GCC on Amd64 showed that it's really beneficial if the
|
||||||
Proper thunk generation is harder because the proc that is to wrap
|
Proper thunk generation is harder because the proc that is to wrap
|
||||||
could stem from a complex expression:
|
could stem from a complex expression:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
receivesClosure(returnsDefaultCC[i])
|
receivesClosure(returnsDefaultCC[i])
|
||||||
|
```
|
||||||
|
|
||||||
A thunk would need to call 'returnsDefaultCC[i]' somehow and that would require
|
A thunk would need to call 'returnsDefaultCC[i]' somehow and that would require
|
||||||
an *additional* closure generation... Ok, not really, but it requires to pass
|
an *additional* closure generation... Ok, not really, but it requires to pass
|
||||||
|
|
@ -460,17 +461,18 @@ to pass a proc pointer around via a generic `ref` type.
|
||||||
|
|
||||||
Example code:
|
Example code:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
proc add(x: int): proc (y: int): int {.closure.} =
|
proc add(x: int): proc (y: int): int {.closure.} =
|
||||||
return proc (y: int): int =
|
return proc (y: int): int =
|
||||||
return x + y
|
return x + y
|
||||||
|
|
||||||
var add2 = add(2)
|
var add2 = add(2)
|
||||||
echo add2(5) #OUT 7
|
echo add2(5) #OUT 7
|
||||||
|
```
|
||||||
|
|
||||||
This should produce roughly this code:
|
This should produce roughly this code:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
type
|
type
|
||||||
Env = ref object
|
Env = ref object
|
||||||
x: int # data
|
x: int # data
|
||||||
|
|
@ -487,11 +489,12 @@ This should produce roughly this code:
|
||||||
var add2 = add(2)
|
var add2 = add(2)
|
||||||
let tmp = if add2.data == nil: add2.prc(5) else: add2.prc(5, add2.data)
|
let tmp = if add2.data == nil: add2.prc(5) else: add2.prc(5, add2.data)
|
||||||
echo tmp
|
echo tmp
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Beware of nesting:
|
Beware of nesting:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
proc add(x: int): proc (y: int): proc (z: int): int {.closure.} {.closure.} =
|
proc add(x: int): proc (y: int): proc (z: int): int {.closure.} {.closure.} =
|
||||||
return lambda (y: int): proc (z: int): int {.closure.} =
|
return lambda (y: int): proc (z: int): int {.closure.} =
|
||||||
return lambda (z: int): int =
|
return lambda (z: int): int =
|
||||||
|
|
@ -499,10 +502,11 @@ Beware of nesting:
|
||||||
|
|
||||||
var add24 = add(2)(4)
|
var add24 = add(2)(4)
|
||||||
echo add24(5) #OUT 11
|
echo add24(5) #OUT 11
|
||||||
|
```
|
||||||
|
|
||||||
This should produce roughly this code:
|
This should produce roughly this code:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
type
|
type
|
||||||
EnvX = ref object
|
EnvX = ref object
|
||||||
x: int # data
|
x: int # data
|
||||||
|
|
@ -530,6 +534,7 @@ This should produce roughly this code:
|
||||||
var tmp2 = tmp.fn(4, tmp.data)
|
var tmp2 = tmp.fn(4, tmp.data)
|
||||||
var add24 = tmp2.fn(4, tmp2.data)
|
var add24 = tmp2.fn(4, tmp2.data)
|
||||||
echo add24(5)
|
echo add24(5)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
We could get rid of nesting environments by always inlining inner anon procs.
|
We could get rid of nesting environments by always inlining inner anon procs.
|
||||||
|
|
@ -540,8 +545,7 @@ however.
|
||||||
Accumulator
|
Accumulator
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
proc getAccumulator(start: int): proc (): int {.closure} =
|
proc getAccumulator(start: int): proc (): int {.closure} =
|
||||||
var i = start
|
var i = start
|
||||||
return lambda: int =
|
return lambda: int =
|
||||||
|
|
@ -560,6 +564,7 @@ Accumulator
|
||||||
var a = accumulator(3)
|
var a = accumulator(3)
|
||||||
var b = accumulator(4)
|
var b = accumulator(4)
|
||||||
echo a() + b()
|
echo a() + b()
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Internals
|
Internals
|
||||||
|
|
@ -614,8 +619,7 @@ keeps the full `int literal(321)` type. Here is an example where that
|
||||||
difference matters.
|
difference matters.
|
||||||
|
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
proc foo(arg: int8) =
|
proc foo(arg: int8) =
|
||||||
echo "def"
|
echo "def"
|
||||||
|
|
||||||
|
|
@ -624,14 +628,14 @@ difference matters.
|
||||||
|
|
||||||
let tmp2 = 123
|
let tmp2 = 123
|
||||||
foo(tmp2) # Error
|
foo(tmp2) # Error
|
||||||
|
```
|
||||||
|
|
||||||
In a context with multiple overloads, the integer literal kind will
|
In a context with multiple overloads, the integer literal kind will
|
||||||
always prefer the `int` type over all other types. If none of the
|
always prefer the `int` type over all other types. If none of the
|
||||||
overloads is of type `int`, then there will be an error because of
|
overloads is of type `int`, then there will be an error because of
|
||||||
ambiguity.
|
ambiguity.
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
proc foo(arg: int) =
|
proc foo(arg: int) =
|
||||||
echo "abc"
|
echo "abc"
|
||||||
proc foo(arg: int8) =
|
proc foo(arg: int8) =
|
||||||
|
|
@ -644,6 +648,7 @@ ambiguity.
|
||||||
echo "def"
|
echo "def"
|
||||||
|
|
||||||
bar(123) # Error ambiguous call
|
bar(123) # Error ambiguous call
|
||||||
|
```
|
||||||
|
|
||||||
In the compiler these integer literal types are represented with the
|
In the compiler these integer literal types are represented with the
|
||||||
node kind `nkIntLit`, type kind `tyInt` and the member `n` of the type
|
node kind `nkIntLit`, type kind `tyInt` and the member `n` of the type
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,7 @@
|
||||||
.. include:: rstcommon.rst
|
.. include:: rstcommon.rst
|
||||||
.. contents::
|
.. contents::
|
||||||
|
|
||||||
.. raw:: html
|
> "A great chef is an artist that I truly respect" -- Robert Stack.
|
||||||
<blockquote><p>
|
|
||||||
"A great chef is an artist that I truly respect" -- Robert Stack.
|
|
||||||
</p></blockquote>
|
|
||||||
|
|
||||||
|
|
||||||
Introduction
|
Introduction
|
||||||
|
|
|
||||||
|
|
@ -30,17 +30,16 @@ The `void` type denotes the absence of any type. Parameters of
|
||||||
type `void` are treated as non-existent, `void` as a return type means that
|
type `void` are treated as non-existent, `void` as a return type means that
|
||||||
the procedure does not return a value:
|
the procedure does not return a value:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
proc nothing(x, y: void): void =
|
proc nothing(x, y: void): void =
|
||||||
echo "ha"
|
echo "ha"
|
||||||
|
|
||||||
nothing() # writes "ha" to stdout
|
nothing() # writes "ha" to stdout
|
||||||
|
```
|
||||||
|
|
||||||
The `void` type is particularly useful for generic code:
|
The `void` type is particularly useful for generic code:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
proc callProc[T](p: proc (x: T), x: T) =
|
proc callProc[T](p: proc (x: T), x: T) =
|
||||||
when T is void:
|
when T is void:
|
||||||
p()
|
p()
|
||||||
|
|
@ -52,15 +51,16 @@ The `void` type is particularly useful for generic code:
|
||||||
|
|
||||||
callProc[int](intProc, 12)
|
callProc[int](intProc, 12)
|
||||||
callProc[void](emptyProc)
|
callProc[void](emptyProc)
|
||||||
|
```
|
||||||
|
|
||||||
However, a `void` type cannot be inferred in generic code:
|
However, a `void` type cannot be inferred in generic code:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
callProc(emptyProc)
|
callProc(emptyProc)
|
||||||
# Error: type mismatch: got (proc ())
|
# Error: type mismatch: got (proc ())
|
||||||
# but expected one of:
|
# but expected one of:
|
||||||
# callProc(p: proc (T), x: T)
|
# callProc(p: proc (T), x: T)
|
||||||
|
```
|
||||||
|
|
||||||
The `void` type is only valid for parameters and return types; other symbols
|
The `void` type is only valid for parameters and return types; other symbols
|
||||||
cannot have the type `void`.
|
cannot have the type `void`.
|
||||||
|
|
@ -97,9 +97,7 @@ to a choice between `T.foo` and `U.foo`. During overload resolution,
|
||||||
the correct type of `foo` is decided from the context. If the type of `foo` is
|
the correct type of `foo` is decided from the context. If the type of `foo` is
|
||||||
ambiguous, a static error will be produced.
|
ambiguous, a static error will be produced.
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim test = "nim c $1"
|
||||||
:test: "nim c $1"
|
|
||||||
|
|
||||||
{.experimental: "overloadableEnums".}
|
{.experimental: "overloadableEnums".}
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -124,6 +122,7 @@ ambiguous, a static error will be produced.
|
||||||
of value2: echo "B"
|
of value2: echo "B"
|
||||||
|
|
||||||
p value2
|
p value2
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Package level objects
|
Package level objects
|
||||||
|
|
@ -144,8 +143,7 @@ available.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
# module A (in an arbitrary package)
|
# module A (in an arbitrary package)
|
||||||
type
|
type
|
||||||
Pack.SomeObject = object # declare as incomplete object of package 'Pack'
|
Pack.SomeObject = object # declare as incomplete object of package 'Pack'
|
||||||
|
|
@ -154,15 +152,16 @@ Example:
|
||||||
|
|
||||||
# Incomplete objects can be used as parameters:
|
# Incomplete objects can be used as parameters:
|
||||||
proc myproc(x: SomeObject) = discard
|
proc myproc(x: SomeObject) = discard
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
# module B (in package "Pack")
|
# module B (in package "Pack")
|
||||||
type
|
type
|
||||||
SomeObject* {.package.} = object # Use 'package' to complete the object
|
SomeObject* {.package.} = object # Use 'package' to complete the object
|
||||||
s, t: string
|
s, t: string
|
||||||
x, y: int
|
x, y: int
|
||||||
|
```
|
||||||
|
|
||||||
This feature will likely be superseded in the future by support for
|
This feature will likely be superseded in the future by support for
|
||||||
recursive module dependencies.
|
recursive module dependencies.
|
||||||
|
|
@ -223,8 +222,7 @@ preface definitions inside a module.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
{.experimental: "codeReordering".}
|
{.experimental: "codeReordering".}
|
||||||
|
|
||||||
proc foo(x: int) =
|
proc foo(x: int) =
|
||||||
|
|
@ -234,14 +232,14 @@ Example:
|
||||||
echo(x)
|
echo(x)
|
||||||
|
|
||||||
foo(10)
|
foo(10)
|
||||||
|
```
|
||||||
|
|
||||||
Variables can also be reordered as well. Variables that are *initialized* (i.e.
|
Variables can also be reordered as well. Variables that are *initialized* (i.e.
|
||||||
variables that have their declaration and assignment combined in a single
|
variables that have their declaration and assignment combined in a single
|
||||||
statement) can have their entire initialization statement reordered. Be wary of
|
statement) can have their entire initialization statement reordered. Be wary of
|
||||||
what code is executed at the top level:
|
what code is executed at the top level:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
{.experimental: "codeReordering".}
|
{.experimental: "codeReordering".}
|
||||||
|
|
||||||
proc a() =
|
proc a() =
|
||||||
|
|
@ -250,6 +248,7 @@ what code is executed at the top level:
|
||||||
var foo = 5
|
var foo = 5
|
||||||
|
|
||||||
a() # outputs: "5"
|
a() # outputs: "5"
|
||||||
|
```
|
||||||
|
|
||||||
..
|
..
|
||||||
TODO: Let's table this for now. This is an *experimental feature* and so the
|
TODO: Let's table this for now. This is an *experimental feature* and so the
|
||||||
|
|
@ -260,8 +259,7 @@ what code is executed at the top level:
|
||||||
code reordering process, and not after. As an example, the output of this
|
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 is the same as it would be with code reordering disabled.
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
{.experimental: "codeReordering".}
|
{.experimental: "codeReordering".}
|
||||||
|
|
||||||
proc x() =
|
proc x() =
|
||||||
|
|
@ -270,12 +268,12 @@ what code is executed at the top level:
|
||||||
var foo = 4
|
var foo = 4
|
||||||
|
|
||||||
x() # "false"
|
x() # "false"
|
||||||
|
```
|
||||||
|
|
||||||
It is important to note that reordering *only* works for symbols at top level
|
It is important to note that reordering *only* works for symbols at top level
|
||||||
scope. Therefore, the following will *fail to compile:*
|
scope. Therefore, the following will *fail to compile:*
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
{.experimental: "codeReordering".}
|
{.experimental: "codeReordering".}
|
||||||
|
|
||||||
proc a() =
|
proc a() =
|
||||||
|
|
@ -284,6 +282,7 @@ scope. Therefore, the following will *fail to compile:*
|
||||||
echo("Hello!")
|
echo("Hello!")
|
||||||
|
|
||||||
a()
|
a()
|
||||||
|
```
|
||||||
|
|
||||||
This feature will likely be replaced with a better solution to remove
|
This feature will likely be replaced with a better solution to remove
|
||||||
the need for forward declarations.
|
the need for forward declarations.
|
||||||
|
|
@ -295,8 +294,7 @@ Automatic dereferencing
|
||||||
Automatic dereferencing is performed for the first argument of a routine call.
|
Automatic dereferencing is performed for the first argument of a routine call.
|
||||||
This feature has to be enabled via `{.experimental: "implicitDeref".}`:
|
This feature has to be enabled via `{.experimental: "implicitDeref".}`:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
{.experimental: "implicitDeref".}
|
{.experimental: "implicitDeref".}
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -309,6 +307,7 @@ This feature has to be enabled via `{.experimental: "implicitDeref".}`:
|
||||||
let n = Node()
|
let n = Node()
|
||||||
echo n.depth
|
echo n.depth
|
||||||
# no need to write n[].depth
|
# no need to write n[].depth
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Special Operators
|
Special Operators
|
||||||
|
|
@ -333,21 +332,21 @@ for a dot operator that can be matched against a re-written form of
|
||||||
the expression, where the unknown field or proc name is passed to
|
the expression, where the unknown field or proc name is passed to
|
||||||
an `untyped` parameter:
|
an `untyped` parameter:
|
||||||
|
|
||||||
.. code-block:: nim
|
```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:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
template `.`(js: PJsonNode, field: untyped): JSON = js[astToStr(field)]
|
template `.`(js: PJsonNode, field: untyped): JSON = js[astToStr(field)]
|
||||||
|
|
||||||
var js = parseJson("{ x: 1, y: 2}")
|
var js = parseJson("{ x: 1, y: 2}")
|
||||||
echo js.x # outputs 1
|
echo js.x # outputs 1
|
||||||
echo js.y # outputs 2
|
echo js.y # outputs 2
|
||||||
|
```
|
||||||
|
|
||||||
The following dot operators are available:
|
The following dot operators are available:
|
||||||
|
|
||||||
|
|
@ -366,9 +365,9 @@ operator `.=`
|
||||||
-------------
|
-------------
|
||||||
This operator will be matched against assignments to missing fields.
|
This operator will be matched against assignments to missing fields.
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
a.b = c # becomes `.=`(a, b, c)
|
a.b = c # becomes `.=`(a, b, c)
|
||||||
|
```
|
||||||
|
|
||||||
Call operator
|
Call operator
|
||||||
-------------
|
-------------
|
||||||
|
|
@ -377,8 +376,7 @@ precedence over dot operators, however it does not match missing overloads
|
||||||
for existing routines. The experimental `callOperator` switch must be enabled
|
for existing routines. The experimental `callOperator` switch must be enabled
|
||||||
to use this operator.
|
to use this operator.
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
{.experimental: "callOperator".}
|
{.experimental: "callOperator".}
|
||||||
|
|
||||||
template `()`(a: int, b: float): untyped = $(a, b)
|
template `()`(a: int, b: float): untyped = $(a, b)
|
||||||
|
|
@ -402,6 +400,7 @@ to use this operator.
|
||||||
|
|
||||||
doAssert not compiles(a.b(c)) # gives a type mismatch error same as b(a, c)
|
doAssert not compiles(a.b(c)) # gives a type mismatch error same as b(a, c)
|
||||||
doAssert (a.b)(c) == `()`(a.b, c)
|
doAssert (a.b)(c) == `()`(a.b, c)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Extended macro pragmas
|
Extended macro pragmas
|
||||||
|
|
@ -412,9 +411,10 @@ can also be applied to type, variable and constant declarations.
|
||||||
|
|
||||||
For types:
|
For types:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
type
|
type
|
||||||
MyObject {.schema: "schema.protobuf".} = object
|
MyObject {.schema: "schema.protobuf".} = object
|
||||||
|
```
|
||||||
|
|
||||||
This is translated to a call to the `schema` macro with a `nnkTypeDef`
|
This is translated to a call to the `schema` macro with a `nnkTypeDef`
|
||||||
AST node capturing the left-hand side, remaining pragmas and the right-hand
|
AST node capturing the left-hand side, remaining pragmas and the right-hand
|
||||||
|
|
@ -437,19 +437,21 @@ For variables and constants, it is largely the same, except a unary node with
|
||||||
the same kind as the section containing a single definition is passed to macros,
|
the same kind as the section containing a single definition is passed to macros,
|
||||||
and macros can return any expression.
|
and macros can return any expression.
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
var
|
var
|
||||||
a = ...
|
a = ...
|
||||||
b {.importc, foo, nodecl.} = ...
|
b {.importc, foo, nodecl.} = ...
|
||||||
c = ...
|
c = ...
|
||||||
|
```
|
||||||
|
|
||||||
Assuming `foo` is a macro or a template, this is roughly equivalent to:
|
Assuming `foo` is a macro or a template, this is roughly equivalent to:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
var a = ...
|
var a = ...
|
||||||
foo:
|
foo:
|
||||||
var b {.importc, nodecl.} = ...
|
var b {.importc, nodecl.} = ...
|
||||||
var c = ...
|
var c = ...
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Symbols as template/macro calls
|
Symbols as template/macro calls
|
||||||
|
|
@ -459,7 +461,7 @@ Templates and macros that take no arguments can be called as lone symbols,
|
||||||
i.e. without parentheses. This is useful for repeated uses of complex
|
i.e. without parentheses. This is useful for repeated uses of complex
|
||||||
expressions that cannot conveniently be represented as runtime values.
|
expressions that cannot conveniently be represented as runtime values.
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
type Foo = object
|
type Foo = object
|
||||||
bar: int
|
bar: int
|
||||||
|
|
||||||
|
|
@ -468,6 +470,7 @@ expressions that cannot conveniently be represented as runtime values.
|
||||||
assert bar == 10
|
assert bar == 10
|
||||||
bar = 15
|
bar = 15
|
||||||
assert bar == 15
|
assert bar == 15
|
||||||
|
```
|
||||||
|
|
||||||
In the future, this may require more specific information on template or macro
|
In the future, this may require more specific information on template or macro
|
||||||
signatures to be used. Specializations for some applications of this may also
|
signatures to be used. Specializations for some applications of this may also
|
||||||
|
|
@ -483,8 +486,7 @@ Not nil annotation
|
||||||
All types for which `nil` is a valid value can be annotated with the
|
All types for which `nil` is a valid value can be annotated with the
|
||||||
`not nil` annotation to exclude `nil` as a valid value:
|
`not nil` annotation to exclude `nil` as a valid value:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
{.experimental: "notnil".}
|
{.experimental: "notnil".}
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -500,6 +502,7 @@ All types for which `nil` is a valid value can be annotated with the
|
||||||
# and also this:
|
# and also this:
|
||||||
var x: PObject
|
var x: PObject
|
||||||
p(x)
|
p(x)
|
||||||
|
```
|
||||||
|
|
||||||
The compiler ensures that every code path initializes variables which contain
|
The compiler ensures that every code path initializes variables which contain
|
||||||
non-nilable pointers. The details of this analysis are still to be specified
|
non-nilable pointers. The details of this analysis are still to be specified
|
||||||
|
|
@ -545,8 +548,7 @@ via a parameter that is not declared as a `var` parameter.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
{.experimental: "strictFuncs".}
|
{.experimental: "strictFuncs".}
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -566,6 +568,7 @@ For example:
|
||||||
m.data = "yeah" # the mutation is here
|
m.data = "yeah" # the mutation is here
|
||||||
# Error: 'mut' can have side effects
|
# Error: 'mut' can have side effects
|
||||||
# an object reachable from 'n' is potentially mutated
|
# an object reachable from 'n' is potentially mutated
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
The algorithm behind this analysis is described in
|
The algorithm behind this analysis is described in
|
||||||
|
|
@ -585,23 +588,23 @@ A view type is a type that is or contains one of the following types:
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
type
|
type
|
||||||
View1 = openArray[byte]
|
View1 = openArray[byte]
|
||||||
View2 = lent string
|
View2 = lent string
|
||||||
View3 = Table[openArray[char], int]
|
View3 = Table[openArray[char], int]
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Exceptions to this rule are types constructed via `ptr` or `proc`.
|
Exceptions to this rule are types constructed via `ptr` or `proc`.
|
||||||
For example, the following types are **not** view types:
|
For example, the following types are **not** view types:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
type
|
type
|
||||||
NotView1 = proc (x: openArray[int])
|
NotView1 = proc (x: openArray[int])
|
||||||
NotView2 = ptr openArray[char]
|
NotView2 = ptr openArray[char]
|
||||||
NotView3 = ptr array[4, lent int]
|
NotView3 = ptr array[4, lent int]
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
The mutability aspect of a view type is not part of the type but part
|
The mutability aspect of a view type is not part of the type but part
|
||||||
|
|
@ -618,8 +621,7 @@ it was borrowed from.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
{.experimental: "views".}
|
{.experimental: "views".}
|
||||||
|
|
||||||
proc take(a: openArray[int]) =
|
proc take(a: openArray[int]) =
|
||||||
|
|
@ -641,6 +643,7 @@ For example:
|
||||||
|
|
||||||
|
|
||||||
main(@[11, 22, 33])
|
main(@[11, 22, 33])
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
A local variable of a view type can borrow from a location
|
A local variable of a view type can borrow from a location
|
||||||
|
|
@ -699,8 +702,7 @@ For the duration of the borrow operation, no mutations to the borrowed locations
|
||||||
may be performed except via the view that borrowed from the
|
may be performed except via the view that borrowed from the
|
||||||
location. The borrowed location is said to be *sealed* during the borrow.
|
location. The borrowed location is said to be *sealed* during the borrow.
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
{.experimental: "views".}
|
{.experimental: "views".}
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -711,16 +713,17 @@ location. The borrowed location is said to be *sealed* during the borrow.
|
||||||
let v: lent Obj = s[0] # seal 's'
|
let v: lent Obj = s[0] # seal 's'
|
||||||
s.setLen 0 # prevented at compile-time because 's' is sealed.
|
s.setLen 0 # prevented at compile-time because 's' is sealed.
|
||||||
echo v.field
|
echo v.field
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
The scope of the view does not matter:
|
The scope of the view does not matter:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
proc valid(s: var seq[Obj]) =
|
proc valid(s: var seq[Obj]) =
|
||||||
let v: lent Obj = s[0] # begin of borrow
|
let v: lent Obj = s[0] # begin of borrow
|
||||||
echo v.field # end of borrow
|
echo v.field # end of borrow
|
||||||
s.setLen 0 # valid because 'v' isn't used afterwards
|
s.setLen 0 # valid because 'v' isn't used afterwards
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
The analysis requires as much precision about mutations as is reasonably obtainable,
|
The analysis requires as much precision about mutations as is reasonably obtainable,
|
||||||
|
|
@ -730,13 +733,13 @@ with `--experimental:strictFuncs`:option:.
|
||||||
|
|
||||||
The analysis is currently control flow insensitive:
|
The analysis is currently control flow insensitive:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
proc invalid(s: var seq[Obj]) =
|
proc invalid(s: var seq[Obj]) =
|
||||||
let v: lent Obj = s[0]
|
let v: lent Obj = s[0]
|
||||||
if false:
|
if false:
|
||||||
s.setLen 0
|
s.setLen 0
|
||||||
echo v.field
|
echo v.field
|
||||||
|
```
|
||||||
|
|
||||||
In this example, the compiler assumes that `s.setLen 0` invalidates the
|
In this example, the compiler assumes that `s.setLen 0` invalidates the
|
||||||
borrow operation of `v` even though a human being can easily see that it
|
borrow operation of `v` even though a human being can easily see that it
|
||||||
|
|
@ -824,8 +827,7 @@ arbitrary set of requirements that the matched type must satisfy.
|
||||||
|
|
||||||
Concepts are written in the following form:
|
Concepts are written in the following form:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
type
|
type
|
||||||
Comparable = concept x, y
|
Comparable = concept x, y
|
||||||
(x < y) is bool
|
(x < y) is bool
|
||||||
|
|
@ -838,6 +840,7 @@ Concepts are written in the following form:
|
||||||
|
|
||||||
for value in s:
|
for value in s:
|
||||||
value is T
|
value is T
|
||||||
|
```
|
||||||
|
|
||||||
The concept matches if:
|
The concept matches if:
|
||||||
|
|
||||||
|
|
@ -850,29 +853,28 @@ as `var`, `ref`, `ptr` and `static` to denote a more specific type of
|
||||||
instance. You can also apply the `type` modifier to create a named instance of
|
instance. You can also apply the `type` modifier to create a named instance of
|
||||||
the type itself:
|
the type itself:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
type
|
type
|
||||||
MyConcept = concept x, var v, ref r, ptr p, static s, type T
|
MyConcept = concept x, var v, ref r, ptr p, static s, type T
|
||||||
...
|
...
|
||||||
|
```
|
||||||
|
|
||||||
Within the concept body, types can appear in positions where ordinary values
|
Within the concept body, types can appear in positions where ordinary values
|
||||||
and parameters are expected. This provides a more convenient way to check for
|
and parameters are expected. This provides a more convenient way to check for
|
||||||
the presence of callable symbols with specific signatures:
|
the presence of callable symbols with specific signatures:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
type
|
type
|
||||||
OutputStream = concept var s
|
OutputStream = concept var s
|
||||||
s.write(string)
|
s.write(string)
|
||||||
|
```
|
||||||
|
|
||||||
In order to check for symbols accepting `type` params, you must prefix
|
In order to check for symbols accepting `type` params, you must prefix
|
||||||
the type with the explicit `type` modifier. The named instance of the
|
the type with the explicit `type` modifier. The named instance of the
|
||||||
type, following the `concept` keyword is also considered to have the
|
type, following the `concept` keyword is also considered to have the
|
||||||
explicit modifier and will be matched only as a type.
|
explicit modifier and will be matched only as a type.
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
type
|
type
|
||||||
# Let's imagine a user-defined casting framework with operators
|
# Let's imagine a user-defined casting framework with operators
|
||||||
# such as `val.to(string)` and `val.to(JSonValue)`. We can test
|
# such as `val.to(string)` and `val.to(JSonValue)`. We can test
|
||||||
|
|
@ -890,6 +892,7 @@ explicit modifier and will be matched only as a type.
|
||||||
x is AdditiveMonoid
|
x is AdditiveMonoid
|
||||||
-x is T
|
-x is T
|
||||||
x - y is T
|
x - y is T
|
||||||
|
```
|
||||||
|
|
||||||
Please note that the `is` operator allows one to easily verify the precise
|
Please note that the `is` operator allows one to easily verify the precise
|
||||||
type signatures of the required operations, but since type inference and
|
type signatures of the required operations, but since type inference and
|
||||||
|
|
@ -909,12 +912,12 @@ When you need to understand why the compiler is not matching a particular
|
||||||
concept and, as a result, a wrong overload is selected, you can apply the
|
concept and, as a result, a wrong overload is selected, you can apply the
|
||||||
`explain` pragma to either the concept body or a particular call-site.
|
`explain` pragma to either the concept body or a particular call-site.
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
type
|
type
|
||||||
MyConcept {.explain.} = concept ...
|
MyConcept {.explain.} = concept ...
|
||||||
|
|
||||||
overloadedProc(x, y, z) {.explain.}
|
overloadedProc(x, y, z) {.explain.}
|
||||||
|
```
|
||||||
|
|
||||||
This will provide Hints in the compiler output either every time the concept is
|
This will provide Hints in the compiler output either every time the concept is
|
||||||
not matched or only on the particular call-site.
|
not matched or only on the particular call-site.
|
||||||
|
|
@ -925,8 +928,7 @@ Generic concepts and type binding rules
|
||||||
|
|
||||||
The concept types can be parametric just like the regular generic types:
|
The concept types can be parametric just like the regular generic types:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
### matrixalgo.nim
|
### matrixalgo.nim
|
||||||
|
|
||||||
import std/typetraits
|
import std/typetraits
|
||||||
|
|
@ -986,6 +988,7 @@ The concept types can be parametric just like the regular generic types:
|
||||||
|
|
||||||
echo m.transposed.determinant
|
echo m.transposed.determinant
|
||||||
setPerspectiveProjection projectionMatrix
|
setPerspectiveProjection projectionMatrix
|
||||||
|
```
|
||||||
|
|
||||||
When the concept type is matched against a concrete type, the unbound type
|
When the concept type is matched against a concrete type, the unbound type
|
||||||
parameters are inferred from the body of the concept in a way that closely
|
parameters are inferred from the body of the concept in a way that closely
|
||||||
|
|
@ -999,11 +1002,11 @@ and `x.data is seq[T]`.
|
||||||
Unbound static params will be inferred from expressions involving the `==`
|
Unbound static params will be inferred from expressions involving the `==`
|
||||||
operator and also when types dependent on them are being matched:
|
operator and also when types dependent on them are being matched:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
type
|
type
|
||||||
MatrixReducer[M, N: static int; T] = concept x
|
MatrixReducer[M, N: static int; T] = concept x
|
||||||
x.reduce(SquareMatrix[N, T]) is array[M, int]
|
x.reduce(SquareMatrix[N, T]) is array[M, int]
|
||||||
|
```
|
||||||
|
|
||||||
The Nim compiler includes a simple linear equation solver, allowing it to
|
The Nim compiler includes a simple linear equation solver, allowing it to
|
||||||
infer static params in some situations where integer arithmetic is involved.
|
infer static params in some situations where integer arithmetic is involved.
|
||||||
|
|
@ -1014,8 +1017,7 @@ modifier to any of the otherwise inferable types to get a type that will be
|
||||||
matched without permanently inferring it. This may be useful when you need
|
matched without permanently inferring it. This may be useful when you need
|
||||||
to match several procs accepting the same wide class of types:
|
to match several procs accepting the same wide class of types:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
type
|
type
|
||||||
Enumerable[T] = concept e
|
Enumerable[T] = concept e
|
||||||
for v in e:
|
for v in e:
|
||||||
|
|
@ -1032,13 +1034,13 @@ to match several procs accepting the same wide class of types:
|
||||||
# it's also possible to give an alias name to a `bind many` type class
|
# it's also possible to give an alias name to a `bind many` type class
|
||||||
type Enum = distinct Enumerable
|
type Enum = distinct Enumerable
|
||||||
o.baz is Enum
|
o.baz is Enum
|
||||||
|
```
|
||||||
|
|
||||||
On the other hand, using `bind once` types allows you to test for equivalent
|
On the other hand, using `bind once` types allows you to test for equivalent
|
||||||
types used in multiple signatures, without actually requiring any concrete
|
types used in multiple signatures, without actually requiring any concrete
|
||||||
types, thus allowing you to encode implementation-defined types:
|
types, thus allowing you to encode implementation-defined types:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
type
|
type
|
||||||
MyConcept = concept x
|
MyConcept = concept x
|
||||||
type T1 = auto
|
type T1 = auto
|
||||||
|
|
@ -1049,6 +1051,7 @@ types, thus allowing you to encode implementation-defined types:
|
||||||
x.alpha(T2)
|
x.alpha(T2)
|
||||||
x.omega(T2) # both procs must accept the same type
|
x.omega(T2) # both procs must accept the same type
|
||||||
# and it must be a numeric sequence
|
# and it must be a numeric sequence
|
||||||
|
```
|
||||||
|
|
||||||
As seen in the previous examples, you can refer to generic concepts such as
|
As seen in the previous examples, you can refer to generic concepts such as
|
||||||
`Enumerable[T]` just by their short name. Much like the regular generic types,
|
`Enumerable[T]` just by their short name. Much like the regular generic types,
|
||||||
|
|
@ -1066,9 +1069,7 @@ in any required way. For example, here is how one might define the classic
|
||||||
`Functor` concept from Haskell and then demonstrate that Nim's `Option[T]`
|
`Functor` concept from Haskell and then demonstrate that Nim's `Option[T]`
|
||||||
type is an instance of it:
|
type is an instance of it:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim test = "nim c $1"
|
||||||
:test: "nim c $1"
|
|
||||||
|
|
||||||
import std/[sugar, typetraits]
|
import std/[sugar, typetraits]
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -1089,6 +1090,7 @@ type is an instance of it:
|
||||||
|
|
||||||
import std/options
|
import std/options
|
||||||
echo Option[int] is Functor # prints true
|
echo Option[int] is Functor # prints true
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Concept derived values
|
Concept derived values
|
||||||
|
|
@ -1098,8 +1100,7 @@ All top level constants or types appearing within the concept body are
|
||||||
accessible through the dot operator in procs where the concept was successfully
|
accessible through the dot operator in procs where the concept was successfully
|
||||||
matched to a concrete type:
|
matched to a concrete type:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
type
|
type
|
||||||
DateTime = concept t1, t2, type T
|
DateTime = concept t1, t2, type T
|
||||||
const Min = T.MinDate
|
const Min = T.MinDate
|
||||||
|
|
@ -1121,6 +1122,7 @@ matched to a concrete type:
|
||||||
|
|
||||||
deviation: float
|
deviation: float
|
||||||
...
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Concept refinement
|
Concept refinement
|
||||||
|
|
@ -1133,8 +1135,7 @@ overload resolution, Nim will assign a higher precedence to the most specific
|
||||||
one. As an alternative way of defining concept refinements, you can use the
|
one. As an alternative way of defining concept refinements, you can use the
|
||||||
object inheritance syntax involving the `of` keyword:
|
object inheritance syntax involving the `of` keyword:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
type
|
type
|
||||||
Graph = concept g, type G of EquallyComparable, Copyable
|
Graph = concept g, type G of EquallyComparable, Copyable
|
||||||
type
|
type
|
||||||
|
|
@ -1168,6 +1169,7 @@ object inheritance syntax involving the `of` keyword:
|
||||||
proc f(g: IncidendeGraph)
|
proc f(g: IncidendeGraph)
|
||||||
proc f(g: BidirectionalGraph) # this one will be preferred if we pass a type
|
proc f(g: BidirectionalGraph) # this one will be preferred if we pass a type
|
||||||
# matching the BidirectionalGraph concept
|
# matching the BidirectionalGraph concept
|
||||||
|
```
|
||||||
|
|
||||||
..
|
..
|
||||||
Converter type classes
|
Converter type classes
|
||||||
|
|
@ -1177,8 +1179,7 @@ object inheritance syntax involving the `of` keyword:
|
||||||
a small set of simpler types. This is achieved with a `return` statement within
|
a small set of simpler types. This is achieved with a `return` statement within
|
||||||
the concept body:
|
the concept body:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
type
|
type
|
||||||
Stringable = concept x
|
Stringable = concept x
|
||||||
$x is string
|
$x is string
|
||||||
|
|
@ -1202,6 +1203,7 @@ object inheritance syntax involving the `of` keyword:
|
||||||
# the same call at the cost of additional instantiations
|
# the same call at the cost of additional instantiations
|
||||||
# the varargs param will be converted to a tuple
|
# the varargs param will be converted to a tuple
|
||||||
proc log(format: static string, varargs[distinct StringRef])
|
proc log(format: static string, varargs[distinct StringRef])
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
..
|
..
|
||||||
|
|
@ -1229,8 +1231,7 @@ object inheritance syntax involving the `of` keyword:
|
||||||
a converter type class, which converts the regular instances of the matching
|
a converter type class, which converts the regular instances of the matching
|
||||||
types to the corresponding VTable type.
|
types to the corresponding VTable type.
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
type
|
type
|
||||||
IntEnumerable = vtref Enumerable[int]
|
IntEnumerable = vtref Enumerable[int]
|
||||||
|
|
||||||
|
|
@ -1243,6 +1244,7 @@ object inheritance syntax involving the `of` keyword:
|
||||||
|
|
||||||
proc addStream(o: var MyObject, e: OutputStream.vtref) =
|
proc addStream(o: var MyObject, e: OutputStream.vtref) =
|
||||||
o.streams.add e
|
o.streams.add e
|
||||||
|
```
|
||||||
|
|
||||||
The procs that will be included in the vtable are derived from the concept
|
The procs that will be included in the vtable are derived from the concept
|
||||||
body and include all proc calls for which all param types were specified as
|
body and include all proc calls for which all param types were specified as
|
||||||
|
|
@ -1272,9 +1274,9 @@ object inheritance syntax involving the `of` keyword:
|
||||||
|
|
||||||
The signature has to be:
|
The signature has to be:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
proc `=deepCopy`(x: T): T
|
proc `=deepCopy`(x: T): T
|
||||||
|
```
|
||||||
|
|
||||||
This mechanism will be used by most data structures that support shared memory,
|
This mechanism will be used by most data structures that support shared memory,
|
||||||
like channels, to implement thread safe automatic memory management.
|
like channels, to implement thread safe automatic memory management.
|
||||||
|
|
@ -1289,7 +1291,7 @@ Dynamic arguments for bindSym
|
||||||
This experimental feature allows the symbol name argument of `macros.bindSym`
|
This experimental feature allows the symbol name argument of `macros.bindSym`
|
||||||
to be computed dynamically.
|
to be computed dynamically.
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
{.experimental: "dynamicBindSym".}
|
{.experimental: "dynamicBindSym".}
|
||||||
|
|
||||||
import macros
|
import macros
|
||||||
|
|
@ -1299,6 +1301,7 @@ to be computed dynamically.
|
||||||
|
|
||||||
echo callOp("+", 1, 2)
|
echo callOp("+", 1, 2)
|
||||||
echo callOp("-", 5, 4)
|
echo callOp("-", 5, 4)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Term rewriting macros
|
Term rewriting macros
|
||||||
|
|
@ -1309,12 +1312,12 @@ 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:
|
compilation pipeline with user defined optimizations:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
template optMul{`*`(a, 2)}(a: int): int = a + a
|
template optMul{`*`(a, 2)}(a: int): int = a + a
|
||||||
|
|
||||||
let x = 3
|
let x = 3
|
||||||
echo x * 2
|
echo x * 2
|
||||||
|
```
|
||||||
|
|
||||||
The compiler now rewrites `x * 2` as `x + x`. The code inside the
|
The compiler now rewrites `x * 2` as `x + x`. The code inside the
|
||||||
curly brackets is the pattern to match against. The operators `*`, `**`,
|
curly brackets is the pattern to match against. The operators `*`, `**`,
|
||||||
|
|
@ -1332,8 +1335,7 @@ Once this limit has been passed, the term rewriting macro will be ignored.
|
||||||
Unfortunately optimizations are hard to get right and even this tiny example
|
Unfortunately optimizations are hard to get right and even this tiny example
|
||||||
is **wrong**:
|
is **wrong**:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
template optMul{`*`(a, 2)}(a: int): int = a + a
|
template optMul{`*`(a, 2)}(a: int): int = a + a
|
||||||
|
|
||||||
proc f(): int =
|
proc f(): int =
|
||||||
|
|
@ -1341,12 +1343,12 @@ is **wrong**:
|
||||||
result = 55
|
result = 55
|
||||||
|
|
||||||
echo f() * 2
|
echo f() * 2
|
||||||
|
```
|
||||||
|
|
||||||
We cannot duplicate 'a' if it denotes an expression that has a side effect!
|
We cannot duplicate 'a' if it denotes an expression that has a side effect!
|
||||||
Fortunately Nim supports side effect analysis:
|
Fortunately Nim supports side effect analysis:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
template optMul{`*`(a, 2)}(a: int{noSideEffect}): int = a + a
|
template optMul{`*`(a, 2)}(a: int{noSideEffect}): int = a + a
|
||||||
|
|
||||||
proc f(): int =
|
proc f(): int =
|
||||||
|
|
@ -1354,6 +1356,7 @@ Fortunately Nim supports side effect analysis:
|
||||||
result = 55
|
result = 55
|
||||||
|
|
||||||
echo f() * 2 # not optimized ;-)
|
echo f() * 2 # not optimized ;-)
|
||||||
|
```
|
||||||
|
|
||||||
You can make one overload matching with a constraint and one without, and the
|
You can make one overload matching with a constraint and one without, and the
|
||||||
one with a constraint will have precedence, and so you can handle both cases
|
one with a constraint will have precedence, and so you can handle both cases
|
||||||
|
|
@ -1363,15 +1366,15 @@ So what about `2 * a`? We should tell the compiler `*` is commutative. We
|
||||||
cannot really do that however as the following code only swaps arguments
|
cannot really do that however as the following code only swaps arguments
|
||||||
blindly:
|
blindly:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
template mulIsCommutative{`*`(a, b)}(a, b: int): int = b * a
|
template mulIsCommutative{`*`(a, b)}(a, b: int): int = b * a
|
||||||
|
```
|
||||||
|
|
||||||
What optimizers really need to do is a *canonicalization*:
|
What optimizers really need to do is a *canonicalization*:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
template canonMul{`*`(a, b)}(a: int{lit}, b: int): int = b * a
|
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.
|
type `int`, but only if it's a literal.
|
||||||
|
|
@ -1429,17 +1432,16 @@ 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
|
but also to every other bound parameter; syntactically they need to occur after
|
||||||
the ordinary AST predicates:
|
the ordinary AST predicates:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
template ex{a = b + c}(a: int{noalias}, b, c: int) =
|
template ex{a = b + c}(a: int{noalias}, b, c: int) =
|
||||||
# this transformation is only valid if 'b' and 'c' do not alias 'a':
|
# this transformation is only valid if 'b' and 'c' do not alias 'a':
|
||||||
a = b
|
a = b
|
||||||
inc a, c
|
inc a, c
|
||||||
|
```
|
||||||
|
|
||||||
Another example:
|
Another example:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
proc somefunc(s: string) = assert s == "variable"
|
proc somefunc(s: string) = assert s == "variable"
|
||||||
proc somefunc(s: string{nkStrLit}) = assert s == "literal"
|
proc somefunc(s: string{nkStrLit}) = assert s == "literal"
|
||||||
proc somefunc(s: string{nkRStrLit}) = assert s == r"raw"
|
proc somefunc(s: string{nkRStrLit}) = assert s == r"raw"
|
||||||
|
|
@ -1454,6 +1456,7 @@ Another example:
|
||||||
somefunc("literal")
|
somefunc("literal")
|
||||||
somefunc(r"raw")
|
somefunc(r"raw")
|
||||||
somefunc("""triple""")
|
somefunc("""triple""")
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Pattern operators
|
Pattern operators
|
||||||
|
|
@ -1467,21 +1470,21 @@ if they are written in infix notation.
|
||||||
|
|
||||||
The `|` operator if used as infix operator creates an ordered choice:
|
The `|` operator if used as infix operator creates an ordered choice:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
template t{0|1}(): untyped = 3
|
template t{0|1}(): untyped = 3
|
||||||
let a = 1
|
let a = 1
|
||||||
# outputs 3:
|
# outputs 3:
|
||||||
echo a
|
echo a
|
||||||
|
```
|
||||||
|
|
||||||
The matching is performed after the compiler performed some optimizations like
|
The matching is performed after the compiler performed some optimizations like
|
||||||
constant folding, so the following does not work:
|
constant folding, so the following does not work:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
template t{0|1}(): untyped = 3
|
template t{0|1}(): untyped = 3
|
||||||
# outputs 1:
|
# outputs 1:
|
||||||
echo 1
|
echo 1
|
||||||
|
```
|
||||||
|
|
||||||
The reason is that the compiler already transformed the 1 into "1" for
|
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
|
the `echo` statement. However, a term rewriting macro should not change the
|
||||||
|
|
@ -1494,20 +1497,19 @@ command line option or temporarily with the `patterns` pragma.
|
||||||
A pattern expression can be bound to a pattern parameter via the `expr{param}`
|
A pattern expression can be bound to a pattern parameter via the `expr{param}`
|
||||||
notation:
|
notation:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
template t{(0|1|2){x}}(x: untyped): untyped = x + 1
|
template t{(0|1|2){x}}(x: untyped): untyped = x + 1
|
||||||
let a = 1
|
let a = 1
|
||||||
# outputs 2:
|
# outputs 2:
|
||||||
echo a
|
echo a
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### The `~` operator
|
### The `~` operator
|
||||||
|
|
||||||
The `~` operator is the 'not' operator in patterns:
|
The `~` operator is the 'not' operator in patterns:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
template t{x = (~x){y} and (~x){z}}(x, y, z: bool) =
|
template t{x = (~x){y} and (~x){z}}(x, y, z: bool) =
|
||||||
x = y
|
x = y
|
||||||
if x: x = z
|
if x: x = z
|
||||||
|
|
@ -1518,6 +1520,7 @@ The `~` operator is the 'not' operator in patterns:
|
||||||
c = false
|
c = false
|
||||||
a = b and c
|
a = b and c
|
||||||
echo a
|
echo a
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### The `*` operator
|
### The `*` operator
|
||||||
|
|
@ -1525,8 +1528,7 @@ The `~` operator is the 'not' operator in patterns:
|
||||||
The `*` operator can *flatten* a nested binary expression like `a & b & c`
|
The `*` operator can *flatten* a nested binary expression like `a & b & c`
|
||||||
to `&(a, b, c)`:
|
to `&(a, b, c)`:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
var
|
var
|
||||||
calls = 0
|
calls = 0
|
||||||
|
|
||||||
|
|
@ -1542,6 +1544,7 @@ to `&(a, b, c)`:
|
||||||
|
|
||||||
# check that it's been optimized properly:
|
# check that it's been optimized properly:
|
||||||
doAssert calls == 1
|
doAssert calls == 1
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
The second operator of `*` must be a parameter; it is used to gather all the
|
The second operator of `*` must be a parameter; it is used to gather all the
|
||||||
|
|
@ -1550,9 +1553,9 @@ 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`
|
which is flattened into a call expression; thus the invocation of `optConc`
|
||||||
produces:
|
produces:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
`&&`("my", space & "awe", "some ", "concat")
|
`&&`("my", space & "awe", "some ", "concat")
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### The `**` operator
|
### The `**` operator
|
||||||
|
|
@ -1560,8 +1563,7 @@ produces:
|
||||||
The `**` is much like the `*` operator, except that it gathers not only
|
The `**` is much like the `*` operator, except that it gathers not only
|
||||||
all the arguments, but also the matched operators in reverse polish notation:
|
all the arguments, but also the matched operators in reverse polish notation:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
import std/macros
|
import std/macros
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -1582,6 +1584,7 @@ all the arguments, but also the matched operators in reverse polish notation:
|
||||||
var x, y, z: Matrix
|
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
|
This passes the expression `x + y * z - x` to the `optM` macro as
|
||||||
an `nnkArgList` node containing::
|
an `nnkArgList` node containing::
|
||||||
|
|
@ -1605,13 +1608,13 @@ Parameters in a pattern are type checked in the matching process. If a
|
||||||
parameter is of the type `varargs`, it is treated specially and can match
|
parameter is of the type `varargs`, it is treated specially and can match
|
||||||
0 or more arguments in the AST to be matched against:
|
0 or more arguments in the AST to be matched against:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
template optWrite{
|
template optWrite{
|
||||||
write(f, x)
|
write(f, x)
|
||||||
((write|writeLine){w})(f, y)
|
((write|writeLine){w})(f, y)
|
||||||
}(x, y: varargs[untyped], f: File, w: untyped) =
|
}(x, y: varargs[untyped], f: File, w: untyped) =
|
||||||
w(f, x, y)
|
w(f, x, y)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
noRewrite pragma
|
noRewrite pragma
|
||||||
|
|
@ -1625,12 +1628,12 @@ e.g. when rewriting term to same term plus extra content.
|
||||||
`noRewrite` pragma can actually prevent further rewriting on marked code,
|
`noRewrite` pragma can actually prevent further rewriting on marked code,
|
||||||
e.g. with given example `echo("ab")` will be rewritten just once:
|
e.g. with given example `echo("ab")` will be rewritten just once:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
template pwnEcho{echo(x)}(x: untyped) =
|
template pwnEcho{echo(x)}(x: untyped) =
|
||||||
{.noRewrite.}: echo("pwned!")
|
{.noRewrite.}: echo("pwned!")
|
||||||
|
|
||||||
echo "ab"
|
echo "ab"
|
||||||
|
```
|
||||||
|
|
||||||
`noRewrite` pragma can be useful to control term-rewriting macros recursion.
|
`noRewrite` pragma can be useful to control term-rewriting macros recursion.
|
||||||
|
|
||||||
|
|
@ -1642,13 +1645,13 @@ Example: Partial evaluation
|
||||||
The following example shows how some simple partial evaluation can be
|
The following example shows how some simple partial evaluation can be
|
||||||
implemented with term rewriting:
|
implemented with term rewriting:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
proc p(x, y: int; cond: bool): int =
|
proc p(x, y: int; cond: bool): int =
|
||||||
result = if cond: x + y else: x - y
|
result = if cond: x + y else: x - y
|
||||||
|
|
||||||
template optP1{p(x, y, true)}(x, y: untyped): untyped = x + y
|
template optP1{p(x, y, true)}(x, y: untyped): untyped = x + y
|
||||||
template optP2{p(x, y, false)}(x, y: untyped): untyped = x - y
|
template optP2{p(x, y, false)}(x, y: untyped): untyped = x - y
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Example: Hoisting
|
Example: Hoisting
|
||||||
|
|
@ -1656,8 +1659,7 @@ Example: Hoisting
|
||||||
|
|
||||||
The following example shows how some form of hoisting can be implemented:
|
The following example shows how some form of hoisting can be implemented:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
import std/pegs
|
import std/pegs
|
||||||
|
|
||||||
template optPeg{peg(pattern)}(pattern: string{lit}): Peg =
|
template optPeg{peg(pattern)}(pattern: string{lit}): Peg =
|
||||||
|
|
@ -1667,6 +1669,7 @@ The following example shows how some form of hoisting can be implemented:
|
||||||
for i in 0 .. 3:
|
for i in 0 .. 3:
|
||||||
echo match("(a b c)", peg"'(' @ ')'")
|
echo match("(a b c)", peg"'(' @ ')'")
|
||||||
echo match("W_HI_Le", peg"\y 'while'")
|
echo match("W_HI_Le", peg"\y 'while'")
|
||||||
|
```
|
||||||
|
|
||||||
The `optPeg` template optimizes the case of a peg constructor with a string
|
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
|
literal, so that the pattern will only be parsed once at program startup and
|
||||||
|
|
@ -1680,8 +1683,7 @@ AST based overloading
|
||||||
Parameter constraints can also be used for ordinary routine parameters; these
|
Parameter constraints can also be used for ordinary routine parameters; these
|
||||||
constraints then affect ordinary overloading resolution:
|
constraints then affect ordinary overloading resolution:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
proc optLit(a: string{lit|`const`}) =
|
proc optLit(a: string{lit|`const`}) =
|
||||||
echo "string literal"
|
echo "string literal"
|
||||||
proc optLit(a: string) =
|
proc optLit(a: string) =
|
||||||
|
|
@ -1696,6 +1698,7 @@ constraints then affect ordinary overloading resolution:
|
||||||
optLit("literal")
|
optLit("literal")
|
||||||
optLit(constant)
|
optLit(constant)
|
||||||
optLit(variable)
|
optLit(variable)
|
||||||
|
```
|
||||||
|
|
||||||
However, the constraints `alias` and `noalias` are not available in
|
However, the constraints `alias` and `noalias` are not available in
|
||||||
ordinary routines.
|
ordinary routines.
|
||||||
|
|
@ -1735,8 +1738,7 @@ Spawn statement
|
||||||
|
|
||||||
The `spawn`:idx: statement can be used to pass a task to the thread pool:
|
The `spawn`:idx: statement can be used to pass a task to the thread pool:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
import std/threadpool
|
import std/threadpool
|
||||||
|
|
||||||
proc processLine(line: string) =
|
proc processLine(line: string) =
|
||||||
|
|
@ -1745,6 +1747,7 @@ The `spawn`:idx: statement can be used to pass a task to the thread pool:
|
||||||
for x in lines("myinput.txt"):
|
for x in lines("myinput.txt"):
|
||||||
spawn processLine(x)
|
spawn processLine(x)
|
||||||
sync()
|
sync()
|
||||||
|
```
|
||||||
|
|
||||||
For reasons of type safety and implementation simplicity the expression
|
For reasons of type safety and implementation simplicity the expression
|
||||||
that `spawn` takes is restricted:
|
that `spawn` takes is restricted:
|
||||||
|
|
@ -1768,8 +1771,7 @@ a `data flow variable`:idx: `FlowVar[T]` that can be read from. The reading
|
||||||
with the `^` operator is **blocking**. However, one can use `blockUntilAny` to
|
with the `^` operator is **blocking**. However, one can use `blockUntilAny` to
|
||||||
wait on multiple flow variables at the same time:
|
wait on multiple flow variables at the same time:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
import std/threadpool, ...
|
import std/threadpool, ...
|
||||||
|
|
||||||
# wait until 2 out of 3 servers received the update:
|
# wait until 2 out of 3 servers received the update:
|
||||||
|
|
@ -1781,6 +1783,7 @@ wait on multiple flow variables at the same time:
|
||||||
assert index >= 0
|
assert index >= 0
|
||||||
responses.del(index)
|
responses.del(index)
|
||||||
discard blockUntilAny(responses)
|
discard blockUntilAny(responses)
|
||||||
|
```
|
||||||
|
|
||||||
Data flow variables ensure that no data races are possible. Due to
|
Data flow variables ensure that no data races are possible. Due to
|
||||||
technical limitations, not every type `T` can be used in
|
technical limitations, not every type `T` can be used in
|
||||||
|
|
@ -1795,9 +1798,7 @@ Parallel statement
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim test = "nim c --threads:on $1"
|
||||||
:test: "nim c --threads:on $1"
|
|
||||||
|
|
||||||
# Compute pi in an inefficient way
|
# Compute pi in an inefficient way
|
||||||
import std/[strutils, math, threadpool]
|
import std/[strutils, math, threadpool]
|
||||||
{.experimental: "parallel".}
|
{.experimental: "parallel".}
|
||||||
|
|
@ -1813,6 +1814,7 @@ Example:
|
||||||
result += ch[k]
|
result += ch[k]
|
||||||
|
|
||||||
echo formatFloat(pi(5000))
|
echo formatFloat(pi(5000))
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
The parallel statement is the preferred mechanism to introduce parallelism in a
|
The parallel statement is the preferred mechanism to introduce parallelism in a
|
||||||
|
|
@ -1853,8 +1855,7 @@ 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:
|
single `locks` section:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
var a, b: TLock[2]
|
var a, b: TLock[2]
|
||||||
var x: TLock[1]
|
var x: TLock[1]
|
||||||
# invalid locking order: TLock[1] cannot be acquired before TLock[2]:
|
# invalid locking order: TLock[1] cannot be acquired before TLock[2]:
|
||||||
|
|
@ -1874,14 +1875,14 @@ single `locks` section:
|
||||||
# valid locking order, locks of the same level acquired at the same time:
|
# valid locking order, locks of the same level acquired at the same time:
|
||||||
{.locks: [a, b].}:
|
{.locks: [a, b].}:
|
||||||
...
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Here is how a typical multilock statement can be implemented in Nim. Note how
|
Here is how a typical multilock statement can be implemented in Nim. Note how
|
||||||
the runtime check is required to ensure a global ordering for two locks `a`
|
the runtime check is required to ensure a global ordering for two locks `a`
|
||||||
and `b` of the same lock level:
|
and `b` of the same lock level:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
template multilock(a, b: ptr TLock; body: untyped) =
|
template multilock(a, b: ptr TLock; body: untyped) =
|
||||||
if cast[ByteAddress](a) < cast[ByteAddress](b):
|
if cast[ByteAddress](a) < cast[ByteAddress](b):
|
||||||
pthread_mutex_lock(a)
|
pthread_mutex_lock(a)
|
||||||
|
|
@ -1895,20 +1896,21 @@ and `b` of the same lock level:
|
||||||
finally:
|
finally:
|
||||||
pthread_mutex_unlock(a)
|
pthread_mutex_unlock(a)
|
||||||
pthread_mutex_unlock(b)
|
pthread_mutex_unlock(b)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Whole routines can also be annotated with a `locks` pragma that takes a lock
|
Whole routines can also be annotated with a `locks` pragma that takes a lock
|
||||||
level. This then means that the routine may acquire locks of up to this level.
|
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:
|
This is essential so that procs can be called within a `locks` section:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
proc p() {.locks: 3.} = discard
|
proc p() {.locks: 3.} = discard
|
||||||
|
|
||||||
var a: TLock[4]
|
var a: TLock[4]
|
||||||
{.locks: [a].}:
|
{.locks: [a].}:
|
||||||
# p's locklevel (3) is strictly less than a's (4) so the call is allowed:
|
# p's locklevel (3) is strictly less than a's (4) so the call is allowed:
|
||||||
p()
|
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
|
||||||
|
|
@ -1924,8 +1926,7 @@ cannot be inferred statically, leading to compiler warnings. By using
|
||||||
`{.locks: "unknown".}`, the base method can be marked explicitly as
|
`{.locks: "unknown".}`, the base method can be marked explicitly as
|
||||||
having unknown lock level as well:
|
having unknown lock level as well:
|
||||||
|
|
||||||
.. code-block:: nim
|
```nim
|
||||||
|
|
||||||
type SomeBase* = ref object of RootObj
|
type SomeBase* = ref object of RootObj
|
||||||
type SomeDerived* = ref object of SomeBase
|
type SomeDerived* = ref object of SomeBase
|
||||||
memberProc*: proc ()
|
memberProc*: proc ()
|
||||||
|
|
@ -1934,5 +1935,6 @@ having unknown lock level as well:
|
||||||
method testMethod(g: SomeDerived) =
|
method testMethod(g: SomeDerived) =
|
||||||
if g.memberProc != nil:
|
if g.memberProc != nil:
|
||||||
g.memberProc()
|
g.memberProc()
|
||||||
|
```
|
||||||
|
|
||||||
This feature may be removed in the future due to its practical difficulties.
|
This feature may be removed in the future due to its practical difficulties.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue