mirror of
https://github.com/nim-lang/Nim.git
synced 2026-02-12 06:18:51 +00:00
Markdown code blocks part 4 (#20189)
No logic was added, just 8 more files have been migrated.
This commit is contained in:
@@ -18,11 +18,11 @@ General Guidelines
|
||||
* (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).
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
proc someproc*(s: string, foo: int) =
|
||||
## Use single backticks for inline code, e.g.: `s` or `someExpr(true)`.
|
||||
## Use a backlash to follow with alphanumeric char: `int8`\s are great.
|
||||
```
|
||||
|
||||
|
||||
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``.
|
||||
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.
|
||||
##
|
||||
## .. 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.
|
||||
|
||||
.. code-block:: Nim
|
||||
|
||||
```Nim
|
||||
## This is the best module ever. It provides answers to everything!
|
||||
##
|
||||
## :Author: Steve McQueen
|
||||
## :Copyright: 1965
|
||||
##
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: Nim
|
||||
|
||||
```Nim
|
||||
proc example1*(x: int) =
|
||||
## Prints the value of `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.
|
||||
|
||||
.. code-block:: Nim
|
||||
|
||||
````Nim
|
||||
proc addThree*(x, y, z: int8): int =
|
||||
## Adds three `int8` values, treating them as unsigned and
|
||||
## truncating the result.
|
||||
##
|
||||
## .. code-block::
|
||||
## # things that aren't suitable for a `runnableExamples` go in code-block:
|
||||
## echo execCmdEx("git pull")
|
||||
## drawOnScreen()
|
||||
## ```
|
||||
## # things that aren't suitable for a `runnableExamples` go in code-block:
|
||||
## echo execCmdEx("git pull")
|
||||
## drawOnScreen()
|
||||
## ```
|
||||
runnableExamples:
|
||||
# `runnableExamples` is usually preferred to ``code-block``, when possible.
|
||||
doAssert addThree(3, 125, 6) == -122
|
||||
result = x +% y +% z
|
||||
````
|
||||
|
||||
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.
|
||||
|
||||
.. code-block:: Nim
|
||||
|
||||
```Nim
|
||||
type
|
||||
NamedQueue*[T] = object ## Provides a linked data structure with names
|
||||
## 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
|
||||
val*: T ## Its value
|
||||
next*: ref NamedQueue[T] ## The next item in the queue
|
||||
```
|
||||
|
||||
|
||||
You have some flexibility when placing the documentation:
|
||||
|
||||
.. code-block:: Nim
|
||||
|
||||
```Nim
|
||||
type
|
||||
NamedQueue*[T] = object
|
||||
## 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
|
||||
val*: T ## Its value
|
||||
next*: ref NamedQueue[T] ## The next item in the queue
|
||||
```
|
||||
|
||||
Make sure to place the documentation beside or within the object.
|
||||
|
||||
.. code-block:: Nim
|
||||
|
||||
```Nim
|
||||
type
|
||||
## Bad: this documentation disappears because it annotates the `type` keyword
|
||||
## above, not `NamedQueue`.
|
||||
@@ -123,14 +124,14 @@ Make sure to place the documentation beside or within the object.
|
||||
## is not what we want.
|
||||
val*: T ## Its value
|
||||
next*: ref NamedQueue[T] ## The next item in the queue
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
.. code-block:: Nim
|
||||
|
||||
```Nim
|
||||
const
|
||||
X* = 42 ## An awesome number.
|
||||
SpreadArray* = [
|
||||
@@ -138,25 +139,26 @@ When declaring module-wide constants and values, documentation is encouraged. Th
|
||||
[2,3,1],
|
||||
[3,1,2],
|
||||
] ## 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 (`#`).
|
||||
|
||||
.. code-block:: Nim
|
||||
|
||||
```Nim
|
||||
const
|
||||
BadMathVals* = [
|
||||
3.14, # pi
|
||||
2.72, # e
|
||||
0.58, # gamma
|
||||
] ## A bunch of badly rounded values.
|
||||
```
|
||||
|
||||
Nim supports Unicode in comments, so the above can be replaced with the following:
|
||||
|
||||
.. code-block:: Nim
|
||||
|
||||
```Nim
|
||||
const
|
||||
BadMathVals* = [
|
||||
3.14, # π
|
||||
2.72, # e
|
||||
0.58, # γ
|
||||
] ## 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
|
||||
via `staticBoundChecks: on`:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
{.push staticBoundChecks: on.}
|
||||
# <--- code section here ---->
|
||||
{.pop.}
|
||||
```
|
||||
|
||||
DrNim currently only tries to prove array indexing or subrange checks,
|
||||
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
|
||||
without additional annotations:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
{.push staticBoundChecks: on.}
|
||||
|
||||
proc sum(a: openArray[int]): int =
|
||||
@@ -64,6 +63,7 @@ without additional annotations:
|
||||
{.pop.}
|
||||
|
||||
echo sum([1, 2, 3])
|
||||
```
|
||||
|
||||
This program contains a famous "index out of bounds" bug. DrNim
|
||||
detects it and produces the following error message::
|
||||
@@ -125,8 +125,7 @@ Example: insertionSort
|
||||
|
||||
**Note**: This example does not yet work with DrNim.
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
import std / logic
|
||||
|
||||
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]).}
|
||||
swap a[t], a[t-1]
|
||||
dec t
|
||||
```
|
||||
|
||||
Unfortunately, the invariants required to prove that this code is correct take more
|
||||
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
|
||||
which doesn't sort at all:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
import std / logic
|
||||
|
||||
proc insertionSort(a: var openArray[int]) {.
|
||||
ensures: forall(i in 1..<a.len, a[i-1] <= a[i]).} =
|
||||
# does not sort, overwrites `a`'s contents!
|
||||
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
|
||||
and should be evaluated:
|
||||
|
||||
.. code-block:: nim
|
||||
```nim
|
||||
proc toUpper(s: string): string =
|
||||
result = newString(len(s))
|
||||
for i in 0..len(s) - 1:
|
||||
result[i] = toUpper(s[i])
|
||||
```
|
||||
|
||||
No, it really can always be evaluated. The code generator should transform
|
||||
``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`
|
||||
In your `main.nim`:
|
||||
|
||||
.. code-block:: nim
|
||||
```nim
|
||||
include "xmlGen.nimf"
|
||||
|
||||
echo generateXML("John Smith","42")
|
||||
```
|
||||
|
||||
Pipe operator
|
||||
=============
|
||||
@@ -150,7 +151,7 @@ Example::
|
||||
|
||||
The filter transforms this into:
|
||||
|
||||
.. code-block:: nim
|
||||
```nim
|
||||
proc generateHTMLPage(title, currentTab, content: string,
|
||||
tabs: openArray[string]): string =
|
||||
result = ""
|
||||
@@ -173,6 +174,7 @@ The filter transforms this into:
|
||||
" A dollar: $.\n" &
|
||||
" </div>\n" &
|
||||
"</body>\n")
|
||||
```
|
||||
|
||||
|
||||
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:.
|
||||
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
# logic.nim
|
||||
import sdl2
|
||||
|
||||
@@ -83,10 +82,10 @@ To install SDL2 you can use `nimble install sdl2`:cmd:.
|
||||
discard renderer.fillRect(rect)
|
||||
delay(16)
|
||||
renderer.present()
|
||||
```
|
||||
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
# mymain.nim
|
||||
import logic
|
||||
|
||||
@@ -97,42 +96,44 @@ To install SDL2 you can use `nimble install sdl2`:cmd:.
|
||||
destroy()
|
||||
|
||||
main()
|
||||
```
|
||||
|
||||
|
||||
Compile this example via:
|
||||
|
||||
```cmd
|
||||
```cmd
|
||||
nim c --hotcodereloading:on mymain.nim
|
||||
```
|
||||
```
|
||||
|
||||
Now start the program and KEEP it running!
|
||||
|
||||
.. code:: cmd
|
||||
```cmd
|
||||
# Unix:
|
||||
mymain &
|
||||
# or Windows (click on the .exe)
|
||||
mymain.exe
|
||||
# edit
|
||||
```
|
||||
|
||||
For example, change the line:
|
||||
|
||||
```nim
|
||||
```nim
|
||||
discard renderer.setDrawColor(255, 128, 128, 0)
|
||||
```
|
||||
```
|
||||
|
||||
into:
|
||||
|
||||
```nim
|
||||
```nim
|
||||
discard renderer.setDrawColor(255, 255, 128, 0)
|
||||
```
|
||||
```
|
||||
|
||||
(This will change the color of the rectangle.)
|
||||
|
||||
Then recompile the project, but do not restart or quit the mymain.exe program!
|
||||
|
||||
```cmd
|
||||
```cmd
|
||||
nim c --hotcodereloading:on mymain.nim
|
||||
```
|
||||
```
|
||||
|
||||
Now give the `mymain` SDL window the focus, press F9, and watch the
|
||||
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
|
||||
the execution of certain statements:
|
||||
|
||||
.. code-block:: Nim
|
||||
```Nim
|
||||
var
|
||||
settings = initTable[string, string]()
|
||||
lastReload: Time
|
||||
@@ -159,6 +160,7 @@ the execution of certain statements:
|
||||
afterCodeReload:
|
||||
lastReload = now()
|
||||
resetProgramState()
|
||||
```
|
||||
|
||||
On each code reload, Nim will first execute all `beforeCodeReload`:idx:
|
||||
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
|
||||
`hasModuleChanged()`:idx: API:
|
||||
|
||||
.. code-block:: Nim
|
||||
```Nim
|
||||
import mydb
|
||||
|
||||
var myCache = initTable[Key, Value]()
|
||||
@@ -175,6 +177,7 @@ executed. To prevent this behavior, one can guard the code with the
|
||||
afterCodeReload:
|
||||
if hasModuleChanged(mydb):
|
||||
resetCache(myCache)
|
||||
```
|
||||
|
||||
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
|
||||
@@ -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
|
||||
with choosenim follows.
|
||||
|
||||
.. code:: console
|
||||
|
||||
```console
|
||||
# Unix/MacOS
|
||||
# Make sure you are in the directory containing your .nim files
|
||||
$ cd your-source-directory
|
||||
@@ -215,6 +217,7 @@ with choosenim follows.
|
||||
|
||||
# verify that you have two files named libnimhcr and libnimrtl in your
|
||||
# source directory (.dll for Windows, .so for Unix, .dylib for MacOS)
|
||||
```
|
||||
|
||||
All modules of the project will be compiled to separate dynamic link
|
||||
libraries placed in the `nimcache` directory. Please note that during
|
||||
|
||||
194
doc/idetools.md
194
doc/idetools.md
@@ -10,10 +10,7 @@
|
||||
.. contents::
|
||||
|
||||
|
||||
.. raw:: html
|
||||
<blockquote><p>
|
||||
"yes, I'm the creator" -- Araq, 2013-07-26 19:28:32.
|
||||
</p></blockquote>
|
||||
> "yes, I'm the creator" -- Araq, 2013-07-26 19:28:32.
|
||||
|
||||
Note: this is mostly outdated, see instead `nimsuggest <nimsuggest.html>`_
|
||||
|
||||
@@ -246,11 +243,12 @@ skConst
|
||||
| **Fourth column**: the type of the const value.
|
||||
| **Docstring**: always the empty string.
|
||||
|
||||
.. code-block:: nim
|
||||
const SOME_SEQUENCE = @[1, 2]
|
||||
--> col 2: $MODULE.SOME_SEQUENCE
|
||||
col 3: seq[int]
|
||||
col 7: ""
|
||||
```nim
|
||||
const SOME_SEQUENCE = @[1, 2]
|
||||
--> col 2: $MODULE.SOME_SEQUENCE
|
||||
col 3: seq[int]
|
||||
col 7: ""
|
||||
```
|
||||
|
||||
|
||||
skEnumField
|
||||
@@ -260,11 +258,12 @@ skEnumField
|
||||
| **Fourth column**: enum type grouping other enum fields.
|
||||
| **Docstring**: always the empty string.
|
||||
|
||||
.. code-block:: nim
|
||||
Open(filename, fmWrite)
|
||||
--> col 2: system.FileMode.fmWrite
|
||||
col 3: FileMode
|
||||
col 7: ""
|
||||
```nim
|
||||
Open(filename, fmWrite)
|
||||
--> col 2: system.FileMode.fmWrite
|
||||
col 3: FileMode
|
||||
col 7: ""
|
||||
```
|
||||
|
||||
|
||||
skForVar
|
||||
@@ -274,13 +273,14 @@ skForVar
|
||||
| **Fourth column**: type of the var.
|
||||
| **Docstring**: always the empty string.
|
||||
|
||||
.. code-block:: nim
|
||||
proc looper(filename = "tests.nim") =
|
||||
for letter in filename:
|
||||
echo letter
|
||||
--> col 2: $MODULE.looper.letter
|
||||
col 3: char
|
||||
col 7: ""
|
||||
```nim
|
||||
proc looper(filename = "tests.nim") =
|
||||
for letter in filename:
|
||||
echo letter
|
||||
--> col 2: $MODULE.looper.letter
|
||||
col 3: char
|
||||
col 7: ""
|
||||
```
|
||||
|
||||
|
||||
skIterator, skClosureIterator
|
||||
@@ -295,13 +295,14 @@ posterior instances of the iterator.
|
||||
| **Fourth column**: signature of the iterator including return type.
|
||||
| **Docstring**: docstring if available.
|
||||
|
||||
.. code-block:: nim
|
||||
let
|
||||
text = "some text"
|
||||
letters = toSeq(runes(text))
|
||||
--> col 2: unicode.runes
|
||||
col 3: iterator (string): Rune
|
||||
col 7: "iterates over any unicode character of the string `s`."
|
||||
```nim
|
||||
let
|
||||
text = "some text"
|
||||
letters = toSeq(runes(text))
|
||||
--> col 2: unicode.runes
|
||||
col 3: iterator (string): Rune
|
||||
col 7: "iterates over any unicode character of the string `s`."
|
||||
```
|
||||
|
||||
|
||||
skLabel
|
||||
@@ -311,13 +312,14 @@ skLabel
|
||||
| **Fourth column**: always the empty string.
|
||||
| **Docstring**: always the empty string.
|
||||
|
||||
.. code-block:: nim
|
||||
proc test(text: string) =
|
||||
var found = -1
|
||||
block loops:
|
||||
--> col 2: $MODULE.test.loops
|
||||
col 3: ""
|
||||
col 7: ""
|
||||
```nim
|
||||
proc test(text: string) =
|
||||
var found = -1
|
||||
block loops:
|
||||
--> col 2: $MODULE.test.loops
|
||||
col 3: ""
|
||||
col 7: ""
|
||||
```
|
||||
|
||||
|
||||
skLet
|
||||
@@ -327,12 +329,13 @@ skLet
|
||||
| **Fourth column**: the type of the let variable.
|
||||
| **Docstring**: always the empty string.
|
||||
|
||||
.. code-block:: nim
|
||||
let
|
||||
text = "some text"
|
||||
--> col 2: $MODULE.text
|
||||
col 3: string
|
||||
col 7: ""
|
||||
```nim
|
||||
let
|
||||
text = "some text"
|
||||
--> col 2: $MODULE.text
|
||||
col 3: string
|
||||
col 7: ""
|
||||
```
|
||||
|
||||
|
||||
skMacro
|
||||
@@ -347,12 +350,13 @@ posterior instances of the macro.
|
||||
| **Fourth column**: signature of the macro including return type.
|
||||
| **Docstring**: docstring if available.
|
||||
|
||||
.. code-block:: nim
|
||||
proc testMacro() =
|
||||
expect(EArithmetic):
|
||||
--> col 2: idetools_api.expect
|
||||
col 3: proc (varargs[expr], stmt): stmt
|
||||
col 7: ""
|
||||
```nim
|
||||
proc testMacro() =
|
||||
expect(EArithmetic):
|
||||
--> col 2: idetools_api.expect
|
||||
col 3: proc (varargs[expr], stmt): stmt
|
||||
col 7: ""
|
||||
```
|
||||
|
||||
|
||||
skMethod
|
||||
@@ -384,14 +388,15 @@ This may change in the future.
|
||||
| **Fourth column**: signature of the method including return type.
|
||||
| **Docstring**: docstring if available.
|
||||
|
||||
.. code-block:: nim
|
||||
method eval(e: PExpr): int = quit "to override!"
|
||||
method eval(e: PLiteral): int = e.x
|
||||
method eval(e: PPlusExpr): int = eval(e.a) + eval(e.b)
|
||||
echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
|
||||
--> col 2: $MODULE.eval
|
||||
col 3: proc (PPlusExpr): int
|
||||
col 7: ""
|
||||
```nim
|
||||
method eval(e: PExpr): int = quit "to override!"
|
||||
method eval(e: PLiteral): int = e.x
|
||||
method eval(e: PPlusExpr): int = eval(e.a) + eval(e.b)
|
||||
echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
|
||||
--> col 2: $MODULE.eval
|
||||
col 3: proc (PPlusExpr): int
|
||||
col 7: ""
|
||||
```
|
||||
|
||||
|
||||
skParam
|
||||
@@ -401,12 +406,13 @@ skParam
|
||||
| **Fourth column**: the type of the parameter.
|
||||
| **Docstring**: always the empty string.
|
||||
|
||||
.. code-block:: nim
|
||||
proc reader(filename = "tests.nim") =
|
||||
let text = readFile(filename)
|
||||
--> col 2: $MODULE.reader.filename
|
||||
col 3: string
|
||||
col 7: ""
|
||||
```nim
|
||||
proc reader(filename = "tests.nim") =
|
||||
let text = readFile(filename)
|
||||
--> col 2: $MODULE.reader.filename
|
||||
col 3: string
|
||||
col 7: ""
|
||||
```
|
||||
|
||||
|
||||
skProc
|
||||
@@ -425,15 +431,16 @@ returned by idetools returns also the pragmas for the proc.
|
||||
| **Fourth column**: signature of the proc including return type.
|
||||
| **Docstring**: docstring if available.
|
||||
|
||||
.. code-block:: nim
|
||||
open(filename, fmWrite)
|
||||
--> col 2: system.Open
|
||||
col 3: proc (var File, string, FileMode, int): bool
|
||||
col 7:
|
||||
"Opens a file named `filename` with given `mode`.
|
||||
```nim
|
||||
open(filename, fmWrite)
|
||||
--> col 2: system.Open
|
||||
col 3: proc (var File, string, FileMode, int): bool
|
||||
col 7:
|
||||
"Opens a file named `filename` with given `mode`.
|
||||
|
||||
Default mode is readonly. Returns true iff the file could be opened.
|
||||
This throws no exception if the file could not 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."
|
||||
```
|
||||
|
||||
|
||||
skResult
|
||||
@@ -443,12 +450,13 @@ skResult
|
||||
| **Fourth column**: the type of the result.
|
||||
| **Docstring**: always the empty string.
|
||||
|
||||
.. code-block:: nim
|
||||
proc getRandomValue() : int =
|
||||
return 4
|
||||
--> col 2: $MODULE.getRandomValue.result
|
||||
col 3: int
|
||||
col 7: ""
|
||||
```nim
|
||||
proc getRandomValue() : int =
|
||||
return 4
|
||||
--> col 2: $MODULE.getRandomValue.result
|
||||
col 3: int
|
||||
col 7: ""
|
||||
```
|
||||
|
||||
|
||||
skTemplate
|
||||
@@ -463,7 +471,7 @@ posterior instances of the template.
|
||||
| **Fourth column**: signature of the template including return type.
|
||||
| **Docstring**: docstring if available.
|
||||
|
||||
.. code-block:: nim
|
||||
`````nim
|
||||
let
|
||||
text = "some text"
|
||||
letters = toSeq(runes(text))
|
||||
@@ -474,13 +482,15 @@ posterior instances of the template.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: nim
|
||||
```nim
|
||||
let
|
||||
numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
odd_numbers = toSeq(filter(numeric) do (x: int) -> bool:
|
||||
if x mod 2 == 1:
|
||||
result = true)
|
||||
assert odd_numbers == @[1, 3, 5, 7, 9]"
|
||||
```
|
||||
`````
|
||||
|
||||
|
||||
skType
|
||||
@@ -490,12 +500,13 @@ skType
|
||||
| **Fourth column**: the type.
|
||||
| **Docstring**: always the empty string.
|
||||
|
||||
.. code-block:: nim
|
||||
proc writeTempFile() =
|
||||
var output: File
|
||||
--> col 2: system.File
|
||||
col 3: File
|
||||
col 7: ""
|
||||
```nim
|
||||
proc writeTempFile() =
|
||||
var output: File
|
||||
--> col 2: system.File
|
||||
col 3: File
|
||||
col 7: ""
|
||||
```
|
||||
|
||||
|
||||
skVar
|
||||
@@ -505,14 +516,15 @@ skVar
|
||||
| **Fourth column**: the type of the var.
|
||||
| **Docstring**: always the empty string.
|
||||
|
||||
.. code-block:: nim
|
||||
proc writeTempFile() =
|
||||
var output: File
|
||||
output.open("/tmp/somefile", fmWrite)
|
||||
output.write("test")
|
||||
--> col 2: $MODULE.writeTempFile.output
|
||||
col 3: File
|
||||
col 7: ""
|
||||
```nim
|
||||
proc writeTempFile() =
|
||||
var output: File
|
||||
output.open("/tmp/somefile", fmWrite)
|
||||
output.write("test")
|
||||
--> col 2: $MODULE.writeTempFile.output
|
||||
col 3: File
|
||||
col 7: ""
|
||||
```
|
||||
|
||||
|
||||
Test suite
|
||||
|
||||
111
doc/intern.md
111
doc/intern.md
@@ -42,25 +42,25 @@ Bootstrapping the compiler
|
||||
|
||||
Compiling the compiler is a simple matter of running:
|
||||
|
||||
.. code:: cmd
|
||||
|
||||
```cmd
|
||||
nim c koch.nim
|
||||
koch boot -d:release
|
||||
```
|
||||
|
||||
For a debug version use:
|
||||
|
||||
.. code:: cmd
|
||||
|
||||
```cmd
|
||||
nim c koch.nim
|
||||
koch boot
|
||||
```
|
||||
|
||||
|
||||
And for a debug version compatible with GDB:
|
||||
|
||||
.. code:: cmd
|
||||
|
||||
```cmd
|
||||
nim c koch.nim
|
||||
koch boot --debuginfo --linedir:on
|
||||
```
|
||||
|
||||
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.
|
||||
@@ -73,10 +73,10 @@ Reproducible builds
|
||||
|
||||
Set the compilation timestamp with the `SOURCE_DATE_EPOCH` environment variable.
|
||||
|
||||
.. code:: cmd
|
||||
|
||||
```cmd
|
||||
export SOURCE_DATE_EPOCH=$(git log -n 1 --format=%at)
|
||||
koch boot # or `./build_all.sh`
|
||||
```
|
||||
|
||||
|
||||
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
|
||||
current commit:
|
||||
|
||||
.. code:: cmd
|
||||
|
||||
```cmd
|
||||
git bisect start bad-commit good-commit
|
||||
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 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:\:
|
||||
|
||||
.. code:: cmd
|
||||
|
||||
```cmd
|
||||
koch temp [options] c test.nim
|
||||
|
||||
# (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)
|
||||
koch temp [options] doc test.nim
|
||||
```
|
||||
|
||||
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
|
||||
the currently compiled main module:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
# inside some compiler module
|
||||
if mdbg:
|
||||
debug someAstNode
|
||||
```
|
||||
|
||||
Using the `isCompilerDebug`:nim: condition along with inserting some statements
|
||||
into the testcase provides more granular logging:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
# compilermodule.nim
|
||||
if isCompilerDebug():
|
||||
debug someAstNode
|
||||
@@ -186,21 +185,21 @@ into the testcase provides more granular logging:
|
||||
{.define(nimCompilerDebug).}
|
||||
let a = 2.5 * 3
|
||||
{.undef(nimCompilerDebug).}
|
||||
```
|
||||
|
||||
Logging can also be scoped to a specific filename as well. This will of course
|
||||
match against every module with that name.
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
if `??`(conf, n.info, "module.nim"):
|
||||
debug(n)
|
||||
```
|
||||
|
||||
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
|
||||
information about the internal compiler types include:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
# pretty print PNode
|
||||
|
||||
# pretty prints the Nim ast
|
||||
@@ -246,23 +245,24 @@ information about the internal compiler types include:
|
||||
|
||||
# print the structure of any type
|
||||
repr(someVar)
|
||||
```
|
||||
|
||||
Here are some other helpful utilities:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
# how did execution reach this location?
|
||||
writeStackTrace()
|
||||
```
|
||||
|
||||
These procs may not already be imported by the module you're editing.
|
||||
You can import them directly for debugging:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
from astalgo import debug
|
||||
from types import typeToString
|
||||
from renderer import renderTree
|
||||
from msgs import `??`
|
||||
```
|
||||
|
||||
Native debugging
|
||||
----------------
|
||||
@@ -280,12 +280,12 @@ and `exitingDebugSection()`:nim:.
|
||||
* LLDB execute `command source tools/compiler.lldb` at startup
|
||||
#. Use one of the scoping helpers like so:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
if isCompilerDebug():
|
||||
enteringDebugSection()
|
||||
else:
|
||||
exitingDebugSection()
|
||||
```
|
||||
|
||||
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.
|
||||
@@ -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
|
||||
could stem from a complex expression:
|
||||
|
||||
.. code-block:: nim
|
||||
```nim
|
||||
receivesClosure(returnsDefaultCC[i])
|
||||
```
|
||||
|
||||
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
|
||||
@@ -460,17 +461,18 @@ to pass a proc pointer around via a generic `ref` type.
|
||||
|
||||
Example code:
|
||||
|
||||
.. code-block:: nim
|
||||
```nim
|
||||
proc add(x: int): proc (y: int): int {.closure.} =
|
||||
return proc (y: int): int =
|
||||
return x + y
|
||||
|
||||
var add2 = add(2)
|
||||
echo add2(5) #OUT 7
|
||||
```
|
||||
|
||||
This should produce roughly this code:
|
||||
|
||||
.. code-block:: nim
|
||||
```nim
|
||||
type
|
||||
Env = ref object
|
||||
x: int # data
|
||||
@@ -487,11 +489,12 @@ This should produce roughly this code:
|
||||
var add2 = add(2)
|
||||
let tmp = if add2.data == nil: add2.prc(5) else: add2.prc(5, add2.data)
|
||||
echo tmp
|
||||
```
|
||||
|
||||
|
||||
Beware of nesting:
|
||||
|
||||
.. code-block:: nim
|
||||
```nim
|
||||
proc add(x: int): proc (y: int): proc (z: int): int {.closure.} {.closure.} =
|
||||
return lambda (y: int): proc (z: int): int {.closure.} =
|
||||
return lambda (z: int): int =
|
||||
@@ -499,10 +502,11 @@ Beware of nesting:
|
||||
|
||||
var add24 = add(2)(4)
|
||||
echo add24(5) #OUT 11
|
||||
```
|
||||
|
||||
This should produce roughly this code:
|
||||
|
||||
.. code-block:: nim
|
||||
```nim
|
||||
type
|
||||
EnvX = ref object
|
||||
x: int # data
|
||||
@@ -530,6 +534,7 @@ This should produce roughly this code:
|
||||
var tmp2 = tmp.fn(4, tmp.data)
|
||||
var add24 = tmp2.fn(4, tmp2.data)
|
||||
echo add24(5)
|
||||
```
|
||||
|
||||
|
||||
We could get rid of nesting environments by always inlining inner anon procs.
|
||||
@@ -540,8 +545,7 @@ however.
|
||||
Accumulator
|
||||
-----------
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
proc getAccumulator(start: int): proc (): int {.closure} =
|
||||
var i = start
|
||||
return lambda: int =
|
||||
@@ -560,6 +564,7 @@ Accumulator
|
||||
var a = accumulator(3)
|
||||
var b = accumulator(4)
|
||||
echo a() + b()
|
||||
```
|
||||
|
||||
|
||||
Internals
|
||||
@@ -614,36 +619,36 @@ keeps the full `int literal(321)` type. Here is an example where that
|
||||
difference matters.
|
||||
|
||||
|
||||
.. code-block:: nim
|
||||
```nim
|
||||
proc foo(arg: int8) =
|
||||
echo "def"
|
||||
|
||||
proc foo(arg: int8) =
|
||||
echo "def"
|
||||
const tmp1 = 123
|
||||
foo(tmp1) # OK
|
||||
|
||||
const tmp1 = 123
|
||||
foo(tmp1) # OK
|
||||
|
||||
let tmp2 = 123
|
||||
foo(tmp2) # Error
|
||||
let tmp2 = 123
|
||||
foo(tmp2) # Error
|
||||
```
|
||||
|
||||
In a context with multiple overloads, the integer literal kind will
|
||||
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
|
||||
ambiguity.
|
||||
|
||||
.. code-block:: nim
|
||||
```nim
|
||||
proc foo(arg: int) =
|
||||
echo "abc"
|
||||
proc foo(arg: int8) =
|
||||
echo "def"
|
||||
foo(123) # output: abc
|
||||
|
||||
proc foo(arg: int) =
|
||||
echo "abc"
|
||||
proc foo(arg: int8) =
|
||||
echo "def"
|
||||
foo(123) # output: abc
|
||||
proc bar(arg: int16) =
|
||||
echo "abc"
|
||||
proc bar(arg: int8) =
|
||||
echo "def"
|
||||
|
||||
proc bar(arg: int16) =
|
||||
echo "abc"
|
||||
proc bar(arg: int8) =
|
||||
echo "def"
|
||||
|
||||
bar(123) # Error ambiguous call
|
||||
bar(123) # Error ambiguous call
|
||||
```
|
||||
|
||||
In the compiler these integer literal types are represented with the
|
||||
node kind `nkIntLit`, type kind `tyInt` and the member `n` of the type
|
||||
|
||||
@@ -8,10 +8,7 @@
|
||||
.. include:: rstcommon.rst
|
||||
.. contents::
|
||||
|
||||
.. raw:: html
|
||||
<blockquote><p>
|
||||
"A great chef is an artist that I truly respect" -- Robert Stack.
|
||||
</p></blockquote>
|
||||
> "A great chef is an artist that I truly respect" -- Robert Stack.
|
||||
|
||||
|
||||
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
|
||||
the procedure does not return a value:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
proc nothing(x, y: void): void =
|
||||
echo "ha"
|
||||
|
||||
nothing() # writes "ha" to stdout
|
||||
```
|
||||
|
||||
The `void` type is particularly useful for generic code:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
proc callProc[T](p: proc (x: T), x: T) =
|
||||
when T is void:
|
||||
p()
|
||||
@@ -52,15 +51,16 @@ The `void` type is particularly useful for generic code:
|
||||
|
||||
callProc[int](intProc, 12)
|
||||
callProc[void](emptyProc)
|
||||
```
|
||||
|
||||
However, a `void` type cannot be inferred in generic code:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
callProc(emptyProc)
|
||||
# Error: type mismatch: got (proc ())
|
||||
# but expected one of:
|
||||
# callProc(p: proc (T), x: T)
|
||||
```
|
||||
|
||||
The `void` type is only valid for parameters and return types; other symbols
|
||||
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
|
||||
ambiguous, a static error will be produced.
|
||||
|
||||
.. code-block:: nim
|
||||
:test: "nim c $1"
|
||||
|
||||
```nim test = "nim c $1"
|
||||
{.experimental: "overloadableEnums".}
|
||||
|
||||
type
|
||||
@@ -124,6 +122,7 @@ ambiguous, a static error will be produced.
|
||||
of value2: echo "B"
|
||||
|
||||
p value2
|
||||
```
|
||||
|
||||
|
||||
Package level objects
|
||||
@@ -144,8 +143,7 @@ available.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
# module A (in an arbitrary package)
|
||||
type
|
||||
Pack.SomeObject = object # declare as incomplete object of package 'Pack'
|
||||
@@ -154,15 +152,16 @@ Example:
|
||||
|
||||
# Incomplete objects can be used as parameters:
|
||||
proc myproc(x: SomeObject) = discard
|
||||
```
|
||||
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
# module B (in package "Pack")
|
||||
type
|
||||
SomeObject* {.package.} = object # Use 'package' to complete the object
|
||||
s, t: string
|
||||
x, y: int
|
||||
```
|
||||
|
||||
This feature will likely be superseded in the future by support for
|
||||
recursive module dependencies.
|
||||
@@ -223,8 +222,7 @@ preface definitions inside a module.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
{.experimental: "codeReordering".}
|
||||
|
||||
proc foo(x: int) =
|
||||
@@ -234,14 +232,14 @@ Example:
|
||||
echo(x)
|
||||
|
||||
foo(10)
|
||||
```
|
||||
|
||||
Variables can also be reordered as well. Variables that are *initialized* (i.e.
|
||||
variables that have their declaration and assignment combined in a single
|
||||
statement) can have their entire initialization statement reordered. Be wary of
|
||||
what code is executed at the top level:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
{.experimental: "codeReordering".}
|
||||
|
||||
proc a() =
|
||||
@@ -250,6 +248,7 @@ what code is executed at the top level:
|
||||
var foo = 5
|
||||
|
||||
a() # outputs: "5"
|
||||
```
|
||||
|
||||
..
|
||||
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 is the same as it would be with code reordering disabled.
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
{.experimental: "codeReordering".}
|
||||
|
||||
proc x() =
|
||||
@@ -270,12 +268,12 @@ what code is executed at the top level:
|
||||
var foo = 4
|
||||
|
||||
x() # "false"
|
||||
```
|
||||
|
||||
It is important to note that reordering *only* works for symbols at top level
|
||||
scope. Therefore, the following will *fail to compile:*
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
{.experimental: "codeReordering".}
|
||||
|
||||
proc a() =
|
||||
@@ -284,6 +282,7 @@ scope. Therefore, the following will *fail to compile:*
|
||||
echo("Hello!")
|
||||
|
||||
a()
|
||||
```
|
||||
|
||||
This feature will likely be replaced with a better solution to remove
|
||||
the need for forward declarations.
|
||||
@@ -295,8 +294,7 @@ Automatic dereferencing
|
||||
Automatic dereferencing is performed for the first argument of a routine call.
|
||||
This feature has to be enabled via `{.experimental: "implicitDeref".}`:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
{.experimental: "implicitDeref".}
|
||||
|
||||
type
|
||||
@@ -309,6 +307,7 @@ This feature has to be enabled via `{.experimental: "implicitDeref".}`:
|
||||
let n = Node()
|
||||
echo n.depth
|
||||
# no need to write n[].depth
|
||||
```
|
||||
|
||||
|
||||
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
|
||||
an `untyped` parameter:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
a.b # becomes `.`(a, b)
|
||||
a.b(c, d) # becomes `.`(a, b, c, d)
|
||||
```
|
||||
|
||||
The matched dot operators can be symbols of any callable kind (procs,
|
||||
templates and macros), depending on the desired effect:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
template `.`(js: PJsonNode, field: untyped): JSON = js[astToStr(field)]
|
||||
|
||||
var js = parseJson("{ x: 1, y: 2}")
|
||||
echo js.x # outputs 1
|
||||
echo js.y # outputs 2
|
||||
```
|
||||
|
||||
The following dot operators are available:
|
||||
|
||||
@@ -366,9 +365,9 @@ operator `.=`
|
||||
-------------
|
||||
This operator will be matched against assignments to missing fields.
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
a.b = c # becomes `.=`(a, b, c)
|
||||
```
|
||||
|
||||
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
|
||||
to use this operator.
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
{.experimental: "callOperator".}
|
||||
|
||||
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 (a.b)(c) == `()`(a.b, c)
|
||||
```
|
||||
|
||||
|
||||
Extended macro pragmas
|
||||
@@ -412,9 +411,10 @@ can also be applied to type, variable and constant declarations.
|
||||
|
||||
For types:
|
||||
|
||||
.. code-block:: nim
|
||||
```nim
|
||||
type
|
||||
MyObject {.schema: "schema.protobuf".} = object
|
||||
```
|
||||
|
||||
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
|
||||
@@ -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,
|
||||
and macros can return any expression.
|
||||
|
||||
.. code-block:: nim
|
||||
```nim
|
||||
var
|
||||
a = ...
|
||||
b {.importc, foo, nodecl.} = ...
|
||||
c = ...
|
||||
```
|
||||
|
||||
Assuming `foo` is a macro or a template, this is roughly equivalent to:
|
||||
|
||||
.. code-block:: nim
|
||||
```nim
|
||||
var a = ...
|
||||
foo:
|
||||
var b {.importc, nodecl.} = ...
|
||||
var c = ...
|
||||
```
|
||||
|
||||
|
||||
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
|
||||
expressions that cannot conveniently be represented as runtime values.
|
||||
|
||||
.. code-block:: nim
|
||||
```nim
|
||||
type Foo = object
|
||||
bar: int
|
||||
|
||||
@@ -468,6 +470,7 @@ expressions that cannot conveniently be represented as runtime values.
|
||||
assert bar == 10
|
||||
bar = 15
|
||||
assert bar == 15
|
||||
```
|
||||
|
||||
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
|
||||
@@ -483,8 +486,7 @@ Not nil annotation
|
||||
All types for which `nil` is a valid value can be annotated with the
|
||||
`not nil` annotation to exclude `nil` as a valid value:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
{.experimental: "notnil".}
|
||||
|
||||
type
|
||||
@@ -500,6 +502,7 @@ All types for which `nil` is a valid value can be annotated with the
|
||||
# and also this:
|
||||
var x: PObject
|
||||
p(x)
|
||||
```
|
||||
|
||||
The compiler ensures that every code path initializes variables which contain
|
||||
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:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
{.experimental: "strictFuncs".}
|
||||
|
||||
type
|
||||
@@ -566,6 +568,7 @@ For example:
|
||||
m.data = "yeah" # the mutation is here
|
||||
# Error: 'mut' can have side effects
|
||||
# an object reachable from 'n' is potentially mutated
|
||||
```
|
||||
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
type
|
||||
View1 = openArray[byte]
|
||||
View2 = lent string
|
||||
View3 = Table[openArray[char], int]
|
||||
```
|
||||
|
||||
|
||||
Exceptions to this rule are types constructed via `ptr` or `proc`.
|
||||
For example, the following types are **not** view types:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
type
|
||||
NotView1 = proc (x: openArray[int])
|
||||
NotView2 = ptr openArray[char]
|
||||
NotView3 = ptr array[4, lent int]
|
||||
```
|
||||
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
{.experimental: "views".}
|
||||
|
||||
proc take(a: openArray[int]) =
|
||||
@@ -641,6 +643,7 @@ For example:
|
||||
|
||||
|
||||
main(@[11, 22, 33])
|
||||
```
|
||||
|
||||
|
||||
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
|
||||
location. The borrowed location is said to be *sealed* during the borrow.
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
{.experimental: "views".}
|
||||
|
||||
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'
|
||||
s.setLen 0 # prevented at compile-time because 's' is sealed.
|
||||
echo v.field
|
||||
```
|
||||
|
||||
|
||||
The scope of the view does not matter:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
proc valid(s: var seq[Obj]) =
|
||||
let v: lent Obj = s[0] # begin of borrow
|
||||
echo v.field # end of borrow
|
||||
s.setLen 0 # valid because 'v' isn't used afterwards
|
||||
```
|
||||
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
proc invalid(s: var seq[Obj]) =
|
||||
let v: lent Obj = s[0]
|
||||
if false:
|
||||
s.setLen 0
|
||||
echo v.field
|
||||
```
|
||||
|
||||
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
|
||||
@@ -824,8 +827,7 @@ arbitrary set of requirements that the matched type must satisfy.
|
||||
|
||||
Concepts are written in the following form:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
type
|
||||
Comparable = concept x, y
|
||||
(x < y) is bool
|
||||
@@ -838,6 +840,7 @@ Concepts are written in the following form:
|
||||
|
||||
for value in s:
|
||||
value is T
|
||||
```
|
||||
|
||||
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
|
||||
the type itself:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
type
|
||||
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
|
||||
and parameters are expected. This provides a more convenient way to check for
|
||||
the presence of callable symbols with specific signatures:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
type
|
||||
OutputStream = concept var s
|
||||
s.write(string)
|
||||
```
|
||||
|
||||
In order to check for symbols accepting `type` params, you must prefix
|
||||
the type with the explicit `type` modifier. The named instance of the
|
||||
type, following the `concept` keyword is also considered to have the
|
||||
explicit modifier and will be matched only as a type.
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
type
|
||||
# Let's imagine a user-defined casting framework with operators
|
||||
# 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 T
|
||||
x - y is T
|
||||
```
|
||||
|
||||
Please note that the `is` operator allows one to easily verify the precise
|
||||
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
|
||||
`explain` pragma to either the concept body or a particular call-site.
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
type
|
||||
MyConcept {.explain.} = concept ...
|
||||
|
||||
overloadedProc(x, y, z) {.explain.}
|
||||
```
|
||||
|
||||
This will provide Hints in the compiler output either every time the concept is
|
||||
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:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
### matrixalgo.nim
|
||||
|
||||
import std/typetraits
|
||||
@@ -986,6 +988,7 @@ The concept types can be parametric just like the regular generic types:
|
||||
|
||||
echo m.transposed.determinant
|
||||
setPerspectiveProjection projectionMatrix
|
||||
```
|
||||
|
||||
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
|
||||
@@ -999,11 +1002,11 @@ and `x.data is seq[T]`.
|
||||
Unbound static params will be inferred from expressions involving the `==`
|
||||
operator and also when types dependent on them are being matched:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
type
|
||||
MatrixReducer[M, N: static int; T] = concept x
|
||||
x.reduce(SquareMatrix[N, T]) is array[M, int]
|
||||
```
|
||||
|
||||
The Nim compiler includes a simple linear equation solver, allowing it to
|
||||
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
|
||||
to match several procs accepting the same wide class of types:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
type
|
||||
Enumerable[T] = concept 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
|
||||
type Enum = distinct Enumerable
|
||||
o.baz is Enum
|
||||
```
|
||||
|
||||
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, thus allowing you to encode implementation-defined types:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
type
|
||||
MyConcept = concept x
|
||||
type T1 = auto
|
||||
@@ -1049,6 +1051,7 @@ types, thus allowing you to encode implementation-defined types:
|
||||
x.alpha(T2)
|
||||
x.omega(T2) # both procs must accept the same type
|
||||
# and it must be a numeric sequence
|
||||
```
|
||||
|
||||
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,
|
||||
@@ -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]`
|
||||
type is an instance of it:
|
||||
|
||||
.. code-block:: nim
|
||||
:test: "nim c $1"
|
||||
|
||||
```nim test = "nim c $1"
|
||||
import std/[sugar, typetraits]
|
||||
|
||||
type
|
||||
@@ -1089,6 +1090,7 @@ type is an instance of it:
|
||||
|
||||
import std/options
|
||||
echo Option[int] is Functor # prints true
|
||||
```
|
||||
|
||||
|
||||
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
|
||||
matched to a concrete type:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
type
|
||||
DateTime = concept t1, t2, type T
|
||||
const Min = T.MinDate
|
||||
@@ -1121,6 +1122,7 @@ matched to a concrete type:
|
||||
|
||||
deviation: float
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
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
|
||||
object inheritance syntax involving the `of` keyword:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
type
|
||||
Graph = concept g, type G of EquallyComparable, Copyable
|
||||
type
|
||||
@@ -1168,6 +1169,7 @@ object inheritance syntax involving the `of` keyword:
|
||||
proc f(g: IncidendeGraph)
|
||||
proc f(g: BidirectionalGraph) # this one will be preferred if we pass a type
|
||||
# matching the BidirectionalGraph concept
|
||||
```
|
||||
|
||||
..
|
||||
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
|
||||
the concept body:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
type
|
||||
Stringable = concept x
|
||||
$x is string
|
||||
@@ -1202,6 +1203,7 @@ object inheritance syntax involving the `of` keyword:
|
||||
# the same call at the cost of additional instantiations
|
||||
# the varargs param will be converted to a tuple
|
||||
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
|
||||
types to the corresponding VTable type.
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
type
|
||||
IntEnumerable = vtref Enumerable[int]
|
||||
|
||||
@@ -1243,6 +1244,7 @@ object inheritance syntax involving the `of` keyword:
|
||||
|
||||
proc addStream(o: var MyObject, e: OutputStream.vtref) =
|
||||
o.streams.add e
|
||||
```
|
||||
|
||||
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
|
||||
@@ -1272,9 +1274,9 @@ object inheritance syntax involving the `of` keyword:
|
||||
|
||||
The signature has to be:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
proc `=deepCopy`(x: T): T
|
||||
```
|
||||
|
||||
This mechanism will be used by most data structures that support shared memory,
|
||||
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`
|
||||
to be computed dynamically.
|
||||
|
||||
.. code-block:: nim
|
||||
```nim
|
||||
{.experimental: "dynamicBindSym".}
|
||||
|
||||
import macros
|
||||
@@ -1299,6 +1301,7 @@ to be computed dynamically.
|
||||
|
||||
echo callOp("+", 1, 2)
|
||||
echo callOp("-", 5, 4)
|
||||
```
|
||||
|
||||
|
||||
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
|
||||
compilation pipeline with user defined optimizations:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
template optMul{`*`(a, 2)}(a: int): int = a + a
|
||||
|
||||
let x = 3
|
||||
echo x * 2
|
||||
```
|
||||
|
||||
The compiler now rewrites `x * 2` as `x + x`. The code inside the
|
||||
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
|
||||
is **wrong**:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
template optMul{`*`(a, 2)}(a: int): int = a + a
|
||||
|
||||
proc f(): int =
|
||||
@@ -1341,12 +1343,12 @@ is **wrong**:
|
||||
result = 55
|
||||
|
||||
echo f() * 2
|
||||
```
|
||||
|
||||
We cannot duplicate 'a' if it denotes an expression that has a side effect!
|
||||
Fortunately Nim supports side effect analysis:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
template optMul{`*`(a, 2)}(a: int{noSideEffect}): int = a + a
|
||||
|
||||
proc f(): int =
|
||||
@@ -1354,6 +1356,7 @@ Fortunately Nim supports side effect analysis:
|
||||
result = 55
|
||||
|
||||
echo f() * 2 # not optimized ;-)
|
||||
```
|
||||
|
||||
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
|
||||
@@ -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
|
||||
blindly:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
template mulIsCommutative{`*`(a, b)}(a, b: int): int = b * a
|
||||
```
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
The `int{lit}` parameter pattern matches against an expression of
|
||||
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
|
||||
the ordinary AST predicates:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
template ex{a = b + c}(a: int{noalias}, b, c: int) =
|
||||
# this transformation is only valid if 'b' and 'c' do not alias 'a':
|
||||
a = b
|
||||
inc a, c
|
||||
```
|
||||
|
||||
Another example:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
proc somefunc(s: string) = assert s == "variable"
|
||||
proc somefunc(s: string{nkStrLit}) = assert s == "literal"
|
||||
proc somefunc(s: string{nkRStrLit}) = assert s == r"raw"
|
||||
@@ -1454,6 +1456,7 @@ Another example:
|
||||
somefunc("literal")
|
||||
somefunc(r"raw")
|
||||
somefunc("""triple""")
|
||||
```
|
||||
|
||||
|
||||
Pattern operators
|
||||
@@ -1467,21 +1470,21 @@ if they are written in infix notation.
|
||||
|
||||
The `|` operator if used as infix operator creates an ordered choice:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
template t{0|1}(): untyped = 3
|
||||
let a = 1
|
||||
# outputs 3:
|
||||
echo a
|
||||
```
|
||||
|
||||
The matching is performed after the compiler performed some optimizations like
|
||||
constant folding, so the following does not work:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
template t{0|1}(): untyped = 3
|
||||
# outputs 1:
|
||||
echo 1
|
||||
```
|
||||
|
||||
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
|
||||
@@ -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}`
|
||||
notation:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
template t{(0|1|2){x}}(x: untyped): untyped = x + 1
|
||||
let a = 1
|
||||
# outputs 2:
|
||||
echo a
|
||||
```
|
||||
|
||||
|
||||
### The `~` operator
|
||||
|
||||
The `~` operator is the 'not' operator in patterns:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
template t{x = (~x){y} and (~x){z}}(x, y, z: bool) =
|
||||
x = y
|
||||
if x: x = z
|
||||
@@ -1518,6 +1520,7 @@ The `~` operator is the 'not' operator in patterns:
|
||||
c = false
|
||||
a = b and c
|
||||
echo a
|
||||
```
|
||||
|
||||
|
||||
### 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`
|
||||
to `&(a, b, c)`:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
var
|
||||
calls = 0
|
||||
|
||||
@@ -1542,6 +1544,7 @@ to `&(a, b, c)`:
|
||||
|
||||
# check that it's been optimized properly:
|
||||
doAssert calls == 1
|
||||
```
|
||||
|
||||
|
||||
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`
|
||||
produces:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
`&&`("my", space & "awe", "some ", "concat")
|
||||
```nim
|
||||
`&&`("my", space & "awe", "some ", "concat")
|
||||
```
|
||||
|
||||
|
||||
### The `**` operator
|
||||
@@ -1560,8 +1563,7 @@ produces:
|
||||
The `**` is much like the `*` operator, except that it gathers not only
|
||||
all the arguments, but also the matched operators in reverse polish notation:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
import std/macros
|
||||
|
||||
type
|
||||
@@ -1582,6 +1584,7 @@ all the arguments, but also the matched operators in reverse polish notation:
|
||||
var x, y, z: Matrix
|
||||
|
||||
echo x + y * z - x
|
||||
```
|
||||
|
||||
This passes the expression `x + y * z - x` to the `optM` macro as
|
||||
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
|
||||
0 or more arguments in the AST to be matched against:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
template optWrite{
|
||||
write(f, x)
|
||||
((write|writeLine){w})(f, y)
|
||||
}(x, y: varargs[untyped], f: File, w: untyped) =
|
||||
w(f, x, y)
|
||||
```
|
||||
|
||||
|
||||
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,
|
||||
e.g. with given example `echo("ab")` will be rewritten just once:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
template pwnEcho{echo(x)}(x: untyped) =
|
||||
{.noRewrite.}: echo("pwned!")
|
||||
|
||||
echo "ab"
|
||||
```
|
||||
|
||||
`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
|
||||
implemented with term rewriting:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
proc p(x, y: int; cond: bool): int =
|
||||
result = if cond: x + y else: 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
|
||||
```
|
||||
|
||||
|
||||
Example: Hoisting
|
||||
@@ -1656,8 +1659,7 @@ Example: Hoisting
|
||||
|
||||
The following example shows how some form of hoisting can be implemented:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
import std/pegs
|
||||
|
||||
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:
|
||||
echo match("(a b c)", peg"'(' @ ')'")
|
||||
echo match("W_HI_Le", peg"\y 'while'")
|
||||
```
|
||||
|
||||
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
|
||||
@@ -1680,8 +1683,7 @@ AST based overloading
|
||||
Parameter constraints can also be used for ordinary routine parameters; these
|
||||
constraints then affect ordinary overloading resolution:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
proc optLit(a: string{lit|`const`}) =
|
||||
echo "string literal"
|
||||
proc optLit(a: string) =
|
||||
@@ -1696,6 +1698,7 @@ constraints then affect ordinary overloading resolution:
|
||||
optLit("literal")
|
||||
optLit(constant)
|
||||
optLit(variable)
|
||||
```
|
||||
|
||||
However, the constraints `alias` and `noalias` are not available in
|
||||
ordinary routines.
|
||||
@@ -1735,8 +1738,7 @@ Spawn statement
|
||||
|
||||
The `spawn`:idx: statement can be used to pass a task to the thread pool:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
import std/threadpool
|
||||
|
||||
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"):
|
||||
spawn processLine(x)
|
||||
sync()
|
||||
```
|
||||
|
||||
For reasons of type safety and implementation simplicity the expression
|
||||
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
|
||||
wait on multiple flow variables at the same time:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
import std/threadpool, ...
|
||||
|
||||
# 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
|
||||
responses.del(index)
|
||||
discard blockUntilAny(responses)
|
||||
```
|
||||
|
||||
Data flow variables ensure that no data races are possible. Due to
|
||||
technical limitations, not every type `T` can be used in
|
||||
@@ -1795,9 +1798,7 @@ Parallel statement
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: nim
|
||||
:test: "nim c --threads:on $1"
|
||||
|
||||
```nim test = "nim c --threads:on $1"
|
||||
# Compute pi in an inefficient way
|
||||
import std/[strutils, math, threadpool]
|
||||
{.experimental: "parallel".}
|
||||
@@ -1813,6 +1814,7 @@ Example:
|
||||
result += ch[k]
|
||||
|
||||
echo formatFloat(pi(5000))
|
||||
```
|
||||
|
||||
|
||||
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
|
||||
single `locks` section:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
var a, b: TLock[2]
|
||||
var x: TLock[1]
|
||||
# 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:
|
||||
{.locks: [a, b].}:
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
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`
|
||||
and `b` of the same lock level:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
template multilock(a, b: ptr TLock; body: untyped) =
|
||||
if cast[ByteAddress](a) < cast[ByteAddress](b):
|
||||
pthread_mutex_lock(a)
|
||||
@@ -1895,20 +1896,21 @@ and `b` of the same lock level:
|
||||
finally:
|
||||
pthread_mutex_unlock(a)
|
||||
pthread_mutex_unlock(b)
|
||||
```
|
||||
|
||||
|
||||
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.
|
||||
This is essential so that procs can be called within a `locks` section:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
proc p() {.locks: 3.} = discard
|
||||
|
||||
var a: TLock[4]
|
||||
{.locks: [a].}:
|
||||
# p's locklevel (3) is strictly less than a's (4) so the call is allowed:
|
||||
p()
|
||||
```
|
||||
|
||||
|
||||
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
|
||||
having unknown lock level as well:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
```nim
|
||||
type SomeBase* = ref object of RootObj
|
||||
type SomeDerived* = ref object of SomeBase
|
||||
memberProc*: proc ()
|
||||
@@ -1934,5 +1935,6 @@ having unknown lock level as well:
|
||||
method testMethod(g: SomeDerived) =
|
||||
if g.memberProc != nil:
|
||||
g.memberProc()
|
||||
```
|
||||
|
||||
This feature may be removed in the future due to its practical difficulties.
|
||||
|
||||
Reference in New Issue
Block a user