mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-31 19:03:42 +00:00
Compare commits
156 Commits
pr_error_m
...
v0.19.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7beea1642d | ||
|
|
a23b5c9265 | ||
|
|
3b2e151d3e | ||
|
|
0b9d158d11 | ||
|
|
123c568236 | ||
|
|
98bf23fe08 | ||
|
|
d02bf88f21 | ||
|
|
90b4648a99 | ||
|
|
a29ab5e65f | ||
|
|
c16fa63f5e | ||
|
|
28946ed9f6 | ||
|
|
f0a78b8ef1 | ||
|
|
c0e741778f | ||
|
|
c3319fcad5 | ||
|
|
7572d40b23 | ||
|
|
fa01531b8c | ||
|
|
e091c99514 | ||
|
|
a8b20e8129 | ||
|
|
b53d726c57 | ||
|
|
ff66aae380 | ||
|
|
219942658b | ||
|
|
7dfb2a8cd3 | ||
|
|
9fcdc14527 | ||
|
|
ad554a2013 | ||
|
|
43b3582952 | ||
|
|
64a4c72392 | ||
|
|
fa3017f6bb | ||
|
|
172e95be50 | ||
|
|
7d88738068 | ||
|
|
5f7679cb50 | ||
|
|
4cece52524 | ||
|
|
3ef686011d | ||
|
|
f0e196e674 | ||
|
|
c514fd5b27 | ||
|
|
1355dd1884 | ||
|
|
fcbe611899 | ||
|
|
b0217beadf | ||
|
|
d37ec666d7 | ||
|
|
da1569afb6 | ||
|
|
abf02b5f28 | ||
|
|
e68775f94d | ||
|
|
98103b5433 | ||
|
|
20c6ec0a22 | ||
|
|
be6f0108f3 | ||
|
|
ae26351a0e | ||
|
|
899ce95420 | ||
|
|
e214241bd3 | ||
|
|
257d62efc9 | ||
|
|
ea2309a99f | ||
|
|
8172ac8c0b | ||
|
|
78f9a55287 | ||
|
|
758c20dc84 | ||
|
|
1a9b954cd9 | ||
|
|
801c102881 | ||
|
|
5d3756ed3f | ||
|
|
939ffc3615 | ||
|
|
20b6c47d3f | ||
|
|
c756983795 | ||
|
|
c8786ad197 | ||
|
|
aa1def75c0 | ||
|
|
2f48d2175b | ||
|
|
71d9a1f289 | ||
|
|
2333645fbd | ||
|
|
171721bf49 | ||
|
|
45655332e8 | ||
|
|
961d8bdf2f | ||
|
|
fb9aa0c821 | ||
|
|
c53046223f | ||
|
|
6977a743f3 | ||
|
|
6844d679fc | ||
|
|
4b06f19b71 | ||
|
|
f8c341e31f | ||
|
|
d6124ccbad | ||
|
|
522896f488 | ||
|
|
427aa63a66 | ||
|
|
01b36cce1e | ||
|
|
db68353848 | ||
|
|
8196fd6f73 | ||
|
|
5f1280194e | ||
|
|
1b2c8317e0 | ||
|
|
80a9ac1400 | ||
|
|
fdce2e0188 | ||
|
|
5e48d4050e | ||
|
|
a482e5735d | ||
|
|
b864c89479 | ||
|
|
8f5a127e5c | ||
|
|
614059d6de | ||
|
|
6a6b77190c | ||
|
|
38b391ff62 | ||
|
|
42056874d1 | ||
|
|
4424fe0acc | ||
|
|
6515810a2e | ||
|
|
c1a6ebf40d | ||
|
|
8f8f3ba12f | ||
|
|
58726f9b81 | ||
|
|
8a356fafb7 | ||
|
|
624d05c73f | ||
|
|
b65b360ac2 | ||
|
|
0d5355b5fd | ||
|
|
8637ca6983 | ||
|
|
a65ac85e4a | ||
|
|
13a8765afc | ||
|
|
64f5c96685 | ||
|
|
aac04585ea | ||
|
|
7cf698442a | ||
|
|
b70b633e8e | ||
|
|
3ed61da6c1 | ||
|
|
25ce980fe1 | ||
|
|
7e1d411279 | ||
|
|
90d18b75c2 | ||
|
|
2e2a2754a4 | ||
|
|
6ad4022b5e | ||
|
|
e0eda28a99 | ||
|
|
06b1d17134 | ||
|
|
754e11de3e | ||
|
|
3340e46310 | ||
|
|
4f432688a3 | ||
|
|
ee18699609 | ||
|
|
ae80f763b3 | ||
|
|
48d3b26c21 | ||
|
|
2818661ffe | ||
|
|
325ca8cae6 | ||
|
|
ed4a7b7f66 | ||
|
|
5b3b091cd1 | ||
|
|
64da338a8b | ||
|
|
e718dc75c5 | ||
|
|
495585bc1e | ||
|
|
7de3bcfb8e | ||
|
|
7cf3944c38 | ||
|
|
44f5c7e90f | ||
|
|
faa9e7a582 | ||
|
|
596524d2ec | ||
|
|
fde803745e | ||
|
|
8a9680e3a2 | ||
|
|
291d7499a4 | ||
|
|
b5bb48dbd2 | ||
|
|
73a7d1456c | ||
|
|
65c2f19cff | ||
|
|
a5aad7ff59 | ||
|
|
0714de4f3b | ||
|
|
41e2f6b7bc | ||
|
|
46f0c7c554 | ||
|
|
b0618ffe17 | ||
|
|
d1114d0995 | ||
|
|
8120d8bede | ||
|
|
7ff17aff5f | ||
|
|
f2a44aa8cf | ||
|
|
1e1a43dd23 | ||
|
|
0f080fdce3 | ||
|
|
221c67b880 | ||
|
|
2923eb08ab | ||
|
|
ddd54a3413 | ||
|
|
ffeb3c6cec | ||
|
|
82896abcbe | ||
|
|
4e9686d1a9 | ||
|
|
d29fc8c693 |
45
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
45
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: You found an unexpected behaviour? Use this template.
|
||||
|
||||
---
|
||||
|
||||
<!-- Think about the title, twice. -->
|
||||
|
||||
<!-- Summarize the Problem here, keep it simple. -->
|
||||
<!-- e.g. `echo` outputs the wrong string. -->
|
||||
|
||||
|
||||
### Example
|
||||
<!-- This should be a source code block.
|
||||
```nim
|
||||
echo "Hello World!"
|
||||
```
|
||||
-->
|
||||
|
||||
|
||||
### Current Output
|
||||
<!--
|
||||
```
|
||||
Hola mundo!
|
||||
```
|
||||
-->
|
||||
|
||||
|
||||
### Expected Output
|
||||
<!--
|
||||
```
|
||||
Hello World!
|
||||
```
|
||||
-->
|
||||
|
||||
|
||||
### Possible Solution
|
||||
<!--- Have you found a possible solution? Post it here. -->
|
||||
|
||||
|
||||
### Additional Information
|
||||
<!--- For Example:
|
||||
A link to a project where the issue is relevant.
|
||||
A link to a related issue or discussion.
|
||||
-->
|
||||
23
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
23
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: You want to suggest a new feature? Use this template.
|
||||
|
||||
---
|
||||
|
||||
### Summary
|
||||
<!--- Short summary of your proposed feature -->
|
||||
|
||||
|
||||
### Description
|
||||
<!--- Describe your solution, what problem does it fix? -->
|
||||
|
||||
|
||||
### Alternatives
|
||||
<!--- Are there any alternatives you've considered? -->
|
||||
|
||||
|
||||
### Additional Information
|
||||
<!--- For Example:
|
||||
A link to a project where the issue is relevant.
|
||||
A link to a related issue or discussion.
|
||||
-->
|
||||
@@ -53,8 +53,8 @@ test-windows:
|
||||
<<: *win_set_path_def
|
||||
script:
|
||||
- call ci\deps.bat
|
||||
- nim c --taintMode:on tests\testament\tester
|
||||
- tests\testament\tester.exe --pedantic all
|
||||
- nim c --taintMode:on testament\tester
|
||||
- testament\tester.exe --pedantic all
|
||||
tags:
|
||||
- windows
|
||||
- fast
|
||||
|
||||
@@ -42,11 +42,11 @@ script:
|
||||
#- nimble install sdl1
|
||||
#- nimble install jester@#head -y
|
||||
#- nimble install niminst
|
||||
- nim c --taintMode:on -d:nimCoroutines tests/testament/tester
|
||||
- tests/testament/tester --pedantic all -d:nimCoroutines
|
||||
- nim c --taintMode:on -d:nimCoroutines testament/tester
|
||||
- testament/tester --pedantic all -d:nimCoroutines
|
||||
- nim c -o:bin/nimpretty nimpretty/nimpretty.nim
|
||||
- nim c -r nimpretty/tester.nim
|
||||
- ./koch docs
|
||||
- ./koch docs --git.commit:devel
|
||||
- ./koch csource
|
||||
- ./koch nimsuggest
|
||||
- nim c -r nimsuggest/tester
|
||||
|
||||
@@ -53,11 +53,11 @@ build_script:
|
||||
# - nimble install opengl -y
|
||||
# - nimble install sdl1 -y
|
||||
# - nimble install jester@#head -y
|
||||
- nim c --taintMode:on -d:nimCoroutines --os:genode -d:posix --compileOnly tests/testament/tester
|
||||
- nim c --taintMode:on -d:nimCoroutines tests/testament/tester
|
||||
- nim c --taintMode:on -d:nimCoroutines --os:genode -d:posix --compileOnly testament/tester
|
||||
- nim c --taintMode:on -d:nimCoroutines testament/tester
|
||||
|
||||
test_script:
|
||||
- tests\testament\tester --pedantic all -d:nimCoroutines
|
||||
- testament\tester --pedantic all -d:nimCoroutines
|
||||
- nim c -r nimdoc\tester
|
||||
# - koch csource
|
||||
# - koch zip
|
||||
|
||||
255
changelog.md
255
changelog.md
@@ -1,253 +1,12 @@
|
||||
## v0.19.X - XX/XX/2018
|
||||
## v0.19.2 - 2018-11-04
|
||||
|
||||
### Changes affecting backwards compatibility
|
||||
This is a bugfix release of version 0.19.0. No new features or breaking changes.
|
||||
The most important bugfixes are:
|
||||
|
||||
- The stdlib module ``future`` has been renamed to ``sugar``.
|
||||
- ``macros.callsite`` is now deprecated. Since the introduction of ``varargs``
|
||||
parameters this became unnecessary.
|
||||
- Anonymous tuples with a single element can now be written as ``(1,)`` with a
|
||||
trailing comma. The underlying AST is ``nnkTupleConstr(newLit 1)`` for this
|
||||
example. ``nnkTupleConstr`` is a new node kind your macros need to be able
|
||||
to deal with!
|
||||
- Indexing into a ``cstring`` for the JS target is now mapped
|
||||
to ``charCodeAt``.
|
||||
- Assignments that would "slice" an object into its supertype are now prevented
|
||||
at runtime. Use ``ref object`` with inheritance rather than ``object`` with
|
||||
inheritance to prevent this issue.
|
||||
- The ``not nil`` type annotation now has to be enabled explicitly
|
||||
via ``{.experimental: "notnil"}`` as we are still not pleased with how this
|
||||
feature works with Nim's containers.
|
||||
- The parser now warns about inconsistent spacing around binary operators as
|
||||
these can easily be confused with unary operators. This warning will likely
|
||||
become an error in the future.
|
||||
- The ``'c`` and ``'C'`` suffix for octal literals is now deprecated to
|
||||
bring the language in line with the standard library (e.g. ``parseOct``).
|
||||
- The dot style for import paths (e.g ``import path.to.module`` instead of
|
||||
``import path/to/module``) has been deprecated.
|
||||
- ``spawn`` can handle the empty seqs/strings that are internally
|
||||
represented as ``nil``.
|
||||
- The most pressing bugs of the documentation generator have been fixed.
|
||||
- async streaming of the httpclient has been fixed.
|
||||
|
||||
#### Breaking changes in the standard library
|
||||
|
||||
- ``re.split`` for empty regular expressions now yields every character in
|
||||
the string which is what other programming languages chose to do.
|
||||
- The returned tuple of ``system.instantiationInfo`` now has a third field
|
||||
containing the column of the instantiation.
|
||||
|
||||
- ``cookies.setCookie`` no longer assumes UTC for the expiration date.
|
||||
- ``strutils.formatEng`` does not distinguish between ``nil`` and ``""``
|
||||
strings anymore for its ``unit`` parameter. Instead the space is controlled
|
||||
by a new parameter ``useUnitSpace``.
|
||||
|
||||
- The ``times.parse`` and ``times.format`` procs have been rewritten.
|
||||
The proc signatures are the same so it should generally not break anything.
|
||||
However, the new implementation is a bit stricter, which is a breaking change.
|
||||
For example ``parse("2017-01-01 foo", "yyyy-MM-dd")`` will now raise an error.
|
||||
|
||||
- ``proc `-`*(a, b: Time): int64`` in the ``times`` module has changed return type
|
||||
to ``times.Duration`` in order to support higher time resolutions.
|
||||
The proc is no longer deprecated.
|
||||
|
||||
- The ``times.Timezone`` is now an immutable ref-type that must be initialized
|
||||
with an explicit constructor (``newTimezone``).
|
||||
|
||||
- ``posix.Timeval.tv_sec`` has changed type to ``posix.Time``.
|
||||
|
||||
- ``math.`mod` `` for floats now behaves the same as ``mod`` for integers
|
||||
(previously it used floor division like Python). Use ``math.floorMod`` for the old behavior.
|
||||
|
||||
- For string inputs, ``unicode.isUpper`` and ``unicode.isLower`` now require a
|
||||
second mandatory parameter ``skipNonAlpha``.
|
||||
|
||||
- For string inputs, ``strutils.isUpperAscii`` and ``strutils.isLowerAscii`` now
|
||||
require a second mandatory parameter ``skipNonAlpha``.
|
||||
|
||||
- ``osLastError`` is now marked with ``sideEffect``
|
||||
- The procs ``parseHexInt`` and ``parseOctInt`` now fail on empty strings
|
||||
and strings containing only valid prefixes, e.g. "0x" for hex integers.
|
||||
|
||||
- ``terminal.setCursorPos`` and ``terminal.setCursorXPos`` now work correctly
|
||||
with 0-based coordinates on POSIX (previously, you needed to use
|
||||
1-based coordinates on POSIX for correct behaviour; the Windows behaviour
|
||||
was always correct).
|
||||
|
||||
- ``lineInfoObj`` now returns absolute path instead of project path.
|
||||
It's used by ``lineInfo``, ``check``, ``expect``, ``require``, etc.
|
||||
|
||||
- ``net.sendTo`` no longer returns an int and now raises an ``OSError``.
|
||||
- `threadpool`'s `await` and derivatives have been renamed to `blockUntil`
|
||||
to avoid confusions with `await` from the `async` macro.
|
||||
|
||||
|
||||
#### Breaking changes in the compiler
|
||||
|
||||
- The undocumented ``#? braces`` parsing mode was removed.
|
||||
- The undocumented PHP backend was removed.
|
||||
- The default location of ``nimcache`` for the native code targets was
|
||||
changed. Read [the compiler user guide](https://nim-lang.org/docs/nimc.html#generated-c-code-directory)
|
||||
for more information.
|
||||
|
||||
### Library additions
|
||||
|
||||
- ``re.split`` now also supports the ``maxsplit`` parameter for consistency
|
||||
with ``strutils.split``.
|
||||
- Added ``system.toOpenArray`` in order to support zero-copy slicing
|
||||
operations. This is currently not yet available for the JavaScript target.
|
||||
- Added ``getCurrentDir``, ``findExe``, ``cpDir`` and ``mvDir`` procs to
|
||||
``nimscript``.
|
||||
- The ``times`` module now supports up to nanosecond time resolution when available.
|
||||
- Added the type ``times.Duration`` for representing fixed durations of time.
|
||||
- Added the proc ``times.convert`` for converting between different time units,
|
||||
e.g days to seconds.
|
||||
- Added the proc ``algorithm.binarySearch[T, K]`` with the ```cmp``` parameter.
|
||||
- Added the proc ``algorithm.upperBound``.
|
||||
- Added inverse hyperbolic functions, ``math.arcsinh``, ``math.arccosh`` and ``math.arctanh`` procs.
|
||||
- Added cotangent, secant and cosecant procs ``math.cot``, ``math.sec`` and ``math.csc``; and their hyperbolic, inverse and inverse hyperbolic functions, ``math.coth``, ``math.sech``, ``math.csch``, ``math.arccot``, ``math.arcsec``, ``math.arccsc``, ``math.arccoth``, ``math.arcsech`` and ``math.arccsch`` procs.
|
||||
- Added the procs ``math.floorMod`` and ``math.floorDiv`` for floor based integer division.
|
||||
- Added the procs ``rationals.`div```, ``rationals.`mod```, ``rationals.floorDiv`` and ``rationals.floorMod`` for rationals.
|
||||
- Added the proc ``math.prod`` for product of elements in openArray.
|
||||
- Added the proc ``parseBinInt`` to parse a binary integer from a string, which returns the value.
|
||||
- ``parseOct`` and ``parseBin`` in parseutils now also support the ``maxLen`` argument similar to ``parseHexInt``.
|
||||
- Added the proc ``flush`` for memory mapped files.
|
||||
- Added the ``MemMapFileStream``.
|
||||
- Added a simple interpreting event parser template ``eventParser`` to the ``pegs`` module.
|
||||
- Added ``macros.copyLineInfo`` to copy lineInfo from other node.
|
||||
- Added ``system.ashr`` an arithmetic right shift for integers.
|
||||
|
||||
### Library changes
|
||||
|
||||
- ``macros.astGenRepr``, ``macros.lispRepr`` and ``macros.treeRepr``
|
||||
now escapes the content of string literals consistently.
|
||||
- ``macros.NimSym`` and ``macros.NimIdent`` is now deprecated in favor
|
||||
of the more general ``NimNode``.
|
||||
- ``macros.getImpl`` now includes the pragmas of types, instead of omitting them.
|
||||
- ``macros.hasCustomPragma`` and ``macros.getCustomPragmaVal`` now
|
||||
also support ``ref`` and ``ptr`` types, pragmas on types and variant
|
||||
fields.
|
||||
- ``system.SomeReal`` is now called ``SomeFloat`` for consistency and
|
||||
correctness.
|
||||
- ``algorithm.smartBinarySearch`` and ``algorithm.binarySearch`` is
|
||||
now joined in ``binarySearch``. ``smartbinarySearch`` is now
|
||||
deprecated.
|
||||
- The `terminal` module now exports additional procs for generating ANSI color
|
||||
codes as strings.
|
||||
- Added the parameter ``val`` for the ``CritBitTree[int].inc`` proc.
|
||||
- An exception raised from a ``test`` block of ``unittest`` now shows its type in
|
||||
error message.
|
||||
- The ``compiler/nimeval`` API was rewritten to simplify the "compiler as an
|
||||
API". Using the Nim compiler and its VM as a scripting engine has never been
|
||||
easier. See ``tests/compilerapi/tcompilerapi.nim`` for an example of how to
|
||||
use the Nim VM in a native Nim application.
|
||||
- Added the parameter ``val`` for the ``CritBitTree[T].incl`` proc.
|
||||
- The proc ``tgamma`` was renamed to ``gamma``. ``tgamma`` is deprecated.
|
||||
- The ``pegs`` module now exports getters for the fields of its ``Peg`` and ``NonTerminal``
|
||||
object types. ``Peg``s with child nodes now have the standard ``items`` and ``pairs``
|
||||
iterators.
|
||||
- The ``accept`` socket procedure defined in the ``net`` module can now accept
|
||||
a nil socket.
|
||||
|
||||
### Language additions
|
||||
|
||||
- Dot calls combined with explicit generic instantiations can now be written
|
||||
as ``x.y[:z]`` which is transformed into ``y[z](x)`` by the parser.
|
||||
- ``func`` is now an alias for ``proc {.noSideEffect.}``.
|
||||
- In order to make ``for`` loops and iterators more flexible to use Nim now
|
||||
supports so called "for-loop macros". See
|
||||
the [manual](manual.html#macros-for-loop-macros) for more details.
|
||||
This feature enables a Python-like generic ``enumerate`` implementation.
|
||||
|
||||
- Case statements can now be rewritten via macros. See the [manual](manual.html#macros-case-statement-macros) for more information.
|
||||
This feature enables custom pattern matchers.
|
||||
|
||||
|
||||
- the `typedesc` special type has been renamed to just `type`.
|
||||
- `static` and `type` are now also modifiers similar to `ref` and `ptr`.
|
||||
They denote the special types `static[T]` and `type[T]`.
|
||||
- Forcing compile-time evaluation with `static` now supports specifying
|
||||
the desired target type (as a concrete type or as a type class)
|
||||
- The `type` operator now supports checking that the supplied expression
|
||||
matches an expected type constraint.
|
||||
|
||||
### Language changes
|
||||
|
||||
- The `importcpp` pragma now allows importing the listed fields of generic
|
||||
C++ types. Support for numeric parameters have also been added through
|
||||
the use of `static[T]` types.
|
||||
(#6415)
|
||||
|
||||
- Native C++ exceptions can now be imported with `importcpp` pragma.
|
||||
Imported exceptions can be raised and caught just like Nim exceptions.
|
||||
More details in language manual.
|
||||
|
||||
- ``nil`` for strings/seqs is finally gone. Instead the default value for
|
||||
these is ``"" / @[]``. Use ``--nilseqs:on`` for a transition period.
|
||||
|
||||
- Accessing the binary zero terminator in Nim's native strings
|
||||
is now invalid. Internally a Nim string still has the trailing zero for
|
||||
zero-copy interoperability with ``cstring``. Compile your code with the
|
||||
new switch ``--laxStrings:on`` if you need a transition period.
|
||||
|
||||
- The command syntax now supports keyword arguments after the first comma.
|
||||
|
||||
- Thread-local variables can now be declared inside procs. This implies all
|
||||
the effects of the ``global`` pragma.
|
||||
|
||||
- Nim now supports the ``except`` clause in the export statement.
|
||||
|
||||
- Range float types, example ``range[0.0 .. Inf]``. More details in language manual.
|
||||
- The ``{.this.}`` pragma has been deprecated. It never worked within generics and
|
||||
we found the resulting code harder to read than the more explicit ``obj.field``
|
||||
syntax.
|
||||
- "Memory regions" for pointer types have been deprecated, they were hardly used
|
||||
anywhere. Note that this has **nothing** to do with the ``--gc:regions`` switch
|
||||
of managing memory.
|
||||
|
||||
- The exception hierarchy was slightly reworked, ``SystemError`` was renamed to
|
||||
``CatchableError`` and is the new base class for any exception that is guaranteed to
|
||||
be catchable. This change should have minimal impact on most existing Nim code.
|
||||
|
||||
|
||||
### Tool changes
|
||||
|
||||
- ``jsondoc2`` has been renamed ``jsondoc``, similar to how ``doc2`` was renamed
|
||||
``doc``. The old ``jsondoc`` can still be invoked with ``jsondoc0``.
|
||||
|
||||
### Compiler changes
|
||||
|
||||
- The VM's instruction count limit was raised to 3 million instructions in
|
||||
order to support more complex computations at compile-time.
|
||||
|
||||
- Support for hot code reloading has been implemented for the JavaScript
|
||||
target. To use it, compile your code with `--hotCodeReloading:on` and use a
|
||||
helper library such as LiveReload or BrowserSync.
|
||||
|
||||
- A new compiler option `--cppCompileToNamespace` puts the generated C++ code
|
||||
into the namespace "Nim" in order to avoid naming conflicts with existing
|
||||
C++ code. This is done for all Nim code - internal and exported.
|
||||
|
||||
- Added ``macros.getProjectPath`` and ``ospaths.putEnv`` procs to Nim's virtual
|
||||
machine.
|
||||
|
||||
- The ``deadCodeElim`` option is now always turned on and the switch has no
|
||||
effect anymore, but is recognized for backwards compatibility.
|
||||
|
||||
- ``experimental`` is now a pragma / command line switch that can enable specific
|
||||
language extensions, it is not an all-or-nothing switch anymore.
|
||||
|
||||
- Nintendo Switch was added as a new platform target. See [the compiler user guide](https://nim-lang.org/docs/nimc.html)
|
||||
for more info.
|
||||
|
||||
- macros.bindSym now capable to accepts not only literal string or string constant expression.
|
||||
bindSym enhancement make it also can accepts computed string or ident node inside macros /
|
||||
compile time functions / static blocks. Only in templates / regular code it retains it's old behavior.
|
||||
This new feature can be accessed via {.experimental: "dynamicBindSym".} pragma/switch.
|
||||
|
||||
- On Posix systems the global system wide configuration is now put under ``/etc/nim/nim.cfg``,
|
||||
it used to be ``/etc/nim.cfg``. Usually it does not exist, however.
|
||||
|
||||
- On Posix systems the user configuration is now looked under ``$XDG_CONFIG_HOME/nim/nim.cfg``
|
||||
(if ``XDG_CONFIG_HOME`` is not defined, then under ``~/.config/nim/nim.cfg``). It used to be
|
||||
``$XDG_CONFIG_DIR/nim.cfg`` (and ``~/.config/nim.cfg``).
|
||||
|
||||
Similarly, on Windows, the user configuration is now looked under ``%APPDATA%/nim/nim.cfg``.
|
||||
This used to be ``%APPDATA%/nim.cfg``.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
663
changelogs/changelog_0_18_1.md
Normal file
663
changelogs/changelog_0_18_1.md
Normal file
@@ -0,0 +1,663 @@
|
||||
## v0.18.0 - 01/03/2018
|
||||
|
||||
### Changes affecting backwards compatibility
|
||||
|
||||
#### Breaking changes in the standard library
|
||||
|
||||
- The ``[]`` proc for strings now raises an ``IndexError`` exception when
|
||||
the specified slice is out of bounds. See issue
|
||||
[#6223](https://github.com/nim-lang/Nim/issues/6223) for more details.
|
||||
You can use ``substr(str, start, finish)`` to get the old behaviour back,
|
||||
see [this commit](https://github.com/nim-lang/nimbot/commit/98cc031a27ea89947daa7f0bb536bcf86462941f) for an example.
|
||||
|
||||
- ``strutils.split`` and ``strutils.rsplit`` with an empty string and a
|
||||
separator now returns that empty string.
|
||||
See issue [#4377](https://github.com/nim-lang/Nim/issues/4377).
|
||||
|
||||
- Arrays of char cannot be converted to ``cstring`` anymore, pointers to
|
||||
arrays of char can! This means ``$`` for arrays can finally exist
|
||||
in ``system.nim`` and do the right thing. This means ``$myArrayOfChar`` changed
|
||||
its behaviour! Compile with ``-d:nimNoArrayToString`` to see where to fix your
|
||||
code.
|
||||
|
||||
- `reExtended` is no longer default for the `re` constructor in the `re`
|
||||
module.
|
||||
|
||||
- The behavior of ``$`` has been changed for all standard library collections. The
|
||||
collection-to-string implementations now perform proper quoting and escaping of
|
||||
strings and chars.
|
||||
|
||||
- `newAsyncSocket` taking an `AsyncFD` now runs `setBlocking(false)` on the
|
||||
fd.
|
||||
|
||||
- ``mod`` and bitwise ``and`` do not produce ``range`` subtypes anymore. This
|
||||
turned out to be more harmful than helpful and the language is simpler
|
||||
without this special typing rule.
|
||||
|
||||
- ``formatFloat``/``formatBiggestFloat`` now support formatting floats with zero
|
||||
precision digits. The previous ``precision = 0`` behavior (default formatting)
|
||||
is now available via ``precision = -1``.
|
||||
|
||||
- Moved from stdlib into Nimble packages:
|
||||
- [``basic2d``](https://github.com/nim-lang/basic2d)
|
||||
_deprecated: use ``glm``, ``arraymancer``, ``neo``, or another package instead_
|
||||
- [``basic3d``](https://github.com/nim-lang/basic3d)
|
||||
_deprecated: use ``glm``, ``arraymancer``, ``neo``, or another package instead_
|
||||
- [``gentabs``](https://github.com/lcrees/gentabs)
|
||||
- [``libuv``](https://github.com/lcrees/libuv)
|
||||
- [``numeric``](https://github.com/lcrees/polynumeric)
|
||||
- [``poly``](https://github.com/lcrees/polynumeric)
|
||||
- [``pdcurses``](https://github.com/lcrees/pdcurses)
|
||||
- [``romans``](https://github.com/lcrees/romans)
|
||||
- [``libsvm``](https://github.com/nim-lang/libsvm_legacy)
|
||||
- [``joyent_http_parser``](https://github.com/nim-lang/joyent_http_parser)
|
||||
|
||||
- Proc [toCountTable](https://nim-lang.org/docs/tables.html#toCountTable,openArray[A])
|
||||
now produces a `CountTable` with values correspoding to the number of occurrences
|
||||
of the key in the input. It used to produce a table with all values set to `1`.
|
||||
|
||||
Counting occurrences in a sequence used to be:
|
||||
|
||||
```nim
|
||||
let mySeq = @[1, 2, 1, 3, 1, 4]
|
||||
var myCounter = initCountTable[int]()
|
||||
|
||||
for item in mySeq:
|
||||
myCounter.inc item
|
||||
```
|
||||
|
||||
Now, you can simply do:
|
||||
|
||||
```nim
|
||||
let
|
||||
mySeq = @[1, 2, 1, 3, 1, 4]
|
||||
myCounter = mySeq.toCountTable()
|
||||
```
|
||||
|
||||
- If you use ``--dynlibOverride:ssl`` with OpenSSL 1.0.x, you now have to
|
||||
define ``openssl10`` symbol (``-d:openssl10``). By default OpenSSL 1.1.x is
|
||||
assumed.
|
||||
|
||||
- ``newNativeSocket`` is now named ``createNativeSocket``.
|
||||
|
||||
- ``newAsyncNativeSocket`` is now named ``createAsyncNativeSocket``
|
||||
and it no longer raises an OS error but returns an ``osInvalidSocket`` when
|
||||
creation fails.
|
||||
|
||||
- The ``securehash`` module is now deprecated. Instead import ``std / sha1``.
|
||||
|
||||
- The ``readPasswordFromStdin`` proc has been moved from the ``rdstdin``
|
||||
to the ``terminal`` module, thus it does not depend on linenoise anymore.
|
||||
|
||||
#### Breaking changes in the compiler
|
||||
|
||||
- ``\n`` is now only the single line feed character like in most
|
||||
other programming languages. The new platform specific newline escape sequence is
|
||||
written as ``\p``. This change only affects the Windows platform.
|
||||
|
||||
- The overloading rules changed slightly so that constrained generics are
|
||||
preferred over unconstrained generics. (Bug #6526)
|
||||
|
||||
- We changed how array accesses "from backwards" like ``a[^1]`` or ``a[0..^1]`` are
|
||||
implemented. These are now implemented purely in ``system.nim`` without compiler
|
||||
support. There is a new "heterogenous" slice type ``system.HSlice`` that takes 2
|
||||
generic parameters which can be ``BackwardsIndex`` indices. ``BackwardsIndex`` is
|
||||
produced by ``system.^``.
|
||||
This means if you overload ``[]`` or ``[]=`` you need to ensure they also work
|
||||
with ``system.BackwardsIndex`` (if applicable for the accessors).
|
||||
|
||||
- The parsing rules of ``if`` expressions were changed so that multiple
|
||||
statements are allowed in the branches. We found few code examples that
|
||||
now fail because of this change, but here is one:
|
||||
|
||||
```nim
|
||||
t[ti] = if exp_negative: '-' else: '+'; inc(ti)
|
||||
```
|
||||
|
||||
This now needs to be written as:
|
||||
|
||||
```nim
|
||||
t[ti] = (if exp_negative: '-' else: '+'); inc(ti)
|
||||
```
|
||||
|
||||
- The experimental overloading of the dot ``.`` operators now take
|
||||
an ``untyped``` parameter as the field name, it used to be
|
||||
a ``static[string]``. You can use ``when defined(nimNewDot)`` to make
|
||||
your code work with both old and new Nim versions.
|
||||
See [special-operators](https://nim-lang.org/docs/manual.html#special-operators)
|
||||
for more information.
|
||||
|
||||
- ``yield`` (or ``await`` which is mapped to ``yield``) never worked reliably
|
||||
in an array, seq or object constructor and is now prevented at compile-time.
|
||||
|
||||
### Library additions
|
||||
|
||||
- **Added ``sequtils.mapLiterals`` for easier construction of array and tuple literals.**
|
||||
|
||||
- Added ``system.runnableExamples`` to make examples in Nim's documentation easier
|
||||
to write and test. The examples are tested as the last step of
|
||||
``nim doc``.
|
||||
|
||||
- Implemented ``getIoHandler`` proc in the ``asyncdispatch`` module that allows
|
||||
you to retrieve the underlying IO Completion Port or ``Selector[AsyncData]``
|
||||
object in the specified dispatcher.
|
||||
|
||||
- For string formatting / interpolation a new module
|
||||
called [strformat](https://nim-lang.org/docs/strformat.html) has been added
|
||||
to the stdlib.
|
||||
|
||||
- The `ReadyKey` type in the selectors module now contains an ``errorCode``
|
||||
field to help distinguish between ``Event.Error`` events.
|
||||
|
||||
- Implemented an `accept` proc that works on a `SocketHandle` in
|
||||
``nativesockets``.
|
||||
|
||||
- Added ``algorithm.rotateLeft``.
|
||||
|
||||
- Added ``typetraits.$`` as an alias for ``typetraits.name``.
|
||||
|
||||
- Added ``system.getStackTraceEntries`` that allows you to access the stack
|
||||
trace in a structured manner without string parsing.
|
||||
|
||||
- Added ``parseutils.parseSaturatedNatural``.
|
||||
|
||||
- Added ``macros.unpackVarargs``.
|
||||
|
||||
- Added support for asynchronous programming for the JavaScript backend using
|
||||
the `asyncjs` module.
|
||||
|
||||
- Added true color support for some terminals. Example:
|
||||
```nim
|
||||
import colors, terminal
|
||||
|
||||
const Nim = "Efficient and expressive programming."
|
||||
|
||||
var
|
||||
fg = colYellow
|
||||
bg = colBlue
|
||||
int = 1.0
|
||||
|
||||
enableTrueColors()
|
||||
|
||||
for i in 1..15:
|
||||
styledEcho bgColor, bg, fgColor, fg, Nim, resetStyle
|
||||
int -= 0.01
|
||||
fg = intensity(fg, int)
|
||||
|
||||
setForegroundColor colRed
|
||||
setBackgroundColor colGreen
|
||||
styledEcho "Red on Green.", resetStyle
|
||||
```
|
||||
|
||||
### Library changes
|
||||
|
||||
- ``echo`` now works with strings that contain ``\0`` (the binary zero is not
|
||||
shown) and ``nil`` strings are equal to empty strings.
|
||||
|
||||
- JSON: Deprecated `getBVal`, `getFNum`, and `getNum` in favour of
|
||||
`getBool`, `getFloat`, `getBiggestInt`. A new `getInt` procedure was also
|
||||
added.
|
||||
|
||||
- ``rationals.toRational`` now uses an algorithm based on continued fractions.
|
||||
This means its results are more precise and it can't run into an infinite loop
|
||||
anymore.
|
||||
|
||||
- ``os.getEnv`` now takes an optional ``default`` parameter that tells ``getEnv``
|
||||
what to return if the environment variable does not exist.
|
||||
|
||||
- The ``random`` procs in ``random.nim`` have all been deprecated. Instead use
|
||||
the new ``rand`` procs. The module now exports the state of the random
|
||||
number generator as type ``Rand`` so multiple threads can easily use their
|
||||
own random number generators that do not require locking. For more information
|
||||
about this rename see issue [#6934](https://github.com/nim-lang/Nim/issues/6934)
|
||||
|
||||
- ``writeStackTrace`` is now proclaimed to have no IO effect (even though it does)
|
||||
so that it is more useful for debugging purposes.
|
||||
|
||||
- ``db_mysql`` module: ``DbConn`` is now a ``distinct`` type that doesn't expose the
|
||||
details of the underlying ``PMySQL`` type.
|
||||
|
||||
- ``parseopt2`` is now deprecated, use ``parseopt`` instead.
|
||||
|
||||
### Language additions
|
||||
|
||||
- It is now possible to forward declare object types so that mutually
|
||||
recursive types can be created across module boundaries. See
|
||||
[package level objects](https://nim-lang.org/docs/manual.html#package-level-objects)
|
||||
for more information.
|
||||
|
||||
- Added support for casting between integers of same bitsize in VM (compile time and nimscript).
|
||||
This allows to, among other things, reinterpret signed integers as unsigned.
|
||||
|
||||
- Custom pragmas are now supported using pragma ``pragma``, please see language
|
||||
manual for details.
|
||||
|
||||
- Standard library modules can now also be imported via the ``std`` pseudo-directory.
|
||||
This is useful in order to distinguish between standard library and nimble package
|
||||
imports:
|
||||
|
||||
```nim
|
||||
import std / [strutils, os, osproc]
|
||||
import someNimblePackage / [strutils, os]
|
||||
```
|
||||
|
||||
### Language changes
|
||||
|
||||
- The **unary** ``<`` is now deprecated, for ``.. <`` use ``..<`` for other usages
|
||||
use the ``pred`` proc.
|
||||
|
||||
- Bodies of ``for`` loops now get their own scope:
|
||||
|
||||
```nim
|
||||
# now compiles:
|
||||
for i in 0..4:
|
||||
let i = i + 1
|
||||
echo i
|
||||
```
|
||||
|
||||
- To make Nim even more robust the system iterators ``..`` and ``countup``
|
||||
now only accept a single generic type ``T``. This means the following code
|
||||
doesn't die with an "out of range" error anymore:
|
||||
|
||||
```nim
|
||||
var b = 5.Natural
|
||||
var a = -5
|
||||
for i in a..b:
|
||||
echo i
|
||||
```
|
||||
|
||||
- ``atomic`` and ``generic`` are no longer keywords in Nim. ``generic`` used to be
|
||||
an alias for ``concept``, ``atomic`` was not used for anything.
|
||||
|
||||
- The memory manager now uses a variant of the TLSF algorithm that has much
|
||||
better memory fragmentation behaviour. According
|
||||
to [http://www.gii.upv.es/tlsf/](http://www.gii.upv.es/tlsf/) the maximum
|
||||
fragmentation measured is lower than 25%. As a nice bonus ``alloc`` and
|
||||
``dealloc`` became O(1) operations.
|
||||
|
||||
- The compiler is now more consistent in its treatment of ambiguous symbols:
|
||||
Types that shadow procs and vice versa are marked as ambiguous (bug #6693).
|
||||
|
||||
- codegenDecl pragma now works for the JavaScript backend. It returns an empty
|
||||
string for function return type placeholders.
|
||||
|
||||
- Extra semantic checks for procs with noreturn pragma: return type is not allowed,
|
||||
statements after call to noreturn procs are no longer allowed.
|
||||
|
||||
- Noreturn proc calls and raising exceptions branches are now skipped during common type
|
||||
deduction in ``if`` and ``case`` expressions. The following code snippets now compile:
|
||||
```nim
|
||||
import strutils
|
||||
let str = "Y"
|
||||
let a = case str:
|
||||
of "Y": true
|
||||
of "N": false
|
||||
else: raise newException(ValueError, "Invalid boolean")
|
||||
let b = case str:
|
||||
of nil, "": raise newException(ValueError, "Invalid boolean")
|
||||
elif str.startsWith("Y"): true
|
||||
elif str.startsWith("N"): false
|
||||
else: false
|
||||
let c = if str == "Y": true
|
||||
elif str == "N": false
|
||||
else:
|
||||
echo "invalid bool"
|
||||
quit("this is the end")
|
||||
```
|
||||
|
||||
- Pragmas now support call syntax, for example: ``{.exportc"myname".}`` and
|
||||
``{.exportc("myname").}``
|
||||
|
||||
- The ``deprecated`` pragma now supports a user-definable warning message for procs.
|
||||
|
||||
```nim
|
||||
proc bar {.deprecated: "use foo instead".} =
|
||||
return
|
||||
|
||||
bar()
|
||||
```
|
||||
|
||||
### Tool changes
|
||||
|
||||
- The ``nim doc`` command is now an alias for ``nim doc2``, the second version of
|
||||
the documentation generator. The old version 1 can still be accessed
|
||||
via the new ``nim doc0`` command.
|
||||
|
||||
- Nim's ``rst2html`` command now supports the testing of code snippets via an RST
|
||||
extension that we called ``:test:``::
|
||||
|
||||
```rst
|
||||
.. code-block:: nim
|
||||
:test:
|
||||
# shows how the 'if' statement works
|
||||
if true: echo "yes"
|
||||
```
|
||||
|
||||
### Compiler changes
|
||||
|
||||
### Bugfixes
|
||||
|
||||
- Fixed "ReraiseError when using try/except within finally block"
|
||||
([#5871](https://github.com/nim-lang/Nim/issues/5871))
|
||||
- Fixed "Range type inference leads to counter-intuitive behvaiour"
|
||||
([#5854](https://github.com/nim-lang/Nim/issues/5854))
|
||||
- Fixed "JSON % operator can fail in extern procs with dynamic types"
|
||||
([#6385](https://github.com/nim-lang/Nim/issues/6385))
|
||||
- Fixed ""intVal is not accessible" in VM"
|
||||
([#6083](https://github.com/nim-lang/Nim/issues/6083))
|
||||
- Fixed "Add excl for OrderedSet"
|
||||
([#2467](https://github.com/nim-lang/Nim/issues/2467))
|
||||
- Fixed "newSeqOfCap actually doesn't reserve memory"
|
||||
([#6403](https://github.com/nim-lang/Nim/issues/6403))
|
||||
- Fixed "[Regression] Nim segfaults"
|
||||
([#6435](https://github.com/nim-lang/Nim/issues/6435))
|
||||
- Fixed "Seq assignment is slower than expected"
|
||||
([#6433](https://github.com/nim-lang/Nim/issues/6433))
|
||||
- Fixed "json module issues with empty dicts and lists"
|
||||
([#6438](https://github.com/nim-lang/Nim/issues/6438))
|
||||
- Fixed "mingw installed via finish.exe fails to link if Nim located in path with whitespace"
|
||||
([#6452](https://github.com/nim-lang/Nim/issues/6452))
|
||||
- Fixed "unittest.check does not perform short-circuit evaluation"
|
||||
([#5784](https://github.com/nim-lang/Nim/issues/5784))
|
||||
- Fixed "Error while concatenating an array of chars."
|
||||
([#5861](https://github.com/nim-lang/Nim/issues/5861))
|
||||
- Fixed "range initialization: [ProveInit] hint: Cannot prove that"
|
||||
([#6474](https://github.com/nim-lang/Nim/issues/6474))
|
||||
- Fixed "scanf can call procs with side-effects multiple times"
|
||||
([#6487](https://github.com/nim-lang/Nim/issues/6487))
|
||||
- Fixed "gcsafe detection problem"
|
||||
([#5620](https://github.com/nim-lang/Nim/issues/5620))
|
||||
- Fixed "C++ codegen: `mitems` generates invalid code."
|
||||
([#4910](https://github.com/nim-lang/Nim/issues/4910))
|
||||
- Fixed "strange runtime behavior on macOS"
|
||||
([#6496](https://github.com/nim-lang/Nim/issues/6496))
|
||||
- Fixed "stdtmpl: invalid indentation after a line ending in question mark"
|
||||
([#5070](https://github.com/nim-lang/Nim/issues/5070))
|
||||
- Fixed "Windows: NAN troubles on c backend"
|
||||
([#6511](https://github.com/nim-lang/Nim/issues/6511))
|
||||
- Fixed "lib/nim/system/cellsets.nim(33, 31) Error: type mismatch while attempting to compile for 16bit CPUs"
|
||||
([#3558](https://github.com/nim-lang/Nim/issues/3558))
|
||||
- Fixed "Can't compile dynlib with ``-d:useNimRtl`` and ``--threads:on``"
|
||||
([#5143](https://github.com/nim-lang/Nim/issues/5143))
|
||||
- Fixed "var s = @[0,1,2,...] can generate thousand of single assignments in C code"
|
||||
([#5007](https://github.com/nim-lang/Nim/issues/5007))
|
||||
- Fixed "`echo` discards everything after a null character"
|
||||
([#1137](https://github.com/nim-lang/Nim/issues/1137))
|
||||
- Fixed "Turn off reExtended by default"
|
||||
([#5627](https://github.com/nim-lang/Nim/issues/5627))
|
||||
- Fixed "Bad Links in docs/backends.html"
|
||||
([#5914](https://github.com/nim-lang/Nim/issues/5914))
|
||||
- Fixed "Index out of bounds error in db_postgres when executing non parameter-substituted queries containing "?""
|
||||
([#6571](https://github.com/nim-lang/Nim/issues/6571))
|
||||
- Fixed "Please add pipe2 support to posix stdlib"
|
||||
([#6553](https://github.com/nim-lang/Nim/issues/6553))
|
||||
- Fixed "Return semantics vary depending on return style"
|
||||
([#6422](https://github.com/nim-lang/Nim/issues/6422))
|
||||
- Fixed "parsecsv.open reports SIGSEGV when calling 'open' on missing file"
|
||||
([#6148](https://github.com/nim-lang/Nim/issues/6148))
|
||||
- Fixed "VCC: Nim generates non-compilable code for system.nim"
|
||||
([#6606](https://github.com/nim-lang/Nim/issues/6606))
|
||||
- Fixed "Generic subtype matches worse than a generic"
|
||||
([#6526](https://github.com/nim-lang/Nim/issues/6526))
|
||||
- Fixed "formatFloat inconsistent scientific notation"
|
||||
([#6589](https://github.com/nim-lang/Nim/issues/6589))
|
||||
- Fixed "Generated c code calls function twice"
|
||||
([#6292](https://github.com/nim-lang/Nim/issues/6292))
|
||||
- Fixed "Range type inference leads to counter-intuitive behvaiour"
|
||||
([#5854](https://github.com/nim-lang/Nim/issues/5854))
|
||||
- Fixed "New backward indexing is too limited"
|
||||
([#6631](https://github.com/nim-lang/Nim/issues/6631))
|
||||
- Fixed "Table usage in a macro (SIGSEGV: Illegal storage access.)"
|
||||
([#1860](https://github.com/nim-lang/Nim/issues/1860))
|
||||
- Fixed "Incorrect deprecation error"
|
||||
([#6634](https://github.com/nim-lang/Nim/issues/6634))
|
||||
- Fixed "Wrong indices in arrays not starting with 0"
|
||||
([#6675](https://github.com/nim-lang/Nim/issues/6675))
|
||||
- Fixed "if expressions"
|
||||
([#6609](https://github.com/nim-lang/Nim/issues/6609))
|
||||
- Fixed "BackwardsIndex: converter + `[]` + unrelated type[^1]: lib/system.nim(3536, 3) Error"
|
||||
([#6692](https://github.com/nim-lang/Nim/issues/6692))
|
||||
- Fixed "BackwardsIndex: converter + `[]` + unrelated type[^1]: lib/system.nim(3536, 3) Error"
|
||||
([#6692](https://github.com/nim-lang/Nim/issues/6692))
|
||||
- Fixed "js backend 0.17.3: array bounds check for non zero based arrays is buggy"
|
||||
([#6532](https://github.com/nim-lang/Nim/issues/6532))
|
||||
- Fixed "HttpClient's new API doesn't work through a proxy for https URLs"
|
||||
([#6685](https://github.com/nim-lang/Nim/issues/6685))
|
||||
- Fixed "isServing isn't declared and isn't compiling"
|
||||
([#6707](https://github.com/nim-lang/Nim/issues/6707))
|
||||
- Fixed "[Regression] value out of range"
|
||||
([#6710](https://github.com/nim-lang/Nim/issues/6710))
|
||||
|
||||
- Fixed "Error when using `multisync` macro"
|
||||
([#6708](https://github.com/nim-lang/Nim/issues/6708))
|
||||
|
||||
- Fixed "formatFloat inconsistent scientific notation"
|
||||
([#6589](https://github.com/nim-lang/Nim/issues/6589))
|
||||
- Fixed "Using : (constructor arguments) for passing values to functions with default arguments causes a compiler crash."
|
||||
([#6765](https://github.com/nim-lang/Nim/issues/6765))
|
||||
- Fixed "In-place object initialization leads to vcc incompatible code"
|
||||
([#6757](https://github.com/nim-lang/Nim/issues/6757))
|
||||
- Fixed "Improve parseCookies doc"
|
||||
([#5721](https://github.com/nim-lang/Nim/issues/5721))
|
||||
- Fixed "Parser regression with nested do notation inside conditional"
|
||||
([#6166](https://github.com/nim-lang/Nim/issues/6166))
|
||||
- Fixed "Request for better error message"
|
||||
([#6776](https://github.com/nim-lang/Nim/issues/6776))
|
||||
- Fixed "Testament tester does not execute test with `exitcode` only"
|
||||
([#6775](https://github.com/nim-lang/Nim/issues/6775))
|
||||
- Fixed "JS integer division off by one"
|
||||
([#6753](https://github.com/nim-lang/Nim/issues/6753))
|
||||
- Fixed "Regression: cannot prove not nil"
|
||||
([#5781](https://github.com/nim-lang/Nim/issues/5781))
|
||||
- Fixed "SIGSEGV: Illegal storage access. (Attempt to read from nil?) in generic proc"
|
||||
([#6073](https://github.com/nim-lang/Nim/issues/6073))
|
||||
- Fixed "Request for better error message"
|
||||
([#6776](https://github.com/nim-lang/Nim/issues/6776))
|
||||
- Fixed "Nim #head: sorting via reference hangs compiler"
|
||||
([#6724](https://github.com/nim-lang/Nim/issues/6724))
|
||||
- Fixed "Cannot cast pointer to char in cpp"
|
||||
([#5979](https://github.com/nim-lang/Nim/issues/5979))
|
||||
- Fixed "asynchttpserver replies with several errors on single request"
|
||||
([#6386](https://github.com/nim-lang/Nim/issues/6386))
|
||||
- Fixed "object variants superclass trigger bad codegen"
|
||||
([#5521](https://github.com/nim-lang/Nim/issues/5521))
|
||||
- Fixed "JS integer division off by one"
|
||||
([#6753](https://github.com/nim-lang/Nim/issues/6753))
|
||||
- Fixed "js backend compiler crash with tables indexed by certain types"
|
||||
([#6568](https://github.com/nim-lang/Nim/issues/6568))
|
||||
- Fixed "Jsgen bug with is"
|
||||
([#6445](https://github.com/nim-lang/Nim/issues/6445))
|
||||
- Fixed "Subrange definition with ..<"
|
||||
([#6788](https://github.com/nim-lang/Nim/issues/6788))
|
||||
- Fixed "fields not initialized: array with enum index type as object field."
|
||||
([#6682](https://github.com/nim-lang/Nim/issues/6682))
|
||||
- Fixed "Can not delete data in table when table's data type is kind of "not nil""
|
||||
([#6555](https://github.com/nim-lang/Nim/issues/6555))
|
||||
- Fixed "tables.nim: Cannot prove that 'n' is initialized"
|
||||
([#6121](https://github.com/nim-lang/Nim/issues/6121))
|
||||
- Fixed "issues with 'not nil' applied to a closure proc"
|
||||
([#6489](https://github.com/nim-lang/Nim/issues/6489))
|
||||
- Fixed "`not nil` not working in some cases"
|
||||
([#4686](https://github.com/nim-lang/Nim/issues/4686))
|
||||
- Fixed "Cannot prove '@[v]' is not nil"
|
||||
([#3993](https://github.com/nim-lang/Nim/issues/3993))
|
||||
|
||||
- Fixed "Feature: support TCP_NODELAY in net.sockets"
|
||||
([#6795](https://github.com/nim-lang/Nim/issues/6795))
|
||||
- Fixed "Code that makes the compiler throw an error message and then hangs"
|
||||
([#6820](https://github.com/nim-lang/Nim/issues/6820))
|
||||
- Fixed "Code that makes the compiler throw an error message and then hangs"
|
||||
([#6820](https://github.com/nim-lang/Nim/issues/6820))
|
||||
- Fixed "Inconsistent behavior with sequence and string slicing"
|
||||
([#6223](https://github.com/nim-lang/Nim/issues/6223))
|
||||
- Fixed "Wrong behavior of "split" (proc and iterator)"
|
||||
([#4377](https://github.com/nim-lang/Nim/issues/4377))
|
||||
- Fixed "[Documentation] Invalid module name: [foo, bar]"
|
||||
([#6831](https://github.com/nim-lang/Nim/issues/6831))
|
||||
- Fixed "The destructor is not called for temporary objects"
|
||||
([#4214](https://github.com/nim-lang/Nim/issues/4214))
|
||||
- Fixed "Destructors does not work with implicit items iterator in for loop"
|
||||
([#985](https://github.com/nim-lang/Nim/issues/985))
|
||||
- Fixed "Error in template when using the type of the parameter inside it"
|
||||
([#6756](https://github.com/nim-lang/Nim/issues/6756))
|
||||
- Fixed "should json.to() respect parent attributes?"
|
||||
([#5856](https://github.com/nim-lang/Nim/issues/5856))
|
||||
- Fixed "json 'to' macro can not marshalize into tuples"
|
||||
([#6095](https://github.com/nim-lang/Nim/issues/6095))
|
||||
- Fixed "json.to fails with seq[T]"
|
||||
([#6604](https://github.com/nim-lang/Nim/issues/6604))
|
||||
- Fixed "json.to() is not worth using compared to marshal.to[T]"
|
||||
([#5848](https://github.com/nim-lang/Nim/issues/5848))
|
||||
- Fixed "Memory not being released in time, running out of memory"
|
||||
([#6031](https://github.com/nim-lang/Nim/issues/6031))
|
||||
- Fixed "[Regression] Bad C codegen for generic code"
|
||||
([#6889](https://github.com/nim-lang/Nim/issues/6889))
|
||||
- Fixed "rollingFileLogger deletes file on every start."
|
||||
([#6264](https://github.com/nim-lang/Nim/issues/6264))
|
||||
- Fixed "Remove/deprecate securehash module."
|
||||
([#6033](https://github.com/nim-lang/Nim/issues/6033))
|
||||
- Fixed "[bug or not] object construction for seq[T] failed without space after colon"
|
||||
([#5999](https://github.com/nim-lang/Nim/issues/5999))
|
||||
- Fixed "issues with the random module"
|
||||
([#4726](https://github.com/nim-lang/Nim/issues/4726))
|
||||
- Fixed "Reassigning local var to seq of objects results in nil element in Object's seq field"
|
||||
([#668](https://github.com/nim-lang/Nim/issues/668))
|
||||
- Fixed "Compilation error with "newseq[string]""
|
||||
([#6726](https://github.com/nim-lang/Nim/issues/6726))
|
||||
- Fixed "await inside array/dict literal produces invalid code - Part 2"
|
||||
([#6626](https://github.com/nim-lang/Nim/issues/6626))
|
||||
- Fixed "terminal.eraseline() gives OverflowError on Windows"
|
||||
([#6931](https://github.com/nim-lang/Nim/issues/6931))
|
||||
- Fixed "[Regression] `sequtils.any` conflicts with `system.any`"
|
||||
([#6932](https://github.com/nim-lang/Nim/issues/6932))
|
||||
- Fixed "C++ codegen: `mitems` generates invalid code."
|
||||
([#4910](https://github.com/nim-lang/Nim/issues/4910))
|
||||
- Fixed "seq.mitems produces invalid cpp codegen"
|
||||
([#6892](https://github.com/nim-lang/Nim/issues/6892))
|
||||
- Fixed "Concepts regression"
|
||||
([#6108](https://github.com/nim-lang/Nim/issues/6108))
|
||||
- Fixed "Generic iterable concept with array crashes compiler"
|
||||
([#6277](https://github.com/nim-lang/Nim/issues/6277))
|
||||
- Fixed "C code generation "‘a’ is a pointer; did you mean to use ‘->’?""
|
||||
([#6462](https://github.com/nim-lang/Nim/issues/6462))
|
||||
- Fixed "`--NimblePath` fails if a `-` in path which is not followed by a number"
|
||||
([#6949](https://github.com/nim-lang/Nim/issues/6949))
|
||||
- Fixed ""not registered in the selector" in asyncfile.close() for something that clearly was registered"
|
||||
([#6906](https://github.com/nim-lang/Nim/issues/6906))
|
||||
- Fixed "strange frexp behavior"
|
||||
([#6353](https://github.com/nim-lang/Nim/issues/6353))
|
||||
|
||||
- Fixed "noreturn branches of case statements shouldn't contribute to type"
|
||||
([#6885](https://github.com/nim-lang/Nim/issues/6885))
|
||||
- Fixed "Type inference for 'if' statements changed"
|
||||
([#6980](https://github.com/nim-lang/Nim/issues/6980))
|
||||
- Fixed "newest asyncdispatch recursion"
|
||||
([#6100](https://github.com/nim-lang/Nim/issues/6100))
|
||||
- Fixed "Ambiguous identifier between set type and proc"
|
||||
([#6965](https://github.com/nim-lang/Nim/issues/6965))
|
||||
|
||||
- Fixed "Inconsistent behavior with sequence and string slicing"
|
||||
([#6223](https://github.com/nim-lang/Nim/issues/6223))
|
||||
|
||||
- Fixed "Unsupported OpenSSL library imported dynamically"
|
||||
([#5000](https://github.com/nim-lang/Nim/issues/5000))
|
||||
- Fixed "`nim check` segfaults"
|
||||
([#6972](https://github.com/nim-lang/Nim/issues/6972))
|
||||
- Fixed "GC deadlock"
|
||||
([#6988](https://github.com/nim-lang/Nim/issues/6988))
|
||||
- Fixed "Create a seq without memory initialization"
|
||||
([#6401](https://github.com/nim-lang/Nim/issues/6401))
|
||||
- Fixed "Fix bug for getch on Windows while using the arrow keys"
|
||||
([#6966](https://github.com/nim-lang/Nim/issues/6966))
|
||||
- Fixed "runnableExamples doesn't work in templates"
|
||||
([#7018](https://github.com/nim-lang/Nim/issues/7018))
|
||||
- Fixed "runnableExamples doesn't work with untyped statement blocks"
|
||||
([#7019](https://github.com/nim-lang/Nim/issues/7019))
|
||||
|
||||
- Fixed "Critical bug in parseBiggestFloat"
|
||||
([#7060](https://github.com/nim-lang/Nim/issues/7060))
|
||||
- Fixed "[RFC] strformat.% should be gone"
|
||||
([#7078](https://github.com/nim-lang/Nim/issues/7078))
|
||||
- Fixed "compiler crash on simple macro"
|
||||
([#7093](https://github.com/nim-lang/Nim/issues/7093))
|
||||
- Fixed "Make newlines sane again"
|
||||
([#7089](https://github.com/nim-lang/Nim/issues/7089))
|
||||
- Fixed "JS - Unicode enum string representation issue"
|
||||
([#6741](https://github.com/nim-lang/Nim/issues/6741))
|
||||
- Fixed "Strange behaviour of 0.17.3 (working ok in 0.17.2)"
|
||||
([#6989](https://github.com/nim-lang/Nim/issues/6989))
|
||||
- Fixed "Strange behaviour of 0.17.3 (working ok in 0.17.2)"
|
||||
([#6989](https://github.com/nim-lang/Nim/issues/6989))
|
||||
- Fixed "Compiler crash: try expression with infix as"
|
||||
([#7116](https://github.com/nim-lang/Nim/issues/7116))
|
||||
- Fixed "nimsuggest crash"
|
||||
([#7140](https://github.com/nim-lang/Nim/issues/7140))
|
||||
- Fixed "[RFC] Reintroduce readChar"
|
||||
([#7072](https://github.com/nim-lang/Nim/issues/7072))
|
||||
- Fixed "Copyright line needs updating"
|
||||
([#7129](https://github.com/nim-lang/Nim/issues/7129))
|
||||
- Fixed "-0.0 doesn't result in negative zero in VM"
|
||||
([#7079](https://github.com/nim-lang/Nim/issues/7079))
|
||||
- Fixed "Windows large filesize"
|
||||
([#7121](https://github.com/nim-lang/Nim/issues/7121))
|
||||
- Fixed "Securehash is not parsimonious with MD5 and other hash modules"
|
||||
([#6961](https://github.com/nim-lang/Nim/issues/6961))
|
||||
- Fixed "os.findExe() shouldn't look in current directory on posix, unless exe has a /"
|
||||
([#6939](https://github.com/nim-lang/Nim/issues/6939))
|
||||
- Fixed "`compiles(...)` with `fatal` pragma causes compiler to exit early"
|
||||
([#7080](https://github.com/nim-lang/Nim/issues/7080))
|
||||
- Fixed "NPE when compile macro that returns concrete value"
|
||||
([#5450](https://github.com/nim-lang/Nim/issues/5450))
|
||||
- Fixed "Using a variable of type `int | float` causes internal compiler error"
|
||||
([#6946](https://github.com/nim-lang/Nim/issues/6946))
|
||||
- Fixed "Unsigned integers could not be used as array indexes."
|
||||
([#7153](https://github.com/nim-lang/Nim/issues/7153))
|
||||
- Fixed "countdown with uint causes underflow"
|
||||
([#4220](https://github.com/nim-lang/Nim/issues/4220))
|
||||
- Fixed "Inconsistent method call syntax"
|
||||
([#7200](https://github.com/nim-lang/Nim/issues/7200))
|
||||
- Fixed "Impossible to create an empty const array"
|
||||
([#6853](https://github.com/nim-lang/Nim/issues/6853))
|
||||
- Fixed "Strange UINT handling"
|
||||
([#3985](https://github.com/nim-lang/Nim/issues/3985))
|
||||
- Fixed "Bad codegen when passing arg that is part of return value destination"
|
||||
([#6960](https://github.com/nim-lang/Nim/issues/6960))
|
||||
- Fixed "No info about gcsafety in error message when global var is accessed in async proc"
|
||||
([#6186](https://github.com/nim-lang/Nim/issues/6186))
|
||||
- Fixed "Resolving package vs. local import ambiguities"
|
||||
([#2819](https://github.com/nim-lang/Nim/issues/2819))
|
||||
- Fixed "Internal error with type() operator"
|
||||
([#3711](https://github.com/nim-lang/Nim/issues/3711))
|
||||
- Fixed "newAsyncSocket should raise an OS error plus other inconsistencies"
|
||||
([#4995](https://github.com/nim-lang/Nim/issues/4995))
|
||||
- Fixed "mapLiterals fails with negative values"
|
||||
([#7215](https://github.com/nim-lang/Nim/issues/7215))
|
||||
- Fixed "fmWrite doesn't truncate file with openAsync, unlike open()"
|
||||
([#5531](https://github.com/nim-lang/Nim/issues/5531))
|
||||
- Fixed "Move libsvm to an external nimble module"
|
||||
([#5786](https://github.com/nim-lang/Nim/issues/5786))
|
||||
- Fixed "Prevent acceptAddr gotcha with newSocket"
|
||||
([#7227](https://github.com/nim-lang/Nim/issues/7227))
|
||||
- Fixed "strtabs.getOrDefault is inconsistent with tables.getOrDefault"
|
||||
([#4265](https://github.com/nim-lang/Nim/issues/4265))
|
||||
|
||||
- Fixed "Code falling through into exception handler when no exception thrown."
|
||||
([#7232](https://github.com/nim-lang/Nim/issues/7232))
|
||||
- Fixed "the new generic inference rules are broken"
|
||||
([#7247](https://github.com/nim-lang/Nim/issues/7247))
|
||||
- Fixed "Odd `..<` regression"
|
||||
([#6992](https://github.com/nim-lang/Nim/issues/6992))
|
||||
- Fixed "Different proc type inferred from default parameter"
|
||||
([#4659](https://github.com/nim-lang/Nim/issues/4659))
|
||||
- Fixed "Different proc type inferred from default parameter"
|
||||
([#4659](https://github.com/nim-lang/Nim/issues/4659))
|
||||
- Fixed "Testament sometimes ignores test failures"
|
||||
([#7236](https://github.com/nim-lang/Nim/issues/7236))
|
||||
- Fixed "New Allocator Fails On >=4GB Requests"
|
||||
([#7120](https://github.com/nim-lang/Nim/issues/7120))
|
||||
- Fixed "User pragmas hide effect specifications from sempass2"
|
||||
([#7216](https://github.com/nim-lang/Nim/issues/7216))
|
||||
- Fixed "C++: SIGABRT instead of IndexError for out-of-bounds"
|
||||
([#6512](https://github.com/nim-lang/Nim/issues/6512))
|
||||
- Fixed "An uncaught exception in cpp mode doesn't show the exception name/msg"
|
||||
([#6431](https://github.com/nim-lang/Nim/issues/6431))
|
||||
253
changelogs/changelog_0_19_0.md
Normal file
253
changelogs/changelog_0_19_0.md
Normal file
@@ -0,0 +1,253 @@
|
||||
## v0.19.X - XX/XX/2018
|
||||
|
||||
### Changes affecting backwards compatibility
|
||||
|
||||
- The stdlib module ``future`` has been renamed to ``sugar``.
|
||||
- ``macros.callsite`` is now deprecated. Since the introduction of ``varargs``
|
||||
parameters this became unnecessary.
|
||||
- Anonymous tuples with a single element can now be written as ``(1,)`` with a
|
||||
trailing comma. The underlying AST is ``nnkTupleConstr(newLit 1)`` for this
|
||||
example. ``nnkTupleConstr`` is a new node kind your macros need to be able
|
||||
to deal with!
|
||||
- Indexing into a ``cstring`` for the JS target is now mapped
|
||||
to ``charCodeAt``.
|
||||
- Assignments that would "slice" an object into its supertype are now prevented
|
||||
at runtime. Use ``ref object`` with inheritance rather than ``object`` with
|
||||
inheritance to prevent this issue.
|
||||
- The ``not nil`` type annotation now has to be enabled explicitly
|
||||
via ``{.experimental: "notnil"}`` as we are still not pleased with how this
|
||||
feature works with Nim's containers.
|
||||
- The parser now warns about inconsistent spacing around binary operators as
|
||||
these can easily be confused with unary operators. This warning will likely
|
||||
become an error in the future.
|
||||
- The ``'c`` and ``'C'`` suffix for octal literals is now deprecated to
|
||||
bring the language in line with the standard library (e.g. ``parseOct``).
|
||||
- The dot style for import paths (e.g ``import path.to.module`` instead of
|
||||
``import path/to/module``) has been deprecated.
|
||||
|
||||
#### Breaking changes in the standard library
|
||||
|
||||
- ``re.split`` for empty regular expressions now yields every character in
|
||||
the string which is what other programming languages chose to do.
|
||||
- The returned tuple of ``system.instantiationInfo`` now has a third field
|
||||
containing the column of the instantiation.
|
||||
|
||||
- ``cookies.setCookie`` no longer assumes UTC for the expiration date.
|
||||
- ``strutils.formatEng`` does not distinguish between ``nil`` and ``""``
|
||||
strings anymore for its ``unit`` parameter. Instead the space is controlled
|
||||
by a new parameter ``useUnitSpace``.
|
||||
|
||||
- The ``times.parse`` and ``times.format`` procs have been rewritten.
|
||||
The proc signatures are the same so it should generally not break anything.
|
||||
However, the new implementation is a bit stricter, which is a breaking change.
|
||||
For example ``parse("2017-01-01 foo", "yyyy-MM-dd")`` will now raise an error.
|
||||
|
||||
- ``proc `-`*(a, b: Time): int64`` in the ``times`` module has changed return type
|
||||
to ``times.Duration`` in order to support higher time resolutions.
|
||||
The proc is no longer deprecated.
|
||||
|
||||
- The ``times.Timezone`` is now an immutable ref-type that must be initialized
|
||||
with an explicit constructor (``newTimezone``).
|
||||
|
||||
- ``posix.Timeval.tv_sec`` has changed type to ``posix.Time``.
|
||||
|
||||
- ``math.`mod` `` for floats now behaves the same as ``mod`` for integers
|
||||
(previously it used floor division like Python). Use ``math.floorMod`` for the old behavior.
|
||||
|
||||
- For string inputs, ``unicode.isUpper`` and ``unicode.isLower`` now require a
|
||||
second mandatory parameter ``skipNonAlpha``.
|
||||
|
||||
- For string inputs, ``strutils.isUpperAscii`` and ``strutils.isLowerAscii`` now
|
||||
require a second mandatory parameter ``skipNonAlpha``.
|
||||
|
||||
- ``osLastError`` is now marked with ``sideEffect``
|
||||
- The procs ``parseHexInt`` and ``parseOctInt`` now fail on empty strings
|
||||
and strings containing only valid prefixes, e.g. "0x" for hex integers.
|
||||
|
||||
- ``terminal.setCursorPos`` and ``terminal.setCursorXPos`` now work correctly
|
||||
with 0-based coordinates on POSIX (previously, you needed to use
|
||||
1-based coordinates on POSIX for correct behaviour; the Windows behaviour
|
||||
was always correct).
|
||||
|
||||
- ``lineInfoObj`` now returns absolute path instead of project path.
|
||||
It's used by ``lineInfo``, ``check``, ``expect``, ``require``, etc.
|
||||
|
||||
- ``net.sendTo`` no longer returns an int and now raises an ``OSError``.
|
||||
- `threadpool`'s `await` and derivatives have been renamed to `blockUntil`
|
||||
to avoid confusions with `await` from the `async` macro.
|
||||
|
||||
|
||||
#### Breaking changes in the compiler
|
||||
|
||||
- The undocumented ``#? braces`` parsing mode was removed.
|
||||
- The undocumented PHP backend was removed.
|
||||
- The default location of ``nimcache`` for the native code targets was
|
||||
changed. Read [the compiler user guide](https://nim-lang.org/docs/nimc.html#generated-c-code-directory)
|
||||
for more information.
|
||||
|
||||
### Library additions
|
||||
|
||||
- ``re.split`` now also supports the ``maxsplit`` parameter for consistency
|
||||
with ``strutils.split``.
|
||||
- Added ``system.toOpenArray`` in order to support zero-copy slicing
|
||||
operations. This is currently not yet available for the JavaScript target.
|
||||
- Added ``getCurrentDir``, ``findExe``, ``cpDir`` and ``mvDir`` procs to
|
||||
``nimscript``.
|
||||
- The ``times`` module now supports up to nanosecond time resolution when available.
|
||||
- Added the type ``times.Duration`` for representing fixed durations of time.
|
||||
- Added the proc ``times.convert`` for converting between different time units,
|
||||
e.g days to seconds.
|
||||
- Added the proc ``algorithm.binarySearch[T, K]`` with the ```cmp``` parameter.
|
||||
- Added the proc ``algorithm.upperBound``.
|
||||
- Added inverse hyperbolic functions, ``math.arcsinh``, ``math.arccosh`` and ``math.arctanh`` procs.
|
||||
- Added cotangent, secant and cosecant procs ``math.cot``, ``math.sec`` and ``math.csc``; and their hyperbolic, inverse and inverse hyperbolic functions, ``math.coth``, ``math.sech``, ``math.csch``, ``math.arccot``, ``math.arcsec``, ``math.arccsc``, ``math.arccoth``, ``math.arcsech`` and ``math.arccsch`` procs.
|
||||
- Added the procs ``math.floorMod`` and ``math.floorDiv`` for floor based integer division.
|
||||
- Added the procs ``rationals.`div```, ``rationals.`mod```, ``rationals.floorDiv`` and ``rationals.floorMod`` for rationals.
|
||||
- Added the proc ``math.prod`` for product of elements in openArray.
|
||||
- Added the proc ``parseBinInt`` to parse a binary integer from a string, which returns the value.
|
||||
- ``parseOct`` and ``parseBin`` in parseutils now also support the ``maxLen`` argument similar to ``parseHexInt``.
|
||||
- Added the proc ``flush`` for memory mapped files.
|
||||
- Added the ``MemMapFileStream``.
|
||||
- Added a simple interpreting event parser template ``eventParser`` to the ``pegs`` module.
|
||||
- Added ``macros.copyLineInfo`` to copy lineInfo from other node.
|
||||
- Added ``system.ashr`` an arithmetic right shift for integers.
|
||||
|
||||
### Library changes
|
||||
|
||||
- ``macros.astGenRepr``, ``macros.lispRepr`` and ``macros.treeRepr``
|
||||
now escapes the content of string literals consistently.
|
||||
- ``macros.NimSym`` and ``macros.NimIdent`` is now deprecated in favor
|
||||
of the more general ``NimNode``.
|
||||
- ``macros.getImpl`` now includes the pragmas of types, instead of omitting them.
|
||||
- ``macros.hasCustomPragma`` and ``macros.getCustomPragmaVal`` now
|
||||
also support ``ref`` and ``ptr`` types, pragmas on types and variant
|
||||
fields.
|
||||
- ``system.SomeReal`` is now called ``SomeFloat`` for consistency and
|
||||
correctness.
|
||||
- ``algorithm.smartBinarySearch`` and ``algorithm.binarySearch`` is
|
||||
now joined in ``binarySearch``. ``smartbinarySearch`` is now
|
||||
deprecated.
|
||||
- The `terminal` module now exports additional procs for generating ANSI color
|
||||
codes as strings.
|
||||
- Added the parameter ``val`` for the ``CritBitTree[int].inc`` proc.
|
||||
- An exception raised from a ``test`` block of ``unittest`` now shows its type in
|
||||
error message.
|
||||
- The ``compiler/nimeval`` API was rewritten to simplify the "compiler as an
|
||||
API". Using the Nim compiler and its VM as a scripting engine has never been
|
||||
easier. See ``tests/compilerapi/tcompilerapi.nim`` for an example of how to
|
||||
use the Nim VM in a native Nim application.
|
||||
- Added the parameter ``val`` for the ``CritBitTree[T].incl`` proc.
|
||||
- The proc ``tgamma`` was renamed to ``gamma``. ``tgamma`` is deprecated.
|
||||
- The ``pegs`` module now exports getters for the fields of its ``Peg`` and ``NonTerminal``
|
||||
object types. ``Peg``s with child nodes now have the standard ``items`` and ``pairs``
|
||||
iterators.
|
||||
- The ``accept`` socket procedure defined in the ``net`` module can now accept
|
||||
a nil socket.
|
||||
|
||||
### Language additions
|
||||
|
||||
- Dot calls combined with explicit generic instantiations can now be written
|
||||
as ``x.y[:z]`` which is transformed into ``y[z](x)`` by the parser.
|
||||
- ``func`` is now an alias for ``proc {.noSideEffect.}``.
|
||||
- In order to make ``for`` loops and iterators more flexible to use Nim now
|
||||
supports so called "for-loop macros". See
|
||||
the [manual](manual.html#macros-for-loop-macros) for more details.
|
||||
This feature enables a Python-like generic ``enumerate`` implementation.
|
||||
|
||||
- Case statements can now be rewritten via macros. See the [manual](manual.html#macros-case-statement-macros) for more information.
|
||||
This feature enables custom pattern matchers.
|
||||
|
||||
|
||||
- the `typedesc` special type has been renamed to just `type`.
|
||||
- `static` and `type` are now also modifiers similar to `ref` and `ptr`.
|
||||
They denote the special types `static[T]` and `type[T]`.
|
||||
- Forcing compile-time evaluation with `static` now supports specifying
|
||||
the desired target type (as a concrete type or as a type class)
|
||||
- The `type` operator now supports checking that the supplied expression
|
||||
matches an expected type constraint.
|
||||
|
||||
### Language changes
|
||||
|
||||
- The `importcpp` pragma now allows importing the listed fields of generic
|
||||
C++ types. Support for numeric parameters have also been added through
|
||||
the use of `static[T]` types.
|
||||
(#6415)
|
||||
|
||||
- Native C++ exceptions can now be imported with `importcpp` pragma.
|
||||
Imported exceptions can be raised and caught just like Nim exceptions.
|
||||
More details in language manual.
|
||||
|
||||
- ``nil`` for strings/seqs is finally gone. Instead the default value for
|
||||
these is ``"" / @[]``. Use ``--nilseqs:on`` for a transition period.
|
||||
|
||||
- Accessing the binary zero terminator in Nim's native strings
|
||||
is now invalid. Internally a Nim string still has the trailing zero for
|
||||
zero-copy interoperability with ``cstring``. Compile your code with the
|
||||
new switch ``--laxStrings:on`` if you need a transition period.
|
||||
|
||||
- The command syntax now supports keyword arguments after the first comma.
|
||||
|
||||
- Thread-local variables can now be declared inside procs. This implies all
|
||||
the effects of the ``global`` pragma.
|
||||
|
||||
- Nim now supports the ``except`` clause in the export statement.
|
||||
|
||||
- Range float types, example ``range[0.0 .. Inf]``. More details in language manual.
|
||||
- The ``{.this.}`` pragma has been deprecated. It never worked within generics and
|
||||
we found the resulting code harder to read than the more explicit ``obj.field``
|
||||
syntax.
|
||||
- "Memory regions" for pointer types have been deprecated, they were hardly used
|
||||
anywhere. Note that this has **nothing** to do with the ``--gc:regions`` switch
|
||||
of managing memory.
|
||||
|
||||
- The exception hierarchy was slightly reworked, ``SystemError`` was renamed to
|
||||
``CatchableError`` and is the new base class for any exception that is guaranteed to
|
||||
be catchable. This change should have minimal impact on most existing Nim code.
|
||||
|
||||
|
||||
### Tool changes
|
||||
|
||||
- ``jsondoc2`` has been renamed ``jsondoc``, similar to how ``doc2`` was renamed
|
||||
``doc``. The old ``jsondoc`` can still be invoked with ``jsondoc0``.
|
||||
|
||||
### Compiler changes
|
||||
|
||||
- The VM's instruction count limit was raised to 3 million instructions in
|
||||
order to support more complex computations at compile-time.
|
||||
|
||||
- Support for hot code reloading has been implemented for the JavaScript
|
||||
target. To use it, compile your code with `--hotCodeReloading:on` and use a
|
||||
helper library such as LiveReload or BrowserSync.
|
||||
|
||||
- A new compiler option `--cppCompileToNamespace` puts the generated C++ code
|
||||
into the namespace "Nim" in order to avoid naming conflicts with existing
|
||||
C++ code. This is done for all Nim code - internal and exported.
|
||||
|
||||
- Added ``macros.getProjectPath`` and ``ospaths.putEnv`` procs to Nim's virtual
|
||||
machine.
|
||||
|
||||
- The ``deadCodeElim`` option is now always turned on and the switch has no
|
||||
effect anymore, but is recognized for backwards compatibility.
|
||||
|
||||
- ``experimental`` is now a pragma / command line switch that can enable specific
|
||||
language extensions, it is not an all-or-nothing switch anymore.
|
||||
|
||||
- Nintendo Switch was added as a new platform target. See [the compiler user guide](https://nim-lang.org/docs/nimc.html)
|
||||
for more info.
|
||||
|
||||
- macros.bindSym now capable to accepts not only literal string or string constant expression.
|
||||
bindSym enhancement make it also can accepts computed string or ident node inside macros /
|
||||
compile time functions / static blocks. Only in templates / regular code it retains it's old behavior.
|
||||
This new feature can be accessed via {.experimental: "dynamicBindSym".} pragma/switch.
|
||||
|
||||
- On Posix systems the global system wide configuration is now put under ``/etc/nim/nim.cfg``,
|
||||
it used to be ``/etc/nim.cfg``. Usually it does not exist, however.
|
||||
|
||||
- On Posix systems the user configuration is now looked under ``$XDG_CONFIG_HOME/nim/nim.cfg``
|
||||
(if ``XDG_CONFIG_HOME`` is not defined, then under ``~/.config/nim/nim.cfg``). It used to be
|
||||
``$XDG_CONFIG_DIR/nim.cfg`` (and ``~/.config/nim.cfg``).
|
||||
|
||||
Similarly, on Windows, the user configuration is now looked under ``%APPDATA%/nim/nim.cfg``.
|
||||
This used to be ``%APPDATA%/nim.cfg``.
|
||||
|
||||
### Bugfixes
|
||||
27
changelogs/changelog_X_XX_X.md
Normal file
27
changelogs/changelog_X_XX_X.md
Normal file
@@ -0,0 +1,27 @@
|
||||
## v0.XX.0 - XX/XX/2018
|
||||
|
||||
### Changes affecting backwards compatibility
|
||||
|
||||
- Example item: ``Foo`` changed to ``Bar``.
|
||||
|
||||
#### Breaking changes in the standard library
|
||||
|
||||
|
||||
#### Breaking changes in the compiler
|
||||
|
||||
### Library additions
|
||||
|
||||
### Library changes
|
||||
|
||||
|
||||
### Language additions
|
||||
|
||||
|
||||
### Language changes
|
||||
|
||||
|
||||
### Tool changes
|
||||
|
||||
### Compiler changes
|
||||
|
||||
### Bugfixes
|
||||
61
changelogs/readme.md
Normal file
61
changelogs/readme.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# Update changelog
|
||||
After each release, call:
|
||||
```
|
||||
git mv changelog.md changelogs/changelog_0_19_0.md # use correct version
|
||||
cp changelogs/changelog_X_XX_X.md changelog.md
|
||||
git add changelog.md
|
||||
```
|
||||
|
||||
## Recent changelogs are saved here (with their git history)
|
||||
|
||||
## Older changelogs are stored in https://github.com/nim-lang/website
|
||||
|
||||
## source files:
|
||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2009-12-21-version-086-released.md
|
||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2010-03-14-version-088-released.md
|
||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2010-10-20-version-0810-released.md
|
||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2011-07-10-version-0812-released.md
|
||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2012-02-09-version-0814-released.md
|
||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2012-09-23-version-090-released.md
|
||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2013-05-20-version-092-released.md
|
||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2014-04-21-version-094-released.md
|
||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2014-10-19-version-096-released.md
|
||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2014-12-29-version-0102-released.md
|
||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2015-04-30-version-0110-released.md
|
||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2015-05-04-version-0112-released.md
|
||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2015-10-27-version-0120-released.md
|
||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2016-01-18-version-0130-released.md
|
||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2016-06-07-version-0140-released.md
|
||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2016-06-09-version-0142-released.md
|
||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2016-09-30-version-0150-released.md
|
||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2016-10-23-version-0152-released.md
|
||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2017-01-08-version-0160-released.md
|
||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2017-05-17-version-0170-released.md
|
||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2017-09-07-version-0172-released.md
|
||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2018-03-01-version-0180-released.md
|
||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2018-09-26-version-0190-released.md
|
||||
|
||||
## urls:
|
||||
* https://nim-lang.org/blog/2009/12/21/version-086-released.html
|
||||
* https://nim-lang.org/blog/2010/03/14/version-088-released.html
|
||||
* https://nim-lang.org/blog/2010/10/20/version-0810-released.html
|
||||
* https://nim-lang.org/blog/2011/07/10/version-0812-released.html
|
||||
* https://nim-lang.org/blog/2012/02/09/version-0814-released.html
|
||||
* https://nim-lang.org/blog/2012/09/23/version-090-released.html
|
||||
* https://nim-lang.org/blog/2013/05/20/version-092-released.html
|
||||
* https://nim-lang.org/blog/2014/04/21/version-094-released.html
|
||||
* https://nim-lang.org/blog/2014/10/19/version-096-released.html
|
||||
* https://nim-lang.org/blog/2014/12/29/version-0102-released.html
|
||||
* https://nim-lang.org/blog/2015/04/30/version-0110-released.html
|
||||
* https://nim-lang.org/blog/2015/05/04/version-0112-released.html
|
||||
* https://nim-lang.org/blog/2015/10/27/version-0120-released.html
|
||||
* https://nim-lang.org/blog/2016/01/18/version-0130-released.html
|
||||
* https://nim-lang.org/blog/2016/06/07/version-0140-released.html
|
||||
* https://nim-lang.org/blog/2016/06/09/version-0142-released.html
|
||||
* https://nim-lang.org/blog/2016/09/30/version-0150-released.html
|
||||
* https://nim-lang.org/blog/2016/10/23/version-0152-released.html
|
||||
* https://nim-lang.org/blog/2017/01/08/version-0160-released.html
|
||||
* https://nim-lang.org/blog/2017/05/17/version-0170-released.html
|
||||
* https://nim-lang.org/blog/2017/09/07/version-0172-released.html
|
||||
* https://nim-lang.org/blog/2018/03/01/version-0180-released.html
|
||||
* https://nim-lang.org/blog/2018/09/26/version-0190-released.html
|
||||
@@ -1,4 +1,4 @@
|
||||
REM - Run the full testsuite; tests\testament\tester all
|
||||
REM - Run the full testsuite; testament\tester all
|
||||
|
||||
REM - Uncomment the list of changes in news.txt
|
||||
REM - write a news ticker entry
|
||||
|
||||
@@ -31,10 +31,18 @@ proc intLiteral(i: BiggestInt): Rope =
|
||||
result = ~"(IL64(-9223372036854775807) - IL64(1))"
|
||||
|
||||
proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
|
||||
if ty == nil: internalError(p.config, n.info, "genLiteral: ty is nil")
|
||||
case n.kind
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
case skipTypes(ty, abstractVarRange).kind
|
||||
var k: TTypeKind
|
||||
if ty != nil:
|
||||
k = skipTypes(ty, abstractVarRange).kind
|
||||
else:
|
||||
case n.kind
|
||||
of nkCharLit: k = tyChar
|
||||
of nkUInt64Lit: k = tyUInt64
|
||||
of nkInt64Lit: k = tyInt64
|
||||
else: k = tyNil # don't go into the case variant that uses 'ty'
|
||||
case k
|
||||
of tyChar, tyNil:
|
||||
result = intLiteral(n.intVal)
|
||||
of tyBool:
|
||||
@@ -46,8 +54,8 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
|
||||
result = "(($1) $2)" % [getTypeDesc(p.module,
|
||||
ty), intLiteral(n.intVal)]
|
||||
of nkNilLit:
|
||||
let t = skipTypes(ty, abstractVarRange)
|
||||
if t.kind == tyProc and t.callConv == ccClosure:
|
||||
let k = if ty == nil: tyPointer else: skipTypes(ty, abstractVarRange).kind
|
||||
if k == tyProc and skipTypes(ty, abstractVarRange).callConv == ccClosure:
|
||||
let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels)
|
||||
result = p.module.tmpBase & rope(id)
|
||||
if id == p.module.labels:
|
||||
@@ -59,7 +67,9 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
|
||||
else:
|
||||
result = rope("NIM_NIL")
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
case skipTypes(ty, abstractVarRange + {tyStatic, tyUserTypeClass, tyUserTypeClassInst}).kind
|
||||
let k = if ty == nil: tyString
|
||||
else: skipTypes(ty, abstractVarRange + {tyStatic, tyUserTypeClass, tyUserTypeClassInst}).kind
|
||||
case k
|
||||
of tyNil:
|
||||
result = genNilStringLiteral(p.module, n.info)
|
||||
of tyString:
|
||||
@@ -1976,8 +1986,8 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
frmt = "$1 = $2->len;$n"
|
||||
lineCg(p, cpsStmts, frmt, tmp.r, rdLoc(a))
|
||||
putIntoDest(p, d, e, tmp.r)
|
||||
of mGCref: unaryStmt(p, e, d, "#nimGCref($1);$n")
|
||||
of mGCunref: unaryStmt(p, e, d, "#nimGCunref($1);$n")
|
||||
of mGCref: unaryStmt(p, e, d, "if ($1) { #nimGCref($1); }$n")
|
||||
of mGCunref: unaryStmt(p, e, d, "if ($1) { #nimGCunref($1); }$n")
|
||||
of mSetLengthStr: genSetLengthStr(p, e, d)
|
||||
of mSetLengthSeq: genSetLengthSeq(p, e, d)
|
||||
of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet,
|
||||
@@ -2394,15 +2404,8 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||
if ex.kind != nkEmpty:
|
||||
genLineDir(p, n)
|
||||
var a: TLoc
|
||||
if ex.kind in nkCallKinds and (ex[0].kind != nkSym or
|
||||
ex[0].sym.magic == mNone):
|
||||
# bug #6037: do not assign to a temp in C++ mode:
|
||||
incl a.flags, lfSingleUse
|
||||
genCall(p, ex, a)
|
||||
if lfSingleUse notin a.flags:
|
||||
line(p, cpsStmts, a.r & ";\L")
|
||||
else:
|
||||
initLocExpr(p, ex, a)
|
||||
initLocExprSingleUse(p, ex, a)
|
||||
line(p, cpsStmts, "(void)(" & a.r & ");\L")
|
||||
of nkAsmStmt: genAsmStmt(p, n)
|
||||
of nkTryStmt:
|
||||
if p.module.compileToCpp and optNoCppExceptions notin p.config.globalOptions:
|
||||
|
||||
@@ -307,11 +307,13 @@ proc genSingleVar(p: BProc, a: PNode) =
|
||||
|
||||
proc genClosureVar(p: BProc, a: PNode) =
|
||||
var immediateAsgn = a.sons[2].kind != nkEmpty
|
||||
var v: TLoc
|
||||
initLocExpr(p, a.sons[0], v)
|
||||
genLineDir(p, a)
|
||||
if immediateAsgn:
|
||||
var v: TLoc
|
||||
initLocExpr(p, a.sons[0], v)
|
||||
genLineDir(p, a)
|
||||
loadInto(p, a.sons[0], a.sons[2], v)
|
||||
else:
|
||||
constructLoc(p, v)
|
||||
|
||||
proc genVarStmt(p: BProc, n: PNode) =
|
||||
for it in n.sons:
|
||||
|
||||
@@ -968,9 +968,9 @@ proc genTypeInfoAux(m: BModule, typ, origType: PType, name: Rope;
|
||||
|
||||
proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): Rope =
|
||||
# bugfix: we need to search the type that contains the discriminator:
|
||||
var objtype = objtype
|
||||
var objtype = objtype.skipTypes(abstractPtrs)
|
||||
while lookupInRecord(objtype.n, d.name) == nil:
|
||||
objtype = objtype.sons[0]
|
||||
objtype = objtype.sons[0].skipTypes(abstractPtrs)
|
||||
if objtype.sym == nil:
|
||||
internalError(m.config, d.info, "anonymous obj with discriminator")
|
||||
result = "NimDT_$1_$2" % [rope($hashType(objtype)), rope(d.name.s.mangle)]
|
||||
|
||||
@@ -567,7 +567,12 @@ proc loadDynamicLib(m: BModule, lib: PLib) =
|
||||
var p = newProc(nil, m)
|
||||
p.options = p.options - {optStackTrace, optEndb}
|
||||
var dest: TLoc
|
||||
initLocExpr(p, lib.path, dest)
|
||||
initLoc(dest, locTemp, lib.path, OnStack)
|
||||
dest.r = getTempName(m)
|
||||
appcg(m, m.s[cfsDynLibInit],"$1 $2;$n",
|
||||
[getTypeDesc(m, lib.path.typ), rdLoc(dest)])
|
||||
expr(p, lib.path, dest)
|
||||
|
||||
add(m.s[cfsVars], p.s(cpsLocals))
|
||||
add(m.s[cfsDynLibInit], p.s(cpsInit))
|
||||
add(m.s[cfsDynLibInit], p.s(cpsStmts))
|
||||
@@ -1630,6 +1635,9 @@ proc cgenWriteModules*(backend: RootRef, config: ConfigRef) =
|
||||
# deps are allowed (and the system module is processed in the wrong
|
||||
# order anyway)
|
||||
g.config = config
|
||||
let (outDir, _, _) = splitFile(config.outfile)
|
||||
if not outDir.isEmpty:
|
||||
createDir(outDir)
|
||||
if g.generatedHeader != nil: finishModule(g.generatedHeader)
|
||||
while g.forwardedProcsCounter > 0:
|
||||
for m in cgenModules(g):
|
||||
|
||||
@@ -462,10 +462,17 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||
result.typ = n.typ
|
||||
|
||||
for i in 0 ..< n.len:
|
||||
if n[i].kind == nkStmtListExpr:
|
||||
case n[i].kind
|
||||
of nkExprColonExpr:
|
||||
if n[i][1].kind == nkStmtListExpr:
|
||||
let (st, ex) = exprToStmtList(n[i][1])
|
||||
result.add(st)
|
||||
n[i][1] = ex
|
||||
of nkStmtListExpr:
|
||||
let (st, ex) = exprToStmtList(n[i])
|
||||
result.add(st)
|
||||
n[i] = ex
|
||||
else: discard
|
||||
result.add(n)
|
||||
|
||||
of nkIfStmt, nkIfExpr:
|
||||
@@ -852,15 +859,8 @@ proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode
|
||||
discard
|
||||
|
||||
of nkStmtList, nkStmtListExpr:
|
||||
assert(isEmptyType(n.typ), "nkStmtListExpr not lowered")
|
||||
|
||||
result = addGotoOut(result, gotoOut)
|
||||
for i in 0 ..< n.len:
|
||||
if n[i].hasYieldsInExpressions:
|
||||
# Lower nkStmtListExpr nodes inside `n[i]` first
|
||||
var ns = false
|
||||
n[i] = ctx.lowerStmtListExprs(n[i], ns)
|
||||
|
||||
if n[i].hasYields:
|
||||
# Create a new split
|
||||
let go = newNodeI(nkGotoState, n[i].info)
|
||||
@@ -1009,7 +1009,7 @@ proc stateFromGotoState(n: PNode): int =
|
||||
assert(n.kind == nkGotoState)
|
||||
result = n[0].intVal.int
|
||||
|
||||
proc tranformStateAssignments(ctx: var Ctx, n: PNode): PNode =
|
||||
proc transformStateAssignments(ctx: var Ctx, n: PNode): PNode =
|
||||
# This transforms 3 patterns:
|
||||
########################## 1
|
||||
# yield e
|
||||
@@ -1051,7 +1051,7 @@ proc tranformStateAssignments(ctx: var Ctx, n: PNode): PNode =
|
||||
result.add(retStmt)
|
||||
else:
|
||||
for i in 0 ..< n.len:
|
||||
n[i] = ctx.tranformStateAssignments(n[i])
|
||||
n[i] = ctx.transformStateAssignments(n[i])
|
||||
|
||||
of nkSkip:
|
||||
discard
|
||||
@@ -1071,7 +1071,7 @@ proc tranformStateAssignments(ctx: var Ctx, n: PNode): PNode =
|
||||
|
||||
else:
|
||||
for i in 0 ..< n.len:
|
||||
n[i] = ctx.tranformStateAssignments(n[i])
|
||||
n[i] = ctx.transformStateAssignments(n[i])
|
||||
|
||||
proc skipStmtList(ctx: Ctx; n: PNode): PNode =
|
||||
result = n
|
||||
@@ -1220,18 +1220,20 @@ proc wrapIntoStateLoop(ctx: var Ctx, n: PNode): PNode =
|
||||
# while true:
|
||||
# block :stateLoop:
|
||||
# gotoState :state
|
||||
# local vars decl (if needed)
|
||||
# body # Might get wrapped in try-except
|
||||
let loopBody = newNodeI(nkStmtList, n.info)
|
||||
result = newTree(nkWhileStmt, newSymNode(ctx.g.getSysSym(n.info, "true")), loopBody)
|
||||
result.info = n.info
|
||||
|
||||
let localVars = newNodeI(nkStmtList, n.info)
|
||||
if not ctx.stateVarSym.isNil:
|
||||
let varSect = newNodeI(nkVarSection, n.info)
|
||||
addVar(varSect, newSymNode(ctx.stateVarSym))
|
||||
loopBody.add(varSect)
|
||||
localVars.add(varSect)
|
||||
|
||||
if not ctx.tempVars.isNil:
|
||||
loopBody.add(ctx.tempVars)
|
||||
localVars.add(ctx.tempVars)
|
||||
|
||||
let blockStmt = newNodeI(nkBlockStmt, n.info)
|
||||
blockStmt.add(newSymNode(ctx.stateLoopLabel))
|
||||
@@ -1240,7 +1242,7 @@ proc wrapIntoStateLoop(ctx: var Ctx, n: PNode): PNode =
|
||||
gs.add(ctx.newStateAccess())
|
||||
gs.add(ctx.g.newIntLit(n.info, ctx.states.len - 1))
|
||||
|
||||
var blockBody = newTree(nkStmtList, gs, n)
|
||||
var blockBody = newTree(nkStmtList, gs, localVars, n)
|
||||
if ctx.hasExceptions:
|
||||
blockBody = ctx.wrapIntoTryExcept(blockBody)
|
||||
|
||||
@@ -1292,13 +1294,18 @@ proc transformClosureIterator*(g: ModuleGraph; fn: PSym, n: PNode): PNode =
|
||||
# should folllow the same logic.
|
||||
ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), fn, fn.info)
|
||||
ctx.stateVarSym.typ = g.createClosureIterStateType(fn)
|
||||
|
||||
ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), fn, fn.info)
|
||||
let n = n.toStmtList
|
||||
var n = n.toStmtList
|
||||
|
||||
discard ctx.newState(n, nil)
|
||||
let gotoOut = newTree(nkGotoState, g.newIntLit(n.info, -1))
|
||||
|
||||
var ns = false
|
||||
n = ctx.lowerStmtListExprs(n, ns)
|
||||
|
||||
if n.hasYieldsInExpressions():
|
||||
internalError(ctx.g.config, "yield in expr not lowered")
|
||||
|
||||
# Splitting transformation
|
||||
discard ctx.transformClosureIteratorBody(n, gotoOut)
|
||||
|
||||
@@ -1314,7 +1321,7 @@ proc transformClosureIterator*(g: ModuleGraph; fn: PSym, n: PNode): PNode =
|
||||
result.add(s)
|
||||
result.add(body)
|
||||
|
||||
result = ctx.tranformStateAssignments(result)
|
||||
result = ctx.transformStateAssignments(result)
|
||||
result = ctx.wrapIntoStateLoop(result)
|
||||
|
||||
# echo "TRANSFORM TO STATES: "
|
||||
|
||||
@@ -769,6 +769,7 @@ proc processArgument*(pass: TCmdLinePass; p: OptParser;
|
||||
# nim filename.nims is the same as "nim e filename.nims":
|
||||
if p.key.endswith(".nims"):
|
||||
config.command = "e"
|
||||
incl(config.globalOptions, optWasNimscript)
|
||||
config.projectName = unixToNativePath(p.key)
|
||||
config.arguments = cmdLineRest(p)
|
||||
result = true
|
||||
|
||||
@@ -226,18 +226,30 @@ proc genComment(d: PDoc, n: PNode): string =
|
||||
toLinenumber(n.info), toColumn(n.info),
|
||||
dummyHasToc, d.options, d.conf), result)
|
||||
|
||||
proc genRecComment(d: PDoc, n: PNode): Rope =
|
||||
proc genRecCommentAux(d: PDoc, n: PNode): Rope =
|
||||
if n == nil: return nil
|
||||
result = genComment(d, n).rope
|
||||
if result == nil:
|
||||
if n.kind notin {nkEmpty..nkNilLit, nkEnumTy, nkTupleTy}:
|
||||
if n.kind in {nkStmtList, nkStmtListExpr, nkTypeDef, nkConstDef,
|
||||
nkObjectTy, nkRefTy, nkPtrTy, nkAsgn, nkFastAsgn}:
|
||||
# notin {nkEmpty..nkNilLit, nkEnumTy, nkTupleTy}:
|
||||
for i in countup(0, len(n)-1):
|
||||
result = genRecComment(d, n.sons[i])
|
||||
result = genRecCommentAux(d, n.sons[i])
|
||||
if result != nil: return
|
||||
else:
|
||||
when defined(nimNoNilSeqs): n.comment = ""
|
||||
else: n.comment = nil
|
||||
|
||||
proc genRecComment(d: PDoc, n: PNode): Rope =
|
||||
if n == nil: return nil
|
||||
result = genComment(d, n).rope
|
||||
if result == nil:
|
||||
if n.kind in {nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef,
|
||||
nkMacroDef, nkTemplateDef, nkConverterDef}:
|
||||
result = genRecCommentAux(d, n[bodyPos])
|
||||
else:
|
||||
result = genRecCommentAux(d, n)
|
||||
|
||||
proc getPlainDocstring(n: PNode): string =
|
||||
## Gets the plain text docstring of a node non destructively.
|
||||
##
|
||||
@@ -413,27 +425,6 @@ proc getAllRunnableExamplesRec(d: PDoc; n, orig: PNode; dest: var Rope) =
|
||||
proc getAllRunnableExamples(d: PDoc; n: PNode; dest: var Rope) =
|
||||
getAllRunnableExamplesRec(d, n, n, dest)
|
||||
|
||||
when false:
|
||||
proc findDocComment(n: PNode): PNode =
|
||||
if n == nil: return nil
|
||||
if not isNil(n.comment) and startsWith(n.comment, "##"): return n
|
||||
for i in countup(0, safeLen(n)-1):
|
||||
result = findDocComment(n.sons[i])
|
||||
if result != nil: return
|
||||
|
||||
proc extractDocComment*(s: PSym, d: PDoc): string =
|
||||
let n = findDocComment(s.ast)
|
||||
result = ""
|
||||
if not n.isNil:
|
||||
if not d.isNil:
|
||||
var dummyHasToc: bool
|
||||
renderRstToOut(d[], parseRst(n.comment, toFilename(d.conf, n.info),
|
||||
toLinenumber(n.info), toColumn(n.info),
|
||||
dummyHasToc, d.options + {roSkipPounds}),
|
||||
result)
|
||||
else:
|
||||
result = n.comment.substr(2).replace("\n##", "\n").strip
|
||||
|
||||
proc isVisible(d: PDoc; n: PNode): bool =
|
||||
result = false
|
||||
if n.kind == nkPostfix:
|
||||
@@ -723,7 +714,6 @@ proc exportSym(d: PDoc; s: PSym) =
|
||||
rope changeFileExt(external, "html")])
|
||||
|
||||
proc generateDoc*(d: PDoc, n, orig: PNode) =
|
||||
if orig.info.fileIndex != n.info.fileIndex: return
|
||||
case n.kind
|
||||
of nkCommentStmt: add(d.modDesc, genComment(d, n))
|
||||
of nkProcDef:
|
||||
@@ -771,13 +761,13 @@ proc generateDoc*(d: PDoc, n, orig: PNode) =
|
||||
proc add(d: PDoc; j: JsonNode) =
|
||||
if j != nil: d.jArray.add j
|
||||
|
||||
proc generateJson*(d: PDoc, n: PNode) =
|
||||
proc generateJson*(d: PDoc, n: PNode, includeComments: bool = true) =
|
||||
case n.kind
|
||||
of nkCommentStmt:
|
||||
if startsWith(n.comment, "##"):
|
||||
let stripped = n.comment.substr(2).strip
|
||||
d.add %{ "comment": %stripped, "line": %n.info.line.int,
|
||||
"col": %n.info.col }
|
||||
if includeComments:
|
||||
d.add %*{"comment": genComment(d, n)}
|
||||
else:
|
||||
add(d.modDesc, genComment(d, n))
|
||||
of nkProcDef:
|
||||
when useEffectSystem: documentRaises(d.cache, n)
|
||||
d.add genJsonItem(d, n, n.sons[namePos], skProc)
|
||||
@@ -805,11 +795,11 @@ proc generateJson*(d: PDoc, n: PNode) =
|
||||
succ(skType, ord(n.kind)-ord(nkTypeSection)))
|
||||
of nkStmtList:
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
generateJson(d, n.sons[i])
|
||||
generateJson(d, n.sons[i], includeComments)
|
||||
of nkWhenStmt:
|
||||
# generate documentation for the first branch only:
|
||||
if not checkForFalse(n.sons[0].sons[0]):
|
||||
generateJson(d, lastSon(n.sons[0]))
|
||||
generateJson(d, lastSon(n.sons[0]), includeComments)
|
||||
else: discard
|
||||
|
||||
proc genTagsItem(d: PDoc, n, nameNode: PNode, k: TSymKind): string =
|
||||
@@ -891,7 +881,7 @@ proc genOutFile(d: PDoc): Rope =
|
||||
setIndexTerm(d[], external, "", title)
|
||||
else:
|
||||
# Modules get an automatic title for the HTML, but no entry in the index.
|
||||
title = "Module " & extractFilename(changeFileExt(d.filename, ""))
|
||||
title = extractFilename(changeFileExt(d.filename, ""))
|
||||
|
||||
let bodyname = if d.hasToc and not d.isPureRst: "doc.body_toc_group"
|
||||
elif d.hasToc: "doc.body_toc"
|
||||
@@ -935,8 +925,12 @@ proc writeOutput*(d: PDoc, useWarning = false) =
|
||||
outfile.string)
|
||||
|
||||
proc writeOutputJson*(d: PDoc, useWarning = false) =
|
||||
var modDesc: string
|
||||
for desc in d.modDesc:
|
||||
modDesc &= desc
|
||||
let content = %*{"orig": d.filename,
|
||||
"nimble": getPackageName(d.conf, d.filename),
|
||||
"moduleDescription": modDesc,
|
||||
"entries": d.jArray}
|
||||
if optStdout in d.conf.globalOptions:
|
||||
write(stdout, $content)
|
||||
@@ -946,7 +940,9 @@ proc writeOutputJson*(d: PDoc, useWarning = false) =
|
||||
write(f, $content)
|
||||
close(f)
|
||||
else:
|
||||
discard "fixme: error report"
|
||||
localError(d.conf, newLineInfo(d.conf, AbsoluteFile d.filename, -1, -1),
|
||||
warnUser, "unable to open file \"" & d.destFile.string &
|
||||
"\" for writing")
|
||||
|
||||
proc commandDoc*(cache: IdentCache, conf: ConfigRef) =
|
||||
var ast = parseFile(conf.projectMainIdx, cache, conf)
|
||||
|
||||
@@ -55,20 +55,26 @@ proc processNodeJson(c: PPassContext, n: PNode): PNode =
|
||||
result = n
|
||||
var g = PGen(c)
|
||||
if shouldProcess(g):
|
||||
generateJson(g.doc, n)
|
||||
generateJson(g.doc, n, false)
|
||||
|
||||
proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
|
||||
template myOpenImpl(ext: untyped) {.dirty.} =
|
||||
var g: PGen
|
||||
new(g)
|
||||
g.module = module
|
||||
var d = newDocumentor(AbsoluteFile toFullPath(graph.config, FileIndex module.position),
|
||||
graph.cache, graph.config)
|
||||
graph.cache, graph.config, ext)
|
||||
d.hasToc = true
|
||||
g.doc = d
|
||||
result = g
|
||||
|
||||
proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
|
||||
myOpenImpl(HtmlExt)
|
||||
|
||||
proc myOpenJson(graph: ModuleGraph; module: PSym): PPassContext =
|
||||
myOpenImpl(JsonExt)
|
||||
|
||||
const docgen2Pass* = makePass(open = myOpen, process = processNode, close = close)
|
||||
const docgen2JsonPass* = makePass(open = myOpen, process = processNodeJson,
|
||||
const docgen2JsonPass* = makePass(open = myOpenJson, process = processNodeJson,
|
||||
close = closeJson)
|
||||
|
||||
proc finishDoc2Pass*(project: string) =
|
||||
|
||||
@@ -17,6 +17,7 @@ type
|
||||
TemplCtx = object
|
||||
owner, genSymOwner: PSym
|
||||
instLines: bool # use the instantiation lines numbers
|
||||
isDeclarative: bool
|
||||
mapping: TIdTable # every gensym'ed symbol needs to be mapped to some
|
||||
# new symbol
|
||||
config: ConfigRef
|
||||
@@ -54,11 +55,30 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
||||
result.add copyNode(c, templ, actual)
|
||||
of nkNone..nkIdent, nkType..nkNilLit: # atom
|
||||
result.add copyNode(c, templ, actual)
|
||||
of nkCommentStmt:
|
||||
# for the documentation generator we don't keep documentation comments
|
||||
# in the AST that would confuse it (bug #9432), but only if we are not in a
|
||||
# "declarative" context (bug #9235).
|
||||
if c.isDeclarative:
|
||||
var res = copyNode(c, templ, actual)
|
||||
for i in countup(0, sonsLen(templ) - 1):
|
||||
evalTemplateAux(templ.sons[i], actual, c, res)
|
||||
result.add res
|
||||
else:
|
||||
result.add newNodeI(nkEmpty, templ.info)
|
||||
else:
|
||||
var isDeclarative = false
|
||||
if templ.kind in {nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef,
|
||||
nkMacroDef, nkTemplateDef, nkConverterDef, nkTypeSection,
|
||||
nkVarSection, nkLetSection, nkConstSection} and
|
||||
not c.isDeclarative:
|
||||
c.isDeclarative = true
|
||||
isDeclarative = true
|
||||
var res = copyNode(c, templ, actual)
|
||||
for i in countup(0, sonsLen(templ) - 1):
|
||||
evalTemplateAux(templ.sons[i], actual, c, res)
|
||||
result.add res
|
||||
if isDeclarative: c.isDeclarative = false
|
||||
|
||||
const
|
||||
errWrongNumberOfArguments = "wrong number of arguments"
|
||||
|
||||
@@ -2282,6 +2282,9 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
|
||||
else: AbsoluteFile(getCurrentDir() / m.config.outFile.string)
|
||||
else:
|
||||
changeFileExt(completeCFilePath(m.config, AbsoluteFile f), ext)
|
||||
let (outDir, _, _) = splitFile(outfile)
|
||||
if not outDir.isEmpty:
|
||||
createDir(outDir)
|
||||
discard writeRopeIfNotEqual(genHeader() & code, outfile)
|
||||
for obj, content in items(globals.classes):
|
||||
genClass(m.config, obj, content, ext)
|
||||
|
||||
@@ -277,8 +277,9 @@ proc freshVarForClosureIter*(g: ModuleGraph; s, owner: PSym): PNode =
|
||||
proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) =
|
||||
let s = n.sym
|
||||
if illegalCapture(s):
|
||||
localError(g.config, n.info, "illegal capture '$1' of type <$2> which is declared here: $3" %
|
||||
[s.name.s, typeToString(s.typ), g.config$s.info])
|
||||
localError(g.config, n.info,
|
||||
("'$1' is of type <$2> which cannot be captured as it would violate memory" &
|
||||
" safety, declared here: $3") % [s.name.s, typeToString(s.typ), g.config$s.info])
|
||||
elif owner.typ.callConv notin {ccClosure, ccDefault}:
|
||||
localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" %
|
||||
[s.name.s, owner.name.s, CallingConvToStr[owner.typ.callConv]])
|
||||
|
||||
@@ -28,10 +28,10 @@ type
|
||||
config: ConfigRef
|
||||
fid: FileIndex
|
||||
lastTok: TTokType
|
||||
inquote: bool
|
||||
inquote, lastTokWasTerse: bool
|
||||
semicolons: SemicolonKind
|
||||
col, lastLineNumber, lineSpan, indentLevel, indWidth: int
|
||||
nested: int
|
||||
col, lastLineNumber, lineSpan, indentLevel, indWidth*: int
|
||||
keepIndents*: int
|
||||
doIndentMore*: int
|
||||
content: string
|
||||
indentStack: seq[int]
|
||||
@@ -41,9 +41,10 @@ type
|
||||
proc openEmitter*(em: var Emitter, cache: IdentCache;
|
||||
config: ConfigRef, fileIdx: FileIndex) =
|
||||
let fullPath = Absolutefile config.toFullPath(fileIdx)
|
||||
em.indWidth = getIndentWidth(fileIdx, llStreamOpen(fullPath, fmRead),
|
||||
cache, config)
|
||||
if em.indWidth == 0: em.indWidth = 2
|
||||
if em.indWidth == 0:
|
||||
em.indWidth = getIndentWidth(fileIdx, llStreamOpen(fullPath, fmRead),
|
||||
cache, config)
|
||||
if em.indWidth == 0: em.indWidth = 2
|
||||
em.config = config
|
||||
em.fid = fileIdx
|
||||
em.lastTok = tkInvalid
|
||||
@@ -52,11 +53,16 @@ proc openEmitter*(em: var Emitter, cache: IdentCache;
|
||||
em.content = newStringOfCap(16_000)
|
||||
em.indentStack = newSeqOfCap[int](30)
|
||||
em.indentStack.add 0
|
||||
em.lastLineNumber = 1
|
||||
|
||||
proc closeEmitter*(em: var Emitter) =
|
||||
if fileExists(em.config.outFile) and readFile(em.config.outFile.string) == em.content:
|
||||
discard "do nothing, see #9499"
|
||||
return
|
||||
var f = llStreamOpen(em.config.outFile, fmWrite)
|
||||
if f == nil:
|
||||
rawMessage(em.config, errGenerated, "cannot open file: " & em.config.outFile.string)
|
||||
return
|
||||
f.llStreamWrite em.content
|
||||
llStreamClose(f)
|
||||
|
||||
@@ -106,15 +112,16 @@ proc softLinebreak(em: var Emitter, lit: string) =
|
||||
for i in 1..em.indentLevel+moreIndent(em): wr(" ")
|
||||
else:
|
||||
# search backwards for a good split position:
|
||||
for a in em.altSplitPos:
|
||||
for a in mitems(em.altSplitPos):
|
||||
if a > em.fixedUntil:
|
||||
var spaces = 0
|
||||
while a+spaces < em.content.len and em.content[a+spaces] == ' ':
|
||||
inc spaces
|
||||
if spaces > 0: delete(em.content, a, a+spaces-1)
|
||||
let ws = "\L" & repeat(' ',em.indentLevel+moreIndent(em))
|
||||
em.col = em.content.len - a
|
||||
let ws = "\L" & repeat(' ', em.indentLevel+moreIndent(em))
|
||||
em.content.insert(ws, a)
|
||||
a = -1
|
||||
break
|
||||
|
||||
proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
|
||||
@@ -134,6 +141,22 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
|
||||
for i in 1 .. LineCommentColumn - em.col: wr(" ")
|
||||
wr lit
|
||||
|
||||
if tok.tokType == tkComment and tok.literal.startsWith("#!nimpretty"):
|
||||
case tok.literal
|
||||
of "#!nimpretty off":
|
||||
inc em.keepIndents
|
||||
wr("\L")
|
||||
em.lastLineNumber = tok.line + 1
|
||||
of "#!nimpretty on":
|
||||
dec em.keepIndents
|
||||
em.lastLineNumber = tok.line
|
||||
wr("\L")
|
||||
#for i in 1 .. tok.indent: wr " "
|
||||
wr tok.literal
|
||||
em.col = 0
|
||||
em.lineSpan = 0
|
||||
return
|
||||
|
||||
var preventComment = false
|
||||
if tok.tokType == tkComment and tok.line == em.lastLineNumber and tok.indent >= 0:
|
||||
# we have an inline comment so handle it before the indentation token:
|
||||
@@ -142,7 +165,7 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
|
||||
em.fixedUntil = em.content.high
|
||||
|
||||
elif tok.indent >= 0:
|
||||
if em.lastTok in (splitters + oprSet):
|
||||
if em.lastTok in (splitters + oprSet) or em.keepIndents > 0:
|
||||
em.indentLevel = tok.indent
|
||||
else:
|
||||
if tok.indent > em.indentStack[^1]:
|
||||
@@ -171,12 +194,13 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
|
||||
wr(" ")
|
||||
em.fixedUntil = em.content.high
|
||||
|
||||
var lastTokWasTerse = false
|
||||
case tok.tokType
|
||||
of tokKeywordLow..tokKeywordHigh:
|
||||
if endsInAlpha(em):
|
||||
wr(" ")
|
||||
elif not em.inquote and not endsInWhite(em) and
|
||||
em.lastTok notin openPars:
|
||||
em.lastTok notin openPars and not em.lastTokWasTerse:
|
||||
#and tok.tokType in oprSet
|
||||
wr(" ")
|
||||
|
||||
@@ -212,14 +236,19 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
|
||||
tkBracketDotRi,
|
||||
tkCurlyDotRi,
|
||||
tkParDotRi,
|
||||
tkColonColon, tkDot:
|
||||
tkColonColon:
|
||||
wr(TokTypeToStr[tok.tokType])
|
||||
of tkDot:
|
||||
lastTokWasTerse = true
|
||||
wr(TokTypeToStr[tok.tokType])
|
||||
of tkEquals:
|
||||
if not em.inquote and not em.endsInWhite: wr(" ")
|
||||
wr(TokTypeToStr[tok.tokType])
|
||||
if not em.inquote: wr(" ")
|
||||
of tkOpr, tkDotDot:
|
||||
if tok.strongSpaceA == 0 and tok.strongSpaceB == 0:
|
||||
if (tok.strongSpaceA == 0 and tok.strongSpaceB == 0) or em.inquote:
|
||||
# bug #9504: remember to not spacify a keyword:
|
||||
lastTokWasTerse = true
|
||||
# if not surrounded by whitespace, don't produce any whitespace either:
|
||||
wr(tok.ident.s)
|
||||
else:
|
||||
@@ -229,8 +258,8 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
|
||||
tok.strongSpaceB == 0 and tok.strongSpaceA > 0
|
||||
|
||||
if not isUnary(tok):
|
||||
wr(" ")
|
||||
rememberSplit(splitBinary)
|
||||
wr(" ")
|
||||
of tkAccent:
|
||||
if not em.inquote and endsInAlpha(em): wr(" ")
|
||||
wr(TokTypeToStr[tok.tokType])
|
||||
@@ -253,6 +282,7 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
|
||||
wr lit
|
||||
|
||||
em.lastTok = tok.tokType
|
||||
em.lastTokWasTerse = lastTokWasTerse
|
||||
em.lastLineNumber = tok.line + em.lineSpan
|
||||
em.lineSpan = 0
|
||||
|
||||
|
||||
@@ -224,7 +224,7 @@ proc openLexer*(lex: var TLexer, fileIdx: FileIndex, inputstream: PLLStream;
|
||||
cache: IdentCache; config: ConfigRef) =
|
||||
openBaseLexer(lex, inputstream)
|
||||
lex.fileIdx = fileidx
|
||||
lex.indentAhead = - 1
|
||||
lex.indentAhead = -1
|
||||
lex.currLineIndent = 0
|
||||
inc(lex.lineNumber, inputstream.lineOffset)
|
||||
lex.cache = cache
|
||||
@@ -740,11 +740,17 @@ proc handleCRLF(L: var TLexer, pos: int): int =
|
||||
result = nimlexbase.handleLF(L, pos)
|
||||
else: result = pos
|
||||
|
||||
proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
|
||||
type
|
||||
StringMode = enum
|
||||
normal,
|
||||
raw,
|
||||
generalized
|
||||
|
||||
proc getString(L: var TLexer, tok: var TToken, mode: StringMode) =
|
||||
var pos = L.bufpos
|
||||
var buf = L.buf # put `buf` in a register
|
||||
var line = L.lineNumber # save linenumber for better error message
|
||||
tokenBegin(tok, pos)
|
||||
tokenBegin(tok, pos - ord(mode == raw))
|
||||
inc pos # skip "
|
||||
if buf[pos] == '\"' and buf[pos+1] == '\"':
|
||||
tok.tokType = tkTripleStrLit # long string literal:
|
||||
@@ -784,12 +790,12 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
|
||||
inc(pos)
|
||||
else:
|
||||
# ordinary string literal
|
||||
if rawMode: tok.tokType = tkRStrLit
|
||||
if mode != normal: tok.tokType = tkRStrLit
|
||||
else: tok.tokType = tkStrLit
|
||||
while true:
|
||||
var c = buf[pos]
|
||||
if c == '\"':
|
||||
if rawMode and buf[pos+1] == '\"':
|
||||
if mode != normal and buf[pos+1] == '\"':
|
||||
inc(pos, 2)
|
||||
add(tok.literal, '"')
|
||||
else:
|
||||
@@ -800,7 +806,7 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
|
||||
tokenEndIgnore(tok, pos)
|
||||
lexMessage(L, errGenerated, "closing \" expected")
|
||||
break
|
||||
elif (c == '\\') and not rawMode:
|
||||
elif (c == '\\') and mode == normal:
|
||||
L.bufpos = pos
|
||||
getEscapedChar(L, tok)
|
||||
pos = L.bufpos
|
||||
@@ -1064,8 +1070,10 @@ proc skip(L: var TLexer, tok: var TToken) =
|
||||
tok.strongSpaceA = 0
|
||||
when defined(nimpretty):
|
||||
var hasComment = false
|
||||
var commentIndent = L.currLineIndent
|
||||
tok.commentOffsetA = L.offsetBase + pos
|
||||
tok.commentOffsetB = tok.commentOffsetA
|
||||
tok.line = -1
|
||||
while true:
|
||||
case buf[pos]
|
||||
of ' ':
|
||||
@@ -1076,9 +1084,6 @@ proc skip(L: var TLexer, tok: var TToken) =
|
||||
inc(pos)
|
||||
of CR, LF:
|
||||
tokenEndPrevious(tok, pos)
|
||||
when defined(nimpretty):
|
||||
# we are not yet in a comment, so update the comment token's line information:
|
||||
if not hasComment: inc tok.line
|
||||
pos = handleCRLF(L, pos)
|
||||
buf = L.buf
|
||||
var indent = 0
|
||||
@@ -1087,13 +1092,19 @@ proc skip(L: var TLexer, tok: var TToken) =
|
||||
inc(pos)
|
||||
inc(indent)
|
||||
elif buf[pos] == '#' and buf[pos+1] == '[':
|
||||
when defined(nimpretty): hasComment = true
|
||||
when defined(nimpretty):
|
||||
hasComment = true
|
||||
if tok.line < 0:
|
||||
tok.line = L.lineNumber
|
||||
commentIndent = indent
|
||||
skipMultiLineComment(L, tok, pos+2, false)
|
||||
pos = L.bufpos
|
||||
buf = L.buf
|
||||
else:
|
||||
break
|
||||
tok.strongSpaceA = 0
|
||||
when defined(nimpretty):
|
||||
if buf[pos] == '#' and tok.line < 0: commentIndent = indent
|
||||
if buf[pos] > ' ' and (buf[pos] != '#' or buf[pos+1] == '#'):
|
||||
tok.indent = indent
|
||||
L.currLineIndent = indent
|
||||
@@ -1101,14 +1112,20 @@ proc skip(L: var TLexer, tok: var TToken) =
|
||||
of '#':
|
||||
# do not skip documentation comment:
|
||||
if buf[pos+1] == '#': break
|
||||
when defined(nimpretty): hasComment = true
|
||||
when defined(nimpretty):
|
||||
hasComment = true
|
||||
if tok.line < 0:
|
||||
tok.line = L.lineNumber
|
||||
|
||||
if buf[pos+1] == '[':
|
||||
skipMultiLineComment(L, tok, pos+2, false)
|
||||
pos = L.bufpos
|
||||
buf = L.buf
|
||||
else:
|
||||
tokenBegin(tok, pos)
|
||||
while buf[pos] notin {CR, LF, nimlexbase.EndOfFile}: inc(pos)
|
||||
while buf[pos] notin {CR, LF, nimlexbase.EndOfFile}:
|
||||
when defined(nimpretty): tok.literal.add buf[pos]
|
||||
inc(pos)
|
||||
tokenEndIgnore(tok, pos+1)
|
||||
when defined(nimpretty):
|
||||
tok.commentOffsetB = L.offsetBase + pos + 1
|
||||
@@ -1120,6 +1137,7 @@ proc skip(L: var TLexer, tok: var TToken) =
|
||||
if hasComment:
|
||||
tok.commentOffsetB = L.offsetBase + pos - 1
|
||||
tok.tokType = tkComment
|
||||
tok.indent = commentIndent
|
||||
if gIndentationWidth <= 0:
|
||||
gIndentationWidth = tok.indent
|
||||
|
||||
@@ -1168,7 +1186,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
|
||||
of 'r', 'R':
|
||||
if L.buf[L.bufpos + 1] == '\"':
|
||||
inc(L.bufpos)
|
||||
getString(L, tok, true)
|
||||
getString(L, tok, raw)
|
||||
else:
|
||||
getSymbol(L, tok)
|
||||
of '(':
|
||||
@@ -1246,9 +1264,9 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
|
||||
lexMessage(L, errGenerated, "invalid token: " & c & " (\\" & $(ord(c)) & ')')
|
||||
of '\"':
|
||||
# check for generalized raw string literal:
|
||||
var rawMode = L.bufpos > 0 and L.buf[L.bufpos-1] in SymChars
|
||||
getString(L, tok, rawMode)
|
||||
if rawMode:
|
||||
let mode = if L.bufpos > 0 and L.buf[L.bufpos-1] in SymChars: generalized else: normal
|
||||
getString(L, tok, mode)
|
||||
if mode == generalized:
|
||||
# tkRStrLit -> tkGStrLit
|
||||
# tkTripleStrLit -> tkGTripleStrLit
|
||||
inc(tok.tokType, 2)
|
||||
|
||||
@@ -41,7 +41,7 @@ type
|
||||
hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
|
||||
hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
|
||||
hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath,
|
||||
hintConditionAlwaysTrue, hintName, hintPattern,
|
||||
hintConditionAlwaysTrue, hintConditionAlwaysFalse, hintName, hintPattern,
|
||||
hintExecuting, hintLinking, hintDependency,
|
||||
hintSource, hintPerformance, hintStackTrace, hintGCStats,
|
||||
hintGlobalVar,
|
||||
@@ -107,6 +107,7 @@ const
|
||||
hintConf: "used config file '$1'",
|
||||
hintPath: "added path: '$1'",
|
||||
hintConditionAlwaysTrue: "condition is always true: '$1'",
|
||||
hintConditionAlwaysFalse: "condition is always false: '$1'",
|
||||
hintName: "name should be: '$1'",
|
||||
hintPattern: "$1",
|
||||
hintExecuting: "$1",
|
||||
@@ -140,7 +141,7 @@ const
|
||||
"Success", "SuccessX", "CC", "LineTooLong",
|
||||
"XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
|
||||
"ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf",
|
||||
"Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency",
|
||||
"Path", "CondTrue", "CondFalse", "Name", "Pattern", "Exec", "Link", "Dependency",
|
||||
"Source", "Performance", "StackTrace", "GCStats", "GlobalVar",
|
||||
"User", "UserRaw", "ExtendedContext",
|
||||
]
|
||||
|
||||
@@ -265,6 +265,7 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||
conf.cmd = cmdInteractive
|
||||
commandInteractive(graph)
|
||||
of "e":
|
||||
incl conf.globalOptions, optWasNimscript
|
||||
commandEval(graph, mainCommandArg(conf))
|
||||
of "nop", "help":
|
||||
# prevent the "success" message:
|
||||
|
||||
@@ -190,10 +190,13 @@ template toFullPath*(conf: ConfigRef; info: TLineInfo): string =
|
||||
proc toMsgFilename*(conf: ConfigRef; info: TLineInfo): string =
|
||||
if info.fileIndex.int32 < 0:
|
||||
result = "???"
|
||||
elif optListFullPaths in conf.globalOptions:
|
||||
result = conf.m.fileInfos[info.fileIndex.int32].fullPath.string
|
||||
return
|
||||
let absPath = conf.m.fileInfos[info.fileIndex.int32].fullPath.string
|
||||
let relPath = conf.m.fileInfos[info.fileIndex.int32].projPath.string
|
||||
if optListFullPaths in conf.globalOptions:
|
||||
result = absPath
|
||||
else:
|
||||
result = conf.m.fileInfos[info.fileIndex.int32].projPath.string
|
||||
result = if absPath.len < relPath.len: absPath else: relPath
|
||||
|
||||
proc toLinenumber*(info: TLineInfo): int {.inline.} =
|
||||
result = int info.line
|
||||
|
||||
@@ -54,7 +54,8 @@ proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) =
|
||||
of cmdArgument:
|
||||
if processArgument(pass, p, argsCount, config): break
|
||||
if pass == passCmd2:
|
||||
if optRun notin config.globalOptions and config.arguments.len > 0 and config.command.normalize != "run":
|
||||
if {optRun, optWasNimscript} * config.globalOptions == {} and
|
||||
config.arguments.len > 0 and config.command.normalize notin ["run", "e"]:
|
||||
rawMessage(config, errGenerated, errArgsNeedRunOption)
|
||||
|
||||
proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
|
||||
|
||||
@@ -45,7 +45,7 @@ type # please make sure we have under 32 options
|
||||
TOptions* = set[TOption]
|
||||
TGlobalOption* = enum # **keep binary compatible**
|
||||
gloptNone, optForceFullMake,
|
||||
optDeadCodeElimUnused, # deprecated, always on
|
||||
optWasNimscript,
|
||||
optListCmd, optCompileOnly, optNoLinking,
|
||||
optCDebug, # turn on debugging information
|
||||
optGenDynLib, # generate a dynamic library
|
||||
|
||||
@@ -46,7 +46,7 @@ type
|
||||
inSemiStmtList*: int
|
||||
emptyNode: PNode
|
||||
when defined(nimpretty2):
|
||||
em: Emitter
|
||||
em*: Emitter
|
||||
|
||||
SymbolMode = enum
|
||||
smNormal, smAllowNil, smAfterDot
|
||||
@@ -286,7 +286,7 @@ proc checkBinary(p: TParser) {.inline.} =
|
||||
#|
|
||||
#| prefixOperator = operator
|
||||
#|
|
||||
#| optInd = COMMENT?
|
||||
#| optInd = COMMENT? IND?
|
||||
#| optPar = (IND{>} | IND{=})?
|
||||
#|
|
||||
#| simpleExpr = arrowExpr (OP0 optInd arrowExpr)* pragma?
|
||||
@@ -1037,6 +1037,7 @@ proc parseParamList(p: var TParser, retColon = true): PNode =
|
||||
addSon(result, p.emptyNode) # return type
|
||||
when defined(nimpretty2):
|
||||
inc p.em.doIndentMore
|
||||
inc p.em.keepIndents
|
||||
let hasParLe = p.tok.tokType == tkParLe and p.tok.indent < 0
|
||||
if hasParLe:
|
||||
getTok(p)
|
||||
@@ -1073,6 +1074,7 @@ proc parseParamList(p: var TParser, retColon = true): PNode =
|
||||
result = p.emptyNode
|
||||
when defined(nimpretty2):
|
||||
dec p.em.doIndentMore
|
||||
dec p.em.keepIndents
|
||||
|
||||
proc optPragmas(p: var TParser): PNode =
|
||||
if p.tok.tokType == tkCurlyDotLe and (p.tok.indent < 0 or realInd(p)):
|
||||
@@ -1578,7 +1580,6 @@ proc parseTry(p: var TParser; isExpr: bool): PNode =
|
||||
colcom(p, b)
|
||||
addSon(b, parseStmt(p))
|
||||
addSon(result, b)
|
||||
if b.kind == nkFinally: break
|
||||
if b == nil: parMessage(p, "expected 'except'")
|
||||
|
||||
proc parseExceptBlock(p: var TParser, kind: TNodeKind): PNode =
|
||||
|
||||
@@ -99,24 +99,26 @@ proc isDotDot(x: string; bounds: (int, int)): bool =
|
||||
proc isSlash(x: string; bounds: (int, int)): bool =
|
||||
bounds[1] == bounds[0] and x[bounds[0]] in {DirSep, AltSep}
|
||||
|
||||
const canonDirSep = when isMainModule: '/' else: DirSep
|
||||
|
||||
proc canon(x: string; result: var string; state: var int) =
|
||||
# state: 0th bit set if isAbsolute path. Other bits count
|
||||
# the number of path components.
|
||||
for b in dirs(x):
|
||||
if (state shr 1 == 0) and isSlash(x, b):
|
||||
result.add DirSep
|
||||
result.add canonDirSep
|
||||
state = state or 1
|
||||
elif result.len > (state and 1) and isDotDot(x, b):
|
||||
var d = result.len
|
||||
# f/..
|
||||
while d > (state and 1) and result[d-1] != DirSep:
|
||||
while (d-1) > (state and 1) and result[d-1] notin {DirSep, AltSep}:
|
||||
dec d
|
||||
if d > 0: setLen(result, d-1)
|
||||
elif isDot(x, b):
|
||||
discard "discard the dot"
|
||||
elif b[1] >= b[0]:
|
||||
if result.len > 0 and result[^1] != DirSep:
|
||||
result.add DirSep
|
||||
if result.len > 0 and result[^1] notin {DirSep, AltSep}:
|
||||
result.add canonDirSep
|
||||
result.add substr(x, b[0], b[1])
|
||||
inc state, 2
|
||||
|
||||
@@ -133,7 +135,7 @@ when FileSystemCaseSensitive:
|
||||
else:
|
||||
template `!=?`(a, b: char): bool = a != b
|
||||
|
||||
proc relativeTo(full, base: string; sep = DirSep): string =
|
||||
proc relativeTo(full, base: string; sep = canonDirSep): string =
|
||||
if full.len == 0: return ""
|
||||
var f, b: PathIter
|
||||
var ff = (0, -1)
|
||||
@@ -207,7 +209,7 @@ when true:
|
||||
canon(f.string, result.string, state)
|
||||
|
||||
proc relativeTo*(fullPath: AbsoluteFile, baseFilename: AbsoluteDir;
|
||||
sep = DirSep): RelativeFile =
|
||||
sep = canonDirSep): RelativeFile =
|
||||
RelativeFile(relativeTo(fullPath.string, baseFilename.string, sep))
|
||||
|
||||
proc toAbsolute*(file: string; base: AbsoluteDir): AbsoluteFile =
|
||||
@@ -222,7 +224,7 @@ when true:
|
||||
|
||||
proc writeFile*(x: AbsoluteFile; content: string) {.borrow.}
|
||||
|
||||
when isMainModule and defined(posix):
|
||||
when isMainModule:
|
||||
doAssert canon"/foo/../bar" == "/bar"
|
||||
doAssert canon"foo/../bar" == "bar"
|
||||
|
||||
@@ -257,4 +259,4 @@ when isMainModule and defined(posix):
|
||||
|
||||
when isMainModule and defined(windows):
|
||||
let nasty = string(AbsoluteDir(r"C:\Users\rumpf\projects\nim\tests\nimble\nimbleDir\linkedPkgs\pkgB-#head\../../simplePkgs/pkgB-#head/") / RelativeFile"pkgA/module.nim")
|
||||
doAssert nasty == r"C:\Users\rumpf\projects\nim\tests\nimble\nimbleDir\simplePkgs\pkgB-#head\pkgA\module.nim"
|
||||
doAssert nasty.replace('/', '\\') == r"C:\Users\rumpf\projects\nim\tests\nimble\nimbleDir\simplePkgs\pkgB-#head\pkgA\module.nim"
|
||||
|
||||
@@ -1104,10 +1104,19 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
else: sym.flags.incl sfUsed
|
||||
of wLiftLocals: discard
|
||||
else: invalidPragma(c, it)
|
||||
elif sym.kind in {skVar,skLet,skParam,skField,skProc,skFunc,skConverter,skMethod,skType}:
|
||||
elif sym != nil and sym.kind in {skVar, skLet, skParam, skField, skProc,
|
||||
skFunc, skConverter, skMethod, skType}:
|
||||
n.sons[i] = semCustomPragma(c, it)
|
||||
else:
|
||||
elif sym != nil:
|
||||
illegalCustomPragma(c, it, sym)
|
||||
else:
|
||||
invalidPragma(c, it)
|
||||
|
||||
proc mergePragmas(n, pragmas: PNode) =
|
||||
if n[pragmasPos].kind == nkEmpty:
|
||||
n[pragmasPos] = pragmas
|
||||
else:
|
||||
for p in pragmas: n.sons[pragmasPos].add p
|
||||
|
||||
proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
|
||||
validPragmas: TSpecialWords) =
|
||||
@@ -1117,11 +1126,12 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
|
||||
if not o.isNil:
|
||||
pushInfoContext(c.config, n.info)
|
||||
var i = 0
|
||||
while i < o.len():
|
||||
while i < o.len:
|
||||
if singlePragma(c, sym, o, i, validPragmas):
|
||||
internalError(c.config, n.info, "implicitPragmas")
|
||||
inc i
|
||||
popInfoContext(c.config)
|
||||
if sym.kind in routineKinds: mergePragmas(sym.ast, o)
|
||||
|
||||
if lfExportLib in sym.loc.flags and sfExportc notin sym.flags:
|
||||
localError(c.config, n.info, ".dynlib requires .exportc")
|
||||
|
||||
@@ -853,12 +853,10 @@ proc loadNode*(g: ModuleGraph; module: PSym): PNode =
|
||||
result = newNodeI(nkStmtList, module.info)
|
||||
for row in db.rows(sql"select data from toplevelstmts where module = ? order by position asc",
|
||||
abs module.id):
|
||||
|
||||
var b = BlobReader(pos: 0)
|
||||
# ensure we can read without index checks:
|
||||
b.s = row[0] & '\0'
|
||||
result.add decodeNode(g, b, module.info)
|
||||
|
||||
db.exec(sql"insert into controlblock(idgen) values (?)", gFrontEndId)
|
||||
replay(g, module, result)
|
||||
|
||||
|
||||
@@ -117,6 +117,7 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
|
||||
cbconf setCommand:
|
||||
conf.command = a.getString 0
|
||||
let arg = a.getString 1
|
||||
incl(conf.globalOptions, optWasNimscript)
|
||||
if arg.len > 0:
|
||||
conf.projectName = arg
|
||||
let path =
|
||||
@@ -159,11 +160,8 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
|
||||
|
||||
defineSymbol(conf.symbols, "nimscript")
|
||||
defineSymbol(conf.symbols, "nimconfig")
|
||||
var registeredPasses {.global.} = false
|
||||
if not registeredPasses:
|
||||
registerPass(graph, semPass)
|
||||
registerPass(graph, evalPass)
|
||||
registeredPasses = true
|
||||
registerPass(graph, semPass)
|
||||
registerPass(graph, evalPass)
|
||||
|
||||
conf.searchPaths.add(conf.libpath)
|
||||
|
||||
|
||||
@@ -379,7 +379,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
|
||||
result = newStrNodeT(s, n, g)
|
||||
else:
|
||||
result = newStrNodeT(getStrOrChar(a), n, g)
|
||||
of mStrToStr: result = a
|
||||
of mStrToStr: result = newStrNodeT(getStrOrChar(a), n, g)
|
||||
of mEnumToStr: result = newStrNodeT(ordinalValToString(a, g), n, g)
|
||||
of mArrToSeq:
|
||||
result = copyTree(a)
|
||||
|
||||
@@ -299,7 +299,13 @@ proc semOf(c: PContext, n: PNode): PNode =
|
||||
result.typ = getSysType(c.graph, n.info, tyBool)
|
||||
return result
|
||||
elif diff == high(int):
|
||||
localError(c.config, n.info, "'$1' cannot be of this subtype" % typeToString(a))
|
||||
if commonSuperclass(a, b) == nil:
|
||||
localError(c.config, n.info, "'$1' cannot be of this subtype" % typeToString(a))
|
||||
else:
|
||||
message(c.config, n.info, hintConditionAlwaysFalse, renderTree(n))
|
||||
result = newIntNode(nkIntLit, 0)
|
||||
result.info = n.info
|
||||
result.typ = getSysType(c.graph, n.info, tyBool)
|
||||
else:
|
||||
localError(c.config, n.info, "'of' takes 2 arguments")
|
||||
n.typ = getSysType(c.graph, n.info, tyBool)
|
||||
|
||||
@@ -207,6 +207,8 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
typ = commonType(typ, n[0].typ)
|
||||
|
||||
var last = sonsLen(n) - 1
|
||||
var catchAllExcepts = 0
|
||||
|
||||
for i in countup(1, last):
|
||||
let a = n.sons[i]
|
||||
checkMinSonsLen(a, 1, c.config)
|
||||
@@ -227,8 +229,16 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
# Overwrite symbol in AST with the symbol in the symbol table.
|
||||
a[0][2] = newSymNode(symbol, a[0][2].info)
|
||||
|
||||
elif a.len == 1:
|
||||
# count number of ``except: body`` blocks
|
||||
inc catchAllExcepts
|
||||
|
||||
else:
|
||||
# support ``except KeyError, ValueError, ... : body``
|
||||
if catchAllExcepts > 0:
|
||||
# if ``except: body`` already encountered,
|
||||
# cannot be followed by a ``except KeyError, ... : body`` block
|
||||
inc catchAllExcepts
|
||||
var is_native, is_imported: bool
|
||||
for j in 0..a.len-2:
|
||||
let tmp = semExceptBranchType(a[j])
|
||||
@@ -238,9 +248,18 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
if is_native and is_imported:
|
||||
localError(c.config, a[0].info, "Mix of imported and native exception types is not allowed in one except branch")
|
||||
|
||||
elif a.kind != nkFinally:
|
||||
elif a.kind == nkFinally:
|
||||
if i != n.len-1:
|
||||
localError(c.config, a.info, "Only one finally is allowed after all other branches")
|
||||
|
||||
else:
|
||||
illFormedAst(n, c.config)
|
||||
|
||||
if catchAllExcepts > 1:
|
||||
# if number of ``except: body`` blocks is greater than 1
|
||||
# or more specific exception follows a general except block, it is invalid
|
||||
localError(c.config, a.info, "Only one general except clause is allowed after more specific exceptions")
|
||||
|
||||
# last child of an nkExcept/nkFinally branch is a statement:
|
||||
a[^1] = semExprBranchScope(c, a[^1])
|
||||
if a.kind != nkFinally: typ = commonType(typ, a[^1])
|
||||
@@ -1917,7 +1936,8 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
case n.sons[j].kind
|
||||
of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr,
|
||||
nkBlockStmt, nkState: discard
|
||||
else: localError(c.config, n.sons[j].info, "unreachable statement after 'return'")
|
||||
else: localError(c.config, n.sons[j].info,
|
||||
"unreachable statement after 'return' statement or '{.noReturn.}' proc")
|
||||
else: discard
|
||||
|
||||
if result.len == 1 and
|
||||
|
||||
@@ -1292,7 +1292,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
|
||||
of tyDistinct:
|
||||
if a.kind == tyDistinct:
|
||||
if sameDistinctTypes(f, a): result = isEqual
|
||||
elif f.base.kind == tyAnything: result = isGeneric
|
||||
#elif f.base.kind == tyAnything: result = isGeneric # issue 4435
|
||||
elif c.coerceDistincts: result = typeRel(c, f.base, a)
|
||||
elif a.kind == tyNil and f.base.kind in NilableTypes:
|
||||
result = f.allowsNil
|
||||
@@ -1911,10 +1911,13 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
|
||||
else:
|
||||
var evaluated = c.semTryConstExpr(c, arg)
|
||||
if evaluated != nil:
|
||||
arg.typ = newTypeS(tyStatic, c)
|
||||
arg.typ.sons = @[evaluated.typ]
|
||||
arg.typ.n = evaluated
|
||||
a = arg.typ
|
||||
# Don't build the type in-place because `evaluated` and `arg` may point
|
||||
# to the same object and we'd end up creating recursive types (#9255)
|
||||
let typ = newTypeS(tyStatic, c)
|
||||
typ.sons = @[evaluated.typ]
|
||||
typ.n = evaluated
|
||||
arg.typ = typ
|
||||
a = typ
|
||||
else:
|
||||
if m.callee.kind == tyGenericBody:
|
||||
if f.kind == tyStatic and typeRel(m, f.base, a) != isNone:
|
||||
|
||||
@@ -72,12 +72,16 @@ proc parsePipe(filename: AbsoluteFile, inputStream: PLLStream; cache: IdentCache
|
||||
i = 0
|
||||
inc linenumber
|
||||
if i+1 < line.len and line[i] == '#' and line[i+1] == '?':
|
||||
inc(i, 2)
|
||||
while i < line.len and line[i] in Whitespace: inc(i)
|
||||
var q: TParser
|
||||
parser.openParser(q, filename, llStreamOpen(substr(line, i)), cache, config)
|
||||
result = parser.parseAll(q)
|
||||
parser.closeParser(q)
|
||||
when defined(nimpretty2):
|
||||
# XXX this is a bit hacky, but oh well...
|
||||
quit "can't nimpretty a source code filter"
|
||||
else:
|
||||
inc(i, 2)
|
||||
while i < line.len and line[i] in Whitespace: inc(i)
|
||||
var q: TParser
|
||||
parser.openParser(q, filename, llStreamOpen(substr(line, i)), cache, config)
|
||||
result = parser.parseAll(q)
|
||||
parser.closeParser(q)
|
||||
llStreamClose(s)
|
||||
|
||||
proc getFilter(ident: PIdent): TFilterKind =
|
||||
@@ -156,14 +160,18 @@ proc openParsers*(p: var TParsers, fileIdx: FileIndex, inputstream: PLLStream;
|
||||
proc closeParsers*(p: var TParsers) =
|
||||
parser.closeParser(p.parser)
|
||||
|
||||
proc parseFile*(fileIdx: FileIndex; cache: IdentCache; config: ConfigRef): PNode {.procvar.} =
|
||||
var
|
||||
p: TParsers
|
||||
f: File
|
||||
proc setupParsers*(p: var TParsers; fileIdx: FileIndex; cache: IdentCache;
|
||||
config: ConfigRef): bool =
|
||||
var f: File
|
||||
let filename = toFullPathConsiderDirty(config, fileIdx)
|
||||
if not open(f, filename.string):
|
||||
rawMessage(config, errGenerated, "cannot open file: " & filename.string)
|
||||
return
|
||||
return false
|
||||
openParsers(p, fileIdx, llStreamOpen(f), cache, config)
|
||||
result = parseAll(p)
|
||||
closeParsers(p)
|
||||
result = true
|
||||
|
||||
proc parseFile*(fileIdx: FileIndex; cache: IdentCache; config: ConfigRef): PNode {.procvar.} =
|
||||
var p: TParsers
|
||||
if setupParsers(p, fileIdx, cache, config):
|
||||
result = parseAll(p)
|
||||
closeParsers(p)
|
||||
|
||||
@@ -110,8 +110,8 @@ proc transformSons(c: PTransf, n: PNode): PTransNode =
|
||||
for i in countup(0, sonsLen(n)-1):
|
||||
result[i] = transform(c, n.sons[i])
|
||||
|
||||
proc newAsgnStmt(c: PTransf, le: PNode, ri: PTransNode): PTransNode =
|
||||
result = newTransNode(nkFastAsgn, PNode(ri).info, 2)
|
||||
proc newAsgnStmt(c: PTransf, kind: TNodeKind, le: PNode, ri: PTransNode): PTransNode =
|
||||
result = newTransNode(kind, PNode(ri).info, 2)
|
||||
result[0] = PTransNode(le)
|
||||
result[1] = ri
|
||||
|
||||
@@ -299,12 +299,6 @@ proc transformBreak(c: PTransf, n: PNode): PTransNode =
|
||||
else:
|
||||
result = n.PTransNode
|
||||
|
||||
proc unpackTuple(c: PTransf, n: PNode, father: PTransNode) =
|
||||
# XXX: BUG: what if `n` is an expression with side-effects?
|
||||
for i in countup(0, sonsLen(c.transCon.forStmt) - 3):
|
||||
add(father, newAsgnStmt(c, c.transCon.forStmt.sons[i],
|
||||
transform(c, newTupleAccess(c.graph, n, i))))
|
||||
|
||||
proc introduceNewLocalVars(c: PTransf, n: PNode): PTransNode =
|
||||
case n.kind
|
||||
of nkSym:
|
||||
@@ -328,6 +322,18 @@ proc introduceNewLocalVars(c: PTransf, n: PNode): PTransNode =
|
||||
result[i] = introduceNewLocalVars(c, n.sons[i])
|
||||
|
||||
proc transformYield(c: PTransf, n: PNode): PTransNode =
|
||||
proc asgnTo(lhs: PNode, rhs: PTransNode): PTransNode =
|
||||
# Choose the right assignment instruction according to the given ``lhs``
|
||||
# node since it may not be a nkSym (a stack-allocated skForVar) but a
|
||||
# nkDotExpr (a heap-allocated slot into the envP block)
|
||||
case lhs.kind:
|
||||
of nkSym:
|
||||
internalAssert c.graph.config, lhs.sym.kind == skForVar
|
||||
result = newAsgnStmt(c, nkFastAsgn, lhs, rhs)
|
||||
of nkDotExpr:
|
||||
result = newAsgnStmt(c, nkAsgn, lhs, rhs)
|
||||
else:
|
||||
internalAssert c.graph.config, false
|
||||
result = newTransNode(nkStmtList, n.info, 0)
|
||||
var e = n.sons[0]
|
||||
# c.transCon.forStmt.len == 3 means that there is one for loop variable
|
||||
@@ -340,13 +346,20 @@ proc transformYield(c: PTransf, n: PNode): PTransNode =
|
||||
for i in countup(0, sonsLen(e) - 1):
|
||||
var v = e.sons[i]
|
||||
if v.kind == nkExprColonExpr: v = v.sons[1]
|
||||
add(result, newAsgnStmt(c, c.transCon.forStmt.sons[i],
|
||||
transform(c, v)))
|
||||
let lhs = c.transCon.forStmt.sons[i]
|
||||
let rhs = transform(c, v)
|
||||
add(result, asgnTo(lhs, rhs))
|
||||
else:
|
||||
unpackTuple(c, e, result)
|
||||
# Unpack the tuple into the loop variables
|
||||
# XXX: BUG: what if `n` is an expression with side-effects?
|
||||
for i in countup(0, sonsLen(c.transCon.forStmt) - 3):
|
||||
let lhs = c.transCon.forStmt.sons[i]
|
||||
let rhs = transform(c, newTupleAccess(c.graph, e, i))
|
||||
add(result, asgnTo(lhs, rhs))
|
||||
else:
|
||||
var x = transform(c, e)
|
||||
add(result, newAsgnStmt(c, c.transCon.forStmt.sons[0], x))
|
||||
let lhs = c.transCon.forStmt.sons[0]
|
||||
let rhs = transform(c, e)
|
||||
add(result, asgnTo(lhs, rhs))
|
||||
|
||||
inc(c.transCon.yieldStmts)
|
||||
if c.transCon.yieldStmts <= 1:
|
||||
@@ -584,7 +597,7 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
|
||||
# generate a temporary and produce an assignment statement:
|
||||
var temp = newTemp(c, formal.typ, formal.info)
|
||||
addVar(v, temp)
|
||||
add(stmtList, newAsgnStmt(c, temp, arg.PTransNode))
|
||||
add(stmtList, newAsgnStmt(c, nkFastAsgn, temp, arg.PTransNode))
|
||||
idNodeTablePut(newC.mapping, formal, temp)
|
||||
of paVarAsgn:
|
||||
assert(skipTypes(formal.typ, abstractInst).kind == tyVar)
|
||||
@@ -595,7 +608,7 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
|
||||
addSonSkipIntLit(typ, formal.typ.sons[0])
|
||||
var temp = newTemp(c, typ, formal.info)
|
||||
addVar(v, temp)
|
||||
add(stmtList, newAsgnStmt(c, temp, arg.PTransNode))
|
||||
add(stmtList, newAsgnStmt(c, nkFastAsgn, temp, arg.PTransNode))
|
||||
idNodeTablePut(newC.mapping, formal, temp)
|
||||
|
||||
var body = iter.getBody.copyTree
|
||||
|
||||
@@ -92,8 +92,7 @@ proc isCaseObj*(n: PNode): bool =
|
||||
|
||||
proc isDeepConstExpr*(n: PNode): bool =
|
||||
case n.kind
|
||||
of nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
|
||||
nkFloatLit..nkFloat64Lit, nkNilLit:
|
||||
of nkCharLit..nkNilLit:
|
||||
result = true
|
||||
of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv:
|
||||
result = isDeepConstExpr(n.sons[1])
|
||||
|
||||
@@ -1079,8 +1079,8 @@ proc inheritanceDiff*(a, b: PType): int =
|
||||
# | returns: +x iff `a` is the x'th direct subclass of `b`
|
||||
# | returns: `maxint` iff `a` and `b` are not compatible at all
|
||||
if a == b or a.kind == tyError or b.kind == tyError: return 0
|
||||
assert a.kind == tyObject
|
||||
assert b.kind == tyObject
|
||||
assert a.kind in {tyObject} + skipPtrs
|
||||
assert b.kind in {tyObject} + skipPtrs
|
||||
var x = a
|
||||
result = 0
|
||||
while x != nil:
|
||||
|
||||
@@ -413,26 +413,12 @@ proc recSetFlagIsRef(arg: PNode) =
|
||||
arg.sons[i].recSetFlagIsRef
|
||||
|
||||
proc setLenSeq(c: PCtx; node: PNode; newLen: int; info: TLineInfo) =
|
||||
# FIXME: this doesn't attempt to solve incomplete
|
||||
# support of tyPtr, tyRef in VM.
|
||||
let typ = node.typ.skipTypes(abstractInst+{tyRange}-{tyTypeDesc})
|
||||
let typeEntry = typ.sons[0].skipTypes(abstractInst+{tyRange}-{tyTypeDesc})
|
||||
let typeKind = case typeEntry.kind
|
||||
of tyUInt..tyUInt64: nkUIntLit
|
||||
of tyRange, tyEnum, tyBool, tyChar, tyInt..tyInt64: nkIntLit
|
||||
of tyFloat..tyFloat128: nkFloatLit
|
||||
of tyString: nkStrLit
|
||||
of tyObject: nkObjConstr
|
||||
of tySequence: nkNilLit
|
||||
of tyProc, tyTuple: nkTupleConstr
|
||||
else: nkEmpty
|
||||
|
||||
let oldLen = node.len
|
||||
setLen(node.sons, newLen)
|
||||
if oldLen < newLen:
|
||||
# TODO: This is still not correct for tyPtr, tyRef default value
|
||||
for i in oldLen ..< newLen:
|
||||
node.sons[i] = newNodeI(typeKind, info)
|
||||
node.sons[i] = getNullValue(typ.sons[0], info, c.config)
|
||||
|
||||
const
|
||||
errIndexOutOfBounds = "index out of bounds"
|
||||
@@ -458,7 +444,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
#if c.traceActive:
|
||||
when traceCode:
|
||||
echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra, " rb ", instr.regB, " rc ", instr.regC
|
||||
# message(c.config, c.debug[pc], warnUser, "Trace")
|
||||
# message(c.config, c.debug[pc], warnUser, "Trace")
|
||||
|
||||
case instr.opcode
|
||||
of opcEof: return regs[ra]
|
||||
@@ -1201,7 +1187,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
of opcOf:
|
||||
decodeBC(rkInt)
|
||||
let typ = c.types[regs[rc].intVal.int]
|
||||
regs[ra].intVal = ord(inheritanceDiff(regs[rb].node.typ, typ) >= 0)
|
||||
regs[ra].intVal = ord(inheritanceDiff(regs[rb].node.typ, typ) <= 0)
|
||||
of opcIs:
|
||||
decodeBC(rkInt)
|
||||
let t1 = regs[rb].node.typ.skipTypes({tyTypeDesc})
|
||||
|
||||
@@ -237,8 +237,15 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
|
||||
of tyRange:
|
||||
result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
|
||||
result.add atomicType("range", mRange)
|
||||
result.add t.n.sons[0].copyTree
|
||||
result.add t.n.sons[1].copyTree
|
||||
if inst:
|
||||
let rng = newNodeX(nkInfix)
|
||||
rng.add newIdentNode(getIdent(cache, ".."), info)
|
||||
rng.add t.n.sons[0].copyTree
|
||||
rng.add t.n.sons[1].copyTree
|
||||
result.add rng
|
||||
else:
|
||||
result.add t.n.sons[0].copyTree
|
||||
result.add t.n.sons[1].copyTree
|
||||
of tyPointer: result = atomicType("pointer", mPointer)
|
||||
of tyString: result = atomicType("string", mString)
|
||||
of tyCString: result = atomicType("cstring", mCString)
|
||||
|
||||
@@ -1069,7 +1069,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||
var tmp = c.genx(n.sons[1])
|
||||
var idx = c.getTemp(getSysType(c.graph, n.info, tyInt))
|
||||
var typ = n.sons[2].typ
|
||||
if m == mOf: typ = typ.skipTypes(abstractPtrs-{tyTypeDesc})
|
||||
if m == mOf: typ = typ.skipTypes(abstractPtrs)
|
||||
c.gABx(n, opcLdImmInt, idx, c.genType(typ))
|
||||
c.gABC(n, if m == mOf: opcOf else: opcIs, dest, tmp, idx)
|
||||
c.freeTemp(tmp)
|
||||
@@ -1088,13 +1088,14 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||
of mEcho:
|
||||
unused(c, n, dest)
|
||||
let n = n[1].skipConv
|
||||
let x = c.getTempRange(n.len, slotTempUnknown)
|
||||
internalAssert c.config, n.kind == nkBracket
|
||||
for i in 0..<n.len:
|
||||
var r: TRegister = x+i
|
||||
c.gen(n.sons[i], r)
|
||||
c.gABC(n, opcEcho, x, n.len)
|
||||
c.freeTempRange(x, n.len)
|
||||
if n.kind == nkBracket:
|
||||
# can happen for nim check, see bug #9609
|
||||
let x = c.getTempRange(n.len, slotTempUnknown)
|
||||
for i in 0..<n.len:
|
||||
var r: TRegister = x+i
|
||||
c.gen(n.sons[i], r)
|
||||
c.gABC(n, opcEcho, x, n.len)
|
||||
c.freeTempRange(x, n.len)
|
||||
of mAppendStrCh:
|
||||
unused(c, n, dest)
|
||||
genBinaryStmtVar(c, n, opcAddStrCh)
|
||||
@@ -1648,8 +1649,9 @@ proc genVarSection(c: PCtx; n: PNode) =
|
||||
#assert(a.sons[0].kind == nkSym) can happen for transformed vars
|
||||
if a.kind == nkVarTuple:
|
||||
for i in 0 .. a.len-3:
|
||||
if not a[i].sym.isGlobal: setSlot(c, a[i].sym)
|
||||
checkCanEval(c, a[i])
|
||||
if a[i].kind == nkSym:
|
||||
if not a[i].sym.isGlobal: setSlot(c, a[i].sym)
|
||||
checkCanEval(c, a[i])
|
||||
c.gen(lowerTupleUnpacking(c.graph, a, c.getOwner))
|
||||
elif a.sons[0].kind == nkSym:
|
||||
let s = a.sons[0].sym
|
||||
|
||||
@@ -73,6 +73,19 @@ path="$lib/pure"
|
||||
opt:speed
|
||||
@end
|
||||
|
||||
@if unix and mingw:
|
||||
# Cross compile for Windows from Linux/OSX using MinGW
|
||||
os = windows
|
||||
|
||||
i386.windows.gcc.path = "/usr/bin"
|
||||
i386.windows.gcc.exe = "i686-w64-mingw32-gcc"
|
||||
i386.windows.gcc.linkerexe = "i686-w64-mingw32-gcc"
|
||||
|
||||
amd64.windows.gcc.path = "/usr/bin"
|
||||
amd64.windows.gcc.exe = "x86_64-w64-mingw32-gcc"
|
||||
amd64.windows.gcc.linkerexe = "x86_64-w64-mingw32-gcc"
|
||||
@end
|
||||
|
||||
@if unix:
|
||||
@if not bsd or haiku:
|
||||
# -fopenmp
|
||||
@@ -89,9 +102,6 @@ path="$lib/pure"
|
||||
tlsEmulation:on
|
||||
@end
|
||||
@if haiku:
|
||||
# Haiku currently have problems with TLS
|
||||
# https://dev.haiku-os.org/ticket/14342
|
||||
tlsEmulation:on
|
||||
gcc.options.linker = "-Wl,--as-needed -lnetwork"
|
||||
gcc.cpp.options.linker = "-Wl,--as-needed -lnetwork"
|
||||
clang.options.linker = "-Wl,--as-needed -lnetwork"
|
||||
|
||||
@@ -38,7 +38,7 @@ doc.section.toc = """
|
||||
# * $seeSrc: generated HTML from doc.item.seesrc (if some switches are used).
|
||||
|
||||
doc.item = """
|
||||
<a id="$itemSymOrIdEnc"></a>
|
||||
<a id="$itemSymOrID"></a>
|
||||
<dt><pre>$header</pre></dt>
|
||||
<dd>
|
||||
$desc
|
||||
|
||||
@@ -268,7 +268,7 @@ form the Nim code, then link them into a static binary along your main C
|
||||
program::
|
||||
|
||||
$ nim c --noMain --noLinking --header:fib.h fib.nim
|
||||
$ gcc -o m -Inimcache -Ipath/to/nim/lib nimcache/*.c maths.c
|
||||
$ gcc -o m -I$HOME/.cache/nim/fib_d -Ipath/to/nim/lib $HOME/.cache/nim/fib_d/*.c maths.c
|
||||
|
||||
The first command runs the Nim compiler with three special options to avoid
|
||||
generating a ``main()`` function in the generated files, avoid linking the
|
||||
|
||||
@@ -78,7 +78,7 @@ only want to see the output of failing tests, go for
|
||||
|
||||
You can also run only a single category of tests. A category is a subdirectory
|
||||
in the ``tests`` directory. There are a couple of special categories; for a
|
||||
list of these, see ``tests/testament/categories.nim``, at the bottom.
|
||||
list of these, see ``testament/categories.nim``, at the bottom.
|
||||
|
||||
::
|
||||
|
||||
@@ -195,13 +195,100 @@ or
|
||||
|
||||
the first is preferred.
|
||||
|
||||
Best practices
|
||||
=============
|
||||
|
||||
Note: these are general guidelines, not hard rules; there are always exceptions.
|
||||
Code reviews can just point to a specific section here to save time and
|
||||
propagate best practices.
|
||||
|
||||
.. _noimplicitbool:
|
||||
Take advantage of no implicit bool conversion
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
doAssert isValid() == true
|
||||
doAssert isValid() # preferred
|
||||
|
||||
.. _immediately_invoked_lambdas:
|
||||
Immediately invoked lambdas (https://en.wikipedia.org/wiki/Immediately-invoked_function_expression)
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
let a = (proc (): auto = getFoo())()
|
||||
let a = block: # preferred
|
||||
getFoo()
|
||||
|
||||
.. _design_for_mcs:
|
||||
Design with method call syntax (UFCS in other languages) chaining in mind
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
proc foo(cond: bool, lines: seq[string]) # bad
|
||||
proc foo(lines: seq[string], cond: bool) # preferred
|
||||
# can be called as: `getLines().foo(false)`
|
||||
|
||||
.. _avoid_quit:
|
||||
Use exceptions (including assert / doAssert) instead of ``quit``
|
||||
rationale: https://forum.nim-lang.org/t/4089
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
quit() # bad in almost all cases
|
||||
doAssert() # preferred
|
||||
|
||||
.. _tests_use_doAssert:
|
||||
Use ``doAssert`` (or ``require``, etc), not ``assert`` in all tests.
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
runnableExamples: assert foo() # bad
|
||||
runnableExamples: doAssert foo() # preferred
|
||||
|
||||
.. _delegate_printing:
|
||||
Delegate printing to caller: return ``string`` instead of calling ``echo``
|
||||
rationale: it's more flexible (eg allows caller to call custom printing,
|
||||
including prepending location info, writing to log files, etc).
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
proc foo() = echo "bar" # bad
|
||||
proc foo(): string = "bar" # preferred (usually)
|
||||
|
||||
.. _use_Option:
|
||||
[Ongoing debate] Consider using Option instead of return bool + var argument,
|
||||
unless stack allocation is needed (eg for efficiency).
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
proc foo(a: var Bar): bool
|
||||
proc foo(): Option[Bar]
|
||||
|
||||
.. _use_doAssert_not_echo:
|
||||
Tests (including in testament) should always prefer assertions over ``echo``,
|
||||
except when that's not possible. It's more precise, easier for readers and
|
||||
maintaners to where expected values refer to. See for example
|
||||
https://github.com/nim-lang/Nim/pull/9335 and https://forum.nim-lang.org/t/4089
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
echo foo() # adds a line in testament `discard` block.
|
||||
doAssert foo() == [1, 2] # preferred, except when not possible to do so.
|
||||
|
||||
The Git stuff
|
||||
=============
|
||||
|
||||
General commit rules
|
||||
--------------------
|
||||
|
||||
1. All changes introduced by the commit (diff lines) must be related to the
|
||||
1. Bugfixes that should be backported to the latest stable release should
|
||||
contain the string ``[backport]`` in the commit message! There will be an
|
||||
outmated process relying on these. However, bugfixes also have the inherent
|
||||
risk of causing regressions which are worse for a "stable, bugfixes-only"
|
||||
branch, so in doubt, leave out the ``[backport]``. Standard library bugfixes
|
||||
are less critical than compiler bugfixes.
|
||||
|
||||
2. All changes introduced by the commit (diff lines) must be related to the
|
||||
subject of the commit.
|
||||
|
||||
If you change some other unrelated to the subject parts of the file, because
|
||||
@@ -211,7 +298,7 @@ General commit rules
|
||||
*Tip:* Never commit everything as is using ``git commit -a``, but review
|
||||
carefully your changes with ``git add -p``.
|
||||
|
||||
2. Changes should not introduce any trailing whitespace.
|
||||
3. Changes should not introduce any trailing whitespace.
|
||||
|
||||
Always check your changes for whitespace errors using ``git diff --check``
|
||||
or add following ``pre-commit`` hook:
|
||||
@@ -221,6 +308,52 @@ General commit rules
|
||||
#!/bin/sh
|
||||
git diff --check --cached || exit $?
|
||||
|
||||
3. Describe your commit and use your common sense.
|
||||
4. Describe your commit and use your common sense.
|
||||
|
||||
Example Commit messages: ``Fixes #123; refs #124``
|
||||
|
||||
indicates that issue ``#123`` is completely fixed (github may automatically
|
||||
close it when the PR is committed), wheres issue ``#124`` is referenced
|
||||
(eg: partially fixed) and won't close the issue when committed.
|
||||
|
||||
5. Commits should be always be rebased against devel (so a fast forward
|
||||
merge can happen)
|
||||
|
||||
eg: use ``git pull --rebase origin devel``. This is to avoid messing up
|
||||
git history, see `#8664 <https://github.com/nim-lang/Nim/issues/8664>`_ .
|
||||
Exceptions should be very rare: when rebase gives too many conflicts, simply
|
||||
squash all commits using the script shown in
|
||||
https://github.com/nim-lang/Nim/pull/9356
|
||||
|
||||
|
||||
6. Do not mix pure formatting changes (eg whitespace changes, nimpretty) or
|
||||
automated changes (eg nimfix) with other code changes: these should be in
|
||||
separate commits (and the merge on github should not squash these into 1).
|
||||
|
||||
|
||||
Continuous Integration (CI)
|
||||
---------------------------
|
||||
|
||||
1. Continuous Integration is by default run on every push in a PR; this clogs
|
||||
the CI pipeline and affects other PR's; if you don't need it (eg for WIP or
|
||||
documentation only changes), add ``[ci skip]`` to your commit message title.
|
||||
This convention is supported by `Appveyor <https://www.appveyor.com/docs/how-to/filtering-commits/#skip-directive-in-commit-message>`_
|
||||
and `Travis <https://docs.travis-ci.com/user/customizing-the-build/#skipping-a-build>`_
|
||||
|
||||
|
||||
2. Consider enabling CI (travis and appveyor) in your own Nim fork, and
|
||||
waiting for CI to be green in that fork (fixing bugs as needed) before
|
||||
opening your PR in original Nim repo, so as to reduce CI congestion. Same
|
||||
applies for updates on a PR: you can test commits on a separate private
|
||||
branch before updating the main PR.
|
||||
|
||||
Code reviews
|
||||
------------
|
||||
|
||||
1. Whenever possible, use github's new 'Suggested change' in code reviews, which
|
||||
saves time explaining the change or applying it; see also
|
||||
https://forum.nim-lang.org/t/4317
|
||||
|
||||
.. include:: docstyle.rst
|
||||
|
||||
|
||||
|
||||
@@ -117,6 +117,7 @@ Output::
|
||||
{
|
||||
"orig": "docgen_sample.nim",
|
||||
"nimble": "",
|
||||
"moduleDescription": "This module is a sample",
|
||||
"entries": [
|
||||
{
|
||||
"name": "helloWorld",
|
||||
|
||||
@@ -4,7 +4,7 @@ Source Code Filters
|
||||
|
||||
.. contents::
|
||||
|
||||
A `Source Code Filter` transforms the input character stream to an in-memory
|
||||
A `Source Code Filter (SCF)` transforms the input character stream to an in-memory
|
||||
output stream before parsing. A filter can be used to provide templating
|
||||
systems or preprocessors.
|
||||
|
||||
@@ -23,10 +23,23 @@ just like an ordinary procedure call with named or positional arguments. The
|
||||
available parameters depend on the invoked filter. Before version 0.12.0 of
|
||||
the language ``#!`` was used instead of ``#?``.
|
||||
|
||||
**Hint:** With ``--hint[codeBegin]:on```or ``--verbosity:2``
|
||||
(or higher) Nim lists the processed code after each filter
|
||||
application.
|
||||
**Hint:** With ``--hint[codeBegin]:on`` or ``--verbosity:2``
|
||||
(or higher) while compiling or `nim check`, Nim lists the processed code after
|
||||
each filter application.
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
First, put your SCF code in a separate file with filters specified in the first line.
|
||||
**Note:** You can name your SCF file with any file extension you want, but the conventional extension is ``.tmpl``.
|
||||
|
||||
If we use `generateXML` code shown above and call the SCF file `xmlGen.tmpl`
|
||||
In your `main.nim`:
|
||||
|
||||
.. code-block:: nim
|
||||
include "xmlGen.tmpl"
|
||||
|
||||
echo generateXML("John Smith","42")
|
||||
|
||||
Pipe operator
|
||||
=============
|
||||
|
||||
@@ -9,7 +9,7 @@ operator = OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9
|
||||
| 'is' | 'isnot' | 'in' | 'notin' | 'of'
|
||||
| 'div' | 'mod' | 'shl' | 'shr' | 'not' | 'static' | '..'
|
||||
prefixOperator = operator
|
||||
optInd = COMMENT?
|
||||
optInd = COMMENT? IND?
|
||||
optPar = (IND{>} | IND{=})?
|
||||
simpleExpr = arrowExpr (OP0 optInd arrowExpr)* pragma?
|
||||
arrowExpr = assignExpr (OP1 optInd assignExpr)*
|
||||
@@ -85,17 +85,19 @@ paramListColon = paramList? (':' optInd typeDesc)?
|
||||
doBlock = 'do' paramListArrow pragmas? colcom stmt
|
||||
procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)?
|
||||
distinct = 'distinct' optInd typeDesc
|
||||
forStmt = 'for' (identWithPragma ^+ comma) 'in' expr colcom stmt
|
||||
forExpr = forStmt
|
||||
expr = (blockExpr
|
||||
| ifExpr
|
||||
| whenExpr
|
||||
| caseExpr
|
||||
| forExpr
|
||||
| tryExpr)
|
||||
/ simpleExpr
|
||||
typeKeyw = 'var' | 'out' | 'ref' | 'ptr' | 'shared' | 'tuple'
|
||||
| 'proc' | 'iterator' | 'distinct' | 'object' | 'enum'
|
||||
primary = typeKeyw typeDescK
|
||||
/ prefixOperator* identOrLiteral primarySuffix*
|
||||
/ 'static' primary
|
||||
/ 'bind' primary
|
||||
typeDesc = simpleExpr
|
||||
typeDefAux = simpleExpr
|
||||
@@ -142,12 +144,11 @@ tryExpr = 'try' colcom stmt &(optInd 'except'|'finally')
|
||||
(optInd 'except' exprList colcom stmt)*
|
||||
(optInd 'finally' colcom stmt)?
|
||||
exceptBlock = 'except' colcom stmt
|
||||
forStmt = 'for' (identWithPragma ^+ comma) 'in' expr colcom stmt
|
||||
blockStmt = 'block' symbol? colcom stmt
|
||||
blockExpr = 'block' symbol? colcom stmt
|
||||
staticStmt = 'static' colcom stmt
|
||||
deferStmt = 'defer' colcom stmt
|
||||
asmStmt = 'asm' pragma? (STR_LIT | RSTR_LIT | TRIPLE_STR_LIT)
|
||||
asmStmt = 'asm' pragma? (STR_LIT | RSTR_LIT | TRIPLESTR_LIT)
|
||||
genericParam = symbol (comma symbol)* (colon expr)? ('=' optInd expr)?
|
||||
genericParamList = '[' optInd
|
||||
genericParam ^* (comma/semicolon) optPar ']'
|
||||
|
||||
@@ -16,9 +16,9 @@ Directory structure
|
||||
|
||||
The Nim project's directory structure is:
|
||||
|
||||
============ ==============================================
|
||||
============ ===================================================
|
||||
Path Purpose
|
||||
============ ==============================================
|
||||
============ ===================================================
|
||||
``bin`` generated binary files
|
||||
``build`` generated C code for the installation
|
||||
``compiler`` the Nim compiler itself; note that this
|
||||
@@ -32,7 +32,7 @@ Path Purpose
|
||||
``lib`` the Nim library
|
||||
``web`` website of Nim; generated by ``nimweb``
|
||||
from the ``*.txt`` and ``*.tmpl`` files
|
||||
============ ==============================================
|
||||
============ ===================================================
|
||||
|
||||
|
||||
Bootstrapping the compiler
|
||||
@@ -386,8 +386,9 @@ So now ``G.c`` MUST contain both ``P1`` and ``P2``, but we haven't even
|
||||
loaded ``P1`` from the symbol file, nor do we want to because we then quickly
|
||||
would restore large parts of the whole program.
|
||||
|
||||
|
||||
Solution
|
||||
~~~~~~~~
|
||||
~~~~~~~~
|
||||
|
||||
The backend must have some logic so that if the currently processed module
|
||||
is from the compilation cache, the ``ast`` field is not accessed. Instead
|
||||
@@ -517,6 +518,7 @@ Code generation for closures
|
||||
|
||||
Code generation for closures is implemented by `lambda lifting`:idx:.
|
||||
|
||||
|
||||
Design
|
||||
------
|
||||
|
||||
@@ -665,5 +667,3 @@ important the hidden formal param is ``void*`` and not something more
|
||||
specialized. However the more specialized env type needs to passed to the
|
||||
backend somehow. We deal with this by modifying ``s.ast[paramPos]`` to contain
|
||||
the formal hidden parameter, but not ``s.typ``!
|
||||
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ test command
|
||||
------------
|
||||
|
||||
The `test`:idx: command can also be invoked with the alias ``tests``. This
|
||||
command will compile and run ``tests/testament/tester.nim``, which is the main
|
||||
command will compile and run ``testament/tester.nim``, which is the main
|
||||
driver of Nim's test suite. You can pass options to the ``test`` command,
|
||||
they will be forwarded to the tester. See its source code for available
|
||||
options.
|
||||
|
||||
11
doc/lib.rst
11
doc/lib.rst
@@ -18,9 +18,9 @@ low-level interface to a C library.
|
||||
|
||||
Read this `document <apis.html>`_ for a quick overview of the API design.
|
||||
|
||||
The `bottom <#nimble>`_ of this page includes a list of 3rd party packages
|
||||
created by the Nim community. These packages are a useful addition to the
|
||||
modules in the standard library.
|
||||
In addition to the modules in the standard library, third-party packages
|
||||
created by the Nim community can be used via `Nimble <#nimble>`_, Nim's
|
||||
package manager.
|
||||
|
||||
|
||||
Pure libraries
|
||||
@@ -59,7 +59,7 @@ Core
|
||||
This module defines compile-time reflection procs for working with types.
|
||||
|
||||
* `threadpool <threadpool.html>`_
|
||||
Implements Nim's `spawn <manual.html#spawn>`_.
|
||||
Implements Nim's `spawn <manual.html#parallel-amp-spawn>`_.
|
||||
|
||||
* `cpuinfo <cpuinfo.html>`_
|
||||
This module implements procs to determine the number of CPUs / cores.
|
||||
@@ -520,4 +520,5 @@ Nimble is a package manager for the Nim programming language.
|
||||
For instructions on how to install Nimble packages see
|
||||
`its README <https://github.com/nim-lang/nimble#readme>`_.
|
||||
|
||||
To see a list of Nimble's packages, check out `https://nimble.directory/ <https://nimble.directory/>`_ or the `packages repos <https://github.com/nim-lang/packages>`_ on GitHub.
|
||||
To see a list of Nimble's packages, check out `https://nimble.directory/ <https://nimble.directory/>`_
|
||||
or the `packages repo <https://github.com/nim-lang/packages>`_ on GitHub.
|
||||
|
||||
@@ -3037,7 +3037,7 @@ The `case expression` is again very similar to the case statement:
|
||||
|
||||
As seen in the above example, the case expression can also introduce side
|
||||
effects. When multiple statements are given for a branch, Nim will use
|
||||
the last expression as the result value, much like in an `expr` template.
|
||||
the last expression as the result value.
|
||||
|
||||
Table constructor
|
||||
-----------------
|
||||
@@ -4611,8 +4611,8 @@ The concept types can be parametric just like the regular generic types:
|
||||
M.data[m * M.N + n] = v
|
||||
|
||||
# Adapt the Matrix type to the concept's requirements
|
||||
template Rows*(M: type Matrix): expr = M.M
|
||||
template Cols*(M: type Matrix): expr = M.N
|
||||
template Rows*(M: type Matrix): int = M.M
|
||||
template Cols*(M: type Matrix): int = M.N
|
||||
template ValueType*(M: type Matrix): type = M.T
|
||||
|
||||
-------------
|
||||
@@ -5036,9 +5036,8 @@ an ``immediate`` pragma and then these templates do not take part in
|
||||
overloading resolution and the parameters' types are *ignored* by the
|
||||
compiler. Explicit immediate templates are now deprecated.
|
||||
|
||||
**Note**: For historical reasons ``stmt`` is an alias for ``typed`` and
|
||||
``expr`` an alias for ``untyped``, but new code should use the newer,
|
||||
clearer names.
|
||||
**Note**: For historical reasons ``stmt`` was an alias for ``typed`` and
|
||||
``expr`` was an alias for ``untyped``, but they are removed.
|
||||
|
||||
|
||||
Passing a code block to a template
|
||||
@@ -5129,9 +5128,6 @@ also ``varargs[untyped]`` so that not even the number of parameters is fixed:
|
||||
However, since a template cannot iterate over varargs, this feature is
|
||||
generally much more useful for macros.
|
||||
|
||||
**Note**: For historical reasons ``varargs[expr]`` is not equivalent
|
||||
to ``varargs[untyped]``.
|
||||
|
||||
|
||||
Symbol binding in templates
|
||||
---------------------------
|
||||
@@ -5965,6 +5961,12 @@ curlies is the pattern to match against. The operators ``*``, ``**``,
|
||||
notation, so to match verbatim against ``*`` the ordinary function call syntax
|
||||
needs to be used.
|
||||
|
||||
Term rewriting macro are applied recursively, up to a limit. This means that
|
||||
if the result of a term rewriting macro is eligible for another rewriting,
|
||||
the compiler will try to perform it, and so on, until no more optimizations
|
||||
are applicable. To avoid putting the compiler into an infinite loop, there is
|
||||
a hard limit on how many times a single term rewriting macro can be applied.
|
||||
Once this limit has been passed, the term rewriting macro will be ignored.
|
||||
|
||||
Unfortunately optimizations are hard to get right and even the tiny example
|
||||
is **wrong**:
|
||||
@@ -8003,7 +8005,7 @@ To enable thread support the ``--threads:on`` command line switch needs to
|
||||
be used. The ``system`` module then contains several threading primitives.
|
||||
See the `threads <threads.html>`_ and `channels <channels.html>`_ modules
|
||||
for the low level thread API. There are also high level parallelism constructs
|
||||
available. See `spawn <#parallel-spawn>`_ for further details.
|
||||
available. See `spawn <#parallel-amp-spawn>`_ for further details.
|
||||
|
||||
Nim's memory model for threads is quite different than that of other common
|
||||
programming languages (C, Pascal, Java): Each thread has its own (garbage
|
||||
|
||||
17
doc/nimc.rst
17
doc/nimc.rst
@@ -157,7 +157,7 @@ passed as a command line argument to the compiler.
|
||||
The ``nim`` executable processes configuration files in the following
|
||||
directories (in this order; later files overwrite previous settings):
|
||||
|
||||
1) ``$nim/config/nim.cfg``, ``/etc/nim/nim.cfg`` (UNIX) or ``%NIM%/config/nim.cfg`` (Windows). This file can be skipped with the ``--skipCfg`` command line option.
|
||||
1) ``$nim/config/nim.cfg``, ``/etc/nim/nim.cfg`` (UNIX) or ``<Nim's installation director>\config\nim.cfg`` (Windows). This file can be skipped with the ``--skipCfg`` command line option.
|
||||
2) If environment variable ``XDG_CONFIG_HOME`` is defined, ``$XDG_CONFIG_HOME/nim/nim.cfg`` or ``~/.config/nim/nim.cfg`` (POSIX) or ``%APPDATA%/nim/nim.cfg`` (Windows). This file can be skipped with the ``--skipUserCfg`` command line option.
|
||||
3) ``$parentDir/nim.cfg`` where ``$parentDir`` stands for any parent directory of the project file's path. These files can be skipped with the ``--skipParentCfg`` command line option.
|
||||
4) ``$projectDir/nim.cfg`` where ``$projectDir`` stands for the project file's path. This file can be skipped with the ``--skipProjCfg`` command line option.
|
||||
@@ -261,6 +261,21 @@ configuration file should contain something like::
|
||||
arm.linux.gcc.exe = "arm-linux-gcc"
|
||||
arm.linux.gcc.linkerexe = "arm-linux-gcc"
|
||||
|
||||
Cross compilation for Windows
|
||||
=============================
|
||||
|
||||
To cross compile for Windows from Linux or OSX using the MinGW-w64 toolchain::
|
||||
|
||||
nim c -d:mingw myproject.nim
|
||||
|
||||
Use ``--cpu:i386`` or ``--cpu:amd64`` to switch the cpu arch.
|
||||
|
||||
The MinGW-w64 toolchain can be installed as follows::
|
||||
|
||||
Ubuntu: apt install mingw-w64
|
||||
CentOS: yum install mingw32-gcc | mingw64-gcc - requires EPEL
|
||||
OSX: brew install mingw-w64
|
||||
|
||||
Cross compilation for Nintendo Switch
|
||||
=====================================
|
||||
|
||||
|
||||
23
doc/nims.rst
23
doc/nims.rst
@@ -6,9 +6,25 @@ Strictly speaking, ``NimScript`` is the subset of Nim that can be evaluated
|
||||
by Nim's builtin virtual machine (VM). This VM is used for Nim's compiletime
|
||||
function evaluation features.
|
||||
|
||||
You can use a ``<myproject>.nims`` file that simply contains Nim code
|
||||
controlling the compilation process. For a directory wide
|
||||
configuration, use ``config.nims`` instead of ``<myproject>.nims``.
|
||||
The ``nim`` executable processes the ``.nims`` configuration files in
|
||||
the following directories (in this order; later files overwrite
|
||||
previous settings):
|
||||
|
||||
1) If environment variable ``XDG_CONFIG_HOME`` is defined,
|
||||
``$XDG_CONFIG_HOME/nim/config.nims`` or
|
||||
``~/.config/nim/config.nims`` (POSIX) or
|
||||
``%APPDATA%/nim/config.nims`` (Windows). This file can be skipped
|
||||
with the ``--skipUserCfg`` command line option.
|
||||
2) ``$parentDir/config.nims`` where ``$parentDir`` stands for any
|
||||
parent directory of the project file's path. These files can be
|
||||
skipped with the ``--skipParentCfg`` command line option.
|
||||
3) ``$projectDir/config.nims`` where ``$projectDir`` stands for the
|
||||
project's path. This file can be skipped with the ``--skipProjCfg``
|
||||
command line option.
|
||||
4) A project can also have a project specific configuration file named
|
||||
``$project.nims`` that resides in the same directory as
|
||||
``$project.nim``. This file can be skipped with the same
|
||||
``--skipProjCfg`` command line option.
|
||||
|
||||
The VM cannot deal with ``importc`` because the FFI is not
|
||||
available. So the stdlib modules using ``importc`` cannot be used with
|
||||
@@ -74,6 +90,7 @@ In fact, as a convention the following tasks should be available:
|
||||
========= ===================================================
|
||||
Task Description
|
||||
========= ===================================================
|
||||
``help`` List all the available NimScript tasks along with their docstrings.
|
||||
``build`` Build the project with the required
|
||||
backend (``c``, ``cpp`` or ``js``).
|
||||
``tests`` Runs the tests belonging to the project.
|
||||
|
||||
@@ -48,7 +48,7 @@ by default.
|
||||
Specifying the location of the query
|
||||
------------------------------------
|
||||
|
||||
Nimsuggest than waits for queries to process. A query consists of a
|
||||
Nimsuggest then waits for queries to process. A query consists of a
|
||||
cryptic 3 letter "command" ``def`` or ``con`` or ``sug`` or ``use`` followed by
|
||||
a location. A query location consists of:
|
||||
|
||||
@@ -59,7 +59,7 @@ a location. A query location consists of:
|
||||
``dirtyfile.nim``
|
||||
This is optional.
|
||||
|
||||
The ``file`` paramater is enough for static analysis, but IDEs
|
||||
The ``file`` parameter is enough for static analysis, but IDEs
|
||||
tend to have *unsaved buffers* where the user may still be in
|
||||
the middle of typing a line. In such situations the IDE can
|
||||
save the current contents to a temporary file and then use the
|
||||
@@ -143,7 +143,7 @@ tab characters (``\t``). The values of each column are:
|
||||
``def`` for definition, ``sug`` for suggestion, etc).
|
||||
2. Type of the symbol. This can be ``skProc``, ``skLet``, and just
|
||||
about any of the enums defined in the module ``compiler/ast.nim``.
|
||||
3. Full qualitifed path of the symbol. If you are querying a symbol
|
||||
3. Fully qualified path of the symbol. If you are querying a symbol
|
||||
defined in the ``proj.nim`` file, this would have the form
|
||||
``proj.symbolName``.
|
||||
4. Type/signature. For variables and enums this will contain the
|
||||
|
||||
4
koch.nim
4
koch.nim
@@ -380,11 +380,11 @@ template `|`(a, b): string = (if a.len > 0: a else: b)
|
||||
proc tests(args: string) =
|
||||
# we compile the tester with taintMode:on to have a basic
|
||||
# taint mode test :-)
|
||||
nimexec "cc --taintMode:on --opt:speed tests/testament/tester"
|
||||
nimexec "cc --taintMode:on --opt:speed testament/tester"
|
||||
# Since tests take a long time (on my machine), and we want to defy Murhpys
|
||||
# law - lets make sure the compiler really is freshly compiled!
|
||||
nimexec "c --lib:lib -d:release --opt:speed compiler/nim.nim"
|
||||
let tester = quoteShell(getCurrentDir() / "tests/testament/tester".exe)
|
||||
let tester = quoteShell(getCurrentDir() / "testament/tester".exe)
|
||||
let success = tryExec tester & " " & (args|"all")
|
||||
if not existsEnv("TRAVIS") and not existsEnv("APPVEYOR"):
|
||||
exec tester & " html"
|
||||
|
||||
@@ -1034,10 +1034,18 @@ proc name*(someProc: NimNode): NimNode {.compileTime.} =
|
||||
someProc.expectRoutine
|
||||
result = someProc[0]
|
||||
if result.kind == nnkPostfix:
|
||||
result = result[1]
|
||||
if result[1].kind == nnkAccQuoted:
|
||||
result = result[1][0]
|
||||
else:
|
||||
result = result[1]
|
||||
elif result.kind == nnkAccQuoted:
|
||||
result = result[0]
|
||||
|
||||
proc `name=`*(someProc: NimNode; val: NimNode) {.compileTime.} =
|
||||
someProc.expectRoutine
|
||||
someProc[0] = val
|
||||
if someProc[0].kind == nnkPostfix:
|
||||
someProc[0][1] = val
|
||||
else: someProc[0] = val
|
||||
|
||||
proc params*(someProc: NimNode): NimNode {.compileTime.} =
|
||||
someProc.expectRoutine
|
||||
|
||||
@@ -108,6 +108,7 @@ proc getDiscriminant(aa: pointer, n: ptr TNimNode): int =
|
||||
of 1: d = ze(cast[ptr int8](a +% n.offset)[])
|
||||
of 2: d = ze(cast[ptr int16](a +% n.offset)[])
|
||||
of 4: d = int(cast[ptr int32](a +% n.offset)[])
|
||||
of 8: d = int(cast[ptr int64](a +% n.offset)[])
|
||||
else: assert(false)
|
||||
return d
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@ proc asyncSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
|
||||
protocol: Protocol = IPPROTO_TCP,
|
||||
buffered = true): AsyncSocket =
|
||||
## Initialises an AsyncSocket object. If a socket cannot be initialised
|
||||
## EOS is raised.
|
||||
## OSError is raised.
|
||||
result = newAsyncSocket()
|
||||
result.socket = socket(domain, typ, protocol, buffered)
|
||||
result.proto = protocol
|
||||
@@ -476,7 +476,7 @@ proc recvLine*(s: AsyncSocket, line: var TaintedString): bool {.deprecated.} =
|
||||
## if this function can only retrieve some data; it will save this data and
|
||||
## add it to the result when a full line is retrieved.
|
||||
##
|
||||
## Unlike ``sockets.recvLine`` this function will raise an EOS or ESSL
|
||||
## Unlike ``sockets.recvLine`` this function will raise an OSError or SslError
|
||||
## exception if an error occurs.
|
||||
##
|
||||
## **Deprecated since version 0.9.2**: This function has been deprecated in
|
||||
@@ -512,7 +512,7 @@ proc readLine*(s: AsyncSocket, line: var TaintedString): bool =
|
||||
## retrieved or the socket has been disconnected in which case ``line`` will
|
||||
## be set to "".
|
||||
##
|
||||
## This function will raise an EOS exception when a socket error occurs.
|
||||
## This function will raise an OSError exception when a socket error occurs.
|
||||
setLen(line.string, 0)
|
||||
var dataReceived = "".TaintedString
|
||||
var ret = s.socket.readLineAsync(dataReceived)
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
##
|
||||
## SSL is supported through the OpenSSL library. This support can be activated
|
||||
## by compiling with the ``-d:ssl`` switch. When an SSL socket is used it will
|
||||
## raise ESSL exceptions when SSL errors occur.
|
||||
## raise SslError exceptions when SSL errors occur.
|
||||
##
|
||||
## Asynchronous sockets are supported, however a better alternative is to use
|
||||
## the `asyncio <asyncio.html>`_ module.
|
||||
@@ -262,7 +262,7 @@ proc socket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
|
||||
protocol: Protocol = IPPROTO_TCP, buffered = true): Socket =
|
||||
## Creates a new socket; returns `InvalidSocket` if an error occurs.
|
||||
|
||||
# TODO: Perhaps this should just raise EOS when an error occurs.
|
||||
# TODO: Perhaps this should just raise OSError when an error occurs.
|
||||
when defined(Windows):
|
||||
result = newTSocket(winlean.socket(cint(domain), cint(typ), cint(protocol)), buffered)
|
||||
else:
|
||||
@@ -422,7 +422,7 @@ proc parseIp4*(s: string): BiggestInt =
|
||||
##
|
||||
## This is equivalent to `inet_ntoa`:idx:.
|
||||
##
|
||||
## Raises EInvalidValue in case of an error.
|
||||
## Raises ValueError in case of an error.
|
||||
var a, b, c, d: int
|
||||
var i = 0
|
||||
var j = parseInt(s, a, i)
|
||||
@@ -543,7 +543,7 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string) {.
|
||||
## If ``server`` is non-blocking then this function returns immediately, and
|
||||
## if there are no connections queued the returned socket will be
|
||||
## ``InvalidSocket``.
|
||||
## This function will raise EOS if an error occurs.
|
||||
## This function will raise OSError if an error occurs.
|
||||
##
|
||||
## The resulting client will inherit any properties of the server socket. For
|
||||
## example: whether the socket is buffered or not.
|
||||
@@ -664,7 +664,7 @@ proc close*(socket: Socket) =
|
||||
discard winlean.closesocket(socket.fd)
|
||||
else:
|
||||
discard posix.close(socket.fd)
|
||||
# TODO: These values should not be discarded. An EOS should be raised.
|
||||
# TODO: These values should not be discarded. An OSError should be raised.
|
||||
# http://stackoverflow.com/questions/12463473/what-happens-if-you-call-close-on-a-bsd-socket-multiple-times
|
||||
when defined(ssl):
|
||||
if socket.isSSL:
|
||||
@@ -920,7 +920,7 @@ when defined(ssl):
|
||||
## Returns ``False`` whenever the socket is not yet ready for a handshake,
|
||||
## ``True`` whenever handshake completed successfully.
|
||||
##
|
||||
## A ESSL error is raised on any other errors.
|
||||
## A SslError error is raised on any other errors.
|
||||
result = true
|
||||
if socket.isSSL:
|
||||
var ret = SSLConnect(socket.sslHandle)
|
||||
@@ -946,7 +946,7 @@ when defined(ssl):
|
||||
## Determines whether a handshake has occurred between a client (``socket``)
|
||||
## and the server that ``socket`` is connected to.
|
||||
##
|
||||
## Throws ESSL if ``socket`` is not an SSL socket.
|
||||
## Throws SslError if ``socket`` is not an SSL socket.
|
||||
if socket.isSSL:
|
||||
return not socket.sslNoHandshake
|
||||
else:
|
||||
@@ -1215,7 +1215,7 @@ proc recv*(socket: Socket, data: var string, size: int, timeout = -1): int =
|
||||
##
|
||||
## When 0 is returned the socket's connection has been closed.
|
||||
##
|
||||
## This function will throw an EOS exception when an error occurs. A value
|
||||
## This function will throw an OSError exception when an error occurs. A value
|
||||
## lower than 0 is never returned.
|
||||
##
|
||||
## A timeout may be specified in milliseconds, if enough data is not received
|
||||
@@ -1273,7 +1273,7 @@ proc recvLine*(socket: Socket, line: var TaintedString, timeout = -1): bool {.
|
||||
## will be set to it.
|
||||
##
|
||||
## ``True`` is returned if data is available. ``False`` suggests an
|
||||
## error, EOS exceptions are not raised and ``False`` is simply returned
|
||||
## error, OSError exceptions are not raised and ``False`` is simply returned
|
||||
## instead.
|
||||
##
|
||||
## If the socket is disconnected, ``line`` will be set to ``""`` and ``True``
|
||||
@@ -1321,7 +1321,7 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1) {.
|
||||
##
|
||||
## If the socket is disconnected, ``line`` will be set to ``""``.
|
||||
##
|
||||
## An EOS exception will be raised in the case of a socket error.
|
||||
## An OSError exception will be raised in the case of a socket error.
|
||||
##
|
||||
## A timeout can be specified in milliseconds, if data is not received within
|
||||
## the specified time an ETimeout exception will be raised.
|
||||
@@ -1394,7 +1394,7 @@ proc readLineAsync*(socket: Socket,
|
||||
## * If some data has been retrieved; ``ReadPartialLine`` is returned.
|
||||
## * If the socket has been disconnected; ``ReadDisconnected`` is returned.
|
||||
## * If no data could be retrieved; ``ReadNone`` is returned.
|
||||
## * If call to ``recv`` failed; **an EOS exception is raised.**
|
||||
## * If call to ``recv`` failed; **an OSError exception is raised.**
|
||||
setLen(line.string, 0)
|
||||
|
||||
template errorOrNone =
|
||||
@@ -1421,7 +1421,7 @@ proc readLineAsync*(socket: Socket,
|
||||
|
||||
proc recv*(socket: Socket): TaintedString {.tags: [ReadIOEffect], deprecated.} =
|
||||
## receives all the available data from the socket.
|
||||
## Socket errors will result in an ``EOS`` error.
|
||||
## Socket errors will result in an ``OSError`` error.
|
||||
## If socket is not a connectionless socket and socket is not connected
|
||||
## ``""`` will be returned.
|
||||
##
|
||||
@@ -1470,7 +1470,7 @@ proc recvAsync*(socket: Socket, s: var TaintedString): bool {.
|
||||
tags: [ReadIOEffect], deprecated.} =
|
||||
## receives all the data from a non-blocking socket. If socket is non-blocking
|
||||
## and there are no messages available, `False` will be returned.
|
||||
## Other socket errors will result in an ``EOS`` error.
|
||||
## Other socket errors will result in an ``OSError`` error.
|
||||
## If socket is not a connectionless socket and socket is not connected
|
||||
## ``s`` will be set to ``""``.
|
||||
##
|
||||
@@ -1547,7 +1547,7 @@ proc recvFromAsync*(socket: Socket, data: var string, length: int,
|
||||
address: var string, port: var Port,
|
||||
flags = 0'i32): bool {.tags: [ReadIOEffect].} =
|
||||
## Variant of ``recvFrom`` for non-blocking sockets. Unlike ``recvFrom``,
|
||||
## this function will raise an EOS error whenever a socket error occurs.
|
||||
## this function will raise an OSError error whenever a socket error occurs.
|
||||
##
|
||||
## If there is no data to be read from the socket ``False`` will be returned.
|
||||
result = true
|
||||
@@ -1622,7 +1622,7 @@ proc sendAsync*(socket: Socket, data: string): int {.tags: [WriteIOEffect].} =
|
||||
## returns the amount of bytes of ``data`` that was successfully sent. This
|
||||
## number may not always be the length of ``data`` but typically is.
|
||||
##
|
||||
## An EOS (or ESSL if socket is an SSL socket) exception is raised if an error
|
||||
## An OSError (or SslError if socket is an SSL socket) exception is raised if an error
|
||||
## occurs.
|
||||
result = send(socket, cstring(data), data.len)
|
||||
when defined(ssl):
|
||||
@@ -1656,7 +1656,7 @@ proc sendAsync*(socket: Socket, data: string): int {.tags: [WriteIOEffect].} =
|
||||
|
||||
|
||||
proc trySend*(socket: Socket, data: string): bool {.tags: [WriteIOEffect].} =
|
||||
## safe alternative to ``send``. Does not raise an EOS when an error occurs,
|
||||
## safe alternative to ``send``. Does not raise an OSError when an error occurs,
|
||||
## and instead returns ``false`` on failure.
|
||||
result = send(socket, cstring(data), data.len) == data.len
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## import db_sqlite
|
||||
## let db = open("mytest.db", nil, nil, nil) # user, password, database name can be nil
|
||||
## let db = open("mytest.db", "", "", "") # user, password, database name can be empty
|
||||
## db.close()
|
||||
##
|
||||
## Creating a table
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
|
||||
from pcre import nil
|
||||
import nre.private.util
|
||||
import nre/private/util
|
||||
import tables
|
||||
from strutils import `%`
|
||||
from math import ceil
|
||||
@@ -469,7 +469,8 @@ proc matchImpl(str: string, pattern: Regex, start, endpos: int, flags: int): Opt
|
||||
# 1x capture count as slack space for PCRE
|
||||
let vecsize = (pattern.captureCount() + 1) * 3
|
||||
# div 2 because each element is 2 cints long
|
||||
myResult.pcreMatchBounds = newSeq[HSlice[cint, cint]](ceil(vecsize / 2).int)
|
||||
# plus 1 because we need the ceiling, not the floor
|
||||
myResult.pcreMatchBounds = newSeq[HSlice[cint, cint]]((vecsize + 1) div 2)
|
||||
myResult.pcreMatchBounds.setLen(vecsize div 3)
|
||||
|
||||
let strlen = if endpos == int.high: str.len else: endpos+1
|
||||
|
||||
@@ -408,7 +408,7 @@ proc startsWith*(s: string, prefix: Regex): bool {.inline.} =
|
||||
result = matchLen(s, prefix) >= 0
|
||||
|
||||
proc endsWith*(s: string, suffix: Regex): bool {.inline.} =
|
||||
## returns true if `s` ends with the pattern `prefix`
|
||||
## returns true if `s` ends with the pattern `suffix`
|
||||
for i in 0 .. s.len-1:
|
||||
if matchLen(s, suffix, i) == s.len - i: return true
|
||||
|
||||
@@ -428,7 +428,7 @@ proc replace*(s: string, sub: Regex, by = ""): string =
|
||||
## "; "
|
||||
result = ""
|
||||
var prev = 0
|
||||
while true:
|
||||
while prev < s.len:
|
||||
var match = findBounds(s, sub, prev)
|
||||
if match.first < 0: break
|
||||
add(result, substr(s, prev, match.first-1))
|
||||
@@ -453,7 +453,7 @@ proc replacef*(s: string, sub: Regex, by: string): string =
|
||||
result = ""
|
||||
var caps: array[MaxSubpatterns, string]
|
||||
var prev = 0
|
||||
while true:
|
||||
while prev < s.len:
|
||||
var match = findBounds(s, sub, caps, prev)
|
||||
if match.first < 0: break
|
||||
add(result, substr(s, prev, match.first-1))
|
||||
|
||||
@@ -105,7 +105,7 @@ proc add*(monitor: FSMonitor, target: string,
|
||||
proc del*(monitor: FSMonitor, wd: cint) =
|
||||
## Removes watched directory or file as specified by ``wd`` from ``monitor``.
|
||||
##
|
||||
## If ``wd`` is not a part of ``monitor`` an EOS error is raised.
|
||||
## If ``wd`` is not a part of ``monitor`` an OSError error is raised.
|
||||
if inotifyRmWatch(monitor.fd, wd) < 0:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
|
||||
@@ -145,7 +145,9 @@ export asyncfutures, asyncstreams
|
||||
##
|
||||
## Futures should **never** be discarded. This is because they may contain
|
||||
## errors. If you do not care for the result of a Future then you should
|
||||
## use the ``asyncCheck`` procedure instead of the ``discard`` keyword.
|
||||
## use the ``asyncCheck`` procedure instead of the ``discard`` keyword. Note
|
||||
## however that this does not wait for completion, and you should use
|
||||
## ``waitFor`` for that purpose.
|
||||
##
|
||||
## Examples
|
||||
## --------
|
||||
|
||||
@@ -339,7 +339,8 @@ proc asyncCheck*[T](future: Future[T]) =
|
||||
## Sets a callback on ``future`` which raises an exception if the future
|
||||
## finished with an error.
|
||||
##
|
||||
## This should be used instead of ``discard`` to discard void futures.
|
||||
## This should be used instead of ``discard`` to discard void futures,
|
||||
## or use ``waitFor`` if you need to wait for the future's completion.
|
||||
assert(not future.isNil, "Future is nil")
|
||||
future.callback =
|
||||
proc () =
|
||||
|
||||
@@ -218,7 +218,7 @@ when defineSsl:
|
||||
var data = await recv(socket.fd.AsyncFD, BufferSize, flags)
|
||||
let length = len(data)
|
||||
if length > 0:
|
||||
let ret = bioWrite(socket.bioIn, addr data[0], data.len.cint)
|
||||
let ret = bioWrite(socket.bioIn, addr data[0], length.cint)
|
||||
if ret < 0:
|
||||
raiseSSLError()
|
||||
elif length == 0:
|
||||
@@ -599,7 +599,7 @@ proc listen*(socket: AsyncSocket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].}
|
||||
## ``Backlog`` specifies the maximum length of the
|
||||
## queue of pending connections.
|
||||
##
|
||||
## Raises an EOS error upon failure.
|
||||
## Raises an OSError error upon failure.
|
||||
if listen(socket.fd, backlog) < 0'i32: raiseOSError(osLastError())
|
||||
|
||||
proc bindAddr*(socket: AsyncSocket, port = Port(0), address = "") {.
|
||||
|
||||
@@ -81,6 +81,9 @@ proc read*[T](future: FutureStream[T]): Future[(bool, T)] =
|
||||
let savedCb = future.cb
|
||||
future.callback =
|
||||
proc (fs: FutureStream[T]) =
|
||||
# Exit early if `resFut` is already complete. (See #8994).
|
||||
if resFut.finished: return
|
||||
|
||||
# We don't want this callback called again.
|
||||
future.cb = nil
|
||||
|
||||
@@ -93,8 +96,7 @@ proc read*[T](future: FutureStream[T]): Future[(bool, T)] =
|
||||
res[0] = true
|
||||
res[1] = fs.queue.popFirst()
|
||||
|
||||
if not resFut.finished:
|
||||
resFut.complete(res)
|
||||
resFut.complete(res)
|
||||
|
||||
# If the saved callback isn't nil then let's call it.
|
||||
if not savedCb.isNil: savedCb()
|
||||
|
||||
@@ -158,10 +158,14 @@ template rawGetKnownHCImpl() {.dirty.} =
|
||||
h = nextTry(h, high(s.data))
|
||||
result = -1 - h # < 0 => MISSING; insert idx = -1 - result
|
||||
|
||||
template rawGetImpl() {.dirty.} =
|
||||
hc = hash(key)
|
||||
template genHash(key: typed): Hash =
|
||||
var hc = hash(key)
|
||||
if hc == 0: # This almost never taken branch should be very predictable.
|
||||
hc = 314159265 # Value doesn't matter; Any non-zero favorite is fine.
|
||||
hc
|
||||
|
||||
template rawGetImpl() {.dirty.} =
|
||||
hc = genHash(key)
|
||||
rawGetKnownHCImpl()
|
||||
|
||||
template rawInsertImpl() {.dirty.} =
|
||||
@@ -794,39 +798,24 @@ proc incl*[A](s: var HashSet[A], other: OrderedSet[A]) =
|
||||
|
||||
proc exclImpl[A](s: var OrderedSet[A], key: A) : bool {. inline .} =
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
var hc: Hash
|
||||
var i = rawGet(s, key, hc)
|
||||
var msk = high(s.data)
|
||||
var n: OrderedKeyValuePairSeq[A]
|
||||
newSeq(n, len(s.data))
|
||||
var h = s.first
|
||||
s.first = -1
|
||||
s.last = -1
|
||||
swap(s.data, n)
|
||||
let hc = genHash(key)
|
||||
result = true
|
||||
|
||||
if i >= 0:
|
||||
result = false
|
||||
# Fix ordering
|
||||
if s.first == i:
|
||||
s.first = s.data[i].next
|
||||
else:
|
||||
var itr = s.first
|
||||
while true:
|
||||
if (s.data[itr].next == i):
|
||||
s.data[itr].next = s.data[i].next
|
||||
if s.last == i:
|
||||
s.last = itr
|
||||
break
|
||||
itr = s.data[itr].next
|
||||
|
||||
dec(s.counter)
|
||||
while true: # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1
|
||||
var j = i # The correctness of this depends on (h+1) in nextTry,
|
||||
var r = j # though may be adaptable to other simple sequences.
|
||||
s.data[i].hcode = 0 # mark current EMPTY
|
||||
s.data[i].key = default(type(s.data[i].key))
|
||||
s.data[i].next = 0
|
||||
doWhile((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)):
|
||||
i = (i + 1) and msk # increment mod table size
|
||||
if isEmpty(s.data[i].hcode): # end of collision cluster; So all done
|
||||
return
|
||||
r = s.data[i].hcode and msk # "home" location of key@i
|
||||
shallowCopy(s.data[j], s.data[i]) # data[i] will be marked EMPTY next loop
|
||||
while h >= 0:
|
||||
var nxt = n[h].next
|
||||
if isFilled(n[h].hcode):
|
||||
if n[h].hcode == hc and n[h].key == key:
|
||||
dec s.counter
|
||||
result = false
|
||||
else:
|
||||
var j = -1 - rawGetKnownHC(s, n[h].key, n[h].hcode)
|
||||
rawInsert(s, s.data, n[h].key, n[h].hcode, j)
|
||||
h = nxt
|
||||
|
||||
proc missingOrExcl*[A](s: var OrderedSet[A], key: A): bool =
|
||||
## Excludes `key` in the set `s` and tells if `key` was removed from `s`. Efficiency: O(n).
|
||||
@@ -1097,6 +1086,12 @@ when isMainModule and not defined(release):
|
||||
for item in s: items.add item
|
||||
assert items == @[2, 6, 7]
|
||||
|
||||
block: #9005
|
||||
var s = initOrderedSet[(int, int)]()
|
||||
for i in 0 .. 30: incl(s, (i, 0))
|
||||
for i in 0 .. 30: excl(s, (i, 0))
|
||||
doAssert s.len == 0
|
||||
|
||||
#block orderedSetIterator:
|
||||
# var a = initOrderedSet[int]()
|
||||
# for value in [9, 2, 1, 5, 1, 8, 4, 2]:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -377,7 +377,7 @@ proc colorNameCmp(x: tuple[name: string, col: Color], y: string): int =
|
||||
|
||||
proc parseColor*(name: string): Color =
|
||||
## parses `name` to a color value. If no valid color could be
|
||||
## parsed ``EInvalidValue`` is raised. Case insensitive.
|
||||
## parsed ``ValueError`` is raised. Case insensitive.
|
||||
if name[0] == '#':
|
||||
result = Color(parseHexInt(name))
|
||||
else:
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Implements Nim's 'spawn'.
|
||||
## Implements Nim's `spawn <manual.html#parallel-amp-spawn>`_.
|
||||
|
||||
when not compileOption("threads"):
|
||||
{.error: "Threadpool requires --threads:on option.".}
|
||||
|
||||
295
lib/pure/editdistance.nim
Normal file
295
lib/pure/editdistance.nim
Normal file
@@ -0,0 +1,295 @@
|
||||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2018 Nim contributors
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements an algorithm to compute the
|
||||
## `edit distance`:idx: between two Unicode strings.
|
||||
|
||||
import unicode
|
||||
|
||||
proc editDistance*(a, b: string): int {.noSideEffect.} =
|
||||
## Returns the unicode-rune edit distance between ``a`` and ``b``.
|
||||
##
|
||||
## This uses the `Levenshtein`:idx: distance algorithm with only a linear
|
||||
## memory overhead.
|
||||
if len(a) > len(b):
|
||||
# make ``b`` the longer string
|
||||
return editDistance(b, a)
|
||||
# strip common prefix
|
||||
var
|
||||
i_start = 0 ## The character starting index of the first rune in both strings ``a`` and ``b``
|
||||
i_next_a = 0
|
||||
i_next_b = 0
|
||||
rune_a, rune_b: Rune
|
||||
len_runes_a = 0 ## The number of relevant runes in string ``a``.
|
||||
len_runes_b = 0 ## The number of relevant runes in string ``b``.
|
||||
block commonPrefix:
|
||||
# ``a`` is the shorter string
|
||||
while i_start < len(a):
|
||||
i_next_a = i_start
|
||||
a.fastRuneAt(i_next_a, rune_a, doInc = true)
|
||||
i_next_b = i_start
|
||||
b.fastRuneAt(i_next_b, rune_b, doInc = true)
|
||||
if rune_a != rune_b:
|
||||
inc(len_runes_a)
|
||||
inc(len_runes_b)
|
||||
break
|
||||
i_start = i_next_a
|
||||
var
|
||||
# we know that we are either at the start of the strings
|
||||
# or that the current value of rune_a is not equal to rune_b
|
||||
# => start search for common suffix after the current rune (``i_next_*``)
|
||||
i_end_a = i_next_a ## The exclusive upper index bound of string ``a``.
|
||||
i_end_b = i_next_b ## The exclusive upper index bound of string ``b``.
|
||||
i_current_a = i_next_a
|
||||
i_current_b = i_next_b
|
||||
block commonSuffix:
|
||||
var
|
||||
add_runes_a = 0
|
||||
add_runes_b = 0
|
||||
while i_current_a < len(a) and i_current_b < len(b):
|
||||
i_next_a = i_current_a
|
||||
a.fastRuneAt(i_next_a, rune_a)
|
||||
i_next_b = i_current_b
|
||||
b.fastRuneAt(i_next_b, rune_b)
|
||||
inc(add_runes_a)
|
||||
inc(add_runes_b)
|
||||
if rune_a != rune_b:
|
||||
i_end_a = i_next_a
|
||||
i_end_b = i_next_b
|
||||
inc(len_runes_a, add_runes_a)
|
||||
inc(len_runes_b, add_runes_b)
|
||||
add_runes_a = 0
|
||||
add_runes_b = 0
|
||||
i_current_a = i_next_a
|
||||
i_current_b = i_next_b
|
||||
if i_current_a >= len(a): # ``a`` exhausted
|
||||
if i_current_b < len(b): # ``b`` not exhausted
|
||||
i_end_a = i_current_a
|
||||
i_end_b = i_current_b
|
||||
inc(len_runes_a, add_runes_a)
|
||||
inc(len_runes_b, add_runes_b)
|
||||
while true:
|
||||
b.fastRuneAt(i_end_b, rune_b)
|
||||
inc(len_runes_b)
|
||||
if i_end_b >= len(b): break
|
||||
elif i_current_b >= len(b): # ``b`` exhausted and ``a`` not exhausted
|
||||
i_end_a = i_current_a
|
||||
i_end_b = i_current_b
|
||||
inc(len_runes_a, add_runes_a)
|
||||
inc(len_runes_b, add_runes_b)
|
||||
while true:
|
||||
a.fastRuneAt(i_end_a, rune_a)
|
||||
inc(len_runes_a)
|
||||
if i_end_a >= len(a): break
|
||||
block specialCases:
|
||||
# trivial cases:
|
||||
if len_runes_a == 0: return len_runes_b
|
||||
if len_runes_b == 0: return len_runes_a
|
||||
# another special case:
|
||||
if len_runes_a == 1:
|
||||
a.fastRuneAt(i_start, rune_a, doInc = false)
|
||||
var i_current_b = i_start
|
||||
while i_current_b < i_end_b:
|
||||
b.fastRuneAt(i_current_b, rune_b, doInc = true)
|
||||
if rune_a == rune_b: return len_runes_b - 1
|
||||
return len_runes_b
|
||||
# common case:
|
||||
var
|
||||
len1 = len_runes_a + 1
|
||||
len2 = len_runes_b + 1
|
||||
row: seq[int]
|
||||
let half = len_runes_a div 2
|
||||
newSeq(row, len2)
|
||||
var e = i_start + len2 - 1 # end marker
|
||||
# initialize first row:
|
||||
for i in 1 .. (len2 - half - 1): row[i] = i
|
||||
row[0] = len1 - half - 1
|
||||
i_current_a = i_start
|
||||
var
|
||||
char2p_i = -1
|
||||
char2p_prev: int
|
||||
for i in 1 .. (len1 - 1):
|
||||
i_next_a = i_current_a
|
||||
a.fastRuneAt(i_next_a, rune_a)
|
||||
var
|
||||
char2p: int
|
||||
D, x: int
|
||||
p: int
|
||||
if i >= (len1 - half):
|
||||
# skip the upper triangle:
|
||||
let offset = i + half - len1
|
||||
if char2p_i == i:
|
||||
b.fastRuneAt(char2p_prev, rune_b)
|
||||
char2p = char2p_prev
|
||||
char2p_i = i + 1
|
||||
else:
|
||||
char2p = i_start
|
||||
for j in 0 ..< offset:
|
||||
rune_b = b.runeAt(char2p)
|
||||
inc(char2p, rune_b.size)
|
||||
char2p_i = i + 1
|
||||
char2p_prev = char2p
|
||||
p = offset
|
||||
rune_b = b.runeAt(char2p)
|
||||
var c3 = row[p] + (if rune_a != rune_b: 1 else: 0)
|
||||
inc(char2p, rune_b.size)
|
||||
inc(p)
|
||||
x = row[p] + 1
|
||||
D = x
|
||||
if x > c3: x = c3
|
||||
row[p] = x
|
||||
inc(p)
|
||||
else:
|
||||
p = 1
|
||||
char2p = i_start
|
||||
D = i
|
||||
x = i
|
||||
if i <= (half + 1):
|
||||
# skip the lower triangle:
|
||||
e = len2 + i - half - 2
|
||||
# main:
|
||||
while p <= e:
|
||||
dec(D)
|
||||
rune_b = b.runeAt(char2p)
|
||||
var c3 = D + (if rune_a != rune_b: 1 else: 0)
|
||||
inc(char2p, rune_b.size)
|
||||
inc(x)
|
||||
if x > c3: x = c3
|
||||
D = row[p] + 1
|
||||
if x > D: x = D
|
||||
row[p] = x
|
||||
inc(p)
|
||||
# lower triangle sentinel:
|
||||
if i <= half:
|
||||
dec(D)
|
||||
rune_b = b.runeAt(char2p)
|
||||
var c3 = D + (if rune_a != rune_b: 1 else: 0)
|
||||
inc(x)
|
||||
if x > c3: x = c3
|
||||
row[p] = x
|
||||
i_current_a = i_next_a
|
||||
result = row[e]
|
||||
|
||||
proc editDistanceAscii*(a, b: string): int {.noSideEffect.} =
|
||||
## Returns the edit distance between `a` and `b`.
|
||||
##
|
||||
## This uses the `Levenshtein`:idx: distance algorithm with only a linear
|
||||
## memory overhead.
|
||||
var len1 = a.len
|
||||
var len2 = b.len
|
||||
if len1 > len2:
|
||||
# make `b` the longer string
|
||||
return editDistanceAscii(b, a)
|
||||
|
||||
# strip common prefix:
|
||||
var s = 0
|
||||
while s < len1 and a[s] == b[s]:
|
||||
inc(s)
|
||||
dec(len1)
|
||||
dec(len2)
|
||||
# strip common suffix:
|
||||
while len1 > 0 and len2 > 0 and a[s+len1-1] == b[s+len2-1]:
|
||||
dec(len1)
|
||||
dec(len2)
|
||||
# trivial cases:
|
||||
if len1 == 0: return len2
|
||||
if len2 == 0: return len1
|
||||
|
||||
# another special case:
|
||||
if len1 == 1:
|
||||
for j in s..s+len2-1:
|
||||
if a[s] == b[j]: return len2 - 1
|
||||
return len2
|
||||
|
||||
inc(len1)
|
||||
inc(len2)
|
||||
var half = len1 shr 1
|
||||
# initalize first row:
|
||||
#var row = cast[ptr array[0..high(int) div 8, int]](alloc(len2*sizeof(int)))
|
||||
var row: seq[int]
|
||||
newSeq(row, len2)
|
||||
var e = s + len2 - 1 # end marker
|
||||
for i in 1..len2 - half - 1: row[i] = i
|
||||
row[0] = len1 - half - 1
|
||||
for i in 1 .. len1 - 1:
|
||||
var char1 = a[i + s - 1]
|
||||
var char2p: int
|
||||
var D, x: int
|
||||
var p: int
|
||||
if i >= len1 - half:
|
||||
# skip the upper triangle:
|
||||
var offset = i - len1 + half
|
||||
char2p = offset
|
||||
p = offset
|
||||
var c3 = row[p] + ord(char1 != b[s + char2p])
|
||||
inc(p)
|
||||
inc(char2p)
|
||||
x = row[p] + 1
|
||||
D = x
|
||||
if x > c3: x = c3
|
||||
row[p] = x
|
||||
inc(p)
|
||||
else:
|
||||
p = 1
|
||||
char2p = 0
|
||||
D = i
|
||||
x = i
|
||||
if i <= half + 1:
|
||||
# skip the lower triangle:
|
||||
e = len2 + i - half - 2
|
||||
# main:
|
||||
while p <= e:
|
||||
dec(D)
|
||||
var c3 = D + ord(char1 != b[char2p + s])
|
||||
inc(char2p)
|
||||
inc(x)
|
||||
if x > c3: x = c3
|
||||
D = row[p] + 1
|
||||
if x > D: x = D
|
||||
row[p] = x
|
||||
inc(p)
|
||||
# lower triangle sentinel:
|
||||
if i <= half:
|
||||
dec(D)
|
||||
var c3 = D + ord(char1 != b[char2p + s])
|
||||
inc(x)
|
||||
if x > c3: x = c3
|
||||
row[p] = x
|
||||
result = row[e]
|
||||
|
||||
|
||||
when isMainModule:
|
||||
doAssert editDistance("", "") == 0
|
||||
doAssert editDistance("kitten", "sitting") == 3 # from Wikipedia
|
||||
doAssert editDistance("flaw", "lawn") == 2 # from Wikipedia
|
||||
|
||||
doAssert editDistance("привет", "превет") == 1
|
||||
doAssert editDistance("Åge", "Age") == 1
|
||||
# editDistance, one string is longer in bytes, but shorter in rune length
|
||||
# first string: 4 bytes, second: 6 bytes, but only 3 runes
|
||||
doAssert editDistance("aaaa", "×××") == 4
|
||||
|
||||
block veryLongStringEditDistanceTest:
|
||||
const cap = 256
|
||||
var
|
||||
s1 = newStringOfCap(cap)
|
||||
s2 = newStringOfCap(cap)
|
||||
while len(s1) < cap:
|
||||
s1.add 'a'
|
||||
while len(s2) < cap:
|
||||
s2.add 'b'
|
||||
doAssert editDistance(s1, s2) == cap
|
||||
|
||||
block combiningCodePointsEditDistanceTest:
|
||||
const s = "A\xCC\x8Age"
|
||||
doAssert editDistance(s, "Age") == 1
|
||||
|
||||
doAssert editDistanceAscii("", "") == 0
|
||||
doAssert editDistanceAscii("kitten", "sitting") == 3 # from Wikipedia
|
||||
doAssert editDistanceAscii("flaw", "lawn") == 2 # from Wikipedia
|
||||
@@ -302,7 +302,7 @@ proc getCurrentEncoding*(): string =
|
||||
|
||||
proc open*(destEncoding = "UTF-8", srcEncoding = "CP1252"): EncodingConverter =
|
||||
## opens a converter that can convert from `srcEncoding` to `destEncoding`.
|
||||
## Raises `EIO` if it cannot fulfill the request.
|
||||
## Raises `IOError` if it cannot fulfill the request.
|
||||
when not defined(windows):
|
||||
result = iconvOpen(destEncoding, srcEncoding)
|
||||
if result == nil:
|
||||
|
||||
@@ -44,20 +44,23 @@ else:
|
||||
const useBuiltinSwap = false
|
||||
|
||||
when useBuiltinSwap:
|
||||
template swapOpImpl(T: typedesc, op: untyped) =
|
||||
## We have to use `copyMem` here instead of a simple deference because they
|
||||
## may point to a unaligned address. A sufficiently smart compiler _should_
|
||||
## be able to elide them when they're not necessary.
|
||||
var tmp: T
|
||||
copyMem(addr tmp, inp, sizeOf(T))
|
||||
tmp = op(tmp)
|
||||
copyMem(outp, addr tmp, sizeOf(T))
|
||||
|
||||
proc swapEndian64*(outp, inp: pointer) {.inline, nosideeffect.}=
|
||||
var i = cast[ptr uint64](inp)
|
||||
var o = cast[ptr uint64](outp)
|
||||
o[] = builtin_bswap64(i[])
|
||||
swapOpImpl(uint64, builtin_bswap64)
|
||||
|
||||
proc swapEndian32*(outp, inp: pointer) {.inline, nosideeffect.}=
|
||||
var i = cast[ptr uint32](inp)
|
||||
var o = cast[ptr uint32](outp)
|
||||
o[] = builtin_bswap32(i[])
|
||||
swapOpImpl(uint32, builtin_bswap32)
|
||||
|
||||
proc swapEndian16*(outp, inp: pointer) {.inline, nosideeffect.}=
|
||||
var i = cast[ptr uint16](inp)
|
||||
var o = cast[ptr uint16](outp)
|
||||
o[] = builtin_bswap16(i[])
|
||||
swapOpImpl(uint16, builtin_bswap16)
|
||||
|
||||
else:
|
||||
proc swapEndian64*(outp, inp: pointer) =
|
||||
|
||||
@@ -32,7 +32,7 @@ import
|
||||
|
||||
const
|
||||
coreAttr* = " accesskey class contenteditable dir hidden id lang " &
|
||||
"spellcheck style tabindex title translate "
|
||||
"spellcheck style tabindex title translate " ## HTML DOM Core Attributes
|
||||
eventAttr* = "onabort onblur oncancel oncanplay oncanplaythrough onchange " &
|
||||
"onclick oncuechange ondblclick ondurationchange onemptied onended " &
|
||||
"onerror onfocus oninput oninvalid onkeydown onkeypress onkeyup onload " &
|
||||
@@ -40,9 +40,9 @@ const
|
||||
"onmouseleave onmousemove onmouseout onmouseover onmouseup onmousewheel " &
|
||||
"onpause onplay onplaying onprogress onratechange onreset onresize " &
|
||||
"onscroll onseeked onseeking onselect onshow onstalled onsubmit " &
|
||||
"onsuspend ontimeupdate ontoggle onvolumechange onwaiting "
|
||||
ariaAttr* = " role "
|
||||
commonAttr* = coreAttr & eventAttr & ariaAttr
|
||||
"onsuspend ontimeupdate ontoggle onvolumechange onwaiting " ## HTML DOM Event Attributes
|
||||
ariaAttr* = " role " ## HTML DOM Aria Attributes
|
||||
commonAttr* = coreAttr & eventAttr & ariaAttr ## HTML DOM Common Attributes
|
||||
|
||||
proc getIdent(e: NimNode): string {.compileTime.} =
|
||||
case e.kind
|
||||
@@ -203,6 +203,11 @@ macro caption*(e: varargs[untyped]): untyped =
|
||||
let e = callsite()
|
||||
result = xmlCheckedTag(e, "caption", commonAttr)
|
||||
|
||||
macro center*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``center`` element.
|
||||
let e = callsite()
|
||||
result = xmlCheckedTag(e, "center", commonAttr)
|
||||
|
||||
macro cite*(e: varargs[untyped]): untyped =
|
||||
## generates the HTML ``cite`` element.
|
||||
let e = callsite()
|
||||
@@ -243,11 +248,21 @@ macro del*(e: varargs[untyped]): untyped =
|
||||
let e = callsite()
|
||||
result = xmlCheckedTag(e, "del", "cite datetime" & commonAttr)
|
||||
|
||||
macro details*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``details`` element.
|
||||
let e = callsite()
|
||||
result = xmlCheckedTag(e, "details", commonAttr & "open")
|
||||
|
||||
macro dfn*(e: varargs[untyped]): untyped =
|
||||
## generates the HTML ``dfn`` element.
|
||||
let e = callsite()
|
||||
result = xmlCheckedTag(e, "dfn", commonAttr)
|
||||
|
||||
macro dialog*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``dialog`` element.
|
||||
let e = callsite()
|
||||
result = xmlCheckedTag(e, "dialog", commonAttr & "open")
|
||||
|
||||
macro `div`*(e: varargs[untyped]): untyped =
|
||||
## generates the HTML ``div`` element.
|
||||
let e = callsite()
|
||||
@@ -428,6 +443,13 @@ macro mark*(e: varargs[untyped]): untyped =
|
||||
let e = callsite()
|
||||
result = xmlCheckedTag(e, "mark", commonAttr)
|
||||
|
||||
macro marquee*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``marquee`` element.
|
||||
let e = callsite()
|
||||
result = xmlCheckedTag(e, "marquee", coreAttr &
|
||||
"behavior bgcolor direction height hspace loop scrollamount " &
|
||||
"scrolldelay truespeed vspace width onbounce onfinish onstart")
|
||||
|
||||
macro meta*(e: varargs[untyped]): untyped =
|
||||
## generates the HTML ``meta`` element.
|
||||
let e = callsite()
|
||||
@@ -487,6 +509,11 @@ macro param*(e: varargs[untyped]): untyped =
|
||||
let e = callsite()
|
||||
result = xmlCheckedTag(e, "param", commonAttr, "name value", true)
|
||||
|
||||
macro picture*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``picture`` element.
|
||||
let e = callsite()
|
||||
result = xmlCheckedTag(e, "picture", commonAttr)
|
||||
|
||||
macro pre*(e: varargs[untyped]): untyped =
|
||||
## generates the HTML ``pre`` element.
|
||||
let e = callsite()
|
||||
@@ -554,6 +581,11 @@ macro select*(e: varargs[untyped]): untyped =
|
||||
result = xmlCheckedTag(e, "select", "autofocus disabled form multiple " &
|
||||
"name required size" & commonAttr)
|
||||
|
||||
macro slot*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``slot`` element.
|
||||
let e = callsite()
|
||||
result = xmlCheckedTag(e, "slot", commonAttr)
|
||||
|
||||
macro small*(e: varargs[untyped]): untyped =
|
||||
## generates the HTML ``small`` element.
|
||||
let e = callsite()
|
||||
@@ -584,6 +616,11 @@ macro sub*(e: varargs[untyped]): untyped =
|
||||
let e = callsite()
|
||||
result = xmlCheckedTag(e, "sub", commonAttr)
|
||||
|
||||
macro summary*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``summary`` element.
|
||||
let e = callsite()
|
||||
result = xmlCheckedTag(e, "summary", commonAttr)
|
||||
|
||||
macro sup*(e: varargs[untyped]): untyped =
|
||||
## generates the HTML ``sup`` element.
|
||||
let e = callsite()
|
||||
@@ -684,7 +721,7 @@ macro wbr*(e: varargs[untyped]): untyped =
|
||||
let e = callsite()
|
||||
result = xmlCheckedTag(e, "wbr", commonAttr, "", true)
|
||||
|
||||
when isMainModule:
|
||||
runnableExamples:
|
||||
let nim = "Nim"
|
||||
assert h1(a(href="http://nim-lang.org", nim)) ==
|
||||
"""<h1><a href="http://nim-lang.org">Nim</a></h1>"""
|
||||
|
||||
@@ -383,14 +383,14 @@ proc selectInto*[T](s: Selector[T], timeout: int,
|
||||
|
||||
if (pevents and EPOLLERR) != 0 or (pevents and EPOLLHUP) != 0:
|
||||
if (pevents and EPOLLHUP) != 0:
|
||||
rkey.errorCode = ECONNRESET.OSErrorCode
|
||||
rkey.errorCode = OSErrorCode ECONNRESET
|
||||
else:
|
||||
# Try reading SO_ERROR from fd.
|
||||
var error: cint
|
||||
var size = sizeof(error).SockLen
|
||||
if getsockopt(fdi.SocketHandle, SOL_SOCKET, SO_ERROR, addr(error),
|
||||
var size = SockLen sizeof(error)
|
||||
if getsockopt(SocketHandle fdi, SOL_SOCKET, SO_ERROR, addr(error),
|
||||
addr(size)) == 0'i32:
|
||||
rkey.errorCode = error.OSErrorCode
|
||||
rkey.errorCode = OSErrorCode error
|
||||
|
||||
rkey.events.incl(Event.Error)
|
||||
if (pevents and EPOLLOUT) != 0:
|
||||
|
||||
@@ -256,12 +256,12 @@ proc loadAny(s: Stream, a: Any, t: var Table[BiggestInt, pointer]) =
|
||||
close(p)
|
||||
|
||||
proc load*[T](s: Stream, data: var T) =
|
||||
## loads `data` from the stream `s`. Raises `EIO` in case of an error.
|
||||
## loads `data` from the stream `s`. Raises `IOError` in case of an error.
|
||||
var tab = initTable[BiggestInt, pointer]()
|
||||
loadAny(s, toAny(data), tab)
|
||||
|
||||
proc store*[T](s: Stream, data: T) =
|
||||
## stores `data` into the stream `s`. Raises `EIO` in case of an error.
|
||||
## stores `data` into the stream `s`. Raises `IOError` in case of an error.
|
||||
var stored = initIntSet()
|
||||
var d: T
|
||||
shallowCopy(d, data)
|
||||
|
||||
@@ -24,7 +24,10 @@ include "system/inclrtl"
|
||||
import bitops
|
||||
|
||||
proc binom*(n, k: int): int {.noSideEffect.} =
|
||||
## Computes the binomial coefficient
|
||||
## Computes the `binomial coefficient <https://en.wikipedia.org/wiki/Binomial_coefficient>`_.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo binom(6, 2) ## 15
|
||||
if k <= 0: return 1
|
||||
if 2*k > n: return binom(n, n-k)
|
||||
result = n
|
||||
@@ -37,7 +40,10 @@ proc createFactTable[N: static[int]]: array[N, int] =
|
||||
result[i] = result[i - 1] * i
|
||||
|
||||
proc fac*(n: int): int =
|
||||
## Computes the faculty/factorial function.
|
||||
## Computes the `factorial <https://en.wikipedia.org/wiki/Factorial>`_ of a non-negative integer ``n``
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo fac(4) ## 24
|
||||
const factTable =
|
||||
when sizeof(int) == 4:
|
||||
createFactTable[13]()
|
||||
@@ -81,8 +87,13 @@ type
|
||||
fcNegInf ## value is negative infinity
|
||||
|
||||
proc classify*(x: float): FloatClass =
|
||||
## Classifies a floating point value. Returns `x`'s class as specified by
|
||||
## Classifies a floating point value. Returns ``x``'s class as specified by
|
||||
## `FloatClass`.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo classify(0.3) ## fcNormal
|
||||
## echo classify(0.0) ## fcZero
|
||||
## echo classify(0.3/0.0) ## fcInf
|
||||
|
||||
# JavaScript and most C compilers have no classify:
|
||||
if x == 0.0:
|
||||
@@ -98,13 +109,21 @@ proc classify*(x: float): FloatClass =
|
||||
# XXX: fcSubnormal is not detected!
|
||||
|
||||
proc isPowerOfTwo*(x: int): bool {.noSideEffect.} =
|
||||
## Returns true, if `x` is a power of two, false otherwise.
|
||||
## Returns ``true``, if ``x`` is a power of two, ``false`` otherwise.
|
||||
## Zero and negative numbers are not a power of two.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo isPowerOfTwo(5) ## false
|
||||
## echo isPowerOfTwo(8) ## true
|
||||
return (x > 0) and ((x and (x - 1)) == 0)
|
||||
|
||||
proc nextPowerOfTwo*(x: int): int {.noSideEffect.} =
|
||||
## Returns `x` rounded up to the nearest power of two.
|
||||
## Returns ``x`` rounded up to the nearest power of two.
|
||||
## Zero and negative numbers get rounded up to 1.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo nextPowerOfTwo(8) ## 8
|
||||
## echo nextPowerOfTwo(9) ## 16
|
||||
result = x - 1
|
||||
when defined(cpu64):
|
||||
result = result or (result shr 32)
|
||||
@@ -118,20 +137,29 @@ proc nextPowerOfTwo*(x: int): int {.noSideEffect.} =
|
||||
result += 1 + ord(x<=0)
|
||||
|
||||
proc countBits32*(n: int32): int {.noSideEffect.} =
|
||||
## Counts the set bits in `n`.
|
||||
## Counts the set bits in ``n``.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo countBits32(13'i32) ## 3
|
||||
var v = n
|
||||
v = v -% ((v shr 1'i32) and 0x55555555'i32)
|
||||
v = (v and 0x33333333'i32) +% ((v shr 2'i32) and 0x33333333'i32)
|
||||
result = ((v +% (v shr 4'i32) and 0xF0F0F0F'i32) *% 0x1010101'i32) shr 24'i32
|
||||
|
||||
proc sum*[T](x: openArray[T]): T {.noSideEffect.} =
|
||||
## Computes the sum of the elements in `x`.
|
||||
## If `x` is empty, 0 is returned.
|
||||
## Computes the sum of the elements in ``x``.
|
||||
## If ``x`` is empty, 0 is returned.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo sum([1.0, 2.5, -3.0, 4.3]) ## 4.8
|
||||
for i in items(x): result = result + i
|
||||
|
||||
proc prod*[T](x: openArray[T]): T {.noSideEffect.} =
|
||||
## Computes the product of the elements in ``x``.
|
||||
## If ``x`` is empty, 1 is returned.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo prod([1.0, 3.0, -0.2]) ## -0.6
|
||||
result = 1.T
|
||||
for i in items(x): result = result * i
|
||||
|
||||
@@ -139,14 +167,22 @@ proc prod*[T](x: openArray[T]): T {.noSideEffect.} =
|
||||
when not defined(JS): # C
|
||||
proc sqrt*(x: float32): float32 {.importc: "sqrtf", header: "<math.h>".}
|
||||
proc sqrt*(x: float64): float64 {.importc: "sqrt", header: "<math.h>".}
|
||||
## Computes the square root of `x`.
|
||||
## Computes the square root of ``x``.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo sqrt(1.44) ## 1.2
|
||||
proc cbrt*(x: float32): float32 {.importc: "cbrtf", header: "<math.h>".}
|
||||
proc cbrt*(x: float64): float64 {.importc: "cbrt", header: "<math.h>".}
|
||||
## Computes the cubic root of `x`
|
||||
|
||||
## Computes the cubic root of ``x``.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo cbrt(2.197) ## 1.3
|
||||
proc ln*(x: float32): float32 {.importc: "logf", header: "<math.h>".}
|
||||
proc ln*(x: float64): float64 {.importc: "log", header: "<math.h>".}
|
||||
## Computes the natural log of `x`
|
||||
## Computes the `natural logarithm <https://en.wikipedia.org/wiki/Natural_logarithm>`_ of ``x``.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo ln(exp(4.0)) ## 4.0
|
||||
else: # JS
|
||||
proc sqrt*(x: float32): float32 {.importc: "Math.sqrt", nodecl.}
|
||||
proc sqrt*(x: float64): float64 {.importc: "Math.sqrt", nodecl.}
|
||||
@@ -155,62 +191,101 @@ else: # JS
|
||||
proc ln*(x: float64): float64 {.importc: "Math.log", nodecl.}
|
||||
|
||||
proc log*[T: SomeFloat](x, base: T): T =
|
||||
## Computes the logarithm ``base`` of ``x``
|
||||
## Computes the logarithm of ``x`` to base ``base``.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo log(9.0, 3.0) ## 2.0
|
||||
ln(x) / ln(base)
|
||||
|
||||
when not defined(JS): # C
|
||||
proc log10*(x: float32): float32 {.importc: "log10f", header: "<math.h>".}
|
||||
proc log10*(x: float64): float64 {.importc: "log10", header: "<math.h>".}
|
||||
## Computes the common logarithm (base 10) of `x`
|
||||
## Computes the common logarithm (base 10) of ``x``.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo log10(100.0) ## 2.0
|
||||
proc exp*(x: float32): float32 {.importc: "expf", header: "<math.h>".}
|
||||
proc exp*(x: float64): float64 {.importc: "exp", header: "<math.h>".}
|
||||
## Computes the exponential function of `x` (pow(E, x))
|
||||
|
||||
## Computes the exponential function of ``x`` (pow(E, x)).
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo exp(1.0) ## 2.718281828459045
|
||||
## echo ln(exp(4.0)) ## 4.0
|
||||
proc sin*(x: float32): float32 {.importc: "sinf", header: "<math.h>".}
|
||||
proc sin*(x: float64): float64 {.importc: "sin", header: "<math.h>".}
|
||||
## Computes the sine of `x`
|
||||
## Computes the sine of ``x``.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo sin(PI / 6) ## 0.4999999999999999
|
||||
## echo sin(degToRad(90.0)) ## 1.0
|
||||
proc cos*(x: float32): float32 {.importc: "cosf", header: "<math.h>".}
|
||||
proc cos*(x: float64): float64 {.importc: "cos", header: "<math.h>".}
|
||||
## Computes the cosine of `x`
|
||||
## Computes the cosine of ``x``.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo cos(2 * PI) ## 1.0
|
||||
## echo cos(degToRad(60.0)) ## 0.5000000000000001
|
||||
proc tan*(x: float32): float32 {.importc: "tanf", header: "<math.h>".}
|
||||
proc tan*(x: float64): float64 {.importc: "tan", header: "<math.h>".}
|
||||
## Computes the tangent of `x`
|
||||
|
||||
## Computes the tangent of ``x``.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo tan(degToRad(45.0)) ## 0.9999999999999999
|
||||
## echo tan(PI / 4) ## 0.9999999999999999
|
||||
proc sinh*(x: float32): float32 {.importc: "sinhf", header: "<math.h>".}
|
||||
proc sinh*(x: float64): float64 {.importc: "sinh", header: "<math.h>".}
|
||||
## Computes the hyperbolic sine of `x`
|
||||
## Computes the `hyperbolic sine <https://en.wikipedia.org/wiki/Hyperbolic_function#Definitions>`_ of ``x``.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo sinh(1.0) ## 1.175201193643801
|
||||
proc cosh*(x: float32): float32 {.importc: "coshf", header: "<math.h>".}
|
||||
proc cosh*(x: float64): float64 {.importc: "cosh", header: "<math.h>".}
|
||||
## Computes the hyperbolic cosine of `x`
|
||||
## Computes the `hyperbolic cosine <https://en.wikipedia.org/wiki/Hyperbolic_function#Definitions>`_ of ``x``.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo cosh(1.0) ## 1.543080634815244
|
||||
proc tanh*(x: float32): float32 {.importc: "tanhf", header: "<math.h>".}
|
||||
proc tanh*(x: float64): float64 {.importc: "tanh", header: "<math.h>".}
|
||||
## Computes the hyperbolic tangent of `x`
|
||||
## Computes the `hyperbolic tangent <https://en.wikipedia.org/wiki/Hyperbolic_function#Definitions>`_ of ``x``.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo tanh(1.0) ## 0.7615941559557649
|
||||
|
||||
proc arccos*(x: float32): float32 {.importc: "acosf", header: "<math.h>".}
|
||||
proc arccos*(x: float64): float64 {.importc: "acos", header: "<math.h>".}
|
||||
## Computes the arc cosine of `x`
|
||||
## Computes the arc cosine of ``x``.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo arccos(1.0) ## 0.0
|
||||
proc arcsin*(x: float32): float32 {.importc: "asinf", header: "<math.h>".}
|
||||
proc arcsin*(x: float64): float64 {.importc: "asin", header: "<math.h>".}
|
||||
## Computes the arc sine of `x`
|
||||
## Computes the arc sine of ``x``.
|
||||
proc arctan*(x: float32): float32 {.importc: "atanf", header: "<math.h>".}
|
||||
proc arctan*(x: float64): float64 {.importc: "atan", header: "<math.h>".}
|
||||
## Calculate the arc tangent of `y` / `x`
|
||||
## Calculate the arc tangent of ``x``.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo arctan(1.0) ## 0.7853981633974483
|
||||
## echo radToDeg(arctan(1.0)) ## 45.0
|
||||
proc arctan2*(y, x: float32): float32 {.importc: "atan2f", header: "<math.h>".}
|
||||
proc arctan2*(y, x: float64): float64 {.importc: "atan2", header: "<math.h>".}
|
||||
## Calculate the arc tangent of `y` / `x`.
|
||||
## `atan2` returns the arc tangent of `y` / `x`; it produces correct
|
||||
## Calculate the arc tangent of ``y`` / ``x``.
|
||||
## `arctan2` returns the arc tangent of ``y`` / ``x``; it produces correct
|
||||
## results even when the resulting angle is near pi/2 or -pi/2
|
||||
## (`x` near 0).
|
||||
|
||||
## (``x`` near 0).
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo arctan2(1.0, 0.0) ## 1.570796326794897
|
||||
## echo radToDeg(arctan2(1.0, 0.0)) ## 90.0
|
||||
proc arcsinh*(x: float32): float32 {.importc: "asinhf", header: "<math.h>".}
|
||||
proc arcsinh*(x: float64): float64 {.importc: "asinh", header: "<math.h>".}
|
||||
## Computes the inverse hyperbolic sine of `x`
|
||||
## Computes the inverse hyperbolic sine of ``x``.
|
||||
proc arccosh*(x: float32): float32 {.importc: "acoshf", header: "<math.h>".}
|
||||
proc arccosh*(x: float64): float64 {.importc: "acosh", header: "<math.h>".}
|
||||
## Computes the inverse hyperbolic cosine of `x`
|
||||
## Computes the inverse hyperbolic cosine of ``x``.
|
||||
proc arctanh*(x: float32): float32 {.importc: "atanhf", header: "<math.h>".}
|
||||
proc arctanh*(x: float64): float64 {.importc: "atanh", header: "<math.h>".}
|
||||
## Computes the inverse hyperbolic tangent of `x`
|
||||
## Computes the inverse hyperbolic tangent of ``x``.
|
||||
|
||||
else: # JS
|
||||
proc log10*(x: float32): float32 {.importc: "Math.log10", nodecl.}
|
||||
@@ -238,59 +313,63 @@ else: # JS
|
||||
proc arctanh*[T: float32|float64](x: T): T {.importc: "Math.atanh", nodecl.}
|
||||
|
||||
proc cot*[T: float32|float64](x: T): T = 1.0 / tan(x)
|
||||
## Computes the cotangent of `x`
|
||||
## Computes the cotangent of ``x``.
|
||||
proc sec*[T: float32|float64](x: T): T = 1.0 / cos(x)
|
||||
## Computes the secant of `x`.
|
||||
## Computes the secant of ``x``.
|
||||
proc csc*[T: float32|float64](x: T): T = 1.0 / sin(x)
|
||||
## Computes the cosecant of `x`
|
||||
## Computes the cosecant of ``x``.
|
||||
|
||||
proc coth*[T: float32|float64](x: T): T = 1.0 / tanh(x)
|
||||
## Computes the hyperbolic cotangent of `x`
|
||||
## Computes the hyperbolic cotangent of ``x``.
|
||||
proc sech*[T: float32|float64](x: T): T = 1.0 / cosh(x)
|
||||
## Computes the hyperbolic secant of `x`
|
||||
## Computes the hyperbolic secant of ``x``.
|
||||
proc csch*[T: float32|float64](x: T): T = 1.0 / sinh(x)
|
||||
## Computes the hyperbolic cosecant of `x`
|
||||
## Computes the hyperbolic cosecant of ``x``.
|
||||
|
||||
proc arccot*[T: float32|float64](x: T): T = arctan(1.0 / x)
|
||||
## Computes the inverse cotangent of `x`
|
||||
## Computes the inverse cotangent of ``x``.
|
||||
proc arcsec*[T: float32|float64](x: T): T = arccos(1.0 / x)
|
||||
## Computes the inverse secant of `x`
|
||||
## Computes the inverse secant of ``x``.
|
||||
proc arccsc*[T: float32|float64](x: T): T = arcsin(1.0 / x)
|
||||
## Computes the inverse cosecant of `x`
|
||||
## Computes the inverse cosecant of ``x``.
|
||||
|
||||
proc arccoth*[T: float32|float64](x: T): T = arctanh(1.0 / x)
|
||||
## Computes the inverse hyperbolic cotangent of `x`
|
||||
## Computes the inverse hyperbolic cotangent of ``x``.
|
||||
proc arcsech*[T: float32|float64](x: T): T = arccosh(1.0 / x)
|
||||
## Computes the inverse hyperbolic secant of `x`
|
||||
## Computes the inverse hyperbolic secant of ``x``.
|
||||
proc arccsch*[T: float32|float64](x: T): T = arcsinh(1.0 / x)
|
||||
## Computes the inverse hyperbolic cosecant of `x`
|
||||
## Computes the inverse hyperbolic cosecant of ``x``.
|
||||
|
||||
const windowsCC89 = defined(windows) and defined(bcc)
|
||||
|
||||
when not defined(JS): # C
|
||||
proc hypot*(x, y: float32): float32 {.importc: "hypotf", header: "<math.h>".}
|
||||
proc hypot*(x, y: float64): float64 {.importc: "hypot", header: "<math.h>".}
|
||||
## Computes the hypotenuse of a right-angle triangle with `x` and
|
||||
## `y` as its base and height. Equivalent to ``sqrt(x*x + y*y)``.
|
||||
|
||||
## Computes the hypotenuse of a right-angle triangle with ``x`` and
|
||||
## ``y`` as its base and height. Equivalent to ``sqrt(x*x + y*y)``.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo hypot(4.0, 3.0) ## 5.0
|
||||
proc pow*(x, y: float32): float32 {.importc: "powf", header: "<math.h>".}
|
||||
proc pow*(x, y: float64): float64 {.importc: "pow", header: "<math.h>".}
|
||||
## computes x to power raised of y.
|
||||
##
|
||||
## To compute power between integers, use `^` e.g. 2 ^ 6
|
||||
## To compute power between integers, use ``^`` e.g. 2 ^ 6
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo pow(16.0, 0.5) ## 4.0
|
||||
|
||||
# TODO: add C89 version on windows
|
||||
when not windowsCC89:
|
||||
proc erf*(x: float32): float32 {.importc: "erff", header: "<math.h>".}
|
||||
proc erf*(x: float64): float64 {.importc: "erf", header: "<math.h>".}
|
||||
## The error function
|
||||
## Computes the `error function <https://en.wikipedia.org/wiki/Error_function>`_ for ``x``.
|
||||
proc erfc*(x: float32): float32 {.importc: "erfcf", header: "<math.h>".}
|
||||
proc erfc*(x: float64): float64 {.importc: "erfc", header: "<math.h>".}
|
||||
## The complementary error function
|
||||
|
||||
## Computes the `complementary error function <https://en.wikipedia.org/wiki/Error_function#Complementary_error_function>`_ for ``x``.
|
||||
proc gamma*(x: float32): float32 {.importc: "tgammaf", header: "<math.h>".}
|
||||
proc gamma*(x: float64): float64 {.importc: "tgamma", header: "<math.h>".}
|
||||
## The gamma function
|
||||
## Computes the the `gamma function <https://en.wikipedia.org/wiki/Gamma_function>`_ for ``x``.
|
||||
proc tgamma*(x: float32): float32
|
||||
{.deprecated: "use gamma instead", importc: "tgammaf", header: "<math.h>".}
|
||||
proc tgamma*(x: float64): float64
|
||||
@@ -299,18 +378,18 @@ when not defined(JS): # C
|
||||
## **Deprecated since version 0.19.0**: Use ``gamma`` instead.
|
||||
proc lgamma*(x: float32): float32 {.importc: "lgammaf", header: "<math.h>".}
|
||||
proc lgamma*(x: float64): float64 {.importc: "lgamma", header: "<math.h>".}
|
||||
## Natural log of the gamma function
|
||||
## Computes the natural log of the gamma function for ``x``.
|
||||
|
||||
proc floor*(x: float32): float32 {.importc: "floorf", header: "<math.h>".}
|
||||
proc floor*(x: float64): float64 {.importc: "floor", header: "<math.h>".}
|
||||
## Computes the floor function (i.e., the largest integer not greater than `x`)
|
||||
## Computes the floor function (i.e., the largest integer not greater than ``x``).
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo floor(-3.5) ## -4.0
|
||||
|
||||
proc ceil*(x: float32): float32 {.importc: "ceilf", header: "<math.h>".}
|
||||
proc ceil*(x: float64): float64 {.importc: "ceil", header: "<math.h>".}
|
||||
## Computes the ceiling function (i.e., the smallest integer not less than `x`)
|
||||
## Computes the ceiling function (i.e., the smallest integer not less than ``x``).
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo ceil(-2.1) ## -2.0
|
||||
@@ -378,21 +457,27 @@ when not defined(JS): # C
|
||||
|
||||
proc trunc*(x: float32): float32 {.importc: "truncf", header: "<math.h>".}
|
||||
proc trunc*(x: float64): float64 {.importc: "trunc", header: "<math.h>".}
|
||||
## Truncates `x` to the decimal point
|
||||
## Truncates ``x`` to the decimal point.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo trunc(PI) # 3.0
|
||||
## echo trunc(-1.85) # -1.0
|
||||
|
||||
proc fmod*(x, y: float32): float32 {.deprecated: "use mod instead", importc: "fmodf", header: "<math.h>".}
|
||||
proc fmod*(x, y: float64): float64 {.deprecated: "use mod instead", importc: "fmod", header: "<math.h>".}
|
||||
## Computes the remainder of `x` divided by `y`
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo fmod(-2.5, 0.3) ## -0.1
|
||||
## Computes the remainder of ``x`` divided by ``y``.
|
||||
## **Deprecated since version 0.19.0**: Use the ``mod`` operator instead.
|
||||
|
||||
proc `mod`*(x, y: float32): float32 {.importc: "fmodf", header: "<math.h>".}
|
||||
proc `mod`*(x, y: float64): float64 {.importc: "fmod", header: "<math.h>".}
|
||||
## Computes the modulo operation for float operators.
|
||||
## Computes the modulo operation for float values (the remainder of ``x`` divided by ``y``).
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## ( 6.5 mod 2.5) == 1.5
|
||||
## (-6.5 mod 2.5) == -1.5
|
||||
## ( 6.5 mod -2.5) == 1.5
|
||||
## (-6.5 mod -2.5) == -1.5
|
||||
|
||||
else: # JS
|
||||
proc hypot*[T: float32|float64](x, y: T): T = return sqrt(x*x + y*y)
|
||||
proc pow*(x, y: float32): float32 {.importC: "Math.pow", nodecl.}
|
||||
@@ -407,17 +492,25 @@ else: # JS
|
||||
|
||||
proc `mod`*(x, y: float32): float32 {.importcpp: "# % #".}
|
||||
proc `mod`*(x, y: float64): float64 {.importcpp: "# % #".}
|
||||
## Computes the modulo operation for float operators.
|
||||
## Computes the modulo operation for float values (the remainder of ``x`` divided by ``y``).
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## ( 6.5 mod 2.5) == 1.5
|
||||
## (-6.5 mod 2.5) == -1.5
|
||||
## ( 6.5 mod -2.5) == 1.5
|
||||
## (-6.5 mod -2.5) == -1.5
|
||||
|
||||
proc round*[T: float32|float64](x: T, places: int = 0): T =
|
||||
## Round a floating point number.
|
||||
##
|
||||
## If `places` is 0 (or omitted), round to the nearest integral value
|
||||
## following normal mathematical rounding rules (e.g. `round(54.5) -> 55.0`).
|
||||
## If `places` is greater than 0, round to the given number of decimal
|
||||
## places, e.g. `round(54.346, 2) -> 54.35`.
|
||||
## If `places` is negative, round to the left of the decimal place, e.g.
|
||||
## `round(537.345, -1) -> 540.0`
|
||||
## This function is NOT reliable. Floating point numbers cannot hold
|
||||
## non integer decimals precisely. If ``places`` is 0 (or omitted),
|
||||
## round to the nearest integral value following normal mathematical
|
||||
## rounding rules (e.g. ``round(54.5) -> 55.0``). If ``places`` is
|
||||
## greater than 0, round to the given number of decimal places,
|
||||
## e.g. ``round(54.346, 2) -> 54.350000000000001421...``. If ``places`` is negative, round
|
||||
## to the left of the decimal place, e.g. ``round(537.345, -1) ->
|
||||
## 540.0``
|
||||
if places == 0:
|
||||
result = round0(x)
|
||||
else:
|
||||
@@ -429,13 +522,25 @@ proc floorDiv*[T: SomeInteger](x, y: T): T =
|
||||
## This is different from the ``div`` operator, which is defined
|
||||
## as ``trunc(x / y)``. That is, ``div`` rounds towards ``0`` and ``floorDiv``
|
||||
## rounds down.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo floorDiv( 13, 3) # 4
|
||||
## echo floorDiv(-13, 3) # -5
|
||||
## echo floorDiv( 13, -3) # -5
|
||||
## echo floorDiv(-13, -3) # 4
|
||||
result = x div y
|
||||
let r = x mod y
|
||||
if (r > 0 and y < 0) or (r < 0 and y > 0): result.dec 1
|
||||
|
||||
proc floorMod*[T: SomeNumber](x, y: T): T =
|
||||
## Floor modulus is conceptually defined as ``x - (floorDiv(x, y) * y).
|
||||
## This proc behaves the same as the ``%`` operator in python.
|
||||
## Floor modulus is conceptually defined as ``x - (floorDiv(x, y) * y)``.
|
||||
## This proc behaves the same as the ``%`` operator in Python.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo floorMod( 13, 3) # 1
|
||||
## echo floorMod(-13, 3) # 2
|
||||
## echo floorMod( 13, -3) # -2
|
||||
## echo floorMod(-13, -3) # -1
|
||||
result = x mod y
|
||||
if (result > 0 and y < 0) or (result < 0 and y > 0): result += y
|
||||
|
||||
@@ -446,10 +551,15 @@ when not defined(JS):
|
||||
importc: "frexp", header: "<math.h>".}
|
||||
proc frexp*[T, U](x: T, exponent: var U): T =
|
||||
## Split a number into mantissa and exponent.
|
||||
## `frexp` calculates the mantissa m (a float greater than or equal to 0.5
|
||||
## and less than 1) and the integer value n such that `x` (the original
|
||||
## float value) equals m * 2**n. frexp stores n in `exponent` and returns
|
||||
## ``frexp`` calculates the mantissa m (a float greater than or equal to 0.5
|
||||
## and less than 1) and the integer value n such that ``x`` (the original
|
||||
## float value) equals ``m * 2**n``. frexp stores n in `exponent` and returns
|
||||
## m.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## var x : int
|
||||
## echo frexp(5.0, x) # 0.625
|
||||
## echo x # 3
|
||||
var exp: int32
|
||||
result = c_frexp(x, exp)
|
||||
exponent = exp
|
||||
@@ -473,7 +583,7 @@ when not defined(JS):
|
||||
else:
|
||||
proc log2*(x: float32): float32 {.importc: "log2f", header: "<math.h>".}
|
||||
proc log2*(x: float64): float64 {.importc: "log2", header: "<math.h>".}
|
||||
## Computes the binary logarithm (base 2) of `x`
|
||||
## Computes the binary logarithm (base 2) of ``x``
|
||||
|
||||
else:
|
||||
proc frexp*[T: float32|float64](x: T, exponent: var int): T =
|
||||
@@ -493,13 +603,16 @@ else:
|
||||
result = 0.99999999999999988898
|
||||
|
||||
proc splitDecimal*[T: float32|float64](x: T): tuple[intpart: T, floatpart: T] =
|
||||
## Breaks `x` into an integral and a fractional part.
|
||||
## Breaks ``x`` into an integer and a fractional part.
|
||||
##
|
||||
## Returns a tuple containing intpart and floatpart representing
|
||||
## Returns a tuple containing ``intpart`` and ``floatpart`` representing
|
||||
## the integer part and the fractional part respectively.
|
||||
##
|
||||
## Both parts have the same sign as `x`. Analogous to the `modf`
|
||||
## Both parts have the same sign as ``x``. Analogous to the ``modf``
|
||||
## function in C.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo splitDecimal(5.25) # (intpart: 5.0, floatpart: 0.25)
|
||||
var
|
||||
absolute: T
|
||||
absolute = abs(x)
|
||||
@@ -513,16 +626,26 @@ proc splitDecimal*[T: float32|float64](x: T): tuple[intpart: T, floatpart: T] =
|
||||
|
||||
proc degToRad*[T: float32|float64](d: T): T {.inline.} =
|
||||
## Convert from degrees to radians
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo degToRad(180.0) # 3.141592653589793
|
||||
result = T(d) * RadPerDeg
|
||||
|
||||
proc radToDeg*[T: float32|float64](d: T): T {.inline.} =
|
||||
## Convert from radians to degrees
|
||||
|
||||
## .. code-block:: nim
|
||||
## echo degToRad(2 * PI) # 360.0
|
||||
result = T(d) / RadPerDeg
|
||||
|
||||
proc sgn*[T: SomeNumber](x: T): int {.inline.} =
|
||||
## Sign function. Returns -1 for negative numbers and `NegInf`, 1 for
|
||||
## positive numbers and `Inf`, and 0 for positive zero, negative zero and
|
||||
## `NaN`.
|
||||
## Sign function. Returns -1 for negative numbers and ``NegInf``, 1 for
|
||||
## positive numbers and ``Inf``, and 0 for positive zero, negative zero and
|
||||
## ``NaN``.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo sgn(-5) # 1
|
||||
## echo sgn(-4.1) # -1
|
||||
ord(T(0) < x) - ord(x < T(0))
|
||||
|
||||
{.pop.}
|
||||
@@ -531,6 +654,9 @@ proc sgn*[T: SomeNumber](x: T): int {.inline.} =
|
||||
proc `^`*[T](x: T, y: Natural): T =
|
||||
## Computes ``x`` to the power ``y``. ``x`` must be non-negative, use
|
||||
## `pow <#pow,float,float>`_ for negative exponents.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo 2 ^ 3 # 8
|
||||
when compiles(y >= T(0)):
|
||||
assert y >= T(0)
|
||||
else:
|
||||
@@ -560,6 +686,9 @@ proc gcd*[T](x, y: T): T =
|
||||
proc gcd*(x, y: SomeInteger): SomeInteger =
|
||||
## Computes the greatest common (positive) divisor of ``x`` and ``y``.
|
||||
## Using binary GCD (aka Stein's) algorithm.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo gcd(24, 30) # 6
|
||||
when x is SomeSignedInt:
|
||||
var x = abs(x)
|
||||
else:
|
||||
@@ -585,6 +714,9 @@ proc gcd*(x, y: SomeInteger): SomeInteger =
|
||||
|
||||
proc lcm*[T](x, y: T): T =
|
||||
## Computes the least common multiple of ``x`` and ``y``.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## echo lcm(24, 30) # 120
|
||||
x div gcd(x, y) * y
|
||||
|
||||
when isMainModule and not defined(JS) and not windowsCC89:
|
||||
|
||||
@@ -90,7 +90,7 @@ proc unmapMem*(f: var MemFile, p: pointer, size: int) =
|
||||
proc open*(filename: string, mode: FileMode = fmRead,
|
||||
mappedSize = -1, offset = 0, newFileSize = -1,
|
||||
allowRemap = false): MemFile =
|
||||
## opens a memory mapped file. If this fails, ``EOS`` is raised.
|
||||
## opens a memory mapped file. If this fails, ``OSError`` is raised.
|
||||
##
|
||||
## ``newFileSize`` can only be set if the file does not exist and is opened
|
||||
## with write access (e.g., with fmReadWrite).
|
||||
@@ -141,7 +141,7 @@ proc open*(filename: string, mode: FileMode = fmRead,
|
||||
if result.mapHandle != 0: discard closeHandle(result.mapHandle)
|
||||
raiseOSError(errCode)
|
||||
# return false
|
||||
#raise newException(EIO, msg)
|
||||
#raise newException(IOError, msg)
|
||||
|
||||
template callCreateFile(winApiProc, filename): untyped =
|
||||
winApiProc(
|
||||
@@ -465,7 +465,7 @@ proc mmsWriteData(s: Stream, buffer: pointer, bufLen: int) =
|
||||
proc newMemMapFileStream*(filename: string, mode: FileMode = fmRead, fileSize: int = -1):
|
||||
MemMapFileStream =
|
||||
## creates a new stream from the file named `filename` with the mode `mode`.
|
||||
## Raises ## `EOS` if the file cannot be opened. See the `system
|
||||
## Raises ## `OSError` if the file cannot be opened. See the `system
|
||||
## <system.html>`_ module for a list of available FileMode enums.
|
||||
## ``fileSize`` can only be set if the file does not exist and is opened
|
||||
## with write access (e.g., with fmReadWrite).
|
||||
|
||||
@@ -221,7 +221,7 @@ proc close*(socket: SocketHandle) =
|
||||
discard winlean.closesocket(socket)
|
||||
else:
|
||||
discard posix.close(socket)
|
||||
# TODO: These values should not be discarded. An EOS should be raised.
|
||||
# TODO: These values should not be discarded. An OSError should be raised.
|
||||
# http://stackoverflow.com/questions/12463473/what-happens-if-you-call-close-on-a-bsd-socket-multiple-times
|
||||
|
||||
proc bindAddr*(socket: SocketHandle, name: ptr SockAddr, namelen: SockLen): cint =
|
||||
@@ -594,7 +594,7 @@ proc setSockOptInt*(socket: SocketHandle, level, optname, optval: int) {.
|
||||
proc setBlocking*(s: SocketHandle, blocking: bool) =
|
||||
## Sets blocking mode on socket.
|
||||
##
|
||||
## Raises EOS on error.
|
||||
## Raises OSError on error.
|
||||
when useWinVersion:
|
||||
var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
|
||||
if ioctlsocket(s, FIONBIO, addr(mode)) == -1:
|
||||
|
||||
@@ -210,7 +210,7 @@ proc newSocket*(fd: SocketHandle, domain: Domain = AF_INET,
|
||||
proc newSocket*(domain, sockType, protocol: cint, buffered = true): Socket =
|
||||
## Creates a new socket.
|
||||
##
|
||||
## If an error occurs EOS will be raised.
|
||||
## If an error occurs OSError will be raised.
|
||||
let fd = createNativeSocket(domain, sockType, protocol)
|
||||
if fd == osInvalidSocket:
|
||||
raiseOSError(osLastError())
|
||||
@@ -221,7 +221,7 @@ proc newSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM,
|
||||
protocol: Protocol = IPPROTO_TCP, buffered = true): Socket =
|
||||
## Creates a new socket.
|
||||
##
|
||||
## If an error occurs EOS will be raised.
|
||||
## If an error occurs OSError will be raised.
|
||||
let fd = createNativeSocket(domain, sockType, protocol)
|
||||
if fd == osInvalidSocket:
|
||||
raiseOSError(osLastError())
|
||||
@@ -229,7 +229,7 @@ proc newSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM,
|
||||
|
||||
proc parseIPv4Address(addressStr: string): IpAddress =
|
||||
## Parses IPv4 adresses
|
||||
## Raises EInvalidValue on errors
|
||||
## Raises ValueError on errors
|
||||
var
|
||||
byteCount = 0
|
||||
currentByte:uint16 = 0
|
||||
@@ -263,7 +263,7 @@ proc parseIPv4Address(addressStr: string): IpAddress =
|
||||
|
||||
proc parseIPv6Address(addressStr: string): IpAddress =
|
||||
## Parses IPv6 adresses
|
||||
## Raises EInvalidValue on errors
|
||||
## Raises ValueError on errors
|
||||
result.family = IpAddressFamily.IPv6
|
||||
if addressStr.len < 2:
|
||||
raise newException(ValueError, "Invalid IP Address")
|
||||
@@ -384,7 +384,7 @@ proc parseIPv6Address(addressStr: string): IpAddress =
|
||||
|
||||
proc parseIpAddress*(addressStr: string): IpAddress =
|
||||
## Parses an IP address
|
||||
## Raises EInvalidValue on error
|
||||
## Raises ValueError on error
|
||||
if addressStr.len == 0:
|
||||
raise newException(ValueError, "IP Address string is empty")
|
||||
if addressStr.contains(':'):
|
||||
@@ -746,7 +746,7 @@ proc listen*(socket: Socket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} =
|
||||
## ``Backlog`` specifies the maximum length of the
|
||||
## queue of pending connections.
|
||||
##
|
||||
## Raises an EOS error upon failure.
|
||||
## Raises an OSError error upon failure.
|
||||
if nativesockets.listen(socket.fd, backlog) < 0'i32:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
@@ -777,7 +777,7 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string,
|
||||
## Blocks until a connection is being made from a client. When a connection
|
||||
## is made sets ``client`` to the client socket and ``address`` to the address
|
||||
## of the connecting client.
|
||||
## This function will raise EOS if an error occurs.
|
||||
## This function will raise OSError if an error occurs.
|
||||
##
|
||||
## The resulting client will inherit any properties of the server socket. For
|
||||
## example: whether the socket is buffered or not.
|
||||
@@ -983,7 +983,7 @@ when defined(ssl):
|
||||
## Returns ``False`` whenever the socket is not yet ready for a handshake,
|
||||
## ``True`` whenever handshake completed successfully.
|
||||
##
|
||||
## A ESSL error is raised on any other errors.
|
||||
## A SslError error is raised on any other errors.
|
||||
##
|
||||
## **Note:** This procedure is deprecated since version 0.14.0.
|
||||
result = true
|
||||
@@ -1011,7 +1011,7 @@ when defined(ssl):
|
||||
## Determines whether a handshake has occurred between a client (``socket``)
|
||||
## and the server that ``socket`` is connected to.
|
||||
##
|
||||
## Throws ESSL if ``socket`` is not an SSL socket.
|
||||
## Throws SslError if ``socket`` is not an SSL socket.
|
||||
if socket.isSSL:
|
||||
return not socket.sslNoHandshake
|
||||
else:
|
||||
@@ -1203,7 +1203,7 @@ proc recv*(socket: Socket, size: int, timeout = -1,
|
||||
##
|
||||
## When ``""`` is returned the socket's connection has been closed.
|
||||
##
|
||||
## This function will throw an EOS exception when an error occurs.
|
||||
## This function will throw an OSError exception when an error occurs.
|
||||
##
|
||||
## A timeout may be specified in milliseconds, if enough data is not received
|
||||
## within the time specified an ETimeout exception will be raised.
|
||||
@@ -1244,7 +1244,7 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1,
|
||||
##
|
||||
## If the socket is disconnected, ``line`` will be set to ``""``.
|
||||
##
|
||||
## An EOS exception will be raised in the case of a socket error.
|
||||
## An OSError exception will be raised in the case of a socket error.
|
||||
##
|
||||
## A timeout can be specified in milliseconds, if data is not received within
|
||||
## the specified time an ETimeout exception will be raised.
|
||||
@@ -1299,7 +1299,7 @@ proc recvLine*(socket: Socket, timeout = -1,
|
||||
##
|
||||
## If the socket is disconnected, the result will be set to ``""``.
|
||||
##
|
||||
## An EOS exception will be raised in the case of a socket error.
|
||||
## An OSError exception will be raised in the case of a socket error.
|
||||
##
|
||||
## A timeout can be specified in milliseconds, if data is not received within
|
||||
## the specified time an ETimeout exception will be raised.
|
||||
@@ -1317,7 +1317,7 @@ proc recvFrom*(socket: Socket, data: var string, length: int,
|
||||
## Receives data from ``socket``. This function should normally be used with
|
||||
## connection-less sockets (UDP sockets).
|
||||
##
|
||||
## If an error occurs an EOS exception will be raised. Otherwise the return
|
||||
## If an error occurs an OSError exception will be raised. Otherwise the return
|
||||
## value will be the length of data received.
|
||||
##
|
||||
## **Warning:** This function does not yet have a buffered implementation,
|
||||
@@ -1390,7 +1390,7 @@ template `&=`*(socket: Socket; data: typed) =
|
||||
send(socket, data)
|
||||
|
||||
proc trySend*(socket: Socket, data: string): bool {.tags: [WriteIOEffect].} =
|
||||
## Safe alternative to ``send``. Does not raise an EOS when an error occurs,
|
||||
## Safe alternative to ``send``. Does not raise an OSError when an error occurs,
|
||||
## and instead returns ``false`` on failure.
|
||||
result = send(socket, cstring(data), data.len) == data.len
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ proc existsFile*(filename: string): bool {.rtl, extern: "nos$1",
|
||||
|
||||
proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect].} =
|
||||
## Returns true iff the directory `dir` exists. If `dir` is a file, false
|
||||
## is returned.
|
||||
## is returned. Follows symlinks.
|
||||
when defined(windows):
|
||||
when useWinUnicode:
|
||||
wrapUnary(a, getFileAttributesW, dir)
|
||||
|
||||
@@ -121,7 +121,7 @@ proc startProcess*(command: string,
|
||||
## invocation if possible as it leads to non portable software.
|
||||
##
|
||||
## Return value: The newly created process object. Nil is never returned,
|
||||
## but ``EOS`` is raised in case of an error.
|
||||
## but ``OSError`` is raised in case of an error.
|
||||
|
||||
proc startCmd*(command: string, options: set[ProcessOption] = {
|
||||
poStdErrToStdOut, poUsePath}): Process {.
|
||||
@@ -236,6 +236,7 @@ proc execProcesses*(cmds: openArray[string],
|
||||
if n > 1:
|
||||
var i = 0
|
||||
var q = newSeq[Process](n)
|
||||
var idxs = newSeq[int](n) # map process index to cmds index
|
||||
|
||||
when defined(windows):
|
||||
var w: WOHandleArray
|
||||
@@ -248,6 +249,7 @@ proc execProcesses*(cmds: openArray[string],
|
||||
if beforeRunEvent != nil:
|
||||
beforeRunEvent(i)
|
||||
q[i] = startProcess(cmds[i], options = options + {poEvalCommand})
|
||||
idxs[i] = i
|
||||
when defined(windows):
|
||||
w[i] = q[i].fProcessHandle
|
||||
inc(i)
|
||||
@@ -304,12 +306,13 @@ proc execProcesses*(cmds: openArray[string],
|
||||
|
||||
if rexit >= 0:
|
||||
result = max(result, abs(q[rexit].peekExitCode()))
|
||||
if afterRunEvent != nil: afterRunEvent(rexit, q[rexit])
|
||||
if afterRunEvent != nil: afterRunEvent(idxs[rexit], q[rexit])
|
||||
close(q[rexit])
|
||||
if i < len(cmds):
|
||||
if beforeRunEvent != nil: beforeRunEvent(i)
|
||||
q[rexit] = startProcess(cmds[i],
|
||||
options = options + {poEvalCommand})
|
||||
idxs[rexit] = i
|
||||
when defined(windows):
|
||||
w[rexit] = q[rexit].fProcessHandle
|
||||
inc(i)
|
||||
|
||||
@@ -267,7 +267,7 @@ proc parseBiggestInt*(s: string, number: var BiggestInt, start = 0): int {.
|
||||
rtl, extern: "npuParseBiggestInt", noSideEffect.} =
|
||||
## parses an integer starting at `start` and stores the value into `number`.
|
||||
## Result is the number of processed chars or 0 if there is no integer.
|
||||
## `EOverflow` is raised if an overflow occurs.
|
||||
## `OverflowError` is raised if an overflow occurs.
|
||||
var res: BiggestInt
|
||||
# use 'res' for exception safety (don't write to 'number' in case of an
|
||||
# overflow exception):
|
||||
@@ -278,7 +278,7 @@ proc parseInt*(s: string, number: var int, start = 0): int {.
|
||||
rtl, extern: "npuParseInt", noSideEffect.} =
|
||||
## parses an integer starting at `start` and stores the value into `number`.
|
||||
## Result is the number of processed chars or 0 if there is no integer.
|
||||
## `EOverflow` is raised if an overflow occurs.
|
||||
## `OverflowError` is raised if an overflow occurs.
|
||||
var res: BiggestInt
|
||||
result = parseBiggestInt(s, res, start)
|
||||
if (sizeof(int) <= 4) and
|
||||
@@ -289,7 +289,7 @@ proc parseInt*(s: string, number: var int, start = 0): int {.
|
||||
|
||||
proc parseSaturatedNatural*(s: string, b: var int, start = 0): int =
|
||||
## parses a natural number into ``b``. This cannot raise an overflow
|
||||
## error. Instead of an ``Overflow`` exception ``high(int)`` is returned.
|
||||
## error. ``high(int)`` is returned for an overflow.
|
||||
## The number of processed character is returned.
|
||||
## This is usually what you really want to use instead of `parseInt`:idx:.
|
||||
## Example:
|
||||
|
||||
@@ -1342,7 +1342,7 @@ when not defined(js):
|
||||
subs: varargs[tuple[pattern: Peg, repl: string]]) {.
|
||||
rtl, extern: "npegs$1".} =
|
||||
## reads in the file `infile`, performs a parallel replacement (calls
|
||||
## `parallelReplace`) and writes back to `outfile`. Raises ``EIO`` if an
|
||||
## `parallelReplace`) and writes back to `outfile`. Raises ``IOError`` if an
|
||||
## error occurs. This is supposed to be used for quick scripting.
|
||||
##
|
||||
## **Note**: this proc does not exist while using the JS backend.
|
||||
|
||||
@@ -157,112 +157,112 @@ proc peek[T](s: Stream, result: var T) =
|
||||
raise newEIO("cannot read from stream")
|
||||
|
||||
proc readChar*(s: Stream): char =
|
||||
## reads a char from the stream `s`. Raises `EIO` if an error occurred.
|
||||
## Returns '\0' as an EOF marker.
|
||||
## reads a char from the stream `s`. Raises `IOError` if an error occurred.
|
||||
## Returns '\\0' as an EOF marker.
|
||||
if readData(s, addr(result), sizeof(result)) != 1: result = '\0'
|
||||
|
||||
proc peekChar*(s: Stream): char =
|
||||
## peeks a char from the stream `s`. Raises `EIO` if an error occurred.
|
||||
## Returns '\0' as an EOF marker.
|
||||
## peeks a char from the stream `s`. Raises `IOError` if an error occurred.
|
||||
## Returns '\\0' as an EOF marker.
|
||||
if peekData(s, addr(result), sizeof(result)) != 1: result = '\0'
|
||||
|
||||
proc readBool*(s: Stream): bool =
|
||||
## reads a bool from the stream `s`. Raises `EIO` if an error occurred.
|
||||
## reads a bool from the stream `s`. Raises `IOError` if an error occurred.
|
||||
read(s, result)
|
||||
|
||||
proc peekBool*(s: Stream): bool =
|
||||
## peeks a bool from the stream `s`. Raises `EIO` if an error occurred.
|
||||
## peeks a bool from the stream `s`. Raises `IOError` if an error occurred.
|
||||
peek(s, result)
|
||||
|
||||
proc readInt8*(s: Stream): int8 =
|
||||
## reads an int8 from the stream `s`. Raises `EIO` if an error occurred.
|
||||
## reads an int8 from the stream `s`. Raises `IOError` if an error occurred.
|
||||
read(s, result)
|
||||
|
||||
proc peekInt8*(s: Stream): int8 =
|
||||
## peeks an int8 from the stream `s`. Raises `EIO` if an error occurred.
|
||||
## peeks an int8 from the stream `s`. Raises `IOError` if an error occurred.
|
||||
peek(s, result)
|
||||
|
||||
proc readInt16*(s: Stream): int16 =
|
||||
## reads an int16 from the stream `s`. Raises `EIO` if an error occurred.
|
||||
## reads an int16 from the stream `s`. Raises `IOError` if an error occurred.
|
||||
read(s, result)
|
||||
|
||||
proc peekInt16*(s: Stream): int16 =
|
||||
## peeks an int16 from the stream `s`. Raises `EIO` if an error occurred.
|
||||
## peeks an int16 from the stream `s`. Raises `IOError` if an error occurred.
|
||||
peek(s, result)
|
||||
|
||||
proc readInt32*(s: Stream): int32 =
|
||||
## reads an int32 from the stream `s`. Raises `EIO` if an error occurred.
|
||||
## reads an int32 from the stream `s`. Raises `IOError` if an error occurred.
|
||||
read(s, result)
|
||||
|
||||
proc peekInt32*(s: Stream): int32 =
|
||||
## peeks an int32 from the stream `s`. Raises `EIO` if an error occurred.
|
||||
## peeks an int32 from the stream `s`. Raises `IOError` if an error occurred.
|
||||
peek(s, result)
|
||||
|
||||
proc readInt64*(s: Stream): int64 =
|
||||
## reads an int64 from the stream `s`. Raises `EIO` if an error occurred.
|
||||
## reads an int64 from the stream `s`. Raises `IOError` if an error occurred.
|
||||
read(s, result)
|
||||
|
||||
proc peekInt64*(s: Stream): int64 =
|
||||
## peeks an int64 from the stream `s`. Raises `EIO` if an error occurred.
|
||||
## peeks an int64 from the stream `s`. Raises `IOError` if an error occurred.
|
||||
peek(s, result)
|
||||
|
||||
proc readUint8*(s: Stream): uint8 =
|
||||
## reads an uint8 from the stream `s`. Raises `EIO` if an error occurred.
|
||||
## reads an uint8 from the stream `s`. Raises `IOError` if an error occurred.
|
||||
read(s, result)
|
||||
|
||||
proc peekUint8*(s: Stream): uint8 =
|
||||
## peeks an uint8 from the stream `s`. Raises `EIO` if an error occurred.
|
||||
## peeks an uint8 from the stream `s`. Raises `IOError` if an error occurred.
|
||||
peek(s, result)
|
||||
|
||||
proc readUint16*(s: Stream): uint16 =
|
||||
## reads an uint16 from the stream `s`. Raises `EIO` if an error occurred.
|
||||
## reads an uint16 from the stream `s`. Raises `IOError` if an error occurred.
|
||||
read(s, result)
|
||||
|
||||
proc peekUint16*(s: Stream): uint16 =
|
||||
## peeks an uint16 from the stream `s`. Raises `EIO` if an error occurred.
|
||||
## peeks an uint16 from the stream `s`. Raises `IOError` if an error occurred.
|
||||
peek(s, result)
|
||||
|
||||
proc readUint32*(s: Stream): uint32 =
|
||||
## reads an uint32 from the stream `s`. Raises `EIO` if an error occurred.
|
||||
## reads an uint32 from the stream `s`. Raises `IOError` if an error occurred.
|
||||
read(s, result)
|
||||
|
||||
proc peekUint32*(s: Stream): uint32 =
|
||||
## peeks an uint32 from the stream `s`. Raises `EIO` if an error occurred.
|
||||
## peeks an uint32 from the stream `s`. Raises `IOError` if an error occurred.
|
||||
peek(s, result)
|
||||
|
||||
proc readUint64*(s: Stream): uint64 =
|
||||
## reads an uint64 from the stream `s`. Raises `EIO` if an error occurred.
|
||||
## reads an uint64 from the stream `s`. Raises `IOError` if an error occurred.
|
||||
read(s, result)
|
||||
|
||||
proc peekUint64*(s: Stream): uint64 =
|
||||
## peeks an uint64 from the stream `s`. Raises `EIO` if an error occurred.
|
||||
## peeks an uint64 from the stream `s`. Raises `IOError` if an error occurred.
|
||||
peek(s, result)
|
||||
|
||||
proc readFloat32*(s: Stream): float32 =
|
||||
## reads a float32 from the stream `s`. Raises `EIO` if an error occurred.
|
||||
## reads a float32 from the stream `s`. Raises `IOError` if an error occurred.
|
||||
read(s, result)
|
||||
|
||||
proc peekFloat32*(s: Stream): float32 =
|
||||
## peeks a float32 from the stream `s`. Raises `EIO` if an error occurred.
|
||||
## peeks a float32 from the stream `s`. Raises `IOError` if an error occurred.
|
||||
peek(s, result)
|
||||
|
||||
proc readFloat64*(s: Stream): float64 =
|
||||
## reads a float64 from the stream `s`. Raises `EIO` if an error occurred.
|
||||
## reads a float64 from the stream `s`. Raises `IOError` if an error occurred.
|
||||
read(s, result)
|
||||
|
||||
proc peekFloat64*(s: Stream): float64 =
|
||||
## peeks a float64 from the stream `s`. Raises `EIO` if an error occurred.
|
||||
## peeks a float64 from the stream `s`. Raises `IOError` if an error occurred.
|
||||
peek(s, result)
|
||||
|
||||
proc readStr*(s: Stream, length: int): TaintedString =
|
||||
## reads a string of length `length` from the stream `s`. Raises `EIO` if
|
||||
## reads a string of length `length` from the stream `s`. Raises `IOError` if
|
||||
## an error occurred.
|
||||
result = newString(length).TaintedString
|
||||
var L = readData(s, cstring(result), length)
|
||||
if L != length: setLen(result.string, L)
|
||||
|
||||
proc peekStr*(s: Stream, length: int): TaintedString =
|
||||
## peeks a string of length `length` from the stream `s`. Raises `EIO` if
|
||||
## peeks a string of length `length` from the stream `s`. Raises `IOError` if
|
||||
## an error occurred.
|
||||
result = newString(length).TaintedString
|
||||
var L = peekData(s, cstring(result), length)
|
||||
@@ -301,7 +301,7 @@ proc peekLine*(s: Stream, line: var TaintedString): bool =
|
||||
|
||||
proc readLine*(s: Stream): TaintedString =
|
||||
## Reads a line from a stream `s`. Note: This is not very efficient. Raises
|
||||
## `EIO` if an error occurred.
|
||||
## `IOError` if an error occurred.
|
||||
result = TaintedString""
|
||||
if s.atEnd:
|
||||
raise newEIO("cannot read from stream")
|
||||
@@ -317,7 +317,7 @@ proc readLine*(s: Stream): TaintedString =
|
||||
|
||||
proc peekLine*(s: Stream): TaintedString =
|
||||
## Peeks a line from a stream `s`. Note: This is not very efficient. Raises
|
||||
## `EIO` if an error occurred.
|
||||
## `IOError` if an error occurred.
|
||||
let pos = getPosition(s)
|
||||
defer: setPosition(s, pos)
|
||||
result = readLine(s)
|
||||
|
||||
@@ -129,7 +129,7 @@ to use prefix instead of postfix operators.
|
||||
``+E`` One or more
|
||||
``?E`` Zero or One
|
||||
``E{n,m}`` From ``n`` up to ``m`` times ``E``
|
||||
``~Ε`` Not predicate
|
||||
``~E`` Not predicate
|
||||
``a ^* b`` Shortcut for ``?(a *(b a))``. Usually used for separators.
|
||||
``a ^* b`` Shortcut for ``?(a +(b a))``. Usually used for separators.
|
||||
``'a'`` Matches a single character
|
||||
@@ -456,10 +456,11 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
|
||||
|
||||
template atom*(input: string; idx: int; c: char): bool =
|
||||
## Used in scanp for the matching of atoms (usually chars).
|
||||
idx < input.len and input[idx] == c
|
||||
## EOF is matched as ``'\0'``.
|
||||
(idx < input.len and input[idx] == c) or (idx == input.len and c == '\0')
|
||||
|
||||
template atom*(input: string; idx: int; s: set[char]): bool =
|
||||
idx < input.len and input[idx] in s
|
||||
(idx < input.len and input[idx] in s) or (idx == input.len and '\0' in s)
|
||||
|
||||
template hasNxt*(input: string; idx: int): bool = idx < input.len
|
||||
|
||||
|
||||
@@ -75,6 +75,10 @@ proc isAlphaAscii*(c: char): bool {.noSideEffect, procvar,
|
||||
## Checks whether or not `c` is alphabetical.
|
||||
##
|
||||
## This checks a-z, A-Z ASCII characters only.
|
||||
runnableExamples:
|
||||
doAssert isAlphaAscii('e') == true
|
||||
doAssert isAlphaAscii('E') == true
|
||||
doAssert isAlphaAscii('8') == false
|
||||
return c in Letters
|
||||
|
||||
proc isAlphaNumeric*(c: char): bool {.noSideEffect, procvar,
|
||||
@@ -82,6 +86,10 @@ proc isAlphaNumeric*(c: char): bool {.noSideEffect, procvar,
|
||||
## Checks whether or not `c` is alphanumeric.
|
||||
##
|
||||
## This checks a-z, A-Z, 0-9 ASCII characters only.
|
||||
runnableExamples:
|
||||
doAssert isAlphaNumeric('n') == true
|
||||
doAssert isAlphaNumeric('8') == true
|
||||
doAssert isAlphaNumeric(' ') == false
|
||||
return c in Letters+Digits
|
||||
|
||||
proc isDigit*(c: char): bool {.noSideEffect, procvar,
|
||||
@@ -89,11 +97,17 @@ proc isDigit*(c: char): bool {.noSideEffect, procvar,
|
||||
## Checks whether or not `c` is a number.
|
||||
##
|
||||
## This checks 0-9 ASCII characters only.
|
||||
runnableExamples:
|
||||
doAssert isDigit('n') == false
|
||||
doAssert isDigit('8') == true
|
||||
return c in Digits
|
||||
|
||||
proc isSpaceAscii*(c: char): bool {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuIsSpaceAsciiChar".} =
|
||||
## Checks whether or not `c` is a whitespace character.
|
||||
runnableExamples:
|
||||
doAssert isSpaceAscii('n') == false
|
||||
doAssert isSpaceAscii(' ') == true
|
||||
return c in Whitespace
|
||||
|
||||
proc isLowerAscii*(c: char): bool {.noSideEffect, procvar,
|
||||
@@ -101,6 +115,10 @@ proc isLowerAscii*(c: char): bool {.noSideEffect, procvar,
|
||||
## Checks whether or not `c` is a lower case character.
|
||||
##
|
||||
## This checks ASCII characters only.
|
||||
runnableExamples:
|
||||
doAssert isLowerAscii('e') == true
|
||||
doAssert isLowerAscii('E') == false
|
||||
doAssert isLowerAscii('7') == false
|
||||
return c in {'a'..'z'}
|
||||
|
||||
proc isUpperAscii*(c: char): bool {.noSideEffect, procvar,
|
||||
@@ -108,6 +126,10 @@ proc isUpperAscii*(c: char): bool {.noSideEffect, procvar,
|
||||
## Checks whether or not `c` is an upper case character.
|
||||
##
|
||||
## This checks ASCII characters only.
|
||||
runnableExamples:
|
||||
doAssert isUpperAscii('e') == false
|
||||
doAssert isUpperAscii('E') == true
|
||||
doAssert isUpperAscii('7') == false
|
||||
return c in {'A'..'Z'}
|
||||
|
||||
template isImpl(call) =
|
||||
@@ -124,6 +146,10 @@ proc isAlphaAscii*(s: string): bool {.noSideEffect, procvar,
|
||||
## Returns true if all characters in `s` are
|
||||
## alphabetic and there is at least one character
|
||||
## in `s`.
|
||||
runnableExamples:
|
||||
doAssert isAlphaAscii("fooBar") == true
|
||||
doAssert isAlphaAscii("fooBar1") == false
|
||||
doAssert isAlphaAscii("foo Bar") == false
|
||||
isImpl isAlphaAscii
|
||||
|
||||
proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar,
|
||||
@@ -134,6 +160,10 @@ proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar,
|
||||
## Returns true if all characters in `s` are
|
||||
## alpanumeric and there is at least one character
|
||||
## in `s`.
|
||||
runnableExamples:
|
||||
doAssert isAlphaNumeric("fooBar") == true
|
||||
doAssert isAlphaNumeric("fooBar") == true
|
||||
doAssert isAlphaNumeric("foo Bar") == false
|
||||
isImpl isAlphaNumeric
|
||||
|
||||
proc isDigit*(s: string): bool {.noSideEffect, procvar,
|
||||
@@ -144,6 +174,9 @@ proc isDigit*(s: string): bool {.noSideEffect, procvar,
|
||||
## Returns true if all characters in `s` are
|
||||
## numeric and there is at least one character
|
||||
## in `s`.
|
||||
runnableExamples:
|
||||
doAssert isDigit("1908") == true
|
||||
doAssert isDigit("fooBar1") == false
|
||||
isImpl isDigit
|
||||
|
||||
proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar,
|
||||
@@ -152,6 +185,9 @@ proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar,
|
||||
##
|
||||
## Returns true if all characters in `s` are whitespace
|
||||
## characters and there is at least one character in `s`.
|
||||
runnableExamples:
|
||||
doAssert isSpaceAscii(" ") == true
|
||||
doAssert isSpaceAscii("") == false
|
||||
isImpl isSpaceAscii
|
||||
|
||||
template isCaseImpl(s, charProc, skipNonAlpha) =
|
||||
@@ -183,6 +219,10 @@ proc isLowerAscii*(s: string, skipNonAlpha: bool): bool =
|
||||
##
|
||||
## For either value of ``skipNonAlpha``, returns false if ``s`` is
|
||||
## an empty string.
|
||||
runnableExamples:
|
||||
doAssert isLowerAscii("1foobar", false) == false
|
||||
doAssert isLowerAscii("1foobar", true) == true
|
||||
doAssert isLowerAscii("1fooBar", true) == false
|
||||
isCaseImpl(s, isLowerAscii, skipNonAlpha)
|
||||
|
||||
proc isUpperAscii*(s: string, skipNonAlpha: bool): bool =
|
||||
@@ -199,15 +239,22 @@ proc isUpperAscii*(s: string, skipNonAlpha: bool): bool =
|
||||
##
|
||||
## For either value of ``skipNonAlpha``, returns false if ``s`` is
|
||||
## an empty string.
|
||||
runnableExamples:
|
||||
doAssert isUpperAscii("1FOO", false) == false
|
||||
doAssert isUpperAscii("1FOO", true) == true
|
||||
doAssert isUpperAscii("1Foo", true) == false
|
||||
isCaseImpl(s, isUpperAscii, skipNonAlpha)
|
||||
|
||||
proc toLowerAscii*(c: char): char {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuToLowerAsciiChar".} =
|
||||
## Converts `c` into lower case.
|
||||
## Returns the lower case version of ``c``.
|
||||
##
|
||||
## This works only for the letters ``A-Z``. See `unicode.toLower
|
||||
## <unicode.html#toLower>`_ for a version that works for any Unicode
|
||||
## character.
|
||||
runnableExamples:
|
||||
doAssert toLowerAscii('A') == 'a'
|
||||
doAssert toLowerAscii('e') == 'e'
|
||||
if c in {'A'..'Z'}:
|
||||
result = chr(ord(c) + (ord('a') - ord('A')))
|
||||
else:
|
||||
@@ -225,6 +272,8 @@ proc toLowerAscii*(s: string): string {.noSideEffect, procvar,
|
||||
## This works only for the letters ``A-Z``. See `unicode.toLower
|
||||
## <unicode.html#toLower>`_ for a version that works for any Unicode
|
||||
## character.
|
||||
runnableExamples:
|
||||
doAssert toLowerAscii("FooBar!") == "foobar!"
|
||||
toImpl toLowerAscii
|
||||
|
||||
proc toUpperAscii*(c: char): char {.noSideEffect, procvar,
|
||||
@@ -234,6 +283,9 @@ proc toUpperAscii*(c: char): char {.noSideEffect, procvar,
|
||||
## This works only for the letters ``A-Z``. See `unicode.toUpper
|
||||
## <unicode.html#toUpper>`_ for a version that works for any Unicode
|
||||
## character.
|
||||
runnableExamples:
|
||||
doAssert toUpperAscii('a') == 'A'
|
||||
doAssert toUpperAscii('E') == 'E'
|
||||
if c in {'a'..'z'}:
|
||||
result = chr(ord(c) - (ord('a') - ord('A')))
|
||||
else:
|
||||
@@ -246,6 +298,8 @@ proc toUpperAscii*(s: string): string {.noSideEffect, procvar,
|
||||
## This works only for the letters ``A-Z``. See `unicode.toUpper
|
||||
## <unicode.html#toUpper>`_ for a version that works for any Unicode
|
||||
## character.
|
||||
runnableExamples:
|
||||
doAssert toUpperAscii("FooBar!") == "FOOBAR!"
|
||||
toImpl toUpperAscii
|
||||
|
||||
proc capitalizeAscii*(s: string): string {.noSideEffect, procvar,
|
||||
@@ -253,6 +307,9 @@ proc capitalizeAscii*(s: string): string {.noSideEffect, procvar,
|
||||
## Converts the first character of `s` into upper case.
|
||||
##
|
||||
## This works only for the letters ``A-Z``.
|
||||
runnableExamples:
|
||||
doAssert capitalizeAscii("foo") == "Foo"
|
||||
doAssert capitalizeAscii("-bar") == "-bar"
|
||||
if s.len == 0: result = ""
|
||||
else: result = toUpperAscii(s[0]) & substr(s, 1)
|
||||
|
||||
@@ -262,6 +319,9 @@ proc normalize*(s: string): string {.noSideEffect, procvar,
|
||||
##
|
||||
## That means to convert it to lower case and remove any '_'. This
|
||||
## should NOT be used to normalize Nim identifier names.
|
||||
runnableExamples:
|
||||
doAssert normalize("Foo_bar") == "foobar"
|
||||
doAssert normalize("Foo Bar") == "foo bar"
|
||||
result = newString(s.len)
|
||||
var j = 0
|
||||
for i in 0..len(s) - 1:
|
||||
@@ -280,6 +340,10 @@ proc cmpIgnoreCase*(a, b: string): int {.noSideEffect,
|
||||
## | 0 iff a == b
|
||||
## | < 0 iff a < b
|
||||
## | > 0 iff a > b
|
||||
runnableExamples:
|
||||
doAssert cmpIgnoreCase("FooBar", "foobar") == 0
|
||||
doAssert cmpIgnoreCase("bar", "Foo") < 0
|
||||
doAssert cmpIgnoreCase("Foo5", "foo4") > 0
|
||||
var i = 0
|
||||
var m = min(a.len, b.len)
|
||||
while i < m:
|
||||
@@ -301,6 +365,9 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect,
|
||||
## | 0 iff a == b
|
||||
## | < 0 iff a < b
|
||||
## | > 0 iff a > b
|
||||
runnableExamples:
|
||||
doAssert cmpIgnoreStyle("foo_bar", "FooBar") == 0
|
||||
doAssert cmpIgnoreStyle("foo_bar_5", "FooBar4") > 0
|
||||
var i = 0
|
||||
var j = 0
|
||||
while true:
|
||||
@@ -344,6 +411,8 @@ proc toOctal*(c: char): string {.noSideEffect, rtl, extern: "nsuToOctal".} =
|
||||
##
|
||||
## The resulting string may not have a leading zero. Its length is always
|
||||
## exactly 3.
|
||||
runnableExamples:
|
||||
doAssert toOctal('!') == "041"
|
||||
result = newString(3)
|
||||
var val = ord(c)
|
||||
for i in countdown(2, 0):
|
||||
@@ -489,11 +558,15 @@ iterator splitWhitespace*(s: string, maxsplit: int = -1): string =
|
||||
##
|
||||
oldSplit(s, Whitespace, maxsplit)
|
||||
|
||||
template accResult(iter: untyped) =
|
||||
result = @[]
|
||||
for x in iter: add(result, x)
|
||||
|
||||
proc splitWhitespace*(s: string, maxsplit: int = -1): seq[string] {.noSideEffect,
|
||||
rtl, extern: "nsuSplitWhitespace".} =
|
||||
## The same as the `splitWhitespace <#splitWhitespace.i,string,int>`_
|
||||
## iterator, but is a proc that returns a sequence of substrings.
|
||||
accumulateResult(splitWhitespace(s, maxsplit))
|
||||
accResult(splitWhitespace(s, maxsplit))
|
||||
|
||||
iterator split*(s: string, sep: char, maxsplit: int = -1): string =
|
||||
## Splits the string `s` into substrings using a single separator.
|
||||
@@ -670,7 +743,7 @@ proc splitLines*(s: string, keepEol = false): seq[string] {.noSideEffect,
|
||||
rtl, extern: "nsuSplitLines".} =
|
||||
## The same as the `splitLines <#splitLines.i,string>`_ iterator, but is a
|
||||
## proc that returns a sequence of substrings.
|
||||
accumulateResult(splitLines(s, keepEol=keepEol))
|
||||
accResult(splitLines(s, keepEol=keepEol))
|
||||
|
||||
proc countLines*(s: string): int {.noSideEffect,
|
||||
rtl, extern: "nsuCountLines".} =
|
||||
@@ -683,6 +756,8 @@ proc countLines*(s: string): int {.noSideEffect,
|
||||
##
|
||||
## In this context, a line is any string seperated by a newline combination.
|
||||
## A line can be an empty string.
|
||||
runnableExamples:
|
||||
doAssert countLines("First line\l and second line.") == 2
|
||||
result = 1
|
||||
var i = 0
|
||||
while i < s.len:
|
||||
@@ -701,7 +776,7 @@ proc split*(s: string, seps: set[char] = Whitespace, maxsplit: int = -1): seq[st
|
||||
runnableExamples:
|
||||
doAssert "a,b;c".split({',', ';'}) == @["a", "b", "c"]
|
||||
doAssert "".split({' '}) == @[""]
|
||||
accumulateResult(split(s, seps, maxsplit))
|
||||
accResult(split(s, seps, maxsplit))
|
||||
|
||||
proc split*(s: string, sep: char, maxsplit: int = -1): seq[string] {.noSideEffect,
|
||||
rtl, extern: "nsuSplitChar".} =
|
||||
@@ -710,7 +785,7 @@ proc split*(s: string, sep: char, maxsplit: int = -1): seq[string] {.noSideEffec
|
||||
runnableExamples:
|
||||
doAssert "a,b,c".split(',') == @["a", "b", "c"]
|
||||
doAssert "".split(' ') == @[""]
|
||||
accumulateResult(split(s, sep, maxsplit))
|
||||
accResult(split(s, sep, maxsplit))
|
||||
|
||||
proc split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.noSideEffect,
|
||||
rtl, extern: "nsuSplitString".} =
|
||||
@@ -727,7 +802,7 @@ proc split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.noSideEff
|
||||
doAssert "a largely spaced sentence".split(" ", maxsplit=1) == @["a", " largely spaced sentence"]
|
||||
doAssert(sep.len > 0)
|
||||
|
||||
accumulateResult(split(s, sep, maxsplit))
|
||||
accResult(split(s, sep, maxsplit))
|
||||
|
||||
proc rsplit*(s: string, seps: set[char] = Whitespace,
|
||||
maxsplit: int = -1): seq[string]
|
||||
@@ -749,7 +824,7 @@ proc rsplit*(s: string, seps: set[char] = Whitespace,
|
||||
## .. code-block:: nim
|
||||
## @["Root#Object#Method", "Index"]
|
||||
##
|
||||
accumulateResult(rsplit(s, seps, maxsplit))
|
||||
accResult(rsplit(s, seps, maxsplit))
|
||||
result.reverse()
|
||||
|
||||
proc rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string]
|
||||
@@ -771,7 +846,7 @@ proc rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string]
|
||||
## .. code-block:: nim
|
||||
## @["Root#Object#Method", "Index"]
|
||||
##
|
||||
accumulateResult(rsplit(s, sep, maxsplit))
|
||||
accResult(rsplit(s, sep, maxsplit))
|
||||
result.reverse()
|
||||
|
||||
proc rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string]
|
||||
@@ -800,7 +875,7 @@ proc rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string]
|
||||
doAssert "a man a plan a canal panama".rsplit("a ") == @["", "man ", "plan ", "canal panama"]
|
||||
doAssert "".rsplit("Elon Musk") == @[""]
|
||||
doAssert "a largely spaced sentence".rsplit(" ") == @["a", "", "largely", "", "", "", "spaced", "sentence"]
|
||||
accumulateResult(rsplit(s, sep, maxsplit))
|
||||
accResult(rsplit(s, sep, maxsplit))
|
||||
result.reverse()
|
||||
|
||||
proc toHex*(x: BiggestInt, len: Positive): string {.noSideEffect,
|
||||
@@ -809,6 +884,9 @@ proc toHex*(x: BiggestInt, len: Positive): string {.noSideEffect,
|
||||
##
|
||||
## The resulting string will be exactly `len` characters long. No prefix like
|
||||
## ``0x`` is generated. `x` is treated as an unsigned value.
|
||||
runnableExamples:
|
||||
doAssert toHex(1984, 6) == "0007C0"
|
||||
doAssert toHex(1984, 2) == "C0"
|
||||
const
|
||||
HexChars = "0123456789ABCDEF"
|
||||
var
|
||||
@@ -822,6 +900,8 @@ proc toHex*(x: BiggestInt, len: Positive): string {.noSideEffect,
|
||||
|
||||
proc toHex*[T: SomeInteger](x: T): string =
|
||||
## Shortcut for ``toHex(x, T.sizeOf * 2)``
|
||||
runnableExamples:
|
||||
doAssert toHex(1984'i64) == "00000000000007C0"
|
||||
toHex(BiggestInt(x), T.sizeOf * 2)
|
||||
|
||||
proc toHex*(s: string): string {.noSideEffect, rtl.} =
|
||||
@@ -843,6 +923,9 @@ proc intToStr*(x: int, minchars: Positive = 1): string {.noSideEffect,
|
||||
##
|
||||
## The resulting string will be minimally `minchars` characters long. This is
|
||||
## achieved by adding leading zeros.
|
||||
runnableExamples:
|
||||
doAssert intToStr(1984) == "1984"
|
||||
doAssert intToStr(1984, 6) == "001984"
|
||||
result = $abs(x)
|
||||
for i in 1 .. minchars - len(result):
|
||||
result = '0' & result
|
||||
@@ -854,6 +937,8 @@ proc parseInt*(s: string): int {.noSideEffect, procvar,
|
||||
## Parses a decimal integer value contained in `s`.
|
||||
##
|
||||
## If `s` is not a valid integer, `ValueError` is raised.
|
||||
runnableExamples:
|
||||
doAssert parseInt("-0042") == -42
|
||||
let L = parseutils.parseInt(s, result, 0)
|
||||
if L != s.len or L == 0:
|
||||
raise newException(ValueError, "invalid integer: " & s)
|
||||
@@ -890,6 +975,9 @@ proc parseFloat*(s: string): float {.noSideEffect, procvar,
|
||||
## Parses a decimal floating point value contained in `s`. If `s` is not
|
||||
## a valid floating point number, `ValueError` is raised. ``NAN``,
|
||||
## ``INF``, ``-INF`` are also supported (case insensitive comparison).
|
||||
runnableExamples:
|
||||
doAssert parseFloat("3.14") == 3.14
|
||||
doAssert parseFloat("inf") == 1.0/0
|
||||
let L = parseutils.parseFloat(s, result, 0)
|
||||
if L != s.len or L == 0:
|
||||
raise newException(ValueError, "invalid float: " & s)
|
||||
@@ -1151,6 +1239,8 @@ proc indent*(s: string, count: Natural, padding: string = " "): string
|
||||
## Indents each line in ``s`` by ``count`` amount of ``padding``.
|
||||
##
|
||||
## **Note:** This does not preserve the new line characters used in ``s``.
|
||||
runnableExamples:
|
||||
doAssert indent("First line\c\l and second line.", 2) == " First line\l and second line."
|
||||
result = ""
|
||||
var i = 0
|
||||
for line in s.splitLines():
|
||||
@@ -1166,6 +1256,8 @@ proc unindent*(s: string, count: Natural, padding: string = " "): string
|
||||
## Unindents each line in ``s`` by ``count`` amount of ``padding``.
|
||||
##
|
||||
## **Note:** This does not preserve the new line characters used in ``s``.
|
||||
runnableExamples:
|
||||
doAssert unindent(" First line\l and second line", 3) == "First line\land second line"
|
||||
result = ""
|
||||
var i = 0
|
||||
for line in s.splitLines():
|
||||
@@ -1260,14 +1352,22 @@ proc addSep*(dest: var string, sep = ", ", startLen: Natural = 0)
|
||||
|
||||
proc allCharsInSet*(s: string, theSet: set[char]): bool =
|
||||
## Returns true iff each character of `s` is in the set `theSet`.
|
||||
runnableExamples:
|
||||
doAssert allCharsInSet("aeea", {'a', 'e'}) == true
|
||||
doAssert allCharsInSet("", {'a', 'e'}) == true
|
||||
for c in items(s):
|
||||
if c notin theSet: return false
|
||||
return true
|
||||
|
||||
proc abbrev*(s: string, possibilities: openArray[string]): int =
|
||||
## Returns the index of the first item in `possibilities` if not ambiguous.
|
||||
## Returns the index of the first item in ``possibilities`` which starts with ``s``, if not ambiguous.
|
||||
##
|
||||
## Returns -1 if no item has been found and -2 if multiple items match.
|
||||
runnableExamples:
|
||||
doAssert abbrev("fac", ["college", "faculty", "industry"]) == 1
|
||||
doAssert abbrev("foo", ["college", "faculty", "industry"]) == -1 # Not found
|
||||
doAssert abbrev("fac", ["college", "faculty", "faculties"]) == -2 # Ambiguous
|
||||
doAssert abbrev("college", ["college", "colleges", "industry"]) == 0
|
||||
result = -1 # none found
|
||||
for i in 0..possibilities.len-1:
|
||||
if possibilities[i].startsWith(s):
|
||||
@@ -1282,6 +1382,8 @@ proc abbrev*(s: string, possibilities: openArray[string]): int =
|
||||
proc join*(a: openArray[string], sep: string = ""): string {.
|
||||
noSideEffect, rtl, extern: "nsuJoinSep".} =
|
||||
## Concatenates all strings in `a` separating them with `sep`.
|
||||
runnableExamples:
|
||||
doAssert join(["A", "B", "Conclusion"], " -> ") == "A -> B -> Conclusion"
|
||||
if len(a) > 0:
|
||||
var L = sep.len * (a.len-1)
|
||||
for i in 0..high(a): inc(L, a[i].len)
|
||||
@@ -1297,6 +1399,8 @@ proc join*[T: not string](a: openArray[T], sep: string = ""): string {.
|
||||
noSideEffect, rtl.} =
|
||||
## Converts all elements in `a` to strings using `$` and concatenates them
|
||||
## with `sep`.
|
||||
runnableExamples:
|
||||
doAssert join([1, 2, 3], " -> ") == "1 -> 2 -> 3"
|
||||
result = ""
|
||||
for i, x in a:
|
||||
if i > 0:
|
||||
@@ -1413,6 +1517,8 @@ proc rfind*(s, sub: string, start: int = -1): int {.noSideEffect.} =
|
||||
## backwards to 0.
|
||||
##
|
||||
## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
|
||||
if sub.len == 0:
|
||||
return -1
|
||||
let realStart = if start == -1: s.len else: start
|
||||
for i in countdown(realStart-sub.len, 0):
|
||||
for j in 0..sub.len-1:
|
||||
@@ -1471,6 +1577,7 @@ proc count*(s: string, sub: string, overlapping: bool = false): int {.
|
||||
## Count the occurrences of a substring `sub` in the string `s`.
|
||||
## Overlapping occurrences of `sub` only count when `overlapping`
|
||||
## is set to true.
|
||||
doAssert sub.len > 0
|
||||
var i = 0
|
||||
while true:
|
||||
i = s.find(sub, i)
|
||||
@@ -1488,6 +1595,7 @@ proc count*(s: string, sub: char): int {.noSideEffect,
|
||||
proc count*(s: string, subs: set[char]): int {.noSideEffect,
|
||||
rtl, extern: "nsuCountCharSet".} =
|
||||
## Count the occurrences of the group of character `subs` in the string `s`.
|
||||
doAssert card(subs) > 0
|
||||
for c in s:
|
||||
if c in subs: inc result
|
||||
|
||||
@@ -1518,11 +1626,7 @@ proc replace*(s, sub: string, by = ""): string {.noSideEffect,
|
||||
result = ""
|
||||
let subLen = sub.len
|
||||
if subLen == 0:
|
||||
for c in s:
|
||||
add result, by
|
||||
add result, c
|
||||
add result, by
|
||||
return
|
||||
result = s
|
||||
elif subLen == 1:
|
||||
# when the pattern is a single char, we use a faster
|
||||
# char-based search that doesn't need a skip table:
|
||||
@@ -1577,21 +1681,22 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect,
|
||||
initSkipTable(a, sub)
|
||||
var i = 0
|
||||
let last = s.high
|
||||
let sublen = max(sub.len, 1)
|
||||
while true:
|
||||
var j = find(a, s, sub, i, last)
|
||||
if j < 0: break
|
||||
# word boundary?
|
||||
if (j == 0 or s[j-1] notin wordChars) and
|
||||
(j+sub.len >= s.len or s[j+sub.len] notin wordChars):
|
||||
add result, substr(s, i, j - 1)
|
||||
add result, by
|
||||
i = j + sublen
|
||||
else:
|
||||
add result, substr(s, i, j)
|
||||
i = j + 1
|
||||
# copy the rest:
|
||||
add result, substr(s, i)
|
||||
let sublen = sub.len
|
||||
if sublen > 0:
|
||||
while true:
|
||||
var j = find(a, s, sub, i, last)
|
||||
if j < 0: break
|
||||
# word boundary?
|
||||
if (j == 0 or s[j-1] notin wordChars) and
|
||||
(j+sub.len >= s.len or s[j+sub.len] notin wordChars):
|
||||
add result, substr(s, i, j - 1)
|
||||
add result, by
|
||||
i = j + sublen
|
||||
else:
|
||||
add result, substr(s, i, j)
|
||||
i = j + 1
|
||||
# copy the rest:
|
||||
add result, substr(s, i)
|
||||
|
||||
proc multiReplace*(s: string, replacements: varargs[(string, string)]): string {.noSideEffect.} =
|
||||
## Same as replace, but specialized for doing multiple replacements in a single
|
||||
@@ -1608,15 +1713,18 @@ proc multiReplace*(s: string, replacements: varargs[(string, string)]): string {
|
||||
result = newStringOfCap(s.len)
|
||||
var i = 0
|
||||
var fastChk: set[char] = {}
|
||||
for tup in replacements: fastChk.incl(tup[0][0]) # Include first character of all replacements
|
||||
for sub, by in replacements.items:
|
||||
if sub.len > 0:
|
||||
# Include first character of all replacements
|
||||
fastChk.incl sub[0]
|
||||
while i < s.len:
|
||||
block sIteration:
|
||||
# Assume most chars in s are not candidates for any replacement operation
|
||||
if s[i] in fastChk:
|
||||
for tup in replacements:
|
||||
if s.continuesWith(tup[0], i):
|
||||
add result, tup[1]
|
||||
inc(i, tup[0].len)
|
||||
for sub, by in replacements.items:
|
||||
if sub.len > 0 and s.continuesWith(sub, i):
|
||||
add result, by
|
||||
inc(i, sub.len)
|
||||
break sIteration
|
||||
# No matching replacement found
|
||||
# copy current character from s
|
||||
@@ -2473,7 +2581,7 @@ when isMainModule:
|
||||
doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz abc"
|
||||
|
||||
doAssert "-lda-ldz -ld abc".replaceWord("") == "-lda-ldz -ld abc"
|
||||
doAssert "oo".replace("", "abc") == "abcoabcoabc"
|
||||
doAssert "oo".replace("", "abc") == "oo"
|
||||
|
||||
type MyEnum = enum enA, enB, enC, enuD, enE
|
||||
doAssert parseEnum[MyEnum]("enu_D") == enuD
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
import macros
|
||||
import strformat
|
||||
from strutils import toLowerAscii
|
||||
from strutils import toLowerAscii, `%`
|
||||
import colors, tables
|
||||
|
||||
when defined(windows):
|
||||
@@ -635,7 +635,8 @@ proc ansiForegroundColorCode*(color: Color): string =
|
||||
|
||||
template ansiForegroundColorCode*(color: static[Color]): string =
|
||||
const rgb = extractRGB(color)
|
||||
(static(fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m"))
|
||||
# no usage of `fmt`, see issue #7632
|
||||
(static("$1$2;$3;$4m" % [$fgPrefix, $(rgb.r), $(rgb.g), $(rgb.b)]))
|
||||
|
||||
proc ansiBackgroundColorCode*(color: Color): string =
|
||||
let rgb = extractRGB(color)
|
||||
@@ -643,7 +644,8 @@ proc ansiBackgroundColorCode*(color: Color): string =
|
||||
|
||||
template ansiBackgroundColorCode*(color: static[Color]): string =
|
||||
const rgb = extractRGB(color)
|
||||
(static(fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m"))
|
||||
# no usage of `fmt`, see issue #7632
|
||||
(static("$1$2;$3;$4m" % [$bgPrefix, $(rgb.r), $(rgb.g), $(rgb.b)]))
|
||||
|
||||
proc setForegroundColor*(f: File, color: Color) =
|
||||
## Sets the terminal's foreground true color.
|
||||
|
||||
@@ -967,11 +967,13 @@ else:
|
||||
return ((0 - tm.toAdjUnix).int, false)
|
||||
return (0, false)
|
||||
|
||||
var a = unix.CTime
|
||||
# In case of a 32-bit time_t, we fallback to the closest available
|
||||
# timezone information.
|
||||
var a = clamp(unix, low(CTime), high(CTime)).CTime
|
||||
let tmPtr = localtime(addr(a))
|
||||
if not tmPtr.isNil:
|
||||
let tm = tmPtr[]
|
||||
return ((unix - tm.toAdjUnix).int, tm.isdst > 0)
|
||||
return ((a.int64 - tm.toAdjUnix).int, tm.isdst > 0)
|
||||
return (0, false)
|
||||
|
||||
proc localZonedTimeFromTime(time: Time): ZonedTime =
|
||||
|
||||
@@ -524,6 +524,22 @@ const
|
||||
0x3000, 0x3000, # ideographic space
|
||||
0xfeff, 0xfeff] #
|
||||
|
||||
unicodeSpaces = [
|
||||
Rune 0x0009, # tab
|
||||
Rune 0x000a, # LF
|
||||
Rune 0x000d, # CR
|
||||
Rune 0x0020, # space
|
||||
Rune 0x0085, # next line
|
||||
Rune 0x00a0, # unknown
|
||||
Rune 0x1680, # Ogham space mark
|
||||
Rune 0x2000, # en dash .. zero-width space
|
||||
Rune 0x200e, Rune 0x200f, # LTR mark .. RTL mark (pattern whitespace)
|
||||
Rune 0x2028, Rune 0x2029, # - 0x3000, 0x3000, #
|
||||
Rune 0x202f, # narrow no-break space
|
||||
Rune 0x205f, # medium mathematical space
|
||||
Rune 0x3000, # ideographic space
|
||||
Rune 0xfeff] # unknown
|
||||
|
||||
toupperRanges = [
|
||||
0x0061, 0x007a, 468, # a-z A-Z
|
||||
0x00e0, 0x00f6, 468, # - -
|
||||
@@ -1733,7 +1749,272 @@ proc lastRune*(s: string; last: int): (Rune, int) =
|
||||
fastRuneAt(s, last-L, r, false)
|
||||
result = (r, L+1)
|
||||
|
||||
proc size*(r: Rune): int {.noSideEffect.} =
|
||||
## Returns the number of bytes the rune ``r`` takes.
|
||||
let v = r.uint32
|
||||
if v <= 0x007F: result = 1
|
||||
elif v <= 0x07FF: result = 2
|
||||
elif v <= 0xFFFF: result = 3
|
||||
elif v <= 0x1FFFFF: result = 4
|
||||
elif v <= 0x3FFFFFF: result = 5
|
||||
elif v <= 0x7FFFFFFF: result = 6
|
||||
else: result = 1
|
||||
|
||||
# --------- Private templates for different split separators -----------
|
||||
proc stringHasSep(s: string, index: int, seps: openarray[Rune]): bool =
|
||||
var rune: Rune
|
||||
fastRuneAt(s, index, rune, false)
|
||||
return seps.contains(rune)
|
||||
|
||||
proc stringHasSep(s: string, index: int, sep: Rune): bool =
|
||||
var rune: Rune
|
||||
fastRuneAt(s, index, rune, false)
|
||||
return sep == rune
|
||||
|
||||
template splitCommon(s, sep, maxsplit: untyped, sepLen: int = -1) =
|
||||
## Common code for split procedures
|
||||
var
|
||||
last = 0
|
||||
splits = maxsplit
|
||||
if len(s) > 0:
|
||||
while last <= len(s):
|
||||
var first = last
|
||||
while last < len(s) and not stringHasSep(s, last, sep):
|
||||
when sep is Rune:
|
||||
inc(last, sepLen)
|
||||
else:
|
||||
inc(last, runeLenAt(s, last))
|
||||
if splits == 0: last = len(s)
|
||||
yield s[first .. (last - 1)]
|
||||
if splits == 0: break
|
||||
dec(splits)
|
||||
when sep is Rune:
|
||||
inc(last, sepLen)
|
||||
else:
|
||||
inc(last, if last < len(s): runeLenAt(s, last) else: 1)
|
||||
|
||||
iterator split*(s: string, seps: openarray[Rune] = unicodeSpaces,
|
||||
maxsplit: int = -1): string =
|
||||
## Splits the unicode string `s` into substrings using a group of separators.
|
||||
##
|
||||
## Substrings are separated by a substring containing only `seps`.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## for word in split("this\lis an\texample"):
|
||||
## writeLine(stdout, word)
|
||||
##
|
||||
## ...generates this output:
|
||||
##
|
||||
## .. code-block::
|
||||
## "this"
|
||||
## "is"
|
||||
## "an"
|
||||
## "example"
|
||||
##
|
||||
## And the following code:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## for word in split("this:is;an$example", {';', ':', '$'}):
|
||||
## writeLine(stdout, word)
|
||||
##
|
||||
## ...produces the same output as the first example. The code:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## let date = "2012-11-20T22:08:08.398990"
|
||||
## let separators = {' ', '-', ':', 'T'}
|
||||
## for number in split(date, separators):
|
||||
## writeLine(stdout, number)
|
||||
##
|
||||
## ...results in:
|
||||
##
|
||||
## .. code-block::
|
||||
## "2012"
|
||||
## "11"
|
||||
## "20"
|
||||
## "22"
|
||||
## "08"
|
||||
## "08.398990"
|
||||
##
|
||||
splitCommon(s, seps, maxsplit)
|
||||
|
||||
iterator splitWhitespace*(s: string): string =
|
||||
## Splits a unicode string at whitespace runes
|
||||
splitCommon(s, unicodeSpaces, -1)
|
||||
|
||||
template accResult(iter: untyped) =
|
||||
result = @[]
|
||||
for x in iter: add(result, x)
|
||||
|
||||
proc splitWhitespace*(s: string): seq[string] {.noSideEffect,
|
||||
rtl, extern: "ncuSplitWhitespace".} =
|
||||
## The same as the `splitWhitespace <#splitWhitespace.i,string>`_
|
||||
## iterator, but is a proc that returns a sequence of substrings.
|
||||
accResult(splitWhitespace(s))
|
||||
|
||||
iterator split*(s: string, sep: Rune, maxsplit: int = -1): string =
|
||||
## Splits the unicode string `s` into substrings using a single separator.
|
||||
##
|
||||
## Substrings are separated by the rune `sep`.
|
||||
## The code:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## for word in split(";;this;is;an;;example;;;", ';'):
|
||||
## writeLine(stdout, word)
|
||||
##
|
||||
## Results in:
|
||||
##
|
||||
## .. code-block::
|
||||
## ""
|
||||
## ""
|
||||
## "this"
|
||||
## "is"
|
||||
## "an"
|
||||
## ""
|
||||
## "example"
|
||||
## ""
|
||||
## ""
|
||||
## ""
|
||||
##
|
||||
splitCommon(s, sep, maxsplit, sep.size)
|
||||
|
||||
proc split*(s: string, seps: openarray[Rune] = unicodeSpaces, maxsplit: int = -1): seq[string] {.
|
||||
noSideEffect, rtl, extern: "nucSplitRunes".} =
|
||||
## The same as the `split iterator <#split.i,string,openarray[Rune]>`_, but is a
|
||||
## proc that returns a sequence of substrings.
|
||||
accResult(split(s, seps, maxsplit))
|
||||
|
||||
proc split*(s: string, sep: Rune, maxsplit: int = -1): seq[string] {.noSideEffect,
|
||||
rtl, extern: "nucSplitRune".} =
|
||||
## The same as the `split iterator <#split.i,string,Rune>`_, but is a proc
|
||||
## that returns a sequence of substrings.
|
||||
accResult(split(s, sep, maxsplit))
|
||||
|
||||
proc strip*(s: string, leading = true, trailing = true,
|
||||
runes: openarray[Rune] = unicodeSpaces): string {.noSideEffect,
|
||||
rtl, extern: "nucStrip".} =
|
||||
## Strips leading or trailing `runes` from `s` and returns
|
||||
## the resulting string.
|
||||
##
|
||||
## If `leading` is true, leading `runes` are stripped.
|
||||
## If `trailing` is true, trailing `runes` are stripped.
|
||||
## If both are false, the string is returned unchanged.
|
||||
var
|
||||
s_i = 0 ## starting index into string ``s``
|
||||
e_i = len(s) - 1 ## ending index into ``s``, where the last ``Rune`` starts
|
||||
if leading:
|
||||
var
|
||||
i = 0
|
||||
l_i: int ## value of ``s_i`` at the beginning of the iteration
|
||||
rune: Rune
|
||||
while i < len(s):
|
||||
l_i = i
|
||||
fastRuneAt(s, i, rune)
|
||||
s_i = i # Assume to start from next rune
|
||||
if not runes.contains(rune):
|
||||
s_i = l_i # Go back to where the current rune starts
|
||||
break
|
||||
if trailing:
|
||||
var
|
||||
i = e_i
|
||||
l_i: int
|
||||
rune: Rune
|
||||
while i >= 0:
|
||||
l_i = i
|
||||
fastRuneAt(s, l_i, rune)
|
||||
var p_i = i - 1
|
||||
while p_i >= 0:
|
||||
var
|
||||
p_i_end = p_i
|
||||
p_rune: Rune
|
||||
fastRuneAt(s, p_i_end, p_rune)
|
||||
if p_i_end < l_i: break
|
||||
i = p_i
|
||||
rune = p_rune
|
||||
dec(p_i)
|
||||
if not runes.contains(rune):
|
||||
e_i = l_i - 1
|
||||
break
|
||||
dec(i)
|
||||
let newLen = e_i - s_i + 1
|
||||
result = newStringOfCap(newLen)
|
||||
if newLen > 0:
|
||||
result.add s[s_i .. e_i]
|
||||
|
||||
proc repeat*(c: Rune, count: Natural): string {.noSideEffect,
|
||||
rtl, extern: "nucRepeatRune".} =
|
||||
## Returns a string of `count` Runes `c`.
|
||||
##
|
||||
## The returned string will have a rune-length of `count`.
|
||||
let s = $c
|
||||
result = newStringOfCap(count * s.len)
|
||||
for i in 0 ..< count:
|
||||
result.add s
|
||||
|
||||
proc align*(s: string, count: Natural, padding = ' '.Rune): string {.
|
||||
noSideEffect, rtl, extern: "nucAlignString".} =
|
||||
## Aligns a unicode string `s` with `padding`, so that it has a rune-length
|
||||
## of `count`.
|
||||
##
|
||||
## `padding` characters (by default spaces) are added before `s` resulting in
|
||||
## right alignment. If ``s.runelen >= count``, no spaces are added and `s` is
|
||||
## returned unchanged. If you need to left align a string use the `alignLeft
|
||||
## proc <#alignLeft>`_.
|
||||
runnableExamples:
|
||||
assert align("abc", 4) == " abc"
|
||||
assert align("a", 0) == "a"
|
||||
assert align("1232", 6) == " 1232"
|
||||
assert align("1232", 6, '#'.Rune) == "##1232"
|
||||
assert align("Åge", 5) == " Åge"
|
||||
assert align("×", 4, '_'.Rune) == "___×"
|
||||
|
||||
let sLen = s.runeLen
|
||||
if sLen < count:
|
||||
let padStr = $padding
|
||||
result = newStringOfCap(padStr.len * count)
|
||||
let spaces = count - sLen
|
||||
for i in 0 ..< spaces: result.add padStr
|
||||
result.add s
|
||||
else:
|
||||
result = s
|
||||
|
||||
proc alignLeft*(s: string, count: Natural, padding = ' '.Rune): string {.
|
||||
noSideEffect.} =
|
||||
## Left-Aligns a unicode string `s` with `padding`, so that it has a
|
||||
## rune-length of `count`.
|
||||
##
|
||||
## `padding` characters (by default spaces) are added after `s` resulting in
|
||||
## left alignment. If ``s.runelen >= count``, no spaces are added and `s` is
|
||||
## returned unchanged. If you need to right align a string use the `align
|
||||
## proc <#align>`_.
|
||||
runnableExamples:
|
||||
assert alignLeft("abc", 4) == "abc "
|
||||
assert alignLeft("a", 0) == "a"
|
||||
assert alignLeft("1232", 6) == "1232 "
|
||||
assert alignLeft("1232", 6, '#'.Rune) == "1232##"
|
||||
assert alignLeft("Åge", 5) == "Åge "
|
||||
assert alignLeft("×", 4, '_'.Rune) == "×___"
|
||||
let sLen = s.runeLen
|
||||
if sLen < count:
|
||||
let padStr = $padding
|
||||
result = newStringOfCap(s.len + (count - sLen) * padStr.len)
|
||||
result.add s
|
||||
for i in sLen ..< count:
|
||||
result.add padStr
|
||||
else:
|
||||
result = s
|
||||
|
||||
|
||||
when isMainModule:
|
||||
|
||||
proc asRune(s: static[string]): Rune =
|
||||
## Compile-time conversion proc for converting string literals to a Rune
|
||||
## value. Returns the first Rune of the specified string.
|
||||
##
|
||||
## Shortcuts code like ``"å".runeAt(0)`` to ``"å".asRune`` and returns a
|
||||
## compile-time constant.
|
||||
if s.len == 0: Rune(0)
|
||||
else: s.runeAt(0)
|
||||
|
||||
let
|
||||
someString = "öÑ"
|
||||
someRunes = @[runeAt(someString, 0), runeAt(someString, 2)]
|
||||
@@ -1898,3 +2179,50 @@ when isMainModule:
|
||||
doAssert(runeSubStr(s, -100, 100) == "Hänsel ««: 10,00€")
|
||||
doAssert(runeSubStr(s, 0, -100) == "")
|
||||
doAssert(runeSubStr(s, 100, -100) == "")
|
||||
|
||||
block splitTests:
|
||||
let s = " this is an example "
|
||||
let s2 = ":this;is;an:example;;"
|
||||
let s3 = ":this×is×an:example××"
|
||||
doAssert s.split() == @["", "this", "is", "an", "example", "", ""]
|
||||
doAssert s2.split(seps = [':'.Rune, ';'.Rune]) == @["", "this", "is", "an", "example", "", ""]
|
||||
doAssert s3.split(seps = [':'.Rune, "×".asRune]) == @["", "this", "is", "an", "example", "", ""]
|
||||
doAssert s.split(maxsplit = 4) == @["", "this", "is", "an", "example "]
|
||||
doAssert s.split(' '.Rune, maxsplit = 1) == @["", "this is an example "]
|
||||
|
||||
block stripTests:
|
||||
doAssert(strip("") == "")
|
||||
doAssert(strip(" ") == "")
|
||||
doAssert(strip("y") == "y")
|
||||
doAssert(strip(" foofoofoo ") == "foofoofoo")
|
||||
doAssert(strip("sfoofoofoos", runes = ['s'.Rune]) == "foofoofoo")
|
||||
|
||||
block:
|
||||
let stripTestRunes = ['b'.Rune, 'a'.Rune, 'r'.Rune]
|
||||
doAssert(strip("barfoofoofoobar", runes = stripTestRunes) == "foofoofoo")
|
||||
doAssert(strip("sfoofoofoos", leading = false, runes = ['s'.Rune]) == "sfoofoofoo")
|
||||
doAssert(strip("sfoofoofoos", trailing = false, runes = ['s'.Rune]) == "foofoofoos")
|
||||
|
||||
block:
|
||||
let stripTestRunes = ["«".asRune, "»".asRune]
|
||||
doAssert(strip("«TEXT»", runes = stripTestRunes) == "TEXT")
|
||||
doAssert(strip("copyright©", leading = false, runes = ["©".asRune]) == "copyright")
|
||||
doAssert(strip("¿Question?", trailing = false, runes = ["¿".asRune]) == "Question?")
|
||||
doAssert(strip("×text×", leading = false, runes = ["×".asRune]) == "×text")
|
||||
doAssert(strip("×text×", trailing = false, runes = ["×".asRune]) == "text×")
|
||||
|
||||
block repeatTests:
|
||||
doAssert repeat('c'.Rune, 5) == "ccccc"
|
||||
doAssert repeat("×".asRune, 5) == "×××××"
|
||||
|
||||
block alignTests:
|
||||
doAssert align("abc", 4) == " abc"
|
||||
doAssert align("a", 0) == "a"
|
||||
doAssert align("1232", 6) == " 1232"
|
||||
doAssert align("1232", 6, '#'.Rune) == "##1232"
|
||||
doAssert align("1232", 6, "×".asRune) == "××1232"
|
||||
doAssert alignLeft("abc", 4) == "abc "
|
||||
doAssert alignLeft("a", 0) == "a"
|
||||
doAssert alignLeft("1232", 6) == "1232 "
|
||||
doAssert alignLeft("1232", 6, '#'.Rune) == "1232##"
|
||||
doAssert alignLeft("1232", 6, "×".asRune) == "1232××"
|
||||
|
||||
283
lib/std/sha1.nim
283
lib/std/sha1.nim
@@ -10,6 +10,7 @@
|
||||
## Note: Import ``std/sha1`` to use this module
|
||||
|
||||
import strutils
|
||||
from endians import bigEndian32, bigEndian64
|
||||
|
||||
const Sha1DigestSize = 20
|
||||
|
||||
@@ -17,166 +18,165 @@ type
|
||||
Sha1Digest = array[0 .. Sha1DigestSize-1, uint8]
|
||||
SecureHash* = distinct Sha1Digest
|
||||
|
||||
# Copyright (c) 2011, Micael Hildenborg
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# * Neither the name of Micael Hildenborg nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY
|
||||
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY
|
||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
# Ported to Nim by Erik O'Leary
|
||||
|
||||
type
|
||||
Sha1State* = array[0 .. 5-1, uint32]
|
||||
Sha1Buffer = array[0 .. 80-1, uint32]
|
||||
Sha1State = object
|
||||
count: int
|
||||
state: array[5, uint32]
|
||||
buf: array[64, byte]
|
||||
|
||||
template clearBuffer(w: Sha1Buffer, len = 16) =
|
||||
zeroMem(addr(w), len * sizeof(uint32))
|
||||
# This implementation of the SHA1 algorithm was ported from the Chromium OS one
|
||||
# with minor modifications that should not affect its functionality.
|
||||
|
||||
proc init*(result: var Sha1State) =
|
||||
result[0] = 0x67452301'u32
|
||||
result[1] = 0xefcdab89'u32
|
||||
result[2] = 0x98badcfe'u32
|
||||
result[3] = 0x10325476'u32
|
||||
result[4] = 0xc3d2e1f0'u32
|
||||
proc newSha1State(): Sha1State =
|
||||
result.count = 0
|
||||
result.state[0] = 0x67452301'u32
|
||||
result.state[1] = 0xEFCDAB89'u32
|
||||
result.state[2] = 0x98BADCFE'u32
|
||||
result.state[3] = 0x10325476'u32
|
||||
result.state[4] = 0xC3D2E1F0'u32
|
||||
|
||||
proc innerHash(state: var Sha1State, w: var Sha1Buffer) =
|
||||
var
|
||||
a = state[0]
|
||||
b = state[1]
|
||||
c = state[2]
|
||||
d = state[3]
|
||||
e = state[4]
|
||||
template ror27(val: uint32): uint32 = (val shr 27) or (val shl 5)
|
||||
template ror2 (val: uint32): uint32 = (val shr 2) or (val shl 30)
|
||||
template ror31(val: uint32): uint32 = (val shr 31) or (val shl 1)
|
||||
|
||||
var round = 0
|
||||
proc transform(ctx: var Sha1State) =
|
||||
var W: array[80, uint32]
|
||||
var A, B, C, D, E: uint32
|
||||
var t = 0
|
||||
|
||||
template rot(value, bits: uint32): uint32 =
|
||||
(value shl bits) or (value shr (32u32 - bits))
|
||||
A = ctx.state[0]
|
||||
B = ctx.state[1]
|
||||
C = ctx.state[2]
|
||||
D = ctx.state[3]
|
||||
E = ctx.state[4]
|
||||
|
||||
template sha1(fun, val: uint32) =
|
||||
let t = rot(a, 5) + fun + e + val + w[round]
|
||||
e = d
|
||||
d = c
|
||||
c = rot(b, 30)
|
||||
b = a
|
||||
a = t
|
||||
template SHA_F1(A, B, C, D, E, t: untyped) =
|
||||
bigEndian32(addr W[t], addr ctx.buf[t * 4])
|
||||
E += ror27(A) + W[t] + (D xor (B and (C xor D))) + 0x5A827999'u32
|
||||
B = ror2(B)
|
||||
|
||||
template process(body: untyped) =
|
||||
w[round] = rot(w[round - 3] xor w[round - 8] xor w[round - 14] xor w[round - 16], 1)
|
||||
body
|
||||
inc(round)
|
||||
while t < 15:
|
||||
SHA_F1(A, B, C, D, E, t + 0)
|
||||
SHA_F1(E, A, B, C, D, t + 1)
|
||||
SHA_F1(D, E, A, B, C, t + 2)
|
||||
SHA_F1(C, D, E, A, B, t + 3)
|
||||
SHA_F1(B, C, D, E, A, t + 4)
|
||||
t += 5
|
||||
SHA_F1(A, B, C, D, E, t + 0) # 16th one, t == 15
|
||||
|
||||
template wrap(dest, value: untyped) =
|
||||
let v = dest + value
|
||||
dest = v
|
||||
template SHA_F11(A, B, C, D, E, t: untyped) =
|
||||
W[t] = ror31(W[t-3] xor W[t-8] xor W[t-14] xor W[t-16])
|
||||
E += ror27(A) + W[t] + (D xor (B and (C xor D))) + 0x5A827999'u32
|
||||
B = ror2(B)
|
||||
|
||||
while round < 16:
|
||||
sha1((b and c) or (not b and d), 0x5a827999'u32)
|
||||
inc(round)
|
||||
SHA_F11(E, A, B, C, D, t + 1)
|
||||
SHA_F11(D, E, A, B, C, t + 2)
|
||||
SHA_F11(C, D, E, A, B, t + 3)
|
||||
SHA_F11(B, C, D, E, A, t + 4)
|
||||
|
||||
while round < 20:
|
||||
process:
|
||||
sha1((b and c) or (not b and d), 0x5a827999'u32)
|
||||
template SHA_F2(A, B, C, D, E, t: untyped) =
|
||||
W[t] = ror31(W[t-3] xor W[t-8] xor W[t-14] xor W[t-16])
|
||||
E += ror27(A) + W[t] + (B xor C xor D) + 0x6ED9EBA1'u32
|
||||
B = ror2(B)
|
||||
|
||||
while round < 40:
|
||||
process:
|
||||
sha1(b xor c xor d, 0x6ed9eba1'u32)
|
||||
t = 20
|
||||
while t < 40:
|
||||
SHA_F2(A, B, C, D, E, t + 0)
|
||||
SHA_F2(E, A, B, C, D, t + 1)
|
||||
SHA_F2(D, E, A, B, C, t + 2)
|
||||
SHA_F2(C, D, E, A, B, t + 3)
|
||||
SHA_F2(B, C, D, E, A, t + 4)
|
||||
t += 5
|
||||
|
||||
while round < 60:
|
||||
process:
|
||||
sha1((b and c) or (b and d) or (c and d), 0x8f1bbcdc'u32)
|
||||
template SHA_F3(A, B, C, D, E, t: untyped) =
|
||||
W[t] = ror31(W[t-3] xor W[t-8] xor W[t-14] xor W[t-16])
|
||||
E += ror27(A) + W[t] + ((B and C) or (D and (B or C))) + 0x8F1BBCDC'u32
|
||||
B = ror2(B)
|
||||
|
||||
while round < 80:
|
||||
process:
|
||||
sha1(b xor c xor d, 0xca62c1d6'u32)
|
||||
while t < 60:
|
||||
SHA_F3(A, B, C, D, E, t + 0)
|
||||
SHA_F3(E, A, B, C, D, t + 1)
|
||||
SHA_F3(D, E, A, B, C, t + 2)
|
||||
SHA_F3(C, D, E, A, B, t + 3)
|
||||
SHA_F3(B, C, D, E, A, t + 4)
|
||||
t += 5
|
||||
|
||||
wrap state[0], a
|
||||
wrap state[1], b
|
||||
wrap state[2], c
|
||||
wrap state[3], d
|
||||
wrap state[4], e
|
||||
template SHA_F4(A, B, C, D, E, t: untyped) =
|
||||
W[t] = ror31(W[t-3] xor W[t-8] xor W[t-14] xor W[t-16])
|
||||
E += ror27(A) + W[t] + (B xor C xor D) + 0xCA62C1D6'u32
|
||||
B = ror2(B)
|
||||
|
||||
proc sha1(src: cstring; len: int): Sha1Digest =
|
||||
#Initialize state
|
||||
var state: Sha1State
|
||||
init(state)
|
||||
while t < 80:
|
||||
SHA_F4(A, B, C, D, E, t + 0)
|
||||
SHA_F4(E, A, B, C, D, t + 1)
|
||||
SHA_F4(D, E, A, B, C, t + 2)
|
||||
SHA_F4(C, D, E, A, B, t + 3)
|
||||
SHA_F4(B, C, D, E, A, t + 4)
|
||||
t += 5
|
||||
|
||||
#Create w buffer
|
||||
var w: Sha1Buffer
|
||||
ctx.state[0] += A
|
||||
ctx.state[1] += B
|
||||
ctx.state[2] += C
|
||||
ctx.state[3] += D
|
||||
ctx.state[4] += E
|
||||
|
||||
#Loop through all complete 64byte blocks.
|
||||
let byteLen = len
|
||||
let endOfFullBlocks = byteLen - 64
|
||||
var endCurrentBlock = 0
|
||||
var currentBlock = 0
|
||||
proc update(ctx: var Sha1State, data: openArray[char]) =
|
||||
var i = ctx.count mod 64
|
||||
var j = 0
|
||||
var len = data.len
|
||||
# Gather 64-bytes worth of data in order to perform a round with the leftover
|
||||
# data we had stored (but not processed yet)
|
||||
if len > 64 - i:
|
||||
copyMem(addr ctx.buf[i], unsafeAddr data[j], 64 - i)
|
||||
len -= 64 - i
|
||||
j += 64 - i
|
||||
transform(ctx)
|
||||
# Update the index since it's used in the while loop below _and_ we want to
|
||||
# keep its value if this code path isn't executed
|
||||
i = 0
|
||||
# Process the bulk of the payload
|
||||
while len >= 64:
|
||||
copyMem(addr ctx.buf[0], unsafeAddr data[j], 64)
|
||||
len -= 64
|
||||
j += 64
|
||||
transform(ctx)
|
||||
# Process the tail of the payload (len is < 64)
|
||||
while len > 0:
|
||||
dec len
|
||||
ctx.buf[i] = byte(data[j])
|
||||
inc i
|
||||
inc j
|
||||
if i == 64:
|
||||
transform(ctx)
|
||||
i = 0
|
||||
ctx.count += data.len
|
||||
|
||||
while currentBlock <= endOfFullBlocks:
|
||||
endCurrentBlock = currentBlock + 64
|
||||
proc finalize(ctx: var Sha1State): Sha1Digest =
|
||||
var cnt = uint64(ctx.count * 8)
|
||||
# A 1 bit
|
||||
update(ctx, "\x80")
|
||||
# Add padding until we reach a complexive size of 64 - 8 bytes
|
||||
while (ctx.count mod 64) != (64 - 8):
|
||||
update(ctx, "\x00")
|
||||
# The message length as a 64bit BE number completes the block
|
||||
var tmp: array[8, char]
|
||||
bigEndian64(addr tmp[0], addr cnt)
|
||||
update(ctx, tmp)
|
||||
# Turn the result into a single 160-bit number
|
||||
for i in 0 ..< 5:
|
||||
bigEndian32(addr ctx.state[i], addr ctx.state[i])
|
||||
copyMem(addr result[0], addr ctx.state[0], Sha1DigestSize)
|
||||
|
||||
var i = 0
|
||||
while currentBlock < endCurrentBlock:
|
||||
w[i] = uint32(src[currentBlock+3]) or
|
||||
uint32(src[currentBlock+2]) shl 8'u32 or
|
||||
uint32(src[currentBlock+1]) shl 16'u32 or
|
||||
uint32(src[currentBlock]) shl 24'u32
|
||||
currentBlock += 4
|
||||
inc(i)
|
||||
# Public API
|
||||
|
||||
innerHash(state, w)
|
||||
proc secureHash*(str: string): SecureHash =
|
||||
var state = newSha1State()
|
||||
state.update(str)
|
||||
SecureHash(state.finalize())
|
||||
|
||||
#Handle last and not full 64 byte block if existing
|
||||
endCurrentBlock = byteLen - currentBlock
|
||||
clearBuffer(w)
|
||||
var lastBlockBytes = 0
|
||||
proc secureHashFile*(filename: string): SecureHash =
|
||||
secureHash(readFile(filename))
|
||||
|
||||
while lastBlockBytes < endCurrentBlock:
|
||||
|
||||
var value = uint32(src[lastBlockBytes + currentBlock]) shl
|
||||
((3'u32 - uint32(lastBlockBytes and 3)) shl 3)
|
||||
|
||||
w[lastBlockBytes shr 2] = w[lastBlockBytes shr 2] or value
|
||||
inc(lastBlockBytes)
|
||||
|
||||
w[lastBlockBytes shr 2] = w[lastBlockBytes shr 2] or (
|
||||
0x80'u32 shl ((3'u32 - uint32(lastBlockBytes and 3)) shl 3)
|
||||
)
|
||||
|
||||
if endCurrentBlock >= 56:
|
||||
innerHash(state, w)
|
||||
clearBuffer(w)
|
||||
|
||||
w[15] = uint32(byteLen) shl 3
|
||||
innerHash(state, w)
|
||||
|
||||
# Store hash in result pointer, and make sure we get in in the correct order
|
||||
# on both endian models.
|
||||
for i in 0 .. Sha1DigestSize-1:
|
||||
result[i] = uint8((int(state[i shr 2]) shr ((3-(i and 3)) * 8)) and 255)
|
||||
|
||||
proc sha1(src: string): Sha1Digest =
|
||||
## Calculate SHA1 from input string
|
||||
sha1(src, src.len)
|
||||
|
||||
proc secureHash*(str: string): SecureHash = SecureHash(sha1(str))
|
||||
proc secureHashFile*(filename: string): SecureHash = secureHash(readFile(filename))
|
||||
proc `$`*(self: SecureHash): string =
|
||||
result = ""
|
||||
for v in Sha1Digest(self):
|
||||
@@ -190,8 +190,15 @@ proc `==`*(a, b: SecureHash): bool =
|
||||
# Not a constant-time comparison, but that's acceptable in this context
|
||||
Sha1Digest(a) == Sha1Digest(b)
|
||||
|
||||
|
||||
when isMainModule:
|
||||
let hash1 = secureHash("a93tgj0p34jagp9[agjp98ajrhp9aej]")
|
||||
doAssert hash1 == hash1
|
||||
doAssert parseSecureHash($hash1) == hash1
|
||||
|
||||
template checkVector(s, exp: string) =
|
||||
doAssert secureHash(s) == parseSecureHash(exp)
|
||||
|
||||
checkVector("", "da39a3ee5e6b4b0d3255bfef95601890afd80709")
|
||||
checkVector("abc", "a9993e364706816aba3e25717850c26c9cd0d89d")
|
||||
checkVector("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
|
||||
"84983e441c3bd26ebaae4aa1f95129e5e54670f1")
|
||||
|
||||
162
lib/system.nim
162
lib/system.nim
@@ -264,40 +264,97 @@ when defined(nimNewRuntime):
|
||||
type lent*{.magic: "BuiltinType".}[T]
|
||||
|
||||
proc high*[T: Ordinal](x: T): T {.magic: "High", noSideEffect.}
|
||||
## returns the highest possible index of an array, a sequence, a string or
|
||||
## the highest possible value of an ordinal value `x`. As a special
|
||||
## returns the highest possible value of an ordinal value `x`. As a special
|
||||
## semantic rule, `x` may also be a type identifier.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## high(2) #=> 9223372036854775807
|
||||
|
||||
proc high*[T: Ordinal|enum](x: typeDesc[T]): T {.magic: "High", noSideEffect.}
|
||||
## returns the highest possible value of an ordinal or enum type.
|
||||
## ``high(int)`` is Nim's way of writing `INT_MAX`:idx: or `MAX_INT`:idx:.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## high(int) #=> 9223372036854775807
|
||||
|
||||
proc high*[T](x: openArray[T]): int {.magic: "High", noSideEffect.}
|
||||
## returns the highest possible index of a sequence `x`.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## var s = @[1,2,3,4,5,6,7]
|
||||
## high(s) #=> 6
|
||||
## for i in low(s)..high(s):
|
||||
## echo s[i]
|
||||
|
||||
proc high*[I, T](x: array[I, T]): I {.magic: "High", noSideEffect.}
|
||||
## returns the highest possible index of an array `x`.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## var arr = [1,2,3,4,5,6,7]
|
||||
## high(arr) #=> 6
|
||||
## high(2) #=> 9223372036854775807
|
||||
## high(int) #=> 9223372036854775807
|
||||
## for i in low(arr)..high(arr):
|
||||
## echo arr[i]
|
||||
|
||||
proc high*[T: Ordinal|enum](x: typeDesc[T]): T {.magic: "High", noSideEffect.}
|
||||
proc high*[T](x: openArray[T]): int {.magic: "High", noSideEffect.}
|
||||
proc high*[I, T](x: array[I, T]): I {.magic: "High", noSideEffect.}
|
||||
proc high*[I, T](x: typeDesc[array[I, T]]): I {.magic: "High", noSideEffect.}
|
||||
## returns the highest possible index of an array type.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## high(array[7, int]) #=> 6
|
||||
|
||||
proc high*(x: cstring): int {.magic: "High", noSideEffect.}
|
||||
## returns the highest possible index of a compatible string `x`.
|
||||
## This is sometimes an O(n) operation.
|
||||
|
||||
proc high*(x: string): int {.magic: "High", noSideEffect.}
|
||||
## returns the highest possible index of a string `x`.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## var str = "Hello world!"
|
||||
## high(str) #=> 11
|
||||
|
||||
proc low*[T: Ordinal|enum](x: typeDesc[T]): T {.magic: "Low", noSideEffect.}
|
||||
## returns the lowest possible value of an ordinal or enum type.
|
||||
## ``low(int)`` is Nim's way of writing `INT_MIN`:idx: or `MIN_INT`:idx:.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## low(int) #=> -9223372036854775808
|
||||
|
||||
proc low*[T](x: openArray[T]): int {.magic: "Low", noSideEffect.}
|
||||
## returns the lowest possible index of a sequence `x`.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## var s = @[1,2,3,4,5,6,7]
|
||||
## low(s) #=> 0
|
||||
|
||||
proc low*[I, T](x: array[I, T]): I {.magic: "Low", noSideEffect.}
|
||||
proc low*[T](x: T): T {.magic: "Low", noSideEffect.}
|
||||
proc low*[I, T](x: typeDesc[array[I, T]]): I {.magic: "Low", noSideEffect.}
|
||||
proc low*(x: cstring): int {.magic: "Low", noSideEffect.}
|
||||
proc low*(x: string): int {.magic: "Low", noSideEffect.}
|
||||
## returns the lowest possible index of an array, a sequence, a string or
|
||||
## the lowest possible value of an ordinal value `x`. As a special
|
||||
## semantic rule, `x` may also be a type identifier.
|
||||
## returns the lowest possible index of an array `x`.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## var arr = [1,2,3,4,5,6,7]
|
||||
## low(arr) #=> 0
|
||||
|
||||
proc low*[T](x: T): T {.magic: "Low", noSideEffect.}
|
||||
## returns the lowest possible value of an ordinal value `x`. As a special
|
||||
## semantic rule, `x` may also be a type identifier.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## low(2) #=> -9223372036854775808
|
||||
## low(int) #=> -9223372036854775808
|
||||
|
||||
proc low*[I, T](x: typeDesc[array[I, T]]): I {.magic: "Low", noSideEffect.}
|
||||
## returns the lowest possible index of an array type.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## low(array[7, int]) #=> 0
|
||||
|
||||
proc low*(x: cstring): int {.magic: "Low", noSideEffect.}
|
||||
## returns the lowest possible index of a compatible string `x`.
|
||||
|
||||
proc low*(x: string): int {.magic: "Low", noSideEffect.}
|
||||
## returns the lowest possible index of a string `x`.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## var str = "Hello world!"
|
||||
## low(str) #=> 0
|
||||
|
||||
proc shallowCopy*[T](x: var T, y: T) {.noSideEffect, magic: "ShallowCopy".}
|
||||
## use this instead of `=` for a `shallow copy`:idx:. The shallow copy
|
||||
@@ -726,19 +783,42 @@ when not defined(JS) and not defined(gcDestructors):
|
||||
|
||||
proc len*[TOpenArray: openArray|varargs](x: TOpenArray): int {.
|
||||
magic: "LengthOpenArray", noSideEffect.}
|
||||
## returns the length of an openarray.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## var s = [1,1,1,1,1]
|
||||
## len(s) #=> 5
|
||||
|
||||
proc len*(x: string): int {.magic: "LengthStr", noSideEffect.}
|
||||
## returns the length of a string.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## var str = "Hello world!"
|
||||
## len(str) #=> 12
|
||||
|
||||
proc len*(x: cstring): int {.magic: "LengthStr", noSideEffect.}
|
||||
## returns the length of a compatible string. This is sometimes
|
||||
## an O(n) operation.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## var str: cstring = "Hello world!"
|
||||
## len(str) #=> 12
|
||||
|
||||
proc len*(x: (type array)|array): int {.magic: "LengthArray", noSideEffect.}
|
||||
proc len*[T](x: seq[T]): int {.magic: "LengthSeq", noSideEffect.}
|
||||
## returns the length of an array, an openarray, a sequence or a string.
|
||||
## This is roughly the same as ``high(T)-low(T)+1``, but its resulting type is
|
||||
## always an int.
|
||||
## returns the length of an array or an array type.
|
||||
## This is roughly the same as ``high(T)-low(T)+1``.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## var arr = [1,1,1,1,1]
|
||||
## len(arr) #=> 5
|
||||
## for i in 0..<arr.len:
|
||||
## echo arr[i] #=> 1,1,1,1,1
|
||||
## len(array[3..8, int]) #=> 6
|
||||
|
||||
proc len*[T](x: seq[T]): int {.magic: "LengthSeq", noSideEffect.}
|
||||
## returns the length of a sequence.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## var s = @[1,1,1,1,1]
|
||||
## len(s) #=> 5
|
||||
|
||||
# set routines:
|
||||
proc incl*[T](x: var set[T], y: T) {.magic: "Incl", noSideEffect.}
|
||||
@@ -889,10 +969,13 @@ proc `div`*(x, y: int32): int32 {.magic: "DivI", noSideEffect.}
|
||||
## ``trunc(x/y)``.
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## 1 div 2 == 0
|
||||
## 2 div 2 == 1
|
||||
## 3 div 2 == 1
|
||||
## 7 div 5 == 1
|
||||
## ( 1 div 2) == 0
|
||||
## ( 2 div 2) == 1
|
||||
## ( 3 div 2) == 1
|
||||
## ( 7 div 3) == 2
|
||||
## (-7 div 3) == -2
|
||||
## ( 7 div -3) == -2
|
||||
## (-7 div -3) == 2
|
||||
|
||||
when defined(nimnomagic64):
|
||||
proc `div`*(x, y: int64): int64 {.magic: "DivI", noSideEffect.}
|
||||
@@ -908,7 +991,10 @@ proc `mod`*(x, y: int32): int32 {.magic: "ModI", noSideEffect.}
|
||||
## ``x - (x div y) * y``.
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## (7 mod 5) == 2
|
||||
## ( 7 mod 5) == 2
|
||||
## (-7 mod 5) == -2
|
||||
## ( 7 mod -5) == 2
|
||||
## (-7 mod -5) == -2
|
||||
|
||||
when defined(nimnomagic64):
|
||||
proc `mod`*(x, y: int64): int64 {.magic: "ModI", noSideEffect.}
|
||||
@@ -1334,11 +1420,11 @@ const
|
||||
## compiler magic. It is useful to embed testing code in a module.
|
||||
|
||||
CompileDate* {.magic: "CompileDate"}: string = "0000-00-00"
|
||||
## is the date of compilation as a string of the form
|
||||
## is the date (in UTC) of compilation as a string of the form
|
||||
## ``YYYY-MM-DD``. This works thanks to compiler magic.
|
||||
|
||||
CompileTime* {.magic: "CompileTime"}: string = "00:00:00"
|
||||
## is the time of compilation as a string of the form
|
||||
## is the time (in UTC) of compilation as a string of the form
|
||||
## ``HH:MM:SS``. This works thanks to compiler magic.
|
||||
|
||||
cpuEndian* {.magic: "CpuEndian"}: Endianness = littleEndian
|
||||
@@ -1674,26 +1760,26 @@ type # these work for most platforms:
|
||||
proc toFloat*(i: int): float {.
|
||||
magic: "ToFloat", noSideEffect, importc: "toFloat".}
|
||||
## converts an integer `i` into a ``float``. If the conversion
|
||||
## fails, `EInvalidValue` is raised. However, on most platforms the
|
||||
## fails, `ValueError` is raised. However, on most platforms the
|
||||
## conversion cannot fail.
|
||||
|
||||
proc toBiggestFloat*(i: BiggestInt): BiggestFloat {.
|
||||
magic: "ToBiggestFloat", noSideEffect, importc: "toBiggestFloat".}
|
||||
## converts an biggestint `i` into a ``biggestfloat``. If the conversion
|
||||
## fails, `EInvalidValue` is raised. However, on most platforms the
|
||||
## fails, `ValueError` is raised. However, on most platforms the
|
||||
## conversion cannot fail.
|
||||
|
||||
proc toInt*(f: float): int {.
|
||||
magic: "ToInt", noSideEffect, importc: "toInt".}
|
||||
## converts a floating point number `f` into an ``int``. Conversion
|
||||
## rounds `f` if it does not contain an integer value. If the conversion
|
||||
## fails (because `f` is infinite for example), `EInvalidValue` is raised.
|
||||
## fails (because `f` is infinite for example), `ValueError` is raised.
|
||||
|
||||
proc toBiggestInt*(f: BiggestFloat): BiggestInt {.
|
||||
magic: "ToBiggestInt", noSideEffect, importc: "toBiggestInt".}
|
||||
## converts a biggestfloat `f` into a ``biggestint``. Conversion
|
||||
## rounds `f` if it does not contain an integer value. If the conversion
|
||||
## fails (because `f` is infinite for example), `EInvalidValue` is raised.
|
||||
## fails (because `f` is infinite for example), `ValueError` is raised.
|
||||
|
||||
proc addQuitProc*(QuitProc: proc() {.noconv.}) {.
|
||||
importc: "atexit", header: "<stdlib.h>".}
|
||||
@@ -1935,7 +2021,7 @@ const
|
||||
NimMinor* {.intdefine.}: int = 19
|
||||
## is the minor number of Nim's version.
|
||||
|
||||
NimPatch* {.intdefine.}: int = 1
|
||||
NimPatch* {.intdefine.}: int = 2
|
||||
## is the patch number of Nim's version.
|
||||
|
||||
NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch
|
||||
@@ -2029,7 +2115,7 @@ when defined(nimNewRoof):
|
||||
template dotdotImpl(t) {.dirty.} =
|
||||
iterator `..`*(a, b: t): t {.inline.} =
|
||||
## A type specialized version of ``..`` for convenience so that
|
||||
## mixing integer types work better.
|
||||
## mixing integer types works better.
|
||||
var res = a
|
||||
while res <= b:
|
||||
yield res
|
||||
@@ -2854,7 +2940,7 @@ when defined(nimnomagic64):
|
||||
## returns the absolute value of `x`. If `x` is ``low(x)`` (that
|
||||
## is -MININT for its type), an overflow exception is thrown (if overflow
|
||||
## checking is turned on).
|
||||
if x < 0: -x else: x
|
||||
result = if x < 0: -x else: x
|
||||
else:
|
||||
proc abs*(x: int64): int64 {.magic: "AbsI64", noSideEffect.} =
|
||||
## returns the absolute value of `x`. If `x` is ``low(x)`` (that
|
||||
@@ -3322,6 +3408,7 @@ when not defined(JS): #and not defined(nimscript):
|
||||
of 1: d = ze(cast[ptr int8](a +% n.offset)[])
|
||||
of 2: d = ze(cast[ptr int16](a +% n.offset)[])
|
||||
of 4: d = int(cast[ptr int32](a +% n.offset)[])
|
||||
of 8: d = int(cast[ptr int64](a +% n.offset)[])
|
||||
else: sysAssert(false, "getDiscriminant: invalid n.typ.size")
|
||||
return d
|
||||
|
||||
@@ -3357,7 +3444,7 @@ when not defined(JS): #and not defined(nimscript):
|
||||
iterator lines*(filename: string): TaintedString {.tags: [ReadIOEffect].} =
|
||||
## Iterates over any line in the file named `filename`.
|
||||
##
|
||||
## If the file does not exist `EIO` is raised. The trailing newline
|
||||
## If the file does not exist `IOError` is raised. The trailing newline
|
||||
## character(s) are removed from the iterated lines. Example:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
@@ -3877,6 +3964,7 @@ proc shallow*[T](s: var seq[T]) {.noSideEffect, inline.} =
|
||||
## marks a sequence `s` as `shallow`:idx:. Subsequent assignments will not
|
||||
## perform deep copies of `s`. This is only useful for optimization
|
||||
## purposes.
|
||||
if s.len == 0: return
|
||||
when not defined(JS) and not defined(nimscript):
|
||||
var s = cast[PGenericSeq](s)
|
||||
s.reserved = s.reserved or seqShallowFlag
|
||||
@@ -3887,6 +3975,8 @@ proc shallow*(s: var string) {.noSideEffect, inline.} =
|
||||
## purposes.
|
||||
when not defined(JS) and not defined(nimscript) and not defined(gcDestructors):
|
||||
var s = cast[PGenericSeq](s)
|
||||
if s == nil:
|
||||
s = cast[PGenericSeq](newString(0))
|
||||
# string literals cannot become 'shallow':
|
||||
if (s.reserved and strlitFlag) == 0:
|
||||
s.reserved = s.reserved or seqShallowFlag
|
||||
|
||||
@@ -17,13 +17,6 @@
|
||||
const
|
||||
NilLibHandle: LibHandle = nil
|
||||
|
||||
proc c_fwrite(buf: pointer, size, n: csize, f: File): cint {.
|
||||
importc: "fwrite", header: "<stdio.h>".}
|
||||
|
||||
proc rawWrite(f: File, s: string) =
|
||||
# we cannot throw an exception here!
|
||||
discard c_fwrite(cstring(s), 1, s.len, f)
|
||||
|
||||
proc nimLoadLibraryError(path: string) =
|
||||
# carefully written to avoid memory allocation:
|
||||
stderr.rawWrite("could not load: ")
|
||||
|
||||
@@ -17,8 +17,15 @@ var
|
||||
## instead of stdmsg.write when printing stacktrace.
|
||||
## Unstable API.
|
||||
|
||||
proc c_fwrite(buf: pointer, size, n: csize, f: File): cint {.
|
||||
importc: "fwrite", header: "<stdio.h>".}
|
||||
|
||||
proc rawWrite(f: File, s: string|cstring) =
|
||||
# we cannot throw an exception here!
|
||||
discard c_fwrite(cstring(s), 1, s.len, f)
|
||||
|
||||
when not defined(windows) or not defined(guiapp):
|
||||
proc writeToStdErr(msg: cstring) = write(stdmsg, msg)
|
||||
proc writeToStdErr(msg: cstring) = rawWrite(stdmsg, msg)
|
||||
|
||||
else:
|
||||
proc MessageBoxA(hWnd: cint, lpText, lpCaption: cstring, uType: int): int32 {.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user